diff m-toolbox/classes/@ao/polynomfit.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/polynomfit.m	Wed Nov 23 19:22:13 2011 +0100
@@ -0,0 +1,317 @@
+% POLYNOMFIT is a polynomial fitting tool
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%  
+% DESCRIPTION: POLYNOMFIT is a polynomial fitting tool based on MATLAB's
+% lscov function. It solves an equation in the form
+%
+%     Y = P(1) * X^N(1) + P(2) * X^N(2) + ...
+%
+% for the fit parameters P. It handles arbitrary powers of the input vector
+% and uncertainties on the dependent vector Y and input vectors X. 
+% The output is a pest object where the fields are containing:
+% Quantity                              % Field
+% Fit coefficients                          y
+% Uncertainties on the fit parameters
+% (given as standard deviations)            dy
+% The reduced CHI2 of the fit              chi2
+% The covariance matrix                    cov
+% The degrees of freedom of the fit        dof
+%  
+% CALL:       P = polynomfit(X, Y, PL)
+%             P = polynomfit(A, PL)
+%  
+% INPUTS:     Y   - dependent variable
+%             X   - input variables
+%             A   - data ao whose x and y fields are used in the fit
+%             PL  - parameter list
+%  
+% OUTPUT:     P   - a pest object with M = numel(N) fitting coefficients
+%
+%
+% PARAMETERS:
+%    'orders' - polynom orders. Eg [0,1,-2] fits to P0 + P1*x + P2./x.^2
+%    'dy'     - uncertainty on the dependent variable
+%    'dx'     - uncertainties on the input variable
+%    'p0'     - initial guess on the fit parameters used ONLY to propagate
+%               uncertainities in the input variable X to the dependent variable Y
+%
+% <a href="matlab:utils.helper.displayMethodInfo('ao', 'polynomfit')">Parameters Description</a>
+%
+% VERSION:     $Id: polynomfit.m,v 1.20 2011/04/08 08:56:11 hewitson Exp $
+%
+% EXAMPLES:
+%
+% % 1) Fit with one object input
+% 
+% nsecs = 5;
+% fs    = 10;
+% n       = [0 1 -2];
+% u1 = unit('mV');
+% 
+% pl1 = plist('nsecs', nsecs, 'fs', fs, ...
+%   'tsfcn', sprintf('t.^%d + t.^%d + t.^%d + randn(size(t))', n), ...
+%   'xunits', 's', 'yunits', u1);
+% a1 = ao(pl1);
+% out1 = polynomfit(a1, plist('orders', n, 'dx', 0.1, 'dy', 0.1, 'P0', zeros(size(n))));
+%
+% % 2) Fit with two objects input
+% 
+% fs      =  1;
+% nsecs   = 10;
+% n       = [0 1 -2];
+% 
+% X = ao(plist('tsfcn', 'randn(size(t))', 'fs', fs, 'nsecs', nsecs, 'yunits', 'm', 'name', 'base'));
+% N = ao(plist('tsfcn', 'randn(size(t))', 'fs', fs, 'nsecs', nsecs, 'yunits', 'm', 'name', 'noise'));
+% C = [ao(1, plist('yunits', 'm', 'name', 'C1')) ...
+%      ao(4, plist('yunits', 'm/m', 'name', 'C2')) ...
+%      ao(2, plist('yunits', 'm/m^(-2)', 'name', 'C3'))];
+% Y = C(1) * X.^0 + C(2) * X.^1 + C(3) * X.^(-2) + N;
+% Y.simplifyYunits;
+% out2 = polynomfit(X, Y, plist('orders', n))
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+function varargout = polynomfit(varargin)
+  
+  % check if this is a call for parameters
+  if utils.helper.isinfocall(varargin{:})
+    varargout{1} = getInfo(varargin{3});
+    return
+  end
+ 
+  % tell the system we are runing
+  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
+  [aos, ao_invars] = utils.helper.collect_objects(varargin(:), 'ao', in_names);
+  pli              = utils.helper.collect_objects(varargin(:), 'plist', in_names);
+  
+  if nargout == 0
+    error('### polynomfit can not be used as a modifier method. Please give at least one output');
+  end
+  
+  % combine plists, making sure the user input is not empty
+  pli = combine(pli, plist());
+  pl = parse(pli, getDefaultPlist());
+
+  % extract arguments
+  if (length(aos) == 1)
+    % we are using x and y fields of the single ao we have
+    x = aos(1).x;
+    dx = aos(1).dx;
+    y = aos(1).y;
+    dy = aos(1).dy;
+    xunits = aos(1).xunits;
+    yunits = aos(1).yunits;
+    argsname = aos(1).name;
+  elseif (length(aos) == 2)
+    % we are using y fields of the two aos we have
+    x = aos(1).y;
+    dx = aos(1).dy;
+    y = aos(2).y;
+    dy = aos(2).dy;
+    xunits = aos(1).yunits;
+    yunits = aos(2).yunits;
+    argsname = [aos(1).name ',' aos(2).name];
+  else
+    error('### polynomfit needs one or two input AOs');
+  end
+  
+  % extract plist parameters. For dx and dy we check the user input plist before  
+  dy = find(pli, 'dy', dy);
+  dx = find(pli, 'dx', dx);
+  n  = find(pl, 'orders');
+  p0 = find(pl, 'p0');
+  
+  % vectors length
+  N = length(y);
+  
+  % number of parameters
+  num_params = length(n);
+  
+  % uncertainty on Y  
+  if isempty(dy)
+    dy = 1;    
+  end
+  if isa(dy, 'ao')
+    % check units
+    if yunits ~= dy.data.yunits
+      error('### Y and DY units are not compatible - %s %s', char(yunits), char(dy.data.yunits));
+    end
+    % extract values from AO
+    dy = dy.y;
+  end
+  if isscalar(dy)
+    % given a single value construct a vector
+    dy = ones(N, 1) * dy;
+  end
+  
+  % weights
+  sigma2 = dy.^2;
+  
+  % extract values for initial guess
+  if (isa(p0, 'ao') || isa(p0, 'pest'))
+    p0 = p0.y;
+  end
+
+  % uncertainty on X
+  if ~isempty(dx)    
+    
+    if length(p0) ~= num_params
+      error('### initial parameters guess p0 is mandatory for proper handling of X uncertainties');
+    end
+    
+    if isa(dx, 'ao')
+      % check units
+      if xunits ~= dx.data.yunits
+        error('### X and DX units are not compatible - %s %s', char(xunits), char(dx.data.yunits));
+      end
+      % extract values from AO
+      dx = dx.y;
+    end
+    if isscalar(dx)
+      % given a single value construct a vector
+      dx = ones(N, 1) * dx;
+    end
+
+    % propagate X uncertainty on Y
+    dy_dx_mod = zeros(N, 1);
+    for k = 1:num_params
+      dy_dx_mod = dy_dx_mod + n(k) * p0(k) * x.^(n(k)-1);
+    end
+    sigma2x = dy_dx_mod.^2 .* dx.^2;
+    
+    % add contribution to weights
+    sigma2 = sigma2 + sigma2x;
+  
+  end
+      
+  % construct matrix with desired powers of X
+  m = zeros(length(x), num_params);
+  for k = 1:num_params
+    m(:,k) = x .^ n(k);
+  end
+
+  % check for the presence of 1/0 terms
+  M = [];
+  X = [];
+  Y = [];
+  S = [];
+  kk = 0;
+  
+  idx = isfinite(m);
+  for jj = 1:size(m,1)
+    if all(idx(jj,:))
+      kk = kk + 1; 
+      M(kk,:) = m(jj,:);
+      X(kk) = x(jj);
+      Y(kk) = y(jj);
+      S(kk,:) = sigma2(jj,:);
+    end
+  end
+  m = M; clear M;
+  x = X'; clear X;
+  y = Y'; clear Y;
+  sigma2 = S; clear S;
+  N = kk;
+  
+  % solve
+  [p, stdp, mse, s] = lscov(m, y, 1./sigma2);
+
+  % scale errors
+  stdp = stdp ./ sqrt(mse);
+  s    = s ./ (mse);
+
+  % compute chi2
+  dof = N - length(p);
+  chi2 = sum((y - polynomeval(x, n, p)).^2 ./ sigma2) / dof;
+
+  % prepare model, units, names
+  model = [];
+  for kk = 1:length(p)
+    if kk == 1      
+      model = [model 'P' num2str(kk) '*X.^(' num2str(n(kk)) ')'];
+    else      
+      model = [model ' + P' num2str(kk) '*X.^(' num2str(n(kk)) ')'];
+    end
+    units(kk) = simplify(yunits/xunits.^(n(kk))); 
+    names{kk} = ['P' num2str(kk)];
+  end
+  model = smodel(plist('expression', model, ...
+    'params', names, ...
+    'values', p, ...
+    'xvar', 'X', ...
+    'xunits', xunits, ...
+    'yunits', yunits ...
+    ));
+  
+  
+  % Build the output pest object
+  out = pest;
+  out.setY(p);
+  out.setDy(stdp);
+  out.setCov(s);
+  out.setChi2(chi2);
+  out.setDof(dof);
+  out.setNames(names{:});
+  out.setYunits(units);
+  out.setModels(model);
+  out.name = sprintf('polynomfit(%s)', argsname);
+  out.addHistory(getInfo('None'), pl,  ao_invars, [aos(:).hist]);
+  % Set procinfo object
+  out.procinfo = plist('MSE', mse);
+  
+  % set outputs
+  varargout{1} = out;
+  
+end
+
+% computes polynomial combination
+function out = polynomeval(x, n, p)
+  assert(length(p) == length(n));
+  out = zeros(size(x, 1), 1);
+  for k = 1:length(n)
+    out = out + p(k) * x.^n(k);
+  end
+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.op, '$Id: polynomfit.m,v 1.20 2011/04/08 08:56:11 hewitson Exp $', sets, pl);
+  ii.setModifier(false);
+  ii.setArgsmin(1);
+end
+
+% get default plist
+function plout = getDefaultPlist()
+  persistent pl;  
+  if ~exist('pl', 'var') || isempty(pl)
+    pl = buildplist();
+  end
+  plout = pl;  
+end
+
+function pl = buildplist()
+  pl = plist();
+  
+  % orders
+  p = param({'orders', 'Polynom orders.'}, [0]);
+  pl.append(p);
+  
+  % default plist for linear fitting
+  pl.append(plist.LINEAR_FIT_PLIST);
+  
+end