diff --git a/tasks/borunov_v_complex_ccs/common/include/common.hpp b/tasks/borunov_v_complex_ccs/common/include/common.hpp new file mode 100644 index 00000000..07cfd754 --- /dev/null +++ b/tasks/borunov_v_complex_ccs/common/include/common.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include + +#include "task/include/task.hpp" + +namespace borunov_v_complex_ccs_seq { + +struct SparseMatrix { + int num_rows = 0; + int num_cols = 0; + std::vector> values; + std::vector row_indices; + std::vector col_ptrs; + + bool operator==(const SparseMatrix &other) const { + return num_rows == other.num_rows && num_cols == other.num_cols && values == other.values && + row_indices == other.row_indices && col_ptrs == other.col_ptrs; + } +}; + +using InType = std::vector; +using OutType = std::vector; +using TestType = std::tuple; +using BaseTask = ppc::task::Task; + +} // namespace borunov_v_complex_ccs_seq diff --git a/tasks/borunov_v_complex_ccs/info.json b/tasks/borunov_v_complex_ccs/info.json new file mode 100644 index 00000000..1ce9ec05 --- /dev/null +++ b/tasks/borunov_v_complex_ccs/info.json @@ -0,0 +1,9 @@ +{ + "student": { + "first_name": "Владислав", + "group_number": "3823Б1ПР3", + "last_name": "Борунов", + "middle_name": "Алексеевич", + "task_number": "1" + } +} diff --git a/tasks/borunov_v_complex_ccs/seq/include/ops_seq.hpp b/tasks/borunov_v_complex_ccs/seq/include/ops_seq.hpp new file mode 100644 index 00000000..c89e5c2b --- /dev/null +++ b/tasks/borunov_v_complex_ccs/seq/include/ops_seq.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "borunov_v_complex_ccs/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace borunov_v_complex_ccs_seq { + +class BorunovVComplexCcsSEQ : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kSEQ; + } + explicit BorunovVComplexCcsSEQ(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; +}; + +} // namespace borunov_v_complex_ccs_seq diff --git a/tasks/borunov_v_complex_ccs/seq/src/ops_seq.cpp b/tasks/borunov_v_complex_ccs/seq/src/ops_seq.cpp new file mode 100644 index 00000000..124a6a25 --- /dev/null +++ b/tasks/borunov_v_complex_ccs/seq/src/ops_seq.cpp @@ -0,0 +1,97 @@ +#include "borunov_v_complex_ccs/seq/include/ops_seq.hpp" + +#include +#include +#include +#include +#include + +#include "borunov_v_complex_ccs/common/include/common.hpp" + +namespace borunov_v_complex_ccs_seq { + +BorunovVComplexCcsSEQ::BorunovVComplexCcsSEQ(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput().resize(1); +} + +bool BorunovVComplexCcsSEQ::ValidationImpl() { + if (GetInput().size() != 2) { + return false; + } + const auto &a = GetInput()[0]; + const auto &b = GetInput()[1]; + if (a.num_cols != b.num_rows) { + return false; + } + if (a.col_ptrs.size() != static_cast(a.num_cols) + 1 || + b.col_ptrs.size() != static_cast(b.num_cols) + 1) { + return false; + } + return true; +} + +bool BorunovVComplexCcsSEQ::PreProcessingImpl() { + const auto &a = GetInput()[0]; + const auto &b = GetInput()[1]; + auto &c = GetOutput()[0]; + + c.num_rows = a.num_rows; + c.num_cols = b.num_cols; + c.col_ptrs.assign(c.num_cols + 1, 0); + c.values.clear(); + c.row_indices.clear(); + + return true; +} + +bool BorunovVComplexCcsSEQ::RunImpl() { + const auto &a = GetInput()[0]; + const auto &b = GetInput()[1]; + auto &c = GetOutput()[0]; + + std::vector> col_accumulator(a.num_rows, {0.0, 0.0}); + std::vector non_zero_indices; + std::vector is_non_zero(a.num_rows, false); + + for (int j = 0; j < b.num_cols; ++j) { + for (int b_idx = b.col_ptrs[j]; b_idx < b.col_ptrs[j + 1]; ++b_idx) { + int p = b.row_indices[b_idx]; + std::complex b_val = b.values[b_idx]; + + for (int a_idx = a.col_ptrs[p]; a_idx < a.col_ptrs[p + 1]; ++a_idx) { + int i = a.row_indices[a_idx]; + std::complex a_val = a.values[a_idx]; + + if (!is_non_zero[i]) { + is_non_zero[i] = true; + non_zero_indices.push_back(i); + } + col_accumulator[i] += a_val * b_val; + } + } + + std::ranges::sort(non_zero_indices); + + for (int i : non_zero_indices) { + if (std::abs(col_accumulator[i]) > 1e-9) { + c.values.push_back(col_accumulator[i]); + c.row_indices.push_back(i); + } + col_accumulator[i] = {0.0, 0.0}; + is_non_zero[i] = false; + } + non_zero_indices.clear(); + + c.col_ptrs[j + 1] = static_cast(c.values.size()); + } + + return true; +} + +bool BorunovVComplexCcsSEQ::PostProcessingImpl() { + return true; +} + +} // namespace borunov_v_complex_ccs_seq diff --git a/tasks/borunov_v_complex_ccs/settings.json b/tasks/borunov_v_complex_ccs/settings.json new file mode 100644 index 00000000..0be0208f --- /dev/null +++ b/tasks/borunov_v_complex_ccs/settings.json @@ -0,0 +1,10 @@ +{ + "tasks": { + "all": "enabled", + "omp": "enabled", + "seq": "enabled", + "stl": "enabled", + "tbb": "enabled" + }, + "tasks_type": "threads" +} diff --git a/tasks/borunov_v_complex_ccs/tests/functional/main.cpp b/tasks/borunov_v_complex_ccs/tests/functional/main.cpp new file mode 100644 index 00000000..a8f6f6cd --- /dev/null +++ b/tasks/borunov_v_complex_ccs/tests/functional/main.cpp @@ -0,0 +1,183 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "borunov_v_complex_ccs/common/include/common.hpp" +#include "borunov_v_complex_ccs/seq/include/ops_seq.hpp" +#include "util/include/func_test_util.hpp" +#include "util/include/util.hpp" + +namespace borunov_v_complex_ccs_seq { + +namespace { + +using DenseMatrix = std::vector>>; + +SparseMatrix GenerateRandomSparseMatrix(int num_rows, int num_cols, double sparsity) { + SparseMatrix mat; + mat.num_rows = num_rows; + mat.num_cols = num_cols; + mat.col_ptrs.assign(num_cols + 1, 0); + + const int sparsity_seed = static_cast(sparsity * 1000.0); + std::seed_seq seed{num_rows, num_cols, sparsity_seed}; + std::mt19937 gen(seed); + std::uniform_real_distribution dist_val(-10.0, 10.0); + std::uniform_real_distribution dist_prob(0.0, 1.0); + + for (int j = 0; j < num_cols; ++j) { + for (int i = 0; i < num_rows; ++i) { + if (dist_prob(gen) < sparsity) { + mat.values.emplace_back(dist_val(gen), dist_val(gen)); + mat.row_indices.push_back(i); + } + } + mat.col_ptrs[j + 1] = static_cast(mat.values.size()); + } + return mat; +} + +DenseMatrix BuildDense(const SparseMatrix &mat) { + DenseMatrix dense(mat.num_rows, std::vector>(mat.num_cols, {0.0, 0.0})); + for (int j = 0; j < mat.num_cols; ++j) { + for (int idx = mat.col_ptrs[j]; idx < mat.col_ptrs[j + 1]; ++idx) { + dense[mat.row_indices[idx]][j] = mat.values[idx]; + } + } + return dense; +} + +DenseMatrix MultiplyDenseMatrices(const DenseMatrix &left, const DenseMatrix &right) { + const int rows = static_cast(left.size()); + const int inner = rows == 0 ? 0 : static_cast(left[0].size()); + const int cols = right.empty() ? 0 : static_cast(right[0].size()); + DenseMatrix result(rows, std::vector>(cols, {0.0, 0.0})); + + for (int i = 0; i < rows; ++i) { + for (int j = 0; j < cols; ++j) { + for (int k = 0; k < inner; ++k) { + result[i][j] += left[i][k] * right[k][j]; + } + } + } + + return result; +} + +SparseMatrix BuildSparseFromDense(const DenseMatrix &dense) { + SparseMatrix c; + c.num_rows = static_cast(dense.size()); + c.num_cols = dense.empty() ? 0 : static_cast(dense[0].size()); + c.col_ptrs.assign(c.num_cols + 1, 0); + + for (int j = 0; j < c.num_cols; ++j) { + for (int i = 0; i < c.num_rows; ++i) { + if (std::abs(dense[i][j]) > 1e-9) { + c.values.push_back(dense[i][j]); + c.row_indices.push_back(i); + } + } + c.col_ptrs[j + 1] = static_cast(c.values.size()); + } + + return c; +} + +SparseMatrix MultiplyDense(const SparseMatrix &a, const SparseMatrix &b) { + DenseMatrix dense_a = BuildDense(a); + DenseMatrix dense_b = BuildDense(b); + DenseMatrix dense_c = MultiplyDenseMatrices(dense_a, dense_b); + return BuildSparseFromDense(dense_c); +} + +} // namespace + +class BorunovVRunFuncTestsThreads : public ppc::util::BaseRunFuncTests { + public: + static std::string PrintTestParam(const TestType &test_param) { + return std::to_string(std::get<0>(test_param)) + "_" + std::to_string(std::get<1>(test_param)) + "_" + + std::to_string(std::get<2>(test_param)); + } + + protected: + void SetUp() override { + TestType params = std::get(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); + int m = std::get<0>(params); + int k = std::get<1>(params); + int n = std::get<2>(params); + + SparseMatrix a = GenerateRandomSparseMatrix(m, k, 0.2); + SparseMatrix b = GenerateRandomSparseMatrix(k, n, 0.2); + + input_data_ = {a, b}; + expected_output_ = {MultiplyDense(a, b)}; + } + + bool CheckTestOutputData(OutType &output_data) final { + if (expected_output_.size() != output_data.size()) { + return false; + } + const auto &expected = expected_output_[0]; + const auto &actual = output_data[0]; + + if (expected.num_rows != actual.num_rows || expected.num_cols != actual.num_cols) { + return false; + } + if (expected.col_ptrs != actual.col_ptrs) { + return false; + } + if (expected.row_indices != actual.row_indices) { + return false; + } + if (expected.values.size() != actual.values.size()) { + return false; + } + + for (std::size_t i = 0; i < expected.values.size(); ++i) { + if (std::abs(expected.values[i] - actual.values[i]) > 1e-6) { + return false; + } + } + return true; + } + + InType GetTestInputData() final { + return input_data_; + } + + private: + InType input_data_; + OutType expected_output_; +}; + +namespace { + +TEST_P(BorunovVRunFuncTestsThreads, MatmulTests) { + ExecuteTest(GetParam()); +} + +const std::array kTestParam = { + std::make_tuple(10, 10, 10), + std::make_tuple(20, 15, 25), + std::make_tuple(5, 30, 5), +}; + +const auto kTestTasksList = std::tuple_cat( + ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_borunov_v_complex_ccs)); + +const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); + +const auto kPerfTestName = BorunovVRunFuncTestsThreads::PrintFuncTestName; + +INSTANTIATE_TEST_SUITE_P(SparseMatrixTests, BorunovVRunFuncTestsThreads, kGtestValues, kPerfTestName); + +} // namespace + +} // namespace borunov_v_complex_ccs_seq diff --git a/tasks/borunov_v_complex_ccs/tests/performance/main.cpp b/tasks/borunov_v_complex_ccs/tests/performance/main.cpp new file mode 100644 index 00000000..375e0fd4 --- /dev/null +++ b/tasks/borunov_v_complex_ccs/tests/performance/main.cpp @@ -0,0 +1,74 @@ +#include + +#include +#include + +#include "borunov_v_complex_ccs/common/include/common.hpp" +#include "borunov_v_complex_ccs/seq/include/ops_seq.hpp" +#include "util/include/perf_test_util.hpp" + +namespace borunov_v_complex_ccs_seq { + +namespace { + +SparseMatrix GenerateSparseMatrixPerf(int num_rows, int num_cols, int non_zeros_per_col) { + SparseMatrix mat; + mat.num_rows = num_rows; + mat.num_cols = num_cols; + mat.col_ptrs.assign(num_cols + 1, 0); + + int step = std::max(1, num_rows / non_zeros_per_col); + + for (int j = 0; j < num_cols; ++j) { + for (int i = 0; i < num_rows; i += step) { + mat.values.emplace_back(static_cast(i + 1), static_cast(j + 1)); + mat.row_indices.push_back(i); + } + mat.col_ptrs[j + 1] = static_cast(mat.values.size()); + } + return mat; +} + +} // namespace + +class BorunovVRunPerfTestThreads : public ppc::util::BaseRunPerfTests { + InType input_data_; + + void SetUp() override { + int m = 200000; + int k = 200000; + int n = 200000; + + SparseMatrix a = GenerateSparseMatrixPerf(m, k, 20); + SparseMatrix b = GenerateSparseMatrixPerf(k, n, 20); + + input_data_ = {a, b}; + } + + bool CheckTestOutputData(OutType &output_data) final { + return output_data.size() == 1; + } + + InType GetTestInputData() final { + return input_data_; + } +}; + +TEST_P(BorunovVRunPerfTestThreads, RunPerfModes) { + ExecuteTest(GetParam()); +} + +namespace { + +const auto kAllPerfTasks = + ppc::util::MakeAllPerfTasks(PPC_SETTINGS_borunov_v_complex_ccs); + +const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks); + +const auto kPerfTestName = BorunovVRunPerfTestThreads::CustomPerfTestName; + +INSTANTIATE_TEST_SUITE_P(RunModeTests, BorunovVRunPerfTestThreads, kGtestValues, kPerfTestName); + +} // namespace + +} // namespace borunov_v_complex_ccs_seq