Search in sources :

Example 6 with Context

use of com.sri.ai.grinder.api.Context in project aic-expresso by aic-sri-international.

the class SGDPLLTTester method runGroupProblemSolvingTestGivenConstraintAndProblem.

/**
 * @param problem
 * @param indices
 * @param constraint
 * @param body
 * @param testAgainstBruteForce
 * @param theoryTestingSupport
 * @param context
 * @throws Error
 */
public static void runGroupProblemSolvingTestGivenConstraintAndProblem(Expression problem, Collection<Expression> indices, Constraint constraint, Expression body, boolean testAgainstBruteForce, TheoryTestingSupport theoryTestingSupport, Context context) throws Error {
    Theory theory = theoryTestingSupport.getTheory();
    Collection<Expression> freeVariables = getFreeVariableMinusIndices(indices, constraint, body, context);
    String problemDescription = problem.toString();
    output(problemDescription);
    Simplifier symbolicInterpreter = (e, c) -> theory.evaluate(e, c);
    long start = System.currentTimeMillis();
    Expression symbolicSolution = symbolicInterpreter.apply(problem, context);
    long time = System.currentTimeMillis() - start;
    output("Symbolic solution: " + symbolicSolution);
    output("Computed in " + time + " ms");
    if (Util.thereExists(new SubExpressionsDepthFirstIterator(symbolicSolution), e -> e instanceof QuantifiedExpression || Sets.isIntensionalSet(e))) {
        throw new Error("Symbolic solution is not quantifier-free: " + symbolicSolution);
    }
    if (testAgainstBruteForce) {
        BinaryFunction<BruteForceCommonInterpreter, Context, Expression> bruteForceSolutionGivenInterpreterAndContextWithAssignmentToOtherVariables = (i, c) -> i.apply(problem, c);
        testSymbolicVsBruteForceComputationForEachAssignment(theory, problemDescription, freeVariables, symbolicSolution, bruteForceSolutionGivenInterpreterAndContextWithAssignmentToOtherVariables, context);
    // A more elegant approach would be to create a "for all free variables : symbolic = problem" expression
    // and solve it by brute force instead of using testSymbolicVsBruteForceComputation
    // which replicates the brute force interpreter to some extent.
    // The reason we do not do this is simply due to the fact that the brute force interpreter would return "false"
    // in case of failure, without indicating which assignment failed, which is very useful for debugging.
    // If interpreters, and in fact the whole framework, provided proofs of its calculations,
    // then we could simply use the more elegant approach.
    } else {
        output("Skipping test againt brute-force.");
    }
}
Also used : BinaryFunction(com.sri.ai.util.base.BinaryFunction) Util.removeFromSetNonDestructively(com.sri.ai.util.Util.removeFromSetNonDestructively) SubExpressionsDepthFirstIterator(com.sri.ai.expresso.helper.SubExpressionsDepthFirstIterator) Expression(com.sri.ai.expresso.api.Expression) Sets(com.sri.ai.grinder.library.set.Sets) Function(java.util.function.Function) GrinderUtil(com.sri.ai.grinder.helper.GrinderUtil) And(com.sri.ai.grinder.library.boole.And) Util.in(com.sri.ai.util.Util.in) AssociativeCommutativeGroup(com.sri.ai.grinder.group.AssociativeCommutativeGroup) BruteForceCommonInterpreter(com.sri.ai.grinder.interpreter.BruteForceCommonInterpreter) IndexExpressions(com.sri.ai.grinder.library.indexexpression.IndexExpressions) Expressions.parse(com.sri.ai.expresso.helper.Expressions.parse) Map(java.util.Map) ThereExists(com.sri.ai.grinder.library.boole.ThereExists) Context(com.sri.ai.grinder.api.Context) ZERO(com.sri.ai.expresso.helper.Expressions.ZERO) SingleQuantifierEliminationProblem(com.sri.ai.grinder.api.SingleQuantifierEliminationProblem) LinkedHashSet(java.util.LinkedHashSet) Type(com.sri.ai.expresso.api.Type) DefaultSingleQuantifierEliminationProblem(com.sri.ai.grinder.core.solver.DefaultSingleQuantifierEliminationProblem) NullaryFunction(com.sri.ai.util.base.NullaryFunction) Util.join(com.sri.ai.util.Util.join) Expressions.getVariablesBeingReferenced(com.sri.ai.expresso.helper.Expressions.getVariablesBeingReferenced) Collection(java.util.Collection) Util.list(com.sri.ai.util.Util.list) DefaultMultiVariableConstraint(com.sri.ai.grinder.core.constraint.DefaultMultiVariableConstraint) Set(java.util.Set) CompleteMultiVariableContext(com.sri.ai.grinder.core.constraint.CompleteMultiVariableContext) QuantifiedExpression(com.sri.ai.expresso.api.QuantifiedExpression) AssignmentMapsIterator(com.sri.ai.grinder.helper.AssignmentMapsIterator) Assignment(com.sri.ai.grinder.interpreter.Assignment) Beta(com.google.common.annotations.Beta) Theory(com.sri.ai.grinder.api.Theory) Expressions.makeSymbol(com.sri.ai.expresso.helper.Expressions.makeSymbol) SingleVariableConstraint(com.sri.ai.grinder.api.SingleVariableConstraint) Simplifier(com.sri.ai.grinder.rewriter.api.Simplifier) Util(com.sri.ai.util.Util) Util.pickKElementsWithoutReplacement(com.sri.ai.util.Util.pickKElementsWithoutReplacement) TRUE(com.sri.ai.expresso.helper.Expressions.TRUE) Constraint(com.sri.ai.grinder.api.Constraint) Context(com.sri.ai.grinder.api.Context) CompleteMultiVariableContext(com.sri.ai.grinder.core.constraint.CompleteMultiVariableContext) BruteForceCommonInterpreter(com.sri.ai.grinder.interpreter.BruteForceCommonInterpreter) QuantifiedExpression(com.sri.ai.expresso.api.QuantifiedExpression) Theory(com.sri.ai.grinder.api.Theory) Expression(com.sri.ai.expresso.api.Expression) QuantifiedExpression(com.sri.ai.expresso.api.QuantifiedExpression) Simplifier(com.sri.ai.grinder.rewriter.api.Simplifier) SubExpressionsDepthFirstIterator(com.sri.ai.expresso.helper.SubExpressionsDepthFirstIterator)

