You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by er...@apache.org on 2021/08/13 23:53:24 UTC

[commons-math] 03/04: MATH-1625: Enable callback in "SimplexOptimizer".

This is an automated email from the ASF dual-hosted git repository.

erans pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-math.git

commit 310b21e4d7071bbdc52e12283b0718c2d5dd5bbc
Author: Gilles Sadowski <gi...@gmail.com>
AuthorDate: Sat Aug 14 01:24:38 2021 +0200

    MATH-1625: Enable callback in "SimplexOptimizer".
---
 .../nonlinear/scalar/noderiv/SimplexOptimizer.java | 120 +++++++++++++++++++--
 1 file changed, 113 insertions(+), 7 deletions(-)

diff --git a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/optim/nonlinear/scalar/noderiv/SimplexOptimizer.java b/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/optim/nonlinear/scalar/noderiv/SimplexOptimizer.java
index fcbd746..0d310b9 100644
--- a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/optim/nonlinear/scalar/noderiv/SimplexOptimizer.java
+++ b/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/optim/nonlinear/scalar/noderiv/SimplexOptimizer.java
@@ -22,6 +22,8 @@ import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.Collections;
 import java.util.function.UnaryOperator;
+import java.util.function.IntSupplier;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 import org.apache.commons.math4.legacy.core.MathArrays;
 import org.apache.commons.math4.legacy.analysis.MultivariateFunction;
