comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:f0afece42f48
1 % TDFIT fit a set of smodels to a set of input and output signals..
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %
4 % DESCRIPTION: TDFIT fit a set of smodels to a set of input and output signals.
5 %
6 %
7 % CALL: b = tdfit(outputs, pl)
8 %
9 % INPUTS: outputs - the AOs representing the outputs of a system.
10 % pl - parameter list (see below)
11 %
12 % OUTPUTs: b - a pest object containing the best-fit parameters,
13 % goodness-of-fit reduced chi-squared, fit degree-of-freedom
14 % covariance matrix and uncertainties. Additional
15 % quantities, like the Information Matrix, are contained
16 % within the procinfo. The best-fit model can be evaluated
17 % from pest\eval.
18 %
19 % <a href="matlab:utils.helper.displayMethodInfo('ao', 'tdfit')">Parameters Description</a>
20 %
21 % EXAMPLES:
22 %
23 % % 1) Sine-wave stimulus of a simple system
24 %
25 % % Sine-wave data
26 % data = ao(plist('tsfcn', 'sin(2*pi*3*t) + 0.01*randn(size(t))', 'fs', 100, 'nsecs', 10));
27 % data.setName;
28 %
29 % % System filter
30 % pzm = pzmodel(1, 1, 10);
31 % f = miir(pzm);
32 %
33 % % Make output signal
34 % dataf = filter(data, f);
35 % dataf.setName;
36 %
37 % % fit model to output
38 % mdl = smodel('(a.*(b + 2*pi*i*f)) ./ (b*(a + 2*pi*i*f))');
39 % mdl.setParams({'a', 'b'}, {2*pi 10*2*pi});
40 % mdl.setXvar('f');
41 % params = tdfit(dataf, plist('inputs', data, 'models', mdl, 'P0', [1 1]));
42 %
43 % % Evaluate fit
44 % mdl.setValues(params);
45 % BestModel = fftfilt(data, mdl);
46 % BestModel.setName;
47 % iplot(data, dataf, BestModel, plist('linestyles', {'-', '-', '--'}))
48 %
49 % % recovered parameters (in Hz)
50 % params.y/2/pi
51 %
52 % VERSION: $Id: tdfit.m,v 1.45 2011/05/26 12:57:27 congedo Exp $
53 %
54 % HISTORY: 05-10-2009 G. Congedo
55 %
56 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
57
58 % 'WhiteningFilters' - Use filter banks for whitening the outputs.
59 % Note: you must fit the two channels at the same time
60 % and supply four filter banks.
61
62
63 function varargout = tdfit(varargin)
64
65 % Check if this is a call for parameters
66 if utils.helper.isinfocall(varargin{:})
67 varargout{1} = getInfo(varargin{3});
68 return
69 end
70
71 import utils.const.*
72 utils.helper.msg(msg.PROC3, 'running %s/%s', mfilename('class'), mfilename);
73
74 % Collect input variable names
75 in_names = cell(size(varargin));
76 for ii = 1:nargin,in_names{ii} = inputname(ii);end
77
78 % Collect all AOs and plists
79 [as, ao_invars, rest] = utils.helper.collect_objects(varargin(:), 'ao', in_names);
80 [pl, pl_invars, rest] = utils.helper.collect_objects(rest, 'plist', in_names);
81
82 if nargout == 0
83 error('### tdfit cannot be used as a modifier. Please give an output variable.');
84 end
85
86 % combine plists
87 pl = parse(pl, getDefaultPlist());
88
89 outputs = copy(as,1);
90
91 % Extract necessary parameters
92 inputs = pl.find('inputs');
93 TFmodels = pl.find('models');
94 WhFlts = pl.find('WhFlts');
95 Ncut = pl.find('Ncut');
96 P0 = pl.find('P0');
97 pnames = pl.find('pnames');
98 inNames = pl.find('innames');
99 outNames = pl.find('outnames');
100 % ADDP = find(pl, 'ADDP');
101 userOpts = pl.find('OPTSET');
102 weights = find(pl, 'WEIGHTS');
103 FitUnc = pl.find('FitUnc');
104 UncMtd = pl.find('UncMtd');
105 linUnc = pl.find('linUnc');
106 FastHess = pl.find('FastHess');
107 SymDiff = pl.find('SymDiff');
108 DiffOrder = pl.find('DiffOrder');
109 lb = pl.find('LB');
110 ub = pl.find('UB');
111 MCsearch = pl.find('MonteCarlo');
112 Npoints = pl.find('Npoints');
113 Noptims = pl.find('Noptims');
114 Algorithm = pl.find('Algorithm');
115 padRatio = pl.find('PadRatio');
116 SISO = pl.find('SingleInputSingleOutput');
117 GradSearch= pl.find('GradSearch');
118 estimator = pl.find('estimator');
119
120 % Convert yes/no, true/false, etc. to booleans
121 FitUnc = utils.prog.yes2true(FitUnc);
122 linUnc = utils.prog.yes2true(linUnc);
123 MCsearch = utils.prog.yes2true(MCsearch);
124 SymDiff = utils.prog.yes2true(SymDiff);
125 SISO = utils.prog.yes2true(SISO);
126 GradSearch = utils.prog.yes2true(GradSearch);
127
128 % consistency check on inputs
129 if isempty(TFmodels)
130 error('### please specify at least a transfer function or a SSM model')
131 end
132 if isempty(inputs)
133 error('### please give the inputs of the system')
134 end
135 if isempty(outputs)
136 error('### please give the outputs of the system')
137 end
138 % if isempty(pnames) || ~iscellstr(pnames)
139 % error('### please give the parameter names in a cell-array of strings')
140 % end
141
142 % look for aliases within the models
143 if ~isa(TFmodels,'ssm')
144 aliasNames = TFmodels(1).aliasNames;
145 aliasValues = TFmodels(1).aliasValues;
146 for ii=2:numel(TFmodels)
147 if ~isempty(TFmodels(ii).aliasNames)
148 aliasNames = union(aliasNames,TFmodels(ii).aliasNames);
149 end
150 end
151 if ~isempty(aliasNames)
152 for kk=1:numel(aliasNames)
153 for ii=1:numel(TFmodels)
154 ix = strcmp(TFmodels(ii).aliasNames,aliasNames{kk});
155 if sum(ix)==0
156 continue;
157 else
158 aliasValues{kk} = TFmodels(ii).aliasValues{ix};
159 end
160 end
161 end
162 end
163 end
164
165 % common params set
166 if isa(TFmodels, 'smodel')
167 [TFmodels,pnames,P0] = cat_mdls(TFmodels,pnames,P0);
168 elseif isa(TFmodels, 'matrix')
169 [TFmodels,pnames,P0] = cat_mdls(TFmodels.objs,pnames,P0);
170 end
171
172 % if isempty(P0) || ~isnumeric(P0) && ~MCsearch
173 % if isa(TFmodels, 'smodel')
174 % if ~isempty(TFmodels(1).values)
175 % P0 = TFmodels.values;
176 % P0 = cell2mat(P0);
177 % if numel(P0)~=numel(TFmodels(1).params)
178 % error('### numbers of parameter values and names do not match')
179 % end
180 % else
181 % error('### please give the initial guess in a numeric array')
182 % end
183 % end
184 % if isa(TFmodels, 'matrix')
185 % if ~isempty(TFmodels.objs(1).values)
186 % P0 = TFmodels.objs(1).values;
187 % P0 = cell2mat(P0);
188 % if numel(P0)~=numel(TFmodels.objs(1).params)
189 % error('### numbers of parameter values and names do not match')
190 % end
191 % else
192 % error('### please give the initial guess in a numeric array')
193 % end
194 % end
195 % end
196 if ~isnumeric(lb) || ~isnumeric(ub)
197 error('### please give lower and upper bounds in a numeric array')
198 end
199 if numel(lb)~=numel(ub)
200 error('### please give lower and upper bounds of the same length')
201 end
202 if isa(TFmodels, 'smodel')
203 pnames = TFmodels(1).params;
204 Np = numel(pnames);
205 % for kk=2:numel(TFmodels)
206 % if numel(TFmodels(kk).params)~=Np
207 % error('### number of parameters must be the same for all transfer function models')
208 % end
209 % if ~strcmp(TFmodels(kk).params,pnames)
210 % error('### all transfer function models must have the same parameters')
211 % end
212 % end
213 end
214 if isa(TFmodels, 'matrix')
215 pnames = TFmodels.objs(1).params;
216 Np = numel(pnames);
217 for kk=2:(TFmodels.nrows*TFmodels.ncols)
218 if numel(TFmodels.objs(kk).params)~=Np
219 error('### number of parameters must be the same for all transfer function models')
220 end
221 if ~strcmp(TFmodels.objs(kk).params,pnames)
222 error('### all transfer function models must have the same parameters')
223 end
224 end
225 end
226 if isa(TFmodels, 'ssm') && isempty(pnames)
227 pnames = getKeys(TFmodels.params);
228 Np = numel(pnames);
229 end
230
231 % check TFmodels, inputs and outputs
232 Nin = numel(inputs);
233 Nout = numel(outputs);
234 NTFmodels = numel(TFmodels);
235 if isa(TFmodels, 'smodel') & size(TFmodels)~=[Nout,Nin]
236 error('### the size of the transfer function does not match with the number of inputs and outputs')
237 end
238 if size(inputs)~=[Nin,1]
239 inputs = inputs';
240 end
241 if size(outputs)~=[Nout,1]
242 outputs = outputs';
243 end
244
245 % checks on inputs and outputs consistency
246 inLen = inputs(1).len;
247 inXdata = inputs(1).x;
248 inFs = inputs(1).fs;
249 for kk=2:Nin
250 if inputs(kk).len~=inLen
251 error('### all inputs must have the same length')
252 end
253 if inputs(kk).x~=inXdata
254 error('### x-fields of all inputs must be the same')
255 end
256 if inputs(kk).fs~=inFs
257 error('### fs-fields of all inputs must be the same')
258 end
259 end
260 outLen = outputs(1).len;
261 outXdata = outputs(1).x;
262 outFs = outputs(1).fs;
263 for kk=2:Nout
264 if outputs(kk).len~=outLen
265 error('### all outputs must have the same length')
266 end
267 if outputs(kk).x~=outXdata
268 error('### x-fields of all outputs must be the same')
269 end
270 if outputs(kk).fs~=outFs
271 error('### fs-fields of all outputs must be the same')
272 end
273 end
274 if inLen~=outLen
275 error('### inputs and outputs must have the same length')
276 end
277 if inXdata~=outXdata
278 error('### x-fields of inputs and outputs must be the same')
279 end
280 if inFs~=outFs
281 error('### fs-fields of inputs and outputs must be the same')
282 end
283
284 % check Whitening Filters
285 Wf = ~isempty(WhFlts);
286 Nwf = numel(WhFlts);
287 if Wf
288 if isempty(Ncut)
289 Ncut = 100;
290 end
291 for ii=1:numel(WhFlts)
292 if ~(isa(WhFlts(ii),'matrix')||isa(WhFlts(ii),'filterbank'))
293 error('### whitening filters must be array of matrix or filterbank class')
294 end
295 end
296 if Nwf~=Nout % size(WhFlts)~=[Nout,Nout]
297 error('### the size of the whitening filter array does not match with the number of outputs to be filtered')
298 end
299 % extract poles and residues
300 B = cell(Nout,1); % cell(Nout,Nin);
301 A = B;
302 for ii=1:Nwf
303 if isa(WhFlts(ii),'matrix')
304 Nflt = max(WhFlts(ii).osize);
305 elseif isa(WhFlts(ii),'filterbank')
306 Nflt = numel(WhFlts(ii).filters);
307 end
308 B{ii} = zeros(Nflt,1);
309 A{ii} = B{ii};
310 for jj=1:Nflt
311 if isa(WhFlts(ii),'matrix')
312 B{ii}(jj) = WhFlts(ii).objs(jj).b(2);
313 A{ii}(jj) = WhFlts(ii).objs(jj).a(1);
314 elseif isa(WhFlts(ii),'filterbank')
315 B{ii}(jj) = WhFlts(ii).filters(jj).b(2);
316 A{ii}(jj) = WhFlts(ii).filters(jj).a(1);
317 end
318 end
319 end
320 end
321
322
323 % Number of data before padding
324 Ndata = inLen;
325 fs = inFs;
326 % nsecs = Ndata/fs;
327
328 % Extract inputs
329 inYdata = inputs.y;
330 if size(inYdata)~=[inLen,Nin]
331 inYdata = inYdata';
332 end
333 outYdata = outputs.y;
334 if size(outYdata)~=[outLen,Nout]
335 outYdata = outYdata';
336 end
337
338
339 if isa(TFmodels, 'smodel') || isa(TFmodels, 'matrix')
340
341 % Zero-pad inputs before parameter estimation.
342 % Pad-ratio is defined as the ratio between the number of zero-padding
343 % points and the data length
344
345 if ~isempty(padRatio)
346 if ~isnumeric(padRatio)
347 error('### please give a numeric pad ratio')
348 else
349 if padRatio~=0
350 Npad = round(padRatio * inLen);
351 else
352 Npad = 0;
353 end
354 end
355 else
356 Npad = 0;
357 end
358
359 NdataPad = Ndata + Npad;
360 Nfft = NdataPad; % 2^nextpow2(NdataPad);
361 Npad = Nfft - Ndata;
362
363 zeroPad = zeros(Npad,Nin);
364 inYdataPad = [inYdata;zeroPad];
365
366 % Fft inputs
367 inFfts = fft(inYdataPad,Nfft);
368 % zero through fs/2
369 % inFfts = inFfts(1:Nfft/2+1,:);
370
371 % Sample TFmodels on fft-frequencies
372 % zero through fs
373 ff = (0:(Nfft-1))'.*fs./Nfft;
374 % zero through fs/2
375 % ff = [0:(Nfft/2)]'.*fs/(Nfft/2+1);
376 % ff = fs/2*linspace(0,1,Nfft/2+1)';
377 for kk=1:NTFmodels
378 TFmodels(kk).setXvar('f');
379 TFmodels(kk).setXvals(ff);
380 end
381
382 % set values for aliases
383 if ~isempty(aliasNames)
384 for kk=1:numel(aliasNames)
385 aliasValues{kk}.setXvar('f');
386 aliasValues{kk}.setXvals(ff);
387 end
388 end
389 % assign aliases to variables
390 aliasTrue = 0;
391 if ~isempty(aliasNames)
392 alias = cell(numel(aliasNames),1);
393 for kk=1:numel(aliasNames)
394 alias{kk} = aliasValues{kk}.double;
395 % assignin('caller',aliasNames{kk},aliasValues{kk}.double);
396 end
397 aliasTrue = 1;
398 end
399
400 % Extract function_handles from TFmodels
401 TFmodelFuncs = cell(size(TFmodels));
402 for ii=1:NTFmodels
403 % TFmodelFuncs{ii} = TFmodels(ii).fitfunc;
404 % TFmodelFuncs{ii} =
405 % @(x)eval_mdl(TFmodels(ii).expr.s,TFmodels(ii).xvar,TFmodels(ii).xvals,TFmodels(ii).params,x);
406 fcnstr = doSubsPnames(TFmodels(ii).expr.s,TFmodels(ii).params);
407 if aliasTrue
408 fcnstr = doSubsAlias(fcnstr,aliasNames);
409 TFmodelFuncs{ii} = ...
410 eval_mdl_alias(fcnstr,TFmodels(ii).xvar{1},TFmodels(ii).xvals{1},alias);
411 else
412 TFmodelFuncs{ii} = ...
413 eval_mdl(fcnstr,TFmodels(ii).xvar{1},TFmodels(ii).xvals{1});
414 end
415 end
416
417 end
418
419 % If requested, compute the analytical gradient
420 if SymDiff || linUnc && (isa(TFmodels, 'smodel') || isa(TFmodels, 'matrix'))
421 % compute symbolic 1st-order differentiation
422 TFmodelDFuncsSmodel = cell(numel(pnames),1);
423 for ll=1:numel(pnames)
424 for ss=1:size(TFmodels,1)
425 for tt=1:size(TFmodels,2)
426 TFmodelDFuncsSmodel{ll}(ss,tt) = diff(TFmodels(ss,tt),pnames{ll});
427 end
428 end
429 end
430 % extract anonymous function
431 TFmodelDFuncs = cell(numel(pnames),1);
432 for ll=1:numel(pnames)
433 TFmodelDFuncs{ll} = cell(size(TFmodels));
434 for ii=1:NTFmodels
435 if ~isempty(TFmodelDFuncsSmodel{ll})
436 fcnstr = doSubsPnames(TFmodelDFuncsSmodel{ll}(ii).expr.s,TFmodelDFuncsSmodel{ll}(ii).params);
437 if aliasTrue
438 fcnstr = doSubsAlias(fcnstr,aliasNames);
439 TFmodelDFuncs{ll}{ii} = ...
440 eval_mdl_alias(fcnstr,TFmodelDFuncsSmodel{ll}(ii).xvar{1},TFmodelDFuncsSmodel{ll}(ii).xvals{1},alias);
441 else
442 TFmodelDFuncs{ll}{ii} = ...
443 eval_mdl(fcnstr,TFmodelDFuncsSmodel{ll}(ii).xvar{1},TFmodelDFuncsSmodel{ll}(ii).xvals{1});
444 end
445 else
446 TFmodelDFuncs{ll}{ii} = @(x)0;
447 end
448 end
449 end
450 if DiffOrder==2
451 % compute symbolic 2nd-order differentiation
452 TFmodelHFuncsSmodel = cell(numel(pnames));
453 % for ii=1:NTFmodels
454 % p = TFmodels(ii).params;
455 for mm=1:numel(pnames)
456 for ll=1:mm
457 for ss=1:size(TFmodels,1)
458 for tt=1:size(TFmodels,2)
459 TFmodelHFuncsSmodel{ll,mm}(ss,tt) = diff(TFmodelDFuncsSmodel{ll}(ss,tt),pnames{mm});
460 end
461 end
462 end
463 end
464 % end
465 % extract anonymous function
466 TFmodelHFuncs = cell(numel(pnames));
467 for mm=1:numel(pnames)
468 for ll=1:mm
469 TFmodelHFuncs{ll,mm} = cell(size(TFmodels));
470 for ii=1:NTFmodels
471 if ~isempty(TFmodelHFuncsSmodel{ll,mm})
472 % TFmodelHFuncs{ll,mm}{ii} = TFmodelHFuncsSmodel{ii}(ll,mm).fitfunc; % inverted indexes are for practicalities
473 fcnstr = doSubsPnames(TFmodelHFuncsSmodel{ll,mm}(ii).expr.s,TFmodelHFuncsSmodel{ll,mm}(ii).params);
474 if aliasTrue
475 fcnstr = doSubsAlias(fcnstr,aliasNames);
476 TFmodelHFuncs{ll,mm}{ii} = ...
477 eval_mdl_alias(fcnstr,TFmodelHFuncsSmodel{ll,mm}(ii).xvar{1},TFmodelHFuncsSmodel{ll,mm}(ii).xvals{1},alias);
478 else
479 TFmodelHFuncs{ll,mm}{ii} = ...
480 eval_mdl(fcnstr,TFmodelHFuncsSmodel{ll,mm}(ii).xvar{1},TFmodelHFuncsSmodel{ll,mm}(ii).xvals{1});
481 end
482 else
483 TFmodelHFuncs{ll,mm}{ii} = @(x)0;
484 end
485 end
486 end
487 end
488 end
489 end
490
491 % Build index for faster computation
492
493 if isa(TFmodels,'smodel')
494 TFidx = compIdx(inYdata, Nout, TFmodels);
495 end
496 if exist('TFmodelDFuncsSmodel','var')
497 TFDidx = cell(numel(pnames),1);
498 for ll=1:numel(pnames)
499 TFDidx{ll} = compIdx(inYdata, Nout, TFmodelDFuncsSmodel{ll});
500 end
501 end
502 if exist('TFmodelHFuncsSmodel','var')
503 TFHidx = cell(numel(pnames));
504 for mm=1:numel(pnames)
505 for ll=1:mm
506 TFHidx{ll,mm} = compIdx(inYdata, Nout, TFmodelHFuncsSmodel{ll,mm});
507 end
508 end
509 end
510
511
512 % Construct the output function handles from TFmodels as funtion of
513 % parameters
514 % if ~Wf
515 outModelFuncs = cell(Nout,1);
516 % if Nout>1
517 if isa(TFmodels, 'smodel') || isa(TFmodels, 'matrix')
518 for ii=1:Nout
519 if SISO
520 outModelFuncs{ii} = @(x)mdl_fftfilt_SISO(x, inFfts(:,ii), TFmodelFuncs{ii}, Npad);
521 else
522 % outModelFuncs{ii} = @(x)mdl_fftfilt(x, ii, inFfts, TFmodelFuncs, Npad);
523 outModelFuncs{ii} = @(x)mdl_fftfilt2(x, ii, inFfts, size(inFfts), TFmodelFuncs, TFidx, Npad);
524 end
525 end
526 elseif isa(TFmodels, 'ssm')
527 SSMmodels = cell(Nout,1);
528 plsym = cell(Nout,1);
529 for ii=1:Nout
530 plsym{ii} = plist('return outputs', outNames{ii}, ...
531 'AOS VARIABLE NAMES', inNames{ii}, ...
532 'AOS', inputs(ii), ...
533 'reorganize', false, ...
534 'set', 'for simulate');
535
536 % SSMmodelOptim = TFmodels.reorganize(plsym{ii});
537 SSMmodels{ii} = TFmodels.reorganize(plsym{ii});
538 % SSMmodels{ii} = SSMmodelOptim.clearNumParams;
539
540 outModelFuncs{ii} = @(x)mdl_ssm(x, pnames, inputs(ii), SSMmodels{ii}, plsym{ii});
541 end
542 end
543 % else
544 % outModelFuncs{1} = @(x)mdl_fftfilt(x, ii, inFfts, TFmodelFuncs, Npad);
545 % end
546 % end
547
548 % In case of symbolic differentiation, construct the output derivatives
549 if SymDiff || linUnc
550 outModelDFuncs = cell(numel(pnames),1);
551 for ll=1:numel(pnames)
552 outModelDFuncs{ll} = cell(Nout,1);
553 for ii=1:Nout
554 if SISO
555 outModelDFuncs{ll}{ii} = @(x)mdl_fftfilt_SISO(x, inFfts(:,ii), TFmodelDFuncs{ll}{ii}, Npad);
556 else
557 % outModelDFuncs{ll}{ii} = @(x)mdl_fftfilt(x, ii, inFfts, TFmodelDFuncs{ll}, Npad);
558 outModelDFuncs{ll}{ii} = @(x)mdl_fftfilt2(x, ii, inFfts, size(inFfts), TFmodelDFuncs{ll}, TFDidx{ll}, Npad);
559 end
560 end
561 end
562 if DiffOrder==2
563 outModelHFuncs = cell(numel(pnames));
564 for mm=1:numel(pnames)
565 for ll=1:mm
566 outModelHFuncs{ll,mm} = cell(Nout,1);
567 for ii=1:Nout
568 if SISO
569 outModelHFuncs{ll,mm}{ii} = @(x)mdl_fftfilt_SISO(x, inFfts(:,ii), TFmodelFuncs{ll,mm}{ii}, Npad);
570 else
571 % outModelHFuncs{ll,mm}{ii} = @(x)mdl_fftfilt(x, ii, inFfts, TFmodelHFuncs{ll,mm}, Npad);
572 outModelHFuncs{ll,mm}{ii} = @(x)mdl_fftfilt2(x, ii, inFfts, size(inFfts), TFmodelHFuncs{ll,mm}, TFHidx{ll,mm}, Npad);
573 end
574 end
575 end
576 end
577 end
578 end
579
580 % In case the whitening filters are provided do a filtering both on
581 % models and data
582 if Wf
583 % filter models
584 outModelFuncs_w = cell(Nout,1);
585 for ii=1:Nout
586 % outModelFuncs_w{ii} = @(x)filter_mdl(x, B, A, outModelFuncs{ii}, Ncut);
587 % outModelFuncs_w{ii} = @(x)mdl_fftfilt_wf(x, ii, inFfts, TFmodelFuncs, Npad, B, A, Ncut);
588 % off-diagonal
589 % outModelFuncs_w{ii} = @(x)mdl_fftfilt_wf(x, ii, outModelFuncs, B, A, Ncut);
590 % diagonal
591 outModelFuncs_w{ii} = @(x)filter_mdl2(x, B{ii}, A{ii}, outModelFuncs{ii}, Ncut);
592 end
593 % filter model derivatives
594 if SymDiff || linUnc
595 % whitening 1st derivatives
596 outModelDFuncs_w = cell(numel(pnames),1);
597 for ll=1:numel(pnames)
598 outModelDFuncs_w{ll} = cell(Nout,1);
599 for ii=1:Nout
600 % off-diagonal
601 % outModelDFuncs_w{ll}{ii} = @(x)mdl_fftfilt_wf(x, ii, outModelDFuncs{ll}, B, A, Ncut);
602 % diagonal
603 outModelDFuncs_w{ll}{ii} = @(x)filter_mdl2(x, B{ii}, A{ii}, outModelDFuncs{ll}{ii}, Ncut);
604 end
605 end
606 if DiffOrder==2
607 % whitening 2nd derivatives
608 outModelHFuncs_w = cell(numel(pnames));
609 for mm=1:numel(pnames)
610 for ll=1:mm
611 outModelHFuncs_w{ll,mm} = cell(Nout,1);
612 for ii=1:Nout
613 % off-diagonal
614 % outModelDFuncs_w{ll}{ii} = @(x)mdl_fftfilt_wf(x, ii, outModelDFuncs{ll}, B, A, Ncut);
615 % diagonal
616 outModelHFuncs_w{ll,mm}{ii} = @(x)filter_mdl2(x, B{ii}, A{ii}, outModelHFuncs{ll,mm}{ii}, Ncut);
617 end
618 end
619 end
620 end
621 end
622 % filter data
623 outYdata_w = zeros(size(outYdata));
624 outYdata_w(1:Ncut,:) = [];
625 % off-diagonal
626 % for ii=1:Nout
627 % for jj=1:Nout
628 % outYdata_w(:,ii) = outYdata_w(:,ii) + filter_data(B{ii,jj}, A{ii,jj}, outYdata(:,jj), Ncut);
629 % end
630 % end
631 % diagonal
632 for ii=1:Nout
633 outYdata_w(:,ii) = filter_data(B{ii}, A{ii}, outYdata(:,ii), Ncut);
634 end
635 % set filtered values to pass to xfit
636 for ii=1:Nout
637 % outputs(ii).setY(outYdata_w(:,ii));
638 outputs(ii) = ao(plist('yvals',outYdata_w(:,ii)));
639 end
640 end
641
642 % set the proper function to pass to xfit
643 if Wf
644 outModelFuncs_4xfit = outModelFuncs_w;
645 else
646 outModelFuncs_4xfit = outModelFuncs;
647 end
648 if SymDiff || linUnc
649 % 1st derivatives
650 outModelDFuncs_4xfit = cell(Nout,1);
651 for ii=1:Nout
652 outModelDFuncs_4xfit{ii} = cell(numel(pnames),1);
653 for ll=1:numel(pnames)
654 if Wf
655 outModelDFuncs_4xfit{ii}{ll} = outModelDFuncs_w{ll}{ii};
656 else
657 outModelDFuncs_4xfit{ii}{ll} = outModelDFuncs{ll}{ii};
658 end
659 end
660 end
661 if DiffOrder==2
662 % 2nd derivatives
663 outModelHFuncs_4xfit = cell(Nout,1);
664 for ii=1:Nout
665 outModelHFuncs_4xfit{ii} = cell(numel(pnames));
666 for mm=1:numel(pnames)
667 for ll=1:mm
668 if Wf
669 outModelHFuncs_4xfit{ii}{ll,mm} = outModelHFuncs_w{ll,mm}{ii};
670 else
671 outModelHFuncs_4xfit{ii}{ll,mm} = outModelHFuncs{ll,mm}{ii};
672 end
673 end
674 end
675 end
676 else
677 outModelHFuncs_4xfit = {};
678 end
679 else
680 outModelDFuncs_4xfit = {};
681 outModelHFuncs_4xfit = {};
682 end
683
684 % replicate pnames, P0, LB, UB for xfit
685 if Nout>1
686 % pnames
687 pnames_4xfit = cell(1,Nout);
688 for ii=1:Nout
689 pnames_4xfit{ii} = pnames;
690 end
691 % P0
692 P0_4xfit = cell(1,Nout);
693 for ii=1:Nout
694 P0_4xfit{ii} = P0;
695 end
696 % LB
697 if ~isempty(lb)
698 lb_4xfit = cell(1,Nout);
699 for ii=1:Nout
700 lb_4xfit{ii} = lb;
701 end
702 else
703 lb_4xfit = [];
704 end
705 % UB
706 if ~isempty(ub)
707 ub_4xfit = cell(1,Nout);
708 for ii=1:Nout
709 ub_4xfit{ii} = ub;
710 end
711 else
712 ub_4xfit = [];
713 end
714
715 else
716 pnames_4xfit = pnames;
717 P0_4xfit = P0;
718 lb_4xfit = lb;
719 ub_4xfit = ub;
720 end
721
722
723 % do fit with xfit
724 fitpl = plist('Function', outModelFuncs_4xfit, ...
725 'pnames', pnames_4xfit, 'P0', P0_4xfit, 'LB', lb_4xfit, 'UB', ub_4xfit, ...
726 'Algorithm', Algorithm, 'FitUnc', FitUnc, 'UncMtd', UncMtd, 'linUnc', linUnc, 'FastHess', FastHess,...
727 'SymGrad', outModelDFuncs_4xfit, 'SymHess', outModelHFuncs_4xfit,...
728 'MonteCarlo', MCsearch, 'Npoints', Npoints, 'Noptims', Noptims, ...
729 'OPTSET', userOpts, 'estimator', estimator, 'weights', weights);
730
731 % Preliminary Gradient search
732 if GradSearch && exist('TFmodelDFuncs','var')
733
734 % set new optimization options
735 userOpts1 = optimset(userOpts,'GradObj','on','LargeScale','on','FinDiffType','central');
736 % 'PrecondBandWidth',0,'TolPCG',1e-4);
737 fitpl1 = fitpl.pset('Algorithm','fminunc','OPTSET',userOpts1);
738
739 % fit
740 params = xfit(outputs, fitpl1);
741
742 % update initial guess
743 if Nout>1
744 P0_4xfit = cell(1,Nout);
745 for ii=1:Nout
746 P0_4xfit{ii} = params.y;
747 end
748 else
749 P0_4xfit = params.y;
750 end
751
752 % restore old optimization options
753 fitpl = fitpl.pset('Algorithm',Algorithm,'OPTSET',userOpts,'P0',P0_4xfit);
754
755 % extract preliminary chain
756 chain = params.chain;
757 end
758
759 % Final search
760 params = xfit(outputs, fitpl);
761
762 % Make output pest
763 out = copy(params,1);
764
765 % Concatenate chains and set it
766 if exist('chain','var')
767 chain = [chain;params.chain];
768 out.setChain(chain);
769 end
770
771 % Set Name and History
772 mdlname = char(TFmodels(1).name);
773 for kk=2:NTFmodels
774 mdlname = strcat(mdlname,[',' char(TFmodels(kk).name)]);
775 end
776 out.name = sprintf('tdfit(%s)', mdlname);
777 out.setNames(pnames);
778 out.addHistory(getInfo('None'), pl, ao_invars(:), [as(:).hist]);
779
780 % Set outputs
781 if nargout > 0
782 varargout{1} = out;
783 end
784 end
785
786 %--------------------------------------------------------------------------
787 % Included Functions
788 %--------------------------------------------------------------------------
789
790 function outYdata = mdl_fftfilt(P, outIdx, inFfts, TFmdl, Npad)
791
792 sz = size(inFfts);
793 outFfts = zeros(sz);
794 for ii=1:sz(2)
795 outFfts(:,ii) = inFfts(:,ii).*TFmdl{outIdx,ii}(P);
796 end
797 outFfts = sum(outFfts,2);
798 outYdata = ifft(outFfts(:,1), 'symmetric');
799 outYdata(end-Npad+1:end,:) = [];
800
801 end
802
803 %--------------------------------------------------------------------------
804
805 function outYdata = mdl_fftfilt2(P, outIdx, inFfts, sz, TFmdl, TFidx, Npad)
806
807 TFeval = zeros(sz);
808 for ii=1:sz(2)
809 if TFidx(outIdx,ii)
810 TFeval(:,ii) = TFmdl{outIdx,ii}(P);
811 end
812 end
813 outFfts = inFfts.*TFeval;
814 outFfts = sum(outFfts,2);
815 outYdata = ifft(outFfts(:,1), 'symmetric');
816 outYdata(end-Npad+1:end,:) = [];
817
818 end
819
820 %--------------------------------------------------------------------------
821
822 function idx = compIdx(inYdata, Nout, mdl)
823
824 % what inputs are actually different from zero
825 b = any(inYdata);
826 b = repmat(b,Nout,1);
827 % what transfer functions are actually different from zero
828 sz = size(mdl);
829 a = zeros(sz);
830 for ii=1:numel(mdl)
831 a(ii) = ~(strcmp(mdl(ii).expr.s,'[]') || strcmp(mdl(ii).expr.s,'0') || strcmp(mdl(ii).expr.s,'0.0'));
832 end
833 % index for computation
834 idx = logical(a.*b);
835
836 end
837
838 %--------------------------------------------------------------------------
839
840 function outYdata = mdl_fftfilt_SISO(P, inFfts, TFmdl, Npad)
841
842 % sz = size(inFfts);
843 % outFfts = zeros(sz);
844 % parfor ii=1:sz(2)
845 outFfts = inFfts.*TFmdl(P);
846 % outFfts = sum(outFfts,2);
847 outYdata = ifft(outFfts(:,1), 'symmetric');
848 outYdata(end-Npad+1:end,:) = [];
849
850 end
851
852 %--------------------------------------------------------------------------
853
854 function outYdata = mdl_ssm(P, pnames, inputs, model, plsym)
855
856 model.doSetParameters(pnames,P);
857 model.keepParameters();
858
859 % to numerical
860 fs = inputs(1).fs;
861 % model.modifyTimeStep(plist('newtimestep',1/fs));
862 model.modifyTimeStep(1/fs);
863
864 %%% get expected outputs
865 outYdata = simulate(model,plsym);
866 outYdata = outYdata.objs(1).y;
867
868 end
869
870 %--------------------------------------------------------------------------
871
872 %--------------------------------------------------------------------------
873
874 % function outYdata = Dmdl_fftfilt(P, outIdx, Dix, inFfts, TFmdl, Npad)
875 %
876 % sz = size(inFfts);
877 % outFfts = zeros(sz);
878 % for ii=1:sz(2)
879 % outFfts(:,ii) = inFfts(:,ii).*TFmdl{outIdx,ii}{Dix}(P);
880 % end
881 % outFfts = sum(outFfts,2);
882 % outYdata = ifft(outFfts(:,1), 'symmetric');
883 % outYdata(end-Npad+1:end,:) = [];
884 %
885 % end
886
887 %--------------------------------------------------------------------------
888
889 % function outYdata = mdl_fftfilt_wf(P, outIdx, inFfts, TFmdl, Npad, B, A, Ncut)
890 %
891 % sz = size(inFfts);
892 % outFfts = zeros(sz);
893 % for ii=1:sz(2)
894 % outFfts(:,ii) = inFfts(:,ii).*TFmdl{outIdx,ii}(P);
895 % end
896 % outYdata = ifft(outFfts, 'symmetric');
897 % outYdata(end-Npad+1:end,:) = [];
898 % for ii=1:sz(2)
899 % outYdata_w(:,ii) = filter_data(B{outIdx,ii},A{outIdx,ii},outYdata(:,ii), Ncut);
900 % end
901 % outYdata = sum(outYdata_w,2);
902 %
903 % % outYdata_w = zeros(size(outYdata,1),1);
904 % % outYdata_w(1:Ncut) = [];
905 % % for jj=1:sz(2)
906 % % outYdata_w = outYdata_w + filter_data(pol{outIdx,jj}, res{outIdx,jj}, outYdata(:,jj), Ncut);
907 % % end
908 % % outYdata = sum(outYdata_w,2);
909 %
910 % end
911
912 %--------------------------------------------------------------------------
913
914 % function outYdata = mdl_fftfilt_wf(P, outIdx, TFmdl, B, A, Ncut)
915 %
916 % os = filter_mdl(P, B, A, TFmdl, Ncut);
917 % outYdata = os(:,outIdx);
918 %
919 % end
920
921 %--------------------------------------------------------------------------
922
923 % function os = filter_mdl(P, B, A, oFuncs, Ncut)
924 %
925 % Nout = numel(oFuncs);
926 % for ii=1:Nout
927 % Y(:,ii) = oFuncs{ii}(P);
928 % end
929 % os = zeros(size(Y));
930 % os(1:Ncut,:) = [];
931 % for ii=1:Nout
932 % for jj=1:Nout
933 % os(:,ii) = os(:,ii) + filter_data(B{ii,jj}, A{ii,jj}, Y(:,jj), Ncut);
934 % end
935 % end
936 %
937 % end
938
939 %--------------------------------------------------------------------------
940
941 % function outYdata = Dmdl_fftfilt_wf(P, outIdx, TFmdl, B, A, Ncut)
942 %
943 % os = filter_mdl(P, B, A, TFmdl, Ncut);
944 % outYdata = os(:,outIdx);
945 %
946 % end
947
948 %--------------------------------------------------------------------------
949
950 function o = filter_data(B, A, X, Ncut)
951
952 N = numel(X);
953 M = numel(B);
954 Y = zeros(N,M);
955 % parfor ii=1:M
956 for ii=1:M
957 Y(:,ii) = filter(A(ii),[1 B(ii)],X);
958 end
959 o = sum(Y,2);
960 o(1:Ncut) = [];
961
962 end
963
964 %--------------------------------------------------------------------------
965
966 function o = filter_mdl2(P, B, A, oFunc, Ncut)
967
968 X = oFunc(P);
969 N = numel(X);
970 M = numel(B);
971 Y = zeros(N,M);
972 for ii=1:M
973 Y(:,ii) = filter(A(ii),[1 B(ii)],X);
974 end
975 o = sum(Y,2);
976 o(1:Ncut) = [];
977
978 end
979
980 %--------------------------------------------------------------------------
981
982 function fcn = eval_mdl(str,xvar,xvals)
983
984 fcn = eval(['@(P,',xvar,')(',str,')']);
985 fcn = @(x)fcn(x,xvals);
986
987 end
988
989 %--------------------------------------------------------------------------
990
991 function fcn = eval_mdl_alias(str,xvar,xvals,alias)
992
993 fcn = eval(['@(P,',xvar,',alias',')(',str,')']);
994 fcn = @(x)fcn(x,xvals,alias);
995
996 end
997 %--------------------------------------------------------------------------
998
999 function str = doSubsPnames(str,params)
1000
1001 lengths = zeros(1,numel(params));
1002 for ii=1:numel(params)
1003 lengths(ii) = length(params{ii});
1004 end
1005 [dummy,ix] = sort(lengths,'descend');
1006 repstr = cell(1,numel(params));
1007 for ii=1:numel(params)
1008 repstr{ii} = ['P(' int2str(ii) ')'];
1009 end
1010 str = regexprep(str,params(ix),repstr(ix));
1011 if isempty(str)
1012 str = '0';
1013 end
1014
1015 end
1016
1017 %--------------------------------------------------------------------------
1018
1019 function str = doSubsAlias(str,alias)
1020
1021 lengths = zeros(1,numel(alias));
1022 for ii=1:numel(alias)
1023 lengths(ii) = length(alias{ii});
1024 end
1025 [dummy,ix] = sort(lengths,'descend');
1026 repstr = cell(1,numel(alias));
1027 for ii=1:numel(alias)
1028 repstr{ii} = ['alias{' int2str(ii) '}'];
1029 end
1030 str = regexprep(str,alias(ix),repstr(ix));
1031
1032 end
1033
1034 %--------------------------------------------------------------------------
1035
1036 function [newMdls,pnames,P0]=cat_mdls(mdls,usedParams,P0)
1037
1038 pnames = mdls(1).params;
1039
1040 % union of all parameters
1041 for ii=2:numel(mdls)
1042 pnames = union(pnames,mdls(ii).params);
1043 end
1044
1045 pvalues = cell(1,numel(pnames));
1046 for kk=1:numel(pnames)
1047 for ii=1:numel(mdls)
1048 ix = strcmp(mdls(ii).params,pnames{kk});
1049 if sum(ix)==0
1050 continue;
1051 else
1052 pvalues{kk} = mdls(ii).values{ix};
1053 end
1054 end
1055 end
1056
1057 % set the used one
1058 for ii=1:numel(mdls)
1059 mdls(ii).setParams(pnames,pvalues);
1060 if ~isempty(usedParams)
1061 mdls(ii).subs(setdiff(pnames,usedParams));
1062 else
1063 usedParams = pnames;
1064 end
1065 end
1066
1067 % copy to new models
1068 for ii=1:size(mdls,1)
1069 for jj=1:size(mdls,2)
1070 newMdls(ii,jj) = smodel(plist('expression',mdls(ii,jj).expr,...
1071 'xvar',mdls(ii,jj).xvar,'xvals',mdls(ii,jj).xvals,...
1072 'name',mdls(ii,jj).name));
1073 end
1074 end
1075
1076 % set the same parameters for all
1077 for ii=1:numel(newMdls)
1078 if ~isempty(P0)
1079 newMdls(ii).setParams(usedParams,P0);
1080 else
1081 for kk=1:numel(usedParams)
1082 ix = strcmp(usedParams(kk),pnames);
1083 P0(kk) = pvalues{ix};
1084 end
1085 newMdls(ii).setParams(usedParams,P0);
1086 end
1087 end
1088
1089 pnames = usedParams;
1090
1091 end
1092
1093 %--------------------------------------------------------------------------
1094 % Get Info Object
1095 %--------------------------------------------------------------------------
1096 function ii = getInfo(varargin)
1097 if nargin == 1 && strcmpi(varargin{1}, 'None')
1098 sets = {};
1099 pl = [];
1100 else
1101 sets = {'Default'};
1102 pl = getDefaultPlist;
1103 end
1104 % Build info object
1105 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);
1106 ii.setModifier(false);
1107 end
1108
1109 %--------------------------------------------------------------------------
1110 % Get Default Plist
1111 %--------------------------------------------------------------------------
1112 function plout = getDefaultPlist()
1113 persistent pl;
1114 if exist('pl', 'var')==0 || isempty(pl)
1115 pl = buildplist();
1116 end
1117 plout = pl;
1118 end
1119
1120 function pl = buildplist()
1121
1122 pl = plist();
1123
1124 % Inputs
1125 p = param({'Inputs', 'An array of input AOs, one per each experiment.'}, paramValue.EMPTY_DOUBLE);
1126 pl.append(p);
1127
1128 % Models
1129 p = param({'Models', 'An array of transfer function SMODELs.'}, paramValue.EMPTY_DOUBLE);
1130 pl.append(p);
1131
1132 % PadRatio
1133 p = param({'PadRatio', ['PadRatio is defined as the ratio between the number of zero-pad points '...
1134 'and the data length.<br>'...
1135 'Define how much to zero-pad data after the signal.<br>'...
1136 'Being <tt>tdfit</tt> a fft-based algorithm, no zero-padding might bias the estimation, '...
1137 'therefore it is strongly suggested to do that.']}, 1);
1138 pl.append(p);
1139
1140 % Whitening Filters
1141 p = param({'WhFlts', 'An array of FILTERBANKs containing the whitening filters per each output AO.'}, paramValue.EMPTY_DOUBLE);
1142 pl.append(p);
1143
1144 % Parameters
1145 p = param({'Pnames', 'A cell-array of parameter names to fit.'}, paramValue.EMPTY_CELL);
1146 pl.append(p);
1147
1148 % P0
1149 p = param({'P0', 'An array of starting guesses for the parameters.'}, paramValue.EMPTY_DOUBLE);
1150 pl.append(p);
1151
1152 % LB
1153 p = param({'LB', ['Lower bounds for the parameters.<br>'...
1154 'This improves convergency. Mandatory for Monte Carlo.']}, paramValue.EMPTY_DOUBLE);
1155 pl.append(p);
1156
1157 % UB
1158 p = param({'UB', ['Upper bounds for the parameters.<br>'...
1159 'This improves the convergency. Mandatory for Monte Carlo.']}, paramValue.EMPTY_DOUBLE);
1160 pl.append(p);
1161
1162 % Algorithm
1163 p = param({'ALGORITHM', ['A string defining the fitting algorithm.<br>'...
1164 '<tt>fminunc</tt>, <tt>fmincon</tt> require ''Optimization Toolbox'' to be installed.<br>'...
1165 '<tt>patternsearch</tt>, <tt>ga</tt>, <tt>simulannealbnd</tt> require ''Genetic Algorithm and Direct Search'' to be installed.<br>']}, ...
1166 {1, {'fminsearch', 'fminunc', 'fmincon', 'patternsearch', 'ga', 'simulannealbnd'}, paramValue.SINGLE});
1167 pl.append(p);
1168
1169 % OPTSET
1170 p = param({'OPTSET', ['An optimisation structure to pass to the fitting algorithm.<br>'...
1171 'See <tt>fminsearch</tt>, <tt>fminunc</tt>, <tt>fmincon</tt>, <tt>optimset</tt>, for details.<br>'...
1172 'See <tt>patternsearch</tt>, <tt>psoptimset</tt>, for details.<br>'...
1173 'See <tt>ga</tt>, <tt>gaoptimset</tt>, for details.<br>'...
1174 'See <tt>simulannealbnd</tt>, <tt>saoptimset</tt>, for details.']}, paramValue.EMPTY_STRING);
1175 pl.append(p);
1176
1177 % SymDiff
1178 p = param({'SymDiff', 'Use symbolic derivatives or not. Only for gradient-based algorithm or for LinUnc option.'}, paramValue.NO_YES);
1179 pl.append(p);
1180
1181 % DiffOrder
1182 p = param({'DiffOrder', 'Symbolic derivative order. Only for SymDiff option.'}, {1, {1,2}, paramValue.SINGLE});
1183 pl.append(p);
1184
1185 % FitUnc
1186 p = param({'FitUnc', 'Fit parameter uncertainties or not.'}, paramValue.YES_NO);
1187 pl.append(p);
1188
1189 % UncMtd
1190 p = param({'UncMtd', ['Choose the uncertainties estimation method.<br>'...
1191 'For multi-channel fitting <tt>hessian</tt> is mandatory.']}, {1, {'hessian', 'jacobian'}, paramValue.SINGLE});
1192 pl.append(p);
1193
1194 % LinUnc
1195 p = param({'LinUnc', 'Force linear symbolic uncertainties.'}, paramValue.YES_NO);
1196 pl.append(p);
1197
1198 % GradSearch
1199 p = param({'GradSearch', 'Do a preliminary gradient-based search using the BFGS Quasi-Newton method.'}, paramValue.NO_YES);
1200 pl.append(p);
1201
1202 % MonteCarlo
1203 p = param({'MonteCarlo', ['Do a Monte Carlo search in the parameter space.<br>'...
1204 'Useful when dealing with high multiplicity of local minima. May be computer-expensive.<br>'...
1205 'Note that, if used, P0 will be ignored. It also requires to define LB and UB.']}, paramValue.NO_YES);
1206 pl.append(p);
1207
1208 % Npoints
1209 p = param({'Npoints', 'Set the number of points in the parameter space to be extracted.'}, 100000);
1210 pl.append(p);
1211
1212 % Noptims
1213 p = param({'Noptims', 'Set the number of optimizations to be performed after the Monte Carlo.'}, 10);
1214 pl.append(p);
1215
1216 % SISO
1217 p = param({'SingleInputSingleOutput', 'Specify whether the model should be considered as Single-Input/Single-Output model. This is for performance.'}, paramValue.NO_YES);
1218 pl.append(p);
1219
1220 end
1221 % END