Example 7 with Context

use of com.sri.ai.grinder.api.Context in project aic-expresso by aic-sri-international.

the class DifferenceArithmeticTheoryTestingSupport method makeRandomAtomOn.

/**
 * Makes a random atom on variable by summing or subtracting terms from two random atoms generated by super class implementation.
 */
@Override
public Expression makeRandomAtomOn(String mainVariable, Context context) {
    String mainVariableName = getVariableName(mainVariable);
    Type mainType = getTestingVariableType(mainVariable);
    List<String> variableNamesThatAreSubtypesOf = getVariableNamesWhoseTypesAreSubtypesOf(mainType);
    int maxNumberOfOtherVariablesInAtom = Math.min(variableNamesThatAreSubtypesOf.size(), 2);
    // used to be 3, but if literal has more than two variables, it steps out of difference arithmetic and may lead
    // to multiplied variables when literals are propagated.
    // For example, X = Y + Z and X = -Y - Z + 3 imply 2Y + 2Z = 3
    int numberOfOtherVariablesInAtom = getRandom().nextInt(maxNumberOfOtherVariablesInAtom);
    // Note: that otherVariablesForAtom will contain only one or zero elements
    ArrayList<String> otherVariablesForAtom = new ArrayList<>();
    if (numberOfOtherVariablesInAtom > 0) {
        otherVariablesForAtom.add(pickTestingVariableAtRandom(mainType, otherName -> !otherName.equals(mainVariableName)));
    }
    ArrayList<Expression> constants = new ArrayList<Expression>();
    int numberOfConstants = getRandom().nextInt(3);
    for (int i = 0; i != numberOfConstants; i++) {
        // Note: We know we can safely sample from the Difference Arithmetic Theory Types.
        Expression sampledConstant = mainType.sampleUniquelyNamedConstant(getRandom());
        Expression constant;
        if (getRandom().nextBoolean()) {
            constant = sampledConstant;
        } else {
            constant = makeSymbol(-sampledConstant.intValue());
        }
        constants.add(constant);
    }
    ArrayList<Expression> leftHandSideArguments = new ArrayList<Expression>();
    leftHandSideArguments.add(parse(mainVariable));
    // needs to be difference, so it's added as negative
    Util.mapIntoList(otherVariablesForAtom, otherVariable -> UnaryMinus.make(parse(otherVariable)), leftHandSideArguments);
    leftHandSideArguments.addAll(constants);
    int numberOfOtherVariablesToBeCanceled = getRandom().nextInt(otherVariablesForAtom.size() + 1);
    ArrayList<String> otherVariablesToBeCanceled = Util.pickKElementsWithoutReplacement(otherVariablesForAtom, numberOfOtherVariablesToBeCanceled, getRandom());
    // note that this term is positive, so it will cancel the previously negative term with the same "other variable"
    Util.mapIntoList(otherVariablesToBeCanceled, v -> parse(v), leftHandSideArguments);
    // Note: it may seem odd to generate an "other variable" and add another term that will cancel it later.
    // However, this is useful for making sure canceling works properly.
    Expression leftHandSide = Plus.make(leftHandSideArguments);
    String functor = pickUniformly(getTheoryFunctors(), getRandom());
    Expression unsimplifiedResult = apply(functor, leftHandSide, 0);
    Expression result = getTheory().simplify(unsimplifiedResult, context);
    return result;
}
Also used : Plus(com.sri.ai.grinder.library.number.Plus) Type(com.sri.ai.expresso.api.Type) Random(java.util.Random) Expression(com.sri.ai.expresso.api.Expression) IntegerInterval(com.sri.ai.expresso.type.IntegerInterval) ArrayList(java.util.ArrayList) Beta(com.google.common.annotations.Beta) Util.pickUniformly(com.sri.ai.util.Util.pickUniformly) Util.map(com.sri.ai.util.Util.map) List(java.util.List) Expressions.makeSymbol(com.sri.ai.expresso.helper.Expressions.makeSymbol) Expressions.apply(com.sri.ai.expresso.helper.Expressions.apply) Expressions.parse(com.sri.ai.expresso.helper.Expressions.parse) Context(com.sri.ai.grinder.api.Context) Util(com.sri.ai.util.Util) AbstractTheoryWithBinaryAtomsTestingSupport(com.sri.ai.grinder.theory.base.AbstractTheoryWithBinaryAtomsTestingSupport) UnaryMinus(com.sri.ai.grinder.library.number.UnaryMinus) Type(com.sri.ai.expresso.api.Type) Expression(com.sri.ai.expresso.api.Expression) ArrayList(java.util.ArrayList)

Example 8 with Context

use of com.sri.ai.grinder.api.Context in project aic-expresso by aic-sri-international.

the class SatisfiabilityOfSingleVariableEqualityConstraintStepSolver method getPropagatedCNFBesidesPropagatedLiterals.

