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 one 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_tof size 5. -mat[joker, 4]accesses the 5th column of the matrix, returning it as astrided_view_tof 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_tof size 2. -mat[3, Range(0, 2)]accesses the 1st and 2nd elements of the 4th row of the matrix, returning it as aview_tof size 2.beginandend. 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 havevecviewbe aview_t. This is equivalent to orderly access tomat[0],mat[1],mat[2], thenmat[3].elem_beginandelem_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_tthan 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_beginis 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.0will set all elements to zero. -mat = mat2will copy all elements frommat2tomat. The two views must have the same shape. The available right-hand side argument types are: - Same type as the*thisvalue. Will just copy the data (unless&RHS==&LHS). Must have the same shape. -CONSTANTof 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_tof 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 methodsmdshapeandmdvalue. 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.realandimag. Returns astrided_view_tof only the real and imaginary values of a view to a complex data type. Example:mat.real()will return astrided_view_tof 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.frontandback. 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::mdspanof 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.
Jokeris used to access a whole dimension. A value of the typeJokeravailable all through ARTS isjoker.Rangeis 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.StridedRangeis 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_tclass.matpack/matpack_mdspan_strided_view_t.h- thestrided_view_tclass.