CBLasso implementation (vs BLasso)

Contents

This script compares the CBlasso and the Blasso recovery in the case of a 3-spikes measure. The architecture is the following
1) We solve the SDP dual formulation of Blasso and CBlasso. For this step, 'cvx' solver is used.
2) We proceed to roots-finding for the previous computed dual polynomial. In this step, we identify a set of locations in which the support of the measure to reconstruct is included.
3) CBlasso : we alternate between Lasso steps and noise estimation
3bis) Blasso : we proceed to a Lasso step

Some parts of this code heavily rely on the following works :
(i) http://www.cims.nyu.edu/~cfgranda/scripts/superres_sdp.m
(ii) http://nbviewer.jupyter.org/github/gpeyre/numerical-tours/blob/master/matlab/sparsity_8_sparsespikes_measures.ipynb

Developers : Claire Boyer, Yohann de Castro, Joseph Salmon (june 2016)
clear all
close all

addpath(genpath('~/Documents/MATLAB/cvx')) ;
addpath('math_tools/') ;

rng(1);

% Display features
ms = 20;
lw = 1;
P = 2048*8;
u = (0:P-1)'/P;

Data generation

fc = 80;
delta = 3/fc; % parameter for safe separation condition
N = 2*fc+1;

Fourier = @(fc,x)exp(-2i*pi*(-fc:fc)'*x(:)');

