Ginkgo  Generated from pipelines/1068515030 branch based on master. Ginkgo version 1.7.0
A numerical linear algebra library targeting many-core architectures
Public Types | Public Member Functions | Static Public Attributes | List of all members
gko::range< Accessor > Class Template Reference

A range is a multidimensional view of the memory. More...

#include <ginkgo/core/base/range.hpp>

Public Types

using accessor = Accessor
 The type of the underlying accessor.
 

Public Member Functions

 ~range ()=default
 Use the default destructor.
 
template<typename... AccessorParams, typename = std::enable_if_t< sizeof...(AccessorParams) != 1 || !std::is_same< range, std::decay<detail::head_t<AccessorParams...>>>::value>>
constexpr range (AccessorParams &&... params)
 Creates a new range. More...
 
template<typename... DimensionTypes>
constexpr auto operator() (DimensionTypes &&... dimensions) const -> decltype(std::declval< accessor >()(std::forward< DimensionTypes >(dimensions)...))
 Returns a value (or a sub-range) with the specified indexes. More...
 
template<typename OtherAccessor >
const rangeoperator= (const range< OtherAccessor > &other) const
 
const rangeoperator= (const range &other) const
 Assigns another range to this range. More...
 
 range (const range &other)=default
 
constexpr size_type length (size_type dimension) const
 Returns the length of the specified dimension of the range. More...
 
constexpr const accessoroperator-> () const noexcept
 Returns a pointer to the accessor. More...
 
constexpr const accessorget_accessor () const noexcept
 `Returns a reference to the accessor. More...
 

Static Public Attributes

static constexpr size_type dimensionality = accessor::dimensionality
 The number of dimensions of the range.
 

Detailed Description

template<typename Accessor>
class gko::range< Accessor >

A range is a multidimensional view of the memory.

The range does not store any of its values by itself. Instead, it obtains the values through an accessor (e.g. accessor::row_major) which describes how the indexes of the range map to physical locations in memory.

There are several advantages of using ranges instead of plain memory pointers:

  1. Code using ranges is easier to read and write, as there is no need for index linearizations.
  2. Code using ranges is safer, as it is impossible to accidentally miscalculate an index or step out of bounds, since range accessors perform bounds checking in debug builds. For performance, this can be disabled in release builds by defining the NDEBUG flag.
  3. Ranges enable generalized code, as algorithms can be written independent of the memory layout. This does not impede various optimizations based on memory layout, as it is always possible to specialize algorithms for ranges with specific memory layouts.
  4. Ranges have various pointwise operations predefined, which reduces the amount of loops that need to be written.

Range operations

Ranges define a complete set of pointwise unary and binary operators which extend the basic arithmetic operators in C++, as well as a few pointwise operations and mathematical functions useful in ginkgo, and a couple of non-pointwise operations. Compound assignment (+=, *=, etc.) is not yet supported at this moment. Here is a complete list of operations:

All binary pointwise operations also work as expected if one of the operands is a scalar and the other is a range. The scalar operand will have the effect as if it was a range of the same size as the other operand, filled with the specified scalar.

Two "global" functions transpose and mmul are also supported. transpose transposes the first two dimensions of the range (i.e. transpose(r)(i, j, ...) == r(j, i, ...)). mmul performs a (batched) matrix multiply of the ranges - the first two dimensions represent the matrices, while the rest represent the batch. For example, given the ranges r1 and r2 of dimensions (3, 2, 3) and (2, 4, 3), respectively, mmul(r1, r2) will return a range of dimensions (3, 4, 3), obtained by multiplying the 3 frontal slices of the range, and stacking the result back vertically.

Compound operations

Multiple range operations can be combined into a single expression. For example, an "axpy" operation can be obtained using y = alpha * x + y, where x an y are ranges, and alpha is a scalar. Range operations are optimized for memory access, and the above code does not allocate additional storage for intermediate ranges alpha * x or alpha * x + y. In fact, the entire computation is done during the assignment, and the results of operations + and * only register the data, and the types of operations that will be computed once the results are needed.

It is possible to store and reuse these intermediate expressions. The following example will overwrite the range x with it's 4th power:

{c++}
auto square = x * x; // this is range constructor, not range assignment!
x = square; // overwrites x with x*x (this is range assignment)
x = square; // overwrites new x (x*x) with (x*x)*(x*x) (as is this)

Caveats

__mmul is not a highly-optimized BLAS-3 version of the matrix multiplication.__ The current design of ranges and accessors prevents that, so if you need a high-performance matrix multiplication, you should use one of the libraries that provide that, or implement your own (you can use pointwise range operations to help simplify that). However, range design might get improved in the future to allow efficient implementations of BLAS-3 kernels.

Aliasing the result range in mmul and transpose is not allowed. Constructs like A = transpose(A), A = mmul(A, A), or A = mmul(A, A) + C lead to undefined behavior. However, aliasing input arguments is allowed: C = mmul(A, A), and even C = mmul(A, A) + C is valid code (in the last example, only pointwise operations are aliased). C = mmul(A, A + C) is not valid though.

Examples

The range unit tests in core/test/base/range.cpp contain lots of simple 1-line examples of range operations. The accessor unit tests in core/test/base/range.cpp show how to use ranges with concrete accessors, and how to use range slices using spans as arguments to range function call operator. Finally, examples/range contains a complete example where ranges are used to implement a simple version of the right-looking LU factorization.

Template Parameters
Accessorunderlying accessor of the range

Constructor & Destructor Documentation

◆ range()

template<typename Accessor>
template<typename... AccessorParams, typename = std::enable_if_t< sizeof...(AccessorParams) != 1 || !std::is_same< range, std::decay<detail::head_t<AccessorParams...>>>::value>>
constexpr gko::range< Accessor >::range ( AccessorParams &&...  params)
inlineexplicitconstexpr

Creates a new range.

Template Parameters
AccessorParamtypes of parameters forwarded to the accessor constructor
Parameters
paramsparameters forwarded to Accessor constructor.

Member Function Documentation

◆ get_accessor()

template<typename Accessor>
constexpr const accessor& gko::range< Accessor >::get_accessor ( ) const
inlineconstexprnoexcept

`Returns a reference to the accessor.

