Mercurial > hg > camir-aes2014
diff 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 diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/toolboxes/distance_learning/mlr/util/lcprog.m Tue Feb 10 15:05:51 2015 +0000 @@ -0,0 +1,307 @@ +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]