use of com.sri.ai.expresso.type.FunctionType in project aic-expresso by aic-sri-international.
the class GrinderUtil method getTypeExpression.
/**
* Returns the type of given expression according to registry.
*/
public static Expression getTypeExpression(Expression expression, Registry registry) {
Expression result;
if (FormulaUtil.isApplicationOfBooleanConnective(expression)) {
result = makeSymbol("Boolean");
} else if (expression.getSyntacticFormType().equals(FunctionApplication.SYNTACTIC_FORM_TYPE) && list(SUM, PRODUCT, MAX).contains(expression.getFunctor().toString())) {
Expression argument = expression.get(0);
if (argument.getSyntacticFormType().equals(IntensionalSet.SYNTACTIC_FORM_TYPE)) {
IntensionalSet intensionalSetArgument = (IntensionalSet) argument;
Expression head = intensionalSetArgument.getHead();
// NOTE: Need to extend the registry as the index expressions in the quantifier may
// declare new types (i.e. function types).
Registry headRegistry = registry.extendWith(intensionalSetArgument.getIndexExpressions());
result = getTypeExpression(head, headRegistry);
} else if (argument.getSyntacticFormType().equals(ExtensionalSets.SYNTACTIC_FORM_TYPE)) {
List<Expression> arguments = ((AbstractExtensionalSet) argument).getElementsDefinitions();
result = getTypeOfCollectionOfNumericExpressionsWithDefaultInteger(arguments, registry);
} else if (expression.hasFunctor(MAX)) {
// MAX can also be applied to a bunch of numbers
result = getTypeOfCollectionOfNumericExpressionsWithDefaultInteger(expression.getArguments(), registry);
} else {
throw new Error(expression.getFunctor() + " defined for sets only but got " + expression.get(0));
}
} else if (Equality.isEquality(expression) || Disequality.isDisequality(expression)) {
result = makeSymbol("Boolean");
} else if (expression.equals(FunctorConstants.REAL_INTERVAL_CLOSED_CLOSED) || expression.equals(FunctorConstants.REAL_INTERVAL_CLOSED_OPEN) || expression.equals(FunctorConstants.REAL_INTERVAL_OPEN_CLOSED) || expression.equals(FunctorConstants.REAL_INTERVAL_OPEN_OPEN)) {
result = FunctionType.make(parse("Set"), parse("Number"), parse("Number"));
} else if (IfThenElse.isIfThenElse(expression)) {
Expression thenType = getTypeExpression(IfThenElse.thenBranch(expression), registry);
Expression elseType = getTypeExpression(IfThenElse.elseBranch(expression), registry);
if (thenType != null && elseType != null && (thenType.equals("Number") && isIntegerOrReal(elseType) || isIntegerOrReal(thenType) && elseType.equals("Number"))) {
result = makeSymbol("Number");
} else if (thenType != null && elseType != null && (thenType.equals("Integer") && elseType.equals("Real") || thenType.equals("Real") && elseType.equals("Integer"))) {
result = makeSymbol("Real");
} else if (thenType != null && (elseType == null || thenType.equals(elseType))) {
result = thenType;
} else if (elseType != null && (thenType == null || elseType.equals(thenType))) {
result = elseType;
} else if (thenType == null) {
throw new Error("Could not determine the types of then and else branches of '" + expression + "'.");
} else if (thenType.equals("Integer") && elseType.hasFunctor(INTEGER_INTERVAL)) {
// TODO: I know, I know, this treatment of integers and interval is terrible... will fix at some point
result = thenType;
} else if (thenType.hasFunctor(INTEGER_INTERVAL) && elseType.equals("Integer")) {
result = elseType;
} else if (thenType.hasFunctor(INTEGER_INTERVAL) && elseType.hasFunctor(INTEGER_INTERVAL)) {
IntegerInterval thenInterval = (IntegerInterval) thenType;
IntegerInterval elseInterval = (IntegerInterval) elseType;
Expression minimumLowerBound = LessThan.simplify(apply(LESS_THAN, thenInterval.getNonStrictLowerBound(), elseInterval.getNonStrictLowerBound()), registry).booleanValue() ? thenInterval.getNonStrictLowerBound() : elseInterval.getNonStrictLowerBound();
Expression maximumUpperBound = GreaterThan.simplify(apply(GREATER_THAN, thenInterval.getNonStrictUpperBound(), elseInterval.getNonStrictUpperBound()), registry).booleanValue() ? thenInterval.getNonStrictUpperBound() : elseInterval.getNonStrictUpperBound();
if (minimumLowerBound.equals(MINUS_INFINITY) && maximumUpperBound.equals(INFINITY)) {
result = makeSymbol("Integer");
} else {
result = apply(INTEGER_INTERVAL, minimumLowerBound, maximumUpperBound);
}
} else {
throw new Error("'" + expression + "' then and else branches have different types (" + thenType + " and " + elseType + " respectively).");
}
} else if (isCardinalityExpression(expression)) {
result = makeSymbol("Integer");
} else if (isNumericFunctionApplication(expression)) {
List<Expression> argumentTypes = mapIntoList(expression.getArguments(), e -> getTypeExpression(e, registry));
int firstNullArgumentTypeIndexIfAny = Util.getIndexOfFirstSatisfyingPredicateOrMinusOne(argumentTypes, t -> t == null);
if (firstNullArgumentTypeIndexIfAny != -1) {
throw new Error("Cannot determine type of " + expression.getArguments().get(firstNullArgumentTypeIndexIfAny) + ", which is needed for determining type of " + expression);
}
/**
* commonDomain is the co-domain shared by all argument function types, or empty tuple for arguments that are not function-typed.
* Therefore, if no argument is function-typed, it will be equal to the empty tuple.
*/
Expression commonDomain = getCommonDomainIncludingConversionOfNonFunctionTypesToNullaryFunctions(argumentTypes, registry);
if (commonDomain == null) {
throw new Error("Operator " + expression.getFunctor() + " applied to arguments of non-compatible types: " + expression + ", types of arguments are " + argumentTypes);
}
boolean noArgumentIsFunctionTyped = commonDomain.equals(EMPTY_TUPLE) && !thereExists(argumentTypes, t -> t.hasFunctor(FunctorConstants.FUNCTION_TYPE));
Expression resultCoDomain;
if (thereExists(argumentTypes, t -> Util.equals(getCoDomainOrItself(t), "Number"))) {
resultCoDomain = makeSymbol("Number");
} else if (thereExists(argumentTypes, t -> Util.equals(getCoDomainOrItself(t), "Real"))) {
resultCoDomain = makeSymbol("Real");
} else if (thereExists(argumentTypes, t -> isRealInterval(getCoDomainOrItself(t)))) {
resultCoDomain = makeSymbol("Real");
} else {
resultCoDomain = makeSymbol("Integer");
}
if (noArgumentIsFunctionTyped) {
result = resultCoDomain;
} else {
result = apply(FUNCTION_TYPE, commonDomain, resultCoDomain);
}
} else if (expression.hasFunctor(FunctorConstants.INTEGER_INTERVAL) || expression.hasFunctor(FunctorConstants.REAL_INTERVAL_CLOSED_CLOSED) || expression.hasFunctor(FunctorConstants.REAL_INTERVAL_OPEN_CLOSED) || expression.hasFunctor(FunctorConstants.REAL_INTERVAL_CLOSED_OPEN) || expression.hasFunctor(FunctorConstants.REAL_INTERVAL_OPEN_OPEN)) {
result = makeSymbol("Set");
} else if (isComparisonFunctionApplication(expression)) {
result = makeSymbol("Boolean");
} else if (expression.hasFunctor(FunctorConstants.FUNCTION_TYPE)) {
// very vague type for now
result = apply(FUNCTION_TYPE, makeSymbol("Set"), makeSymbol("Set"));
} else if (Sets.isIntensionalMultiSet(expression)) {
IntensionalSet set = (IntensionalSet) expression;
// NOTE: Need to extend the registry as the index expressions in the quantifier may
// declare new types (i.e. function types).
Registry headRegistry = registry.extendWith(set.getIndexExpressions());
Expression headType = getTypeExpression(set.getHead(), headRegistry);
result = new DefaultIntensionalMultiSet(list(), headType, TRUE);
} else if (Sets.isExtensionalSet(expression)) {
// very vague type for now
result = apply(FUNCTION_TYPE, makeSymbol("Set"));
} else if (expression.hasFunctor(FunctorConstants.INTERSECTION) || expression.hasFunctor(FunctorConstants.UNION) || expression.hasFunctor(FunctorConstants.INTENSIONAL_UNION)) {
// very vague type for now
result = apply(FUNCTION_TYPE, makeSymbol("Set"));
} else if (expression.getSyntacticFormType().equals(Symbol.SYNTACTIC_FORM_TYPE)) {
if (expression.getValue() instanceof Integer) {
result = makeSymbol("Integer");
} else if (expression.getValue() instanceof Double) {
result = makeSymbol("Real");
} else if (expression.getValue() instanceof Rational) {
Rational rational = (Rational) expression.getValue();
boolean isInteger = rational.isInteger();
result = makeSymbol(isInteger ? "Integer" : "Real");
} else if (expression.getValue() instanceof Number) {
result = makeSymbol("Number");
} else if (expression.getValue() instanceof String && expression.isStringLiteral()) {
result = makeSymbol("String");
} else if (expression.getValue() instanceof Boolean) {
result = makeSymbol("Boolean");
} else if (expression.equals(Expressions.INFINITY) || expression.equals(Expressions.MINUS_INFINITY)) {
result = makeSymbol("Number");
} else {
result = registry.getTypeOfRegisteredSymbol(expression);
if (result == null) {
Type type = getFirstSatisfyingPredicateOrNull(registry.getTypes(), t -> t.contains(expression));
if (type != null) {
result = parse(type.getName());
}
}
}
} else if (expression.hasFunctor(FunctorConstants.GET) && expression.numberOfArguments() == 2 && Expressions.isNumber(expression.get(1))) {
Expression argType = getTypeExpression(expression.get(0), registry);
if (TupleType.isTupleType(argType)) {
TupleType tupleType = (TupleType) GrinderUtil.fromTypeExpressionToItsIntrinsicMeaning(argType, registry);
result = parse(tupleType.getElementTypes().get(expression.get(1).intValue() - 1).toString());
} else {
throw new Error("get type from tuple for '" + expression + "' currently not supported.");
}
} else if (expression.hasFunctor(FunctorConstants.TUPLE_TYPE)) {
// Is a type expression already.
result = expression;
} else if (expression.getSyntacticFormType().equals(FunctionApplication.SYNTACTIC_FORM_TYPE)) {
Expression functionType = getTypeExpression(expression.getFunctor(), registry);
if (functionType == null) {
throw new Error("Type of '" + expression.getFunctor() + "' required, but unknown to registry.");
}
Expression coDomain = FunctionType.getCodomain(functionType);
List<Expression> argumentsTypesList = FunctionType.getArgumentList(functionType);
if (expression.getArguments().size() != argumentsTypesList.size()) {
throw new Error("Function " + expression.getFunctor() + " is of type " + functionType + " but has incorrect number of arguments = " + expression.getArguments());
}
for (int idx = 0; idx < expression.getArguments().size(); idx++) {
Expression arg = expression.get(idx);
Expression argExprType = argumentsTypesList.get(idx);
Type argType = registry.getType(argExprType);
if (!isSubtypeOf(arg, argType, registry)) {
throw new Error("Function " + expression.getFunctor() + " is of type " + functionType + " but has arguments that are not legal subtypes [#" + idx + "] = " + expression.getArguments());
}
}
result = coDomain;
} else if (Tuple.isTuple(expression)) {
List<Expression> elementTypes = expression.getArguments().stream().map(element -> getTypeExpression(element, registry)).collect(Collectors.toList());
result = TupleType.make(elementTypes);
} else if (expression instanceof QuantifiedExpressionWithABody) {
QuantifiedExpressionWithABody quantifiedExpressionWithABody = (QuantifiedExpressionWithABody) expression;
// NOTE: Need to extend the registry as the index expressions in the quantifier may
// declare new types (i.e. function types).
Registry quantifiedExpressionWithABodyRegistry = registry.extendWith(quantifiedExpressionWithABody.getIndexExpressions());
result = getTypeExpression(quantifiedExpressionWithABody.getBody(), quantifiedExpressionWithABodyRegistry);
} else if (expression instanceof LambdaExpression) {
LambdaExpression lambdaExpression = (LambdaExpression) expression;
Collection<Expression> domain = IndexExpressions.getIndexDomainsOfQuantifiedExpression(lambdaExpression);
IndexExpressionsSet indexExpressions = lambdaExpression.getIndexExpressions();
Registry lambdaExpressionWithABodyRegistry = registry.extendWith(indexExpressions);
Expression coDomain = getTypeExpression(lambdaExpression.getBody(), lambdaExpressionWithABodyRegistry);
result = Expressions.apply(FUNCTION_TYPE, domain, coDomain);
} else if (expression instanceof AbstractExpressionWrapper) {
Expression innerExpression = ((AbstractExpressionWrapper) expression).getInnerExpression();
result = getTypeExpression(innerExpression, registry);
} else {
throw new Error("GrinderUtil.getType does not yet know how to determine the type of this sort of expression: " + expression);
}
return result;
}
use of com.sri.ai.expresso.type.FunctionType in project aic-expresso by aic-sri-international.
the class InversionSimplifier method applyInversion.
private static Expression applyInversion(Expression summationIndexedByFunction, Expression summationIndexFunctionName, FunctionType summationIndexFunctionType, List<Expression> originalQuantifierOrder, List<Expression> inversionQuantifierOrder, Context context) {
Expression result;
int indexOfSummationIndexedByFunction = inversionQuantifierOrder.indexOf(summationIndexedByFunction);
// NOTE: only products will bubble up before the summation.
List<Expression> productsBefore = inversionQuantifierOrder.subList(0, indexOfSummationIndexedByFunction);
Expression lastQuantifierHead = getHead(originalQuantifierOrder.get(originalQuantifierOrder.size() - 1));
Context innerContext = context;
for (Expression quantifier : originalQuantifierOrder) {
innerContext = innerContext.extendWith(getIndexExpressions(quantifier));
}
Context lastQuantifierHeadContext = innerContext;
// TODO - remove temporary hack which collapses the function's argument domains
// due to all the domain arguments being treated as constants. Instead should be using
// set expression types on singletons.
// Determine which domain arguments from the summation function can be collapsed
List<Expression> productsBeforeIndices = new ArrayList<>();
for (Expression productBefore : productsBefore) {
productsBeforeIndices.add(getIndexAndType(productBefore).first);
}
Set<Integer> domainArgsToRemove = new HashSet<>();
Set<Integer> domainArgsHaveVar = new HashSet<>();
new SubExpressionsDepthFirstIterator(lastQuantifierHead).forEachRemaining(e -> {
if (e.hasFunctor(summationIndexFunctionName)) {
for (int i = 0; i < e.numberOfArguments(); i++) {
if (lastQuantifierHeadContext.getTheory().isVariable(e.get(0), lastQuantifierHeadContext)) {
domainArgsHaveVar.add(i);
}
if (productsBeforeIndices.contains(e.get(i)) || Util.thereExists(new SubExpressionsDepthFirstIterator(e.get(i)), es -> productsBeforeIndices.contains(es))) {
domainArgsToRemove.add(i);
}
}
}
});
// Remove arg positions that were not populated by a variable.
for (int i = 0; i < summationIndexFunctionType.getArity(); i++) {
if (!domainArgsHaveVar.contains(i)) {
domainArgsToRemove.add(i);
}
}
List<Expression> argTypes = new ArrayList<>();
for (int i = 0; i < summationIndexFunctionType.getArity(); i++) {
if (!domainArgsToRemove.contains(i)) {
argTypes.add(parse(summationIndexFunctionType.getArgumentTypes().get(i).getName()));
}
}
Expression codomainType = parse(summationIndexFunctionType.getCodomain().getName());
Expression summationIndexReducedType;
if (argTypes.size() == 0) {
summationIndexReducedType = codomainType;
} else {
summationIndexReducedType = FunctionType.make(codomainType, argTypes);
}
Expression summationIndex = IndexExpressions.makeIndexExpression(summationIndexFunctionName, summationIndexReducedType);
Expression phi = lastQuantifierHead.replaceAllOccurrences(e -> {
Expression r = e;
if (e.hasFunctor(summationIndexFunctionName)) {
List<Expression> argsToKeep = new ArrayList<>();
for (int i = 0; i < e.numberOfArguments(); i++) {
if (!domainArgsToRemove.contains(i)) {
argsToKeep.add(e.get(i));
}
}
if (argsToKeep.size() > 0) {
r = Expressions.apply(summationIndexFunctionName, argsToKeep);
} else {
r = summationIndexFunctionName;
}
}
return r;
}, lastQuantifierHeadContext);
Expression summationHead = phi;
for (int i = indexOfSummationIndexedByFunction + 1; i < inversionQuantifierOrder.size(); i++) {
Expression quantifier = inversionQuantifierOrder.get(i);
Expression quantifierIntensionalSet = IntensionalSet.intensionalMultiSet(getIndexExpressions(quantifier), summationHead, getCondition(quantifier));
summationHead = Expressions.apply(quantifier.getFunctor(), quantifierIntensionalSet);
}
Expression innerSummation = IntensionalSet.intensionalMultiSet(new ExtensionalIndexExpressionsSet(summationIndex), summationHead, getCondition(summationIndexedByFunction));
result = Expressions.apply(FunctorConstants.SUM, innerSummation);
for (int i = productsBefore.size() - 1; i >= 0; i--) {
Expression product = productsBefore.get(i);
product = IntensionalSet.intensionalMultiSet(getIndexExpressions(product), result, getCondition(product));
result = Expressions.apply(FunctorConstants.PRODUCT, product);
}
return result;
}
use of com.sri.ai.expresso.type.FunctionType in project aic-expresso by aic-sri-international.
the class InversionSimplifier method getIndexAndFunctionType.
private static Pair<Expression, FunctionType> getIndexAndFunctionType(Expression functionOnIntensionalSet, Context context) {
IndexExpressionsSet indexExpressionsSet = getIndexExpressions(functionOnIntensionalSet);
List<Expression> indices = IndexExpressions.getIndices(indexExpressionsSet);
if (indices.size() != 1) {
throw new UnsupportedOperationException("Currently only support singular indices");
}
Expression index = indices.get(0);
Context intensionalSetContext = context.extendWith(indexExpressionsSet);
Type type = GrinderUtil.getType(index, intensionalSetContext);
FunctionType functionType = null;
if (type instanceof FunctionType) {
functionType = (FunctionType) type;
}
Pair<Expression, FunctionType> result = new Pair<>(index, functionType);
return result;
}
use of com.sri.ai.expresso.type.FunctionType in project aic-expresso by aic-sri-international.
the class InversionSimplifier method simplify.
public static Expression simplify(Expression expression, Context context) {
Expression result = expression;
if (isSummationIndexedByFunctionOfQuantifiers(expression, context)) {
// NOTE: at this point we know we have a summation indexed by a function
Expression summationIndexedByFunction = expression;
Pair<Expression, FunctionType> indexAndFunctionType = getIndexAndFunctionType(summationIndexedByFunction, context);
Expression summationIndexFunctionName = indexAndFunctionType.first;
FunctionType summationIndexFunctionType = indexAndFunctionType.second;
List<Expression> originalQuantifierOrder = new ArrayList<>();
collectQuantifiers(summationIndexedByFunction, originalQuantifierOrder);
List<Expression> inversionQuantifierOrder = new ArrayList<>();
if (isInversionPossible(summationIndexedByFunction, summationIndexFunctionName, summationIndexFunctionType, originalQuantifierOrder, inversionQuantifierOrder, context)) {
result = applyInversion(summationIndexedByFunction, summationIndexFunctionName, summationIndexFunctionType, originalQuantifierOrder, inversionQuantifierOrder, context);
}
}
return result;
}
use of com.sri.ai.expresso.type.FunctionType in project aic-expresso by aic-sri-international.
the class SetOfArgumentTuplesForFunctionOccurringInExpression method computeUnionArgs.
private static void computeUnionArgs(List<Expression> unionArgs, Predicate<Expression> isF, FunctionType fType, Predicate<Expression> isFApplication, Expression e) {
// if E does not contain ƒ, oc<sub>ƒ</sub>[E] is ∅
if (!containsF(e, isF)) {
unionArgs.add(Sets.EMPTY_SET);
} else // if E is ƒ(t) for t a tuple, oc<sub>ƒ</sub>[E] is {t}
if (isFApplication.apply(e)) {
Expression tupleT = Expressions.makeTuple(e.getArguments());
Expression setT = ExtensionalSets.makeUniSet(tupleT);
unionArgs.add(setT);
} else // if E is ƒ, oc<sub>ƒ</sub>[E] is Α
if (isF.apply(e)) {
List<Expression> domainTypes = new ArrayList<>();
for (Type domainType : fType.getArgumentTypes()) {
domainTypes.add(Expressions.parse(domainType.getName()));
}
Expression tupleT = Expressions.makeTuple(domainTypes);
Expression setT = ExtensionalSets.makeUniSet(tupleT);
unionArgs.add(setT);
} else // if E is g(E′) for g a function symbol distinct from ƒ and t a k-tuple of expressions
if (Expressions.isFunctionApplicationWithArguments(e)) {
// if g(E′) is if C the E<sub>1</sub> else E<sub>2</sub> and C does not contain ƒ
if (IfThenElse.isIfThenElse(e) && !containsF(IfThenElse.condition(e), isF)) {
// then oc<sub>ƒ</sub>[E] is<br>
// if C then oc<sub>ƒ</sub>[E<sub>1</sub>] else oc<sub>ƒ</sub>[E<sub>2</sub>]
Expression e1 = IfThenElse.thenBranch(e);
List<Expression> thenUnionArgs = new ArrayList<>();
computeUnionArgs(thenUnionArgs, isF, fType, isFApplication, e1);
Expression thenBranch = makeUnion(thenUnionArgs);
Expression e2 = IfThenElse.elseBranch(e);
List<Expression> elseUnionArgs = new ArrayList<>();
computeUnionArgs(elseUnionArgs, isF, fType, isFApplication, e2);
Expression elseBranch = makeUnion(elseUnionArgs);
Expression ifThenElse = IfThenElse.make(IfThenElse.condition(e), thenBranch, elseBranch);
unionArgs.add(ifThenElse);
} else {
// oc<sub>ƒ</sub>[t<sub>1</sub>] ∪ … ∪ oc<sub>ƒ</sub>[t<sub>k</sub>]
for (Expression t : e.getArguments()) {
computeUnionArgs(unionArgs, isF, fType, isFApplication, t);
}
}
} else // quantified expression,
if (isArbitraryQuantifier(e) && !containsF(getQuantifierCondition(e), isF)) {
// then oc<sub>ƒ</sub>[E] is<br>
// ⋃<sub>x:C</sub>oc<sub>ƒ</sub>[E′]
QuantifiedExpression q = (QuantifiedExpression) e;
IndexExpressionsSet x = q.getIndexExpressions();
Expression c = getQuantifierCondition(e);
Expression ePrime = getQuantifiedExpression(q);
List<Expression> ePrimeUnionArgs = new ArrayList<>();
computeUnionArgs(ePrimeUnionArgs, isF, fType, isFApplication, ePrime);
Expression ocfOfEPrime = makeUnion(ePrimeUnionArgs);
Expression intensionalMultiSet = IntensionalSet.intensionalMultiSet(x, ocfOfEPrime, c);
Expression intensionalUnion = Expressions.apply(FunctorConstants.INTENSIONAL_UNION, intensionalMultiSet);
unionArgs.add(intensionalUnion);
} else // quantified expression,
if (isArbitraryQuantifier(e)) {
// then oc<sub>ƒ</sub>[E] is<br>
// ⋃<sub>x</sub>(oc<sub>ƒ</sub>[C] ∪ oc<sub>ƒ</sub>[E′])
QuantifiedExpression q = (QuantifiedExpression) e;
IndexExpressionsSet x = q.getIndexExpressions();
Expression c = getQuantifierCondition(e);
Expression ePrime = getQuantifiedExpression(q);
List<Expression> conditionUnionArgs = new ArrayList<>();
computeUnionArgs(conditionUnionArgs, isF, fType, isFApplication, c);
List<Expression> ePrimeUnionArgs = new ArrayList<>();
computeUnionArgs(ePrimeUnionArgs, isF, fType, isFApplication, ePrime);
List<Expression> combinedUnionArgs = new ArrayList<>();
combinedUnionArgs.addAll(conditionUnionArgs);
combinedUnionArgs.addAll(ePrimeUnionArgs);
Expression combinedUnion = makeUnion(combinedUnionArgs);
Expression intensionalMultiSet = IntensionalSet.intensionalMultiSet(x, combinedUnion, Expressions.TRUE);
Expression intensionalUnion = Expressions.apply(FunctorConstants.INTENSIONAL_UNION, intensionalMultiSet);
unionArgs.add(intensionalUnion);
} else {
throw new UnsupportedOperationException("Do not have logic for handling expression of the form: " + e);
}
}
Aggregations