Skip to content

CXX Interface

Devin Matthews edited this page Oct 5, 2019 · 15 revisions

MArray is a convenient interface for accessing and manipulating tensors in C++. It is distributed as a part of TBLIS, and is included tblis namespace as well as the original MArray namespace. MArray provides several categories of tensor utilities:

  • Container and view classes (both fixed and variable dimension).
  • Multi-dimensional iteration helpers.
  • Simple tensor algorithms (such as rotation).
  • Expression templates for vectorized element-wise operations on tensors.

Containers and Views

Variable-dimension Containers: MArray::varray<T,Allocator> (a.k.a. tblis::tensor<T,Allocator>)

T is the type of individual tensor elements. Allocator is optional and defaults to std::allocator<T>.

  • Constructors

    • varray() Create a 0-dimensional tensor. While a zero-dimensional tensor should technically be a scalar (1 element), this instance has no accessible elements.
    • varray(lengths) Create a tensor with the given lengths (the number of elements along each direction). The number of dimensions is automatically determined. The data is zero-initialized. lengths is a braced init list or container (vector, array, etc.) of integral types.
    • varray(lengths, val) Like above but initialize elements to value val
    • varray(lengths, uninitialized) Like above but do not initialize elements. uninitialized is a global variable in the MArray namespace.
    • varray(lengths, layout) Create a zero-initialized tensor with the specified data layout, either ROW_MAJOR or COLUMN_MAJOR.
    • varray(lengths, val, layout) Create a tensor with the specified data layout and elements initialized to val.
    • varray(lengths, uninitialized, layout) Create an uninitialized tensor with the specified data layout.
    • varray(other) Create a copy of the tensor other, which can be either a container or a view.
    • varray(other, layout) Create a copy of the tensor other, with the copied data in the specified layout.
  • Reinitialization

    • reset(...) Takes the same arguments as the constructor. All current data is lost.
  • Resizing

    • resize(lengths) Resize the current tensor and retain any current data. The number of lengths must match the current dimension. Any new elements not corresponding to current data are zero-initialized.
    • resize(lengths, val) Like above but initialize any new elements to val.
  • Push/Pop/Front/Back

    • push_back(val) (1-dimensional tensors only) Append val to the end of the tensor (vector), increasing the length by 1.
    • push_back(dim, other) Increase the length along dimension dim by 1. The new elements are given by the elements of tensor other. other must have one fewer dimension that the current tensor and the lengths of other must match the current lengths, skipping dimension dim.
    • pop_back() (1-dimensional tensors only) Remove the last element of the tensor, decreasing the length by 1.
    • pop_back(dim) Decrease the length along dimension dim by 1.
    • cfront() (1-dimensional tensors only) Return a const-qualified reference to the first element.
    • front() (1-dimensional tensors only) Return a reference to the first element.
    • cback() (1-dimensional tensors only) Return a const-qualified reference to the last element.
    • back() (1-dimensional tensors only) Return a reference to the last element.
    • cfront(dim) Return a read-only view of the first sub-tensor along dimension dim.
    • front(dim) Return a view of the first sub-tensor along dimension dim.
    • cback(dim) Return a read-only view of the last sub-tensor along dimension dim.
    • back(dim) Return a view of the last sub-tensor along dimension dim.
  • Swap

    • swap(other) Swap the dimension, lengths, and contents of this tensor with tensor other, which must be precisely the same type.
  • Assignment

    • operator=(other) Copy the elements of tensor other into this tensor. The dimension and lengths of other must match the current tensor.
    • operator=(val) Set each element of this tensor to val.
  • Views

    • cview() Return a read-only view of this tensor.
    • view() Return a view of this tensor. The view will be read-only if this is const-qualified.
    • cview(other) Free function that is equivalent to other.cview().
    • view(other) Free function that is equivalent to other.view().
    • fix<N>() Return a fixed-dimension view. The current dimension must equal N.
    • fix<N>(other) Free function equivalent to other.fix<N>().
    • shifted(shifts) Return a view where the starting element in each dimension is given by shifts, which can be a braced init list or contained of integral types. Note that shifted views may reference non-existent elements; it is the user's responsibility to ensure that views are well-defined.
    • shifted(dim, shift) Return a view that is shifted only along dimension dim.
    • shifted_down(dim) Equivalent to x.shifted(dim, x.length(dim)).
    • shifted_up(dim) Equivalent to x.shifted(dim, -x.length(dim)).
    • permuted(perm) Return a permuted view. For example: auto B = A.permuted({2,0,1}) implies B(z,x,y) == A(x,y,z).
    • lowered(split) Return a view with possibly lower dimensionality. The ith dimension of the view is the aggregate of dimensions split[i],...,split[i+1]-1. Thus the number of dimensions in the new view is the number of splits minus one. split must be in ascending order.
    • reversed() Return a view where the order of elements along each dimension is reversed.
    • reversed(dim) Return a view where the order of elements along dimension dim is reversed.
  • Element Access

    • operator()(idx0, idx1, ...) Return a reference to the element with the specified indices. All parameters must be integral types.
    • operator()(idx_or_slice0, idx_or_slice1, ...) Return a view of the specified sub-tensor. Each idx_or_sliceN may be either an integral type specifying that a dimension is to be fixed, a range object giving a range of elements to include along a dimension, or slice::all which specifies that all elements are to be included along a dimension. At least one parameter must not be an integral type, and the number of dimensions in the resulting view is given by the number of non-integral (range or all) parameters.
  • Iteration

    • for_each_element(func) Call func for each element in the tensor. func must be callable as either func(val&) or func(val&, idx) where val& is a reference to an element of the tensor and idx is a std::vector<len_type> of indices.
    • for_each_element<N>(func) Call func for each element in the tensor. func must be callable as either func(val&) or func(val&, idx0, ..., idxN-1) where val& is a reference to an element of the tensor and idxN is the index along the Nth dimension. N must equal the number of dimensions.
  • Structure and Data

    • cdata() Return a const-qualified pointer to the element at index (0,...,0).
    • data() Return a pointer to the element at index (0,...,0).
    • length(dim) Return the length of dimension dim.
    • lengths() Return the lengths of all dimensions (as a read-only container).
    • stride(dim) Return the stride of dimension dim.
    • strides() Return the strides of all dimensions (as a read-only container).
    • dimension() Return the number of dimensions.
  • Printing

    • Tensors can be printed in a hierarchical C array-like format using operator<<.
  • Static Helper Functions

    • strides(lengths) Return the strides for a tensor with the given lengths and the default layout.
    • strides(lengths, layout) Return the strides for a tensor with the given lengths and the given layout.
    • size(lengths) Return the number of elements in a tensor with the given lengths.
    • is_contiguous(lengths, strides) Returns a std::pair<bool,stride_type>. The first member is true if the tensor specified by the given lengths and strides is contiguous, i.e. elements are arranged consecutively in memory. The second member gives the total number of elements if the tensor is contiguous.

