Multidimensional views
There are two view classes in Matpack: view_t
and strided_view_t
.
The former is for contiguous data, while the latter is for strided data as the name implies.
From a performance perspective, contiguous data is generally faster to access than strided data - often by a significant margin.
A view is either created as part of on of the Multidimensional data classes or by accessing a subarray of an existing view.
Accessing a subarray in a contiguous manner will create a view_t
, while accessing a subarray in a strided manner will create a strided_view_t
.
Both classes are implemented in the matpack
namespace. They are template classes that
define their rank and a const
-qualified data type.
They are implemented with public inheritance from std::mdspan
. view_t
inherits from a std::mdspan
with
std::layout_right
layout, while strided_view_t
inherits from a std::mdspan
with
std::layout_stride
layout. Both are defined to have dynamic extents in all dimensions.
Warning
It is very important that you follow the const
-qualification of the data type in all
methods and functions that manually create views. This is followed by the matpack
classes
internally, but if you work around this you can easily create bugs that are hard to find.
Do not manually call the constructors of view_t
or strided_view_t
with data pointers or
custom std::mdspan
without confirming that you are following the const
-qualification.
The view_t
and strided_view_t
classes are not available via the python interface.
Member methods
The list below is all the methods available for both view_t
and strided_view_t
.
The examples will assume a 4x5
matrix of view_t<Complex, 2>
, a complex matrix, called mat
and mat2
.
These are the common member methods for both classes:
size
- the total number of elements in the view. Example:mat.size() == 20
.shape
- the shape of the view. Example:mat.shape() == {4, 5}
.extent(i)
- the extent of the view in dimensioni
. Example:mat.extent(0) == 5
,mat.extent(1) == 4
.stride(i)
- the stride of the view in dimensioni
. Example:mat.stride(0) == 1
,mat.stride(1) == 5
.operator[]
- access elements or sub-views of the view. Examples: -mat[3, 4]
accesses the complex element in row 3 and column 4. -mat[3]
accesses the 4th row of the matrix, returning aview_t
of size 5. -mat[joker, 4]
accesses the 5th column of the matrix, returning it as astrided_view_t
of size 4. -mat[3, StridedRange(0, 2, 2)]
accesses the 1st and 3rd elements of the 4th row of the matrix, returning it as astrided_view_t
of size 2. -mat[3, Range(0, 2)]
accesses the 1st and 2nd elements of the 4th row of the matrix, returning it as aview_t
of size 2.begin
andend
. These are iterators that can be used in range-based for loops. They allow iterations over the inner view. Example:for (auto& vecview : mat) { ... }
, will havevecview
be aview_t
. This is equivalent to orderly access tomat[0]
,mat[1]
,mat[2]
, thenmat[3]
.elem_begin
andelem_end
. There are element-wise iterators. Combined withelemwise_range
, they allow iteration of the data in the view element-by-element. Example:for (auto& elem : elemwise_range(mat)) { ... }
will iterate over all elements in the matrix. This is equivalent to orderly access tomat[0, 0]
,mat[0, 1]
, …,mat[3, 4]
. This type of access is much more efficient forview_t
than forstrided_view_t
.elem_at(i)
. Helper method to access the element at indexi
. This is equivalent to*(elem_begin() + i)
, but forstrided_view_t
, the implementation is the other way around (elem_begin
is implemented as a function ofelem_at
). Example:mat.elem_at(0) == mat[0, 0]
.operator=
- copy the data from another view, or set all elements to a single value. Examples: -mat = 0.0
will set all elements to zero. -mat = mat2
will copy all elements frommat2
tomat
. The two views must have the same shape. The available right-hand side argument types are: - Same type as the*this
value. Will just copy the data (unless&RHS==&LHS
). Must have the same shape. -CONSTANT
of the same date type as that of the view. Will set all elements to this value. - Any otherdata_t
,cdata_t
,view_t
, orstrided_view_t
of the same rank whose data type is convertible to the data type of the view. Will copy the data. Must have the same shape. - Any data type we can access via the helper methodsmdshape
andmdvalue
. These are meta-functions that help access things like the Eigen matrix library types. Will copy the data. Must have the same shape.+=
,-=
,*=
, and/=
. Will perform element-wise addition, subtraction, multiplication, and division, respectively. The right-hand side must either have the same shape or be a constant. Examples:mat += 1.0
,mat -= mat2
,mat *= 2.0
,mat /= mat2
.real
andimag
. Returns astrided_view_t
of only the real and imaginary values of a view to a complex data type. Example:mat.real()
will return astrided_view_t
of the real values ofmat
.ncols
,nrows
,npages
,nbooks
,nshelves
,nvitrines
, andnlibraries
. These are helper methods to access the shape of the view in the right-most 7 dimensions. Ensure you do not call these on too low-rank views. Example:mat.nrows() == 4
.front
andback
. These are helper methods to access the first and last element of the view. Example:mat.front() == mat[0]
,mat.back() == mat[3]
.base_set
. This overwrites the view with another view.base_md
. This returns the basestd::mdspan
of the view. Useful for interfacing with libraries that usestd::mdspan
.
These methods are only available for view_t
but are not available for strided_view_t
:
view_as
- view the data as if it had a different shape. The size must remain constant. Example:mat.view_as(2, 2, 5)
will produce aview_t<Complex, 3>
of shape2x2x5
.
Accessing data - operator[]
The access operator - operator[]
- is a template that accepts any integer, Joker
, Range
, or StridedRange
as its arguments.
They work as follows:
Integers are used to access a single element or dimension in the view.
Joker
is used to access a whole dimension. A value of the typeJoker
available all through ARTS isjoker
.Range
is used to access a range of elements in a dimension. It has a constant stride of 1 to allow for contiguous access even in the view it produces. It has two arguments: the start index of the new view and the number of elements.StridedRange
is used to access a range of elements in a dimension with a custom stride. It has three arguments: the start index of the new view, the number of elements, and the stride.
Calling the access operator on a view_t
will produce either a reference to the internal element, a view_t
, or a strided_view_t
.
Calling the access operator on a strided_view_t
will produce either a reference to the internal element or another strided_view_t
.
The type of the produced view depends on the type of the arguments to the access operator. All integers will produce a reference to the internal element.
For a view_t
, there are two access patterns that produces a view_t
, both which requires that the right-most access arguments are of type Joker
.
These are:
- All the left-most access arguments are integers.
- All the left-most access arguments are first integers and then followed by a single Range
argument.
Any other combination will produce a strided_view_t
. And there is no safe way to go back to a view_t
from a strided_view_t
,
even if you later access your new strided_view_t
in a manner that would normally produce a view_t
.
Tip
Omitting dangling joker
is perfectly fine. The access operator will automatically fill in
all missing right-most arguments with joker
. This is especially useful when you want to access,
for instance, a matrix view from a tensor view of rank 3 or higher.
Relevant files
The relevant files for the data holding core matpack types are:
matpack/matpack_mdspan_view_t.h
- theview_t
class.matpack/matpack_mdspan_strided_view_t.h
- thestrided_view_t
class.