| 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, ¤t_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 |