from Solve quadratically constrained overdetermined l1 minimization. by Haim Avron
Solve min ||K * x - f||_1 s.t. ||y - x||_2 <= \epsilon where K has more rows than columns.

l1minqc(K, f, y, epsilon, x0, lytol, mu, newtonmaxiter, maxiter, itertol)
function xp = l1minqc(K, f, y, epsilon, x0, lytol, mu, newtonmaxiter, maxiter, itertol) 
% xp = l1minqc(K, f, y, epsilon, x0, lytol, mu, newtonmaxiter, maxiter, itertol) 
%
% Solve quadratically constrained l1 minimization:
% min ||K * x - f||_1   s.t.  ||y - x||_2 <= \epsilon
%
% Reformulate as the second-order cone program
% min_{x,u}  sum(u)   s.t.    Kx - u - f<= 0,
%                            -Kx - u + f<= 0,
%      1/2(||y - x||^2 - \epsilon^2) <= 0
% and use a log barrier algorithm.
%  
%
% x0 - Nx1 vector, initial point.
%
% K - Matrix K which is m-by-n. m >= n. Three options:
%     dense matrix - either \ or blendenpik is used to solve linear
%                    equations (for search direction).
%     sparse matrix - use LSQR to solve linear equations using a 
%                     preconditionning.
%     function handle - represents Kx and K'x. 
%                       K * x = K(x, 'notransp')
%                       K' * x = K(x, 'transp')
%                       Use unpreconditioned LSQR. 
%
%
% f - mx1 right hand side.
%
% y - nx1 base solution.
%
% epsilon - scalar - max distance from base solution.
%
% lytol - The log barrier algorithm terminates when the duality gap <= lytol.
%         Also, the number of log barrier iterations is completely
%         determined by lytol.
%         Default = 1e-3.
%
% mu - Factor by which to increase the barrier constant at each iteration.
%      Default = 10.
%
% newtonmaxiter - Max iterations for Newton method.
%                 Default = 20.
%
% maxiter - Maximum number of iterations for LSQR.
%           Ignored if K is dense.
%           Default = 100.
%
% itertol - Tolerance for LSQR for sparse K.
%           Ignored if K is dense.
%           Default = 1e-8.
%
% Algorithm is described in Section 5 of:
% "L1-sparse reconstruction of sharp point set surfaces"
% http://www.cs.tau.ac.il/~haima/l1sparse-tog-final.pdf
%
% Written by: Haim Avron, based on l1-Magic
% Email: haim.avron@gmail.com
% Created: April 2009
%

%% Complete parameters
if (nargin < 6)
    lytol = 1e-3; 
end
if (nargin < 7)
    mu = 10; 
end
if (nargin < 8) 
    newtonmaxiter = 20; 
end
if (nargin < 9) 
    maxiter = 100; 
end
if (nargin < 10)
    itertol = 1e-8; 
end

tstart = cputime;

funchand = isa(K,'function_handle');

newtontol = lytol;
N = length(x0);

x = x0;
if (funchand)
    d = abs(K(x0, 'notransp') - f);
else
    d = abs(K * x0 - f);
end
u = (0.95)*(d) + (0.10)*max(d);

disp(sprintf('Original l1 norm = %.3f, original functional = %.3f', sum(d), sum(u)));

% choose initial value of tau so that the duality gap after the first
% step will be about the origial norm
tau = (2 * length(d) + 1) / sum(d);

lyiter = ceil((log(2 * length(d) + 1)-log(lytol)-log(tau))/log(mu));
disp(sprintf('Number of log barrier iterations = %d\n', lyiter));

totaliter = 0;

for ii = 1:lyiter

  [xp, up, ntiter] = l1minqc_newton(x, u, K, f, y, epsilon, tau, newtontol, newtonmaxiter, itertol, maxiter);
  totaliter = totaliter + ntiter;
  
  if (funchand)
      res = abs(K(xp, 'notransp') - f);
  else
      res = abs(K * xp - f);
  end
  disp(sprintf('\nLog barrier iter = %d, l1 = %.3f, functional = %8.3f, tau = %8.3e, total newton iter = %d\n',...
       ii, sum(res), sum(up), tau, totaliter));
  
  x = xp;
  u = up;
 
  tau = mu*tau;
end

solvetime = cputime - tstart;
disp(sprintf('\n\nTotal solution time (CPU time) is %.2e seconds.',  solvetime));

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Newton method
function [xp, up, niter] = l1minqc_newton(x0, u0, K, f, y, epsilon, tau, newtontol, newtonmaxiter, itertol, maxiter)

