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