Ginkgo Generated from branch based on master. Ginkgo version 1.8.0
A numerical linear algebra library targeting many-core architectures
Loading...
Searching...
No Matches
ilu.hpp
1// SPDX-FileCopyrightText: 2017 - 2024 The Ginkgo authors
2//
3// SPDX-License-Identifier: BSD-3-Clause
4
5#ifndef GKO_PUBLIC_CORE_PRECONDITIONER_ILU_HPP_
6#define GKO_PUBLIC_CORE_PRECONDITIONER_ILU_HPP_
7
8
9#include <memory>
10#include <type_traits>
11
12
13#include <ginkgo/core/base/abstract_factory.hpp>
14#include <ginkgo/core/base/composition.hpp>
15#include <ginkgo/core/base/exception.hpp>
16#include <ginkgo/core/base/exception_helpers.hpp>
17#include <ginkgo/core/base/lin_op.hpp>
18#include <ginkgo/core/base/precision_dispatch.hpp>
19#include <ginkgo/core/base/std_extensions.hpp>
20#include <ginkgo/core/config/config.hpp>
21#include <ginkgo/core/config/registry.hpp>
22#include <ginkgo/core/factorization/par_ilu.hpp>
23#include <ginkgo/core/matrix/dense.hpp>
24#include <ginkgo/core/preconditioner/isai.hpp>
25#include <ginkgo/core/preconditioner/utils.hpp>
26#include <ginkgo/core/solver/gmres.hpp>
27#include <ginkgo/core/solver/ir.hpp>
28#include <ginkgo/core/solver/solver_traits.hpp>
29#include <ginkgo/core/solver/triangular.hpp>
30#include <ginkgo/core/stop/combined.hpp>
31#include <ginkgo/core/stop/iteration.hpp>
32#include <ginkgo/core/stop/residual_norm.hpp>
33
34
35namespace gko {
36namespace preconditioner {
37namespace detail {
38
39
40template <typename LSolverType, typename USolverType>
41constexpr bool support_ilu_parse =
42 std::is_same<typename USolverType::transposed_type, LSolverType>::value &&
43 (is_instantiation_of<LSolverType, solver::LowerTrs>::value ||
44 is_instantiation_of<LSolverType, solver::Ir>::value ||
45 is_instantiation_of<LSolverType, solver::Gmres>::value ||
46 is_instantiation_of<LSolverType, preconditioner::LowerIsai>::value);
47
48
49template <typename Ilu,
50 std::enable_if_t<!support_ilu_parse<typename Ilu::l_solver_type,
51 typename Ilu::u_solver_type>>* =
52 nullptr>
53typename Ilu::parameters_type ilu_parse(
54 const config::pnode& config, const config::registry& context,
55 const config::type_descriptor& td_for_child)
56{
57 GKO_INVALID_STATE(
58 "preconditioner::Ilu only supports limited type for parse.");
59}
60
61template <
62 typename Ilu,
63 std::enable_if_t<support_ilu_parse<typename Ilu::l_solver_type,
64 typename Ilu::u_solver_type>>* = nullptr>
65typename Ilu::parameters_type ilu_parse(
66 const config::pnode& config, const config::registry& context,
67 const config::type_descriptor& td_for_child);
68
69} // namespace detail
70
71
121template <typename LSolverType = solver::LowerTrs<>,
122 typename USolverType = solver::UpperTrs<>, bool ReverseApply = false,
123 typename IndexType = int32>
124class Ilu : public EnableLinOp<
125 Ilu<LSolverType, USolverType, ReverseApply, IndexType>>,
126 public Transposable {
127 friend class EnableLinOp<Ilu>;
128 friend class EnablePolymorphicObject<Ilu, LinOp>;
129
130public:
131 static_assert(
132 std::is_same<typename LSolverType::value_type,
133 typename USolverType::value_type>::value,
134 "Both the L- and the U-solver must use the same `value_type`!");
135 using value_type = typename LSolverType::value_type;
136 using l_solver_type = LSolverType;
137 using u_solver_type = USolverType;
138 static constexpr bool performs_reverse_apply = ReverseApply;
139 using index_type = IndexType;
140 using transposed_type =
141 Ilu<typename USolverType::transposed_type,
142 typename LSolverType::transposed_type, ReverseApply, IndexType>;
143
144 class Factory;
145
147 : public enable_parameters_type<parameters_type, Factory> {
151 std::shared_ptr<const typename l_solver_type::Factory>
153
157 std::shared_ptr<const typename u_solver_type::Factory>
159
163 std::shared_ptr<const LinOpFactory> factorization_factory{};
164
165 GKO_DEPRECATED("use with_l_solver instead")
166 parameters_type& with_l_solver_factory(
167 deferred_factory_parameter<const typename l_solver_type::Factory>
168 solver)
169 {
170 return with_l_solver(std::move(solver));
171 }
172
173 parameters_type& with_l_solver(
175 solver)
176 {
177 this->l_solver_generator = std::move(solver);
178 this->deferred_factories["l_solver"] = [](const auto& exec,
179 auto& params) {
180 if (!params.l_solver_generator.is_empty()) {
181 params.l_solver_factory =
182 params.l_solver_generator.on(exec);
183 }
184 };
185 return *this;
186 }
187
188 GKO_DEPRECATED("use with_u_solver instead")
189 parameters_type& with_u_solver_factory(
190 deferred_factory_parameter<const typename u_solver_type::Factory>
191 solver)
192 {
193 return with_u_solver(std::move(solver));
194 }
195
196 parameters_type& with_u_solver(
197 deferred_factory_parameter<const typename u_solver_type::Factory>
198 solver)
199 {
200 this->u_solver_generator = std::move(solver);
201 this->deferred_factories["u_solver"] = [](const auto& exec,
202 auto& params) {
203 if (!params.u_solver_generator.is_empty()) {
204 params.u_solver_factory =
205 params.u_solver_generator.on(exec);
206 }
207 };
208 return *this;
209 }
210
211 GKO_DEPRECATED("use with_factorization instead")
212 parameters_type& with_factorization_factory(
213 deferred_factory_parameter<const LinOpFactory> factorization)
214 {
215 return with_factorization(std::move(factorization));
216 }
217
218 parameters_type& with_factorization(
219 deferred_factory_parameter<const LinOpFactory> factorization)
220 {
221 this->factorization_generator = std::move(factorization);
222 this->deferred_factories["factorization"] = [](const auto& exec,
223 auto& params) {
224 if (!params.factorization_generator.is_empty()) {
225 params.factorization_factory =
226 params.factorization_generator.on(exec);
227 }
228 };
229 return *this;
230 }
231
232 private:
233 deferred_factory_parameter<const typename l_solver_type::Factory>
234 l_solver_generator;
235
236 deferred_factory_parameter<const typename u_solver_type::Factory>
237 u_solver_generator;
238
239 deferred_factory_parameter<const LinOpFactory> factorization_generator;
240 };
241
244
263 const config::pnode& config, const config::registry& context,
264 const config::type_descriptor& td_for_child =
265 config::make_type_descriptor<value_type, index_type>())
266 {
267 return detail::ilu_parse<Ilu>(config, context, td_for_child);
268 }
269
275 std::shared_ptr<const l_solver_type> get_l_solver() const
276 {
277 return l_solver_;
278 }
279
285 std::shared_ptr<const u_solver_type> get_u_solver() const
286 {
287 return u_solver_;
288 }
289
290 std::unique_ptr<LinOp> transpose() const override
291 {
292 std::unique_ptr<transposed_type> transposed{
293 new transposed_type{this->get_executor()}};
294 transposed->set_size(gko::transpose(this->get_size()));
295 transposed->l_solver_ =
297 this->get_u_solver()->transpose()));
298 transposed->u_solver_ =
300 this->get_l_solver()->transpose()));
301
302 return std::move(transposed);
303 }
304
305 std::unique_ptr<LinOp> conj_transpose() const override
306 {
307 std::unique_ptr<transposed_type> transposed{
308 new transposed_type{this->get_executor()}};
309 transposed->set_size(gko::transpose(this->get_size()));
310 transposed->l_solver_ =
312 this->get_u_solver()->conj_transpose()));
313 transposed->u_solver_ =
315 this->get_l_solver()->conj_transpose()));
316
317 return std::move(transposed);
318 }
319
325 Ilu& operator=(const Ilu& other)
326 {
327 if (&other != this) {
329 auto exec = this->get_executor();
330 l_solver_ = other.l_solver_;
331 u_solver_ = other.u_solver_;
332 parameters_ = other.parameters_;
333 if (other.get_executor() != exec) {
334 l_solver_ = gko::clone(exec, l_solver_);
335 u_solver_ = gko::clone(exec, u_solver_);
336 }
337 }
338 return *this;
339 }
340
347 Ilu& operator=(Ilu&& other)
348 {
349 if (&other != this) {
351 auto exec = this->get_executor();
352 l_solver_ = std::move(other.l_solver_);
353 u_solver_ = std::move(other.u_solver_);
354 parameters_ = std::exchange(other.parameters_, parameters_type{});
355 if (other.get_executor() != exec) {
356 l_solver_ = gko::clone(exec, l_solver_);
357 u_solver_ = gko::clone(exec, u_solver_);
358 }
359 }
360 return *this;
361 }
362
367 Ilu(const Ilu& other) : Ilu{other.get_executor()} { *this = other; }
368
374 Ilu(Ilu&& other) : Ilu{other.get_executor()} { *this = std::move(other); }
375
376protected:
377 void apply_impl(const LinOp* b, LinOp* x) const override
378 {
379 // take care of real-to-complex apply
381 [&](auto dense_b, auto dense_x) {
382 this->set_cache_to(dense_b);
383 if (!ReverseApply) {
384 l_solver_->apply(dense_b, cache_.intermediate);
385 if (u_solver_->apply_uses_initial_guess()) {
386 dense_x->copy_from(cache_.intermediate);
387 }
388 u_solver_->apply(cache_.intermediate, dense_x);
389 } else {
390 u_solver_->apply(dense_b, cache_.intermediate);
391 if (l_solver_->apply_uses_initial_guess()) {
392 dense_x->copy_from(cache_.intermediate);
393 }
394 l_solver_->apply(cache_.intermediate, dense_x);
395 }
396 },
397 b, x);
398 }
399
400 void apply_impl(const LinOp* alpha, const LinOp* b, const LinOp* beta,
401 LinOp* x) const override
402 {
404 [&](auto dense_alpha, auto dense_b, auto dense_beta, auto dense_x) {
405 this->set_cache_to(dense_b);
406 if (!ReverseApply) {
407 l_solver_->apply(dense_b, cache_.intermediate);
408 u_solver_->apply(dense_alpha, cache_.intermediate,
409 dense_beta, dense_x);
410 } else {
411 u_solver_->apply(dense_b, cache_.intermediate);
412 l_solver_->apply(dense_alpha, cache_.intermediate,
413 dense_beta, dense_x);
414 }
415 },
416 alpha, b, beta, x);
417 }
418
419 explicit Ilu(std::shared_ptr<const Executor> exec)
420 : EnableLinOp<Ilu>(std::move(exec))
421 {}
422
423 explicit Ilu(const Factory* factory, std::shared_ptr<const LinOp> lin_op)
424 : EnableLinOp<Ilu>(factory->get_executor(), lin_op->get_size()),
425 parameters_{factory->get_parameters()}
426 {
427 auto comp =
428 std::dynamic_pointer_cast<const Composition<value_type>>(lin_op);
429 std::shared_ptr<const LinOp> l_factor;
430 std::shared_ptr<const LinOp> u_factor;
431
432 // build factorization if we weren't passed a composition
433 if (!comp) {
434 auto exec = lin_op->get_executor();
435 if (!parameters_.factorization_factory) {
436 parameters_.factorization_factory =
437 factorization::ParIlu<value_type, index_type>::build().on(
438 exec);
439 }
440 auto fact = std::shared_ptr<const LinOp>(
441 parameters_.factorization_factory->generate(lin_op));
442 // ensure that the result is a composition
443 comp =
444 std::dynamic_pointer_cast<const Composition<value_type>>(fact);
445 if (!comp) {
446 GKO_NOT_SUPPORTED(comp);
447 }
448 }
449 if (comp->get_operators().size() == 2) {
450 l_factor = comp->get_operators()[0];
451 u_factor = comp->get_operators()[1];
452 } else {
453 GKO_NOT_SUPPORTED(comp);
454 }
455 GKO_ASSERT_EQUAL_DIMENSIONS(l_factor, u_factor);
456
457 auto exec = this->get_executor();
458
459 // If no factories are provided, generate default ones
460 if (!parameters_.l_solver_factory) {
461 l_solver_ = generate_default_solver<l_solver_type>(exec, l_factor);
462 } else {
463 l_solver_ = parameters_.l_solver_factory->generate(l_factor);
464 }
465 if (!parameters_.u_solver_factory) {
466 u_solver_ = generate_default_solver<u_solver_type>(exec, u_factor);
467 } else {
468 u_solver_ = parameters_.u_solver_factory->generate(u_factor);
469 }
470 }
471
479 void set_cache_to(const LinOp* b) const
480 {
481 if (cache_.intermediate == nullptr) {
482 cache_.intermediate =
484 }
485 // Use b as the initial guess for the first triangular solve
486 cache_.intermediate->copy_from(b);
487 }
488
489
497 template <typename SolverType>
498 static std::enable_if_t<solver::has_with_criteria<SolverType>::value,
499 std::unique_ptr<SolverType>>
500 generate_default_solver(const std::shared_ptr<const Executor>& exec,
501 const std::shared_ptr<const LinOp>& mtx)
502 {
503 constexpr gko::remove_complex<value_type> default_reduce_residual{1e-4};
504 const unsigned int default_max_iters{
505 static_cast<unsigned int>(mtx->get_size()[0])};
506
507 return SolverType::build()
508 .with_criteria(
509 gko::stop::Iteration::build().with_max_iters(default_max_iters),
511 .with_reduction_factor(default_reduce_residual))
512 .on(exec)
513 ->generate(mtx);
514 }
515
519 template <typename SolverType>
520 static std::enable_if_t<!solver::has_with_criteria<SolverType>::value,
521 std::unique_ptr<SolverType>>
522 generate_default_solver(const std::shared_ptr<const Executor>& exec,
523 const std::shared_ptr<const LinOp>& mtx)
524 {
525 return SolverType::build().on(exec)->generate(mtx);
526 }
527
528private:
529 std::shared_ptr<const l_solver_type> l_solver_{};
530 std::shared_ptr<const u_solver_type> u_solver_{};
541 mutable struct cache_struct {
542 cache_struct() = default;
543 ~cache_struct() = default;
544 cache_struct(const cache_struct&) {}
545 cache_struct(cache_struct&&) {}
546 cache_struct& operator=(const cache_struct&) { return *this; }
547 cache_struct& operator=(cache_struct&&) { return *this; }
548 std::unique_ptr<LinOp> intermediate{};
549 } cache_;
550};
551
552
553} // namespace preconditioner
554} // namespace gko
555
556
557#endif // GKO_PUBLIC_CORE_PRECONDITIONER_ILU_HPP_
The EnableLinOp mixin can be used to provide sensible default implementations of the majority of the ...
Definition lin_op.hpp:880
This mixin inherits from (a subclass of) PolymorphicObject and provides a base implementation of a ne...
Definition polymorphic_object.hpp:663
Definition lin_op.hpp:118
LinOp(const LinOp &)=default
Copy-constructs a LinOp.
const dim< 2 > & get_size() const noexcept
Returns the size of the operator.
Definition lin_op.hpp:211
LinOp & operator=(const LinOp &)=default
Copy-assigns a LinOp.
std::shared_ptr< const Executor > get_executor() const noexcept
Returns the Executor of the object.
Definition polymorphic_object.hpp:235
Linear operators which support transposition should implement the Transposable interface.
Definition lin_op.hpp:434
pnode describes a tree of properties.
Definition property_tree.hpp:28
This class stores additional context for creating Ginkgo objects from configuration files.
Definition registry.hpp:168
This class describes the value and index types to be used when building a Ginkgo type from a configur...
Definition type_descriptor.hpp:37
Represents a factory parameter of factory type that can either initialized by a pre-existing factory ...
Definition abstract_factory.hpp:309
The enable_parameters_type mixin is used to create a base implementation of the factory parameters st...
Definition abstract_factory.hpp:211
static std::unique_ptr< Dense > create(std::shared_ptr< const Executor > exec, const dim< 2 > &size={}, size_type stride=0)
Creates an uninitialized Dense matrix of the specified size.
Definition ilu.hpp:242
The Incomplete LU (ILU) preconditioner solves the equation for a given lower triangular matrix L,...
Definition ilu.hpp:126
Ilu(Ilu &&other)
Move-constructs an ILU preconditioner.
Definition ilu.hpp:374
Ilu & operator=(Ilu &&other)
Move-assigns an ILU preconditioner.
Definition ilu.hpp:347
std::shared_ptr< const l_solver_type > get_l_solver() const
Returns the solver which is used for the provided L matrix.
Definition ilu.hpp:275
static parameters_type parse(const config::pnode &config, const config::registry &context, const config::type_descriptor &td_for_child=config::make_type_descriptor< value_type, index_type >())
Create the parameters from the property_tree.
Definition ilu.hpp:262
std::unique_ptr< LinOp > conj_transpose() const override
Returns a LinOp representing the conjugate transpose of the Transposable object.
Definition ilu.hpp:305
Ilu(const Ilu &other)
Copy-constructs an ILU preconditioner.
Definition ilu.hpp:367
std::shared_ptr< const u_solver_type > get_u_solver() const
Returns the solver which is used for the provided U matrix.
Definition ilu.hpp:285
std::unique_ptr< LinOp > transpose() const override
Returns a LinOp representing the transpose of the Transposable object.
Definition ilu.hpp:290
Ilu & operator=(const Ilu &other)
Copy-assigns an ILU preconditioner.
Definition ilu.hpp:325
The ResidualNorm class is a stopping criterion which stops the iteration process when the actual resi...
Definition residual_norm.hpp:110
#define GKO_ENABLE_BUILD_METHOD(_factory_name)
Defines a build method for the factory, simplifying its construction by removing the repetitive typin...
Definition abstract_factory.hpp:394
#define GKO_ENABLE_LIN_OP_FACTORY(_lin_op, _parameters_name, _factory_name)
This macro will generate a default implementation of a LinOpFactory for the LinOp subclass it is defi...
Definition lin_op.hpp:1018
@ factory
LinOpFactory events.
The Ginkgo namespace.
Definition abstract_factory.hpp:20
typename detail::remove_complex_s< T >::type remove_complex
Obtain the type which removed the complex of complex/scalar type or the template parameter of class b...
Definition math.hpp:326
void precision_dispatch_real_complex(Function fn, const LinOp *in, LinOp *out)
Calls the given function with the given LinOps temporarily converted to matrix::Dense<ValueType>* as ...
Definition precision_dispatch.hpp:94
detail::cloned_type< Pointer > clone(const Pointer &p)
Creates a unique clone of the object pointed to by p.
Definition utils_helper.hpp:175
batch_dim< 2, DimensionType > transpose(const batch_dim< 2, DimensionType > &input)
Returns a batch_dim object with its dimensions swapped for batched operators.
Definition batch_dim.hpp:120
std::decay_t< T > * as(U *obj)
Performs polymorphic type conversion.
Definition utils_helper.hpp:309
detail::shared_type< OwningPointer > share(OwningPointer &&p)
Marks the object pointed to by p as shared.
Definition utils_helper.hpp:226
std::shared_ptr< const LinOpFactory > factorization_factory
Factory for the factorization.
Definition ilu.hpp:163
std::shared_ptr< const typename u_solver_type::Factory > u_solver_factory
Factory for the U solver.
Definition ilu.hpp:158
std::shared_ptr< const typename l_solver_type::Factory > l_solver_factory
Factory for the L solver.
Definition ilu.hpp:152