| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "zyazeva_s_gauss_jordan_elimination/mpi/include/ops_mpi.hpp" | ||
| 2 | |||
| 3 | #include <mpi.h> | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <climits> | ||
| 7 | #include <cmath> | ||
| 8 | #include <cstddef> | ||
| 9 | #include <utility> | ||
| 10 | #include <vector> | ||
| 11 | |||
| 12 | #include "zyazeva_s_gauss_jordan_elimination/common/include/common.hpp" | ||
| 13 | |||
| 14 | namespace zyazeva_s_gauss_jordan_elimination { | ||
| 15 | |||
| 16 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | ZyazevaSGaussJordanElMPI::ZyazevaSGaussJordanElMPI(const InType &in) { |
| 17 | SetTypeOfTask(GetStaticTypeOfTask()); | ||
| 18 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | InType temp = in; |
| 19 | 14 | GetInput() = std::move(temp); | |
| 20 | 14 | } | |
| 21 | |||
| 22 | 14 | bool ZyazevaSGaussJordanElMPI::ValidationImpl() { | |
| 23 | 14 | int rank = 0; | |
| 24 | 14 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); | |
| 25 | |||
| 26 | 14 | bool is_valid = false; | |
| 27 | |||
| 28 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
|
14 | if (rank == 0) { |
| 29 | const auto &matrix = GetInput(); | ||
| 30 | |||
| 31 |
1/2✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
|
7 | if (matrix.empty()) { |
| 32 | is_valid = false; | ||
| 33 | } else { | ||
| 34 | 7 | int n = static_cast<int>(matrix.size()); | |
| 35 | 7 | is_valid = true; | |
| 36 | |||
| 37 |
2/2✓ Branch 0 taken 19 times.
✓ Branch 1 taken 7 times.
|
26 | for (const auto &row : matrix) { |
| 38 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
|
19 | if (row.size() != (static_cast<std::size_t>(n) + 1)) { |
| 39 | ✗ | is_valid = false; | |
| 40 | ✗ | break; | |
| 41 | } | ||
| 42 | } | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | 14 | MPI_Bcast(&is_valid, 1, MPI_C_BOOL, 0, MPI_COMM_WORLD); | |
| 47 | 14 | return is_valid; | |
| 48 | } | ||
| 49 | |||
| 50 | 14 | bool ZyazevaSGaussJordanElMPI::PreProcessingImpl() { | |
| 51 | 14 | GetOutput() = std::vector<float>(); | |
| 52 | 14 | return true; | |
| 53 | } | ||
| 54 | namespace { | ||
| 55 | |||
| 56 | 14 | std::vector<int> DistributeRows(int size, int n) { | |
| 57 | 14 | std::vector<int> rows(size); | |
| 58 | 14 | int base = n / size; | |
| 59 | 14 | int extra = n % size; | |
| 60 |
2/2✓ Branch 0 taken 28 times.
✓ Branch 1 taken 14 times.
|
42 | for (int proc = 0; proc < size; ++proc) { |
| 61 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 10 times.
|
46 | rows[proc] = base + (proc < extra ? 1 : 0); |
| 62 | } | ||
| 63 | 14 | return rows; | |
| 64 | } | ||
| 65 | |||
| 66 | void CalculateScatterParameters(int size, const std::vector<int> &rows, int width, std::vector<int> &send_counts, | ||
| 67 | std::vector<int> &displs) { | ||
| 68 | int offset_rows = 0; | ||
| 69 |
2/2✓ Branch 0 taken 28 times.
✓ Branch 1 taken 14 times.
|
42 | for (int proc = 0; proc < size; ++proc) { |
| 70 | 28 | send_counts[proc] = rows[proc] * width; | |
| 71 | 28 | displs[proc] = offset_rows * width; | |
| 72 | 28 | offset_rows += rows[proc]; | |
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | void ScatterMatrix(int rank, const std::vector<double> &flat, const std::vector<int> &send_counts, | ||
| 77 | const std::vector<int> &displs, std::vector<double> &local_matrix, int local_rows, int width) { | ||
| 78 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | MPI_Scatterv(rank == 0 ? flat.data() : nullptr, send_counts.data(), displs.data(), MPI_DOUBLE, local_matrix.data(), |
| 79 | local_rows * width, MPI_DOUBLE, 0, MPI_COMM_WORLD); | ||
| 80 | 14 | } | |
| 81 | |||
| 82 | int CalculateStartRow(int rank, const std::vector<int> &rows) { | ||
| 83 | int start_row = 0; | ||
| 84 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 14 times.
|
21 | for (int proc = 0; proc < rank; ++proc) { |
| 85 | 7 | start_row += rows[proc]; | |
| 86 | } | ||
| 87 | return start_row; | ||
| 88 | } | ||
| 89 | |||
| 90 | int FindRowOwner(int row_index, int size, const std::vector<int> &rows) { | ||
| 91 | int owner = 0; | ||
| 92 | int acc = 0; | ||
| 93 |
2/4✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 52 times.
✗ Branch 3 not taken.
|
104 | for (int proc = 0; proc < size; ++proc) { |
| 94 |
6/8✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 38 times.
✓ Branch 4 taken 52 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 14 times.
✓ Branch 7 taken 38 times.
|
104 | if (row_index >= acc && row_index < acc + rows[proc]) { |
| 95 | owner = proc; | ||
| 96 | break; | ||
| 97 | } | ||
| 98 | 28 | acc += rows[proc]; | |
| 99 | } | ||
| 100 | return owner; | ||
| 101 | } | ||
| 102 | |||
| 103 | int FindLocalPivotRow(int k, int start_row, int local_rows, const std::vector<double> &local_matrix, int width, | ||
| 104 | double k_eps) { | ||
| 105 | int local_found = INT_MAX; | ||
| 106 |
2/2✓ Branch 0 taken 51 times.
✓ Branch 1 taken 16 times.
|
67 | for (int i = 0; i < local_rows; ++i) { |
| 107 | 51 | int gi = start_row + i; | |
| 108 |
2/2✓ Branch 0 taken 19 times.
✓ Branch 1 taken 32 times.
|
51 | if (gi < k) { |
| 109 | 19 | continue; | |
| 110 | } | ||
| 111 | 32 | if (std::fabs(local_matrix[(static_cast<std::size_t>(i) * static_cast<std::size_t>(width)) + | |
| 112 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 22 times.
|
32 | static_cast<std::size_t>(k)]) > k_eps) { |
| 113 | local_found = gi; | ||
| 114 | break; | ||
| 115 | } | ||
| 116 | } | ||
| 117 | return local_found; | ||
| 118 | } | ||
| 119 | |||
| 120 | int FindGlobalPivotRow(int local_found) { | ||
| 121 | 38 | int global_found = 0; | |
| 122 |
1/2✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
|
38 | MPI_Allreduce(&local_found, &global_found, 1, MPI_INT, MPI_MIN, MPI_COMM_WORLD); |
| 123 | 38 | return global_found; | |
| 124 | } | ||
| 125 | |||
| 126 | void SwapRowsSameOwner(int rank, int owner, int k, int global_found, int start_row, std::vector<double> &local_matrix, | ||
| 127 | int width) { | ||
| 128 | 38 | if (rank == owner) { | |
| 129 | 19 | int lk = k - start_row; | |
| 130 | 19 | int lf = global_found - start_row; | |
| 131 |
2/2✓ Branch 0 taken 76 times.
✓ Branch 1 taken 19 times.
|
95 | for (int j = 0; j < width; ++j) { |
| 132 | std::swap( | ||
| 133 | 76 | local_matrix[(static_cast<std::size_t>(lk) * static_cast<std::size_t>(width)) + static_cast<std::size_t>(j)], | |
| 134 | 76 | local_matrix[(static_cast<std::size_t>(lf) * static_cast<std::size_t>(width)) + static_cast<std::size_t>(j)]); | |
| 135 | } | ||
| 136 | } | ||
| 137 | } | ||
| 138 | |||
| 139 | 38 | void NormalizePivotRow(int rank, int owner, int k, int start_row, std::vector<double> &local_matrix, | |
| 140 | std::vector<double> &pivot, int width, double k_eps) { | ||
| 141 |
2/2✓ Branch 0 taken 19 times.
✓ Branch 1 taken 19 times.
|
38 | if (rank == owner) { |
| 142 | 19 | int lk = k - start_row; | |
| 143 | double piv = | ||
| 144 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
|
19 | local_matrix[(static_cast<std::size_t>(lk) * static_cast<std::size_t>(width)) + static_cast<std::size_t>(k)]; |
| 145 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
|
19 | if (std::fabs(piv) < k_eps) { |
| 146 | ✗ | MPI_Abort(MPI_COMM_WORLD, 1); | |
| 147 | } | ||
| 148 |
2/2✓ Branch 0 taken 76 times.
✓ Branch 1 taken 19 times.
|
95 | for (int j = 0; j < width; ++j) { |
| 149 | 76 | pivot[j] = | |
| 150 | 76 | local_matrix[(static_cast<std::size_t>(lk) * static_cast<std::size_t>(width)) + static_cast<std::size_t>(j)] / | |
| 151 | piv; | ||
| 152 | } | ||
| 153 | using DiffT = std::vector<double>::difference_type; | ||
| 154 |
1/2✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
|
19 | std::ranges::copy(pivot, local_matrix.begin() + (static_cast<DiffT>(lk) * static_cast<DiffT>(width))); |
| 155 | } | ||
| 156 | |||
| 157 | 38 | MPI_Bcast(pivot.data(), width, MPI_DOUBLE, owner, MPI_COMM_WORLD); | |
| 158 | 38 | } | |
| 159 | |||
| 160 | 38 | void EliminateColumn(int k, int start_row, int local_rows, std::vector<double> &local_matrix, | |
| 161 | const std::vector<double> &pivot, int width) { | ||
| 162 |
2/2✓ Branch 0 taken 57 times.
✓ Branch 1 taken 38 times.
|
95 | for (int i = 0; i < local_rows; ++i) { |
| 163 | 57 | int gi = start_row + i; | |
| 164 |
2/2✓ Branch 0 taken 19 times.
✓ Branch 1 taken 38 times.
|
57 | if (gi == k) { |
| 165 | 19 | continue; | |
| 166 | } | ||
| 167 | double f = | ||
| 168 | 38 | local_matrix[(static_cast<std::size_t>(i) * static_cast<std::size_t>(width)) + static_cast<std::size_t>(k)]; | |
| 169 |
2/2✓ Branch 0 taken 162 times.
✓ Branch 1 taken 38 times.
|
200 | for (int j = 0; j < width; ++j) { |
| 170 | 162 | local_matrix[(static_cast<std::size_t>(i) * static_cast<std::size_t>(width)) + static_cast<std::size_t>(j)] -= | |
| 171 | 162 | f * pivot[j]; | |
| 172 | } | ||
| 173 | } | ||
| 174 | 38 | } | |
| 175 | |||
| 176 | 14 | std::vector<double> ExtractLocalSolution(int local_rows, const std::vector<double> &local_matrix, int width, int n) { | |
| 177 | 14 | std::vector<double> local_solution(local_rows); | |
| 178 |
2/2✓ Branch 0 taken 19 times.
✓ Branch 1 taken 14 times.
|
33 | for (int i = 0; i < local_rows; ++i) { |
| 179 | 19 | local_solution[i] = | |
| 180 | 19 | local_matrix[(static_cast<std::size_t>(i) * static_cast<std::size_t>(width)) + static_cast<std::size_t>(n)]; | |
| 181 | } | ||
| 182 | 14 | return local_solution; | |
| 183 | } | ||
| 184 | |||
| 185 | 14 | std::vector<double> GatherSolutions(int rank, int size, const std::vector<int> &rows, | |
| 186 | const std::vector<double> &local_solution, int n) { | ||
| 187 | 14 | std::vector<int> sol_counts(size); | |
| 188 |
1/4✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
14 | std::vector<int> sol_displs(size); |
| 189 | |||
| 190 | int off = 0; | ||
| 191 |
2/2✓ Branch 0 taken 28 times.
✓ Branch 1 taken 14 times.
|
42 | for (int proc = 0; proc < size; ++proc) { |
| 192 | 28 | sol_counts[proc] = rows[proc]; | |
| 193 | 28 | sol_displs[proc] = off; | |
| 194 | 28 | off += sol_counts[proc]; | |
| 195 | } | ||
| 196 | |||
| 197 | 14 | std::vector<double> solution; | |
| 198 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
|
14 | if (rank == 0) { |
| 199 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
7 | solution.resize(static_cast<std::size_t>(n)); |
| 200 | } | ||
| 201 | |||
| 202 |
3/4✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
|
21 | MPI_Gatherv(local_solution.data(), static_cast<int>(local_solution.size()), MPI_DOUBLE, |
| 203 | rank == 0 ? solution.data() : nullptr, sol_counts.data(), sol_displs.data(), MPI_DOUBLE, 0, | ||
| 204 | MPI_COMM_WORLD); | ||
| 205 | |||
| 206 | 14 | return solution; | |
| 207 | } | ||
| 208 | |||
| 209 | 14 | bool SetFinalOutput(int rank, const std::vector<double> &solution, std::vector<float> &output_ref) { | |
| 210 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
|
14 | if (rank == 0) { |
| 211 | 7 | std::vector<float> final_solution(solution.size()); | |
| 212 |
2/2✓ Branch 0 taken 19 times.
✓ Branch 1 taken 7 times.
|
26 | for (std::size_t i = 0; i < solution.size(); ++i) { |
| 213 | 19 | final_solution[i] = static_cast<float>(solution[i]); | |
| 214 | } | ||
| 215 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
7 | output_ref = final_solution; |
| 216 | } | ||
| 217 | 14 | return true; | |
| 218 | } | ||
| 219 | |||
| 220 | } // namespace | ||
| 221 | |||
| 222 | 14 | bool ZyazevaSGaussJordanElMPI::RunImpl() { | |
| 223 | 14 | int rank = 0; | |
| 224 | 14 | int size = 0; | |
| 225 | 14 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); | |
| 226 | 14 | MPI_Comm_size(MPI_COMM_WORLD, &size); | |
| 227 | |||
| 228 | const auto &input = GetInput(); | ||
| 229 | |||
| 230 | 14 | int n = 0; | |
| 231 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
|
14 | if (rank == 0) { |
| 232 | 7 | n = static_cast<int>(input.size()); | |
| 233 | } | ||
| 234 | static_cast<void>(input); | ||
| 235 | 14 | MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD); | |
| 236 | |||
| 237 | 14 | const int width = n + 1; | |
| 238 | |||
| 239 | 14 | std::vector<double> flat(static_cast<std::size_t>(n) * static_cast<std::size_t>(width)); | |
| 240 | |||
| 241 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
|
14 | if (rank == 0) { |
| 242 |
2/2✓ Branch 0 taken 19 times.
✓ Branch 1 taken 7 times.
|
26 | for (int i = 0; i < n; ++i) { |
| 243 |
2/2✓ Branch 0 taken 76 times.
✓ Branch 1 taken 19 times.
|
95 | for (int j = 0; j < width; ++j) { |
| 244 | 76 | flat[(static_cast<std::size_t>(i) * static_cast<std::size_t>(width)) + static_cast<std::size_t>(j)] = | |
| 245 | 76 | static_cast<double>(input[static_cast<std::size_t>(i)][static_cast<std::size_t>(j)]); | |
| 246 | } | ||
| 247 | } | ||
| 248 | } | ||
| 249 | |||
| 250 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | MPI_Bcast(flat.data(), n * width, MPI_DOUBLE, 0, MPI_COMM_WORLD); |
| 251 | |||
| 252 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | std::vector<int> rows = DistributeRows(size, n); |
| 253 | |||
| 254 |
1/4✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
14 | std::vector<int> send_counts(size); |
| 255 |
1/4✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
14 | std::vector<int> displs(size); |
| 256 | 14 | CalculateScatterParameters(size, rows, width, send_counts, displs); | |
| 257 | |||
| 258 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | int local_rows = rows[rank]; |
| 259 |
1/4✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
14 | std::vector<double> local_matrix(static_cast<std::size_t>(local_rows) * width); |
| 260 | |||
| 261 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
|
14 | ScatterMatrix(rank, flat, send_counts, displs, local_matrix, local_rows, width); |
| 262 | |||
| 263 | 14 | int start_row = CalculateStartRow(rank, rows); | |
| 264 | |||
| 265 | const double k_eps = 1e-12; | ||
| 266 |
1/4✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
14 | std::vector<double> pivot(width); |
| 267 | |||
| 268 |
2/2✓ Branch 0 taken 38 times.
✓ Branch 1 taken 14 times.
|
52 | for (int k = 0; k < n; ++k) { |
| 269 | 38 | int owner = FindRowOwner(k, size, rows); | |
| 270 | |||
| 271 | int local_found = FindLocalPivotRow(k, start_row, local_rows, local_matrix, width, k_eps); | ||
| 272 | 38 | int global_found = FindGlobalPivotRow(local_found); | |
| 273 | |||
| 274 | 38 | int owner2 = FindRowOwner(global_found, size, rows); | |
| 275 | |||
| 276 |
1/2✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
|
38 | if (owner == owner2) { |
| 277 |
2/2✓ Branch 0 taken 19 times.
✓ Branch 1 taken 19 times.
|
38 | SwapRowsSameOwner(rank, owner, k, global_found, start_row, local_matrix, width); |
| 278 | } | ||
| 279 | |||
| 280 |
1/2✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
|
38 | NormalizePivotRow(rank, owner, k, start_row, local_matrix, pivot, width, k_eps); |
| 281 | 38 | EliminateColumn(k, start_row, local_rows, local_matrix, pivot, width); | |
| 282 | } | ||
| 283 | |||
| 284 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | std::vector<double> local_solution = ExtractLocalSolution(local_rows, local_matrix, width, n); |
| 285 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | std::vector<double> solution = GatherSolutions(rank, size, rows, local_solution, n); |
| 286 | |||
| 287 | auto &output_ref = const_cast<std::vector<float> &>(GetOutput()); | ||
| 288 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
28 | return SetFinalOutput(rank, solution, output_ref); |
| 289 | } | ||
| 290 | |||
| 291 | 14 | bool ZyazevaSGaussJordanElMPI::PostProcessingImpl() { | |
| 292 | 14 | int rank = 0; | |
| 293 | 14 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); | |
| 294 | |||
| 295 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
|
14 | if (rank == 0) { |
| 296 | 7 | return !GetOutput().empty(); | |
| 297 | } | ||
| 298 | return true; | ||
| 299 | } | ||
| 300 | |||
| 301 | } // namespace zyazeva_s_gauss_jordan_elimination | ||
| 302 |