numerical-collection-cpp 0.10.0
A collection of algorithms in numerical analysis implemented in C++
Loading...
Searching...
No Matches
newton_raphson.h
Go to the documentation of this file.
1/*
2 * Copyright 2021 MusicScience37 (Kenta Kabashima)
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
20#pragma once
21
22#include <cmath>
23#include <limits>
24#include <type_traits>
25
26#include <Eigen/LU>
27
38
39namespace num_collect::roots {
40
42constexpr auto newton_raphson_tag =
43 logging::log_tag_view("num_collect::roots::newton_raphson");
44
50template <concepts::differentiable_function Function>
52
60template <concepts::single_variate_differentiable_function Function>
61class newton_raphson<Function>
62 : public function_root_finder_base<newton_raphson<Function>, Function> {
63public:
66
68 using base_type =
70
71 using typename base_type::function_type;
72 using typename base_type::variable_type;
73
74 static_assert(
75 std::is_same_v<variable_type, typename function_type::jacobian_type>);
76
82 explicit newton_raphson(const function_type& function = function_type())
83 : base_type(newton_raphson_tag, function) {}
84
90 void init(const variable_type& variable) {
91 variable_ = variable;
92 last_change_ = std::numeric_limits<variable_type>::infinity();
93 iterations_ = 0;
94 evaluations_ = 0;
95
96 function().evaluate_on(variable_);
97 ++evaluations_;
98 using std::abs;
99 value_norm_ = abs(function().value());
100 }
101
103 void iterate() {
104 change_ = -function().value() / function().jacobian();
105 variable_ += change_;
106
107 function().evaluate_on(variable_);
108 ++evaluations_;
109 ++iterations_;
110 using std::abs;
111 last_change_ = abs(change_);
112 value_norm_ = abs(function().value());
113 }
114
116 [[nodiscard]] auto is_stop_criteria_satisfied() const -> bool {
117 return (iterations() > max_iterations_) ||
118 (last_change() < tol_last_change_) ||
119 (value_norm() < tol_value_norm_);
120 }
121
125 const {
126 iteration_logger.template append<index_type>(
127 "Iter.", &this_type::iterations);
128 iteration_logger.template append<index_type>(
129 "Eval.", &this_type::evaluations);
130 iteration_logger.template append<variable_type>(
131 "Value", &this_type::value_norm);
132 iteration_logger.template append<variable_type>(
133 "Change", &this_type::last_change);
134 }
135
136 using base_type::function;
137
143 [[nodiscard]] auto variable() const -> const variable_type& {
144 return variable_;
145 }
146
152 [[nodiscard]] auto value() const
153 -> std::invoke_result_t<decltype(&function_type::value),
154 const function_type> {
155 return function().value();
156 }
157
163 [[nodiscard]] auto jacobian() const
164 -> std::invoke_result_t<decltype(&function_type::jacobian),
165 const function_type> {
166 return function().jacobian();
167 }
168
174 [[nodiscard]] auto iterations() const noexcept -> index_type {
175 return iterations_;
176 }
177
183 [[nodiscard]] auto evaluations() const noexcept -> index_type {
184 return evaluations_;
185 }
186
192 [[nodiscard]] auto last_change() const noexcept -> variable_type {
193 return last_change_;
194 }
195
201 [[nodiscard]] auto value_norm() const noexcept -> variable_type {
202 return value_norm_;
203 }
204
212 NUM_COLLECT_PRECONDITION(val > 0, this->logger(),
213 "Maximum number of iterations must be a positive integer.");
214 max_iterations_ = val;
215 return *this;
216 }
217
225 NUM_COLLECT_PRECONDITION(val >= static_cast<variable_type>(0),
226 this->logger(),
227 "Tolerance of last change of the variable must be a "
228 "non-negative value.");
229 tol_last_change_ = val;
230 return *this;
231 }
232
240 NUM_COLLECT_PRECONDITION(val >= static_cast<variable_type>(0),
241 this->logger(),
242 "Tolerance of the norm of function value must be a "
243 "non-negative value.");
244 tol_value_norm_ = val;
245 return *this;
246 }
247
248private:
250 variable_type variable_{};
251
253 variable_type change_{};
254
256 index_type iterations_{};
257
259 index_type evaluations_{};
260
262 variable_type last_change_{};
263
265 variable_type value_norm_{};
266
268 static constexpr index_type default_max_iterations = 1000;
269
271 index_type max_iterations_{default_max_iterations};
272
274 static constexpr auto default_tol_last_change =
275 static_cast<variable_type>(1e-6);
276
278 variable_type tol_last_change_{default_tol_last_change};
279
281 static constexpr auto default_tol_value_norm =
282 static_cast<variable_type>(1e-6);
283
285 variable_type tol_value_norm_{default_tol_value_norm};
286};
287
295template <concepts::multi_variate_differentiable_function Function>
296class newton_raphson<Function>
297 : public function_root_finder_base<newton_raphson<Function>, Function> {
298public:
301
303 using base_type =
305
306 using typename base_type::function_type;
307 using typename base_type::variable_type;
308
310 using scalar_type = typename variable_type::Scalar;
311
313 using jacobian_type = typename function_type::jacobian_type;
314
317 Eigen::PartialPivLU<typename Function::jacobian_type>;
318
324 explicit newton_raphson(const function_type& function = function_type())
325 : base_type(newton_raphson_tag, function) {}
326
332 void init(const variable_type& variable) {
333 variable_ = variable;
334 last_change_ = std::numeric_limits<scalar_type>::infinity();
335 iterations_ = 0;
336 evaluations_ = 0;
337
338 function().evaluate_on(variable_);
339 ++evaluations_;
340 value_norm_ = function().value().norm();
341 }
342
344 void iterate() {
345 jacobian_solver_.compute(function().jacobian());
346 change_ = -jacobian_solver_.solve(function().value());
347 variable_ += change_;
348
349 function().evaluate_on(variable_);
350 ++evaluations_;
351 ++iterations_;
352 last_change_ = change_.norm();
353 value_norm_ = function().value().norm();
354 }
355
357 [[nodiscard]] auto is_stop_criteria_satisfied() const -> bool {
358 return (iterations() > max_iterations_) ||
359 (last_change() < tol_last_change_) ||
360 (value_norm() < tol_value_norm_);
361 }
362
366 const {
367 iteration_logger.template append<index_type>(
368 "Iter.", &this_type::iterations);
369 iteration_logger.template append<index_type>(
370 "Eval.", &this_type::evaluations);
371 iteration_logger.template append<scalar_type>(
372 "Value", &this_type::value_norm);
373 iteration_logger.template append<scalar_type>(
374 "Change", &this_type::last_change);
375 }
376
377 using base_type::function;
378
384 [[nodiscard]] auto variable() const -> const variable_type& {
385 return variable_;
386 }
387
393 [[nodiscard]] auto value() const
394 -> std::invoke_result_t<decltype(&function_type::value),
395 const function_type> {
396 return function().value();
397 }
398
404 [[nodiscard]] auto jacobian() const
405 -> std::invoke_result_t<decltype(&function_type::jacobian),
406 const function_type> {
407 return function().jacobian();
408 }
409
415 [[nodiscard]] auto iterations() const noexcept -> index_type {
416 return iterations_;
417 }
418
424 [[nodiscard]] auto evaluations() const noexcept -> index_type {
425 return evaluations_;
426 }
427
433 [[nodiscard]] auto last_change() const noexcept -> scalar_type {
434 return last_change_;
435 }
436
442 [[nodiscard]] auto value_norm() const noexcept -> scalar_type {
443 return value_norm_;
444 }
445
453 NUM_COLLECT_PRECONDITION(val > 0, this->logger(),
454 "Maximum number of iterations must be a positive integer.");
455 max_iterations_ = val;
456 return *this;
457 }
458
465 auto tol_last_change(const scalar_type& val) -> this_type& {
466 NUM_COLLECT_PRECONDITION(val >= static_cast<scalar_type>(0),
467 this->logger(),
468 "Tolerance of last change of the variable must be a "
469 "non-negative value.");
470 tol_last_change_ = val;
471 return *this;
472 }
473
480 auto tol_value_norm(const scalar_type& val) -> this_type& {
481 NUM_COLLECT_PRECONDITION(val >= static_cast<scalar_type>(0),
482 this->logger(),
483 "Tolerance of the norm of function value must be a "
484 "non-negative value.");
485 tol_value_norm_ = val;
486 return *this;
487 }
488
489private:
491 variable_type variable_{};
492
494 variable_type change_{};
495
497 jacobian_solver_type jacobian_solver_{};
498
500 index_type iterations_{};
501
503 index_type evaluations_{};
504
506 scalar_type last_change_{};
507
509 scalar_type value_norm_{};
510
512 static constexpr index_type default_max_iterations = 1000;
513
515 index_type max_iterations_{default_max_iterations};
516
518 static constexpr auto default_tol_last_change =
519 static_cast<scalar_type>(1e-6);
520
522 scalar_type tol_last_change_{default_tol_last_change};
523
525 static constexpr auto default_tol_value_norm =
526 static_cast<scalar_type>(1e-6);
527
529 scalar_type tol_value_norm_{default_tol_value_norm};
530};
531
532} // namespace num_collect::roots
Class of tags of logs without memory management.
Base class of root-finding algorithms for functions.
void iterate()
Iterate the algorithm once.
auto jacobian() const -> std::invoke_result_t< decltype(&function_type::jacobian), const function_type >
Get Jacobian matrix.
void init(const variable_type &variable)
Initialize.
newton_raphson(const function_type &function=function_type())
Constructor.
auto variable() const -> const variable_type &
Get current variable.
typename variable_type::Scalar scalar_type
Type of scalars in variables.
auto iterations() const noexcept -> index_type
Get the number of iterations.
auto tol_last_change(const variable_type &val) -> this_type &
Set tolerance of last change of the variable.
auto last_change() const noexcept -> variable_type
Get the last change of the variable.
auto evaluations() const noexcept -> index_type
Get the number of function evaluations.
auto value_norm() const noexcept -> scalar_type
Get the norm of function value.
auto tol_value_norm(const variable_type &val) -> this_type &
Set tolerance of the norm of function value.
void configure_iteration_logger(logging::iterations::iteration_logger< this_type > &iteration_logger) const
Configure an iteration logger.
auto last_change() const noexcept -> scalar_type
Get the last change of the variable.
auto value_norm() const noexcept -> variable_type
Get the norm of function value.
auto max_iterations(index_type val) -> this_type &
Set maximum number of iterations.
auto is_stop_criteria_satisfied() const -> bool
Determine if stopping criteria of the algorithm are satisfied.
Eigen::PartialPivLU< typename Function::jacobian_type > jacobian_solver_type
Type of solvers of Jacobian matrices.
auto tol_value_norm(const scalar_type &val) -> this_type &
Set tolerance of the norm of function value.
typename function_type::jacobian_type jacobian_type
Type of Jacobian matrices.
auto value() const -> std::invoke_result_t< decltype(&function_type::value), const function_type >
Get current value.
auto tol_last_change(const scalar_type &val) -> this_type &
Set tolerance of last change of the variable.
Class of Newton-Raphson method.
Definition of differentiable_function concept.
Definition of exceptions.
Definition of function_root_finder_base class.
Definition of index_type type.
Definition of iteration_logger class.
Definition of log_tag_view class.
Definition of macros for logging.
Definition of multi_variate_differentiable_function concept.
std::ptrdiff_t index_type
Type of indices in this library.
Definition index_type.h:33
Namespace of root-finding algorithms.
constexpr auto newton_raphson_tag
Tag of newton_raphson.
STL namespace.
Definition of NUM_COLLECT_PRECONDITION macro.
#define NUM_COLLECT_PRECONDITION(CONDITION,...)
Check whether a precondition is satisfied and throw an exception if not.
Definition of single_variate_differentiable_function concept.