Phi  = @(fc,x,a)Fourier(fc,x)*a;
PhiS = @(fc,u,p)real( Fourier(fc,u)'* p );

% True signal
x0 = [.5-delta .5 .5+2*delta]'; % spike positions
a0 = [1 1 -1]'; % spike magnitudes
n = length(x0);
Phix0 = Phi(fc,x0,a0) ;

% Noisy observations
sigma = 1  ;
w = randn(N,1) + 1i * randn(N,1);
w = sigma * w;
y = Phix0 + w;

% Plot data
f0 = PhiS(fc,u,Phix0);
f = PhiS(fc,u,y);
figure(1);
hold on;
set(gca,'FontSize',16)
plot(u, [f0 f]);
stem(x0, 10*sign(a0), 'k.--', 'MarkerSize', ms, 'LineWidth', 1);
axis tight; box on;
h=legend('${\mathcal{F}_n}^* \left( {\mathcal{F}_n} (\mu^0) \right)$', '${\mathcal{F}_n}^* y$');
set(h,'Interpreter','latex')

Blasso - constructing the dual polynomial

% Parameters for SDP resolution
q = ones(N,1);
alpha = 2 ;

% SDP Blasso
lambda_max_blasso = norm(PhiS(fc,u,y),inf) / N ;
lambda_blasso = N * lambda_max_blasso/alpha;
tic
cvx_solver sdpt3 % SeDuMi %
        cvx_begin sdp quiet
        cvx_precision high;
        variable X(N+1,N+1) hermitian;
        variable c(N) complex;
        X >= 0;
        X(N+1,N+1) == 1;
        X(1:N,N+1) == c .* conj(q);
        trace(X) == 2;
        for j = 1:N-1,
            sum(diag(X,j)) == X(N+1-j,N+1);
        end
        if lambda_blasso==0
            maximize(real( c' * y ))
        else
            minimize( norm(y/lambda_blasso-c, 'fro') )
        end
        cvx_end
Blasso_coeff = X(1:N,N+1);
toc

% Plot the Blasso polynomial

etaLambda_Blasso = PhiS(fc,u,Blasso_coeff);

figure(10) ;
hold on;
set(gca,'FontSize',16) ;
stem(x0, sign(a0), 'k.--', 'MarkerSize', ms, 'LineWidth', lw);
plot([0 1],  [1 1], 'k--', 'LineWidth', lw);
plot([0 1], -[1 1], 'k--', 'LineWidth', lw);
plot(u, etaLambda_Blasso, 'b', 'LineWidth', lw);
axis([0 1 -1.1 1.1]);
set(gca, 'XTick', [], 'YTick', [0 1]); box on;
title('Blasso')
Elapsed time is 27.850022 seconds.

CBlasso - constructing the dual polynomial

% SDP CBLasso
lambda_max_CBlasso = norm(PhiS(fc,u,y),inf) /(norm(y)*sqrt(N))  ;
lambda_CBlasso = lambda_max_CBlasso/alpha ;
tic
cvx_precision best
cvx_solver sdpt3
cvx_begin sdp quiet
    variable X(N+1,N+1) hermitian;
    variable c(N) complex;
    X >= 0;
    X(N+1,N+1) == 1;
    X(1:N,N+1) == c .* conj(q);
    trace(X) == 2;
    norm(c)<= 1/(sqrt(N)*lambda_CBlasso) ;
    for j = 1:N-1,
        sum(diag(X,j)) == X(N+1-j,N+1);
    end
    maximize(real(c'*y))
cvx_end
toc

CBlasso_coeff = X(1:N,N+1);




% Plot the CBlasso polynomial

etaLambda_CBlasso = PhiS(fc,u,CBlasso_coeff);
figure(11) ;
hold on;
set(gca,'FontSize',16) ;
stem(x0, sign(a0), 'k.--', 'MarkerSize', ms, 'LineWidth', lw);
plot([0 1],  [1 1], 'k--', 'LineWidth', lw);
plot([0 1], -[1 1], 'k--', 'LineWidth', lw);
plot(u, etaLambda_CBlasso, 'b', 'LineWidth', lw);
axis([0 1 -1.1 1.1]);
set(gca, 'XTick', [], 'YTick', [0 1]); box on;
title('CBlasso')
Elapsed time is 25.498846 seconds.

Roots-finding

tol = 1e-2 ;
% CBlasso poly
aux =- conv(CBlasso_coeff,flipud(conj(CBlasso_coeff))) ; % poly coeff of $- |p|^2$
aux(2*fc+1)=1+aux(2*fc+1);
roots_all_CBlasso = roots(flipud(aux)); % roots function convention


% CBlasso: Isolate roots on the unit circle
roots_CBlasso_detected = roots_all_CBlasso(abs(1-abs(roots_all_CBlasso)) < tol);
[~,I]=sort(angle(roots_CBlasso_detected));
roots_CBlasso_detected = roots_CBlasso_detected(I); % roots of 1-|p|^2
roots_CBlasso_detected = roots_CBlasso_detected(1:2:end); % roots of 1-|p


% Blasso poly
aux =- conv(Blasso_coeff,flipud(conj(Blasso_coeff))) ; % poly coeff of (- |p|^2)
aux(2*fc+1)=1+aux(2*fc+1);
roots_all_Blasso = roots(flipud(aux)); % roots function convention


% Blasso: Isolate roots on the unit circle
roots_Blasso_detected = roots_all_Blasso(abs(1-abs(roots_all_Blasso)) < tol);
[~,I]=sort(angle(roots_Blasso_detected));
roots_Blasso_detected = roots_Blasso_detected(I); % roots of 1-|p|^2
roots_Blasso_detected = roots_Blasso_detected(1:2:end); % roots of 1-|p



% Plot the complex roots
figure(20) ;
set(gca,'FontSize',16)
plot(real(roots_all_CBlasso),imag(roots_all_CBlasso),'*');
hold on;
plot(real(roots_CBlasso_detected),imag(roots_CBlasso_detected),'kp');
plot(cos(2*pi*x0), sin(2*pi*x0),'ro');
plot( exp(1i*linspace(0,2*pi,200)), '--' );
hold off;
legend('Roots CBlasso','Roots detected','Support of x','Unit circle'); axis equal; axis([-1 1 -1 1]*1.3);



figure(21) ;
set(gca,'FontSize',16)
plot(real(roots_all_Blasso),imag(roots_all_Blasso),'*');
hold on;
plot(real(roots_Blasso_detected),imag(roots_Blasso_detected),'kp');
plot(cos(2*pi*x0), sin(2*pi*x0),'ro');
plot( exp(1i*linspace(0,2*pi,200)), '--' );
hold off;
legend('Roots Blasso','Roots detected','Support of x','Unit circle'); axis equal; axis([-1 1 -1 1]*1.3);

Solution via Blasso

% Precomputation for Blasso
x_Blasso = angle(roots_Blasso_detected)/(2*pi);
x_Blasso = sort(mod(x_Blasso,1));
Phix_Blasso = Fourier(fc,x_Blasso);
sign_coeff_Blasso = sign(real(Phix_Blasso'*Blasso_coeff));


% Lasso step
tic ;
lasso_sol_1 = (Phix_Blasso\y - lambda_blasso*pinv(Phix_Blasso'*Phix_Blasso)*sign_coeff_Blasso );
toc ;
Elapsed time is 0.002452 seconds.

Solution via Scaled lasso (alternating Lasso step and noise estimation)

% Precomputation for CBlasso
x_CBlasso = angle(roots_CBlasso_detected)/(2*pi);
x_CBlasso = sort(mod(x_CBlasso,1));
Phix_CBlasso = Fourier(fc,x_CBlasso);
sign_coeff_CBlasso = sign(real(Phix_CBlasso'*CBlasso_coeff));

% Alternating Lasso steps and noise estimations
tol = 1e-4 ;
maxiter = 1000 ;
niter = 0 ;
sigma_old = norm(y)/sqrt(N) ;
sigma_hat = 0.1 ; % initialization for $\hat{\sigma}$
tic ;
while (abs(sigma_hat(end) - sigma_old) > tol) && (niter<maxiter)
    niter = niter + 1 ;
    sigma_old = sigma_hat(end) ;
    lambda = sigma_hat(end) * lambda_CBlasso ;
    scaled_lasso_sol = (Phix_CBlasso\y - lambda*N*pinv(Phix_CBlasso'*Phix_CBlasso)*sign_coeff_CBlasso );
    sigma_hat = norm(y - Phix_CBlasso * scaled_lasso_sol)/sqrt(2*N) ; % should divide by sqrt(2)
end
toc ;
Elapsed time is 0.000915 seconds.

Comparison plot : reconstructed measures via Blasso and CBlasso

figure(100);
hold on;
set(gca,'FontSize',16)
stem(x0, a0, 'k.--', 'MarkerSize', ms, 'LineWidth', 1);
stem(x_Blasso, lasso_sol_1, 'ro--', 'MarkerSize', ms/2, 'LineWidth', 1);
stem(x_CBlasso, scaled_lasso_sol, 'b*--', 'MarkerSize', ms/2, 'LineWidth', 1);
axis([0 1 -1.1 1.1]);
legend('Original', 'BLasso','CBLasso');

Noise estimation

sigma
sigma_CBlasso = norm(y - Phix_CBlasso * scaled_lasso_sol)/sqrt(2*N)  % divided by sqrt(2) due to the complex case
sigma_Blasso = norm(y - Phix_Blasso * lasso_sol_1)/sqrt(2*N)
sigma =

     1


sigma_CBlasso =

    0.9780


sigma_Blasso =

    1.1467