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 org.apache.commons.math.ConvergenceException; 021 import org.apache.commons.math.FunctionEvaluationException; 022 import org.apache.commons.math.MathRuntimeException; 023 import org.apache.commons.math.analysis.UnivariateRealFunction; 024 import org.apache.commons.math.exception.util.LocalizedFormats; 025 import org.apache.commons.math.random.RandomGenerator; 026 import org.apache.commons.math.util.FastMath; 027 028 /** 029 * Special implementation of the {@link UnivariateRealOptimizer} interface adding 030 * multi-start features to an existing optimizer. 031 * <p> 032 * This class wraps a classical optimizer to use it several times in 033 * turn with different starting points in order to avoid being trapped 034 * into a local extremum when looking for a global one. 035 * </p> 036 * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 f??vr. 2011) $ 037 * @since 2.0 038 */ 039 public class MultiStartUnivariateRealOptimizer implements UnivariateRealOptimizer { 040 041 /** Serializable version identifier. */ 042 private static final long serialVersionUID = 5983375963110961019L; 043 044 /** Underlying classical optimizer. */ 045 private final UnivariateRealOptimizer optimizer; 046 047 /** Maximal number of iterations allowed. */ 048 private int maxIterations; 049 050 /** Maximal number of evaluations allowed. */ 051 private int maxEvaluations; 052 053 /** Number of iterations already performed for all starts. */ 054 private int totalIterations; 055 056 /** Number of evaluations already performed for all starts. */ 057 private int totalEvaluations; 058 059 /** Number of starts to go. */ 060 private int starts; 061 062 /** Random generator for multi-start. */ 063 private RandomGenerator generator; 064 065 /** Found optima. */ 066 private double[] optima; 067 068 /** Found function values at optima. */ 069 private double[] optimaValues; 070 071 /** 072 * Create a multi-start optimizer from a single-start optimizer 073 * @param optimizer single-start optimizer to wrap 074 * @param starts number of starts to perform (including the 075 * first one), multi-start is disabled if value is less than or 076 * equal to 1 077 * @param generator random generator to use for restarts 078 */ 079 public MultiStartUnivariateRealOptimizer(final UnivariateRealOptimizer optimizer, 080 final int starts, 081 final RandomGenerator generator) { 082 this.optimizer = optimizer; 083 this.totalIterations = 0; 084 this.starts = starts; 085 this.generator = generator; 086 this.optima = null; 087 setMaximalIterationCount(Integer.MAX_VALUE); 088 setMaxEvaluations(Integer.MAX_VALUE); 089 } 090 091 /** {@inheritDoc} */ 092 public double getFunctionValue() { 093 return optimaValues[0]; 094 } 095 096 /** {@inheritDoc} */ 097 public double getResult() { 098 return optima[0]; 099 } 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 }