11#ifndef __Levenberg_Marquardt_h
12#define __Levenberg_Marquardt_h
14#include "MEAL/GaussJordan.h"
38 unsigned get_nparam () const;
41 double get_param (unsigned iparam) const;
44 void set_param (unsigned iparam, ) const;
47 bool get_infit (unsigned index) const;
50 Yt evaluate (std::vector<Gt>* gradient);
55 The type of the gradient, Gt, is explicity specified in the
56 declaration of this template class. The types of At and Yt are
57 implicitly specified by the template instantiation of the methods
58 of this class. If Yt or Gt is not of type float or double, there
63 const Yt operator - (const Yt &, const Yt &);
66 const Gt operator * (const Yt &, const Gt &);
70 The LevenbergMarquardt class is used in three stages:
72 <LI> call to ::init() with data and model
73 <LI> repeated calls to ::iter() with data and model, comparing the chisq
74 returned in order to determine convergence (or not) of fit
75 <LI>call to ::result() to get curvature and covariance matrices
79 class LevenbergMarquardt
87 lamda_increase_factor = 10.0;
88 lamda_decrease_factor = 0.1;
89 singular_threshold = 1e-8;
90 restore_policy = NULL;
99 template <
class At,
class Et,
class Mt>
100 float init (
const std::vector< At >& x,
101 const std::vector< Et >& y,
105 template <
class At,
class Et,
class Mt>
106 float iter (
const std::vector< At >& x,
107 const std::vector< Et >& y,
112 void result (Mt& model,
113 std::vector<std::vector<double> >& covariance = null_arg,
114 std::vector<std::vector<double> >& curvature = null_arg);
117 double get_log_det_curvature()
const {
return log_det_alpha; }
120 unsigned get_nparam_infit()
const {
return nparam_infit; }
125 float lamda_increase_factor;
126 float lamda_decrease_factor;
131 float singular_threshold;
133 RestorePolicy* restore_policy;
138 template <
class Mt>
void solve_delta (
const Mt& model);
141 template <
class At,
class Et,
class Mt>
142 float calculate_chisq (
const std::vector< At >& x,
143 const std::vector< Et >& y,
149 std::vector<Grad> gradient;
152 std::vector<double> beta;
155 std::vector<std::vector<double> > alpha;
156 double log_det_alpha = 0.0;
159 std::vector<std::vector<double> > delta;
162 std::vector<std::string> names;
163 std::vector<const char*> name_ptrs;
166 float best_chisq = 0;
169 std::vector<std::vector<double> > best_alpha;
171 std::vector<double> best_beta;
174 unsigned nparam_infit = 0;
177 std::vector<double> backup;
179 static std::vector<std::vector<double> > null_arg;
187 static void apply (Mt& model,
const At& abscissa)
188 { abscissa.apply(); }
192 class AbscissaTraits<double>
196 static void apply (Mt& model,
double abscissa)
197 { model.set_abscissa(abscissa); }
204 virtual void store () = 0;
206 virtual void restore () = 0;
211 class WeightingScheme
215 typedef typename Et::val_type val_type;
216 typedef typename Et::var_type var_type;
219 WeightingScheme (
const Et& estimate)
221 set_variance (estimate.get_variance());
225 void set_variance (
const var_type& variance)
227 inverse_variance = 1.0 / variance;
231 val_type difference (
const Et& estimate,
const val_type& model)
233 return estimate.get_value() - model;
237 val_type norm (
const val_type& x)
const
242 val_type get_weighted_conjugate (
const val_type& data)
const
244 return data * inverse_variance;
247 float get_weighted_norm (
const val_type& data)
const
249 return norm(data) * inverse_variance;
252 var_type inverse_variance;
260 class WeightingScheme< std::complex<Et> >
265 typedef std::complex<Et>
type;
266 typedef std::complex<typename Et::val_type> val_type;
267 typedef typename Et::var_type var_type;
270 WeightingScheme (
const type& estimate)
272 set_variance (estimate);
276 void set_variance (
const type& estimate)
278 inv_var_real = 1.0 / estimate.real().get_variance();
279 inv_var_imag = 1.0 / estimate.imag().get_variance();
283 val_type difference (
const type& estimate,
const val_type& model)
285 val_type val (estimate.real().get_value(), estimate.imag().get_value());
290 val_type norm (
const val_type& x)
const
295 val_type get_weighted_conjugate (
const val_type& data)
const
297 return val_type (data.real()*inv_var_real, -data.imag()*inv_var_imag);
300 float get_weighted_norm (
const val_type& data)
const
302 return data.real()*data.real()*inv_var_real + data.imag()*data.imag()*inv_var_imag;
305 var_type inv_var_real;
306 var_type inv_var_imag;
311 template <
class Mt,
class At,
class Et,
class Grad>
317 std::vector<Grad>& gradient,
319 std::vector<std::vector<double> >& alpha,
320 std::vector<double>& beta);
324 template <
class Mt,
class Yt,
class Wt,
class Grad>
327 const Yt& delta_data,
328 const Wt& weighting_scheme,
329 const std::vector<Grad>& gradient,
331 std::vector<std::vector<double> >& alpha,
332 std::vector<double>& beta);
335 std::string get_name (
const Mt& model,
unsigned iparam);
340std::vector<std::vector<double> > MEAL::LevenbergMarquardt<Grad>::null_arg;
343unsigned MEAL::LevenbergMarquardt<Grad>::verbose = 0;
346template <
class At,
class Et,
class Mt>
347float MEAL::LevenbergMarquardt<Grad>::init
348(
const std::vector< At >& x,
349 const std::vector< Et >& y,
353 std::cerr <<
"MEAL::LevenbergMarquardt<Grad>::init" << std::endl;
356 alpha.resize (model.get_nparam());
357 beta.resize (model.get_nparam());
358 delta.resize (model.get_nparam());
359 backup.resize (model.get_nparam());
360 names.resize (model.get_nparam());
361 name_ptrs.resize (model.get_nparam());
363 for (
unsigned j=0; j<model.get_nparam(); j++)
365 alpha[j].resize (model.get_nparam());
370 std::cerr <<
"MEAL::LevenbergMarquardt<Grad>::init calculate chisq" << std::endl;
372 best_chisq = calculate_chisq (x, y, model);
378 std::cerr <<
"MEAL::LevenbergMarquardt<Grad>::init chisq=" << best_chisq << std::endl;
384void verify_orthogonal (
const std::vector<std::vector<double > >& alpha,
const T& model)
386 unsigned nrow = alpha.size();
391 unsigned nparam = model.get_nparam ();
394 for (
unsigned iparam=0; iparam < nparam; iparam++)
395 if (model.get_infit(iparam))
401 std::vector<std::string> names (nfree);
402 std::vector<unsigned> indeces (nfree);
405 for (
unsigned krow=0; krow<nfree; krow++)
407 while (!model.get_infit(kparam))
410 names[krow] = model.get_param_name(kparam);
411 indeces[krow] = kparam;
416 std::vector<double> row_mod (nfree, 0.0);
422 for (
unsigned irow=0; irow<nfree; irow++)
425 for (
unsigned jcol=0; jcol<nfree; jcol++)
426 norm += alpha[irow][jcol] * alpha[irow][jcol];
428 row_mod[irow] =
sqrt(norm);
430 if (row_mod[irow] == 0)
431 std::cerr << irow <<
"=" << names[irow] <<
" gradient = 0" << std::endl;
434 for (
unsigned krow=0; krow<nfree; krow++)
436 if (row_mod[krow] == 0)
439 for (
unsigned irow=krow+1; irow<nfree; irow++)
441 if (row_mod[irow] == 0)
447 for (
unsigned jcol=0; jcol<nfree; jcol++)
449 degen += alpha[krow][jcol] * alpha[irow][jcol];
452 degen /= row_mod[krow] * row_mod[irow];
456 double ival = model.get_param(indeces[irow]);
457 double kval = model.get_param(indeces[krow]);
459 std::cerr <<
"degen(" << names[krow] <<
"," << names[irow] <<
") = "
460 << degen << std::endl
461 <<
"\t" << names[krow] <<
" = " << kval << std::endl
462 <<
"\t" << names[irow] <<
" = " << ival << std::endl;
465 if (!true_math::finite(degen))
467 std::cerr <<
"NaN or Inf in curvature matrix" << std::endl;
475std::string MEAL::get_name (
const Mt& model,
unsigned iparam)
478 for (
unsigned i=0; i < model.get_nparam(); i++)
480 if (model.get_infit(i))
483 return model.get_param_name(i);
504void MEAL::LevenbergMarquardt<Grad>::solve_delta (
const Mt& model)
507 std::cerr <<
"MEAL::LevenbergMarquardt<Grad>::solve_delta" << std::endl;
509 if (alpha.size() != model.get_nparam())
511 throw Error (InvalidState,
512 "MEAL::LevenbergMarquardt<Grad>::solve_delta",
513 "alpha.size=%d != model.nparam=%d",
514 alpha.size(), model.get_nparam());
519 std::cerr <<
"MEAL::LevenbergMarquardt<Grad>::solve_delta lamda="
520 << lamda <<
" nparam=" << model.get_nparam() << std::endl;
524 for (
unsigned ifit=0; ifit<model.get_nparam(); ifit++)
527 std::cerr <<
"MEAL::LevenbergMarquardt<Grad>::solve_delta i=" << ifit
528 <<
" " << model.get_param_name(ifit);
530 if (model.get_infit(ifit))
533 std::cerr <<
" in fit" << std::endl;
536 for (
unsigned jfit=0; jfit<model.get_nparam(); jfit++)
538 if (model.get_infit(jfit))
540 alpha[iinfit][jinfit]=best_alpha[ifit][jfit];
545 alpha[iinfit][iinfit] *= (1.0 + lamda);
546 delta[iinfit][0]=best_beta[ifit];
547 names[iinfit] = model.get_param_name(ifit);
548 name_ptrs[iinfit] = names[iinfit].c_str();
552 else if (verbose > 0)
553 std::cerr <<
" fixed" << std::endl;
557 throw Error (InvalidState,
"MEAL::LevenbergMarquardt<Grad>::solve_delta",
"no parameters in fit");
559 nparam_infit = iinfit;
562 std::cerr <<
"MEAL::LevenbergMarquardt<Grad>::solve_delta for " << iinfit <<
" parameters" << std::endl;
565 std::vector<std::vector<double> > temp_copy (alpha);
570 log_det_alpha =
MEAL::GaussJordan (alpha, delta, iinfit, singular_threshold, &name_ptrs);
575 verify_orthogonal (temp_copy, model);
576 throw error +=
"MEAL::LevenbergMarquardt<Grad>::solve_delta";
580 std::cerr <<
"MEAL::LevenbergMarquardt<Grad>::solve_delta exit" << std::endl;
605template <
class At,
class Et,
class Mt>
606float MEAL::LevenbergMarquardt<Grad>::iter
607(
const std::vector< At >& x,
608 const std::vector< Et >& y,
612 std::cerr <<
"MEAL::LevenbergMarquardt<Grad>::iter" << std::endl;
620 std::cerr <<
"MEAL::LevenbergMarquardt<Grad>::iter update model" << std::endl;
623 for (
unsigned ifit=0; ifit<model.get_nparam(); ifit++)
627 if (model.get_infit(ifit))
629 change = delta[iinfit][0];
633 backup[ifit] = model.get_param (ifit);
636 std::cerr <<
" delta[" << ifit <<
"]=" << change << std::endl;
638 model.set_param (ifit, backup[ifit] + change);
642 std::cerr <<
"MEAL::LevenbergMarquardt<Grad>::iter calculate new chisq" << std::endl;
644 float new_chisq = calculate_chisq (x, y, model);
646 if (new_chisq < best_chisq)
648 lamda *= lamda_decrease_factor;
651 std::cerr <<
"MEAL::LevenbergMarquardt<Grad>::iter new chisq="
652 << new_chisq <<
"\n better fit; lamda=" << lamda << std::endl;
655 restore_policy->store ();
657 best_chisq = new_chisq;
663 lamda *= lamda_increase_factor;
666 std::cerr <<
"MEAL::LevenbergMarquardt<Grad>::iter new chisq="
667 << new_chisq <<
"\n worse fit; lamda=" << lamda << std::endl;
670 restore_policy->restore ();
673 for (
unsigned iparm=0; iparm<model.get_nparam(); iparm++)
674 model.set_param (iparm, backup[iparm]);
694void MEAL::LevenbergMarquardt<Grad>::result
696 std::vector<std::vector<double> >& covar,
697 std::vector<std::vector<double> >& curve )
700 std::cerr <<
"MEAL::LevenbergMarquardt<Grad>::result" << std::endl;
702 if (&curve != &null_arg)
708 if (&covar == &null_arg)
711 covar.resize (model.get_nparam());
714 for (
unsigned idim=0; idim < model.get_nparam(); idim++)
716 covar[idim].resize (model.get_nparam());
718 if (!model.get_infit(idim))
720 for (
unsigned jdim=0; jdim < model.get_nparam(); jdim++)
721 covar[idim][jdim] = 0;
726 for (
unsigned jdim=0; jdim < model.get_nparam(); jdim++)
728 if (model.get_infit(jdim))
730 covar[idim][jdim] = alpha [iindim][jindim];
734 covar[idim][jdim] = 0;
757template <
class At,
class Et,
class Mt>
758float MEAL::LevenbergMarquardt<Grad>::calculate_chisq
759(
const std::vector< At >& x,
760 const std::vector< Et >& y,
764 std::cerr <<
"MEAL::LevenbergMarquardt<Grad>::chisq nparam="
765 << model.get_nparam() << std::endl;
767 if (alpha.size() != model.get_nparam())
768 throw Error (InvalidState,
"MEAL::LevenbergMarquardt<Grad>::chisq",
769 "alpha.size=%d != model.nparam=%d",
770 alpha.size(), model.get_nparam());
772 if (y.size() < x.size())
773 throw Error (InvalidParam,
"MEAL::LevenbergMarquardt<Grad>::chisq",
774 "y.size=%d < x.size=%d", y.size(), x.size());
778 for (
unsigned j=0; j<alpha.size(); j++)
780 for (
unsigned k=0; k<=j; k++)
785 for (
unsigned ipt=0; ipt < x.size(); ipt++)
788 std::cerr <<
"MEAL::LevenbergMarquardt<Grad>::chisq lmcoff[" << ipt <<
"/" << x.size() <<
"]" << std::endl;
790 Chisq += lmcoff (model, x[ipt], y[ipt], gradient, alpha, beta);
794 for (
unsigned ifit=1; ifit<model.get_nparam(); ifit++)
795 for (
unsigned jfit=0; jfit<ifit; jfit++)
796 alpha[jfit][ifit]=alpha[ifit][jfit];
801template <
class Mt,
class At,
class Et,
class Grad>
808 std::vector<Grad>& gradient,
810 std::vector<std::vector<double> >& alpha,
811 std::vector<double>& beta
815 if (LevenbergMarquardt<Grad>::verbose > 2)
816 std::cerr <<
"MEAL::lmcoff data=" << data << std::endl;
818 AbscissaTraits<At>::apply (model, abscissa);
820 if (LevenbergMarquardt<Grad>::verbose > 2)
821 std::cerr <<
"MEAL::lmcoff abscissa applied" << std::endl;
823 WeightingScheme<Et> weight (data);
825 float result = lmcoff1 (model,
826 weight.difference (data, model.evaluate (&gradient)),
827 weight, gradient, alpha, beta);
829 if (LevenbergMarquardt<Grad>::verbose > 2)
830 std::cerr <<
"MEAL::lmcoff lmcoff1 computed" << std::endl;
836 error <<
"\n\t" "data=" << data <<
" model=" << model.evaluate ();
837 throw error +=
"MEAL::lmcoff";
841template <
class Mt,
class Yt,
class Wt,
class Grad>
847 const std::vector<Grad>& gradient,
849 std::vector<std::vector<double> >& alpha,
850 std::vector<double>& beta
855 ElementTraits<Grad> traits;
857 if (LevenbergMarquardt<Grad>::verbose > 2)
858 std::cerr <<
"MEAL::lmcoff1 delta_y=" << delta_y << std::endl;
860 Yt w_delta_y = weight.get_weighted_conjugate (delta_y);
862 for (
unsigned ifit=0; ifit < model.get_nparam(); ifit++)
864 if (model.get_infit(ifit))
866 double term = traits.to_real (w_delta_y * gradient[ifit]);
867 if (!true_math::finite(term))
868 throw Error (InvalidState,
"MEAL::lmcoff1"
869 "non-finite contribution to beta");
874 if (LevenbergMarquardt<Grad>::verbose > 2)
875 std::cerr <<
"MEAL::lmcoff1 compute weighted conjugate of gradient"
876 "[" << ifit <<
"]" << std::endl;
878 Grad w_gradient = weight.get_weighted_conjugate (gradient[ifit]);
880 if (LevenbergMarquardt<Grad>::verbose > 2)
881 std::cerr <<
"MEAL::lmcoff1 add to curvature matrix" << std::endl;
884 for (
unsigned jfit=0; jfit <= ifit; jfit++)
886 if (model.get_infit(jfit))
888 double term = traits.to_real (w_gradient * gradient[jfit]);
889 if (!true_math::finite(term))
890 throw Error (InvalidState,
"MEAL::lmcoff1",
"non-finite contribution to alpha");
892 alpha[ifit][jfit] += term;
899 float chisq = weight.get_weighted_norm (delta_y);
901 if (LevenbergMarquardt<Grad>::verbose > 1 || !true_math::finite(chisq))
902 std::cerr <<
"MEAL::lmcoff1 chisq=" << chisq << std::endl;
908 error <<
"\n\t" "delta_y=" << delta_y;
909 throw error +=
"MEAL::lmcoff1";
Namespace in which all modeling and calibration related code is declared.
Definition ExampleComplex2.h:16
T GaussJordan(std::vector< std::vector< T > > &a, std::vector< std::vector< U > > &b, int nrow=-1, double singular_threshold=0.0, std::vector< const char * > *names=0)
Definition GaussJordan.h:44
const ScalarMath sqrt(const ScalarMath &x)
Return a ScalarMath instance representing x^.5.
Definition ScalarMath.C:151