@@ -112,6 +114,8 @@ public class SimplexOptimizer extends MultivariateOptimizer {
     private SimulatedAnnealing simulatedAnnealing = null;
     /** Number of additional optimizations (optional). */
     private int bestListSize = 0;
+    /** Callbacks. */
+    private final List<Observer> callbacks = new CopyOnWriteArrayList<>();
 
     /**
      * @param checker Convergence checker.
@@ -129,6 +133,38 @@ public class SimplexOptimizer extends MultivariateOptimizer {
         this(new SimpleValueChecker(rel, abs));
     }
 
+    /**
+     * Callback interface for updating caller's code with the current
+     * state of the optimization.
+     */
+    @FunctionalInterface
+    public interface Observer {
+        /**
+         * Method called after each modification of the {@code simplex}.
+         *
+         * @param simplex Current simplex.
+         * @param isInit {@code true} at the start of a new search (either
+         * "main" or "best list"), after the initial simplex's vertices
+         * have been evaluated.
+         * @param numEval Number of evaluations of the objective function.
+         */
+        void update(Simplex simplex,
+                    boolean isInit,
+                    int numEval);
+    }
+
+    /**
+     * Register a callback.
+     *
+     * @param cb Callback.
+     */
+    public void addObserver(Observer cb) {
+        if (cb == null) {
+            throw new NullPointerException("Callback");
+        }
+        callbacks.add(cb);
+    }
+
     /** {@inheritDoc} */
     @Override
     protected PointValuePair doOptimize() {
@@ -149,6 +185,7 @@ public class SimplexOptimizer extends MultivariateOptimizer {
         final List<PointValuePair> bestList = new ArrayList<>();
 
         Simplex currentSimplex = initialSimplex.translate(getStartPoint()).evaluate(evalFunc, comparator);
+        notifyObservers(currentSimplex, true);
         double temperature = Double.NaN; // Only used with simulated annealing.
         Simplex previousSimplex = null;
 
@@ -190,20 +227,31 @@ public class SimplexOptimizer extends MultivariateOptimizer {
                                       simulatedAnnealing.metropolis(temperature));
 
                 for (int i = 0; i < simulatedAnnealing.getEpochDuration(); i++) {
-                    currentSimplex = update.apply(currentSimplex).evaluate(evalFunc, comparator);
+                    // Simplex is transformed (and observers are notified).
+                    currentSimplex = applyUpdate(update,
+                                                 currentSimplex,
+                                                 evalFunc,
+                                                 comparator);
                 }
             } else {
                 // No simulated annealing.
                 final UnaryOperator<Simplex> update =
                     updateRule.create(evalFunc, comparator, null);
 
-                currentSimplex = update.apply(currentSimplex).evaluate(evalFunc, comparator);
+                // Simplex is transformed (and observers are notified).
+                currentSimplex = applyUpdate(update,
+                                             currentSimplex,
+                                             evalFunc,
+                                             comparator);
             }
 
             if (bestListSize != 0) {
                 // Store best points.
                 for (int i = 0; i < currentSimplex.getSize(); i++) {
-                    keepIfBetter(currentSimplex.get(i), comparator, bestList, bestListSize);
+                    keepIfBetter(currentSimplex.get(i),
+                                 comparator,
+                                 bestList,
+                                 bestListSize);
                 }
             }
 
@@ -216,7 +264,13 @@ public class SimplexOptimizer extends MultivariateOptimizer {
             throw new ConvergenceException();
         } else {
             // Additional optimizations.
-            return bestListSearch(evalFunc, comparator, bestList);
+            // Reference to counter in the "main" search in order to retrieve
+            // the total number of evaluations in the "best list" search.
+            final IntSupplier evalCount = () -> getEvaluations();
+            return bestListSearch(evalFunc,
+                                  comparator,
+                                  bestList,
+                                  evalCount);
         }
     }
 
@@ -392,11 +446,13 @@ public class SimplexOptimizer extends MultivariateOptimizer {
      * @param evalFunc Objective function.
      * @param comp Fitness comparator.
      * @param starts Starting points.
+     * @param evalCount Evaluation counter.
      * @return the optimum.
      */
     private PointValuePair bestListSearch(MultivariateFunction evalFunc,
                                           Comparator<PointValuePair> comp,
-                                          List<PointValuePair> starts) {
+                                          List<PointValuePair> starts,
+                                          IntSupplier evalCount) {
         PointValuePair best = starts.get(0); // Overall best result.
 
         // Additional local optimizations using each of the best
@@ -413,7 +469,9 @@ public class SimplexOptimizer extends MultivariateOptimizer {
                                                   simplex,
                                                   evalFunc,
                                                   getConvergenceChecker(),
-                                                  getGoalType());
+                                                  getGoalType(),
+                                                  callbacks,
+                                                  evalCount);
             if (comp.compare(r, best) < 0) {
                 best = r; // New overall best.
             }
@@ -430,14 +488,26 @@ public class SimplexOptimizer extends MultivariateOptimizer {
      * incrementing the main counter.
      * @param checker Convergence checker.
      * @param goalType Whether to minimize or maximize the objective function.
+     * @param cbList Callbacks.
+     * @param evalCount Evaluation counter.
      * @return the optimum.
      */
     private static PointValuePair directSearch(double[] init,
                                                Simplex simplex,
                                                MultivariateFunction eval,
                                                ConvergenceChecker<PointValuePair> checker,
-                                               GoalType goalType) {
+                                               GoalType goalType,
+                                               List<Observer> cbList,
+                                               final IntSupplier evalCount) {
         final SimplexOptimizer optim = new SimplexOptimizer(checker);
+
+        for (Observer cOrig : cbList) {
+            final SimplexOptimizer.Observer cNew = (spx, isInit, numEval) ->
+                cOrig.update(spx, isInit, evalCount.getAsInt());
+
+            optim.addObserver(cNew);
+        }
+
         return optim.optimize(MaxEval.unlimited(),
                               new ObjectiveFunction(eval),
                               goalType,
@@ -445,4 +515,40 @@ public class SimplexOptimizer extends MultivariateOptimizer {
                               simplex,
                               new NelderMeadTransform());
     }
+
+    /**
+     * @param simplex Current simplex.
+     * @param isInit Set to {@code true} at the start of a new search
+     * (either "main" or "best list"), after the evaluation of the initial
+     * simplex's vertices.
+     */
+    private void notifyObservers(Simplex simplex,
+                                 boolean isInit) {
+        for (Observer cb : callbacks) {
+            cb.update(simplex,
+                      isInit,
+                      getEvaluations());
+        }
+    }
+
+    /**
+     * Applies the {@code update} to the given {@code simplex} (and notifies
+     * observers).
+     *
+     * @param update Simplex transformation.
+     * @param simplex Current simplex.
+     * @param eval Objective function.
+     * @param comp Fitness comparator.
+     * @return the transformed simplex.
+     */
+    private Simplex applyUpdate(UnaryOperator<Simplex> update,
+                                Simplex simplex,
+                                MultivariateFunction eval,
+                                Comparator<PointValuePair> comp) {
+        final Simplex transformed = update.apply(simplex).evaluate(eval, comp);
+
+        notifyObservers(transformed, false);
+
+        return transformed;
+    }
 }

Re: [commons-math] 03/04: MATH-1625: Enable callback in "SimplexOptimizer".

Posted by Gilles Sadowski <gi...@gmail.com>.
> >
> > +    public void addObserver(Observer cb) {
> > +        if (cb == null) {
> > +            throw new NullPointerException("Callback");
> > +        }
> >
>
> These types of checks can use Objects.requireNonNull

Done.

Thanks,
Gilles

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: [commons-math] 03/04: MATH-1625: Enable callback in "SimplexOptimizer".

Posted by Alex Herbert <al...@gmail.com>.
On Sat, 14 Aug 2021 at 00:53, <er...@apache.org> wrote:

> This is an automated email from the ASF dual-hosted git repository.
>
> erans pushed a commit to branch master
> in repository https://gitbox.apache.org/repos/asf/commons-math.git
>
> commit 310b21e4d7071bbdc52e12283b0718c2d5dd5bbc
> Author: Gilles Sadowski <gi...@gmail.com>
> AuthorDate: Sat Aug 14 01:24:38 2021 +0200
>
>     MATH-1625: Enable callback in "SimplexOptimizer".
> ---
>  .../nonlinear/scalar/noderiv/SimplexOptimizer.java | 120
> +++++++++++++++++++--
>  1 file changed, 113 insertions(+), 7 deletions(-)
>
> +    public void addObserver(Observer cb) {
> +        if (cb == null) {
> +            throw new NullPointerException("Callback");
> +        }
>

These types of checks can use Objects.requireNonNull