Arcane  v4.1.10.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
SkylineLUSolver.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/* SkylineLUSolver.h (C) 2000-2026 */
9/* */
10/* Direct solver that uses Skyline LU factorization. */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCCORE_ALINA_SKYLINELUSOLVER_H
13#define ARCCORE_ALINA_SKYLINELUSOLVER_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/*
26The code is adopted from Kratos project http://www.cimne.com/kratos. The
27original code came with the following copyright notice:
28\verbatim
29Kratos Multi-Physics
30
31Copyright (c) 2012, Pooyan Dadvand, Riccardo Rossi,
32CIMNE (International Center for Numerical Methods in Engineering)
33All rights reserved.
34
35Redistribution and use in source and binary forms, with or without
36modification, are permitted provided that the following conditions are met:
37
38 Redistributions of source code must retain the above copyright notice, this
39 list of conditions and the following disclaimer.
40 Redistributions in binary form must reproduce the above copyright notice,
41 this list of conditions and the following disclaimer in the documentation
42 and/or other materials provided with the distribution.
43 All advertising materials mentioning features or use of this software must
44 display the following acknowledgement:
45 This product includes Kratos Multi-Physics technology.
46 Neither the name of the CIMNE nor the names of its contributors may be used
47 to endorse or promote products derived from this software without specific
48 prior written permission.
49
50THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY EXPRESS OR
51IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
52MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
53EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
54INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
55LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
56PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED ANDON ANY THEORY OF
57LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING NEGLIGENCE
58OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THISSOFTWARE, EVEN IF
59ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
60\endverbatim
61*/
62
63/*---------------------------------------------------------------------------*/
64/*---------------------------------------------------------------------------*/
65
66#include <vector>
67#include <algorithm>
68
69#include "arccore/alina/ValueTypeInterface.h"
70#include "arccore/alina/CuthillMcKeeReorderer.h"
71#include "arccore/alina/AlinaUtils.h"
72
73/*---------------------------------------------------------------------------*/
74/*---------------------------------------------------------------------------*/
75
76namespace Arcane::Alina::solver
77{
78
79/*---------------------------------------------------------------------------*/
80/*---------------------------------------------------------------------------*/
84template <typename ValueType, class ordering = CuthillMcKeeReorderer<false>>
85class SkylineLUSolver
86{
87 public:
88
89 typedef ValueType value_type;
90 typedef typename math::scalar_of<value_type>::type scalar_type;
91 typedef typename math::rhs_of<value_type>::type rhs_type;
92
93 typedef Alina::detail::empty_params params;
94
95 static size_t coarse_enough()
96 {
98 }
99
100 template <class Matrix>
101 SkylineLUSolver(const Matrix& A, const params& = params())
102 : n(backend::nbRow(A))
103 , perm(n)
104 , ptr(n + 1, 0)
105 , D(n, math::zero<value_type>())
106 , y(n)
107 {
108 // Find the permutation for the ordering.
109 ordering::get(A, perm);
110
111 // Get inverse permutation
112 std::vector<int> invperm(n);
113 for (int i = 0; i < n; ++i)
114 invperm[perm[i]] = i;
115
116 /* Let us find how large the rows of L and the columns of U should
117 * be. Provisionally, we will store in ptr[i] the minimum required
118 * height of column i over the diagonal, and length of row i below
119 * the diagonal. The value(i,j) in the reordered matrix will be
120 * the same as the value(perm[i],perm[j]) in the original matrix;
121 * or, the value(i,j) in the original matrix will be the same as
122 * value(invperm[i],invperm[j]) in the reordered matrix.
123 */
124
125 // Traverse the matrix finding nonzero elements
126 for (int i = 0; i < n; ++i) {
127 for (auto a = backend::row_begin(A, i); a; ++a) {
128 int j = a.col();
129 value_type v = a.value();
130
131 int newi = invperm[i];
132 int newj = invperm[j];
133
134 if (!math::is_zero(v)) {
135 if (newi > newj) {
136 // row newi needs length at least newi - newj
137 if (ptr[newi] < newi - newj)
138 ptr[newi] = newi - newj;
139 }
140 else if (newi < newj) {
141 // column newj needs height at least newj - newi
142 if (ptr[newj] < newj - newi)
143 ptr[newj] = newj - newi;
144 }
145 }
146 }
147 }
148
149 // Transform ptr so that it doesn't contain the required lengths
150 // and heights, but the indexes to the entries
151 {
152 int last = 0;
153 for (int i = 1; i <= n; ++i) {
154 int tmp = ptr[i];
155 ptr[i] = ptr[i - 1] + last;
156 last = tmp;
157 }
158 }
159
160 // Allocate variables for skyline format entries
161 L.resize(ptr.back(), math::zero<value_type>());
162 U.resize(ptr.back(), math::zero<value_type>());
163
164 // And finally traverse again the CSR matrix, copying its entries
165 // into the correct places in the skyline format
166 for (int i = 0; i < n; ++i) {
167 for (auto a = backend::row_begin(A, i); a; ++a) {
168 int j = a.col();
169 value_type v = a.value();
170
171 int newi = invperm[i];
172 int newj = invperm[j];
173
174 if (!math::is_zero(v)) {
175 if (newi < newj) {
176 U[ptr[newj + 1] + newi - newj] = v;
177 }
178 else if (newi == newj) {
179 D[newi] = v;
180 }
181 else /* newi > newj */ {
182 L[ptr[newi + 1] + newj - newi] = v;
183 }
184 }
185 }
186 }
187
188 factorize();
189 }
190
191 template <class Vec1, class Vec2>
192 void operator()(const Vec1& rhs, Vec2& x) const
193 {
194 // y = L^-1 * perm[rhs] ;
195 // y = U^-1 * y ;
196 // x = invperm[y];
197
198 for (int i = 0; i < n; ++i) {
199 rhs_type sum;
200 sum = rhs[perm[i]];
201 for (int k = ptr[i], j = i - ptr[i + 1] + k; k < ptr[i + 1]; ++k, ++j)
202 sum -= L[k] * y[j];
203
204 y[i] = D[i] * sum;
205 }
206
207 for (int j = n - 1; j >= 0; --j) {
208 for (int k = ptr[j], i = j - ptr[j + 1] + k; k < ptr[j + 1]; ++k, ++i)
209 y[i] -= U[k] * y[j];
210 }
211
212 for (int i = 0; i < n; ++i)
213 x[perm[i]] = y[i];
214 }
215
216 size_t bytes() const
217 {
218 return backend::bytes(perm) +
219 backend::bytes(ptr) +
220 backend::bytes(L) +
221 backend::bytes(U) +
222 backend::bytes(D);
223 }
224
225 private:
226
227 int n;
228 std::vector<int> perm;
229 std::vector<int> ptr;
230 std::vector<value_type> L;
231 std::vector<value_type> U;
232 std::vector<value_type> D;
233
234 mutable std::vector<rhs_type> y;
235
236 /*
237 * Perform and in-place LU factorization of a skyline matrix by Crout's
238 * algorithm. The diagonal of U contains the 1's.
239 * The equivalent MATLAB code for a full matrix would be:
240 * for k=1:n-1
241 * A(1,k+1)=A(1,k+1)/A(1,1);
242 * for i=2:k
243 * sum=A(i,k+1);
244 * for j=1:i-1
245 * sum=sum-A(i,j)*A(j,k+1);
246 * end;
247 * A(i,k+1)=sum/A(i,i);
248 * end
249 * for i=2:k
250 * sum=A(k+1,i);
251 * for j=1:i-1
252 * sum=sum-A(j,i)*A(k+1,j);
253 * end;
254 * A(k+1,i)=sum;
255 * end
256 * sum=A(k+1,k+1);
257 * for i=1:k
258 * sum=sum-A(k+1,i)*A(i,k+1);
259 * end
260 * A(k+1,k+1)=sum;
261 * end
262 */
263 void factorize()
264 {
265 precondition(!math::is_zero(D[0]), "Zero diagonal in skyline_lu");
266 D[0] = math::inverse(D[0]);
267
268 for (int k = 0; k < n - 1; ++k) {
269 // check whether A(1,k+1) lies within the skyline structure
270 if (ptr[k + 1] + k + 1 == ptr[k + 2]) {
271 U[ptr[k + 1]] = D[0] * U[ptr[k + 1]];
272 }
273
274 // Compute column k+1 of U
275 int indexEntry = ptr[k + 1];
276 int iBeginCol = k + 1 - ptr[k + 2] + ptr[k + 1];
277 for (int i = iBeginCol; i <= k; ++indexEntry, ++i) {
278 if (i == 0)
279 continue;
280
281 value_type sum = U[indexEntry]; // this is element U(i,k+1)
282
283 // Multiply row i of L and Column k+1 of U
284 int jBeginRow = i - ptr[i + 1] + ptr[i];
285 int jBeginMult = std::max(iBeginCol, jBeginRow);
286
287 int indexL = ptr[i] + jBeginMult - jBeginRow;
288 int indexU = ptr[k + 1] + jBeginMult - iBeginCol;
289 for (int j = jBeginMult; j < i; ++j, ++indexL, ++indexU)
290 sum -= L[indexL] * U[indexU];
291
292 U[indexEntry] = D[i] * sum;
293 }
294
295 // Compute row k+1 of L
296 indexEntry = ptr[k + 1];
297 int jBeginRow = k + 1 - ptr[k + 2] + ptr[k + 1];
298 for (int i = iBeginCol; i <= k; ++indexEntry, ++i) {
299 if (i == 0)
300 continue;
301
302 value_type sum = L[indexEntry]; // this is the element L(k+1,i)
303
304 // Multiply row k+1 of L and column i of U
305 int jBeginCol = i - ptr[i + 1] + ptr[i];
306 int jBeginMult = std::max(jBeginCol, jBeginRow);
307
308 int indexL = ptr[k + 1] + jBeginMult - jBeginRow;
309 int indexU = ptr[i] + jBeginMult - jBeginCol;
310
311 for (int j = jBeginMult; j < i; ++j, ++indexL, ++indexU)
312 sum -= L[indexL] * U[indexU];
313
314 L[indexEntry] = sum;
315 }
316
317 // Find element in diagonal
318 value_type sum = D[k + 1];
319 for (int j = ptr[k + 1]; j < ptr[k + 2]; ++j)
320 sum -= L[j] * U[j];
321
322 precondition(!math::is_zero(sum),
323 "Zero sum in skyline_lu factorization");
324
325 D[k + 1] = math::inverse(sum);
326 }
327 }
328};
329
330} // namespace Arcane::Alina::solver
331
332#endif
Class to handle empty parameters list.
Definition AlinaUtils.h:90
Matrix class, to be used by user.
Number of rows for statically sized matrix types.