Mind the Gap: The PhysioNet/Computing in Cardiology Challenge 2010 1.0.0

File: <base>/sources/Ikaro-Silva/mcaf.m (13,235 bytes)
function [sig,varargout]=mcaf(x,varargin)
%
%
%[sig,[mse],[xhat],[param]]=mcaf(x,d_ind,[CH],[Fs],[show],[verbose])
%
%Input Arguments are:
% x       -NxM data matrix. Each column represent a channel
% d_din   -scalar index indicating the target column (to be predicted)
% CH      -Optional Mx1 cell array with the names of each channel 
% Fs      -Optional scalar indicating sampling frequency in Hz (default is 125 Hz)
% show    -Optional logical argument indicating to plot final results
% verbose -Optional logical argument indicating progress of the code
% 
% %Output Arguments are:
% sig     -Nx1 reconstructed signal
% mse     -Optional output Nx1 mean square error of the final estimate and desired response
% xhat    -Optional output 1xM final Kalman weights 
% param   -Optional detailed information of filter training 
%          including final filter parameters and NxM Kalman weights as a function of time
%
%Multi Channel Adaptive Filtering
%Based on the Physionet 2010 Challenge phys_est_phase2_v11 version.
% Copyright (C) 2010 Ikaro Silva
% 
% This library is free software; you can redistribute it and/or modify it under
% the terms of the GNU Library General Public License as published by the Free
% Software Foundation; either version 2 of the License, or (at your option) any
% later version.
% 
% This library is distributed in the hope that it will be useful, but WITHOUT ANY
% WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
% PARTICULAR PURPOSE.  See the GNU Library General Public License for more
% details.
% 
% You should have received a copy of the GNU Library General Public License along
% with this library; if not, write to the Free Software Foundation, Inc., 59
% Temple Place - Suite 330, Boston, MA 02111-1307, USA.
% 
% You may contact the author by e-mail (ikaro@ieee.org).
%
%NOTE: this function uses PARLOOPS, so make sure to type 'matlabpool'
%prior to running it.
%%Example
% 
% clear all;clc;close all
% load mat_c05
% d_ind=2;
% CH={'RESP','PLETH','ABP','II','V','AVR','CVP'};
% verbose=1;
% show=1;
% [sig]=mcaf(mat_c05,d_ind,CH,[],show,verbose);



Fs=125;
pole_order=20;
d_ind=4;
CH={};
par_mode=1;
show=1;
verbose=1;
par_name={'d_ind','CH','Fs','show','verbose'};

%Get optional parameters
for n=2:nargin
    if(~isempty(varargin{n-1}))
        eval([par_name{n-1} '=varargin{n-1};'])
    end
end

if(verbose)
    display('***Running MCAF***');
end
%targets=dlmread([data_dir fname '.missing']);
Ntarget=round(Fs*30);
[N,L]=size(x);
if(isempty(CH))
    CH=cell(L,1);
end
if(length(CH) ~= L)
    error(['CH is ' num2str(length(CH)) ' long, should be a cell array of size ' num2str(L)])
end

%%% GET SIGNALS %%%%
d=x(:,d_ind);
sig_name=CH{d_ind};

%Replace original signal with a estimate of it base on past history
sig=[d(end-2*Ntarget+1:end-Ntarget);d(1:end-Ntarget)];
x(:,d_ind)=sig;

%Get Rid of any initial clipping
clp=cumsum(abs(sign(diff(d))));
clp_end=(find(clp~=0));
if(~isempty(clp_end) && ~clp(Fs) && (clp_end(1) < N-2*Ntarget) )
    d(1:clp_end-1)=[];
    x(1:clp_end-1,:)=[];
    N=length(d);
    warning('Getting rid of initial flat signal artifact');
end

n2=N-Ntarget;
n1=1;
sig=zeros(N,1);
mse=0;
NCh=length(CH);
param.CH=CH;
param.Ntarget=Ntarget;
param.w=NaN;
param.XHAT=NaN;
param.fin_best=' ';
param.init=' ';
param.CH=CH;
param.Ntarget=Ntarget;
param.w=NaN;
param.XHAT=NaN;
param.fin_best=' ';
param.init=' ';
param.n1=n1;
param.n2=n2;
maxy=max(d(1:N-Ntarget));
miny=min(d(1:N-Ntarget));
y=[1:L];
ws1=1;      %sum sqrt
ws2=1;      %rms


%Define GALL Parameters
M=35;
epsi=10^-2;
R=zeros(L,1);
H=zeros(L,M+2);
K=zeros(L,M+1);
D=zeros(n2-n1+1,L);     %estimates for each signal
P=zeros(L,1);
B=zeros(L,1);
G=zeros(L,1); %gain
param.D=D;




