use of com.sri.ai.expresso.type.TupleType in project aic-expresso by aic-sri-international.
the class TupleRewriterTest method testTupleValuedFreeVariablesSimplifier.
@Test
public void testTupleValuedFreeVariablesSimplifier() {
Context tupleTheoryContext = new TrueContext(new CompoundTheory(new DifferenceArithmeticTheory(false, false), new TupleTheory()));
TupleType nTupleType = new TupleType(new IntegerInterval(1, 10), new IntegerInterval(1, 10));
tupleTheoryContext = (Context) GrinderUtil.extendRegistryWith(map("N", nTupleType.toString()), Arrays.asList(nTupleType), tupleTheoryContext);
TupleValuedFreeVariablesSimplifier simplifier = new TupleValuedFreeVariablesSimplifier();
Expression expression = parse("sum( {{ (on X in 1..10) if N = (2, X) then 2 else 3 }} )");
Expression simplified = simplifier.apply(expression, tupleTheoryContext);
Assert.assertEquals(parse("if get(N, 1) = 2 then 29 else 30"), simplified);
}
use of com.sri.ai.expresso.type.TupleType in project aic-expresso by aic-sri-international.
the class TupleValuedFreeVariablesSimplifier method simplify.
public static Expression simplify(Expression expression, Context context) {
Expression result = expression;
// First see if we have any free variables.
Map<Expression, Expression> freeVariablesAndTypes = Expressions.freeVariablesAndTypes(expression, context);
if (freeVariablesAndTypes.size() > 0) {
// Retrieve those that are tuples
Map<Expression, TupleType> freeVariablesOfTupleType = freeVariablesAndTypes.entrySet().stream().filter(entry -> entry.getValue() != null && TupleType.isTupleType(entry.getValue())).collect(Collectors.toMap(e -> e.getKey(), e -> (TupleType) GrinderUtil.fromTypeExpressionToItsIntrinsicMeaning(e.getValue(), context)));
if (freeVariablesOfTupleType.size() > 0) {
final Map<Expression, List<Pair<Expression, Integer>>> freeVariableComponentsMap = constructComponentMap(freeVariablesOfTupleType, expression, context);
// Replace the free tuple variables with their componentised forms
// e.g. N --> (N1, N2)
Expression componentisedExpression = expression.replaceAllOccurrences(expr -> {
Expression replacement = expr;
List<Pair<Expression, Integer>> replacementComponents = freeVariableComponentsMap.get(expr);
if (replacementComponents != null) {
replacement = constructComponentTuple(replacementComponents);
}
return replacement;
}, context);
// Evaluate the expression with the un-componentized free tuple variables, within an extended
// context that knows about the newly componentized variables
Context contextExtendedWithComponentVariables = extendContextWithComponentVariables(context, freeVariablesOfTupleType, freeVariableComponentsMap);
Expression evaluatedResult = context.getTheory().evaluate(componentisedExpression, contextExtendedWithComponentVariables);
// Translate back the free variable components
// e.g:
// if N1 = 2 then 29 else 30
// ---->
// if get(N, 1) = 2 then 29 else 30
final Map<Expression, Pair<Expression, Integer>> componentToFreeVariableMap = createReverseLookupMap(freeVariableComponentsMap);
result = evaluatedResult.replaceAllOccurrences(expr -> {
Expression replacement = expr;
Pair<Expression, Integer> correspondingFreeVariableWithIndex = componentToFreeVariableMap.get(expr);
if (correspondingFreeVariableWithIndex != null) {
replacement = Expressions.apply(FunctorConstants.GET, correspondingFreeVariableWithIndex.first, correspondingFreeVariableWithIndex.second);
}
return replacement;
}, contextExtendedWithComponentVariables);
}
}
return result;
}
use of com.sri.ai.expresso.type.TupleType in project aic-expresso by aic-sri-international.
the class TupleValuedFreeVariablesSimplifier method constructComponentMap.
private static Map<Expression, List<Pair<Expression, Integer>>> constructComponentMap(Map<Expression, TupleType> freeVariablesOfTupleType, Expression expression, Context context) {
Map<Expression, List<Pair<Expression, Integer>>> result = new LinkedHashMap<>();
for (Map.Entry<Expression, TupleType> freeVariableOfTupleType : freeVariablesOfTupleType.entrySet()) {
Expression freeVariable = freeVariableOfTupleType.getKey();
TupleType freeVariableTupleType = freeVariableOfTupleType.getValue();
List<Pair<Expression, Integer>> components = new ArrayList<>();
int tupleArity = freeVariableTupleType.getArity();
for (int i = 1; i <= tupleArity; i++) {
String proposedComponentVariableName = freeVariable.toString() + i;
Expression componentVariable = Expressions.makeUniqueVariable(proposedComponentVariableName, expression, context);
components.add(new Pair<>(componentVariable, i));
}
result.put(freeVariable, components);
}
return result;
}
use of com.sri.ai.expresso.type.TupleType in project aic-expresso by aic-sri-international.
the class GrinderUtil method fromTypeExpressionToItsIntrinsicMeaning.
/**
* A method mapping type expressions to their intrinsic {@link Type} objects,
* where "intrinsic" means there is only one possible {@link Type} object
* for them in the registry of grinder
* (therefore, it cannot be used for, say, categorical types defined
* by the user and registered in the registry by name only).
* Current recognized type expressions are
* <code>Boolean</code>, <code>Integer</code>, and function applications
* of the type <code>model..n</code>.
* If there is no such meaning, the method returns <code>null</code>.
* @param typeExpression
* @param registry TODO
* @return
*/
public static Type fromTypeExpressionToItsIntrinsicMeaning(Expression typeExpression, Registry registry) throws Error {
Type type;
if (typeExpression.equals("Boolean")) {
type = BOOLEAN_TYPE;
} else if (typeExpression.equals("Integer")) {
type = INTEGER_TYPE;
} else if (typeExpression.equals("Real")) {
type = REAL_TYPE;
} else if (typeExpression.hasFunctor(INTEGER_INTERVAL) && typeExpression.numberOfArguments() == 2) {
type = new IntegerInterval(typeExpression.get(0), typeExpression.get(1));
} else if ((typeExpression.hasFunctor(FunctorConstants.REAL_INTERVAL_CLOSED_CLOSED) || typeExpression.hasFunctor(FunctorConstants.REAL_INTERVAL_OPEN_CLOSED) || typeExpression.hasFunctor(FunctorConstants.REAL_INTERVAL_CLOSED_OPEN) || typeExpression.hasFunctor(FunctorConstants.REAL_INTERVAL_OPEN_OPEN)) && typeExpression.numberOfArguments() == 2) {
type = new RealInterval(typeExpression.toString());
} else if (FunctionType.isFunctionType(typeExpression)) {
Function<Expression, Type> getType = e -> registry.getTypeFromTypeExpression(e);
Type codomain = getType.apply(FunctionType.getCodomain(typeExpression));
List<Expression> argumentTypeExpressions = FunctionType.getArgumentList(typeExpression);
ArrayList<Type> argumentTypes = mapIntoArrayList(argumentTypeExpressions, getType);
Type[] argumentTypesArray = new Type[argumentTypes.size()];
type = new FunctionType(codomain, argumentTypes.toArray(argumentTypesArray));
} else if (TupleType.isTupleType(typeExpression)) {
List<Type> elementTypes = typeExpression.getArguments().stream().map(elementTypeExpression -> registry.getTypeFromTypeExpression(elementTypeExpression)).collect(Collectors.toList());
type = new TupleType(elementTypes);
} else {
type = null;
}
return type;
}
use of com.sri.ai.expresso.type.TupleType in project aic-expresso by aic-sri-international.
the class GrinderUtil method getTypeExpressionOfExpression.
/**
* Returns the type of given expression according to registry.
*/
public static Expression getTypeExpressionOfExpression(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 = getTypeExpressionOfExpression(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 = getTypeExpressionOfExpression(IfThenElse.thenBranch(expression), registry);
Expression elseType = getTypeExpressionOfExpression(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 -> getTypeExpressionOfExpression(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 = getTypeExpressionOfExpression(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.getTypeExpressionOfRegisteredSymbol(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 = getTypeExpressionOfExpression(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 = getTypeExpressionOfExpression(expression.getFunctor(), registry);
if (functionType == null) {
throw new Error("Type of '" + expression.getFunctor() + "' required but unknown.");
}
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.getTypeFromTypeExpression(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 -> getTypeExpressionOfExpression(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 = getTypeExpressionOfExpression(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 = getTypeExpressionOfExpression(lambdaExpression.getBody(), lambdaExpressionWithABodyRegistry);
result = Expressions.apply(FUNCTION_TYPE, domain, coDomain);
} else if (expression instanceof AbstractExpressionWrapper) {
Expression innerExpression = ((AbstractExpressionWrapper) expression).getInnerExpression();
result = getTypeExpressionOfExpression(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;
}
Aggregations