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

org.xcsp.parser.loaders.ConstraintRecognizer Maven / Gradle / Ivy

Go to download

Java Tools for parsing XCSP3 instances, compiling JvCSP3 models, and checking solutions. For more information about XCSP3, follow www.xcsp.org

The newest version!
package org.xcsp.parser.loaders;

import static org.xcsp.common.Types.TypeConditionOperatorRel.EQ;
import static org.xcsp.common.Types.TypeConditionOperatorRel.GE;
import static org.xcsp.common.Types.TypeConditionOperatorRel.GT;
import static org.xcsp.common.Types.TypeConditionOperatorRel.LE;
import static org.xcsp.common.Types.TypeConditionOperatorRel.LT;
import static org.xcsp.common.Types.TypeExpr.AND;
import static org.xcsp.common.Types.TypeExpr.LONG;
import static org.xcsp.common.Types.TypeExpr.OR;
import static org.xcsp.common.Types.TypeExpr.VAR;
import static org.xcsp.common.predicates.MatcherInterface.add_mul_vals;
import static org.xcsp.common.predicates.MatcherInterface.add_mul_vars;
import static org.xcsp.common.predicates.MatcherInterface.add_vars;
import static org.xcsp.common.predicates.MatcherInterface.logic_vars;
import static org.xcsp.common.predicates.MatcherInterface.max_vars;
import static org.xcsp.common.predicates.MatcherInterface.min_vars;
import static org.xcsp.common.predicates.MatcherInterface.set_vals;
import static org.xcsp.common.predicates.MatcherInterface.val;
import static org.xcsp.common.predicates.MatcherInterface.var;
import static org.xcsp.common.predicates.MatcherInterface.varOrVal;
import static org.xcsp.common.predicates.MatcherInterface.AbstractOperation.ariop;
import static org.xcsp.common.predicates.MatcherInterface.AbstractOperation.relop;
import static org.xcsp.common.predicates.MatcherInterface.AbstractOperation.setop;
import static org.xcsp.common.predicates.MatcherInterface.AbstractOperation.unalop;
import static org.xcsp.common.predicates.XNode.node;
import static org.xcsp.parser.callbacks.XCallbacks.XCallbacksParameters.RECOGNIZE_BINARY_PRIMITIVES;
import static org.xcsp.parser.callbacks.XCallbacks.XCallbacksParameters.RECOGNIZE_EXTREMUM_CASES;
import static org.xcsp.parser.callbacks.XCallbacks.XCallbacksParameters.RECOGNIZE_LOGIC_CASES;
import static org.xcsp.parser.callbacks.XCallbacks.XCallbacksParameters.RECOGNIZE_SUM_CASES;
import static org.xcsp.parser.callbacks.XCallbacks.XCallbacksParameters.RECOGNIZE_TERNARY_PRIMITIVES;
import static org.xcsp.parser.callbacks.XCallbacks.XCallbacksParameters.RECOGNIZE_UNARY_PRIMITIVES;
import static org.xcsp.parser.loaders.CtrLoaderInteger.trInteger;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import org.xcsp.common.Condition;
import org.xcsp.common.Condition.ConditionVal;
import org.xcsp.common.Condition.ConditionVar;
import org.xcsp.common.Types.TypeArithmeticOperator;
import org.xcsp.common.Types.TypeConditionOperatorRel;
import org.xcsp.common.Types.TypeConditionOperatorSet;
import org.xcsp.common.Types.TypeEqNeOperator;
import org.xcsp.common.Types.TypeExpr;
import org.xcsp.common.Utilities;
import org.xcsp.common.predicates.MatcherInterface.Matcher;
import org.xcsp.common.predicates.XNodeParent;
import org.xcsp.parser.callbacks.XCallbacks;
import org.xcsp.parser.callbacks.XCallbacks.XCallbacksParameters;
import org.xcsp.parser.entries.XVariables.XVarInteger;

public class ConstraintRecognizer {

	private XCallbacks xc;

	// unary
	private Matcher x_relop_k = new Matcher(node(relop, var, val));
	private Matcher k_relop_x = new Matcher(node(relop, val, var));
	private Matcher x_ariop_k__relop_l = new Matcher(node(relop, node(ariop, var, val), val));
	private Matcher l_relop__x_ariop_k = new Matcher(node(relop, val, node(ariop, var, val)));
	private Matcher x_setop_S = new Matcher(node(setop, var, set_vals));
	private Matcher x_in_intvl = new Matcher(node(AND, node(TypeExpr.LE, var, val), node(TypeExpr.LE, val, var)));
	private Matcher x_notin_intvl = new Matcher(node(OR, node(TypeExpr.LE, var, val), node(TypeExpr.LE, val, var)));

