Skip to content
28 changes: 28 additions & 0 deletions tasks/titaev_m_yakobi/common/include/common.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#pragma once

#include <string>
#include <tuple>
#include <vector>

#include "task/include/task.hpp"

namespace titaev_m_yakobi {

using ValueType = double;

struct JacobiInput {
std::vector<ValueType> A;
std::vector<ValueType> b;
std::vector<ValueType> x0;
int n = 0;
ValueType eps = 1e-6;
int max_iter = 1000;
};

using InType = JacobiInput;
using OutType = std::vector<ValueType>;
using TestType = std::tuple<int, std::string>;

using BaseTask = ppc::task::Task<InType, OutType>;

} // namespace titaev_m_yakobi
9 changes: 9 additions & 0 deletions tasks/titaev_m_yakobi/info.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"student": {
"first_name": "Максим",
"last_name": "Титаев",
"middle_name": "Алексеевич",
"group_number": "3823Б1ФИ2",
"task_number": "2"
}
}
29 changes: 29 additions & 0 deletions tasks/titaev_m_yakobi/mpi/include/ops_mpi.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#pragma once

#include <vector>

#include "task/include/task.hpp"
#include "titaev_m_yakobi/common/include/common.hpp"

namespace titaev_m_yakobi {

class TitaevMYakobiMPI : public BaseTask {
public:
static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() {
return ppc::task::TypeOfTask::kMPI;
}
explicit TitaevMYakobiMPI(const InType &in);

private:
static int CheckConvergence(int rank, int n, const std::vector<ValueType> &x_new_global,
const std::vector<ValueType> &x_old, ValueType eps);
bool ValidationImpl() override;
bool PreProcessingImpl() override;
bool RunImpl() override;
bool PostProcessingImpl() override;

void ComputeLocal(const std::vector<ValueType> &x_old, std::vector<ValueType> &x_new_local, int start_row,
int my_rows);
};

} // namespace titaev_m_yakobi
149 changes: 149 additions & 0 deletions tasks/titaev_m_yakobi/mpi/src/ops_mpi.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#include "titaev_m_yakobi/mpi/include/ops_mpi.hpp"

#include <mpi.h>

#include <algorithm>
#include <cmath>
#include <cstddef>
#include <utility>
#include <vector>

#include "titaev_m_yakobi/common/include/common.hpp"

namespace titaev_m_yakobi {

TitaevMYakobiMPI::TitaevMYakobiMPI(const InType &in) {
SetTypeOfTask(GetStaticTypeOfTask());
GetInput() = in;
GetOutput().resize(in.n, 0.0);
}

bool TitaevMYakobiMPI::ValidationImpl() {
const auto &in = GetInput();
if (in.n <= 0) {
return false;
}
const auto n_size = static_cast<std::size_t>(in.n);
if (n_size * n_size != in.A.size()) {
return false;
}
if (n_size != in.b.size()) {
return false;
}
if (!in.x0.empty() && n_size != in.x0.size()) {
return false;
}
if (in.eps <= 0.0 || in.max_iter <= 0) {
return false;
}
return true;
}

int TitaevMYakobiMPI::CheckConvergence(int rank, int n, const std::vector<ValueType> &x_new_global,
const std::vector<ValueType> &x_old, ValueType eps) {
if (rank != 0) {
return 0;
}

ValueType max_diff = 0.0;
for (int i = 0; i < n; ++i) {
const ValueType diff = std::fabs(x_new_global[i] - x_old[i]);
max_diff = std::max(diff, max_diff);
}
return (max_diff < eps) ? 1 : 0;
}

bool TitaevMYakobiMPI::PreProcessingImpl() {
auto &in = GetInput();
auto &out = GetOutput();
if (in.x0.empty()) {
in.x0.assign(in.n, 0.0);
}
out = in.x0;
return true;
}

void TitaevMYakobiMPI::ComputeLocal(const std::vector<ValueType> &x_old, std::vector<ValueType> &x_new_local,
int start_row, int my_rows) {
const auto &in = GetInput();
const int n = in.n;

for (int local_i = 0; local_i < my_rows; ++local_i) {
const int i = start_row + local_i;
const ValueType diag = in.A[(i * n) + i];
if (std::fabs(diag) < 1e-15) {
continue;
}

ValueType sum = 0.0;
for (int j = 0; j < n; ++j) {
if (j != i) {
sum += in.A[(i * n) + j] * x_old[j];
}
}
x_new_local[local_i] = (in.b[i] - sum) / diag;
}
}

bool TitaevMYakobiMPI::RunImpl() {
const auto &in = GetInput();
auto &out = GetOutput();
const int n = in.n;

int rank = 0;
int size = 0;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);

std::vector<ValueType> x_old = out;

const int rows_per_proc = n / size;
const int remainder = n % size;
const int my_rows = rows_per_proc + (rank < remainder ? 1 : 0);
const int start_row = (rank * rows_per_proc) + std::min(rank, remainder);

std::vector<ValueType> x_new_local(my_rows, 0.0);

std::vector<int> recvcounts(size);
std::vector<int> displs(size);
for (int proc = 0; proc < size; ++proc) {
const int rows_proc = rows_per_proc + (proc < remainder ? 1 : 0);
recvcounts[proc] = rows_proc;
displs[proc] = (proc * rows_per_proc) + std::min(proc, remainder);
}

for (int iter = 0; iter < in.max_iter; ++iter) {
ComputeLocal(x_old, x_new_local, start_row, my_rows);

std::vector<ValueType> x_new_global;
if (rank == 0) {
x_new_global.resize(n);
}

MPI_Gatherv(x_new_local.data(), my_rows, MPI_DOUBLE, x_new_global.data(), recvcounts.data(), displs.data(),
MPI_DOUBLE, 0, MPI_COMM_WORLD);

int converged = CheckConvergence(rank, n, x_new_global, x_old, in.eps);

if (rank == 0) {
x_old = std::move(x_new_global);
}

MPI_Bcast(x_old.data(), n, MPI_DOUBLE, 0, MPI_COMM_WORLD);
MPI_Bcast(&converged, 1, MPI_INT, 0, MPI_COMM_WORLD);

out = x_old;

if (converged != 0) {
break;
}
}

return true;
}