%%%%%%%%       ANALYZE THE SIGNALS  %%%%%%%%%%%%%%%%%%%%
%Return DC of input if the signal is flat
if(length(unique(x(1:N-Ntarget,d_ind)))==1)
    sig=ones(N,1).*x(1,d_ind);
    warning('Signal is flat. Returning DC as estimate')
    if(nargout >1)
        varargout(1)={NaN};
        if(nargout>2)
            varargout(2)={NaN};
            if(nargout>3)
                varargout(3)={zeros(1,L)};
            end
        end
    end
    return
end
%%%%%%%%%%%%%%%%%%
%Training Phase
%%%%%%%%%%%%%%%%%%

%Optimize xcross each signal
POLE=[[1-0.0005.^[linspace(0,1,pole_order)]] ];
pad=d(n1:n2).*0;
target=d(n1:n2);
BETA=[[1-0.0001.^[linspace(0,1,6)]] ];
BETA(1)=0.5;
err=zeros(1,L);
std_tar=std(target);
NBETA=length(BETA);
NPOLE=length(POLE);
tmp_err1=zeros(NBETA,NPOLE)+NaN;
tmp_err2=zeros(NBETA,NPOLE)+NaN;
for m=1:L
    if(verbose)
        display(['Getting reconstruction of CH ' num2str(m) ' ( out of ' num2str(L) ' CHs)']);
    end
      for beta=1:NBETA
        parfor p=1:NPOLE
            [trash,dhat_tmp,h,k]=gall(x(n1:n2,y(m)),target,M,BETA(beta),POLE(p),epsi,[],[]);
            [trash,dhat_tmp,trash,trash]=gall(x(n1:n2,y(m)),pad,[],[],POLE(p),epsi,k,h); %test phase
            %tmp_err=sqrt(mean((dhat_tmp(end-8*Ntarget:end)-target(end-8*Ntarget:end)).^2));
            if(any(isnan(dhat_tmp)) || isnan(sum(diff(dhat_tmp))) ||~sum(diff(dhat_tmp)))
                dhat_tmp=x(n1:n2,y(m));
            end
            tmp_err1(beta,p)=sum((dhat_tmp-target).^2);
            tmp_err2(beta,p)=sqrt(mean((dhat_tmp-target).^2));
        end
    end %of optimization loop
    [best_err1,best_ind1]=nanmin(tmp_err1(:));
    [best_err2,best_ind2]=nanmin(tmp_err2(:));
    
    df1=tmp_err1(best_ind1)-tmp_err1(best_ind2);
    df2=tmp_err2(best_ind2)-tmp_err2(best_ind1);
    if((ws1*df1)<(ws2*df2))
        [best_err,best_ind]=nanmin(tmp_err1(:));
    else
        [best_err,best_ind]=nanmin(tmp_err2(:));
    end
    [beta_opt,p_opt]=ind2sub([NBETA NPOLE],best_ind);
    [trash,dhat_tmp,h,k]=gall(x(n1:n2,y(m)),target,M,BETA(beta_opt),POLE(p_opt),epsi,[],[]);
    [trash,dhat_tmp,trash,trash]=gall(x(n1:n2,y(m)),pad,[],[],POLE(p_opt),epsi,k,h); %test phase
     if(any(isnan(dhat_tmp)) || isnan(sum(diff(dhat_tmp))) ||~sum(diff(dhat_tmp)))
          dhat_tmp=x(n1:n2,y(m));
     end
    err(m)=best_err;
    dhat=dhat_tmp;
    H(m,:)=h;
    K(m,:)=k;
    P(m)=POLE(p_opt);
    B(m)=BETA(beta_opt);
    G(m)=std_tar/std(dhat);
    if(isinf(G(m)) || isnan(G(m)))
        G(m)=1;
    end
    D(:,m)=dhat'.*G(m);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%Combine each estimator using Kalman filter to obtain final estimate
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bad=find(isnan(err)==1);
if(~isempty(bad))
    warning('NaN error found')
    D(:,bad)=-D(:,d_ind);
end

