
org.xcsp.parser.callbacks.SolutionChecker Maven / Gradle / Ivy
/*
* Copyright (c) 2016 XCSP3 Team ([email protected])
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package org.xcsp.parser.callbacks;
import static org.xcsp.common.Types.TypeObjective.LEX;
import static org.xcsp.common.Types.TypeObjective.MAXIMUM;
import static org.xcsp.common.Types.TypeObjective.MINIMUM;
import static org.xcsp.common.Types.TypeObjective.NVALUES;
import static org.xcsp.common.Types.TypeObjective.PRODUCT;
import static org.xcsp.common.Types.TypeObjective.SUM;
import static org.xcsp.common.Utilities.control;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xcsp.common.Condition;
import org.xcsp.common.Condition.ConditionIntset;
import org.xcsp.common.Condition.ConditionIntvl;
import org.xcsp.common.Condition.ConditionVal;
import org.xcsp.common.Condition.ConditionVar;
import org.xcsp.common.Constants;
import org.xcsp.common.Types.TypeAtt;
import org.xcsp.common.Types.TypeChild;
import org.xcsp.common.Types.TypeFlag;
import org.xcsp.common.Types.TypeObjective;
import org.xcsp.common.Types.TypeOperatorRel;
import org.xcsp.common.Types.TypeRank;
import org.xcsp.common.Utilities;
import org.xcsp.common.domains.Domains.Dom;
import org.xcsp.common.domains.Domains.DomSymbolic;
import org.xcsp.common.predicates.TreeEvaluator;
import org.xcsp.common.predicates.XNode;
import org.xcsp.common.predicates.XNodeParent;
import org.xcsp.common.structures.AbstractTuple;
import org.xcsp.parser.XParser;
import org.xcsp.parser.entries.XConstraints.XCtr;
import org.xcsp.parser.entries.XObjectives.XObj;
import org.xcsp.parser.entries.XVariables.XVar;
import org.xcsp.parser.entries.XVariables.XVarInteger;
import org.xcsp.parser.entries.XVariables.XVarSymbolic;
/**
* This class allows us to check solutions and bounds obtained for XCSP3 instances.
*
* @author Gilles Audemard and Christophe Lecoutre
*/
public final class SolutionChecker implements XCallbacks2 {
// ************************************************************************
// ***** Main (and other static stuff)
// ************************************************************************
private static final int MAX_DISPLAY_STRING_SIZE = 2000;
public static void main(String[] args) throws Exception {
boolean competitionMode = args.length > 0 && args[0].equals("-cm");
args = competitionMode ? Arrays.copyOfRange(args, 1, args.length) : args;
if (args.length != 1 && args.length != 2) {
System.out.println("Usage: " + SolutionChecker.class.getName() + " [-cm] [ ]");
} else
new SolutionChecker(competitionMode, args[0],
args.length == 1 ? System.in : args[1].charAt(0) == '<' ? new ByteArrayInputStream(args[1].getBytes()) : new FileInputStream(args[1]));
}
// ************************************************************************
// ***** Implementation object (bridge pattern)
// ************************************************************************
private Implem implem = new Implem(this);
@Override
public Implem implem() {
return implem;
}
// ************************************************************************
// ***** Intern class
// ************************************************************************
/** The class that manages all information about the (current) solution to test. */
private class Solution {
/** The root of the XML tree representing a solution (element instantiation). */
private Element root;
/** The sequence of variables of the solution. */
private Object[] variables;
/** The sequence of values of the solution. */
private Object[] values;
/**
* The sequence of costs of the solution. We have 0 cost for a satisfaction problem, and several costs for a multi-optimization problem.
*/
private BigInteger[] costs;
/** The map that stores the value assigned to each variable. */
private Map map = new HashMap<>();
private int intValueOf(XVarInteger x) {
control(map.containsKey(x), "The variable " + x + " is not assigned a value");
Object value = map.get(x);
if (value instanceof String && ((String) value).equals("*")) {
control(((Dom) x.dom).nValues() == 1, "* is accepted when there is only one value");
value = x.firstValue();
}
return Utilities.safeInt((Long) value);
}
private int[] intValuesOf(XVarInteger[] list) {
return Stream.of(list).mapToInt(x -> intValueOf(x)).toArray();
}
private int[][] intValuesOf(XVarInteger[][] lists) {
return Stream.of(lists).map(t -> intValuesOf(t)).toArray(int[][]::new);
}
private String symbolicValueOf(XVarSymbolic x) {
control(map.containsKey(x), "The variable " + x + " is not assigned a value");
return (String) map.get(x);
}
private String[] symbolicValuesOf(XVarSymbolic[] list) {
return Stream.of(list).map(x -> symbolicValueOf(x)).toArray(String[]::new);
}
private Solution(Element root) {
this.root = root;
Element[] childs = Utilities.childElementsOf(this.root);
control(Utilities.isTag(childs[0], TypeChild.list) && Utilities.isTag(childs[1], TypeChild.values), "Badly formed solution/instantiation");
}
private void parseVariablesAndValues(XParser parser) {
Element[] childs = Utilities.childElementsOf(this.root);
variables = parser.parseSequence(childs[0].getTextContent().trim(), "\\s+");
for (Object x : variables) {
control(x == null || x instanceof XVarInteger || x instanceof XVarSymbolic,
x + " " + " is not an integer or symbolic variable. Currently, only these types of variables are supported.");
// null is also accepted (although it corresponds to an undefined variable in an array) but the associated value must be *
}
values = parser.parseSequence(childs[1].getTextContent().trim(), "\\s+");
control(variables.length == values.length, "list and values must be of the same size");
for (int i = 0; i < variables.length; i++) {
if (variables[i] == null)
control(values[i] instanceof String && ((String) values[i]).equals("*"),
"* must be necessarily associated with a null variable (corresponding to a hole in an array)");
else {
XVar x = (XVar) variables[i];
map.put(x, values[i]);
if (!(values[i] instanceof String && ((String) values[i]).equals("*"))) {
if (x instanceof XVarInteger)
control(((Dom) x.dom).contains(intValueOf((XVarInteger) x)), "Wrong value for variable " + x);
else if (x instanceof XVarSymbolic)
control(((DomSymbolic) x.dom).contains(symbolicValueOf((XVarSymbolic) x)), "Wrong value for variable " + x);
else
unimplementedCase();
}
}
}
// map.entrySet().stream().forEach(e -> System.out.println(e.getKey() + "->" + e.getValue() + " "));
costs = root.getAttribute(TypeAtt.cost.name()).length() == 0 ? null
: Stream.of(root.getAttribute(TypeAtt.cost.name()).split("\\s+")).map(s -> new BigInteger(s)).toArray(BigInteger[]::new);
// costs = root.getAttribute(TypeAtt.cost.name()).length() == 0 ? null :
// parser.parseSequence(root.getAttribute(TypeAtt.cost.name()), "\\s+");
control(costs == null || costs.length == parser.oEntries.size(),
"Either you indicate no cost at all or you indicate a long cost for each objective.");
}
}
// ************************************************************************
// ***** Fields and Constructors
// ************************************************************************
private boolean competitionMode;
private BigInteger competitionComputedCost;
/** The current solution to test */
private Solution solution;
/** The current constraint of the (current) solution to test. */
private XCtr currCtr;
/** The current objective of the (current) solution to test. */
private XObj currObj;
/** The numbers used for the current constraint and objective. */
private int numCtr, numObj;
/** The list of ids of violated constraints (for the current solution). */
public List violatedCtrs;
/** The list of ids of invalid objectives (for the current solution). */
public List invalidObjs;
public SolutionChecker(boolean competitionMode, String fileName, InputStream solutionStream) throws Exception {
this.competitionMode = competitionMode;
implem().rawParameters(); // to avoid being obliged to override special functions
Scanner scanner = new Scanner(solutionStream);
if (competitionMode) {
List vlines = new ArrayList<>(), slines = new ArrayList<>();
while (scanner.hasNext()) {
String line = scanner.nextLine();
if (line.startsWith("s "))
slines.add(line);
else if (line.startsWith("v "))
vlines.add(line);
}
scanner.close();
String vline = vlines.size() == 0 ? null : vlines.stream().map(s -> s.substring(2)).collect(Collectors.joining(" ")).trim();
if (slines.size() != 1)
System.out.println("One s line expected");
else if (slines.get(0).startsWith("s SATISFIABLE") || slines.get(0).startsWith("s OPTIMUM")) {
if (vline == null || !vline.endsWith(""))
System.out.println("ERROR: no instantiation found");
else {
try {
Element elt = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(vline.getBytes()))
.getDocumentElement();
this.solution = new Solution(elt);
loadInstance(fileName);
if (violatedCtrs.size() == 0 && invalidObjs.size() == 0) {
System.out.println("OK\t" + (competitionComputedCost != null ? competitionComputedCost : ""));
} else {
System.out.println("INVALID Solution! (" + (violatedCtrs.size() + invalidObjs.size()) + " errors)");
if (violatedCtrs.size() > 0)
System.out.println(" Violated Constraint " + violatedCtrs.get(0));
if (invalidObjs.size() > 0)
System.out.println(" Invalid Objective " + invalidObjs.get(0));
}
} catch (Exception e) {
System.out.println("ERROR: the instantiation cannot be checked " + e);
e.printStackTrace();
}
}
}
} else {
// code below to be improved
String s = scanner.useDelimiter("\\A").next();
scanner.close();
while (true) {
implem().allIds.clear();
int start = s.indexOf("", start);
if (start == -1 || end == -1)
break;
String sol = s.substring(start, end + " ".length());
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(sol.getBytes()));
this.solution = new Solution(doc.getDocumentElement());
loadInstance(fileName);
s = s.substring(end + "".length());
}
}
}
protected void controlConstraint(boolean condition) {
if (!condition) {
String s = currCtr.toString();
violatedCtrs.add(currCtr.id + " : " + (s.length() > MAX_DISPLAY_STRING_SIZE ? s.substring(0, MAX_DISPLAY_STRING_SIZE) : s));
// System.out.println(currCtr.id + " : " + (s.length() > MAX_DISPLAY_STRING_SIZE ? s.substring(0, MAX_DISPLAY_STRING_SIZE) : s));
}
}
protected void controlObjective(BigInteger computedCost) {
competitionComputedCost = computedCost;
if (!competitionMode && solution.costs != null && solution.costs[numObj] != null && !computedCost.equals(solution.costs[numObj])) {
System.out.println(computedCost + " vs " + solution.costs[numObj]);
String s = currObj.toString();
invalidObjs.add(currObj.id + " : " + (s.length() > MAX_DISPLAY_STRING_SIZE ? s.substring(0, MAX_DISPLAY_STRING_SIZE) : s));
}
}
// ************************************************************************
// ***** Redefining Callback Functions on Main Components for Checking Solution(s)
// ************************************************************************
@Override
public void loadVariables(XParser parser) {
if (!competitionMode)
System.out.println("LOG: Check variables");
XCallbacks2.super.loadVariables(parser);
solution.parseVariablesAndValues(parser);
}
@Override
public void loadConstraints(XParser parser) {
if (!competitionMode)
System.out.println("LOG: Check constraints");
violatedCtrs = new ArrayList<>();
numCtr = -1;
XCallbacks2.super.loadConstraints(parser);
}
@Override
public void loadCtr(XCtr c) {
for (XVar x : c.vars()) {
Object obj = solution.map.get(x);
control(obj != null, x + "is not given a value although it is involved in one constraint ");
control(!(obj instanceof String && ((String) obj).equals("*")), x + " cannot be assigned the value * because it has not degree 0");
}
currCtr = c;
numCtr++;
XCallbacks2.super.loadCtr(c);
}
@Override
public void loadObjectives(XParser parser) {
invalidObjs = new ArrayList<>();
if (parser.oEntries.size() > 0) {
if (!competitionMode)
System.out.println("LOG: Check objectives");
numObj = -1;
XCallbacks2.super.loadObjectives(parser);
}
}
@Override
public void loadObj(XObj o) {
control(o.type != TypeObjective.LEX, "Currently, objectives of type lex are not managed by this checker.");
currObj = o;
numObj++;
XCallbacks2.super.loadObj(o);
}
@Override
public void endInstance() {
if (!competitionMode)
if (violatedCtrs.size() == 0 && invalidObjs.size() == 0) {
System.out.println("OK\t" + (competitionComputedCost != null ? competitionComputedCost : ""));
} else {
System.out.println("INVALID Solution! (" + (violatedCtrs.size() + invalidObjs.size()) + " errors)");
violatedCtrs.stream().forEach(c -> System.out.println(" Violated Constraint " + c));
invalidObjs.stream().forEach(o -> System.out.println(" Invalid Objective " + o));
}
}
// ************************************************************************
// ***** Methods on integer variables/constraints
// ************************************************************************
@Override
public void buildVarInteger(XVarInteger x, int minValue, int maxValue) {} // nothing to do
@Override
public void buildVarInteger(XVarInteger x, int[] values) {} // nothing to do
private long[] valuesOfTrees(XNode[] trees, int[] coeffs) {
XVarInteger[][] scopes = Stream.of(trees).map(t -> t.vars()).toArray(XVarInteger[][]::new);
return IntStream.range(0, trees.length)
.mapToLong(i -> new TreeEvaluator(trees[i]).evaluate(solution.intValuesOf(scopes[i])) * (coeffs == null ? 1 : coeffs[i])).toArray();
}
@Override
public void buildCtrIntension(String id, XVarInteger[] scope, XNodeParent tree) {
Utilities.control(tree.exactlyVars(scope), "Pb with scope");
controlConstraint(new TreeEvaluator(tree).evaluate(solution.intValuesOf(scope)) == 1);
}
@Override
public void buildCtrExtension(String id, XVarInteger x, int[] values, boolean positive, Set flags) {
controlConstraint(Utilities.contains(values, solution.intValueOf(x)) == positive);
}
@Override
public void buildCtrExtension(String id, XVarInteger[] list, int[][] tuples, boolean positive, Set flags) {
int[] tuple = solution.intValuesOf(list);
boolean found = Stream.of(tuples).parallel().anyMatch(t -> IntStream.range(0, t.length).allMatch(i -> t[i] == Constants.STAR || t[i] == tuple[i]));
// TODO dichotomic search instead of linear search ? compatible with * ?
controlConstraint(found == positive);
}
@Override
public void buildCtrExtension(String id, XVarInteger[] list, AbstractTuple[] tuples, boolean positive, Set flags) {
int[] tuple = solution.intValuesOf(list);
boolean found = Stream.of(tuples).parallel().anyMatch(t -> t.match(tuple));
controlConstraint(found == positive);
}
private String reachedState(String startState, XVarInteger[] list, Object[][] transitions) {
Map map = new HashMap<>();
Stream.of(transitions).forEach(tr -> map.put(tr[0] + ":" + tr[1], tr[2] + ""));
String current = startState;
for (XVarInteger x : list) {
String next = map.get(current + ":" + solution.intValueOf(x));
if (next == null)
return null;
else
current = next;
}
return current;
}
@Override
public void buildCtrRegular(String id, XVarInteger[] list, Object[][] transitions, String startState, String[] finalStates) {
String state = reachedState(startState, list, transitions);
controlConstraint(state != null && Arrays.stream(finalStates).anyMatch(v -> v.equals(state)));
}
@Override
public void buildCtrMDD(String id, XVarInteger[] list, Object[][] transitions) {
String state = reachedState((String) transitions[0][0], list, transitions); // The first state of the first transition MUST be the
// starting state
controlConstraint(state != null);
}
@Override
public void buildCtrAllDifferent(String id, XVarInteger[] list) {
controlConstraint(IntStream.of(solution.intValuesOf(list)).distinct().count() == list.length);
}
@Override
public void buildCtrAllDifferentExcept(String id, XVarInteger[] list, int[] except) {
XVarInteger[] sublist = Stream.of(list).filter(x -> !Utilities.contains(except, solution.intValueOf(x))).toArray(XVarInteger[]::new);
controlConstraint(IntStream.of(solution.intValuesOf(sublist)).distinct().count() == sublist.length);
}
private boolean distinctVectors(int[] v1, int[] v2) {
assert v1.length == v2.length;
return IntStream.range(0, v1.length).anyMatch(i -> v1[i] != v2[i]);
}
@Override
public void buildCtrAllDifferentList(String id, XVarInteger[][] lists) {
int[][] tuples = solution.intValuesOf(lists);
controlConstraint(
IntStream.range(0, tuples.length).allMatch(i -> IntStream.range(i + 1, tuples.length).allMatch(j -> distinctVectors(tuples[i], tuples[j]))));
}
@Override
public void buildCtrAllDifferentMatrix(String id, XVarInteger[][] matrix) {
int[][] tuples = solution.intValuesOf(matrix);
controlConstraint(IntStream.range(0, tuples.length).allMatch(i -> IntStream.of(tuples[i]).distinct().count() == tuples[i].length)); // rows
int[][] transposedTuples = IntStream.range(0, tuples.length).mapToObj(i -> IntStream.range(0, tuples[0].length).map(j -> tuples[j][i]).toArray())
.toArray(int[][]::new);
controlConstraint(
IntStream.range(0, transposedTuples.length).allMatch(i -> IntStream.of(transposedTuples[i]).distinct().count() == transposedTuples[i].length)); // cols
}
@Override
public void buildCtrAllDifferent(String id, XNode[] trees) {
long[] t = valuesOfTrees(trees, null);
controlConstraint(LongStream.of(t).distinct().count() == trees.length);
}
@Override
public void buildCtrAllEqual(String id, XVarInteger[] list) {
controlConstraint(IntStream.of(solution.intValuesOf(list)).distinct().count() == 1);
}
@Override
public void buildCtrOrdered(String id, XVarInteger[] list, TypeOperatorRel operator) {
int[] tuple = solution.intValuesOf(list);
controlConstraint(IntStream.range(0, tuple.length - 1).allMatch(i -> operator.isValidFor(tuple[i], tuple[i + 1])));
}
@Override
public void buildCtrOrdered(String id, XVarInteger[] list, int[] lengths, TypeOperatorRel operator) {
int[] tuple = solution.intValuesOf(list);
controlConstraint(IntStream.range(0, tuple.length - 1).allMatch(i -> operator.isValidFor(tuple[i] + lengths[i], tuple[i + 1])));
}
@Override
public void buildCtrOrdered(String id, XVarInteger[] list, XVarInteger[] lengths, TypeOperatorRel operator) {
int[] ls = solution.intValuesOf(list);
int[] lg = solution.intValuesOf(lengths);
controlConstraint(IntStream.range(0, ls.length - 1).allMatch(i -> operator.isValidFor(ls[i] + lg[i], ls[i + 1])));
}
private boolean orderedVectors(int[] v1, int[] v2, TypeOperatorRel operator) {
assert v1.length == v2.length;
for (int i = 0; i < v1.length; i++) {
if (operator == TypeOperatorRel.LE || operator == TypeOperatorRel.LT) {
if (v1[i] < v2[i])
return true;
if (v1[i] > v2[i])
return false;
} else if (operator == TypeOperatorRel.GE || operator == TypeOperatorRel.GT) {
if (v1[i] > v2[i])
return true;
if (v1[i] < v2[i])
return false;
}
}
return operator == TypeOperatorRel.LE || operator == TypeOperatorRel.GE;
}
@Override
public void buildCtrLex(String id, XVarInteger[][] lists, TypeOperatorRel operator) {
int[][] tuples = solution.intValuesOf(lists);
controlConstraint(IntStream.range(0, tuples.length)
.allMatch(i -> IntStream.range(i + 1, tuples.length).allMatch(j -> orderedVectors(tuples[i], tuples[j], operator))));
}
@Override
public void buildCtrLexMatrix(String id, XVarInteger[][] matrix, TypeOperatorRel operator) {
int[][] tuples = solution.intValuesOf(matrix);
controlConstraint(IntStream.range(0, tuples.length)
.allMatch(i -> IntStream.range(i + 1, tuples.length).allMatch(j -> orderedVectors(tuples[i], tuples[j], operator))));
int[][] transposedTuples = IntStream.range(0, tuples[0].length).mapToObj(i -> IntStream.range(0, tuples.length).map(j -> tuples[j][i]).toArray())
.toArray(int[][]::new);
controlConstraint(IntStream.range(0, transposedTuples.length).allMatch(
i -> IntStream.range(i + 1, transposedTuples.length).allMatch(j -> orderedVectors(transposedTuples[i], transposedTuples[j], operator))));
}
protected void checkCondition(int value, Condition condition) {
if (condition instanceof ConditionVar)
controlConstraint(((ConditionVar) condition).operator.isValidFor(value, solution.intValueOf((XVarInteger) ((ConditionVar) condition).x)));
else if (condition instanceof ConditionVal)
controlConstraint(((ConditionVal) condition).operator.isValidFor(value, ((ConditionVal) condition).k));
else if (condition instanceof ConditionIntvl)
controlConstraint(((ConditionIntvl) condition).operator.isValidFor(value, ((ConditionIntvl) condition).min, ((ConditionIntvl) condition).max));
else if (condition instanceof ConditionIntset)
controlConstraint(((ConditionIntset) condition).operator.isValidFor(value, ((ConditionIntset) condition).t));
}
@Override
public void buildCtrSum(String id, XVarInteger[] list, Condition condition) {
checkCondition(IntStream.of(solution.intValuesOf(list)).sum(), condition);
}
@Override
public void buildCtrSum(String id, XVarInteger[] list, int[] coeffs, Condition condition) {
checkCondition(IntStream.range(0, list.length).map(i -> solution.intValueOf(list[i]) * coeffs[i]).sum(), condition);
}
@Override
public void buildCtrSum(String id, XVarInteger[] list, XVarInteger[] coeffs, Condition condition) {
checkCondition(IntStream.range(0, list.length).map(i -> solution.intValueOf(list[i]) * solution.intValueOf(coeffs[i])).sum(), condition);
}
@Override
public void buildCtrSum(String id, XNode[] trees, Condition condition) {
long[] t = valuesOfTrees(trees, null);
BigInteger b = BigInteger.ZERO;
for (long v : t)
b = b.add(BigInteger.valueOf(v));
checkCondition(b.intValueExact(), condition);
}
@Override
public void buildCtrSum(String id, XNode[] trees, int[] coeffs, Condition condition) {
long[] t = valuesOfTrees(trees, coeffs);
BigInteger b = BigInteger.ZERO;
for (long v : t)
b = b.add(BigInteger.valueOf(v));
checkCondition(b.intValueExact(), condition);
}
@Override
public void buildCtrSum(String id, XNode[] trees, XVarInteger[] coeffs, Condition condition) {
XVarInteger[][] scopes = Stream.of(trees).map(t -> t.vars()).toArray(XVarInteger[][]::new);
long[] t = IntStream.range(0, trees.length)
.mapToLong(i -> new TreeEvaluator(trees[i]).evaluate(solution.intValuesOf(scopes[i])) * solution.intValueOf(coeffs[i])).toArray();
BigInteger b = BigInteger.ZERO;
for (long v : t)
b = b.add(BigInteger.valueOf(v));
checkCondition(b.intValueExact(), condition);
}
@Override
public void buildCtrCount(String id, XVarInteger[] list, int[] values, Condition condition) {
checkCondition((int) IntStream.of(solution.intValuesOf(list)).filter(v -> Utilities.contains(values, v)).count(), condition);
}
@Override
public void buildCtrCount(String id, XVarInteger[] list, XVarInteger[] values, Condition condition) {
buildCtrCount(id, list, solution.intValuesOf(values), condition);
}
@Override
public void buildCtrNValuesExcept(String id, XVarInteger[] list, int[] except, Condition condition) {
checkCondition((int) IntStream.of(solution.intValuesOf(list)).filter(v -> !Utilities.contains(except, v)).distinct().count(), condition);
}
@Override
public void buildCtrNValues(String id, XVarInteger[] list, Condition condition) {
buildCtrNValuesExcept(id, list, new int[] {}, condition);
}
@Override
public void buildCtrCardinality(String id, XVarInteger[] list, boolean closed, int[] values, int[] occurs) {
int tuple[] = solution.intValuesOf(list);
controlConstraint(IntStream.range(0, values.length).allMatch(i -> IntStream.of(tuple).filter(v -> v == values[i]).count() == occurs[i]));
controlConstraint(!closed || IntStream.of(tuple).allMatch(v -> Utilities.contains(values, v)));
}
@Override
public void buildCtrCardinality(String id, XVarInteger[] list, boolean closed, int[] values, XVarInteger[] occurs) {
buildCtrCardinality(id, list, closed, values, solution.intValuesOf(occurs));
}
@Override
public void buildCtrCardinality(String id, XVarInteger[] list, boolean closed, XVarInteger[] values, XVarInteger[] occurs) {
buildCtrCardinality(id, list, closed, solution.intValuesOf(values), solution.intValuesOf(occurs));
}
@Override
public void buildCtrCardinality(String id, XVarInteger[] list, boolean closed, XVarInteger[] values, int[] occurs) {
buildCtrCardinality(id, list, closed, solution.intValuesOf(values), occurs);
}
@Override
public void buildCtrCardinality(String id, XVarInteger[] list, boolean closed, int[] values, int[] occursMin, int[] occursMax) {
int[] tuple = solution.intValuesOf(list);
controlConstraint(IntStream.range(0, values.length).allMatch(i -> {
int nb = (int) IntStream.of(tuple).filter(v -> v == values[i]).count();
return occursMin[i] <= nb && nb <= occursMax[i];
}));
controlConstraint(!closed || IntStream.of(tuple).allMatch(v -> Utilities.contains(values, v)));
}
@Override
public void buildCtrCardinality(String id, XVarInteger[] list, boolean closed, XVarInteger[] values, int[] occursMin, int[] occursMax) {
buildCtrCardinality(id, list, closed, solution.intValuesOf(values), occursMin, occursMax);
}
@Override
public void buildCtrMaximum(String id, XVarInteger[] list, Condition condition) {
checkCondition(IntStream.of(solution.intValuesOf(list)).max().getAsInt(), condition);
}
@Override
public void buildCtrMaximum(String id, XNode[] trees, Condition condition) {
long[] t = valuesOfTrees(trees, null);
checkCondition(Utilities.safeInt(LongStream.of(t).max().getAsLong()), condition);
}
@Override
public void buildCtrMinimum(String id, XVarInteger[] list, Condition condition) {
checkCondition(IntStream.of(solution.intValuesOf(list)).min().getAsInt(), condition);
}
@Override
public void buildCtrMinimum(String id, XNode[] trees, Condition condition) {
long[] t = valuesOfTrees(trees, null);
checkCondition(Utilities.safeInt(LongStream.of(t).min().getAsLong()), condition);
}
private void checkArgMin(String id, int[] tuple, int startIndex, XVarInteger index, TypeRank rank, Condition condition, int value) {
int i = solution.intValueOf(index) - startIndex;
controlConstraint(tuple[i] == value);
controlConstraint(rank != TypeRank.FIRST || !Utilities.contains(tuple, value, 0, i - 1));
controlConstraint(rank != TypeRank.LAST || !Utilities.contains(tuple, value, i + 1, tuple.length - 1));
if (condition != null)
checkCondition(value, condition);
}
@Override
public void buildCtrMaximum(String id, XVarInteger[] list, int startIndex, XVarInteger index, TypeRank rank, Condition condition) {
int[] tuple = solution.intValuesOf(list);
checkArgMin(id, tuple, startIndex, index, rank, condition, IntStream.of(tuple).max().getAsInt());
}
@Override
public void buildCtrMinimum(String id, XVarInteger[] list, int startIndex, XVarInteger index, TypeRank rank, Condition condition) {
int[] tuple = solution.intValuesOf(list);
checkArgMin(id, tuple, startIndex, index, rank, condition, IntStream.of(tuple).min().getAsInt());
}
@Override
public void buildCtrChannel(String id, XVarInteger[] list, int startIndex) { // x[i]=j iff x[j]=i
controlConstraint(IntStream.range(0, list.length).allMatch(i -> {
int j = solution.intValueOf(list[i]) - startIndex;
return 0 <= j && j < list.length && solution.intValueOf(list[j]) == i + startIndex; // + startIndex ???
}));
}
@Override
public void buildCtrChannel(String id, XVarInteger[] list1, int startIndex1, XVarInteger[] list2, int startIndex2) {
int[] t1 = solution.intValuesOf(list1), t2 = solution.intValuesOf(list2);
// x[i]=j iff x[j]=i
controlConstraint(IntStream.range(0, t1.length).allMatch(i -> {
int j = t1[i] - startIndex2;
return 0 <= j && j < t2.length && (t2[j] - startIndex1) == i;
}));
}
@Override
public void buildCtrChannel(String id, XVarInteger[] list, int startIndex, XVarInteger value) {
int[] tuple = solution.intValuesOf(list);
controlConstraint(IntStream.of(tuple).filter(v -> v == 1).count() == 1);
int pos = solution.intValueOf(value) - startIndex;
controlConstraint(0 <= pos && pos < list.length && tuple[pos] == 1);
}
@Override
public void buildCtrElement(String id, XVarInteger[] list, int value) {
controlConstraint(Utilities.contains(solution.intValuesOf(list), value));
}
@Override
public void buildCtrElement(String id, XVarInteger[] list, XVarInteger value) {
buildCtrElement(id, list, solution.intValueOf(value));
}
private void controlElement(String id, int[] list, int startIndex, XVarInteger index, TypeRank rank, int value) {
int i = solution.intValueOf(index) - startIndex;
controlConstraint(list[i] == value);
controlConstraint(rank != TypeRank.FIRST || !Utilities.contains(list, value, 0, i - 1));
controlConstraint(rank != TypeRank.LAST || !Utilities.contains(list, value, i + 1, list.length - 1));
}
@Override
public void buildCtrElement(String id, XVarInteger[] list, int startIndex, XVarInteger index, TypeRank rank, int value) {
controlElement(id, solution.intValuesOf(list), startIndex, index, rank, value);
}
@Override
public void buildCtrElement(String id, XVarInteger[] list, int startIndex, XVarInteger index, TypeRank rank, XVarInteger value) {
buildCtrElement(id, list, startIndex, index, rank, solution.intValueOf(value));
}
@Override
public void buildCtrElement(String id, int[] list, int startIndex, XVarInteger index, TypeRank rank, XVarInteger value) {
controlElement(id, list, startIndex, index, rank, solution.intValueOf(value));
}
@Override
public void buildCtrElement(String id, int[][] matrix, int startRowIndex, XVarInteger rowIndex, int startColIndex, XVarInteger colIndex, int value) {
int i = solution.intValueOf(rowIndex) - startRowIndex;
int j = solution.intValueOf(colIndex) - startColIndex;
controlConstraint(matrix[i][j] == value);
}
@Override
public void buildCtrElement(String id, int[][] matrix, int startRowIndex, XVarInteger rowIndex, int startColIndex, XVarInteger colIndex,
XVarInteger value) {
buildCtrElement(id, matrix, startRowIndex, rowIndex, startColIndex, colIndex, solution.intValueOf(value));
}
@Override
public void buildCtrElement(String id, XVarInteger[][] matrix, int startRowIndex, XVarInteger rowIndex, int startColIndex, XVarInteger colIndex,
int value) {
buildCtrElement(id, solution.intValuesOf(matrix), startRowIndex, rowIndex, startColIndex, colIndex, value);
}
@Override
public void buildCtrElement(String id, XVarInteger[][] matrix, int startRowIndex, XVarInteger rowIndex, int startColIndex, XVarInteger colIndex,
XVarInteger value) {
buildCtrElement(id, matrix, startRowIndex, rowIndex, startColIndex, colIndex, solution.intValueOf(value));
}
@Override
public void buildCtrStretch(String id, XVarInteger[] list, int[] values, int[] widthsMin, int[] widthsMax) {
int[] tuple = solution.intValuesOf(list);
for (int i = 0, j; i < tuple.length; i = j) {
int v = tuple[i]; // value of the current stretch
for (j = i + 1; j < tuple.length && tuple[j] == v; j++)
;
int width = j - i, pos = IntStream.range(0, values.length).filter(p -> values[p] == v).findFirst().getAsInt();
controlConstraint(widthsMin[pos] <= width && width <= widthsMax[pos]);
}
}
@Override
public void buildCtrStretch(String id, XVarInteger[] list, int[] values, int[] widthsMin, int[] widthsMax, int[][] patterns) {
buildCtrStretch(id, list, values, widthsMin, widthsMax);
int[] tuple = solution.intValuesOf(list);
controlConstraint(IntStream.range(0, tuple.length - 1)
.noneMatch(i -> tuple[i] != tuple[i + 1] && Stream.of(patterns).anyMatch(t -> t[0] == tuple[i] && t[1] == tuple[i + 1])));
}
@Override
public void buildCtrNoOverlap(String id, XVarInteger[] origins, int[] lengths, boolean zeroIgnored) {
int[] tuple = solution.intValuesOf(origins);
int[] sublist = IntStream.range(0, origins.length).filter(i -> !zeroIgnored || lengths[i] != 0).toArray();
controlConstraint(IntStream.range(0, sublist.length).allMatch(i -> IntStream.range(0, sublist.length).filter(j -> j > i)
.allMatch(j -> tuple[sublist[i]] + lengths[sublist[i]] <= tuple[sublist[j]] || tuple[sublist[j]] + lengths[sublist[j]] <= tuple[sublist[i]])));
}
@Override
public void buildCtrNoOverlap(String id, XVarInteger[] origins, XVarInteger[] lengths, boolean zeroIgnored) {
buildCtrNoOverlap(id, origins, solution.intValuesOf(lengths), zeroIgnored);
}
@Override
public void buildCtrNoOverlap(String id, XVarInteger[][] origins, int[][] lengths, boolean zeroIgnored) {
int[][] tuples = solution.intValuesOf(origins);
int[] sublist = IntStream.range(0, origins.length).filter(i -> !zeroIgnored || IntStream.of(lengths[i]).allMatch(l -> l != 0)).toArray();
controlConstraint(IntStream.range(0, sublist.length)
.allMatch(i -> IntStream.range(0, sublist.length).filter(j -> j > i).allMatch(
j -> IntStream.range(0, origins[0].length).anyMatch(k -> tuples[sublist[i]][k] + lengths[sublist[i]][k] <= tuples[sublist[j]][k]
|| tuples[sublist[j]][k] + lengths[sublist[j]][k] <= tuples[sublist[i]][k]))));
}
@Override
public void buildCtrNoOverlap(String id, XVarInteger[][] origins, XVarInteger[][] lengths, boolean zeroIgnored) {
buildCtrNoOverlap(id, origins, solution.intValuesOf(lengths), zeroIgnored);
}
@Override
public void buildCtrCumulative(String id, XVarInteger[] origins, int[] lengths, int[] heights, Condition condition) {
int[] tuple = solution.intValuesOf(origins);
int min = IntStream.of(tuple).min().getAsInt(), max = IntStream.range(0, tuple.length).map(i -> tuple[i] + lengths[i]).max().getAsInt();
IntStream.rangeClosed(min, max).forEach(t -> {
int h = IntStream.range(0, tuple.length).filter(i -> tuple[i] <= t && t < tuple[i] + lengths[i]).map(i -> heights[i]).sum();
checkCondition(h, condition);
});
}
@Override
public void buildCtrCumulative(String id, XVarInteger[] origins, int[] lengths, XVarInteger[] heights, Condition condition) {
buildCtrCumulative(id, origins, lengths, solution.intValuesOf(heights), condition);
}
@Override
public void buildCtrCumulative(String id, XVarInteger[] origins, XVarInteger[] lengths, int[] heights, Condition condition) {
buildCtrCumulative(id, origins, solution.intValuesOf(lengths), heights, condition);
}
@Override
public void buildCtrCumulative(String id, XVarInteger[] origins, XVarInteger[] lengths, XVarInteger[] heights, Condition condition) {
buildCtrCumulative(id, origins, solution.intValuesOf(lengths), solution.intValuesOf(heights), condition);
}
@Override
public void buildCtrCumulative(String id, XVarInteger[] origins, int[] lengths, XVarInteger[] ends, int[] heights, Condition condition) {
buildCtrCumulative(id, origins, lengths, heights, condition);
controlConstraint(IntStream.range(0, origins.length).allMatch(i -> solution.intValueOf(origins[i]) + lengths[i] == solution.intValueOf(ends[i])));
}
@Override
public void buildCtrCumulative(String id, XVarInteger[] origins, int[] lengths, XVarInteger[] ends, XVarInteger[] heights, Condition condition) {
buildCtrCumulative(id, origins, lengths, ends, solution.intValuesOf(heights), condition);
}
@Override
public void buildCtrCumulative(String id, XVarInteger[] origins, XVarInteger[] lengths, XVarInteger[] ends, int[] heights, Condition condition) {
buildCtrCumulative(id, origins, solution.intValuesOf(lengths), ends, heights, condition);
}
@Override
public void buildCtrCumulative(String id, XVarInteger[] origins, XVarInteger[] lengths, XVarInteger[] ends, XVarInteger[] heights, Condition condition) {
buildCtrCumulative(id, origins, solution.intValuesOf(lengths), ends, solution.intValuesOf(heights), condition);
}
@Override
public void buildCtrInstantiation(String id, XVarInteger[] list, int[] values) {
controlConstraint(IntStream.range(0, list.length).allMatch(i -> solution.intValueOf(list[i]) == values[i]));
}
@Override
public void buildCtrClause(String id, XVarInteger[] pos, XVarInteger[] neg) {
controlConstraint(IntStream.of(solution.intValuesOf(pos)).anyMatch(p -> p == 1) || IntStream.of(solution.intValuesOf(neg)).anyMatch(p -> p == 0));
}
@Override
public void buildCtrCircuit(String id, XVarInteger[] list, int startIndex) {
control(startIndex == 0, "Other cases currently not implemented");
int[] tuple = solution.intValuesOf(list);
controlConstraint(IntStream.of(tuple).distinct().count() == list.length);
int nbLoops = (int) IntStream.range(0, list.length).filter(i -> tuple[i] == i).count();
controlConstraint(nbLoops != list.length);
int i = 0;
while (i < list.length && tuple[i] == i)
i++;
Set s = new TreeSet<>();
while (tuple[i] != i && !s.contains(tuple[i])) {
s.add(tuple[i]);
i = tuple[i];
}
controlConstraint(s.size() == (tuple.length - nbLoops));
}
@Override
public void buildCtrCircuit(String id, XVarInteger[] list, int startIndex, int size) {
buildCtrCircuit(id, list, startIndex);
int nbLoops = (int) IntStream.range(0, list.length).filter(i -> solution.intValueOf(list[i]) == i).count();
controlConstraint(size == (list.length - nbLoops));
}
@Override
public void buildCtrCircuit(String id, XVarInteger[] list, int startIndex, XVarInteger size) {
buildCtrCircuit(id, list, startIndex, solution.intValueOf(size));
}
// ************************************************************************
// ***** Methods for managing objectives
// ************************************************************************
@Override
public void buildObjToMinimize(String id, XVarInteger x) {
controlObjective(BigInteger.valueOf(solution.intValueOf(x)));
}
@Override
public void buildObjToMaximize(String id, XVarInteger x) {
buildObjToMinimize(id, x); // possible to refer to 'minimize' because the code for checking is independent of minimization/maximization
}
@Override
public void buildObjToMinimize(String id, XNodeParent tree) {
controlObjective(BigInteger.valueOf(new TreeEvaluator(tree).evaluate(solution.intValuesOf(tree.vars()))));
}
@Override
public void buildObjToMaximize(String id, XNodeParent tree) {
buildObjToMinimize(id, tree); // possible to refer to 'minimize' because the code for checking is independent of minimization/maximization
}
@Override
public void buildObjToMinimize(String id, TypeObjective type, XVarInteger[] list) {
buildObjToMinimize(id, type, list, null);
}
@Override
public void buildObjToMaximize(String id, TypeObjective type, XVarInteger[] list) {
buildObjToMinimize(id, type, list, null);
}
private void computeObjective(String id, TypeObjective type, BigInteger[] list, int[] coeffs) {
BigInteger[] bis = coeffs == null ? list
: IntStream.range(0, list.length).mapToObj(i -> list[i].multiply(BigInteger.valueOf(coeffs[i]))).toArray(BigInteger[]::new);
if (type == NVALUES) {
controlObjective(BigInteger.valueOf(Stream.of(bis).distinct().count()));
} else {
assert type != LEX;
BigInteger computedCost = bis[0];
for (int i = 1; i < list.length; i++) {
if (type == SUM)
computedCost = computedCost.add(bis[i]);
if (type == PRODUCT)
computedCost = computedCost.multiply(bis[i]);
if (type == MINIMUM)
computedCost = computedCost.min(bis[i]);
if (type == MAXIMUM)
computedCost = computedCost.max(bis[i]);
}
controlObjective(computedCost);
}
}
@Override
public void buildObjToMinimize(String id, TypeObjective type, XVarInteger[] list, int[] coeffs) {
computeObjective(id, type, Stream.of(list).map(x -> BigInteger.valueOf(solution.intValueOf(x))).toArray(BigInteger[]::new), coeffs);
}
@Override
public void buildObjToMaximize(String id, TypeObjective type, XVarInteger[] list, int[] coeffs) {
buildObjToMinimize(id, type, list, coeffs); // possible to refer to 'minimize' because the code for checking is independent of
// minimization/maximization
}
@Override
public void buildObjToMinimize(String id, TypeObjective type, XNode[] trees) {
buildObjToMinimize(id, type, trees, null);
}
@Override
public void buildObjToMaximize(String id, TypeObjective type, XNode[] trees) {
buildObjToMinimize(id, type, trees); // possible to refer to 'minimize' because the code for checking is independent of
// minimization/maximization
}
@Override
public void buildObjToMinimize(String id, TypeObjective type, XNode[] trees, int[] coeffs) {
long[] t = valuesOfTrees(trees, null);
computeObjective(id, type, IntStream.range(0, t.length).mapToObj(i -> BigInteger.valueOf(t[i])).toArray(BigInteger[]::new), coeffs);
}
@Override
public void buildObjToMaximize(String id, TypeObjective type, XNode[] trees, int[] coeffs) {
buildObjToMinimize(id, type, trees, coeffs); // possible to refer to 'minimize' because the code for checking is independent of
// minimization/maximization
}
// ************************************************************************
// ***** Methods on symbolic variables/constraints
// ************************************************************************
/** The map that associates an arbitrary integer value with each symbol. */
private Map mapOfSymbols = new HashMap<>();
@Override
public void buildVarSymbolic(XVarSymbolic x, String[] values) {
for (String v : values)
if (mapOfSymbols.get(v) == null)
mapOfSymbols.put(v, mapOfSymbols.size());
}
@Override
public void buildCtrIntension(String id, XVarSymbolic[] scope, XNodeParent tree) {
Utilities.control(tree.exactlyVars(scope), "Pb with scope");
controlConstraint(new TreeEvaluator(tree, mapOfSymbols)
.evaluate(Stream.of(solution.symbolicValuesOf(scope)).mapToInt(s -> mapOfSymbols.get(s)).toArray()) == 1);
}
@Override
public void buildCtrExtension(String id, XVarSymbolic x, String[] values, boolean positive, Set flags) {
controlConstraint(Stream.of(values).anyMatch(v -> v.equals(solution.symbolicValueOf(x))) == positive);
}
@Override
public void buildCtrExtension(String id, XVarSymbolic[] list, String[][] tuples, boolean positive, Set flags) {
String[] tuple = solution.symbolicValuesOf(list);
boolean found = Stream.of(tuples).anyMatch(t -> IntStream.range(0, t.length).allMatch(i -> t[i].equals("*") || t[i].equals(tuple[i])));
// TODO dichotomic search instead of linear search ? compatible with * ?
controlConstraint(found == positive);
}
@Override
public void buildCtrAllDifferent(String id, XVarSymbolic[] list) {
controlConstraint(Stream.of(solution.symbolicValuesOf(list)).distinct().count() == list.length);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy