Skip to content

Add shape::, array_ref::, and array::bounds() #105

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

Merged
merged 2 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ docs/*

# Visual Studio folder status
.vs
.vscode

# perf files
perf.data
Expand Down
31 changes: 31 additions & 0 deletions include/array/array.h
Original file line number Diff line number Diff line change
Expand Up @@ -1030,6 +1030,12 @@ NDARRAY_HOST_DEVICE const DimsSrc& assert_dims_compatible(const DimsSrc& src) {
return src;
}

/** Return a tuple of generic `dims` with same min and extents and all strides set to `unresolved`. */
template <class Dims, size_t... Is>
auto bounds_tuple(const Dims& dims, index_sequence<Is...>) {
return std::make_tuple(dim<>(std::get<Is>(dims).min(), std::get<Is>(dims).extent(), unresolved)...);
}

} // namespace internal

template <class... Dims>
Expand Down Expand Up @@ -1293,6 +1299,13 @@ class shape {
NDARRAY_HOST_DEVICE index_t rows() const { return i().extent(); }
NDARRAY_HOST_DEVICE index_t columns() const { return j().extent(); }

/** Returns a shape with dynamic dims, with the same min and extents but
* strides initialized to `nda::unresolved`. This can be used to create
* an array with the same dimensions but different compile-time constraints. */
NDARRAY_HOST_DEVICE auto bounds() const {
return make_shape_from_tuple(internal::bounds_tuple(dims_, dim_indices()));
}

/** A shape is equal to another shape if the dim objects of each
* dimension from both shapes are equal. */
template <class... OtherDims, class = enable_if_same_rank<OtherDims...>>
Expand Down Expand Up @@ -2115,6 +2128,15 @@ class array_ref {
}
const nda::dim<> dim(size_t d) const { return shape_.dim(d); }
NDARRAY_HOST_DEVICE size_type size() const { return shape_.size(); }
/** Returns a shape with dynamic dims, with the same min and extents but
* strides initialized to `nda::unresolved`. This can be used to create
* an array with the same dimensions but different compile-time constraints:
*
* nda::array_ref<T, SrcShape> ref(...);
* nda::array<T, DstShape> y(ref.bounds()); // Compact array with the same `min`
* // and `extents` as `ref`.
*/
NDARRAY_HOST_DEVICE auto bounds() const { return shape_.bounds(); }
NDARRAY_HOST_DEVICE bool empty() const { return base() != nullptr ? shape_.empty() : true; }
NDARRAY_HOST_DEVICE bool is_compact() const { return shape_.is_compact(); }

Expand Down Expand Up @@ -2577,6 +2599,15 @@ class array {
}
const nda::dim<> dim(size_t d) const { return shape_.dim(d); }
size_type size() const { return shape_.size(); }
/** Returns a shape with dynamic dims, with the same min and extents but
* strides initialized to `nda::unresolved`. This can be used to create
* an array with the same dimensions but different compile-time constraints:
*
* nda::array<T, SrcShape> other(...);
* nda::array<T, DstShape> y(other.bounds()); // Compact array with the same `min`
* // and `extents` as `other`.
*/
auto bounds() const { return shape_.bounds(); }
bool empty() const { return shape_.empty(); }
bool is_compact() const { return shape_.is_compact(); }

Expand Down
21 changes: 21 additions & 0 deletions test/array.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,27 @@ TEST(array_default_constructor) {
sparse.clear();
}

TEST(array_construct_from_bounds) {
// Illustrate creating an array from incompatible compile-time
// shapes using bounds().
using SrcShape = shape<dense_dim<>, dim<>>;
using DstShape = shape<dim<>, dense_dim<>>; // dense_dim is swapped.

SrcShape src_shape({-1, 10}, {2, 5, /*stride =*/100});
auto src = array<int, SrcShape>(src_shape);
auto dst = array<int, DstShape>(src.bounds());

// min and extent are preserved.
ASSERT_EQ(src.dim<0>().min(), dst.dim<0>().min());
ASSERT_EQ(src.dim<0>().extent(), dst.dim<0>().extent());
ASSERT_EQ(src.dim<1>().min(), dst.dim<1>().min());
ASSERT_EQ(src.dim<1>().extent(), dst.dim<1>().extent());
// Ensure that `dst` did not inherit the strides of its parent and is
// created compact.
ASSERT(!src.is_compact());
ASSERT(dst.is_compact());
}

TEST(array_static_convertibility) {
using A0 = array_of_rank<int, 0>;
using A3 = array_of_rank<int, 3>;
Expand Down
25 changes: 25 additions & 0 deletions test/shape.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ TEST(shape_scalar) {
ASSERT_EQ(s.flat_extent(), 1);
ASSERT_EQ(s.size(), 1);
ASSERT_EQ(s(), 0);
ASSERT_EQ(s.bounds().rank(), 0);
}

TEST(shape_1d) {
Expand Down Expand Up @@ -205,6 +206,30 @@ TEST(auto_strides) {
test_auto_strides<10>();
}

TEST(bounds) {
dim</* Min= */ 0, /* Extent= */ 10> x;
dense_dim<> y(-2, 12);
auto s = make_shape(x, y);
s.resolve();

auto bounds = s.bounds();
// Returns a generic shape w/o compile-time min, extents, or strides.
ASSERT_EQ(bounds.dim<0>().Min, dynamic);
ASSERT_EQ(bounds.dim<0>().Extent, dynamic);
ASSERT_EQ(bounds.dim<0>().Stride, dynamic);
ASSERT_EQ(bounds.dim<1>().Min, dynamic);
ASSERT_EQ(bounds.dim<1>().Extent, dynamic);
ASSERT_EQ(bounds.dim<1>().Stride, dynamic);
// Check that dynamic min and extents are preserved.
ASSERT_EQ(bounds.dim<0>().min(), x.min());
ASSERT_EQ(bounds.dim<0>().extent(), x.extent());
ASSERT_EQ(bounds.dim<1>().min(), y.min());
ASSERT_EQ(bounds.dim<1>().extent(), y.extent());
// Bounds have strides set to unresolved.
ASSERT_EQ(bounds.dim<0>().stride(), nda::unresolved);
ASSERT_EQ(bounds.dim<1>().stride(), nda::unresolved);
}

TEST(broadcast_dim) {
dim<> x(0, 10, 1);
broadcast_dim<> y;
Expand Down