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

org.chocosolver.parser.xcsp.XCSPParser Maven / Gradle / Ivy

There is a newer version: 4.10.17
Show newest version
/*
 * This file is part of choco-parsers, http://choco-solver.org/
 *
 * Copyright (c) 2024, IMT Atlantique. All rights reserved.
 *
 * Licensed under the BSD 4-clause license.
 *
 * See LICENSE file in the project root for full license information.
 */
package org.chocosolver.parser.xcsp;

import gnu.trove.map.hash.TIntObjectHashMap;
import gnu.trove.map.hash.TObjectIntHashMap;
import org.chocosolver.parser.ParserException;
import org.chocosolver.solver.Model;
import org.chocosolver.solver.Solver;
import org.chocosolver.solver.constraints.extension.Tuples;
import org.chocosolver.solver.constraints.nary.automata.FA.FiniteAutomaton;
import org.chocosolver.solver.expression.discrete.arithmetic.ArExpression;
import org.chocosolver.solver.expression.discrete.arithmetic.NaArExpression;
import org.chocosolver.solver.expression.discrete.logical.LoExpression;
import org.chocosolver.solver.expression.discrete.logical.NaLoExpression;
import org.chocosolver.solver.expression.discrete.relational.NaReExpression;
import org.chocosolver.solver.expression.discrete.relational.ReExpression;
import org.chocosolver.solver.search.strategy.Search;
import org.chocosolver.solver.search.strategy.selectors.values.IntValueSelector;
import org.chocosolver.solver.search.strategy.selectors.variables.InputOrder;
import org.chocosolver.solver.variables.BoolVar;
import org.chocosolver.solver.variables.IntVar;
import org.chocosolver.solver.variables.Task;
import org.chocosolver.util.objects.graphs.MultivaluedDecisionDiagram;
import org.chocosolver.util.objects.queues.CircularQueue;
import org.chocosolver.util.objects.setDataStructures.iterable.IntIterableRangeSet;
import org.chocosolver.util.tools.ArrayUtils;
import org.chocosolver.util.tools.MathUtils;
import org.chocosolver.util.tools.VariableUtils;
import org.xcsp.common.Condition;
import org.xcsp.common.Types;
import org.xcsp.common.predicates.XNode;
import org.xcsp.common.predicates.XNodeParent;
import org.xcsp.common.structures.Transition;
import org.xcsp.parser.callbacks.XCallbacks2;
import org.xcsp.parser.entries.XConstraints;
import org.xcsp.parser.entries.XVariables;

import java.io.File;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import static org.xcsp.common.Constants.STAR_INT;
import static org.xcsp.common.Types.TypeExpr.*;

