00001 /* 00002 Copyright (C) 2010 - 2014 Christoph Haenisch 00003 00004 Chair of Medical Engineering (mediTEC) 00005 RWTH Aachen University 00006 Pauwelsstr. 20 00007 52074 Aachen 00008 Germany 00009 00010 See license.txt for more information. 00011 00012 Version 0.1.0 (2012-06-10) 00013 */ 00014 00021 #ifndef OPTIMIZATION_HPP_4562354833 00022 #define OPTIMIZATION_HPP_4562354833 00023 00024 00025 #include <stdexcept> 00026 00027 #include <Eigen/Core> 00028 #include <Eigen/Dense> 00029 00030 #include "Coordinate.hpp" 00031 #include "Definitions.hpp" 00032 00033 00034 namespace TRTK 00035 { 00036 00037 00214 namespace Optimization 00215 { 00216 00217 00239 template <class ValueType> 00240 struct Options 00241 { 00242 typedef ValueType value_type; 00243 00244 value_type error_tolerance; 00245 value_type lambda; 00246 unsigned max_number_iterations; 00247 value_type nu; 00248 value_type spacing; 00249 00250 enum Algorithm 00251 { 00252 LEVENBERG_MARQUARDT, 00253 NEWTON_RAPHSON 00254 } algorithm; 00255 00256 Options() : 00257 error_tolerance(1e-15), 00258 lambda(1e-2), 00259 max_number_iterations(25), 00260 nu(10), 00261 spacing(1e-6), 00262 algorithm(LEVENBERG_MARQUARDT) 00263 { 00264 } 00265 }; 00266 00267 00273 template <class ValueType> 00274 struct Result 00275 { 00276 typedef Eigen::Matrix<ValueType, Eigen::Dynamic, Eigen::Dynamic> matrix_type; 00277 typedef TRTK::Coordinate<ValueType> Coordinate; 00278 typedef ValueType value_type; 00279 00280 value_type error; 00281 matrix_type jacobian; 00282 unsigned number_of_iterations; 00283 Coordinate root; 00284 00285 Result() : error(0), number_of_iterations(0) 00286 { 00287 } 00288 }; 00289 00290 00368 template <class Function, class ValueType> 00369 class Gradient 00370 { 00371 public: 00372 Gradient(Function & function, Options<ValueType> options = Options<ValueType>()); 00373 virtual ~Gradient(); 00374 00375 Coordinate<ValueType> operator()(const Coordinate<ValueType> & value); 00376 00377 private: 00378 Function & function; 00379 Options<ValueType> options; 00380 }; 00381 00382 00385 template<class Function, class ValueType> 00386 Gradient<Function, ValueType>::Gradient(Function & function, Options<ValueType> options) : 00387 function(function), 00388 options(options) 00389 { 00390 } 00391 00392 00395 template<class Function, class ValueType> 00396 Gradient<Function, ValueType>::~Gradient() 00397 { 00398 } 00399 00400 00403 template<class Function, class ValueType> 00404 Coordinate<ValueType> Gradient<Function, ValueType>::operator()(const Coordinate<ValueType> & value) 00405 { 00406 // function : R^n --> R 00407 00408 const unsigned n = value.size(); 00409 00410 Coordinate<ValueType> gradient; 00411 gradient.resize(n); 00412 00413 #ifdef TRTK_PARALLELIZE_GRADIENT 00414 #pragma omp parallel for 00415 #endif // TRTK_PARALLELIZE_GRADIENT 00416 00417 for (int i = 0; i < int(n); ++i) 00418 { 00419 // Use the five-point stencil method to compute the derivatives. 00420 00421 const ValueType spacing = options.spacing; 00422 00423 Coordinate<ValueType> h; 00424 h.resize(n); 00425 h[i] = spacing; // = (0, ..., 0, spacing, 0, ..., 0) 00426 00427 ValueType a = function(value - 2.0 * h); 00428 ValueType b = function(value - h); 00429 ValueType c = function(value + h); 00430 ValueType d = function(value + 2.0 * h); 00431 00432 gradient[i] = (a - 8.0 * b + 8.0 * c - d) / (12.0 * spacing); 00433 } 00434 00435 return gradient; 00436 } 00437 00438 00505 template <class Function, class ValueType> 00506 Eigen::Matrix<ValueType, Eigen::Dynamic, Eigen::Dynamic> jacobian(Function f, const Coordinate<ValueType> & x, Options<ValueType> options = Options<ValueType>()) 00507 { 00508 using namespace Eigen; 00509 00510 typedef TRTK::Coordinate<ValueType> Coordinate; 00511 00512 // function : R^n --> R^m 00513 00514 const unsigned n = x.size(); 00515 const unsigned m = f(x).size(); 00516 00517 Matrix<ValueType, Dynamic, Dynamic> jacobian(m, n); 00518 00519 #ifdef TRTK_PARALLELIZE_JACOBIAN 00520 #pragma omp parallel for 00521 #endif // TRTK_PARALLELIZE_JACOBIAN 00522 00523 for (int col = 0; col < int(n); ++col) 00524 { 00525 // Use the five-point stencil method to compute the derivatives. 00526 00527 const ValueType spacing = options.spacing; 00528 00529 Coordinate h; 00530 h.resize(n); 00531 h[col] = spacing; // = (0, ..., 0, spacing, 0, ..., 0) 00532 00533 Coordinate a = f(x - 2.0 * h); 00534 Coordinate b = f(x - h); 00535 Coordinate c = f(x + h); 00536 Coordinate d = f(x + 2.0 * h); 00537 00538 Coordinate derivatives_in_col = (a - 8.0 * b + 8.0 * c - d) / (12.0 * spacing); 00539 00540 for (unsigned row = 0; row < m; ++row) 00541 { 00542 jacobian(row, col) = derivatives_in_col[row]; 00543 } 00544 } 00545 00546 return jacobian; 00547 } 00548 00549 00634 template <class ValueType, class Function> 00635 Gradient<Function, ValueType> makeGradient(Function & function, Options<ValueType> options = Options<ValueType>()) 00636 { 00637 return Gradient<Function, ValueType>(function, options); 00638 } 00639 00640 00714 template <class Function, class ValueType> 00715 Result<ValueType> solve(Function function, const Coordinate<ValueType> & start_value, Options<ValueType> options = Options<ValueType>()) 00716 { 00717 switch(options.algorithm) 00718 { 00719 case Options<ValueType>::LEVENBERG_MARQUARDT: 00720 return solveLevenbergMarquardt(function, start_value, options); 00721 00722 case Options<ValueType>::NEWTON_RAPHSON: 00723 return solveLevenbergMarquardt(function, start_value, options); 00724 00725 default: 00726 throw std::invalid_argument("TRTK::Optimization::solve(): Unknown solver algorithm."); 00727 } 00728 } 00729 00730 00769 template<class UnaryFunction, class ValueType> 00770 ValueType solve1D(UnaryFunction f, ValueType start_value, unsigned max_number_iterations = 25) 00771 { 00772 const ValueType epsilon = 0.000001; 00773 00774 ValueType x = start_value; 00775 00776 for (unsigned i = 0; i < max_number_iterations; ++i) 00777 { 00778 ValueType a = f(x - 2 * epsilon); 00779 ValueType b = f(x - epsilon); 00780 ValueType c = f(x + epsilon); 00781 ValueType d = f(x + 2 * epsilon); 00782 00783 ValueType f_prime = (a - 8 * b + 8 * c - d) / (12 * epsilon); // five-point stencil method 00784 00785 if (f_prime == 0) break; // Avoid a division by zero. 00786 00787 x = x - f(x) / f_prime; 00788 } 00789 00790 return x; 00791 } 00792 00793 00896 template <class Function, class ValueType> 00897 Result<ValueType> solveLevenbergMarquardt(Function function, const Coordinate<ValueType> & start_value, Options<ValueType> options = Options<ValueType>()) 00898 { 00899 // The algorithm is implemented as described in the original article 00900 // of Marquardt. However, instead of scaling the matrix A and the vector 00901 // g we add lambda * diag(A) to A rather than lambda * I since this is 00902 // equivalent but more efficient. 00903 00904 using std::numeric_limits; 00905 using std::sqrt; 00906 00907 using namespace Eigen; 00908 00909 typedef TRTK::Coordinate<ValueType> Coordinate; 00910 typedef Eigen::Matrix<ValueType, Dynamic, 1> Vector; 00911 typedef Eigen::Matrix<ValueType, Dynamic, Dynamic> Matrix; 00912 00913 unsigned iteration = 0; 00914 const unsigned m = function(start_value).size(); // function : R^n --> R^m 00915 ValueType error = 0; 00916 ValueType lambda = options.lambda; // damping parameter 00917 ValueType nu = options.nu; // damping parameter 00918 ValueType Phi = numeric_limits<ValueType>::max(); 00919 Coordinate x = start_value; 00920 Matrix jacobian; 00921 00922 assert(lambda > 0); 00923 assert(nu > 1); 00924 00925 for (iteration = 0; iteration < options.max_number_iterations; ++iteration) 00926 { 00927 // Compute Phi(lambda) and Phi(lambda/nu). 00928 00929 // Now, compute (A + lambda * diag(A)) * delta = g where A = Jacobian(f)^T * 00930 // Jacobian(f) and g = Jacobian(f)^T * f. Split the computation due to 00931 // stability reasons; this also leads to a speed up since no inverse must 00932 // be computed. 00933 00934 Vector x1 = x.toArray(); // --> Phi(lambda / nu) 00935 Vector x2 = x.toArray(); // --> Phi(lambda) 00936 Vector f = function(x).toArray(); 00937 00938 jacobian = Optimization::jacobian(function, x, options); 00939 00940 if (jacobian.norm() == 0) 00941 { 00942 error = function(x).norm() / sqrt(ValueType(m)); // RMSE 00943 break; 00944 } 00945 00946 Matrix jj = jacobian.transpose() * jacobian; 00947 Matrix diagonal = Matrix(jj.diagonal().asDiagonal()); 00948 Matrix left_term1 = jj + lambda / nu * diagonal; 00949 Matrix left_term2 = jj + lambda * diagonal; 00950 Vector right_term = jacobian.transpose() * -f; 00951 00952 // Same as "x1 = x1 - (jj + lambda / nu * diagonal).inverse() * jacobian.transpose() * f;" 00953 // but faster and more stable: 00954 00955 Vector delta = left_term1.fullPivLu().solve(right_term); 00956 x1 = delta + x1; 00957 00958 delta = left_term2.fullPivLu().solve(right_term); 00959 x2 = delta + x2; 00960 00961 ValueType Phi1 = function(Coordinate(x1)).squaredNorm(); 00962 ValueType Phi2 = function(Coordinate(x2)).squaredNorm(); 00963 00964 // Case-by-case analysis 00965 00966 if (Phi1 <= Phi) 00967 { 00968 // case i. in the original article 00969 00970 lambda = lambda / nu; 00971 Phi = Phi1; 00972 x = Coordinate(x1); 00973 } 00974 else 00975 { 00976 if (Phi2 <= Phi) 00977 { 00978 // case ii. in the original article 00979 00980 lambda = lambda; 00981 Phi = Phi2; 00982 x = Coordinate(x2); 00983 } 00984 else 00985 { 00986 // case iii. in the original article 00987 00988 Vector x3 = x.toArray(); 00989 00990 ValueType lambda3 = lambda; 00991 ValueType Phi3 = Phi; 00992 00993 unsigned w = 0; 00994 const unsigned MAX_POWER = 100; // this bound is arbitrarily chosen... 00995 00996 do 00997 { 00998 lambda3 *= nu; // increase lambda by successive multiplication 00999 01000 Matrix left_term = jj + lambda3 * diagonal; 01001 x3 += left_term.fullPivLu().solve(right_term); 01002 01003 Phi3 = function(Coordinate(x3)).squaredNorm(); 01004 } 01005 while (!(Phi3 <= Phi || w++ >= MAX_POWER)); 01006 01007 if (w > MAX_POWER) 01008 { 01009 // increasing lambda does not seem to work, hence do nothing 01010 // and keep the old value (Phi3 might now be even worse...) 01011 } 01012 else 01013 { 01014 lambda = lambda3; 01015 Phi = Phi3; 01016 x = Coordinate(x3); 01017 } 01018 } 01019 } 01020 01021 // Converged or small enough RMSE? 01022 01023 Coordinate return_value = function(x); 01024 01025 // Check whether the error (RMSE) is less than the given error 01026 // tolerance. Should the situation arise, stop the iteration. 01027 01028 error = return_value.norm() / sqrt(ValueType(m)); 01029 01030 if (error < options.error_tolerance) break; 01031 } 01032 01033 Result<ValueType> result; 01034 01035 result.error = error; 01036 result.jacobian = jacobian; 01037 result.number_of_iterations = iteration + 1; 01038 result.root = x; 01039 01040 return result; 01041 } 01042 01043 01120 template <class Function, class ValueType> 01121 Result<ValueType> solveNewtonRaphsonMethod(Function function, const Coordinate<ValueType> & start_value, Options<ValueType> options = Options<ValueType>()) 01122 { 01123 using namespace Eigen; 01124 01125 using std::sqrt; 01126 01127 typedef TRTK::Coordinate<ValueType> Coordinate; 01128 01129 // function : R^n --> R^m 01130 01131 // const unsigned n = start_value.size(); 01132 const unsigned m = function(start_value).size(); 01133 01134 Coordinate x = start_value; 01135 01136 Matrix<ValueType, Dynamic, Dynamic> jacobian; 01137 01138 ValueType error = 0; 01139 01140 unsigned iteration = 0; 01141 01142 for (iteration = 0; iteration < options.max_number_iterations; ++iteration) 01143 { 01144 Coordinate return_value = function(x); 01145 01146 // Check whether the error (RMS) is less than the given error 01147 // tolerance. Should the situation arise, stop the iteration. 01148 01149 error = return_value.norm() / sqrt(ValueType(m)); 01150 01151 if (error < options.error_tolerance) break; 01152 01153 jacobian = Optimization::jacobian(function, x, options); 01154 01155 if (jacobian.norm() == 0) break; 01156 01157 Matrix<ValueType, Dynamic, 1> x_ = x.toArray(); 01158 Matrix<ValueType, Dynamic, 1> f_ = function(x).toArray(); 01159 01160 // Same as "x_ = x_ - Jacobian.inverse() * f_;" but faster and more stable: 01161 01162 Matrix<ValueType, Dynamic, 1> delta = jacobian.fullPivLu().solve(-f_); 01163 x_ = delta + x_; 01164 01165 x = Coordinate(x_); 01166 } 01167 01168 Result<ValueType> result; 01169 01170 result.error = error; 01171 result.jacobian = jacobian; 01172 result.number_of_iterations = iteration; 01173 result.root = x; 01174 01175 return result; 01176 } 01177 01178 01179 } // namespace Optimization 01180 01181 01182 } // namespace TRTK 01183 01184 01185 #endif // OPTIMIZATION_HPP_4562354833
Documentation generated by Doxygen