From 965b80acaa7ce73363086f7b9350b12702957042 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 28 Feb 2023 10:18:10 -0800 Subject: [PATCH 01/60] initial --- .../detail/geometry/polygon_ref.cuh | 66 ++++++ .../geometry_collection/multipolygon_ref.cuh | 110 +++++++++ .../detail/point_polygon_distance.cuh | 87 +++++++ .../detail/ranges/multilinestring_range.cuh | 2 +- .../detail/ranges/multipolygon_range.cuh | 223 ++++++++++++++++++ .../experimental/geometry/polygon_ref.cuh | 64 +++++ .../geometry_collection/multipolygon_ref.cuh | 79 +++++++ .../experimental/point_polygon_distance.cuh | 41 ++++ .../ranges/multipolygon_range.cuh | 136 +++++++++++ 9 files changed, 807 insertions(+), 1 deletion(-) create mode 100644 cpp/include/cuspatial/experimental/detail/geometry/polygon_ref.cuh create mode 100644 cpp/include/cuspatial/experimental/detail/geometry_collection/multipolygon_ref.cuh create mode 100644 cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh create mode 100644 cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh create mode 100644 cpp/include/cuspatial/experimental/geometry/polygon_ref.cuh create mode 100644 cpp/include/cuspatial/experimental/geometry_collection/multipolygon_ref.cuh create mode 100644 cpp/include/cuspatial/experimental/point_polygon_distance.cuh create mode 100644 cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh diff --git a/cpp/include/cuspatial/experimental/detail/geometry/polygon_ref.cuh b/cpp/include/cuspatial/experimental/detail/geometry/polygon_ref.cuh new file mode 100644 index 000000000..8b156ea6a --- /dev/null +++ b/cpp/include/cuspatial/experimental/detail/geometry/polygon_ref.cuh @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2022, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "linestring_ref.cuh" + +#include +#include +#include +#include + +#include +#include + +namespace cuspatial { + +template +CUSPATIAL_HOST_DEVICE polygon_ref::polygon_ref(RingIterator ring_begin, + RingIterator ring_end, + VecIterator point_begin, + VecIterator point_end) + : _ring_begin(ring_begin), _ring_end(ring_end), _point_begin(begin), _point_end(end) +{ + using T = iterator_vec_base_type; + static_assert(is_same, iterator_value_type>(), "must be vec2d type"); +} + +template +CUSPATIAL_HOST_DEVICE auto polygon_ref::num_rings() const +{ + return thrust::distance(_point_begin, _point_end) - 1; +} + +template +CUSPATIAL_HOST_DEVICE auto polygon_ref::ring_begin() const +{ + return detail::make_counting_transform_iterator(0, to_linestring_ref{_point_begin}); +} + +template +CUSPATIAL_HOST_DEVICE auto polygon_ref::ring_end() const +{ + return ring_begin() + num_rings(); +} + +template +template +CUSPATIAL_HOST_DEVICE auto polygon_ref::ring(IndexType i) const +{ + return *(ring_begin() + i); +} + +} // namespace cuspatial diff --git a/cpp/include/cuspatial/experimental/detail/geometry_collection/multipolygon_ref.cuh b/cpp/include/cuspatial/experimental/detail/geometry_collection/multipolygon_ref.cuh new file mode 100644 index 000000000..fe4e60cde --- /dev/null +++ b/cpp/include/cuspatial/experimental/detail/geometry_collection/multipolygon_ref.cuh @@ -0,0 +1,110 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace cuspatial { + +template +struct to_polygon_functor { + using difference_type = typename thrust::iterator_difference::type; + PartIterator part_begin; + RingIterator ring_begin; + VecIterator point_begin; + + CUSPATIAL_HOST_DEVICE + to_polygon_functor(PartIterator part_begin, RingIterator ring_begin, VecIterator point_begin) + : part_begin(part_begin), ring_begin(ring_begin), point_begin(point_begin) + { + } + + CUSPATIAL_HOST_DEVICE auto operator()(difference_type i) + { + return polygon_ref{point_begin + part_begin[i], point_begin + part_begin[i + 1]}; + } +}; + +template +class multipolygon_ref; + +template +CUSPATIAL_HOST_DEVICE multipolygon_ref::multipolygon_ref( + PartIterator part_begin, + PartIterator part_end, + RingIterator ring_begin, + RingIterator ring_end, + VecIterator point_begin, + VecIterator point_end) + : _part_begin(part_begin), + _part_end(part_end), + _ring_begin(ring_begin), + _ring_end(ring_end), + _point_begin(point_begin), + _point_end(point_end) +{ +} + +template +CUSPATIAL_HOST_DEVICE auto multipolygon_ref::num_polygons() + const +{ + return thrust::distance(_part_begin, _part_end) - 1; +} + +template +CUSPATIAL_HOST_DEVICE auto multipolygon_ref::part_begin() + const +{ + return detail::make_counting_transform_iterator( + 0, to_polygon_functor{_part_begin, _ring_begin, _point_begin}); +} + +template +CUSPATIAL_HOST_DEVICE auto multipolygon_ref::part_end() + const +{ + return part_begin() + num_polygons(); +} + +template +CUSPATIAL_HOST_DEVICE auto multipolygon_ref::ring_begin() + const +{ + return detail::make_counting_transform_iterator(0, + to_linestring_functor{_part_begin, _point_begin}); +} + +template +CUSPATIAL_HOST_DEVICE auto multipolygon_ref::ring_end() + const +{ + return part_begin() + num_polygons(); +} + +template +CUSPATIAL_HOST_DEVICE auto multipolygon_ref::point_begin() + const +{ + return _point_begin; +} + +template +CUSPATIAL_HOST_DEVICE auto multipolygon_ref::point_end() + const +{ + return _point_end; +} + +template +template +CUSPATIAL_HOST_DEVICE auto multipolygon_ref::operator[]( + IndexType i) const +{ + return *(part_begin() + i); +} + +} // namespace cuspatial diff --git a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh new file mode 100644 index 000000000..b51c252aa --- /dev/null +++ b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace cuspatial { +namespace detail { + +/** + * @brief Kernel to compute the distance between pairs of point and polygon. + */ +template +void __global__ pairwise_point_polygon_distance_kernel(MultiPointRange multipoints, + MultiPolygonRange multipolygons, + OutputIterator distances) +{ + using T = typename MultiPointRange::element_t; + + for (auto idx = threadIdx.x + blockIdx.x * blockDim.x; idx < multipolygons.num_points(); + idx += gridDim.x * blockDim.x) { + auto geometry_idx = multipolygons.geometry_idx_from_point_idx(idx); + } +} + +} // namespace detail +template +OutputIt pairwise_point_polygon_distance(MultiPointRange multipoints, + MultiPolygonRange multipoiygons, + OutputIt distances_first, + rmm::cuda_stream_view stream) +{ + using T = typename MultiPointRange::element_t; + + static_assert(is_same_floating_point(), + "Inputs must have same floating point value type."); + + static_assert( + is_same, typename MultiPointRange::point_t, typename MultiPolygonRange::point_t>(), + "Inputs must be cuspatial::vec_2d"); + + CUSPATIAL_EXPECTS(multipoints.size() == multipolygons.size(), + "Must have the same number of input rows."); + + auto [threads_per_block, n_blocks] = grid_id(multipolygons.num_points()); + + pairwise_point_polygon_distance_kernel<<>>( + multipoints, multipolygons, distances_first); + + CUSPATIAL_CHECK_CUDA(stream.value()); +} + +} // namespace cuspatial diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh index dd373be3f..851269313 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh @@ -16,7 +16,6 @@ #pragma once -#include #include #include #include @@ -29,6 +28,7 @@ #include #include +#include namespace cuspatial { diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh new file mode 100644 index 000000000..7fe9a9387 --- /dev/null +++ b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2022, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace cuspatial { + +using namespace detail; + +template +struct to_multipolygon_functor { + using difference_type = typename thrust::iterator_difference::type; + GeometryIterator _geometry_begin; + PartIterator _part_begin; + RingIterator _ring_begin; + VecIterator _point_begin; + VecIterator _point_end; + + CUSPATIAL_HOST_DEVICE + to_multipolygon_functor(GeometryIterator geometry_begin, + PartIterator part_begin, + RingIterator ring_begin, + VecIterator point_begin, + VecIterator point_end) + : _geometry_begin(geometry_begin), + _part_begin(part_begin), + _ring_begin(ring_begin), + _point_begin(point_begin), + _point_end(point_end) + { + } + + CUSPATIAL_HOST_DEVICE auto operator()(difference_type i) + { + return multipolygon_ref{_part_begin + _geometry_begin[i], + thrust::next(_part_begin + _geometry_begin[i + 1]), + _point_begin, + _point_end}; + } +}; + +template +class multipolygon_range; + +template +multipolygon_range::multipolygon_range( + GeometryIterator geometry_begin, + GeometryIterator geometry_end, + PartIterator part_begin, + PartIterator part_end, + VecIterator point_begin, + VecIterator point_end) + : _geometry_begin(geometry_begin), + _geometry_end(geometry_end), + _part_begin(part_begin), + _part_end(part_end), + _point_begin(point_begin), + _point_end(point_end) +{ +} + +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range::num_multipolygons() +{ + return thrust::distance(_geometry_begin, _geometry_end) - 1; +} + +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range::num_polygons() +{ + return thrust::distance(_part_begin, _part_end) - 1; +} + +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range::num_rings() +{ + return thrust::distance(_ring_begin, _ring_end) - 1; +} + +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range::num_points() +{ + return thrust::distance(_point_begin, _point_end); +} + +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range::multipolygon_begin() +{ + return detail::make_counting_transform_iterator( + 0, + to_multipolygon_functor{_geometry_begin, _part_begin, _ring_begin, _point_begin, _point_end}); +} + +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range::multipolygon_end() +{ + return multipolygon_begin() + num_multipolygons(); +} + +template +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range::ring_idx_from_point_idx( + IndexType point_idx) +{ + return thrust::distance(_ring_begin, + thrust::prev(thrust::upper_bound(_ring_begin, _ring_end, point_idx))); +} + +template +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range::part_idx_from_ring_idx( + IndexType ring_idx) +{ + return thrust::distance(_part_begin, + thrust::prev(thrust::upper_bound(_part_begin, _part_begin, ring_idx))); +} + +template +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range::geometry_idx_from_part_idx( + IndexType part_idx) +{ + return thrust::distance( + _geometry_begin, thrust::prev(thrust::upper_bound(_geometry_begin, _geometry_end, part_idx))); +} + +template +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range::geometry_idx_from_point_idx( + IndexType point_idx) +{ + return geometry_idx_from_part_idx(part_idx_from_ring_idx(ring_idx_from_part_idx(point_idx))); +} + +template +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range::operator[]( + IndexType multipolygon_idx) +{ + return multipolygon_begin()[multipolygon_idx]; +} + +} // namespace cuspatial diff --git a/cpp/include/cuspatial/experimental/geometry/polygon_ref.cuh b/cpp/include/cuspatial/experimental/geometry/polygon_ref.cuh new file mode 100644 index 000000000..42c3ce1a9 --- /dev/null +++ b/cpp/include/cuspatial/experimental/geometry/polygon_ref.cuh @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +namespace cuspatial { + +/** + * @brief Represent a reference to a polygon stored in a structure of arrays. + * + * @tparam VecIterator type of iterator to the underlying point array. + */ +template +class polygon_ref { + public: + CUSPATIAL_HOST_DEVICE polygon_ref(RingIterator ring_begin, + RingIterator ring_end, + VecIterator point_begin, + VecIterator point_end); + + /// Return the number of rings in the polygon + CUSPATIAL_HOST_DEVICE auto num_rings() const; + + /// Return iterator to the first ring of the polygon + CUSPATIAL_HOST_DEVICE auto ring_begin() const; + /// Return iterator to one past the last ring + CUSPATIAL_HOST_DEVICE auto ring_end() const; + + /// Return iterator to the first ring of the polygon + CUSPATIAL_HOST_DEVICE auto begin() const { return ring_begin(); } + /// Return iterator to one past the last ring + CUSPATIAL_HOST_DEVICE auto end() const { return ring_end(); } + + /// Return an enumerated range to the rings. + CUSPATIAL_HOST_DEVICE auto enumerate() { return detail::enumerate_range{begin(), end()}; } + + /// Return the `ring_idx`th ring in the polygon. + template + CUSPATIAL_HOST_DEVICE auto ring(IndexType ring_idx) const; + + protected: + RingIterator _ring_begin; + RingIterator _ring_end; + VecIterator _point_begin; + VecIterator _point_end; +}; + +} // namespace cuspatial +#include diff --git a/cpp/include/cuspatial/experimental/geometry_collection/multipolygon_ref.cuh b/cpp/include/cuspatial/experimental/geometry_collection/multipolygon_ref.cuh new file mode 100644 index 000000000..a6aab7cc4 --- /dev/null +++ b/cpp/include/cuspatial/experimental/geometry_collection/multipolygon_ref.cuh @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +namespace cuspatial { + +/** + * @brief Represent a reference to a multipolygon stored in a structure of arrays. + * + * @tparam PartIterator type of iterator to the part offset array. + * @tparam VecIterator type of iterator to the underlying point array. + */ +template +class multipolygon_ref { + public: + CUSPATIAL_HOST_DEVICE multipolygon_ref(PartIterator part_begin, + PartIterator part_end, + VecIterator point_begin, + VecIterator point_end); + /// Return the number of polygons in the multipolygon. + CUSPATIAL_HOST_DEVICE auto num_polygons() const; + /// Return the number of polygons in the multipolygon. + CUSPATIAL_HOST_DEVICE auto size() const { return num_polygons(); } + + /// Return iterator to the first polygon. + CUSPATIAL_HOST_DEVICE auto part_begin() const; + /// Return iterator to one past the last polygon. + CUSPATIAL_HOST_DEVICE auto part_end() const; + + /// Return iterator to the first polygon. + CUSPATIAL_HOST_DEVICE auto ring_begin() const; + /// Return iterator to one past the last polygon. + CUSPATIAL_HOST_DEVICE auto ring_begin() const; + + /// Return iterator to the first point of the multipolygon. + CUSPATIAL_HOST_DEVICE auto point_begin() const; + /// Return iterator to one past the last point of the multipolygon. + CUSPATIAL_HOST_DEVICE auto point_end() const; + + /// Return iterator to the first polygon of the multipolygon. + CUSPATIAL_HOST_DEVICE auto begin() const { return part_begin(); } + /// Return iterator to one past the last polygon of the multipolygon. + CUSPATIAL_HOST_DEVICE auto end() const { return part_end(); } + + /// Return an enumerated range to the polygons. + CUSPATIAL_HOST_DEVICE auto enumerate() const { return detail::enumerate_range{begin(), end()}; } + + /// Return `polygon_idx`th polygon in the multipolygon. + template + CUSPATIAL_HOST_DEVICE auto operator[](IndexType polygon_idx) const; + + protected: + PartIterator _part_begin; + PartIterator _part_end; + RingIterator _ring_begin; + RingIterator _ring_end; + VecIterator _point_begin; + VecIterator _point_end; +}; + +} // namespace cuspatial + +#include diff --git a/cpp/include/cuspatial/experimental/point_polygon_distance.cuh b/cpp/include/cuspatial/experimental/point_polygon_distance.cuh new file mode 100644 index 000000000..c5394e902 --- /dev/null +++ b/cpp/include/cuspatial/experimental/point_polygon_distance.cuh @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace cuspatial { + +/** + * @ingroup distance + * @copybrief cuspatial::pairwise_point_polygon_distance + * + * @tparam MultiPointRange An instance of template type `MultiPointRange` + * @tparam MultiPolygonRangeB An instance of template type `MultiPolygonRange` + * + * @param multipoints Range of multipoints in each distance pair. + * @param multipolygons Range of multipolygons in each distance pair. + * @return Iterator past the last distance computed + */ +template +OutputIt pairwise_point_polygon_distance(MultiPointRange multipoints, + MultiPolygonRangeB multipoiygons, + OutputIt distances_first, + rmm::cuda_stream_view stream = rmm::cuda_stream_default); +} // namespace cuspatial + +#include diff --git a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh new file mode 100644 index 000000000..331302348 --- /dev/null +++ b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2022, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include +#include +#include +#include + +namespace cuspatial { + +/** + * @brief Non-owning range-based interface to multipolygon data + * @ingroup ranges + * + * Provides a range-based interface to contiguous storage of multipolygon data, to make it easier + * to access and iterate over multipolygons, polygons, rings and points. + * + * Conforms to GeoArrow's specification of multipolygon: + * https://github.com/geopandas/geo-arrow-spec/blob/main/format.md + * + * @tparam GeometryIterator iterator type for the geometry offset array. Must meet + * the requirements of [LegacyRandomAccessIterator][LinkLRAI]. + * @tparam PartIterator iterator type for the part offset array. Must meet + * the requirements of [LegacyRandomAccessIterator][LinkLRAI]. + * @tparam RingIterator iterator type for the ring offset array. Must meet + * the requirements of [LegacyRandomAccessIterator][LinkLRAI]. + * @tparam VecIterator iterator type for the point array. Must meet + * the requirements of [LegacyRandomAccessIterator][LinkLRAI]. + * + * @note Though this object is host/device compatible, + * The underlying iterator must be device accessible if used in device kernel. + * + * [LinkLRAI]: https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator + * "LegacyRandomAccessIterator" + */ +template +class multipolygon_range { + public: + using geometry_it_t = GeometryIterator; + using part_it_t = PartIterator; + using ring_it_t = RingIterator; + using point_it_t = VecIterator; + using point_t = iterator_value_type; + using element_t = iterator_vec_base_type; + + multipolygon_range(GeometryIterator geometry_begin, + GeometryIterator geometry_end, + PartIterator part_begin, + PartIterator part_end, + RingIterator ring_begin, + RingIterator ring_end, + VecIterator points_begin, + VecIterator points_end); + + /// Return the number of multipolygons in the array. + CUSPATIAL_HOST_DEVICE auto size() { return num_multipolygons(); } + + /// Return the number of multipolygons in the array. + CUSPATIAL_HOST_DEVICE auto num_multipolygons(); + + /// Return the total number of polygons in the array. + CUSPATIAL_HOST_DEVICE auto num_polygons(); + + /// Return the total number of rings in the array. + CUSPATIAL_HOST_DEVICE auto num_rings(); + + /// Return the total number of points in the array. + CUSPATIAL_HOST_DEVICE auto num_points(); + + /// Return the iterator to the first multipolygon in the range. + CUSPATIAL_HOST_DEVICE auto multipolygon_begin(); + + /// Return the iterator to the one past the last multipolygon in the range. + CUSPATIAL_HOST_DEVICE auto multipolygon_end(); + + /// Return the iterator to the first multipolygon in the range. + CUSPATIAL_HOST_DEVICE auto begin() { return multipolygon_begin(); } + + /// Return the iterator to the one past the last multipolygon in the range. + CUSPATIAL_HOST_DEVICE auto end() { return multipolygon_end(); } + + /// Given the index of a point, return the ring index where the point locates. + template + CUSPATIAL_HOST_DEVICE auto ring_idx_from_point_idx(IndexType point_idx); + + /// Given the index of a ring, return the part (polygon) index + /// where the polygon locates. + template + CUSPATIAL_HOST_DEVICE auto part_idx_from_ring_idx(IndexType ring_idx); + + /// Given the index of a part (polygon), return the geometry (multipolygon) index + /// where the polygon locates. + template + CUSPATIAL_HOST_DEVICE auto geometry_idx_from_part_idx(IndexType part_idx); + + /// Given the index of a point, return the geometry (multipolygon) index where the + /// point locates. + template + CUSPATIAL_HOST_DEVICE auto geometry_idx_from_point_idx(IndexType point_idx); + + /// Returns the `multipolygon_idx`th multipolygon in the range. + template + CUSPATIAL_HOST_DEVICE auto operator[](IndexType multipolygon_idx); + + protected: + GeometryIterator _geometry_begin; + GeometryIterator _geometry_end; + PartIterator _part_begin; + PartIterator _part_end; + VecIterator _point_begin; + VecIterator _point_end; +}; + +} // namespace cuspatial + +#include From 02f61feca933bca3de3f545d7331b1f2d8fb678b Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 7 Mar 2023 11:01:25 -0800 Subject: [PATCH 02/60] add pragma once for floating_point.cuh --- cpp/include/cuspatial/detail/utility/floating_point.cuh | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/include/cuspatial/detail/utility/floating_point.cuh b/cpp/include/cuspatial/detail/utility/floating_point.cuh index 744c7cb88..7a44aeb73 100644 --- a/cpp/include/cuspatial/detail/utility/floating_point.cuh +++ b/cpp/include/cuspatial/detail/utility/floating_point.cuh @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#pragma once #include From 7bd797fc23864c9cc7a83e0e4ac743a0b64ebdb4 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 7 Mar 2023 11:02:20 -0800 Subject: [PATCH 03/60] add polygon_ref structure --- .../detail/geometry/polygon_ref.cuh | 26 ++++++++++++++----- .../experimental/geometry/polygon_ref.cuh | 12 ++++++--- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/geometry/polygon_ref.cuh b/cpp/include/cuspatial/experimental/detail/geometry/polygon_ref.cuh index 8b156ea6a..02a917cd4 100644 --- a/cpp/include/cuspatial/experimental/detail/geometry/polygon_ref.cuh +++ b/cpp/include/cuspatial/experimental/detail/geometry/polygon_ref.cuh @@ -15,11 +15,10 @@ */ #pragma once -#include "linestring_ref.cuh" - #include #include #include +#include #include #include @@ -32,28 +31,41 @@ CUSPATIAL_HOST_DEVICE polygon_ref::polygon_ref(RingIt RingIterator ring_end, VecIterator point_begin, VecIterator point_end) - : _ring_begin(ring_begin), _ring_end(ring_end), _point_begin(begin), _point_end(end) + : _ring_begin(ring_begin), _ring_end(ring_end), _point_begin(point_begin), _point_end(point_end) { - using T = iterator_vec_base_type; + using T = iterator_vec_base_type; static_assert(is_same, iterator_value_type>(), "must be vec2d type"); } template CUSPATIAL_HOST_DEVICE auto polygon_ref::num_rings() const { - return thrust::distance(_point_begin, _point_end) - 1; + return thrust::distance(_ring_begin, _ring_end) - 1; } template CUSPATIAL_HOST_DEVICE auto polygon_ref::ring_begin() const { - return detail::make_counting_transform_iterator(0, to_linestring_ref{_point_begin}); + return detail::make_counting_transform_iterator(0, + to_linestring_functor{_ring_begin, _point_begin}); } template CUSPATIAL_HOST_DEVICE auto polygon_ref::ring_end() const { - return ring_begin() + num_rings(); + return ring_begin() + size(); +} + +template +CUSPATIAL_HOST_DEVICE auto polygon_ref::point_begin() const +{ + return _point_begin; +} + +template +CUSPATIAL_HOST_DEVICE auto polygon_ref::point_end() const +{ + return _point_end; } template diff --git a/cpp/include/cuspatial/experimental/geometry/polygon_ref.cuh b/cpp/include/cuspatial/experimental/geometry/polygon_ref.cuh index 42c3ce1a9..5905eab2c 100644 --- a/cpp/include/cuspatial/experimental/geometry/polygon_ref.cuh +++ b/cpp/include/cuspatial/experimental/geometry/polygon_ref.cuh @@ -16,7 +16,6 @@ #pragma once #include -#include namespace cuspatial { @@ -36,19 +35,24 @@ class polygon_ref { /// Return the number of rings in the polygon CUSPATIAL_HOST_DEVICE auto num_rings() const; + /// Return the number of rings in the polygon + CUSPATIAL_HOST_DEVICE auto size() const { return num_rings(); } + /// Return iterator to the first ring of the polygon CUSPATIAL_HOST_DEVICE auto ring_begin() const; /// Return iterator to one past the last ring CUSPATIAL_HOST_DEVICE auto ring_end() const; + /// Return iterator to the first point of the polygon + CUSPATIAL_HOST_DEVICE auto point_begin() const; + /// Return iterator to one past the last point + CUSPATIAL_HOST_DEVICE auto point_end() const; + /// Return iterator to the first ring of the polygon CUSPATIAL_HOST_DEVICE auto begin() const { return ring_begin(); } /// Return iterator to one past the last ring CUSPATIAL_HOST_DEVICE auto end() const { return ring_end(); } - /// Return an enumerated range to the rings. - CUSPATIAL_HOST_DEVICE auto enumerate() { return detail::enumerate_range{begin(), end()}; } - /// Return the `ring_idx`th ring in the polygon. template CUSPATIAL_HOST_DEVICE auto ring(IndexType ring_idx) const; From 0968c15b72d3632700b284bacd4dbc9f2481bfa5 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 7 Mar 2023 11:04:23 -0800 Subject: [PATCH 04/60] add multipolygon_ref class --- .../geometry_collection/multipolygon_ref.cuh | 15 +++++++++++---- .../geometry_collection/multipolygon_ref.cuh | 10 ++++++---- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/geometry_collection/multipolygon_ref.cuh b/cpp/include/cuspatial/experimental/detail/geometry_collection/multipolygon_ref.cuh index fe4e60cde..940c6ef83 100644 --- a/cpp/include/cuspatial/experimental/detail/geometry_collection/multipolygon_ref.cuh +++ b/cpp/include/cuspatial/experimental/detail/geometry_collection/multipolygon_ref.cuh @@ -15,16 +15,23 @@ struct to_polygon_functor { PartIterator part_begin; RingIterator ring_begin; VecIterator point_begin; + VecIterator point_end; CUSPATIAL_HOST_DEVICE - to_polygon_functor(PartIterator part_begin, RingIterator ring_begin, VecIterator point_begin) - : part_begin(part_begin), ring_begin(ring_begin), point_begin(point_begin) + to_polygon_functor(PartIterator part_begin, + RingIterator ring_begin, + VecIterator point_begin, + VecIterator point_end) + : part_begin(part_begin), ring_begin(ring_begin), point_begin(point_begin), point_end(point_end) { } CUSPATIAL_HOST_DEVICE auto operator()(difference_type i) { - return polygon_ref{point_begin + part_begin[i], point_begin + part_begin[i + 1]}; + return polygon_ref{ring_begin + part_begin[i], + thrust::next(ring_begin + part_begin[i + 1]), + point_begin, + point_end}; } }; @@ -60,7 +67,7 @@ CUSPATIAL_HOST_DEVICE auto multipolygon_ref diff --git a/cpp/include/cuspatial/experimental/geometry_collection/multipolygon_ref.cuh b/cpp/include/cuspatial/experimental/geometry_collection/multipolygon_ref.cuh index a6aab7cc4..64da05797 100644 --- a/cpp/include/cuspatial/experimental/geometry_collection/multipolygon_ref.cuh +++ b/cpp/include/cuspatial/experimental/geometry_collection/multipolygon_ref.cuh @@ -26,11 +26,13 @@ namespace cuspatial { * @tparam PartIterator type of iterator to the part offset array. * @tparam VecIterator type of iterator to the underlying point array. */ -template +template class multipolygon_ref { public: CUSPATIAL_HOST_DEVICE multipolygon_ref(PartIterator part_begin, PartIterator part_end, + RingIterator ring_begin, + RingIterator ring_end, VecIterator point_begin, VecIterator point_end); /// Return the number of polygons in the multipolygon. @@ -43,10 +45,10 @@ class multipolygon_ref { /// Return iterator to one past the last polygon. CUSPATIAL_HOST_DEVICE auto part_end() const; - /// Return iterator to the first polygon. - CUSPATIAL_HOST_DEVICE auto ring_begin() const; - /// Return iterator to one past the last polygon. + /// Return iterator to the first ring. CUSPATIAL_HOST_DEVICE auto ring_begin() const; + /// Return iterator to one past the last ring. + CUSPATIAL_HOST_DEVICE auto ring_end() const; /// Return iterator to the first point of the multipolygon. CUSPATIAL_HOST_DEVICE auto point_begin() const; From a659eab1df056666af3fec2ac382c1221f6c5d91 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 7 Mar 2023 11:05:06 -0800 Subject: [PATCH 05/60] update multipolygon_range class --- .../detail/ranges/multipolygon_range.cuh | 104 ++++++++++++++---- .../ranges/multipolygon_range.cuh | 51 ++++++--- 2 files changed, 114 insertions(+), 41 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh index 7fe9a9387..b44420532 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -43,6 +44,7 @@ struct to_multipolygon_functor { GeometryIterator _geometry_begin; PartIterator _part_begin; RingIterator _ring_begin; + RingIterator _ring_end; VecIterator _point_begin; VecIterator _point_end; @@ -50,11 +52,13 @@ struct to_multipolygon_functor { to_multipolygon_functor(GeometryIterator geometry_begin, PartIterator part_begin, RingIterator ring_begin, + RingIterator ring_end, VecIterator point_begin, VecIterator point_end) : _geometry_begin(geometry_begin), _part_begin(part_begin), _ring_begin(ring_begin), + _ring_end(ring_end), _point_begin(point_begin), _point_end(point_end) { @@ -62,8 +66,22 @@ struct to_multipolygon_functor { CUSPATIAL_HOST_DEVICE auto operator()(difference_type i) { + auto new_part_begin = _part_begin + _geometry_begin[i]; + auto new_part_end = thrust::next(_part_begin, _geometry_begin[i + 1] + 1); + + printf( + "In to_multipolygon_functor: %d %d %d\n\t new_part_begin_val: %d, " + "new_part_end_dist_from_begin: %d\n", + static_cast(i), + static_cast(_geometry_begin[i]), + static_cast(_geometry_begin[i + 1]), + static_cast(*new_part_begin), + static_cast(thrust::distance(new_part_end, new_part_begin))); + return multipolygon_ref{_part_begin + _geometry_begin[i], - thrust::next(_part_begin + _geometry_begin[i + 1]), + thrust::next(_part_begin, _geometry_begin[i + 1] + 1), + _ring_begin, + _ring_end, _point_begin, _point_end}; } @@ -79,17 +97,21 @@ template -multipolygon_range::multipolygon_range( +multipolygon_range::multipolygon_range( GeometryIterator geometry_begin, GeometryIterator geometry_end, PartIterator part_begin, PartIterator part_end, + RingIterator ring_begin, + RingIterator ring_end, VecIterator point_begin, VecIterator point_end) : _geometry_begin(geometry_begin), _geometry_end(geometry_end), _part_begin(part_begin), _part_end(part_end), + _ring_begin(ring_begin), + _ring_end(ring_end), _point_begin(point_begin), _point_end(point_end) { @@ -100,7 +122,7 @@ template CUSPATIAL_HOST_DEVICE auto -multipolygon_range::num_multipolygons() +multipolygon_range::num_multipolygons() { return thrust::distance(_geometry_begin, _geometry_end) - 1; } @@ -110,7 +132,7 @@ template CUSPATIAL_HOST_DEVICE auto -multipolygon_range::num_polygons() +multipolygon_range::num_polygons() { return thrust::distance(_part_begin, _part_end) - 1; } @@ -120,7 +142,7 @@ template CUSPATIAL_HOST_DEVICE auto -multipolygon_range::num_rings() +multipolygon_range::num_rings() { return thrust::distance(_ring_begin, _ring_end) - 1; } @@ -130,7 +152,7 @@ template CUSPATIAL_HOST_DEVICE auto -multipolygon_range::num_points() +multipolygon_range::num_points() { return thrust::distance(_point_begin, _point_end); } @@ -140,11 +162,12 @@ template CUSPATIAL_HOST_DEVICE auto -multipolygon_range::multipolygon_begin() +multipolygon_range::multipolygon_begin() { return detail::make_counting_transform_iterator( 0, - to_multipolygon_functor{_geometry_begin, _part_begin, _ring_begin, _point_begin, _point_end}); + to_multipolygon_functor{ + _geometry_begin, _part_begin, _ring_begin, _ring_end, _point_begin, _point_end}); } template CUSPATIAL_HOST_DEVICE auto -multipolygon_range::multipolygon_end() +multipolygon_range::multipolygon_end() { return multipolygon_begin() + num_multipolygons(); } @@ -163,11 +186,11 @@ template template CUSPATIAL_HOST_DEVICE auto -multipolygon_range::ring_idx_from_point_idx( - IndexType point_idx) +multipolygon_range:: + ring_idx_from_point_idx(IndexType point_idx) { - return thrust::distance(_ring_begin, - thrust::prev(thrust::upper_bound(_ring_begin, _ring_end, point_idx))); + return thrust::distance( + _ring_begin, thrust::prev(thrust::upper_bound(thrust::seq, _ring_begin, _ring_end, point_idx))); } template template CUSPATIAL_HOST_DEVICE auto -multipolygon_range::part_idx_from_ring_idx( - IndexType ring_idx) +multipolygon_range:: + part_idx_from_ring_idx(IndexType ring_idx) { - return thrust::distance(_part_begin, - thrust::prev(thrust::upper_bound(_part_begin, _part_begin, ring_idx))); + return thrust::distance( + _part_begin, + thrust::prev(thrust::upper_bound(thrust::seq, _part_begin, _part_begin, ring_idx))); } template template CUSPATIAL_HOST_DEVICE auto -multipolygon_range::geometry_idx_from_part_idx( - IndexType part_idx) +multipolygon_range:: + geometry_idx_from_part_idx(IndexType part_idx) { return thrust::distance( - _geometry_begin, thrust::prev(thrust::upper_bound(_geometry_begin, _geometry_end, part_idx))); + _geometry_begin, + thrust::prev(thrust::upper_bound(thrust::seq, _geometry_begin, _geometry_end, part_idx))); } template template CUSPATIAL_HOST_DEVICE auto -multipolygon_range::geometry_idx_from_point_idx( - IndexType point_idx) +multipolygon_range:: + geometry_idx_from_segment_idx(IndexType segment_idx) +{ + auto ring_idx = ring_idx_from_point_idx(segment_idx); + if (!is_valid_segment_id(segment_idx, ring_idx)) + return multipolygon_range:: + INVALID_INDEX; + return geometry_idx_from_part_idx(part_idx_from_ring_idx(ring_idx)); +} + +template +template +CUSPATIAL_HOST_DEVICE bool +multipolygon_range::is_valid_segment_id( + IndexType1 point_idx, IndexType2 ring_idx) { - return geometry_idx_from_part_idx(part_idx_from_ring_idx(ring_idx_from_part_idx(point_idx))); + if constexpr (std::is_signed_v) + return point_idx >= 0 && point_idx < (_ring_begin[ring_idx + 1] - 1); + else + return point_idx < (_ring_begin[ring_idx + 1] - 1); } template template CUSPATIAL_HOST_DEVICE auto -multipolygon_range::operator[]( +multipolygon_range::operator[]( IndexType multipolygon_idx) { return multipolygon_begin()[multipolygon_idx]; } +template +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range::get_segment( + IndexType segment_idx) +{ + return segment{_point_begin[segment_idx], _point_begin[segment_idx + 1]}; +} + } // namespace cuspatial diff --git a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh index 331302348..b9efd64e5 100644 --- a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh @@ -63,6 +63,8 @@ class multipolygon_range { using point_t = iterator_value_type; using element_t = iterator_vec_base_type; + int64_t static constexpr INVALID_INDEX = -1; + multipolygon_range(GeometryIterator geometry_begin, GeometryIterator geometry_end, PartIterator part_begin, @@ -98,37 +100,52 @@ class multipolygon_range { /// Return the iterator to the one past the last multipolygon in the range. CUSPATIAL_HOST_DEVICE auto end() { return multipolygon_end(); } - - /// Given the index of a point, return the ring index where the point locates. + /// Given the index of a segment, return the geometry (multipolygon) index where the + /// segment locates. + /// Segment index is the index to the starting point of the segment. If the + /// index is the last point of the ring, then it is not a valid index. + /// This function returns multipolygon_range::INVALID_INDEX if the index is invalid. template - CUSPATIAL_HOST_DEVICE auto ring_idx_from_point_idx(IndexType point_idx); + CUSPATIAL_HOST_DEVICE auto geometry_idx_from_segment_idx(IndexType segment_idx); - /// Given the index of a ring, return the part (polygon) index - /// where the polygon locates. + /// Returns the `multipolygon_idx`th multipolygon in the range. template - CUSPATIAL_HOST_DEVICE auto part_idx_from_ring_idx(IndexType ring_idx); + CUSPATIAL_HOST_DEVICE auto operator[](IndexType multipolygon_idx); - /// Given the index of a part (polygon), return the geometry (multipolygon) index - /// where the polygon locates. - template - CUSPATIAL_HOST_DEVICE auto geometry_idx_from_part_idx(IndexType part_idx); + // template + // CUSPATIAL_HOST_DEVICE auto get_point(IndexType point_idx); - /// Given the index of a point, return the geometry (multipolygon) index where the - /// point locates. template - CUSPATIAL_HOST_DEVICE auto geometry_idx_from_point_idx(IndexType point_idx); - - /// Returns the `multipolygon_idx`th multipolygon in the range. - template - CUSPATIAL_HOST_DEVICE auto operator[](IndexType multipolygon_idx); + CUSPATIAL_HOST_DEVICE auto get_segment(IndexType segment_idx); protected: GeometryIterator _geometry_begin; GeometryIterator _geometry_end; PartIterator _part_begin; PartIterator _part_end; + RingIterator _ring_begin; + RingIterator _ring_end; VecIterator _point_begin; VecIterator _point_end; + + private: + /// Given the index of a point, return the ring index + /// where the point locates. + template + CUSPATIAL_HOST_DEVICE auto ring_idx_from_point_idx(IndexType point_idx); + + /// Given the index of a ring, return the part (polygon) index + /// where the ring locates. + template + CUSPATIAL_HOST_DEVICE auto part_idx_from_ring_idx(IndexType ring_idx); + + /// Given the index of a part (polygon), return the geometry (multipolygon) index + /// where the polygon locates. + template + CUSPATIAL_HOST_DEVICE auto geometry_idx_from_part_idx(IndexType part_idx); + + template + CUSPATIAL_HOST_DEVICE bool is_valid_segment_id(IndexType1 segment_idx, IndexType2 ring_idx); }; } // namespace cuspatial From c2740702c2497774d05a377f91a58ee0bec1bee3 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 7 Mar 2023 11:05:53 -0800 Subject: [PATCH 06/60] update multipoint_range class --- .../experimental/detail/ranges/multipoint_range.cuh | 7 +++++++ .../experimental/ranges/multipoint_range.cuh | 12 ++++++++++++ 2 files changed, 19 insertions(+) diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multipoint_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multipoint_range.cuh index 08b3d5d5e..d3d8926d5 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multipoint_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multipoint_range.cuh @@ -133,4 +133,11 @@ multipoint_range::geometry_idx_from_point_idx(Ind thrust::prev(thrust::upper_bound(thrust::seq, _geometry_begin, _geometry_end, idx))); } +template +template +CUSPATIAL_HOST_DEVICE auto multipoint_range::point(IndexType idx) +{ + return _points_begin[idx]; +} + } // namespace cuspatial diff --git a/cpp/include/cuspatial/experimental/ranges/multipoint_range.cuh b/cpp/include/cuspatial/experimental/ranges/multipoint_range.cuh index 366f9c18e..1ee08b5cc 100644 --- a/cpp/include/cuspatial/experimental/ranges/multipoint_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multipoint_range.cuh @@ -51,6 +51,8 @@ class multipoint_range { using point_t = iterator_value_type; using element_t = iterator_vec_base_type; + int32_t INVALID_IDX = -1; + /** * @brief Construct a new multipoint array object */ @@ -129,6 +131,16 @@ class multipoint_range { template CUSPATIAL_HOST_DEVICE auto operator[](IndexType idx); + /** + * @brief Returns the `idx`th point in the array. + * + * @tparam IndexType type of the index + * @param idx the index to the point + * @return a vec_2d object + */ + template + CUSPATIAL_HOST_DEVICE auto point(IndexType idx); + protected: /// Iterator to the start of the index array of start positions to each multipoint. GeometryIterator _geometry_begin; From 12ffa53f6a917a18bf103fc02c8eb11884b84464 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 7 Mar 2023 11:06:36 -0800 Subject: [PATCH 07/60] update is_point_in_polygon usage with polygon_ref --- .../detail/is_point_in_polygon.cuh | 104 ------------------ .../detail/pairwise_point_in_polygon.cuh | 4 +- .../experimental/detail/point_in_polygon.cuh | 4 +- 3 files changed, 4 insertions(+), 108 deletions(-) delete mode 100644 cpp/include/cuspatial/experimental/detail/is_point_in_polygon.cuh diff --git a/cpp/include/cuspatial/experimental/detail/is_point_in_polygon.cuh b/cpp/include/cuspatial/experimental/detail/is_point_in_polygon.cuh deleted file mode 100644 index b9c7d5ac6..000000000 --- a/cpp/include/cuspatial/experimental/detail/is_point_in_polygon.cuh +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2022, NVIDIA CORPORATION. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include - -namespace cuspatial { -namespace detail { - -/** - * @brief Kernel to test if a point is inside a polygon. - * - * Implemented based on Eric Haines's crossings-multiply algorithm: - * See "Crossings test" section of http://erich.realtimerendering.com/ptinpoly/ - * The improvement in addenda is also addopted to remove divisions in this kernel. - * - * TODO: the ultimate goal of refactoring this as independent function is to remove - * src/utility/point_in_polygon.cuh and its usage in quadtree_point_in_polygon.cu. It isn't - * possible today without further work to refactor quadtree_point_in_polygon into header only - * API. - */ -template ::difference_type, - class Cart2dItDiffType = typename std::iterator_traits::difference_type> -__device__ inline bool is_point_in_polygon(Cart2d const& test_point, - OffsetType poly_begin, - OffsetType poly_end, - OffsetIterator ring_offsets_first, - OffsetItDiffType const& num_rings, - Cart2dIt poly_points_first, - Cart2dItDiffType const& num_poly_points) -{ - using T = iterator_vec_base_type; - - bool point_is_within = false; - bool is_colinear = false; - // for each ring - for (auto ring_idx = poly_begin; ring_idx < poly_end; ring_idx++) { - int32_t ring_idx_next = ring_idx + 1; - int32_t ring_begin = ring_offsets_first[ring_idx]; - int32_t ring_end = - (ring_idx_next < num_rings) ? ring_offsets_first[ring_idx_next] : num_poly_points; - - Cart2d b = poly_points_first[ring_end - 1]; - bool y0_flag = b.y > test_point.y; - bool y1_flag; - // for each line segment, including the segment between the last and first vertex - for (auto point_idx = ring_begin; point_idx < ring_end; point_idx++) { - Cart2d const a = poly_points_first[point_idx]; - T run = b.x - a.x; - T rise = b.y - a.y; - - // Points on the line segment are the same, so intersection is impossible. - // This is possible because we allow closed or unclosed polygons. - T constexpr zero = 0.0; - if (float_equal(run, zero) && float_equal(rise, zero)) continue; - - T rise_to_point = test_point.y - a.y; - - // colinearity test - T run_to_point = test_point.x - a.x; - is_colinear = float_equal(run * rise_to_point, run_to_point * rise); - if (is_colinear) { break; } - - y1_flag = a.y > test_point.y; - if (y1_flag != y0_flag) { - // Transform the following inequality to avoid division - // test_point.x < (run / rise) * rise_to_point + a.x - auto lhs = (test_point.x - a.x) * rise; - auto rhs = run * rise_to_point; - if (lhs < rhs != y1_flag) { point_is_within = not point_is_within; } - } - b = a; - y0_flag = y1_flag; - } - if (is_colinear) { - point_is_within = false; - break; - } - } - - return point_is_within; -} -} // namespace detail -} // namespace cuspatial diff --git a/cpp/include/cuspatial/experimental/detail/pairwise_point_in_polygon.cuh b/cpp/include/cuspatial/experimental/detail/pairwise_point_in_polygon.cuh index 8753367f6..f4659477a 100644 --- a/cpp/include/cuspatial/experimental/detail/pairwise_point_in_polygon.cuh +++ b/cpp/include/cuspatial/experimental/detail/pairwise_point_in_polygon.cuh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2022-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include diff --git a/cpp/include/cuspatial/experimental/detail/point_in_polygon.cuh b/cpp/include/cuspatial/experimental/detail/point_in_polygon.cuh index 5232cae1f..58737fe61 100644 --- a/cpp/include/cuspatial/experimental/detail/point_in_polygon.cuh +++ b/cpp/include/cuspatial/experimental/detail/point_in_polygon.cuh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2022-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ #pragma once #include -#include +#include #include #include From 749033340a81e816f30f95ce3a0b401d17ee8eb1 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 7 Mar 2023 11:07:04 -0800 Subject: [PATCH 08/60] update multilinestring_range --- .../multilinestring_ref.cuh | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/cpp/include/cuspatial/experimental/detail/geometry_collection/multilinestring_ref.cuh b/cpp/include/cuspatial/experimental/detail/geometry_collection/multilinestring_ref.cuh index d1041818f..b8768d18f 100644 --- a/cpp/include/cuspatial/experimental/detail/geometry_collection/multilinestring_ref.cuh +++ b/cpp/include/cuspatial/experimental/detail/geometry_collection/multilinestring_ref.cuh @@ -1,6 +1,22 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #pragma once -#include "cuspatial/cuda_utils.hpp" +#include #include #include @@ -22,6 +38,9 @@ struct to_linestring_functor { CUSPATIAL_HOST_DEVICE auto operator()(difference_type i) { + printf("In to_linestring_functor: %d %d\n", + static_cast(part_begin[i]), + static_cast(part_begin[i + 1])); return linestring_ref{point_begin + part_begin[i], point_begin + part_begin[i + 1]}; } }; From f66528713dcb4eab82984648050c55c1e50d2253 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 7 Mar 2023 11:07:22 -0800 Subject: [PATCH 09/60] add point to polygon kernel --- .../detail/point_polygon_distance.cuh | 118 +++++++++++++++--- .../experimental/point_polygon_distance.cuh | 2 +- 2 files changed, 102 insertions(+), 18 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh index b51c252aa..7ab760b0b 100644 --- a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh @@ -17,50 +17,108 @@ #pragma once #include +#include #include #include +#include #include -#include -#include +#include +#include #include +#include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include -#include -#include -#include +#include #include namespace cuspatial { namespace detail { +template +struct point_in_multipolygon_test_functor { + MultiPointRange multipoints; + MultiPolygonRange multipolygons; + + point_in_multipolygon_test_functor(MultiPointRange multipoints, MultiPolygonRange multipolygons) + : multipoints(multipoints), multipolygons(multipolygons) + { + } + + template + uint8_t __device__ operator()(IndexType pidx) + { + printf("%d\n", static_cast(pidx)); + + auto point = thrust::raw_reference_cast(multipoints.point(pidx)); + + printf("%f, %f\n", point.x, point.y); + + auto geometry_idx = multipoints.geometry_idx_from_point_idx(pidx); + + printf("%d\n", static_cast(geometry_idx)); + + bool intersects = false; + for (auto polygon : multipolygons[geometry_idx]) { + printf("here\n"); + intersects = intersects || is_point_in_polygon(point, polygon); + } + + return static_cast(intersects); + } +}; + /** * @brief Kernel to compute the distance between pairs of point and polygon. */ -template +template void __global__ pairwise_point_polygon_distance_kernel(MultiPointRange multipoints, MultiPolygonRange multipolygons, + IntersectionRange intersects, OutputIterator distances) { using T = typename MultiPointRange::element_t; + T dist_squared = std::numeric_limits::max(); for (auto idx = threadIdx.x + blockIdx.x * blockDim.x; idx < multipolygons.num_points(); idx += gridDim.x * blockDim.x) { - auto geometry_idx = multipolygons.geometry_idx_from_point_idx(idx); + auto geometry_idx = multipolygons.geometry_idx_from_segment_idx(idx); + if (geometry_idx == MultiPolygonRange::INVALID_INDEX) continue; + + if (intersects[geometry_idx]) { + // TODO: only the leading thread of the pair need to store the result, atomics is not needed. + atomicMin(&distances[geometry_idx], T{0.0}); + continue; + } + + printf("In distance kernel: %d %d %d", + static_cast(idx), + static_cast(geometry_idx), + static_cast(intersects.size())); + + auto [a, b] = multipolygons.get_segment(idx); + for (vec_2d point : multipoints[geometry_idx]) { + dist_squared = min(dist_squared, point_to_segment_distance_squared(point, a, b)); + } + + atomicMin(&distances[geometry_idx], sqrt(dist_squared)); } } } // namespace detail + template OutputIt pairwise_point_polygon_distance(MultiPointRange multipoints, - MultiPolygonRange multipoiygons, + MultiPolygonRange multipolygons, OutputIt distances_first, rmm::cuda_stream_view stream) { @@ -76,12 +134,38 @@ OutputIt pairwise_point_polygon_distance(MultiPointRange multipoints, CUSPATIAL_EXPECTS(multipoints.size() == multipolygons.size(), "Must have the same number of input rows."); - auto [threads_per_block, n_blocks] = grid_id(multipolygons.num_points()); + auto multipoint_intersects = [&]() { + rmm::device_uvector point_intersects(multipoints.num_points(), stream); + + thrust::tabulate(rmm::exec_policy(stream), + point_intersects.begin(), + point_intersects.end(), + detail::point_in_multipolygon_test_functor{multipoints, multipolygons}); + + rmm::device_uvector multipoint_intersects(multipoints.num_multipoints(), stream); + auto offset_as_key_it = detail::make_counting_transform_iterator( + 0, offsets_to_keys_functor{multipoints.offsets_begin(), multipoints.offsets_end()}); - pairwise_point_polygon_distance_kernel<<>>( - multipoints, multipolygons, distances_first); + thrust::reduce_by_key(rmm::exec_policy(stream), + offset_as_key_it, + offset_as_key_it + multipoints.num_points(), + point_intersects.begin(), + thrust::make_discard_iterator(), + multipoint_intersects.begin(), + thrust::logical_or()); + + return multipoint_intersects; + }(); + + auto [threads_per_block, n_blocks] = grid_1d(multipolygons.num_points()); + + detail:: + pairwise_point_polygon_distance_kernel<<>>( + multipoints, multipolygons, multipoint_intersects.begin(), distances_first); CUSPATIAL_CHECK_CUDA(stream.value()); + + return distances_first + multipoints.size(); } } // namespace cuspatial diff --git a/cpp/include/cuspatial/experimental/point_polygon_distance.cuh b/cpp/include/cuspatial/experimental/point_polygon_distance.cuh index c5394e902..95c3a7d2c 100644 --- a/cpp/include/cuspatial/experimental/point_polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/point_polygon_distance.cuh @@ -33,7 +33,7 @@ namespace cuspatial { */ template OutputIt pairwise_point_polygon_distance(MultiPointRange multipoints, - MultiPolygonRangeB multipoiygons, + MultiPolygonRange multipoiygons, OutputIt distances_first, rmm::cuda_stream_view stream = rmm::cuda_stream_default); } // namespace cuspatial From 291f6e679eea9e87fb87646a09593d07bf863326 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 7 Mar 2023 11:08:10 -0800 Subject: [PATCH 10/60] add segment deduction guide --- cpp/include/cuspatial/experimental/geometry/segment.cuh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cpp/include/cuspatial/experimental/geometry/segment.cuh b/cpp/include/cuspatial/experimental/geometry/segment.cuh index 30d9240d4..8667e174c 100644 --- a/cpp/include/cuspatial/experimental/geometry/segment.cuh +++ b/cpp/include/cuspatial/experimental/geometry/segment.cuh @@ -19,6 +19,8 @@ #include #include +#include + #include namespace cuspatial { @@ -61,4 +63,7 @@ class alignas(sizeof(Vertex)) segment { template segment(vec_2d a, vec_2d b) -> segment>; +template +segment(thrust::device_reference> a, thrust::device_reference> b) + -> segment>; } // namespace cuspatial From efa68830493511998064a00fba9f3b293528865b Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 7 Mar 2023 11:08:30 -0800 Subject: [PATCH 11/60] add owning object type to vector factories --- .../cuspatial_test/vector_factories.cuh | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/cpp/include/cuspatial_test/vector_factories.cuh b/cpp/include/cuspatial_test/vector_factories.cuh index 00e4c5e07..7747dfe33 100644 --- a/cpp/include/cuspatial_test/vector_factories.cuh +++ b/cpp/include/cuspatial_test/vector_factories.cuh @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -50,6 +51,63 @@ auto make_device_uvector(std::initializer_list inl, return res; } +/** + * @brief Owning object of a multipolygon array following geoarrow layout. + * + * @tparam GeometryArray Array type of geometry offset array + * @tparam PartArray Array type of part offset array + * @tparam RingArray Array type of ring offset array + * @tparam CoordinateArray Array type of coordinate array + */ +template +class multipolygon_array { + public: + multipolygon_array(GeometryArray geometry_offsets_array, + PartArray part_offsets_array, + RingArray ring_offsets_array, + CoordinateArray coordinate_offsets_array) + : _geometry_offsets_array(geometry_offsets_array), + _part_offsets_array(part_offsets_array), + _ring_offsets_array(ring_offsets_array), + _coordinate_offsets_array(coordinate_offsets_array) + { + } + + /// Return the number of multilinestrings + auto size() { return _geometry_offsets_array.size() - 1; } + + /// Return range object of the multilinestring array + auto range() + { + return multipolygon_range(_geometry_offsets_array.begin(), + _geometry_offsets_array.end(), + _part_offsets_array.begin(), + _part_offsets_array.end(), + _ring_offsets_array.begin(), + _ring_offsets_array.end(), + _coordinate_offsets_array.begin(), + _coordinate_offsets_array.end()); + } + + protected: + GeometryArray _geometry_offsets_array; + PartArray _part_offsets_array; + RingArray _ring_offsets_array; + CoordinateArray _coordinate_offsets_array; +}; + +template +auto make_multipolygon_array(std::initializer_list geometry_inl, + std::initializer_list part_inl, + std::initializer_list ring_inl, + std::initializer_list> coord_inl) +{ + return multipolygon_array{make_device_vector(geometry_inl), + make_device_vector(part_inl), + make_device_vector(ring_inl), + make_device_vector(coord_inl)}; +} + /** * @brief Owning object of a multilinestring array following geoarrow layout. * From 23146ef32dbac357085464c11b47c4a73af56b8b Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 7 Mar 2023 11:08:58 -0800 Subject: [PATCH 12/60] add tests --- cpp/tests/CMakeLists.txt | 3 + .../spatial/point_polygon_distance_test.cu | 90 +++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 cpp/tests/experimental/spatial/point_polygon_distance_test.cu diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 6182e1238..821c7881b 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -133,6 +133,9 @@ ConfigureTest(POINT_DISTANCE_TEST_EXP ConfigureTest(POINT_LINESTRING_DISTANCE_TEST_EXP experimental/spatial/point_linestring_distance_test.cu) +ConfigureTest(POINT_POLYGON_DISTANCE_TEST_EXP + experimental/spatial/point_polygon_distance_test.cu) + ConfigureTest(HAUSDORFF_TEST_EXP experimental/spatial/hausdorff_test.cu) diff --git a/cpp/tests/experimental/spatial/point_polygon_distance_test.cu b/cpp/tests/experimental/spatial/point_polygon_distance_test.cu new file mode 100644 index 000000000..987cefa04 --- /dev/null +++ b/cpp/tests/experimental/spatial/point_polygon_distance_test.cu @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +#include +#include + +#include + +using namespace cuspatial; +using namespace cuspatial::test; + +template +struct PairwisePointPolygonDistanceTest : public ::testing::Test { + rmm::cuda_stream_view stream() { return rmm::cuda_stream_default; } + rmm::mr::device_memory_resource* mr() { return rmm::mr::get_current_device_resource(); } + + void run_single(std::initializer_list>> multipoints, + std::initializer_list multipolygon_geometry_offsets, + std::initializer_list multipolygon_part_offsets, + std::initializer_list multipolygon_ring_offsets, + std::initializer_list> multipolygon_coordinates, + std::initializer_list expected) + { + auto d_multipoints = make_multipoints_array(multipoints); + auto d_multipolygons = make_multipolygon_array(multipolygon_geometry_offsets, + multipolygon_part_offsets, + multipolygon_ring_offsets, + multipolygon_coordinates); + + auto got = rmm::device_uvector(d_multipoints.size(), stream()); + + auto ret = pairwise_point_polygon_distance( + d_multipoints.range(), d_multipolygons.range(), got.begin(), stream()); + + auto d_expected = make_device_vector(expected); + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(got, d_expected); + EXPECT_EQ(ret, got.end()); + } +}; + +using TestTypes = ::testing::Types; + +TYPED_TEST_CASE(PairwisePointPolygonDistanceTest, TestTypes); + +TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairOnePolygonOneRing) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST(this->run_single, + {{P{0, 0}}}, + {0, 1}, + {0, 1}, + {0, 5}, + {P{-1, -1}, P{1, -1}, P{1, 1}, P{-1, 1}, P{-1, -1}}, + {0.0}); +} + +TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairOnePolygonOneRing2) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST(this->run_single, + {{P{0, 2}}}, + {0, 1}, + {0, 1}, + {0, 5}, + {P{-1, -1}, P{1, -1}, P{1, 1}, P{-1, 1}, P{-1, -1}}, + {1.0}); +} From ead160a14c111f683c9c550944b95b7db7bfc12a Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 7 Mar 2023 11:09:21 -0800 Subject: [PATCH 13/60] add helper files --- .../detail/utility/offset_to_keys.cuh | 51 ++++++++ .../detail/algorithm/is_point_in_polygon.cuh | 112 ++++++++++++++++++ 2 files changed, 163 insertions(+) create mode 100644 cpp/include/cuspatial/detail/utility/offset_to_keys.cuh create mode 100644 cpp/include/cuspatial/experimental/detail/algorithm/is_point_in_polygon.cuh diff --git a/cpp/include/cuspatial/detail/utility/offset_to_keys.cuh b/cpp/include/cuspatial/detail/utility/offset_to_keys.cuh new file mode 100644 index 000000000..70665b4db --- /dev/null +++ b/cpp/include/cuspatial/detail/utility/offset_to_keys.cuh @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +/** @brief Given list offset and row `i`, return a unique key that represent the list of `i`. + * + * The key is computed by performing a `upper_bound` search with `i` in the offset array. + * Then subtracts the position with the start of offset array. + * + * Example: + * offset: 0 0 0 1 3 4 4 4 + * i: 0 1 2 3 + * key: 3 4 4 5 + * + * Note that the values of `key`, {offset[3], offset[4], offset[5]} denotes the ending + * position of the first 3 non-empty list. + */ +template +struct offsets_to_keys_functor { + Iterator _offsets_begin; + Iterator _offsets_end; + + offsets_to_keys_functor(Iterator offset_begin, Iterator offset_end) + : _offsets_begin(offset_begin), _offsets_end(offset_end) + { + } + + template + IndexType __device__ operator()(IndexType i) + { + return thrust::distance(_offsets_begin, + thrust::upper_bound(thrust::seq, _offsets_begin, _offsets_end, i)); + } +}; diff --git a/cpp/include/cuspatial/experimental/detail/algorithm/is_point_in_polygon.cuh b/cpp/include/cuspatial/experimental/detail/algorithm/is_point_in_polygon.cuh new file mode 100644 index 000000000..e489c22dd --- /dev/null +++ b/cpp/include/cuspatial/experimental/detail/algorithm/is_point_in_polygon.cuh @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2022, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include +#include + +namespace cuspatial { +namespace detail { + +/** + * @brief Kernel to test if a point is inside a polygon. + * + * Implemented based on Eric Haines's crossings-multiply algorithm: + * See "Crossings test" section of http://erich.realtimerendering.com/ptinpoly/ + * The improvement in addenda is also addopted to remove divisions in this kernel. + * + * TODO: the ultimate goal of refactoring this as independent function is to remove + * src/utility/point_in_polygon.cuh and its usage in quadtree_point_in_polygon.cu. It isn't + * possible today without further work to refactor quadtree_point_in_polygon into header only + * API. + */ +template +__device__ inline bool is_point_in_polygon(vec_2d const& test_point, PolygonRef const& polygon) +{ + bool point_is_within = false; + bool is_colinear = false; + printf("Polygon num rings: %d\n", static_cast(polygon.num_rings())); + for (auto ring : polygon) { + printf("here in ring\n"); + bool y0_flag = ring.segment(ring.num_segments() - 1).v2.y > test_point.y; + bool y1_flag; + for (auto [a, b] : ring) { + printf("here in segment (%f, %f) -> (%f, %f)\n", a.x, a.y, b.x, b.y); + // for each line segment, including the segment between the last and first vertex + T run = b.x - a.x; + T rise = b.y - a.y; + + // Points on the line segment are the same, so intersection is impossible. + // This is possible because we allow closed or unclosed polygons. + T constexpr zero = 0.0; + if (float_equal(run, zero) && float_equal(rise, zero)) continue; + + T rise_to_point = test_point.y - a.y; + + // colinearity test + T run_to_point = test_point.x - a.x; + is_colinear = float_equal(run * rise_to_point, run_to_point * rise); + if (is_colinear) { break; } + + y1_flag = a.y > test_point.y; + if (y1_flag != y0_flag) { + // Transform the following inequality to avoid division + // test_point.x < (run / rise) * rise_to_point + a.x + auto lhs = (test_point.x - a.x) * rise; + auto rhs = run * rise_to_point; + if (lhs < rhs != y1_flag) { point_is_within = not point_is_within; } + } + b = a; + y0_flag = y1_flag; + } + if (is_colinear) { + point_is_within = false; + break; + } + } + + printf("Exiting pip.\n"); + + return point_is_within; +} + +template ::difference_type, + class Cart2dItDiffType = typename std::iterator_traits::difference_type> +__device__ inline bool is_point_in_polygon(Cart2d const& test_point, + OffsetType poly_begin, + OffsetType poly_end, + OffsetIterator ring_offsets_first, + OffsetItDiffType const& num_rings, + Cart2dIt poly_points_first, + Cart2dItDiffType const& num_poly_points) +{ + auto polygon = polygon_ref{ring_offsets_first + poly_begin, + ring_offsets_first + poly_end, + poly_points_first, + poly_points_first + num_poly_points}; + return is_point_in_polygon(test_point, polygon); +} + +} // namespace detail +} // namespace cuspatial From 09bd35f2bd8ead3a6fa525bb28ccfb4cf5981ecd Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 8 Mar 2023 13:15:27 -0800 Subject: [PATCH 14/60] add more tests --- .../spatial/point_polygon_distance_test.cu | 337 ++++++++++++++++++ 1 file changed, 337 insertions(+) diff --git a/cpp/tests/experimental/spatial/point_polygon_distance_test.cu b/cpp/tests/experimental/spatial/point_polygon_distance_test.cu index 987cefa04..519ed6819 100644 --- a/cpp/tests/experimental/spatial/point_polygon_distance_test.cu +++ b/cpp/tests/experimental/spatial/point_polygon_distance_test.cu @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -61,6 +62,20 @@ using TestTypes = ::testing::Types; TYPED_TEST_CASE(PairwisePointPolygonDistanceTest, TestTypes); +TYPED_TEST(PairwisePointPolygonDistanceTest, ZeroPairs) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST(this->run_single, + std::initializer_list>{}, + {0}, + {0}, + {0}, + std::initializer_list

{}, + std::initializer_list{}); +} + TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairOnePolygonOneRing) { using T = TypeParam; @@ -88,3 +103,325 @@ TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairOnePolygonOneRing2) {P{-1, -1}, P{1, -1}, P{1, 1}, P{-1, 1}, P{-1, -1}}, {1.0}); } + +TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairOnePolygonTwoRings) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST(this->run_single, + {{P{0, 0}}}, + {0, 1}, + {0, 2}, + {0, 5, 10}, + { + P{-2, -2}, + P{2, -2}, + P{2, 2}, + P{-2, 2}, + P{-2, -2}, + P{-1, -1}, + P{1, -1}, + P{1, 1}, + P{-1, 1}, + P{-1, -1}, + }, + {1.0}); +} + +TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairOnePolygonTwoRings2) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST(this->run_single, + {{P{1.5, 0}}}, + {0, 1}, + {0, 2}, + {0, 5, 10}, + { + P{-2, -2}, + P{2, -2}, + P{2, 2}, + P{-2, 2}, + P{-2, -2}, + P{-1, -1}, + P{1, -1}, + P{1, 1}, + P{-1, 1}, + P{-1, -1}, + }, + {0.0}); +} + +TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairOnePolygonTwoRings3) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST(this->run_single, + {{P{3, 0}}}, + {0, 1}, + {0, 2}, + {0, 5, 10}, + { + P{-2, -2}, + P{2, -2}, + P{2, 2}, + P{-2, 2}, + P{-2, -2}, + P{-1, -1}, + P{1, -1}, + P{1, 1}, + P{-1, 1}, + P{-1, -1}, + }, + {1.0}); +} + +TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairTwoPolygonOneRing) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST(this->run_single, + {{P{1, 1}}}, + {0, 2}, + {0, 1, 2}, + {0, 5, 10}, + { + P{-2, -2}, + P{0, -2}, + P{0, 0}, + P{-2, 0}, + P{-2, -2}, + P{0, 0}, + P{2, 0}, + P{2, 2}, + P{0, 2}, + P{0, 0}, + }, + {0.0}); +} + +TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairTwoPolygonOneRing2) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST(this->run_single, + {{P{-1, -1}}}, + {0, 2}, + {0, 1, 2}, + {0, 5, 10}, + { + P{-2, -2}, + P{0, -2}, + P{0, 0}, + P{-2, 0}, + P{-2, -2}, + P{0, 0}, + P{2, 0}, + P{2, 2}, + P{0, 2}, + P{0, 0}, + }, + {0.0}); +} + +TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairTwoPolygonOneRing3) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST(this->run_single, + {{P{-1, 0.5}}}, + {0, 2}, + {0, 1, 2}, + {0, 5, 10}, + { + P{-2, -2}, + P{0, -2}, + P{0, 0}, + P{-2, 0}, + P{-2, -2}, + P{0, 0}, + P{2, 0}, + P{2, 2}, + P{0, 2}, + P{0, 0}, + }, + {0.5}); +} + +TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairTwoPolygonOneRing4) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST(this->run_single, + {{P{-0.3, 1}}}, + {0, 2}, + {0, 1, 2}, + {0, 5, 10}, + { + P{-2, -2}, + P{0, -2}, + P{0, 0}, + P{-2, 0}, + P{-2, -2}, + P{0, 0}, + P{2, 0}, + P{2, 2}, + P{0, 2}, + P{0, 0}, + }, + {0.3}); +} + +TYPED_TEST(PairwisePointPolygonDistanceTest, TwoPairOnePolygonOneRing) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST(this->run_single, + {{P{-0.6, -0.6}}, {P{0, 0}}}, + {0, 1, 2}, + {0, 1, 2}, + {0, 4, 8}, + { + P{-1, -1}, + P{0, 0}, + P{0, 1}, + P{-1, -1}, + P{1, 1}, + P{1, 0}, + P{2, 2}, + P{1, 1}, + }, + {0.0, 1.0}); +} + +TYPED_TEST(PairwisePointPolygonDistanceTest, TwoPairTwoPolygonTwoRing) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST(this->run_single, + {{P{2.5, 3}}, {P{-2.5, -1.5}}}, + {0, 1, 2}, + {0, 2, 4}, + {0, 5, 10, 14, 18}, + { + P{0, 0}, + P{3, 0}, + P{3, 3}, + P{0, 3}, + P{0, 0}, + P{1, 1}, + P{2, 1}, + P{2, 2}, + P{1, 2}, + P{1, 1}, + + P{0, 0}, + P{-3, 0}, + P{-3, -3}, + P{0, 0}, + P{-1, -1}, + P{-2, -1}, + P{-2, -2}, + P{-1, -1}, + }, + {0.0, 0.5}); +} + +TYPED_TEST(PairwisePointPolygonDistanceTest, ThreePolygons) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST(this->run_single, + {{P{1, 1}}, {P{2, 2}}, {P{1.5, 0}}}, + {0, 1, 2, 3}, + {0, 2, 3, 6}, + {0, 4, 8, 12, 17, 22, 27}, + {// POLYGON ((0 1, -1 -1, 1 -1, 0 1), (0 0.5, 0.5 -0.5, -0.5 -0.5, 0 0.5)) + P{0, 1}, + P{-1, -1}, + P{1, -1}, + P{0, 1}, + P{0, 0.5}, + P{0.5, -0.5}, + P{-0.5, -0.5}, + P{0, 0.5}, + // POLYGON ((1 1, 1 2, 2 1, 1 1)) + P{1, 1}, + P{1, 2}, + P{2, 1}, + P{1, 1}, + // POLYGON ( + // (-3 -3, 3 -3, 3 3, -3 3, -3 -3), + // (-2 -2, -1 -2, -1 2, -2 2, -2 -2), + // (2 2, 2 -2, 1 -2, 1 2, 2 2) + // ) + P{-3, -3}, + P{3, -3}, + P{3, 3}, + P{-3, 3}, + P{-3, -3}, + P{-2, -2}, + P{-1, -2}, + P{-1, 2}, + P{-2, 2}, + P{-2, -2}, + P{2, 2}, + P{2, -2}, + P{1, -2}, + P{1, 2}, + P{2, 2}}, + {0.894427190999916, 0.7071067811865476, 0.5}); +} + +TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairMultiPointOnePolygon) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST(this->run_single, + {{P{0, 3}, P{2, 0}}}, + {0, 1}, + {0, 1}, + {0, 5}, + {P{0, 1}, P{-1, -1}, P{1, -1}, P{0, 1}}, + {1.3416407864998738}); +} + +TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairMultiPointOnePolygon2) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST(this->run_single, + {{P{0, 3}, P{0, 0}}}, + {0, 1}, + {0, 1}, + {0, 5}, + {P{0, 1}, P{-1, -1}, P{1, -1}, P{0, 1}}, + {0.0}); +} + +TYPED_TEST(PairwisePointPolygonDistanceTest, TwoPairMultiPointOnePolygon2) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST( + this->run_single, + {{P{0, 2}, P{0, 0}}, {P{1, 1}, P{-1, -1}}}, + {0, 1, 2}, + {0, 1, 2}, + {0, 5, 9}, + {P{-1, -1}, P{1, -1}, P{1, 1}, P{-1, 1}, P{-1, -1}, P{-1, 1}, P{1, 1}, P{0, -1}, P{-1, 1}}, + {0.0, 0.0}); +} From 92760d13d330f63ca5f4d999220edfa3233e4427 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 8 Mar 2023 13:23:03 -0800 Subject: [PATCH 15/60] bug fixes --- .../detail/algorithm/is_point_in_polygon.cuh | 23 ++++++++++-- .../detail/geometry/linestring_ref.cuh | 14 +++++++- .../detail/point_polygon_distance.cuh | 35 +++++++++++++++---- .../detail/ranges/multipolygon_range.cuh | 8 +++-- .../experimental/geometry/linestring_ref.cuh | 5 +++ .../ranges/multipolygon_range.cuh | 3 -- 6 files changed, 73 insertions(+), 15 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/algorithm/is_point_in_polygon.cuh b/cpp/include/cuspatial/experimental/detail/algorithm/is_point_in_polygon.cuh index e489c22dd..61b80b365 100644 --- a/cpp/include/cuspatial/experimental/detail/algorithm/is_point_in_polygon.cuh +++ b/cpp/include/cuspatial/experimental/detail/algorithm/is_point_in_polygon.cuh @@ -45,9 +45,18 @@ __device__ inline bool is_point_in_polygon(vec_2d const& test_point, PolygonR printf("Polygon num rings: %d\n", static_cast(polygon.num_rings())); for (auto ring : polygon) { printf("here in ring\n"); - bool y0_flag = ring.segment(ring.num_segments() - 1).v2.y > test_point.y; + auto last_segment = ring.segment(ring.num_segments() - 1); + printf("Last segment is: (%f %f)->(%f %f)\n", + last_segment.v1.x, + last_segment.v1.y, + last_segment.v2.x, + last_segment.v2.y); + + auto b = last_segment.v2; + bool y0_flag = b.y > test_point.y; bool y1_flag; - for (auto [a, b] : ring) { + for (auto it = ring.point_begin(); it != ring.point_end(); ++it) { + vec_2d a = *it; printf("here in segment (%f, %f) -> (%f, %f)\n", a.x, a.y, b.x, b.y); // for each line segment, including the segment between the last and first vertex T run = b.x - a.x; @@ -65,7 +74,12 @@ __device__ inline bool is_point_in_polygon(vec_2d const& test_point, PolygonR is_colinear = float_equal(run * rise_to_point, run_to_point * rise); if (is_colinear) { break; } + // y0_flag = a.y > test_point.y; y1_flag = a.y > test_point.y; + printf("\t y0_flag: %d, y1_flag: %d, point_is_within: %d\n", + static_cast(y0_flag), + static_cast(y1_flag), + static_cast(point_is_within)); if (y1_flag != y0_flag) { // Transform the following inequality to avoid division // test_point.x < (run / rise) * rise_to_point + a.x @@ -82,11 +96,14 @@ __device__ inline bool is_point_in_polygon(vec_2d const& test_point, PolygonR } } - printf("Exiting pip.\n"); + printf("Exiting pip. Result: %d\n", static_cast(point_is_within)); return point_is_within; } +/** + * @brief Compatibility layer with non-OOP style input + */ template CUSPATIAL_HOST_DEVICE auto linestring_ref::num_segments() const { // The number of segment equals the number of points minus 1. And the number of points - // is thrust::distance(_point_begin, _point_end) - 1. + // is thrust::distance(_point_begin, _point_end). return thrust::distance(_point_begin, _point_end) - 1; } @@ -70,6 +70,18 @@ CUSPATIAL_HOST_DEVICE auto linestring_ref::segment_end() const return segment_begin() + num_segments(); } +template +CUSPATIAL_HOST_DEVICE auto linestring_ref::point_begin() const +{ + return _point_begin; +} + +template +CUSPATIAL_HOST_DEVICE auto linestring_ref::point_end() const +{ + return _point_end; +} + template template CUSPATIAL_HOST_DEVICE auto linestring_ref::segment(IndexType i) const diff --git a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh index 7ab760b0b..efd825cb3 100644 --- a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh @@ -16,11 +16,14 @@ #pragma once +#include + #include #include #include #include #include +#include #include #include #include @@ -36,6 +39,7 @@ #include #include +#include #include namespace cuspatial { @@ -70,6 +74,7 @@ struct point_in_multipolygon_test_functor { intersects = intersects || is_point_in_polygon(point, polygon); } + printf("Intersect: %d\n", static_cast(intersects)); return static_cast(intersects); } }; @@ -88,25 +93,32 @@ void __global__ pairwise_point_polygon_distance_kernel(MultiPointRange multipoin { using T = typename MultiPointRange::element_t; - T dist_squared = std::numeric_limits::max(); for (auto idx = threadIdx.x + blockIdx.x * blockDim.x; idx < multipolygons.num_points(); idx += gridDim.x * blockDim.x) { auto geometry_idx = multipolygons.geometry_idx_from_segment_idx(idx); if (geometry_idx == MultiPolygonRange::INVALID_INDEX) continue; + printf("Intesects? %d Geometry_idx: %d\n", + static_cast(intersects[geometry_idx]), + static_cast(geometry_idx)); + if (intersects[geometry_idx]) { // TODO: only the leading thread of the pair need to store the result, atomics is not needed. + printf("In intersects, idx: %d\n", static_cast(idx)); atomicMin(&distances[geometry_idx], T{0.0}); continue; } - printf("In distance kernel: %d %d %d", + printf("In distance kernel: point_idx: %d segment_idx: %d\n", static_cast(idx), - static_cast(geometry_idx), - static_cast(intersects.size())); + static_cast(geometry_idx)); - auto [a, b] = multipolygons.get_segment(idx); + T dist_squared = std::numeric_limits::max(); + auto [a, b] = multipolygons.get_segment(idx); for (vec_2d point : multipoints[geometry_idx]) { + printf("point: %f %f\n", point.x, point.y); + printf("segment: (%f %f) -> (%f %f)\n", a.x, a.y, b.x, b.y); + printf("dist: %f\n", point_to_segment_distance_squared(point, a, b)); dist_squared = min(dist_squared, point_to_segment_distance_squared(point, a, b)); } @@ -134,6 +146,8 @@ OutputIt pairwise_point_polygon_distance(MultiPointRange multipoints, CUSPATIAL_EXPECTS(multipoints.size() == multipolygons.size(), "Must have the same number of input rows."); + if (multipoints.size() == 0) return distances_first; + auto multipoint_intersects = [&]() { rmm::device_uvector point_intersects(multipoints.num_points(), stream); @@ -142,7 +156,10 @@ OutputIt pairwise_point_polygon_distance(MultiPointRange multipoints, point_intersects.end(), detail::point_in_multipolygon_test_functor{multipoints, multipolygons}); + // TODO: optimize when input is not a multipolygon rmm::device_uvector multipoint_intersects(multipoints.num_multipoints(), stream); + detail::zero_data_async(multipoint_intersects.begin(), multipoint_intersects.end(), stream); + auto offset_as_key_it = detail::make_counting_transform_iterator( 0, offsets_to_keys_functor{multipoints.offsets_begin(), multipoints.offsets_end()}); @@ -157,10 +174,16 @@ OutputIt pairwise_point_polygon_distance(MultiPointRange multipoints, return multipoint_intersects; }(); + thrust::fill(rmm::exec_policy(stream), + distances_first, + distances_first + multipoints.size(), + std::numeric_limits::max()); auto [threads_per_block, n_blocks] = grid_1d(multipolygons.num_points()); + std::cout << "Size of multipoint intersects: " << multipoint_intersects.size() << std::endl; + detail:: - pairwise_point_polygon_distance_kernel<<>>( + pairwise_point_polygon_distance_kernel<<>>( multipoints, multipolygons, multipoint_intersects.begin(), distances_first); CUSPATIAL_CHECK_CUDA(stream.value()); diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh index b44420532..33b7a512e 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh @@ -203,8 +203,7 @@ multipolygon_range:: part_idx_from_ring_idx(IndexType ring_idx) { return thrust::distance( - _part_begin, - thrust::prev(thrust::upper_bound(thrust::seq, _part_begin, _part_begin, ring_idx))); + _part_begin, thrust::prev(thrust::upper_bound(thrust::seq, _part_begin, _part_end, ring_idx))); } template :: geometry_idx_from_segment_idx(IndexType segment_idx) { + printf("segment_idx: %d\n", static_cast(segment_idx)); auto ring_idx = ring_idx_from_point_idx(segment_idx); + printf("ring_idx: %d\n", static_cast(ring_idx)); if (!is_valid_segment_id(segment_idx, ring_idx)) return multipolygon_range:: INVALID_INDEX; + + auto part_idx = part_idx_from_ring_idx(ring_idx); + printf("part_idx: %d\n", static_cast(part_idx)); return geometry_idx_from_part_idx(part_idx_from_ring_idx(ring_idx)); } diff --git a/cpp/include/cuspatial/experimental/geometry/linestring_ref.cuh b/cpp/include/cuspatial/experimental/geometry/linestring_ref.cuh index 0ebad40f4..707b9e0cc 100644 --- a/cpp/include/cuspatial/experimental/geometry/linestring_ref.cuh +++ b/cpp/include/cuspatial/experimental/geometry/linestring_ref.cuh @@ -38,6 +38,11 @@ class linestring_ref { /// Return iterator to one past the last segment CUSPATIAL_HOST_DEVICE auto segment_end() const; + /// Return iterator to the first point of the linestring + CUSPATIAL_HOST_DEVICE auto point_begin() const; + /// Return iterator to one past the last point + CUSPATIAL_HOST_DEVICE auto point_end() const; + /// Return iterator to the first segment of the linestring CUSPATIAL_HOST_DEVICE auto begin() const { return segment_begin(); } /// Return iterator to one past the last segment diff --git a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh index b9efd64e5..8a7300213 100644 --- a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh @@ -112,9 +112,6 @@ class multipolygon_range { template CUSPATIAL_HOST_DEVICE auto operator[](IndexType multipolygon_idx); - // template - // CUSPATIAL_HOST_DEVICE auto get_point(IndexType point_idx); - template CUSPATIAL_HOST_DEVICE auto get_segment(IndexType segment_idx); From 8acb5dc48127664d92b5f89370616aa1dfa25b6e Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 8 Mar 2023 13:47:29 -0800 Subject: [PATCH 16/60] cleanups --- .../detail/algorithm/is_point_in_polygon.cuh | 14 --------- .../multilinestring_ref.cuh | 3 -- .../detail/point_polygon_distance.cuh | 30 ++----------------- .../detail/ranges/multipolygon_range.cuh | 27 ++++++++--------- .../ranges/multipolygon_range.cuh | 7 +++++ 5 files changed, 22 insertions(+), 59 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/algorithm/is_point_in_polygon.cuh b/cpp/include/cuspatial/experimental/detail/algorithm/is_point_in_polygon.cuh index 61b80b365..135011122 100644 --- a/cpp/include/cuspatial/experimental/detail/algorithm/is_point_in_polygon.cuh +++ b/cpp/include/cuspatial/experimental/detail/algorithm/is_point_in_polygon.cuh @@ -42,22 +42,14 @@ __device__ inline bool is_point_in_polygon(vec_2d const& test_point, PolygonR { bool point_is_within = false; bool is_colinear = false; - printf("Polygon num rings: %d\n", static_cast(polygon.num_rings())); for (auto ring : polygon) { - printf("here in ring\n"); auto last_segment = ring.segment(ring.num_segments() - 1); - printf("Last segment is: (%f %f)->(%f %f)\n", - last_segment.v1.x, - last_segment.v1.y, - last_segment.v2.x, - last_segment.v2.y); auto b = last_segment.v2; bool y0_flag = b.y > test_point.y; bool y1_flag; for (auto it = ring.point_begin(); it != ring.point_end(); ++it) { vec_2d a = *it; - printf("here in segment (%f, %f) -> (%f, %f)\n", a.x, a.y, b.x, b.y); // for each line segment, including the segment between the last and first vertex T run = b.x - a.x; T rise = b.y - a.y; @@ -76,10 +68,6 @@ __device__ inline bool is_point_in_polygon(vec_2d const& test_point, PolygonR // y0_flag = a.y > test_point.y; y1_flag = a.y > test_point.y; - printf("\t y0_flag: %d, y1_flag: %d, point_is_within: %d\n", - static_cast(y0_flag), - static_cast(y1_flag), - static_cast(point_is_within)); if (y1_flag != y0_flag) { // Transform the following inequality to avoid division // test_point.x < (run / rise) * rise_to_point + a.x @@ -96,8 +84,6 @@ __device__ inline bool is_point_in_polygon(vec_2d const& test_point, PolygonR } } - printf("Exiting pip. Result: %d\n", static_cast(point_is_within)); - return point_is_within; } diff --git a/cpp/include/cuspatial/experimental/detail/geometry_collection/multilinestring_ref.cuh b/cpp/include/cuspatial/experimental/detail/geometry_collection/multilinestring_ref.cuh index b8768d18f..b4c76ec32 100644 --- a/cpp/include/cuspatial/experimental/detail/geometry_collection/multilinestring_ref.cuh +++ b/cpp/include/cuspatial/experimental/detail/geometry_collection/multilinestring_ref.cuh @@ -38,9 +38,6 @@ struct to_linestring_functor { CUSPATIAL_HOST_DEVICE auto operator()(difference_type i) { - printf("In to_linestring_functor: %d %d\n", - static_cast(part_begin[i]), - static_cast(part_begin[i + 1])); return linestring_ref{point_begin + part_begin[i], point_begin + part_begin[i + 1]}; } }; diff --git a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh index efd825cb3..21bc5a332 100644 --- a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh @@ -58,23 +58,13 @@ struct point_in_multipolygon_test_functor { template uint8_t __device__ operator()(IndexType pidx) { - printf("%d\n", static_cast(pidx)); - - auto point = thrust::raw_reference_cast(multipoints.point(pidx)); - - printf("%f, %f\n", point.x, point.y); - + auto point = thrust::raw_reference_cast(multipoints.point(pidx)); auto geometry_idx = multipoints.geometry_idx_from_point_idx(pidx); - printf("%d\n", static_cast(geometry_idx)); - bool intersects = false; for (auto polygon : multipolygons[geometry_idx]) { - printf("here\n"); intersects = intersects || is_point_in_polygon(point, polygon); } - - printf("Intersect: %d\n", static_cast(intersects)); return static_cast(intersects); } }; @@ -98,27 +88,15 @@ void __global__ pairwise_point_polygon_distance_kernel(MultiPointRange multipoin auto geometry_idx = multipolygons.geometry_idx_from_segment_idx(idx); if (geometry_idx == MultiPolygonRange::INVALID_INDEX) continue; - printf("Intesects? %d Geometry_idx: %d\n", - static_cast(intersects[geometry_idx]), - static_cast(geometry_idx)); - if (intersects[geometry_idx]) { - // TODO: only the leading thread of the pair need to store the result, atomics is not needed. - printf("In intersects, idx: %d\n", static_cast(idx)); - atomicMin(&distances[geometry_idx], T{0.0}); + if (multipolygons.is_first_point_of_multipolygon(idx, geometry_idx)) + distances[geometry_idx] = T{0.0}; continue; } - printf("In distance kernel: point_idx: %d segment_idx: %d\n", - static_cast(idx), - static_cast(geometry_idx)); - T dist_squared = std::numeric_limits::max(); auto [a, b] = multipolygons.get_segment(idx); for (vec_2d point : multipoints[geometry_idx]) { - printf("point: %f %f\n", point.x, point.y); - printf("segment: (%f %f) -> (%f %f)\n", a.x, a.y, b.x, b.y); - printf("dist: %f\n", point_to_segment_distance_squared(point, a, b)); dist_squared = min(dist_squared, point_to_segment_distance_squared(point, a, b)); } @@ -180,8 +158,6 @@ OutputIt pairwise_point_polygon_distance(MultiPointRange multipoints, std::numeric_limits::max()); auto [threads_per_block, n_blocks] = grid_1d(multipolygons.num_points()); - std::cout << "Size of multipoint intersects: " << multipoint_intersects.size() << std::endl; - detail:: pairwise_point_polygon_distance_kernel<<>>( multipoints, multipolygons, multipoint_intersects.begin(), distances_first); diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh index 33b7a512e..0228d1de8 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh @@ -66,18 +66,6 @@ struct to_multipolygon_functor { CUSPATIAL_HOST_DEVICE auto operator()(difference_type i) { - auto new_part_begin = _part_begin + _geometry_begin[i]; - auto new_part_end = thrust::next(_part_begin, _geometry_begin[i + 1] + 1); - - printf( - "In to_multipolygon_functor: %d %d %d\n\t new_part_begin_val: %d, " - "new_part_end_dist_from_begin: %d\n", - static_cast(i), - static_cast(_geometry_begin[i]), - static_cast(_geometry_begin[i + 1]), - static_cast(*new_part_begin), - static_cast(thrust::distance(new_part_end, new_part_begin))); - return multipolygon_ref{_part_begin + _geometry_begin[i], thrust::next(_part_begin, _geometry_begin[i + 1] + 1), _ring_begin, @@ -229,15 +217,12 @@ CUSPATIAL_HOST_DEVICE auto multipolygon_range:: geometry_idx_from_segment_idx(IndexType segment_idx) { - printf("segment_idx: %d\n", static_cast(segment_idx)); auto ring_idx = ring_idx_from_point_idx(segment_idx); - printf("ring_idx: %d\n", static_cast(ring_idx)); if (!is_valid_segment_id(segment_idx, ring_idx)) return multipolygon_range:: INVALID_INDEX; auto part_idx = part_idx_from_ring_idx(ring_idx); - printf("part_idx: %d\n", static_cast(part_idx)); return geometry_idx_from_part_idx(part_idx_from_ring_idx(ring_idx)); } @@ -280,4 +265,16 @@ multipolygon_range::g return segment{_point_begin[segment_idx], _point_begin[segment_idx + 1]}; } +template +template +CUSPATIAL_HOST_DEVICE bool +multipolygon_range:: + is_first_point_of_multipolygon(IndexType1 point_idx, IndexType2 geometry_idx) +{ + return point_idx == _ring_begin[_part_begin[_geometry_begin[geometry_idx]]]; +} + } // namespace cuspatial diff --git a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh index 8a7300213..775c7dba4 100644 --- a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh @@ -112,9 +112,16 @@ class multipolygon_range { template CUSPATIAL_HOST_DEVICE auto operator[](IndexType multipolygon_idx); + /// Returns the `segment_idx`th segment in the multipolygon range. template CUSPATIAL_HOST_DEVICE auto get_segment(IndexType segment_idx); + /// Returns `true` if `point_idx`th point is the first point of its + /// multipolygon + template + CUSPATIAL_HOST_DEVICE bool is_first_point_of_multipolygon(IndexType1 point_idx, + IndexType2 geometry_idx); + protected: GeometryIterator _geometry_begin; GeometryIterator _geometry_end; From a2b94fe7410d0e4a32940ee52a13db59955e0e27 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 8 Mar 2023 14:38:32 -0800 Subject: [PATCH 17/60] fix tests --- cpp/tests/experimental/spatial/point_polygon_distance_test.cu | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/tests/experimental/spatial/point_polygon_distance_test.cu b/cpp/tests/experimental/spatial/point_polygon_distance_test.cu index 519ed6819..ce5f78b23 100644 --- a/cpp/tests/experimental/spatial/point_polygon_distance_test.cu +++ b/cpp/tests/experimental/spatial/point_polygon_distance_test.cu @@ -308,7 +308,7 @@ TYPED_TEST(PairwisePointPolygonDistanceTest, TwoPairTwoPolygonTwoRing) using P = vec_2d; CUSPATIAL_RUN_TEST(this->run_single, - {{P{2.5, 3}}, {P{-2.5, -1.5}}}, + {{P{2.5, 3}}, {P{-1.75, -1.5}}}, {0, 1, 2}, {0, 2, 4}, {0, 5, 10, 14, 18}, @@ -333,7 +333,7 @@ TYPED_TEST(PairwisePointPolygonDistanceTest, TwoPairTwoPolygonTwoRing) P{-2, -2}, P{-1, -1}, }, - {0.0, 0.5}); + {0.0, 0.17677669529663687}); } TYPED_TEST(PairwisePointPolygonDistanceTest, ThreePolygons) From 46a67fe5612967a44d7a1708b0b742e0997a7e02 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 8 Mar 2023 14:39:07 -0800 Subject: [PATCH 18/60] optimize single point range input --- .../experimental/detail/point_polygon_distance.cuh | 7 ++++++- .../experimental/detail/ranges/multipoint_range.cuh | 6 ++++++ .../cuspatial/experimental/ranges/multipoint_range.cuh | 5 +++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh index 21bc5a332..b217c5f35 100644 --- a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh @@ -126,6 +126,9 @@ OutputIt pairwise_point_polygon_distance(MultiPointRange multipoints, if (multipoints.size() == 0) return distances_first; + // Compute whether each multipoint intersects with the corresponding multipolygon. + // First, compute the point-multipolygon intersection. Then use reduce-by-key to + // compute the multipoint-multipolygon intersection. auto multipoint_intersects = [&]() { rmm::device_uvector point_intersects(multipoints.num_points(), stream); @@ -134,7 +137,9 @@ OutputIt pairwise_point_polygon_distance(MultiPointRange multipoints, point_intersects.end(), detail::point_in_multipolygon_test_functor{multipoints, multipolygons}); - // TODO: optimize when input is not a multipolygon + // `multipoints` contains only single points, no need to reduce. + if (multipoints.is_single_point_range()) return point_intersects; + rmm::device_uvector multipoint_intersects(multipoints.num_multipoints(), stream); detail::zero_data_async(multipoint_intersects.begin(), multipoint_intersects.end(), stream); diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multipoint_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multipoint_range.cuh index d3d8926d5..7b7ce5243 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multipoint_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multipoint_range.cuh @@ -140,4 +140,10 @@ CUSPATIAL_HOST_DEVICE auto multipoint_range::poin return _points_begin[idx]; } +template +CUSPATIAL_HOST_DEVICE bool multipoint_range::is_single_point_range() +{ + return num_multipoints() == num_points(); +} + } // namespace cuspatial diff --git a/cpp/include/cuspatial/experimental/ranges/multipoint_range.cuh b/cpp/include/cuspatial/experimental/ranges/multipoint_range.cuh index 1ee08b5cc..1e2ac7c42 100644 --- a/cpp/include/cuspatial/experimental/ranges/multipoint_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multipoint_range.cuh @@ -141,6 +141,11 @@ class multipoint_range { template CUSPATIAL_HOST_DEVICE auto point(IndexType idx); + /** + * @brief Returns `true` if the range contains only single points + */ + CUSPATIAL_HOST_DEVICE bool is_single_point_range(); + protected: /// Iterator to the start of the index array of start positions to each multipoint. GeometryIterator _geometry_begin; From b725b52f70067c3171ee67081de9f9b0af6b5280 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 8 Mar 2023 14:51:02 -0800 Subject: [PATCH 19/60] docs, type checks in range ctor --- .../experimental/detail/point_polygon_distance.cuh | 9 --------- .../detail/ranges/multipoint_range.cuh | 2 ++ .../detail/ranges/multipolygon_range.cuh | 2 ++ .../experimental/point_polygon_distance.cuh | 14 +++++++++----- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh index b217c5f35..b39ba6dec 100644 --- a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh @@ -16,8 +16,6 @@ #pragma once -#include - #include #include #include @@ -114,13 +112,6 @@ OutputIt pairwise_point_polygon_distance(MultiPointRange multipoints, { using T = typename MultiPointRange::element_t; - static_assert(is_same_floating_point(), - "Inputs must have same floating point value type."); - - static_assert( - is_same, typename MultiPointRange::point_t, typename MultiPolygonRange::point_t>(), - "Inputs must be cuspatial::vec_2d"); - CUSPATIAL_EXPECTS(multipoints.size() == multipolygons.size(), "Must have the same number of input rows."); diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multipoint_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multipoint_range.cuh index 7b7ce5243..f28ed5ef2 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multipoint_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multipoint_range.cuh @@ -63,6 +63,8 @@ multipoint_range::multipoint_range(GeometryIterat _points_begin(points_begin), _points_end(points_end) { + static_assert(is_vec_2d>(), + "Coordinate range must be constructed with iterators to vec_2d."); } template diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh index 0228d1de8..7e0e868bf 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh @@ -103,6 +103,8 @@ multipolygon_range::m _point_begin(point_begin), _point_end(point_end) { + static_assert(is_vec_2d>(), + "Coordinate range must be constructed with iterators to vec_2d."); } template OutputIt pairwise_point_polygon_distance(MultiPointRange multipoints, From ab59e7d1352ed3b3c843abbfc95f09b06e8ad57f Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 8 Mar 2023 15:13:15 -0800 Subject: [PATCH 20/60] use range based for loop in is_point_in_polygon --- .../experimental/detail/algorithm/is_point_in_polygon.cuh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/algorithm/is_point_in_polygon.cuh b/cpp/include/cuspatial/experimental/detail/algorithm/is_point_in_polygon.cuh index 135011122..5ddf208d7 100644 --- a/cpp/include/cuspatial/experimental/detail/algorithm/is_point_in_polygon.cuh +++ b/cpp/include/cuspatial/experimental/detail/algorithm/is_point_in_polygon.cuh @@ -16,6 +16,7 @@ #pragma once +#include "cuspatial/experimental/geometry_collection/multipoint_ref.cuh" #include #include @@ -48,8 +49,8 @@ __device__ inline bool is_point_in_polygon(vec_2d const& test_point, PolygonR auto b = last_segment.v2; bool y0_flag = b.y > test_point.y; bool y1_flag; - for (auto it = ring.point_begin(); it != ring.point_end(); ++it) { - vec_2d a = *it; + auto ring_points = multipoint_ref{ring.point_begin(), ring.point_end()}; + for (vec_2d a : ring_points) { // for each line segment, including the segment between the last and first vertex T run = b.x - a.x; T rise = b.y - a.y; From b136c0b3f1707e870af3c4fae2f2dbfebc3f89a5 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 8 Mar 2023 17:49:05 -0800 Subject: [PATCH 21/60] Apply suggestions from code review --- .../experimental/detail/algorithm/is_point_in_polygon.cuh | 3 +-- .../cuspatial/experimental/detail/geometry/polygon_ref.cuh | 2 +- .../cuspatial/experimental/detail/point_polygon_distance.cuh | 1 + .../experimental/detail/ranges/multipolygon_range.cuh | 3 +-- .../cuspatial/experimental/ranges/multipolygon_range.cuh | 5 +++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/algorithm/is_point_in_polygon.cuh b/cpp/include/cuspatial/experimental/detail/algorithm/is_point_in_polygon.cuh index 5ddf208d7..e9f43818d 100644 --- a/cpp/include/cuspatial/experimental/detail/algorithm/is_point_in_polygon.cuh +++ b/cpp/include/cuspatial/experimental/detail/algorithm/is_point_in_polygon.cuh @@ -16,7 +16,7 @@ #pragma once -#include "cuspatial/experimental/geometry_collection/multipoint_ref.cuh" +#include #include #include @@ -67,7 +67,6 @@ __device__ inline bool is_point_in_polygon(vec_2d const& test_point, PolygonR is_colinear = float_equal(run * rise_to_point, run_to_point * rise); if (is_colinear) { break; } - // y0_flag = a.y > test_point.y; y1_flag = a.y > test_point.y; if (y1_flag != y0_flag) { // Transform the following inequality to avoid division diff --git a/cpp/include/cuspatial/experimental/detail/geometry/polygon_ref.cuh b/cpp/include/cuspatial/experimental/detail/geometry/polygon_ref.cuh index 02a917cd4..3c1dfeb68 100644 --- a/cpp/include/cuspatial/experimental/detail/geometry/polygon_ref.cuh +++ b/cpp/include/cuspatial/experimental/detail/geometry/polygon_ref.cuh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh index b39ba6dec..110a16087 100644 --- a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh @@ -87,6 +87,7 @@ void __global__ pairwise_point_polygon_distance_kernel(MultiPointRange multipoin if (geometry_idx == MultiPolygonRange::INVALID_INDEX) continue; if (intersects[geometry_idx]) { + // Leading thread of the pair writes to the output if (multipolygons.is_first_point_of_multipolygon(idx, geometry_idx)) distances[geometry_idx] = T{0.0}; continue; diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh index 7e0e868bf..452d1be2e 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -224,7 +224,6 @@ multipolygon_range:: return multipolygon_range:: INVALID_INDEX; - auto part_idx = part_idx_from_ring_idx(ring_idx); return geometry_idx_from_part_idx(part_idx_from_ring_idx(ring_idx)); } diff --git a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh index 775c7dba4..7850a48db 100644 --- a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -100,6 +100,7 @@ class multipolygon_range { /// Return the iterator to the one past the last multipolygon in the range. CUSPATIAL_HOST_DEVICE auto end() { return multipolygon_end(); } + /// Given the index of a segment, return the geometry (multipolygon) index where the /// segment locates. /// Segment index is the index to the starting point of the segment. If the @@ -116,7 +117,7 @@ class multipolygon_range { template CUSPATIAL_HOST_DEVICE auto get_segment(IndexType segment_idx); - /// Returns `true` if `point_idx`th point is the first point of its + /// Returns `true` if `point_idx`th point is the first point of `geometry_idx`th /// multipolygon template CUSPATIAL_HOST_DEVICE bool is_first_point_of_multipolygon(IndexType1 point_idx, From 744f32f2e0febf59131ad274bd1282a3998a7b3f Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 8 Mar 2023 17:52:43 -0800 Subject: [PATCH 22/60] add docs --- .../cuspatial/experimental/detail/point_polygon_distance.cuh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh index 110a16087..a3395de97 100644 --- a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh @@ -43,6 +43,9 @@ namespace cuspatial { namespace detail { +/** + * @brief For each point in the multipoint, compute point-in-multipolygon in corresponding pair. + */ template struct point_in_multipolygon_test_functor { MultiPointRange multipoints; From bb6c63785a12cb42fd9124f1e9cbce64b97e9341 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 8 Mar 2023 17:53:19 -0800 Subject: [PATCH 23/60] style --- .../cuspatial/experimental/ranges/multipolygon_range.cuh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh index 7850a48db..b9cf666ea 100644 --- a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh @@ -100,7 +100,7 @@ class multipolygon_range { /// Return the iterator to the one past the last multipolygon in the range. CUSPATIAL_HOST_DEVICE auto end() { return multipolygon_end(); } - + /// Given the index of a segment, return the geometry (multipolygon) index where the /// segment locates. /// Segment index is the index to the starting point of the segment. If the From 8319e7c0f387ee86c16a5968d64d25976a0f2e02 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 10 Mar 2023 09:55:35 -0800 Subject: [PATCH 24/60] fix bug in PiP tests --- .../detail/algorithm/is_point_in_polygon.cuh | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/algorithm/is_point_in_polygon.cuh b/cpp/include/cuspatial/experimental/detail/algorithm/is_point_in_polygon.cuh index e9f43818d..c59322803 100644 --- a/cpp/include/cuspatial/experimental/detail/algorithm/is_point_in_polygon.cuh +++ b/cpp/include/cuspatial/experimental/detail/algorithm/is_point_in_polygon.cuh @@ -33,6 +33,13 @@ namespace detail { * See "Crossings test" section of http://erich.realtimerendering.com/ptinpoly/ * The improvement in addenda is also addopted to remove divisions in this kernel. * + * @tparam T type of coordinate + * @tparam PolygonRef polygon_ref type + * @param test_point point to test for point in polygon + * @param polygon polygon to test for point in polygon + * @return boolean to indicate if point is inside the polygon. + * `false` if point is on the edge of the polygon. + * * TODO: the ultimate goal of refactoring this as independent function is to remove * src/utility/point_in_polygon.cuh and its usage in quadtree_point_in_polygon.cu. It isn't * possible today without further work to refactor quadtree_point_in_polygon into header only @@ -104,10 +111,10 @@ __device__ inline bool is_point_in_polygon(Cart2d const& test_point, Cart2dIt poly_points_first, Cart2dItDiffType const& num_poly_points) { - auto polygon = polygon_ref{ring_offsets_first + poly_begin, - ring_offsets_first + poly_end, + auto polygon = polygon_ref{thrust::next(ring_offsets_first, poly_begin), + thrust::next(ring_offsets_first, poly_end + 1), poly_points_first, - poly_points_first + num_poly_points}; + thrust::next(poly_points_first, num_poly_points)}; return is_point_in_polygon(test_point, polygon); } From 86d36d9b36fa936d192f772ea88674f81812cf3c Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 14 Mar 2023 10:22:55 -0700 Subject: [PATCH 25/60] updates with test docs and API docs, address review --- .../geometry_collection/multipolygon_ref.cuh | 1 + .../experimental/point_polygon_distance.cuh | 1 + .../cuspatial_test/vector_factories.cuh | 4 +- .../spatial/point_polygon_distance_test.cu | 60 ++++++++++++++++++- 4 files changed, 62 insertions(+), 4 deletions(-) diff --git a/cpp/include/cuspatial/experimental/geometry_collection/multipolygon_ref.cuh b/cpp/include/cuspatial/experimental/geometry_collection/multipolygon_ref.cuh index 64da05797..9affb3fba 100644 --- a/cpp/include/cuspatial/experimental/geometry_collection/multipolygon_ref.cuh +++ b/cpp/include/cuspatial/experimental/geometry_collection/multipolygon_ref.cuh @@ -24,6 +24,7 @@ namespace cuspatial { * @brief Represent a reference to a multipolygon stored in a structure of arrays. * * @tparam PartIterator type of iterator to the part offset array. + * @tparam RingIterator type of iterator to the ring offset array. * @tparam VecIterator type of iterator to the underlying point array. */ template diff --git a/cpp/include/cuspatial/experimental/point_polygon_distance.cuh b/cpp/include/cuspatial/experimental/point_polygon_distance.cuh index a1a0093d9..6398531ef 100644 --- a/cpp/include/cuspatial/experimental/point_polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/point_polygon_distance.cuh @@ -27,6 +27,7 @@ namespace cuspatial { * @tparam MultiPointRange An instance of template type `MultiPointRange` * @tparam MultiPolygonRange An instance of template type `MultiPolygonRange` * @tparam OutputIt iterator type for output array. Must meet the requirements of [LRAI](LinkLRAI). + * Must be an iterator to type convertible from floating points. * * @param multipoints Range of multipoints, one per computed distance pair. * @param multipolygons Range of multilinestrings, one per computed distance pair. diff --git a/cpp/include/cuspatial_test/vector_factories.cuh b/cpp/include/cuspatial_test/vector_factories.cuh index 7747dfe33..9053ed2c0 100644 --- a/cpp/include/cuspatial_test/vector_factories.cuh +++ b/cpp/include/cuspatial_test/vector_factories.cuh @@ -73,10 +73,10 @@ class multipolygon_array { { } - /// Return the number of multilinestrings + /// Return the number of multipolygons auto size() { return _geometry_offsets_array.size() - 1; } - /// Return range object of the multilinestring array + /// Return range object of the multipolygon array auto range() { return multipolygon_range(_geometry_offsets_array.begin(), diff --git a/cpp/tests/experimental/spatial/point_polygon_distance_test.cu b/cpp/tests/experimental/spatial/point_polygon_distance_test.cu index ce5f78b23..64219b55f 100644 --- a/cpp/tests/experimental/spatial/point_polygon_distance_test.cu +++ b/cpp/tests/experimental/spatial/point_polygon_distance_test.cu @@ -62,6 +62,7 @@ using TestTypes = ::testing::Types; TYPED_TEST_CASE(PairwisePointPolygonDistanceTest, TestTypes); +// Inputs are empty columns TYPED_TEST(PairwisePointPolygonDistanceTest, ZeroPairs) { using T = TypeParam; @@ -76,6 +77,9 @@ TYPED_TEST(PairwisePointPolygonDistanceTest, ZeroPairs) std::initializer_list{}); } +// Point in 1 ring polygon. +// POINT (0 0) +// POLYGON ((-1 -1, 1, -1, 1 1, -1 1, -1 -1)) TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairOnePolygonOneRing) { using T = TypeParam; @@ -90,6 +94,9 @@ TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairOnePolygonOneRing) {0.0}); } +// Point outside 1 ring polygon. +// POINT (0 2) +// POLYGON ((-1 -1, 1 -1, 1 1, -1 1, -1 -1)) TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairOnePolygonOneRing2) { using T = TypeParam; @@ -104,6 +111,9 @@ TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairOnePolygonOneRing2) {1.0}); } +// Point in the hole. Polygon has two rings. Point in the hole. +// POINT (0 0) +// POLYGON ((-2 -2, 2 -2, 2 2, -2 2, -2 -2), (-1 -1, 1 -1, 1 1, -1 1, -1 -1)) TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairOnePolygonTwoRings) { using T = TypeParam; @@ -129,6 +139,9 @@ TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairOnePolygonTwoRings) {1.0}); } +// Point in polygon. Polygon has two rings. Point outside of polygon. +// POINT (1.5 0) +// POLYGON ((-2 -2, 2 -2, 2 2, -2 2, -2 -2), (-1 -1, 1 -1, 1 1, -1 1, -1 -1)) TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairOnePolygonTwoRings2) { using T = TypeParam; @@ -154,6 +167,9 @@ TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairOnePolygonTwoRings2) {0.0}); } +// Point outside of polygon. Polygon has two rings. Point outside of polygon. +// POINT (3 0) +// POLYGON ((-2 -2, 2 -2, 2 2, -2 2, -2 -2), (-1 -1, 1 -1, 1 1, -1 1, -1 -1)) TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairOnePolygonTwoRings3) { using T = TypeParam; @@ -179,6 +195,9 @@ TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairOnePolygonTwoRings3) {1.0}); } +// 1 Multipolygon with 2 Polygons. Point intersects with second polygon +// POINT (1 1) +// MULTIPOLYGON (((-2 -2, 0 -2, 0 0, -2 0, -2 -2)), ((0 0, 2 0, 2 2, 0 2, 0 0))) TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairTwoPolygonOneRing) { using T = TypeParam; @@ -204,6 +223,9 @@ TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairTwoPolygonOneRing) {0.0}); } +// 1 Multipolygon with 2 Polygons. Point intersects with first polygon. +// POINT (-1 -1) +// MULTIPOLYGON (((-2 -2, 0 -2, 0 0, -2 0, -2 -2)), ((0 0, 2 0, 2 2, 0 2, 0 0))) TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairTwoPolygonOneRing2) { using T = TypeParam; @@ -229,6 +251,9 @@ TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairTwoPolygonOneRing2) {0.0}); } +// 1 Multipolygon with 2 Polygons. Point does not intersect. Closer to first polygon. +// POINT (-1 0.5) +// MULTIPOLYGON (((-2 -2, 0 -2, 0 0, -2 0, -2 -2)), ((0 0, 2 0, 2 2, 0 2, 0 0))) TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairTwoPolygonOneRing3) { using T = TypeParam; @@ -254,6 +279,9 @@ TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairTwoPolygonOneRing3) {0.5}); } +// 1 Multipolygon with 2 Polygons. Point does not intersect. Closer to second polygon. +// POINT (-0.3, 1) +// MULTIPOLYGON (((-2 -2, 0 -2, 0 0, -2 0, -2 -2)), ((0 0, 2 0, 2 2, 0 2, 0 0))) TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairTwoPolygonOneRing4) { using T = TypeParam; @@ -279,6 +307,12 @@ TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairTwoPolygonOneRing4) {0.3}); } +// Two Pairs. +// POINT (-0.6 -0.6) +// POLYGON ((-1 -1, 0 0, 0 1, -1 -1)) +// +// POINT (0 0) +// POLYGON ((1 1, 1 0, 2 2, 1 1)) TYPED_TEST(PairwisePointPolygonDistanceTest, TwoPairOnePolygonOneRing) { using T = TypeParam; @@ -302,6 +336,12 @@ TYPED_TEST(PairwisePointPolygonDistanceTest, TwoPairOnePolygonOneRing) {0.0, 1.0}); } +// Two Pairs, each polygon has two rings. +// POINT (2.5, 3) +// POLYGON ((0 0, 3 0, 3 3, 0 3, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1)) +// +// POINT (-1.75, -1.5) +// POLYGON ((0 0, -3 0, -3 -3, 0 0), (-1 -1, -2 -1, -2 -2, -1 -1)) TYPED_TEST(PairwisePointPolygonDistanceTest, TwoPairTwoPolygonTwoRing) { using T = TypeParam; @@ -336,6 +376,19 @@ TYPED_TEST(PairwisePointPolygonDistanceTest, TwoPairTwoPolygonTwoRing) {0.0, 0.17677669529663687}); } +// Three Polygons +// POINT (1 1) +// POLYGON ((0 1, -1 -1, 1 -1, 0 1), (0 0.5, 0.5 -0.5, -0.5 -0.5, 0 0.5)) +// +// POINT (2 2) +// POLYGON ((1 1, 1 2, 2 1, 1 1)) +// +// POINT (1.5 0) +// POLYGON ( +// (-3 -3, 3 -3, 3 3, -3 3, -3 -3), +// (-2 -2, -1 -2, -1 2, -2 2, -2 -2), +// (2 2, 2 -2, 1 -2, 1 2, 2 2) +// ) TYPED_TEST(PairwisePointPolygonDistanceTest, ThreePolygons) { using T = TypeParam; @@ -383,6 +436,7 @@ TYPED_TEST(PairwisePointPolygonDistanceTest, ThreePolygons) {0.894427190999916, 0.7071067811865476, 0.5}); } +// Multipoint tests: 1 multipoint - 1 polygon. No Intersection. TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairMultiPointOnePolygon) { using T = TypeParam; @@ -397,6 +451,7 @@ TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairMultiPointOnePolygon) {1.3416407864998738}); } +// Multipoint tests: 1 multipoint - 1 polygon. Intesects. TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairMultiPointOnePolygon2) { using T = TypeParam; @@ -411,6 +466,7 @@ TYPED_TEST(PairwisePointPolygonDistanceTest, OnePairMultiPointOnePolygon2) {0.0}); } +// Multipoint tests: 2 multipoints - 2 polygons. TYPED_TEST(PairwisePointPolygonDistanceTest, TwoPairMultiPointOnePolygon2) { using T = TypeParam; @@ -418,10 +474,10 @@ TYPED_TEST(PairwisePointPolygonDistanceTest, TwoPairMultiPointOnePolygon2) CUSPATIAL_RUN_TEST( this->run_single, - {{P{0, 2}, P{0, 0}}, {P{1, 1}, P{-1, -1}}}, + {{P{0, 2}, P{3, 0}}, {P{1, 1}, P{-1, -1}}}, {0, 1, 2}, {0, 1, 2}, {0, 5, 9}, {P{-1, -1}, P{1, -1}, P{1, 1}, P{-1, 1}, P{-1, -1}, P{-1, 1}, P{1, 1}, P{0, -1}, P{-1, 1}}, - {0.0, 0.0}); + {1.0, 0.0}); } From 7bf8cbfa634ccfc1e83fcf76b79842cdfe01a13c Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 14 Mar 2023 10:48:41 -0700 Subject: [PATCH 26/60] update offsets_to_keys --- .../detail/utility/offset_to_keys.cuh | 51 ------------------- ...inestring_intersection_with_duplicates.cuh | 36 ++----------- .../detail/point_polygon_distance.cuh | 4 +- .../spatial/linestring_intersection_test.cu | 5 +- 4 files changed, 8 insertions(+), 88 deletions(-) delete mode 100644 cpp/include/cuspatial/detail/utility/offset_to_keys.cuh diff --git a/cpp/include/cuspatial/detail/utility/offset_to_keys.cuh b/cpp/include/cuspatial/detail/utility/offset_to_keys.cuh deleted file mode 100644 index 70665b4db..000000000 --- a/cpp/include/cuspatial/detail/utility/offset_to_keys.cuh +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2023, NVIDIA CORPORATION. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -/** @brief Given list offset and row `i`, return a unique key that represent the list of `i`. - * - * The key is computed by performing a `upper_bound` search with `i` in the offset array. - * Then subtracts the position with the start of offset array. - * - * Example: - * offset: 0 0 0 1 3 4 4 4 - * i: 0 1 2 3 - * key: 3 4 4 5 - * - * Note that the values of `key`, {offset[3], offset[4], offset[5]} denotes the ending - * position of the first 3 non-empty list. - */ -template -struct offsets_to_keys_functor { - Iterator _offsets_begin; - Iterator _offsets_end; - - offsets_to_keys_functor(Iterator offset_begin, Iterator offset_end) - : _offsets_begin(offset_begin), _offsets_end(offset_end) - { - } - - template - IndexType __device__ operator()(IndexType i) - { - return thrust::distance(_offsets_begin, - thrust::upper_bound(thrust::seq, _offsets_begin, _offsets_end, i)); - } -}; diff --git a/cpp/include/cuspatial/experimental/detail/linestring_intersection_with_duplicates.cuh b/cpp/include/cuspatial/experimental/detail/linestring_intersection_with_duplicates.cuh index dc3f4c5fd..a01ba0a63 100644 --- a/cpp/include/cuspatial/experimental/detail/linestring_intersection_with_duplicates.cuh +++ b/cpp/include/cuspatial/experimental/detail/linestring_intersection_with_duplicates.cuh @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -123,37 +124,6 @@ struct offsets_update_functor { } }; -/** @brief Given list offset and row `i`, return a unique key that represent the list of `i`. - * - * The key is computed by performing a `upper_bound` search with `i` in the offset array. - * Then subtracts the position with the start of offset array. - * - * Example: - * offset: 0 0 0 1 3 4 4 4 - * i: 0 1 2 3 - * key: 3 4 4 5 - * - * Note that the values of `key`, {offset[3], offset[4], offset[5]} denotes the ending - * position of the first 3 non-empty list. - */ -template -struct offsets_to_keys_functor { - Iterator _offsets_begin; - Iterator _offsets_end; - - offsets_to_keys_functor(Iterator offset_begin, Iterator offset_end) - : _offsets_begin(offset_begin), _offsets_end(offset_end) - { - } - - template - IndexType __device__ operator()(IndexType i) - { - return thrust::distance(_offsets_begin, - thrust::upper_bound(thrust::seq, _offsets_begin, _offsets_end, i)); - } -}; - } // namespace intersection_functors /// Internal structure to provide convenient access to the intersection intermediate id arrays. @@ -318,7 +288,7 @@ struct linestring_intersection_intermediates { rmm::device_uvector reduced_keys(num_pairs(), stream); rmm::device_uvector reduced_flags(num_pairs(), stream); auto keys_begin = make_counting_transform_iterator( - 0, intersection_functors::offsets_to_keys_functor{offsets->begin(), offsets->end()}); + 0, upper_bound_index_functor{offsets->begin(), offsets->end()}); auto [keys_end, flags_end] = thrust::reduce_by_key(rmm::exec_policy(stream), @@ -387,7 +357,7 @@ struct linestring_intersection_intermediates { auto keys_begin() { return make_counting_transform_iterator( - 0, intersection_functors::offsets_to_keys_functor{offsets->begin(), offsets->end()}); + 0, upper_bound_index_functor{offsets->begin(), offsets->end()}); } /// Return the number of pairs in the intermediates diff --git a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh index a3395de97..7207e3c19 100644 --- a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -139,7 +139,7 @@ OutputIt pairwise_point_polygon_distance(MultiPointRange multipoints, detail::zero_data_async(multipoint_intersects.begin(), multipoint_intersects.end(), stream); auto offset_as_key_it = detail::make_counting_transform_iterator( - 0, offsets_to_keys_functor{multipoints.offsets_begin(), multipoints.offsets_end()}); + 0, detail::upper_bound_index_functor{multipoints.offsets_begin(), multipoints.offsets_end()}); thrust::reduce_by_key(rmm::exec_policy(stream), offset_as_key_it, diff --git a/cpp/tests/experimental/spatial/linestring_intersection_test.cu b/cpp/tests/experimental/spatial/linestring_intersection_test.cu index 762a88c64..02769dc33 100644 --- a/cpp/tests/experimental/spatial/linestring_intersection_test.cu +++ b/cpp/tests/experimental/spatial/linestring_intersection_test.cu @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -83,8 +84,8 @@ linestring_intersection_result segment_sort_intersection_result( rmm::device_uvector geometry_collection_keys(num_geoms, stream); auto geometry_collection_keys_begin = detail::make_counting_transform_iterator( 0, - detail::intersection_functors::offsets_to_keys_functor{ - result.geometry_collection_offset->begin(), result.geometry_collection_offset->end()}); + detail::upper_bound_index_functor{result.geometry_collection_offset->begin(), + result.geometry_collection_offset->end()}); thrust::copy(rmm::exec_policy(stream), geometry_collection_keys_begin, geometry_collection_keys_begin + num_geoms, From ff48dc7d2b9a5b3bbeabe95e8756cc9cfbbf3ee7 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 14 Mar 2023 10:49:30 -0700 Subject: [PATCH 27/60] update floating_point docs --- cpp/include/cuspatial/detail/utility/floating_point.cuh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/include/cuspatial/detail/utility/floating_point.cuh b/cpp/include/cuspatial/detail/utility/floating_point.cuh index 7a44aeb73..362994e8b 100644 --- a/cpp/include/cuspatial/detail/utility/floating_point.cuh +++ b/cpp/include/cuspatial/detail/utility/floating_point.cuh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2022-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From b4420a73a651e2e9b29e3a73082458607c143be0 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 14 Mar 2023 11:08:49 -0700 Subject: [PATCH 28/60] use any_of --- .../detail/point_polygon_distance.cuh | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh index 7207e3c19..e4b71cb76 100644 --- a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -33,6 +34,7 @@ #include #include #include +#include #include #include @@ -48,6 +50,8 @@ namespace detail { */ template struct point_in_multipolygon_test_functor { + using T = typename MultiPointRange::element_t; + MultiPointRange multipoints; MultiPolygonRange multipolygons; @@ -59,13 +63,15 @@ struct point_in_multipolygon_test_functor { template uint8_t __device__ operator()(IndexType pidx) { - auto point = thrust::raw_reference_cast(multipoints.point(pidx)); - auto geometry_idx = multipoints.geometry_idx_from_point_idx(pidx); + vec_2d const& point = multipoints.point(pidx); + auto geometry_idx = multipoints.geometry_idx_from_point_idx(pidx); + + auto const& polys = multipolygons[geometry_idx]; + bool intersects = + thrust::any_of(thrust::seq, polys.begin(), polys.end(), [&point] __device__(auto poly) { + return is_point_in_polygon(point, poly); + }); - bool intersects = false; - for (auto polygon : multipolygons[geometry_idx]) { - intersects = intersects || is_point_in_polygon(point, polygon); - } return static_cast(intersects); } }; From 44e2843be533e117696c773c301fd7bb363a1166 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 14 Mar 2023 13:40:44 -0700 Subject: [PATCH 29/60] add large (multigrid) tests, address reviews --- .../detail/point_polygon_distance.cuh | 8 +-- .../detail/ranges/multilinestring_range.cuh | 2 +- .../detail/ranges/multipoint_range.cuh | 2 +- .../experimental/geometry/linestring_ref.cuh | 2 +- .../experimental/geometry/segment.cuh | 2 +- .../experimental/point_polygon_distance.cuh | 5 +- .../experimental/ranges/multipoint_range.cuh | 2 - .../ranges/multipolygon_range.cuh | 9 ++-- .../cuspatial_test/vector_factories.cuh | 19 ++++--- .../spatial/point_polygon_distance_test.cu | 50 +++++++++++++++++-- 10 files changed, 74 insertions(+), 27 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh index e4b71cb76..d5d12d3a2 100644 --- a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh @@ -67,6 +67,7 @@ struct point_in_multipolygon_test_functor { auto geometry_idx = multipoints.geometry_idx_from_point_idx(pidx); auto const& polys = multipolygons[geometry_idx]; + // TODO: benchmark against range based for loop bool intersects = thrust::any_of(thrust::seq, polys.begin(), polys.end(), [&point] __device__(auto poly) { return is_point_in_polygon(point, poly); @@ -96,14 +97,13 @@ void __global__ pairwise_point_polygon_distance_kernel(MultiPointRange multipoin if (geometry_idx == MultiPolygonRange::INVALID_INDEX) continue; if (intersects[geometry_idx]) { - // Leading thread of the pair writes to the output - if (multipolygons.is_first_point_of_multipolygon(idx, geometry_idx)) - distances[geometry_idx] = T{0.0}; + distances[geometry_idx] = T{0.0}; continue; } + auto [a, b] = multipolygons.get_segment(idx); + T dist_squared = std::numeric_limits::max(); - auto [a, b] = multipolygons.get_segment(idx); for (vec_2d point : multipoints[geometry_idx]) { dist_squared = min(dist_squared, point_to_segment_distance_squared(point, a, b)); } diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh index 851269313..5add3a769 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2022-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multipoint_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multipoint_range.cuh index f28ed5ef2..fa7d4002b 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multipoint_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multipoint_range.cuh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2022-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cpp/include/cuspatial/experimental/geometry/linestring_ref.cuh b/cpp/include/cuspatial/experimental/geometry/linestring_ref.cuh index 707b9e0cc..4d0092420 100644 --- a/cpp/include/cuspatial/experimental/geometry/linestring_ref.cuh +++ b/cpp/include/cuspatial/experimental/geometry/linestring_ref.cuh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2022-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cpp/include/cuspatial/experimental/geometry/segment.cuh b/cpp/include/cuspatial/experimental/geometry/segment.cuh index 8667e174c..214819694 100644 --- a/cpp/include/cuspatial/experimental/geometry/segment.cuh +++ b/cpp/include/cuspatial/experimental/geometry/segment.cuh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2022-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cpp/include/cuspatial/experimental/point_polygon_distance.cuh b/cpp/include/cuspatial/experimental/point_polygon_distance.cuh index 6398531ef..b2538a0ae 100644 --- a/cpp/include/cuspatial/experimental/point_polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/point_polygon_distance.cuh @@ -24,13 +24,14 @@ namespace cuspatial { * @ingroup distance * @brief Computes pairwise multipoint to multipolygon distance * - * @tparam MultiPointRange An instance of template type `MultiPointRange` - * @tparam MultiPolygonRange An instance of template type `MultiPolygonRange` + * @tparam MultiPointRange An instance of template type `multipoint_range` + * @tparam MultiPolygonRange An instance of template type `multipolygon_range` * @tparam OutputIt iterator type for output array. Must meet the requirements of [LRAI](LinkLRAI). * Must be an iterator to type convertible from floating points. * * @param multipoints Range of multipoints, one per computed distance pair. * @param multipolygons Range of multilinestrings, one per computed distance pair. + * @param stream The CUDA stream on which to perform computations * @return Output Iterator past the last distance computed * * [LinkLRAI]: https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator diff --git a/cpp/include/cuspatial/experimental/ranges/multipoint_range.cuh b/cpp/include/cuspatial/experimental/ranges/multipoint_range.cuh index 1e2ac7c42..5cc06f62b 100644 --- a/cpp/include/cuspatial/experimental/ranges/multipoint_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multipoint_range.cuh @@ -51,8 +51,6 @@ class multipoint_range { using point_t = iterator_value_type; using element_t = iterator_vec_base_type; - int32_t INVALID_IDX = -1; - /** * @brief Construct a new multipoint array object */ diff --git a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh index b9cf666ea..2e05066ed 100644 --- a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh @@ -101,11 +101,10 @@ class multipolygon_range { /// Return the iterator to the one past the last multipolygon in the range. CUSPATIAL_HOST_DEVICE auto end() { return multipolygon_end(); } - /// Given the index of a segment, return the geometry (multipolygon) index where the - /// segment locates. - /// Segment index is the index to the starting point of the segment. If the - /// index is the last point of the ring, then it is not a valid index. - /// This function returns multipolygon_range::INVALID_INDEX if the index is invalid. + /// Given the index of a segment, return the index of the geometry (multipolygon) that contains + /// the segment. Segment index is the index to the starting point of the segment. If the index is + /// the last point of the ring, then it is not a valid index. This function returns + /// multipolygon_range::INVALID_INDEX if the index is invalid. template CUSPATIAL_HOST_DEVICE auto geometry_idx_from_segment_idx(IndexType segment_idx); diff --git a/cpp/include/cuspatial_test/vector_factories.cuh b/cpp/include/cuspatial_test/vector_factories.cuh index 9053ed2c0..d4887e6e4 100644 --- a/cpp/include/cuspatial_test/vector_factories.cuh +++ b/cpp/include/cuspatial_test/vector_factories.cuh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2022-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,13 @@ namespace cuspatial { namespace test { +template +auto make_device_vector(Range rng) +{ + using T = typename Range::value_type; + return rmm::device_vector(rng.begin(), rng.end()); +} + template auto make_device_vector(std::initializer_list inl) { @@ -96,11 +103,11 @@ class multipolygon_array { CoordinateArray _coordinate_offsets_array; }; -template -auto make_multipolygon_array(std::initializer_list geometry_inl, - std::initializer_list part_inl, - std::initializer_list ring_inl, - std::initializer_list> coord_inl) +template +auto make_multipolygon_array(IndexRange geometry_inl, + IndexRange part_inl, + IndexRange ring_inl, + CoordRange coord_inl) { return multipolygon_array{make_device_vector(geometry_inl), make_device_vector(part_inl), diff --git a/cpp/tests/experimental/spatial/point_polygon_distance_test.cu b/cpp/tests/experimental/spatial/point_polygon_distance_test.cu index 64219b55f..bec557c4f 100644 --- a/cpp/tests/experimental/spatial/point_polygon_distance_test.cu +++ b/cpp/tests/experimental/spatial/point_polygon_distance_test.cu @@ -17,7 +17,9 @@ #include #include +#include #include +#include #include #include @@ -29,6 +31,8 @@ using namespace cuspatial; using namespace cuspatial::test; +double constexpr PI = 3.14159265358979323846; + template struct PairwisePointPolygonDistanceTest : public ::testing::Test { rmm::cuda_stream_view stream() { return rmm::cuda_stream_default; } @@ -40,12 +44,29 @@ struct PairwisePointPolygonDistanceTest : public ::testing::Test { std::initializer_list multipolygon_ring_offsets, std::initializer_list> multipolygon_coordinates, std::initializer_list expected) + { + std::vector> multipolygon_coordinates_vec(multipolygon_coordinates); + return this->run_single(multipoints, + multipolygon_geometry_offsets, + multipolygon_part_offsets, + multipolygon_ring_offsets, + multipolygon_coordinates_vec, + expected); + } + + void run_single(std::initializer_list>> multipoints, + std::initializer_list multipolygon_geometry_offsets, + std::initializer_list multipolygon_part_offsets, + std::initializer_list multipolygon_ring_offsets, + std::vector> const& multipolygon_coordinates, + std::initializer_list expected) { auto d_multipoints = make_multipoints_array(multipoints); - auto d_multipolygons = make_multipolygon_array(multipolygon_geometry_offsets, - multipolygon_part_offsets, - multipolygon_ring_offsets, - multipolygon_coordinates); + auto d_multipolygons = make_multipolygon_array( + range{multipolygon_geometry_offsets.begin(), multipolygon_geometry_offsets.end()}, + range{multipolygon_part_offsets.begin(), multipolygon_part_offsets.end()}, + range{multipolygon_ring_offsets.begin(), multipolygon_ring_offsets.end()}, + range{multipolygon_coordinates.begin(), multipolygon_coordinates.end()}); auto got = rmm::device_uvector(d_multipoints.size(), stream()); @@ -481,3 +502,24 @@ TYPED_TEST(PairwisePointPolygonDistanceTest, TwoPairMultiPointOnePolygon2) {P{-1, -1}, P{1, -1}, P{1, 1}, P{-1, 1}, P{-1, -1}, P{-1, 1}, P{1, 1}, P{0, -1}, P{-1, 1}}, {1.0, 0.0}); } + +// Large distance test +TYPED_TEST(PairwisePointPolygonDistanceTest, DistanceTestManyVertex) +{ + using T = TypeParam; + using P = vec_2d; + + std::size_t num_vertex = 2000; + P centroid{0.0, 0.0}; + T radius = 1.0; + + std::vector

polygon; + auto it = detail::make_counting_transform_iterator(0, [](auto i) { + T theta = i / (2 * PI); + return P{cos(theta), sin(theta)}; + }); + std::copy(it, it + num_vertex, std::back_inserter(polygon)); + + CUSPATIAL_RUN_TEST( + this->run_single, {{P{0.0, 0.0}}}, {0, 1}, {0, 1}, {0, num_vertex + 1}, polygon, {0.0}); +} From 49972d4d213fa295387cfe482b78392aacb3d2d5 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 14 Mar 2023 18:04:18 -0700 Subject: [PATCH 30/60] add upper_bound_index.cuh --- .../detail/utility/upper_bound_index.cuh | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 cpp/include/cuspatial/detail/utility/upper_bound_index.cuh diff --git a/cpp/include/cuspatial/detail/utility/upper_bound_index.cuh b/cpp/include/cuspatial/detail/utility/upper_bound_index.cuh new file mode 100644 index 000000000..048b76edf --- /dev/null +++ b/cpp/include/cuspatial/detail/utility/upper_bound_index.cuh @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +namespace cuspatial { +namespace detail { + +/** @brief Returns the index of the first element in the range `[first, last)` such that + * `value < element` is true (i.e. strictly greater), or `distance(first, last)` if no such element + * is found. + * + * A common use of the functor is to apply `*_by_key` algorithm to list values. The keys + * iterator can be constructed with `upper_bound_index_functor{offsets.begin(), offsets.end()}`. + * + * Example: + * ``` + * offset: 0 0 0 1 3 4 4 4 + * i: 0 1 2 3 + * key: 3 4 4 5 + * ``` + */ +template +struct upper_bound_index_functor { + Iterator _offsets_begin; + Iterator _offsets_end; + + upper_bound_index_functor(Iterator offset_begin, Iterator offset_end) + : _offsets_begin(offset_begin), _offsets_end(offset_end) + { + } + + template + IndexType __device__ operator()(IndexType i) + { + return thrust::distance(_offsets_begin, + thrust::upper_bound(thrust::seq, _offsets_begin, _offsets_end, i)); + } +}; + +} // namespace detail +} // namespace cuspatial From f000e00585be9bf00c0f831160c94d49d13ca379 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 14 Mar 2023 22:52:16 -0700 Subject: [PATCH 31/60] remove upper_bound_index --- .../detail/utility/upper_bound_index.cuh | 58 ------------------- ...inestring_intersection_with_duplicates.cuh | 11 +--- .../detail/point_polygon_distance.cuh | 9 +-- .../spatial/linestring_intersection_test.cu | 8 +-- 4 files changed, 11 insertions(+), 75 deletions(-) delete mode 100644 cpp/include/cuspatial/detail/utility/upper_bound_index.cuh diff --git a/cpp/include/cuspatial/detail/utility/upper_bound_index.cuh b/cpp/include/cuspatial/detail/utility/upper_bound_index.cuh deleted file mode 100644 index 048b76edf..000000000 --- a/cpp/include/cuspatial/detail/utility/upper_bound_index.cuh +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2023, NVIDIA CORPORATION. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -namespace cuspatial { -namespace detail { - -/** @brief Returns the index of the first element in the range `[first, last)` such that - * `value < element` is true (i.e. strictly greater), or `distance(first, last)` if no such element - * is found. - * - * A common use of the functor is to apply `*_by_key` algorithm to list values. The keys - * iterator can be constructed with `upper_bound_index_functor{offsets.begin(), offsets.end()}`. - * - * Example: - * ``` - * offset: 0 0 0 1 3 4 4 4 - * i: 0 1 2 3 - * key: 3 4 4 5 - * ``` - */ -template -struct upper_bound_index_functor { - Iterator _offsets_begin; - Iterator _offsets_end; - - upper_bound_index_functor(Iterator offset_begin, Iterator offset_end) - : _offsets_begin(offset_begin), _offsets_end(offset_end) - { - } - - template - IndexType __device__ operator()(IndexType i) - { - return thrust::distance(_offsets_begin, - thrust::upper_bound(thrust::seq, _offsets_begin, _offsets_end, i)); - } -}; - -} // namespace detail -} // namespace cuspatial diff --git a/cpp/include/cuspatial/experimental/detail/linestring_intersection_with_duplicates.cuh b/cpp/include/cuspatial/experimental/detail/linestring_intersection_with_duplicates.cuh index f917927c5..c56c4bfbe 100644 --- a/cpp/include/cuspatial/experimental/detail/linestring_intersection_with_duplicates.cuh +++ b/cpp/include/cuspatial/experimental/detail/linestring_intersection_with_duplicates.cuh @@ -16,11 +16,11 @@ #include #include -#include #include #include #include #include +#include #include #include @@ -287,8 +287,7 @@ struct linestring_intersection_intermediates { // Use `reduce_by_key` to compute the number of removed geometry per list. rmm::device_uvector reduced_keys(num_pairs(), stream); rmm::device_uvector reduced_flags(num_pairs(), stream); - auto keys_begin = make_counting_transform_iterator( - 0, upper_bound_index_functor{offsets->begin(), offsets->end()}); + auto keys_begin = make_geometry_id_iterator(offsets->begin(), offsets->end()); auto [keys_end, flags_end] = thrust::reduce_by_key(rmm::exec_policy(stream), @@ -354,11 +353,7 @@ struct linestring_intersection_intermediates { } /// Return list-id corresponding to the geometry - auto keys_begin() - { - return make_counting_transform_iterator( - 0, upper_bound_index_functor{offsets->begin(), offsets->end()}); - } + auto keys_begin() { return make_geometry_id_iterator(offsets->begin(), offsets->end()); } /// Return the number of pairs in the intermediates auto num_pairs() { return offsets->size() - 1; } diff --git a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh index d5d12d3a2..704f67a6b 100644 --- a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh @@ -20,10 +20,10 @@ #include #include #include -#include #include #include #include +#include #include #include @@ -120,7 +120,8 @@ OutputIt pairwise_point_polygon_distance(MultiPointRange multipoints, OutputIt distances_first, rmm::cuda_stream_view stream) { - using T = typename MultiPointRange::element_t; + using T = typename MultiPointRange::element_t; + using index_t = typename MultiPointRange::index_t; CUSPATIAL_EXPECTS(multipoints.size() == multipolygons.size(), "Must have the same number of input rows."); @@ -144,8 +145,8 @@ OutputIt pairwise_point_polygon_distance(MultiPointRange multipoints, rmm::device_uvector multipoint_intersects(multipoints.num_multipoints(), stream); detail::zero_data_async(multipoint_intersects.begin(), multipoint_intersects.end(), stream); - auto offset_as_key_it = detail::make_counting_transform_iterator( - 0, detail::upper_bound_index_functor{multipoints.offsets_begin(), multipoints.offsets_end()}); + auto offset_as_key_it = + make_geometry_id_iterator(multipoints.offsets_begin(), multipoints.offsets_end()); thrust::reduce_by_key(rmm::exec_policy(stream), offset_as_key_it, diff --git a/cpp/tests/experimental/spatial/linestring_intersection_test.cu b/cpp/tests/experimental/spatial/linestring_intersection_test.cu index a2956fe76..0ec93957f 100644 --- a/cpp/tests/experimental/spatial/linestring_intersection_test.cu +++ b/cpp/tests/experimental/spatial/linestring_intersection_test.cu @@ -20,7 +20,6 @@ #include #include -#include #include #include #include @@ -82,10 +81,9 @@ linestring_intersection_result segment_sort_intersection_result( // Compute keys for each row in the union column. Rows of the same list // are assigned the same label. rmm::device_uvector geometry_collection_keys(num_geoms, stream); - auto geometry_collection_keys_begin = detail::make_counting_transform_iterator( - 0, - detail::upper_bound_index_functor{result.geometry_collection_offset->begin(), - result.geometry_collection_offset->end()}); + auto geometry_collection_keys_begin = make_geometry_id_iterator( + result.geometry_collection_offset->begin(), result.geometry_collection_offset->end()); + thrust::copy(rmm::exec_policy(stream), geometry_collection_keys_begin, geometry_collection_keys_begin + num_geoms, From 57504e6e4226b7b66d46dd60647f48f65329680d Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 15 Mar 2023 08:39:26 -0700 Subject: [PATCH 32/60] update geometry id factory --- cpp/include/cuspatial/experimental/iterator_factory.cuh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cpp/include/cuspatial/experimental/iterator_factory.cuh b/cpp/include/cuspatial/experimental/iterator_factory.cuh index 4f1280e9a..67c35e3fc 100644 --- a/cpp/include/cuspatial/experimental/iterator_factory.cuh +++ b/cpp/include/cuspatial/experimental/iterator_factory.cuh @@ -164,9 +164,8 @@ struct index_to_geometry_id { CUSPATIAL_HOST_DEVICE auto operator()(IndexT idx) { - return thrust::distance( - geometry_begin, - thrust::prev(thrust::upper_bound(thrust::seq, geometry_begin, geometry_end, idx))); + return thrust::distance(geometry_begin, + thrust::upper_bound(thrust::seq, geometry_begin, geometry_end, idx)); } }; From 48659221fa7d8ff5eb3d68133d63cd53802a7b55 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 15 Mar 2023 09:21:52 -0700 Subject: [PATCH 33/60] add get gtest scripts in cmake --- cpp/CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 8bd8958bc..f9bf49ad9 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -103,7 +103,10 @@ include(cmake/Modules/ConfigureCUDA.cmake) rapids_cpm_init() # find or add cuDF include(cmake/thirdparty/CUSPATIAL_GetCUDF.cmake) - +# find or install GoogleTest +if (CUSPATIAL_BUILD_TESTS) + include(cmake/thirdparty/get_gtests.cmake) +endif() ################################################################################################### # - library targets ------------------------------------------------------------------------------- From eff0e436b5ac126f73f65aee41c1a82b7fb0e8e0 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 16 Mar 2023 19:08:39 -0700 Subject: [PATCH 34/60] add generators and generator tests --- .../pairwise_point_polygon_distance.cu | 85 +++++++++ .../cuspatial_test/geometry_generator.cuh | 180 ++++++++++++++++++ .../cuspatial_test/vector_factories.cuh | 101 +++++++++- cpp/tests/CMakeLists.txt | 1 + .../utility_test/test_float_equivalent.cu | 16 ++ .../utility_test/test_geometry_generators.cu | 76 ++++++++ 6 files changed, 450 insertions(+), 9 deletions(-) create mode 100644 cpp/benchmarks/pairwise_point_polygon_distance.cu create mode 100644 cpp/include/cuspatial_test/geometry_generator.cuh create mode 100644 cpp/tests/utility_test/test_geometry_generators.cu diff --git a/cpp/benchmarks/pairwise_point_polygon_distance.cu b/cpp/benchmarks/pairwise_point_polygon_distance.cu new file mode 100644 index 000000000..476f45bc3 --- /dev/null +++ b/cpp/benchmarks/pairwise_point_polygon_distance.cu @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utility/geometry_factory.cuh" + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +using namespace cuspatial; + +template +void pairwise_point_polygon_distance_benchmark(nvbench::state& state, nvbench::type_list) +{ + // TODO: to be replaced by nvbench fixture once it's ready + cuspatial::rmm_pool_raii rmm_pool; + + auto const num_string_pairs{state.get_int64("NumStrings")}; + auto const num_segments_per_string{state.get_int64("NumSegmentsPerString")}; + + auto [ls1, ls1_offset] = + generate_linestring(num_string_pairs, num_segments_per_string, 1, {0, 0}); + auto [ls2, ls2_offset] = + generate_linestring(num_string_pairs, num_segments_per_string, 1, {100, 100}); + + auto distances = rmm::device_vector(ls1.size()); + auto out_it = distances.begin(); + + auto multilinestrings1 = make_multilinestring_range(1, + thrust::make_counting_iterator(0), + num_string_pairs, + ls1_offset.begin(), + ls1.size(), + ls1.begin()); + auto multilinestrings2 = make_multilinestring_range(1, + thrust::make_counting_iterator(0), + num_string_pairs, + ls2_offset.begin(), + ls2.size(), + ls2.begin()); + auto const total_points = ls1.size() + ls2.size(); + + state.add_element_count(num_string_pairs, "LineStringPairs"); + state.add_element_count(total_points, "NumPoints"); + state.add_global_memory_reads(total_points * 2, "CoordinatesDataSize"); + state.add_global_memory_reads(num_string_pairs * 2, "OffsetsDataSize"); + state.add_global_memory_writes(num_string_pairs); + + state.exec(nvbench::exec_tag::sync, + [&multilinestrings1, &multilinestrings2, &out_it](nvbench::launch& launch) { + pairwise_linestring_distance(multilinestrings1, multilinestrings2, out_it); + }); +} + +using floating_point_types = nvbench::type_list; +NVBENCH_BENCH_TYPES(pairwise_linestring_distance_benchmark, NVBENCH_TYPE_AXES(floating_point_types)) + .set_type_axes_names({"CoordsType"}) + .add_int64_axis("NumStrings", {1'000, 10'000, 100'000}) + .add_int64_axis("NumSegmentsPerString", {10, 100, 1'000}); diff --git a/cpp/include/cuspatial_test/geometry_generator.cuh b/cpp/include/cuspatial_test/geometry_generator.cuh new file mode 100644 index 000000000..758ed6ef2 --- /dev/null +++ b/cpp/include/cuspatial_test/geometry_generator.cuh @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include +#include +#include + +#include +#include + +namespace cuspatial { +namespace test { + +constexpr double PI = 3.14159265358979323846; + +template +struct multipolygon_generator_parameter { + using element_t = T; + + std::size_t num_multipolygons; + std::size_t num_polygons_per_multipolygon; + std::size_t num_holes_per_polygon; + std::size_t num_sides_per_ring; + vec_2d centroid; + T radius; + + std::size_t num_polygons() { return num_multipolygons * num_polygons_per_multipolygon; } + std::size_t num_rings() { return num_polygons() * num_rings_per_polygon(); } + std::size_t num_coords() { return num_rings() * num_vertices_per_ring(); } + std::size_t num_vertices_per_ring() { return num_sides_per_ring + 1; } + std::size_t num_rings_per_polygon() { return num_holes_per_polygon + 1; } +}; + +template +VecIterator generate_ring(std::size_t num_sides, + vec_2d centroid, + T radius, + VecIterator points_it, + rmm::cuda_stream_view stream) +{ + std::size_t num_vertices = num_sides + 1; + std::cout << "generate_ring: \n num_vertices: " << num_vertices << std::endl; + thrust::tabulate( + rmm::exec_policy(stream), + points_it, + points_it + num_vertices, + [num_sides, centroid, radius] __device__(int32_t i) { + // Overrides last coordinate to make sure ring is closed. + if (i == num_sides) return vec_2d{centroid.x + radius, centroid.y}; + + T angle = 2.0 * PI * i / num_sides; + return vec_2d{centroid.x + radius * cos(angle), centroid.y + radius * sin(angle)}; + }); + + std::cout << "Done generate_ring: \n" << std::endl; + + return points_it + num_vertices; +} + +template +VecIterator generate_polygon(std::size_t num_sides_per_ring, + std::size_t num_holes, + vec_2d centroid, + T radius, + VecIterator points_it, + rmm::cuda_stream_view stream) +{ + std::cout << "generate_polygon: \n" << std::endl; + // make shell + points_it = generate_ring(num_sides_per_ring, centroid, radius, points_it, stream); + + // Align hole centroid to the horizontal axis of the polygon centroid + T hole_radius = radius / num_holes; + T max_hole_displacement = radius - hole_radius; + + // make hole + for (std::size_t i = 0; i < num_holes; ++i) { + T displacement_x = -max_hole_displacement + i * hole_radius * 2; + T displacement_y = 0.0; + vec_2d hole_centroid = centroid + vec_2d{displacement_x, displacement_y}; + points_it = generate_ring(num_sides_per_ring, hole_centroid, hole_radius, points_it, stream); + } + + return points_it; +} + +template +VecIterator generate_multipolygon(std::size_t num_polygons, + std::size_t num_sides_per_ring, + std::size_t num_holes, + vec_2d origin, + T radius, + VecIterator points_it, + rmm::cuda_stream_view stream) +{ + std::cout << "generate_multipolygon: \n" << std::endl; + for (std::size_t i = 0; i < num_polygons; ++i) { + vec_2d centroid = origin + vec_2d{i * radius * 3, 0}; + points_it = + generate_polygon(num_sides_per_ring, num_holes, centroid, radius, points_it, stream); + } + + return points_it; +} + +/** + * @brief Helper to generate multipolygon arrays used for benchmarks. + * + * @tparam T The floating point type for the coordinates + * @param stream The CUDA stream to use for device memory operations and kernel launches + * @return A tuple of x and y coordinates of points and offsets to which the first point + * of each linestring starts. + */ +template +auto generate_multipolygon_array(multipolygon_generator_parameter params, + rmm::cuda_stream_view stream) +{ + std::cout << "Num_coords: " << params.num_coords() << std::endl; + rmm::device_uvector> coordinates(params.num_coords(), stream); + auto points_it = coordinates.begin(); + + for (std::size_t i = 0; i < params.num_multipolygons; ++i) { + points_it = generate_multipolygon(params.num_polygons_per_multipolygon, + params.num_sides_per_ring, + params.num_holes_per_polygon, + params.centroid, + params.radius, + points_it, + stream); + } + + rmm::device_uvector ring_offsets(params.num_rings() + 1, stream); + rmm::device_uvector part_offsets(params.num_polygons() + 1, stream); + rmm::device_uvector geometry_offsets(params.num_multipolygons + 1, stream); + + thrust::sequence(rmm::exec_policy(stream), + ring_offsets.begin(), + ring_offsets.end(), + std::size_t{0}, + params.num_vertices_per_ring()); + + thrust::sequence(rmm::exec_policy(stream), + part_offsets.begin(), + part_offsets.end(), + std::size_t{0}, + params.num_rings_per_polygon()); + + std::cout << "num_polygons_per_multipolygon:" << params.num_polygons_per_multipolygon + << std::endl; + thrust::sequence(rmm::exec_policy(stream), + geometry_offsets.begin(), + geometry_offsets.end(), + std::size_t{0}, + params.num_polygons_per_multipolygon); + + return make_multipolygon_array_from_uvector>(std::move(geometry_offsets), + std::move(part_offsets), + std::move(ring_offsets), + std::move(coordinates)); +} + +} // namespace test +} // namespace cuspatial diff --git a/cpp/include/cuspatial_test/vector_factories.cuh b/cpp/include/cuspatial_test/vector_factories.cuh index 18b670444..89077b216 100644 --- a/cpp/include/cuspatial_test/vector_factories.cuh +++ b/cpp/include/cuspatial_test/vector_factories.cuh @@ -14,6 +14,8 @@ * limitations under the License. */ +#include + #include #include #include @@ -21,10 +23,16 @@ #include #include +#include +#include #include +#include +#include + #include #include +#include #include namespace cuspatial { @@ -69,10 +77,15 @@ auto make_device_uvector(std::initializer_list inl, template class multipolygon_array { public: - multipolygon_array(GeometryArray geometry_offsets_array, - PartArray part_offsets_array, - RingArray ring_offsets_array, - CoordinateArray coordinate_offsets_array) + using geometry_t = typename GeometryArray::value_type; + using part_t = typename PartArray::value_type; + using ring_t = typename RingArray::value_type; + using coord_t = typename CoordinateArray::value_type; + + multipolygon_array(thrust::device_vector geometry_offsets_array, + thrust::device_vector part_offsets_array, + thrust::device_vector ring_offsets_array, + thrust::device_vector coordinate_offsets_array) : _geometry_offsets_array(geometry_offsets_array), _part_offsets_array(part_offsets_array), _ring_offsets_array(ring_offsets_array), @@ -80,6 +93,17 @@ class multipolygon_array { { } + multipolygon_array(rmm::device_uvector&& geometry_offsets_array, + rmm::device_uvector&& part_offsets_array, + rmm::device_uvector&& ring_offsets_array, + rmm::device_uvector&& coordinate_offsets_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)), + _coordinate_offsets_array(std::move(coordinate_offsets_array)) + { + } + /// Return the number of multipolygons auto size() { return _geometry_offsets_array.size() - 1; } @@ -96,6 +120,31 @@ class multipolygon_array { _coordinate_offsets_array.end()); } + auto to_host() const + { + auto geometry_offsets = cuspatial::test::to_host(_geometry_offsets_array); + auto part_offsets = cuspatial::test::to_host(_part_offsets_array); + auto ring_offsets = cuspatial::test::to_host(_ring_offsets_array); + auto coordinate_offsets = cuspatial::test::to_host(_coordinate_offsets_array); + + return std::tuple{geometry_offsets, part_offsets, ring_offsets, coordinate_offsets}; + } + + /** + * @brief Output stream operator for `multipolygon_array` for human-readable formatting + */ + friend std::ostream& operator<<( + std::ostream& os, + multipolygon_array const& arr) + { + auto [geometry_offsets, part_offsets, ring_offsets, coordinates] = arr.to_host(); + + return os << "Geometry Offsets:\n\t{" << geometry_offsets << "}\n" + << "Part Offsets:\n\t{" << part_offsets << "}\n" + << "Ring Offsets: \n\t{" << ring_offsets << "}\n" + << "Coordinates: \n\t{" << coordinates << "}\n"; + } + protected: GeometryArray _geometry_offsets_array; PartArray _part_offsets_array; @@ -103,16 +152,50 @@ class multipolygon_array { CoordinateArray _coordinate_offsets_array; }; -template +template auto make_multipolygon_array(IndexRange geometry_inl, IndexRange part_inl, IndexRange ring_inl, CoordRange coord_inl) { - return multipolygon_array{make_device_vector(geometry_inl), - make_device_vector(part_inl), - make_device_vector(ring_inl), - make_device_vector(coord_inl)}; + using CoordType = typename CoordRange::value_type; + using DeviceIndexVector = thrust::device_vector; + using DeviceCoordVector = thrust::device_vector; + + return multipolygon_array(make_device_vector(geometry_inl), + make_device_vector(part_inl), + make_device_vector(ring_inl), + make_device_vector(coord_inl)); +} + +template +auto make_multipolygon_array(std::initializer_list geometry_inl, + std::initializer_list part_inl, + std::initializer_list ring_inl, + std::initializer_list> coord_inl) +{ + return make_multipolygon_array(range(geometry_inl.begin(), geometry_inl.end()), + range(part_inl.begin(), part_inl.end()), + range(ring_inl.begin(), ring_inl.end()), + range(coord_inl.begin(), coord_inl.end())); +} + +template +auto make_multipolygon_array_from_uvector(rmm::device_uvector geometry_inl, + rmm::device_uvector part_inl, + rmm::device_uvector ring_inl, + rmm::device_uvector coord_inl) +{ + return multipolygon_array, + rmm::device_uvector, + rmm::device_uvector, + rmm::device_uvector>( + std::move(geometry_inl), std::move(part_inl), std::move(ring_inl), std::move(coord_inl)); } /** diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 66c99bf7e..bfd334d80 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -113,6 +113,7 @@ ConfigureTest(SPATIAL_WINDOW_POINT_TEST ConfigureTest(UTILITY_TEST utility_test/test_float_equivalent.cu utility_test/test_multipoint_factory.cu + utility_test/test_geometry_generators.cu ) # Experimental API diff --git a/cpp/tests/utility_test/test_float_equivalent.cu b/cpp/tests/utility_test/test_float_equivalent.cu index b88a7c811..d92de9299 100644 --- a/cpp/tests/utility_test/test_float_equivalent.cu +++ b/cpp/tests/utility_test/test_float_equivalent.cu @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2022, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #include #include diff --git a/cpp/tests/utility_test/test_geometry_generators.cu b/cpp/tests/utility_test/test_geometry_generators.cu new file mode 100644 index 000000000..1df09157b --- /dev/null +++ b/cpp/tests/utility_test/test_geometry_generators.cu @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include + +#include + +#include + +using namespace cuspatial; +using namespace cuspatial::test; + +template +struct GeometryFactoryTest : public BaseFixture { + rmm::cuda_stream_view stream() { return rmm::cuda_stream_default; } + + template + void run(multipolygon_generator_parameter params, MultipolygonArray expected) + { + auto got = generate_multipolygon_array(params, stream()); + + std::cout << "done generate multipolygon array" << std::endl; + + stream().synchronize(); + + std::cout << "done sync" << std::endl; + + auto [got_geometry_offsets, got_part_offsets, got_ring_offsets, got_coordinates] = + got.to_host(); + + std::cout << "done sync to host" << std::endl; + + auto [expected_geometry_offsets, + expected_part_offsets, + expected_ring_offsets, + expected_coordinates] = expected.to_host(); + + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(expected_geometry_offsets, got_geometry_offsets); + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(expected_part_offsets, got_part_offsets); + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(expected_ring_offsets, got_ring_offsets); + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(expected_coordinates, got_coordinates); + } +}; + +using TestTypes = ::testing::Types; + +TYPED_TEST_CASE(GeometryFactoryTest, TestTypes); + +TYPED_TEST(GeometryFactoryTest, multipolygonarray_basic) +{ + using T = TypeParam; + using P = vec_2d; + + auto params = multipolygon_generator_parameter{1, 1, 0, 3, {0.0, 0.0}, 1.0}; + auto expected = make_multipolygon_array( + {0, 1}, + {0, 1}, + {0, 4}, + {P{1.0, 0.0}, P{-0.5, 0.8660254037844386}, P{-0.5, -0.8660254037844386}, P{1.0, 0.0}}); + + CUSPATIAL_RUN_TEST(this->run, params, std::move(expected)); +} From 6d14d698088dec35aa48dcbe36064d8ae9b0ba7b Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 17 Mar 2023 15:46:54 -0700 Subject: [PATCH 35/60] add parameterized test fixture --- cpp/include/cuspatial_test/base_fixture.hpp | 59 +++- .../cuspatial_test/geometry_generator.cuh | 2 +- .../cuspatial_test/vector_equality.hpp | 5 +- .../utility_test/test_geometry_generators.cu | 281 +++++++++++++++++- 4 files changed, 321 insertions(+), 26 deletions(-) diff --git a/cpp/include/cuspatial_test/base_fixture.hpp b/cpp/include/cuspatial_test/base_fixture.hpp index 1dc4d4b30..fb8b3876a 100644 --- a/cpp/include/cuspatial_test/base_fixture.hpp +++ b/cpp/include/cuspatial_test/base_fixture.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2022-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include #include @@ -21,16 +22,10 @@ namespace cuspatial { namespace test { -/** - * @brief Base test fixture class from which all libcuspatial tests should inherit. - * - * Example: - * ``` - * class MyTestFixture : public cuspatial::test::BaseFixture {}; - * ``` - */ -class BaseFixture : public ::testing::Test { +template +class RMMResourceMixin { rmm::mr::device_memory_resource* _mr{rmm::mr::get_current_device_resource()}; + rmm::cuda_stream_view _stream{rmm::cuda_stream_default}; public: /** @@ -39,6 +34,50 @@ class BaseFixture : public ::testing::Test { * @return pointer to memory resource */ rmm::mr::device_memory_resource* mr() { return _mr; } + + /** + * @brief Returns `cuda_stream_view` that should be used for computation in + * tests inheriting from this fixture. + * @return view to cuda stream + */ + rmm::cuda_stream_view stream() { return _stream; } +}; + +/** + * @brief Base test fixture class from which libcuspatial test with no parameterization or only with + * type parameterization should inherit. + * + * Example: + * ``` + * class MyTestFixture : public cuspatial::test::BaseFixture {}; + * ``` + */ +class BaseFixture : public RMMResourceMixin, public ::testing::Test { +}; + +/** + * @brief Base test fixture class from which libcuspatial test with only value parameterization + * should inherit. + * + * Example: + * ``` + * template + * class MyTest : public cuspatial::test::BaseFixtureWithParam {}; + * + * TEST_P(MyTest, TestParamterGet) { + * auto [a, b, c] = GetParam(); + * ... + * } + * + * INSTANTIATE_TEST_SUITE_P(MyTests, MyTest, ::testing::Values( + * std::make_tuple(1, 2, 3), + * std::make_tuple(4, 5, 6, 9), + * std::make_tuple(7, 8))) + * ``` + */ +template +class BaseFixtureWithParam : public RMMResourceMixin>, + public ::testing::TestWithParam> { }; } // namespace test diff --git a/cpp/include/cuspatial_test/geometry_generator.cuh b/cpp/include/cuspatial_test/geometry_generator.cuh index 758ed6ef2..4d460a978 100644 --- a/cpp/include/cuspatial_test/geometry_generator.cuh +++ b/cpp/include/cuspatial_test/geometry_generator.cuh @@ -87,7 +87,7 @@ VecIterator generate_polygon(std::size_t num_sides_per_ring, points_it = generate_ring(num_sides_per_ring, centroid, radius, points_it, stream); // Align hole centroid to the horizontal axis of the polygon centroid - T hole_radius = radius / num_holes; + T hole_radius = radius / (num_holes + 1); T max_hole_displacement = radius - hole_radius; // make hole diff --git a/cpp/include/cuspatial_test/vector_equality.hpp b/cpp/include/cuspatial_test/vector_equality.hpp index 52a36d374..88b2f193f 100644 --- a/cpp/include/cuspatial_test/vector_equality.hpp +++ b/cpp/include/cuspatial_test/vector_equality.hpp @@ -169,9 +169,10 @@ inline void expect_vector_equivalent(Vector1 const& lhs, Vector2 const& rhs) } } -template -inline void expect_vector_equivalent(Vector1 const& lhs, Vector2 const& rhs, T abs_error) +template +inline void expect_vector_equivalent(Vector1 const& lhs, Vector2 const& rhs, U abs_error) { + using T = typename Vector1::value_type; static_assert(std::is_same_v, "Value type mismatch."); static_assert(!std::is_integral_v, "Integral types cannot be compared with an error."); diff --git a/cpp/tests/utility_test/test_geometry_generators.cu b/cpp/tests/utility_test/test_geometry_generators.cu index 1df09157b..d968d4821 100644 --- a/cpp/tests/utility_test/test_geometry_generators.cu +++ b/cpp/tests/utility_test/test_geometry_generators.cu @@ -19,31 +19,30 @@ #include -#include +#include +#include using namespace cuspatial; using namespace cuspatial::test; template -struct GeometryFactoryTest : public BaseFixture { - rmm::cuda_stream_view stream() { return rmm::cuda_stream_default; } +auto constexpr abs_error() +{ + return std::is_same_v ? 1e-7 : 1e-15; +}; +template +struct GeometryFactoryTest : public BaseFixture { template - void run(multipolygon_generator_parameter params, MultipolygonArray expected) + void run(multipolygon_generator_parameter params, + MultipolygonArray expected, + T abs_error = 0.0) { auto got = generate_multipolygon_array(params, stream()); - std::cout << "done generate multipolygon array" << std::endl; - - stream().synchronize(); - - std::cout << "done sync" << std::endl; - auto [got_geometry_offsets, got_part_offsets, got_ring_offsets, got_coordinates] = got.to_host(); - std::cout << "done sync to host" << std::endl; - auto [expected_geometry_offsets, expected_part_offsets, expected_ring_offsets, @@ -52,7 +51,10 @@ struct GeometryFactoryTest : public BaseFixture { CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(expected_geometry_offsets, got_geometry_offsets); CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(expected_part_offsets, got_part_offsets); CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(expected_ring_offsets, got_ring_offsets); - CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(expected_coordinates, got_coordinates); + if (abs_error == 0.0) + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(expected_coordinates, got_coordinates); + else + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(expected_coordinates, got_coordinates, abs_error); } }; @@ -74,3 +76,256 @@ TYPED_TEST(GeometryFactoryTest, multipolygonarray_basic) CUSPATIAL_RUN_TEST(this->run, params, std::move(expected)); } + +TYPED_TEST(GeometryFactoryTest, multipolygonarray_basic2) +{ + using T = TypeParam; + using P = vec_2d; + + auto params = multipolygon_generator_parameter{1, 1, 0, 4, {0.0, 0.0}, 1.0}; + auto expected = make_multipolygon_array( + {0, 1}, {0, 1}, {0, 5}, {P{1.0, 0.0}, P{0.0, 1.0}, P{-1.0, 0.0}, P{0.0, -1.0}, P{1.0, 0.0}}); + + CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error()); +} + +TYPED_TEST(GeometryFactoryTest, multipolygonarray_1poly1hole) +{ + using T = TypeParam; + using P = vec_2d; + + auto params = multipolygon_generator_parameter{1, 1, 1, 4, {0.0, 0.0}, 1.0}; + auto expected = make_multipolygon_array({0, 1}, + {0, 2}, + {0, 5, 10}, + {P{1.0, 0.0}, + P{0.0, 1.0}, + P{-1.0, 0.0}, + P{0.0, -1.0}, + P{1.0, 0.0}, + P{0.0, 0.0}, + P{-0.5, 0.5}, + P{-1.0, 0.0}, + P{-0.5, -0.5}, + P{0.0, 0.0}}); + + CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error()); +} + +TYPED_TEST(GeometryFactoryTest, multipolygonarray_1poly2holes) +{ + using T = TypeParam; + using P = vec_2d; + + auto params = multipolygon_generator_parameter{1, 1, 2, 4, {0.0, 0.0}, 1.0}; + auto expected = make_multipolygon_array({0, 1}, + {0, 3}, + {0, 5, 10, 15}, + { + P{1.0, 0.0}, + P{0.0, 1.0}, + P{-1.0, 0.0}, + P{0.0, -1.0}, + P{1.0, 0.0}, + P{-0.3333333333333333, 0.0}, + P{-0.6666666666666666, 0.3333333333333333}, + P{-1.0, 0.0}, + P{-0.6666666666666666, -0.3333333333333333}, + P{-0.3333333333333333, 0.0}, + P{0.3333333333333333, 0.0}, + P{0.0, 0.3333333333333333}, + P{-0.3333333333333333, 0.0}, + P{0.0, -0.3333333333333333}, + P{0.3333333333333333, 0.0}, + }); + + CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error()); +} + +TYPED_TEST(GeometryFactoryTest, multipolygonarray_2poly0hole) +{ + using T = TypeParam; + using P = vec_2d; + + auto params = multipolygon_generator_parameter{1, 2, 0, 4, {0.0, 0.0}, 1.0}; + auto expected = make_multipolygon_array({0, 2}, + {0, 1, 2}, + {0, 5, 10}, + { + P{1.0, 0.0}, + P{0.0, 1.0}, + P{-1.0, 0.0}, + P{0.0, -1.0}, + P{1.0, 0.0}, + P{4.0, 0.0}, + P{3.0, 1.0}, + P{2.0, 0.0}, + P{3.0, -1.0}, + P{4.0, 0.0}, + }); + + CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error()); +} + +TYPED_TEST(GeometryFactoryTest, multipolygonarray_2poly1hole) +{ + using T = TypeParam; + using P = vec_2d; + + auto params = multipolygon_generator_parameter{1, 2, 1, 4, {0.0, 0.0}, 1.0}; + auto expected = + make_multipolygon_array({0, 2}, + {0, 2, 4}, + {0, 5, 10, 15, 20}, + { + P{1.0, 0.0}, P{0.0, 1.0}, P{-1.0, 0.0}, P{0.0, -1.0}, P{1.0, 0.0}, + P{0.0, 0.0}, P{-0.5, 0.5}, P{-1.0, 0.0}, P{-0.5, -0.5}, P{0.0, 0.0}, + P{4.0, 0.0}, P{3.0, 1.0}, P{2.0, 0.0}, P{3.0, -1.0}, P{4.0, 0.0}, + P{3.0, 0.0}, P{2.5, 0.5}, P{2.0, 0.0}, P{2.5, -0.5}, P{3.0, 0.0}, + }); + + CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error()); +} + +TYPED_TEST(GeometryFactoryTest, multipolygonarray_2multipolygon1poly0hole) +{ + using T = TypeParam; + using P = vec_2d; + + auto params = multipolygon_generator_parameter{2, 1, 0, 4, {0.0, 0.0}, 1.0}; + auto expected = make_multipolygon_array({0, 1, 2}, + {0, 1, 2}, + {0, 5, 10}, + { + P{1.0, 0.0}, + P{0.0, 1.0}, + P{-1.0, 0.0}, + P{0.0, -1.0}, + P{1.0, 0.0}, + P{1.0, 0.0}, + P{0.0, 1.0}, + P{-1.0, 0.0}, + P{0.0, -1.0}, + P{1.0, 0.0}, + }); + + CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error()); +} + +TYPED_TEST(GeometryFactoryTest, multipolygonarray_2multipolygon1poly1hole) +{ + using T = TypeParam; + using P = vec_2d; + + auto params = multipolygon_generator_parameter{2, 1, 1, 4, {0.0, 0.0}, 1.0}; + auto expected = + make_multipolygon_array({0, 1, 2}, + {0, 2, 4}, + {0, 5, 10, 15, 20}, + { + P{1.0, 0.0}, P{0.0, 1.0}, P{-1.0, 0.0}, P{0.0, -1.0}, P{1.0, 0.0}, + P{0.0, 0.0}, P{-0.5, 0.5}, P{-1.0, 0.0}, P{-0.5, -0.5}, P{0.0, 0.0}, + P{1.0, 0.0}, P{0.0, 1.0}, P{-1.0, 0.0}, P{0.0, -1.0}, P{1.0, 0.0}, + P{0.0, 0.0}, P{-0.5, 0.5}, P{-1.0, 0.0}, P{-0.5, -0.5}, P{0.0, 0.0}, + }); + + CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error()); +} + +TYPED_TEST(GeometryFactoryTest, multipolygonarray_2multipolygon2poly1hole) +{ + using T = TypeParam; + using P = vec_2d; + + auto params = multipolygon_generator_parameter{2, 2, 1, 4, {0.0, 0.0}, 1.0}; + auto expected = make_multipolygon_array( + {0, 2, 4}, + {0, 2, 4, 6, 8}, + {0, 5, 10, 15, 20, 25, 30, 35, 40}, + { + P{1.0, 0.0}, P{0.0, 1.0}, P{-1.0, 0.0}, P{0.0, -1.0}, P{1.0, 0.0}, P{0.0, 0.0}, + P{-0.5, 0.5}, P{-1.0, 0.0}, P{-0.5, -0.5}, P{0.0, 0.0}, P{4.0, 0.0}, P{3.0, 1.0}, + P{2.0, 0.0}, P{3.0, -1.0}, P{4.0, 0.0}, P{3.0, 0.0}, P{2.5, 0.5}, P{2.0, 0.0}, + P{2.5, -0.5}, P{3.0, 0.0}, P{1.0, 0.0}, P{0.0, 1.0}, P{-1.0, 0.0}, P{0.0, -1.0}, + P{1.0, 0.0}, P{0.0, 0.0}, P{-0.5, 0.5}, P{-1.0, 0.0}, P{-0.5, -0.5}, P{0.0, 0.0}, + P{4.0, 0.0}, P{3.0, 1.0}, P{2.0, 0.0}, P{3.0, -1.0}, P{4.0, 0.0}, P{3.0, 0.0}, + P{2.5, 0.5}, P{2.0, 0.0}, P{2.5, -0.5}, P{3.0, 0.0}, + }); + + CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error()); +} + +TYPED_TEST(GeometryFactoryTest, multipolygonarray_basic_centroid) +{ + using T = TypeParam; + using P = vec_2d; + + auto params = multipolygon_generator_parameter{1, 1, 0, 4, {2.0, 3.0}, 1.0}; + auto expected = make_multipolygon_array( + {0, 1}, {0, 1}, {0, 4}, {P{2.0, 3.0}, P{1.0, 4.0}, P{0.0, 3.0}, P{1.0, 2.0}, P{2.0, 3.0}}); + + CUSPATIAL_RUN_TEST(this->run, params, std::move(expected)); +} + +TYPED_TEST(GeometryFactoryTest, multipolygonarray_basic_radius) +{ + using T = TypeParam; + using P = vec_2d; + + auto params = multipolygon_generator_parameter{1, 1, 0, 4, {0.0, 0.0}, 6.0}; + auto expected = make_multipolygon_array( + {0, 1}, {0, 1}, {0, 4}, {P{6.0, 0.0}, P{0.0, 6.0}, P{-6.0, 0.0}, P{0.0, -6.0}, P{6.0, 0.0}}); + + CUSPATIAL_RUN_TEST(this->run, params, std::move(expected)); +} + +struct GeometryFactoryCountVerificationTest : public BaseFixtureWithParam, + float> { + void run(multipolygon_generator_parameter params) + { + auto got = generate_multipolygon_array(params, stream()); + + auto [got_geometry_offsets, got_part_offsets, got_ring_offsets, got_coordinates] = + got.to_host(); + + EXPECT_EQ(got_geometry_offsets.size(), params.num_multipolygons + 1); + EXPECT_EQ(got_part_offsets.size(), params.num_polygons() + 1); + EXPECT_EQ(got_ring_offsets.size(), params.num_rings() + 1); + EXPECT_EQ(got_coordinates.size(), params.num_coords()); + } +}; + +TEST_P(GeometryFactoryCountVerificationTest, CountsVerification) +{ + // Structured binding unsupported by Gtest + std::size_t num_multipolygons = std::get<0>(GetParam()); + std::size_t num_polygons_per_multipolygon = std::get<1>(GetParam()); + std::size_t num_holes_per_polygon = std::get<2>(GetParam()); + std::size_t num_sides_per_ring = std::get<3>(GetParam()); + vec_2d centroid = std::get<4>(GetParam()); + float radius = std::get<5>(GetParam()); + + auto params = multipolygon_generator_parameter{num_multipolygons, + num_polygons_per_multipolygon, + num_holes_per_polygon, + num_sides_per_ring, + centroid, + radius}; + CUSPATIAL_RUN_TEST(this->run, params); +} + +INSTANTIATE_TEST_SUITE_P( + GeometryFactoryCountVerificationTests, + GeometryFactoryCountVerificationTest, + ::testing::Combine( + ::testing::Range(1 << 4, 1 << 10, 2), // num_multipolygons + ::testing::Range(1 << 4, 1 << 10, 2), // num_polygons_per_multipolygon + ::testing::Range(1 << 4, 1 << 10, 2), // num_holes_per_polygon + ::testing::Range(1 << 4, 1 << 10, 2), // num_sides_per_ring + ::testing::Values(vec_2d{0.0, 0.0}, vec_2d{1.0, 5.0}), // centroid + ::testing::Values(1.0, 6.0) // radius + )); From 5f347fd18c05da5830419c0cd42e9af9a8026146 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 17 Mar 2023 17:42:38 -0700 Subject: [PATCH 36/60] adopt single kernel launch device multipolygon generation --- .../detail/ranges/multipolygon_range.cuh | 20 +++ .../ranges/multipolygon_range.cuh | 36 ++-- .../cuspatial_test/geometry_generator.cuh | 169 +++++++++--------- .../utility_test/test_geometry_generators.cu | 128 ++++++------- 4 files changed, 191 insertions(+), 162 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh index 452d1be2e..6d0e51a34 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh @@ -170,6 +170,26 @@ multipolygon_range::m return multipolygon_begin() + num_multipolygons(); } +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range::point_begin() +{ + return _point_begin; +} + +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range::point_end() +{ + return _point_end; +} + template CUSPATIAL_HOST_DEVICE auto geometry_idx_from_segment_idx(IndexType segment_idx); + /// Given the index of a point, return the ring index + /// where the point locates. + template + CUSPATIAL_HOST_DEVICE auto ring_idx_from_point_idx(IndexType point_idx); + + /// Given the index of a ring, return the part (polygon) index + /// where the ring locates. + template + CUSPATIAL_HOST_DEVICE auto part_idx_from_ring_idx(IndexType ring_idx); + + /// Given the index of a part (polygon), return the geometry (multipolygon) index + /// where the polygon locates. + template + CUSPATIAL_HOST_DEVICE auto geometry_idx_from_part_idx(IndexType part_idx); + /// Returns the `multipolygon_idx`th multipolygon in the range. template CUSPATIAL_HOST_DEVICE auto operator[](IndexType multipolygon_idx); @@ -133,21 +154,6 @@ class multipolygon_range { VecIterator _point_end; private: - /// Given the index of a point, return the ring index - /// where the point locates. - template - CUSPATIAL_HOST_DEVICE auto ring_idx_from_point_idx(IndexType point_idx); - - /// Given the index of a ring, return the part (polygon) index - /// where the ring locates. - template - CUSPATIAL_HOST_DEVICE auto part_idx_from_ring_idx(IndexType ring_idx); - - /// Given the index of a part (polygon), return the geometry (multipolygon) index - /// where the polygon locates. - template - CUSPATIAL_HOST_DEVICE auto geometry_idx_from_part_idx(IndexType part_idx); - template CUSPATIAL_HOST_DEVICE bool is_valid_segment_id(IndexType1 segment_idx, IndexType2 ring_idx); }; diff --git a/cpp/include/cuspatial_test/geometry_generator.cuh b/cpp/include/cuspatial_test/geometry_generator.cuh index 4d460a978..8d9c5666e 100644 --- a/cpp/include/cuspatial_test/geometry_generator.cuh +++ b/cpp/include/cuspatial_test/geometry_generator.cuh @@ -16,6 +16,9 @@ #include +#include +#include +#include #include #include @@ -41,83 +44,83 @@ struct multipolygon_generator_parameter { vec_2d centroid; T radius; - std::size_t num_polygons() { return num_multipolygons * num_polygons_per_multipolygon; } - std::size_t num_rings() { return num_polygons() * num_rings_per_polygon(); } - std::size_t num_coords() { return num_rings() * num_vertices_per_ring(); } - std::size_t num_vertices_per_ring() { return num_sides_per_ring + 1; } - std::size_t num_rings_per_polygon() { return num_holes_per_polygon + 1; } + CUSPATIAL_HOST_DEVICE std::size_t num_polygons() + { + return num_multipolygons * num_polygons_per_multipolygon; + } + CUSPATIAL_HOST_DEVICE std::size_t num_rings() { return num_polygons() * num_rings_per_polygon(); } + CUSPATIAL_HOST_DEVICE std::size_t num_coords() { return num_rings() * num_vertices_per_ring(); } + CUSPATIAL_HOST_DEVICE std::size_t num_vertices_per_ring() { return num_sides_per_ring + 1; } + CUSPATIAL_HOST_DEVICE std::size_t num_rings_per_polygon() { return num_holes_per_polygon + 1; } + CUSPATIAL_HOST_DEVICE T hole_radius() { return radius / (num_holes_per_polygon + 1); } }; -template -VecIterator generate_ring(std::size_t num_sides, - vec_2d centroid, - T radius, - VecIterator points_it, - rmm::cuda_stream_view stream) +template +vec_2d __device__ generate_polygon_coordinate(std::size_t point_local_idx, + std::size_t num_sides, + vec_2d centroid, + T radius) { - std::size_t num_vertices = num_sides + 1; - std::cout << "generate_ring: \n num_vertices: " << num_vertices << std::endl; - thrust::tabulate( - rmm::exec_policy(stream), - points_it, - points_it + num_vertices, - [num_sides, centroid, radius] __device__(int32_t i) { - // Overrides last coordinate to make sure ring is closed. - if (i == num_sides) return vec_2d{centroid.x + radius, centroid.y}; - - T angle = 2.0 * PI * i / num_sides; - return vec_2d{centroid.x + radius * cos(angle), centroid.y + radius * sin(angle)}; - }); - - std::cout << "Done generate_ring: \n" << std::endl; - - return points_it + num_vertices; + // Overrides last coordinate to make sure ring is closed. + if (point_local_idx == num_sides) return vec_2d{centroid.x + radius, centroid.y}; + + T angle = 2.0 * PI * point_local_idx / num_sides; + + return vec_2d{centroid.x + radius * cos(angle), centroid.y + radius * sin(angle)}; } -template -VecIterator generate_polygon(std::size_t num_sides_per_ring, - std::size_t num_holes, - vec_2d centroid, - T radius, - VecIterator points_it, - rmm::cuda_stream_view stream) +template +vec_2d __device__ multipolygon_centroid_displacement(vec_2d centroid, + std::size_t part_local_idx, + T radius) { - std::cout << "generate_polygon: \n" << std::endl; - // make shell - points_it = generate_ring(num_sides_per_ring, centroid, radius, points_it, stream); - - // Align hole centroid to the horizontal axis of the polygon centroid - T hole_radius = radius / (num_holes + 1); - T max_hole_displacement = radius - hole_radius; + return centroid + vec_2d{part_local_idx * radius * T{3.0}, T{0.0}}; +} - // make hole - for (std::size_t i = 0; i < num_holes; ++i) { - T displacement_x = -max_hole_displacement + i * hole_radius * 2; - T displacement_y = 0.0; - vec_2d hole_centroid = centroid + vec_2d{displacement_x, displacement_y}; - points_it = generate_ring(num_sides_per_ring, hole_centroid, hole_radius, points_it, stream); - } +template +vec_2d __device__ polygon_centroid_displacement(vec_2d centroid, + std::size_t ring_local_idx, + T radius, + T hole_radius) +{ + // This is a shell + if (ring_local_idx == 0) { return centroid; } - return points_it; + // This is a hole + ring_local_idx -= 1; // offset hole indices to be 0-based + T max_hole_displacement = radius - hole_radius; + T displacement_x = -max_hole_displacement + ring_local_idx * hole_radius * 2; + T displacement_y = 0.0; + return centroid + vec_2d{displacement_x, displacement_y}; } -template -VecIterator generate_multipolygon(std::size_t num_polygons, - std::size_t num_sides_per_ring, - std::size_t num_holes, - vec_2d origin, - T radius, - VecIterator points_it, - rmm::cuda_stream_view stream) +template +void __global__ generate_multipolygon_array_coordinates(MultipolygonRange multipolygons, + multipolygon_generator_parameter params) { - std::cout << "generate_multipolygon: \n" << std::endl; - for (std::size_t i = 0; i < num_polygons; ++i) { - vec_2d centroid = origin + vec_2d{i * radius * 3, 0}; - points_it = - generate_polygon(num_sides_per_ring, num_holes, centroid, radius, points_it, stream); + for (auto idx = threadIdx.x + blockIdx.x * blockDim.x; idx < multipolygons.num_points(); + idx += gridDim.x * blockDim.x) { + auto ring_idx = multipolygons.ring_idx_from_point_idx(idx); + auto part_idx = multipolygons.part_idx_from_ring_idx(ring_idx); + auto geometry_idx = multipolygons.geometry_idx_from_part_idx(part_idx); + + auto point_local_idx = idx - params.num_vertices_per_ring() * ring_idx; + auto ring_local_idx = ring_idx - params.num_rings_per_polygon() * part_idx; + auto part_local_idx = part_idx - params.num_polygons_per_multipolygon * geometry_idx; + + auto centroid = polygon_centroid_displacement( + multipolygon_centroid_displacement(params.centroid, part_local_idx, params.radius), + ring_local_idx, + params.radius, + params.hole_radius()); + + if (ring_local_idx == 0) // Generate coordinate for shell + multipolygons.point_begin()[idx] = generate_polygon_coordinate( + point_local_idx, params.num_sides_per_ring, centroid, params.radius); + else // Generate coordinate for holes + multipolygons.point_begin()[idx] = generate_polygon_coordinate( + point_local_idx, params.num_sides_per_ring, centroid, params.hole_radius()); } - - return points_it; } /** @@ -132,23 +135,10 @@ template auto generate_multipolygon_array(multipolygon_generator_parameter params, rmm::cuda_stream_view stream) { - std::cout << "Num_coords: " << params.num_coords() << std::endl; - rmm::device_uvector> coordinates(params.num_coords(), stream); - auto points_it = coordinates.begin(); - - for (std::size_t i = 0; i < params.num_multipolygons; ++i) { - points_it = generate_multipolygon(params.num_polygons_per_multipolygon, - params.num_sides_per_ring, - params.num_holes_per_polygon, - params.centroid, - params.radius, - points_it, - stream); - } - - rmm::device_uvector ring_offsets(params.num_rings() + 1, stream); - rmm::device_uvector part_offsets(params.num_polygons() + 1, stream); rmm::device_uvector geometry_offsets(params.num_multipolygons + 1, stream); + rmm::device_uvector part_offsets(params.num_polygons() + 1, stream); + rmm::device_uvector ring_offsets(params.num_rings() + 1, stream); + rmm::device_uvector> coordinates(params.num_coords(), stream); thrust::sequence(rmm::exec_policy(stream), ring_offsets.begin(), @@ -162,14 +152,27 @@ auto generate_multipolygon_array(multipolygon_generator_parameter params, std::size_t{0}, params.num_rings_per_polygon()); - std::cout << "num_polygons_per_multipolygon:" << params.num_polygons_per_multipolygon - << std::endl; thrust::sequence(rmm::exec_policy(stream), geometry_offsets.begin(), geometry_offsets.end(), std::size_t{0}, params.num_polygons_per_multipolygon); + auto multipolygons = multipolygon_range(geometry_offsets.begin(), + geometry_offsets.end(), + part_offsets.begin(), + part_offsets.end(), + ring_offsets.begin(), + ring_offsets.end(), + coordinates.begin(), + coordinates.end()); + + auto [tpb, nblocks] = grid_1d(multipolygons.num_points()); + + generate_multipolygon_array_coordinates<<>>(multipolygons, params); + + CUSPATIAL_CHECK_CUDA(stream.value()); + return make_multipolygon_array_from_uvector>(std::move(geometry_offsets), std::move(part_offsets), std::move(ring_offsets), diff --git a/cpp/tests/utility_test/test_geometry_generators.cu b/cpp/tests/utility_test/test_geometry_generators.cu index d968d4821..359c87f92 100644 --- a/cpp/tests/utility_test/test_geometry_generators.cu +++ b/cpp/tests/utility_test/test_geometry_generators.cu @@ -26,9 +26,9 @@ using namespace cuspatial; using namespace cuspatial::test; template -auto constexpr abs_error() +auto constexpr abs_error(T radius) { - return std::is_same_v ? 1e-7 : 1e-15; + return radius * (std::is_same_v ? 1e-7 : 1e-15); }; template @@ -86,7 +86,7 @@ TYPED_TEST(GeometryFactoryTest, multipolygonarray_basic2) auto expected = make_multipolygon_array( {0, 1}, {0, 1}, {0, 5}, {P{1.0, 0.0}, P{0.0, 1.0}, P{-1.0, 0.0}, P{0.0, -1.0}, P{1.0, 0.0}}); - CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error()); + CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error(params.radius)); } TYPED_TEST(GeometryFactoryTest, multipolygonarray_1poly1hole) @@ -109,7 +109,7 @@ TYPED_TEST(GeometryFactoryTest, multipolygonarray_1poly1hole) P{-0.5, -0.5}, P{0.0, 0.0}}); - CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error()); + CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error(params.radius)); } TYPED_TEST(GeometryFactoryTest, multipolygonarray_1poly2holes) @@ -139,7 +139,7 @@ TYPED_TEST(GeometryFactoryTest, multipolygonarray_1poly2holes) P{0.3333333333333333, 0.0}, }); - CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error()); + CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error(params.radius)); } TYPED_TEST(GeometryFactoryTest, multipolygonarray_2poly0hole) @@ -164,7 +164,7 @@ TYPED_TEST(GeometryFactoryTest, multipolygonarray_2poly0hole) P{4.0, 0.0}, }); - CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error()); + CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error(params.radius)); } TYPED_TEST(GeometryFactoryTest, multipolygonarray_2poly1hole) @@ -184,7 +184,7 @@ TYPED_TEST(GeometryFactoryTest, multipolygonarray_2poly1hole) P{3.0, 0.0}, P{2.5, 0.5}, P{2.0, 0.0}, P{2.5, -0.5}, P{3.0, 0.0}, }); - CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error()); + CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error(params.radius)); } TYPED_TEST(GeometryFactoryTest, multipolygonarray_2multipolygon1poly0hole) @@ -209,7 +209,7 @@ TYPED_TEST(GeometryFactoryTest, multipolygonarray_2multipolygon1poly0hole) P{1.0, 0.0}, }); - CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error()); + CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error(params.radius)); } TYPED_TEST(GeometryFactoryTest, multipolygonarray_2multipolygon1poly1hole) @@ -229,7 +229,7 @@ TYPED_TEST(GeometryFactoryTest, multipolygonarray_2multipolygon1poly1hole) P{0.0, 0.0}, P{-0.5, 0.5}, P{-1.0, 0.0}, P{-0.5, -0.5}, P{0.0, 0.0}, }); - CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error()); + CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error(params.radius)); } TYPED_TEST(GeometryFactoryTest, multipolygonarray_2multipolygon2poly1hole) @@ -252,7 +252,7 @@ TYPED_TEST(GeometryFactoryTest, multipolygonarray_2multipolygon2poly1hole) P{2.5, 0.5}, P{2.0, 0.0}, P{2.5, -0.5}, P{3.0, 0.0}, }); - CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error()); + CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error(params.radius)); } TYPED_TEST(GeometryFactoryTest, multipolygonarray_basic_centroid) @@ -262,9 +262,9 @@ TYPED_TEST(GeometryFactoryTest, multipolygonarray_basic_centroid) auto params = multipolygon_generator_parameter{1, 1, 0, 4, {2.0, 3.0}, 1.0}; auto expected = make_multipolygon_array( - {0, 1}, {0, 1}, {0, 4}, {P{2.0, 3.0}, P{1.0, 4.0}, P{0.0, 3.0}, P{1.0, 2.0}, P{2.0, 3.0}}); + {0, 1}, {0, 1}, {0, 5}, {P{3.0, 3.0}, P{2.0, 4.0}, P{1.0, 3.0}, P{2.0, 2.0}, P{3.0, 3.0}}); - CUSPATIAL_RUN_TEST(this->run, params, std::move(expected)); + CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error(params.radius)); } TYPED_TEST(GeometryFactoryTest, multipolygonarray_basic_radius) @@ -274,58 +274,58 @@ TYPED_TEST(GeometryFactoryTest, multipolygonarray_basic_radius) auto params = multipolygon_generator_parameter{1, 1, 0, 4, {0.0, 0.0}, 6.0}; auto expected = make_multipolygon_array( - {0, 1}, {0, 1}, {0, 4}, {P{6.0, 0.0}, P{0.0, 6.0}, P{-6.0, 0.0}, P{0.0, -6.0}, P{6.0, 0.0}}); - - CUSPATIAL_RUN_TEST(this->run, params, std::move(expected)); -} - -struct GeometryFactoryCountVerificationTest : public BaseFixtureWithParam, - float> { - void run(multipolygon_generator_parameter params) - { - auto got = generate_multipolygon_array(params, stream()); + {0, 1}, {0, 1}, {0, 5}, {P{6.0, 0.0}, P{0.0, 6.0}, P{-6.0, 0.0}, P{0.0, -6.0}, P{6.0, 0.0}}); - auto [got_geometry_offsets, got_part_offsets, got_ring_offsets, got_coordinates] = - got.to_host(); - - EXPECT_EQ(got_geometry_offsets.size(), params.num_multipolygons + 1); - EXPECT_EQ(got_part_offsets.size(), params.num_polygons() + 1); - EXPECT_EQ(got_ring_offsets.size(), params.num_rings() + 1); - EXPECT_EQ(got_coordinates.size(), params.num_coords()); - } -}; - -TEST_P(GeometryFactoryCountVerificationTest, CountsVerification) -{ - // Structured binding unsupported by Gtest - std::size_t num_multipolygons = std::get<0>(GetParam()); - std::size_t num_polygons_per_multipolygon = std::get<1>(GetParam()); - std::size_t num_holes_per_polygon = std::get<2>(GetParam()); - std::size_t num_sides_per_ring = std::get<3>(GetParam()); - vec_2d centroid = std::get<4>(GetParam()); - float radius = std::get<5>(GetParam()); - - auto params = multipolygon_generator_parameter{num_multipolygons, - num_polygons_per_multipolygon, - num_holes_per_polygon, - num_sides_per_ring, - centroid, - radius}; - CUSPATIAL_RUN_TEST(this->run, params); + CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error(params.radius)); } -INSTANTIATE_TEST_SUITE_P( - GeometryFactoryCountVerificationTests, - GeometryFactoryCountVerificationTest, - ::testing::Combine( - ::testing::Range(1 << 4, 1 << 10, 2), // num_multipolygons - ::testing::Range(1 << 4, 1 << 10, 2), // num_polygons_per_multipolygon - ::testing::Range(1 << 4, 1 << 10, 2), // num_holes_per_polygon - ::testing::Range(1 << 4, 1 << 10, 2), // num_sides_per_ring - ::testing::Values(vec_2d{0.0, 0.0}, vec_2d{1.0, 5.0}), // centroid - ::testing::Values(1.0, 6.0) // radius - )); +// struct GeometryFactoryCountVerificationTest : public BaseFixtureWithParam, +// float> { +// void run(multipolygon_generator_parameter params) +// { +// auto got = generate_multipolygon_array(params, stream()); + +// auto [got_geometry_offsets, got_part_offsets, got_ring_offsets, got_coordinates] = +// got.to_host(); + +// EXPECT_EQ(got_geometry_offsets.size(), params.num_multipolygons + 1); +// EXPECT_EQ(got_part_offsets.size(), params.num_polygons() + 1); +// EXPECT_EQ(got_ring_offsets.size(), params.num_rings() + 1); +// EXPECT_EQ(got_coordinates.size(), params.num_coords()); +// } +// }; + +// TEST_P(GeometryFactoryCountVerificationTest, CountsVerification) +// { +// // Structured binding unsupported by Gtest +// std::size_t num_multipolygons = std::get<0>(GetParam()); +// std::size_t num_polygons_per_multipolygon = std::get<1>(GetParam()); +// std::size_t num_holes_per_polygon = std::get<2>(GetParam()); +// std::size_t num_sides_per_ring = std::get<3>(GetParam()); +// vec_2d centroid = std::get<4>(GetParam()); +// float radius = std::get<5>(GetParam()); + +// auto params = multipolygon_generator_parameter{num_multipolygons, +// num_polygons_per_multipolygon, +// num_holes_per_polygon, +// num_sides_per_ring, +// centroid, +// radius}; +// CUSPATIAL_RUN_TEST(this->run, params); +// } + +// INSTANTIATE_TEST_SUITE_P( +// GeometryFactoryCountVerificationTests, +// GeometryFactoryCountVerificationTest, +// ::testing::Combine( +// ::testing::Range(1 << 4, 1 << 10, 2), // num_multipolygons +// ::testing::Range(1 << 4, 1 << 10, 2), // num_polygons_per_multipolygon +// ::testing::Range(1 << 4, 1 << 10, 2), // num_holes_per_polygon +// ::testing::Range(1 << 4, 1 << 10, 2), // num_sides_per_ring +// ::testing::Values(vec_2d{0.0, 0.0}, vec_2d{1.0, 5.0}), // centroid +// ::testing::Values(1.0, 6.0) // radius +// )); From 293ee0f7a4967c1923d4b1aa5e0823261d61b2cc Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Sun, 19 Mar 2023 22:27:04 -0700 Subject: [PATCH 37/60] documentation --- .../detail/ranges/multipolygon_range.cuh | 10 +- .../cuspatial_test/geometry_generator.cuh | 91 +++++++++++++++---- .../cuspatial_test/vector_factories.cuh | 11 ++- 3 files changed, 87 insertions(+), 25 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh index 6d0e51a34..4b9325e09 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh @@ -16,11 +16,6 @@ #pragma once -#include -#include -#include -#include - #include #include #include @@ -28,6 +23,11 @@ #include #include +#include +#include +#include +#include + #include #include diff --git a/cpp/include/cuspatial_test/geometry_generator.cuh b/cpp/include/cuspatial_test/geometry_generator.cuh index 8d9c5666e..ee9952080 100644 --- a/cpp/include/cuspatial_test/geometry_generator.cuh +++ b/cpp/include/cuspatial_test/geometry_generator.cuh @@ -33,6 +33,11 @@ namespace test { constexpr double PI = 3.14159265358979323846; +/** + * @brief Struct to store the parameters of the multipolygon array generator + * + * @tparam T Type of the coordinates + */ template struct multipolygon_generator_parameter { using element_t = T; @@ -69,19 +74,62 @@ vec_2d __device__ generate_polygon_coordinate(std::size_t point_local_idx, return vec_2d{centroid.x + radius * cos(angle), centroid.y + radius * sin(angle)}; } +/** + * @brief Apply displacement to the centroid of a polygon. + * + * The `i`th polygon's centroid is displaced by (3*radius*i, 0). This makes sure + * polygons within a multipolygon does not overlap. + * + * @tparam T Type of the coordinates + * @param centroid The first centroid of the polygons + * @param part_local_idx Local index of the polygon + * @param radius Radius of each polygon + * @return Displaced centroid + */ template -vec_2d __device__ multipolygon_centroid_displacement(vec_2d centroid, - std::size_t part_local_idx, - T radius) +vec_2d __device__ polygon_centroid_displacement(vec_2d centroid, + std::size_t part_local_idx, + T radius) { return centroid + vec_2d{part_local_idx * radius * T{3.0}, T{0.0}}; } +/** + * @brief Given a ring centroid, displace it based on its ring index. + * + * A Polygon contains at least 1 shell. It may contain 0 or more holes. + * The shell is the leading ring of the polygon (index 0). All holes' centroid + * has the same y value as the shell's centroid. Holes are aligned from left + * to right on the center axis, with no overlapping areas. It may look like: + * + * ****** + * ** ** + * * * + * * * + * * * + * @@@ @@@ @@@ @@@ * + * @ @ @ @ @ * + * @ @ @ @ @ * + * @ @ @ @ @ * + * *@@@ @@@ @@@ @@@ * + * * * + * * * + * * * + * * * + * ** ** + * ****** + * + * + * @tparam T Type of the coordinates + * @param centroid The center of the polygon + * @param ring_local_idx Local index of the ring + * @param radius Radius of the polygon + * @param hole_radius Radius of each hole + * @return Centroid of the ring + */ template -vec_2d __device__ polygon_centroid_displacement(vec_2d centroid, - std::size_t ring_local_idx, - T radius, - T hole_radius) +vec_2d __device__ +ring_centroid_displacement(vec_2d centroid, std::size_t ring_local_idx, T radius, T hole_radius) { // This is a shell if (ring_local_idx == 0) { return centroid; } @@ -94,6 +142,17 @@ vec_2d __device__ polygon_centroid_displacement(vec_2d centroid, return centroid + vec_2d{displacement_x, displacement_y}; } +/** + * @brief Kernel to generate coordinates for multipolygon arrays. + * + * @pre This kernel requires that the three offset arrays (geometry, part, ring) has been prefilled + * with the correct offsets. + * + * @tparam T Type of the coordinate + * @tparam MultipolygonRange A specialization of `multipolygon_range` + * @param multipolygons The range of multipolygons + * @param params Parameters to generate the mulitpolygons + */ template void __global__ generate_multipolygon_array_coordinates(MultipolygonRange multipolygons, multipolygon_generator_parameter params) @@ -108,8 +167,8 @@ void __global__ generate_multipolygon_array_coordinates(MultipolygonRange multip auto ring_local_idx = ring_idx - params.num_rings_per_polygon() * part_idx; auto part_local_idx = part_idx - params.num_polygons_per_multipolygon * geometry_idx; - auto centroid = polygon_centroid_displacement( - multipolygon_centroid_displacement(params.centroid, part_local_idx, params.radius), + auto centroid = ring_centroid_displacement( + polygon_centroid_displacement(params.centroid, part_local_idx, params.radius), ring_local_idx, params.radius, params.hole_radius()); @@ -124,12 +183,12 @@ void __global__ generate_multipolygon_array_coordinates(MultipolygonRange multip } /** - * @brief Helper to generate multipolygon arrays used for benchmarks. + * @brief Helper to generate multipolygon arrays used for tests and benchmarks. * * @tparam T The floating point type for the coordinates + * @param params The parameters to set for the multipolygon array * @param stream The CUDA stream to use for device memory operations and kernel launches - * @return A tuple of x and y coordinates of points and offsets to which the first point - * of each linestring starts. + * @return A cuspatial::test::multipolygon_array object. */ template auto generate_multipolygon_array(multipolygon_generator_parameter params, @@ -173,10 +232,10 @@ auto generate_multipolygon_array(multipolygon_generator_parameter params, CUSPATIAL_CHECK_CUDA(stream.value()); - return make_multipolygon_array_from_uvector>(std::move(geometry_offsets), - std::move(part_offsets), - std::move(ring_offsets), - std::move(coordinates)); + return make_multipolygon_array>(std::move(geometry_offsets), + std::move(part_offsets), + std::move(ring_offsets), + std::move(coordinates)); } } // namespace test diff --git a/cpp/include/cuspatial_test/vector_factories.cuh b/cpp/include/cuspatial_test/vector_factories.cuh index 89077b216..2ca1270fb 100644 --- a/cpp/include/cuspatial_test/vector_factories.cuh +++ b/cpp/include/cuspatial_test/vector_factories.cuh @@ -120,6 +120,9 @@ class multipolygon_array { _coordinate_offsets_array.end()); } + /** + * @brief Copy the offset arrays to host. + */ auto to_host() const { auto geometry_offsets = cuspatial::test::to_host(_geometry_offsets_array); @@ -186,10 +189,10 @@ auto make_multipolygon_array(std::initializer_list geometry_inl, } template -auto make_multipolygon_array_from_uvector(rmm::device_uvector geometry_inl, - rmm::device_uvector part_inl, - rmm::device_uvector ring_inl, - rmm::device_uvector coord_inl) +auto make_multipolygon_array(rmm::device_uvector geometry_inl, + rmm::device_uvector part_inl, + rmm::device_uvector ring_inl, + rmm::device_uvector coord_inl) { return multipolygon_array, rmm::device_uvector, From 00a6728a5a99b63518b3b1c890b03ce5c2377c18 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Sun, 19 Mar 2023 22:38:08 -0700 Subject: [PATCH 38/60] document function --- .../cuspatial_test/geometry_generator.cuh | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/cpp/include/cuspatial_test/geometry_generator.cuh b/cpp/include/cuspatial_test/geometry_generator.cuh index ee9952080..376b0817c 100644 --- a/cpp/include/cuspatial_test/geometry_generator.cuh +++ b/cpp/include/cuspatial_test/geometry_generator.cuh @@ -60,11 +60,24 @@ struct multipolygon_generator_parameter { CUSPATIAL_HOST_DEVICE T hole_radius() { return radius / (num_holes_per_polygon + 1); } }; +/** + * @brief Generate coordinates for the ring based on the local index of the point. + * + * The ring is generated by walking a point around a centroid with a fixed radius. + * Each step has equal angles. + * + * @tparam T Type of coordinate + * @param point_local_idx Local point of the point + * @param num_sides Number of sides of the polygon + * @param centroid Centroid of the ring + * @param radius Radius of the ring + * @return Coordinate of the point + */ template -vec_2d __device__ generate_polygon_coordinate(std::size_t point_local_idx, - std::size_t num_sides, - vec_2d centroid, - T radius) +vec_2d __device__ generate_ring_coordinate(std::size_t point_local_idx, + std::size_t num_sides, + vec_2d centroid, + T radius) { // Overrides last coordinate to make sure ring is closed. if (point_local_idx == num_sides) return vec_2d{centroid.x + radius, centroid.y}; @@ -174,10 +187,10 @@ void __global__ generate_multipolygon_array_coordinates(MultipolygonRange multip params.hole_radius()); if (ring_local_idx == 0) // Generate coordinate for shell - multipolygons.point_begin()[idx] = generate_polygon_coordinate( + multipolygons.point_begin()[idx] = generate_ring_coordinate( point_local_idx, params.num_sides_per_ring, centroid, params.radius); else // Generate coordinate for holes - multipolygons.point_begin()[idx] = generate_polygon_coordinate( + multipolygons.point_begin()[idx] = generate_ring_coordinate( point_local_idx, params.num_sides_per_ring, centroid, params.hole_radius()); } } From d910a7c4fbe0e236bf2e22372cd494dad5725e54 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Sun, 19 Mar 2023 23:10:06 -0700 Subject: [PATCH 39/60] enable value parameterized test --- .../utility_test/test_geometry_generators.cu | 91 +++++++++---------- 1 file changed, 41 insertions(+), 50 deletions(-) diff --git a/cpp/tests/utility_test/test_geometry_generators.cu b/cpp/tests/utility_test/test_geometry_generators.cu index 359c87f92..223eb6c73 100644 --- a/cpp/tests/utility_test/test_geometry_generators.cu +++ b/cpp/tests/utility_test/test_geometry_generators.cu @@ -279,53 +279,44 @@ TYPED_TEST(GeometryFactoryTest, multipolygonarray_basic_radius) CUSPATIAL_RUN_TEST(this->run, params, std::move(expected), abs_error(params.radius)); } -// struct GeometryFactoryCountVerificationTest : public BaseFixtureWithParam, -// float> { -// void run(multipolygon_generator_parameter params) -// { -// auto got = generate_multipolygon_array(params, stream()); - -// auto [got_geometry_offsets, got_part_offsets, got_ring_offsets, got_coordinates] = -// got.to_host(); - -// EXPECT_EQ(got_geometry_offsets.size(), params.num_multipolygons + 1); -// EXPECT_EQ(got_part_offsets.size(), params.num_polygons() + 1); -// EXPECT_EQ(got_ring_offsets.size(), params.num_rings() + 1); -// EXPECT_EQ(got_coordinates.size(), params.num_coords()); -// } -// }; - -// TEST_P(GeometryFactoryCountVerificationTest, CountsVerification) -// { -// // Structured binding unsupported by Gtest -// std::size_t num_multipolygons = std::get<0>(GetParam()); -// std::size_t num_polygons_per_multipolygon = std::get<1>(GetParam()); -// std::size_t num_holes_per_polygon = std::get<2>(GetParam()); -// std::size_t num_sides_per_ring = std::get<3>(GetParam()); -// vec_2d centroid = std::get<4>(GetParam()); -// float radius = std::get<5>(GetParam()); - -// auto params = multipolygon_generator_parameter{num_multipolygons, -// num_polygons_per_multipolygon, -// num_holes_per_polygon, -// num_sides_per_ring, -// centroid, -// radius}; -// CUSPATIAL_RUN_TEST(this->run, params); -// } - -// INSTANTIATE_TEST_SUITE_P( -// GeometryFactoryCountVerificationTests, -// GeometryFactoryCountVerificationTest, -// ::testing::Combine( -// ::testing::Range(1 << 4, 1 << 10, 2), // num_multipolygons -// ::testing::Range(1 << 4, 1 << 10, 2), // num_polygons_per_multipolygon -// ::testing::Range(1 << 4, 1 << 10, 2), // num_holes_per_polygon -// ::testing::Range(1 << 4, 1 << 10, 2), // num_sides_per_ring -// ::testing::Values(vec_2d{0.0, 0.0}, vec_2d{1.0, 5.0}), // centroid -// ::testing::Values(1.0, 6.0) // radius -// )); +struct GeometryFactoryCountVerificationTest + : public BaseFixtureWithParam { + void run(multipolygon_generator_parameter params) + { + auto got = generate_multipolygon_array(params, stream()); + + auto [got_geometry_offsets, got_part_offsets, got_ring_offsets, got_coordinates] = + got.to_host(); + + EXPECT_EQ(got_geometry_offsets.size(), params.num_multipolygons + 1); + EXPECT_EQ(got_part_offsets.size(), params.num_polygons() + 1); + EXPECT_EQ(got_ring_offsets.size(), params.num_rings() + 1); + EXPECT_EQ(got_coordinates.size(), params.num_coords()); + } +}; + +TEST_P(GeometryFactoryCountVerificationTest, CountsVerification) +{ + // Structured binding unsupported by Gtest + std::size_t num_multipolygons = std::get<0>(GetParam()); + std::size_t num_polygons_per_multipolygon = std::get<1>(GetParam()); + std::size_t num_holes_per_polygon = std::get<2>(GetParam()); + std::size_t num_sides_per_ring = std::get<3>(GetParam()); + + auto params = multipolygon_generator_parameter{num_multipolygons, + num_polygons_per_multipolygon, + num_holes_per_polygon, + num_sides_per_ring, + vec_2d{0.0, 0.0}, + 1.0}; + CUSPATIAL_RUN_TEST(this->run, params); +} + +INSTANTIATE_TEST_SUITE_P( + GeometryFactoryCountVerificationTests, + GeometryFactoryCountVerificationTest, + ::testing::Combine(::testing::Values(1, 1000), // num_multipolygons + ::testing::Values(1, 30), // num_polygons_per_multipolygon + ::testing::Values(0, 100), // num_holes_per_polygon + ::testing::Values(3, 100) // num_sides_per_ring + )); From bd6a4ebe2324a22beec7bdfec9f10bad1a8378aa Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Sun, 19 Mar 2023 23:12:41 -0700 Subject: [PATCH 40/60] revert distance benchmark --- .../pairwise_point_polygon_distance.cu | 85 ------------------- 1 file changed, 85 deletions(-) delete mode 100644 cpp/benchmarks/pairwise_point_polygon_distance.cu diff --git a/cpp/benchmarks/pairwise_point_polygon_distance.cu b/cpp/benchmarks/pairwise_point_polygon_distance.cu deleted file mode 100644 index 476f45bc3..000000000 --- a/cpp/benchmarks/pairwise_point_polygon_distance.cu +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2023, NVIDIA CORPORATION. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "utility/geometry_factory.cuh" - -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -#include - -using namespace cuspatial; - -template -void pairwise_point_polygon_distance_benchmark(nvbench::state& state, nvbench::type_list) -{ - // TODO: to be replaced by nvbench fixture once it's ready - cuspatial::rmm_pool_raii rmm_pool; - - auto const num_string_pairs{state.get_int64("NumStrings")}; - auto const num_segments_per_string{state.get_int64("NumSegmentsPerString")}; - - auto [ls1, ls1_offset] = - generate_linestring(num_string_pairs, num_segments_per_string, 1, {0, 0}); - auto [ls2, ls2_offset] = - generate_linestring(num_string_pairs, num_segments_per_string, 1, {100, 100}); - - auto distances = rmm::device_vector(ls1.size()); - auto out_it = distances.begin(); - - auto multilinestrings1 = make_multilinestring_range(1, - thrust::make_counting_iterator(0), - num_string_pairs, - ls1_offset.begin(), - ls1.size(), - ls1.begin()); - auto multilinestrings2 = make_multilinestring_range(1, - thrust::make_counting_iterator(0), - num_string_pairs, - ls2_offset.begin(), - ls2.size(), - ls2.begin()); - auto const total_points = ls1.size() + ls2.size(); - - state.add_element_count(num_string_pairs, "LineStringPairs"); - state.add_element_count(total_points, "NumPoints"); - state.add_global_memory_reads(total_points * 2, "CoordinatesDataSize"); - state.add_global_memory_reads(num_string_pairs * 2, "OffsetsDataSize"); - state.add_global_memory_writes(num_string_pairs); - - state.exec(nvbench::exec_tag::sync, - [&multilinestrings1, &multilinestrings2, &out_it](nvbench::launch& launch) { - pairwise_linestring_distance(multilinestrings1, multilinestrings2, out_it); - }); -} - -using floating_point_types = nvbench::type_list; -NVBENCH_BENCH_TYPES(pairwise_linestring_distance_benchmark, NVBENCH_TYPE_AXES(floating_point_types)) - .set_type_axes_names({"CoordsType"}) - .add_int64_axis("NumStrings", {1'000, 10'000, 100'000}) - .add_int64_axis("NumSegmentsPerString", {10, 100, 1'000}); From 9e7446841a1b8e74237c143173541789a268766e Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Sun, 19 Mar 2023 23:16:45 -0700 Subject: [PATCH 41/60] mixin doc --- cpp/include/cuspatial_test/base_fixture.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cpp/include/cuspatial_test/base_fixture.hpp b/cpp/include/cuspatial_test/base_fixture.hpp index fb8b3876a..004a25c3c 100644 --- a/cpp/include/cuspatial_test/base_fixture.hpp +++ b/cpp/include/cuspatial_test/base_fixture.hpp @@ -22,6 +22,9 @@ namespace cuspatial { namespace test { +/** + * @brief Uses CRTP to supply rmm resources for test fixtures. + */ template class RMMResourceMixin { rmm::mr::device_memory_resource* _mr{rmm::mr::get_current_device_resource()}; From fabc5ad6c7d3e7972f47947c58dd1fd78f3c775c Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 20 Mar 2023 14:17:08 -0700 Subject: [PATCH 42/60] add point-polygon distance benchmark --- cpp/benchmarks/CMakeLists.txt | 3 +- .../cuspatial_test/geometry_generator.cuh | 56 +++++++++++++++++++ cpp/include/cuspatial_test/random.cuh | 19 +++++-- .../cuspatial_test/vector_factories.cuh | 29 +++++++++- .../spatial/point_distance_test.cu | 9 ++- 5 files changed, 104 insertions(+), 12 deletions(-) diff --git a/cpp/benchmarks/CMakeLists.txt b/cpp/benchmarks/CMakeLists.txt index 48e66a0a7..09e0292f6 100644 --- a/cpp/benchmarks/CMakeLists.txt +++ b/cpp/benchmarks/CMakeLists.txt @@ -83,7 +83,8 @@ ConfigureBench(HAUSDORFF_BENCH hausdorff_benchmark.cpp) ConfigureNVBench(DISTANCES_BENCH - pairwise_linestring_distance.cu) + pairwise_linestring_distance.cu + pairwise_point_polygon_distance.cu) ConfigureNVBench(POINTS_IN_RANGE_BENCH points_in_range.cu) diff --git a/cpp/include/cuspatial_test/geometry_generator.cuh b/cpp/include/cuspatial_test/geometry_generator.cuh index 376b0817c..07cd0ae75 100644 --- a/cpp/include/cuspatial_test/geometry_generator.cuh +++ b/cpp/include/cuspatial_test/geometry_generator.cuh @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include #include @@ -251,5 +252,60 @@ auto generate_multipolygon_array(multipolygon_generator_parameter params, std::move(coordinates)); } +/** + * @brief Struct to store the parameters of the multipoint aray + * + * @tparam T Type of the coordinates + */ +template +struct multipoint_generator_parameter { + using element_t = T; + + std::size_t num_multipoints; + std::size_t num_points_per_multipoints; + vec_2d lower_left; + vec_2d upper_right; + + CUSPATIAL_HOST_DEVICE std::size_t num_points() + { + return num_multipoints * num_points_per_multipoints; + } +}; + +/** + * @brief Helper to generate random multipoints within a range + * + * @tparam T The floating point type for the coordinates + * @param params Parameters to specify for the multipoints + * @param stream The CUDA stream to use for device memory operations and kernel launches + * @return a cuspatial::test::multipoint_array object + */ +template +auto generate_multipoint_array(multipoint_generator_parameter params, + rmm::cuda_stream_view stream) +{ + rmm::device_uvector> coordinates(params.num_points(), stream); + rmm::device_uvector offsets(params.num_multipoints + 1, stream); + + thrust::sequence(rmm::exec_policy(stream), + offsets.begin(), + offsets.end(), + std::size_t{0}, + params.num_points_per_multipoints); + + auto engine_x = deterministic_engine(params.num_points()); + auto engine_y = deterministic_engine(2 * params.num_points()); + + auto x_dist = make_uniform_dist(params.lower_left.x, params.upper_right.x); + auto y_dist = make_uniform_dist(params.lower_left.y, params.upper_right.y); + + auto point_gen = + point_generator(params.lower_left, params.upper_right, engine_x, engine_y, x_dist, y_dist); + + thrust::tabulate(rmm::exec_policy(stream), coordinates.begin(), coordinates.end(), point_gen); + + return make_multipoints_array(std::move(offsets), std::move(coordinates)); +} + } // namespace test } // namespace cuspatial diff --git a/cpp/include/cuspatial_test/random.cuh b/cpp/include/cuspatial_test/random.cuh index 5d8e2b29e..f880f7e13 100644 --- a/cpp/include/cuspatial_test/random.cuh +++ b/cpp/include/cuspatial_test/random.cuh @@ -33,6 +33,8 @@ #include #include +using namespace cuspatial; + /** * @brief Identifies a probability distribution type. */ @@ -150,14 +152,21 @@ struct value_generator { template struct point_generator { using Cart2D = cuspatial::vec_2d; - value_generator vgen; - - point_generator(T lower_bound, T upper_bound, thrust::minstd_rand& engine, Generator gen) - : vgen(lower_bound, upper_bound, engine, gen) + value_generator vgenx; + value_generator vgeny; + + point_generator(vec_2d lower_left, + vec_2d upper_right, + thrust::minstd_rand& engine_x, + thrust::minstd_rand& engine_y, + Generator gen_x, + Generator gen_y) + : vgenx(lower_left.x, upper_right.x, engine_x, gen_x), + vgeny(lower_left.y, upper_right.y, engine_y, gen_y) { } - __device__ Cart2D operator()(size_t n) { return {vgen(n), vgen(n)}; } + __device__ Cart2D operator()(size_t n) { return {vgenx(n), vgeny(n)}; } }; /** diff --git a/cpp/include/cuspatial_test/vector_factories.cuh b/cpp/include/cuspatial_test/vector_factories.cuh index 2ca1270fb..50cf02d2d 100644 --- a/cpp/include/cuspatial_test/vector_factories.cuh +++ b/cpp/include/cuspatial_test/vector_factories.cuh @@ -270,11 +270,22 @@ auto make_multilinestring_array(std::initializer_list geometry_inl, template class multipoint_array { public: - multipoint_array(GeometryArray geometry_offsets_array, CoordinateArray coordinate_array) + using geometry_t = typename GeometryArray::value_type; + using coord_t = typename CoordinateArray::value_type; + + multipoint_array(thrust::device_vector geometry_offsets_array, + thrust::device_vector coordinate_array) : _geometry_offsets(geometry_offsets_array), _coordinates(coordinate_array) { } + multipoint_array(rmm::device_uvector&& geometry_offsets_array, + rmm::device_uvector&& coordinate_array) + : _geometry_offsets(std::move(geometry_offsets_array)), + _coordinates(std::move(coordinate_array)) + { + } + /// Return the number of multipoints auto size() { return _geometry_offsets.size() - 1; } @@ -322,8 +333,20 @@ auto make_multipoints_array(std::initializer_list(offsets), - rmm::device_vector>(coordinates)}; + return multipoint_array, rmm::device_vector>>{ + rmm::device_vector(offsets), rmm::device_vector>(coordinates)}; +} + +/** + * @brief Factory method to construct multipoint array by moving the offsets and coordinates from + * `rmm::device_uvector`. + */ +template +auto make_multipoints_array(rmm::device_uvector geometry_offsets, + rmm::device_uvector> coords) +{ + return multipoint_array, rmm::device_uvector>>{ + std::move(geometry_offsets), std::move(coords)}; } } // namespace test diff --git a/cpp/tests/experimental/spatial/point_distance_test.cu b/cpp/tests/experimental/spatial/point_distance_test.cu index a6d873a27..e84053ccf 100644 --- a/cpp/tests/experimental/spatial/point_distance_test.cu +++ b/cpp/tests/experimental/spatial/point_distance_test.cu @@ -57,9 +57,12 @@ struct PairwisePointDistanceTest : public ::testing::Test { std::size_t seed, rmm::cuda_stream_view stream = rmm::cuda_stream_default) { - auto engine = deterministic_engine(0); - auto uniform = make_normal_dist(0.0, 1.0); - auto pgen = point_generator(T{0.0}, T{1.0}, engine, uniform); + auto engine_x = deterministic_engine(0); + auto engine_y = deterministic_engine(0); + auto uniform_x = make_normal_dist(0.0, 1.0); + auto uniform_y = make_normal_dist(0.0, 1.0); + auto pgen = + point_generator(vec_2d{0, 0}, vec_2d{1, 1}, engine_x, engine_y, uniform_x, uniform_y); rmm::device_vector> points(num_points); auto counting_iter = thrust::make_counting_iterator(seed); thrust::transform( From de5a66312bf5648393edbe64ecfcc03d6528e7e9 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 20 Mar 2023 17:12:16 -0700 Subject: [PATCH 43/60] add benchmark file --- .../pairwise_point_polygon_distance.cu | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 cpp/benchmarks/pairwise_point_polygon_distance.cu diff --git a/cpp/benchmarks/pairwise_point_polygon_distance.cu b/cpp/benchmarks/pairwise_point_polygon_distance.cu new file mode 100644 index 000000000..dad757833 --- /dev/null +++ b/cpp/benchmarks/pairwise_point_polygon_distance.cu @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#include +#include + +#include +#include + +using namespace cuspatial; +using namespace cuspatial::test; + +template +void pairwise_point_polygon_distance_benchmark(nvbench::state& state, nvbench::type_list) +{ + // TODO: to be replaced by nvbench fixture once it's ready + cuspatial::rmm_pool_raii rmm_pool; + rmm::cuda_stream_view stream{rmm::cuda_stream_default}; + + auto const num_pairs{static_cast(state.get_int64("num_pairs"))}; + + auto const num_polygons_per_multipolygon{ + static_cast(state.get_int64("num_polygons_per_multipolygon"))}; + auto const num_holes_per_polygon{ + static_cast(state.get_int64("num_holes_per_polygon"))}; + auto const num_sides_per_ring{static_cast(state.get_int64("num_sides_per_ring"))}; + + auto const num_points_per_multipoint{ + static_cast(state.get_int64("num_points_per_multipoint"))}; + + auto mpoly_generator_param = multipolygon_generator_parameter{ + num_pairs, num_polygons_per_multipolygon, num_holes_per_polygon, num_sides_per_ring}; + + auto mpoint_generator_param = multipoint_generator_parameter{ + num_pairs, num_points_per_multipoint, vec_2d{-1, -1}, vec_2d{0, 0}}; + + auto multipolygons = generate_multipolygon_array(mpoly_generator_param, stream); + auto multipoints = generate_multipoint_array(mpoint_generator_param, stream); + + auto distances = rmm::device_vector(num_pairs); + auto out_it = distances.begin(); + + auto mpoly_view = multipolygons.range(); + auto mpoint_view = multipoints.range(); + + state.add_element_count(num_pairs, "NumPairs"); + state.add_element_count(mpoly_generator_param.num_polygons(), "NumPolygons"); + state.add_element_count(mpoly_generator_param.num_rings(), "NumRings"); + state.add_element_count(mpoly_generator_param.num_coords(), "NumPoints (in mpoly)"); + state.add_element_count(mpoly_generator_param.num_coords() * mpoly_generator_param.num_rings() * + mpoly_generator_param.num_polygons(), + "Multipolygon Complexity"); + state.add_element_count(mpoint_generator_param.num_points(), "NumPoints (in multipoints)"); + + state.add_global_memory_reads( + mpoly_generator_param.num_coords() + mpoint_generator_param.num_points(), + "CoordinatesReadSize"); + state.add_global_memory_reads( + (mpoly_generator_param.num_rings() + 1) + (mpoly_generator_param.num_polygons() + 1) + + (mpoly_generator_param.num_multipolygons + 1) + (mpoint_generator_param.num_multipoints + 1), + "OffsetsDataSize"); + + state.add_global_memory_writes(num_pairs); + + state.exec(nvbench::exec_tag::sync, + [&mpoly_view, &mpoint_view, &out_it, &stream](nvbench::launch& launch) { + pairwise_point_polygon_distance(mpoint_view, mpoly_view, out_it, stream); + }); +} + +using floating_point_types = nvbench::type_list; + +// Benchmark scalability with simple multipolygon (3 sides, 0 hole, 1 poly) +// NVBENCH_BENCH_TYPES(pairwise_point_polygon_distance_benchmark, +// NVBENCH_TYPE_AXES(floating_point_types)) +// .set_type_axes_names({"CoordsType"}) +// .add_int64_axis("num_pairs", {1, 1'00, 10'000, 1'000'000, 100'000'000}) +// .add_int64_axis("num_polygons_per_multipolygon", {1}) +// .add_int64_axis("num_holes_per_polygon", {0}) +// .add_int64_axis("num_sides_per_ring", {3}) +// .add_int64_axis("num_points_per_multipoint", {1}); + +// Benchmark scalability with complex multipolygon (100 sides, 10 holes, 3 polys) +// NVBENCH_BENCH_TYPES(pairwise_point_polygon_distance_benchmark, +// NVBENCH_TYPE_AXES(floating_point_types)) +// .set_type_axes_names({"CoordsType"}) +// .add_int64_axis("num_pairs", {1'000, 10'000, 100'000, 1'000'000}) +// .add_int64_axis("num_polygons_per_multipolygon", {3}) +// .add_int64_axis("num_holes_per_polygon", {10}) +// .add_int64_axis("num_sides_per_ring", {100}) +// .add_int64_axis("num_points_per_multipoint", {1}); + +// // Benchmark impact of rings (100K pairs, 1 polygon, 3 sides) +// NVBENCH_BENCH_TYPES(pairwise_point_polygon_distance_benchmark, +// NVBENCH_TYPE_AXES(floating_point_types)) +// .set_type_axes_names({"CoordsType"}) +// .add_int64_axis("num_pairs", {100'000}) +// .add_int64_axis("num_polygons_per_multipolygon", {1}) +// .add_int64_axis("num_holes_per_polygon", {0, 10, 100}) +// .add_int64_axis("num_sides_per_ring", {3}) +// .add_int64_axis("num_points_per_multipoint", {1}); + +// Benchmark impact of rings (1M pairs, 1 polygon, 0 holes, 3 sides) +NVBENCH_BENCH_TYPES(pairwise_point_polygon_distance_benchmark, + NVBENCH_TYPE_AXES(floating_point_types)) + .set_type_axes_names({"CoordsType"}) + .add_int64_axis("num_pairs", {1'000}) + .add_int64_axis("num_polygons_per_multipolygon", {1}) + .add_int64_axis("num_holes_per_polygon", {0}) + .add_int64_axis("num_sides_per_ring", {3}) + .add_int64_axis("num_points_per_multipoint", {100, 1'000, 10'000, 100'000}); From 5c67a802af04061d17350f2cd4f9813633ad4d99 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 20 Mar 2023 17:58:09 -0700 Subject: [PATCH 44/60] address review comments --- .../ranges/multipolygon_range.cuh | 10 ++++------ .../cuspatial_test/geometry_generator.cuh | 20 +++++++++---------- .../utility_test/test_float_equivalent.cu | 2 +- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh index 69602f846..268428cef 100644 --- a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh @@ -114,18 +114,16 @@ class multipolygon_range { template CUSPATIAL_HOST_DEVICE auto geometry_idx_from_segment_idx(IndexType segment_idx); - /// Given the index of a point, return the ring index - /// where the point locates. + /// Given the index of a point, return the index of the ring that contains the point. template CUSPATIAL_HOST_DEVICE auto ring_idx_from_point_idx(IndexType point_idx); - /// Given the index of a ring, return the part (polygon) index - /// where the ring locates. + /// Given the index of a ring, return the index of the part (polygon) that contains the point. template CUSPATIAL_HOST_DEVICE auto part_idx_from_ring_idx(IndexType ring_idx); - /// Given the index of a part (polygon), return the geometry (multipolygon) index - /// where the polygon locates. + /// Given the index of a part (polygon), return the index of the geometry (multipolygon) that + /// contains the part. template CUSPATIAL_HOST_DEVICE auto geometry_idx_from_part_idx(IndexType part_idx); diff --git a/cpp/include/cuspatial_test/geometry_generator.cuh b/cpp/include/cuspatial_test/geometry_generator.cuh index 376b0817c..209b4c545 100644 --- a/cpp/include/cuspatial_test/geometry_generator.cuh +++ b/cpp/include/cuspatial_test/geometry_generator.cuh @@ -31,8 +31,6 @@ namespace cuspatial { namespace test { -constexpr double PI = 3.14159265358979323846; - /** * @brief Struct to store the parameters of the multipolygon array generator * @@ -45,7 +43,7 @@ struct multipolygon_generator_parameter { std::size_t num_multipolygons; std::size_t num_polygons_per_multipolygon; std::size_t num_holes_per_polygon; - std::size_t num_sides_per_ring; + std::size_t num_edges_per_ring; vec_2d centroid; T radius; @@ -55,7 +53,7 @@ struct multipolygon_generator_parameter { } CUSPATIAL_HOST_DEVICE std::size_t num_rings() { return num_polygons() * num_rings_per_polygon(); } CUSPATIAL_HOST_DEVICE std::size_t num_coords() { return num_rings() * num_vertices_per_ring(); } - CUSPATIAL_HOST_DEVICE std::size_t num_vertices_per_ring() { return num_sides_per_ring + 1; } + CUSPATIAL_HOST_DEVICE std::size_t num_vertices_per_ring() { return num_edges_per_ring + 1; } CUSPATIAL_HOST_DEVICE std::size_t num_rings_per_polygon() { return num_holes_per_polygon + 1; } CUSPATIAL_HOST_DEVICE T hole_radius() { return radius / (num_holes_per_polygon + 1); } }; @@ -67,22 +65,22 @@ struct multipolygon_generator_parameter { * Each step has equal angles. * * @tparam T Type of coordinate - * @param point_local_idx Local point of the point - * @param num_sides Number of sides of the polygon + * @param point_local_idx Local index of the point + * @param num_edges Number of sides of the polygon * @param centroid Centroid of the ring * @param radius Radius of the ring * @return Coordinate of the point */ template vec_2d __device__ generate_ring_coordinate(std::size_t point_local_idx, - std::size_t num_sides, + std::size_t num_edges, vec_2d centroid, T radius) { // Overrides last coordinate to make sure ring is closed. - if (point_local_idx == num_sides) return vec_2d{centroid.x + radius, centroid.y}; + if (point_local_idx == num_edges) return vec_2d{centroid.x + radius, centroid.y}; - T angle = 2.0 * PI * point_local_idx / num_sides; + T angle = (2.0 * M_PI * point_local_idx) / num_edges; return vec_2d{centroid.x + radius * cos(angle), centroid.y + radius * sin(angle)}; } @@ -188,10 +186,10 @@ void __global__ generate_multipolygon_array_coordinates(MultipolygonRange multip if (ring_local_idx == 0) // Generate coordinate for shell multipolygons.point_begin()[idx] = generate_ring_coordinate( - point_local_idx, params.num_sides_per_ring, centroid, params.radius); + point_local_idx, params.num_edges_per_ring, centroid, params.radius); else // Generate coordinate for holes multipolygons.point_begin()[idx] = generate_ring_coordinate( - point_local_idx, params.num_sides_per_ring, centroid, params.hole_radius()); + point_local_idx, params.num_edges_per_ring, centroid, params.hole_radius()); } } diff --git a/cpp/tests/utility_test/test_float_equivalent.cu b/cpp/tests/utility_test/test_float_equivalent.cu index d92de9299..2f7c1070f 100644 --- a/cpp/tests/utility_test/test_float_equivalent.cu +++ b/cpp/tests/utility_test/test_float_equivalent.cu @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2022-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From eab8089608c09e7becd47ca867aeb688f9e8f34d Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 20 Mar 2023 19:23:15 -0700 Subject: [PATCH 45/60] update sides->edge --- .../pairwise_point_polygon_distance.cu | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cpp/benchmarks/pairwise_point_polygon_distance.cu b/cpp/benchmarks/pairwise_point_polygon_distance.cu index dad757833..4016b03ab 100644 --- a/cpp/benchmarks/pairwise_point_polygon_distance.cu +++ b/cpp/benchmarks/pairwise_point_polygon_distance.cu @@ -41,13 +41,13 @@ void pairwise_point_polygon_distance_benchmark(nvbench::state& state, nvbench::t static_cast(state.get_int64("num_polygons_per_multipolygon"))}; auto const num_holes_per_polygon{ static_cast(state.get_int64("num_holes_per_polygon"))}; - auto const num_sides_per_ring{static_cast(state.get_int64("num_sides_per_ring"))}; + auto const num_edges_per_ring{static_cast(state.get_int64("num_edges_per_ring"))}; auto const num_points_per_multipoint{ static_cast(state.get_int64("num_points_per_multipoint"))}; auto mpoly_generator_param = multipolygon_generator_parameter{ - num_pairs, num_polygons_per_multipolygon, num_holes_per_polygon, num_sides_per_ring}; + num_pairs, num_polygons_per_multipolygon, num_holes_per_polygon, num_edges_per_ring}; auto mpoint_generator_param = multipoint_generator_parameter{ num_pairs, num_points_per_multipoint, vec_2d{-1, -1}, vec_2d{0, 0}}; @@ -95,7 +95,7 @@ using floating_point_types = nvbench::type_list; // .add_int64_axis("num_pairs", {1, 1'00, 10'000, 1'000'000, 100'000'000}) // .add_int64_axis("num_polygons_per_multipolygon", {1}) // .add_int64_axis("num_holes_per_polygon", {0}) -// .add_int64_axis("num_sides_per_ring", {3}) +// .add_int64_axis("num_edges_per_ring", {3}) // .add_int64_axis("num_points_per_multipoint", {1}); // Benchmark scalability with complex multipolygon (100 sides, 10 holes, 3 polys) @@ -105,7 +105,7 @@ using floating_point_types = nvbench::type_list; // .add_int64_axis("num_pairs", {1'000, 10'000, 100'000, 1'000'000}) // .add_int64_axis("num_polygons_per_multipolygon", {3}) // .add_int64_axis("num_holes_per_polygon", {10}) -// .add_int64_axis("num_sides_per_ring", {100}) +// .add_int64_axis("num_edges_per_ring", {100}) // .add_int64_axis("num_points_per_multipoint", {1}); // // Benchmark impact of rings (100K pairs, 1 polygon, 3 sides) @@ -115,15 +115,15 @@ using floating_point_types = nvbench::type_list; // .add_int64_axis("num_pairs", {100'000}) // .add_int64_axis("num_polygons_per_multipolygon", {1}) // .add_int64_axis("num_holes_per_polygon", {0, 10, 100}) -// .add_int64_axis("num_sides_per_ring", {3}) +// .add_int64_axis("num_edges_per_ring", {3}) // .add_int64_axis("num_points_per_multipoint", {1}); // Benchmark impact of rings (1M pairs, 1 polygon, 0 holes, 3 sides) NVBENCH_BENCH_TYPES(pairwise_point_polygon_distance_benchmark, NVBENCH_TYPE_AXES(floating_point_types)) .set_type_axes_names({"CoordsType"}) - .add_int64_axis("num_pairs", {1'000}) + .add_int64_axis("num_pairs", {500}) .add_int64_axis("num_polygons_per_multipolygon", {1}) .add_int64_axis("num_holes_per_polygon", {0}) - .add_int64_axis("num_sides_per_ring", {3}) - .add_int64_axis("num_points_per_multipoint", {100, 1'000, 10'000, 100'000}); + .add_int64_axis("num_edges_per_ring", {3}) + .add_int64_axis("num_points_per_multipoint", {50, 5'00, 5'000}); From 11439778e5a29a3e5021ed19cbcdcdf88281a65c Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 22 Mar 2023 17:20:47 -0700 Subject: [PATCH 46/60] add nvtx range and fix fixtures --- .../pairwise_point_polygon_distance.cu | 61 ++++++++++--------- cpp/include/cuspatial/detail/nvtx/ranges.hpp | 10 +-- .../detail/point_polygon_distance.cuh | 3 + 3 files changed, 41 insertions(+), 33 deletions(-) diff --git a/cpp/benchmarks/pairwise_point_polygon_distance.cu b/cpp/benchmarks/pairwise_point_polygon_distance.cu index 4016b03ab..e143f0ea3 100644 --- a/cpp/benchmarks/pairwise_point_polygon_distance.cu +++ b/cpp/benchmarks/pairwise_point_polygon_distance.cu @@ -65,8 +65,9 @@ void pairwise_point_polygon_distance_benchmark(nvbench::state& state, nvbench::t state.add_element_count(mpoly_generator_param.num_polygons(), "NumPolygons"); state.add_element_count(mpoly_generator_param.num_rings(), "NumRings"); state.add_element_count(mpoly_generator_param.num_coords(), "NumPoints (in mpoly)"); - state.add_element_count(mpoly_generator_param.num_coords() * mpoly_generator_param.num_rings() * - mpoly_generator_param.num_polygons(), + state.add_element_count(static_cast(mpoly_generator_param.num_coords() * + mpoly_generator_param.num_rings() * + mpoly_generator_param.num_polygons()), "Multipolygon Complexity"); state.add_element_count(mpoint_generator_param.num_points(), "NumPoints (in multipoints)"); @@ -89,41 +90,45 @@ void pairwise_point_polygon_distance_benchmark(nvbench::state& state, nvbench::t using floating_point_types = nvbench::type_list; // Benchmark scalability with simple multipolygon (3 sides, 0 hole, 1 poly) -// NVBENCH_BENCH_TYPES(pairwise_point_polygon_distance_benchmark, -// NVBENCH_TYPE_AXES(floating_point_types)) -// .set_type_axes_names({"CoordsType"}) -// .add_int64_axis("num_pairs", {1, 1'00, 10'000, 1'000'000, 100'000'000}) -// .add_int64_axis("num_polygons_per_multipolygon", {1}) -// .add_int64_axis("num_holes_per_polygon", {0}) -// .add_int64_axis("num_edges_per_ring", {3}) -// .add_int64_axis("num_points_per_multipoint", {1}); +NVBENCH_BENCH_TYPES(pairwise_point_polygon_distance_benchmark, + NVBENCH_TYPE_AXES(floating_point_types)) + .set_type_axes_names({"CoordsType"}) + .add_int64_axis("num_pairs", {1, 1'00, 10'000, 1'000'000, 100'000'000}) + .add_int64_axis("num_polygons_per_multipolygon", {1}) + .add_int64_axis("num_holes_per_polygon", {0}) + .add_int64_axis("num_edges_per_ring", {3}) + .add_int64_axis("num_points_per_multipoint", {1}) + .set_name("point_polygon_distance_benchmark_simple_polygon"); // Benchmark scalability with complex multipolygon (100 sides, 10 holes, 3 polys) -// NVBENCH_BENCH_TYPES(pairwise_point_polygon_distance_benchmark, -// NVBENCH_TYPE_AXES(floating_point_types)) -// .set_type_axes_names({"CoordsType"}) -// .add_int64_axis("num_pairs", {1'000, 10'000, 100'000, 1'000'000}) -// .add_int64_axis("num_polygons_per_multipolygon", {3}) -// .add_int64_axis("num_holes_per_polygon", {10}) -// .add_int64_axis("num_edges_per_ring", {100}) -// .add_int64_axis("num_points_per_multipoint", {1}); +NVBENCH_BENCH_TYPES(pairwise_point_polygon_distance_benchmark, + NVBENCH_TYPE_AXES(floating_point_types)) + .set_type_axes_names({"CoordsType"}) + .add_int64_axis("num_pairs", {1'000, 10'000, 100'000, 1'000'000}) + .add_int64_axis("num_polygons_per_multipolygon", {2}) + .add_int64_axis("num_holes_per_polygon", {3}) + .add_int64_axis("num_edges_per_ring", {50}) + .add_int64_axis("num_points_per_multipoint", {1}) + .set_name("point_polygon_distance_benchmark_complex_polygon"); // // Benchmark impact of rings (100K pairs, 1 polygon, 3 sides) -// NVBENCH_BENCH_TYPES(pairwise_point_polygon_distance_benchmark, -// NVBENCH_TYPE_AXES(floating_point_types)) -// .set_type_axes_names({"CoordsType"}) -// .add_int64_axis("num_pairs", {100'000}) -// .add_int64_axis("num_polygons_per_multipolygon", {1}) -// .add_int64_axis("num_holes_per_polygon", {0, 10, 100}) -// .add_int64_axis("num_edges_per_ring", {3}) -// .add_int64_axis("num_points_per_multipoint", {1}); +NVBENCH_BENCH_TYPES(pairwise_point_polygon_distance_benchmark, + NVBENCH_TYPE_AXES(floating_point_types)) + .set_type_axes_names({"CoordsType"}) + .add_int64_axis("num_pairs", {10'000}) + .add_int64_axis("num_polygons_per_multipolygon", {1}) + .add_int64_axis("num_holes_per_polygon", {0, 10, 100, 1000}) + .add_int64_axis("num_edges_per_ring", {3}) + .add_int64_axis("num_points_per_multipoint", {1}) + .set_name("point_polygon_distance_benchmark_ring_numbers"); // Benchmark impact of rings (1M pairs, 1 polygon, 0 holes, 3 sides) NVBENCH_BENCH_TYPES(pairwise_point_polygon_distance_benchmark, NVBENCH_TYPE_AXES(floating_point_types)) .set_type_axes_names({"CoordsType"}) - .add_int64_axis("num_pairs", {500}) + .add_int64_axis("num_pairs", {100}) .add_int64_axis("num_polygons_per_multipolygon", {1}) .add_int64_axis("num_holes_per_polygon", {0}) .add_int64_axis("num_edges_per_ring", {3}) - .add_int64_axis("num_points_per_multipoint", {50, 5'00, 5'000}); + .add_int64_axis("num_points_per_multipoint", {50, 5'00, 5'000, 50'000, 500'000}) + .set_name("point_polygon_distance_benchmark_points_in_multipoint"); diff --git a/cpp/include/cuspatial/detail/nvtx/ranges.hpp b/cpp/include/cuspatial/detail/nvtx/ranges.hpp index 1757ae1e5..4e9517378 100644 --- a/cpp/include/cuspatial/detail/nvtx/ranges.hpp +++ b/cpp/include/cuspatial/detail/nvtx/ranges.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, NVIDIA CORPORATION. + * Copyright (c) 2020-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,16 +20,16 @@ namespace cuspatial { /** - * @brief Tag type for libcudf's NVTX domain. + * @brief Tag type for libcuspatial's NVTX domain. */ struct libcuspatial_domain { - static constexpr char const* name{"libcuspatial"}; ///< Name of the libcudf domain + static constexpr char const* name{"libcuspatial"}; ///< Name of the libcuspatial domain }; /** - * @brief Alias for an NVTX range in the libcudf domain. + * @brief Alias for an NVTX range in the libcuspatial domain. */ -using thread_range = ::nvtx3::domain_thread_range; +using thread_range = ::nvtx3::domain_thread_range; } // namespace cuspatial diff --git a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh index 704f67a6b..58751b7d3 100644 --- a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -120,6 +121,8 @@ OutputIt pairwise_point_polygon_distance(MultiPointRange multipoints, OutputIt distances_first, rmm::cuda_stream_view stream) { + CUSPATIAL_FUNC_RANGE(); + using T = typename MultiPointRange::element_t; using index_t = typename MultiPointRange::index_t; From 1c1fbc48f7b7a366bd7c9e6a54aaffdc91ad499d Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 13 Apr 2023 17:20:40 -0700 Subject: [PATCH 47/60] rename plural->singular form --- cpp/include/cuspatial_test/geometry_generator.cuh | 2 +- cpp/include/cuspatial_test/vector_factories.cuh | 8 ++++---- .../experimental/find/find_duplicate_points_test.cu | 6 +++--- .../experimental/find/find_points_on_segments_test.cu | 2 +- .../spatial/point_polygon_distance_test.cu | 2 +- cpp/tests/utility_test/test_multipoint_factory.cu | 10 +++++----- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/cpp/include/cuspatial_test/geometry_generator.cuh b/cpp/include/cuspatial_test/geometry_generator.cuh index d3c3c56e5..4960b3394 100644 --- a/cpp/include/cuspatial_test/geometry_generator.cuh +++ b/cpp/include/cuspatial_test/geometry_generator.cuh @@ -302,7 +302,7 @@ auto generate_multipoint_array(multipoint_generator_parameter params, thrust::tabulate(rmm::exec_policy(stream), coordinates.begin(), coordinates.end(), point_gen); - return make_multipoints_array(std::move(offsets), std::move(coordinates)); + return make_multipoint_array(std::move(offsets), std::move(coordinates)); } } // namespace test diff --git a/cpp/include/cuspatial_test/vector_factories.cuh b/cpp/include/cuspatial_test/vector_factories.cuh index 50cf02d2d..c3de5a83d 100644 --- a/cpp/include/cuspatial_test/vector_factories.cuh +++ b/cpp/include/cuspatial_test/vector_factories.cuh @@ -309,17 +309,17 @@ class multipoint_array { * * Example: Construct an array of 2 multipoints, each with 2, 0, 1 points: * using P = vec_2d; - * make_multipoints_array({{P{0.0, 1.0}, P{2.0, 0.0}}, {}, {P{3.0, 4.0}}}); + * make_multipoint_array({{P{0.0, 1.0}, P{2.0, 0.0}}, {}, {P{3.0, 4.0}}}); * * Example: Construct an empty multilinestring array: - * make_multipoints_array({}); // Explicit parameter required to deduce type. + * make_multipoint_array({}); // Explicit parameter required to deduce type. * * @tparam T Type of coordinate * @param inl List of multipoints * @return multipoints_array object */ template -auto make_multipoints_array(std::initializer_list>> inl) +auto make_multipoint_array(std::initializer_list>> inl) { std::vector offsets{0}; std::transform(inl.begin(), inl.end(), std::back_inserter(offsets), [](auto multipoint) { @@ -342,7 +342,7 @@ auto make_multipoints_array(std::initializer_list -auto make_multipoints_array(rmm::device_uvector geometry_offsets, +auto make_multipoint_array(rmm::device_uvector geometry_offsets, rmm::device_uvector> coords) { return multipoint_array, rmm::device_uvector>>{ diff --git a/cpp/tests/experimental/find/find_duplicate_points_test.cu b/cpp/tests/experimental/find/find_duplicate_points_test.cu index 980e8282a..4c10c8929 100644 --- a/cpp/tests/experimental/find/find_duplicate_points_test.cu +++ b/cpp/tests/experimental/find/find_duplicate_points_test.cu @@ -41,7 +41,7 @@ TYPED_TEST(FindDuplicatePointsTest, simple) using T = TypeParam; using P = vec_2d; - auto multipoints = make_multipoints_array({{P{0.0, 0.0}, P{1.0, 0.0}, P{0.0, 0.0}}}); + auto multipoints = make_multipoint_array({{P{0.0, 0.0}, P{1.0, 0.0}, P{0.0, 0.0}}}); rmm::device_vector flags(multipoints.range().num_points()); std::vector expected_flags{0, 0, 1}; @@ -56,7 +56,7 @@ TYPED_TEST(FindDuplicatePointsTest, empty) using T = TypeParam; using P = vec_2d; - auto multipoints = make_multipoints_array({}); + auto multipoints = make_multipoint_array({}); rmm::device_vector flags(multipoints.range().num_points()); std::vector expected_flags{}; @@ -71,7 +71,7 @@ TYPED_TEST(FindDuplicatePointsTest, multi) using T = TypeParam; using P = vec_2d; - auto multipoints = make_multipoints_array( + auto multipoints = make_multipoint_array( {{P{0.0, 0.0}, P{1.0, 0.0}, P{0.0, 0.0}, P{0.0, 0.0}, P{1.0, 0.0}, P{2.0, 0.0}}, {P{5.0, 5.0}, P{5.0, 5.0}}, {P{0.0, 0.0}}}); diff --git a/cpp/tests/experimental/find/find_points_on_segments_test.cu b/cpp/tests/experimental/find/find_points_on_segments_test.cu index 7248d7650..8d184f601 100644 --- a/cpp/tests/experimental/find/find_points_on_segments_test.cu +++ b/cpp/tests/experimental/find/find_points_on_segments_test.cu @@ -41,7 +41,7 @@ struct FindPointOnSegmentTest : public BaseFixture { std::initializer_list> segments, std::initializer_list expected_flags) { - auto d_multipoints = make_multipoints_array(multipoints); + auto d_multipoints = make_multipoint_array(multipoints); auto d_segment_offsets = make_device_vector(segment_offsets); auto d_segments = make_device_vector>(segments); diff --git a/cpp/tests/experimental/spatial/point_polygon_distance_test.cu b/cpp/tests/experimental/spatial/point_polygon_distance_test.cu index bec557c4f..2e898181f 100644 --- a/cpp/tests/experimental/spatial/point_polygon_distance_test.cu +++ b/cpp/tests/experimental/spatial/point_polygon_distance_test.cu @@ -61,7 +61,7 @@ struct PairwisePointPolygonDistanceTest : public ::testing::Test { std::vector> const& multipolygon_coordinates, std::initializer_list expected) { - auto d_multipoints = make_multipoints_array(multipoints); + auto d_multipoints = make_multipoint_array(multipoints); auto d_multipolygons = make_multipolygon_array( range{multipolygon_geometry_offsets.begin(), multipolygon_geometry_offsets.end()}, range{multipolygon_part_offsets.begin(), multipolygon_part_offsets.end()}, diff --git a/cpp/tests/utility_test/test_multipoint_factory.cu b/cpp/tests/utility_test/test_multipoint_factory.cu index 5e1d77f88..5a5df91be 100644 --- a/cpp/tests/utility_test/test_multipoint_factory.cu +++ b/cpp/tests/utility_test/test_multipoint_factory.cu @@ -35,7 +35,7 @@ TYPED_TEST(MultiPointFactoryTest, simple) using P = vec_2d; auto multipoints = - make_multipoints_array({{P{0.0, 0.0}, P{1.0, 0.0}}, {P{2.0, 0.0}, P{2.0, 2.0}}}); + make_multipoint_array({{P{0.0, 0.0}, P{1.0, 0.0}}, {P{2.0, 0.0}, P{2.0, 2.0}}}); auto [offsets, coords] = multipoints.release(); @@ -52,7 +52,7 @@ TYPED_TEST(MultiPointFactoryTest, empty) using T = TypeParam; using P = vec_2d; - auto multipoints = make_multipoints_array({}); + auto multipoints = make_multipoint_array({}); auto [offsets, coords] = multipoints.release(); @@ -68,7 +68,7 @@ TYPED_TEST(MultiPointFactoryTest, mixed_empty_multipoint) using T = TypeParam; using P = vec_2d; - auto multipoints = make_multipoints_array({{P{1.0, 0.0}}, {}, {P{2.0, 3.0}, P{4.0, 5.0}}}); + auto multipoints = make_multipoint_array({{P{1.0, 0.0}}, {}, {P{2.0, 3.0}, P{4.0, 5.0}}}); auto [offsets, coords] = multipoints.release(); @@ -84,7 +84,7 @@ TYPED_TEST(MultiPointFactoryTest, mixed_empty_multipoint2) using T = TypeParam; using P = vec_2d; - auto multipoints = make_multipoints_array({{}, {P{1.0, 0.0}}, {P{2.0, 3.0}, P{4.0, 5.0}}}); + auto multipoints = make_multipoint_array({{}, {P{1.0, 0.0}}, {P{2.0, 3.0}, P{4.0, 5.0}}}); auto [offsets, coords] = multipoints.release(); @@ -100,7 +100,7 @@ TYPED_TEST(MultiPointFactoryTest, mixed_empty_multipoint3) using T = TypeParam; using P = vec_2d; - auto multipoints = make_multipoints_array({{P{1.0, 0.0}}, {P{2.0, 3.0}, P{4.0, 5.0}}, {}}); + auto multipoints = make_multipoint_array({{P{1.0, 0.0}}, {P{2.0, 3.0}, P{4.0, 5.0}}, {}}); auto [offsets, coords] = multipoints.release(); From 80d92e4b20f870e835a0084d81252af923a51037 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 13 Apr 2023 17:32:49 -0700 Subject: [PATCH 48/60] style --- cpp/include/cuspatial_test/vector_factories.cuh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/include/cuspatial_test/vector_factories.cuh b/cpp/include/cuspatial_test/vector_factories.cuh index 3cd2989a5..25c280694 100644 --- a/cpp/include/cuspatial_test/vector_factories.cuh +++ b/cpp/include/cuspatial_test/vector_factories.cuh @@ -349,7 +349,7 @@ auto make_multipoint_array(std::initializer_list */ template auto make_multipoint_array(rmm::device_uvector geometry_offsets, - rmm::device_uvector> coords) + rmm::device_uvector> coords) { return multipoint_array, rmm::device_uvector>>{ std::move(geometry_offsets), std::move(coords)}; From 2ecb8d517f9d1cbb6598ac9dc66e74654aa0d19f Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 24 Apr 2023 23:24:43 -0700 Subject: [PATCH 49/60] use cpm to pull nvtx --- cpp/CMakeLists.txt | 10 +- cpp/include/cuspatial/detail/nvtx/nvtx3.hpp | 1934 ------------------ cpp/include/cuspatial/detail/nvtx/ranges.hpp | 9 +- 3 files changed, 11 insertions(+), 1942 deletions(-) delete mode 100644 cpp/include/cuspatial/detail/nvtx/nvtx3.hpp diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 1b6afb223..93043b249 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -108,6 +108,14 @@ include(cmake/Modules/ConfigureCUDA.cmake) rapids_cpm_init() # find or add cuDF include(cmake/thirdparty/CUSPATIAL_GetCUDF.cmake) + +# install nvtx3 +CPMAddPackage( + NAME NVTX + GITHUB_REPOSITORY NVIDIA/NVTX + GIT_TAG v3.1.0-c-cpp + GIT_SHALLOW TRUE) + # find or install GoogleTest if (CUSPATIAL_BUILD_TESTS) include(cmake/thirdparty/get_gtest.cmake) @@ -196,7 +204,7 @@ endif() target_compile_definitions(cuspatial PUBLIC "SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_${RMM_LOGGING_LEVEL}") # Specify the target module library dependencies -target_link_libraries(cuspatial PUBLIC cudf::cudf) +target_link_libraries(cuspatial PRIVATE nvtx3-cpp PUBLIC cudf::cudf) add_library(cuspatial::cuspatial ALIAS cuspatial) diff --git a/cpp/include/cuspatial/detail/nvtx/nvtx3.hpp b/cpp/include/cuspatial/detail/nvtx/nvtx3.hpp deleted file mode 100644 index fb90ea668..000000000 --- a/cpp/include/cuspatial/detail/nvtx/nvtx3.hpp +++ /dev/null @@ -1,1934 +0,0 @@ -/* - * Copyright (c) 2020-2022, NVIDIA CORPORATION. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#if defined(NVTX3_MINOR_VERSION) and NVTX3_MINOR_VERSION < 0 -#error \ - "Trying to #include NVTX version 3 in a source file where an older NVTX version has already been included. If you are not directly using NVTX (the NVIDIA Tools Extension library), you are getting this error because libraries you are using have included different versions of NVTX. Suggested solutions are: (1) reorder #includes so the newest NVTX version is included first, (2) avoid using the conflicting libraries in the same .c/.cpp file, or (3) update the library using the older NVTX version to use the newer version instead." -#endif - -/** - * @brief Semantic minor version number. - * - * Major version number is hardcoded into the "nvtx3" namespace/prefix. - * - * If this value is incremented, the above version include guard needs to be - * updated. - */ -#define NVTX3_MINOR_VERSION 0 - -#include - -#include - -/** - * @file nvtx3.hpp - * - * @brief Provides C++ constructs making the NVTX library safer and easier to - * use with zero overhead. - */ - -/** - * \mainpage - * \tableofcontents - * - * \section QUICK_START Quick Start - * - * To add NVTX ranges to your code, use the `nvtx3::thread_range` RAII object. A - * range begins when the object is created, and ends when the object is - * destroyed. - * - * \code{.cpp} - * #include "nvtx3.hpp" - * void some_function(){ - * // Begins a NVTX range with the message "some_function" - * // The range ends when some_function() returns and `r` is destroyed - * nvtx3::thread_range r{"some_function"}; - * - * for(int i = 0; i < 6; ++i){ - * nvtx3::thread_range loop{"loop range"}; - * std::this_thread::sleep_for(std::chrono::seconds{1}); - * } - * } // Range ends when `r` is destroyed - * \endcode - * - * The example code above generates the following timeline view in Nsight - * Systems: - * - * \image html - * https://raw.githubusercontent.com/jrhemstad/nvtx_wrappers/master/docs/example_range.png - * - * Alternatively, use the \ref MACROS like `NVTX3_FUNC_RANGE()` to add - * ranges to your code that automatically use the name of the enclosing function - * as the range's message. - * - * \code{.cpp} - * #include "nvtx3.hpp" - * void some_function(){ - * // Creates a range with a message "some_function" that ends when the - * enclosing - * // function returns - * NVTX3_FUNC_RANGE(); - * ... - * } - * \endcode - * - * - * \section Overview - * - * The NVTX library provides a set of functions for users to annotate their code - * to aid in performance profiling and optimization. These annotations provide - * information to tools like Nsight Systems to improve visualization of - * application timelines. - * - * \ref RANGES are one of the most commonly used NVTX constructs for annotating - * a span of time. For example, imagine a user wanted to see every time a - * function, `my_function`, is called and how long it takes to execute. This can - * be accomplished with an NVTX range created on the entry to the function and - * terminated on return from `my_function` using the push/pop C APIs: - * - * ``` - * void my_function(...){ - * nvtxRangePushA("my_function"); // Begins NVTX range - * // do work - * nvtxRangePop(); // Ends NVTX range - * } - * ``` - * - * One of the challenges with using the NVTX C API is that it requires manually - * terminating the end of the range with `nvtxRangePop`. This can be challenging - * if `my_function()` has multiple returns or can throw exceptions as it - * requires calling `nvtxRangePop()` before all possible return points. - * - * NVTX++ solves this inconvenience through the "RAII" technique by providing a - * `nvtx3::thread_range` class that begins a range at construction and ends the - * range on destruction. The above example then becomes: - * - * ``` - * void my_function(...){ - * nvtx3::thread_range r{"my_function"}; // Begins NVTX range - * // do work - * } // Range ends on exit from `my_function` when `r` is destroyed - * ``` - * - * The range object `r` is deterministically destroyed whenever `my_function` - * returns---ending the NVTX range without manual intervention. For more - * information, see \ref RANGES and `nvtx3::domain_thread_range`. - * - * Another inconvenience of the NVTX C APIs are the several constructs where the - * user is expected to initialize an object at the beginning of an application - * and reuse that object throughout the lifetime of the application. For example - * Domains, Categories, and Registered messages. - * - * Example: - * ``` - * nvtxDomainHandle_t D = nvtxDomainCreateA("my domain"); - * // Reuse `D` throughout the rest of the application - * ``` - * - * This can be problematic if the user application or library does not have an - * explicit initialization function called before all other functions to - * ensure that these long-lived objects are initialized before being used. - * - * NVTX++ makes use of the "construct on first use" technique to alleviate this - * inconvenience. In short, a function local static object is constructed upon - * the first invocation of a function and returns a reference to that object on - * all future invocations. See the documentation for - * `nvtx3::registered_message`, `nvtx3::domain`, `nvtx3::named_category`, and - * https://isocpp.org/wiki/faq/ctors#static-init-order-on-first-use for more - * information. - * - * Using construct on first use, the above example becomes: - * ``` - * struct my_domain{ static constexpr char const* name{"my domain"}; }; - * - * // The first invocation of `domain::get` for the type `my_domain` will - * // construct a `nvtx3::domain` object and return a reference to it. Future - * // invocations simply return a reference. - * nvtx3::domain const& D = nvtx3::domain::get(); - * ``` - * For more information about NVTX and how it can be used, see - * https://docs.nvidia.com/cuda/profiler-users-guide/index.html#nvtx and - * https://devblogs.nvidia.com/cuda-pro-tip-generate-custom-application-profile-timelines-nvtx/ - * for more information. - * - * \section RANGES Ranges - * - * Ranges are used to describe a span of time during the execution of an - * application. Common examples are using ranges to annotate the time it takes - * to execute a function or an iteration of a loop. - * - * NVTX++ uses RAII to automate the generation of ranges that are tied to the - * lifetime of objects. Similar to `std::lock_guard` in the C++ Standard - * Template Library. - * - * \subsection THREAD_RANGE Thread Range - * - * `nvtx3::domain_thread_range` is a class that begins a range upon construction - * and ends the range at destruction. This is one of the most commonly used - * constructs in NVTX++ and is useful for annotating spans of time on a - * particular thread. These ranges can be nested to arbitrary depths. - * - * `nvtx3::thread_range` is an alias for a `nvtx3::domain_thread_range` in the - * global NVTX domain. For more information about Domains, see \ref DOMAINS. - * - * Various attributes of a range can be configured constructing a - * `nvtx3::domain_thread_range` with a `nvtx3::event_attributes` object. For - * more information, see \ref ATTRIBUTES. - * - * Example: - * - * \code{.cpp} - * void some_function(){ - * // Creates a range for the duration of `some_function` - * nvtx3::thread_range r{}; - * - * while(true){ - * // Creates a range for every loop iteration - * // `loop_range` is nested inside `r` - * nvtx3::thread_range loop_range{}; - * } - * } - * \endcode - * - * \subsection PROCESS_RANGE Process Range - * - * `nvtx3::domain_process_range` is identical to `nvtx3::domain_thread_range` - * with the exception that a `domain_process_range` can be created and destroyed - * on different threads. This is useful to annotate spans of time that can - * bridge multiple threads. - * - * `nvtx3::domain_thread_range`s should be preferred unless one needs the - * ability to begin and end a range on different threads. - * - * \section MARKS Marks - * - * `nvtx3::mark` allows annotating an instantaneous event in an application's - * timeline. For example, indicating when a mutex is locked or unlocked. - * - * \code{.cpp} - * std::mutex global_lock; - * void lock_mutex(){ - * global_lock.lock(); - * // Marks an event immediately after the mutex is locked - * nvtx3::mark("lock_mutex"); - * } - * \endcode - * - * \section DOMAINS Domains - * - * Similar to C++ namespaces, Domains allow for scoping NVTX events. By default, - * all NVTX events belong to the "global" domain. Libraries and applications - * should scope their events to use a custom domain to differentiate where the - * events originate from. - * - * It is common for a library or application to have only a single domain and - * for the name of that domain to be known at compile time. Therefore, Domains - * in NVTX++ are represented by _tag types_. - * - * For example, to define a custom domain, simply define a new concrete type - * (a `class` or `struct`) with a `static` member called `name` that contains - * the desired name of the domain. - * - * ``` - * struct my_domain{ static constexpr char const* name{"my domain"}; }; - * ``` - * - * For any NVTX++ construct that can be scoped to a domain, the type `my_domain` - * can be passed as an explicit template argument to scope it to the custom - * domain. - * - * The tag type `nvtx3::domain::global` represents the global NVTX domain. - * - * \code{.cpp} - * // By default, `domain_thread_range` belongs to the global domain - * nvtx3::domain_thread_range<> r0{}; - * - * // Alias for a `domain_thread_range` in the global domain - * nvtx3::thread_range r1{}; - * - * // `r` belongs to the custom domain - * nvtx3::domain_thread_range r{}; - * \endcode - * - * When using a custom domain, it is recommended to define type aliases for NVTX - * constructs in the custom domain. - * ``` - * using my_thread_range = nvtx3::domain_thread_range; - * using my_registered_message = nvtx3::registered_message; - * using my_named_category = nvtx3::named_category; - * ``` - * - * See `nvtx3::domain` for more information. - * - * \section ATTRIBUTES Event Attributes - * - * NVTX events can be customized with various attributes to provide additional - * information (such as a custom message) or to control visualization of the - * event (such as the color used). These attributes can be specified per-event - * via arguments to a `nvtx3::event_attributes` object. - * - * NVTX events can be customized via four "attributes": - * - \ref COLOR : color used to visualize the event in tools. - * - \ref MESSAGES : Custom message string. - * - \ref PAYLOAD : User-defined numerical value. - * - \ref CATEGORY : Intra-domain grouping. - * - * It is possible to construct a `nvtx3::event_attributes` from any number of - * attribute objects (nvtx3::color, nvtx3::message, nvtx3::payload, - * nvtx3::category) in any order. If an attribute is not specified, a tool - * specific default value is used. See `nvtx3::event_attributes` for more - * information. - * - * \code{.cpp} - * // Custom color, message - * event_attributes attr{nvtx3::rgb{127, 255, 0}, - * "message"}; - * - * // Custom color, message, payload, category - * event_attributes attr{nvtx3::rgb{127, 255, 0}, - * nvtx3::payload{42}, - * "message", - * nvtx3::category{1}}; - * - * // Arguments can be in any order - * event_attributes attr{nvtx3::payload{42}, - * nvtx3::category{1}, - * "message", - * nvtx3::rgb{127, 255, 0}}; - * - * // "First wins" with multiple arguments of the same type - * event_attributes attr{ nvtx3::payload{42}, nvtx3::payload{7} }; // payload is - * 42 \endcode - * - * \subsection MESSAGES message - * - * A `nvtx3::message` allows associating a custom message string with an NVTX - * event. - * - * Example: - * \code{.cpp} - * // Create an `event_attributes` with the custom message "my message" - * nvtx3::event_attributes attr{nvtx3::message{"my message"}}; - * - * // strings and string literals implicitly assumed to be a `nvtx3::message` - * nvtx3::event_attributes attr{"my message"}; - * \endcode - * - * \subsubsection REGISTERED_MESSAGE Registered Messages - * - * Associating a `nvtx3::message` with an event requires copying the contents of - * the message every time the message is used, i.e., copying the entire message - * string. This may cause non-trivial overhead in performance sensitive code. - * - * To eliminate this overhead, NVTX allows registering a message string, - * yielding a "handle" that is inexpensive to copy that may be used in place of - * a message string. When visualizing the events, tools such as Nsight Systems - * will take care of mapping the message handle to its string. - * - * A message should be registered once and the handle reused throughout the rest - * of the application. This can be done by either explicitly creating static - * `nvtx3::registered_message` objects, or using the - * `nvtx3::registered_message::get` construct on first use helper (recommended). - * - * Similar to \ref DOMAINS, `nvtx3::registered_message::get` requires defining a - * custom tag type with a static `message` member whose value will be the - * contents of the registered string. - * - * Example: - * \code{.cpp} - * // Explicitly constructed, static `registered_message` - * static registered_message static_message{"my message"}; - * - * // Or use construct on first use: - * // Define a tag type with a `message` member string to register - * struct my_message{ static constexpr char const* message{ "my message" }; }; - * - * // Uses construct on first use to register the contents of - * // `my_message::message` - * nvtx3::registered_message const& msg = - * nvtx3::registered_message::get(); \endcode - * - * \subsection COLOR color - * - * Associating a `nvtx3::color` with an event allows controlling how the event - * is visualized in a tool such as Nsight Systems. This is a convenient way to - * visually differentiate among different events. - * - * \code{.cpp} - * // Define a color via rgb color values - * nvtx3::color c{nvtx3::rgb{127, 255, 0}}; - * nvtx3::event_attributes attr{c}; - * - * // rgb color values can be passed directly to an `event_attributes` - * nvtx3::event_attributes attr1{nvtx3::rgb{127,255,0}}; - * \endcode - * - * \subsection CATEGORY category - * - * A `nvtx3::category` is simply an integer id that allows for fine-grain - * grouping of NVTX events. For example, one might use separate categories for - * IO, memory allocation, compute, etc. - * - * \code{.cpp} - * nvtx3::event_attributes{nvtx3::category{1}}; - * \endcode - * - * \subsubsection NAMED_CATEGORIES Named Categories - * - * Associates a `name` string with a category `id` to help differentiate among - * categories. - * - * For any given category id `Id`, a `named_category{Id, "name"}` should only - * be constructed once and reused throughout an application. This can be done by - * either explicitly creating static `nvtx3::named_category` objects, or using - * the `nvtx3::named_category::get` construct on first use helper (recommended). - * - * Similar to \ref DOMAINS, `nvtx3::named_category::get` requires defining a - * custom tag type with static `name` and `id` members. - * - * \code{.cpp} - * // Explicitly constructed, static `named_category` - * static nvtx3::named_category static_category{42, "my category"}; - * - * // OR use construct on first use: - * // Define a tag type with `name` and `id` members - * struct my_category{ - * static constexpr char const* name{"my category"}; // category name - * static constexpr category::id_type id{42}; // category id - * }; - * - * // Use construct on first use to name the category id `42` - * // with name "my category" - * nvtx3::named_category const& my_category = - * named_category::get(); - * - * // Range `r` associated with category id `42` - * nvtx3::event_attributes attr{my_category}; - * \endcode - * - * \subsection PAYLOAD payload - * - * Allows associating a user-defined numerical value with an event. - * - * ``` - * nvtx3:: event_attributes attr{nvtx3::payload{42}}; // Constructs a payload - * from - * // the `int32_t` value 42 - * ``` - * - * - * \section EXAMPLE Example - * - * Putting it all together: - * \code{.cpp} - * // Define a custom domain tag type - * struct my_domain{ static constexpr char const* name{"my domain"}; }; - * - * // Define a named category tag type - * struct my_category{ - * static constexpr char const* name{"my category"}; - * static constexpr uint32_t id{42}; - * }; - * - * // Define a registered message tag type - * struct my_message{ static constexpr char const* message{"my message"}; }; - * - * // For convenience, use aliases for domain scoped objects - * using my_thread_range = nvtx3::domain_thread_range; - * using my_registered_message = nvtx3::registered_message; - * using my_named_category = nvtx3::named_category; - * - * // Default values for all attributes - * nvtx3::event_attributes attr{}; - * my_thread_range r0{attr}; - * - * // Custom (unregistered) message, and unnamed category - * nvtx3::event_attributes attr1{"message", nvtx3::category{2}}; - * my_thread_range r1{attr1}; - * - * // Alternatively, pass arguments of `event_attributes` ctor directly to - * // `my_thread_range` - * my_thread_range r2{"message", nvtx3::category{2}}; - * - * // construct on first use a registered message - * auto msg = my_registered_message::get(); - * - * // construct on first use a named category - * auto category = my_named_category::get(); - * - * // Use registered message and named category - * my_thread_range r3{msg, category, nvtx3::rgb{127, 255, 0}, - * nvtx3::payload{42}}; - * - * // Any number of arguments in any order - * my_thread_range r{nvtx3::rgb{127, 255,0}, msg}; - * - * \endcode - * \section MACROS Convenience Macros - * - * Oftentimes users want to quickly and easily add NVTX ranges to their library - * or application to aid in profiling and optimization. - * - * A convenient way to do this is to use the \ref NVTX3_FUNC_RANGE and - * \ref NVTX3_FUNC_RANGE_IN macros. These macros take care of constructing an - * `nvtx3::domain_thread_range` with the name of the enclosing function as the - * range's message. - * - * \code{.cpp} - * void some_function(){ - * // Automatically generates an NVTX range for the duration of the function - * // using "some_function" as the event's message. - * NVTX3_FUNC_RANGE(); - * } - * \endcode - */ - -/** - * @brief Enables the use of constexpr when support for C++14 relaxed constexpr - * is present. - * - * Initializing a legacy-C (i.e., no constructor) union member requires - * initializing in the constructor body. Non-empty constexpr constructors - * require C++14 relaxed constexpr. - */ -#if __cpp_constexpr >= 201304L -#define NVTX3_RELAXED_CONSTEXPR constexpr -#else -#define NVTX3_RELAXED_CONSTEXPR -#endif - -namespace nvtx3 { -namespace detail { -/** - * @brief Verifies if a type `T` contains a member `T::name` of type `const - * char*` or `const wchar_t*`. - * - * @tparam T The type to verify - * @return True if `T` contains a member `T::name` of type `const char*` or - * `const wchar_t*`. - */ -template -constexpr auto has_name_member() noexcept -> decltype(T::name, bool()) -{ - return (std::is_same_v::type> or - std::is_same_v::type>); -} -} // namespace detail - -/** - * @brief `domain`s allow for grouping NVTX events into a single scope to - * differentiate them from events in other `domain`s. - * - * By default, all NVTX constructs are placed in the "global" NVTX domain. - * - * A custom `domain` may be used in order to differentiate a library's or - * application's NVTX events from other events. - * - * `domain`s are expected to be long-lived and unique to a library or - * application. As such, it is assumed a domain's name is known at compile - * time. Therefore, all NVTX constructs that can be associated with a domain - * require the domain to be specified via a *type* `DomainName` passed as an - * explicit template parameter. - * - * The type `domain::global` may be used to indicate that the global NVTX - * domain should be used. - * - * None of the C++ NVTX constructs require the user to manually construct a - * `domain` object. Instead, if a custom domain is desired, the user is - * expected to define a type `DomainName` that contains a member - * `DomainName::name` which resolves to either a `char const*` or `wchar_t - * const*`. The value of `DomainName::name` is used to name and uniquely - * identify the custom domain. - * - * Upon the first use of an NVTX construct associated with the type - * `DomainName`, the "construct on first use" pattern is used to construct a - * function local static `domain` object. All future NVTX constructs - * associated with `DomainType` will use a reference to the previously - * constructed `domain` object. See `domain::get`. - * - * Example: - * ``` - * // The type `my_domain` defines a `name` member used to name and identify - * the - * // `domain` object identified by `my_domain`. - * struct my_domain{ static constexpr char const* name{"my_domain"}; }; - * - * // The NVTX range `r` will be grouped with all other NVTX constructs - * // associated with `my_domain`. - * nvtx3::domain_thread_range r{}; - * - * // An alias can be created for a `domain_thread_range` in the custom domain - * using my_thread_range = nvtx3::domain_thread_range; - * my_thread_range my_range{}; - * - * // `domain::global` indicates that the global NVTX domain is used - * nvtx3::domain_thread_range r2{}; - * - * // For convenience, `nvtx3::thread_range` is an alias for a range in the - * // global domain - * nvtx3::thread_range r3{}; - * ``` - */ -class domain { - public: - domain(domain const&) = delete; - domain& operator=(domain const&) = delete; - domain(domain&&) = delete; - domain& operator=(domain&&) = delete; - - /** - * @brief Returns reference to an instance of a function local static - * `domain` object. - * - * Uses the "construct on first use" idiom to safely ensure the `domain` - * object is initialized exactly once upon first invocation of - * `domain::get()`. All following invocations will return a - * reference to the previously constructed `domain` object. See - * https://isocpp.org/wiki/faq/ctors#static-init-order-on-first-use - * - * None of the constructs in this header require the user to directly invoke - * `domain::get`. It is automatically invoked when constructing objects like - * a `domain_thread_range` or `category`. Advanced users may wish to use - * `domain::get` for the convenience of the "construct on first use" idiom - * when using domains with their own use of the NVTX C API. - * - * This function is threadsafe as of C++11. If two or more threads call - * `domain::get` concurrently, exactly one of them is guaranteed - * to construct the `domain` object and the other(s) will receive a - * reference to the object after it is fully constructed. - * - * The domain's name is specified via the type `DomainName` pass as an - * explicit template parameter. `DomainName` is required to contain a - * member `DomainName::name` that resolves to either a `char const*` or - * `wchar_t const*`. The value of `DomainName::name` is used to name and - * uniquely identify the `domain`. - * - * Example: - * ``` - * // The type `my_domain` defines a `name` member used to name and identify - * // the `domain` object identified by `my_domain`. - * struct my_domain{ static constexpr char const* name{"my domain"}; }; - * - * auto D = domain::get(); // First invocation constructs a - * // `domain` with the name "my domain" - * - * auto D1 = domain::get(); // Simply returns reference to - * // previously constructed `domain`. - * ``` - * - * @tparam DomainName Type that contains a `DomainName::name` member used to - * name the `domain` object. - * @return Reference to the `domain` corresponding to the type `DomainName`. - */ - template - static domain const& get() - { - static_assert(detail::has_name_member(), - "Type used to identify a domain must contain a name member of" - "type const char* or const wchar_t*"); - static domain const d{DomainName::name}; - return d; - } - - /** - * @brief Conversion operator to `nvtxDomainHandle_t`. - * - * Allows transparently passing a domain object into an API expecting a - * native `nvtxDomainHandle_t` object. - */ - operator nvtxDomainHandle_t() const noexcept { return _domain; } - - /** - * @brief Tag type for the "global" NVTX domain. - * - * This type may be passed as a template argument to any function/class - * expecting a type to identify a domain to indicate that the global domain - * should be used. - * - * All NVTX events in the global domain across all libraries and - * applications will be grouped together. - * - */ - struct global { - }; - - private: - /** - * @brief Construct a new domain with the specified `name`. - * - * This constructor is private as it is intended that `domain` objects only - * be created through the `domain::get` function. - * - * @param name A unique name identifying the domain - */ - explicit domain(char const* name) noexcept : _domain{nvtxDomainCreateA(name)} {} - - /** - * @brief Construct a new domain with the specified `name`. - * - * This constructor is private as it is intended that `domain` objects only - * be created through the `domain::get` function. - * - * @param name A unique name identifying the domain - */ - explicit domain(wchar_t const* name) noexcept : _domain{nvtxDomainCreateW(name)} {} - - /** - * @brief Construct a new domain with the specified `name`. - * - * This constructor is private as it is intended that `domain` objects only - * be created through the `domain::get` function. - * - * @param name A unique name identifying the domain - */ - explicit domain(std::string const& name) noexcept : domain{name.c_str()} {} - - /** - * @brief Construct a new domain with the specified `name`. - * - * This constructor is private as it is intended that `domain` objects only - * be created through the `domain::get` function. - * - * @param name A unique name identifying the domain - */ - explicit domain(std::wstring const& name) noexcept : domain{name.c_str()} {} - - /** - * @brief Default constructor creates a `domain` representing the - * "global" NVTX domain. - * - * All events not associated with a custom `domain` are grouped in the - * "global" NVTX domain. - * - */ - domain() = default; - - /** - * @brief Destroy the domain object, unregistering and freeing all domain - * specific resources. - */ - ~domain() noexcept { nvtxDomainDestroy(_domain); } - - private: - nvtxDomainHandle_t const _domain{}; ///< The `domain`s NVTX handle -}; - -/** - * @brief Returns reference to the `domain` object that represents the global - * NVTX domain. - * - * This specialization for `domain::global` returns a default constructed, - * `domain` object for use when the "global" domain is desired. - * - * All NVTX events in the global domain across all libraries and applications - * will be grouped together. - * - * @return Reference to the `domain` corresponding to the global NVTX domain. - */ -template <> -inline domain const& domain::get() -{ - static domain const d{}; - return d; -} - -/** - * @brief Indicates the values of the red, green, blue color channels for - * a rgb color code. - */ -struct rgb { - /// Type used for component values - using component_type = uint8_t; - - /** - * @brief Construct a rgb with red, green, and blue channels - * specified by `red_`, `green_`, and `blue_`, respectively. - * - * Valid values are in the range `[0,255]`. - * - * @param red_ Value of the red channel - * @param green_ Value of the green channel - * @param blue_ Value of the blue channel - */ - constexpr rgb(component_type red_, component_type green_, component_type blue_) noexcept - : red{red_}, green{green_}, blue{blue_} - { - } - - component_type const red{}; ///< Red channel value - component_type const green{}; ///< Green channel value - component_type const blue{}; ///< Blue channel value -}; - -/** - * @brief Indicates the value of the alpha, red, green, and blue color - * channels for an argb color code. - */ -struct argb final : rgb { - /** - * @brief Construct an argb with alpha, red, green, and blue channels - * specified by `alpha_`, `red_`, `green_`, and `blue_`, respectively. - * - * Valid values are in the range `[0,255]`. - * - * @param alpha_ Value of the alpha channel (opacity) - * @param red_ Value of the red channel - * @param green_ Value of the green channel - * @param blue_ Value of the blue channel - * - */ - constexpr argb(component_type alpha_, - component_type red_, - component_type green_, - component_type blue_) noexcept - : rgb{red_, green_, blue_}, alpha{alpha_} - { - } - - component_type const alpha{}; ///< Alpha channel value -}; - -/** - * @brief Represents a custom color that can be associated with an NVTX event - * via it's `event_attributes`. - * - * Specifying colors for NVTX events is a convenient way to visually - * differentiate among different events in a visualization tool such as Nsight - * Systems. - */ -class color { - public: - /// Type used for the color's value - using value_type = uint32_t; - - /** - * @brief Constructs a `color` using the value provided by `hex_code`. - * - * `hex_code` is expected to be a 4 byte argb hex code. - * - * The most significant byte indicates the value of the alpha channel - * (opacity) (0-255) - * - * The next byte indicates the value of the red channel (0-255) - * - * The next byte indicates the value of the green channel (0-255) - * - * The least significant byte indicates the value of the blue channel - * (0-255) - * - * @param hex_code The hex code used to construct the `color` - */ - constexpr explicit color(value_type hex_code) noexcept : _value{hex_code} {} - - /** - * @brief Construct a `color` using the alpha, red, green, blue components - * in `argb`. - * - * @param argb The alpha, red, green, blue components of the desired `color` - */ - constexpr color(argb argb) noexcept - : color{from_bytes_msb_to_lsb(argb.alpha, argb.red, argb.green, argb.blue)} - { - } - - /** - * @brief Construct a `color` using the red, green, blue components in - * `rgb`. - * - * Uses maximum value for the alpha channel (opacity) of the `color`. - * - * @param rgb The red, green, blue components of the desired `color` - */ - constexpr color(rgb rgb) noexcept - : color{from_bytes_msb_to_lsb(0xFF, rgb.red, rgb.green, rgb.blue)} - { - } - - /** - * @brief Returns the `color`s argb hex code - * - */ - constexpr value_type get_value() const noexcept { return _value; } - - /** - * @brief Return the NVTX color type of the color. - * - */ - constexpr nvtxColorType_t get_type() const noexcept { return _type; } - - color() = delete; - ~color() = default; - color(color const&) = default; - color& operator=(color const&) = default; - color(color&&) = default; - color& operator=(color&&) = default; - - private: - /** - * @brief Constructs an unsigned, 4B integer from the component bytes in - * most to least significant byte order. - * - */ - constexpr static value_type from_bytes_msb_to_lsb(uint8_t byte3, - uint8_t byte2, - uint8_t byte1, - uint8_t byte0) noexcept - { - return uint32_t{byte3} << 24 | uint32_t{byte2} << 16 | uint32_t{byte1} << 8 | uint32_t{byte0}; - } - - value_type const _value{}; ///< color's argb color code - nvtxColorType_t const _type{NVTX_COLOR_ARGB}; ///< NVTX color type code -}; - -/** - * @brief Object for intra-domain grouping of NVTX events. - * - * A `category` is simply an integer id that allows for fine-grain grouping of - * NVTX events. For example, one might use separate categories for IO, memory - * allocation, compute, etc. - * - * Example: - * \code{.cpp} - * nvtx3::category cat1{1}; - * - * // Range `r1` belongs to the category identified by the value `1`. - * nvtx3::thread_range r1{cat1}; - * - * // Range `r2` belongs to the same category as `r1` - * nvtx3::thread_range r2{nvtx3::category{1}}; - * \endcode - * - * To associate a name string with a category id, see `named_category`. - */ -class category { - public: - /// Type used for `category`s integer id. - using id_type = uint32_t; - - /** - * @brief Construct a `category` with the specified `id`. - * - * The `category` will be unnamed and identified only by its `id` value. - * - * All `category` objects sharing the same `id` are equivalent. - * - * @param[in] id The `category`'s identifying value - */ - constexpr explicit category(id_type id) noexcept : id_{id} {} - - /** - * @brief Returns the id of the category. - * - */ - constexpr id_type get_id() const noexcept { return id_; } - - category() = delete; - ~category() = default; - category(category const&) = default; - category& operator=(category const&) = default; - category(category&&) = default; - category& operator=(category&&) = default; - - private: - id_type const id_{}; ///< category's unique identifier -}; - -/** - * @brief A `category` with an associated name string. - * - * Associates a `name` string with a category `id` to help differentiate among - * categories. - * - * For any given category id `Id`, a `named_category(Id, "name")` should only - * be constructed once and reused throughout an application. This can be done - * by either explicitly creating static `named_category` objects, or using the - * `named_category::get` construct on first use helper (recommended). - * - * Creating two or more `named_category` objects with the same value for `id` - * in the same domain results in undefined behavior. - * - * Similarly, behavior is undefined when a `named_category` and `category` - * share the same value of `id`. - * - * Example: - * \code{.cpp} - * // Explicitly constructed, static `named_category` - * static nvtx3::named_category static_category{42, "my category"}; - * - * // Range `r` associated with category id `42` - * nvtx3::thread_range r{static_category}; - * - * // OR use construct on first use: - * - * // Define a type with `name` and `id` members - * struct my_category{ - * static constexpr char const* name{"my category"}; // category name - * static constexpr category::id_type id{42}; // category id - * }; - * - * // Use construct on first use to name the category id `42` - * // with name "my category" - * auto my_category = named_category::get(); - * - * // Range `r` associated with category id `42` - * nvtx3::thread_range r{my_category}; - * \endcode - * - * `named_category`'s association of a name to a category id is local to the - * domain specified by the type `D`. An id may have a different name in - * another domain. - * - * @tparam D Type containing `name` member used to identify the `domain` to - * which the `named_category` belongs. Else, `domain::global` to indicate - * that the global NVTX domain should be used. - */ -template -class named_category final : public category { - public: - /** - * @brief Returns a global instance of a `named_category` as a - * function-local static. - * - * Creates a `named_category` with name and id specified by the contents of - * a type `C`. `C::name` determines the name and `C::id` determines the - * category id. - * - * This function is useful for constructing a named `category` exactly once - * and reusing the same instance throughout an application. - * - * Example: - * \code{.cpp} - * // Define a type with `name` and `id` members - * struct my_category{ - * static constexpr char const* name{"my category"}; // category name - * static constexpr uint32_t id{42}; // category id - * }; - * - * // Use construct on first use to name the category id `42` - * // with name "my category" - * auto cat = named_category::get(); - * - * // Range `r` associated with category id `42` - * nvtx3::thread_range r{cat}; - * \endcode - * - * Uses the "construct on first use" idiom to safely ensure the `category` - * object is initialized exactly once. See - * https://isocpp.org/wiki/faq/ctors#static-init-order-on-first-use - * - * @tparam C Type containing a member `C::name` that resolves to either a - * `char const*` or `wchar_t const*` and `C::id`. - */ - template - static named_category const& get() noexcept - { - static_assert(detail::has_name_member(), - "Type used to name a category must contain a name member."); - static named_category const category{C::id, C::name}; - return category; - } - /** - * @brief Construct a `category` with the specified `id` and `name`. - * - * The name `name` will be registered with `id`. - * - * Every unique value of `id` should only be named once. - * - * @param[in] id The category id to name - * @param[in] name The name to associated with `id` - */ - named_category(id_type id, char const* name) noexcept : category{id} - { - nvtxDomainNameCategoryA(domain::get(), get_id(), name); - }; - - /** - * @brief Construct a `category` with the specified `id` and `name`. - * - * The name `name` will be registered with `id`. - * - * Every unique value of `id` should only be named once. - * - * @param[in] id The category id to name - * @param[in] name The name to associated with `id` - */ - named_category(id_type id, wchar_t const* name) noexcept : category{id} - { - nvtxDomainNameCategoryW(domain::get(), get_id(), name); - }; -}; - -/** - * @brief A message registered with NVTX. - * - * Normally, associating a `message` with an NVTX event requires copying the - * contents of the message string. This may cause non-trivial overhead in - * highly performance sensitive regions of code. - * - * message registration is an optimization to lower the overhead of - * associating a message with an NVTX event. Registering a message yields a - * handle that is inexpensive to copy that may be used in place of a message - * string. - * - * A particular message should only be registered once and the handle - * reused throughout the rest of the application. This can be done by either - * explicitly creating static `registered_message` objects, or using the - * `registered_message::get` construct on first use helper (recommended). - * - * Example: - * \code{.cpp} - * // Explicitly constructed, static `registered_message` - * static registered_message static_message{"message"}; - * - * // "message" is associated with the range `r` - * nvtx3::thread_range r{static_message}; - * - * // Or use construct on first use: - * - * // Define a type with a `message` member that defines the contents of the - * // registered message - * struct my_message{ static constexpr char const* message{ "my message" }; }; - * - * // Uses construct on first use to register the contents of - * // `my_message::message` - * auto msg = registered_message::get(); - * - * // "my message" is associated with the range `r` - * nvtx3::thread_range r{msg}; - * \endcode - * - * `registered_message`s are local to a particular domain specified via - * the type `D`. - * - * @tparam D Type containing `name` member used to identify the `domain` to - * which the `registered_message` belongs. Else, `domain::global` to indicate - * that the global NVTX domain should be used. - */ -template -class registered_message { - public: - /** - * @brief Returns a global instance of a `registered_message` as a function - * local static. - * - * Provides a convenient way to register a message with NVTX without having - * to explicitly register the message. - * - * Upon first invocation, constructs a `registered_message` whose contents - * are specified by `message::message`. - * - * All future invocations will return a reference to the object constructed - * in the first invocation. - * - * Example: - * \code{.cpp} - * // Define a type with a `message` member that defines the contents of the - * // registered message - * struct my_message{ static constexpr char const* message{ "my message" }; - * }; - * - * // Uses construct on first use to register the contents of - * // `my_message::message` - * auto msg = registered_message::get(); - * - * // "my message" is associated with the range `r` - * nvtx3::thread_range r{msg}; - * \endcode - * - * @tparam M Type required to contain a member `M::message` that - * resolves to either a `char const*` or `wchar_t const*` used as the - * registered message's contents. - * @return Reference to a `registered_message` associated with the type `M`. - */ - template - static registered_message const& get() noexcept - { - static registered_message const registered_message{M::message}; - return registered_message; - } - - /** - * @brief Constructs a `registered_message` from the specified `msg` string. - * - * Registers `msg` with NVTX and associates a handle with the registered - * message. - * - * A particular message should should only be registered once and the handle - * reused throughout the rest of the application. - * - * @param msg The contents of the message - */ - explicit registered_message(char const* msg) noexcept - : handle_{nvtxDomainRegisterStringA(domain::get(), msg)} - { - } - - /** - * @brief Constructs a `registered_message` from the specified `msg` string. - * - * Registers `msg` with NVTX and associates a handle with the registered - * message. - * - * A particular message should should only be registered once and the handle - * reused throughout the rest of the application. - * - * @param msg The contents of the message - */ - explicit registered_message(std::string const& msg) noexcept : registered_message{msg.c_str()} {} - - /** - * @brief Constructs a `registered_message` from the specified `msg` string. - * - * Registers `msg` with NVTX and associates a handle with the registered - * message. - * - * A particular message should should only be registered once and the handle - * reused throughout the rest of the application. - * - * @param msg The contents of the message - */ - explicit registered_message(wchar_t const* msg) noexcept - : handle_{nvtxDomainRegisterStringW(domain::get(), msg)} - { - } - - /** - * @brief Constructs a `registered_message` from the specified `msg` string. - * - * Registers `msg` with NVTX and associates a handle with the registered - * message. - * - * A particular message should only be registered once and the handle - * reused throughout the rest of the application. - * - * @param msg The contents of the message - */ - explicit registered_message(std::wstring const& msg) noexcept : registered_message{msg.c_str()} {} - - /** - * @brief Returns the registered message's handle - * - */ - nvtxStringHandle_t get_handle() const noexcept { return handle_; } - - registered_message() = delete; - ~registered_message() = default; - registered_message(registered_message const&) = default; - registered_message& operator=(registered_message const&) = default; - registered_message(registered_message&&) = default; - registered_message& operator=(registered_message&&) = default; - - private: - nvtxStringHandle_t const handle_{}; ///< The handle returned from - ///< registering the message with NVTX -}; - -/** - * @brief Allows associating a message string with an NVTX event via - * its `EventAttribute`s. - * - * Associating a `message` with an NVTX event through its `event_attributes` - * allows for naming events to easily differentiate them from other events. - * - * Every time an NVTX event is created with an associated `message`, the - * contents of the message string must be copied. This may cause non-trivial - * overhead in highly performance sensitive sections of code. Use of a - * `nvtx3::registered_message` is recommended in these situations. - * - * Example: - * \code{.cpp} - * // Creates an `event_attributes` with message "message 0" - * nvtx3::event_attributes attr0{nvtx3::message{"message 0"}}; - * - * // `range0` contains message "message 0" - * nvtx3::thread_range range0{attr0}; - * - * // `std::string` and string literals are implicitly assumed to be - * // the contents of an `nvtx3::message` - * // Creates an `event_attributes` with message "message 1" - * nvtx3::event_attributes attr1{"message 1"}; - * - * // `range1` contains message "message 1" - * nvtx3::thread_range range1{attr1}; - * - * // `range2` contains message "message 2" - * nvtx3::thread_range range2{nvtx3::message{"message 2"}}; - * - * // `std::string` and string literals are implicitly assumed to be - * // the contents of an `nvtx3::message` - * // `range3` contains message "message 3" - * nvtx3::thread_range range3{"message 3"}; - * \endcode - */ -class message { - public: - using value_type = nvtxMessageValue_t; - - /** - * @brief Construct a `message` whose contents are specified by `msg`. - * - * @param msg The contents of the message - */ - NVTX3_RELAXED_CONSTEXPR message(char const* msg) noexcept : type_{NVTX_MESSAGE_TYPE_ASCII} - { - value_.ascii = msg; - } - - /** - * @brief Construct a `message` whose contents are specified by `msg`. - * - * @param msg The contents of the message - */ - message(std::string const& msg) noexcept : message{msg.c_str()} {} - - /** - * @brief Disallow construction for `std::string` r-value - * - * `message` is a non-owning type and therefore cannot take ownership of an - * r-value. Therefore, constructing from an r-value is disallowed to prevent - * a dangling pointer. - * - */ - message(std::string&&) = delete; - - /** - * @brief Construct a `message` whose contents are specified by `msg`. - * - * @param msg The contents of the message - */ - NVTX3_RELAXED_CONSTEXPR message(wchar_t const* msg) noexcept : type_{NVTX_MESSAGE_TYPE_UNICODE} - { - value_.unicode = msg; - } - - /** - * @brief Construct a `message` whose contents are specified by `msg`. - * - * @param msg The contents of the message - */ - message(std::wstring const& msg) noexcept : message{msg.c_str()} {} - - /** - * @brief Disallow construction for `std::wstring` r-value - * - * `message` is a non-owning type and therefore cannot take ownership of an - * r-value. Therefore, constructing from an r-value is disallowed to prevent - * a dangling pointer. - * - */ - message(std::wstring&&) = delete; - - /** - * @brief Construct a `message` from a `registered_message`. - * - * @tparam D Type containing `name` member used to identify the `domain` - * to which the `registered_message` belongs. Else, `domain::global` to - * indicate that the global NVTX domain should be used. - * @param msg The message that has already been registered with NVTX. - */ - template - message(registered_message const& msg) noexcept : type_{NVTX_MESSAGE_TYPE_REGISTERED} - { - value_.registered = msg.get_handle(); - } - - /** - * @brief Return the union holding the value of the message. - * - */ - NVTX3_RELAXED_CONSTEXPR value_type get_value() const noexcept { return value_; } - - /** - * @brief Return the type information about the value the union holds. - * - */ - NVTX3_RELAXED_CONSTEXPR nvtxMessageType_t get_type() const noexcept { return type_; } - - private: - nvtxMessageType_t const type_{}; ///< message type - nvtxMessageValue_t value_{}; ///< message contents -}; - -/** - * @brief A numerical value that can be associated with an NVTX event via - * its `event_attributes`. - * - * Example: - * ``` - * nvtx3:: event_attributes attr{nvtx3::payload{42}}; // Constructs a payload - * from - * // the `int32_t` value 42 - * - * // `range0` will have an int32_t payload of 42 - * nvtx3::thread_range range0{attr}; - * - * // range1 has double payload of 3.14 - * nvtx3::thread_range range1{ nvtx3::payload{3.14} }; - * ``` - */ -class payload { - public: - using value_type = typename nvtxEventAttributes_v2::payload_t; - - /** - * @brief Construct a `payload` from a signed, 8 byte integer. - * - * @param value Value to use as contents of the payload - */ - NVTX3_RELAXED_CONSTEXPR explicit payload(int64_t value) noexcept - : type_{NVTX_PAYLOAD_TYPE_INT64}, value_{} - { - value_.llValue = value; - } - - /** - * @brief Construct a `payload` from a signed, 4 byte integer. - * - * @param value Value to use as contents of the payload - */ - NVTX3_RELAXED_CONSTEXPR explicit payload(int32_t value) noexcept - : type_{NVTX_PAYLOAD_TYPE_INT32}, value_{} - { - value_.iValue = value; - } - - /** - * @brief Construct a `payload` from an unsigned, 8 byte integer. - * - * @param value Value to use as contents of the payload - */ - NVTX3_RELAXED_CONSTEXPR explicit payload(uint64_t value) noexcept - : type_{NVTX_PAYLOAD_TYPE_UNSIGNED_INT64}, value_{} - { - value_.ullValue = value; - } - - /** - * @brief Construct a `payload` from an unsigned, 4 byte integer. - * - * @param value Value to use as contents of the payload - */ - NVTX3_RELAXED_CONSTEXPR explicit payload(uint32_t value) noexcept - : type_{NVTX_PAYLOAD_TYPE_UNSIGNED_INT32}, value_{} - { - value_.uiValue = value; - } - - /** - * @brief Construct a `payload` from a single-precision floating point - * value. - * - * @param value Value to use as contents of the payload - */ - NVTX3_RELAXED_CONSTEXPR explicit payload(float value) noexcept - : type_{NVTX_PAYLOAD_TYPE_FLOAT}, value_{} - { - value_.fValue = value; - } - - /** - * @brief Construct a `payload` from a double-precision floating point - * value. - * - * @param value Value to use as contents of the payload - */ - NVTX3_RELAXED_CONSTEXPR explicit payload(double value) noexcept - : type_{NVTX_PAYLOAD_TYPE_DOUBLE}, value_{} - { - value_.dValue = value; - } - - /** - * @brief Return the union holding the value of the payload - * - */ - NVTX3_RELAXED_CONSTEXPR value_type get_value() const noexcept { return value_; } - - /** - * @brief Return the information about the type the union holds. - * - */ - NVTX3_RELAXED_CONSTEXPR nvtxPayloadType_t get_type() const noexcept { return type_; } - - private: - nvtxPayloadType_t const type_; ///< Type of the payload value - value_type value_; ///< Union holding the payload value -}; - -/** - * @brief Describes the attributes of a NVTX event. - * - * NVTX events can be customized via four "attributes": - * - * - color: color used to visualize the event in tools such as Nsight - * Systems. See `color`. - * - message: Custom message string. See `message`. - * - payload: User-defined numerical value. See `payload`. - * - category: Intra-domain grouping. See `category`. - * - * These component attributes are specified via an `event_attributes` object. - * See `nvtx3::color`, `nvtx3::message`, `nvtx3::payload`, and - * `nvtx3::category` for how these individual attributes are constructed. - * - * While it is possible to specify all four attributes, it is common to want - * to only specify a subset of attributes and use default values for the - * others. For convenience, `event_attributes` can be constructed from any - * number of attribute components in any order. - * - * Example: - * \code{.cpp} - * event_attributes attr{}; // No arguments, use defaults for all attributes - * - * event_attributes attr{"message"}; // Custom message, rest defaulted - * - * // Custom color & message - * event_attributes attr{"message", nvtx3::rgb{127, 255, 0}}; - * - * /// Custom color & message, can use any order of arguments - * event_attributes attr{nvtx3::rgb{127, 255, 0}, "message"}; - * - * - * // Custom color, message, payload, category - * event_attributes attr{nvtx3::rgb{127, 255, 0}, - * "message", - * nvtx3::payload{42}, - * nvtx3::category{1}}; - * - * // Custom color, message, payload, category, can use any order of arguments - * event_attributes attr{nvtx3::payload{42}, - * nvtx3::category{1}, - * "message", - * nvtx3::rgb{127, 255, 0}}; - * - * // Multiple arguments of the same type are allowed, but only the first is - * // used. All others are ignored - * event_attributes attr{ nvtx3::payload{42}, nvtx3::payload{7} }; // payload - * is 42 - * - * // Range `r` will be customized according the attributes in `attr` - * nvtx3::thread_range r{attr}; - * - * // For convenience, the arguments that can be passed to the - * `event_attributes` - * // constructor may be passed to the `domain_thread_range` constructor where - * // they will be forwarded to the `EventAttribute`s constructor - * nvtx3::thread_range r{nvtx3::payload{42}, nvtx3::category{1}, "message"}; - * \endcode - */ -class event_attributes { - public: - using value_type = nvtxEventAttributes_t; - - /** - * @brief Default constructor creates an `event_attributes` with no - * category, color, payload, nor message. - */ - constexpr event_attributes() noexcept - : attributes_{ - NVTX_VERSION, // version - sizeof(nvtxEventAttributes_t), // size - 0, // category - NVTX_COLOR_UNKNOWN, // color type - 0, // color value - NVTX_PAYLOAD_UNKNOWN, // payload type - {}, // payload value (union) - NVTX_MESSAGE_UNKNOWN, // message type - {} // message value (union) - } - { - } - - /** - * @brief Variadic constructor where the first argument is a `category`. - * - * Sets the value of the `EventAttribute`s category based on `c` and - * forwards the remaining variadic parameter pack to the next constructor. - * - */ - template - NVTX3_RELAXED_CONSTEXPR explicit event_attributes(category const& c, Args const&... args) noexcept - : event_attributes(args...) - { - attributes_.category = c.get_id(); - } - - /** - * @brief Variadic constructor where the first argument is a `color`. - * - * Sets the value of the `EventAttribute`s color based on `c` and forwards - * the remaining variadic parameter pack to the next constructor. - * - */ - template - NVTX3_RELAXED_CONSTEXPR explicit event_attributes(color const& c, Args const&... args) noexcept - : event_attributes(args...) - { - attributes_.color = c.get_value(); - attributes_.colorType = c.get_type(); - } - - /** - * @brief Variadic constructor where the first argument is a `payload`. - * - * Sets the value of the `EventAttribute`s payload based on `p` and forwards - * the remaining variadic parameter pack to the next constructor. - * - */ - template - NVTX3_RELAXED_CONSTEXPR explicit event_attributes(payload const& p, Args const&... args) noexcept - : event_attributes(args...) - { - attributes_.payload = p.get_value(); - attributes_.payloadType = p.get_type(); - } - - /** - * @brief Variadic constructor where the first argument is a `message`. - * - * Sets the value of the `EventAttribute`s message based on `m` and forwards - * the remaining variadic parameter pack to the next constructor. - * - */ - template - explicit event_attributes(message const& m, Args const&... args) noexcept - : event_attributes(args...) - { - attributes_.message = m.get_value(); - attributes_.messageType = m.get_type(); - } - - ~event_attributes() = default; - event_attributes(event_attributes const&) = default; - event_attributes& operator=(event_attributes const&) = default; - event_attributes(event_attributes&&) = default; - event_attributes& operator=(event_attributes&&) = default; - - /** - * @brief Get raw pointer to underlying NVTX attributes object. - * - */ - constexpr value_type const* get() const noexcept { return &attributes_; } - - private: - value_type attributes_{}; ///< The NVTX attributes structure -}; - -/** - * @brief A RAII object for creating a NVTX range local to a thread within a - * domain. - * - * When constructed, begins a nested NVTX range on the calling thread in the - * specified domain. Upon destruction, ends the NVTX range. - * - * Behavior is undefined if a `domain_thread_range` object is - * created/destroyed on different threads. - * - * `domain_thread_range` is neither moveable nor copyable. - * - * `domain_thread_range`s may be nested within other ranges. - * - * The domain of the range is specified by the template type parameter `D`. - * By default, the `domain::global` is used, which scopes the range to the - * global NVTX domain. The convenience alias `thread_range` is provided for - * ranges scoped to the global domain. - * - * A custom domain can be defined by creating a type, `D`, with a static - * member `D::name` whose value is used to name the domain associated with - * `D`. `D::name` must resolve to either `char const*` or `wchar_t const*` - * - * Example: - * ``` - * // Define a type `my_domain` with a member `name` used to name the domain - * // associated with the type `my_domain`. - * struct my_domain{ - * static constexpr const char * name{"my domain"}; - * }; - * ``` - * - * Usage: - * ``` - * nvtx3::domain_thread_range<> r0{"range 0"}; // Range in global domain - * - * nvtx3::thread_range r1{"range 1"}; // Alias for range in global domain - * - * nvtx3::domain_thread_range r2{"range 2"}; // Range in custom - * domain - * - * // specify an alias to a range that uses a custom domain - * using my_thread_range = nvtx3::domain_thread_range; - * - * my_thread_range r3{"range 3"}; // Alias for range in custom domain - * ``` - */ -template -class domain_thread_range { - public: - /** - * @brief Construct a `domain_thread_range` with the specified - * `event_attributes` - * - * Example: - * ``` - * nvtx3::event_attributes attr{"msg", nvtx3::rgb{127,255,0}}; - * nvtx3::domain_thread_range<> range{attr}; // Creates a range with message - * contents - * // "msg" and green color - * ``` - * - * @param[in] attr `event_attributes` that describes the desired attributes - * of the range. - */ - explicit domain_thread_range(event_attributes const& attr) noexcept - { - nvtxDomainRangePushEx(domain::get(), attr.get()); - } - - /** - * @brief Constructs a `domain_thread_range` from the constructor arguments - * of an `event_attributes`. - * - * Forwards the arguments `first, args...` to construct an - * `event_attributes` object. The `event_attributes` object is then - * associated with the `domain_thread_range`. - * - * For more detail, see `event_attributes` documentation. - * - * Example: - * ``` - * // Creates a range with message "message" and green color - * nvtx3::domain_thread_range<> r{"message", nvtx3::rgb{127,255,0}}; - * ``` - * - * @note To prevent making needless copies of `event_attributes` objects, - * this constructor is disabled when the first argument is an - * `event_attributes` object, instead preferring the explicit - * `domain_thread_range(event_attributes const&)` constructor. - * - * @param[in] first First argument to forward to the `event_attributes` - * constructor. - * @param[in] args Variadic parameter pack of additional arguments to - * forward. - * - */ - template >>> - explicit domain_thread_range(First const& first, Args const&... args) noexcept - : domain_thread_range{event_attributes{first, args...}} - { - } - - /** - * @brief Default constructor creates a `domain_thread_range` with no - * message, color, payload, nor category. - * - */ - domain_thread_range() : domain_thread_range{event_attributes{}} {} - - domain_thread_range(domain_thread_range const&) = delete; - domain_thread_range& operator=(domain_thread_range const&) = delete; - domain_thread_range(domain_thread_range&&) = delete; - domain_thread_range& operator=(domain_thread_range&&) = delete; - - /** - * @brief Destroy the domain_thread_range, ending the NVTX range event. - */ - ~domain_thread_range() noexcept { nvtxDomainRangePop(domain::get()); } -}; - -/** - * @brief Alias for a `domain_thread_range` in the global NVTX domain. - */ -using thread_range = domain_thread_range<>; - -/** - * @brief A RAII object for creating a NVTX range within a domain that can be - * created and destroyed on different threads. - * - * When constructed, begins a NVTX range in the specified domain. Upon - * destruction, ends the NVTX range. - * - * Similar to `nvtx3::domain_thread_range`, the only difference being that - * `domain_process_range` can start and end on different threads. - * - * Use of `nvtx3::domain_thread_range` should be preferred unless one needs - * the ability to start and end a range on different threads. - * - * `domain_process_range` is moveable, but not copyable. - * - * @tparam D Type containing `name` member used to identify the `domain` - * to which the `domain_process_range` belongs. Else, `domain::global` to - * indicate that the global NVTX domain should be used. - */ -template -class domain_process_range { - public: - /** - * @brief Construct a new domain process range object - * - * @param attr - */ - explicit domain_process_range(event_attributes const& attr) noexcept - : range_id_{nvtxDomainRangeStartEx(domain::get(), attr.get())} - { - } - - /** - * @brief Construct a new domain process range object - * - * @param first - * @param args - */ - template >>> - explicit domain_process_range(First const& first, Args const&... args) noexcept - : domain_process_range{event_attributes{first, args...}} - { - } - - /** - * @brief Construct a new domain process range object - * - */ - constexpr domain_process_range() noexcept : domain_process_range{event_attributes{}} {} - - /** - * @brief Destroy the `domain_process_range` ending the range. - * - */ - ~domain_process_range() noexcept - { - if (not moved_from_) { nvtxRangeEnd(range_id_); } - } - - domain_process_range(domain_process_range const&) = delete; - domain_process_range& operator=(domain_process_range const&) = delete; - - domain_process_range(domain_process_range&& other) noexcept : range_id_{other.range_id_} - { - other.moved_from_ = true; - } - - domain_process_range& operator=(domain_process_range&& other) noexcept - { - range_id_ = other.range_id_; - other.moved_from_ = true; - } - - private: - nvtxRangeId_t range_id_; ///< Range id used to correlate - ///< the start/end of the range - bool moved_from_{false}; ///< Indicates if the object has had - ///< it's contents moved from it, - ///< indicating it should not attempt - ///< to end the NVTX range. -}; - -/** - * @brief Alias for a `domain_process_range` in the global NVTX domain. - */ -using process_range = domain_process_range<>; - -/** - * @brief Annotates an instantaneous point in time with the attributes specified - * by `attr`. - * - * Unlike a "range", a mark is an instantaneous event in an application, e.g., - * locking/unlocking a mutex. - * - * \code{.cpp} - * std::mutex global_lock; - * void lock_mutex(){ - * global_lock.lock(); - * nvtx3::mark("lock_mutex"); - * } - * \endcode - * - * @tparam D Type containing `name` member used to identify the `domain` - * to which the `domain_process_range` belongs. Else, `domain::global` to - * indicate that the global NVTX domain should be used. - * @param[in] attr `event_attributes` that describes the desired attributes - * of the mark. - */ -template -inline void mark(event_attributes const& attr) noexcept -{ - nvtxDomainMarkEx(domain::get(), attr.get()); -} - -} // namespace nvtx3 - -/** - * @brief Convenience macro for generating a range in the specified `domain` - * from the lifetime of a function - * - * This macro is useful for generating an NVTX range in `domain` from - * the entry point of a function to its exit. It is intended to be the first - * line of the function. - * - * Constructs a static `registered_message` using the name of the immediately - * enclosing function returned by `__func__` and constructs a - * `nvtx3::thread_range` using the registered function name as the range's - * message. - * - * Example: - * ``` - * struct my_domain{static constexpr char const* name{"my_domain"};}; - * - * void foo(...){ - * NVTX3_FUNC_RANGE_IN(my_domain); // Range begins on entry to foo() - * // do stuff - * ... - * } // Range ends on return from foo() - * ``` - * - * @param[in] D Type containing `name` member used to identify the - * `domain` to which the `registered_message` belongs. Else, - * `domain::global` to indicate that the global NVTX domain should be used. - */ -#define NVTX3_FUNC_RANGE_IN(D) \ - static ::nvtx3::registered_message const nvtx3_func_name__{__func__}; \ - static ::nvtx3::event_attributes const nvtx3_func_attr__{nvtx3_func_name__}; \ - ::nvtx3::domain_thread_range const nvtx3_range__{nvtx3_func_attr__}; - -/** - * @brief Convenience macro for generating a range in the global domain from the - * lifetime of a function. - * - * This macro is useful for generating an NVTX range in the global domain from - * the entry point of a function to its exit. It is intended to be the first - * line of the function. - * - * Constructs a static `registered_message` using the name of the immediately - * enclosing function returned by `__func__` and constructs a - * `nvtx3::thread_range` using the registered function name as the range's - * message. - * - * Example: - * ``` - * void foo(...){ - * NVTX3_FUNC_RANGE(); // Range begins on entry to foo() - * // do stuff - * ... - * } // Range ends on return from foo() - * ``` - */ -#define NVTX3_FUNC_RANGE() NVTX3_FUNC_RANGE_IN(::nvtx3::domain::global) diff --git a/cpp/include/cuspatial/detail/nvtx/ranges.hpp b/cpp/include/cuspatial/detail/nvtx/ranges.hpp index 4e9517378..3ec96c953 100644 --- a/cpp/include/cuspatial/detail/nvtx/ranges.hpp +++ b/cpp/include/cuspatial/detail/nvtx/ranges.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023, NVIDIA CORPORATION. + * Copyright (c) 2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,7 @@ #pragma once -#include "nvtx3.hpp" +#include namespace cuspatial { /** @@ -26,11 +26,6 @@ struct libcuspatial_domain { static constexpr char const* name{"libcuspatial"}; ///< Name of the libcuspatial domain }; -/** - * @brief Alias for an NVTX range in the libcuspatial domain. - */ -using thread_range = ::nvtx3::domain_thread_range; - } // namespace cuspatial /** From 76b26abedc4499118132e3b088e727e83b1be90c Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 25 Apr 2023 10:55:25 -0700 Subject: [PATCH 50/60] fixes broken API usage --- .../experimental/join/quadtree_point_in_polygon_test_large.cu | 3 ++- cpp/tests/experimental/range/multilinestring_range_test.cu | 2 +- cpp/tests/experimental/spatial/point_distance_test.cu | 3 +-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp/tests/experimental/join/quadtree_point_in_polygon_test_large.cu b/cpp/tests/experimental/join/quadtree_point_in_polygon_test_large.cu index a982632c8..4a392b827 100644 --- a/cpp/tests/experimental/join/quadtree_point_in_polygon_test_large.cu +++ b/cpp/tests/experimental/join/quadtree_point_in_polygon_test_large.cu @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -58,7 +59,7 @@ inline auto generate_points( { auto engine = cuspatial::test::deterministic_engine(0); auto uniform = cuspatial::test::make_normal_dist(0.0, 1.0); - auto pgen = cuspatial::test::point_generator(T{0.0}, T{1.0}, engine, uniform); + auto pgen = cuspatial::test::point_generator(cuspatial::vec_2d{0.0, 0.0}, cuspatial::vec_2d{1.0, 1.0}, engine, engine, uniform, uniform); auto num_points = quads.size() * points_per_quad; rmm::device_uvector> points(num_points, stream, mr); diff --git a/cpp/tests/experimental/range/multilinestring_range_test.cu b/cpp/tests/experimental/range/multilinestring_range_test.cu index 9fcb0097e..7226dc4c0 100644 --- a/cpp/tests/experimental/range/multilinestring_range_test.cu +++ b/cpp/tests/experimental/range/multilinestring_range_test.cu @@ -128,7 +128,7 @@ struct MultilinestringRangeTest : public BaseFixture { thrust::device_vector> got_coordinates(multipoint_range.point_begin(), multipoint_range.point_end()); - auto expected_multipoint = make_multipoints_array(expected); + auto expected_multipoint = make_multipoint_array(expected); auto expected_range = expected_multipoint.range(); thrust::device_vector expected_geometry_offset(expected_range.offsets_begin(), diff --git a/cpp/tests/experimental/spatial/point_distance_test.cu b/cpp/tests/experimental/spatial/point_distance_test.cu index 517a4e605..f28d135fb 100644 --- a/cpp/tests/experimental/spatial/point_distance_test.cu +++ b/cpp/tests/experimental/spatial/point_distance_test.cu @@ -15,7 +15,6 @@ */ #include - #include #include @@ -60,7 +59,7 @@ struct PairwisePointDistanceTest : public ::testing::Test { { auto engine = cuspatial::test::deterministic_engine(0); auto uniform = cuspatial::test::make_normal_dist(0.0, 1.0); - auto pgen = cuspatial::test::point_generator(T{0.0}, T{1.0}, engine, uniform); + auto pgen = cuspatial::test::point_generator(vec_2d{0.0, 0.0}, vec_2d{1.0, 1.0}, engine, engine, uniform, uniform); rmm::device_vector> points(num_points); auto counting_iter = thrust::make_counting_iterator(seed); thrust::transform( From 8d34780ec67d194635cea56bd21f18f85e5757fb Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 25 Apr 2023 14:05:17 -0700 Subject: [PATCH 51/60] using rapids-cpm-find --- cpp/CMakeLists.txt | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index dc756d1e8..34457171a 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -109,12 +109,21 @@ rapids_cpm_init() # find or add cuDF include(cmake/thirdparty/CUSPATIAL_GetCUDF.cmake) -# install nvtx3 -CPMAddPackage( - NAME NVTX - GITHUB_REPOSITORY NVIDIA/NVTX - GIT_TAG v3.1.0-c-cpp - GIT_SHALLOW TRUE) +# # install nvtx3 +# CPMAddPackage( +# NAME NVTX +# GITHUB_REPOSITORY NVIDIA/NVTX +# GIT_TAG v3.1.0-c-cpp +# GIT_SHALLOW TRUE) + +rapids_cpm_find( + NVTX3 3.1.0 + GLOBAL_TARGETS nvtx3-cpp + CPM_ARGS + GITHUB_REPOSITORY NVIDIA/NVTX + GIT_TAG v3.1.0-c-cpp + GIT_SHALLOW TRUE +) # find or install GoogleTest if (CUSPATIAL_BUILD_TESTS) @@ -205,7 +214,8 @@ endif() target_compile_definitions(cuspatial PUBLIC "SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_${RMM_LOGGING_LEVEL}") # Specify the target module library dependencies -target_link_libraries(cuspatial PRIVATE nvtx3-cpp PUBLIC cudf::cudf) +target_link_libraries(cuspatial PUBLIC cudf::cudf) +target_link_libraries(cuspatial PUBLIC nvtx3-cpp) add_library(cuspatial::cuspatial ALIAS cuspatial) From 0e66450e8f2e239781cc7c506c833f47077991ef Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 25 Apr 2023 18:16:52 -0700 Subject: [PATCH 52/60] style --- cpp/include/cuspatial/detail/nvtx/ranges.hpp | 2 +- .../join/quadtree_point_in_polygon_test_large.cu | 7 ++++++- cpp/tests/experimental/spatial/point_distance_test.cu | 5 +++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/cpp/include/cuspatial/detail/nvtx/ranges.hpp b/cpp/include/cuspatial/detail/nvtx/ranges.hpp index 3ec96c953..7cca9d288 100644 --- a/cpp/include/cuspatial/detail/nvtx/ranges.hpp +++ b/cpp/include/cuspatial/detail/nvtx/ranges.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, NVIDIA CORPORATION. + * Copyright (c) 2020-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cpp/tests/experimental/join/quadtree_point_in_polygon_test_large.cu b/cpp/tests/experimental/join/quadtree_point_in_polygon_test_large.cu index 4a392b827..ec3b6312a 100644 --- a/cpp/tests/experimental/join/quadtree_point_in_polygon_test_large.cu +++ b/cpp/tests/experimental/join/quadtree_point_in_polygon_test_large.cu @@ -59,7 +59,12 @@ inline auto generate_points( { auto engine = cuspatial::test::deterministic_engine(0); auto uniform = cuspatial::test::make_normal_dist(0.0, 1.0); - auto pgen = cuspatial::test::point_generator(cuspatial::vec_2d{0.0, 0.0}, cuspatial::vec_2d{1.0, 1.0}, engine, engine, uniform, uniform); + auto pgen = cuspatial::test::point_generator(cuspatial::vec_2d{0.0, 0.0}, + cuspatial::vec_2d{1.0, 1.0}, + engine, + engine, + uniform, + uniform); auto num_points = quads.size() * points_per_quad; rmm::device_uvector> points(num_points, stream, mr); diff --git a/cpp/tests/experimental/spatial/point_distance_test.cu b/cpp/tests/experimental/spatial/point_distance_test.cu index f28d135fb..8b099481c 100644 --- a/cpp/tests/experimental/spatial/point_distance_test.cu +++ b/cpp/tests/experimental/spatial/point_distance_test.cu @@ -14,8 +14,8 @@ * limitations under the License. */ -#include #include +#include #include #include @@ -59,7 +59,8 @@ struct PairwisePointDistanceTest : public ::testing::Test { { auto engine = cuspatial::test::deterministic_engine(0); auto uniform = cuspatial::test::make_normal_dist(0.0, 1.0); - auto pgen = cuspatial::test::point_generator(vec_2d{0.0, 0.0}, vec_2d{1.0, 1.0}, engine, engine, uniform, uniform); + auto pgen = cuspatial::test::point_generator( + vec_2d{0.0, 0.0}, vec_2d{1.0, 1.0}, engine, engine, uniform, uniform); rmm::device_vector> points(num_points); auto counting_iter = thrust::make_counting_iterator(seed); thrust::transform( From b089297154d484354984cce991aa586a16e468d8 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 25 Apr 2023 18:46:10 -0700 Subject: [PATCH 53/60] add build export set arg --- cpp/CMakeLists.txt | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 34457171a..5d03ae65d 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -109,16 +109,11 @@ rapids_cpm_init() # find or add cuDF include(cmake/thirdparty/CUSPATIAL_GetCUDF.cmake) -# # install nvtx3 -# CPMAddPackage( -# NAME NVTX -# GITHUB_REPOSITORY NVIDIA/NVTX -# GIT_TAG v3.1.0-c-cpp -# GIT_SHALLOW TRUE) - rapids_cpm_find( NVTX3 3.1.0 GLOBAL_TARGETS nvtx3-cpp + BUILD_EXPORT_SET cuspatial-exports + INSTALL_EXPORT_SET cuspatial-exports CPM_ARGS GITHUB_REPOSITORY NVIDIA/NVTX GIT_TAG v3.1.0-c-cpp @@ -215,7 +210,7 @@ target_compile_definitions(cuspatial PUBLIC "SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_${ # Specify the target module library dependencies target_link_libraries(cuspatial PUBLIC cudf::cudf) -target_link_libraries(cuspatial PUBLIC nvtx3-cpp) +target_link_libraries(cuspatial PRIVATE nvtx3-cpp) add_library(cuspatial::cuspatial ALIAS cuspatial) From 545fdc25f4bc2aa98998cc872f3d582189575ed3 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 3 May 2023 14:15:22 -0700 Subject: [PATCH 54/60] update cmakelists.txt --- cpp/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index e9cf13c41..91aa06971 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -109,6 +109,8 @@ rapids_cpm_init() # find or add cuDF include(cmake/thirdparty/CUSPATIAL_GetCUDF.cmake) +if (USE_NVTX) + rapids_cpm_find( NVTX3 3.1.0 GLOBAL_TARGETS nvtx3-cpp @@ -118,7 +120,11 @@ rapids_cpm_find( GITHUB_REPOSITORY NVIDIA/NVTX GIT_TAG v3.1.0-c-cpp GIT_SHALLOW TRUE + DOWNLOAD_ONLY TRUE ) +include(nvtxImportedTargets.cmake) + +endif() # find or install GoogleTest if (CUSPATIAL_BUILD_TESTS) From aa8169b8e5678b4de8821158fcf36bbb9a80499b Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 4 May 2023 20:51:50 +0000 Subject: [PATCH 55/60] Move --- cpp/benchmarks/{ => distance}/pairwise_point_polygon_distance.cu | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename cpp/benchmarks/{ => distance}/pairwise_point_polygon_distance.cu (100%) diff --git a/cpp/benchmarks/pairwise_point_polygon_distance.cu b/cpp/benchmarks/distance/pairwise_point_polygon_distance.cu similarity index 100% rename from cpp/benchmarks/pairwise_point_polygon_distance.cu rename to cpp/benchmarks/distance/pairwise_point_polygon_distance.cu From db628beedda03a044e5cc47c2d6fe8d118a404f9 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 4 May 2023 21:11:09 +0000 Subject: [PATCH 56/60] Fix bad renames --- cpp/include/cuspatial_test/vector_factories.cuh | 9 +++++++-- .../equality/pairwise_multipoint_equals_count_test.cu | 4 ++-- cpp/tests/point_in_polygon/point_in_polygon_test.cu | 2 +- cpp/tests/range/multipolygon_range_test.cu | 4 ++-- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/cpp/include/cuspatial_test/vector_factories.cuh b/cpp/include/cuspatial_test/vector_factories.cuh index 7bd6ebed9..67caed444 100644 --- a/cpp/include/cuspatial_test/vector_factories.cuh +++ b/cpp/include/cuspatial_test/vector_factories.cuh @@ -348,9 +348,14 @@ class multipoint_array { * coordinates */ template -auto make_multipoints_array(GeometryRange geometry_inl, CoordRange coordinates_inl) +auto make_multipoint_array(GeometryRange geometry_inl, CoordRange coordinates_inl) { - return multipoint_array{make_device_vector(geometry_inl), make_device_vector(coordinates_inl)}; + using IndexType = typename GeometryRange::value_type; + using CoordType = typename CoordRange::value_type; + using DeviceIndexVector = thrust::device_vector; + using DeviceCoordVector = thrust::device_vector; + + return multipoint_array{make_device_vector(geometry_inl), make_device_vector(coordinates_inl)}; } /** diff --git a/cpp/tests/equality/pairwise_multipoint_equals_count_test.cu b/cpp/tests/equality/pairwise_multipoint_equals_count_test.cu index a12420858..a7d5f57de 100644 --- a/cpp/tests/equality/pairwise_multipoint_equals_count_test.cu +++ b/cpp/tests/equality/pairwise_multipoint_equals_count_test.cu @@ -32,8 +32,8 @@ struct PairwiseMultipointEqualsCountTest : public BaseFixture { std::initializer_list>> rhs_coordinates, std::initializer_list expected) { - auto larray = make_multipoints_array(lhs_coordinates); - auto rarray = make_multipoints_array(rhs_coordinates); + auto larray = make_multipoint_array(lhs_coordinates); + auto rarray = make_multipoint_array(rhs_coordinates); auto lhs = larray.range(); auto rhs = rarray.range(); diff --git a/cpp/tests/point_in_polygon/point_in_polygon_test.cu b/cpp/tests/point_in_polygon/point_in_polygon_test.cu index d958a9a97..00e4229ed 100644 --- a/cpp/tests/point_in_polygon/point_in_polygon_test.cu +++ b/cpp/tests/point_in_polygon/point_in_polygon_test.cu @@ -383,7 +383,7 @@ TYPED_TEST(PointInPolygonTest, ContainsButCollinearWithBoundary) { using T = TypeParam; - auto point = cuspatial::test::make_multipoints_array({{{0.5, 0.5}}}); + auto point = cuspatial::test::make_multipoint_array({{{0.5, 0.5}}}); auto polygon = cuspatial::test::make_multipolygon_array( {0, 1}, {0, 1}, diff --git a/cpp/tests/range/multipolygon_range_test.cu b/cpp/tests/range/multipolygon_range_test.cu index 0f1b08809..f1ca60437 100644 --- a/cpp/tests/range/multipolygon_range_test.cu +++ b/cpp/tests/range/multipolygon_range_test.cu @@ -140,10 +140,10 @@ struct MultipolygonRangeTest : public BaseFixture { multipolygon_coordinates); auto rng = multipolygon_array.range().as_multipoint_range(); - auto got = make_multipoints_array(range(rng.offsets_begin(), rng.offsets_end()), + auto got = make_multipoint_array(range(rng.offsets_begin(), rng.offsets_end()), range(rng.point_begin(), rng.point_end())); - auto expected = make_multipoints_array( + auto expected = make_multipoint_array( range(multipoint_geometry_offset.begin(), multipoint_geometry_offset.end()), range(multipoint_coordinates.begin(), multipoint_coordinates.end())); From cc6ff278ad9c042c32873fdfbd6129c7335fe4af Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 5 May 2023 16:43:59 +0000 Subject: [PATCH 57/60] Record progress --- cpp/CMakeLists.txt | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 053f83d7d..af85e5593 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -114,15 +114,13 @@ if (USE_NVTX) rapids_cpm_find( NVTX3 3.1.0 GLOBAL_TARGETS nvtx3-cpp - BUILD_EXPORT_SET cuspatial-exports - INSTALL_EXPORT_SET cuspatial-exports CPM_ARGS GITHUB_REPOSITORY NVIDIA/NVTX GIT_TAG v3.1.0-c-cpp GIT_SHALLOW TRUE DOWNLOAD_ONLY TRUE ) -include(nvtxImportedTargets.cmake) +include(${NVTX3_SOURCE_DIR}/nvtxImportedTargets.cmake) endif() @@ -268,6 +266,11 @@ install(TARGETS cuspatial install(DIRECTORY ${CUSPATIAL_SOURCE_DIR}/include/cuspatial DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +message("Installing ${NVTX3_SOURCE_DIR}/include/nvtx3 to /home/coder/scratch") + +install(DIRECTORY ${NVTX3_SOURCE_DIR} + DESTINATION /home/coder/scratch) + set(doc_string [=[ Provide targets for the cuSpatial library. @@ -292,6 +295,32 @@ rapids_export( DOCUMENTATION doc_string ) +set(nvtx3_final_code_block + [=[ + if(NOT TARGET nvtx3-c) + add_library(nvtx3-c INTERFACE IMPORTED GLOBAL) + set_target_properties(nvtx3-c PROPERTIES VERSION ${NVTX3_VERSION}) + target_include_directories(nvtx3-c INTERFACE + set_target_properties(nvtx3-c PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include") + target_link_libraries(nvtx3-c INTERFACE ${CMAKE_DL_LIBS}) + + #------------------------------------------------------- + # Define "nvtx3-cpp" library for the NVTX v3 C++ API + # Separate target allows attaching independent compiler requirements if needed + add_library(nvtx3-cpp INTERFACE IMPORTED GLOBAL) + set_target_properties(nvtx3-cpp PROPERTIES VERSION ${NVTX3_VERSION}) + target_link_libraries(nvtx3-cpp INTERFACE nvtx3-c) + endif() + ]=] +) + +rapids_export( + INSTALL nvtx3-cpp + EXPORT_SET cuspatial-exports + GLOBAL_TARGETS nvtx3-cpp + FINAL_CODE_BLOCK nvtx3_final_code_block +) ################################################################################################ # - build export ------------------------------------------------------------------------------- @@ -304,6 +333,12 @@ rapids_export( DOCUMENTATION doc_string ) +rapids_export( + BUILD nvtx3-cpp + EXPORT_SET cuspatial-exports + GLOBAL_TARGETS nvtx3-cpp + FINAL_CODE_BLOCK nvtx3_final_code_block +) # ################################################################################################## # * build documentation ---------------------------------------------------------------------------- From 4d4cf2861a95b79d52f27fe8125d4cb09ba28e93 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 9 May 2023 17:00:05 -0700 Subject: [PATCH 58/60] revert non nvtx3 cmake releated changes --- .../pairwise_point_polygon_distance.cu | 134 -------- .../distance/point_polygon_distance.cuh | 3 - cpp/include/cuspatial/detail/nvtx/ranges.hpp | 13 +- .../cuspatial_test/geometry_generator.cuh | 311 ------------------ cpp/include/cuspatial_test/random.cuh | 17 +- .../cuspatial_test/vector_factories.cuh | 44 +-- cpp/tests/distance/point_distance_test.cu | 6 +- .../distance/point_polygon_distance_test.cu | 2 +- .../pairwise_multipoint_equals_count_test.cu | 4 +- cpp/tests/find/find_duplicate_points_test.cu | 6 +- .../find/find_points_on_segments_test.cu | 2 +- .../quadtree_point_in_polygon_test_large.cu | 7 +- .../point_in_polygon/point_in_polygon_test.cu | 2 +- cpp/tests/range/multilinestring_range_test.cu | 2 +- cpp/tests/range/multipolygon_range_test.cu | 4 +- .../utility_test/test_multipoint_factory.cu | 10 +- 16 files changed, 42 insertions(+), 525 deletions(-) delete mode 100644 cpp/benchmarks/distance/pairwise_point_polygon_distance.cu delete mode 100644 cpp/include/cuspatial_test/geometry_generator.cuh diff --git a/cpp/benchmarks/distance/pairwise_point_polygon_distance.cu b/cpp/benchmarks/distance/pairwise_point_polygon_distance.cu deleted file mode 100644 index e143f0ea3..000000000 --- a/cpp/benchmarks/distance/pairwise_point_polygon_distance.cu +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c) 2023, NVIDIA CORPORATION. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include - -#include -#include - -#include -#include - -using namespace cuspatial; -using namespace cuspatial::test; - -template -void pairwise_point_polygon_distance_benchmark(nvbench::state& state, nvbench::type_list) -{ - // TODO: to be replaced by nvbench fixture once it's ready - cuspatial::rmm_pool_raii rmm_pool; - rmm::cuda_stream_view stream{rmm::cuda_stream_default}; - - auto const num_pairs{static_cast(state.get_int64("num_pairs"))}; - - auto const num_polygons_per_multipolygon{ - static_cast(state.get_int64("num_polygons_per_multipolygon"))}; - auto const num_holes_per_polygon{ - static_cast(state.get_int64("num_holes_per_polygon"))}; - auto const num_edges_per_ring{static_cast(state.get_int64("num_edges_per_ring"))}; - - auto const num_points_per_multipoint{ - static_cast(state.get_int64("num_points_per_multipoint"))}; - - auto mpoly_generator_param = multipolygon_generator_parameter{ - num_pairs, num_polygons_per_multipolygon, num_holes_per_polygon, num_edges_per_ring}; - - auto mpoint_generator_param = multipoint_generator_parameter{ - num_pairs, num_points_per_multipoint, vec_2d{-1, -1}, vec_2d{0, 0}}; - - auto multipolygons = generate_multipolygon_array(mpoly_generator_param, stream); - auto multipoints = generate_multipoint_array(mpoint_generator_param, stream); - - auto distances = rmm::device_vector(num_pairs); - auto out_it = distances.begin(); - - auto mpoly_view = multipolygons.range(); - auto mpoint_view = multipoints.range(); - - state.add_element_count(num_pairs, "NumPairs"); - state.add_element_count(mpoly_generator_param.num_polygons(), "NumPolygons"); - state.add_element_count(mpoly_generator_param.num_rings(), "NumRings"); - state.add_element_count(mpoly_generator_param.num_coords(), "NumPoints (in mpoly)"); - state.add_element_count(static_cast(mpoly_generator_param.num_coords() * - mpoly_generator_param.num_rings() * - mpoly_generator_param.num_polygons()), - "Multipolygon Complexity"); - state.add_element_count(mpoint_generator_param.num_points(), "NumPoints (in multipoints)"); - - state.add_global_memory_reads( - mpoly_generator_param.num_coords() + mpoint_generator_param.num_points(), - "CoordinatesReadSize"); - state.add_global_memory_reads( - (mpoly_generator_param.num_rings() + 1) + (mpoly_generator_param.num_polygons() + 1) + - (mpoly_generator_param.num_multipolygons + 1) + (mpoint_generator_param.num_multipoints + 1), - "OffsetsDataSize"); - - state.add_global_memory_writes(num_pairs); - - state.exec(nvbench::exec_tag::sync, - [&mpoly_view, &mpoint_view, &out_it, &stream](nvbench::launch& launch) { - pairwise_point_polygon_distance(mpoint_view, mpoly_view, out_it, stream); - }); -} - -using floating_point_types = nvbench::type_list; - -// Benchmark scalability with simple multipolygon (3 sides, 0 hole, 1 poly) -NVBENCH_BENCH_TYPES(pairwise_point_polygon_distance_benchmark, - NVBENCH_TYPE_AXES(floating_point_types)) - .set_type_axes_names({"CoordsType"}) - .add_int64_axis("num_pairs", {1, 1'00, 10'000, 1'000'000, 100'000'000}) - .add_int64_axis("num_polygons_per_multipolygon", {1}) - .add_int64_axis("num_holes_per_polygon", {0}) - .add_int64_axis("num_edges_per_ring", {3}) - .add_int64_axis("num_points_per_multipoint", {1}) - .set_name("point_polygon_distance_benchmark_simple_polygon"); - -// Benchmark scalability with complex multipolygon (100 sides, 10 holes, 3 polys) -NVBENCH_BENCH_TYPES(pairwise_point_polygon_distance_benchmark, - NVBENCH_TYPE_AXES(floating_point_types)) - .set_type_axes_names({"CoordsType"}) - .add_int64_axis("num_pairs", {1'000, 10'000, 100'000, 1'000'000}) - .add_int64_axis("num_polygons_per_multipolygon", {2}) - .add_int64_axis("num_holes_per_polygon", {3}) - .add_int64_axis("num_edges_per_ring", {50}) - .add_int64_axis("num_points_per_multipoint", {1}) - .set_name("point_polygon_distance_benchmark_complex_polygon"); - -// // Benchmark impact of rings (100K pairs, 1 polygon, 3 sides) -NVBENCH_BENCH_TYPES(pairwise_point_polygon_distance_benchmark, - NVBENCH_TYPE_AXES(floating_point_types)) - .set_type_axes_names({"CoordsType"}) - .add_int64_axis("num_pairs", {10'000}) - .add_int64_axis("num_polygons_per_multipolygon", {1}) - .add_int64_axis("num_holes_per_polygon", {0, 10, 100, 1000}) - .add_int64_axis("num_edges_per_ring", {3}) - .add_int64_axis("num_points_per_multipoint", {1}) - .set_name("point_polygon_distance_benchmark_ring_numbers"); - -// Benchmark impact of rings (1M pairs, 1 polygon, 0 holes, 3 sides) -NVBENCH_BENCH_TYPES(pairwise_point_polygon_distance_benchmark, - NVBENCH_TYPE_AXES(floating_point_types)) - .set_type_axes_names({"CoordsType"}) - .add_int64_axis("num_pairs", {100}) - .add_int64_axis("num_polygons_per_multipolygon", {1}) - .add_int64_axis("num_holes_per_polygon", {0}) - .add_int64_axis("num_edges_per_ring", {3}) - .add_int64_axis("num_points_per_multipoint", {50, 5'00, 5'000, 50'000, 500'000}) - .set_name("point_polygon_distance_benchmark_points_in_multipoint"); diff --git a/cpp/include/cuspatial/detail/distance/point_polygon_distance.cuh b/cpp/include/cuspatial/detail/distance/point_polygon_distance.cuh index 5253c3ce0..1729b6775 100644 --- a/cpp/include/cuspatial/detail/distance/point_polygon_distance.cuh +++ b/cpp/include/cuspatial/detail/distance/point_polygon_distance.cuh @@ -20,7 +20,6 @@ #include #include -#include #include #include #include @@ -90,8 +89,6 @@ OutputIt pairwise_point_polygon_distance(MultiPointRange multipoints, OutputIt distances_first, rmm::cuda_stream_view stream) { - CUSPATIAL_FUNC_RANGE(); - using T = typename MultiPointRange::element_t; using index_t = typename MultiPointRange::index_t; diff --git a/cpp/include/cuspatial/detail/nvtx/ranges.hpp b/cpp/include/cuspatial/detail/nvtx/ranges.hpp index 7cca9d288..1757ae1e5 100644 --- a/cpp/include/cuspatial/detail/nvtx/ranges.hpp +++ b/cpp/include/cuspatial/detail/nvtx/ranges.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023, NVIDIA CORPORATION. + * Copyright (c) 2020, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,16 +16,21 @@ #pragma once -#include +#include "nvtx3.hpp" namespace cuspatial { /** - * @brief Tag type for libcuspatial's NVTX domain. + * @brief Tag type for libcudf's NVTX domain. */ struct libcuspatial_domain { - static constexpr char const* name{"libcuspatial"}; ///< Name of the libcuspatial domain + static constexpr char const* name{"libcuspatial"}; ///< Name of the libcudf domain }; +/** + * @brief Alias for an NVTX range in the libcudf domain. + */ +using thread_range = ::nvtx3::domain_thread_range; + } // namespace cuspatial /** diff --git a/cpp/include/cuspatial_test/geometry_generator.cuh b/cpp/include/cuspatial_test/geometry_generator.cuh deleted file mode 100644 index 2e3ab27c4..000000000 --- a/cpp/include/cuspatial_test/geometry_generator.cuh +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Copyright (c) 2023, NVIDIA CORPORATION. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -namespace cuspatial { -namespace test { - -/** - * @brief Struct to store the parameters of the multipolygon array generator - * - * @tparam T Type of the coordinates - */ -template -struct multipolygon_generator_parameter { - using element_t = T; - - std::size_t num_multipolygons; - std::size_t num_polygons_per_multipolygon; - std::size_t num_holes_per_polygon; - std::size_t num_edges_per_ring; - vec_2d centroid; - T radius; - - CUSPATIAL_HOST_DEVICE std::size_t num_polygons() - { - return num_multipolygons * num_polygons_per_multipolygon; - } - CUSPATIAL_HOST_DEVICE std::size_t num_rings() { return num_polygons() * num_rings_per_polygon(); } - CUSPATIAL_HOST_DEVICE std::size_t num_coords() { return num_rings() * num_vertices_per_ring(); } - CUSPATIAL_HOST_DEVICE std::size_t num_vertices_per_ring() { return num_edges_per_ring + 1; } - CUSPATIAL_HOST_DEVICE std::size_t num_rings_per_polygon() { return num_holes_per_polygon + 1; } - CUSPATIAL_HOST_DEVICE T hole_radius() { return radius / (num_holes_per_polygon + 1); } -}; - -/** - * @brief Generate coordinates for the ring based on the local index of the point. - * - * The ring is generated by walking a point around a centroid with a fixed radius. - * Each step has equal angles. - * - * @tparam T Type of coordinate - * @param point_local_idx Local index of the point - * @param num_edges Number of sides of the polygon - * @param centroid Centroid of the ring - * @param radius Radius of the ring - * @return Coordinate of the point - */ -template -vec_2d __device__ generate_ring_coordinate(std::size_t point_local_idx, - std::size_t num_edges, - vec_2d centroid, - T radius) -{ - // Overrides last coordinate to make sure ring is closed. - if (point_local_idx == num_edges) return vec_2d{centroid.x + radius, centroid.y}; - - T angle = (2.0 * M_PI * point_local_idx) / num_edges; - - return vec_2d{centroid.x + radius * cos(angle), centroid.y + radius * sin(angle)}; -} - -/** - * @brief Apply displacement to the centroid of a polygon. - * - * The `i`th polygon's centroid is displaced by (3*radius*i, 0). This makes sure - * polygons within a multipolygon does not overlap. - * - * @tparam T Type of the coordinates - * @param centroid The first centroid of the polygons - * @param part_local_idx Local index of the polygon - * @param radius Radius of each polygon - * @return Displaced centroid - */ -template -vec_2d __device__ polygon_centroid_displacement(vec_2d centroid, - std::size_t part_local_idx, - T radius) -{ - return centroid + vec_2d{part_local_idx * radius * T{3.0}, T{0.0}}; -} - -/** - * @brief Given a ring centroid, displace it based on its ring index. - * - * A Polygon contains at least 1 shell. It may contain 0 or more holes. - * The shell is the leading ring of the polygon (index 0). All holes' centroid - * has the same y value as the shell's centroid. Holes are aligned from left - * to right on the center axis, with no overlapping areas. It may look like: - * - * ****** - * ** ** - * * * - * * * - * * * - * @@@ @@@ @@@ @@@ * - * @ @ @ @ @ * - * @ @ @ @ @ * - * @ @ @ @ @ * - * *@@@ @@@ @@@ @@@ * - * * * - * * * - * * * - * * * - * ** ** - * ****** - * - * - * @tparam T Type of the coordinates - * @param centroid The center of the polygon - * @param ring_local_idx Local index of the ring - * @param radius Radius of the polygon - * @param hole_radius Radius of each hole - * @return Centroid of the ring - */ -template -vec_2d __device__ -ring_centroid_displacement(vec_2d centroid, std::size_t ring_local_idx, T radius, T hole_radius) -{ - // This is a shell - if (ring_local_idx == 0) { return centroid; } - - // This is a hole - ring_local_idx -= 1; // offset hole indices to be 0-based - T max_hole_displacement = radius - hole_radius; - T displacement_x = -max_hole_displacement + ring_local_idx * hole_radius * 2; - T displacement_y = 0.0; - return centroid + vec_2d{displacement_x, displacement_y}; -} - -/** - * @brief Kernel to generate coordinates for multipolygon arrays. - * - * @pre This kernel requires that the three offset arrays (geometry, part, ring) has been prefilled - * with the correct offsets. - * - * @tparam T Type of the coordinate - * @tparam MultipolygonRange A specialization of `multipolygon_range` - * @param multipolygons The range of multipolygons - * @param params Parameters to generate the mulitpolygons - */ -template -void __global__ generate_multipolygon_array_coordinates(MultipolygonRange multipolygons, - multipolygon_generator_parameter params) -{ - for (auto idx = threadIdx.x + blockIdx.x * blockDim.x; idx < multipolygons.num_points(); - idx += gridDim.x * blockDim.x) { - auto ring_idx = multipolygons.ring_idx_from_point_idx(idx); - auto part_idx = multipolygons.part_idx_from_ring_idx(ring_idx); - auto geometry_idx = multipolygons.geometry_idx_from_part_idx(part_idx); - - auto point_local_idx = idx - params.num_vertices_per_ring() * ring_idx; - auto ring_local_idx = ring_idx - params.num_rings_per_polygon() * part_idx; - auto part_local_idx = part_idx - params.num_polygons_per_multipolygon * geometry_idx; - - auto centroid = ring_centroid_displacement( - polygon_centroid_displacement(params.centroid, part_local_idx, params.radius), - ring_local_idx, - params.radius, - params.hole_radius()); - - if (ring_local_idx == 0) // Generate coordinate for shell - multipolygons.point_begin()[idx] = generate_ring_coordinate( - point_local_idx, params.num_edges_per_ring, centroid, params.radius); - else // Generate coordinate for holes - multipolygons.point_begin()[idx] = generate_ring_coordinate( - point_local_idx, params.num_edges_per_ring, centroid, params.hole_radius()); - } -} - -/** - * @brief Helper to generate multipolygon arrays used for tests and benchmarks. - * - * @tparam T The floating point type for the coordinates - * @param params The parameters to set for the multipolygon array - * @param stream The CUDA stream to use for device memory operations and kernel launches - * @return A cuspatial::test::multipolygon_array object. - */ -template -auto generate_multipolygon_array(multipolygon_generator_parameter params, - rmm::cuda_stream_view stream) -{ - rmm::device_uvector geometry_offsets(params.num_multipolygons + 1, stream); - rmm::device_uvector part_offsets(params.num_polygons() + 1, stream); - rmm::device_uvector ring_offsets(params.num_rings() + 1, stream); - rmm::device_uvector> coordinates(params.num_coords(), stream); - - thrust::sequence(rmm::exec_policy(stream), - ring_offsets.begin(), - ring_offsets.end(), - std::size_t{0}, - params.num_vertices_per_ring()); - - thrust::sequence(rmm::exec_policy(stream), - part_offsets.begin(), - part_offsets.end(), - std::size_t{0}, - params.num_rings_per_polygon()); - - thrust::sequence(rmm::exec_policy(stream), - geometry_offsets.begin(), - geometry_offsets.end(), - std::size_t{0}, - params.num_polygons_per_multipolygon); - - auto multipolygons = multipolygon_range(geometry_offsets.begin(), - geometry_offsets.end(), - part_offsets.begin(), - part_offsets.end(), - ring_offsets.begin(), - ring_offsets.end(), - coordinates.begin(), - coordinates.end()); - - auto [tpb, nblocks] = grid_1d(multipolygons.num_points()); - - generate_multipolygon_array_coordinates<<>>(multipolygons, params); - - CUSPATIAL_CHECK_CUDA(stream.value()); - - return make_multipolygon_array>(std::move(geometry_offsets), - std::move(part_offsets), - std::move(ring_offsets), - std::move(coordinates)); -} - -/** - * @brief Struct to store the parameters of the multipoint aray - * - * @tparam T Type of the coordinates - */ -template -struct multipoint_generator_parameter { - using element_t = T; - - std::size_t num_multipoints; - std::size_t num_points_per_multipoints; - vec_2d lower_left; - vec_2d upper_right; - - CUSPATIAL_HOST_DEVICE std::size_t num_points() - { - return num_multipoints * num_points_per_multipoints; - } -}; - -/** - * @brief Helper to generate random multipoints within a range - * - * @tparam T The floating point type for the coordinates - * @param params Parameters to specify for the multipoints - * @param stream The CUDA stream to use for device memory operations and kernel launches - * @return a cuspatial::test::multipoint_array object - */ -template -auto generate_multipoint_array(multipoint_generator_parameter params, - rmm::cuda_stream_view stream) -{ - rmm::device_uvector> coordinates(params.num_points(), stream); - rmm::device_uvector offsets(params.num_multipoints + 1, stream); - - thrust::sequence(rmm::exec_policy(stream), - offsets.begin(), - offsets.end(), - std::size_t{0}, - params.num_points_per_multipoints); - - auto engine_x = deterministic_engine(params.num_points()); - auto engine_y = deterministic_engine(2 * params.num_points()); - - auto x_dist = make_uniform_dist(params.lower_left.x, params.upper_right.x); - auto y_dist = make_uniform_dist(params.lower_left.y, params.upper_right.y); - - auto point_gen = - point_generator(params.lower_left, params.upper_right, engine_x, engine_y, x_dist, y_dist); - - thrust::tabulate(rmm::exec_policy(stream), coordinates.begin(), coordinates.end(), point_gen); - - return make_multipoint_array(std::move(offsets), std::move(coordinates)); -} - -} // namespace test -} // namespace cuspatial diff --git a/cpp/include/cuspatial_test/random.cuh b/cpp/include/cuspatial_test/random.cuh index d2a65af3b..8cd6b71e2 100644 --- a/cpp/include/cuspatial_test/random.cuh +++ b/cpp/include/cuspatial_test/random.cuh @@ -154,21 +154,14 @@ struct value_generator { template struct point_generator { using Cart2D = cuspatial::vec_2d; - value_generator vgenx; - value_generator vgeny; - - point_generator(vec_2d lower_left, - vec_2d upper_right, - thrust::minstd_rand& engine_x, - thrust::minstd_rand& engine_y, - Generator gen_x, - Generator gen_y) - : vgenx(lower_left.x, upper_right.x, engine_x, gen_x), - vgeny(lower_left.y, upper_right.y, engine_y, gen_y) + value_generator vgen; + + point_generator(T lower_bound, T upper_bound, thrust::minstd_rand& engine, Generator gen) + : vgen(lower_bound, upper_bound, engine, gen) { } - __device__ Cart2D operator()(size_t n) { return {vgenx(n), vgeny(n)}; } + __device__ Cart2D operator()(size_t n) { return {vgen(n), vgen(n)}; } }; /** diff --git a/cpp/include/cuspatial_test/vector_factories.cuh b/cpp/include/cuspatial_test/vector_factories.cuh index 67caed444..020645f94 100644 --- a/cpp/include/cuspatial_test/vector_factories.cuh +++ b/cpp/include/cuspatial_test/vector_factories.cuh @@ -309,22 +309,11 @@ auto make_multilinestring_array(std::initializer_list geometry_inl, template class multipoint_array { public: - using geometry_t = typename GeometryArray::value_type; - using coord_t = typename CoordinateArray::value_type; - - multipoint_array(thrust::device_vector geometry_offsets_array, - thrust::device_vector coordinate_array) + multipoint_array(GeometryArray geometry_offsets_array, CoordinateArray coordinate_array) : _geometry_offsets(geometry_offsets_array), _coordinates(coordinate_array) { } - multipoint_array(rmm::device_uvector&& geometry_offsets_array, - rmm::device_uvector&& coordinate_array) - : _geometry_offsets(std::move(geometry_offsets_array)), - _coordinates(std::move(coordinate_array)) - { - } - /// Return the number of multipoints auto size() { return _geometry_offsets.size() - 1; } @@ -348,14 +337,9 @@ class multipoint_array { * coordinates */ template -auto make_multipoint_array(GeometryRange geometry_inl, CoordRange coordinates_inl) +auto make_multipoints_array(GeometryRange geometry_inl, CoordRange coordinates_inl) { - using IndexType = typename GeometryRange::value_type; - using CoordType = typename CoordRange::value_type; - using DeviceIndexVector = thrust::device_vector; - using DeviceCoordVector = thrust::device_vector; - - return multipoint_array{make_device_vector(geometry_inl), make_device_vector(coordinates_inl)}; + return multipoint_array{make_device_vector(geometry_inl), make_device_vector(coordinates_inl)}; } /** @@ -363,17 +347,17 @@ auto make_multipoint_array(GeometryRange geometry_inl, CoordRange coordinates_in * * Example: Construct an array of 2 multipoints, each with 2, 0, 1 points: * using P = vec_2d; - * make_multipoint_array({{P{0.0, 1.0}, P{2.0, 0.0}}, {}, {P{3.0, 4.0}}}); + * make_multipoints_array({{P{0.0, 1.0}, P{2.0, 0.0}}, {}, {P{3.0, 4.0}}}); * * Example: Construct an empty multilinestring array: - * make_multipoint_array({}); // Explicit parameter required to deduce type. + * make_multipoints_array({}); // Explicit parameter required to deduce type. * * @tparam T Type of coordinate * @param inl List of multipoints * @return multipoints_array object */ template -auto make_multipoint_array(std::initializer_list>> inl) +auto make_multipoints_array(std::initializer_list>> inl) { std::vector offsets{0}; std::transform(inl.begin(), inl.end(), std::back_inserter(offsets), [](auto multipoint) { @@ -387,20 +371,8 @@ auto make_multipoint_array(std::initializer_list return init; }); - return multipoint_array, rmm::device_vector>>{ - rmm::device_vector(offsets), rmm::device_vector>(coordinates)}; -} - -/** - * @brief Factory method to construct multipoint array by moving the offsets and coordinates from - * `rmm::device_uvector`. - */ -template -auto make_multipoint_array(rmm::device_uvector geometry_offsets, - rmm::device_uvector> coords) -{ - return multipoint_array, rmm::device_uvector>>{ - std::move(geometry_offsets), std::move(coords)}; + return multipoint_array{rmm::device_vector(offsets), + rmm::device_vector>(coordinates)}; } } // namespace test diff --git a/cpp/tests/distance/point_distance_test.cu b/cpp/tests/distance/point_distance_test.cu index 72476ee19..573c5232a 100644 --- a/cpp/tests/distance/point_distance_test.cu +++ b/cpp/tests/distance/point_distance_test.cu @@ -14,9 +14,10 @@ * limitations under the License. */ -#include #include +#include + #include #include #include @@ -58,8 +59,7 @@ struct PairwisePointDistanceTest : public ::testing::Test { { auto engine = cuspatial::test::deterministic_engine(0); auto uniform = cuspatial::test::make_normal_dist(0.0, 1.0); - auto pgen = cuspatial::test::point_generator( - vec_2d{0.0, 0.0}, vec_2d{1.0, 1.0}, engine, engine, uniform, uniform); + auto pgen = cuspatial::test::point_generator(T{0.0}, T{1.0}, engine, uniform); rmm::device_vector> points(num_points); auto counting_iter = thrust::make_counting_iterator(seed); thrust::transform( diff --git a/cpp/tests/distance/point_polygon_distance_test.cu b/cpp/tests/distance/point_polygon_distance_test.cu index c16d324e0..2cee8da4b 100644 --- a/cpp/tests/distance/point_polygon_distance_test.cu +++ b/cpp/tests/distance/point_polygon_distance_test.cu @@ -61,7 +61,7 @@ struct PairwisePointPolygonDistanceTest : public ::testing::Test { std::vector> const& multipolygon_coordinates, std::initializer_list expected) { - auto d_multipoints = make_multipoint_array(multipoints); + auto d_multipoints = make_multipoints_array(multipoints); auto d_multipolygons = make_multipolygon_array( range{multipolygon_geometry_offsets.begin(), multipolygon_geometry_offsets.end()}, range{multipolygon_part_offsets.begin(), multipolygon_part_offsets.end()}, diff --git a/cpp/tests/equality/pairwise_multipoint_equals_count_test.cu b/cpp/tests/equality/pairwise_multipoint_equals_count_test.cu index a7d5f57de..a12420858 100644 --- a/cpp/tests/equality/pairwise_multipoint_equals_count_test.cu +++ b/cpp/tests/equality/pairwise_multipoint_equals_count_test.cu @@ -32,8 +32,8 @@ struct PairwiseMultipointEqualsCountTest : public BaseFixture { std::initializer_list>> rhs_coordinates, std::initializer_list expected) { - auto larray = make_multipoint_array(lhs_coordinates); - auto rarray = make_multipoint_array(rhs_coordinates); + auto larray = make_multipoints_array(lhs_coordinates); + auto rarray = make_multipoints_array(rhs_coordinates); auto lhs = larray.range(); auto rhs = rarray.range(); diff --git a/cpp/tests/find/find_duplicate_points_test.cu b/cpp/tests/find/find_duplicate_points_test.cu index 49bc86bb9..f4185227c 100644 --- a/cpp/tests/find/find_duplicate_points_test.cu +++ b/cpp/tests/find/find_duplicate_points_test.cu @@ -41,7 +41,7 @@ TYPED_TEST(FindDuplicatePointsTest, simple) using T = TypeParam; using P = vec_2d; - auto multipoints = make_multipoint_array({{P{0.0, 0.0}, P{1.0, 0.0}, P{0.0, 0.0}}}); + auto multipoints = make_multipoints_array({{P{0.0, 0.0}, P{1.0, 0.0}, P{0.0, 0.0}}}); rmm::device_vector flags(multipoints.range().num_points()); std::vector expected_flags{0, 0, 1}; @@ -56,7 +56,7 @@ TYPED_TEST(FindDuplicatePointsTest, empty) using T = TypeParam; using P = vec_2d; - auto multipoints = make_multipoint_array({}); + auto multipoints = make_multipoints_array({}); rmm::device_vector flags(multipoints.range().num_points()); std::vector expected_flags{}; @@ -71,7 +71,7 @@ TYPED_TEST(FindDuplicatePointsTest, multi) using T = TypeParam; using P = vec_2d; - auto multipoints = make_multipoint_array( + auto multipoints = make_multipoints_array( {{P{0.0, 0.0}, P{1.0, 0.0}, P{0.0, 0.0}, P{0.0, 0.0}, P{1.0, 0.0}, P{2.0, 0.0}}, {P{5.0, 5.0}, P{5.0, 5.0}}, {P{0.0, 0.0}}}); diff --git a/cpp/tests/find/find_points_on_segments_test.cu b/cpp/tests/find/find_points_on_segments_test.cu index 4296f6479..8981986bc 100644 --- a/cpp/tests/find/find_points_on_segments_test.cu +++ b/cpp/tests/find/find_points_on_segments_test.cu @@ -41,7 +41,7 @@ struct FindPointOnSegmentTest : public BaseFixture { std::initializer_list> segments, std::initializer_list expected_flags) { - auto d_multipoints = make_multipoint_array(multipoints); + auto d_multipoints = make_multipoints_array(multipoints); auto d_segment_offsets = make_device_vector(segment_offsets); auto d_segments = make_device_vector>(segments); diff --git a/cpp/tests/join/quadtree_point_in_polygon_test_large.cu b/cpp/tests/join/quadtree_point_in_polygon_test_large.cu index 8da2a2aed..14a956016 100644 --- a/cpp/tests/join/quadtree_point_in_polygon_test_large.cu +++ b/cpp/tests/join/quadtree_point_in_polygon_test_large.cu @@ -58,12 +58,7 @@ inline auto generate_points( { auto engine = cuspatial::test::deterministic_engine(0); auto uniform = cuspatial::test::make_normal_dist(0.0, 1.0); - auto pgen = cuspatial::test::point_generator(cuspatial::vec_2d{0.0, 0.0}, - cuspatial::vec_2d{1.0, 1.0}, - engine, - engine, - uniform, - uniform); + auto pgen = cuspatial::test::point_generator(T{0.0}, T{1.0}, engine, uniform); auto num_points = quads.size() * points_per_quad; rmm::device_uvector> points(num_points, stream, mr); diff --git a/cpp/tests/point_in_polygon/point_in_polygon_test.cu b/cpp/tests/point_in_polygon/point_in_polygon_test.cu index 00e4229ed..d958a9a97 100644 --- a/cpp/tests/point_in_polygon/point_in_polygon_test.cu +++ b/cpp/tests/point_in_polygon/point_in_polygon_test.cu @@ -383,7 +383,7 @@ TYPED_TEST(PointInPolygonTest, ContainsButCollinearWithBoundary) { using T = TypeParam; - auto point = cuspatial::test::make_multipoint_array({{{0.5, 0.5}}}); + auto point = cuspatial::test::make_multipoints_array({{{0.5, 0.5}}}); auto polygon = cuspatial::test::make_multipolygon_array( {0, 1}, {0, 1}, diff --git a/cpp/tests/range/multilinestring_range_test.cu b/cpp/tests/range/multilinestring_range_test.cu index ec374ef8f..0d00d5c5f 100644 --- a/cpp/tests/range/multilinestring_range_test.cu +++ b/cpp/tests/range/multilinestring_range_test.cu @@ -128,7 +128,7 @@ struct MultilinestringRangeTest : public BaseFixture { thrust::device_vector> got_coordinates(multipoint_range.point_begin(), multipoint_range.point_end()); - auto expected_multipoint = make_multipoint_array(expected); + auto expected_multipoint = make_multipoints_array(expected); auto expected_range = expected_multipoint.range(); thrust::device_vector expected_geometry_offset(expected_range.offsets_begin(), diff --git a/cpp/tests/range/multipolygon_range_test.cu b/cpp/tests/range/multipolygon_range_test.cu index f1ca60437..0f1b08809 100644 --- a/cpp/tests/range/multipolygon_range_test.cu +++ b/cpp/tests/range/multipolygon_range_test.cu @@ -140,10 +140,10 @@ struct MultipolygonRangeTest : public BaseFixture { multipolygon_coordinates); auto rng = multipolygon_array.range().as_multipoint_range(); - auto got = make_multipoint_array(range(rng.offsets_begin(), rng.offsets_end()), + auto got = make_multipoints_array(range(rng.offsets_begin(), rng.offsets_end()), range(rng.point_begin(), rng.point_end())); - auto expected = make_multipoint_array( + auto expected = make_multipoints_array( range(multipoint_geometry_offset.begin(), multipoint_geometry_offset.end()), range(multipoint_coordinates.begin(), multipoint_coordinates.end())); diff --git a/cpp/tests/utility_test/test_multipoint_factory.cu b/cpp/tests/utility_test/test_multipoint_factory.cu index ad800cf98..f7b1ba0a4 100644 --- a/cpp/tests/utility_test/test_multipoint_factory.cu +++ b/cpp/tests/utility_test/test_multipoint_factory.cu @@ -34,7 +34,7 @@ TYPED_TEST(MultiPointFactoryTest, simple) using P = vec_2d; auto multipoints = - make_multipoint_array({{P{0.0, 0.0}, P{1.0, 0.0}}, {P{2.0, 0.0}, P{2.0, 2.0}}}); + make_multipoints_array({{P{0.0, 0.0}, P{1.0, 0.0}}, {P{2.0, 0.0}, P{2.0, 2.0}}}); auto [offsets, coords] = multipoints.release(); @@ -51,7 +51,7 @@ TYPED_TEST(MultiPointFactoryTest, empty) using T = TypeParam; using P = vec_2d; - auto multipoints = make_multipoint_array({}); + auto multipoints = make_multipoints_array({}); auto [offsets, coords] = multipoints.release(); @@ -67,7 +67,7 @@ TYPED_TEST(MultiPointFactoryTest, mixed_empty_multipoint) using T = TypeParam; using P = vec_2d; - auto multipoints = make_multipoint_array({{P{1.0, 0.0}}, {}, {P{2.0, 3.0}, P{4.0, 5.0}}}); + auto multipoints = make_multipoints_array({{P{1.0, 0.0}}, {}, {P{2.0, 3.0}, P{4.0, 5.0}}}); auto [offsets, coords] = multipoints.release(); @@ -83,7 +83,7 @@ TYPED_TEST(MultiPointFactoryTest, mixed_empty_multipoint2) using T = TypeParam; using P = vec_2d; - auto multipoints = make_multipoint_array({{}, {P{1.0, 0.0}}, {P{2.0, 3.0}, P{4.0, 5.0}}}); + auto multipoints = make_multipoints_array({{}, {P{1.0, 0.0}}, {P{2.0, 3.0}, P{4.0, 5.0}}}); auto [offsets, coords] = multipoints.release(); @@ -99,7 +99,7 @@ TYPED_TEST(MultiPointFactoryTest, mixed_empty_multipoint3) using T = TypeParam; using P = vec_2d; - auto multipoints = make_multipoint_array({{P{1.0, 0.0}}, {P{2.0, 3.0}, P{4.0, 5.0}}, {}}); + auto multipoints = make_multipoints_array({{P{1.0, 0.0}}, {P{2.0, 3.0}, P{4.0, 5.0}}, {}}); auto [offsets, coords] = multipoints.release(); From 1b978cd239b0e0d57d4ea13f7c4d26a3152d87e8 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 9 May 2023 17:01:09 -0700 Subject: [PATCH 59/60] add file that got removed --- .../cuspatial_test/geometry_generator.cuh | 255 ++++++++++++++++ .../test_point_linestring_distance.py | 287 ++++++++++++++++++ 2 files changed, 542 insertions(+) create mode 100644 cpp/include/cuspatial_test/geometry_generator.cuh create mode 100644 python/cuspatial/cuspatial/tests/spatial/distance/test_point_linestring_distance.py diff --git a/cpp/include/cuspatial_test/geometry_generator.cuh b/cpp/include/cuspatial_test/geometry_generator.cuh new file mode 100644 index 000000000..4dd2dc73a --- /dev/null +++ b/cpp/include/cuspatial_test/geometry_generator.cuh @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace cuspatial { +namespace test { + +/** + * @brief Struct to store the parameters of the multipolygon array generator + * + * @tparam T Type of the coordinates + */ +template +struct multipolygon_generator_parameter { + using element_t = T; + + std::size_t num_multipolygons; + std::size_t num_polygons_per_multipolygon; + std::size_t num_holes_per_polygon; + std::size_t num_edges_per_ring; + vec_2d centroid; + T radius; + + CUSPATIAL_HOST_DEVICE std::size_t num_polygons() + { + return num_multipolygons * num_polygons_per_multipolygon; + } + CUSPATIAL_HOST_DEVICE std::size_t num_rings() { return num_polygons() * num_rings_per_polygon(); } + CUSPATIAL_HOST_DEVICE std::size_t num_coords() { return num_rings() * num_vertices_per_ring(); } + CUSPATIAL_HOST_DEVICE std::size_t num_vertices_per_ring() { return num_edges_per_ring + 1; } + CUSPATIAL_HOST_DEVICE std::size_t num_rings_per_polygon() { return num_holes_per_polygon + 1; } + CUSPATIAL_HOST_DEVICE T hole_radius() { return radius / (num_holes_per_polygon + 1); } +}; + +/** + * @brief Generate coordinates for the ring based on the local index of the point. + * + * The ring is generated by walking a point around a centroid with a fixed radius. + * Each step has equal angles. + * + * @tparam T Type of coordinate + * @param point_local_idx Local index of the point + * @param num_edges Number of sides of the polygon + * @param centroid Centroid of the ring + * @param radius Radius of the ring + * @return Coordinate of the point + */ +template +vec_2d __device__ generate_ring_coordinate(std::size_t point_local_idx, + std::size_t num_edges, + vec_2d centroid, + T radius) +{ + // Overrides last coordinate to make sure ring is closed. + if (point_local_idx == num_edges) return vec_2d{centroid.x + radius, centroid.y}; + + T angle = (2.0 * M_PI * point_local_idx) / num_edges; + + return vec_2d{centroid.x + radius * cos(angle), centroid.y + radius * sin(angle)}; +} + +/** + * @brief Apply displacement to the centroid of a polygon. + * + * The `i`th polygon's centroid is displaced by (3*radius*i, 0). This makes sure + * polygons within a multipolygon does not overlap. + * + * @tparam T Type of the coordinates + * @param centroid The first centroid of the polygons + * @param part_local_idx Local index of the polygon + * @param radius Radius of each polygon + * @return Displaced centroid + */ +template +vec_2d __device__ polygon_centroid_displacement(vec_2d centroid, + std::size_t part_local_idx, + T radius) +{ + return centroid + vec_2d{part_local_idx * radius * T{3.0}, T{0.0}}; +} + +/** + * @brief Given a ring centroid, displace it based on its ring index. + * + * A Polygon contains at least 1 shell. It may contain 0 or more holes. + * The shell is the leading ring of the polygon (index 0). All holes' centroid + * has the same y value as the shell's centroid. Holes are aligned from left + * to right on the center axis, with no overlapping areas. It may look like: + * + * ****** + * ** ** + * * * + * * * + * * * + * @@@ @@@ @@@ @@@ * + * @ @ @ @ @ * + * @ @ @ @ @ * + * @ @ @ @ @ * + * *@@@ @@@ @@@ @@@ * + * * * + * * * + * * * + * * * + * ** ** + * ****** + * + * + * @tparam T Type of the coordinates + * @param centroid The center of the polygon + * @param ring_local_idx Local index of the ring + * @param radius Radius of the polygon + * @param hole_radius Radius of each hole + * @return Centroid of the ring + */ +template +vec_2d __device__ +ring_centroid_displacement(vec_2d centroid, std::size_t ring_local_idx, T radius, T hole_radius) +{ + // This is a shell + if (ring_local_idx == 0) { return centroid; } + + // This is a hole + ring_local_idx -= 1; // offset hole indices to be 0-based + T max_hole_displacement = radius - hole_radius; + T displacement_x = -max_hole_displacement + ring_local_idx * hole_radius * 2; + T displacement_y = 0.0; + return centroid + vec_2d{displacement_x, displacement_y}; +} + +/** + * @brief Kernel to generate coordinates for multipolygon arrays. + * + * @pre This kernel requires that the three offset arrays (geometry, part, ring) has been prefilled + * with the correct offsets. + * + * @tparam T Type of the coordinate + * @tparam MultipolygonRange A specialization of `multipolygon_range` + * @param multipolygons The range of multipolygons + * @param params Parameters to generate the mulitpolygons + */ +template +void __global__ generate_multipolygon_array_coordinates(MultipolygonRange multipolygons, + multipolygon_generator_parameter params) +{ + for (auto idx = threadIdx.x + blockIdx.x * blockDim.x; idx < multipolygons.num_points(); + idx += gridDim.x * blockDim.x) { + auto ring_idx = multipolygons.ring_idx_from_point_idx(idx); + auto part_idx = multipolygons.part_idx_from_ring_idx(ring_idx); + auto geometry_idx = multipolygons.geometry_idx_from_part_idx(part_idx); + + auto point_local_idx = idx - params.num_vertices_per_ring() * ring_idx; + auto ring_local_idx = ring_idx - params.num_rings_per_polygon() * part_idx; + auto part_local_idx = part_idx - params.num_polygons_per_multipolygon * geometry_idx; + + auto centroid = ring_centroid_displacement( + polygon_centroid_displacement(params.centroid, part_local_idx, params.radius), + ring_local_idx, + params.radius, + params.hole_radius()); + + if (ring_local_idx == 0) // Generate coordinate for shell + multipolygons.point_begin()[idx] = generate_ring_coordinate( + point_local_idx, params.num_edges_per_ring, centroid, params.radius); + else // Generate coordinate for holes + multipolygons.point_begin()[idx] = generate_ring_coordinate( + point_local_idx, params.num_edges_per_ring, centroid, params.hole_radius()); + } +} + +/** + * @brief Helper to generate multipolygon arrays used for tests and benchmarks. + * + * @tparam T The floating point type for the coordinates + * @param params The parameters to set for the multipolygon array + * @param stream The CUDA stream to use for device memory operations and kernel launches + * @return A cuspatial::test::multipolygon_array object. + */ +template +auto generate_multipolygon_array(multipolygon_generator_parameter params, + rmm::cuda_stream_view stream) +{ + rmm::device_uvector geometry_offsets(params.num_multipolygons + 1, stream); + rmm::device_uvector part_offsets(params.num_polygons() + 1, stream); + rmm::device_uvector ring_offsets(params.num_rings() + 1, stream); + rmm::device_uvector> coordinates(params.num_coords(), stream); + + thrust::sequence(rmm::exec_policy(stream), + ring_offsets.begin(), + ring_offsets.end(), + std::size_t{0}, + params.num_vertices_per_ring()); + + thrust::sequence(rmm::exec_policy(stream), + part_offsets.begin(), + part_offsets.end(), + std::size_t{0}, + params.num_rings_per_polygon()); + + thrust::sequence(rmm::exec_policy(stream), + geometry_offsets.begin(), + geometry_offsets.end(), + std::size_t{0}, + params.num_polygons_per_multipolygon); + + auto multipolygons = multipolygon_range(geometry_offsets.begin(), + geometry_offsets.end(), + part_offsets.begin(), + part_offsets.end(), + ring_offsets.begin(), + ring_offsets.end(), + coordinates.begin(), + coordinates.end()); + + auto [tpb, nblocks] = grid_1d(multipolygons.num_points()); + + generate_multipolygon_array_coordinates<<>>(multipolygons, params); + + CUSPATIAL_CHECK_CUDA(stream.value()); + + return make_multipolygon_array>(std::move(geometry_offsets), + std::move(part_offsets), + std::move(ring_offsets), + std::move(coordinates)); +} + +} // namespace test +} // namespace cuspatial diff --git a/python/cuspatial/cuspatial/tests/spatial/distance/test_point_linestring_distance.py b/python/cuspatial/cuspatial/tests/spatial/distance/test_point_linestring_distance.py new file mode 100644 index 000000000..f7636cf72 --- /dev/null +++ b/python/cuspatial/cuspatial/tests/spatial/distance/test_point_linestring_distance.py @@ -0,0 +1,287 @@ +import geopandas as gpd +import pytest +from shapely.geometry import ( + LineString, + MultiLineString, + MultiPoint, + Point, + Polygon, +) + +import cudf + +from cuspatial import pairwise_point_linestring_distance +from cuspatial.io.geopandas import from_geopandas + + +@pytest.mark.parametrize( + "points, lines", + [ + ([Point(0, 0)], [LineString([(1, 0), (0, 1)])]), + ([MultiPoint([(0, 0), (0.5, 0.5)])], [LineString([(1, 0), (0, 1)])]), + ( + [Point(0, 0)], + [MultiLineString([[(1, 0), (0, 1)], [(0, 0), (1, 1)]])], + ), + ( + [MultiPoint([(0, 0), (1, 1)])], + [MultiLineString([[(1, 0), (0, 1)], [(0, 0), (1, 1)]])], + ), + ], +) +def test_one_pair(points, lines): + hpts = gpd.GeoSeries(points) + hls = gpd.GeoSeries(lines) + + gpts = from_geopandas(hpts) + gls = from_geopandas(hls) + + got = pairwise_point_linestring_distance(gpts, gls) + expected = hpts.distance(hls) + + cudf.testing.assert_series_equal(got, cudf.Series(expected)) + + +@pytest.mark.parametrize("num_pairs", [1, 2, 100]) +def test_random_point_linestring_pairs( + num_pairs, point_generator, linestring_generator +): + max_num_segments_per_linestring = 50 + + hpts = gpd.GeoSeries([*point_generator(num_pairs)]) + hlines = gpd.GeoSeries( + [*linestring_generator(num_pairs, max_num_segments_per_linestring)] + ) + + gpts = from_geopandas(hpts) + glines = from_geopandas(hlines) + + got = pairwise_point_linestring_distance(gpts, glines) + expected = hpts.distance(hlines) + + cudf.testing.assert_series_equal(got, cudf.Series(expected)) + + +@pytest.mark.parametrize("num_pairs", [1, 2, 100]) +def test_random_multipoint_linestring_pairs( + num_pairs, multipoint_generator, linestring_generator +): + max_num_points_per_multipoint = 10 + max_num_segments_per_linestring = 50 + + hpts = gpd.GeoSeries( + [*multipoint_generator(num_pairs, max_num_points_per_multipoint)] + ) + hlines = gpd.GeoSeries( + [*linestring_generator(num_pairs, max_num_segments_per_linestring)] + ) + + gpts = from_geopandas(hpts) + glines = from_geopandas(hlines) + + got = pairwise_point_linestring_distance(gpts, glines) + expected = hpts.distance(hlines) + + cudf.testing.assert_series_equal(got, cudf.Series(expected)) + + +@pytest.mark.parametrize("num_pairs", [1, 2, 100]) +def test_random_point_multilinestring_pairs( + num_pairs, point_generator, multilinestring_generator +): + max_num_linestring_per_multilinestring = 10 + max_num_segments_per_linestring = 50 + + hpts = gpd.GeoSeries([*point_generator(num_pairs)]) + hlines = gpd.GeoSeries( + [ + *multilinestring_generator( + num_pairs, + max_num_linestring_per_multilinestring, + max_num_segments_per_linestring, + ) + ] + ) + + gpts = from_geopandas(hpts) + glines = from_geopandas(hlines) + + got = pairwise_point_linestring_distance(gpts, glines) + expected = hpts.distance(hlines) + + cudf.testing.assert_series_equal(got, cudf.Series(expected)) + + +@pytest.mark.parametrize("num_pairs", [1, 2, 100]) +def test_random_multipoint_multilinestring_pairs( + num_pairs, multipoint_generator, multilinestring_generator +): + max_num_points_per_multipoint = 10 + max_num_linestring_per_multilinestring = 10 + max_num_segments_per_linestring = 50 + + hpts = gpd.GeoSeries( + [*multipoint_generator(num_pairs, max_num_points_per_multipoint)] + ) + hlines = gpd.GeoSeries( + [ + *multilinestring_generator( + num_pairs, + max_num_linestring_per_multilinestring, + max_num_segments_per_linestring, + ) + ] + ) + + gpts = from_geopandas(hpts) + glines = from_geopandas(hlines) + + got = pairwise_point_linestring_distance(gpts, glines) + expected = hpts.distance(hlines) + + cudf.testing.assert_series_equal(got, cudf.Series(expected)) + + +@pytest.mark.parametrize( + "lhs, rhs", + [ + ( + [Point(0, 0), LineString([(1.0, 1.0), (1.0, 2.0)])], + [ + LineString([(0.5, 0.5), (1.2, 1.7)]), + LineString([(8.0, 6.4), (11.3, 21.7)]), + ], + ), + ( + [Point(1.0, 2.0), Point(1.0, 1.0)], + [ + LineString([(0.5, 0.5), (1.2, 1.7)]), + Polygon([(8.0, 6.4), (0.0, 0.0), (-5.0, -7.8)]), + ], + ), + ], +) +def test_mixed_geometry_series_raise(lhs, rhs): + lhs = from_geopandas(gpd.GeoSeries(lhs)) + rhs = from_geopandas(gpd.GeoSeries(rhs)) + + with pytest.raises(ValueError, match=".*must contain only.*"): + pairwise_point_linestring_distance(lhs, rhs) + + +def test_multipoint_multilinestring_sliced_pairs( + multipoint_generator, multilinestring_generator +): + num_pairs = 2 + max_num_linestring_per_multilinestring = 2 + max_num_segments_per_linestring = 2 + max_num_points_per_multipoint = 2 + + hpts = gpd.GeoSeries( + [*multipoint_generator(num_pairs, max_num_points_per_multipoint)] + ) + hlines = gpd.GeoSeries( + [ + *multilinestring_generator( + num_pairs, + max_num_linestring_per_multilinestring, + max_num_segments_per_linestring, + ) + ] + ) + + gpts = from_geopandas(hpts) + glines = from_geopandas(hlines) + + hslicepts = hpts[0:1] + hslicelines = hlines[0:1] + slicegpts = gpts[0:1] + sliceglines = glines[0:1] + got = pairwise_point_linestring_distance(slicegpts, sliceglines) + expected = hslicepts.distance(hslicelines) + + cudf.testing.assert_series_equal(got, cudf.Series(expected)) + + hslicepts = hpts[1:2] + hslicelines = hlines[1:2] + slicegpts = gpts[1:2] + sliceglines = glines[1:2] + got = pairwise_point_linestring_distance(slicegpts, sliceglines) + expected = hslicepts.distance(hslicelines) + + # Just dropping the index for now, that is not libcuSpatial's problem + got.index = [1] + cudf.testing.assert_series_equal(got, cudf.Series(expected)) + + +@pytest.mark.parametrize("slice_index", [0, 1, 2, 3, 4]) +def test_multipoint_multilinestring_sliced_many( + multipoint_generator, multilinestring_generator, slice_twenty, slice_index +): + num_pairs = 20 + max_num_linestring_per_multilinestring = 5 + max_num_segments_per_linestring = 5 + max_num_points_per_multipoint = 5 + + hpts = gpd.GeoSeries( + [*multipoint_generator(num_pairs, max_num_points_per_multipoint)] + ) + hlines = gpd.GeoSeries( + [ + *multilinestring_generator( + num_pairs, + max_num_linestring_per_multilinestring, + max_num_segments_per_linestring, + ) + ] + ) + + gpts = from_geopandas(hpts) + glines = from_geopandas(hlines) + + pslice = slice_twenty[slice_index] + hslicepts = hpts[pslice] + hslicelines = hlines[pslice] + slicegpts = gpts[pslice] + sliceglines = glines[pslice] + got = pairwise_point_linestring_distance(slicegpts, sliceglines) + expected = hslicepts.distance(hslicelines) + got.index = cudf.RangeIndex(pslice.start, pslice.stop) + + cudf.testing.assert_series_equal(got, cudf.Series(expected)) + + +def test_multipoint_multilinestring_sliced_noncontiguous( + multipoint_generator, multilinestring_generator +): + num_pairs = 20 + max_num_linestring_per_multilinestring = 5 + max_num_segments_per_linestring = 5 + max_num_points_per_multipoint = 5 + + hpts = gpd.GeoSeries( + [*multipoint_generator(num_pairs, max_num_points_per_multipoint)] + ) + hlines = gpd.GeoSeries( + [ + *multilinestring_generator( + num_pairs, + max_num_linestring_per_multilinestring, + max_num_segments_per_linestring, + ) + ] + ) + + gpts = from_geopandas(hpts) + glines = from_geopandas(hlines) + + pslice = [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] + hslicepts = hpts[pslice] + hslicelines = hlines[pslice] + slicegpts = gpts[pslice] + sliceglines = glines[pslice] + got = pairwise_point_linestring_distance(slicegpts, sliceglines) + expected = hslicepts.distance(hslicelines) + got.index = pslice + + cudf.testing.assert_series_equal(got, cudf.Series(expected)) From 960d921806739903d28507710b5550fa2d0fe263 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 9 May 2023 17:03:39 -0700 Subject: [PATCH 60/60] remove file --- .../test_point_linestring_distance.py | 287 ------------------ 1 file changed, 287 deletions(-) delete mode 100644 python/cuspatial/cuspatial/tests/spatial/distance/test_point_linestring_distance.py diff --git a/python/cuspatial/cuspatial/tests/spatial/distance/test_point_linestring_distance.py b/python/cuspatial/cuspatial/tests/spatial/distance/test_point_linestring_distance.py deleted file mode 100644 index f7636cf72..000000000 --- a/python/cuspatial/cuspatial/tests/spatial/distance/test_point_linestring_distance.py +++ /dev/null @@ -1,287 +0,0 @@ -import geopandas as gpd -import pytest -from shapely.geometry import ( - LineString, - MultiLineString, - MultiPoint, - Point, - Polygon, -) - -import cudf - -from cuspatial import pairwise_point_linestring_distance -from cuspatial.io.geopandas import from_geopandas - - -@pytest.mark.parametrize( - "points, lines", - [ - ([Point(0, 0)], [LineString([(1, 0), (0, 1)])]), - ([MultiPoint([(0, 0), (0.5, 0.5)])], [LineString([(1, 0), (0, 1)])]), - ( - [Point(0, 0)], - [MultiLineString([[(1, 0), (0, 1)], [(0, 0), (1, 1)]])], - ), - ( - [MultiPoint([(0, 0), (1, 1)])], - [MultiLineString([[(1, 0), (0, 1)], [(0, 0), (1, 1)]])], - ), - ], -) -def test_one_pair(points, lines): - hpts = gpd.GeoSeries(points) - hls = gpd.GeoSeries(lines) - - gpts = from_geopandas(hpts) - gls = from_geopandas(hls) - - got = pairwise_point_linestring_distance(gpts, gls) - expected = hpts.distance(hls) - - cudf.testing.assert_series_equal(got, cudf.Series(expected)) - - -@pytest.mark.parametrize("num_pairs", [1, 2, 100]) -def test_random_point_linestring_pairs( - num_pairs, point_generator, linestring_generator -): - max_num_segments_per_linestring = 50 - - hpts = gpd.GeoSeries([*point_generator(num_pairs)]) - hlines = gpd.GeoSeries( - [*linestring_generator(num_pairs, max_num_segments_per_linestring)] - ) - - gpts = from_geopandas(hpts) - glines = from_geopandas(hlines) - - got = pairwise_point_linestring_distance(gpts, glines) - expected = hpts.distance(hlines) - - cudf.testing.assert_series_equal(got, cudf.Series(expected)) - - -@pytest.mark.parametrize("num_pairs", [1, 2, 100]) -def test_random_multipoint_linestring_pairs( - num_pairs, multipoint_generator, linestring_generator -): - max_num_points_per_multipoint = 10 - max_num_segments_per_linestring = 50 - - hpts = gpd.GeoSeries( - [*multipoint_generator(num_pairs, max_num_points_per_multipoint)] - ) - hlines = gpd.GeoSeries( - [*linestring_generator(num_pairs, max_num_segments_per_linestring)] - ) - - gpts = from_geopandas(hpts) - glines = from_geopandas(hlines) - - got = pairwise_point_linestring_distance(gpts, glines) - expected = hpts.distance(hlines) - - cudf.testing.assert_series_equal(got, cudf.Series(expected)) - - -@pytest.mark.parametrize("num_pairs", [1, 2, 100]) -def test_random_point_multilinestring_pairs( - num_pairs, point_generator, multilinestring_generator -): - max_num_linestring_per_multilinestring = 10 - max_num_segments_per_linestring = 50 - - hpts = gpd.GeoSeries([*point_generator(num_pairs)]) - hlines = gpd.GeoSeries( - [ - *multilinestring_generator( - num_pairs, - max_num_linestring_per_multilinestring, - max_num_segments_per_linestring, - ) - ] - ) - - gpts = from_geopandas(hpts) - glines = from_geopandas(hlines) - - got = pairwise_point_linestring_distance(gpts, glines) - expected = hpts.distance(hlines) - - cudf.testing.assert_series_equal(got, cudf.Series(expected)) - - -@pytest.mark.parametrize("num_pairs", [1, 2, 100]) -def test_random_multipoint_multilinestring_pairs( - num_pairs, multipoint_generator, multilinestring_generator -): - max_num_points_per_multipoint = 10 - max_num_linestring_per_multilinestring = 10 - max_num_segments_per_linestring = 50 - - hpts = gpd.GeoSeries( - [*multipoint_generator(num_pairs, max_num_points_per_multipoint)] - ) - hlines = gpd.GeoSeries( - [ - *multilinestring_generator( - num_pairs, - max_num_linestring_per_multilinestring, - max_num_segments_per_linestring, - ) - ] - ) - - gpts = from_geopandas(hpts) - glines = from_geopandas(hlines) - - got = pairwise_point_linestring_distance(gpts, glines) - expected = hpts.distance(hlines) - - cudf.testing.assert_series_equal(got, cudf.Series(expected)) - - -@pytest.mark.parametrize( - "lhs, rhs", - [ - ( - [Point(0, 0), LineString([(1.0, 1.0), (1.0, 2.0)])], - [ - LineString([(0.5, 0.5), (1.2, 1.7)]), - LineString([(8.0, 6.4), (11.3, 21.7)]), - ], - ), - ( - [Point(1.0, 2.0), Point(1.0, 1.0)], - [ - LineString([(0.5, 0.5), (1.2, 1.7)]), - Polygon([(8.0, 6.4), (0.0, 0.0), (-5.0, -7.8)]), - ], - ), - ], -) -def test_mixed_geometry_series_raise(lhs, rhs): - lhs = from_geopandas(gpd.GeoSeries(lhs)) - rhs = from_geopandas(gpd.GeoSeries(rhs)) - - with pytest.raises(ValueError, match=".*must contain only.*"): - pairwise_point_linestring_distance(lhs, rhs) - - -def test_multipoint_multilinestring_sliced_pairs( - multipoint_generator, multilinestring_generator -): - num_pairs = 2 - max_num_linestring_per_multilinestring = 2 - max_num_segments_per_linestring = 2 - max_num_points_per_multipoint = 2 - - hpts = gpd.GeoSeries( - [*multipoint_generator(num_pairs, max_num_points_per_multipoint)] - ) - hlines = gpd.GeoSeries( - [ - *multilinestring_generator( - num_pairs, - max_num_linestring_per_multilinestring, - max_num_segments_per_linestring, - ) - ] - ) - - gpts = from_geopandas(hpts) - glines = from_geopandas(hlines) - - hslicepts = hpts[0:1] - hslicelines = hlines[0:1] - slicegpts = gpts[0:1] - sliceglines = glines[0:1] - got = pairwise_point_linestring_distance(slicegpts, sliceglines) - expected = hslicepts.distance(hslicelines) - - cudf.testing.assert_series_equal(got, cudf.Series(expected)) - - hslicepts = hpts[1:2] - hslicelines = hlines[1:2] - slicegpts = gpts[1:2] - sliceglines = glines[1:2] - got = pairwise_point_linestring_distance(slicegpts, sliceglines) - expected = hslicepts.distance(hslicelines) - - # Just dropping the index for now, that is not libcuSpatial's problem - got.index = [1] - cudf.testing.assert_series_equal(got, cudf.Series(expected)) - - -@pytest.mark.parametrize("slice_index", [0, 1, 2, 3, 4]) -def test_multipoint_multilinestring_sliced_many( - multipoint_generator, multilinestring_generator, slice_twenty, slice_index -): - num_pairs = 20 - max_num_linestring_per_multilinestring = 5 - max_num_segments_per_linestring = 5 - max_num_points_per_multipoint = 5 - - hpts = gpd.GeoSeries( - [*multipoint_generator(num_pairs, max_num_points_per_multipoint)] - ) - hlines = gpd.GeoSeries( - [ - *multilinestring_generator( - num_pairs, - max_num_linestring_per_multilinestring, - max_num_segments_per_linestring, - ) - ] - ) - - gpts = from_geopandas(hpts) - glines = from_geopandas(hlines) - - pslice = slice_twenty[slice_index] - hslicepts = hpts[pslice] - hslicelines = hlines[pslice] - slicegpts = gpts[pslice] - sliceglines = glines[pslice] - got = pairwise_point_linestring_distance(slicegpts, sliceglines) - expected = hslicepts.distance(hslicelines) - got.index = cudf.RangeIndex(pslice.start, pslice.stop) - - cudf.testing.assert_series_equal(got, cudf.Series(expected)) - - -def test_multipoint_multilinestring_sliced_noncontiguous( - multipoint_generator, multilinestring_generator -): - num_pairs = 20 - max_num_linestring_per_multilinestring = 5 - max_num_segments_per_linestring = 5 - max_num_points_per_multipoint = 5 - - hpts = gpd.GeoSeries( - [*multipoint_generator(num_pairs, max_num_points_per_multipoint)] - ) - hlines = gpd.GeoSeries( - [ - *multilinestring_generator( - num_pairs, - max_num_linestring_per_multilinestring, - max_num_segments_per_linestring, - ) - ] - ) - - gpts = from_geopandas(hpts) - glines = from_geopandas(hlines) - - pslice = [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] - hslicepts = hpts[pslice] - hslicelines = hlines[pslice] - slicegpts = gpts[pslice] - sliceglines = glines[pslice] - got = pairwise_point_linestring_distance(slicegpts, sliceglines) - expected = hslicepts.distance(hslicelines) - got.index = pslice - - cudf.testing.assert_series_equal(got, cudf.Series(expected))