blob: 63329517aeef20708facc37dc94b51096ab479b2 [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.direct;
import org.apache.commons.math.MaxIterationsExceededException;
import org.apache.commons.math.analysis.UnivariateRealFunction;
import org.apache.commons.math.FunctionEvaluationException;
import org.apache.commons.math.optimization.GoalType;
import org.apache.commons.math.optimization.OptimizationException;
import org.apache.commons.math.optimization.RealPointValuePair;
import org.apache.commons.math.optimization.general.AbstractScalarDifferentiableOptimizer;
import org.apache.commons.math.optimization.univariate.AbstractUnivariateRealOptimizer;
import org.apache.commons.math.optimization.univariate.BracketFinder;
import org.apache.commons.math.optimization.univariate.BrentOptimizer;
/**
* Powell algorithm.
* This code is translated and adapted from the Python version of this
* algorithm (as implemented in module {@code optimize.py} v0.5 of
* <em>SciPy</em>).
*
* @version $Revision$ $Date$
* @since 2.2
*/
public class PowellOptimizer
extends AbstractScalarDifferentiableOptimizer {
/**
* Default relative tolerance for line search ({@value}).
*/
public static final double DEFAULT_LS_RELATIVE_TOLERANCE = 1e-7;
/**
* Default absolute tolerance for line search ({@value}).
*/
public static final double DEFAULT_LS_ABSOLUTE_TOLERANCE = 1e-11;
/**
* Line search.
*/
private final LineSearch line;
/**
* Constructor with default line search tolerances (see the
* {@link #PowellOptimizer(double,double) other constructor}).
*/
public PowellOptimizer() {
this(DEFAULT_LS_RELATIVE_TOLERANCE,
DEFAULT_LS_ABSOLUTE_TOLERANCE);
}
/**
* Constructor with default absolute line search tolerances (see
* the {@link #PowellOptimizer(double,double) other constructor}).
*
* @param lsRelativeTolerance Relative error tolerance for
* the line search algorithm ({@link BrentOptimizer}).
*/
public PowellOptimizer(double lsRelativeTolerance) {
this(lsRelativeTolerance,
DEFAULT_LS_ABSOLUTE_TOLERANCE);
}
/**
* @param lsRelativeTolerance Relative error tolerance for
* the line search algorithm ({@link BrentOptimizer}).
* @param lsAbsoluteTolerance Relative error tolerance for
* the line search algorithm ({@link BrentOptimizer}).
*/
public PowellOptimizer(double lsRelativeTolerance,
double lsAbsoluteTolerance) {
line = new LineSearch(lsRelativeTolerance,
lsAbsoluteTolerance);
}
/** {@inheritDoc} */
@Override
protected RealPointValuePair doOptimize()
throws FunctionEvaluationException,
OptimizationException {
final double[] guess = point.clone();
final int n = guess.length;
final double[][] direc = new double[n][n];
for (int i = 0; i < n; i++) {
direc[i][i] = 1;
}
double[] x = guess;
double fVal = computeObjectiveValue(x);
double[] x1 = x.clone();
while (true) {
incrementIterationsCounter();
double fX = fVal;
double fX2 = 0;
double delta = 0;
int bigInd = 0;
double alphaMin = 0;
for (int i = 0; i < n; i++) {
final double[] d = /* Arrays.*/ copyOf(direc[i], n); // Java 1.5 does not support Arrays.copyOf()
fX2 = fVal;
line.search(x, d);
fVal = line.getValueAtOptimum();
alphaMin = line.getOptimum();
final double[][] result = newPointAndDirection(x, d, alphaMin);
x = result[0];
if ((fX2 - fVal) > delta) {
delta = fX2 - fVal;
bigInd = i;
}
}
final RealPointValuePair previous = new RealPointValuePair(x1, fX);
final RealPointValuePair current = new RealPointValuePair(x, fVal);
if (getConvergenceChecker().converged(getIterations(), previous, current)) {
if (goal == GoalType.MINIMIZE) {
return (fVal < fX) ? current : previous;
} else {
return (fVal > fX) ? current : previous;
}
}
final double[] d = new double[n];
final double[] x2 = new double[n];
for (int i = 0; i < n; i++) {
d[i] = x[i] - x1[i];
x2[i] = 2 * x[i] - x1[i];
}
x1 = x.clone();
fX2 = computeObjectiveValue(x2);
if (fX > fX2) {
double t = 2 * (fX + fX2 - 2 * fVal);
double temp = fX - fVal - delta;
t *= temp * temp;
temp = fX - fX2;
t -= delta * temp * temp;
if (t < 0.0) {
line.search(x, d);
fVal = line.getValueAtOptimum();
alphaMin = line.getOptimum();
final double[][] result = newPointAndDirection(x, d, alphaMin);
x = result[0];
final int lastInd = n - 1;
direc[bigInd] = direc[lastInd];
direc[lastInd] = result[1];
}
}
}
}
/**
* Compute a new point (in the original space) and a new direction
* vector, resulting from the line search.
* The parameters {@code p} and {@code d} will be changed in-place.
*
* @param p Point used in the line search.
* @param d Direction used in the line search.
* @param optimum Optimum found by the line search.
* @return a 2-element array containing the new point (at index 0) and
* the new direction (at index 1).
*/
private double[][] newPointAndDirection(double[] p,
double[] d,
double optimum) {
final int n = p.length;
final double[][] result = new double[2][n];
final double[] nP = result[0];
final double[] nD = result[1];
for (int i = 0; i < n; i++) {
nD[i] = d[i] * optimum;
nP[i] = p[i] + nD[i];
}
return result;
}
/**
* Class for finding the minimum of the objective function along a given
* direction.
*/
private class LineSearch {
/**
* Optimizer.
*/
private final AbstractUnivariateRealOptimizer optim = new BrentOptimizer();
/**
* Automatic bracketing.
*/
private final BracketFinder bracket = new BracketFinder();
/**
* Value of the optimum.
*/
private double optimum = Double.NaN;
/**
* Value of the objective function at the optimum.
*/
private double valueAtOptimum = Double.NaN;
/**
* @param relativeTolerance Relative tolerance.
* @param absoluteTolerance Absolute tolerance.
*/
public LineSearch(double relativeTolerance,
double absoluteTolerance) {
optim.setRelativeAccuracy(relativeTolerance);
optim.setAbsoluteAccuracy(absoluteTolerance);
}
/**
* Find the minimum of the function {@code f(p + alpha * d)}.
*
* @param p Starting point.
* @param d Search direction.
* @throws FunctionEvaluationException if function cannot be evaluated at some test point
* @throws OptimizationException if algorithm fails to converge
*/
public void search(final double[] p, final double[] d)
throws OptimizationException, FunctionEvaluationException {
// Reset.
optimum = Double.NaN;
valueAtOptimum = Double.NaN;
try {
final int n = p.length;
final UnivariateRealFunction f = new UnivariateRealFunction() {
public double value(double alpha)
throws FunctionEvaluationException {
final double[] x = new double[n];
for (int i = 0; i < n; i++) {
x[i] = p[i] + alpha * d[i];
}
final double obj;
obj = computeObjectiveValue(x);
return obj;
}
};
bracket.search(f, goal, 0, 1);
optimum = optim.optimize(f, goal,
bracket.getLo(),
bracket.getHi(),
bracket.getMid());
valueAtOptimum = optim.getFunctionValue();
} catch (MaxIterationsExceededException e) {
throw new OptimizationException(e);
}
}
/**
* @return the optimum.
*/
public double getOptimum() {
return optimum;
}
/**
* @return the value of the function at the optimum.
*/
public double getValueAtOptimum() {
return valueAtOptimum;
}
}
/**
* Java 1.5 does not support Arrays.copyOf()
*
* @param source the array to be copied
* @param newLen the length of the copy to be returned
* @return the copied array, truncated or padded as necessary.
*/
private double[] copyOf(double[] source, int newLen) {
double[] output = new double[newLen];
System.arraycopy(source, 0, output, 0, Math.min(source.length, newLen));
return output;
}
}