fixed an error in multistart univariate optimizer:

the optima found were sorted according to the independant variable x,
not according to the function value y

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@797785 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Luc Maisonobe 2009-07-25 16:12:25 +00:00
parent 48d014dac1
commit 4e2dd4df1f
1 changed files with 71 additions and 16 deletions

View File

@ -17,8 +17,6 @@
package org.apache.commons.math.optimization; package org.apache.commons.math.optimization;
import java.util.Arrays;
import org.apache.commons.math.ConvergenceException; import org.apache.commons.math.ConvergenceException;
import org.apache.commons.math.FunctionEvaluationException; import org.apache.commons.math.FunctionEvaluationException;
import org.apache.commons.math.MathRuntimeException; import org.apache.commons.math.MathRuntimeException;
@ -65,6 +63,9 @@ public class MultiStartUnivariateRealOptimizer implements UnivariateRealOptimize
/** Found optima. */ /** Found optima. */
private double[] optima; private double[] optima;
/** Found function values at optima. */
private double[] optimaValues;
/** /**
* Create a multi-start optimizer from a single-start optimizer * Create a multi-start optimizer from a single-start optimizer
* @param optimizer single-start optimizer to wrap * @param optimizer single-start optimizer to wrap
@ -175,16 +176,17 @@ public class MultiStartUnivariateRealOptimizer implements UnivariateRealOptimize
* in the constructor. It is ordered with the results from the * in the constructor. It is ordered with the results from the
* runs that did converge first, sorted from best to worst * runs that did converge first, sorted from best to worst
* objective value (i.e in ascending order if minimizing and in * objective value (i.e in ascending order if minimizing and in
* descending order if maximizing), followed by and null elements * descending order if maximizing), followed by Double.NaN elements
* corresponding to the runs that did not converge. This means all * corresponding to the runs that did not converge. This means all
* elements will be null if the {@link #optimize(UnivariateRealFunction, * elements will be NaN if the {@link #optimize(UnivariateRealFunction,
* GoalType, double, double) optimize} method did throw a {@link * GoalType, double, double) optimize} method did throw a {@link
* ConvergenceException ConvergenceException}). This also means that * ConvergenceException ConvergenceException}). This also means that
* if the first element is non null, it is the best point found across * if the first element is not NaN, it is the best point found across
* all starts.</p> * all starts.</p>
* @return array containing the optima * @return array containing the optima
* @exception IllegalStateException if {@link #optimize(UnivariateRealFunction, * @exception IllegalStateException if {@link #optimize(UnivariateRealFunction,
* GoalType, double, double) optimize} has not been called * GoalType, double, double) optimize} has not been called
* @see #getOptimaValues()
*/ */
public double[] getOptima() throws IllegalStateException { public double[] getOptima() throws IllegalStateException {
if (optima == null) { if (optima == null) {
@ -193,6 +195,32 @@ public class MultiStartUnivariateRealOptimizer implements UnivariateRealOptimize
return optima.clone(); return optima.clone();
} }
/** Get all the function values at optima found during the last call to {@link
* #optimize(UnivariateRealFunction, GoalType, double, double) optimize}.
* <p>
* The returned array as one element for each start as specified
* in the constructor. It is ordered with the results from the
* runs that did converge first, sorted from best to worst
* objective value (i.e in ascending order if minimizing and in
* descending order if maximizing), followed by Double.NaN elements
* corresponding to the runs that did not converge. This means all
* elements will be NaN if the {@link #optimize(UnivariateRealFunction,
* GoalType, double, double) optimize} method did throw a {@link
* ConvergenceException ConvergenceException}). This also means that
* if the first element is not NaN, it is the best point found across
* all starts.</p>
* @return array containing the optima
* @exception IllegalStateException if {@link #optimize(UnivariateRealFunction,
* GoalType, double, double) optimize} has not been called
* @see #getOptima()
*/
public double[] getOptimaValues() throws IllegalStateException {
if (optimaValues == null) {
throw MathRuntimeException.createIllegalStateException("no optimum computed yet");
}
return optimaValues.clone();
}
/** {@inheritDoc} */ /** {@inheritDoc} */
public double optimize(final UnivariateRealFunction f, final GoalType goalType, public double optimize(final UnivariateRealFunction f, final GoalType goalType,
final double min, final double max) final double min, final double max)
@ -200,6 +228,7 @@ public class MultiStartUnivariateRealOptimizer implements UnivariateRealOptimize
FunctionEvaluationException { FunctionEvaluationException {
optima = new double[starts]; optima = new double[starts];
optimaValues = new double[starts];
totalIterations = 0; totalIterations = 0;
totalEvaluations = 0; totalEvaluations = 0;
@ -211,13 +240,16 @@ public class MultiStartUnivariateRealOptimizer implements UnivariateRealOptimize
optimizer.setMaxEvaluations(maxEvaluations - totalEvaluations); optimizer.setMaxEvaluations(maxEvaluations - totalEvaluations);
final double bound1 = min + generator.nextDouble() * (max - min); final double bound1 = min + generator.nextDouble() * (max - min);
final double bound2 = min + generator.nextDouble() * (max - min); final double bound2 = min + generator.nextDouble() * (max - min);
optima[i] = optimizer.optimize(f, goalType, optima[i] = optimizer.optimize(f, goalType,
Math.min(bound1, bound2), Math.min(bound1, bound2),
Math.max(bound1, bound2)); Math.max(bound1, bound2));
optimaValues[i] = optimizer.getFunctionValue();
} catch (FunctionEvaluationException fee) { } catch (FunctionEvaluationException fee) {
optima[i] = Double.NaN; optima[i] = Double.NaN;
optimaValues[i] = Double.NaN;
} catch (ConvergenceException ce) { } catch (ConvergenceException ce) {
optima[i] = Double.NaN; optima[i] = Double.NaN;
optimaValues[i] = Double.NaN;
} }
totalIterations += optimizer.getIterationCount(); totalIterations += optimizer.getIterationCount();
@ -231,14 +263,37 @@ public class MultiStartUnivariateRealOptimizer implements UnivariateRealOptimize
if (Double.isNaN(optima[i])) { if (Double.isNaN(optima[i])) {
optima[i] = optima[--lastNaN]; optima[i] = optima[--lastNaN];
optima[lastNaN + 1] = Double.NaN; optima[lastNaN + 1] = Double.NaN;
optimaValues[i] = optimaValues[--lastNaN];
optimaValues[lastNaN + 1] = Double.NaN;
} }
} }
Arrays.sort(optima, 0, lastNaN);
if (goalType == GoalType.MAXIMIZE) { double currX = optima[0];
for (int i = 0, j = lastNaN - 1; i < j; ++i, --j) { double currY = optimaValues[0];
double tmp = optima[i]; for (int j = 1; j < lastNaN; ++j) {
optima[i] = optima[j]; final double prevY = currY;
optima[j] = tmp; currX = optima[j];
currY = optimaValues[j];
if ((goalType == GoalType.MAXIMIZE) ^ (currY < prevY)) {
// the current element should be inserted closer to the beginning
int i = j - 1;
double mIX = optima[i];
double mIY = optimaValues[i];
while ((i >= 0) && ((goalType == GoalType.MAXIMIZE) ^ (currY < mIY))) {
optima[i + 1] = mIX;
optimaValues[i + 1] = mIY;
if (i-- != 0) {
mIX = optima[i];
mIY = optimaValues[i];
} else {
mIX = Double.NaN;
mIY = Double.NaN;
}
}
optima[i + 1] = currX;
optimaValues[i + 1] = currY;
currX = optima[j];
currY = optimaValues[j];
} }
} }