Variable-dimension Views: MArray::varray_view

T is the type of individual tensor elements, if it is const-qualified then the view is read-only.

  • Constructors

    • varray_view() Create a 0-dimensional view. While a zero-dimensional tensor should technically be a scalar (1 element), this instance has no accessible elements.
    • varray_view(lengths, ptr) Create a view with the given lengths and the given base pointer (pointer to the element with all zero indices). The number of dimensions is automatically determined.
    • varray_view(lengths, ptr, layout) Create a view with the specified data layout, either ROW_MAJOR or COLUMN_MAJOR.
    • varray_view(lengths, ptr, strides) Create a view with the specified data structure given by lengths and strides. strides[i] is the distance in memory between elements along dimension i.
    • varray_view(other) Create a view of the tensor other, which can be either a container or another view.
  • Reinitialization

    • reset(...) Takes the same arguments as the constructor.
  • Front/Back

  • Assignment

  • Views

  • Element Access

  • Iteration

  • Structure and Data

  • Printing

  • Static Helper Functions

    • All functions in these categories are the same as for varray<T>. (Note that the Push/Pop functions are not included).
  • Mutation

    • shift(shifts) Shift the current view in-place, as in shifted(shifts).
    • shift(dim, shift) Shift the current view in-place, as in shifted(dim, shift).
    • shift_down(dim) Shift the current view in-place, as in shifted_down(dim).
    • shift_up(dim) Shift the current view in-place, as in shift_up(dim).
    • permute(perm) Permute the current view in-place, as in permuted(perm).
    • lower(splits) Replace the current view with a lower-dimensional view, as in lowered(splits).
    • reverse() Reverse each dimension of the view, as in reversed().
    • reverse(dim) Reverse dimension dim of the view, as in reversed(dim).
    • data(ptr) Change the base pointer. The old pointer is returned.
    • length(dim, len) Change the length for dimension dim. The old length is returned.
    • stride(dim, str) Change the stride for dimension dim. The old stride is returned.
  • Swap

    • swap(other) Swap the dimension, lengths, and pointer of this view with other, which must be precisely the same type.

