% Conjugate Gradients minimization routine. Uses gradient magnitude % for termination condition. % % function [x] = conjgrad(x0,lsfun,lsparams,ogfun,ogparams,varargin) % x0 - initial value of the solution [d,1] % lsfun - pointer to line search function % function [alpha] = lsfun(x0,direction,ogfun,params) % x0 - location to start line search [d,1] % direction - direction from x0 along which to search [d,1] % ogfun - poitner to objective/gradient function % params - cell array of parameters to pass to ogfun % alpha - appx. location of minimum along direction % appx. minimium is: x0+alpha.*direction % lsparams - cell array of parameters to pass to lsparams % ogfun - pointer to objective/gradient calculation function % function [obj,grad] = ogfun(x,params) % x - parameter value at which to calculate obj/grad [d,1] % params - additional parameters % obj - objective value at x [scalar] % grad - gradient at x [d,1] % ogparams - cell array of parameters to pass to ogfun % x - final value of the solution (appxroximate local minimum) [d,1] % % Additional parameters given by name/value pairs via varagin. % E.g. conjgrad(x0,lsfun,params,ogfun,'verbose',0) % % This is an implementation of the Polak-Ribiere Nonlinear Conjugate % Gradients, as described by "An introduction to the conjugate % gradient method without the agonizing pain" by Shewchuk (1994) and % "Numerical Optimization" by Nocedal and Wright. % % Note: this code automatically uses the last alpha as initial value for % line search. This can backfire, causing lots of backtracking, if the % optimization surface isn't nice. To avoid this, pass a value of alpha0 % as part of lsparams (e.g. 'alpha0',1e-6) % % Written by Jason Rennie, February 2005 % Last modified: Mon Feb 5 21:06:51 2007 function [x,numiter] = conjgrad(x0,lsfun,lsparams,ogfun,ogparams,varargin) fn = mfilename; if nargin() < 5 error('insufficient parameters') end % Parameters to be set via varargin verbose = 2; % print progress information if true tol = 1e-3; % geometric decrease in gradient magnitude to declare minimum maxiter = 1000; % stop after this many iterations (if no minimum found) nu = 0.1; abstol = 0; % stop if gradient magnitude goes below this allowNonDecrease = 0; % don't stop if line search fails to find decrease digits = 12; % digits of precision to use for objective comparisons % Process varargin paramgt; t0 = clock; t1 = t0; ogcalls = 0; x = x0; numiter = 0; j = 0; alpha = 1e-10; ogcalls = ogcalls + 1; [obj,dx] = ogfun(x,ogparams{:}); r = -dx; s = r; d = s; deltanew = full(r'*d); deltazero = deltanew; if verbose >= 2 fprintf(1,'Begin deltazero=%1.1e tol=%.0e obj=%.1f\n',deltazero,tol,obj); end while numiter < maxiter & abs(deltanew) > tol.*tol.*abs(deltazero) & abs(deltanew) > abstol numiter = numiter + 1; j = j + 1; prevobj = obj; if alpha < 1e-10 alpha = 1e-10; end %checkgrad2(ogfun,x,ogparams); [alpha,obj,dx,ogc] = lsfun(x,obj,dx,d,ogfun,ogparams,'alpha0',alpha,'verbose',verbose,lsparams{:}); ogcalls = ogcalls + ogc; x = x + alpha.*d; if j==1 & pround(obj,digits) > pround(prevobj,digits) & ~allowNonDecrease %fprintf(1,'Line search could not find decrease in direction of negative gradient (bug in objective/gradient code?) dx''*dx=%.4e\n',full(dx'*dx)); if verbose >= 2 fprintf(1,'%s i=%d delta=%1.3e obj=%1.3e Time: %.1f s Calls: %d\n',fn,numiter,deltanew,obj,etime(clock,t0),ogcalls); end return end r = -dx; deltaold = deltanew; deltamid = full(r'*s); deltanew = full(r'*r); if verbose >= 2 lastt = t1; t1 = clock; fprintf(1,'i=%d x=%d alpha=%1.1e delta=%1.1e dobj=%1.1e time=%.1f obj=%.1f\n',numiter,ogc,alpha,deltanew,obj-prevobj,etime(t1,lastt),obj); end beta = (deltanew-deltamid)./deltaold; d = r + max(0,beta).*d; if deltamid./deltanew >= nu | d'*dx >= 0 if verbose >= 3 & length(x0) > 1 fprintf(1,'RESET\n'); end d = r; j = 0; end s = r; end if verbose fprintf(1,'%s i=%d delta=%1.3e obj=%1.3e Time: %.1f s Calls: %d\n',fn,numiter,deltanew,obj,etime(clock,t0),ogcalls); end % ChangeLog % 7/25/06 - Only print RESET if dimensionality is greater than 1. % 7/25/06 - Only break loop if line search returns larger objective % (accouting for precision issues) % 5/31/05 - return number of iterations % 5/18/05 - pass lsparams to lsfun after 'secstep' % 5/17/05 - default abstol to zero % 5/17/05 - make sure dx'*dx is full() in warning % 5/1/05 - Check for d'*dx >= 0 % 3/22/05 - Add abstol parameter/stopping condition % 3/19/05 - Tell line search to use previous iteration alpha as 1st % guess; many line searches complete after one obj/grad call, % especially when close to the solution