@Override
protected Iterable<Iterable<Expression>> getPropagatedCNFBesidesPropagatedLiterals(Context context) {
    if (!variableIsBoundToUniquelyNamedConstant(context)) {
        // the following logic only holds if the variable is not bound to a uniquely named constants,
        // since that eliminates all disequalities to other uniquely named constants as redundant
        long variableDomainSize = getConstraint().getVariableTypeSize(context);
        if (variableDomainSize >= 0 && getConstraint().numberOfDisequals() >= variableDomainSize) {
            // the following procedure can be very expensive but the condition above will rarely be satisfied
            ArrayList<Expression> variableDisequals = getVariableDisequals(context);
            Set<Expression> uniquelyNamedConstantDisequals = getUniquelyNamedConstantDisequals(context);
            Expression typeExpression = GrinderUtil.getTypeExpressionOfExpression(getConstraint().getVariable(), context);
            Type type = context.getTypeFromTypeExpression(typeExpression);
            ArrayList<Expression> remainingUniquelyNamedConstants = arrayListFrom(new PredicateIterator<>(type.iterator(), c -> !uniquelyNamedConstantDisequals.contains(c)));
            CartesianProductIterator<ArrayList<Expression>> subsetOfVariableDisequalsAndRemainingConstantsPermutationIterator = new CartesianProductIterator<ArrayList<Expression>>(() -> new SubsetsOfKIterator<Expression>(variableDisequals, remainingUniquelyNamedConstants.size()), () -> new PermutationIterator<Expression>(remainingUniquelyNamedConstants));
            FunctionIterator<ArrayList<ArrayList<Expression>>, Iterable<Expression>> clausesIterator = FunctionIterator.make(subsetOfVariableDisequalsAndRemainingConstantsPermutationIterator, (ArrayList<ArrayList<Expression>> subsetAndPermutation) -> clauseNegatingAssignmentOfSubsetOfVariablesToParticularPermutationOfRemainingConstants(subsetAndPermutation));
            Iterable<Iterable<Expression>> clauses = in(clausesIterator);
            return clauses;
        }
    }
    // otherwise, nothing is implied.
    return list();
}
Also used : PredicateIterator(com.sri.ai.util.collect.PredicateIterator) PermutationIterator(com.sri.ai.util.collect.PermutationIterator) NestedIterator(com.sri.ai.util.collect.NestedIterator) PairOf(com.sri.ai.util.base.PairOf) Expression(com.sri.ai.expresso.api.Expression) CartesianProductIterator(com.sri.ai.util.collect.CartesianProductIterator) PairOf.makePairOf(com.sri.ai.util.base.PairOf.makePairOf) ArrayList(java.util.ArrayList) GrinderUtil(com.sri.ai.grinder.helper.GrinderUtil) SubsetsOfKIterator(com.sri.ai.util.collect.SubsetsOfKIterator) Util.in(com.sri.ai.util.Util.in) Expressions.zipApply(com.sri.ai.expresso.helper.Expressions.zipApply) Equality(com.sri.ai.grinder.library.Equality) Expressions.apply(com.sri.ai.expresso.helper.Expressions.apply) DISEQUALITY(com.sri.ai.grinder.library.FunctorConstants.DISEQUALITY) Context(com.sri.ai.grinder.api.Context) Util.thereExists(com.sri.ai.util.Util.thereExists) Util.arrayList(com.sri.ai.util.Util.arrayList) FunctionIterator(com.sri.ai.util.collect.FunctionIterator) LinkedHashSet(java.util.LinkedHashSet) Util.arrayListFrom(com.sri.ai.util.Util.arrayListFrom) Function(com.google.common.base.Function) Type(com.sri.ai.expresso.api.Type) AbstractBooleanWithPropagatedLiteralsRequiringPropagatedLiteralsAndCNFToBeSatisfiedStepSolver(com.sri.ai.grinder.core.solver.AbstractBooleanWithPropagatedLiteralsRequiringPropagatedLiteralsAndCNFToBeSatisfiedStepSolver) Iterator(java.util.Iterator) Util.list(com.sri.ai.util.Util.list) Set(java.util.Set) Beta(com.google.common.annotations.Beta) Util.toLinkedHashSet(com.sri.ai.util.Util.toLinkedHashSet) List(java.util.List) FunctionIterator.functionIterator(com.sri.ai.util.collect.FunctionIterator.functionIterator) Util(com.sri.ai.util.Util) PairOfElementsInListIterator(com.sri.ai.util.collect.PairOfElementsInListIterator) TRUE(com.sri.ai.expresso.helper.Expressions.TRUE) ArrayList(java.util.ArrayList) Type(com.sri.ai.expresso.api.Type) Expression(com.sri.ai.expresso.api.Expression) CartesianProductIterator(com.sri.ai.util.collect.CartesianProductIterator)

Example 9 with Context

use of com.sri.ai.grinder.api.Context in project aic-expresso by aic-sri-international.

the class AbstractSingleVariableNumericConstraintFeasibilityRegionStepSolver method makeLowerBoundsAndStrictness.

/**
 * A method setting {@link #lowerBoundsIncludingImplicitOnes} and {@link #fromLowerBoundsIncludingImplicitOnesToStrictness}
 * from constraint and variable's type.
 * @param context
 */