Fixed-dimension Containers: MArray::marray<T,N,Allocator>

T is the type of individual tensor elements. N is the number of dimensions (> 0). Allocator is optional and defaults to std::allocator<T>.

matrix<T,Allocator> is a synonym for marray<T,2,Allocator> and row<T,Allocator> is a synonym for marray<T,1,Allocator>.

  • Constructors

    • marray() Create a tensor with all lengths equal to zero; it has no accessible elements.
    • marray(lengths) Create a tensor with the given lengths (the number of elements along each direction). The number of lengths must equal N. The data is zero-initialized. lengths is a braced init list or container (vector, array, etc.) of integral types.
    • marray(lengths, val) Like above but initialize elements to value val
    • marray(lengths, uninitialized) Like above but do not initialize elements. uninitialized is a global variable in the MArray namespace.
    • marray(lengths, layout) Create a zero-initialized tensor with the specified data layout, either ROW_MAJOR or COLUMN_MAJOR.
    • marray(lengths, val, layout) Create a tensor with the specified data layout and elements initialized to val.
    • marray(lengths, uninitialized, layout) Create an uninitialized tensor with the specified data layout.
    • marray(other) Create a copy of the tensor other, which can be either a container or a view.
    • marray(other, layout) Create a copy of the tensor other, with the copied data in the specified layout.
    • marray(nested-init-list) Create a tensor with elements and lengths given by nested-init-list, which must be a nested braced init list with N levels. This constructor can also be invoked by using assignment initialization, e.g. marray<int,2> M = {{0, 1}, {2, 3}}. Note that for 1-dimensional tensors this constructor is ambiguous with marray(lengths).
    • marray(nested-init-list, layout) Same as above but use the specified layout.
  • Reinitialization

    • reset(...) Takes the same arguments as the constructor. All current data is lost.
  • Resizing

    • resize(lengths) Resize the current tensor and retain any current data. The number of lengths must match the current dimension. Any new elements not corresponding to current data are zero-initialized.
    • resize(lengths, val) Like above but initialize any new elements to val.
  • Push/Pop/Front/Back

    • push_back(val) (1-dimensional tensors only) Append val to the end of the tensor (vector), increasing the length by 1.
    • push_back(dim, other) Increase the length along dimension dim by 1. The new elements are given by the elements of tensor other. other must have one fewer dimension that the current tensor and the lengths of other must match the current lengths, skipping dimension dim.
    • push_back<dim>(other) Same as above.
    • pop_back() (1-dimensional tensors only) Remove the last element of the tensor, decreasing the length by 1.
    • pop_back(dim) Decrease the length along dimension dim by 1.
    • pop_back<dim>() Same as above.
    • cfront() (1-dimensional tensors only) Return a const-qualified reference to the first element.
    • front() (1-dimensional tensors only) Return a reference to the first element.
    • cback() (1-dimensional tensors only) Return a const-qualified reference to the last element.
    • back() (1-dimensional tensors only) Return a reference to the last element.
    • cfront(dim) Return a read-only view of the first sub-tensor along dimension dim.
    • front(dim) Return a view of the first sub-tensor along dimension dim.
    • cback(dim) Return a read-only view of the last sub-tensor along dimension dim.
    • back(dim) Return a view of the last sub-tensor along dimension dim.
    • cfront<dim>() Return a read-only view of the first sub-tensor along dimension dim.
    • front<dim>() Return a view of the first sub-tensor along dimension dim.
    • cback<dim>() Return a read-only view of the last sub-tensor along dimension dim.
    • back<dim>() Return a view of the last sub-tensor along dimension dim.
  • Swap

    • swap(other) Swap the dimension, lengths, and contents of this tensor with tensor other, which must be precisely the same type.
  • Assignment

    • operator=(other) Copy the elements of tensor other into this tensor. The dimension and lengths of other must match the current tensor.
    • operator=(val) Set each element of this tensor to val.
    • operator=(nested-init-list) Assign the elements of this tensor as in the constructor version. The lengths of the braced init lists must match the current lengths.
    • operator=(expression) Assign the elements of this tensor from the given element-wise tensor expression. See Expression Templates.
    • operator+=(expression) Modify the elements of this tensor from the given element-wise tensor expression. See Expression Templates.
    • operator-=(expression) Modify the elements of this tensor from the given element-wise tensor expression. See Expression Templates.
    • operator*=(expression) Modify the elements of this tensor from the given element-wise tensor expression. See Expression Templates.
    • operator/=(expression) Modify the elements of this tensor from the given element-wise tensor expression. See Expression Templates.
  • Comparison

    • operator==(other) Return true if this tensor and other are the same size and shape and have the same elements.
    • operator!=(other) Return !(*this == other).
  • Views

    • cview() Return a read-only view of this tensor.
    • view() Return a view of this tensor. The view will be read-only if this is const-qualified.
    • cview(other) Free function that is equivalent to other.cview().
    • view(other) Free function that is equivalent to other.view().
    • vary(other) Free function that returns a variable-dimension view of other.
    • shifted(shifts) Return a view where the starting element in each dimension is given by shifts, which can be a braced init list or contained of integral types. Note that shifted views may reference non-existent elements; it is the user's responsibility to ensure that views are well-defined.
    • shifted(dim, shift) Return a view that is shifted only along dimension dim.
    • shifted_down(dim) Equivalent to x.shifted(dim, x.length(dim)).
    • shifted_up(dim) Equivalent to x.shifted(dim, -x.length(dim)).
    • shifted<dim>(shift) Return a view that is shifted only along dimension dim.
    • shifted_down<dim>() Equivalent to x.shifted(dim, x.length(dim)).
    • shifted_up<dim>() Equivalent to x.shifted(dim, -x.length(dim)).
    • permuted(perm) Return a permuted view. For example: auto B = A.permuted({2,0,1}) implies B(z,x,y) == A(x,y,z).
    • transposed() (2-dimensional tensors only) Equivalent to permuted({1,0}).
    • T() (2-dimensional tensors only) Equivalent to permuted({1,0}).
    • lowered<ndim>(split) Return a view with possibly lower dimensionality. The ith dimension of the view is the aggregate of dimensions split[i],...,split[i+1]-1. Thus the number of dimensions in the new view is the number of splits minus one and must be equal to ndim. split must be in ascending order.
    • reversed() Return a view where the order of elements along each dimension is reversed.
    • reversed(dim) Return a view where the order of elements along dimension dim is reversed.
    • reversed<dim>() Return a view where the order of elements along dimension dim is reversed.
  • Iterators

    • cbegin() Return an iterator of the first dimension. This iterator has its own [cr]begin() and [cr]end() functions to access iterators over successive dimensions. Iterators over the final dimension can be dereferenced to yield references to elements, while intermediate iterators can be dereferenced to yield sliced views(). This version returns an iterator that yields const-qualified references or views.
    • begin() Same as above, but const-ness is determined by the object.
    • crbegin() Same as cbegin() but start at the last element and work backwards.
    • rbegin() Same as begin() but start at the last element and work backwards.
    • cend() Return an end iterator for the first dimension.
    • end() Return an end iterator for the first dimension.
    • crend() Return an end iterator for the first dimension.
    • rend() Return an end iterator for the first dimension.
  • Element Access

    • operator[](idx) Return a temporary object representing a slice along the first dimension at position idx. This object can be further indexed with operator[] or you can call the view() function on it to get a view. If operator[] is used N times, then the result is equivalent to using operator() with all arguments of the operator[] invocations, in the same order. E.g. for marray<double,3> A(...), A[x][y][z] is the same as A(x,y,z).
    • operator[](range) Return a temporary object representing a slice along the first dimension, where elements in the given range object are retained.
    • operator[](slice::all) Return a temporary object representing a slice along the first dimension, where all elements are retained. Useful when a slice along a later dimension is needed. Obtaining a view from a temporary object returned from any operator[] chain (with less than N calls) is equivalent to calling operator[](slice::all) on it to a total of N calls.
    • operator()(idx0, idx1, ...) Return a reference to the element with the specified indices. All parameters must be integral types.
    • operator()(idx_or_slice0, idx_or_slice1, ...) Return a view of the specified sub-tensor. Each idx_or_sliceN may be either an integral type specifying that a dimension is to be fixed, a range object giving a range of elements to include along a dimension, or slice::all which specifies that all elements are to be included along a dimension. At least one parameter must not be an integral type, and the number of dimension in the resulting view is given by the number of non-integral (range or all) parameters.
  • Iteration

    • for_each_element(func) Call func for each element in the tensor. func must be callable as either func(val&) or func(val&, idx0, ..., idxN-1) where val& is a reference to an element of the tensor and idxN is the index along the Nth dimension. N must equal the number of dimensions.
  • Structure and Data

    • cdata() Return a const-qualified pointer to the element at index (0,...,0).
    • data() Return a pointer to the element at index (0,...,0).
    • length(dim) Return the length of dimension dim.
    • length<dim>() Return the length of dimension dim.
    • lengths() Return the lengths of all dimensions (as a read-only container).
    • stride(dim) Return the stride of dimension dim.
    • stride<dim>() Return the stride of dimension dim.
    • strides() Return the strides of all dimensions (as a read-only container).
    • dimension() Return the number of dimensions.
  • Printing

    • Tensors can be printed in a hierarchical C array-like format using operator<<.
  • Static Helper Functions

    • strides(lengths) Return the strides for a tensor with the given lengths and the default layout.
    • strides(lengths, layout) Return the strides for a tensor with the given lengths and the given layout.
    • size(lengths) Return the number of elements in a tensor with the given lengths.
    • is_contiguous(lengths, strides) Returns a std::pair<bool,stride_type>. The first member is true if the tensor specified by the given lengths and strides is contiguous, i.e. elements are arranged consecutively in memory. The second member gives the total number of elements if the tensor is contiguous.

