diff --git a/tasks/sokolov_k_gauss_jordan/common/include/common.hpp b/tasks/sokolov_k_gauss_jordan/common/include/common.hpp new file mode 100644 index 00000000..87622031 --- /dev/null +++ b/tasks/sokolov_k_gauss_jordan/common/include/common.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +#include "task/include/task.hpp" + +namespace sokolov_k_gauss_jordan { + +using InType = int; +using OutType = int; +using TestType = std::tuple; +using BaseTask = ppc::task::Task; + +} // namespace sokolov_k_gauss_jordan diff --git a/tasks/sokolov_k_gauss_jordan/info.json b/tasks/sokolov_k_gauss_jordan/info.json new file mode 100644 index 00000000..3a65a37a --- /dev/null +++ b/tasks/sokolov_k_gauss_jordan/info.json @@ -0,0 +1,9 @@ +{ + "student": { + "first_name": "Кирилл", + "group_number": "3823Б1ПР4", + "last_name": "Соколов", + "middle_name": "Денисович", + "task_number": "2" + } +} diff --git a/tasks/sokolov_k_gauss_jordan/mpi/include/ops_mpi.hpp b/tasks/sokolov_k_gauss_jordan/mpi/include/ops_mpi.hpp new file mode 100644 index 00000000..32f9ce0c --- /dev/null +++ b/tasks/sokolov_k_gauss_jordan/mpi/include/ops_mpi.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include "sokolov_k_gauss_jordan/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace sokolov_k_gauss_jordan { + +class SokolovKGaussJordanMPI : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kMPI; + } + explicit SokolovKGaussJordanMPI(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + int n_{}; + std::vector matrix_; + std::vector original_matrix_; + std::vector solution_; +}; + +} // namespace sokolov_k_gauss_jordan diff --git a/tasks/sokolov_k_gauss_jordan/mpi/src/ops_mpi.cpp b/tasks/sokolov_k_gauss_jordan/mpi/src/ops_mpi.cpp new file mode 100644 index 00000000..7ad03cce --- /dev/null +++ b/tasks/sokolov_k_gauss_jordan/mpi/src/ops_mpi.cpp @@ -0,0 +1,213 @@ +#include "sokolov_k_gauss_jordan/mpi/include/ops_mpi.hpp" + +#include + +#include +#include +#include + +#include "sokolov_k_gauss_jordan/common/include/common.hpp" + +namespace sokolov_k_gauss_jordan { + +namespace { + +void GenerateSystem(int n, std::vector &matrix) { + int cols = n + 1; + matrix.assign(static_cast(n) * cols, 0.0); + unsigned int seed = static_cast(n) * 1234567U; + auto gen = [&seed]() -> double { + seed = (seed * 1103515245U) + 12345U; + return static_cast(((seed >> 16) % 100) + 1); + }; + for (int i = 0; i < n; i++) { + double row_sum = 0.0; + for (int j = 0; j < n; j++) { + if (i != j) { + matrix[(i * cols) + j] = gen() / 100.0; + row_sum += std::abs(matrix[(i * cols) + j]); + } + } + matrix[(i * cols) + i] = row_sum + gen(); + } + for (int i = 0; i < n; i++) { + double b_val = 0.0; + for (int j = 0; j < n; j++) { + b_val += matrix[(i * cols) + j] * (j + 1.0); + } + matrix[(i * cols) + n] = b_val; + } +} + +void ComputeDistribution(int n, int cols, int world_size, std::vector &rows_per_proc, + std::vector &row_offsets, std::vector &sendcounts, std::vector &displs) { + rows_per_proc.resize(world_size); + row_offsets.assign(world_size, 0); + sendcounts.resize(world_size); + displs.resize(world_size); + for (int i = 0; i < world_size; i++) { + rows_per_proc[i] = (n / world_size) + (i < (n % world_size) ? 1 : 0); + if (i > 0) { + row_offsets[i] = row_offsets[i - 1] + rows_per_proc[i - 1]; + } + sendcounts[i] = rows_per_proc[i] * cols; + displs[i] = row_offsets[i] * cols; + } +} + +int FindRowOwner(int k, const std::vector &row_offsets, const std::vector &rows_per_proc, int world_size) { + for (int pp = 0; pp < world_size; pp++) { + if (k < row_offsets[pp] + rows_per_proc[pp]) { + return pp; + } + } + return world_size - 1; +} + +void EliminationStep(std::vector &local_data, std::vector &pivot_row, int local_rows, int local_start, + int cols, int k, int rank, const std::vector &row_offsets, + const std::vector &rows_per_proc, int world_size) { + int owner = FindRowOwner(k, row_offsets, rows_per_proc, world_size); + + if (rank == owner) { + int li = k - local_start; + double pivot_val = local_data[(li * cols) + k]; + for (int j = 0; j < cols; j++) { + local_data[(li * cols) + j] /= pivot_val; + pivot_row[j] = local_data[(li * cols) + j]; + } + } + + MPI_Bcast(pivot_row.data(), cols, MPI_DOUBLE, owner, MPI_COMM_WORLD); + + for (int i = 0; i < local_rows; i++) { + int gi = local_start + i; + if (gi != k) { + double factor = local_data[(i * cols) + k]; + for (int j = 0; j < cols; j++) { + local_data[(i * cols) + j] -= factor * pivot_row[j]; + } + } + } +} + +void CollectResults(std::vector &local_data, std::vector &matrix, int local_rows, int cols, + const std::vector &sendcounts, const std::vector &displs, int world_size, int rank) { + if (rank != 0) { + if (local_rows > 0) { + MPI_Send(local_data.data(), local_rows * cols, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD); + } + return; + } + for (int i = 0; i < local_rows * cols; i++) { + matrix[i] = local_data[i]; + } + for (int pp = 1; pp < world_size; pp++) { + if (sendcounts[pp] > 0) { + MPI_Recv(matrix.data() + displs[pp], sendcounts[pp], MPI_DOUBLE, pp, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + } + } +} + +} // namespace + +SokolovKGaussJordanMPI::SokolovKGaussJordanMPI(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = 0; +} + +bool SokolovKGaussJordanMPI::ValidationImpl() { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + int valid = 0; + if (rank == 0) { + valid = (GetInput() > 0 && GetOutput() == 0) ? 1 : 0; + } + MPI_Bcast(&valid, 1, MPI_INT, 0, MPI_COMM_WORLD); + return valid == 1; +} + +bool SokolovKGaussJordanMPI::PreProcessingImpl() { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + n_ = GetInput(); + MPI_Bcast(&n_, 1, MPI_INT, 0, MPI_COMM_WORLD); + if (n_ <= 0) { + return false; + } + solution_.assign(n_, 0.0); + if (rank == 0) { + GenerateSystem(n_, matrix_); + original_matrix_ = matrix_; + } + return true; +} + +bool SokolovKGaussJordanMPI::RunImpl() { + if (n_ <= 0) { + return false; + } + + int rank = 0; + int world_size = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &world_size); + + if (rank == 0) { + matrix_ = original_matrix_; + } + + int cols = n_ + 1; + std::vector rows_per_proc; + std::vector row_offsets; + std::vector sendcounts; + std::vector displs; + ComputeDistribution(n_, cols, world_size, rows_per_proc, row_offsets, sendcounts, displs); + + int local_rows = rows_per_proc[rank]; + int local_start = row_offsets[rank]; + std::vector local_data(static_cast(local_rows) * cols); + + MPI_Scatterv(matrix_.data(), sendcounts.data(), displs.data(), MPI_DOUBLE, local_data.data(), local_rows * cols, + MPI_DOUBLE, 0, MPI_COMM_WORLD); + + std::vector pivot_row(cols); + for (int k = 0; k < n_; k++) { + EliminationStep(local_data, pivot_row, local_rows, local_start, cols, k, rank, row_offsets, rows_per_proc, + world_size); + } + + CollectResults(local_data, matrix_, local_rows, cols, sendcounts, displs, world_size, rank); + + if (rank == 0) { + for (int i = 0; i < n_; i++) { + solution_[i] = matrix_[(i * cols) + n_]; + } + } + + return true; +} + +bool SokolovKGaussJordanMPI::PostProcessingImpl() { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + int valid = 1; + if (rank == 0) { + const double eps = 1e-6; + for (int i = 0; i < n_; i++) { + if (std::abs(solution_[i] - (i + 1.0)) > eps) { + valid = 0; + break; + } + } + } + MPI_Bcast(&valid, 1, MPI_INT, 0, MPI_COMM_WORLD); + if (valid == 0) { + return false; + } + GetOutput() = GetInput(); + return true; +} + +} // namespace sokolov_k_gauss_jordan diff --git a/tasks/sokolov_k_gauss_jordan/report.md b/tasks/sokolov_k_gauss_jordan/report.md new file mode 100644 index 00000000..20dcbf06 --- /dev/null +++ b/tasks/sokolov_k_gauss_jordan/report.md @@ -0,0 +1,182 @@ +# Метод Гаусса-Жордана (решение СЛАУ) + +- Студент: Соколов Кирилл Денисович, группа 3823Б1ПР4 +- Группа: 3823Б1ПР4 +- Технология: SEQ, MPI +- Вариант: 17 + +## 1. Введение + +Решение систем линейных алгебраических уравнений (СЛАУ) относится к базовым задачам вычислительной математики. +Метод Гаусса-Жордана приводит расширенную матрицу системы к приведенному ступенчатому виду, после чего решение +считывается из последнего столбца. Цель работы: реализовать последовательный и параллельный (MPI) варианты метода, +провести замеры производительности и оценить ускорение и эффективность при разном числе процессов. + +## 2. Постановка задачи + +Дана система линейных уравнений Ax = b с квадратной матрицей A размера n x n и вектором правых частей b. +Требуется найти вектор x. + +- Вход: размер системы n (целое, n > 0). Матрица и вектор b формируются внутри программы детерминированно по n + для воспроизводимости тестов. +- Выход: признак успешного решения; при успехе вектор x проверяется на совпадение с эталонным решением + (x[i] = i + 1). +- Ограничения: матрица строится диагонально доминантной, чтобы метод был устойчив без выбора главного элемента + по столбцу в параллельной версии. + +## 3. Базовый алгоритм + +Используется метод Гаусса-Жордана с частичным выбором главного элемента по столбцу. + +1. Расширенная матрица [A | b] хранится по строкам в одномерном массиве размера n * (n+1). +2. Для каждого столбца k = 0, 1, ..., n-1: + - Выбор главного элемента: в столбце k среди строк с номерами k, k+1, ..., n-1 ищется строка с максимальным + по модулю элементом; эта строка меняется местами со строкой k. + - Нормировка строки k: деление строки k на диагональный элемент a_kk. + - Исключение столбца k во всех остальных строках: для каждой строки i != k выполняется вычитание из строки i + строки k, умноженной на a_ik, так что в столбце k получаются нули. +3. После n шагов в первых n столбцах получается единичная матрица, решение находится в (n+1)-м столбце. + +Выбор главного элемента повышает устойчивость к ошибкам округления. Сложность последовательного алгоритма +O(n^3). + +## 4. Схема распараллеливания (MPI) + +Используется горизонтальная схема разбиения: матрица делится между процессами по строкам. Каждый процесс хранит +свой блок строк расширенной матрицы. + +- Распределение данных: процесс с рангом 0 генерирует полную матрицу [A | b], затем с помощью MPI_Scatterv + распределяет строки по процессам (каждому процессу передается свое количество строк и смещение). Число строк на + процесс вычисляется так, чтобы разница между максимальным и минимальным количеством не превышала 1. +- Роли процессов: для каждого шага k определяется процесс-владелец строки k (тот, в чей блок попадает строка k). + Этот процесс нормирует свою строку k и рассылает ее всем процессам через MPI_Bcast. Все процессы выполняют + исключение столбца k в своих локальных строках. +- Сбор результата: после всех шагов процесс 0 собирает обновленные блоки строк с остальных процессов с помощью + MPI_Send (неранговые процессы отправляют свои блоки рангу 0). Ранг 0 формирует полную матрицу и извлекает вектор + решения из последнего столбца. +- Топология: линейная (COMM_WORLD); коллективные операции Bcast и Scatterv, точечные Send/Recv при сборе. + +## 5. Детали реализации + +- Структура кода: последовательная версия в seq/src/ops_seq.cpp (класс SokolovKGaussJordanSEQ), параллельная в + mpi/src/ops_mpi.cpp (SokolovKGaussJordanMPI). Общие типы и базовый класс в common/include/common.hpp. Генерация + системы вынесена в анонимные функции GenerateSystem в обоих файлах. +- Ключевые функции SEQ: GenerateSystem (построение диагонально доминантной матрицы и правой части), GaussJordanStep + (один шаг метода с выбором главного элемента и исключением). +- Ключевые функции MPI: ComputeDistribution (подсчет строк и смещений по процессам), FindRowOwner (определение + владельца строки k), EliminationStep (нормировка строки владельцем, Bcast, исключение в локальных строках), + CollectResults (сбор блоков на ранге 0 через MPI_Send/Recv). +- Угловые случаи: n <= 0 обрабатывается в ValidationImpl и PreProcessingImpl; при большом числе процессов часть + процессов может получить 0 строк, циклы и обмены с нулевым размером учтены. +- Память: на ранге 0 хранится полная матрица до и после сбора; на остальных рангах только локальный блок строк. + +## 6. Экспериментальная установка + +- CPU: Intel Core i5-10400kf +- Ядра/Потоки: 6/12 +- ОС: Windows 10 +- Компилятор: MSVC 14.44 +- Тип сборки: Release +- MPI: MS_MPI 10.0 +- CMake: 4.2.0-rc-1 +- Тестирование: Google Test +- Данные: тестовая система генерируется по размеру n детерминированным генератором (линейный конгруэнтный + генератор с фиксированным зерном от n); эталонное решение x[i] = i + 1 + +## 7. Результаты и обсуждение + +### 7.1 Проверка корректности + +Корректность проверяется функциональными тестами: для заданного n строится система с известным решением x[i] = i + 1, +после решения вычисленное решение сравнивается с эталоном с допуском 1e-6. Тесты прогоняются для последовательной и +MPI-реализаций на разных n (включая малые и большие размеры). Успешное прохождение CI (включая функциональные и +статический анализ) подтверждает корректность реализации. + +### 7.2 Производительность + +Режим task_run: замеряется время многократного выполнения только этапа Run (без повторной генерации данных и +постобработки). Режим task_pipeline: замеряется время полного прохода Validation, PreProcessing, Run, +PostProcessing. + +Таблица task_run: + +| Процессов | Время, с | Ускорение | Эффективность | +|-----------|----------|-----------|---------------| +| 1 (seq) | 1.1558 | 1.00 | - | +| 2 | 1.1481 | 1.01 | 50.3% | +| 4 | 0.6078 | 1.90 | 47.6% | +| 6 | 0.4532 | 2.55 | 42.5% | +| 8 | 0.4673 | 2.48 | 30.9% | + +Таблица task_pipeline: + +| Процессов | Время, с | Ускорение | Эффективность | +|-----------|----------|-----------|---------------| +| 1 (seq) | 1.1623 | 1.00 | - | +| 2 | 1.1570 | 1.00 | 50.2% | +| 4 | 0.6145 | 1.89 | 47.3% | +| 6 | 0.4965 | 2.34 | 39.0% | +| 8 | 0.4747 | 2.45 | 30.6% | + +Ускорение вычислено относительно последовательного варианта (1 процесс); эффективность - как (Ускорение / число +процессов) * 100%. + +Основные факторы, ограничивающие масштабируемость: накладные расходы MPI (Bcast на каждом из n шагов, Scatterv в +начале и сбор в конце), дисбаланс нагрузки при малом n и большом числе процессов. + +## 8. Выводы + +Реализованы последовательный и параллельный (MPI) варианты метода Гаусса-Жордана с горизонтальным разбиением матрицы +по строкам. Последовательный вариант использует частичный выбор главного элемента; параллельный распределяет строки +через MPI_Scatterv и собирает результат через MPI_Send. Замеры на 2, 4, 6 и 8 процессах позволяют оценить ускорение и +эффективность для режимов task_run и task_pipeline. Ожидаемо, task_run дает лучшее ускорение за счет отсутствия +повторной генерации данных. Рост накладных расходов при увеличении числа процессов ограничивает эффективность при +фиксированном размере задачи. + +## 9. Источники + +1. Бахвалов Н.С., Жидков Н.П., Кобельков Г.М. Численные методы. М.: БИНОМ. Лаборатория знаний, 2015. +2. Антонов А.С. Параллельное программирование с использованием технологии MPI. М.: Изд-во МГУ, 2004. +3. Message Passing Interface Forum. MPI: A Message-Passing Interface Standard. Version 4.0. 2021. +4. Gropp W., Lusk E., Skjellum A. Using MPI: Portable Parallel Programming with the Message Passing Interface. 3rd ed. + MIT Press, 2014. +5. Калиткин Н.Н. Численные методы. М.: Наука, 1978. + +## Приложение + +Последовательный шаг метода (нормировка строки k и исключение столбца k): + +```cpp +void GaussJordanStep(std::vector &matrix, int n, int cols, int k) { + int max_row = k; + double max_val = std::abs(matrix[(k * cols) + k]); + for (int i = k + 1; i < n; i++) { + double val = std::abs(matrix[(i * cols) + k]); + if (val > max_val) { max_val = val; max_row = i; } + } + if (max_row != k) { /* обмен строк k и max_row */ } + double pivot = matrix[(k * cols) + k]; + for (int j = k; j < cols; j++) matrix[(k * cols) + j] /= pivot; + for (int i = 0; i < n; i++) + if (i != k) { + double factor = matrix[(i * cols) + k]; + for (int j = k; j < cols; j++) + matrix[(i * cols) + j] -= factor * matrix[(k * cols) + j]; + } +} +``` + +Параллельный шаг: владелец строки k нормирует ее и рассылает, все процессы исключают столбец k в своих +строках: + +```cpp +MPI_Bcast(pivot_row.data(), cols, MPI_DOUBLE, owner, MPI_COMM_WORLD); +for (int i = 0; i < local_rows; i++) { + int gi = local_start + i; + if (gi != k) { + double factor = local_data[(i * cols) + k]; + for (int j = 0; j < cols; j++) + local_data[(i * cols) + j] -= factor * pivot_row[j]; + } +} +``` diff --git a/tasks/sokolov_k_gauss_jordan/seq/include/ops_seq.hpp b/tasks/sokolov_k_gauss_jordan/seq/include/ops_seq.hpp new file mode 100644 index 00000000..183db925 --- /dev/null +++ b/tasks/sokolov_k_gauss_jordan/seq/include/ops_seq.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include "sokolov_k_gauss_jordan/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace sokolov_k_gauss_jordan { + +class SokolovKGaussJordanSEQ : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kSEQ; + } + explicit SokolovKGaussJordanSEQ(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + int n_{}; + std::vector matrix_; + std::vector original_matrix_; + std::vector solution_; +}; + +} // namespace sokolov_k_gauss_jordan diff --git a/tasks/sokolov_k_gauss_jordan/seq/src/ops_seq.cpp b/tasks/sokolov_k_gauss_jordan/seq/src/ops_seq.cpp new file mode 100644 index 00000000..7a7df101 --- /dev/null +++ b/tasks/sokolov_k_gauss_jordan/seq/src/ops_seq.cpp @@ -0,0 +1,125 @@ +#include "sokolov_k_gauss_jordan/seq/include/ops_seq.hpp" + +#include +#include +#include + +#include "sokolov_k_gauss_jordan/common/include/common.hpp" + +namespace sokolov_k_gauss_jordan { + +namespace { + +void GenerateSystem(int n, std::vector &matrix) { + int cols = n + 1; + matrix.assign(static_cast(n) * cols, 0.0); + unsigned int seed = static_cast(n) * 1234567U; + auto gen = [&seed]() -> double { + seed = (seed * 1103515245U) + 12345U; + return static_cast(((seed >> 16) % 100) + 1); + }; + for (int i = 0; i < n; i++) { + double row_sum = 0.0; + for (int j = 0; j < n; j++) { + if (i != j) { + matrix[(i * cols) + j] = gen() / 100.0; + row_sum += std::abs(matrix[(i * cols) + j]); + } + } + matrix[(i * cols) + i] = row_sum + gen(); + } + for (int i = 0; i < n; i++) { + double b_val = 0.0; + for (int j = 0; j < n; j++) { + b_val += matrix[(i * cols) + j] * (j + 1.0); + } + matrix[(i * cols) + n] = b_val; + } +} + +void GaussJordanStep(std::vector &matrix, int n, int cols, int k) { + int max_row = k; + double max_val = std::abs(matrix[(k * cols) + k]); + for (int i = k + 1; i < n; i++) { + double val = std::abs(matrix[(i * cols) + k]); + if (val > max_val) { + max_val = val; + max_row = i; + } + } + + if (max_row != k) { + for (int j = 0; j < cols; j++) { + double tmp = matrix[(k * cols) + j]; + matrix[(k * cols) + j] = matrix[(max_row * cols) + j]; + matrix[(max_row * cols) + j] = tmp; + } + } + + double pivot = matrix[(k * cols) + k]; + for (int j = k; j < cols; j++) { + matrix[(k * cols) + j] /= pivot; + } + + for (int i = 0; i < n; i++) { + if (i != k) { + double factor = matrix[(i * cols) + k]; + for (int j = k; j < cols; j++) { + matrix[(i * cols) + j] -= factor * matrix[(k * cols) + j]; + } + } + } +} + +} // namespace + +SokolovKGaussJordanSEQ::SokolovKGaussJordanSEQ(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = 0; +} + +bool SokolovKGaussJordanSEQ::ValidationImpl() { + return GetInput() > 0 && GetOutput() == 0; +} + +bool SokolovKGaussJordanSEQ::PreProcessingImpl() { + n_ = GetInput(); + if (n_ <= 0) { + return false; + } + GenerateSystem(n_, matrix_); + original_matrix_ = matrix_; + solution_.assign(n_, 0.0); + return true; +} + +bool SokolovKGaussJordanSEQ::RunImpl() { + if (n_ <= 0) { + return false; + } + matrix_ = original_matrix_; + int cols = n_ + 1; + + for (int k = 0; k < n_; k++) { + GaussJordanStep(matrix_, n_, cols, k); + } + + for (int i = 0; i < n_; i++) { + solution_[i] = matrix_[(i * cols) + n_]; + } + return true; +} + +bool SokolovKGaussJordanSEQ::PostProcessingImpl() { + const double eps = 1e-6; + for (int i = 0; i < n_; i++) { + if (std::abs(solution_[i] - (i + 1.0)) > eps) { + return false; + } + } + GetOutput() = GetInput(); + return true; +} + +} // namespace sokolov_k_gauss_jordan diff --git a/tasks/sokolov_k_gauss_jordan/settings.json b/tasks/sokolov_k_gauss_jordan/settings.json new file mode 100644 index 00000000..16f25e42 --- /dev/null +++ b/tasks/sokolov_k_gauss_jordan/settings.json @@ -0,0 +1,7 @@ +{ + "tasks": { + "mpi": "enabled", + "seq": "enabled" + }, + "tasks_type": "processes" +} diff --git a/tasks/sokolov_k_gauss_jordan/tests/.clang-tidy b/tasks/sokolov_k_gauss_jordan/tests/.clang-tidy new file mode 100644 index 00000000..ef43b7aa --- /dev/null +++ b/tasks/sokolov_k_gauss_jordan/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/sokolov_k_gauss_jordan/tests/functional/main.cpp b/tasks/sokolov_k_gauss_jordan/tests/functional/main.cpp new file mode 100644 index 00000000..e6a01359 --- /dev/null +++ b/tasks/sokolov_k_gauss_jordan/tests/functional/main.cpp @@ -0,0 +1,225 @@ +#include + +#include +#include +#include +#include +#include + +#include "sokolov_k_gauss_jordan/common/include/common.hpp" +#include "sokolov_k_gauss_jordan/mpi/include/ops_mpi.hpp" +#include "sokolov_k_gauss_jordan/seq/include/ops_seq.hpp" +#include "util/include/func_test_util.hpp" +#include "util/include/util.hpp" + +namespace sokolov_k_gauss_jordan { + +class SokolovKGaussJordanFuncTests : public ppc::util::BaseRunFuncTests { + public: + static std::string PrintTestParam(const TestType &test_param) { + return std::to_string(std::get<0>(test_param)) + "_" + std::get<1>(test_param); + } + + 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 { + return (input_data_ == output_data); + } + + InType GetTestInputData() final { + return input_data_; + } + + private: + InType input_data_ = 0; +}; + +namespace { + +TEST_P(SokolovKGaussJordanFuncTests, MatmulFromPic) { + ExecuteTest(GetParam()); +} + +const std::array kTestParam = {std::make_tuple(3, "3"), std::make_tuple(5, "5"), std::make_tuple(7, "7")}; + +const auto kTestTasksList = std::tuple_cat( + ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_sokolov_k_gauss_jordan), + ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_sokolov_k_gauss_jordan)); + +const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); + +const auto kPerfTestName = SokolovKGaussJordanFuncTests::PrintFuncTestName; + +INSTANTIATE_TEST_SUITE_P(PicMatrixTests, SokolovKGaussJordanFuncTests, kGtestValues, kPerfTestName); + +TEST(SokolovKGaussJordanSEQFunc, SolveSize1) { + auto task = std::make_shared(1); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + EXPECT_EQ(task->GetOutput(), 1); +} + +TEST(SokolovKGaussJordanSEQFunc, SolveSize2) { + auto task = std::make_shared(2); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + EXPECT_EQ(task->GetOutput(), 2); +} + +TEST(SokolovKGaussJordanSEQFunc, SolveSize4) { + auto task = std::make_shared(4); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + EXPECT_EQ(task->GetOutput(), 4); +} + +TEST(SokolovKGaussJordanSEQFunc, SolveSize10) { + auto task = std::make_shared(10); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + EXPECT_EQ(task->GetOutput(), 10); +} + +TEST(SokolovKGaussJordanSEQFunc, SolveSize50) { + auto task = std::make_shared(50); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + EXPECT_EQ(task->GetOutput(), 50); +} + +TEST(SokolovKGaussJordanSEQFunc, SolveSize100) { + auto task = std::make_shared(100); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + EXPECT_EQ(task->GetOutput(), 100); +} + +TEST(SokolovKGaussJordanSEQFunc, ValidationFailsZero) { + auto task = std::make_shared(0); + EXPECT_FALSE(task->Validation()); + task->PreProcessing(); + task->Run(); + task->PostProcessing(); +} + +TEST(SokolovKGaussJordanSEQFunc, ValidationFailsNegative) { + auto task = std::make_shared(-5); + EXPECT_FALSE(task->Validation()); + task->PreProcessing(); + task->Run(); + task->PostProcessing(); +} + +TEST(SokolovKGaussJordanMPIFunc, SolveSize1) { + if (!ppc::util::IsUnderMpirun()) { + GTEST_SKIP(); + } + auto task = std::make_shared(1); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + EXPECT_EQ(task->GetOutput(), 1); +} + +TEST(SokolovKGaussJordanMPIFunc, SolveSize2) { + if (!ppc::util::IsUnderMpirun()) { + GTEST_SKIP(); + } + auto task = std::make_shared(2); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + EXPECT_EQ(task->GetOutput(), 2); +} + +TEST(SokolovKGaussJordanMPIFunc, SolveSize4) { + if (!ppc::util::IsUnderMpirun()) { + GTEST_SKIP(); + } + auto task = std::make_shared(4); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + EXPECT_EQ(task->GetOutput(), 4); +} + +TEST(SokolovKGaussJordanMPIFunc, SolveSize10) { + if (!ppc::util::IsUnderMpirun()) { + GTEST_SKIP(); + } + auto task = std::make_shared(10); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + EXPECT_EQ(task->GetOutput(), 10); +} + +TEST(SokolovKGaussJordanMPIFunc, SolveSize50) { + if (!ppc::util::IsUnderMpirun()) { + GTEST_SKIP(); + } + auto task = std::make_shared(50); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + EXPECT_EQ(task->GetOutput(), 50); +} + +TEST(SokolovKGaussJordanMPIFunc, SolveSize100) { + if (!ppc::util::IsUnderMpirun()) { + GTEST_SKIP(); + } + auto task = std::make_shared(100); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + EXPECT_EQ(task->GetOutput(), 100); +} + +TEST(SokolovKGaussJordanMPIFunc, ValidationFailsZero) { + if (!ppc::util::IsUnderMpirun()) { + GTEST_SKIP(); + } + auto task = std::make_shared(0); + EXPECT_FALSE(task->Validation()); + task->PreProcessing(); + task->Run(); + task->PostProcessing(); +} + +TEST(SokolovKGaussJordanMPIFunc, ValidationFailsNegative) { + if (!ppc::util::IsUnderMpirun()) { + GTEST_SKIP(); + } + auto task = std::make_shared(-5); + EXPECT_FALSE(task->Validation()); + task->PreProcessing(); + task->Run(); + task->PostProcessing(); +} + +} // namespace + +} // namespace sokolov_k_gauss_jordan diff --git a/tasks/sokolov_k_gauss_jordan/tests/performance/main.cpp b/tasks/sokolov_k_gauss_jordan/tests/performance/main.cpp new file mode 100644 index 00000000..4641b739 --- /dev/null +++ b/tasks/sokolov_k_gauss_jordan/tests/performance/main.cpp @@ -0,0 +1,40 @@ +#include + +#include "sokolov_k_gauss_jordan/common/include/common.hpp" +#include "sokolov_k_gauss_jordan/mpi/include/ops_mpi.hpp" +#include "sokolov_k_gauss_jordan/seq/include/ops_seq.hpp" +#include "util/include/perf_test_util.hpp" + +namespace sokolov_k_gauss_jordan { + +class SokolovKGaussJordanPerfTests : public ppc::util::BaseRunPerfTests { + const int kCount_ = 100; + InType input_data_{}; + + void SetUp() override { + input_data_ = kCount_ * 9; + } + + bool CheckTestOutputData(OutType &output_data) final { + return input_data_ == output_data; + } + + InType GetTestInputData() final { + return input_data_; + } +}; + +TEST_P(SokolovKGaussJordanPerfTests, RunPerfModes) { + ExecuteTest(GetParam()); +} + +const auto kAllPerfTasks = ppc::util::MakeAllPerfTasks( + PPC_SETTINGS_sokolov_k_gauss_jordan); + +const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks); + +const auto kPerfTestName = SokolovKGaussJordanPerfTests::CustomPerfTestName; + +INSTANTIATE_TEST_SUITE_P(RunModeTests, SokolovKGaussJordanPerfTests, kGtestValues, kPerfTestName); + +} // namespace sokolov_k_gauss_jordan