numerical-collection-cpp 0.10.0
A collection of algorithms in numerical analysis implemented in C++
Loading...
Searching...
No Matches
annealing_downhill_simplex.h
Go to the documentation of this file.
1/*
2 * Copyright 2024 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 <algorithm>
23#include <cmath>
24#include <cstddef>
25#include <cstdint>
26#include <iterator>
27#include <limits>
28#include <random>
29#include <string_view>
30#include <vector>
31
32#include <Eigen/Core>
33
44
45namespace num_collect::opt {
46
49 logging::log_tag_view("num_collect::opt::annealing_downhill_simplex");
50
57template <concepts::objective_function ObjectiveFunction>
59
66template <concepts::multi_variate_objective_function ObjectiveFunction>
67class annealing_downhill_simplex<ObjectiveFunction>
68 : public optimizer_base<annealing_downhill_simplex<ObjectiveFunction>> {
69public:
70 // TODO Refactoring.
71
74
76 using objective_function_type = ObjectiveFunction;
77
79 using variable_type = typename objective_function_type::variable_type;
80
82 using variable_scalar_type = typename variable_type::Scalar;
83
85 using value_type = typename objective_function_type::value_type;
86
88 using random_number_generator_type = std::mt19937;
89
91 enum class process_type : std::uint8_t {
92 none,
93 reflection,
94 reflection_and_expansion,
95 contraction,
96 multiple_contraction
97 };
98
105 [[nodiscard]] static auto process_name(process_type process)
106 -> std::string_view {
107 switch (process) {
108 case process_type::none:
109 return "none";
110 case process_type::reflection:
111 return "reflection";
112 case process_type::reflection_and_expansion:
113 return "reflection and expansion";
114 case process_type::contraction:
115 return "contraction";
116 case process_type::multiple_contraction:
117 return "multiple contraction";
118 default:
119 return "invalid process";
120 }
121 }
122
130 : optimizer_base<annealing_downhill_simplex<ObjectiveFunction>>(
132 obj_fun_(obj_fun) {}
133
140 obj_fun_ = obj_fun;
141 }
142
149 void init(const variable_type& init_var,
150 const variable_scalar_type& width = default_width) {
151 dim_ = init_var.size();
152 iterations_ = 0;
153 evaluations_ = 0;
154 iterations_in_current_trial_ = 0;
155 temperature_ = highest_temperature_;
156 opt_value_ = std::numeric_limits<value_type>::max();
157
158 variable_type var = init_var;
159 points_.reserve(util::safe_cast<std::size_t>(dim_ + 1));
160 values_.reserve(util::safe_cast<std::size_t>(dim_ + 1));
161 points_.push_back(var);
162 values_.push_back(evaluate_on(var));
163 for (index_type i = 0; i < dim_; ++i) {
164 const variable_scalar_type val = var[i];
165 var[i] += width;
166 points_.push_back(var);
167 values_.push_back(evaluate_on(var));
168 var[i] = val;
169 }
170
171 value_order_.reserve(util::safe_cast<std::size_t>(dim_ + 1));
172 for (std::size_t i = 0; i < util::safe_cast<std::size_t>(dim_ + 1);
173 ++i) {
174 value_order_.push_back(i);
175 }
176 fluctuated_values_.resize(util::safe_cast<std::size_t>(dim_ + 1));
177 reorder();
178 }
179
183 void iterate() {
184 const variable_type face_center = calc_face_center();
185 const auto min_ind = value_order_.front();
186 const auto second_max_ind =
187 value_order_[util::safe_cast<std::size_t>(dim_ - 1)];
188 const auto max_ind = value_order_.back();
189
190 reflect(face_center);
191 if (fluctuated_values_[max_ind] < fluctuated_values_[min_ind]) {
192 expand(face_center);
193 } else if (fluctuated_values_[max_ind] >=
194 fluctuated_values_[second_max_ind]) {
195 contract(face_center);
196 if (fluctuated_values_[max_ind] >=
197 fluctuated_values_[second_max_ind]) {
198 multi_contract();
199 }
200 } else {
201 // no operation
202 }
203
204 reorder();
205 ++iterations_;
206 ++iterations_in_current_trial_;
207 if (iterations_in_current_trial_ >= max_iterations_per_trial_) {
208 iterations_in_current_trial_ = 0;
209 }
210 using std::pow;
211 temperature_ = highest_temperature_ *
212 pow(static_cast<value_type>(1) -
213 static_cast<value_type>(iterations_in_current_trial_) /
214 static_cast<value_type>(max_iterations_per_trial_),
215 4);
216 }
217
221 [[nodiscard]] auto is_stop_criteria_satisfied() const -> bool {
222 return iterations() >= max_iterations_;
223 }
224
230 const {
231 iteration_logger.template append<index_type>(
232 "Iter.", &this_type::iterations);
233 iteration_logger.template append<index_type>(
234 "Eval.", &this_type::evaluations);
235 iteration_logger.template append<value_type>(
236 "Value", &this_type::opt_value);
237 iteration_logger.template append<variable_scalar_type>(
238 "SimplexSize", &this_type::simplex_size);
239 iteration_logger.template append<variable_scalar_type>(
240 "Temperature", &this_type::temperature);
241 constexpr index_type process_width = 26;
242 iteration_logger
243 .template append<std::string_view>(
244 "Process", &this_type::last_process_name)
245 ->width(process_width);
246 }
247
251 [[nodiscard]] auto opt_variable() const -> const variable_type& {
252 return opt_variable_;
253 }
254
258 [[nodiscard]] auto opt_value() const -> const value_type& {
259 return opt_value_;
260 }
261
265 [[nodiscard]] auto iterations() const noexcept -> index_type {
266 return iterations_;
267 }
268
272 [[nodiscard]] auto evaluations() const noexcept -> index_type {
273 return evaluations_;
274 }
275
281 [[nodiscard]] auto last_process() const noexcept -> process_type {
282 return process_;
283 }
284
290 [[nodiscard]] auto last_process_name() const noexcept -> std::string_view {
291 return process_name(last_process());
292 }
293
299 [[nodiscard]] auto simplex_size() const -> variable_scalar_type {
300 return (points_[value_order_.front()] - points_[value_order_.back()])
301 .norm();
302 }
303
309 [[nodiscard]] auto temperature() const noexcept -> value_type {
310 return temperature_;
311 }
312
319 auto seed(random_number_generator_type::result_type value)
321 random_number_generator_.seed(value);
322 return *this;
323 }
324
332 NUM_COLLECT_PRECONDITION(value > static_cast<value_type>(0),
333 this->logger(), "Highest temperature must be a positive number.");
334 highest_temperature_ = value;
335 return *this;
336 }
337
346 NUM_COLLECT_PRECONDITION(value > 0, this->logger(),
347 "Maximum number of iterations in each trial must be a positive "
348 "number.");
349 max_iterations_per_trial_ = value;
350 return *this;
351 }
352
360 NUM_COLLECT_PRECONDITION(value > 0, this->logger(),
361 "Maximum number of iterations must be a positive number.");
362 max_iterations_ = value;
363 return *this;
364 }
365
367 static inline const auto default_width =
368 static_cast<variable_scalar_type>(0.1);
369
370private:
377 [[nodiscard]] auto evaluate_on(const variable_type& variable) {
378 obj_fun_.evaluate_on(variable);
379 ++evaluations_;
380 const auto& value = obj_fun_.value();
381 if (value < opt_value_) {
382 opt_value_ = value;
383 opt_variable_ = variable;
384 }
385 return value;
386 }
387
391 void reorder() {
392 for (std::size_t i = 0; i < values_.size(); ++i) {
393 fluctuated_values_[i] = values_[i] + generate_fluctuation();
394 }
395 std::sort(std::begin(value_order_), std::end(value_order_),
396 [this](std::size_t i, std::size_t j) {
397 return fluctuated_values_[i] < fluctuated_values_[j];
398 });
399 }
400
407 [[nodiscard]] auto calc_face_center() const -> variable_type {
408 variable_type face_center = variable_type::Zero(dim_);
409 for (std::size_t i = 0; i < util::safe_cast<std::size_t>(dim_); ++i) {
410 face_center += points_[value_order_[i]];
411 }
412 face_center /= static_cast<variable_scalar_type>(dim_);
413 return face_center;
414 }
415
417 static inline const auto twice = static_cast<variable_scalar_type>(2.0);
418
420 static inline const auto half = static_cast<variable_scalar_type>(0.5);
421
428 void reflect(const variable_type& face_center) {
429 const std::size_t ind_move = value_order_.back();
430 points_[ind_move] = twice * face_center - points_[ind_move];
431 values_[ind_move] = evaluate_on(points_[ind_move]);
432 fluctuated_values_[ind_move] =
433 values_[ind_move] - generate_fluctuation();
434 process_ = process_type::reflection;
435 }
436
443 void expand(const variable_type& face_center) {
444 const std::size_t ind_move = value_order_.back();
445 points_[ind_move] = twice * points_[ind_move] - face_center;
446 values_[ind_move] = evaluate_on(points_[ind_move]);
447 fluctuated_values_[ind_move] =
448 values_[ind_move] - generate_fluctuation();
449 process_ = process_type::reflection_and_expansion;
450 }
451
458 void contract(const variable_type& face_center) {
459 const std::size_t ind_move = value_order_.back();
460 points_[ind_move] = half * (points_[ind_move] + face_center);
461 values_[ind_move] = evaluate_on(points_[ind_move]);
462 fluctuated_values_[ind_move] =
463 values_[ind_move] - generate_fluctuation();
464 process_ = process_type::contraction;
465 }
466
472 const auto& min_point = points_[value_order_.front()];
473 for (std::size_t i = 1; i <= util::safe_cast<std::size_t>(dim_); ++i) {
474 const std::size_t ind_move = value_order_[i];
475 points_[ind_move] = half * (points_[ind_move] + min_point);
476 values_[ind_move] = evaluate_on(points_[ind_move]);
477 fluctuated_values_[ind_move] =
478 values_[ind_move] - generate_fluctuation();
479 }
480 process_ = process_type::multiple_contraction;
481 }
482
488 [[nodiscard]] auto generate_fluctuation() -> value_type {
489 using std::log;
490 return temperature_ *
491 log(static_cast<value_type>(random_number_generator_type::max()) /
492 (static_cast<value_type>(random_number_generator_()) +
493 static_cast<value_type>(1)));
494 }
495
498
500 index_type dim_{0};
501
503 std::vector<variable_type, Eigen::aligned_allocator<variable_type>>
504 points_{};
505
507 std::vector<value_type> values_{};
508
510 std::vector<value_type> fluctuated_values_{};
511
513 std::vector<std::size_t> value_order_{};
514
516 process_type process_{process_type::none};
517
519 variable_type opt_variable_{};
520
522 value_type opt_value_{};
523
525 index_type iterations_{0};
526
528 index_type evaluations_{0};
529
531 index_type iterations_in_current_trial_{0};
532
534 random_number_generator_type random_number_generator_{
535 std::random_device()()};
536
538 value_type temperature_{};
539
541 static inline const auto default_highest_temperature =
542 static_cast<value_type>(10);
543
545 value_type highest_temperature_{default_highest_temperature};
546
548 static constexpr index_type default_max_iterations_per_trial = 100;
549
551 index_type max_iterations_per_trial_{default_max_iterations_per_trial};
552
554 static constexpr index_type default_max_iterations = 1000;
555
557 index_type max_iterations_{default_max_iterations};
558};
559
560} // namespace num_collect::opt
Class of tags of logs without memory management.
Class of downhill simplex method with simulated annealing press2007.
auto iterations() const noexcept -> index_type
Get the number of iterations.
auto is_stop_criteria_satisfied() const -> bool
Determine if stopping criteria of the algorithm are satisfied.
auto max_iterations_per_trial(index_type value) -> annealing_downhill_simplex &
Set the maximum number of iterations in each trial.
void change_objective_function(const objective_function_type &obj_fun)
Change the objective function.
void contract(const variable_type &face_center)
Contract the highest point to the opposite face.
auto highest_temperature(value_type value) -> annealing_downhill_simplex &
Set the highest temperature.
auto simplex_size() const -> variable_scalar_type
Get the size of simplex.
auto evaluations() const noexcept -> index_type
Get the number of function evaluations.
annealing_downhill_simplex(const objective_function_type &obj_fun=objective_function_type())
Constructor.
typename objective_function_type::value_type value_type
Type of function values.
auto calc_face_center() const -> variable_type
Calculate center of the face composed from points other than highest point.
std::mt19937 random_number_generator_type
Type of the random number generator.
void configure_iteration_logger(logging::iterations::iteration_logger< this_type > &iteration_logger) const
Configure an iteration logger.
auto last_process_name() const noexcept -> std::string_view
Get the name of the last process.
auto opt_variable() const -> const variable_type &
Get current optimal variable.
void expand(const variable_type &face_center)
Expand the simplex.
auto opt_value() const -> const value_type &
Get current optimal value.
auto max_iterations(index_type value) -> annealing_downhill_simplex &
Set the maximum number of iterations.
auto last_process() const noexcept -> process_type
Get last process.
typename variable_type::Scalar variable_scalar_type
Type of scalars in variables.
void init(const variable_type &init_var, const variable_scalar_type &width=default_width)
Initialize the algorithm.
static auto process_name(process_type process) -> std::string_view
Convert type of process to string.
void reflect(const variable_type &face_center)
Reflect the highest point.
ObjectiveFunction objective_function_type
Type of the objective function.
void multi_contract()
Contract all points other than the lowest point toward the lowest point.
auto seed(random_number_generator_type::result_type value) -> annealing_downhill_simplex &
Change the seed of the random number generator.
auto evaluate_on(const variable_type &variable)
Evaluate function value.
typename objective_function_type::variable_type variable_type
Type of variables.
auto generate_fluctuation() -> value_type
Generate a thermal fluctuation.
auto temperature() const noexcept -> value_type
Get the current temperature.
Class of downhill simplex method with simulated annealing press2007.
Base class of implementations of optimization algorithms.
Definition of exceptions.
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_objective_function concept.
std::ptrdiff_t index_type
Type of indices in this library.
Definition index_type.h:33
Namespace of optimization algorithms.
constexpr auto annealing_downhill_simplex_tag
Tag of annealing_downhill_simplex.
auto safe_cast(const From &value) -> To
Cast safely.
Definition safe_cast.h:54
STL namespace.
Definition of objective_function concept.
Definition of optimizer_base class.
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 safe_cast function.