Fixed-dimension Views: MArray::marray_view<T,N>

T is the type of individual tensor elements, if it is const-qualified then the view is read-only. N is the number of dimensions.

  • Constructors

    • marray_view() Create a view with all zero lengths, it has no accessible elements.
    • marray_view(lengths, ptr) Create a view with the given lengths and the given base pointer (pointer to the element with all zero indices).
    • marray_view(lengths, ptr, layout) Create a view with the specified data layout, either ROW_MAJOR or COLUMN_MAJOR.
    • marray_view(lengths, ptr, strides) Create a view with the specified data structure given by lengths and strides. strides[i] is the distance in memory between elements along dimension i.
    • marray_view(other) Create a view of the tensor other, which can be either a container or another view.
  • Reinitialization

    • reset(...) Takes the same arguments as the constructor.
  • Front/Back

  • Assignment

  • Views

  • Element Access

  • Iteration

  • Structure and Data

  • Printing

  • Static Helper Functions

    • All functions in these categories are the same as for varray<T>. (Note that the Push/Pop functions are not included).
  • Mutation

    • shift(shifts) Shift the current view in-place, as in shifted(shifts).
    • shift(dim, shift) Shift the current view in-place, as in shifted(dim, shift).
    • shift_down(dim) Shift the current view in-place, as in shifted_down(dim).
    • shift_up(dim) Shift the current view in-place, as in shift_up(dim).
    • shift<dim>(shift) Shift the current view in-place, as in shifted<dim>(shift).
    • shift_down<dim>() Shift the current view in-place, as in shifted_down<dim>().
    • shift_up<dim>() Shift the current view in-place, as in shift_up<dim>().
    • permute(perm) Permute the current view in-place, as in permuted(perm).
    • transpose() (2-dimensional views only) Transpose the view in-place, as in transposed().
    • reverse() Reverse each dimension of the view, as in reversed().
    • reverse(dim) Reverse dimension dim of the view, as in reversed(dim).
    • reverse<dim>() Reverse dimension dim of the view, as in reversed<dim>().
    • data(ptr) Change the base pointer. The old pointer is returned.
    • length(dim, len) Change the length for dimension dim. The old length is returned.
    • stride(dim, str) Change the stride for dimension dim. The old stride is returned.
    • length<dim>(len) Change the length for dimension dim. The old length is returned.
    • stride<dim>(str) Change the stride for dimension dim. The old stride is returned.
  • Swap

    • swap(other) Swap the dimension, lengths, and pointer of this view with other, which must be precisely the same type.

