Ginkgo  Generated from pipelines/1868155508 branch based on main. Ginkgo version 1.10.0
A numerical linear algebra library targeting many-core architectures
ic.hpp
1 // SPDX-FileCopyrightText: 2017 - 2025 The Ginkgo authors
2 //
3 // SPDX-License-Identifier: BSD-3-Clause
4 
5 #ifndef GKO_PUBLIC_CORE_PRECONDITIONER_IC_HPP_
6 #define GKO_PUBLIC_CORE_PRECONDITIONER_IC_HPP_
7 
8 
9 #include <memory>
10 #include <type_traits>
11 
12 #include <ginkgo/core/base/abstract_factory.hpp>
13 #include <ginkgo/core/base/composition.hpp>
14 #include <ginkgo/core/base/exception.hpp>
15 #include <ginkgo/core/base/exception_helpers.hpp>
16 #include <ginkgo/core/base/lin_op.hpp>
17 #include <ginkgo/core/base/precision_dispatch.hpp>
18 #include <ginkgo/core/base/type_traits.hpp>
19 #include <ginkgo/core/config/config.hpp>
20 #include <ginkgo/core/config/registry.hpp>
21 #include <ginkgo/core/factorization/par_ic.hpp>
22 #include <ginkgo/core/matrix/dense.hpp>
23 #include <ginkgo/core/solver/solver_traits.hpp>
24 #include <ginkgo/core/solver/triangular.hpp>
25 #include <ginkgo/core/stop/combined.hpp>
26 #include <ginkgo/core/stop/iteration.hpp>
27 #include <ginkgo/core/stop/residual_norm.hpp>
28 
29 
30 namespace gko {
31 namespace preconditioner {
32 namespace detail {
33 
34 
35 template <typename Type>
36 constexpr bool support_ic_parse =
37  std::is_same_v<typename Type::l_solver_type, LinOp>;
38 
39 
40 template <typename Ic, std::enable_if_t<!support_ic_parse<Ic>>* = nullptr>
41 typename Ic::parameters_type ic_parse(
42  const config::pnode& config, const config::registry& context,
43  const config::type_descriptor& td_for_child)
44 {
45  GKO_INVALID_STATE(
46  "preconditioner::Ic only supports limited type for parse.");
47 }
48 
49 template <typename Ic, std::enable_if_t<support_ic_parse<Ic>>* = nullptr>
50 typename Ic::parameters_type ic_parse(
51  const config::pnode& config, const config::registry& context,
52  const config::type_descriptor& td_for_child);
53 
54 
55 } // namespace detail
56 
57 
107 template <typename LSolverTypeOrValueType = solver::LowerTrs<>,
108  typename IndexType = int32>
109 class Ic : public EnableLinOp<Ic<LSolverTypeOrValueType, IndexType>>,
110  public Transposable {
111  friend class EnableLinOp<Ic>;
112  friend class EnablePolymorphicObject<Ic, LinOp>;
113 
114 public:
115  using l_solver_type =
116  std::conditional_t<gko::detail::is_ginkgo_linop<LSolverTypeOrValueType>,
117  LSolverTypeOrValueType, LinOp>;
118  static_assert(std::is_same<gko::detail::transposed_type<
119  gko::detail::transposed_type<l_solver_type>>,
120  l_solver_type>::value,
121  "l_solver_type::transposed_type must be symmetric");
122  using value_type = gko::detail::get_value_type<LSolverTypeOrValueType>;
123  using lh_solver_type = gko::detail::transposed_type<l_solver_type>;
124  using index_type = IndexType;
126 
127  class Factory;
128 
130  : public enable_parameters_type<parameters_type, Factory> {
134  std::shared_ptr<const gko::detail::factory_type<l_solver_type>>
136 
140  std::shared_ptr<const LinOpFactory> factorization_factory{};
141 
142  GKO_DEPRECATED("use with_l_solver instead")
143  parameters_type& with_l_solver_factory(
145  const gko::detail::factory_type<l_solver_type>>
146  solver)
147  {
148  return with_l_solver(std::move(solver));
149  }
150 
158  const gko::detail::factory_type<l_solver_type>>
159  solver)
160  {
161  this->l_solver_generator = std::move(solver);
162  this->deferred_factories["l_solver"] = [](const auto& exec,
163  auto& params) {
164  if (!params.l_solver_generator.is_empty()) {
165  params.l_solver_factory =
166  params.l_solver_generator.on(exec);
167  }
168  };
169  return *this;
170  }
171 
172  GKO_DEPRECATED("use with_factorization instead")
173  parameters_type& with_factorization_factory(
174  deferred_factory_parameter<const LinOpFactory> factorization)
175  {
176  return with_factorization(std::move(factorization));
177  }
178 
179  parameters_type& with_factorization(
181  {
182  this->factorization_generator = std::move(factorization);
183  this->deferred_factories["factorization"] = [](const auto& exec,
184  auto& params) {
185  if (!params.factorization_generator.is_empty()) {
186  params.factorization_factory =
187  params.factorization_generator.on(exec);
188  }
189  };
190  return *this;
191  }
192 
193  private:
194  deferred_factory_parameter<
195  const gko::detail::factory_type<l_solver_type>>
196  l_solver_generator;
197 
198  deferred_factory_parameter<const LinOpFactory> factorization_generator;
199  };
200 
203 
223  const config::pnode& config, const config::registry& context,
224  const config::type_descriptor& td_for_child =
225  config::make_type_descriptor<value_type, index_type>())
226  {
227  // parse is not templated, so we can only use SFINAE later
228  return detail::ic_parse<Ic>(config, context, td_for_child);
229  }
230 
236  std::shared_ptr<const l_solver_type> get_l_solver() const
237  {
238  return l_solver_;
239  }
240 
246  std::shared_ptr<const lh_solver_type> get_lh_solver() const
247  {
248  return lh_solver_;
249  }
250 
251  std::unique_ptr<LinOp> transpose() const override
252  {
253  std::unique_ptr<transposed_type> transposed{
254  new transposed_type{this->get_executor()}};
255  transposed->set_size(gko::transpose(this->get_size()));
256  transposed->l_solver_ =
257  share(as<gko::detail::transposed_type<lh_solver_type>>(
258  as<Transposable>(this->get_lh_solver())->transpose()));
259  transposed->lh_solver_ =
260  share(as<gko::detail::transposed_type<l_solver_type>>(
261  as<Transposable>(this->get_l_solver())->transpose()));
262 
263  return std::move(transposed);
264  }
265 
266  std::unique_ptr<LinOp> conj_transpose() const override
267  {
268  std::unique_ptr<transposed_type> transposed{
269  new transposed_type{this->get_executor()}};
270  transposed->set_size(gko::transpose(this->get_size()));
271  transposed->l_solver_ =
272  share(as<gko::detail::transposed_type<lh_solver_type>>(
273  as<Transposable>(this->get_lh_solver())->conj_transpose()));
274  transposed->lh_solver_ =
275  share(as<gko::detail::transposed_type<l_solver_type>>(
276  as<Transposable>(this->get_l_solver())->conj_transpose()));
277 
278  return std::move(transposed);
279  }
280 
286  Ic& operator=(const Ic& other)
287  {
288  if (&other != this) {
290  auto exec = this->get_executor();
291  l_solver_ = other.l_solver_;
292  lh_solver_ = other.lh_solver_;
293  parameters_ = other.parameters_;
294  if (other.get_executor() != exec) {
295  l_solver_ = gko::clone(exec, l_solver_);
296  lh_solver_ = gko::clone(exec, lh_solver_);
297  }
298  }
299  return *this;
300  }
301 
308  Ic& operator=(Ic&& other)
309  {
310  if (&other != this) {
312  auto exec = this->get_executor();
313  l_solver_ = std::move(other.l_solver_);
314  lh_solver_ = std::move(other.lh_solver_);
315  parameters_ = std::exchange(other.parameters_, parameters_type{});
316  if (other.get_executor() != exec) {
317  l_solver_ = gko::clone(exec, l_solver_);
318  lh_solver_ = gko::clone(exec, lh_solver_);
319  }
320  }
321  return *this;
322  }
323 
328  Ic(const Ic& other) : Ic{other.get_executor()} { *this = other; }
329 
335  Ic(Ic&& other) : Ic{other.get_executor()} { *this = std::move(other); }
336 
337 protected:
338  void apply_impl(const LinOp* b, LinOp* x) const override
339  {
340  // take care of real-to-complex apply
341  precision_dispatch_real_complex<value_type>(
342  [&](auto dense_b, auto dense_x) {
343  this->set_cache_to(dense_b);
344  l_solver_->apply(dense_b, cache_.intermediate);
345  if (lh_solver_->apply_uses_initial_guess()) {
346  dense_x->copy_from(cache_.intermediate);
347  }
348  lh_solver_->apply(cache_.intermediate, dense_x);
349  },
350  b, x);
351  }
352 
353  void apply_impl(const LinOp* alpha, const LinOp* b, const LinOp* beta,
354  LinOp* x) const override
355  {
356  precision_dispatch_real_complex<value_type>(
357  [&](auto dense_alpha, auto dense_b, auto dense_beta, auto dense_x) {
358  this->set_cache_to(dense_b);
359  l_solver_->apply(dense_b, cache_.intermediate);
360  lh_solver_->apply(dense_alpha, cache_.intermediate, dense_beta,
361  dense_x);
362  },
363  alpha, b, beta, x);
364  }
365 
366  explicit Ic(std::shared_ptr<const Executor> exec)
367  : EnableLinOp<Ic>(std::move(exec))
368  {}
369 
370  explicit Ic(const Factory* factory, std::shared_ptr<const LinOp> lin_op)
371  : EnableLinOp<Ic>(factory->get_executor(), lin_op->get_size()),
372  parameters_{factory->get_parameters()}
373  {
374  auto comp =
375  std::dynamic_pointer_cast<const Composition<value_type>>(lin_op);
376  std::shared_ptr<const LinOp> l_factor;
377 
378  // build factorization if we weren't passed a composition
379  if (!comp) {
380  auto exec = lin_op->get_executor();
381 
382  if (!parameters_.factorization_factory) {
383  parameters_.factorization_factory =
384  factorization::ParIc<value_type, index_type>::build()
385  .with_both_factors(false)
386  .on(exec);
387  }
388  auto fact = std::shared_ptr<const LinOp>(
389  parameters_.factorization_factory->generate(lin_op));
390  // ensure that the result is a composition
391  comp = gko::as<const Composition<value_type>>(fact);
392  }
393  // comp must contain one or two factors
394  if (comp->get_operators().size() > 2 || comp->get_operators().empty()) {
395  GKO_NOT_SUPPORTED(comp);
396  }
397  l_factor = comp->get_operators()[0];
398  GKO_ASSERT_IS_SQUARE_MATRIX(l_factor);
399 
400  auto exec = this->get_executor();
401 
402  // If no factories are provided, generate default ones
403  if (!parameters_.l_solver_factory) {
404  // when l_solver_type is LinOp, use LowerTrs as the default one
405  l_solver_ = generate_default_solver<std::conditional_t<
406  std::is_same_v<l_solver_type, LinOp>,
407  solver::LowerTrs<value_type, index_type>, l_solver_type>>(
408  exec, l_factor);
409  // If comp contains both factors: We only check the dimension from
410  // the second factor. However, we still use the l_solver^H not
411  // generate the solver on L^H to preserve the Hermitian property of
412  // this preconditioner. LSolver(L)^H is not always LSolver^H(L^H).
413  if (comp->get_operators().size() == 2) {
414  auto lh_factor = comp->get_operators()[1];
415  GKO_ASSERT_EQUAL_DIMENSIONS(l_factor, lh_factor);
416  }
417  lh_solver_ = as<lh_solver_type>(
418  as<Transposable>(l_solver_)->conj_transpose());
419  } else {
420  l_solver_ = parameters_.l_solver_factory->generate(l_factor);
421  lh_solver_ = as<lh_solver_type>(
422  as<Transposable>(l_solver_)->conj_transpose());
423  }
424  }
425 
433  void set_cache_to(const LinOp* b) const
434  {
435  if (cache_.intermediate == nullptr) {
436  cache_.intermediate =
438  }
439  // Use b as the initial guess for the first triangular solve
440  cache_.intermediate->copy_from(b);
441  }
442 
450  template <typename SolverType>
451  static std::enable_if_t<solver::has_with_criteria<SolverType>::value &&
452  !std::is_same_v<SolverType, LinOp>,
453  std::unique_ptr<SolverType>>
454  generate_default_solver(const std::shared_ptr<const Executor>& exec,
455  const std::shared_ptr<const LinOp>& mtx)
456  {
457  const gko::remove_complex<value_type> default_reduce_residual{1e-4};
458  const unsigned int default_max_iters{
459  static_cast<unsigned int>(mtx->get_size()[0])};
460 
461  return SolverType::build()
462  .with_criteria(
463  gko::stop::Iteration::build().with_max_iters(default_max_iters),
465  .with_reduction_factor(default_reduce_residual))
466  .on(exec)
467  ->generate(mtx);
468  }
469 
473  template <typename SolverType>
474  static std::enable_if_t<!solver::has_with_criteria<SolverType>::value &&
475  !std::is_same_v<SolverType, LinOp>,
476  std::unique_ptr<SolverType>>
477  generate_default_solver(const std::shared_ptr<const Executor>& exec,
478  const std::shared_ptr<const LinOp>& mtx)
479  {
480  return SolverType::build().on(exec)->generate(mtx);
481  }
482 
483 private:
484  std::shared_ptr<const l_solver_type> l_solver_{};
485  std::shared_ptr<const lh_solver_type> lh_solver_{};
496  mutable struct cache_struct {
497  cache_struct() = default;
498  ~cache_struct() = default;
499  cache_struct(const cache_struct&) {}
500  cache_struct(cache_struct&&) {}
501  cache_struct& operator=(const cache_struct&) { return *this; }
502  cache_struct& operator=(cache_struct&&) { return *this; }
503  std::unique_ptr<LinOp> intermediate{};
504  } cache_;
505 };
506 
507 
508 } // namespace preconditioner
509 } // namespace gko
510 
511 
512 #endif // GKO_PUBLIC_CORE_PRECONDITIONER_IC_HPP_
gko::preconditioner::Ic::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: ic.hpp:222
gko::preconditioner::Ic::conj_transpose
std::unique_ptr< LinOp > conj_transpose() const override
Returns a LinOp representing the conjugate transpose of the Transposable object.
Definition: ic.hpp:266
gko::config::pnode
pnode describes a tree of properties.
Definition: property_tree.hpp:28
gko::LinOp
Definition: lin_op.hpp:117
gko::preconditioner::Ic::get_lh_solver
std::shared_ptr< const lh_solver_type > get_lh_solver() const
Returns the solver which is used for the L^H matrix.
Definition: ic.hpp:246
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:433
gko::preconditioner::Ic::parameters_type::factorization_factory
std::shared_ptr< const LinOpFactory > factorization_factory
Factory for the factorization.
Definition: ic.hpp:140
gko::preconditioner::Ic
The Incomplete Cholesky (IC) preconditioner solves the equation for a given lower triangular matrix ...
Definition: ic.hpp:109
gko::preconditioner::Ic::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: ic.hpp:236
gko::preconditioner::Ic::operator=
Ic & operator=(Ic &&other)
Move-assigns an IC preconditioner.
Definition: ic.hpp:308
gko::preconditioner::Ic::Ic
Ic(Ic &&other)
Move-constructs an IC preconditioner.
Definition: ic.hpp:335
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:39
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:173
gko
The Ginkgo namespace.
Definition: abstract_factory.hpp:20
gko::stop::ResidualNorm
The ResidualNorm class is a stopping criterion which stops the iteration process when the actual resi...
Definition: residual_norm.hpp:113
gko::as
std::decay_t< T > * as(U *obj)
Performs polymorphic type conversion.
Definition: utils_helper.hpp:307
gko::preconditioner::Ic::parameters_type::with_l_solver
parameters_type & with_l_solver(deferred_factory_parameter< const gko::detail::factory_type< l_solver_type >> solver)
When LSolverTypeOrValueType is a concrete solver type, this only accepts the factory from the same co...
Definition: ic.hpp:156
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:1017
gko::share
detail::shared_type< OwningPointer > share(OwningPointer &&p)
Marks the object pointed to by p as shared.
Definition: utils_helper.hpp:224
gko::preconditioner::Ic::parameters_type
Definition: ic.hpp:129
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:119
gko::preconditioner::Ic::transpose
std::unique_ptr< LinOp > transpose() const override
Returns a LinOp representing the transpose of the Transposable object.
Definition: ic.hpp:251
gko::preconditioner::Ic::Factory
Definition: ic.hpp:201
gko::preconditioner::Ic::operator=
Ic & operator=(const Ic &other)
Copy-assigns an IC preconditioner.
Definition: ic.hpp:286
gko::preconditioner::Ic::parameters_type::l_solver_factory
std::shared_ptr< const gko::detail::factory_type< l_solver_type > > l_solver_factory
Factory for the L solver.
Definition: ic.hpp:135
gko::config::registry
This class stores additional context for creating Ginkgo objects from configuration files.
Definition: registry.hpp:167
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::Ic::Ic
Ic(const Ic &other)
Copy-constructs an IC preconditioner.
Definition: ic.hpp:328
gko::LinOpFactory
A LinOpFactory represents a higher order mapping which transforms one linear operator into another.
Definition: lin_op.hpp:384
gko::PolymorphicObject::get_executor
std::shared_ptr< const Executor > get_executor() const noexcept
Returns the Executor of the object.
Definition: polymorphic_object.hpp:243
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:210
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:264
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:877
gko::LinOp::LinOp
LinOp(const LinOp &)=default
Copy-constructs a LinOp.
gko::EnablePolymorphicObject
This mixin inherits from (a subclass of) PolymorphicObject and provides a base implementation of a ne...
Definition: polymorphic_object.hpp:667