diff --git a/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/common/include/common.hpp b/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/common/include/common.hpp new file mode 100644 index 0000000000..b8b5f1e345 --- /dev/null +++ b/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/common/include/common.hpp @@ -0,0 +1,22 @@ +#pragma once +#include +#include + +#include "task/include/task.hpp" + +namespace popova_e_global_optimization_parallelization_by_dividing_the_search_area { + +struct OptimizationInput { + double x_min; + double x_max; + double y_min; + double y_max; + double step; +}; + +using InType = OptimizationInput; +using OutType = std::tuple; +using TestType = std::tuple; +using BaseTask = ppc::task::Task; + +} // namespace popova_e_global_optimization_parallelization_by_dividing_the_search_area diff --git a/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/info.json b/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/info.json new file mode 100644 index 0000000000..8a89e78c16 --- /dev/null +++ b/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/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/popova_e_global_optimization_parallelization_by_dividing_the_search_area/mpi/include/ops_mpi.hpp b/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/mpi/include/ops_mpi.hpp new file mode 100644 index 0000000000..5e1c73ba0c --- /dev/null +++ b/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/mpi/include/ops_mpi.hpp @@ -0,0 +1,24 @@ +#pragma once +#include "popova_e_global_optimization_parallelization_by_dividing_the_search_area/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace popova_e_global_optimization_parallelization_by_dividing_the_search_area { + +class PopovaEGlobalOptimizationMPI : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kMPI; + } + + explicit PopovaEGlobalOptimizationMPI(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + + static double FunctionToOptimize(double px, double py); +}; + +} // namespace popova_e_global_optimization_parallelization_by_dividing_the_search_area diff --git a/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/mpi/src/ops_mpi.cpp b/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/mpi/src/ops_mpi.cpp new file mode 100644 index 0000000000..01f46c90ef --- /dev/null +++ b/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/mpi/src/ops_mpi.cpp @@ -0,0 +1,196 @@ +#include "popova_e_global_optimization_parallelization_by_dividing_the_search_area/mpi/include/ops_mpi.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "popova_e_global_optimization_parallelization_by_dividing_the_search_area/common/include/common.hpp" + +namespace popova_e_global_optimization_parallelization_by_dividing_the_search_area { + +namespace { + +struct XRange { + double start{}; + double end{}; + bool has_work{}; +}; + +struct LocalBest { + double f{std::numeric_limits::max()}; + double x{}; + double y{}; +}; + +double SafeStep(double step) { + return std::max(step, std::numeric_limits::epsilon()); +} + +double FunctionToOptimizeImpl(double px, double py) { + return ((px - 2.0) * (px - 2.0)) + ((py - 3.0) * (py - 3.0)); +} + +XRange ComputeXRange(int rank, int world_size, double x_min, double x_max, double step) { + const double safe_step = SafeStep(step); + const double total_x_range = x_max - x_min; + + if (total_x_range < safe_step / 2.0) { + if (rank == 0) { + return XRange{.start = x_min, .end = x_max, .has_work = true}; + } + return XRange{.start = x_min, .end = x_min, .has_work = false}; + } + + const std::size_t total_points = static_cast(std::floor((total_x_range / safe_step) + 0.5)) + 1U; + if (total_points == 0U) { + return XRange{.start = x_min, .end = x_min, .has_work = false}; + } + + const auto world = static_cast(world_size); + const auto r = static_cast(rank); + const std::size_t base_points = total_points / world; + const std::size_t remainder = total_points % world; + + std::size_t my_points = base_points; + if (r < remainder) { + my_points += 1U; + } + + std::size_t prefix_points = r * base_points; + if (r < remainder) { + prefix_points += r; + } else { + prefix_points += remainder; + } + + if (my_points == 0U) { + return XRange{.start = x_min, .end = x_min, .has_work = false}; + } + + const double x_start = x_min + (static_cast(prefix_points) * safe_step); + double x_end = x_min + (static_cast(prefix_points + my_points - 1U) * safe_step); + x_end = std::min(x_end, x_max); + + return XRange{.start = x_start, .end = x_end, .has_work = true}; +} + +LocalBest ParallelSearch(int rank, int world_size, double x_min, double x_max, double y_min, double y_max, + double step) { + const double safe_step = SafeStep(step); + + const XRange xr = ComputeXRange(rank, world_size, x_min, x_max, step); + if (!xr.has_work) { + return LocalBest{.f = std::numeric_limits::max(), .x = x_min, .y = y_min}; + } + + LocalBest best{.f = std::numeric_limits::max(), .x = x_min, .y = y_min}; + + const auto num_x = static_cast(std::floor(((xr.end - xr.start) / safe_step) + 1.5)); + const auto num_y = static_cast(std::floor(((y_max - y_min) / safe_step) + 1.5)); + + for (std::size_t ix = 0; ix < num_x; ++ix) { + double px = xr.start + (static_cast(ix) * safe_step); + px = std::min(px, xr.end); + for (std::size_t iy = 0; iy < num_y; ++iy) { + double py = y_min + (static_cast(iy) * safe_step); + py = std::min(py, y_max); + const double fval = FunctionToOptimizeImpl(px, py); + if (fval < best.f) { + best.f = fval; + best.x = px; + best.y = py; + } + } + } + + return best; +} + +std::array ReduceGlobalMin(int rank, int world_size, const LocalBest &local_best) { + std::array local_data = {local_best.f, local_best.x, local_best.y}; + + std::vector all_data; + if (rank == 0) { + all_data.resize(static_cast(world_size) * 3U); + } + + MPI_Gather(local_data.data(), 3, MPI_DOUBLE, all_data.data(), 3, MPI_DOUBLE, 0, MPI_COMM_WORLD); + + std::array global_out = local_data; + if (rank == 0) { + global_out[0] = all_data[0]; + global_out[1] = all_data[1]; + global_out[2] = all_data[2]; + + for (int pi = 1; pi < world_size; ++pi) { + const auto idx = static_cast(pi) * 3U; + if (all_data[idx] < global_out[0]) { + global_out[0] = all_data[idx]; + global_out[1] = all_data[idx + 1U]; + global_out[2] = all_data[idx + 2U]; + } + } + } + + MPI_Bcast(global_out.data(), 3, MPI_DOUBLE, 0, MPI_COMM_WORLD); + return global_out; +} + +} // namespace + +PopovaEGlobalOptimizationMPI::PopovaEGlobalOptimizationMPI(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = std::make_tuple(0.0, 0.0, std::numeric_limits::max()); +} + +bool PopovaEGlobalOptimizationMPI::ValidationImpl() { + const auto &in = GetInput(); + return (in.x_max > in.x_min) && (in.y_max > in.y_min) && (in.step > 0); +} + +bool PopovaEGlobalOptimizationMPI::PreProcessingImpl() { + return true; +} + +double PopovaEGlobalOptimizationMPI::FunctionToOptimize(double px, double py) { + return FunctionToOptimizeImpl(px, py); +} + +bool PopovaEGlobalOptimizationMPI::RunImpl() { + const auto &in = GetInput(); + + int rank = 0; + int world_size = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &world_size); + + const LocalBest local_coarse = ParallelSearch(rank, world_size, in.x_min, in.x_max, in.y_min, in.y_max, in.step); + const std::array global_coarse = ReduceGlobalMin(rank, world_size, local_coarse); + + const double refine_step = std::max(in.step / 2.0, std::numeric_limits::epsilon()); + const double x_ref_min = std::max(in.x_min, global_coarse[1] - in.step); + const double x_ref_max = std::min(in.x_max, global_coarse[1] + in.step); + const double y_ref_min = std::max(in.y_min, global_coarse[2] - in.step); + const double y_ref_max = std::min(in.y_max, global_coarse[2] + in.step); + + const LocalBest local_refined = + ParallelSearch(rank, world_size, x_ref_min, x_ref_max, y_ref_min, y_ref_max, refine_step); + const std::array global_refined = ReduceGlobalMin(rank, world_size, local_refined); + + GetOutput() = std::make_tuple(global_refined[1], global_refined[2], global_refined[0]); + + return true; +} + +bool PopovaEGlobalOptimizationMPI::PostProcessingImpl() { + return true; +} + +} // namespace popova_e_global_optimization_parallelization_by_dividing_the_search_area diff --git a/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/report.md b/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/report.md new file mode 100644 index 0000000000..1098b78296 --- /dev/null +++ b/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/report.md @@ -0,0 +1,175 @@ +# Многошаговая схема решения двумерных задач глобальной оптимизации. Распараллеливание путём разделения области поиска + +- **Студент**: Попова Елизавета Сергеевна, группа 3823Б1ПР1 +- **Технология**: SEQ | MPI +- **Вариант**: 12 + +## 1. Введение + +Задачи глобальной оптимизации встречаются в инженерных расчётах, обработке сигналов, машинном обучении и во многих прикладных областях. В данной работе рассматривается функция двух переменных f(x, y). Требуется найти такие значения `x` и `y`, при которых функция принимает минимум на заданной области поиска D. То есть ищем точку `(x*, y*)` в области D, где функция `f(x, y)` принимает минимальное значение. + +Обозначения: +- `f(x, y)` — оптимизируемая функция; +- `D` — область поиска (в нашем случае — прямоугольник); +- `(x*, y*)` — точка минимума на области D ("оптимальное значение"); +- `f_min` — минимальное найденное значение функции. + +Цель работы — реализовать многошаговый поиск минимума на прямоугольной области и распараллелить вычисления с помощью технологии MPI путём разделения области поиска между процессами. + +## 2. Постановка задачи + +Задача: реализовать последовательную (SEQ) и параллельную (MPI) версию схему решения двумерных задач глобальной оптимизации. + +### 2.1 Входные данные + +Входные данные — границы области оптимизации и шаг сетки, заданы в виде структуры оптимизации: +```cpp +struct OptimizationInput { + double x_min; // минимальная граница по оси X + double x_max; // максимальная граница по оси X + double y_min; // минимальная граница по оси Y + double y_max; // максимальная граница по оси Y + double step; // шаг сетки для дискретизации области поиска +}; +using InType = OptimizationInput; +``` +Где: +- `x_min`, `x_max` — определяют диапазон поиска по оси X (прямоугольная область поиска) +- `y_min`, `y_max` — определяют диапазон поиска по оси Y +- `step` — шаг, с которым строится сетка точек для перебора + +Ограничения входных данных: +- `x_max > x_min` — максимальная граница X должна быть больше минимальной +- `y_max > y_min` — максимальная граница Y должна быть больше минимальной +- `step > 0` — шаг должен быть положительным числом + +### 2.2 Выходные данные +Выходные данные — кортеж из найденных координат точки минимума и значения функции в этой точке: +```cpp +using OutType = std::tuple; // (x_best, y_best, f_min) +``` +Где: +- `x_best` — координата X найденной точки минимума +- `y_best` — координата Y найденной точки минимума +- `f_min` — минимальное значение функции f(x_best, y_best) на заданной области поиска + +Используется тестовая функция `f(x, y) = (x - 2)^2 + (y - 3)^2`. Для справки: её глобальный минимум на всей плоскости достигается в точке (2, 3), f_min = 0. + +## 3. Базовый алгоритм (последовательная версия) + +Последовательная версия схемы состоит из 4-х этапов: +1. **`ValidationImpl()`**: проверка корректности входных данных; +2. **`PreProcessingImpl()`**: подготовка; +3. **`RunImpl()`**: вычисление минимума: + - выбирается «безопасный» шаг `h = max(step, epsilon)`, чтобы исключить деление на 0 и слишком маленькие шаги; + - строится равномерная сетка по `X` и `Y` с шагом `h`; + - выполняется полный перебор сетки (coarse), выбирается точка с минимальным значением; + - выполняется уточнение (refine) в окрестности найденной точки: + - шаг уменьшается: `h_ref = max(step/2, epsilon)`; + - область сужается до `[x* - step, x* + step]` по `X` и `[y* - step, y* + step]` по `Y`; + - границы уточнённой области ограничиваются исходными границами D, чтобы не выйти за область поиска +4. **`PostProcessingImpl()`**: завершающий этап. + + +## 4. Схема распараллеливания (MPI) + +Распараллеливание происходит на самом тяжелом этапe — перебор точек сетки. +### 4.1 Разбиение области поиска +Используется разбиение области по оси X: +- Вся область `[x_min, x_max]` дискретизуется в набор точек с шагом `step`; +- Точки X делятся между процессами на непрерывные отрезки' +- По оси Y каждый процесс проходит весь диапазон `[y_min, y_max]`. + +Если точек по X меньше, чем процессов, часть процессов получает `has_work=false` и возвращает нейтральный результат (минимум = `max()`). + + +### 4.2 Локальный поиск и глобальное сведение результата + +На каждом процессе: +- выполняется локальный поиск минимума на своей части X; +- результат локального минимума хранится как `(f, x, y)`. + +Упрощённый алгоритм локального поиска: +```cpp +local_best_f = +inf +local_best_x = x_min +local_best_y = y_min + +for (x по своей части X) { + for (y по всей сетке Y) { + f = f(x, y) + if (f < local_best_f) { + local_best_f = f + local_best_x = x + local_best_y = y + } + } +} +``` +Особенности реализации: +- `MPI_Gather` собирает локальные минимумы на `rank=0`; +- корневой процесс выбирает лучший (минимальный `f`); +- `MPI_Bcast` рассылает найденный глобальный минимум всем процессам. + + +## 5. Детали реализации +Структура проекта + +| Файл | Суть | +| ---------------------- | ------------------------------ | +| `common.hpp` | Общее определение типов данных | +| `ops_seq.hpp/.cpp` | Последовательная реализация | +| `ops_mpi.hpp/.cpp` | MPI-реализация | +| `functional/main.cpp` | Функциональные тесты | +| `performance/main.cpp` | Тесты на производительность | + + +Разбиение диапазона реализовано в `ComputeXRange(...)`: +- вычисляется число точек по X; +- распределяются точки по процессам; +- для каждого ранга вычисляется префикс (сколько точек до него) и свой диапазон [start, end]. + +## 6. Экспериментальная среда + +| Параметр | Значение | +| ---------- | ------------------------------------------------ | +| CPU | Intel Core i3-6006U (2 ядра / 4 потока, 2.0 ГГц) | +| RAM | 8 GB | +| ОС | Windows 10 64-bit | +| Компилятор | MSVC (Visual Studio Build Tools) | +| MPI | Microsoft MPI 10.1 (mpiexec 10.1.12498.52) | + +## 7. Результаты и обсуждение + +### 7.1 Корректность + +Функциональные тесты проверяют, что: +- найденная точка близка к (2, 3) с погрешностью по координатам порядка `max(1e-6, step)`; +- значение функции близко к ожидаемому с погрешностью порядка `max(1e-6, step^2 * 2)`. + +Набор тестов включает разные шаги и области — 6 кейсов. + +### 7.2 Производительность + +Производительные тесты используют область X в диапазоне [0, 4], Y в диапазоне [0, 6], `step = 0.01`. + +| Mode | Count | Time, s | Speedup | Efficiency | +| ---- | ----- | ------- | ------- | ---------- | +| SEQ | 1 | 0.00091 | 1 | N/A | +| MPI | 2 | 0.00061 | 1.49 | 74.5% | +| MPI | 4 | 0.00032 | 2.93 | 73.0% | + + +## 8. Заключение + +В ходе работы: +- Реализован многошаговый сеточный поиск глобального минимума двумерной функции; +- Реализована MPI-версия с разбиением области поиска по оси X; +- Обеспечено сведение локальных минимумов в глобальный результат; +- Проведено тестирование корректности для различных диапазонов поиска; +- Рассчитаны характеристики ускорения и эффективности для распараллеленого алгоритма. + +## 9. Источники +1. Сысоев А. В., Лекции по курсу «Параллельное программирование для кластерных систем». +2. Документация по курсу «Параллельное программирование», URL: https://learning-process.github.io/parallel_programming_course/ru/index.html +3. Репозиторий курса «Параллельное программирование», URL: https://github.com/learning-process/ppc-2025-processes-engineers \ No newline at end of file diff --git a/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/seq/include/ops_seq.hpp b/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/seq/include/ops_seq.hpp new file mode 100644 index 0000000000..ea0502e122 --- /dev/null +++ b/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/seq/include/ops_seq.hpp @@ -0,0 +1,24 @@ +#pragma once +#include "popova_e_global_optimization_parallelization_by_dividing_the_search_area/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace popova_e_global_optimization_parallelization_by_dividing_the_search_area { + +class PopovaEOptimisationSEQ : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kSEQ; + } + + explicit PopovaEOptimisationSEQ(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + + static double FunctionToOptimize(double px, double py); +}; + +} // namespace popova_e_global_optimization_parallelization_by_dividing_the_search_area diff --git a/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/seq/src/ops_seq.cpp b/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/seq/src/ops_seq.cpp new file mode 100644 index 0000000000..9da2a7b1a6 --- /dev/null +++ b/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/seq/src/ops_seq.cpp @@ -0,0 +1,90 @@ +#include "popova_e_global_optimization_parallelization_by_dividing_the_search_area/seq/include/ops_seq.hpp" + +#include +#include +#include +#include +#include + +#include "popova_e_global_optimization_parallelization_by_dividing_the_search_area/common/include/common.hpp" + +namespace popova_e_global_optimization_parallelization_by_dividing_the_search_area { + +PopovaEOptimisationSEQ::PopovaEOptimisationSEQ(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = std::make_tuple(0.0, 0.0, std::numeric_limits::max()); +} + +bool PopovaEOptimisationSEQ::ValidationImpl() { + const auto &in = GetInput(); + return (in.x_max > in.x_min) && (in.y_max > in.y_min) && (in.step > 0); +} + +bool PopovaEOptimisationSEQ::PreProcessingImpl() { + return true; +} + +double PopovaEOptimisationSEQ::FunctionToOptimize(double px, double py) { + return ((px - 2.0) * (px - 2.0)) + ((py - 3.0) * (py - 3.0)); +} + +bool PopovaEOptimisationSEQ::RunImpl() { + const auto &in = GetInput(); + const double safe_step = std::max(in.step, std::numeric_limits::epsilon()); + + double f_min = std::numeric_limits::max(); + double x_best = in.x_min; + double y_best = in.y_min; + + const auto num_x = static_cast(std::floor(((in.x_max - in.x_min) / safe_step) + 0.5)) + 1U; + const auto num_y = static_cast(std::floor(((in.y_max - in.y_min) / safe_step) + 0.5)) + 1U; + + for (std::size_t ix = 0; ix < num_x; ++ix) { + double px = in.x_min + (static_cast(ix) * safe_step); + px = std::min(px, in.x_max); + for (std::size_t iy = 0; iy < num_y; ++iy) { + double py = in.y_min + (static_cast(iy) * safe_step); + py = std::min(py, in.y_max); + double fval = FunctionToOptimize(px, py); + if (fval < f_min) { + f_min = fval; + x_best = px; + y_best = py; + } + } + } + + const double refine_step = std::max(in.step / 2.0, std::numeric_limits::epsilon()); + const double x_ref_min = std::max(in.x_min, x_best - in.step); + const double x_ref_max = std::min(in.x_max, x_best + in.step); + const double y_ref_min = std::max(in.y_min, y_best - in.step); + const double y_ref_max = std::min(in.y_max, y_best + in.step); + + const auto ref_num_x = static_cast(std::floor(((x_ref_max - x_ref_min) / refine_step) + 0.5)) + 1U; + const auto ref_num_y = static_cast(std::floor(((y_ref_max - y_ref_min) / refine_step) + 0.5)) + 1U; + + for (std::size_t ix = 0; ix < ref_num_x; ++ix) { + double px = x_ref_min + (static_cast(ix) * refine_step); + px = std::min(px, x_ref_max); + for (std::size_t iy = 0; iy < ref_num_y; ++iy) { + double py = y_ref_min + (static_cast(iy) * refine_step); + py = std::min(py, y_ref_max); + double fval = FunctionToOptimize(px, py); + if (fval < f_min) { + f_min = fval; + x_best = px; + y_best = py; + } + } + } + + GetOutput() = std::make_tuple(x_best, y_best, f_min); + return true; +} + +bool PopovaEOptimisationSEQ::PostProcessingImpl() { + return true; +} + +} // namespace popova_e_global_optimization_parallelization_by_dividing_the_search_area diff --git a/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/settings.json b/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/settings.json new file mode 100644 index 0000000000..b1a0d52574 --- /dev/null +++ b/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/settings.json @@ -0,0 +1,7 @@ +{ + "tasks_type": "processes", + "tasks": { + "mpi": "enabled", + "seq": "enabled" + } +} diff --git a/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/tests/.clang-tidy b/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/tests/.clang-tidy new file mode 100644 index 0000000000..ef43b7aa8a --- /dev/null +++ b/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/tests/.clang-tidy @@ -0,0 +1,13 @@ +InheritParentConfig: true + +Checks: > + -modernize-loop-convert, + -cppcoreguidelines-avoid-goto, + -cppcoreguidelines-avoid-non-const-global-variables, + -misc-use-anonymous-namespace, + -modernize-use-std-print, + -modernize-type-traits + +CheckOptions: + - key: readability-function-cognitive-complexity.Threshold + value: 50 # Relaxed for tests diff --git a/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/tests/functional/main.cpp b/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/tests/functional/main.cpp new file mode 100644 index 0000000000..126aff536a --- /dev/null +++ b/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/tests/functional/main.cpp @@ -0,0 +1,81 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include "popova_e_global_optimization_parallelization_by_dividing_the_search_area/common/include/common.hpp" +#include "popova_e_global_optimization_parallelization_by_dividing_the_search_area/mpi/include/ops_mpi.hpp" +#include "popova_e_global_optimization_parallelization_by_dividing_the_search_area/seq/include/ops_seq.hpp" +#include "util/include/func_test_util.hpp" +#include "util/include/util.hpp" + +namespace popova_e_global_optimization_parallelization_by_dividing_the_search_area { + +class PopovaRunFuncTests : public ppc::util::BaseRunFuncTests { + public: + static std::string PrintTestParam(const TestType &test_param) { + const auto &label = std::get<1>(test_param); + return label; + } + + protected: + void SetUp() override { + TestType params = std::get(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); + input_data_ = std::get<0>(params); + } + + bool CheckTestOutputData(OutType &output_data) final { + const auto &[x_min, y_min, f_min] = output_data; + const double target_x = std::clamp(2.0, input_data_.x_min, input_data_.x_max); + const double target_y = std::clamp(3.0, input_data_.y_min, input_data_.y_max); + const double f_expected = ((target_x - 2.0) * (target_x - 2.0)) + ((target_y - 3.0) * (target_y - 3.0)); + const double tol_pos = std::max(1e-6, input_data_.step); + const double tol_f = std::max(1e-6, input_data_.step * input_data_.step * 2.0); + + bool x_ok = std::abs(x_min - target_x) < tol_pos; + bool y_ok = std::abs(y_min - target_y) < tol_pos; + bool f_ok = std::abs(f_min - f_expected) < tol_f; + + return x_ok && y_ok && f_ok; + } + + InType GetTestInputData() final { + return input_data_; + } + + private: + InType input_data_{}; +}; + +namespace { + +TEST_P(PopovaRunFuncTests, FindsGridMinimum) { + ExecuteTest(GetParam()); +} + +const std::array kTestParams = {std::make_tuple(InType{0.0, 4.0, 0.0, 6.0, 1.0}, "test_1"), + std::make_tuple(InType{0.0, 4.0, 0.0, 6.0, 0.50}, "test_2"), + std::make_tuple(InType{-1.0, 4.0, -1.0, 7.0, 0.25}, "test_3"), + std::make_tuple(InType{-2.0, 2.0, -2.0, 2.0, 0.50}, "test_4"), + std::make_tuple(InType{1.0, 3.0, 2.0, 4.0, 0.20}, "test_5"), + std::make_tuple(InType{0.0, 5.0, 0.0, 8.0, 0.33}, "test_6")}; + +const auto kTestTasksList = std::tuple_cat( + ppc::util::AddFuncTask( + kTestParams, PPC_SETTINGS_popova_e_global_optimization_parallelization_by_dividing_the_search_area), + ppc::util::AddFuncTask( + kTestParams, PPC_SETTINGS_popova_e_global_optimization_parallelization_by_dividing_the_search_area)); + +const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); + +const auto kTestName = PopovaRunFuncTests::PrintFuncTestName; + +INSTANTIATE_TEST_SUITE_P(GridSearchTests, PopovaRunFuncTests, kGtestValues, kTestName); + +} // namespace + +} // namespace popova_e_global_optimization_parallelization_by_dividing_the_search_area diff --git a/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/tests/performance/main.cpp b/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/tests/performance/main.cpp new file mode 100644 index 0000000000..a891b03534 --- /dev/null +++ b/tasks/popova_e_global_optimization_parallelization_by_dividing_the_search_area/tests/performance/main.cpp @@ -0,0 +1,47 @@ +#include + +#include + +#include "popova_e_global_optimization_parallelization_by_dividing_the_search_area/common/include/common.hpp" +#include "popova_e_global_optimization_parallelization_by_dividing_the_search_area/mpi/include/ops_mpi.hpp" +#include "popova_e_global_optimization_parallelization_by_dividing_the_search_area/seq/include/ops_seq.hpp" +#include "util/include/perf_test_util.hpp" + +namespace popova_e_global_optimization_parallelization_by_dividing_the_search_area { + +class PopovaERunPerfTest : public ppc::util::BaseRunPerfTests { + protected: + static constexpr double kStep = 0.01; + static constexpr double kXMax = 4.0; + static constexpr double kYMax = 6.0; + + InType input_data{}; + + void SetUp() override { + input_data = InType{0.0, kXMax, 0.0, kYMax, kStep}; + } + + bool CheckTestOutputData(OutType &output_data) final { + auto [x_min, y_min, f_min] = output_data; + return std::abs(x_min - 2.0) < 1e-6 && std::abs(y_min - 3.0) < 1e-6 && std::abs(f_min - 0.0) < 1e-6; + } + + InType GetTestInputData() final { + return input_data; + } +}; + +TEST_P(PopovaERunPerfTest, RunPerfModes) { + ExecuteTest(GetParam()); +} + +const auto kAllPerfTasks = ppc::util::MakeAllPerfTasks( + PPC_SETTINGS_popova_e_global_optimization_parallelization_by_dividing_the_search_area); + +const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks); + +const auto kPerfTestName = PopovaERunPerfTest::CustomPerfTestName; + +INSTANTIATE_TEST_SUITE_P(RunModeTests, PopovaERunPerfTest, kGtestValues, kPerfTestName); + +} // namespace popova_e_global_optimization_parallelization_by_dividing_the_search_area