12#ifndef ARCCORE_ALINA_MPI_DISTRIBUTED_MATRIX_H
13#define ARCCORE_ALINA_MPI_DISTRIBUTED_MATRIX_H
26#include "arccore/alina/AlinaUtils.h"
27#include "arccore/alina/MessagePassingUtils.h"
29#include "arccore/accelerator/Atomic.h"
35#include <unordered_map>
43namespace Arcane::Alina
51template <
class Backend>
52class CommunicationPattern
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;
67 std::vector<ptrdiff_t> nbr;
68 std::vector<ptr_type> ptr;
69 std::vector<col_type> col;
76 mutable std::vector<rhs_type> val;
82 std::vector<ptrdiff_t> nbr;
83 std::vector<ptr_type> ptr;
90 mutable std::vector<rhs_type> val;
94 std::shared_ptr<vector> x_rem;
98 size_t n_rem_cols,
const col_type* p_rem_cols)
100 , loc_cols(n_loc_cols)
102 ARCCORE_ALINA_TIC(
"communication pattern");
105 loc_beg = domain[comm.rank];
109 std::vector<col_type> rem_cols(p_rem_cols, p_rem_cols + n_rem_cols);
111 std::sort(rem_cols.begin(), rem_cols.end());
112 rem_cols.erase(std::unique(rem_cols.begin(), rem_cols.end()), rem_cols.end());
114 ptrdiff_t ncols = rem_cols.size();
115 ptrdiff_t rnbr = 0, snbr = 0, send_size = 0;
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])
135 idx.insert(idx.end(), std::make_pair(rem_cols[i], std::make_tuple(rnbr - 1, i)));
138 recv.val.resize(ncols);
139 recv.req.resize(rnbr);
141 recv.nbr.reserve(rnbr);
142 recv.ptr.reserve(rnbr + 1);
143 recv.ptr.push_back(0);
145 for (
int d = 0; d < comm.size; ++d) {
147 recv.nbr.push_back(d);
148 recv.ptr.push_back(recv.ptr.back() + rcounts[d]);
153 for (ptrdiff_t d = 0; d < comm.size; ++d) {
156 send_size += scounts[d];
160 send.col.resize(send_size);
161 send.val.resize(send_size);
162 send.req.resize(snbr);
164 send.nbr.reserve(snbr);
165 send.ptr.reserve(snbr + 1);
166 send.ptr.push_back(0);
168 for (ptrdiff_t d = 0; d < comm.size; ++d) {
170 send.nbr.push_back(d);
171 send.ptr.push_back(send.ptr.back() + scounts[d]);
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);
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);
186 ARCCORE_ALINA_TIC(
"MPI Wait");
187 comm.waitAll(recv.req);
188 comm.waitAll(send.req);
189 ARCCORE_ALINA_TOC(
"MPI Wait");
192 for (col_type& c : send.col)
195 ARCCORE_ALINA_TOC(
"communication pattern");
198 template <
class OtherBackend>
199 CommunicationPattern(
const CommunicationPattern<OtherBackend>& C)
203 , loc_cols(C.loc_cols)
205 send.nbr = C.send.nbr;
206 send.ptr = C.send.ptr;
207 send.col = C.send.col;
208 send.val.resize(C.send.val.size());
209 send.req.resize(C.send.req.size());
211 recv.nbr = C.recv.nbr;
212 recv.ptr = C.recv.ptr;
213 recv.val.resize(C.recv.val.size());
214 recv.req.resize(C.recv.req.size());
217 void move_to_backend(
const backend_params& bprm = backend_params())
220 x_rem = Backend::create_vector(recv.count(), bprm);
224 gather = std::make_shared<Gather>(loc_cols, send.col, bprm);
228 int domain(ptrdiff_t col)
const
230 return std::get<0>(idx.at(col));
233 int local_index(ptrdiff_t col)
const
235 return std::get<1>(idx.at(col));
238 std::tuple<int, int> remote_info(ptrdiff_t col)
const
243 std::unordered_map<ptrdiff_t, std::tuple<int, int>>::const_iterator
249 std::unordered_map<ptrdiff_t, std::tuple<int, int>>::const_iterator
255 size_t renumber(
size_t n, col_type* col)
const
257 for (
size_t i = 0; i < n; ++i)
258 col[i] = std::get<1>(idx.at(col[i]));
262 bool needs_remote()
const
264 return !recv.val.empty();
267 template <
class Vector>
268 void start_exchange(
const Vector& x)
const
271 for (
size_t i = 0; i < recv.nbr.size(); ++i)
272 recv.req[i] = comm.doIReceive(&recv.val[recv.ptr[i]], recv.ptr[i + 1] - recv.ptr[i],
273 recv.nbr[i], tag_exc_vals);
276 if (!send.val.empty()) {
277 (*gather)(x, send.val);
279 for (
size_t i = 0; i < send.nbr.size(); ++i)
280 send.req[i] = comm.doISend(&send.val[send.ptr[i]], send.ptr[i + 1] - send.ptr[i],
281 send.nbr[i], tag_exc_vals);
285 void finish_exchange()
const
287 ARCCORE_ALINA_TIC(
"MPI Wait");
288 comm.waitAll(recv.req);
289 comm.waitAll(send.req);
290 ARCCORE_ALINA_TOC(
"MPI Wait");
292 if (!recv.val.empty())
293 backend::copy(recv.val, *x_rem);
296 template <
typename T>
297 void exchange(
const T* send_val, T* recv_val)
const
299 for (
size_t i = 0; i < recv.nbr.size(); ++i)
300 recv.req[i] = comm.doIReceive(&recv_val[recv.ptr[i]], recv.ptr[i + 1] - recv.ptr[i],
301 recv.nbr[i], tag_exc_vals);
303 for (
size_t i = 0; i < send.nbr.size(); ++i)
304 send.req[i] = comm.doISend(
const_cast<T*
>(&send_val[send.ptr[i]]), send.ptr[i + 1] - send.ptr[i],
305 send.nbr[i], tag_exc_vals);
307 ARCCORE_ALINA_TIC(
"MPI Wait");
308 comm.waitAll(recv.req);
309 comm.waitAll(send.req);
310 ARCCORE_ALINA_TOC(
"MPI Wait");
318 ptrdiff_t loc_col_shift()
const
325 using Gather = Backend::gather;
327 static const int tag_set_comm = 1001;
328 static const int tag_exc_cols = 1002;
329 static const int tag_exc_vals = 1003;
333 std::unordered_map<ptrdiff_t, std::tuple<int, int>> idx;
334 std::shared_ptr<Gather> gather;
339 friend class CommunicationPattern;
347template <
class Backend>
348class DistributedMatrix
352 typedef typename Backend::value_type value_type;
353 typedef typename math::rhs_of<value_type>::type rhs_type;
354 typedef typename math::scalar_of<value_type>::type scalar_type;
355 typedef typename Backend::params backend_params;
356 typedef typename Backend::matrix matrix;
358 typedef typename Backend::matrix build_matrix;
361 std::shared_ptr<build_matrix> a_loc,
362 std::shared_ptr<build_matrix> a_rem,
363 std::shared_ptr<CommPattern> c = std::shared_ptr<CommPattern>())
371 C = std::make_shared<CommPattern>(comm, a_loc->ncols, a_rem->nbNonZero(), a_rem->col);
374 a_rem->ncols = C->recv.count();
376 n_loc_rows = a_loc->nbRow();
377 n_loc_cols = a_loc->ncols;
378 n_loc_nonzeros = a_loc->nbNonZero() + a_rem->nbNonZero();
380 n_glob_rows = comm.reduceSum(n_loc_rows);
381 n_glob_cols = comm.reduceSum(n_loc_cols);
382 n_glob_nonzeros = comm.reduceSum(n_loc_nonzeros);
386 template <
class OtherBackend>
387 DistributedMatrix(
const DistributedMatrix<OtherBackend>& A)
388 : a_loc(std::make_shared<build_matrix>(*A.local()))
389 , a_rem(std::make_shared<build_matrix>(*A.remote()))
391 C = std::make_shared<CommPattern>(A.cpat());
393 this->a_rem->ncols = C->recv.count();
395 n_loc_rows = A.loc_rows();
396 n_loc_cols = A.loc_cols();
397 n_loc_nonzeros = A.loc_nonzeros();
398 n_glob_rows = A.glob_rows();
399 n_glob_cols = A.glob_cols();
400 n_glob_nonzeros = A.glob_nonzeros();
403 template <
class Matrix>
406 ptrdiff_t _n_loc_cols = -1)
407 : n_loc_rows(backend::nbRow(A))
408 , n_loc_cols(_n_loc_cols < 0 ? n_loc_rows : _n_loc_cols)
409 , n_loc_nonzeros(backend::nonzeros(A))
413 ptrdiff_t loc_beg = domain[comm.rank];
414 ptrdiff_t loc_end = domain[comm.rank + 1];
416 n_glob_cols = domain.
back();
417 n_glob_rows = comm.reduceSum(n_loc_rows);
418 n_glob_nonzeros = comm.reduceSum(n_loc_nonzeros);
421 a_loc = std::make_shared<build_matrix>();
422 a_rem = std::make_shared<build_matrix>();
424 build_matrix& A_loc = *a_loc;
425 build_matrix& A_rem = *a_rem;
427 A_loc.set_size(n_loc_rows, n_loc_cols,
true);
428 A_rem.set_size(n_loc_rows, 0,
true);
431 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
432 for (
auto a = backend::row_begin(A, i); a; ++a) {
433 ptrdiff_t c = a.col();
435 if (loc_beg <= c && c < loc_end)
443 A_loc.set_nonzeros(A_loc.scan_row_sizes());
444 A_rem.set_nonzeros(A_rem.scan_row_sizes());
447 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
448 ptrdiff_t loc_head = A_loc.ptr[i];
449 ptrdiff_t rem_head = A_rem.ptr[i];
451 for (
auto a = backend::row_begin(A, i); a; ++a) {
452 ptrdiff_t c = a.col();
453 value_type v = a.value();
455 if (loc_beg <= c && c < loc_end) {
456 A_loc.col[loc_head] = c - loc_beg;
457 A_loc.val[loc_head] = v;
461 A_rem.col[rem_head] = c;
462 A_rem.val[rem_head] = v;
469 C = std::make_shared<CommPattern>(comm, n_loc_cols, a_rem->nbNonZero(), a_rem->col);
470 a_rem->ncols = C->recv.count();
475 return C->mpi_comm();
478 std::shared_ptr<build_matrix> local()
const
483 std::shared_ptr<build_matrix> remote()
const
488 std::shared_ptr<matrix> local_backend()
const
493 std::shared_ptr<matrix> remote_backend()
const
498 ptrdiff_t loc_rows()
const
503 ptrdiff_t loc_cols()
const
508 ptrdiff_t loc_col_shift()
const
510 return C->loc_col_shift();
513 ptrdiff_t loc_nonzeros()
const
515 return n_loc_nonzeros;
518 ptrdiff_t glob_rows()
const
523 ptrdiff_t glob_cols()
const
528 ptrdiff_t glob_nonzeros()
const
530 return n_glob_nonzeros;
538 void set_local(std::shared_ptr<matrix> a)
543 void move_to_backend(
const backend_params& bprm = backend_params(),
bool keep_src =
false)
545 ARCCORE_ALINA_TIC(
"move to backend");
547 A_loc = Backend::copy_matrix(a_loc, bprm);
550 if (!A_rem && a_rem && a_rem->nbNonZero() > 0) {
552 auto rem_copy = std::make_shared<build_matrix>(*a_rem);
553 C->renumber(rem_copy->nbNonZero(), rem_copy->col);
554 A_rem = Backend::copy_matrix(rem_copy, bprm);
557 C->renumber(a_rem->nbNonZero(), a_rem->col);
558 A_rem = Backend::copy_matrix(a_rem, bprm);
562 C->move_to_backend(bprm);
568 ARCCORE_ALINA_TOC(
"move to backend");
571 template <
class A,
class VecX,
class B,
class VecY>
572 void mul(A alpha,
const VecX& x, B beta, VecY& y)
const
574 const auto one = math::identity<scalar_type>();
576 C->start_exchange(x);
579 backend::spmv(alpha, *A_loc, x, beta, y);
582 C->finish_exchange();
584 if (C->needs_remote())
585 backend::spmv(alpha, *A_rem, *C->x_rem, one, y);
588 template <
class Vec1,
class Vec2,
class Vec3>
589 void residual(
const Vec1& f,
const Vec2& x, Vec3& r)
const
591 const auto one = math::identity<scalar_type>();
593 C->start_exchange(x);
594 backend::residual(f, *A_loc, x, r);
596 C->finish_exchange();
598 if (C->needs_remote())
599 backend::spmv(-one, *A_rem, *C->x_rem, one, r);
604 std::shared_ptr<CommPattern> C;
605 std::shared_ptr<matrix> A_loc, A_rem;
606 std::shared_ptr<build_matrix> a_loc, a_rem;
608 ptrdiff_t n_loc_rows, n_glob_rows;
609 ptrdiff_t n_loc_cols, n_glob_cols;
610 ptrdiff_t n_loc_nonzeros, n_glob_nonzeros;
616template <
class Backend>
617std::shared_ptr<DistributedMatrix<Backend>>
620 ARCCORE_ALINA_TIC(
"MPI Transpose");
621 typedef typename Backend::value_type value_type;
623 typedef typename Backend::matrix build_matrix;
624 typedef typename Backend::col_type
col_type;
626 static const int tag_cnt = 2001;
627 static const int tag_col = 2002;
628 static const int tag_val = 2003;
631 const CommPattern& C = A.cpat();
633 build_matrix& A_loc = *A.local();
634 build_matrix& A_rem = *A.remote();
636 ptrdiff_t nrows = A_loc.ncols;
637 ptrdiff_t ncols = A_loc.nbRow();
649 std::shared_ptr<build_matrix> t_ptr;
651 std::vector<col_type> tmp_col(A_rem.col.data(), A_rem.col.data() + A_rem.nbNonZero());
652 C.renumber(tmp_col.size(), tmp_col.data());
654 col_type* a_rem_col = tmp_col.data();
655 col_type* a_rem_col_backup = A_rem.col.data();
656 A_rem.col.setPointerZeroCopy(a_rem_col);
660 t_ptr = transpose(A_rem);
662 A_rem.col.setPointerZeroCopy(a_rem_col_backup);
665 build_matrix& t_rem = *t_ptr;
669 ptrdiff_t loc_beg = domain[comm.rank];
670 for (
size_t i = 0; i < t_rem.nbNonZero(); ++i)
671 t_rem.col[i] += loc_beg;
674 std::vector<ptrdiff_t> row_size(t_rem.nbRow());
675 for (
size_t i = 0; i < t_rem.nbRow(); ++i)
676 row_size[i] = t_rem.ptr[i + 1] - t_rem.ptr[i];
680 std::vector<ptrdiff_t> rem_ptr(C.send.count() + 1);
683 for (
size_t i = 0; i < C.send.nbr.size(); ++i) {
684 ptrdiff_t beg = C.send.ptr[i];
685 ptrdiff_t end = C.send.ptr[i + 1];
687 recv_cnt_req[i] = comm.doIReceive(&rem_ptr[beg + 1], end - beg, C.send.nbr[i], tag_cnt);
690 for (
size_t i = 0; i < C.recv.nbr.size(); ++i) {
691 ptrdiff_t beg = C.recv.ptr[i];
692 ptrdiff_t end = C.recv.ptr[i + 1];
694 send_cnt_req[i] = comm.doISend(&row_size[beg], end - beg, C.recv.nbr[i], tag_cnt);
697 ARCCORE_ALINA_TIC(
"MPI Wait");
698 comm.waitAll(recv_cnt_req);
699 ARCCORE_ALINA_TOC(
"MPI Wait");
700 std::partial_sum(rem_ptr.begin(), rem_ptr.end(), rem_ptr.begin());
703 std::vector<col_type> rem_col(rem_ptr.back());
704 std::vector<value_type> rem_val(rem_ptr.back());
706 for (
size_t i = 0; i < C.send.nbr.size(); ++i) {
707 ptrdiff_t rbeg = C.send.ptr[i];
708 ptrdiff_t rend = C.send.ptr[i + 1];
710 ptrdiff_t cbeg = rem_ptr[rbeg];
711 ptrdiff_t cend = rem_ptr[rend];
713 recv_col_req[i] = comm.doIReceive(&rem_col[cbeg], cend - cbeg, C.send.nbr[i], tag_col);
714 recv_val_req[i] = comm.doIReceive(&rem_val[cbeg], cend - cbeg, C.send.nbr[i], tag_val);
717 for (
size_t i = 0; i < C.recv.nbr.size(); ++i) {
718 ptrdiff_t rbeg = C.recv.ptr[i];
719 ptrdiff_t rend = C.recv.ptr[i + 1];
721 ptrdiff_t cbeg = t_rem.ptr[rbeg];
722 ptrdiff_t cend = t_rem.ptr[rend];
724 send_col_req[i] = comm.doISend(&t_rem.col[cbeg], cend - cbeg, C.recv.nbr[i], tag_col);
725 send_val_req[i] = comm.doISend(&t_rem.val[cbeg], cend - cbeg, C.recv.nbr[i], tag_val);
730 auto T_ptr = std::make_shared<build_matrix>();
731 build_matrix& T_rem = *T_ptr;
732 T_rem.set_size(nrows, 0,
true);
734 for (
size_t i = 0; i < C.send.count(); ++i)
735 T_rem.ptr[1 + C.send.col[i]] += rem_ptr[i + 1] - rem_ptr[i];
737 T_rem.scan_row_sizes();
738 T_rem.set_nonzeros();
742 ARCCORE_ALINA_TIC(
"MPI Wait");
743 comm.waitAll(recv_col_req);
744 comm.waitAll(recv_val_req);
745 ARCCORE_ALINA_TOC(
"MPI Wait");
747 for (
size_t i = 0; i < C.send.count(); ++i) {
748 ptrdiff_t row = C.send.col[i];
749 ptrdiff_t head = T_rem.ptr[row];
751 for (ptrdiff_t j = rem_ptr[i]; j < rem_ptr[i + 1]; ++j, ++head) {
752 T_rem.col[head] = rem_col[j];
753 T_rem.val[head] = rem_val[j];
756 T_rem.ptr[row] = head;
759 std::rotate(T_rem.ptr.data(), T_rem.ptr.data() + nrows, T_rem.ptr.data() + nrows + 1);
762 ARCCORE_ALINA_TIC(
"MPI Wait");
763 comm.waitAll(send_cnt_req);
764 comm.waitAll(send_col_req);
765 comm.waitAll(send_val_req);
766 ARCCORE_ALINA_TOC(
"MPI Wait");
768 ARCCORE_ALINA_TOC(
"MPI Transpose");
770 return std::make_shared<DistributedMatrix<Backend>>(comm, transpose(A_loc), T_ptr);
776template <
class Backend>
777std::shared_ptr<typename Backend::matrix>
780 bool need_values =
true)
782 typedef typename Backend::matrix build_matrix;
784 static const int tag_ptr = 3001;
785 static const int tag_col = 3002;
786 static const int tag_val = 3003;
788 ARCCORE_ALINA_TIC(
"remote_rows");
791 build_matrix& B_loc = *B.local();
792 build_matrix& B_rem = *B.remote();
793 ptrdiff_t B_beg = B.loc_col_shift();
795 size_t nrecv = C.recv.nbr.size();
796 size_t nsend = C.send.nbr.size();
800 UniqueArray<MessagePassing::Request> send_ptr_req(nsend);
801 UniqueArray<MessagePassing::Request> send_col_req(nsend);
802 UniqueArray<MessagePassing::Request> send_val_req(nsend);
804 std::vector<build_matrix> send_rows(nsend);
806 for (
size_t k = 0; k < nsend; ++k) {
807 ptrdiff_t beg = C.send.ptr[k];
808 ptrdiff_t end = C.send.ptr[k + 1];
810 build_matrix& m = send_rows[k];
811 m.set_size(end - beg, 0,
false);
814 for (ptrdiff_t i = 0, ii = beg; ii < end; ++i, ++ii) {
815 ptrdiff_t r = C.send.col[ii];
817 ptrdiff_t w = (B_loc.ptr[r + 1] - B_loc.ptr[r]) + (B_rem.ptr[r + 1] - B_rem.ptr[r]);
824 send_ptr_req[k] = comm.doISend(m.ptr.data(), m.nbRow(), C.send.nbr[k], tag_ptr);
826 m.set_nonzeros(nnz, need_values);
828 for (ptrdiff_t i = 0, ii = beg, head = 0; ii < end; ++i, ++ii) {
829 ptrdiff_t r = C.send.col[ii];
832 for (ptrdiff_t j = B_loc.ptr[r]; j < B_loc.ptr[r + 1]; ++j) {
833 m.col[head] = B_loc.col[j] + B_beg;
836 m.val[head] = B_loc.val[j];
842 for (ptrdiff_t j = B_rem.ptr[r]; j < B_rem.ptr[r + 1]; ++j) {
843 m.col[head] = B_rem.col[j];
846 m.val[head] = B_rem.val[j];
852 send_col_req[k] = comm.doISend(m.col.data(), m.nbNonZero(), C.send.nbr[k], tag_col);
854 send_val_req[k] = comm.doISend(m.val.data(), m.nbNonZero(), C.send.nbr[k], tag_val);
858 UniqueArray<MessagePassing::Request> recv_ptr_req(nrecv);
859 UniqueArray<MessagePassing::Request> recv_col_req(nrecv);
860 UniqueArray<MessagePassing::Request> recv_val_req(nrecv);
862 auto B_nbr = std::make_shared<build_matrix>();
863 B_nbr->set_size(C.recv.count(), 0,
false);
866 for (
size_t k = 0; k < nrecv; ++k) {
867 ptrdiff_t beg = C.recv.ptr[k];
868 ptrdiff_t end = C.recv.ptr[k + 1];
870 recv_ptr_req[k] = comm.doIReceive(&B_nbr->ptr[beg + 1], end - beg, C.recv.nbr[k], tag_ptr);
873 ARCCORE_ALINA_TIC(
"MPI Wait");
874 comm.waitAll(recv_ptr_req);
875 ARCCORE_ALINA_TOC(
"MPI Wait");
877 B_nbr->set_nonzeros(B_nbr->scan_row_sizes(), need_values);
879 for (
size_t k = 0; k < nrecv; ++k) {
880 ptrdiff_t rbeg = C.recv.ptr[k];
881 ptrdiff_t rend = C.recv.ptr[k + 1];
883 ptrdiff_t cbeg = B_nbr->ptr[rbeg];
884 ptrdiff_t cend = B_nbr->ptr[rend];
886 recv_col_req[k] = comm.doIReceive(&B_nbr->col[cbeg], cend - cbeg, C.recv.nbr[k], tag_col);
889 recv_val_req[k] = comm.doIReceive(&B_nbr->val[cbeg], cend - cbeg, C.recv.nbr[k], tag_val);
892 ARCCORE_ALINA_TIC(
"MPI Wait");
893 comm.waitAll(send_ptr_req);
894 comm.waitAll(send_col_req);
895 comm.waitAll(recv_col_req);
898 comm.waitAll(send_val_req);
899 comm.waitAll(recv_val_req);
901 ARCCORE_ALINA_TOC(
"MPI Wait");
903 ARCCORE_ALINA_TOC(
"remote_rows");
910template <
class Backend>
911std::shared_ptr<DistributedMatrix<Backend>>
914 typedef typename Backend::value_type value_type;
915 using build_matrix = Backend::matrix;
916 typedef typename Backend::col_type
col_type;
917 ARCCORE_ALINA_TIC(
"product");
921 build_matrix& A_loc = *A.local();
922 build_matrix& A_rem = *A.remote();
923 build_matrix& B_loc = *B.local();
924 build_matrix& B_rem = *B.remote();
926 ptrdiff_t A_rows = A.loc_rows();
927 ptrdiff_t B_cols = B.loc_cols();
929 ptrdiff_t B_beg = B.loc_col_shift();
930 ptrdiff_t B_end = B_beg + B_cols;
932 auto b_nbr = remote_rows(Acp, B);
933 build_matrix& B_nbr = *b_nbr;
937 std::vector<col_type> rem_cols(B_rem.nbNonZero() + B_nbr.nbNonZero());
939 std::copy(B_nbr.col.data(), B_nbr.col.data() + B_nbr.nbNonZero(),
940 std::copy(B_rem.col.data(), B_rem.col.data() + B_rem.nbNonZero(), rem_cols.begin()));
942 std::sort(rem_cols.begin(), rem_cols.end());
943 rem_cols.erase(std::unique(rem_cols.begin(), rem_cols.end()), rem_cols.end());
945 ptrdiff_t n_rem_cols = 0;
946 std::unordered_map<ptrdiff_t, int> rem_idx(2 * rem_cols.size());
947 for (ptrdiff_t c : rem_cols) {
948 if (c >= B_beg && c < B_end)
950 rem_idx[c] = n_rem_cols++;
954 auto c_loc = std::make_shared<build_matrix>();
955 auto c_rem = std::make_shared<build_matrix>();
957 build_matrix& C_loc = *c_loc;
958 build_matrix& C_rem = *c_rem;
960 C_loc.set_size(A_rows, B_cols,
false);
961 C_rem.set_size(A_rows, 0,
false);
966 ARCCORE_ALINA_TIC(
"analyze");
968 std::vector<ptrdiff_t> loc_marker(B_end - B_beg, -1);
969 std::vector<ptrdiff_t> rem_marker(n_rem_cols, -1);
971 for (ptrdiff_t ia = begin; ia < (begin + size); ++ia) {
972 ptrdiff_t loc_cols = 0;
973 ptrdiff_t rem_cols = 0;
975 for (ptrdiff_t ja = A_loc.ptr[ia], ea = A_loc.ptr[ia + 1]; ja < ea; ++ja) {
976 ptrdiff_t ca = A_loc.col[ja];
978 for (ptrdiff_t jb = B_loc.ptr[ca], eb = B_loc.ptr[ca + 1]; jb < eb; ++jb) {
979 ptrdiff_t cb = B_loc.col[jb];
981 if (loc_marker[cb] != ia) {
987 for (ptrdiff_t jb = B_rem.ptr[ca], eb = B_rem.ptr[ca + 1]; jb < eb; ++jb) {
988 ptrdiff_t cb = rem_idx[B_rem.col[jb]];
990 if (rem_marker[cb] != ia) {
997 for (ptrdiff_t ja = A_rem.ptr[ia], ea = A_rem.ptr[ia + 1]; ja < ea; ++ja) {
998 ptrdiff_t ca = Acp.local_index(A_rem.col[ja]);
1000 for (ptrdiff_t jb = B_nbr.ptr[ca], eb = B_nbr.ptr[ca + 1]; jb < eb; ++jb) {
1001 ptrdiff_t cb = B_nbr.col[jb];
1003 if (cb >= B_beg && cb < B_end) {
1006 if (loc_marker[cb] != ia) {
1007 loc_marker[cb] = ia;
1014 if (rem_marker[cb] != ia) {
1015 rem_marker[cb] = ia;
1022 C_loc.ptr[ia + 1] = loc_cols;
1023 C_rem.ptr[ia + 1] = rem_cols;
1026 ARCCORE_ALINA_TOC(
"analyze");
1028 C_loc.set_nonzeros(C_loc.scan_row_sizes());
1029 C_rem.set_nonzeros(C_rem.scan_row_sizes());
1031 ARCCORE_ALINA_TIC(
"compute");
1033 std::vector<ptrdiff_t> loc_marker(B_end - B_beg, -1);
1034 std::vector<ptrdiff_t> rem_marker(n_rem_cols, -1);
1036 for (ptrdiff_t ia = begin; ia < (begin + size); ++ia) {
1037 ptrdiff_t loc_beg = C_loc.ptr[ia];
1038 ptrdiff_t rem_beg = C_rem.ptr[ia];
1039 ptrdiff_t loc_end = loc_beg;
1040 ptrdiff_t rem_end = rem_beg;
1042 for (ptrdiff_t ja = A_loc.ptr[ia], ea = A_loc.ptr[ia + 1]; ja < ea; ++ja) {
1043 ptrdiff_t ca = A_loc.col[ja];
1044 value_type va = A_loc.val[ja];
1046 for (ptrdiff_t jb = B_loc.ptr[ca], eb = B_loc.ptr[ca + 1]; jb < eb; ++jb) {
1047 ptrdiff_t cb = B_loc.col[jb];
1048 value_type vb = B_loc.val[jb];
1050 if (loc_marker[cb] < loc_beg) {
1051 loc_marker[cb] = loc_end;
1053 C_loc.col[loc_end] = cb;
1054 C_loc.val[loc_end] = va * vb;
1059 C_loc.val[loc_marker[cb]] += va * vb;
1063 for (ptrdiff_t jb = B_rem.ptr[ca], eb = B_rem.ptr[ca + 1]; jb < eb; ++jb) {
1064 ptrdiff_t gb = B_rem.col[jb];
1065 ptrdiff_t cb = rem_idx[gb];
1066 value_type vb = B_rem.val[jb];
1068 if (rem_marker[cb] < rem_beg) {
1069 rem_marker[cb] = rem_end;
1071 C_rem.col[rem_end] = gb;
1072 C_rem.val[rem_end] = va * vb;
1077 C_rem.val[rem_marker[cb]] += va * vb;
1082 for (ptrdiff_t ja = A_rem.ptr[ia], ea = A_rem.ptr[ia + 1]; ja < ea; ++ja) {
1083 ptrdiff_t ca = Acp.local_index(A_rem.col[ja]);
1084 value_type va = A_rem.val[ja];
1086 for (ptrdiff_t jb = B_nbr.ptr[ca], eb = B_nbr.ptr[ca + 1]; jb < eb; ++jb) {
1087 ptrdiff_t gb = B_nbr.col[jb];
1088 value_type vb = B_nbr.val[jb];
1090 if (gb >= B_beg && gb < B_end) {
1091 ptrdiff_t cb = gb - B_beg;
1093 if (loc_marker[cb] < loc_beg) {
1094 loc_marker[cb] = loc_end;
1096 C_loc.col[loc_end] = cb;
1097 C_loc.val[loc_end] = va * vb;
1102 C_loc.val[loc_marker[cb]] += va * vb;
1106 ptrdiff_t cb = rem_idx[gb];
1108 if (rem_marker[cb] < rem_beg) {
1109 rem_marker[cb] = rem_end;
1111 C_rem.col[rem_end] = gb;
1112 C_rem.val[rem_end] = va * vb;
1117 C_rem.val[rem_marker[cb]] += va * vb;
1124 ARCCORE_ALINA_TOC(
"compute");
1125 ARCCORE_ALINA_TOC(
"product");
1127 return std::make_shared<DistributedMatrix<Backend>>(A.comm(), c_loc, c_rem);
1133template <
class Backend,
class T>
1136 using build_matrix = Backend::matrix;
1138 build_matrix& A_loc = *A.local();
1139 build_matrix& A_rem = *A.remote();
1141 ptrdiff_t n = A_loc.nbRow();
1144 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1145 for (ptrdiff_t j = A_loc.ptr[i], e = A_loc.ptr[i + 1]; j < e; ++j)
1147 for (ptrdiff_t j = A_rem.ptr[i], e = A_rem.ptr[i + 1]; j < e; ++j)
1156template <
class Backend>
1159 sort_rows(*A.local());
1160 sort_rows(*A.remote());
1171namespace Arcane::Alina::backend
1177template <
class Backend>
1182 return A.loc_rows();
1186template <
class Backend,
class Alpha,
class Vec1,
class Beta,
class Vec2>
1189 static void apply(Alpha alpha,
1191 const Vec1& x, Beta beta, Vec2& y)
1193 A.mul(alpha, x, beta, y);
1197template <
class Backend,
class Vec1,
class Vec2,
class Vec3>
1200 static void apply(
const Vec1& rhs,
1202 const Vec2& x, Vec3& r)
1204 A.residual(rhs, x, r);
1215namespace Arcane::Alina
1222template <
class Backend>
1223std::shared_ptr<numa_vector<typename Backend::value_type>>
1226 return diagonal(*A.local(), invert);
1233template <
bool scale,
class Backend>
1234typename math::scalar_of<typename Backend::value_type>::type
1235spectral_radius(
const DistributedMatrix<Backend>& A,
int power_iters = 0)
1237 ARCCORE_ALINA_TIC(
"spectral radius");
1238 typedef typename Backend::value_type value_type;
1239 typedef typename math::rhs_of<value_type>::type rhs_type;
1240 typedef typename math::scalar_of<value_type>::type scalar_type;
1241 typedef CSRMatrix<value_type> build_matrix;
1243 AlinaCommunicator comm = A.comm();
1245 const build_matrix& A_loc = *A.local();
1246 const build_matrix& A_rem = *A.remote();
1247 const CommunicationPattern<Backend>& C = A.cpat();
1249 const ptrdiff_t n = A_loc.nbRow();
1250 scalar_type radius = 0;
1253 if (power_iters <= 0) {
1255 scalar_type emax = 0;
1256 value_type dia = math::identity<value_type>();
1258 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1261 for (ptrdiff_t j = A_loc.ptr[i], e = A_loc.ptr[i + 1]; j < e; ++j) {
1262 ptrdiff_t c = A_loc.col[j];
1263 value_type v = A_loc.val[j];
1267 if (scale && c == i)
1271 for (ptrdiff_t j = A_rem.ptr[i], e = A_rem.ptr[i + 1]; j < e; ++j)
1272 s += math::norm(A_rem.val[j]);
1275 s *= math::norm(math::inverse(dia));
1277 emax = std::max(emax, s);
1289 std::atomic<scalar_type> atomic_b0_loc_norm = {};
1295 std::mt19937 rng(comm.size * nt + tid);
1296 std::uniform_real_distribution<scalar_type> rnd(-1, 1);
1298 scalar_type t_norm = 0;
1300 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1301 rhs_type v = math::constant<rhs_type>(rnd(rng));
1304 t_norm += math::norm(math::inner_product(v, v));
1306 for (ptrdiff_t j = A_rem.ptr[i], e = A_rem.ptr[i + 1]; j < e; ++j) {
1307 rem_col[j] = C.local_index(A_rem.col[j]);
1312 atomic_b0_loc_norm += t_norm;
1314 scalar_type b0_loc_norm = atomic_b0_loc_norm;
1315 scalar_type b0_norm = comm.reduceSum(b0_loc_norm);
1318 b0_norm = 1 /
sqrt(b0_norm);
1320 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1321 b0[i] = b0_norm * b0[i];
1325 std::vector<rhs_type> b0_send(C.send.count());
1326 std::vector<rhs_type> b0_recv(C.recv.count());
1328 for (
size_t i = 0, m = C.send.count(); i < m; ++i)
1329 b0_send[i] = b0[C.send.col[i]];
1330 C.exchange(b0_send.data(), b0_recv.data());
1332 for (
int iter = 0; iter < power_iters;) {
1337 std::atomic<scalar_type> atomic_b1_loc_norm = 0;
1338 std::atomic<scalar_type> atomic_loc_radius = 0;
1341 scalar_type t_norm = 0;
1342 scalar_type t_radi = 0;
1343 value_type dia = math::identity<value_type>();
1345 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1346 rhs_type s = math::zero<rhs_type>();
1348 for (ptrdiff_t j = A_loc.ptr[i], e = A_loc.ptr[i + 1]; j < e; ++j) {
1349 ptrdiff_t c = A_loc.col[j];
1350 value_type v = A_loc.val[j];
1351 if (scale && c == i)
1356 for (ptrdiff_t j = A_rem.ptr[i], e = A_rem.ptr[i + 1]; j < e; ++j)
1357 s += A_rem.val[j] * b0_recv[rem_col[j]];
1360 s = math::inverse(dia) * s;
1362 t_norm += math::norm(math::inner_product(s, s));
1363 t_radi += math::norm(math::inner_product(s, b0[i]));
1370 atomic_b1_loc_norm += t_norm;
1371 atomic_loc_radius += t_radi;
1374 scalar_type b1_loc_norm = atomic_b1_loc_norm;
1375 scalar_type loc_radius = atomic_loc_radius;
1377 radius = comm.reduceSum(loc_radius);
1379 if (++iter < power_iters) {
1380 scalar_type b1_norm;
1381 b1_norm = comm.reduceSum(b1_loc_norm);
1384 b1_norm = 1 /
sqrt(b1_norm);
1386 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1387 b0[i] = b1_norm * b1[i];
1391 for (
size_t i = 0, m = C.send.count(); i < m; ++i)
1392 b0_send[i] = b0[C.send.col[i]];
1393 C.exchange(b0_send.data(), b0_recv.data());
1397 ARCCORE_ALINA_TOC(
"spectral radius");
1399 return radius < 0 ? static_cast<scalar_type>(2) : radius;
Call to handle communication pattern.
Distributed Matrix using message passing.
NUMA-aware vector container.
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.
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...
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.