dynare/matlab/lmmcp/lmmcp.m

626 lines
18 KiB
Matlab

function [x,FVAL,EXITFLAG,OUTPUT,JACOB] = lmmcp(FUN,x,lb,ub,options,varargin)
% LMMCP solves a mixed complementarity problem.
%
% LMMCP uses a semismooth least squares formulation. The method applies a
% Levenberg-Marquardt/Gauss-Newton algorithm to a least-squares formulation.
%
% X = LMMCP(FUN,X0) tries to solve the system of nonlinear equations F(X)=0 and
% starts at the vector X0. FUN accepts a vector X and return a vector F of equation
% values F evaluated at X and, as second output if required, a matrix J, the
% Jacobian evaluated at X.
%
% X = LMMCP(FUN,X0,LB,UB) solves the mixed complementarity problem of the form:
% LB =X => F(X)>0,
% LB<=X<=UB => F(X)=0,
% X =UB => F(X)<0.
%
% X = LMMCP(FUN,X0,LB,UB,OPTIONS) solves the MCP problem using the options
% defined in the structure OPTIONS. Main fields are
% Display : control the display of iterations, 'none' (default),
% 'iter-detailed' or 'final-detailed'
% Switch from phase I to phase II
% preprocess : activate preprocessor for phase I (default = 1)
% presteps : number of iterations in phase I (default = 20)
% Termination parameters
% MaxIter : Maximum number of iterations (default = 500)
% tmin : safeguard stepsize (default = 1E-12)
% TolFun : Termination tolerance on the function value, a positive
% scalar (default = sqrt(eps))
% Stepsize parameters
% m : number of previous function values to use in the nonmonotone
% line search rule (default = 10)
% kwatch : maximum number of steps (default = 20 and should not be
% smaller than m)
% watchdog : activate the watchdog strategy (default = 1)
% Ther are other minor parameters. Please see the code for their default values
% and interpretation.
%
% [X,FVAL] = LMMCP(FUN,X0,...) returns the value of the equations FUN at X.
%
% [X,FVAL,EXITFLAG] = LMMCP(FUN,X0,...) returns EXITFLAG that describes the exit
% conditions. Possible values are
% 1 : LMMCP converged to a root
% 0 : Too many iterations
% -1 :
%
% [X,FVAL,EXITFLAG,OUTPUT] = LMMCP(FUN,X0,...) returns the structure OUTPUT that
% contains the number of iterations (OUTPUT.iterations), the value of the merit
% function (OUTPUT.Psix), and the norm of the derivative of the merit function
% (OUTPUT.normDPsix).
%
% [X,FVAL,EXITFLAG,OUTPUT,JACOB] = LMMCP(FUN,X0,...) returns JACOB the Jacobian
% of FUN evaluated at X.
%
% More details of the main program may be found in the following paper:
%
% Christian Kanzow and Stefania Petra: On a semismooth least squares formulation of
% complementarity problems with gap reduction. Optimization Methods and Software
% 19, 2004, pp. 507-525.
%
% In addition, the current implementation uses a preprocessor which is the
% projected Levenberg-Marquardt step from the following preprint:
%
% Christian Kanzow and Stefania Petra: Projected filter trust region methods for a
% semismooth least squares formulation of mixed complementarity
% problems. Optimization Methods and Software
% 22, 2007, pp. 713-735.
%
% A user's guide is also available:
%
% Christian Kanzow and Stefania Petra (2005).
% LMMCP --- A Levenberg-Marquardt-type MATLAB Solver for Mixed Complementarity Problems.
% University of Wuerzburg.
% http://www.mathematik.uni-wuerzburg.de/~kanzow/software/UserGuide.pdf
%
% This is a modification by Christophe Gouel of the original files, which can be
% downloaded from:
% http://www.mathematik.uni-wuerzburg.de/~kanzow/software/LMMCP.zip
%
% Written by Christian Kanzow and Stefania Petra
% Institute of Applied Mathematics and Statistics
% University of Wuerzburg
% Am Hubland
% 97074 Wuerzburg
% GERMANY
%
% e-mail: kanzow@mathematik.uni-wuerzburg.de
% petra@mathematik.uni-wuerzburg.de
%
% Christian Kanzow sent a private message to Dynare Team on July 8, 2014,
% confirming the free software status of lmmcp and granting unlimited
% permission to use, copy, modifiy or redistribute the file.
% Copyright © 2005 Christian Kanzow and Stefania Petra
% Copyright © 2013 Christophe Gouel
% Copyright © 2014-2017 Dynare Team
%
% Unlimited permission is granted to everyone to use, copy, modify or
% distribute this software.
%% Initialization
defaultopt = struct(...
'beta', 0.55,...
'Big', 1e10,...
'delta', 5,...
'deltamin', 1,...
'Display', 'none',...
'epsilon1', 1e-6,...
'eta', 0.95,...
'kwatch', 20,...
'lambda1', 0.1,...
'm', 10,...
'MaxIter', 500,...
'null', 1e-10,...
'preprocess', 1,...
'presteps', 20,...
'sigma', 1e-4,...
'sigma1', 0.5,...
'sigma2', 2,...
'tmin', 1e-12,...
'TolFun', sqrt(eps),...
'watchdog', 1);
if nargin < 4
ub = inf(size(x));
if nargin < 3
lb = -inf(size(x));
end
end
if nargin < 5 || isempty(options) || ~isstruct(options)
options = defaultopt;
else
warning('off','catstruct:DuplicatesFound')
options = catstruct(defaultopt,options);
end
warning('off','MATLAB:rankDeficientMatrix')
switch options.Display
case {'off','none'}
verbosity = 0;
case {'iter','iter-detailed'}
verbosity = 2;
case {'final','final-detailed'}
verbosity = 1;
otherwise
verbosity = 0;
end
% parameter settings
eps1 = options.epsilon1;
eps2 = 0.5*options.TolFun^2;
null = options.null;
Big = options.Big;
% maximal number of iterations
kmax = options.MaxIter;
% choice of lambda
lambda1 = options.lambda1;
lambda2 = 1-lambda1;
% steplength parameters
beta = options.beta;
sigma = options.sigma;
tmin = options.tmin;
% parameters watchdog and nonmonotone line search; redefined later
m = options.m;
kwatch = options.kwatch;
watchdog = options.watchdog; % 1=watchdog strategy active, otherwise not
% parameters for preprocessor
preprocess = options.preprocess; % 1=preprocessor used, otherwise not
presteps = options.presteps; % maximum number of preprocessing steps
% trust-region parameters for preprocessor
delta = options.delta;
deltamin = options.deltamin;
sigma1 = options.sigma1;
sigma2 = options.sigma2;
eta = options.eta;
% initializations
k = 0;
k_main = 0;
% compute a feasible starting point by projection
x = max(lb,min(x,ub));
n = length(x);
OUTPUT.Dim = n;
% definition of index sets I_l, I_u and I_lu
Indexset = zeros(n,1);
I_l = lb>-Big & ub>Big;
I_u = lb<-Big & ub<Big;
I_lu = lb>-Big & ub<Big;
Indexset(I_l) = 1;
Indexset(I_u) = 2;
Indexset(I_lu) = 3;
% function evaluations
[Fx,DFx] = feval(FUN,x,varargin{:});
% choice of NCP-function and corresponding evaluations
Phix = Phi(x,Fx,lb,ub,lambda1,lambda2,n,Indexset);
normPhix = norm(Phix);
Psix = 0.5*(Phix'*Phix);
DPhix = DPhi(x,Fx,DFx,lb,ub,lambda1,lambda2,n,Indexset);
DPsix = DPhix'*Phix;
normDPsix = norm(DPsix);
% save initial values
x0 = x;
Phix0 = Phix;
Psix0 = Psix;
DPhix0 = DPhix;
DPsix0 = DPsix;
normDPsix0 = normDPsix;
% watchdog strategy
aux = zeros(m,1);
aux(1) = Psix;
MaxPsi = Psix;
if watchdog==1
kbest = k;
xbest = x;
Phibest = Phix;
Psibest = Psix;
DPhibest = DPhix;
DPsibest = DPsix;
normDPsibest = normDPsix;
end
% initial output
if verbosity > 1
fprintf(' k Psi(x) || DPsi(x) || stepsize\n');
disp('====================================================================')
disp('********************* Output at starting point *********************')
fprintf('%4.0f %24.5e %24.5e\n',k,Psix,normDPsix);
end
%% Preprocessor using local method
if preprocess==1
if verbosity > 1
disp('************************** Preprocessor ****************************')
end
normpLM=1;
while (k < presteps) && (Psix > eps2) && (normpLM>null)
k = k+1;
% choice of Levenberg-Marquardt parameter, note that we do not use
% the condition estimator for large-scale problems, although this
% may cause numerical problems in some examples
i = false;
mu = 0;
if n<100
i = true;
mu = 1e-16;
if condest(DPhix'*DPhix)>1e25
mu = 1e-6/(k+1);
end
end
if i
pLM = [DPhix; sqrt(mu)*speye(n)]\[-Phix; zeros(n,1)];
else
pLM = -DPhix\Phix;
end
normpLM = norm(pLM);
% compute the projected Levenberg-Marquard step onto box Xk
lbnew = max(min(lb-x,0),-delta);
ubnew = min(max(ub-x,0),delta);
d = max(lbnew,min(pLM,ubnew));
xnew = x+d;
% function evaluations etc.
[Fxnew,DFxnew] = feval(FUN,xnew,varargin{:});
Phixnew = Phi(xnew,Fxnew,lb,ub,lambda1,lambda2,n,Indexset);
Psixnew = 0.5*(Phixnew'*Phixnew);
normPhixnew = norm(Phixnew);
% update of delta
if normPhixnew<=eta*normPhix
delta = max(deltamin,sigma2*delta);
elseif normPhixnew>5*eta*normPhix
delta = max(deltamin,sigma1*delta);
end
% update
x = xnew;
Fx = Fxnew;
DFx = DFxnew;
Phix = Phixnew;
Psix = Psixnew;
normPhix = normPhixnew;
DPhix = DPhi(x,Fx,DFx,lb,ub,lambda1,lambda2,n,Indexset);
DPsix = DPhix'*Phix;
normDPsix = norm(DPsix,inf);
% output at each iteration
t=1;
if verbosity > 1
fprintf('%4.0f %24.5e %24.5e %11.7g\n',k,Psix,normDPsix,t);
end
end
end
% terminate program or redefine current iterate as original initial point
if preprocess==1 && Psix<eps2
if verbosity > 0
fprintf('Psix = %1.4e\nnormDPsix = %1.4e\n',Psix,normDPsix);
disp('Approximate solution found.')
end
EXITFLAG = 1;
FVAL = Fx;
OUTPUT.iterations = k;
OUTPUT.Psix = Psix;
OUTPUT.normDPsix = normDPsix;
JACOB = DFx;
return
elseif preprocess==1 && Psix>=eps2
x=x0;
Phix=Phix0;
Psix=Psix0;
DPhix=DPhix0;
DPsix=DPsix0;
if verbosity > 1
disp('******************** Restart with initial point ********************')
fprintf('%4.0f %24.5e %24.5e\n',k_main,Psix0,normDPsix0);
end
end
%% Main algorithm
if verbosity > 1
disp('************************** Main program ****************************')
end
while (k < kmax) && (Psix > eps2)
% choice of Levenberg-Marquardt parameter, note that we do not use
% the condition estimator for large-scale problems, although this
% may cause numerical problems in some examples
i = false;
if n<100
i = true;
mu = 1e-16;
if condest(DPhix'*DPhix)>1e25
mu = 1e-1/(k+1);
end
end
% compute a Levenberg-Marquard direction
if i
d = [DPhix; sqrt(mu)*speye(n)]\[-Phix; zeros(n,1)];
else
d = -DPhix\Phix;
end
% computation of steplength t using the nonmonotone Armijo-rule
% starting with the 6-th iteration
% computation of steplength t using the monotone Armijo-rule if
% d is a 'good' descent direction or k<=5
t = 1;
xnew = x+d;
Fxnew = feval(FUN,xnew,varargin{:});
Phixnew = Phi(xnew,Fxnew,lb,ub,lambda1,lambda2,n,Indexset);
Psixnew = 0.5*(Phixnew'*Phixnew);
const = sigma*DPsix'*d;
while (Psixnew > MaxPsi + const*t) && (t > tmin)
t = t*beta;
xnew = x+t*d;
Fxnew = feval(FUN,xnew,varargin{:});
Phixnew = Phi(xnew,Fxnew,lb,ub,lambda1,lambda2,n,Indexset);
Psixnew = 0.5*(Phixnew'*Phixnew);
end
% updatings
x = xnew;
Fx = Fxnew;
Phix = Phixnew;
Psix = Psixnew;
[~,DFx] = feval(FUN,x,varargin{:});
DPhix = DPhi(x,Fx,DFx,lb,ub,lambda1,lambda2,n,Indexset);
DPsix = DPhix'*Phix;
normDPsix = norm(DPsix);
k = k+1;
k_main = k_main+1;
if k_main<=5
aux(mod(k_main,m)+1) = Psix;
MaxPsi = Psix;
else
aux(mod(k_main,m)+1) = Psix;
MaxPsi = max(aux);
end
% updatings for the watchdog strategy
if watchdog ==1
if Psix<Psibest
kbest = k;
xbest = x;
Phibest = Phix;
Psibest = Psix;
DPhibest = DPhix;
DPsibest = DPsix;
normDPsibest = normDPsix;
elseif k-kbest>kwatch
x=xbest;
Phix=Phibest;
Psix=Psibest;
DPhix=DPhibest;
DPsix=DPsibest;
normDPsix=normDPsibest;
MaxPsi=Psix;
end
end
if verbosity > 1
% output at each iteration
fprintf('%4.0f %24.5e %24.5e %11.7g\n',k,Psix,normDPsix,t);
end
end
%% Final output
if Psix<=eps2
EXITFLAG = 1;
if verbosity > 0, disp('Approximate solution found.'); end
elseif k>=kmax
EXITFLAG = 0;
if verbosity > 0, disp('Maximum iteration number reached.'); end
elseif normDPsix<=eps1
EXITFLAG = -1; % Provisoire
if verbosity > 0, disp('Approximate stationary point found.'); end
else
EXITFLAG = -1; % Provisoire
if verbosity > 0, disp('No solution found.'); end
end
FVAL = Fx;
OUTPUT.iterations = k;
OUTPUT.Psix = Psix;
OUTPUT.normDPsix = normDPsix;
JACOB = DFx;
%% Subfunctions
function y = Phi(x,Fx,lb,ub,lambda1,lambda2,n,Indexset)
%% PHI
y = zeros(2*n,1);
phi_u = sqrt((ub-x).^2+Fx.^2)-ub+x+Fx;
LZ = false(n,1); % logical zero
I0 = Indexset==0;
y(I0) = -lambda1*Fx(I0);
y([LZ; I0]) = -lambda2*Fx(I0);
I1 = Indexset==1;
y(I1) = lambda1*(-x(I1)+lb(I1)-Fx(I1)+sqrt((x(I1)-lb(I1)).^2+Fx(I1).^2));
y([LZ; I1]) = lambda2*max(0,x(I1)-lb(I1)).*max(0,Fx(I1));
I2 = Indexset==2;
y(I2) = -lambda1*phi_u(I2);
y([LZ; I2]) = lambda2*max(0,ub(I2)-x(I2)).*max(0,-Fx(I2));
I3 = Indexset==3;
y(I3) = lambda1*(sqrt((x(I3)-lb(I3)).^2+phi_u(I3).^2)-x(I3)+lb(I3)-phi_u(I3));
y([LZ; I3]) = lambda2*(max(0,x(I3)-lb(I3)).*max(0,Fx(I3))+max(0,ub(I3)-x(I3)).*max(0,-Fx(I3)));
function H = DPhi(x,Fx,DFx,lb,ub,lambda1,lambda2,n,Indexset)
%% DPHI evaluates an element of the C-subdifferential of operator Phi
null = 1e-8;
beta_l = zeros(n,1);
beta_u = zeros(n,1);
alpha_l = zeros(n,1);
alpha_u = zeros(n,1);
z = zeros(n,1);
H2 = sparse(n,n);
I = abs(x-lb)<=null & abs(Fx)<=null;
beta_l(I) = 1;
z(I) = 1;
I = abs(ub-x)<=null & abs(Fx)<=null;
beta_u(I) = 1;
z(I) = 1;
I = x-lb>=-null & Fx>=-null;
alpha_l(I) = 1;
I = ub-x>=-null & Fx<=null;
alpha_u(I) = 1;
Da = zeros(n,1);
Db = zeros(n,1);
I = 1:n;
I0 = Indexset==0;
Da(I0) = 0;
Db(I0) = -1;
H2(I0,:) = -DFx(I0,:);
I1 = Indexset==1;
denom1 = zeros(n,1);
denom2 = zeros(n,1);
if any(I1)
denom1(I1) = max(null,sqrt((x(I1)-lb(I1)).^2+Fx(I1).^2));
denom2(I1) = max(null,sqrt(z(I1).^2+(DFx(I1,:)*z).^2));
end
I1b = Indexset==1 & beta_l==0;
Da(I1b) = (x(I1b)-lb(I1b))./denom1(I1b)-1;
Db(I1b) = Fx(I1b)./denom1(I1b)-1;
I1b = Indexset==1 & beta_l~=0;
if any(I1b)
Da(I1b) = z(I1b)./denom2(I1b)-1;
Db(I1b) = (DFx(I1b,:)*z)./denom2(I1b)-1;
end
I1a = I(Indexset==1 & alpha_l==1);
if any(I1a)
H2(I1a,:) = spdiags(x(I1a)-lb(I1a), 0, length(I1a), length(I1a))*DFx(I1a,:) +...
sparse(1:length(I1a),I1a,Fx(I1a),length(I1a),n,length(I1a));
end
I2 = Indexset==2;
denom1 = zeros(n,1);
denom2 = zeros(n,1);
if any(I2)
denom1(I2) = max(null,sqrt((ub(I2)-x(I2)).^2+Fx(I2).^2));
denom2(I2) = max(null,sqrt(z(I2).^2+(DFx(I2,:)*z).^2));
end
I2b = Indexset==2 & beta_u==0;
Da(I2b) = (ub(I2b)-x(I2b))./denom1(I2b)-1;
Db(I2b) = -Fx(I2b)./denom1(I2b)-1;
I2b = Indexset==2 & beta_u~=0;
if any(I2b)
Da(I2b) = -z(I2b)./denom2(I2b)-1;
Db(I2b) = -(DFx(I2b,:)*z)./denom2(I2b)-1;
end
I2a = I(Indexset==2 & alpha_u==1);
if any(I2a)
H2(I2a,:) = bsxfun(@times,x(I2a)-ub(I2a),DFx(I2a,:))+...
sparse(1:length(I2a),I2a,Fx(I2a),length(I2a),n,length(I2a));
end
I3 = Indexset==3;
phi = zeros(n,1);
ai = zeros(n,1);
bi = zeros(n,1);
ci = zeros(n,1);
di = zeros(n,1);
denom1 = zeros(n,1);
denom2 = zeros(n,1);
denom3 = zeros(n,1);
denom4 = zeros(n,1);
if any(I3)
phi(I3) = -ub(I3)+x(I3)+Fx(I3)+sqrt((ub(I3)-x(I3)).^2+Fx(I3).^2);
denom1(I3) = max(null,sqrt((x(I3)-lb(I3)).^2+phi(I3).^2));
denom2(I3) = max(null,sqrt(z(I3).^2+(DFx(I3,:)*z).^2));
denom3(I3) = max(null,sqrt((ub(I3)-x(I3)).^2+Fx(I3).^2));
denom4(I3) = max(null,sqrt(z(I3).^2));
end
I3bu = Indexset==3 & beta_u==0;
ci(I3bu) = (x(I3bu)-ub(I3bu))./denom3(I3bu)+1;
di(I3bu) = Fx(I3bu)./denom3(I3bu)+1;
I3bu = Indexset==3 & beta_u~=0;
if any(I3bu)
ci(I3bu) = 1+z(I3bu)./denom2(I3bu);
di(I3bu) = 1+(DFx(I3bu,:)*z)./denom2(I3bu);
end
I3bl = Indexset==3 & beta_l==0;
ai(I3bl) = (x(I3bl)-lb(I3bl))./denom1(I3bl)-1;
bi(I3bl) = phi(I3bl)./denom1(I3bl)-1;
I3bl = Indexset==3 & beta_l~=0;
if any(I3bl)
ai(I3bl) = z(I3bl)./denom4(I3bl)-1;
bi(I3bl) = (ci(I3bl).*z(I3bl)+(di(I3bl,ones(1,n)).*DFx(I3bl,:))*z)./denom4(I3bl)-1;
end
Da(I3) = ai(I3)+bi(I3).*ci(I3);
Db(I3) = bi(I3).*di(I3);
I3a = I(Indexset==3 & alpha_l==1 & alpha_u==1);
if any(I3a)
H2(I3a,:) = bsxfun(@times,-lb(I3a)-ub(I3a)+2*x(I3a),DFx(I3a,:))+...
2*sparse(1:length(I3a),I3a,Fx(I3a),length(I3a),n,length(I3a));
end
I3a = I(Indexset==3 & alpha_l==1 & alpha_u~=1);
if any(I3a)
H2(I3a,:) = bsxfun(@times,x(I3a)-lb(I3a),DFx(I3a,:))+...
sparse(1:length(I3a),I3a,Fx(I3a),length(I3a),n,length(I3a));
end
I3a = I(Indexset==3 & alpha_l~=1 & alpha_u==1);
if any(I3a)
H2(I3a,:) = bsxfun(@times,x(I3a)-ub(I3a),DFx(I3a,:))+...
sparse(1:length(I3a),I3a,Fx(I3a),length(I3a),n,length(I3a));
end
H1 = spdiags(Db,0,length(Db),length(Db))*DFx;
H1 = H1 + spdiags(Da, 0, length(Da), length(Da));
H = [lambda1*H1; lambda2*H2];