Arcane  4.2.3.0
Developer documentation
Loading...
Searching...
No Matches
DistributedMatrixMarketSolver.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/*---------------------------------------------------------------------------*/
9/*
10 * This file is based on the work on AMGCL library (version march 2026)
11 * which can be found at https://github.com/ddemidov/amgcl.
12 *
13 * Copyright (c) 2012-2022 Denis Demidov <dennis.demidov@gmail.com>
14 * SPDX-License-Identifier: MIT
15 */
16/*---------------------------------------------------------------------------*/
17/*---------------------------------------------------------------------------*/
18
19// For Eigen
20#pragma GCC diagnostic ignored "-Wdeprecated-copy"
21#pragma GCC diagnostic ignored "-Wint-in-bool-context"
22
23#include "arccore/alina/AMG.h"
24#include "arccore/alina/CoarseningRuntime.h"
25#include "arccore/alina/RelaxationRuntime.h"
26#include "arccore/alina/DistributedSubDomainDeflation.h"
27#include "arccore/alina/DistributedSolverRuntime.h"
28#include "arccore/alina/DistributedDirectSolverRuntime.h"
29#include "arccore/alina/Profiler.h"
30
31#include "arccore/common/internal/ProgramOptions.h"
32
33#include <iostream>
34#include <iterator>
35#include <iomanip>
36#include <fstream>
37#include <vector>
38#include <algorithm>
39#include <numeric>
40#include <cmath>
41#include <cassert>
42
43using namespace Arcane;
44using namespace Arcane::Alina;
45
46using Alina::precondition;
47
48//---------------------------------------------------------------------------
49std::vector<ptrdiff_t>
50read_problem(const Alina::AlinaCommunicator& world,
51 const std::string& A_file,
52 const std::string& rhs_file,
53 const std::string& part_file,
54 int block_size,
55 std::vector<ptrdiff_t>& ptr,
56 std::vector<ptrdiff_t>& col,
57 std::vector<double>& val,
58 std::vector<double>& rhs)
59{
60 // Read partition
61 std::vector<int> part;
62 std::vector<ptrdiff_t> domain(world.size + 1, 0);
63 ptrdiff_t n = 0;
64 std::string line;
65
66 {
67 std::ifstream f(part_file.c_str());
68 precondition(f, "Failed to open part file (" + part_file + ")");
69
70 while (std::getline(f, line)) {
71 if (line[0] == '%')
72 continue;
73 std::istringstream is(line);
74 ptrdiff_t cols;
75 precondition(is >> n >> cols, "Unsupported format in matrix file");
76 break;
77 }
78
79 precondition(n, "Zero sized partition vector");
80 precondition(n % block_size == 0, "Matrix size is not divisible by block_size");
81
82 part.reserve(n);
83
84 while (std::getline(f, line)) {
85 if (line[0] == '%')
86 continue;
87 std::istringstream is(line);
88 int p;
89 precondition(is >> p, "Unsupported format in part file");
90 precondition(p < world.size, "MPI world does not correspond to partition");
91
92 part.push_back(p);
93 ++domain[p + 1];
94 }
95
96 std::partial_sum(domain.begin(), domain.end(), domain.begin());
97 }
98
99 ptrdiff_t chunk_beg = domain[world.rank];
100 ptrdiff_t chunk_end = domain[world.rank + 1];
101 ptrdiff_t chunk = chunk_end - chunk_beg;
102
103 // Reorder unknowns
104 std::vector<ptrdiff_t> order(n);
105 {
106 for (ptrdiff_t i = 0; i < n; ++i) {
107 int p = part[i];
108 int j = domain[p]++;
109
110 order[i] = j;
111 }
112
113 std::rotate(domain.begin(), domain.end() - 1, domain.end());
114 domain[0] = 0;
115 }
116
117 // Read matrix chunk
118 {
119 std::ifstream A(A_file.c_str());
120 precondition(A, "Failed to open matrix file (" + A_file + ")");
121
122 ptrdiff_t nnz = 0;
123 while (std::getline(A, line)) {
124 if (line[0] == '%')
125 continue;
126 std::istringstream is(line);
127 ptrdiff_t rows, m;
128 precondition(is >> rows >> m >> nnz, "Unsupported format in matrix file");
129 precondition(rows == n, "Matrix and partition have incompatible sizes");
130 precondition(n == m, "Non-square matrix in matrix file");
131 break;
132 }
133
134 {
135 std::vector<ptrdiff_t> I, J;
136 std::vector<double> V;
137
138 ptr.clear();
139 ptr.resize(chunk + 1, 0);
140
141 while (std::getline(A, line)) {
142 if (line[0] == '%')
143 continue;
144 std::istringstream is(line);
145 ptrdiff_t i, j;
146 double v;
147 precondition(is >> i >> j >> v, "Unsupported format in matrix file");
148 --i;
149 --j;
150
151 if (part[i] != world.rank)
152 continue;
153
154 ++ptr[order[i] + 1 - chunk_beg];
155
156 I.push_back(order[i] - chunk_beg);
157 J.push_back(order[j]);
158 V.push_back(v);
159 }
160
161 std::partial_sum(ptr.begin(), ptr.end(), ptr.begin());
162
163 ptrdiff_t loc_nnz = ptr.back();
164
165 col.clear();
166 col.resize(loc_nnz);
167 val.clear();
168 val.resize(loc_nnz);
169
170 for (ptrdiff_t i = 0; i < loc_nnz; ++i) {
171 ptrdiff_t row = I[i];
172 col[ptr[row]] = J[i];
173 val[ptr[row]] = V[i];
174 ++ptr[row];
175 }
176 std::rotate(ptr.begin(), ptr.end() - 1, ptr.end());
177 ptr[0] = 0;
178 }
179 }
180
181 // Read RHS chunk.
182 {
183 std::ifstream f(rhs_file.c_str());
184 precondition(f, "Failed to open rhs file (" + rhs_file + ")");
185
186 while (std::getline(f, line)) {
187 if (line[0] == '%')
188 continue;
189 std::istringstream is(line);
190 ptrdiff_t rows, cols;
191 precondition(is >> rows >> cols, "Unsupported format in matrix file");
192 precondition(rows == n, "RHS size should coincide with matrix size");
193 break;
194 }
195
196 rhs.clear();
197 rhs.reserve(chunk);
198
199 ptrdiff_t pos = 0;
200 while (std::getline(f, line)) {
201 if (line[0] == '%')
202 continue;
203 std::istringstream is(line);
204 double v;
205 precondition(is >> v, "Unsupported format in RHS file");
206
207 if (part[pos++] != world.rank)
208 continue;
209
210 rhs.push_back(v);
211 }
212
213 assert(rhs.size() + 1 == ptr.size());
214 }
215
216 return domain;
217}
218
219//---------------------------------------------------------------------------
220int main(int argc, char* argv[])
221{
222 auto& prof = Alina::Profiler::globalProfiler();
224 Alina::AlinaCommunicator world(mpm);
225 Alina::AlinaContext context;
226
227 if (world.rank == 0)
228 std::cout << "World size: " << world.size << std::endl;
229
230 // Read configuration from command line
231 auto coarsening = Alina::eCoarserningType::smoothed_aggregation;
232 auto relaxation = Alina::eRelaxationType::spai0;
233 auto iterative_solver = Alina::eSolverType::bicgstabl;
234 auto direct_solver = Alina::eDistributedDirectSolverType::skyline_lu;
235 std::string parameter_file;
236 std::string A_file = "A.mtx";
237 std::string rhs_file = "b.mtx";
238 std::string part_file = "partition.mtx";
239 std::string out_file;
240
241 namespace po = Arcane::ProgramOptions;
242 po::options_description desc("Options");
243
244 desc.add_options()("help,h", "show help");
245 desc.add_options()("coarsening,c",
246 po::value<Alina::eCoarserningType>(&coarsening)->default_value(coarsening),
247 "ruge_stuben, aggregation, smoothed_aggregation, smoothed_aggr_emin");
248 desc.add_options()("relaxation,r",
249 po::value<Alina::eRelaxationType>(&relaxation)->default_value(relaxation),
250 "gauss_seidel, ilu0, damped_jacobi, spai0, chebyshev");
251 desc.add_options()("iter_solver,i",
252 po::value<Alina::eSolverType>(&iterative_solver)->default_value(iterative_solver),
253 "cg, bicgstab, bicgstabl, gmres");
254 desc.add_options()("dir_solver,d",
255 po::value<Alina::eDistributedDirectSolverType>(&direct_solver)->default_value(direct_solver),
256 "skyline_lu");
257 desc.add_options()("params,p",
258 po::value<std::string>(&parameter_file),
259 "parameter file in json format");
260 desc.add_options()("matrix,A",
261 po::value<std::string>(&A_file)->default_value(A_file),
262 "The system matrix in MatrixMarket format");
263 desc.add_options()("rhs,b",
264 po::value<std::string>(&rhs_file)->default_value(rhs_file),
265 "The right-hand side in MatrixMarket format");
266 desc.add_options()("part,s",
267 po::value<std::string>(&part_file)->default_value(part_file),
268 "Partitioning of the problem in MatrixMarket format");
269 desc.add_options()("output,o",
270 po::value<std::string>(&out_file),
271 "The output file (saved in MatrixMarket format)");
272
274 po::store(po::parse_command_line(argc, argv, desc), vm);
275 po::notify(vm);
276
277 if (vm.count("help")) {
278 if (world.rank == 0)
279 std::cout << desc << std::endl;
280 return 0;
281 }
282
284 if (vm.count("params"))
285 prm.read_json(parameter_file);
286
287 prm.put("local.coarsening.type", coarsening);
288 prm.put("local.relax.type", relaxation);
289 prm.put("isolver.type", iterative_solver);
290 prm.put("dsolver.type", direct_solver);
291
292 int block_size = prm.get("precond.coarsening.aggr.block_size", 1);
293
294 prof.tic("read problem");
295 std::vector<ptrdiff_t> ptr;
296 std::vector<ptrdiff_t> col;
297 std::vector<double> val;
298 std::vector<double> rhs;
299
300 std::vector<ptrdiff_t> domain = read_problem(
301 world, A_file, rhs_file, part_file, block_size, ptr, col, val, rhs);
302
303 ptrdiff_t chunk = domain[world.rank + 1] - domain[world.rank];
304 prof.toc("read problem");
305
306 prof.tic("setup");
307 using Backend = BuiltinBackend<double>;
309 Backend,
314
315 std::function<double(ptrdiff_t, unsigned)> dv = Alina::constant_deflation(block_size);
316 prm.put("num_def_vec", block_size);
317 prm.put("def_vec", &dv);
318
319 SDD solve(context, world, std::tie(chunk, ptr, col, val), prm);
320 double tm_setup = prof.toc("setup");
321
322 std::vector<double> x(chunk, 0);
323
324 prof.tic("solve");
325 size_t iters;
326 double resid;
327 std::tie(iters, resid) = solve(rhs, x);
328 double tm_solve = prof.toc("solve");
329
330 if (vm.count("output")) {
331 prof.tic("save");
332 for (int r = 0; r < world.size; ++r) {
333 if (r == world.rank) {
334 std::ofstream f(out_file.c_str(), r == 0 ? std::ios::trunc : std::ios::app);
335
336 if (r == 0) {
337 f << "%%MatrixMarket matrix array real general\n"
338 << domain.back() << " 1\n";
339 }
340
341 std::ostream_iterator<double> oi(f, "\n");
342 std::copy(x.begin(), x.end(), oi);
343 }
344 world.barrier();
345 }
346 prof.toc("save");
347 }
348
349 if (world.rank == 0) {
350 std::cout << "Iterations: " << iters << std::endl
351 << "Error: " << resid << std::endl
352 << std::endl
353 << prof << std::endl;
354 }
355}
Algebraic multigrid method.
Definition AMG.h:71
Runtime wrapper for distributed direct solvers.
Distributed solver based on subdomain deflation.
Class to store parameters as a hierarchical key/value tree.
Definition AlinaUtils.h:113
static Ref< IMessagePassingMng > createAndInitializeMPI(int *argc, char **argv[], int wanted_thread_level)
Initialize MPI and creates a manager associated with the MPI_WORLD_COMM communicator.
Describes a set of command-line options.
Stores parsed option values.
Reference to an instance.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
Convenience wrapper around MPI_Comm.
Runtime configurable relaxation.
Pointwise constant deflation vectors.