numerical-collection-cpp 0.10.0
A collection of algorithms in numerical analysis implemented in C++
Loading...
Searching...
No Matches
downhill_simplex.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 <algorithm>
23#include <cstddef>
24#include <cstdint>
25#include <iterator>
26#include <string_view>
27#include <vector>
28
29#include <Eigen/Core>
30
38
39namespace num_collect::opt {
40
42constexpr auto downhill_simplex_tag =
43 logging::log_tag_view("num_collect::opt::downhill_simplex");
44
50template <concepts::objective_function ObjectiveFunction>
52
58template <concepts::multi_variate_objective_function ObjectiveFunction>
59class downhill_simplex<ObjectiveFunction>
60 : public optimizer_base<downhill_simplex<ObjectiveFunction>> {
61public:
64
66 using objective_function_type = ObjectiveFunction;
67
69 using variable_type = typename objective_function_type::variable_type;
70
72 using variable_scalar_type = typename variable_type::Scalar;
73
75 using value_type = typename objective_function_type::value_type;
76
78 enum class process_type : std::uint8_t {
79 none,
80 reflection,
81 reflection_and_expansion,
82 contraction,
83 multiple_contraction
84 };
85
92 [[nodiscard]] static auto process_name(process_type process)
93 -> std::string_view {
94 switch (process) {
95 case process_type::none:
96 return "none";
97 case process_type::reflection:
98 return "reflection";
99 case process_type::reflection_and_expansion:
100 return "reflection and expansion";
101 case process_type::contraction:
102 return "contraction";
103 case process_type::multiple_contraction:
104 return "multiple contraction";
105 default:
106 return "invalid process";
107 }
108 }
109
117 : optimizer_base<downhill_simplex<ObjectiveFunction>>(
119 obj_fun_(obj_fun) {}
120
127 obj_fun_ = obj_fun;
128 }
129
136 void init(const variable_type& init_var,
137 const variable_scalar_type& width = default_width) {
138 dim_ = init_var.size();
139 iterations_ = 0;
140 evaluations_ = 0;
141
142 variable_type var = init_var;
143 points_.clear();
144 values_.clear();
145 points_.reserve(util::safe_cast<std::size_t>(dim_ + 1));
146 values_.reserve(util::safe_cast<std::size_t>(dim_ + 1));
147 points_.push_back(var);
148 values_.push_back(evaluate_on(var));
149 for (index_type i = 0; i < dim_; ++i) {
150 const variable_scalar_type val = var[i];
151 var[i] += width;
152 points_.push_back(var);
153 values_.push_back(evaluate_on(var));
154 var[i] = val;
155 }
156
157 value_order_.clear();
158 value_order_.reserve(util::safe_cast<std::size_t>(dim_ + 1));
159 for (std::size_t i = 0; i < util::safe_cast<std::size_t>(dim_ + 1);
160 ++i) {
161 value_order_.push_back(i);
162 }
163 reorder();
164 }
165
169 void iterate() {
170 const variable_type face_center = calc_face_center();
171 const auto min_ind = value_order_.front();
172 const auto second_max_ind =
173 value_order_[util::safe_cast<std::size_t>(dim_ - 1)];
174 const auto max_ind = value_order_.back();
175
176 reflect(face_center);
177 if (values_[max_ind] < values_[min_ind]) {
178 expand(face_center);
179 } else if (values_[max_ind] >= values_[second_max_ind]) {
180 contract(face_center);
181 if (values_[max_ind] >= values_[second_max_ind]) {
182 multi_contract();
183 }
184 } else {
185 // no operation
186 }
187
188 reorder();
189 ++iterations_;
190 }
191
195 [[nodiscard]] auto is_stop_criteria_satisfied() const -> bool {
196 return (simplex_size() < tol_simplex_size_) ||
197 (iterations() >= max_iterations_);
198 }
199
205 const {
206 iteration_logger.template append<index_type>(
207 "Iter.", &this_type::iterations);
208 iteration_logger.template append<index_type>(
209 "Eval.", &this_type::evaluations);
210 iteration_logger.template append<value_type>(
211 "Value", &this_type::opt_value);
212 iteration_logger.template append<variable_scalar_type>(
213 "SimplexSize", &this_type::simplex_size);
214 constexpr index_type process_width = 26;
215 iteration_logger
216 .template append<std::string_view>(
217 "Process", &this_type::last_process_name)
218 ->width(process_width);
219 }
220
224 [[nodiscard]] auto opt_variable() const -> const variable_type& {
225 return points_[value_order_.front()];
226 }
227
231 [[nodiscard]] auto opt_value() const -> const value_type& {
232 return values_[value_order_.front()];
233 }
234
238 [[nodiscard]] auto iterations() const noexcept -> index_type {
239 return iterations_;
240 }
241
245 [[nodiscard]] auto evaluations() const noexcept -> index_type {
246 return evaluations_;
247 }
248
254 [[nodiscard]] auto last_process() const noexcept -> process_type {
255 return process_;
256 }
257
263 [[nodiscard]] auto last_process_name() const noexcept -> std::string_view {
264 return process_name(last_process());
265 }
266
272 [[nodiscard]] auto simplex_size() const -> variable_scalar_type {
273 return (points_[value_order_.front()] - points_[value_order_.back()])
274 .norm();
275 }
276
278 static inline const auto default_width =
279 static_cast<variable_scalar_type>(0.1);
280
288 -> downhill_simplex& {
289 tol_simplex_size_ = value;
290 return *this;
291 }
292
293private:
300 [[nodiscard]] auto evaluate_on(const variable_type& variable) {
301 obj_fun_.evaluate_on(variable);
302 ++evaluations_;
303 return obj_fun_.value();
304 }
305
309 void reorder() {
310 std::sort(std::begin(value_order_), std::end(value_order_),
311 [this](std::size_t i, std::size_t j) {
312 return values_[i] < values_[j];
313 });
314 }
315
322 [[nodiscard]] auto calc_face_center() const -> variable_type {
323 variable_type face_center = variable_type::Zero(dim_);
324 for (std::size_t i = 0; i < util::safe_cast<std::size_t>(dim_); ++i) {
325 face_center += points_[value_order_[i]];
326 }
327 face_center /= static_cast<variable_scalar_type>(dim_);
328 return face_center;
329 }
330
332 static inline const auto twice = static_cast<variable_scalar_type>(2.0);
333
335 static inline const auto half = static_cast<variable_scalar_type>(0.5);
336
343 void reflect(const variable_type& face_center) {
344 const std::size_t ind_move = value_order_.back();
345 points_[ind_move] = twice * face_center - points_[ind_move];
346 values_[ind_move] = evaluate_on(points_[ind_move]);
347 process_ = process_type::reflection;
348 }
349
356 void expand(const variable_type& face_center) {
357 const std::size_t ind_move = value_order_.back();
358 points_[ind_move] = twice * points_[ind_move] - face_center;
359 values_[ind_move] = evaluate_on(points_[ind_move]);
360 process_ = process_type::reflection_and_expansion;
361 }
362
369 void contract(const variable_type& face_center) {
370 const std::size_t ind_move = value_order_.back();
371 points_[ind_move] = half * (points_[ind_move] + face_center);
372 values_[ind_move] = evaluate_on(points_[ind_move]);
373 process_ = process_type::contraction;
374 }
375
381 const auto& min_point = points_[value_order_.front()];
382 for (std::size_t i = 1; i <= util::safe_cast<std::size_t>(dim_); ++i) {
383 const std::size_t ind_move = value_order_[i];
384 points_[ind_move] = half * (points_[ind_move] + min_point);
385 values_[ind_move] = evaluate_on(points_[ind_move]);
386 }
387 process_ = process_type::multiple_contraction;
388 }
389
392
394 index_type dim_{0};
395
397 std::vector<variable_type, Eigen::aligned_allocator<variable_type>>
398 points_{};
399
401 std::vector<value_type> values_{};
402
404 std::vector<std::size_t> value_order_{};
405
407 process_type process_{process_type::none};
408
410 index_type iterations_{0};
411
413 index_type evaluations_{0};
414
416 static inline const auto default_tol_simplex_size =
417 static_cast<variable_scalar_type>(1e-4);
418
420 variable_scalar_type tol_simplex_size_{default_tol_simplex_size};
421
423 static constexpr index_type default_max_iterations = 1000;
424
426 index_type max_iterations_{default_max_iterations};
427};
428
429} // namespace num_collect::opt
Class of tags of logs without memory management.
Class of downhill simplex method press2007.
typename variable_type::Scalar variable_scalar_type
Type of scalars in variables.
void contract(const variable_type &face_center)
Contract the highest point to the opposite face.
void expand(const variable_type &face_center)
Expand the simplex.
auto opt_variable() const -> const variable_type &
Get current optimal variable.
auto evaluate_on(const variable_type &variable)
Evaluate function value.
static auto process_name(process_type process) -> std::string_view
Convert type of process to string.
auto is_stop_criteria_satisfied() const -> bool
Determine if stopping criteria of the algorithm are satisfied.
auto last_process_name() const noexcept -> std::string_view
Get the name of the last process.
typename objective_function_type::value_type value_type
Type of function values.
ObjectiveFunction objective_function_type
Type of the objective function.
auto calc_face_center() const -> variable_type
Calculate center of the face composed from points other than highest point.
auto iterations() const noexcept -> index_type
Get the number of iterations.
auto simplex_size() const -> variable_scalar_type
Get the size of simplex.
downhill_simplex(const objective_function_type &obj_fun=objective_function_type())
Constructor.
objective_function_type obj_fun_
Objective function.
auto tol_simplex_size(const variable_scalar_type &value) -> downhill_simplex &
Set tolerance of size of simplex.
typename objective_function_type::variable_type variable_type
Type of variables.
void reflect(const variable_type &face_center)
Reflect the highest point.
auto evaluations() const noexcept -> index_type
Get the number of function evaluations.
void change_objective_function(const objective_function_type &obj_fun)
Change the objective function.
void multi_contract()
Contract all points other than the lowest point toward the lowest point.
auto opt_value() const -> const value_type &
Get current optimal value.
void configure_iteration_logger(logging::iterations::iteration_logger< this_type > &iteration_logger) const
Configure an iteration logger.
auto last_process() const noexcept -> process_type
Get last process.
void init(const variable_type &init_var, const variable_scalar_type &width=default_width)
Initialize the algorithm.
Class of downhill simplex method.
Base class of implementations of optimization algorithms.
Definition of index_type type.
Definition of iteration_logger class.
Definition of log_tag_view class.
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 downhill_simplex_tag
Tag of 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 safe_cast function.