www.gusucode.com > robotmanip 工具箱 matlab源码程序 > robotmanip/+robotics/+manip/+internal/DampedBFGSwGradientProjection.m
classdef DampedBFGSwGradientProjection < robotics.manip.internal.NLPSolverInterface %This class is for internal use only. It may be removed in the future. %DAMPEDBFGSWGRADIENTPROJECTION Solver for problems with nonlinear cost % and linear constraints. % % min F(x) % x % s.t. A* x <= b % where x is an n-by-1 vector; F(x) is a scalar and a generic nonlinear % function of x; A is an m-by-n matrix, b is an m-by-1 vector. % Note, m is the number of constraints, n is the number of variables. % Copyright 2016 The MathWorks, Inc. % % References: % % [1] J. Zhao and N. Badler, Inverse kinematics positioning using % nonlinear programming for highly articulated figures % ACM Transactions on Graphics, Vol. 13, No. 4, 1994. % % [2] H. Badreddine, S. Vandewalle and J. Meyers, Sequential % quadratic programming for optimal control in direct numerical % simulation of turbulent flow % Journal of Computational Physics, 256, 1-16, 2014 % % [3] D. Goldfarb, Extension of Davidon's variable metric method to % maximization under linear inequality and equality constraints % SIAM Journal of Applied Mathematics, vol. 17, No. 4, 1969 % % [4] J. Nocedal and S. Wright, Numerical Optimization (Second Ed.) % Springer, New York, 2006 % % [5] D. Bertsekas, Nonlinear Programming (Second Ed.) % Athena Scientific, Belmont, MA, 1999 % properties (Access = protected) %GradientTolerance The iteration stops if the norm of the gradient % falls below this value (a positive value) GradientTolerance %ArmijoRule parameters (for line search) % The Armijo condition for a viable step: % f(x) - f(x + step) >= - sigma* grad(x) * step % where step = direction * beta^k %ArmijoRuleBeta A scalar between 0 and 1 ArmijoRuleBeta %ArmijoRuleSigma A small positive number ArmijoRuleSigma end methods function obj = DampedBFGSwGradientProjection() %DampedBFGSwGradientProjection Constructor obj.MaxNumIteration = 1500; obj.MaxTime = 10; obj.GradientTolerance = 1e-7; obj.SolutionTolerance = 1e-6; obj.ArmijoRuleBeta = 0.4; obj.ArmijoRuleSigma = 1e-5; obj.ConstraintsOn = true; obj.RandomRestart = true; obj.StepTolerance = 1e-14; obj.Name = 'BFGSGradientProjection'; end function params = getSolverParams(obj) params.Name = obj.Name; params.MaxNumIteration = obj.MaxNumIteration; params.MaxTime = obj.MaxTime; params.GradientTolerance = obj.GradientTolerance; params.SolutionTolerance = obj.SolutionTolerance; params.ArmijoRuleBeta = obj.ArmijoRuleBeta; params.ArmijoRuleSigma = obj.ArmijoRuleSigma; params.ConstraintsOn = obj.ConstraintsOn; params.RandomRestart = obj.RandomRestart; params.StepTolerance = obj.StepTolerance; end function setSolverParams(obj, params) obj.MaxNumIteration = params.MaxNumIteration; obj.MaxTime = params.MaxTime; obj.GradientTolerance = params.GradientTolerance; obj.SolutionTolerance = params.SolutionTolerance; obj.ConstraintsOn = params.ConstraintsOn; obj.RandomRestart = params.RandomRestart; obj.StepTolerance = params.StepTolerance; end function newobj = copy(obj) %COPY newobj = robotics.manip.internal.DampedBFGSwGradientProjection(); newobj.Name = obj.Name; newobj.ConstraintBound = obj.ConstraintBound; newobj.ConstraintMatrix = obj.ConstraintMatrix; newobj.ConstraintsOn = obj.ConstraintsOn; newobj.SolutionTolerance = obj.SolutionTolerance; newobj.RandomRestart = obj.RandomRestart; newobj.SeedInternal = obj.SeedInternal; newobj.MaxTimeInternal = obj.MaxTimeInternal; newobj.MaxNumIterationInternal = obj.MaxNumIterationInternal; newobj.CostFcn = obj.CostFcn; newobj.GradientFcn = obj.GradientFcn; newobj.SolutionEvaluationFcn = obj.SolutionEvaluationFcn; newobj.RandomSeedFcn = obj.RandomSeedFcn; newobj.BoundHandlingFcn = obj.BoundHandlingFcn; newobj.ExtraArgs = obj.ExtraArgs; newobj.setSolverParams(obj.getSolverParams); end end methods (Access = {?robotics.manip.internal.NLPSolverInterface, ... ?matlab.unittest.TestCase}) function [xSol, exitFlag, err, iter] = solveInternal(obj) x = obj.SeedInternal; t0 = tic; % Dimension n = size(x,1); [cost, ~, ~, obj.ExtraArgs] = obj.CostFcn(x, obj.ExtraArgs); grad = obj.GradientFcn(x, obj.ExtraArgs); grad = grad(:); % H starts as a positive-definite matrix (so symmetric) H = eye(n); if obj.ConstraintsOn % Identify active-set (constraints that hit equality) activeSet = obj.ConstraintMatrix'*x >= obj.ConstraintBound; A = obj.ConstraintMatrix(:, activeSet); else A = []; end % Project Hessian Inverse to the valid manifold using a way % similar to Gram-Schmidt process % This ensures that the H update does not violate constraints for a = A rho = a'*H*a; % a scalar H = H - (1/rho)*H*(a*a')*H; end % Main loop for i = 1:obj.MaxNumIterationInternal if timeLimitExceeded(obj, toc(t0)) xSol = x; exitFlag = robotics.manip.internal.NLPSolverExitFlags.TimeLimitExceeded; err = obj.SolutionEvaluationFcn(xSol, obj.ExtraArgs); iter = i; return end if isempty(A) alpha = 0; else alpha = (A'*A)\A'*grad; % alpha is an indicator end Hg = H*grad; if atLocalMinimum(obj, Hg, alpha) % Evaluate found local minimum xSol = x; exitFlag = robotics.manip.internal.NLPSolverExitFlags.LocalMinimumFound; err = obj.SolutionEvaluationFcn(xSol, obj.ExtraArgs); iter = i; return; end if obj.ConstraintsOn && ~isempty(A) B = inv(A'*A); [threshold, p] = max(alpha./sqrt(diag(B))); % If the norm of the modified gradient Hg is smaller than % threshold, drop the constraint corresponding to the % threshold value. if norm(Hg) < 0.5*threshold % Find the constraint to drop activeConstraintIndices = find(activeSet); idxp = activeConstraintIndices(p); % Update active constraints A activeSet(idxp) = false; A = obj.ConstraintMatrix(:, activeSet); % Compute Projection P = eye(n) - A*((A'*A)\A'); % Update H, allowing more flexibility in update % direction ap = obj.ConstraintMatrix(:, idxp); H = H + (1/(ap'*P*ap))*P*(ap*ap')*P; continue end end % Take a test step, then screen current inactive constraints % and see if any becomes active, and determine the step % scaling factor upper bound lambda % s.t. ai'*(x + lambdai*s) - bi <=0 for all i % lambdai needs to be positive s = - Hg; if obj.ConstraintsOn ... && any(~activeSet) % if there is inactive constraints bIn = obj.ConstraintBound(~activeSet); AIn = obj.ConstraintMatrix(:,~activeSet); inactiveConstraintIndices = find(~activeSet); lambdas = (bIn - AIn'*x)./(AIn'*s); % We don't care the case when ai'*x-bi < 0 and ai'*s<0 L = find(lambdas>0); if ~isempty(L) [lambda, l] = min(lambdas(lambdas>0)); idxl = inactiveConstraintIndices(L(l)); else lambda = 0; end else lambda = 0; end if lambda > 0 gamma = min(1, lambda); else % When the test step does not hit any constraint gamma = 1; end s0 = s; % Line search beta = obj.ArmijoRuleBeta; sigma = obj.ArmijoRuleSigma; [costNew,~,~, obj.ExtraArgs] = obj.CostFcn(x+gamma*s0, obj.ExtraArgs); m = 0; while cost - costNew < -sigma * grad'*(gamma*s0) if stepSizeBelowMinimum(obj, gamma) xSol = x; exitFlag = robotics.manip.internal.NLPSolverExitFlags.StepSizeBelowMinimum; err = obj.SolutionEvaluationFcn(xSol, obj.ExtraArgs); iter = i; return end gamma = beta * gamma; m = m+1; [costNew,~,~, obj.ExtraArgs] = obj.CostFcn(x + gamma*s0, obj.ExtraArgs); end xNew = x + gamma*s0; gradNew = obj.GradientFcn(xNew, obj.ExtraArgs); gradNew = gradNew(:); % Case 1: gamma = 1 < lambda, xNew has not reached new % new constraint. % Case 2: gamma = 1 and lambda = 0, no new constraints. % In both cases, H is updated in an unconstrained % manner using damped BFGS % When gamma == lambda, H is projected to the updated % constrained manifold as new constraint is activated. if m==0 && abs(gamma - lambda)< sqrt(eps) % ? a = obj.ConstraintMatrix(:,idxl); activeSet(idxl) = true; A = obj.ConstraintMatrix(:, activeSet); % Project the search direction to constrained manifold H = H - (1/(a'*H*a))*(H*(a*a'*H)); else % Update search direction using damped BFGS y = gradNew - grad; d1 = 0.2; d2 = 0.8; % Hessian damping formulation 1 (update s) if s'*y < d1*y'*H*y theta = d2*y'*H*y/(y'*H*y - s'*y); else theta = 1; end sNew = theta*s + (1-theta)*H*y; rho = sNew'*y; V = eye(n)- (sNew*y')/rho; H = V*H*V' + (sNew*sNew')/rho; % % Hessian damping formula 2 (update y) % if s'*y < d1*s'*H*s % % th = d2*(s'*H*s)/(s'*H*s - s'*y); % y = th*y + (1 - th)*H*s; % end % % H = (eye(n) - (s*y')/(y'*s))*H*(eye(n)- (y*s')/(y'*s)) + s*s'/(y'*s); % Making sure H is not non-PSD if hessianNotPositiveSemidefinite(obj, H) xSol = xNew; exitFlag = robotics.manip.internal.NLPSolverExitFlags.HessianNotPositiveSemidefinite; err = obj.SolutionEvaluationFcn(xSol, obj.ExtraArgs); iter = i; return end end if searchDirectionInvalid(obj, xNew) % This really should not happen, just in case xSol = x; exitFlag = robotics.manip.internal.NLPSolverExitFlags.SearchDirectionInvalid; err = obj.SolutionEvaluationFcn(xSol, obj.ExtraArgs); iter = i; return end x = xNew; grad = gradNew; cost = costNew; end xSol = xNew; exitFlag = robotics.manip.internal.NLPSolverExitFlags.IterationLimitExceeded; err = obj.SolutionEvaluationFcn(xSol, obj.ExtraArgs); iter = i; end function flag = atLocalMinimum(obj, Hg, alpha) flag = norm(Hg)<obj.GradientTolerance && all(alpha<=0); end function flag = searchDirectionInvalid(obj, xNew) flag = obj.ConstraintsOn && any(obj.ConstraintMatrix'*xNew - obj.ConstraintBound > sqrt(eps)); end function flag = hessianNotPositiveSemidefinite(~,H) [~, p] = chol(H + sqrt(eps)*eye(size(H))); flag = p ~= 0; end function flag = stepSizeBelowMinimum(obj, gamma) flag = gamma < obj.StepTolerance; end end end