b2api
B2000++ API Reference Manual, VERSION 4.6
 
Loading...
Searching...
No Matches
b2spliss_solver.H
1//------------------------------------------------------------------------
2// b2spliss_solver.H --
3//
4//
5// written Neda Ebrahimi Pour <neda.ebrahimipour@dlr.de>
6//
7// (c) 2022-2023 Deutsches Zentrum für Luft- und Raumfahrt (DLR) e.V.
8// Linder Höhe, 51147 Köln
9//
10// All Rights Reserved. Proprietary source code. The contents of
11// this file may not be disclosed to third parties, copied or
12// duplicated in any form, in whole or in part, without the prior
13// written permission of SMR.
14//------------------------------------------------------------------------
15#ifndef B2SPLISS_SOLVER_H_
16#define B2SPLISS_SOLVER_H_
17
18#include <string>
19#include <vector>
20
21#include "b2ppconfig.h"
22#include "b2sparse_solver.H"
23#include "spliss/spliss.h"
24
25namespace b2000::b2linalg {
26
27// Struct for all configurable variables for Spliss
28typedef struct {
29 double residuum;
30 size_t iteration;
31 bool verbose;
32 std::string precond_type;
33 double relaxation;
34} Spliss_config;
35
36// LDLT solver for Spliss
37template <typename T>
38class Spliss_LDLt_sparse_solver : public LDLt_sparse_solver<T> {
39public:
40 Spliss_LDLt_sparse_solver() : updated_value(false) {}
41
42 ~Spliss_LDLt_sparse_solver() {}
43
44 void init(
45 size_t s, size_t nnz, const size_t* colind, const size_t* rowind, const T* value,
46 const int connectivity, const Dictionary& dictionary) {
47 // No distributed communication for now, a function local assignement
48 auto communicator = std::make_shared<spliss::LocalCommunicator>();
49
50 // B2000 only stores the lower triangular matrix (symmetric matrix pattern).
51 // Spliss requires both, the lower and upper part of
52 // the matrix, thus we need to fill the upper part, too (if condition)!
53 std::vector<std::pair<spliss::LocalIndexStoreT, spliss::GlobalIndexStoreT>>
54 vector_of_positions;
55 size_t r_ptr = colind[0];
56 for (size_t j = 0; j != s; ++j) {
57 for (size_t r_end = colind[j + 1]; r_ptr != r_end; ++r_ptr) {
58 vector_of_positions.emplace_back(rowind[r_ptr], j);
59 // Store input indices for potential update of the matrix values,
60 // used in the resolve function.
61 indices.emplace_back(rowind[r_ptr], j);
62
63 if (rowind[r_ptr] != j) { vector_of_positions.emplace_back(j, rowind[r_ptr]); }
64 }
65 }
66 // The number of local blocks is "s", the size per block is 1!
67 family = std::make_shared<const spliss::VectorFamily>(communicator, s, 1);
68
69 // First two entries (family) provide the size of colums and rows (s x s matrix)
70 // Last the matrix pattern (colum major).
71 auto connect = std::make_shared<MatrixConnectivityT>(
72 family, family, CreateCSRPatternFromVectorOfPositions(*family, vector_of_positions));
73
74 auto connectivityDiag = std::make_shared<MatrixConnectivityT>(
75 family, family, spliss::CreateDiagonalSparsityPattern(*family));
76
77 // Coloring for partitioning (parallel execution)
78 auto colors = spliss::ComputeColors(*connect);
79
80 // Reference to nonzero values
81 // Required later in resolve function, in the case
82 // the values change during runtime!
83 A_reference = value;
84
85 A = std::make_shared<MatrixT>(family, family, colors, connect, connectivityDiag);
86
87 A->GetAccess().SetZero();
88
89 conf.residuum = dictionary.get_double("SPLISS_RESIDUUM", 1.0E-8);
90 conf.iteration = dictionary.get_int("SPLISS_MAX_ITER", 1000000);
91 conf.verbose = dictionary.get_bool("SPLISS_VERBOSE", false);
92 conf.precond_type = dictionary.get_string("SPLISS_PRECOND", "LU_JACOBI_CG");
93 conf.relaxation = dictionary.get_double("SPLISS_RELAXATION", 0.8);
94
95 using VectorT = typename MatrixT::SourceVectorT;
96 VectorT vec_format(family);
97 auto criterion = std::make_shared<spliss::ConservativeStopCriterion<T>>(
98 conf.residuum, spliss::ResidualCheckMode::Relative, conf.iteration, conf.verbose);
99
100 auto&& stack = spliss::InitSolverStack(A);
101
102 if (conf.precond_type == "ILU_CG") {
103 solver = stack.template Append<SimpleILUDecomposition>(A->GetOffDiagonalData())
104 .template Append<spliss::CG>(criterion)
105 .GetSolver();
106 } else if (conf.precond_type == "LU_CG") {
107 solver = stack.template Append<spliss::LUDecomposition>()
108 .template Append<spliss::CG>(criterion)
109 .GetSolver();
110 } else if (conf.precond_type == "CG") {
111 solver = stack.template Append<spliss::CG>(criterion).GetSolver();
112 } else if (conf.precond_type == "JACOBI") {
113 solver = stack.template Append<spliss::LUDecomposition>()
114 .template Append<spliss::Jacobi>(criterion, conf.relaxation)
115 .GetSolver();
116 } else {
117 auto criterionJacobi = std::make_shared<spliss::ConfidentStopCriterion<T>>(5u);
118 solver = stack.template Append<spliss::LUDecomposition>()
119 .template Append<spliss::Jacobi>(criterionJacobi, conf.relaxation)
120 .template Append<spliss::CG>(criterion)
121 .GetSolver();
122 }
123
124 updated_value = true;
125 }
126
127 void update_value() { updated_value = true; }
128
129 void resolve(
130 size_t s, size_t nrhs, const T* b, size_t ldb, T* x, size_t ldx,
131 char left_or_right = ' ') {
132 logging::Logger logger = logging::get_logger("linear_algebra.sparse_solver.spliss");
133 if (s == 0) { return; }
134
135 using VectorT = typename MatrixT::SourceVectorT;
136 VectorT b_value(family), x_value(family);
137
138 if (updated_value) {
139 auto a = A->GetAccess();
140
141 // Fill the matrix with nonzero values.
142 // Here the lower and upper part needs to be filled.
143 size_t r_ptr = 0;
144 for (const auto& i : indices) {
145 a(i.first, i.second)(0, 0) = A_reference[r_ptr];
146 r_ptr++;
147 if (i.first != i.second) {
148 a(i.second, i.first)(0, 0) = a(i.first, i.second)(0, 0);
149 }
150 }
151
152 solver->Prepare();
153 updated_value = false;
154 }
155
156 for (size_t i = 0; i != nrhs; ++i) {
157 x_value.SetZero();
158
159 {
160 auto b_access = b_value.GetAccess();
161 for (size_t j = 0; j != s; ++j) { b_access[j][0] = b[j + i * ldb]; }
162 }
163 auto status = solver->Apply(b_value, x_value);
164
165 std::cout << "Total number of iterations: " << status->LastIterationNumber()
166 << std::endl;
167
168#ifdef SPLISS_DEBUG_OUTPUT
169 auto convergedData = status->GetConvergenceData();
170
171 for (size_t k = 0; k != convergedData.size(); k++) {
172 std::cout << "Obtained solution: " << k << ", " << convergedData[k] << std::endl;
173 }
174#endif // SPLISS_DEBUG_OUTPUT
175
176 if (status->GetReason() != spliss::SolverStoppingReason::RelativeReductionSuccess) {
177 Exception e;
178 e << "Solver stopped without success!" << THROW;
179 }
180
181 {
182 auto x_access = x_value.GetAccess();
183 for (size_t j = 0; j != s; ++j) { x[j + i * ldx] = x_access[j][0]; }
184 }
185 }
186 }
187
188protected:
189 bool updated_value;
190 std::shared_ptr<const spliss::VectorFamily> family;
191 // Matrix storage typ, only CSR is supported
192 using MatrixConnectivityT = spliss::MatrixConnectivity<spliss::CSRPattern>;
193 // Blockstorage, here we use 1 Block per nonzero entry in the matrix
194 // Template Parameters: DataScalarType, ConnectivityType, RowsPerBlock, ColumnsPerBlock
195 using StorageT = spliss::CompactBlockStorage<T, MatrixConnectivityT, 1, 1>;
196 // DataBasedMatrix< ScalarType, ConnectivityType, RowsPerBlock, ColumnsPerBlock,
197 // DataScalarType, StorageType
198 using MatrixT = spliss::DataBasedMatrix<T, MatrixConnectivityT, 1, 1, T, StorageT>;
199 std::shared_ptr<MatrixT> A;
200 // Setting for ILU decomposition
201 template <typename ScalarType, typename StorageType>
202 using SimpleILUDecomposition = spliss::ILUDecomposition<ScalarType, StorageType, StorageType>;
203 // Spliss configuration struct
204 Spliss_config conf;
205 // Reference to the Matrix
206 const T* A_reference;
207 // Vector holding the indices of the nonzero rows and colums
208 std::vector<std::pair<size_t, size_t>> indices;
209 // Pointer for solve Spliss
210 std::shared_ptr<spliss::SolverInterface<T>> solver;
211};
212
213template <typename T>
214class Spliss_LDLt_extension_sparse_solver : public LDLt_extension_sparse_solver<T>,
215 public Spliss_LDLt_sparse_solver<T> {
216public:
217 Spliss_LDLt_extension_sparse_solver() : Spliss_LDLt_sparse_solver<T>(), div(0) {}
218
219 void init(
220 size_t size_, size_t nnz_, const size_t* colind_, const size_t* rowind_, const T* value_,
221 size_t size_ext_, const int connectivity, const Dictionary& dictionary) {
222 if (size_ext_ != 1) { UnimplementedError() << THROW; }
223
224 Spliss_LDLt_sparse_solver<T>::init(
225 size_, nnz_, colind_, rowind_, value_, connectivity, dictionary);
226 m_ab.resize(size_ * 2);
227 }
228
229 void update_value() { Spliss_LDLt_sparse_solver<T>::update_value(); }
230
231 void resolve(
232 size_t s, size_t nrhs, const T* b, size_t ldb, T* x, size_t ldx, const T* ma_ = 0,
233 const T* mb_ = 0, const T* mc_ = 0, char left_or_right = ' ') {
234 if (mc_ != 0) {
235 std::copy(ma_, ma_ + s - 1, &m_ab[0]);
236 std::copy(mb_, mb_ + s - 1, &m_ab[s - 1]);
237 Spliss_LDLt_sparse_solver<T>::resolve(s - 1, 2, &m_ab[0], s - 1, &m_ab[0], s - 1);
238 div = 1 / (*mc_ - blas::dot(s - 1, ma_, 1, &m_ab[s - 1], 1));
239 }
240
241 for (size_t i = 0; i != nrhs; ++i) {
242 const T x2 = x[ldx * i + s - 1] =
243 div * (b[ldb * i + s - 1] - blas::dot(s - 1, b + ldb * i, 1, &m_ab[s - 1], 1));
244 Spliss_LDLt_sparse_solver<T>::resolve(s - 1, 1, b + ldb * i, ldb, x + ldx * i, ldx);
245 blas::axpy(s - 1, -x2, &m_ab[0], 1, x + ldx * i, 1);
246 }
247 }
248
249protected:
250 std::vector<T> m_ab;
251 T div;
252};
253
254template <>
255class Spliss_LDLt_sparse_solver<csda<double>> : public LDLt_sparse_solver<csda<double>> {
256public:
257 Spliss_LDLt_sparse_solver() {
258 UnimplementedError() << "The Spliss solver is only enabled for doubles." << THROW;
259 }
260
261 void init(
262 size_t s, size_t nnz, const size_t* colind, const size_t* rowind,
263 const csda<double>* value, const int connectivity, const Dictionary& dictionary) {}
264
265 void update_value() {}
266
267 void resolve(
268 size_t s, size_t nrhs, const csda<double>* b, size_t ldb, csda<double>* x, size_t ldx,
269 char left_or_right = ' ') {
271 }
272};
273
274template <>
275class Spliss_LDLt_extension_sparse_solver<csda<double>>
276 : public LDLt_extension_sparse_solver<csda<double>> {
277public:
278 Spliss_LDLt_extension_sparse_solver() {
279 UnimplementedError() << "The Spliss solver is only enabled for doubles." << THROW;
280 }
281
282 void init(
283 size_t s, size_t nnz, const size_t* colind, const size_t* rowind,
284 const csda<double>* value, size_t, const int connectivity, const Dictionary& dictionary) {
285 }
286
287 void update_value() {}
288
289 void resolve(
290 size_t s, size_t nrhs, const csda<double>* b, size_t ldb, csda<double>* x, size_t ldx,
291 const csda<double>* ma = 0, const csda<double>* mb = 0, const csda<double>* mc = 0,
292 char left_or_right = ' ') {
294 }
295};
296
297} // namespace b2000::b2linalg
298
299#endif // B2SPLISS_SOLVER_H_
#define THROW
Definition b2exception.H:198
Logger & get_logger(const std::string &logger_name="")
Definition b2logging.H:829
GenericException< UnimplementedError_name > UnimplementedError
Definition b2exception.H:314