Skip to content

Commit b85feed

Browse files
authored
Ашихмин Даниил. Технология SEQ-MPI. Вычисление многомерных интегралов с использованием многошаговой схемы (метод Симпсона). Вариант 9. (#292)
## Описание - **Задача**: Вычисление многомерных интегралов с использованием многошаговой схемы (метод Симпсона). - **Вариант**: 9. - **Технология**: SEQ, MPI. - **Описание** вашей реализации и отчёта. В работе реализован метод численного интегрирования по правилу Симпсона для многомерных функций. Реализованы последовательная и параллельная версии алгоритма. Параллельная реализация выполнена с использованием MPI с распределением задач между процессами, при этом вычисления организованы по блокам для каждого процесса. В отчёте приведены описание алгоритма, структура кода, псевдокод, проверка корректности результатов и анализ производительности. --- ## Чек-лист - [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, являются точными и достоверными
1 parent 8102561 commit b85feed

11 files changed

Lines changed: 661 additions & 0 deletions

File tree

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#pragma once
2+
3+
#include <string>
4+
#include <tuple>
5+
#include <vector>
6+
7+
#include "task/include/task.hpp"
8+
9+
namespace ashihmin_d_calculate_integrals_by_simpson {
10+
11+
struct SimpsonInput {
12+
std::vector<double> left_bounds;
13+
std::vector<double> right_bounds;
14+
int partitions{};
15+
};
16+
17+
using InType = SimpsonInput;
18+
using OutType = double;
19+
using TestType = std::tuple<int, std::string>;
20+
using BaseTask = ppc::task::Task<InType, OutType>;
21+
22+
} // namespace ashihmin_d_calculate_integrals_by_simpson
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ФИ2",
7+
"task_number": "3"
8+
}
9+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#pragma once
2+
3+
#include <vector>
4+
5+
#include "ashihmin_d_calculate_integrals_by_simpson/common/include/common.hpp"
6+
#include "task/include/task.hpp"
7+
8+
namespace ashihmin_d_calculate_integrals_by_simpson {
9+
10+
class AshihminDCalculateIntegralsBySimpsonMPI : public BaseTask {
11+
public:
12+
static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() {
13+
return ppc::task::TypeOfTask::kMPI;
14+
}
15+
explicit AshihminDCalculateIntegralsBySimpsonMPI(const InType &in);
16+
17+
private:
18+
bool ValidationImpl() override;
19+
bool PreProcessingImpl() override;
20+
bool RunImpl() override;
21+
bool PostProcessingImpl() override;
22+
23+
static double IntegrandFunction(const std::vector<double> &coordinates);
24+
};
25+
26+
} // namespace ashihmin_d_calculate_integrals_by_simpson
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
#include "ashihmin_d_calculate_integrals_by_simpson/mpi/include/ops_mpi.hpp"
2+
3+
#include <mpi.h>
4+
5+
#include <algorithm>
6+
#include <cmath>
7+
#include <cstddef>
8+
#include <vector>
9+
10+
#include "ashihmin_d_calculate_integrals_by_simpson/common/include/common.hpp"
11+
12+
namespace ashihmin_d_calculate_integrals_by_simpson {
13+
14+
namespace {
15+
16+
double Function(const std::vector<double> &coordinates) {
17+
double total = 0.0;
18+
for (double value : coordinates) {
19+
total += value * value;
20+
}
21+
return total;
22+
}
23+
24+
double CalculateWeightCoefficient(const std::vector<int> &indices, int partitions) {
25+
double weight_coefficient = 1.0;
26+
for (int node_index : indices) {
27+
if (node_index == 0 || node_index == partitions) {
28+
weight_coefficient *= 1.0;
29+
} else if (node_index % 2 == 0) {
30+
weight_coefficient *= 2.0;
31+
} else {
32+
weight_coefficient *= 4.0;
33+
}
34+
}
35+
return weight_coefficient;
36+
}
37+
38+
std::vector<double> CalculatePoint(const std::vector<int> &indices, const std::vector<double> &step_sizes,
39+
const std::vector<double> &left_bounds) {
40+
std::vector<double> point(indices.size());
41+
for (std::size_t dim = 0; dim < indices.size(); ++dim) {
42+
point[dim] = left_bounds[dim] + (indices[dim] * step_sizes[dim]);
43+
}
44+
return point;
45+
}
46+
47+
double ComputeLocalSum(int start_index, int end_index, int dimensions, int partitions,
48+
const std::vector<double> &step_sizes, const std::vector<double> &left_bounds) {
49+
double local_sum = 0.0;
50+
51+
std::vector<int> indices(static_cast<std::size_t>(dimensions), 0);
52+
53+
for (int first_index = start_index; first_index <= end_index; ++first_index) {
54+
indices[0] = first_index;
55+
56+
int inner_points_count = 1;
57+
for (int dim = 1; dim < dimensions; ++dim) {
58+
inner_points_count *= (partitions + 1);
59+
}
60+
61+
for (int inner_index = 0; inner_index < inner_points_count; ++inner_index) {
62+
int temp_index = inner_index;
63+
for (int dim = 1; dim < dimensions; ++dim) {
64+
indices[dim] = temp_index % (partitions + 1);
65+
temp_index /= (partitions + 1);
66+
}
67+
68+
const double weight = CalculateWeightCoefficient(indices, partitions);
69+
const auto point = CalculatePoint(indices, step_sizes, left_bounds);
70+
local_sum += weight * Function(point);
71+
}
72+
}
73+
74+
return local_sum;
75+
}
76+
77+
} // namespace
78+
79+
AshihminDCalculateIntegralsBySimpsonMPI::AshihminDCalculateIntegralsBySimpsonMPI(const InType &input) {
80+
SetTypeOfTask(GetStaticTypeOfTask());
81+
GetInput() = input;
82+
GetOutput() = 0.0;
83+
}
84+
85+
bool AshihminDCalculateIntegralsBySimpsonMPI::ValidationImpl() {
86+
const auto &input = GetInput();
87+
if (input.left_bounds.size() != input.right_bounds.size()) {
88+
return false;
89+
}
90+
if (input.partitions <= 0 || input.partitions % 2 != 0) {
91+
return false;
92+
}
93+
for (std::size_t index = 0; index < input.left_bounds.size(); ++index) {
94+
if (input.right_bounds[index] <= input.left_bounds[index]) {
95+
return false;
96+
}
97+
}
98+
return true;
99+
}
100+
101+
bool AshihminDCalculateIntegralsBySimpsonMPI::PreProcessingImpl() {
102+
GetOutput() = 0.0;
103+
return true;
104+
}
105+
106+
double AshihminDCalculateIntegralsBySimpsonMPI::IntegrandFunction(const std::vector<double> &coordinates) {
107+
return Function(coordinates);
108+
}
109+
110+
bool AshihminDCalculateIntegralsBySimpsonMPI::RunImpl() {
111+
int process_rank = 0;
112+
int process_count = 1;
113+
MPI_Comm_rank(MPI_COMM_WORLD, &process_rank);
114+
MPI_Comm_size(MPI_COMM_WORLD, &process_count);
115+
116+
const auto &input = GetInput();
117+
const int dimensions = static_cast<int>(input.left_bounds.size());
118+
const int partitions = input.partitions;
119+
120+
if (dimensions == 0) {
121+
GetOutput() = 0.0;
122+
return true;
123+
}
124+
125+
std::vector<double> step_sizes(static_cast<std::size_t>(dimensions));
126+
for (int dim = 0; dim < dimensions; ++dim) {
127+
step_sizes[static_cast<std::size_t>(dim)] = (input.right_bounds[dim] - input.left_bounds[dim]) / partitions;
128+
}
129+
130+
const int total_nodes = partitions + 1;
131+
const int chunk_size = total_nodes / process_count;
132+
const int remainder = total_nodes % process_count;
133+
134+
int start_index = (process_rank * chunk_size) + std::min(process_rank, remainder);
135+
int end_index = start_index + chunk_size - 1;
136+
if (process_rank < remainder) {
137+
end_index += 1;
138+
}
139+
140+
double local_sum = 0.0;
141+
if (start_index <= end_index) {
142+
local_sum = ComputeLocalSum(start_index, end_index, dimensions, partitions, step_sizes, input.left_bounds);
143+
}
144+
145+
double global_sum = 0.0;
146+
MPI_Reduce(&local_sum, &global_sum, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
147+
148+
double volume_element = 1.0;
149+
for (double step : step_sizes) {
150+
volume_element *= step;
151+
}
152+
153+
if (process_rank == 0) {
154+
GetOutput() = global_sum * volume_element / std::pow(3.0, dimensions);
155+
}
156+
157+
MPI_Bcast(&GetOutput(), 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
158+
return true;
159+
}
160+
161+
bool AshihminDCalculateIntegralsBySimpsonMPI::PostProcessingImpl() {
162+
return true;
163+
}
164+
165+
} // namespace ashihmin_d_calculate_integrals_by_simpson
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
# Отчёт
2+
--------------------------------------------------------------------------
3+
4+
## Численное вычисление многомерных интегралов методом Симпсона
5+
6+
Студент: Ашихмин Даниил
7+
Группа: 3823Б1ФИ2
8+
9+
Технология: SEQ-MPI
10+
11+
--------------------------------------------------------------------------
12+
13+
## Введение
14+
В данной работе реализуется алгоритм численного вычисления многомерных интегралов методом Симпсона. Рассматривается последовательная и параллельная (MPI) реализации. Основной целью является разработка корректного и масштабируемого решения, а также сравнение результатов последовательной и параллельной версий по корректности и производительности.
15+
16+
--------------------------------------------------------------------------
17+
18+
## Постановка задачи
19+
Требуется вычислить значение определённого многомерного интеграла на прямоугольной области с использованием метода Симпсона.
20+
21+
Входные данные:
22+
23+
· Количество разбиений partitions по каждой размерности (чётное число)
24+
· Вектор левых границ left_bounds и правых границ right_bounds (жёстко задаются для всех размерностей в коде)
25+
· Функция Function(coordinates) - многомерная функция, значение которой требуется проинтегрировать
26+
27+
Выходные данные:
28+
29+
· Численное значение интеграла
30+
31+
Требования:
32+
33+
· Корректная работа при любой размерности интеграла
34+
· Использование метода Симпсона
35+
· Поддержка параллельной реализации с применением MPI
36+
· Корректное воспроизведение результата в последовательной версии
37+
38+
--------------------------------------------------------------------------
39+
40+
## Базовый алгоритм (последовательный)
41+
Последовательная версия алгоритма выполняет следующие шаги:
42+
43+
· Формируется равномерная сетка по каждой размерности
44+
· Вычисляются шаги интегрирования
45+
· Для каждой точки сетки вычисляется значение подынтегральной функции
46+
· К значению функции применяется вес, соответствующий методу Симпсона
47+
· Производится суммирование всех взвешенных значений
48+
· Итоговый результат умножается на коэффициент, зависящий от шагов интегрирования
49+
50+
--------------------------------------------------------------------------
51+
52+
## Схема распараллеливания (MPI)
53+
Параллельная версия использует разбиение вычислений по процессам.
54+
55+
Распределение данных:
56+
57+
· Полный диапазон индексов сетки по первому измерению разбивается между процессами
58+
· Каждый процесс обрабатывает свой поддиапазон точек, используя ComputeLocalSum
59+
· Локальные суммы объединяются через MPI_Reduce, итоговое значение рассылается всем процессам через MPI_Bcast
60+
61+
Схема взаимодействия процессов:
62+
63+
· Используются только MPI_Comm_rank, MPI_Comm_size, MPI_Reduce и MPI_Bcast
64+
65+
--------------------------------------------------------------------------
66+
67+
## Детали реализации
68+
Структура кода:
69+
70+
· common.hpp - общие структуры данных и вспомогательные функции
71+
· ops_seq.hpp / ops_seq.cpp - последовательная реализация метода Симпсона
72+
· ops_mpi.hpp / ops_mpi.cpp - параллельная MPI-реализация
73+
· tests/functional/main.cpp - функциональные тесты
74+
· tests/performance/main.cpp - тесты производительности
75+
76+
Особенности реализации:
77+
78+
· Корректная работа с произвольной размерностью интеграла
79+
· Проверка корректности входных параметров
80+
· Аккуратная работа с индексами сетки и распределением точек между процессами
81+
· Локальная сумма рассчитывается только по точкам, назначенным текущему процессу
82+
83+
Основные вспомогательные функции:
84+
85+
· Генерация координат точки по индексам
86+
· Вычисление веса точки по методу Симпсона
87+
· Преобразование индексов в линейный диапазон
88+
89+
--------------------------------------------------------------------------
90+
91+
## Экспериментальная установка
92+
Аппаратное обеспечение:
93+
94+
· CPU - AMD Ryzen 5 3500X (3.6 - 4.1 GHz, 6 ядер)
95+
· RAM - 16 ГБ
96+
97+
Программное обеспечение:
98+
99+
· OS - Windows 11 Pro x64
100+
· MPI - Microsoft MPI 10.1.1
101+
· Компилятор - MSVC 19.x
102+
· Система сборки - CMake
103+
· Конфигурация - Release
104+
105+
Параметры запуска:
106+
107+
· PPC_NUM_PROC = 4
108+
· PPC_NUM_THREADS = 1
109+
110+
--------------------------------------------------------------------------
111+
112+
## Результаты и обсуждение
113+
114+
## Проверка корректности
115+
Функциональные тесты проверяют:
116+
117+
· Корректность вычисления интеграла в последовательной версии
118+
· Совпадение результатов последовательной и MPI-версий
119+
· Корректную работу при различных размерностях
120+
121+
Результаты:
122+
123+
· Все функциональные тесты пройдены успешно
124+
· Полученные значения совпадают в пределах допустимой погрешности
125+
126+
## Производительность
127+
128+
Результаты тестов производительности метода Симпсона представлены в таблице:
129+
| Mode | Count | Time (s) | Speed-up | Efficiency |
130+
|--------|-------|----------|----------|------------|
131+
| seq | 1 | 0.0103 | 1.00 | 100% |
132+
| mpi | 2 | 0.0060 | 1.72 | 86% |
133+
| mpi | 4 | 0.0038 | 2.71 | 68% |
134+
135+
Тесты производительности показывают:
136+
137+
· Ускорение вычислений при увеличении числа процессов
138+
· Корректную масштабируемость алгоритма
139+
· Отсутствие деградации производительности на малых размерностях
140+
141+
--------------------------------------------------------------------------
142+
143+
## Заключение
144+
В ходе выполнения работы был реализован алгоритм численного вычисления многомерных интегралов методом Симпсона в последовательном и параллельном вариантах.
145+
146+
Достигнутые результаты:
147+
148+
· Реализован корректный алгоритм метода Симпсона
149+
· Создана MPI-версия с распределением вычислений по процессам
150+
· Подтверждена корректность результатов с помощью тестирования
151+
· Проведён анализ производительности
152+
153+
--------------------------------------------------------------------------
154+
155+
## References
156+
· MPI Standard - https://www.mpi-forum.org/docs/
157+
· Microsoft MPI Documentation - https://learn.microsoft.com/message-passing-interface
158+
· cppreference.com - https://en.cppreference.com

0 commit comments

Comments
 (0)