diff --git a/.github/workflows/maven-test.yml b/.github/workflows/maven-test.yml
index 2653d6dce5..87d70dcefb 100644
--- a/.github/workflows/maven-test.yml
+++ b/.github/workflows/maven-test.yml
@@ -7,7 +7,7 @@ on: [push, pull_request]
jobs:
test-ibex:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-latest
# The different steps
steps:
- uses: actions/checkout@v4
@@ -27,7 +27,9 @@ jobs:
# Regression tests
- name: Test Ibex
- run: mvn --file pom.xml test -Pcoverage -DtestFailureIgnore=true -Dgroups=ibex
+ run: |
+ export LD_LIBRARY_PATH=/usr/local/lib:/usr/local/lib/ibex/3rd
+ mvn --file pom.xml test -Pcoverage -DtestFailureIgnore=true -Dgroups=ibex
test-solver:
runs-on: ubuntu-latest
diff --git a/CHANGES.md b/CHANGES.md
index c20e3a6658..3afb740357 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -14,13 +14,19 @@ NEXT MILESTONE
### Other closed issues and pull requests:
See [milestone 5.0.0-beta.2](https://github.com/chocoteam/choco-solver/milestone/41)
+- Performance improvement in PropXPlusYEqZ and PropAbsolute
- Fix bug in ConflictOrderingSearch (monitor not plugged)
- Performance improvement for LastConflict, ConflictOrderingSearch and StrategySequencer.
- Fix bug : regular constraints parsed from XCSP now support negative values
+- Fix bug : Unexpected behavior when using Task with scalar (#1114)
+- Replace Cumulative implementation by state-of-the-art implementation of TimeTabling and OverloadChecking
+- Fix bug : IntVar no longer extends Iterable to avoid errors in nested loops
+- Fix bug : Fix iterator issue when using ISet, especially within graph variables (#1174 and #1175)
#### Contributors to this release:
- [Charles Prud'homme](https://github.com/cprudhom) (@cprudhom)
- [Jean-Guillaume Fages](https://github.com/jgFages) (@jgFages)
+- [Arthur Godet](https://github.com/ArthurGodet) (@ArthurGodet)
**Full Changelog**: https://github.com/chocoteam/choco-solver/compare/v5.0.0-beta.1...v5.0.0-beta.2
@@ -70,23 +76,6 @@ See [milestone 5.0.0](https://github.com/chocoteam/choco-solver/milestone/40)
**Full Changelog**: https://github.com/chocoteam/choco-solver/compare/v4.10.17...v4.10.18
-4.10.17 - 23 Sep 2024
- -------------------
-
- ### Hotfix
- - Fix bug in `PropHybridTable` (#1102)
-
- **Full Changelog**: https://github.com/chocoteam/choco-solver/compare/v4.10.16...v4.10.17
-
-4.10.16 - 12 Sep 2024
--------------------
-
-### Hotfix
-- Fix bug in `IntAffineView` (#1101)
-
-**Full Changelog**: https://github.com/chocoteam/choco-solver/compare/v4.10.15...v4.10.16
-
-
4.10.17 - 23 Sep 2024
-------------------
diff --git a/Makefile b/Makefile
index 666129168f..fea79341cb 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
# like running test suites
ROOT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
-PRETTY_DATE := $(shell date +'%m/%d/%Y %H:%M')
+PRETTY_DATE := $(shell date +'%Y-%m-%dT%H:%M')
DATE := $(shell date +'%y%m%d_%H%M')
CURRENT_VERSION := $(shell mvn help:evaluate -Dexpression=project.version | grep -v "\[INFO\]" | grep -v "\[WARNING\]")
@@ -53,8 +53,8 @@ tests : 1s 10s ibex checker mzn xcsp mps dimacs expl lcg
mvn test -DtestFailureIgnore=true -Dgroups="$@"
update_date:
- @sed -i '' 's|\s*System.out.printf("c Choco.*|System.out.printf("c Choco%s ($(PRETTY_DATE))\\n", lcg? " with LCG" : "");|' parsers/src/main/java/org/chocosolver/parser/xcsp/XCSP.java
- @sed -i '' 's|\s*System.out.printf("%% Choco%s (.*|System.out.printf("%% Choco%s ($(PRETTY_DATE))\\n", lcg? " with LCG" : "");|' parsers/src/main/java/org/chocosolver/parser/flatzinc/Flatzinc.java
+ @sed -i '' 's|\s*System.out.printf("c Choco.*|System.out.printf("c Choco-solver%s ($(CURRENT_VERSION), $(PRETTY_DATE))\\n", lcg? " with LCG" : "");|' parsers/src/main/java/org/chocosolver/parser/xcsp/XCSP.java
+ @sed -i '' 's|\s*System.out.printf("%% Choco.*|System.out.printf("%% Choco-solver%s ($(CURRENT_VERSION), $(PRETTY_DATE))\\n", lcg? " with LCG" : "");|' parsers/src/main/java/org/chocosolver/parser/flatzinc/Flatzinc.java
compet: update_date clean package
@@ -70,6 +70,11 @@ delmsc:
@rm ~/.minizinc/solvers/choco-$(VERSION).msc
docker: compet
+ # add an image with LCG support
+ @sed -i '' 's|python3.*|python3 "$$(dirname "$$0")/fzn-choco.py" "$$@" "-lcg"|' parsers/src/main/minizinc/fzn-choco.sh
+ @docker build -f $(ROOT_DIR)/parsers/src/main/minizinc/docker/Dockerfile.dms -t chocoteam/choco-solver-mzn:$(CURRENT_VERSION)-X $(ROOT_DIR)
+ # add an image without LCG support
+ @sed -i '' 's|python3.*|python3 "$$(dirname "$$0")/fzn-choco.py" "$$@"|' parsers/src/main/minizinc/fzn-choco.sh
@docker build -f $(ROOT_DIR)/parsers/src/main/minizinc/docker/Dockerfile.dms -t chocoteam/choco-solver-mzn:$(CURRENT_VERSION) $(ROOT_DIR)
antlr:
diff --git a/examples/src/test/java/org/chocosolver/examples/integer/CostasArraysTest.java b/examples/src/test/java/org/chocosolver/examples/integer/CostasArraysTest.java
index 1279f65f8d..2da5c75508 100644
--- a/examples/src/test/java/org/chocosolver/examples/integer/CostasArraysTest.java
+++ b/examples/src/test/java/org/chocosolver/examples/integer/CostasArraysTest.java
@@ -36,7 +36,7 @@ public void test(){
CostasArrays ca = new CostasArrays();
ca.execute();
Assert.assertEquals(ca.getModel().getSolver().getSolutionCount(), 1);
- Assert.assertEquals(ca.getModel().getSolver().getNodeCount(), 6539);
+ Assert.assertEquals(ca.getModel().getSolver().getNodeCount(), 8_987);
}
@Test(groups = "10s", timeOut = 60000)
diff --git a/examples/src/test/java/org/chocosolver/examples/integer/KnapsackTest.java b/examples/src/test/java/org/chocosolver/examples/integer/KnapsackTest.java
index a71c669738..de6c3e89af 100644
--- a/examples/src/test/java/org/chocosolver/examples/integer/KnapsackTest.java
+++ b/examples/src/test/java/org/chocosolver/examples/integer/KnapsackTest.java
@@ -131,7 +131,7 @@ public void testOPT13() throws IOException, SetUpException {
ks.solveIt(s, true);
Assert.assertEquals(s.getSolver().getBestSolutionValue().intValue(), 2657, "obj val");
Assert.assertEquals(s.getSolver().getSolutionCount(), 3, "nb sol");
- Assert.assertEquals(s.getSolver().getNodeCount(), 60, "nb nod");
+ Assert.assertEquals(s.getSolver().getNodeCount(), 59, "nb nod");
}
@Test(groups = "1s", timeOut = 60000)
diff --git a/parsers/pom.xml b/parsers/pom.xml
index fb8b05f8d1..b84979cc2f 100644
--- a/parsers/pom.xml
+++ b/parsers/pom.xml
@@ -44,7 +44,7 @@
* Adds a model to the list of models to run in parallel.
- * The model can either be a fresh one, ready for populating, or a populated one.
+ * The model can either be fresh, ready for populating, or a populated one.
*
* Important:
*
*
@@ -26,7 +26,7 @@ public abstract class Reason implements ICause {
*/
static final Clause UNDEF = new Clause(new int[]{0});
/**
- * A thread-local clause to explain a propagation with one literal.
+ * A thread-local clause to explain a modification with one literal.
* This clause is static and can be reused in the same thread.
*/
private final static ThreadLocal
*
+ * This constraint ensures that all variables from vars are pairwise different, except those which are in except. + *
+ * This decomposition is based on the simple arithmetic reasoning. + * + * @param vars array of integer variables + * @param except array of values that can be excluded from the allDifferent constraint + */ + default void allDifferentExceptDec(IntVar[] vars, int[] except) { + BoolVar[] bs = new BoolVar[vars.length]; + IntIterableRangeSet set = new IntIterableRangeSet(except); + for (int i = 0; i < vars.length; i++) { + bs[i] = ref().boolVar(); + ref().reifyXnotinS(vars[i], set, bs[i]); + } + for (int i = 0; i < vars.length - 1; i++) { + for (int j = i + 1; j < vars.length; j++) { + ref().impXrelYC(vars[i], "!=", vars[j], 0, bs[i].and(bs[j]).boolVar()); + } + } + } + + /** + * Creates and posts a decomposition of an allDifferent_except constraint. + *+ * This constraint ensures that all variables from vars are pairwise different, except those which are in except. + *
+ * This decomposition is based on the global cardinality constraint. + * + * @param vars array of integer variables + * @param except array of values that can be excluded from the allDifferent constraint + */ + default void allDifferentExceptDecWithGCC(IntVar[] vars, int[] except) { + IntIterableRangeSet values = new IntIterableRangeSet(); + for (IntVar var : vars) { + values.addAll(var); + } + values.addAll(except); + IntIterableRangeSet sexcept = new IntIterableRangeSet(except); + int[] valuesArray = values.toArray(); + IntVar[] occurrences = new IntVar[valuesArray.length]; + for (int i = 0; i < valuesArray.length; i++) { + occurrences[i] = ref().intVar(0, sexcept.contains(valuesArray[i]) ? vars.length : 1); + } + ref().globalCardinality(vars, valuesArray, occurrences, true).post(); + } + /** * Posts a decomposition of an among constraint. * nbVar is the number of variables of the collection vars that take their value in values. @@ -81,7 +132,7 @@ default void amongDec(IntVar nbVar, IntVar[] vars, IntVar[] values) { * @param durations processing time of each task * @param heights resource consumption of each task * @param capacity resource capacity - * @see org.chocosolver.solver.constraints.IIntConstraintFactory#cumulative(IntVar[], int[], + * @see org.chocosolver.solver.constraints.ISchedulingFactory#cumulative(IntVar[], int[], * int[], int) */ default void cumulativeTimeDec(IntVar[] starts, int[] durations, int[] heights, int capacity) { @@ -119,7 +170,7 @@ default void cumulativeTimeDec(IntVar[] starts, int[] durations, int[] heights, * @param tasks set of tasks * @param heights resource consumption of each task * @param capacity resource capacity - * @see org.chocosolver.solver.constraints.IIntConstraintFactory#cumulative(IntVar[], int[], + * @see org.chocosolver.solver.constraints.ISchedulingFactory#cumulative(IntVar[], int[], * int[], int) */ default void cumulativeDec(Task[] tasks, IntVar[] heights, IntVar capacity) { @@ -139,7 +190,7 @@ default void cumulativeDec(Task[] tasks, IntVar[] heights, IntVar capacity) { * @param durations processing time of each task * @param heights resource consumption of each task * @param capacity resource capacity - * @see org.chocosolver.solver.constraints.IIntConstraintFactory#cumulative(IntVar[], int[], + * @see org.chocosolver.solver.constraints.ISchedulingFactory#cumulative(IntVar[], int[], * int[], int) */ default void cumulativeDec(IntVar[] starts, IntVar[] durations, IntVar[] heights, IntVar capacity) { @@ -187,6 +238,32 @@ default void cumulativeDec(IntVar[] starts, IntVar[] durations, IntVar[] heights } + /** + * Creates a decomposition of the disjunctive constraint. + * This constraint ensures that no two tasks overlap. + *
+ * If a task is optional, it can be performed with a duration of 0 and the other mandatory tasks can overlap it.
+ * Otherwise, the task must be performed and cannot be overlapped by any other task even if it has a duration of 0.
+ *
+ * @param tasks array of tasks
+ * @param strict set to true to prevent overlapping even if the duration is equal to 0
+ */
+ default void disjunctiveDec(Task[] tasks, boolean strict) {
+ // all durations must be positive or zero
+ for (int i = 0; i < tasks.length; i++) {
+ tasks[i].getDuration().ge(0).post();
+ BoolVar di0 = strict ? ref().boolVar(false) : ref().isEq(tasks[i].getDuration(), 0);
+ for (int j = i + 1; j < tasks.length; j++) {
+ BoolVar dj0 = strict ? ref().boolVar(false) : ref().isEq(tasks[j].getDuration(), 0);
+ BoolVar ibeforej = ref().scalar(new IntVar[]{tasks[i].getStart(), tasks[i].getDuration(), tasks[j].getStart()},
+ new int[]{1, 1, -1}, "<=", 0).reify();
+ BoolVar jbeforei = ref().scalar(new IntVar[]{tasks[j].getStart(), tasks[j].getDuration(), tasks[i].getStart()},
+ new int[]{1, 1, -1}, "<=", 0).reify();
+ ref().addClauses(new BoolVar[]{di0, dj0, ibeforej, jbeforei}, new BoolVar[]{});
+ }
+ }
+ }
+
/**
* Creates an element constraint: value = matrix[rowIndex-offset][colIndex-colOffset]
*
@@ -204,7 +281,8 @@ default void element(IntVar value, int[][] matrix, IntVar rowIndex, int rowOffse
IntVar rv = ref().intView(1, rowIndex, -rowOffset);
IntVar cv = ref().intView(1, colIndex, -colOffset);
IntVar idx = ref().intVar(0, d0 * d1 - 1);
- ref().scalar(new IntVar[]{rv, cv}, new int[]{d1, 1}, "=", idx).post();
+ ref().table(new IntVar[]{rv, cv, idx},
+ TuplesFactory.scalar(new IntVar[]{rv, cv}, new int[]{d1, 1}, idx, 1)).post();
// flatten the array
int[] mvars = ArrayUtils.flatten(matrix);
// post the element constraint
@@ -359,7 +437,7 @@ default void binPackingDec(IntVar[] bin, int[] w, IntVar[] load, int offset) {
default void circuitDec(IntVar[] S, int offset) {
int n = S.length;
ref().allDifferent(S, "AC").post();
- IntVar[] t = ref().intVarArray("t", n - 1, offset + 1, n + offset -1 );
+ IntVar[] t = ref().intVarArray("t", n - 1, offset + 1, n + offset - 1);
ref().allDifferent(t, "AC").post();
ref().arithm(t[0], "=", S[0]).post();
for (int i = 1; i < n - 2; i++) {
@@ -433,9 +511,8 @@ default void argminDec(IntVar z, int offset, IntVar[] vars) {
* @implNote This is encoded thanks to a table constraint.
*/
default void ifThenElseDec(BoolVar[] c, int[] x, IntVar y) {
- Tuples tuples = new Tuples();
int star = Math.max(2, y.getUB() + 1);
- tuples.setUniversalValue(star);
+ Tuples tuples = new Tuples(star);
int[] t = new int[c.length + 1];
Arrays.fill(t, 0);
t[c.length] = star;
@@ -478,9 +555,8 @@ default void ifThenElseDec(BoolVar[] c, IntVar[] x, IntVar y) {
//y.eq(x[i]).decompose().impliedBy(c[i].and(d[i]).boolVar());
c[i].and(d[i]).imp(y.eq(x[i])).post();
}/*/
- Tuples tuples = new Tuples();
- int univ = Math.max(2, y.getUB() + 1);
- tuples.setUniversalValue(univ);
+ int univ = Math.min(0, Stream.of(c).mapToInt(IntVar::getLB).min().orElse(0)) - 1;
+ Tuples tuples = new Tuples(univ);
int[] t = new int[c.length + 1];
Arrays.fill(t, 0);
t[c.length] = c.length;
diff --git a/solver/src/main/java/org/chocosolver/solver/constraints/IIntConstraintFactory.java b/solver/src/main/java/org/chocosolver/solver/constraints/IIntConstraintFactory.java
index a74a6a73fe..852de3e744 100644
--- a/solver/src/main/java/org/chocosolver/solver/constraints/IIntConstraintFactory.java
+++ b/solver/src/main/java/org/chocosolver/solver/constraints/IIntConstraintFactory.java
@@ -14,6 +14,7 @@
import gnu.trove.set.hash.TIntHashSet;
import org.chocosolver.solver.ISelf;
import org.chocosolver.solver.Model;
+import org.chocosolver.solver.Settings;
import org.chocosolver.solver.constraints.binary.*;
import org.chocosolver.solver.constraints.binary.element.ElementFactory;
import org.chocosolver.solver.constraints.extension.Tuples;
@@ -44,8 +45,6 @@
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.CumulFilter;
-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.knapsack.PropKnapsack;
@@ -82,7 +81,6 @@
import java.util.Arrays;
import java.util.BitSet;
import java.util.List;
-import java.util.stream.IntStream;
import java.util.stream.Stream;
import static java.lang.Math.abs;
@@ -223,7 +221,26 @@ default Constraint notMember(IntVar var, IntIterableRangeSet set) {
*/
default Constraint absolute(IntVar var1, IntVar var2) {
assert var1.getModel() == var2.getModel();
- return new Constraint(ConstraintsName.ABSOLUTE, new PropAbsolute(var1, var2));
+ if (var2.isInstantiated()) {
+ if (var2.getValue() < 0) {
+ return arithm(var1, "=", -var2.getValue());
+ } else {
+ return arithm(var1, "=", var2.getValue());
+ }
+ } else if (var1.isInstantiated()) {
+ if (var1.getValue() == 0) {
+ var2.eq(0).post();
+ } else if (var1.getValue() > 0) {
+ return member(var2, new int[]{-var1.getValue(), var1.getValue()});
+ } else {
+ return ref().falseConstraint();
+ }
+ }
+ return new Constraint(ConstraintsName.ABSOLUTE,
+ ref().getSolver().isLCG() ?
+ new PropAbsoluteLight(var1, var2) :
+ new PropAbsolute(var1, var2)
+ );
}
/**
@@ -432,15 +449,15 @@ default Constraint square(IntVar var1, IntVar var2) {
* @param var2 second variable
*/
default Constraint table(IntVar var1, IntVar var2, Tuples tuples) {
- if (!var1.hasEnumeratedDomain() || !var2.hasEnumeratedDomain()) {
- return table(var1, var2, tuples, "CT+");
- } else {
- return table(var1, var2, tuples, "AC3bit+rm");
- }
+ String algo = "CT";
+ return table(var1, var2, tuples, algo);
}
/**
* Creates a table constraint over a couple of variables var1 and var2:
+ *
+ * - CT: table constraint which applies the Compact-Table algorithm,
- * 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.make(tasks.length));
- }
-
- /**
- * 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) {
- return cumulative(tasks, heights, capacity, incremental, Arrays.stream(filters).map(f -> f.make(tasks.length)).toArray(CumulFilter[]::new));
- }
-
- /**
- * 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, CumulFilter... filters) {
- if (ref().getSolver().isLCG()) {
- if (ref().getSettings().warnUser()) {
- ref().getSolver().log().white().println(
- "Warning: cumulative constraint is decomposed (due to LCG).");
- }
- ref().cumulativeDec(tasks, heights, capacity);
- return ref().voidConstraint();
- }
- if (tasks.length != heights.length) {
- throw new SolverException("Tasks and heights arrays should have same size");
- }
- 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 and posts a decomposition of 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 starts starting time of each task
- * @param durations processing time of each task
- * @param heights resource consumption of each task
- * @param capacity resource capacity
- */
- default Constraint cumulative(IntVar[] starts, int[] durations, int[] heights, int capacity) {
- int n = starts.length;
- final IntVar[] d = new IntVar[n];
- final IntVar[] h = new IntVar[n];
- final IntVar[] e = new IntVar[n];
- Task[] tasks = new Task[n];
- for (int i = 0; i < n; i++) {
- d[i] = ref().intVar(durations[i]);
- h[i] = ref().intVar(heights[i]);
- e[i] = ref().intVar(starts[i].getName() + "_e",
- starts[i].getLB() + durations[i],
- starts[i].getUB() + durations[i],
- true);
- tasks[i] = new Task(starts[i], d[i], e[i]);
- }
- return ref().cumulative(tasks, h, ref().intVar(capacity), false, Cumulative.Filter.NAIVETIME);
- }
-
/**
*
* Create a decreasing constraint which ensures that the variables in {@code vars} are decreasing.
@@ -1647,9 +1578,9 @@ default Constraint diffN(IntVar[] X, IntVar[] Y, IntVar[] width, IntVar[] height
return Constraint.merge(ConstraintsName.DIFFNWITHCUMULATIVE,
diffNCons,
min(minX, X), max(maxX, EX), scalar(new IntVar[]{maxX, minX}, new int[]{1, -1}, "=", diffX),
- cumulative(TX, height, diffY),
+ model.cumulative(TX, height, diffY),
min(minY, Y), max(maxY, EY), scalar(new IntVar[]{maxY, minY}, new int[]{1, -1}, "=", diffY),
- cumulative(TY, width, diffX)
+ model.cumulative(TY, width, diffX)
);
} else {
return diffNCons;
@@ -1845,9 +1776,9 @@ default Constraint inverseChanneling(IntVar[] vars1, IntVar[] vars2, int offset1
}
Propagator
- * Default configuration with GACSTR+ algorithm for feasible tuples and GAC3rm otherwise
+ * Default configuration is "CT" if memory consumption is not too high, "STR2+" otherwise.
*
* @param vars variables forming the tuples
* @param tuples the relation between the variables (list of allowed/forbidden tuples)
+ * @see Settings#getMaxSizeInMBToUseCompactTable()
+ * @see Settings#setMaxSizeInMBToUseCompactTable(int)
*/
default Constraint table(IntVar[] vars, Tuples tuples) {
- String algo = "GAC3rm";
- if (tuples.isFeasible()) {
- //noinspection OptionalGetWithoutIsPresent
- if (tuples.nbTuples() > 512 &&
- (IntStream.range(0, vars.length)
- .map(i -> tuples.max(i) - tuples.min(i))
- .max().getAsInt()) < 512) {
- algo = "CT+";
- } else if (tuples.allowUniversalValue()) {
- // STR2+ or CT+, depending on dom size
+ String algo = "CT";
+ long estimatedMem = 0;
+ long maxMem = ref().getSettings().getMaxSizeInMBToUseCompactTable();
+ for (int i = 0; i < vars.length && tuples.isFeasible(); i++) {
+ // on word encoded 64 bits on a long (of 64 bits)
+ estimatedMem += ((tuples.nbTuples() / 64L) * vars[i].getRange()) * 64 / 1024 / 1024; // in MB
+ if (estimatedMem < 0 || estimatedMem > maxMem) {
algo = "STR2+";
- } else {
- algo = "GACSTR+";
+ break;
}
}
return table(vars, tuples, algo);
@@ -2743,7 +2672,10 @@ default Constraint table(IntVar[] vars, Tuples tuples) {
/**
* Creates a table constraint, with the specified algorithm defined algo
*
- * - CT+: Compact-Table algorithm (AC),
+ * - CT: Compact-Table algorithm (AC),
+ *
+ * - CT+: Compact-Table algorithm for allowed tuples only (AC),
*
+ * heights 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(final List
+ * heights 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(final Task[] tasks, final IntVar[] heights, final IntVar capacity) {
+ return cumulative(tasks, heights, capacity, false, false);
+ }
+
+ /**
+ * 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.
+ *
+ * heights 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 energyNaive whether to apply the naive energy filtering
+ * @param disjunctiveEnergyNaive whether to apply the naive disjunctive energy filtering
+ * @return a cumulative constraint
+ */
+ default Constraint cumulative(
+ final Task[] tasks,
+ final IntVar[] heights,
+ final IntVar capacity,
+ final boolean energyNaive,
+ final boolean disjunctiveEnergyNaive
+ ) {
+ if (tasks.length != heights.length) {
+ throw new SolverException("Tasks and heights arrays should have same size");
+ }
+ // keep only tasks that can have impact on resource consumption
+ final List
+ * Task duration and height should be >= 0
+ * Discards tasks whose duration or height is equal to zero
+ *
+ * @param starts starting time of each task
+ * @param durations processing time of each task
+ * @param heights resource consumption of each task
+ * @param capacity resource capacity
+ */
+ default Constraint cumulative(final IntVar[] starts, final int[] durations, final int[] heights, final int capacity) {
+ int n = starts.length;
+ final IntVar[] d = new IntVar[n];
+ final IntVar[] h = new IntVar[n];
+ final IntVar[] e = new IntVar[n];
+ Task[] tasks = new Task[n];
+ for (int i = 0; i < n; i++) {
+ d[i] = ref().intVar(durations[i]);
+ h[i] = ref().intVar(heights[i]);
+ e[i] = ref().intVar(starts[i].getName() + "_e",
+ starts[i].getLB() + durations[i],
+ starts[i].getUB() + durations[i],
+ true);
+ tasks[i] = ref().taskVar(starts[i], d[i], e[i]);
+ }
+ return cumulative(tasks, h, ref().intVar(capacity));
+ }
+
+ /////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////// SEARCH ///////////////////////////////////
+ /////////////////////////////////////////////////////////////////////////////
+
+ static IntVar[] extractVars(final Task[] tasks, final Function
+ * Beware that the search is not complete in general, as stated in the paper.
+ *
+ * @param tasks the tasks
+ * @return the strategy
+ */
+ default IntStrategy setTimes(final Task[] tasks) {
+ SetTimes setTimes = new SetTimes(tasks);
+ ref().post(new Constraint("SET_TIMES", setTimes));
+ return Search.intVarSearch(setTimes, new IntDomainMin(), extractStartVars(tasks));
+ }
+
+ enum ArbitrationRule {
+ MIN_EST, MAX_EST, MIN_LST, MAX_LST, MIN_ECT, MAX_ECT, MIN_LCT, MAX_LCT
+ }
+
+ /**
+ * Returns true if the first task is before the second task according to the rule.
+ * If the rule is MIN_EST(0), then returns task1.est < task2.est
+ * If the rule is MAX_EST(1), then returns task1.est > task2.est
+ * If the rule is MIN_LST(2), then returns task1.lst < task2.lst
+ * If the rule is MAX_LST(3), then returns task1.lst > task2.lst
+ * If the rule is MIN_ECT(4), then returns task1.eet < task2.eet
+ * If the rule is MAX_ECT(5), then returns task1.eet > task2.eet
+ * If the rule is MIN_LCT(6), then returns task1.let < task2.let
+ * If the rule is MAX_LCT(7), then returns task1.let > task2.let
+ *
+ * @param task1 the first task
+ * @param task2 the second task
+ * @param rule the rule
+ * @return true iff task1 is before task2 according to the rule
+ */
+ default boolean before(final Task task1, final Task task2, final ArbitrationRule rule) {
+ switch (rule) {
+ case MIN_EST:
+ return task1.getStart().getLB() < task2.getStart().getLB();
+ case MAX_EST:
+ return task1.getStart().getLB() > task2.getStart().getLB();
+ case MIN_LST:
+ return task1.getStart().getUB() < task2.getStart().getUB();
+ case MAX_LST:
+ return task1.getStart().getUB() > task2.getStart().getUB();
+ case MIN_ECT:
+ return task1.getEnd().getLB() < task2.getEnd().getLB();
+ case MAX_ECT:
+ return task1.getEnd().getLB() > task2.getEnd().getLB();
+ case MIN_LCT:
+ return task1.getEnd().getUB() < task2.getEnd().getUB();
+ case MAX_LCT:
+ return task1.getEnd().getUB() > task2.getEnd().getUB();
+ default:
+ throw new UnsupportedOperationException("Task comparison should be either MIN_EST(0), MAX_EST(1), MIN_LST(2), MAX_LST(3), MIN_EET(4), MAX_EET(5), MIN_LET(6) or MAX_LET(7).");
+ }
+ }
+
+ /**
+ * Builds a strategy that selects the task's start with the smallest lower bound (in case of equality, it selects the task with the smallest end's lower bound).
+ *
+ * @param tasks the tasks
+ * @return the strategy
+ */
+ default IntStrategy smallest(final Task[] tasks) {
+ return smallest(tasks, ArbitrationRule.MIN_ECT);
+ }
+
+ default IntStrategy smallest(final Task[] tasks, final ArbitrationRule arbitrationRule) {
+ IntVar[] starts = extractStartVars(tasks);
+ return Search.intVarSearch(
+ variables -> {
+ int idx = -1;
+ for (int i = 0; i < tasks.length; i++) {
+ if (!starts[i].isInstantiated()) {
+ if (idx == -1
+ || starts[i].getLB() < starts[idx].getLB()
+ || starts[i].getLB() == starts[idx].getLB() && before(tasks[i], tasks[idx], arbitrationRule)
+ ) {
+ idx = i;
+ }
+ }
+ }
+ return idx == -1 ? null : starts[idx];
+ },
+ new IntDomainMin(),
+ starts
+ );
+ }
+}
diff --git a/solver/src/main/java/org/chocosolver/solver/constraints/ImpliedConstraint.java b/solver/src/main/java/org/chocosolver/solver/constraints/ImpliedConstraint.java
index 8a809d2746..5b8c25e5b4 100644
--- a/solver/src/main/java/org/chocosolver/solver/constraints/ImpliedConstraint.java
+++ b/solver/src/main/java/org/chocosolver/solver/constraints/ImpliedConstraint.java
@@ -96,8 +96,7 @@ public void activate(int idx) throws ContradictionException {
for (int p = indices[idx]; p < indices[idx + 1]; p++) {
assert (propagators[p].isReifiedAndSilent());
propagators[p].setReifiedTrue();
- model.getSolver().getEngine().execute(propagators[p]);
- model.getSolver().getEngine().onPropagatorExecution(propagators[p]);
+ model.getSolver().getEngine().execute(propagators[p], true);
}
}
diff --git a/solver/src/main/java/org/chocosolver/solver/constraints/Propagator.java b/solver/src/main/java/org/chocosolver/solver/constraints/Propagator.java
index 1e4ef5570c..c44b32434a 100755
--- a/solver/src/main/java/org/chocosolver/solver/constraints/Propagator.java
+++ b/solver/src/main/java/org/chocosolver/solver/constraints/Propagator.java
@@ -624,7 +624,7 @@ public boolean isReified() {
@Override
public Function
+ * tasks[i].mustBePerformed() ⇒ (heights[i] ≤ capacity ∨ tasks[i].duration = 0).
+ *
+ * @param i the index of the task
+ * @throws ContradictionException whenever a filtering error occurs
+ */
+ private void propagateTaskHeight(final int i) throws ContradictionException {
+ if (lcg()) {
+ propTaskHeightExplained(i);
+ } else {
+ propTaskHeight(i);
+ }
+ }
+
+ /**
+ * Propagates the capacity constraint for the task at index i. More particularly, it enforces the following equation :
+ *
+ * tasks[i].mustBePerformed() ⇒ (heights[i] ≤ capacity ∨ tasks[i].duration = 0).
+ *
+ * @param i the index of the task
+ * @throws ContradictionException whenever a filtering error occurs
+ */
+ private void propTaskHeight(final int i) throws ContradictionException {
+ if (capacity.getUB() < heights[i].getLB()) {
+ if (tasks[i].mustBePerformed()) {
+ tasks[i].updateDuration(0, 0, this);
+ } else {
+ tasks[i].forceToBeOptional(this);
+ }
+ } else if (tasks[i].mustBePerformed() && tasks[i].getMinDuration() > 0) {
+ heights[i].updateUpperBound(capacity.getUB(), this);
+ capacity.updateLowerBound(heights[i].getLB(), this);
+ }
+ }
+
+ /**
+ * Propagates the capacity constraint for the task at index i with explanations. More particularly, it enforces the
+ * following equation :
+ *
+ * tasks[i].mustBePerformed() ⇒ (heights[i] ≤ capacity ∨ tasks[i].duration = 0).
+ *
+ * @param i the index of the task
+ * @throws ContradictionException whenever a filtering error occurs
+ */
+ private void propTaskHeightExplained(final int i) throws ContradictionException {
+ if (capacity.getUB() < heights[i].getLB()) {
+ if (tasks[i].mustBePerformed()) {
+ final Reason reason = tasks[i] instanceof OptionalTask
+ ? Reason.r(heights[i].getLELit(heights[i].getLB() - 1), capacity.getGELit(capacity.getUB() + 1), ((OptionalTask) tasks[i]).getPerformed().getEQLit(0))
+ : Reason.r(heights[i].getLELit(heights[i].getLB() - 1), capacity.getGELit(capacity.getUB() + 1));
+ tasks[i].updateMaxDuration(0, this, reason);
+ } else {
+ tasks[i].forceToBeOptional(this, Reason.r(heights[i].getLELit(heights[i].getLB() - 1), capacity.getGELit(capacity.getUB() + 1)));
+ }
+ } else if (tasks[i].mustBePerformed() && tasks[i].getMinDuration() > 0) {
+ final int durationLit = tasks[i].getDuration().getEQLit(0);
+ final Reason reasonHeight;
+ final Reason reasonCapacity;
+ if (tasks[i] instanceof OptionalTask) {
+ final int performedLit = ((OptionalTask) tasks[i]).getPerformed().getEQLit(0);
+ reasonHeight = Reason.r(capacity.getGELit(capacity.getUB() + 1), durationLit, performedLit);
+ reasonCapacity = Reason.r(heights[i].getLELit(heights[i].getLB() - 1), durationLit, performedLit);
+ } else {
+ reasonHeight = Reason.r(capacity.getGELit(capacity.getUB() + 1), durationLit);
+ reasonCapacity = Reason.r(heights[i].getLELit(heights[i].getLB() - 1), durationLit);
+ }
+ heights[i].updateUpperBound(capacity.getUB(), this, reasonHeight);
+ capacity.updateLowerBound(heights[i].getLB(), this, reasonCapacity);
+ }
+ }
+}
diff --git a/solver/src/main/java/org/chocosolver/solver/constraints/nary/cumulative/SchedulingUtils.java b/solver/src/main/java/org/chocosolver/solver/constraints/nary/cumulative/SchedulingUtils.java
new file mode 100644
index 0000000000..82b5751051
--- /dev/null
+++ b/solver/src/main/java/org/chocosolver/solver/constraints/nary/cumulative/SchedulingUtils.java
@@ -0,0 +1,293 @@
+package org.chocosolver.solver.constraints.nary.cumulative;
+
+import org.chocosolver.sat.Reason;
+import org.chocosolver.solver.ICause;
+import org.chocosolver.solver.exception.ContradictionException;
+import org.chocosolver.solver.variables.IntVar;
+import org.chocosolver.solver.variables.OptionalTask;
+import org.chocosolver.solver.variables.Task;
+import org.chocosolver.solver.variables.events.PropagatorEventType;
+
+import java.util.List;
+
+public class SchedulingUtils {
+ /**
+ * Returns true iff the combination of the task and the height variable can be considered as optional.
+ *
+ * @param task a task
+ * @param height its height variable in a propagator
+ * @return true iff the combination of the task and the height variable can be considered as optional
+ */
+ public static boolean mayBePerformed(Task task, IntVar height) {
+ return (height == null || height.getUB() > 0) && task.mayBePerformed();
+ }
+
+ /**
+ * Returns true iff the combination of the task and the height variable must be considered as a performed task (not optional).
+ *
+ * @param task a task
+ * @param height its height variable in a propagator
+ * @return true iff the combination of the task and the height variable must be considered as a performed task (not optional)
+ */
+ public static boolean mustBePerformed(Task task, IntVar height) {
+ return (height == null || height.getLB() > 0) && task.mustBePerformed();
+ }
+
+ /**
+ * Forces the task to be considered as optional. If the task must be performed, the height variable cannot be higher than 0. Otherwise, if the
+ * task is optional, it is set as such. Else, a ContradictionException is thrown.
+ *
+ * @param task a task
+ * @param height a height variable
+ * @param cause the propagator that forces the task to be count as optional
+ * @return true iff setting the task as optional has filtered a variable
+ * @throws ContradictionException an exception if a domain has been emptied
+ */
+ public static boolean filterOptionalTask(Task task, IntVar height, ICause cause) throws ContradictionException {
+ boolean isOpt = task instanceof OptionalTask;
+ if (height != null && (!isOpt || task.mustBePerformed())) {
+ // if the task is not optional or if it must be performed
+ return height.updateUpperBound(0, cause);
+ } else if (isOpt) {
+ // if the task is optional and can be set as such
+ return task.forceToBeOptional(cause);
+ } else {
+ ContradictionException ex = new ContradictionException();
+ ex.set(cause, task.getStart(), "Task " + task + " should be set optional but it cannot be");
+ throw ex;
+ }
+ }
+
+ /**
+ * Updates the earliest start time (est) of the task, considering its height variable. The height variable's upper bound is set to 0, or the task
+ * can become optional if updating its est would empty the start variable's domain.
+ *
+ * @param task a task
+ * @param height a height variable
+ * @param est the est
+ * @param cause the propagator that filters the est
+ * @return true iff updating the est has filtered a variable
+ * @throws ContradictionException an exception if a domain has been emptied
+ */
+ public static boolean filterEst(Task task, IntVar height, int est, ICause cause) throws ContradictionException {
+ if (height != null && height.getLB() == 0 && task.mustBePerformed() && est > task.getLst()) {
+ return height.updateUpperBound(0, cause);
+ } else if (height == null || height.getLB() > 0 || task instanceof OptionalTask) {
+ return task.updateEst(est, cause);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Updates the earliest start time (est) of the task, considering its height variable. The height variable's upper bound is set to 0, or the task
+ * can become optional if updating its est would empty the start variable's domain.
+ *
+ * @param task a task
+ * @param height a height variable
+ * @param est the est
+ * @param cause the propagator that filters the est
+ * @param reason the reason of the filtering
+ * @return true iff updating the est has filtered a variable
+ * @throws ContradictionException an exception if a domain has been emptied
+ */
+ public static boolean filterEst(Task task, IntVar height, int est, ICause cause, Reason reason) throws ContradictionException {
+ if (height != null && height.getLB() == 0 && task.mustBePerformed() && est > task.getLst()) {
+ return height.updateUpperBound(0, cause, Reason.gather(reason, task.getStart().getMaxLit()));
+ } else if (height == null || height.getLB() > 0 || task instanceof OptionalTask) {
+ return task.updateEst(est, cause, reason);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Updates the latest start time (lst) of the task, considering its height variable. The height variable's upper bound is set to 0, or the task
+ * can become optional if updating its lst would empty the start variable's domain.
+ *
+ * @param task a task
+ * @param height a height variable
+ * @param lst the lst
+ * @param cause the propagator that filters the est
+ * @return true iff updating the lst has filtered a variable
+ * @throws ContradictionException an exception if a domain has been emptied
+ */
+ public static boolean filterLst(Task task, IntVar height, int lst, ICause cause) throws ContradictionException {
+ if (height != null && height.getLB() == 0 && task.mustBePerformed() && lst < task.getEst()) {
+ return height.updateUpperBound(0, cause);
+ } else if (height == null || height.getLB() > 0 || task instanceof OptionalTask) {
+ return task.updateLst(lst, cause);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Updates the latest start time (lst) of the task, considering its height variable. The height variable's upper bound is set to 0, or the task
+ * can become optional if updating its lst would empty the start variable's domain.
+ *
+ * @param task a task
+ * @param height a height variable
+ * @param lst the lst
+ * @param cause the propagator that filters the est
+ * @param reason the reason of the filtering
+ * @return true iff updating the lst has filtered a variable
+ * @throws ContradictionException an exception if a domain has been emptied
+ */
+ public static boolean filterLst(Task task, IntVar height, int lst, ICause cause, Reason reason) throws ContradictionException {
+ if (height != null && height.getLB() == 0 && task.mustBePerformed() && lst < task.getEst()) {
+ return height.updateUpperBound(0, cause, Reason.gather(reason, task.getStart().getMinLit()));
+ } else if (height == null || height.getLB() > 0 || task instanceof OptionalTask) {
+ return task.updateLst(lst, cause, reason);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Updates the earliest completion time (ect) of the task, considering its height variable. The height variable's upper bound is set to 0, or the
+ * task can become optional if updating its ect would empty the end variable's domain.
+ *
+ * @param task a task
+ * @param height a height variable
+ * @param ect the ect
+ * @param cause the propagator that filters the est
+ * @return true iff updating the ect has filtered a variable
+ * @throws ContradictionException an exception if a domain has been emptied
+ */
+ public static boolean filterEct(Task task, IntVar height, int ect, ICause cause) throws ContradictionException {
+ if (height != null && height.getLB() == 0 && task.mustBePerformed() && ect > task.getLct()) {
+ return height.updateUpperBound(0, cause);
+ } else if (height == null || height.getLB() > 0 || task instanceof OptionalTask) {
+ return task.updateEct(ect, cause);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Updates the earliest completion time (ect) of the task, considering its height variable. The height variable's upper bound is set to 0, or the
+ * task can become optional if updating its ect would empty the end variable's domain.
+ *
+ * @param task a task
+ * @param height a height variable
+ * @param ect the ect
+ * @param cause the propagator that filters the est
+ * @param reason the reason of the filtering
+ * @return true iff updating the ect has filtered a variable
+ * @throws ContradictionException an exception if a domain has been emptied
+ */
+ public static boolean filterEct(Task task, IntVar height, int ect, ICause cause, Reason reason) throws ContradictionException {
+ if (height != null && height.getLB() == 0 && task.mustBePerformed() && ect > task.getLct()) {
+ return height.updateUpperBound(0, cause, Reason.gather(reason, task.getEnd().getMaxLit()));
+ } else if (height == null || height.getLB() > 0 || task instanceof OptionalTask) {
+ return task.updateEct(ect, cause, reason);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Updates the latest completion time (lct) of the task, considering its height variable. The height variable's upper bound is set to 0, or the
+ * task can become optional if updating its lct would empty the end variable's domain.
+ *
+ * @param task a task
+ * @param height a height variable
+ * @param lct the lct
+ * @param cause the propagator that filters the est
+ * @return true iff updating the lct has filtered a variable
+ * @throws ContradictionException an exception if a domain has been emptied
+ */
+ public static boolean filterLct(Task task, IntVar height, int lct, ICause cause) throws ContradictionException {
+ if (height != null && height.getLB() == 0 && task.mustBePerformed() && lct < task.getEct()) {
+ return height.updateUpperBound(0, cause);
+ } else if (height == null || height.getLB() > 0 || task instanceof OptionalTask) {
+ return task.updateLct(lct, cause);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Updates the latest completion time (lct) of the task, considering its height variable. The height variable's upper bound is set to 0, or the
+ * task can become optional if updating its lct would empty the end variable's domain.
+ *
+ * @param task a task
+ * @param height a height variable
+ * @param lct the lct
+ * @param cause the propagator that filters the est
+ * @param reason the reason of the filtering
+ * @return true iff updating the lct has filtered a variable
+ * @throws ContradictionException an exception if a domain has been emptied
+ */
+ public static boolean filterLct(Task task, IntVar height, int lct, ICause cause, Reason reason) throws ContradictionException {
+ if (height != null && height.getLB() == 0 && task.mustBePerformed() && lct < task.getEct()) {
+ return height.updateUpperBound(0, cause, Reason.gather(reason, task.getEnd().getMinLit()));
+ } else if (height == null || height.getLB() > 0 || task instanceof OptionalTask) {
+ return task.updateLct(lct, cause, reason);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Updates the bounds of the task's duration variable, considering its height variable. The height variable's upper bound is set to 0, or the
+ * task becomes optional if updating empties the duration variable's domain.
+ *
+ * @param task a task
+ * @param height a height variable
+ * @param minDuration the min duration
+ * @param maxDuration the max duration
+ * @param cause the propagator that filters the est
+ * @return true iff updating the bounds of the task's duration variable has filtered a variable
+ * @throws ContradictionException an exception if a domain has been emptied
+ */
+ public static boolean filterDuration(Task task, IntVar height, int minDuration, int maxDuration, ICause cause) throws ContradictionException {
+ if (height != null && height.getLB() == 0 && task.mustBePerformed() &&
+ (maxDuration < task.getMinDuration() || task.getMaxDuration() < minDuration)) {
+ return height.updateUpperBound(0, cause);
+ } else if (height == null || height.getLB() > 0 || task instanceof OptionalTask) {
+ return task.updateDuration(minDuration, maxDuration, cause);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns true iff the two tasks may be performed and they are intersecting, i.e. a.lst < b.ect && b.lst < a.ect.
+ *
+ * @param a a task
+ * @param b another task
+ * @return true iff the two tasks may be performed and they are intersecting
+ */
+ public static boolean intersect(Task a, Task b) {
+ return a.mayBePerformed() && b.mayBePerformed() &&
+ b.getLst() < a.getEct() && a.getLst() < b.getEct();
+ }
+
+ /**
+ * Returns true iff either of the two tasks may not be performed or they are disjoint, i.e. a.est >= b.lct && b.est >= a.lct.
+ *
+ * @param a a task
+ * @param b another task
+ * @return true iff either of the two tasks may not be performed or they are disjoint
+ */
+ public static boolean disjoint(Task a, Task b) {
+ return !a.mayBePerformed() || !b.mayBePerformed() || a.getEst() >= b.getLct() || b.getEst() >= a.getLct();
+ }
+
+ /**
+ * Enforces the relation between start, duration and end variables of the tasks.
+ *
+ * @param tasks the tasks
+ * @throws ContradictionException an exception thrown when enforcing task relation
+ */
+ public static void enforceTaskVariablesRelation(final List
@@ -42,14 +40,6 @@ protected Constraint makeOpposite() {
if (propagators[0] instanceof PropSum) {
PropSum me = (PropSum) propagators[0];
return new SumConstraint(me.opposite());
- } else if (propagators[0] instanceof PropXplusYeqZ) {
- PropXplusYeqZ me = (PropXplusYeqZ) propagators[0];
- return new SumConstraint(new PropSum(
- me.getVars(),
- 2,
- Operator.NQ,
- 0
- ));
}
return super.makeOpposite();
}
diff --git a/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropLocalConDis.java b/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropLocalConDis.java
index 63265e89ab..0f86597a10 100644
--- a/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropLocalConDis.java
+++ b/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropLocalConDis.java
@@ -116,9 +116,7 @@ private boolean applyDeductions() throws ContradictionException {
for (int p = 0; p < propagators[0].length; p++) {
assert (propagators[0][p].isReifiedAndSilent());
propagators[0][p].setReifiedTrue();
- //todo: solver.getEventObserver().activePropagator(bool, propagators[p]);
- propagators[0][p].propagate(PropagatorEventType.FULL_PROPAGATION.getMask());
- model.getSolver().getEngine().onPropagatorExecution(propagators[0][p]);
+ model.getSolver().getEngine().execute(propagators[0][p], true);
}
change = false;
}
diff --git a/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXeqCHalfReif.java b/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXeqCHalfReif.java
index 2f61cacd7d..92be7f45f5 100644
--- a/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXeqCHalfReif.java
+++ b/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXeqCHalfReif.java
@@ -46,7 +46,7 @@ public int getPropagationConditions(int vIdx) {
if (vIdx < 1) {
return IntEventType.all();
}
- return IntEventType.INCLOW.getMask();
+ return IntEventType.INSTANTIATE.getMask();
}
@Override
@@ -60,7 +60,15 @@ public void propagate(int evtmask) throws ContradictionException {
}
// if b is undefined and x does not contain c, b must be false
else if (!x.contains(c)) {
- b.setToFalse(this, lcg() ? Reason.r(x.getLit(c, IntVar.LR_EQ)) : Reason.undef());
+ if(x.hasEnumeratedDomain()) {
+ b.setToFalse(this, lcg() ? Reason.r(x.getLit(c, IntVar.LR_EQ)) : Reason.undef());
+ }else{
+ if(x.getUB() < c){
+ b.setToFalse(this, lcg() ? Reason.r(x.getMaxLit()) : Reason.undef());
+ } else if (x.getLB() > c) {
+ b.setToFalse(this, lcg() ? Reason.r(x.getMinLit()) : Reason.undef());
+ }
+ }
setPassive();
}
}
diff --git a/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXeqYHalfReif.java b/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXeqYHalfReif.java
index a4b966fd90..d3420cdb95 100644
--- a/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXeqYHalfReif.java
+++ b/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXeqYHalfReif.java
@@ -48,7 +48,7 @@ public int getPropagationConditions(int vIdx) {
if (vIdx < 2) {
return IntEventType.all();
}
- return IntEventType.INCLOW.getMask();
+ return IntEventType.INSTANTIATE.getMask();
}
@Override
@@ -86,14 +86,14 @@ public void propagate(int evtmask) throws ContradictionException {
for (int val = vars[0].getLB(); val <= ub; val = vars[0].nextValue(val)) {
if (!vars[1].contains(val)) {
vars[0].removeValue(val, this,
- lcg() ? Reason.r(vars[1].getLit(val, IntVar.LR_NE), b.getValLit()) : Reason.undef());
+ lcg() ? Reason.r(vars[1].getLit(val, IntVar.LR_EQ), b.getValLit()) : Reason.undef());
}
}
ub = vars[1].getUB();
for (int val = vars[1].getLB(); val <= ub; val = vars[1].nextValue(val)) {
if (!vars[0].contains(val)) {
vars[1].removeValue(val, this,
- lcg() ? Reason.r(vars[0].getLit(val, IntVar.LR_NE), b.getValLit()) : Reason.undef());
+ lcg() ? Reason.r(vars[0].getLit(val, IntVar.LR_EQ), b.getValLit()) : Reason.undef());
}
}
}
@@ -111,13 +111,29 @@ public void propagate(int evtmask) throws ContradictionException {
break;
case 0b10:
if (!x.contains(y.getValue())) {
- b.setToFalse(this, lcg() ? Reason.r(y.getValLit(), x.getLit(y.getValue(), IntVar.LR_EQ)) : Reason.undef());
+ if(x.hasEnumeratedDomain() && y.hasEnumeratedDomain()) {
+ b.setToFalse(this, lcg() ? Reason.r(y.getValLit(), x.getLit(y.getValue(), IntVar.LR_EQ)) : Reason.undef());
+ }else{
+ if(x.getUB() < y.getValue()) {
+ b.setToFalse(this, lcg() ? Reason.r(x.getMaxLit(), y.getValLit()) : Reason.undef());
+ } else if (x.getLB() > y.getValue()) {
+ b.setToFalse(this, lcg() ? Reason.r(x.getMinLit(), y.getValLit()) : Reason.undef());
+ }
+ }
setPassive();
}
break;
case 0b01:
if (!y.contains(x.getValue())) {
- b.setToFalse(this, lcg() ? Reason.r(x.getValLit(), y.getLit(x.getValue(), IntVar.LR_EQ)) : Reason.undef());
+ if(x.hasEnumeratedDomain() && y.hasEnumeratedDomain()) {
+ b.setToFalse(this, lcg() ? Reason.r(x.getValLit(), y.getLit(x.getValue(), IntVar.LR_EQ)) : Reason.undef());
+ }else{
+ if(y.getUB() < x.getValue()) {
+ b.setToFalse(this, lcg() ? Reason.r(y.getMaxLit(), x.getValLit()) : Reason.undef());
+ } else if (y.getLB() > x.getValue()) {
+ b.setToFalse(this, lcg() ? Reason.r(y.getMinLit(), x.getValLit()) : Reason.undef());
+ }
+ }
setPassive();
}
break;
diff --git a/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXgeCHalfReif.java b/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXgeCHalfReif.java
index 105b88d478..826642baa1 100644
--- a/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXgeCHalfReif.java
+++ b/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXgeCHalfReif.java
@@ -46,7 +46,7 @@ public int getPropagationConditions(int vIdx) {
if (vIdx < 1) {
return IntEventType.DECUPP.getMask();
}
- return IntEventType.INCLOW.getMask();
+ return IntEventType.INSTANTIATE.getMask();
}
@Override
diff --git a/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXinSHalfReif.java b/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXinSHalfReif.java
index c84ede941b..8b6e28f78c 100644
--- a/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXinSHalfReif.java
+++ b/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXinSHalfReif.java
@@ -45,7 +45,7 @@ public int getPropagationConditions(int vIdx) {
if (vIdx == 0) {
return IntEventType.all();
}
- return IntEventType.INCLOW.getMask();
+ return IntEventType.INSTANTIATE.getMask();
}
@Override
diff --git a/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXleCHalfReif.java b/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXleCHalfReif.java
index 1578bad731..aadf5371e2 100644
--- a/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXleCHalfReif.java
+++ b/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXleCHalfReif.java
@@ -43,7 +43,10 @@ public PropXleCHalfReif(IntVar x, int c, BoolVar b) {
@Override
public int getPropagationConditions(int vIdx) {
- return IntEventType.INCLOW.getMask();
+ if(vIdx < 1){
+ return IntEventType.INCLOW.getMask();
+ }
+ return IntEventType.INSTANTIATE.getMask();
}
@Override
diff --git a/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXleYHalfReif.java b/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXleYHalfReif.java
index 2a17c246c1..096978f96c 100644
--- a/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXleYHalfReif.java
+++ b/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXleYHalfReif.java
@@ -41,10 +41,13 @@ public PropXleYHalfReif(IntVar x, IntVar y, BoolVar b) {
@Override
public int getPropagationConditions(int vIdx) {
- if (vIdx == 1) {
+ if (vIdx == 0) {
+ return IntEventType.INCLOW.getMask();
+ } else if (vIdx == 1) {
return IntEventType.DECUPP.getMask();
+ }else{
+ return IntEventType.INSTANTIATE.getMask();
}
- return IntEventType.INCLOW.getMask();
}
@Override
diff --git a/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXneCHalfReif.java b/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXneCHalfReif.java
index b898917bd3..27747c4e15 100644
--- a/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXneCHalfReif.java
+++ b/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXneCHalfReif.java
@@ -43,10 +43,7 @@ public PropXneCHalfReif(IntVar x, int c, BoolVar b) {
@Override
public int getPropagationConditions(int vIdx) {
- if (vIdx < 1) {
- return IntEventType.INSTANTIATE.getMask();
- }
- return IntEventType.INCLOW.getMask();
+ return IntEventType.INSTANTIATE.getMask();
}
@Override
diff --git a/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXneYHalfReif.java b/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXneYHalfReif.java
index b2e4a0ad5d..d85b822771 100644
--- a/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXneYHalfReif.java
+++ b/solver/src/main/java/org/chocosolver/solver/constraints/reification/PropXneYHalfReif.java
@@ -43,10 +43,7 @@ public PropXneYHalfReif(IntVar x, IntVar y, BoolVar b) {
@Override
public int getPropagationConditions(int vIdx) {
- if (vIdx < 2) {
- return IntEventType.INSTANTIATE.getMask();
- }
- return IntEventType.INCLOW.getMask();
+ return IntEventType.INSTANTIATE.getMask();
}
@Override
diff --git a/solver/src/main/java/org/chocosolver/solver/constraints/ternary/PropDivXYZ.java b/solver/src/main/java/org/chocosolver/solver/constraints/ternary/PropDivXYZ.java
index 1774f74a60..adf5b62892 100644
--- a/solver/src/main/java/org/chocosolver/solver/constraints/ternary/PropDivXYZ.java
+++ b/solver/src/main/java/org/chocosolver/solver/constraints/ternary/PropDivXYZ.java
@@ -19,6 +19,7 @@
import org.chocosolver.util.ESat;
import org.chocosolver.util.tools.MathUtils;
+import static org.chocosolver.solver.variables.IntVar.LR_EQ;
import static org.chocosolver.solver.variables.IntVar.LR_NE;
/**
@@ -104,7 +105,7 @@ public void propagate(int evtmask) throws ContradictionException {
// sZ = 0 && sX!=0 => |x| < |y|
if (Z.isInstantiatedTo(0) && !X.contains(0)) {
hasChanged |= absX.updateUpperBound(absY.getUB() - 1, this,
- lcg() ? Reason.r(0, Z.getValLit(), X.getLit(0, LR_NE), absY.getMaxLit()) : Reason.undef());
+ lcg() ? Reason.r(0, Z.getValLit(), X.getLit(0, LR_EQ), absY.getMaxLit()) : Reason.undef());
}
break;
case 5: // X and Z are instanciated
@@ -366,7 +367,7 @@ protected boolean sameSign(IntVar a, IntVar b, Reason r) throws ContradictionExc
}
if (!b.contains(0)) {
res |= a.removeValue(0, this,
- lcg() ? Reason.gather(r, b.getLit(0, LR_NE)) : Reason.undef());
+ lcg() ? Reason.gather(r, b.getLit(0, LR_EQ)) : Reason.undef());
}
return res;
}
diff --git a/solver/src/main/java/org/chocosolver/solver/constraints/ternary/PropDivXYZLight.java b/solver/src/main/java/org/chocosolver/solver/constraints/ternary/PropDivXYZLight.java
new file mode 100644
index 0000000000..3ad65426fb
--- /dev/null
+++ b/solver/src/main/java/org/chocosolver/solver/constraints/ternary/PropDivXYZLight.java
@@ -0,0 +1,189 @@
+/*
+ * This file is part of choco-solver, http://choco-solver.org/
+ *
+ * Copyright (c) 2025, 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.ternary;
+
+import org.chocosolver.sat.Reason;
+import org.chocosolver.solver.Model;
+import org.chocosolver.solver.constraints.Explained;
+import org.chocosolver.solver.constraints.Propagator;
+import org.chocosolver.solver.constraints.PropagatorPriority;
+import org.chocosolver.solver.exception.ContradictionException;
+import org.chocosolver.solver.exception.SolverException;
+import org.chocosolver.solver.variables.IntVar;
+import org.chocosolver.solver.variables.events.IntEventType;
+import org.chocosolver.util.ESat;
+
+/**
+ * Z = ceil(X/Y)
+ * A propagator for the constraint Z = ceil(X / Y) where X, Y and Z are possibly negative integer variables.
+ * The constructor introduces views of the variables to handle the sign of the variables correctly.
+ */
+@Explained
+public class PropDivXYZLight extends Propagator
+ * If the provided
+ * If the provided
+ *
+ * @param algo an indication for the algorithm to use for the table constraint
+ * @param positiveCombinations if true, only feasible tuples are kept in the table, otherwise only infeasible tuples are kept.
* @return a TABLE constraint that captures the expression
* @see Model#table(IntVar[], Tuples, String)
* @see Model#table(IntVar, IntVar, Tuples, String)
*/
- default Constraint extension(String algo) {
+ default Constraint extension(String algo, boolean positiveCombinations) {
+ // override the feasibility
+ boolean feasible = algo.endsWith("+") || positiveCombinations;
+ // Extract variables from the expression, map individual variables to their positions
HashSet
+ *
+ * @param algo an indication for the algorithm to use for the table constraint
* @return a TABLE constraint that captures the expression
+ * @see Model#table(IntVar[], Tuples, String)
+ * @see Model#table(IntVar, IntVar, Tuples, String)
+ */
+ default Constraint extension(String algo) {
+ return extension(algo, true);
+ }
+
+ /**
+ * Represents the relational expression as an extension constraint (aka, table).
+ * This method is a shortcut for {@link #extension(String, boolean)} with
+ *
+ *
+ * @return a TABLE constraint that captures the expression
+ * @see Model#table(IntVar[], Tuples, String)
+ * @see Model#table(IntVar, IntVar, Tuples, String)
*/
default Constraint extension() {
return extension("");
}
+ /**
+ * Embodies the relational expression as a constraint.
+ * This method is a shortcut for {@link #decompose()} or {@link #extension(String, boolean)}.
+ * It decides whether to decompose the expression into a set of constraints
+ * or to represent it as an extension constraint based on the size of the domain and the number of variables involved.
+ * If the cardinality of the domain is small enough and the number of variables is limited, it uses an extension constraint.
+ * Otherwise, it decomposes the expression into a set of constraints.
+ *
+ *
+ * @return a Constraint that captures the relational expression.
+ */
+ default Constraint embody() {
+ HashSet
+ * The strategy is composed of the following meta strategies:
+ * *
* The strategy is composed of the following variable selectors:
*
+ * Note that this strategy must be combined with (fast) restarts to be useful.
+ *
+ * The strategy is composed of the following meta strategies:
+ * *
+ * The strategy is composed of the following variable selectors:
+ *
+ * The strategy is composed of the following value selectors:
+ *
+ * Note that this strategy must be combined with (fast) restarts to be useful.
+ *
- * A call to {@link Task#ensureBoundConsistency()} is required before launching the resolution,
- * this will not be done automatically.
*
* @param s integer variable, starting time
* @param d fixed duration
* @return a task variable.
*/
default Task taskVar(IntVar s, int d) {
- return new Task(s, d);
+ return taskVar(s, d, false);
}
/**
* Creates a task variable, based on a starting time s and a duration d
* such that: s + d = e, where e is the ending time.
- *
- * A call to {@link Task#ensureBoundConsistency()} is required before launching the resolution,
- * this will not be done automatically.
*
* @param s integer variable, starting time
* @param d integer variable, duration
* @return a task variable.
*/
default Task taskVar(IntVar s, IntVar d) {
- if (d.isInstantiated()) {
- return new Task(s, d, ref().offset(s, d.getValue()));
- } else {
- int[] bounds = VariableUtils.boundsForAddition(s, d);
- IntVar end = ref().intVar(bounds[0], bounds[1]);
- return new Task(s, d, end);
- }
+ return taskVar(s, d, false);
}
/**
* Creates a task variable, based on a starting time s and a duration d
* such that: s + d = e, where e is the ending time.
- *
- * A call to {@link Task#ensureBoundConsistency()} is required before launching the resolution,
- * this will not be done automatically.
*
* @param s integer variable, starting time
* @param d fixed duration
@@ -604,15 +605,12 @@ default Task taskVar(IntVar s, IntVar d) {
* @return a task variable.
*/
default Task taskVar(IntVar s, int d, IntVar e) {
- return new Task(s, d, e);
+ return taskVar(s, d, e, false);
}
/**
* Creates a task variable, made of a starting time s,
* a duration d and an ending time e such that: s + d = e.
- *
- * A call to {@link Task#ensureBoundConsistency()} is required before launching the resolution,
- * this will not be done automatically.
*
* @param s integer variable, starting time
* @param d integer variable, duration
@@ -620,16 +618,175 @@ default Task taskVar(IntVar s, int d, IntVar e) {
* @return a task variable.
*/
default Task taskVar(IntVar s, IntVar d, IntVar e) {
- return new Task(s, d, e);
+ return taskVar(s, d, e, false);
+ }
+
+ /**
+ * Creates a task variable, based on an earliest starting time est, a latest completion time lst,
+ * a duration d, an earliest completion time ect and a latest completion time lct
+ * such that: start + duration = end.
+ *
+ * @param est earliest starting time
+ * @param lst latest starting time
+ * @param d duration
+ * @param ect earliest completion time
+ * @param lct latest completion time
+ * @param optional whether to create an OptionalTask or a Task
+ * @return a task variable.
+ */
+ default Task taskVar(int est, int lst, int d, int ect, int lct, boolean optional) {
+ if (optional) {
+ return new OptionalTask(ref(), est, lst, d, ect, lct);
+ } else {
+ return new Task(ref(), est, lst, d, ect, lct);
+ }
+ }
+
+ /**
+ * Creates a task variable, based on an earliest starting time est, a latest completion time lst,
+ * a duration d, an earliest completion time ect and a latest completion time lct
+ * such that: start + duration = end.
+ *
+ * @param est earliest starting time
+ * @param lst latest starting time
+ * @param d duration
+ * @param ect earliest completion time
+ * @param lct latest completion time
+ * @param performed boolean variable, whether the task is performed or not
+ * @return a task variable.
+ */
+ default OptionalTask taskVar(int est, int lst, int d, int ect, int lct, BoolVar performed) {
+ return new OptionalTask(ref(), est, lst, d, ect, lct, performed);
+ }
+
+ /**
+ * Creates a task variable, based on a starting time s and a duration d
+ * such that: s + d = e, where e is the ending time.
+ *
+ * @param s integer variable, starting time
+ * @param d fixed duration
+ * @param optional whether to create an OptionalTask or a Task
+ * @return a task variable.
+ */
+ default Task taskVar(IntVar s, int d, boolean optional) {
+ if (optional) {
+ return new OptionalTask(s, d);
+ } else {
+ return new Task(s, d);
+ }
+ }
+
+ /**
+ * Creates a task variable, based on a starting time s and a duration d
+ * such that: s + d = e, where e is the ending time.
+ *
+ * @param s integer variable, starting time
+ * @param d fixed duration
+ * @param performed boolean variable, whether the task is performed or not
+ * @return a task variable.
+ */
+ default OptionalTask taskVar(IntVar s, int d, BoolVar performed) {
+ return new OptionalTask(s, d, performed);
+ }
+
+ /**
+ * Creates a task variable, based on a starting time s and a duration d
+ * such that: s + d = e, where e is the ending time.
+ *
+ * @param s integer variable, starting time
+ * @param d integer variable, duration
+ * @param optional whether to create an OptionalTask or a Task
+ * @return a task variable.
+ */
+ default Task taskVar(IntVar s, IntVar d, boolean optional) {
+ if (optional) {
+ return new OptionalTask(s, d);
+ } else {
+ return new Task(s, d);
+ }
+ }
+
+ /**
+ * Creates a task variable, based on a starting time s and a duration d
+ * such that: s + d = e, where e is the ending time.
+ *
+ * @param s integer variable, starting time
+ * @param d integer variable, duration
+ * @param performed boolean variable, whether the task is performed or not
+ * @return a task variable.
+ */
+ default OptionalTask taskVar(IntVar s, IntVar d, BoolVar performed) {
+ return new OptionalTask(s, d, performed);
+ }
+
+ /**
+ * Creates a task variable, based on a starting time s and a duration d
+ * such that: s + d = e, where e is the ending time.
+ *
+ * @param s integer variable, starting time
+ * @param d fixed duration
+ * @param e integer variable, ending time
+ * @param optional whether to create an OptionalTask or a Task
+ * @return a task variable.
+ */
+ default Task taskVar(IntVar s, int d, IntVar e, boolean optional) {
+ if (optional) {
+ return new OptionalTask(s, d, e);
+ } else {
+ return new Task(s, d, e);
+ }
+ }
+
+ /**
+ * Creates a task variable, based on a starting time s and a duration d
+ * such that: s + d = e, where e is the ending time.
+ *
+ * @param s integer variable, starting time
+ * @param d fixed duration
+ * @param e integer variable, ending time
+ * @param performed boolean variable, whether the task is performed or not
+ * @return a task variable.
+ */
+ default OptionalTask taskVar(IntVar s, int d, IntVar e, BoolVar performed) {
+ return new OptionalTask(s, d, e, performed);
+ }
+
+ /**
+ * Creates a task variable, made of a starting time s,
+ * a duration d and an ending time e such that: s + d = e.
+ *
+ * @param s integer variable, starting time
+ * @param d integer variable, duration
+ * @param e integer variable, ending time
+ * @param optional whether to create an OptionalTask or a Task
+ * @return a task variable.
+ */
+ default Task taskVar(IntVar s, IntVar d, IntVar e, boolean optional) {
+ if (optional) {
+ return new OptionalTask(s, d, e);
+ } else {
+ return new Task(s, d, e);
+ }
+ }
+
+ /**
+ * Creates a task variable, made of a starting time s,
+ * a duration d and an ending time e such that: s + d = e.
+ *
+ * @param s integer variable, starting time
+ * @param d integer variable, duration
+ * @param e integer variable, ending time
+ * @param performed boolean variable, whether the task is performed or not
+ * @return a task variable.
+ */
+ default OptionalTask taskVar(IntVar s, IntVar d, IntVar e, BoolVar performed) {
+ return new OptionalTask(s, d, e, performed);
}
/**
* Creates an array of s.length task variables,
* where task i is made of a starting time s_i,
* a processing time p_i and an ending time e_i such that: s_i + p_i = e_i.
- *
- * A call to {@link Task#ensureBoundConsistency()} is required before launching the resolution,
- * this will not be done automatically.
*
* @param s integer variables, starting times
* @param p integer variables, processing times
@@ -652,9 +809,6 @@ default Task[] taskVarArray(IntVar[] s, IntVar[] p, IntVar[] e) {
* where task i,j is made of a starting time s_(i,j),
* a processing time p_(i,j) and an ending time e_(i,j) such that:
* s_(i,j) + p_(i,j) = e_(i,j).
- *
- * A call to {@link Task#ensureBoundConsistency()} is required before launching the resolution,
- * this will not be done automatically.
*
* @param s integer variables, starting times
* @param p integer variables, processing times
diff --git a/solver/src/main/java/org/chocosolver/solver/variables/IViewFactory.java b/solver/src/main/java/org/chocosolver/solver/variables/IViewFactory.java
index ab82ee73f6..3324db40aa 100644
--- a/solver/src/main/java/org/chocosolver/solver/variables/IViewFactory.java
+++ b/solver/src/main/java/org/chocosolver/solver/variables/IViewFactory.java
@@ -187,11 +187,7 @@ default IntVar abs(IntVar var) {
int ub = max(-var.getLB(), var.getUB());
String name = "|" + var.getName() + "|";
IntVar abs;
- if (var.hasEnumeratedDomain()) {
- abs = ref().intVar(name, 0, ub, false);
- } else {
- abs = ref().intVar(name, 0, ub, true);
- }
+ abs = ref().intVar(name, 0, ub);
ref().absolute(abs, var).post();
return abs;
}
diff --git a/solver/src/main/java/org/chocosolver/solver/variables/IntVar.java b/solver/src/main/java/org/chocosolver/solver/variables/IntVar.java
index 851e9e8f7e..3f6b313e01 100644
--- a/solver/src/main/java/org/chocosolver/solver/variables/IntVar.java
+++ b/solver/src/main/java/org/chocosolver/solver/variables/IntVar.java
@@ -37,7 +37,7 @@
* @author Charles Prud'homme
* @since 18 nov. 2010
*/
-public interface IntVar extends ICause, Variable, Iterable
@@ -57,11 +54,6 @@ public class FixedIntVarImpl extends AbstractVariable implements IntVar {
*/
private DisposableRangeIterator _riterator;
- /**
- * Value iterator allowing for(int i:this) loops
- */
- private final IntVarValueIterator _javaIterator = new IntVarValueIterator(this);
-
/**
* Creates a variable whom domain is natively reduced to the singleton {
+ * If the resolution has already started ({@code model.getSolver().getSearchState() != SearchState.NEW}),
+ * the method stops without applying any pre-processing.
+ *
+ * @param model the model to pre-process
+ * @implNote The model's constraints are iterated in order to filter constraint with the
+ * following properties:
+ *
+ * - CT+: table constraint which applies the Compact-Table algorithm on allowed tuples,
* - 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,
@@ -449,7 +466,7 @@ default Constraint table(IntVar var1, IntVar var2, Tuples tuples) {
*
* @param var1 first variable
* @param var2 second variable
- * @param tuples the relation between the two variables, among {"AC3", "AC3rm", "AC3bit+rm", "AC2001", "CT+", "FC"}
+ * @param tuples the relation between the two variables, among {"AC3", "AC3rm", "AC3bit+rm", "AC2001", "CT+", "CT", "FC"}
*/
default Constraint table(IntVar var1, IntVar var2, Tuples tuples, String algo) {
Object[] args = variableUniqueness(new IntVar[]{var1, var2});
@@ -463,13 +480,27 @@ default Constraint table(IntVar var1, IntVar var2, Tuples tuples, String algo) {
ref().addTable(new IntVar[]{var1, var2}, tuples);
return ref().voidConstraint();
}
+ if (algo.endsWith("+") && !tuples.isFeasible()) {
+ throw new SolverException(algo + " table algorithm cannot be used with forbidden tuples.");
+ }
+ if (tuples.allowUniversalValue() && !(algo.contains("CT+") || algo.contains("STR2+"))) {
+ throw new SolverException(algo + " table algorithm cannot be used with short tuples.");
+ }
+
Propagator
+ *
* - GAC2001: Arc Consistency version 2001 for tuples,
*
@@ -2763,11 +2695,13 @@ default Constraint table(IntVar[] vars, Tuples tuples) {
*
* @param vars variables forming the tuples
* @param tuples the relation between the variables (list of allowed/forbidden tuples). Should not be modified once passed to the constraint.
- * @param algo to choose among {"CT+", "GAC3rm", "GAC2001", "GACSTR", "GAC2001+", "GAC3rm+", "FC", "STR2+"}
+ * @param algo to choose among {"CT", "CT+", "AC3rm", "GAC3rm", "AC2001", "GAC2001", "GACSTR", "GAC2001+", "GAC3rm+", "FC", "STR2+"}
*/
default Constraint table(IntVar[] vars, Tuples tuples, String algo) {
// if some variables appears more than one time, the filtering algorithm can be not correct
vars = (IntVar[]) variableUniqueness(vars)[0];
+ // check views too
+
if (ref().getSolver().isLCG()) {
if (ref().getSettings().warnUser()) {
ref().getSolver().log().white().println(
@@ -2780,6 +2714,7 @@ default Constraint table(IntVar[] vars, Tuples tuples, String algo) {
switch (algo) {
case "FC":
return table(vars[0], vars[1], tuples, algo);
+ case "AC2001":
case "GAC2001":
return table(vars[0], vars[1], tuples, "AC2001");
case "CT+":
@@ -2790,19 +2725,22 @@ default Constraint table(IntVar[] vars, Tuples tuples, String algo) {
return table(vars[0], vars[1], tuples);
}
}
- if (algo.contains("+") && !tuples.isFeasible()) {
+ if (algo.endsWith("+") && !tuples.isFeasible()) {
throw new SolverException(algo + " table algorithm cannot be used with forbidden tuples.");
}
- if (tuples.allowUniversalValue() && !(algo.contains("CT+") || algo.contains("STR2+"))) {
+ if (tuples.allowUniversalValue() && !(algo.startsWith("CT") || algo.contains("STR2+"))) {
throw new SolverException(algo + " table algorithm cannot be used with short tuples.");
}
Propagator
+ *
+ * @author Charles Prud'homme
+ * @since 04/07/2025
+ */
+@Explained
+public class PropAbsoluteLight extends Propagatorfilter.
+ * 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 vars concerned variables
+ * @return two Tuples objects, the first one contains the valid tuples and the second one contains the invalid tuples
+ */
+ public static Tuples[] generateTuples(TupleValidator filter, IntVar... vars) {
+ Tuples[] tuples = new Tuples[2];
+ tuples[0] = new Tuples(true);
+ tuples[1] = new Tuples(false);
+ 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[0].add(t.clone());
+ } else {
+ tuples[1].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;
+
+ }
+
+ /**
+ * Randomly generate n tuples from vars and return the number of tuples that are valid according to filter
+ * as the ratio of valid tuples over the total number of tuples.
+ * @param n number of tuples to generate
+ * @param filter tuple validator
+ * @param vars concerned variables
+ * @return the ratio of valid tuples over the total number of tuples
+ */
+ public static double sample(int n, Random rnd, TupleValidator filter, IntVar... vars){
+ int valid = 0;
+ for (int i = 0; i < n; i++) {
+ int[] t = new int[vars.length];
+ for (int j = 0; j < vars.length; j++) {
+ int v = vars[j].getLB() + rnd.nextInt(vars[j].getUB() - vars[j].getLB() + 1);
+ while(!vars[j].contains(v)){
+ v = vars[j].getLB() + rnd.nextInt(vars[j].getUB() - vars[j].getLB() + 1);
+ }
+ t[j] = v;
+ }
+ if (filter.valid(t)) {
+ valid++;
+ }
+ }
+ return ((double) valid) / n;
+ }
+
/**
* A method that randomly generates tuples from a set of variables.
*
@@ -120,7 +184,7 @@ public static Tuples generateTuples(TupleValidator filter, boolean feasible, Int
*
+ *
+ * @author Charles Prud'homme
+ * @since 17/10/2024
+ */
+public interface IAlldifferentAlgorithm {
+ boolean propagate() throws ContradictionException;
+}
diff --git a/solver/src/main/java/org/chocosolver/solver/constraints/nary/cumulative/CumulFilter.java b/solver/src/main/java/org/chocosolver/solver/constraints/nary/cumulative/CumulFilter.java
deleted file mode 100644
index 68a5806eb5..0000000000
--- a/solver/src/main/java/org/chocosolver/solver/constraints/nary/cumulative/CumulFilter.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * This file is part of choco-solver, http://choco-solver.org/
- *
- * Copyright (c) 2025, 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.nary.cumulative;
-
-import org.chocosolver.solver.constraints.Propagator;
-import org.chocosolver.solver.exception.ContradictionException;
-import org.chocosolver.solver.variables.IntVar;
-import org.chocosolver.util.objects.setDataStructures.ISet;
-
-
-
-/**
- * Class able to filter a subset of tasks for the cumulative constraint
- * @author Jean-Guillaume Fages
- */
-public abstract class CumulFilter {
-
- //***********************************************************************************
- // VARIABLES
- //***********************************************************************************
-
- protected int nbMaxTasks;
-
- //***********************************************************************************
- // CONSTRUCTORS
- //***********************************************************************************
-
- /**
- * An object which can filter subset of tasks for the cumulative constraint
- * @param nbMaxTasks maximum number of tasks
- */
- public CumulFilter(int nbMaxTasks){
- this.nbMaxTasks = nbMaxTasks;
- }
-
- //***********************************************************************************
- // METHODS
- //***********************************************************************************
-
- /**
- * Filters the cumulative constraint over the subset of tasks induced by tasks
- * @param s start variables
- * @param d duration variables
- * @param e end variables
- * @param h height variables
- * @param capa maximum capacity variable
- * @param tasks subset of tasks to filter
- * @param aCause a cumulative propagator
- * @throws ContradictionException
- */
- public abstract void filter(IntVar[] s, IntVar[] d, IntVar[] e, IntVar[] h, IntVar capa, ISet tasks, Propagator
+ *
+ * The explanations are the ones described in [Schutt2011].
+ *
+ * Gay, S., Hartert, R., and Schaus, P.: “Simple and Scalable Time-Table Filtering for the Cumulative Constraint”. In: Principles and Practice of Constraint Programming - 21st International Conference, CP 2015, Cork, Ireland, August 31 - September 4, 2015, Proceedings. Ed. by Gilles Pesant. Vol. 9255. Lecture Notes in Computer Science. Springer, 2015, pp. 149–157
+ *
+ * Petr Vilım: “Timetable Edge Finding Filtering Algorithm for Discrete Cumulative Resources”. In: Integration of AI and OR Techniques in Constraint Programming for Combinatorial Optimization Problems - 8th International Conference, CPAIOR 2011, Berlin, Germany, May 23-27, 2011. Proceedings. Ed. by Tobias Achterberg and J. Christopher Beck. Vol. 6697. Lecture Notes in Computer Science. Springer, 2011, pp. 230–245
+ *
+ * Andreas Schutt et al. “Explaining the cumulative propagator”. In: Constraints An Int. J. 16.3 (2011), pp. 250–282
+ *
+ * @author Arthur Godet algo is empty, the default algorithm is used for the table constraint.
+ * algo ends with a plus sign ('+'), it indicates that the table should only contain feasible tuples.
+ * In this case, the feasible parameter is ignored and set to true.
+ * If the provided algo does not end with a plus sign, the feasible parameter determines whether
+ * the table should contain feasible or infeasible tuples.
+ * algo set to an empty string and feasible set to true.
+ * This means that the table will only contain feasible tuples.
+ * algo set to an empty string and feasible set to true.
+ * This means that the table will only contain feasible tuples.
+ *
+ *
*
@@ -639,24 +644,137 @@ public static AbstractStrategy
* In addition, the phase-saving mechanism is activated.
+ *
+ *
+ *
+ *
+ *
+ *
+ * In addition, the phase-saving mechanism is activated.
+ *
+ *
- *
- * @author Jean-Guillaume Fages, Charles Prud'homme
- * @since 2 april 2014
- */
-public class IntDomainRandomBound implements IntValueSelector {
-
- private final Random rand;
-
- public IntDomainRandomBound(long seed) {
- this.rand = new Random(seed);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int selectValue(IntVar var) {
- return rand.nextBoolean() ? var.getLB() : var.getUB();
- }
-}
diff --git a/solver/src/main/java/org/chocosolver/solver/search/strategy/selectors/values/IntDomainSticky.java b/solver/src/main/java/org/chocosolver/solver/search/strategy/selectors/values/IntDomainSticky.java
new file mode 100644
index 0000000000..5901de8ac7
--- /dev/null
+++ b/solver/src/main/java/org/chocosolver/solver/search/strategy/selectors/values/IntDomainSticky.java
@@ -0,0 +1,106 @@
+/*
+ * This file is part of choco-solver, http://choco-solver.org/
+ *
+ * Copyright (c) 2025, 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.search.strategy.selectors.values;
+
+import gnu.trove.map.hash.TIntIntHashMap;
+import org.chocosolver.solver.variables.IVariableMonitor;
+import org.chocosolver.solver.variables.IntVar;
+import org.chocosolver.solver.variables.events.IEventType;
+import org.chocosolver.solver.variables.events.IntEventType;
+
+import java.util.OptionalInt;
+import java.util.function.BiPredicate;
+import java.util.function.Function;
+
+/**
+ * A value selector that returns the last value assigned to a variable while propagating.
+ * If no value was assigned, falls back to 'mainSelector'.
+ * This value selector monitors the variables and stores the last value assigned to each variable.
+ * Then, when a value has to be selected, it checks if the last assigned value is still in the domain of the variable.
+ * If it is, it returns this value, otherwise it falls back to the main selector.
+ *
+ * @author Charles Prud'homme
+ */
+public final class IntDomainSticky implements IntValueSelector, Function
+ *
+ * @author Charles Prud'homme
+ * @since 24/02/2025
+ */
+public class RoundRobinGroup
+ *
+ * @author Charles Prud'homme
+ * @since 11/03/2025
+ */
+
+import org.chocosolver.solver.Model;
+import org.chocosolver.solver.search.loop.monitors.IMonitorContradiction;
+import org.chocosolver.solver.search.strategy.decision.Decision;
+import org.chocosolver.solver.variables.Variable;
+
+public abstract class MetaStrategythis which is in the domain.
@@ -759,6 +761,14 @@ default boolean updateBounds(int lb, int ub, ICause cause, Reason reason) throws
*/
boolean hasEnumeratedDomain();
+ /**
+ * Indicates wether (or not) this has an enumerated domain (represented in extension) and is not a single value
+ * @return true if the domain is enumerated and the variable is not instantiated yet, false otherwise.
+ */
+ default boolean hasUnfixedEnumeratedDomain() {
+ return !isInstantiated() && hasEnumeratedDomain();
+ }
+
/**
* Allow to monitor removed values of this.
*
diff --git a/solver/src/main/java/org/chocosolver/solver/variables/OptionalTask.java b/solver/src/main/java/org/chocosolver/solver/variables/OptionalTask.java
new file mode 100644
index 0000000000..effd4ba875
--- /dev/null
+++ b/solver/src/main/java/org/chocosolver/solver/variables/OptionalTask.java
@@ -0,0 +1,493 @@
+/*
+ * This file is part of choco-solver, http://choco-solver.org/
+ *
+ * Copyright (c) 2025, 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.variables;
+
+import org.chocosolver.sat.Reason;
+import org.chocosolver.solver.ICause;
+import org.chocosolver.solver.Model;
+import org.chocosolver.solver.exception.ContradictionException;
+
+/**
+ * Container representing an optional task:
+ * It ensures that: start + duration = end, if and only if the task is performed
+ *
+ * @author Arthur Godet
+ * @since 25/11/2023
+ */
+public class OptionalTask extends Task {
+
+ //***********************************************************************************
+ // VARIABLES
+ //***********************************************************************************
+
+ private final BoolVar performed;
+
+ //***********************************************************************************
+ // CONSTRUCTORS
+ //***********************************************************************************
+
+ /**
+ * Container representing an optional task:
+ * It ensures that: start + duration = end, if and only if the task is performed, end being an offset view of start + duration.
+ *
+ * @param model the Model of the variables
+ * @param est earliest starting time
+ * @param lst latest starting time
+ * @param d duration
+ * @param ect earliest completion time
+ * @param lct latest completion time
+ */
+ public OptionalTask(Model model, int est, int lst, int d, int ect, int lct) {
+ this(model, est, lst, d, ect, lct, model.boolVar());
+ }
+
+ /**
+ * Container representing an optional task:
+ * It ensures that: start + duration = end, if and only if the task is performed, end being an offset view of start + duration.
+ *
+ * @param model the Model of the variables
+ * @param est earliest starting time
+ * @param lst latest starting time
+ * @param d duration
+ * @param ect earliest completion time
+ * @param lct latest completion time
+ * @param performed performed variable
+ */
+ public OptionalTask(Model model, int est, int lst, int d, int ect, int lct, BoolVar performed) {
+ super(model, est, lst, d, ect, lct);
+ this.performed = performed;
+ }
+
+ /**
+ * Container representing an optional task:
+ * It ensures that: start + duration = end, if and only if the task is performed, end being an offset view of start + duration.
+ *
+ * @param s start variable
+ * @param d duration value
+ */
+ public OptionalTask(IntVar s, int d) {
+ this(s, d, s.getModel().boolVar());
+ }
+
+ /**
+ * Container representing an optional task:
+ * It ensures that: start + duration = end, if and only if the task is performed, end being an offset view of start + duration.
+ *
+ * @param s start variable
+ * @param d duration value
+ * @param performed performed variable
+ */
+ public OptionalTask(IntVar s, int d, BoolVar performed) {
+ super(s, d);
+ this.performed = performed;
+ }
+
+ /**
+ * Container representing a task:
+ * It ensures that: start + duration = end, if and only if the task is performed.
+ *
+ * @param s start variable
+ * @param d duration variable
+ */
+ public OptionalTask(IntVar s, IntVar d) {
+ this(s, d, s.getModel().boolVar());
+ }
+
+ /**
+ * Container representing a task:
+ * It ensures that: start + duration = end, if and only if the task is performed.
+ *
+ * @param s start variable
+ * @param d duration variable
+ * @param performed performed variable
+ */
+ public OptionalTask(IntVar s, IntVar d, BoolVar performed) {
+ super(s, d);
+ this.performed = performed;
+ }
+
+ /**
+ * Container representing an optional task:
+ * It ensures that: start + duration = end, if and only if the task is performed, end being an offset view of start + duration.
+ *
+ * @param s start variable
+ * @param d duration value
+ * @param e end variable
+ */
+ public OptionalTask(IntVar s, int d, IntVar e) {
+ this(s, d, e, s.getModel().boolVar());
+ }
+
+ /**
+ * Container representing an optional task:
+ * It ensures that: start + duration = end, if and only if the task is performed, end being an offset view of start + duration.
+ *
+ * @param s start variable
+ * @param d duration value
+ * @param e end variable
+ * @param performed performed variable
+ */
+ public OptionalTask(IntVar s, int d, IntVar e, BoolVar performed) {
+ super(s, d, e);
+ this.performed = performed;
+ }
+
+ /**
+ * Container representing an optional task:
+ * It ensures that: start + duration = end, if and only if the task is performed
+ *
+ * @param s start variable
+ * @param d duration variable
+ * @param e end variable
+ */
+ public OptionalTask(IntVar s, IntVar d, IntVar e) {
+ this(s, d, e, s.getModel().boolVar());
+ }
+
+ /**
+ * Container representing an optional task:
+ * It ensures that: start + duration = end, if and only if the task is performed
+ *
+ * @param s start variable
+ * @param d duration variable
+ * @param e end variable
+ * @param performed performed variable
+ */
+ public OptionalTask(IntVar s, IntVar d, IntVar e, BoolVar performed) {
+ super(s, d, e);
+ this.performed = performed;
+ }
+
+ //***********************************************************************************
+ // ACCESSORS
+ //***********************************************************************************
+
+ @Override
+ public boolean mayBePerformed() {
+ return performed.getUB() == 1;
+ }
+
+ @Override
+ public boolean mustBePerformed() {
+ return performed.getLB() == 1;
+ }
+
+ public BoolVar getPerformed() {
+ return this.performed;
+ }
+
+ @Override
+ public int getEst() {
+ if (mayBePerformed()) {
+ return start.getLB();
+ } else {
+ return Integer.MIN_VALUE;
+ }
+ }
+
+ @Override
+ public int getLst() {
+ if (mayBePerformed()) {
+ return start.getUB();
+ } else {
+ return Integer.MIN_VALUE;
+ }
+ }
+
+ @Override
+ public int getEct() {
+ if (mayBePerformed()) {
+ return end.getLB();
+ } else {
+ return Integer.MAX_VALUE;
+ }
+ }
+
+ @Override
+ public int getLct() {
+ if (mayBePerformed()) {
+ return end.getUB();
+ } else {
+ return Integer.MAX_VALUE;
+ }
+ }
+
+ @Override
+ public int getMinDuration() {
+ if (mayBePerformed()) {
+ return duration.getLB();
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public int getMaxDuration() {
+ if (mayBePerformed()) {
+ return duration.getUB();
+ } else {
+ return Integer.MAX_VALUE;
+ }
+ }
+
+ @Override
+ public boolean forceToBePerformed(ICause cause) throws ContradictionException {
+ return performed.updateLowerBound(1, cause);
+ }
+
+ @Override
+ public boolean forceToBePerformed(ICause cause, Reason reason) throws ContradictionException {
+ return performed.updateLowerBound(1, cause, reason);
+ }
+
+ @Override
+ public boolean forceToBeOptional(ICause cause) throws ContradictionException {
+ return performed.updateUpperBound(0, cause);
+ }
+
+ @Override
+ public boolean forceToBeOptional(ICause cause, Reason reason) throws ContradictionException {
+ return performed.updateUpperBound(0, cause, reason);
+ }
+
+ @Override
+ public boolean updateEst(int est, ICause cause) throws ContradictionException {
+ if (mayBePerformed()) {
+ if (est <= start.getUB()) {
+ return super.updateEst(est, cause);
+ } else {
+ performed.updateUpperBound(0, cause);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean updateLst(int lst, ICause cause) throws ContradictionException {
+ if (mayBePerformed()) {
+ if (lst >= start.getLB()) {
+ return super.updateLst(lst, cause);
+ } else {
+ performed.updateUpperBound(0, cause);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean updateEct(int ect, ICause cause) throws ContradictionException {
+ if (mayBePerformed()) {
+ if (ect <= end.getUB()) {
+ return super.updateEct(ect, cause);
+ } else {
+ performed.updateUpperBound(0, cause);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean updateLct(int lct, ICause cause) throws ContradictionException {
+ if (mayBePerformed()) {
+ if (lct >= end.getLB()) {
+ return super.updateLct(lct, cause);
+ } else {
+ performed.updateUpperBound(0, cause);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean updateMinDuration(int minDuration, ICause cause) throws ContradictionException {
+ if (mayBePerformed()) {
+ if (minDuration <= duration.getUB()) {
+ return super.updateMinDuration(minDuration, cause);
+ } else {
+ performed.updateUpperBound(0, cause);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean updateMaxDuration(int maxDuration, ICause cause) throws ContradictionException {
+ if (mayBePerformed()) {
+ if (maxDuration >= duration.getLB()) {
+ return super.updateMaxDuration(maxDuration, cause);
+ } else {
+ performed.updateUpperBound(0, cause);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean updateDuration(int minDuration, int maxDuration, ICause cause) throws ContradictionException {
+ if (mayBePerformed()) {
+ if (minDuration <= maxDuration && minDuration <= duration.getUB() && maxDuration >= duration.getLB()) {
+ return super.updateDuration(minDuration, maxDuration, cause);
+ } else {
+ performed.updateUpperBound(0, cause);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean instantiateStartAt(int t, ICause cause) throws ContradictionException {
+ if (mayBePerformed()) {
+ if (start.getLB() <= t && t <= start.getUB()) {
+ return super.instantiateStartAt(t, cause);
+ } else {
+ performed.updateUpperBound(0, cause);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean instantiateEndAt(int t, ICause cause) throws ContradictionException {
+ if (mayBePerformed()) {
+ if (end.getLB() <= t && t <= end.getUB()) {
+ return super.instantiateEndAt(t, cause);
+ } else {
+ performed.updateUpperBound(0, cause);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean updateEst(int est, ICause cause, Reason reason) throws ContradictionException {
+ if (mayBePerformed()) {
+ if (est <= start.getUB()) {
+ return super.updateEst(est, cause, reason);
+ } else {
+ performed.updateUpperBound(0, cause, reason);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean updateLst(int lst, ICause cause, Reason reason) throws ContradictionException {
+ if (mayBePerformed()) {
+ if (lst >= start.getLB()) {
+ return super.updateLst(lst, cause, reason);
+ } else {
+ performed.updateUpperBound(0, cause, reason);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean updateEct(int ect, ICause cause, Reason reason) throws ContradictionException {
+ if (mayBePerformed()) {
+ if (ect <= end.getUB()) {
+ return super.updateEct(ect, cause, reason);
+ } else {
+ performed.updateUpperBound(0, cause, reason);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean updateLct(int lct, ICause cause, Reason reason) throws ContradictionException {
+ if (mayBePerformed()) {
+ if (lct >= end.getLB()) {
+ return super.updateLct(lct, cause, reason);
+ } else {
+ performed.updateUpperBound(0, cause, reason);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean updateMinDuration(int minDuration, ICause cause, Reason reason) throws ContradictionException {
+ if (mayBePerformed()) {
+ if (minDuration <= duration.getUB()) {
+ return super.updateMinDuration(minDuration, cause, reason);
+ } else {
+ performed.updateUpperBound(0, cause, reason);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean updateMaxDuration(int maxDuration, ICause cause, Reason reason) throws ContradictionException {
+ if (mayBePerformed()) {
+ if (maxDuration >= duration.getLB()) {
+ return super.updateMaxDuration(maxDuration, cause, reason);
+ } else {
+ performed.updateUpperBound(0, cause, reason);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean updateDuration(int minDuration, int maxDuration, ICause cause, Reason reason) throws ContradictionException {
+ if (mayBePerformed()) {
+ if (minDuration <= maxDuration && minDuration <= duration.getUB() && maxDuration >= duration.getLB()) {
+ return super.updateDuration(minDuration, maxDuration, cause, reason);
+ } else {
+ performed.updateUpperBound(0, cause, reason);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean instantiateStartAt(int t, ICause cause, Reason reason) throws ContradictionException {
+ if (mayBePerformed()) {
+ if (start.getLB() <= t && t <= start.getUB()) {
+ return super.instantiateStartAt(t, cause, reason);
+ } else {
+ performed.updateUpperBound(0, cause, reason);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean instantiateEndAt(int t, ICause cause, Reason reason) throws ContradictionException {
+ if (mayBePerformed()) {
+ if (end.getLB() <= t && t <= end.getUB()) {
+ return super.instantiateEndAt(t, cause, reason);
+ } else {
+ performed.updateUpperBound(0, cause, reason);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Task getMirror() {
+ if (mirror == null) {
+ mirror = new OptionalTask(end.neg().intVar(), duration, start.neg().intVar(), performed);
+ }
+ return mirror;
+ }
+
+ @Override
+ public String toString() {
+ return "OptionalTask[" +
+ "start=" + start +
+ ", duration=" + duration +
+ ", end=" + end +
+ ", performed=" + performed +
+ ']';
+ }
+}
diff --git a/solver/src/main/java/org/chocosolver/solver/variables/Task.java b/solver/src/main/java/org/chocosolver/solver/variables/Task.java
index 7d8b279225..d72b0c31d8 100644
--- a/solver/src/main/java/org/chocosolver/solver/variables/Task.java
+++ b/solver/src/main/java/org/chocosolver/solver/variables/Task.java
@@ -9,33 +9,41 @@
*/
package org.chocosolver.solver.variables;
+import org.chocosolver.solver.ICause;
import org.chocosolver.sat.Reason;
import org.chocosolver.solver.Model;
+import org.chocosolver.solver.constraints.Constraint;
+import org.chocosolver.solver.constraints.Explained;
+import org.chocosolver.solver.constraints.Propagator;
+import org.chocosolver.solver.constraints.PropagatorPriority;
import org.chocosolver.solver.exception.ContradictionException;
-import org.chocosolver.solver.variables.events.IEventType;
-import org.chocosolver.solver.variables.events.IntEventType;
+import org.chocosolver.solver.variables.events.PropagatorEventType;
import org.chocosolver.solver.variables.view.integer.IntAffineView;
+import org.chocosolver.util.ESat;
-import java.util.ArrayList;
import java.util.function.Consumer;
/**
* Container representing a task:
* It ensures that: start + duration = end
*
- * @author Jean-Guillaume Fages
- * @since 04/02/2013
+ * @author Arthur Godet
+ * @since 25/11/2023
*/
-public class Task {
+@Explained
+public class Task extends Propagator
*
@@ -83,11 +80,6 @@ public final class BitsetIntVarImpl extends AbstractVariable implements IntVar {
*/
private DisposableRangeIterator _riterator;
- /**
- * Value iterator allowing for(int i:this) loops
- */
- private IntVarValueIterator _javaIterator;
-
/**
* Create an enumerated IntVar based on a bitset
*
@@ -585,11 +577,6 @@ public int getDomainSize() {
return SIZE.get();
}
- @Override
- public int getRange() {
- return getUB() - getLB() + 1;
- }
-
@Override
public int nextValue(int aValue) {
aValue -= OFFSET;
@@ -817,13 +804,4 @@ public int max() {
}
return _riterator;
}
-
- @Override
- public Iterator
*
@@ -56,10 +54,6 @@ public class BoolVarImpl extends AbstractVariable implements BoolVar {
* To iterate over ranges
*/
private DisposableRangeIterator _riterator;
- /**
- * Value iterator allowing for(int i:this) loops
- */
- private IntVarValueIterator _javaIterator;
/**
* Set to true if this variable reacts is associated with at least one propagator which reacts
* on value removal
@@ -250,7 +244,7 @@ public boolean updateBounds(int lb, int ub, ICause cause) throws ContradictionEx
}
return hasChanged;
}
-
+
@Override
public boolean isInstantiated() {
return mValue < kUNDEF;
@@ -270,7 +264,7 @@ public boolean contains(int aValue) {
}
@Override
- public int getValue() throws IllegalStateException{
+ public int getValue() throws IllegalStateException {
if (!isInstantiated()) {
throw new IllegalStateException("getValue() can be only called on instantiated variable. " +
name + " is not instantiated");
@@ -319,6 +313,7 @@ public int getDomainSize() {
@Override
public int getRange() {
+ // For boolean variables, the range is always equal to the domain size
return getDomainSize();
}
@@ -442,15 +437,6 @@ public DisposableRangeIterator getRangeIterator(boolean bottomUp) {
return _riterator;
}
- @Override
- public Iteratorconstante}.
*
@@ -397,10 +389,4 @@ public int max() {
return _riterator;
}
- @Override
- public Iterator
*
@@ -62,11 +60,6 @@ public final class IntervalIntVarImpl extends AbstractVariable implements IntVar
*/
private DisposableRangeIterator _riterator;
- /**
- * Value iterator allowing for(int i:this) loops
- */
- private IntVarValueIterator _javaIterator;
-
/**
* Create a bounded domain IntVar : [min,max]
*
@@ -348,8 +341,8 @@ public boolean contains(int aValue) {
}
@Override
- public int getValue() throws IllegalStateException{
- if(!isInstantiated()) {
+ public int getValue() throws IllegalStateException {
+ if (!isInstantiated()) {
throw new IllegalStateException("getValue() can be only called on instantiated variable. " +
name + " is not instantiated");
}
@@ -383,6 +376,7 @@ public int getDomainSize() {
@Override
public int getRange() {
+ // For bounded variables, the range is always equal to the domain size
return getDomainSize();
}
@@ -506,13 +500,4 @@ public DisposableRangeIterator getRangeIterator(boolean bottomUp) {
return _riterator;
}
- @Override
- public Iterator extends AbstractView implements Bo
* Range iterator
*/
protected DisposableRangeIterator _riterator;
- /**
- * Value iterator allowing for(int i:this) loops
- */
- private final IntVarValueIterator _javaIterator = new IntVarValueIterator(this);
public BoolSetView(int v, S setVar) {
super("boolSetView[" + v + " in " + setVar.getName() + "]", setVar);
@@ -167,7 +161,8 @@ public int getDomainSize() {
@Override
public int getRange() {
- return getUB() - getLB();
+ // For boolean variables, the range is always equal to the domain size
+ return getDomainSize();
}
@Override
@@ -205,7 +200,7 @@ public final int getTypeAndKind() {
}
@Override
- public final int getValue() throws IllegalStateException{
+ public final int getValue() throws IllegalStateException {
if (!isInstantiated()) {
throw new IllegalStateException("getValue() can be only called on instantiated variable. " +
name + " is not instantiated");
@@ -401,9 +396,4 @@ public final DisposableRangeIterator getRangeIterator(boolean bottomUp) {
return _riterator;
}
- @Override
- public Iteratoritem has been removed
- * (may require to update iterator structure)
- * @param item removed value
- */
- default void notifyRemoving(int item){
- // nothing to do by default
- }
-
/**
* Returns the next int in the iteration.
*
diff --git a/solver/src/main/java/org/chocosolver/util/objects/setDataStructures/linkedlist/Set_LinkedList.java b/solver/src/main/java/org/chocosolver/util/objects/setDataStructures/linkedlist/Set_LinkedList.java
index f7fbb45c9c..c222ce71a9 100644
--- a/solver/src/main/java/org/chocosolver/util/objects/setDataStructures/linkedlist/Set_LinkedList.java
+++ b/solver/src/main/java/org/chocosolver/util/objects/setDataStructures/linkedlist/Set_LinkedList.java
@@ -10,6 +10,7 @@
package org.chocosolver.util.objects.setDataStructures.linkedlist;
import org.chocosolver.util.objects.setDataStructures.AbstractSet;
+import org.chocosolver.util.objects.setDataStructures.FixedIntArrayIterator;
import org.chocosolver.util.objects.setDataStructures.ISetIterator;
import org.chocosolver.util.objects.setDataStructures.SetType;
@@ -29,10 +30,11 @@ public class Set_LinkedList extends AbstractSet {
// VARIABLE
//***********************************************************************************
- private IntCell first, last;
+ private IntCell first;
+ private IntCell last;
private int size;
private IntCell poolGC;
- private final ISetIterator iter = newIterator();
+ private final ListSetIterator iter = new ListSetIterator();
//***********************************************************************************
// METHODS
@@ -46,8 +48,8 @@ public int size() {
@Override
public boolean contains(int element) {
IntCell cell = first;
- while(cell != null){
- if(cell.element == element){
+ while (cell != null && cell != poolGC) {
+ if (cell.element == element) {
return true;
}
cell = cell.next;
@@ -57,20 +59,28 @@ public boolean contains(int element) {
@Override
public boolean add(int element) {
- if(contains(element)){
+ if (contains(element)) {
return false;
}
+ // add at end
if (poolGC == null) {
- first = new IntCell(element, first);
+ IntCell newCell = new IntCell(element, null);
+ if (last != null) {
+ last.next = newCell;
+ }
+ last = newCell;
} else {
IntCell recycled = poolGC;
poolGC = poolGC.next;
- recycled.init(element, first);
- first = recycled;
+ recycled.init(element, null);
+ if (last != null) {
+ last.next = recycled;
+ }
+ last = recycled;
}
- if(last==null){
- assert size==0;
- last=first;
+ if (first == null) {
+ assert size == 0;
+ first = last;
}
this.size++;
notifyObservingElementAdded(element);
@@ -79,23 +89,27 @@ public boolean add(int element) {
@Override
public boolean remove(int element) {
- if(first == null){
+ if (first == null) {
return false;
- } else if(first.element == element){
+ } else if (first.element == element) {
iter.notifyRemoving(element);
first = first.next;
- if(first==null)last=null;
+ if (first == null) {
+ last = null;
+ }
size--;
notifyObservingElementRemoved(element);
return true;
- }else {
+ } else {
IntCell previous = first;
IntCell current = first.next;
while (current != null) {
if (current.element == element) {
iter.notifyRemoving(element);
previous.next = current.next;
- if(previous.next==null) last = previous;
+ if (previous.next == null) {
+ last = previous;
+ }
current.next = poolGC;
poolGC = current;
size--;
@@ -118,17 +132,17 @@ public void clear() {
first = null;
last = null;
size = 0;
- notifyObservingCleared();
+ notifyObservingCleared();
}
@Override
public int min() {
- if(isEmpty()) throw new IllegalStateException("cannot find minimum of an empty set");
+ if (isEmpty()) throw new IllegalStateException("cannot find minimum of an empty set");
IntCell current = first;
int min = current.element;
- while(current.next!=null){
+ while (current.next != null) {
current = current.next;
- if(min > current.element){
+ if (min > current.element) {
min = current.element;
}
}
@@ -137,12 +151,12 @@ public int min() {
@Override
public int max() {
- if(isEmpty()) throw new IllegalStateException("cannot find maximum of an empty set");
+ if (isEmpty()) throw new IllegalStateException("cannot find maximum of an empty set");
IntCell current = first;
int max = current.element;
- while(current.next!=null){
+ while (current.next != null) {
current = current.next;
- if(max < current.element){
+ if (max < current.element) {
max = current.element;
}
}
@@ -150,54 +164,42 @@ public int max() {
}
@Override
- public SetType getSetType(){
+ public SetType getSetType() {
return SetType.LINKED_LIST;
}
+ @Override
+ public int[] toArray() {
+ int[] copy = new int[size];
+ IntCell current = first;
+ int idx = 0;
+ while (current != null) {
+ copy[idx++] = current.element;
+ current = current.next;
+ }
+ return copy;
+ }
+
//***********************************************************************************
// ITERATOR
//***********************************************************************************
@Override
- public ISetIterator iterator(){
+ public ISetIterator iterator() {
iter.reset();
return iter;
}
@Override
- public ISetIterator newIterator(){
- return new ISetIterator() {
- int r =0;
- private IntCell nextCell = first;
- @Override
- public void reset() {
- nextCell = first;
- r++;
- }
- @Override
- public void notifyRemoving(int item) {
- if(nextCell != null && nextCell.element == item){
- nextCell = nextCell.next;
- }
- }
- @Override
- public boolean hasNext() {
- return nextCell != null;
- }
- @Override
- public int nextInt() {
- int e = nextCell.element;
- nextCell = nextCell.next;
- return e;
- }
- };
+ public ISetIterator newIterator() {
+ return new FixedIntArrayIterator(toArray());
}
//***********************************************************************************
// STRUCTURE
//***********************************************************************************
- private class IntCell {
+ private static class IntCell {
private int element;
private IntCell next;
@@ -212,8 +214,36 @@ public void init(int element, IntCell next) {
}
@Override
- public String toString(){
- return element+"";
+ public String toString() {
+ return element + "";
+ }
+ }
+
+ private class ListSetIterator implements ISetIterator {
+
+ private IntCell nextCell = first;
+
+ @Override
+ public void reset() {
+ nextCell = first;
+ }
+
+ public void notifyRemoving(int item) {
+ if (nextCell != null && nextCell.element == item) {
+ nextCell = nextCell.next;
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ return nextCell != null && size() > 0;
+ }
+
+ @Override
+ public int nextInt() {
+ int e = nextCell.element;
+ nextCell = nextCell.next;
+ return e;
}
}
}
diff --git a/solver/src/main/java/org/chocosolver/util/objects/setDataStructures/swapList/Set_Std_Swap2.java b/solver/src/main/java/org/chocosolver/util/objects/setDataStructures/swapList/Set_Std_Swap2.java
index 56728ad0d5..1114c89b52 100644
--- a/solver/src/main/java/org/chocosolver/util/objects/setDataStructures/swapList/Set_Std_Swap2.java
+++ b/solver/src/main/java/org/chocosolver/util/objects/setDataStructures/swapList/Set_Std_Swap2.java
@@ -9,12 +9,8 @@
*/
package org.chocosolver.util.objects.setDataStructures.swapList;
-import gnu.trove.list.array.TIntArrayList;
import org.chocosolver.memory.IEnvironment;
import org.chocosolver.memory.IStateInt;
-import org.chocosolver.util.objects.setDataStructures.AbstractSet;
-import org.chocosolver.util.objects.setDataStructures.ISetIterator;
-import org.chocosolver.util.objects.setDataStructures.SetType;
/**
* Set of integers based on BipartiteSet implementation for small sets (arraylist inside)
@@ -22,16 +18,13 @@
*
* @author : Charles Prud'homme, Jean-Guillaume FAGES (fix remove)
*/
-public class Set_Std_Swap2 extends AbstractSet {
+public class Set_Std_Swap2 extends Set_Swap2 {
//***********************************************************************************
// VARIABLES
//***********************************************************************************
- protected IStateInt size;
- protected TIntArrayList values;
- private final ISetIterator iter = newIterator();
-
+ private final IStateInt size;
//***********************************************************************************
// CONSTRUCTOR
@@ -42,116 +35,26 @@ public class Set_Std_Swap2 extends AbstractSet {
* @param e backtracking environment
*/
public Set_Std_Swap2(IEnvironment e){
+ super();
size = e.makeInt(0);
- values = new TIntArrayList(4);
}
//***********************************************************************************
// METHODS
//***********************************************************************************
- @Override
- public boolean add(int element) {
- if(!contains(element)){
- int pos = size.add(1);
- values.insert(pos - 1, element);
- notifyObservingElementAdded(element);
- return true;
- }
- return false;
- }
-
- @Override
- public boolean remove(int element) {
- int pos = values.indexOf(element);
- int s = size();
- if(pos > -1 && pos < s){
- iter.notifyRemoving(element);
- s--;
- int t = values.get(s);
- values.set(pos, t);
- values.set(s, element);
- size.add(-1);
- notifyObservingElementRemoved(element);
- return true;
- }else return false;
- }
-
- @Override
- public boolean contains(int element) {
- int pos = values.indexOf(element);
- return pos > -1 && pos < size();
- }
-
@Override
public int size() {
return size.get();
}
@Override
- public void clear() {
- size.set(0);
- notifyObservingCleared();
- }
-
- @Override
- public int min() {
- if(isEmpty()) throw new IllegalStateException("cannot find minimum of an empty set");
- int min = values.get(0);
- for(int i = 1; i< size(); i++){
- if(min > values.get(i)){
- min = values.get(i);
- }
- }
- return min;
- }
-
- @Override
- public int max() {
- if(isEmpty()) throw new IllegalStateException("cannot find maximum of an empty set");
- int max = values.get(0);
- for(int i = 1; i< size(); i++){
- if(max < values.get(i)){
- max = values.get(i);
- }
- }
- return max;
- }
-
- @Override
- public SetType getSetType() {
- return SetType.SMALLBIPARTITESET;
- }
-
- @Override
- public ISetIterator iterator(){
- iter.reset();
- return iter;
+ protected void setSize(int s) {
+ size.set(s);
}
@Override
- public ISetIterator newIterator(){
- return new ISetIterator() {
- private int idx;
- @Override
- public void reset() {
- idx = 0;
- }
- @Override
- public void notifyRemoving(int item) {
- if(idx>0 && item == values.get(idx-1)){
- idx--;
- }
- }
- @Override
- public boolean hasNext() {
- return idx < size();
- }
- @Override
- public int nextInt() {
- idx ++;
- return values.get(idx-1);
- }
- };
+ protected void addSize(int delta) {
+ size.add(delta);
}
}
diff --git a/solver/src/main/java/org/chocosolver/util/objects/setDataStructures/swapList/Set_Swap.java b/solver/src/main/java/org/chocosolver/util/objects/setDataStructures/swapList/Set_Swap.java
index b2107e08ad..ae765596ff 100644
--- a/solver/src/main/java/org/chocosolver/util/objects/setDataStructures/swapList/Set_Swap.java
+++ b/solver/src/main/java/org/chocosolver/util/objects/setDataStructures/swapList/Set_Swap.java
@@ -10,10 +10,9 @@
package org.chocosolver.util.objects.setDataStructures.swapList;
import org.chocosolver.solver.exception.SolverException;
-import org.chocosolver.util.objects.setDataStructures.AbstractSet;
-import org.chocosolver.util.objects.setDataStructures.ISet;
-import org.chocosolver.util.objects.setDataStructures.ISetIterator;
-import org.chocosolver.util.objects.setDataStructures.SetType;
+import org.chocosolver.util.objects.setDataStructures.*;
+
+import java.util.Arrays;
/**
* Bipartite set of integers:
@@ -33,8 +32,9 @@ public class Set_Swap extends AbstractSet implements ISet.WithOffset {
private int size;
private final int mapOffset;
- private int[] values, map;
- private final ISetIterator iter = newIterator();
+ private int[] values;
+ private int[] map;
+ private final MyISetIterator iter = new MyISetIterator();
//***********************************************************************************
// CONSTRUCTOR
@@ -44,12 +44,12 @@ public class Set_Swap extends AbstractSet implements ISet.WithOffset {
* Creates an empty bipartite set having numbers greater or equal than offSet (possibly < 0)
* @param offSet smallest allowed value in this set (possibly < 0)
*/
- public Set_Swap(int offSet){
+ public Set_Swap(int offSet) {
mapOffset = offSet;
size = 0;
values = new int[16];
map = new int[16];
- for(int i=0;i> detectIntEqualities(Model model) {
for (IntVar i : ivars) {
id2pos.put(i.getId(), pos++);
}
- // 3. create a undirected graph to store equality constraints
+ // 3. create an undirected graph to store equality constraints
UndirectedGraph g = new UndirectedGraph(pos, SetType.LINKED_LIST, false);
+ int[] nbEqs = {0, 0};
Arrays.stream(model.getCstrs())
.filter(ARITHM.and(POSTED).and(EQXY))
- .map(c -> c.getPropagator(0))
- .forEach(p -> {
+ .forEach(c -> {
+ Propagator> p = c.getPropagator(0);
g.addNode(id2pos.get(p.getVar(0).getId()));
g.addNode(id2pos.get(p.getVar(1).getId()));
g.addEdge(id2pos.get(p.getVar(0).getId()),
id2pos.get(p.getVar(1).getId()));
+ model.unpost(c);
+ nbEqs[0]++;
});
// 4. Prepare graph DFS for all unlabelled nodes
List
> components = new ArrayList<>();
@@ -94,22 +111,92 @@ public static List
> detectIntEqualities(Model model) {
components.add(component);
}
}
- // 5. Remove all equality constraints
- model.unpost(Arrays.stream(model.getCstrs())
- .filter(ARITHM.and(POSTED).and(EQXY))
- .toArray(Constraint[]::new));
- // 6 Post new equality constraints
+ // 5 Post new equality constraints
+ int nbAllEqs = 0;
for (List
ConstraintsName.ARITHM
+ * Constraint.Status.POSTED
+ * instanceof PropEqualX_Y
+ *