Skip to content

Python like Range#2006

Open
artv3 wants to merge 25 commits into
developfrom
artv3/zeroTo
Open

Python like Range#2006
artv3 wants to merge 25 commits into
developfrom
artv3/zeroTo

Conversation

@artv3

@artv3 artv3 commented Mar 25, 2026

Copy link
Copy Markdown
Member

#Summary

Many of my loops start at zero, this gives users a shortcut to generate the desired range segment.

@artv3 artv3 requested a review from a team March 25, 2026 22:49
Comment thread include/RAJA/index/RangeSegment.hpp Outdated
@artv3 artv3 changed the title Add ZeroTo RangeSegment Python like Range Mar 31, 2026
@artv3

artv3 commented Mar 31, 2026

Copy link
Copy Markdown
Member Author

Hi @llnl/raja-core I've pushed up the discussed changes.

Comment thread examples/raja-ranges.cpp Outdated
Comment thread include/RAJA/index/RangeSegment.hpp Outdated
Comment thread include/RAJA/index/RangeSegment.hpp Outdated
Comment thread include/RAJA/index/RangeSegment.hpp Outdated
Comment thread include/RAJA/index/RangeSegment.hpp Outdated
Comment thread include/RAJA/index/RangeSegment.hpp Outdated
Comment thread include/RAJA/index/RangeSegment.hpp Outdated
Comment thread include/RAJA/index/RangeSegment.hpp Outdated
Comment thread test/unit/index/test-rangesegment.cpp
Comment thread test/unit/index/test-rangesegment.cpp
@artv3

artv3 commented Apr 17, 2026

Copy link
Copy Markdown
Member Author

I played with making a range type that could have any of begin, end, and stride be runtime or compile time constants. Is that something that you are interested in @artv3. Right now you construct the range in the lambda so the compiler knows that begin is a constant 0, but if you constructed the range outside of the lambda and captured it then the compiler would not know that begin is a constant. If you had a range type that could have any of the parameters be constant or runtime could fix that.

Yes absolutely! We are always stronger when we combine our ideas! @rhornung67, @MrBurmark could this be something we carve out time to discuss at the next RAJA meeting?

@MrBurmark

Copy link
Copy Markdown
Member

Yes absolutely! We are always stronger when we combine our ideas! @rhornung67, @MrBurmark could this be something we carve out time to discuss at the next RAJA meeting?

Here is a basic prototype without support for things like strongly typed indices. https://godbolt.org/z/o4zdcEEdT

@artv3 artv3 added this to the June 2026 Release milestone Apr 28, 2026
@MrBurmark

Copy link
Copy Markdown
Member

Yes absolutely! We are always stronger when we combine our ideas! @rhornung67, @MrBurmark could this be something we carve out time to discuss at the next RAJA meeting?

Here is a basic prototype without support for things like strongly typed indices. https://godbolt.org/z/o4zdcEEdT

Regarding making a range where the member variables can be runtime or compile time, that could involve changing the underlying TypedRange class and combining it with the strided version. Maybe we could even unify our integral range types into a single class. Potentially usable in the subviews PR as well #1915. Let's think about doing that as a separate PR.

@artv3

artv3 commented Jun 19, 2026

Copy link
Copy Markdown
Member Author

I pushed up code that aims to address the comments, my question now though if we are happy with the following:

  - Compile-time rejection for the explicit mismatch cases:
      - RAJA::range<int>(RangeStrongIndex(3), 17) is rejected.
      - RAJA::range<RangeStrongIndex>(AnotherRangeStrongIndex(3), 17) is rejected.
      - The same pattern is enforced for the strided overload.

Is this too aggressive or should we allow mixing if the underlying data type is the same? I don't use strongly typed indices so I would be interested in how folks would like to handle these cases

@MrBurmark

Copy link
Copy Markdown
Member

I pushed up code that aims to address the comments, my question now though if we are happy with the following:

  - Compile-time rejection for the explicit mismatch cases:
      - RAJA::range<int>(RangeStrongIndex(3), 17) is rejected.
      - RAJA::range<RangeStrongIndex>(AnotherRangeStrongIndex(3), 17) is rejected.
      - The same pattern is enforced for the strided overload.

Is this too aggressive or should we allow mixing if the underlying data type is the same? I don't use strongly typed indices so I would be interested in how folks would like to handle these cases

