Linear Operators

Linear Operators#

Reference API: Linear Operators
Reference API
Linear Operators

A module dedicated to the implementation and usage of the Linear operators in Ginkgo. More...

Collaboration diagram for Linear Operators:

Modules

 Factorizations
 A module dedicated to the implementation and usage of the Factorizations in Ginkgo.
 
 SpMV employing different Matrix formats
 A module dedicated to the implementation and usage of the various Matrix Formats in Ginkgo.
 
 Preconditioners
 A module dedicated to the implementation and usage of the Preconditioners in Ginkgo.
 
 Solvers
 A module dedicated to the implementation and usage of the Solvers in Ginkgo.
 

Classes

class  gko::Combination< ValueType >
 
class  gko::Composition< ValueType >
 
class  gko::LinOpFactory
 
class  gko::ReadableFromMatrixData< ValueType, IndexType >
 
class  gko::WritableToMatrixData< ValueType, IndexType >
 
class  gko::DiagonalLinOpExtractable
 
class  gko::DiagonalExtractable< ValueType >
 
class  gko::EnableAbsoluteComputation< AbsoluteLinOp >
 
class  gko::EnableLinOp< ConcreteLinOp, PolymorphicBase >
 
class  gko::Perturbation< ValueType >
 
class  gko::multigrid::FixedCoarsening< ValueType, IndexType >
 
class  gko::multigrid::Pgm< ValueType, IndexType >
 
class  gko::solver::Minres< ValueType >
 

Macros

#define GKO_CREATE_FACTORY_PARAMETERS(_parameters_name, _factory_name)
 
#define GKO_ENABLE_BUILD_METHOD(_factory_name)
 
#define GKO_FACTORY_PARAMETER(_name, ...)
 
#define GKO_FACTORY_PARAMETER_SCALAR(_name, _default)    GKO_FACTORY_PARAMETER(_name, _default)
 
#define GKO_FACTORY_PARAMETER_VECTOR(_name, ...)    GKO_FACTORY_PARAMETER(_name, __VA_ARGS__)
 
#define GKO_ENABLE_LIN_OP_FACTORY(_lin_op, _parameters_name, _factory_name)
 

Typedefs

template<typename ConcreteFactory , typename ConcreteLinOp , typename ParametersType , typename PolymorphicBase = LinOpFactory>
using gko::EnableDefaultLinOpFactory = EnableDefaultFactory< ConcreteFactory, ConcreteLinOp, ParametersType, PolymorphicBase >
 

Detailed Description

A module dedicated to the implementation and usage of the Linear operators in Ginkgo.

Below we elaborate on one of the most important concepts of Ginkgo, the linear operator. The linear operator (LinOp) is a base class for all linear algebra objects in Ginkgo. The main benefit of having a single base class for the entire collection of linear algebra objects (as opposed to having separate hierarchies for matrices, solvers and preconditioners) is the generality it provides.

Advantages of this approach and usage

A common interface often allows for writing more generic code. If a user's routine requires only operations provided by the LinOp interface, the same code can be used for any kind of linear operators, independent of whether these are matrices, solvers or preconditioners. This feature is also extensively used in Ginkgo itself. For example, a preconditioner used inside a Krylov solver is a LinOp. This allows the user to supply a wide variety of preconditioners: either the ones which were designed to be used in this scenario (like ILU or block-Jacobi), a user-supplied matrix which is known to be a good preconditioner for the specific problem, or even another solver (e.g., if constructing a flexible GMRES solver).

For example, a matrix free implementation would require the user to provide an apply implementation and instead of passing the generated matrix to the solver, they would have to provide their apply implementation for all the executors needed and no other code needs to be changed. See The custom-matrix-format program example for more details.

Linear operator as a concept

The linear operator (LinOp) is a base class for all linear algebra objects in Ginkgo. The main benefit of having a single base class for the entire collection of linear algebra objects (as opposed to having separate hierarchies for matrices, solvers and preconditioners) is the generality it provides.

First, since all subclasses provide a common interface, the library users are exposed to a smaller set of routines. For example, a matrix-vector product, a preconditioner application, or even a system solve are just different terms given to the operation of applying a certain linear operator to a vector. As such, Ginkgo uses the same routine name, LinOp::apply() for each of these operations, where the actual operation performed depends on the type of linear operator involved in the operation.

Second, a common interface often allows for writing more generic code. If a user's routine requires only operations provided by the LinOp interface, the same code can be used for any kind of linear operators, independent of whether these are matrices, solvers or preconditioners. This feature is also extensively used in Ginkgo itself. For example, a preconditioner used inside a Krylov solver is a LinOp. This allows the user to supply a wide variety of preconditioners: either the ones which were designed to be used in this scenario (like ILU or block-Jacobi), a user-supplied matrix which is known to be a good preconditioner for the specific problem, or even another solver (e.g., if constructing a flexible GMRES solver).

