Arcane  4.2.3.0
Developer documentation
Loading...
Searching...
No Matches
DistributedCoarsening.h
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/* DistributedCoarsening.h (C) 2000-2026 */
9/* */
10/* Distributed coarsening algorithms. */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCCORE_ALINA_MPI_DISTRIBUTEDCOARSENING_H
13#define ARCCORE_ALINA_MPI_DISTRIBUTEDCOARSENING_H
14/*---------------------------------------------------------------------------*/
15/*---------------------------------------------------------------------------*/
16/*
17 * This file is based on the work on AMGCL library (version march 2026)
18 * which can be found at https://github.com/ddemidov/amgcl.
19 *
20 * Copyright (c) 2012-2022 Denis Demidov <dennis.demidov@gmail.com>
21 * SPDX-License-Identifier: MIT
22 */
23/*---------------------------------------------------------------------------*/
24/*---------------------------------------------------------------------------*/
25
26#include "arccore/alina/BuiltinBackend.h"
27#include "arccore/alina/AlinaUtils.h"
28#include "arccore/alina/Coarsening.h"
29#include "arccore/alina/MessagePassingUtils.h"
30#include "arccore/alina/DistributedMatrix.h"
31
32#include <cstddef>
33#include <tuple>
34#include <memory>
35#include <numeric>
36#include <cassert>
37
38/*---------------------------------------------------------------------------*/
39/*---------------------------------------------------------------------------*/
40
41namespace Arcane::Alina
42{
43
44/*---------------------------------------------------------------------------*/
45/*---------------------------------------------------------------------------*/
49template <class Backend>
50struct DistributedPMISAggregation
51{
52 typedef typename Backend::value_type value_type;
53 typedef typename math::scalar_of<value_type>::type scalar_type;
54 typedef DistributedMatrix<Backend> matrix;
55 typedef CommunicationPattern<Backend> CommPattern;
56 using build_matrix = Backend::matrix;
57 using col_type = Backend::col_type;
58 using ptr_type = Backend::ptr_type;
60 using bool_matrix = bool_backend::matrix;
61
62 struct params
63 {
66
67 // Strong connectivity threshold
68 double eps_strong = 0.08;
69
70 // Block size for non-scalar problems.
71 Int32 block_size = 1;
72
73 params() = default;
74
75 params(const PropertyTree& p)
76 : ARCCORE_ALINA_PARAMS_IMPORT_CHILD(p, nullspace)
77 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, eps_strong)
78 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, block_size)
79 {
80 p.check_params({ "nullspace", "eps_strong", "block_size" });
81 }
82
83 void get(PropertyTree& p, const std::string& path) const
84 {
85 ARCCORE_ALINA_PARAMS_EXPORT_CHILD(p, path, nullspace);
86 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, eps_strong);
87 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, block_size);
88 }
89
90 } & prm;
91
92 std::shared_ptr<DistributedMatrix<bool_backend>> conn;
93 std::shared_ptr<matrix> p_tent;
94
95 DistributedPMISAggregation(const matrix& A, params& prm)
96 : prm(prm)
97 {
98 ptrdiff_t n = A.loc_rows();
100 UniqueArray<int> owner(n);
101
102 if (prm.block_size == 1) {
103 conn = conn_strength(A, prm.eps_strong);
104
105 ptrdiff_t naggr = aggregates(*conn, state, owner);
106 p_tent = tentative_prolongation(A.comm(), n, naggr, state, owner);
107 }
108 else {
109 typedef typename math::scalar_of<value_type>::type scalar;
110 using sbackend = BuiltinBackend<scalar, col_type, ptr_type>;
111
112 ptrdiff_t np = n / prm.block_size;
113
114 assert(np * prm.block_size == n && "Matrix size should be divisible by block_size");
115
116 DistributedMatrix<sbackend> A_pw(A.comm(),
117 pointwise_matrix(*A.local(), prm.block_size),
118 pointwise_matrix(*A.remote(), prm.block_size));
119
120 auto conn_pw = conn_strength(A_pw, prm.eps_strong);
121
122 UniqueArray<ptrdiff_t> state_pw(np);
123 UniqueArray<int> owner_pw(np);
124
125 ptrdiff_t naggr = aggregates(*conn_pw, state_pw, owner_pw);
126
127 conn = std::make_shared<DistributedMatrix<bool_backend>>(A.comm(),
128 expand_conn(*A.local(), *A_pw.local(), *conn_pw->local(), prm.block_size),
129 expand_conn(*A.remote(), *A_pw.remote(), *conn_pw->remote(), prm.block_size));
130
131 arccoreParallelFor(0, np, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
132 for (ptrdiff_t ip = begin; ip < (begin + size); ++ip) {
133 ptrdiff_t i = ip * prm.block_size;
134 ptrdiff_t s = state_pw[ip];
135 int o = owner_pw[ip];
136
137 for (unsigned k = 0; k < prm.block_size; ++k) {
138 state[i + k] = (s < 0) ? s : (s * prm.block_size + k);
139 owner[i + k] = o;
140 }
141 }
142 });
143
144 p_tent = tentative_prolongation(A.comm(), n, naggr * prm.block_size, state, owner);
145 }
146 }
147
148 std::shared_ptr<DistributedMatrix<bool_backend>>
149 squared_interface(const DistributedMatrix<bool_backend>& A)
150 {
151 const CommunicationPattern<bool_backend>& C = A.cpat();
152
153 bool_matrix& A_loc = *A.local();
154 bool_matrix& A_rem = *A.remote();
155
156 ptrdiff_t A_rows = A.loc_rows();
157
158 ptrdiff_t A_beg = A.loc_col_shift();
159 ptrdiff_t A_end = A_beg + A_rows;
160
161 auto a_nbr = remote_rows(C, A, false);
162 bool_matrix& A_nbr = *a_nbr;
163
164 // Build mapping from global to local column numbers in the remote part of
165 // the square matrix.
166 UniqueArray<ptrdiff_t> rem_cols(A_rem.nbNonZero() + A_nbr.nbNonZero());
167
168 std::copy(A_nbr.col.data(), A_nbr.col.data() + A_nbr.nbNonZero(),
169 std::copy(A_rem.col.data(), A_rem.col.data() + A_rem.nbNonZero(), rem_cols.begin()));
170
171 std::sort(rem_cols.begin(), rem_cols.end());
172 rem_cols.erase(std::unique(rem_cols.begin(), rem_cols.end()), rem_cols.end());
173
174 ptrdiff_t n_rem_cols = 0;
175 std::unordered_map<ptrdiff_t, int> rem_idx(2 * rem_cols.size());
176 for (ptrdiff_t c : rem_cols) {
177 if (c >= A_beg && c < A_end)
178 continue;
179 rem_idx[c] = n_rem_cols++;
180 }
181
182 // Build the product.
183 auto s_loc = std::make_shared<bool_matrix>();
184 auto s_rem = std::make_shared<bool_matrix>();
185
186 bool_matrix& S_loc = *s_loc;
187 bool_matrix& S_rem = *s_rem;
188
189 S_loc.set_size(A_rows, A_rows, false);
190 S_rem.set_size(A_rows, 0, false);
191
192 S_loc.ptr[0] = 0;
193 S_rem.ptr[0] = 0;
194
195 ARCCORE_ALINA_TIC("analyze");
196 arccoreParallelFor(0, A_rows, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
197 UniqueArray<ptrdiff_t> loc_marker(A_rows, -1);
198 UniqueArray<ptrdiff_t> rem_marker(n_rem_cols, -1);
199
200 for (ptrdiff_t ia = begin; ia < (begin + size); ++ia) {
201 ptrdiff_t loc_cols = 0;
202 ptrdiff_t rem_cols = 0;
203
204 for (ptrdiff_t ja = A_rem.ptr[ia], ea = A_rem.ptr[ia + 1]; ja < ea; ++ja) {
205 ptrdiff_t ca = C.local_index(A_rem.col[ja]);
206
207 for (ptrdiff_t jb = A_nbr.ptr[ca], eb = A_nbr.ptr[ca + 1]; jb < eb; ++jb) {
208 ptrdiff_t cb = A_nbr.col[jb];
209
210 if (cb >= A_beg && cb < A_end) {
211 cb -= A_beg;
212
213 if (loc_marker[cb] != ia) {
214 loc_marker[cb] = ia;
215 ++loc_cols;
216 }
217 }
218 else {
219 cb = rem_idx[cb];
220
221 if (rem_marker[cb] != ia) {
222 rem_marker[cb] = ia;
223 ++rem_cols;
224 }
225 }
226 }
227 }
228
229 for (ptrdiff_t ja = A_loc.ptr[ia], ea = A_loc.ptr[ia + 1]; ja < ea; ++ja) {
230 ptrdiff_t ca = A_loc.col[ja];
231
232 for (ptrdiff_t jb = A_rem.ptr[ca], eb = A_rem.ptr[ca + 1]; jb < eb; ++jb) {
233 ptrdiff_t cb = rem_idx[A_rem.col[jb]];
234
235 if (rem_marker[cb] != ia) {
236 rem_marker[cb] = ia;
237 ++rem_cols;
238 }
239 }
240 }
241
242 if (rem_cols) {
243 for (ptrdiff_t ja = A_loc.ptr[ia], ea = A_loc.ptr[ia + 1]; ja < ea; ++ja) {
244 ptrdiff_t ca = A_loc.col[ja];
245
246 for (ptrdiff_t jb = A_loc.ptr[ca], eb = A_loc.ptr[ca + 1]; jb < eb; ++jb) {
247 ptrdiff_t cb = A_loc.col[jb];
248
249 if (loc_marker[cb] != ia) {
250 loc_marker[cb] = ia;
251 ++loc_cols;
252 }
253 }
254 }
255 }
256
257 S_rem.ptr[ia + 1] = rem_cols;
258 S_loc.ptr[ia + 1] = rem_cols ? loc_cols : 0;
259 }
260 });
261 ARCCORE_ALINA_TOC("analyze");
262
263 S_loc.set_nonzeros(S_loc.scan_row_sizes(), false);
264 S_rem.set_nonzeros(S_rem.scan_row_sizes(), false);
265
266 ARCCORE_ALINA_TIC("compute");
267 arccoreParallelFor(0, A_rows, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
268 UniqueArray<ptrdiff_t> loc_marker(A_rows, -1);
269 UniqueArray<ptrdiff_t> rem_marker(n_rem_cols, -1);
270
271 for (ptrdiff_t ia = begin; ia < (begin + size); ++ia) {
272 ptrdiff_t loc_beg = S_loc.ptr[ia];
273 ptrdiff_t rem_beg = S_rem.ptr[ia];
274 ptrdiff_t loc_end = loc_beg;
275 ptrdiff_t rem_end = rem_beg;
276
277 if (rem_beg == S_rem.ptr[ia + 1])
278 continue;
279
280 for (ptrdiff_t ja = A_loc.ptr[ia], ea = A_loc.ptr[ia + 1]; ja < ea; ++ja) {
281 ptrdiff_t ca = A_loc.col[ja];
282
283 for (ptrdiff_t jb = A_loc.ptr[ca], eb = A_loc.ptr[ca + 1]; jb < eb; ++jb) {
284 ptrdiff_t cb = A_loc.col[jb];
285
286 if (loc_marker[cb] < loc_beg) {
287 loc_marker[cb] = loc_end;
288 S_loc.col[loc_end] = cb;
289 ++loc_end;
290 }
291 }
292
293 for (ptrdiff_t jb = A_rem.ptr[ca], eb = A_rem.ptr[ca + 1]; jb < eb; ++jb) {
294 ptrdiff_t gb = A_rem.col[jb];
295 ptrdiff_t cb = rem_idx[gb];
296
297 if (rem_marker[cb] < rem_beg) {
298 rem_marker[cb] = rem_end;
299 S_rem.col[rem_end] = gb;
300 ++rem_end;
301 }
302 }
303 }
304
305 for (ptrdiff_t ja = A_rem.ptr[ia], ea = A_rem.ptr[ia + 1]; ja < ea; ++ja) {
306 ptrdiff_t ca = C.local_index(A_rem.col[ja]);
307
308 for (ptrdiff_t jb = A_nbr.ptr[ca], eb = A_nbr.ptr[ca + 1]; jb < eb; ++jb) {
309 ptrdiff_t gb = A_nbr.col[jb];
310
311 if (gb >= A_beg && gb < A_end) {
312 ptrdiff_t cb = gb - A_beg;
313
314 if (loc_marker[cb] < loc_beg) {
315 loc_marker[cb] = loc_end;
316 S_loc.col[loc_end] = cb;
317 ++loc_end;
318 }
319 }
320 else {
321 ptrdiff_t cb = rem_idx[gb];
322
323 if (rem_marker[cb] < rem_beg) {
324 rem_marker[cb] = rem_end;
325 S_rem.col[rem_end] = gb;
326 ++rem_end;
327 }
328 }
329 }
330 }
331 }
332 });
333 ARCCORE_ALINA_TOC("compute");
334
335 return std::make_shared<DistributedMatrix<bool_backend>>(A.comm(), s_loc, s_rem);
336 }
337
338 template <class B>
339 std::shared_ptr<DistributedMatrix<bool_backend>>
340 conn_strength(const DistributedMatrix<B>& A, scalar_type eps_strong)
341 {
342 typedef typename B::value_type val_type;
343 typedef CSRMatrix<val_type> B_matrix;
344
345 ARCCORE_ALINA_TIC("conn_strength");
346 ptrdiff_t n = A.loc_rows();
347
348 const B_matrix& A_loc = *A.local();
349 const B_matrix& A_rem = *A.remote();
350 const CommunicationPattern<B>& C = A.cpat();
351
352 scalar_type eps_squared = eps_strong * eps_strong;
353
354 auto d = diagonal(A_loc);
355 numa_vector<val_type>& D = *d;
356
357 UniqueArray<val_type> D_loc(C.send.count());
358 UniqueArray<val_type> D_rem(C.recv.count());
359
360 for (size_t i = 0, nv = C.send.count(); i < nv; ++i)
361 D_loc[i] = D[C.send.col[i]];
362
363 if (D_loc.size() != 0)
364 C.exchange(&D_loc[0], &D_rem[0]);
365
366 auto s_loc = std::make_shared<bool_matrix>();
367 auto s_rem = std::make_shared<bool_matrix>();
368
369 bool_matrix& S_loc = *s_loc;
370 bool_matrix& S_rem = *s_rem;
371
372 S_loc.set_size(n, n, true);
373 S_rem.set_size(n, 0, true);
374
375 S_loc.val.resize(A_loc.nbNonZero());
376 S_rem.val.resize(A_rem.nbNonZero());
377
378 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
379 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
380 val_type eps_dia_i = eps_squared * D[i];
381
382 for (ptrdiff_t j = A_loc.ptr[i], e = A_loc.ptr[i + 1]; j < e; ++j) {
383 ptrdiff_t c = A_loc.col[j];
384 val_type v = A_loc.val[j];
385
386 if ((S_loc.val[j] = (c == i || (eps_dia_i * D[c] < v * v))))
387 ++S_loc.ptr[i + 1];
388 }
389
390 for (ptrdiff_t j = A_rem.ptr[i], e = A_rem.ptr[i + 1]; j < e; ++j) {
391 ptrdiff_t c = C.local_index(A_rem.col[j]);
392 val_type v = A_rem.val[j];
393
394 if ((S_rem.val[j] = (eps_dia_i * D_rem[c] < v * v)))
395 ++S_rem.ptr[i + 1];
396 }
397 }
398 });
399
400 S_loc.setNbNonZero(S_loc.scan_row_sizes());
401 S_rem.setNbNonZero(S_rem.scan_row_sizes());
402
403 S_loc.col.resize(S_loc.nbNonZero());
404 S_rem.col.resize(S_rem.nbNonZero());
405
406 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
407 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
408 ptrdiff_t loc_head = S_loc.ptr[i];
409 ptrdiff_t rem_head = S_rem.ptr[i];
410
411 for (ptrdiff_t j = A_loc.ptr[i], e = A_loc.ptr[i + 1]; j < e; ++j)
412 if (S_loc.val[j])
413 S_loc.col[loc_head++] = A_loc.col[j];
414
415 for (ptrdiff_t j = A_rem.ptr[i], e = A_rem.ptr[i + 1]; j < e; ++j)
416 if (S_rem.val[j])
417 S_rem.col[rem_head++] = A_rem.col[j];
418 }
419 });
420 ARCCORE_ALINA_TOC("conn_strength");
421
422 return std::make_shared<DistributedMatrix<bool_backend>>(A.comm(), s_loc, s_rem);
423 }
424
425 ptrdiff_t aggregates(const DistributedMatrix<bool_backend>& A,
426 UniqueArray<ptrdiff_t>& loc_state,
427 UniqueArray<int>& loc_owner)
428 {
429 ARCCORE_ALINA_TIC("PMIS");
430 static const int tag_exc_cnt = 4001;
431 static const int tag_exc_pts = 4002;
432
433 const bool_matrix& A_loc = *A.local();
434 const bool_matrix& A_rem = *A.remote();
435
436 ptrdiff_t n = A_loc.nbRow();
437
438 AlinaCommunicator comm = A.comm();
439
440 // 1. Get symbolic square of the connectivity matrix.
441 ARCCORE_ALINA_TIC("symbolic square");
442 auto S = squared_interface(A);
443 const bool_matrix& S_loc = *S->local();
444 const bool_matrix& S_rem = *S->remote();
445 const CommunicationPattern<bool_backend>& Sp = S->cpat();
446 ARCCORE_ALINA_TOC("symbolic square");
447
448 // 2. Apply PMIS algorithm to the symbolic square.
449 ptrdiff_t n_undone = 0;
450 UniqueArray<ptrdiff_t> rem_state(Sp.recv.count(), DistributedPMISAggregation::undone);
451 UniqueArray<int> rem_owner(Sp.recv.count(), -1);
452 UniqueArray<ptrdiff_t> send_state(Sp.send.count());
453 UniqueArray<int> send_owner(Sp.send.count());
454
455 // Remove lonely nodes.
456 std::atomic<ptrdiff_t> atomic_n_undone = 0;
457 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
458 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
459 ptrdiff_t wl = A_loc.ptr[i + 1] - A_loc.ptr[i];
460 ptrdiff_t wr = S_rem.ptr[i + 1] - S_rem.ptr[i];
461
462 if (wl + wr == 1) {
463 loc_state[i] = DistributedPMISAggregation::deleted;
464 ++atomic_n_undone;
465 }
466 else {
467 loc_state[i] = DistributedPMISAggregation::undone;
468 }
469
470 loc_owner[i] = -1;
471 }
472 });
473
474 n_undone = n - atomic_n_undone;
475
476 // Exchange state
477 for (ptrdiff_t i = 0, m = Sp.send.count(); i < m; ++i)
478 send_state[i] = loc_state[Sp.send.col[i]];
479 if (send_state.size() != 0)
480 Sp.exchange(&send_state[0], &rem_state[0]);
481
482 UniqueArray<UniqueArray<ptrdiff_t>> send_pts(Sp.recv.nbr.size());
483 UniqueArray<ptrdiff_t> recv_pts;
484
485 UniqueArray<MessagePassing::Request> send_cnt_req(Sp.recv.nbr.size());
486 UniqueArray<MessagePassing::Request> send_pts_req(Sp.recv.nbr.size());
487
488 ptrdiff_t naggr = 0;
489
490 UniqueArray<ptrdiff_t> nbr;
491 while (true) {
492 for (size_t i = 0; i < Sp.recv.nbr.size(); ++i)
493 send_pts[i].clear();
494
495 if (n_undone) {
496 for (ptrdiff_t i = 0; i < n; ++i) {
497 if (loc_state[i] != DistributedPMISAggregation::undone)
498 continue;
499
500 if (S_rem.ptr[i + 1] > S_rem.ptr[i]) {
501 // Boundary points
502 bool selectable = true;
503 for (ptrdiff_t j = S_rem.ptr[i], e = S_rem.ptr[i + 1]; j < e; ++j) {
504 int d, c;
505 std::tie(d, c) = Sp.remote_info(S_rem.col[j]);
506
507 if (rem_state[c] == DistributedPMISAggregation::undone && Sp.recv.nbr[d] > comm.rank) {
508 selectable = false;
509 break;
510 }
511 }
512
513 if (!selectable)
514 continue;
515
516 ptrdiff_t id = naggr++;
517 loc_owner[i] = comm.rank;
518 loc_state[i] = id;
519 --n_undone;
520
521 // A gives immediate neighbors
522 for (ptrdiff_t j = A_loc.ptr[i], e = A_loc.ptr[i + 1]; j < e; ++j) {
523 ptrdiff_t c = A_loc.col[j];
524 if (c != i) {
525 if (loc_state[c] == DistributedPMISAggregation::undone)
526 --n_undone;
527 loc_owner[c] = comm.rank;
528 loc_state[c] = id;
529 }
530 }
531
532 for (ptrdiff_t j = A_rem.ptr[i], e = A_rem.ptr[i + 1]; j < e; ++j) {
533 ptrdiff_t c = A_rem.col[j];
534 int d, k;
535 std::tie(d, k) = Sp.remote_info(c);
536
537 rem_state[k] = id;
538
539 send_pts[d].push_back(c);
540 send_pts[d].push_back(id);
541 }
542
543 // S gives removed neighbors
544 for (ptrdiff_t j = S_loc.ptr[i], e = S_loc.ptr[i + 1]; j < e; ++j) {
545 ptrdiff_t c = S_loc.col[j];
546 if (c != i && loc_state[c] == DistributedPMISAggregation::undone) {
547 loc_owner[c] = comm.rank;
548 loc_state[c] = id;
549 --n_undone;
550 }
551 }
552
553 for (ptrdiff_t j = S_rem.ptr[i], e = S_rem.ptr[i + 1]; j < e; ++j) {
554 ptrdiff_t c = S_rem.col[j];
555 int d, k;
556 std::tie(d, k) = Sp.remote_info(c);
557
558 if (rem_state[k] == DistributedPMISAggregation::undone) {
559 rem_state[k] = id;
560 send_pts[d].push_back(c);
561 send_pts[d].push_back(id);
562 }
563 }
564 }
565 else {
566 // Inner points
567 ptrdiff_t id = naggr++;
568 loc_owner[i] = comm.rank;
569 loc_state[i] = id;
570 --n_undone;
571
572 nbr.clear();
573
574 for (ptrdiff_t j = A_loc.ptr[i], e = A_loc.ptr[i + 1]; j < e; ++j) {
575 ptrdiff_t c = A_loc.col[j];
576
577 if (c != i && loc_state[c] != DistributedPMISAggregation::deleted) {
578 if (loc_state[c] == DistributedPMISAggregation::undone)
579 --n_undone;
580 loc_owner[c] = comm.rank;
581 loc_state[c] = id;
582 nbr.push_back(c);
583 }
584 }
585
586 for (ptrdiff_t k : nbr) {
587 for (ptrdiff_t j = A_loc.ptr[k], e = A_loc.ptr[k + 1]; j < e; ++j) {
588 ptrdiff_t c = A_loc.col[j];
589 if (c != k && loc_state[c] == DistributedPMISAggregation::undone) {
590 loc_owner[c] = comm.rank;
591 loc_state[c] = id;
592 --n_undone;
593 }
594 }
595 }
596 }
597 }
598 }
599 SmallArray<MessagePassing::Request> all_send_requests;
600 // Create an array to keep the values of the number of points
601 SmallArray<int> npts_values(Sp.recv.nbr.size());
602 for (size_t i = 0; i < Sp.recv.nbr.size(); ++i) {
603 int npts = send_pts[i].size();
604 npts_values[i] = npts;
605 all_send_requests.add(comm.doISend(npts_values.data() + i, 1, Sp.recv.nbr[i], tag_exc_cnt));
606
607 if (npts == 0)
608 continue;
609 all_send_requests.add(comm.doISend(&send_pts[i][0], npts, Sp.recv.nbr[i], tag_exc_pts));
610 }
611
612 for (size_t i = 0; i < Sp.send.nbr.size(); ++i) {
613 int npts = 0;
614 comm.doReceive(&npts, 1, Sp.send.nbr[i], tag_exc_cnt);
615
616 if (npts == 0)
617 continue;
618 recv_pts.resize(npts);
619 comm.doReceive(&recv_pts[0], npts, Sp.send.nbr[i], tag_exc_pts);
620
621 for (int k = 0; k < npts; k += 2) {
622 ptrdiff_t c = recv_pts[k] - Sp.loc_col_shift();
623 ptrdiff_t id = recv_pts[k + 1];
624
625 if (loc_state[c] == DistributedPMISAggregation::undone)
626 --n_undone;
627
628 loc_owner[c] = Sp.send.nbr[i];
629 loc_state[c] = id;
630 }
631 }
632 comm.waitAll(all_send_requests);
633
634 for (ptrdiff_t i = 0, m = Sp.send.count(); i < m; ++i)
635 send_state[i] = loc_state[Sp.send.col[i]];
636 if (send_state.size() != 0)
637 Sp.exchange(&send_state[0], &rem_state[0]);
638
639 if (0 == comm.reduceSum(n_undone))
640 break;
641 }
642
643 // Some of the aggregates could potentially vanish during expansion
644 // step (*) above. We need to exclude those and renumber the rest.
645 ARCCORE_ALINA_TIC("drop empty aggregates");
646 for (ptrdiff_t i = 0, m = Sp.send.count(); i < m; ++i)
647 send_owner[i] = loc_owner[Sp.send.col[i]];
648 if (send_owner.size() != 0)
649 Sp.exchange(&send_owner[0], &rem_owner[0]);
650
651 UniqueArray<ptrdiff_t> new_id(naggr + 1, 0);
652 for (ptrdiff_t i = 0; i < n; ++i) {
653 if (loc_owner[i] == comm.rank && loc_state[i] >= 0)
654 new_id[loc_state[i] + 1] = 1;
655 }
656
657 for (size_t i = 0; i < Sp.recv.count(); ++i) {
658 if (rem_owner[i] == comm.rank && rem_state[i] >= 0)
659 new_id[rem_state[i] + 1] = 1;
660 }
661
662 std::partial_sum(new_id.begin(), new_id.end(), new_id.begin());
663
664 SmallArray<MessagePassing::Request> all_send_requests;
665 if (comm.reduceSum(naggr - new_id.back()) > 0) {
666 naggr = new_id.back();
667
668 for (ptrdiff_t i = 0; i < n; ++i) {
669 if (loc_owner[i] == comm.rank && loc_state[i] >= 0) {
670 loc_state[i] = new_id[loc_state[i]];
671 }
672 }
673
674 for (size_t i = 0; i < Sp.recv.nbr.size(); ++i) {
675 send_pts[i].clear();
676 }
677
678 for (auto p = Sp.remote_begin(); p != Sp.remote_end(); ++p) {
679 ptrdiff_t c = p->first;
680
681 int d, k;
682 std::tie(d, k) = p->second;
683
684 if (rem_owner[k] == comm.rank && rem_state[k] >= 0) {
685 send_pts[d].push_back(c);
686 send_pts[d].push_back(new_id[rem_state[k]]);
687 }
688 }
689
690 for (size_t i = 0; i < Sp.recv.nbr.size(); ++i) {
691 int npts = send_pts[i].size();
692 all_send_requests.add(comm.doISend(&npts, 1, Sp.recv.nbr[i], tag_exc_cnt));
693
694 if (!npts)
695 continue;
696 all_send_requests.add(comm.doISend(&send_pts[i][0], npts, Sp.recv.nbr[i], tag_exc_pts));
697 }
698
699 for (size_t i = 0; i < Sp.send.nbr.size(); ++i) {
700 int npts;
701 comm.doReceive(&npts, 1, Sp.send.nbr[i], tag_exc_cnt);
702
703 if (!npts)
704 continue;
705 recv_pts.resize(npts);
706 comm.doReceive(&recv_pts[0], npts, Sp.send.nbr[i], tag_exc_pts);
707
708 for (int k = 0; k < npts; k += 2) {
709 ptrdiff_t c = recv_pts[k] - Sp.loc_col_shift();
710 ptrdiff_t id = recv_pts[k + 1];
711
712 loc_state[c] = id;
713 }
714 }
715 comm.waitAll(all_send_requests);
716
717 for (size_t i = 0; i < Sp.recv.nbr.size(); ++i) {
718 int npts = send_pts[i].size();
719 comm.wait(send_cnt_req[i]);
720 if (!npts)
721 continue;
722 comm.wait(send_pts_req[i]);
723 }
724 }
725
726 ARCCORE_ALINA_TOC("drop empty aggregates");
727 ARCCORE_ALINA_TOC("PMIS");
728
729 return naggr;
730 }
731
732 std::shared_ptr<matrix>
733 tentative_prolongation(AlinaCommunicator comm, ptrdiff_t n, ptrdiff_t naggr,
734 UniqueArray<ptrdiff_t>& state, UniqueArray<int>& owner)
735 {
736 auto p_loc = std::make_shared<build_matrix>();
737 auto p_rem = std::make_shared<build_matrix>();
738 build_matrix& P_loc = *p_loc;
739 build_matrix& P_rem = *p_rem;
740
741 ARCCORE_ALINA_TIC("tentative prolongation");
742
743 if (int null_cols = prm.nullspace.cols) {
744 ptrdiff_t nba = naggr / prm.block_size;
745
746 UniqueArray<ptrdiff_t> fdom = comm.exclusive_sum(n);
747 UniqueArray<ptrdiff_t> cdom = comm.exclusive_sum(naggr);
748
749 UniqueArray<int> scounts(comm.size, 0);
750 UniqueArray<int> rcounts(comm.size);
751
752 // Precompute the shape of the prolongation operator.
753 // Each row contains exactly nullspace.cols non-zero entries.
754 // Rows that do not belong to any aggregate are empty.
755 P_loc.set_size(n, null_cols * nba, true);
756 P_rem.set_size(n, 0, true);
757
758 // Also count the number of local DOFs in local aggregates
759 ptrdiff_t loc_dofs = 0;
760
761 for (ptrdiff_t i = 0; i < n; ++i) {
762 if (state[i] == DistributedPMISAggregation::deleted)
763 continue;
764
765 if (owner[i] == comm.rank) {
766 P_loc.ptr[i + 1] = null_cols;
767 ++loc_dofs;
768 }
769 else {
770 P_rem.ptr[i + 1] = null_cols;
771 ++scounts[owner[i]];
772 }
773 }
774
775 // Setup the exchange
776 SmallSpan<const int> send_counts_view(scounts);
777 SmallSpan<int> receive_counts_view(rcounts);
778 MessagePassing::mpAllToAll(comm.messagePassingMng(), send_counts_view, receive_counts_view, 1);
779
780 P_loc.set_nonzeros(P_loc.scan_row_sizes());
781 P_rem.set_nonzeros(P_rem.scan_row_sizes());
782
783 int snbr = 0;
784 int rnbr = 0;
785 for (int i = 0; i < comm.size; ++i) {
786 if (scounts[i])
787 ++snbr;
788 if (rcounts[i])
789 ++rnbr;
790 }
791
792 UniqueArray<int> send_nbr;
793 send_nbr.reserve(snbr);
794 UniqueArray<int> recv_nbr;
795 recv_nbr.reserve(rnbr);
796 UniqueArray<int> send_ptr;
797 send_ptr.reserve(snbr + 1);
798 send_ptr.push_back(0);
799 UniqueArray<int> recv_ptr;
800 recv_ptr.reserve(rnbr + 1);
801 recv_ptr.push_back(0);
802
803 for (int i = 0; i < comm.size; ++i) {
804 if (scounts[i]) {
805 send_nbr.push_back(i);
806 send_ptr.push_back(send_ptr.back() + scounts[i]);
807 }
808 if (rcounts[i]) {
809 recv_nbr.push_back(i);
810 recv_ptr.push_back(recv_ptr.back() + rcounts[i]);
811 }
812 }
813
814 int send_dofs = send_ptr.back();
815 int recv_dofs = recv_ptr.back();
816
817 UniqueArray<ptrdiff_t> send_agg(send_dofs); // IDs of the aggregates we are sending
818 UniqueArray<ptrdiff_t> send_dof(send_dofs); // DOFs included in the aggregates
819 UniqueArray<double> send_row(send_dofs * null_cols); // Rows of the nullspace matrix corresponding to the DOFs
820
821 UniqueArray<ptrdiff_t> recv_agg(recv_dofs); // IDs of the aggregates we are receiving
822 UniqueArray<ptrdiff_t> recv_dof(recv_dofs); // DOFs included in the aggregates
823 UniqueArray<double> recv_row(recv_dofs * null_cols); // Rows of the nullspace matrix corresponding to the DOFs
824
825 // Prepare the data to send
826 UniqueArray<ptrdiff_t> send_rank_ptr(comm.size + 1);
827 send_rank_ptr[0] = 0;
828 std::partial_sum(scounts.begin(), scounts.end(), send_rank_ptr.begin() + 1);
829 for (ptrdiff_t i = 0; i < n; ++i) {
830 auto s = state[i];
831 auto o = owner[i];
832
833 if (s == DistributedPMISAggregation::deleted)
834 continue;
835 if (o == comm.rank)
836 continue;
837
838 auto head = send_rank_ptr[o]++;
839
840 send_agg[head] = s;
841 send_dof[head] = i + fdom[comm.rank];
842 std::copy_n(&prm.nullspace.B[i * null_cols], null_cols, &send_row[head * null_cols]);
843 }
844
845 // Exchange the data
846 UniqueArray<MessagePassing::Request> send_req(3 * snbr);
847 UniqueArray<MessagePassing::Request> recv_req(3 * rnbr);
848
849 for (int i = 0; i < rnbr; ++i) {
850 int n = recv_nbr[i];
851 int p = recv_ptr[i];
852 int w = recv_ptr[i + 1] - p;
853
854 MessagePassing::Request* req = &recv_req[3 * i];
855
856 req[0] = comm.doIReceive(&recv_agg[p], w, n, tag_exc_agg);
857 req[1] = comm.doIReceive(&recv_dof[p], w, n, tag_exc_dof);
858 req[2] = comm.doIReceive(&recv_row[null_cols * p], null_cols * w, n, tag_exc_row);
859 }
860
861 for (int i = 0; i < snbr; ++i) {
862 int n = send_nbr[i];
863 int p = send_ptr[i];
864 int w = send_ptr[i + 1] - p;
865
866 MessagePassing::Request* req = &send_req[3 * i];
867
868 req[0] = comm.doISend(&send_agg[p], w, n, tag_exc_agg);
869 req[1] = comm.doISend(&send_dof[p], w, n, tag_exc_dof);
870 req[2] = comm.doISend(&send_row[null_cols * p], null_cols * w, n, tag_exc_row);
871 }
872
873 ARCCORE_ALINA_TIC("MPI Wait");
874 comm.waitAll(recv_req);
875 comm.waitAll(send_req);
876 ARCCORE_ALINA_TOC("MPI Wait");
877
878 // Sort the fine-level points by the aggregate number.
879 // The order vector contains tuples of (aggr, dof, src, dst),
880 // where src points to a row in B, and dst points to a row in P
881 UniqueArray<std::tuple<ptrdiff_t, ptrdiff_t, double*, value_type*>> order;
882 order.reserve(loc_dofs + recv_dofs);
883 for (ptrdiff_t i = 0; i < n; ++i) {
884 auto s = state[i];
885 auto o = owner[i];
886
887 if (s == DistributedPMISAggregation::deleted)
888 continue;
889 if (o != comm.rank)
890 continue;
891
892 order.emplace_back(s / prm.block_size, i + fdom[comm.rank],
893 &prm.nullspace.B[i * null_cols], &P_loc.val[P_loc.ptr[i]]);
894 }
895 for (ptrdiff_t i = 0; i < recv_dofs; ++i) {
896 order.emplace_back(recv_agg[i] / prm.block_size, recv_dof[i],
897 &recv_row[i * null_cols], nullptr);
898 }
899 std::sort(order.begin(), order.end());
900
901 UniqueArray<ptrdiff_t> aggr_ptr(nba + 1, 0);
902 for (size_t i = 0; i < order.size(); ++i)
903 ++aggr_ptr[std::get<0>(order[i]) + 1];
904 std::partial_sum(aggr_ptr.begin(), aggr_ptr.end(), aggr_ptr.begin());
905
906 // Compute the tentative prolongation operator and null-space vectors
907 // for the coarser level.
908 UniqueArray<double> Bnew;
909 Bnew.resize(nba * null_cols * null_cols);
910
911 arccoreParallelFor(0, nba, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
912 Alina::detail::QRFactorization<double> qr;
913 UniqueArray<double> Bpart;
914
915 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
916 auto aggr_beg = aggr_ptr[i];
917 auto aggr_end = aggr_ptr[i + 1];
918 auto d = aggr_end - aggr_beg;
919
920 Bpart.resize(d * null_cols);
921
922 for (ptrdiff_t j = aggr_beg, r = 0; j < aggr_end; ++j, ++r) {
923 auto src = std::get<2>(order[j]);
924 for (int c = 0; c < null_cols; ++c)
925 Bpart[r + d * c] = src[c];
926 }
927
928 qr.factorize(d, null_cols, &Bpart[0], Alina::detail::col_major);
929
930 for (ptrdiff_t r = 0, k = i * null_cols * null_cols; r < null_cols; ++r)
931 for (int c = 0; c < null_cols; ++c, ++k)
932 Bnew[k] = qr.R(r, c);
933
934 for (ptrdiff_t j = aggr_beg, r = 0; j < aggr_end; ++j, ++r) {
935 auto src = std::get<2>(order[j]);
936 auto dst = std::get<3>(order[j]);
937
938 if (dst) {
939 // TODO: this is just a workaround to make non-scalar value
940 // types compile. Most probably this won't actually work.
941 for (int c = 0; c < null_cols; ++c)
942 dst[c] = qr.Q(r, c) * math::identity<value_type>();
943 }
944 else {
945 for (int c = 0; c < null_cols; ++c)
946 src[c] = qr.Q(r, c);
947 }
948 }
949 }
950 });
951
952 // Exchange the computed rows of the prolongation operator with the
953 // owners.
954 for (int i = 0; i < snbr; ++i) {
955 int n = send_nbr[i];
956 int p = send_ptr[i];
957 int w = send_ptr[i + 1] - p;
958 send_req[i] = comm.doIReceive(&send_row[null_cols * p], null_cols * w, n, tag_exc_row);
959 }
960
961 for (int i = 0; i < rnbr; ++i) {
962 int n = recv_nbr[i];
963 int p = recv_ptr[i];
964 int w = recv_ptr[i + 1] - p;
965 recv_req[i] = comm.doISend(&recv_row[null_cols * p], null_cols * w, n, tag_exc_row);
966 }
967
968 // Fill column numbers
969 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
970 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
971 ptrdiff_t s = state[i];
972 if (s == DistributedPMISAggregation::deleted)
973 continue;
974
975 int d = owner[i];
976 if (d == comm.rank) {
977 auto col = &P_loc.col[P_loc.ptr[i]];
978 for (int j = 0; j < null_cols; ++j) {
979 col[j] = null_cols * s / prm.block_size + j;
980 }
981 }
982 else {
983 auto col = &P_rem.col[P_rem.ptr[i]];
984 for (int j = 0; j < null_cols; ++j) {
985 col[j] = null_cols * (s + cdom[d]) / prm.block_size + j;
986 }
987 }
988 }
989 });
990
991 ARCCORE_ALINA_TIC("MPI Wait");
992 comm.waitAll(send_req);
993 comm.waitAll(recv_req);
994 ARCCORE_ALINA_TOC("MPI Wait");
995
996 // Use the P rows computed by the neighbors
997 for (ptrdiff_t k = 0; k < send_dofs; ++k) {
998 auto i = send_dof[k] - fdom[comm.rank];
999 auto src = &send_row[k * null_cols];
1000 auto dst = &P_rem.val[P_rem.ptr[i]];
1001
1002 for (ptrdiff_t j = 0; j < null_cols; ++j) {
1003 dst[j] = src[j] * math::identity<value_type>();
1004 }
1005 }
1006
1007 std::swap(prm.nullspace.B, Bnew);
1008 }
1009 else {
1010 UniqueArray<ptrdiff_t> dom = comm.exclusive_sum(naggr);
1011
1012 P_loc.set_size(n, naggr, true);
1013 P_rem.set_size(n, 0, true);
1014
1015 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1016 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1017 if (state[i] == DistributedPMISAggregation::deleted)
1018 continue;
1019
1020 if (owner[i] == comm.rank) {
1021 ++P_loc.ptr[i + 1];
1022 }
1023 else {
1024 ++P_rem.ptr[i + 1];
1025 }
1026 }
1027 });
1028
1029 P_loc.set_nonzeros(P_loc.scan_row_sizes());
1030 P_rem.set_nonzeros(P_rem.scan_row_sizes());
1031
1032 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1033 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1034 ptrdiff_t s = state[i];
1035 if (s == DistributedPMISAggregation::deleted)
1036 continue;
1037
1038 int d = owner[i];
1039 if (d == comm.rank) {
1040 P_loc.col[P_loc.ptr[i]] = s;
1041 P_loc.val[P_loc.ptr[i]] = math::identity<value_type>();
1042 }
1043 else {
1044 P_rem.col[P_rem.ptr[i]] = s + dom[d];
1045 P_rem.val[P_rem.ptr[i]] = math::identity<value_type>();
1046 }
1047 }
1048 });
1049 }
1050 ARCCORE_ALINA_TOC("tentative prolongation");
1051
1052 return std::make_shared<matrix>(comm, p_loc, p_rem);
1053 }
1054
1055 template <class pw_matrix>
1056 std::shared_ptr<bool_matrix>
1057 expand_conn(const build_matrix& A, const pw_matrix& Ap, const bool_matrix& Cp,
1058 unsigned block_size) const
1059 {
1060 ptrdiff_t np = Cp.nbRow();
1061 ptrdiff_t n = np * block_size;
1062
1063 auto c = std::make_shared<bool_matrix>();
1064 bool_matrix& C = *c;
1065
1066 C.set_size(n, n, true);
1067 C.val.resize(A.nbNonZero());
1068
1069 arccoreParallelFor(0, np, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1070 UniqueArray<ptrdiff_t> j(block_size);
1071 UniqueArray<ptrdiff_t> e(block_size);
1072
1073 for (ptrdiff_t ip = begin; ip < (begin + size); ++ip) {
1074 ptrdiff_t ia = ip * block_size;
1075
1076 for (unsigned k = 0; k < block_size; ++k) {
1077 j[k] = A.ptr[ia + k];
1078 e[k] = A.ptr[ia + k + 1];
1079 }
1080
1081 for (ptrdiff_t jp = Ap.ptr[ip], ep = Ap.ptr[ip + 1]; jp < ep; ++jp) {
1082 ptrdiff_t cp = Ap.col[jp];
1083 bool sp = Cp.val[jp];
1084
1085 ptrdiff_t col_end = (cp + 1) * block_size;
1086
1087 for (unsigned k = 0; k < block_size; ++k) {
1088 ptrdiff_t beg = j[k];
1089 ptrdiff_t end = e[k];
1090
1091 while (beg < end && A.col[beg] < col_end) {
1092 C.val[beg++] = sp;
1093
1094 if (sp)
1095 ++C.ptr[ia + k + 1];
1096 }
1097
1098 j[k] = beg;
1099 }
1100 }
1101 }
1102 });
1103
1104 C.setNbNonZero(C.scan_row_sizes());
1105 C.col.resize(C.nbNonZero());
1106
1107 arccoreParallelFor(0, np, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1108 UniqueArray<ptrdiff_t> j(block_size);
1109 UniqueArray<ptrdiff_t> e(block_size);
1110 UniqueArray<ptrdiff_t> h(block_size);
1111
1112 for (ptrdiff_t ip = begin; ip < (begin + size); ++ip) {
1113 ptrdiff_t ia = ip * block_size;
1114
1115 for (unsigned k = 0; k < block_size; ++k) {
1116 j[k] = A.ptr[ia + k];
1117 e[k] = A.ptr[ia + k + 1];
1118 h[k] = C.ptr[ia + k];
1119 }
1120
1121 for (ptrdiff_t jp = Ap.ptr[ip], ep = Ap.ptr[ip + 1]; jp < ep; ++jp) {
1122 ptrdiff_t cp = Ap.col[jp];
1123 bool sp = Cp.val[jp];
1124
1125 ptrdiff_t col_end = (cp + 1) * block_size;
1126
1127 for (unsigned k = 0; k < block_size; ++k) {
1128 ptrdiff_t beg = j[k];
1129 ptrdiff_t end = e[k];
1130 ptrdiff_t hed = h[k];
1131
1132 while (beg < end && A.col[beg] < col_end) {
1133 if (sp)
1134 C.col[hed++] = A.col[beg];
1135 ++beg;
1136 }
1137
1138 j[k] = beg;
1139 h[k] = hed;
1140 }
1141 }
1142 }
1143 });
1144
1145 return c;
1146 }
1147
1148 private:
1149
1150 static const int undone = -2;
1151 static const int deleted = -1;
1152
1153 static const int tag_exc_agg = 4011;
1154 static const int tag_exc_dof = 4012;
1155 static const int tag_exc_row = 4013;
1156};
1157
1158/*---------------------------------------------------------------------------*/
1159/*---------------------------------------------------------------------------*/
1163template <class Backend>
1164struct DistributedAggregationCoarsening
1165{
1166 typedef typename Backend::value_type value_type;
1167 typedef typename math::scalar_of<value_type>::type scalar_type;
1168 using build_matrix = Backend::matrix;
1169
1170 struct params
1171 {
1172 // aggregation params
1173 typedef typename DistributedPMISAggregation<Backend>::params aggr_params;
1174 aggr_params aggr;
1175
1190 float over_interp = 1.5f;
1191
1192 params() = default;
1193
1194 params(const PropertyTree& p)
1195 : ARCCORE_ALINA_PARAMS_IMPORT_CHILD(p, aggr)
1196 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, over_interp)
1197 {
1198 p.check_params({ "aggr", "over_interp" });
1199 }
1200
1201 void get(Alina::PropertyTree& p, const std::string& path) const
1202 {
1203 ARCCORE_ALINA_PARAMS_EXPORT_CHILD(p, path, aggr);
1204 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, over_interp);
1205 }
1206 } prm;
1207
1208 DistributedAggregationCoarsening(const params& prm = params())
1209 : prm(prm)
1210 {}
1211
1212 std::tuple<std::shared_ptr<DistributedMatrix<Backend>>,
1213 std::shared_ptr<DistributedMatrix<Backend>>>
1214 transfer_operators(const DistributedMatrix<Backend>& A)
1215 {
1216 DistributedPMISAggregation<Backend> aggr(A, prm.aggr);
1217 return std::make_tuple(aggr.p_tent, transpose(*aggr.p_tent));
1218 }
1219
1220 std::shared_ptr<DistributedMatrix<Backend>>
1221 coarse_operator(const DistributedMatrix<Backend>& A,
1222 const DistributedMatrix<Backend>& P,
1223 const DistributedMatrix<Backend>& R) const
1224 {
1225 return detail::scaled_galerkin(A, P, R, 1 / prm.over_interp);
1226 }
1227};
1228
1229/*---------------------------------------------------------------------------*/
1230/*---------------------------------------------------------------------------*/
1231
1232template <class Backend>
1233unsigned block_size(const DistributedAggregationCoarsening<Backend>& c)
1234{
1235 return c.prm.aggr.block_size;
1236}
1237
1238/*---------------------------------------------------------------------------*/
1239/*---------------------------------------------------------------------------*/
1243template <class Backend>
1244struct DistributedSmoothedAggregationCoarsening
1245{
1246 typedef typename Backend::value_type value_type;
1247 typedef typename math::scalar_of<value_type>::type scalar_type;
1248 using build_matrix = Backend::matrix;
1249 using col_type = Backend::col_type;
1250 using ptr_type = Backend::ptr_type;
1251 using bool_backend = BuiltinBackend<char, col_type, ptr_type>;
1252 using bool_matrix = bool_backend::matrix;
1253
1254 struct params
1255 {
1256 // aggregation params
1257 typedef typename DistributedPMISAggregation<Backend>::params aggr_params;
1258 aggr_params aggr;
1259
1261 scalar_type relax = 1.0;
1262
1263 // Estimate the matrix spectral radius.
1264 // This usually improves convergence rate and results in faster solves,
1265 // but costs some time during setup.
1266 bool estimate_spectral_radius = false;
1267
1268 // Number of power iterations to apply for the spectral radius
1269 // estimation. Use Gershgorin disk theorem when power_iters = 0.
1270 int power_iters = 0;
1271
1272 params() = default;
1273
1274 params(const PropertyTree& p)
1275 : ARCCORE_ALINA_PARAMS_IMPORT_CHILD(p, aggr)
1276 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, relax)
1277 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, estimate_spectral_radius)
1278 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, power_iters)
1279 {
1280 p.check_params({ "aggr", "relax", "estimate_spectral_radius", "power_iters" });
1281 }
1282
1283 void get(PropertyTree& p, const std::string& path) const
1284 {
1285 ARCCORE_ALINA_PARAMS_EXPORT_CHILD(p, path, aggr);
1286 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, relax);
1287 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, estimate_spectral_radius);
1288 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, power_iters);
1289 }
1290 } prm;
1291
1292 DistributedSmoothedAggregationCoarsening(const params& prm = params())
1293 : prm(prm)
1294 {}
1295
1296 std::tuple<std::shared_ptr<DistributedMatrix<Backend>>,
1297 std::shared_ptr<DistributedMatrix<Backend>>>
1298 transfer_operators(const DistributedMatrix<Backend>& A)
1299 {
1300 typedef DistributedMatrix<Backend> DM;
1301 using build_matrix = Backend::matrix;
1302
1303 DistributedPMISAggregation<Backend> aggr(A, prm.aggr);
1304 prm.aggr.eps_strong *= 0.5;
1305
1306 AlinaCommunicator comm = A.comm();
1307 const build_matrix& A_loc = *A.local();
1308 const build_matrix& A_rem = *A.remote();
1309
1310 bool_matrix& S_loc = *aggr.conn->local();
1311 bool_matrix& S_rem = *aggr.conn->remote();
1312
1313 ARCCORE_ALINA_TIC("filtered matrix");
1314 ptrdiff_t n = A.loc_rows();
1315
1316 scalar_type omega = prm.relax;
1317 if (prm.estimate_spectral_radius) {
1318 omega *= static_cast<scalar_type>(4.0 / 3) / spectral_radius<true>(A, prm.power_iters);
1319 }
1320 else {
1321 omega *= static_cast<scalar_type>(2.0 / 3);
1322 }
1323
1324 auto af_loc = std::make_shared<build_matrix>();
1325 auto af_rem = std::make_shared<build_matrix>();
1326
1327 build_matrix& Af_loc = *af_loc;
1328 build_matrix& Af_rem = *af_rem;
1329
1330 numa_vector<value_type> Af_loc_val(S_loc.nbNonZero(), false);
1331 numa_vector<value_type> Af_rem_val(S_rem.nbNonZero(), false);
1332
1333 Af_loc.own_data = false;
1334 Af_loc.setNbRow(S_loc.nbRow());
1335 Af_loc.ncols = S_loc.ncols;
1336 Af_loc.setNbNonZero(S_loc.nbNonZero());
1337 Af_loc.ptr.setPointerZeroCopy(S_loc.ptr.data());
1338 Af_loc.col.setPointerZeroCopy(S_loc.col.data());
1339 Af_loc.val.setPointerZeroCopy(Af_loc_val.data());
1340
1341 Af_rem.own_data = false;
1342 Af_rem.setNbRow(S_rem.nbRow());
1343 Af_rem.ncols = S_rem.ncols;
1344 Af_rem.setNbNonZero(S_rem.nbNonZero());
1345 Af_rem.ptr.setPointerZeroCopy(S_rem.ptr.data());
1346 Af_rem.col.setPointerZeroCopy(S_rem.col.data());
1347 Af_rem.val.setPointerZeroCopy(Af_rem_val.data());
1348
1349 numa_vector<value_type> Df(n, false);
1350
1351 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1352 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1353
1354 ptrdiff_t loc_head = Af_loc.ptr[i];
1355 ptrdiff_t rem_head = Af_rem.ptr[i];
1356
1357 value_type dia_f = math::zero<value_type>();
1358
1359 for (ptrdiff_t j = A_loc.ptr[i], e = A_loc.ptr[i + 1]; j < e; ++j)
1360 if (A_loc.col[j] == i || !S_loc.val[j])
1361 dia_f += A_loc.val[j];
1362
1363 for (ptrdiff_t j = A_rem.ptr[i], e = A_rem.ptr[i + 1]; j < e; ++j)
1364 if (!S_rem.val[j])
1365 dia_f += A_rem.val[j];
1366
1367 dia_f = -omega * math::inverse(dia_f);
1368
1369 for (ptrdiff_t j = A_loc.ptr[i], e = A_loc.ptr[i + 1]; j < e; ++j) {
1370 if (A_loc.col[j] == i) {
1371 Af_loc.val[loc_head++] = (1 - omega) * math::identity<value_type>();
1372 }
1373 else if (S_loc.val[j]) {
1374 Af_loc.val[loc_head++] = dia_f * A_loc.val[j];
1375 }
1376 }
1377
1378 for (ptrdiff_t j = A_rem.ptr[i], e = A_rem.ptr[i + 1]; j < e; ++j) {
1379 if (S_rem.val[j]) {
1380 Af_rem.val[rem_head++] = dia_f * A_rem.val[j];
1381 }
1382 }
1383 }
1384 });
1385
1386 auto Af = std::make_shared<DM>(comm, af_loc, af_rem);
1387 ARCCORE_ALINA_TOC("filtered matrix");
1388
1389 // 5. Smooth tentative prolongation with the filtered matrix.
1390 ARCCORE_ALINA_TIC("smoothing");
1391 auto P = product(*Af, *aggr.p_tent);
1392 ARCCORE_ALINA_TOC("smoothing");
1393
1394 return std::make_tuple(P, transpose(*P));
1395 }
1396
1397 std::shared_ptr<DistributedMatrix<Backend>>
1398 coarse_operator(const DistributedMatrix<Backend>& A,
1399 const DistributedMatrix<Backend>& P,
1400 const DistributedMatrix<Backend>& R) const
1401 {
1402 return detail::galerkin(A, P, R);
1403 }
1404};
1405
1406/*---------------------------------------------------------------------------*/
1407/*---------------------------------------------------------------------------*/
1408
1409template <class Backend>
1410unsigned block_size(const DistributedSmoothedAggregationCoarsening<Backend>& c)
1411{
1412 return c.prm.aggr.block_size;
1413}
1414
1415/*---------------------------------------------------------------------------*/
1416/*---------------------------------------------------------------------------*/
1417
1418} // namespace Arcane::Alina
1419
1420/*---------------------------------------------------------------------------*/
1421/*---------------------------------------------------------------------------*/
1422
1423#endif
Call to handle communication pattern.
Distributed Matrix using message passing.
Class to store parameters as a hierarchical key/value tree.
Definition AlinaUtils.h:113
1D data vector with value semantics (STL style).
C void mpAllToAll(IMessagePassingMng *pm, Span< const char > send_buf, Span< char > recv_buf, Int32 count)
__host__ __device__ Real3x3 transpose(const Real3x3 &t)
Transpose the matrix.
Definition MathUtils.h:265
void arccoreParallelFor(const ComplexForLoopRanges< RankValue, IndexType_ > &loop_ranges, const ForLoopRunInfo &run_info, const LambdaType &lambda_function, const ReducerArgs &... reducer_args)
Applies the lambda function lambda_function concurrently over the iteration interval given by loop_ra...
Definition ParallelFor.h:86
std::int32_t Int32
Signed integer type of 32 bits.
Distributed non-smoothed aggregation coarsening scheme.
nullspace_params nullspace
Near nullspace parameters.
Distributed smoothed aggregation coarsening scheme.