%% Determine external libraries
use_blendenpik = ~isempty(which('blendenpik'));
use_sptrisolve = ~isempty(which('sptrisolve'));

if (use_blendenpik)
    blendenpik_params.tol = itertol;
end

%% Determine matrix type
if isa(K, 'function_handle');
    Ktype = 2;
    Kapply = @(x) K(x, 'notransp');
    Ktapply = @(x) K(x, 'transp');
else
    Kt = K';
    Kapply = @(x) K * x;
    Ktapply = @(x) Kt * x;
    if (issparse(K))
        Ktype = 1;
    else
        Ktype = 0;
    end
end

% line search parameters
alpha = 0.01;
beta = 0.5;

N = length(x0);
M = length(f);

% initial point
x = x0;
u = u0;
r = x - y;
fu1 = Kapply(x) - u - f;
fu2 = -Kapply(x) - u + f;
% if (Ktype == 2)
%     fu1 = K(x, 'notransp') - u - f;
%     fu2 = -K(x, 'notransp') - u + f;
% else
%     fu1 = K * x - u - f;
%     fu2 = - K * x - u + f;
% end
fe = 1/2*(r'*r - epsilon^2);
fc = sum(u) - (1/tau)*(sum(log(-fu1)) + sum(log(-fu2)) + log(-fe));


perm = [];

niter = 0;
done = 0;
while (~done)

    ntgz = 1./fu1 - 1./fu2;
    ntgu = -tau - 1./fu1 - 1./fu2;

    sig11 = 1./fu1.^2 + 1./fu2.^2;
    sig12 = -1./fu1.^2 + 1./fu2.^2;
    %% The following are the 'true' formulas, but to avoid overflow other
    %% formulas are used.
    %sigx = sig11 - sig12.^2./sig11;
    %ssigx = sqrt(sigx);
    ssigx = 2 ./ (abs(fu1) .* abs(fu2) .* sqrt(sig11));
    SSigx = spdiags(ssigx, 0, M, M);
% 
%     if (Ktype == 2)
%         gradf = -(1/tau)*[K(ntgz, 'transp') + 1/fe*r; ntgu];
%     else
%         gradf = -(1/tau)*[Kt * ntgz + 1/fe*r; ntgu];
%     end
    gradf = -(1/tau)*[Ktapply(ntgz) + 1/fe*r; ntgu];
    w = [(ntgz - sig12./sig11.*ntgu) ./ ssigx;
        zeros(N, 1);
        1];
    
    % Now we need to solve min(norm(Ktilde*dx - w, 2)) 
    tstart = cputime;
    switch(Ktype) 
        case 0
            Ktilde = [SSigx * K; (1/sqrt(-fe)) * eye(N, N); (1/fe) * r'];
            if (max(ssigx) / (-fe) < 1e3)
                if (use_blendenpik)
                    dx = blendenpik(Ktilde, w, blendenpik_params);
                    solvemethod = 'Blendenpik';
                else
                    dx = Ktilde \ w;
                    solvemethod = 'Direct (MATLAB backslash)';
                end
            else
                % In this case Ktilde should be well-conditioned to begin
                % with. We can use an iterative method without worry.
                solvemethod = 'Iterative';
                [dx, flag] = lsqr(Ktilde, w, itertol, maxiter);
                if (flag ~= 0)
                    disp('WARNING: LSQR failed to converege!!!');
                end
            end
            
        case 1
                
            Ktilde1 = [SSigx * K; (1/sqrt(-fe)) * speye(N, N)];
            Ktilde = [Ktilde1; (1/fe) * r'];

            if (max(ssigx) / (-fe) < 1e3)
                solvemethod = 'Semi-direct Cholesky';
                if (~isempty(perm))
                    Ktilde1P = Ktilde1(:, perm);
                    clear L;
                    [L, p] = chol2(Ktilde1P' * Ktilde1P);
                else
                    [L, p, perm] = chol2(Ktilde1' * Ktilde1);
                    if (p > 0)
                        solvemethod = 'Semi-direct QR';
                        opts.Q = 'discard';
                        opts.econ = N;
                        clear L;
                        [Qdummy, L, P] = spqr(Ktilde1, opts);
                        [perm, dummy] = find(P);
                        p = 0;
                    end
                end

                if (p > 0)
                    solvemethod = 'Semi-direct QR';
                    clear L;
                    L = spqr(Ktilde1P, 0);
                end
                % Using sptrisolve explicitly to solve the traingular systems
                % is much faster! 
                if (use_sptrisolve)
                    precond_fn = @(b, transp) sptrisolve(L, b, 'upper', transp); 
                else
                    precond_fn = @(b, transp) precond_by_L(L, b, transp);
                end
                [dx1, flag] = lsqr(Ktilde(:, perm), w, itertol, maxiter, precond_fn);
                if (flag ~= 0)
                    disp('WARNING: LSQR failed to converege!!!');
                end
                dx(perm, 1) = dx1;
            else
                % In this case Ktilde should be well-conditioned to begin
                % with. We can use an iterative method without worry.
                solvemethod = 'Iterative';
                [dx, flag] = lsqr(Ktilde, w, itertol, maxiter);
                if (flag ~= 0)
                    disp('WARNING: LSQR failed to converege!!!');
                end
            end

        case 2
            solvemethod = 'Iterative';
            Ktilde = @(w, transp) large_scale_Ktilde(K, N, fe, r, SSigx, w, transp);
            [dx, flag, relres, iter] = lsqr(Ktilde, w, itertol, maxiter);
            if (flag ~= 0)
                disp('WARNING: LSQR failed to converege!!!');
            end
    end
    solvetime = cputime - tstart;
    disp(sprintf('\t\tEquation solve time (CPU time): %.2e\tMethod: %s', solvetime, solvemethod));

    Kdx = Kapply(dx);
    du = (ntgu - sig12 .* Kdx) ./ sig11;

    % minimum step size that stays in the interior
    s = 1;
    xp = x + s*dx;  up = u + s*du;  rp = r + s * dx;
    coneiter = 0;
    while ( (max(abs(Kapply(xp) - f)-up) > 0) || (rp'*rp > epsilon^2) )
        s = beta*s;
        xp = x + s*dx;  up = u + s*du;  rp = r + s*dx;
        coneiter = coneiter + 1;
        if (coneiter > 32)
            disp('Stuck on cone iterations, returning previous iterate.');
            xp = x;  up = u;
            return
        end
    end

    % backtracking line search
    %% TODO: func handle
    fu1p = Kapply(xp) - f - up;  fu2p = f - Kapply(xp) - up;  
    fep = 1/2*(rp'*rp - epsilon^2);
    fcp = sum(up) - (1/tau)*(sum(log(-fu1p)) + sum(log(-fu2p)) + log(-fep));
    fclin = fc + alpha*s*(gradf'*[dx; du]);
    backiter = 0;
    while (fcp > fclin)
        s = beta*s;
        xp = x + s*dx;  up = u + s*du;  rp = r + s*dx;
        fu1p = Kapply(xp) - f - up;  fu2p = f - Kapply(xp) - up;  fep = 1/2*(rp'*rp - epsilon^2);
        fcp = sum(up) - (1/tau)*(sum(log(-fu1p)) + sum(log(-fu2p)) + log(-fep));
        fclin = fc + alpha*s*(gradf'*[dx; du]);
        backiter = backiter + 1;
        if (backiter > 32)
            disp('Stuck on backtracking line search, returning previous iterate.');
            xp = x;  up = u;
            return
        end
    end

    % set up for next iteration
    x = xp; u = up;  r = rp;
    fu1 = fu1p;  fu2 = fu2p;  fe = fep;  fc = fcp;

    lambda2 = -(gradf'*[dx; du]);
    stepsize = s*norm([dx; du]);
    niter = niter + 1;
    done = (lambda2/2 < newtontol) | (niter >= newtonmaxiter);

    disp(sprintf('Newton iter = %d, Functional = %.2e, Newton decrement = %.2e, Stepsize = %.2e, Cone iterations = %d, Backtrack iterations = %d', ...
        niter, fc, lambda2/2, stepsize, coneiter, backiter));

end
    
%%
function x = precond_by_L(L, b, transp)

if (strcmp(transp, 'transp'))
    x = L' \ b;
else
    x = L \ b;
end

%%
function x = large_scale_Ktilde(K, N, fe, r, SSigx, w, transp)

if (strcmp(transp, 'notransp'))
    x = [SSigx * K(w, 'notransp');
         (1/sqrt(-fe)) * w;
         (1/fe) * r' * w];
else
    M = size(SSigx, 1);
    x = K(SSigx * w(1:M), 'transp') + (1/sqrt(-fe)) * w(M+1:M+N) + (1/fe) * w(end) * r;
end


            
                   

Contact us at files@mathworks.com