/**
 * 

* Project: choco-parsers. * * @author Charles Prud'homme * @since 01/06/2016. */ public class XCSPParser implements XCallbacks2 { private static final String S_INST_IN = "v mvars; protected HashSet symbolics; protected TObjectIntHashMap symbolToInt; protected TIntObjectHashMap intToSymbol; protected int unusedSymbol = 0; private ArrayList ovars; /** * The model to feed */ Model model; Implem implem; public void model(Model model, String instance) throws Exception { this.model = model; this.mvars = new HashMap<>(); this.symbolics = new HashSet<>(); this.symbolToInt = new TObjectIntHashMap<>(); this.intToSymbol = new TIntObjectHashMap<>(); this.implem = new Implem(this); File file = new File(instance); if (file.exists()) { loadInstance(instance); } else { throw new RuntimeException("FILE DOES NOT EXIST"); } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////// VARIABLES ////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public void buildVarInteger(XVariables.XVarInteger x, int minValue, int maxValue) { mvars.put(x, model.intVar(x.id, minValue, maxValue)); } @Override public void buildVarInteger(XVariables.XVarInteger x, int[] values) { mvars.put(x, model.intVar(x.id, values)); } @Override public void buildVarSymbolic(XVariables.XVarSymbolic x, String[] values) { int[] domain = new int[values.length]; for (int i = 0; i < values.length; i++) { int value; if (symbolToInt.contains(values[i])) { value = symbolToInt.get(values[i]); } else { value = unusedSymbol++; symbolToInt.put(values[i], value); intToSymbol.put(value, values[i]); } domain[i] = value; } mvars.put(x, model.intVar(x.id, domain)); symbolics.add(var(x)); } @Override public void buildCtrIntension(String id, XVariables.XVarInteger[] scope, XNodeParent tree) { if(tree.type == IF){ ReExpression b = buildRe(tree.sons[0]); b.imp(buildRe(tree.sons[1])).post(); b.not().imp(buildRe(tree.sons[2])).post(); } else { ReExpression exp = buildRe(tree); if (VariableUtils.domainCardinality(vars(scope)) < Integer.MAX_VALUE / 1000) { exp.extension().post(); } else { exp.decompose().post(); } } } private ArExpression[] extractAr(XNode[] sons) { return Arrays.stream(sons).map(this::buildAr).toArray(ArExpression[]::new); } private ReExpression[] extractRe(XNode[] sons) { return Arrays.stream(sons).map(this::buildRe).toArray(ReExpression[]::new); } private ReExpression buildRe(XNode tree) { Types.TypeExpr type = tree.type; if (type == Types.TypeExpr.VAR) { return (BoolVar) var(tree.var(0)); } else if (type == Types.TypeExpr.LONG) { // TODO: deal with boolean primitive? return (BoolVar) model.intVar(tree.val(0)); } XNode[] sons = tree.sons; switch (type) { // relationnal case LT: return buildAr(sons[0]).lt(buildAr(sons[1])); case LE: return buildAr(sons[0]).le(buildAr(sons[1])); case GE: return buildAr(sons[0]).ge(buildAr(sons[1])); case GT: return buildAr(sons[0]).gt(buildAr(sons[1])); case NE: return buildAr(sons[0]).ne(buildAr(sons[1])); case IN: List set0 = new ArrayList<>(); for (XNode sonsons : sons[1].sons) { set0.add(buildAr(sonsons)); } //noinspection ConstantForZeroLengthArrayAllocation return buildAr(sons[0]).in(set0.toArray(new ArExpression[0])); case NOTIN: List set1 = new ArrayList<>(); for (XNode sonsons : sons[1].sons) { set1.add(buildAr(sonsons)); } //noinspection ConstantForZeroLengthArrayAllocation return buildAr(sons[0]).notin(set1.toArray(new ArExpression[0])); case EQ: if (sons.length == 2) { return buildAr(sons[0]).eq(buildAr(sons[1])); } else { return new NaReExpression(ReExpression.Operator.EQ, extractAr(sons)); } // logical case AND: return new NaLoExpression(LoExpression.Operator.AND, extractRe(sons)); case OR: return new NaLoExpression(LoExpression.Operator.OR, extractRe(sons)); case XOR: return new NaLoExpression(LoExpression.Operator.XOR, extractRe(sons)); case IFF: return new NaLoExpression(LoExpression.Operator.IFF, extractRe(sons)); case IMP: return buildRe(sons[0]).imp(buildRe(sons[1])); case NOT: return buildRe(sons[0]).not(); default: throw new UnsupportedOperationException("Unknown type : " + type); } } private ArExpression buildAr(XNode node) { Types.TypeExpr type = node.type; if (type == Types.TypeExpr.VAR) { return var(node.var(0)); } else if (type == Types.TypeExpr.SYMBOL) { return model.intVar(symbolToInt.get(node.toString())); } else if (type == Types.TypeExpr.LONG) { return new ArExpression.IntPrimitive(node.val(0), model); } XNode[] sons = node.sons; if (type.isLogicalOperator() && type.arityMax > 1 || type.equals(Types.TypeExpr.NOT)) { ReExpression[] res = extractRe(sons); switch (type) { // logical case AND: return new NaLoExpression(LoExpression.Operator.AND, res); case OR: return new NaLoExpression(LoExpression.Operator.OR, res); case XOR: return new NaLoExpression(LoExpression.Operator.XOR, res); case IFF: return new NaLoExpression(LoExpression.Operator.IFF, res); case IMP: return new NaLoExpression(LoExpression.Operator.OR, res); case NOT: return res[0].not(); default: throw new UnsupportedOperationException("Unknown type : " + type); } } else { if(type == IN){ List set = new ArrayList<>(); for (XNode sonsons : sons[1].sons) { set.add(buildAr(sonsons)); } //noinspection ConstantForZeroLengthArrayAllocation return buildAr(sons[0]).in(set.toArray(new ArExpression[0])); }else if(type == NOTIN){ List set = new ArrayList<>(); for (XNode sonsons : sons[1].sons) { set.add(buildAr(sonsons)); } //noinspection ConstantForZeroLengthArrayAllocation return buildAr(sons[0]).notin(set.toArray(new ArExpression[0])); } ArExpression[] aes = extractAr(sons); switch (type) { // arithmetic // return this == ADD || this == SUB || this == MUL || this == DIV || this == MOD || this == POW || this == DIST; case ADD: return new NaArExpression(ArExpression.Operator.ADD, aes); case SUB: return aes[0].sub(aes[1]); case MUL: return new NaArExpression(ArExpression.Operator.MUL, aes); case MIN: return new NaArExpression(ArExpression.Operator.MIN, aes); case MAX: return new NaArExpression(ArExpression.Operator.MAX, aes); case DIV: return aes[0].div(aes[1]); case MOD: return aes[0].mod(aes[1]); case POW: return aes[0].pow(aes[1]); case DIST: return aes[0].dist(aes[1]); case NEG: return aes[0].neg(); case ABS: return aes[0].abs(); case SQR: return aes[0].sqr(); // relationnal case LT: return aes[0].lt(aes[1]); case LE: return aes[0].le(aes[1]); case GE: return aes[0].ge(aes[1]); case GT: return aes[0].gt(aes[1]); case NE: return aes[0].ne(aes[1]); case EQ: if (aes.length == 2) { return aes[0].eq(aes[1]); } else { return new NaReExpression(ReExpression.Operator.EQ, aes); } // logical case IF: return ((ReExpression) aes[0]).ift(aes[1], aes[2]); default: throw new UnsupportedOperationException("Unknown type : " + type); } } } private IntVar var(V var) { return mvars.get(var); } private IntVar[] vars(V[] vars) { return Arrays.stream(vars).map(this::var).toArray(IntVar[]::new); } private IntVar[][] vars(V[][] vars) { return Arrays.stream(vars).map(this::vars).toArray(IntVar[][]::new); } private BoolVar bool(V var) { return (BoolVar) mvars.get(var); } private BoolVar[] bools(V[] vars) { return Arrays.stream(vars).map(this::bool).toArray(BoolVar[]::new); } private BoolVar[][] bools(V[][] vars) { return Arrays.stream(vars).map(this::bools).toArray(BoolVar[][]::new); } private IntVar var(XNode tree) { //todo: use caching to avoid useless clones return buildAr(tree).intVar(); } private IntVar[] vars(XNode[] trees) { return Arrays.stream(trees).map(this::var).toArray(IntVar[]::new); } public String printSolution(boolean format) { StringBuilder buffer = new StringBuilder(); if (ovars == null) { ovars = new ArrayList<>(mvars.values()); ovars.sort(IntVar::compareTo); } buffer.append(String.format(S_INST_IN, model.getSolver().getSolutionCount())); if (model.getSolver().hasObjective()) { buffer.append("cost='").append(model.getObjective().asIntVar().getValue()).append("' "); } buffer.append(">"); buffer.append(format ? "\nv \t" : "").append(S_LIST_IN); // list variables ovars.forEach(ovar -> buffer.append(ovar.getName()).append(' ')); buffer.append(S_LIST_OUT).append(format ? "\nv \t" : "").append(S_VALU_IN); ovars.forEach(ovar -> { if (symbolics.contains(ovar)) { buffer.append(intToSymbol.get(ovar.getValue())).append(' '); } else { buffer.append(ovar.getValue()).append(' '); } }); buffer.append(S_VALU_OUT).append(format ? "\nv " : "").append(S_INST_OUT); return buffer.toString(); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////// EXTENSION CONSTRAINTS //////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public void buildCtrIntension(String id, XVariables.XVarSymbolic[] scope, XNodeParent syntaxTreeRoot) { ReExpression exp = buildRe(syntaxTreeRoot); if (VariableUtils.domainCardinality(vars(scope)) < Integer.MAX_VALUE / 1000) { exp.extension().post(); } else { exp.decompose().post(); } } @Override public void buildCtrExtension(String id, XVariables.XVarSymbolic x, String[] values, boolean positive, Set flags) { if (flags.contains(Types.TypeFlag.STARRED_TUPLES)) { // can you manage tables with symbol * ? throw new ParserException("Tables with symbol * are not supported"); } //noinspection StatementWithEmptyBody if (flags.contains(Types.TypeFlag.UNCLEAN_TUPLES)) { // do you have to clean the tuples, so as to remove those that cannot be built from variable domains ? } if (positive) { model.member(var(x), Arrays.stream(values).mapToInt(t -> symbolToInt.get(t)).toArray()).post(); } else { model.notMember(var(x), Arrays.stream(values).mapToInt(t -> symbolToInt.get(t)).toArray()).post(); } } @Override public void buildCtrExtension(String id, XVariables.XVarSymbolic[] list, String[][] tuples, boolean positive, Set flags) { //noinspection StatementWithEmptyBody if (flags.contains(Types.TypeFlag.UNCLEAN_TUPLES)) { // do you have to clean the tuples, so as to remove those that cannot be built from variable domains ? } Tuples mTuples = new Tuples(Arrays.stream(tuples) .map(t -> Arrays.stream(t).mapToInt(e -> symbolToInt.get(e)).toArray()) .toArray(int[][]::new), positive); if (flags.contains(Types.TypeFlag.STARRED_TUPLES)) { if (!positive) { // can you manage tables with symbol * ? throw new ParserException("Negative tables with symbol * are not supported"); } mTuples.setUniversalValue(STAR_INT); } model.table(vars(list), mTuples).post(); } @Override public void buildCtrExtension(String id, XVariables.XVarInteger[] list, int[][] tuples, boolean positive, Set flags) { //noinspection StatementWithEmptyBody if (flags.contains(Types.TypeFlag.UNCLEAN_TUPLES)) { // do you have to clean the tuples, so as to remove those that cannot be built from variable domains ? } Tuples mTuples = new Tuples(tuples, positive); if (flags.contains(Types.TypeFlag.STARRED_TUPLES)) { if (!positive) { // can you manage tables with symbol * ? throw new ParserException("Negative tables with symbol * are not supported"); } mTuples.setUniversalValue(STAR_INT); } model.table(vars(list), mTuples).post(); } @Override public void buildCtrExtension(String id, XVariables.XVarInteger x, int[] values, boolean positive, Set flags) { if (flags.contains(Types.TypeFlag.STARRED_TUPLES)) { // can you manage tables with symbol * ? throw new ParserException("Tables with symbol * are not supported"); } //noinspection StatementWithEmptyBody if (flags.contains(Types.TypeFlag.UNCLEAN_TUPLES)) { // do you have to clean the tuples, so as to remove those that cannot be built from variable domains ? } if (positive) { model.member(var(x), values).post(); } else { model.notMember(var(x), values).post(); } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////// PRIMITIVE CONSTRAINTS //////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private static ReExpression rel(ArExpression a, Types.TypeConditionOperatorRel op, int k) { ReExpression e = null; switch (op) { case LT: e = a.lt(k); break; case LE: e = a.le(k); break; case GE: e = a.ge(k); break; case GT: e = a.gt(k); break; case NE: e = a.ne(k); break; case EQ: e = a.eq(k); break; } return e; } private static ReExpression rel(ArExpression a, Types.TypeConditionOperatorRel op, IntVar k) { ReExpression e = null; switch (op) { case LT: e = a.lt(k); break; case LE: e = a.le(k); break; case GE: e = a.ge(k); break; case GT: e = a.gt(k); break; case NE: e = a.ne(k); break; case EQ: e = a.eq(k); break; } return e; } private static ArExpression ari(ArExpression a, Types.TypeArithmeticOperator opa, ArExpression b) { ArExpression e = null; switch (opa) { case ADD: e = a.add(b); break; case SUB: e = a.sub(b); break; case MUL: e = a.mul(b); break; case DIV: e = a.div(b); break; case MOD: e = a.mod(b); break; case DIST: e = a.dist(b); break; } return e; } @Override public void buildCtrPrimitive(String id, XVariables.XVarInteger x, Types.TypeConditionOperatorRel op, int k) { switch (op) { case LT: model.arithm(var(x), "<", k).post(); break; case LE: model.arithm(var(x), "<=", k).post(); break; case GE: model.arithm(var(x), ">=", k).post(); break; case GT: model.arithm(var(x), ">", k).post(); break; case NE: model.arithm(var(x), "!=", k).post(); break; case EQ: model.arithm(var(x), "=", k).post(); break; default: rel(var(x), op, k).post(); break; } } @Override public void buildCtrPrimitive(String id, XVariables.XVarInteger x, Types.TypeArithmeticOperator opa, XVariables.XVarInteger y, Types.TypeConditionOperatorRel op, int k) { //System.out.printf("%s %s %s %s %d\n", x.id, opa.toString(), y.id, op.toString(), k); if (opa.equals(Types.TypeArithmeticOperator.MOD) || opa.equals(Types.TypeArithmeticOperator.POW)) { rel(ari(var(x), opa, var(y)), op, k).post(); } else if (opa.equals(Types.TypeArithmeticOperator.DIST)) { switch (op) { case LT: model.distance(var(x), var(y), "<", k).post(); break; case LE: model.distance(var(x), var(y), "<", k + 1).post(); break; case GE: model.distance(var(x), var(y), ">", k - 1).post(); break; case GT: model.distance(var(x), var(y), ">", k).post(); break; case NE: model.distance(var(x), var(y), "!=", k).post(); break; case EQ: model.distance(var(x), var(y), "=", k).post(); break; } } else { String o = ""; switch (opa) { case ADD: o = "+"; break; case SUB: o = "-"; break; case MUL: o = "*"; break; case DIV: o = "/"; break; default: throw new IllegalStateException("Unexpected value: " + opa); } switch (op) { case LT: model.arithm(var(x), o, var(y), "<", k).post(); break; case LE: model.arithm(var(x), o, var(y), "<=", k).post(); break; case GE: model.arithm(var(x), o, var(y), ">=", k).post(); break; case GT: model.arithm(var(x), o, var(y), ">", k).post(); break; case NE: model.arithm(var(x), o, var(y), "!=", k).post(); break; case EQ: model.arithm(var(x), o, var(y), "=", k).post(); break; } } } @Override public void buildCtrPrimitive(String id, XVariables.XVarInteger x, Types.TypeArithmeticOperator opa, XVariables.XVarInteger y, Types.TypeConditionOperatorRel op, XVariables.XVarInteger z) { // TODO rel(ari(var(x), opa, var(y)), op, var(z)).post(); } @Override public void buildCtrLogic(String id, Types.TypeLogicalOperator op, XVariables.XVarInteger[] vars) { repost(id); } @Override public void buildCtrLogic(String id, XVariables.XVarInteger x, XVariables.XVarInteger y, Types.TypeConditionOperatorRel op, int k) { repost(id); } @Override public void buildCtrLogic(String id, XVariables.XVarInteger x, XVariables.XVarInteger y, Types.TypeConditionOperatorRel op, XVariables.XVarInteger z) { repost(id); } @Override public void buildCtrLogic(String id, XVariables.XVarInteger x, Types.TypeEqNeOperator op, Types.TypeLogicalOperator lop, XVariables.XVarInteger[] vars) { repost(id); } @Override public void buildCtrPrimitive(String id, XVariables.XVarInteger x, Types.TypeArithmeticOperator aop, int p, Types.TypeConditionOperatorRel op, int k) { rel(ari(var(x), aop, model.intVar(p)), op, model.intVar(k)).post(); } @Override public void buildCtrPrimitive(String id, XVariables.XVarInteger x, Types.TypeUnaryArithmeticOperator aop, XVariables.XVarInteger y) { switch (aop) { case ABS: model.absolute(var(x), var(y)).post(); break; case NEG: model.arithm(var(x), "+", var(y), "=", 0).post(); break; case SQR: model.square(var(x), var(y)).post(); break; case NOT: XCallbacks2.super.buildCtrPrimitive(id, x, aop, y); break; } } @Override public void buildCtrPrimitive(String id, XVariables.XVarInteger x, Types.TypeArithmeticOperator aop, int p, Types.TypeConditionOperatorRel op, XVariables.XVarInteger y) { rel(ari(var(x), aop, model.intVar(p)), op, var(y)).post(); } @Override public void buildCtrPrimitive(String id, XVariables.XVarInteger x, Types.TypeConditionOperatorSet op, int[] t) { switch (op) { case IN: model.member(var(x), t).post(); break; case NOTIN: model.notMember(var(x), t).post(); break; } } @Override public void buildCtrPrimitive(String id, XVariables.XVarInteger x, Types.TypeConditionOperatorSet op, int min, int max) { switch (op) { case IN: model.member(var(x), min, max).post(); break; case NOTIN: model.notMember(var(x), min, max).post(); break; } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////// GLOBAL CONSTRAINTS ////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public void buildCtrAtLeast(String id, XVariables.XVarInteger[] list, int value, int k) { model.count(value, vars(list), model.intVar(k, list.length)).post(); } @Override public void buildCtrAtMost(String id, XVariables.XVarInteger[] list, int value, int k) { model.count(value, vars(list), model.intVar(0, k)).post(); } @Override public void buildCtrAllDifferent(String id, XVariables.XVarInteger[] list) { model.allDifferent(vars(list)).post(); } @Override public void buildCtrAllDifferentMatrix(String id, XVariables.XVarInteger[][] matrix) { for (XVariables.XVarInteger[] list : matrix) { model.allDifferent(vars(list)).post(); } XVariables.XVarInteger[][] tmatrix = ArrayUtils.transpose(matrix); for (XVariables.XVarInteger[] list : tmatrix) { model.allDifferent(vars(list)).post(); } } @Override public void buildCtrAllDifferentExcept(String id, XVariables.XVarInteger[] list, int[] except) { if (except.length == 0) { model.allDifferent(vars(list)).post(); } else if (except.length == 1) { model.allDifferentUnderCondition(vars(list), x -> !x.contains(except[0]), true).post(); } else { IntIterableRangeSet set = new IntIterableRangeSet(except); model.allDifferentUnderCondition(vars(list), x -> !set.intersect(x), true).post(); } } @Override public void buildCtrAllDifferentMatrix(String id, XVariables.XVarInteger[][] matrix, int[] except) { this.buildCtrAllDifferentExcept(id, ArrayUtils.flatten(matrix), except); } @Override public void buildCtrAllDifferentList(String id, XVariables.XVarInteger[][] lists) { int d1 = lists.length; for (int i = 0; i < d1; i++) { for (int j = i + 1; j < d1; j++) { buildDistinctVectors(vars(lists[i]), vars(lists[j])); } } } @Override public void buildCtrAllDifferent(String id, XNode[] trees) { model.allDifferent(vars(trees)).post(); } @Override public void buildCtrAllDifferent(String id, XVariables.XVarSymbolic[] list) { model.allDifferent(vars(list)).post(); } private void buildDistinctVectors(IntVar[] t1, IntVar[] t2) { int k = t1.length; BoolVar[] diffs = model.boolVarArray(k); for (int i = 0; i < k; i++) { model.reifyXneY(t1[i], t2[i], diffs[i]); } model.addClausesBoolOrArrayEqualTrue(diffs); } @Override public void buildCtrAllEqual(String id, XVariables.XVarInteger[] list) { model.allEqual(vars(list)).post(); } @Override public void buildCtrAllEqual(String id, XNode[] trees) { model.allEqual(vars(trees)).post(); } @Override public void buildCtrNotAllEqual(String id, XVariables.XVarInteger[] list) { model.notAllEqual(vars(list)).post(); } @Override public void buildCtrCardinality(String id, XVariables.XVarInteger[] list, boolean closed, int[] values, XVariables.XVarInteger[] occurs) { model.globalCardinality(vars(list), values, vars(occurs), closed).post(); } @Override public void buildCtrCardinality(String id, XVariables.XVarInteger[] list, boolean closed, int[] values, int[] occurs) { model.globalCardinality( vars(list), values, Arrays.stream(occurs) .mapToObj(v -> model.intVar(v)) .toArray(IntVar[]::new), closed ).post(); } @Override public void buildCtrCardinality(String id, XVariables.XVarInteger[] list, boolean closed, int[] values, int[] occursMin, int[] occursMax) { model.globalCardinality( vars(list), values, IntStream.range(0, values.length) .mapToObj(i -> model.intVar(occursMin[i], occursMax[i])) .toArray(IntVar[]::new), closed ).post(); } @Override public void buildCtrCardinality(String id, XVariables.XVarInteger[] list, boolean closed, XVariables.XVarInteger[] values, XVariables.XVarInteger[] occurs) { model.globalCardinalityDec(vars(list), vars(values), vars(occurs), closed); } @Override public void buildCtrCardinality(String id, XVariables.XVarInteger[] list, boolean closed, XVariables.XVarInteger[] values, int[] occurs) { model.globalCardinalityDec(vars(list), vars(values), Arrays.stream(occurs).mapToObj(o -> model.intVar(o)) .toArray(IntVar[]::new), closed); } @Override public void buildCtrCardinality(String id, XVariables.XVarInteger[] list, boolean closed, XVariables.XVarInteger[] values, int[] occursMin, int[] occursMax) { model.globalCardinalityDec(vars(list), vars(values), IntStream.range(0, occursMin.length) .mapToObj(i -> model.intVar(occursMin[i], occursMax[i])).toArray(IntVar[]::new), closed); } @Override public void buildCtrClause(String id, XVariables.XVarInteger[] pos, XVariables.XVarInteger[] neg) { model.addClauses(bools(pos), bools(neg)); } @Override public void buildCtrCircuit(String id, XVariables.XVarInteger[] list, int startIndex) { model.subCircuit(vars(list), startIndex, model.intVar("circ_size", 0, list.length)).post(); } @Override public void buildCtrCircuit(String id, XVariables.XVarInteger[] list, int startIndex, int size) { model.subCircuit(vars(list), startIndex, model.intVar(size)).post(); } @Override public void buildCtrCircuit(String id, XVariables.XVarInteger[] list, int startIndex, XVariables.XVarInteger size) { model.subCircuit(vars(list), startIndex, var(size)).post(); } private void buildSum(IntVar[] res, int[] coeffs, Condition condition) { if (condition instanceof Condition.ConditionRel) { Condition.ConditionRel conditionRel = (Condition.ConditionRel) condition; IntVar resu = null; if (conditionRel instanceof Condition.ConditionVal) { resu = model.intVar((int) ((Condition.ConditionVal) condition).k); } else if (conditionRel instanceof Condition.ConditionVar) { resu = var(((XVariables.XVarInteger) ((Condition.ConditionVar) conditionRel).x)); } switch (conditionRel.operator) { case LT: model.scalar(res, coeffs, "<", resu).post(); break; case LE: model.scalar(res, coeffs, "<=", resu).post(); break; case GE: model.scalar(res, coeffs, ">=", resu).post(); break; case GT: model.scalar(res, coeffs, ">", resu).post(); break; case NE: model.scalar(res, coeffs, "!=", resu).post(); break; case EQ: model.scalar(res, coeffs, "=", resu).post(); break; } } else if (condition instanceof Condition.ConditionSet) { Condition.ConditionSet conditionSet = (Condition.ConditionSet) condition; IntVar resu = null; if (conditionSet instanceof Condition.ConditionIntset) { int[] values = ((Condition.ConditionIntset) condition).t; resu = model.intVar(values); } else if (conditionSet instanceof Condition.ConditionIntvl) { int lb = (int) ((Condition.ConditionIntvl) condition).min; int ub = (int) ((Condition.ConditionIntvl) condition).max; resu = model.intVar(lb, ub); } switch (conditionSet.operator) { case IN: { model.scalar(res, coeffs, "=", resu).post(); } break; case NOTIN: { int[] bounds = VariableUtils.boundsForScalar(res, coeffs); IntVar sum = model.intVar(bounds[0], bounds[1]); resu.ne(sum).post(); model.scalar(res, coeffs, "=", sum).post(); } break; } } } @Override public void buildCtrSum(String id, XNode[] trees, Condition condition) { int[] coeffs = new int[trees.length]; Arrays.fill(coeffs, 1); buildSum(vars(trees), coeffs, condition); } @Override public void buildCtrSum(String id, XNode[] trees, int[] coeffs, Condition condition) { buildSum(vars(trees), coeffs, condition); } @Override public void buildCtrSum(String id, XNode[] trees, XVariables.XVarInteger[] coeffs, Condition condition) { IntVar[] res = new IntVar[trees.length]; for (int i = 0; i < trees.length; i++) { IntVar var = var(trees[i]); int[] bounds = VariableUtils.boundsForMultiplication(var, var(coeffs[i])); res[i] = model.intVar(bounds[0], bounds[1]); model.times(var, var(coeffs[i]), res[i]).post(); } int[] _coeffs = new int[trees.length]; Arrays.fill(_coeffs, 1); buildSum(res, _coeffs, condition); } @Override public void buildCtrSum(String id, XVariables.XVarInteger[] list, Condition condition) { int[] coeffs = new int[list.length]; Arrays.fill(coeffs, 1); buildSum(vars(list), coeffs, condition); } @Override public void buildCtrSum(String id, XVariables.XVarInteger[] list, int[] coeffs, Condition condition) { buildSum(vars(list), coeffs, condition); } @Override public void buildCtrSum(String id, XVariables.XVarInteger[] list, XVariables.XVarInteger[] _coeffs, Condition condition) { IntVar[] res = new IntVar[list.length]; for (int i = 0; i < list.length; i++) { int[] bounds = VariableUtils.boundsForMultiplication(var(list[i]), var(_coeffs[i])); res[i] = model.intVar(bounds[0], bounds[1]); model.times(var(list[i]), var(_coeffs[i]), res[i]).post(); } int[] coeffs = new int[list.length]; Arrays.fill(coeffs, 1); buildSum(res, coeffs, condition); } @Override public void buildCtrCount(String id, XVariables.XVarInteger[] list, int[] values, Condition condition) { IntVar x = condToVar(condition, 0, list.length); model.among(x, vars(list), values).post(); } @Override public void buildCtrCount(String id, XNode[] trees, int[] values, Condition condition) { IntVar x = condToVar(condition, 0, trees.length); model.among(x, vars(trees), values).post(); } @Override public void buildCtrCount(String id, XVariables.XVarInteger[] list, XVariables.XVarInteger[] values, Condition condition) { IntVar x = condToVar(condition, 0, list.length); model.amongDec(x, vars(list), vars(values)); } private void buildCtrNValues(String id, IntVar[] vars, Condition condition) { if (condition instanceof Condition.ConditionRel) { Condition.ConditionRel conditionRel = (Condition.ConditionRel) condition; switch (conditionRel.operator) { case LT: case LE: model.atMostNValues(vars, condToVar(condition, 0, vars.length), false).post(); return; case GE: case GT: //TODO model.atLeastNValues(vars, condToVar(condition, 0, vars.length), false).post(); return; case NE: { IntVar count = model.intVar(0, vars.length); model.nValues(vars, count).post(); IntVar limit = condToVar(condition, 0, vars.length); model.arithm(count, "!=", limit).post(); } return; case EQ: model.nValues(vars, condToVar(condition, 0, vars.length)).post(); return; } } this.unimplementedCase(id); } @Override public void buildCtrNValues(String id, XVariables.XVarInteger[] list, Condition condition) { buildCtrNValues(id, vars(list), condition); } @Override public void buildCtrNValues(String id, XNode[] trees, Condition condition) { buildCtrNValues(id, vars(trees), condition); } @Override public void buildCtrNValuesExcept(String id, XVariables.XVarInteger[] list, int[] except, Condition condition) { XCallbacks2.super.buildCtrNValuesExcept(id, list, except, condition); } @Override public void buildCtrRegular(String id, XVariables.XVarInteger[] list, Transition[] transitions, String startState, String[] finalStates) { FiniteAutomaton auto = new FiniteAutomaton(); TObjectIntHashMap s2s = new TObjectIntHashMap<>(16, 1.5f, -1); for (Transition tr : transitions) { int f = s2s.get(tr.start); int v = ((Long) tr.value).intValue(); if (f == -1) { f = auto.addState(); s2s.put(tr.start, f); } int t = s2s.get(tr.end); if (t == -1) { t = auto.addState(); s2s.put(tr.end, t); } auto.addTransition(f, t, v); } auto.setInitialState(s2s.get(startState)); auto.setFinal(Arrays.stream(finalStates).mapToInt(s2s::get).toArray()); model.regular(vars(list), auto).post(); } @Override public void buildCtrMDD(String id, XVariables.XVarInteger[] list, Transition[] transitions) { HashMap> layers = new HashMap<>(); HashSet possibleRoots = new HashSet<>(), notRoots = new HashSet<>(); Set possibleWells = new HashSet<>(), notWells = new HashSet<>(); for (int t = 0; t < transitions.length; t++) { String src = transitions[t].start, tgt = transitions[t].end; notWells.add(src); notRoots.add(tgt); if (!notRoots.contains(src)) { possibleRoots.add(src); } if (!notWells.contains(tgt)) { possibleWells.add(tgt); } possibleRoots.remove(tgt); possibleWells.remove(src); List succs = layers.computeIfAbsent(src, k -> new ArrayList<>()); succs.add(transitions[t]); } String first = possibleRoots.toArray(new String[1])[0]; String last = possibleWells.toArray(new String[1])[0]; TObjectIntHashMap map = new TObjectIntHashMap<>(); map.put(first, 0); map.put(last, -1); possibleRoots.add(last); int n = 1; int[][] mtransitions = new int[transitions.length][3]; int k = 0; CircularQueue queue = new CircularQueue<>(layers.size()); queue.addLast(first); while (!queue.isEmpty()) { String src = queue.pollFirst(); List succs = layers.get(src); if (succs == null) continue; for (Transition t : succs) { String tgt = t.end; if (!possibleRoots.contains(tgt)) { queue.addLast(tgt); possibleRoots.add(tgt); map.put(tgt, n++); } mtransitions[k++] = new int[]{map.get(src), ((Long) t.value).intValue(), map.get(tgt)}; } } IntVar[] mVars = vars(list); MultivaluedDecisionDiagram mdd = new MultivaluedDecisionDiagram(mVars, mtransitions); model.mddc(mVars, mdd).post(); } @Override public void buildCtrExactly(String id, XVariables.XVarInteger[] list, int value, int k) { model.count(value, vars(list), model.intVar(k)).post(); } @Override public void buildCtrExactly(String id, XVariables.XVarInteger[] list, int value, XVariables.XVarInteger k) { model.count(value, vars(list), var(k)).post(); } @Override public void buildCtrAmong(String id, XVariables.XVarInteger[] list, int[] values, int k) { model.among(model.intVar(k), vars(list), values).post(); } @Override public void buildCtrAmong(String id, XVariables.XVarInteger[] list, int[] values, XVariables.XVarInteger k) { model.among(var(k), vars(list), values).post(); } @Override public void buildCtrMinimum(String id, XVariables.XVarInteger[] list, Condition condition) { buildMin(vars(list), condition); } @Override public void buildCtrMinimum(String id, XNode[] trees, Condition condition) { buildMin(vars(trees), condition); } private void buildMin(IntVar[] vars, Condition condition) { int min = Arrays.stream(vars).min(Comparator.comparingInt(IntVar::getLB)).get().getLB(); int max = Arrays.stream(vars).max(Comparator.comparingInt(IntVar::getUB)).get().getUB(); IntVar x = condToVar(condition, min, max); model.min(x, vars).post(); } @Override public void buildCtrMinimumArg(String id, XVariables.XVarInteger[] list, Types.TypeRank rank, Condition condition) { buildArgmin(vars(list), rank, condition); } @Override public void buildCtrMinimumArg(String id, XNode[] trees, Types.TypeRank rank, Condition condition) { buildArgmin(vars(trees), rank, condition); } private void buildArgmin(IntVar[] vars, Types.TypeRank rank, Condition condition) { IntVar max = condToVar(condition, 0, vars.length); if (rank.equals(Types.TypeRank.LAST)) { ArrayUtils.reverse(vars); IntVar max2 = model.intView(-1, max, vars.length); model.argmin(max2, 0, vars).post(); } else { model.argmin(max, 0, vars).post(); } } @Override public void buildCtrElement(String id, XVariables.XVarInteger[] list, Condition condition) { IntVar[] vars = vars(list); int min = Arrays.stream(vars).min(Comparator.comparingInt(IntVar::getLB)).get().getLB(); int max = Arrays.stream(vars).max(Comparator.comparingInt(IntVar::getUB)).get().getUB(); IntVar x = condToVar(condition, min, max); model.element(x, vars(list), model.intVar(0, list.length), 0).post(); } @Override public void buildCtrElement(String id, XVariables.XVarInteger[] list, int startIndex, XVariables.XVarInteger index, Types.TypeRank rank, Condition condition) { if (rank == Types.TypeRank.ANY) { IntVar[] vars = vars(list); int min = Arrays.stream(vars).min(Comparator.comparingInt(IntVar::getLB)).get().getLB(); int max = Arrays.stream(vars).max(Comparator.comparingInt(IntVar::getUB)).get().getUB(); IntVar x = condToVar(condition, min, max); model.element(x, vars(list), var(index), startIndex).post(); } else XCallbacks2.super.buildCtrElement(id, list, startIndex, index, rank, condition); } @Override public void buildCtrElement(String id, int[] list, int startIndex, XVariables.XVarInteger index, Types.TypeRank rank, Condition condition) { if (rank == Types.TypeRank.ANY) { int min = Arrays.stream(list).min().getAsInt(); int max = Arrays.stream(list).max().getAsInt(); IntVar x = condToVar(condition, min, max); model.element(x, list, var(index), startIndex).post(); } else XCallbacks2.super.buildCtrElement(id, list, startIndex, index, rank, condition); } @Override public void buildCtrElement(String id, int[][] matrix, int startRowIndex, XVariables.XVarInteger rowIndex, int startColIndex, XVariables.XVarInteger colIndex, Condition condition) { int min = Arrays.stream(matrix).mapToInt(r -> Arrays.stream(r).min().getAsInt()).min().getAsInt(); int max = Arrays.stream(matrix).mapToInt(r -> Arrays.stream(r).max().getAsInt()).max().getAsInt(); IntVar x = condToVar(condition, min, max); model.element(x, matrix, var(rowIndex), startColIndex, var(colIndex), startColIndex); } @Override public void buildCtrElement(String id, XVariables.XVarInteger[][] matrix, int startRowIndex, XVariables.XVarInteger rowIndex, int startColIndex, XVariables.XVarInteger colIndex, Condition condition) { IntVar[][] vars = vars(matrix); int min = Arrays.stream(vars).mapToInt(r -> Arrays.stream(r).mapToInt(IntVar::getLB) .min().getAsInt()).min().getAsInt(); int max = Arrays.stream(vars).mapToInt(r -> Arrays.stream(r).mapToInt(IntVar::getLB) .max().getAsInt()).max().getAsInt(); IntVar x = condToVar(condition, min, max); model.element(x, vars(matrix), var(rowIndex), startColIndex, var(colIndex), startColIndex); } @Override public void buildCtrMaximum(String id, XVariables.XVarInteger[] list, Condition condition) { buildMax(vars(list), condition); } @Override public void buildCtrMaximum(String id, XNode[] trees, Condition condition) { buildMax(vars(trees), condition); } private void buildMax(IntVar[] vars, Condition condition) { int min = Arrays.stream(vars).min(Comparator.comparingInt(IntVar::getLB)).get().getLB(); int max = Arrays.stream(vars).max(Comparator.comparingInt(IntVar::getUB)).get().getUB(); IntVar x = condToVar(condition, min, max); model.max(x, vars).post(); } @Override public void buildCtrMaximumArg(String id, XVariables.XVarInteger[] list, Types.TypeRank rank, Condition condition) { buildArgmax(vars(list), rank, condition); } @Override public void buildCtrMaximumArg(String id, XNode[] trees, Types.TypeRank rank, Condition condition) { buildArgmax(vars(trees), rank, condition); } private void buildArgmax(IntVar[] vars, Types.TypeRank rank, Condition condition) { IntVar max = condToVar(condition, 0, vars.length); if (rank.equals(Types.TypeRank.LAST)) { ArrayUtils.reverse(vars); IntVar max2 = model.intView(-1, max, vars.length); model.argmax(max2, 0, vars).post(); } else { model.argmax(max, 0, vars).post(); } } @Override public void buildCtrLexMatrix(String id, XVariables.XVarInteger[][] matrix, Types.TypeOperatorRel operator) { switch (operator) { case LT: { model.lexChainLess(vars(matrix)).post(); XVariables.XVarInteger[][] tmatrix = ArrayUtils.transpose(matrix); model.lexChainLess(vars(tmatrix)).post(); } break; case LE: { model.lexChainLessEq(vars(matrix)).post(); XVariables.XVarInteger[][] tmatrix = ArrayUtils.transpose(matrix); model.lexChainLessEq(vars(tmatrix)).post(); } break; case GT: { XVariables.XVarInteger[][] rmatrix = matrix.clone(); ArrayUtils.reverse(rmatrix); model.lexChainLess(vars(rmatrix)).post(); model.lexChainLess(vars(ArrayUtils.transpose(rmatrix))).post(); } break; case GE: { XVariables.XVarInteger[][] rmatrix = matrix.clone(); ArrayUtils.reverse(rmatrix); model.lexChainLessEq(vars(rmatrix)).post(); model.lexChainLessEq(vars(ArrayUtils.transpose(rmatrix))).post(); } break; } } @Override public void buildCtrPrecedence(String id, XVariables.XVarInteger[] list, int[] values, boolean covered) { model.intValuePrecedeChain(vars(list), values).post(); if (covered) { buildCtrAtLeast(id, list, values[values.length - 1], 1); } } @Override public void buildCtrPrecedence(String id, XVariables.XVarInteger[] list) { IntVar[] vars = vars(list); model.intValuePrecedeChain(vars, Arrays.stream(vars) .flatMapToInt(IntVar::stream) .boxed() .collect(Collectors.toSet()) .stream().mapToInt(i -> i) .sorted().toArray()) .post(); } @Override public void buildCtrOrdered(String id, XVariables.XVarInteger[] list, Types.TypeOperatorRel operator) { IntVar[] vars = vars(list); IntVar[][] vectors = new IntVar[vars.length][1]; for (int i = 0; i < vars.length; i++) { vectors[i] = new IntVar[]{vars[i]}; } lexCtr(vectors, operator); } @Override public void buildCtrOrdered(String id, XVariables.XVarInteger[] list, int[] lengths, Types.TypeOperatorRel operator) { IntVar[] vars = vars(list); IntVar[][] vectors = new IntVar[vars.length * 2 - 1][1]; int k = 0; for (int i = 0; i < vars.length - 1; i++) { vectors[k++] = new IntVar[]{vars[i]}; vectors[k++] = new IntVar[]{vars[i].add(lengths[i]).intVar()}; } vectors[k] = new IntVar[]{vars[vars.length - 1]}; lexCtr(vectors, operator); } @Override public void buildCtrOrdered(String id, XVariables.XVarInteger[] list, XVariables.XVarInteger[] lengths, Types.TypeOperatorRel operator) { IntVar[] vars = vars(list); IntVar[][] vectors = new IntVar[vars.length * 2 - 1][1]; int k = 0; for (int i = 0; i < vars.length - 1; i++) { vectors[k++] = new IntVar[]{vars[i]}; vectors[k++] = new IntVar[]{vars[i].add(var(lengths[i])).intVar()}; } vectors[k] = new IntVar[]{vars[vars.length - 1]}; lexCtr(vectors, operator); } @Override public void buildCtrLex(String id, XVariables.XVarInteger[] list, int[] limit, Types.TypeOperatorRel operator) { IntVar[][] scopes = new IntVar[2][]; scopes[0] = vars(list); scopes[1] = Arrays.stream(limit).mapToObj(l -> model.intVar(l)).toArray(IntVar[]::new); lexCtr(scopes, operator); } @Override public void buildCtrLex(String id, XVariables.XVarInteger[][] lists, Types.TypeOperatorRel operator) { lexCtr(vars(lists), operator); } private void lexCtr(IntVar[][] vectors, Types.TypeOperatorRel operator) { switch (operator) { case LT: model.lexChainLess(vectors).post(); break; case LE: model.lexChainLessEq(vectors).post(); break; case GE: { ArrayUtils.reverse(vectors); model.lexChainLessEq(vectors).post(); } break; case GT: { ArrayUtils.reverse(vectors); model.lexChainLess(vectors).post(); } break; } } @Override public void buildCtrChannel(String id, XVariables.XVarInteger[] list, int startIndex) { model.inverseChanneling(vars(list), vars(list), startIndex, startIndex).post(); } @Override public void buildCtrChannel(String id, XVariables.XVarInteger[] list1, int startIndex1, XVariables.XVarInteger[] list2, int startIndex2) { if (list1.length == list2.length) { model.inverseChanneling(vars(list1), vars(list2), startIndex1, startIndex2).post(); } else if (list1.length < list2.length) { IntVar[] x = vars(list1); IntVar[] y = vars(list2); for (int xi = 0; xi < x.length; xi++) { model.element(model.intVar(xi + startIndex1), y, x[xi], startIndex2).post(); } } else { XCallbacks2.super.buildCtrChannel(id, list1, startIndex1, list2, startIndex2); } } @Override public void buildCtrChannel(String id, XVariables.XVarInteger[] list, int startIndex, XVariables.XVarInteger value) { model.boolsIntChanneling(bools(list), var(value), startIndex).post(); } @Override public void buildCtrNoOverlap(String id, XVariables.XVarInteger[] origins, int[] lengths, boolean zeroIgnored) { // disjunctive model.cumulative( IntStream.range(0, origins.length) .mapToObj(i -> model.taskVar(var(origins[i]), lengths[i])) .toArray(Task[]::new), model.intVarArray(origins.length, 1, 1), model.intVar(1) ).post(); } @Override public void buildCtrNoOverlap(String id, XVariables.XVarInteger[] origins, XVariables.XVarInteger[] lengths, boolean zeroIgnored) { // disjunctive model.cumulative( IntStream.range(0, origins.length) .mapToObj(i -> model.taskVar(var(origins[i]), var(lengths[i]))) .toArray(Task[]::new), model.intVarArray(origins.length, 1, 1), model.intVar(1) ).post(); } @Override public void buildCtrNoOverlap(String id, XVariables.XVarInteger[][] origins, int[][] lengths, boolean zeroIgnored) { if (origins[0].length == 2) { IntVar[] X = Arrays.stream(origins).map(o -> var(o[0])).toArray(IntVar[]::new); IntVar[] Y = Arrays.stream(origins).map(o -> var(o[1])).toArray(IntVar[]::new); IntVar[] W = Arrays.stream(lengths).map(l -> model.intVar(l[0])).toArray(IntVar[]::new); IntVar[] H = Arrays.stream(lengths).map(l -> model.intVar(l[1])).toArray(IntVar[]::new); model.diffN(X, Y, W, H, true).post(); } else { XCallbacks2.super.buildCtrNoOverlap(id, origins, lengths, zeroIgnored); } } @Override public void buildCtrNoOverlap(String id, XVariables.XVarInteger[][] origins, XVariables.XVarInteger[][] lengths, boolean zeroIgnored) { if (origins[0].length == 2 && zeroIgnored) { IntVar[] X = Arrays.stream(origins).map(o -> var(o[0])).toArray(IntVar[]::new); IntVar[] Y = Arrays.stream(origins).map(o -> var(o[1])).toArray(IntVar[]::new); IntVar[] W = Arrays.stream(lengths).map(l -> var(l[0])).toArray(IntVar[]::new); IntVar[] H = Arrays.stream(lengths).map(l -> var(l[1])).toArray(IntVar[]::new); model.diffN(X, Y, W, H, true).post(); }else{ XCallbacks2.super.buildCtrNoOverlap(id, origins, lengths, zeroIgnored); } } @Override public void buildCtrNoOverlap(String id, XVariables.XVarInteger[] xs, XVariables.XVarInteger[] ys, XVariables.XVarInteger[] lx, int[] ly, boolean zeroIgnored) { if (zeroIgnored) { model.diffN( vars(xs), vars(ys), vars(lx), IntStream.of(ly).mapToObj(l -> model.intVar(l)).toArray(IntVar[]::new), true ).post(); }else{ XCallbacks2.super.buildCtrNoOverlap(id, xs, ys, lx, ly, zeroIgnored); } } @Override public void buildCtrCumulative(String id, XVariables.XVarInteger[] origins, int[] lengths, int[] heights, Condition condition) { if (condition instanceof Condition.ConditionRel) { int sumLe = Arrays.stream(heights).sum(); model.cumulative( IntStream.range(0, origins.length) .mapToObj(i -> model.taskVar(var(origins[i]), lengths[i])) .toArray(Task[]::new), IntStream.range(0, origins.length) .mapToObj(i -> model.intVar(heights[i])) .toArray(IntVar[]::new), condToVar(condition, 0, sumLe) ).post(); return; } XCallbacks2.super.buildCtrCumulative(id, origins, lengths, heights, condition); } @Override public void buildCtrCumulative(String id, XVariables.XVarInteger[] origins, int[] lengths, XVariables.XVarInteger[] heights, Condition condition) { if (condition instanceof Condition.ConditionRel) { int sumLe = (int) Arrays.stream(heights).mapToLong(XVariables.XVarInteger::lastValue).sum(); model.cumulative( IntStream.range(0, origins.length) .mapToObj(i -> model.taskVar(var(origins[i]), lengths[i])) .toArray(Task[]::new), vars(heights), condToVar(condition, 0, sumLe) ).post(); return; } XCallbacks2.super.buildCtrCumulative(id, origins, lengths, heights, condition); } @Override public void buildCtrCumulative(String id, XVariables.XVarInteger[] origins, XVariables.XVarInteger[] lengths, int[] heights, Condition condition) { if (condition instanceof Condition.ConditionRel) { int sumLe = Arrays.stream(heights).sum(); model.cumulative( IntStream.range(0, origins.length) .mapToObj(i -> model.taskVar(var(origins[i]), var(lengths[i]))) .toArray(Task[]::new), IntStream.range(0, origins.length) .mapToObj(i -> model.intVar(heights[i])) .toArray(IntVar[]::new), condToVar(condition, 0, sumLe) ).post(); return; } XCallbacks2.super.buildCtrCumulative(id, origins, lengths, heights, condition); } @Override public void buildCtrCumulative(String id, XVariables.XVarInteger[] origins, XVariables.XVarInteger[] lengths, XVariables.XVarInteger[] heights, Condition condition) { if (condition instanceof Condition.ConditionRel) { int sumLe = (int) Arrays.stream(heights).mapToLong(XVariables.XVarInteger::lastValue).sum(); model.cumulative( IntStream.range(0, origins.length) .mapToObj(i -> model.taskVar(var(origins[i]), var(lengths[i]))) .toArray(Task[]::new), vars(heights), condToVar(condition, 0, sumLe) ).post(); return; } XCallbacks2.super.buildCtrCumulative(id, origins, lengths, heights, condition); } @Override public void buildCtrCumulative(String id, XVariables.XVarInteger[] origins, int[] lengths, XVariables.XVarInteger[] ends, int[] heights, Condition condition) { if (condition instanceof Condition.ConditionRel) { int sumLe = IntStream.of(heights).sum(); model.cumulative( IntStream.range(0, origins.length) .mapToObj(i -> model.taskVar(var(origins[i]), lengths[i])) .toArray(Task[]::new), IntStream.of(heights).mapToObj(i -> model.intVar(i)).toArray(IntVar[]::new), condToVar(condition, 0, sumLe) ).post(); return; } XCallbacks2.super.buildCtrCumulative(id, origins, lengths, heights, condition); } @Override public void buildCtrCumulative(String id, XVariables.XVarInteger[] origins, int[] lengths, XVariables.XVarInteger[] ends, XVariables.XVarInteger[] heights, Condition condition) { if (condition instanceof Condition.ConditionRel) { int sumLe = (int) Arrays.stream(heights).mapToLong(XVariables.XVarInteger::lastValue).sum(); model.cumulative( IntStream.range(0, origins.length) .mapToObj(i -> model.taskVar(var(origins[i]), lengths[i])) .toArray(Task[]::new), vars(heights), condToVar(condition, 0, sumLe) ).post(); return; } XCallbacks2.super.buildCtrCumulative(id, origins, lengths, heights, condition); } @Override public void buildCtrCumulative(String id, XVariables.XVarInteger[] origins, XVariables.XVarInteger[] lengths, XVariables.XVarInteger[] ends, int[] heights, Condition condition) { if (condition instanceof Condition.ConditionRel) { int sumLe = IntStream.of(heights).sum(); model.cumulative( IntStream.range(0, origins.length) .mapToObj(i -> model.taskVar(var(origins[i]), var(lengths[i]))) .toArray(Task[]::new), IntStream.of(heights).mapToObj(i -> model.intVar(i)).toArray(IntVar[]::new), condToVar(condition, 0, sumLe) ).post(); return; } XCallbacks2.super.buildCtrCumulative(id, origins, lengths, heights, condition); } @Override public void buildCtrCumulative(String id, XVariables.XVarInteger[] origins, XVariables.XVarInteger[] lengths, XVariables.XVarInteger[] ends, XVariables.XVarInteger[] heights, Condition condition) { if (condition instanceof Condition.ConditionRel) { int sumLe = (int) Arrays.stream(heights).mapToLong(XVariables.XVarInteger::lastValue).sum(); model.cumulative( IntStream.range(0, origins.length) .mapToObj(i -> model.taskVar(var(origins[i]), var(lengths[i]))) .toArray(Task[]::new), vars(heights), condToVar(condition, 0, sumLe) ).post(); return; } XCallbacks2.super.buildCtrCumulative(id, origins, lengths, heights, condition); } @Override public void buildCtrBinPacking(String id, XVariables.XVarInteger[] list, int[] sizes, Condition condition) { int sumSiz = Arrays.stream(sizes).sum(); IntVar[] cds = new IntVar[list.length]; for (int i = 0; i < cds.length; i++) { cds[i] = condToVar(condition, 0, sumSiz); } model.binPacking(vars(list), sizes, cds, 0).post(); } @Override public void buildCtrBinPacking(String id, XVariables.XVarInteger[] list, int[] sizes, int[] capacities, boolean loads) { model.binPacking(vars(list), sizes, IntStream.of(capacities).mapToObj(c -> model.intVar(loads ? c : 0, c)).toArray(IntVar[]::new), 0).post(); } @Override public void buildCtrBinPacking(String id, XVariables.XVarInteger[] list, int[] sizes, XVariables.XVarInteger[] capacities, boolean loads) { IntVar[] binLoad; if (loads) { binLoad = vars(capacities); } else { binLoad = Arrays.stream(capacities).map(c -> model.intVar(0, (int) c.lastValue())).toArray(IntVar[]::new); for (int i = 0; i < binLoad.length; i++) { binLoad[i].le(var(capacities[i])).post(); } } model.binPacking(vars(list), sizes, binLoad, 0).post(); } @Override public void buildCtrBinPacking(String id, XVariables.XVarInteger[] list, int[] sizes, Condition[] conditions, int startIndex) { int sumSiz = Arrays.stream(sizes).sum(); IntVar[] cds = new IntVar[conditions.length]; for (int i = 0; i < cds.length; i++) { cds[i] = condToVar(conditions[i], 0, sumSiz); } model.binPacking(vars(list), sizes, cds, startIndex).post(); } @Override public void buildCtrKnapsack(String id, XVariables.XVarInteger[] list, int[] weights, Condition wcondition, int[] profits, Condition pcondition) { assert IntStream.of(weights).min().orElse(0) > -1; assert IntStream.of(profits).min().orElse(0) > -1; model.knapsack(vars(list), condToVar(wcondition, 0, Arrays.stream(weights).sum()), condToVar(pcondition, 0, Arrays.stream(profits).sum()), weights, profits).post(); } @Override public void buildCtrFlow(String id, XVariables.XVarInteger[] list, int[] balance, int[][] arcs) { int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE, offset = Integer.MAX_VALUE; for (int i = 0; i < list.length; i++) { assert list[i].firstValue() >= 0; offset = Math.min(Math.min(arcs[i][0],arcs[i][1]), offset); min = Math.min(MathUtils.safeCast(list[i].firstValue()), min); max = Math.max(MathUtils.safeCast(list[i].lastValue()), max); } model.costFlow( ArrayUtils.getColumn(arcs, 0), ArrayUtils.getColumn(arcs, 1), balance, IntStream.range(0, list.length).map(i -> 1).toArray(), vars(list), model.intVar(min * list.length, max * list.length), offset); } @Override public void buildCtrFlow(String id, XVariables.XVarInteger[] list, int[] balance, int[][] arcs, int[] weights, Condition condition) { int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE, offset = Integer.MAX_VALUE; for (int i = 0; i < list.length; i++) { assert weights[i] >= 0; assert list[i].firstValue() >= 0; offset = Math.min(Math.min(arcs[i][0], arcs[i][1]), offset); min = Math.min(MathUtils.safeCast(list[i].firstValue() * weights[i]), min); max = Math.max(MathUtils.safeCast(list[i].lastValue() * weights[i]), max); } model.costFlow( ArrayUtils.getColumn(arcs, 0), ArrayUtils.getColumn(arcs, 1), balance, weights, vars(list), condToVar(condition, min * list.length, max * list.length), offset); } @Override public void buildCtrInstantiation(String id, XVariables.XVarInteger[] list, int[] values) { Tuples tuples = new Tuples(true); tuples.add(values); model.table(vars(list), tuples).post(); } /** * This method ignore the operator * * @param condition * @return */ private IntVar condToVar(Condition condition, int min, int max) { if (condition instanceof Condition.ConditionVal) { return dealWithConditionVal((Condition.ConditionVal) condition, min, max); } else if (condition instanceof Condition.ConditionVar) { return dealWithConditionVar((Condition.ConditionVar) condition, min, max); } else if (condition instanceof Condition.ConditionIntvl) { return dealWithConditionIntvl((Condition.ConditionIntvl) condition, min, max); } else if (condition instanceof Condition.ConditionIntset) { return dealWithConditionIntset((Condition.ConditionIntset) condition, min, max); } else { throw new ParserException("unknow result for condV" + condition); } } private IntVar dealWithConditionVal(Condition.ConditionVal condition, int min, int max) { int k = (int) condition.k; switch (condition.operator) { case LT: return model.intVar(Math.min(min, k - 1), Math.min(max, k - 1)); case LE: return model.intVar(Math.min(min, k), Math.min(max, k)); case GT: return model.intVar(Math.max(min, k + 1), Math.max(max, k + 1)); case GE: return model.intVar(Math.max(min, k), Math.max(max, k)); case NE: IntVar r = model.intVar(min, max); r.ne(k).post(); return r; case EQ: return model.intVar(k); } throw new ParserException("dealWithConditionVal " + condition); } private IntVar dealWithConditionVar(Condition.ConditionVar condition, int min, int max) { IntVar k = var((XVariables.XVarInteger) condition.x); IntVar res = model.intVar(min, max); switch (condition.operator) { case LT: res.lt(k).post(); break; case LE: res.le(k).post(); break; case GE: res.ge(k).post(); break; case GT: res.gt(k).post(); break; case NE: res.ne(k).post(); break; case EQ: res.eq(k).post(); break; } return res; } private IntVar dealWithConditionIntset(Condition.ConditionIntset condition, int min, int max) { int[] values = condition.t; switch (condition.operator) { case IN: return model.intVar(values); case NOTIN: IntVar r = model.intVar(min, max); model.notMember(r, values).post(); return r; } throw new ParserException("dealWithConditionVal " + condition); } private IntVar dealWithConditionIntvl(Condition.ConditionIntvl condition, int min, int max) { int mi = (int) condition.min; int ma = (int) condition.max; switch (condition.operator) { case IN: return model.intVar(Math.min(mi, min), Math.max(ma, max)); case NOTIN: IntVar r = model.intVar(min, max); model.notMember(r, min, max).post(); return r; } throw new ParserException("dealWithConditionVal " + condition); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////// GROUP /////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public Implem implem() { return implem; } @Override public void loadGroup(XConstraints.XGroup g) { beginGroup(g); if (g.template instanceof XConstraints.XCtr) loadCtrs((XConstraints.XCtr) g.template, g.argss, g); else if (g.template instanceof XConstraints.XLogic && ((XConstraints.XLogic) g.template).getType() == Types.TypeCtr.not) { XConstraints.CEntryReifiable child = ((XConstraints.XLogic) g.template).components[0]; // http://sofdem.github.io/gccat/aux/pdf/not_all_equal.pdf Stream.of(g.argss).forEach(o -> model.notAllEqual(vars((XVariables.XVarInteger[]) o)).post()); } else unimplementedCase(g); endGroup(g); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////// ANNOTATIONS ///////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public void buildAnnotationDecision(XVariables.XVarInteger[] list) { model.addHook("decisions", vars(list)); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////// OBJECTIVE /////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private IntVar optSum(IntVar[] vars) { int[] bounds = VariableUtils.boundsForAddition(vars); IntVar res = model.intVar("SUM", bounds[0], bounds[1], true); model.sum(vars, "=", res).post(); return res; } private IntVar optScalar(IntVar[] vars, int[] coeffs) { int[] bounds = VariableUtils.boundsForScalar(vars, coeffs); //bounds[0] = 184396; IntVar res = model.intVar("SCALAR", bounds[0], bounds[1], true); model.scalar(vars, coeffs, "=", res).post(); return res; } private IntVar optMin(IntVar[] vars) { int[] bounds = VariableUtils.boundsForMinimum(vars); IntVar res = model.intVar("MIN", bounds[0], bounds[1]); model.min(res, vars).post(); return res; } private IntVar optMax(IntVar[] vars) { int[] bounds = VariableUtils.boundsForMaximum(vars); IntVar res = model.intVar("MAX", bounds[0], bounds[1]); model.max(res, vars).post(); return res; } private IntVar optNValues(IntVar[] vars) { IntVar res = model.intVar("NVALUES", 0, vars.length); model.nValues(vars, res).post(); return res; } private void buildObjective(boolean maximize, Types.TypeObjective type, IntVar[] vars) { switch (type) { case SUM: model.setObjective(maximize, optSum(vars)); break; case MINIMUM: model.setObjective(maximize, optMin(vars)); break; case MAXIMUM: model.setObjective(maximize, optMax(vars)); break; case NVALUES: model.setObjective(maximize, optNValues(vars)); break; case EXPRESSION: case PRODUCT: case LEX: throw new UnsupportedOperationException("Unknown objective"); } } private void buildObjective(boolean maximize, Types.TypeObjective type, IntVar[] vars, int[] coeffs) { // Only SUM supports coeffs switch (type) { case SUM: model.setObjective(maximize, optScalar(vars, coeffs)); break; case MINIMUM: case MAXIMUM: case NVALUES: case EXPRESSION: case PRODUCT: case LEX: throw new UnsupportedOperationException("Unknown objective"); } } @Override public void buildObjToMinimize(String id, XVariables.XVarInteger x) { model.setObjective(false, var(x)); } @Override public void buildObjToMaximize(String id, XVariables.XVarInteger x) { model.setObjective(true, var(x)); } @Override public void buildObjToMaximize(String id, XNodeParent tree) { model.setObjective(true, buildAr(tree).intVar()); } @Override public void buildObjToMinimize(String id, XNodeParent tree) { model.setObjective(false, buildAr(tree).intVar()); } @Override public void buildObjToMinimize(String id, Types.TypeObjective type, XVariables.XVarInteger[] list) { buildObjective(false, type, vars(list)); } @Override public void buildObjToMaximize(String id, Types.TypeObjective type, XVariables.XVarInteger[] list) { buildObjective(true, type, vars(list)); } @Override public void buildObjToMinimize(String id, Types.TypeObjective type, XNode[] trees) { buildObjective(false, type, vars(trees)); } @Override public void buildObjToMaximize(String id, Types.TypeObjective type, XNode[] trees) { buildObjective(true, type, vars(trees)); } @Override public void buildObjToMinimize(String id, Types.TypeObjective type, XVariables.XVarInteger[] list, int[] coeffs) { buildObjective(false, type, vars(list), coeffs); } @Override public void buildObjToMaximize(String id, Types.TypeObjective type, XVariables.XVarInteger[] list, int[] coeffs) { buildObjective(true, type, vars(list), coeffs); } @Override public void buildObjToMinimize(String id, Types.TypeObjective type, XNode[] trees, int[] coeffs) { buildObjective(false, type, vars(trees), coeffs); } @Override public void buildObjToMaximize(String id, Types.TypeObjective type, XNode[] trees, int[] coeffs) { buildObjective(true, type, vars(trees), coeffs); } @Override public void buildAnnotationValHeuristicStatic(XVariables.XVarInteger[] list, int[] order) { Solver solver = model.getSolver(); IntValueSelector valueSelector = var -> { for (int j : order) { if (var.contains(j)) { return j; } } return var.getLB(); }; solver.setSearch( solver.getSearch(), Search.intVarSearch(new InputOrder<>(model), valueSelector, vars(list))); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy