Skip to content

Commit

Permalink
Make preprocessing possible (disabled by default):
Browse files Browse the repository at this point in the history
`Settings.init().setTimeLimitForPreprocessing(2_000)`
  • Loading branch information
cprudhom committed Jul 3, 2024
1 parent aa3a776 commit 850b4af
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 6 deletions.
23 changes: 23 additions & 0 deletions solver/src/main/java/org/chocosolver/solver/Settings.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ public class Settings {

private int maxTupleSizeForSubstitution = 10_000;

private int timeLimitForPreprocessing = -1;

private boolean sortPropagatorActivationWRTPriority = true;

private int maxPropagatorPriority = PropagatorPriority.VERY_SLOW.getValue();
Expand Down Expand Up @@ -289,6 +291,27 @@ public Settings setMaxTupleSizeForSubstitution(int maxTupleSizeForSubstitution)
return this;
}

/**
* @return the time allocated for the preprocessing
*/
public long getTimeLimitForPreprocessing() {
return timeLimitForPreprocessing;
}

/**
* Set the time allocated for the preprocessing step.
* If the time limit is reached, the preprocessing is stopped and the resolution starts.
* Set a negative value to disable the time limit.
*
* @param timeLimitForPreprocessing time limit for Strong Arc Consistency (in milliseconds)
* @return the current instance
* @see org.chocosolver.solver.Solver#preprocessing(long)
*/
public Settings setTimeLimitForPreprocessing(int timeLimitForPreprocessing) {
this.timeLimitForPreprocessing = timeLimitForPreprocessing;
return this;
}


/**
* @return {@code true} if propagators are sorted wrt their priority on initial activation.
Expand Down
71 changes: 65 additions & 6 deletions solver/src/main/java/org/chocosolver/solver/Solver.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import org.chocosolver.solver.variables.Variable;
import org.chocosolver.util.ESat;
import org.chocosolver.util.criteria.Criterion;
import org.chocosolver.util.iterators.DisposableValueIterator;
import org.chocosolver.util.logger.ANSILogger;
import org.chocosolver.util.logger.Logger;

Expand Down Expand Up @@ -439,6 +440,7 @@ protected boolean initialize() {
defaultSearch = true;
mModel.getSettings().makeDefaultSearch(mModel);
}
preprocessing(getModel().getSettings().getTimeLimitForPreprocessing());
if (completeSearch && !defaultSearch) {
BlackBoxConfigurator bb = BlackBoxConfigurator.init();
bb.complete(mModel, M.getStrategy());
Expand All @@ -460,6 +462,62 @@ protected boolean initialize() {
return ok;
}

/**
* This method is called after the initial propagation and before the search loop starts.
* It sequentially applies Arc Consistency on every combination of (variable, value).
* If a value is not supported by any other variable, it is removed from the domain of the variable.
* The method ends when the time limit is reached or when all combination have been checked.
* @implSpec A first propagation must have been done before calling this method.
*/
public void preprocessing(long timeLimitInMS) {
if(!getEngine().isInitialized()){
throw new SolverException("A call to solver.propagate() must be done before calling solver.preprocessing()");
}
if (getModel().getSettings().warnUser()) {
logger.white().printf("Running preprocessing step (%dms).\n", timeLimitInMS);
}
long tl = System.currentTimeMillis() + timeLimitInMS;
IntVar[] ivars = mModel.retrieveIntVars(true);
loop:
for (int i = 0; i < ivars.length; i++) {
IntVar v = ivars[i];
if (!v.isInstantiated()) { // if the variable is not instantiated
DisposableValueIterator it = v.getValueIterator(true);
while (it.hasNext()) {
if (System.currentTimeMillis() > tl) {
break loop;
}
int a = it.next();
if(!hasSupport(v, a)){
try {
v.removeValue(a, Cause.Null);
if (getModel().getSettings().warnUser()) {
logger.white().printf("Preprocessing removed value %d from %s\n", a, v.getName());
}
} catch (ContradictionException e) {
throw new SolverException("Preprocessing failed");
}
}
}
it.dispose();
}
}
}

private boolean hasSupport(IntVar var, int val) {
mModel.getEnvironment().worldPush();
try {
var.instantiateTo(val, Cause.Null);
mModel.getSolver().getEngine().propagate();
return true;
} catch (ContradictionException e) {
mModel.getSolver().getEngine().flush();
return false;
} finally {
mModel.getEnvironment().worldPop();
}
}

/**
* Basic propagation:
* <ul>
Expand All @@ -473,6 +531,7 @@ protected boolean initialize() {
* it is bypassed by the learnt unit nogood),</li>
* <li>finally, a fix point is reached.</li>
* </ul>
*
* @throws ContradictionException if failure occurs during propagation
*/
private void doPropagate() throws ContradictionException {
Expand Down Expand Up @@ -1165,7 +1224,7 @@ public void setPropagate(Propagate p) {
* @see #clearRestarter()
*/
public void addRestarter(AbstractRestart restarter) {
if(restarter != AbstractRestart.NO_RESTART) {
if (restarter != AbstractRestart.NO_RESTART) {
restarter.setNext(this.restarter);
this.restarter = restarter;
}
Expand Down Expand Up @@ -1219,13 +1278,13 @@ public void setSearch(AbstractStrategy... strategies) {
"An iteration over it child moves is needed: this.getMove().getChildMoves().");
} else {
strategies = Arrays.stream(strategies).filter(Objects::nonNull)
.flatMap(s -> (s instanceof StrategiesSequencer) ? Arrays.stream(((StrategiesSequencer<?>) s).getStrategies()) : Stream.of(s))
.toArray(AbstractStrategy[]::new);
if(strategies.length == 0){
.flatMap(s -> (s instanceof StrategiesSequencer) ? Arrays.stream(((StrategiesSequencer<?>) s).getStrategies()) : Stream.of(s))
.toArray(AbstractStrategy[]::new);
if (strategies.length == 0) {
M.removeStrategy();
}else if(strategies.length == 1) {
} else if (strategies.length == 1) {
M.setStrategy(strategies[0]);
}else{
} else {
M.setStrategy(Search.sequencer(strategies));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.chocosolver.solver.Settings;
import org.chocosolver.solver.Solver;
import org.chocosolver.solver.constraints.Constraint;
import org.chocosolver.solver.exception.ContradictionException;
import org.chocosolver.solver.propagation.PropagationProfiler;
import org.chocosolver.solver.search.limits.NodeCounter;
import org.chocosolver.solver.search.loop.lns.neighbors.RandomNeighborhood;
Expand All @@ -28,6 +29,7 @@
import org.chocosolver.solver.variables.BoolVar;
import org.chocosolver.solver.variables.IntVar;
import org.chocosolver.util.ProblemMaker;
import org.chocosolver.util.tools.VariableUtils;
import org.testng.Assert;
import org.testng.annotations.Test;

Expand Down Expand Up @@ -433,4 +435,16 @@ public void testSolvingFlow() {
Assert.assertEquals(m.group(2), "155");
Assert.assertEquals(m.group(3), "1024");
}

@Test(groups = "1s")
public void testPreprocessing() throws ContradictionException {
Model model = ProblemMaker.makeNQueenWithBinaryConstraints(4);
Solver solver = model.getSolver();
long before = VariableUtils.domainCardinality(model.retrieveIntVars(true));
Assert.assertEquals(before, 256);
solver.propagate();
solver.preprocessing(2000);
long after = VariableUtils.domainCardinality(model.retrieveIntVars(true));
Assert.assertEquals(after,32);
}
}

0 comments on commit 850b4af

Please sign in to comment.