/*========================================================================= * * 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 "itkFRPROptimizer.h" namespace itk { const double FRPR_TINY = 1e-20; FRPROptimizer::FRPROptimizer() { m_UseUnitLengthGradient = false; m_OptimizationType = OptimizationEnum::PolakRibiere; } FRPROptimizer::~FRPROptimizer() = default; void FRPROptimizer::GetValueAndDerivative(ParametersType & p, double * val, ParametersType * xi) { this->m_CostFunction->GetValueAndDerivative(p, *val, *xi); if (this->GetMaximize()) { (*val) = -(*val); for (unsigned int i = 0; i < this->GetSpaceDimension(); ++i) { (*xi)[i] = -(*xi)[i]; } } if (this->GetUseUnitLengthGradient()) { double len = (*xi)[0] * (*xi)[0]; for (unsigned int i = 1; i < this->GetSpaceDimension(); ++i) { len += (*xi)[i] * (*xi)[i]; } len = std::sqrt(len / this->GetSpaceDimension()); for (unsigned int i = 0; i < this->GetSpaceDimension(); ++i) { (*xi)[i] /= len; } } } void FRPROptimizer::LineOptimize(ParametersType * p, ParametersType & xi, double * val) { ParametersType tempCoord(this->GetSpaceDimension()); this->LineOptimize(p, xi, val, tempCoord); } void FRPROptimizer::LineOptimize(ParametersType * p, ParametersType & xi, double * val, ParametersType & tempCoord) { this->SetLine(*p, xi); double ax = 0.0; double fa = (*val); double xx = this->GetStepLength(); double fx; double bx; double fb; this->LineBracket(&ax, &xx, &bx, &fa, &fx, &fb, tempCoord); this->SetCurrentLinePoint(xx, fx); double extX = 0; double extVal = 0; this->BracketedLineOptimize(ax, xx, bx, fa, fx, fb, &extX, &extVal, tempCoord); this->SetCurrentLinePoint(extX, extVal); (*p) = this->GetCurrentPosition(); (*val) = extVal; } void FRPROptimizer::StartOptimization() { unsigned int i; if (m_CostFunction.IsNull()) { return; } this->InvokeEvent(StartEvent()); this->SetStop(false); this->SetSpaceDimension(m_CostFunction->GetNumberOfParameters()); FRPROptimizer::ParametersType tempCoord(this->GetSpaceDimension()); double gg, gam, dgg; FRPROptimizer::ParametersType g(this->GetSpaceDimension()); FRPROptimizer::ParametersType h(this->GetSpaceDimension()); FRPROptimizer::ParametersType xi(this->GetSpaceDimension()); FRPROptimizer::ParametersType p(this->GetSpaceDimension()); p = this->GetInitialPosition(); this->SetCurrentPosition(p); double fp; this->GetValueAndDerivative(p, &fp, &xi); for (i = 0; i < this->GetSpaceDimension(); ++i) { g[i] = -xi[i]; xi[i] = g[i]; h[i] = g[i]; } unsigned int limitCount = 0; for (unsigned int currentIteration = 0; currentIteration <= this->GetMaximumIteration(); ++currentIteration) { this->SetCurrentIteration(currentIteration); double fret; fret = fp; this->LineOptimize(&p, xi, &fret, tempCoord); if (2.0 * itk::Math::abs(fret - fp) <= this->GetValueTolerance() * (itk::Math::abs(fret) + itk::Math::abs(fp) + FRPR_TINY)) { if (limitCount < this->GetSpaceDimension()) { this->GetValueAndDerivative(p, &fp, &xi); xi[limitCount] = 1; ++limitCount; } else { this->SetCurrentPosition(p); this->InvokeEvent(EndEvent()); return; } } else { limitCount = 0; this->GetValueAndDerivative(p, &fp, &xi); } gg = 0.0; dgg = 0.0; if (m_OptimizationType == OptimizationEnum::PolakRibiere) { for (i = 0; i < this->GetSpaceDimension(); ++i) { gg += g[i] * g[i]; dgg += (xi[i] + g[i]) * xi[i]; } } if (m_OptimizationType == OptimizationEnum::FletchReeves) { for (i = 0; i < this->GetSpaceDimension(); ++i) { gg += g[i] * g[i]; dgg += xi[i] * xi[i]; } } if (gg == 0.0) { this->SetCurrentPosition(p); this->InvokeEvent(EndEvent()); return; } gam = dgg / gg; for (i = 0; i < this->GetSpaceDimension(); ++i) { g[i] = -xi[i]; xi[i] = g[i] + gam * h[i]; h[i] = xi[i]; } this->SetCurrentPosition(p); this->InvokeEvent(IterationEvent()); } this->InvokeEvent(EndEvent()); } void FRPROptimizer::SetToPolakRibiere() { m_OptimizationType = OptimizationEnum::PolakRibiere; } void FRPROptimizer::SetToFletchReeves() { m_OptimizationType = OptimizationEnum::FletchReeves; } void FRPROptimizer::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "OptimizationType: " << m_OptimizationType << std::endl; os << indent << "UseUnitLengthGradient: " << (m_UseUnitLengthGradient ? "On" : "Off") << std::endl; } std::ostream & operator<<(std::ostream & out, const FRPROptimizerEnums::Optimization value) { return out << [value] { switch (value) { case FRPROptimizerEnums::Optimization::FletchReeves: return "itk::FRPROptimizerEnums::Optimization::FletchReeves"; case FRPROptimizerEnums::Optimization::PolakRibiere: return "itk::FRPROptimizerEnums::Optimization::PolakRibiere"; default: return "INVALID VALUE FOR itk::FRPROptimizerEnums::Optimization"; } }(); } } // end of namespace itk