1 /*=========================================================================
2 Program: vv http://www.creatis.insa-lyon.fr/rio/vv
5 - University of LYON http://www.universite-lyon.fr/
6 - Léon Bérard cancer center http://www.centreleonberard.fr
7 - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr
9 This software is distributed WITHOUT ANY WARRANTY; without even
10 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11 PURPOSE. See the copyright notices for more information.
13 It is distributed under dual licence
15 - BSD See included LICENSE.txt file
16 - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
17 ===========================================================================**/
18 #ifndef __clitkLBFGSBOptimizer_cxx
19 #define __clitkLBFGSBOptimizer_cxx
20 #include "clitkLBFGSBOptimizer.h"
21 #include "vnl/algo/vnl_lbfgsb.h"
22 #include <vnl/vnl_math.h>
25 extern double v3p_netlib_dpmeps_();
32 /** \class LBFGSBOptimizerHelper
33 * \brief Wrapper helper around vnl_lbfgsb.
35 * This class is used to translate iteration events, etc, from
36 * vnl_lbfgsb into iteration events in ITK.
38 class ITK_EXPORT LBFGSBOptimizerHelper :
42 typedef LBFGSBOptimizerHelper Self;
43 typedef vnl_lbfgsb Superclass;
45 /** Create with a reference to the ITK object */
46 LBFGSBOptimizerHelper( vnl_cost_function& f,
47 LBFGSBOptimizer* itkObj );
49 /** Handle new iteration event */
50 virtual bool report_iter();
53 LBFGSBOptimizer* m_ItkObj;
65 m_OptimizerInitialized = false;
68 m_LowerBound = InternalBoundValueType(0);
69 m_UpperBound = InternalBoundValueType(0);
70 m_BoundSelection = InternalBoundSelectionType(0);
72 m_CostFunctionConvergenceFactor = 1e+7;
73 m_ProjectedGradientTolerance = 1e-5;
74 m_MaximumNumberOfIterations = 500;
75 m_MaximumNumberOfEvaluations = 500;
76 m_MaximumNumberOfCorrections = 5;
77 m_CurrentIteration = 0;
79 m_InfinityNormOfProjectedGradient = 0.0;
80 m_StopConditionDescription.str("");
91 delete m_VnlOptimizer;
99 ::PrintSelf( std::ostream& os, itk::Indent indent) const
101 Superclass::PrintSelf(os, indent);
103 os << indent << "LowerBound: " << m_LowerBound << std::endl;
104 os << indent << "UpperBound: " << m_UpperBound << std::endl;
105 os << indent << "BoundSelection: " << m_BoundSelection << std::endl;
107 os << indent << "CostFunctionConvergenceFactor: " <<
108 m_CostFunctionConvergenceFactor << std::endl;
110 os << indent << "ProjectedGradientTolerance: " <<
111 m_ProjectedGradientTolerance << std::endl;
113 os << indent << "MaximumNumberOfIterations: " <<
114 m_MaximumNumberOfIterations << std::endl;
116 os << indent << "MaximumNumberOfEvaluations: " <<
117 m_MaximumNumberOfEvaluations << std::endl;
119 os << indent << "MaximumNumberOfCorrections: " <<
120 m_MaximumNumberOfCorrections << std::endl;
122 os << indent << "CurrentIteration: " <<
123 m_CurrentIteration << std::endl;
125 os << indent << "Value: " <<
126 m_Value << std::endl;
128 os << indent << "InfinityNormOfProjectedGradient: " <<
129 m_InfinityNormOfProjectedGradient << std::endl;
131 if( this->m_VnlOptimizer )
133 os << indent << "Vnl LBFGSB Failure Code: " <<
134 this->m_VnlOptimizer->get_failure_code() << std::endl;
144 const BoundValueType& value )
146 m_LowerBound = value;
147 if( m_OptimizerInitialized )
149 m_VnlOptimizer->set_lower_bound( m_LowerBound );
173 const BoundValueType& value )
175 m_UpperBound = value;
176 if( m_OptimizerInitialized )
178 m_VnlOptimizer->set_upper_bound( m_UpperBound );
198 * Set bound selection array
203 const BoundSelectionType& value )
205 m_BoundSelection = value;
206 if( m_OptimizerInitialized )
208 m_VnlOptimizer->set_bound_selection( m_BoundSelection );
214 * Get bound selection array
218 ::BoundSelectionType &
220 ::GetBoundSelection()
222 return m_BoundSelection;
226 /** Set/Get the CostFunctionConvergenceFactor. Algorithm terminates
227 * when the reduction in cost function is less than factor * epsmcj
228 * where epsmch is the machine precision.
229 * Typical values for factor: 1e+12 for low accuracy;
230 * 1e+7 for moderate accuracy and 1e+1 for extremely high accuracy.
234 ::SetCostFunctionConvergenceFactor( double value )
238 itkExceptionMacro("Value " << value
239 << " is too small for SetCostFunctionConvergenceFactor()"
240 << "a typical range would be from 1.0 to 1e+12");
242 m_CostFunctionConvergenceFactor = value;
243 if( m_OptimizerInitialized )
245 m_VnlOptimizer->set_cost_function_convergence_factor(
246 m_CostFunctionConvergenceFactor );
252 /** Set/Get the ProjectedGradientTolerance. Algorithm terminates
253 * when the project gradient is below the tolerance. Default value
258 ::SetProjectedGradientTolerance( double value )
260 m_ProjectedGradientTolerance = value;
261 if( m_OptimizerInitialized )
263 m_VnlOptimizer->set_projected_gradient_tolerance(
264 m_ProjectedGradientTolerance );
270 /** Set/Get the MaximumNumberOfIterations. Default is 500 */
273 ::SetMaximumNumberOfIterations( unsigned int value )
275 m_MaximumNumberOfIterations = value;
280 /** Set/Get the MaximumNumberOfEvaluations. Default is 500 */
283 ::SetMaximumNumberOfEvaluations( unsigned int value )
285 m_MaximumNumberOfEvaluations = value;
286 if( m_OptimizerInitialized )
288 m_VnlOptimizer->set_max_function_evals( m_MaximumNumberOfEvaluations );
294 /** Set/Get the MaximumNumberOfCorrections. Default is 5 */
297 ::SetMaximumNumberOfCorrections( unsigned int value )
299 m_MaximumNumberOfCorrections = value;
300 if( m_OptimizerInitialized )
302 m_VnlOptimizer->set_max_variable_metric_corrections(
303 m_MaximumNumberOfCorrections );
310 * Connect a Cost Function
314 SetCostFunction( itk::SingleValuedCostFunction * costFunction )
316 m_CostFunction = costFunction;
318 const unsigned int numberOfParameters =
319 costFunction->GetNumberOfParameters();
321 CostFunctionAdaptorType * adaptor =
322 new CostFunctionAdaptorType( numberOfParameters );
324 adaptor->SetCostFunction( costFunction );
326 if( m_OptimizerInitialized )
328 delete m_VnlOptimizer;
331 this->SetCostFunctionAdaptor( adaptor );
333 m_VnlOptimizer = new InternalOptimizerType( *adaptor, this );
335 // set the optimizer parameters
336 m_VnlOptimizer->set_lower_bound( m_LowerBound );
337 m_VnlOptimizer->set_upper_bound( m_UpperBound );
338 m_VnlOptimizer->set_bound_selection( m_BoundSelection );
339 m_VnlOptimizer->set_cost_function_convergence_factor(
340 m_CostFunctionConvergenceFactor );
341 m_VnlOptimizer->set_projected_gradient_tolerance(
342 m_ProjectedGradientTolerance );
343 m_VnlOptimizer->set_max_function_evals( m_MaximumNumberOfEvaluations );
344 m_VnlOptimizer->set_max_variable_metric_corrections(
345 m_MaximumNumberOfCorrections );
347 m_OptimizerInitialized = true;
354 * Start the optimization
358 ::StartOptimization( void )
361 // Check if all the bounds parameters are the same size as the initial parameters.
362 unsigned int numberOfParameters = m_CostFunction->GetNumberOfParameters();
364 if ( this->GetInitialPosition().Size() < numberOfParameters )
366 itkExceptionMacro( << "InitialPosition array does not have sufficient number of elements" );
369 if ( m_LowerBound.size() < numberOfParameters )
371 itkExceptionMacro( << "LowerBound array does not have sufficient number of elements" );
374 if ( m_UpperBound.size() < numberOfParameters )
376 itkExceptionMacro( << "UppperBound array does not have sufficient number of elements" );
379 if ( m_BoundSelection.size() < numberOfParameters )
381 itkExceptionMacro( << "BoundSelection array does not have sufficient number of elements" );
384 if( this->GetMaximize() )
386 this->GetNonConstCostFunctionAdaptor()->NegateCostFunctionOn();
389 this->SetCurrentPosition( this->GetInitialPosition() );
391 ParametersType parameters( this->GetInitialPosition() );
393 // Clear the description
394 m_StopConditionDescription.str("");
396 // vnl optimizers return the solution by reference
397 // in the variable provided as initial position
398 m_VnlOptimizer->minimize( parameters );
400 if ( parameters.size() != this->GetInitialPosition().Size() )
402 // set current position to initial position and throw an exception
403 this->SetCurrentPosition( this->GetInitialPosition() );
404 itkExceptionMacro( << "Error occured in optimization" );
407 this->SetCurrentPosition( parameters );
409 this->InvokeEvent( itk::EndEvent() );
414 /*-------------------------------------------------------------------------
416 *-------------------------------------------------------------------------
419 /** Create with a reference to the ITK object */
420 LBFGSBOptimizerHelper
421 ::LBFGSBOptimizerHelper( vnl_cost_function& f,
422 LBFGSBOptimizer* itkObj )
429 /** Handle new iteration event */
431 LBFGSBOptimizerHelper
434 Superclass::report_iter();
436 m_ItkObj->m_InfinityNormOfProjectedGradient =
437 this->get_inf_norm_projected_gradient();
439 m_ItkObj->InvokeEvent( itk::IterationEvent() );
441 m_ItkObj->m_CurrentIteration = this->num_iterations_;
444 m_ItkObj->m_Value = m_ItkObj->GetCachedValue();
447 // Return true to terminate the optimization loop.
448 if( this->num_iterations_ > m_ItkObj->m_MaximumNumberOfIterations )
450 m_ItkObj->m_StopConditionDescription <<
451 "Too many iterations. Iterations = " <<
452 m_ItkObj->m_MaximumNumberOfIterations;
463 GetStopConditionDescription() const
467 m_StopConditionDescription.str("");
468 m_StopConditionDescription << this->GetNameOfClass() << ": ";
469 switch (m_VnlOptimizer->get_failure_code())
471 case vnl_nonlinear_minimizer::ERROR_FAILURE:
472 m_StopConditionDescription << "Failure";
474 case vnl_nonlinear_minimizer::ERROR_DODGY_INPUT:
475 m_StopConditionDescription << "Dodgy input";
477 case vnl_nonlinear_minimizer::CONVERGED_FTOL:
478 m_StopConditionDescription << "Function tolerance reached after "
479 << m_CurrentIteration
481 << "The relative reduction of the cost function <= "
482 << m_CostFunctionConvergenceFactor * v3p_netlib_dpmeps_()
483 << " = CostFunctionConvergenceFactor ("
484 << m_CostFunctionConvergenceFactor
485 << ") * machine precision ("
486 << v3p_netlib_dpmeps_()
489 case vnl_nonlinear_minimizer::CONVERGED_XTOL:
490 m_StopConditionDescription << "Solution tolerance reached";
492 case vnl_nonlinear_minimizer::CONVERGED_XFTOL:
493 m_StopConditionDescription << "Solution and Function tolerance both reached";
495 case vnl_nonlinear_minimizer::CONVERGED_GTOL:
496 m_StopConditionDescription << "Gradient tolerance reached. "
497 << "Projected gradient tolerance is "
498 << m_ProjectedGradientTolerance;
500 case vnl_nonlinear_minimizer::FAILED_TOO_MANY_ITERATIONS:
501 m_StopConditionDescription << "Too many iterations. Iterations = "
502 << m_MaximumNumberOfEvaluations;
504 case vnl_nonlinear_minimizer::FAILED_FTOL_TOO_SMALL:
505 m_StopConditionDescription << "Function tolerance too small";
507 case vnl_nonlinear_minimizer::FAILED_XTOL_TOO_SMALL:
508 m_StopConditionDescription << "Solution tolerance too small";
510 case vnl_nonlinear_minimizer::FAILED_GTOL_TOO_SMALL:
511 m_StopConditionDescription << "Gradient tolerance too small";
513 case vnl_nonlinear_minimizer::FAILED_USER_REQUEST:
516 return m_StopConditionDescription.str();
520 return std::string("");
524 } // end namespace clitk