diff --git a/tasks/global_search_strongin/common/include/common.hpp b/tasks/global_search_strongin/common/include/common.hpp new file mode 100644 index 0000000000..753c893b02 --- /dev/null +++ b/tasks/global_search_strongin/common/include/common.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +#include "task/include/task.hpp" + +namespace global_search_strongin { + +struct SamplePoint { + double x = 0.0; + double value = 0.0; +}; + +using InType = std::tuple>; +using OutType = double; +using BaseTask = ppc::task::Task; + +} // namespace global_search_strongin diff --git a/tasks/global_search_strongin/info.json b/tasks/global_search_strongin/info.json new file mode 100644 index 0000000000..02a89a3a70 --- /dev/null +++ b/tasks/global_search_strongin/info.json @@ -0,0 +1,9 @@ +{ + "student": { + "first_name": "Мария", + "last_name": "Шеленкова", + "middle_name": "Сергеевна", + "group_number": "3823Б1ФИ1", + "task_number": "3" + } +} diff --git a/tasks/global_search_strongin/mpi/include/ops_mpi.hpp b/tasks/global_search_strongin/mpi/include/ops_mpi.hpp new file mode 100644 index 0000000000..7cf76343be --- /dev/null +++ b/tasks/global_search_strongin/mpi/include/ops_mpi.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +#include "global_search_strongin/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace global_search_strongin { + +class StronginSearchMpi : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kMPI; + } + + explicit StronginSearchMpi(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + + [[nodiscard]] double ComputeGlobalSlope() const; + [[nodiscard]] std::pair IntervalRange(int intervals) const; + [[nodiscard]] std::pair EvaluateIntervals(int start, int end, double m) const; + bool TryInsertPoint(const InType &input, int best_index, double epsilon, double m, double left_bound, + double right_bound, int &insert_index, double &new_point, double &new_value); + static void BroadcastInsertionData(int &continue_flag, int &insert_index, double &new_point, double &new_value); + bool ProcessIteration(const InType &input, double epsilon, double left_bound, double right_bound); + + int rank_ = 0; + int world_size_ = 1; + std::vector points_; + double best_x_ = 0.0; + double best_value_ = 0.0; + int iterations_done_ = 0; +}; + +} // namespace global_search_strongin diff --git a/tasks/global_search_strongin/mpi/src/ops_mpi.cpp b/tasks/global_search_strongin/mpi/src/ops_mpi.cpp new file mode 100644 index 0000000000..b0a3f3ce48 --- /dev/null +++ b/tasks/global_search_strongin/mpi/src/ops_mpi.cpp @@ -0,0 +1,248 @@ +#include "global_search_strongin/mpi/include/ops_mpi.hpp" + +#include + +#include +#include +#include +#include +#include + +#include "global_search_strongin/common/include/common.hpp" + +namespace global_search_strongin { +namespace { + +constexpr double kReliability = 2.0; + +struct IntervalCharacteristic { + double value = 0.0; + int index = -1; +}; + +double Evaluate(const InType &input, double x) { + const auto &objective = std::get<4>(input); + return objective ? objective(x) : 0.0; +} + +bool Comparator(const SamplePoint &lhs, const SamplePoint &rhs) { + return lhs.x < rhs.x; +} + +} // namespace + +StronginSearchMpi::StronginSearchMpi(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = OutType{}; +} + +bool StronginSearchMpi::ValidationImpl() { + const auto &input = GetInput(); + const auto &objective = std::get<4>(input); + const double left = std::get<0>(input); + const double right = std::get<1>(input); + const double epsilon = std::get<2>(input); + const int max_iterations = std::get<3>(input); + + if (!objective) { + return false; + } + if (!(left < right)) { + return false; + } + if (epsilon <= 0.0) { + return false; + } + return max_iterations > 0; +} + +bool StronginSearchMpi::PreProcessingImpl() { + MPI_Comm_rank(MPI_COMM_WORLD, &rank_); + MPI_Comm_size(MPI_COMM_WORLD, &world_size_); + + points_.clear(); + const auto &input = GetInput(); + + const double left_bound = std::get<0>(input); + const double right_bound = std::get<1>(input); + const SamplePoint left{.x = left_bound, .value = Evaluate(input, left_bound)}; + const SamplePoint right{.x = right_bound, .value = Evaluate(input, right_bound)}; + points_.push_back(left); + points_.push_back(right); + if (!std::ranges::is_sorted(points_, Comparator)) { + std::ranges::sort(points_, Comparator); + } + best_x_ = left.value < right.value ? left.x : right.x; + best_value_ = std::min(left.value, right.value); + iterations_done_ = 0; + return true; +} + +bool StronginSearchMpi::RunImpl() { + const auto &input = GetInput(); + + const double left_bound = std::get<0>(input); + const double right_bound = std::get<1>(input); + const double epsilon = std::get<2>(input); + const int max_iterations = std::get<3>(input); + + while (iterations_done_ < max_iterations) { + if (!ProcessIteration(input, epsilon, left_bound, right_bound)) { + break; + } + ++iterations_done_; + } + + return true; +} + +bool StronginSearchMpi::PostProcessingImpl() { + if (rank_ == 0) { + GetOutput() = best_value_; + } + + MPI_Bcast(&best_value_, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); + if (rank_ != 0) { + GetOutput() = best_value_; + } + return true; +} + +double StronginSearchMpi::ComputeGlobalSlope() const { + const int intervals = static_cast(points_.size()) - 1; + double local_max_slope = 0.0; + for (int i = 0; i < intervals; ++i) { + const auto left_index = static_cast(i); + const auto &left = points_[left_index]; + const auto &right = points_[left_index + 1]; + const double delta = right.x - left.x; + if (delta <= 0.0) { + continue; + } + const double diff = right.value - left.value; + const double slope = std::fabs(diff) / delta; + local_max_slope = std::max(local_max_slope, slope); + } + + double global_max_slope = 0.0; + MPI_Allreduce(&local_max_slope, &global_max_slope, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); + return global_max_slope; +} + +std::pair StronginSearchMpi::IntervalRange(int intervals) const { + const int base = intervals / world_size_; + const int remainder = intervals % world_size_; + const int start = (base * rank_) + std::min(rank_, remainder); + const int count = base + (rank_ < remainder ? 1 : 0); + return {start, start + count}; +} + +std::pair StronginSearchMpi::EvaluateIntervals(int start, int end, double m) const { + double local_best_value = -std::numeric_limits::infinity(); + int local_best_index = -1; + for (int idx = start; idx < end; ++idx) { + const auto left_index = static_cast(idx); + const auto &left = points_[left_index]; + const auto &right = points_[left_index + 1]; + const double delta = right.x - left.x; + if (delta <= 0.0) { + continue; + } + const double diff = right.value - left.value; + const double candidate = (m * delta) + ((diff * diff) / (m * delta)) - (2.0 * (right.value + left.value)); + if (candidate > local_best_value) { + local_best_value = candidate; + local_best_index = idx; + } + } + + IntervalCharacteristic local{.value = local_best_value, .index = local_best_index}; + IntervalCharacteristic global{}; + MPI_Allreduce(&local, &global, 1, MPI_DOUBLE_INT, MPI_MAXLOC, MPI_COMM_WORLD); + return {global.value, global.index}; +} + +bool StronginSearchMpi::TryInsertPoint(const InType &input, int best_index, double epsilon, double m, double left_bound, + double right_bound, int &insert_index, double &new_point, double &new_value) { + const auto left_index = static_cast(best_index); + const auto &left = points_[left_index]; + const auto &right = points_[left_index + 1]; + const double interval_length = right.x - left.x; + if (interval_length < epsilon) { + return false; + } + + new_point = (0.5 * (left.x + right.x)) - ((right.value - left.value) / (2.0 * m)); + new_point = std::clamp(new_point, left_bound, right_bound); + const bool already_used = std::ranges::any_of(points_, [new_point](const SamplePoint &point) { + return std::fabs(point.x - new_point) < std::numeric_limits::epsilon(); + }); + if (already_used) { + return false; + } + + new_value = Evaluate(input, new_point); + if (new_value < best_value_) { + best_value_ = new_value; + best_x_ = new_point; + } + auto insert_it = std::ranges::upper_bound(points_, SamplePoint{.x = new_point, .value = new_value}, Comparator); + insert_index = static_cast(insert_it - points_.begin()); + points_.insert(insert_it, SamplePoint{.x = new_point, .value = new_value}); + return true; +} + +void StronginSearchMpi::BroadcastInsertionData(int &continue_flag, int &insert_index, double &new_point, + double &new_value) { + MPI_Bcast(&continue_flag, 1, MPI_INT, 0, MPI_COMM_WORLD); + if (continue_flag == 0) { + return; + } + MPI_Bcast(&insert_index, 1, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Bcast(&new_point, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); + MPI_Bcast(&new_value, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); +} + +bool StronginSearchMpi::ProcessIteration(const InType &input, double epsilon, double left_bound, double right_bound) { + if (points_.size() < 2) { + return false; + } + const int intervals = static_cast(points_.size()) - 1; + const double global_max_slope = ComputeGlobalSlope(); + const double m = global_max_slope > 0.0 ? kReliability * global_max_slope : 1.0; + const auto [start, end] = IntervalRange(intervals); + const auto interval_selection = EvaluateIntervals(start, end, m); + const int best_index = interval_selection.second; + if (best_index < 0 || best_index >= intervals) { + return false; + } + + int insert_index = 0; + double new_point = 0.0; + double new_value = 0.0; + int continue_flag = 0; + if (rank_ == 0) { + continue_flag = + TryInsertPoint(input, best_index, epsilon, m, left_bound, right_bound, insert_index, new_point, new_value) ? 1 + : 0; + } + + BroadcastInsertionData(continue_flag, insert_index, new_point, new_value); + if (continue_flag == 0) { + return false; + } + + if (rank_ != 0) { + points_.insert(points_.begin() + insert_index, SamplePoint{.x = new_point, .value = new_value}); + if (new_value < best_value_) { + best_value_ = new_value; + best_x_ = new_point; + } + } + + MPI_Bcast(&best_value_, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); + return true; +} + +} // namespace global_search_strongin diff --git a/tasks/global_search_strongin/report.md b/tasks/global_search_strongin/report.md new file mode 100644 index 0000000000..537b35fdbb --- /dev/null +++ b/tasks/global_search_strongin/report.md @@ -0,0 +1,83 @@ +# Отчет по лабораторной работе №3. +## "Алгоритм глобального поиска (Стронгин)" + +**Студент:** Шеленкова Мария Сергеевна +**Группа:** 3823Б1ФИ1 +**Вариант:** 11 + +### 1. Введение +**Мотивация.** Задачи глобальной оптимизации встречаются в инженерных расчётах и моделировании. Анализ алгоритма Стронгина с параллельной поддержкой позволяет оценить возможности масштабирования. +**Проблематика.** Большая доля коммуникаций может нивелировать преимущества распараллеливания, особенно при дешёвой функции отклика. +**Ожидаемый результат.** При лёгких функциях значимого ускорения не планировалось; цель эксперимента — подтвердить корректность параллельной схемы и оценить характер накладных расходов. + +### 2. Постановка задачи +**Задано:** отрезок `[left, right]`, параметр точности `epsilon`, ограничение `max_iterations` и однопараметрическая функция. +**Требуется:** найти значение глобального минимума функции на заданном отрезке. +**Входные данные:** `InType = tuple`. +**Выходные данные:** `OutType = double` — минимальное найденное значение функции. + +### 3. Последовательный алгоритм +1. Инициализация: берутся две точки на концах отрезка `left` и `right`, вычисляются значения функции, список точек сортируется по `x`. +2. На каждой итерации оценивается «крутизна» на текущем наборе точек: + * вычисляется максимальный локальный наклон + \( M = \max_i \frac{|f(x_i)-f(x_{i-1})|}{x_i-x_{i-1}} \); + * вводится параметр \( m = r\cdot M \) (здесь `r = 2.0`), а если \(M = 0\), то берётся \(m = 1\). +3. Для каждого интервала \([x_{i-1}, x_i]\) считается характеристика Стронгина: + + $$R_i = m\Delta_i + \frac{(f_i - f_{i-1})^2}{m\Delta_i} - 2(f_i + f_{i-1}),\quad \Delta_i = x_i - x_{i-1}$$ + + Выбирается интервал с максимальным \(R_i\). +4. В выбранный интервал вставляется новая точка + + $$x_{new} = \frac{x_{i-1}+x_i}{2} - \frac{f_i - f_{i-1}}{2m}$$ + + с защитами от выхода за границы и от попадания слишком близко к концам интервала. +5. Остановка: если длина выбранного интервала меньше `epsilon`, либо новую точку нельзя корректно вставить (дубликат/вне интервала), либо достигнут `max_iterations`. +6. Минимум (`best_point`, `best_value`) обновляется по мере добавления точек. + +### 4. Схема распараллеливания +**Декомпозиция данных.** Интервалы между соседними точками распределяются между процессами (каждый процесс рассматривает свой диапазон индексов интервалов). На каждом процессе вычисляются характеристики Стронгина для локальных интервалов. + +**Коммуникация.** Лучший интервал выбирается глобально через коллективную операцию (редукция по максимуму с «привязкой» к индексу/владельцу). После этого вычисляется одна новая точка для выбранного интервала и рассылается всем процессам (параметры вставки и/или обновлённый минимум). + +В реализации выбор лучшего интервала выполнен канонично через `MPI_Allreduce` с типом `MPI_DOUBLE_INT` и оператором `MPI_MAXLOC` (сравнение по характеристике с переносом индекса). + +**Синхронизация.** На каждой итерации выполняются глобальная редукция для выбора интервала и широковещательная рассылка данных о вставке, после чего все процессы синхронно добавляют одинаковую новую точку в свой локальный список. + +### 5. Экспериментальная установка +* Процессор: 13th Gen Intel(R) Core(TM) i9-13980HX (24 физических ядра, 32 логических потока). +* Оперативная память: 32 GB. +* Операционная система: Microsoft Windows 11 Pro. +* Компилятор: MSVC 19.40, конфигурация Release x64. +* MPI-библиотека: Microsoft MPI 10.1.12498.52. + +Для воспроизводимости параметры тестов и результаты замеров приведены в этом отчёте. + +### 6. Результаты и обсуждение +#### 6.1. Корректность +Функциональные тесты запускались командой `ppc_func_tests.exe --gtest_filter=*global_search_strongin*`. Проверка включала несколько функций с известными минимумами; последовательная и MPI-реализации дают совпадающие результаты в пределах допуска. + +#### 6.2. Производительность +Замеры выполнялись утилитой `ppc_perf_tests.exe` в конфигурации Release. + +Команды запуска: +* SEQ: `ppc_perf_tests.exe --gtest_filter=*global_search_strongin_seq_enabled*` +* MPI (N процессов): `mpiexec -n N ppc_perf_tests.exe --gtest_filter=*global_search_strongin_mpi_enabled*` (проверено для N=1,2,4,8) + +| Реализация | Процессов | Режим теста | Время, сек | +|-----------:|----------:|-------------|-----------:| +| seq | 1 | pipeline | 0.33886070 | +| seq | 1 | task_run | 0.06682144 | +| mpi | 1 | pipeline | 0.35951032 | +| mpi | 1 | task_run | 0.06921308 | +| mpi | 2 | pipeline | 0.35693232 | +| mpi | 2 | task_run | 0.07633388 | +| mpi | 4 | pipeline | 0.39469252 | +| mpi | 4 | task_run | 0.08500110 | +| mpi | 8 | pipeline | 0.65025518 | +| mpi | 8 | task_run | 0.12715020 | + +Выполненные измерения показывают, что даже при умеренно утяжелённой функции накладные расходы MPI могут доминировать и не давать ускорения на малом числе итераций. + +### 7. Выводы +Алгоритм обеспечивает корректный поиск глобального минимума и демонстрирует ожидаемое поведение производительности. Параллельная схема пригодна для задач с высокой стоимостью вычисления функции, тогда как на лёгких функциях рациональнее использовать последовательную реализацию. diff --git a/tasks/global_search_strongin/seq/include/ops_seq.hpp b/tasks/global_search_strongin/seq/include/ops_seq.hpp new file mode 100644 index 0000000000..75af924ce2 --- /dev/null +++ b/tasks/global_search_strongin/seq/include/ops_seq.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include + +#include "global_search_strongin/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace global_search_strongin { + +class StronginSearchSeq : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kSEQ; + } + + explicit StronginSearchSeq(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + + [[nodiscard]] double ComputeMaxSlope() const; + [[nodiscard]] std::optional SelectInterval(double m) const; + bool InsertPoint(const InType &input, std::size_t interval_index, double epsilon, double m, double left_bound, + double right_bound); + + std::vector points_; + double best_x_ = 0.0; + double best_value_ = 0.0; + int iterations_done_ = 0; +}; + +} // namespace global_search_strongin diff --git a/tasks/global_search_strongin/seq/src/ops_seq.cpp b/tasks/global_search_strongin/seq/src/ops_seq.cpp new file mode 100644 index 0000000000..841b3659b3 --- /dev/null +++ b/tasks/global_search_strongin/seq/src/ops_seq.cpp @@ -0,0 +1,201 @@ +#include "global_search_strongin/seq/include/ops_seq.hpp" + +#include +#include +#include +#include +#include + +#include "global_search_strongin/common/include/common.hpp" + +namespace global_search_strongin { +namespace { + +constexpr double kNearEps = 1e-12; +constexpr double kReliability = 2.0; + +double Evaluate(const InType &input, double x) { + const auto &objective = std::get<4>(input); + return objective ? objective(x) : 0.0; +} + +bool Comparator(const SamplePoint &lhs, const SamplePoint &rhs) { + return lhs.x < rhs.x; +} + +} // namespace + +StronginSearchSeq::StronginSearchSeq(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = OutType{}; +} + +bool StronginSearchSeq::ValidationImpl() { + const auto &input = GetInput(); + const auto &objective = std::get<4>(input); + const double left = std::get<0>(input); + const double right = std::get<1>(input); + const double epsilon = std::get<2>(input); + const int max_iterations = std::get<3>(input); + + if (!objective) { + return false; + } + if (!(left < right)) { + return false; + } + if (epsilon <= 0.0) { + return false; + } + return max_iterations > 0; +} + +bool StronginSearchSeq::PreProcessingImpl() { + points_.clear(); + const auto &input = GetInput(); + + const double left_bound = std::get<0>(input); + const double right_bound = std::get<1>(input); + + const SamplePoint left{.x = left_bound, .value = Evaluate(input, left_bound)}; + const SamplePoint right{.x = right_bound, .value = Evaluate(input, right_bound)}; + points_.push_back(left); + points_.push_back(right); + if (!std::ranges::is_sorted(points_, Comparator)) { + std::ranges::sort(points_, Comparator); + } + + best_x_ = left.value < right.value ? left.x : right.x; + best_value_ = std::min(left.value, right.value); + iterations_done_ = 0; + return true; +} + +bool StronginSearchSeq::RunImpl() { + const auto &input = GetInput(); + + const double left_bound = std::get<0>(input); + const double right_bound = std::get<1>(input); + const double epsilon = std::get<2>(input); + const int max_iterations = std::get<3>(input); + + while (iterations_done_ < max_iterations) { + if (points_.size() < 2) { + break; + } + + const double max_slope = ComputeMaxSlope(); + const double m = max_slope > 0.0 ? kReliability * max_slope : 1.0; + const auto interval_index = SelectInterval(m); + if (!interval_index.has_value()) { + break; + } + + if (!InsertPoint(input, *interval_index, epsilon, m, left_bound, right_bound)) { + break; + } + + ++iterations_done_; + } + + return true; +} + +bool StronginSearchSeq::PostProcessingImpl() { + GetOutput() = best_value_; + return true; +} + +double StronginSearchSeq::ComputeMaxSlope() const { + if (points_.size() < 2) { + return 0.0; + } + double max_slope = 0.0; + for (std::size_t i = 1; i < points_.size(); ++i) { + const auto &left = points_[i - 1]; + const auto &right = points_[i]; + const double delta = right.x - left.x; + if (delta <= 0.0) { + continue; + } + const double slope = std::fabs(right.value - left.value) / delta; + max_slope = std::max(max_slope, slope); + } + return max_slope; +} + +std::optional StronginSearchSeq::SelectInterval(double m) const { + if (points_.size() < 2) { + return std::nullopt; + } + + double best_characteristic = -std::numeric_limits::infinity(); + std::optional best_index = std::nullopt; + + for (std::size_t i = 1; i < points_.size(); ++i) { + const auto &left = points_[i - 1]; + const auto &right = points_[i]; + const double delta = right.x - left.x; + if (delta <= 0.0) { + continue; + } + + const double diff = right.value - left.value; + const double characteristic = (m * delta) + ((diff * diff) / (m * delta)) - (2.0 * (right.value + left.value)); + + if (characteristic > best_characteristic) { + best_characteristic = characteristic; + best_index = i; + } + } + + if (!std::isfinite(best_characteristic)) { + return std::nullopt; + } + return best_index; +} + +bool StronginSearchSeq::InsertPoint(const InType &input, std::size_t interval_index, double epsilon, double m, + double left_bound, double right_bound) { + if (interval_index == 0 || interval_index >= points_.size()) { + return false; + } + + const SamplePoint &left = points_[interval_index - 1]; + const SamplePoint &right = points_[interval_index]; + const double interval_length = right.x - left.x; + if (interval_length < epsilon) { + return false; + } + + double x_new = (0.5 * (left.x + right.x)) - ((right.value - left.value) / (2.0 * m)); + x_new = std::clamp(x_new, left_bound, right_bound); + + if (x_new <= left.x + kNearEps || x_new >= right.x - kNearEps) { + x_new = 0.5 * (left.x + right.x); + } + + if (x_new <= left_bound || x_new >= right_bound) { + return false; + } + + const bool already_used = + std::ranges::any_of(points_, [x_new](const SamplePoint &p) { return std::fabs(p.x - x_new) < kNearEps; }); + if (already_used) { + return false; + } + + const double value = Evaluate(input, x_new); + if (value < best_value_) { + best_value_ = value; + best_x_ = x_new; + } + + SamplePoint new_point{.x = x_new, .value = value}; + auto insert_it = std::ranges::upper_bound(points_, new_point, Comparator); + points_.insert(insert_it, new_point); + return true; +} + +} // namespace global_search_strongin diff --git a/tasks/global_search_strongin/settings.json b/tasks/global_search_strongin/settings.json new file mode 100644 index 0000000000..b1a0d52574 --- /dev/null +++ b/tasks/global_search_strongin/settings.json @@ -0,0 +1,7 @@ +{ + "tasks_type": "processes", + "tasks": { + "mpi": "enabled", + "seq": "enabled" + } +} diff --git a/tasks/global_search_strongin/tests/functional/main.cpp b/tasks/global_search_strongin/tests/functional/main.cpp new file mode 100644 index 0000000000..37df54b940 --- /dev/null +++ b/tasks/global_search_strongin/tests/functional/main.cpp @@ -0,0 +1,84 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "global_search_strongin/common/include/common.hpp" +#include "global_search_strongin/mpi/include/ops_mpi.hpp" +#include "global_search_strongin/seq/include/ops_seq.hpp" +#include "util/include/func_test_util.hpp" +#include "util/include/util.hpp" + +namespace global_search_strongin { + +using TestType = std::tuple, double>; + +class StronginFuncTests : public ppc::util::BaseRunFuncTests { + public: + static std::string PrintTestParam(const TestType &test_param) { + std::string result = "test_" + std::to_string(std::get<0>(test_param)) + "_from_" + + std::to_string(std::get<1>(test_param)) + "_to_" + std::to_string(std::get<2>(test_param)) + + "_epsilon_" + std::to_string(std::get<3>(test_param)) + "_max_iters_" + + std::to_string(std::get<4>(test_param)); + std::ranges::replace(result, '.', '_'); + std::ranges::replace(result, '-', 'm'); + return result; + } + + protected: + void SetUp() override { + TestType params = std::get(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); + + const double left = std::get<1>(params); + const double right = std::get<2>(params); + const double epsilon = std::get<3>(params); + const int max_iters = std::get<4>(params); + const auto function = std::get<5>(params); + + expected_result = std::get<6>(params); + input = std::make_tuple(left, right, epsilon, max_iters, function); + } + + InType GetTestInputData() final { + return input; + } + + bool CheckTestOutputData(OutType &output_data) final { + constexpr double kTolerance = 1e-2; + return std::abs(output_data - expected_result) <= kTolerance; + } + + InType input; + OutType expected_result{0.0}; +}; + +namespace { + +const std::array kTestParams = { + std::make_tuple(1, -5.0, 5.0, 0.1, 500, [](double x) { return x * x; }, 0.0), + std::make_tuple(2, 2.0, 14.0, 0.1, 500, [](double x) { return (x - 2) * (x - 2); }, 0.0), + std::make_tuple(3, 0.0, 8.0, 0.1, 1000, [](double x) { return std::sin(x); }, -1.0), +}; + +const auto kTaskList = + std::tuple_cat(ppc::util::AddFuncTask(kTestParams, PPC_SETTINGS_global_search_strongin), + ppc::util::AddFuncTask(kTestParams, PPC_SETTINGS_global_search_strongin)); + +const auto kGTestValues = ppc::util::ExpandToValues(kTaskList); + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables,modernize-type-traits) +INSTANTIATE_TEST_SUITE_P(Strongin, StronginFuncTests, kGTestValues, + StronginFuncTests::PrintFuncTestName); + +TEST_P(StronginFuncTests, Runs) { + ExecuteTest(GetParam()); +} + +} // namespace + +} // namespace global_search_strongin diff --git a/tasks/global_search_strongin/tests/performance/main.cpp b/tasks/global_search_strongin/tests/performance/main.cpp new file mode 100644 index 0000000000..8a5ae59826 --- /dev/null +++ b/tasks/global_search_strongin/tests/performance/main.cpp @@ -0,0 +1,59 @@ +#include + +#include +#include + +#include "global_search_strongin/common/include/common.hpp" +#include "global_search_strongin/mpi/include/ops_mpi.hpp" +#include "global_search_strongin/seq/include/ops_seq.hpp" +#include "util/include/perf_test_util.hpp" + +namespace global_search_strongin { + +class StronginPerfTests : public ppc::util::BaseRunPerfTests { + protected: + void SetUp() override { + const double left = -5.0; + const double right = 5.0; + const double epsilon = 1e-4; + const int max_iters = 40000; + input_ = std::make_tuple(left, right, epsilon, max_iters, Function); + expected_result_ = -6.1; + } + + InType GetTestInputData() final { + return input_; + } + + bool CheckTestOutputData(OutType &output_data) final { + constexpr double kTolerance = 1e-2; + return std::abs(output_data - expected_result_) <= kTolerance; + } + + private: + InType input_; + OutType expected_result_{0.0}; + + static double Function(double x) { + return (0.002 * x * x) + (5.0 * std::sin(30.0 * x)) + std::sin(200.0 * std::sin(50.0 * x)) + + (0.1 * std::cos(300.0 * x)); + } +}; + +namespace { + +const auto kPerfTasks = + ppc::util::MakeAllPerfTasks(PPC_SETTINGS_global_search_strongin); + +const auto kGTestValues = ppc::util::TupleToGTestValues(kPerfTasks); + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables,modernize-type-traits) +INSTANTIATE_TEST_SUITE_P(StronginPerf, StronginPerfTests, kGTestValues, StronginPerfTests::CustomPerfTestName); + +TEST_P(StronginPerfTests, Runs) { + ExecuteTest(GetParam()); +} + +} // namespace + +} // namespace global_search_strongin