Ginkgo Generated from branch based on main. Ginkgo version 1.10.0
A numerical linear algebra library targeting many-core architectures
Loading...
Searching...
No Matches
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
30namespace gko {
31namespace preconditioner {
32namespace detail {
33
34
35template <typename Type>
36constexpr bool support_ic_parse =
37 std::is_same_v<typename Type::l_solver_type, LinOp>;
38
39
40template <typename Ic, std::enable_if_t<!support_ic_parse<Ic>>* = nullptr>
41typename 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
49template <typename Ic, std::enable_if_t<support_ic_parse<Ic>>* = nullptr>
50typename 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
107template <typename LSolverTypeOrValueType = solver::LowerTrs<>,
108 typename IndexType = int32>
109class Ic : public EnableLinOp<Ic<LSolverTypeOrValueType, IndexType>>,
110 public Transposable {
111 friend class EnableLinOp<Ic>;
112 friend class EnablePolymorphicObject<Ic, LinOp>;
113
114public:
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;
125 using transposed_type = Ic<LSolverTypeOrValueType, 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(
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
201 GKO_ENABLE_LIN_OP_FACTORY(Ic, parameters, Factory);
203
222 static parameters_type parse(
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>>(
259 transposed->lh_solver_ =
260 share(as<gko::detail::transposed_type<l_solver_type>>(
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>>(
274 transposed->lh_solver_ =
275 share(as<gko::detail::transposed_type<l_solver_type>>(
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
337protected:
338 void apply_impl(const LinOp* b, LinOp* x) const override
339 {
340 // take care of real-to-complex apply
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 {
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
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),
464 gko::stop::ResidualNorm<value_type>::build()
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
483private:
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_
The EnableLinOp mixin can be used to provide sensible default implementations of the majority of the ...
Definition lin_op.hpp:879
This mixin inherits from (a subclass of) PolymorphicObject and provides a base implementation of a ne...
Definition polymorphic_object.hpp:668
A LinOpFactory represents a higher order mapping which transforms one linear operator into another.
Definition lin_op.hpp:385
Definition lin_op.hpp:117
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:210
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:243
Linear operators which support transposition should implement the Transposable interface.
Definition lin_op.hpp:433
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:167
This class describes the value and index types to be used when building a Ginkgo type from a configur...
Definition type_descriptor.hpp:39
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.
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
Ic & operator=(const Ic &other)
Copy-assigns an IC preconditioner.
Definition ic.hpp:286
Ic & operator=(Ic &&other)
Move-assigns an IC preconditioner.
Definition ic.hpp:308
std::unique_ptr< LinOp > transpose() const override
Returns a LinOp representing the transpose of the Transposable object.
Definition ic.hpp:251
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
Ic(Ic &&other)
Move-constructs an IC preconditioner.
Definition ic.hpp:335
std::unique_ptr< LinOp > conj_transpose() const override
Returns a LinOp representing the conjugate transpose of the Transposable object.
Definition ic.hpp:266
Ic(const Ic &other)
Copy-constructs an IC preconditioner.
Definition ic.hpp:328
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
#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:1017
The Factorization namespace.
Definition ic.hpp:26
@ factory
LinOpFactory events.
Definition profiler_hook.hpp:32
The Preconditioner namespace.
Definition gauss_seidel.hpp:19
The ginkgo Solve namespace.
Definition bicg.hpp:28
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:264
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:97
detail::cloned_type< Pointer > clone(const Pointer &p)
Creates a unique clone of the object pointed to by p.
Definition utils_helper.hpp:173
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
std::decay_t< T > * as(U *obj)
Performs polymorphic type conversion.
Definition utils_helper.hpp:307
detail::shared_type< OwningPointer > share(OwningPointer &&p)
Marks the object pointed to by p as shared.
Definition utils_helper.hpp:224
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
std::shared_ptr< const LinOpFactory > factorization_factory
Factory for the factorization.
Definition ic.hpp:140
std::shared_ptr< const gko::detail::factory_type< l_solver_type > > l_solver_factory
Factory for the L solver.
Definition ic.hpp:135