marp | theme | headingDivider |
---|---|---|
true |
ginkgo |
2 |
Early-Adopter Session 11.12.2024
- Non-Native Operators
- Custom
gko::LinOp
Subtypes - Precision Dispatch
Scenario: A user wants to solve linear systems with Ginkgo, but the system operator has no equivalent Ginkgo type.
Happens if:
- Different interpretation of data storage (e.g. storing the diagonal element of each row first in the CSR format)
- Uncommon matrix format is used (e.g. LDU format in OpenFOAM)
- Operator application is implemented matrix-free
- ...
-
Converting to Ginkgo Type
Extract operator entries and store them in
gko::matrix_data
, orgko::device_matrix_data
.Read entries into a Ginkgo matrix with
mtx->read(data)
. -
Wrapping in Custom
gko::LinOp
Create new subtype of
gko::LinOp
that uses non-native operator internally.Pass subtype to Ginkgo solvers.
class MyLinOp: public gko::EnableLinOp<MyLinOp, gko::LinOp> {
// required constructor
MyLinOp(std::shared_ptr<const gko::Executor> exec)
: gko::EnableLinOp<MyLinOp, gko::LinOp>(exec) { ... }
// required method implementations
void apply_impl(const gko::LinOp* b, gko::LinOp* x) const override;
void apply_impl(const gko::LinOp* alpha, const gko::LinOp* b,
const gko::LinOp* beta, gko::LinOp* x) const override;
};
gko::EnableLinOp<MyLinOp, gko::LinOp>
can be shortened to gko::EnableLinOp<MyLinOp>
, since gko::LinOp
is the default base type.
It is an error to derive from gko::LinOp
directly.
class MyLinOp: public gko::EnableLinOp<MyLinOp, gko::LinOp> {
// required constructor
MyLinOp(std::shared_ptr<const gko::Executor> exec)
: gko::EnableLinOp<MyLinOp, gko::LinOp>(exec) { ... }
// Passing the LinOp size to the base class
MyLinOp(std::shared_ptr<const gko::Executor> exec, ...)
: gko::EnableLinOp<MyLinOp, gko::LinOp>(exec, gko::dim<2>(...)) { ... }
...
};
The gko::LinOp
class itself is derived from gko::PolymorphicObject
.
Thus, it is necessary to implement more pure virtual functions than the apply_impl
.
The gko::EnableLinOp
provides sensible default implementations for those virtual functions.
Note: Most implementations will rely on the copy/move assignments, so custom classes without those will fail to compile.
Polymorphic Ginkgo types don't offer public constructors and instead require to use the static create
method.
Custom subtypes may follow this, or just provide a public constructor.
class MyLinOp: public gko::EnableLinOp<MyLinOp> {
friend gko::EnablePolymorphicObject<MyLinOp, gko::LinOp>;
public:
static std::unique_ptr<MyLinOp> create(...);
private:
MyLinOp(std::shared_ptr<const gko::Executor> exec);
// more constructors
};
auto obj = MyLinOp::create(args...);
friend
declaration is required so that EnablePolymorphicObject
can access the constructor.
class MyLinOp: public gko::EnableLinOp<MyLinOp> {
public:
MyLinOp(std::shared_ptr<const gko::Executor> exec);
// more constructors
};
auto obj = std::make_unique<MyLinOp>(args...);
void MyLinop::apply_impl(const gko::LinOp* b, gko::LinOp* x) const{
// get gko::matrix::Dense from gko::LinOp inputs
auto dense_b = gko::as<gko::matrix::Dense<ValueType>>(b);
auto dense_x = gko::as<gko::matrix::Dense<ValueType>>(x);
my_operator(extra_data, dense_b->get_const_values(), x->get_values());
}
Note: gko::as<...>
is a more flexible version of dynamic_cast<...>
, but throws on failure.
Ginkgo provides utilities to handle the dispatch to the runtime gko::matrix::Dense<T>
more easily.
The gko::precision_dispatch<T>
function will either
- cast the inputs to
gko::matrix::Dense<T>
if that is their runtime type - convert the inputs to
gko::matrix::Dense<T>
if that conversion exists - throw an error
Available conversions:
Dense<T1> <--> Dense<T2>
Dense<std::complex<T1>> <--> Dense<std::complex<T2>>
with T1, T2 in {float, double, gko::half}
#include <ginkgo/core/base/precision_dispatch.hpp>
void MyLinop::apply_impl(const gko::LinOp* b, gko::LinOp* x) const{
// let ginkgo handle the casting to the runtime type
gko::precision_dispatch<ValueType>(
[this](auto dense_b, auto dense_x){
my_operator(extra_data, dense_b->get_const_values(), x->get_values());
},
b, x
);
}
The precision dispatch is also available as:
gko::mixed_precision_dispatch
: allows different precision types forb
andx
without conversiongko::[mixed_]precision_dispatch_real_complex
: allows converting complex vectors to real vectors (with 2x columns)gko::experimental::precision_dispatch_real_complex_distributed
: same as above but with distributed vectors
- Derive from
gko::EnableLinOp<MyLinOp>
, not fromgko::LinOp
- The constructor
MyLinOp(std::shared_ptr<const Executor>)
has to be available to the classgko::EnablePolymorphicObject<MyLinOp, gko::LinOp>
- Use precision dispatch to handle runtime types of input/output vectors of
apply_impl