Home | History | Annotate | Download | only in optimization
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one or more
      3  * contributor license agreements.  See the NOTICE file distributed with
      4  * this work for additional information regarding copyright ownership.
      5  * The ASF licenses this file to You under the Apache License, Version 2.0
      6  * (the "License"); you may not use this file except in compliance with
      7  * the License.  You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package org.apache.commons.math.optimization;
     19 
     20 import org.apache.commons.math.ConvergenceException;
     21 import org.apache.commons.math.FunctionEvaluationException;
     22 import org.apache.commons.math.MathRuntimeException;
     23 import org.apache.commons.math.analysis.UnivariateRealFunction;
     24 import org.apache.commons.math.exception.util.LocalizedFormats;
     25 import org.apache.commons.math.random.RandomGenerator;
     26 import org.apache.commons.math.util.FastMath;
     27 
     28 /**
     29  * Special implementation of the {@link UnivariateRealOptimizer} interface adding
     30  * multi-start features to an existing optimizer.
     31  * <p>
     32  * This class wraps a classical optimizer to use it several times in
     33  * turn with different starting points in order to avoid being trapped
     34  * into a local extremum when looking for a global one.
     35  * </p>
     36  * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 fvr. 2011) $
     37  * @since 2.0
     38  */
     39 public class MultiStartUnivariateRealOptimizer implements UnivariateRealOptimizer {
     40 
     41     /** Serializable version identifier. */
     42     private static final long serialVersionUID = 5983375963110961019L;
     43 
     44     /** Underlying classical optimizer. */
     45     private final UnivariateRealOptimizer optimizer;
     46 
     47     /** Maximal number of iterations allowed. */
     48     private int maxIterations;
     49 
     50     /** Maximal number of evaluations allowed. */
     51     private int maxEvaluations;
     52 
     53     /** Number of iterations already performed for all starts. */
     54     private int totalIterations;
     55 
     56     /** Number of evaluations already performed for all starts. */
     57     private int totalEvaluations;
     58 
     59     /** Number of starts to go. */
     60     private int starts;
     61 
     62     /** Random generator for multi-start. */
     63     private RandomGenerator generator;
     64 
     65     /** Found optima. */
     66     private double[] optima;
     67 
     68     /** Found function values at optima. */
     69     private double[] optimaValues;
     70 
     71     /**
     72      * Create a multi-start optimizer from a single-start optimizer
     73      * @param optimizer single-start optimizer to wrap
     74      * @param starts number of starts to perform (including the
     75      * first one), multi-start is disabled if value is less than or
     76      * equal to 1
     77      * @param generator random generator to use for restarts
     78      */
     79     public MultiStartUnivariateRealOptimizer(final UnivariateRealOptimizer optimizer,
     80                                              final int starts,
     81                                              final RandomGenerator generator) {
     82         this.optimizer        = optimizer;
     83         this.totalIterations  = 0;
     84         this.starts           = starts;
     85         this.generator        = generator;
     86         this.optima           = null;
     87         setMaximalIterationCount(Integer.MAX_VALUE);
     88         setMaxEvaluations(Integer.MAX_VALUE);
     89     }
     90 
     91     /** {@inheritDoc} */
     92     public double getFunctionValue() {
     93         return optimaValues[0];
     94     }
     95 
     96     /** {@inheritDoc} */
     97     public double getResult() {
     98         return optima[0];
     99     }
    100 
    101     /** {@inheritDoc} */
    102     public double getAbsoluteAccuracy() {
    103         return optimizer.getAbsoluteAccuracy();
    104     }
    105 
    106     /** {@inheritDoc} */
    107     public int getIterationCount() {
    108         return totalIterations;
    109     }
    110 
    111     /** {@inheritDoc} */
    112     public int getMaximalIterationCount() {
    113         return maxIterations;
    114     }
    115 
    116     /** {@inheritDoc} */
    117     public int getMaxEvaluations() {
    118         return maxEvaluations;
    119     }
    120 
    121     /** {@inheritDoc} */
    122     public int getEvaluations() {
    123         return totalEvaluations;
    124     }
    125 
    126     /** {@inheritDoc} */
    127     public double getRelativeAccuracy() {
    128         return optimizer.getRelativeAccuracy();
    129     }
    130 
    131     /** {@inheritDoc} */
    132     public void resetAbsoluteAccuracy() {
    133         optimizer.resetAbsoluteAccuracy();
    134     }
    135 
    136     /** {@inheritDoc} */
    137     public void resetMaximalIterationCount() {
    138         optimizer.resetMaximalIterationCount();
    139     }
    140 
    141     /** {@inheritDoc} */
    142     public void resetRelativeAccuracy() {
    143         optimizer.resetRelativeAccuracy();
    144     }
    145 
    146     /** {@inheritDoc} */
    147     public void setAbsoluteAccuracy(double accuracy) {
    148         optimizer.setAbsoluteAccuracy(accuracy);
    149     }
    150 
    151     /** {@inheritDoc} */
    152     public void setMaximalIterationCount(int count) {
    153         this.maxIterations = count;
    154     }
    155 
    156     /** {@inheritDoc} */
    157     public void setMaxEvaluations(int maxEvaluations) {
    158         this.maxEvaluations = maxEvaluations;
    159     }
    160 
    161     /** {@inheritDoc} */
    162     public void setRelativeAccuracy(double accuracy) {
    163         optimizer.setRelativeAccuracy(accuracy);
    164     }
    165 
    166     /** Get all the optima found during the last call to {@link
    167      * #optimize(UnivariateRealFunction, GoalType, double, double) optimize}.
    168      * <p>The optimizer stores all the optima found during a set of
    169      * restarts. The {@link #optimize(UnivariateRealFunction, GoalType,
    170      * double, double) optimize} method returns the best point only. This
    171      * method returns all the points found at the end of each starts,
    172      * including the best one already returned by the {@link
    173      * #optimize(UnivariateRealFunction, GoalType, double, double) optimize}
    174      * method.
    175      * </p>
    176      * <p>
    177      * The returned array as one element for each start as specified
    178      * in the constructor. It is ordered with the results from the
    179      * runs that did converge first, sorted from best to worst
    180      * objective value (i.e in ascending order if minimizing and in
    181      * descending order if maximizing), followed by Double.NaN elements
    182      * corresponding to the runs that did not converge. This means all
    183      * elements will be NaN if the {@link #optimize(UnivariateRealFunction,
    184      * GoalType, double, double) optimize} method did throw a {@link
    185      * ConvergenceException ConvergenceException}). This also means that
    186      * if the first element is not NaN, it is the best point found across
    187      * all starts.</p>
    188      * @return array containing the optima
    189      * @exception IllegalStateException if {@link #optimize(UnivariateRealFunction,
    190      * GoalType, double, double) optimize} has not been called
    191      * @see #getOptimaValues()
    192      */
    193     public double[] getOptima() throws IllegalStateException {
    194         if (optima == null) {
    195             throw MathRuntimeException.createIllegalStateException(LocalizedFormats.NO_OPTIMUM_COMPUTED_YET);
    196         }
    197         return optima.clone();
    198     }
    199 
    200     /** Get all the function values at optima found during the last call to {@link
    201      * #optimize(UnivariateRealFunction, GoalType, double, double) optimize}.
    202      * <p>
    203      * The returned array as one element for each start as specified
    204      * in the constructor. It is ordered with the results from the
    205      * runs that did converge first, sorted from best to worst
    206      * objective value (i.e in ascending order if minimizing and in
    207      * descending order if maximizing), followed by Double.NaN elements
    208      * corresponding to the runs that did not converge. This means all
    209      * elements will be NaN if the {@link #optimize(UnivariateRealFunction,
    210      * GoalType, double, double) optimize} method did throw a {@link
    211      * ConvergenceException ConvergenceException}). This also means that
    212      * if the first element is not NaN, it is the best point found across
    213      * all starts.</p>
    214      * @return array containing the optima
    215      * @exception IllegalStateException if {@link #optimize(UnivariateRealFunction,
    216      * GoalType, double, double) optimize} has not been called
    217      * @see #getOptima()
    218      */
    219     public double[] getOptimaValues() throws IllegalStateException {
    220         if (optimaValues == null) {
    221             throw MathRuntimeException.createIllegalStateException(LocalizedFormats.NO_OPTIMUM_COMPUTED_YET);
    222         }
    223         return optimaValues.clone();
    224     }
    225 
    226     /** {@inheritDoc} */
    227     public double optimize(final UnivariateRealFunction f, final GoalType goalType,
    228                            final double min, final double max)
    229         throws ConvergenceException, FunctionEvaluationException {
    230 
    231         optima           = new double[starts];
    232         optimaValues     = new double[starts];
    233         totalIterations  = 0;
    234         totalEvaluations = 0;
    235 
    236         // multi-start loop
    237         for (int i = 0; i < starts; ++i) {
    238 
    239             try {
    240                 optimizer.setMaximalIterationCount(maxIterations - totalIterations);
    241                 optimizer.setMaxEvaluations(maxEvaluations - totalEvaluations);
    242                 final double bound1 = (i == 0) ? min : min + generator.nextDouble() * (max - min);
    243                 final double bound2 = (i == 0) ? max : min + generator.nextDouble() * (max - min);
    244                 optima[i]       = optimizer.optimize(f, goalType,
    245                                                      FastMath.min(bound1, bound2),
    246                                                      FastMath.max(bound1, bound2));
    247                 optimaValues[i] = optimizer.getFunctionValue();
    248             } catch (FunctionEvaluationException fee) {
    249                 optima[i]       = Double.NaN;
    250                 optimaValues[i] = Double.NaN;
    251             } catch (ConvergenceException ce) {
    252                 optima[i]       = Double.NaN;
    253                 optimaValues[i] = Double.NaN;
    254             }
    255 
    256             totalIterations  += optimizer.getIterationCount();
    257             totalEvaluations += optimizer.getEvaluations();
    258 
    259         }
    260 
    261         // sort the optima from best to worst, followed by NaN elements
    262         int lastNaN = optima.length;
    263         for (int i = 0; i < lastNaN; ++i) {
    264             if (Double.isNaN(optima[i])) {
    265                 optima[i] = optima[--lastNaN];
    266                 optima[lastNaN + 1] = Double.NaN;
    267                 optimaValues[i] = optimaValues[--lastNaN];
    268                 optimaValues[lastNaN + 1] = Double.NaN;
    269             }
    270         }
    271 
    272         double currX = optima[0];
    273         double currY = optimaValues[0];
    274         for (int j = 1; j < lastNaN; ++j) {
    275             final double prevY = currY;
    276             currX = optima[j];
    277             currY = optimaValues[j];
    278             if ((goalType == GoalType.MAXIMIZE) ^ (currY < prevY)) {
    279                 // the current element should be inserted closer to the beginning
    280                 int i = j - 1;
    281                 double mIX = optima[i];
    282                 double mIY = optimaValues[i];
    283                 while ((i >= 0) && ((goalType == GoalType.MAXIMIZE) ^ (currY < mIY))) {
    284                     optima[i + 1]       = mIX;
    285                     optimaValues[i + 1] = mIY;
    286                     if (i-- != 0) {
    287                         mIX = optima[i];
    288                         mIY = optimaValues[i];
    289                     } else {
    290                         mIX = Double.NaN;
    291                         mIY = Double.NaN;
    292                     }
    293                 }
    294                 optima[i + 1]       = currX;
    295                 optimaValues[i + 1] = currY;
    296                 currX = optima[j];
    297                 currY = optimaValues[j];
    298             }
    299         }
    300 
    301         if (Double.isNaN(optima[0])) {
    302             throw new OptimizationException(
    303                     LocalizedFormats.NO_CONVERGENCE_WITH_ANY_START_POINT,
    304                     starts);
    305         }
    306 
    307         // return the found point given the best objective function value
    308         return optima[0];
    309 
    310     }
    311 
    312     /** {@inheritDoc} */
    313     public double optimize(final UnivariateRealFunction f, final GoalType goalType,
    314                            final double min, final double max, final double startValue)
    315             throws ConvergenceException, FunctionEvaluationException {
    316         return optimize(f, goalType, min, max);
    317     }
    318 }
    319