Reputation: 394
I've been reviewing new ways of slicing arrays using the latest standards, but it's been a bit overwhelming and I have a question.
Is there any new, succinct way, with the new C++23 additions to ranges
, views
, mdspan
, or even the ones to come submdspan
, mdarray
, to extract elements of an mdspan
into another container using a vector with the indices to be extracted?
I have to work with std::array
that I know their size beforehand, however, the indices will be known only at runtime so I can use temporary std::vector
types or other containers.
Here's a godbolt example, reproduced below
#include <iostream>
#include <array>
#include <vector>
#include <https://raw.githubusercontent.com/kokkos/mdspan/single-header/mdspan.hpp>
int main()
{
constexpr std::size_t pDeg = 16 ;
std::array<std::size_t,pDeg*pDeg> arr;
for (std::size_t i=0;i<arr.size();i++)
arr[i] = i ;
auto md = std::mdspan<std::size_t,std::extents<std::size_t,pDeg,pDeg>>(arr.data());
std::vector<std::size_t> ind1 = {1,5,6,7,8,9,4,5};
std::vector<std::size_t> ind2 = {14,11,12,13,11,1,5,6};
}
I have functions implemented to operate on matrices: multiply()
, add()
, etc
I would like to write matrix algebra expressions like
auto result = add(multiply(md[ind1,:],md[:,ind2]),md[ind1,ind2]);
where md[ind1,:]
is a submatrix containing rows 1,5,6,7,8,9,4,5 of md
, md[:,ind2]
contains columns 14,11,12,13,11,1,5,6 of md
and md[ind1,ind2]
is a 8-by-8 submatrix of md
containing all combinations of rows and columns given by ind1
and ind2
respectively (notice that indices can be repeated).
or something like
auto result = add(multiply(md.extract<0>(ind1),md.extract<1>(ind2)),md.extract<0,1>(ind1,ind2));
where result
is always a std::array
that I know the size at compile time.
The methods do not necessarily have to be the ones I propose, but they needs to be in a sort of short notation since there will be many matrix algebra operations.
Upvotes: 1
Views: 335
Reputation: 16300
I believe the most succinct way to select rows or columns is to use the following transformation. This is not necessarily the most efficient code, but it is quite general.
template<class MDSpanArray, class Selection1, class Selection2>
auto select(MDSpanArray&& arr, Selection1 ind1, Selection2 ind2) {
return std::views::transform(
std::move(ind1),
[ind2 = std::move(ind2), &arr](auto i) {
return std::views::transform(ind2, [i, &arr](auto j) {return arr[i, j];});
}
);
}
you can use it like this, the selections can be concrete dynamic arrays or a iota range based on the sizes:
auto&& arr2_identity = select(arr2, std::views::iota(0UL, arr2.extent(0)), std::views::iota(0UL, arr2.extent(1)));
auto&& arr2_rows_ind1 = select(arr2, ind1, std::views::iota(0UL, arr2.extent(1)));
auto&& arr2_cols_ind2 = select(arr2, std::views::iota(0UL, arr2.extent(0)), ind2);
auto&& arr2_ind1_ind2 = select(arr2, ind1, ind2);
Here it is the full code, https://godbolt.org/z/aodo8dsvs.
I don't understand what you mean by the add
and multiply
functions, but it seems that you have a good idea on how to implement those.
The overall code resulted a bit unergonomic, mainly because the original array (mdspan
) and the selection would have slightly different access syntax.
So I gave it a shot with my own library (https://github.com/correaa/boost-multi) as well.
In this case you can use it like this:
https://godbolt.org/z/sEG5YvfYq
multi::array<std::size_t, 2> arr2 = ...;
...
using multi::_;
select(arr2, _ , _ ); // or select(arr2);
select(arr2, ind1, _ ); // or select(arr2, ind1);
select(arr2, _ , ind2);
select(arr2, ind1, ind2);
The functions can be further optimized in each case, but that requires a somewhat advanced use of library features: https://godbolt.org/z/aa1cbY6bf
Upvotes: 0