Skip to content
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

Add Comprehensive Test for Multigeometry Range Classes #1197

Merged
merged 22 commits into from
Jul 31, 2023
Merged
Changes from 1 commit
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
Prev Previous commit
Next Next commit
add one thousands test for multipolygon range, and fix polygon/multip…
…olygon_ref
  • Loading branch information
isVoid committed Jul 25, 2023
commit 75a8657d2b3a1ad6c55da13379b7fa5811015a88
4 changes: 2 additions & 2 deletions cpp/include/cuspatial/detail/geometry/polygon_ref.cuh
Original file line number Diff line number Diff line change
@@ -59,13 +59,13 @@ CUSPATIAL_HOST_DEVICE auto polygon_ref<RingIterator, VecIterator>::ring_end() co
template <typename RingIterator, typename VecIterator>
CUSPATIAL_HOST_DEVICE auto polygon_ref<RingIterator, VecIterator>::point_begin() const
{
return _point_begin;
return thrust::next(_point_begin, *_ring_begin);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain these changes? Why can't point_begin be used as stored?

Copy link
Contributor Author

@isVoid isVoid Jul 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tl;dr. Because point_begin API is supposed to return only the point range to the current multilinestring (not all multilinestrings). And that the offsets stored in a *_ref are global offsets to the lower level range.

Consider this example:

Multilinestring_range:
Geometry offsets: [0, 2, 4]
Part Offsets: [0, 2, 4, 6, 8]
Points: [P0, P1... P8]

A multilinestring_ref constructed for the second multilinestring is stored as below:

Part Offsets: [4, 6, 8]
Points: [P0, ... P8]

Notice that in part offset, the offsets are global offsets (4 means that the first point to the multilinestring locates at _point_begin + 4). You may ask why we don't store as below:

Part Offsets: [0, 2, 4]
Points: [P4 ... P8]

That's an alternative too. Just different ways to skin a cat. I don't see an advantage to either one.

}

template <typename RingIterator, typename VecIterator>
CUSPATIAL_HOST_DEVICE auto polygon_ref<RingIterator, VecIterator>::point_end() const
{
return _point_end;
return thrust::next(_point_begin, *thrust::prev(_ring_end));
}

template <typename RingIterator, typename VecIterator>
Original file line number Diff line number Diff line change
@@ -112,14 +112,14 @@ template <typename PartIterator, typename RingIterator, typename VecIterator>
CUSPATIAL_HOST_DEVICE auto multipolygon_ref<PartIterator, RingIterator, VecIterator>::point_begin()
const
{
return _point_begin;
return thrust::next(_point_begin, *thrust::next(_ring_begin, *_part_begin));
}

template <typename PartIterator, typename RingIterator, typename VecIterator>
CUSPATIAL_HOST_DEVICE auto multipolygon_ref<PartIterator, RingIterator, VecIterator>::point_end()
const
{
return _point_end;
return thrust::next(_point_begin, *thrust::next(_ring_begin, *thrust::prev(_part_end)));
}

template <typename PartIterator, typename RingIterator, typename VecIterator>
2 changes: 1 addition & 1 deletion cpp/include/cuspatial_test/geometry_generator.cuh
Original file line number Diff line number Diff line change
@@ -354,7 +354,7 @@ auto generate_multilinestring_array(multilinestring_generator_parameter<T> param
params.origin,
detail::random_walk_functor<T>{params.segment_length});

return make_multilinestring_array(
return make_multilinestring_array<std::size_t, T>(
std::move(geometry_offset), std::move(part_offset), std::move(points));
}

