diff m-toolbox/classes/@ao/tdfit.m @ 0:f0afece42f48

Import.
author Daniele Nicolodi <nicolodi@science.unitn.it>
date Wed, 23 Nov 2011 19:22:13 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/m-toolbox/classes/@ao/tdfit.m	Wed Nov 23 19:22:13 2011 +0100
@@ -0,0 +1,1221 @@
+% TDFIT fit a set of smodels to a set of input and output signals..
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DESCRIPTION: TDFIT fit a set of smodels to a set of input and output signals. 
+% 
+%
+% CALL:        b = tdfit(outputs, pl)
+%
+% INPUTS:      outputs  - the AOs representing the outputs of a system.
+%              pl       - parameter list (see below)
+%
+% OUTPUTs:     b  - a pest object containing the best-fit parameters,
+%                   goodness-of-fit reduced chi-squared, fit degree-of-freedom
+%                   covariance matrix and uncertainties. Additional
+%                   quantities, like the Information Matrix, are contained 
+%                   within the procinfo. The best-fit model can be evaluated
+%                   from pest\eval.
+%
+% <a href="matlab:utils.helper.displayMethodInfo('ao', 'tdfit')">Parameters Description</a>
+%
+% EXAMPLES:
+%
+% % 1) Sine-wave stimulus of a simple system
+% 
+%   % Sine-wave data
+%   data = ao(plist('tsfcn', 'sin(2*pi*3*t) + 0.01*randn(size(t))', 'fs', 100, 'nsecs', 10));
+%   data.setName;
+% 
+%   % System filter
+%   pzm = pzmodel(1, 1, 10);
+%   f = miir(pzm);
+% 
+%   % Make output signal
+%   dataf = filter(data, f);
+%   dataf.setName;
+% 
+%   % fit model to output
+%   mdl = smodel('(a.*(b + 2*pi*i*f)) ./ (b*(a + 2*pi*i*f))');
+%   mdl.setParams({'a', 'b'}, {2*pi 10*2*pi});
+%   mdl.setXvar('f');
+%   params = tdfit(dataf, plist('inputs', data, 'models', mdl, 'P0', [1 1]));
+% 
+%   % Evaluate fit
+%   mdl.setValues(params);
+%   BestModel = fftfilt(data, mdl);
+%   BestModel.setName;
+%   iplot(data, dataf, BestModel, plist('linestyles', {'-', '-', '--'}))
+% 
+%   % recovered parameters (in Hz)
+%   params.y/2/pi
+% 
+% VERSION:     $Id: tdfit.m,v 1.45 2011/05/26 12:57:27 congedo Exp $
+%
+% HISTORY:     05-10-2009 G. Congedo
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%     'WhiteningFilters'  - Use filter banks for whitening the outputs. 
+%                           Note: you must fit the two channels at the same time 
+%                           and supply four filter banks.  
+
+
+function varargout = tdfit(varargin)
+  
+  % Check if this is a call for parameters
+  if utils.helper.isinfocall(varargin{:})
+    varargout{1} = getInfo(varargin{3});
+    return
+  end
+  
+  import utils.const.*
+  utils.helper.msg(msg.PROC3, 'running %s/%s', mfilename('class'), mfilename);
+  
+  % Collect input variable names
+  in_names = cell(size(varargin));
+  for ii = 1:nargin,in_names{ii} = inputname(ii);end
+  
+  % Collect all AOs and plists
+  [as, ao_invars, rest] = utils.helper.collect_objects(varargin(:), 'ao', in_names);
+  [pl, pl_invars, rest] = utils.helper.collect_objects(rest, 'plist', in_names);
+    
+  if nargout == 0
+    error('### tdfit cannot be used as a modifier. Please give an output variable.');
+  end
+    
+  % combine plists
+  pl = parse(pl, getDefaultPlist());
+  
+  outputs = copy(as,1);
+  
+  % Extract necessary parameters
+  inputs    = pl.find('inputs');
+  TFmodels  = pl.find('models');
+  WhFlts    = pl.find('WhFlts');
+  Ncut      = pl.find('Ncut');
+  P0        = pl.find('P0');
+  pnames    = pl.find('pnames');
+  inNames   = pl.find('innames');
+  outNames  = pl.find('outnames');
+%   ADDP      = find(pl, 'ADDP');
+  userOpts  = pl.find('OPTSET');
+  weights   = find(pl, 'WEIGHTS');
+  FitUnc    = pl.find('FitUnc');
+  UncMtd    = pl.find('UncMtd');
+  linUnc    = pl.find('linUnc');
+  FastHess  = pl.find('FastHess');
+  SymDiff   = pl.find('SymDiff');
+  DiffOrder = pl.find('DiffOrder');
+  lb        = pl.find('LB');
+  ub        = pl.find('UB');
+  MCsearch  = pl.find('MonteCarlo');
+  Npoints   = pl.find('Npoints');
+  Noptims   = pl.find('Noptims');
+  Algorithm = pl.find('Algorithm');
+  padRatio  = pl.find('PadRatio');
+  SISO      = pl.find('SingleInputSingleOutput');
+  GradSearch= pl.find('GradSearch');
+  estimator = pl.find('estimator');
+  
+  % Convert yes/no, true/false, etc. to booleans
+  FitUnc      = utils.prog.yes2true(FitUnc);
+  linUnc      = utils.prog.yes2true(linUnc);
+  MCsearch    = utils.prog.yes2true(MCsearch);
+  SymDiff     = utils.prog.yes2true(SymDiff);
+  SISO        = utils.prog.yes2true(SISO);
+  GradSearch  = utils.prog.yes2true(GradSearch);
+   
+  % consistency check on inputs
+  if isempty(TFmodels)
+    error('### please specify at least a transfer function or a SSM model')
+  end
+  if isempty(inputs)
+    error('### please give the inputs of the system')
+  end
+  if isempty(outputs)
+    error('### please give the outputs of the system')
+  end
+%   if isempty(pnames) || ~iscellstr(pnames)
+%     error('### please give the parameter names in a cell-array of strings')
+%   end
+
+  % look for aliases within the models
+  if ~isa(TFmodels,'ssm')
+    aliasNames = TFmodels(1).aliasNames;
+    aliasValues = TFmodels(1).aliasValues;
+    for ii=2:numel(TFmodels)
+      if ~isempty(TFmodels(ii).aliasNames)
+        aliasNames = union(aliasNames,TFmodels(ii).aliasNames);
+      end
+    end
+    if ~isempty(aliasNames)
+      for kk=1:numel(aliasNames)
+        for ii=1:numel(TFmodels)
+          ix = strcmp(TFmodels(ii).aliasNames,aliasNames{kk});
+          if sum(ix)==0
+            continue;
+          else
+            aliasValues{kk} = TFmodels(ii).aliasValues{ix};
+          end
+        end
+      end
+    end
+  end
+
+  % common params set
+  if isa(TFmodels, 'smodel')
+    [TFmodels,pnames,P0] = cat_mdls(TFmodels,pnames,P0);
+  elseif isa(TFmodels, 'matrix')
+    [TFmodels,pnames,P0] = cat_mdls(TFmodels.objs,pnames,P0);
+  end
+  
+%   if isempty(P0) || ~isnumeric(P0) && ~MCsearch
+%     if isa(TFmodels, 'smodel')
+%       if ~isempty(TFmodels(1).values)
+%         P0 = TFmodels.values;
+%         P0 = cell2mat(P0);
+%         if numel(P0)~=numel(TFmodels(1).params)
+%           error('### numbers of parameter values and names do not match')
+%         end
+%       else
+%         error('### please give the initial guess in a numeric array')
+%       end
+%     end
+%     if isa(TFmodels, 'matrix')
+%       if ~isempty(TFmodels.objs(1).values)
+%         P0 = TFmodels.objs(1).values;
+%         P0 = cell2mat(P0);
+%         if numel(P0)~=numel(TFmodels.objs(1).params)
+%           error('### numbers of parameter values and names do not match')
+%         end
+%       else
+%         error('### please give the initial guess in a numeric array')
+%       end
+%     end
+%   end
+  if ~isnumeric(lb) || ~isnumeric(ub)
+    error('### please give lower and upper bounds in a numeric array')
+  end
+  if numel(lb)~=numel(ub)
+    error('### please give lower and upper bounds of the same length')
+  end
+  if isa(TFmodels, 'smodel')
+    pnames = TFmodels(1).params;
+    Np = numel(pnames);
+%     for kk=2:numel(TFmodels)
+%       if numel(TFmodels(kk).params)~=Np
+%         error('### number of parameters must be the same for all transfer function models')
+%       end
+%       if ~strcmp(TFmodels(kk).params,pnames)
+%         error('### all transfer function models must have the same parameters')
+%       end
+%     end
+  end
+  if isa(TFmodels, 'matrix')
+    pnames = TFmodels.objs(1).params;
+    Np = numel(pnames);
+    for kk=2:(TFmodels.nrows*TFmodels.ncols)
+      if numel(TFmodels.objs(kk).params)~=Np
+        error('### number of parameters must be the same for all transfer function models')
+      end
+      if ~strcmp(TFmodels.objs(kk).params,pnames)
+        error('### all transfer function models must have the same parameters')
+      end
+    end
+  end
+  if isa(TFmodels, 'ssm') && isempty(pnames)
+    pnames = getKeys(TFmodels.params);
+    Np = numel(pnames);
+  end
+  
+  % check TFmodels, inputs and outputs
+  Nin = numel(inputs);
+  Nout = numel(outputs);
+  NTFmodels = numel(TFmodels);
+  if isa(TFmodels, 'smodel') & size(TFmodels)~=[Nout,Nin]
+    error('### the size of the transfer function does not match with the number of inputs and outputs')
+  end
+  if size(inputs)~=[Nin,1]
+    inputs = inputs';
+  end
+  if size(outputs)~=[Nout,1]
+    outputs = outputs';
+  end
+  
+  % checks on inputs and outputs consistency
+  inLen = inputs(1).len;
+  inXdata = inputs(1).x;
+  inFs = inputs(1).fs;
+  for kk=2:Nin
+    if inputs(kk).len~=inLen
+      error('### all inputs must have the same length')
+    end
+    if inputs(kk).x~=inXdata
+      error('### x-fields of all inputs must be the same')
+    end
+    if inputs(kk).fs~=inFs
+      error('### fs-fields of all inputs must be the same')
+    end
+  end
+  outLen = outputs(1).len;
+  outXdata = outputs(1).x;
+  outFs = outputs(1).fs;
+  for kk=2:Nout
+    if outputs(kk).len~=outLen
+      error('### all outputs must have the same length')
+    end
+    if outputs(kk).x~=outXdata
+      error('### x-fields of all outputs must be the same')
+    end
+    if outputs(kk).fs~=outFs
+      error('### fs-fields of all outputs must be the same')
+    end
+  end
+  if inLen~=outLen
+    error('### inputs and outputs must have the same length')
+  end
+  if inXdata~=outXdata
+    error('### x-fields of inputs and outputs must be the same')
+  end
+  if inFs~=outFs
+    error('### fs-fields of inputs and outputs must be the same')
+  end
+  
+  % check Whitening Filters
+  Wf = ~isempty(WhFlts);
+  Nwf = numel(WhFlts);
+  if Wf
+    if isempty(Ncut)
+      Ncut = 100;
+    end
+    for ii=1:numel(WhFlts)
+      if ~(isa(WhFlts(ii),'matrix')||isa(WhFlts(ii),'filterbank'))
+        error('### whitening filters must be array of matrix or filterbank class')
+      end
+    end
+    if Nwf~=Nout % size(WhFlts)~=[Nout,Nout]
+      error('### the size of the whitening filter array does not match with the number of outputs to be filtered')
+    end
+    % extract poles and residues
+    B = cell(Nout,1); % cell(Nout,Nin);
+    A = B;
+    for ii=1:Nwf
+      if isa(WhFlts(ii),'matrix')
+        Nflt = max(WhFlts(ii).osize);
+      elseif isa(WhFlts(ii),'filterbank')
+        Nflt = numel(WhFlts(ii).filters);
+      end
+      B{ii} = zeros(Nflt,1);
+      A{ii} = B{ii};
+      for jj=1:Nflt
+        if isa(WhFlts(ii),'matrix')
+          B{ii}(jj) = WhFlts(ii).objs(jj).b(2);
+          A{ii}(jj) = WhFlts(ii).objs(jj).a(1);
+        elseif isa(WhFlts(ii),'filterbank')
+          B{ii}(jj) = WhFlts(ii).filters(jj).b(2);
+          A{ii}(jj) = WhFlts(ii).filters(jj).a(1);
+        end
+      end
+    end
+  end
+
+ 
+  % Number of data before padding
+  Ndata = inLen;
+  fs = inFs;
+%   nsecs = Ndata/fs;
+  
+  % Extract inputs
+  inYdata = inputs.y;
+  if size(inYdata)~=[inLen,Nin]
+    inYdata = inYdata';
+  end
+  outYdata = outputs.y;
+  if size(outYdata)~=[outLen,Nout]
+    outYdata = outYdata';
+  end
+  
+  
+  if isa(TFmodels, 'smodel') || isa(TFmodels, 'matrix')
+    
+    % Zero-pad inputs before parameter estimation.
+    % Pad-ratio is defined as the ratio between the number of zero-padding
+    % points and the data length
+
+    if ~isempty(padRatio)
+      if ~isnumeric(padRatio)
+        error('### please give a numeric pad ratio')
+      else
+        if padRatio~=0
+          Npad = round(padRatio * inLen);
+        else
+          Npad = 0;
+        end
+      end
+    else
+      Npad = 0;
+    end
+
+    NdataPad = Ndata + Npad;
+    Nfft = NdataPad; % 2^nextpow2(NdataPad);
+    Npad = Nfft - Ndata;
+
+    zeroPad = zeros(Npad,Nin);
+    inYdataPad = [inYdata;zeroPad];
+
+    % Fft inputs
+    inFfts = fft(inYdataPad,Nfft);
+    % zero through fs/2
+  %   inFfts = inFfts(1:Nfft/2+1,:);
+
+    % Sample TFmodels on fft-frequencies
+    % zero through fs
+    ff = (0:(Nfft-1))'.*fs./Nfft;
+    % zero through fs/2
+  %   ff = [0:(Nfft/2)]'.*fs/(Nfft/2+1);
+  %   ff = fs/2*linspace(0,1,Nfft/2+1)';
+    for kk=1:NTFmodels
+      TFmodels(kk).setXvar('f');
+      TFmodels(kk).setXvals(ff);
+    end
+    
+    % set values for aliases
+    if ~isempty(aliasNames)
+      for kk=1:numel(aliasNames)
+        aliasValues{kk}.setXvar('f');
+        aliasValues{kk}.setXvals(ff);
+      end
+    end
+    % assign aliases to variables
+    aliasTrue = 0;
+    if ~isempty(aliasNames)
+      alias = cell(numel(aliasNames),1);
+      for kk=1:numel(aliasNames)
+        alias{kk} = aliasValues{kk}.double;
+%         assignin('caller',aliasNames{kk},aliasValues{kk}.double);
+      end
+      aliasTrue = 1;
+    end
+
+    % Extract function_handles from TFmodels
+    TFmodelFuncs = cell(size(TFmodels));
+    for ii=1:NTFmodels
+  %     TFmodelFuncs{ii} = TFmodels(ii).fitfunc;
+  %     TFmodelFuncs{ii} =
+  %     @(x)eval_mdl(TFmodels(ii).expr.s,TFmodels(ii).xvar,TFmodels(ii).xvals,TFmodels(ii).params,x);
+      fcnstr = doSubsPnames(TFmodels(ii).expr.s,TFmodels(ii).params);
+      if aliasTrue
+        fcnstr = doSubsAlias(fcnstr,aliasNames);
+        TFmodelFuncs{ii} = ...
+          eval_mdl_alias(fcnstr,TFmodels(ii).xvar{1},TFmodels(ii).xvals{1},alias);
+      else
+        TFmodelFuncs{ii} = ...
+          eval_mdl(fcnstr,TFmodels(ii).xvar{1},TFmodels(ii).xvals{1});
+      end
+    end
+  
+  end
+  
+  % If requested, compute the analytical gradient
+  if SymDiff || linUnc && (isa(TFmodels, 'smodel') || isa(TFmodels, 'matrix'))
+    % compute symbolic 1st-order differentiation
+    TFmodelDFuncsSmodel = cell(numel(pnames),1);
+    for ll=1:numel(pnames)
+      for ss=1:size(TFmodels,1)
+        for tt=1:size(TFmodels,2)
+          TFmodelDFuncsSmodel{ll}(ss,tt) = diff(TFmodels(ss,tt),pnames{ll});
+        end
+      end
+    end
+    % extract anonymous function
+    TFmodelDFuncs = cell(numel(pnames),1);
+    for ll=1:numel(pnames)
+      TFmodelDFuncs{ll} = cell(size(TFmodels));
+      for ii=1:NTFmodels
+        if ~isempty(TFmodelDFuncsSmodel{ll})
+          fcnstr = doSubsPnames(TFmodelDFuncsSmodel{ll}(ii).expr.s,TFmodelDFuncsSmodel{ll}(ii).params);
+          if aliasTrue
+            fcnstr = doSubsAlias(fcnstr,aliasNames);
+            TFmodelDFuncs{ll}{ii} = ...
+              eval_mdl_alias(fcnstr,TFmodelDFuncsSmodel{ll}(ii).xvar{1},TFmodelDFuncsSmodel{ll}(ii).xvals{1},alias);
+          else
+            TFmodelDFuncs{ll}{ii} = ...
+              eval_mdl(fcnstr,TFmodelDFuncsSmodel{ll}(ii).xvar{1},TFmodelDFuncsSmodel{ll}(ii).xvals{1});
+          end
+        else
+          TFmodelDFuncs{ll}{ii} = @(x)0;
+        end
+      end
+    end
+    if DiffOrder==2
+      % compute symbolic 2nd-order differentiation
+      TFmodelHFuncsSmodel = cell(numel(pnames));
+%       for ii=1:NTFmodels
+%         p = TFmodels(ii).params;
+        for mm=1:numel(pnames)
+          for ll=1:mm
+            for ss=1:size(TFmodels,1)
+              for tt=1:size(TFmodels,2)
+                TFmodelHFuncsSmodel{ll,mm}(ss,tt) = diff(TFmodelDFuncsSmodel{ll}(ss,tt),pnames{mm});
+              end
+            end
+          end
+        end
+%       end
+      % extract anonymous function
+      TFmodelHFuncs = cell(numel(pnames));
+      for mm=1:numel(pnames)
+        for ll=1:mm
+          TFmodelHFuncs{ll,mm} = cell(size(TFmodels));
+          for ii=1:NTFmodels
+            if ~isempty(TFmodelHFuncsSmodel{ll,mm})
+%               TFmodelHFuncs{ll,mm}{ii} = TFmodelHFuncsSmodel{ii}(ll,mm).fitfunc; % inverted indexes are for practicalities
+              fcnstr = doSubsPnames(TFmodelHFuncsSmodel{ll,mm}(ii).expr.s,TFmodelHFuncsSmodel{ll,mm}(ii).params);
+              if aliasTrue
+                fcnstr = doSubsAlias(fcnstr,aliasNames);
+                TFmodelHFuncs{ll,mm}{ii} = ...
+                  eval_mdl_alias(fcnstr,TFmodelHFuncsSmodel{ll,mm}(ii).xvar{1},TFmodelHFuncsSmodel{ll,mm}(ii).xvals{1},alias);
+              else
+                TFmodelHFuncs{ll,mm}{ii} = ...
+                  eval_mdl(fcnstr,TFmodelHFuncsSmodel{ll,mm}(ii).xvar{1},TFmodelHFuncsSmodel{ll,mm}(ii).xvals{1});
+              end
+            else
+              TFmodelHFuncs{ll,mm}{ii} = @(x)0;
+            end
+          end
+        end
+      end
+    end
+  end
+  
+  % Build index for faster computation
+  
+  if isa(TFmodels,'smodel')
+    TFidx = compIdx(inYdata, Nout, TFmodels);
+  end
+  if exist('TFmodelDFuncsSmodel','var')
+    TFDidx = cell(numel(pnames),1);
+    for ll=1:numel(pnames)
+      TFDidx{ll} = compIdx(inYdata, Nout, TFmodelDFuncsSmodel{ll});
+    end
+  end
+  if exist('TFmodelHFuncsSmodel','var')
+    TFHidx = cell(numel(pnames));
+    for mm=1:numel(pnames)
+      for ll=1:mm
+        TFHidx{ll,mm} = compIdx(inYdata, Nout, TFmodelHFuncsSmodel{ll,mm});
+      end
+    end
+  end
+ 
+    
+  % Construct the output function handles from TFmodels as funtion of
+  % parameters
+%   if ~Wf
+    outModelFuncs = cell(Nout,1);
+%     if Nout>1
+    if isa(TFmodels, 'smodel') || isa(TFmodels, 'matrix')
+      for ii=1:Nout
+        if SISO
+          outModelFuncs{ii} = @(x)mdl_fftfilt_SISO(x, inFfts(:,ii), TFmodelFuncs{ii}, Npad);
+        else
+%           outModelFuncs{ii} = @(x)mdl_fftfilt(x, ii, inFfts, TFmodelFuncs, Npad);
+          outModelFuncs{ii} = @(x)mdl_fftfilt2(x, ii, inFfts, size(inFfts), TFmodelFuncs, TFidx, Npad);
+        end
+      end
+    elseif isa(TFmodels, 'ssm')
+      SSMmodels = cell(Nout,1);
+      plsym = cell(Nout,1);
+      for ii=1:Nout
+        plsym{ii} = plist('return outputs', outNames{ii}, ...
+                    'AOS VARIABLE NAMES', inNames{ii}, ...
+                    'AOS', inputs(ii), ...
+                    'reorganize', false, ...
+                    'set', 'for simulate');
+
+%         SSMmodelOptim = TFmodels.reorganize(plsym{ii});
+        SSMmodels{ii} = TFmodels.reorganize(plsym{ii});
+%         SSMmodels{ii} = SSMmodelOptim.clearNumParams;
+
+        outModelFuncs{ii} = @(x)mdl_ssm(x, pnames, inputs(ii), SSMmodels{ii}, plsym{ii});
+      end
+    end
+%     else
+%       outModelFuncs{1} = @(x)mdl_fftfilt(x, ii, inFfts, TFmodelFuncs, Npad);
+%     end
+%   end
+  
+  % In case of symbolic differentiation, construct the output derivatives
+  if SymDiff || linUnc
+    outModelDFuncs = cell(numel(pnames),1);
+    for ll=1:numel(pnames)
+      outModelDFuncs{ll} = cell(Nout,1);
+      for ii=1:Nout
+        if SISO
+          outModelDFuncs{ll}{ii} = @(x)mdl_fftfilt_SISO(x, inFfts(:,ii), TFmodelDFuncs{ll}{ii}, Npad);
+        else
+%           outModelDFuncs{ll}{ii} = @(x)mdl_fftfilt(x, ii, inFfts, TFmodelDFuncs{ll}, Npad);
+          outModelDFuncs{ll}{ii} = @(x)mdl_fftfilt2(x, ii, inFfts, size(inFfts), TFmodelDFuncs{ll}, TFDidx{ll}, Npad);
+        end
+      end
+    end
+    if DiffOrder==2
+      outModelHFuncs = cell(numel(pnames));
+      for mm=1:numel(pnames)
+        for ll=1:mm
+          outModelHFuncs{ll,mm} = cell(Nout,1);
+          for ii=1:Nout
+            if SISO
+              outModelHFuncs{ll,mm}{ii} = @(x)mdl_fftfilt_SISO(x, inFfts(:,ii), TFmodelFuncs{ll,mm}{ii}, Npad);
+            else
+%               outModelHFuncs{ll,mm}{ii} = @(x)mdl_fftfilt(x, ii, inFfts, TFmodelHFuncs{ll,mm}, Npad);
+              outModelHFuncs{ll,mm}{ii} = @(x)mdl_fftfilt2(x, ii, inFfts, size(inFfts), TFmodelHFuncs{ll,mm}, TFHidx{ll,mm}, Npad);
+            end
+          end
+        end
+      end
+    end
+  end
+  
+  % In case the whitening filters are provided do a filtering both on
+  % models and data
+  if Wf
+    % filter models
+    outModelFuncs_w = cell(Nout,1);
+    for ii=1:Nout
+%       outModelFuncs_w{ii} = @(x)filter_mdl(x, B, A, outModelFuncs{ii}, Ncut);
+%       outModelFuncs_w{ii} = @(x)mdl_fftfilt_wf(x, ii, inFfts, TFmodelFuncs, Npad, B, A, Ncut);
+      % off-diagonal
+%       outModelFuncs_w{ii} = @(x)mdl_fftfilt_wf(x, ii, outModelFuncs, B, A, Ncut);
+      % diagonal
+      outModelFuncs_w{ii} = @(x)filter_mdl2(x, B{ii}, A{ii}, outModelFuncs{ii}, Ncut);
+    end
+    % filter model derivatives
+    if SymDiff || linUnc
+      % whitening 1st derivatives
+      outModelDFuncs_w = cell(numel(pnames),1);
+      for ll=1:numel(pnames)
+        outModelDFuncs_w{ll} = cell(Nout,1);
+        for ii=1:Nout
+          % off-diagonal
+%           outModelDFuncs_w{ll}{ii} = @(x)mdl_fftfilt_wf(x, ii, outModelDFuncs{ll}, B, A, Ncut);
+          % diagonal
+          outModelDFuncs_w{ll}{ii} = @(x)filter_mdl2(x, B{ii}, A{ii}, outModelDFuncs{ll}{ii}, Ncut);
+        end
+      end
+      if DiffOrder==2
+        % whitening 2nd derivatives
+        outModelHFuncs_w = cell(numel(pnames));
+        for mm=1:numel(pnames)
+          for ll=1:mm
+            outModelHFuncs_w{ll,mm} = cell(Nout,1);
+            for ii=1:Nout
+              % off-diagonal
+    %           outModelDFuncs_w{ll}{ii} = @(x)mdl_fftfilt_wf(x, ii, outModelDFuncs{ll}, B, A, Ncut);
+              % diagonal
+              outModelHFuncs_w{ll,mm}{ii} = @(x)filter_mdl2(x, B{ii}, A{ii}, outModelHFuncs{ll,mm}{ii}, Ncut);
+            end
+          end
+        end
+      end
+    end
+    % filter data
+    outYdata_w = zeros(size(outYdata));
+    outYdata_w(1:Ncut,:) = [];
+    % off-diagonal
+%     for ii=1:Nout
+%       for jj=1:Nout
+%         outYdata_w(:,ii) = outYdata_w(:,ii) + filter_data(B{ii,jj}, A{ii,jj}, outYdata(:,jj), Ncut);
+%       end
+%     end
+    % diagonal
+    for ii=1:Nout
+      outYdata_w(:,ii) = filter_data(B{ii}, A{ii}, outYdata(:,ii), Ncut);
+    end
+    % set filtered values to pass to xfit
+    for ii=1:Nout
+%       outputs(ii).setY(outYdata_w(:,ii));
+      outputs(ii) = ao(plist('yvals',outYdata_w(:,ii)));
+    end
+  end
+  
+  % set the proper function to pass to xfit
+  if Wf
+    outModelFuncs_4xfit = outModelFuncs_w;
+  else
+    outModelFuncs_4xfit = outModelFuncs;
+  end
+  if SymDiff || linUnc
+      % 1st derivatives
+      outModelDFuncs_4xfit = cell(Nout,1);
+      for ii=1:Nout
+        outModelDFuncs_4xfit{ii} = cell(numel(pnames),1);
+        for ll=1:numel(pnames)
+          if Wf
+            outModelDFuncs_4xfit{ii}{ll} = outModelDFuncs_w{ll}{ii};
+          else
+            outModelDFuncs_4xfit{ii}{ll} = outModelDFuncs{ll}{ii};
+          end
+        end
+      end
+      if DiffOrder==2
+        % 2nd derivatives
+        outModelHFuncs_4xfit = cell(Nout,1);
+        for ii=1:Nout
+          outModelHFuncs_4xfit{ii} = cell(numel(pnames));
+          for mm=1:numel(pnames)
+            for ll=1:mm
+              if Wf
+                outModelHFuncs_4xfit{ii}{ll,mm} = outModelHFuncs_w{ll,mm}{ii};
+              else
+                outModelHFuncs_4xfit{ii}{ll,mm} = outModelHFuncs{ll,mm}{ii};
+              end
+            end
+          end
+        end
+      else
+        outModelHFuncs_4xfit = {};
+      end
+  else
+    outModelDFuncs_4xfit = {};
+    outModelHFuncs_4xfit = {};
+  end
+  
+  % replicate pnames, P0, LB, UB for xfit
+  if Nout>1
+    % pnames
+    pnames_4xfit = cell(1,Nout);
+    for ii=1:Nout
+      pnames_4xfit{ii} = pnames;
+    end
+    % P0
+    P0_4xfit = cell(1,Nout);
+    for ii=1:Nout
+      P0_4xfit{ii} = P0;
+    end
+    % LB
+    if ~isempty(lb)
+      lb_4xfit = cell(1,Nout);
+      for ii=1:Nout
+        lb_4xfit{ii} = lb;
+      end
+    else
+      lb_4xfit = [];
+    end
+    % UB
+    if ~isempty(ub)
+      ub_4xfit = cell(1,Nout);
+      for ii=1:Nout
+        ub_4xfit{ii} = ub;
+      end
+    else
+      ub_4xfit = [];
+    end
+    
+  else
+    pnames_4xfit = pnames;
+    P0_4xfit = P0;
+    lb_4xfit = lb;
+    ub_4xfit = ub;
+  end
+  
+  
+  % do fit with xfit
+  fitpl = plist('Function', outModelFuncs_4xfit, ...
+    'pnames', pnames_4xfit, 'P0', P0_4xfit, 'LB', lb_4xfit, 'UB', ub_4xfit, ...
+    'Algorithm', Algorithm, 'FitUnc', FitUnc, 'UncMtd', UncMtd, 'linUnc', linUnc, 'FastHess', FastHess,...
+    'SymGrad', outModelDFuncs_4xfit, 'SymHess', outModelHFuncs_4xfit,...
+    'MonteCarlo', MCsearch, 'Npoints', Npoints, 'Noptims', Noptims, ...
+    'OPTSET', userOpts, 'estimator', estimator, 'weights', weights);
+
+  % Preliminary Gradient search
+  if GradSearch && exist('TFmodelDFuncs','var')
+    
+    % set new optimization options
+    userOpts1 = optimset(userOpts,'GradObj','on','LargeScale','on','FinDiffType','central');
+%                          'PrecondBandWidth',0,'TolPCG',1e-4);
+    fitpl1 = fitpl.pset('Algorithm','fminunc','OPTSET',userOpts1);
+    
+    % fit
+    params = xfit(outputs, fitpl1);
+    
+    % update initial guess
+    if Nout>1
+      P0_4xfit = cell(1,Nout);
+      for ii=1:Nout
+        P0_4xfit{ii} = params.y;
+      end
+    else
+      P0_4xfit = params.y;
+    end
+    
+    % restore old optimization options
+    fitpl = fitpl.pset('Algorithm',Algorithm,'OPTSET',userOpts,'P0',P0_4xfit);
+    
+    % extract preliminary chain
+    chain = params.chain;
+  end
+  
+  % Final search
+  params = xfit(outputs, fitpl);
+
+  % Make output pest
+  out = copy(params,1);
+  
+  % Concatenate chains and set it
+  if exist('chain','var')
+    chain = [chain;params.chain];
+    out.setChain(chain);
+  end
+  
+  % Set Name and History
+  mdlname = char(TFmodels(1).name);
+  for kk=2:NTFmodels
+    mdlname = strcat(mdlname,[',' char(TFmodels(kk).name)]);
+  end
+  out.name = sprintf('tdfit(%s)', mdlname);
+  out.setNames(pnames);
+  out.addHistory(getInfo('None'), pl, ao_invars(:), [as(:).hist]);
+   
+  % Set outputs
+  if nargout > 0
+    varargout{1} = out;
+  end
+end
+
+%--------------------------------------------------------------------------
+% Included Functions
+%--------------------------------------------------------------------------
+
+function outYdata = mdl_fftfilt(P, outIdx, inFfts, TFmdl, Npad)
+  
+  sz = size(inFfts);
+  outFfts = zeros(sz);
+  for ii=1:sz(2)
+    outFfts(:,ii) = inFfts(:,ii).*TFmdl{outIdx,ii}(P);
+  end
+  outFfts = sum(outFfts,2);
+  outYdata = ifft(outFfts(:,1), 'symmetric');
+  outYdata(end-Npad+1:end,:) = [];
+    
+end
+
+%--------------------------------------------------------------------------
+
+function outYdata = mdl_fftfilt2(P, outIdx, inFfts, sz, TFmdl, TFidx, Npad)
+  
+  TFeval = zeros(sz);
+  for ii=1:sz(2)
+    if TFidx(outIdx,ii)
+      TFeval(:,ii) = TFmdl{outIdx,ii}(P);
+    end
+  end
+  outFfts = inFfts.*TFeval;
+  outFfts = sum(outFfts,2);
+  outYdata = ifft(outFfts(:,1), 'symmetric');
+  outYdata(end-Npad+1:end,:) = [];
+    
+end
+
+%--------------------------------------------------------------------------
+
+function idx = compIdx(inYdata, Nout, mdl)
+
+  % what inputs are actually different from zero
+  b = any(inYdata);
+  b = repmat(b,Nout,1);
+  % what transfer functions are actually different from zero
+  sz = size(mdl);
+  a = zeros(sz);
+  for ii=1:numel(mdl)
+    a(ii) = ~(strcmp(mdl(ii).expr.s,'[]') || strcmp(mdl(ii).expr.s,'0') || strcmp(mdl(ii).expr.s,'0.0'));
+  end
+  % index for computation
+  idx = logical(a.*b);
+  
+end
+
+%--------------------------------------------------------------------------
+
+function outYdata = mdl_fftfilt_SISO(P, inFfts, TFmdl, Npad)
+  
+%   sz = size(inFfts);
+%   outFfts = zeros(sz);
+%   parfor ii=1:sz(2)
+  outFfts = inFfts.*TFmdl(P);
+%   outFfts = sum(outFfts,2);
+  outYdata = ifft(outFfts(:,1), 'symmetric');
+  outYdata(end-Npad+1:end,:) = [];
+    
+end
+
+%--------------------------------------------------------------------------
+
+function  outYdata = mdl_ssm(P, pnames, inputs, model, plsym)
+   
+  model.doSetParameters(pnames,P);
+  model.keepParameters();
+  
+  % to numerical
+  fs = inputs(1).fs;
+%   model.modifyTimeStep(plist('newtimestep',1/fs));
+  model.modifyTimeStep(1/fs);
+  
+  %%% get expected outputs
+  outYdata = simulate(model,plsym);
+  outYdata = outYdata.objs(1).y;
+  
+end
+
+%--------------------------------------------------------------------------
+
+%--------------------------------------------------------------------------
+
+% function outYdata = Dmdl_fftfilt(P, outIdx, Dix, inFfts, TFmdl, Npad)
+%   
+%   sz = size(inFfts);
+%   outFfts = zeros(sz);
+%   for ii=1:sz(2)
+%     outFfts(:,ii) = inFfts(:,ii).*TFmdl{outIdx,ii}{Dix}(P);
+%   end
+%   outFfts = sum(outFfts,2);
+%   outYdata = ifft(outFfts(:,1), 'symmetric');
+%   outYdata(end-Npad+1:end,:) = [];
+%     
+% end
+
+%--------------------------------------------------------------------------
+
+% function outYdata = mdl_fftfilt_wf(P, outIdx, inFfts, TFmdl, Npad, B, A, Ncut)
+%   
+%   sz = size(inFfts);
+%   outFfts = zeros(sz);
+%   for ii=1:sz(2)
+%     outFfts(:,ii) = inFfts(:,ii).*TFmdl{outIdx,ii}(P);
+%   end
+%   outYdata = ifft(outFfts, 'symmetric');
+%   outYdata(end-Npad+1:end,:) = [];
+%   for ii=1:sz(2)
+%     outYdata_w(:,ii) = filter_data(B{outIdx,ii},A{outIdx,ii},outYdata(:,ii), Ncut);
+%   end
+%   outYdata = sum(outYdata_w,2);
+% 
+% %     outYdata_w = zeros(size(outYdata,1),1);
+% %     outYdata_w(1:Ncut) = [];
+% %     for jj=1:sz(2)
+% %       outYdata_w = outYdata_w + filter_data(pol{outIdx,jj}, res{outIdx,jj}, outYdata(:,jj), Ncut);
+% %     end
+% %     outYdata = sum(outYdata_w,2);  
+%     
+% end
+
+%--------------------------------------------------------------------------
+
+% function outYdata = mdl_fftfilt_wf(P, outIdx, TFmdl, B, A, Ncut)
+%   
+%   os = filter_mdl(P, B, A, TFmdl, Ncut);
+%   outYdata = os(:,outIdx);
+%   
+% end
+
+%--------------------------------------------------------------------------
+
+% function os = filter_mdl(P, B, A, oFuncs, Ncut)
+%   
+%   Nout = numel(oFuncs);
+%   for ii=1:Nout
+%     Y(:,ii) = oFuncs{ii}(P);
+%   end
+%   os = zeros(size(Y));
+%   os(1:Ncut,:) = [];
+%   for ii=1:Nout
+%     for jj=1:Nout
+%       os(:,ii) = os(:,ii) + filter_data(B{ii,jj}, A{ii,jj}, Y(:,jj), Ncut);
+%     end
+%   end
+%   
+% end
+
+%--------------------------------------------------------------------------
+
+% function outYdata = Dmdl_fftfilt_wf(P, outIdx, TFmdl, B, A, Ncut)
+%   
+%   os = filter_mdl(P, B, A, TFmdl, Ncut);
+%   outYdata = os(:,outIdx);
+%   
+% end
+
+%--------------------------------------------------------------------------
+
+function o = filter_data(B, A, X, Ncut)
+  
+  N = numel(X);
+  M = numel(B);
+  Y = zeros(N,M);  
+%   parfor ii=1:M
+  for ii=1:M
+    Y(:,ii) = filter(A(ii),[1 B(ii)],X);
+  end    
+  o = sum(Y,2);  
+  o(1:Ncut) = [];
+  
+end
+
+%--------------------------------------------------------------------------
+
+function o = filter_mdl2(P, B, A, oFunc, Ncut)
+  
+  X = oFunc(P);
+  N = numel(X);
+  M = numel(B);
+  Y = zeros(N,M);  
+  for ii=1:M
+    Y(:,ii) = filter(A(ii),[1 B(ii)],X);
+  end    
+  o = sum(Y,2);  
+  o(1:Ncut) = [];
+  
+end
+
+%--------------------------------------------------------------------------
+
+function fcn = eval_mdl(str,xvar,xvals)
+
+  fcn = eval(['@(P,',xvar,')(',str,')']);
+  fcn = @(x)fcn(x,xvals);  
+  
+end
+
+%--------------------------------------------------------------------------
+
+function fcn = eval_mdl_alias(str,xvar,xvals,alias)
+
+  fcn = eval(['@(P,',xvar,',alias',')(',str,')']);
+  fcn = @(x)fcn(x,xvals,alias);  
+  
+end
+%--------------------------------------------------------------------------
+
+function str = doSubsPnames(str,params)
+  
+  lengths = zeros(1,numel(params));
+  for ii=1:numel(params)
+    lengths(ii) = length(params{ii});
+  end
+  [dummy,ix] = sort(lengths,'descend');
+  repstr = cell(1,numel(params));
+  for ii=1:numel(params)
+    repstr{ii} = ['P(' int2str(ii) ')'];
+  end
+  str = regexprep(str,params(ix),repstr(ix));
+  if isempty(str)
+    str = '0';
+  end
+  
+end
+
+%--------------------------------------------------------------------------
+
+function str = doSubsAlias(str,alias)
+  
+  lengths = zeros(1,numel(alias));
+  for ii=1:numel(alias)
+    lengths(ii) = length(alias{ii});
+  end
+  [dummy,ix] = sort(lengths,'descend');
+  repstr = cell(1,numel(alias));
+  for ii=1:numel(alias)
+    repstr{ii} = ['alias{' int2str(ii) '}'];
+  end
+  str = regexprep(str,alias(ix),repstr(ix));
+ 
+end
+
+%--------------------------------------------------------------------------
+
+function [newMdls,pnames,P0]=cat_mdls(mdls,usedParams,P0)
+
+  pnames = mdls(1).params;
+
+  % union of all parameters
+  for ii=2:numel(mdls)
+    pnames = union(pnames,mdls(ii).params);
+  end
+  
+  pvalues = cell(1,numel(pnames));
+  for kk=1:numel(pnames)
+    for ii=1:numel(mdls)
+      ix = strcmp(mdls(ii).params,pnames{kk});
+      if sum(ix)==0
+        continue;
+      else
+        pvalues{kk} = mdls(ii).values{ix};
+      end
+    end
+  end
+  
+  % set the used one
+  for ii=1:numel(mdls)
+    mdls(ii).setParams(pnames,pvalues);
+    if ~isempty(usedParams)
+      mdls(ii).subs(setdiff(pnames,usedParams));
+    else
+      usedParams = pnames;
+    end
+  end
+  
+  % copy to new models
+  for ii=1:size(mdls,1)
+    for jj=1:size(mdls,2)
+      newMdls(ii,jj) = smodel(plist('expression',mdls(ii,jj).expr,...
+        'xvar',mdls(ii,jj).xvar,'xvals',mdls(ii,jj).xvals,...
+        'name',mdls(ii,jj).name));
+    end
+  end
+  
+  % set the same parameters for all
+  for ii=1:numel(newMdls)
+    if ~isempty(P0)
+      newMdls(ii).setParams(usedParams,P0);
+    else
+      for kk=1:numel(usedParams)
+      ix = strcmp(usedParams(kk),pnames);
+      P0(kk) = pvalues{ix};
+      end
+      newMdls(ii).setParams(usedParams,P0);
+    end
+  end
+  
+  pnames = usedParams;
+
+end
+
+%--------------------------------------------------------------------------
+% Get Info Object
+%--------------------------------------------------------------------------
+function ii = getInfo(varargin)
+  if nargin == 1 && strcmpi(varargin{1}, 'None')
+    sets = {};
+    pl   = [];
+  else
+    sets = {'Default'};
+    pl   = getDefaultPlist;
+  end
+  % Build info object
+  ii = minfo(mfilename, 'ao', 'ltpda', utils.const.categories.sigproc, '$Id: tdfit.m,v 1.45 2011/05/26 12:57:27 congedo Exp $', sets, pl);
+  ii.setModifier(false);
+end
+
+%--------------------------------------------------------------------------
+% Get Default Plist
+%--------------------------------------------------------------------------
+function plout = getDefaultPlist()
+  persistent pl;  
+  if exist('pl', 'var')==0 || isempty(pl)
+    pl = buildplist();
+  end
+  plout = pl;  
+end
+
+function pl = buildplist()
+  
+  pl = plist();
+  
+  % Inputs
+  p = param({'Inputs', 'An array of input AOs, one per each experiment.'}, paramValue.EMPTY_DOUBLE);
+  pl.append(p);
+ 
+  % Models
+  p = param({'Models', 'An array of transfer function SMODELs.'}, paramValue.EMPTY_DOUBLE);
+  pl.append(p);
+  
+  % PadRatio
+  p = param({'PadRatio', ['PadRatio is defined as the ratio between the number of zero-pad points '...
+    'and the data length.<br>'...
+    'Define how much to zero-pad data after the signal.<br>'...
+    'Being <tt>tdfit</tt> a fft-based algorithm, no zero-padding might bias the estimation, '...
+    'therefore it is strongly suggested to do that.']}, 1);
+  pl.append(p);
+  
+  % Whitening Filters
+   p = param({'WhFlts', 'An array of FILTERBANKs containing the whitening filters per each output AO.'}, paramValue.EMPTY_DOUBLE);
+  pl.append(p);
+  
+  % Parameters
+  p = param({'Pnames', 'A cell-array of parameter names to fit.'}, paramValue.EMPTY_CELL);
+  pl.append(p);
+  
+  % P0
+  p = param({'P0', 'An array of starting guesses for the parameters.'}, paramValue.EMPTY_DOUBLE);
+  pl.append(p);
+  
+  % LB
+  p = param({'LB', ['Lower bounds for the parameters.<br>'...
+    'This improves convergency. Mandatory for Monte Carlo.']}, paramValue.EMPTY_DOUBLE);
+  pl.append(p);
+  
+  % UB
+  p = param({'UB', ['Upper bounds for the parameters.<br>'...
+    'This improves the convergency. Mandatory for Monte Carlo.']}, paramValue.EMPTY_DOUBLE);
+  pl.append(p);
+  
+  % Algorithm
+  p = param({'ALGORITHM', ['A string defining the fitting algorithm.<br>'...
+    '<tt>fminunc</tt>, <tt>fmincon</tt> require ''Optimization Toolbox'' to be installed.<br>'...
+    '<tt>patternsearch</tt>, <tt>ga</tt>, <tt>simulannealbnd</tt> require ''Genetic Algorithm and Direct Search'' to be installed.<br>']}, ...
+    {1, {'fminsearch', 'fminunc', 'fmincon', 'patternsearch', 'ga', 'simulannealbnd'}, paramValue.SINGLE});
+  pl.append(p);
+
+  % OPTSET
+  p = param({'OPTSET', ['An optimisation structure to pass to the fitting algorithm.<br>'...
+    'See <tt>fminsearch</tt>, <tt>fminunc</tt>, <tt>fmincon</tt>, <tt>optimset</tt>, for details.<br>'...
+    'See <tt>patternsearch</tt>, <tt>psoptimset</tt>, for details.<br>'... 
+    'See <tt>ga</tt>, <tt>gaoptimset</tt>, for details.<br>'...
+    'See <tt>simulannealbnd</tt>, <tt>saoptimset</tt>, for details.']}, paramValue.EMPTY_STRING);
+  pl.append(p);
+  
+  % SymDiff
+  p = param({'SymDiff', 'Use symbolic derivatives or not. Only for gradient-based algorithm or for LinUnc option.'}, paramValue.NO_YES);
+  pl.append(p);
+  
+  % DiffOrder
+  p = param({'DiffOrder', 'Symbolic derivative order. Only for SymDiff option.'}, {1, {1,2}, paramValue.SINGLE});
+  pl.append(p);
+  
+  % FitUnc
+  p = param({'FitUnc', 'Fit parameter uncertainties or not.'}, paramValue.YES_NO);
+  pl.append(p);
+  
+  % UncMtd
+  p = param({'UncMtd', ['Choose the uncertainties estimation method.<br>'...
+    'For multi-channel fitting <tt>hessian</tt> is mandatory.']}, {1, {'hessian', 'jacobian'}, paramValue.SINGLE});
+  pl.append(p);
+  
+  % LinUnc
+  p = param({'LinUnc', 'Force linear symbolic uncertainties.'}, paramValue.YES_NO);
+  pl.append(p);
+  
+  % GradSearch
+  p = param({'GradSearch', 'Do a preliminary gradient-based search using the BFGS Quasi-Newton method.'}, paramValue.NO_YES);
+  pl.append(p);
+  
+  % MonteCarlo
+  p = param({'MonteCarlo', ['Do a Monte Carlo search in the parameter space.<br>'...
+    'Useful when dealing with high multiplicity of local minima. May be computer-expensive.<br>'...
+    'Note that, if used, P0 will be ignored. It also requires to define LB and UB.']}, paramValue.NO_YES);
+  pl.append(p);
+  
+  % Npoints
+  p = param({'Npoints', 'Set the number of points in the parameter space to be extracted.'}, 100000);
+  pl.append(p);
+  
+  % Noptims
+  p = param({'Noptims', 'Set the number of optimizations to be performed after the Monte Carlo.'}, 10);
+  pl.append(p);
+  
+  % SISO
+  p = param({'SingleInputSingleOutput', 'Specify whether the model should be considered as Single-Input/Single-Output model. This is for performance.'}, paramValue.NO_YES);
+  pl.append(p);
+  
+end
+% END