GCC Code Coverage Report


Directory: ./
File: tasks/lobanov_d_multiply_matrix_ccs/mpi/src/ops_mpi.cpp
Date: 2026-01-10 02:40:41
Exec Total Coverage
Lines: 244 248 98.4%
Functions: 24 24 100.0%
Branches: 152 262 58.0%

Line Branch Exec Source
1 #include "lobanov_d_multiply_matrix_ccs/mpi/include/ops_mpi.hpp"
2
3 #include <mpi.h>
4
5 #include <algorithm>
6 #include <array>
7 #include <cmath>
8 #include <cstddef>
9 #include <tuple>
10 #include <utility>
11 #include <vector>
12
13 #include "lobanov_d_multiply_matrix_ccs/common/include/common.hpp"
14
15 namespace lobanov_d_multiply_matrix_ccs {
16
17 namespace {
18 constexpr double kEpsilonThreshold = 1e-10;
19
20 8 std::vector<int> CountElementsPerRow(const CompressedColumnMatrix &source_matrix, int column_count) {
21 8 std::vector<int> row_element_count(column_count, 0);
22
23
2/2
✓ Branch 0 taken 969 times.
✓ Branch 1 taken 8 times.
977 for (int element_index = 0; element_index < source_matrix.non_zero_count; element_index++) {
24
1/2
✓ Branch 0 taken 969 times.
✗ Branch 1 not taken.
969 int row_idx = source_matrix.row_index_data[element_index];
25
1/2
✓ Branch 0 taken 969 times.
✗ Branch 1 not taken.
969 if (row_idx >= 0 && row_idx < column_count) {
26 969 row_element_count[row_idx]++;
27 }
28 }
29
30 8 return row_element_count;
31 }
32
33 8 void BuildColumnPointers(CompressedColumnMatrix &transposed_result, const std::vector<int> &row_element_count) {
34 8 transposed_result.column_pointer_data.resize(transposed_result.column_count + 1);
35 8 transposed_result.column_pointer_data[0] = 0;
36
37
2/2
✓ Branch 0 taken 210 times.
✓ Branch 1 taken 8 times.
218 for (int row_index = 0; row_index < transposed_result.column_count; row_index++) {
38 210 transposed_result.column_pointer_data[row_index + 1] =
39 210 transposed_result.column_pointer_data[row_index] + row_element_count[row_index];
40 }
41 8 }
42
43 8 void FillTransposedMatrixData(const CompressedColumnMatrix &source_matrix, CompressedColumnMatrix &transposed_result) {
44 8 transposed_result.value_data.resize(source_matrix.non_zero_count);
45 8 transposed_result.row_index_data.resize(source_matrix.non_zero_count);
46
47 8 std::vector<int> current_position_array(transposed_result.column_count, 0);
48
49
2/2
✓ Branch 0 taken 205 times.
✓ Branch 1 taken 8 times.
213 for (int column_counter = 0; column_counter < source_matrix.column_count; column_counter++) {
50 205 int column_start = source_matrix.column_pointer_data[column_counter];
51 205 int column_end = source_matrix.column_pointer_data[column_counter + 1];
52
53
2/2
✓ Branch 0 taken 969 times.
✓ Branch 1 taken 205 times.
1174 for (int element_counter = column_start; element_counter < column_end; element_counter++) {
54
1/2
✓ Branch 0 taken 969 times.
✗ Branch 1 not taken.
969 int row_position = source_matrix.row_index_data[element_counter];
55 969 double element_value = source_matrix.value_data[element_counter];
56
57
2/4
✓ Branch 0 taken 969 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 969 times.
✗ Branch 3 not taken.
969 if (row_position >= 0 && row_position < transposed_result.column_count) {
58
1/2
✓ Branch 0 taken 969 times.
✗ Branch 1 not taken.
969 int target_slot = transposed_result.column_pointer_data[row_position] + current_position_array[row_position];
59
60
2/4
✓ Branch 0 taken 969 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 969 times.
✗ Branch 3 not taken.
969 if (target_slot >= 0 && target_slot < source_matrix.non_zero_count) {
61 969 transposed_result.value_data[target_slot] = element_value;
62 969 transposed_result.row_index_data[target_slot] = column_counter;
63 }
64
65 969 current_position_array[row_position]++;
66 }
67 }
68 }
69 8 }
70
71 bool ValidateInputParameters(int column_index, const std::vector<int> &local_column_pointers,
72 const std::vector<double> &local_values, const std::vector<int> &local_row_indices) {
73
1/2
✓ Branch 0 taken 208 times.
✗ Branch 1 not taken.
208 if (column_index < 0 || column_index >= static_cast<int>(local_column_pointers.size()) - 1) {
74 return false;
75 }
76
77
1/2
✓ Branch 0 taken 208 times.
✗ Branch 1 not taken.
208 if (local_values.size() != local_row_indices.size()) {
78 return false;
79 }
80
81 return true;
82 }
83
84 bool ValidateTransposedMatrix(const CompressedColumnMatrix &transposed_matrix_a) {
85
2/4
✓ Branch 0 taken 208 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 208 times.
✗ Branch 3 not taken.
208 if (transposed_matrix_a.column_count <= 0 || transposed_matrix_a.row_count <= 0) {
86 return false;
87 }
88
89 208 if (transposed_matrix_a.column_pointer_data.size() !=
90
1/2
✓ Branch 0 taken 208 times.
✗ Branch 1 not taken.
208 static_cast<std::size_t>(transposed_matrix_a.column_count) + 1) {
91 return false;
92 }
93
94 return true;
95 }
96
97 bool ValidateColumnRange(int column_start_position, int column_end_position, const std::vector<double> &local_values) {
98
2/4
✓ Branch 0 taken 208 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 208 times.
✗ Branch 3 not taken.
208 return column_start_position >= 0 && static_cast<std::size_t>(column_end_position) <= local_values.size() &&
99 column_start_position <= column_end_position;
100 }
101
102 bool ValidateTemporaryArrays(const std::vector<double> &temporary_row_values, const std::vector<int> &row_marker_array,
103 int column_count) {
104
2/4
✓ Branch 0 taken 208 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 208 times.
✗ Branch 3 not taken.
208 return temporary_row_values.size() == static_cast<std::size_t>(column_count) &&
105 row_marker_array.size() == static_cast<std::size_t>(column_count);
106 }
107
108 bool ValidateTransposedRange(int transposed_start, int transposed_end, const std::vector<double> &transposed_values) {
109
2/4
✓ Branch 0 taken 963 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 963 times.
✗ Branch 3 not taken.
963 return transposed_start >= 0 && static_cast<std::size_t>(transposed_end) <= transposed_values.size() &&
110 transposed_start <= transposed_end;
111 }
112
113 void ProcessTransposedElement(int matrix_a_row, int column_index, double matrix_a_value, double matrix_b_value,
114 std::vector<double> &temporary_row_values, std::vector<int> &row_marker_array) {
115 5524 if (row_marker_array[matrix_a_row] != column_index) {
116 3903 row_marker_array[matrix_a_row] = column_index;
117 3903 temporary_row_values[matrix_a_row] = matrix_a_value * matrix_b_value;
118 } else {
119 1621 temporary_row_values[matrix_a_row] += matrix_a_value * matrix_b_value;
120 }
121 }
122
123 963 void ProcessMatrixBElement(int matrix_b_row, double matrix_b_value, const CompressedColumnMatrix &transposed_matrix_a,
124 int column_index, std::vector<double> &temporary_row_values,
125 std::vector<int> &row_marker_array) {
126
1/2
✓ Branch 0 taken 963 times.
✗ Branch 1 not taken.
963 int transposed_start = transposed_matrix_a.column_pointer_data[matrix_b_row];
127
1/2
✓ Branch 0 taken 963 times.
✗ Branch 1 not taken.
963 int transposed_end = transposed_matrix_a.column_pointer_data[matrix_b_row + 1];
128
129 if (!ValidateTransposedRange(transposed_start, transposed_end, transposed_matrix_a.value_data)) {
130 return;
131 }
132
133
2/2
✓ Branch 0 taken 5524 times.
✓ Branch 1 taken 963 times.
6487 for (int transposed_index = transposed_start; transposed_index < transposed_end; transposed_index++) {
134
2/2
✓ Branch 0 taken 3903 times.
✓ Branch 1 taken 1621 times.
5524 int matrix_a_row = transposed_matrix_a.row_index_data[transposed_index];
135
2/2
✓ Branch 0 taken 3903 times.
✓ Branch 1 taken 1621 times.
5524 double matrix_a_value = transposed_matrix_a.value_data[transposed_index];
136
137 ProcessTransposedElement(matrix_a_row, column_index, matrix_a_value, matrix_b_value, temporary_row_values,
138 row_marker_array);
139 }
140 }
141
142 208 void ProcessLocalColumnElements(int column_start_position, int column_end_position,
143 const std::vector<double> &local_values, const std::vector<int> &local_row_indices,
144 const CompressedColumnMatrix &transposed_matrix_a, int column_index,
145 std::vector<double> &temporary_row_values, std::vector<int> &row_marker_array) {
146
2/2
✓ Branch 0 taken 963 times.
✓ Branch 1 taken 208 times.
1171 for (int element_index = column_start_position; element_index < column_end_position; element_index++) {
147 963 int matrix_b_row = local_row_indices[element_index];
148 963 double matrix_b_value = local_values[element_index];
149
150 963 ProcessMatrixBElement(matrix_b_row, matrix_b_value, transposed_matrix_a, column_index, temporary_row_values,
151 row_marker_array);
152 }
153 208 }
154
155 208 void CollectNonZeroResults(const std::vector<double> &temporary_row_values, const std::vector<int> &row_marker_array,
156 int column_index, int column_count, std::vector<double> &result_values,
157 std::vector<int> &result_row_indices) {
158
2/2
✓ Branch 0 taken 13080 times.
✓ Branch 1 taken 208 times.
13288 for (int row_index = 0; row_index < column_count; row_index++) {
159
3/4
✓ Branch 0 taken 3903 times.
✓ Branch 1 taken 9177 times.
✓ Branch 2 taken 3903 times.
✗ Branch 3 not taken.
13080 if (row_marker_array[row_index] == column_index && std::abs(temporary_row_values[row_index]) > kEpsilonThreshold) {
160 result_values.push_back(temporary_row_values[row_index]);
161 result_row_indices.push_back(row_index);
162 }
163 }
164 208 }
165
166 bool ValidateInputRange(int start_column, int end_column, int column_count) {
167
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (start_column < 0 || end_column < 0 || start_column >= end_column) {
168 return false;
169 }
170
171
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (start_column >= column_count) {
172 return false;
173 }
174
175 return true;
176 }
177
178 bool ValidateMatrixB(const CompressedColumnMatrix &matrix_b) {
179
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (matrix_b.column_count <= 0 || matrix_b.row_count <= 0) {
180 return false;
181 }
182
183
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (matrix_b.column_pointer_data.size() != static_cast<std::size_t>(matrix_b.column_count) + 1) {
184 return false;
185 }
186
187 return true;
188 }
189
190 208 int ExtractSingleColumnData(int col_index, const CompressedColumnMatrix &matrix_b, std::vector<double> &local_values,
191 std::vector<int> &local_row_indices) {
192 208 int col_start = matrix_b.column_pointer_data[col_index];
193 208 int col_end = matrix_b.column_pointer_data[col_index + 1];
194
195
2/2
✓ Branch 0 taken 963 times.
✓ Branch 1 taken 208 times.
1171 for (int idx = col_start; idx < col_end; ++idx) {
196
2/2
✓ Branch 0 taken 860 times.
✓ Branch 1 taken 103 times.
963 int row_idx = matrix_b.row_index_data[idx];
197
198 local_values.push_back(matrix_b.value_data[idx]);
199 local_row_indices.push_back(row_idx);
200 }
201
202 208 return static_cast<int>(local_values.size());
203 }
204
205 bool ValidateProcessCount(int total_processes) {
206 return total_processes > 0;
207 }
208
209 8 void InitializeResultMatrix(const CompressedColumnMatrix &matrix_a, const CompressedColumnMatrix &matrix_b,
210 CompressedColumnMatrix &result_matrix) {
211 8 result_matrix.row_count = matrix_a.row_count;
212 8 result_matrix.column_count = matrix_b.column_count;
213
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 result_matrix.non_zero_count = 0;
214 result_matrix.value_data.clear();
215 result_matrix.row_index_data.clear();
216 result_matrix.column_pointer_data.clear();
217 8 result_matrix.column_pointer_data.push_back(0);
218 8 }
219
220 8 bool ReceiveProcessDataMPI(int process_id, std::vector<double> &values, std::vector<int> &row_indices,
221 std::vector<int> &col_pointers) {
222 8 int nnz = 0;
223 8 int cols = 0;
224 MPI_Status status;
225
226
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if (MPI_Recv(&nnz, 1, MPI_INT, process_id, 0, MPI_COMM_WORLD, &status) != MPI_SUCCESS) {
227 return false;
228 }
229
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if (MPI_Recv(&cols, 1, MPI_INT, process_id, 1, MPI_COMM_WORLD, &status) != MPI_SUCCESS) {
230 return false;
231 }
232
233 const int max_size = 100000000;
234
4/8
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 8 times.
8 if (nnz < 0 || cols < 0 || nnz > max_size || cols > max_size) {
235 return false;
236 }
237
238 8 values.resize(nnz);
239 8 row_indices.resize(nnz);
240 8 col_pointers.resize(cols + 1);
241
242
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (nnz > 0) {
243
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if (MPI_Recv(values.data(), nnz, MPI_DOUBLE, process_id, 2, MPI_COMM_WORLD, &status) != MPI_SUCCESS) {
244 return false;
245 }
246
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if (MPI_Recv(row_indices.data(), nnz, MPI_INT, process_id, 3, MPI_COMM_WORLD, &status) != MPI_SUCCESS) {
247 return false;
248 }
249 }
250
251
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if (MPI_Recv(col_pointers.data(), cols + 1, MPI_INT, process_id, 4, MPI_COMM_WORLD, &status) != MPI_SUCCESS) {
252 return false;
253 }
254
255 return true;
256 }
257
258 16 void ProcessLocalWorkerData(int pid, const std::vector<std::vector<double>> &value_collections,
259 const std::vector<std::vector<int>> &row_index_collections,
260 const std::vector<std::vector<int>> &column_pointer_collections, int &value_offset,
261 CompressedColumnMatrix &result_matrix) {
262
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (value_collections[pid].size() != row_index_collections[pid].size()) {
263 return;
264 }
265
266
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (column_pointer_collections[pid].empty()) {
267 return;
268 }
269
270
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (!value_collections[pid].empty()) {
271 16 result_matrix.value_data.insert(result_matrix.value_data.end(), value_collections[pid].begin(),
272 value_collections[pid].end());
273
274 16 result_matrix.row_index_data.insert(result_matrix.row_index_data.end(), row_index_collections[pid].begin(),
275 row_index_collections[pid].end());
276 }
277
278
2/2
✓ Branch 0 taken 208 times.
✓ Branch 1 taken 16 times.
224 for (size_t i = 1; i < column_pointer_collections[pid].size(); ++i) {
279
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 208 times.
208 int adjusted_value = std::max(column_pointer_collections[pid][i], 0);
280 208 result_matrix.column_pointer_data.push_back(adjusted_value + value_offset);
281 }
282
283 16 value_offset += static_cast<int>(value_collections[pid].size());
284 }
285
286 8 void ReceiveAllWorkerData(int total_processes, std::vector<std::vector<double>> &value_collections,
287 std::vector<std::vector<int>> &row_index_collections,
288 std::vector<std::vector<int>> &column_pointer_collections,
289 std::vector<double> &local_result_values, std::vector<int> &local_result_row_indices,
290 std::vector<int> &local_result_column_pointers) {
291 value_collections[0] = std::move(local_result_values);
292 row_index_collections[0] = std::move(local_result_row_indices);
293 column_pointer_collections[0] = std::move(local_result_column_pointers);
294
295
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
16 for (int pid = 1; pid < total_processes; ++pid) {
296
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if (!ReceiveProcessDataMPI(pid, value_collections[pid], row_index_collections[pid],
297 8 column_pointer_collections[pid])) {
298 value_collections[pid].clear();
299 row_index_collections[pid].clear();
300 column_pointer_collections[pid].clear();
301 }
302 }
303 8 }
304
305
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 void FinalizeResultMatrix(CompressedColumnMatrix &result_matrix) {
306 8 result_matrix.non_zero_count = static_cast<int>(result_matrix.value_data.size());
307
308
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (result_matrix.column_pointer_data.size() != static_cast<std::size_t>(result_matrix.column_count) + 1) {
309 result_matrix.column_pointer_data.resize(result_matrix.column_count + 1);
310 if (!result_matrix.column_pointer_data.empty()) {
311 result_matrix.column_pointer_data[0] = 0;
312 }
313 }
314 8 }
315
316 } // namespace
317
318
1/2
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
16 LobanovDMultiplyMatrixMPI::LobanovDMultiplyMatrixMPI(const InType &input_matrices) {
319 SetTypeOfTask(GetStaticTypeOfTask());
320 GetInput() = input_matrices;
321
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 CompressedColumnMatrix empty_matrix;
322 empty_matrix.row_count = 0;
323 empty_matrix.column_count = 0;
324 empty_matrix.non_zero_count = 0;
325
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 GetOutput() = empty_matrix;
326 16 }
327
328 16 bool LobanovDMultiplyMatrixMPI::ValidationImpl() {
329 const auto &[matrix_a, matrix_b] = GetInput();
330
2/4
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
16 return (matrix_a.column_count == matrix_b.row_count && matrix_a.row_count > 0 && matrix_a.column_count > 0 &&
331
2/4
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
32 matrix_b.row_count > 0 && matrix_b.column_count > 0);
332 }
333
334 16 bool LobanovDMultiplyMatrixMPI::PreProcessingImpl() {
335 16 return true;
336 }
337
338
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 void LobanovDMultiplyMatrixMPI::ComputeTransposedMatrixMPI(const CompressedColumnMatrix &source_matrix,
339 CompressedColumnMatrix &transposed_result) {
340 transposed_result.ZeroInitialize();
341
342 8 transposed_result.row_count = source_matrix.column_count;
343 8 transposed_result.column_count = source_matrix.row_count;
344 8 transposed_result.non_zero_count = source_matrix.non_zero_count;
345
346 8 std::vector<int> row_element_count = CountElementsPerRow(source_matrix, transposed_result.column_count);
347
348
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 BuildColumnPointers(transposed_result, row_element_count);
349
350
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 FillTransposedMatrixData(source_matrix, transposed_result);
351 8 }
352
353 16 std::pair<int, int> LobanovDMultiplyMatrixMPI::DetermineColumnDistribution(int total_columns, int process_rank,
354 int process_count) {
355 16 int base_columns_per_process = total_columns / process_count;
356 16 int remaining_columns = total_columns % process_count;
357
358
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 int start_column = (process_rank * base_columns_per_process) + std::min(process_rank, remaining_columns);
359
2/4
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
32 int end_column = start_column + base_columns_per_process + (process_rank < remaining_columns ? 1 : 0);
360
361
3/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
24 start_column = std::max(0, std::min(start_column, total_columns));
362
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 end_column = std::max(0, std::min(end_column, total_columns));
363
364 start_column = std::min(start_column, end_column);
365
366 16 return {start_column, end_column};
367 }
368
369
1/2
✓ Branch 0 taken 208 times.
✗ Branch 1 not taken.
208 void LobanovDMultiplyMatrixMPI::ProcessLocalColumnMPI(
370 const CompressedColumnMatrix &transposed_matrix_a, const std::vector<double> &local_values,
371 const std::vector<int> &local_row_indices, const std::vector<int> &local_column_pointers, int column_index,
372 std::vector<double> &temporary_row_values, std::vector<int> &row_marker_array, std::vector<double> &result_values,
373 std::vector<int> &result_row_indices) {
374 if (!ValidateInputParameters(column_index, local_column_pointers, local_values, local_row_indices)) {
375 return;
376 }
377
378 if (!ValidateTransposedMatrix(transposed_matrix_a)) {
379 return;
380 }
381
382
1/2
✓ Branch 0 taken 208 times.
✗ Branch 1 not taken.
208 int column_start_position = local_column_pointers[column_index];
383
1/2
✓ Branch 0 taken 208 times.
✗ Branch 1 not taken.
208 int column_end_position = local_column_pointers[column_index + 1];
384
385 if (!ValidateColumnRange(column_start_position, column_end_position, local_values)) {
386 return;
387 }
388
389 if (!ValidateTemporaryArrays(temporary_row_values, row_marker_array, transposed_matrix_a.column_count)) {
390 return;
391 }
392
393 208 ProcessLocalColumnElements(column_start_position, column_end_position, local_values, local_row_indices,
394 transposed_matrix_a, column_index, temporary_row_values, row_marker_array);
395
396 208 CollectNonZeroResults(temporary_row_values, row_marker_array, column_index, transposed_matrix_a.column_count,
397 result_values, result_row_indices);
398 }
399
400
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 void LobanovDMultiplyMatrixMPI::ExtractLocalColumnData(const CompressedColumnMatrix &matrix_b, int start_column,
401 int end_column, std::vector<double> &local_values,
402 std::vector<int> &local_row_indices,
403 std::vector<int> &local_column_pointers) {
404 local_values.clear();
405 local_row_indices.clear();
406 local_column_pointers.clear();
407 16 local_column_pointers.push_back(0);
408
409
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (!ValidateInputRange(start_column, end_column, matrix_b.column_count)) {
410 return;
411 }
412
413 if (!ValidateMatrixB(matrix_b)) {
414 return;
415 }
416
417 16 end_column = std::min(end_column, matrix_b.column_count);
418
419
2/2
✓ Branch 0 taken 208 times.
✓ Branch 1 taken 16 times.
224 for (int current_col = start_column; current_col < end_column; ++current_col) {
420
2/2
✓ Branch 1 taken 150 times.
✓ Branch 2 taken 58 times.
208 int new_size = ExtractSingleColumnData(current_col, matrix_b, local_values, local_row_indices);
421 local_column_pointers.push_back(new_size);
422 }
423 }
424
425
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 void LobanovDMultiplyMatrixMPI::MultiplyLocalMatricesMPI(const CompressedColumnMatrix &transposed_matrix_a,
426 const std::vector<double> &local_values,
427 const std::vector<int> &local_row_indices,
428 const std::vector<int> &local_column_pointers,
429 int local_column_count, std::vector<double> &result_values,
430 std::vector<int> &result_row_indices,
431 std::vector<int> &result_column_pointers) {
432 result_values.clear();
433 result_row_indices.clear();
434 result_column_pointers.clear();
435 16 result_column_pointers.push_back(0);
436
437
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (transposed_matrix_a.column_count <= 0) {
438 return;
439 }
440
441
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (local_values.size() != local_row_indices.size()) {
442 return;
443 }
444
445
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (local_column_pointers.size() != static_cast<std::size_t>(local_column_count) + 1) {
446 return;
447 }
448
449
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (transposed_matrix_a.column_count > 1000000) { // разумный предел
450 return;
451 }
452
453 int matrix_rows = transposed_matrix_a.column_count;
454
455 16 std::vector<double> temp_row_buffer(matrix_rows, 0.0);
456
1/4
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
16 std::vector<int> row_tracker(matrix_rows, -1);
457
458 int column_counter = 0;
459
2/2
✓ Branch 0 taken 208 times.
✓ Branch 1 taken 16 times.
224 while (column_counter < local_column_count) {
460
1/2
✓ Branch 1 taken 208 times.
✗ Branch 2 not taken.
208 ProcessLocalColumnMPI(transposed_matrix_a, local_values, local_row_indices, local_column_pointers, column_counter,
461 temp_row_buffer, row_tracker, result_values, result_row_indices);
462
463
1/4
✓ Branch 1 taken 208 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
208 result_column_pointers.push_back(static_cast<int>(result_values.size()));
464 208 column_counter++;
465 }
466 }
467
468 8 bool LobanovDMultiplyMatrixMPI::ProcessMasterRank(const CompressedColumnMatrix &matrix_a,
469 const CompressedColumnMatrix &matrix_b,
470 std::vector<double> &local_result_values,
471 std::vector<int> &local_result_row_indices,
472 std::vector<int> &local_result_column_pointers, int total_processes) {
473
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (!ValidateProcessCount(total_processes)) {
474 return false;
475 }
476
477 8 CompressedColumnMatrix result_matrix;
478
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 InitializeResultMatrix(matrix_a, matrix_b, result_matrix);
479
480
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 std::vector<std::vector<double>> value_collections(total_processes);
481
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 std::vector<std::vector<int>> row_index_collections(total_processes);
482
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 std::vector<std::vector<int>> column_pointer_collections(total_processes);
483
484
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 ReceiveAllWorkerData(total_processes, value_collections, row_index_collections, column_pointer_collections,
485 local_result_values, local_result_row_indices, local_result_column_pointers);
486
487 8 int value_offset = 0;
488
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 8 times.
24 for (int pid = 0; pid < total_processes; ++pid) {
489
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 ProcessLocalWorkerData(pid, value_collections, row_index_collections, column_pointer_collections, value_offset,
490 result_matrix);
491 }
492
493
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 FinalizeResultMatrix(result_matrix);
494
495
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 GetOutput() = result_matrix;
496
497
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 MPI_Barrier(MPI_COMM_WORLD);
498 return true;
499 8 }
500
501 8 bool LobanovDMultiplyMatrixMPI::ProcessWorkerRank(const std::vector<double> &local_result_values,
502 const std::vector<int> &local_result_row_indices,
503 const std::vector<int> &local_result_column_pointers,
504 int local_column_count) {
505 8 int local_non_zero_count = static_cast<int>(local_result_values.size());
506 8 int local_column_total = local_column_count;
507
508 8 MPI_Send(&local_non_zero_count, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
509 8 MPI_Send(&local_column_total, 1, MPI_INT, 0, 1, MPI_COMM_WORLD);
510 8 MPI_Send(local_result_values.data(), local_non_zero_count, MPI_DOUBLE, 0, 2, MPI_COMM_WORLD);
511 8 MPI_Send(local_result_row_indices.data(), local_non_zero_count, MPI_INT, 0, 3, MPI_COMM_WORLD);
512 8 MPI_Send(local_result_column_pointers.data(), local_column_count + 1, MPI_INT, 0, 4, MPI_COMM_WORLD);
513
514 8 MPI_Barrier(MPI_COMM_WORLD);
515 8 return true;
516 }
517
518 16 bool LobanovDMultiplyMatrixMPI::RunImpl() {
519 16 int process_rank = 0;
520 16 int total_processes = 0;
521 16 MPI_Comm_rank(MPI_COMM_WORLD, &process_rank);
522 16 MPI_Comm_size(MPI_COMM_WORLD, &total_processes);
523 const auto &[matrix_a, matrix_b] = GetInput();
524
525 16 CompressedColumnMatrix transposed_matrix_a;
526 transposed_matrix_a.row_count = 0;
527 transposed_matrix_a.column_count = 0;
528 transposed_matrix_a.non_zero_count = 0;
529 transposed_matrix_a.value_data.clear();
530 transposed_matrix_a.row_index_data.clear();
531 transposed_matrix_a.column_pointer_data.clear();
532
533
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
16 if (process_rank == 0) {
534
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 ComputeTransposedMatrixMPI(matrix_a, transposed_matrix_a);
535
536 8 std::array<int, 3> transposed_dims = {transposed_matrix_a.row_count, transposed_matrix_a.column_count,
537 8 transposed_matrix_a.non_zero_count};
538
539
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 MPI_Bcast(transposed_dims.data(), 3, MPI_INT, 0, MPI_COMM_WORLD);
540
541
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (transposed_dims[2] > 0) {
542
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 MPI_Bcast(transposed_matrix_a.value_data.data(), transposed_dims[2], MPI_DOUBLE, 0, MPI_COMM_WORLD);
543
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 MPI_Bcast(transposed_matrix_a.row_index_data.data(), transposed_dims[2], MPI_INT, 0, MPI_COMM_WORLD);
544 }
545
546
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 MPI_Bcast(transposed_matrix_a.column_pointer_data.data(), transposed_dims[1] + 1, MPI_INT, 0, MPI_COMM_WORLD);
547
548 8 transposed_matrix_a.row_count = transposed_dims[0];
549 8 transposed_matrix_a.column_count = transposed_dims[1];
550 8 transposed_matrix_a.non_zero_count = transposed_dims[2];
551 } else {
552 8 std::array<int, 3> transposed_dims{};
553
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 MPI_Bcast(transposed_dims.data(), 3, MPI_INT, 0, MPI_COMM_WORLD);
554
555 8 transposed_matrix_a.row_count = transposed_dims[0];
556 8 transposed_matrix_a.column_count = transposed_dims[1];
557 8 transposed_matrix_a.non_zero_count = transposed_dims[2];
558
559
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (transposed_matrix_a.non_zero_count > 0) {
560
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 transposed_matrix_a.value_data.resize(static_cast<std::size_t>(transposed_matrix_a.non_zero_count), 0.0);
561
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 transposed_matrix_a.row_index_data.resize(static_cast<std::size_t>(transposed_matrix_a.non_zero_count), 0);
562
563
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 MPI_Bcast(transposed_matrix_a.value_data.data(), transposed_matrix_a.non_zero_count, MPI_DOUBLE, 0,
564 MPI_COMM_WORLD);
565
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 MPI_Bcast(transposed_matrix_a.row_index_data.data(), transposed_matrix_a.non_zero_count, MPI_INT, 0,
566 MPI_COMM_WORLD);
567 }
568
569
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 transposed_matrix_a.column_pointer_data.resize(static_cast<std::size_t>(transposed_matrix_a.column_count) + 1U, 0);
570
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 MPI_Bcast(transposed_matrix_a.column_pointer_data.data(), transposed_matrix_a.column_count + 1, MPI_INT, 0,
571 MPI_COMM_WORLD);
572 }
573
574 16 std::array<int, 2> columns_per_process = {0, 0};
575
576
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
16 if (process_rank == 0) {
577 8 auto [start, end] = DetermineColumnDistribution(matrix_b.column_count, process_rank, total_processes);
578 8 columns_per_process[0] = start;
579 8 columns_per_process[1] = end;
580
581
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
16 for (int pid = 1; pid < total_processes; ++pid) {
582 8 auto [pid_start, pid_end] = DetermineColumnDistribution(matrix_b.column_count, pid, total_processes);
583
584 8 std::array<int, 2> pid_columns = {pid_start, pid_end};
585
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 MPI_Send(pid_columns.data(), 2, MPI_INT, pid, 0, MPI_COMM_WORLD);
586 }
587 } else {
588
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 MPI_Recv(columns_per_process.data(), 2, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
589 }
590
591 16 int start_column = columns_per_process[0];
592 16 int end_column = columns_per_process[1];
593 16 int local_column_count = end_column - start_column;
594
595 16 std::vector<double> local_values;
596 16 std::vector<int> local_row_indices;
597 16 std::vector<int> local_column_pointers;
598
599
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 ExtractLocalColumnData(matrix_b, start_column, end_column, local_values, local_row_indices, local_column_pointers);
600
601 16 std::vector<double> result_values;
602 16 std::vector<int> result_row_indices;
603 16 std::vector<int> result_column_pointers;
604
605
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 MultiplyLocalMatricesMPI(transposed_matrix_a, local_values, local_row_indices, local_column_pointers,
606 local_column_count, result_values, result_row_indices, result_column_pointers);
607
608
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
16 if (process_rank == 0) {
609
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 return ProcessMasterRank(matrix_a, matrix_b, result_values, result_row_indices, result_column_pointers,
610 total_processes);
611 }
612
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 return ProcessWorkerRank(result_values, result_row_indices, result_column_pointers, local_column_count);
613 16 }
614
615 16 bool LobanovDMultiplyMatrixMPI::PostProcessingImpl() {
616 16 int current_process_rank = 0;
617 16 MPI_Comm_rank(MPI_COMM_WORLD, &current_process_rank);
618
619 const CompressedColumnMatrix &computed_result = GetOutput();
620
621
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
16 if (current_process_rank == 0) {
622
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 bool dimensions_valid = computed_result.row_count > 0 && computed_result.column_count > 0;
623 bool structure_valid =
624 8 computed_result.column_pointer_data.size() == static_cast<std::size_t>(computed_result.column_count) + 1U;
625 8 return dimensions_valid && structure_valid;
626 }
627
628 8 bool is_empty = computed_result.row_count == 0 && computed_result.column_count == 0;
629 8 return is_empty;
630 }
631
632 } // namespace lobanov_d_multiply_matrix_ccs
633