nikcleju@45
|
1 function [xk,niter,residuals,outputData,opts] =NESTA(A,At,b,muf,delta,opts)
|
nikcleju@45
|
2 % [xk,niter,residuals,outputData] =NESTA(A,At,b,muf,delta,opts)
|
nikcleju@45
|
3 %
|
nikcleju@45
|
4 % Solves a L1 minimization problem under a quadratic constraint using the
|
nikcleju@45
|
5 % Nesterov algorithm, with continuation:
|
nikcleju@45
|
6 %
|
nikcleju@45
|
7 % min_x || U x ||_1 s.t. ||y - Ax||_2 <= delta
|
nikcleju@45
|
8 %
|
nikcleju@45
|
9 % Continuation is performed by sequentially applying Nesterov's algorithm
|
nikcleju@45
|
10 % with a decreasing sequence of values of mu0 >= mu >= muf
|
nikcleju@45
|
11 %
|
nikcleju@45
|
12 % The primal prox-function is also adapted by accounting for a first guess
|
nikcleju@45
|
13 % xplug that also tends towards x_muf
|
nikcleju@45
|
14 %
|
nikcleju@45
|
15 % The observation matrix A is a projector
|
nikcleju@45
|
16 %
|
nikcleju@45
|
17 % Inputs: A and At - measurement matrix and adjoint (either a matrix, in which
|
nikcleju@45
|
18 % case At is unused, or function handles). m x n dimensions.
|
nikcleju@45
|
19 % b - Observed data, a m x 1 array
|
nikcleju@45
|
20 % muf - The desired value of mu at the last continuation step.
|
nikcleju@45
|
21 % A smaller mu leads to higher accuracy.
|
nikcleju@45
|
22 % delta - l2 error bound. This enforces how close the variable
|
nikcleju@45
|
23 % must fit the observations b, i.e. || y - Ax ||_2 <= delta
|
nikcleju@45
|
24 % If delta = 0, enforces y = Ax
|
nikcleju@45
|
25 % Common heuristic: delta = sqrt(m + 2*sqrt(2*m))*sigma;
|
nikcleju@45
|
26 % where sigma=std(noise).
|
nikcleju@45
|
27 % opts -
|
nikcleju@45
|
28 % This is a structure that contains additional options,
|
nikcleju@45
|
29 % some of which are optional.
|
nikcleju@45
|
30 % The fieldnames are case insensitive. Below
|
nikcleju@45
|
31 % are the possible fieldnames:
|
nikcleju@45
|
32 %
|
nikcleju@45
|
33 % opts.xplug - the first guess for the primal prox-function, and
|
nikcleju@45
|
34 % also the initial point for xk. By default, xplug = At(b)
|
nikcleju@45
|
35 % opts.U and opts.Ut - Analysis/Synthesis operators
|
nikcleju@45
|
36 % (either matrices of function handles).
|
nikcleju@45
|
37 % opts.normU - if opts.U is provided, this should be norm(U)
|
nikcleju@45
|
38 % otherwise it will have to be calculated (potentially
|
nikcleju@45
|
39 % expensive)
|
nikcleju@45
|
40 % opts.MaxIntIter - number of continuation steps.
|
nikcleju@45
|
41 % default is 5
|
nikcleju@45
|
42 % opts.maxiter - max number of iterations in an inner loop.
|
nikcleju@45
|
43 % default is 10,000
|
nikcleju@45
|
44 % opts.TolVar - tolerance for the stopping criteria
|
nikcleju@45
|
45 % opts.stopTest - which stopping criteria to apply
|
nikcleju@45
|
46 % opts.stopTest == 1 : stop when the relative
|
nikcleju@45
|
47 % change in the objective function is less than
|
nikcleju@45
|
48 % TolVar
|
nikcleju@45
|
49 % opts.stopTest == 2 : stop with the l_infinity norm
|
nikcleju@45
|
50 % of difference in the xk variable is less
|
nikcleju@45
|
51 % than TolVar
|
nikcleju@45
|
52 % opts.TypeMin - if this is 'L1' (default), then
|
nikcleju@45
|
53 % minimizes a smoothed version of the l_1 norm.
|
nikcleju@45
|
54 % If this is 'tv', then minimizes a smoothed
|
nikcleju@45
|
55 % version of the total-variation norm.
|
nikcleju@45
|
56 % The string is case insensitive.
|
nikcleju@45
|
57 % opts.Verbose - if this is 0 or false, then very
|
nikcleju@45
|
58 % little output is displayed. If this is 1 or true,
|
nikcleju@45
|
59 % then output every iteration is displayed.
|
nikcleju@45
|
60 % If this is a number p greater than 1, then
|
nikcleju@45
|
61 % output is displayed every pth iteration.
|
nikcleju@45
|
62 % opts.fid - if this is 1 (default), the display is
|
nikcleju@45
|
63 % the usual Matlab screen. If this is the file-id
|
nikcleju@45
|
64 % of a file opened with fopen, then the display
|
nikcleju@45
|
65 % will be redirected to this file.
|
nikcleju@45
|
66 % opts.errFcn - if this is a function handle,
|
nikcleju@45
|
67 % then the program will evaluate opts.errFcn(xk)
|
nikcleju@45
|
68 % at every iteration and display the result.
|
nikcleju@45
|
69 % ex. opts.errFcn = @(x) norm( x - x_true )
|
nikcleju@45
|
70 % opts.outFcn - if this is a function handle,
|
nikcleju@45
|
71 % then then program will evaluate opts.outFcn(xk)
|
nikcleju@45
|
72 % at every iteration and save the results in outputData.
|
nikcleju@45
|
73 % If the result is a vector (as opposed to a scalar),
|
nikcleju@45
|
74 % it should be a row vector and not a column vector.
|
nikcleju@45
|
75 % ex. opts.outFcn = @(x) [norm( x - xtrue, 'inf' ),...
|
nikcleju@45
|
76 % norm( x - xtrue) / norm(xtrue)]
|
nikcleju@45
|
77 % opts.AAtinv - this is an experimental new option. AAtinv
|
nikcleju@45
|
78 % is the inverse of AA^*. This allows the use of a
|
nikcleju@45
|
79 % matrix A which is not a projection, but only
|
nikcleju@45
|
80 % for the noiseless (i.e. delta = 0) case.
|
nikcleju@45
|
81 % opts.USV - another experimental option. This supercedes
|
nikcleju@45
|
82 % the AAtinv option, so it is recommended that you
|
nikcleju@45
|
83 % do not define AAtinv. This allows the use of a matrix
|
nikcleju@45
|
84 % A which is not a projection, and works for the
|
nikcleju@45
|
85 % noisy ( i.e. delta > 0 ) case.
|
nikcleju@45
|
86 % opts.USV should contain three fields:
|
nikcleju@45
|
87 % opts.USV.U is the U from [U,S,V] = svd(A)
|
nikcleju@45
|
88 % likewise, opts.USV.S and opts.USV.V are S and V
|
nikcleju@45
|
89 % from svd(A). S may be a matrix or a vector.
|
nikcleju@45
|
90 %
|
nikcleju@45
|
91 % Outputs:
|
nikcleju@45
|
92 % xk - estimate of the solution x
|
nikcleju@45
|
93 % niter - number of iterations
|
nikcleju@45
|
94 % residuals - first column is the residual at every step,
|
nikcleju@45
|
95 % second column is the value of f_mu at every step
|
nikcleju@45
|
96 % outputData - a matrix, where each row r is the output
|
nikcleju@45
|
97 % from opts.outFcn, if supplied.
|
nikcleju@45
|
98 % opts - the structure containing the options that were used
|
nikcleju@45
|
99 %
|
nikcleju@45
|
100 % Written by: Jerome Bobin, Caltech
|
nikcleju@45
|
101 % Email: bobin@acm.caltech.edu
|
nikcleju@45
|
102 % Created: February 2009
|
nikcleju@45
|
103 % Modified (version 1.0): May 2009, Jerome Bobin and Stephen Becker, Caltech
|
nikcleju@45
|
104 % Modified (version 1.1): Nov 2009, Stephen Becker, Caltech
|
nikcleju@45
|
105 %
|
nikcleju@45
|
106 % NESTA Version 1.1
|
nikcleju@45
|
107 % See also Core_Nesterov
|
nikcleju@45
|
108
|
nikcleju@45
|
109
|
nikcleju@45
|
110 if nargin < 6, opts = []; end
|
nikcleju@45
|
111 if isempty(opts) && isnumeric(opts), opts = struct; end
|
nikcleju@45
|
112
|
nikcleju@45
|
113 %---- Set defaults
|
nikcleju@45
|
114 fid = setOpts('fid',1);
|
nikcleju@45
|
115 Verbose = setOpts('Verbose',true);
|
nikcleju@45
|
116 function printf(varargin), fprintf(fid,varargin{:}); end
|
nikcleju@45
|
117 MaxIntIter = setOpts('MaxIntIter',5,1);
|
nikcleju@45
|
118 TypeMin = setOpts('TypeMin','L1');
|
nikcleju@45
|
119 TolVar = setOpts('tolvar',1e-5);
|
nikcleju@45
|
120 [U,U_userSet] = setOpts('U', @(x) x );
|
nikcleju@45
|
121 if ~isa(U,'function_handle')
|
nikcleju@45
|
122 Ut = setOpts('Ut',[]);
|
nikcleju@45
|
123 else
|
nikcleju@45
|
124 Ut = setOpts('Ut', @(x) x );
|
nikcleju@45
|
125 end
|
nikcleju@45
|
126 xplug = setOpts('xplug',[]);
|
nikcleju@45
|
127 normU = setOpts('normU',[]); % so we can tell if it's been set
|
nikcleju@45
|
128
|
nikcleju@45
|
129 residuals = []; outputData = [];
|
nikcleju@45
|
130 AAtinv = setOpts('AAtinv',[]);
|
nikcleju@45
|
131 USV = setOpts('USV',[]);
|
nikcleju@45
|
132 if ~isempty(USV)
|
nikcleju@45
|
133 if isstruct(USV)
|
nikcleju@45
|
134 Q = USV.U; % we can't use "U" as the variable name
|
nikcleju@45
|
135 % since "U" already refers to the analysis operator
|
nikcleju@45
|
136 S = USV.S;
|
nikcleju@45
|
137 if isvector(S), s = S; %S = diag(s);
|
nikcleju@45
|
138 else s = diag(S); end
|
nikcleju@45
|
139 %V = USV.V;
|
nikcleju@45
|
140 else
|
nikcleju@45
|
141 error('opts.USV must be a structure');
|
nikcleju@45
|
142 end
|
nikcleju@45
|
143 end
|
nikcleju@45
|
144
|
nikcleju@45
|
145 % -- We can handle non-projections IF a (fast) routine for computing
|
nikcleju@45
|
146 % the psuedo-inverse is available.
|
nikcleju@45
|
147 % We can handle a nonzero delta, but we need the full SVD
|
nikcleju@45
|
148 if isempty(AAtinv) && isempty(USV)
|
nikcleju@45
|
149 % Check if A is a partial isometry, i.e. if AA' = I
|
nikcleju@45
|
150 z = randn(size(b));
|
nikcleju@45
|
151 if isa(A,'function_handle'), AAtz = A(At(z));
|
nikcleju@45
|
152 else AAtz = A*(A'*z); end
|
nikcleju@45
|
153 if norm( AAtz - z )/norm(z) > 1e-8
|
nikcleju@45
|
154 error('Measurement matrix A must be a partial isometry: AA''=I');
|
nikcleju@45
|
155 end
|
nikcleju@45
|
156 end
|
nikcleju@45
|
157
|
nikcleju@45
|
158 % -- Find a initial guess if not already provided.
|
nikcleju@45
|
159 % Use least-squares solution: x_ref = A'*inv(A*A')*b
|
nikcleju@45
|
160 % If A is a projection, the least squares solution is trivial
|
nikcleju@45
|
161 if isempty(xplug) || norm(xplug) < 1e-12
|
nikcleju@45
|
162 if ~isempty(USV) && isempty(AAtinv)
|
nikcleju@45
|
163 AAtinv = Q*diag( s.^(-2) )*Q';
|
nikcleju@45
|
164 end
|
nikcleju@45
|
165 if ~isempty(AAtinv)
|
nikcleju@45
|
166 if delta > 0 && isempty(USV)
|
nikcleju@45
|
167 error('delta must be zero for non-projections');
|
nikcleju@45
|
168 end
|
nikcleju@45
|
169 if isa(AAtinv,'function_handle')
|
nikcleju@45
|
170 x_ref = AAtinv(b);
|
nikcleju@45
|
171 else
|
nikcleju@45
|
172 x_ref = AAtinv * b;
|
nikcleju@45
|
173 end
|
nikcleju@45
|
174 else
|
nikcleju@45
|
175 x_ref = b;
|
nikcleju@45
|
176 end
|
nikcleju@45
|
177
|
nikcleju@45
|
178 if isa(A,'function_handle')
|
nikcleju@45
|
179 x_ref=At(x_ref);
|
nikcleju@45
|
180 else
|
nikcleju@45
|
181 x_ref = A'*x_ref;
|
nikcleju@45
|
182 end
|
nikcleju@45
|
183
|
nikcleju@45
|
184 if isempty(xplug)
|
nikcleju@45
|
185 xplug = x_ref;
|
nikcleju@45
|
186 end
|
nikcleju@45
|
187 % x_ref itself is used to calculate mu_0
|
nikcleju@45
|
188 % in the case that xplug has very small norm
|
nikcleju@45
|
189 else
|
nikcleju@45
|
190 x_ref = xplug;
|
nikcleju@45
|
191 end
|
nikcleju@45
|
192
|
nikcleju@45
|
193 % use x_ref, not xplug, to find mu_0
|
nikcleju@45
|
194 if isa(U,'function_handle')
|
nikcleju@45
|
195 Ux_ref = U(x_ref);
|
nikcleju@45
|
196 else
|
nikcleju@45
|
197 Ux_ref = U*x_ref;
|
nikcleju@45
|
198 end
|
nikcleju@45
|
199 switch lower(TypeMin)
|
nikcleju@45
|
200 case 'l1'
|
nikcleju@45
|
201 mu0 = 0.9*max(abs(Ux_ref));
|
nikcleju@45
|
202 case 'tv'
|
nikcleju@45
|
203 mu0 = ValMUTv(Ux_ref);
|
nikcleju@45
|
204 end
|
nikcleju@45
|
205
|
nikcleju@45
|
206 % -- If U was set by the user and normU not supplied, then calcuate norm(U)
|
nikcleju@45
|
207 if U_userSet && isempty(normU)
|
nikcleju@45
|
208 % simple case: U*U' = I or U'*U = I, in which case norm(U) = 1
|
nikcleju@45
|
209 z = randn(size(xplug));
|
nikcleju@45
|
210 if isa(U,'function_handle'), UtUz = Ut(U(z)); else UtUz = U'*(U*z); end
|
nikcleju@45
|
211 if norm( UtUz - z )/norm(z) < 1e-8
|
nikcleju@45
|
212 normU = 1;
|
nikcleju@45
|
213 else
|
nikcleju@45
|
214 z = randn(size(Ux_ref));
|
nikcleju@45
|
215 if isa(U,'function_handle')
|
nikcleju@45
|
216 UUtz = U(Ut(z));
|
nikcleju@45
|
217 else
|
nikcleju@45
|
218 UUtz = U*(U'*z);
|
nikcleju@45
|
219 end
|
nikcleju@45
|
220 if norm( UUtz - z )/norm(z) < 1e-8
|
nikcleju@45
|
221 normU = 1;
|
nikcleju@45
|
222 end
|
nikcleju@45
|
223 end
|
nikcleju@45
|
224
|
nikcleju@45
|
225 if isempty(normU)
|
nikcleju@45
|
226 % have to actually calculate the norm
|
nikcleju@45
|
227 if isa(U,'function_handle')
|
nikcleju@45
|
228 [normU,cnt] = my_normest(U,Ut,length(xplug),1e-3,30);
|
nikcleju@45
|
229 if cnt == 30, printf('Warning: norm(U) may be inaccurate\n'); end
|
nikcleju@45
|
230 else
|
nikcleju@45
|
231 [mU,nU] = size(U);
|
nikcleju@45
|
232 if mU < nU, UU = U*U'; else UU = U'*U; end
|
nikcleju@45
|
233 % last resort is to call MATLAB's "norm", which is slow
|
nikcleju@45
|
234 if norm( UU - diag(diag(UU)),'fro') < 100*eps
|
nikcleju@45
|
235 % this means the matrix is diagonal, so norm is easy:
|
nikcleju@45
|
236 normU = sqrt( max(abs(diag(UU))) );
|
nikcleju@45
|
237 elseif issparse(UU)
|
nikcleju@45
|
238 normU = sqrt( normest(UU) );
|
nikcleju@45
|
239 else
|
nikcleju@45
|
240 if min(size(U)) > 2000
|
nikcleju@45
|
241 % norm(randn(2000)) takes about 5 seconds on my PC
|
nikcleju@45
|
242 printf('Warning: calculation of norm(U) may be slow\n');
|
nikcleju@45
|
243 end
|
nikcleju@45
|
244 normU = sqrt( norm(UU) );
|
nikcleju@45
|
245 end
|
nikcleju@45
|
246 end
|
nikcleju@45
|
247 end
|
nikcleju@45
|
248 opts.normU = normU;
|
nikcleju@45
|
249 end
|
nikcleju@45
|
250
|
nikcleju@45
|
251
|
nikcleju@45
|
252 niter = 0;
|
nikcleju@45
|
253 Gamma = (muf/mu0)^(1/MaxIntIter);
|
nikcleju@45
|
254 mu = mu0;
|
nikcleju@45
|
255 Gammat= (TolVar/0.1)^(1/MaxIntIter);
|
nikcleju@45
|
256 TolVar = 0.1;
|
nikcleju@45
|
257
|
nikcleju@45
|
258 for nl=1:MaxIntIter
|
nikcleju@45
|
259
|
nikcleju@45
|
260 mu = mu*Gamma;
|
nikcleju@45
|
261 TolVar=TolVar*Gammat; opts.TolVar = TolVar;
|
nikcleju@45
|
262 opts.xplug = xplug;
|
nikcleju@45
|
263 if Verbose, printf('\tBeginning %s Minimization; mu = %g\n',opts.TypeMin,mu); end
|
nikcleju@45
|
264 [xk,niter_int,res,out,optsOut] = Core_Nesterov(...
|
nikcleju@45
|
265 A,At,b,mu,delta,opts);
|
nikcleju@45
|
266
|
nikcleju@45
|
267 xplug = xk;
|
nikcleju@45
|
268 niter = niter_int + niter;
|
nikcleju@45
|
269
|
nikcleju@45
|
270 residuals = [residuals; res];
|
nikcleju@45
|
271 outputData = [outputData; out];
|
nikcleju@45
|
272
|
nikcleju@45
|
273 end
|
nikcleju@45
|
274 opts = optsOut;
|
nikcleju@45
|
275
|
nikcleju@45
|
276
|
nikcleju@45
|
277 %---- internal routine for setting defaults
|
nikcleju@45
|
278 function [var,userSet] = setOpts(field,default,mn,mx)
|
nikcleju@45
|
279 var = default;
|
nikcleju@45
|
280 % has the option already been set?
|
nikcleju@45
|
281 if ~isfield(opts,field)
|
nikcleju@45
|
282 % see if there is a capitalization problem:
|
nikcleju@45
|
283 names = fieldnames(opts);
|
nikcleju@45
|
284 for i = 1:length(names)
|
nikcleju@45
|
285 if strcmpi(names{i},field)
|
nikcleju@45
|
286 opts.(field) = opts.(names{i});
|
nikcleju@45
|
287 opts = rmfield(opts,names{i});
|
nikcleju@45
|
288 break;
|
nikcleju@45
|
289 end
|
nikcleju@45
|
290 end
|
nikcleju@45
|
291 end
|
nikcleju@45
|
292 if isfield(opts,field) && ~isempty(opts.(field))
|
nikcleju@45
|
293 var = opts.(field); % override the default
|
nikcleju@45
|
294 userSet = true;
|
nikcleju@45
|
295 else
|
nikcleju@45
|
296 userSet = false;
|
nikcleju@45
|
297 end
|
nikcleju@45
|
298 % perform error checking, if desired
|
nikcleju@45
|
299 if nargin >= 3 && ~isempty(mn)
|
nikcleju@45
|
300 if var < mn
|
nikcleju@45
|
301 printf('Variable %s is %f, should be at least %f\n',...
|
nikcleju@45
|
302 field,var,mn); error('variable out-of-bounds');
|
nikcleju@45
|
303 end
|
nikcleju@45
|
304 end
|
nikcleju@45
|
305 if nargin >= 4 && ~isempty(mx)
|
nikcleju@45
|
306 if var > mx
|
nikcleju@45
|
307 printf('Variable %s is %f, should be at least %f\n',...
|
nikcleju@45
|
308 field,var,mn); error('variable out-of-bounds');
|
nikcleju@45
|
309 end
|
nikcleju@45
|
310 end
|
nikcleju@45
|
311 opts.(field) = var;
|
nikcleju@45
|
312 end
|
nikcleju@45
|
313
|
nikcleju@45
|
314
|
nikcleju@45
|
315
|
nikcleju@45
|
316
|
nikcleju@45
|
317 %---- internal routine for setting mu0 in the tv minimization case
|
nikcleju@45
|
318 function th=ValMUTv(x)
|
nikcleju@45
|
319
|
nikcleju@45
|
320 N = length(x);n = floor(sqrt(N));
|
nikcleju@45
|
321 Dv = spdiags([reshape([-ones(n-1,n); zeros(1,n)],N,1) ...
|
nikcleju@45
|
322 reshape([zeros(1,n); ones(n-1,n)],N,1)], [0 1], N, N);
|
nikcleju@45
|
323 Dh = spdiags([reshape([-ones(n,n-1) zeros(n,1)],N,1) ...
|
nikcleju@45
|
324 reshape([zeros(n,1) ones(n,n-1)],N,1)], [0 n], N, N);
|
nikcleju@45
|
325 D = sparse([Dh;Dv]);
|
nikcleju@45
|
326
|
nikcleju@45
|
327
|
nikcleju@45
|
328 Dhx = Dh*x;
|
nikcleju@45
|
329 Dvx = Dv*x;
|
nikcleju@45
|
330
|
nikcleju@45
|
331 sk = sqrt(abs(Dhx).^2 + abs(Dvx).^2);
|
nikcleju@45
|
332 th = max(sk);
|
nikcleju@45
|
333
|
nikcleju@45
|
334 end
|
nikcleju@45
|
335
|
nikcleju@45
|
336 end %-- end of NESTA function
|
nikcleju@45
|
337
|
nikcleju@45
|
338 %%%%%%%%%%%% POWER METHOD TO ESTIMATE NORM %%%%%%%%%%%%%%%
|
nikcleju@45
|
339 % Copied from MATLAB's "normest" function, but allows function handles, not just sparse matrices
|
nikcleju@45
|
340 function [e,cnt] = my_normest(S,St,n,tol, maxiter)
|
nikcleju@45
|
341 %MY_NORMEST Estimate the matrix 2-norm via power method.
|
nikcleju@45
|
342 if nargin < 4, tol = 1.e-6; end
|
nikcleju@45
|
343 if nargin < 5, maxiter = 20; end
|
nikcleju@45
|
344 if isempty(St)
|
nikcleju@45
|
345 St = S; % we assume the matrix is symmetric;
|
nikcleju@45
|
346 end
|
nikcleju@45
|
347 x = ones(n,1);
|
nikcleju@45
|
348 cnt = 0;
|
nikcleju@45
|
349 e = norm(x);
|
nikcleju@45
|
350 if e == 0, return, end
|
nikcleju@45
|
351 x = x/e;
|
nikcleju@45
|
352 e0 = 0;
|
nikcleju@45
|
353 while abs(e-e0) > tol*e && cnt < maxiter
|
nikcleju@45
|
354 e0 = e;
|
nikcleju@45
|
355 Sx = S(x);
|
nikcleju@45
|
356 if nnz(Sx) == 0
|
nikcleju@45
|
357 Sx = rand(size(Sx));
|
nikcleju@45
|
358 end
|
nikcleju@45
|
359 e = norm(Sx);
|
nikcleju@45
|
360 x = St(Sx);
|
nikcleju@45
|
361 x = x/norm(x);
|
nikcleju@45
|
362 cnt = cnt+1;
|
nikcleju@45
|
363 end
|
nikcleju@45
|
364 end
|