All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sri.ai.grinder.sgdpllt.theory.equality.SatisfiabilityOfSingleVariableEqualityConstraintStepSolver Maven / Gradle / Ivy

/*
 * Copyright (c) 2013, SRI International
 * All rights reserved.
 * Licensed under the The BSD 3-Clause License;
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 * 
 * http://opensource.org/licenses/BSD-3-Clause
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * 
 * Neither the name of the aic-expresso nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.sri.ai.grinder.sgdpllt.theory.equality;

import static com.sri.ai.expresso.helper.Expressions.TRUE;
import static com.sri.ai.expresso.helper.Expressions.apply;
import static com.sri.ai.expresso.helper.Expressions.zipApply;
import static com.sri.ai.grinder.sgdpllt.library.FunctorConstants.DISEQUALITY;
import static com.sri.ai.util.Util.arrayList;
import static com.sri.ai.util.Util.arrayListFrom;
import static com.sri.ai.util.Util.in;
import static com.sri.ai.util.Util.list;
import static com.sri.ai.util.Util.thereExists;
import static com.sri.ai.util.Util.toLinkedHashSet;
import static com.sri.ai.util.base.PairOf.makePairOf;
import static com.sri.ai.util.collect.FunctionIterator.functionIterator;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import com.google.common.annotations.Beta;
import com.google.common.base.Function;
import com.sri.ai.expresso.api.Expression;
import com.sri.ai.expresso.api.Type;
import com.sri.ai.grinder.helper.GrinderUtil;
import com.sri.ai.grinder.sgdpllt.api.Context;
import com.sri.ai.grinder.sgdpllt.core.solver.AbstractBooleanWithPropagatedLiteralsRequiringPropagatedLiteralsAndCNFToBeSatisfiedStepSolver;
import com.sri.ai.grinder.sgdpllt.library.Equality;
import com.sri.ai.util.Util;
import com.sri.ai.util.base.PairOf;
import com.sri.ai.util.collect.CartesianProductIterator;
import com.sri.ai.util.collect.FunctionIterator;
import com.sri.ai.util.collect.NestedIterator;
import com.sri.ai.util.collect.PairOfElementsInListIterator;
import com.sri.ai.util.collect.PermutationIterator;
import com.sri.ai.util.collect.PredicateIterator;
import com.sri.ai.util.collect.SubsetsOfKIterator;

/**
 * A {@link AbstractBooleanWithPropagatedLiteralsRequiringPropagatedLiteralsAndCNFToBeSatisfiedStepSolver} for a {@link SingleVariableEqualityConstraint}.
 * 

* This step solver works by providing propagated literals of the form Y op Z * for every pair of literals X = Y and X op Z, * where X is the constraint's variable and op is either = or !=. *

* It also provides a propagated CNF that encodes the requirement that the number of distinct values * from which X is constrained to be disequal from does not exceed the domain size. * For example, X != Y and X != Z and X != a, with X's type {a, b, c, d}, * propagates the CNF (Y != b or Z = c) and (Y != b or Z != d) and (Y != c or Z != b) and (Y != c or Z != d) and (Y != d and Z != b) and (Y != d and Z != c). *

* In general, we must generate a CNF with a clause negating each possible assignment of variables disequal from X * to a permutation of k distinct uniquely named constants not constrained to be disequal from X, * where k is the type size minus the number of uniquely named constants not constrained to be disequal from X. * The reason for that is that, if any of these assignments were true, then X would be constrained * to be difference from the uniquely named constants it already is constrained against in the constraint itself, * plus constrained to be different from k other uniquely named constants, * meaning that has to be disequal from a number of uniquely named constants equal to its type's size, * leaving no possible assignment to it. *

* While the size of this propagated CNF depends on X's type size, which can be very large, * this check is only needed if the number of terms to which X is constrained to be * disequal from is equally large. This preserves * the fact that the algorithm's time complexity depends on the constraint size alone, * not the type size. * * @author braz * */ @Beta public class SatisfiabilityOfSingleVariableEqualityConstraintStepSolver extends AbstractBooleanWithPropagatedLiteralsRequiringPropagatedLiteralsAndCNFToBeSatisfiedStepSolver { private boolean alreadyCheckedIfNumberOfDistinctExpressionsIsLessThanStepSolverShouldBeMade = false; private NumberOfDistinctExpressionsIsLessThanStepSolver numberOfDistinctExpressionsIsLessThanStepSolver; public SatisfiabilityOfSingleVariableEqualityConstraintStepSolver(SingleVariableEqualityConstraint constraint) { super(constraint); } @Override public SingleVariableEqualityConstraint getConstraint() { return (SingleVariableEqualityConstraint) super.getConstraint(); } private NumberOfDistinctExpressionsIsLessThanStepSolver getNumberOfDistinctExpressionsIsLessThanStepSolver(Context context) { if ( ! alreadyCheckedIfNumberOfDistinctExpressionsIsLessThanStepSolverShouldBeMade) { long variableTypeSize = getConstraint().getVariableTypeSize(context); if (variableTypeSize >= 0) { numberOfDistinctExpressionsIsLessThanStepSolver = new NumberOfDistinctExpressionsIsLessThanStepSolver((int) variableTypeSize, getConstraint().getDisequals()); } alreadyCheckedIfNumberOfDistinctExpressionsIsLessThanStepSolverShouldBeMade = true; } return numberOfDistinctExpressionsIsLessThanStepSolver; } private void setNumberOfDistinctExpressionsIsLessThanStepSolver(NumberOfDistinctExpressionsIsLessThanStepSolver numberOfDistinctExpressionsIsLessThanStepSolver) { this.numberOfDistinctExpressionsIsLessThanStepSolver = numberOfDistinctExpressionsIsLessThanStepSolver; } @Override protected boolean usingDefaultImplementationOfMakePropagatedCNF() { return true; } @Override protected Iterable getPropagatedLiterals(Context context) { Iterator propagatedLiteralsIterator; if (getConstraint().getPropagateAllLiteralsWhenVariableIsBound()) { propagatedLiteralsIterator = new NestedIterator<>(getConstraint().getExternalLiterals()); } else { Iterator> pairsOfEqualsToVariableIterator = pairsOfEqualsToVariableIterator(); Iterator propagatedEqualities = functionIterator(pairsOfEqualsToVariableIterator, p -> Equality.make(p.first, p.second)); Iterator propagatedDisequalities = functionIterator(arrayListsOfEqualAndDisequalToVariableIterator(), p -> apply(DISEQUALITY, p)); propagatedLiteralsIterator = new NestedIterator<>(getConstraint().getExternalLiterals(), propagatedEqualities, propagatedDisequalities); } Iterable result = in(propagatedLiteralsIterator); return result; } protected Iterator> pairsOfEqualsToVariableIterator() { PairOfElementsInListIterator pairsOfPositiveAtomsIterator = new PairOfElementsInListIterator<>(getConstraint().getPositiveNormalizedAtoms()); // Function, PairOf> makePairOfSecondArguments = p -> makePairOf(p.first.get(1), p.second.get(1)); // above lambda somehow not working at Ciaran's environment, replacing with seemingly identical anonymous class object below Function, PairOf> makePairOfSecondArguments = new Function, PairOf>() { @Override public PairOf apply(PairOf p) { return makePairOf(p.first.get(1), p.second.get(1)); } }; Iterator> pairsOfEqualsToVariableIterator = functionIterator(pairsOfPositiveAtomsIterator, makePairOfSecondArguments); return pairsOfEqualsToVariableIterator; } @Override protected Iterable> 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 variableDisequals = getVariableDisequals(context); Set uniquelyNamedConstantDisequals = getUniquelyNamedConstantDisequals(context); Expression typeExpression = GrinderUtil.getTypeExpressionOfExpression(getConstraint().getVariable(), context); Type type = context.getTypeFromTypeExpression(typeExpression); ArrayList remainingUniquelyNamedConstants = arrayListFrom(new PredicateIterator<>(type.iterator(), c -> ! uniquelyNamedConstantDisequals.contains(c))); CartesianProductIterator> subsetOfVariableDisequalsAndRemainingConstantsPermutationIterator = new CartesianProductIterator>( () -> new SubsetsOfKIterator(variableDisequals, remainingUniquelyNamedConstants.size()), () -> new PermutationIterator(remainingUniquelyNamedConstants)); FunctionIterator>, Iterable> clausesIterator = FunctionIterator.make( subsetOfVariableDisequalsAndRemainingConstantsPermutationIterator, (ArrayList> subsetAndPermutation) -> clauseNegatingAssignmentOfSubsetOfVariablesToParticularPermutationOfRemainingConstants(subsetAndPermutation) ); Iterable> clauses = in(clausesIterator); return clauses; } } // otherwise, nothing is implied. return list(); } /** * @param subsetAndPermutation * @return */ private List clauseNegatingAssignmentOfSubsetOfVariablesToParticularPermutationOfRemainingConstants(ArrayList> subsetAndPermutation) { return zipApply( DISEQUALITY, list( subsetAndPermutation.get(0).iterator(), subsetAndPermutation.get(1).iterator())); } private Iterator> arrayListsOfEqualAndDisequalToVariableIterator() { Function, ArrayList> extractSecondArguments = equalityAndDisequality -> arrayList(equalityAndDisequality.get(0).get(1), equalityAndDisequality.get(1).get(1)); Iterator> result = FunctionIterator.make( new CartesianProductIterator( () -> getConstraint().getPositiveNormalizedAtoms().iterator(), () -> getConstraint().getNegativeNormalizedAtoms().iterator()), extractSecondArguments); return result; } private boolean variableIsBoundToUniquelyNamedConstant(Context context) { return thereExists(getConstraint().getPositiveNormalizedAtoms(), l -> context.isUniquelyNamedConstant(l.get(1))); } private ArrayList getVariableDisequals(Context context) { return getConstraint().getNegativeNormalizedAtoms().stream(). map(e -> e.get(1)). // second arguments of Variable != Term filter(e -> ! context.isUniquelyNamedConstant(e)). // only Variables collect(Util.toArrayList(10)); } private LinkedHashSet getUniquelyNamedConstantDisequals(Context context) { return getConstraint().getNegativeNormalizedAtoms().stream(). map(e -> e.get(1)). // second arguments of Variable != Term filter(e -> context.isUniquelyNamedConstant(e)). // only constants collect(toLinkedHashSet()); } @Override protected Step solutionIfPropagatedLiteralsAndSplittersCNFAreSatisfied(Context context) { Step result; if (getNumberOfDistinctExpressionsIsLessThanStepSolver(context) != null) { Step numberStep = getNumberOfDistinctExpressionsIsLessThanStepSolver(context).step(context); if (numberStep.itDepends()) { SatisfiabilityOfSingleVariableEqualityConstraintStepSolver stepSolverIfExpressionIsTrue = clone(); stepSolverIfExpressionIsTrue.setNumberOfDistinctExpressionsIsLessThanStepSolver((NumberOfDistinctExpressionsIsLessThanStepSolver) numberStep.getStepSolverForWhenSplitterIsTrue()); SatisfiabilityOfSingleVariableEqualityConstraintStepSolver stepSolverIfExpressionIsFalse = clone(); stepSolverIfExpressionIsFalse.setNumberOfDistinctExpressionsIsLessThanStepSolver((NumberOfDistinctExpressionsIsLessThanStepSolver) numberStep.getStepSolverForWhenSplitterIsFalse()); result = new ItDependsOn(numberStep.getSplitterLiteral(), numberStep.getContextSplittingWhenSplitterIsLiteral(), stepSolverIfExpressionIsTrue, stepSolverIfExpressionIsFalse); } else { result = new Solution(numberStep.getValue()); } } else { result = new Solution(TRUE); } return result; } @Override public SatisfiabilityOfSingleVariableEqualityConstraintStepSolver clone() { return (SatisfiabilityOfSingleVariableEqualityConstraintStepSolver) super.clone(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy