/*========================================================================= * * Copyright NumFOCUS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "itkRegularStepGradientDescentBaseOptimizer.h" namespace itk { /** * Constructor */ RegularStepGradientDescentBaseOptimizer::RegularStepGradientDescentBaseOptimizer() { m_MaximumStepLength = 1.0; m_MinimumStepLength = 1e-3; m_GradientMagnitudeTolerance = 1e-4; m_NumberOfIterations = 100; m_CurrentIteration = 0; m_Value = 0; m_Maximize = false; m_CostFunction = nullptr; m_CurrentStepLength = 0; m_StopCondition = StopConditionEnum::Unknown; m_Gradient.Fill(0.0f); m_PreviousGradient.Fill(0.0f); m_RelaxationFactor = 0.5; m_StopConditionDescription.str(""); } /** * Start the optimization */ void RegularStepGradientDescentBaseOptimizer::StartOptimization() { itkDebugMacro("StartOptimization"); m_CurrentStepLength = m_MaximumStepLength; m_CurrentIteration = 0; m_StopCondition = StopConditionEnum::Unknown; m_StopConditionDescription.str(""); m_StopConditionDescription << this->GetNameOfClass() << ": "; // validity check for the value of GradientMagnitudeTolerance if (m_GradientMagnitudeTolerance < 0.0) { itkExceptionMacro("Gradient magnitude tolerance must be" "greater or equal 0.0. Current value is " << m_GradientMagnitudeTolerance); } const unsigned int spaceDimension = m_CostFunction->GetNumberOfParameters(); m_Gradient = DerivativeType(spaceDimension); m_PreviousGradient = DerivativeType(spaceDimension); m_Gradient.Fill(0.0f); m_PreviousGradient.Fill(0.0f); this->SetCurrentPosition(GetInitialPosition()); this->ResumeOptimization(); } /** * Resume the optimization */ void RegularStepGradientDescentBaseOptimizer::ResumeOptimization() { itkDebugMacro("ResumeOptimization"); m_Stop = false; this->InvokeEvent(StartEvent()); while (!m_Stop) { if (m_CurrentIteration >= m_NumberOfIterations) { m_StopCondition = StopConditionEnum::MaximumNumberOfIterations; m_StopConditionDescription << "Maximum number of iterations (" << m_NumberOfIterations << ") exceeded."; this->StopOptimization(); break; } m_PreviousGradient = m_Gradient; try { m_CostFunction->GetValueAndDerivative(this->GetCurrentPosition(), m_Value, m_Gradient); } catch (const ExceptionObject & excp) { m_StopCondition = StopConditionEnum::CostFunctionError; m_StopConditionDescription << "Cost function error after " << m_CurrentIteration << " iterations. " << excp.GetDescription(); this->StopOptimization(); throw; } if (m_Stop) { break; } this->AdvanceOneStep(); ++m_CurrentIteration; } } /** * Stop optimization */ void RegularStepGradientDescentBaseOptimizer::StopOptimization() { itkDebugMacro("StopOptimization"); m_Stop = true; this->InvokeEvent(EndEvent()); } /** * Advance one Step following the gradient direction */ void RegularStepGradientDescentBaseOptimizer::AdvanceOneStep() { itkDebugMacro("AdvanceOneStep"); const unsigned int spaceDimension = m_CostFunction->GetNumberOfParameters(); DerivativeType transformedGradient(spaceDimension); DerivativeType previousTransformedGradient(spaceDimension); const ScalesType & scales = this->GetScales(); if (m_RelaxationFactor < 0.0) { itkExceptionMacro("Relaxation factor must be positive. Current value is " << m_RelaxationFactor); } if (m_RelaxationFactor >= 1.0) { itkExceptionMacro("Relaxation factor must less than 1.0. Current value is " << m_RelaxationFactor); } // Make sure the scales have been set properly if (scales.size() != spaceDimension) { itkExceptionMacro("The size of Scales is " << scales.size() << ", but the NumberOfParameters for the CostFunction is " << spaceDimension << '.'); } for (unsigned int i = 0; i < spaceDimension; ++i) { transformedGradient[i] = m_Gradient[i] / scales[i]; previousTransformedGradient[i] = m_PreviousGradient[i] / scales[i]; } double magnitudeSquare = 0; for (unsigned int dim = 0; dim < spaceDimension; ++dim) { const double weighted = transformedGradient[dim]; magnitudeSquare += weighted * weighted; } const double gradientMagnitude = std::sqrt(magnitudeSquare); if (gradientMagnitude < m_GradientMagnitudeTolerance) { m_StopCondition = StopConditionEnum::GradientMagnitudeTolerance; m_StopConditionDescription << "Gradient magnitude tolerance met after " << m_CurrentIteration << " iterations. Gradient magnitude (" << gradientMagnitude << ") is less than gradient magnitude tolerance (" << m_GradientMagnitudeTolerance << ")."; this->StopOptimization(); return; } double scalarProduct = 0; for (unsigned int i = 0; i < spaceDimension; ++i) { const double weight1 = transformedGradient[i]; const double weight2 = previousTransformedGradient[i]; scalarProduct += weight1 * weight2; } // If there is a direction change if (scalarProduct < 0) { m_CurrentStepLength *= m_RelaxationFactor; } if (m_CurrentStepLength < m_MinimumStepLength) { m_StopCondition = StopConditionEnum::StepTooSmall; m_StopConditionDescription << "Step too small after " << m_CurrentIteration << " iterations. Current step (" << m_CurrentStepLength << ") is less than minimum step (" << m_MinimumStepLength << ")."; this->StopOptimization(); return; } double direction; if (this->m_Maximize) { direction = 1.0; } else { direction = -1.0; } const double factor = direction * m_CurrentStepLength / gradientMagnitude; // This method StepAlongGradient() will // be overloaded in non-vector spaces this->StepAlongGradient(factor, transformedGradient); this->InvokeEvent(IterationEvent()); } const std::string RegularStepGradientDescentBaseOptimizer::GetStopConditionDescription() const { return m_StopConditionDescription.str(); } void RegularStepGradientDescentBaseOptimizer::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "MaximumStepLength: " << m_MaximumStepLength << std::endl; os << indent << "MinimumStepLength: " << m_MinimumStepLength << std::endl; os << indent << "RelaxationFactor: " << m_RelaxationFactor << std::endl; os << indent << "GradientMagnitudeTolerance: " << m_GradientMagnitudeTolerance << std::endl; os << indent << "NumberOfIterations: " << m_NumberOfIterations << std::endl; os << indent << "CurrentIteration: " << m_CurrentIteration << std::endl; os << indent << "Value: " << m_Value << std::endl; os << indent << "Maximize: " << m_Maximize << std::endl; if (m_CostFunction) { os << indent << "CostFunction: " << &m_CostFunction << std::endl; } else { os << indent << "CostFunction: " << "(None)" << std::endl; } os << indent << "CurrentStepLength: " << m_CurrentStepLength << std::endl; os << indent << "StopCondition: " << m_StopCondition << std::endl; os << indent << "Gradient: " << m_Gradient << std::endl; } /** Print enum values */ std::ostream & operator<<(std::ostream & out, const RegularStepGradientDescentBaseOptimizerEnums::StopCondition value) { return out << [value] { switch (value) { case RegularStepGradientDescentBaseOptimizerEnums::StopCondition::GradientMagnitudeTolerance: return "itk::RegularStepGradientDescentBaseOptimizerEnums::" "StopCondition::GradientMagnitudeTolerance"; case RegularStepGradientDescentBaseOptimizerEnums::StopCondition::StepTooSmall: return "itk::RegularStepGradientDescentBaseOptimizerEnums::" "StopCondition::StepTooSmall"; case RegularStepGradientDescentBaseOptimizerEnums::StopCondition::ImageNotAvailable: return "itk::RegularStepGradientDescentBaseOptimizerEnums::" "StopCondition::ImageNotAvailable"; case RegularStepGradientDescentBaseOptimizerEnums::StopCondition::CostFunctionError: return "itk::RegularStepGradientDescentBaseOptimizerEnums::" "StopCondition::CostFunctionError"; case RegularStepGradientDescentBaseOptimizerEnums::StopCondition::MaximumNumberOfIterations: return "itk::RegularStepGradientDescentBaseOptimizerEnums::" "StopCondition::MaximumNumberOfIterations"; case RegularStepGradientDescentBaseOptimizerEnums::StopCondition::Unknown: return "itk::RegularStepGradientDescentBaseOptimizerEnums::" "StopCondition::Unknown"; default: return "INVALID VALUE FOR " "itk::RegularStepGradientDescentBaseOptimizerEnums::" "StopCondition"; } }(); } } // end namespace itk