56 #ifndef OPTIMIZATION__LINE_SEARCH__MORE_THUENTE_LINE_SEARCH_HPP_ 57 #define OPTIMIZATION__LINE_SEARCH__MORE_THUENTE_LINE_SEARCH_HPP_ 71 namespace comp = helper_functions::comparisons;
72 namespace optimization
119 const StepT max_step,
120 const StepT min_step,
122 const StepT mu = 1.e-4F,
123 const StepT eta = 0.1F,
124 const std::int32_t max_iterations = 10)
126 m_step_min{min_step},
127 m_optimization_direction{optimization_direction},
130 m_max_iterations{max_iterations}
132 if (min_step < 0.0F) {
throw std::domain_error(
"Min step cannot be negative.");}
133 if (max_step < min_step) {
throw std::domain_error(
"Max step cannot be smaller than min step.");}
134 if (mu < 0.0F || mu > 1.0F) {
throw std::domain_error(
"mu must be in (0, 1).");}
135 if (eta < 0.0F || eta > 1.0F) {
throw std::domain_error(
"eta must be in (0, 1).");}
136 if (max_iterations < 1) {
throw std::domain_error(
"Less than 1 iteration is not allowed.");}
137 m_compute_mode.set_score().set_jacobian();
157 template<
typename DomainValueT,
typename OptimizationProblemT>
158 DomainValueT compute_next_step_(
159 const DomainValueT & x0,
160 const DomainValueT & initial_step,
161 OptimizationProblemT & optimization_problem);
174 template<
typename OptimizationProblemT>
175 class ObjectiveFunction;
181 template<
typename ObjectiveFunctionT>
182 class AuxiliaryFunction;
185 template<
typename FunctionValueT>
186 StepT find_next_step_length(
187 const FunctionValueT & f_t,
const FunctionValueT & f_l,
const FunctionValueT & f_u);
191 template<
typename FunctionValueT>
192 Interval update_interval(
193 const FunctionValueT & f_t,
const FunctionValueT & f_l,
const FunctionValueT & f_u);
200 std::int32_t m_max_iterations{};
203 template<
typename DomainValueT,
typename OptimizationProblemT>
205 const DomainValueT & x0,
const DomainValueT & initial_step,
206 OptimizationProblemT & optimization_problem)
208 auto a_t = std::min(static_cast<StepT>(initial_step.norm()), get_step_max());
209 if (a_t < m_step_min) {
215 using FunctionPhi = ObjectiveFunction<OptimizationProblemT>;
217 using FunctionPsi = AuxiliaryFunction<FunctionPhi>;
218 FunctionPhi phi{x0, initial_step, optimization_problem, m_optimization_direction};
219 FunctionPsi psi{phi, m_mu};
222 Interval interval{m_step_min, get_step_max()};
223 const auto phi_0 = phi(0.0F);
224 auto phi_t = phi(a_t);
225 auto psi_t = psi(a_t);
226 auto f_l = psi(interval.a_l);
227 auto f_u = psi(interval.a_u);
229 bool use_auxiliary_function =
true;
231 for (
auto step_iterations = 0; step_iterations < m_max_iterations; ++step_iterations) {
232 constexpr decltype(psi_t.value) ZERO{};
233 if ((psi_t.value <= ZERO) &&
234 (std::abs(phi_t.derivative) <= m_eta * std::abs(phi_0.derivative)))
244 if (use_auxiliary_function) {
245 a_t = find_next_step_length(psi_t, f_l, f_u);
247 a_t = find_next_step_length(phi_t, f_l, f_u);
249 if (a_t < m_step_min || std::isnan(a_t)) {
261 if (use_auxiliary_function && (psi_t.value <= 0.0 && psi_t.derivative > 0.0)) {
262 use_auxiliary_function =
false;
264 f_l = phi(interval.a_l);
265 f_u = phi(interval.a_u);
268 if (use_auxiliary_function) {
271 interval = update_interval(psi_t, f_l, f_u);
272 f_l = psi(interval.a_l);
273 f_u = psi(interval.a_u);
277 interval = update_interval(phi_t, f_l, f_u);
278 f_l = phi(interval.a_l);
279 f_u = phi(interval.a_u);
281 constexpr
auto EPS = std::numeric_limits<StepT>::epsilon();
288 return a_t * phi.get_step_direction();
291 template<
typename OptimizationProblemT>
292 class MoreThuenteLineSearch::ObjectiveFunction
294 using ValueT =
typename OptimizationProblemT::Value;
295 using JacobianT =
typename OptimizationProblemT::Jacobian;
296 using DomainValueT =
typename OptimizationProblemT::DomainValue;
308 const DomainValueT & starting_state,
309 const DomainValueT & initial_step,
310 OptimizationProblemT & underlying_function,
312 : m_starting_state{starting_state},
313 m_step_direction{initial_step.normalized()},
314 m_underlying_function{underlying_function}
316 m_compute_mode.set_score().set_jacobian();
317 m_underlying_function.evaluate(m_starting_state, m_compute_mode);
318 m_underlying_function.jacobian(m_starting_state, m_underlying_function_jacobian);
319 const auto derivative = m_underlying_function_jacobian.dot(m_step_direction);
321 case OptimizationDirection::kMinimization:
322 if (derivative > ValueT{0.0}) {
323 m_step_direction *= -1.0;
326 case OptimizationDirection::kMaximization:
327 if (derivative < ValueT{0.0}) {
328 m_step_direction *= -1.0;
333 m_multiplier = ValueT{-1.0};
341 if (step_size < StepT{0.0}) {
throw std::runtime_error(
"Step cannot be negative");}
342 const auto current_state = m_starting_state + step_size * m_step_direction;
343 m_underlying_function.evaluate(current_state, m_compute_mode);
344 m_underlying_function.jacobian(current_state, m_underlying_function_jacobian);
347 m_multiplier * m_underlying_function(current_state),
348 m_multiplier * m_underlying_function_jacobian.dot(m_step_direction)};
352 const DomainValueT & get_step_direction()
const noexcept {
return m_step_direction;}
355 DomainValueT m_starting_state;
356 DomainValueT m_step_direction;
357 OptimizationProblemT & m_underlying_function;
359 JacobianT m_underlying_function_jacobian;
360 ValueT m_multiplier{1.0};
364 template<
typename ObjectiveFunctionT>
365 class MoreThuenteLineSearch::AuxiliaryFunction
367 using FunctionValue =
typename ObjectiveFunctionT::FunctionValue;
371 AuxiliaryFunction(ObjectiveFunctionT & objective_function,
const StepT & mu)
372 : m_objective_function{objective_function},
374 m_initial_objective_function_value{objective_function(0.0F)} {}
379 const auto & objective_function_value = m_objective_function(step_size);
381 objective_function_value.value -
382 m_initial_objective_function_value.value -
383 m_mu * step_size * objective_function_value.derivative;
384 const auto derivative =
385 objective_function_value.derivative - m_mu * m_initial_objective_function_value.derivative;
386 return {step_size, value, derivative};
390 ObjectiveFunctionT & m_objective_function;
396 template<
typename FunctionValueT>
398 const FunctionValueT & f_t,
const FunctionValueT & f_l,
const FunctionValueT & f_u)
400 if (std::isnan(f_t.argument) || std::isnan(f_l.argument) || std::isnan(f_u.argument)) {
401 throw std::runtime_error(
"Got nan values in the step computation function.");
403 constexpr
auto kValueEps = 0.00001;
404 constexpr
auto kStepEps = 0.00001F;
407 const auto find_cubic_minimizer = [kStepEps](
const auto & f_a,
const auto & f_b) -> StepT {
411 const auto z = 3.0F * (f_a.value - f_b.value) /
412 (f_b.argument - f_a.argument) + f_a.derivative + f_b.derivative;
413 const auto w = std::sqrt(z * z - f_a.derivative * f_b.derivative);
415 return f_b.argument - (f_b.argument - f_a.argument) * (f_b.derivative +
w - z) /
416 (f_b.derivative - f_a.derivative + 2.0F *
w);
420 const auto find_a_q = [kStepEps](
421 const FunctionValueT & f_a,
const FunctionValueT & f_b) -> StepT {
425 return f_a.argument + 0.5F *
426 (f_b.argument - f_a.argument) * (f_b.argument - f_a.argument) * f_a.derivative /
427 (f_a.value - f_b.value + (f_b.argument - f_a.argument) * f_a.derivative);
431 const auto find_a_s = [kStepEps](
432 const FunctionValueT & f_a,
const FunctionValueT & f_b) -> StepT {
436 return f_a.argument +
437 (f_b.argument - f_a.argument) * f_a.derivative / (f_a.derivative - f_b.derivative);
441 if (f_t.value > f_l.value) {
442 const auto a_c = find_cubic_minimizer(f_l, f_t);
443 const auto a_q = find_a_q(f_l, f_t);
444 if (std::fabs(a_c - f_l.argument) < std::fabs(a_q - f_l.argument)) {
447 return 0.5F * (a_q + a_c);
449 }
else if (f_t.derivative * f_l.derivative < 0) {
450 const auto a_c = find_cubic_minimizer(f_l, f_t);
451 const auto a_s = find_a_s(f_l, f_t);
452 if (std::fabs(a_c - f_t.argument) >= std::fabs(a_s - f_t.argument)) {
457 }
else if (
comp::abs_lte(std::abs(f_t.derivative), std::abs(f_l.derivative), kValueEps)) {
459 const auto a_c = find_cubic_minimizer(f_l, f_t);
460 const auto a_s = find_a_s(f_l, f_t);
461 if (std::fabs(a_c - f_t.argument) < std::fabs(a_s - f_t.argument)) {
464 static_cast<StepT>(a_c));
468 static_cast<StepT>(a_s));
471 return find_cubic_minimizer(f_t, f_u);
475 template<
typename FunctionValueT>
476 MoreThuenteLineSearch::Interval MoreThuenteLineSearch::update_interval(
477 const FunctionValueT & f_t,
const FunctionValueT & f_l,
const FunctionValueT & f_u)
481 if (f_t.value > f_l.value) {
482 return {f_l.argument, f_t.argument};
483 }
else if (f_t.derivative * (f_t.argument - f_l.argument) < 0) {
484 return {f_t.argument, f_u.argument};
485 }
else if (f_t.derivative * (f_t.argument - f_l.argument) > 0) {
486 return {f_t.argument, f_l.argument};
489 return {f_t.argument, f_t.argument};
497 #endif // OPTIMIZATION__LINE_SEARCH__MORE_THUENTE_LINE_SEARCH_HPP_ Definition: common/optimization/include/optimization/utils.hpp:38
float_t StepT
Definition: line_search.hpp:38
OptimizationDirection
An enum that defines the direction of optimization.
Definition: more_thuente_line_search.hpp:100
Base class (CRTP) to mange the step length during optimization.
Definition: line_search.hpp:34
float float32_t
Definition: types.hpp:36
StepT argument
Definition: more_thuente_line_search.hpp:302
constexpr common::types::float32_t kDelta
This value is used in More-Thuente paper without explanation (in the paper: Section 4...
Definition: more_thuente_line_search.hpp:78
ValueT value
Definition: more_thuente_line_search.hpp:303
bool abs_lte(const T &a, const T &b, const T &eps)
Check for approximate less than or equal in absolute terms.
Definition: float_comparisons.hpp:69
w
Definition: catr_diff.py:22
This class describes a More-Thuente line search as presented in the paper "Line Search Algorithms wit...
Definition: more_thuente_line_search.hpp:94
ValueT derivative
Definition: more_thuente_line_search.hpp:304
Definition: bool_comparisons.hpp:32
bool approx_eq(const T &a, const T &b, const T &abs_eps, const T &rel_eps)
Check for approximate equality in absolute and relative terms.
Definition: float_comparisons.hpp:139
DomainValueT compute_next_step_(const DomainValueT &x0, const DomainValueT &initial_step, OptimizationProblemT &optimization_problem)
Calculates the next step.
Definition: more_thuente_line_search.hpp:204
A utility struct that holds the argument, value and derivative of a function.
Definition: more_thuente_line_search.hpp:300
MoreThuenteLineSearch(const StepT max_step, const StepT min_step, const OptimizationDirection optimization_direction=OptimizationDirection::kMinimization, const StepT mu=1.e-4F, const StepT eta=0.1F, const std::int32_t max_iterations=10)
Constructs a new instance.
Definition: more_thuente_line_search.hpp:118
This file defines the lanelet2_map_provider_node class.
Definition: quick_sort.hpp:24