Go to the documentation of this file.00001 #ifndef OPENTISSUE_CORE_MATH_OPTIMIZATION_BFGS_H
00002 #define OPENTISSUE_CORE_MATH_OPTIMIZATION_BFGS_H
00003
00004
00005
00006
00007
00008
00009
00010 #include <OpenTissue/configuration.h>
00011
00012 #include <OpenTissue/core/math/big/big_types.h>
00013 #include <OpenTissue/core/math/optimization/optimization_constants.h>
00014 #include <OpenTissue/core/math/optimization/optimization_armijo_backtracking.h>
00015 #include <OpenTissue/core/math/optimization/optimization_stationary_point.h>
00016
00017 #include <OpenTissue/core/math/math_value_traits.h>
00018 #include <OpenTissue/core/math/math_is_number.h>
00019
00020 #include <cmath>
00021 #include <stdexcept>
00022 #include <cassert>
00023
00024 namespace OpenTissue
00025 {
00026 namespace math
00027 {
00028 namespace optimization
00029 {
00030
00031 namespace detail
00032 {
00033
00044 template<typename vector_type,typename matrix_type>
00045 inline void bfgs_update_inverse_hessian(vector_type const & y,vector_type const & s, matrix_type & H)
00046 {
00047 using std::fabs;
00048
00049 typedef typename matrix_type::value_type real_type;
00050 typedef typename ublas::identity_matrix<real_type> identity_matrix_type;
00051 typedef typename OpenTissue::math::ValueTraits<real_type> value_traits;
00052
00053 if(H.size1() <= 0 || H.size2() <= 0)
00054 throw std::invalid_argument("bfgs_update_inverse_hessian(): Hessian was empty");
00055 if(y.size() <= 0 )
00056 throw std::invalid_argument("bfgs_update_inverse_hessian(): y_i was empty");
00057 if(s.size() <= 0 )
00058 throw std::invalid_argument("bfgs_update_inverse_hessian(): s_i was empty");
00059
00060 assert(y.size()==H.size2() || "bfgs_update_inverse_hessian(): inconsistent matrix vector dimensions");
00061
00062 assert(H.size1()==H.size2() || "bfgs_update_inverse_hessian(): Incompatible dimensions of Hessian matrix");
00063
00064 size_t const N = H.size1();
00065
00066 real_type dot = inner_prod(y,s);
00067 if( fabs(dot) <= value_traits::zero() )
00068 {
00069 dot = value_traits::one();
00070 }
00071
00072 real_type rho = value_traits::one() / dot;
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113 vector_type v;
00114 v.resize(N,false);
00115 ublas::axpy_prod(H,y,v,true);
00116 real_type gamma = rho*rho*ublas::inner_prod(y,v) + rho;
00117 for (typename vector_type::size_type i = 0; i < N; ++ i)
00118 {
00119 ublas::row(H,i) -= rho*s(i) * v;
00120 ublas::row(H,i) += (gamma*s(i) - rho * v(i)) * s;
00121 }
00122
00123 }
00124
00125 }
00126
00259 template <
00260 typename T
00261 , typename function_functor
00262 , typename gradient_functor
00263 >
00264 inline void bfgs(
00265 function_functor & f
00266 , gradient_functor & nabla_f
00267 , ublas::compressed_matrix<T> & H
00268 , boost::numeric::ublas::vector<T> & x
00269 , size_t const & max_iterations
00270 , T const & absolute_tolerance
00271 , T const & relative_tolerance
00272 , T const & stagnation_tolerance
00273 , size_t & status
00274 , size_t & iteration
00275 , T & error
00276 , T const & alpha
00277 , T const & beta
00278 , ublas::vector<T> * profiling = 0
00279 )
00280 {
00281 using std::fabs;
00282 using std::min;
00283 using std::max;
00284
00285 typedef ublas::compressed_matrix<T> matrix_type;
00286 typedef ublas::vector<T> vector_type;
00287 typedef T real_type;
00288 typedef OpenTissue::math::ValueTraits<T> value_traits;
00289
00290 if(max_iterations <= 0)
00291 throw std::invalid_argument("max_iterations must be larger than zero");
00292 if(absolute_tolerance < value_traits::zero() )
00293 throw std::invalid_argument("absolute_tolerance must be non-negative");
00294 if(relative_tolerance < value_traits::zero() )
00295 throw std::invalid_argument("relative_tolerance must be non-negative");
00296 if(stagnation_tolerance < value_traits::zero() )
00297 throw std::invalid_argument("stagnation_tolerance must be non-negative");
00298 if (beta >= value_traits::one() )
00299 throw std::invalid_argument("Illegal beta value");
00300 if (alpha <= value_traits::zero() )
00301 throw std::invalid_argument("Illegal alpha value");
00302 if(beta<=alpha)
00303 throw std::invalid_argument("beta must be larger than alpha");
00304 if(profiling == &x)
00305 throw std::logic_error("profiling must not point to x-vector");
00306
00307 error = value_traits::infinity();
00308 iteration = 0;
00309 status = OK;
00310
00311 size_t const m = x.size();
00312 if(m==0)
00313 return;
00314
00315 status = ITERATING;
00316
00317 if(profiling)
00318 {
00319 (*profiling).resize( max_iterations );
00320 (*profiling).clear();
00321 }
00322
00323
00324 vector_type y_k;
00325 vector_type s_k;
00326 vector_type dx;
00327 vector_type x_old;
00328 vector_type nabla_f_k;
00329 vector_type nabla_f_k1;
00330
00331
00332 y_k.resize(m);
00333 s_k.resize(m);
00334 dx.resize(m);
00335 x_old.resize(m);
00336 nabla_f_k.resize(m);
00337 nabla_f_k1.resize(m);
00338
00339
00340 real_type f_0 = f(x);
00341 ublas::noalias( nabla_f_k ) = nabla_f(x);
00342
00343
00344 for (; iteration < max_iterations; ++iteration)
00345 {
00346 if(profiling)
00347 (*profiling)(iteration) = f_0;
00348
00349
00350 if(stationary_point( nabla_f_k, absolute_tolerance, error ) )
00351 {
00352 status = ABSOLUTE_CONVERGENCE;
00353 return;
00354 }
00355
00356
00357
00358 ublas::axpy_prod(H, -nabla_f_k, dx, true);
00359
00360 x_old.assign( x );
00361 real_type f_tau = f_0;
00362 armijo_backtracking(
00363 f
00364 , nabla_f_k
00365 , x_old
00366 , x
00367 , dx
00368 , relative_tolerance
00369 , stagnation_tolerance
00370 , alpha
00371 , beta
00372 , f_tau
00373 , status
00374 );
00375
00376 if(status != OK)
00377 return;
00378
00379
00380 ublas::noalias( nabla_f_k1 ) = nabla_f(x);
00381 ublas::noalias( s_k ) = x - x_old;
00382 ublas::noalias( y_k ) = nabla_f_k1 - nabla_f_k;
00383
00384 detail::bfgs_update_inverse_hessian(y_k,s_k,H);
00385
00386
00387 f_0 = f_tau;
00388 nabla_f_k.assign( nabla_f_k1 );
00389
00390 }
00391 }
00392
00393 }
00394 }
00395 }
00396
00397
00398 #endif