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

org.chocosolver.solver.constraints.extension.TuplesFactory Maven / Gradle / Ivy

There is a newer version: 4.10.17
Show newest version
/*
 * This file is part of choco-solver, http://choco-solver.org/
 *
 * Copyright (c) 2023, 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.solver.constraints.extension;

import org.chocosolver.solver.Settings;
import org.chocosolver.solver.constraints.Operator;
import org.chocosolver.solver.exception.SolverException;
import org.chocosolver.solver.variables.IntVar;

import java.util.Random;

import static org.chocosolver.util.tools.ArrayUtils.concat;

/**
 * A Factory to ease generation of tuples.
 * One may keep in mind that tuples generation directly depends on the product of domain cardinality, but also on the algorithm defines in the filter.
 * and thus may be time consuming!
 * 
* * @author Charles Prud'homme * @since 24/04/2014 */ public class TuplesFactory { TuplesFactory() { } /** * A method that generates all tuples from a set of variables and stores (and returns) the valid tuples wrt to the filter. * One may keep in mind that tuples generation directly depends on the product of domain cardinality, but also on the algorithm defines in the filter. * * @param filter tuple validator * @param feasible are tuples feasible (or infeasible) * @param doms domains * @return the valid tuples wrt to filter * @see source */ public static Tuples generateTuples(TupleValidator filter, boolean feasible, int[]... doms) { Tuples tuples = new Tuples(feasible); int n = doms.length; int[] t = new int[n]; int[] i = new int[n]; for (int j = 0; j < n; j++) { t[j] = doms[j][0]; } while (true) { if (filter.valid(t)) tuples.add(t.clone()); int j; for (j = 0; j < n; j++) { i[j]++; if (i[j] < doms[j].length) { t[j] = doms[j][i[j]]; break; } i[j] = 0; t[j] = doms[j][0]; } if (j == n) break; } return tuples; } /** * A method that generates all tuples from a set of variables and stores (and returns) the valid tuples wrt to the filter. * One may keep in mind that tuples generation directly depends on the product of domain cardinality, but also on the algorithm defines in the filter. * * @param filter tuple validator * @param feasible are tuples feasible (or infeasible) * @param vars concerned variables * @return the valid tuples wrt to filter */ public static Tuples generateTuples(TupleValidator filter, boolean feasible, IntVar... vars) { Tuples tuples = new Tuples(feasible); int n = vars.length; int[] cvalue = new int[n]; int[] t = new int[n]; for (int j = 0; j < n; j++) { t[j] = cvalue[j] = vars[j].getLB(); } while (true) { if (filter.valid(t)) tuples.add(t.clone()); int j; for (j = 0; j < n; j++) { int v = t[j] = cvalue[j] = vars[j].nextValue(cvalue[j]); if (v < Integer.MAX_VALUE) { break; } t[j] = cvalue[j] = vars[j].getLB(); } if (j == n) break; } return tuples; } /** * A method that randomly generates tuples from a set of variables. * * @param proba probability to keep a tuple in Tuples (0 < proba < 1) * @param random an instance of pseudorandom numbers streamer * @param vars concerned variables * @return the set of retain tuples, which can be empty * @implNote

* If proba <= 0, then the tuple list will be empty *

*

* If proba > 1, then all combination will be retained *

*

All combinations of instantiations of vars are generated * and for each of them, an instantiation is kept with probability p. *

*/ public static Tuples randomTuples(final double proba, final Random random, IntVar... vars) { return generateTuples(vs -> random.nextDouble() < proba, true,vars); } // BEWARE: PLEASE, keep signatures sorted by increasing arity and alphabetical order!! //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // BINARIES //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Generate valid tuples for absolute constraint: VAR1 = |VAR2| * * @return a Tuples object, reserved for a table constraint */ public static Tuples absolute(IntVar VAR1, IntVar VAR2) { return generateTuples(values -> values[0] == Math.abs(values[1]), true, VAR1, VAR2); } /** * Generate valid tuples for VAR1 OP VAR2, where OP in {"=", "!=", ">","<",">=","<="} * * @return a Tuples object, reserved for a table constraint */ public static Tuples arithm(IntVar VAR1, String OP, IntVar VAR2) { final Operator op = Operator.get(OP); return generateTuples(values -> { switch (op) { case LT: return values[0] < values[1]; case GT: return values[0] > values[1]; case LE: return values[0] <= values[1]; case GE: return values[0] >= values[1]; case NQ: return values[0] != values[1]; case EQ: return values[0] == values[1]; default: throw new SolverException("Unexpected Tuple operator " + op + " (should be in {\"=\", \"!=\", \">\",\"<\",\">=\",\"<=\"})"); } }, true, VAR1, VAR2); } /** * Generate valid tuples for an element constraint : TABLE[INDEX-OFFSET] = VALUE * * @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) * @return a Tuples object, reserved for a table constraint */ public static Tuples element(IntVar VALUE, int[] TABLE, IntVar INDEX, int OFFSET) { Tuples t = new Tuples(true); for (int v = INDEX.getLB(); v <= INDEX.getUB(); v = INDEX.nextValue(v)) { if (v - OFFSET >= 0 && v - OFFSET < TABLE.length && VALUE.contains(TABLE[v - OFFSET])) { t.add(TABLE[v - OFFSET], v); } } return t; } /** * Generate valid tuples for an element constraint : MATRIX[ROWINDEX-ROWOFFSET][COLINDEX-COLOFFSET] = VALUE * * @param VALUE an integer variable taking its value in MATRIX * @param MATRIX an array of integer values * @param ROWINDEX an integer variable representing the row of value of VALUE in MATRIX * @param ROWOFFSET offset matching ROWINDEX.LB and MATRIX[0] (Generally 0) * @param COLINDEX an integer variable representing the column of value of VALUE in MATRIX * @param COLOFFSET offset matching COLINDEX.LB and MATRIX[][0] (Generally 0) * @return a Tuples object, reserved for a table constraint */ public static Tuples element(IntVar VALUE, int[][] MATRIX, IntVar ROWINDEX, int ROWOFFSET, IntVar COLINDEX, int COLOFFSET) { Tuples t = new Tuples(true); for (int i = 0; i < MATRIX.length; i++) { for (int j = 0; j < MATRIX[i].length; j++) { if (ROWINDEX.contains(i - ROWOFFSET) && COLINDEX.contains(j - COLOFFSET) && VALUE.contains(MATRIX[i][j])) { t.add(i, j, MATRIX[i - -ROWOFFSET][j - COLOFFSET]); } } } return t; } /** * Generate valid tuples for minimum constraint: VAR1 % m = VAR2 * * @return a Tuples object, reserved for a table constraint */ public static Tuples modulo(IntVar VAR1, int m, IntVar VAR2) { return generateTuples(values -> values[1] == values[0] % m, true, VAR1, VAR2); } /** * Generate valid tuples for absolute constraint: VAR1 = VAR2^POWER * * @return a Tuples object, reserved for a table constraint */ public static Tuples power(IntVar VAR1, IntVar VAR2, final int POWER) { return generateTuples(values -> values[0] == Math.pow(values[1], POWER), true, VAR1, VAR2); } /** * Generate valid tuples for absolute constraint: VAR1 = VAR2^2 * * @return a Tuples object, reserved for a table constraint */ public static Tuples square(IntVar VAR1, IntVar VAR2) { return generateTuples(values -> values[0] == Math.pow(values[1], 2), true, VAR1, VAR2); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // TERNARIES //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Generate valid tuples for euclidean division constraint: DIVIDEND / DIVISOR = RESULT, rounding towards 0 * * @return a Tuples object, reserved for a table constraint */ public static Tuples eucl_div(IntVar DIVIDEND, IntVar DIVISOR, IntVar RESULT) { return generateTuples(values -> values[0] / values[1] == values[2], true, DIVIDEND, DIVISOR, RESULT); } /** * Generate valid tuples for minus constraint: MAX = max(VAR1,VAR2) * * @return a Tuples object, reserved for a table constraint */ public static Tuples maximum(IntVar VAR1, IntVar VAR2, IntVar MAX) { return generateTuples(values -> values[0] == Math.max(values[1], values[2]), true, MAX, VAR1, VAR2); } /** * Generate valid tuples for minimum constraint: MIN = min(VAR1,VAR2) * * @return a Tuples object, reserved for a table constraint */ public static Tuples minimum(IntVar VAR1, IntVar VAR2, IntVar MIN) { return generateTuples(values -> values[0] == Math.min(values[1], values[2]), true, MIN, VAR1, VAR2); } /** * Generate valid tuples for minimum constraint: VAR1 % VAR2 = RES * * @return a Tuples object, reserved for a table constraint */ public static Tuples modulo(IntVar VAR1, IntVar VAR2, IntVar RES) { return generateTuples(values -> values[1] != 0 && values[2] == values[0] % values[1], true, VAR1, VAR2, RES); } /** * Generate valid tuples for minus constraint: VAR1 - VAR2 = RESULT * * @return a Tuples object, reserved for a table constraint */ public static Tuples minus(IntVar VAR1, IntVar VAR2, IntVar RESULT) { return generateTuples(values -> values[0] - values[1] == values[2], true, VAR1, VAR2, RESULT); } /** * Generate valid tuples for plus constraint: VAR1 + VAR2 = RESULT * * @return a Tuples object, reserved for a table constraint */ public static Tuples plus(IntVar VAR1, IntVar VAR2, IntVar RESULT) { return generateTuples(values -> values[0] + values[1] == values[2], true, VAR1, VAR2, RESULT); } /** * Generate valid tuples for times constraint: VAR1 * VAR2 = RESULT * * @return a Tuples object, reserved for a table constraint */ public static Tuples times(IntVar VAR1, IntVar VAR2, IntVar RESULT) { return generateTuples(values -> values[0] * values[1] == values[2], true, VAR1, VAR2, RESULT); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // NARIES //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Generate valid tuples for allDifferent constraint * * @param VARS concerned variables * @return a Tuples object, reserved for a table constraint */ public static Tuples allDifferent(IntVar... VARS) { return generateTuples(values -> { for (int i = 0; i < values.length - 1; i++) { for (int j = i + 1; j < values.length; j++) { if (values[j] == values[i]) return false; } } return true; }, true, VARS); } /** * Generate valid tuples for allEquals constraint * * @param VARS concerned variables * @return a Tuples object, reserved for a table constraint */ public static Tuples allEquals(IntVar... VARS) { int min = Integer.MIN_VALUE; int max = Integer.MAX_VALUE; for (int i = 0; i < VARS.length; i++) { min = Math.max(min, VARS[i].getLB()); max = Math.min(max, VARS[i].getUB()); } Tuples tuples = new Tuples(true); for (int k = min; k <= max; k++) { int[] t = new int[VARS.length]; for (int i = 0; i < VARS.length; i++) { t[i] = k; } tuples.add(t.clone()); } return tuples; /*return generateTuples(new TupleValidator() { @Override public boolean valid(int... values) { for (int i = 0; i < values.length - 1; i++) { if (values[i] != values[i + 1]) return false; } return true; } }, true, VARS);*/ } /** * Generate valid tuples for lexChainLess constraint * * @param VARS concerned variables * @return a Tuples object, reserved for a table constraint */ public static Tuples lex_chain_less(IntVar... VARS) { return generateTuples(values -> { for (int i = 0; i < values.length - 1; i++) { if (values[i] < values[i + 1]) return false; } return true; }, true, VARS); } /** * Generate valid tuples for lexChainLessEq constraint * * @param VARS concerned variables * @return a Tuples object, reserved for a table constraint */ public static Tuples lex_chain_less_eq(IntVar... VARS) { return generateTuples(values -> { for (int i = 0; i < values.length - 1; i++) { if (values[i] <= values[i + 1]) return false; } return true; }, true, VARS); } /** * Generate valid tuples for ∑i in |VARS|VARSi*COEFFSi OPERATOR SCALAR * * @param VARS concerned variables * @return a Tuples object, reserved for a table constraint */ public static Tuples scalar(IntVar[] VARS, final int[] COEFFS, IntVar SCALAR, final int SCALAR_COEFF) { Tuples left = generateTuples(TupleValidator.TRUE, true, VARS); Tuples tuples = new Tuples(true); int n = VARS.length; for (int[] tleft : left.tuples) { int right = 0; for (int i = 0; i < n; i++) { right += tleft[i] * COEFFS[i]; } if (right % SCALAR_COEFF == 0 && SCALAR.contains(right / SCALAR_COEFF)) { int[] t = new int[n + 1]; System.arraycopy(tleft, 0, t, 0, n); t[n] = right / SCALAR_COEFF; tuples.add(t); } } return tuples; } /** * Generate valid tuples for ∑i in |VARS|VARSi*COEFFSi OPERATOR SCALAR + CSTE *

* with OPERATOR in {"=", "!=", ">","<",">=","<="} * * @param VARS concerned variables * @return a Tuples object, reserved for a table constraint */ public static Tuples scalar(IntVar[] VARS, final int[] COEFFS, final String OPERATOR, IntVar SCALAR, final int SCALAR_COEFF, int CSTE) { if ("=".equals(OPERATOR) && CSTE == 0) { return scalar(VARS, COEFFS, SCALAR, SCALAR_COEFF); } final Operator op = Operator.get(OPERATOR); return generateTuples(values -> { int scalar = 0; for (int i = 0; i < values.length - 1; i++) { scalar += values[i] * COEFFS[i]; } switch (op) { case LT: return scalar < values[values.length - 1] * SCALAR_COEFF + CSTE; case GT: return scalar > values[values.length - 1] * SCALAR_COEFF + CSTE; case LE: return scalar <= values[values.length - 1] * SCALAR_COEFF + CSTE; case GE: return scalar >= values[values.length - 1] * SCALAR_COEFF + CSTE; case NQ: return scalar != values[values.length - 1] * SCALAR_COEFF + CSTE; case EQ: return scalar == values[values.length - 1] * SCALAR_COEFF + CSTE; default: throw new SolverException("Unexpected Tuple operator " + op + " (should be in {\"=\", \"!=\", \">\",\"<\",\">=\",\"<=\"})"); } }, true, concat(VARS, SCALAR)); } /** * Generate valid tuples for ∑i in |VARS|VARSi OPERATOR SUM + CSTE *

* with OPERATOR in {"=", "!=", ">","<",">=","<="} * * @param VARS concerned variables * @return a Tuples object, reserved for a table constraint */ public static Tuples sum(IntVar[] VARS, final String OPERATOR, IntVar SUM, int CSTE) { final Operator op = Operator.get(OPERATOR); return generateTuples(values -> { int sum = 0; for (int i = 0; i < values.length - 1; i++) { sum += values[i]; } switch (op) { case LT: return sum < values[values.length - 1] + CSTE; case GT: return sum > values[values.length - 1] + CSTE; case LE: return sum <= values[values.length - 1] + CSTE; case GE: return sum >= values[values.length - 1] + CSTE; case NQ: return sum != values[values.length - 1] + CSTE; case EQ: return sum == values[values.length - 1] + CSTE; default: throw new SolverException("Unexpected Tuple operator " + op + " (should be in {\"=\", \"!=\", \">\",\"<\",\">=\",\"<=\"})"); } }, true, concat(VARS, SUM)); } /** * Check whether the intension constraint to extension constraint substitution is enabled and can be achieved * * @param VARS list of variables involved * @return a boolean */ public static boolean canBeTupled(IntVar... VARS) { Settings settings = VARS[0].getModel().getSettings(); if (!settings.enableTableSubstitution()) { return false; } long doms = 1; for (int i = 0; i < VARS.length && doms < settings.getMaxTupleSizeForSubstitution(); i++) { if (!VARS[i].hasEnumeratedDomain()) { return false; } doms *= VARS[i].getDomainSize(); } return (doms < settings.getMaxTupleSizeForSubstitution()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy