| /* |
| * 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; |
| } |
| |
| } |