-
Notifications
You must be signed in to change notification settings - Fork 436
added a mapper that maps the indexes of a view to the indices of a container #2880
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
66dd786
f818520
89accf2
1d46493
4c62edc
e3f1e74
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,236 @@ | ||||||||||||||
| /*************************************************************************** | ||||||||||||||
| * Copyright (c) Johan Mabille, Sylvain Corlay and Wolf Vollprecht * | ||||||||||||||
| * Copyright (c) QuantStack * | ||||||||||||||
| * * | ||||||||||||||
| * Distributed under the terms of the BSD 3-Clause License. * | ||||||||||||||
| * * | ||||||||||||||
| * The full license is in the file LICENSE, distributed with this software. * | ||||||||||||||
| ****************************************************************************/ | ||||||||||||||
|
|
||||||||||||||
| #ifndef XTENSOR_INDEX_MAPPER_HPP | ||||||||||||||
| #define XTENSOR_INDEX_MAPPER_HPP | ||||||||||||||
|
|
||||||||||||||
| #include "xview.hpp" | ||||||||||||||
|
|
||||||||||||||
| namespace xt | ||||||||||||||
| { | ||||||||||||||
|
|
||||||||||||||
| template<class UndefinedView> struct index_mapper; | ||||||||||||||
|
|
||||||||||||||
| /** | ||||||||||||||
| * @class index_mapper | ||||||||||||||
| * @brief A helper class for mapping indices between views and their underlying containers. | ||||||||||||||
| * | ||||||||||||||
| * The `index_mapper` class provides functionality to convert indices from a view's coordinate system | ||||||||||||||
| * to the corresponding indices in the underlying container. This is particularly useful for views | ||||||||||||||
| * that contain integral slices (fixed indices), as these slices reduce the dimensionality of the view. | ||||||||||||||
| * | ||||||||||||||
| * @tparam UndefinedView The primary template parameter, specialized for `xt::xview` types. | ||||||||||||||
| * | ||||||||||||||
| * @note This class is specialized for `xt::xview<UnderlyingContainer, Slices...>` types only. | ||||||||||||||
| * Other view types will trigger a compilation error. | ||||||||||||||
| * | ||||||||||||||
| * @example | ||||||||||||||
| * @code | ||||||||||||||
| * xt::xarray<double> a = xt::arange(24).reshape({2, 3, 4}); | ||||||||||||||
| * auto view1 = xt::view(a, 1, xt::all(), xt::all()); // Fixed first dimension | ||||||||||||||
| * index_mapper<decltype(view1)> mapper; | ||||||||||||||
| * | ||||||||||||||
| * // Map view indices (i,j) to container indices (1,i,j) | ||||||||||||||
| * double val = mapper.map(a, view1, 0, 0); // Returns a(1, 0, 0) | ||||||||||||||
| * double val2 = mapper.map(a, view1, 1, 2); // Returns a(1, 1, 2) | ||||||||||||||
| * @endcode | ||||||||||||||
| */ | ||||||||||||||
| template<class UnderlyingContainer, class... Slices> | ||||||||||||||
| class index_mapper< xt::xview<UnderlyingContainer, Slices...> > | ||||||||||||||
| { | ||||||||||||||
| static constexpr size_t n_slices = sizeof...(Slices); ///< @brief Total number of slices in the view | ||||||||||||||
| static constexpr size_t n_free = ((!std::is_integral_v<Slices>) + ... ); ///< @brief Number of free (non-integral) slices | ||||||||||||||
| public: | ||||||||||||||
| using view_type = xt::xview<UnderlyingContainer, Slices...>; ///< @brief The view type this mapper works with | ||||||||||||||
|
|
||||||||||||||
| using value_type = typename xt::xview<UnderlyingContainer, Slices...>::value_type; ///< @brief Value type of the underlying container | ||||||||||||||
| private: | ||||||||||||||
| /// @brief Helper type alias for the I-th slice type | ||||||||||||||
|
alexandrehoffmann marked this conversation as resolved.
Outdated
|
||||||||||||||
| template<size_t I> using ith_slice_type = std::tuple_element_t<I, std::tuple<Slices...> >; | ||||||||||||||
|
|
||||||||||||||
| /** | ||||||||||||||
|
alexandrehoffmann marked this conversation as resolved.
Outdated
|
||||||||||||||
| * @brief Helper metafunction to generate an index sequence excluding newaxis slices. | ||||||||||||||
| * | ||||||||||||||
| * This recursive template builds an `std::index_sequence` containing indices of slices | ||||||||||||||
| * that are not `xt::newaxis`. Newaxis slices increase dimensionality but don't correspond | ||||||||||||||
| * to actual dimensions in the underlying container. | ||||||||||||||
| * | ||||||||||||||
| * @tparam first Current slice index being processed. | ||||||||||||||
| * @tparam indices... Accumulated indices of non-newaxis slices. | ||||||||||||||
| */ | ||||||||||||||
| template<size_t first, size_t... indices> | ||||||||||||||
| struct indices_sequence_helper | ||||||||||||||
| { | ||||||||||||||
| using not_new_axis_type = typename indices_sequence_helper<first + 1, indices..., first>::Type; // we add the current axis | ||||||||||||||
| using new_axis_type = typename indices_sequence_helper<first + 1, indices...>::Type; // we skip the current axis | ||||||||||||||
|
|
||||||||||||||
| using Type = std::conditional_t< xt::detail::is_newaxis< ith_slice_type<first> >::value , new_axis_type, not_new_axis_type>; | ||||||||||||||
| }; | ||||||||||||||
| /// @brief Base case: recursion termination | ||||||||||||||
| template<size_t... indices> | ||||||||||||||
| struct indices_sequence_helper<n_slices, indices...> | ||||||||||||||
| { | ||||||||||||||
| using Type = std::index_sequence<indices...>; | ||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
| using indices_sequence = indices_sequence_helper<0>::Type; ///< @brief Index sequence of non-newaxis slices | ||||||||||||||
|
|
||||||||||||||
| /** | ||||||||||||||
|
alexandrehoffmann marked this conversation as resolved.
Outdated
|
||||||||||||||
| * @brief Maps an index for a specific slice to the corresponding index in the underlying container. | ||||||||||||||
| * | ||||||||||||||
| * For integral slices (fixed indices), returns the fixed index value. | ||||||||||||||
| * For non-integral slices (like `xt::all()`), applies the slice transformation to the index. | ||||||||||||||
| * | ||||||||||||||
| * @tparam I The slice index to map. | ||||||||||||||
| * @tparam Index Type of the index (must be integral). | ||||||||||||||
| * @param view The view object containing slice information. | ||||||||||||||
| * @param i The index within the slice to map. | ||||||||||||||
| * @return size_t The mapped index in the underlying container. | ||||||||||||||
| * | ||||||||||||||
| * @throws Assertion failure if `i != 0` for integral slices. | ||||||||||||||
| * @throws Assertion failure if `i >= slice.size()` for non-integral slices. | ||||||||||||||
|
||||||||||||||
| * @throws Assertion failure if `i != 0` for integral slices. | |
| * @throws Assertion failure if `i >= slice.size()` for non-integral slices. | |
| * @note This function uses assertions to enforce preconditions: | |
| * - Aborts with assertion failure if `i != 0` for integral slices. | |
| * - Aborts with assertion failure if `i >= slice.size()` for non-integral slices. | |
| * These are not exceptions and will terminate the program in debug builds if triggered. |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The dimension() method returns the number of free (non-integral) dimensions, but the method name is ambiguous. It's unclear whether this returns the view's dimension or the container's dimension. Consider renaming to free_dimensions() or view_dimension() to make the purpose clearer.
| constexpr size_t dimension() const { return n_free; } | |
| constexpr size_t free_dimensions() const { return n_free; } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -30,6 +30,7 @@ | |||||||||||||||
| #include "xtensor/misc/xmanipulation.hpp" | ||||||||||||||||
| #include "xtensor/views/xstrided_view.hpp" | ||||||||||||||||
| #include "xtensor/views/xview.hpp" | ||||||||||||||||
| #include "xtensor/views/index_mapper.hpp" | ||||||||||||||||
|
|
||||||||||||||||
| namespace xt | ||||||||||||||||
| { | ||||||||||||||||
|
|
@@ -143,6 +144,57 @@ namespace xt | |||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| TEST(xview_mapping, simple) | ||||||||||||||||
| { | ||||||||||||||||
| view_shape_type shape = {3, 4}; | ||||||||||||||||
| xarray<double> a(shape); | ||||||||||||||||
| std::vector<double> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; | ||||||||||||||||
| std::copy(data.cbegin(), data.cend(), a.template begin<layout_type::row_major>()); | ||||||||||||||||
|
|
||||||||||||||||
| auto view1 = view(a, 1, range(1, 4)); | ||||||||||||||||
|
|
||||||||||||||||
| index_mapper<decltype(view1)> mapper1; | ||||||||||||||||
|
|
||||||||||||||||
| EXPECT_EQ(a(1, 1), mapper1.map(a, view1, 0)); | ||||||||||||||||
| EXPECT_EQ(a(1, 2), mapper1.map(a, view1, 1)); | ||||||||||||||||
| EXPECT_EQ(size_t(1), mapper1.dimension()); | ||||||||||||||||
| //~ XT_EXPECT_ANY_THROW(mapper1.map_at(a, view1, 10)); | ||||||||||||||||
|
alexandrehoffmann marked this conversation as resolved.
Outdated
|
||||||||||||||||
|
|
||||||||||||||||
| auto view0 = view(a, 0, range(0, 3)); | ||||||||||||||||
| index_mapper<decltype(view0)> mapper0; | ||||||||||||||||
|
|
||||||||||||||||
| EXPECT_EQ(a(0, 0), mapper0.map(a, view0, 0)); | ||||||||||||||||
| EXPECT_EQ(a(0, 1), mapper0.map(a, view0, 1)); | ||||||||||||||||
| EXPECT_EQ(size_t(1), mapper0.dimension()); | ||||||||||||||||
|
|
||||||||||||||||
| auto view2 = view(a, range(0, 2), 2); | ||||||||||||||||
| index_mapper<decltype(view2)> mapper2; | ||||||||||||||||
| EXPECT_EQ(a(0, 2), mapper2.map(a, view2, 0)); | ||||||||||||||||
| EXPECT_EQ(a(1, 2), mapper2.map(a, view2, 1)); | ||||||||||||||||
| EXPECT_EQ(size_t(1), mapper2.dimension()); | ||||||||||||||||
|
|
||||||||||||||||
| //~ auto view4 = view(a, 1); | ||||||||||||||||
| //~ index_mapper<decltype(view4)> mapper4; | ||||||||||||||||
| //~ EXPECT_EQ(size_t(1), mapper4.dimension()); | ||||||||||||||||
| //~ | ||||||||||||||||
| //~ auto view5 = view(view4, 1); | ||||||||||||||||
| //~ index_mapper<decltype(view5)> mapper5; | ||||||||||||||||
| //~ EXPECT_EQ(size_t(0), mapper5.dimension()); | ||||||||||||||||
|
||||||||||||||||
| //~ auto view4 = view(a, 1); | |
| //~ index_mapper<decltype(view4)> mapper4; | |
| //~ EXPECT_EQ(size_t(1), mapper4.dimension()); | |
| //~ | |
| //~ auto view5 = view(view4, 1); | |
| //~ index_mapper<decltype(view5)> mapper5; | |
| //~ EXPECT_EQ(size_t(0), mapper5.dimension()); |
Uh oh!
There was an error while loading. Please reload this page.