Returns
reference to the accessor

Referenced by gko::range< Accessor >::operator=().

◆ length()

template<typename Accessor>
constexpr size_type gko::range< Accessor >::length ( size_type  dimension) const
inlineconstexpr

Returns the length of the specified dimension of the range.

Parameters
dimensionthe dimensions whose length is returned
Returns
the length of the dimension-th dimension of the range

Referenced by gko::matrix_data< ValueType, IndexType >::matrix_data().

◆ operator()()

template<typename Accessor>
template<typename... DimensionTypes>
constexpr auto gko::range< Accessor >::operator() ( DimensionTypes &&...  dimensions) const -> decltype(std::declval<accessor>()( std::forward<DimensionTypes>(dimensions)...))
inlineconstexpr

Returns a value (or a sub-range) with the specified indexes.

Template Parameters
DimensionTypesThe types of indexes. Supported types depend on the underlying accessor, but are usually either integer types or spans. If at least one index is a span, the returned value will be a sub-range.
Parameters
dimensionsthe indexes of the values.
Returns
a value on position (dimensions...).

References gko::range< Accessor >::dimensionality.

◆ operator->()

template<typename Accessor>
constexpr const accessor* gko::range< Accessor >::operator-> ( ) const
inlineconstexprnoexcept

Returns a pointer to the accessor.

Can be used to access data and functions of a specific accessor.

Returns
pointer to the accessor

◆ operator=() [1/2]

template<typename Accessor>
const range& gko::range< Accessor >::operator= ( const range< Accessor > &  other) const
inline

Assigns another range to this range.

The order of assignment is defined by the accessor of this range, thus the memory access will be optimized for the resulting range, and not for the other range. If the sizes of two ranges do not match, the result is undefined. Sizes of the ranges are checked at runtime in debug builds.

Note
Temporary accessors are allowed to define the implementation of the assignment as deleted, so do not expect r1 * r2 = r2 to work.
Parameters
otherthe range to copy the data from

References gko::range< Accessor >::get_accessor().

◆ operator=() [2/2]

template<typename Accessor>
template<typename OtherAccessor >
const range& gko::range< Accessor >::operator= ( const range< OtherAccessor > &  other) const
inline

This is a version of the function which allows to copy between ranges of different accessors.

Template Parameters
OtherAccessoraccessor of the other range

The documentation for this class was generated from the following file: