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]