53 changes: 39 additions & 14 deletions cpp/include/cuspatial_test/vector_factories.cuh
Original file line number Diff line number Diff line change
@@ -101,6 +101,17 @@ class multipolygon_array {
{
}

multipolygon_array(rmm::device_vector<geometry_t>&& geometry_offsets_array,
rmm::device_vector<part_t>&& part_offsets_array,
rmm::device_vector<ring_t>&& ring_offsets_array,
rmm::device_vector<coord_t>&& coordinates_array)
: _geometry_offsets_array(std::move(geometry_offsets_array)),
_part_offsets_array(std::move(part_offsets_array)),
_ring_offsets_array(std::move(ring_offsets_array)),
_coordinates_array(std::move(coordinates_array))
{
}

multipolygon_array(rmm::device_uvector<geometry_t>&& geometry_offsets_array,
rmm::device_uvector<part_t>&& part_offsets_array,
rmm::device_uvector<ring_t>&& ring_offsets_array,
@@ -230,9 +241,9 @@ class multilinestring_array {
multilinestring_array(GeometryArray geometry_offsets_array,
PartArray part_offsets_array,
CoordinateArray coordinate_array)
: _geometry_offset_array(geometry_offsets_array),
_part_offset_array(part_offsets_array),
_coordinate_array(coordinate_array)
: _geometry_offset_array(std::move(geometry_offsets_array)),
_part_offset_array(std::move(part_offsets_array)),
_coordinate_array(std::move(coordinate_array))
{
}

@@ -271,19 +282,33 @@ class multilinestring_array {
* @param coord_inl Ramge of coordinate
* @return multilinestring array object
*/
template <typename IndexRangeA,
typename IndexRangeB,
typename CoordRange,
typename IndexType = typename IndexRangeB::value_type>
auto make_multilinestring_array(IndexRangeA geometry_inl,
IndexRangeB part_inl,
CoordRange coord_inl)
template <typename IndexType, typename T>
auto make_multilinestring_array(rmm::device_uvector<IndexType>&& geometry_inl,
rmm::device_uvector<IndexType>&& part_inl,
rmm::device_uvector<vec_2d<T>>&& coord_inl)
{
using CoordType = typename CoordRange::value_type;
using DeviceIndexVector = rmm::device_vector<IndexType>;
using DeviceCoordVector = rmm::device_vector<CoordType>;
return multilinestring_array<rmm::device_uvector<IndexType>,
rmm::device_uvector<IndexType>,
rmm::device_uvector<vec_2d<T>>>(
std::move(geometry_inl), std::move(part_inl), std::move(coord_inl));
}

return multilinestring_array<DeviceIndexVector, DeviceIndexVector, DeviceCoordVector>(
/**
* @brief Construct an owning object of a multilinestring array from ranges
*
* @param geometry_inl Range of geometry offsets
* @param part_inl Range of part offsets
* @param coord_inl Ramge of coordinate
* @return multilinestring array object
*/
template <typename IndexType, typename T>
auto make_multilinestring_array(rmm::device_vector<IndexType>&& geometry_inl,
rmm::device_vector<IndexType>&& part_inl,
rmm::device_vector<vec_2d<T>>&& coord_inl)
{
return multilinestring_array<rmm::device_vector<IndexType>,
rmm::device_vector<IndexType>,
rmm::device_vector<vec_2d<T>>>(
std::move(geometry_inl), std::move(part_inl), std::move(coord_inl));
}

186 changes: 184 additions & 2 deletions cpp/tests/range/multipolygon_range_test.cu
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@
#include <rmm/exec_policy.hpp>
#include <rmm/mr/device/device_memory_resource.hpp>

#include <thrust/sequence.h>
#include <thrust/tabulate.h>

#include <initializer_list>
@@ -659,7 +660,7 @@ class MultipolygonRangeTestBase : public BaseFixture {
template <typename MultiPolygonRef>
__device__ vec_2d<T> operator()(MultiPolygonRef mpolygon)
{
return mpolygon.size() > 0 ? mpolygon[0].point_begin()[0] : vec_2d<T>{-1, -1};
return mpolygon.size() > 0 ? mpolygon.point_begin()[0] : vec_2d<T>{-1, -1};
}
};

@@ -776,7 +777,11 @@ class MultipolygonRangeTestBase : public BaseFixture {
{
auto rng = range();
auto d_points = rmm::device_uvector<vec_2d<T>>(rng.num_multipolygons(), stream());
thrust::copy(rmm::exec_policy(stream()), rng.point_begin(), rng.point_end(), d_points.begin());
thrust::transform(rmm::exec_policy(stream()),
rng.multipolygon_begin(),
rng.multipolygon_end(),
d_points.begin(),
copy_leading_point_functor{});
return d_points;
}

@@ -1138,3 +1143,180 @@ class MultipolygonRangeOneTest : public MultipolygonRangeTestBase<T> {
TYPED_TEST_CASE(MultipolygonRangeOneTest, FloatingPointTypes);

TYPED_TEST(MultipolygonRangeOneTest, OneMultipolygonRange) { this->run_test(); }

template <typename T>
class MultipolygonRangeOneThousandTest : public MultipolygonRangeTestBase<T> {
public:
struct make_points_functor {
__device__ auto operator()(std::size_t i)
{
auto geometry_idx = i / 4;
auto intra_point_idx = i % 4;
return vec_2d<T>{geometry_idx * T{10.} + intra_point_idx,
geometry_idx * T{10.} + intra_point_idx};
}
};

void make_test_multipolygon()
{
auto geometry_offsets = rmm::device_vector<std::size_t>(1001);
auto part_offsets = rmm::device_vector<std::size_t>(1001);
auto ring_offsets = rmm::device_vector<std::size_t>(1001);
auto coordinates = rmm::device_vector<vec_2d<T>>(4000);

thrust::sequence(
rmm::exec_policy(this->stream()), geometry_offsets.begin(), geometry_offsets.end());

thrust::sequence(rmm::exec_policy(this->stream()), part_offsets.begin(), part_offsets.end());

thrust::sequence(
rmm::exec_policy(this->stream()), ring_offsets.begin(), ring_offsets.end(), 0, 4);

thrust::tabulate(rmm::exec_policy(this->stream()),
coordinates.begin(),
coordinates.end(),
make_points_functor{});

this->test_multipolygon = std::make_unique<multipolygon_array<rmm::device_vector<std::size_t>,
rmm::device_vector<std::size_t>,
rmm::device_vector<std::size_t>,
rmm::device_vector<vec_2d<T>>>>(
std::move(geometry_offsets),
std::move(part_offsets),
std::move(ring_offsets),
std::move(coordinates));
}

void test_num_multipolygons() { EXPECT_EQ(this->range().num_multipolygons(), 1000); }

void test_num_polygons() { EXPECT_EQ(this->range().num_polygons(), 1000); }

void test_num_rings() { EXPECT_EQ(this->range().num_rings(), 1000); }

void test_num_points() { EXPECT_EQ(this->range().num_points(), 4000); }

void test_multipolygon_it()
{
rmm::device_uvector<vec_2d<T>> d_points = this->copy_leading_point_multipolygon();
rmm::device_uvector<vec_2d<T>> expected(1000, this->stream());
thrust::tabulate(rmm::exec_policy(this->stream()),
expected.begin(),
expected.end(),
[] __device__(std::size_t i) {
return vec_2d<T>{i * T{10.}, i * T{10.}};
});

CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(d_points, expected);
}

void test_point_it()
{
rmm::device_uvector<vec_2d<T>> d_points = this->copy_all_points();
rmm::device_uvector<vec_2d<T>> expected(4000, this->stream());

thrust::tabulate(
rmm::exec_policy(this->stream()), expected.begin(), expected.end(), make_points_functor{});

CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(d_points, expected);
}

void test_geometry_offsets_it()
{
rmm::device_uvector<std::size_t> d_offsets = this->copy_geometry_offsets();
auto expected = rmm::device_uvector<std::size_t>(1001, this->stream());

thrust::sequence(rmm::exec_policy(this->stream()), expected.begin(), expected.end());

CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(d_offsets, expected);
}

void test_part_offset_it()
{
rmm::device_uvector<std::size_t> d_offsets = this->copy_part_offsets();
auto expected = rmm::device_uvector<std::size_t>(1001, this->stream());

thrust::sequence(rmm::exec_policy(this->stream()), expected.begin(), expected.end());

CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(d_offsets, expected);
}

void test_ring_offset_it()
{
rmm::device_uvector<std::size_t> d_offsets = this->copy_ring_offsets();
auto expected = rmm::device_uvector<std::size_t>(1001, this->stream());

thrust::sequence(rmm::exec_policy(this->stream()), expected.begin(), expected.end(), 0, 4);

CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(d_offsets, expected);
}

void test_ring_idx_from_point_idx()
{
rmm::device_uvector<std::size_t> d_ring_idx = this->copy_ring_idx_from_point_idx();
auto expected = rmm::device_uvector<std::size_t>(4000, this->stream());

thrust::tabulate(rmm::exec_policy(this->stream()),
expected.begin(),
expected.end(),
[] __device__(std::size_t i) { return i / 4; });

CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(d_ring_idx, expected);
}

void test_part_idx_from_ring_idx()
{
rmm::device_uvector<std::size_t> d_part_idx = this->copy_part_idx_from_ring_idx();
auto expected = rmm::device_uvector<std::size_t>(1000, this->stream());

thrust::sequence(rmm::exec_policy(this->stream()), expected.begin(), expected.end());

CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(d_part_idx, expected);
}

void test_geometry_idx_from_part_idx()
{
rmm::device_uvector<std::size_t> d_geometry_idx = this->copy_geometry_idx_from_part_idx();
auto expected = rmm::device_uvector<std::size_t>(1000, this->stream());

thrust::sequence(rmm::exec_policy(this->stream()), expected.begin(), expected.end());

CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(d_geometry_idx, expected);
}

void test_array_access_operator()
{
auto all_points = this->copy_all_points_of_ith_multipolygon(777);
auto expected = make_device_vector<vec_2d<T>>({
{7770, 7770},
{7771, 7771},
{7772, 7772},
{7773, 7773},
});

CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(all_points, expected);
}

void test_multipolygon_point_count_it()
{
rmm::device_uvector<std::size_t> d_point_count = this->copy_multipolygon_point_count();
auto expected = rmm::device_uvector<std::size_t>(1000, this->stream());

thrust::fill(rmm::exec_policy(this->stream()), expected.begin(), expected.end(), 4);

CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(d_point_count, expected);
}

void test_multipolygon_ring_count_it()
{
rmm::device_uvector<std::size_t> d_ring_count = this->copy_multipolygon_ring_count();
auto expected = rmm::device_uvector<std::size_t>(1000, this->stream());

thrust::fill(rmm::exec_policy(this->stream()), expected.begin(), expected.end(), 1);

CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(d_ring_count, expected);
}
};

TYPED_TEST_CASE(MultipolygonRangeOneThousandTest, FloatingPointTypes);

TYPED_TEST(MultipolygonRangeOneThousandTest, OneThousandMultipolygonRange) { this->run_test(); }