I think these cases should fail, rejecting incorrect index types is the whole point.
Earlier on this PR I asked that mixing one or more non-strongly typed index and one strongly typed index work. I'm not sure is that is a good idea or not, I was trying to let people do this RAJA::range(1, rsiN) instead of this RAJA::range(RangeStrongIndex(1), rsiN). It seemed overly verbose, but I'm not sure that is really in the spirit of stringly typed indices or not.

@MrBurmark

Copy link
Copy Markdown
Member

Actually thinking about how types are decided, I'm not sure that it makes sense to take the type if the stride into account. If begin and end are int and stride is long for example we would currently choose long for the overall index type. However all of the values would have to be representable as int, I suppose the one-past the last value could possibly not be representable, for example with 2-bit ints range(0,3,2) would be problematic as it would contain {0, 2} with 4 being the one-past the end value and not representable. I'm not sure that we want to handle that case by hoping people provide a larger stride type if they plan to have a range like that.

@artv3

artv3 commented Jun 19, 2026

Copy link
Copy Markdown
Member Author

Actually thinking about how types are decided, I'm not sure that it makes sense to take the type if the stride into account. If begin and end are int and stride is long for example we would currently choose long for the overall index type. However all of the values would have to be representable as int, I suppose the one-past the last value could possibly not be representable, for example with 2-bit ints range(0,3,2) would be problematic as it would contain {0, 2} with 4 being the one-past the end value and not representable. I'm not sure that we want to handle that case by hoping people provide a larger stride type if they plan to have a range like that.

right, maybe we can bring it up at the next @llnl/raja-core meeting

@MrBurmark

MrBurmark commented Jun 23, 2026

Copy link
Copy Markdown
Member
RAJA::range<int>(RangeStrongIndex(3), 17) // is rejected.
RAJA::range<RangeStrongIndex>(AnotherRangeStrongIndex(3), 17) // is rejected.
RAJA::range<RangeStrongIndex>(RangeStrongIndex(3), RangeStrongIndex(17)) // is ok.
RAJA::range<RangeStrongIndex>(RangeStrongIndex(3), 17) // is ok.
RAJA::range<RangeStrongIndex>(3, 17) // is ok.
RAJA::range(RangeStrongIndex(3), RangeStrongIndex(17)) // is ok, chooses RangeStrongIndex.
RAJA::range(RangeStrongIndex(3), 17) // is ok, chooses RangeStrongIndex.
RAJA::range(3, 17) // is ok, chooses int.
RAJA::range(RangeStrongIndex(3), static_cast<long>(17)) // is rejected.
RAJA::range(static_cast<short>(3), 17) // is ok, chooses int.
RAJA::range(3, static_cast<short>(17)) // is ok, chooses int.

@artv3

artv3 commented Jun 23, 2026

Copy link
Copy Markdown
Member Author
RAJA::range<int>(RangeStrongIndex(3), 17) rejected.
RAJA::range<RangeStrongIndex>(AnotherRangeStrongIndex(3), 17) is rejected.
RAJA::range<RangeStrongIndex>(RangeStrongIndex(3), RangeStrongIndex(17)) is ok.
RAJA::range<RangeStrongIndex>(RangeStrongIndex(3), 17) is ok?
RAJA::range<RangeStrongIndex>(3, 17) is ok?
RAJA::range(RangeStrongIndex(3), RangeStrongIndex(17)) is ok.
RAJA::range(RangeStrongIndex(3), 17) is ok?
RAJA::range(3, 17) is ok.

I'm in favor of supporting those that read "ok" -- let me know if there are objections. The only concern is type narrowing, but I think we should build with -Werror=narrowing

@rhornung67

Copy link
Copy Markdown
Member

I added warnings as errors to CI improvements issue here: #1943

Please add specific items to that list and we will eventually work them in.

@MrBurmark

MrBurmark commented Jun 23, 2026

Copy link
Copy Markdown
Member
RAJA::range<int>(3, 17, 1) // is ok
RAJA::range<int>(3, 17, static_cast<short>(1))  // is ok
RAJA::range<int>(3, 17, static_cast<long>(1))  // is rejected? or is ok?
RAJA::range<int>(3, 17, RangeStrongIndex(1))  // is rejected
RAJA::range<RangeStrongIndex>(3, 17, 1)  // is ok.
RAJA::range<RangeStrongIndex>(RangeStrongIndex(3), RangeStrongIndex(17), 1)  // is ok.
RAJA::range<RangeStrongIndex>(RangeStrongIndex(3), RangeStrongIndex(17), RangeStrongIndex(1))  // is ok.
RAJA::range(3, 17, 1) // is ok, chooses int.
RAJA::range(3, 17, static_cast<short>(1))  // is ok, chooses int.
RAJA::range(3, 17, static_cast<long>(1))  // is rejected? or is ok, chooses int?
RAJA::range(3, 17, RangeStrongIndex(1))  // is rejected
RAJA::range(RangeStrongIndex(3), RangeStrongIndex(17), 1)  // is ok, chooses RangeStrongIndex.
RAJA::range(RangeStrongIndex(3), 17, 1)  // is ok, chooses RangeStrongIndex.

