/******************************************************************** ** Image Component Library (ICL) ** ** ** ** Copyright (C) 2006-2013 CITEC, University of Bielefeld ** ** Neuroinformatics Group ** ** Website: www.iclcv.org and ** ** http://opensource.cit-ec.de/projects/icl ** ** ** ** File : ICLMath/src/ICLMath/LevenbergMarquardtFitter.cpp ** ** Module : ICLMath ** ** Authors: Christof Elbrechter, Sergius Gaulik ** ** ** ** ** ** GNU LESSER GENERAL PUBLIC LICENSE ** ** This file may be used under the terms of the GNU Lesser General ** ** Public License version 3.0 as published by the ** ** ** ** Free Software Foundation and appearing in the file LICENSE.LGPL ** ** included in the packaging of this file. Please review the ** ** following information to ensure the license requirements will ** ** be met: http://www.gnu.org/licenses/lgpl-3.0.txt ** ** ** ** The development of this software was supported by the ** ** Excellence Cluster EXC 277 Cognitive Interaction Technology. ** ** The Excellence Cluster EXC 277 is a grant of the Deutsche ** ** Forschungsgemeinschaft (DFG) in the context of the German ** ** Excellence Initiative. ** ** ** ********************************************************************/ #include #include #include using namespace icl::utils; namespace icl{ namespace math{ template LevenbergMarquardtFitter::LevenbergMarquardtFitter(){ } template LevenbergMarquardtFitter::LevenbergMarquardtFitter(Function f, int outputDim, const std::vector &js, Scalar tau, int maxIterations, Scalar minError, Scalar lambdaMultiplier, Scalar eps1, Scalar eps2, const std::string &linSolver){ init(f,outputDim,js,tau,maxIterations,minError,lambdaMultiplier,eps1,eps2,linSolver); } template LevenbergMarquardtFitter::LevenbergMarquardtFitter(FunctionMat f, int outputDim, const std::vector &js, Scalar tau, int maxIterations, Scalar minError, Scalar lambdaMultiplier, Scalar eps1, Scalar eps2, const std::string &linSolver){ init(f,outputDim,js,tau,maxIterations,minError,lambdaMultiplier,eps1,eps2,linSolver); } template void LevenbergMarquardtFitter::init(Function f, int outputDim, const std::vector &js, Scalar tau, int maxIterations, Scalar minError, Scalar lambdaMultiplier, Scalar eps1, Scalar eps2, const std::string &linSolver){ this->useMultiThreading = false; this->f = f; if(js.size()){ this->js = js; }else{ this->js = create_numerical_jacobians(outputDim,f); } this->tau = tau; this->maxIterations = maxIterations; this->minError = minError; this->lambdaMultiplier = lambdaMultiplier; this->eps1 = eps1; this->eps2 = eps2; this->linSolver = linSolver; this->useMat = false; } template void LevenbergMarquardtFitter::init(FunctionMat f, int outputDim, const std::vector &js, Scalar tau, int maxIterations, Scalar minError, Scalar lambdaMultiplier, Scalar eps1, Scalar eps2, const std::string &linSolver){ this->useMultiThreading = false; this->fMat = f; if(js.size()){ this->jsMat = js; }else{ this->jsMat = create_numerical_jacobians(outputDim,f); } this->tau = tau; this->maxIterations = maxIterations; this->minError = minError; this->lambdaMultiplier = lambdaMultiplier; this->eps1 = eps1; this->eps2 = eps2; this->linSolver = linSolver; this->useMat = true; } template inline Scalar sqr_dist(const Scalar &a, const Scalar &b){ return sqr(a-b); } template Scalar LevenbergMarquardtFitter::error(const Matrix &ys, const Matrix &y_est) const { return ys.sqrDistanceTo(y_est)/2.0; } template void LevenbergMarquardtFitter::setUseMultiThreading(bool enable){ useMultiThreading = enable; } template typename LevenbergMarquardtFitter::Result LevenbergMarquardtFitter::fitVec(const Matrix &xs, const Matrix &ys, Params params){ const int O = ys.cols(); // output dim ICLASSERT_THROW(O == (int)js.size(), ICLException("LevenbergMarquardtFitter::fit: ys.cols() and outputDim differ")); const int I = xs.cols(); const int D = xs.rows(); const int P = params.dim(); const int MAX_IT = maxIterations; const Scalar MIN_E = minError; const Scalar EPS1 = eps1; const Scalar EPS2 = eps2; dst.setDim(P); J.setBounds(P,D); H.setBounds(P,P); H.setBounds(P,P); params_new.setDim(P); y_est.setBounds(O,D); dy.setBounds(D); std::vector v(O,2.0); std::vector lambdas(O,0.0); std::vector xs_rows(D); std::vector ys_rows(D); std::vector y_est_rows(D); for(int i=0;i(xs.row_begin(i)),false); ys_rows[i] = Vector(O, const_cast(ys.row_begin(i)),false); y_est_rows[i] = Vector(O, y_est.row_begin(i), false); y_est_rows[i] = f(params,xs_rows[i]); } Scalar e = error(ys,y_est); Scalar eInit = e; if(e < minError){ Result r = {-1, eInit, e, lambdas, params}; if(dbg) dbg(r); return r; } int it = 0; #ifdef USE_OPENMP bool mt = useMultiThreading; #endif for(;it < MAX_IT; ++it){ for(int o=0;o 1) { #ifdef USE_OPENMP #pragma omp parallel for if(mt) #endif for(int i=0;i maxN) maxN = tmp; } if (maxN <= EPS1) { Result result = { it, eInit, e, lambdas, params}; return result; } // first guess of lambda if (it == 0) { Scalar H_max = H(0,0); for (unsigned int i = 1; i < H.cols(); ++i) { if (H(i,i) > H_max) H_max = H(i,i); } lambdas[o] = tau * H_max; } } for(int i=0;i 0.0) { v[o] = 2.0; params = params_new; e = e_new; lambdas[o] *= iclMax(1.0/3.0, 1.0 - pow(2.0*delta - 1.0, 3)); if (O == 1) { #ifdef USE_OPENMP #pragma omp parallel for if(mt) #endif for(int i=0;i maxN) maxN = tmp; } if (maxN <= EPS1) { Result result = { it, eInit, e, lambdas, params}; return result; } } } else { if (O == 1) { // change the hessian back to normal for(int i=0;i 1.e15) { Result r = { it, eInit, e, lambdas, params }; if(dbg) dbg(r); return r; } } if(dbg){ Result r = { it, eInit, e, lambdas, params}; dbg(r); } } } Result result = { it, eInit, e, lambdas, params }; return result; } template typename LevenbergMarquardtFitter::Result LevenbergMarquardtFitter::fitMat(const Matrix &xs, const Matrix &ys, Params params){ const int O = ys.cols(); // output dim ICLASSERT_THROW(O == (int)jsMat.size(), ICLException("LevenbergMarquardtFitter::fit: ys.cols() and outputDim differ")); const int D = xs.cols(); const int P = params.dim(); const int MAX_IT = maxIterations; const Scalar MIN_E = minError; const Scalar EPS1 = eps1; const Scalar EPS2 = eps2; dst.setDim(P); J.setBounds(D,P); H.setBounds(P,P); H.setBounds(P,P); params_new.setDim(P); y_est.setBounds(O,D); dy.setBounds(D); std::vector v(O,2.0); std::vector lambdas(O,0.0); y_est = fMat(params, xs); Scalar e = error(ys,y_est); Scalar eInit = e; if(e < minError){ Result r = {-1, eInit, e, lambdas, params}; if(dbg) dbg(r); return r; } int it = 0; for(; it < MAX_IT; ++it){ for(int o=0;o 1) { if (o > 0) y_est = fMat(params, xs); jsMat[o](params, xs, J); dy = y_est.row(o).transp() - ys.transp().row(o).transp(); matrix_mult_t(J.transp(),J.transp(),H,SRC1_T); matrix_mult_t(J.transp(),dy,dst,SRC1_T); Scalar maxN = fabs(dst[0]); for (unsigned int i = 1; i < dst.rows(); ++i) { Scalar tmp = fabs(dst[i]); if (tmp > maxN) maxN = tmp; } if (maxN <= EPS1) { Result result = { it, eInit, e, lambdas, params}; return result; } // first guess of lambda if (it == 0) { Scalar H_max = H(0,0); for (unsigned int i = 1; i < H.cols(); ++i) { if (H(i,i) > H_max) H_max = H(i,i); } lambdas[o] = tau * H_max; } } for(int i=0;i 0.0) { v[o] = 2.0; params = params_new; e = e_new; lambdas[o] *= iclMax(1.0/3.0, 1.0 - pow(2.0*delta - 1.0, 3)); if (O == 1) { jsMat[o](params, xs, J); dy = y_est.row(o).transp() - ys.transp().row(o).transp(); matrix_mult_t(J.transp(),J.transp(),H,SRC1_T); matrix_mult_t(J.transp(),dy,dst,SRC1_T); Scalar maxN = fabs(dst[0]); for (unsigned int i = 1; i < dst.rows(); ++i) { Scalar tmp = fabs(dst[i]); if (tmp > maxN) maxN = tmp; } if (maxN <= EPS1) { Result result = { it, eInit, e, lambdas, params}; return result; } } } else { if (O == 1) { // change the hessian back to normal for(int i=0;i 1.e15) { Result r = { it, eInit, e, lambdas, params }; if(dbg) dbg(r); return r; } } if(dbg){ Result r = { it, eInit, e, lambdas, params}; dbg(r); } } } Result result = { it, eInit, e, lambdas, params }; return result; } template typename LevenbergMarquardtFitter::Result LevenbergMarquardtFitter::fit(const Matrix &xs, const Matrix &ys, Params params){ if (useMat) return fitMat(xs, ys, params); else return fitVec(xs, ys, params); } namespace{ template struct NumericJacobian : public FunctionImpl&, const DynColVector&, DynColVector&>{ typedef typename LevenbergMarquardtFitter::Function Function; typedef typename LevenbergMarquardtFitter::Params Params; typedef typename LevenbergMarquardtFitter::Vector Vector; int o; Function f; Scalar delta; NumericJacobian(int o, Function f, Scalar delta): o(o),f(f),delta(delta){} virtual void operator()(const Params ¶ms, const Vector &x, Vector &target) const{ Vector p = params; for(unsigned int i=0;i struct NumericJacobianMat : public FunctionImpl&, const DynMatrix&, DynMatrix&>{ typedef typename LevenbergMarquardtFitter::FunctionMat FunctionMat; typedef typename LevenbergMarquardtFitter::Params Params; typedef typename LevenbergMarquardtFitter::Vector Vector; typedef typename LevenbergMarquardtFitter::Matrix Matrix; int o; FunctionMat f; Scalar delta; NumericJacobianMat(int o, FunctionMat f, Scalar delta): o(o),f(f),delta(delta){} virtual void operator()(const Params ¶ms, const Matrix &x, Matrix &target) const{ Vector p = params; for(unsigned int i=0;i typename LevenbergMarquardtFitter::Jacobian LevenbergMarquardtFitter::create_numerical_jacobian(int o, Function f, float delta){ return Jacobian(new NumericJacobian(o,f,delta)); } template typename LevenbergMarquardtFitter::JacobianMat LevenbergMarquardtFitter::create_numerical_jacobian(int o, FunctionMat f, float delta){ return JacobianMat(new NumericJacobianMat(o,f,delta)); } template std::vector::Jacobian> LevenbergMarquardtFitter::create_numerical_jacobians(int n, Function f, float delta){ std::vector::Jacobian> js(n); for(int i=0;i std::vector::JacobianMat> LevenbergMarquardtFitter::create_numerical_jacobians(int n, FunctionMat f, float delta){ std::vector::JacobianMat> js(n); for(int i=0;i void LevenbergMarquardtFitter::setDebugCallback(DebugCallback dbg){ this->dbg = dbg; } template typename LevenbergMarquardtFitter::Data LevenbergMarquardtFitter::create_data(const Params &p, Function f, int xDim, int yDim, int num, Scalar minX, Scalar maxX){ URand r(minX,maxX); Data data = { Matrix(xDim,num), Matrix(yDim, num) }; for(int i=0;i void LevenbergMarquardtFitter::default_debug_callback(const Result &r){ std::cout << r << std::endl; } template class ICLMath_API LevenbergMarquardtFitter; template class ICLMath_API LevenbergMarquardtFitter; } // namespace math }