#ifndef __LEVENBERG_MARQUARDT_H__
#define __LEVENBERG_MARQUARDT_H__
#include "matrix.h"
namespace blop
{
template <class par_type>
inline bool gaussj(matrix<par_type> &a, double b[], int n, int *indxc, int *indxr, int *ipiv)
{
int irow=0, icol=0;
for(int j=0; j<n; ++j) ipiv[j] = 0;
for(int i=0; i<n; ++i)
{
double big = 0;
for(int j=0; j<n; ++j)
{
if( ipiv[j] != 1)
{
for(int k=0; k<n; ++k)
{
if(ipiv[k] == 0)
{
if(::fabs(a(j,k).dbl()) >= big)
{
big = ::fabs(a(j,k).dbl());
irow = j;
icol = k;
}
}
}
}
}
++(ipiv[icol]);
if(irow != icol)
{
for(int l=0; l<n; ++l) swap(a(irow,l).dbl(),a(icol,l).dbl());
swap(b[irow],b[icol]);
}
indxr[i] = irow;
indxc[i] = icol;
if(a(icol,icol).dbl() == 0.0) return false;
double pivinv = 1.0/a(icol,icol).dbl();
a(icol,icol).dbl() = 1.0;
for(int l=0; l<n; ++l) a(icol,l).dbl() *= pivinv;
b[icol] *= pivinv;
for(int ll=0; ll<n; ++ll)
{
if(ll != icol)
{
double dum=a(ll,icol).dbl();
a(ll,icol).dbl() = 0.0;
for(int l=0; l<n; ++l) a(ll,l).dbl() -= a(icol,l).dbl()*dum;
b[ll] -= b[icol]*dum;
}
}
}
for(int l=n-1; l>=0; --l)
{
if(indxr[l] != indxc[l])
{
for(int k=0; k<n; ++k)
{
swap(a(k,indxr[l]).dbl(),a(k,indxc[l]).dbl());
}
}
}
return true;
}
template <class par_type>
inline bool invert_hessian(const matrix<par_type> &alpha, matrix<par_type> &covar,
const double beta[], double beta_temp[],
int NFREE, double lambda,
int *indxc, int *indxr, int *ipiv)
{
for(int j=0; j<NFREE; ++j)
{
for(int k=0; k<NFREE; ++k) covar(j,k).dbl() = alpha(j,k).dbl();
covar(j,j).dbl() = alpha(j,j).dbl() * (1 + lambda);
beta_temp[j] = beta[j];
}
return gaussj(covar,beta_temp,NFREE,indxc,indxr,ipiv);
}
template <class x_type, class y_type, class par_type, class func_type, class der0_type>
inline double calc_chisq(const matrix<x_type> &x, const matrix<x_type> &sigma_x,
const matrix<y_type> &y, const matrix<y_type> &sigma_y,
const std::vector<par_type> ¶meters,
const func_type &fitfunc,
matrix<y_type> &y_out,
der0_type &der0)
{
const int NDATA = x.rows();
double chisq = 0;
for(int idata=0; idata<NDATA; ++idata)
{
fitfunc(x.row(idata), parameters, y_out.row(idata));
chisq += der0(x.row(idata),
sigma_x.row(idata),
y.row(idata),
sigma_y.row(idata),
y_out.row(idata));
}
return chisq;
}
template <class x_type, class y_type, class par_type, class func_type, class der1_type, class der2_type>
inline void calc_der(const matrix<x_type> &x, const matrix<x_type> &sigma_x,
const matrix<y_type> &y, const matrix<y_type> &sigma_y,
std::vector<par_type> parameters,
const std::vector<bool> ¶meter_free,
const int NPARS, const int NFREE,
matrix<par_type> &alpha, double beta[],
const std::vector<func_type> &fitfunc_derivs,
const matrix<y_type> &func_values,
matrix<y_type> dfda[],
der1_type &der1,
der2_type &der2)
{
const int NDATA = y.rows();
for(int j=0; j<NFREE; ++j)
{
for(int k=0; k<=j; ++k) alpha(j,k).dbl() = 0;
beta[j] = 0;
}
for(int idata=0; idata<NDATA; ++idata)
{
for(int i1=-1,i1_all=0; i1_all<NPARS; ++i1_all)
{
if(!parameter_free[i1_all]) continue;
++i1;
fitfunc_derivs[i1_all](x.row(idata), parameters, dfda[i1_all].row(idata));
beta[i1] -= 0.5* der1(x.row(idata), sigma_x.row(idata),
y.row(idata), sigma_y.row(idata),
func_values.row(idata),
dfda[i1_all].row(idata));
for(int i2=-1,i2_all=0; i2_all <= i1_all; ++i2_all)
{
if(!parameter_free[i2_all]) continue;
++i2;
alpha(i1,i2).dbl() += 0.5 * der2(x.row(idata), sigma_x.row(idata),
y.row(idata), sigma_y.row(idata),
func_values.row(idata),
dfda[i1_all].row(idata),
dfda[i2_all].row(idata));
}
}
}
for(int j=1; j<NFREE; ++j)
{
for(int k=0; k<j; ++k) alpha(k,j).dbl() = alpha(j,k).dbl();
}
}
template <class par_type>
void expand_covar(matrix<par_type> &covar,const std::vector<bool> ¶meter_free,int nfit)
{
int npars = covar.rows();
if(nfit == npars) return;
int i = nfit-1;
for(int i_out = npars-1; i_out >= 0; --i_out)
{
if(!parameter_free[i_out])
{
for(int j=0; j<=i_out; ++j) covar(i_out,j).dbl() = covar(j,i_out).dbl() = 0;
}
else
{
int k=0;
for(int j=0; j<=i_out; ++j)
{
if(parameter_free[j]) covar(i_out,j).dbl() = covar(j,i_out).dbl() = covar(i,k++).dbl();
else covar(i_out,j).dbl() = covar(j,i_out).dbl() = 0;
}
--i;
}
}
}
template <class x_type, class y_type, class par_type, class func_type, class der0_type, class der1_type, class der2_type, class convfunc_type>
int levenberg_marquardt(const matrix<x_type> &x, const matrix<x_type> &sigma_x,
const matrix<y_type> &y, const matrix<y_type> &sigma_y,
std::vector<par_type> ¶meters,
const std::vector<bool> ¶meter_free,
matrix<par_type> &covar, double *chisq_out,int verbose,
func_type &fitfunc,
const std::vector<func_type> &fitfunc_derivs,
int fitfunc_components,
int maxstep, double lambda,
der0_type &der0,
der1_type &der1,
der2_type &der2,
convfunc_type convergence_criterion)
{
const int NPARS = parameters.size();
const int NDATA = x.rows();
const int NY = y.cols();
if(x.rows() != y.rows() ||
x.rows() != sigma_x.rows() ||
x.rows() != sigma_y.rows())
{
warning::print("Mismatch in the number of data points!","levenberg_marquardt(...)");
return 0;
}
if(y.cols() != sigma_y.cols())
{
warning::print("# of components in y and sigma_y do not agree","levenberg_marquardt(...)");
return 0;
}
if(y.cols() != fitfunc_components)
{
warning::print("# of components in y and fitfunc do not agree","levenberg_marquardt(...)");
return 0;
}
if(x.cols() != sigma_x.cols())
{
warning::print("# of components in x and sigma_x do not agree","levenberg_marquardt(...)");
return 0;
}
if(covar.rows() != NPARS || covar.cols() != NPARS)
{
warning::print("Mismatch in the number of parameters and covariance matrix dimension","levenberg_marquardt(...)");
return 0;
}
matrix<par_type> alpha(NPARS,NPARS);
double *beta = new double[NPARS];
double *beta_temp = new double[NPARS];
std::vector<blop::var> trial_parameters(NPARS);
double *delta_parameters = new double[NPARS];
matrix<y_type> func_values(NDATA,NY);
double chisq;
matrix<y_type>::default_nrows(NDATA);
matrix<y_type>::default_ncols(NY);
matrix<y_type> *dfda = new matrix<y_type>[NPARS];
int NFREE=0;
for(int i=0; i<NPARS; ++i)
{
if(parameter_free[i])
{
++NFREE;
if(verbose>1) cerr<<"parameter["<<i<<"] is free"<<endl;
}
else
{
if(verbose>1) cerr<<"parameter["<<i<<"] is fixed"<<endl;
}
}
if(verbose>1) cerr<<"Free params = "<<NFREE<<endl;
int *indxc = new int[NFREE];
int *indxr = new int[NFREE];
int *ipiv = new int[NFREE];
if(verbose>2)
{
cerr<<"..: Fitted function: "<<fitfunc<<endl;
for(int ipar=0; ipar<NPARS; ++ipar)
{
cerr<<"..: Derivative wrt par #"<<ipar+1<<": "<<fitfunc_derivs[ipar]<<endl;
}
}
*chisq_out = calc_chisq(x,sigma_x,y,sigma_y,parameters,fitfunc,func_values,der0);
calc_der(x,sigma_x,y,sigma_y,parameters,parameter_free,NPARS,NFREE,alpha,beta,
fitfunc_derivs,func_values,dfda,der1,der2);
if(verbose>1)
{
cerr<<"..: Fit initialized"<<endl;
cerr<<" Starting parameters: ";
for(int i=0; i<NPARS; ++i) cerr<<parameters[i].dbl()<<" ";
cerr<<endl;
cerr<<" Initial chi2 = "<<*chisq_out<<endl;
}
if(maxstep <= 0) maxstep = NFREE * 50;
int it_numb;
for(it_numb = 0; it_numb < maxstep; ++it_numb)
{
if(verbose>1) cerr<<"..: iterations step #"<<it_numb<<endl;
if(invert_hessian(alpha,covar,beta,beta_temp,NFREE,lambda,indxc,indxr,ipiv))
{
if(verbose>1) cerr<<" gauss_jordan succeeded"<<endl;
for(int j=0; j<NFREE; ++j) delta_parameters[j] = beta_temp[j];
}
else
{
if(verbose>1) cerr<<"..: gauss_jordan failed"<<endl;
double a=0;
for(int j=0; j<NFREE; ++j) a += ::fabs(covar(j,j).dbl())*(1+lambda);
if(a <= 0) a = lambda;
for(int j=0; j<NFREE; ++j) delta_parameters[j] = beta[j]/a;
}
for(int j=0,l=0; l<NPARS; ++l)
{
if(parameter_free[l]) trial_parameters[l].dbl() = parameters[l].dbl()+delta_parameters[j++];
else trial_parameters[l].dbl() = parameters[l].dbl();
}
chisq = calc_chisq(x,sigma_x,y,sigma_y,trial_parameters,fitfunc,func_values,der0);
if(verbose>1)
{
cerr<<" Lambda = "<<lambda<<endl;
cerr<<" Delta params = ";
for(int i=0; i<NPARS; ++i) cerr<<delta_parameters[i]<<" ";
cerr<<endl;
cerr<<" Trial params = ";
for(int i=0; i<NPARS; ++i) cerr<<trial_parameters[i].dbl()<<" ";
cerr<<endl;
cerr<<" chi2 = "<<*chisq_out<<" --> "<<chisq<<endl;
}
int converged = convergence_criterion(*chisq_out, chisq, trial_parameters, parameters, it_numb);
if(chisq < *chisq_out)
{
if(verbose>1) cerr<<" Step accepted"<<endl;
for(int j=0; j<NPARS; ++j) parameters[j].dbl() = trial_parameters[j].dbl();
calc_der(x,sigma_x,y,sigma_y,parameters,parameter_free,NPARS,NFREE,
alpha,beta,fitfunc_derivs,func_values,dfda,der1,der2);
lambda *= 0.1;
*chisq_out = chisq;
}
else
{
if(verbose>1) cerr<<" Step rejected"<<endl;
lambda *= 10;
chisq = *chisq_out;
}
if(verbose>1) cerr<<endl;
if(converged) break;
}
if(it_numb >= maxstep && verbose>0) cerr<<"FIT REACHED MAX ITERATION NUMBER: "<<maxstep<<endl;
if(!invert_hessian(alpha,covar,beta,beta_temp,NFREE,0,indxc,indxr,ipiv))
{
cerr<<"Failed to invert the hessian matrix of chi2!"<<endl;
cerr<<"The reported covariance matrix is meaningless"<<endl;
}
expand_covar(covar,parameter_free,NFREE);
delete [] dfda;
delete [] delta_parameters;
delete [] beta;
delete [] beta_temp;
delete [] indxr;
delete [] indxc;
delete [] ipiv;
return it_numb;
}
}
#endif