protected void makeLowerBoundsAndStrictness(Context context) {
    AbstractSingleVariableConstraint abstractSingleVariableConstraint = (AbstractSingleVariableConstraint) constraint;
    FunctionIterator<Expression, Pair<Expression, Boolean>> lowerBoundsAndStrictnessFromPositiveNormalizedAtomsIterator = functionIterator(predicateIterator(abstractSingleVariableConstraint.getPositiveNormalizedAtoms(), // X > Y, so Y is a strict lower bound
    e -> e.hasFunctor(GREATER_THAN)), // bound is strict
    e -> processExplicitLowerBoundAndStrictnessPair(e.get(1), true, context));
    FunctionIterator<Expression, Pair<Expression, Boolean>> lowerBoundsAndStrictnessFromNegativeNormalizedAtomsIterator = functionIterator(predicateIterator(abstractSingleVariableConstraint.getNegativeNormalizedAtoms(), e -> e.hasFunctor(LESS_THAN)), // not (X < Y) <=> X >= Y, so bound is non-strict
    e -> processExplicitLowerBoundAndStrictnessPair(e.get(1), false, context));
    Pair<Expression, Boolean> typeLowerBoundAndStrictness = getTypeLowerBoundAndStrictness(context);
    Iterator<Pair<Expression, Boolean>> lowerBoundsAndStrictnessIterator = new NestedIterator<>(lowerBoundsAndStrictnessFromPositiveNormalizedAtomsIterator, lowerBoundsAndStrictnessFromNegativeNormalizedAtomsIterator, typeLowerBoundAndStrictness);
    lowerBoundsIncludingImplicitOnes = arrayList();
    fromLowerBoundsIncludingImplicitOnesToStrictness = map();
    for (Pair<Expression, Boolean> boundAndStrictness : in(lowerBoundsAndStrictnessIterator)) {
        Expression bound = boundAndStrictness.first;
        lowerBoundsIncludingImplicitOnes.add(bound);
        Boolean strictness = boundAndStrictness.second;
        Boolean previousStrictness = fromLowerBoundsIncludingImplicitOnesToStrictness.get(bound);
        if (previousStrictness == null || (!previousStrictness && strictness)) {
            // if no strictness information so far, store current one; otherwise, only need to change it if previous occurrences were non-strict and this one is strict
            fromLowerBoundsIncludingImplicitOnesToStrictness.put(bound, strictness);
        }
    }
}
Also used : AbstractExpressionWithPropagatedLiteralsStepSolver(com.sri.ai.grinder.core.solver.AbstractExpressionWithPropagatedLiteralsStepSolver) Expressions(com.sri.ai.expresso.helper.Expressions) NestedIterator(com.sri.ai.util.collect.NestedIterator) INFINITY(com.sri.ai.expresso.helper.Expressions.INFINITY) PairOf(com.sri.ai.util.base.PairOf) Expression(com.sri.ai.expresso.api.Expression) EQUALITY(com.sri.ai.grinder.library.FunctorConstants.EQUALITY) CartesianProductIterator(com.sri.ai.util.collect.CartesianProductIterator) PairOf.makePairOf(com.sri.ai.util.base.PairOf.makePairOf) ArrayList(java.util.ArrayList) Util.in(com.sri.ai.util.Util.in) Util.map(com.sri.ai.util.Util.map) ExpressionLiteralSplitterStepSolver(com.sri.ai.grinder.api.ExpressionLiteralSplitterStepSolver) AbstractSingleVariableConstraint(com.sri.ai.grinder.core.constraint.AbstractSingleVariableConstraint) Symbol(com.sri.ai.expresso.api.Symbol) Equality(com.sri.ai.grinder.library.Equality) Expressions.apply(com.sri.ai.expresso.helper.Expressions.apply) TrueContext(com.sri.ai.grinder.core.TrueContext) ConstantExpressionStepSolver(com.sri.ai.grinder.theory.base.ConstantExpressionStepSolver) Map(java.util.Map) AbstractSingleVariableDifferenceArithmeticConstraintFeasibilityRegionStepSolver(com.sri.ai.grinder.theory.differencearithmetic.AbstractSingleVariableDifferenceArithmeticConstraintFeasibilityRegionStepSolver) Context(com.sri.ai.grinder.api.Context) Pair.pair(com.sri.ai.util.base.Pair.pair) ConstantStepSolver(com.sri.ai.grinder.theory.base.ConstantStepSolver) Util.arrayList(com.sri.ai.util.Util.arrayList) FunctionIterator(com.sri.ai.util.collect.FunctionIterator) Pair(com.sri.ai.util.base.Pair) PredicateIterator.predicateIterator(com.sri.ai.util.collect.PredicateIterator.predicateIterator) Util.arrayListFrom(com.sri.ai.util.Util.arrayListFrom) LiteralStepSolver(com.sri.ai.grinder.theory.base.LiteralStepSolver) LESS_THAN_OR_EQUAL_TO(com.sri.ai.grinder.library.FunctorConstants.LESS_THAN_OR_EQUAL_TO) Function(com.google.common.base.Function) Iterator(java.util.Iterator) Util.iterator(com.sri.ai.util.Util.iterator) Util.list(com.sri.ai.util.Util.list) MINUS_INFINITY(com.sri.ai.expresso.helper.Expressions.MINUS_INFINITY) MaximumExpressionStepSolver(com.sri.ai.grinder.helper.MaximumExpressionStepSolver) GREATER_THAN(com.sri.ai.grinder.library.FunctorConstants.GREATER_THAN) Beta(com.google.common.annotations.Beta) GREATER_THAN_OR_EQUAL_TO(com.sri.ai.grinder.library.FunctorConstants.GREATER_THAN_OR_EQUAL_TO) StepSolver(com.sri.ai.grinder.api.StepSolver) Expressions.makeSymbol(com.sri.ai.expresso.helper.Expressions.makeSymbol) FunctionIterator.functionIterator(com.sri.ai.util.collect.FunctionIterator.functionIterator) LESS_THAN(com.sri.ai.grinder.library.FunctorConstants.LESS_THAN) Util(com.sri.ai.util.Util) FunctorConstants(com.sri.ai.grinder.library.FunctorConstants) PairOfElementsInListIterator(com.sri.ai.util.collect.PairOfElementsInListIterator) Expression(com.sri.ai.expresso.api.Expression) NestedIterator(com.sri.ai.util.collect.NestedIterator) AbstractSingleVariableConstraint(com.sri.ai.grinder.core.constraint.AbstractSingleVariableConstraint) Pair(com.sri.ai.util.base.Pair)

Example 10 with Context

use of com.sri.ai.grinder.api.Context in project aic-expresso by aic-sri-international.

