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

org.chocosolver.solver.constraints.IIntConstraintFactory Maven / Gradle / Ivy

There is a newer version: 4.10.16
Show newest version
/**
 * Copyright (c) 2016, Ecole des Mines de Nantes
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by the .
 * 4. Neither the name of the  nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY  ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL  BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.chocosolver.solver.constraints;

import gnu.trove.list.array.TIntArrayList;
import gnu.trove.set.TIntSet;
import gnu.trove.set.hash.TIntHashSet;
import org.chocosolver.solver.ISelf;
import org.chocosolver.solver.Model;
import org.chocosolver.solver.constraints.binary.*;
import org.chocosolver.solver.constraints.binary.element.ElementFactory;
import org.chocosolver.solver.constraints.extension.Tuples;
import org.chocosolver.solver.constraints.extension.TuplesFactory;
import org.chocosolver.solver.constraints.extension.binary.*;
import org.chocosolver.solver.constraints.extension.nary.*;
import org.chocosolver.solver.constraints.nary.PropDiffN;
import org.chocosolver.solver.constraints.nary.PropIntValuePrecedeChain;
import org.chocosolver.solver.constraints.nary.PropKLoops;
import org.chocosolver.solver.constraints.nary.PropKnapsack;
import org.chocosolver.solver.constraints.nary.alldifferent.AllDifferent;
import org.chocosolver.solver.constraints.nary.alldifferent.conditions.Condition;
import org.chocosolver.solver.constraints.nary.alldifferent.conditions.PropCondAllDiffInst;
import org.chocosolver.solver.constraints.nary.alldifferent.conditions.PropCondAllDiff_AC;
import org.chocosolver.solver.constraints.nary.among.PropAmongGAC;
import org.chocosolver.solver.constraints.nary.automata.CostRegular;
import org.chocosolver.solver.constraints.nary.automata.FA.IAutomaton;
import org.chocosolver.solver.constraints.nary.automata.FA.ICostAutomaton;
import org.chocosolver.solver.constraints.nary.automata.PropMultiCostRegular;
import org.chocosolver.solver.constraints.nary.automata.PropRegular;
import org.chocosolver.solver.constraints.nary.binPacking.PropItemToLoad;
import org.chocosolver.solver.constraints.nary.binPacking.PropLoadToItem;
import org.chocosolver.solver.constraints.nary.channeling.*;
import org.chocosolver.solver.constraints.nary.circuit.*;
import org.chocosolver.solver.constraints.nary.count.PropCountVar;
import org.chocosolver.solver.constraints.nary.count.PropCount_AC;
import org.chocosolver.solver.constraints.nary.cumulative.Cumulative;
import org.chocosolver.solver.constraints.nary.element.PropElementV_fast;
import org.chocosolver.solver.constraints.nary.globalcardinality.GlobalCardinality;
import org.chocosolver.solver.constraints.nary.lex.PropLex;
import org.chocosolver.solver.constraints.nary.lex.PropLexChain;
import org.chocosolver.solver.constraints.nary.min_max.PropBoolMax;
import org.chocosolver.solver.constraints.nary.min_max.PropBoolMin;
import org.chocosolver.solver.constraints.nary.min_max.PropMax;
import org.chocosolver.solver.constraints.nary.min_max.PropMin;
import org.chocosolver.solver.constraints.nary.nValue.PropAMNV;
import org.chocosolver.solver.constraints.nary.nValue.PropAtLeastNValues;
import org.chocosolver.solver.constraints.nary.nValue.PropAtLeastNValues_AC;
import org.chocosolver.solver.constraints.nary.nValue.PropAtMostNValues;
import org.chocosolver.solver.constraints.nary.nValue.amnv.graph.Gci;
import org.chocosolver.solver.constraints.nary.nValue.amnv.mis.MDRk;
import org.chocosolver.solver.constraints.nary.nValue.amnv.rules.R;
import org.chocosolver.solver.constraints.nary.nValue.amnv.rules.R1;
import org.chocosolver.solver.constraints.nary.nValue.amnv.rules.R3;
import org.chocosolver.solver.constraints.nary.sort.PropKeysorting;
import org.chocosolver.solver.constraints.nary.sum.IntLinCombFactory;
import org.chocosolver.solver.constraints.nary.tree.PropAntiArborescences;
import org.chocosolver.solver.constraints.ternary.*;
import org.chocosolver.solver.constraints.unary.Member;
import org.chocosolver.solver.constraints.unary.NotMember;
import org.chocosolver.solver.exception.SolverException;
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.tools.ArrayUtils;
import org.chocosolver.util.tools.VariableUtils;

import java.util.Arrays;
import java.util.BitSet;

import static java.lang.Math.abs;

/**
 * Interface to make constraints over BoolVar and IntVar
 *
 * A kind of factory relying on interface default implementation to allow (multiple) inheritance
 *
 * @author Jean-Guillaume FAGES
 * @author Charles Prud'homme
 * @since 4.0.0
 */
public interface IIntConstraintFactory extends ISelf {

	//##################################################################################################################
	// UNARIES #########################################################################################################
	//##################################################################################################################

	/**
	 * Creates an arithmetic constraint : var op cste,
	 * where op in {"=", "!=", ">","<",">=","<="}
	 *
	 * @param var  a variable
	 * @param op   an operator
	 * @param cste a constant
	 */
	default Constraint arithm(IntVar var, String op, int cste) {
		return new Arithmetic(var, Operator.get(op), cste);
	}

	/**
	 * Creates a member constraint.
	 * Ensures var takes its values in table
	 *
	 * @param var   an integer variable
	 * @param table an array of values
	 */
	default Constraint member(IntVar var, int[] table) {
		return new Member(var, table);
	}

	/**
	 * Creates a member constraint.
	 * Ensures var takes its values in [LB, UB]
	 *
	 * @param var an integer variable
	 * @param lb  the lower bound of the interval
	 * @param ub  the upper bound of the interval
	 */
	default Constraint member(IntVar var, int lb, int ub) {
		return new Member(var, lb, ub);
	}

	/**
	 * Gets the opposite of a given constraint
	 * Works for any constraint, including globals, but the associated performances might be weak
	 * @param cstr a constraint
	 * @return the opposite constraint of cstr
	 */
	default Constraint not(Constraint cstr){
		return cstr.getOpposite();
	}

	/**
	 * Creates a notMember constraint.
	 * Ensures var does not take its values in table
	 *
	 * @param var   an integer variable
	 * @param table an array of values
	 */
	default Constraint notMember(IntVar var, int[] table) {
		return new NotMember(var, table);
	}

	/**
	 * Creates a notMember constraint.
	 * Ensures var does not take its values in [lb, UB]
	 *
	 * @param var an integer variable
	 * @param lb  the lower bound of the interval
	 * @param ub  the upper bound of the interval
	 */
	default Constraint notMember(IntVar var, int lb, int ub) {
		return new NotMember(var, lb, ub);
	}

	//##################################################################################################################
	//BINARIES #########################################################################################################
	//##################################################################################################################

	/**
	 * Creates an absolute value constraint: var1 = |var2|
	 */
	default Constraint absolute(IntVar var1, IntVar var2) {
		assert var1.getModel() == var2.getModel();
		return new Constraint("Absolute", new PropAbsolute(var1, var2));
	}

	/**
	 * Creates an arithmetic constraint: var1 op var2,
	 * where op in {"=", "!=", ">","<",">=","<="}
	 *
	 * @param var1 first variable
	 * @param op   an operator
	 * @param var2 second variable
	 */
	default Constraint arithm(IntVar var1, String op, IntVar var2) {
		if (var2.isInstantiated()) {
			return arithm(var1, op, var2.getValue());
		}
		if (var1.isInstantiated()) {
			return arithm(var2, Operator.getFlip(op), var1.getValue());
		}
		return new Arithmetic(var1, Operator.get(op), var2);
	}