@artv3

artv3 commented Jun 23, 2026

Copy link
Copy Markdown
Member Author
RAJA::range(3, 17, static_cast<long>(1))  // is rejected? or is ok, chooses int?

this would be type narrowing which I think should throw an error

@trws

trws commented Jun 24, 2026

Copy link
Copy Markdown
Member
RAJA::range(3, 17, static_cast<long>(1))  // is rejected? or is ok, chooses int?

this would be type narrowing which I think should throw an error

I've mostly been just lurking here since it looks like you've hit everything I expected @artv3, also I love seeing this idea happen great stuff, but this surprises me. Is it not using std::common_type<Ts...> to get the type to use so that it will use the largest of the specified integral types?

@artv3

artv3 commented Jun 24, 2026

Copy link
Copy Markdown
Member Author
RAJA::range(3, 17, static_cast<long>(1))  // is rejected? or is ok, chooses int?

this would be type narrowing which I think should throw an error

I've mostly been just lurking here since it looks like you've hit everything I expected @artv3, also I love seeing this idea happen great stuff, but this surprises me. Is it not using std::common_type<Ts...> to get the type to use so that it will use the largest of the specified integral types?

oh thats right that would happen, but then is that we we want? to promote (3,17) to longs?

@trws

trws commented Jun 24, 2026

Copy link
Copy Markdown
Member

Speaking just for me it's what I would want. As long as all three are convertible, which I think common_type requires anyway, it makes it much easier to be sure it's correct. I could easily see someone using the range(1, groups::size) construction as self documenting range and if the upper is an int64_t or unsigned or whatever then it just does the right thing. If they aren't losslessly convertible, like the strongly typed indices, then failing makes sense though.

@MrBurmark

MrBurmark commented Jun 24, 2026

Copy link
Copy Markdown
Member

@trws We do use common type across all of the arguments to get a type for the range if you don't specify one. My question for RAJA::range(3, 17, static_cast<long>(1)) is what does it mean if stride is of a larger type than begin and end? In general all of the values in the range have to representable as the common type of begin and end. Stride tells you which of those values to use and in which order they appear. So does it make sense for the type of stride to affect the type used by the range?

@trws

trws commented Jun 25, 2026

Copy link
Copy Markdown
Member

Honestly I think it does, at least in as much as when iter + stride is executed we want the result to be of the same type as iter. I suppose it wouldn't have to be, but if not it would have to be convertible to the type of iter and that seems a bit harder to reason about. Also even if we actually do re-order them based on the sign of the stride, in a logical sense I think of it more like:

for (std::common_type_t<Begin,End,Stride> iter = begin; iter < end; iter += stride)

@artv3

artv3 commented Jun 30, 2026

Copy link
Copy Markdown
Member Author
RAJA::range<int>(3, 17, 1) // is ok
RAJA::range<int>(3, 17, static_cast<short>(1))  // is ok
RAJA::range<int>(3, 17, static_cast<long>(1))  // is rejected? or is ok?
RAJA::range<int>(3, 17, RangeStrongIndex(1))  // is rejected
RAJA::range<RangeStrongIndex>(3, 17, 1)  // is ok.
RAJA::range<RangeStrongIndex>(RangeStrongIndex(3), RangeStrongIndex(17), 1)  // is ok.
RAJA::range<RangeStrongIndex>(RangeStrongIndex(3), RangeStrongIndex(17), RangeStrongIndex(1))  // is ok.
RAJA::range(3, 17, 1) // is ok, chooses int.
RAJA::range(3, 17, static_cast<short>(1))  // is ok, chooses int.
RAJA::range(3, 17, static_cast<long>(1))  // is rejected? or is ok, chooses int?
RAJA::range(3, 17, RangeStrongIndex(1))  // is rejected
RAJA::range(RangeStrongIndex(3), RangeStrongIndex(17), 1)  // is ok, chooses RangeStrongIndex.
RAJA::range(RangeStrongIndex(3), 17, 1)  // is ok, chooses RangeStrongIndex.

My latest push should support this matrix

@artv3 artv3 requested a review from a team June 30, 2026 19:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants