Arcane  4.2.3.0
Developer documentation
Loading...
Searching...
No Matches
DistributedMatrix.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/* DistributedMatrix.h (C) 2000-2026 */
9/* */
10/* Distributed Matrix using message passing. */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCCORE_ALINA_MPI_DISTRIBUTED_MATRIX_H
13#define ARCCORE_ALINA_MPI_DISTRIBUTED_MATRIX_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/AlinaUtils.h"
27#include "arccore/alina/MessagePassingUtils.h"
28
29#include "arccore/accelerator/Atomic.h"
30
31#include <vector>
32#include <algorithm>
33
34#include <memory>
35#include <unordered_map>
36#include <random>
37
38#include <mpi.h>
39
40/*---------------------------------------------------------------------------*/
41/*---------------------------------------------------------------------------*/
42
43namespace Arcane::Alina
44{
45
46/*---------------------------------------------------------------------------*/
47/*---------------------------------------------------------------------------*/
51template <class Backend>
52class CommunicationPattern
53{
54 public:
55
56 typedef typename Backend::value_type value_type;
57 typedef typename math::rhs_of<value_type>::type rhs_type;
58 typedef typename math::scalar_of<value_type>::type scalar_type;
59 typedef typename Backend::matrix matrix;
60 typedef typename Backend::vector vector;
61 typedef typename Backend::params backend_params;
62 typedef typename Backend::col_type col_type;
63 typedef typename Backend::ptr_type ptr_type;
64
65 struct
66 {
67 std::vector<ptrdiff_t> nbr;
68 std::vector<ptr_type> ptr;
69 std::vector<col_type> col;
70
71 size_t count() const
72 {
73 return col.size();
74 }
75
76 mutable std::vector<rhs_type> val;
78 } send;
79
80 struct
81 {
82 std::vector<ptrdiff_t> nbr;
83 std::vector<ptr_type> ptr;
84
85 size_t count() const
86 {
87 return val.size();
88 }
89
90 mutable std::vector<rhs_type> val;
92 } recv;
93
94 std::shared_ptr<vector> x_rem;
95
96 CommunicationPattern(AlinaCommunicator comm,
97 ptrdiff_t n_loc_cols,
98 size_t n_rem_cols, const col_type* p_rem_cols)
99 : comm(comm)
100 , loc_cols(n_loc_cols)
101 {
102 ARCCORE_ALINA_TIC("communication pattern");
103 // Get domain boundaries
104 UniqueArray<ptrdiff_t> domain = comm.exclusive_sum(n_loc_cols);
105 loc_beg = domain[comm.rank];
106
107 // Renumber remote columns,
108 // find out how many remote values we need from each process.
109 std::vector<col_type> rem_cols(p_rem_cols, p_rem_cols + n_rem_cols);
110
111 std::sort(rem_cols.begin(), rem_cols.end());
112 rem_cols.erase(std::unique(rem_cols.begin(), rem_cols.end()), rem_cols.end());
113
114 ptrdiff_t ncols = rem_cols.size();
115 ptrdiff_t rnbr = 0, snbr = 0, send_size = 0;
116
117 {
118 UniqueArray<int> rcounts(comm.size, 0);
119 UniqueArray<int> scounts(comm.size);
120
121 // Build index for column renumbering;
122 // count how many domains send us data and how much.
123 idx.reserve(2 * ncols);
124 for (int i = 0, d = 0, last = -1; i < ncols; ++i) {
125 while (rem_cols[i] >= domain[d + 1])
126 ++d;
127
128 ++rcounts[d];
129
130 if (last < d) {
131 last = d;
132 ++rnbr;
133 }
134
135 idx.insert(idx.end(), std::make_pair(rem_cols[i], std::make_tuple(rnbr - 1, i)));
136 }
137
138 recv.val.resize(ncols);
139 recv.req.resize(rnbr);
140
141 recv.nbr.reserve(rnbr);
142 recv.ptr.reserve(rnbr + 1);
143 recv.ptr.push_back(0);
144
145 for (int d = 0; d < comm.size; ++d) {
146 if (rcounts[d]) {
147 recv.nbr.push_back(d);
148 recv.ptr.push_back(recv.ptr.back() + rcounts[d]);
149 }
150 }
151 MessagePassing::mpAllToAll(comm.messagePassingMng(), rcounts, scounts, 1);
152
153 for (ptrdiff_t d = 0; d < comm.size; ++d) {
154 if (scounts[d]) {
155 ++snbr;
156 send_size += scounts[d];
157 }
158 }
159
160 send.col.resize(send_size);
161 send.val.resize(send_size);
162 send.req.resize(snbr);
163
164 send.nbr.reserve(snbr);
165 send.ptr.reserve(snbr + 1);
166 send.ptr.push_back(0);
167
168 for (ptrdiff_t d = 0; d < comm.size; ++d) {
169 if (scounts[d]) {
170 send.nbr.push_back(d);
171 send.ptr.push_back(send.ptr.back() + scounts[d]);
172 }
173 }
174 }
175
176 // What columns do you need from me?
177 for (size_t i = 0; i < send.nbr.size(); ++i)
178 send.req[i] = comm.doIReceive(&send.col[send.ptr[i]], send.ptr[i + 1] - send.ptr[i],
179 send.nbr[i], tag_exc_cols);
180
181 // Here is what I need from you:
182 for (size_t i = 0; i < recv.nbr.size(); ++i)
183 recv.req[i] = comm.doISend(&rem_cols[recv.ptr[i]], recv.ptr[i + 1] - recv.ptr[i],
184 recv.nbr[i], tag_exc_cols);
185
186 ARCCORE_ALINA_TIC("MPI Wait");
187 // TODO: in multi-thead, order of wait is important. Use only one list.
188 comm.waitAll(send.req);
189 comm.waitAll(recv.req);
190 ARCCORE_ALINA_TOC("MPI Wait");
191
192 // Shift columns to send to local numbering:
193 for (col_type& c : send.col)
194 c -= loc_beg;
195
196 ARCCORE_ALINA_TOC("communication pattern");
197 }
198
199 template <class OtherBackend>
200 CommunicationPattern(const CommunicationPattern<OtherBackend>& C)
201 : comm(C.comm)
202 , idx(C.idx)
203 , loc_beg(C.loc_beg)
204 , loc_cols(C.loc_cols)
205 {
206 send.nbr = C.send.nbr;
207 send.ptr = C.send.ptr;
208 send.col = C.send.col;
209 send.val.resize(C.send.val.size());
210 send.req.resize(C.send.req.size());
211
212 recv.nbr = C.recv.nbr;
213 recv.ptr = C.recv.ptr;
214 recv.val.resize(C.recv.val.size());
215 recv.req.resize(C.recv.req.size());
216 }
217
218 void move_to_backend(const backend_params& bprm = backend_params())
219 {
220 if (!x_rem) {
221 x_rem = Backend::create_vector(recv.count(), bprm);
222 }
223
224 if (!gather) {
225 gather = std::make_shared<Gather>(loc_cols, send.col, bprm);
226 }
227 }
228
229 int domain(ptrdiff_t col) const
230 {
231 return std::get<0>(idx.at(col));
232 }
233
234 int local_index(ptrdiff_t col) const
235 {
236 return std::get<1>(idx.at(col));
237 }
238
239 std::tuple<int, int> remote_info(ptrdiff_t col) const
240 {
241 return idx.at(col);
242 }
243
244 std::unordered_map<ptrdiff_t, std::tuple<int, int>>::const_iterator
245 remote_begin() const
246 {
247 return idx.cbegin();
248 }
249
250 std::unordered_map<ptrdiff_t, std::tuple<int, int>>::const_iterator
251 remote_end() const
252 {
253 return idx.cend();
254 }
255
256 size_t renumber(size_t n, col_type* col) const
257 {
258 for (size_t i = 0; i < n; ++i)
259 col[i] = std::get<1>(idx.at(col[i]));
260 return recv.count();
261 }
262
263 bool needs_remote() const
264 {
265 return !recv.val.empty();
266 }
267
268 template <class Vector>
269 void start_exchange(const Vector& x) const
270 {
271 // Start receiving ghost values from our neighbours.
272 for (size_t i = 0; i < recv.nbr.size(); ++i)
273 recv.req[i] = comm.doIReceive(&recv.val[recv.ptr[i]], recv.ptr[i + 1] - recv.ptr[i],
274 recv.nbr[i], tag_exc_vals);
275
276 // Start sending our data to neighbours.
277 if (!send.val.empty()) {
278 (*gather)(x, send.val);
279
280 for (size_t i = 0; i < send.nbr.size(); ++i)
281 send.req[i] = comm.doISend(&send.val[send.ptr[i]], send.ptr[i + 1] - send.ptr[i],
282 send.nbr[i], tag_exc_vals);
283 }
284 }
285
286 void finish_exchange() const
287 {
288 ARCCORE_ALINA_TIC("MPI Wait");
289 // TODO: Use only one list to make sure it works in hybrid or shared memory
290 comm.waitAll(recv.req);
291 comm.waitAll(send.req);
292 ARCCORE_ALINA_TOC("MPI Wait");
293
294 if (!recv.val.empty())
295 backend::copy(recv.val, *x_rem);
296 }
297
298 template <typename T>
299 void exchange(const T* send_val, T* recv_val) const
300 {
301 for (size_t i = 0; i < recv.nbr.size(); ++i)
302 recv.req[i] = comm.doIReceive(&recv_val[recv.ptr[i]], recv.ptr[i + 1] - recv.ptr[i],
303 recv.nbr[i], tag_exc_vals);
304
305 for (size_t i = 0; i < send.nbr.size(); ++i)
306 send.req[i] = comm.doISend(const_cast<T*>(&send_val[send.ptr[i]]), send.ptr[i + 1] - send.ptr[i],
307 send.nbr[i], tag_exc_vals);
308
309 ARCCORE_ALINA_TIC("MPI Wait");
310 // TODO: Use only one list to make sure it works in hybrid or shared memory
311 comm.waitAll(recv.req);
312 comm.waitAll(send.req);
313 ARCCORE_ALINA_TOC("MPI Wait");
314 }
315
316 AlinaCommunicator mpi_comm() const
317 {
318 return comm;
319 }
320
321 ptrdiff_t loc_col_shift() const
322 {
323 return loc_beg;
324 }
325
326 private:
327
328 using Gather = Backend::gather;
329
330 static const int tag_set_comm = 1001;
331 static const int tag_exc_cols = 1002;
332 static const int tag_exc_vals = 1003;
333
335
336 std::unordered_map<ptrdiff_t, std::tuple<int, int>> idx;
337 std::shared_ptr<Gather> gather;
338 ptrdiff_t loc_beg;
339 ptrdiff_t loc_cols;
340
341 template <class B>
342 friend class CommunicationPattern;
343};
344
345/*---------------------------------------------------------------------------*/
346/*---------------------------------------------------------------------------*/
350template <class Backend>
351class DistributedMatrix
352{
353 public:
354
355 typedef typename Backend::value_type value_type;
356 typedef typename math::rhs_of<value_type>::type rhs_type;
357 typedef typename math::scalar_of<value_type>::type scalar_type;
358 typedef typename Backend::params backend_params;
359 typedef typename Backend::matrix matrix;
360 typedef CommunicationPattern<Backend> CommPattern;
361 typedef typename Backend::matrix build_matrix;
362
363 DistributedMatrix(AlinaCommunicator comm,
364 std::shared_ptr<build_matrix> a_loc,
365 std::shared_ptr<build_matrix> a_rem,
366 std::shared_ptr<CommPattern> c = std::shared_ptr<CommPattern>())
367 : a_loc(a_loc)
368 , a_rem(a_rem)
369 {
370 if (c) {
371 C = c;
372 }
373 else {
374 C = std::make_shared<CommPattern>(comm, a_loc->ncols, a_rem->nbNonZero(), a_rem->col);
375 }
376
377 a_rem->ncols = C->recv.count();
378
379 n_loc_rows = a_loc->nbRow();
380 n_loc_cols = a_loc->ncols;
381 n_loc_nonzeros = a_loc->nbNonZero() + a_rem->nbNonZero();
382
383 n_glob_rows = comm.reduceSum(n_loc_rows);
384 n_glob_cols = comm.reduceSum(n_loc_cols);
385 n_glob_nonzeros = comm.reduceSum(n_loc_nonzeros);
386 }
387
388 // Copy the distributed_matrix from another backend
389 template <class OtherBackend>
390 DistributedMatrix(const DistributedMatrix<OtherBackend>& A)
391 : a_loc(std::make_shared<build_matrix>(*A.local()))
392 , a_rem(std::make_shared<build_matrix>(*A.remote()))
393 {
394 C = std::make_shared<CommPattern>(A.cpat());
395
396 this->a_rem->ncols = C->recv.count();
397
398 n_loc_rows = A.loc_rows();
399 n_loc_cols = A.loc_cols();
400 n_loc_nonzeros = A.loc_nonzeros();
401 n_glob_rows = A.glob_rows();
402 n_glob_cols = A.glob_cols();
403 n_glob_nonzeros = A.glob_nonzeros();
404 }
405
406 template <class Matrix>
407 DistributedMatrix(AlinaCommunicator comm,
408 const Matrix& A,
409 ptrdiff_t _n_loc_cols = -1)
410 : n_loc_rows(backend::nbRow(A))
411 , n_loc_cols(_n_loc_cols < 0 ? n_loc_rows : _n_loc_cols)
412 , n_loc_nonzeros(backend::nonzeros(A))
413 {
414 // Get sizes of each domain in comm.
415 UniqueArray<ptrdiff_t> domain = comm.exclusive_sum(n_loc_cols);
416 ptrdiff_t loc_beg = domain[comm.rank];
417 ptrdiff_t loc_end = domain[comm.rank + 1];
418
419 n_glob_cols = domain.back();
420 n_glob_rows = comm.reduceSum(n_loc_rows);
421 n_glob_nonzeros = comm.reduceSum(n_loc_nonzeros);
422
423 // Split the matrix into local and remote parts.
424 a_loc = std::make_shared<build_matrix>();
425 a_rem = std::make_shared<build_matrix>();
426
427 build_matrix& A_loc = *a_loc;
428 build_matrix& A_rem = *a_rem;
429
430 A_loc.set_size(n_loc_rows, n_loc_cols, true);
431 A_rem.set_size(n_loc_rows, 0, true);
432
433 arccoreParallelFor(0, n_loc_rows, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
434 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
435 for (auto a = backend::row_begin(A, i); a; ++a) {
436 ptrdiff_t c = a.col();
437
438 if (loc_beg <= c && c < loc_end)
439 ++A_loc.ptr[i + 1];
440 else
441 ++A_rem.ptr[i + 1];
442 }
443 }
444 });
445
446 A_loc.set_nonzeros(A_loc.scan_row_sizes());
447 A_rem.set_nonzeros(A_rem.scan_row_sizes());
448
449 arccoreParallelFor(0, n_loc_rows, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
450 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
451 ptrdiff_t loc_head = A_loc.ptr[i];
452 ptrdiff_t rem_head = A_rem.ptr[i];
453
454 for (auto a = backend::row_begin(A, i); a; ++a) {
455 ptrdiff_t c = a.col();
456 value_type v = a.value();
457
458 if (loc_beg <= c && c < loc_end) {
459 A_loc.col[loc_head] = c - loc_beg;
460 A_loc.val[loc_head] = v;
461 ++loc_head;
462 }
463 else {
464 A_rem.col[rem_head] = c;
465 A_rem.val[rem_head] = v;
466 ++rem_head;
467 }
468 }
469 }
470 });
471
472 C = std::make_shared<CommPattern>(comm, n_loc_cols, a_rem->nbNonZero(), a_rem->col);
473 a_rem->ncols = C->recv.count();
474 }
475
476 AlinaCommunicator comm() const
477 {
478 return C->mpi_comm();
479 }
480
481 std::shared_ptr<build_matrix> local() const
482 {
483 return a_loc;
484 }
485
486 std::shared_ptr<build_matrix> remote() const
487 {
488 return a_rem;
489 }
490
491 std::shared_ptr<matrix> local_backend() const
492 {
493 return A_loc;
494 }
495
496 std::shared_ptr<matrix> remote_backend() const
497 {
498 return A_rem;
499 }
500
501 ptrdiff_t loc_rows() const
502 {
503 return n_loc_rows;
504 }
505
506 ptrdiff_t loc_cols() const
507 {
508 return n_loc_cols;
509 }
510
511 ptrdiff_t loc_col_shift() const
512 {
513 return C->loc_col_shift();
514 }
515
516 ptrdiff_t loc_nonzeros() const
517 {
518 return n_loc_nonzeros;
519 }
520
521 ptrdiff_t glob_rows() const
522 {
523 return n_glob_rows;
524 }
525
526 ptrdiff_t glob_cols() const
527 {
528 return n_glob_cols;
529 }
530
531 ptrdiff_t glob_nonzeros() const
532 {
533 return n_glob_nonzeros;
534 }
535
536 const CommunicationPattern<Backend>& cpat() const
537 {
538 return *C;
539 }
540
541 void set_local(std::shared_ptr<matrix> a)
542 {
543 A_loc = a;
544 }
545
546 void move_to_backend(const backend_params& bprm = backend_params(), bool keep_src = false)
547 {
548 ARCCORE_ALINA_TIC("move to backend");
549 if (!A_loc) {
550 A_loc = Backend::copy_matrix(a_loc, bprm);
551 }
552
553 if (!A_rem && a_rem && a_rem->nbNonZero() > 0) {
554 if (keep_src) {
555 auto rem_copy = std::make_shared<build_matrix>(*a_rem);
556 C->renumber(rem_copy->nbNonZero(), rem_copy->col);
557 A_rem = Backend::copy_matrix(rem_copy, bprm);
558 }
559 else {
560 C->renumber(a_rem->nbNonZero(), a_rem->col);
561 A_rem = Backend::copy_matrix(a_rem, bprm);
562 }
563 }
564
565 C->move_to_backend(bprm);
566
567 if (!keep_src) {
568 a_loc.reset();
569 a_rem.reset();
570 }
571 ARCCORE_ALINA_TOC("move to backend");
572 }
573
574 template <class A, class VecX, class B, class VecY>
575 void mul(A alpha, const VecX& x, B beta, VecY& y) const
576 {
577 const auto one = math::identity<scalar_type>();
578
579 C->start_exchange(x);
580
581 // Compute local part of the product.
582 backend::spmv(alpha, *A_loc, x, beta, y);
583
584 // Compute remote part of the product.
585 C->finish_exchange();
586
587 if (C->needs_remote())
588 backend::spmv(alpha, *A_rem, *C->x_rem, one, y);
589 }
590
591 template <class Vec1, class Vec2, class Vec3>
592 void residual(const Vec1& f, const Vec2& x, Vec3& r) const
593 {
594 const auto one = math::identity<scalar_type>();
595
596 C->start_exchange(x);
597 backend::residual(f, *A_loc, x, r);
598
599 C->finish_exchange();
600
601 if (C->needs_remote())
602 backend::spmv(-one, *A_rem, *C->x_rem, one, r);
603 }
604
605 private:
606
607 std::shared_ptr<CommPattern> C;
608 std::shared_ptr<matrix> A_loc, A_rem;
609 std::shared_ptr<build_matrix> a_loc, a_rem;
610
611 ptrdiff_t n_loc_rows, n_glob_rows;
612 ptrdiff_t n_loc_cols, n_glob_cols;
613 ptrdiff_t n_loc_nonzeros, n_glob_nonzeros;
614};
615
616/*---------------------------------------------------------------------------*/
617/*---------------------------------------------------------------------------*/
618
619template <class Backend>
620std::shared_ptr<DistributedMatrix<Backend>>
621transpose(const DistributedMatrix<Backend>& A)
622{
623 ARCCORE_ALINA_TIC("MPI Transpose");
624 typedef typename Backend::value_type value_type;
625 typedef CommunicationPattern<Backend> CommPattern;
626 typedef typename Backend::matrix build_matrix;
627 typedef typename Backend::col_type col_type;
628
629 static const int tag_cnt = 2001;
630 static const int tag_col = 2002;
631 static const int tag_val = 2003;
632
633 AlinaCommunicator comm = A.comm();
634 const CommPattern& C = A.cpat();
635
636 build_matrix& A_loc = *A.local();
637 build_matrix& A_rem = *A.remote();
638
639 ptrdiff_t nrows = A_loc.ncols;
640 ptrdiff_t ncols = A_loc.nbRow();
641
642 UniqueArray<MessagePassing::Request> recv_cnt_req(C.send.req.size());
643 UniqueArray<MessagePassing::Request> recv_col_req(C.send.req.size());
644 UniqueArray<MessagePassing::Request> recv_val_req(C.send.req.size());
645
646 UniqueArray<MessagePassing::Request> send_cnt_req(C.recv.req.size());
647 UniqueArray<MessagePassing::Request> send_col_req(C.recv.req.size());
648 UniqueArray<MessagePassing::Request> send_val_req(C.recv.req.size());
649
650 // Our transposed remote part becomes remote part of someone else,
651 // and the other way around.
652 std::shared_ptr<build_matrix> t_ptr;
653 {
654 std::vector<col_type> tmp_col(A_rem.col.data(), A_rem.col.data() + A_rem.nbNonZero());
655 C.renumber(tmp_col.size(), tmp_col.data());
656
657 col_type* a_rem_col = tmp_col.data();
658 col_type* a_rem_col_backup = A_rem.col.data();
659 A_rem.col.setPointerZeroCopy(a_rem_col);
660
661 //std::swap(a_rem_col, A_rem.col);
662
663 t_ptr = transpose(A_rem);
664
665 A_rem.col.setPointerZeroCopy(a_rem_col_backup);
666 //std::swap(a_rem_col, A_rem.col);
667 }
668 build_matrix& t_rem = *t_ptr;
669
670 // Shift to global numbering:
671 UniqueArray<ptrdiff_t> domain = comm.exclusive_sum(ncols);
672 ptrdiff_t loc_beg = domain[comm.rank];
673 for (size_t i = 0; i < t_rem.nbNonZero(); ++i)
674 t_rem.col[i] += loc_beg;
675
676 // Shift from row pointers to row sizes:
677 std::vector<ptrdiff_t> row_size(t_rem.nbRow());
678 for (size_t i = 0; i < t_rem.nbRow(); ++i)
679 row_size[i] = t_rem.ptr[i + 1] - t_rem.ptr[i];
680
681 // Sizes of transposed remote blocks:
682 // 1. Exchange rem_ptr
683 std::vector<ptrdiff_t> rem_ptr(C.send.count() + 1);
684 rem_ptr[0] = 0;
685
686 for (size_t i = 0; i < C.send.nbr.size(); ++i) {
687 ptrdiff_t beg = C.send.ptr[i];
688 ptrdiff_t end = C.send.ptr[i + 1];
689
690 recv_cnt_req[i] = comm.doIReceive(&rem_ptr[beg + 1], end - beg, C.send.nbr[i], tag_cnt);
691 }
692
693 for (size_t i = 0; i < C.recv.nbr.size(); ++i) {
694 ptrdiff_t beg = C.recv.ptr[i];
695 ptrdiff_t end = C.recv.ptr[i + 1];
696
697 send_cnt_req[i] = comm.doISend(&row_size[beg], end - beg, C.recv.nbr[i], tag_cnt);
698 }
699
700 ARCCORE_ALINA_TIC("MPI Wait");
701 comm.waitAll(recv_cnt_req);
702 ARCCORE_ALINA_TOC("MPI Wait");
703 std::partial_sum(rem_ptr.begin(), rem_ptr.end(), rem_ptr.begin());
704
705 // 2. Start exchange of rem_col, rem_val
706 std::vector<col_type> rem_col(rem_ptr.back());
707 std::vector<value_type> rem_val(rem_ptr.back());
708
709 for (size_t i = 0; i < C.send.nbr.size(); ++i) {
710 ptrdiff_t rbeg = C.send.ptr[i];
711 ptrdiff_t rend = C.send.ptr[i + 1];
712
713 ptrdiff_t cbeg = rem_ptr[rbeg];
714 ptrdiff_t cend = rem_ptr[rend];
715
716 recv_col_req[i] = comm.doIReceive(&rem_col[cbeg], cend - cbeg, C.send.nbr[i], tag_col);
717 recv_val_req[i] = comm.doIReceive(&rem_val[cbeg], cend - cbeg, C.send.nbr[i], tag_val);
718 }
719
720 for (size_t i = 0; i < C.recv.nbr.size(); ++i) {
721 ptrdiff_t rbeg = C.recv.ptr[i];
722 ptrdiff_t rend = C.recv.ptr[i + 1];
723
724 ptrdiff_t cbeg = t_rem.ptr[rbeg];
725 ptrdiff_t cend = t_rem.ptr[rend];
726
727 send_col_req[i] = comm.doISend(&t_rem.col[cbeg], cend - cbeg, C.recv.nbr[i], tag_col);
728 send_val_req[i] = comm.doISend(&t_rem.val[cbeg], cend - cbeg, C.recv.nbr[i], tag_val);
729 }
730
731 // 3. While rem_col and rem_val are in flight,
732 // start constructing our remote part:
733 auto T_ptr = std::make_shared<build_matrix>();
734 build_matrix& T_rem = *T_ptr;
735 T_rem.set_size(nrows, 0, true);
736
737 for (size_t i = 0; i < C.send.count(); ++i)
738 T_rem.ptr[1 + C.send.col[i]] += rem_ptr[i + 1] - rem_ptr[i];
739
740 T_rem.scan_row_sizes();
741 T_rem.set_nonzeros();
742
743 // 4. Finish rem_col and rem_val exchange, and
744 // finish contruction of our remote part.
745 ARCCORE_ALINA_TIC("MPI Wait");
746 comm.waitAll(recv_col_req);
747 comm.waitAll(recv_val_req);
748 ARCCORE_ALINA_TOC("MPI Wait");
749
750 for (size_t i = 0; i < C.send.count(); ++i) {
751 ptrdiff_t row = C.send.col[i];
752 ptrdiff_t head = T_rem.ptr[row];
753
754 for (ptrdiff_t j = rem_ptr[i]; j < rem_ptr[i + 1]; ++j, ++head) {
755 T_rem.col[head] = rem_col[j];
756 T_rem.val[head] = rem_val[j];
757 }
758
759 T_rem.ptr[row] = head;
760 }
761
762 std::rotate(T_rem.ptr.data(), T_rem.ptr.data() + nrows, T_rem.ptr.data() + nrows + 1);
763 T_rem.ptr[0] = 0;
764
765 ARCCORE_ALINA_TIC("MPI Wait");
766 comm.waitAll(send_cnt_req);
767 comm.waitAll(send_col_req);
768 comm.waitAll(send_val_req);
769 ARCCORE_ALINA_TOC("MPI Wait");
770
771 ARCCORE_ALINA_TOC("MPI Transpose");
772
773 return std::make_shared<DistributedMatrix<Backend>>(comm, transpose(A_loc), T_ptr);
774}
775
776/*---------------------------------------------------------------------------*/
777/*---------------------------------------------------------------------------*/
778
779template <class Backend>
780std::shared_ptr<typename Backend::matrix>
781remote_rows(const CommunicationPattern<Backend>& C,
783 bool need_values = true)
784{
785 typedef typename Backend::matrix build_matrix;
786
787 static const int tag_ptr = 3001;
788 static const int tag_col = 3002;
789 static const int tag_val = 3003;
790
791 ARCCORE_ALINA_TIC("remote_rows");
792 AlinaCommunicator comm = C.mpi_comm();
793
794 build_matrix& B_loc = *B.local();
795 build_matrix& B_rem = *B.remote();
796 ptrdiff_t B_beg = B.loc_col_shift();
797
798 size_t nrecv = C.recv.nbr.size();
799 size_t nsend = C.send.nbr.size();
800
801 // Create blocked matrix to send to each domain
802 // that needs data from us:
803 UniqueArray<MessagePassing::Request> send_ptr_req(nsend);
804 UniqueArray<MessagePassing::Request> send_col_req(nsend);
805 UniqueArray<MessagePassing::Request> send_val_req(nsend);
806
807 std::vector<build_matrix> send_rows(nsend);
808
809 for (size_t k = 0; k < nsend; ++k) {
810 ptrdiff_t beg = C.send.ptr[k];
811 ptrdiff_t end = C.send.ptr[k + 1];
812
813 build_matrix& m = send_rows[k];
814 m.set_size(end - beg, 0, false);
815
816 size_t nnz = 0;
817 for (ptrdiff_t i = 0, ii = beg; ii < end; ++i, ++ii) {
818 ptrdiff_t r = C.send.col[ii];
819
820 ptrdiff_t w = (B_loc.ptr[r + 1] - B_loc.ptr[r]) + (B_rem.ptr[r + 1] - B_rem.ptr[r]);
821
822 m.ptr[i] = w;
823 nnz += w;
824 }
825 m.setNbNonZero(nnz);
826
827 send_ptr_req[k] = comm.doISend(m.ptr.data(), m.nbRow(), C.send.nbr[k], tag_ptr);
828
829 m.set_nonzeros(nnz, need_values);
830
831 for (ptrdiff_t i = 0, ii = beg, head = 0; ii < end; ++i, ++ii) {
832 ptrdiff_t r = C.send.col[ii];
833
834 // Contribution of the local part:
835 for (ptrdiff_t j = B_loc.ptr[r]; j < B_loc.ptr[r + 1]; ++j) {
836 m.col[head] = B_loc.col[j] + B_beg;
837
838 if (need_values)
839 m.val[head] = B_loc.val[j];
840
841 ++head;
842 }
843
844 // Contribution of the remote part:
845 for (ptrdiff_t j = B_rem.ptr[r]; j < B_rem.ptr[r + 1]; ++j) {
846 m.col[head] = B_rem.col[j];
847
848 if (need_values)
849 m.val[head] = B_rem.val[j];
850
851 ++head;
852 }
853 }
854
855 send_col_req[k] = comm.doISend(m.col.data(), m.nbNonZero(), C.send.nbr[k], tag_col);
856 if (need_values)
857 send_val_req[k] = comm.doISend(m.val.data(), m.nbNonZero(), C.send.nbr[k], tag_val);
858 }
859
860 // Receive rows of B in block format from our neighbors:
861 UniqueArray<MessagePassing::Request> recv_ptr_req(nrecv);
862 UniqueArray<MessagePassing::Request> recv_col_req(nrecv);
863 UniqueArray<MessagePassing::Request> recv_val_req(nrecv);
864
865 auto B_nbr = std::make_shared<build_matrix>();
866 B_nbr->set_size(C.recv.count(), 0, false);
867 B_nbr->ptr[0] = 0;
868
869 for (size_t k = 0; k < nrecv; ++k) {
870 ptrdiff_t beg = C.recv.ptr[k];
871 ptrdiff_t end = C.recv.ptr[k + 1];
872
873 recv_ptr_req[k] = comm.doIReceive(&B_nbr->ptr[beg + 1], end - beg, C.recv.nbr[k], tag_ptr);
874 }
875
876 ARCCORE_ALINA_TIC("MPI Wait");
877 comm.waitAll(recv_ptr_req);
878 ARCCORE_ALINA_TOC("MPI Wait");
879
880 B_nbr->set_nonzeros(B_nbr->scan_row_sizes(), need_values);
881
882 for (size_t k = 0; k < nrecv; ++k) {
883 ptrdiff_t rbeg = C.recv.ptr[k];
884 ptrdiff_t rend = C.recv.ptr[k + 1];
885
886 ptrdiff_t cbeg = B_nbr->ptr[rbeg];
887 ptrdiff_t cend = B_nbr->ptr[rend];
888
889 recv_col_req[k] = comm.doIReceive(&B_nbr->col[cbeg], cend - cbeg, C.recv.nbr[k], tag_col);
890
891 if (need_values)
892 recv_val_req[k] = comm.doIReceive(&B_nbr->val[cbeg], cend - cbeg, C.recv.nbr[k], tag_val);
893 }
894
895 ARCCORE_ALINA_TIC("MPI Wait");
896 comm.waitAll(send_ptr_req);
897 comm.waitAll(send_col_req);
898 comm.waitAll(recv_col_req);
899
900 if (need_values) {
901 comm.waitAll(send_val_req);
902 comm.waitAll(recv_val_req);
903 }
904 ARCCORE_ALINA_TOC("MPI Wait");
905
906 ARCCORE_ALINA_TOC("remote_rows");
907 return B_nbr;
908}
909
910/*---------------------------------------------------------------------------*/
911/*---------------------------------------------------------------------------*/
912
913template <class Backend>
914std::shared_ptr<DistributedMatrix<Backend>>
916{
917 typedef typename Backend::value_type value_type;
918 using build_matrix = Backend::matrix;
919 typedef typename Backend::col_type col_type;
920 ARCCORE_ALINA_TIC("product");
921
922 const CommunicationPattern<Backend>& Acp = A.cpat();
923
924 build_matrix& A_loc = *A.local();
925 build_matrix& A_rem = *A.remote();
926 build_matrix& B_loc = *B.local();
927 build_matrix& B_rem = *B.remote();
928
929 ptrdiff_t A_rows = A.loc_rows();
930 ptrdiff_t B_cols = B.loc_cols();
931
932 ptrdiff_t B_beg = B.loc_col_shift();
933 ptrdiff_t B_end = B_beg + B_cols;
934
935 auto b_nbr = remote_rows(Acp, B);
936 build_matrix& B_nbr = *b_nbr;
937
938 // Build mapping from global to local column numbers in the remote part of
939 // the product matrix.
940 std::vector<col_type> rem_cols(B_rem.nbNonZero() + B_nbr.nbNonZero());
941
942 std::copy(B_nbr.col.data(), B_nbr.col.data() + B_nbr.nbNonZero(),
943 std::copy(B_rem.col.data(), B_rem.col.data() + B_rem.nbNonZero(), rem_cols.begin()));
944
945 std::sort(rem_cols.begin(), rem_cols.end());
946 rem_cols.erase(std::unique(rem_cols.begin(), rem_cols.end()), rem_cols.end());
947
948 ptrdiff_t n_rem_cols = 0;
949 std::unordered_map<ptrdiff_t, int> rem_idx(2 * rem_cols.size());
950 for (ptrdiff_t c : rem_cols) {
951 if (c >= B_beg && c < B_end)
952 continue;
953 rem_idx[c] = n_rem_cols++;
954 }
955
956 // Build the product.
957 auto c_loc = std::make_shared<build_matrix>();
958 auto c_rem = std::make_shared<build_matrix>();
959
960 build_matrix& C_loc = *c_loc;
961 build_matrix& C_rem = *c_rem;
962
963 C_loc.set_size(A_rows, B_cols, false);
964 C_rem.set_size(A_rows, 0, false);
965
966 C_loc.ptr[0] = 0;
967 C_rem.ptr[0] = 0;
968
969 ARCCORE_ALINA_TIC("analyze");
970 arccoreParallelFor(0, A_rows, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
971 std::vector<ptrdiff_t> loc_marker(B_end - B_beg, -1);
972 std::vector<ptrdiff_t> rem_marker(n_rem_cols, -1);
973
974 for (ptrdiff_t ia = begin; ia < (begin + size); ++ia) {
975 ptrdiff_t loc_cols = 0;
976 ptrdiff_t rem_cols = 0;
977
978 for (ptrdiff_t ja = A_loc.ptr[ia], ea = A_loc.ptr[ia + 1]; ja < ea; ++ja) {
979 ptrdiff_t ca = A_loc.col[ja];
980
981 for (ptrdiff_t jb = B_loc.ptr[ca], eb = B_loc.ptr[ca + 1]; jb < eb; ++jb) {
982 ptrdiff_t cb = B_loc.col[jb];
983
984 if (loc_marker[cb] != ia) {
985 loc_marker[cb] = ia;
986 ++loc_cols;
987 }
988 }
989
990 for (ptrdiff_t jb = B_rem.ptr[ca], eb = B_rem.ptr[ca + 1]; jb < eb; ++jb) {
991 ptrdiff_t cb = rem_idx[B_rem.col[jb]];
992
993 if (rem_marker[cb] != ia) {
994 rem_marker[cb] = ia;
995 ++rem_cols;
996 }
997 }
998 }
999
1000 for (ptrdiff_t ja = A_rem.ptr[ia], ea = A_rem.ptr[ia + 1]; ja < ea; ++ja) {
1001 ptrdiff_t ca = Acp.local_index(A_rem.col[ja]);
1002
1003 for (ptrdiff_t jb = B_nbr.ptr[ca], eb = B_nbr.ptr[ca + 1]; jb < eb; ++jb) {
1004 ptrdiff_t cb = B_nbr.col[jb];
1005
1006 if (cb >= B_beg && cb < B_end) {
1007 cb -= B_beg;
1008
1009 if (loc_marker[cb] != ia) {
1010 loc_marker[cb] = ia;
1011 ++loc_cols;
1012 }
1013 }
1014 else {
1015 cb = rem_idx[cb];
1016
1017 if (rem_marker[cb] != ia) {
1018 rem_marker[cb] = ia;
1019 ++rem_cols;
1020 }
1021 }
1022 }
1023 }
1024
1025 C_loc.ptr[ia + 1] = loc_cols;
1026 C_rem.ptr[ia + 1] = rem_cols;
1027 }
1028 });
1029 ARCCORE_ALINA_TOC("analyze");
1030
1031 C_loc.set_nonzeros(C_loc.scan_row_sizes());
1032 C_rem.set_nonzeros(C_rem.scan_row_sizes());
1033
1034 ARCCORE_ALINA_TIC("compute");
1035 arccoreParallelFor(0, A_rows, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1036 std::vector<ptrdiff_t> loc_marker(B_end - B_beg, -1);
1037 std::vector<ptrdiff_t> rem_marker(n_rem_cols, -1);
1038
1039 for (ptrdiff_t ia = begin; ia < (begin + size); ++ia) {
1040 ptrdiff_t loc_beg = C_loc.ptr[ia];
1041 ptrdiff_t rem_beg = C_rem.ptr[ia];
1042 ptrdiff_t loc_end = loc_beg;
1043 ptrdiff_t rem_end = rem_beg;
1044
1045 for (ptrdiff_t ja = A_loc.ptr[ia], ea = A_loc.ptr[ia + 1]; ja < ea; ++ja) {
1046 ptrdiff_t ca = A_loc.col[ja];
1047 value_type va = A_loc.val[ja];
1048
1049 for (ptrdiff_t jb = B_loc.ptr[ca], eb = B_loc.ptr[ca + 1]; jb < eb; ++jb) {
1050 ptrdiff_t cb = B_loc.col[jb];
1051 value_type vb = B_loc.val[jb];
1052
1053 if (loc_marker[cb] < loc_beg) {
1054 loc_marker[cb] = loc_end;
1055
1056 C_loc.col[loc_end] = cb;
1057 C_loc.val[loc_end] = va * vb;
1058
1059 ++loc_end;
1060 }
1061 else {
1062 C_loc.val[loc_marker[cb]] += va * vb;
1063 }
1064 }
1065
1066 for (ptrdiff_t jb = B_rem.ptr[ca], eb = B_rem.ptr[ca + 1]; jb < eb; ++jb) {
1067 ptrdiff_t gb = B_rem.col[jb];
1068 ptrdiff_t cb = rem_idx[gb];
1069 value_type vb = B_rem.val[jb];
1070
1071 if (rem_marker[cb] < rem_beg) {
1072 rem_marker[cb] = rem_end;
1073
1074 C_rem.col[rem_end] = gb;
1075 C_rem.val[rem_end] = va * vb;
1076
1077 ++rem_end;
1078 }
1079 else {
1080 C_rem.val[rem_marker[cb]] += va * vb;
1081 }
1082 }
1083 }
1084
1085 for (ptrdiff_t ja = A_rem.ptr[ia], ea = A_rem.ptr[ia + 1]; ja < ea; ++ja) {
1086 ptrdiff_t ca = Acp.local_index(A_rem.col[ja]);
1087 value_type va = A_rem.val[ja];
1088
1089 for (ptrdiff_t jb = B_nbr.ptr[ca], eb = B_nbr.ptr[ca + 1]; jb < eb; ++jb) {
1090 ptrdiff_t gb = B_nbr.col[jb];
1091 value_type vb = B_nbr.val[jb];
1092
1093 if (gb >= B_beg && gb < B_end) {
1094 ptrdiff_t cb = gb - B_beg;
1095
1096 if (loc_marker[cb] < loc_beg) {
1097 loc_marker[cb] = loc_end;
1098
1099 C_loc.col[loc_end] = cb;
1100 C_loc.val[loc_end] = va * vb;
1101
1102 ++loc_end;
1103 }
1104 else {
1105 C_loc.val[loc_marker[cb]] += va * vb;
1106 }
1107 }
1108 else {
1109 ptrdiff_t cb = rem_idx[gb];
1110
1111 if (rem_marker[cb] < rem_beg) {
1112 rem_marker[cb] = rem_end;
1113
1114 C_rem.col[rem_end] = gb;
1115 C_rem.val[rem_end] = va * vb;
1116
1117 ++rem_end;
1118 }
1119 else {
1120 C_rem.val[rem_marker[cb]] += va * vb;
1121 }
1122 }
1123 }
1124 }
1125 }
1126 });
1127 ARCCORE_ALINA_TOC("compute");
1128 ARCCORE_ALINA_TOC("product");
1129
1130 return std::make_shared<DistributedMatrix<Backend>>(A.comm(), c_loc, c_rem);
1131}
1132
1133/*---------------------------------------------------------------------------*/
1134/*---------------------------------------------------------------------------*/
1135
1136template <class Backend, class T>
1137void scale(DistributedMatrix<Backend>& A, T s)
1138{
1139 using build_matrix = Backend::matrix;
1140
1141 build_matrix& A_loc = *A.local();
1142 build_matrix& A_rem = *A.remote();
1143
1144 ptrdiff_t n = A_loc.nbRow();
1145
1146 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1147 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1148 for (ptrdiff_t j = A_loc.ptr[i], e = A_loc.ptr[i + 1]; j < e; ++j)
1149 A_loc.val[j] *= s;
1150 for (ptrdiff_t j = A_rem.ptr[i], e = A_rem.ptr[i + 1]; j < e; ++j)
1151 A_rem.val[j] *= s;
1152 }
1153 });
1154}
1155
1156/*---------------------------------------------------------------------------*/
1157/*---------------------------------------------------------------------------*/
1158
1159template <class Backend>
1160void sort_rows(DistributedMatrix<Backend>& A)
1161{
1162 sort_rows(*A.local());
1163 sort_rows(*A.remote());
1164}
1165
1166/*---------------------------------------------------------------------------*/
1167/*---------------------------------------------------------------------------*/
1168
1169} // namespace Arcane::Alina
1170
1171/*---------------------------------------------------------------------------*/
1172/*---------------------------------------------------------------------------*/
1173
1174namespace Arcane::Alina::backend
1175{
1176
1177/*---------------------------------------------------------------------------*/
1178/*---------------------------------------------------------------------------*/
1179
1180template <class Backend>
1182{
1183 static size_t get(const DistributedMatrix<Backend>& A)
1184 {
1185 return A.loc_rows();
1186 }
1187};
1188
1189template <class Backend, class Alpha, class Vec1, class Beta, class Vec2>
1190struct spmv_impl<Alpha, DistributedMatrix<Backend>, Vec1, Beta, Vec2>
1191{
1192 static void apply(Alpha alpha,
1194 const Vec1& x, Beta beta, Vec2& y)
1195 {
1196 A.mul(alpha, x, beta, y);
1197 }
1198};
1199
1200template <class Backend, class Vec1, class Vec2, class Vec3>
1201struct residual_impl<DistributedMatrix<Backend>, Vec1, Vec2, Vec3>
1202{
1203 static void apply(const Vec1& rhs,
1205 const Vec2& x, Vec3& r)
1206 {
1207 A.residual(rhs, x, r);
1208 }
1209};
1210
1211/*---------------------------------------------------------------------------*/
1212/*---------------------------------------------------------------------------*/
1213}
1214
1215/*---------------------------------------------------------------------------*/
1216/*---------------------------------------------------------------------------*/
1217
1218namespace Arcane::Alina
1219{
1220
1221/*---------------------------------------------------------------------------*/
1222/*---------------------------------------------------------------------------*/
1223
1224// Diagonal of the matrix
1225template <class Backend>
1226std::shared_ptr<numa_vector<typename Backend::value_type>>
1227diagonal(const DistributedMatrix<Backend>& A, bool invert = false)
1228{
1229 return diagonal(*A.local(), invert);
1230}
1231
1232/*---------------------------------------------------------------------------*/
1233/*---------------------------------------------------------------------------*/
1234
1235// Estimate spectral radius of the matrix.
1236template <bool scale, class Backend>
1237typename math::scalar_of<typename Backend::value_type>::type
1238spectral_radius(const DistributedMatrix<Backend>& A, int power_iters = 0)
1239{
1240 ARCCORE_ALINA_TIC("spectral radius");
1241 typedef typename Backend::value_type value_type;
1242 typedef typename math::rhs_of<value_type>::type rhs_type;
1243 typedef typename math::scalar_of<value_type>::type scalar_type;
1244 typedef CSRMatrix<value_type> build_matrix;
1245
1246 AlinaCommunicator comm = A.comm();
1247
1248 const build_matrix& A_loc = *A.local();
1249 const build_matrix& A_rem = *A.remote();
1250 const CommunicationPattern<Backend>& C = A.cpat();
1251
1252 const ptrdiff_t n = A_loc.nbRow();
1253 scalar_type radius = 0;
1254
1255 // TODO: Use the code in CSRMatrixOperation.h
1256 if (power_iters <= 0) {
1257 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1258 scalar_type emax = 0;
1259 value_type dia = math::identity<value_type>();
1260
1261 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1262 scalar_type s = 0;
1263
1264 for (ptrdiff_t j = A_loc.ptr[i], e = A_loc.ptr[i + 1]; j < e; ++j) {
1265 ptrdiff_t c = A_loc.col[j];
1266 value_type v = A_loc.val[j];
1267
1268 s += math::norm(v);
1269
1270 if (scale && c == i)
1271 dia = v;
1272 }
1273
1274 for (ptrdiff_t j = A_rem.ptr[i], e = A_rem.ptr[i + 1]; j < e; ++j)
1275 s += math::norm(A_rem.val[j]);
1276
1277 if (scale)
1278 s *= math::norm(math::inverse(dia));
1279
1280 emax = std::max(emax, s);
1281 }
1282
1284 });
1285 }
1286 else {
1287 numa_vector<rhs_type> b0(n, false), b1(n, false);
1288 numa_vector<ptrdiff_t> rem_col(A_rem.nbNonZero(), false);
1289
1290 // Fill the initial vector with random values.
1291 // Also extract the inverted matrix diagonal values.
1292 std::atomic<scalar_type> atomic_b0_loc_norm = {};
1293
1294 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1295 const int tid = TaskFactory::currentTaskThreadIndex();
1296 const int nt = ConcurrencyBase::maxAllowedThread();
1297
1298 std::mt19937 rng(comm.size * nt + tid);
1299 std::uniform_real_distribution<scalar_type> rnd(-1, 1);
1300
1301 scalar_type t_norm = 0;
1302
1303 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1304 rhs_type v = math::constant<rhs_type>(rnd(rng));
1305
1306 b0[i] = v;
1307 t_norm += math::norm(math::inner_product(v, v));
1308
1309 for (ptrdiff_t j = A_rem.ptr[i], e = A_rem.ptr[i + 1]; j < e; ++j) {
1310 rem_col[j] = C.local_index(A_rem.col[j]);
1311 }
1312 }
1313
1314 // GG: Not reproducible
1315 atomic_b0_loc_norm += t_norm;
1316 });
1317 scalar_type b0_loc_norm = atomic_b0_loc_norm;
1318 scalar_type b0_norm = comm.reduceSum(b0_loc_norm);
1319
1320 // Normalize b0
1321 b0_norm = 1 / sqrt(b0_norm);
1322 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1323 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1324 b0[i] = b0_norm * b0[i];
1325 }
1326 });
1327
1328 std::vector<rhs_type> b0_send(C.send.count());
1329 std::vector<rhs_type> b0_recv(C.recv.count());
1330
1331 for (size_t i = 0, m = C.send.count(); i < m; ++i)
1332 b0_send[i] = b0[C.send.col[i]];
1333 C.exchange(b0_send.data(), b0_recv.data());
1334
1335 for (int iter = 0; iter < power_iters;) {
1336 // b1 = (D * A) * b0
1337 // b1_norm = ||b1||
1338 // radius = <b1,b0>
1339
1340 std::atomic<scalar_type> atomic_b1_loc_norm = 0;
1341 std::atomic<scalar_type> atomic_loc_radius = 0;
1342
1343 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1344 scalar_type t_norm = 0;
1345 scalar_type t_radi = 0;
1346 value_type dia = math::identity<value_type>();
1347
1348 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1349 rhs_type s = math::zero<rhs_type>();
1350
1351 for (ptrdiff_t j = A_loc.ptr[i], e = A_loc.ptr[i + 1]; j < e; ++j) {
1352 ptrdiff_t c = A_loc.col[j];
1353 value_type v = A_loc.val[j];
1354 if (scale && c == i)
1355 dia = v;
1356 s += v * b0[c];
1357 }
1358
1359 for (ptrdiff_t j = A_rem.ptr[i], e = A_rem.ptr[i + 1]; j < e; ++j)
1360 s += A_rem.val[j] * b0_recv[rem_col[j]];
1361
1362 if (scale)
1363 s = math::inverse(dia) * s;
1364
1365 t_norm += math::norm(math::inner_product(s, s));
1366 t_radi += math::norm(math::inner_product(s, b0[i]));
1367
1368 b1[i] = s;
1369 }
1370
1371 {
1372 // GG: Not reproducible
1373 atomic_b1_loc_norm += t_norm;
1374 atomic_loc_radius += t_radi;
1375 }
1376 });
1377 scalar_type b1_loc_norm = atomic_b1_loc_norm;
1378 scalar_type loc_radius = atomic_loc_radius;
1379
1380 radius = comm.reduceSum(loc_radius);
1381
1382 if (++iter < power_iters) {
1383 scalar_type b1_norm;
1384 b1_norm = comm.reduceSum(b1_loc_norm);
1385
1386 // b0 = b1 / b1_norm
1387 b1_norm = 1 / sqrt(b1_norm);
1388 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1389 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1390 b0[i] = b1_norm * b1[i];
1391 }
1392 });
1393
1394 for (size_t i = 0, m = C.send.count(); i < m; ++i)
1395 b0_send[i] = b0[C.send.col[i]];
1396 C.exchange(b0_send.data(), b0_recv.data());
1397 }
1398 }
1399 }
1400 ARCCORE_ALINA_TOC("spectral radius");
1401
1402 return radius < 0 ? static_cast<scalar_type>(2) : radius;
1403}
1404
1405/*---------------------------------------------------------------------------*/
1406/*---------------------------------------------------------------------------*/
1407
1408} // namespace Arcane::Alina
1409
1410/*---------------------------------------------------------------------------*/
1411/*---------------------------------------------------------------------------*/
1412
1413#endif
Call to handle communication pattern.
Distributed Matrix using message passing.
NUMA-aware vector container.
Definition NumaVector.h:42
T & back()
Last element of the array.
static Int32 maxAllowedThread()
Maximum number of allowed threads for multi-threading.
Loop execution information.
Matrix class, to be used by user.
static Int32 currentTaskThreadIndex()
Index (between 0 and nbAllowedThread()-1) of the thread executing the current task.
1D data vector with value semantics (STL style).
Vector class, to be used by user.
__host__ __device__ DataType doAtomic(DataType *ptr, ValueType value)
Applies the atomic operation Operation to the value at address ptr with the value value.
C void mpAllToAll(IMessagePassingMng *pm, Span< const char > send_buf, Span< char > recv_buf, Int32 count)
apfloat sqrt(apfloat v)
Square root of v.
Definition MathApfloat.h:69
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.
Convenience wrapper around MPI_Comm.
UniqueArray< T > exclusive_sum(T n) const
Exclusive sum over mpi communicator.
Implementation for residual error compuatation.
Implementation for function returning the number of rows in a matrix.
Implementation for matrix-vector product.