Mercurial > hg > ltpda
diff m-toolbox/classes/@ao/bilinfit.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/bilinfit.m Wed Nov 23 19:22:13 2011 +0100 @@ -0,0 +1,286 @@ +% BILINFIT is a linear fitting tool +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% DESCRIPTION: BILINFIT linear fitting tool based on MATLAB's lscov +% function. It solves an equation in the form +% +% Y = X(1) * P(1) + X(2) * P(2) + ... + P(N+1) +% +% for the fit parameters P. It handles an arbitrary number of input vectors +% and uncertainties on the dependent vector Y and input vectors X(1..N). +% 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 = bilinfit(X1, X2, .., XN, Y, PL) +% +% INPUTS: Y - dependent variable +% X(1..N) - input variables +% PL - parameter list +% +% OUTPUT: P - a pest object with the N+1 elements +% +% +% PARAMETERS: +% 'dy' - uncertainty on the dependent variable +% 'dx' - uncertainties on the input variables +% 'p0' - initial guess on the fit parameters to propagate uncertainities +% in the input variables X(1..N) to the dependent variable Y +% +% <a href="matlab:utils.helper.displayMethodInfo('ao', 'bilinfit')">Parameters Description</a> +% +% VERSION: $Id: bilinfit.m,v 1.20 2011/04/08 08:56:11 hewitson Exp $ +% +% EXAMPLES: +% +% % 1) Determine the coefficients of a linear combination of noises: +% +% % Make some data +% fs = 10; +% nsecs = 10; +% x1 = ao(plist('tsfcn', 'randn(size(t))', 'fs', fs, 'nsecs', nsecs, 'yunits', 'm')); +% x2 = ao(plist('tsfcn', 'randn(size(t))', 'fs', fs, 'nsecs', nsecs, 'yunits', 'm')); +% n = ao(plist('tsfcn', 'randn(size(t))', 'fs', fs, 'nsecs', nsecs, 'yunits', 'm')); +% c = [ao(1,plist('yunits','m/m')) ao(2,plist('yunits','m/m'))]; +% y = c(1)*x1 + c(2)*x2 + n; +% y.simplifyYunits; +% +% % Get a fit for the c coefficients and a constant term +% p = bilinfit(x1, x2, y) +% +% % Do linear combination: using eval +% pl_split = plist('times', [1 5]); +% yfit = eval(p, split(x1, pl_split), split(x2, pl_split)); +% +% % Plot (compare data with fit) +% iplot(y, yfit, plist('Linestyles', {'-','--'})) +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +function varargout = bilinfit(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); + pl = utils.helper.collect_objects(varargin(:), 'plist', in_names); + + if nargout == 0 + error('### bilinfit can not be used as a modifier method. Please give at least one output'); + end + + if numel(aos) < 2 + error('### bilinfit needs at least 2 inputs AOs'); + end + + % combine plists + pl = parse(pl, getDefaultPlist()); + + % extract parameters + dy = find(pl, 'dy'); + dx = find(pl, 'dx'); + p0 = find(pl, 'p0'); + + % collect inputs + Y = aos(end); + X = aos(1:end-1); + + % collect inputs names + argsname = aos(1).name; + for jj = 2:numel(aos) + argsname = [argsname ',' aos(jj).name]; + end + + % get data from AOs + x = X(:).y; + y = Y.y; + + % vectors length + N = length(y); + + % uncertainty on Y + if isempty(dy) + dy = 1; + end + if isa(dy, 'ao') + % check units + if Y.yunits ~= dy.yunits + error('### Y and DY units are not compatible - %s %s', char(Y.yunits), char(dy.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 + + % squares + sigma2 = dy.^2; + sigma2y_rms = sqrt(sum(sigma2)/N); + + % extract values for initial guess + if (isa(p0, 'ao') || isa(p0, 'pest')) + p0 = p0.y; + end + + % uncertainty on X + if ~isempty(dx) + + for k = 1:length(dx) + dxi = dx(k); + + if ~isempty(dxi) + if isa(dxi, 'ao') + % check units + if X(k).yunits ~= dxi.yunits + error('### X and DX units are not compatible - %s %s', char(X.yunits), char(dxi.yunits)); + end + % extract values from AO + dxi = dxi.y; + end + if isscalar(dxi) + % given a single value construct a vector + dxi = ones(N, 1) * dxi; + end + + % squares + sigma2xi = dxi.^2; + + % if A0 guess are not given + if isempty(p0(k)) + % set it to obtain equal error contribution to the Y error + sigma2xi_rms = sqrt(sum(sigma2xi)/N); + p0(k) = sigma2y_rms/sigma2xi_rms; + end + + % add contribution to weights + sigma2 = sigma2 + sigma2xi * p0(k)^2; + end + + end + end + + % constant term + c = ones(N, 1); + + % build matrix + m = [x c]; + + % solve + [p, stdx, mse, s] = lscov(m, y, 1./sigma2); + + % scale errors and covariance matrix + stdp = stdx ./ sqrt(mse); + s = s ./ mse; + + % compute chi2 + dof = N - length(p); + chi2 = sum((y - lincom(m, p)).^2 ./ sigma2) / dof; + + % prepare model, units, names + model = []; + for kk = 1:length(p) + switch kk + case 1 + units(kk) = simplify(Y.yunits/X(kk).yunits); + model = ['P' num2str(kk) '*X' num2str(kk)]; + xvar{kk} = ['X' num2str(kk)]; + xunits{kk} = X(kk).yunits; + case length(p) + units(kk) = Y.yunits; + model = [model ' + P' num2str(kk)]; + otherwise + units(kk) = simplify(Y.yunits/X(kk).yunits); + model = [model ' + P' num2str(kk) '*X' num2str(kk)]; + xvar{kk} = ['X' num2str(kk)]; + xunits{kk} = X(kk).yunits; + end + names{kk} = ['P' num2str(kk)]; + + end + model = smodel(plist('expression', model, ... + 'params', names, ... + 'values', p, ... + 'xvar', xvar, ... + 'xunits', xunits, ... + 'yunits', Y.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('bilinfit(%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 linear combination +function out = lincom(x, p) + assert(size(x, 2) == length(p)); + out = zeros(size(x, 1), 1); + for k = 1:length(p) + out = out + x(:,k) * p(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: bilinfit.m,v 1.20 2011/04/08 08:56:11 hewitson Exp $', sets, pl); + ii.setModifier(false); + ii.setArgsmin(2); +end + +% get default plist + +function plout = getDefaultPlist() + persistent pl; + if ~exist('pl', 'var') || isempty(pl) + pl = buildplist(); + end + plout = pl; +end + +function pl = buildplist() + + % default plist for linear fitting + pl = plist.MULTILINEAR_FIT_PLIST; + +end