001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.math.optimization;
019    
020    import java.util.Arrays;
021    import java.util.Comparator;
022    
023    import org.apache.commons.math.FunctionEvaluationException;
024    import org.apache.commons.math.MathRuntimeException;
025    import org.apache.commons.math.analysis.DifferentiableMultivariateRealFunction;
026    import org.apache.commons.math.exception.util.LocalizedFormats;
027    import org.apache.commons.math.random.RandomVectorGenerator;
028    
029    /**
030     * Special implementation of the {@link DifferentiableMultivariateRealOptimizer} interface adding
031     * multi-start features to an existing optimizer.
032     * <p>
033     * This class wraps a classical optimizer to use it several times in
034     * turn with different starting points in order to avoid being trapped
035     * into a local extremum when looking for a global one.
036     * </p>
037     * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 f??vr. 2011) $
038     * @since 2.0
039     */
040    public class MultiStartDifferentiableMultivariateRealOptimizer
041        implements DifferentiableMultivariateRealOptimizer {
042    
043        /** Underlying classical optimizer. */
044        private final DifferentiableMultivariateRealOptimizer optimizer;
045    
046        /** Maximal number of iterations allowed. */
047        private int maxIterations;
048    
049        /** Number of iterations already performed for all starts. */
050        private int totalIterations;
051    
052        /** Maximal number of evaluations allowed. */
053        private int maxEvaluations;
054    
055        /** Number of evaluations already performed for all starts. */
056        private int totalEvaluations;
057    
058        /** Number of gradient evaluations already performed for all starts. */
059        private int totalGradientEvaluations;
060    
061        /** Number of starts to go. */
062        private int starts;
063    
064        /** Random generator for multi-start. */
065        private RandomVectorGenerator generator;
066    
067        /** Found optima. */
068        private RealPointValuePair[] optima;
069    
070        /**
071         * Create a multi-start optimizer from a single-start optimizer
072         * @param optimizer single-start optimizer to wrap
073         * @param starts number of starts to perform (including the
074         * first one), multi-start is disabled if value is less than or
075         * equal to 1
076         * @param generator random vector generator to use for restarts
077         */
078        public MultiStartDifferentiableMultivariateRealOptimizer(final DifferentiableMultivariateRealOptimizer optimizer,
079                                                                 final int starts,
080                                                                 final RandomVectorGenerator generator) {
081            this.optimizer                = optimizer;
082            this.totalIterations          = 0;
083            this.totalEvaluations         = 0;
084            this.totalGradientEvaluations = 0;
085            this.starts                   = starts;
086            this.generator                = generator;
087            this.optima                   = null;
088            setMaxIterations(Integer.MAX_VALUE);
089            setMaxEvaluations(Integer.MAX_VALUE);
090        }
091    
092        /** Get all the optima found during the last call to {@link
093         * #optimize(DifferentiableMultivariateRealFunction, GoalType, double[])
094         * optimize}.
095         * <p>The optimizer stores all the optima found during a set of
096         * restarts. The {@link #optimize(DifferentiableMultivariateRealFunction,
097         * GoalType, double[]) optimize} method returns the best point only. This
098         * method returns all the points found at the end of each starts,
099         * including the best one already returned by the {@link
100         * #optimize(DifferentiableMultivariateRealFunction, GoalType, double[])
101         * optimize} method.
102         * </p>
103         * <p>
104         * The returned array as one element for each start as specified
105         * in the constructor. It is ordered with the results from the
106         * runs that did converge first, sorted from best to worst
107         * objective value (i.e in ascending order if minimizing and in
108         * descending order if maximizing), followed by and null elements
109         * corresponding to the runs that did not converge. This means all
110         * elements will be null if the {@link #optimize(DifferentiableMultivariateRealFunction,
111         * GoalType, double[]) optimize} method did throw a {@link
112         * org.apache.commons.math.ConvergenceException ConvergenceException}).
113         * This also means that if the first element is non null, it is the best
114         * point found across all starts.</p>
115         * @return array containing the optima
116         * @exception IllegalStateException if {@link #optimize(DifferentiableMultivariateRealFunction,
117         * GoalType, double[]) optimize} has not been called
118         */
119        public RealPointValuePair[] getOptima() throws IllegalStateException {
120            if (optima == null) {
121                throw MathRuntimeException.createIllegalStateException(LocalizedFormats.NO_OPTIMUM_COMPUTED_YET);
122            }
123            return optima.clone();
124        }
125    
126        /** {@inheritDoc} */
127        public void setMaxIterations(int maxIterations) {
128            this.maxIterations = maxIterations;
129        }
130    
131        /** {@inheritDoc} */
132        public int getMaxIterations() {
133            return maxIterations;
134        }
135    
136        /** {@inheritDoc} */
137        public int getIterations() {
138            return totalIterations;
139        }
140    
141        /** {@inheritDoc} */
142        public void setMaxEvaluations(int maxEvaluations) {
143            this.maxEvaluations = maxEvaluations;
144        }
145    
146        /** {@inheritDoc} */
147        public int getMaxEvaluations() {
148            return maxEvaluations;
149        }
150    
151        /** {@inheritDoc} */
152        public int getEvaluations() {
153            return totalEvaluations;
154        }
155    
156        /** {@inheritDoc} */
157        public int getGradientEvaluations() {
158            return totalGradientEvaluations;
159        }
160    
161        /** {@inheritDoc} */
162        public void setConvergenceChecker(RealConvergenceChecker checker) {
163            optimizer.setConvergenceChecker(checker);
164        }
165    
166        /** {@inheritDoc} */
167        public RealConvergenceChecker getConvergenceChecker() {
168            return optimizer.getConvergenceChecker();
169        }
170    
171        /** {@inheritDoc} */
172        public RealPointValuePair optimize(final DifferentiableMultivariateRealFunction f,
173                                             final GoalType goalType,
174                                             double[] startPoint)
175            throws FunctionEvaluationException, OptimizationException, FunctionEvaluationException {
176    
177            optima                   = new RealPointValuePair[starts];
178            totalIterations          = 0;
179            totalEvaluations         = 0;
180            totalGradientEvaluations = 0;
181    
182            // multi-start loop
183            for (int i = 0; i < starts; ++i) {
184    
185                try {
186                    optimizer.setMaxIterations(maxIterations - totalIterations);
187                    optimizer.setMaxEvaluations(maxEvaluations - totalEvaluations);
188                    optima[i] = optimizer.optimize(f, goalType,
189                                                   (i == 0) ? startPoint : generator.nextVector());
190                } catch (FunctionEvaluationException fee) {
191                    optima[i] = null;
192                } catch (OptimizationException oe) {
193                    optima[i] = null;
194                }
195    
196                totalIterations          += optimizer.getIterations();
197                totalEvaluations         += optimizer.getEvaluations();
198                totalGradientEvaluations += optimizer.getGradientEvaluations();
199    
200            }
201    
202            // sort the optima from best to worst, followed by null elements
203            Arrays.sort(optima, new Comparator<RealPointValuePair>() {
204                public int compare(final RealPointValuePair o1, final RealPointValuePair o2) {
205                    if (o1 == null) {
206                        return (o2 == null) ? 0 : +1;
207                    } else if (o2 == null) {
208                        return -1;
209                    }
210                    final double v1 = o1.getValue();
211                    final double v2 = o2.getValue();
212                    return (goalType == GoalType.MINIMIZE) ?
213                            Double.compare(v1, v2) : Double.compare(v2, v1);
214                }
215            });
216    
217            if (optima[0] == null) {
218                throw new OptimizationException(
219                        LocalizedFormats.NO_CONVERGENCE_WITH_ANY_START_POINT,
220                        starts);
221            }
222    
223            // return the found point given the best objective function value
224            return optima[0];
225    
226        }
227    
228    }