blob: d2d5268234179a473b3b5e8d7f46dd0cd44a319c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.optimization;
import java.util.Arrays;
import java.util.Comparator;
import org.apache.commons.math.MathRuntimeException;
import org.apache.commons.math.analysis.DifferentiableMultivariateVectorialFunction;
import org.apache.commons.math.FunctionEvaluationException;
import org.apache.commons.math.exception.util.LocalizedFormats;
import org.apache.commons.math.random.RandomVectorGenerator;
/**
* Special implementation of the {@link DifferentiableMultivariateVectorialOptimizer} interface adding
* multi-start features to an existing optimizer.
* <p>
* This class wraps a classical optimizer to use it several times in
* turn with different starting points in order to avoid being trapped
* into a local extremum when looking for a global one.
* </p>
* @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
* @since 2.0
*/
public class MultiStartDifferentiableMultivariateVectorialOptimizer
implements DifferentiableMultivariateVectorialOptimizer {
/** Serializable version identifier. */
private static final long serialVersionUID = 9206382258980561530L;
/** Underlying classical optimizer. */
private final DifferentiableMultivariateVectorialOptimizer optimizer;
/** Maximal number of iterations allowed. */
private int maxIterations;
/** Number of iterations already performed for all starts. */
private int totalIterations;
/** Maximal number of evaluations allowed. */
private int maxEvaluations;
/** Number of evaluations already performed for all starts. */
private int totalEvaluations;
/** Number of jacobian evaluations already performed for all starts. */
private int totalJacobianEvaluations;
/** Number of starts to go. */
private int starts;
/** Random generator for multi-start. */
private RandomVectorGenerator generator;
/** Found optima. */
private VectorialPointValuePair[] optima;
/**
* Create a multi-start optimizer from a single-start optimizer
* @param optimizer single-start optimizer to wrap
* @param starts number of starts to perform (including the
* first one), multi-start is disabled if value is less than or
* equal to 1
* @param generator random vector generator to use for restarts
*/
public MultiStartDifferentiableMultivariateVectorialOptimizer(
final DifferentiableMultivariateVectorialOptimizer optimizer,
final int starts,
final RandomVectorGenerator generator) {
this.optimizer = optimizer;
this.totalIterations = 0;
this.totalEvaluations = 0;
this.totalJacobianEvaluations = 0;
this.starts = starts;
this.generator = generator;
this.optima = null;
setMaxIterations(Integer.MAX_VALUE);
setMaxEvaluations(Integer.MAX_VALUE);
}
/** Get all the optima found during the last call to {@link
* #optimize(DifferentiableMultivariateVectorialFunction,
* double[], double[], double[]) optimize}.
* <p>The optimizer stores all the optima found during a set of
* restarts. The {@link #optimize(DifferentiableMultivariateVectorialFunction,
* double[], double[], double[]) optimize} method returns the
* best point only. This method returns all the points found at the
* end of each starts, including the best one already returned by the {@link
* #optimize(DifferentiableMultivariateVectorialFunction, double[],
* double[], double[]) optimize} method.
* </p>
* <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 and null elements
* corresponding to the runs that did not converge. This means all
* elements will be null if the {@link #optimize(DifferentiableMultivariateVectorialFunction,
* double[], double[], double[]) optimize} method did throw a {@link
* org.apache.commons.math.ConvergenceException ConvergenceException}).
* This also means that if the first element is non null, it is the best
* point found across all starts.</p>
* @return array containing the optima
* @exception IllegalStateException if {@link #optimize(DifferentiableMultivariateVectorialFunction,
* double[], double[], double[]) optimize} has not been called
*/
public VectorialPointValuePair[] getOptima() throws IllegalStateException {
if (optima == null) {
throw MathRuntimeException.createIllegalStateException(LocalizedFormats.NO_OPTIMUM_COMPUTED_YET);
}
return optima.clone();
}
/** {@inheritDoc} */
public void setMaxIterations(int maxIterations) {
this.maxIterations = maxIterations;
}
/** {@inheritDoc} */
public int getMaxIterations() {
return maxIterations;
}
/** {@inheritDoc} */
public int getIterations() {
return totalIterations;
}
/** {@inheritDoc} */
public void setMaxEvaluations(int maxEvaluations) {
this.maxEvaluations = maxEvaluations;
}
/** {@inheritDoc} */
public int getMaxEvaluations() {
return maxEvaluations;
}
/** {@inheritDoc} */
public int getEvaluations() {
return totalEvaluations;
}
/** {@inheritDoc} */
public int getJacobianEvaluations() {
return totalJacobianEvaluations;
}
/** {@inheritDoc} */
public void setConvergenceChecker(VectorialConvergenceChecker checker) {
optimizer.setConvergenceChecker(checker);
}
/** {@inheritDoc} */
public VectorialConvergenceChecker getConvergenceChecker() {
return optimizer.getConvergenceChecker();
}
/** {@inheritDoc} */
public VectorialPointValuePair optimize(final DifferentiableMultivariateVectorialFunction f,
final double[] target, final double[] weights,
final double[] startPoint)
throws FunctionEvaluationException, OptimizationException, IllegalArgumentException {
optima = new VectorialPointValuePair[starts];
totalIterations = 0;
totalEvaluations = 0;
totalJacobianEvaluations = 0;
// multi-start loop
for (int i = 0; i < starts; ++i) {
try {
optimizer.setMaxIterations(maxIterations - totalIterations);
optimizer.setMaxEvaluations(maxEvaluations - totalEvaluations);
optima[i] = optimizer.optimize(f, target, weights,
(i == 0) ? startPoint : generator.nextVector());
} catch (FunctionEvaluationException fee) {
optima[i] = null;
} catch (OptimizationException oe) {
optima[i] = null;
}
totalIterations += optimizer.getIterations();
totalEvaluations += optimizer.getEvaluations();
totalJacobianEvaluations += optimizer.getJacobianEvaluations();
}
// sort the optima from best to worst, followed by null elements
Arrays.sort(optima, new Comparator<VectorialPointValuePair>() {
public int compare(final VectorialPointValuePair o1, final VectorialPointValuePair o2) {
if (o1 == null) {
return (o2 == null) ? 0 : +1;
} else if (o2 == null) {
return -1;
}
return Double.compare(weightedResidual(o1), weightedResidual(o2));
}
private double weightedResidual(final VectorialPointValuePair pv) {
final double[] value = pv.getValueRef();
double sum = 0;
for (int i = 0; i < value.length; ++i) {
final double ri = value[i] - target[i];
sum += weights[i] * ri * ri;
}
return sum;
}
});
if (optima[0] == null) {
throw new OptimizationException(
LocalizedFormats.NO_CONVERGENCE_WITH_ANY_START_POINT,
starts);
}
// return the found point given the best objective function value
return optima[0];
}
}