Algorithms

Rotation

  • rotate(tensor, shifts) This is a free function that rotates the data of the given tensor along each dimension. For example, if shifts[2] == 3, then the element at position (...,i_2,...) is moved to position (...,i_2+3,...). In this version, the shift is applied along all dimensions simulateneously. A shift of 0 indicates no rotation. Elements which would be rotated off the ends of the dimension ranges are wrapped around. The operation is performed in-place, and the execution time is linear in the number of elements and the number of rotated dimensions.
  • rotate(tensor, dim, shift) Same as above, but rotate data only along dimension dim.

Tensor Operations

The seven basic tensor operations (and their matrix and vector counterparts) are accessible via a convenient API:

  • In each function, the scalars alpha and beta are of type T, which must also be the element type (with possible const-qualification) of the tensor objects.
  • Tensors A, B, and C may be any tensor type.
  • While the T template parameter can be deduced in some cases, it is often necessary to specify it explicitly.
  • idx_A, idx_B, and idx_C are index strings (const index_type*) that specify the details of the operation. If index_type is char (the default), then these may be string literals).
  • The comm parameter may be with a tblis::communicator to use for parallelization or tblis::single to specify single-threaded operation. If no comm parameter is given, threading is automatic.
  • In reduction operations, op specifies the type of reduction (one of REDUCE_SUM, REDUCE_SUM_ABS == REDUCE_NORM_1, REDUCE_MAX, REDUCE_MAX_ABS == REDUCE_NORM_INF, REDUCE_MIN, REDUCE_MIN_ABS, or REDUCE_NORM_2).