	/**
	 * Creates an arithmetic constraint : var1 op var2,
	 * where op in {"=", "!=", ">","<",">=","<="} or {"+", "-"}
	 *
	 * @param var1 first variable
	 * @param op1  an operator
	 * @param var2 second variable
	 * @param op2  another operator
	 * @param cste an operator
	 */
	default Constraint arithm(IntVar var1, String op1, IntVar var2, String op2, int cste) {
		if (var2.isInstantiated()) {
			if ("+".equals(op1)) {
				return arithm(var1, op2, cste - var2.getValue());
			} else if ("-".equals(op1)) {
				return arithm(var1, op2, cste + var2.getValue());
			}
		}
		if (var1.isInstantiated()) {
			if ("+".equals(op1)) {
				return arithm(var2, op2, cste - var1.getValue());
			} else if ("-".equals(op1)) {
				return arithm(var2, Operator.getFlip(op2), var1.getValue() - cste);
			}
		}
		return new Arithmetic(var1, Operator.get(op1), var2, Operator.get(op2), cste);
	}

	/**
	 * Creates a distance constraint : |var1-var2| op cste
	 * 
* where op can take its value among {"=", ">", "<", "!="} */ default Constraint distance(IntVar var1, IntVar var2, String op, int cste) { assert var1.getModel() == var2.getModel(); Operator operator = Operator.get(op); if (operator != Operator.EQ && operator != Operator.GT && operator != Operator.LT && operator != Operator.NQ) { throw new SolverException("Unexpected operator for distance"); } if (cste < 0) { switch (operator) { case EQ: case LT: default: throw new SolverException("Invalid PropDistanceXYC operator "+operator); } } return new Constraint("DistanceXYC " + operator.name(), new PropDistanceXYC(ArrayUtils.toArray(var1, var2), operator, cste)); } /** * Creates an element constraint: value = table[index-offset] * * @param value an integer variable taking its value in table * @param table an array of integer values * @param index an integer variable representing the value of value in table * @param offset offset matching index.lb and table[0] (Generally 0) */ default Constraint element(IntVar value, int[] table, IntVar index, int offset) { return ElementFactory.detect(value, table, index, offset); } /** * Creates an element constraint: value = table[index] * * @param value an integer variable taking its value in table * @param table an array of integer values * @param index an integer variable representing the value of value in table */ default Constraint element(IntVar value, int[] table, IntVar index) { return element(value, table, index, 0); } /** * Creates a square constraint: var1 = var2^2 */ default Constraint square(IntVar var1, IntVar var2) { assert var1.getModel() == var2.getModel(); return new Constraint("Square", new PropSquare(var1, var2)); } /** * Create a table constraint over a couple of variables var1 and var2 * * Uses AC3rm algorithm by default * * @param var1 first variable * @param var2 second variable */ default Constraint table(IntVar var1, IntVar var2, Tuples tuples) { return table(var1,var2,tuples,"AC3bit+rm"); } /** * Creates a table constraint over a couple of variables var1 and var2:
* - AC2001: table constraint which applies the AC2001 algorithm,
* - AC3: table constraint which applies the AC3 algorithm,
* - AC3rm: table constraint which applies the AC3 rm algorithm,
* - AC3bit+rm (default): table constraint which applies the AC3 bit+rm algorithm,
* - FC: table constraint which applies forward checking algorithm.
* * @param var1 first variable * @param var2 second variable * @param tuples the relation between the two variables, among {"AC3", "AC3rm", "AC3bit+rm", "AC2001", "FC"} */ default Constraint table(IntVar var1, IntVar var2, Tuples tuples, String algo) { Propagator p; switch (algo) { case "AC2001": p = new PropBinAC2001(var1, var2, tuples); break; case "FC": p = new PropBinFC(var1, var2, tuples); break; case "AC3": p = new PropBinAC3(var1, var2, tuples); break; case "AC3rm": p = new PropBinAC3rm(var1, var2, tuples); break; case "AC3bit+rm": p = new PropBinAC3bitrm(var1, var2, tuples); break; default: throw new SolverException("Table algorithm "+algo+" is unkown"); } return new Constraint("TableBin(" + algo + ")", p); } /** * Creates a multiplication constraint: X * Y = Z * * @param X first variable * @param Y a constant * @param Z result variable */ default Constraint times(IntVar X, int Y, IntVar Z) { if (Y == 0) { return arithm(Z, "=", 0); } else if (Y == 1) { return arithm(X, "=", Z); } else if (Y < 0) { return times(X.getModel().intMinusView(X), -Y, Z); } else { return new Constraint("Times", new PropScale(X, Y, Z)); } } /** * Creates a multiplication constraint: X * Y = Z * * @param X first variable * @param Y second variable * @param Z a constant (result) */ default Constraint times(IntVar X, IntVar Y, int Z) { return times(X, Y, X.getModel().intVar(Z)); } //################################################################################################################## //TERNARIES ######################################################################################################## //################################################################################################################## /** * Creates an arithmetic constraint: var1 op1 var2 op2 var3, * where op1 and op2 in {"=", "!=", ">","<",">=","<="} or {"+", "-"} * * @param var1 first variable * @param op1 an operator * @param var2 second variable * @param op2 another operator * @param var3 third variable */ default Constraint arithm(IntVar var1, String op1, IntVar var2, String op2, IntVar var3) { switch (op1) { case "+": return scalar(new IntVar[]{var1, var2}, new int[]{1, 1}, op2, var3); case "-": return scalar(new IntVar[]{var1, var2}, new int[]{1, -1}, op2, var3); default: switch (op2) { case "+": return scalar(new IntVar[]{var1, var3}, new int[]{1, -1}, op1, var2); case "-": return scalar(new IntVar[]{var1, var3}, new int[]{1, 1}, op1, var2); default: throw new SolverException("Unknown operators for arithm constraint"); } } } /** * Creates a distance constraint: |var1-var2| op var3 *
* where op can take its value among {"=", ">", "<"} * * @param var1 first variable * @param var2 second variable * @param op an operator * @param var3 resulting variable */ default Constraint distance(IntVar var1, IntVar var2, String op, IntVar var3) { Operator oper = Operator.get(op); if (oper != Operator.EQ && oper != Operator.GT && oper != Operator.LT) { throw new SolverException("Unexpected operator for distance"); } return new Constraint("DistanceXYZ " + op, new PropDistanceXYZ(ArrayUtils.toArray(var1,var2,var3), oper)); } /** * Creates an euclidean division constraint. * Ensures dividend / divisor = result, rounding towards 0 * Also ensures divisor != 0 * * @param dividend dividend * @param divisor divisor * @param result result */ default Constraint div(IntVar dividend, IntVar divisor, IntVar result) { return new Constraint("DivisionEucl", new PropDivXYZ(dividend, divisor, result)); } /** * Creates a maximum constraint : max = max(var1, var2) * (Bound Consistency) * * @param max a variable * @param var1 a variable * @param var2 a variable */ default Constraint max(IntVar max, IntVar var1, IntVar var2) { return new Constraint("Max", new PropMaxBC(max, var1, var2)); } /** * Creates a minimum constraint: min = min(var1, var2) * (Bound Consistency) * * @param min a variable * @param var1 a variable * @param var2 a variable */ default Constraint min(IntVar min, IntVar var1, IntVar var2) { return new Constraint("Min", new PropMinBC(min, var1, var2)); } /** * Creates a modulo constraint. * Ensures X % Y = Z, *
i.e.:
* - X / Y = T1 and,
* - T1 * Y = T2 and,
* - Z + T2 = X
*
* where T1 = T2 = [-|X|, |X|] * * @param X first variable * @param Y second variable * @param Z result */ default Constraint mod(IntVar X, IntVar Y, IntVar Z) { int xl = abs(X.getLB()); int xu = abs(X.getUB()); int b = Math.max(xl, xu); Model model = X.getModel(); IntVar t1 = model.intVar(model.generateName("T1_"), -b, b, true); IntVar t2 = model.intVar(model.generateName("T2_"), -b, b, true); div(X, Y, t1).post(); times(t1, Y, t2).post(); return sum(new IntVar[]{Z, t2}, "=", X); } /** * Creates a multiplication constraint: X * Y = Z * * @param X first variable * @param Y second variable * @param Z result variable */ @SuppressWarnings("SuspiciousNameCombination") default Constraint times(IntVar X, IntVar Y, IntVar Z) { if (Y.isInstantiated()) { return times(X, Y.getValue(), Z); } else if (X.isInstantiated()) { return times(Y, X.getValue(), Z); } else if (TuplesFactory.canBeTupled(X, Y, Z)) { return table(new IntVar[]{X, Y, Z}, TuplesFactory.times(X, Y, Z)); } else { return new Times(X, Y, Z); } } //################################################################################################################## //GLOBALS ########################################################################################################## //################################################################################################################## /** * Creates an allDifferent constraint. * Ensures that all variables from vars take a different value. * Uses BC plus a probabilistic AC propagator to get a compromise between BC and AC * * @param vars list of variables */ default Constraint allDifferent(IntVar... vars) { return allDifferent(vars, "DEFAULT"); } /** * Creates an allDifferent constraint. * Ensures that all variables from vars take a different value. * The consistency level should be chosen among "BC", "AC" and "DEFAULT". * * @param vars list of variables * @param CONSISTENCY consistency level, among {"BC", "AC"} *

* BC: * Based on: "A Fast and Simple Algorithm for Bounds Consistency of the AllDifferent Constraint"
* A. Lopez-Ortiz, CG. Quimper, J. Tromp, P.van Beek *
* AC: * Uses Regin algorithm * Runs in O(m.n) worst case time for the initial propagation and then in O(n+m) on average. *

* DEFAULT: *
* Uses BC plus a probabilistic AC propagator to get a compromise between BC and AC */ default Constraint allDifferent(IntVar[] vars, String CONSISTENCY) { if(vars.length<=1) return _me().trueConstraint(); return new AllDifferent(vars, CONSISTENCY); } /** * Creates an allDifferent constraint subject to the given condition. More precisely: * * IF singleCondition * for all X,Y in vars, condition(X) => X != Y * ELSE * for all X,Y in vars, condition(X) AND condition(Y) => X != Y * * @param vars collection of variables * @param condition condition defining which variables should be constrained * @param singleCondition specifies how to apply filtering */ default Constraint allDifferentUnderCondition(IntVar[] vars, Condition condition, boolean singleCondition) { if (singleCondition) { return new Constraint("AllDifferent" + condition, new PropCondAllDiffInst(vars, condition, singleCondition), new PropCondAllDiff_AC(vars, condition) ); } return new Constraint("AllDifferent" + condition, new PropCondAllDiffInst(vars, condition, singleCondition)); } /** * Creates an allDifferent constraint for variables that are not equal to 0. * There can be multiple variables equal to 0. * * @param vars collection of variables */ default Constraint allDifferentExcept0(IntVar[] vars) { return allDifferentUnderCondition(vars, Condition.EXCEPT_0, true); } /** * Creates an allEqual constraint. * Ensures that all variables from vars take the same value. * * @param vars list of variables */ default Constraint allEqual(IntVar... vars) { return atMostNValues(vars, _me().intVar(1), false); } /** * Creates an allEqual constraint. * Ensures that all variables from vars take more than a single value. * * @param vars list of variables */ default Constraint notAllEqual(IntVar... vars) { return atLeastNValues(vars, _me().intVar(2), false); } /** * Creates an among constraint. * nbVar is the number of variables of the collection vars that take their value in values. *
gccat among *
* Propagator : * C. Bessiere, E. Hebrard, B. Hnich, Z. Kiziltan, T. Walsh, * Among, common and disjoint Constraints * CP-2005 * * @param nbVar a variable * @param vars vector of variables * @param values set of values */ default Constraint among(IntVar nbVar, IntVar[] vars, int[] values) { int[] vls = new TIntHashSet(values).toArray(); // remove double occurrences Arrays.sort(vls); // sort return new Constraint("Among", new PropAmongGAC(ArrayUtils.append(vars, new IntVar[]{nbVar}), vls)); } /** * Creates an and constraint that is satisfied if all boolean variables in bools are true * @param bools an array of boolean variable * @return a constraint and ensuring that variables in bools are all set to true */ default Constraint and(BoolVar... bools) { Model s = bools[0].getModel(); IntVar sum = s.intVar(0, bools.length, true); s.sum(bools, "=", sum).post(); return s.arithm(sum, "=", bools.length); } /** * Creates an and constraint that is satisfied if all constraints in cstrs are satisfied * BEWARE: this should not be used to post several constraints at once but in a reification context * @param cstrs an array of constraints * @return a constraint and ensuring that all constraints in cstrs are satisfied */ default Constraint and(Constraint... cstrs){ BoolVar[] bools = new BoolVar[cstrs.length]; for(int i=0;i= nValues to hold. *

* This embeds a light propagator by default. * Additional filtering algorithms can be added. * * @param vars collection of variables * @param nValues limit variable * @param AC additional filtering algorithm, domain filtering algorithm derivated from (Soft)AllDifferent */ default Constraint atLeastNValues(IntVar[] vars, IntVar nValues, boolean AC) { int[] vals = getDomainUnion(vars); if (AC) { return new Constraint("AtLeastNValues", new PropAtLeastNValues(vars, vals, nValues), new PropAtLeastNValues_AC(vars, nValues)); } else { return new Constraint("AtLeastNValues", new PropAtLeastNValues(vars, vals, nValues)); } } /** * Creates an atMostNValue constraint. * Let N be the number of distinct values assigned to the variables of the vars collection. * Enforce condition N <= nValues to hold. *

* This embeds a light propagator by default. * Additional filtering algorithms can be added. * * @param vars collection of variables * @param nValues limit variable * @param STRONG "AMNV" Filters the conjunction of AtMostNValue and disequalities * (see Fages and Lapègue Artificial Intelligence 2014) * automatically detects disequalities and allDifferent constraints. * Presumably useful when nValues must be minimized. */ default Constraint atMostNValues(IntVar[] vars, IntVar nValues, boolean STRONG) { int[] vals = getDomainUnion(vars); if (STRONG) { Gci gci = new Gci(vars); R[] rules = new R[]{new R1(), new R3(vars.length, nValues.getModel())}; return new Constraint("AtMostNValues", new PropAtMostNValues(vars, vals, nValues), new PropAMNV(vars, nValues, gci, new MDRk(gci), rules)); } else { return new Constraint("AtMostNValues", new PropAtMostNValues(vars, vals, nValues)); } } /** * Creates a BinPacking constraint. * Bin Packing formulation: * forall b in [0,binLoad.length-1], * binLoad[b]=sum(itemSize[i] | i in [0,itemSize.length-1], itemBin[i] = b+offset * forall i in [0,itemSize.length-1], itemBin is in [offset,binLoad.length-1+offset], * * @param itemBin IntVar representing the bin of each item * @param itemSize int representing the size of each item * @param binLoad IntVar representing the load of each bin (i.e. the sum of the size of the items in it) * @param offset 0 by default but typically 1 if used within MiniZinc * (which counts from 1 to n instead of from 0 to n-1) */ default Constraint binPacking(IntVar[] itemBin, int[] itemSize, IntVar[] binLoad, int offset) { Model model = itemBin[0].getModel(); // redundant filtering int sum = 0; for (int is : itemSize) { sum += is; } return Constraint.merge("BinPacking", new Constraint("BinPacking", new PropItemToLoad(itemBin,itemSize,binLoad, offset), new PropLoadToItem(itemBin,itemSize,binLoad, offset)), model.sum(binLoad, "=", sum) ); } /** * Creates an channeling constraint between an integer variable and a set of boolean variables. * Maps the boolean assignments variables bVars with the standard assignment variable var.
* var = i <-> bVars[i-offset] = 1 * * @param bVars array of boolean variables * @param var observed variable. Should presumably have an enumerated domain * @param offset 0 by default but typically 1 if used within MiniZinc * (which counts from 1 to n instead of from 0 to n-1) */ default Constraint boolsIntChanneling(BoolVar[] bVars, IntVar var, int offset) { if (var.hasEnumeratedDomain()) { return new Constraint("DomainChanneling", new PropEnumDomainChanneling(bVars, var, offset)); } else { IntVar enumV = var.getModel().intVar(var.getName() + "_enumImage", var.getLB(), var.getUB(), false); return new Constraint("BoolChanneling", new PropEnumDomainChanneling(bVars, enumV, offset), new PropEqualX_Y(var, enumV) ); } } /** * Creates an channeling constraint between an integer variable and a set of bit variables. * Ensures that var = 20*BIT_1 + 21*BIT_2 + ... 2n-1*BIT_n. *
* BIT_1 is related to the first bit of OCTET (2^0), * BIT_2 is related to the first bit of OCTET (2^1), etc. *
* The upper bound of var is given by 2n, where n is the size of the array bits. * * @param bits the array of bits * @param var the numeric value */ default Constraint bitsIntChanneling(BoolVar[] bits, IntVar var) { return new Constraint("bitsIntChanneling", new PropBitChanneling(var, bits)); } /** * Creates an channeling constraint between an integer variable and a set of clauses. * Link each value from the domain of var to two boolean variable: * one reifies the equality to the i^th value of the variable domain, * the other reifies the less-or-equality to the i^th value of the variable domain. * Contract: eVars.lenght == lVars.length == var.getUB() - var.getLB() + 1 * Contract: var is not a boolean variable * * @param var an Integer variable * @param eVars array of EQ boolean variables * @param lVars array of LQ boolean variables */ default Constraint clausesIntChanneling(IntVar var, BoolVar[] eVars, BoolVar[] lVars) { return new Constraint("clausesIntChanneling", new PropClauseChanneling(var, eVars, lVars)); } /** * Creates a circuit constraint which ensures that *

the elements of vars define a covering circuit *

where vars[i] = offset+j means that j is the successor of i. *

* Filtering algorithms: *

subtour elimination : Caseau & Laburthe (ICLP'97) *

allDifferent GAC algorithm: Régin (AAAI'94) *

dominator-based filtering: Fages & Lorca (CP'11) *

Strongly Connected Components based filtering (Cambazar & Bourreau JFPC'06 and Fages and Lorca TechReport'12) * * @param vars vector of variables which take their value in [offset,offset+|vars|-1] * @return a circuit constraint */ default Constraint circuit(IntVar[] vars) { return circuit(vars, 0); } /** * Creates a circuit constraint which ensures that *

the elements of vars define a covering circuit *

where vars[i] = offset+j means that j is the successor of i. *

* Filtering algorithms: *

subtour elimination : Caseau & Laburthe (ICLP'97) *

allDifferent GAC algorithm: Régin (AAAI'94) *

dominator-based filtering: Fages & Lorca (CP'11) *

Strongly Connected Components based filtering (Cambazar & Bourreau JFPC'06 and Fages and Lorca TechReport'12) * * @param vars vector of variables which take their value in [offset,offset+|vars|-1] * @param offset 0 by default but typically 1 if used within MiniZinc * (which counts from 1 to n instead of from 0 to n-1) * @return a circuit constraint */ default Constraint circuit(IntVar[] vars, int offset) { return circuit(vars, offset, CircuitConf.RD); } /** * Creates a circuit constraint which ensures that *

the elements of vars define a covering circuit *

where vars[i] = offset+j means that j is the successor of i. *

* Filtering algorithms: *

subtour elimination : Caseau & Laburthe (ICLP'97) *

allDifferent GAC algorithm: Régin (AAAI'94) *

dominator-based filtering: Fages & Lorca (CP'11) *

Strongly Connected Components based filtering (Cambazard & Bourreau JFPC'06 and Fages and Lorca TechReport'12) *

See Fages PhD Thesis (2014) for more information * * @param vars vector of variables which take their value in [offset,offset+|vars|-1] * @param offset 0 by default but typically 1 if used within MiniZinc * (which counts from 1 to n instead of from 0 to n-1) * @param conf filtering options * @return a circuit constraint */ default Constraint circuit(IntVar[] vars, int offset, CircuitConf conf) { Propagator[] props; if (conf == CircuitConf.LIGHT) { props = new Propagator[]{new PropNoSubtour(vars, offset)}; } else { props = new Propagator[]{ new PropNoSubtour(vars, offset), new PropCircuit_ArboFiltering(vars, offset, conf), new PropCircuit_AntiArboFiltering(vars, offset, conf), new PropCircuitSCC(vars, offset, conf) }; } return new Constraint("Circuit", ArrayUtils.append(allDifferent(vars, "AC").propagators, props)); } /** * Creates a regular constraint that supports a cost function. * Ensures that the assignment of a sequence of variables is recognized by costAutomaton, a deterministic finite automaton, * and that the sum of the costs associated to each assignment is bounded by the cost variable. * This version allows to specify different costs according to the automaton state at which the assignment occurs * (i.e. the transition starts) * * @param vars sequence of variables * @param cost cost variable * @param costAutomaton a deterministic finite automaton defining the regular language and the costs * Can be built with method CostAutomaton.makeSingleResource(...) */ default Constraint costRegular(IntVar[] vars, IntVar cost, ICostAutomaton costAutomaton) { return new CostRegular(vars, cost, costAutomaton); } /** * Creates a count constraint. * Let N be the number of variables of the vars collection assigned to value value; * Enforce condition N = limit to hold. *

* * @param value an int * @param vars a vector of variables * @param limit a variable */ default Constraint count(int value, IntVar[] vars, IntVar limit) { return new Constraint("Count", new PropCount_AC(vars, value, limit)); } /** * Creates a count constraint. * Let N be the number of variables of the vars collection assigned to value value; * Enforce condition N = limit to hold. *

* * @param value a variable * @param vars a vector of variables * @param limit a variable */ default Constraint count(IntVar value, IntVar[] vars, IntVar limit) { if (value.isInstantiated()) { return count(value.getValue(), vars, limit); } else if (value.hasEnumeratedDomain()) { return new Constraint("Count", new PropCountVar(vars, value, limit)); } else { Model model = value.getModel(); IntVar Evalue = model.intVar(model.generateName("COUNT_"), value.getLB(), value.getUB(), false); return new Constraint("Count", new PropEqualX_Y(Evalue, value), new PropCountVar(vars, Evalue, limit)); } } /** * Creates a cumulative constraint: Enforces that at each point in time, * the cumulated height of the set of tasks that overlap that point * does not exceed a given limit. * * Task duration and height should be >= 0 * Discards tasks whose duration or height is equal to zero * * @param tasks Task objects containing start, duration and end variables * @param heights integer variables representing the resource consumption of each task * @param capacity integer variable representing the resource capacity * @return a cumulative constraint */ default Constraint cumulative(Task[] tasks, IntVar[] heights, IntVar capacity) { return cumulative(tasks, heights, capacity, true); } /** * Creates a cumulative constraint: Enforces that at each point in time, * the cumulated height of the set of tasks that overlap that point * does not exceed a given limit. * * Task duration and height should be >= 0 * Discards tasks whose duration or height is equal to zero * * @param tasks Task objects containing start, duration and end variables * @param heights integer variables representing the resource consumption of each task * @param capacity integer variable representing the resource capacity * @param incremental specifies if an incremental propagation should be applied * @return a cumulative constraint */ default Constraint cumulative(Task[] tasks, IntVar[] heights, IntVar capacity, boolean incremental) { return cumulative(tasks,heights,capacity,incremental, Cumulative.Filter.DEFAULT); } /** * Creates a cumulative constraint: Enforces that at each point in time, * the cumulated height of the set of tasks that overlap that point * does not exceed a given limit. * * Task duration and height should be >= 0 * Discards tasks whose duration or height is equal to zero * * @param tasks Task objects containing start, duration and end variables * @param heights integer variables representing the resource consumption of each task * @param capacity integer variable representing the resource capacity * @param incremental specifies if an incremental propagation should be applied * @param filters specifies which filtering algorithms to apply * @return a cumulative constraint */ default Constraint cumulative(Task[] tasks, IntVar[] heights, IntVar capacity, boolean incremental, Cumulative.Filter... filters) { int nbUseFull = 0; for (int h = 0; h < heights.length; h++) { if (heights[h].getUB()>0 && tasks[h].getDuration().getUB()>0) { nbUseFull++; } } // remove tasks that have no impact on resource consumption if (nbUseFull < tasks.length) { if (nbUseFull == 0) return arithm(capacity, ">=", 0); Task[] T2 = new Task[nbUseFull]; IntVar[] H2 = new IntVar[nbUseFull]; int idx = 0; for (int h = 0; h < heights.length; h++) { if (heights[h].getUB()>0 && tasks[h].getDuration().getUB()>0) { T2[idx] = tasks[h]; H2[idx] = heights[h]; idx++; } } tasks = T2; heights = H2; } return new Cumulative(tasks, heights, capacity, incremental, filters); } /** * Creates a diffN constraint. Constrains each rectanglei, given by their origins Xi,Yi * and sizes widthi,heighti, to be non-overlapping. * * @param X collection of coordinates in first dimension * @param Y collection of coordinates in second dimension * @param width collection of width (each duration should be > 0) * @param height collection of height (each height should be >= 0) * @param addCumulativeReasoning indicates whether or not redundant cumulative constraints should be put on each dimension (advised) * @return a non-overlapping constraint */ default Constraint diffN(IntVar[] X, IntVar[] Y, IntVar[] width, IntVar[] height, boolean addCumulativeReasoning) { Model model = X[0].getModel(); Constraint diffNCons = new Constraint( "DiffN", new PropDiffN(X, Y, width, height, false), new PropDiffN(X, Y, width, height, false) ); if (addCumulativeReasoning) { IntVar[] EX = new IntVar[X.length]; IntVar[] EY = new IntVar[X.length]; Task[] TX = new Task[X.length]; Task[] TY = new Task[X.length]; int minx = Integer.MAX_VALUE / 2; int maxx = Integer.MIN_VALUE / 2; int miny = Integer.MAX_VALUE / 2; int maxy = Integer.MIN_VALUE / 2; for (int i = 0; i < X.length; i++) { EX[i] = model.intVar(model.generateName("diffN_"), X[i].getLB() + width[i].getLB(), X[i].getUB() + width[i].getUB(), true); EY[i] = model.intVar(model.generateName("diffN_"), Y[i].getLB() + height[i].getLB(), Y[i].getUB() + height[i].getUB(), true); TX[i] = new Task(X[i], width[i], EX[i]); TY[i] = new Task(Y[i], height[i], EY[i]); minx = Math.min(minx, X[i].getLB()); miny = Math.min(miny, Y[i].getLB()); maxx = Math.max(maxx, X[i].getUB() + width[i].getUB()); maxy = Math.max(maxy, Y[i].getUB() + height[i].getUB()); } IntVar maxX = model.intVar(model.generateName("diffN_"), minx, maxx, true); IntVar minX = model.intVar(model.generateName("diffN_"), minx, maxx, true); IntVar diffX = model.intVar(model.generateName("diffN_"), 0, maxx - minx, true); IntVar maxY = model.intVar(model.generateName("diffN_"), miny, maxy, true); IntVar minY = model.intVar(model.generateName("diffN_"), miny, maxy, true); IntVar diffY = model.intVar(model.generateName("diffN_"), 0, maxy - miny, true); return Constraint.merge("DiffNWithCumulative", diffNCons, min(minX, X), max(maxX, EX), scalar(new IntVar[]{maxX, minX}, new int[]{1, -1}, "=", diffX), cumulative(TX, height, diffY), min(minY, Y), max(maxY, EY), scalar(new IntVar[]{maxY, minY}, new int[]{1, -1}, "=", diffY), cumulative(TY, width, diffX) ); }else{ return diffNCons; } } /** * Creates a element constraint: value = table[index-offset] * where table is an array of variables. * * @param value value variable * @param table array of variables * @param index index variable in range [offset,offset+|table|-1] * @param offset int offset, generally 0 */ default Constraint element(IntVar value, IntVar[] table, IntVar index, int offset) { // uses two propagator to perform a fix point return new Constraint( "Element", new PropElementV_fast(value, table, index, offset, true)); } /** * Creates a global cardinality constraint (GCC): * Each value values[i] should be taken by exactly occurrences[i] variables of vars. *
* This constraint does not ensure any well-defined level of consistency, yet. * * @param vars collection of variables * @param values collection of constrained values * @param occurrences collection of cardinality variables * @param closed restricts domains of vars to values if set to true */ default Constraint globalCardinality(IntVar[] vars, int[] values, IntVar[] occurrences, boolean closed) { assert values.length == occurrences.length; if (!closed) { return new GlobalCardinality(vars, values, occurrences); } else { TIntArrayList toAdd = new TIntArrayList(); TIntSet givenValues = new TIntHashSet(); for (int i : values) { assert !givenValues.contains(i); givenValues.add(i); } for (IntVar var : vars) { int ub = var.getUB(); for (int k = var.getLB(); k <= ub; k = var.nextValue(k)) { if (!givenValues.contains(k)) { if (!toAdd.contains(k)) { toAdd.add(k); } } } } if (toAdd.size() > 0) { int n2 = values.length + toAdd.size(); int[] v2 = new int[n2]; IntVar[] cards = new IntVar[n2]; System.arraycopy(values, 0, v2, 0, values.length); System.arraycopy(occurrences, 0, cards, 0, values.length); for (int i = values.length; i < n2; i++) { v2[i] = toAdd.get(i - values.length); cards[i] = vars[0].getModel().intVar(0); } return new GlobalCardinality(vars, v2, cards); } else { return new GlobalCardinality(vars, values, occurrences); } } } /** * Creates an inverse channeling between vars1 and vars2: * vars1[i] = j <=> vars2[j] = i * Performs AC if domains are enumerated. * If not, then it works on bounds without guaranteeing BC * (enumerated domains are strongly recommended) *

* Beware you should have |vars1| = |vars2| * * @param vars1 vector of variables which take their value in [0,|vars2|-1] * @param vars2 vector of variables which take their value in [0,|vars1|-1] */ default Constraint inverseChanneling(IntVar[] vars1, IntVar[] vars2) { return inverseChanneling(vars1,vars2,0,0); } /** * Creates an inverse channeling between vars1 and vars2: * vars1[i-offset2] = j <=> vars2[j-offset1] = i * Performs AC if domains are enumerated. * If not, then it works on bounds without guaranteeing BC * (enumerated domains are strongly recommended) *

* Beware you should have |vars1| = |vars2| * * @param vars1 vector of variables which take their value in [offset1,offset1+|vars2|-1] * @param vars2 vector of variables which take their value in [offset2,offset2+|vars1|-1] * @param offset1 lowest value in vars1 (most often 0) * @param offset2 lowest value in vars2 (most often 0) */ default Constraint inverseChanneling(IntVar[] vars1, IntVar[] vars2, int offset1, int offset2) { if (vars1.length != vars2.length) throw new UnsupportedOperationException(Arrays.toString(vars1) + " and " + Arrays.toString(vars2) + " should have same size"); boolean allEnum = true; for (int i = 0; i < vars1.length && allEnum; i++) { if (!(vars1[i].hasEnumeratedDomain() && vars2[i].hasEnumeratedDomain())) { allEnum = false; } } Propagator ip = allEnum ? new PropInverseChannelAC(vars1, vars2, offset1, offset2) : new PropInverseChannelBC(vars1, vars2, offset1, offset2); return new Constraint("InverseChanneling", ArrayUtils.append( allDifferent(vars1, "").getPropagators(), allDifferent(vars2, "").getPropagators(), new Propagator[]{ip} )); } /** * Creates an intValuePrecedeChain constraint. * Ensure that if there exists j such that X[j] = T, then, there must exist i < j such that * X[i] = S. * * @param X an array of variables * @param S a value * @param T another value */ default Constraint intValuePrecedeChain(IntVar[] X, int S, int T) { return new Constraint("int_value_precede", new PropIntValuePrecedeChain(X, S, T)); } /** * Creates an intValuePrecedeChain constraint. * Ensure that, for each pair of V[k] and V[l] of values in V, such that k < l, * if there exists j such that X[j] = V[l], then, there must exist i < j such that * X[i] = V[k]. * * @param X array of variables * @param V array of (distinct) values */ default Constraint intValuePrecedeChain(IntVar[] X, int[] V) { if (V.length > 1) { TIntHashSet values = new TIntHashSet(); PropIntValuePrecedeChain[] ps = new PropIntValuePrecedeChain[V.length - 1]; values.add(V[0]); for (int i = 1; i < V.length; i++) { if (values.contains(V[i])) { throw new SolverException("\"int_value_precede\" requires V to be made of distinct values"); } values.add(V[i]); ps[i - 1] = new PropIntValuePrecedeChain(X, V[i - 1], V[i]); } return new Constraint("int_value_precede", ps); } else { return _me().trueConstraint(); } } /** * Creates a knapsack constraint. * Ensures that : *
- occurrences[i] * weight[i] = weightSum *
- occurrences[i] * energy[i] = energySum *
and maximizing the value of energySum. *

*

* A knapsack constraint * wikipedia:
* "Given a set of items, each with a weight and an energy value, * determine the count of each item to include in a collection so that * the total weight is less than or equal to a given limit and the total value is as large as possible. * It derives its name from the problem faced by someone who is constrained by a fixed-size knapsack * and must fill it with the most useful items." * The limit over weightSum has to be specified either in its domain or with an additional constraint: *

	 *     model.post(solver.arithm(weightSum, "<=", limit);
	 * 
* * @param occurrences number of occurrences of every item * @param weightSum load of the knapsack * @param energySum profit of the knapsack * @param weight weight of each item (must be >=0) * @param energy energy of each item (must be >=0) */ default Constraint knapsack(IntVar[] occurrences, IntVar weightSum, IntVar energySum, int[] weight, int[] energy) { return new Constraint("Knapsack", ArrayUtils.append( scalar(occurrences, weight, "=",weightSum).propagators, scalar(occurrences, energy, "=", energySum).propagators, new Propagator[]{new PropKnapsack(occurrences, weightSum, energySum, weight, energy)} )); } /** * Creates a keySort constraint which ensures that the variables of SORTEDvars correspond to the variables * of vars according to a permutation stored in PERMvars (optional, can be null). * The variables of SORTEDvars are also sorted in increasing order wrt to K-size tuples. * The sort is stable, that is, ties are broken using the position of the tuple in vars. *

*

* For example:
* - vars= (<4,2,2>,<2,3,1>,<4,2,1><1,3,0>)
* - SORTEDvars= (<1,3,0>,<2,3,1>,<4,2,2>,<4,2,1>)
* - PERMvars= (2,1,3,0)
* - K = 2
* * @param vars a tuple of array of variables * @param PERMvars array of permutation variables, domains should be [1,vars.length] -- Can be null * @param SORTEDvars a tuple of array of variables sorted in increasing order * @param K key perfixes size (0 ≤ k ≤ m, where m is the size of the array of variable) * @return a keySort constraint */ default Constraint keySort(IntVar[][] vars, IntVar[] PERMvars, IntVar[][] SORTEDvars, int K) { if (PERMvars == null) { int n = vars.length; PERMvars = new IntVar[n]; for (int p = 0; p < n; p++) { PERMvars[p] = vars[0][0].getModel().intVar("p_" + (p + 1), 1, n, true); } } return new Constraint("keySort", new PropKeysorting(vars, SORTEDvars, PERMvars, K)); } /** * Creates a lexChainLess constraint. * For each pair of consecutive vectors varsi and varsi+1 of the vars collection * varsi is lexicographically strictly less than than varsi+1 * * @param vars collection of vectors of variables */ default Constraint lexChainLess(IntVar[]... vars) { return new Constraint("LexChain(<) ", new PropLexChain(vars, true)); } /** * Creates a lexChainLessEq constraint. * For each pair of consecutive vectors varsi and varsi+1 of the vars collection * varsi is lexicographically less or equal than than varsi+1 * * @param vars collection of vectors of variables */ default Constraint lexChainLessEq(IntVar[]... vars) { return new Constraint("LexChain(<=)", new PropLexChain(vars, false)); } /** * Creates a lexLess constraint. * Ensures that vars1 is lexicographically strictly less than vars2. * * @param vars1 vector of variables * @param vars2 vector of variables */ default Constraint lexLess(IntVar[] vars1, IntVar[] vars2) { return new Constraint("Lex(<)", new PropLex(vars1, vars2, true)); } /** * Creates a lexLessEq constraint. * Ensures that vars1 is lexicographically less or equal than vars2. * * @param vars1 vector of variables * @param vars2 vector of variables */ default Constraint lexLessEq(IntVar[] vars1, IntVar[] vars2) { return new Constraint("Lex(<=)", new PropLex(vars1, vars2, false)); } /** * Creates a maximum constraint. * max is the maximum value of the collection of domain variables vars * * @param max a variable * @param vars a vector of variables, of size > 0 */ default Constraint max(IntVar max, IntVar[] vars) { if(vars.length == 2){ return max(max, vars[0], vars[1]); }else { return new Constraint("Max", new PropMax(vars, max)); } } /** * Creates a maximum constraint. * max is the maximum value of the collection of boolean variables vars * * @param max a boolean variable * @param vars a vector of boolean variables, of size > 0 */ default Constraint max(BoolVar max, BoolVar[] vars) { return new Constraint("MinOverBools", new PropBoolMax(vars, max)); } /** * Create a constraint where solutions (tuples) are encoded by a multi-valued decision diagram. * The order of the variables in vars is important and must refer to the MDD. * * @param vars the array of variables * @param MDD the multi-valued decision diagram encoding solutions */ default Constraint mddc(IntVar[] vars, MultivaluedDecisionDiagram MDD) { return new Constraint("mddc", new PropLargeMDDC(MDD, vars)); } /** * Creates a minimum constraint. * min is the minimum value of the collection of domain variables vars * * @param min a variable * @param vars a vector of variables, of size > 0 */ default Constraint min(IntVar min, IntVar[] vars) { if(vars.length == 2) { return min(min, vars[0], vars[1]); }else{ return new Constraint("Min", new PropMin(vars, min)); } } /** * Creates a minimum constraint. * min is the minimum value of the collection of boolean variables vars * * @param min a boolean variable * @param vars a vector of boolean variables, of size > 0 */ default Constraint min(BoolVar min, BoolVar[] vars) { return new Constraint("MinOverBools", new PropBoolMin(vars, min)); } /** * Creates a regular constraint that supports a multiple cost function. * Ensures that the assignment of a sequence of vars is recognized by costAutomaton, a deterministic finite automaton, * and that the sum of the cost vector associated to each assignment is bounded by the variable vector costVars. * This version allows to specify different costs according to the automaton state at which the assignment occurs * (i.e. the transition starts) * * @param vars sequence of variables * @param costVars cost variables * @param costAutomaton a deterministic finite automaton defining the regular language and the costs * Can be built from method CostAutomaton.makeMultiResources(...) */ default Constraint multiCostRegular(IntVar[] vars, IntVar[] costVars, ICostAutomaton costAutomaton) { return new Constraint("MultiCostRegular", new PropMultiCostRegular(vars, costVars, costAutomaton)); } /** * Creates an nValue constraint. * Let N be the number of distinct values assigned to the variables of the vars collection. * Enforce condition N = nValues to hold. *

* This embeds a light propagator by default. * Additional filtering algorithms can be added. *

* see atleast_nvalue and atmost_nvalue * * @param vars collection of variables * @param nValues limit variable * @return the conjunction of atleast_nvalue and atmost_nvalue */ default Constraint nValues(IntVar[] vars, IntVar nValues) { int[] vals = getDomainUnion(vars); Gci gci = new Gci(vars); R[] rules = new R[]{new R1(), new R3(vars.length, nValues.getModel())}; return new Constraint("nValue", // at least new PropAtLeastNValues(vars, vals, nValues), // at most new PropAtMostNValues(vars, vals, nValues), new PropAMNV(vars, nValues, gci, new MDRk(gci), rules)); } /** * Creates an and constraint that is satisfied if at least one boolean variables in bools is true * @param bools an array of boolean variable * @return a constraint that is satisfied if at least one boolean variables in bools is true */ default Constraint or(BoolVar... bools) { Model s = bools[0].getModel(); IntVar sum = s.intVar(0, bools.length, true); s.sum(bools, "=", sum).post(); return s.arithm(sum, ">=", 1); } /** * Creates an and constraint that is satisfied if at least one constraint in cstrs are satisfied * @param cstrs an array of constraints * @return a constraint and ensuring that at least one constraint in cstrs are satisfied */ default Constraint or(Constraint... cstrs){ BoolVar[] bools = new BoolVar[cstrs.length]; for(int i=0;i the elements of vars define a covering path from start to end *

where vars[i] = j means that j is the successor of i. *

Moreover, vars[end] = |vars| *

Requires : |vars|>0 *

* Filtering algorithms: see circuit constraint * * @param vars vector of variables which take their value in [0,|vars|] * @param start variable indicating the index of the first variable in the path * @param end variable indicating the index of the last variable in the path * @return a path constraint */ default Constraint path(IntVar[] vars, IntVar start, IntVar end) { return path(vars,start,end,0); } /** * Creates a path constraint which ensures that *

the elements of vars define a covering path from start to end *

where vars[i] = offset+j means that j is the successor of i. *

Moreover, vars[end-offset] = |vars|+offset *

Requires : |vars|>0 *

* Filtering algorithms: see circuit constraint * * @param vars vector of variables which take their value in [offset,offset+|vars|] * @param start variable indicating the index of the first variable in the path * @param end variable indicating the index of the last variable in the path * @param offset 0 by default but typically 1 if used within MiniZinc * (which counts from 1 to n instead of from 0 to n-1) * @return a path constraint */ default Constraint path(IntVar[] vars, IntVar start, IntVar end, int offset) { assert start != null && end != null && vars != null; switch (vars.length) { case 0: throw new UnsupportedOperationException("|vars| Should be strictly greater than 0"); case 1: return Constraint.merge("path", arithm(start, "=", offset), arithm(end, "=", offset), arithm(vars[0], "=", 1 + offset) ); default: if (start == end) { return start.getModel().falseConstraint(); } else { return Constraint.merge("path", arithm(start, "!=", end), circuit(ArrayUtils.append(vars, new IntVar[]{start}), offset), element(end.getModel().intVar(vars.length + offset), vars, end, offset) ); } } } /** * Creates a regular constraint. * Enforces the sequence of vars to be a word * recognized by the deterministic finite automaton. * For example regexp = "(1|2)(3*)(4|5)"; * The same dfa can be used for different propagators. * * @param vars sequence of variables * @param automaton a deterministic finite automaton defining the regular language */ default Constraint regular(IntVar[] vars, IAutomaton automaton) { return new Constraint("Regular", new PropRegular(vars, automaton)); } /** * Creates a scalar constraint which ensures that Sum(vars[i]*coeffs[i]) operator scalar * * @param vars a collection of IntVar * @param coeffs a collection of int, for which |vars|=|coeffs| * @param operator an operator in {"=", "!=", ">","<",">=","<="} * @param scalar an integer * @return a scalar constraint */ default Constraint scalar(IntVar[] vars, int[] coeffs, String operator, int scalar) { assert vars.length>0; Model s = vars[0].getModel(); IntVar scalarVar = s.intVar(scalar); return scalar(vars,coeffs,operator,scalarVar); } /** * Creates a scalar constraint which ensures that Sum(vars[i]*coeffs[i]) operator scalar * * @param vars a collection of IntVar * @param coeffs a collection of int, for which |vars|=|coeffs| * @param operator an operator in {"=", "!=", ">","<",">=","<="} * @param scalar an IntVar * @return a scalar constraint */ default Constraint scalar(IntVar[] vars, int[] coeffs, String operator, IntVar scalar) { return IntLinCombFactory.reduce(vars, coeffs, Operator.get(operator), scalar); } /** * Creates a sort constraint which ensures that the variables of sortedVars correspond to the variables * of vars according to a permutation. The variables of sortedVars are also sorted in increasing order. *

*

* For example:
* - X= (4,2,1,3)
* - Y= (1,2,3,4) * * @param vars an array of variables * @param sortedVars an array of variables sorted in increasing order * @return a sort constraint */ default Constraint sort(IntVar[] vars, IntVar[] sortedVars) { // return new Constraint("Sort", new PropSort(vars, sortedVars)); IntVar[][] X = new IntVar[vars.length][1]; IntVar[][] Y = new IntVar[sortedVars.length][1]; for (int i = 0; i < vars.length; i++) { X[i][0] = vars[i]; Y[i][0] = sortedVars[i]; } return keySort(X, null, Y, 1); } /** * Creates a subCircuit constraint which ensures that *

the elements of vars define a single circuit of subcircuitSize nodes where *

vars[i] = offset+j means that j is the successor of i. *

and vars[i] = offset+i means that i is not part of the circuit *

the constraint ensures that |{vars[i] =/= offset+i}| = subCircuitLength *

*

Filtering algorithms: *

subtour elimination : Caseau & Laburthe (ICLP'97) *

allDifferent GAC algorithm: Régin (AAAI'94) *

dominator-based filtering: Fages & Lorca (CP'11) (adaptive scheme by default, see implementation) * * @param vars a vector of variables * @param offset 0 by default but 1 if used within MiniZinc * (which counts from 1 to n instead of from 0 to n-1) * @param subCircuitLength expected number of nodes in the circuit * @return a subCircuit constraint */ default Constraint subCircuit(IntVar[] vars, int offset, IntVar subCircuitLength) { int n = vars.length; Model model = vars[0].getModel(); IntVar nbLoops = model.intVar("nLoops", 0, n, true); return new Constraint("SubCircuit", ArrayUtils.append( allDifferent(vars, "AC").getPropagators(), ArrayUtils.toArray( new PropEqualXY_C(new IntVar[]{nbLoops, subCircuitLength}, n), new PropKLoops(vars, offset, nbLoops), new PropSubcircuit(vars, offset, subCircuitLength), new PropSubcircuitDominatorFilter(vars, offset,true) ) )); } /** * Creates a subPath constraint which ensures that *

the elements of vars define a path of SIZE vertices, leading from start to end *

where vars[i] = offset+j means that j is the successor of i. *

where vars[i] = offset+i means that vertex i is excluded from the path. *

Moreover, vars[end-offset] = |vars|+offset *

Requires : |vars|>0 *

* Filtering algorithms: see subCircuit constraint * * @param vars vector of variables which take their value in [offset,offset+|vars|] * @param start variable indicating the index of the first variable in the path * @param end variable indicating the index of the last variable in the path * @param offset 0 by default but typically 1 if used within MiniZinc * (which counts from 1 to n instead of from 0 to n-1) * @param SIZE variable indicating the number of variables to belong to the path * @return a subPath constraint */ default Constraint subPath(IntVar[] vars, IntVar start, IntVar end, int offset, IntVar SIZE) { assert start != null && end != null && vars != null; switch (vars.length) { case 0: throw new UnsupportedOperationException("|vars| Should be strictly greater than 0"); case 1: return Constraint.merge("subPath", arithm(start, "=", offset), arithm(end, "=", offset), arithm(vars[0], "=", 1 + offset), arithm(SIZE, "=", 1) ); default: return Constraint.merge("subPath", arithm(start, "<", vars.length + offset), subCircuit(ArrayUtils.append(vars, new IntVar[]{start}), offset, end.getModel().intOffsetView(SIZE, 1)), element(end.getModel().intVar(vars.length + offset), vars, end, offset) ); } } /** * Creates a sum constraint. * Enforces that ∑i in |vars|varsi operator sum. * * @param vars a collection of IntVar * @param operator operator in {"=", "!=", ">","<",">=","<="} * @param sum an integer * @return a sum constraint */ default Constraint sum(IntVar[] vars, String operator, int sum) { assert vars.length>0; Model s = vars[0].getModel(); IntVar sumVar = s.intVar(sum); return IntLinCombFactory.reduce(vars, Operator.get(operator), sumVar); } /** * Creates a sum constraint. * Enforces that ∑i in |vars|varsi operator sum. * * @param vars a collection of IntVar * @param operator operator in {"=", "!=", ">","<",">=","<="} * @param sum an IntVar * @return a sum constraint */ default Constraint sum(IntVar[] vars, String operator, IntVar sum) { return IntLinCombFactory.reduce(vars, Operator.get(operator), sum); } /** * Creates a sum constraint. * Enforces that ∑i in |vars|varsi operator sum. * This constraint is much faster than the one over integer variables * * @param vars a vector of boolean variables * @param sum an integer */ default Constraint sum(BoolVar[] vars, String operator, int sum) { assert vars.length>0; Model s = vars[0].getModel(); IntVar sumVar = s.intVar(sum); return sum(vars,operator,sumVar); } /** * Creates a sum constraint. * Enforces that ∑i in |vars|varsi operator sum. * This constraint is much faster than the one over integer variables * * @param vars a vector of boolean variables * @param sum a variable */ default Constraint sum(BoolVar[] vars, String operator, IntVar sum) { if(sum.getModel().getSettings().enableDecompositionOfBooleanSum()){ int[] bounds = VariableUtils.boundsForAddition(vars); IntVar p = sum.getModel().intVar(sum.getModel().generateName("RSLT_"), bounds[0], bounds[1], true); IntLinCombFactory.reduce(vars, Operator.EQ, p).post(); return arithm(p, operator, sum); }else { return IntLinCombFactory.reduce(vars, Operator.get(operator), sum); } } /** * Creates a table constraint specifying that the sequence of variables vars must belong to the list of tuples * (or must NOT belong in case of infeasible tuples) * * Default configuration with GACSTR+ algorithm for feasible tuples and GAC3rm otherwise * * @param vars variables forming the tuples * @param tuples the relation between the variables (list of allowed/forbidden tuples) */ default Constraint table(IntVar[] vars, Tuples tuples) { String algo = "GAC3rm"; if(tuples.isFeasible()){ if(tuples.nbTuples()>500){ algo = "CT+"; }else{ algo = "GACSTR+"; } } return table(vars,tuples,algo); } /** * Creates a table constraint, with the specified algorithm defined algo *

* - CT+: Compact-Table algorithm (AC), *
* - GAC2001: Arc Consistency version 2001 for tuples, *
* - GAC2001+: Arc Consistency version 2001 for allowed tuples, *
* - GAC3rm: Arc Consistency version AC3 rm for tuples, *
* - GAC3rm+ (default): Arc Consistency version 3rm for allowed tuples, *
* - GACSTR+: Arc Consistency version STR for allowed tuples, *
* - STR2+: Arc Consistency version STR2 for allowed tuples, *
* - FC: Forward Checking. *
* - MDD+: uses a multi-valued decision diagram for allowed tuples (see mddc constraint), * * @param vars variables forming the tuples * @param tuples the relation between the variables (list of allowed/forbidden tuples) * @param algo to choose among {"TC+", "GAC3rm", "GAC2001", "GACSTR", "GAC2001+", "GAC3rm+", "FC", "STR2+"} */ default Constraint table(IntVar[] vars, Tuples tuples, String algo) { if (vars.length == 2) { table(vars[0], vars[1], tuples); } if(algo.contains("+") && !tuples.isFeasible()){ throw new SolverException(algo+" table algorithm cannot be used with forbidden tuples."); } Propagator p; switch (algo) { case "CT+": p = new PropCompactTable(vars, tuples); break; case "MDD+": p = new PropLargeMDDC(new MultivaluedDecisionDiagram(vars, tuples), vars); break; case "FC": p = new PropLargeFC(vars, tuples); break; case "GAC3rm": p = new PropLargeGAC3rm(vars, tuples); break; case "GAC2001": p = new PropLargeGAC2001(vars, tuples); break; case "GACSTR+": p = new PropLargeGACSTRPos(vars, tuples); break; case "GAC2001+": p = new PropLargeGAC2001Positive(vars, tuples); break; case "GAC3rm+": p = new PropLargeGAC3rmPositive(vars, tuples); break; case "STR2+": p = new PropTableStr2(vars, tuples); break; default: throw new SolverException("Table algorithm "+algo+" is unkown"); } return new Constraint("Table(" + algo + ")", p); } /** * Creates a tree constraint. * Partition succs variables into nbTrees (anti) arborescences *

succs[i] = j means that j is the successor of i. *

and succs[i] = i means that i is a root *

*

dominator-based filtering: Fages & Lorca (CP'11) *

However, the filtering over nbTrees is quite light here * * @param succs successors variables, taking their domain in [0,|succs|-1] * @param nbTrees number of arborescences (=number of loops) * @return a tree constraint */ default Constraint tree(IntVar[] succs, IntVar nbTrees) { return tree(succs, nbTrees, 0); } /** * Creates a tree constraint. * Partition succs variables into nbTrees (anti) arborescences *

succs[i] = offset+j means that j is the successor of i. *

and succs[i] = offset+i means that i is a root *

*

dominator-based filtering: Fages & Lorca (CP'11) *

However, the filtering over nbTrees is quite light here * * @param succs successors variables, taking their domain in [offset,|succs|-1+offset] * @param nbTrees number of arborescences (=number of loops) * @param offset 0 by default but 1 if used within MiniZinc * (which counts from 1 to n instead of from 0 to n-1) * @return a tree constraint */ default Constraint tree(IntVar[] succs, IntVar nbTrees, int offset) { return new Constraint("tree", new PropAntiArborescences(succs, offset, false), new PropKLoops(succs, offset, nbTrees) ); } /** * Get the list of values in the domains of vars * @param vars an array of integer variables * @return the list of values in the domains of vars */ default int[] getDomainUnion(IntVar... vars) { int m = vars[0].getLB(), M = vars[0].getUB(), j, k; for(int i = 1; i < vars.length; i++){ if(m > (k = vars[i].getLB())){ m = k; } if(M < (j = vars[i].getUB())){ M = j; } } BitSet values = new BitSet(M - m +1); for (IntVar v : vars) { int lb = v.getLB(); for (int i = v.getUB(); i >= lb; i = v.previousValue(i)) { values.set(i - m); } } int[] vs = new int[values.cardinality()]; k = 0; for(int i = values.nextSetBit(0); i >= 0; i = values.nextSetBit(i+1)){ vs[k++] = i + m; } return vs; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy