kokkos / mdspan

Reference implementation of mdspan targeting C++23
Other
413 stars 69 forks source link

How to "flip" mdspan using submdspan (or any other utility)? #352

Open Sunday111 opened 4 months ago

Sunday111 commented 4 months ago

I've been experimenting with mdspan for a few days, and one thing remains unclear to me: Is it possible to use submdspan to create an mdspan that "views" the original data in reverse order?

For example, consider a span with 9 rows and 10 columns:

constexpr int num_rows = 9;
constexpr int num_columns = 10;
auto data = std::views::iota(0, num_columns * num_rows) | std::ranges::to<std::vector>();
const exstd::mdspan<const int, exstd::extents<int, num_rows, num_columns>, exstd::layout_right> span{data.data()};

This represents the following table:

      0,   1,   2,   3,   4,   5,   6,   7,   8,   9
     10,  11,  12,  13,  14,  15,  16,  17,  18,  19
     20,  21,  22,  23,  24,  25,  26,  27,  28,  29
     30,  31,  32,  33,  34,  35,  36,  37,  38,  39
     40,  41,  42,  43,  44,  45,  46,  47,  48,  49
     50,  51,  52,  53,  54,  55,  56,  57,  58,  59
     60,  61,  62,  63,  64,  65,  66,  67,  68,  69
     70,  71,  72,  73,  74,  75,  76,  77,  78,  79
     80,  81,  82,  83,  84,  85,  86,  87,  88,  89

Is there a way to call the submdspan function to get the same data but with the rows flipped?

     80,  81,  82,  83,  84,  85,  86,  87,  88,  89
     70,  71,  72,  73,  74,  75,  76,  77,  78,  79
     60,  61,  62,  63,  64,  65,  66,  67,  68,  69
     50,  51,  52,  53,  54,  55,  56,  57,  58,  59
     40,  41,  42,  43,  44,  45,  46,  47,  48,  49
     30,  31,  32,  33,  34,  35,  36,  37,  38,  39
     20,  21,  22,  23,  24,  25,  26,  27,  28,  29
     10,  11,  12,  13,  14,  15,  16,  17,  18,  19
      0,   1,   2,   3,   4,   5,   6,   7,   8,   9

I tried to achieve this by passing a negative stride into strided_slice, but it returned incorrect results (with negative extents):

exstd::submdspan(span, exstd::strided_slice{.offset = 5, .extent = 5, .stride = -1}, exstd::full_extent)

At this point, I think I do not understand how this is supposed to be done. As far as I understand, the original intention of this proposal was to have a tool similar to indexing in NumPy, MATLAB, or Fortran, which can handle this task easily.

crtrott commented 2 months ago

No this is not currently supported and was not brought up as a requirement in the last 9 years ... mdspan itself can support this - though none of the standard layouts do. You could write one relatively easily though - in particular if you really are just interested in 2D. For submdspan to support this a proposal to the standard would be necessary. Basically, a negative stride right now would result in a negative extent which is invalid. One could however write a proposal to change how a negative stride is treated, more likely we would need a new slice specifier because the return type of the mdspan needs to change (since the current layouts do not support negative strides). If your "reverse layout" indeed is exactly going from the back in memory (which in 2D isn't 100% clear to me). You could go with a reverse accessor I think.

I.e. like the default accessor but just store the total offset, and then have an access function which does:

reference access(pointer_type ptr, size_t idx) const { return ptr[offset - idx]; }
mhoemmen commented 2 months ago

As Christian explained, you can get the desired effect by defining a "reverse accessor."

As far as I understand, the original intention of this proposal was to have a tool similar to indexing in NumPy, MATLAB, or Fortran.

The front matter of the proposal explains the authors' design intent.

https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p0009r18.html

The authors considered and rejected Python-style "negative indices mean counting from the back." It complicates indexing math by adding branches and/or modular arithmetic, both of which may affect performance. One could wrap mdspan to get this effect.

spinicist commented 2 months ago

No this is not currently supported and was not brought up as a requirement in the last 9 years ...

Do you mean that flipping an mdspan in general has never come up, or specifically doing it via submdspan?

burnpanck commented 1 month ago

The authors considered and rejected Python-style "negative indices mean counting from the back." It complicates indexing math by adding branches and/or modular arithmetic, both of which may affect performance. One could wrap mdspan to get this effect.

Indexing using a negative index is not the same as having a negative stride. Giving certain indexing values special meaning (like the python "index from the back") obviously adds a lot of potential runtime overhead. On the other hand, negative strides together with the exact same (mathematical) mapping rules of the mdspan/submdspan create no overhead during access of the span. The constructors of those types could become slightly more expensive, depending on their exact specification though.

In my experience (mostly with numpy), views with negative strides have come up as useful every once in a while. Thus, I would welcome additions to submdspan to make them compatible with negative strides. That is, if they really aren't; I actually have no experience with std::submdspan yet.