On Tue, May 20, 2025 at 11:20 AM Luc Grosheintz <luc.groshei...@gmail.com>
wrote:

>
>
> On 5/20/25 10:48 AM, Tomasz Kaminski wrote:
> > On Tue, May 20, 2025 at 10:45 AM Luc Grosheintz <
> luc.groshei...@gmail.com>
> > wrote:
> >
> >>
> >>
> >> On 5/20/25 10:24 AM, Tomasz Kaminski wrote:
> >>> On Sun, May 18, 2025 at 10:16 PM Luc Grosheintz <
> >> luc.groshei...@gmail.com>
> >>> wrote:
> >>>
> >>>> Implements the remaining parts of layout_left and layout_right; and
> all
> >>>> of layout_stride.
> >>>>
> >>>> libstdc++-v3/ChangeLog:
> >>>>
> >>>>           * include/std/mdspan(layout_stride): New class.
> >>>>
> >>>> Signed-off-by: Luc Grosheintz <luc.groshei...@gmail.com>
> >>>> ---
> >>>>    libstdc++-v3/include/std/mdspan | 219
> +++++++++++++++++++++++++++++++-
> >>>>    1 file changed, 216 insertions(+), 3 deletions(-)
> >>>>
> >>>> diff --git a/libstdc++-v3/include/std/mdspan
> >>>> b/libstdc++-v3/include/std/mdspan
> >>>> index b1984eb2a33..31a38c736c2 100644
> >>>> --- a/libstdc++-v3/include/std/mdspan
> >>>> +++ b/libstdc++-v3/include/std/mdspan
> >>>> @@ -366,6 +366,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>>>          class mapping;
> >>>>      };
> >>>>
> >>>> +  struct layout_stride
> >>>> +  {
> >>>> +    template<typename _Extents>
> >>>> +      class mapping;
> >>>> +  };
> >>>> +
> >>>>      namespace __mdspan
> >>>>      {
> >>>>        template<typename _Tp>
> >>>> @@ -434,7 +440,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>>>
> >>>>        template<typename _Mapping>
> >>>>          concept __standardized_mapping = __mapping_of<layout_left,
> >> _Mapping>
> >>>> -                                      || __mapping_of<layout_right,
> >>>> _Mapping>;
> >>>> +                                      || __mapping_of<layout_right,
> >>>> _Mapping>
> >>>> +                                      || __mapping_of<layout_stride,
> >>>> _Mapping>;
> >>>>
> >>>>        template<typename M>
> >>>>          concept __mapping_like = requires
> >>>> @@ -503,6 +510,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>>>           : mapping(__other.extents(), __mdspan::__internal_ctor{})
> >>>>           { }
> >>>>
> >>>> +      template<typename _OExtents>
> >>>> +       requires (is_constructible_v<extents_type, _OExtents>)
> >>>> +       constexpr explicit(extents_type::rank() > 0)
> >>>> +       mapping(const layout_stride::mapping<_OExtents>& __other)
> >>>> +       : mapping(__other.extents(), __mdspan::__internal_ctor{})
> >>>> +       {
> >>>> +         __glibcxx_assert(
> >>>> +           layout_left::mapping<_OExtents>(__other.extents()) ==
> >> __other);
> >>>>
> >>> Could this be *this == other?
> >>>
> >>>> +       }
> >>>> +
> >>>>          constexpr mapping&
> >>>>          operator=(const mapping&) noexcept = default;
> >>>>
> >>>> @@ -518,8 +535,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>>>           constexpr index_type
> >>>>           operator()(_Indices... __indices) const noexcept
> >>>>           {
> >>>> -         return __mdspan::__linear_index_left(
> >>>> -           this->extents(), static_cast<index_type>(__indices)...);
> >>>> +         return __mdspan::__linear_index_left(_M_extents,
> >>>> +           static_cast<index_type>(__indices)...);
> >>>>           }
> >>>>
> >>>>          static constexpr bool
> >>>> @@ -633,6 +650,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>>>           : mapping(__other.extents(), __mdspan::__internal_ctor{})
> >>>>           { }
> >>>>
> >>>> +      template<class _OExtents>
> >>>> +       requires (is_constructible_v<extents_type, _OExtents>)
> >>>> +       constexpr explicit(extents_type::rank() > 0)
> >>>> +       mapping(const layout_stride::mapping<_OExtents>& __other)
> >> noexcept
> >>>> +       : mapping(__other.extents(), __mdspan::__internal_ctor{})
> >>>> +       {
> >>>> +         __glibcxx_assert(
> >>>> +           layout_right::mapping<_OExtents>(__other.extents()) ==
> >>>> __other);
> >>>>
> >>> Similary here.
> >>>
> >>>> +       }
> >>>> +
> >>>>          constexpr mapping&
> >>>>          operator=(const mapping&) noexcept = default;
> >>>>
> >>>> @@ -695,6 +722,192 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>>>           [[no_unique_address]] _Extents _M_extents;
> >>>>        };
> >>>>
> >>>> +  namespace __mdspan
> >>>> +  {
> >>>> +    template<typename _Mapping, size_t... _Counts>
> >>>> +      constexpr typename _Mapping::index_type
> >>>> +      __offset_impl(const _Mapping& __m, index_sequence<_Counts...>)
> >>>> noexcept
> >>>> +      { return __m(((void) _Counts, 0)...); }
> >>>> +
> >>>> +    template<typename _Mapping>
> >>>> +      constexpr typename _Mapping::index_type
> >>>> +      __offset(const _Mapping& __m) noexcept
> >>>> +      {
> >>>>
> >>> Again, I would define __impl as nested lambda here:
> >>> auto __impl = [&]<size_t... Countws>(index_seqeunce<_Counts>) noexcept
> >>> { return  __m(((void) _Counts, 0)...);  }
> >>>
> >>>> +       return __offset_impl(__m,
> >>>> +           make_index_sequence<_Mapping::extents_type::rank()>());
> >>>> +      }
> >>>> +
> >>>> +    template<typename _Mapping, typename... _Indices>
> >>>> +      constexpr typename _Mapping::index_type
> >>>> +      __linear_index_strides(const _Mapping& __m,
> >>>> +                            _Indices... __indices)
> >>>> +      {
> >>>> +       using _IndexType = typename _Mapping::index_type;
> >>>> +       _IndexType __res = 0;
> >>>> +       if constexpr (sizeof...(__indices) > 0)
> >>>> +         {
> >>>> +           auto __update = [&, __pos = 0u](_IndexType __idx) mutable
> >>>> +             {
> >>>> +               __res += __idx * __m.stride(__pos++);
> >>>> +             };
> >>>> +           (__update(__indices), ...);
> >>>> +         }
> >>>> +       return __res;
> >>>> +      }
> >>>> +  }
> >>>> +
> >>>> +  template<typename _Extents>
> >>>> +    class layout_stride::mapping
> >>>> +    {
> >>>> +      static_assert(__mdspan::__layout_extent<_Extents>,
> >>>> +       "The size of extents_type must be representable as
> index_type");
> >>>> +
> >>>> +    public:
> >>>> +      using extents_type = _Extents;
> >>>> +      using index_type = typename extents_type::index_type;
> >>>> +      using size_type = typename extents_type::size_type;
> >>>> +      using rank_type = typename extents_type::rank_type;
> >>>> +      using layout_type = layout_stride;
> >>>> +
> >>>> +      constexpr
> >>>> +      mapping() noexcept
> >>>> +      {
> >>>> +       auto __stride = index_type(1);
> >>>> +       for(size_t __i = extents_type::rank(); __i > 0; --__i)
> >>>> +         {
> >>>> +           _M_strides[__i - 1] = __stride;
> >>>> +           __stride *= _M_extents.extent(__i - 1);
> >>>> +         }
> >>>> +      }
> >>>> +
> >>>> +      constexpr
> >>>> +      mapping(const mapping&) noexcept = default;
> >>>> +
> >>>> +      template<__mdspan::__valid_index_type<index_type> _OIndexType>
> >>>> +       constexpr
> >>>> +       mapping(const extents_type& __exts,
> >>>> +               span<_OIndexType, extents_type::rank()> __strides)
> >> noexcept
> >>>> +       : _M_extents(__exts)
> >>>> +       {
> >>>> +         for(size_t __i = 0; __i < extents_type::rank(); ++__i)
> >>>> +           _M_strides[__i] = index_type(as_const(__strides[__i]));
> >>>> +       }
> >>>> +
> >>>> +      template<__mdspan::__valid_index_type<index_type> _OIndexType>
> >>>> +       constexpr
> >>>> +       mapping(const extents_type& __exts,
> >>>> +               const array<_OIndexType, extents_type::rank()>&
> >> __strides)
> >>>> +       noexcept
> >>>> +       : mapping(__exts,
> >>>> +                 span<const _OIndexType,
> >> extents_type::rank()>(__strides))
> >>>> +       { }
> >>>> +
> >>>> +      template<__mdspan::__mapping_like _StridedMapping>
> >>>> +       requires (is_constructible_v<extents_type,
> >>>> +                                    typename
> >>>> _StridedMapping::extents_type>
> >>>> +                && _StridedMapping::is_always_unique()
> >>>> +                && _StridedMapping::is_always_strided())
> >>>> +       constexpr explicit(!(
> >>>> +         is_convertible_v<typename _StridedMapping::extents_type,
> >>>> extents_type>
> >>>> +         && __mdspan::__standardized_mapping<_StridedMapping>))
> >>>> +       mapping(const _StridedMapping& __other) noexcept
> >>>> +       : _M_extents(extents_type(__other.extents()))
> >>>> +       {
> >>>> +         if constexpr (extents_type::rank() > 0)
> >>>> +           for(size_t __i = 0; __i < extents_type::rank(); ++__i)
> >>>> +             _M_strides[__i] = index_type(__other.stride(__i));
> >>>> +       }
> >>>> +
> >>>> +      constexpr mapping&
> >>>> +      operator=(const mapping&) noexcept = default;
> >>>> +
> >>>> +      constexpr const _Extents&
> >>>> +      extents() const noexcept { return _M_extents; }
> >>>> +
> >>>> +      constexpr array<index_type, extents_type::rank()>
> >>>> +      strides() const noexcept { return _M_strides; }
> >>>>
> >>> Does this constructor work? I mean _M_strides in not std::array,
> >>> just array. I assumed you would need to create local array and copy
> them.
> >>>
> >>>> +
> >>>> +      constexpr index_type
> >>>> +      required_span_size() const noexcept
> >>>> +      {
> >>>> +       index_type __ret = 1;
> >>>> +       for(size_t __i = 0; __i < extents_type::rank(); ++__i)
> >>>> +         {
> >>>> +           index_type __ext = _M_extents.extent(__i);
> >>>> +           if(__ext == 0)
> >>>> +             return 0;
> >>>> +           __ret += (__ext - 1) * _M_strides[__i];
> >>>> +         }
> >>>> +       return __ret;
> >>>> +      }
> >>>> +
> >>>> +      template<__mdspan::__valid_index_type<index_type>... _Indices>
> >>>> +       requires (sizeof...(_Indices) == extents_type::rank())
> >>>> +       constexpr index_type operator()(_Indices... __indices) const
> >>>> noexcept
> >>>> +       {
> >>>> +         return __mdspan::__linear_index_strides(*this,
> >>>> +           static_cast<index_type>(__indices)...);
> >>>> +       }
> >>>> +
> >>>> +      static constexpr bool
> >>>> +      is_always_unique() noexcept { return true; }
> >>>> +
> >>>> +      static constexpr bool
> >>>> +      is_always_exhaustive() noexcept { return false; }
> >>>> +
> >>>> +      static constexpr bool
> >>>> +      is_always_strided() noexcept { return true; }
> >>>> +
> >>>> +      static constexpr bool
> >>>> +      is_unique() noexcept { return true; }
> >>>> +
> >>>> +      constexpr bool
> >>>> +      is_exhaustive() const noexcept
> >>>> +      {
> >>>> +       if constexpr (extents_type::rank() == 0)
> >>>> +         return true;
> >>>> +       else
> >>>> +       {
> >>>> +         // Under the assumption that the mapping is unique and has
> >>>> positive
> >>>> +         // size, the mapping is exhaustive, if and only if the
> largest
> >>>> value
> >>>> +         // returned by m(i...) is within the required range.
> >>>> +         // However, the standard requires implementing a condition
> >>>> that's not
> >>>> +         // always true when the size of the mapping is zero.
> >>>>
> >>> With the precondition on the constructor
> >>> https://eel.is/c++draft/mdspan.layout.stride#cons-4.3,
> >>> I do not think you can produce a layout_stride where this would
> produce a
> >>> different result.
> >>> If you agree I would change the comment that "C++23
> >>> [mdspan.layout.stride.cons] p4.3"
> >>> implies that this produces the same result.
> >>
> >> If the size of the extent is zero, then almost any choice of strides
> >> will work as a counter example. For example:
> >>
> >>     extents = [0, 1, 1]
> >>     strides = [2, 4, 8]
> >>
> >> is exhaustive, because every element (of the empty set) is covered.
> >> However, [2, 4, 8] don't match the formula in the spec [1].
> >>
> > My point is that constructing layout_stride with extents and strides from
> > the
> > example violates the precondition on the constructor:
> > https://eel.is/c++draft/mdspan.layout.stride#cons-4.3.
> > So this function cannot observe the difference.
> >
>
> I don't see it. I'll run through it step by step.
>
> Pick `p[i] = i` as the permutation and check that:
>     i == 1: s[p[1]] >= s[p[0]] * e[p[0]]
>                s[1] >= s[0] * e[0]
>                  4  >= 2 * 0
>
>     i == 2:    s[2] >= s[1] * e[1]
>                  8  >= 4 * 1
>
> Therefore, rank_ == 3 > 0 and there exists a permutation P
> such that
>      s[P[i]] >= s[P[i-1]] * e[P[i-1]
> for all i in [1, rank_).
>
> I think that covers the entire precondition.
>
Indeed, however if we consider following:
  extents = [1, 3, 0]
  strides = [2, 8, 8]
This is exhaustive (and your checks answer that), and technically
is_strided.
However, the constructor will not accept it. I think this is general
question on
empty mappings and layout strided.


> >>
> >> If the size of extents isn't zero, then the two approaches are the
> >> same.
> >>
> >> [1]: https://eel.is/c++draft/mdspan.layout.stride#obs-5.2
> >>
> >>>>
> >>>> +         auto __size = __mdspan::__fwd_prod(_M_extents,
> >>>> extents_type::rank());
> >>>> +         if (__size == 0)
> >>>> +           return true;
> >>>> +         return __size == required_span_size();
> >>>> +       }
> >>>> +      }
> >>>> +
> >>>> +      static constexpr bool
> >>>> +      is_strided() noexcept { return true; }
> >>>> +
> >>>> +      constexpr index_type
> >>>> +      stride(rank_type __r) const noexcept { return _M_strides[__r];
> }
> >>>> +
> >>>> +      template<__mdspan::__mapping_like _OMapping>
> >>>> +       requires ((extents_type::rank() ==
> >> _OMapping::extents_type::rank())
> >>>> +                 && _OMapping::is_always_strided())
> >>>> +       friend constexpr bool
> >>>> +       operator==(const mapping& __self, const _OMapping& __other)
> >>>> noexcept
> >>>> +       {
> >>>> +         if(__self.extents() != __other.extents())
> >>>> +           return false;
> >>>> +         if constexpr (extents_type::rank() > 0)
> >>>> +           for(size_t __i = 0; __i < extents_type::rank(); ++__i)
> >>>> +             if (__self.stride(__i) != __other.stride(__i))
> >>>> +               return false;
> >>>> +         return __mdspan::__offset(__other) == 0;
> >>>> +       }
> >>>> +
> >>>> +    private:
> >>>> +      using _S_strides_t = typename __array_traits<index_type,
> >>>> +
> >>>> extents_type::rank()>::_Type;
> >>>> +      [[no_unique_address]] _Extents _M_extents;
> >>>> +      [[no_unique_address]] _S_strides_t _M_strides;
> >>>> +    };
> >>>> +
> >>>>    _GLIBCXX_END_NAMESPACE_VERSION
> >>>>    }
> >>>>    #endif
> >>>> --
> >>>> 2.49.0
> >>>>
> >>>>
> >>>
> >>
> >>
> >
>
>

Reply via email to