added the optimization package from Mantissa

git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/math/trunk@512381 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Luc Maisonobe 2007-02-27 20:23:52 +00:00
parent c3feb16615
commit cd301c9e12
10 changed files with 651 additions and 629 deletions

View File

@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.optimization;
package org.apache.commons.math.optimization;
/** This interface specifies how to check if a {@link
* DirectSearchOptimizer direct search method} has converged.

View File

@ -15,9 +15,9 @@
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.optimization;
package org.apache.commons.math.optimization;
import org.spaceroots.mantissa.MantissaException;
import org.apache.commons.math.MathException;
/** This class represents exceptions thrown by cost functions.
@ -27,40 +27,31 @@ import org.spaceroots.mantissa.MantissaException;
*/
public class CostException
extends MantissaException {
extends MathException {
/** Simple constructor.
* Build an exception with a default message
*/
public CostException() {
super("cost exception");
}
/** Serializable version identifier. */
private static final long serialVersionUID = 467695563268795689L;
/** Simple constructor.
* Build an exception with the specified message
* @param message exception message
*/
public CostException(String message) {
super(message);
}
/** Simple constructor.
* Build an exception from a cause
* @param cause cause of this exception
*/
public CostException(Throwable cause) {
super(cause);
}
/** Simple constructor.
* Build an exception from a message and a cause
* @param message exception message
* @param cause cause of this exception
*/
public CostException(String message, Throwable cause) {
super(message, cause);
}
private static final long serialVersionUID = -6099968585593678071L;
/**
* Constructs a new <code>MathException</code> with specified
* formatted detail message.
* Message formatting is delegated to {@link java.text.MessageFormat}.
* @param pattern format specifier
* @param arguments format arguments
*/
public CostException(String pattern, Object[] arguments) {
super(pattern, arguments);
}
/**
* Constructs a new <code>MathException</code> with specified
* nested <code>Throwable</code> root cause.
*
* @param rootCause the exception or error that caused this exception
* to be thrown.
*/
public CostException(Throwable rootCause) {
super(rootCause);
}
}

View File

@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.optimization;
package org.apache.commons.math.optimization;
/** This interface represents a cost function to be minimized.
* @author Luc Maisonobe

View File

@ -0,0 +1,600 @@
//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.ConvergenceException;
import org.apache.commons.math.DimensionMismatchException;
import org.apache.commons.math.random.CorrelatedRandomVectorGenerator;
import org.apache.commons.math.random.JDKRandomGenerator;
import org.apache.commons.math.random.NotPositiveDefiniteMatrixException;
import org.apache.commons.math.random.RandomGenerator;
import org.apache.commons.math.random.RandomVectorGenerator;
import org.apache.commons.math.random.UncorrelatedRandomVectorGenerator;
import org.apache.commons.math.random.UniformRandomGenerator;
import org.apache.commons.math.stat.descriptive.moment.VectorialCovariance;
import org.apache.commons.math.stat.descriptive.moment.VectorialMean;
/** This class implements simplex-based direct search optimization
* algorithms.
* <p>Direct search methods only use cost function values, they don't
* need derivatives and don't either try to compute approximation of
* the derivatives. According to a 1996 paper by Margaret H. Wright
* (<a href="http://cm.bell-labs.com/cm/cs/doc/96/4-02.ps.gz">Direct
* Search Methods: Once Scorned, Now Respectable</a>), they are used
* when either the computation of the derivative is impossible (noisy
* functions, unpredictable dicontinuities) or difficult (complexity,
* computation cost). In the first cases, rather than an optimum, a
* <em>not too bad</em> point is desired. In the latter cases, an
* optimum is desired but cannot be reasonably found. In all cases
* direct search methods can be useful.</p>
* <p>Simplex-based direct search methods are based on comparison of
* the cost function values at the vertices of a simplex (which is a
* set of n+1 points in dimension n) that is updated by the algorithms
* steps.</p>
* <p>The instances can be built either in single-start or in
* multi-start mode. Multi-start is a traditional way to try to avoid
* beeing trapped in a local minimum and miss the global minimum of a
* function. It can also be used to verify the convergence of an
* algorithm. In multi-start mode, the {@link #minimizes(CostFunction,
* int, ConvergenceChecker, double[], double[]) minimizes}
* method returns the best minimum found after all starts, and the
* {@link #getMinima getMinima} method can be used to retrieve all
* minima from all starts (including the one already provided by the
* {@link #minimizes(CostFunction, int, ConvergenceChecker, double[],
* double[]) minimizes} method).</p>
* <p>This class is the base class performing the boilerplate simplex
* initialization and handling. The simplex update by itself is
* performed by the derived classes according to the implemented
* algorithms.</p>
* @author Luc Maisonobe
* @version $Id: DirectSearchOptimizer.java 1705 2006-09-17 19:57:39Z luc $
* @see CostFunction
* @see NelderMead
* @see MultiDirectional
*/
public abstract class DirectSearchOptimizer {
/** Simple constructor.
*/
protected DirectSearchOptimizer() {
}
/** Minimizes a cost function.
* <p>The initial simplex is built from two vertices that are
* considered to represent two opposite vertices of a box parallel
* to the canonical axes of the space. The simplex is the subset of
* vertices encountered while going from vertexA to vertexB
* travelling along the box edges only. This can be seen as a scaled
* regular simplex using the projected separation between the given
* points as the scaling factor along each coordinate axis.</p>
* <p>The optimization is performed in single-start mode.</p>
* @param f cost function
* @param maxEvaluations maximal number of function calls for each
* start (note that the number will be checked <em>after</em>
* complete simplices have been evaluated, this means that in some
* cases this number will be exceeded by a few units, depending on
* the dimension of the problem)
* @param checker object to use to check for convergence
* @param vertexA first vertex
* @param vertexB last vertex
* @return the point/cost pairs giving the minimal cost
* @exception CostException if the cost function throws one during
* the search
* @exception NoConvergenceException if none of the starts did
* converge (it is not thrown if at least one start did converge)
*/
public PointCostPair minimizes(CostFunction f, int maxEvaluations,
ConvergenceChecker checker,
double[] vertexA, double[] vertexB)
throws CostException, ConvergenceException {
// set up optimizer
buildSimplex(vertexA, vertexB);
setSingleStart();
// compute minimum
return minimizes(f, maxEvaluations, checker);
}
/** Minimizes a cost function.
* <p>The initial simplex is built from two vertices that are
* considered to represent two opposite vertices of a box parallel
* to the canonical axes of the space. The simplex is the subset of
* vertices encountered while going from vertexA to vertexB
* travelling along the box edges only. This can be seen as a scaled
* regular simplex using the projected separation between the given
* points as the scaling factor along each coordinate axis.</p>
* <p>The optimization is performed in multi-start mode.</p>
* @param f cost function
* @param maxEvaluations maximal number of function calls for each
* start (note that the number will be checked <em>after</em>
* complete simplices have been evaluated, this means that in some
* cases this number will be exceeded by a few units, depending on
* the dimension of the problem)
* @param checker object to use to check for convergence
* @param vertexA first vertex
* @param vertexB last vertex
* @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 seed seed for the random vector generator
* @return the point/cost pairs giving the minimal cost
* @exception CostException if the cost function throws one during
* the search
* @exception NoConvergenceException if none of the starts did
* converge (it is not thrown if at least one start did converge)
*/
public PointCostPair minimizes(CostFunction f, int maxEvaluations,
ConvergenceChecker checker,
double[] vertexA, double[] vertexB,
int starts, long seed)
throws CostException, ConvergenceException {
// set up the simplex travelling around the box
buildSimplex(vertexA, vertexB);
// we consider the simplex could have been produced by a generator
// having its mean value at the center of the box, the standard
// deviation along each axe beeing the corresponding half size
double[] mean = new double[vertexA.length];
double[] standardDeviation = new double[vertexA.length];
for (int i = 0; i < vertexA.length; ++i) {
mean[i] = 0.5 * (vertexA[i] + vertexB[i]);
standardDeviation[i] = 0.5 * Math.abs(vertexA[i] - vertexB[i]);
}
RandomGenerator rg = new JDKRandomGenerator();
rg.setSeed(seed);
UniformRandomGenerator urg = new UniformRandomGenerator(rg);
RandomVectorGenerator rvg =
new UncorrelatedRandomVectorGenerator(mean, standardDeviation, urg);
setMultiStart(starts, rvg);
// compute minimum
return minimizes(f, maxEvaluations, checker);
}
/** Minimizes a cost function.
* <p>The simplex is built from all its vertices.</p>
* <p>The optimization is performed in single-start mode.</p>
* @param f cost function
* @param maxEvaluations maximal number of function calls for each
* start (note that the number will be checked <em>after</em>
* complete simplices have been evaluated, this means that in some
* cases this number will be exceeded by a few units, depending on
* the dimension of the problem)
* @param checker object to use to check for convergence
* @param vertices array containing all vertices of the simplex
* @return the point/cost pairs giving the minimal cost
* @exception CostException if the cost function throws one during
* the search
* @exception ConvergenceException if none of the starts did
* converge (it is not thrown if at least one start did converge)
*/
public PointCostPair minimizes(CostFunction f, int maxEvaluations,
ConvergenceChecker checker,
double[][] vertices)
throws CostException, ConvergenceException {
// set up optimizer
buildSimplex(vertices);
setSingleStart();
// compute minimum
return minimizes(f, maxEvaluations, checker);
}
/** Minimizes a cost function.
* <p>The simplex is built from all its vertices.</p>
* <p>The optimization is performed in multi-start mode.</p>
* @param f cost function
* @param maxEvaluations maximal number of function calls for each
* start (note that the number will be checked <em>after</em>
* complete simplices have been evaluated, this means that in some
* cases this number will be exceeded by a few units, depending on
* the dimension of the problem)
* @param checker object to use to check for convergence
* @param vertices array containing all vertices of the simplex
* @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 seed seed for the random vector generator
* @return the point/cost pairs giving the minimal cost
* @exception NotPositiveDefiniteMatrixException if the vertices
* array is degenerated
* @exception CostException if the cost function throws one during
* the search
* @exception ConvergenceException if none of the starts did
* converge (it is not thrown if at least one start did converge)
*/
public PointCostPair minimizes(CostFunction f, int maxEvaluations,
ConvergenceChecker checker,
double[][] vertices,
int starts, long seed)
throws NotPositiveDefiniteMatrixException,
CostException, ConvergenceException {
try {
// store the points into the simplex
buildSimplex(vertices);
// compute the statistical properties of the simplex points
VectorialMean meanStat = new VectorialMean(vertices[0].length);
VectorialCovariance covStat = new VectorialCovariance(vertices[0].length);
for (int i = 0; i < vertices.length; ++i) {
meanStat.increment(vertices[i]);
covStat.increment(vertices[i]);
}
RandomGenerator rg = new JDKRandomGenerator();
rg.setSeed(seed);
RandomVectorGenerator rvg =
new CorrelatedRandomVectorGenerator(meanStat.getResult(),
covStat.getResult(),
new UniformRandomGenerator(rg));
setMultiStart(starts, rvg);
// compute minimum
return minimizes(f, maxEvaluations, checker);
} catch (DimensionMismatchException dme) {
// this should not happen
throw new RuntimeException("internal error");
}
}
/** Minimizes a cost function.
* <p>The simplex is built randomly.</p>
* <p>The optimization is performed in single-start mode.</p>
* @param f cost function
* @param maxEvaluations maximal number of function calls for each
* start (note that the number will be checked <em>after</em>
* complete simplices have been evaluated, this means that in some
* cases this number will be exceeded by a few units, depending on
* the dimension of the problem)
* @param checker object to use to check for convergence
* @param generator random vector generator
* @return the point/cost pairs giving the minimal cost
* @exception CostException if the cost function throws one during
* the search
* @exception ConvergenceException if none of the starts did
* converge (it is not thrown if at least one start did converge)
*/
public PointCostPair minimizes(CostFunction f, int maxEvaluations,
ConvergenceChecker checker,
RandomVectorGenerator generator)
throws CostException, ConvergenceException {
// set up optimizer
buildSimplex(generator);
setSingleStart();
// compute minimum
return minimizes(f, maxEvaluations, checker);
}
/** Minimizes a cost function.
* <p>The simplex is built randomly.</p>
* <p>The optimization is performed in multi-start mode.</p>
* @param f cost function
* @param maxEvaluations maximal number of function calls for each
* start (note that the number will be checked <em>after</em>
* complete simplices have been evaluated, this means that in some
* cases this number will be exceeded by a few units, depending on
* the dimension of the problem)
* @param checker object to use to check for convergence
* @param generator random vector generator
* @param starts number of starts to perform (including the
* first one), multi-start is disabled if value is less than or
* equal to 1
* @return the point/cost pairs giving the minimal cost
* @exception CostException if the cost function throws one during
* the search
* @exception ConvergenceException if none of the starts did
* converge (it is not thrown if at least one start did converge)
*/
public PointCostPair minimizes(CostFunction f, int maxEvaluations,
ConvergenceChecker checker,
RandomVectorGenerator generator,
int starts)
throws CostException, ConvergenceException {
// set up optimizer
buildSimplex(generator);
setMultiStart(starts, generator);
// compute minimum
return minimizes(f, maxEvaluations, checker);
}
/** Build a simplex from two extreme vertices.
* <p>The two vertices are considered to represent two opposite
* vertices of a box parallel to the canonical axes of the
* space. The simplex is the subset of vertices encountered while
* going from vertexA to vertexB travelling along the box edges
* only. This can be seen as a scaled regular simplex using the
* projected separation between the given points as the scaling
* factor along each coordinate axis.</p>
* @param vertexA first vertex
* @param vertexB last vertex
*/
private void buildSimplex(double[] vertexA, double[] vertexB) {
int n = vertexA.length;
simplex = new PointCostPair[n + 1];
// set up the simplex travelling around the box
for (int i = 0; i <= n; ++i) {
double[] vertex = new double[n];
if (i > 0) {
System.arraycopy(vertexB, 0, vertex, 0, i);
}
if (i < n) {
System.arraycopy(vertexA, i, vertex, i, n - i);
}
simplex[i] = new PointCostPair(vertex, Double.NaN);
}
}
/** Build a simplex from all its points.
* @param vertices array containing all vertices of the simplex
*/
private void buildSimplex(double[][] vertices) {
int n = vertices.length - 1;
simplex = new PointCostPair[n + 1];
for (int i = 0; i <= n; ++i) {
simplex[i] = new PointCostPair(vertices[i], Double.NaN);
}
}
/** Build a simplex randomly.
* @param generator random vector generator
*/
private void buildSimplex(RandomVectorGenerator generator) {
// use first vector size to compute the number of points
double[] vertex = generator.nextVector();
int n = vertex.length;
simplex = new PointCostPair[n + 1];
simplex[0] = new PointCostPair(vertex, Double.NaN);
// fill up the vertex
for (int i = 1; i <= n; ++i) {
simplex[i] = new PointCostPair(generator.nextVector(), Double.NaN);
}
}
/** Set up single-start mode.
*/
private void setSingleStart() {
starts = 1;
generator = null;
minima = null;
}
/** Set up multi-start mode.
* @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 void setMultiStart(int starts, RandomVectorGenerator generator) {
if (starts < 2) {
this.starts = 1;
this.generator = null;
minima = null;
} else {
this.starts = starts;
this.generator = generator;
minima = null;
}
}
/** Get all the minima found during the last call to {@link
* #minimizes(CostFunction, int, ConvergenceChecker, double[], double[])
* minimizes}.
* <p>The optimizer stores all the minima found during a set of
* restarts when multi-start mode is enabled. The {@link
* #minimizes(CostFunction, int, ConvergenceChecker, double[], double[])
* minimizes} 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 #minimizes(CostFunction,
* int, ConvergenceChecker, double[], double[]) minimizes} method.
* The array as one element for each start as specified in the constructor
* (it has one element only if optimizer has been set up for single-start).</p>
* <p>The array containing the minima is ordered with the results
* from the runs that did converge first, sorted from lowest to
* highest minimum cost, and null elements corresponding to the runs
* that did not converge (all elements will be null if the {@link
* #minimizes(CostFunction, int, ConvergenceChecker, double[], double[])
* minimizes} method throwed a {@link ConvergenceException
* ConvergenceException}).</p>
* @return array containing the minima, or null if {@link
* #minimizes(CostFunction, int, ConvergenceChecker, double[], double[])
* minimizes} has not been called
*/
public PointCostPair[] getMinima() {
return (PointCostPair[]) minima.clone();
}
/** Minimizes a cost function.
* @param f cost function
* @param maxEvaluations maximal number of function calls for each
* start (note that the number will be checked <em>after</em>
* complete simplices have been evaluated, this means that in some
* cases this number will be exceeded by a few units, depending on
* the dimension of the problem)
* @param checker object to use to check for convergence
* @return the point/cost pairs giving the minimal cost
* @exception CostException if the cost function throws one during
* the search
* @exception ConvergenceException if none of the starts did
* converge (it is not thrown if at least one start did converge)
*/
private PointCostPair minimizes(CostFunction f, int maxEvaluations,
ConvergenceChecker checker)
throws CostException, ConvergenceException {
this.f = f;
minima = new PointCostPair[starts];
// multi-start loop
for (int i = 0; i < starts; ++i) {
evaluations = 0;
evaluateSimplex();
for (boolean loop = true; loop;) {
if (checker.converged(simplex)) {
// we have found a minimum
minima[i] = simplex[0];
loop = false;
} else if (evaluations >= maxEvaluations) {
// this start did not converge, try a new one
minima[i] = null;
loop = false;
} else {
iterateSimplex();
}
}
if (i < (starts - 1)) {
// restart
buildSimplex(generator);
}
}
// sort the minima from lowest cost to highest cost, followed by
// null elements
Arrays.sort(minima, pointCostPairComparator);
// return the found point given the lowest cost
if (minima[0] == null) {
throw new ConvergenceException("none of the {0} start points"
+ " lead to convergence",
new String[] {
Integer.toString(starts)
});
}
return minima[0];
}
/** Compute the next simplex of the algorithm.
*/
protected abstract void iterateSimplex()
throws CostException;
/** Evaluate the cost on one point.
* <p>A side effect of this method is to count the number of
* function evaluations</p>
* @param x point on which the cost function should be evaluated
* @return cost at the given point
* @exception CostException if no cost can be computed for the parameters
*/
protected double evaluateCost(double[] x)
throws CostException {
evaluations++;
return f.cost(x);
}
/** Evaluate all the non-evaluated points of the simplex.
* @exception CostException if no cost can be computed for the parameters
*/
protected void evaluateSimplex()
throws CostException {
// evaluate the cost at all non-evaluated simplex points
for (int i = 0; i < simplex.length; ++i) {
PointCostPair pair = simplex[i];
if (Double.isNaN(pair.cost)) {
simplex[i] = new PointCostPair(pair.point, evaluateCost(pair.point));
}
}
// sort the simplex from lowest cost to highest cost
Arrays.sort(simplex, pointCostPairComparator);
}
/** Replace the worst point of the simplex by a new point.
* @param pointCostPair point to insert
*/
protected void replaceWorstPoint(PointCostPair pointCostPair) {
int n = simplex.length - 1;
for (int i = 0; i < n; ++i) {
if (simplex[i].cost > pointCostPair.cost) {
PointCostPair tmp = simplex[i];
simplex[i] = pointCostPair;
pointCostPair = tmp;
}
}
simplex[n] = pointCostPair;
}
/** Comparator for {@link PointCostPair PointCostPair} objects. */
private static Comparator pointCostPairComparator = new Comparator() {
public int compare(Object o1, Object o2) {
if (o1 == null) {
return (o2 == null) ? 0 : +1;
} else if (o2 == null) {
return -1;
} else {
double cost1 = ((PointCostPair) o1).cost;
double cost2 = ((PointCostPair) o2).cost;
return (cost1 < cost2) ? -1 : ((o1 == o2) ? 0 : +1);
}
}
};
/** Simplex. */
protected PointCostPair[] simplex;
/** Cost function. */
private CostFunction f;
/** Number of evaluations already performed. */
private int evaluations;
/** Number of starts to go. */
private int starts;
/** Random generator for multi-start. */
private RandomVectorGenerator generator;
/** Found minima. */
private PointCostPair[] minima;
}

View File

@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.optimization;
package org.apache.commons.math.optimization;
/** This class implements the multi-directional direct search method.

View File

@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.optimization;
package org.apache.commons.math.optimization;
/** This class implements the Nelder-Mead direct search method.

View File

@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.optimization;
package org.apache.commons.math.optimization;
/** This class holds a point and its associated cost.
* <p>This is a simple immutable container.</p>

View File

@ -1,583 +0,0 @@
// 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.spaceroots.mantissa.optimization;
import org.spaceroots.mantissa.random.RandomVectorGenerator;
import org.spaceroots.mantissa.random.UncorrelatedRandomVectorGenerator;
import org.spaceroots.mantissa.random.CorrelatedRandomVectorGenerator;
import org.spaceroots.mantissa.random.UniformRandomGenerator;
import org.spaceroots.mantissa.random.VectorialSampleStatistics;
import org.spaceroots.mantissa.random.NotPositiveDefiniteMatrixException;
import java.util.Arrays;
import java.util.Comparator;
/** This class implements simplex-based direct search optimization
* algorithms.
* <p>Direct search method only use cost function values, they don't
* need derivatives and don't either try to compute approximation of
* the derivatives. According to a 1996 paper by Margaret H. Wright
* (<a href="http://cm.bell-labs.com/cm/cs/doc/96/4-02.ps.gz">Direct
* Search Methods: Once Scorned, Now Respectable</a>), they are used
* when either the computation of the derivative is impossible (noisy
* functions, unpredictable dicontinuities) or difficult (complexity,
* computation cost). In the first cases, rather than an optimum, a
* <em>not too bad</em> point is desired. In the latter cases, an
* optimum is desired but cannot be reasonably found. In all cases
* direct search methods can be useful.</p>
* <p>Simplex-based direct search methods are based on comparison of
* the cost function values at the vertices of a simplex (which is a
* set of n+1 points in dimension n) that is updated by the algorithms
* steps.</p>
* <p>The instances can be built either in single-start or in
* multi-start mode. Multi-start is a traditional way to try to avoid
* beeing trapped in a local minimum and miss the global minimum of a
* function. It can also be used to verify the convergence of an
* algorithm. In multi-start mode, the {@link #minimizes(CostFunction,
* int, ConvergenceChecker, double[], double[]) minimizes}
* method returns the best minimum found after all starts, and the
* {@link #getMinima getMinima} method can be used to retrieve all
* minima from all starts (including the one already provided by the
* {@link #minimizes(CostFunction, int, ConvergenceChecker, double[],
* double[]) minimizes} method).</p>
* <p>This class is the base class performing the boilerplate simplex
* initialization and handling. The simplex update by itself is
* performed by the derived classes according to the implemented
* algorithms.</p>
* @author Luc Maisonobe
* @version $Id: DirectSearchOptimizer.java 1705 2006-09-17 19:57:39Z luc $
* @see CostFunction
* @see NelderMead
* @see MultiDirectional
*/
public abstract class DirectSearchOptimizer {
/** Simple constructor.
*/
protected DirectSearchOptimizer() {
}
/** Minimizes a cost function.
* <p>The initial simplex is built from two vertices that are
* considered to represent two opposite vertices of a box parallel
* to the canonical axes of the space. The simplex is the subset of
* vertices encountered while going from vertexA to vertexB
* travelling along the box edges only. This can be seen as a scaled
* regular simplex using the projected separation between the given
* points as the scaling factor along each coordinate axis.</p>
* <p>The optimization is performed in single-start mode.</p>
* @param f cost function
* @param maxEvaluations maximal number of function calls for each
* start (note that the number will be checked <em>after</em>
* complete simplices have been evaluated, this means that in some
* cases this number will be exceeded by a few units, depending on
* the dimension of the problem)
* @param checker object to use to check for convergence
* @param vertexA first vertex
* @param vertexB last vertex
* @return the point/cost pairs giving the minimal cost
* @exception CostException if the cost function throws one during
* the search
* @exception NoConvergenceException if none of the starts did
* converge (it is not thrown if at least one start did converge)
*/
public PointCostPair minimizes(CostFunction f, int maxEvaluations,
ConvergenceChecker checker,
double[] vertexA, double[] vertexB)
throws CostException, NoConvergenceException {
// set up optimizer
buildSimplex(vertexA, vertexB);
setSingleStart();
// compute minimum
return minimizes(f, maxEvaluations, checker);
}
/** Minimizes a cost function.
* <p>The initial simplex is built from two vertices that are
* considered to represent two opposite vertices of a box parallel
* to the canonical axes of the space. The simplex is the subset of
* vertices encountered while going from vertexA to vertexB
* travelling along the box edges only. This can be seen as a scaled
* regular simplex using the projected separation between the given
* points as the scaling factor along each coordinate axis.</p>
* <p>The optimization is performed in multi-start mode.</p>
* @param f cost function
* @param maxEvaluations maximal number of function calls for each
* start (note that the number will be checked <em>after</em>
* complete simplices have been evaluated, this means that in some
* cases this number will be exceeded by a few units, depending on
* the dimension of the problem)
* @param checker object to use to check for convergence
* @param vertexA first vertex
* @param vertexB last vertex
* @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 seed seed for the random vector generator
* @return the point/cost pairs giving the minimal cost
* @exception CostException if the cost function throws one during
* the search
* @exception NoConvergenceException if none of the starts did
* converge (it is not thrown if at least one start did converge)
*/
public PointCostPair minimizes(CostFunction f, int maxEvaluations,
ConvergenceChecker checker,
double[] vertexA, double[] vertexB,
int starts, long seed)
throws CostException, NoConvergenceException {
// set up the simplex travelling around the box
buildSimplex(vertexA, vertexB);
// we consider the simplex could have been produced by a generator
// having its mean value at the center of the box, the standard
// deviation along each axe beeing the corresponding half size
double[] mean = new double[vertexA.length];
double[] standardDeviation = new double[vertexA.length];
for (int i = 0; i < vertexA.length; ++i) {
mean[i] = 0.5 * (vertexA[i] + vertexB[i]);
standardDeviation[i] = 0.5 * Math.abs(vertexA[i] - vertexB[i]);
}
RandomVectorGenerator rvg =
new UncorrelatedRandomVectorGenerator(mean, standardDeviation,
new UniformRandomGenerator(seed));
setMultiStart(starts, rvg);
// compute minimum
return minimizes(f, maxEvaluations, checker);
}
/** Minimizes a cost function.
* <p>The simplex is built from all its vertices.</p>
* <p>The optimization is performed in single-start mode.</p>
* @param f cost function
* @param maxEvaluations maximal number of function calls for each
* start (note that the number will be checked <em>after</em>
* complete simplices have been evaluated, this means that in some
* cases this number will be exceeded by a few units, depending on
* the dimension of the problem)
* @param checker object to use to check for convergence
* @param vertices array containing all vertices of the simplex
* @return the point/cost pairs giving the minimal cost
* @exception CostException if the cost function throws one during
* the search
* @exception NoConvergenceException if none of the starts did
* converge (it is not thrown if at least one start did converge)
*/
public PointCostPair minimizes(CostFunction f, int maxEvaluations,
ConvergenceChecker checker,
double[][] vertices)
throws CostException, NoConvergenceException {
// set up optimizer
buildSimplex(vertices);
setSingleStart();
// compute minimum
return minimizes(f, maxEvaluations, checker);
}
/** Minimizes a cost function.
* <p>The simplex is built from all its vertices.</p>
* <p>The optimization is performed in multi-start mode.</p>
* @param f cost function
* @param maxEvaluations maximal number of function calls for each
* start (note that the number will be checked <em>after</em>
* complete simplices have been evaluated, this means that in some
* cases this number will be exceeded by a few units, depending on
* the dimension of the problem)
* @param checker object to use to check for convergence
* @param vertices array containing all vertices of the simplex
* @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 seed seed for the random vector generator
* @return the point/cost pairs giving the minimal cost
* @exception NotPositiveDefiniteMatrixException if the vertices
* array is degenerated
* @exception CostException if the cost function throws one during
* the search
* @exception NoConvergenceException if none of the starts did
* converge (it is not thrown if at least one start did converge)
*/
public PointCostPair minimizes(CostFunction f, int maxEvaluations,
ConvergenceChecker checker,
double[][] vertices,
int starts, long seed)
throws NotPositiveDefiniteMatrixException,
CostException, NoConvergenceException {
// store the points into the simplex
buildSimplex(vertices);
// compute the statistical properties of the simplex points
VectorialSampleStatistics statistics = new VectorialSampleStatistics();
for (int i = 0; i < vertices.length; ++i) {
statistics.add(vertices[i]);
}
RandomVectorGenerator rvg =
new CorrelatedRandomVectorGenerator(statistics.getMean(),
statistics.getCovarianceMatrix(null),
new UniformRandomGenerator(seed));
setMultiStart(starts, rvg);
// compute minimum
return minimizes(f, maxEvaluations, checker);
}
/** Minimizes a cost function.
* <p>The simplex is built randomly.</p>
* <p>The optimization is performed in single-start mode.</p>
* @param f cost function
* @param maxEvaluations maximal number of function calls for each
* start (note that the number will be checked <em>after</em>
* complete simplices have been evaluated, this means that in some
* cases this number will be exceeded by a few units, depending on
* the dimension of the problem)
* @param checker object to use to check for convergence
* @param generator random vector generator
* @return the point/cost pairs giving the minimal cost
* @exception CostException if the cost function throws one during
* the search
* @exception NoConvergenceException if none of the starts did
* converge (it is not thrown if at least one start did converge)
*/
public PointCostPair minimizes(CostFunction f, int maxEvaluations,
ConvergenceChecker checker,
RandomVectorGenerator generator)
throws CostException, NoConvergenceException {
// set up optimizer
buildSimplex(generator);
setSingleStart();
// compute minimum
return minimizes(f, maxEvaluations, checker);
}
/** Minimizes a cost function.
* <p>The simplex is built randomly.</p>
* <p>The optimization is performed in multi-start mode.</p>
* @param f cost function
* @param maxEvaluations maximal number of function calls for each
* start (note that the number will be checked <em>after</em>
* complete simplices have been evaluated, this means that in some
* cases this number will be exceeded by a few units, depending on
* the dimension of the problem)
* @param checker object to use to check for convergence
* @param generator random vector generator
* @param starts number of starts to perform (including the
* first one), multi-start is disabled if value is less than or
* equal to 1
* @return the point/cost pairs giving the minimal cost
* @exception CostException if the cost function throws one during
* the search
* @exception NoConvergenceException if none of the starts did
* converge (it is not thrown if at least one start did converge)
*/
public PointCostPair minimizes(CostFunction f, int maxEvaluations,
ConvergenceChecker checker,
RandomVectorGenerator generator,
int starts)
throws CostException, NoConvergenceException {
// set up optimizer
buildSimplex(generator);
setMultiStart(starts, generator);
// compute minimum
return minimizes(f, maxEvaluations, checker);
}
/** Build a simplex from two extreme vertices.
* <p>The two vertices are considered to represent two opposite
* vertices of a box parallel to the canonical axes of the
* space. The simplex is the subset of vertices encountered while
* going from vertexA to vertexB travelling along the box edges
* only. This can be seen as a scaled regular simplex using the
* projected separation between the given points as the scaling
* factor along each coordinate axis.</p>
* @param vertexA first vertex
* @param vertexB last vertex
*/
private void buildSimplex(double[] vertexA, double[] vertexB) {
int n = vertexA.length;
simplex = new PointCostPair[n + 1];
// set up the simplex travelling around the box
for (int i = 0; i <= n; ++i) {
double[] vertex = new double[n];
if (i > 0) {
System.arraycopy(vertexB, 0, vertex, 0, i);
}
if (i < n) {
System.arraycopy(vertexA, i, vertex, i, n - i);
}
simplex[i] = new PointCostPair(vertex, Double.NaN);
}
}
/** Build a simplex from all its points.
* @param vertices array containing all vertices of the simplex
*/
private void buildSimplex(double[][] vertices) {
int n = vertices.length - 1;
simplex = new PointCostPair[n + 1];
for (int i = 0; i <= n; ++i) {
simplex[i] = new PointCostPair(vertices[i], Double.NaN);
}
}
/** Build a simplex randomly.
* @param generator random vector generator
*/
private void buildSimplex(RandomVectorGenerator generator) {
// use first vector size to compute the number of points
double[] vertex = generator.nextVector();
int n = vertex.length;
simplex = new PointCostPair[n + 1];
simplex[0] = new PointCostPair(vertex, Double.NaN);
// fill up the vertex
for (int i = 1; i <= n; ++i) {
simplex[i] = new PointCostPair(generator.nextVector(), Double.NaN);
}
}
/** Set up single-start mode.
*/
private void setSingleStart() {
starts = 1;
generator = null;
minima = null;
}
/** Set up multi-start mode.
* @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 void setMultiStart(int starts, RandomVectorGenerator generator) {
if (starts < 2) {
this.starts = 1;
this.generator = null;
minima = null;
} else {
this.starts = starts;
this.generator = generator;
minima = null;
}
}
/** Get all the minima found during the last call to {@link
* #minimizes(CostFunction, int, ConvergenceChecker, double[], double[])
* minimizes}.
* <p>The optimizer stores all the minima found during a set of
* restarts when multi-start mode is enabled. The {@link
* #minimizes(CostFunction, int, ConvergenceChecker, double[], double[])
* minimizes} 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 #minimizes(CostFunction,
* int, ConvergenceChecker, double[], double[]) minimizes} method.
* The array as one element for each start as specified in the constructor
* (it has one element only if optimizer has been set up for single-start).</p>
* <p>The array containing the minima is ordered with the results
* from the runs that did converge first, sorted from lowest to
* highest minimum cost, and null elements corresponding to the runs
* that did not converge (all elements will be null if the {@link
* #minimizes(CostFunction, int, ConvergenceChecker, double[], double[])
* minimizes} method throwed a {@link NoConvergenceException
* NoConvergenceException}).</p>
* @return array containing the minima, or null if {@link
* #minimizes(CostFunction, int, ConvergenceChecker, double[], double[])
* minimizes} has not been called
*/
public PointCostPair[] getMinima() {
return (PointCostPair[]) minima.clone();
}
/** Minimizes a cost function.
* @param f cost function
* @param maxEvaluations maximal number of function calls for each
* start (note that the number will be checked <em>after</em>
* complete simplices have been evaluated, this means that in some
* cases this number will be exceeded by a few units, depending on
* the dimension of the problem)
* @param checker object to use to check for convergence
* @return the point/cost pairs giving the minimal cost
* @exception CostException if the cost function throws one during
* the search
* @exception NoConvergenceException if none of the starts did
* converge (it is not thrown if at least one start did converge)
*/
private PointCostPair minimizes(CostFunction f, int maxEvaluations,
ConvergenceChecker checker)
throws CostException, NoConvergenceException {
this.f = f;
minima = new PointCostPair[starts];
// multi-start loop
for (int i = 0; i < starts; ++i) {
evaluations = 0;
evaluateSimplex();
for (boolean loop = true; loop;) {
if (checker.converged(simplex)) {
// we have found a minimum
minima[i] = simplex[0];
loop = false;
} else if (evaluations >= maxEvaluations) {
// this start did not converge, try a new one
minima[i] = null;
loop = false;
} else {
iterateSimplex();
}
}
if (i < (starts - 1)) {
// restart
buildSimplex(generator);
}
}
// sort the minima from lowest cost to highest cost, followed by
// null elements
Arrays.sort(minima, pointCostPairComparator);
// return the found point given the lowest cost
if (minima[0] == null) {
throw new NoConvergenceException("none of the {0} start points"
+ " lead to convergence",
new String[] {
Integer.toString(starts)
});
}
return minima[0];
}
/** Compute the next simplex of the algorithm.
*/
protected abstract void iterateSimplex()
throws CostException;
/** Evaluate the cost on one point.
* <p>A side effect of this method is to count the number of
* function evaluations</p>
* @param x point on which the cost function should be evaluated
* @return cost at the given point
* @exception CostException if no cost can be computed for the parameters
*/
protected double evaluateCost(double[] x)
throws CostException {
evaluations++;
return f.cost(x);
}
/** Evaluate all the non-evaluated points of the simplex.
* @exception CostException if no cost can be computed for the parameters
*/
protected void evaluateSimplex()
throws CostException {
// evaluate the cost at all non-evaluated simplex points
for (int i = 0; i < simplex.length; ++i) {
PointCostPair pair = simplex[i];
if (Double.isNaN(pair.cost)) {
simplex[i] = new PointCostPair(pair.point, evaluateCost(pair.point));
}
}
// sort the simplex from lowest cost to highest cost
Arrays.sort(simplex, pointCostPairComparator);
}
/** Replace the worst point of the simplex by a new point.
* @param pointCostPair point to insert
*/
protected void replaceWorstPoint(PointCostPair pointCostPair) {
int n = simplex.length - 1;
for (int i = 0; i < n; ++i) {
if (simplex[i].cost > pointCostPair.cost) {
PointCostPair tmp = simplex[i];
simplex[i] = pointCostPair;
pointCostPair = tmp;
}
}
simplex[n] = pointCostPair;
}
/** Comparator for {@link PointCostPair PointCostPair} objects. */
private static Comparator pointCostPairComparator = new Comparator() {
public int compare(Object o1, Object o2) {
if (o1 == null) {
return (o2 == null) ? 0 : +1;
} else if (o2 == null) {
return -1;
} else {
double cost1 = ((PointCostPair) o1).cost;
double cost2 = ((PointCostPair) o2).cost;
return (cost1 < cost2) ? -1 : ((o1 == o2) ? 0 : +1);
}
}
};
/** Simplex. */
protected PointCostPair[] simplex;
/** Cost function. */
private CostFunction f;
/** Number of evaluations already performed. */
private int evaluations;
/** Number of starts to go. */
private int starts;
/** Random generator for multi-start. */
private RandomVectorGenerator generator;
/** Found minima. */
private PointCostPair[] minima;
}

View File

@ -15,7 +15,14 @@
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.optimization;
package org.apache.commons.math.optimization;
import org.apache.commons.math.optimization.ConvergenceChecker;
import org.apache.commons.math.optimization.CostException;
import org.apache.commons.math.optimization.CostFunction;
import org.apache.commons.math.optimization.MultiDirectional;
import org.apache.commons.math.ConvergenceException;
import org.apache.commons.math.optimization.PointCostPair;
import junit.framework.*;
@ -27,7 +34,7 @@ public class MultiDirectionalTest
}
public void testRosenbrock()
throws CostException, NoConvergenceException {
throws CostException, ConvergenceException {
CostFunction rosenbrock =
new CostFunction() {
@ -51,7 +58,7 @@ public class MultiDirectionalTest
}
public void testPowell()
throws CostException, NoConvergenceException {
throws CostException, ConvergenceException {
CostFunction powell =
new CostFunction() {

View File

@ -15,7 +15,14 @@
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.optimization;
package org.apache.commons.math.optimization;
import org.apache.commons.math.optimization.ConvergenceChecker;
import org.apache.commons.math.optimization.CostException;
import org.apache.commons.math.optimization.CostFunction;
import org.apache.commons.math.optimization.NelderMead;
import org.apache.commons.math.ConvergenceException;
import org.apache.commons.math.optimization.PointCostPair;
import junit.framework.*;
@ -27,7 +34,7 @@ public class NelderMeadTest
}
public void testRosenbrock()
throws CostException, NoConvergenceException {
throws CostException, ConvergenceException {
CostFunction rosenbrock =
new CostFunction() {
@ -53,7 +60,7 @@ public class NelderMeadTest
}
public void testPowell()
throws CostException, NoConvergenceException {
throws CostException, ConvergenceException {
CostFunction powell =
new CostFunction() {