Skip to content

Commit 8b6c3e3

Browse files
authored
Гонозов Леонид. Технология SEQ|MPI. Алгоритм глобального поиска (Стронгина) для одномерных задач оптимизации. Распараллеливание по характеристикам. Вариант 11 (#301)
<!-- Требования к названию pull request: "<Фамилия> <Имя>. Технология <TECHNOLOGY_NAME:SEQ|OMP|TBB|STL|MPI>. <Полное название задачи>. Вариант <Номер>" --> ## Описание <!-- Пожалуйста, предоставьте подробное описание вашей реализации, включая: - основные детали решения (описание выбранного алгоритма) - применение технологии параллелизма (если применимо) --> - **Задача**: Задача: Алгоритм глобального поиска (Стронгина) для одномерных задач оптимизации. Распараллеливание по характеристикам. - **Вариант**: 11 - **Технология**: SEQ, MPI - **Описание** SEQ: Алгоритм нужен для нахождения глобального минимума одномерной функции на отрезке вещественной оси и представляет собой последовательность итераций, на которых происходит вычисление новой координаты для испытания, исходя из заданных параметров и координат прошлых испытаний. За начальные испытания берутся границы отрезка, на котором требуется найти глобальный минимум. После каждой итерации проверяется достижения отрезком, в котором проводилось новое испытание, параметра остановки. MPI: По всем процессам на каждой итерации происходит пересылка через MPI_Bcast последовательности и высчитанных на корневом процессе коэффициентов. Локально на каждом процессе происходит подсчёт характеристик и интервала, на котором они находятся. Затем эти данные собираются через MPI_Reduce (в качестве операции используется MPI_MAXLOC) на корневом процессе, на котором происходит вычисление новой точки испытаний и проверка значения функции в этой точке на минимальность. После чего определяется условие продолжение алгоритма, рассылаемое затем по всем процессам через MPI_Bcast. --- ## Чек-лист <!-- Пожалуйста, убедитесь, что следующие пункты выполнены **до** отправки pull request'а и запроса его ревью: --> - [x] **Статус CI**: Все CI-задачи (сборка, тесты, генерация отчёта) успешно проходят на моей ветке в моем форке - [x] **Директория и именование задачи**: Я создал директорию с именем `<фамилия>_<первая_буква_имени>_<короткое_название_задачи>` - [x] **Полное описание задачи**: Я предоставил полное описание задачи в теле pull request - [x] **clang-format**: Мои изменения успешно проходят `clang-format` локально в моем форке (нет ошибок форматирования) - [x] **clang-tidy**: Мои изменения успешно проходят `clang-tidy` локально в моем форке (нет предупреждений/ошибок) - [x] **Функциональные тесты**: Все функциональные тесты успешно проходят локально на моей машине - [x] **Тесты производительности**: Все тесты производительности успешно проходят локально на моей машине - [x] **Ветка**: Я работаю в ветке, названной точно так же, как директория моей задачи (например, `nesterov_a_vector_sum`), а не в `master` - [x] **Правдивое содержание**: Я подтверждаю, что все сведения, указанные в этом pull request, являются точными и достоверными <!-- ПРИМЕЧАНИЕ: Ложные сведения в этом чек-листе могут привести к отклонению PR и получению нулевого балла за соответствующую задачу. -->
1 parent b85feed commit 8b6c3e3

11 files changed

Lines changed: 750 additions & 0 deletions

File tree

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#pragma once
2+
3+
#include <functional>
4+
#include <string>
5+
#include <tuple>
6+
7+
#include "task/include/task.hpp"
8+
9+
namespace gonozov_l_global_search {
10+
11+
using InType = std::tuple<std::function<double(double)>, double, double, double, double>;
12+
// 0 - минимизируемая функция,
13+
// 1 - значение коэффициента r
14+
// 2 - левая граница интервала
15+
// 3 - правая граница интервала
16+
// 4 - точность вычислений
17+
using OutType = double; // значение координаты в точке глобального минимума
18+
using TestType = std::tuple<std::function<double(double)>, double, double, double, double, std::string, double>;
19+
using BaseTask = ppc::task::Task<InType, OutType>;
20+
21+
} // namespace gonozov_l_global_search
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"student": {
3+
"first_name": "Леонид",
4+
"last_name": "Гонозов",
5+
"middle_name": "Андреевич",
6+
"group_number": "3823Б1ФИ3",
7+
"task_number": "3"
8+
}
9+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#pragma once
2+
3+
#include "gonozov_l_global_search/common/include/common.hpp"
4+
#include "task/include/task.hpp"
5+
6+
namespace gonozov_l_global_search {
7+
8+
class GonozovLGlobalSearchMPI : public BaseTask {
9+
public:
10+
static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() {
11+
return ppc::task::TypeOfTask::kMPI;
12+
}
13+
explicit GonozovLGlobalSearchMPI(const InType &in);
14+
15+
private:
16+
bool ValidationImpl() override;
17+
bool PreProcessingImpl() override;
18+
bool RunImpl() override;
19+
bool PostProcessingImpl() override;
20+
};
21+
22+
} // namespace gonozov_l_global_search
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
#include "gonozov_l_global_search/mpi/include/ops_mpi.hpp"
2+
3+
#include <mpi.h>
4+
5+
#include <algorithm>
6+
#include <cmath>
7+
#include <functional>
8+
#include <limits>
9+
#include <tuple>
10+
#include <utility>
11+
#include <vector>
12+
13+
#include "gonozov_l_global_search/common/include/common.hpp"
14+
15+
namespace gonozov_l_global_search {
16+
17+
GonozovLGlobalSearchMPI::GonozovLGlobalSearchMPI(const InType &in) {
18+
SetTypeOfTask(GetStaticTypeOfTask());
19+
GetInput() = in;
20+
GetOutput() = 0.0;
21+
}
22+
23+
bool GonozovLGlobalSearchMPI::ValidationImpl() {
24+
return (std::get<1>(GetInput()) > 1.0) && (std::get<2>(GetInput()) < std::get<3>(GetInput())) &&
25+
(std::get<4>(GetInput()) > 0);
26+
}
27+
28+
bool GonozovLGlobalSearchMPI::PreProcessingImpl() {
29+
return true;
30+
}
31+
32+
namespace {
33+
34+
double CountM(int t, double highm, const std::vector<double> &x, const std::function<double(double)> &f) {
35+
if (highm == -std::numeric_limits<double>::infinity()) {
36+
return std::abs((f(x[1]) - f(x[0])) / (x[1] - x[0]));
37+
}
38+
39+
double highm1 = std::abs((f(x.back()) - f(x[t - 1])) / (x.back() - x[t - 1]));
40+
double highm2 = std::abs((f(x[t]) - f(x.back())) / (x[t] - x.back()));
41+
42+
double high = std::max(highm, highm1);
43+
return std::max(high, highm2);
44+
}
45+
46+
double IntervalCharacteristic(double x1, double x2, double f1, double f2, double m) {
47+
double dx = x2 - x1;
48+
return (m * dx) + ((f2 - f1) * (f2 - f1) / (m * dx)) - (2.0 * (f1 + f2));
49+
}
50+
51+
void InizialzationStartParameters(int proc_rank, std::vector<double> &test_sequence,
52+
const std::function<double(double)> &function, double &global_min_x,
53+
double &global_min_value, double a, double b) {
54+
if (proc_rank == 0) {
55+
test_sequence = {a, b};
56+
if (function(b) < global_min_value) {
57+
global_min_x = b;
58+
global_min_value = function(b);
59+
}
60+
}
61+
}
62+
63+
void FormNewtestSequence(int proc_rank, std::vector<double> &test_sequence, int *n) {
64+
if (proc_rank == 0) {
65+
*n = static_cast<int>(test_sequence.size());
66+
}
67+
MPI_Bcast(n, 1, MPI_INT, 0, MPI_COMM_WORLD);
68+
69+
if (proc_rank != 0) {
70+
test_sequence.resize(*n);
71+
}
72+
73+
MPI_Bcast(test_sequence.data(), *n, MPI_DOUBLE, 0, MPI_COMM_WORLD);
74+
75+
std::ranges::sort(test_sequence.begin(), test_sequence.end());
76+
}
77+
78+
void FormNewParameters(int proc_rank, double &m, double &highm, double r, int t,
79+
const std::vector<double> &test_sequence, const std::function<double(double)> &function) {
80+
if (proc_rank == 0) {
81+
highm = CountM(t, highm, test_sequence, function);
82+
m = (highm == 0.0) ? 1.0 : r * highm;
83+
}
84+
MPI_Bcast(&m, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
85+
}
86+
87+
void CountingNewCoordinateContinueIteration(int proc_rank, int glob, bool &continue_iteration,
88+
std::vector<double> &test_sequence, int &t,
89+
const std::function<double(double)> &function, double &global_min_x,
90+
double &global_min_value, double m, double eps) {
91+
if (proc_rank == 0) {
92+
if (glob <= 0 || std::cmp_greater_equal(glob, test_sequence.size())) {
93+
continue_iteration = false;
94+
} else {
95+
t = glob;
96+
if (t < 1) {
97+
continue_iteration = false;
98+
}
99+
100+
double x_new = (0.5 * (test_sequence[t] + test_sequence[t - 1])) -
101+
((function(test_sequence[t]) - function(test_sequence[t - 1])) / (2.0 * m));
102+
103+
double fx = function(x_new);
104+
if (fx < global_min_value) {
105+
global_min_value = fx;
106+
global_min_x = x_new;
107+
}
108+
test_sequence.push_back(x_new);
109+
}
110+
111+
continue_iteration = std::abs(test_sequence[t] - test_sequence[t - 1]) > eps;
112+
}
113+
114+
MPI_Bcast(&continue_iteration, 1, MPI_C_BOOL, 0, MPI_COMM_WORLD);
115+
}
116+
} // namespace
117+
bool GonozovLGlobalSearchMPI::RunImpl() {
118+
auto function = std::get<0>(GetInput());
119+
double r = std::get<1>(GetInput());
120+
double a = std::get<2>(GetInput());
121+
double b = std::get<3>(GetInput());
122+
double eps = std::get<4>(GetInput());
123+
124+
int proc_num = 0;
125+
int proc_rank = 0;
126+
MPI_Comm_rank(MPI_COMM_WORLD, &proc_rank);
127+
MPI_Comm_size(MPI_COMM_WORLD, &proc_num);
128+
129+
std::vector<double> test_sequence;
130+
double global_min_x = a;
131+
double global_min_value = function(a);
132+
133+
int t = 1;
134+
double highm = -std::numeric_limits<double>::infinity();
135+
136+
InizialzationStartParameters(proc_rank, test_sequence, function, global_min_x, global_min_value, a, b);
137+
138+
bool continue_iteration = true;
139+
140+
while (continue_iteration) {
141+
int n = 0;
142+
FormNewtestSequence(proc_rank, test_sequence, &n);
143+
144+
double m = 0.0;
145+
146+
FormNewParameters(proc_rank, m, highm, r, t, test_sequence, function);
147+
148+
int intervals = n - 1;
149+
int per_proc = intervals / proc_num;
150+
int rem = intervals % proc_num;
151+
152+
int l = (proc_rank * per_proc) + std::min(proc_rank, rem);
153+
int r_i = l + per_proc + (proc_rank < rem ? 1 : 0);
154+
155+
double local_max = -std::numeric_limits<double>::infinity();
156+
int local_idx = -1;
157+
158+
if (l < r_i) {
159+
for (int i = l + 1; i <= r_i; ++i) {
160+
double highr = IntervalCharacteristic(test_sequence[i - 1], test_sequence[i], function(test_sequence[i - 1]),
161+
function(test_sequence[i]), m);
162+
if (highr > local_max) {
163+
local_max = highr;
164+
local_idx = i;
165+
}
166+
}
167+
}
168+
169+
struct {
170+
double val;
171+
int idx;
172+
} loc{}, glob{};
173+
loc.val = local_max;
174+
loc.idx = local_idx;
175+
176+
MPI_Reduce(&loc, &glob, 1, MPI_DOUBLE_INT, MPI_MAXLOC, 0, MPI_COMM_WORLD);
177+
178+
CountingNewCoordinateContinueIteration(proc_rank, glob.idx, continue_iteration, test_sequence, t, function,
179+
global_min_x, global_min_value, m, eps);
180+
}
181+
182+
// рассылаем результат всем процессам
183+
MPI_Bcast(&global_min_x, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
184+
185+
GetOutput() = global_min_x;
186+
187+
return true;
188+
}
189+
190+
bool GonozovLGlobalSearchMPI::PostProcessingImpl() {
191+
return true;
192+
}
193+
194+
} // namespace gonozov_l_global_search

0 commit comments

Comments
 (0)