diff --git a/solver/src/main/java/org/chocosolver/solver/Solver.java b/solver/src/main/java/org/chocosolver/solver/Solver.java index 41097dfa63..0c2859e8b7 100644 --- a/solver/src/main/java/org/chocosolver/solver/Solver.java +++ b/solver/src/main/java/org/chocosolver/solver/Solver.java @@ -21,6 +21,7 @@ import org.chocosolver.solver.propagation.PropagationEngine; import org.chocosolver.solver.search.SearchState; import org.chocosolver.solver.search.limits.ICounter; +import org.chocosolver.solver.search.limits.TimeCounter; import org.chocosolver.solver.search.loop.Reporting; import org.chocosolver.solver.search.loop.learn.Learn; import org.chocosolver.solver.search.loop.learn.LearnNothing; @@ -169,6 +170,8 @@ public enum Action { */ protected List criteria; + protected TimeCounter timeCounter; + /** * Indicates if the default search loop is in use (set to true in that case). */ @@ -1007,11 +1010,21 @@ public boolean hasEndedUnexpectedly() { return mMeasures.getSearchState() == SearchState.KILLED; } + /** + * @return true if the time limit is met. + */ + public boolean isTimeLimitMet() { + if (timeCounter != null) { + return timeCounter.isMet(); + } + return false; + } + /** * @return true if the search loops encountered at least one of the stop criteria declared. */ public boolean isStopCriterionMet() { - boolean ismet = false; + boolean ismet = isTimeLimitMet(); for (int i = 0; i < criteria.size() && !ismet; i++) { ismet = criteria.get(i).isMet(); } @@ -1311,7 +1324,16 @@ public void removeHints() { */ public void addStopCriterion(Criterion... criterion) { if (criterion != null) { - Collections.addAll(criteria, criterion); + for (int i = 0; i < criterion.length; i++) { + if (criterion[i] instanceof TimeCounter) { + TimeCounter tc = (TimeCounter) criterion[i]; + if (timeCounter == null || timeCounter.getLimitValue() > tc.getLimitValue()) { + timeCounter = tc; + } + } else { + criteria.add(criterion[i]); + } + } } } diff --git a/solver/src/main/java/org/chocosolver/solver/propagation/PropagationEngine.java b/solver/src/main/java/org/chocosolver/solver/propagation/PropagationEngine.java index 7b94aff6d9..f115cbdb63 100644 --- a/solver/src/main/java/org/chocosolver/solver/propagation/PropagationEngine.java +++ b/solver/src/main/java/org/chocosolver/solver/propagation/PropagationEngine.java @@ -189,6 +189,9 @@ public void propagate() throws ContradictionException { manageModifications(); for (int i = nextNotEmpty(); i > -1; i = nextNotEmpty()) { assert !pro_queue[i].isEmpty() : "try to pop a propagator from an empty queue"; + if (model.getSolver().isTimeLimitMet()) { + return; + } lastProp = pro_queue[i].pollFirst(); if (pro_queue[i].isEmpty()) { notEmpty &= ~(1 << i); diff --git a/solver/src/test/java/org/chocosolver/solver/LimitsTest.java b/solver/src/test/java/org/chocosolver/solver/LimitsTest.java index 92986894e6..607416eb56 100644 --- a/solver/src/test/java/org/chocosolver/solver/LimitsTest.java +++ b/solver/src/test/java/org/chocosolver/solver/LimitsTest.java @@ -9,12 +9,15 @@ */ package org.chocosolver.solver; +import org.chocosolver.solver.exception.SolverException; +import org.chocosolver.solver.variables.IntVar; import org.chocosolver.util.tools.TimeUtils; import org.testng.Assert; import org.testng.annotations.Test; import static org.chocosolver.util.ProblemMaker.makeNQueenWithBinaryConstraints; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; /** @@ -35,6 +38,35 @@ public void testTime() { assertTrue(tl - (tl * 5 / 100) <= tc && tc <= tl + (tl * 5 / 100), tl + " vs. " + tc); } + @Test(groups="1s", timeOut=60000) + public void testTime2() throws SolverException { + Model model = new Model(); + + IntVar v7 = model.intVar("@v7", IntVar.MIN_INT_BOUND, IntVar.MAX_INT_BOUND, true); + + model.post( + model.arithm(v7, ">", v7), + model.arithm(v7, "<", v7) + ); + // TODO : such a simple case should be detected within the constraint declaration + // TODO : this test might need to be changed if better model analysis is done during model declaration + + Solver solver = model.getSolver(); + solver.limitTime(250); + + long start = System.currentTimeMillis(); + boolean solved = solver.solve(); + long took = System.currentTimeMillis() - start; + + assertFalse(solved); + assertEquals(solver.getNodeCount(), 1); + assertEquals(solver.getBackTrackCount(), 0); + assertEquals(solver.getFailCount(), 0); + assertEquals(solver.getSolutionCount(), 0); + assertTrue(solver.isStopCriterionMet()); + assertTrue(took <= 1000); // less than 1 second + } + @Test(groups="1s", timeOut=60000) public void testNode() { Model s = makeNQueenWithBinaryConstraints(12);