DefaultOpMatchingService ambiguity

Sorry for all the posts lately…

When matching ops using a request with a generic interface (i.e., Ops.XXX.YYY), it would seem desirable to give preference to the op that doesn’t require any input conversion or casting over an op of the same priority that does require input conversion or casting. Likewise, casting seems preferable to conversion. Is such as selection/filtering process possible in the future? This is brought up due to my following scenario where input type and priority are not adequate to obtain the desired matching behavior.

%%% Only read on if you want the example. Thanks! -Jay %%%

I’ve implemented three versions of the same op that take different inputs and the same args to find the minimum enclosing circle around a set of points (RealLocalizable). With the following calls to the ops I get the following matcher analysis text [1],[2],&[3] as indicated in the code… (I added the Ops.Geometric.SmallestEnclosingCircle interface as it didn’t exist yet)

        PointSamplerList<IntType> pl = getRandomPoints(maxRadius, xOffset, yOffset, nPoints, rndSeed);
        
        List<RealLocalizable> theList = new Vector<>();
        theList.addAll(pl);
        Collection<RealLocalizable> theCollection = new HashSet<RealLocalizable>();
        theCollection.addAll(pl);
        RealCursor<IntType> theCursor = pl.cursor();
        
        // Analysis in appendix [1]
        UnaryFunctionOp<RealCursor,Circle> opCursor = Functions.unary(IJ2PluginUtility.ij().op(), Ops.Geometric.SmallestEnclosingCircle.class, Circle.class, theCursor, (RealLocalizable) null, false, null);
        Circle retCursor = opCursor.compute1(theCursor);
        
        // Analysis in appendix [2]
        UnaryFunctionOp<List,Circle> opList = Functions.unary(IJ2PluginUtility.ij().op(), Ops.Geometric.SmallestEnclosingCircle.class, Circle.class, theList, (RealLocalizable) null, false, null);
        Circle retList = opList.compute1(theList);
        
        // Analysis in appendix [3]
        UnaryFunctionOp<Collection,Circle> opCollection = Functions.unary(IJ2PluginUtility.ij().op(), Ops.Geometric.SmallestEnclosingCircle.class, Circle.class, theCollection, (RealLocalizable) null, false, null);
        Circle retCollection = opCollection.compute1(theCollection);

As indicated above each one fails to find a “best” op (when using equal priority). When I run each of the ops by requesting the op class specifically (from my project), the ops are found and run successfully. Likewise when given highest priority on an individual basis things are successful (List -> function.ops.DefaultSmallestEnclosingCircle.class; Collection -> function.ops.DefaultSmallestEnclosingCircleofCollection.class; and RealCursor -> function.ops.DefaultSmallestEnclosingCircleofRealCursor.class). Usually play with priority to allow automatic matching of the generic Ops.XXX.YYY interface, but in my case, I can’t because the top candidate in all cases (after conversion) is the op that takes a List (even RealCursor gets converted to a single item List). Potentially allowing conversion from Collection to List is reasonable but potentially I would like control over this and is why I would like a separate op.

Thanks for whatever feedback you might have.

Cheers,

Jay

Appendix of Matching Analyses

[1]

Multiple 'net.imagej.ops.Ops$Geometric$SmallestEnclosingCircle/net.imagej.ops.special.function.UnaryFunctionOp' ops of priority 0.0:
1. (Circle out) =
    function.ops.DefaultSmallestEnclosingCircle(
        List in,
        RealLocalizable center?,
        boolean randomizePointRemoval?,
        int rndSeed?)
2. (Circle out) =
    function.ops.DefaultSmallestEnclosingCircleOfRealCursor(
        RealCursor in,
        RealLocalizable center?,
        boolean randomizePointRemoval?,
        int rndSeed?)

Request:
-    net.imagej.ops.Ops$Geometric$SmallestEnclosingCircle/net.imagej.ops.special.function.UnaryFunctionOp(
        PointSamplerCursor,
        null,
        Boolean,
        null)

Candidates:
1.     (Circle out) =
    function.ops.DefaultSmallestEnclosingCircle(
        List in,
        RealLocalizable center?,
        boolean randomizePointRemoval?,
        int rndSeed?)
2.     (Circle out) =
    function.ops.DefaultSmallestEnclosingCircleOfCollection(
        Collection in,
        RealLocalizable center?,
        boolean randomizePointRemoval?,
        int rndSeed?)
    Argument types do not match: 0: cannot coerce image.roi.PointSamplerCursor -> java.util.Collection<? extends net.imglib2.RealLocalizable>
3.     (Circle out) =
    function.ops.DefaultSmallestEnclosingCircleOfRealCursor(
        RealCursor in,
        RealLocalizable center?,
        boolean randomizePointRemoval?,
        int rndSeed?)

[2]

Multiple 'net.imagej.ops.Ops$Geometric$SmallestEnclosingCircle/net.imagej.ops.special.function.UnaryFunctionOp' ops of priority 0.0:
1. (Circle out) =
    function.ops.DefaultSmallestEnclosingCircle(
        List in,
        RealLocalizable center?,
        boolean randomizePointRemoval?,
        int rndSeed?)
2. (Circle out) =
    function.ops.DefaultSmallestEnclosingCircleOfCollection(
        Collection in,
        RealLocalizable center?,
        boolean randomizePointRemoval?,
        int rndSeed?)

Request:
-    net.imagej.ops.Ops$Geometric$SmallestEnclosingCircle/net.imagej.ops.special.function.UnaryFunctionOp(
        Vector,
        null,
        Boolean,
        null)

Candidates:
1.     (Circle out) =
    function.ops.DefaultSmallestEnclosingCircle(
        List in,
        RealLocalizable center?,
        boolean randomizePointRemoval?,
        int rndSeed?)
2.     (Circle out) =
    function.ops.DefaultSmallestEnclosingCircleOfCollection(
        Collection in,
        RealLocalizable center?,
        boolean randomizePointRemoval?,
        int rndSeed?)
3.     (Circle out) =
    function.ops.DefaultSmallestEnclosingCircleOfRealCursor(
        RealCursor in,
        RealLocalizable center?,
        boolean randomizePointRemoval?,
        int rndSeed?)
    Argument types do not match: 0: cannot coerce java.util.Vector -> net.imglib2.RealCursor<?>

[3]

Multiple 'net.imagej.ops.Ops$Geometric$SmallestEnclosingCircle/net.imagej.ops.special.function.UnaryFunctionOp' ops of priority 0.0:
1. (Circle out) =
    function.ops.DefaultSmallestEnclosingCircle(
        List in,
        RealLocalizable center?,
        boolean randomizePointRemoval?,
        int rndSeed?)
2. (Circle out) =
    function.ops.DefaultSmallestEnclosingCircleOfCollection(
        Collection in,
        RealLocalizable center?,
        boolean randomizePointRemoval?,
        int rndSeed?)

Request:
-    net.imagej.ops.Ops$Geometric$SmallestEnclosingCircle/net.imagej.ops.special.function.UnaryFunctionOp(
        HashSet,
        null,
        Boolean,
        null)

Candidates:
1.     (Circle out) =
    function.ops.DefaultSmallestEnclosingCircle(
        List in,
        RealLocalizable center?,
        boolean randomizePointRemoval?,
        int rndSeed?)
2.     (Circle out) =
    function.ops.DefaultSmallestEnclosingCircleOfCollection(
        Collection in,
        RealLocalizable center?,
        boolean randomizePointRemoval?,
        int rndSeed?)
3.     (Circle out) =
    function.ops.DefaultSmallestEnclosingCircleOfRealCursor(
        RealCursor in,
        RealLocalizable center?,
        boolean randomizePointRemoval?,
        int rndSeed?)
    Argument types do not match: 0: cannot coerce java.util.HashSet -> net.imglib2.RealCursor<?>

Yes, this is planned: imagej/imagej-ops#103.

Thanks @ctrueden.

Cheers,

Jay