diff --git a/tasks/mpi/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/func_tests/main_korobeinikov.cpp b/tasks/mpi/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/func_tests/main_korobeinikov.cpp new file mode 100644 index 00000000000..e896c8881c0 --- /dev/null +++ b/tasks/mpi/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/func_tests/main_korobeinikov.cpp @@ -0,0 +1,598 @@ +// Copyright 2024 Korobeinikov Arseny +#include + +#include +#include +#include + +#include "mpi/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/include/ops_mpi_korobeinikov.hpp" + +namespace korobeinikov_a_test_task_mpi_lab_02 { + +std::vector getRandomVector(int sz) { + std::random_device dev; + std::mt19937 gen(dev()); + std::vector vec(sz); + for (int i = 0; i < sz; i++) { + vec[i] = gen() % 100; + } + return vec; +} + +} // namespace korobeinikov_a_test_task_mpi_lab_02 + +TEST(korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B, + Test_1_determinate_matrix_3_x_1_and_1_x_3_to_prove_correctness_seq_and_mpi) { + boost::mpi::communicator world; + // Create data + std::vector A = {3, 2, 1}; + std::vector B = {1, 2, 3}; + int count_rows_A = 3; + int count_cols_A = 1; + int count_rows_B = 1; + int count_cols_B = 3; + + std::vector out_mpi(9, 0); + std::vector right_answer = {3, 6, 9, 2, 4, 6, 1, 2, 3}; + int count_rows_out_mpi = 0; + int count_cols_out_mpi = 0; + int count_rows_RA = 3; + int count_cols_RA = 3; + + // Create TaskData + std::shared_ptr taskDataPar = std::make_shared(); + + if (world.rank() == 0) { + // Create TaskData + taskDataPar->inputs.emplace_back(reinterpret_cast(A.data())); + taskDataPar->inputs_count.emplace_back(A.size()); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_rows_A)); + taskDataPar->inputs_count.emplace_back(1); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_cols_A)); + taskDataPar->inputs_count.emplace_back(1); + + taskDataPar->inputs.emplace_back(reinterpret_cast(B.data())); + taskDataPar->inputs_count.emplace_back(B.size()); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_rows_B)); + taskDataPar->inputs_count.emplace_back(1); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_cols_B)); + taskDataPar->inputs_count.emplace_back(1); + + taskDataPar->outputs.emplace_back(reinterpret_cast(out_mpi.data())); + taskDataPar->outputs_count.emplace_back(out_mpi.size()); + taskDataPar->outputs.emplace_back(reinterpret_cast(&count_rows_out_mpi)); + taskDataPar->outputs_count.emplace_back(1); + taskDataPar->outputs.emplace_back(reinterpret_cast(&count_cols_out_mpi)); + taskDataPar->outputs_count.emplace_back(1); + } + + korobeinikov_a_test_task_mpi_lab_02::TestMPITaskParallel testMpiTaskParallel(taskDataPar); + ASSERT_EQ(testMpiTaskParallel.validation(), true); + testMpiTaskParallel.pre_processing(); + testMpiTaskParallel.run(); + testMpiTaskParallel.post_processing(); + + if (world.rank() == 0) { + // Create data + std::vector out_seq(9, 0); + int count_rows_out_seq = 0; + int count_cols_out_seq = 0; + + // Create TaskData + std::shared_ptr taskDataSeq = std::make_shared(); + taskDataSeq->inputs.emplace_back(reinterpret_cast(A.data())); + taskDataSeq->inputs_count.emplace_back(A.size()); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_rows_A)); + taskDataSeq->inputs_count.emplace_back(1); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_cols_A)); + taskDataSeq->inputs_count.emplace_back(1); + + taskDataSeq->inputs.emplace_back(reinterpret_cast(B.data())); + taskDataSeq->inputs_count.emplace_back(B.size()); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_rows_B)); + taskDataSeq->inputs_count.emplace_back(1); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_cols_B)); + taskDataSeq->inputs_count.emplace_back(1); + + taskDataSeq->outputs.emplace_back(reinterpret_cast(out_seq.data())); + taskDataSeq->outputs_count.emplace_back(out_seq.size()); + taskDataSeq->outputs.emplace_back(reinterpret_cast(&count_rows_out_seq)); + taskDataSeq->outputs_count.emplace_back(1); + taskDataSeq->outputs.emplace_back(reinterpret_cast(&count_cols_out_seq)); + taskDataSeq->outputs_count.emplace_back(1); + + // Create Task + korobeinikov_a_test_task_mpi_lab_02::TestMPITaskSequential testMpiTaskSequential(taskDataSeq); + ASSERT_EQ(testMpiTaskSequential.validation(), true); + testMpiTaskSequential.pre_processing(); + testMpiTaskSequential.run(); + testMpiTaskSequential.post_processing(); + + for (size_t i = 0; i < right_answer.size(); i++) { + ASSERT_EQ(right_answer[i], out_seq[i]); + } + for (size_t i = 0; i < right_answer.size(); i++) { + ASSERT_EQ(right_answer[i], out_mpi[i]); + } + ASSERT_EQ(count_rows_out_seq, count_rows_RA); + ASSERT_EQ(count_cols_out_seq, count_cols_RA); + ASSERT_EQ(count_rows_out_mpi, count_rows_RA); + ASSERT_EQ(count_cols_out_mpi, count_cols_RA); + } +} + +TEST(korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B, + Test_2_determinate_matrix_3_x_3_and_3_x_2_to_prove_correctness_seq_and_mpi) { + boost::mpi::communicator world; + // Create data + std::vector A = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector B = {1, 2, 3, 4, 5, 6}; + int count_rows_A = 3; + int count_cols_A = 3; + int count_rows_B = 3; + int count_cols_B = 2; + + std::vector out_mpi(6, 0); + std::vector right_answer = {22, 28, 49, 64, 76, 100}; + int count_rows_out_mpi = 0; + int count_cols_out_mpi = 0; + int count_rows_RA = 3; + int count_cols_RA = 2; + + // Create TaskData + std::shared_ptr taskDataPar = std::make_shared(); + + if (world.rank() == 0) { + // Create TaskData + taskDataPar->inputs.emplace_back(reinterpret_cast(A.data())); + taskDataPar->inputs_count.emplace_back(A.size()); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_rows_A)); + taskDataPar->inputs_count.emplace_back(1); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_cols_A)); + taskDataPar->inputs_count.emplace_back(1); + + taskDataPar->inputs.emplace_back(reinterpret_cast(B.data())); + taskDataPar->inputs_count.emplace_back(B.size()); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_rows_B)); + taskDataPar->inputs_count.emplace_back(1); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_cols_B)); + taskDataPar->inputs_count.emplace_back(1); + + taskDataPar->outputs.emplace_back(reinterpret_cast(out_mpi.data())); + taskDataPar->outputs_count.emplace_back(out_mpi.size()); + taskDataPar->outputs.emplace_back(reinterpret_cast(&count_rows_out_mpi)); + taskDataPar->outputs_count.emplace_back(1); + taskDataPar->outputs.emplace_back(reinterpret_cast(&count_cols_out_mpi)); + taskDataPar->outputs_count.emplace_back(1); + } + + korobeinikov_a_test_task_mpi_lab_02::TestMPITaskParallel testMpiTaskParallel(taskDataPar); + ASSERT_EQ(testMpiTaskParallel.validation(), true); + testMpiTaskParallel.pre_processing(); + testMpiTaskParallel.run(); + testMpiTaskParallel.post_processing(); + + if (world.rank() == 0) { + // Create data + std::vector out_seq(6, 0); + int count_rows_out_seq = 0; + int count_cols_out_seq = 0; + + // Create TaskData + std::shared_ptr taskDataSeq = std::make_shared(); + taskDataSeq->inputs.emplace_back(reinterpret_cast(A.data())); + taskDataSeq->inputs_count.emplace_back(A.size()); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_rows_A)); + taskDataSeq->inputs_count.emplace_back(1); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_cols_A)); + taskDataSeq->inputs_count.emplace_back(1); + + taskDataSeq->inputs.emplace_back(reinterpret_cast(B.data())); + taskDataSeq->inputs_count.emplace_back(B.size()); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_rows_B)); + taskDataSeq->inputs_count.emplace_back(1); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_cols_B)); + taskDataSeq->inputs_count.emplace_back(1); + + taskDataSeq->outputs.emplace_back(reinterpret_cast(out_seq.data())); + taskDataSeq->outputs_count.emplace_back(out_seq.size()); + taskDataSeq->outputs.emplace_back(reinterpret_cast(&count_rows_out_seq)); + taskDataSeq->outputs_count.emplace_back(1); + taskDataSeq->outputs.emplace_back(reinterpret_cast(&count_cols_out_seq)); + taskDataSeq->outputs_count.emplace_back(1); + + // Create Task + korobeinikov_a_test_task_mpi_lab_02::TestMPITaskSequential testMpiTaskSequential(taskDataSeq); + ASSERT_EQ(testMpiTaskSequential.validation(), true); + testMpiTaskSequential.pre_processing(); + testMpiTaskSequential.run(); + testMpiTaskSequential.post_processing(); + + for (size_t i = 0; i < right_answer.size(); i++) { + ASSERT_EQ(right_answer[i], out_seq[i]); + } + for (size_t i = 0; i < right_answer.size(); i++) { + ASSERT_EQ(right_answer[i], out_mpi[i]); + } + ASSERT_EQ(count_rows_out_seq, count_rows_RA); + ASSERT_EQ(count_cols_out_seq, count_cols_RA); + ASSERT_EQ(count_rows_out_mpi, count_rows_RA); + ASSERT_EQ(count_cols_out_mpi, count_cols_RA); + } +} + +TEST(korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B, + Test_3_determinate_matrix_4_x_4_and_4_x_4_to_prove_correctness_seq_and_mpi) { + boost::mpi::communicator world; + + // Create data + std::vector A = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + std::vector B = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + int count_rows_A = 4; + int count_cols_A = 4; + int count_rows_B = 4; + int count_cols_B = 4; + + std::vector out_mpi(16, 0); + int count_rows_out_mpi = 0; + int count_cols_out_mpi = 0; + + // Create TaskData + std::shared_ptr taskDataPar = std::make_shared(); + + if (world.rank() == 0) { + // Create TaskData + taskDataPar->inputs.emplace_back(reinterpret_cast(A.data())); + taskDataPar->inputs_count.emplace_back(A.size()); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_rows_A)); + taskDataPar->inputs_count.emplace_back(1); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_cols_A)); + taskDataPar->inputs_count.emplace_back(1); + + taskDataPar->inputs.emplace_back(reinterpret_cast(B.data())); + taskDataPar->inputs_count.emplace_back(B.size()); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_rows_B)); + taskDataPar->inputs_count.emplace_back(1); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_cols_B)); + taskDataPar->inputs_count.emplace_back(1); + + taskDataPar->outputs.emplace_back(reinterpret_cast(out_mpi.data())); + taskDataPar->outputs_count.emplace_back(out_mpi.size()); + taskDataPar->outputs.emplace_back(reinterpret_cast(&count_rows_out_mpi)); + taskDataPar->outputs_count.emplace_back(1); + taskDataPar->outputs.emplace_back(reinterpret_cast(&count_cols_out_mpi)); + taskDataPar->outputs_count.emplace_back(1); + } + + korobeinikov_a_test_task_mpi_lab_02::TestMPITaskParallel testMpiTaskParallel(taskDataPar); + ASSERT_EQ(testMpiTaskParallel.validation(), true); + testMpiTaskParallel.pre_processing(); + testMpiTaskParallel.run(); + testMpiTaskParallel.post_processing(); + + if (world.rank() == 0) { + // Create data + std::vector out_seq(16, 0); + int count_rows_out_seq = 0; + int count_cols_out_seq = 0; + + std::vector right_answer = {90, 100, 110, 120, 202, 228, 254, 280, 314, 356, 398, 440, 426, 484, 542, 600}; + int count_rows_RA = 4; + int count_cols_RA = 4; + // Create TaskData + std::shared_ptr taskDataSeq = std::make_shared(); + taskDataSeq->inputs.emplace_back(reinterpret_cast(A.data())); + taskDataSeq->inputs_count.emplace_back(A.size()); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_rows_A)); + taskDataSeq->inputs_count.emplace_back(1); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_cols_A)); + taskDataSeq->inputs_count.emplace_back(1); + + taskDataSeq->inputs.emplace_back(reinterpret_cast(B.data())); + taskDataSeq->inputs_count.emplace_back(B.size()); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_rows_B)); + taskDataSeq->inputs_count.emplace_back(1); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_cols_B)); + taskDataSeq->inputs_count.emplace_back(1); + + taskDataSeq->outputs.emplace_back(reinterpret_cast(out_seq.data())); + taskDataSeq->outputs_count.emplace_back(out_seq.size()); + taskDataSeq->outputs.emplace_back(reinterpret_cast(&count_rows_out_seq)); + taskDataSeq->outputs_count.emplace_back(1); + taskDataSeq->outputs.emplace_back(reinterpret_cast(&count_cols_out_seq)); + taskDataSeq->outputs_count.emplace_back(1); + + // Create Task + korobeinikov_a_test_task_mpi_lab_02::TestMPITaskSequential testMpiTaskSequential(taskDataSeq); + ASSERT_EQ(testMpiTaskSequential.validation(), true); + testMpiTaskSequential.pre_processing(); + testMpiTaskSequential.run(); + testMpiTaskSequential.post_processing(); + + for (size_t i = 0; i < right_answer.size(); i++) { + ASSERT_EQ(right_answer[i], out_seq[i]); + } + for (size_t i = 0; i < right_answer.size(); i++) { + ASSERT_EQ(right_answer[i], out_mpi[i]); + } + ASSERT_EQ(count_rows_out_seq, count_rows_RA); + ASSERT_EQ(count_cols_out_seq, count_cols_RA); + ASSERT_EQ(count_rows_out_mpi, count_rows_RA); + ASSERT_EQ(count_cols_out_mpi, count_cols_RA); + } +} + +TEST(korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B, Test_4_random_matrixes) { + boost::mpi::communicator world; + // Create data + std::vector A(9); + std::vector B(9); + int count_rows_A = 3; + int count_cols_A = 3; + int count_rows_B = 3; + int count_cols_B = 3; + + std::vector out_mpi(9, 0); + int count_rows_out_mpi = 0; + int count_cols_out_mpi = 0; + + // Create TaskData + std::shared_ptr taskDataPar = std::make_shared(); + + if (world.rank() == 0) { + // Create TaskData + A = korobeinikov_a_test_task_mpi_lab_02::getRandomVector(9); + B = korobeinikov_a_test_task_mpi_lab_02::getRandomVector(9); + taskDataPar->inputs.emplace_back(reinterpret_cast(A.data())); + taskDataPar->inputs_count.emplace_back(A.size()); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_rows_A)); + taskDataPar->inputs_count.emplace_back(1); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_cols_A)); + taskDataPar->inputs_count.emplace_back(1); + + taskDataPar->inputs.emplace_back(reinterpret_cast(B.data())); + taskDataPar->inputs_count.emplace_back(B.size()); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_rows_B)); + taskDataPar->inputs_count.emplace_back(1); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_cols_B)); + taskDataPar->inputs_count.emplace_back(1); + + taskDataPar->outputs.emplace_back(reinterpret_cast(out_mpi.data())); + taskDataPar->outputs_count.emplace_back(out_mpi.size()); + taskDataPar->outputs.emplace_back(reinterpret_cast(&count_rows_out_mpi)); + taskDataPar->outputs_count.emplace_back(1); + taskDataPar->outputs.emplace_back(reinterpret_cast(&count_cols_out_mpi)); + taskDataPar->outputs_count.emplace_back(1); + } + + korobeinikov_a_test_task_mpi_lab_02::TestMPITaskParallel testMpiTaskParallel(taskDataPar); + ASSERT_EQ(testMpiTaskParallel.validation(), true); + testMpiTaskParallel.pre_processing(); + testMpiTaskParallel.run(); + testMpiTaskParallel.post_processing(); + + if (world.rank() == 0) { + // Create data + std::vector out_seq(9, 0); + int count_rows_out_seq = 0; + int count_cols_out_seq = 0; + + // Create TaskData + std::shared_ptr taskDataSeq = std::make_shared(); + taskDataSeq->inputs.emplace_back(reinterpret_cast(A.data())); + taskDataSeq->inputs_count.emplace_back(A.size()); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_rows_A)); + taskDataSeq->inputs_count.emplace_back(1); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_cols_A)); + taskDataSeq->inputs_count.emplace_back(1); + + taskDataSeq->inputs.emplace_back(reinterpret_cast(B.data())); + taskDataSeq->inputs_count.emplace_back(B.size()); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_rows_B)); + taskDataSeq->inputs_count.emplace_back(1); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_cols_B)); + taskDataSeq->inputs_count.emplace_back(1); + + taskDataSeq->outputs.emplace_back(reinterpret_cast(out_seq.data())); + taskDataSeq->outputs_count.emplace_back(out_seq.size()); + taskDataSeq->outputs.emplace_back(reinterpret_cast(&count_rows_out_seq)); + taskDataSeq->outputs_count.emplace_back(1); + taskDataSeq->outputs.emplace_back(reinterpret_cast(&count_cols_out_seq)); + taskDataSeq->outputs_count.emplace_back(1); + + // Create Task + korobeinikov_a_test_task_mpi_lab_02::TestMPITaskSequential testMpiTaskSequential(taskDataSeq); + ASSERT_EQ(testMpiTaskSequential.validation(), true); + testMpiTaskSequential.pre_processing(); + testMpiTaskSequential.run(); + testMpiTaskSequential.post_processing(); + + for (size_t i = 0; i < out_seq.size(); i++) { + ASSERT_EQ(out_mpi[i], out_seq[i]); + } + ASSERT_EQ(count_rows_out_seq, count_rows_out_mpi); + ASSERT_EQ(count_cols_out_seq, count_cols_out_mpi); + } +} + +TEST(korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B, Test_5_empty_matrixes) { + boost::mpi::communicator world; + // Create data + std::vector A; + std::vector B; + int count_rows_A = 0; + int count_cols_A = 0; + int count_rows_B = 0; + int count_cols_B = 0; + + std::vector out_mpi(0, 0); + int count_rows_out_mpi = 0; + int count_cols_out_mpi = 0; + // Create TaskData + std::shared_ptr taskDataPar = std::make_shared(); + + if (world.rank() == 0) { + // Create TaskData + taskDataPar->inputs.emplace_back(reinterpret_cast(A.data())); + taskDataPar->inputs_count.emplace_back(A.size()); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_rows_A)); + taskDataPar->inputs_count.emplace_back(1); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_cols_A)); + taskDataPar->inputs_count.emplace_back(1); + + taskDataPar->inputs.emplace_back(reinterpret_cast(B.data())); + taskDataPar->inputs_count.emplace_back(B.size()); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_rows_B)); + taskDataPar->inputs_count.emplace_back(1); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_cols_B)); + taskDataPar->inputs_count.emplace_back(1); + + taskDataPar->outputs.emplace_back(reinterpret_cast(out_mpi.data())); + taskDataPar->outputs_count.emplace_back(out_mpi.size()); + taskDataPar->outputs.emplace_back(reinterpret_cast(&count_rows_out_mpi)); + taskDataPar->outputs_count.emplace_back(1); + taskDataPar->outputs.emplace_back(reinterpret_cast(&count_cols_out_mpi)); + taskDataPar->outputs_count.emplace_back(1); + } + + korobeinikov_a_test_task_mpi_lab_02::TestMPITaskParallel testMpiTaskParallel(taskDataPar); + ASSERT_EQ(testMpiTaskParallel.validation(), true); + testMpiTaskParallel.pre_processing(); + testMpiTaskParallel.run(); + testMpiTaskParallel.post_processing(); + + if (world.rank() == 0) { + // Create data + std::vector out_seq(0, 0); + int count_rows_out_seq = 0; + int count_cols_out_seq = 0; + + // Create TaskData + std::shared_ptr taskDataSeq = std::make_shared(); + taskDataSeq->inputs.emplace_back(reinterpret_cast(A.data())); + taskDataSeq->inputs_count.emplace_back(A.size()); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_rows_A)); + taskDataSeq->inputs_count.emplace_back(1); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_cols_A)); + taskDataSeq->inputs_count.emplace_back(1); + + taskDataSeq->inputs.emplace_back(reinterpret_cast(B.data())); + taskDataSeq->inputs_count.emplace_back(B.size()); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_rows_B)); + taskDataSeq->inputs_count.emplace_back(1); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_cols_B)); + taskDataSeq->inputs_count.emplace_back(1); + + taskDataSeq->outputs.emplace_back(reinterpret_cast(out_seq.data())); + taskDataSeq->outputs_count.emplace_back(out_seq.size()); + taskDataSeq->outputs.emplace_back(reinterpret_cast(&count_rows_out_seq)); + taskDataSeq->outputs_count.emplace_back(1); + taskDataSeq->outputs.emplace_back(reinterpret_cast(&count_cols_out_seq)); + taskDataSeq->outputs_count.emplace_back(1); + + // Create Task + korobeinikov_a_test_task_mpi_lab_02::TestMPITaskSequential testMpiTaskSequential(taskDataSeq); + ASSERT_EQ(testMpiTaskSequential.validation(), true); + testMpiTaskSequential.pre_processing(); + testMpiTaskSequential.run(); + testMpiTaskSequential.post_processing(); + + for (size_t i = 0; i < out_seq.size(); i++) { + ASSERT_EQ(out_mpi[i], out_seq[i]); + } + ASSERT_EQ(count_rows_out_seq, count_rows_out_mpi); + ASSERT_EQ(count_cols_out_seq, count_cols_out_mpi); + } +} + +TEST(korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B, Test_6_validation_false_1) { + boost::mpi::communicator world; + // Create data + std::vector A(10); + std::vector B(10); + int count_rows_A = 5; + int count_cols_A = 5; + int count_rows_B = 5; + int count_cols_B = 5; + + std::vector out_mpi(0, 0); + int count_rows_out_mpi = 0; + int count_cols_out_mpi = 0; + // Create TaskData + std::shared_ptr taskDataPar = std::make_shared(); + + if (world.rank() == 0) { + // Create TaskData + taskDataPar->inputs.emplace_back(reinterpret_cast(A.data())); + taskDataPar->inputs_count.emplace_back(A.size()); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_rows_A)); + taskDataPar->inputs_count.emplace_back(1); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_cols_A)); + taskDataPar->inputs_count.emplace_back(1); + + taskDataPar->inputs.emplace_back(reinterpret_cast(B.data())); + taskDataPar->inputs_count.emplace_back(B.size()); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_rows_B)); + taskDataPar->inputs_count.emplace_back(1); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_cols_B)); + taskDataPar->inputs_count.emplace_back(1); + + taskDataPar->outputs.emplace_back(reinterpret_cast(out_mpi.data())); + taskDataPar->outputs_count.emplace_back(out_mpi.size()); + taskDataPar->outputs.emplace_back(reinterpret_cast(&count_rows_out_mpi)); + taskDataPar->outputs_count.emplace_back(1); + taskDataPar->outputs.emplace_back(reinterpret_cast(&count_cols_out_mpi)); + taskDataPar->outputs_count.emplace_back(1); + } + + korobeinikov_a_test_task_mpi_lab_02::TestMPITaskParallel testMpiTaskParallel(taskDataPar); + if (world.rank() == 0) { + ASSERT_EQ(testMpiTaskParallel.validation(), false); + } +} + +TEST(korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B, Test_7_validation_false_2) { + boost::mpi::communicator world; + // Create data + std::vector A(10); + std::vector B(15); + int count_rows_A = 5; + int count_cols_A = 2; + int count_rows_B = 3; + int count_cols_B = 5; + + std::vector out_mpi(0, 0); + int count_rows_out_mpi = 0; + int count_cols_out_mpi = 0; + // Create TaskData + std::shared_ptr taskDataPar = std::make_shared(); + + if (world.rank() == 0) { + // Create TaskData + taskDataPar->inputs.emplace_back(reinterpret_cast(A.data())); + taskDataPar->inputs_count.emplace_back(A.size()); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_rows_A)); + taskDataPar->inputs_count.emplace_back(1); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_cols_A)); + taskDataPar->inputs_count.emplace_back(1); + + taskDataPar->inputs.emplace_back(reinterpret_cast(B.data())); + taskDataPar->inputs_count.emplace_back(B.size()); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_rows_B)); + taskDataPar->inputs_count.emplace_back(1); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_cols_B)); + taskDataPar->inputs_count.emplace_back(1); + + taskDataPar->outputs.emplace_back(reinterpret_cast(out_mpi.data())); + taskDataPar->outputs_count.emplace_back(out_mpi.size()); + taskDataPar->outputs.emplace_back(reinterpret_cast(&count_rows_out_mpi)); + taskDataPar->outputs_count.emplace_back(1); + taskDataPar->outputs.emplace_back(reinterpret_cast(&count_cols_out_mpi)); + taskDataPar->outputs_count.emplace_back(1); + } + + korobeinikov_a_test_task_mpi_lab_02::TestMPITaskParallel testMpiTaskParallel(taskDataPar); + if (world.rank() == 0) { + ASSERT_EQ(testMpiTaskParallel.validation(), false); + } +} \ No newline at end of file diff --git a/tasks/mpi/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/include/ops_mpi_korobeinikov.hpp b/tasks/mpi/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/include/ops_mpi_korobeinikov.hpp new file mode 100644 index 00000000000..8559dee3eb5 --- /dev/null +++ b/tasks/mpi/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/include/ops_mpi_korobeinikov.hpp @@ -0,0 +1,82 @@ +// Copyright 2024 Korobeinikov Arseny +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "core/task/include/task.hpp" + +namespace korobeinikov_a_test_task_mpi_lab_02 { + +struct Matrix { + std::vector data; + int count_rows; + int count_cols; + + Matrix() { + count_rows = 0; + count_cols = 0; + data = std::vector(); + } + + Matrix(int count_rows_, int count_cols_) { + count_rows = count_rows_; + count_cols = count_cols_; + data = std::vector(count_cols * count_rows); + } + + int& get_el(int row, int col) { + size_t index = row * count_cols + col; + assert(index < data.size()); + return data[index]; + } + + const int& get_el(int row, int col) const { + size_t index = row * count_cols + col; + assert(index < data.size()); + return data[index]; + } +}; + +class TestMPITaskSequential : public ppc::core::Task { + public: + explicit TestMPITaskSequential(std::shared_ptr taskData_) : Task(std::move(taskData_)) {} + bool pre_processing() override; + bool validation() override; + bool run() override; + bool post_processing() override; + + private: + Matrix A; + Matrix B; + + Matrix res; +}; + +class TestMPITaskParallel : public ppc::core::Task { + public: + explicit TestMPITaskParallel(std::shared_ptr taskData_) : Task(std::move(taskData_)) {} + bool pre_processing() override; + bool validation() override; + bool run() override; + bool post_processing() override; + + private: + std::vector input_, local_input_; + Matrix A; + Matrix B; + std::vector local_A_rows; + std::vector local_B_cols; + + Matrix res; + boost::mpi::communicator world; +}; + +} // namespace korobeinikov_a_test_task_mpi_lab_02 \ No newline at end of file diff --git a/tasks/mpi/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/perf_tests/main_korobeinikov.cpp b/tasks/mpi/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/perf_tests/main_korobeinikov.cpp new file mode 100644 index 00000000000..0c27a2d4a81 --- /dev/null +++ b/tasks/mpi/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/perf_tests/main_korobeinikov.cpp @@ -0,0 +1,155 @@ +// Copyright 2024 Korobeinikov Arseny +#include + +#include + +#include "core/perf/include/perf.hpp" +#include "mpi/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/include/ops_mpi_korobeinikov.hpp" + +// mpiexec -n 4 mpi_perf_tests + +TEST(mpi_korobeinikov_perf_test_lab_02, test_pipeline_run) { + boost::mpi::communicator world; + + // Create data + std::vector A; + std::vector B; + int count_rows_A = 150; + int count_cols_A = 150; + int count_rows_B = 150; + int count_cols_B = 150; + + std::vector out; + int count_rows_out = 0; + int count_cols_out = 0; + int count_rows_RA = 150; + int count_cols_RA = 150; + + // Create TaskData + std::shared_ptr taskDataPar = std::make_shared(); + if (world.rank() == 0) { + A = std::vector(22500, 1); + B = std::vector(22500, 1); + out = std::vector(22500, 0); + taskDataPar->inputs.emplace_back(reinterpret_cast(A.data())); + taskDataPar->inputs_count.emplace_back(A.size()); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_rows_A)); + taskDataPar->inputs_count.emplace_back(1); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_cols_A)); + taskDataPar->inputs_count.emplace_back(1); + + taskDataPar->inputs.emplace_back(reinterpret_cast(B.data())); + taskDataPar->inputs_count.emplace_back(B.size()); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_rows_B)); + taskDataPar->inputs_count.emplace_back(1); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_cols_B)); + taskDataPar->inputs_count.emplace_back(1); + + taskDataPar->outputs.emplace_back(reinterpret_cast(out.data())); + taskDataPar->outputs_count.emplace_back(out.size()); + taskDataPar->outputs.emplace_back(reinterpret_cast(&count_rows_out)); + taskDataPar->outputs_count.emplace_back(1); + taskDataPar->outputs.emplace_back(reinterpret_cast(&count_cols_out)); + taskDataPar->outputs_count.emplace_back(1); + } + + auto testMpiTaskParallel = std::make_shared(taskDataPar); + ASSERT_EQ(testMpiTaskParallel->validation(), true); + testMpiTaskParallel->pre_processing(); + testMpiTaskParallel->run(); + testMpiTaskParallel->post_processing(); + + // Create Perf attributes + auto perfAttr = std::make_shared(); + perfAttr->num_running = 10; + const boost::mpi::timer current_timer; + perfAttr->current_timer = [&] { return current_timer.elapsed(); }; + + // Create and init perf results + auto perfResults = std::make_shared(); + + // Create Perf analyzer + auto perfAnalyzer = std::make_shared(testMpiTaskParallel); + perfAnalyzer->pipeline_run(perfAttr, perfResults); + if (world.rank() == 0) { + ppc::core::Perf::print_perf_statistic(perfResults); + for (size_t i = 0; i < out.size(); i++) { + ASSERT_EQ(150, out[i]); + } + ASSERT_EQ(count_rows_out, count_rows_RA); + ASSERT_EQ(count_cols_out, count_cols_RA); + } +} + +TEST(mpi_korobeinikov_perf_test_lab_02, test_task_run) { + boost::mpi::communicator world; + + // Create data + std::vector A; + std::vector B; + int count_rows_A = 150; + int count_cols_A = 150; + int count_rows_B = 150; + int count_cols_B = 150; + + std::vector out; + int count_rows_out = 0; + int count_cols_out = 0; + int count_rows_RA = 150; + int count_cols_RA = 150; + + // Create TaskData + std::shared_ptr taskDataPar = std::make_shared(); + if (world.rank() == 0) { + A = std::vector(22500, 1); + B = std::vector(22500, 1); + out = std::vector(22500, 0); + taskDataPar->inputs.emplace_back(reinterpret_cast(A.data())); + taskDataPar->inputs_count.emplace_back(A.size()); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_rows_A)); + taskDataPar->inputs_count.emplace_back(1); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_cols_A)); + taskDataPar->inputs_count.emplace_back(1); + + taskDataPar->inputs.emplace_back(reinterpret_cast(B.data())); + taskDataPar->inputs_count.emplace_back(B.size()); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_rows_B)); + taskDataPar->inputs_count.emplace_back(1); + taskDataPar->inputs.emplace_back(reinterpret_cast(&count_cols_B)); + taskDataPar->inputs_count.emplace_back(1); + + taskDataPar->outputs.emplace_back(reinterpret_cast(out.data())); + taskDataPar->outputs_count.emplace_back(out.size()); + taskDataPar->outputs.emplace_back(reinterpret_cast(&count_rows_out)); + taskDataPar->outputs_count.emplace_back(1); + taskDataPar->outputs.emplace_back(reinterpret_cast(&count_cols_out)); + taskDataPar->outputs_count.emplace_back(1); + } + + auto testMpiTaskParallel = std::make_shared(taskDataPar); + ASSERT_EQ(testMpiTaskParallel->validation(), true); + testMpiTaskParallel->pre_processing(); + testMpiTaskParallel->run(); + testMpiTaskParallel->post_processing(); + + // Create Perf attributes + auto perfAttr = std::make_shared(); + perfAttr->num_running = 10; + const boost::mpi::timer current_timer; + perfAttr->current_timer = [&] { return current_timer.elapsed(); }; + + // Create and init perf results + auto perfResults = std::make_shared(); + + // Create Perf analyzer + auto perfAnalyzer = std::make_shared(testMpiTaskParallel); + perfAnalyzer->task_run(perfAttr, perfResults); + if (world.rank() == 0) { + ppc::core::Perf::print_perf_statistic(perfResults); + for (size_t i = 0; i < out.size(); i++) { + ASSERT_EQ(150, out[i]); + } + ASSERT_EQ(count_rows_out, count_rows_RA); + ASSERT_EQ(count_cols_out, count_cols_RA); + } +} diff --git a/tasks/mpi/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/src/ops_mpi_korobeinikov.cpp b/tasks/mpi/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/src/ops_mpi_korobeinikov.cpp new file mode 100644 index 00000000000..06894b1bb75 --- /dev/null +++ b/tasks/mpi/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/src/ops_mpi_korobeinikov.cpp @@ -0,0 +1,322 @@ +// Copyright 2024 Korobeinikov Arseny +#include "mpi/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/include/ops_mpi_korobeinikov.hpp" + +#include +#include + +bool korobeinikov_a_test_task_mpi_lab_02::TestMPITaskSequential::pre_processing() { + internal_order_test(); + // Init value for input and output + A.data.reserve(taskData->inputs_count[0]); + auto *tmp_ptr_1 = reinterpret_cast(taskData->inputs[0]); + std::copy(tmp_ptr_1, tmp_ptr_1 + taskData->inputs_count[0], A.data.begin()); + A.count_rows = (int)*taskData->inputs[1]; + A.count_cols = (int)*taskData->inputs[2]; + + B.data.reserve(taskData->inputs_count[3]); + auto *tmp_ptr_2 = reinterpret_cast(taskData->inputs[3]); + std::copy(tmp_ptr_2, tmp_ptr_2 + taskData->inputs_count[3], B.data.begin()); + B.count_rows = (int)*taskData->inputs[4]; + B.count_cols = (int)*taskData->inputs[5]; + + res = Matrix(A.count_rows, B.count_cols); + return true; +} + +bool korobeinikov_a_test_task_mpi_lab_02::TestMPITaskSequential::validation() { + internal_order_test(); + return taskData->inputs.size() == 6 && taskData->inputs_count.size() == 6 && taskData->outputs.size() == 3 && + taskData->outputs_count.size() == 3 && (*taskData->inputs[2] == *taskData->inputs[4]) && + ((*taskData->inputs[1]) * (*taskData->inputs[2]) == (int)taskData->inputs_count[0]) && + ((*taskData->inputs[4]) * (*taskData->inputs[5]) == (int)taskData->inputs_count[3]); +} + +bool korobeinikov_a_test_task_mpi_lab_02::TestMPITaskSequential::run() { + internal_order_test(); + + for (int i = 0; i < A.count_rows; i++) { + for (int j = 0; j < B.count_cols; j++) { + res.get_el(i, j) = 0; + for (int k = 0; k < A.count_cols; k++) { + res.get_el(i, j) += A.get_el(i, k) * B.get_el(k, j); + } + } + } + return true; +} + +bool korobeinikov_a_test_task_mpi_lab_02::TestMPITaskSequential::post_processing() { + internal_order_test(); + + std::copy(res.data.begin(), res.data.end(), reinterpret_cast(taskData->outputs[0])); + *reinterpret_cast(taskData->outputs[1]) = res.count_rows; + *reinterpret_cast(taskData->outputs[2]) = res.count_cols; + return true; +} + +bool korobeinikov_a_test_task_mpi_lab_02::TestMPITaskParallel::pre_processing() { + internal_order_test(); + + return true; +} + +bool korobeinikov_a_test_task_mpi_lab_02::TestMPITaskParallel::validation() { + internal_order_test(); + if (world.rank() == 0) { + if (taskData->inputs_count[0] == 0 || taskData->inputs_count[3] == 0) { + return true; + } + return taskData->inputs.size() == 6 && taskData->inputs_count.size() == 6 && taskData->outputs.size() == 3 && + taskData->outputs_count.size() == 3 && (*taskData->inputs[2] == *taskData->inputs[4]) && + ((*taskData->inputs[1]) * (*taskData->inputs[2]) == (int)taskData->inputs_count[0]) && + ((*taskData->inputs[4]) * (*taskData->inputs[5]) == (int)taskData->inputs_count[3]); + } + return true; +} + +bool korobeinikov_a_test_task_mpi_lab_02::TestMPITaskParallel::run() { + internal_order_test(); + int num_use_proc; + int count_rows_on_proc; + int count_cols_on_proc; + int A_rows; + int A_cols; + int B_rows; + int B_cols; + + // Getting data by a null process + if (world.rank() == 0) { + A.data.reserve(taskData->inputs_count[0]); + auto *tmp_ptr_1 = reinterpret_cast(taskData->inputs[0]); + std::copy(tmp_ptr_1, tmp_ptr_1 + taskData->inputs_count[0], std::back_inserter(A.data)); + A.count_rows = (int)*taskData->inputs[1]; + A.count_cols = (int)*taskData->inputs[2]; + + A_rows = A.count_rows; + A_cols = A.count_cols; + + B.data.reserve(taskData->inputs_count[3]); + auto *tmp_ptr_2 = reinterpret_cast(taskData->inputs[3]); + std::copy(tmp_ptr_2, tmp_ptr_2 + taskData->inputs_count[3], std::back_inserter(B.data)); + B.count_rows = (int)*taskData->inputs[4]; + B.count_cols = (int)*taskData->inputs[5]; + + B_rows = B.count_rows; + B_cols = B.count_cols; + + num_use_proc = std::min(A.count_rows, std::min(world.size(), B.count_cols)); + if (num_use_proc != 0) { + count_rows_on_proc = A.count_rows / num_use_proc; + count_cols_on_proc = B.count_cols / num_use_proc; + } else { + count_rows_on_proc = 0; + count_cols_on_proc = 0; + } + + res = Matrix(A.count_rows, B.count_cols); + } + + broadcast(world, num_use_proc, 0); + broadcast(world, count_rows_on_proc, 0); + broadcast(world, count_cols_on_proc, 0); + broadcast(world, A_rows, 0); + broadcast(world, A_cols, 0); + broadcast(world, B_rows, 0); + broadcast(world, B_cols, 0); + + if (num_use_proc == 0) { + return true; + } + std::vector displs; + std::vector scounts; + + // Send A rows by scatterv + if (world.rank() == 0) { + for (int i = 0; i < num_use_proc - 1; i++) { + scounts.push_back(count_rows_on_proc * A_cols); + displs.push_back(count_rows_on_proc * A_cols * i); + } + scounts.push_back(count_rows_on_proc * A_cols + (A_rows % num_use_proc) * A_cols); + displs.push_back(count_rows_on_proc * A_cols * (num_use_proc - 1)); + for (int i = num_use_proc; i < world.size(); i++) { + scounts.push_back(0); + displs.push_back(scounts[num_use_proc - 1] + count_rows_on_proc * A_cols + (A_rows % num_use_proc) * A_cols); + } + } + + for (int i = 0; i < num_use_proc - 1; i++) { + if (world.rank() == i) { + local_A_rows = std::vector(count_rows_on_proc * A_cols); + } + } + if (world.rank() == num_use_proc - 1) { + local_A_rows = std::vector(count_rows_on_proc * A_cols + (A_rows % num_use_proc) * A_cols); + } + for (int i = num_use_proc; i < world.size(); i++) { + if (world.rank() == i) { + local_A_rows = std::vector(1); + } + } + + if (world.rank() == 0) { + scatterv(world, A.data.data(), scounts, displs, local_A_rows.data(), local_A_rows.size(), 0); + } + for (int i = 1; i < num_use_proc; i++) { + if (world.rank() == i) { + scatterv(world, local_A_rows.data(), local_A_rows.size(), 0); + } + } + for (int i = num_use_proc; i < world.size(); i++) { + if (world.rank() == i) { + scatterv(world, local_A_rows.data(), 0, 0); + } + } + + // Send B cols by (send && recv) + std::vector a_begin_row(num_use_proc); + std::vector a_end_row(num_use_proc); + for (int i = 0; i < num_use_proc; i++) { + a_begin_row[i] = i * count_rows_on_proc; + } + for (int i = 0; i < num_use_proc - 1; i++) { + a_end_row[i] = (i + 1) * count_rows_on_proc - 1; + } + a_end_row[num_use_proc - 1] = A_rows - 1; + + std::vector b_begin_col(num_use_proc); + std::vector b_end_col(num_use_proc); + for (int i = 0; i < num_use_proc; i++) { + b_begin_col[i] = i * count_cols_on_proc; + } + for (int i = 0; i < num_use_proc - 1; i++) { + b_end_col[i] = (i + 1) * count_cols_on_proc - 1; + } + b_end_col[num_use_proc - 1] = B_cols - 1; + + int delta_for_B = count_cols_on_proc * B_rows; + std::vector tmp_B_cols; + if (world.rank() == 0) { + local_B_cols = std::vector(); + for (int j = 0; j <= b_end_col[0]; j++) { + for (int i = 0; i < B_rows; i++) { + local_B_cols.push_back(B.get_el(i, j)); + } + } + + for (int proc = 1; proc < num_use_proc - 1; proc++) { + tmp_B_cols.clear(); + for (int j = b_begin_col[proc]; j <= b_end_col[proc]; j++) { + for (int i = 0; i < B_rows; i++) { + tmp_B_cols.push_back(B.get_el(i, j)); + } + } + world.send(proc, 0, tmp_B_cols.data(), delta_for_B); + } + if (num_use_proc != 1) { + int proc = num_use_proc - 1; + tmp_B_cols.clear(); + for (int j = b_begin_col[proc]; j <= b_end_col[proc]; j++) { + for (int i = 0; i < B_rows; i++) { + tmp_B_cols.push_back(B.get_el(i, j)); + } + } + world.send(proc, 0, tmp_B_cols.data(), delta_for_B + (B_cols % num_use_proc) * B_rows); + } + } else { + if (world.rank() == num_use_proc - 1 && num_use_proc != 0) { + local_B_cols = std::vector(delta_for_B + (B_cols % num_use_proc) * B_rows); + world.recv(0, 0, local_B_cols.data(), delta_for_B + (B_cols % num_use_proc) * B_rows); + } else { + if (world.rank() < num_use_proc) { + local_B_cols = std::vector(delta_for_B); + world.recv(0, 0, local_B_cols.data(), delta_for_B); + } + } + } + + // Calculate res matrix + int size_local_B_cols_for_last = delta_for_B + (B_cols % num_use_proc) * B_rows; + int size_local_B_cols = delta_for_B; + tmp_B_cols.clear(); + tmp_B_cols = std::vector(size_local_B_cols_for_last); + for (int proc1 = 0; proc1 < num_use_proc; proc1++) { + if (world.rank() == proc1) { + if (world.rank() != num_use_proc - 1) { + for (int proc2 = 0; proc2 < num_use_proc; proc2++) { + if (world.rank() != proc2) { + world.send(proc2, 0, local_B_cols.data(), size_local_B_cols); + } + } + } else { + for (int proc2 = 0; proc2 < num_use_proc; proc2++) { + if (world.rank() != proc2) { + world.send(proc2, 0, local_B_cols.data(), size_local_B_cols_for_last); + } + } + } + + for (int i = a_begin_row[proc1]; i <= a_end_row[proc1]; i++) { + for (int j = b_begin_col[proc1]; j <= b_end_col[proc1]; j++) { + int tmp_res_el = 0; + for (int n = 0; n < A_cols; n++) { + tmp_res_el += local_A_rows[(i - a_begin_row[proc1]) * A_cols + n] * + local_B_cols[(j - b_begin_col[proc1]) * B_rows + n]; + } + if (proc1 == 0) { + res.data[i * B_cols + j] = tmp_res_el; + } else { + world.send(0, 0, tmp_res_el); + } + } + } + } else { + if (world.rank() < num_use_proc) { + if (proc1 != num_use_proc - 1) { + world.recv(proc1, 0, tmp_B_cols.data(), size_local_B_cols); + } else { + world.recv(proc1, 0, tmp_B_cols.data(), size_local_B_cols_for_last); + } + + for (int i = a_begin_row[world.rank()]; i <= a_end_row[world.rank()]; i++) { + for (int j = b_begin_col[proc1]; j <= b_end_col[proc1]; j++) { + int tmp_res_el = 0; + for (int n = 0; n < A_cols; n++) { + tmp_res_el += local_A_rows[(i - a_begin_row[world.rank()]) * A_cols + n] * + tmp_B_cols[(j - b_begin_col[proc1]) * B_rows + n]; + } + if (world.rank() == 0) { + res.data[i * B_cols + j] = tmp_res_el; + } else { + world.send(0, 0, tmp_res_el); + } + } + } + } + } + if (world.rank() == 0) { + for (int i = a_end_row[0] + 1; i < A_rows; i++) { + for (int j = b_begin_col[proc1]; j <= b_end_col[proc1]; j++) { + int tmp_res_el = 0; + int proc_sender = i / count_rows_on_proc < num_use_proc ? i / count_rows_on_proc : num_use_proc - 1; + world.recv(proc_sender, 0, tmp_res_el); + res.data[i * B_cols + j] = tmp_res_el; + } + } + } + } + + return true; +} + +bool korobeinikov_a_test_task_mpi_lab_02::TestMPITaskParallel::post_processing() { + internal_order_test(); + if (world.rank() == 0) { + std::copy(res.data.begin(), res.data.end(), reinterpret_cast(taskData->outputs[0])); + *reinterpret_cast(taskData->outputs[1]) = res.count_rows; + *reinterpret_cast(taskData->outputs[2]) = res.count_cols; + return true; + } + return true; +} + +// mpiexec -n 4 mpi_func_tests \ No newline at end of file diff --git a/tasks/seq/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/func_tests/main_korobeinikov.cpp b/tasks/seq/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/func_tests/main_korobeinikov.cpp new file mode 100644 index 00000000000..f4ff9d3c088 --- /dev/null +++ b/tasks/seq/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/func_tests/main_korobeinikov.cpp @@ -0,0 +1,242 @@ +// Copyright 2024 Korobeinukov Arseny +#include + +#include "seq/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/include/ops_seq_korobeinikov.hpp" + +TEST(korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B, Test_1_without_negative_elemets) { + // Create data + std::vector A = {3, 2, 1}; + std::vector B = {1, 2, 3}; + int count_rows_A = 3; + int count_cols_A = 1; + int count_rows_B = 1; + int count_cols_B = 3; + + std::vector out(9, 0); + std::vector right_answer = {3, 6, 9, 2, 4, 6, 1, 2, 3}; + int count_rows_out = 3; + int count_cols_out = 3; + int count_rows_RA = 3; + int count_cols_RA = 3; + + // Create TaskData + std::shared_ptr taskDataSeq = std::make_shared(); + taskDataSeq->inputs.emplace_back(reinterpret_cast(A.data())); + taskDataSeq->inputs_count.emplace_back(A.size()); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_rows_A)); + taskDataSeq->inputs_count.emplace_back(1); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_cols_A)); + taskDataSeq->inputs_count.emplace_back(1); + + taskDataSeq->inputs.emplace_back(reinterpret_cast(B.data())); + taskDataSeq->inputs_count.emplace_back(B.size()); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_rows_B)); + taskDataSeq->inputs_count.emplace_back(1); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_cols_B)); + taskDataSeq->inputs_count.emplace_back(1); + + taskDataSeq->outputs.emplace_back(reinterpret_cast(out.data())); + taskDataSeq->outputs_count.emplace_back(out.size()); + taskDataSeq->outputs.emplace_back(reinterpret_cast(&count_rows_out)); + taskDataSeq->outputs_count.emplace_back(1); + taskDataSeq->outputs.emplace_back(reinterpret_cast(&count_cols_out)); + taskDataSeq->outputs_count.emplace_back(1); + + // Create Task + korobeinikov_a_test_task_seq_lab_02::TestTaskSequential testTaskSequential(taskDataSeq); + ASSERT_EQ(testTaskSequential.validation(), true); + testTaskSequential.pre_processing(); + testTaskSequential.run(); + testTaskSequential.post_processing(); + for (size_t i = 0; i < right_answer.size(); i++) { + ASSERT_EQ(right_answer[i], out[i]); + } + ASSERT_EQ(count_rows_out, count_rows_RA); + ASSERT_EQ(count_cols_out, count_cols_RA); +} + +TEST(korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B, Test_2_with_negative_elemets) { + // Create data + std::vector A = {-3, 2, -1}; + std::vector B = {1, 2, 3}; + int count_rows_A = 3; + int count_cols_A = 1; + int count_rows_B = 1; + int count_cols_B = 3; + + std::vector out(9, 0); + std::vector right_answer = {-3, -6, -9, 2, 4, 6, -1, -2, -3}; + int count_rows_out = 3; + int count_cols_out = 3; + int count_rows_RA = 3; + int count_cols_RA = 3; + + // Create TaskData + std::shared_ptr taskDataSeq = std::make_shared(); + taskDataSeq->inputs.emplace_back(reinterpret_cast(A.data())); + taskDataSeq->inputs_count.emplace_back(A.size()); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_rows_A)); + taskDataSeq->inputs_count.emplace_back(1); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_cols_A)); + taskDataSeq->inputs_count.emplace_back(1); + + taskDataSeq->inputs.emplace_back(reinterpret_cast(B.data())); + taskDataSeq->inputs_count.emplace_back(B.size()); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_rows_B)); + taskDataSeq->inputs_count.emplace_back(1); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_cols_B)); + taskDataSeq->inputs_count.emplace_back(1); + + taskDataSeq->outputs.emplace_back(reinterpret_cast(out.data())); + taskDataSeq->outputs_count.emplace_back(out.size()); + taskDataSeq->outputs.emplace_back(reinterpret_cast(&count_rows_out)); + taskDataSeq->outputs_count.emplace_back(1); + taskDataSeq->outputs.emplace_back(reinterpret_cast(&count_cols_out)); + taskDataSeq->outputs_count.emplace_back(1); + + // Create Task + korobeinikov_a_test_task_seq_lab_02::TestTaskSequential testTaskSequential(taskDataSeq); + ASSERT_EQ(testTaskSequential.validation(), true); + testTaskSequential.pre_processing(); + testTaskSequential.run(); + testTaskSequential.post_processing(); + for (size_t i = 0; i < right_answer.size(); i++) { + ASSERT_EQ(right_answer[i], out[i]); + } + ASSERT_EQ(count_rows_out, count_rows_RA); + ASSERT_EQ(count_cols_out, count_cols_RA); +} + +TEST(korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B, Test_3_only_zero) { + // Create data + std::vector A = {0, 0, 0}; + std::vector B = {0, 0, 0}; + int count_rows_A = 3; + int count_cols_A = 1; + int count_rows_B = 1; + int count_cols_B = 3; + + std::vector out(9, 0); + std::vector right_answer = {0, 0, 0, 0, 0, 0, 0, 0, 0}; + int count_rows_out = 3; + int count_cols_out = 3; + int count_rows_RA = 3; + int count_cols_RA = 3; + + // Create TaskData + std::shared_ptr taskDataSeq = std::make_shared(); + taskDataSeq->inputs.emplace_back(reinterpret_cast(A.data())); + taskDataSeq->inputs_count.emplace_back(A.size()); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_rows_A)); + taskDataSeq->inputs_count.emplace_back(1); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_cols_A)); + taskDataSeq->inputs_count.emplace_back(1); + + taskDataSeq->inputs.emplace_back(reinterpret_cast(B.data())); + taskDataSeq->inputs_count.emplace_back(B.size()); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_rows_B)); + taskDataSeq->inputs_count.emplace_back(1); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_cols_B)); + taskDataSeq->inputs_count.emplace_back(1); + + taskDataSeq->outputs.emplace_back(reinterpret_cast(out.data())); + taskDataSeq->outputs_count.emplace_back(out.size()); + taskDataSeq->outputs.emplace_back(reinterpret_cast(&count_rows_out)); + taskDataSeq->outputs_count.emplace_back(1); + taskDataSeq->outputs.emplace_back(reinterpret_cast(&count_cols_out)); + taskDataSeq->outputs_count.emplace_back(1); + + // Create Task + korobeinikov_a_test_task_seq_lab_02::TestTaskSequential testTaskSequential(taskDataSeq); + ASSERT_EQ(testTaskSequential.validation(), true); + testTaskSequential.pre_processing(); + testTaskSequential.run(); + testTaskSequential.post_processing(); + for (size_t i = 0; i < right_answer.size(); i++) { + ASSERT_EQ(right_answer[i], out[i]); + } + ASSERT_EQ(count_rows_out, count_rows_RA); + ASSERT_EQ(count_cols_out, count_cols_RA); +} + +TEST(korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B, Test_4_validation_false_1) { + // Create data + std::vector A = {0, 0, 0}; + std::vector B = {0, 0, 0}; + int count_rows_A = 3; + int count_cols_A = 1; + int count_rows_B = 3; + int count_cols_B = 1; + + std::vector out(9, 0); + int count_rows_out = 3; + int count_cols_out = 3; + + // Create TaskData + std::shared_ptr taskDataSeq = std::make_shared(); + taskDataSeq->inputs.emplace_back(reinterpret_cast(A.data())); + taskDataSeq->inputs_count.emplace_back(A.size()); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_rows_A)); + taskDataSeq->inputs_count.emplace_back(1); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_cols_A)); + taskDataSeq->inputs_count.emplace_back(1); + + taskDataSeq->inputs.emplace_back(reinterpret_cast(B.data())); + taskDataSeq->inputs_count.emplace_back(B.size()); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_rows_B)); + taskDataSeq->inputs_count.emplace_back(1); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_cols_B)); + taskDataSeq->inputs_count.emplace_back(1); + + taskDataSeq->outputs.emplace_back(reinterpret_cast(out.data())); + taskDataSeq->outputs_count.emplace_back(out.size()); + taskDataSeq->outputs.emplace_back(reinterpret_cast(&count_rows_out)); + taskDataSeq->outputs_count.emplace_back(1); + taskDataSeq->outputs.emplace_back(reinterpret_cast(&count_cols_out)); + taskDataSeq->outputs_count.emplace_back(1); + + // Create Task + korobeinikov_a_test_task_seq_lab_02::TestTaskSequential testTaskSequential(taskDataSeq); + ASSERT_EQ(testTaskSequential.validation(), false); +} + +TEST(korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B, Test_5_validation_false_2) { + // Create data + std::vector A = {0, 0, 0}; + std::vector B = {0, 0, 0}; + int count_rows_A = 3; + int count_cols_A = 1; + int count_rows_B = 1; + int count_cols_B = 10; + + std::vector out(9, 0); + int count_rows_out = 3; + int count_cols_out = 3; + + // Create TaskData + std::shared_ptr taskDataSeq = std::make_shared(); + taskDataSeq->inputs.emplace_back(reinterpret_cast(A.data())); + taskDataSeq->inputs_count.emplace_back(A.size()); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_rows_A)); + taskDataSeq->inputs_count.emplace_back(1); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_cols_A)); + taskDataSeq->inputs_count.emplace_back(1); + + taskDataSeq->inputs.emplace_back(reinterpret_cast(B.data())); + taskDataSeq->inputs_count.emplace_back(B.size()); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_rows_B)); + taskDataSeq->inputs_count.emplace_back(1); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_cols_B)); + taskDataSeq->inputs_count.emplace_back(1); + + taskDataSeq->outputs.emplace_back(reinterpret_cast(out.data())); + taskDataSeq->outputs_count.emplace_back(out.size()); + taskDataSeq->outputs.emplace_back(reinterpret_cast(&count_rows_out)); + taskDataSeq->outputs_count.emplace_back(1); + taskDataSeq->outputs.emplace_back(reinterpret_cast(&count_cols_out)); + taskDataSeq->outputs_count.emplace_back(1); + + // Create Task + korobeinikov_a_test_task_seq_lab_02::TestTaskSequential testTaskSequential(taskDataSeq); + ASSERT_EQ(testTaskSequential.validation(), false); +} \ No newline at end of file diff --git a/tasks/seq/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/include/ops_seq_korobeinikov.hpp b/tasks/seq/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/include/ops_seq_korobeinikov.hpp new file mode 100644 index 00000000000..acb79bbc2d5 --- /dev/null +++ b/tasks/seq/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/include/ops_seq_korobeinikov.hpp @@ -0,0 +1,56 @@ +// Copyright 2024 Korobeinikov Arseny +#pragma once + +#include +#include +#include + +#include "core/task/include/task.hpp" + +namespace korobeinikov_a_test_task_seq_lab_02 { + +struct Matrix { + std::vector data; + int count_rows; + int count_cols; + + Matrix() { + count_rows = 0; + count_cols = 0; + data = std::vector(); + } + + Matrix(int count_rows_, int count_cols_) { + count_rows = count_rows_; + count_cols = count_cols_; + data = std::vector(count_cols * count_rows); + } + int& get_el(int row, int col) { + size_t index = row * count_cols + col; + assert(index < data.size()); + return data[index]; + } + + const int& get_el(int row, int col) const { + size_t index = row * count_cols + col; + assert(index < data.size()); + return data[index]; + } +}; + +class TestTaskSequential : public ppc::core::Task { + public: + explicit TestTaskSequential(std::shared_ptr taskData_) : Task(std::move(taskData_)) {} + bool pre_processing() override; + bool validation() override; + bool run() override; + bool post_processing() override; + + private: + Matrix A; + Matrix B; + + Matrix res; +}; + +} // namespace korobeinikov_a_test_task_seq_lab_02 \ No newline at end of file diff --git a/tasks/seq/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/perf_tests/main_korobeinikov.cpp b/tasks/seq/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/perf_tests/main_korobeinikov.cpp new file mode 100644 index 00000000000..30f0f905a3d --- /dev/null +++ b/tasks/seq/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/perf_tests/main_korobeinikov.cpp @@ -0,0 +1,137 @@ +// Copyright 2024 Korobeinikov Arseny +#include + +#include "core/perf/include/perf.hpp" +#include "seq/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/include/ops_seq_korobeinikov.hpp" + +TEST(sequential_korobeinikov_perf_test_lab_02, test_pipeline_run) { + // Create data + std::vector A(53361, 1); + std::vector B(53361, 1); + int count_rows_A = 231; + int count_cols_A = 231; + int count_rows_B = 231; + int count_cols_B = 231; + + std::vector out(53361, 0); + std::vector right_answer(53361, 231); + int count_rows_out = 0; + int count_cols_out = 0; + int count_rows_RA = 231; + int count_cols_RA = 231; + + // Create TaskData + std::shared_ptr taskDataSeq = std::make_shared(); + taskDataSeq->inputs.emplace_back(reinterpret_cast(A.data())); + taskDataSeq->inputs_count.emplace_back(A.size()); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_rows_A)); + taskDataSeq->inputs_count.emplace_back(1); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_cols_A)); + taskDataSeq->inputs_count.emplace_back(1); + + taskDataSeq->inputs.emplace_back(reinterpret_cast(B.data())); + taskDataSeq->inputs_count.emplace_back(B.size()); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_rows_B)); + taskDataSeq->inputs_count.emplace_back(1); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_cols_B)); + taskDataSeq->inputs_count.emplace_back(1); + + taskDataSeq->outputs.emplace_back(reinterpret_cast(out.data())); + taskDataSeq->outputs_count.emplace_back(out.size()); + taskDataSeq->outputs.emplace_back(reinterpret_cast(&count_rows_out)); + taskDataSeq->outputs_count.emplace_back(1); + taskDataSeq->outputs.emplace_back(reinterpret_cast(&count_cols_out)); + taskDataSeq->outputs_count.emplace_back(1); + + // Create Task + auto testTaskSequential = std::make_shared(taskDataSeq); + + // Create Perf attributes + auto perfAttr = std::make_shared(); + perfAttr->num_running = 10; + const auto t0 = std::chrono::high_resolution_clock::now(); + perfAttr->current_timer = [&] { + auto current_time_point = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(current_time_point - t0).count(); + return static_cast(duration) * 1e-9; + }; + + // Create and init perf results + auto perfResults = std::make_shared(); + + // Create Perf analyzer + auto perfAnalyzer = std::make_shared(testTaskSequential); + perfAnalyzer->pipeline_run(perfAttr, perfResults); + ppc::core::Perf::print_perf_statistic(perfResults); + for (size_t i = 0; i < right_answer.size(); i++) { + ASSERT_EQ(right_answer[i], out[i]); + } + ASSERT_EQ(count_rows_out, count_rows_RA); + ASSERT_EQ(count_cols_out, count_cols_RA); +} + +TEST(sequential_korobeinikov_perf_test_lab_02, test_task_run) { + // Create data + std::vector A(53361, 1); + std::vector B(53361, 1); + int count_rows_A = 231; + int count_cols_A = 231; + int count_rows_B = 231; + int count_cols_B = 231; + + std::vector out(53361, 0); + std::vector right_answer(53361, 231); + int count_rows_out = 0; + int count_cols_out = 0; + int count_rows_RA = 231; + int count_cols_RA = 231; + + // Create TaskData + std::shared_ptr taskDataSeq = std::make_shared(); + taskDataSeq->inputs.emplace_back(reinterpret_cast(A.data())); + taskDataSeq->inputs_count.emplace_back(A.size()); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_rows_A)); + taskDataSeq->inputs_count.emplace_back(1); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_cols_A)); + taskDataSeq->inputs_count.emplace_back(1); + + taskDataSeq->inputs.emplace_back(reinterpret_cast(B.data())); + taskDataSeq->inputs_count.emplace_back(B.size()); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_rows_B)); + taskDataSeq->inputs_count.emplace_back(1); + taskDataSeq->inputs.emplace_back(reinterpret_cast(&count_cols_B)); + taskDataSeq->inputs_count.emplace_back(1); + + taskDataSeq->outputs.emplace_back(reinterpret_cast(out.data())); + taskDataSeq->outputs_count.emplace_back(out.size()); + taskDataSeq->outputs.emplace_back(reinterpret_cast(&count_rows_out)); + taskDataSeq->outputs_count.emplace_back(1); + taskDataSeq->outputs.emplace_back(reinterpret_cast(&count_cols_out)); + taskDataSeq->outputs_count.emplace_back(1); + + // Create Task + auto testTaskSequential = std::make_shared(taskDataSeq); + + // Create Perf attributes + auto perfAttr = std::make_shared(); + perfAttr->num_running = 10; + const auto t0 = std::chrono::high_resolution_clock::now(); + perfAttr->current_timer = [&] { + auto current_time_point = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(current_time_point - t0).count(); + return static_cast(duration) * 1e-9; + }; + + // Create and init perf results + auto perfResults = std::make_shared(); + + // Create Perf analyzer + auto perfAnalyzer = std::make_shared(testTaskSequential); + perfAnalyzer->task_run(perfAttr, perfResults); + ppc::core::Perf::print_perf_statistic(perfResults); + for (size_t i = 0; i < right_answer.size(); i++) { + ASSERT_EQ(right_answer[i], out[i]); + } + ASSERT_EQ(count_rows_out, count_rows_RA); + ASSERT_EQ(count_cols_out, count_cols_RA); +} diff --git a/tasks/seq/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/src/ops_seq_korobeinikov.cpp b/tasks/seq/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/src/ops_seq_korobeinikov.cpp new file mode 100644 index 00000000000..4baa4b50271 --- /dev/null +++ b/tasks/seq/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/src/ops_seq_korobeinikov.cpp @@ -0,0 +1,55 @@ +// Copyright 2024 Korobeinikov Brseny +#include "seq/korobeinikov_matrix_multiplication_horizontal_scheme_A_vertical_scheme_B/include/ops_seq_korobeinikov.hpp" + +using namespace std::chrono_literals; + +bool korobeinikov_a_test_task_seq_lab_02::TestTaskSequential::pre_processing() { + internal_order_test(); + // Init value for input and output + A.data.reserve(taskData->inputs_count[0]); + auto *tmp_ptr_1 = reinterpret_cast(taskData->inputs[0]); + std::copy(tmp_ptr_1, tmp_ptr_1 + taskData->inputs_count[0], A.data.begin()); + A.count_rows = (int)*taskData->inputs[1]; + A.count_cols = (int)*taskData->inputs[2]; + + B.data.reserve(taskData->inputs_count[3]); + auto *tmp_ptr_2 = reinterpret_cast(taskData->inputs[3]); + std::copy(tmp_ptr_2, tmp_ptr_2 + taskData->inputs_count[3], B.data.begin()); + B.count_rows = (int)*taskData->inputs[4]; + B.count_cols = (int)*taskData->inputs[5]; + + res = Matrix(A.count_rows, B.count_cols); + return true; +} + +bool korobeinikov_a_test_task_seq_lab_02::TestTaskSequential::validation() { + internal_order_test(); + return taskData->inputs.size() == 6 && taskData->inputs_count.size() == 6 && taskData->outputs.size() == 3 && + taskData->outputs_count.size() == 3 && (*taskData->inputs[2] == *taskData->inputs[4]) && + ((*taskData->inputs[1]) * (*taskData->inputs[2]) == (int)taskData->inputs_count[0]) && + ((*taskData->inputs[4]) * (*taskData->inputs[5]) == (int)taskData->inputs_count[3]); +} + +bool korobeinikov_a_test_task_seq_lab_02::TestTaskSequential::run() { + internal_order_test(); + + for (int i = 0; i < A.count_rows; i++) { + for (int j = 0; j < B.count_cols; j++) { + res.get_el(i, j) = 0; + for (int k = 0; k < A.count_cols; k++) { + res.get_el(i, j) += A.get_el(i, k) * B.get_el(k, j); + } + } + } + + return true; +} + +bool korobeinikov_a_test_task_seq_lab_02::TestTaskSequential::post_processing() { + internal_order_test(); + + std::copy(res.data.begin(), res.data.end(), reinterpret_cast(taskData->outputs[0])); + *reinterpret_cast(taskData->outputs[1]) = res.count_rows; + *reinterpret_cast(taskData->outputs[2]) = res.count_cols; + return true; +}