21 #include <ObjexxFCL/Fmath.hh>
22 #include <basic/Tracer.hh>
31 #include <basic/options/option.hh>
32 #include <basic/options/keys/optimization.OptionKeys.gen.hh>
34 #include <utility/vector1.hh>
40 }
using namespace ObjexxFCL;
47 static basic::Tracer
TR(
"core.optimization");
50 namespace optimization {
53 LineMinimizationAlgorithm::~LineMinimizationAlgorithm() {}
56 func_1d::dump(
Real displacement ) {
57 for(
uint i = 1 ; i <= _starting_point.size() ; ++i ) {
58 _eval_point[i] = _starting_point[i] + ( displacement * _search_direction[i] );
60 return _func.dump( _starting_point, _eval_point );
63 static basic::Tracer
TR(
"core.optimization.LineMinimizer" );
69 BrentLineMinimization::operator()(
74 int const problem_size( current_position.size() );
78 Real AX,
XX, BX, FA, FX, FB;
82 for (
int i = 1; i <= problem_size; ++i ) {
83 if ( std::abs(search_direction[i]) > std::abs(derivmax) ) {
84 derivmax = search_direction[i];
88 if ( std::abs(derivmax) <= .0001 ) {
89 Real final_value =_func(current_position);
94 func_1d this_line_func( current_position, search_direction, _func );
101 MNBRAK(AX,XX,BX,FA,FX,FB,this_line_func);
103 Real final_value = BRENT(AX,XX,BX,FA,FX,FB,_tolerance,this_line_func);
105 for (
int j = 1; j <= problem_size; ++j ) {
106 search_direction[j] *= _last_accepted_step;
107 current_position[j] += search_direction[j];
118 BrentLineMinimization::MNBRAK(
128 Real DUM,
R, Q, U, ULIM, FU;
129 Real const GOLD = { 1.618034 };
130 Real const GLIMIT = { 100.0 };
131 Real const TINY = { 1.E-20 };
143 CX = BX+GOLD*(BX-AX);
150 U = BX-((BX-CX)*Q-(BX-AX)*
R)/(2.*sign(std::max(std::abs(Q-R),TINY),Q-R));
151 ULIM = BX+GLIMIT*(CX-BX);
152 if ( (BX-U)*(U-CX) > 0.0 ) {
160 }
else if ( FU > FB ) {
168 }
else if ( (CX-U)*(U-ULIM) > 0. ) {
178 }
else if ( (U-ULIM)*(ULIM-CX) >= 0. ) {
198 BrentLineMinimization::BRENT(
209 Real const brent_abs_tolerance( _abs_tolerance );
214 Real V,W,
X,FX,FV,FW,XM,
R,Q,
P,ETEMP,
D,U,FU;
216 int const ITMAX = { 100 };
217 Real const CGOLD = { 0.3819660 };
218 Real const ZEPS = { 1.0E-10 };
247 for (
int iter = 1; iter <= ITMAX; ++iter ) {
249 TOL1 = TOL*std::abs(X)+ZEPS;
256 if ( ( std::abs(FB-FX) <= brent_abs_tolerance ) &&
257 ( std::abs(FA-FX) <= brent_abs_tolerance ) )
break;
262 if ( std::abs(X-XM) <= (TOL2-.5*(B-A)) )
break;
264 if ( std::abs(E) > TOL1 ) {
269 if ( Q > 0.0 ) P = -
P;
273 if ( std::abs(P) >= std::abs(.5*Q*ETEMP) ||
274 P <= Q*(A-X) || P >= Q*(B-X) )
goto L1;
277 if ( U-A < TOL2 || B-U < TOL2 ) D = sign(TOL1,XM-X);
288 if ( std::abs(D) >= TOL1 ) {
320 if ( FU <= FW || W == X ) {
325 }
else if ( FU <= FV || V == X || V == W ) {
331 if ( iter >= ITMAX ) {
332 TR.Error <<
"BRENT exceed maximum iterations. " << iter << std::endl;
333 std::exit( EXIT_FAILURE );
338 _last_accepted_step =
X;
347 ArmijoLineMinimization::operator()(
353 Real const FACTOR( 0.5 );
354 int const problem_size( current_position.size() );
356 Real max_step_limit( 1.0 );
358 _num_linemin_calls++;
361 func_1d this_line_func( current_position, search_direction, _func );
366 for (
int i = 1 ; i <= problem_size; ++i ) {
367 if ( std::abs( search_direction[ i ] ) >
368 std::abs( derivmax ) ) derivmax = search_direction[ i ];
372 if ( std::abs(derivmax) < .0001 ) {
373 Real final_value = this_line_func( 0.0 );
378 Real init_step( _last_accepted_step / FACTOR );
379 if ( init_step > max_step_limit ) init_step = max_step_limit;
381 Real final_value = Armijo( init_step, this_line_func );
383 for (
int j = 1; j <= problem_size; ++j ) {
384 search_direction[j] *= _last_accepted_step;
385 current_position[j] += search_direction[j];
397 ArmijoLineMinimization::Armijo(
410 Real const FACTOR( 0.5 );
411 Real const SIGMA( 0.1 );
412 Real const SIGMA2( 0.8 );
413 static Real const MINSTEP( basic::options::option[ basic::options::OptionKeys::optimization::armijo_min_stepsize ]() );
417 Real func_value = func_eval( init_step );
420 _last_accepted_step = init_step;
422 if (func_value < _func_to_beat+init_step*SIGMA2*_deriv_sum ) {
423 Real test_step = init_step/FACTOR;
424 Real test_func_value = func_eval( test_step );
426 if (test_func_value < func_value) {
427 _last_accepted_step = test_step;
428 return test_func_value;
433 Real far_step = init_step;
434 while (func_value > _func_to_beat + init_step*SIGMA*_deriv_sum) {
436 if ( ( init_step <= 1e-5 * far_step ) ||
437 (init_step < MINSTEP && func_value >= _func_to_beat)) {
438 Real test_step = ( func_value - _func_to_beat ) / init_step;
440 TR.Error <<
"Inaccurate G! step= " << ( init_step ) <<
" Deriv= " <<
441 ( _deriv_sum ) <<
" Finite Diff= " << ( test_step ) << std::endl;
443 func_eval.
dump( init_step );
444 _last_accepted_step = 0.0;
445 return _func_to_beat;
448 init_step *= FACTOR*FACTOR;
451 func_value = func_eval( init_step );
455 _last_accepted_step = init_step;
457 if ( init_step < 0.0 ) {
458 TR <<
"Forced to do parabolic fit!" << std::endl;
460 Real test_step = -_deriv_sum*init_step*init_step/
461 (2*(func_value - _func_to_beat - init_step * _deriv_sum));
462 if (test_step > 1e-3*far_step && test_step < far_step) {
463 Real test_func_value = func_eval( test_step );
465 if ( test_func_value < func_value ) {
466 _last_accepted_step = test_step;
467 func_value = test_func_value;
478 StrongWolfeLineMinimization::operator()(
484 int const problem_size( current_position.size() );
487 _num_linemin_calls++;
490 func_1d this_line_func( current_position, search_direction, _func );
495 for (
int i = 1 ; i <= problem_size; ++i ) {
496 if ( std::abs( search_direction[ i ] ) >
497 std::abs( derivmax ) ) derivmax = search_direction[ i ];
501 if ( std::abs(derivmax) < .0001 ) {
502 Real final_value = this_line_func( 0.0 );
508 Real init_step( std::min( 2.0*_last_accepted_step, 1.0 ) );
510 Real final_value = StrongWolfe( init_step, this_line_func );
512 for (
int j = 1; j <= problem_size; ++j ) {
513 search_direction[j] *= _last_accepted_step;
514 current_position[j] += search_direction[j];
527 StrongWolfeLineMinimization::StrongWolfe(
534 Real const param_c1( 1.0e-1 );
535 Real const param_c2( 0.8 );
537 Real func_value0( _func_to_beat );
538 Real func_value1( 0.0 );
539 Real func_value_prev( _func_to_beat );
540 Real func_value_return( 0.0 );
546 Real deriv0( _deriv_sum );
548 Real deriv_prev( deriv0 );
551 Real alpha_max( 2.0 );
552 Real alpha_prev( alpha0 );
553 Real alpha1( init_step );
554 Size iterations( 1 );
557 func_value1 = func_eval( alpha1 );
559 if( ( func_value1 > ( func_value0 + ( param_c1 * alpha1 * deriv0 ) ) ) ||
560 ( ( iterations > 1 ) && func_value1 >= func_value_prev ) ) {
562 _last_accepted_step = zoom( alpha_prev, func_value_prev, deriv_prev, alpha1, func_value1, deriv1,
563 func_value0, deriv0, func_value_return, func_eval );
564 store_current_derivatives( func_eval.
_dE_dvars );
565 return func_value_return;
568 deriv1 = func_eval.
dfunc( alpha1 );
570 if ( std::abs( deriv1 ) <= -1.0 * param_c2 * deriv0 ) {
573 _last_accepted_step = alpha1;
574 store_current_derivatives( func_eval.
_dE_dvars );
578 if ( deriv1 >= 0.0 ) {
584 _last_accepted_step = zoom( alpha1, func_value1, deriv1, alpha_prev, func_value_prev, deriv_prev,
585 func_value0, deriv0, func_value_return, func_eval );
586 store_current_derivatives( func_eval.
_dE_dvars );
587 return func_value_return;
592 func_value_prev = func_value1;
594 alpha1 = 0.5*( alpha1 + alpha_max );
599 StrongWolfeLineMinimization::zoom(
612 Real const param_c1( 1.0e-1 );
613 Real const param_c2( 0.8 );
615 Real alpha_test( 0.0 );
616 Real func_test( 0.0 );
617 Real deriv_test( 0.0 );
619 int static step_count( 0 );
620 Size iterations( 0 );
621 Size const max_iterations_to_try( 8 );
622 Real const min_interval_threshold( 1.0e-5 );
623 Real const min_step_size( 1.0e-5 );
624 Real const scale_factor( 0.66 );
630 alpha_test = quadratic_interpolation( alpha_low, func_low, deriv_low, alpha_high, func_high );
642 func_test = func_eval( alpha_test );
643 deriv_test = func_eval.
dfunc( alpha_test );
645 if( iterations > max_iterations_to_try ) {
652 func_return = func_test;
656 if( std::abs( alpha_low - alpha_high ) <= min_interval_threshold ) {
663 func_return = func_test;
667 if( alpha_test <= min_step_size ) {
674 func_return = func_test;
680 if( ( func_test > func_zero + param_c1 * alpha_test * deriv_zero ) ||
681 ( func_test >= func_low ) ) {
689 alpha_high = alpha_test;
690 func_high = func_test;
691 deriv_high = deriv_test;
693 Real cubic_interp = cubic_interpolation( alpha_low, func_low, deriv_low, alpha_test, func_test, deriv_test );
694 Real quadratic_interp = quadratic_interpolation( alpha_low, func_low, deriv_low, alpha_test, func_test );
696 if( std::abs( cubic_interp - alpha_low ) < std::abs( quadratic_interp - alpha_low ) ) {
697 alpha_test = cubic_interp;
699 alpha_test = 0.5 * ( cubic_interp + quadratic_interp );
703 if( std::abs( deriv_test ) <= -1.0 * param_c2 * deriv_zero ) {
706 func_return = func_test;
710 Real save_alpha_test = alpha_test;
711 Real save_func_test = func_test;
712 Real save_deriv_test = deriv_test;
717 if( deriv_test*deriv_low < 0.0 ) {
728 Real cubic_interp = cubic_interpolation( alpha_low, func_low, deriv_low, alpha_test, func_test, deriv_test );
729 Real quadratic_interp = secant_interpolation( alpha_low, deriv_low, alpha_test, deriv_test );
736 if( std::abs( cubic_interp - alpha_test ) >= std::abs( quadratic_interp - alpha_test ) ) {
738 alpha_test = cubic_interp;
741 alpha_test = quadratic_interp;
744 }
else if ( std::abs( deriv_test ) <= std::abs( deriv_low ) ) {
757 Real try_alpha_test = secant_interpolation( alpha_low, deriv_low, alpha_test, deriv_test );
760 Real scaled_alpha_test = alpha_test + scale_factor*( alpha_high - alpha_test );
763 if( alpha_test > alpha_low ) {
765 alpha_test = std::min( scaled_alpha_test, try_alpha_test );
768 alpha_test = std::max( scaled_alpha_test, try_alpha_test );
776 if( deriv_high == 0.0 ) {
777 alpha_test = quadratic_interpolation( alpha_test, func_test, deriv_test, alpha_high, func_high );
779 alpha_test = cubic_interpolation( alpha_test, func_test, deriv_test, alpha_high, func_high, deriv_high );
786 if( ( deriv_test * ( alpha_test - alpha_low ) ) >= 0.0 ) {
788 alpha_high = alpha_low;
789 func_high = func_low;
790 deriv_high = deriv_low;
793 alpha_low = save_alpha_test;
794 func_low = save_func_test;
795 deriv_low = save_deriv_test;
799 if( alpha_high > alpha_low ) {
800 alpha_test = std::min( alpha_low + scale_factor*( alpha_high - alpha_low ), alpha_test );
802 alpha_test = std::max( alpha_low + scale_factor*( alpha_high - alpha_low ), alpha_test );
815 LineMinimizationAlgorithm::store_current_derivatives(
820 for(
uint i = 1 ; i <= _stored_derivatives.size() ; ++i ) {
821 _stored_derivatives[i] = curr_derivs[i];
827 LineMinimizationAlgorithm::fetch_stored_derivatives(
832 for(
uint i = 1 ; i <= _stored_derivatives.size() ; ++i ) {
833 set_derivs[i] = _stored_derivatives[i];
840 LineMinimizationAlgorithm::quadratic_interpolation(
848 return ( 2.0*alpha_low*( func_high - func_low ) -
849 deriv_low * ( alpha_high*alpha_high - alpha_low*alpha_low ) ) /
850 ( 2.0 * ( (func_high - func_low ) - ( deriv_low * ( alpha_high - alpha_low ) ) ) );
854 LineMinimizationAlgorithm::quadratic_deriv_interpolation(
866 return ( alpha_low + ( (deriv_low/((func_low - func_high)/(alpha_high - alpha_low) + deriv_low))*0.5) *(alpha_high -alpha_low) );
870 LineMinimizationAlgorithm::secant_interpolation(
879 return ( alpha_low + (deriv_low/(deriv_low - deriv_high))*(alpha_high -alpha_low) );
883 LineMinimizationAlgorithm::cubic_interpolation(
892 Real cubic_beta1 = deriv_low + deriv_high - 3.0*( func_low - func_high )/(alpha_low - alpha_high);
893 Real max_beta_derivs = std::max( std::max( std::abs( cubic_beta1), std::abs( deriv_low ) ), std::abs( deriv_high ) );
894 Real gamma = max_beta_derivs * std::sqrt( std::pow( cubic_beta1 / max_beta_derivs, 2.0 ) - (deriv_low/max_beta_derivs)*(deriv_high/max_beta_derivs));
895 if( alpha_high < alpha_low ) gamma = -gamma;
896 Real numer = (gamma - deriv_low) + cubic_beta1;
897 Real denom = ((gamma - deriv_low) + gamma) + deriv_high;
898 Real fraction = numer/denom;
902 return ( alpha_low + fraction * ( alpha_high - alpha_low ) );