From 0986161e2efdeed4e08b9e977cd45395447fa18a Mon Sep 17 00:00:00 2001 From: cprudhom Date: Fri, 20 Oct 2023 08:50:43 +0200 Subject: [PATCH] Remove randomness by default of BBox strat + add tie breaker (that can be random)+ code cleanup --- .../org/chocosolver/parser/RegParser.java | 11 +- .../chocosolver/parser/flatzinc/Flatzinc.java | 53 +++++--- .../flatzinc/ast/searches/IntSearch.java | 2 +- .../parser/handlers/VarSelHandler.java | 1 + .../org/chocosolver/parser/xcsp/XCSP.java | 53 +++++--- .../org/chocosolver/parser/RegParserTest.java | 2 +- .../chocosolver/solver/ParallelPortfolio.java | 18 +-- .../search/strategy/BlackBoxConfigurator.java | 11 +- .../solver/search/strategy/Search.java | 16 ++- .../solver/search/strategy/SearchParams.java | 33 ++++- ...AbstractFailureBasedVariableSelector.java} | 124 +++--------------- .../AbstractScoreBasedValueSelector.java | 110 ++++++++++++++++ .../variables/ConflictHistorySearch.java | 57 ++++---- .../selectors/variables/DomOverWDeg.java | 45 +++---- .../selectors/variables/DomOverWDegRef.java | 22 ++-- .../solver/search/ObjectiveTest.java | 2 +- .../solver/search/loop/LNSTest.java | 4 +- 17 files changed, 330 insertions(+), 234 deletions(-) rename solver/src/main/java/org/chocosolver/solver/search/strategy/selectors/variables/{AbstractCriterionBasedVariableSelector.java => AbstractFailureBasedVariableSelector.java} (68%) create mode 100644 solver/src/main/java/org/chocosolver/solver/search/strategy/selectors/variables/AbstractScoreBasedValueSelector.java diff --git a/parsers/src/main/java/org/chocosolver/parser/RegParser.java b/parsers/src/main/java/org/chocosolver/parser/RegParser.java index 9f22b43a44..9b9ca9d06c 100644 --- a/parsers/src/main/java/org/chocosolver/parser/RegParser.java +++ b/parsers/src/main/java/org/chocosolver/parser/RegParser.java @@ -92,6 +92,12 @@ public abstract class RegParser implements IParser { usage = "Define the variable heuristic to use.") public SearchParams.VariableSelection varH = SearchParams.VariableSelection.DOMWDEG_CACD; + @SuppressWarnings("FieldMayBeFinal") + @Option(name = "-tie", + forbids = {"-varsel"}, + usage = "Define the variable tie breaker to use with black-box strategies.") + private SearchParams.VariableTieBreaker tie = SearchParams.VariableTieBreaker.SMALLEST_DOMAIN; + @Option(name = "-flush", forbids = {"-varsel"}, usage = "Autoflush weights on black-box strategies (default: 32).") @@ -173,6 +179,9 @@ public abstract class RegParser implements IParser { @Option(name = "-dfx", usage = "Force default explanation algorithm.") public boolean dftexp = false; + @Option(name = "-gpa", usage = "Use the Generating Partial Assignment procedure (default: false).") + public boolean gpa = false; + /** * Default settings to apply */ @@ -254,7 +263,7 @@ public final boolean setUp(String... args) throws SetUpException { System.out.printf("%s\n", Arrays.toString(args)); } if(varsel == null){ - varsel = new SearchParams.VarSelConf(varH, flushRate); + varsel = new SearchParams.VarSelConf(varH, tie, flushRate); } if(valsel == null){ valsel = new SearchParams.ValSelConf(valH, best, bestRate, last); diff --git a/parsers/src/main/java/org/chocosolver/parser/flatzinc/Flatzinc.java b/parsers/src/main/java/org/chocosolver/parser/flatzinc/Flatzinc.java index 89382e098a..fa8a344126 100644 --- a/parsers/src/main/java/org/chocosolver/parser/flatzinc/Flatzinc.java +++ b/parsers/src/main/java/org/chocosolver/parser/flatzinc/Flatzinc.java @@ -209,25 +209,40 @@ public void parse(Model target, Datas data, InputStream is) { @Override public void freesearch(Solver solver) { BlackBoxConfigurator bb = BlackBoxConfigurator.init(); - boolean opt = solver.getObjectiveManager().isOptimization(); - // variable selection - SearchParams.ValSelConf defaultValSel = new SearchParams.ValSelConf( - SearchParams.ValueSelection.MIN, opt, 1, opt); - SearchParams.VarSelConf defaultVarSel = new SearchParams.VarSelConf( - SearchParams.VariableSelection.DOMWDEG, Integer.MAX_VALUE); - bb.setIntVarStrategy((vars) -> defaultVarSel.make().apply(vars, defaultValSel.make().apply(vars[0].getModel()))); - // restart policy - SearchParams.ResConf defaultResConf = new SearchParams.ResConf( - SearchParams.Restart.LUBY, 500, 50_000, true); - bb.setRestartPolicy(defaultResConf.make()); - // other parameters - bb.setNogoodOnRestart(true) - .setRestartOnSolution(true) - .setExcludeObjective(true) - .setExcludeViews(false) - .setMetaStrategy(m -> Search.lastConflict(m, 1)); - if (level.isLoggable(Level.INFO)) { - solver.log().println(bb.toString()); + if (solver.getObjectiveManager().isOptimization()) { + // For COP + SearchParams.ValSelConf defaultValSel = new SearchParams.ValSelConf( + SearchParams.ValueSelection.MIN, true, 16, true); + SearchParams.VarSelConf defaultVarSel = new SearchParams.VarSelConf( + SearchParams.VariableSelection.DOMWDEG_CACD, SearchParams.VariableTieBreaker.SMALLEST_DOMAIN, 32); + bb.setIntVarStrategy((vars) -> defaultVarSel.make().apply(vars, defaultValSel.make().apply(vars[0].getModel()))); + // restart policy + SearchParams.ResConf defaultResConf = new SearchParams.ResConf( + SearchParams.Restart.GEOMETRIC, 5, 1.05, 50_000, true); + bb.setRestartPolicy(defaultResConf.make()); + // complementary settings + bb.setNogoodOnRestart(true) + .setRestartOnSolution(true) + .setExcludeObjective(true) + .setExcludeViews(false) + .setMetaStrategy(m -> Search.lastConflict(m, 4)); + }else{ + // For CSP + SearchParams.ValSelConf defaultValSel = new SearchParams.ValSelConf( + SearchParams.ValueSelection.MIN, false, 16, false); + SearchParams.VarSelConf defaultVarSel = new SearchParams.VarSelConf( + SearchParams.VariableSelection.DOMWDEG, SearchParams.VariableTieBreaker.SMALLEST_DOMAIN, 32); + bb.setIntVarStrategy((vars) -> defaultVarSel.make().apply(vars, defaultValSel.make().apply(vars[0].getModel()))); + // restart policy + SearchParams.ResConf defaultResConf = new SearchParams.ResConf( + SearchParams.Restart.GEOMETRIC, 5, 1.05, 50_000, true); + bb.setRestartPolicy(defaultResConf.make()); + // complementary settings + bb.setNogoodOnRestart(false) + .setRestartOnSolution(false) + .setExcludeObjective(true) + .setExcludeViews(false) + .setMetaStrategy(m -> Search.lastConflict(m, 1)); } bb.make(solver.getModel()); } diff --git a/parsers/src/main/java/org/chocosolver/parser/flatzinc/ast/searches/IntSearch.java b/parsers/src/main/java/org/chocosolver/parser/flatzinc/ast/searches/IntSearch.java index 47d6650de6..ecb2d973fa 100644 --- a/parsers/src/main/java/org/chocosolver/parser/flatzinc/ast/searches/IntSearch.java +++ b/parsers/src/main/java/org/chocosolver/parser/flatzinc/ast/searches/IntSearch.java @@ -61,7 +61,7 @@ private static VariableSelector variableSelector(IntVar[] variables, Var case max_regret: return new MaxRegret(); case dom_w_deg: - return new DomOverWDeg<>(variables, variables[0].getModel().getSeed()); + return new DomOverWDeg<>(variables); default: System.err.println("% No implementation for " + varChoice.name() + ". Set default."); return null; diff --git a/parsers/src/main/java/org/chocosolver/parser/handlers/VarSelHandler.java b/parsers/src/main/java/org/chocosolver/parser/handlers/VarSelHandler.java index b2dba61108..40161ee2e9 100644 --- a/parsers/src/main/java/org/chocosolver/parser/handlers/VarSelHandler.java +++ b/parsers/src/main/java/org/chocosolver/parser/handlers/VarSelHandler.java @@ -48,6 +48,7 @@ protected SearchParams.VarSelConf parse(String argument) throws NumberFormatExce if (pars.length == 3) { return new SearchParams.VarSelConf( SearchParams.VariableSelection.valueOf(pars[0].toUpperCase()), + SearchParams.VariableTieBreaker.valueOf(pars[1].toUpperCase()), Integer.parseInt(pars[2]) ); } diff --git a/parsers/src/main/java/org/chocosolver/parser/xcsp/XCSP.java b/parsers/src/main/java/org/chocosolver/parser/xcsp/XCSP.java index a81c344792..a433e5d4a3 100644 --- a/parsers/src/main/java/org/chocosolver/parser/xcsp/XCSP.java +++ b/parsers/src/main/java/org/chocosolver/parser/xcsp/XCSP.java @@ -142,23 +142,42 @@ public void parse(Model target, XCSPParser parser) throws Exception { @Override public void freesearch(Solver solver) { BlackBoxConfigurator bb = BlackBoxConfigurator.init(); - boolean opt = solver.getObjectiveManager().isOptimization(); - // variable selection - SearchParams.ValSelConf defaultValSel = new SearchParams.ValSelConf( - SearchParams.ValueSelection.MIN, opt, 1, opt); - SearchParams.VarSelConf defaultVarSel = new SearchParams.VarSelConf( - SearchParams.VariableSelection.DOMWDEG, Integer.MAX_VALUE); - bb.setIntVarStrategy((vars) -> defaultVarSel.make().apply(vars, defaultValSel.make().apply(vars[0].getModel()))); - // restart policy - SearchParams.ResConf defaultResConf = new SearchParams.ResConf( - SearchParams.Restart.LUBY, 500, 50_000, true); - bb.setRestartPolicy(defaultResConf.make()); - // other parameters - bb.setNogoodOnRestart(true) - .setRestartOnSolution(true) - .setExcludeObjective(true) - .setExcludeViews(false) - .setMetaStrategy(m -> Search.lastConflict(m, 1)); + if (solver.getObjectiveManager().isOptimization()) { + // For COP + SearchParams.ValSelConf defaultValSel = new SearchParams.ValSelConf( + SearchParams.ValueSelection.MIN, true, 16, true); + SearchParams.VarSelConf defaultVarSel = new SearchParams.VarSelConf( + SearchParams.VariableSelection.DOMWDEG, SearchParams.VariableTieBreaker.SMALLEST_DOMAIN, 32); + bb.setIntVarStrategy((vars) -> defaultVarSel.make().apply(vars, defaultValSel.make().apply(vars[0].getModel()))); + // restart policy + SearchParams.ResConf defaultResConf = new SearchParams.ResConf( + SearchParams.Restart.GEOMETRIC, 10, 1.05, 50_000, true); + bb.setRestartPolicy(defaultResConf.make()); + // complementary settings + bb.setNogoodOnRestart(true) + .setRestartOnSolution(true) + .setExcludeObjective(true) + .setExcludeViews(false) + .setMetaStrategy(m -> Search.lastConflict(m, 1)) + .setRefinedPartialAssignmentGeneration(gpa); + } else { + // For CSP + SearchParams.ValSelConf defaultValSel = new SearchParams.ValSelConf( + SearchParams.ValueSelection.MIN, false, 16, true); + SearchParams.VarSelConf defaultVarSel = new SearchParams.VarSelConf( + SearchParams.VariableSelection.DOMWDEG_CACD, SearchParams.VariableTieBreaker.LARGEST_DOMAIN, 32); + bb.setIntVarStrategy((vars) -> defaultVarSel.make().apply(vars, defaultValSel.make().apply(vars[0].getModel()))); + // restart policy + SearchParams.ResConf defaultResConf = new SearchParams.ResConf( + SearchParams.Restart.GEOMETRIC, 5, 1.05, 50_000, true); + bb.setRestartPolicy(defaultResConf.make()); + // complementary settings + bb.setNogoodOnRestart(true) + .setRestartOnSolution(false) + .setExcludeObjective(true) + .setExcludeViews(false) + .setMetaStrategy(m -> Search.lastConflict(m, 4)); + } if (level.isLoggable(Level.INFO)) { solver.log().println(bb.toString()); } diff --git a/parsers/src/test/java/org/chocosolver/parser/RegParserTest.java b/parsers/src/test/java/org/chocosolver/parser/RegParserTest.java index 654630942d..bea3449834 100644 --- a/parsers/src/test/java/org/chocosolver/parser/RegParserTest.java +++ b/parsers/src/test/java/org/chocosolver/parser/RegParserTest.java @@ -156,7 +156,7 @@ public void testVarsel1() throws CmdLineException { Assert.assertNull(parser.varsel); p.parseArgument("-f", "-varsel", "[CHS,LARGEST_DOMAIN,64]", "/file"); Assert.assertEquals(parser.varsel, new SearchParams.VarSelConf( - SearchParams.VariableSelection.CHS, 64)); + SearchParams.VariableSelection.CHS, SearchParams.VariableTieBreaker.LARGEST_DOMAIN, 64)); } @Test(groups = "1s") diff --git a/solver/src/main/java/org/chocosolver/solver/ParallelPortfolio.java b/solver/src/main/java/org/chocosolver/solver/ParallelPortfolio.java index 4966dee1ce..b5212b8c0a 100644 --- a/solver/src/main/java/org/chocosolver/solver/ParallelPortfolio.java +++ b/solver/src/main/java/org/chocosolver/solver/ParallelPortfolio.java @@ -437,7 +437,7 @@ private void configureModel(int workerID) { SearchParams.ValueSelection.MIN, opt, 16, true); intValSel = intValConf.make(); intVarConf = new SearchParams.VarSelConf( - SearchParams.VariableSelection.DOMWDEG, 32); + SearchParams.VariableSelection.DOMWDEG, SearchParams.VariableTieBreaker.SMALLEST_DOMAIN, 32); intVarSel = intVarConf.make(); bb.setIntVarStrategy((vars) -> intVarSel.apply(vars, intValSel.apply(worker))); bb.setMetaStrategy(m -> Search.lastConflict(m, 2)); @@ -451,7 +451,7 @@ private void configureModel(int workerID) { SearchParams.ValueSelection.MIN, opt, 16, true); intValSel = intValConf.make(); intVarConf = new SearchParams.VarSelConf( - SearchParams.VariableSelection.CHS, 32); + SearchParams.VariableSelection.CHS, SearchParams.VariableTieBreaker.SMALLEST_DOMAIN, 32); intVarSel = intVarConf.make(); bb.setIntVarStrategy((vars) -> intVarSel.apply(vars, intValSel.apply(worker))); bb.setMetaStrategy(m -> Search.lastConflict(m, 2)); @@ -465,7 +465,7 @@ private void configureModel(int workerID) { SearchParams.ValueSelection.MIN, opt, 16, true); intValSel = intValConf.make(); intVarConf = new SearchParams.VarSelConf( - SearchParams.VariableSelection.DOMWDEG_CACD, 32); + SearchParams.VariableSelection.DOMWDEG_CACD, SearchParams.VariableTieBreaker.SMALLEST_DOMAIN, 32); intVarSel = intVarConf.make(); bb.setIntVarStrategy((vars) -> intVarSel.apply(vars, intValSel.apply(worker))); bb.setMetaStrategy(m -> Search.lastConflict(m, 2)); @@ -479,7 +479,7 @@ private void configureModel(int workerID) { SearchParams.ValueSelection.MIN, opt, 16, true); intValSel = intValConf.make(); intVarConf = new SearchParams.VarSelConf( - SearchParams.VariableSelection.FRBA, 32); + SearchParams.VariableSelection.FRBA, SearchParams.VariableTieBreaker.SMALLEST_DOMAIN, 32); intVarSel = intVarConf.make(); bb.setIntVarStrategy((vars) -> intVarSel.apply(vars, intValSel.apply(worker))); bb.setMetaStrategy(m -> Search.lastConflict(m, 2)); @@ -493,7 +493,7 @@ private void configureModel(int workerID) { SearchParams.ValueSelection.MIN, opt, 16, true); intValSel = intValConf.make(); intVarConf = new SearchParams.VarSelConf( - SearchParams.VariableSelection.ACTIVITY, 32); + SearchParams.VariableSelection.ACTIVITY, SearchParams.VariableTieBreaker.LARGEST_DOMAIN, 32); intVarSel = intVarConf.make(); bb.setIntVarStrategy((vars) -> intVarSel.apply(vars, intValSel.apply(worker))); bb.setMetaStrategy(m -> Search.lastConflict(m, 2)); @@ -504,7 +504,7 @@ private void configureModel(int workerID) { SearchParams.ValueSelection.MIN, opt, 16, true); intValSel = intValConf.make(); intVarConf = new SearchParams.VarSelConf( - SearchParams.VariableSelection.DOMWDEG_CACD, 32); + SearchParams.VariableSelection.DOMWDEG_CACD, SearchParams.VariableTieBreaker.LARGEST_DOMAIN, 32); intVarSel = intVarConf.make(); bb.setIntVarStrategy((vars) -> intVarSel.apply(vars, intValSel.apply(worker))); bb.setMetaStrategy(m -> Search.lastConflict(m, 2)); @@ -515,7 +515,7 @@ private void configureModel(int workerID) { SearchParams.ValueSelection.MIN, opt, 16, true); intValSel = intValConf.make(); intVarConf = new SearchParams.VarSelConf( - SearchParams.VariableSelection.DOMWDEG, 32); + SearchParams.VariableSelection.DOMWDEG, SearchParams.VariableTieBreaker.LARGEST_DOMAIN, 32); intVarSel = intVarConf.make(); bb.setIntVarStrategy((vars) -> intVarSel.apply(vars, intValSel.apply(worker))); bb.setMetaStrategy(m -> Search.lastConflict(m, 2)); @@ -529,7 +529,7 @@ private void configureModel(int workerID) { SearchParams.ValueSelection.MIN, opt, 16, true); intValSel = intValConf.make(); intVarConf = new SearchParams.VarSelConf( - SearchParams.VariableSelection.FRBA, 32); + SearchParams.VariableSelection.FRBA, SearchParams.VariableTieBreaker.LARGEST_DOMAIN, 32); intVarSel = intVarConf.make(); bb.setIntVarStrategy((vars) -> intVarSel.apply(vars, intValSel.apply(worker))); bb.setMetaStrategy(m -> Search.lastConflict(m, 2)); @@ -540,7 +540,7 @@ private void configureModel(int workerID) { SearchParams.ValueSelection.MIN, opt, 16, true); intValSel = intValConf.make(); intVarConf = new SearchParams.VarSelConf( - SearchParams.VariableSelection.CHS, 32); + SearchParams.VariableSelection.CHS, SearchParams.VariableTieBreaker.SMALLEST_DOMAIN, 32); intVarSel = intVarConf.make(); bb.setIntVarStrategy((vars) -> intVarSel.apply(vars, intValSel.apply(worker))); bb.setMetaStrategy(m -> Search.lastConflict(m, 1)); diff --git a/solver/src/main/java/org/chocosolver/solver/search/strategy/BlackBoxConfigurator.java b/solver/src/main/java/org/chocosolver/solver/search/strategy/BlackBoxConfigurator.java index 1afa590feb..c05a3d0700 100644 --- a/solver/src/main/java/org/chocosolver/solver/search/strategy/BlackBoxConfigurator.java +++ b/solver/src/main/java/org/chocosolver/solver/search/strategy/BlackBoxConfigurator.java @@ -13,8 +13,6 @@ import org.chocosolver.solver.ResolutionPolicy; import org.chocosolver.solver.Solver; import org.chocosolver.solver.search.restart.AbstractRestart; -import org.chocosolver.solver.search.restart.GeometricalCutoff; -import org.chocosolver.solver.search.restart.Restarter; import org.chocosolver.solver.search.strategy.selectors.values.RealDomainMax; import org.chocosolver.solver.search.strategy.selectors.values.RealDomainMin; import org.chocosolver.solver.search.strategy.selectors.variables.Cyclic; @@ -161,11 +159,12 @@ public static BlackBoxConfigurator forCSP() { SearchParams.ValSelConf defaultValSel = new SearchParams.ValSelConf( SearchParams.ValueSelection.MIN, false, 16, true); SearchParams.VarSelConf defaultVarSel = new SearchParams.VarSelConf( - SearchParams.VariableSelection.DOMWDEG_CACD, 32); + SearchParams.VariableSelection.DOMWDEG_CACD, SearchParams.VariableTieBreaker.LARGEST_DOMAIN, 32); bb.setIntVarStrategy((vars) -> defaultVarSel.make().apply(vars, defaultValSel.make().apply(vars[0].getModel()))); // restart policy - bb.setRestartPolicy(s -> new Restarter(new GeometricalCutoff(5, 1.05), - c -> s.getFailCount() >= c, 50_000, true)); + SearchParams.ResConf defaultResConf = new SearchParams.ResConf( + SearchParams.Restart.GEOMETRIC, 5, 1.05, 50_000, true); + bb.setRestartPolicy(defaultResConf.make()); // complementary settings bb.setNogoodOnRestart(true) .setRestartOnSolution(false) @@ -186,7 +185,7 @@ public static BlackBoxConfigurator forCOP() { SearchParams.ValSelConf defaultValSel = new SearchParams.ValSelConf( SearchParams.ValueSelection.MIN, true, 16, true); SearchParams.VarSelConf defaultVarSel = new SearchParams.VarSelConf( - SearchParams.VariableSelection.DOMWDEG, 32); + SearchParams.VariableSelection.DOMWDEG, SearchParams.VariableTieBreaker.SMALLEST_DOMAIN, 32); bb.setIntVarStrategy((vars) -> defaultVarSel.make().apply(vars, defaultValSel.make().apply(vars[0].getModel()))); // restart policy SearchParams.ResConf defaultResConf = new SearchParams.ResConf( diff --git a/solver/src/main/java/org/chocosolver/solver/search/strategy/Search.java b/solver/src/main/java/org/chocosolver/solver/search/strategy/Search.java index 462d137632..2818d59afa 100644 --- a/solver/src/main/java/org/chocosolver/solver/search/strategy/Search.java +++ b/solver/src/main/java/org/chocosolver/solver/search/strategy/Search.java @@ -187,7 +187,7 @@ public static SetStrategy setVarSearch(SetVar... sets) { * https://dblp.org/rec/conf/ecai/BoussemartHLS04 */ public static AbstractStrategy domOverWDegSearch(SetVar... vars) { - return setVarSearch(new DomOverWDeg<>(vars, 0), new SetDomainMin(), true, vars); + return setVarSearch(new DomOverWDeg<>(vars), new SetDomainMin(), true, vars); } /** @@ -200,7 +200,7 @@ public static AbstractStrategy domOverWDegSearch(SetVar... vars) { * https://dblp.org/rec/conf/ictai/WattezLPT19 */ public static AbstractStrategy domOverWDegRefSearch(SetVar... vars) { - return setVarSearch(new DomOverWDegRef<>(vars, 0), new SetDomainMin(), true, vars); + return setVarSearch(new DomOverWDegRef<>(vars), new SetDomainMin(), true, vars); } /** @@ -214,7 +214,7 @@ public static AbstractStrategy domOverWDegRefSearch(SetVar... vars) { * https://dblp.org/rec/conf/sac/HabetT19 */ public static AbstractStrategy conflictHistorySearch(SetVar... vars) { - return setVarSearch(new ConflictHistorySearch<>(vars, 0), new SetDomainMin(), true, vars); + return setVarSearch(new ConflictHistorySearch<>(vars), new SetDomainMin(), true, vars); } /** @@ -482,7 +482,9 @@ public static AbstractStrategy intVarSearch(IntVar... vars) { } valueSelector = new IntDomainLast(solution, valueSelector, null); } - return intVarSearch(new DomOverWDeg<>(vars, 0), valueSelector, vars); + return intVarSearch( + new DomOverWDegRef<>(vars), + valueSelector, vars); } /** @@ -496,7 +498,7 @@ public static AbstractStrategy intVarSearch(IntVar... vars) { * https://dblp.org/rec/conf/ecai/BoussemartHLS04 */ public static AbstractStrategy domOverWDegSearch(IntVar... vars) { - return intVarSearch(new DomOverWDeg<>(vars, 0), new IntDomainMin(), vars); + return intVarSearch(new DomOverWDeg<>(vars), new IntDomainMin(), vars); } /** @@ -509,7 +511,7 @@ public static AbstractStrategy domOverWDegSearch(IntVar... vars) { * https://dblp.org/rec/conf/ictai/WattezLPT19 */ public static AbstractStrategy domOverWDegRefSearch(IntVar... vars) { - return intVarSearch(new DomOverWDegRef<>(vars, 0), new IntDomainMin(), vars); + return intVarSearch(new DomOverWDegRef<>(vars), new IntDomainMin(), vars); } /** @@ -543,7 +545,7 @@ public static AbstractStrategy activityBasedSearch(IntVar... vars) { * https://dblp.org/rec/conf/sac/HabetT19 */ public static AbstractStrategy conflictHistorySearch(IntVar... vars) { - return intVarSearch(new ConflictHistorySearch<>(vars, 0), new IntDomainMin(), vars); + return intVarSearch(new ConflictHistorySearch<>(vars), new IntDomainMin(), vars); } /** diff --git a/solver/src/main/java/org/chocosolver/solver/search/strategy/SearchParams.java b/solver/src/main/java/org/chocosolver/solver/search/strategy/SearchParams.java index 460ece9ad1..411981f034 100644 --- a/solver/src/main/java/org/chocosolver/solver/search/strategy/SearchParams.java +++ b/solver/src/main/java/org/chocosolver/solver/search/strategy/SearchParams.java @@ -16,8 +16,10 @@ import org.chocosolver.solver.search.strategy.selectors.variables.*; import org.chocosolver.solver.search.strategy.strategy.AbstractStrategy; import org.chocosolver.solver.variables.IntVar; +import org.chocosolver.solver.variables.Variable; import org.chocosolver.util.tools.TimeUtils; +import java.util.Comparator; import java.util.function.BiFunction; import java.util.function.Function; @@ -224,16 +226,20 @@ public int getRuns() { */ class VarSelConf { final SearchParams.VariableSelection varsel; + final SearchParams.VariableTieBreaker tiebreaker; final int flushRate; /** * Configure the variable selection strategy * * @param varsel variable selection strategy + * @param tiebreaker variable tie-breaker strategy, ignored if the variable selection strategy is not + * CHS, DOMWDEG or DOMWDEG_CACD. It is used to break ties between variables with the same score * @param flushRate number of restarts before flushing the scores of the variables */ - public VarSelConf(SearchParams.VariableSelection varsel, int flushRate) { + public VarSelConf(SearchParams.VariableSelection varsel, SearchParams.VariableTieBreaker tiebreaker, int flushRate) { this.varsel = varsel; + this.tiebreaker = tiebreaker; this.flushRate = flushRate; } @@ -243,20 +249,39 @@ public VarSelConf(SearchParams.VariableSelection varsel, int flushRate) { * @return the variable selection strategy as a function of variables */ public BiFunction> make() { + final Comparator tie; + switch (tiebreaker) { + default: + case LEX: + tie = (v1, v2) -> 0; + break; + case SMALLEST_DOMAIN: + tie = Comparator.comparingInt(Variable::getDomainSize); + break; + case LARGEST_DOMAIN: + tie = (v1, v2) -> -Comparator.comparingInt(Variable::getDomainSize).compare(v1, v2); + break; + case SMALLEST_VALUE: + tie = Comparator.comparingInt(IntVar::getLB); + break; + case LARGEST_VALUE: + tie = Comparator.comparingInt(IntVar::getUB); + break; + } switch (varsel) { case ACTIVITY: return (vars, vsel) -> new ActivityBased(vars[0].getModel(), vars, vsel, 0.999d, 0.2d, 8, 1, 0); case CHS: - return (vars, vsel) -> Search.intVarSearch(new ConflictHistorySearch<>(vars, 0, flushRate), vsel, vars); + return (vars, vsel) -> Search.intVarSearch(new ConflictHistorySearch<>(vars, tie, flushRate), vsel, vars); case DOM: case FIRST_FAIL: return (vars, vsel) -> Search.intVarSearch(new FirstFail(vars[0].getModel()), vsel, vars); default: case DOMWDEG: - return (vars, vsel) -> Search.intVarSearch(new DomOverWDeg<>(vars, 0, flushRate), vsel, vars); + return (vars, vsel) -> Search.intVarSearch(new DomOverWDeg<>(vars, tie, flushRate), vsel, vars); case DOMWDEG_CACD: - return (vars, vsel) -> Search.intVarSearch(new DomOverWDegRef<>(vars, 0, flushRate), vsel, vars); + return (vars, vsel) -> Search.intVarSearch(new DomOverWDegRef<>(vars, tie, flushRate), vsel, vars); case FLBA: return (vars, vsel) -> Search.intVarSearch(new FailureBased<>(vars, 0, 4), vsel, vars); case FRBA: diff --git a/solver/src/main/java/org/chocosolver/solver/search/strategy/selectors/variables/AbstractCriterionBasedVariableSelector.java b/solver/src/main/java/org/chocosolver/solver/search/strategy/selectors/variables/AbstractFailureBasedVariableSelector.java similarity index 68% rename from solver/src/main/java/org/chocosolver/solver/search/strategy/selectors/variables/AbstractCriterionBasedVariableSelector.java rename to solver/src/main/java/org/chocosolver/solver/search/strategy/selectors/variables/AbstractFailureBasedVariableSelector.java index 415ef5a504..697e83a4a4 100644 --- a/solver/src/main/java/org/chocosolver/solver/search/strategy/selectors/variables/AbstractCriterionBasedVariableSelector.java +++ b/solver/src/main/java/org/chocosolver/solver/search/strategy/selectors/variables/AbstractFailureBasedVariableSelector.java @@ -9,24 +9,20 @@ */ package org.chocosolver.solver.search.strategy.selectors.variables; -import gnu.trove.list.array.TIntArrayList; import gnu.trove.map.TObjectDoubleMap; import gnu.trove.map.hash.TObjectDoubleHashMap; -import org.chocosolver.memory.IEnvironment; -import org.chocosolver.memory.IStateInt; -import org.chocosolver.solver.Solver; import org.chocosolver.solver.constraints.Propagator; import org.chocosolver.solver.exception.ContradictionException; import org.chocosolver.solver.search.loop.monitors.IMonitorContradiction; -import org.chocosolver.solver.search.loop.monitors.IMonitorRestart; import org.chocosolver.solver.variables.IVariableMonitor; import org.chocosolver.solver.variables.Variable; import org.chocosolver.solver.variables.events.IEventType; -import java.util.*; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; import java.util.function.BiConsumer; import java.util.function.BiFunction; -import java.util.stream.Collectors; /** *

@@ -35,8 +31,9 @@ * @author Charles Prud'homme * @since 26/02/2020. */ -public abstract class AbstractCriterionBasedVariableSelector implements VariableSelector, - IVariableMonitor, IMonitorContradiction, IMonitorRestart { +public abstract class AbstractFailureBasedVariableSelector + extends AbstractScoreBasedValueSelector + implements IVariableMonitor, IMonitorContradiction { /** * An element helps to keep 2 things up to date: @@ -63,33 +60,6 @@ public Element(int count, int w0, int w1) { return w; }; - protected static final int FLUSH_TOPS = 20; - protected static final double FLUSH_RATIO = .9 * FLUSH_TOPS; - protected int flushThs; - - protected final HashSet tops = new HashSet<>(); - protected int loop = 0; - - /** - * Randomness to break ties - */ - private final java.util.Random random; - /*** - * Pointer to the last free variable - */ - private final IStateInt last; - /** - * Temporary. Stores index of variables with the same (best) score - */ - private final TIntArrayList bests = new TIntArrayList(); - /** - * A reference to the Solver - */ - protected final Solver solver; - /** - * Needed to save operations - */ - final IEnvironment environment; /** * The number of conflicts which have occurred since the beginning of the search. */ @@ -103,7 +73,7 @@ public Element(int count, int w0, int w1) { */ private final HashMap observed = new HashMap<>(); /** - * Scoring for each variables, is updated dynamically. + * Scoring for each variable, is updated dynamically. */ final TObjectDoubleMap weights = new TObjectDoubleHashMap<>(15, 1.5f, 0.); /** @@ -124,52 +94,17 @@ public Element(int count, int w0, int w1) { } }; - public AbstractCriterionBasedVariableSelector(V[] vars, long seed, int flush) { - this.random = new java.util.Random(seed); - this.solver = vars[0].getModel().getSolver(); - this.environment = vars[0].getModel().getEnvironment(); - this.last = environment.makeInt(vars.length - 1); - this.flushThs = flush; - } - @Override - public final V getVariable(V[] vars) { - V best = null; - bests.resetQuick(); - double w = Double.NEGATIVE_INFINITY; - int to = last.get(); - for (int idx = 0; idx <= to; idx++) { - int domSize = vars[idx].getDomainSize(); - if (domSize > 1) { - double weight = weight(vars[idx]) / domSize; - //System.out.printf("%3f%n", weight); - if (w < weight) { - bests.resetQuick(); - bests.add(idx); - w = weight; - } else if (w == weight) { - bests.add(idx); - } - } else { - // swap - V tmp = vars[to]; - vars[to] = vars[idx]; - vars[idx] = tmp; - idx--; - to--; - } - } - last.set(to); - if (bests.size() > 0) { - //System.out.printf("%s%n", bests); - int currentVar = bests.get(random.nextInt(bests.size())); - best = vars[currentVar]; - } - return best; + /** + * Create a failure based variable selector + * @param vars scope variables + * @param tieBreaker a tiebreaker when scores are equal + * @param flushRate the number of restarts before cleaning the scores + */ + public AbstractFailureBasedVariableSelector(V[] vars, Comparator tieBreaker, int flushRate) { + super(vars, tieBreaker, flushRate); } - protected abstract double weight(V v); - @Override public final void onContradiction(ContradictionException cex) { conflicts++; @@ -231,35 +166,6 @@ public final void onContradiction(ContradictionException cex) { int remapInc() { return 0; } - - /** - * This method sorts elements wrt to their weight. - * If 90% of the top 20 elements remain unchanged, then weights are flushed - * - * @return true if the weights should be flushed - */ - protected boolean flushWeights(TObjectDoubleMap q) { - //if(true)return false; - List temp = weights.keySet().stream() - .sorted(Comparator.comparingDouble(q::get)) - .limit(FLUSH_TOPS) - .collect(Collectors.toList()); - long cnt = temp.stream().filter(tops::contains).count(); - if (cnt >= FLUSH_RATIO) { - loop++; - } else { - loop = 0; - } - tops.clear(); - if (loop == flushThs) { - loop = 0; - return true; - } else { - tops.addAll(temp); - return false; - } - } - ////////////////////////////////////////////////////////////////////// ////////////////// THIS IS RELATED TO INCREMENTAL FUTVARS //////////// ////////////////////////////////////////////////////////////////////// diff --git a/solver/src/main/java/org/chocosolver/solver/search/strategy/selectors/variables/AbstractScoreBasedValueSelector.java b/solver/src/main/java/org/chocosolver/solver/search/strategy/selectors/variables/AbstractScoreBasedValueSelector.java new file mode 100644 index 0000000000..cbf41c3e43 --- /dev/null +++ b/solver/src/main/java/org/chocosolver/solver/search/strategy/selectors/variables/AbstractScoreBasedValueSelector.java @@ -0,0 +1,110 @@ +/* + * This file is part of choco-solver, http://choco-solver.org/ + * + * Copyright (c) 2023, IMT Atlantique. All rights reserved. + * + * Licensed under the BSD 4-clause license. + * + * See LICENSE file in the project root for full license information. + */ +package org.chocosolver.solver.search.strategy.selectors.variables; + +import org.chocosolver.memory.IEnvironment; +import org.chocosolver.memory.IStateInt; +import org.chocosolver.solver.Solver; +import org.chocosolver.solver.search.loop.monitors.IMonitorRestart; +import org.chocosolver.solver.variables.Variable; + +import java.util.Comparator; + +/** + * Score-based variable selector. + *
+ * + * @author Charles Prud'homme + * @since 26/05/2023 + */ +public abstract class AbstractScoreBasedValueSelector implements VariableSelector, IMonitorRestart { + /** + * A reference to the Solver + */ + protected final Solver solver; + /** + * Needed to save operations + */ + final IEnvironment environment; + /*** + * Pointer to the last free variable + */ + private final IStateInt last; + + /** + * Default tie breaker: lexical ordering + */ + private final Comparator tieBreaker; + + private final int flushRate; + + /** + * Create a value selector based on the score of each variable + * + * @param vars variables to branch on + * @param tieBreaker a tiebreaker when scores are equal + * @param flushRate the rate at which scores are flushed, based on restart number + */ + public AbstractScoreBasedValueSelector(V[] vars, Comparator tieBreaker, int flushRate) { + this.solver = vars[0].getModel().getSolver(); + this.environment = vars[0].getModel().getEnvironment(); + this.last = environment.makeInt(vars.length - 1); + this.tieBreaker = tieBreaker; + this.flushRate = flushRate; + } + + @Override + public final V getVariable(V[] vars) { + V best = null; + double w = Double.NEGATIVE_INFINITY; + int to = last.get(); + for (int idx = 0; idx <= to; idx++) { + int domSize = vars[idx].getDomainSize(); + if (domSize > 1) { + double weight = score(vars[idx]) / domSize; + if (w < weight || (w == weight && tieBreaker.compare(vars[idx], best) < 0)) { + best = vars[idx]; + w = weight; + } + } else { + // swap + V tmp = vars[to]; + vars[to] = vars[idx]; + vars[idx] = tmp; + idx--; + to--; + } + } + last.set(to); + return best; + } + + /** + * Compute the score of a variable + * + * @param v a variable + * @return a score + */ + protected abstract double score(V v); + + @Override + public void afterRestart() { + if (solver.getRestartCount() % (flushRate + 1) == flushRate) { + flushScores(); + } + } + + /** + * + */ + protected abstract void flushScores(); + + +} diff --git a/solver/src/main/java/org/chocosolver/solver/search/strategy/selectors/variables/ConflictHistorySearch.java b/solver/src/main/java/org/chocosolver/solver/search/strategy/selectors/variables/ConflictHistorySearch.java index 45ec111728..9dc99d414d 100644 --- a/solver/src/main/java/org/chocosolver/solver/search/strategy/selectors/variables/ConflictHistorySearch.java +++ b/solver/src/main/java/org/chocosolver/solver/search/strategy/selectors/variables/ConflictHistorySearch.java @@ -14,9 +14,9 @@ import gnu.trove.map.hash.TObjectDoubleHashMap; import gnu.trove.map.hash.TObjectIntHashMap; import org.chocosolver.solver.constraints.Propagator; -import org.chocosolver.solver.search.loop.monitors.IMonitorRestart; import org.chocosolver.solver.variables.Variable; +import java.util.Comparator; import java.util.stream.Stream; /** @@ -28,9 +28,7 @@ * @since 25/02/2020. */ @SuppressWarnings("rawtypes") -public class ConflictHistorySearch - extends AbstractCriterionBasedVariableSelector - implements IMonitorRestart { +public class ConflictHistorySearch extends AbstractFailureBasedVariableSelector { /** * Related to CHS, @@ -57,12 +55,25 @@ public class ConflictHistorySearch */ private final TObjectIntMap conflict = new TObjectIntHashMap<>(10, 0.5f, 0); - public ConflictHistorySearch(V[] vars, long seed) { - this(vars, seed, Integer.MAX_VALUE); + /** + * Create a Conflict History Search variable selector. + * The default tie-breaker is lexico. + * The default flush rate is 32. + * @param vars variables to branch on + */ + public ConflictHistorySearch(V[] vars) { + this(vars, (v1, v2) -> 0, 32); } - public ConflictHistorySearch(V[] vars, long seed, int flushThs) { - super(vars, seed, flushThs); + + /** + * Create a Conflict History Search variable selector. + * @param vars variables to branch on + * @param tieBreaker a tiebreaker comparator when two variables have the same score + * @param flushRate the number of restarts before forgetting scores + */ + public ConflictHistorySearch(V[] vars, Comparator tieBreaker, int flushRate) { + super(vars, tieBreaker, flushRate); } @Override @@ -81,9 +92,9 @@ public void remove() { } @Override - protected double weight(Variable v) { + protected double score(Variable v) { double[] w = {0.}; - v.streamPropagators().forEach(prop ->{ + v.streamPropagators().forEach(prop -> { long fut = Stream.of(prop.getVars()) .filter(Variable::isInstantiated) .limit(2) @@ -109,18 +120,20 @@ void increase(Propagator prop, Element elt, double[] ws) { @Override public void afterRestart() { - if (flushWeights(q)) { - q.clear(); - conflict.forEachEntry((a1, b) -> { - conflict.put(a1, conflicts); - return true; - }); - } else { - for (Propagator p : q.keySet()) { - double qj = q.get(p); - q.put(p, qj * Math.pow(DECAY, (conflicts - conflict.get(p)))); - } - alpha = .4d; + for (Propagator p : q.keySet()) { + double qj = q.get(p); + q.put(p, qj * Math.pow(DECAY, (conflicts - conflict.get(p)))); } + alpha = .4d; + super.afterRestart(); + } + + @Override + public void flushScores() { + q.clear(); + conflict.forEachEntry((a1, b) -> { + conflict.put(a1, conflicts); + return true; + }); } } diff --git a/solver/src/main/java/org/chocosolver/solver/search/strategy/selectors/variables/DomOverWDeg.java b/solver/src/main/java/org/chocosolver/solver/search/strategy/selectors/variables/DomOverWDeg.java index 64194a4afd..f7baa6d062 100644 --- a/solver/src/main/java/org/chocosolver/solver/search/strategy/selectors/variables/DomOverWDeg.java +++ b/solver/src/main/java/org/chocosolver/solver/search/strategy/selectors/variables/DomOverWDeg.java @@ -10,11 +10,12 @@ package org.chocosolver.solver.search.strategy.selectors.variables; import org.chocosolver.solver.constraints.Propagator; -import org.chocosolver.solver.search.loop.monitors.IMonitorRestart; import org.chocosolver.solver.variables.IntVar; import org.chocosolver.solver.variables.Variable; import org.chocosolver.util.tools.VariableUtils; +import java.util.Comparator; + /** * Implementation of DowOverWDeg[1]. *

@@ -24,27 +25,27 @@ * @author Charles Prud'homme * @since 12/07/12 */ -public class DomOverWDeg extends AbstractCriterionBasedVariableSelector implements IMonitorRestart { +public class DomOverWDeg extends AbstractFailureBasedVariableSelector { /** - * Creates a DomOverWDeg variable selector + * Creates a DomOverWDeg variable selector. + * The default tiebreaker is lexical ordering. + * The default flush rate is 32. * * @param variables decision variables - * @param seed seed for breaking ties randomly */ - public DomOverWDeg(V[] variables, long seed) { - this(variables, seed, Integer.MAX_VALUE); + public DomOverWDeg(V[] variables) { + this(variables, (v1, v2)->0, 32); } /** * Creates a DomOverWDeg variable selector - * - * @param variables decision variables - * @param seed seed for breaking ties randomly - * @param flushThs flush threshold, when reached, it flushes scores + * @param variables scope variables + * @param tieBreaker a tiebreaker comparator when two variables have the same score + * @param flushRate the number of restarts before forgetting scores */ - public DomOverWDeg(V[] variables, long seed,int flushThs) { - super(variables, seed, flushThs); + public DomOverWDeg(V[] variables, Comparator tieBreaker, int flushRate) { + super(variables, tieBreaker, flushRate); } @@ -64,7 +65,7 @@ public final void remove() { } @Override - protected final double weight(Variable v) { + protected final double score(Variable v) { //assert weightW(v) == weights.get(v) : "wrong weight for " + v + ", expected " + weightW(v) + ", but found " + weights.get(v); return 1 + weights.get(v); } @@ -96,19 +97,13 @@ void increase(Propagator prop, Element elt, double[] ws) { final int remapInc() { return 1; } - + @Override - public void afterRestart() { - /*if (vars[0].getModel().getSolver().getSolutionCount() > solution) { - solution = vars[0].getModel().getSolver().getSolutionCount(); - } - if (solution > 0 && top(20)) {*/ - if (flushWeights(weights)) { - weights.forEachEntry((a1, b) -> { - weights.put(a1, 0.); - return true; - }); - } + public void flushScores() { + weights.forEachEntry((a1, b) -> { + weights.put(a1, 0.); + return true; + }); } // <-- FOR DEBUGGING PURPOSE ONLY diff --git a/solver/src/main/java/org/chocosolver/solver/search/strategy/selectors/variables/DomOverWDegRef.java b/solver/src/main/java/org/chocosolver/solver/search/strategy/selectors/variables/DomOverWDegRef.java index 8a794f74e0..4e315c345c 100644 --- a/solver/src/main/java/org/chocosolver/solver/search/strategy/selectors/variables/DomOverWDegRef.java +++ b/solver/src/main/java/org/chocosolver/solver/search/strategy/selectors/variables/DomOverWDegRef.java @@ -14,6 +14,8 @@ import org.chocosolver.solver.variables.Variable; import org.chocosolver.util.tools.VariableUtils; +import java.util.Comparator; + /** * Implementation of refined DowOverWDeg. * @@ -26,28 +28,28 @@ public class DomOverWDegRef extends DomOverWDeg { /** * Creates a DomOverWDegRef variable selector with "CACD" as weight incrementer. + * The default tiebreaker is lexical ordering. + * The default flush rate is 32. * * @param variables decision variables - * @param seed seed for breaking ties randomly */ - public DomOverWDegRef(V[] variables, long seed) { - super(variables, seed); + public DomOverWDegRef(V[] variables) { + this(variables, (v1, v2) -> 0, 32); } /** * Creates a DomOverWDegRef variable selector with "CACD" as weight incrementer. * - * @param variables decision variables - * @param seed seed for breaking ties randomly - * @param flushThs flush threshold, when reached, it flushes scores + * @param variables scope variables + * @param tieBreaker a tiebreaker comparator when two variables have the same score + * @param flushRate the number of restarts before forgetting scores */ - public DomOverWDegRef(V[] variables, long seed, int flushThs) { - super(variables, seed, flushThs); + public DomOverWDegRef(V[] variables, Comparator tieBreaker, int flushRate) { + super(variables, tieBreaker, flushRate); } /** - * @implNote - * This is the reason this class exists. + * @implNote This is the reason this class exists. * The only difference with {@link DomOverWDeg} is the increment * which is not 1 for each variable. */ diff --git a/solver/src/test/java/org/chocosolver/solver/search/ObjectiveTest.java b/solver/src/test/java/org/chocosolver/solver/search/ObjectiveTest.java index 801ff96fea..7530399954 100644 --- a/solver/src/test/java/org/chocosolver/solver/search/ObjectiveTest.java +++ b/solver/src/test/java/org/chocosolver/solver/search/ObjectiveTest.java @@ -443,7 +443,7 @@ public void testCP1() { solver.attach(solution); int[] t = new int[2]; - solver.setSearch(new IntStrategy(ticks, new DomOverWDeg(ticks, 0L), + solver.setSearch(new IntStrategy(ticks, new DomOverWDeg<>(ticks), new IntDomainLast(solution, new IntDomainBest(), (x, v) -> { int c = 0; diff --git a/solver/src/test/java/org/chocosolver/solver/search/loop/LNSTest.java b/solver/src/test/java/org/chocosolver/solver/search/loop/LNSTest.java index 2e4cbe8801..b3cfbbc7f2 100644 --- a/solver/src/test/java/org/chocosolver/solver/search/loop/LNSTest.java +++ b/solver/src/test/java/org/chocosolver/solver/search/loop/LNSTest.java @@ -319,7 +319,7 @@ public void testPN1() { // Set up basic search for first sol. - Move basicsearch = new MoveBinaryDFS(new IntStrategy(decvars, new DomOverWDeg<>(decvars, 992634), new IntDomainMin())); + Move basicsearch = new MoveBinaryDFS(new IntStrategy(decvars, new DomOverWDeg<>(decvars), new IntDomainMin())); Solver solver = model.getSolver(); solver.setMove(basicsearch); @@ -332,7 +332,7 @@ public void testPN1() { in.init(); // Should this be necessary? // Type of search within LNS neighbourhoods - Move innersearch = new MoveBinaryDFS(new IntStrategy(decvars, new DomOverWDeg<>(decvars, 0L), new IntDomainMin())); + Move innersearch = new MoveBinaryDFS(new IntStrategy(decvars, new DomOverWDeg<>(decvars), new IntDomainMin())); MoveLNS lns = new MoveLNS(innersearch, in, new BacktrackCounter(model, 50));