err(bad)=inf;
%sort the signals according to the error
best_ind=sortrows([err(:) [1:L]']);

%Initiliaze state vector
w0=ones(L,1)./L;
Klamb=[0 0.75 0.9 0.97 0.997 0.9997 0.99997 0.999997 0.9999997 0.99999997];
NKlamb=length(Klamb);
K0=[];
NOISE=[];

k_opt=[inf NaN];
wopt=NaN;
XHAT=NaN;
XHATopt=NaN;
max_warn=0;
Opt_st=10;
mse1=zeros(NKlamb,1)+NaN;
mse2=zeros(NKlamb,1)+NaN;
if(verbose)
    display(['Combining individual reconstructions']);
end
parfor k=1:NKlamb
    k_tmp=Klamb(k);
    [wtmp,XHAT,ALPHA]=kallman(D,target,w0,k_tmp,K0,NOISE);
    dhat_tmp=D*wtmp;
    mse1(k)=sum((dhat_tmp(end-Opt_st:end)-target(end-Opt_st:end)).^2);
    mse2(k)=sqrt(mean((dhat_tmp(end-Opt_st:end)-target(end-Opt_st:end)).^2));
end

[K_err1,best_k1]=nanmin(mse1);
[K_err2,best_k2]=nanmin(mse2);
df1=mse1(best_k1)-mse1(best_k2);
df2=mse2(best_k2)-mse2(best_k1);
if((ws1*df1)<(ws2*df2))
    [K_err,best_k]=nanmin(mse1(:));
else
    [K_err,best_k]=nanmin(mse2(:));
end


[wtmp,XHAT,ALPHA]=kallman(D,target,w0,Klamb(best_k),K0,NOISE);
k_opt(1)=K_err;
k_opt(2)=Klamb(best_k);
wopt=wtmp;
XHATopt=XHAT;
[trash,Max_ind]=max(XHATopt(end-(60*2)*Fs:end,:),[],2);
if(length(unique(Max_ind))>2) %did not have the unique statement before
    max_warn=1;
end
if(max_warn)
    warning('Attempting to stabilize weights')
    mse1_best=[Inf NaN];
    mse2_best=[Inf NaN];
    wopt2=wtmp;
    wopt1=wtmp;
    XHATopt1=[];
    XHATopt2=[];
    Kwin_max=15*Fs;
    for k=1:length(Klamb)
        k_tmp=Klamb(k);
        [wtmp,XHAT,ALPHA]=kallman(D,target,w0,k_tmp,K0,NOISE);        
        %Apply averaging to XHAT  
        for kal_win=[(5*Fs):(5*Fs):Kwin_max]
            wtmp=mean(XHAT(end-kal_win:end,:))';
            dhat_tmp=D*wtmp;
            mse1=sum((dhat_tmp(end-Opt_st:end)-target(end-Opt_st:end)).^2);
            %mse1=mean(abs(dhat_tmp(end-Opt_st:end)-target(end-Opt_st:end)));
            mse2=sqrt(mean((dhat_tmp(end-Opt_st:end)-target(end-Opt_st:end)).^2));
            if(mse1 < mse1_best(1))
                wopt1=wtmp;
                XHATopt1=XHAT;
                mse1_best(1)=mse1;
                mse2_best(2)=mse2;
            end
            if(mse2 < mse2_best(1))
                wopt2=wtmp;
                XHATopt2=XHAT;
                mse1_best(2)=mse1;
                mse2_best(1)=mse2;
            end
        end
    end
    if(ws1*(mse1_best(1)-mse1_best(2)) < ws2*(mse2_best(1)-mse2_best(2)))
        wopt=wopt1;
        XHATopt=XHAT;
        mse_best=mse1;
    else
        wopt=wopt2;
        XHATopt=XHAT;
        mse_best=mse2;
    end
end
if(any(isnan(wopt)))
    warning('Kallman weightst not found.')
    wopt=ones(L,1)./L;
end
if(min(wopt)==0)
    warning('Weights normalized')
    wopt=ones(L,1)./L;
end

%Real Final Deal
D2=zeros(N,L);     %estimates for each signal
parfor m=1:L
    [err_tmp,dhat2,trash,trash]=gall(x(:,y(m)),d.*0,...
        [],[],P(m),epsi,K(m,:),H(m,:)); %test phase
    if(any(isnan(H(m,:))) || ~sum(H(m,:)) || any(isnan(dhat2)) || ~sum(diff(dhat2)))
        dhat2=x(:,y(m));
    end
    D2(:,m)=dhat2'.*G(m);
end
dhat2_A=D2*wopt;

%Update dc component
dhat2_B=dhat2_A-mean(dhat2_A)+mean(d(n2-3*Ntarget:n2));
mseA1=sum((dhat2_A(end-2*Ntarget:end-Ntarget)-d(end-2*Ntarget:end-Ntarget)).^2);
mseA2=sqrt(mean((dhat2_A(end-2*Ntarget:end-Ntarget)-d(end-2*Ntarget:end-Ntarget)).^2));
mseB1=sum((dhat2_B(end-2*Ntarget:end-Ntarget)-d(end-2*Ntarget:end-Ntarget)).^2);
mseB2=sqrt(mean((dhat2_B(end-2*Ntarget:end-Ntarget)-d(end-2*Ntarget:end-Ntarget)).^2));
df1=min(mseA1,mseB1)- (mseA1*(mseA2<mseB2)) - (mseB1*(mseA2>mseB2));
df2=min(mseA2,mseB2)- (mseA2*(mseA1<mseB1)) - (mseB2*(mseA1>mseB1));

if ((ws1*df1) < (ws2*df2))
    if(mseA1<=mseB1)
        dhat2=dhat2_A;
    else
        dhat2=dhat2_B;
    end
else
    if(mseA2<=mseB2)
        dhat2=dhat2_A;
    else
        dhat2=dhat2_B;
    end
end

%Pass final signal through a GALL filter
%Optimize xcross each signal
BETA2=[0 0.5 0.95 0.995 0.9995 0.99995 0.999995 0.9999997 ];
NBETA2=length(BETA2);
opt=inf;
dhat3_tmp=inf;
H3=[];
K3=[];
B3=[];
dhat3=dhat2;
st_ind=max(N-6*Ntarget,1);
tmp_err1=zeros(NBETA2,1)+NaN;
tmp_err2=zeros(NBETA2,1)+NaN;
parfor beta=1:NBETA2
    [trash,trash,h,k]=gall(dhat2(n1:n2),target,M,BETA2(beta),0,epsi,[],[]);
    [trash,dhat3_tmp,trash,trash]=gall(dhat2,d.*0,[],[],0,epsi,k,h); %test phase
    
    tmp_err1(beta)=sum((dhat3_tmp(st_ind:end-Ntarget)-d(st_ind:end-Ntarget)).^2);
    tmp_err2(beta)=sqrt(mean((dhat3_tmp(st_ind:end-Ntarget)-d(st_ind:end-Ntarget)).^2));
    
end %of optimization loop
[beta1_err,best_beta1]=nanmin(tmp_err1(:));
[beta2_err,best_beta2]=nanmin(tmp_err2(:));
df1=tmp_err1(best_beta1)-tmp_err1(best_beta2);
df2=tmp_err2(best_beta2)-tmp_err2(best_beta1);
if((ws1*df1)<(ws2*df2))
    [beta2_err,best_beta2]=nanmin(tmp_err1(:));
else
    [beta2_err,best_beta2]=nanmin(tmp_err2(:));
end   
[trash,trash,h,k]=gall(dhat2(n1:n2),target,M,BETA2(best_beta2),0,epsi,[],[]);
[trash,dhat3_tmp,trash,trash]=gall(dhat2,d.*0,[],[],0,epsi,k,h); %test phase
opt=beta2_err;
dhat3=dhat3_tmp;
H3=h;
K3=k;
B3=BETA2(best_beta2);
dhat3(dhat3>maxy)=maxy;
dhat3(dhat3<miny)=miny;

if(isnan(dhat3))
    dc_tmp=unique(d(1:end-Ntarget));
    if(length(unique(dc_tmp))==1)
        dhat3=zeros(size(d))+dc_tmp;
    end
end
mse=sum((dhat3(n1:n2)-target).^2);
NCh=L;
[trash,fin_best]=max(XHAT(end,:));
[trash,init_best]=max(XHAT(1,:));
[trash,ave_best]=max(mean(XHAT,1));

if(show)
    plot_res(d,dhat3,n2,miny,maxy,CH,d_ind,mse,fin_best,ave_best,XHATopt,y,sig_name,N,init_best);
end
sig=dhat3;
param.R=R;
param.H=H;
param.K=K;
param.d=d;
param.D=D2;
param.P=P;
param.res=(d(1:n2)-dhat3(1:n2)).^2;
param.res_ref=(D(:,d_ind)-d(1:n2)).^2;
param.w=wopt;
param.XHAT=XHATopt;
param.fin_best=CH{fin_best};
param.init=CH{init_best};
param.sqi=1./(1+param.res./var(sig-mean(sig)));

if(nargout >1)
    varargout(1)={mse};
    if(nargout>2)
        varargout(2)={mean(XHAT,1)};
        if(nargout>3)
            varargout(3)={param};
        end
    end
end

if(verbose)
    display('->DONE running MCAF');
end

%%%%%%%%%%%% END OF MAIN %%%%%%%%%%%%%%%%%%%%%%

function plot_res(d,dhat3,n2,miny,maxy,CH,d_ind,mse,fin_best,ave_best,XHATopt,y,sig_name,N,init_best)

%Plot Kallman Results
figure
subplot(311)
plot(d)
hold on;grid on
plot(dhat3,'r')
line([n2 n2],[miny maxy].*1.5,'Color','g','LineStyle','--','LineWidth',3)
if(~isempty(CH{1}))
legend(CH{d_ind},'Estimated')
title(['CH= ' sig_name ' MSE= ' num2str(mse) ,' Final Best= ' CH{fin_best}])
end
xlim([0 N])

subplot(312)
plot(XHATopt)
grid on
if(~isempty(CH{1}))
    legend(CH(y))
    title(['Ave Best Channel: ' CH{ave_best}])
end
xlim([0 N])

subplot(313)
plot((d(1:n2)-dhat3(1:n2)).^2)
xlim([0 N])