diff --git a/solver/src/main/java/org/chocosolver/solver/ParallelPortfolio.java b/solver/src/main/java/org/chocosolver/solver/ParallelPortfolio.java index 2bf62ca9c3..5266641105 100644 --- a/solver/src/main/java/org/chocosolver/solver/ParallelPortfolio.java +++ b/solver/src/main/java/org/chocosolver/solver/ParallelPortfolio.java @@ -417,11 +417,7 @@ private synchronized void updateFromSolution(Model m) { if (solverVal == bestVal) { getSolutionFound().set(true); finder = m; - if (m.getResolutionPolicy() == ResolutionPolicy.MAXIMIZE) { - models.forEach(s1 -> s1.getSolver().getObjectiveManager().updateBestLB(bestVal)); - } else { - models.forEach(s1 -> s1.getSolver().getObjectiveManager().updateBestUB(bestVal)); - } + models.forEach(s1 -> s1.getSolver().getObjectiveManager().updateBestSolution(bestVal)); } } } diff --git a/solver/src/main/java/org/chocosolver/solver/objective/AbstractIntObjManager.java b/solver/src/main/java/org/chocosolver/solver/objective/AbstractIntObjManager.java index e2f30b79e3..98bd917c76 100644 --- a/solver/src/main/java/org/chocosolver/solver/objective/AbstractIntObjManager.java +++ b/solver/src/main/java/org/chocosolver/solver/objective/AbstractIntObjManager.java @@ -16,58 +16,142 @@ import org.chocosolver.solver.variables.IntVar; import org.chocosolver.util.objects.setDataStructures.iterable.IntIterableRangeSet; +import java.util.Objects; import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.IntUnaryOperator; /** * @author Jean-Guillaume Fages, Charles Prud'homme, Arnaud Malapert - * */ -abstract class AbstractIntObjManager extends AbstractObjManager { +public abstract class AbstractIntObjManager implements IObjectiveManager { private static final long serialVersionUID = 5539060355541720114L; - public AbstractIntObjManager(AbstractIntObjManager objman) { - super(objman); - } + /** + * The variable to optimize + **/ + transient protected final IntVar objective; + + /** + * Define how should the objective be optimize + */ + protected final ResolutionPolicy policy; + + /** + * define the precision to consider a variable as instantiated + **/ + protected final int precision; - public AbstractIntObjManager(IntVar objective, ResolutionPolicy policy, Number precision) { - super(objective, policy, precision); + /** + * best lower bound found so far + **/ + protected int bestProvedLB; + + /** + * best upper bound found so far + **/ + protected int bestProvedUB; + + /** + * Define how the cut should be updated when posting the cut + **/ + transient protected IntUnaryOperator cutComputer = n -> n; // walking cut by default + + public AbstractIntObjManager(AbstractIntObjManager objman) { + objective = objman.objective; + policy = objman.policy; + precision = objman.precision; + bestProvedLB = objman.bestProvedLB; + bestProvedUB = objman.bestProvedUB; + cutComputer = objman.cutComputer; + } + + public AbstractIntObjManager(IntVar objective, ResolutionPolicy policy, int precision) { + assert Objects.nonNull(objective); + this.objective = objective; + assert Objects.nonNull(policy); + this.policy = policy; + this.precision = precision; bestProvedLB = objective.getLB() - 1; bestProvedUB = objective.getUB() + 1; } + + @Override + public final IntVar getObjective() { + return objective; + } + + @Override + public final ResolutionPolicy getPolicy() { + return policy; + } + + @Override + public final Number getBestLB() { + return bestProvedLB; + } + + @Override + public final Number getBestUB() { + return bestProvedUB; + } + + @Override + public final void setCutComputer(Function cutComputer) { + this.cutComputer = operand -> cutComputer.apply(operand).intValue(); + } + + public final void setCutComputer(IntUnaryOperator cutComputer) { + this.cutComputer = cutComputer; + } + + @Override + public void setStrictDynamicCut() { + cutComputer = n -> n + precision; + } + @Override - public synchronized boolean updateBestLB(Number lb) { - if (bestProvedLB.intValue() < lb.intValue()) { + public final void setWalkingDynamicCut() { + cutComputer = n -> n; + } + + public synchronized boolean updateBestLB(int lb) { + if (bestProvedLB < lb) { bestProvedLB = lb; return true; } return false; } - @Override - public synchronized boolean updateBestUB(Number ub) { - if (bestProvedUB.intValue() > ub.intValue()) { + public synchronized boolean updateBestUB(int ub) { + if (bestProvedUB > ub) { bestProvedUB = ub; return true; } return false; } + @Override + public boolean updateBestSolution(Number n) { + return updateBestSolution(n.intValue()); + } + + /** + * Informs the manager that a new solution has been found + */ + public abstract boolean updateBestSolution(int n); + @Override public boolean updateBestSolution() { - if(!objective.isInstantiated()) { + if (!objective.isInstantiated()) { throw new SolverException( - "objective variable (" + objective + ") is not instantiated on solution. Check constraints and/or decision variables."); + "objective variable (" + objective + ") is not instantiated on solution. Check constraints and/or decision variables."); } return updateBestSolution(objective.getValue()); } - @Override - public void setStrictDynamicCut() { - cutComputer = (Number n) -> n.intValue() + precision.intValue(); - } - @Override public void resetBestBounds() { bestProvedLB = objective.getLB() - 1; @@ -99,13 +183,13 @@ public MinIntObjManager(IntVar objective) { } @Override - public boolean updateBestSolution(Number n) { + public boolean updateBestSolution(int n) { return updateBestUB(n); } @Override public void postDynamicCut() throws ContradictionException { - objective.updateBounds(bestProvedLB.intValue(), cutComputer.apply(bestProvedUB).intValue(), this); + objective.updateBounds(bestProvedLB, cutComputer.applyAsInt(bestProvedUB), this); } @Override @@ -115,7 +199,7 @@ public Number getBestSolutionValue() { @Override public void explain(int p, ExplanationForSignedClause explanation) { - objective.intersectLit(IntIterableRangeSet.MIN, bestProvedUB.intValue() - 1, explanation); + objective.intersectLit(IntIterableRangeSet.MIN, bestProvedUB - 1, explanation); } } @@ -134,13 +218,13 @@ public MaxIntObjManager(IntVar objective) { } @Override - public boolean updateBestSolution(Number n) { + public boolean updateBestSolution(int n) { return updateBestLB(n); } @Override public void postDynamicCut() throws ContradictionException { - objective.updateBounds(cutComputer.apply(bestProvedLB).intValue(), bestProvedUB.intValue(), this); + objective.updateBounds(cutComputer.applyAsInt(bestProvedLB), bestProvedUB, this); } @Override @@ -150,6 +234,6 @@ public Number getBestSolutionValue() { @Override public void explain(int p, ExplanationForSignedClause explanation) { - objective.intersectLit(bestProvedLB.intValue() + 1, IntIterableRangeSet.MAX, explanation); + objective.intersectLit(bestProvedLB + 1, IntIterableRangeSet.MAX, explanation); } } \ No newline at end of file diff --git a/solver/src/main/java/org/chocosolver/solver/objective/AbstractObjManager.java b/solver/src/main/java/org/chocosolver/solver/objective/AbstractObjManager.java deleted file mode 100644 index 9a30f6a030..0000000000 --- a/solver/src/main/java/org/chocosolver/solver/objective/AbstractObjManager.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * This file is part of choco-solver, http://choco-solver.org/ - * - * Copyright (c) 2022, 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.objective; - -import org.chocosolver.solver.ResolutionPolicy; -import org.chocosolver.solver.variables.Variable; - -import java.util.Objects; -import java.util.function.Function; - -/** - * This class defines common methods to COP based on maximization or minimization of integer or real variable - * @author Jean-Guillaume Fages, Charles Prud'homme, Arnaud Malapert - * - */ -abstract class AbstractObjManager implements IObjectiveManager { - - private static final long serialVersionUID = 4330218142281861652L; - - /** The variable to optimize **/ - transient protected final V objective; - - /** Define how should the objective be optimize */ - protected final ResolutionPolicy policy; - - /** define the precision to consider a variable as instantiated **/ - protected final Number precision; - - /** best lower bound found so far **/ - protected Number bestProvedLB; - - /** best upper bound found so far **/ - protected Number bestProvedUB; - - /** Define how the cut should be update when posting the cut **/ - transient protected Function cutComputer = n -> n; // walking cut by default - - public AbstractObjManager(AbstractObjManager objman) { - objective = objman.objective; - policy = objman.policy; - precision = objman.precision; - bestProvedLB = objman.bestProvedLB; - bestProvedUB = objman.bestProvedUB; - cutComputer = objman.cutComputer; - } - - - public AbstractObjManager(V objective, ResolutionPolicy policy, Number precision) { - super(); - assert Objects.nonNull(objective); - this.objective = objective; - assert Objects.nonNull(policy); - this.policy = policy; - assert Objects.nonNull(precision); - this.precision = precision; - } - - @Override - public final V getObjective() { - return objective; - } - - @Override - public final ResolutionPolicy getPolicy() { - return policy; - } - - @Override - public final Number getBestLB() { - return bestProvedLB; - } - - @Override - public final Number getBestUB() { - return bestProvedUB; - } - - @Override - public final void setCutComputer(Function cutComputer) { - this.cutComputer = cutComputer; - } - - @Override - public final void setWalkingDynamicCut() { - cutComputer = (obj) -> obj; - } - - -} \ No newline at end of file diff --git a/solver/src/main/java/org/chocosolver/solver/objective/AbstractRealObjManager.java b/solver/src/main/java/org/chocosolver/solver/objective/AbstractRealObjManager.java index f097bfc06e..6b9375666b 100644 --- a/solver/src/main/java/org/chocosolver/solver/objective/AbstractRealObjManager.java +++ b/solver/src/main/java/org/chocosolver/solver/objective/AbstractRealObjManager.java @@ -13,56 +13,142 @@ import org.chocosolver.solver.exception.ContradictionException; import org.chocosolver.solver.variables.RealVar; +import java.util.Objects; +import java.util.function.DoubleUnaryOperator; +import java.util.function.Function; + /** * @author Jean-Guillaume Fages, Charles Prud'homme, Arnaud Malapert */ -abstract class AbstractRealObjManager extends AbstractObjManager { +abstract class AbstractRealObjManager implements IObjectiveManager { private static final long serialVersionUID = 8038511375883592639L; - public AbstractRealObjManager(AbstractObjManager objman) { - super(objman); + /** + * The variable to optimize + **/ + transient protected final RealVar objective; + + /** + * Define how should the objective be optimize + */ + protected final ResolutionPolicy policy; + + /** + * define the precision to consider a variable as instantiated + **/ + protected final double precision; + + /** + * best lower bound found so far + **/ + protected double bestProvedLB; + + /** + * best upper bound found so far + **/ + protected double bestProvedUB; + + /** + * Define how the cut should be updated when posting the cut + **/ + transient protected DoubleUnaryOperator cutComputer = n -> n; // walking cut by default + + + public AbstractRealObjManager(AbstractRealObjManager objman) { + objective = objman.objective; + policy = objman.policy; + precision = objman.precision; + bestProvedLB = objman.bestProvedLB; + bestProvedUB = objman.bestProvedUB; + cutComputer = objman.cutComputer; + } - public AbstractRealObjManager(RealVar objective, ResolutionPolicy policy, Number precision) { - super(objective, policy, precision); - double prec = Math.abs(precision.doubleValue()); - bestProvedLB = objective.getLB() - prec; - bestProvedUB = objective.getUB() + prec; + public AbstractRealObjManager(RealVar objective, ResolutionPolicy policy, double precision) { + assert Objects.nonNull(objective); + this.objective = objective; + assert Objects.nonNull(policy); + this.policy = policy; + this.precision = precision; + this.bestProvedLB = objective.getLB() - precision; + this.bestProvedUB = objective.getUB() + precision; + } + + + @Override + public final RealVar getObjective() { + return objective; + } + + @Override + public final ResolutionPolicy getPolicy() { + return policy; + } + + @Override + public final Number getBestLB() { + return bestProvedLB; + } + + @Override + public final Number getBestUB() { + return bestProvedUB; + } + + @Override + public final void setCutComputer(Function cutComputer) { + this.cutComputer = operand -> cutComputer.apply(operand).intValue(); + } + + public final void setCutComputer(DoubleUnaryOperator cutComputer) { + this.cutComputer = cutComputer; } @Override - public synchronized boolean updateBestLB(Number lb) { - if (bestProvedLB.doubleValue() < lb.doubleValue()) { + public void setStrictDynamicCut() { + cutComputer = n -> n + precision; + } + + @Override + public final void setWalkingDynamicCut() { + cutComputer = n -> n; + } + + + public synchronized boolean updateBestLB(double lb) { + if (bestProvedLB < lb) { bestProvedLB = lb; return true; } return false; } - @Override - public synchronized boolean updateBestUB(Number ub) { - if (bestProvedUB.doubleValue() > ub.doubleValue()) { + public synchronized boolean updateBestUB(double ub) { + if (bestProvedUB > ub) { bestProvedUB = ub; return true; } return false; } + @Override + public boolean updateBestSolution(Number n) { + return updateBestSolution(n.doubleValue()); + } + + public abstract boolean updateBestSolution(double n); + @Override public boolean updateBestSolution() { assert objective.isInstantiated(); return updateBestSolution(objective.getUB()); } - @Override - public void setStrictDynamicCut() { - cutComputer = (Number n) -> n.doubleValue() + precision.doubleValue(); - } private int getNbDecimals() { int dec = 0; - double p = precision.doubleValue(); + double p = precision; while ((int) p <= 0 && dec <= 12) { dec++; p *= 10; @@ -72,7 +158,7 @@ private int getNbDecimals() { @Override public void resetBestBounds() { - double prec = Math.abs(precision.doubleValue()); + double prec = Math.abs(precision); bestProvedLB = objective.getLB() - prec; bestProvedUB = objective.getUB() + prec; } @@ -97,13 +183,13 @@ public MinRealObjManager(RealVar objective, double precision) { } @Override - public boolean updateBestSolution(Number n) { + public boolean updateBestSolution(double n) { return updateBestUB(n); } @Override public void postDynamicCut() throws ContradictionException { - objective.updateBounds(bestProvedLB.doubleValue(), cutComputer.apply(bestProvedUB).doubleValue(), this); + objective.updateBounds(bestProvedLB, cutComputer.applyAsDouble(bestProvedUB), this); } @Override @@ -127,13 +213,13 @@ public MaxRealObjManager(RealVar objective, double precision) { } @Override - public boolean updateBestSolution(Number n) { + public boolean updateBestSolution(double n) { return updateBestLB(n); } @Override public void postDynamicCut() throws ContradictionException { - objective.updateBounds(cutComputer.apply(bestProvedLB).doubleValue(), bestProvedUB.doubleValue(), this); + objective.updateBounds(cutComputer.applyAsDouble(bestProvedLB), bestProvedUB, this); } @Override diff --git a/solver/src/main/java/org/chocosolver/solver/objective/IBoundsManager.java b/solver/src/main/java/org/chocosolver/solver/objective/IBoundsManager.java index 8ef8390ba1..e4e32643c4 100644 --- a/solver/src/main/java/org/chocosolver/solver/objective/IBoundsManager.java +++ b/solver/src/main/java/org/chocosolver/solver/objective/IBoundsManager.java @@ -42,22 +42,6 @@ default boolean isOptimization() { */ Number getBestUB(); - /** - * States that lb is a global lower bound on the problem - * - * @param lb lower bound - * @return true iff the lower bound has been updated - */ - boolean updateBestLB(Number lb); - - /** - * States that ub is a global upper bound on the problem - * - * @param ub upper bound - * @return true iff the upper bound has been updated - */ - boolean updateBestUB(Number ub); - /** * @return the best solution value found so far (returns the initial bound if no solution has been found yet) */ diff --git a/solver/src/main/java/org/chocosolver/solver/objective/IObjectiveManager.java b/solver/src/main/java/org/chocosolver/solver/objective/IObjectiveManager.java index 4d0455e652..258343c4aa 100644 --- a/solver/src/main/java/org/chocosolver/solver/objective/IObjectiveManager.java +++ b/solver/src/main/java/org/chocosolver/solver/objective/IObjectiveManager.java @@ -13,7 +13,9 @@ import org.chocosolver.solver.exception.ContradictionException; import org.chocosolver.solver.variables.Variable; +import java.util.function.DoubleUnaryOperator; import java.util.function.Function; +import java.util.function.IntUnaryOperator; /** * interface to monitor the bounds of the objective variable. @@ -39,7 +41,9 @@ public interface IObjectiveManager extends IBoundsManager, I boolean updateBestSolution(); /** - * Set a user-defined cut computer to avoid "worse" solutions + * Set a user-defined cut computer to avoid "worse" solutions. + * @see AbstractIntObjManager#setCutComputer(IntUnaryOperator) + * @see AbstractRealObjManager#setCutComputer(DoubleUnaryOperator) */ void setCutComputer(Function cutComputer); diff --git a/solver/src/main/java/org/chocosolver/solver/objective/ObjectiveFactory.java b/solver/src/main/java/org/chocosolver/solver/objective/ObjectiveFactory.java index 3649b2857f..20fbd251f5 100644 --- a/solver/src/main/java/org/chocosolver/solver/objective/ObjectiveFactory.java +++ b/solver/src/main/java/org/chocosolver/solver/objective/ObjectiveFactory.java @@ -10,7 +10,6 @@ package org.chocosolver.solver.objective; import org.chocosolver.solver.ResolutionPolicy; -import org.chocosolver.solver.exception.ContradictionException; import org.chocosolver.solver.variables.IntVar; import org.chocosolver.solver.variables.RealVar; import org.chocosolver.solver.variables.Variable; @@ -97,9 +96,9 @@ public static IObjectiveManager makeObjectiveManager(RealVar objective, @SuppressWarnings("unchecked") public static V copy(V object) { try { - Class c = object.getClass(); + Class c = object.getClass(); // Use the "copy constructor": - Constructor ct = c.getConstructor(c); + Constructor ct = c.getConstructor(c); return (V) ct.newInstance(object); } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { // fails silently @@ -151,16 +150,6 @@ public Number getBestUB() { throw new UnsupportedOperationException("There is no objective bounds in satisfaction problems"); } - @Override - public boolean updateBestLB(Number lb) { - throw new UnsupportedOperationException("There is no objective bounds in satisfaction problems"); - } - - @Override - public boolean updateBestUB(Number ub) { - throw new UnsupportedOperationException("There is no objective bounds in satisfaction problems"); - } - @Override public Number getBestSolutionValue() { throw new UnsupportedOperationException("There is no objective variable in satisfaction problems"); @@ -198,7 +187,7 @@ public void setCutComputer(Function cutComputer) { } @Override - public void postDynamicCut() throws ContradictionException { + public void postDynamicCut() { // nothing to do } 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 6b7aea840b..639ba40fb0 100644 --- a/solver/src/test/java/org/chocosolver/solver/search/ObjectiveTest.java +++ b/solver/src/test/java/org/chocosolver/solver/search/ObjectiveTest.java @@ -262,7 +262,7 @@ public void testAdaptiveCut1() { model.getSolver().reset(); final int finalI = i; oman.setCutComputer(n -> n.intValue() - ends[finalI]); - oman.updateBestUB(best); + oman.updateBestSolution(best); } assertEquals(best, 34); assertEquals(model.getSolver().getSolutionCount(), 0); // the last resolution fails at finding solutions @@ -283,7 +283,7 @@ public void testAdaptiveCut2() { } model.getSolver().reset(); ends[0] = floorDiv(ends[0], 2); - oman.updateBestUB(best); + oman.updateBestSolution(best); } assertEquals(best, 34); assertEquals(model.getSolver().getSolutionCount(), 0); // the last resolution fails at finding solutions