Arcane  4.2.3.0
Developer documentation
Loading...
Searching...
No Matches
Relaxation.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/* Relaxation.h (C) 2000-2026 */
9/* */
10/* Various relaxation algorithms. */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCCORE_ALINA_RELAXATION_H
13#define ARCCORE_ALINA_RELAXATION_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/BackendInterface.h"
27#include "arccore/alina/Adapters.h"
28#include "arccore/alina/ValueTypeInterface.h"
29#include "arccore/alina/QRFactorizationImpl.h"
30#include "arccore/alina/BuiltinBackend.h"
31#include "arccore/alina/DenseMatrixInverseImpl.h"
32#include "arccore/alina/BackendInterface.h"
33#include "arccore/alina/ILUSolverImpl.h"
34#include "arccore/alina/ValueTypeInterface.h"
35#include "arccore/alina/RelaxationBase.h"
36
37#include <deque>
38#include <queue>
39
40/*---------------------------------------------------------------------------*/
41/*---------------------------------------------------------------------------*/
42
43namespace Arcane::Alina
44{
45
46/*---------------------------------------------------------------------------*/
47/*---------------------------------------------------------------------------*/
51template <class BlockBackend, template <class> class Relax>
53{
54 typedef typename BlockBackend::value_type BlockType;
55
56 template <class Backend>
57 class type
58 {
59 public:
60
61 typedef Backend backend_type;
62
63 typedef Relax<BlockBackend> Base;
64
65 typedef typename Backend::matrix matrix;
66 typedef typename Backend::vector vector;
67 typedef typename Base::params params;
68 typedef typename Backend::params backend_params;
69
70 typedef typename Backend::value_type value_type;
71 typedef typename Backend::col_type col_type;
72 typedef typename Backend::ptr_type ptr_type;
73 typedef typename BuiltinBackend<value_type, col_type, ptr_type>::matrix build_matrix;
74
75 template <class Matrix>
76 type(const Matrix& A,
77 const params& prm = params(),
78 const backend_params& bprm = backend_params())
79 : base(*std::make_shared<CSRMatrix<BlockType, col_type, ptr_type>>(adapter::block_matrix<BlockType>(A)), prm, bprm)
80 , nrows(backend::nbRow(A) / math::static_rows<BlockType>::value)
81 {}
82
83 template <class Matrix, class VectorRHS, class VectorX, class VectorTMP>
84 void apply_pre(const Matrix& A,
85 const VectorRHS& rhs,
86 VectorX& x,
87 VectorTMP& tmp) const
88 {
89 auto F = backend::reinterpret_as_rhs<BlockType>(rhs);
90 auto X = backend::reinterpret_as_rhs<BlockType>(x);
91 auto T = backend::reinterpret_as_rhs<BlockType>(tmp);
92 base.apply_pre(A, F, X, T);
93 }
94
95 template <class Matrix, class VectorRHS, class VectorX, class VectorTMP>
96 void apply_post(const Matrix& A,
97 const VectorRHS& rhs,
98 VectorX& x,
99 VectorTMP& tmp) const
100 {
101 auto F = backend::reinterpret_as_rhs<BlockType>(rhs);
102 auto X = backend::reinterpret_as_rhs<BlockType>(x);
103 auto T = backend::reinterpret_as_rhs<BlockType>(tmp);
104 base.apply_post(A, F, X, T);
105 }
106
107 template <class Matrix, class Vec1, class Vec2>
108 void apply(const Matrix& A, const Vec1& rhs, Vec2&& x) const
109 {
110 auto F = backend::reinterpret_as_rhs<BlockType>(rhs);
111 auto X = backend::reinterpret_as_rhs<BlockType>(x);
112 base.apply(A, F, X);
113 }
114
115 const matrix& system_matrix() const
116 {
117 return base.system_matrix();
118 }
119
120 std::shared_ptr<matrix> system_matrix_ptr() const
121 {
122 return base.system_matrix_ptr();
123 }
124
125 size_t bytes() const
126 {
127 return base.bytes();
128 }
129
130 private:
131
132 Base base;
133 size_t nrows;
134 };
135};
136
137/*---------------------------------------------------------------------------*/
138/*---------------------------------------------------------------------------*/
142template <class Backend, template <class> class Relax>
143class RelaxationAsPreconditioner
144{
145 public:
146
147 typedef Backend backend_type;
148
149 typedef Relax<Backend> smoother;
150
151 typedef typename Backend::matrix matrix;
152 typedef typename Backend::vector vector;
153 typedef typename smoother::params params;
154 typedef typename Backend::params backend_params;
155
156 typedef typename Backend::value_type value_type;
157 typedef typename Backend::col_type col_type;
158 typedef typename Backend::ptr_type ptr_type;
159 typedef typename BuiltinBackend<value_type, col_type, ptr_type>::matrix build_matrix;
160
161 template <class Matrix>
162 RelaxationAsPreconditioner([[maybe_unused]] const AlinaContext& context,
163 const Matrix& M,
164 const params& prm = params(),
165 const backend_params& bprm = backend_params())
166 : prm(prm)
167 {
168 init(std::make_shared<build_matrix>(M), bprm);
169 }
170
171 RelaxationAsPreconditioner([[maybe_unused]] const AlinaContext& context,
172 std::shared_ptr<build_matrix> M,
173 const params& prm = params(),
174 const backend_params& bprm = backend_params())
175 : prm(prm)
176 {
177 init(M, bprm);
178 }
179
180 template <class Vec1, class Vec2>
181 void apply(const Vec1& rhs, Vec2&& x) const
182 {
183 S->apply(*A, rhs, x);
184 }
185
186 const matrix& system_matrix() const
187 {
188 return *A;
189 }
190
191 std::shared_ptr<matrix> system_matrix_ptr() const
192 {
193 return A;
194 }
195
196 size_t bytes() const
197 {
198 size_t b = 0;
199
200 if (A)
201 b += backend::bytes(*A);
202 if (S)
203 b += backend::bytes(*S);
204
205 return b;
206 }
207
208 private:
209
210 params prm;
211
212 std::shared_ptr<matrix> A;
213 std::shared_ptr<smoother> S;
214
215 void init(std::shared_ptr<build_matrix> M, const backend_params& bprm)
216 {
217 A = Backend::copy_matrix(M, bprm);
218 S = std::make_shared<smoother>(*M, prm, bprm);
219 }
220
221 friend std::ostream& operator<<(std::ostream& os, const RelaxationAsPreconditioner& p)
222 {
223 os << "Relaxation as preconditioner" << std::endl;
224 os << " Unknowns: " << backend::nbRow(p.system_matrix()) << std::endl;
225 os << " Nonzeros: " << backend::nonzeros(p.system_matrix()) << std::endl;
226 os << " Memory: " << human_readable_memory(p.bytes()) << std::endl;
227
228 return os;
229 }
230};
231
232/*---------------------------------------------------------------------------*/
233/*---------------------------------------------------------------------------*/
245template <class Backend>
247: public RelaxationBase
248{
249 public:
250
251 typedef typename Backend::value_type value_type;
252 typedef typename Backend::vector vector;
253
254 typedef typename math::scalar_of<value_type>::type scalar_type;
255
257 struct params
258 {
261
263 // use boosting factor for a more conservative upper bound estimate
264 // See: Adams, Brezina, Hu, Tuminaro,
265 // PARALLEL MULTIGRID SMOOTHING: POLYNOMIAL VERSUS
266 // GAUSS-SEIDEL, J. Comp. Phys. 188 (2003) 593-610.
267 //
268 double higher = 1.0;
269
271 double lower = 1.0 / 30.0;
272
273 // Number of power iterations to apply for the spectral radius
274 // estimation. When 0, use Gershgorin disk theorem to estimate
275 // spectral radius.
276 Int32 power_iters = 0;
277
278 // Scale the system matrix
279 bool scale = false;
280
281 params() = default;
282
283 params(const PropertyTree& p)
284 : ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, degree)
285 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, higher)
286 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, lower)
287 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, power_iters)
288 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, scale)
289 {
290 p.check_params({ "degree", "higher", "lower", "power_iters", "scale" });
291 }
292
293 void get(PropertyTree& p, const std::string& path) const
294 {
295 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, degree);
296 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, higher);
297 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, lower);
298 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, power_iters);
299 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, scale);
300 }
301 } prm;
302
304 template <class Matrix>
305 ChebyshevRelaxation(const Matrix& A, const params& prm,
306 const typename Backend::params& backend_prm)
307 : prm(prm)
308 , p(Backend::create_vector(backend::nbRow(A), backend_prm))
309 , r(Backend::create_vector(backend::nbRow(A), backend_prm))
310 {
311 scalar_type hi, lo;
312
313 //using spectral_radius;
314
315 if (prm.scale) {
316 M = Backend::copy_vector(diagonal(A, /*invert*/ true), backend_prm);
317 hi = spectral_radius<true>(A, prm.power_iters);
318 }
319 else {
320 hi = spectral_radius<false>(A, prm.power_iters);
321 }
322
323 lo = hi * prm.lower;
324 hi *= prm.higher;
325
326 // Centre of ellipse containing the eigenvalues of A:
327 d = 0.5 * (hi + lo);
328
329 // Semi-major axis of ellipse containing the eigenvalues of A:
330 c = 0.5 * (hi - lo);
331 }
332
333 template <class Matrix, class VectorRHS, class VectorX, class VectorTMP>
334 void apply_pre(const Matrix& A, const VectorRHS& rhs, VectorX& x, VectorTMP&) const
335 {
336 solve(A, rhs, x);
337 }
338
339 template <class Matrix, class VectorRHS, class VectorX, class VectorTMP>
340 void apply_post(const Matrix& A, const VectorRHS& rhs, VectorX& x, VectorTMP&) const
341 {
342 solve(A, rhs, x);
343 }
344
345 template <class Matrix, class VectorRHS, class VectorX>
346 void apply(const Matrix& A, const VectorRHS& rhs, VectorX& x) const
347 {
348 backend::clear(x);
349 solve(A, rhs, x);
350 }
351
352 size_t bytes() const
353 {
354 size_t b = backend::bytes(*p) + backend::bytes(*r);
355 if (prm.scale)
356 b += backend::bytes(*M);
357 return b;
358 }
359
360 private:
361
362 std::shared_ptr<typename Backend::matrix_diagonal> M;
363 mutable std::shared_ptr<vector> p, r;
364
365 scalar_type c, d;
366
367 template <class Matrix, class VectorB, class VectorX>
368 void solve(const Matrix& A, const VectorB& b, VectorX& x) const
369 {
370 static const scalar_type one = math::identity<scalar_type>();
371 static const scalar_type zero = math::zero<scalar_type>();
372
373 scalar_type alpha = zero, beta = zero;
374
375 for (unsigned k = 0; k < prm.degree; ++k) {
376 backend::residual(b, A, x, *r);
377
378 if (prm.scale)
379 backend::vmul(one, *M, *r, zero, *r);
380
381 if (k == 0) {
382 alpha = math::inverse(d);
383 beta = zero;
384 }
385 else if (k == 1) {
386 alpha = 2 * d * math::inverse(2 * d * d - c * c);
387 beta = alpha * d - one;
388 }
389 else {
390 alpha = math::inverse(d - 0.25 * alpha * c * c);
391 beta = alpha * d - one;
392 }
393
394 backend::axpby(alpha, *r, beta, *p);
395 backend::axpby(one, *p, one, x);
396 }
397 }
398};
399
400/*---------------------------------------------------------------------------*/
401/*---------------------------------------------------------------------------*/
408template <class Backend>
410: public RelaxationBase
411{
412 typedef typename Backend::value_type value_type;
413 typedef typename math::scalar_of<value_type>::type scalar_type;
414
416 struct params
417 {
419 scalar_type damping = 0.72;
420
421 params() = default;
422
423 params(scalar_type damping)
425 {}
426
427 params(const PropertyTree& p)
428 : ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, damping)
429 {
430 p.check_params({ "damping" });
431 }
432
433 void get(PropertyTree& p, const std::string& path) const
434 {
435 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, damping);
436 }
437 } prm;
438
439 std::shared_ptr<typename Backend::matrix_diagonal> dia;
440
442
447 template <class Matrix>
449 const params& prm,
450 const typename Backend::params& backend_prm)
451 : prm(prm)
452 , dia(Backend::copy_vector(diagonal(A, true), backend_prm))
453 {}
454
456
463 template <class Matrix, class VectorRHS, class VectorX, class VectorTMP>
464 void apply_pre(const Matrix& A, const VectorRHS& rhs, VectorX& x, VectorTMP& tmp) const
465 {
466 backend::residual(rhs, A, x, tmp);
467 backend::vmul(prm.damping, *dia, tmp, math::identity<scalar_type>(), x);
468 }
469
471
478 template <class Matrix, class VectorRHS, class VectorX, class VectorTMP>
479 void apply_post(const Matrix& A, const VectorRHS& rhs, VectorX& x, VectorTMP& tmp) const
480 {
481 backend::residual(rhs, A, x, tmp);
482 backend::vmul(prm.damping, *dia, tmp, math::identity<scalar_type>(), x);
483 }
484
485 template <class Matrix, class VectorRHS, class VectorX>
486 void apply(const Matrix&, const VectorRHS& rhs, VectorX& x) const
487 {
488 backend::vmul(math::identity<scalar_type>(), *dia, rhs, math::zero<scalar_type>(), x);
489 }
490
491 size_t bytes() const
492 {
493 return backend::bytes(*dia);
494 }
495};
496
497/*---------------------------------------------------------------------------*/
498/*---------------------------------------------------------------------------*/
509template <class Backend>
511: public RelaxationBase
512{
514 struct params
515 {
517 bool serial;
518
519 params()
520 : serial(false)
521 {}
522
523 params(const PropertyTree& p)
524 : ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, serial)
525 {
526 p.check_params({ "serial" });
527 }
528
529 void get(PropertyTree& p, const std::string& path) const
530 {
531 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, serial);
532 }
533 };
534
535 bool is_serial;
536
538 template <class Matrix>
539 GaussSeidelRelaxation(const Matrix& A, const params& prm, const typename Backend::params&)
540 : is_serial(prm.serial || ConcurrencyBase::maxAllowedThread() < 4)
541 {
542 if (!is_serial) {
543 forward = std::make_shared<parallel_sweep<true>>(A);
544 backward = std::make_shared<parallel_sweep<false>>(A);
545 }
546 }
547
549 template <class Matrix, class VectorRHS, class VectorX, class VectorTMP>
550 void apply_pre(const Matrix& A, const VectorRHS& rhs, VectorX& x, VectorTMP&) const
551 {
552 if (is_serial)
553 serial_sweep(A, rhs, x, true);
554 else
555 forward->sweep(rhs, x);
556 }
557
559 template <class Matrix, class VectorRHS, class VectorX, class VectorTMP>
560 void apply_post(const Matrix& A, const VectorRHS& rhs, VectorX& x, VectorTMP&) const
561 {
562 if (is_serial)
563 serial_sweep(A, rhs, x, false);
564 else
565 backward->sweep(rhs, x);
566 }
567
568 template <class Matrix, class VectorRHS, class VectorX>
569 void apply(const Matrix& A, const VectorRHS& rhs, VectorX& x) const
570 {
571 backend::clear(x);
572 if (is_serial) {
573 serial_sweep(A, rhs, x, true);
574 serial_sweep(A, rhs, x, false);
575 }
576 else {
577 forward->sweep(rhs, x);
578 backward->sweep(rhs, x);
579 }
580 }
581
582 size_t bytes() const
583 {
584 size_t b = 0;
585 if (forward)
586 b += forward->bytes();
587 if (backward)
588 b += backward->bytes();
589 return b;
590 }
591
592 private:
593
594 template <class Matrix, class VectorRHS, class VectorX>
595 static void serial_sweep(const Matrix& A, const VectorRHS& rhs, VectorX& x, bool forward)
596 {
597 typedef typename backend::value_type<Matrix>::type val_type;
598 typedef typename math::rhs_of<val_type>::type rhs_type;
599
600 const ptrdiff_t n = backend::nbRow(A);
601
602 const ptrdiff_t beg = forward ? 0 : n - 1;
603 const ptrdiff_t end = forward ? n : -1;
604 const ptrdiff_t inc = forward ? 1 : -1;
605
606 for (ptrdiff_t i = beg; i != end; i += inc) {
607 val_type D = math::identity<val_type>();
608 rhs_type X;
609 X = rhs[i];
610
611 for (auto a = backend::row_begin(A, i); a; ++a) {
612 ptrdiff_t c = a.col();
613 val_type v = a.value();
614
615 if (c == i)
616 D = v;
617 else
618 X -= v * x[c];
619 }
620
621 x[i] = math::inverse(D) * X;
622 }
623 }
624
625 template <bool forward>
626 struct parallel_sweep
627 {
628 typedef typename Backend::value_type value_type;
629 typedef typename math::rhs_of<value_type>::type rhs_type;
630
631 struct task
632 {
633 ptrdiff_t beg, end;
634 task(ptrdiff_t beg, ptrdiff_t end)
635 : beg(beg)
636 , end(end)
637 {}
638 };
639
640 int nthreads = 0;
641
642 // thread-specific storage:
648
649 template <class Matrix>
650 parallel_sweep(const Matrix& A)
651 : nthreads(ConcurrencyBase::maxAllowedThread())
652 , tasks(nthreads)
653 , ptr(nthreads)
654 , col(nthreads)
655 , val(nthreads)
656 , ord(nthreads)
657 {
658
659 ptrdiff_t n = backend::nbRow(A);
660 ptrdiff_t nlev = 0;
661
662 UniqueArray<ptrdiff_t> level(n, 0);
663 UniqueArray<ptrdiff_t> order(n, 0);
664
665 // 1. split rows into levels.
666 ptrdiff_t beg = forward ? 0 : n - 1;
667 ptrdiff_t end = forward ? n : -1;
668 ptrdiff_t inc = forward ? 1 : -1;
669
670 for (ptrdiff_t i = beg; i != end; i += inc) {
671 ptrdiff_t l = level[i];
672
673 for (auto a = backend::row_begin(A, i); a; ++a) {
674 ptrdiff_t c = a.col();
675
676 if (forward) {
677 if (c >= i)
678 continue;
679 }
680 else {
681 if (c <= i)
682 continue;
683 }
684
685 l = std::max(l, level[c] + 1);
686 }
687
688 level[i] = l;
689 nlev = std::max(nlev, l + 1);
690 }
691
692 // 2. reorder matrix rows.
693 UniqueArray<ptrdiff_t> start(nlev + 1, 0);
694
695 for (ptrdiff_t i = 0; i < n; ++i)
696 ++start[level[i] + 1];
697
698 std::partial_sum(start.begin(), start.end(), start.begin());
699
700 for (ptrdiff_t i = 0; i < n; ++i)
701 order[start[level[i]]++] = i;
702
703 std::rotate(start.begin(), start.end() - 1, start.end());
704 start[0] = 0;
705
706 // 3. Organize matrix rows into tasks.
707 // Each level is split into nthreads tasks.
708 UniqueArray<ptrdiff_t> thread_rows(nthreads, 0);
709 UniqueArray<ptrdiff_t> thread_cols(nthreads, 0);
710
711 // TODO: Use a grain size of 1 to use all threads
712 arccoreParallelFor(0, nthreads, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
713 for (ptrdiff_t tid = begin; tid < (begin + size); ++tid) {
714 for (ptrdiff_t lev = 0; lev < nlev; ++lev) {
715 // split each level into tasks.
716 ptrdiff_t lev_size = start[lev + 1] - start[lev];
717 ptrdiff_t chunk_size = (lev_size + nthreads - 1) / nthreads;
718
719 ptrdiff_t beg = std::min(tid * chunk_size, lev_size);
720 ptrdiff_t end = std::min(beg + chunk_size, lev_size);
721
722 beg += start[lev];
723 end += start[lev];
724
725 tasks[tid].push_back(task(beg, end));
726
727 // count rows and nonzeros in the current task
728 thread_rows[tid] += end - beg;
729 for (ptrdiff_t i = beg; i < end; ++i) {
730 ptrdiff_t j = order[i];
731 thread_cols[tid] += backend::row_nonzeros(A, j);
732 }
733 }
734 }
735 });
736
737 // 4. reorganize matrix data for better cache and NUMA locality.
738 arccoreParallelFor(0, nthreads, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
739 for (ptrdiff_t tid = begin; tid < (begin + size); ++tid) {
740 col[tid].reserve(thread_cols[tid]);
741 val[tid].reserve(thread_cols[tid]);
742 ord[tid].reserve(thread_rows[tid]);
743 ptr[tid].reserve(thread_rows[tid] + 1);
744 ptr[tid].push_back(0);
745 for (task& t : tasks[tid]) {
746 ptrdiff_t loc_beg = ptr[tid].size() - 1;
747 ptrdiff_t loc_end = loc_beg;
748
749 for (ptrdiff_t r = t.beg; r < t.end; ++r, ++loc_end) {
750 ptrdiff_t i = order[r];
751
752 ord[tid].push_back(i);
753
754 for (auto a = backend::row_begin(A, i); a; ++a) {
755 col[tid].push_back(a.col());
756 val[tid].push_back(a.value());
757 }
758
759 ptr[tid].push_back(col[tid].size());
760 }
761
762 t.beg = loc_beg;
763 t.end = loc_end;
764 }
765 }
766 });
767 }
768
769 template <class Vector1, class Vector2>
770 void sweep(const Vector1& rhs, Vector2& x) const
771 {
772 // NOTE GG: This part was using OpenMP in the original implementation
773 for (ptrdiff_t tid = 0; tid < nthreads; ++tid) {
774 for (const task& t : tasks[tid]) {
775 for (ptrdiff_t r = t.beg; r < t.end; ++r) {
776 ptrdiff_t i = ord[tid][r];
777 ptrdiff_t beg = ptr[tid][r];
778 ptrdiff_t end = ptr[tid][r + 1];
779
780 value_type D = math::identity<value_type>();
781 rhs_type X;
782 X = rhs[i];
783
784 for (ptrdiff_t j = beg; j < end; ++j) {
785 ptrdiff_t c = col[tid][j];
786 value_type v = val[tid][j];
787
788 if (c == i)
789 D = v;
790 else
791 X -= v * x[c];
792 }
793
794 x[i] = math::inverse(D) * X;
795 }
796
797 // each task corresponds to a level, so we need
798 // to synchronize across threads at this point:
799 // TODO: Thread barrier
800 }
801 }
802 }
803
804 size_t bytes() const
805 {
806 size_t b = 0;
807
808 for (int i = 0; i < nthreads; ++i) {
809 b += sizeof(task) * tasks[i].size();
810 b += backend::bytes(ptr[i]);
811 b += backend::bytes(col[i]);
812 b += backend::bytes(val[i]);
813 b += backend::bytes(ord[i]);
814 }
815
816 return b;
817 }
818 };
819
820 std::shared_ptr<parallel_sweep<true>> forward;
821 std::shared_ptr<parallel_sweep<false>> backward;
822};
823
824/*---------------------------------------------------------------------------*/
825/*---------------------------------------------------------------------------*/
833template <class Backend>
835: public RelaxationBase
836{
837 typedef typename Backend::value_type value_type;
838 typedef typename Backend::col_type col_type;
839 typedef typename Backend::ptr_type ptr_type;
840 typedef typename Backend::vector vector;
841 typedef typename Backend::matrix matrix;
842 typedef typename Backend::matrix_diagonal matrix_diagonal;
843
844 typedef typename math::scalar_of<value_type>::type scalar_type;
845 typedef Impl::ILUSolver<Backend> ilu_solve;
846
848 struct params
849 {
851 scalar_type damping;
852
855
856 params()
857 : damping(1)
858 {}
859
860 params(const PropertyTree& p)
861 : ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, damping)
862 , ARCCORE_ALINA_PARAMS_IMPORT_CHILD(p, solve)
863 {
864 p.check_params({ "damping", "solve" }, { "k" });
865 }
866
867 void get(PropertyTree& p, const std::string& path) const
868 {
869 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, damping);
870 ARCCORE_ALINA_PARAMS_EXPORT_CHILD(p, path, solve);
871 }
872 } prm;
873
875 template <class Matrix>
876 ILU0Relaxation(const Matrix& A, const params& prm, const typename Backend::params& bprm)
877 : prm(prm)
878 {
879 typedef typename BuiltinBackend<value_type, col_type, ptr_type>::matrix build_matrix;
880 const size_t n = backend::nbRow(A);
881
882 size_t Lnz = 0, Unz = 0;
883
884 for (ptrdiff_t i = 0; i < static_cast<ptrdiff_t>(n); ++i) {
885 ptrdiff_t row_beg = A.ptr[i];
886 ptrdiff_t row_end = A.ptr[i + 1];
887
888 for (ptrdiff_t j = row_beg; j < row_end; ++j) {
889 ptrdiff_t c = A.col[j];
890 if (c < i)
891 ++Lnz;
892 else if (c > i)
893 ++Unz;
894 }
895 }
896
897 auto L = std::make_shared<build_matrix>();
898 auto U = std::make_shared<build_matrix>();
899
900 L->set_size(n, n);
901 L->set_nonzeros(Lnz);
902 L->ptr[0] = 0;
903 U->set_size(n, n);
904 U->set_nonzeros(Unz);
905 U->ptr[0] = 0;
906
907 size_t Lhead = 0;
908 size_t Uhead = 0;
909
910 auto D = std::make_shared<numa_vector<value_type>>(n, false);
911
912 UniqueArray<value_type*> work(n, nullptr);
913
914 for (ptrdiff_t i = 0; i < static_cast<ptrdiff_t>(n); ++i) {
915 ptrdiff_t row_beg = A.ptr[i];
916 ptrdiff_t row_end = A.ptr[i + 1];
917
918 for (ptrdiff_t j = row_beg; j < row_end; ++j) {
919 ptrdiff_t c = A.col[j];
920 value_type v = A.val[j];
921
922 if (c < i) {
923 L->col[Lhead] = c;
924 L->val[Lhead] = v;
925 work[c] = L->val + Lhead;
926 ++Lhead;
927 }
928 else if (c == i) {
929 (*D)[i] = v;
930 work[c] = &(*D)[i];
931 }
932 else {
933 U->col[Uhead] = c;
934 U->val[Uhead] = v;
935 work[c] = U->val + Uhead;
936 ++Uhead;
937 }
938 }
939
940 L->ptr[i + 1] = Lhead;
941 U->ptr[i + 1] = Uhead;
942
943 for (ptrdiff_t j = row_beg; j < row_end; ++j) {
944 ptrdiff_t c = A.col[j];
945
946 // Exit if diagonal is reached
947 if (c >= i) {
948 precondition(c == i, "No diagonal value in system matrix");
949 precondition(!math::is_zero((*D)[i]), "Zero pivot in ILU");
950
951 (*D)[i] = math::inverse((*D)[i]);
952 break;
953 }
954
955 // Compute the multiplier for jrow
956 value_type tl = (*work[c]) * (*D)[c];
957 *work[c] = tl;
958
959 // Perform linear combination
960 for (ptrdiff_t k = U->ptr[c]; k < static_cast<ptrdiff_t>(U->ptr[c + 1]); ++k) {
961 value_type* w = work[U->col[k]];
962 if (w)
963 *w -= tl * U->val[k];
964 }
965 }
966
967 // Get rid of zeros in the factors
968 Lhead = L->ptr[i];
969 Uhead = U->ptr[i];
970
971 for (ptrdiff_t j = Lhead, e = L->ptr[i + 1]; j < e; ++j) {
972 auto v = L->val[j];
973 if (!math::is_zero(v)) {
974 L->col[Lhead] = L->col[j];
975 L->val[Lhead] = v;
976 ++Lhead;
977 }
978 }
979
980 for (ptrdiff_t j = Uhead, e = U->ptr[i + 1]; j < e; ++j) {
981 auto v = U->val[j];
982 if (!math::is_zero(v)) {
983 U->col[Uhead] = U->col[j];
984 U->val[Uhead] = v;
985 ++Uhead;
986 }
987 }
988 L->ptr[i + 1] = Lhead;
989 U->ptr[i + 1] = Uhead;
990
991 // Refresh work
992 for (ptrdiff_t j = row_beg; j < row_end; ++j)
993 work[A.col[j]] = nullptr;
994 }
995
996 L->setNbNonZero(Lhead);
997 U->setNbNonZero(Uhead);
998
999 ilu = std::make_shared<ilu_solve>(L, U, D, prm.solve, bprm);
1000 }
1001
1003 template <class Matrix, class VectorRHS, class VectorX, class VectorTMP>
1004 void apply_pre(const Matrix& A, const VectorRHS& rhs, VectorX& x, VectorTMP& tmp) const
1005 {
1006 backend::residual(rhs, A, x, tmp);
1007 ilu->solve(tmp);
1008 backend::axpby(prm.damping, tmp, math::identity<scalar_type>(), x);
1009 }
1010
1012 template <class Matrix, class VectorRHS, class VectorX, class VectorTMP>
1013 void apply_post(const Matrix& A, const VectorRHS& rhs, VectorX& x, VectorTMP& tmp) const
1014 {
1015 backend::residual(rhs, A, x, tmp);
1016 ilu->solve(tmp);
1017 backend::axpby(prm.damping, tmp, math::identity<scalar_type>(), x);
1018 }
1019
1021 template <class Matrix, class VectorRHS, class VectorX>
1022 void apply(const Matrix&, const VectorRHS& rhs, VectorX& x) const
1023 {
1024 backend::copy(rhs, x);
1025 ilu->solve(x);
1026 }
1027
1028 size_t bytes() const
1029 {
1030 return ilu->bytes();
1031 }
1032
1033 private:
1034
1035 std::shared_ptr<ilu_solve> ilu;
1036};
1037
1038/*---------------------------------------------------------------------------*/
1039/*---------------------------------------------------------------------------*/
1043template <class Backend>
1045: public RelaxationBase
1046{
1047 typedef typename Backend::value_type value_type;
1048 typedef typename Backend::col_type col_type;
1049 typedef typename Backend::ptr_type ptr_type;
1050 typedef typename Backend::matrix matrix;
1051 typedef typename Backend::matrix_diagonal matrix_diagonal;
1052 typedef typename Backend::vector vector;
1053
1054 typedef typename math::scalar_of<value_type>::type scalar_type;
1055
1056 typedef Impl::ILUSolver<Backend> ilu_solve;
1057
1059 struct params
1060 {
1062 int k;
1063
1065 scalar_type damping;
1066
1069
1070 params()
1071 : k(1)
1072 , damping(1)
1073 {}
1074
1075 params(const PropertyTree& p)
1076 : ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, k)
1077 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, damping)
1078 , ARCCORE_ALINA_PARAMS_IMPORT_CHILD(p, solve)
1079 {
1080 p.check_params({ "k", "damping", "solve" });
1081 }
1082
1083 void get(PropertyTree& p, const std::string& path) const
1084 {
1085 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, k);
1086 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, damping);
1087 ARCCORE_ALINA_PARAMS_EXPORT_CHILD(p, path, solve);
1088 }
1089 } prm;
1090
1092 template <class Matrix>
1093 ILUKRelaxation(const Matrix& A, const params& prm, const typename Backend::params& bprm)
1094 : prm(prm)
1095 {
1096 typedef typename BuiltinBackend<value_type, col_type, ptr_type>::matrix build_matrix;
1097
1098 const size_t n = backend::nbRow(A);
1099
1100 size_t Anz = backend::nonzeros(A);
1101
1103 Lptr.reserve(n + 1);
1104 Lptr.push_back(0);
1106 Lcol.reserve(Anz / 3);
1108 Lval.reserve(Anz / 3);
1109
1111 Uptr.reserve(n + 1);
1112 Uptr.push_back(0);
1114 Ucol.reserve(Anz / 3);
1116 Uval.reserve(Anz / 3);
1117
1118 UniqueArray<int> Ulev;
1119 Ulev.reserve(Anz / 3);
1120
1121 auto D = std::make_shared<numa_vector<value_type>>(n, false);
1122
1123 sparse_vector w(n, prm.k);
1124
1125 for (ptrdiff_t i = 0; i < static_cast<ptrdiff_t>(n); ++i) {
1126 w.reset(i);
1127
1128 for (auto a = backend::row_begin(A, i); a; ++a) {
1129 w.add(a.col(), a.value(), 0);
1130 }
1131
1132 while (!w.q.empty()) {
1133 nonzero& a = w.next_nonzero();
1134 a.val = a.val * (*D)[a.col];
1135
1136 for (ptrdiff_t j = Uptr[a.col], e = Uptr[a.col + 1]; j < e; ++j) {
1137 int lev = std::max(a.lev, Ulev[j]) + 1;
1138 w.add(Ucol[j], -a.val * Uval[j], lev);
1139 }
1140 }
1141
1142 w.sort();
1143
1144 for (const nonzero& e : w.nz) {
1145 if (e.col < i) {
1146 Lcol.push_back(e.col);
1147 Lval.push_back(e.val);
1148 }
1149 else if (e.col == i) {
1150 (*D)[i] = math::inverse(e.val);
1151 }
1152 else {
1153 Ucol.push_back(e.col);
1154 Uval.push_back(e.val);
1155 Ulev.push_back(e.lev);
1156 }
1157 }
1158
1159 Lptr.push_back(Lcol.size());
1160 Uptr.push_back(Ucol.size());
1161 }
1162
1163 ilu = std::make_shared<ilu_solve>(
1164 std::make_shared<build_matrix>(n, n, Lptr, Lcol, Lval),
1165 std::make_shared<build_matrix>(n, n, Uptr, Ucol, Uval),
1166 D, prm.solve, bprm);
1167 }
1168
1170 template <class Matrix, class VectorRHS, class VectorX, class VectorTMP>
1171 void apply_pre(const Matrix& A, const VectorRHS& rhs, VectorX& x, VectorTMP& tmp) const
1172 {
1173 backend::residual(rhs, A, x, tmp);
1174 ilu->solve(tmp);
1175 backend::axpby(prm.damping, tmp, math::identity<scalar_type>(), x);
1176 }
1177
1179 template <class Matrix, class VectorRHS, class VectorX, class VectorTMP>
1180 void apply_post(const Matrix& A, const VectorRHS& rhs, VectorX& x, VectorTMP& tmp) const
1181 {
1182 backend::residual(rhs, A, x, tmp);
1183 ilu->solve(tmp);
1184 backend::axpby(prm.damping, tmp, math::identity<scalar_type>(), x);
1185 }
1186
1187 template <class Matrix, class VectorRHS, class VectorX>
1188 void apply(const Matrix&, const VectorRHS& rhs, VectorX& x) const
1189 {
1190 backend::copy(rhs, x);
1191 ilu->solve(x);
1192 }
1193
1194 size_t bytes() const
1195 {
1196 return ilu->bytes();
1197 }
1198
1199 private:
1200
1201 std::shared_ptr<ilu_solve> ilu;
1202
1203 struct nonzero
1204 {
1205 ptrdiff_t col;
1206 value_type val;
1207 int lev;
1208
1209 nonzero()
1210 : col(-1)
1211 {}
1212
1213 nonzero(ptrdiff_t col, const value_type& val, int lev)
1214 : col(col)
1215 , val(val)
1216 , lev(lev)
1217 {}
1218
1219 friend bool operator<(const nonzero& a, const nonzero& b)
1220 {
1221 return a.col < b.col;
1222 }
1223 };
1224
1225 struct sparse_vector
1226 {
1227 struct comp_indices
1228 {
1229 const std::deque<nonzero>& nz;
1230
1231 comp_indices(const std::deque<nonzero>& nz)
1232 : nz(nz)
1233 {}
1234
1235 bool operator()(int a, int b) const
1236 {
1237 return nz[a].col > nz[b].col;
1238 }
1239 };
1240
1241 typedef std::priority_queue<int, UniqueArray<int>, comp_indices>
1242 priority_queue;
1243
1244 int lfil;
1245
1246 std::deque<nonzero> nz;
1248 priority_queue q;
1249
1250 ptrdiff_t dia;
1251
1252 sparse_vector(size_t n, int lfil)
1253 : lfil(lfil)
1254 , idx(n, -1)
1255 , q(comp_indices(nz))
1256 , dia(0)
1257 {}
1258
1259 void add(ptrdiff_t col, const value_type& val, int lev)
1260 {
1261 if (idx[col] < 0) {
1262 if (lev <= lfil) {
1263 int p = nz.size();
1264 idx[col] = p;
1265 nz.push_back(nonzero(col, val, lev));
1266 if (col < dia)
1267 q.push(p);
1268 }
1269 }
1270 else {
1271 nonzero& a = nz[idx[col]];
1272 a.val += val;
1273 a.lev = std::min(a.lev, lev);
1274 }
1275 }
1276
1277 typename std::deque<nonzero>::iterator begin()
1278 {
1279 return nz.begin();
1280 }
1281
1282 typename std::deque<nonzero>::iterator end()
1283 {
1284 return nz.end();
1285 }
1286
1287 nonzero& next_nonzero()
1288 {
1289 int p = q.top();
1290 q.pop();
1291 return nz[p];
1292 }
1293
1294 void sort()
1295 {
1296 std::sort(nz.begin(), nz.end());
1297 }
1298
1299 void reset(ptrdiff_t d)
1300 {
1301 for (const nonzero& e : nz)
1302 idx[e.col] = -1;
1303 nz.clear();
1304 dia = d;
1305 }
1306 };
1307};
1308
1309/*---------------------------------------------------------------------------*/
1310/*---------------------------------------------------------------------------*/
1311
1312namespace detail
1313{
1314 template <class Matrix>
1315 std::shared_ptr<Matrix> symb_product(const Matrix& A, const Matrix& B)
1316 {
1317 auto C = std::make_shared<Matrix>();
1318
1319 C->set_size(A.nbRow(), B.ncols);
1320
1321 auto A_ptr = A.ptr.data();
1322 auto A_col = A.col.data();
1323 auto B_ptr = B.ptr.data();
1324 auto B_col = B.col.data();
1325 auto C_ptr = C->ptr.data();
1326 C_ptr[0] = 0;
1327
1328 arccoreParallelFor(0, A.nbRow(), ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1329 UniqueArray<ptrdiff_t> marker(B.ncols, -1);
1330
1331 for (ptrdiff_t ia = begin; ia < (begin + size); ++ia) {
1332 ptrdiff_t C_cols = 0;
1333 for (ptrdiff_t ja = A_ptr[ia], ea = A_ptr[ia + 1]; ja < ea; ++ja) {
1334 ptrdiff_t ca = A_col[ja];
1335
1336 for (ptrdiff_t jb = B_ptr[ca], eb = B_ptr[ca + 1]; jb < eb; ++jb) {
1337 ptrdiff_t cb = B_col[jb];
1338 if (marker[cb] != ia) {
1339 marker[cb] = ia;
1340 ++C_cols;
1341 }
1342 }
1343 }
1344 C_ptr[ia + 1] = C_cols;
1345 }
1346 });
1347
1348 C->set_nonzeros(C->scan_row_sizes(), /*need_values = */ false);
1349 auto C_col = C->col.data();
1350
1351 arccoreParallelFor(0, A.nbRow(), ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1352 UniqueArray<ptrdiff_t> marker(B.ncols, -1);
1353
1354 for (ptrdiff_t ia = begin; ia < (begin + size); ++ia) {
1355 ptrdiff_t row_beg = C_ptr[ia];
1356 ptrdiff_t row_end = row_beg;
1357
1358 for (ptrdiff_t ja = A_ptr[ia], ea = A_ptr[ia + 1]; ja < ea; ++ja) {
1359 ptrdiff_t ca = A_col[ja];
1360
1361 for (ptrdiff_t jb = B_ptr[ca], eb = B_ptr[ca + 1]; jb < eb; ++jb) {
1362 ptrdiff_t cb = B_col[jb];
1363
1364 if (marker[cb] < row_beg) {
1365 marker[cb] = row_end;
1366 C_col[row_end] = cb;
1367 ++row_end;
1368 }
1369 }
1370 }
1371
1372 std::sort(C_col + row_beg, C_col + row_end);
1373 }
1374 });
1375
1376 return C;
1377 }
1378
1379} // namespace detail
1380
1381/*---------------------------------------------------------------------------*/
1382/*---------------------------------------------------------------------------*/
1386template <class Backend>
1388: public RelaxationBase
1389{
1390 typedef typename Backend::value_type value_type;
1391
1392 typedef ILU0Relaxation<Backend> Base;
1393
1395 struct params : Base::params
1396 {
1397 typedef typename Base::params BasePrm;
1398
1400 int k;
1401
1402 params()
1403 : k(1)
1404 {}
1405
1406 params(const PropertyTree& p)
1407 : BasePrm(p)
1408 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, k)
1409 {
1410 p.check_params( { "k", "damping", "solve" });
1411 }
1412
1413 void get(PropertyTree& p, const std::string& path) const
1414 {
1415 BasePrm::get(p, path);
1416 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, k);
1417 }
1418 } prm;
1419
1421 template <class Matrix>
1422 ILUPRelaxation(const Matrix& A, const params& prm, const typename Backend::params& bprm)
1423 : prm(prm)
1424 {
1425 if (prm.k == 0) {
1426 base = std::make_shared<Base>(A, prm, bprm);
1427 }
1428 else {
1429 auto P = detail::symb_product(A, A);
1430 for (int k = 1; k < prm.k; ++k) {
1431 P = detail::symb_product(*P, A);
1432 }
1433
1434 ptrdiff_t n = backend::nbRow(A);
1435 P->val.resize(P->nbNonZero());
1436
1437 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1438 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1439 ptrdiff_t p_beg = P->ptr[i];
1440 ptrdiff_t p_end = P->ptr[i + 1];
1441 ptrdiff_t a_beg = A.ptr[i];
1442 ptrdiff_t a_end = A.ptr[i + 1];
1443
1444 std::fill(P->val + p_beg, P->val + p_end, math::zero<value_type>());
1445
1446 for (ptrdiff_t ja = a_beg, ea = a_end, jp = p_beg, ep = p_end; ja < ea; ++ja) {
1447 ptrdiff_t ca = A.col[ja];
1448 while (jp < ep && P->col[jp] < ca)
1449 ++jp;
1450 if (P->col[jp] == ca)
1451 P->val[jp] = A.val[ja];
1452 }
1453 }
1454 });
1455
1456 base = std::make_shared<Base>(*P, prm, bprm);
1457 }
1458 }
1459
1461 template <class Matrix, class VectorRHS, class VectorX, class VectorTMP>
1462 void apply_pre(const Matrix& A, const VectorRHS& rhs, VectorX& x, VectorTMP& tmp) const
1463 {
1464 base->apply_pre(A, rhs, x, tmp);
1465 }
1466
1468 template <class Matrix, class VectorRHS, class VectorX, class VectorTMP>
1469 void apply_post(const Matrix& A, const VectorRHS& rhs, VectorX& x, VectorTMP& tmp) const
1470 {
1471 base->apply_post(A, rhs, x, tmp);
1472 }
1473
1474 template <class Matrix, class VectorRHS, class VectorX>
1475 void apply(const Matrix& A, const VectorRHS& rhs, VectorX& x) const
1476 {
1477 base->apply(A, rhs, x);
1478 }
1479
1480 size_t bytes() const
1481 {
1482 return base->bytes();
1483 }
1484
1485 private:
1486
1487 std::shared_ptr<Base> base;
1488};
1489
1490/*---------------------------------------------------------------------------*/
1491/*---------------------------------------------------------------------------*/
1500template <class Backend>
1502: public RelaxationBase
1503{
1504 typedef typename Backend::value_type value_type;
1505 typedef typename Backend::col_type col_type;
1506 typedef typename Backend::ptr_type ptr_type;
1507 typedef typename Backend::matrix matrix;
1508 typedef typename Backend::matrix_diagonal matrix_diagonal;
1509 typedef typename Backend::vector vector;
1510
1511 typedef typename math::scalar_of<value_type>::type scalar_type;
1512
1513 typedef Impl::ILUSolver<Backend> ilu_solve;
1514
1516 struct params
1517 {
1519 double p = 2.0;
1520
1522 double tau = 1.0e-2;
1523
1525 double damping = 1.0;
1526
1529
1530 params() = default;
1531
1532 params(const PropertyTree& p)
1533 : ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, p)
1534 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, tau)
1535 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, damping)
1536 , ARCCORE_ALINA_PARAMS_IMPORT_CHILD(p, solve)
1537 {
1538 p.check_params( { "p", "tau", "damping", "solve" });
1539 }
1540
1541 void get(PropertyTree& p, const std::string& path) const
1542 {
1543 double p2 = this->p;
1544 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, p2);
1545 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, tau);
1546 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, damping);
1547 ARCCORE_ALINA_PARAMS_EXPORT_CHILD(p, path, solve);
1548 }
1549 } prm;
1550
1552 template <class Matrix>
1553 ILUTRelaxation(const Matrix& A, const params& prm, const typename Backend::params& bprm)
1554 : prm(prm)
1555 {
1556 const size_t n = backend::nbRow(A);
1557
1558 size_t Lnz = 0, Unz = 0;
1559
1560 for (ptrdiff_t i = 0; i < static_cast<ptrdiff_t>(n); ++i) {
1561 ptrdiff_t row_beg = A.ptr[i];
1562 ptrdiff_t row_end = A.ptr[i + 1];
1563
1564 int lenL = 0, lenU = 0;
1565 for (ptrdiff_t j = row_beg; j < row_end; ++j) {
1566 ptrdiff_t c = A.col[j];
1567 if (c < i)
1568 ++lenL;
1569 else if (c > i)
1570 ++lenU;
1571 }
1572
1573 Lnz += static_cast<size_t>(lenL * prm.p);
1574 Unz += static_cast<size_t>(lenU * prm.p);
1575 }
1576
1577 auto L = std::make_shared<build_matrix>();
1578 auto U = std::make_shared<build_matrix>();
1579
1580 L->set_size(n, n);
1581 L->set_nonzeros(Lnz);
1582 L->ptr[0] = 0;
1583 U->set_size(n, n);
1584 U->set_nonzeros(Unz);
1585 U->ptr[0] = 0;
1586
1587 auto D = std::make_shared<numa_vector<value_type>>(n, false);
1588
1589 sparse_vector w(n);
1590
1591 for (ptrdiff_t i = 0, Lhead = 0, Uhead = 0; i < static_cast<ptrdiff_t>(n); ++i) {
1592 w.dia = i;
1593
1594 int lenL = 0;
1595 int lenU = 0;
1596
1597 scalar_type tol = math::zero<scalar_type>();
1598
1599 for (auto a = backend::row_begin(A, i); a; ++a) {
1600 w[a.col()] = a.value();
1601 tol += math::norm(a.value());
1602
1603 if (a.col() < i)
1604 ++lenL;
1605 if (a.col() > i)
1606 ++lenU;
1607 }
1608 tol *= prm.tau / (lenL + lenU);
1609
1610 while (!w.q.empty()) {
1611 ptrdiff_t k = w.next_nonzero();
1612 w[k] = w[k] * (*D)[k];
1613 value_type wk = w[k];
1614
1615 if (math::norm(wk) > tol) {
1616 for (ptrdiff_t j = U->ptr[k]; j < static_cast<ptrdiff_t>(U->ptr[k + 1]); ++j)
1617 w[U->col[j]] -= wk * U->val[j];
1618 }
1619 }
1620
1621 w.move_to(
1622 static_cast<int>(lenL * prm.p),
1623 static_cast<int>(lenU * prm.p),
1624 tol, Lhead, *L, Uhead, *U, *D);
1625
1626 L->ptr[i + 1] = Lhead;
1627 U->ptr[i + 1] = Uhead;
1628 }
1629
1630 L->setNbNonZero(L->ptr[n]);
1631 U->setNbNonZero(U->ptr[n]);
1632
1633 ilu = std::make_shared<ilu_solve>(L, U, D, prm.solve, bprm);
1634 }
1635
1637 template <class Matrix, class VectorRHS, class VectorX, class VectorTMP>
1638 void apply_pre(const Matrix& A, const VectorRHS& rhs, VectorX& x, VectorTMP& tmp) const
1639 {
1640 backend::residual(rhs, A, x, tmp);
1641 ilu->solve(tmp);
1642 backend::axpby(prm.damping, tmp, math::identity<scalar_type>(), x);
1643 }
1644
1646 template <class Matrix, class VectorRHS, class VectorX, class VectorTMP>
1647 void apply_post(const Matrix& A, const VectorRHS& rhs, VectorX& x, VectorTMP& tmp) const
1648 {
1649 backend::residual(rhs, A, x, tmp);
1650 ilu->solve(tmp);
1651 backend::axpby(prm.damping, tmp, math::identity<scalar_type>(), x);
1652 }
1653
1654 template <class Matrix, class VectorRHS, class VectorX>
1655 void apply(const Matrix&, const VectorRHS& rhs, VectorX& x) const
1656 {
1657 backend::copy(rhs, x);
1658 ilu->solve(x);
1659 }
1660
1661 size_t bytes() const
1662 {
1663 return ilu->bytes();
1664 }
1665
1666 private:
1667
1668 typedef typename BuiltinBackend<value_type, col_type, ptr_type>::matrix build_matrix;
1669 std::shared_ptr<ilu_solve> ilu;
1670
1671 struct sparse_vector
1672 {
1673 struct nonzero
1674 {
1675 ptrdiff_t col;
1676 value_type val;
1677
1678 nonzero()
1679 : col(-1)
1680 {}
1681
1682 nonzero(ptrdiff_t col, const value_type& val = math::zero<value_type>())
1683 : col(col)
1684 , val(val)
1685 {}
1686 };
1687
1688 struct comp_indices
1689 {
1690 const UniqueArray<nonzero>& nz;
1691
1692 comp_indices(const UniqueArray<nonzero>& nz)
1693 : nz(nz)
1694 {}
1695
1696 bool operator()(int a, int b) const
1697 {
1698 return nz[a].col > nz[b].col;
1699 }
1700 };
1701
1702 typedef std::priority_queue<int, UniqueArray<int>, comp_indices> priority_queue;
1703
1706 priority_queue q;
1707
1708 ptrdiff_t dia;
1709
1710 sparse_vector(size_t n)
1711 : idx(n, -1)
1712 , q(comp_indices(nz))
1713 , dia(0)
1714 {
1715 nz.reserve(16);
1716 }
1717
1718 value_type operator[](ptrdiff_t i) const
1719 {
1720 if (idx[i] >= 0)
1721 return nz[idx[i]].val;
1722 return math::zero<value_type>();
1723 }
1724
1725 value_type& operator[](ptrdiff_t i)
1726 {
1727 if (idx[i] == -1) {
1728 int p = nz.size();
1729 idx[i] = p;
1730 nz.push_back(nonzero(i));
1731 if (i < dia)
1732 q.push(p);
1733 }
1734 return nz[idx[i]].val;
1735 }
1736
1737 typename UniqueArray<nonzero>::iterator begin()
1738 {
1739 return nz.begin();
1740 }
1741
1742 typename UniqueArray<nonzero>::iterator end()
1743 {
1744 return nz.end();
1745 }
1746
1747 ptrdiff_t next_nonzero()
1748 {
1749 int p = q.top();
1750 q.pop();
1751 return nz[p].col;
1752 }
1753
1754 struct higher_than
1755 {
1756 scalar_type tol;
1757 ptrdiff_t dia;
1758
1759 higher_than(scalar_type tol, ptrdiff_t dia)
1760 : tol(tol)
1761 , dia(dia)
1762 {}
1763
1764 bool operator()(const nonzero& v) const
1765 {
1766 return v.col == dia || math::norm(v.val) > tol;
1767 }
1768 };
1769
1770 struct L_first
1771 {
1772 ptrdiff_t dia;
1773
1774 L_first(ptrdiff_t dia)
1775 : dia(dia)
1776 {}
1777
1778 bool operator()(const nonzero& v) const
1779 {
1780 return v.col < dia;
1781 }
1782 };
1783
1784 struct by_abs_val
1785 {
1786 ptrdiff_t dia;
1787
1788 by_abs_val(ptrdiff_t dia)
1789 : dia(dia)
1790 {}
1791
1792 bool operator()(const nonzero& a, const nonzero& b) const
1793 {
1794 if (a.col == dia)
1795 return true;
1796 if (b.col == dia)
1797 return false;
1798
1799 return math::norm(a.val) > math::norm(b.val);
1800 }
1801 };
1802
1803 struct by_col
1804 {
1805 bool operator()(const nonzero& a, const nonzero& b) const
1806 {
1807 return a.col < b.col;
1808 }
1809 };
1810
1811 void move_to(int lp, int up, scalar_type tol,
1812 ptrdiff_t& Lhead, build_matrix& L,
1813 ptrdiff_t& Uhead, build_matrix& U,
1815 {
1816 typedef typename UniqueArray<nonzero>::iterator ptr;
1817
1818 ptr b = nz.begin();
1819 ptr e = nz.end();
1820
1821 // Move zeros to back:
1822 e = std::partition(b, e, higher_than(tol, dia));
1823
1824 // Split L and U:
1825 ptr m = std::partition(b, e, L_first(dia));
1826
1827 // Get largest p elements in L and U.
1828 ptr lend = std::min(b + lp, m);
1829 ptr uend = std::min(m + up, e);
1830
1831 if (lend != m)
1832 std::nth_element(b, lend, m, by_abs_val(dia));
1833 if (uend != e)
1834 std::nth_element(m, uend, e, by_abs_val(dia));
1835
1836 // Sort entries by column number
1837 std::sort(b, lend, by_col());
1838 std::sort(m, uend, by_col());
1839
1840 // copy L to the output matrix.
1841 for (ptr a = b; a != lend; ++a) {
1842 L.col[Lhead] = a->col;
1843 L.val[Lhead] = a->val;
1844
1845 ++Lhead;
1846 }
1847
1848 // Store inverted diagonal.
1849 D[dia] = math::inverse(m->val);
1850
1851 if (m != uend) {
1852 ++m;
1853
1854 // copy U to the output matrix.
1855 for (ptr a = m; a != uend; ++a) {
1856 U.col[Uhead] = a->col;
1857 U.val[Uhead] = a->val;
1858
1859 ++Uhead;
1860 }
1861 }
1862
1863 for (const nonzero& e : nz)
1864 idx[e.col] = -1;
1865 nz.clear();
1866 }
1867 };
1868};
1869
1870/*---------------------------------------------------------------------------*/
1871/*---------------------------------------------------------------------------*/
1881template <class Backend>
1883: public RelaxationBase
1884{
1885 typedef typename Backend::value_type value_type;
1886 typedef typename Backend::matrix_diagonal matrix_diagonal;
1887
1888 typedef typename math::scalar_of<value_type>::type scalar_type;
1891
1893 template <class Matrix>
1894 SPAI0Relaxation(const Matrix& A, const params&, const typename Backend::params& backend_prm)
1895 {
1896 const size_t n = backend::nbRow(A);
1897
1898 auto m = std::make_shared<numa_vector<value_type>>(n, false);
1899
1900 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1901 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1902 value_type num = math::zero<value_type>();
1903 scalar_type den = math::zero<scalar_type>();
1904
1905 for (auto a = backend::row_begin(A, i); a; ++a) {
1906 value_type v = a.value();
1907 scalar_type norm_v = math::norm(v);
1908 den += norm_v * norm_v;
1909 if (a.col() == i)
1910 num += v;
1911 }
1912
1913 (*m)[i] = math::inverse(den) * num;
1914 }
1915 });
1916
1917 M = Backend::copy_vector(m, backend_prm);
1918 }
1919
1921 template <class Matrix, class VectorRHS, class VectorX, class VectorTMP>
1922 void apply_pre(const Matrix& A, const VectorRHS& rhs, VectorX& x, VectorTMP& tmp) const
1923 {
1924 static const scalar_type one = math::identity<scalar_type>();
1925 backend::residual(rhs, A, x, tmp);
1926 backend::vmul(one, *M, tmp, one, x);
1927 }
1928
1930 template <class Matrix, class VectorRHS, class VectorX, class VectorTMP>
1931 void apply_post(const Matrix& A, const VectorRHS& rhs, VectorX& x, VectorTMP& tmp) const
1932 {
1933 static const scalar_type one = math::identity<scalar_type>();
1934 backend::residual(rhs, A, x, tmp);
1935 backend::vmul(one, *M, tmp, one, x);
1936 }
1937
1938 template <class Matrix, class VectorRHS, class VectorX>
1939 void apply(const Matrix&, const VectorRHS& rhs, VectorX& x) const
1940 {
1941 backend::vmul(math::identity<scalar_type>(), *M, rhs, math::zero<scalar_type>(), x);
1942 }
1943
1944 size_t bytes() const
1945 {
1946 return backend::bytes(*M);
1947 }
1948
1949 std::shared_ptr<matrix_diagonal> M;
1950};
1951
1952/*---------------------------------------------------------------------------*/
1953/*---------------------------------------------------------------------------*/
1963template <class Backend>
1965: public RelaxationBase
1966{
1967 typedef typename Backend::value_type value_type;
1968 typedef typename Backend::vector vector;
1969
1970 typedef typename math::scalar_of<value_type>::type scalar_type;
1971
1974
1976 template <class Matrix>
1977 SPAI1Relaxation(const Matrix& A, const params&, const typename Backend::params& backend_prm)
1978 {
1979 typedef typename backend::value_type<Matrix>::type value_type;
1980
1981 const size_t n = backend::nbRow(A);
1982 const size_t m = backend::nbColumn(A);
1983
1984 auto Ainv = std::make_shared<Matrix>(A);
1985
1986 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1987 UniqueArray<ptrdiff_t> marker(m, -1);
1991
1992 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1993 ptrdiff_t row_beg = A.ptr[i];
1994 ptrdiff_t row_end = A.ptr[i + 1];
1995
1996 I.assign(A.col + row_beg, A.col + row_end);
1997
1998 J.clear();
1999 for (ptrdiff_t j = row_beg; j < row_end; ++j) {
2000 ptrdiff_t c = A.col[j];
2001
2002 for (ptrdiff_t jj = A.ptr[c], ee = A.ptr[c + 1]; jj < ee; ++jj) {
2003 ptrdiff_t cc = A.col[jj];
2004 if (marker[cc] < 0) {
2005 marker[cc] = 1;
2006 J.push_back(cc);
2007 }
2008 }
2009 }
2010 std::sort(J.begin(), J.end());
2011 B.assign(I.size() * J.size(), math::zero<value_type>());
2012 ek.assign(J.size(), math::zero<value_type>());
2013 for (size_t j = 0; j < J.size(); ++j) {
2014 marker[J[j]] = j;
2015 if (J[j] == static_cast<ptrdiff_t>(i))
2016 ek[j] = math::identity<value_type>();
2017 }
2018
2019 for (ptrdiff_t j = row_beg; j < row_end; ++j) {
2020 ptrdiff_t c = A.col[j];
2021
2022 for (auto a = backend::row_begin(A, c); a; ++a)
2023 B[marker[a.col()] + J.size() * (j - row_beg)] = a.value();
2024 }
2025
2026 qr.solve(J.size(), I.size(), &B[0], &ek[0], &Ainv->val[row_beg],
2027 Alina::detail::col_major);
2028
2029 for (size_t j = 0; j < J.size(); ++j)
2030 marker[J[j]] = -1;
2031 }
2032 });
2033
2034 M = Backend::copy_matrix(Ainv, backend_prm);
2035 }
2036
2038 template <class Matrix, class VectorRHS, class VectorX, class VectorTMP>
2039 void apply_pre(const Matrix& A, const VectorRHS& rhs, VectorX& x, VectorTMP& tmp) const
2040 {
2041 backend::residual(rhs, A, x, tmp);
2042 backend::spmv(math::identity<scalar_type>(), *M, tmp, math::identity<scalar_type>(), x);
2043 }
2044
2046 template <class Matrix, class VectorRHS, class VectorX, class VectorTMP>
2047 void apply_post(const Matrix& A, const VectorRHS& rhs, VectorX& x, VectorTMP& tmp) const
2048 {
2049 backend::residual(rhs, A, x, tmp);
2050 backend::spmv(math::identity<scalar_type>(), *M, tmp, math::identity<scalar_type>(), x);
2051 }
2052
2053 template <class Matrix, class VectorRHS, class VectorX>
2054 void apply(const Matrix&, const VectorRHS& rhs, VectorX& x) const
2055 {
2056 backend::spmv(math::identity<scalar_type>(), *M, rhs, math::zero<scalar_type>(), x);
2057 }
2058
2059 size_t bytes() const
2060 {
2061 return backend::bytes(*M);
2062 }
2063
2064 std::shared_ptr<typename Backend::matrix> M;
2065};
2066
2067/*---------------------------------------------------------------------------*/
2068/*---------------------------------------------------------------------------*/
2069
2070} // namespace Arcane::Alina
2071
2072/*---------------------------------------------------------------------------*/
2073/*---------------------------------------------------------------------------*/
2074
2075namespace Arcane::Alina::backend
2076{
2077
2078template <class Backend>
2080 typename std::enable_if<(Alina::math::static_rows<typename Backend::value_type>::value > 1)>::type> : std::false_type
2081{};
2082
2083template <class Backend>
2085 typename std::enable_if<
2086 !Backend::provides_row_iterator::value>::type> : std::false_type
2087{};
2088
2089} // namespace Arcane::Alina::backend
2090
2091/*---------------------------------------------------------------------------*/
2092/*---------------------------------------------------------------------------*/
2093
2094#endif
2095
2096/*---------------------------------------------------------------------------*/
2097/*---------------------------------------------------------------------------*/
Integer size() const
Number of elements in the vector.
ArrayIterator< pointer > iterator
Type of the iterator over an array element.
size_t bytes() const
Memory used in bytes.
Definition Relaxation.h:352
ChebyshevRelaxation(const Matrix &A, const params &prm, const typename Backend::params &backend_prm)
Constructs smoother for the system matrix.
Definition Relaxation.h:305
Solver for sparse triangular systems obtained as a result of an incomplete LU factorization.
Class to store parameters as a hierarchical key/value tree.
Definition AlinaUtils.h:113
Base class for solvers.
Class to handle empty parameter list.
Definition AlinaUtils.h:94
NUMA-aware vector container.
Definition NumaVector.h:42
void clear()
Removes the elements from the array.
iterator begin()
Iterator over the first element of the array.
void reserve(Int64 new_capacity)
Reserves memory for new_capacity elements.
void push_back(ConstReferenceType val)
Adds the element val to the end of the array.
void assign(InputIterator first, InputIterator last)
Replaces the contents of the instance with copies of those in the range [first, \alast).
iterator end()
Iterator over the first element after the end of the array.
Basic information for multi-threading management.
Loop execution information.
Matrix class, to be used by user.
1D data vector with value semantics (STL style).
void arccoreParallelFor(const ComplexForLoopRanges< RankValue, IndexType_ > &loop_ranges, const ForLoopRunInfo &run_info, const LambdaType &lambda_function, const ReducerArgs &... reducer_args)
Applies the lambda function lambda_function concurrently over the iteration interval given by loop_ra...
Definition ParallelFor.h:86
std::int32_t Int32
Signed integer type of 32 bits.
Sparse matrix stored in CSR (Compressed Sparse Row) format.
Definition CSRMatrix.h:98
double lower
Lowest-to-highest eigen value ratio.
Definition Relaxation.h:271
double higher
highest eigen value safety upscaling.
Definition Relaxation.h:268
Int32 degree
Chebyshev polynomial degree.
Definition Relaxation.h:260
void apply_post(const Matrix &A, const VectorRHS &rhs, VectorX &x, VectorTMP &tmp) const
Apply post-relaxation.
Definition Relaxation.h:479
DampedJacobiRelaxation(const Matrix &A, const params &prm, const typename Backend::params &backend_prm)
Constructs smoother for the system matrix.
Definition Relaxation.h:448
void apply_pre(const Matrix &A, const VectorRHS &rhs, VectorX &x, VectorTMP &tmp) const
Apply pre-relaxation.
Definition Relaxation.h:464
size_t bytes() const
Memory used in bytes.
Definition Relaxation.h:491
bool serial
Use serial version of the algorithm.
Definition Relaxation.h:517
Gauss-Seidel relaxation.
Definition Relaxation.h:512
void apply_pre(const Matrix &A, const VectorRHS &rhs, VectorX &x, VectorTMP &) const
Apply pre-relaxation.
Definition Relaxation.h:550
size_t bytes() const
Memory used in bytes.
Definition Relaxation.h:582
GaussSeidelRelaxation(const Matrix &A, const params &prm, const typename Backend::params &)
Constructs smoother for the system matrix.
Definition Relaxation.h:539
void apply_post(const Matrix &A, const VectorRHS &rhs, VectorX &x, VectorTMP &) const
Apply post-relaxation.
Definition Relaxation.h:560
ilu_solve::params solve
Parameters for sparse triangular system solver.
Definition Relaxation.h:854
scalar_type damping
Damping factor.
Definition Relaxation.h:851
void apply_pre(const Matrix &A, const VectorRHS &rhs, VectorX &x, VectorTMP &tmp) const
Apply pre-relaxation.
void apply(const Matrix &, const VectorRHS &rhs, VectorX &x) const
Apply post-relaxation.
void apply_post(const Matrix &A, const VectorRHS &rhs, VectorX &x, VectorTMP &tmp) const
Apply post-relaxation.
ILU0Relaxation(const Matrix &A, const params &prm, const typename Backend::params &bprm)
Constructs smoother for the system matrix.
Definition Relaxation.h:876
size_t bytes() const
Memory used in bytes.
ilu_solve::params solve
Parameters for sparse triangular system solver.
scalar_type damping
Damping factor.
size_t bytes() const
Memory used in bytes.
void apply_post(const Matrix &A, const VectorRHS &rhs, VectorX &x, VectorTMP &tmp) const
Apply post-relaxation.
void apply_pre(const Matrix &A, const VectorRHS &rhs, VectorX &x, VectorTMP &tmp) const
Apply pre-relaxation.
ILUKRelaxation(const Matrix &A, const params &prm, const typename Backend::params &bprm)
Constructs smoother for the system matrix.
void apply_pre(const Matrix &A, const VectorRHS &rhs, VectorX &x, VectorTMP &tmp) const
Apply pre-relaxation.
ILUPRelaxation(const Matrix &A, const params &prm, const typename Backend::params &bprm)
Constructs smoother for the system matrix.
void apply_post(const Matrix &A, const VectorRHS &rhs, VectorX &x, VectorTMP &tmp) const
Apply post-relaxation.
size_t bytes() const
Memory used in bytes.
double tau
Minimum magnitude of non-zero elements relative to the current row norm.
ilu_solve::params solve
Parameters for sparse triangular system solver.
ILUTRelaxation(const Matrix &A, const params &prm, const typename Backend::params &bprm)
Constructs smoother for the system matrix.
void apply_pre(const Matrix &A, const VectorRHS &rhs, VectorX &x, VectorTMP &tmp) const
Apply pre-relaxation.
void apply_post(const Matrix &A, const VectorRHS &rhs, VectorX &x, VectorTMP &tmp) const
Apply post-relaxation.
size_t bytes() const
Memory used in bytes.
Converts input matrix to block format before constructing an AMG smoother.
Definition Relaxation.h:53
Alina::detail::empty_params params
Relaxation parameters.
size_t bytes() const
Memory used in bytes.
void apply_pre(const Matrix &A, const VectorRHS &rhs, VectorX &x, VectorTMP &tmp) const
Apply pre-relaxation.
SPAI0Relaxation(const Matrix &A, const params &, const typename Backend::params &backend_prm)
Constructs smoother for the system matrix.
void apply_post(const Matrix &A, const VectorRHS &rhs, VectorX &x, VectorTMP &tmp) const
Apply post-relaxation.
Sparse approximate interface smoother.
Alina::detail::empty_params params
Relaxation parameters.
SPAI1Relaxation(const Matrix &A, const params &, const typename Backend::params &backend_prm)
Constructs smoother for the system matrix.
size_t bytes() const
Memory used in bytes.
void apply_post(const Matrix &A, const VectorRHS &rhs, VectorX &x, VectorTMP &tmp) const
Apply post-relaxation.
void apply_pre(const Matrix &A, const VectorRHS &rhs, VectorX &x, VectorTMP &tmp) const
Apply pre-relaxation.
Is the relaxation supported by the backend?
Number of rows for statically sized matrix types.