comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:f0afece42f48
1 % POLYNOMFIT is a polynomial fitting tool
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %
4 % DESCRIPTION: POLYNOMFIT is a polynomial fitting tool based on MATLAB's
5 % lscov function. It solves an equation in the form
6 %
7 % Y = P(1) * X^N(1) + P(2) * X^N(2) + ...
8 %
9 % for the fit parameters P. It handles arbitrary powers of the input vector
10 % and uncertainties on the dependent vector Y and input vectors X.
11 % The output is a pest object where the fields are containing:
12 % Quantity % Field
13 % Fit coefficients y
14 % Uncertainties on the fit parameters
15 % (given as standard deviations) dy
16 % The reduced CHI2 of the fit chi2
17 % The covariance matrix cov
18 % The degrees of freedom of the fit dof
19 %
20 % CALL: P = polynomfit(X, Y, PL)
21 % P = polynomfit(A, PL)
22 %
23 % INPUTS: Y - dependent variable
24 % X - input variables
25 % A - data ao whose x and y fields are used in the fit
26 % PL - parameter list
27 %
28 % OUTPUT: P - a pest object with M = numel(N) fitting coefficients
29 %
30 %
31 % PARAMETERS:
32 % 'orders' - polynom orders. Eg [0,1,-2] fits to P0 + P1*x + P2./x.^2
33 % 'dy' - uncertainty on the dependent variable
34 % 'dx' - uncertainties on the input variable
35 % 'p0' - initial guess on the fit parameters used ONLY to propagate
36 % uncertainities in the input variable X to the dependent variable Y
37 %
38 % <a href="matlab:utils.helper.displayMethodInfo('ao', 'polynomfit')">Parameters Description</a>
39 %
40 % VERSION: $Id: polynomfit.m,v 1.20 2011/04/08 08:56:11 hewitson Exp $
41 %
42 % EXAMPLES:
43 %
44 % % 1) Fit with one object input
45 %
46 % nsecs = 5;
47 % fs = 10;
48 % n = [0 1 -2];
49 % u1 = unit('mV');
50 %
51 % pl1 = plist('nsecs', nsecs, 'fs', fs, ...
52 % 'tsfcn', sprintf('t.^%d + t.^%d + t.^%d + randn(size(t))', n), ...
53 % 'xunits', 's', 'yunits', u1);
54 % a1 = ao(pl1);
55 % out1 = polynomfit(a1, plist('orders', n, 'dx', 0.1, 'dy', 0.1, 'P0', zeros(size(n))));
56 %
57 % % 2) Fit with two objects input
58 %
59 % fs = 1;
60 % nsecs = 10;
61 % n = [0 1 -2];
62 %
63 % X = ao(plist('tsfcn', 'randn(size(t))', 'fs', fs, 'nsecs', nsecs, 'yunits', 'm', 'name', 'base'));
64 % N = ao(plist('tsfcn', 'randn(size(t))', 'fs', fs, 'nsecs', nsecs, 'yunits', 'm', 'name', 'noise'));
65 % C = [ao(1, plist('yunits', 'm', 'name', 'C1')) ...
66 % ao(4, plist('yunits', 'm/m', 'name', 'C2')) ...
67 % ao(2, plist('yunits', 'm/m^(-2)', 'name', 'C3'))];
68 % Y = C(1) * X.^0 + C(2) * X.^1 + C(3) * X.^(-2) + N;
69 % Y.simplifyYunits;
70 % out2 = polynomfit(X, Y, plist('orders', n))
71 %
72 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
73
74 function varargout = polynomfit(varargin)
75
76 % check if this is a call for parameters
77 if utils.helper.isinfocall(varargin{:})
78 varargout{1} = getInfo(varargin{3});
79 return
80 end
81
82 % tell the system we are runing
83 import utils.const.*
84 utils.helper.msg(msg.PROC3, 'running %s/%s', mfilename('class'), mfilename);
85
86 % collect input variable names
87 in_names = cell(size(varargin));
88 for ii = 1:nargin,in_names{ii} = inputname(ii);end
89
90 % collect all AOs and plists
91 [aos, ao_invars] = utils.helper.collect_objects(varargin(:), 'ao', in_names);
92 pli = utils.helper.collect_objects(varargin(:), 'plist', in_names);
93
94 if nargout == 0
95 error('### polynomfit can not be used as a modifier method. Please give at least one output');
96 end
97
98 % combine plists, making sure the user input is not empty
99 pli = combine(pli, plist());
100 pl = parse(pli, getDefaultPlist());
101
102 % extract arguments
103 if (length(aos) == 1)
104 % we are using x and y fields of the single ao we have
105 x = aos(1).x;
106 dx = aos(1).dx;
107 y = aos(1).y;
108 dy = aos(1).dy;
109 xunits = aos(1).xunits;
110 yunits = aos(1).yunits;
111 argsname = aos(1).name;
112 elseif (length(aos) == 2)
113 % we are using y fields of the two aos we have
114 x = aos(1).y;
115 dx = aos(1).dy;
116 y = aos(2).y;
117 dy = aos(2).dy;
118 xunits = aos(1).yunits;
119 yunits = aos(2).yunits;
120 argsname = [aos(1).name ',' aos(2).name];
121 else
122 error('### polynomfit needs one or two input AOs');
123 end
124
125 % extract plist parameters. For dx and dy we check the user input plist before
126 dy = find(pli, 'dy', dy);
127 dx = find(pli, 'dx', dx);
128 n = find(pl, 'orders');
129 p0 = find(pl, 'p0');
130
131 % vectors length
132 N = length(y);
133
134 % number of parameters
135 num_params = length(n);
136
137 % uncertainty on Y
138 if isempty(dy)
139 dy = 1;
140 end
141 if isa(dy, 'ao')
142 % check units
143 if yunits ~= dy.data.yunits
144 error('### Y and DY units are not compatible - %s %s', char(yunits), char(dy.data.yunits));
145 end
146 % extract values from AO
147 dy = dy.y;
148 end
149 if isscalar(dy)
150 % given a single value construct a vector
151 dy = ones(N, 1) * dy;
152 end
153
154 % weights
155 sigma2 = dy.^2;
156
157 % extract values for initial guess
158 if (isa(p0, 'ao') || isa(p0, 'pest'))
159 p0 = p0.y;
160 end
161
162 % uncertainty on X
163 if ~isempty(dx)
164
165 if length(p0) ~= num_params
166 error('### initial parameters guess p0 is mandatory for proper handling of X uncertainties');
167 end
168
169 if isa(dx, 'ao')
170 % check units
171 if xunits ~= dx.data.yunits
172 error('### X and DX units are not compatible - %s %s', char(xunits), char(dx.data.yunits));
173 end
174 % extract values from AO
175 dx = dx.y;
176 end
177 if isscalar(dx)
178 % given a single value construct a vector
179 dx = ones(N, 1) * dx;
180 end
181
182 % propagate X uncertainty on Y
183 dy_dx_mod = zeros(N, 1);
184 for k = 1:num_params
185 dy_dx_mod = dy_dx_mod + n(k) * p0(k) * x.^(n(k)-1);
186 end
187 sigma2x = dy_dx_mod.^2 .* dx.^2;
188
189 % add contribution to weights
190 sigma2 = sigma2 + sigma2x;
191
192 end
193
194 % construct matrix with desired powers of X
195 m = zeros(length(x), num_params);
196 for k = 1:num_params
197 m(:,k) = x .^ n(k);
198 end
199
200 % check for the presence of 1/0 terms
201 M = [];
202 X = [];
203 Y = [];
204 S = [];
205 kk = 0;
206
207 idx = isfinite(m);
208 for jj = 1:size(m,1)
209 if all(idx(jj,:))
210 kk = kk + 1;
211 M(kk,:) = m(jj,:);
212 X(kk) = x(jj);
213 Y(kk) = y(jj);
214 S(kk,:) = sigma2(jj,:);
215 end
216 end
217 m = M; clear M;
218 x = X'; clear X;
219 y = Y'; clear Y;
220 sigma2 = S; clear S;
221 N = kk;
222
223 % solve
224 [p, stdp, mse, s] = lscov(m, y, 1./sigma2);
225
226 % scale errors
227 stdp = stdp ./ sqrt(mse);
228 s = s ./ (mse);
229
230 % compute chi2
231 dof = N - length(p);
232 chi2 = sum((y - polynomeval(x, n, p)).^2 ./ sigma2) / dof;
233
234 % prepare model, units, names
235 model = [];
236 for kk = 1:length(p)
237 if kk == 1
238 model = [model 'P' num2str(kk) '*X.^(' num2str(n(kk)) ')'];
239 else
240 model = [model ' + P' num2str(kk) '*X.^(' num2str(n(kk)) ')'];
241 end
242 units(kk) = simplify(yunits/xunits.^(n(kk)));
243 names{kk} = ['P' num2str(kk)];
244 end
245 model = smodel(plist('expression', model, ...
246 'params', names, ...
247 'values', p, ...
248 'xvar', 'X', ...
249 'xunits', xunits, ...
250 'yunits', yunits ...
251 ));
252
253
254 % Build the output pest object
255 out = pest;
256 out.setY(p);
257 out.setDy(stdp);
258 out.setCov(s);
259 out.setChi2(chi2);
260 out.setDof(dof);
261 out.setNames(names{:});
262 out.setYunits(units);
263 out.setModels(model);
264 out.name = sprintf('polynomfit(%s)', argsname);
265 out.addHistory(getInfo('None'), pl, ao_invars, [aos(:).hist]);
266 % Set procinfo object
267 out.procinfo = plist('MSE', mse);
268
269 % set outputs
270 varargout{1} = out;
271
272 end
273
274 % computes polynomial combination
275 function out = polynomeval(x, n, p)
276 assert(length(p) == length(n));
277 out = zeros(size(x, 1), 1);
278 for k = 1:length(n)
279 out = out + p(k) * x.^n(k);
280 end
281 end
282
283 % get info object
284 function ii = getInfo(varargin)
285 if nargin == 1 && strcmpi(varargin{1}, 'None')
286 sets = {};
287 pl = [];
288 else
289 sets = {'Default'};
290 pl = getDefaultPlist();
291 end
292 % build info object
293 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);
294 ii.setModifier(false);
295 ii.setArgsmin(1);
296 end
297
298 % get default plist
299 function plout = getDefaultPlist()
300 persistent pl;
301 if ~exist('pl', 'var') || isempty(pl)
302 pl = buildplist();
303 end
304 plout = pl;
305 end
306
307 function pl = buildplist()
308 pl = plist();
309
310 % orders
311 p = param({'orders', 'Polynom orders.'}, [0]);
312 pl.append(p);
313
314 % default plist for linear fitting
315 pl.append(plist.LINEAR_FIT_PLIST);
316
317 end