mirror of
https://github.com/apache/commons-math.git
synced 2025-02-07 18:49:40 +00:00
MATH-395: Another bug uncovered; all things being equal, the code now behaves
like the Puthon implementation. MATH-397: Modified "BrentOptimizer" following the changes in "AbstractUnivariateRealOptimizer". git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@980032 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
7572385a7e
commit
962315ba93
@ -139,14 +139,14 @@ public abstract class ConvergingAlgorithmImpl implements ConvergingAlgorithm {
|
||||
/**
|
||||
* Increment the iterations counter by 1.
|
||||
*
|
||||
* @throws OptimizationException if the maximal number
|
||||
* @throws MaxIterationsExceededException if the maximal number
|
||||
* of iterations is exceeded.
|
||||
* @since 2.2
|
||||
*/
|
||||
protected void incrementIterationsCounter()
|
||||
throws ConvergenceException {
|
||||
throws MaxIterationsExceededException {
|
||||
if (++iterationCount > maximalIterationCount) {
|
||||
throw new ConvergenceException(new MaxIterationsExceededException(maximalIterationCount));
|
||||
throw new MaxIterationsExceededException(maximalIterationCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -260,5 +260,6 @@ public abstract class AbstractUnivariateRealOptimizer
|
||||
*
|
||||
* @return the optimum.
|
||||
*/
|
||||
protected abstract double doOptimize();
|
||||
protected abstract double doOptimize()
|
||||
throws MaxIterationsExceededException, FunctionEvaluationException;
|
||||
}
|
||||
|
@ -41,39 +41,37 @@ public class BrentOptimizer extends AbstractUnivariateRealOptimizer {
|
||||
* Construct a solver.
|
||||
*/
|
||||
public BrentOptimizer() {
|
||||
super(100, 1E-10);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public double optimize(final UnivariateRealFunction f, final GoalType goalType,
|
||||
final double min, final double max, final double startValue)
|
||||
throws MaxIterationsExceededException, FunctionEvaluationException {
|
||||
clearResult();
|
||||
return localMin(f, goalType, min, startValue, max,
|
||||
getRelativeAccuracy(), getAbsoluteAccuracy());
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public double optimize(final UnivariateRealFunction f, final GoalType goalType,
|
||||
final double min, final double max)
|
||||
throws MaxIterationsExceededException, FunctionEvaluationException {
|
||||
return optimize(f, goalType, min, max, min + GOLDEN_SECTION * (max - min));
|
||||
setMaxEvaluations(1000);
|
||||
setMaximalIterationCount(100);
|
||||
setAbsoluteAccuracy(1e-11);
|
||||
setRelativeAccuracy(1e-9);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the minimum of the function {@code f} within the interval {@code (a, b)}.
|
||||
* Perform the optimization.
|
||||
*
|
||||
* If the function {@code f} is defined on the interval {@code (a, b)}, then
|
||||
* this method finds an approximation {@code x} to the point at which {@code f}
|
||||
* attains its minimum.<br/>
|
||||
* {@code t} and {@code eps} define a tolerance {@code tol = eps |x| + t} and
|
||||
* {@code f} is never evaluated at two points closer together than {@code tol}.
|
||||
* {@code eps} should be no smaller than <em>2 macheps</em> and preferable not
|
||||
* much less than <em>sqrt(macheps)</em>, where <em>macheps</em> is the relative
|
||||
* machine precision. {@code t} should be positive.
|
||||
* @param f the function to solve.
|
||||
* @param goalType type of optimization goal: either {@link GoalType#MAXIMIZE}
|
||||
* or {@link GoalType#MINIMIZE}.
|
||||
* @return the optimum.
|
||||
*/
|
||||
protected double doOptimize()
|
||||
throws MaxIterationsExceededException, FunctionEvaluationException {
|
||||
return localMin(getGoalType() == GoalType.MINIMIZE,
|
||||
getMin(), getStartValue(), getMax(),
|
||||
getRelativeAccuracy(), getAbsoluteAccuracy());
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the minimum of the function within the interval {@code (lo, hi)}.
|
||||
*
|
||||
* If the function is defined on the interval {@code (lo, hi)}, then
|
||||
* this method finds an approximation {@code x} to the point at which
|
||||
* the function attains its minimum.<br/>
|
||||
* {@code t} and {@code eps} define a tolerance {@code tol = eps |x| + t}
|
||||
* and the function is never evaluated at two points closer together than
|
||||
* {@code tol}. {@code eps} should be no smaller than <em>2 macheps</em> and
|
||||
* preferable not much less than <em>sqrt(macheps)</em>, where
|
||||
* <em>macheps</em> is the relative machine precision. {@code t} should be
|
||||
* positive.
|
||||
* @param isMinim {@code true} when minimizing the function.
|
||||
* @param lo Lower bound of the interval.
|
||||
* @param mid Point inside the interval {@code [lo, hi]}.
|
||||
* @param hi Higher bound of the interval.
|
||||
@ -85,8 +83,7 @@ public class BrentOptimizer extends AbstractUnivariateRealOptimizer {
|
||||
* @throws FunctionEvaluationException if an error occurs evaluating
|
||||
* the function.
|
||||
*/
|
||||
private double localMin(UnivariateRealFunction f,
|
||||
GoalType goalType,
|
||||
private double localMin(boolean isMinim,
|
||||
double lo, double mid, double hi,
|
||||
double eps, double t)
|
||||
throws MaxIterationsExceededException, FunctionEvaluationException {
|
||||
@ -108,16 +105,16 @@ public class BrentOptimizer extends AbstractUnivariateRealOptimizer {
|
||||
double x = mid;
|
||||
double v = x;
|
||||
double w = x;
|
||||
double d = 0;
|
||||
double e = 0;
|
||||
double fx = computeObjectiveValue(f, x);
|
||||
if (goalType == GoalType.MAXIMIZE) {
|
||||
double fx = computeObjectiveValue(x);
|
||||
if (!isMinim) {
|
||||
fx = -fx;
|
||||
}
|
||||
double fv = fx;
|
||||
double fw = fx;
|
||||
|
||||
int count = 0;
|
||||
while (count < maximalIterationCount) {
|
||||
while (true) {
|
||||
double m = 0.5 * (a + b);
|
||||
final double tol1 = eps * Math.abs(x) + t;
|
||||
final double tol2 = 2 * tol1;
|
||||
@ -127,7 +124,6 @@ public class BrentOptimizer extends AbstractUnivariateRealOptimizer {
|
||||
double p = 0;
|
||||
double q = 0;
|
||||
double r = 0;
|
||||
double d = 0;
|
||||
double u = 0;
|
||||
|
||||
if (Math.abs(e) > tol1) { // Fit parabola.
|
||||
@ -191,8 +187,8 @@ public class BrentOptimizer extends AbstractUnivariateRealOptimizer {
|
||||
u = x + d;
|
||||
}
|
||||
|
||||
double fu = computeObjectiveValue(f, u);
|
||||
if (goalType == GoalType.MAXIMIZE) {
|
||||
double fu = computeObjectiveValue(u);
|
||||
if (!isMinim) {
|
||||
fu = -fu;
|
||||
}
|
||||
|
||||
@ -229,16 +225,10 @@ public class BrentOptimizer extends AbstractUnivariateRealOptimizer {
|
||||
}
|
||||
}
|
||||
} else { // termination
|
||||
setResult(x, (goalType == GoalType.MAXIMIZE) ? -fx : fx, count);
|
||||
setFunctionValue(isMinim ? fx : -fx);
|
||||
return x;
|
||||
}
|
||||
++count;
|
||||
incrementIterationsCounter();
|
||||
}
|
||||
throw new MaxIterationsExceededException(maximalIterationCount);
|
||||
}
|
||||
|
||||
/** Temporary workaround. */
|
||||
protected double doOptimize() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
@ -48,8 +48,8 @@ public class MultiStartUnivariateRealOptimizerTest {
|
||||
assertEquals(-1.0, f.value(optima[i]), 1.0e-10);
|
||||
assertEquals(f.value(optima[i]), optimaValues[i], 1.0e-10);
|
||||
}
|
||||
assertTrue(minimizer.getEvaluations() > 1500);
|
||||
assertTrue(minimizer.getEvaluations() < 1700);
|
||||
assertTrue(minimizer.getEvaluations() > 150);
|
||||
assertTrue(minimizer.getEvaluations() < 250);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -84,8 +84,8 @@ public class MultiStartUnivariateRealOptimizerTest {
|
||||
}
|
||||
|
||||
double result = minimizer.optimize(f, GoalType.MINIMIZE, -0.3, -0.2);
|
||||
assertEquals(-0.27195612525275803, result, 1.0e-13);
|
||||
assertEquals(-0.27195612525275803, minimizer.getResult(), 1.0e-13);
|
||||
assertEquals(-0.2719561270319131, result, 1.0e-13);
|
||||
assertEquals(-0.2719561270319131, minimizer.getResult(), 1.0e-13);
|
||||
assertEquals(-0.04433426954946637, minimizer.getFunctionValue(), 1.0e-13);
|
||||
|
||||
double[] optima = minimizer.getOptima();
|
||||
@ -93,10 +93,9 @@ public class MultiStartUnivariateRealOptimizerTest {
|
||||
for (int i = 0; i < optima.length; ++i) {
|
||||
assertEquals(f.value(optima[i]), optimaValues[i], 1.0e-10);
|
||||
}
|
||||
|
||||
assertTrue(minimizer.getEvaluations() >= 300);
|
||||
assertTrue(minimizer.getEvaluations() <= 420);
|
||||
assertTrue(minimizer.getIterationCount() >= 100);
|
||||
assertTrue(minimizer.getIterationCount() <= 140);
|
||||
assertTrue(minimizer.getEvaluations() >= 120);
|
||||
assertTrue(minimizer.getEvaluations() <= 170);
|
||||
assertTrue(minimizer.getIterationCount() >= 120);
|
||||
assertTrue(minimizer.getIterationCount() <= 170);
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ import org.apache.commons.math.analysis.SinFunction;
|
||||
import org.apache.commons.math.analysis.UnivariateRealFunction;
|
||||
import org.apache.commons.math.optimization.GoalType;
|
||||
import org.apache.commons.math.optimization.UnivariateRealOptimizer;
|
||||
import org.apache.commons.math.stat.descriptive.DescriptiveStatistics;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
@ -50,13 +51,13 @@ public final class BrentOptimizerTest {
|
||||
} catch (Exception e) {
|
||||
fail("wrong exception caught");
|
||||
}
|
||||
assertEquals(3 * Math.PI / 2, minimizer.optimize(f, GoalType.MINIMIZE, 4, 5), 70 * minimizer.getAbsoluteAccuracy());
|
||||
assertEquals(3 * Math.PI / 2, minimizer.optimize(f, GoalType.MINIMIZE, 4, 5), 10 * minimizer.getRelativeAccuracy());
|
||||
assertTrue(minimizer.getIterationCount() <= 50);
|
||||
assertEquals(3 * Math.PI / 2, minimizer.optimize(f, GoalType.MINIMIZE, 1, 5), 70 * minimizer.getAbsoluteAccuracy());
|
||||
assertEquals(3 * Math.PI / 2, minimizer.optimize(f, GoalType.MINIMIZE, 1, 5), 10 * minimizer.getRelativeAccuracy());
|
||||
assertTrue(minimizer.getIterationCount() <= 50);
|
||||
assertTrue(minimizer.getEvaluations() <= 100);
|
||||
assertTrue(minimizer.getEvaluations() >= 30);
|
||||
minimizer.setMaxEvaluations(50);
|
||||
assertTrue(minimizer.getEvaluations() >= 15);
|
||||
minimizer.setMaxEvaluations(10);
|
||||
try {
|
||||
minimizer.optimize(f, GoalType.MINIMIZE, 4, 5);
|
||||
fail("an exception should have been thrown");
|
||||
@ -82,35 +83,35 @@ public final class BrentOptimizerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQuinticMinPythonComparison() throws MathException {
|
||||
public void testQuinticMinStatistics() throws MathException {
|
||||
// The function has local minima at -0.27195613 and 0.82221643.
|
||||
UnivariateRealFunction f = new QuinticFunction();
|
||||
UnivariateRealOptimizer minimizer = new BrentOptimizer();
|
||||
minimizer.setRelativeAccuracy(1e-12);
|
||||
minimizer.setRelativeAccuracy(1e-10);
|
||||
minimizer.setAbsoluteAccuracy(1e-11);
|
||||
|
||||
double result;
|
||||
int nIter, nEval;
|
||||
final DescriptiveStatistics[] stat = new DescriptiveStatistics[3];
|
||||
for (int i = 0; i < stat.length; i++) {
|
||||
stat[i] = new DescriptiveStatistics();
|
||||
}
|
||||
|
||||
result = minimizer.optimize(f, GoalType.MINIMIZE, -0.3, -0.2, -0.25);
|
||||
nIter = minimizer.getIterationCount();
|
||||
nEval = minimizer.getEvaluations();
|
||||
// XXX Python: -0.27195612805911351 (instead of -0.2719561279558559).
|
||||
assertEquals(-0.2719561279558559, result, 1e-12);
|
||||
// XXX Python: 15 (instead of 18).
|
||||
assertEquals(18, nEval);
|
||||
// XXX Python: 11 (instead of 17).
|
||||
assertEquals(17, nIter);
|
||||
final double min = -0.75;
|
||||
final double max = 0.25;
|
||||
final int nSamples = 200;
|
||||
final double delta = (max - min) / nSamples;
|
||||
for (int i = 0; i < nSamples; i++) {
|
||||
final double start = min + i * delta;
|
||||
stat[0].addValue(minimizer.optimize(f, GoalType.MINIMIZE, min, max, start));
|
||||
stat[1].addValue(minimizer.getIterationCount());
|
||||
stat[2].addValue(minimizer.getEvaluations());
|
||||
}
|
||||
|
||||
result = minimizer.optimize(f, GoalType.MINIMIZE, 0.7, 0.9, 0.8);
|
||||
nIter = minimizer.getIterationCount();
|
||||
nEval = minimizer.getEvaluations();
|
||||
// XXX Python: 0.82221643488363705 (instead of 0.8222164326561908).
|
||||
assertEquals(0.8222164326561908, result, 1e-12);
|
||||
// XXX Python: 25 (instead of 43).
|
||||
assertEquals(43, nEval);
|
||||
// XXX Python: 21 (instead of 24).
|
||||
assertEquals(24, nIter);
|
||||
final double meanOptValue = stat[0].getMean();
|
||||
final double medianIter = stat[1].getPercentile(50);
|
||||
final double medianEval = stat[2].getPercentile(50);
|
||||
assertTrue(meanOptValue > -0.27195612812 && meanOptValue < -0.27195612811);
|
||||
assertEquals(medianIter, 17, Math.ulp(1d));
|
||||
assertEquals(medianEval, 18, Math.ulp(1d));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -120,7 +121,7 @@ public final class BrentOptimizerTest {
|
||||
UnivariateRealFunction f = new QuinticFunction();
|
||||
UnivariateRealOptimizer minimizer = new BrentOptimizer();
|
||||
assertEquals(0.27195613, minimizer.optimize(f, GoalType.MAXIMIZE, 0.2, 0.3), 1.0e-8);
|
||||
minimizer.setMaximalIterationCount(20);
|
||||
minimizer.setMaximalIterationCount(5);
|
||||
try {
|
||||
minimizer.optimize(f, GoalType.MAXIMIZE, 0.2, 0.3);
|
||||
fail("an exception should have been thrown");
|
||||
@ -136,11 +137,13 @@ public final class BrentOptimizerTest {
|
||||
UnivariateRealFunction f = new SinFunction();
|
||||
UnivariateRealOptimizer solver = new BrentOptimizer();
|
||||
|
||||
solver.setRelativeAccuracy(1e-8);
|
||||
|
||||
// endpoint is minimum
|
||||
double result = solver.optimize(f, GoalType.MINIMIZE, 3 * Math.PI / 2, 5);
|
||||
assertEquals(3 * Math.PI / 2, result, 70 * solver.getAbsoluteAccuracy());
|
||||
assertEquals(3 * Math.PI / 2, result, 10 * solver.getRelativeAccuracy());
|
||||
|
||||
result = solver.optimize(f, GoalType.MINIMIZE, 4, 3 * Math.PI / 2);
|
||||
assertEquals(3 * Math.PI / 2, result, 80 * solver.getAbsoluteAccuracy());
|
||||
assertEquals(3 * Math.PI / 2, result, 10 * solver.getRelativeAccuracy());
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user