Skip to content

Commit 0b65d2b

Browse files
authored
Merge pull request #128 from scijava/scijava-ops-image/rai-focusers
Add RAI focusers
2 parents 41184ce + cd33533 commit 0b65d2b

File tree

11 files changed

+666
-103
lines changed

11 files changed

+666
-103
lines changed

scijava-function/src/main/java/org/scijava/function/Computers.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2710,6 +2710,8 @@ default void compute(I1 in1, I2 in2, I3 in3, I4 in4, I5 in5, I6 in6, I7 in7,
27102710
}
27112711

27122712
static {
2713+
ALL_COMPUTERS[0][0] = Computers.Arity0.class;
2714+
ALL_ARITIES.put(Computers.Arity0.class, 0);
27132715
ALL_COMPUTERS[1][0] = Computers.Arity1_1.class;
27142716
ALL_ARITIES.put(Computers.Arity1_1.class, 1);
27152717
ALL_COMPUTERS[1][1] = Computers.Arity1.class;

scijava-function/templates/main/java/org/scijava/function/Computers.vm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ public final class Computers {
139139
#end##foreach($arity)
140140

141141
static {
142+
ALL_COMPUTERS[0][0] = Computers.Arity0.class;
143+
ALL_ARITIES.put(Computers.Arity0.class, 0);
142144
#foreach($arity in $arities)
143145
#end
144146
#foreach($arity in [1..$maxArity])

scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpEnvironment.java

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,7 @@
8585
import org.scijava.priority.Priority;
8686
import org.scijava.struct.FunctionalMethodType;
8787
import org.scijava.struct.ItemIO;
88-
import org.scijava.types.DefaultTypeReifier;
89-
import org.scijava.types.Nil;
90-
import org.scijava.types.TypeReifier;
91-
import org.scijava.types.Types;
88+
import org.scijava.types.*;
9289
import org.slf4j.Logger;
9390
import org.slf4j.LoggerFactory;
9491

@@ -633,10 +630,28 @@ private List<RichOp<?>> resolveOpDependencies(OpInfo info,
633630
Map<TypeVariable<?>, Type> typeVarAssigns, Hints hints)
634631
{
635632

633+
// All type variables mapped to Any must be removed before matching
634+
// dependencies. Consider the case of matching a request for a
635+
// Function<Any, Double>, which has matched an Op that is a
636+
// Function<List<N extends Number>, Double> with a Function<N, Double>
637+
// dependency. In this case, N maps to Any because List<N> maps to Any. We
638+
// cannot simply search for a Function<Any, Double> dependency, however,
639+
// because that might return a Function<Byte, Double> which would not work
640+
// for an arbitrary input of type <N extends Number>. Therefore, the correct
641+
// bound on the dependency is Function<N extends Number, Double> i.e. we
642+
// must ignore the Any.
643+
Map<TypeVariable<?>, Type> dependencyTypeVarAssigns = new HashMap<>();
644+
for (var entry : typeVarAssigns.entrySet()) {
645+
var value = entry.getValue();
646+
if (!value.equals(Any.class) && !(value instanceof Any)) {
647+
dependencyTypeVarAssigns.put(entry.getKey(), entry.getValue());
648+
}
649+
}
650+
// Then, match dependencies
636651
final List<RichOp<?>> dependencyChains = new ArrayList<>();
637-
638652
for (final OpDependencyMember<?> dependency : Infos.dependencies(info)) {
639-
final OpRequest dep = inferOpRequest(dependency, typeVarAssigns);
653+
final OpRequest dep = inferOpRequest(dependency,
654+
dependencyTypeVarAssigns);
640655
try {
641656
// TODO: Consider a new Hint implementation
642657
Hints hintsCopy = hints.plus(DependencyMatching.IN_PROGRESS,

scijava-ops-engine/src/main/java/org/scijava/ops/engine/matcher/simplify/SimplificationUtils.java

Lines changed: 54 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,7 @@
4444
import org.scijava.function.Computers;
4545
import org.scijava.function.Container;
4646
import org.scijava.function.Mutable;
47-
import org.scijava.ops.api.Hints;
48-
import org.scijava.ops.api.OpEnvironment;
49-
import org.scijava.ops.api.OpInfo;
50-
import org.scijava.ops.api.OpMatchingException;
51-
import org.scijava.ops.api.OpRequest;
52-
import org.scijava.ops.api.RichOp;
47+
import org.scijava.ops.api.*;
5348
import org.scijava.ops.engine.BaseOpHints;
5449
import org.scijava.ops.engine.util.Infos;
5550
import org.scijava.ops.engine.util.internal.AnnotationUtils;
@@ -203,19 +198,61 @@ public static SimplifiedOpInfo simplifyInfo(OpEnvironment env, OpInfo info) {
203198
outSimplifier = org.scijava.ops.api.Ops.rich(identity);
204199
}
205200

206-
RichOp<Computers.Arity1<?, ?>> copyOp = null;
201+
RichOp<Computers.Arity1<?, ?>> copyOp = getCopyOp(env, info, inFocusers,
202+
outSimplifier, h);
203+
return new SimplifiedOpInfo(info, inFocusers, outSimplifier, copyOp);
204+
}
205+
206+
/**
207+
* Helper method that finds the {@code engine.copy} Op needed for a
208+
* {@link SimplifiedOpInfo}
209+
*
210+
* @param env the {@link OpEnvironment} containing {@code engine.copy} Ops.
211+
* @param info the original {@link OpInfo}.
212+
* @param inFocusers the {@code engine.focus} Ops used to focus the inputs to
213+
* the simplified Op
214+
* @param outSimplifier the {@code engine.simplify} Op used to simplify the
215+
* output for the simplified Op
216+
* @param h {@link Hints} to be used in matching the copy Op.
217+
* @return a {@code engine.copy} Op
218+
*/
219+
private static RichOp<Computers.Arity1<?, ?>> getCopyOp( //
220+
OpEnvironment env, //
221+
OpInfo info, //
222+
List<RichOp<Function<?, ?>>> inFocusers, //
223+
RichOp<Function<?, ?>> outSimplifier, //
224+
Hints h //
225+
) {
207226
int ioIndex = SimplificationUtils.findMutableArgIndex(Types.raw(info
208227
.opType()));
209-
if (ioIndex > -1) {
210-
var nil = Nil.of(outType(info.outputType(), outSimplifier));
211-
var copier = env.unary("engine.copy", h).inType(nil).outType(nil)
212-
.computer();
213-
copyOp = org.scijava.ops.api.Ops.rich(copier);
228+
// If IO index is -1, output is returned - no need to copy.
229+
if (ioIndex == -1) {
230+
return null;
214231
}
215-
return new SimplifiedOpInfo(info, inFocusers, outSimplifier, copyOp);
232+
// Otherwise, we need an Op to convert the simple output back into the
233+
// pre-focused input
234+
// Determine simple output type.
235+
var simpleOut = outType(info.outputType(), outSimplifier);
236+
// Determine unfocused input type.
237+
var focuserInfo = Ops.info(inFocusers.get(ioIndex));
238+
Map<TypeVariable<?>, Type> typeAssigns = new HashMap<>();
239+
GenericAssignability.inferTypeVariables( //
240+
new Type[] { focuserInfo.outputType() }, //
241+
new Type[] { info.inputTypes().get(ioIndex) }, //
242+
typeAssigns //
243+
);
244+
var unfocusedInput = Nil.of(Types.mapVarToTypes( //
245+
focuserInfo.inputTypes().get(0), //
246+
typeAssigns //
247+
));
248+
// Match a copier
249+
return Ops.rich(env.unary("engine.copy", h) //
250+
.inType(simpleOut) //
251+
.outType(unfocusedInput) //
252+
.computer());
216253
}
217254

218-
private static Type outType(Type originalOutput,
255+
private static Nil<?> outType(Type originalOutput,
219256
RichOp<Function<?, ?>> outputSimplifier)
220257
{
221258
Map<TypeVariable<?>, Type> typeAssigns = new HashMap<>();
@@ -225,8 +262,9 @@ private static Type outType(Type originalOutput,
225262
new Type[] { originalOutput }, //
226263
typeAssigns //
227264
);
228-
return Types.mapVarToTypes(org.scijava.ops.api.Ops.info(outputSimplifier)
229-
.outputType(), typeAssigns);
265+
Type outType = Types.mapVarToTypes(org.scijava.ops.api.Ops.info(
266+
outputSimplifier).outputType(), typeAssigns);
267+
return Nil.of(outType);
230268
}
231269

232270
/**

scijava-ops-engine/src/test/java/org/scijava/ops/engine/matcher/MatchingWithAnyTest.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,24 @@
3232
import static org.junit.jupiter.api.Assertions.assertEquals;
3333
import static org.junit.jupiter.api.Assertions.assertThrows;
3434

35+
import java.util.Arrays;
36+
import java.util.List;
3537
import java.util.function.BiFunction;
3638
import java.util.function.Function;
3739

40+
import org.junit.jupiter.api.Assertions;
3841
import org.junit.jupiter.api.BeforeAll;
3942
import org.junit.jupiter.api.Test;
4043
import org.scijava.function.Computers;
4144
import org.scijava.function.Producer;
45+
import org.scijava.ops.api.Ops;
4246
import org.scijava.ops.engine.AbstractTestEnvironment;
4347
import org.scijava.ops.engine.adapt.functional.ComputersToFunctionsViaSource;
4448
import org.scijava.ops.spi.OpCollection;
49+
import org.scijava.ops.spi.OpDependency;
4550
import org.scijava.ops.spi.OpField;
51+
import org.scijava.ops.spi.OpMethod;
52+
import org.scijava.priority.Priority;
4653
import org.scijava.types.Any;
4754

4855
/**
@@ -106,6 +113,36 @@ public void testRunAnyFunction2FromComputer2() {
106113
assertEquals(Long.toString(in1 + in2), out.getValue());
107114
}
108115

116+
@Test
117+
public void testMatchingAnyWithDependencies() {
118+
var op = ops.op("test.AnyWithDependencies").arity1().inType(Any.class)
119+
.outType(Double.class).function();
120+
var richOp = Ops.rich(op);
121+
var info = Ops.info(op);
122+
Assertions.assertTrue(info.toString().contains("dependentAnyOp"));
123+
var dInfo = richOp.infoTree().dependencies().get(0);
124+
Assertions.assertTrue(dInfo.toString().contains("lowPriority"));
125+
}
126+
127+
@OpMethod(names = "test.AnyWithDependencies", type = Function.class)
128+
public static <N extends Number> Double dependentAnyOp(@OpDependency(
129+
name = "test.AnyDependent") Function<N, Double> e, List<N> input)
130+
{
131+
return input.stream().map(e).reduce(0.0, Double::sum);
132+
}
133+
134+
@OpMethod(names = "test.AnyDependent", type = Function.class,
135+
priority = Priority.HIGH)
136+
public static Double highPriorityDependingOp(String input) {
137+
throw new IllegalStateException("This should not be called!");
138+
}
139+
140+
@OpMethod(names = "test.AnyDependent", type = Function.class,
141+
priority = Priority.LOW)
142+
public static Double lowPriorityDependingOp(Number input) {
143+
return input.doubleValue();
144+
}
145+
109146
@OpField(names = "test.functionAndLongToLong")
110147
public final BiFunction<Function<Long, Long>, Long, Long> funcAndLongToLong = //
111148
(t, u) -> t.apply(u);

0 commit comments

Comments
 (0)