bool TitaevMYakobiMPI::PostProcessingImpl() {
return true;
}

} // namespace titaev_m_yakobi
174 changes: 174 additions & 0 deletions tasks/titaev_m_yakobi/report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# Отчёт

---

## Численное решение системы линейных уравнений методом Якоби

**Студент:** Титаев Максим
**Группа:** 3823Б1ФИ1

**Технология:** SEQ-MPI

---

## Введение

В данной работе реализуется алгоритм численного решения системы линейных уравнений методом Якоби. Рассматриваются последовательная и параллельная (MPI) реализации. Основной целью является разработка корректного и масштабируемого решения, а также сравнение результатов последовательной и параллельной версий по корректности и производительности.

---

## Постановка задачи

Требуется решить систему линейных уравнений.

**Входные данные:**

- Матрица коэффициентов
- Вектор правых частей
- Начальное приближение (если не задано, используется нулевой вектор)
- Погрешность и максимальное число итераций

**Выходные данные:**

- Вектор решения

**Требования:**

- Корректная работа для любой размерности системы
- Использование метода Якоби
- Поддержка параллельной реализации с применением MPI
- Совпадение результатов последовательной и MPI-версий

---

## Базовый алгоритм (последовательный)

Последовательная версия алгоритма выполняет следующие шаги:

- Инициализация вектора начальным приближением
- Повторение до сходимости или достижения максимального числа итераций:
- Вычисление нового значения каждой компоненты решения
- Вычисление максимального отклонения между текущим и предыдущим векторами
- Проверка условия сходимости
- Обновление текущего вектора решения

---

## Схема распараллеливания (MPI)

Параллельная версия использует разбиение строк матрицы по процессам.

**Распределение данных:**

- Полный диапазон строк матрицы разбивается между процессами
- Каждый процесс вычисляет новые значения решения для своего блока строк
- На каждом шаге производится сбор значений на корневом процессе

**Схема взаимодействия процессов:**

- Каждый процесс вычисляет локальный блок
- MPI_Gatherv используется для сбора блоков на корневом процессе
- Корневой процесс проверяет сходимость и обновляет глобальный вектор решения
- Результат рассылается всем процессам через MPI_Bcast

**Используемые средства MPI:**

- `MPI_Comm_rank`
- `MPI_Comm_size`
- `MPI_Gatherv`
- `MPI_Bcast`

---

## Детали реализации

**Структура кода:**

- `common.hpp` – общие структуры данных и вспомогательные функции
- `ops_seq.hpp / ops_seq.cpp` – последовательная реализация метода Якоби
- `ops_mpi.hpp / ops_mpi.cpp` – параллельная MPI-реализация
- `tests/functional/main.cpp` – функциональные тесты
- `tests/performance/main.cpp` – тесты производительности

**Особенности реализации:**

- Проверка корректности входных параметров
- Инициализация начального приближения
- Итеративное решение системы с MPI
- Корректное обновление вектора решения на всех процессах
- Завершение и постобработка

**Обновление решения на каждом шаге:**

- Каждый процесс вычисляет новые значения для своего блока
- Собираются все блоки на корневом процессе
- Проверяется сходимость
- Обновляется вектор решения и рассылается всем процессам

---

## Экспериментальная установка

**Аппаратное обеспечение:**

- CPU: AMD Ryzen 5 3500X (3.6 – 4.1 GHz, 6 ядер)
- RAM: 16 ГБ

**Программное обеспечение:**

- OS: Windows 11 Pro x64
- MPI: Microsoft MPI 10.1.1
- Компилятор: MSVC 19.x
- Система сборки: CMake
- Конфигурация: Release

**Параметры запуска:**

- `PPC_NUM_PROC = 4`
- `PPC_NUM_THREADS = 1`

---

## Результаты и обсуждение

### Проверка корректности

Функциональные тесты проверяют:

- Корректность вычисления решения последовательной версии
- Совпадение результатов последовательной и MPI-версий
- Корректную работу при различных размерностях системы

**Результаты:**

- Все функциональные тесты пройдены успешно
- Полученные значения совпадают в пределах допустимой погрешности

### Производительность

| Mode | Count | Time (s) | Speed-up | Efficiency |
|--------|-------|----------|----------|------------|
| seq | 1 | 0.0125 | 1.00 | 100% |
| mpi | 2 | 0.0073 | 1.71 | 85% |
| mpi | 4 | 0.0041 | 3.05 | 76% |

---

## Заключение

В ходе выполнения работы был реализован алгоритм численного решения системы линейных уравнений методом Якоби в последовательном и параллельном вариантах.

Достигнутые результаты:

- Реализован корректный алгоритм метода Якоби
- Создана MPI-версия с распределением вычислений по процессам
- Подтверждена корректность результатов с помощью тестирования
- Проведен анализ производительности

---

## References

- MPI Standard - https://www.mpi-forum.org/docs/
- Microsoft MPI Documentation - https://learn.microsoft.com/message-passing-interface
- cppreference.com - https://en.cppreference.com
Loading
Loading