Code covered by the BSD License  

Highlights from
rilt

image thumbnail
from rilt by Iari-Gabriel Marino
Regularized Inverse Laplace Transform

rilt(t,y,s,g0,alpha,varargin)
function [g,yfit,cfg] = rilt(t,y,s,g0,alpha,varargin)
%     rilt   Regularized Inverse Laplace Transform
% 
% [g,yfit,cfg] = rilt(t,y,s,g0,alpha)
% 
% Array g(s) is the Inverse Laplace Transform of the array y(t),
% calculated by a regularized least squares method. This script is
% an emulation of S. Provencher CONTIN program, written in Fortran.
% See http://s-provencher.com/pages/contin.shtml.
% 
% y: data array, a function of the array t (for example: time).
% s: the s space, defined by the user.
% g0: the guess distribution, defined by the user.
% 
% yfit is the Laplace Transform of g and is fitted
% against y at each step of calculation.
% 
% cfg is a structure containing the parameters of the calculation.
% 
% alpha: the regularization parameter (see CONTIN manual,
% by S. Provencher: http://s-provencher.com/pages/contin.shtml,
% Section 3.2.2).
% In this script the second derivative of g is minimized with
% weight alpha^2 ("principle of parsimony"): hor high alpha
% values, the output g distribution will be smooth and
% regular. For alpha==0, the output g distribution will
% be totally 'free'.
% 
% TIP: Start with a g0(s) at low resolution (for example, 10
% points), and obtain a g. Then define an s-space at higher
% resolution and create a corrispondent g0 by interpolating the
% g, for example:
%   g0 = interp1(old_s,g,new_s,'linear');
% Finally repeat the rilt algorithm starting with the new g0.
% 
% REMARKS
% The Inverse Laplace Trasform is a highly ill-posed problem and 
% is therefore intrinsically affected by numerical instability, i.e.
% its solution may not be unique, may not exist or may not depend
% continuously on the data. The g calculated by rilt.m is only
% one of the possible solutions, and this limit is unavoidable.
% Changing the parameters, the solution may change too.
% 
% [...] = rilt(t,y,s,g0,alpha,plot_type,maxsearch,options,shape,constraints,R,w)
% 
% plot_type: defines how to plot the data during the calculation.
% Does not affect the result. Default is 'logarithmic'.
% Available options for plot_type:
%     'logarithmic' - logarithmic x axis
%     'linear' - linear x axis
% 
% maxsearch: Max number of iterations. Default: 1000.
% 
% options: options structure for fminsearch. Default:
%     options = optimset('MaxFunEvals',1e8,'MaxIter',1e8);
%     
% shape: can be 'raise' for raise-up dynamics:
%     y(t) = sum { g(s)*(1-exp(-t/s)) }
% or 'decay' for relaxation dynamics:
%     y(t) = sum { g(s)*exp(-t/s) }
% default is 'decay'.
% 
% constraints: allowed constraints on the g distribution function
% (a cell of multiple constraints can be passed). Available options
% for constraints:
%     'g>0'
%     'zero_at_the_extremes'
% Default is: constraints = {'g>0';'zero_at_the_extremes'};
% 
% R is a matrix determining the form of the Regularizor.
% (see http://s-provencher.com/pages/contin.shtml for details)
% Default settings:
%     R = zeros(length(y)-2,length(y));
% 
% w is the array of fitting weigths for each value of y
% Default settings:
%     w = ones(size(y(:)));
% 
% Leave an empty array to set the default value for the parameters.
% Feel free to contact me and suggest modifications.
% -------------------------------------------------------------
% (c) 2007 Iari-Gabriel Marino, Ph.D.
% University of Parma
% Physics Department
% Viale G.P. Usberti, 7/a
% 43100 Parma - Italy
% e-mail: iarigabriel.marino@fis.unipr.it
% web: http://www.fis.unipr.it/home/marino/
% Tel. +39 (0) 521 906212
% Fax +39 (0) 521 905223
% -------------------------------------------------------------
% 
% Example:
%
% % Target g(s) function:
% g0 = [0 0 10.1625 25.1974 21.8711 1.6377 7.3895 8.736 1.4256 0 0]';
% % s space:
% s0  = logspace(-3,6,length(g0))';
% 
% semilogx(s0,g0); title('Target times distribution - Press any key...'); shg
% pause
% 
% t = linspace(0.01,500,100)';
% [sM,tM] = meshgrid(s0,t);
% A = exp(-tM./sM);
% % Data:
% data = A*g0 + 0.07*rand(size(t));
% 
% alpha = 1e-2;
% 
% % Guess s space and g function
% s = logspace(-3,6,20)';
% g = ones(size(s));
% [g,yfit,cfg] = rilt(t,data,s,g,alpha,'logarithmic',[],[],[],{'g>0'},[],[]);
% 
% subplot(1,2,1)
% plot(t,data,'.',t,yfit,'ro'); title('data and fitting')
% subplot(1,2,2)
% semilogx(s0,g0/max(g0),s,g/max(g),'o-'); title('g-target and g');

%###########################################################

g0 = g0(:); % must be column
s = s(:); % must be column
y = y(:); % must be column
t = t(:); % must be column

if nargin == 5
    plot_type = @semilogx;
    maxsearch = 1000;
    options = optimset('MaxFunEvals',1e8,'MaxIter',1e8);
    shape = 'decay';
    constraints = {'g>0','zero_at_the_extremes'};
    R = zeros(length(g0)-2,length(g0));
    w = ones(size(y(:)));
elseif nargin == 12
    if isempty(varargin{1})
        plot_type = @semilogx;
    else
        if strcmp(varargin{1},'logarithmic')
            plot_type = @semilogx;
        elseif strcmp(varargin{1},'linear')
            plot_type = @plot;
        end
    end
    if isempty(varargin{2})
        maxsearch = 1000;
    else
        maxsearch = varargin{2};
    end
    if isempty(varargin{3})
        options = optimset('MaxFunEvals',1e8,'MaxIter',1e8);
    else
        options = varargin{3};
    end
    if isempty(varargin{4})
        shape = 'decay';
    else
        shape = varargin{4};
    end
    if isempty(varargin{5})
        constraints = {'g>0';'zero_at_the_extremes'};
    else
        constraints = varargin{5};
    end
    if isempty(varargin{6})
        R = zeros(length(g0)-2,length(g0));
    else
        R = varargin{6};
    end
    if isempty(varargin{7})
        w = ones(size(y(:)));
    else
        w = varargin{7};
    end
end

[sM,tM] = meshgrid(s,t);

% Also raising kinetics can be inverted
if strcmp(shape,'raise')
    A = 1 - exp(-tM./sM);
elseif strcmp(shape,'decay')
    A = exp(-tM./sM);
else
    error(['Unknown shape: ' shape]);
    return;
end

% Rough normalization of g0, to start with a good guess
g0 = g0*sum(y)/sum(A*g0);

% Plots initialization
fh = gcf; clf(fh);
set(fh,'doublebuffer','on');
s1h = subplot(2,2,1); plot(t,y,'.',t,A*g0); title('Data and fitting curve'); axis tight
s2h = subplot(2,2,2); feval(plot_type,s,g0,'o-'); title('Initial distribution...'); axis tight
s3h = subplot(2,2,3); msdh = plot(0,0); title('Normalized msd');
s4h = subplot(2,2,4); plot(t,abs(y-A*g0)); title('Residuals'); axis tight
msd2plot = 0;
drawnow;

% Main cycle
ly = length(y);
oldssd = Inf;
tic
for k = 1:maxsearch,
    % msd: The mean square deviation; this is the function
    % that has to be minimized by fminsearch
    [g,msdfit] = fminsearch(@msd,g0,options,y,A,alpha,R,w,constraints);
    %--- Re-apply constraints ----
    if ismember('zero_at_the_extremes',constraints)
        g(1) = 0;
        g(end) = 0;
    end
    if ismember('g>0',constraints)
        g = abs(g);
    end
    %--------------------
    g0 = g; % for the next step
    ssd = sqrt(msdfit/ly); % Sample Standard Deviation
    ssdStr = num2str(ssd);
    deltassd = oldssd-ssd; % Difference between "old ssd" and "current ssd"
    disp([int2str(k) ': ' ssdStr])
    oldssd = ssd;
    msd2plot(k) = msdfit/ly;
    plotdata(s1h,s2h,s3h,s4h,t,y,A,g,k,maxsearch,plot_type,s,msd2plot,msdh,ssdStr,deltassd);

    % Condition for the stabilization (end of cycles):
    % difference between "old ssd" and "current ssd" == 0
    if deltassd == 0, % < eps,
        disp(['Stabilization reached at step: ' int2str(k) '/' int2str(maxsearch)])
        break;
    end
end
disp(['Elapsed time: ' num2str(toc/60) ' min.'])

% Saving parameters and results
yfit = A*g; % fitting curve

cfg.t = t;
cfg.y = y;
cfg.yfit = yfit;
cfg.g0 = g0;
cfg.alpha = alpha;
cfg.R = R;
cfg.w = w;
cfg.maxsearch = maxsearch;
cfg.constraints = constraints;
cfg.date = datestr(now,30);

% Store g and s in a temporary file
% that can be loaded as a starting configuration
save rilt_temp g s cfg
disp('Temp output saved.')
set(gcf,'name','Fitting done')

% ### SUBS #####################################################
function out = msd(g,y,A,alpha,R,w,constraints)
% msd: The mean square deviation; this is the function
% that has to be minimized by fminsearch

% Constraints and any 'a priori' knowledge
if ismember('zero_at_the_extremes',constraints)
    g(1) = 0;
    g(end) = 0;
end
if ismember('g>0',constraints)
    g = abs(g); % must be g(i)>=0 for each i
end
r = diff(diff(g(1:end))); % second derivative of g
yfit = A*g;
% Sum of weighted square residuals
VAR = sum(w.*(y-yfit).^2);
% Regularizor
REG = alpha^2 * sum((r-R*g).^2);
% Output to be minimized
out = VAR+REG;

% ### SUBS #####################################################
function plotdata(s1h,s2h,s3h,s4h,t,y,A,g,k,maxsearch,plot_type,s,msd2plot,msdh,ssdStr,deltassd)
% For the "real-time" plots

axes(s1h);
plot(t,y,'.',t,A*g); title('Data')
xlabel('Time (s)');
axis tight

axes(s2h);
feval(plot_type,s,g,'o-');
title('Relaxation times distribution g(s)');
xlabel('s');
axis tight

axes(s3h);
title(['ssd: ' ssdStr '; \Deltassd: ' num2str(deltassd)])
ylabel('Sample Standard Deviation')
xlabel('Step')
set(msdh,'xdata',1:k,'ydata',msd2plot);
axis tight

axes(s4h);
plot(t,abs(y-A*g)/length(y),'o-');
title('Normalized residuals')
xlabel('Time (s)');
axis tight

set(gcf,'name',['Step:' int2str(k) '/' int2str(maxsearch)])

drawnow

Contact us at files@mathworks.com