the class ExpressoAPIExamples method main.

public static void main(String[] args) {
    // Symbols are expressions representing Java values, such as a string,
    // a number, a boolean, and even any other objects (Expressions themselves will throw an error, though, to prevent common mistakes)
    Expression a = DefaultSymbol.createSymbol("a");
    Expression ten = DefaultSymbol.createSymbol(10);
    Expression trueValue = DefaultSymbol.createSymbol(true);
    Expression object = DefaultSymbol.createSymbol(Car.ferrari);
    // Below, we use Util.println to avoid having to write System.out.println.
    // Util has lots and lots and lots of very useful methods that eliminate boring Java chores.
    // We definitely recommend studying it carefully!
    // To easily write just "println" and have it work, write "println" and use Ctrl-1 to be offered the option
    // of statically importing Util.println.
    // You can statically import an identifier in Eclipse by placing the cursor on it and pressing Shift-Ctrl-M in Windows.
    println("a        : " + a);
    println("ten      : " + ten);
    println("trueValue: " + trueValue);
    println("object   : " + object);
    // It is easier to remember to make symbols using Expressions.makeSymbol:
    // 'Expressions' is a class with lots of useful static methods for working with expressions.
    // This of it as the counterpart of 'Util', but for expressions.
    // Having many useful expression classes in a single place makes it easier to remember them and access them through code completion.
    // As with 'Util', we often import its methods statically.
    a = makeSymbol("a");
    ten = makeSymbol(10);
    trueValue = makeSymbol(true);
    object = makeSymbol(Car.ferrari);
    println("a        : " + a);
    println("ten      : " + ten);
    println("trueValue: " + trueValue);
    println("object   : " + object);
    // The second most important type of expressions are function applications.
    // They consist of a functor (an expression representing a function, typically a symbol) applied to zero or more arguments:
    Expression f = makeSymbol("f");
    Expression g = makeSymbol("g");
    Expression fATen = new DefaultFunctionApplication(f, list(a, ten));
    Expression gOnNothing = new DefaultFunctionApplication(g, list());
    println("function f applied to a and ten: " + fATen);
    println("function g applied to nothing: " + gOnNothing);
    // It is much easier to use Expressions.apply:
    fATen = apply(f, a, ten);
    gOnNothing = apply(g);
    println("function f applied to a and ten: " + fATen);
    println("function g applied to nothing: " + gOnNothing);
    // Naturally, function applications can be applied to any expression, including other function applications:
    Expression gFATen = apply(g, apply(f, a, ten));
    println("function g applied to function f applied to a and ten: " + gFATen);
    // If we do not create symbols for Java values, apply does it automatically for us:
    gFATen = apply("g", apply("f", "a", 10));
    println("function g applied to function f applied to a and ten: " + gFATen);
    // Some operators are output in special infix notation for readability:
    // again not creating symbols first
    Expression twoPlusTwoPlusThree = apply("+", 2, 2, 3);
    println("two plus two plus three: " + twoPlusTwoPlusThree);
    Expression arithmetic1 = apply("*", 2, apply("+", 2, 3));
    Expression arithmetic2 = apply("+", 2, apply("*", 2, 3));
    println("Arithmetic gets printed while respecting usual precedence rules by using parentheses: " + arithmetic1);
    println("Arithmetic gets printed while respecting usual precedence rules by using parentheses: " + arithmetic2);
    // Same for logic:
    Expression logic1 = apply("and", "p", apply("or", "q", "r"));
    Expression logic2 = apply("or", "p", apply("and", "q", "r"));
    println("Same for logic: " + logic1);
    println("Same for logic: " + logic2);
    // It is not good practice to use separate strings for referring to the same operators.
    // In the future, we may decide to change the string associated to an operator
    // and have that string used in many places would make that hard to effect.
    // FunctorConstants is a class with lots of static fields for operator strings:
    arithmetic1 = apply(FunctorConstants.TIMES, 2, apply(FunctorConstants.PLUS, 2, 3));
    logic1 = apply(FunctorConstants.AND, "p", apply(FunctorConstants.OR, "q", "r"));
    // Functor constants can also be statically imported:
    arithmetic1 = apply(TIMES, 2, apply(PLUS, 2, 3));
    logic1 = apply(AND, "p", apply(OR, "q", "r"));
    // We can access the functor and arguments of a function application:
    println("The functor of " + gFATen + " is " + gFATen.getFunctor());
    println("The second argument of " + fATen + " is " + fATen.get(1));
    // returns a List<Expression>
    println("All arguments of " + fATen + " are " + fATen.getArguments());
    // We can also set new functors or arguments.
    // IMPORTANT: expressions are IMMUTABLE, so this creates a new expression,
    // although it does re-use the unchanged parts.
    Expression newArgument = gFATen.set(0, a);
    println("Changed first argument of " + gFATen + " to " + a + " and obtained " + newArgument);
    println("Original expression continues the same, since they are immutable: " + gFATen);
    // getFunctor returns a symbol, so to check if it is, for example, "f", we need
    // to write expression.getFunctor().equals(makeSymbol("f"))
    // which is too long.
    // Instead, we can use 'hasFunctor'
    println(fATen + " has functor \"f\": " + fATen.hasFunctor("f"));
    // Finally, we can parse expressions for strings.
    // BUT we should never use that to construct expressions if we have the sub-expressions already represented as Java objects.
    // For example, don't do this: parse("f(" + a + ", " + b + ")");
    // Instead, use apply("f", a, b);
    ten = parse("10");
    trueValue = parse("true");
    fATen = parse("f(a,10)");
    arithmetic1 = parse("2*(2 + 3)");
    arithmetic2 = parse("2+(2 * 3)");
    println(ten);
    println(trueValue);
    println(fATen);
    println(arithmetic1);
    println(arithmetic2);
    // Another important type of expression is sets.
    // There are two dimensions for sets: they can be uni- or multi-sets, and they can be extensionally or intensionally defined.
    // 
    // A uni-set has at most one instance of each element in it. This is the typical mathematical set.
    // A multi-set may have multiple instances of the same element in it.
    // For example, the multi-set {1,2,2,3} is distinct from multi-set {1,2,2,2,3}.
    // In Expresso, we use double-brackets for denoting multi-sets:  {{ 1, 2, 2, 3 }}.
    // {{ }} denotes the empty multi-set.
    // The singleton uni-set with an empty set in it is denoted { {} }.
    // You need a space between the brackets to avoid them being parsed as a double bracket.
    // 
    // An extensionally defined set is an explicit enumeration of its elements: {1, 2, 3}, {{1, 2, 2, 3}}, {}, {{ }}.
    // An intensionally defined set is defined by a condition: { (on I in Integer)  I^2 : I > 3 and I <= 100 }, for example,
    // which is equal to { 16, 25, 36, ..., 10000 }.
    // The general form of an intensionally defined set (or, less precisely but more succinctly, an intensional set) is
    // { (on Index1 in Index1Domain, Index2 in Index2Domain, ..., Index_n in Index_nDomain)   Head   :  Condition }
    // We can also have intensionally defined multi-sets using double brackets.
    // Here are some ways of constructing sets:
    a = makeSymbol("a");
    Expression b = makeSymbol("b");
    Expression c = makeSymbol("c");
    Expression d = makeSymbol("d");
    Expression extensionalUniSet = ExtensionalSets.makeUniSet(a, b, c, d);
    Expression extensionalMultiSet = ExtensionalSets.makeMultiSet(a, b, c, d);
    println(extensionalUniSet);
    println(extensionalMultiSet);
    // Creating an intensionally defined set programmatically (as opposed to parsing a string description of it)
    // is a bit of work (this will be shown below).
    // Here's an example of parsing one:
    Expression intensionalUniSet = parse("{ ( on P in People, F in Foods ) eats(P, F) : not (P = Rodrigo and F = shrimp) }");
    println(intensionalUniSet);
    // Here's how to do it from scratch, but see next the way we typically actually do it.
    Expression p = makeSymbol("P");
    Expression people = makeSymbol("People");
    f = makeSymbol("F");
    Expression foods = makeSymbol("Foods");
    IndexExpressionsSet indices = new ExtensionalIndexExpressionsSet(apply(IN, p, people), apply(IN, f, foods));
    // The "extensional" in ExtensionalIndexExpressionsSet means that the list/set of indices is extensionally defined,
    // even though they will be the indices of an intensionally defined set.
    intensionalUniSet = // IntensionalSet.intensionalUniSet, or simply intensionalUniSet, also works
    IntensionalSet.makeUniSet(indices, apply("eats", p, f), apply(NOT, apply(AND, Equality.make(p, "Rodrigo"), Equality.make(f, "shrimp"))));
    // Note that Equality.make(p, "Rodrigo") is the same as apply(FunctorConstants.EQUAL, p, "Rodrigo").
    // We often have 'make' methods for many operators: And.make, Or.make and so on.
    // packages in com.sri.ai.expresso.grinder.sgdpllt.library have many such operator-specific classes.
    println(intensionalUniSet);
    // When writing code on sets, we typically are modifying an existing set expression, so we can re-use its parts,
    // by using special part-replacement methods.
    // This requires the variable to implement the IntensionalSet interface, though.
    // IMPORTANT: expressions are IMMUTABLE, so setCondition and other part-replacement methods return a NEW expression,
    // although the parts not replaced are re-used.
    IntensionalSet intensionalSetCast = (IntensionalSet) intensionalUniSet;
    Expression noCondition = intensionalSetCast.setCondition(makeSymbol(true));
    println("Set with no condition: " + noCondition);
    Expression headSaysLoveInsteadOfEats = intensionalSetCast.setHead(apply("loves", p, f));
    println("Set with new head: " + headSaysLoveInsteadOfEats);
    Expression withNewIndices = intensionalSetCast.setIndexExpressions(new ExtensionalIndexExpressionsSet(apply(IN, p, people), apply(IN, f, foods), apply(IN, "D", "Days")));
    println("Set with new indices: " + withNewIndices);
    // summations and products are just function applications of FunctorConstants.SUM and FunctorConstants.PRODUCT on intensional multi-sets.
    // sum( {{ (on Indices)  Head  : Condition }} ) represents the summation (in Latex notation) sum_{Indices : Condition} Head
    Expression summation = apply(SUM, intensionalSetCast);
    println(summation);
    Expression product = apply(PRODUCT, intensionalSetCast);
    println(product);
    // /// Evaluating expressions
    // The above code shows how to deal with the syntax of expressions.
    // Evaluating expressions requires knowing about the semantics, that is, to what functions each operator corresponds to ("+" to addition, etc).
    // This is provided by a theory, which for now it suffices to know is a collection of methods for evaluating expressions
    // according to an interpretation to some symbols.
    Theory theory = new CompoundTheory(new EqualityTheory(false, true), new DifferenceArithmeticTheory(false, false), new LinearRealArithmeticTheory(false, false), new TupleTheory(), new PropositionalTheory());
    // Because this evaluation is symbolic, evaluated expressions may involve free variables.
    // In this case, the result of the evaluation will be a simplified expression that
    // is equivalent to the original expression for all possible assignments to the free variables.
    // For example, X + 0*Y is evaluate to X because, for any assignment to (X,Y), X + 0*Y = X.
    // true context: all assignments to free variables are of interest
    Context context = new TrueContext(theory);
    // We will later see how we can use contexts that restrict the free variable assignments of interest.
    // Now that we have a theory and a context, we can evaluate expressions:
    println("1 + 0*X + 1  =  " + theory.evaluate(parse("1 + 1"), context));
    evaluate(new String[] { "1 + 1", "2", "X + 1 + 1", "X + 2", "sum({{ (on I in 1..10) I }})", "55", "product({{ (on I in 1..5) 2 : I != 3 and I != 5 }})", "8" // see many more examples in SymbolicShell.java
    }, theory, context);
    // now let us assume we have a free variable J which is an integer
    // Contexts are, like expressions, also IMMUTABLE:
    Context context2 = context.extendWithSymbolsAndTypes("J", "Integer");
    // However, here we just want to use the same variable 'context' all along, so we keep the updated context in it:
    context = context2;
    // Because we store the reference to the modified context in the same variable, we lose the reference to the original one,
    // but, if we wanted, we could keep contexts in a stack, for example,
    // so that we could always easily revert back to a previous context if needed.
    evaluate(new String[] { "X + 1 + 1 + J", "X + 2 + J", "sum({{ (on I in 1..10) I : I != J }})", "if J > 0 then if J <= 10 then -1 * J + 55 else 55 else 55" // see many more examples in SymbolicShell.java
    }, theory, context);
    // now let us assume we have a free variable J which is an integer
    // The context is also a boolean formula (a constraint)
    // Current, its value is "true", but we can conjoin it with a literal J < 0
    context = context.conjoin(parse("J < 0"));
    evaluate(new String[] { "J < 1", "true", // J is irrelevant because it is out of the range of I
    "sum({{ (on I in 1..1000) I : I != J }})", // J is irrelevant because it is out of the range of I
    "500500" // see many more examples in SymbolicShell.java
    }, theory, context);
    // we now add another symbol and constraint
    context = context.extendWithSymbolsAndTypes("K", "Integer");
    context = context.conjoin(parse("K > 0"));
    evaluate(new String[] { "J < K", "true" // see many more examples in SymbolicShell.java
    }, theory, context);
    // Obtaining all free variables in an expression.
    // In order to obtain all free variables appearing in an expression
    // (and therefore excluding quantified variables (for all X, there exists X) and set indices ({ (on Z in Real) Z }))
    // we must traverse the expression and select its sub-expressions that are variables.
    // However, we need to know what a variable is.
    // It is not enough to say that any symbol is a variable, because "1" and "true" are symbols, but not variables.
    // It is not enough to say that any symbols that is an identifier (starting with an alphabet letter) is a variable,
    // because we may have uniquely named constants such as "john" and "bob" that are not to be treated as variables
    // (we want "john = bob" to be evaluated to "false", and if they were variable, this would not happen.
    // The way Expresso deals with this question is by letting the user define a predicate in the context that
    // encoded what a uniquely named constant is, and considering any other symbol to be considered a variable.
    // By default, Expresso follows the Prolog convention of capitalized variables: X is a variable, x is not.
    // Note how this takes "Real" to be a variable!
    context = new TrueContext();
    Expression expression = parse("X + f(g(x, Y, 1, true, false, 10, bob, john, there exists Z in Real : 10, { (on W in Real) 1 } ))");
    Set<Expression> variablesInExpression = Expressions.freeVariables(expression, context);
    // outputs [X, Y]
    println("variables in " + expression + " by Prolog standard: " + variablesInExpression);
    // More recently, we have adopted the practice of not caring about capitalization.
    // This means that we may, for example, defined uniquely named constants to be any symbols that are not in a given set of variables.
    Set<Expression> allVariables = set(parse("x"), parse("X"), parse("Y"), parse("Z"), parse("W"));
    context = context.setIsUniquelyNamedConstantPredicate(new UniquelyNamedConstantAreAllSymbolsNotIn(allVariables));
    variablesInExpression = Expressions.freeVariables(expression, context);
    println("variables in " + expression + " if all variables is " + allVariables + ": " + // outputs [x, X, Y]
    variablesInExpression);
    // Sometimes, it is useful to replace subexpressions in a given expression by another subexpression:
    expression = parse("f(f(f(X))) + X");
    Expression valueOfX = parse("10");
    Expression replacementOfFirstOccurrenceOnly = expression.replaceFirstOccurrence(parse("X"), valueOfX, context);
    println("Replacing only the first occurrence of X by its value gives " + replacementOfFirstOccurrenceOnly);
    Expression replacementOfAllOccurrences = expression.replaceAllOccurrences(parse("X"), valueOfX, context);
    println("Replacing all occurrences of X by its value gives " + replacementOfAllOccurrences);
    // There are a LOT of variants of these functions in Expression (not Expressions),
    // including some very flexible ones that allow the user to provide a function for determining the replacement.
    // Here's how to decide if a point is in the convex hull of other two points:
    Context convexityBaseContext = new TrueContext(theory);
    convexityBaseContext = convexityBaseContext.setIsUniquelyNamedConstantPredicate(new UniquelyNamedConstantIncludingBooleansAndNumbersPredicate(set()));
    convexityBaseContext = convexityBaseContext.extendWithSymbolsAndTypes("p", "Real", "p1", "Real", "p2", "Real", "X", "Integer");
    Expression pValue;
    Expression p1Value;
    Expression p2Value;
    String[] examples = { "4", "3", "5", "6", "3", "5" // this case is still buggy:
    // "if X = 1 then 2 else 3", "if X = 2 then 1 else 5", "if X = 10 then 0 else 10",
    };
    for (int i = 0; i != examples.length; i += 3) {
        pValue = parse(examples[i]);
        p1Value = parse(examples[i + 1]);
        p2Value = parse(examples[i + 2]);
        Context convexityContext;
        convexityContext = convexityBaseContext.conjoin(parse("p  = " + pValue));
        convexityContext = convexityContext.conjoin(parse("p1 = " + p1Value));
        convexityContext = convexityContext.conjoin(parse("p2 = " + p2Value));
        Expression isInConvexHull = parse("there exists c1 in [0;1] : there exists c2 in [0;1] : c1 + c2 = 1 and p = c1*p1 + c2*p2");
        Expression result = theory.evaluate(isInConvexHull, convexityContext);
        println("p (value " + pValue + ") is in the convex hull of p1 and p2 (" + p1Value + ", " + p2Value + "): " + result);
    }
    // Even coding the values directly still results in a bug.
    // Expression isInConvexHull =
    // parse("there exists c1 in [0;1] : there exists c2 in [0;1] : c1 + c2 = 1 and (if X = 1 then 2 else 3) = c1*(if X = 2 then 1 else 3) + c2*(if X = 10 then 0 else 10)");
    // Expression result = theory.evaluate(isInConvexHull, convexityBaseContext);
    // println("if X = 1 then 2 else 3 is in the convex hull of if X = 2 then 1 else 3 and if X = 10 then 0 else 10: " + result);
    // Obtaining and using types:
    context = new TrueContext();
    context = context.extendWithSymbolsAndTypes("I", "3..8", "P", "Boolean");
    println(context.getSymbolsAndTypes());
    println(context.getTypeExpressionOfRegisteredSymbol(parse("I")));
    Expression typeExpression = context.getTypeExpressionOfRegisteredSymbol(parse("I"));
    Type type = context.getTypeOfRegisteredSymbol(parse("I"));
    Iterator<Expression> iteratorToValuesInType = type.iterator();
    println("All values of the type " + typeExpression + " of I: " + Util.join(iteratorToValuesInType));
    typeExpression = context.getTypeExpressionOfRegisteredSymbol(parse("P"));
    type = context.getTypeOfRegisteredSymbol(parse("P"));
    iteratorToValuesInType = type.iterator();
    println("All values of the type " + typeExpression + " of P: " + Util.join(iteratorToValuesInType));
}
Also used : TrueContext(com.sri.ai.grinder.core.TrueContext) Context(com.sri.ai.grinder.api.Context) EqualityTheory(com.sri.ai.grinder.theory.equality.EqualityTheory) PropositionalTheory(com.sri.ai.grinder.theory.propositional.PropositionalTheory) EqualityTheory(com.sri.ai.grinder.theory.equality.EqualityTheory) LinearRealArithmeticTheory(com.sri.ai.grinder.theory.linearrealarithmetic.LinearRealArithmeticTheory) Theory(com.sri.ai.grinder.api.Theory) DifferenceArithmeticTheory(com.sri.ai.grinder.theory.differencearithmetic.DifferenceArithmeticTheory) CompoundTheory(com.sri.ai.grinder.theory.compound.CompoundTheory) TupleTheory(com.sri.ai.grinder.theory.tuple.TupleTheory) DifferenceArithmeticTheory(com.sri.ai.grinder.theory.differencearithmetic.DifferenceArithmeticTheory) LinearRealArithmeticTheory(com.sri.ai.grinder.theory.linearrealarithmetic.LinearRealArithmeticTheory) PropositionalTheory(com.sri.ai.grinder.theory.propositional.PropositionalTheory) CompoundTheory(com.sri.ai.grinder.theory.compound.CompoundTheory) TupleTheory(com.sri.ai.grinder.theory.tuple.TupleTheory) TrueContext(com.sri.ai.grinder.core.TrueContext) ExtensionalIndexExpressionsSet(com.sri.ai.expresso.core.ExtensionalIndexExpressionsSet) Type(com.sri.ai.expresso.api.Type) IntensionalSet(com.sri.ai.expresso.api.IntensionalSet) UniquelyNamedConstantAreAllSymbolsNotIn(com.sri.ai.grinder.helper.UniquelyNamedConstantAreAllSymbolsNotIn) Expression(com.sri.ai.expresso.api.Expression) DefaultFunctionApplication(com.sri.ai.expresso.core.DefaultFunctionApplication) ExtensionalIndexExpressionsSet(com.sri.ai.expresso.core.ExtensionalIndexExpressionsSet) IndexExpressionsSet(com.sri.ai.expresso.api.IndexExpressionsSet) UniquelyNamedConstantIncludingBooleansAndNumbersPredicate(com.sri.ai.grinder.helper.UniquelyNamedConstantIncludingBooleansAndNumbersPredicate)