A key observation for providing a unified interface for matrices, solvers, and preconditioners is that the most common operation performed on all of them can be expressed as an application of a linear operator to a vector:

  • the sparse matrix-vector product with a matrix \(A\) is a linear operator application \(y = Ax\);
  • the application of a preconditioner is a linear operator application \(y = M^{-1}x\), where \(M\) is an approximation of the original system matrix \(A\) (thus a preconditioner represents an "approximate inverse" operator \(M^{-1}\)).
  • the system solve \(Ax = b\) can be viewed as linear operator application \(x = A^{-1}b\) (it goes without saying that the implementation of linear system solves does not follow this conceptual idea), so a linear system solver can be viewed as a representation of the operator \(A^{-1}\).

Finally, direct manipulation of LinOp objects is rarely required in simple scenarios. As an illustrative example, one could construct a fixed-point iteration routine \(x_{k+1} = Lx_k + b\) as follows:

std::unique_ptr<matrix::Dense<>> calculate_fixed_point(
int iters, const LinOp *L, const matrix::Dense<> *x0
const matrix::Dense<> *b)
{
auto x = gko::clone(x0);
auto tmp = gko::clone(x0);
auto one = Dense<>::create(L->get_executor(), {1.0,});
for (int i = 0; i < iters; ++i) {
L->apply(tmp, x);
x->add_scaled(one, b);
tmp->copy_from(x);
}
return x;
}
constexpr T one()
Definition math.hpp:630
detail::cloned_type< Pointer > clone(const Pointer &p)
Definition utils_helper.hpp:173

Here, if \(L\) is a matrix, LinOp::apply() refers to the matrix vector product, and L->apply(a, b) computes \(b = L \cdot a\). x->add_scaled(one, b) is the axpy vector update \(x:=x+b\).

The interesting part of this example is the apply() routine at line 4 of the function body. Since this routine is part of the LinOp base class, the fixed-point iteration routine can calculate a fixed point not only for matrices, but for any type of linear operator.

Linear Operators

Macro Definition Documentation

◆ GKO_CREATE_FACTORY_PARAMETERS

#define GKO_CREATE_FACTORY_PARAMETERS (   _parameters_name,
  _factory_name 
)
Value:
public: \
class _factory_name; \
struct _parameters_name##_type \
: public ::gko::enable_parameters_type<_parameters_name##_type, \
_factory_name>
Definition abstract_factory.hpp:211

This Macro will generate a new type containing the parameters for the factory _factory_name. For more details, see GKO_ENABLE_LIN_OP_FACTORY(). It is required to use this macro before calling the macro GKO_ENABLE_LIN_OP_FACTORY(). It is also required to use the same names for all parameters between both macros.

Parameters
_parameters_namename of the parameters member in the class
_factory_namename of the generated factory type

◆ GKO_ENABLE_BUILD_METHOD

#define GKO_ENABLE_BUILD_METHOD (   _factory_name)
Value:
static auto build()->decltype(_factory_name::create()) \
{ \
return _factory_name::create(); \
} \
static_assert(true, \
"This assert is used to counter the false positive extra " \
"semi-colon warnings")

Defines a build method for the factory, simplifying its construction by removing the repetitive typing of factory's name.

Parameters
_factory_namethe factory for which to define the method

◆ 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 defined in.

It is required to first call the macro GKO_CREATE_FACTORY_PARAMETERS() before this one in order to instantiate the parameters type first.

The list of parameters for the factory should be defined in a code block after the macro definition, and should contain a list of GKO_FACTORY_PARAMETER_* declarations. The class should provide a constructor with signature _lin_op(const _factory_name *, std::shared_ptr<const LinOp>) which the factory will use a callback to construct the object.

A minimal example of a linear operator is the following:

