12#ifndef ARCCORE_ALINA_COARSENING_H
13#define ARCCORE_ALINA_COARSENING_H
34#include "arccore/alina/BuiltinBackend.h"
35#include "arccore/alina/AlinaUtils.h"
36#include "arccore/alina/QRFactorizationImpl.h"
37#include "arccore/alina/Adapters.h"
38#include "arccore/alina/ValueTypeInterface.h"
45namespace Arcane::Alina::detail
53template <
class Matrix>
54std::shared_ptr<Matrix> galerkin(
const Matrix& A,
const Matrix& P,
const Matrix& R)
56 return product(R, *product(A, P));
64template <
class Matrix>
65std::shared_ptr<Matrix> scaled_galerkin(
const Matrix& A,
const Matrix& P,
const Matrix& R,
float s)
67 auto a = galerkin(A, P, R);
80namespace Arcane::Alina
86struct nullspace_params
96 std::vector<double>
B;
110 rows = p.get(
"rows", rows);
112 precondition(
cols > 0,
"Error in nullspace parameters: "
113 "B is set, but cols is not");
115 precondition(rows > 0,
"Error in nullspace parameters: "
116 "B is set, but rows is not");
118 B.assign(b, b + rows *
cols);
121 precondition(
cols == 0,
"Error in nullspace parameters: "
122 "cols > 0, but B is empty");
125 p.check_params( {
"cols",
"rows",
"B" });
128 void get(PropertyTree&,
const std::string&)
const {}
167 : ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p,
eps_strong)
169 p.check_params( {
"eps_strong",
"block_size" });
172 void get(PropertyTree& p,
const std::string& path)
const
174 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path,
eps_strong);
178 static const ptrdiff_t undefined = -1;
179 static const ptrdiff_t removed = -2;
198 std::vector<ptrdiff_t>
id;
206 template <
class Matrix>
210 ,
id(backend::nbRow(A))
212 typedef typename backend::value_type<Matrix>::type value_type;
213 typedef typename math::scalar_of<value_type>::type scalar_type;
217 const size_t n = backend::nbRow(A);
220 auto dia = diagonal(A);
222 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
223 value_type eps_dia_i = eps_squared * (*dia)[i];
225 for (ptrdiff_t j = A.ptr[i], e = A.ptr[i + 1]; j < e; ++j) {
226 ptrdiff_t c = A.col[j];
227 value_type v = A.val[j];
238 for (
size_t i = 0; i < n; ++i) {
239 ptrdiff_t j = A.ptr[i], e = A.ptr[i + 1];
240 max_neib = std::max<size_t>(max_neib, e - j);
242 ptrdiff_t state = removed;
252 std::vector<ptrdiff_t> neib;
253 neib.reserve(max_neib);
256 for (
size_t i = 0; i < n; ++i) {
257 if (
id[i] != undefined)
262 ptrdiff_t cur_id =
static_cast<ptrdiff_t
>(
count++);
267 for (ptrdiff_t j = A.ptr[i], e = A.ptr[i + 1]; j < e; ++j) {
268 ptrdiff_t c = A.col[j];
278 for (ptrdiff_t c : neib) {
279 for (ptrdiff_t j = A.ptr[c], e = A.ptr[c + 1]; j < e; ++j) {
280 ptrdiff_t cc = A.col[j];
292 std::vector<ptrdiff_t> cnt(
count, 0);
293 for (ptrdiff_t i :
id)
296 std::partial_sum(cnt.begin(), cnt.end(), cnt.begin());
298 if (
static_cast<ptrdiff_t
>(
count) > cnt.back()) {
301 for (
size_t i = 0; i < n; ++i)
303 id[i] = cnt[
id[i]] - 1;
315 const std::vector<ptrdiff_t>& key;
318 skip_negative(
const std::vector<ptrdiff_t>& key,
int block_size)
320 , block_size(block_size)
323 bool operator()(ptrdiff_t i, ptrdiff_t j)
const
326 return static_cast<size_t>(key[i]) / block_size <
327 static_cast<size_t>(key[j]) / block_size;
342template <
class Matrix>
343std::shared_ptr<Matrix>
344tentative_prolongation(
size_t n,
346 const std::vector<ptrdiff_t> aggr,
350 typedef typename backend::value_type<Matrix>::type value_type;
351 typedef typename backend::col_type<Matrix>::type col_type;
353 auto P = std::make_shared<Matrix>();
355 ARCCORE_ALINA_TIC(
"tentative");
356 if (nullspace.
cols > 0) {
357 ptrdiff_t nba = naggr / block_size;
361 std::vector<ptrdiff_t> order(n);
362 for (
size_t i = 0; i < n; ++i)
365 std::vector<ptrdiff_t> aggr_ptr(nba + 1, 0);
366 for (ptrdiff_t i = 0; i < static_cast<ptrdiff_t>(n); ++i) {
367 ptrdiff_t a = aggr[order[i]];
370 ++aggr_ptr[a / block_size + 1];
372 std::partial_sum(aggr_ptr.begin(), aggr_ptr.end(), aggr_ptr.begin());
377 P->set_size(n, nullspace.
cols * nba);
381 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
382 P->ptr[i + 1] = aggr[i] < 0 ? 0 : nullspace.
cols;
391 std::vector<double> Bnew;
392 Bnew.resize(nba * nullspace.
cols * nullspace.
cols);
395 Alina::detail::QRFactorization<double> qr;
396 std::vector<double> Bpart;
398 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
399 auto aggr_beg = aggr_ptr[i];
400 auto aggr_end = aggr_ptr[i + 1];
401 auto d = aggr_end - aggr_beg;
403 Bpart.resize(d * nullspace.
cols);
405 for (ptrdiff_t j = aggr_beg, jj = 0; j < aggr_end; ++j, ++jj) {
406 ptrdiff_t ib = nullspace.
cols * order[j];
407 for (
int k = 0; k < nullspace.
cols; ++k)
408 Bpart[jj + d * k] = nullspace.
B[ib + k];
411 qr.factorize(d, nullspace.
cols, &Bpart[0], Alina::detail::col_major);
413 for (
int ii = 0, kk = 0; ii < nullspace.
cols; ++ii)
414 for (
int jj = 0; jj < nullspace.
cols; ++jj, ++kk)
415 Bnew[i * nullspace.
cols * nullspace.
cols + kk] = qr.R(ii, jj);
417 for (ptrdiff_t j = aggr_beg, ii = 0; j < aggr_end; ++j, ++ii) {
418 col_type* c = &P->col[P->ptr[order[j]]];
419 value_type* v = &P->val[P->ptr[order[j]]];
421 for (
int jj = 0; jj < nullspace.
cols; ++jj) {
422 c[jj] = i * nullspace.
cols + jj;
425 v[jj] = qr.Q(ii, jj) * math::identity<value_type>();
431 std::swap(nullspace.
B, Bnew);
434 P->set_size(n, naggr);
437 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
438 P->ptr[i + 1] = (aggr[i] >= 0);
442 P->set_nonzeros(P->scan_row_sizes());
445 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
447 P->col[P->ptr[i]] = aggr[i];
448 P->val[P->ptr[i]] = math::identity<value_type>();
453 ARCCORE_ALINA_TOC(
"tentative");
487 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p,
block_size)
489 p.check_params( {
"eps_strong",
"block_size" });
494 plain_aggregates::params::get(p, path);
495 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, block_size);
499 static const ptrdiff_t undefined = -1;
500 static const ptrdiff_t removed = -2;
509 std::vector<ptrdiff_t>
id;
512 template <
class Matrix>
517 plain_aggregates aggr(A, prm);
519 remove_small_aggregates(A.nbRow(), 1, min_aggregate, aggr);
522 strong_connection.swap(aggr.strong_connection);
526 strong_connection.resize(backend::nonzeros(A));
527 id.resize(backend::nbRow(A));
529 auto ap = pointwise_matrix(A, prm.block_size);
532 plain_aggregates pw_aggr(Ap, prm);
534 remove_small_aggregates(Ap.nbRow(), prm.block_size, min_aggregate, pw_aggr);
536 count = pw_aggr.count * prm.block_size;
538 arccoreParallelFor(0, Ap.nbRow(), ForLoopRunInfo{}, [&](
Int32 begin,
Int32 size) {
539 std::vector<ptrdiff_t> j(prm.block_size);
540 std::vector<ptrdiff_t> e(prm.block_size);
542 for (ptrdiff_t ip = begin; ip < (begin + size); ++ip) {
543 ptrdiff_t ia = ip * prm.block_size;
545 for (unsigned k = 0; k < prm.block_size; ++k, ++ia) {
546 id[ia] = prm.block_size * pw_aggr.id[ip] + k;
549 e[k] = A.ptr[ia + 1];
552 for (ptrdiff_t jp = Ap.ptr[ip], ep = Ap.ptr[ip + 1]; jp < ep; ++jp) {
553 ptrdiff_t cp = Ap.col[jp];
554 bool sp = (cp == ip) || pw_aggr.strong_connection[jp];
556 ptrdiff_t col_end = (cp + 1) * prm.block_size;
558 for (unsigned k = 0; k < prm.block_size; ++k) {
559 ptrdiff_t beg = j[k];
560 ptrdiff_t end = e[k];
562 while (beg < end && A.col[beg] < col_end) {
563 strong_connection[beg] = sp && A.col[beg] != (ia + k);
575 static void remove_small_aggregates(
size_t n,
unsigned block_size,
unsigned min_aggregate,
576 plain_aggregates& aggr)
578 if (min_aggregate <= 1)
582 std::vector<ptrdiff_t> count(aggr.count, 0);
584 for (
size_t i = 0; i < n; ++i) {
585 ptrdiff_t
id = aggr.id[i];
593 for (
size_t i = 0; i < aggr.count; ++i) {
594 if (block_size * count[i] < min_aggregate) {
605 for (
size_t i = 0; i < n; ++i) {
606 ptrdiff_t
id = aggr.id[i];
608 aggr.id[i] = count[id];
637template <
class Backend>
638struct AggregationCoarsening
668 :
over_interp(math::static_rows<typename Backend::value_type>::value == 1 ? 1.5f : 2.0f)
672 : ARCCORE_ALINA_PARAMS_IMPORT_CHILD(p, aggr)
673 , ARCCORE_ALINA_PARAMS_IMPORT_CHILD(p, nullspace)
674 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, over_interp)
676 p.check_params( {
"aggr",
"nullspace",
"over_interp" });
679 void get(PropertyTree& p,
const std::string& path)
const
681 ARCCORE_ALINA_PARAMS_EXPORT_CHILD(p, path, aggr);
682 ARCCORE_ALINA_PARAMS_EXPORT_CHILD(p, path, nullspace);
683 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, over_interp);
687 explicit AggregationCoarsening(
const params& prm = params())
698 template <
class Matrix>
699 std::tuple<std::shared_ptr<Matrix>, std::shared_ptr<Matrix>>
702 const size_t n = backend::nbRow(A);
704 ARCCORE_ALINA_TIC(
"aggregates");
705 Aggregates aggr(A, prm.aggr, prm.nullspace.cols);
706 ARCCORE_ALINA_TOC(
"aggregates");
708 ARCCORE_ALINA_TIC(
"interpolation");
709 auto P = tentative_prolongation<Matrix>(
710 n, aggr.
count, aggr.
id, prm.nullspace, prm.aggr.block_size);
711 ARCCORE_ALINA_TOC(
"interpolation");
713 return std::make_tuple(P, transpose(*P));
724 template <
class Matrix>
725 std::shared_ptr<Matrix>
728 return detail::scaled_galerkin(A, P, R, 1 / prm.over_interp);
740template <
template <
class>
class Coarsening>
743 template <
class Backend>
746 typedef math::element_of<typename Backend::value_type>::type Scalar;
748 typedef Coarsening<BaseBackend> Base;
750 typedef typename Base::params params;
753 type(
const params& prm = params())
756 template <
class Matrix>
757 typename std::enable_if<backend::coarsening_is_supported<BaseBackend, Coarsening>::value &&
759 std::tuple<std::shared_ptr<Matrix>, std::shared_ptr<Matrix>>>::type
760 transfer_operators(
const Matrix& B)
762 typedef typename backend::value_type<Matrix>::type Block;
763 auto T = base.transfer_operators(*adapter::unblock_matrix(B));
765 auto& P = *std::get<0>(T);
766 auto& R = *std::get<1>(T);
771 return std::make_tuple(
772 std::make_shared<Matrix>(adapter::block_matrix<Block>(P)),
773 std::make_shared<Matrix>(adapter::block_matrix<Block>(R)));
776 template <
class Matrix>
777 typename std::enable_if<backend::coarsening_is_supported<BaseBackend, Coarsening>::value &&
779 std::tuple<std::shared_ptr<Matrix>, std::shared_ptr<Matrix>>>::type
780 transfer_operators(
const Matrix& A)
782 return base.transfer_operators(A);
785 template <
class Matrix>
786 typename std::enable_if<!backend::coarsening_is_supported<BaseBackend, Coarsening>::value,
787 std::tuple<std::shared_ptr<Matrix>, std::shared_ptr<Matrix>>>::type
788 transfer_operators(
const Matrix&)
790 throw std::logic_error(
"The coarsening is not supported by the backend");
793 template <
class Matrix>
794 std::shared_ptr<Matrix>
797 return base.coarse_operator(A, P, R);
810template <
class Vector>
811int rigid_body_modes(
int ndim,
const Vector& coo, std::vector<double>& B,
bool transpose =
false)
813 precondition(ndim == 2 || ndim == 3,
"Only 2D or 3D problems are supported");
814 precondition(coo.size() % ndim == 0,
"Coordinate vector size should be divisible by ndim");
816 size_t n = coo.size();
817 int nmodes = (ndim == 2 ? 3 : 6);
818 B.resize(n * nmodes, 0.0);
820 const int stride1 = transpose ? 1 : nmodes;
821 const int stride2 = transpose ? n : 1;
823 double sn = 1 / sqrt(n);
826 for (
size_t i = 0; i < n; ++i) {
827 size_t nod = i / ndim;
828 size_t dim = i % ndim;
830 double x = coo[nod * 2 + 0];
831 double y = coo[nod * 2 + 1];
834 B[i * stride1 + dim * stride2] = sn;
839 B[i * stride1 + 2 * stride2] = -y;
842 B[i * stride1 + 2 * stride2] = x;
847 else if (ndim == 3) {
848 for (
size_t i = 0; i < n; ++i) {
849 size_t nod = i / ndim;
850 size_t dim = i % ndim;
852 double x = coo[nod * 3 + 0];
853 double y = coo[nod * 3 + 1];
854 double z = coo[nod * 3 + 2];
857 B[i * stride1 + dim * stride2] = sn;
862 B[i * stride1 + 3 * stride2] = y;
863 B[i * stride1 + 5 * stride2] = z;
866 B[i * stride1 + 3 * stride2] = -x;
867 B[i * stride1 + 4 * stride2] = -z;
870 B[i * stride1 + 4 * stride2] = y;
871 B[i * stride1 + 5 * stride2] = -x;
878 std::array<double, 6>
dot;
879 for (
int i = ndim; i < nmodes; ++i) {
880 std::fill(
dot.begin(),
dot.end(), 0.0);
881 for (
size_t j = 0; j < n; ++j) {
882 for (
int k = 0; k < i; ++k)
883 dot[k] += B[j * stride1 + k * stride2] * B[j * stride1 + i * stride2];
886 for (
size_t j = 0; j < n; ++j) {
887 for (
int k = 0; k < i; ++k)
888 B[j * stride1 + i * stride2] -= dot[k] * B[j * stride1 + k * stride2];
889 s += B[j * stride1 + i * stride2] * B[j * stride1 + i * stride2];
892 for (
size_t j = 0; j < n; ++j)
893 B[j * stride1 + i * stride2] /= s;
907template <
class Backend>
908struct RugeStubenCoarsening
943 : ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p,
eps_strong)
944 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p,
do_trunc)
945 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p,
eps_trunc)
947 p.check_params( {
"eps_strong",
"do_trunc",
"eps_trunc" });
950 void get(
PropertyTree& p,
const std::string& path)
const
952 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, eps_strong);
953 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, do_trunc);
954 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, eps_trunc);
958 explicit RugeStubenCoarsening(
const params& prm = params())
962 template <
class Matrix>
963 std::tuple<std::shared_ptr<Matrix>, std::shared_ptr<Matrix>>
964 transfer_operators(
const Matrix& A)
const
966 typedef typename backend::value_type<Matrix>::type Val;
967 typedef typename backend::col_type<Matrix>::type Col;
968 typedef typename backend::ptr_type<Matrix>::type Ptr;
969 typedef typename math::scalar_of<Val>::type Scalar;
971 const size_t n = backend::nbRow(A);
973 static const Scalar eps = Alina::detail::eps<Scalar>(1);
975 static const Val zero = math::zero<Val>();
977 std::vector<char> cf(n,
'U');
978 CSRMatrix<char, Col, Ptr> S;
980 ARCCORE_ALINA_TIC(
"C/F split");
981 connect(A, prm.eps_strong, S, cf);
983 ARCCORE_ALINA_TOC(
"C/F split");
985 ARCCORE_ALINA_TIC(
"interpolation");
987 std::vector<ptrdiff_t> cidx(n);
988 for (
size_t i = 0; i < n; ++i)
990 cidx[i] =
static_cast<ptrdiff_t
>(nc++);
993 throw error::empty_level();
995 auto P = std::make_shared<Matrix>();
996 P->set_size(n, nc,
true);
998 std::vector<Val> Amin, Amax;
1005 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1006 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1013 Val amin = zero, amax = zero;
1015 for (ptrdiff_t j = A.ptr[i], e = A.ptr[i + 1]; j < e; ++j) {
1016 if (!S.val[j] || cf[A.col[j]] !=
'C')
1019 amin = std::min(amin, A.val[j]);
1020 amax = std::max(amax, A.val[j]);
1023 Amin[i] = (amin *= prm.eps_trunc);
1024 Amax[i] = (amax *= prm.eps_trunc);
1026 for (ptrdiff_t j = A.ptr[i], e = A.ptr[i + 1]; j < e; ++j) {
1027 if (!S.val[j] || cf[A.col[j]] !=
'C')
1030 if (A.val[j] < amin || amax < A.val[j])
1035 for (ptrdiff_t j = A.ptr[i], e = A.ptr[i + 1]; j < e; ++j)
1036 if (S.val[j] && cf[A.col[j]] ==
'C')
1042 P->set_nonzeros(P->scan_row_sizes());
1044 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1045 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1046 ptrdiff_t row_head = P->ptr[i];
1049 P->col[row_head] = cidx[i];
1050 P->val[row_head] = math::identity<Val>();
1055 Val a_num = zero, a_den = zero;
1056 Val b_num = zero, b_den = zero;
1057 Val d_neg = zero, d_pos = zero;
1059 for (ptrdiff_t j = A.ptr[i], e = A.ptr[i + 1]; j < e; ++j) {
1060 ptrdiff_t c = A.col[j];
1070 if (S.val[j] && cf[c] ==
'C') {
1072 if (prm.do_trunc && Amin[i] < v)
1078 if (S.val[j] && cf[c] ==
'C') {
1080 if (prm.do_trunc && v < Amax[i])
1090 if (math::norm(
static_cast<Val
>(a_den - d_neg)) > eps)
1091 cf_neg = math::norm(a_den) / math::norm(
static_cast<Val
>(a_den - d_neg));
1093 if (math::norm(
static_cast<Val
>(b_den - d_pos)) > eps)
1094 cf_pos = math::norm(b_den) / math::norm(
static_cast<Val
>(b_den - d_pos));
1097 if (zero < b_num && math::norm(b_den) < eps)
1100 Scalar alpha = math::norm(a_den) > eps ? -cf_neg * math::norm(a_num) / (math::norm(dia) * math::norm(a_den)) : 0;
1101 Scalar beta = math::norm(b_den) > eps ? -cf_pos * math::norm(b_num) / (math::norm(dia) * math::norm(b_den)) : 0;
1103 for (ptrdiff_t j = A.ptr[i], e = A.ptr[i + 1]; j < e; ++j) {
1104 ptrdiff_t c = A.col[j];
1107 if (!S.val[j] || cf[c] !=
'C')
1109 if (prm.do_trunc && Amin[i] <= v && v <= Amax[i])
1112 P->col[row_head] = cidx[c];
1113 P->val[row_head] = (v < zero ? alpha : beta) * v;
1118 ARCCORE_ALINA_TOC(
"interpolation");
1120 return std::make_tuple(P,
transpose(*P));
1123 template <
class Matrix>
1124 std::shared_ptr<Matrix>
1125 coarse_operator(
const Matrix& A,
const Matrix& P,
const Matrix& R)
const
1127 return detail::galerkin(A, P, R);
1139 template <
typename Val,
typename Col,
typename Ptr>
1140 static void connect(CSRMatrix<Val, Col, Ptr>
const& A,
float eps_strong,
1141 CSRMatrix<char, Col, Ptr>& S,
1142 std::vector<char>& cf)
1144 typedef typename math::scalar_of<Val>::type Scalar;
1146 const size_t n = backend::nbRow(A);
1147 const size_t nnz = backend::nonzeros(A);
1148 const Scalar eps = Alina::detail::eps<Scalar>(1);
1152 S.ptr.resize(n + 1);
1156 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1157 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1161 Val a_min = math::zero<Val>();
1163 for (
auto a = backend::row_begin(A, i); a; ++a)
1165 a_min = std::min(a_min, a.value());
1167 if (math::norm(a_min) < eps) {
1172 a_min *= eps_strong;
1174 for (Ptr j = A.ptr[i], e = A.ptr[i + 1]; j < e; ++j)
1175 S.val[j] = (A.col[j] != i && A.val[j] < a_min);
1180 for (
size_t i = 0; i < nnz; ++i)
1182 ++(S.ptr[A.col[i] + 1]);
1185 S.col.resize(S.ptr[n]);
1187 for (
size_t i = 0; i < n; ++i)
1188 for (Ptr j = A.ptr[i], e = A.ptr[i + 1]; j < e; ++j)
1190 S.col[S.ptr[A.col[j]]++] = i;
1192 std::rotate(S.ptr.data(), S.ptr.data() + n, S.ptr.data() + n + 1);
1197 template <
typename Val,
typename Col,
typename Ptr>
1198 static void cfsplit(CSRMatrix<Val, Col, Ptr>
const& A,
1199 CSRMatrix<char, Col, Ptr>
const& S,
1200 std::vector<char>& cf)
1202 const size_t n = A.nbRow();
1204 std::vector<Col> lambda(n);
1207 for (
size_t i = 0; i < n; ++i) {
1209 for (Ptr j = S.ptr[i], e = S.ptr[i + 1]; j < e; ++j)
1210 temp += (cf[S.col[j]] ==
'U' ? 1 : 2);
1219 std::vector<Ptr> ptr(n + 1, 0);
1220 std::vector<Ptr> cnt(n, 0);
1221 std::vector<Ptr> i2n(n);
1222 std::vector<Ptr> n2i(n);
1224 for (
size_t i = 0; i < n; ++i)
1225 ++ptr[lambda[i] + 1];
1227 std::partial_sum(ptr.begin(), ptr.end(), ptr.begin());
1229 for (
size_t i = 0; i < n; ++i) {
1230 Col lam = lambda[i];
1231 Ptr idx = ptr[lam] + cnt[lam]++;
1240 for (
size_t top = n; top-- > 0;) {
1242 Col lam = lambda[i];
1245 std::replace(cf.begin(), cf.end(),
'U',
'C');
1259 for (Ptr j = S.ptr[i], e = S.ptr[i + 1]; j < e; ++j) {
1268 for (Ptr aj = A.ptr[c], ae = A.ptr[c + 1]; aj < ae; ++aj) {
1273 Col lam_a = lambda[ac];
1275 if (cf[ac] !=
'U' ||
static_cast<size_t>(lam_a) + 1 >= n)
1278 Ptr old_pos = n2i[ac];
1279 Ptr new_pos = ptr[lam_a] + cnt[lam_a] - 1;
1281 n2i[i2n[old_pos]] = new_pos;
1282 n2i[i2n[new_pos]] = old_pos;
1284 std::swap(i2n[old_pos], i2n[new_pos]);
1288 ptr[lam_a + 1] = ptr[lam_a] + cnt[lam_a];
1290 lambda[ac] = lam_a + 1;
1295 for (Ptr j = A.ptr[i], e = A.ptr[i + 1]; j < e; j++) {
1300 Col lam = lambda[c];
1302 if (cf[c] !=
'U' || lam == 0)
1305 Ptr old_pos = n2i[c];
1306 Ptr new_pos = ptr[lam];
1308 n2i[i2n[old_pos]] = new_pos;
1309 n2i[i2n[new_pos]] = old_pos;
1311 std::swap(i2n[old_pos], i2n[new_pos]);
1316 lambda[c] = lam - 1;
1330template <
class Backend>
1331struct SmoothedAggregationCoarserning
1376 bool estimate_spectral_radius =
false;
1380 int power_iters = 0;
1385 : ARCCORE_ALINA_PARAMS_IMPORT_CHILD(p,
aggr)
1386 , ARCCORE_ALINA_PARAMS_IMPORT_CHILD(p,
nullspace)
1387 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p,
relax)
1388 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, estimate_spectral_radius)
1389 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, power_iters)
1391 p.check_params({
"aggr",
"nullspace",
"relax",
"estimate_spectral_radius",
"power_iters" });
1394 void get(
PropertyTree& p,
const std::string& path)
const
1396 ARCCORE_ALINA_PARAMS_EXPORT_CHILD(p, path, aggr);
1397 ARCCORE_ALINA_PARAMS_EXPORT_CHILD(p, path, nullspace);
1398 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, relax);
1399 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, estimate_spectral_radius);
1400 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, power_iters);
1404 SmoothedAggregationCoarserning(
const params& prm = params())
1408 template <
class Matrix>
1409 std::tuple<std::shared_ptr<Matrix>, std::shared_ptr<Matrix>>
1410 transfer_operators(
const Matrix& A)
1412 typedef typename backend::value_type<Matrix>::type value_type;
1413 typedef typename math::scalar_of<value_type>::type scalar_type;
1415 const size_t n = backend::nbRow(A);
1417 ARCCORE_ALINA_TIC(
"aggregates");
1418 Aggregates aggr(A, prm.aggr, prm.nullspace.cols);
1419 prm.aggr.eps_strong *= 0.5;
1420 ARCCORE_ALINA_TOC(
"aggregates");
1422 auto P_tent = tentative_prolongation<Matrix>(n, aggr.count, aggr.id, prm.nullspace, prm.aggr.
block_size);
1424 auto P = std::make_shared<Matrix>();
1425 P->set_size(backend::nbRow(*P_tent), backend::nbColumn(*P_tent),
true);
1427 scalar_type omega = prm.relax;
1428 if (prm.estimate_spectral_radius) {
1429 omega *=
static_cast<scalar_type
>(4.0 / 3) / spectral_radius<true>(A, prm.power_iters);
1432 omega *=
static_cast<scalar_type
>(2.0 / 3);
1435 ARCCORE_ALINA_TIC(
"smoothing");
1436 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1437 std::vector<ptrdiff_t> marker(P->ncols, -1);
1440 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1441 for (ptrdiff_t ja = A.ptr[i], ea = A.ptr[i + 1]; ja < ea; ++ja) {
1442 ptrdiff_t ca = A.col[ja];
1445 if (ca != i && !aggr.strong_connection[ja])
1448 for (ptrdiff_t jp = P_tent->ptr[ca], ep = P_tent->ptr[ca + 1]; jp < ep; ++jp) {
1449 ptrdiff_t cp = P_tent->col[jp];
1451 if (marker[cp] != i) {
1460 P->scan_row_sizes();
1463 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1464 std::vector<ptrdiff_t> marker(P->ncols, -1);
1467 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1471 value_type dia = math::zero<value_type>();
1472 for (ptrdiff_t j = A.ptr[i], e = A.ptr[i + 1]; j < e; ++j) {
1473 if (A.col[j] == i || !aggr.strong_connection[j])
1476 if (!math::is_zero(dia))
1477 dia = -omega * math::inverse(dia);
1479 ptrdiff_t row_beg = P->ptr[i];
1480 ptrdiff_t row_end = row_beg;
1481 for (ptrdiff_t ja = A.ptr[i], ea = A.ptr[i + 1]; ja < ea; ++ja) {
1482 ptrdiff_t ca = A.col[ja];
1485 if (ca != i && !aggr.strong_connection[ja])
1488 value_type va = (ca == i)
1489 ?
static_cast<value_type
>(
static_cast<scalar_type
>(1 - omega) * math::identity<value_type>())
1492 for (ptrdiff_t jp = P_tent->ptr[ca], ep = P_tent->ptr[ca + 1]; jp < ep; ++jp) {
1493 ptrdiff_t cp = P_tent->col[jp];
1494 value_type vp = P_tent->val[jp];
1496 if (marker[cp] < row_beg) {
1497 marker[cp] = row_end;
1498 P->col[row_end] = cp;
1499 P->val[row_end] = va * vp;
1503 P->val[marker[cp]] += va * vp;
1509 ARCCORE_ALINA_TOC(
"smoothing");
1511 return std::make_tuple(P,
transpose(*P));
1514 template <
class Matrix>
1515 std::shared_ptr<Matrix>
1516 coarse_operator(
const Matrix& A,
const Matrix& P,
const Matrix& R)
const
1518 return detail::galerkin(A, P, R);
1532template <
class Backend>
1533struct SmoothedAggregationEnergyMinCoarsening
1549 : ARCCORE_ALINA_PARAMS_IMPORT_CHILD(p, aggr)
1550 , ARCCORE_ALINA_PARAMS_IMPORT_CHILD(p, nullspace)
1552 p.check_params( {
"aggr",
"nullspace" });
1555 void get(PropertyTree& p,
const std::string& path)
const
1557 ARCCORE_ALINA_PARAMS_EXPORT_CHILD(p, path, aggr);
1558 ARCCORE_ALINA_PARAMS_EXPORT_CHILD(p, path, nullspace);
1562 SmoothedAggregationEnergyMinCoarsening(
const params& prm = params())
1566 template <
class Matrix>
1567 std::tuple<std::shared_ptr<Matrix>, std::shared_ptr<Matrix>>
1568 transfer_operators(
const Matrix& A)
1570 typedef typename backend::value_type<Matrix>::type Val;
1571 typedef typename backend::col_type<Matrix>::type Col;
1572 typedef typename backend::ptr_type<Matrix>::type Ptr;
1573 typedef ptrdiff_t Idx;
1575 ARCCORE_ALINA_TIC(
"aggregates");
1576 Aggregates aggr(A, prm.aggr, prm.nullspace.cols);
1577 prm.aggr.eps_strong *= 0.5;
1578 ARCCORE_ALINA_TOC(
"aggregates");
1580 ARCCORE_ALINA_TIC(
"interpolation");
1581 auto P_tent = tentative_prolongation<Matrix>(backend::nbRow(A), aggr.count, aggr.id, prm.nullspace, prm.aggr.
block_size);
1584 CSRMatrix<Val, Col, Ptr> Af;
1585 Af.set_size(backend::nbRow(A), backend::nbColumn(A));
1588 std::vector<Val> dia(Af.nbRow());
1590 arccoreParallelFor(0, Af.nbRow(), ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1591 for (Idx i = begin; i < (begin + size); ++i) {
1592 Idx row_begin = A.ptr[i];
1593 Idx row_end = A.ptr[i + 1];
1594 Idx row_width = row_end - row_begin;
1596 Val D = math::zero<Val>();
1597 for (Idx j = row_begin; j < row_end; ++j) {
1603 else if (!aggr.strong_connection[j]) {
1610 Af.ptr[i + 1] = row_width;
1614 Af.set_nonzeros(Af.scan_row_sizes());
1616 arccoreParallelFor(0, Af.nbRow(), ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1617 for (Idx i = begin; i < (begin + size); ++i) {
1619 Idx row_begin = A.ptr[i];
1620 Idx row_end = A.ptr[i + 1];
1621 Idx row_head = Af.ptr[i];
1623 for (Idx j = row_begin; j < row_end; ++j) {
1627 Af.col[row_head] = i;
1628 Af.val[row_head] = dia[i];
1631 else if (aggr.strong_connection[j]) {
1632 Af.col[row_head] = c;
1633 Af.val[row_head] = A.val[j];
1640 std::vector<Val> omega;
1642 auto P = interpolation(Af, dia, *P_tent, omega);
1643 auto R = restriction(Af, dia, *P_tent, omega);
1644 ARCCORE_ALINA_TOC(
"interpolation");
1646 return std::make_tuple(P, R);
1649 template <
class Matrix>
1650 std::shared_ptr<Matrix>
1651 coarse_operator(
const Matrix& A,
const Matrix& P,
const Matrix& R)
const
1653 return detail::galerkin(A, P, R);
1658 template <
class AMatrix,
typename Val,
typename Col,
typename Ptr>
1659 static std::shared_ptr<CSRMatrix<Val, Col, Ptr>>
1660 interpolation(
const AMatrix& A,
const std::vector<Val>& Adia,
1661 const CSRMatrix<Val, Col, Ptr>& P_tent,
1662 std::vector<Val>& omega)
1664 const size_t n = backend::nbRow(P_tent);
1665 const size_t nc = backend::nbColumn(P_tent);
1667 auto AP = product(A, P_tent,
true);
1669 omega.resize(nc, math::zero<Val>());
1670 std::vector<Val> denum(nc, math::zero<Val>());
1676 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1677 std::vector<ptrdiff_t> marker(nc, -1);
1682 std::vector<Col> adap_col(128);
1683 std::vector<Val> adap_val(128);
1685 for (ptrdiff_t ia = begin; ia < (begin + size); ++ia) {
1690 for (
auto a = A.row_begin(ia); a; ++a) {
1692 Val va = math::inverse(Adia[ca]) * a.value();
1694 for (
auto p = AP->row_begin(ca); p; ++p) {
1696 Val v = va * p.value();
1698 if (marker[c] < 0) {
1699 marker[c] = adap_col.size();
1700 adap_col.push_back(c);
1701 adap_val.push_back(v);
1704 adap_val[marker[c]] += v;
1709 detail::sort_row(&adap_col[0], &adap_val[0], adap_col.size());
1714 Ptr ja = AP->ptr[ia], ea = AP->ptr[ia + 1],
1715 jb = 0, eb = adap_col.size();
1716 ja < ea && jb < eb;) {
1717 Col ca = AP->col[ja];
1718 Col cb = adap_col[jb];
1725 Val v = AP->val[ja] * adap_val[jb];
1727 std::scoped_lock lock(mutex1);
1736 for (
size_t j = 0, e = adap_col.size(); j < e; ++j) {
1737 Col c = adap_col[j];
1738 Val v = adap_val[j];
1740 std::scoped_lock lock(mutex2);
1748 for (
size_t i = 0, m = omega.size(); i < m; ++i)
1749 omega[i] = math::inverse(denum[i]) * omega[i];
1758 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1759 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1760 Val dia = math::inverse(Adia[i]);
1762 for (Ptr ja = AP->ptr[i], ea = AP->ptr[i + 1],
1763 jp = P_tent.ptr[i], ep = P_tent.ptr[i + 1];
1765 Col ca = AP->col[ja];
1766 Val va = -dia * AP->val[ja] * omega[ca];
1768 for (; jp < ep; ++jp) {
1769 Col cp = P_tent.col[jp];
1774 va += P_tent.val[jp];
1787 template <
typename AMatrix,
typename Val,
typename Col,
typename Ptr>
1788 static std::shared_ptr<CSRMatrix<Val, Col, Ptr>>
1789 restriction(
const AMatrix& A,
const std::vector<Val>& Adia,
1790 const CSRMatrix<Val, Col, Ptr>& P_tent,
1791 const std::vector<Val>& omega)
1793 const size_t nc = backend::nbColumn(P_tent);
1798 auto RA = product(*R_tent, A,
true);
1807 arccoreParallelFor(0, nc, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1808 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1811 for (Ptr ja = RA->ptr[i], ea = RA->ptr[i + 1],
1812 jr = R_tent->ptr[i], er = R_tent->ptr[i + 1];
1814 Col ca = RA->col[ja];
1815 Val va = -w * math::inverse(Adia[ca]) * RA->val[ja];
1817 for (; jr < er; ++jr) {
1818 Col cr = R_tent->col[jr];
1823 va += R_tent->val[jr];
1845namespace Arcane::Alina::backend
1848template <
class Backend>
1850 typename std::enable_if<!std::is_arithmetic<typename backend::value_type<Backend>::type>::value>
::type> : std::false_type
std::vector< char > strong_connection
Strong connectivity matrix.
std::vector< ptrdiff_t > id
Aggerate id that each fine-level variable belongs to.
size_t count
Number of aggregates.
pointwise_aggregates(const Matrix &A, const params &prm, unsigned min_aggregate)
Constructs aggregates for a given matrix.
Informations d'exécution d'une boucle.
Matrix class, to be used by user.
Vector class, to be used by user.
__host__ __device__ Real dot(Real2 u, Real2 v)
Produit scalaire de u par v dans .
__host__ __device__ double sqrt(double v)
Racine carrée de v.
__host__ __device__ Real3x3 transpose(const Real3x3 &t)
Transpose la matrice.
void arccoreParallelFor(const ComplexForLoopRanges< RankValue > &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...
std::int32_t Int32
Type entier signé sur 32 bits.
float over_interp
Over-interpolation factor .
nullspace_params nullspace
Near nullspace parameters.
Aggregates::params aggr
Aggregation parameters.
std::tuple< std::shared_ptr< Matrix >, std::shared_ptr< Matrix > > transfer_operators(const Matrix &A)
Creates transfer operators for the given system matrix.
std::shared_ptr< Matrix > coarse_operator(const Matrix &A, const Matrix &P, const Matrix &R) const
Creates system matrix for the coarser level.
Apply a scalar coarsening on a block matrix.
bool do_trunc
Truncate prolongation operator?
float eps_trunc
Truncation parameter .
float eps_strong
Parameter defining strong couplings.
Classic Ruge-Stuben coarsening with direct interpolation.
float relax
Relaxation factor.
Aggregates::params aggr
Aggregation parameters.
nullspace_params nullspace
Near nullspace parameters.
Aggregates::params aggr
Aggregation parameters.
nullspace_params nullspace
Near nullspace parameters.
Is the coarsening supported by the backend?
Number of rows for statically sized matrix types.
std::vector< double > B
Near nullspace vectors.
int cols
Number of vectors in near nullspace.
float eps_strong
Parameter defining strong couplings.
std::vector< ptrdiff_t > id
Aggerate id that each fine-level variable belongs to.
std::vector< char > strong_connection
Strong connectivity matrix.
plain_aggregates(const Matrix &A, const params &prm)
Constructs aggregates for a given matrix.
size_t count
Number of aggregates.
Int32 block_size
Block size for the system matrix.