Mercurial > hg > camir-aes2014
view toolboxes/distance_learning/mlr/util/lcprog.m @ 0:e9a9cd732c1e tip
first hg version after svn
author | wolffd |
---|---|
date | Tue, 10 Feb 2015 15:05:51 +0000 |
parents | |
children |
line wrap: on
line source
function [z, w, status, z0] = lcprog(M, q, varargin) %LCPROG - Linear complementary programming with Lemke's algorithm. % % [z, w] = lcprog(M, q); % [z, w, status, z0] = lcprog(M, q, varargin); % % Inputs: % M - Matrix (n x n matrix) % q - Vector (n vector) % % Optional Inputs: % Optional name-value pairs are: % 'maxiter' Maximum number of iterations before termination of algorithm (default is (1+n)^3). % 'tiebreak' Method to break ties for pivot selection (default is 'first'). Options are: % 'first' Select pivot with lowest index. % 'last' Select pivot with highest index. % 'random' Select a pivot at random. % 'tolpiv' Pivot tolerance below which a number is considered to be zero (default is 1.0e-9). % % Outputs: % z - Solution vector (n vector) % w - Complementary solution vector (n vector) % status - Status of optimization with values (integer) % 6 Ray termination % 1 Convergence to a solution (normal termination) % 0 Termination prior to convergence % -2 Infeasible problem % -3 Bad pivot to an infeasible solution % z0 - Artificial variable which is 0 upon normal termination (scalar) % % Comments: % Given a matrix M and a vector q, the linear complementary programming (LCP) problem seeks an % orthogonal pair of non-negative vectors w and z such that % w = M * z + q % with % w' * z = 0 % w >= 0 % z >= 0 . % % The algorithm starts with the problem % w = M * z + q + z0 * 1 % with an artificial variable z0 > 0 such that q + z0*1 >= 0. The algorithm goes through a series % of pivot steps that seeks to drive z0 to 0. The algorithm can stop in two ways with either % normal termination or ray termination. % % With normal termination, the artificial variable z0 is zero so that the original problem is % solved. % % With ray termination, the artificial variable z0 is non-negative with "ray" solution in the form % w = wR + lambda * dw % z = zR + lambda * dz % z0 = z0R + lambda * dz0 % that satisfies % w = M * z + q + z0 * 1 % w' * z = 0 % for any lambda >= 0. If ray termination occurs, the returned variables w, z, and z0 are the % points on the ray with lambda = 0. % % If tolpiv is too small, the algorithm may identify a pivot that is effectively zero. If this % situation occurs, the status code -3 is triggered. Try a larger pivot tolerance 'tolpiv' although % a pivot that is too large can trigger false convergence due to failure to find a pivot. % % % Copyright 2007 The MathWorks, Inc. % $Revision: 1.1.6.4 $ $ Date: $ % initialization if nargin < 2 || isempty(M) || isempty(q) error('finance:lcprog:MissingInputArgument', ... 'One or more missing required input arguments M, q.'); end q = q(:); n = size(q,1); % process name-value pairs (if any) if mod(nargin-2,2) ~= 0 error('finance:lcprog:InvalidNameValuePair', ... 'Invalid name-value pairs with either a missing name or value.'); end names = { 'maxiter', 'tiebreak', 'tolpiv' }; % names values = { (1 + n)^3, 'first', 1.0e-9 }; % default values try [maxiter, tiebreak, tolpiv] = parsepvpairs(names, values, varargin{:}); catch E E.throw end % check optional arguments if maxiter <= 0 error('finance:lcprog:NonPositiveInteger', ... 'Maximum number of iterations (maxiter) must be a positive integer.'); end if ~isempty(strmatch(tiebreak,{'default','first'})) tb = 1; elseif ~isempty(strmatch(tiebreak,'last')) tb = 2; elseif ~isempty(strmatch(tiebreak,'random')) tb = 3; else tb = 1; end if tolpiv < 2*eps error('finance:lcprog:InvalidTolerance', ... 'Unrealistically small tolerance (tolpiv) specified.'); end % trivial solution if all(q >= 0) z = zeros(n,1); w = q; status = 1; z0 = 0; return end % set up general problem w = zeros(n,1); z = zeros(n,1); u = ones(n,1); z0 = max(-q); tableau = [ eye(n), -M, -u, q]; basic = (1:n)'; % initial pivot and initial basis r = find(q == min(q),1); % r are pivot rows cx = r; % w(r) leaves the basis c = 2*n + 1; % initial pivot column basic(r) = c; % z0 enters the basis % pivot step pr = (1/tableau(r,c)) * tableau(r,:); pc = tableau(:,c); tableau = tableau - pc * pr; tableau(r,:) = pr; % main loop for iter = 2:maxiter % get new basic variable if cx <= n c = n + cx; elseif cx <= 2*n c = cx - n; end % do ratio test d = tableau(:,c); mind = NaN(n,1); for i = 1:n if d(i) > tolpiv mind(i) = tableau(i,end)/d(i); end end iind = find(mind == nanmin(mind)); % ray termination if isempty(iind) % cannot find a new basic variable for i = 1:n if basic(i) <= n ii = basic(i); w(ii) = tableau(i,end); z(ii) = 0; elseif basic(i) <= 2*n ii = basic(i) - n; w(ii) = 0; z(ii) = tableau(i,end); else z0 = tableau(i,end); end end if lcprealitycheck(M, q, w, z) status = -3; z = NaN(n,1); w = NaN(n,1); z0 = NaN; else status = 6; end return end % identify next basic variable to be replaced using ratio test if tb == 3 ii = ceil(rand()*numel(iind)); if ii < 1 ii = 1; end r = iind(ii); elseif tb == 2 r = iind(end); elseif tb == 1 r = iind(1); end for i = 1:numel(iind) % always select z0 if it is in the index set if basic(iind(i)) == (2*n + 1) r = iind(i); end end % new basis cx = basic(r); % get variable leaving the basis basic(r) = c; % move new basic variable into the basis % pivot step pr = (1/tableau(r,c)) * tableau(r,:); pc = tableau(:,c); tableau = tableau - pc * pr; tableau(r,:) = pr; if ~isfinite(tableau(1,end)) % trap overflow/underflow warning('finance:lcprog:InfeasibleProblem', ... 'Tableau overflow/underflow due to bad pivot.'); status = -3; z = NaN(n,1); w = NaN(n,1); z0 = NaN; return end % convergence test if all(basic <= 2*n) % test if z0 left the basis -> convergence for i = 1:n if basic(i) <= n ii = basic(i); w(ii) = tableau(i,end); z(ii) = 0; else ii = basic(i) - n; w(ii) = 0; z(ii) = tableau(i,end); end end z0 = 0; if lcprealitycheck(M, q, w, z) status = -3; z = NaN(n,1); w = NaN(n,1); z0 = NaN; else status = 1; end return end end % too many iterations for i = 1:n if basic(i) <= n ii = basic(i); w(ii) = tableau(i,end); z(ii) = 0; elseif basic(i) <= 2*n ii = basic(i) - n; w(ii) = 0; z(ii) = tableau(i,end); else z0 = tableau(i,end); end end if lcprealitycheck(M, q, w, z) status = -3; z = NaN(n,1); w = NaN(n,1); z0 = NaN; else status = 0; end function notok = lcprealitycheck(M, q, w, z) %LCPREALITYCHECK - Make sure candidate solution z and w is complementary basic feasible. u = w - M*z - q; if (max(u) - min(u)) > 1 warning('finance:lcprog:InfeasibleProblem', ... 'Candidate solution is infeasible due to a bad pivot.'); notok = true; else notok = false; end % [EOF]