	// binary
	private Matcher x_relop_y = new Matcher(node(relop, var, var));
	private Matcher x_ariop_y__relop_k = new Matcher(node(relop, node(ariop, var, var), val));
	private Matcher k_relop__x_ariop_y = new Matcher(node(relop, val, node(ariop, var, var)));
	private Matcher x_relop__y_ariop_k = new Matcher(node(relop, var, node(ariop, var, val)));
	private Matcher y_ariop_k__relop_x = new Matcher(node(relop, node(ariop, var, val), var));
	private Matcher logic_y_relop_k__eq_x = new Matcher(node(TypeExpr.EQ, node(relop, var, val), var));
	private Matcher logic_k_relop_y__eq_x = new Matcher(node(TypeExpr.EQ, node(relop, val, var), var));
	private Matcher unalop_x__eq_y = new Matcher(node(TypeExpr.EQ, node(unalop, var), var));

	// ternary
	private Matcher x_ariop_y__relop_z = new Matcher(node(relop, node(ariop, var, var), var));
	private Matcher z_relop__x_ariop_y = new Matcher(node(relop, var, node(ariop, var, var)));
	private Matcher logic_y_relop_z__eq_x = new Matcher(node(TypeExpr.EQ, node(relop, var, var), var));

	// logic
	private Matcher logic_X = new Matcher(logic_vars);
	private Matcher logic_X__eq_x = new Matcher(node(TypeExpr.EQ, logic_vars, var));
	private Matcher logic_X__ne_x = new Matcher(node(TypeExpr.NE, logic_vars, var));

	// extremum
	private Matcher min_relop = new Matcher(node(relop, min_vars, varOrVal));
	private Matcher max_relop = new Matcher(node(relop, max_vars, varOrVal));

	// sum
	private Matcher add_vars__relop = new Matcher(node(relop, add_vars, varOrVal));
	private Matcher add_mul_vals__relop = new Matcher(node(relop, add_mul_vals, varOrVal));
	private Matcher add_mul_vars__relop = new Matcher(node(relop, add_mul_vars, varOrVal));

	// The following maps are useful for dealing with intension constraints. We use LinkdHashMap because insertion order
	// may be important
	private Map>> unaryRules = new LinkedHashMap<>();
	private Map>> binaryRules = new LinkedHashMap<>();
	private Map>> ternaryRules = new LinkedHashMap<>();
	private Map>> logicRules = new LinkedHashMap<>();
	private Map>> sumRules = new LinkedHashMap<>();
	private Map>> extremumRules = new LinkedHashMap<>();

	private Condition basicCondition(XNodeParent r) {
		if (r.type.isRelationalOperator() && r.sons.length == 2 && r.sons[1].type.oneOf(VAR, LONG))
			return r.sons[1].type == VAR ? new ConditionVar(r.relop(0), r.sons[1].var(0)) : new ConditionVal(r.relop(0), r.sons[1].val(0));
		return null;
	}

	ConstraintRecognizer(XCallbacks xc) {
		this.xc = xc;
		unaryRules.put(x_relop_k, (id, r) -> xc.buildCtrPrimitive(id, r.var(0), r.relop(0), r.val(0)));
		unaryRules.put(k_relop_x, (id, r) -> xc.buildCtrPrimitive(id, r.var(0), r.relop(0).arithmeticInversion(), r.val(0)));
		unaryRules.put(x_ariop_k__relop_l, (id, r) -> xc.buildCtrPrimitive(id, r.var(0), r.ariop(0), r.val(0), r.relop(0), r.val(1)));
		unaryRules.put(l_relop__x_ariop_k, (id, r) -> xc.buildCtrPrimitive(id, r.var(0), r.ariop(0), r.val(1), r.relop(0).arithmeticInversion(), r.val(0)));
		unaryRules.put(x_setop_S, (id, r) -> xc.buildCtrPrimitive(id, r.var(0), r.type.toSetop(), r.arrayOfVals()));
		unaryRules.put(x_in_intvl, (id, r) -> xc.buildCtrPrimitive(id, r.var(0), TypeConditionOperatorSet.IN, r.val(1), r.val(0)));
		unaryRules.put(x_notin_intvl, (id, r) -> xc.buildCtrPrimitive(id, r.var(0), TypeConditionOperatorSet.NOTIN, r.val(0) + 1, r.val(1) - 1));

		binaryRules.put(x_relop_y, (id, r) -> xc.buildCtrPrimitive(id, r.var(0), TypeArithmeticOperator.SUB, r.var(1), r.relop(0), 0));
		binaryRules.put(x_ariop_y__relop_k, (id, r) -> xc.buildCtrPrimitive(id, r.var(0), r.ariop(0), r.var(1), r.relop(0), r.val(0)));
		binaryRules.put(k_relop__x_ariop_y, (id, r) -> xc.buildCtrPrimitive(id, r.var(0), r.ariop(0), r.var(1), r.relop(0).arithmeticInversion(), r.val(0)));
		binaryRules.put(x_relop__y_ariop_k, (id, r) -> xc.buildCtrPrimitive(id, r.var(1), r.ariop(0), r.val(0), r.relop(0).arithmeticInversion(), r.var(0)));
		binaryRules.put(y_ariop_k__relop_x, (id, r) -> xc.buildCtrPrimitive(id, r.var(0), r.ariop(0), r.val(0), r.relop(0), r.var(1)));
		binaryRules.put(logic_y_relop_k__eq_x, (id, r) -> xc.buildCtrLogic(id, r.var(1), r.var(0), r.relop(1), r.val(0)));
		binaryRules.put(logic_k_relop_y__eq_x, (id, r) -> xc.buildCtrLogic(id, r.var(1), r.var(0), r.relop(1).arithmeticInversion(), r.val(0)));
		binaryRules.put(unalop_x__eq_y, (id, r) -> xc.buildCtrPrimitive(id, r.var(1), r.sons[0].type.toUnalop(), r.var(0)));

		ternaryRules.put(x_ariop_y__relop_z, (id, r) -> xc.buildCtrPrimitive(id, r.var(0), r.ariop(0), r.var(1), r.relop(0), r.var(2)));
		ternaryRules.put(z_relop__x_ariop_y, (id, r) -> xc.buildCtrPrimitive(id, r.var(1), r.ariop(0), r.var(2), r.relop(0).arithmeticInversion(), r.var(0)));

		logicRules.put(logic_X, (id, r) -> xc.buildCtrLogic(id, r.type.toLogop(), r.arrayOfVars()));
		logicRules.put(logic_X__eq_x,
				(id, r) -> xc.buildCtrLogic(id, r.sons[1].var(0), TypeEqNeOperator.EQ, r.sons[0].type.toLogop(), r.sons[0].arrayOfVars()));
		logicRules.put(logic_X__ne_x,
				(id, r) -> xc.buildCtrLogic(id, r.sons[1].var(0), TypeEqNeOperator.NE, r.sons[0].type.toLogop(), r.sons[0].arrayOfVars()));

		logicRules.put(logic_y_relop_z__eq_x, (id, r) -> xc.buildCtrLogic(id, r.var(2), r.var(0), r.relop(1), r.var(1)));
		sumRules.put(add_vars__relop, (id, r) -> xc.buildCtrSum(id, r.sons[0].arrayOfVars(), basicCondition(r)));
		sumRules.put(add_mul_vals__relop, (id, r) -> {
			int[] coeffs = Stream.of(r.sons[0].sons).mapToInt(s -> s.type == VAR ? 1 : s.val(0)).toArray();
			if (IntStream.of(coeffs).allMatch(v -> v == 1))
				xc.buildCtrSum(id, r.sons[0].arrayOfVars(), basicCondition(r));
			else
				xc.buildCtrSum(id, r.sons[0].arrayOfVars(), coeffs, basicCondition(r));
		});
		sumRules.put(add_mul_vars__relop, (id, r) -> {
			XVarInteger[] list = Stream.of(r.sons[0].sons).map(s -> s.var(0)).toArray(XVarInteger[]::new);
			XVarInteger[] coeffs = Stream.of(r.sons[0].sons).map(s -> s.var(1)).toArray(XVarInteger[]::new);
			xc.buildCtrSum(id, list, coeffs, basicCondition(r));
		});
		extremumRules.put(min_relop, (id, r) -> xc.buildCtrMinimum(id, r.sons[0].vars(), basicCondition(r)));
		extremumRules.put(max_relop, (id, r) -> xc.buildCtrMaximum(id, r.sons[0].vars(), basicCondition(r)));
	}

	private void posted(String id) {
		Utilities.control(!xc.implem().postedRecognizedCtrs.contains(id), "Pb with the same constraint posted twice");
		xc.implem().postedRecognizedCtrs.add(id);
	}

	/**
	 * Returns {@code true} if a target matcher from the specified array matches the specified tree. Matching is
	 * considered only if the specified condition evaluates to {@code true}.
	 * 
	 * @param id
	 *            the constraint id
	 * @param tree
	 *            the constraint predicate
	 * @param matchers
	 *            the targets for matching
	 * @param condition
	 *            when {@code true}, matching is considered
	 * @return {@code true} if a target matcher from the specified array matches the specified tree
	 */
	private boolean recognizeIntensionIn(String id, XNodeParent tree, Map>> rules,
			boolean condition) {
		return condition && rules.entrySet().stream().anyMatch(rule -> {
			if (!rule.getKey().matches(tree))
				return false;
			// System.out.println("Rec " + rule.getKey().target());
			posted(id); // keep it before calling the rule (because reposting is possible)
			rule.getValue().accept(id, tree);
			return true;
		});
	}

