001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package org.apache.commons.math.optimization.direct; 019 020 import org.apache.commons.math.MaxIterationsExceededException; 021 import org.apache.commons.math.analysis.UnivariateRealFunction; 022 import org.apache.commons.math.FunctionEvaluationException; 023 import org.apache.commons.math.optimization.GoalType; 024 import org.apache.commons.math.optimization.OptimizationException; 025 import org.apache.commons.math.optimization.RealPointValuePair; 026 import org.apache.commons.math.optimization.general.AbstractScalarDifferentiableOptimizer; 027 import org.apache.commons.math.optimization.univariate.AbstractUnivariateRealOptimizer; 028 import org.apache.commons.math.optimization.univariate.BracketFinder; 029 import org.apache.commons.math.optimization.univariate.BrentOptimizer; 030 031 /** 032 * Powell algorithm. 033 * This code is translated and adapted from the Python version of this 034 * algorithm (as implemented in module {@code optimize.py} v0.5 of 035 * <em>SciPy</em>). 036 * 037 * @version $Revision$ $Date$ 038 * @since 2.2 039 */ 040 public class PowellOptimizer 041 extends AbstractScalarDifferentiableOptimizer { 042 /** 043 * Default relative tolerance for line search ({@value}). 044 */ 045 public static final double DEFAULT_LS_RELATIVE_TOLERANCE = 1e-7; 046 /** 047 * Default absolute tolerance for line search ({@value}). 048 */ 049 public static final double DEFAULT_LS_ABSOLUTE_TOLERANCE = 1e-11; 050 /** 051 * Line search. 052 */ 053 private final LineSearch line; 054 055 /** 056 * Constructor with default line search tolerances (see the 057 * {@link #PowellOptimizer(double,double) other constructor}). 058 */ 059 public PowellOptimizer() { 060 this(DEFAULT_LS_RELATIVE_TOLERANCE, 061 DEFAULT_LS_ABSOLUTE_TOLERANCE); 062 } 063 064 /** 065 * Constructor with default absolute line search tolerances (see 066 * the {@link #PowellOptimizer(double,double) other constructor}). 067 * 068 * @param lsRelativeTolerance Relative error tolerance for 069 * the line search algorithm ({@link BrentOptimizer}). 070 */ 071 public PowellOptimizer(double lsRelativeTolerance) { 072 this(lsRelativeTolerance, 073 DEFAULT_LS_ABSOLUTE_TOLERANCE); 074 } 075 076 /** 077 * @param lsRelativeTolerance Relative error tolerance for 078 * the line search algorithm ({@link BrentOptimizer}). 079 * @param lsAbsoluteTolerance Relative error tolerance for 080 * the line search algorithm ({@link BrentOptimizer}). 081 */ 082 public PowellOptimizer(double lsRelativeTolerance, 083 double lsAbsoluteTolerance) { 084 line = new LineSearch(lsRelativeTolerance, 085 lsAbsoluteTolerance); 086 } 087 088 /** {@inheritDoc} */ 089 @Override 090 protected RealPointValuePair doOptimize() 091 throws FunctionEvaluationException, 092 OptimizationException { 093 final double[] guess = point.clone(); 094 final int n = guess.length; 095 096 final double[][] direc = new double[n][n]; 097 for (int i = 0; i < n; i++) { 098 direc[i][i] = 1; 099 } 100 101 double[] x = guess; 102 double fVal = computeObjectiveValue(x); 103 double[] x1 = x.clone(); 104 while (true) { 105 incrementIterationsCounter(); 106 107 double fX = fVal; 108 double fX2 = 0; 109 double delta = 0; 110 int bigInd = 0; 111 double alphaMin = 0; 112 113 for (int i = 0; i < n; i++) { 114 final double[] d = /* Arrays.*/ copyOf(direc[i], n); // Java 1.5 does not support Arrays.copyOf() 115 116 fX2 = fVal; 117 118 line.search(x, d); 119 fVal = line.getValueAtOptimum(); 120 alphaMin = line.getOptimum(); 121 final double[][] result = newPointAndDirection(x, d, alphaMin); 122 x = result[0]; 123 124 if ((fX2 - fVal) > delta) { 125 delta = fX2 - fVal; 126 bigInd = i; 127 } 128 } 129 130 final RealPointValuePair previous = new RealPointValuePair(x1, fX); 131 final RealPointValuePair current = new RealPointValuePair(x, fVal); 132 if (getConvergenceChecker().converged(getIterations(), previous, current)) { 133 if (goal == GoalType.MINIMIZE) { 134 return (fVal < fX) ? current : previous; 135 } else { 136 return (fVal > fX) ? current : previous; 137 } 138 } 139 140 final double[] d = new double[n]; 141 final double[] x2 = new double[n]; 142 for (int i = 0; i < n; i++) { 143 d[i] = x[i] - x1[i]; 144 x2[i] = 2 * x[i] - x1[i]; 145 } 146 147 x1 = x.clone(); 148 fX2 = computeObjectiveValue(x2); 149 150 if (fX > fX2) { 151 double t = 2 * (fX + fX2 - 2 * fVal); 152 double temp = fX - fVal - delta; 153 t *= temp * temp; 154 temp = fX - fX2; 155 t -= delta * temp * temp; 156 157 if (t < 0.0) { 158 line.search(x, d); 159 fVal = line.getValueAtOptimum(); 160 alphaMin = line.getOptimum(); 161 final double[][] result = newPointAndDirection(x, d, alphaMin); 162 x = result[0]; 163 164 final int lastInd = n - 1; 165 direc[bigInd] = direc[lastInd]; 166 direc[lastInd] = result[1]; 167 } 168 } 169 } 170 } 171 172 /** 173 * Compute a new point (in the original space) and a new direction 174 * vector, resulting from the line search. 175 * The parameters {@code p} and {@code d} will be changed in-place. 176 * 177 * @param p Point used in the line search. 178 * @param d Direction used in the line search. 179 * @param optimum Optimum found by the line search. 180 * @return a 2-element array containing the new point (at index 0) and 181 * the new direction (at index 1). 182 */ 183 private double[][] newPointAndDirection(double[] p, 184 double[] d, 185 double optimum) { 186 final int n = p.length; 187 final double[][] result = new double[2][n]; 188 final double[] nP = result[0]; 189 final double[] nD = result[1]; 190 for (int i = 0; i < n; i++) { 191 nD[i] = d[i] * optimum; 192 nP[i] = p[i] + nD[i]; 193 } 194 return result; 195 } 196 197 /** 198 * Class for finding the minimum of the objective function along a given 199 * direction. 200 */ 201 private class LineSearch { 202 /** 203 * Optimizer. 204 */ 205 private final AbstractUnivariateRealOptimizer optim = new BrentOptimizer(); 206 /** 207 * Automatic bracketing. 208 */ 209 private final BracketFinder bracket = new BracketFinder(); 210 /** 211 * Value of the optimum. 212 */ 213 private double optimum = Double.NaN; 214 /** 215 * Value of the objective function at the optimum. 216 */ 217 private double valueAtOptimum = Double.NaN; 218 219 /** 220 * @param relativeTolerance Relative tolerance. 221 * @param absoluteTolerance Absolute tolerance. 222 */ 223 public LineSearch(double relativeTolerance, 224 double absoluteTolerance) { 225 optim.setRelativeAccuracy(relativeTolerance); 226 optim.setAbsoluteAccuracy(absoluteTolerance); 227 } 228 229 /** 230 * Find the minimum of the function {@code f(p + alpha * d)}. 231 * 232 * @param p Starting point. 233 * @param d Search direction. 234 * @throws FunctionEvaluationException if function cannot be evaluated at some test point 235 * @throws OptimizationException if algorithm fails to converge 236 */ 237 public void search(final double[] p, final double[] d) 238 throws OptimizationException, FunctionEvaluationException { 239 240 // Reset. 241 optimum = Double.NaN; 242 valueAtOptimum = Double.NaN; 243 244 try { 245 final int n = p.length; 246 final UnivariateRealFunction f = new UnivariateRealFunction() { 247 public double value(double alpha) 248 throws FunctionEvaluationException { 249 250 final double[] x = new double[n]; 251 for (int i = 0; i < n; i++) { 252 x[i] = p[i] + alpha * d[i]; 253 } 254 final double obj; 255 obj = computeObjectiveValue(x); 256 return obj; 257 } 258 }; 259 260 bracket.search(f, goal, 0, 1); 261 optimum = optim.optimize(f, goal, 262 bracket.getLo(), 263 bracket.getHi(), 264 bracket.getMid()); 265 valueAtOptimum = optim.getFunctionValue(); 266 } catch (MaxIterationsExceededException e) { 267 throw new OptimizationException(e); 268 } 269 } 270 271 /** 272 * @return the optimum. 273 */ 274 public double getOptimum() { 275 return optimum; 276 } 277 /** 278 * @return the value of the function at the optimum. 279 */ 280 public double getValueAtOptimum() { 281 return valueAtOptimum; 282 } 283 } 284 285 /** 286 * Java 1.5 does not support Arrays.copyOf() 287 * 288 * @param source the array to be copied 289 * @param newLen the length of the copy to be returned 290 * @return the copied array, truncated or padded as necessary. 291 */ 292 private double[] copyOf(double[] source, int newLen) { 293 double[] output = new double[newLen]; 294 System.arraycopy(source, 0, output, 0, Math.min(source.length, newLen)); 295 return output; 296 } 297 298 }