/*
* Copyright (c) 2012-2015, Peter Abeles. All Rights Reserved.
*
* This file is part of DDogleg (http://ddogleg.org).
*
* Licensed 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.ddogleg.optimization.impl;
import org.ddogleg.optimization.LineSearch;
import org.ddogleg.optimization.OptimizationException;
import org.ddogleg.optimization.functions.CoupledDerivative;
/**
*
* Line search algorithm that provides a guaranteed sufficient decrease according to the Wolfe condition.
* This is the same algorithm by Minpack-2 [2]. The logic in the code below is based off of [2] but the original
* paper [1] was being looked at too.
*
*
*
*
* Wolfe condition
* {@code φ(α) ≤ φ(0) + ftol*αφ'(0)}
* {@code |φ'(α)| ≤ gtol*|φ'(0)|}
* where ftol and gtol determine the precision needed to terminate the search..
*
*
*
* This is basically a "cleaned up" version of the code from:.
* MINPACK-2 Project. November 1993.
* Argonne National Laboratory and University of Minnesota.
* Brett M. Averick, Richard G. Carter, and Jorge J. More'.
*
* Permission has been given by Jorge Moré (after he emerged from underwater) to redistribute csrch on
* July 8th 2012 via e-mail.
*
*
*
* [1] Jorge J. More and David J. Thuente, "Line Search Algorithms with Guaranteed Sufficient Decrease"
* ACM Transactions of Mathematical Software, Vol 20 , No. 3, September 1994, Pages 286-307
* [2] MINPACK-2 source code http://ftp.mcs.anl.gov/pub/MINPACK-2/csrch/
*
* @author Peter Abeles
*/
public class LineSearchMore94 implements LineSearch {
// Various magic numbers. See [1]
private static final double p5=0.5,p66=0.66,xtrapl=1.1,xtrapu=4.0;
// computes the function value and derivative
private CoupledDerivative function;
// thresholds to test for convergence
private double ftol=-1; // tolerance for sufficient decrease. ftol > 0
private double gtol; // tolerance for curvature condition. gtol >= 0
private double xtol; // relative tolerance for an acceptable step. xtol >= 0
// Only a warning is provided if xtol has not been meet
// value of the function and derivative at 0
private double finit;
private double ginit;
// Equal to ftol*ginit.
private double gtest;
// size of the bracketed region
private double width;
private double width1;
// flag indicating if the step size has been bracketed yet
private boolean bracket;
// which stage in the optimization process is it
private int stage;
// stx and sty define the closed interval that the solution must lie inside of.
// - Before a bound/bracket is found stx < sty.
// - After the bracket has been found stx and sty defined the allowed interval's end points.
// The relative magnitude of stx and sty is variable.
// stx is alpha_l in the paper and is the best possible step found so far
private double stx,fx,gx;
// sty = alpha_u in the paper
private double sty,fy,gy;
// Adaptively found lower and upper bounds used to bracket the solution
private double stmin,stmax;
// Current trial value for the step. Corresponds to alpha_t in the paper
private double stp, fp, gp;
// User specified lower and upper bound for the step stp
private double stpmin,stpmax;
// Indicates if there were any numerical issues that caused it to stop iterating
private String message;
// Indicates if this iit is the first iteration and indicates if what values need to be computed at stp
private boolean firstIteration;
// Indicates if it converged to a solution
private boolean converged;
// if true the estimated parameters have been updated
boolean updated;
/**
* Configures the line search.
*
* @param ftol Tolerance for sufficient decrease. ftol {@code >} 0. Smaller value for loose tolerance. Try 1e-4
* @param gtol Tolerance for curvature condition. gtol ≥ 0. Larger value for loose tolerance. Try 1e-3
* @param xtol Relative tolerance for acceptable step. xtol ≥ 0. Larger value for loose tolerance. Try 1e-4.
*
* @return Reference to this class to allow for command chaining.
*/
public LineSearchMore94 setConvergence(double ftol, double gtol, double xtol) {
if( ftol < 0 )
throw new IllegalArgumentException("ftol must be >= 0 ");
if( gtol < 0 )
throw new IllegalArgumentException("gtol must be >= 0 ");
if( xtol < 0 )
throw new IllegalArgumentException("xtol must be >= 0 ");
this.ftol = ftol;
this.gtol = gtol;
this.xtol = xtol;
return this;
}
@Override
public void setFunction( CoupledDerivative function) {
this.function = function;
}
@Override
public void init(double funcAtZero, double derivAtZero, double funcAtInit, double stepInit ,
double stepMin, double stepMax )
{
if( ftol < 0 )
throw new IllegalArgumentException("Must call setParameters first before init");
if( stepMax < stepMin )
throw new IllegalArgumentException("stpmin must be < stpmax");
if( stepMin < 0 )
throw new IllegalArgumentException("stpmin must be > 0");
if( stepInit < stepMin)
throw new IllegalArgumentException("Initial step is less than the minimum allowed step.");
if( stepInit > stepMax)
throw new IllegalArgumentException("Initial step is more than the maximum allowed step.");
if( derivAtZero >= 0 )
throw new IllegalArgumentException("Initial derivative is >= 0");
this.stpmin = stepMin;
this.stpmax = stepMax;
this.bracket = false;
this.stage = 0;
this.finit = funcAtZero;
this.ginit = derivAtZero;
this.gtest = ftol*ginit;
this.width = stpmax - stpmin;
this.width1 = width/p5;
this.stp = stepInit;
this.fp = funcAtInit;
stx = 0;
fx = finit;
gx = ginit;
sty = 0;
fy = finit;
gy = ginit;
stmin = 0;
stmax = stp + xtrapu*stp;
message = null;
firstIteration = true;
converged = false;
updated = false;
}
@Override
public boolean iterate() throws OptimizationException {
// update the function and derivative values at stp
function.setInput(stp);
gp = function.computeDerivative();
if( !firstIteration ) {
fp = function.computeFunction();
} else {
firstIteration = false;
}
// Enter the second stage if a point has been found which meets the first wolfe condition
// and is known to be past the optimal condition by having a positive derivative
double ftest = finit + stp*gtest;
if( stage == 0 && fp <= ftest && gp >= 0 )
stage = 1;
// check warning conditions
if( bracket && (stp <= stmin || stp >= stmax))
message = "Rounding error preventing progress.";
if( bracket && stmax - stmin <= xtol*stmax) {
converged = true;
message = "XTOL test satisfied";
}
if( stp == stpmax && fp <= ftest && gp <= gtest )
message = "stp == stpmax";
if( stp == stpmin && (fp > ftest || gp >= gtest ))
message = "stp == stpmin";
// Check for convergence using the Wolfe conditions
if( fp <= ftest && Math.abs(gp) <= gtol*(-ginit)) {
converged = true;
return true;
}
// Warning messages indicates that no progress can be made and that it should stop iterating
if( message != null || converged )
return true;
// See if it is searching for the upper bound still
if( stage == 0 && fp <= fx && fp > ftest ) {
// Transform values using the function phi(x). See pg 291
// This function has been modified from what's in the paper by removing phi(0) since it
// did not effect the outcome of the inequalities.
// phi(x) = f(x) - ftol*f'(0)*x
fp -= stp*gtest; // fm
fx -= stx*gtest; // fxm
fy -= sty*gtest; // fym
gp -= gtest; // gm
gx -= gtest; // gxm
gy -= gtest; // gym
// compute the new step
dcstep();
// transform these values back into the original function
fx += stx*gtest;
fy += sty*gtest;
gx += gtest;
gy += gtest;
} else {
// A upper bound has been found and the step is being refined
dcstep();
}
// decide if a bisection step is needed
if( bracket ) {
if( Math.abs(sty-stx) >= p66*width1 )
stp = stx + p5*(sty-stx);
width1 = width;
width = Math.abs(sty-stx);
}
// set the minimum and maximum steps allowed for stp
if( bracket ) {
// if a bracket has been found then stx and sty define the lower and upper bounds, but
// their order is variable
stmin = Math.min(stx,sty);
stmax = Math.max(stx,sty);
} else {
stmin = stp + xtrapl*(stp-stx);
stmax = stp + xtrapu*(stp-stx);
}
// force the stp to be within the user specified bound
stp = Math.max(stp, stpmin);
stp = Math.min(stp, stpmax);
// see if further progress can be made. If not set stp to be equal to
// the best point obtained so far
if( bracket && (stp <= stmin || stp >= stmax ) || (bracket && stmax-stmin <= xtol*stmax))
stp=stx;
updated = true;
return false;
}
/**
* Computes the new step and updates fx,fy,gx,dy.
*/
private void dcstep() {
double sgnd = gp*Math.signum(gx);
// the new step
double stpf;
if( fp > fx ) {
stpf = handleCase1();
} else if( sgnd < 0 ) {
stpf = handleCase2();
} else if( Math.abs(gp) < Math.abs(gx) ) {
stpf = handleCase3();
} else {
stpf = handleCase4();
}
// update the bracket which contains the solution
if( fp > fx ) {
// found an upper bound
sty = stp;
fy = fp;
gy = gp;
} else {
// see if its on the other side of the dip
if( sgnd < 0 ) {
sty = stx;
fy = fx;
gy = gx;
}
stx = stp;
fx = fp;
gx = gp;
}
stp = stpf;
updated = true;
}
/**
* stp has a higher value than stx. The minimum must be between these two values.
* Pick a point which is close to stx since it has a lower value.
*
* @return The new step.
*/
private double handleCase1() {
double stpc = SearchInterpolate.cubic2(fx, gx, stx, fp, gp, stp);
double stpq = SearchInterpolate.quadratic(fx,gx,stx,fp,stp);
// If the cubic step is closer to stx than the quadratic step take that
bracket = true;
if( Math.abs(stpc-stx) < Math.abs(stpq-stx)) {
return stpc;
} else {
// otherwise go with the average of the twp
return stpc + (stpq-stpc)/2.0;
}
}
/**
* The sign of the derivative has swapped, indicating that the function is on the other
* side of the dip and that a bracket has been found.
*
* @return The new step.
*/
private double handleCase2() {
double stpc = SearchInterpolate.cubic2(fp, gp, stp, fx, gx, stx);
double stpq = SearchInterpolate.quadratic2(gp,stp,gx,stx);
// use which ever is closest to stp since it is lower than stx
bracket = true;
if( Math.abs(stpc - stp) > Math.abs(stpq - stp)) {
return stpc;
} else {
return stpq;
}
}
/**
* The derivative at stp has a smaller magnitude than at stx and is likely to be closer to the minimum. However
* there are special cases that need to be dealt with here.
*
* @return The new step.
*/
private double handleCase3() {
double stpf;
// use cubic interpolation only if it is safe
double stpc = SearchInterpolate.cubicSafe(fp, gp, stp, fx, gx, stx, stmin, stmax);
double stpq = SearchInterpolate.quadratic2(gp,stp,gx,stx);
if( bracket ) {
// Use which ever step is closer to stp
if( Math.abs(stpc-stp) < Math.abs(stpq-stp)){
stpf = stpc;
} else {
stpf = stpq;
}
if( stp > stx ) {
stpf = Math.min(stp+p66*(sty-stp),stpf);
} else {
stpf = Math.max(stp+p66*(sty-stp),stpf);
}
} else {
// because a bracket has not been found, take whichever step is farther from stp
if( Math.abs(stpc-stp) > Math.abs(stpq-stp)) {
stpf = stpc;
} else {
stpf = stpq;
}
stpf = Math.min(stmax,stpf); // this really is stmax/stmin and not stpmax/stpmin
stpf = Math.max(stmin,stpf); // In the for tran can stmax and stmin is passed in for stpmax and stpmin
}
return stpf;
}
/**
* Lower function value. On the same side of the dip because the gradients have the same sign. The magnitude
* of the derivative did not decrease.
*
* @return The new step.
*/
private double handleCase4() {
if( bracket ) {
return SearchInterpolate.cubic2(fp, gp, stp, fy, gy, sty);
} else if( stp > stx ) {
return stmax; // see comment in case 3 about stmax and stmin
} else {
return stmin;
}
}
@Override
public boolean isConverged() {
return converged;
}
@Override
public double getStep() {
return stp;
}
@Override
public String getWarning() {
return message;
}
@Override
public double getFunction() {
return fp;
}
@Override
public boolean isUpdated() {
return updated;
}
}
Ads help maintain this website.