|  | // 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 | 
|  |  | 
|  | // IWYU pragma: private | 
|  | #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 |