The functions are:

  • add<T>(alpha, A, idx_A, beta, B, idx_B)
  • add<T>(comm, alpha, A, idx_A, beta, B, idx_B)
  • dot<T>(A, idx_A, B, idx_B) The dot product is returned from the function.
  • dot<T>(A, idx_A, B, idx_B, result&) The dot product is returned in the reference result&.
  • dot<T>(comm, A, idx_A, B, idx_B)
  • dot<T>(comm, A, idx_A, B, idx_B, result&)
  • reduce<T>(op, A, idx_A) The result is returned as a std::pair<T,stride_type> where the first member is the result of the reduction and the second member is the offset of the maximal/minimal value (relative to the base pointer; when defined).
  • reduce<T>(op, A, idx_A, result&, idx&) The result is returned as above, but in the result& and idx& references, respectively.
  • reduce<T>(comm, op, A, idx_A)
  • reduce<T>(comm, op, A, idx_A, result&, idx&)
  • scale<T>(alpha, A, idx_A)
  • scale<T>(comm, alpha, A, idx_A)
  • set<T>(alpha, A, idx_A)
  • set<T>(comm, alpha, A, idx_A)
  • shift<T>(alpha, beta, A, idx_A)
  • shift<T>(comm, alpha, beta, A, idx_A)
  • mult<T>(alpha, A, idx_A, B, idx_B, beta, C, idx_C)
  • mult<T>(comm, alpha, A, idx_A, B, idx_B, beta, C, idx_C)

Expression Templates

TODO