Author: erans Date: Wed Jul 28 12:03:41 2010 New Revision: 980032 URL: http://svn.apache.org/viewvc?rev=980032&view=rev Log: 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".
Modified: commons/proper/math/trunk/src/main/java/org/apache/commons/math/ConvergingAlgorithmImpl.java commons/proper/math/trunk/src/main/java/org/apache/commons/math/optimization/univariate/AbstractUnivariateRealOptimizer.java commons/proper/math/trunk/src/main/java/org/apache/commons/math/optimization/univariate/BrentOptimizer.java commons/proper/math/trunk/src/test/java/org/apache/commons/math/optimization/MultiStartUnivariateRealOptimizerTest.java commons/proper/math/trunk/src/test/java/org/apache/commons/math/optimization/univariate/BrentOptimizerTest.java Modified: commons/proper/math/trunk/src/main/java/org/apache/commons/math/ConvergingAlgorithmImpl.java URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math/ConvergingAlgorithmImpl.java?rev=980032&r1=980031&r2=980032&view=diff ============================================================================== --- commons/proper/math/trunk/src/main/java/org/apache/commons/math/ConvergingAlgorithmImpl.java (original) +++ commons/proper/math/trunk/src/main/java/org/apache/commons/math/ConvergingAlgorithmImpl.java Wed Jul 28 12:03:41 2010 @@ -139,14 +139,14 @@ public abstract class ConvergingAlgorith /** * 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); } } } Modified: commons/proper/math/trunk/src/main/java/org/apache/commons/math/optimization/univariate/AbstractUnivariateRealOptimizer.java URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math/optimization/univariate/AbstractUnivariateRealOptimizer.java?rev=980032&r1=980031&r2=980032&view=diff ============================================================================== --- commons/proper/math/trunk/src/main/java/org/apache/commons/math/optimization/univariate/AbstractUnivariateRealOptimizer.java (original) +++ commons/proper/math/trunk/src/main/java/org/apache/commons/math/optimization/univariate/AbstractUnivariateRealOptimizer.java Wed Jul 28 12:03:41 2010 @@ -260,5 +260,6 @@ public abstract class AbstractUnivariate * * @return the optimum. */ - protected abstract double doOptimize(); + protected abstract double doOptimize() + throws MaxIterationsExceededException, FunctionEvaluationException; } Modified: commons/proper/math/trunk/src/main/java/org/apache/commons/math/optimization/univariate/BrentOptimizer.java URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math/optimization/univariate/BrentOptimizer.java?rev=980032&r1=980031&r2=980032&view=diff ============================================================================== --- commons/proper/math/trunk/src/main/java/org/apache/commons/math/optimization/univariate/BrentOptimizer.java (original) +++ commons/proper/math/trunk/src/main/java/org/apache/commons/math/optimization/univariate/BrentOptimizer.java Wed Jul 28 12:03:41 2010 @@ -41,39 +41,37 @@ public class BrentOptimizer extends Abst * Construct a solver. */ public BrentOptimizer() { - super(100, 1E-10); + setMaxEvaluations(1000); + setMaximalIterationCount(100); + setAbsoluteAccuracy(1e-11); + setRelativeAccuracy(1e-9); } - /** {...@inheritdoc} */ - public double optimize(final UnivariateRealFunction f, final GoalType goalType, - final double min, final double max, final double startValue) + /** + * Perform the optimization. + * + * @return the optimum. + */ + protected double doOptimize() throws MaxIterationsExceededException, FunctionEvaluationException { - clearResult(); - return localMin(f, goalType, min, startValue, max, + return localMin(getGoalType() == GoalType.MINIMIZE, + getMin(), getStartValue(), getMax(), 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)); - } - /** - * Find the minimum of the function {...@code f} within the interval {...@code (a, b)}. + * Find the minimum of the function within the interval {...@code (lo, hi)}. * - * 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}. + * 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 Abst * @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 Abst 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 Abst 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 Abst 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 Abst } } } 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(); } } Modified: commons/proper/math/trunk/src/test/java/org/apache/commons/math/optimization/MultiStartUnivariateRealOptimizerTest.java URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math/optimization/MultiStartUnivariateRealOptimizerTest.java?rev=980032&r1=980031&r2=980032&view=diff ============================================================================== --- commons/proper/math/trunk/src/test/java/org/apache/commons/math/optimization/MultiStartUnivariateRealOptimizerTest.java (original) +++ commons/proper/math/trunk/src/test/java/org/apache/commons/math/optimization/MultiStartUnivariateRealOptimizerTest.java Wed Jul 28 12:03:41 2010 @@ -48,8 +48,8 @@ public class MultiStartUnivariateRealOpt 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 MultiStartUnivariateRealOpt } 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 MultiStartUnivariateRealOpt 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); } } Modified: commons/proper/math/trunk/src/test/java/org/apache/commons/math/optimization/univariate/BrentOptimizerTest.java URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math/optimization/univariate/BrentOptimizerTest.java?rev=980032&r1=980031&r2=980032&view=diff ============================================================================== --- commons/proper/math/trunk/src/test/java/org/apache/commons/math/optimization/univariate/BrentOptimizerTest.java (original) +++ commons/proper/math/trunk/src/test/java/org/apache/commons/math/optimization/univariate/BrentOptimizerTest.java Wed Jul 28 12:03:41 2010 @@ -29,6 +29,7 @@ import org.apache.commons.math.analysis. 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(); + } + + 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.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); - - 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()); } }