Ginkgo  Generated from pipelines/1330831941 branch based on master. Ginkgo version 1.8.0
A numerical linear algebra library targeting many-core architectures
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 
35 namespace gko {
36 namespace preconditioner {
37 namespace detail {
38 
39 
40 template <typename LSolverType, typename USolverType>
41 constexpr 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 
49 template <typename Ilu,
50  std::enable_if_t<!support_ilu_parse<typename Ilu::l_solver_type,
51  typename Ilu::u_solver_type>>* =
52  nullptr>
53 typename 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 
61 template <
62  typename Ilu,
63  std::enable_if_t<support_ilu_parse<typename Ilu::l_solver_type,
64  typename Ilu::u_solver_type>>* = nullptr>
65 typename 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 
121 template <typename LSolverType = solver::LowerTrs<>,
122  typename USolverType = solver::UpperTrs<>, bool ReverseApply = false,
123  typename IndexType = int32>
124 class Ilu : public EnableLinOp<
125  Ilu<LSolverType, USolverType, ReverseApply, IndexType>>,
126  public Transposable {
127  friend class EnableLinOp<Ilu>;
128  friend class EnablePolymorphicObject<Ilu, LinOp>;
129 
130 public:
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_ =
296  share(as<typename u_solver_type::transposed_type>(
297  this->get_u_solver()->transpose()));
298  transposed->u_solver_ =
299  share(as<typename l_solver_type::transposed_type>(
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_ =
311  share(as<typename u_solver_type::transposed_type>(
312  this->get_u_solver()->conj_transpose()));
313  transposed->u_solver_ =
314  share(as<typename l_solver_type::transposed_type>(
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 
376 protected:
377  void apply_impl(const LinOp* b, LinOp* x) const override
378  {
379  // take care of real-to-complex apply
380  precision_dispatch_real_complex<value_type>(
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  {
403  precision_dispatch_real_complex<value_type>(
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 
528 private:
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_
gko::preconditioner::Ilu::Factory
Definition: ilu.hpp:242
gko::config::pnode
pnode describes a tree of properties.
Definition: property_tree.hpp:28
gko::preconditioner::Ilu::parameters_type::u_solver_factory
std::shared_ptr< const typename u_solver_type::Factory > u_solver_factory
Factory for the U solver.
Definition: ilu.hpp:158
gko::LinOp
Definition: lin_op.hpp:118
gko::preconditioner::Ilu::operator=
Ilu & operator=(Ilu &&other)
Move-assigns an ILU preconditioner.
Definition: ilu.hpp:347
gko::matrix::Dense::create
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.
gko::log::profile_event_category::factory
LinOpFactory events.
gko::Transposable
Linear operators which support transposition should implement the Transposable interface.
Definition: lin_op.hpp:434
gko::preconditioner::Ilu::Ilu
Ilu(const Ilu &other)
Copy-constructs an ILU preconditioner.
Definition: ilu.hpp:367
gko::preconditioner::Ilu::operator=
Ilu & operator=(const Ilu &other)
Copy-assigns an ILU preconditioner.
Definition: ilu.hpp:325
gko::config::type_descriptor
This class describes the value and index types to be used when building a Ginkgo type from a configur...
Definition: type_descriptor.hpp:37
gko::clone
detail::cloned_type< Pointer > clone(const Pointer &p)
Creates a unique clone of the object pointed to by p.
Definition: utils_helper.hpp:175
gko
The Ginkgo namespace.
Definition: abstract_factory.hpp:20
gko::preconditioner::Ilu::get_u_solver
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
gko::stop::ResidualNorm
The ResidualNorm class is a stopping criterion which stops the iteration process when the actual resi...
Definition: residual_norm.hpp:110
gko::preconditioner::Ilu::parse
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
gko::preconditioner::Ilu::parameters_type::factorization_factory
std::shared_ptr< const LinOpFactory > factorization_factory
Factory for the factorization.
Definition: ilu.hpp:163
GKO_ENABLE_LIN_OP_FACTORY
#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
gko::preconditioner::Ilu::transpose
std::unique_ptr< LinOp > transpose() const override
Returns a LinOp representing the transpose of the Transposable object.
Definition: ilu.hpp:290
gko::preconditioner::Ilu::parameters_type::l_solver_factory
std::shared_ptr< const typename l_solver_type::Factory > l_solver_factory
Factory for the L solver.
Definition: ilu.hpp:152
gko::share
detail::shared_type< OwningPointer > share(OwningPointer &&p)
Marks the object pointed to by p as shared.
Definition: utils_helper.hpp:226
gko::transpose
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
gko::preconditioner::Ilu::parameters_type
Definition: ilu.hpp:146
gko::config::registry
This class stores additional context for creating Ginkgo objects from configuration files.
Definition: registry.hpp:168
GKO_ENABLE_BUILD_METHOD
#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
gko::preconditioner::Ilu::Ilu
Ilu(Ilu &&other)
Move-constructs an ILU preconditioner.
Definition: ilu.hpp:374
gko::preconditioner::Ilu::get_l_solver
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
gko::PolymorphicObject::get_executor
std::shared_ptr< const Executor > get_executor() const noexcept
Returns the Executor of the object.
Definition: polymorphic_object.hpp:235
gko::enable_parameters_type
The enable_parameters_type mixin is used to create a base implementation of the factory parameters st...
Definition: abstract_factory.hpp:211
gko::LinOp::get_size
const dim< 2 > & get_size() const noexcept
Returns the size of the operator.
Definition: lin_op.hpp:211
gko::preconditioner::Ilu::conj_transpose
std::unique_ptr< LinOp > conj_transpose() const override
Returns a LinOp representing the conjugate transpose of the Transposable object.
Definition: ilu.hpp:305
gko::remove_complex
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
gko::deferred_factory_parameter
Represents a factory parameter of factory type that can either initialized by a pre-existing factory ...
Definition: abstract_factory.hpp:309
gko::EnableLinOp
The EnableLinOp mixin can be used to provide sensible default implementations of the majority of the ...
Definition: lin_op.hpp:878
gko::LinOp::LinOp
LinOp(const LinOp &)=default
Copy-constructs a LinOp.
gko::preconditioner::Ilu
The Incomplete LU (ILU) preconditioner solves the equation for a given lower triangular matrix L,...
Definition: ilu.hpp:124
gko::EnablePolymorphicObject
This mixin inherits from (a subclass of) PolymorphicObject and provides a base implementation of a ne...
Definition: polymorphic_object.hpp:662