Example 1: Sparsity-Assisted Signal Smoothing (K = 2)

This example shows the use of SASS for filtering a signal that has discontinuities in its derivative (the signal has cusps). SASS is based on modeling the data, y, as: y = f + g + w where f is a low-pass signal, g has a sparse order-k derivative, and w is white noise.

Ivan Selesnick
selesi@poly.edu
2013

Contents

Start

clear

printme = @(filename) print('-dpdf', sprintf('figures/Example1_%s', filename));

ramp = @(n) n .* (n >= 0);

Make signals

N = 512;
n = (0:N-1)';

% M- : discontinuity points
M1 = 100;
M2 = 125;
M3 = 150;
M4 = 250;
M5 = 250;
M6 = 300;
M7 = 150;
M8 = 175;
M9 = 200;

% g : piecewise linear signal
g = 2 * ramp( n - M1 ) - 4 * ramp( n - M2 ) + 2 * ramp( n - M3 );
g = g + 9 * (ramp(n - M4 + 6) - ramp( n - M4 ));
g = g - ramp( n - M5 ) + 9 * ramp( n - M6 ) - 8 * ramp( n - M6 - 6 );
g = g + 2 * ramp( n - M7 ) - 4 * ramp( n - M8 ) + 2*ramp( n - M9 ) ;
g = 1 - 0.05 * g;

randn('state', 1);
rand('state', 1);               % set rand/randn states for reproducibility of example

f = zeros(size(n));             % f : low-frequency sinusoids
for k = 1:6
    f = f + 0.5 * randn * cos(2*pi*k/N*n + 2*pi*rand);
end

sigma = 0.5;
noise = sigma * randn(size(n)); % noise : white Gaussian noise

s = g + f;                      % s : noise-free signal

data = g + f + noise;           % data : noisy signal

Display signals

ax = [0 N -4 4];

figure(1)
clf

subplot(4, 1, 1)
plot(n, f)
title('f')
axis(ax)

subplot(4, 1, 2)
plot(n, g);
title('g')
axis(ax)

subplot(4, 1, 3)
plot(n, s)
title('f + g')
axis(ax)

subplot(4, 1, 4)
plot(n, data)
title('f + g + noise')
axis(ax)

orient tall
printme('data')

Perform preprocessing

The SASS algorithm may introduce transients at the beginning and end of the signal. To reduce the transients, replace first and last samples of the data by a low-order polynomial approximation.

r = 1;
M = 15;
y = preproc(r, M, data);        % y : preprocessed data

figure(1)
clf
subplot(2, 1, 1)
plot(n, y)
title('Preprocessed data')
xlim([0 N])

Filter matrices

Define filter matrices for SASS.

d = 1;                          % filter is of order 2d
fc = 0.02;                      % fc : cut-off frequency (0 < fc < 0.5) (cycles/sample)
K = 2;                          % K : order of sparse derivative

% Banded filter matrices:
[A, B, B1, D, a, b, b1, H1norm HTH1norm] = ABfilt(d, fc, N, K);
% [A, B] = ABfilt(d, fc, N, K);

H = @(x) [nan(d,1); A\(B*x); nan(d,1)];     % H: high-pass filter
L = @(x) x - H(x);                          % L: low-pass filter

txt_filt = sprintf('d = %d, fc = %.3f', d, fc);

Low-pass filter

The low-pass filter preserves the low-pass signal, f, with high accuracy, but significantly distorts the piecewise linear signal, g. This guides how to set the cuf-off frequency, fc. fc should be chosen so that the low-pass filter passes the low-pass signal.

x_lpf = L(y);

txt_LPF = sprintf('Low-pass filtering (%s)', txt_filt);

err = s - x_lpf;
RMSE_LPF = sqrt(mean(err(K+1:end-K).^2))

figure(1)
clf

GRAY = [1 1 1]*0.7;

subplot(2, 1, 1)
plot(n, data)
title('Data')
xlabel('Time (samples)')
axis(ax)

subplot(2, 1, 2)
plot(n, data, 'color', GRAY)
line(n, x_lpf);
title('Low-pass filtering (LPF)')
axis(ax)
xlabel('Time (samples)')
axnote(sprintf('RMSE = %.4f', RMSE_LPF))

orient portrait
printme('LPF')
RMSE_LPF =

    0.2513

SASS (L1 norm)

beta = 3;
lam = beta * sigma * HTH1norm;      % lam : regularization parameter

