numerical-collection-cpp 0.10.0
A collection of algorithms in numerical analysis implemented in C++
Loading...
Searching...
No Matches
dividing_rectangles.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 <cmath>
24#include <cstddef>
25#include <iterator>
26#include <limits>
27#include <queue>
28#include <tuple>
29#include <utility>
30#include <vector>
31
42#include "num_collect/util/is_eigen_vector.h" // IWYU pragma: keep
44
45namespace num_collect::opt {
46
49 logging::log_tag_view("num_collect::opt::dividing_rectangles");
50
57template <concepts::objective_function ObjectiveFunction>
59 : public optimizer_base<dividing_rectangles<ObjectiveFunction>> {
60public:
63
65 using objective_function_type = ObjectiveFunction;
66
68 using variable_type = typename objective_function_type::variable_type;
69
71 using value_type = typename objective_function_type::value_type;
72
83
90 obj_fun_ = obj_fun;
91 }
92
99 void init(const variable_type& lower, const variable_type& upper) {
100 if constexpr (is_eigen_vector_v<variable_type>) {
101 NUM_COLLECT_PRECONDITION(lower.size() == upper.size(),
102 this->logger(),
103 "Lower and upper limits must have the same size.");
104 }
105 lower_ = lower;
106 upper_ = upper;
107 width_ = upper_ - lower_;
109
110 const auto half = static_cast<value_type>(0.5);
111 const variable_type first_var = half * width_ + lower_;
112 obj_fun_.evaluate_on(first_var);
113 opt_variable_ = first_var;
115
116 iterations_ = 0;
117 evaluations_ = 1;
118
119 rects_.clear();
120 rects_.emplace_back();
121 if constexpr (is_eigen_vector_v<variable_type>) {
122 rects_[0].push(rectangle(variable_type::Zero(dim_),
123 variable_type::Ones(dim_), opt_value_));
124 } else {
125 rects_[0].push(rectangle(static_cast<variable_type>(0),
126 static_cast<variable_type>(1), opt_value_));
127 }
128 }
129
133 void iterate() {
134 const auto divided_rects = determine_rects();
135 for (auto iter = std::rbegin(divided_rects);
136 iter != std::rend(divided_rects); ++iter) {
137 divide_rect(iter->first);
138 }
139 ++iterations_;
140 }
141
145 [[nodiscard]] auto is_stop_criteria_satisfied() const -> bool {
146 return evaluations() >= max_evaluations_;
147 }
148
154 const {
155 iteration_logger.template append<index_type>(
156 "Iter.", &this_type::iterations);
157 iteration_logger.template append<index_type>(
158 "Eval.", &this_type::evaluations);
159 iteration_logger.template append<value_type>(
160 "Value", &this_type::opt_value);
161 }
162
166 [[nodiscard]] auto opt_variable() const -> const variable_type& {
167 return opt_variable_;
168 }
169
173 [[nodiscard]] auto opt_value() const -> const value_type& {
174 return opt_value_;
175 }
176
180 [[nodiscard]] auto iterations() const noexcept -> index_type {
181 return iterations_;
182 }
183
187 [[nodiscard]] auto evaluations() const noexcept -> index_type {
188 return evaluations_;
189 }
190
198 NUM_COLLECT_PRECONDITION(value > 0, this->logger(),
199 "Maximum number of function evaluations must be a positive "
200 "integer.");
201 max_evaluations_ = value;
202 return *this;
203 }
204
213 NUM_COLLECT_PRECONDITION(value > static_cast<value_type>(0),
214 this->logger(),
215 "Minimum rate of improvement in the function value required "
216 "for potentially optimal rectangles must be a positive value.");
217 min_rate_imp_ = value;
218 return *this;
219 }
220
221private:
225 class rectangle {
226 public:
235 const value_type& value)
236 : lower_(lower),
237 upper_(upper),
238 is_divided_(util::safe_cast<std::size_t>(get_size(lower)), false),
239 dist_(norm(lower - upper) * half),
240 value_(value) {}
241
247 [[nodiscard]] auto lower() const -> const variable_type& {
248 return lower_;
249 }
250
256 [[nodiscard]] auto upper() const -> const variable_type& {
257 return upper_;
258 }
259
265 [[nodiscard]] auto is_divided() const -> const std::vector<bool>& {
266 return is_divided_;
267 }
268
274 [[nodiscard]] auto dist() const -> const value_type& { return dist_; }
275
281 [[nodiscard]] auto value() const -> const value_type& { return value_; }
282
295 !is_divided_[static_cast<std::size_t>(dim)]);
296 if constexpr (is_eigen_vector_v<variable_type>) {
297 lower_(dim) = lower;
298 upper_(dim) = upper;
299 } else {
300 lower_ = lower;
301 upper_ = upper;
302 }
303 dist_ = norm(lower_ - upper_) * half;
304 value_ = value;
305 is_divided_[static_cast<std::size_t>(dim)] = true;
306 if (std::all_of(std::begin(is_divided_), std::end(is_divided_),
307 [](bool elem) { return elem; })) {
308 std::fill(
309 std::begin(is_divided_), std::end(is_divided_), false);
310 }
311 }
312
322 [[nodiscard]] auto divide(index_type dim, value_type lower,
324 auto copy = rectangle(*this);
325 copy.divide_in_place(dim, lower, upper, value);
326 return copy;
327 }
328
329 private:
331 static inline const auto half = static_cast<value_type>(0.5);
332
335
338
344 std::vector<bool> is_divided_;
345
348
351 };
352
364 [[nodiscard]] auto operator()(
365 const rectangle& left, const rectangle& right) const -> bool {
366 return left.value() > right.value();
367 }
368 };
369
376 [[nodiscard]] auto evaluate_on(const variable_type& variable)
377 -> value_type {
378 variable_type actual_var;
379 if constexpr (is_eigen_vector_v<variable_type>) {
380 actual_var = variable.cwiseProduct(width_) + lower_;
381 } else {
382 actual_var = variable * width_ + lower_;
383 }
384 obj_fun_.evaluate_on(actual_var);
385 const value_type value = correct_value_if_needed(obj_fun_.value());
386 if (value < opt_value_) {
387 opt_variable_ = actual_var;
388 opt_value_ = value;
389 }
390 ++evaluations_;
391 return value;
392 }
393
399 [[nodiscard]] auto determine_rects() const
400 -> std::vector<std::pair<std::size_t, value_type>> {
401 std::vector<std::pair<std::size_t, value_type>> search_rects;
402 std::size_t ind = 0;
403 // find the largest rectangle
404 for (; ind < rects_.size(); ++ind) {
405 if (!rects_[ind].empty()) {
406 break;
407 }
408 }
409 search_rects.emplace_back(ind, std::numeric_limits<value_type>::max());
410 ++ind;
411 // convex full scan
412 for (; ind < rects_.size(); ++ind) {
413 if (rects_[ind].empty()) {
414 continue;
415 }
416 while (true) {
417 const auto& [last_ind, last_slope] = search_rects.back();
418 const auto slope =
419 calculate_slope(rects_[last_ind].top(), rects_[ind].top());
420 if (slope <= last_slope) {
421 search_rects.emplace_back(ind, slope);
422 break;
423 }
424 search_rects.pop_back();
425 }
426 }
427 // remove rectangles which won't update optimal value
428 using std::abs;
429 const auto value_bound = opt_value_ - min_rate_imp_ * abs(opt_value_);
430 for (auto iter = search_rects.begin(); iter != search_rects.end();) {
431 const auto& [ind, slope] = *iter;
432 if (rects_[ind].top().value() - slope * rects_[ind].top().dist() <=
433 value_bound) {
434 ++iter;
435 } else {
436 iter = search_rects.erase(iter);
437 }
438 }
439 return search_rects;
440 }
441
449 [[nodiscard]] static auto calculate_slope(const rectangle& larger_rect,
450 const rectangle& smaller_rect) -> value_type {
451 return (larger_rect.value() - smaller_rect.value()) /
452 (larger_rect.dist() - smaller_rect.dist());
453 }
454
460 void divide_rect(std::size_t index) {
461 if (index + 1 == rects_.size()) {
462 rects_.emplace_back();
463 }
464 rectangle origin = std::move(rects_[index].top());
465 rects_[index].pop();
466
467 const auto [divided_dim, lower_value, upper_value] =
469 value_type divided_lowest;
470 value_type divided_highest;
471 if constexpr (is_eigen_vector_v<variable_type>) {
472 divided_lowest = origin.lower()(divided_dim);
473 divided_highest = origin.upper()(divided_dim);
474 } else {
475 divided_lowest = origin.lower();
476 divided_highest = origin.upper();
477 }
478 const auto [divided_lower, divided_upper] =
479 separate_section(divided_lowest, divided_highest);
480
481 rects_[index + 1].push(origin.divide(
482 divided_dim, divided_lowest, divided_lower, lower_value));
483 rects_[index + 1].push(origin.divide(
484 divided_dim, divided_upper, divided_highest, upper_value));
485 origin.divide_in_place(
486 divided_dim, divided_lower, divided_upper, origin.value());
487 rects_[index + 1].push(std::move(origin));
488 }
489
497 [[nodiscard]] auto determine_divided_dimension(const rectangle& rect)
498 -> std::tuple<index_type, value_type, value_type> {
499 value_type min_value = std::numeric_limits<value_type>::max();
500 index_type dim = 0;
501 value_type dim_lower_value{};
502 value_type dim_upper_value{};
503 for (index_type i = 0; i < dim_; ++i) {
504 if (rect.is_divided()[util::safe_cast<std::size_t>(i)]) {
505 continue;
506 }
507
508 value_type width;
509 if constexpr (is_eigen_vector_v<variable_type>) {
510 width = rect.upper()[i] - rect.lower()[i];
511 } else {
512 width = rect.upper() - rect.lower();
513 }
514 static const auto one_third = static_cast<value_type>(1.0 / 3.0);
515 const value_type diff = width * one_third;
516
517 static const auto half = static_cast<value_type>(0.5);
518 variable_type center = (rect.lower() + rect.upper()) * half;
519 value_type origin_center;
520 if constexpr (is_eigen_vector_v<variable_type>) {
521 origin_center = center[i];
522 } else {
523 origin_center = center;
524 }
525
526 value_type lower_value;
527 value_type upper_value;
528 if constexpr (is_eigen_vector_v<variable_type>) {
529 center[i] = origin_center - diff;
530 lower_value = evaluate_on(center);
531 center[i] = origin_center + diff;
532 upper_value = evaluate_on(center);
533 } else {
534 lower_value = evaluate_on(origin_center - diff);
535 upper_value = evaluate_on(origin_center + diff);
536 }
537
538 if (lower_value < min_value) {
539 min_value = lower_value;
540 dim = i;
541 dim_lower_value = lower_value;
542 dim_upper_value = upper_value;
543 }
544 if (upper_value < min_value) {
545 min_value = upper_value;
546 dim = i;
547 dim_lower_value = lower_value;
548 dim_upper_value = upper_value;
549 }
550 }
551
552 return {dim, dim_lower_value, dim_upper_value};
553 }
554
562 [[nodiscard]] static auto separate_section(value_type lowest,
563 value_type highest) -> std::pair<value_type, value_type> {
564 const auto width = highest - lowest;
565 static const auto one_third = static_cast<value_type>(1.0 / 3.0);
566 static const auto two_thirds = static_cast<value_type>(2.0 / 3.0);
567 return {lowest + one_third * width, lowest + two_thirds * width};
568 }
569
576 [[nodiscard]] static auto correct_value_if_needed(value_type value) noexcept
577 -> value_type {
578 constexpr auto safe_limit =
579 std::numeric_limits<value_type>::max() * 1e-2;
580 if (!isfinite(value) || value > safe_limit) {
581 return safe_limit;
582 }
583 return value;
584 }
585
588
594 std::vector<std::priority_queue<rectangle, std::vector<rectangle>,
597
600
603
606
609
612
615
618
621
623 static constexpr index_type default_max_evaluations = 10000;
624
627
632 static inline const auto default_min_rate_imp =
633 static_cast<value_type>(1e-4);
634
640};
641
642} // namespace num_collect::opt
Definition of assertion macros.
#define NUM_COLLECT_DEBUG_ASSERT(CONDITION)
Macro to check whether a condition is satisfied in debug build only.
Definition assert.h:75
Class of tags of logs without memory management.
auto logger() const noexcept -> const num_collect::logging::logger &
Access to the logger.
Class of hyper rectangle in DIRECT method.
std::vector< bool > is_divided_
Whether each dimension is divided.
auto divide(index_type dim, value_type lower, value_type upper, value_type value) const -> rectangle
Divide this object and return the divided rectangle.
rectangle(const variable_type &lower, const variable_type &upper, const value_type &value)
Constructor.
variable_type upper_
Element-wise upper limit.
value_type dist_
Distance between center point and end point.
void divide_in_place(index_type dim, value_type lower, value_type upper, value_type value)
Divide this object in place.
auto value() const -> const value_type &
Get function value at the center.
value_type value_
Function value at the center.
auto dist() const -> const value_type &
Get distance between center point and end point.
variable_type lower_
Element-wise lower limit.
auto upper() const -> const variable_type &
Get element-wise upper limit.
auto lower() const -> const variable_type &
Get element-wise lower limit.
auto is_divided() const -> const std::vector< bool > &
Get whether each dimension is divided.
typename objective_function_type::variable_type variable_type
Type of variables.
value_type opt_value_
Current optimal value.
static auto separate_section(value_type lowest, value_type highest) -> std::pair< value_type, value_type >
Separate a section.
variable_type lower_
Element-wise lower limit.
variable_type width_
Element-wise width.
static auto calculate_slope(const rectangle &larger_rect, const rectangle &smaller_rect) -> value_type
Calculate slope.
auto evaluate_on(const variable_type &variable) -> value_type
Evaluate function value.
void configure_iteration_logger(logging::iterations::iteration_logger< this_type > &iteration_logger) const
Configure an iteration logger.
static auto correct_value_if_needed(value_type value) noexcept -> value_type
Correct function values if needed.
auto determine_rects() const -> std::vector< std::pair< std::size_t, value_type > >
Search rectangles to divide.
std::vector< std::priority_queue< rectangle, std::vector< rectangle >, greater_rectangle > > rects_
Rectangles.
auto iterations() const noexcept -> index_type
Get the number of iterations.
dividing_rectangles< ObjectiveFunction > this_type
This class.
void change_objective_function(const objective_function_type &obj_fun)
Change the objective function.
objective_function_type obj_fun_
Objective function.
auto determine_divided_dimension(const rectangle &rect) -> std::tuple< index_type, value_type, value_type >
Determine the divided dimension of a rectangle.
variable_type upper_
Element-wise upper limit.
dividing_rectangles(const objective_function_type &obj_fun=objective_function_type())
Constructor.
void iterate()
Iterate the algorithm once.
value_type min_rate_imp_
Minimum rate of improvement in the function value required for potentially optimal rectangles.
index_type iterations_
Number of iterations.
ObjectiveFunction objective_function_type
Type of the objective function.
static const auto default_min_rate_imp
Default minimum rate of improvement in the function value required for potentially optimal rectangles...
void init(const variable_type &lower, const variable_type &upper)
Initialize the algorithm.
auto max_evaluations(index_type value) -> dividing_rectangles &
Set the maximum number of function evaluations.
void divide_rect(std::size_t index)
Divide a rectangle.
typename objective_function_type::value_type value_type
Type of function values.
variable_type opt_variable_
Current optimal variable.
index_type max_evaluations_
Maximum number of function evaluations.
auto is_stop_criteria_satisfied() const -> bool
Determine if stopping criteria of the algorithm are satisfied.
static constexpr index_type default_max_evaluations
Default maximum number of function evaluations.
index_type evaluations_
Number of function evaluations.
index_type dim_
Number of dimension.
auto opt_variable() const -> const variable_type &
Get current optimal variable.
auto evaluations() const noexcept -> index_type
Get the number of function evaluations.
auto min_rate_imp(value_type value) -> dividing_rectangles &
Set the minimum rate of improvement in the function value required for potentially optimal rectangles...
auto opt_value() const -> const value_type &
Get current optimal value.
Definition of get_size class.
Definition of index_type type.
Definition of is_eigen_vector class.
Definition of isfinite function.
Definition of iteration_logger class.
Definition of log_tag_view class.
std::ptrdiff_t index_type
Type of indices in this library.
Definition index_type.h:33
auto norm(const Matrix &matrix)
Calculate norm of a matrix.
Definition norm.h:39
auto get_size(const Matrix &matrix) -> index_type
Get the size.
Definition get_size.h:39
auto isfinite(const T &val) -> bool
Check whether a number is finite.
Definition isfinite.h:39
Namespace of optimization algorithms.
constexpr auto dividing_rectangles_tag
Tag of dividing_rectangles.
Namespace of utilities.
Definition assert.h:30
auto safe_cast(const From &value) -> To
Cast safely.
Definition safe_cast.h:54
constexpr bool is_eigen_vector_v
Get whether a type is a Eigen's vector.
STL namespace.
Definition of norm class.
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.
auto operator()(const rectangle &left, const rectangle &right) const -> bool
Compare rectangles.