++
struct MyLinOp : public EnableLinOp<MyLinOp> {
GKO_ENABLE_LIN_OP_FACTORY(MyLinOp, my_parameters, Factory) {
// a factory parameter named "my_value", of type int and default
// value of 5
int GKO_FACTORY_PARAMETER_SCALAR(my_value, 5);
// a factory parameter named `my_pair` of type `std::pair<int,int>`
// and default value {5, 5}
std::pair<int, int> GKO_FACTORY_PARAMETER_VECTOR(my_pair, 5, 5);
};
// constructor needed by EnableLinOp
explicit MyLinOp(std::shared_ptr<const Executor> exec) {
: EnableLinOp<MyLinOp>(exec) {}
// constructor needed by the factory
explicit MyLinOp(const Factory *factory,
std::shared_ptr<const LinOp> matrix)
: EnableLinOp<MyLinOp>(factory->get_executor()), matrix->get_size()),
// store factory's parameters locally
my_parameters_{factory->get_parameters()},
{
int value = my_parameters_.my_value;
// do something with value
}
#define GKO_FACTORY_PARAMETER_SCALAR(_name, _default)
Definition abstract_factory.hpp:445
#define GKO_ENABLE_LIN_OP_FACTORY(_lin_op, _parameters_name, _factory_name)
Definition lin_op.hpp:1016
#define GKO_FACTORY_PARAMETER_VECTOR(_name,...)
Definition abstract_factory.hpp:461

MyLinOp can then be created as follows:

++
auto exec = gko::ReferenceExecutor::create();
// create a factory with default `my_value` parameter
auto fact = MyLinOp::build().on(exec);
// create a operator using the factory:
auto my_op = fact->generate(gko::matrix::Identity::create(exec, 2));
std::cout << my_op->get_my_parameters().my_value; // prints 5
// create a factory with custom `my_value` parameter
auto fact = MyLinOp::build().with_my_value(0).on(exec);
// create a operator using the factory:
auto my_op = fact->generate(gko::matrix::Identity::create(exec, 2));
std::cout << my_op->get_my_parameters().my_value; // prints 0
static std::unique_ptr< Identity > create(std::shared_ptr< const Executor > exec, dim< 2 > size)
Note
It is possible to combine both the #GKO_CREATE_FACTORY_PARAMETER_*() macros with this one in a unique macro for class templates (not with regular classes). Splitting this into two distinct macros allows to use them in all contexts. See https://stackoverflow.com/q/50202718/9385966 for more details.
Parameters
_lin_opconcrete operator for which the factory is to be created [CRTP parameter]
_parameters_namename of the parameters member in the class (its type is <_parameters_name>_type, the protected member's name is <_parameters_name>_, and the public getter's name is get_<_parameters_name>())
_factory_namename of the generated factory type

◆ GKO_FACTORY_PARAMETER

#define GKO_FACTORY_PARAMETER (   _name,
  ... 
)
Value:
_name{__VA_ARGS__}; \
\
template <typename... Args> \
auto with_##_name(Args&&... _value) \
->std::decay_t<decltype(*(this->self()))>& \
{ \
using type = decltype(this->_name); \
this->_name = type{std::forward<Args>(_value)...}; \
return *(this->self()); \
} \
static_assert(true, \
"This assert is used to counter the false positive extra " \
"semi-colon warnings")

Creates a factory parameter in the factory parameters structure.

Parameters
_namename of the parameter
__VA_ARGS__default value of the parameter
See also
GKO_ENABLE_LIN_OP_FACTORY for more details, and usage example

◆ GKO_FACTORY_PARAMETER_SCALAR

#define GKO_FACTORY_PARAMETER_SCALAR (   _name,
  _default 
)     GKO_FACTORY_PARAMETER(_name, _default)

Creates a scalar factory parameter in the factory parameters structure.

Scalar in this context means that the constructor for this type only takes a single parameter.

Parameters
_namename of the parameter
_defaultdefault value of the parameter
See also
GKO_ENABLE_LIN_OP_FACTORY for more details, and usage example

◆ GKO_FACTORY_PARAMETER_VECTOR

#define GKO_FACTORY_PARAMETER_VECTOR (   _name,
  ... 
)     GKO_FACTORY_PARAMETER(_name, __VA_ARGS__)

Creates a vector factory parameter in the factory parameters structure.

Vector in this context means that the constructor for this type takes multiple parameters.

Parameters
_namename of the parameter
_defaultdefault value of the parameter
See also
GKO_ENABLE_LIN_OP_FACTORY for more details, and usage example

Typedef Documentation

◆ EnableDefaultLinOpFactory

template<typename ConcreteFactory , typename ConcreteLinOp , typename ParametersType , typename PolymorphicBase = LinOpFactory>
using gko::EnableDefaultLinOpFactory = typedef EnableDefaultFactory<ConcreteFactory, ConcreteLinOp, ParametersType, PolymorphicBase>

This is an alias for the EnableDefaultFactory mixin, which correctly sets the template parameters to enable a subclass of LinOpFactory.

Template Parameters
ConcreteFactorythe concrete factory which is being implemented [CRTP parameter]
ConcreteLinOpthe concrete LinOp type which this factory produces, needs to have a constructor which takes a const ConcreteFactory *, and an std::shared_ptr<const LinOp> as parameters.
ParametersTypea subclass of enable_parameters_type template which defines all of the parameters of the factory
PolymorphicBaseparent of ConcreteFactory in the polymorphic hierarchy, has to be a subclass of LinOpFactory