Nit = 100;                          % Nit : number of iterations

[x_L1, cost_L1, u_L1, v_L1] = sass(y, d, fc, K, lam, 'L1', [], Nit);
% x : denoised signal
% cost : cost function history
% u : sparse order-K derivative
% v : inv(A)*B1*u

txt_L1 = sprintf('SASS (L1, K = %d, \\lambda = %.2f, %s)', K, lam, txt_filt)

err = s - x_L1;
RMSE_L1 = sqrt(mean(err(K+1:end-K).^2))
0 incorrectly locked zeros detected.

txt_L1 =

SASS (L1, K = 2, \lambda = 6.00, d = 1, fc = 0.020)


RMSE_L1 =

    0.1430

Cost function

figure(2)
clf
plot(cost_L1)
title('Cost function history');
xlabel('Iteration')

Denoised signal

figure(2)
clf
subplot(2, 1, 1)
plot(n, x_L1)
axnote(sprintf('RMSE = %.4f', RMSE_L1))
title(txt_L1)
axis(ax)

Optimality scatter plot

figure(2)
clf
plot_scatter(A, B, B1, y, u_L1, lam, 'L1', []);
title(sprintf('L1 optimality scatter plot (%d iterations)', Nit))
printme('L1_scatter')

Components

figure(1)
clf
plot_components(n, data, x_lpf, x_L1, u_L1, v_L1, txt_L1);
orient tall
printme('L1_components')
figure(1)
clf

subplot(2, 1, 1)
plot(n, data)
title('Data')
xlabel('Time (samples)')
axis(ax)

subplot(2, 1, 2)
plot( n, data, 'color', GRAY);
line( n, x_L1 );
title('Sparsity-assisted signal smoothing (SASS) with L1 norm')
xlabel('Time (samples)')
axis(ax)

orient portrait
printme('L1')

SASS (log penalty)

The log penalty function is non-convex

% rho : Controls non-convexity of penalty (0 <= rho <= 1)
rho = 0.5;

% initialize with L1 solution
[x_log, cost_log, u_log, v_log, a] = sass(y, d, fc, K, lam, 'log', rho, Nit, u_L1);

txt_log = sprintf('SASS (log, K = %d, \\lambda = %.2f, \\rho = %.2f, %s)', K, lam, rho, txt_filt);

err = s - x_log;
RMSE_log = sqrt(mean(err(K+1:end-K).^2))
0 incorrectly locked zeros detected.

RMSE_log =

    0.1278

Cost function

figure(2)
clf
plot(cost_L1)
title('Cost function history');
xlabel('Iteration')

Denoised signal

figure(2)
clf
subplot(2, 1, 1)
plot(n, x_log)
title(txt_L1)
axnote(sprintf('RMSE = %.4f', RMSE_log))
axis(ax)

Optimality scatter plot

figure(2)
clf
plot_scatter(A, B, B1, y, u_log, lam, 'log', a);
title(sprintf('log optimality scatter plot (%d iterations)', Nit))
printme('log_satter')

Components

figure(1)
clf
plot_components(n, data, x_lpf, x_log, u_log, v_log, txt_log);
orient tall
printme('log_components')
figure(1)
clf

subplot(2, 1, 1)
plot(n, data)
title('Data')
xlabel('Time (samples)')
axis(ax)

subplot(2, 1, 2)
plot( n, data, 'color', GRAY);
line( n, x_log );
title('Sparsity-assisted signal smoothing (SASS) with log penalty')
% title(txt_log)
axnote(sprintf('RMSE = %.4f', RMSE_log))
xlabel('Time (samples)')
axis(ax)

orient portrait
printme('log')

Save figure to pdf file

set(0, 'DefaultAxesFontSize', 8);

figure(10)
clf

M = 4;

subplot(M, 1, 1)
plot(n, data)
title('Noisy data');
axis(ax)

subplot(M, 1, 2)
plot(n, x_lpf);
title(txt_LPF)
axnote(sprintf('RMSE = %.3f', RMSE_LPF))
axis(ax)

subplot(M, 1, 3)
plot(n, x_L1)
title(txt_L1);
axnote(sprintf('RMSE = %.3f', RMSE_L1))
axis(ax)

subplot(M, 1, 4)
plot(n, x_log)
title(txt_log);
axnote(sprintf('RMSE = %.3f', RMSE_log))
axis(ax)

orient tall
printme('fig1')

set(0, 'DefaultAxesFontSize', 'remove');