Mercurial > hg > pycsalgos
comparison pyCSalgos/BP/l1qec.py @ 37:afcfd4d1d548
17.11.2011 Lots of stuff:
Implemented l1qec() (variant of l1 minimization with both quadratic and equality constraints - no ABS, no lambda)
Implemented SL0a2() (variant of SL0a approximate recovery with both quadratic and equality constraints - no ABS, no lambda)
Fixed HUGE bug: was running SL0 instead of BP!!!
author | nikcleju |
---|---|
date | Thu, 17 Nov 2011 17:29:54 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
36:539b21849166 | 37:afcfd4d1d548 |
---|---|
1 # -*- coding: utf-8 -*- | |
2 """ | |
3 Created on Thu Nov 17 15:47:36 2011 | |
4 | |
5 Solve l1 minimization with quadratic AND equality constraints | |
6 | |
7 @author: ncleju | |
8 """ | |
9 | |
10 | |
11 import numpy as np | |
12 import scipy.linalg | |
13 import math | |
14 | |
15 class l1qecInputValueError(Exception): | |
16 pass | |
17 | |
18 # This is not normally used, so it is not tested, probably doesn't work | |
19 def cgsolve(A, b, tol, maxiter, verbose=1): | |
20 raise Exception('Shouldn\'t run cgsolve(), as this is absolutely not tested. Comment this if you really want to proceed.') | |
21 | |
22 | |
23 #if (nargin < 5), verbose = 1; end | |
24 # Optional argument | |
25 | |
26 #implicit = isa(A,'function_handle'); | |
27 if hasattr(A, '__call__'): | |
28 implicit = True | |
29 else: | |
30 implicit = False | |
31 | |
32 x = np.zeros(b.size) | |
33 r = b.copy() | |
34 d = r.copy() | |
35 delta = np.vdot(r,r) | |
36 delta0 = np.vdot(b,b) | |
37 numiter = 0 | |
38 bestx = x.copy() | |
39 bestres = math.sqrt(delta/delta0) | |
40 while (numiter < maxiter) and (delta > tol**2*delta0): | |
41 | |
42 # q = A*d | |
43 #if (implicit), q = A(d); else q = A*d; end | |
44 if implicit: | |
45 q = A(d) | |
46 else: | |
47 q = np.dot(A,d) | |
48 | |
49 alpha = delta/np.vdot(d,q) | |
50 x = x + alpha*d | |
51 | |
52 if divmod(numiter+1,50)[1] == 0: | |
53 # r = b - Aux*x | |
54 #if (implicit), r = b - A(x); else r = b - A*x; end | |
55 if implicit: | |
56 r = b - A(x) | |
57 else: | |
58 r = b - np.dot(A,x) | |
59 else: | |
60 r = r - alpha*q | |
61 #end | |
62 | |
63 deltaold = delta; | |
64 delta = np.vdot(r,r) | |
65 beta = delta/deltaold; | |
66 d = r + beta*d | |
67 numiter = numiter + 1 | |
68 if (math.sqrt(delta/delta0) < bestres): | |
69 bestx = x.copy() | |
70 bestres = math.sqrt(delta/delta0) | |
71 #end | |
72 | |
73 if ((verbose) and (divmod(numiter,verbose)[1]==0)): | |
74 #disp(sprintf('cg: Iter = #d, Best residual = #8.3e, Current residual = #8.3e', ... | |
75 # numiter, bestres, sqrt(delta/delta0))); | |
76 print 'cg: Iter = ',numiter,', Best residual = ',bestres,', Current residual = ',math.sqrt(delta/delta0) | |
77 #end | |
78 | |
79 #end | |
80 | |
81 if (verbose): | |
82 #disp(sprintf('cg: Iterations = #d, best residual = #14.8e', numiter, bestres)); | |
83 print 'cg: Iterations = ',numiter,', best residual = ',bestres | |
84 #end | |
85 x = bestx.copy() | |
86 res = bestres | |
87 iter = numiter | |
88 | |
89 return x,res,iter | |
90 | |
91 | |
92 | |
93 def l1qec_newton(x0, u0, A, At, b, epsilon, Aexact, tau, newtontol, newtonmaxiter, cgtol, cgmaxiter, verbose=False): | |
94 | |
95 # check if the matrix A is implicit or explicit | |
96 #largescale = isa(A,'function_handle'); | |
97 if hasattr(A, '__call__'): | |
98 largescale = True | |
99 else: | |
100 largescale = False | |
101 | |
102 # line search parameters | |
103 alpha = 0.01 | |
104 beta = 0.5 | |
105 | |
106 #if (~largescale), AtA = A'*A; end | |
107 if not largescale: | |
108 AtA = np.dot(A.T,A) | |
109 | |
110 # initial point | |
111 x = x0.copy() | |
112 u = u0.copy() | |
113 #if (largescale), r = A(x) - b; else r = A*x - b; end | |
114 if largescale: | |
115 r = A(x) - b | |
116 else: | |
117 r = np.dot(A,x) - b | |
118 | |
119 fu1 = x - u | |
120 fu2 = -x - u | |
121 fe = 1.0/2*(np.vdot(r,r) - epsilon**2) | |
122 f = u.sum() - (1.0/tau)*(np.log(-fu1).sum() + np.log(-fu2).sum() + math.log(-fe)) | |
123 | |
124 niter = 0 | |
125 done = 0 | |
126 while not done: | |
127 | |
128 #if (largescale), atr = At(r); else atr = A'*r; end | |
129 if largescale: | |
130 atr = At(r) | |
131 else: | |
132 atr = np.dot(A.T,r) | |
133 | |
134 #ntgz = 1./fu1 - 1./fu2 + 1/fe*atr; | |
135 ntgz = 1.0/fu1 - 1.0/fu2 + 1.0/fe*atr | |
136 #ntgu = -tau - 1./fu1 - 1./fu2; | |
137 ntgu = -tau - 1.0/fu1 - 1.0/fu2 | |
138 #gradf = -(1/tau)*[ntgz; ntgu]; | |
139 gradf = -(1.0/tau)*np.concatenate((ntgz, ntgu),0) | |
140 | |
141 #sig11 = 1./fu1.^2 + 1./fu2.^2; | |
142 sig11 = 1.0/(fu1**2) + 1.0/(fu2**2) | |
143 #sig12 = -1./fu1.^2 + 1./fu2.^2; | |
144 sig12 = -1.0/(fu1**2) + 1.0/(fu2**2) | |
145 #sigx = sig11 - sig12.^2./sig11; | |
146 sigx = sig11 - (sig12**2)/sig11 | |
147 | |
148 #w1p = ntgz - sig12./sig11.*ntgu; | |
149 w1p = ntgz - sig12/sig11*ntgu | |
150 if largescale: | |
151 #h11pfun = @(z) sigx.*z - (1/fe)*At(A(z)) + 1/fe^2*(atr'*z)*atr; | |
152 h11pfun = lambda z: sigx*z - (1.0/fe)*At(A(z)) + 1.0/(fe**2)*np.dot(np.dot(atr.T,z),atr) | |
153 dx,cgres,cgiter = cgsolve(h11pfun, w1p, cgtol, cgmaxiter, 0) | |
154 if (cgres > 1.0/2): | |
155 if verbose: | |
156 print 'Cannot solve system. Returning previous iterate. (See Section 4 of notes for more information.)' | |
157 xp = x.copy() | |
158 up = u.copy() | |
159 return xp,up,niter | |
160 #end | |
161 Adx = A(dx) | |
162 else: | |
163 #H11p = diag(sigx) - (1/fe)*AtA + (1/fe)^2*atr*atr'; | |
164 # Attention: atr is column vector, so atr*atr' means outer(atr,atr) | |
165 H11p = np.diag(sigx) - (1.0/fe)*AtA + (1.0/fe)**2*np.outer(atr,atr) | |
166 #opts.POSDEF = true; opts.SYM = true; | |
167 #[dx,hcond] = linsolve(H11p, w1p, opts); | |
168 #if (hcond < 1e-14) | |
169 # disp('Matrix ill-conditioned. Returning previous iterate. (See Section 4 of notes for more information.)'); | |
170 # xp = x; up = u; | |
171 # return | |
172 #end | |
173 | |
174 # Nic says: from tveq_newton.m | |
175 K = Aexact.shape[0] | |
176 afac = (np.diag(H11p)).max() | |
177 #Hp = [H11p afac*A'; afac*A zeros(K)]) | |
178 Hp = np.vstack(( np.hstack((H11p,afac*Aexact.T)) , np.hstack((afac*Aexact,np.zeros((K,K)))) )) | |
179 wp = np.concatenate((w1p , np.zeros(K))) | |
180 try: | |
181 #dx = scipy.linalg.solve(H11p, w1p, sym_pos=True) | |
182 #hcond = 1.0/np.linalg.cond(H11p) | |
183 dxv = scipy.linalg.solve(Hp, wp, sym_pos=False) # Only symmetric, not posdef | |
184 dx = dxv[:x0.size] | |
185 hcond = 1.0/np.linalg.cond(Hp) | |
186 except scipy.linalg.LinAlgError: | |
187 if verbose: | |
188 print 'Matrix ill-conditioned. Returning previous iterate. (See Section 4 of notes for more information.)' | |
189 xp = x.copy() | |
190 up = u.copy() | |
191 return xp,up,niter | |
192 if hcond < 1e-14: | |
193 if verbose: | |
194 print 'Matrix ill-conditioned. Returning previous iterate. (See Section 4 of notes for more information.)' | |
195 xp = x.copy() | |
196 up = u.copy() | |
197 return xp,up,niter | |
198 | |
199 #Adx = A*dx; | |
200 Adx = np.dot(A,dx) | |
201 #end | |
202 #du = (1./sig11).*ntgu - (sig12./sig11).*dx; | |
203 du = (1.0/sig11)*ntgu - (sig12/sig11)*dx; | |
204 | |
205 # minimum step size that stays in the interior | |
206 #ifu1 = find((dx-du) > 0); ifu2 = find((-dx-du) > 0); | |
207 ifu1 = np.nonzero((dx-du)>0) | |
208 ifu2 = np.nonzero((-dx-du)>0) | |
209 #aqe = Adx'*Adx; bqe = 2*r'*Adx; cqe = r'*r - epsilon^2; | |
210 aqe = np.dot(Adx.T,Adx) | |
211 bqe = 2*np.dot(r.T,Adx) | |
212 cqe = np.vdot(r,r) - epsilon**2 | |
213 #smax = min(1,min([... | |
214 # -fu1(ifu1)./(dx(ifu1)-du(ifu1)); -fu2(ifu2)./(-dx(ifu2)-du(ifu2)); ... | |
215 # (-bqe+sqrt(bqe^2-4*aqe*cqe))/(2*aqe) | |
216 # ])); | |
217 smax = min(1,np.concatenate( (-fu1[ifu1]/(dx[ifu1]-du[ifu1]) , -fu2[ifu2]/(-dx[ifu2]-du[ifu2]) , np.array([ (-bqe + math.sqrt(bqe**2-4*aqe*cqe))/(2*aqe) ]) ) , 0).min()) | |
218 | |
219 s = 0.99 * smax | |
220 | |
221 # backtracking line search | |
222 suffdec = 0 | |
223 backiter = 0 | |
224 while not suffdec: | |
225 #xp = x + s*dx; up = u + s*du; rp = r + s*Adx; | |
226 xp = x + s*dx | |
227 up = u + s*du | |
228 rp = r + s*Adx | |
229 #fu1p = xp - up; fu2p = -xp - up; fep = 1/2*(rp'*rp - epsilon^2); | |
230 fu1p = xp - up | |
231 fu2p = -xp - up | |
232 fep = 0.5*(np.vdot(rp,rp) - epsilon**2) | |
233 #fp = sum(up) - (1/tau)*(sum(log(-fu1p)) + sum(log(-fu2p)) + log(-fep)); | |
234 fp = up.sum() - (1.0/tau)*(np.log(-fu1p).sum() + np.log(-fu2p).sum() + math.log(-fep)) | |
235 #flin = f + alpha*s*(gradf'*[dx; du]); | |
236 flin = f + alpha*s*np.dot(gradf.T , np.concatenate((dx,du),0)) | |
237 #suffdec = (fp <= flin); | |
238 if fp <= flin: | |
239 suffdec = True | |
240 else: | |
241 suffdec = False | |
242 | |
243 s = beta*s | |
244 backiter = backiter + 1 | |
245 if (backiter > 32): | |
246 if verbose: | |
247 print 'Stuck on backtracking line search, returning previous iterate. (See Section 4 of notes for more information.)' | |
248 xp = x.copy() | |
249 up = u.copy() | |
250 return xp,up,niter | |
251 #end | |
252 #end | |
253 | |
254 # set up for next iteration | |
255 #x = xp; u = up; r = rp; | |
256 x = xp.copy() | |
257 u = up.copy() | |
258 r = rp.copy() | |
259 #fu1 = fu1p; fu2 = fu2p; fe = fep; f = fp; | |
260 fu1 = fu1p.copy() | |
261 fu2 = fu2p.copy() | |
262 fe = fep | |
263 f = fp | |
264 | |
265 #lambda2 = -(gradf'*[dx; du]); | |
266 lambda2 = -np.dot(gradf.T , np.concatenate((dx,du),0)) | |
267 #stepsize = s*norm([dx; du]); | |
268 stepsize = s * np.linalg.norm(np.concatenate((dx,du),0)) | |
269 niter = niter + 1 | |
270 #done = (lambda2/2 < newtontol) | (niter >= newtonmaxiter); | |
271 if lambda2/2.0 < newtontol or niter >= newtonmaxiter: | |
272 done = 1 | |
273 else: | |
274 done = 0 | |
275 | |
276 #disp(sprintf('Newton iter = #d, Functional = #8.3f, Newton decrement = #8.3f, Stepsize = #8.3e', ... | |
277 if verbose: | |
278 print 'Newton iter = ',niter,', Functional = ',f,', Newton decrement = ',lambda2/2.0,', Stepsize = ',stepsize | |
279 | |
280 if verbose: | |
281 if largescale: | |
282 print ' CG Res = ',cgres,', CG Iter = ',cgiter | |
283 else: | |
284 print ' H11p condition number = ',hcond | |
285 #end | |
286 | |
287 #end | |
288 return xp,up,niter | |
289 | |
290 def l1qec_logbarrier(x0, A, At, b, epsilon, Aexact, Atexact, bexact, lbtol=1e-3, mu=10, cgtol=1e-8, cgmaxiter=200, verbose=False): | |
291 | |
292 # Check if epsilon > 0. If epsilon is 0, the algorithm fails. You should run the algo with equality constraint instead | |
293 if epsilon == 0: | |
294 raise l1qecInputValueError('Epsilon should be > 0!') | |
295 | |
296 #largescale = isa(A,'function_handle'); | |
297 if hasattr(A, '__call__'): | |
298 largescale = True | |
299 else: | |
300 largescale = False | |
301 | |
302 # if (nargin < 6), lbtol = 1e-3; end | |
303 # if (nargin < 7), mu = 10; end | |
304 # if (nargin < 8), cgtol = 1e-8; end | |
305 # if (nargin < 9), cgmaxiter = 200; end | |
306 # Nic: added them as optional parameteres | |
307 | |
308 newtontol = lbtol | |
309 newtonmaxiter = 50 | |
310 | |
311 #N = length(x0); | |
312 N = x0.size | |
313 | |
314 # starting point --- make sure that it is feasible | |
315 if largescale: | |
316 if np.linalg.norm(A(x0) - b) > epsilon or np.linalg.norm( np.dot(Aexact,x0) - bexact ) > 1e-15: | |
317 if verbose: | |
318 print 'Starting point infeasible; using x0 = At*inv(AAt)*y.' | |
319 #AAt = @(z) A(At(z)); | |
320 AAt = lambda z: A(At(z)) | |
321 # TODO: implement cgsolve | |
322 w,cgres,cgiter = cgsolve(AAt, b, cgtol, cgmaxiter, 0) | |
323 if (cgres > 1.0/2): | |
324 if verbose: | |
325 print 'A*At is ill-conditioned: cannot find starting point' | |
326 xp = x0.copy() | |
327 return xp | |
328 #end | |
329 x0 = At(w) | |
330 #end | |
331 else: | |
332 # Nic: add test for np.dot(Aexact,x0) - bexact ) > 1e-15 | |
333 if np.linalg.norm( np.dot(A,x0) - b ) > epsilon or np.linalg.norm( np.dot(Aexact,x0) - bexact ) > 1e-15: | |
334 if verbose: | |
335 print 'Starting point infeasible; using x0 = At*inv(AAt)*y.' | |
336 | |
337 #Nic: stack A and Aexact, b and bexact, and use them instead of A and b | |
338 Abig = np.vstack((A,Aexact)) | |
339 bbig = np.concatenate((b,bexact)) | |
340 try: | |
341 w = scipy.linalg.solve(np.dot(Abig,Abig.T), bbig, sym_pos=True) | |
342 #w = np.linalg.solve(np.dot(A,A.T), b) | |
343 hcond = 1.0/np.linalg.cond(np.dot(Abig,Abig.T)) | |
344 except scipy.linalg.LinAlgError: | |
345 if verbose: | |
346 print 'A*At is ill-conditioned: cannot find starting point' | |
347 xp = x0.copy() | |
348 return xp | |
349 if hcond < 1e-14: | |
350 if verbose: | |
351 print 'A*At is ill-conditioned: cannot find starting point' | |
352 xp = x0.copy() | |
353 return xp | |
354 x0 = np.dot(Abig.T, w) | |
355 # try: | |
356 # w = scipy.linalg.solve(np.dot(A,A.T), b, sym_pos=True) | |
357 # #w = np.linalg.solve(np.dot(A,A.T), b) | |
358 # hcond = 1.0/scipy.linalg.cond(np.dot(A,A.T)) | |
359 # except scipy.linalg.LinAlgError: | |
360 # print 'A*At is ill-conditioned: cannot find starting point' | |
361 # xp = x0.copy() | |
362 # return xp | |
363 # if hcond < 1e-14: | |
364 # print 'A*At is ill-conditioned: cannot find starting point' | |
365 # xp = x0.copy() | |
366 # return xp | |
367 # #x0 = A'*w; | |
368 # x0 = np.dot(A.T, w) | |
369 #end | |
370 #end | |
371 x = x0.copy() | |
372 u = (0.95)*np.abs(x0) + (0.10)*np.abs(x0).max() | |
373 | |
374 #disp(sprintf('Original l1 norm = #.3f, original functional = #.3f', sum(abs(x0)), sum(u))); | |
375 if verbose: | |
376 print 'Original l1 norm = ',np.abs(x0).sum(),'original functional = ',u.sum() | |
377 | |
378 # choose initial value of tau so that the duality gap after the first | |
379 # step will be about the origial norm | |
380 tau = max(((2*N+1.0)/np.abs(x0).sum()), 1) | |
381 | |
382 lbiter = math.ceil((math.log(2*N+1)-math.log(lbtol)-math.log(tau))/math.log(mu)) | |
383 #disp(sprintf('Number of log barrier iterations = #d\n', lbiter)); | |
384 if verbose: | |
385 print 'Number of log barrier iterations = ',lbiter | |
386 | |
387 totaliter = 0 | |
388 | |
389 # Added by Nic, to fix some crashing | |
390 if lbiter == 0: | |
391 xp = np.zeros(x0.size) | |
392 #end | |
393 | |
394 #for ii = 1:lbiter | |
395 for ii in np.arange(lbiter): | |
396 | |
397 xp,up,ntiter = l1qec_newton(x, u, A, At, b, epsilon, Aexact, tau, newtontol, newtonmaxiter, cgtol, cgmaxiter) | |
398 totaliter = totaliter + ntiter | |
399 | |
400 #disp(sprintf('\nLog barrier iter = #d, l1 = #.3f, functional = #8.3f, tau = #8.3e, total newton iter = #d\n', ... | |
401 # ii, sum(abs(xp)), sum(up), tau, totaliter)); | |
402 if verbose: | |
403 print 'Log barrier iter = ',ii,', l1 = ',np.abs(xp).sum(),', functional = ',up.sum(),', tau = ',tau,', total newton iter = ',totaliter | |
404 x = xp.copy() | |
405 u = up.copy() | |
406 | |
407 tau = mu*tau | |
408 | |
409 #end | |
410 return xp | |
411 |