Main MRPT website > C++ reference
MRPT logo
CLevenbergMarquardt.h
Go to the documentation of this file.
00001 /* +---------------------------------------------------------------------------+
00002    |          The Mobile Robot Programming Toolkit (MRPT) C++ library          |
00003    |                                                                           |
00004    |                       http://www.mrpt.org/                                |
00005    |                                                                           |
00006    |   Copyright (C) 2005-2011  University of Malaga                           |
00007    |                                                                           |
00008    |    This software was written by the Machine Perception and Intelligent    |
00009    |      Robotics Lab, University of Malaga (Spain).                          |
00010    |    Contact: Jose-Luis Blanco  <jlblanco@ctima.uma.es>                     |
00011    |                                                                           |
00012    |  This file is part of the MRPT project.                                   |
00013    |                                                                           |
00014    |     MRPT is free software: you can redistribute it and/or modify          |
00015    |     it under the terms of the GNU General Public License as published by  |
00016    |     the Free Software Foundation, either version 3 of the License, or     |
00017    |     (at your option) any later version.                                   |
00018    |                                                                           |
00019    |   MRPT is distributed in the hope that it will be useful,                 |
00020    |     but WITHOUT ANY WARRANTY; without even the implied warranty of        |
00021    |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         |
00022    |     GNU General Public License for more details.                          |
00023    |                                                                           |
00024    |     You should have received a copy of the GNU General Public License     |
00025    |     along with MRPT.  If not, see <http://www.gnu.org/licenses/>.         |
00026    |                                                                           |
00027    +---------------------------------------------------------------------------+ */
00028 #ifndef  CLevenbergMarquardt_H
00029 #define  CLevenbergMarquardt_H
00030 
00031 #include <mrpt/utils/CDebugOutputCapable.h>
00032 #include <mrpt/math/CMatrixD.h>
00033 #include <mrpt/math/utils.h>
00034 
00035 /*---------------------------------------------------------------
00036         Class
00037   ---------------------------------------------------------------*/
00038 namespace mrpt
00039 {
00040 namespace math
00041 {
00042         /** An implementation of the Levenberg-Marquardt algorithm for least-square minimization.
00043          *
00044          *  Refer to this <a href="http://www.mrpt.org/Levenberg%E2%80%93Marquardt_algorithm" >page</a> for more details on the algorithm and its usage.
00045          *
00046          * \tparam NUMTYPE The numeric type for all the operations (float, double, or long double)
00047          * \tparam USERPARAM The type of the "y" input to the user supplied evaluation functor. Default type is a vector of NUMTYPE.
00048          * \ingroup mrpt_base_grp
00049          */
00050         template <typename VECTORTYPE = mrpt::vector_double, class USERPARAM = VECTORTYPE >
00051         class CLevenbergMarquardtTempl : public mrpt::utils::CDebugOutputCapable
00052         {
00053         public:
00054                 typedef typename VECTORTYPE::value_type  NUMTYPE;
00055 
00056 
00057                 /** The type of the function passed to execute. The user must supply a function which evaluates the error of a given point in the solution space.
00058                   *  \param x The state point under examination.
00059                   *  \param y The same object passed to "execute" as the parameter "userParam".
00060                   *  \param out The vector of (non-squared) errors, of the average square root error, for the given "x". The functor code must set the size of this vector.
00061                   */
00062                 typedef void (*TFunctorEval)(
00063                         const VECTORTYPE &x,
00064                         const USERPARAM &y,
00065                         VECTORTYPE &out);
00066 
00067                 /** The type of an optional functor passed to \a execute to replace the Euclidean addition "x_new = x_old + x_incr" by any other operation.
00068                   */
00069                 typedef void (*TFunctorIncrement)(
00070                         VECTORTYPE &x_new,
00071                         const VECTORTYPE &x_old,
00072                         const VECTORTYPE &x_incr,
00073                         const USERPARAM &user_param);
00074 
00075                 struct TResultInfo
00076                 {
00077                         NUMTYPE         final_sqr_err;
00078                         size_t          iterations_executed;
00079                         VECTORTYPE      last_err_vector;                //!< The last error vector returned by the user-provided functor.
00080                         CMatrixTemplateNumeric<NUMTYPE> path;   //!< Each row is the optimized value at each iteration.
00081 
00082                         /** This matrix can be used to obtain an estimate of the optimal parameters covariance matrix:
00083                           *  \f[ COV = H M H^\top \f]
00084                           *  With COV the covariance matrix of the optimal parameters, H this matrix, and M the covariance of the input (observations).
00085                           */
00086                         //CMatrixTemplateNumeric<NUMTYPE> H;
00087                 };
00088 
00089                 /** Executes the LM-method, with derivatives estimated from
00090                   *  \a functor is a user-provided function which takes as input two vectors, in this order:
00091                   *             - x: The parameters to be optimized.
00092                   *             - userParam: The vector passed to the LM algorithm, unmodified.
00093                   *       and must return the "error vector", or the error (not squared) in each measured dimension, so the sum of the square of that output is the overall square error.
00094                   *
00095                   * \a x_increment_adder Is an optional functor which may replace the Euclidean "x_new = x + x_increment" at the core of the incremental optimizer by
00096                   *     any other operation. It can be used for example, in on-manifold optimizations.
00097                   */
00098                 static void     execute(
00099                         VECTORTYPE                      &out_optimal_x,
00100                         const VECTORTYPE        &x0,
00101                         TFunctorEval            functor,
00102                         const VECTORTYPE        &increments,
00103                         const USERPARAM         &userParam,
00104                         TResultInfo                     &out_info,
00105                         bool                            verbose = false,
00106                         const size_t            maxIter = 200,
00107                         const NUMTYPE           tau = 1e-3,
00108                         const NUMTYPE           e1 = 1e-8,
00109                         const NUMTYPE           e2 = 1e-8,
00110                         bool                returnPath =true,
00111                         TFunctorIncrement       x_increment_adder = NULL
00112                         )
00113                 {
00114                         using namespace mrpt;
00115                         using namespace mrpt::utils;
00116                         using namespace mrpt::math;
00117                         using namespace std;
00118 
00119                         MRPT_START
00120 
00121                         VECTORTYPE &x=out_optimal_x; // Var rename
00122 
00123                         // Asserts:
00124                         ASSERT_( increments.size() == x0.size() );
00125 
00126                         x=x0;                                                                   // Start with the starting point
00127                         VECTORTYPE      f_x;                                    // The vector error from the user function
00128                         CMatrixTemplateNumeric<NUMTYPE> AUX;
00129                         CMatrixTemplateNumeric<NUMTYPE> J;              // The Jacobian of "f"
00130                         CMatrixTemplateNumeric<NUMTYPE> H;              // The Hessian of "f"
00131                         VECTORTYPE      g;                                              // The gradient
00132 
00133                         // Compute the jacobian and the Hessian:
00134                         mrpt::math::estimateJacobian( x, functor, increments, userParam, J);
00135                         H.multiply_AtA(J);
00136 
00137                         const size_t  H_len = H.getColCount();
00138 
00139                         // Compute the gradient:
00140                         functor(x, userParam ,f_x);
00141                         J.multiply_Atb(f_x, g);
00142 
00143                         // Start iterations:
00144                         bool    found = math::norm_inf(g)<=e1;
00145                         if (verbose && found)   printf_debug("[LM] End condition: math::norm_inf(g)<=e1 :%f\n",math::norm_inf(g));
00146 
00147                         NUMTYPE lambda = tau * H.maximumDiagonal();
00148                         size_t  iter = 0;
00149                         NUMTYPE v = 2;
00150 
00151                         VECTORTYPE      h_lm;
00152                         VECTORTYPE      xnew, f_xnew ;
00153                         NUMTYPE                 F_x  = pow( math::norm( f_x ), 2);
00154 
00155                         const size_t    N = x.size();
00156 
00157                         if (returnPath) {
00158                                 out_info.path.setSize(maxIter,N+1);
00159                                 out_info.path.block(iter,0,1,N) = x.transpose();
00160                         }       else out_info.path = Eigen::Matrix<NUMTYPE,Eigen::Dynamic,Eigen::Dynamic>(); // Empty matrix
00161 
00162                         while (!found && ++iter<maxIter)
00163                         {
00164                                 // H_lm = -( H + \lambda I ) ^-1 * g
00165                                 for (size_t k=0;k<H_len;k++)
00166                                         H(k,k)+= lambda;
00167                                         //H(k,k) *= 1+lambda;
00168 
00169                                 H.inv_fast(AUX);
00170                                 AUX.multiply_Ab(g,h_lm);
00171                                 h_lm *= NUMTYPE(-1.0);
00172 
00173                                 double h_lm_n2 = math::norm(h_lm);
00174                                 double x_n2 = math::norm(x);
00175 
00176                                 if (verbose) printf_debug( (format("[LM] Iter: %u x:",(unsigned)iter)+ sprintf_vector(" %f",x) + string("\n")).c_str() );
00177 
00178                                 if (h_lm_n2<e2*(x_n2+e2))
00179                                 {
00180                                         // Done:
00181                                         found = true;
00182                                         if (verbose) printf_debug("[LM] End condition: %e < %e\n", h_lm_n2, e2*(x_n2+e2) );
00183                                 }
00184                                 else
00185                                 {
00186                                         // Improvement: xnew = x + h_lm;
00187                                         if (!x_increment_adder)
00188                                                         xnew = x + h_lm; // Normal Euclidean space addition.
00189                                         else    x_increment_adder(xnew, x, h_lm, userParam);
00190 
00191                                         functor(xnew, userParam ,f_xnew );
00192                                         const double F_xnew = pow( math::norm(f_xnew), 2);
00193 
00194                                         // denom = h_lm^t * ( \lambda * h_lm - g )
00195                                         VECTORTYPE      tmp(h_lm);
00196                                         tmp *= lambda;
00197                                         tmp -= g;
00198                                         tmp.array() *=h_lm.array();
00199                                         double denom = tmp.sum();
00200                                         double l = (F_x - F_xnew) / denom;
00201 
00202                                         if (l>0) // There is an improvement:
00203                                         {
00204                                                 // Accept new point:
00205                                                 x   = xnew;
00206                                                 f_x = f_xnew;
00207                                                 F_x = F_xnew;
00208 
00209                                                 math::estimateJacobian( x, functor, increments, userParam, J);
00210                                                 H.multiply_AtA(J);
00211                                                 J.multiply_Atb(f_x, g);
00212 
00213                                                 found = math::norm_inf(g)<=e1;
00214                                                 if (verbose && found)   printf_debug("[LM] End condition: math::norm_inf(g)<=e1 : %e\n", math::norm_inf(g) );
00215 
00216                                                 lambda *= max(0.33, 1-pow(2*l-1,3) );
00217                                                 v = 2;
00218                                         }
00219                                         else
00220                                         {
00221                                                 // Nope...
00222                                                 lambda *= v;
00223                                                 v*= 2;
00224                                         }
00225 
00226 
00227                                         if (returnPath) {
00228                                                 out_info.path.block(iter,0,1,x.size()) = x.transpose();
00229                                                 out_info.path(iter,x.size()) = F_x;
00230                                         }
00231                                 }
00232                         } // end while
00233 
00234                         // Output info:
00235                         out_info.final_sqr_err = F_x;
00236                         out_info.iterations_executed = iter;
00237                         out_info.last_err_vector = f_x;
00238                         if (returnPath) out_info.path.setSize(iter,N+1);
00239 
00240                         MRPT_END
00241                 }
00242 
00243         }; // End of class def.
00244 
00245 
00246         typedef CLevenbergMarquardtTempl<vector_double> CLevenbergMarquardt;  //!< The default name for the LM class is an instantiation for "double"
00247 
00248         } // End of namespace
00249 } // End of namespace
00250 #endif



Page generated by Doxygen 1.7.5 for MRPT 0.9.5 SVN: at Thu Oct 13 21:25:36 UTC 2011