	private boolean recognizeIntension(String id, XNodeParent tree, int arity) {
		Map map = xc.implem().currParameters;
		if (recognizeIntensionIn(id, tree, unaryRules, arity == 1 && map.containsKey(RECOGNIZE_UNARY_PRIMITIVES)))
			return true;
		if (recognizeIntensionIn(id, tree, binaryRules, arity == 2 && map.containsKey(RECOGNIZE_BINARY_PRIMITIVES)))
			return true;
		if (recognizeIntensionIn(id, tree, ternaryRules, arity == 3 && map.containsKey(RECOGNIZE_TERNARY_PRIMITIVES)))
			return true;
		if (recognizeIntensionIn(id, tree, logicRules, map.containsKey(RECOGNIZE_LOGIC_CASES)))
			return true;
		if (recognizeIntensionIn(id, tree, sumRules, map.containsKey(RECOGNIZE_SUM_CASES)))
			return true;
		if (recognizeIntensionIn(id, tree, extremumRules, map.containsKey(RECOGNIZE_EXTREMUM_CASES)))
			return true;
		return false;
	}

	/**
	 * Returns {@code true} if a specific constraint, such as a primitive, logic, sum or extremum (minimum, maximum)
	 * constraint matches the specified predicate. In that case, this specific constraint is posted. Note that a
	 * successful matching can be discarded when overriding callback functions by simply reposting the original
	 * constraint.
	 * 
	 * @param id
	 *            the constraint id
	 * @param tree
	 *            the constraint predicate
	 * @param arity
	 *            the constraint arity
	 * @return {@code true} if a specific constraint corresponds to the specified predicate
	 */
	public boolean specificIntensionCases(String id, XNodeParent tree, int arity) {
		recognizeIntension(id, tree, arity);
		return xc.implem().postedRecognizedCtrs.contains(id); // let as it is, because constraints may be reposted
	}

	private Runnable recognizeCount(String id, XVarInteger[] list, int[] values, TypeConditionOperatorRel op, Condition condition) {
		if (xc.implem().currParameters.containsKey(XCallbacksParameters.RECOGNIZE_COUNT_CASES)) {
			if (values.length == 1) {
				int value = values[0];
				if (condition instanceof ConditionVal) {
					int k = trInteger(((ConditionVal) condition).k); // other controls on k ?
					if (op == LT)
						return () -> xc.buildCtrAtMost(id, list, value, k - 1);
					if (op == LE)
						return () -> xc.buildCtrAtMost(id, list, value, k);
					if (op == GE)
						return () -> xc.buildCtrAtLeast(id, list, value, k);
					if (op == GT)
						return () -> xc.buildCtrAtLeast(id, list, value, k + 1);
					if (op == EQ)
						return () -> xc.buildCtrExactly(id, list, value, k);
				} else if (condition instanceof ConditionVar) {
					if (op == EQ)
						return () -> xc.buildCtrExactly(id, list, value, (XVarInteger) ((ConditionVar) condition).x);
				}
			} else if (op == EQ) {
				if (condition instanceof ConditionVal)
					return () -> xc.buildCtrAmong(id, list, values, trInteger(((ConditionVal) condition).k));
				else if (condition instanceof ConditionVar)
					return () -> xc.buildCtrAmong(id, list, values, (XVarInteger) ((ConditionVar) condition).x);
			}
		}
		return null;
	}

	public boolean specificCountCases(String id, XVarInteger[] list, int[] values, TypeConditionOperatorRel op, Condition condition) {
		Runnable recognized = recognizeCount(id, list, values, op, condition);
		if (recognized == null)
			return false;
		recognized.run();
		posted(id);
		return true;
	}

	private Runnable recognizeNvalues(String id, XVarInteger[] list, Condition condition) {
		if (xc.implem().currParameters.containsKey(XCallbacksParameters.RECOGNIZE_NVALUES_CASES) && condition instanceof ConditionVal) {
			TypeConditionOperatorRel op = ((ConditionVal) condition).operator;
			int k = trInteger(((ConditionVal) condition).k);
			if (op == EQ && k == list.length)
				return () -> xc.buildCtrAllDifferent(id, list);
			if (op == EQ && k == 1)
				return () -> xc.buildCtrAllEqual(id, list);
			if ((op == GE && k == 2) || (op == GT && k == 1))
				return () -> xc.buildCtrNotAllEqual(id, list);
		}
		return null;
	}

	public boolean specificNvaluesCases(String id, XVarInteger[] list, Condition condition) {
		Runnable recognized = recognizeNvalues(id, list, condition);
		if (recognized == null)
			return false;
		recognized.run();
		posted(id);
		return true;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy