diff --git a/core/test/base/range_accessors.cpp b/core/test/base/range_accessors.cpp index eecac4ae60f..7a5ffe7c18a 100644 --- a/core/test/base/range_accessors.cpp +++ b/core/test/base/range_accessors.cpp @@ -62,6 +62,35 @@ class RowMajorAccessor : public ::testing::Test { }; +TEST_F(RowMajorAccessor, CanCreateWithDim) +{ + row_major_int_range r2{data, gko::dim<2>{3, 2}, 3u}; + + EXPECT_EQ(r2(0, 0), 1); + EXPECT_EQ(r2(0, 1), 2); + EXPECT_EQ(r2(1, 0), 3); + EXPECT_EQ(r2(1, 1), 4); + EXPECT_EQ(r2(2, 0), 5); + EXPECT_EQ(r2(2, 1), 6); +} + + +TEST_F(RowMajorAccessor, CanCreateDefaultStride) +{ + row_major_int_range r2{data, gko::dim<2>{3, 3}}; + + EXPECT_EQ(r2(0, 0), 1); + EXPECT_EQ(r2(0, 1), 2); + EXPECT_EQ(r2(0, 2), -1); + EXPECT_EQ(r2(1, 0), 3); + EXPECT_EQ(r2(1, 1), 4); + EXPECT_EQ(r2(1, 2), -2); + EXPECT_EQ(r2(2, 0), 5); + EXPECT_EQ(r2(2, 1), 6); + EXPECT_EQ(r2(2, 2), -3); +} + + TEST_F(RowMajorAccessor, CanAccessData) { EXPECT_EQ(r(0, 0), 1); @@ -135,4 +164,248 @@ TEST_F(RowMajorAccessor, CanAssignSubranges) } +class RowMajorAccessor3d : public ::testing::Test { +protected: + using span = gko::span; + static constexpr gko::size_type dimensionality{3}; + + using row_major_int_range = + gko::range>; + + // clang-format off + int data[2 * 3 * 4]{ + 1, 2, -1, 11, + 3, 4, -2, 12, + 5, 6, -3, 13, + + 21, 22, 23, 24, + 25, 26, 27, 28, + 29, 30, 31, 32 + }; + // clang-format on + const gko::dim dim1{2, 3, 4}; + const gko::dim dim2{2, 2, 3}; + row_major_int_range default_r{data, dim1}; + row_major_int_range custom_r{ + data, dim2, + std::array{12, 4}}; +}; + + +TEST_F(RowMajorAccessor3d, CanAccessData) +{ + EXPECT_EQ(default_r(0, 0, 0), 1); + EXPECT_EQ(custom_r(0, 0, 0), 1); + EXPECT_EQ(default_r(0, 1, 0), 3); + EXPECT_EQ(custom_r(0, 1, 0), 3); + EXPECT_EQ(default_r(0, 1, 3), 12); + EXPECT_EQ(default_r(0, 2, 2), -3); + EXPECT_EQ(default_r(1, 2, 1), 30); + EXPECT_EQ(default_r(1, 2, 2), 31); + EXPECT_EQ(default_r(1, 2, 3), 32); +} + + +TEST_F(RowMajorAccessor3d, CanWriteData) +{ + default_r(0, 0, 0) = 4; + custom_r(1, 1, 1) = 100; + + EXPECT_EQ(default_r(0, 0, 0), 4); + EXPECT_EQ(custom_r(0, 0, 0), 4); + EXPECT_EQ(default_r(1, 1, 1), 100); + EXPECT_EQ(custom_r(1, 1, 1), 100); +} + + +TEST_F(RowMajorAccessor3d, CanCreateSubrange) +{ + auto subr = custom_r(span{0, 2}, span{1, 2}, span{1, 3}); + + EXPECT_EQ(subr(0, 0, 0), 4); + EXPECT_EQ(subr(0, 0, 1), -2); + EXPECT_EQ(subr(1, 0, 0), 26); + EXPECT_EQ(subr(1, 0, 1), 27); +} + + +TEST_F(RowMajorAccessor3d, CanCreateRowVector) +{ + auto subr = default_r(1u, 2u, span{0, 2}); + + EXPECT_EQ(subr(0, 0, 0), 29); + EXPECT_EQ(subr(0, 0, 1), 30); +} + + +TEST_F(RowMajorAccessor3d, CanCreateColumnVector) +{ + auto subr = default_r(span{0u, 2u}, 1u, 3u); + + EXPECT_EQ(subr(0, 0, 0), 12); + EXPECT_EQ(subr(1, 0, 0), 28); +} + + +TEST_F(RowMajorAccessor3d, CanAssignValues) +{ + default_r(1, 1, 1) = default_r(0, 0, 0); + + EXPECT_EQ(data[17], 1); +} + + +TEST_F(RowMajorAccessor3d, CanAssignSubranges) +{ + default_r(1u, span{0, 2}, span{0, 3}) = + custom_r(0u, span{0, 2}, span{0, 3}); + + EXPECT_EQ(data[12], 1); + EXPECT_EQ(data[13], 2); + EXPECT_EQ(data[14], -1); + EXPECT_EQ(data[15], 24); + EXPECT_EQ(data[16], 3); + EXPECT_EQ(data[17], 4); + EXPECT_EQ(data[18], -2); + EXPECT_EQ(data[19], 28); + EXPECT_EQ(data[20], 29); + EXPECT_EQ(data[21], 30); + EXPECT_EQ(data[22], 31); + EXPECT_EQ(data[23], 32); +} + + +class BlockColMajorAccessor3d : public ::testing::Test { +protected: + using span = gko::span; + static constexpr gko::size_type dimensionality{3}; + + using blk_col_major_range = + gko::range>; + + // clang-format off + int data[2 * 3 * 4]{ + 1, 3, 5, + 2, 4, 6, + -1,-2,-3, + 11,12,13, + + 21,25,29, + 22,26,30, + 23,27,31, + 24,28,32 + + /* This matrix actually looks like + 1, 2, -1, 11, + 3, 4, -2, 12, + 5, 6, -3, 13, + + 21, 22, 23, 24, + 25, 26, 27, 28, + 29, 30, 31, 32 + */ + }; + // clang-format on + const gko::dim dim1{2, 3, 4}; + const gko::dim dim2{2, 2, 3}; + blk_col_major_range default_r{data, dim1}; + blk_col_major_range custom_r{ + data, dim2, + std::array{12, 3}}; +}; + + +TEST_F(BlockColMajorAccessor3d, ComputesCorrectStride) +{ + auto range_stride = default_r.get_accessor().stride; + auto check_stride = std::array{12, 3}; + + ASSERT_EQ(range_stride, check_stride); +} + + +TEST_F(BlockColMajorAccessor3d, CanAccessData) +{ + EXPECT_EQ(default_r(0, 0, 0), 1); + EXPECT_EQ(custom_r(0, 0, 0), 1); + EXPECT_EQ(default_r(0, 1, 0), 3); + EXPECT_EQ(custom_r(0, 1, 0), 3); + EXPECT_EQ(default_r(0, 1, 1), 4); + EXPECT_EQ(default_r(0, 1, 3), 12); + EXPECT_EQ(default_r(0, 2, 2), -3); + EXPECT_EQ(default_r(1, 2, 1), 30); + EXPECT_EQ(default_r(1, 2, 2), 31); + EXPECT_EQ(default_r(1, 2, 3), 32); +} + + +TEST_F(BlockColMajorAccessor3d, CanWriteData) +{ + default_r(0, 0, 0) = 4; + custom_r(1, 1, 1) = 100; + + EXPECT_EQ(default_r(0, 0, 0), 4); + EXPECT_EQ(custom_r(0, 0, 0), 4); + EXPECT_EQ(default_r(1, 1, 1), 100); + EXPECT_EQ(custom_r(1, 1, 1), 100); +} + + +TEST_F(BlockColMajorAccessor3d, CanCreateSubrange) +{ + auto subr = custom_r(span{0, 2}, span{1, 2}, span{1, 3}); + + EXPECT_EQ(subr(0, 0, 0), 4); + EXPECT_EQ(subr(0, 0, 1), -2); + EXPECT_EQ(subr(1, 0, 0), 26); + EXPECT_EQ(subr(1, 0, 1), 27); +} + + +TEST_F(BlockColMajorAccessor3d, CanCreateRowVector) +{ + auto subr = default_r(1u, 2u, span{0, 2}); + + EXPECT_EQ(subr(0, 0, 0), 29); + EXPECT_EQ(subr(0, 0, 1), 30); +} + + +TEST_F(BlockColMajorAccessor3d, CanCreateColumnVector) +{ + auto subr = default_r(span{0u, 2u}, 1u, 3u); + + EXPECT_EQ(subr(0, 0, 0), 12); + EXPECT_EQ(subr(1, 0, 0), 28); +} + + +TEST_F(BlockColMajorAccessor3d, CanAssignValues) +{ + default_r(1, 1, 1) = default_r(0, 0, 0); + + EXPECT_EQ(data[16], 1); +} + + +TEST_F(BlockColMajorAccessor3d, CanAssignSubranges) +{ + default_r(1u, span{0, 2}, span{0, 3}) = + custom_r(0u, span{0, 2}, span{0, 3}); + + EXPECT_EQ(data[12], 1); + EXPECT_EQ(data[15], 2); + EXPECT_EQ(data[18], -1); + EXPECT_EQ(data[21], 24); + EXPECT_EQ(data[13], 3); + EXPECT_EQ(data[16], 4); + EXPECT_EQ(data[19], -2); + EXPECT_EQ(data[22], 28); + EXPECT_EQ(data[14], 29); + EXPECT_EQ(data[17], 30); + EXPECT_EQ(data[20], 31); + EXPECT_EQ(data[23], 32); +} + + } // namespace diff --git a/include/ginkgo/core/base/range_accessors.hpp b/include/ginkgo/core/base/range_accessors.hpp index f4fd01247d4..c4abdde4e45 100644 --- a/include/ginkgo/core/base/range_accessors.hpp +++ b/include/ginkgo/core/base/range_accessors.hpp @@ -35,8 +35,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include +#include +#include #include #include @@ -50,6 +52,233 @@ namespace gko { namespace accessor { +namespace detail { + + +/** + * This helper runs from first to last dimension in order to compute the index. + * The index is computed like this: + * indices: x1, x2, x3, ... + * compute(stride, x1, x2, x3) -> x1 * stride[0] + x2 * stride[1] + x3 + */ +template +struct row_major_helper_s { + static_assert(total_dim >= 1, "Dimensionality must be >= 1"); + static_assert(current_iter < total_dim, "Iteration must be < total_dim!"); + + static constexpr size_type dim_idx{current_iter - 1}; + + template + static constexpr GKO_ATTRIBUTES ValueType + compute(const std::array &size, + const std::array 1 ? total_dim - 1 : 0)> + &stride, + FirstType first, Indices &&... idxs) + { + return GKO_ASSERT(first < size[dim_idx]), + first * stride[dim_idx] + + row_major_helper_s:: + compute(size, stride, std::forward(idxs)...); + } +}; + +template +struct row_major_helper_s { + template + static constexpr GKO_ATTRIBUTES ValueType + compute(const std::array &size, + const std::array 1 ? total_dim - 1 : 0)>, + FirstType first) + { + return GKO_ASSERT(first < size[total_dim - 1]), first; + } +}; + + +/** + * Computes the storage index for the given indices with respect to the given + * stride array for row-major access + * + * @param size the multi-dimensional sizes of the range of values + * @param stride the stride array + * @param idxs the multi-dimensional indices of the desired entry + */ +template +constexpr GKO_ATTRIBUTES ValueType compute_storage_index( + const std::array &size, + const std::array 1 ? total_dim - 1 : 0)> &stride, + Indices &&... idxs) +{ + return row_major_helper_s::compute( + size, stride, std::forward(idxs)...); +} + + +template +constexpr GKO_ATTRIBUTES std::enable_if_t spans_in_size( + const std::array &) +{ + return 0; +} + +template +constexpr GKO_ATTRIBUTES std::enable_if_t<(iter < N), int> spans_in_size( + const std::array &size, First first, + Remaining &&... remaining) +{ + static_assert(sizeof...(Remaining) + 1 == N - iter, + "Number of remaining spans must be equal to N - iter"); + return GKO_ASSERT(span{first}.is_valid()), + GKO_ASSERT(span{first} <= span{size[iter]}), + spans_in_size(size, std::forward(remaining)...); +} + + +template +constexpr GKO_ATTRIBUTES int validate_spans( + const std::array &size, Spans &&... spans) +{ + return detail::spans_in_size<0>(size, std::forward(spans)...); +} + + +template +struct are_span_compatible_impl + : public std::integral_constant {}; + +template +struct are_span_compatible_impl + : public std::conditional< + std::is_integral>::value || + std::is_same, span>::value, + are_span_compatible_impl< + has_span || std::is_same, span>::value, + Args...>, + std::false_type>::type {}; + + +/** + * Evaluates if at least one type of Args is a gko::span and the others either + * also gko::span or fulfill std::is_integral + */ +template +using are_span_compatible = are_span_compatible_impl; + + +template +constexpr GKO_ATTRIBUTES + std::enable_if_t<(current == N), std::array> + to_array_impl(const dim &, Dims &&... dims) +{ + static_assert(sizeof...(Dims) == N, + "Number of arguments must match dimensionality!"); + return {{std::forward(dims)...}}; +} + + +template +constexpr GKO_ATTRIBUTES + std::enable_if_t<(current < N), std::array> + to_array_impl(const dim &size, Dims &&... dims) +{ + return to_array_impl( + size, std::forward(dims)..., size[current]); +} + + +template +constexpr GKO_ATTRIBUTES std::array to_array(const dim &size) +{ + return to_array_impl(size); +} + + +template +constexpr GKO_ATTRIBUTES std::enable_if_t +mult_dim_upwards_impl(const std::array &) +{ + return 1; +} + +template +constexpr GKO_ATTRIBUTES std::enable_if_t<(iter < N), ValueType> +mult_dim_upwards_impl(const std::array &size) +{ + return size[iter] * mult_dim_upwards_impl(size); +} + + +template +constexpr GKO_ATTRIBUTES + std::enable_if_t> + compute_default_stride_array_impl(const std::array &, + Args &&... args) +{ + return {{std::forward(args)...}}; +} + +template +constexpr GKO_ATTRIBUTES std::enable_if_t< + (iter < N) && (iter == sizeof...(Args) + 1), std::array> +compute_default_stride_array_impl(const std::array &size, + Args &&... args) +{ + return compute_default_stride_array_impl( + size, std::forward(args)..., mult_dim_upwards_impl(size)); +} + + +template +constexpr GKO_ATTRIBUTES + std::array 0 ? dimensions - 1 : 0)> + compute_default_stride_array(const std::array &size) +{ + return compute_default_stride_array_impl(size); +} + + +template +GKO_ATTRIBUTES std::enable_if_t multidim_for_each_impl( + const std::array &, Callable callable, Indices &&... indices) +{ + static_assert(iter == sizeof...(Indices), + "Number arguments must match current iteration!"); + callable(std::forward(indices)...); +} + +template +GKO_ATTRIBUTES std::enable_if_t<(iter < N)> multidim_for_each_impl( + const std::array &size, Callable &&callable, + Indices &&... indices) +{ + static_assert(iter == sizeof...(Indices), + "Number arguments must match current iteration!"); + for (size_type i = 0; i < size[iter]; ++i) { + multidim_for_each_impl(size, std::forward(callable), + std::forward(indices)..., i); + } +} + + +/** + * Creates a recursive for-loop for each dimension and calls dest(indices...) = + * source(indices...) + */ +template +GKO_ATTRIBUTES void multidim_for_each(const std::array &size, + Callable &&callable) +{ + multidim_for_each_impl<0>(size, std::forward(callable)); +} + + +} // namespace detail + + /** * A row_major accessor is a bridge between a range and the row-major memory * layout. @@ -59,19 +288,20 @@ namespace accessor { * constructor parameters for this class to the range (it will forward it to * this class). * - * @warning The current implementation is incomplete, and only allows for - * 2-dimensional ranges. + * @warning For backward compatability reasons, a specialization is provided + * for dimensionality == 2. * * @tparam ValueType type of values this accessor returns - * @tparam Dimensionality number of dimensions of this accessor (has to be 2) + * @tparam Dimensionality number of dimensions of this accessor */ template class row_major { public: friend class range; - static_assert(Dimensionality == 2, - "This accessor is only implemented for matrices"); + static_assert(Dimensionality != 0, + "This accessor does not support a dimensionality of 0!"); + static constexpr size_type dimensionality = Dimensionality; /** * Type of values returned by the accessor. @@ -86,9 +316,179 @@ class row_major { /** * Number of dimensions of the accessor. */ - static constexpr size_type dimensionality = 2; using const_accessor = row_major; + using stride_type = std::array; + using length_type = std::array; + +protected: + /** + * Creates a row_major accessor. + * + * @param data pointer to the block of memory containing the data + * @param lengths size / length of the accesses of each dimension + * @param stride distance (in elements) between starting positions of + * the dimensions (i.e. + * `x_1 * stride_1 + x_2 * stride_2 * ... + x_n` + * points to the element at (x_1, x_2, ..., x_n)) + */ + constexpr GKO_ATTRIBUTES explicit row_major(data_type data, + dim size, + stride_type stride) + : data{data}, + lengths(detail::to_array(size)), + stride(stride) + {} + + /** + * Creates a row_major accessor with a default stride (assumes no padding) + * + * @param data pointer to the block of memory containing the data + * @param lengths size / length of the accesses of each dimension + */ + constexpr GKO_ATTRIBUTES explicit row_major(data_type data, + dim size) + : data{data}, + lengths(detail::to_array(size)), + stride(detail::compute_default_stride_array(lengths)) + {} + +public: + /** + * Creates a row_major range which contains a read-only version of the + * current accessor. + * + * @returns a row major range which is read-only. + */ + constexpr GKO_ATTRIBUTES range to_const() const + { + // TODO Remove this functionality all together (if requested) + return range(data, lengths, stride); + } + + /** + * Returns the data element at the specified indices + * + * @param row row index + * @param col column index + * + * @return data element at (indices...) + */ + template + constexpr GKO_ATTRIBUTES + std::enable_if_t::value, value_type &> + operator()(Indices &&... indices) const + { + return data[detail::compute_storage_index( + lengths, stride, std::forward(indices)...)]; + } + + /** + * Returns the sub-range spanning the range (x1_span, x2_span, ...) + * + * @param rows row span + * @param cols column span + * + * @return sub-range spanning the given spans + */ + template + constexpr GKO_ATTRIBUTES std::enable_if_t< + detail::are_span_compatible::value, range> + operator()(SpanTypes... spans) const + { + return detail::validate_spans(lengths, spans...), + range{ + data + detail::compute_storage_index(lengths, stride, + (span{spans}.begin)...), + dim{ + (span{spans}.end - span{spans}.begin)...}, + stride}; + } + + /** + * Returns the length in dimension `dimension`. + * + * @param dimension a dimension index + * + * @return length in dimension `dimension` + */ + constexpr GKO_ATTRIBUTES size_type length(size_type dimension) const + { + return lengths[dimension]; + } + + /** + * Copies data from another accessor + * + * @warning Do not use this function since it is not optimized for a + * specific executor. It will always be performed sequentially. + * Please write an optimized version (adjusted to the architecture) + * by iterating through the values yourself. + * + * @tparam OtherAccessor type of the other accessor + * + * @param other other accessor + */ + template + GKO_ATTRIBUTES void copy_from(const OtherAccessor &other) const + { + detail::multidim_for_each(lengths, [this, &other](auto... indices) { + (*this)(indices...) = other(indices...); + }); + } + + /** + * Reference to the underlying data. + */ + const data_type data; + + /** + * An array of dimension sizes. + */ + const length_type lengths; + + /** + * Distance between consecutive rows for each dimension (except the first). + */ + const stride_type stride; +}; + + +/** + * A row_major accessor is a bridge between a range and the row-major memory + * layout. + * + * You should never try to explicitly create an instance of this accessor. + * Instead, supply it as a template parameter to a range, and pass the + * constructor parameters for this class to the range (it will forward it to + * this class). + * + * @note This is the original implementation, which is now the specialization + * for Dimensionality = 2. + * + * @tparam ValueType type of values this accessor returns + */ +template +class row_major { +public: + friend class range; + + /** + * Number of dimensions of the accessor. + */ + static constexpr size_type dimensionality = 2; + + /** + * Type of values returned by the accessor. + */ + using value_type = ValueType; + + /** + * Type of underlying data storage. + */ + using data_type = value_type *; + + using const_accessor = row_major; protected: /** @@ -108,6 +508,36 @@ class row_major { : data{data}, lengths{num_rows, num_cols}, stride{stride} {} + /** + * Creates a row_major accessor. + * + * @param data pointer to the block of memory containing the data + * @param lengths size / length of the accesses of each dimension + * @param stride distance (in elements) between starting positions of + * consecutive rows (i.e. `data + i * stride` points to + * the `i`-th row) + */ + constexpr GKO_ATTRIBUTES explicit row_major(data_type data, + dim size, + size_type stride) + : data{data}, + lengths(detail::to_array(size)), + stride(stride) + {} + + /** + * Creates a row_major accessor with a default stride (assumes no padding) + * + * @param data pointer to the block of memory containing the data + * @param lengths size / length of the accesses of each dimension + */ + constexpr GKO_ATTRIBUTES explicit row_major(data_type data, + dim size) + : data{data}, + lengths(detail::to_array(size)), + stride{size[1]} + {} + public: /** * Creates a row_major range which contains a read-only version of the @@ -205,6 +635,303 @@ class row_major { }; +namespace detail { +/** + * Namespace for helper functions and structs for + * the block column major accessor. + */ +namespace blk_col_major { + + +/** + * Runs from first to last dimension in order to compute the index. + * + * The index is computed like this: + * indices: x1, x2, x3, ..., xn + * compute(stride, x1, x2, x3, ..., x(n-1), xn) -> + * x1 * stride[0] + x2 * stride[1] + ... + * + x(n-2) * stride[n-3] + x(n-1) + xn * stride[n-2] + * Note that swap of the last two strides, making this 'block column major'. + */ +template +struct index_helper_s { + static_assert(total_dim >= 1, "Dimensionality must be >= 1"); + static_assert(current_iter <= total_dim, "Iteration must be <= total_dim!"); + + static constexpr size_type dim_idx{current_iter - 1}; + + template + static constexpr GKO_ATTRIBUTES ValueType + compute(const std::array &size, + const std::array 0 ? total_dim - 1 : 0)> + &stride, + FirstType first, Indices &&... idxs) + { + if (current_iter == total_dim - 1) { + return GKO_ASSERT(first < size[dim_idx]), + first + + index_helper_s:: + compute(size, stride, + std::forward(idxs)...); + } + + return GKO_ASSERT(first < size[dim_idx]), + first * stride[dim_idx] + + index_helper_s:: + compute(size, stride, std::forward(idxs)...); + } +}; + +template +struct index_helper_s { + static_assert(total_dim >= 2, "Dimensionality must be >= 2"); + + static constexpr size_type dim_idx{total_dim - 1}; + + template + static constexpr GKO_ATTRIBUTES ValueType + compute(const std::array &size, + const std::array 1 ? total_dim - 1 : 0)> + &stride, + FirstType first) + { + return GKO_ASSERT(first < size[total_dim - 1]), + first * stride[dim_idx - 1]; + } +}; + +/** + * Computes the flat storage index for block-column-major access. + * + * @param size the multi-dimensional sizes of the range of values + * @param stride the stride array + * @param idxs the multi-dimensional indices of the desired entry + */ +template +constexpr GKO_ATTRIBUTES ValueType compute_index( + const std::array &size, + const std::array 0 ? total_dim - 1 : 0)> &stride, + Indices &&... idxs) +{ + return index_helper_s::compute( + size, stride, std::forward(idxs)...); +} + + +template +constexpr GKO_ATTRIBUTES + std::enable_if_t<(iter == N - 1) && (iter == sizeof...(Args) + 1), + std::array> + default_stride_array_impl(const std::array &size, + Args &&... args) +{ + return {{std::forward(args)..., size[N - 2]}}; +} + +template +constexpr GKO_ATTRIBUTES std::enable_if_t<(iter < N - 1 || iter == N) && + (iter == sizeof...(Args) + 1), + std::array> +default_stride_array_impl(const std::array &size, Args &&... args) +{ + return default_stride_array_impl( + size, std::forward(args)..., + detail::mult_dim_upwards_impl(size)); +} + +template +constexpr GKO_ATTRIBUTES + std::array 0 ? dimensions - 1 : 0)> + default_stride_array(const std::array &size) +{ + return default_stride_array_impl(size); +} + + +} // namespace blk_col_major +} // namespace detail + + +/** + * A bridge between a range and a block-column-major memory layout. + * + * Only the innermost two dimensions are regarded as defining + * a column-major matrix, and the rest of the dimensions are treated + * identically to \ref row_major. + * + * You should not try to explicitly create an instance of this accessor. + * Instead, supply it as a template parameter to a range, and pass the + * constructor parameters for this class to the range (it will forward it to + * this class). + * + * @tparam ValueType type of values this accessor returns + * @tparam Dimensionality number of dimensions of this accessor + */ +template +class block_col_major { +public: + friend class range; + + static_assert(Dimensionality != 0, + "This accessor does not support a dimensionality of 0!"); + static_assert(Dimensionality != 1, + "Please use row_major accessor for 1D ranges."); + + /** + * Number of dimensions of the accessor. + */ + static constexpr size_type dimensionality = Dimensionality; + + /** + * Type of values returned by the accessor. + */ + using value_type = ValueType; + + /** + * Type of underlying data storage. + */ + using data_type = value_type *; + + using const_accessor = block_col_major; + using stride_type = std::array; + using length_type = std::array; + +protected: + /** + * Creates a block_col_major accessor. + * + * @param data pointer to the block of memory containing the data + * @param lengths size / length of the accesses of each dimension + * @param stride distance (in elements) between starting positions of + * the dimensions (i.e. + * `x_1 * stride_1 + x_2 * stride_2 * ... + x_(n-1) + x_n * stride_(n-1)` + * points to the element at (x_1, x_2, ..., x_n)) + */ + constexpr GKO_ATTRIBUTES explicit block_col_major(data_type data, + dim size, + stride_type stride) + : data{data}, + lengths(detail::to_array(size)), + stride(stride) + {} + + /** + * Creates a block_col_major accessor with a default stride + * (assumes no padding) + * + * @param data pointer to the block of memory containing the data + * @param lengths size / length of the accesses of each dimension + */ + constexpr GKO_ATTRIBUTES explicit block_col_major(data_type data, + dim size) + : data{data}, + lengths(detail::to_array(size)), + stride(detail::blk_col_major::default_stride_array(lengths)) + {} + +public: + /** + * Creates a block_col_major range which contains a read-only version of + * the current accessor. + * + * @returns a block column major range which is read-only. + */ + constexpr GKO_ATTRIBUTES range to_const() const + { + // TODO Remove this functionality all together (if requested) + return range(data, lengths, stride); + } + + /** + * Returns the data element at the specified indices + * + * @param row row index + * @param col column index + * + * @return data element at (indices...) + */ + template + constexpr GKO_ATTRIBUTES + std::enable_if_t::value, value_type &> + operator()(Indices &&... indices) const + { + return data[detail::blk_col_major::compute_index( + lengths, stride, std::forward(indices)...)]; + } + + /** + * Returns the sub-range spanning the range (x1_span, x2_span, ...) + * + * @param rows row span + * @param cols column span + * + * @return sub-range spanning the given spans + */ + template + constexpr GKO_ATTRIBUTES + std::enable_if_t::value, + range> + operator()(SpanTypes... spans) const + { + return detail::validate_spans(lengths, spans...), + range{ + data + detail::blk_col_major::compute_index( + lengths, stride, (span{spans}.begin)...), + dim{ + (span{spans}.end - span{spans}.begin)...}, + stride}; + } + + /** + * Returns the length in dimension `dimension`. + * + * @param dimension a dimension index + * + * @return length in dimension `dimension` + */ + constexpr GKO_ATTRIBUTES size_type length(size_type dimension) const + { + return lengths[dimension]; + } + + /** + * Copies data from another accessor + * + * @warning Do not use this function since it is not optimized for a + * specific executor. It will always be performed sequentially. + * Please write an optimized version (adjusted to the architecture) + * by iterating through the values yourself. + * + * @tparam OtherAccessor type of the other accessor + * + * @param other other accessor + */ + template + GKO_ATTRIBUTES void copy_from(const OtherAccessor &other) const + { + detail::multidim_for_each(lengths, [this, &other](auto... indices) { + (*this)(indices...) = other(indices...); + }); + } + + /** + * Reference to the underlying data. + */ + const data_type data; + + /** + * An array of dimension sizes. + */ + const length_type lengths; + + /** + * Distance between consecutive 'layers' for each dimension + * (except the second, for which it is 1). + */ + const stride_type stride; +}; + + } // namespace accessor } // namespace gko