wolffd@0
|
1 function [z, w, status, z0] = lcprog(M, q, varargin)
|
wolffd@0
|
2 %LCPROG - Linear complementary programming with Lemke's algorithm.
|
wolffd@0
|
3 %
|
wolffd@0
|
4 % [z, w] = lcprog(M, q);
|
wolffd@0
|
5 % [z, w, status, z0] = lcprog(M, q, varargin);
|
wolffd@0
|
6 %
|
wolffd@0
|
7 % Inputs:
|
wolffd@0
|
8 % M - Matrix (n x n matrix)
|
wolffd@0
|
9 % q - Vector (n vector)
|
wolffd@0
|
10 %
|
wolffd@0
|
11 % Optional Inputs:
|
wolffd@0
|
12 % Optional name-value pairs are:
|
wolffd@0
|
13 % 'maxiter' Maximum number of iterations before termination of algorithm (default is (1+n)^3).
|
wolffd@0
|
14 % 'tiebreak' Method to break ties for pivot selection (default is 'first'). Options are:
|
wolffd@0
|
15 % 'first' Select pivot with lowest index.
|
wolffd@0
|
16 % 'last' Select pivot with highest index.
|
wolffd@0
|
17 % 'random' Select a pivot at random.
|
wolffd@0
|
18 % 'tolpiv' Pivot tolerance below which a number is considered to be zero (default is 1.0e-9).
|
wolffd@0
|
19 %
|
wolffd@0
|
20 % Outputs:
|
wolffd@0
|
21 % z - Solution vector (n vector)
|
wolffd@0
|
22 % w - Complementary solution vector (n vector)
|
wolffd@0
|
23 % status - Status of optimization with values (integer)
|
wolffd@0
|
24 % 6 Ray termination
|
wolffd@0
|
25 % 1 Convergence to a solution (normal termination)
|
wolffd@0
|
26 % 0 Termination prior to convergence
|
wolffd@0
|
27 % -2 Infeasible problem
|
wolffd@0
|
28 % -3 Bad pivot to an infeasible solution
|
wolffd@0
|
29 % z0 - Artificial variable which is 0 upon normal termination (scalar)
|
wolffd@0
|
30 %
|
wolffd@0
|
31 % Comments:
|
wolffd@0
|
32 % Given a matrix M and a vector q, the linear complementary programming (LCP) problem seeks an
|
wolffd@0
|
33 % orthogonal pair of non-negative vectors w and z such that
|
wolffd@0
|
34 % w = M * z + q
|
wolffd@0
|
35 % with
|
wolffd@0
|
36 % w' * z = 0
|
wolffd@0
|
37 % w >= 0
|
wolffd@0
|
38 % z >= 0 .
|
wolffd@0
|
39 %
|
wolffd@0
|
40 % The algorithm starts with the problem
|
wolffd@0
|
41 % w = M * z + q + z0 * 1
|
wolffd@0
|
42 % with an artificial variable z0 > 0 such that q + z0*1 >= 0. The algorithm goes through a series
|
wolffd@0
|
43 % of pivot steps that seeks to drive z0 to 0. The algorithm can stop in two ways with either
|
wolffd@0
|
44 % normal termination or ray termination.
|
wolffd@0
|
45 %
|
wolffd@0
|
46 % With normal termination, the artificial variable z0 is zero so that the original problem is
|
wolffd@0
|
47 % solved.
|
wolffd@0
|
48 %
|
wolffd@0
|
49 % With ray termination, the artificial variable z0 is non-negative with "ray" solution in the form
|
wolffd@0
|
50 % w = wR + lambda * dw
|
wolffd@0
|
51 % z = zR + lambda * dz
|
wolffd@0
|
52 % z0 = z0R + lambda * dz0
|
wolffd@0
|
53 % that satisfies
|
wolffd@0
|
54 % w = M * z + q + z0 * 1
|
wolffd@0
|
55 % w' * z = 0
|
wolffd@0
|
56 % for any lambda >= 0. If ray termination occurs, the returned variables w, z, and z0 are the
|
wolffd@0
|
57 % points on the ray with lambda = 0.
|
wolffd@0
|
58 %
|
wolffd@0
|
59 % If tolpiv is too small, the algorithm may identify a pivot that is effectively zero. If this
|
wolffd@0
|
60 % situation occurs, the status code -3 is triggered. Try a larger pivot tolerance 'tolpiv' although
|
wolffd@0
|
61 % a pivot that is too large can trigger false convergence due to failure to find a pivot.
|
wolffd@0
|
62 %
|
wolffd@0
|
63
|
wolffd@0
|
64 %
|
wolffd@0
|
65 % Copyright 2007 The MathWorks, Inc.
|
wolffd@0
|
66 % $Revision: 1.1.6.4 $ $ Date: $
|
wolffd@0
|
67
|
wolffd@0
|
68 % initialization
|
wolffd@0
|
69
|
wolffd@0
|
70 if nargin < 2 || isempty(M) || isempty(q)
|
wolffd@0
|
71 error('finance:lcprog:MissingInputArgument', ...
|
wolffd@0
|
72 'One or more missing required input arguments M, q.');
|
wolffd@0
|
73 end
|
wolffd@0
|
74
|
wolffd@0
|
75 q = q(:);
|
wolffd@0
|
76 n = size(q,1);
|
wolffd@0
|
77
|
wolffd@0
|
78 % process name-value pairs (if any)
|
wolffd@0
|
79
|
wolffd@0
|
80 if mod(nargin-2,2) ~= 0
|
wolffd@0
|
81 error('finance:lcprog:InvalidNameValuePair', ...
|
wolffd@0
|
82 'Invalid name-value pairs with either a missing name or value.');
|
wolffd@0
|
83 end
|
wolffd@0
|
84
|
wolffd@0
|
85 names = { 'maxiter', 'tiebreak', 'tolpiv' }; % names
|
wolffd@0
|
86 values = { (1 + n)^3, 'first', 1.0e-9 }; % default values
|
wolffd@0
|
87 try
|
wolffd@0
|
88 [maxiter, tiebreak, tolpiv] = parsepvpairs(names, values, varargin{:});
|
wolffd@0
|
89
|
wolffd@0
|
90 catch E
|
wolffd@0
|
91 E.throw
|
wolffd@0
|
92 end
|
wolffd@0
|
93
|
wolffd@0
|
94 % check optional arguments
|
wolffd@0
|
95
|
wolffd@0
|
96 if maxiter <= 0
|
wolffd@0
|
97 error('finance:lcprog:NonPositiveInteger', ...
|
wolffd@0
|
98 'Maximum number of iterations (maxiter) must be a positive integer.');
|
wolffd@0
|
99 end
|
wolffd@0
|
100
|
wolffd@0
|
101 if ~isempty(strmatch(tiebreak,{'default','first'}))
|
wolffd@0
|
102 tb = 1;
|
wolffd@0
|
103 elseif ~isempty(strmatch(tiebreak,'last'))
|
wolffd@0
|
104 tb = 2;
|
wolffd@0
|
105 elseif ~isempty(strmatch(tiebreak,'random'))
|
wolffd@0
|
106 tb = 3;
|
wolffd@0
|
107 else
|
wolffd@0
|
108 tb = 1;
|
wolffd@0
|
109 end
|
wolffd@0
|
110
|
wolffd@0
|
111 if tolpiv < 2*eps
|
wolffd@0
|
112 error('finance:lcprog:InvalidTolerance', ...
|
wolffd@0
|
113 'Unrealistically small tolerance (tolpiv) specified.');
|
wolffd@0
|
114 end
|
wolffd@0
|
115
|
wolffd@0
|
116 % trivial solution
|
wolffd@0
|
117
|
wolffd@0
|
118 if all(q >= 0)
|
wolffd@0
|
119 z = zeros(n,1);
|
wolffd@0
|
120 w = q;
|
wolffd@0
|
121 status = 1;
|
wolffd@0
|
122 z0 = 0;
|
wolffd@0
|
123 return
|
wolffd@0
|
124 end
|
wolffd@0
|
125
|
wolffd@0
|
126 % set up general problem
|
wolffd@0
|
127
|
wolffd@0
|
128 w = zeros(n,1);
|
wolffd@0
|
129 z = zeros(n,1);
|
wolffd@0
|
130 u = ones(n,1);
|
wolffd@0
|
131 z0 = max(-q);
|
wolffd@0
|
132
|
wolffd@0
|
133 tableau = [ eye(n), -M, -u, q];
|
wolffd@0
|
134 basic = (1:n)';
|
wolffd@0
|
135
|
wolffd@0
|
136 % initial pivot and initial basis
|
wolffd@0
|
137
|
wolffd@0
|
138 r = find(q == min(q),1); % r are pivot rows
|
wolffd@0
|
139 cx = r; % w(r) leaves the basis
|
wolffd@0
|
140 c = 2*n + 1; % initial pivot column
|
wolffd@0
|
141 basic(r) = c; % z0 enters the basis
|
wolffd@0
|
142
|
wolffd@0
|
143 % pivot step
|
wolffd@0
|
144
|
wolffd@0
|
145 pr = (1/tableau(r,c)) * tableau(r,:);
|
wolffd@0
|
146 pc = tableau(:,c);
|
wolffd@0
|
147 tableau = tableau - pc * pr;
|
wolffd@0
|
148 tableau(r,:) = pr;
|
wolffd@0
|
149
|
wolffd@0
|
150 % main loop
|
wolffd@0
|
151
|
wolffd@0
|
152 for iter = 2:maxiter
|
wolffd@0
|
153
|
wolffd@0
|
154 % get new basic variable
|
wolffd@0
|
155
|
wolffd@0
|
156 if cx <= n
|
wolffd@0
|
157 c = n + cx;
|
wolffd@0
|
158 elseif cx <= 2*n
|
wolffd@0
|
159 c = cx - n;
|
wolffd@0
|
160 end
|
wolffd@0
|
161
|
wolffd@0
|
162 % do ratio test
|
wolffd@0
|
163
|
wolffd@0
|
164 d = tableau(:,c);
|
wolffd@0
|
165 mind = NaN(n,1);
|
wolffd@0
|
166 for i = 1:n
|
wolffd@0
|
167 if d(i) > tolpiv
|
wolffd@0
|
168 mind(i) = tableau(i,end)/d(i);
|
wolffd@0
|
169 end
|
wolffd@0
|
170 end
|
wolffd@0
|
171 iind = find(mind == nanmin(mind));
|
wolffd@0
|
172
|
wolffd@0
|
173 % ray termination
|
wolffd@0
|
174
|
wolffd@0
|
175 if isempty(iind) % cannot find a new basic variable
|
wolffd@0
|
176 for i = 1:n
|
wolffd@0
|
177 if basic(i) <= n
|
wolffd@0
|
178 ii = basic(i);
|
wolffd@0
|
179 w(ii) = tableau(i,end);
|
wolffd@0
|
180 z(ii) = 0;
|
wolffd@0
|
181 elseif basic(i) <= 2*n
|
wolffd@0
|
182 ii = basic(i) - n;
|
wolffd@0
|
183 w(ii) = 0;
|
wolffd@0
|
184 z(ii) = tableau(i,end);
|
wolffd@0
|
185 else
|
wolffd@0
|
186 z0 = tableau(i,end);
|
wolffd@0
|
187 end
|
wolffd@0
|
188 end
|
wolffd@0
|
189 if lcprealitycheck(M, q, w, z)
|
wolffd@0
|
190 status = -3;
|
wolffd@0
|
191 z = NaN(n,1);
|
wolffd@0
|
192 w = NaN(n,1);
|
wolffd@0
|
193 z0 = NaN;
|
wolffd@0
|
194 else
|
wolffd@0
|
195 status = 6;
|
wolffd@0
|
196 end
|
wolffd@0
|
197 return
|
wolffd@0
|
198 end
|
wolffd@0
|
199
|
wolffd@0
|
200 % identify next basic variable to be replaced using ratio test
|
wolffd@0
|
201
|
wolffd@0
|
202 if tb == 3
|
wolffd@0
|
203 ii = ceil(rand()*numel(iind));
|
wolffd@0
|
204 if ii < 1
|
wolffd@0
|
205 ii = 1;
|
wolffd@0
|
206 end
|
wolffd@0
|
207 r = iind(ii);
|
wolffd@0
|
208 elseif tb == 2
|
wolffd@0
|
209 r = iind(end);
|
wolffd@0
|
210 elseif tb == 1
|
wolffd@0
|
211 r = iind(1);
|
wolffd@0
|
212 end
|
wolffd@0
|
213 for i = 1:numel(iind) % always select z0 if it is in the index set
|
wolffd@0
|
214 if basic(iind(i)) == (2*n + 1)
|
wolffd@0
|
215 r = iind(i);
|
wolffd@0
|
216 end
|
wolffd@0
|
217 end
|
wolffd@0
|
218
|
wolffd@0
|
219 % new basis
|
wolffd@0
|
220
|
wolffd@0
|
221 cx = basic(r); % get variable leaving the basis
|
wolffd@0
|
222 basic(r) = c; % move new basic variable into the basis
|
wolffd@0
|
223
|
wolffd@0
|
224 % pivot step
|
wolffd@0
|
225
|
wolffd@0
|
226 pr = (1/tableau(r,c)) * tableau(r,:);
|
wolffd@0
|
227 pc = tableau(:,c);
|
wolffd@0
|
228 tableau = tableau - pc * pr;
|
wolffd@0
|
229 tableau(r,:) = pr;
|
wolffd@0
|
230
|
wolffd@0
|
231 if ~isfinite(tableau(1,end)) % trap overflow/underflow
|
wolffd@0
|
232 warning('finance:lcprog:InfeasibleProblem', ...
|
wolffd@0
|
233 'Tableau overflow/underflow due to bad pivot.');
|
wolffd@0
|
234 status = -3;
|
wolffd@0
|
235 z = NaN(n,1);
|
wolffd@0
|
236 w = NaN(n,1);
|
wolffd@0
|
237 z0 = NaN;
|
wolffd@0
|
238 return
|
wolffd@0
|
239 end
|
wolffd@0
|
240
|
wolffd@0
|
241 % convergence test
|
wolffd@0
|
242
|
wolffd@0
|
243 if all(basic <= 2*n) % test if z0 left the basis -> convergence
|
wolffd@0
|
244 for i = 1:n
|
wolffd@0
|
245 if basic(i) <= n
|
wolffd@0
|
246 ii = basic(i);
|
wolffd@0
|
247 w(ii) = tableau(i,end);
|
wolffd@0
|
248 z(ii) = 0;
|
wolffd@0
|
249 else
|
wolffd@0
|
250 ii = basic(i) - n;
|
wolffd@0
|
251 w(ii) = 0;
|
wolffd@0
|
252 z(ii) = tableau(i,end);
|
wolffd@0
|
253 end
|
wolffd@0
|
254 end
|
wolffd@0
|
255 z0 = 0;
|
wolffd@0
|
256 if lcprealitycheck(M, q, w, z)
|
wolffd@0
|
257 status = -3;
|
wolffd@0
|
258 z = NaN(n,1);
|
wolffd@0
|
259 w = NaN(n,1);
|
wolffd@0
|
260 z0 = NaN;
|
wolffd@0
|
261 else
|
wolffd@0
|
262 status = 1;
|
wolffd@0
|
263 end
|
wolffd@0
|
264 return
|
wolffd@0
|
265 end
|
wolffd@0
|
266 end
|
wolffd@0
|
267
|
wolffd@0
|
268 % too many iterations
|
wolffd@0
|
269
|
wolffd@0
|
270 for i = 1:n
|
wolffd@0
|
271 if basic(i) <= n
|
wolffd@0
|
272 ii = basic(i);
|
wolffd@0
|
273 w(ii) = tableau(i,end);
|
wolffd@0
|
274 z(ii) = 0;
|
wolffd@0
|
275 elseif basic(i) <= 2*n
|
wolffd@0
|
276 ii = basic(i) - n;
|
wolffd@0
|
277 w(ii) = 0;
|
wolffd@0
|
278 z(ii) = tableau(i,end);
|
wolffd@0
|
279 else
|
wolffd@0
|
280 z0 = tableau(i,end);
|
wolffd@0
|
281 end
|
wolffd@0
|
282 end
|
wolffd@0
|
283 if lcprealitycheck(M, q, w, z)
|
wolffd@0
|
284 status = -3;
|
wolffd@0
|
285 z = NaN(n,1);
|
wolffd@0
|
286 w = NaN(n,1);
|
wolffd@0
|
287 z0 = NaN;
|
wolffd@0
|
288 else
|
wolffd@0
|
289 status = 0;
|
wolffd@0
|
290 end
|
wolffd@0
|
291
|
wolffd@0
|
292
|
wolffd@0
|
293 function notok = lcprealitycheck(M, q, w, z)
|
wolffd@0
|
294 %LCPREALITYCHECK - Make sure candidate solution z and w is complementary basic feasible.
|
wolffd@0
|
295
|
wolffd@0
|
296 u = w - M*z - q;
|
wolffd@0
|
297
|
wolffd@0
|
298 if (max(u) - min(u)) > 1
|
wolffd@0
|
299 warning('finance:lcprog:InfeasibleProblem', ...
|
wolffd@0
|
300 'Candidate solution is infeasible due to a bad pivot.');
|
wolffd@0
|
301 notok = true;
|
wolffd@0
|
302 else
|
wolffd@0
|
303 notok = false;
|
wolffd@0
|
304 end
|
wolffd@0
|
305
|
wolffd@0
|
306
|
wolffd@0
|
307 % [EOF]
|