| // This file is part of Eigen, a lightweight C++ template library |
| // for linear algebra. |
| // |
| // Copyright (C) 2009 Thomas Capricelli <orzel@freehackers.org> |
| // Copyright (C) 2012 Desire Nuentsa <desire.nuentsa_wakam@inria.fr> |
| // |
| // The algorithm of this class initially comes from MINPACK whose original authors are: |
| // Copyright Jorge More - Argonne National Laboratory |
| // Copyright Burt Garbow - Argonne National Laboratory |
| // Copyright Ken Hillstrom - Argonne National Laboratory |
| // |
| // This Source Code Form is subject to the terms of the Minpack license |
| // (a BSD-like license) described in the campaigned CopyrightMINPACK.txt file. |
| // |
| // This Source Code Form is subject to the terms of the Mozilla |
| // Public License v. 2.0. If a copy of the MPL was not distributed |
| // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| |
| #ifndef EIGEN_LEVENBERGMARQUARDT_H |
| #define EIGEN_LEVENBERGMARQUARDT_H |
| |
| |
| #include "./InternalHeaderCheck.h" |
| |
| namespace Eigen { |
| namespace LevenbergMarquardtSpace { |
| enum Status { |
| NotStarted = -2, |
| Running = -1, |
| ImproperInputParameters = 0, |
| RelativeReductionTooSmall = 1, |
| RelativeErrorTooSmall = 2, |
| RelativeErrorAndReductionTooSmall = 3, |
| CosinusTooSmall = 4, |
| TooManyFunctionEvaluation = 5, |
| FtolTooSmall = 6, |
| XtolTooSmall = 7, |
| GtolTooSmall = 8, |
| UserAsked = 9 |
| }; |
| } |
| |
| template <typename Scalar_, int NX=Dynamic, int NY=Dynamic> |
| struct DenseFunctor |
| { |
| typedef Scalar_ Scalar; |
| enum { |
| InputsAtCompileTime = NX, |
| ValuesAtCompileTime = NY |
| }; |
| typedef Matrix<Scalar,InputsAtCompileTime,1> InputType; |
| typedef Matrix<Scalar,ValuesAtCompileTime,1> ValueType; |
| typedef Matrix<Scalar,ValuesAtCompileTime,InputsAtCompileTime> JacobianType; |
| typedef ColPivHouseholderQR<JacobianType> QRSolver; |
| const int m_inputs, m_values; |
| |
| DenseFunctor() : m_inputs(InputsAtCompileTime), m_values(ValuesAtCompileTime) {} |
| DenseFunctor(int inputs, int values) : m_inputs(inputs), m_values(values) {} |
| |
| int inputs() const { return m_inputs; } |
| int values() const { return m_values; } |
| |
| //int operator()(const InputType &x, ValueType& fvec) { } |
| // should be defined in derived classes |
| |
| //int df(const InputType &x, JacobianType& fjac) { } |
| // should be defined in derived classes |
| }; |
| |
| template <typename Scalar_, typename Index_> |
| struct SparseFunctor |
| { |
| typedef Scalar_ Scalar; |
| typedef Index_ Index; |
| typedef Matrix<Scalar,Dynamic,1> InputType; |
| typedef Matrix<Scalar,Dynamic,1> ValueType; |
| typedef SparseMatrix<Scalar, ColMajor, Index> JacobianType; |
| typedef SparseQR<JacobianType, COLAMDOrdering<int> > QRSolver; |
| enum { |
| InputsAtCompileTime = Dynamic, |
| ValuesAtCompileTime = Dynamic |
| }; |
| |
| SparseFunctor(int inputs, int values) : m_inputs(inputs), m_values(values) {} |
| |
| int inputs() const { return m_inputs; } |
| int values() const { return m_values; } |
| |
| const int m_inputs, m_values; |
| //int operator()(const InputType &x, ValueType& fvec) { } |
| // to be defined in the functor |
| |
| //int df(const InputType &x, JacobianType& fjac) { } |
| // to be defined in the functor if no automatic differentiation |
| |
| }; |
| namespace internal { |
| template <typename QRSolver, typename VectorType> |
| void lmpar2(const QRSolver &qr, const VectorType &diag, const VectorType &qtb, |
| typename VectorType::Scalar m_delta, typename VectorType::Scalar &par, |
| VectorType &x); |
| } |
| /** |
| * \ingroup NonLinearOptimization_Module |
| * \brief Performs non linear optimization over a non-linear function, |
| * using a variant of the Levenberg Marquardt algorithm. |
| * |
| * Check wikipedia for more information. |
| * http://en.wikipedia.org/wiki/Levenberg%E2%80%93Marquardt_algorithm |
| */ |
| template<typename FunctorType_> |
| class LevenbergMarquardt : internal::no_assignment_operator |
| { |
| public: |
| typedef FunctorType_ FunctorType; |
| typedef typename FunctorType::QRSolver QRSolver; |
| typedef typename FunctorType::JacobianType JacobianType; |
| typedef typename JacobianType::Scalar Scalar; |
| typedef typename JacobianType::RealScalar RealScalar; |
| typedef typename QRSolver::StorageIndex PermIndex; |
| typedef Matrix<Scalar,Dynamic,1> FVectorType; |
| typedef PermutationMatrix<Dynamic,Dynamic,int> PermutationType; |
| public: |
| LevenbergMarquardt(FunctorType& functor) |
| : m_functor(functor),m_nfev(0),m_njev(0),m_fnorm(0.0),m_gnorm(0), |
| m_isInitialized(false),m_info(InvalidInput) |
| { |
| resetParameters(); |
| m_useExternalScaling=false; |
| } |
| |
| LevenbergMarquardtSpace::Status minimize(FVectorType &x); |
| LevenbergMarquardtSpace::Status minimizeInit(FVectorType &x); |
| LevenbergMarquardtSpace::Status minimizeOneStep(FVectorType &x); |
| LevenbergMarquardtSpace::Status lmder1( |
| FVectorType &x, |
| const Scalar tol = std::sqrt(NumTraits<Scalar>::epsilon()) |
| ); |
| static LevenbergMarquardtSpace::Status lmdif1( |
| FunctorType &functor, |
| FVectorType &x, |
| Index *nfev, |
| const Scalar tol = std::sqrt(NumTraits<Scalar>::epsilon()) |
| ); |
| |
| /** Sets the default parameters */ |
| void resetParameters() |
| { |
| using std::sqrt; |
| |
| m_factor = 100.; |
| m_maxfev = 400; |
| m_ftol = sqrt(NumTraits<RealScalar>::epsilon()); |
| m_xtol = sqrt(NumTraits<RealScalar>::epsilon()); |
| m_gtol = 0. ; |
| m_epsfcn = 0. ; |
| } |
| |
| /** Sets the tolerance for the norm of the solution vector*/ |
| void setXtol(RealScalar xtol) { m_xtol = xtol; } |
| |
| /** Sets the tolerance for the norm of the vector function*/ |
| void setFtol(RealScalar ftol) { m_ftol = ftol; } |
| |
| /** Sets the tolerance for the norm of the gradient of the error vector*/ |
| void setGtol(RealScalar gtol) { m_gtol = gtol; } |
| |
| /** Sets the step bound for the diagonal shift */ |
| void setFactor(RealScalar factor) { m_factor = factor; } |
| |
| /** Sets the error precision */ |
| void setEpsilon (RealScalar epsfcn) { m_epsfcn = epsfcn; } |
| |
| /** Sets the maximum number of function evaluation */ |
| void setMaxfev(Index maxfev) {m_maxfev = maxfev; } |
| |
| /** Use an external Scaling. If set to true, pass a nonzero diagonal to diag() */ |
| void setExternalScaling(bool value) {m_useExternalScaling = value; } |
| |
| /** \returns the tolerance for the norm of the solution vector */ |
| RealScalar xtol() const {return m_xtol; } |
| |
| /** \returns the tolerance for the norm of the vector function */ |
| RealScalar ftol() const {return m_ftol; } |
| |
| /** \returns the tolerance for the norm of the gradient of the error vector */ |
| RealScalar gtol() const {return m_gtol; } |
| |
| /** \returns the step bound for the diagonal shift */ |
| RealScalar factor() const {return m_factor; } |
| |
| /** \returns the error precision */ |
| RealScalar epsilon() const {return m_epsfcn; } |
| |
| /** \returns the maximum number of function evaluation */ |
| Index maxfev() const {return m_maxfev; } |
| |
| /** \returns a reference to the diagonal of the jacobian */ |
| FVectorType& diag() {return m_diag; } |
| |
| /** \returns the number of iterations performed */ |
| Index iterations() { return m_iter; } |
| |
| /** \returns the number of functions evaluation */ |
| Index nfev() { return m_nfev; } |
| |
| /** \returns the number of jacobian evaluation */ |
| Index njev() { return m_njev; } |
| |
| /** \returns the norm of current vector function */ |
| RealScalar fnorm() {return m_fnorm; } |
| |
| /** \returns the norm of the gradient of the error */ |
| RealScalar gnorm() {return m_gnorm; } |
| |
| /** \returns the LevenbergMarquardt parameter */ |
| RealScalar lm_param(void) { return m_par; } |
| |
| /** \returns a reference to the current vector function |
| */ |
| FVectorType& fvec() {return m_fvec; } |
| |
| /** \returns a reference to the matrix where the current Jacobian matrix is stored |
| */ |
| JacobianType& jacobian() {return m_fjac; } |
| |
| /** \returns a reference to the triangular matrix R from the QR of the jacobian matrix. |
| * \sa jacobian() |
| */ |
| JacobianType& matrixR() {return m_rfactor; } |
| |
| /** the permutation used in the QR factorization |
| */ |
| PermutationType permutation() {return m_permutation; } |
| |
| /** |
| * \brief Reports whether the minimization was successful |
| * \returns \c Success if the minimization was successful, |
| * \c NumericalIssue if a numerical problem arises during the |
| * minimization process, for example during the QR factorization |
| * \c NoConvergence if the minimization did not converge after |
| * the maximum number of function evaluation allowed |
| * \c InvalidInput if the input matrix is invalid |
| */ |
| ComputationInfo info() const |
| { |
| |
| return m_info; |
| } |
| private: |
| JacobianType m_fjac; |
| JacobianType m_rfactor; // The triangular matrix R from the QR of the jacobian matrix m_fjac |
| FunctorType &m_functor; |
| FVectorType m_fvec, m_qtf, m_diag; |
| Index n; |
| Index m; |
| Index m_nfev; |
| Index m_njev; |
| RealScalar m_fnorm; // Norm of the current vector function |
| RealScalar m_gnorm; //Norm of the gradient of the error |
| RealScalar m_factor; // |
| Index m_maxfev; // Maximum number of function evaluation |
| RealScalar m_ftol; //Tolerance in the norm of the vector function |
| RealScalar m_xtol; // |
| RealScalar m_gtol; //tolerance of the norm of the error gradient |
| RealScalar m_epsfcn; // |
| Index m_iter; // Number of iterations performed |
| RealScalar m_delta; |
| bool m_useExternalScaling; |
| PermutationType m_permutation; |
| FVectorType m_wa1, m_wa2, m_wa3, m_wa4; //Temporary vectors |
| RealScalar m_par; |
| bool m_isInitialized; // Check whether the minimization step has been called |
| ComputationInfo m_info; |
| }; |
| |
| template<typename FunctorType> |
| LevenbergMarquardtSpace::Status |
| LevenbergMarquardt<FunctorType>::minimize(FVectorType &x) |
| { |
| LevenbergMarquardtSpace::Status status = minimizeInit(x); |
| if (status==LevenbergMarquardtSpace::ImproperInputParameters) { |
| m_isInitialized = true; |
| return status; |
| } |
| do { |
| // std::cout << " uv " << x.transpose() << "\n"; |
| status = minimizeOneStep(x); |
| } while (status==LevenbergMarquardtSpace::Running); |
| m_isInitialized = true; |
| return status; |
| } |
| |
| template<typename FunctorType> |
| LevenbergMarquardtSpace::Status |
| LevenbergMarquardt<FunctorType>::minimizeInit(FVectorType &x) |
| { |
| n = x.size(); |
| m = m_functor.values(); |
| |
| m_wa1.resize(n); m_wa2.resize(n); m_wa3.resize(n); |
| m_wa4.resize(m); |
| m_fvec.resize(m); |
| //FIXME Sparse Case : Allocate space for the jacobian |
| m_fjac.resize(m, n); |
| // m_fjac.reserve(VectorXi::Constant(n,5)); // FIXME Find a better alternative |
| if (!m_useExternalScaling) |
| m_diag.resize(n); |
| eigen_assert( (!m_useExternalScaling || m_diag.size()==n) && "When m_useExternalScaling is set, the caller must provide a valid 'm_diag'"); |
| m_qtf.resize(n); |
| |
| /* Function Body */ |
| m_nfev = 0; |
| m_njev = 0; |
| |
| /* check the input parameters for errors. */ |
| if (n <= 0 || m < n || m_ftol < 0. || m_xtol < 0. || m_gtol < 0. || m_maxfev <= 0 || m_factor <= 0.){ |
| m_info = InvalidInput; |
| return LevenbergMarquardtSpace::ImproperInputParameters; |
| } |
| |
| if (m_useExternalScaling) |
| for (Index j = 0; j < n; ++j) |
| if (m_diag[j] <= 0.) |
| { |
| m_info = InvalidInput; |
| return LevenbergMarquardtSpace::ImproperInputParameters; |
| } |
| |
| /* evaluate the function at the starting point */ |
| /* and calculate its norm. */ |
| m_nfev = 1; |
| if ( m_functor(x, m_fvec) < 0) |
| return LevenbergMarquardtSpace::UserAsked; |
| m_fnorm = m_fvec.stableNorm(); |
| |
| /* initialize levenberg-marquardt parameter and iteration counter. */ |
| m_par = 0.; |
| m_iter = 1; |
| |
| return LevenbergMarquardtSpace::NotStarted; |
| } |
| |
| template<typename FunctorType> |
| LevenbergMarquardtSpace::Status |
| LevenbergMarquardt<FunctorType>::lmder1( |
| FVectorType &x, |
| const Scalar tol |
| ) |
| { |
| n = x.size(); |
| m = m_functor.values(); |
| |
| /* check the input parameters for errors. */ |
| if (n <= 0 || m < n || tol < 0.) |
| return LevenbergMarquardtSpace::ImproperInputParameters; |
| |
| resetParameters(); |
| m_ftol = tol; |
| m_xtol = tol; |
| m_maxfev = 100*(n+1); |
| |
| return minimize(x); |
| } |
| |
| |
| template<typename FunctorType> |
| LevenbergMarquardtSpace::Status |
| LevenbergMarquardt<FunctorType>::lmdif1( |
| FunctorType &functor, |
| FVectorType &x, |
| Index *nfev, |
| const Scalar tol |
| ) |
| { |
| Index n = x.size(); |
| Index m = functor.values(); |
| |
| /* check the input parameters for errors. */ |
| if (n <= 0 || m < n || tol < 0.) |
| return LevenbergMarquardtSpace::ImproperInputParameters; |
| |
| NumericalDiff<FunctorType> numDiff(functor); |
| // embedded LevenbergMarquardt |
| LevenbergMarquardt<NumericalDiff<FunctorType> > lm(numDiff); |
| lm.setFtol(tol); |
| lm.setXtol(tol); |
| lm.setMaxfev(200*(n+1)); |
| |
| LevenbergMarquardtSpace::Status info = LevenbergMarquardtSpace::Status(lm.minimize(x)); |
| if (nfev) |
| * nfev = lm.nfev(); |
| return info; |
| } |
| |
| } // end namespace Eigen |
| |
| #endif // EIGEN_LEVENBERGMARQUARDT_H |