/*
* 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.math3.optimization.direct;
import org.apache.commons.math3.util.FastMath;
import org.apache.commons.math3.util.MathArrays;
import org.apache.commons.math3.analysis.UnivariateFunction;
import org.apache.commons.math3.analysis.MultivariateFunction;
import org.apache.commons.math3.exception.NumberIsTooSmallException;
import org.apache.commons.math3.exception.NotStrictlyPositiveException;
import org.apache.commons.math3.optimization.GoalType;
import org.apache.commons.math3.optimization.PointValuePair;
import org.apache.commons.math3.optimization.ConvergenceChecker;
import org.apache.commons.math3.optimization.MultivariateOptimizer;
import org.apache.commons.math3.optimization.univariate.BracketFinder;
import org.apache.commons.math3.optimization.univariate.BrentOptimizer;
import org.apache.commons.math3.optimization.univariate.UnivariatePointValuePair;
import org.apache.commons.math3.optimization.univariate.SimpleUnivariateValueChecker;
Powell algorithm. This code is translated and adapted from the Python version of this algorithm (as implemented in module optimize.py
v0.5 of SciPy).
The default stopping criterion is based on the differences of the
function value between two successive iterations. It is however possible
to define a custom convergence checker that might terminate the algorithm
earlier.
The internal line search optimizer is a BrentOptimizer
with a convergence checker set to SimpleUnivariateValueChecker
. Deprecated: As of 3.1 (to be removed in 4.0). Since: 2.2
/**
* 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>).
* <br/>
* The default stopping criterion is based on the differences of the
* function value between two successive iterations. It is however possible
* to define a custom convergence checker that might terminate the algorithm
* earlier.
* <br/>
* The internal line search optimizer is a {@link BrentOptimizer} with a
* convergence checker set to {@link SimpleUnivariateValueChecker}.
*
* @deprecated As of 3.1 (to be removed in 4.0).
* @since 2.2
*/
@Deprecated
public class PowellOptimizer
extends BaseAbstractMultivariateOptimizer<MultivariateFunction>
implements MultivariateOptimizer {
Minimum relative tolerance.
/**
* Minimum relative tolerance.
*/
private static final double MIN_RELATIVE_TOLERANCE = 2 * FastMath.ulp(1d);
Relative threshold.
/**
* Relative threshold.
*/
private final double relativeThreshold;
Absolute threshold.
/**
* Absolute threshold.
*/
private final double absoluteThreshold;
Line search.
/**
* Line search.
*/
private final LineSearch line;
This constructor allows to specify a user-defined convergence checker,
in addition to the parameters that control the default convergence
checking procedure.
The internal line search tolerances are set to the square-root of their
corresponding value in the multivariate optimizer.
Params: - rel – Relative threshold.
- abs – Absolute threshold.
- checker – Convergence checker.
Throws: - NotStrictlyPositiveException – if
abs <= 0
. - NumberIsTooSmallException – if
rel < 2 * Math.ulp(1d)
.
/**
* This constructor allows to specify a user-defined convergence checker,
* in addition to the parameters that control the default convergence
* checking procedure.
* <br/>
* The internal line search tolerances are set to the square-root of their
* corresponding value in the multivariate optimizer.
*
* @param rel Relative threshold.
* @param abs Absolute threshold.
* @param checker Convergence checker.
* @throws NotStrictlyPositiveException if {@code abs <= 0}.
* @throws NumberIsTooSmallException if {@code rel < 2 * Math.ulp(1d)}.
*/
public PowellOptimizer(double rel,
double abs,
ConvergenceChecker<PointValuePair> checker) {
this(rel, abs, FastMath.sqrt(rel), FastMath.sqrt(abs), checker);
}
This constructor allows to specify a user-defined convergence checker,
in addition to the parameters that control the default convergence
checking procedure and the line search tolerances.
Params: - rel – Relative threshold for this optimizer.
- abs – Absolute threshold for this optimizer.
- lineRel – Relative threshold for the internal line search optimizer.
- lineAbs – Absolute threshold for the internal line search optimizer.
- checker – Convergence checker.
Throws: - NotStrictlyPositiveException – if
abs <= 0
. - NumberIsTooSmallException – if
rel < 2 * Math.ulp(1d)
.
/**
* This constructor allows to specify a user-defined convergence checker,
* in addition to the parameters that control the default convergence
* checking procedure and the line search tolerances.
*
* @param rel Relative threshold for this optimizer.
* @param abs Absolute threshold for this optimizer.
* @param lineRel Relative threshold for the internal line search optimizer.
* @param lineAbs Absolute threshold for the internal line search optimizer.
* @param checker Convergence checker.
* @throws NotStrictlyPositiveException if {@code abs <= 0}.
* @throws NumberIsTooSmallException if {@code rel < 2 * Math.ulp(1d)}.
*/
public PowellOptimizer(double rel,
double abs,
double lineRel,
double lineAbs,
ConvergenceChecker<PointValuePair> checker) {
super(checker);
if (rel < MIN_RELATIVE_TOLERANCE) {
throw new NumberIsTooSmallException(rel, MIN_RELATIVE_TOLERANCE, true);
}
if (abs <= 0) {
throw new NotStrictlyPositiveException(abs);
}
relativeThreshold = rel;
absoluteThreshold = abs;
// Create the line search optimizer.
line = new LineSearch(lineRel,
lineAbs);
}
The parameters control the default convergence checking procedure.
The internal line search tolerances are set to the square-root of their
corresponding value in the multivariate optimizer.
Params: - rel – Relative threshold.
- abs – Absolute threshold.
Throws: - NotStrictlyPositiveException – if
abs <= 0
. - NumberIsTooSmallException – if
rel < 2 * Math.ulp(1d)
.
/**
* The parameters control the default convergence checking procedure.
* <br/>
* The internal line search tolerances are set to the square-root of their
* corresponding value in the multivariate optimizer.
*
* @param rel Relative threshold.
* @param abs Absolute threshold.
* @throws NotStrictlyPositiveException if {@code abs <= 0}.
* @throws NumberIsTooSmallException if {@code rel < 2 * Math.ulp(1d)}.
*/
public PowellOptimizer(double rel,
double abs) {
this(rel, abs, null);
}
Builds an instance with the default convergence checking procedure.
Params: - rel – Relative threshold.
- abs – Absolute threshold.
- lineRel – Relative threshold for the internal line search optimizer.
- lineAbs – Absolute threshold for the internal line search optimizer.
Throws: - NotStrictlyPositiveException – if
abs <= 0
. - NumberIsTooSmallException – if
rel < 2 * Math.ulp(1d)
.
Since: 3.1
/**
* Builds an instance with the default convergence checking procedure.
*
* @param rel Relative threshold.
* @param abs Absolute threshold.
* @param lineRel Relative threshold for the internal line search optimizer.
* @param lineAbs Absolute threshold for the internal line search optimizer.
* @throws NotStrictlyPositiveException if {@code abs <= 0}.
* @throws NumberIsTooSmallException if {@code rel < 2 * Math.ulp(1d)}.
* @since 3.1
*/
public PowellOptimizer(double rel,
double abs,
double lineRel,
double lineAbs) {
this(rel, abs, lineRel, lineAbs, null);
}
{@inheritDoc} /** {@inheritDoc} */
@Override
protected PointValuePair doOptimize() {
final GoalType goal = getGoalType();
final double[] guess = getStartPoint();
final int n = guess.length;
final double[][] direc = new double[n][n];
for (int i = 0; i < n; i++) {
direc[i][i] = 1;
}
final ConvergenceChecker<PointValuePair> checker
= getConvergenceChecker();
double[] x = guess;
double fVal = computeObjectiveValue(x);
double[] x1 = x.clone();
int iter = 0;
while (true) {
++iter;
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 = MathArrays.copyOf(direc[i]);
fX2 = fVal;
final UnivariatePointValuePair optimum = line.search(x, d);
fVal = optimum.getValue();
alphaMin = optimum.getPoint();
final double[][] result = newPointAndDirection(x, d, alphaMin);
x = result[0];
if ((fX2 - fVal) > delta) {
delta = fX2 - fVal;
bigInd = i;
}
}
// Default convergence check.
boolean stop = 2 * (fX - fVal) <=
(relativeThreshold * (FastMath.abs(fX) + FastMath.abs(fVal)) +
absoluteThreshold);
final PointValuePair previous = new PointValuePair(x1, fX);
final PointValuePair current = new PointValuePair(x, fVal);
if (!stop && checker != null) {
stop = checker.converged(iter, previous, current);
}
if (stop) {
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) {
final UnivariatePointValuePair optimum = line.search(x, d);
fVal = optimum.getValue();
alphaMin = optimum.getPoint();
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.
Params: - p – Point used in the line search.
- d – Direction used in the line search.
- optimum – Optimum found by the line search.
Returns: a 2-element array containing the new point (at index 0) and
the new direction (at index 1).
/**
* Compute a new point (in the original space) and a new direction
* vector, resulting from the line search.
*
* @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[] nP = new double[n];
final double[] nD = new double[n];
for (int i = 0; i < n; i++) {
nD[i] = d[i] * optimum;
nP[i] = p[i] + nD[i];
}
final double[][] result = new double[2][];
result[0] = nP;
result[1] = nD;
return result;
}
Class for finding the minimum of the objective function along a given
direction.
/**
* Class for finding the minimum of the objective function along a given
* direction.
*/
private class LineSearch extends BrentOptimizer {
Value that will pass the precondition check for BrentOptimizer
but will not pass the convergence check, so that the custom checker will always decide when to stop the line search. /**
* Value that will pass the precondition check for {@link BrentOptimizer}
* but will not pass the convergence check, so that the custom checker
* will always decide when to stop the line search.
*/
private static final double REL_TOL_UNUSED = 1e-15;
Value that will pass the precondition check for BrentOptimizer
but will not pass the convergence check, so that the custom checker will always decide when to stop the line search. /**
* Value that will pass the precondition check for {@link BrentOptimizer}
* but will not pass the convergence check, so that the custom checker
* will always decide when to stop the line search.
*/
private static final double ABS_TOL_UNUSED = Double.MIN_VALUE;
Automatic bracketing.
/**
* Automatic bracketing.
*/
private final BracketFinder bracket = new BracketFinder();
The "BrentOptimizer" default stopping criterion uses the tolerances
to check the domain (point) values, not the function values.
We thus create a custom checker to use function values.
Params: - rel – Relative threshold.
- abs – Absolute threshold.
/**
* The "BrentOptimizer" default stopping criterion uses the tolerances
* to check the domain (point) values, not the function values.
* We thus create a custom checker to use function values.
*
* @param rel Relative threshold.
* @param abs Absolute threshold.
*/
LineSearch(double rel,
double abs) {
super(REL_TOL_UNUSED,
ABS_TOL_UNUSED,
new SimpleUnivariateValueChecker(rel, abs));
}
Find the minimum of the function f(p + alpha * d)
. Params: - p – Starting point.
- d – Search direction.
Throws: - TooManyEvaluationsException –
if the number of evaluations is exceeded.
Returns: the optimum.
/**
* Find the minimum of the function {@code f(p + alpha * d)}.
*
* @param p Starting point.
* @param d Search direction.
* @return the optimum.
* @throws org.apache.commons.math3.exception.TooManyEvaluationsException
* if the number of evaluations is exceeded.
*/
public UnivariatePointValuePair search(final double[] p, final double[] d) {
final int n = p.length;
final UnivariateFunction f = new UnivariateFunction() {
{@inheritDoc} /** {@inheritDoc} */
public double value(double alpha) {
final double[] x = new double[n];
for (int i = 0; i < n; i++) {
x[i] = p[i] + alpha * d[i];
}
final double obj = PowellOptimizer.this.computeObjectiveValue(x);
return obj;
}
};
final GoalType goal = PowellOptimizer.this.getGoalType();
bracket.search(f, goal, 0, 1);
// Passing "MAX_VALUE" as a dummy value because it is the enclosing
// class that counts the number of evaluations (and will eventually
// generate the exception).
return optimize(Integer.MAX_VALUE, f, goal,
bracket.getLo(), bracket.getHi(), bracket.getMid());
}
}
}