Aggregations

Context (com.sri.ai.grinder.api.Context)132 Expression (com.sri.ai.expresso.api.Expression)100 Test (org.junit.Test)50 TrueContext (com.sri.ai.grinder.core.TrueContext)40 TheoryTestingSupport (com.sri.ai.grinder.tester.TheoryTestingSupport)36 DifferenceArithmeticTheory (com.sri.ai.grinder.theory.differencearithmetic.DifferenceArithmeticTheory)32 Type (com.sri.ai.expresso.api.Type)31 Theory (com.sri.ai.grinder.api.Theory)24 PropositionalTheory (com.sri.ai.grinder.theory.propositional.PropositionalTheory)23 IndexExpressionsSet (com.sri.ai.expresso.api.IndexExpressionsSet)22 EqualityTheory (com.sri.ai.grinder.theory.equality.EqualityTheory)21 Set (java.util.Set)20 IntensionalSet (com.sri.ai.expresso.api.IntensionalSet)19 CompoundTheory (com.sri.ai.grinder.theory.compound.CompoundTheory)19 CompleteMultiVariableContext (com.sri.ai.grinder.core.constraint.CompleteMultiVariableContext)16 Map (java.util.Map)16 Beta (com.google.common.annotations.Beta)15 Constraint (com.sri.ai.grinder.api.Constraint)15 Expressions.parse (com.sri.ai.expresso.helper.Expressions.parse)14 SingleVariableConstraint (com.sri.ai.grinder.api.SingleVariableConstraint)14