From 7022c1de963555d00f7a90960763723e5b7d9aab Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 23 Mar 2023 18:12:19 -0700 Subject: [PATCH 01/54] initial --- .../experimental/ranges/multilinestring_range.cuh | 10 ++++++++++ .../experimental/ranges/multipolygon_range.cuh | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh b/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh index c99b5ca92..995262041 100644 --- a/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -129,6 +130,15 @@ class multilinestring_range { CUSPATIAL_HOST_DEVICE thrust::pair, vec_2d> segment( IndexType segment_idx); + /// Returns an infinite iterator to the "tiled" segments of the multilinestring. + /// If the multilinestring range has 5 segments, this iterator will iterate on the + /// 0th, 1st, 2nd, 3rd, 4th, 0th, 1st, 2nd, 3rd, 4th segment for the first 10 iterations. + /// + /// The name of `tile` comes from numpy.tile[1] + /// [1] https://numpy.org/doc/stable/reference/generated/numpy.tile.html + template + CUSPATIAL_HOST_DEVICE segment segment_tiled_begin(IndexType period); + /// Returns the `multilinestring_idx`th multilinestring in the range. template CUSPATIAL_HOST_DEVICE auto operator[](IndexType multilinestring_idx); diff --git a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh index a977e71dd..f4e2930df 100644 --- a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh @@ -141,6 +141,16 @@ class multipolygon_range { CUSPATIAL_HOST_DEVICE bool is_first_point_of_multipolygon(IndexType1 point_idx, IndexType2 geometry_idx); + + /// Returns an infinite iterator to the "repeated" polygons of the multipolygon range. + /// If the multipolygon range has 2 polygons, an iterator with repeats 3 will iterate on the + /// 0th, 0th, 0th, 1st, 1st, 1st, 0th, 0th, 0th, 1st, 1st, 1st polygon for the first 12 iterations. + /// + /// The name of `repeated` comes from [numpy.repeat](1) + /// [1] https://numpy.org/doc/stable/reference/generated/numpy.repeat.html + template + CUSPATIAL_HOST_DEVICE auto polygon_wraparound_repeated_begin(IndexType repeats); + protected: GeometryIterator _geometry_begin; GeometryIterator _geometry_end; From 5686be33b38667f27dd75da6caa5e8eb54b016dc Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 28 Mar 2023 11:19:35 -0700 Subject: [PATCH 02/54] add segment iterator and tests --- .../detail/ranges/multilinestring_range.cuh | 40 +++++++++++++++++++ .../ranges/multilinestring_range.cuh | 30 +++++++++----- .../ranges/multipolygon_range.cuh | 16 ++++---- .../cuspatial_test/vector_factories.cuh | 10 ++--- cpp/tests/CMakeLists.txt | 4 ++ 5 files changed, 78 insertions(+), 22 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh index 5add3a769..f793ae3d8 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -104,6 +105,13 @@ multilinestring_range::num_points() return thrust::distance(_point_begin, _point_end); } +template +CUSPATIAL_HOST_DEVICE auto +multilinestring_range::num_segments() +{ + return num_points() - num_linestrings(); +} + template CUSPATIAL_HOST_DEVICE auto multilinestring_range::multilinestring_begin() @@ -196,6 +204,38 @@ multilinestring_range::segment(Inde return thrust::make_pair(_point_begin[segment_idx], _point_begin[segment_idx + 1]); } +template +CUSPATIAL_HOST_DEVICE auto +multilinestring_range::segment_begin() +{ + return detail::make_counting_transform_iterator( + 0, + detail::to_valid_segment_functor{ + this->subtracted_part_begin(), this->subtracted_part_end(), _point_begin}); +} + +template +CUSPATIAL_HOST_DEVICE auto +multilinestring_range::segment_end() +{ + return segment_begin() + num_segments(); +} + +template +CUSPATIAL_HOST_DEVICE auto +multilinestring_range::subtracted_part_begin() +{ + return detail::make_counting_transform_iterator( + 0, detail::to_subtracted_by_index_iterator{_part_begin}); +} + +template +CUSPATIAL_HOST_DEVICE auto +multilinestring_range::subtracted_part_end() +{ + return subtracted_part_begin() + thrust::distance(_part_begin, _part_end); +} + template template CUSPATIAL_HOST_DEVICE auto diff --git a/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh b/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh index 995262041..f7a384961 100644 --- a/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh @@ -18,10 +18,10 @@ #include #include +#include #include #include #include -#include #include @@ -78,6 +78,9 @@ class multilinestring_range { /// Return the total number of points in the array. CUSPATIAL_HOST_DEVICE auto num_points(); + /// Return the total number of segments in the array. + CUSPATIAL_HOST_DEVICE auto num_segments(); + /// Return the iterator to the first multilinestring in the range. CUSPATIAL_HOST_DEVICE auto multilinestring_begin(); @@ -130,14 +133,19 @@ class multilinestring_range { CUSPATIAL_HOST_DEVICE thrust::pair, vec_2d> segment( IndexType segment_idx); - /// Returns an infinite iterator to the "tiled" segments of the multilinestring. - /// If the multilinestring range has 5 segments, this iterator will iterate on the - /// 0th, 1st, 2nd, 3rd, 4th, 0th, 1st, 2nd, 3rd, 4th segment for the first 10 iterations. - /// - /// The name of `tile` comes from numpy.tile[1] - /// [1] https://numpy.org/doc/stable/reference/generated/numpy.tile.html - template - CUSPATIAL_HOST_DEVICE segment segment_tiled_begin(IndexType period); + /// Returns an iterator to the start of the segment + CUSPATIAL_HOST_DEVICE auto segment_begin(); + + /// Returns an iterator to the end of the segment + CUSPATIAL_HOST_DEVICE auto segment_end(); + + // /// Returns an infinite iterator to the "tiled" segments of the multilinestring. + // /// If the multilinestring range has 5 segments, this iterator will iterate on the + // /// 0th, 1st, 2nd, 3rd, 4th, 0th, 1st, 2nd, 3rd, 4th segment for the first 10 iterations. + // /// + // /// The name of `tile` comes from numpy.tile[1] + // /// [1] https://numpy.org/doc/stable/reference/generated/numpy.tile.html + // CUSPATIAL_HOST_DEVICE auto segment_tiled_begin(); /// Returns the `multilinestring_idx`th multilinestring in the range. template @@ -151,6 +159,10 @@ class multilinestring_range { VecIterator _point_begin; VecIterator _point_end; + // TODO: find a better name + CUSPATIAL_HOST_DEVICE auto subtracted_part_begin(); + CUSPATIAL_HOST_DEVICE auto subtracted_part_end(); + private: /// @internal /// Return the iterator to the part index where the point locates. diff --git a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh index f4e2930df..d4de3c13f 100644 --- a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh @@ -142,14 +142,14 @@ class multipolygon_range { IndexType2 geometry_idx); - /// Returns an infinite iterator to the "repeated" polygons of the multipolygon range. - /// If the multipolygon range has 2 polygons, an iterator with repeats 3 will iterate on the - /// 0th, 0th, 0th, 1st, 1st, 1st, 0th, 0th, 0th, 1st, 1st, 1st polygon for the first 12 iterations. - /// - /// The name of `repeated` comes from [numpy.repeat](1) - /// [1] https://numpy.org/doc/stable/reference/generated/numpy.repeat.html - template - CUSPATIAL_HOST_DEVICE auto polygon_wraparound_repeated_begin(IndexType repeats); + // /// Returns an infinite iterator to the "repeated" polygons of the multipolygon range. + // /// If the multipolygon range has 2 polygons, an iterator with repeats 3 will iterate on the + // /// 0th, 0th, 0th, 1st, 1st, 1st, 0th, 0th, 0th, 1st, 1st, 1st polygon for the first 12 iterations. + // /// + // /// The name of `repeated` comes from [numpy.repeat](1) + // /// [1] https://numpy.org/doc/stable/reference/generated/numpy.repeat.html + // template + // CUSPATIAL_HOST_DEVICE auto polygon_wraparound_repeated_begin(IndexType repeats); protected: GeometryIterator _geometry_begin; diff --git a/cpp/include/cuspatial_test/vector_factories.cuh b/cpp/include/cuspatial_test/vector_factories.cuh index 2ca1270fb..1644b915a 100644 --- a/cpp/include/cuspatial_test/vector_factories.cuh +++ b/cpp/include/cuspatial_test/vector_factories.cuh @@ -213,10 +213,10 @@ class multilinestring_array { public: multilinestring_array(GeometryArray geometry_offsets_array, PartArray part_offsets_array, - CoordinateArray coordinate_offset_array) + CoordinateArray coordinate_array) : _geometry_offset_array(geometry_offsets_array), _part_offset_array(part_offsets_array), - _coordinate_offset_array(coordinate_offset_array) + _coordinate_array(coordinate_array) { } @@ -230,14 +230,14 @@ class multilinestring_array { _geometry_offset_array.end(), _part_offset_array.begin(), _part_offset_array.end(), - _coordinate_offset_array.begin(), - _coordinate_offset_array.end()); + _coordinate_array.begin(), + _coordinate_array.end()); } protected: GeometryArray _geometry_offset_array; PartArray _part_offset_array; - CoordinateArray _coordinate_offset_array; + CoordinateArray _coordinate_array; }; /** diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 2d48d8f39..f81ea76ba 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -185,3 +185,7 @@ ConfigureTest(FIND_TEST_EXP experimental/find/find_and_combine_segments_test.cu experimental/find/find_points_on_segments_test.cu experimental/find/find_duplicate_points_test.cu) + +ConfigureTest(RANGE_TEST_EXP + experimental/range/multilinestring_range_test.cu +) From a74af14e66179e523030d9a762f1175d0746aa25 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 28 Mar 2023 13:45:51 -0700 Subject: [PATCH 03/54] WIP --- .../distance/linestring_polygon_distance.hpp | 0 .../distance_utils/point_in_multipolygon.cuh | 0 .../experimental/detail/functors.cuh | 80 +++++++ .../detail/linestring_polygon_distance.cuh | 116 ++++++++++ .../detail/ranges/multilinestring_range.cuh | 23 ++ .../detail/ranges/multipolygon_range.cuh | 46 ++++ .../linestring_polygon_distance.cuh | 48 ++++ .../ranges/multilinestring_range.cuh | 31 ++- .../ranges/multipolygon_range.cuh | 17 +- .../range/multilinestring_range_test.cu | 218 ++++++++++++++++++ .../range/multipolygon_range_test.cu | 78 +++++++ 11 files changed, 641 insertions(+), 16 deletions(-) create mode 100644 cpp/include/cuspatial/distance/linestring_polygon_distance.hpp create mode 100644 cpp/include/cuspatial/experimental/detail/distance_utils/point_in_multipolygon.cuh create mode 100644 cpp/include/cuspatial/experimental/detail/functors.cuh create mode 100644 cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh create mode 100644 cpp/include/cuspatial/experimental/linestring_polygon_distance.cuh create mode 100644 cpp/tests/experimental/range/multilinestring_range_test.cu create mode 100644 cpp/tests/experimental/range/multipolygon_range_test.cu diff --git a/cpp/include/cuspatial/distance/linestring_polygon_distance.hpp b/cpp/include/cuspatial/distance/linestring_polygon_distance.hpp new file mode 100644 index 000000000..e69de29bb diff --git a/cpp/include/cuspatial/experimental/detail/distance_utils/point_in_multipolygon.cuh b/cpp/include/cuspatial/experimental/detail/distance_utils/point_in_multipolygon.cuh new file mode 100644 index 000000000..e69de29bb diff --git a/cpp/include/cuspatial/experimental/detail/functors.cuh b/cpp/include/cuspatial/experimental/detail/functors.cuh new file mode 100644 index 000000000..674ae1b22 --- /dev/null +++ b/cpp/include/cuspatial/experimental/detail/functors.cuh @@ -0,0 +1,80 @@ + +#pragma once + +#include +#include + +#include + +namespace cuspatial { +namespace detail { + +template +struct to_subtracted_by_index_iterator { + OffsetIterator begin; + + template + CUSPATIAL_HOST_DEVICE auto operator()(IndexType i) + { + // printf("begin[i]: %d i: %d\n", static_cast(begin[i]), static_cast(i)); + return begin[i] - i; + } +}; + +template +to_subtracted_by_index_iterator(OffsetIterator) -> to_subtracted_by_index_iterator; + +template +struct to_valid_segment_functor { + using element_t = iterator_vec_base_type; + + OffsetIterator begin; + OffsetIterator end; + CoordinateIterator point_begin; + + template + CUSPATIAL_HOST_DEVICE segment operator()(IndexType i) + { + auto kit = thrust::upper_bound(thrust::seq, begin, end, i); + auto k = thrust::distance(begin, kit); + auto pid = i + k - 1; + + // printf("%d %d %d\n", static_cast(i), static_cast(k), static_cast(pid)); + return segment{point_begin[pid], point_begin[pid + 1]}; + } +}; + +template +to_valid_segment_functor(OffsetIterator, OffsetIterator, CoordinateIterator) + -> to_valid_segment_functor; + +template +struct wraparound_functor { + IndexType length; + + template + CUSPATIAL_HOST_DEVICE auto operator()(IndexType2 i) + { + return i % length; + } +}; + +template +wraparound_functor(IndexType) -> wraparound_functor; + +template +struct repeat_functor { + IndexType repeats; + + template + CUSPATIAL_HOST_DEVICE auto operator()(IndexType2 i) + { + return i / repeats; + } +}; + +template +wraparound_functor(IndexType) -> wraparound_functor; + +} // namespace detail +} // namespace cuspatial diff --git a/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh b/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh new file mode 100644 index 000000000..222e5b38f --- /dev/null +++ b/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh @@ -0,0 +1,116 @@ +/* + * 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 + +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 { + using T = typename MultiPointRange::element_t; + + MultiPointRange multipoints; + MultiPolygonRange multipolygons; + + point_in_multipolygon_test_functor(MultiPointRange multipoints, MultiPolygonRange multipolygons) + : multipoints(multipoints), multipolygons(multipolygons) + { + } + + template + uint8_t __device__ operator()(IndexType pidx) + { + vec_2d const& point = multipoints.point(pidx); + 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); + }); + + return static_cast(intersects); + } +}; + +} // namespace detail + +template +OutputIt pairwise_linestring_polygon_distance(MultiLinestringRange multilinestrings, + MultiPolygonRange multipolygons, + OutputIt distances_first, + rmm::cuda_stream_view stream) +{ + using T = typename MultiLinestringRange::element_t; + using index_t = typename MultiLinestringRange::index_t; + + CUSPATIAL_EXPECTS(multilinestrings.size() == multipolygons.size(), + "Must have the same number of input rows."); + + if (multilinestrings.size() == 0) return distances_first; + + // Create a multipoint range from multilinestrings + auto multipoints = multilinestrings.as_multipoint_range(); + + // 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); + + thrust::tabulate(rmm::exec_policy(stream), + point_intersects.begin(), + point_intersects.end(), + detail::point_in_multipolygon_test_functor{multipoints, multipolygons}); + + // `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); + + 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, + offset_as_key_it + multipoints.num_points(), + point_intersects.begin(), + thrust::make_discard_iterator(), + multipoint_intersects.begin(), + thrust::logical_or()); + + return multipoint_intersects; + }(); +} + +} // 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 f793ae3d8..1e6ebbe11 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh @@ -25,9 +25,12 @@ #include #include #include +#include #include #include +#include + #include #include @@ -236,6 +239,26 @@ multilinestring_range::subtracted_p return subtracted_part_begin() + thrust::distance(_part_begin, _part_end); } +template +CUSPATIAL_HOST_DEVICE auto +multilinestring_range::segment_tiled_begin() +{ + auto tiled_it = + detail::make_counting_transform_iterator(0, detail::wraparound_functor{num_segments()}); + return thrust::make_permutation_iterator(segment_begin(), tiled_it); +} + +template +CUSPATIAL_HOST_DEVICE auto +multilinestring_range::as_multipoint_range() +{ + auto multipoint_geometry_it = thrust::make_permutation_iterator(_part_begin, _geometry_begin); + return multipoint_range{multipoint_geometry_it, + multipoint_geometry_it + num_multilinestrings(), + _point_begin, + _point_end}; +} + template template CUSPATIAL_HOST_DEVICE auto diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh index 49b00a87b..5c9493d8f 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh @@ -193,6 +193,52 @@ multipolygon_range::p return _point_end; } +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range::polygon_begin() +{ + return detail::make_counting_transform_iterator( + 0, to_polygon_functor{_part_begin, _ring_begin, point_begin, point_end}); +} + +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range::polygon_end() +{ + return polygon_begin() + num_polygons(); +} + +template +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range:: + polygon_repeated_begin(IndexType repeats) +{ + auto repeat_it = detail::make_counting_transform_iterator(0, repeat_functor{repeats}); + return thrust::make_permutation_iterator(polygon_begin(), repeat_it); +} + +template +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range::polygon_repeated_end( + IndexType repeats) +{ + return polygon_repeated_begin(repeats) + repeats * num_polygons(); +} + template + +namespace cuspatial { + +/** + * @ingroup distance + * @brief Computes pairwise multilinestring to multipolygon distance + * + * @tparam MultiLinestringRange 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 multilinestrings Range of multilinestrings, one per computed distance pair. + * @param multipolygons Range of multipolygons, 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 + * "LegacyRandomAccessIterator" + */ +template +OutputIt pairwise_linestring_polygon_distance( + MultiLinestringRange multilinestrings, + MultiPolygonRange multipoiygons, + OutputIt distances_first, + rmm::cuda_stream_view stream = rmm::cuda_stream_default); +} // namespace cuspatial + +#include diff --git a/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh b/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh index f7a384961..c491da973 100644 --- a/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh @@ -139,18 +139,35 @@ class multilinestring_range { /// Returns an iterator to the end of the segment CUSPATIAL_HOST_DEVICE auto segment_end(); - // /// Returns an infinite iterator to the "tiled" segments of the multilinestring. - // /// If the multilinestring range has 5 segments, this iterator will iterate on the - // /// 0th, 1st, 2nd, 3rd, 4th, 0th, 1st, 2nd, 3rd, 4th segment for the first 10 iterations. - // /// - // /// The name of `tile` comes from numpy.tile[1] - // /// [1] https://numpy.org/doc/stable/reference/generated/numpy.tile.html - // CUSPATIAL_HOST_DEVICE auto segment_tiled_begin(); + /// Infinite iterators + /// Note: Infinite iterators currently doesn't work with ranges that contains empty geometry. + + /// Returns an infinite iterator to the "tiled" segments of the multilinestring. + /// If the multilinestring range has 5 segments, this iterator will iterate on the + /// 0th, 1st, 2nd, 3rd, 4th, 0th, 1st, 2nd, 3rd, 4th segment for the first 10 iterations. + /// + /// The name of `tile` comes from numpy.tile[1] + /// [1] https://numpy.org/doc/stable/reference/generated/numpy.tile.html + CUSPATIAL_HOST_DEVICE auto segment_tiled_begin(); /// Returns the `multilinestring_idx`th multilinestring in the range. template CUSPATIAL_HOST_DEVICE auto operator[](IndexType multilinestring_idx); + /// Raw offsets iterator + + CUSPATIAL_HOST_DEVICE auto geometry_offsets_begin() { return _geometry_begin; } + CUSPATIAL_HOST_DEVICE auto geometry_offsets_end() { return _geometry_end; } + CUSPATIAL_HOST_DEVICE auto part_offsets_begin() { return _part_begin; } + CUSPATIAL_HOST_DEVICE auto part_offsets_end() { return _part_end; } + + /// Range Casts + + /// Casts the multilinestring range into a multipoint range. + /// This treats each multilinestring as simply a collection of points, + /// ignoring all edges of the multilinestring. + CUSPATIAL_HOST_DEVICE auto as_multipoint_range(); + protected: GeometryIterator _geometry_begin; GeometryIterator _geometry_end; diff --git a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh index d4de3c13f..b72685fa5 100644 --- a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh @@ -141,15 +141,14 @@ class multipolygon_range { CUSPATIAL_HOST_DEVICE bool is_first_point_of_multipolygon(IndexType1 point_idx, IndexType2 geometry_idx); - - // /// Returns an infinite iterator to the "repeated" polygons of the multipolygon range. - // /// If the multipolygon range has 2 polygons, an iterator with repeats 3 will iterate on the - // /// 0th, 0th, 0th, 1st, 1st, 1st, 0th, 0th, 0th, 1st, 1st, 1st polygon for the first 12 iterations. - // /// - // /// The name of `repeated` comes from [numpy.repeat](1) - // /// [1] https://numpy.org/doc/stable/reference/generated/numpy.repeat.html - // template - // CUSPATIAL_HOST_DEVICE auto polygon_wraparound_repeated_begin(IndexType repeats); + /// Returns an infinite iterator to the "repeated" polygons of the multipolygon range. + /// If the multipolygon range has 2 polygons, an iterator with repeats 3 will iterate on the + /// 0th, 0th, 0th, 1st, 1st, 1st, 0th, 0th, 0th polygon for the first 9 iterations. + /// + /// The name of `repeated` comes from [numpy.repeat](1) + /// [1] https://numpy.org/doc/stable/reference/generated/numpy.repeat.html + template + CUSPATIAL_HOST_DEVICE auto polygon_wraparound_repeated_begin(IndexType repeats); protected: GeometryIterator _geometry_begin; diff --git a/cpp/tests/experimental/range/multilinestring_range_test.cu b/cpp/tests/experimental/range/multilinestring_range_test.cu new file mode 100644 index 000000000..396d23f88 --- /dev/null +++ b/cpp/tests/experimental/range/multilinestring_range_test.cu @@ -0,0 +1,218 @@ +/* + * 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 + +#include + +using namespace cuspatial; +using namespace cuspatial::test; + +template +struct MultilinestringRangeTest : public BaseFixture { + void run_segment_test_single(std::initializer_list geometry_offset, + std::initializer_list part_offset, + std::initializer_list> coordinates, + std::initializer_list> expected) + { + auto multilinestring_array = + make_multilinestring_array(geometry_offset, part_offset, coordinates); + + auto rng = multilinestring_array.range(); + + rmm::device_uvector> got(rng.num_segments(), stream()); + thrust::copy(rmm::exec_policy(stream()), rng.segment_begin(), rng.segment_end(), got.begin()); + + auto d_expected = thrust::device_vector>(expected.begin(), expected.end()); + CUSPATIAL_EXPECT_VEC2D_PAIRS_EQUIVALENT(d_expected, got); + } + + void run_tiled_segments_test_single(std::initializer_list geometry_offset, + std::initializer_list part_offset, + std::initializer_list> coordinates, + std::size_t length, + std::initializer_list> expected) + { + auto multilinestring_array = + make_multilinestring_array(geometry_offset, part_offset, coordinates); + + auto rng = multilinestring_array.range(); + + rmm::device_uvector> got(length, stream()); + auto it = rng.segment_tiled_begin(); + thrust::copy(rmm::exec_policy(stream()), it, it + length, got.begin()); + + auto d_expected = thrust::device_vector>(expected.begin(), expected.end()); + CUSPATIAL_EXPECT_VEC2D_PAIRS_EQUIVALENT(d_expected, got); + } +}; + +using TestTypes = ::testing::Types; + +TYPED_TEST_CASE(MultilinestringRangeTest, TestTypes); + +TYPED_TEST(MultilinestringRangeTest, SegmentIteratorOneSegmentTest) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST( + this->run_segment_test_single, {0, 1}, {0, 2}, {P{0, 0}, P{1, 1}}, {S{P{0, 0}, P{1, 1}}}); +} + +TYPED_TEST(MultilinestringRangeTest, SegmentIteratorTwoSegmentTest) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_segment_test_single, + {0, 2}, + {0, 2, 4}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{3, 3}}, + {S{P{0, 0}, P{1, 1}}, S{P{2, 2}, P{3, 3}}}); +} + +TYPED_TEST(MultilinestringRangeTest, SegmentIteratorTwoSegmentTest2) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_segment_test_single, + {0, 1, 2}, + {0, 2, 4}, + {P{0, 0}, P{1, 1}, P{0, 0}, P{1, 1}}, + {S{P{0, 0}, P{1, 1}}, S{P{0, 0}, P{1, 1}}}); +} + +TYPED_TEST(MultilinestringRangeTest, SegmentIteratorManyPairTest) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_segment_test_single, + {0, 1, 2, 3}, + {0, 6, 11, 14}, + {P{0, 0}, + P{1, 1}, + P{2, 2}, + P{3, 3}, + P{4, 4}, + P{5, 5}, + P{10, 10}, + P{11, 11}, + P{12, 12}, + P{13, 13}, + P{14, 14}, + P{20, 20}, + P{21, 21}, + P{22, 22}}, + {S{P{0, 0}, P{1, 1}}, + S{P{1, 1}, P{2, 2}}, + S{P{2, 2}, P{3, 3}}, + S{P{3, 3}, P{4, 4}}, + S{P{4, 4}, P{5, 5}}, + S{P{10, 10}, P{11, 11}}, + S{P{11, 11}, P{12, 12}}, + S{P{12, 12}, P{13, 13}}, + S{P{13, 13}, P{14, 14}}, + S{P{20, 20}, P{21, 21}}, + S{P{21, 21}, P{22, 22}}}); +} + +TYPED_TEST(MultilinestringRangeTest, TiledSegmentIteratorTestOneLine) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_tiled_segments_test_single, + {0, 1}, + {0, 2}, + {P{0, 0}, P{1, 1}}, + 3, + {S{P{0, 0}, P{1, 1}}, S{P{0, 0}, P{1, 1}}, S{P{0, 0}, P{1, 1}}}); +} + +TYPED_TEST(MultilinestringRangeTest, TiledSegmentIteratorTestTwoLines) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_tiled_segments_test_single, + {0, 1, 2}, + {0, 2, 4}, + {P{0, 0}, P{1, 1}, P{10, 10}, P{11, 11}}, + 5, + { + S{P{0, 0}, P{1, 1}}, + S{P{10, 10}, P{11, 11}}, + S{P{0, 0}, P{1, 1}}, + S{P{10, 10}, P{11, 11}}, + S{P{0, 0}, P{1, 1}}, + }); +} + +TYPED_TEST(MultilinestringRangeTest, TiledSegmentIteratorTestThreeLines) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST( + this->run_tiled_segments_test_single, + {0, 1, 2, 3}, + {0, 2, 4, 8}, + {P{0, 0}, P{1, 1}, P{10, 10}, P{11, 11}, P{20, 20}, P{21, 21}, P{22, 22}, P{23, 23}}, + 10, + {S{P{0, 0}, P{1, 1}}, + S{P{10, 10}, P{11, 11}}, + S{P{20, 20}, P{21, 21}}, + S{P{21, 21}, P{22, 22}}, + S{P{22, 22}, P{23, 23}}, + S{P{0, 0}, P{1, 1}}, + S{P{10, 10}, P{11, 11}}, + S{P{20, 20}, P{21, 21}}, + S{P{21, 21}, P{22, 22}}, + S{P{22, 22}, P{23, 23}}}); +} + +/// FIXME: Currently, segment iterator doesn't handle empty linestrings. +TYPED_TEST(MultilinestringRangeTest, DISABLED_SegmentIteratorWithEmptyLineTest) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST( + this->run_segment_test_single, + {0, 1, 2, 3}, + {0, 3, 3, 6}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}, P{12, 12}}, + {S{P{0, 0}, P{1, 1}}, S{P{1, 1}, P{2, 2}}, S{P{10, 10}, P{11, 11}}, S{P{11, 11}, P{12, 12}}}); +} diff --git a/cpp/tests/experimental/range/multipolygon_range_test.cu b/cpp/tests/experimental/range/multipolygon_range_test.cu new file mode 100644 index 000000000..39b18498a --- /dev/null +++ b/cpp/tests/experimental/range/multipolygon_range_test.cu @@ -0,0 +1,78 @@ +/* + * 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 + +#include + +using namespace cuspatial; +using namespace cuspatial::test; + +struct polygon_ref_exact_equal_comparator { + template + bool operator()(PolygonRef first, PolygonRef second) + { + if (first.num_rings() != second.num_rings()) return false; + + bool equals = true; + for (std::size_t i = 0; equals && i < first.num_rings(); ++i) { + auto ring_first = first[i]; + auto ring_second = second[i]; + if (ring_first.size() != ring_second.size()) equals = false; + for (std::size_t j = 0; j < ring_first.size(); ++j) + equals = equals && ring_first[j] == ring_second[j]; + } + return equals; + } +}; + +template +struct MultipolygonRangeTest : public BaseFixture { + void run_polygon_iterator_test_single(std::initializer_list geometry_offset, + std::initializer_list part_offset, + std::initializer_list ring_offset, + std::initializer_list> coordinates, + std::initializer_list expected_geometry_offset, + std::initializer_list expected_part_offset, + std::initializer_list expected_ring_offset, + std::initializer_list> expected_coordinates) + { + auto multipolygon_array = + make_multipolygon_array(geometry_offset, part_offset, ring_offset, coordinates); + + auto expected_multipolygon_array = make_multipolygon_array( + expected_geometry_offset, expected_part_offset, expected_ring_offset, expected_coordinates); + + auto rng = multipolygon_array.range(); + auto expected_rng = expected_multipolygon_array.range(); + + auto equal = rmm::device_uvector(expected_multipolygon_array.size(), stream()); + + thrust::transform(multipolygon_array.polygon_begin(), + multipolygon_array.polygon_end(), + expected_multipolygon_array.begin(), + equal.begin(), + polygon_ref_exact_equal_comparator{}); + } +}; From 1c28faa50e185264a3581ed628ad9c73b61cdc6e Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 29 Mar 2023 15:23:04 -0700 Subject: [PATCH 04/54] making progress towards getting the right index --- .../experimental/detail/functors.cuh | 32 ++-- .../detail/linestring_polygon_distance.cuh | 88 ++++++++++- .../detail/ranges/multilinestring_range.cuh | 17 +++ .../detail/ranges/multipolygon_range.cuh | 94 ++++++------ .../ranges/multilinestring_range.cuh | 6 + .../ranges/multipolygon_range.cuh | 30 +++- cpp/include/cuspatial_test/base_fixture.hpp | 12 +- cpp/include/cuspatial_test/test_util.cuh | 14 ++ cpp/tests/CMakeLists.txt | 5 +- .../range/multilinestring_range_test.cu | 50 +++++++ .../range/multipolygon_range_test.cu | 137 +++++++++++++----- .../linestring_polygon_distance_test.cu | 112 ++++++++++++++ 12 files changed, 493 insertions(+), 104 deletions(-) create mode 100644 cpp/tests/experimental/spatial/linestring_polygon_distance_test.cu diff --git a/cpp/include/cuspatial/experimental/detail/functors.cuh b/cpp/include/cuspatial/experimental/detail/functors.cuh index 674ae1b22..f2e0b1231 100644 --- a/cpp/include/cuspatial/experimental/detail/functors.cuh +++ b/cpp/include/cuspatial/experimental/detail/functors.cuh @@ -9,6 +9,16 @@ namespace cuspatial { namespace detail { +struct offset_pair_to_count_iterator { + template + CUSPATIAL_HOST_DEVICE auto operator()(OffsetPairIterator p) + { + auto first = thrust::get<0>(p); + auto second = thrust::get<1>(p); + return second - first; + } +}; + template struct to_subtracted_by_index_iterator { OffsetIterator begin; @@ -62,19 +72,19 @@ struct wraparound_functor { template wraparound_functor(IndexType) -> wraparound_functor; -template -struct repeat_functor { - IndexType repeats; +// template +// struct repeat_functor { +// IndexType repeats; - template - CUSPATIAL_HOST_DEVICE auto operator()(IndexType2 i) - { - return i / repeats; - } -}; +// template +// CUSPATIAL_HOST_DEVICE auto operator()(IndexType2 i) +// { +// return i / repeats; +// } +// }; -template -wraparound_functor(IndexType) -> wraparound_functor; +// template +// wraparound_functor(IndexType) -> wraparound_functor; } // namespace detail } // namespace cuspatial diff --git a/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh b/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh index 222e5b38f..bc642e7b8 100644 --- a/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh @@ -16,12 +16,22 @@ #pragma once +#include + #include +#include +#include +#include +#include #include +#include #include #include #include +#include +#include +#include #include #include @@ -62,6 +72,39 @@ struct point_in_multipolygon_test_functor { } }; +template +void __global__ pairwise_linestring_polygon_distance_kernel(MultiLinestringRange multilinestrings, + MultiPolygonRange multipolygons, + IndexRange thread_bounds, + uint8_t* intersects, + OutputIt* distances) +{ + using T = typename MultiLinestringRange::element_t; + using index_t = iterator_value_type; + + auto num_threads = thread_bounds[thread_bounds.size() - 1]; + for (auto idx = blockDim.x * blockIdx.x + threadIdx.x; idx < num_threads; + idx += blockDim.x * gridDim.x) { + auto it = thrust::prev( + thrust::upper_bound(thrust::seq, thread_bounds.begin(), thread_bounds.end(), idx)); + auto geometry_id = thrust::distance(thread_bounds.begin(), it); + auto local_idx = idx - *it; + + if (intersects[geometry_id]) { + distances[geometry_id] = 0.0f; + continue; + } + + printf("idx: %d, geometry_id: %d, local_idx: %d\n", + static_cast(idx), + static_cast(geometry_id), + static_cast(local_idx)); + } +} + } // namespace detail template @@ -71,7 +114,7 @@ OutputIt pairwise_linestring_polygon_distance(MultiLinestringRange multilinestri rmm::cuda_stream_view stream) { using T = typename MultiLinestringRange::element_t; - using index_t = typename MultiLinestringRange::index_t; + using index_t = iterator_value_type; CUSPATIAL_EXPECTS(multilinestrings.size() == multipolygons.size(), "Must have the same number of input rows."); @@ -111,6 +154,49 @@ OutputIt pairwise_linestring_polygon_distance(MultiLinestringRange multilinestri return multipoint_intersects; }(); + + auto thread_counts = + rmm::device_uvector(multilinestrings.num_multilinestrings(), stream); + + auto per_multilinestring_point_count_it = + multilinestrings.per_multilinestring_point_count_begin(); + auto per_multipolygon_point_count_it = multipolygons.per_multipolygon_point_count_begin(); + + thrust::transform( + rmm::exec_policy(stream), + per_multilinestring_point_count_it, + per_multilinestring_point_count_it + multilinestrings.num_multilinestrings(), + per_multipolygon_point_count_it, + thread_counts.begin(), + [] __device__(index_t multilinestring_point_count, index_t multipolygon_point_count) { + auto multilinestring_segment_count = + multilinestring_point_count == 0 ? 0 : multilinestring_point_count - 1; + auto multipolygon_segment_count = + multipolygon_point_count == 0 ? 0 : multipolygon_point_count - 1; + return multilinestring_segment_count * multipolygon_segment_count; + }); + + auto thread_bounds = rmm::device_uvector(thread_counts.size() + 1, stream); + detail::zero_data_async(thread_bounds.begin(), thread_bounds.end(), stream); + + thrust::inclusive_scan(rmm::exec_policy(stream), + thread_counts.begin(), + thread_counts.end(), + thrust::next(thread_bounds.begin())); + + cuspatial::test::print_device_vector(thread_bounds, "thread_bounds: "); + + auto num_threads = thread_bounds.back_element(stream); + auto [tpb, num_blocks] = grid_1d(num_threads); + + detail::pairwise_linestring_polygon_distance_kernel<<>>( + multilinestrings, + multipolygons, + range{thread_bounds.begin(), thread_bounds.end()}, + multipoint_intersects.begin(), + distances_first); + + return distances_first + multilinestrings.num_multilinestrings(); } } // 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 1e6ebbe11..923ca4b35 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh @@ -207,6 +207,23 @@ multilinestring_range::segment(Inde return thrust::make_pair(_point_begin[segment_idx], _point_begin[segment_idx + 1]); } +template +CUSPATIAL_HOST_DEVICE auto multilinestring_range:: + per_multilinestring_point_count_begin() +{ + auto multilinestring_offset_it = thrust::make_permutation_iterator(_part_begin, _geometry_begin); + auto paired_it = + thrust::make_zip_iterator(multilinestring_offset_it, thrust::next(multilinestring_offset_it)); + return thrust::make_transform_iterator(paired_it, detail::offset_pair_to_count_iterator{}); +} + +template +CUSPATIAL_HOST_DEVICE auto multilinestring_range:: + per_multilinestring_point_count_end() +{ + return per_multilinestring_point_count_begin() + num_multilinestrings(); +} + template CUSPATIAL_HOST_DEVICE auto multilinestring_range::segment_begin() diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh index 5c9493d8f..2fbb26b5a 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,7 @@ #include #include +#include #include #include @@ -193,26 +195,26 @@ multipolygon_range::p return _point_end; } -template -CUSPATIAL_HOST_DEVICE auto -multipolygon_range::polygon_begin() -{ - return detail::make_counting_transform_iterator( - 0, to_polygon_functor{_part_begin, _ring_begin, point_begin, point_end}); -} - -template -CUSPATIAL_HOST_DEVICE auto -multipolygon_range::polygon_end() -{ - return polygon_begin() + num_polygons(); -} +// template +// CUSPATIAL_HOST_DEVICE auto +// multipolygon_range::polygon_begin() +// { +// return detail::make_counting_transform_iterator( +// 0, to_polygon_functor{_part_begin, _ring_begin, point_begin, point_end}); +// } + +// template +// CUSPATIAL_HOST_DEVICE auto +// multipolygon_range::polygon_end() +// { +// return polygon_begin() + num_polygons(); +// } template CUSPATIAL_HOST_DEVICE auto multipolygon_range:: - polygon_repeated_begin(IndexType repeats) + ring_idx_from_point_idx(IndexType point_idx) { - auto repeat_it = detail::make_counting_transform_iterator(0, repeat_functor{repeats}); - return thrust::make_permutation_iterator(polygon_begin(), repeat_it); + 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::polygon_repeated_end( - IndexType repeats) +multipolygon_range:: + part_idx_from_ring_idx(IndexType ring_idx) { - return polygon_repeated_begin(repeats) + repeats * num_polygons(); + return thrust::distance( + _part_begin, thrust::prev(thrust::upper_bound(thrust::seq, _part_begin, _part_end, ring_idx))); } template CUSPATIAL_HOST_DEVICE auto multipolygon_range:: - ring_idx_from_point_idx(IndexType point_idx) + geometry_idx_from_part_idx(IndexType part_idx) { return thrust::distance( - _ring_begin, thrust::prev(thrust::upper_bound(thrust::seq, _ring_begin, _ring_end, point_idx))); + _geometry_begin, + thrust::prev(thrust::upper_bound(thrust::seq, _geometry_begin, _geometry_end, part_idx))); } template CUSPATIAL_HOST_DEVICE auto multipolygon_range:: - part_idx_from_ring_idx(IndexType ring_idx) + geometry_idx_from_segment_idx(IndexType segment_idx) { - return thrust::distance( - _part_begin, thrust::prev(thrust::upper_bound(thrust::seq, _part_begin, _part_end, ring_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 auto multipolygon_range:: - geometry_idx_from_part_idx(IndexType part_idx) + per_multipolygon_point_count_begin() { - return thrust::distance( - _geometry_begin, - thrust::prev(thrust::upper_bound(thrust::seq, _geometry_begin, _geometry_end, part_idx))); + auto multipolygon_point_offset_it = thrust::make_permutation_iterator( + _ring_begin, thrust::make_permutation_iterator(_part_begin, _geometry_begin)); + + auto point_offset_pair_it = thrust::make_zip_iterator(multipolygon_point_offset_it, + thrust::next(multipolygon_point_offset_it)); + + return thrust::make_transform_iterator(point_offset_pair_it, + detail::offset_pair_to_count_iterator{}); } template -template CUSPATIAL_HOST_DEVICE auto multipolygon_range:: - geometry_idx_from_segment_idx(IndexType segment_idx) + per_multipolygon_point_count_end() { - 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)); + return per_multipolygon_point_count_begin() + num_multipolygons(); } template , vec_2d> segment( IndexType segment_idx); + /// Returns an iterator to the counts of points per multilinestring + CUSPATIAL_HOST_DEVICE auto per_multilinestring_point_count_begin(); + + /// Returns an iterator to the counts of points per multilinestring + CUSPATIAL_HOST_DEVICE auto per_multilinestring_point_count_end(); + /// Returns an iterator to the start of the segment CUSPATIAL_HOST_DEVICE auto segment_begin(); diff --git a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh index b72685fa5..ff8857181 100644 --- a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh @@ -107,6 +107,12 @@ class multipolygon_range { /// Return the iterator to the one past the last point in the range. CUSPATIAL_HOST_DEVICE auto point_end(); + // /// Return the iterator to the first polygon in the range. + // CUSPATIAL_HOST_DEVICE auto polygon_begin(); + + // /// Return the iterator to the one past the last polygon in the range. + // CUSPATIAL_HOST_DEVICE auto polygon_end(); + /// 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 @@ -141,14 +147,22 @@ class multipolygon_range { CUSPATIAL_HOST_DEVICE bool is_first_point_of_multipolygon(IndexType1 point_idx, IndexType2 geometry_idx); - /// Returns an infinite iterator to the "repeated" polygons of the multipolygon range. - /// If the multipolygon range has 2 polygons, an iterator with repeats 3 will iterate on the - /// 0th, 0th, 0th, 1st, 1st, 1st, 0th, 0th, 0th polygon for the first 9 iterations. - /// - /// The name of `repeated` comes from [numpy.repeat](1) - /// [1] https://numpy.org/doc/stable/reference/generated/numpy.repeat.html - template - CUSPATIAL_HOST_DEVICE auto polygon_wraparound_repeated_begin(IndexType repeats); + /// Returns an iterator to the number of points of the first multipolygon + CUSPATIAL_HOST_DEVICE auto per_multipolygon_point_count_begin(); + /// Returns the one past the iterator to the number of points of the last multipolygon + CUSPATIAL_HOST_DEVICE auto per_multipolygon_point_count_end(); + + // /// Returns an infinite iterator to the "repeated" polygons of the multipolygon range. + // /// If the multipolygon range has 2 polygons, an iterator with repeats 3 will iterate on the + // /// 0th, 0th, 0th, 1st, 1st, 1st, 0th, 0th, 0th polygon for the first 9 iterations. + // /// + // /// The name of `repeated` comes from [numpy.repeat](1) + // /// [1] https://numpy.org/doc/stable/reference/generated/numpy.repeat.html + // template + // CUSPATIAL_HOST_DEVICE auto polygon_wraparound_repeated_begin(IndexType repeats); + + // template + // CUSPATIAL_HOST_DEVICE auto segment_wraparound_repeated_begin() protected: GeometryIterator _geometry_begin; diff --git a/cpp/include/cuspatial_test/base_fixture.hpp b/cpp/include/cuspatial_test/base_fixture.hpp index 54f8be5f6..52635c34d 100644 --- a/cpp/include/cuspatial_test/base_fixture.hpp +++ b/cpp/include/cuspatial_test/base_fixture.hpp @@ -54,8 +54,7 @@ class RMMResourceMixin { * class MyTestFixture : public cuspatial::test::BaseFixture {}; * ``` */ -class BaseFixture : public RMMResourceMixin, public ::testing::Test { -}; +class BaseFixture : public RMMResourceMixin, public ::testing::Test {}; /** * @brief Base test fixture class from which libcuspatial test with only value parameterization @@ -79,8 +78,13 @@ class BaseFixture : public RMMResourceMixin, public ::testing::Test { */ template class BaseFixtureWithParam : public RMMResourceMixin, - public ::testing::TestWithParam> { -}; + public ::testing::TestWithParam> {}; + +/** + * @brief Floating point types to be used in libcuspatial tests + * + */ +using FloatingPointTypes = ::testing::Types; } // namespace test } // namespace cuspatial diff --git a/cpp/include/cuspatial_test/test_util.cuh b/cpp/include/cuspatial_test/test_util.cuh index 2310e74fb..e30089ba4 100644 --- a/cpp/include/cuspatial_test/test_util.cuh +++ b/cpp/include/cuspatial_test/test_util.cuh @@ -99,5 +99,19 @@ void print_device_range(Iter begin, std::cout << post; } +/** + * @brief + */ +template +void print_device_vector(Vector const& vec, std::string_view pre = "", std::string_view post = "\n") +{ + using T = typename Vector::value_type; + auto hvec = to_host(vec); + + std::cout << pre; + std::for_each(hvec.begin(), hvec.end(), [](auto const& x) { std::cout << x << " "; }); + std::cout << post; +} + } // namespace test } // namespace cuspatial diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index f81ea76ba..16f11d8ea 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -132,6 +132,9 @@ ConfigureTest(POINT_LINESTRING_DISTANCE_TEST_EXP ConfigureTest(POINT_POLYGON_DISTANCE_TEST_EXP experimental/spatial/point_polygon_distance_test.cu) +ConfigureTest(LINESTRING_POLYGON_DISTANCE_TEST_EXP + experimental/spatial/linestring_polygon_distance_test.cu) + ConfigureTest(HAUSDORFF_TEST_EXP experimental/spatial/hausdorff_test.cu) @@ -188,4 +191,4 @@ ConfigureTest(FIND_TEST_EXP ConfigureTest(RANGE_TEST_EXP experimental/range/multilinestring_range_test.cu -) + experimental/range/multipolygon_range_test.cu) diff --git a/cpp/tests/experimental/range/multilinestring_range_test.cu b/cpp/tests/experimental/range/multilinestring_range_test.cu index 396d23f88..d862d2701 100644 --- a/cpp/tests/experimental/range/multilinestring_range_test.cu +++ b/cpp/tests/experimental/range/multilinestring_range_test.cu @@ -66,6 +66,27 @@ struct MultilinestringRangeTest : public BaseFixture { auto d_expected = thrust::device_vector>(expected.begin(), expected.end()); CUSPATIAL_EXPECT_VEC2D_PAIRS_EQUIVALENT(d_expected, got); } + + void run_per_multilinestring_point_count_test(std::initializer_list geometry_offset, + std::initializer_list part_offset, + std::initializer_list> coordinates, + std::initializer_list expected) + { + auto multilinestring_array = + make_multilinestring_array(geometry_offset, part_offset, coordinates); + + auto d_expected = thrust::device_vector(expected.begin(), expected.end()); + + auto rng = multilinestring_array.range(); + + rmm::device_uvector got(rng.num_multilinestrings(), stream()); + thrust::copy(rmm::exec_policy(stream()), + rng.per_multilinestring_point_count_begin(), + rng.per_multilinestring_point_count_end(), + got.begin()); + + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(d_expected, got); + } }; using TestTypes = ::testing::Types; @@ -123,14 +144,17 @@ TYPED_TEST(MultilinestringRangeTest, SegmentIteratorManyPairTest) P{3, 3}, P{4, 4}, P{5, 5}, + P{10, 10}, P{11, 11}, P{12, 12}, P{13, 13}, P{14, 14}, + P{20, 20}, P{21, 21}, P{22, 22}}, + {S{P{0, 0}, P{1, 1}}, S{P{1, 1}, P{2, 2}}, S{P{2, 2}, P{3, 3}}, @@ -216,3 +240,29 @@ TYPED_TEST(MultilinestringRangeTest, DISABLED_SegmentIteratorWithEmptyLineTest) {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}, P{12, 12}}, {S{P{0, 0}, P{1, 1}}, S{P{1, 1}, P{2, 2}}, S{P{10, 10}, P{11, 11}}, S{P{11, 11}, P{12, 12}}}); } + +TYPED_TEST(MultilinestringRangeTest, PerMultilinestringCountTest) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_per_multilinestring_point_count_test, + {0, 1, 2, 3}, + {0, 3, 3, 6}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}, P{12, 12}}, + {3, 0, 3}); +} + +TYPED_TEST(MultilinestringRangeTest, PerMultilinestringCountTest2) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_per_multilinestring_point_count_test, + {0, 1, 3}, + {0, 3, 3, 6}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}, P{12, 12}}, + {3, 3}); +} diff --git a/cpp/tests/experimental/range/multipolygon_range_test.cu b/cpp/tests/experimental/range/multipolygon_range_test.cu index 39b18498a..97d8e4eaa 100644 --- a/cpp/tests/experimental/range/multipolygon_range_test.cu +++ b/cpp/tests/experimental/range/multipolygon_range_test.cu @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -29,50 +30,116 @@ using namespace cuspatial; using namespace cuspatial::test; -struct polygon_ref_exact_equal_comparator { - template - bool operator()(PolygonRef first, PolygonRef second) - { - if (first.num_rings() != second.num_rings()) return false; - - bool equals = true; - for (std::size_t i = 0; equals && i < first.num_rings(); ++i) { - auto ring_first = first[i]; - auto ring_second = second[i]; - if (ring_first.size() != ring_second.size()) equals = false; - for (std::size_t j = 0; j < ring_first.size(); ++j) - equals = equals && ring_first[j] == ring_second[j]; - } - return equals; - } -}; +// struct polygon_ref_exact_equal_comparator { +// template +// bool operator()(PolygonRef first, PolygonRef second) +// { +// if (first.num_rings() != second.num_rings()) return false; + +// bool equals = true; +// for (std::size_t i = 0; equals && i < first.num_rings(); ++i) { +// auto ring_first = first[i]; +// auto ring_second = second[i]; +// if (ring_first.size() != ring_second.size()) equals = false; +// for (std::size_t j = 0; j < ring_first.size(); ++j) +// equals = equals && ring_first[j] == ring_second[j]; +// } +// return equals; +// } +// }; template struct MultipolygonRangeTest : public BaseFixture { - void run_polygon_iterator_test_single(std::initializer_list geometry_offset, - std::initializer_list part_offset, - std::initializer_list ring_offset, - std::initializer_list> coordinates, - std::initializer_list expected_geometry_offset, - std::initializer_list expected_part_offset, - std::initializer_list expected_ring_offset, - std::initializer_list> expected_coordinates) + void run_per_multipolygon_count_iterator_single( + std::initializer_list geometry_offset, + std::initializer_list part_offset, + std::initializer_list ring_offset, + std::initializer_list> coordinates, + std::initializer_list expected_point_counts) { auto multipolygon_array = make_multipolygon_array(geometry_offset, part_offset, ring_offset, coordinates); + auto rng = multipolygon_array.range(); - auto expected_multipolygon_array = make_multipolygon_array( - expected_geometry_offset, expected_part_offset, expected_ring_offset, expected_coordinates); + auto got = rmm::device_uvector(rng.num_multipolygons(), stream()); - auto rng = multipolygon_array.range(); - auto expected_rng = expected_multipolygon_array.range(); + thrust::copy(rmm::exec_policy(stream()), + rng.per_multipolygon_point_count_begin(), + rng.per_multipolygon_point_count_end(), + got.begin()); - auto equal = rmm::device_uvector(expected_multipolygon_array.size(), stream()); + auto d_expected = thrust::device_vector(expected_point_counts.begin(), + expected_point_counts.end()); - thrust::transform(multipolygon_array.polygon_begin(), - multipolygon_array.polygon_end(), - expected_multipolygon_array.begin(), - equal.begin(), - polygon_ref_exact_equal_comparator{}); + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(got, d_expected); } }; + +TYPED_TEST_CASE(MultipolygonRangeTest, FloatingPointTypes); + +TYPED_TEST(MultipolygonRangeTest, PerMultipolygonCountIterator) +{ + CUSPATIAL_RUN_TEST(this->run_per_multipolygon_count_iterator_single, + {0, 1}, + {0, 1}, + {0, 4}, + {{0, 0}, {1, 0}, {1, 1}, {0, 1}}, + {4}); +} + +TYPED_TEST(MultipolygonRangeTest, PerMultipolygonCountIterator2) +{ + CUSPATIAL_RUN_TEST( + this->run_per_multipolygon_count_iterator_single, + {0, 1}, + {0, 2}, + {0, 4, 8}, + {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0.2, 0.2}, {0.2, 0.3}, {0.3, 0.3}, {0.3, 0.2}}, + {8}); +} + +TYPED_TEST(MultipolygonRangeTest, PerMultipolygonCountIterator3) +{ + CUSPATIAL_RUN_TEST(this->run_per_multipolygon_count_iterator_single, + {0, 2}, + {0, 2, 3}, + {0, 4, 8, 12}, + {{0, 0}, + {1, 0}, + {1, 1}, + {0, 1}, + {0.2, 0.2}, + {0.2, 0.3}, + {0.3, 0.3}, + {0.3, 0.2}, + {0, 0}, + {1, 0}, + {1, 1}, + {0, 1}}, + {12}); +} + +TYPED_TEST(MultipolygonRangeTest, PerMultipolygonCountIterator4) +{ + CUSPATIAL_RUN_TEST(this->run_per_multipolygon_count_iterator_single, + {0, 2, 3}, + {0, 2, 3, 4}, + {0, 4, 8, 12, 16}, + {{0, 0}, + {1, 0}, + {1, 1}, + {0, 1}, + {0.2, 0.2}, + {0.2, 0.3}, + {0.3, 0.3}, + {0.3, 0.2}, + {0, 0}, + {1, 0}, + {1, 1}, + {0, 1}, + {0, 0}, + {1, 0}, + {1, 1}, + {0, 1}}, + {12, 4}); +} diff --git a/cpp/tests/experimental/spatial/linestring_polygon_distance_test.cu b/cpp/tests/experimental/spatial/linestring_polygon_distance_test.cu new file mode 100644 index 000000000..8cbc85138 --- /dev/null +++ b/cpp/tests/experimental/spatial/linestring_polygon_distance_test.cu @@ -0,0 +1,112 @@ +/* + * 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 + +#include +#include +#include + +#include + +using namespace cuspatial; +using namespace cuspatial::test; + +template +struct PairwiseLinestringPolygonDistanceTest : public BaseFixture { + 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 multilinestring_geometry_offsets, + std::initializer_list multilinestring_part_offsets, + std::initializer_list> multilinestring_coordinates, + 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 multilinestrings = make_multilinestring_array( + multilinestring_geometry_offsets, multilinestring_part_offsets, multilinestring_coordinates); + + auto multipolygons = make_multipolygon_array(multipolygon_geometry_offsets, + multipolygon_part_offsets, + multipolygon_ring_offsets, + multipolygon_coordinates); + + auto got = rmm::device_uvector(multilinestrings.size(), stream()); + + auto ret = pairwise_linestring_polygon_distance( + multilinestrings.range(), 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(PairwiseLinestringPolygonDistanceTest, TestTypes); + +// Inputs are empty columns +TYPED_TEST(PairwiseLinestringPolygonDistanceTest, ZeroPairs) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST(this->run_single, + {0}, + {0}, + std::initializer_list

{}, + {0}, + {0}, + {0}, + std::initializer_list

{}, + {}); +} + +TYPED_TEST(PairwiseLinestringPolygonDistanceTest, TwoPairs) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST(this->run_single, + {0, 1, 2}, + {0, 4, 7}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{3, 3}, P{10, 10}, P{11, 11}, P{12, 12}}, + {0, 1, 2}, + {0, 1, 2}, + {0, 4, 9}, + {P{-1, -1}, + P{-2, -2}, + P{-2, -1}, + P{-1, -1}, + P{-10, -10}, + P{-10, -11}, + P{-11, -11}, + P{-11, -10}, + P{-10, -10}}, + {0.0, 0.0}); +} From 243d2d93b63abeb6a2d80c6cd6f23aadb5a17b2e Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 29 Mar 2023 15:47:26 -0700 Subject: [PATCH 05/54] commiting progress --- .../cuspatial/experimental/ranges/multilinestring_range.cuh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh b/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh index 075cabbeb..c0d41848d 100644 --- a/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh @@ -139,6 +139,12 @@ class multilinestring_range { /// Returns an iterator to the counts of points per multilinestring CUSPATIAL_HOST_DEVICE auto per_multilinestring_point_count_end(); + /// Returns an iterator to the counts of points per multilinestring + CUSPATIAL_HOST_DEVICE auto multilinestring_segment_count_begin(); + + /// Returns an iterator to the counts of points per multilinestring + CUSPATIAL_HOST_DEVICE auto multilinestring_segment_count_end(); + /// Returns an iterator to the start of the segment CUSPATIAL_HOST_DEVICE auto segment_begin(); From 0da0afc5f2a2d38a89910a163b600b646d81956f Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 31 Mar 2023 15:03:35 -0700 Subject: [PATCH 06/54] able to get the right segment combinations from within the kernel, need to test more rigidly --- .../experimental/detail/functors.cuh | 12 +- .../detail/linestring_polygon_distance.cuh | 112 +++++--- .../detail/ranges/multilinestring_range.cuh | 35 ++- .../detail/ranges/multipolygon_range.cuh | 112 +++++++- .../ranges/multilinestring_range.cuh | 6 + .../ranges/multipolygon_range.cuh | 23 ++ .../range/multilinestring_range_test.cu | 85 +++++++ .../range/multipolygon_range_test.cu | 239 ++++++++++++++++-- 8 files changed, 566 insertions(+), 58 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/functors.cuh b/cpp/include/cuspatial/experimental/detail/functors.cuh index f2e0b1231..e3530b47e 100644 --- a/cpp/include/cuspatial/experimental/detail/functors.cuh +++ b/cpp/include/cuspatial/experimental/detail/functors.cuh @@ -9,7 +9,7 @@ namespace cuspatial { namespace detail { -struct offset_pair_to_count_iterator { +struct offset_pair_to_count_functor { template CUSPATIAL_HOST_DEVICE auto operator()(OffsetPairIterator p) { @@ -19,6 +19,16 @@ struct offset_pair_to_count_iterator { } }; +struct point_count_to_segment_count_functor { + template + CUSPATIAL_HOST_DEVICE auto operator()(IndexPair n_point_linestring_pair) + { + auto nPoints = thrust::get<0>(n_point_linestring_pair); + auto nLinestrings = thrust::get<1>(n_point_linestring_pair); + return nPoints - nLinestrings; + } +}; + template struct to_subtracted_by_index_iterator { OffsetIterator begin; diff --git a/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh b/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh index bc642e7b8..f5f51741b 100644 --- a/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -76,11 +77,14 @@ template -void __global__ pairwise_linestring_polygon_distance_kernel(MultiLinestringRange multilinestrings, - MultiPolygonRange multipolygons, - IndexRange thread_bounds, - uint8_t* intersects, - OutputIt* distances) +void __global__ +pairwise_linestring_polygon_distance_kernel(MultiLinestringRange multilinestrings, + MultiPolygonRange multipolygons, + IndexRange thread_bounds, + IndexRange multilinestrings_segment_offsets, + IndexRange multipolygons_segment_offsets, + uint8_t* intersects, + OutputIt* distances) { using T = typename MultiLinestringRange::element_t; using index_t = iterator_value_type; @@ -98,13 +102,48 @@ void __global__ pairwise_linestring_polygon_distance_kernel(MultiLinestringRange continue; } - printf("idx: %d, geometry_id: %d, local_idx: %d\n", - static_cast(idx), - static_cast(geometry_id), - static_cast(local_idx)); + auto num_segment_this_multilinestring = + multilinestrings.multilinestring_segment_count_begin()[geometry_id]; + + auto multilinestring_segment_id = + local_idx % num_segment_this_multilinestring + multilinestrings_segment_offsets[geometry_id]; + auto multipolygon_segment_id = + local_idx / num_segment_this_multilinestring + multipolygons_segment_offsets[geometry_id]; + + printf( + "idx: %d, geometry_id: %d, local_idx: %d, multilinestring_segment_id: %d, " + "multipolygon_segment_id: %d\n", + static_cast(idx), + static_cast(geometry_id), + static_cast(local_idx), + static_cast(multilinestring_segment_id), + static_cast(multipolygon_segment_id)); + + auto [a, b] = multilinestrings.segment_begin()[multilinestring_segment_id]; + auto [c, d] = multipolygons.segment_begin()[multipolygon_segment_id]; + + printf("ab: (%f, %f) -> (%f, %f), cd: (%f, %f) -> (%f, %f)\n", + static_cast(a.x), + static_cast(a.y), + static_cast(b.x), + static_cast(b.y), + static_cast(c.x), + static_cast(c.y), + static_cast(d.x), + static_cast(d.y)); + + atomicMin(&distances[geometry_id], sqrt(squared_segment_distance(a, b, c, d))); } } +template +struct functor { + index_t __device__ operator()(thrust::tuple t) + { + return thrust::get<0>(t) * thrust::get<1>(t); + } +}; + } // namespace detail template @@ -155,36 +194,43 @@ OutputIt pairwise_linestring_polygon_distance(MultiLinestringRange multilinestri return multipoint_intersects; }(); - auto thread_counts = - rmm::device_uvector(multilinestrings.num_multilinestrings(), stream); - - auto per_multilinestring_point_count_it = - multilinestrings.per_multilinestring_point_count_begin(); - auto per_multipolygon_point_count_it = multipolygons.per_multipolygon_point_count_begin(); + // Compute the "boundary" of threads. Threads are partitioned based on the number of linestrings + // times the number of polygons in a multipoint-multipolygon pair. + auto segment_count_product_it = thrust::make_transform_iterator( + thrust::make_zip_iterator(multilinestrings.multilinestring_segment_count_begin(), + multipolygons.multipolygon_segment_count_begin()), + detail::functor{}); - thrust::transform( - rmm::exec_policy(stream), - per_multilinestring_point_count_it, - per_multilinestring_point_count_it + multilinestrings.num_multilinestrings(), - per_multipolygon_point_count_it, - thread_counts.begin(), - [] __device__(index_t multilinestring_point_count, index_t multipolygon_point_count) { - auto multilinestring_segment_count = - multilinestring_point_count == 0 ? 0 : multilinestring_point_count - 1; - auto multipolygon_segment_count = - multipolygon_point_count == 0 ? 0 : multipolygon_point_count - 1; - return multilinestring_segment_count * multipolygon_segment_count; - }); - - auto thread_bounds = rmm::device_uvector(thread_counts.size() + 1, stream); + auto thread_bounds = rmm::device_uvector(multilinestrings.size() + 1, stream); detail::zero_data_async(thread_bounds.begin(), thread_bounds.end(), stream); thrust::inclusive_scan(rmm::exec_policy(stream), - thread_counts.begin(), - thread_counts.end(), + segment_count_product_it, + segment_count_product_it + thread_bounds.size() - 1, thrust::next(thread_bounds.begin())); + auto multilinestring_segment_offsets = + rmm::device_uvector(multilinestrings.num_multilinestrings() + 1, stream); + auto multipolygon_segment_offsets = + rmm::device_uvector(multipolygons.num_multipolygons() + 1, stream); + + thrust::exclusive_scan( + rmm::exec_policy(stream), + multilinestrings.multilinestring_segment_count_begin(), + multilinestrings.multilinestring_segment_count_begin() + multilinestring_segment_offsets.size(), + multilinestring_segment_offsets.begin()); + + thrust::exclusive_scan( + rmm::exec_policy(stream), + multipolygons.multipolygon_segment_count_begin(), + multipolygons.multipolygon_segment_count_begin() + multipolygon_segment_offsets.size(), + multipolygon_segment_offsets.begin()); + cuspatial::test::print_device_vector(thread_bounds, "thread_bounds: "); + cuspatial::test::print_device_vector(multilinestring_segment_offsets, + "multilinestring_segment_offsets:"); + cuspatial::test::print_device_vector(multipolygon_segment_offsets, + "multipolygon_segment_offsets: "); auto num_threads = thread_bounds.back_element(stream); auto [tpb, num_blocks] = grid_1d(num_threads); @@ -193,6 +239,8 @@ OutputIt pairwise_linestring_polygon_distance(MultiLinestringRange multilinestri multilinestrings, multipolygons, range{thread_bounds.begin(), thread_bounds.end()}, + range{multilinestring_segment_offsets.begin(), multilinestring_segment_offsets.end()}, + range{multipolygon_segment_offsets.begin(), multipolygon_segment_offsets.end()}, multipoint_intersects.begin(), distances_first); diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh index 923ca4b35..78f0d71ad 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh @@ -16,6 +16,7 @@ #pragma once +#include "thrust/iterator/zip_iterator.h" #include #include #include @@ -214,7 +215,7 @@ CUSPATIAL_HOST_DEVICE auto multilinestring_range @@ -224,6 +225,38 @@ CUSPATIAL_HOST_DEVICE auto multilinestring_range +CUSPATIAL_HOST_DEVICE auto multilinestring_range:: + multilinestring_segment_count_begin() +{ + auto n_point_linestring_pair_it = thrust::make_zip_iterator( + per_multilinestring_point_count_begin(), multilinestring_linestring_count_begin()); + return thrust::make_transform_iterator(n_point_linestring_pair_it, + detail::point_count_to_segment_count_functor{}); +} + +template +CUSPATIAL_HOST_DEVICE auto multilinestring_range:: + multilinestring_segment_count_end() +{ + return multilinestring_segment_count_begin() + num_multilinestrings(); +} + +template +CUSPATIAL_HOST_DEVICE auto multilinestring_range:: + multilinestring_linestring_count_begin() +{ + auto paired_it = thrust::make_zip_iterator(_geometry_begin, thrust::next(_geometry_begin)); + return thrust::make_transform_iterator(paired_it, detail::offset_pair_to_count_functor{}); +} + +template +CUSPATIAL_HOST_DEVICE auto multilinestring_range:: + multilinestring_linestring_count_end() +{ + return multilinestring_linestring_count_begin() + num_multilinestrings(); +} + template CUSPATIAL_HOST_DEVICE auto multilinestring_range::segment_begin() diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh index 2fbb26b5a..9c53bae19 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh @@ -152,6 +152,16 @@ multipolygon_range::n return thrust::distance(_point_begin, _point_end); } +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range::num_segments() +{ + return num_points() - num_rings(); +} + template :: thrust::next(multipolygon_point_offset_it)); return thrust::make_transform_iterator(point_offset_pair_it, - detail::offset_pair_to_count_iterator{}); + detail::offset_pair_to_count_functor{}); } template :: return per_multipolygon_point_count_begin() + num_multipolygons(); } +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range:: + multipolygon_ring_count_begin() +{ + auto multipolygon_ring_offset_it = + thrust::make_permutation_iterator(_part_begin, _geometry_begin); + + auto ring_offset_pair_it = thrust::make_zip_iterator(multipolygon_ring_offset_it, + thrust::next(multipolygon_ring_offset_it)); + + return thrust::make_transform_iterator(ring_offset_pair_it, + detail::offset_pair_to_count_functor{}); +} + +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range:: + multipolygon_ring_count_end() +{ + return multipolygon_ring_count_begin() + num_multipolygons(); +} + +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range:: + multipolygon_segment_count_begin() +{ + auto multipolygon_point_ring_count_it = thrust::make_zip_iterator( + per_multipolygon_point_count_begin(), multipolygon_ring_count_begin()); + + return thrust::make_transform_iterator(multipolygon_point_ring_count_it, + detail::point_count_to_segment_count_functor{}); +} + +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range:: + multipolygon_segment_count_end() +{ + return multipolygon_segment_count_begin() + num_multipolygons(); +} + template ::g return segment{_point_begin[segment_idx], _point_begin[segment_idx + 1]}; } +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range::segment_begin() +{ + return detail::make_counting_transform_iterator( + 0, + detail::to_valid_segment_functor{ + this->subtracted_ring_begin(), this->subtracted_ring_end(), _point_begin}); +} + +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range::segment_end() +{ + return segment_begin() + num_segments(); +} + +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range:: + subtracted_ring_begin() +{ + return detail::make_counting_transform_iterator( + 0, detail::to_subtracted_by_index_iterator{_ring_begin}); +} + +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range::subtracted_ring_end() +{ + return subtracted_ring_begin() + thrust::distance(_ring_begin, _ring_end); +} + template CUSPATIAL_HOST_DEVICE bool is_valid_segment_id(IndexType1 segment_idx, IndexType2 ring_idx); diff --git a/cpp/tests/experimental/range/multilinestring_range_test.cu b/cpp/tests/experimental/range/multilinestring_range_test.cu index d862d2701..75a1c508a 100644 --- a/cpp/tests/experimental/range/multilinestring_range_test.cu +++ b/cpp/tests/experimental/range/multilinestring_range_test.cu @@ -87,6 +87,27 @@ struct MultilinestringRangeTest : public BaseFixture { CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(d_expected, got); } + + void run_multilinestring_segment_count_test(std::initializer_list geometry_offset, + std::initializer_list part_offset, + std::initializer_list> coordinates, + std::initializer_list expected) + { + auto multilinestring_array = + make_multilinestring_array(geometry_offset, part_offset, coordinates); + + auto d_expected = thrust::device_vector(expected.begin(), expected.end()); + + auto rng = multilinestring_array.range(); + + rmm::device_uvector got(rng.num_multilinestrings(), stream()); + thrust::copy(rmm::exec_policy(stream()), + rng.multilinestring_segment_count_begin(), + rng.multilinestring_segment_count_end(), + got.begin()); + + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(d_expected, got); + } }; using TestTypes = ::testing::Types; @@ -266,3 +287,67 @@ TYPED_TEST(MultilinestringRangeTest, PerMultilinestringCountTest2) {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}, P{12, 12}}, {3, 3}); } + +TYPED_TEST(MultilinestringRangeTest, MultilinestringSegmentCountTest) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST( + this->run_multilinestring_segment_count_test, {0, 1}, {0, 3}, {P{0, 0}, P{1, 1}, P{2, 2}}, {2}); +} + +// FIXME: contains empty linestring +TYPED_TEST(MultilinestringRangeTest, DISABLED_MultilinestringSegmentCountTest2) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_multilinestring_segment_count_test, + {0, 1, 3}, + {0, 3, 3, 6}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}, P{12, 12}}, + {2, 2}); +} + +TYPED_TEST(MultilinestringRangeTest, MultilinestringSegmentCountTest3) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_multilinestring_segment_count_test, + {0, 1, 2}, + {0, 3, 6}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}, P{12, 12}}, + {2, 2}); +} + +TYPED_TEST(MultilinestringRangeTest, MultilinestringSegmentCountTest4) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_multilinestring_segment_count_test, + {0, 1, 3}, + {0, 3, 5, 7}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}, P{20, 20}, P{21, 21}}, + {2, 2}); +} + +// FIXME: contains empty linestring +TYPED_TEST(MultilinestringRangeTest, DISABLED_MultilinestringSegmentCountTest5) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_multilinestring_segment_count_test, + {0, 1, 2, 3}, + {0, 3, 3, 6}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}, P{12, 12}}, + {2, 0, 2}); +} diff --git a/cpp/tests/experimental/range/multipolygon_range_test.cu b/cpp/tests/experimental/range/multipolygon_range_test.cu index 97d8e4eaa..7d8accdb0 100644 --- a/cpp/tests/experimental/range/multipolygon_range_test.cu +++ b/cpp/tests/experimental/range/multipolygon_range_test.cu @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -30,27 +31,28 @@ using namespace cuspatial; using namespace cuspatial::test; -// struct polygon_ref_exact_equal_comparator { -// template -// bool operator()(PolygonRef first, PolygonRef second) -// { -// if (first.num_rings() != second.num_rings()) return false; - -// bool equals = true; -// for (std::size_t i = 0; equals && i < first.num_rings(); ++i) { -// auto ring_first = first[i]; -// auto ring_second = second[i]; -// if (ring_first.size() != ring_second.size()) equals = false; -// for (std::size_t j = 0; j < ring_first.size(); ++j) -// equals = equals && ring_first[j] == ring_second[j]; -// } -// return equals; -// } -// }; - template struct MultipolygonRangeTest : public BaseFixture { - void run_per_multipolygon_count_iterator_single( + void run_multipolygon_segment_iterator_single(std::initializer_list geometry_offset, + std::initializer_list part_offset, + std::initializer_list ring_offset, + std::initializer_list> coordinates, + std::initializer_list> expected) + { + auto multipolygon_array = + make_multipolygon_array(geometry_offset, part_offset, ring_offset, coordinates); + auto rng = multipolygon_array.range(); + + auto got = rmm::device_uvector>(rng.num_segments(), stream()); + + thrust::copy(rmm::exec_policy(stream()), rng.segment_begin(), rng.segment_end(), got.begin()); + + auto d_expected = thrust::device_vector>(expected.begin(), expected.end()); + + CUSPATIAL_EXPECT_VEC2D_PAIRS_EQUIVALENT(got, d_expected); + } + + void run_multipolygon_point_count_iterator_single( std::initializer_list geometry_offset, std::initializer_list part_offset, std::initializer_list ring_offset, @@ -73,13 +75,95 @@ struct MultipolygonRangeTest : public BaseFixture { CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(got, d_expected); } + + void run_multipolygon_segment_count_single( + std::initializer_list geometry_offset, + std::initializer_list part_offset, + std::initializer_list ring_offset, + std::initializer_list> coordinates, + std::initializer_list expected_segment_counts) + { + auto multipolygon_array = + make_multipolygon_array(geometry_offset, part_offset, ring_offset, coordinates); + auto rng = multipolygon_array.range(); + + auto got = rmm::device_uvector(rng.num_multipolygons(), stream()); + + thrust::copy(rmm::exec_policy(stream()), + rng.multipolygon_segment_count_begin(), + rng.multipolygon_segment_count_end(), + got.begin()); + + auto d_expected = thrust::device_vector(expected_segment_counts.begin(), + expected_segment_counts.end()); + + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(got, d_expected); + } }; TYPED_TEST_CASE(MultipolygonRangeTest, FloatingPointTypes); +TYPED_TEST(MultipolygonRangeTest, SegmentIterators) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + CUSPATIAL_RUN_TEST(this->run_multipolygon_segment_iterator_single, + {0, 1}, + {0, 1}, + {0, 4}, + {{0, 0}, {1, 0}, {1, 1}, {0, 0}}, + {S{P{0, 0}, P{1, 0}}, S{P{1, 0}, P{1, 1}}, S{P{1, 1}, P{0, 0}}}); +} + +TYPED_TEST(MultipolygonRangeTest, SegmentIterators2) +{ + CUSPATIAL_RUN_TEST(this->run_multipolygon_segment_iterator_single, + {0, 1}, + {0, 2}, + {0, 4, 8}, + {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {10, 10}, {11, 10}, {11, 11}, {10, 10}}, + {{{0, 0}, {1, 0}}, + {{1, 0}, {1, 1}}, + {{1, 1}, {0, 0}}, + {{10, 10}, {11, 10}}, + {{11, 10}, {11, 11}}, + {{11, 11}, {10, 10}}}); +} + +TYPED_TEST(MultipolygonRangeTest, SegmentIterators3) +{ + CUSPATIAL_RUN_TEST(this->run_multipolygon_segment_iterator_single, + {0, 2}, + {0, 1, 2}, + {0, 4, 8}, + {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {10, 10}, {11, 10}, {11, 11}, {10, 10}}, + {{{0, 0}, {1, 0}}, + {{1, 0}, {1, 1}}, + {{1, 1}, {0, 0}}, + {{10, 10}, {11, 10}}, + {{11, 10}, {11, 11}}, + {{11, 11}, {10, 10}}}); +} + +TYPED_TEST(MultipolygonRangeTest, SegmentIterators4) +{ + CUSPATIAL_RUN_TEST(this->run_multipolygon_segment_iterator_single, + {0, 1, 2}, + {0, 1, 2}, + {0, 4, 8}, + {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {10, 10}, {11, 10}, {11, 11}, {10, 10}}, + {{{0, 0}, {1, 0}}, + {{1, 0}, {1, 1}}, + {{1, 1}, {0, 0}}, + {{10, 10}, {11, 10}}, + {{11, 10}, {11, 11}}, + {{11, 11}, {10, 10}}}); +} + TYPED_TEST(MultipolygonRangeTest, PerMultipolygonCountIterator) { - CUSPATIAL_RUN_TEST(this->run_per_multipolygon_count_iterator_single, + CUSPATIAL_RUN_TEST(this->run_multipolygon_point_count_iterator_single, {0, 1}, {0, 1}, {0, 4}, @@ -90,7 +174,7 @@ TYPED_TEST(MultipolygonRangeTest, PerMultipolygonCountIterator) TYPED_TEST(MultipolygonRangeTest, PerMultipolygonCountIterator2) { CUSPATIAL_RUN_TEST( - this->run_per_multipolygon_count_iterator_single, + this->run_multipolygon_point_count_iterator_single, {0, 1}, {0, 2}, {0, 4, 8}, @@ -100,7 +184,7 @@ TYPED_TEST(MultipolygonRangeTest, PerMultipolygonCountIterator2) TYPED_TEST(MultipolygonRangeTest, PerMultipolygonCountIterator3) { - CUSPATIAL_RUN_TEST(this->run_per_multipolygon_count_iterator_single, + CUSPATIAL_RUN_TEST(this->run_multipolygon_point_count_iterator_single, {0, 2}, {0, 2, 3}, {0, 4, 8, 12}, @@ -121,7 +205,7 @@ TYPED_TEST(MultipolygonRangeTest, PerMultipolygonCountIterator3) TYPED_TEST(MultipolygonRangeTest, PerMultipolygonCountIterator4) { - CUSPATIAL_RUN_TEST(this->run_per_multipolygon_count_iterator_single, + CUSPATIAL_RUN_TEST(this->run_multipolygon_point_count_iterator_single, {0, 2, 3}, {0, 2, 3, 4}, {0, 4, 8, 12, 16}, @@ -143,3 +227,112 @@ TYPED_TEST(MultipolygonRangeTest, PerMultipolygonCountIterator4) {0, 1}}, {12, 4}); } + +TYPED_TEST(MultipolygonRangeTest, MultipolygonSegmentCount) +{ + CUSPATIAL_RUN_TEST(this->run_multipolygon_segment_count_single, + {0, 1}, + {0, 1}, + {0, 4}, + {{0, 0}, {1, 0}, {1, 1}, {0, 1}}, + {3}); +} + +TYPED_TEST(MultipolygonRangeTest, MultipolygonSegmentCount2) +{ + CUSPATIAL_RUN_TEST( + this->run_multipolygon_segment_count_single, + {0, 1}, + {0, 2}, + {0, 4, 8}, + {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0.2, 0.2}, {0.2, 0.3}, {0.3, 0.3}, {0.3, 0.2}}, + {6}); +} + +TYPED_TEST(MultipolygonRangeTest, MultipolygonSegmentCount3) +{ + CUSPATIAL_RUN_TEST(this->run_multipolygon_segment_count_single, + {0, 2}, + {0, 2, 3}, + {0, 4, 8, 12}, + {{0, 0}, + {1, 0}, + {1, 1}, + {0, 1}, + {0.2, 0.2}, + {0.2, 0.3}, + {0.3, 0.3}, + {0.3, 0.2}, + {0, 0}, + {1, 0}, + {1, 1}, + {0, 1}}, + {9}); +} + +TYPED_TEST(MultipolygonRangeTest, MultipolygonSegmentCount4) +{ + CUSPATIAL_RUN_TEST(this->run_multipolygon_segment_count_single, + {0, 2, 3}, + {0, 2, 3, 4}, + {0, 4, 8, 12, 16}, + {{0, 0}, + {1, 0}, + {1, 1}, + {0, 1}, + {0.2, 0.2}, + {0.2, 0.3}, + {0.3, 0.3}, + {0.3, 0.2}, + {0, 0}, + {1, 0}, + {1, 1}, + {0, 1}, + {0, 0}, + {1, 0}, + {1, 1}, + {0, 1}}, + {9, 3}); +} + +// TYPED_TEST(MultipolygonRangeTest, MultipolygonSegmentCount_ConatainsEmptyRing) +// { +// CUSPATIAL_RUN_TEST(this->run_multipolygon_segment_count_single, +// {0, 2, 3}, +// {0, 2, 3, 4}, +// {0, 4, 4, 8, 12}, +// {{0, 0}, +// {1, 0}, +// {1, 1}, +// {0, 1}, +// {0.2, 0.2}, +// {0.2, 0.3}, +// {0.3, 0.3}, +// {0.3, 0.2}, +// {0, 0}, +// {1, 0}, +// {1, 1}, +// {0, 1}}, +// {6, 3}); +// } + +// TYPED_TEST(MultipolygonRangeTest, MultipolygonSegmentCount_ConatainsEmptyPart) +// { +// CUSPATIAL_RUN_TEST(this->run_multipolygon_segment_count_single, +// {0, 3, 4}, +// {0, 2, 2, 3, 4}, +// {0, 4, 8, 12}, +// {{0, 0}, +// {1, 0}, +// {1, 1}, +// {0, 1}, +// {0.2, 0.2}, +// {0.2, 0.3}, +// {0.3, 0.3}, +// {0.3, 0.2}, +// {0, 0}, +// {1, 0}, +// {1, 1}, +// {0, 1}}, +// {6, 3}); +// } From 49e43aaefe8f384cba0fcf6fdf8ff20cef09c041 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 31 Mar 2023 15:26:22 -0700 Subject: [PATCH 07/54] [skip-ci] initial port from feature/linestring_polygon_distance_header --- .../experimental/detail/functors.cuh | 101 +++++ .../detail/ranges/multilinestring_range.cuh | 119 +++++- .../detail/ranges/multipolygon_range.cuh | 164 +++++++- .../ranges/multilinestring_range.cuh | 57 +++ .../ranges/multipolygon_range.cuh | 46 +++ cpp/include/cuspatial_test/base_fixture.hpp | 16 +- cpp/tests/CMakeLists.txt | 4 + .../range/multilinestring_range_test.cu | 353 ++++++++++++++++++ .../range/multipolygon_range_test.cu | 338 +++++++++++++++++ 9 files changed, 1185 insertions(+), 13 deletions(-) create mode 100644 cpp/include/cuspatial/experimental/detail/functors.cuh create mode 100644 cpp/tests/experimental/range/multilinestring_range_test.cu create mode 100644 cpp/tests/experimental/range/multipolygon_range_test.cu diff --git a/cpp/include/cuspatial/experimental/detail/functors.cuh b/cpp/include/cuspatial/experimental/detail/functors.cuh new file mode 100644 index 000000000..4d7b083e3 --- /dev/null +++ b/cpp/include/cuspatial/experimental/detail/functors.cuh @@ -0,0 +1,101 @@ + +#pragma once + +#include +#include +#include + +#include + +namespace cuspatial { +namespace detail { + +struct offset_pair_to_count_functor { + template + CUSPATIAL_HOST_DEVICE auto operator()(OffsetPairIterator p) + { + auto first = thrust::get<0>(p); + auto second = thrust::get<1>(p); + return second - first; + } +}; + +struct point_count_to_segment_count_functor { + template + CUSPATIAL_HOST_DEVICE auto operator()(IndexPair n_point_linestring_pair) + { + auto nPoints = thrust::get<0>(n_point_linestring_pair); + auto nLinestrings = thrust::get<1>(n_point_linestring_pair); + return nPoints - nLinestrings; + } +}; + +template +struct to_subtracted_by_index_iterator { + OffsetIterator begin; + + template + CUSPATIAL_HOST_DEVICE auto operator()(IndexType i) + { + // printf("begin[i]: %d i: %d\n", static_cast(begin[i]), static_cast(i)); + return begin[i] - i; + } +}; + +template +to_subtracted_by_index_iterator(OffsetIterator) -> to_subtracted_by_index_iterator; + +template +struct to_valid_segment_functor { + using element_t = iterator_vec_base_type; + + OffsetIterator begin; + OffsetIterator end; + CoordinateIterator point_begin; + + template + CUSPATIAL_HOST_DEVICE segment operator()(IndexType i) + { + auto kit = thrust::upper_bound(thrust::seq, begin, end, i); + auto k = thrust::distance(begin, kit); + auto pid = i + k - 1; + + // printf("%d %d %d\n", static_cast(i), static_cast(k), static_cast(pid)); + return segment{point_begin[pid], point_begin[pid + 1]}; + } +}; + +template +to_valid_segment_functor(OffsetIterator, OffsetIterator, CoordinateIterator) + -> to_valid_segment_functor; + +template +struct wraparound_functor { + IndexType length; + + template + CUSPATIAL_HOST_DEVICE auto operator()(IndexType2 i) + { + return i % length; + } +}; + +template +wraparound_functor(IndexType) -> wraparound_functor; + +// template +// struct repeat_functor { +// IndexType repeats; + +// template +// CUSPATIAL_HOST_DEVICE auto operator()(IndexType2 i) +// { +// return i / repeats; +// } +// }; + +// template +// wraparound_functor(IndexType) -> wraparound_functor; + +} // namespace detail +} // 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 332cacaa8..78f0d71ad 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh @@ -16,6 +16,7 @@ #pragma once +#include "thrust/iterator/zip_iterator.h" #include #include #include @@ -23,11 +24,14 @@ #include #include -#include +#include #include +#include #include #include +#include + #include #include @@ -82,11 +86,6 @@ multilinestring_range::multilinestr _point_begin(point_begin), _point_end(point_end) { - static_assert(is_vec_2d>, - "point_begin and point_end must be iterators to floating point vec_2d types."); - - CUSPATIAL_EXPECTS_VALID_MULTILINESTRING_SIZES( - num_points(), num_multilinestrings() + 1, num_linestrings() + 1); } template @@ -110,6 +109,13 @@ multilinestring_range::num_points() return thrust::distance(_point_begin, _point_end); } +template +CUSPATIAL_HOST_DEVICE auto +multilinestring_range::num_segments() +{ + return num_points() - num_linestrings(); +} + template CUSPATIAL_HOST_DEVICE auto multilinestring_range::multilinestring_begin() @@ -202,6 +208,107 @@ multilinestring_range::segment(Inde return thrust::make_pair(_point_begin[segment_idx], _point_begin[segment_idx + 1]); } +template +CUSPATIAL_HOST_DEVICE auto multilinestring_range:: + per_multilinestring_point_count_begin() +{ + auto multilinestring_offset_it = thrust::make_permutation_iterator(_part_begin, _geometry_begin); + auto paired_it = + thrust::make_zip_iterator(multilinestring_offset_it, thrust::next(multilinestring_offset_it)); + return thrust::make_transform_iterator(paired_it, detail::offset_pair_to_count_functor{}); +} + +template +CUSPATIAL_HOST_DEVICE auto multilinestring_range:: + per_multilinestring_point_count_end() +{ + return per_multilinestring_point_count_begin() + num_multilinestrings(); +} + +template +CUSPATIAL_HOST_DEVICE auto multilinestring_range:: + multilinestring_segment_count_begin() +{ + auto n_point_linestring_pair_it = thrust::make_zip_iterator( + per_multilinestring_point_count_begin(), multilinestring_linestring_count_begin()); + return thrust::make_transform_iterator(n_point_linestring_pair_it, + detail::point_count_to_segment_count_functor{}); +} + +template +CUSPATIAL_HOST_DEVICE auto multilinestring_range:: + multilinestring_segment_count_end() +{ + return multilinestring_segment_count_begin() + num_multilinestrings(); +} + +template +CUSPATIAL_HOST_DEVICE auto multilinestring_range:: + multilinestring_linestring_count_begin() +{ + auto paired_it = thrust::make_zip_iterator(_geometry_begin, thrust::next(_geometry_begin)); + return thrust::make_transform_iterator(paired_it, detail::offset_pair_to_count_functor{}); +} + +template +CUSPATIAL_HOST_DEVICE auto multilinestring_range:: + multilinestring_linestring_count_end() +{ + return multilinestring_linestring_count_begin() + num_multilinestrings(); +} + +template +CUSPATIAL_HOST_DEVICE auto +multilinestring_range::segment_begin() +{ + return detail::make_counting_transform_iterator( + 0, + detail::to_valid_segment_functor{ + this->subtracted_part_begin(), this->subtracted_part_end(), _point_begin}); +} + +template +CUSPATIAL_HOST_DEVICE auto +multilinestring_range::segment_end() +{ + return segment_begin() + num_segments(); +} + +template +CUSPATIAL_HOST_DEVICE auto +multilinestring_range::subtracted_part_begin() +{ + return detail::make_counting_transform_iterator( + 0, detail::to_subtracted_by_index_iterator{_part_begin}); +} + +template +CUSPATIAL_HOST_DEVICE auto +multilinestring_range::subtracted_part_end() +{ + return subtracted_part_begin() + thrust::distance(_part_begin, _part_end); +} + +template +CUSPATIAL_HOST_DEVICE auto +multilinestring_range::segment_tiled_begin() +{ + auto tiled_it = + detail::make_counting_transform_iterator(0, detail::wraparound_functor{num_segments()}); + return thrust::make_permutation_iterator(segment_begin(), tiled_it); +} + +template +CUSPATIAL_HOST_DEVICE auto +multilinestring_range::as_multipoint_range() +{ + auto multipoint_geometry_it = thrust::make_permutation_iterator(_part_begin, _geometry_begin); + return multipoint_range{multipoint_geometry_it, + multipoint_geometry_it + num_multilinestrings(), + _point_begin, + _point_end}; +} + template template CUSPATIAL_HOST_DEVICE auto diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh index b9e281af8..1846c8213 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,7 @@ #include #include +#include #include #include @@ -104,7 +106,7 @@ multipolygon_range::m _point_end(point_end) { static_assert(is_vec_2d>, - "point_begin and point_end must be iterators to floating point vec_2d types."); + "Coordinate range must be constructed with iterators to vec_2d."); CUSPATIAL_EXPECTS_VALID_MULTIPOLYGON_SIZES( num_points(), num_multipolygons() + 1, num_polygons() + 1, num_rings() + 1); @@ -150,6 +152,16 @@ multipolygon_range::n return thrust::distance(_point_begin, _point_end); } +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range::num_segments() +{ + return num_points() - num_rings(); +} + template ::p return _point_end; } +// template +// CUSPATIAL_HOST_DEVICE auto +// multipolygon_range::polygon_begin() +// { +// return detail::make_counting_transform_iterator( +// 0, to_polygon_functor{_part_begin, _ring_begin, point_begin, point_end}); +// } + +// template +// CUSPATIAL_HOST_DEVICE auto +// multipolygon_range::polygon_end() +// { +// return polygon_begin() + num_polygons(); +// } + template :: return geometry_idx_from_part_idx(part_idx_from_ring_idx(ring_idx)); } +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range:: + per_multipolygon_point_count_begin() +{ + auto multipolygon_point_offset_it = thrust::make_permutation_iterator( + _ring_begin, thrust::make_permutation_iterator(_part_begin, _geometry_begin)); + + auto point_offset_pair_it = thrust::make_zip_iterator(multipolygon_point_offset_it, + thrust::next(multipolygon_point_offset_it)); + + return thrust::make_transform_iterator(point_offset_pair_it, + detail::offset_pair_to_count_functor{}); +} + +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range:: + per_multipolygon_point_count_end() +{ + return per_multipolygon_point_count_begin() + num_multipolygons(); +} + +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range:: + multipolygon_ring_count_begin() +{ + auto multipolygon_ring_offset_it = + thrust::make_permutation_iterator(_part_begin, _geometry_begin); + + auto ring_offset_pair_it = thrust::make_zip_iterator(multipolygon_ring_offset_it, + thrust::next(multipolygon_ring_offset_it)); + + return thrust::make_transform_iterator(ring_offset_pair_it, + detail::offset_pair_to_count_functor{}); +} + +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range:: + multipolygon_ring_count_end() +{ + return multipolygon_ring_count_begin() + num_multipolygons(); +} + +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range:: + multipolygon_segment_count_begin() +{ + auto multipolygon_point_ring_count_it = thrust::make_zip_iterator( + per_multipolygon_point_count_begin(), multipolygon_ring_count_begin()); + + return thrust::make_transform_iterator(multipolygon_point_ring_count_it, + detail::point_count_to_segment_count_functor{}); +} + +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range:: + multipolygon_segment_count_end() +{ + return multipolygon_segment_count_begin() + num_multipolygons(); +} + template ::g return segment{_point_begin[segment_idx], _point_begin[segment_idx + 1]}; } +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range::segment_begin() +{ + return detail::make_counting_transform_iterator( + 0, + detail::to_valid_segment_functor{ + this->subtracted_ring_begin(), this->subtracted_ring_end(), _point_begin}); +} + +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range::segment_end() +{ + return segment_begin() + num_segments(); +} + +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range:: + subtracted_ring_begin() +{ + return detail::make_counting_transform_iterator( + 0, detail::to_subtracted_by_index_iterator{_ring_begin}); +} + +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range::subtracted_ring_end() +{ + return subtracted_ring_begin() + thrust::distance(_ring_begin, _ring_end); +} + template #include +#include #include #include #include @@ -77,6 +78,9 @@ class multilinestring_range { /// Return the total number of points in the array. CUSPATIAL_HOST_DEVICE auto num_points(); + /// Return the total number of segments in the array. + CUSPATIAL_HOST_DEVICE auto num_segments(); + /// Return the iterator to the first multilinestring in the range. CUSPATIAL_HOST_DEVICE auto multilinestring_begin(); @@ -141,10 +145,59 @@ class multilinestring_range { CUSPATIAL_HOST_DEVICE thrust::pair, vec_2d> segment( IndexType segment_idx); + /// Returns an iterator to the counts of points per multilinestring + CUSPATIAL_HOST_DEVICE auto per_multilinestring_point_count_begin(); + + /// Returns an iterator to the counts of points per multilinestring + CUSPATIAL_HOST_DEVICE auto per_multilinestring_point_count_end(); + + /// Returns an iterator to the counts of points per multilinestring + CUSPATIAL_HOST_DEVICE auto multilinestring_segment_count_begin(); + + /// Returns an iterator to the counts of points per multilinestring + CUSPATIAL_HOST_DEVICE auto multilinestring_segment_count_end(); + + /// Returns an iterator to the counts of points per multilinestring + CUSPATIAL_HOST_DEVICE auto multilinestring_linestring_count_begin(); + + /// Returns an iterator to the counts of points per multilinestring + CUSPATIAL_HOST_DEVICE auto multilinestring_linestring_count_end(); + + /// Returns an iterator to the start of the segment + CUSPATIAL_HOST_DEVICE auto segment_begin(); + + /// Returns an iterator to the end of the segment + CUSPATIAL_HOST_DEVICE auto segment_end(); + + /// Infinite iterators + /// Note: Infinite iterators currently doesn't work with ranges that contains empty geometry. + + /// Returns an infinite iterator to the "tiled" segments of the multilinestring. + /// If the multilinestring range has 5 segments, this iterator will iterate on the + /// 0th, 1st, 2nd, 3rd, 4th, 0th, 1st, 2nd, 3rd, 4th segment for the first 10 iterations. + /// + /// The name of `tile` comes from numpy.tile[1] + /// [1] https://numpy.org/doc/stable/reference/generated/numpy.tile.html + CUSPATIAL_HOST_DEVICE auto segment_tiled_begin(); + /// Returns the `multilinestring_idx`th multilinestring in the range. template CUSPATIAL_HOST_DEVICE auto operator[](IndexType multilinestring_idx); + /// Raw offsets iterator + + CUSPATIAL_HOST_DEVICE auto geometry_offsets_begin() { return _geometry_begin; } + CUSPATIAL_HOST_DEVICE auto geometry_offsets_end() { return _geometry_end; } + CUSPATIAL_HOST_DEVICE auto part_offsets_begin() { return _part_begin; } + CUSPATIAL_HOST_DEVICE auto part_offsets_end() { return _part_end; } + + /// Range Casts + + /// Casts the multilinestring range into a multipoint range. + /// This treats each multilinestring as simply a collection of points, + /// ignoring all edges of the multilinestring. + CUSPATIAL_HOST_DEVICE auto as_multipoint_range(); + protected: GeometryIterator _geometry_begin; GeometryIterator _geometry_end; @@ -153,6 +206,10 @@ class multilinestring_range { VecIterator _point_begin; VecIterator _point_end; + // TODO: find a better name + CUSPATIAL_HOST_DEVICE auto subtracted_part_begin(); + CUSPATIAL_HOST_DEVICE auto subtracted_part_end(); + private: /// @internal /// Return the iterator to the part index where the point locates. diff --git a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh index bb472f15e..2bea47a2a 100644 --- a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh @@ -90,6 +90,9 @@ class multipolygon_range { /// Return the total number of points in the array. CUSPATIAL_HOST_DEVICE auto num_points(); + /// Return the total number of segments in the array. + CUSPATIAL_HOST_DEVICE auto num_segments(); + /// Return the iterator to the first multipolygon in the range. CUSPATIAL_HOST_DEVICE auto multipolygon_begin(); @@ -120,6 +123,12 @@ class multipolygon_range { /// Return the iterator to the one past the last ring offset in the range. CUSPATIAL_HOST_DEVICE auto ring_offset_end() { return _ring_end; } + // /// Return the iterator to the first polygon in the range. + // CUSPATIAL_HOST_DEVICE auto polygon_begin(); + + // /// Return the iterator to the one past the last polygon in the range. + // CUSPATIAL_HOST_DEVICE auto polygon_end(); + /// 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 @@ -154,6 +163,39 @@ class multipolygon_range { CUSPATIAL_HOST_DEVICE bool is_first_point_of_multipolygon(IndexType1 point_idx, IndexType2 geometry_idx); + /// Returns an iterator to the number of points of the first multipolygon + CUSPATIAL_HOST_DEVICE auto per_multipolygon_point_count_begin(); + /// Returns the one past the iterator to the number of points of the last multipolygon + CUSPATIAL_HOST_DEVICE auto per_multipolygon_point_count_end(); + + /// Returns an iterator to the number of rings of the first multipolygon + CUSPATIAL_HOST_DEVICE auto multipolygon_ring_count_begin(); + /// Returns the one past the iterator to the number of rings of the last multipolygon + CUSPATIAL_HOST_DEVICE auto multipolygon_ring_count_end(); + + /// Returns an iterator to the number of segments of the first multipolygon + CUSPATIAL_HOST_DEVICE auto multipolygon_segment_count_begin(); + /// Returns the one past the iterator to the number of segments of the last multipolygon + CUSPATIAL_HOST_DEVICE auto multipolygon_segment_count_end(); + + /// Returns an iterator to the start of the segment + CUSPATIAL_HOST_DEVICE auto segment_begin(); + + /// Returns an iterator to the end of the segment + CUSPATIAL_HOST_DEVICE auto segment_end(); + + // /// Returns an infinite iterator to the "repeated" polygons of the multipolygon range. + // /// If the multipolygon range has 2 polygons, an iterator with repeats 3 will iterate on the + // /// 0th, 0th, 0th, 1st, 1st, 1st, 0th, 0th, 0th polygon for the first 9 iterations. + // /// + // /// The name of `repeated` comes from [numpy.repeat](1) + // /// [1] https://numpy.org/doc/stable/reference/generated/numpy.repeat.html + // template + // CUSPATIAL_HOST_DEVICE auto polygon_wraparound_repeated_begin(IndexType repeats); + + // template + // CUSPATIAL_HOST_DEVICE auto segment_wraparound_repeated_begin() + protected: GeometryIterator _geometry_begin; GeometryIterator _geometry_end; @@ -164,6 +206,10 @@ class multipolygon_range { VecIterator _point_begin; VecIterator _point_end; + // TODO: find a better name + CUSPATIAL_HOST_DEVICE auto subtracted_ring_begin(); + CUSPATIAL_HOST_DEVICE auto subtracted_ring_end(); + private: template CUSPATIAL_HOST_DEVICE bool is_valid_segment_id(IndexType1 segment_idx, IndexType2 ring_idx); diff --git a/cpp/include/cuspatial_test/base_fixture.hpp b/cpp/include/cuspatial_test/base_fixture.hpp index 4e925025b..52635c34d 100644 --- a/cpp/include/cuspatial_test/base_fixture.hpp +++ b/cpp/include/cuspatial_test/base_fixture.hpp @@ -31,8 +31,8 @@ class RMMResourceMixin { public: /** - * @brief Returns pointer to `device_memory_resource` that should be used for all tests inheriting - * from this fixture. + * @brief Returns pointer to `device_memory_resource` that should be used for + * all tests inheriting from this fixture * @return pointer to memory resource */ rmm::mr::device_memory_resource* mr() { return _mr; } @@ -54,8 +54,7 @@ class RMMResourceMixin { * class MyTestFixture : public cuspatial::test::BaseFixture {}; * ``` */ -class BaseFixture : public RMMResourceMixin, public ::testing::Test { -}; +class BaseFixture : public RMMResourceMixin, public ::testing::Test {}; /** * @brief Base test fixture class from which libcuspatial test with only value parameterization @@ -79,8 +78,13 @@ class BaseFixture : public RMMResourceMixin, public ::testing::Test { */ template class BaseFixtureWithParam : public RMMResourceMixin, - public ::testing::TestWithParam> { -}; + public ::testing::TestWithParam> {}; + +/** + * @brief Floating point types to be used in libcuspatial tests + * + */ +using FloatingPointTypes = ::testing::Types; } // namespace test } // namespace cuspatial diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 821de8bff..a5fa3c222 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -195,3 +195,7 @@ ConfigureTest(JOIN_POINT_IN_POLYGON_LARGE_TEST_EXP ConfigureTest(JOIN_POINT_TO_LINESTRING_SMALL_TEST_EXP experimental/join/quadtree_point_to_nearest_linestring_test_small.cu) + +ConfigureTest(RANGE_TEST_EXP + experimental/range/multilinestring_range_test.cu + experimental/range/multipolygon_range_test.cu) diff --git a/cpp/tests/experimental/range/multilinestring_range_test.cu b/cpp/tests/experimental/range/multilinestring_range_test.cu new file mode 100644 index 000000000..75a1c508a --- /dev/null +++ b/cpp/tests/experimental/range/multilinestring_range_test.cu @@ -0,0 +1,353 @@ +/* + * 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 + +#include + +using namespace cuspatial; +using namespace cuspatial::test; + +template +struct MultilinestringRangeTest : public BaseFixture { + void run_segment_test_single(std::initializer_list geometry_offset, + std::initializer_list part_offset, + std::initializer_list> coordinates, + std::initializer_list> expected) + { + auto multilinestring_array = + make_multilinestring_array(geometry_offset, part_offset, coordinates); + + auto rng = multilinestring_array.range(); + + rmm::device_uvector> got(rng.num_segments(), stream()); + thrust::copy(rmm::exec_policy(stream()), rng.segment_begin(), rng.segment_end(), got.begin()); + + auto d_expected = thrust::device_vector>(expected.begin(), expected.end()); + CUSPATIAL_EXPECT_VEC2D_PAIRS_EQUIVALENT(d_expected, got); + } + + void run_tiled_segments_test_single(std::initializer_list geometry_offset, + std::initializer_list part_offset, + std::initializer_list> coordinates, + std::size_t length, + std::initializer_list> expected) + { + auto multilinestring_array = + make_multilinestring_array(geometry_offset, part_offset, coordinates); + + auto rng = multilinestring_array.range(); + + rmm::device_uvector> got(length, stream()); + auto it = rng.segment_tiled_begin(); + thrust::copy(rmm::exec_policy(stream()), it, it + length, got.begin()); + + auto d_expected = thrust::device_vector>(expected.begin(), expected.end()); + CUSPATIAL_EXPECT_VEC2D_PAIRS_EQUIVALENT(d_expected, got); + } + + void run_per_multilinestring_point_count_test(std::initializer_list geometry_offset, + std::initializer_list part_offset, + std::initializer_list> coordinates, + std::initializer_list expected) + { + auto multilinestring_array = + make_multilinestring_array(geometry_offset, part_offset, coordinates); + + auto d_expected = thrust::device_vector(expected.begin(), expected.end()); + + auto rng = multilinestring_array.range(); + + rmm::device_uvector got(rng.num_multilinestrings(), stream()); + thrust::copy(rmm::exec_policy(stream()), + rng.per_multilinestring_point_count_begin(), + rng.per_multilinestring_point_count_end(), + got.begin()); + + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(d_expected, got); + } + + void run_multilinestring_segment_count_test(std::initializer_list geometry_offset, + std::initializer_list part_offset, + std::initializer_list> coordinates, + std::initializer_list expected) + { + auto multilinestring_array = + make_multilinestring_array(geometry_offset, part_offset, coordinates); + + auto d_expected = thrust::device_vector(expected.begin(), expected.end()); + + auto rng = multilinestring_array.range(); + + rmm::device_uvector got(rng.num_multilinestrings(), stream()); + thrust::copy(rmm::exec_policy(stream()), + rng.multilinestring_segment_count_begin(), + rng.multilinestring_segment_count_end(), + got.begin()); + + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(d_expected, got); + } +}; + +using TestTypes = ::testing::Types; + +TYPED_TEST_CASE(MultilinestringRangeTest, TestTypes); + +TYPED_TEST(MultilinestringRangeTest, SegmentIteratorOneSegmentTest) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST( + this->run_segment_test_single, {0, 1}, {0, 2}, {P{0, 0}, P{1, 1}}, {S{P{0, 0}, P{1, 1}}}); +} + +TYPED_TEST(MultilinestringRangeTest, SegmentIteratorTwoSegmentTest) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_segment_test_single, + {0, 2}, + {0, 2, 4}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{3, 3}}, + {S{P{0, 0}, P{1, 1}}, S{P{2, 2}, P{3, 3}}}); +} + +TYPED_TEST(MultilinestringRangeTest, SegmentIteratorTwoSegmentTest2) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_segment_test_single, + {0, 1, 2}, + {0, 2, 4}, + {P{0, 0}, P{1, 1}, P{0, 0}, P{1, 1}}, + {S{P{0, 0}, P{1, 1}}, S{P{0, 0}, P{1, 1}}}); +} + +TYPED_TEST(MultilinestringRangeTest, SegmentIteratorManyPairTest) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_segment_test_single, + {0, 1, 2, 3}, + {0, 6, 11, 14}, + {P{0, 0}, + P{1, 1}, + P{2, 2}, + P{3, 3}, + P{4, 4}, + P{5, 5}, + + P{10, 10}, + P{11, 11}, + P{12, 12}, + P{13, 13}, + P{14, 14}, + + P{20, 20}, + P{21, 21}, + P{22, 22}}, + + {S{P{0, 0}, P{1, 1}}, + S{P{1, 1}, P{2, 2}}, + S{P{2, 2}, P{3, 3}}, + S{P{3, 3}, P{4, 4}}, + S{P{4, 4}, P{5, 5}}, + S{P{10, 10}, P{11, 11}}, + S{P{11, 11}, P{12, 12}}, + S{P{12, 12}, P{13, 13}}, + S{P{13, 13}, P{14, 14}}, + S{P{20, 20}, P{21, 21}}, + S{P{21, 21}, P{22, 22}}}); +} + +TYPED_TEST(MultilinestringRangeTest, TiledSegmentIteratorTestOneLine) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_tiled_segments_test_single, + {0, 1}, + {0, 2}, + {P{0, 0}, P{1, 1}}, + 3, + {S{P{0, 0}, P{1, 1}}, S{P{0, 0}, P{1, 1}}, S{P{0, 0}, P{1, 1}}}); +} + +TYPED_TEST(MultilinestringRangeTest, TiledSegmentIteratorTestTwoLines) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_tiled_segments_test_single, + {0, 1, 2}, + {0, 2, 4}, + {P{0, 0}, P{1, 1}, P{10, 10}, P{11, 11}}, + 5, + { + S{P{0, 0}, P{1, 1}}, + S{P{10, 10}, P{11, 11}}, + S{P{0, 0}, P{1, 1}}, + S{P{10, 10}, P{11, 11}}, + S{P{0, 0}, P{1, 1}}, + }); +} + +TYPED_TEST(MultilinestringRangeTest, TiledSegmentIteratorTestThreeLines) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST( + this->run_tiled_segments_test_single, + {0, 1, 2, 3}, + {0, 2, 4, 8}, + {P{0, 0}, P{1, 1}, P{10, 10}, P{11, 11}, P{20, 20}, P{21, 21}, P{22, 22}, P{23, 23}}, + 10, + {S{P{0, 0}, P{1, 1}}, + S{P{10, 10}, P{11, 11}}, + S{P{20, 20}, P{21, 21}}, + S{P{21, 21}, P{22, 22}}, + S{P{22, 22}, P{23, 23}}, + S{P{0, 0}, P{1, 1}}, + S{P{10, 10}, P{11, 11}}, + S{P{20, 20}, P{21, 21}}, + S{P{21, 21}, P{22, 22}}, + S{P{22, 22}, P{23, 23}}}); +} + +/// FIXME: Currently, segment iterator doesn't handle empty linestrings. +TYPED_TEST(MultilinestringRangeTest, DISABLED_SegmentIteratorWithEmptyLineTest) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST( + this->run_segment_test_single, + {0, 1, 2, 3}, + {0, 3, 3, 6}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}, P{12, 12}}, + {S{P{0, 0}, P{1, 1}}, S{P{1, 1}, P{2, 2}}, S{P{10, 10}, P{11, 11}}, S{P{11, 11}, P{12, 12}}}); +} + +TYPED_TEST(MultilinestringRangeTest, PerMultilinestringCountTest) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_per_multilinestring_point_count_test, + {0, 1, 2, 3}, + {0, 3, 3, 6}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}, P{12, 12}}, + {3, 0, 3}); +} + +TYPED_TEST(MultilinestringRangeTest, PerMultilinestringCountTest2) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_per_multilinestring_point_count_test, + {0, 1, 3}, + {0, 3, 3, 6}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}, P{12, 12}}, + {3, 3}); +} + +TYPED_TEST(MultilinestringRangeTest, MultilinestringSegmentCountTest) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST( + this->run_multilinestring_segment_count_test, {0, 1}, {0, 3}, {P{0, 0}, P{1, 1}, P{2, 2}}, {2}); +} + +// FIXME: contains empty linestring +TYPED_TEST(MultilinestringRangeTest, DISABLED_MultilinestringSegmentCountTest2) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_multilinestring_segment_count_test, + {0, 1, 3}, + {0, 3, 3, 6}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}, P{12, 12}}, + {2, 2}); +} + +TYPED_TEST(MultilinestringRangeTest, MultilinestringSegmentCountTest3) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_multilinestring_segment_count_test, + {0, 1, 2}, + {0, 3, 6}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}, P{12, 12}}, + {2, 2}); +} + +TYPED_TEST(MultilinestringRangeTest, MultilinestringSegmentCountTest4) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_multilinestring_segment_count_test, + {0, 1, 3}, + {0, 3, 5, 7}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}, P{20, 20}, P{21, 21}}, + {2, 2}); +} + +// FIXME: contains empty linestring +TYPED_TEST(MultilinestringRangeTest, DISABLED_MultilinestringSegmentCountTest5) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_multilinestring_segment_count_test, + {0, 1, 2, 3}, + {0, 3, 3, 6}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}, P{12, 12}}, + {2, 0, 2}); +} diff --git a/cpp/tests/experimental/range/multipolygon_range_test.cu b/cpp/tests/experimental/range/multipolygon_range_test.cu new file mode 100644 index 000000000..7d8accdb0 --- /dev/null +++ b/cpp/tests/experimental/range/multipolygon_range_test.cu @@ -0,0 +1,338 @@ +/* + * 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 +#include +#include + +#include + +using namespace cuspatial; +using namespace cuspatial::test; + +template +struct MultipolygonRangeTest : public BaseFixture { + void run_multipolygon_segment_iterator_single(std::initializer_list geometry_offset, + std::initializer_list part_offset, + std::initializer_list ring_offset, + std::initializer_list> coordinates, + std::initializer_list> expected) + { + auto multipolygon_array = + make_multipolygon_array(geometry_offset, part_offset, ring_offset, coordinates); + auto rng = multipolygon_array.range(); + + auto got = rmm::device_uvector>(rng.num_segments(), stream()); + + thrust::copy(rmm::exec_policy(stream()), rng.segment_begin(), rng.segment_end(), got.begin()); + + auto d_expected = thrust::device_vector>(expected.begin(), expected.end()); + + CUSPATIAL_EXPECT_VEC2D_PAIRS_EQUIVALENT(got, d_expected); + } + + void run_multipolygon_point_count_iterator_single( + std::initializer_list geometry_offset, + std::initializer_list part_offset, + std::initializer_list ring_offset, + std::initializer_list> coordinates, + std::initializer_list expected_point_counts) + { + auto multipolygon_array = + make_multipolygon_array(geometry_offset, part_offset, ring_offset, coordinates); + auto rng = multipolygon_array.range(); + + auto got = rmm::device_uvector(rng.num_multipolygons(), stream()); + + thrust::copy(rmm::exec_policy(stream()), + rng.per_multipolygon_point_count_begin(), + rng.per_multipolygon_point_count_end(), + got.begin()); + + auto d_expected = thrust::device_vector(expected_point_counts.begin(), + expected_point_counts.end()); + + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(got, d_expected); + } + + void run_multipolygon_segment_count_single( + std::initializer_list geometry_offset, + std::initializer_list part_offset, + std::initializer_list ring_offset, + std::initializer_list> coordinates, + std::initializer_list expected_segment_counts) + { + auto multipolygon_array = + make_multipolygon_array(geometry_offset, part_offset, ring_offset, coordinates); + auto rng = multipolygon_array.range(); + + auto got = rmm::device_uvector(rng.num_multipolygons(), stream()); + + thrust::copy(rmm::exec_policy(stream()), + rng.multipolygon_segment_count_begin(), + rng.multipolygon_segment_count_end(), + got.begin()); + + auto d_expected = thrust::device_vector(expected_segment_counts.begin(), + expected_segment_counts.end()); + + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(got, d_expected); + } +}; + +TYPED_TEST_CASE(MultipolygonRangeTest, FloatingPointTypes); + +TYPED_TEST(MultipolygonRangeTest, SegmentIterators) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + CUSPATIAL_RUN_TEST(this->run_multipolygon_segment_iterator_single, + {0, 1}, + {0, 1}, + {0, 4}, + {{0, 0}, {1, 0}, {1, 1}, {0, 0}}, + {S{P{0, 0}, P{1, 0}}, S{P{1, 0}, P{1, 1}}, S{P{1, 1}, P{0, 0}}}); +} + +TYPED_TEST(MultipolygonRangeTest, SegmentIterators2) +{ + CUSPATIAL_RUN_TEST(this->run_multipolygon_segment_iterator_single, + {0, 1}, + {0, 2}, + {0, 4, 8}, + {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {10, 10}, {11, 10}, {11, 11}, {10, 10}}, + {{{0, 0}, {1, 0}}, + {{1, 0}, {1, 1}}, + {{1, 1}, {0, 0}}, + {{10, 10}, {11, 10}}, + {{11, 10}, {11, 11}}, + {{11, 11}, {10, 10}}}); +} + +TYPED_TEST(MultipolygonRangeTest, SegmentIterators3) +{ + CUSPATIAL_RUN_TEST(this->run_multipolygon_segment_iterator_single, + {0, 2}, + {0, 1, 2}, + {0, 4, 8}, + {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {10, 10}, {11, 10}, {11, 11}, {10, 10}}, + {{{0, 0}, {1, 0}}, + {{1, 0}, {1, 1}}, + {{1, 1}, {0, 0}}, + {{10, 10}, {11, 10}}, + {{11, 10}, {11, 11}}, + {{11, 11}, {10, 10}}}); +} + +TYPED_TEST(MultipolygonRangeTest, SegmentIterators4) +{ + CUSPATIAL_RUN_TEST(this->run_multipolygon_segment_iterator_single, + {0, 1, 2}, + {0, 1, 2}, + {0, 4, 8}, + {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {10, 10}, {11, 10}, {11, 11}, {10, 10}}, + {{{0, 0}, {1, 0}}, + {{1, 0}, {1, 1}}, + {{1, 1}, {0, 0}}, + {{10, 10}, {11, 10}}, + {{11, 10}, {11, 11}}, + {{11, 11}, {10, 10}}}); +} + +TYPED_TEST(MultipolygonRangeTest, PerMultipolygonCountIterator) +{ + CUSPATIAL_RUN_TEST(this->run_multipolygon_point_count_iterator_single, + {0, 1}, + {0, 1}, + {0, 4}, + {{0, 0}, {1, 0}, {1, 1}, {0, 1}}, + {4}); +} + +TYPED_TEST(MultipolygonRangeTest, PerMultipolygonCountIterator2) +{ + CUSPATIAL_RUN_TEST( + this->run_multipolygon_point_count_iterator_single, + {0, 1}, + {0, 2}, + {0, 4, 8}, + {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0.2, 0.2}, {0.2, 0.3}, {0.3, 0.3}, {0.3, 0.2}}, + {8}); +} + +TYPED_TEST(MultipolygonRangeTest, PerMultipolygonCountIterator3) +{ + CUSPATIAL_RUN_TEST(this->run_multipolygon_point_count_iterator_single, + {0, 2}, + {0, 2, 3}, + {0, 4, 8, 12}, + {{0, 0}, + {1, 0}, + {1, 1}, + {0, 1}, + {0.2, 0.2}, + {0.2, 0.3}, + {0.3, 0.3}, + {0.3, 0.2}, + {0, 0}, + {1, 0}, + {1, 1}, + {0, 1}}, + {12}); +} + +TYPED_TEST(MultipolygonRangeTest, PerMultipolygonCountIterator4) +{ + CUSPATIAL_RUN_TEST(this->run_multipolygon_point_count_iterator_single, + {0, 2, 3}, + {0, 2, 3, 4}, + {0, 4, 8, 12, 16}, + {{0, 0}, + {1, 0}, + {1, 1}, + {0, 1}, + {0.2, 0.2}, + {0.2, 0.3}, + {0.3, 0.3}, + {0.3, 0.2}, + {0, 0}, + {1, 0}, + {1, 1}, + {0, 1}, + {0, 0}, + {1, 0}, + {1, 1}, + {0, 1}}, + {12, 4}); +} + +TYPED_TEST(MultipolygonRangeTest, MultipolygonSegmentCount) +{ + CUSPATIAL_RUN_TEST(this->run_multipolygon_segment_count_single, + {0, 1}, + {0, 1}, + {0, 4}, + {{0, 0}, {1, 0}, {1, 1}, {0, 1}}, + {3}); +} + +TYPED_TEST(MultipolygonRangeTest, MultipolygonSegmentCount2) +{ + CUSPATIAL_RUN_TEST( + this->run_multipolygon_segment_count_single, + {0, 1}, + {0, 2}, + {0, 4, 8}, + {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0.2, 0.2}, {0.2, 0.3}, {0.3, 0.3}, {0.3, 0.2}}, + {6}); +} + +TYPED_TEST(MultipolygonRangeTest, MultipolygonSegmentCount3) +{ + CUSPATIAL_RUN_TEST(this->run_multipolygon_segment_count_single, + {0, 2}, + {0, 2, 3}, + {0, 4, 8, 12}, + {{0, 0}, + {1, 0}, + {1, 1}, + {0, 1}, + {0.2, 0.2}, + {0.2, 0.3}, + {0.3, 0.3}, + {0.3, 0.2}, + {0, 0}, + {1, 0}, + {1, 1}, + {0, 1}}, + {9}); +} + +TYPED_TEST(MultipolygonRangeTest, MultipolygonSegmentCount4) +{ + CUSPATIAL_RUN_TEST(this->run_multipolygon_segment_count_single, + {0, 2, 3}, + {0, 2, 3, 4}, + {0, 4, 8, 12, 16}, + {{0, 0}, + {1, 0}, + {1, 1}, + {0, 1}, + {0.2, 0.2}, + {0.2, 0.3}, + {0.3, 0.3}, + {0.3, 0.2}, + {0, 0}, + {1, 0}, + {1, 1}, + {0, 1}, + {0, 0}, + {1, 0}, + {1, 1}, + {0, 1}}, + {9, 3}); +} + +// TYPED_TEST(MultipolygonRangeTest, MultipolygonSegmentCount_ConatainsEmptyRing) +// { +// CUSPATIAL_RUN_TEST(this->run_multipolygon_segment_count_single, +// {0, 2, 3}, +// {0, 2, 3, 4}, +// {0, 4, 4, 8, 12}, +// {{0, 0}, +// {1, 0}, +// {1, 1}, +// {0, 1}, +// {0.2, 0.2}, +// {0.2, 0.3}, +// {0.3, 0.3}, +// {0.3, 0.2}, +// {0, 0}, +// {1, 0}, +// {1, 1}, +// {0, 1}}, +// {6, 3}); +// } + +// TYPED_TEST(MultipolygonRangeTest, MultipolygonSegmentCount_ConatainsEmptyPart) +// { +// CUSPATIAL_RUN_TEST(this->run_multipolygon_segment_count_single, +// {0, 3, 4}, +// {0, 2, 2, 3, 4}, +// {0, 4, 8, 12}, +// {{0, 0}, +// {1, 0}, +// {1, 1}, +// {0, 1}, +// {0.2, 0.2}, +// {0.2, 0.3}, +// {0.3, 0.3}, +// {0.3, 0.2}, +// {0, 0}, +// {1, 0}, +// {1, 1}, +// {0, 1}}, +// {6, 3}); +// } From e8e45d0724cb3f52287f7ba2582f22b2a876d6a0 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 31 Mar 2023 16:26:23 -0700 Subject: [PATCH 08/54] Apply suggestions from code review --- .../detail/ranges/multipolygon_range.cuh | 21 ------------------- .../ranges/multilinestring_range.cuh | 2 +- .../ranges/multipolygon_range.cuh | 11 ---------- 3 files changed, 1 insertion(+), 33 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh index 1846c8213..6ef4db52c 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh @@ -205,27 +205,6 @@ multipolygon_range::p return _point_end; } -// template -// CUSPATIAL_HOST_DEVICE auto -// multipolygon_range::polygon_begin() -// { -// return detail::make_counting_transform_iterator( -// 0, to_polygon_functor{_part_begin, _ring_begin, point_begin, point_end}); -// } - -// template -// CUSPATIAL_HOST_DEVICE auto -// multipolygon_range::polygon_end() -// { -// return polygon_begin() + num_polygons(); -// } - template - // CUSPATIAL_HOST_DEVICE auto polygon_wraparound_repeated_begin(IndexType repeats); - - // template - // CUSPATIAL_HOST_DEVICE auto segment_wraparound_repeated_begin() protected: GeometryIterator _geometry_begin; From fadf5c8fee981109a466a702a3f2dafada250df6 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 31 Mar 2023 16:27:18 -0700 Subject: [PATCH 09/54] Update cpp/include/cuspatial/experimental/detail/functors.cuh --- .../experimental/detail/functors.cuh | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/functors.cuh b/cpp/include/cuspatial/experimental/detail/functors.cuh index 4d7b083e3..7095b4a15 100644 --- a/cpp/include/cuspatial/experimental/detail/functors.cuh +++ b/cpp/include/cuspatial/experimental/detail/functors.cuh @@ -69,33 +69,6 @@ template to_valid_segment_functor(OffsetIterator, OffsetIterator, CoordinateIterator) -> to_valid_segment_functor; -template -struct wraparound_functor { - IndexType length; - - template - CUSPATIAL_HOST_DEVICE auto operator()(IndexType2 i) - { - return i % length; - } -}; - -template -wraparound_functor(IndexType) -> wraparound_functor; - -// template -// struct repeat_functor { -// IndexType repeats; - -// template -// CUSPATIAL_HOST_DEVICE auto operator()(IndexType2 i) -// { -// return i / repeats; -// } -// }; - -// template -// wraparound_functor(IndexType) -> wraparound_functor; } // namespace detail } // namespace cuspatial From fe315422e5c0209861228fcd927d5d467fedbfd4 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 31 Mar 2023 16:36:03 -0700 Subject: [PATCH 10/54] address review comments --- .../detail/ranges/multilinestring_range.cuh | 17 +--- .../detail/ranges/multipolygon_range.cuh | 10 +-- .../ranges/multilinestring_range.cuh | 15 +--- .../ranges/multipolygon_range.cuh | 11 +-- .../range/multilinestring_range_test.cu | 77 ------------------- 5 files changed, 13 insertions(+), 117 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh index 78f0d71ad..d12046eb4 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh @@ -210,7 +210,7 @@ multilinestring_range::segment(Inde template CUSPATIAL_HOST_DEVICE auto multilinestring_range:: - per_multilinestring_point_count_begin() + multilinestring_point_count_begin() { auto multilinestring_offset_it = thrust::make_permutation_iterator(_part_begin, _geometry_begin); auto paired_it = @@ -220,9 +220,9 @@ CUSPATIAL_HOST_DEVICE auto multilinestring_range CUSPATIAL_HOST_DEVICE auto multilinestring_range:: - per_multilinestring_point_count_end() + multilinestring_point_count_end() { - return per_multilinestring_point_count_begin() + num_multilinestrings(); + return multilinestring_point_count_begin() + num_multilinestrings(); } template @@ -230,7 +230,7 @@ CUSPATIAL_HOST_DEVICE auto multilinestring_range::subtracted_p return subtracted_part_begin() + thrust::distance(_part_begin, _part_end); } -template -CUSPATIAL_HOST_DEVICE auto -multilinestring_range::segment_tiled_begin() -{ - auto tiled_it = - detail::make_counting_transform_iterator(0, detail::wraparound_functor{num_segments()}); - return thrust::make_permutation_iterator(segment_begin(), tiled_it); -} - template CUSPATIAL_HOST_DEVICE auto multilinestring_range::as_multipoint_range() diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh index 6ef4db52c..432bc9768 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh @@ -268,7 +268,7 @@ template CUSPATIAL_HOST_DEVICE auto multipolygon_range:: - per_multipolygon_point_count_begin() + multipolygon_point_count_begin() { auto multipolygon_point_offset_it = thrust::make_permutation_iterator( _ring_begin, thrust::make_permutation_iterator(_part_begin, _geometry_begin)); @@ -286,9 +286,9 @@ template CUSPATIAL_HOST_DEVICE auto multipolygon_range:: - per_multipolygon_point_count_end() + multipolygon_point_count_end() { - return per_multipolygon_point_count_begin() + num_multipolygons(); + return multipolygon_point_count_begin() + num_multipolygons(); } template :: multipolygon_segment_count_begin() { - auto multipolygon_point_ring_count_it = thrust::make_zip_iterator( - per_multipolygon_point_count_begin(), multipolygon_ring_count_begin()); + auto multipolygon_point_ring_count_it = + thrust::make_zip_iterator(multipolygon_point_count_begin(), multipolygon_ring_count_begin()); return thrust::make_transform_iterator(multipolygon_point_ring_count_it, detail::point_count_to_segment_count_functor{}); diff --git a/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh b/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh index 6fff8d532..7a72a5298 100644 --- a/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh @@ -146,10 +146,10 @@ class multilinestring_range { IndexType segment_idx); /// Returns an iterator to the counts of points per multilinestring - CUSPATIAL_HOST_DEVICE auto per_multilinestring_point_count_begin(); + CUSPATIAL_HOST_DEVICE auto multilinestring_point_count_begin(); /// Returns an iterator to the counts of points per multilinestring - CUSPATIAL_HOST_DEVICE auto per_multilinestring_point_count_end(); + CUSPATIAL_HOST_DEVICE auto multilinestring_point_count_end(); /// Returns an iterator to the counts of points per multilinestring CUSPATIAL_HOST_DEVICE auto multilinestring_segment_count_begin(); @@ -169,17 +169,6 @@ class multilinestring_range { /// Returns an iterator to the end of the segment CUSPATIAL_HOST_DEVICE auto segment_end(); - /// Infinite iterators - /// Note: Infinite iterators currently doesn't work with ranges that contains empty geometry. - - /// Returns an infinite iterator to the "tiled" segments of the multilinestring. - /// If the multilinestring range has 5 segments, this iterator will iterate on the - /// 0th, 1st, 2nd, 3rd, 4th, 0th, 1st, 2nd, 3rd, 4th segment for the first 10 iterations. - /// - /// The name of `tile` comes from numpy.tile[1] - /// [1] https://numpy.org/doc/stable/reference/generated/numpy.tile.html - CUSPATIAL_HOST_DEVICE auto segment_tiled_begin(); - /// Returns the `multilinestring_idx`th multilinestring in the range. template CUSPATIAL_HOST_DEVICE auto operator[](IndexType multilinestring_idx); diff --git a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh index 24160925b..5daa5ad9f 100644 --- a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh @@ -123,12 +123,6 @@ class multipolygon_range { /// Return the iterator to the one past the last ring offset in the range. CUSPATIAL_HOST_DEVICE auto ring_offset_end() { return _ring_end; } - // /// Return the iterator to the first polygon in the range. - // CUSPATIAL_HOST_DEVICE auto polygon_begin(); - - // /// Return the iterator to the one past the last polygon in the range. - // CUSPATIAL_HOST_DEVICE auto polygon_end(); - /// 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 @@ -164,9 +158,9 @@ class multipolygon_range { IndexType2 geometry_idx); /// Returns an iterator to the number of points of the first multipolygon - CUSPATIAL_HOST_DEVICE auto per_multipolygon_point_count_begin(); + CUSPATIAL_HOST_DEVICE auto multipolygon_point_count_begin(); /// Returns the one past the iterator to the number of points of the last multipolygon - CUSPATIAL_HOST_DEVICE auto per_multipolygon_point_count_end(); + CUSPATIAL_HOST_DEVICE auto multipolygon_point_count_end(); /// Returns an iterator to the number of rings of the first multipolygon CUSPATIAL_HOST_DEVICE auto multipolygon_ring_count_begin(); @@ -184,7 +178,6 @@ class multipolygon_range { /// Returns an iterator to the end of the segment CUSPATIAL_HOST_DEVICE auto segment_end(); - protected: GeometryIterator _geometry_begin; GeometryIterator _geometry_end; diff --git a/cpp/tests/experimental/range/multilinestring_range_test.cu b/cpp/tests/experimental/range/multilinestring_range_test.cu index 75a1c508a..4230013f6 100644 --- a/cpp/tests/experimental/range/multilinestring_range_test.cu +++ b/cpp/tests/experimental/range/multilinestring_range_test.cu @@ -48,25 +48,6 @@ struct MultilinestringRangeTest : public BaseFixture { CUSPATIAL_EXPECT_VEC2D_PAIRS_EQUIVALENT(d_expected, got); } - void run_tiled_segments_test_single(std::initializer_list geometry_offset, - std::initializer_list part_offset, - std::initializer_list> coordinates, - std::size_t length, - std::initializer_list> expected) - { - auto multilinestring_array = - make_multilinestring_array(geometry_offset, part_offset, coordinates); - - auto rng = multilinestring_array.range(); - - rmm::device_uvector> got(length, stream()); - auto it = rng.segment_tiled_begin(); - thrust::copy(rmm::exec_policy(stream()), it, it + length, got.begin()); - - auto d_expected = thrust::device_vector>(expected.begin(), expected.end()); - CUSPATIAL_EXPECT_VEC2D_PAIRS_EQUIVALENT(d_expected, got); - } - void run_per_multilinestring_point_count_test(std::initializer_list geometry_offset, std::initializer_list part_offset, std::initializer_list> coordinates, @@ -189,64 +170,6 @@ TYPED_TEST(MultilinestringRangeTest, SegmentIteratorManyPairTest) S{P{21, 21}, P{22, 22}}}); } -TYPED_TEST(MultilinestringRangeTest, TiledSegmentIteratorTestOneLine) -{ - using T = TypeParam; - using P = vec_2d; - using S = segment; - - CUSPATIAL_RUN_TEST(this->run_tiled_segments_test_single, - {0, 1}, - {0, 2}, - {P{0, 0}, P{1, 1}}, - 3, - {S{P{0, 0}, P{1, 1}}, S{P{0, 0}, P{1, 1}}, S{P{0, 0}, P{1, 1}}}); -} - -TYPED_TEST(MultilinestringRangeTest, TiledSegmentIteratorTestTwoLines) -{ - using T = TypeParam; - using P = vec_2d; - using S = segment; - - CUSPATIAL_RUN_TEST(this->run_tiled_segments_test_single, - {0, 1, 2}, - {0, 2, 4}, - {P{0, 0}, P{1, 1}, P{10, 10}, P{11, 11}}, - 5, - { - S{P{0, 0}, P{1, 1}}, - S{P{10, 10}, P{11, 11}}, - S{P{0, 0}, P{1, 1}}, - S{P{10, 10}, P{11, 11}}, - S{P{0, 0}, P{1, 1}}, - }); -} - -TYPED_TEST(MultilinestringRangeTest, TiledSegmentIteratorTestThreeLines) -{ - using T = TypeParam; - using P = vec_2d; - using S = segment; - - CUSPATIAL_RUN_TEST( - this->run_tiled_segments_test_single, - {0, 1, 2, 3}, - {0, 2, 4, 8}, - {P{0, 0}, P{1, 1}, P{10, 10}, P{11, 11}, P{20, 20}, P{21, 21}, P{22, 22}, P{23, 23}}, - 10, - {S{P{0, 0}, P{1, 1}}, - S{P{10, 10}, P{11, 11}}, - S{P{20, 20}, P{21, 21}}, - S{P{21, 21}, P{22, 22}}, - S{P{22, 22}, P{23, 23}}, - S{P{0, 0}, P{1, 1}}, - S{P{10, 10}, P{11, 11}}, - S{P{20, 20}, P{21, 21}}, - S{P{21, 21}, P{22, 22}}, - S{P{22, 22}, P{23, 23}}}); -} - /// FIXME: Currently, segment iterator doesn't handle empty linestrings. TYPED_TEST(MultilinestringRangeTest, DISABLED_SegmentIteratorWithEmptyLineTest) { From af0c35e5b251d4cc67c0e87fac90993aa54661e0 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 31 Mar 2023 16:38:21 -0700 Subject: [PATCH 11/54] address review comments --- .../range/multipolygon_range_test.cu | 84 ++++++++++--------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/cpp/tests/experimental/range/multipolygon_range_test.cu b/cpp/tests/experimental/range/multipolygon_range_test.cu index 7d8accdb0..582d3490e 100644 --- a/cpp/tests/experimental/range/multipolygon_range_test.cu +++ b/cpp/tests/experimental/range/multipolygon_range_test.cu @@ -295,44 +295,46 @@ TYPED_TEST(MultipolygonRangeTest, MultipolygonSegmentCount4) {9, 3}); } -// TYPED_TEST(MultipolygonRangeTest, MultipolygonSegmentCount_ConatainsEmptyRing) -// { -// CUSPATIAL_RUN_TEST(this->run_multipolygon_segment_count_single, -// {0, 2, 3}, -// {0, 2, 3, 4}, -// {0, 4, 4, 8, 12}, -// {{0, 0}, -// {1, 0}, -// {1, 1}, -// {0, 1}, -// {0.2, 0.2}, -// {0.2, 0.3}, -// {0.3, 0.3}, -// {0.3, 0.2}, -// {0, 0}, -// {1, 0}, -// {1, 1}, -// {0, 1}}, -// {6, 3}); -// } - -// TYPED_TEST(MultipolygonRangeTest, MultipolygonSegmentCount_ConatainsEmptyPart) -// { -// CUSPATIAL_RUN_TEST(this->run_multipolygon_segment_count_single, -// {0, 3, 4}, -// {0, 2, 2, 3, 4}, -// {0, 4, 8, 12}, -// {{0, 0}, -// {1, 0}, -// {1, 1}, -// {0, 1}, -// {0.2, 0.2}, -// {0.2, 0.3}, -// {0.3, 0.3}, -// {0.3, 0.2}, -// {0, 0}, -// {1, 0}, -// {1, 1}, -// {0, 1}}, -// {6, 3}); -// } +// FIXME: multipolygon doesn't constructor doesn't allow empty rings, should it? +TYPED_TEST(MultipolygonRangeTest, DISABLED_MultipolygonSegmentCount_ConatainsEmptyRing) +{ + CUSPATIAL_RUN_TEST(this->run_multipolygon_segment_count_single, + {0, 2, 3}, + {0, 2, 3, 4}, + {0, 4, 4, 8, 12}, + {{0, 0}, + {1, 0}, + {1, 1}, + {0, 1}, + {0.2, 0.2}, + {0.2, 0.3}, + {0.3, 0.3}, + {0.3, 0.2}, + {0, 0}, + {1, 0}, + {1, 1}, + {0, 1}}, + {6, 3}); +} + +// FIXME: multipolygon doesn't constructor doesn't allow empty rings, should it? +TYPED_TEST(MultipolygonRangeTest, DISABLED_MultipolygonSegmentCount_ConatainsEmptyPart) +{ + CUSPATIAL_RUN_TEST(this->run_multipolygon_segment_count_single, + {0, 3, 4}, + {0, 2, 2, 3, 4}, + {0, 4, 8, 12}, + {{0, 0}, + {1, 0}, + {1, 1}, + {0, 1}, + {0.2, 0.2}, + {0.2, 0.3}, + {0.3, 0.3}, + {0.3, 0.2}, + {0, 0}, + {1, 0}, + {1, 1}, + {0, 1}}, + {6, 3}); +} From 05bf7ff26f432ce70916cc665c064b7a880882a4 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 31 Mar 2023 16:40:05 -0700 Subject: [PATCH 12/54] [skip-ci] address review coments --- .../experimental/detail/ranges/multilinestring_range.cuh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh index d12046eb4..ec615c6c8 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh @@ -16,10 +16,10 @@ #pragma once -#include "thrust/iterator/zip_iterator.h" #include #include #include +#include #include #include From 24351264f99a8bc329dbd806227567095d481247 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 31 Mar 2023 16:55:26 -0700 Subject: [PATCH 13/54] Add docstrings for functors --- .../experimental/detail/functors.cuh | 53 ++++++++++++++++--- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/functors.cuh b/cpp/include/cuspatial/experimental/detail/functors.cuh index 7095b4a15..f28f02e66 100644 --- a/cpp/include/cuspatial/experimental/detail/functors.cuh +++ b/cpp/include/cuspatial/experimental/detail/functors.cuh @@ -10,16 +10,39 @@ namespace cuspatial { namespace detail { +/** + * @brief Given iterator a pair of offsets, return the number of elements between the offsets. + * + * Example: + * pair of offsets: (0, 3), (3, 5), (5, 8) + * number of elements between offsets: 3, 2, 3 + * + * @tparam OffsetPairIterator Must be iterator type to thrust::pair of indices. + * @param p Iterator of thrust::pair of indices. + */ struct offset_pair_to_count_functor { template CUSPATIAL_HOST_DEVICE auto operator()(OffsetPairIterator p) { - auto first = thrust::get<0>(p); - auto second = thrust::get<1>(p); - return second - first; + return thrust::get<1>(p) - thrust::get<0>(p); } }; +/** + * @brief Convert counts of points to counts of segments. + * + * A Multilinestring is composed of a series of Linestrings. Each Linestring is composed of a + * segments. The number of segments in a multilinestring is the number of points in the + * multilinestring subtracting the number of linestrings. + * + * Caveats: This has a strong assumption that the Multilinestring does not contain empty linestring. + * While each non-empty linestring in the multilinestring can cause 1 invalid segment, an empty + * multilinestring not introduce and invalid segments since it does not contain any points. + * + * @tparam IndexPair Must be iterator to a pair of counts + * @param n_point_linestring_pair A pair of counts, the first is the number of points, the second is + * the number of linestrings. + */ struct point_count_to_segment_count_functor { template CUSPATIAL_HOST_DEVICE auto operator()(IndexPair n_point_linestring_pair) @@ -30,6 +53,15 @@ struct point_count_to_segment_count_functor { } }; +/** + * @brief Given an iterator of offsets, return an iterator of offsets subtracted by the index. + * + * @tparam OffsetIterator Iterator type to the offset + * + * Caveats: This has a strong assumption that the Multilinestring does not contain empty linestring. + * While each non-empty linestring in the multilinestring can cause 1 invalid segment, an empty + * multilinestring not introduce and invalid segments since it does not contain any points. + */ template struct to_subtracted_by_index_iterator { OffsetIterator begin; @@ -37,14 +69,24 @@ struct to_subtracted_by_index_iterator { template CUSPATIAL_HOST_DEVICE auto operator()(IndexType i) { - // printf("begin[i]: %d i: %d\n", static_cast(begin[i]), static_cast(i)); return begin[i] - i; } }; +/// Deduction guide for to_subtracted_by_index_iterator template to_subtracted_by_index_iterator(OffsetIterator) -> to_subtracted_by_index_iterator; +/** + * @brief Return a segment from the a partitioned range of points + * + * Used in a counting transform iterator. Given an index of the segment, offset it by the number of + * skipped segments preceding i in the partitioned range of points. Dereference the corresponding + * point and the point following to make a segment. + * + * @tparam OffsetIterator the iterator type indicating partitions of the point range. + * @tparam CoordinateIterator the iterator type to the point range. + */ template struct to_valid_segment_functor { using element_t = iterator_vec_base_type; @@ -60,15 +102,14 @@ struct to_valid_segment_functor { auto k = thrust::distance(begin, kit); auto pid = i + k - 1; - // printf("%d %d %d\n", static_cast(i), static_cast(k), static_cast(pid)); return segment{point_begin[pid], point_begin[pid + 1]}; } }; +/// Deduction guide for to_valid_segment_functor template to_valid_segment_functor(OffsetIterator, OffsetIterator, CoordinateIterator) -> to_valid_segment_functor; - } // namespace detail } // namespace cuspatial From 98a4a0f762a16a67de24766d873633d5f049e099 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 31 Mar 2023 17:10:22 -0700 Subject: [PATCH 14/54] [skip-ci] adds linestring_count test --- .../ranges/multilinestring_range.cuh | 4 +- .../range/multilinestring_range_test.cu | 65 ++++++++++++++++--- .../range/multipolygon_range_test.cu | 4 +- 3 files changed, 61 insertions(+), 12 deletions(-) diff --git a/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh b/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh index 7a72a5298..cf3cee562 100644 --- a/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh @@ -148,10 +148,10 @@ class multilinestring_range { /// Returns an iterator to the counts of points per multilinestring CUSPATIAL_HOST_DEVICE auto multilinestring_point_count_begin(); - /// Returns an iterator to the counts of points per multilinestring + /// Returns an iterator to the counts of segments per multilinestring CUSPATIAL_HOST_DEVICE auto multilinestring_point_count_end(); - /// Returns an iterator to the counts of points per multilinestring + /// Returns an iterator to the counts of segments per multilinestring CUSPATIAL_HOST_DEVICE auto multilinestring_segment_count_begin(); /// Returns an iterator to the counts of points per multilinestring diff --git a/cpp/tests/experimental/range/multilinestring_range_test.cu b/cpp/tests/experimental/range/multilinestring_range_test.cu index 4230013f6..2c11dfc3e 100644 --- a/cpp/tests/experimental/range/multilinestring_range_test.cu +++ b/cpp/tests/experimental/range/multilinestring_range_test.cu @@ -48,10 +48,10 @@ struct MultilinestringRangeTest : public BaseFixture { CUSPATIAL_EXPECT_VEC2D_PAIRS_EQUIVALENT(d_expected, got); } - void run_per_multilinestring_point_count_test(std::initializer_list geometry_offset, - std::initializer_list part_offset, - std::initializer_list> coordinates, - std::initializer_list expected) + void run_multilinestring_point_count_test(std::initializer_list geometry_offset, + std::initializer_list part_offset, + std::initializer_list> coordinates, + std::initializer_list expected) { auto multilinestring_array = make_multilinestring_array(geometry_offset, part_offset, coordinates); @@ -62,8 +62,8 @@ struct MultilinestringRangeTest : public BaseFixture { rmm::device_uvector got(rng.num_multilinestrings(), stream()); thrust::copy(rmm::exec_policy(stream()), - rng.per_multilinestring_point_count_begin(), - rng.per_multilinestring_point_count_end(), + rng.multilinestring_point_count_begin(), + rng.multilinestring_point_count_end(), got.begin()); CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(d_expected, got); @@ -89,6 +89,27 @@ struct MultilinestringRangeTest : public BaseFixture { CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(d_expected, got); } + + void run_multilinestring_linestring_count_test(std::initializer_list geometry_offset, + std::initializer_list part_offset, + std::initializer_list> coordinates, + std::initializer_list expected) + { + auto multilinestring_array = + make_multilinestring_array(geometry_offset, part_offset, coordinates); + + auto d_expected = thrust::device_vector(expected.begin(), expected.end()); + + auto rng = multilinestring_array.range(); + + rmm::device_uvector got(rng.num_multilinestrings(), stream()); + thrust::copy(rmm::exec_policy(stream()), + rng.multilinestring_linestring_count_begin(), + rng.multilinestring_linestring_count_end(), + got.begin()); + + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(d_expected, got); + } }; using TestTypes = ::testing::Types; @@ -191,7 +212,7 @@ TYPED_TEST(MultilinestringRangeTest, PerMultilinestringCountTest) using P = vec_2d; using S = segment; - CUSPATIAL_RUN_TEST(this->run_per_multilinestring_point_count_test, + CUSPATIAL_RUN_TEST(this->run_multilinestring_point_count_test, {0, 1, 2, 3}, {0, 3, 3, 6}, {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}, P{12, 12}}, @@ -204,7 +225,7 @@ TYPED_TEST(MultilinestringRangeTest, PerMultilinestringCountTest2) using P = vec_2d; using S = segment; - CUSPATIAL_RUN_TEST(this->run_per_multilinestring_point_count_test, + CUSPATIAL_RUN_TEST(this->run_multilinestring_point_count_test, {0, 1, 3}, {0, 3, 3, 6}, {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}, P{12, 12}}, @@ -274,3 +295,31 @@ TYPED_TEST(MultilinestringRangeTest, DISABLED_MultilinestringSegmentCountTest5) {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}, P{12, 12}}, {2, 0, 2}); } + +TYPED_TEST(MultilinestringRangeTest, MultilinestringLinestringCountTest) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST( + this->run_multilinestring_linestring_count_test, + {0, 1, 2, 3}, + {0, 3, 6, 9}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}, P{12, 12}, P{20, 20}, P{21, 21}, P{22, 22}}, + {1, 1, 1}); +} + +TYPED_TEST(MultilinestringRangeTest, MultilinestringLinestringCountTest2) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST( + this->run_multilinestring_linestring_count_test, + {0, 1, 3}, + {0, 3, 6, 9}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}, P{12, 12}, P{20, 20}, P{21, 21}, P{22, 22}}, + {1, 2}); +} diff --git a/cpp/tests/experimental/range/multipolygon_range_test.cu b/cpp/tests/experimental/range/multipolygon_range_test.cu index 582d3490e..c4af51a03 100644 --- a/cpp/tests/experimental/range/multipolygon_range_test.cu +++ b/cpp/tests/experimental/range/multipolygon_range_test.cu @@ -66,8 +66,8 @@ struct MultipolygonRangeTest : public BaseFixture { auto got = rmm::device_uvector(rng.num_multipolygons(), stream()); thrust::copy(rmm::exec_policy(stream()), - rng.per_multipolygon_point_count_begin(), - rng.per_multipolygon_point_count_end(), + rng.multipolygon_point_count_begin(), + rng.multipolygon_point_count_end(), got.begin()); auto d_expected = thrust::device_vector(expected_point_counts.begin(), From 9b727235d1541bd2bd60441b6afa0f6a5506e0f2 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 31 Mar 2023 17:12:33 -0700 Subject: [PATCH 15/54] [skip-ci] revert removed code --- .../experimental/detail/ranges/multilinestring_range.cuh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh index ec615c6c8..0cea9eb16 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh @@ -86,6 +86,11 @@ multilinestring_range::multilinestr _point_begin(point_begin), _point_end(point_end) { + static_assert(is_vec_2d>, + "point_begin and point_end must be iterators to floating point vec_2d types."); + + CUSPATIAL_EXPECTS_VALID_MULTILINESTRING_SIZES( + num_points(), num_multilinestrings() + 1, num_linestrings() + 1); } template From 6971200dc1148eafb530c792f104448b6bde5da7 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 31 Mar 2023 17:13:52 -0700 Subject: [PATCH 16/54] style --- cpp/include/cuspatial_test/base_fixture.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cpp/include/cuspatial_test/base_fixture.hpp b/cpp/include/cuspatial_test/base_fixture.hpp index 52635c34d..ae92c9fd3 100644 --- a/cpp/include/cuspatial_test/base_fixture.hpp +++ b/cpp/include/cuspatial_test/base_fixture.hpp @@ -54,7 +54,8 @@ class RMMResourceMixin { * class MyTestFixture : public cuspatial::test::BaseFixture {}; * ``` */ -class BaseFixture : public RMMResourceMixin, public ::testing::Test {}; +class BaseFixture : public RMMResourceMixin, public ::testing::Test { +}; /** * @brief Base test fixture class from which libcuspatial test with only value parameterization @@ -78,7 +79,8 @@ class BaseFixture : public RMMResourceMixin, public ::testing::Test {}; */ template class BaseFixtureWithParam : public RMMResourceMixin, - public ::testing::TestWithParam> {}; + public ::testing::TestWithParam> { +}; /** * @brief Floating point types to be used in libcuspatial tests From 6f7a226fd564ca884fe6126124cfcd48d52ece7b Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 31 Mar 2023 17:17:47 -0700 Subject: [PATCH 17/54] update unexpected changes --- .../cuspatial/experimental/detail/ranges/multipolygon_range.cuh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh index 432bc9768..455d4e995 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh @@ -106,7 +106,7 @@ multipolygon_range::m _point_end(point_end) { static_assert(is_vec_2d>, - "Coordinate range must be constructed with iterators to vec_2d."); + "point_begin and point_end must be iterators to floating point vec_2d types."); CUSPATIAL_EXPECTS_VALID_MULTIPOLYGON_SIZES( num_points(), num_multipolygons() + 1, num_polygons() + 1, num_rings() + 1); From 641934f9de72cb7155486494806d45a3cd2bec68 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 31 Mar 2023 17:18:28 -0700 Subject: [PATCH 18/54] license --- .../cuspatial/experimental/detail/functors.cuh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cpp/include/cuspatial/experimental/detail/functors.cuh b/cpp/include/cuspatial/experimental/detail/functors.cuh index f28f02e66..6cc0ab35e 100644 --- a/cpp/include/cuspatial/experimental/detail/functors.cuh +++ b/cpp/include/cuspatial/experimental/detail/functors.cuh @@ -1,3 +1,18 @@ +/* + * 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 From 890e95bab387547cc247b428efe1492845933783 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 31 Mar 2023 18:28:21 -0700 Subject: [PATCH 19/54] Update cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh --- .../experimental/detail/ranges/multilinestring_range.cuh | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh index 0cea9eb16..152f9c1ae 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh @@ -24,6 +24,7 @@ #include #include +#include #include #include #include From 040e432c9c2258da130542478526818116b0af7d Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Sat, 1 Apr 2023 19:43:29 -0700 Subject: [PATCH 20/54] making a bit of progress --- .../detail/linestring_polygon_distance.cuh | 53 +++- .../detail/ranges/multilinestring_range.cuh | 4 +- .../linestring_polygon_distance_test.cu | 251 +++++++++++++++++- 3 files changed, 294 insertions(+), 14 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh b/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh index f5f51741b..594fe5632 100644 --- a/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -38,6 +39,8 @@ #include #include +#include + namespace cuspatial { namespace detail { @@ -64,9 +67,28 @@ struct point_in_multipolygon_test_functor { 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); + + int i = 0; + bool intersects = thrust::any_of( + thrust::seq, + polys.begin(), + polys.end(), + [&point, &i, &pidx, &geometry_idx] __device__(auto poly) { + vec_2d first_point = poly.point_begin()[0]; + printf( + "pidx: %d, geometry_idx: %d, (%f, %f) in poly %d (first point: (%f %f), size: %d)?\n", + static_cast(pidx), + static_cast(geometry_idx), + point.x, + point.y, + i, + first_point.x, + first_point.y, + static_cast(poly.size())); + bool res = is_point_in_polygon(point, poly); + printf("res: %d", res); + ++i; + return res; }); return static_cast(intersects); @@ -97,6 +119,12 @@ pairwise_linestring_polygon_distance_kernel(MultiLinestringRange multilinestring auto geometry_id = thrust::distance(thread_bounds.begin(), it); auto local_idx = idx - *it; + printf("idx: %d, geometry_id: %d, intersects?: %d, local_idx: %d\n", + static_cast(idx), + static_cast(geometry_id), + static_cast(intersects[geometry_id]), + static_cast(local_idx)); + if (intersects[geometry_id]) { distances[geometry_id] = 0.0f; continue; @@ -104,25 +132,22 @@ pairwise_linestring_polygon_distance_kernel(MultiLinestringRange multilinestring auto num_segment_this_multilinestring = multilinestrings.multilinestring_segment_count_begin()[geometry_id]; - auto multilinestring_segment_id = local_idx % num_segment_this_multilinestring + multilinestrings_segment_offsets[geometry_id]; auto multipolygon_segment_id = local_idx / num_segment_this_multilinestring + multipolygons_segment_offsets[geometry_id]; printf( - "idx: %d, geometry_id: %d, local_idx: %d, multilinestring_segment_id: %d, " + "multilinestring_segment_id: %d, " "multipolygon_segment_id: %d\n", - static_cast(idx), - static_cast(geometry_id), - static_cast(local_idx), static_cast(multilinestring_segment_id), static_cast(multipolygon_segment_id)); auto [a, b] = multilinestrings.segment_begin()[multilinestring_segment_id]; auto [c, d] = multipolygons.segment_begin()[multipolygon_segment_id]; - printf("ab: (%f, %f) -> (%f, %f), cd: (%f, %f) -> (%f, %f)\n", + auto distance = sqrt(squared_segment_distance(a, b, c, d)); + printf("ab: (%f, %f) -> (%f, %f), cd: (%f, %f) -> (%f, %f), dist: %f\n", static_cast(a.x), static_cast(a.y), static_cast(b.x), @@ -130,7 +155,8 @@ pairwise_linestring_polygon_distance_kernel(MultiLinestringRange multilinestring static_cast(c.x), static_cast(c.y), static_cast(d.x), - static_cast(d.y)); + static_cast(d.y), + static_cast(distance)); atomicMin(&distances[geometry_id], sqrt(squared_segment_distance(a, b, c, d))); } @@ -226,12 +252,19 @@ OutputIt pairwise_linestring_polygon_distance(MultiLinestringRange multilinestri multipolygons.multipolygon_segment_count_begin() + multipolygon_segment_offsets.size(), multipolygon_segment_offsets.begin()); + std::cout << "multipoint intersect size: " << multipoint_intersects.size() << std::endl; + cuspatial::test::print_device_vector(multipoint_intersects, "multipoint_intersects: "); cuspatial::test::print_device_vector(thread_bounds, "thread_bounds: "); cuspatial::test::print_device_vector(multilinestring_segment_offsets, "multilinestring_segment_offsets:"); cuspatial::test::print_device_vector(multipolygon_segment_offsets, "multipolygon_segment_offsets: "); + thrust::fill(rmm::exec_policy(stream), + distances_first, + distances_first + multilinestrings.num_multilinestrings(), + std::numeric_limits::max()); + auto num_threads = thread_bounds.back_element(stream); auto [tpb, num_blocks] = grid_1d(num_threads); diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh index 137f260cf..5e0e6bbc1 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 "thrust/iterator/zip_iterator.h" #include #include #include @@ -25,6 +24,7 @@ #include #include +#include #include #include #include @@ -301,7 +301,7 @@ multilinestring_range::as_multipoin { auto multipoint_geometry_it = thrust::make_permutation_iterator(_part_begin, _geometry_begin); return multipoint_range{multipoint_geometry_it, - multipoint_geometry_it + num_multilinestrings(), + multipoint_geometry_it + thrust::distance(_geometry_begin, _geometry_end), _point_begin, _point_end}; } diff --git a/cpp/tests/experimental/spatial/linestring_polygon_distance_test.cu b/cpp/tests/experimental/spatial/linestring_polygon_distance_test.cu index 8cbc85138..b843e1de0 100644 --- a/cpp/tests/experimental/spatial/linestring_polygon_distance_test.cu +++ b/cpp/tests/experimental/spatial/linestring_polygon_distance_test.cu @@ -61,8 +61,8 @@ struct PairwiseLinestringPolygonDistanceTest : public BaseFixture { auto d_expected = make_device_vector(expected); - // CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(got, d_expected); - // EXPECT_EQ(ret, got.end()); + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(got, d_expected); + EXPECT_EQ(ret, got.end()); } }; @@ -87,6 +87,253 @@ TYPED_TEST(PairwiseLinestringPolygonDistanceTest, ZeroPairs) {}); } +// One Pair Test matrix: +// 1. One pair, one part multilinestring, one part, one ring multipolygon (111) +// 2. One pair, one part multilinestring, one part, two ring multipolygon (112) +// 3. One pair, one part multilinestring, two part, two ring multipolygon (122) +// 4. One pair, two part multilinestring, two part, two ring multipolygon (222) + +// For each of the above, test the following: +// 1. Disjoint +// 2. Contains +// 3. Crosses + +TYPED_TEST(PairwiseLinestringPolygonDistanceTest, OnePair111Disjoint) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST(this->run_single, + {0, 1}, + {0, 4}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{3, 3}}, + {0, 1}, + {0, 1}, + {0, 4}, + {P{-1, -1}, P{-2, -2}, P{-2, -1}, P{-1, -1}}, + {std::sqrt(T{2})}); +} + +TYPED_TEST(PairwiseLinestringPolygonDistanceTest, OnePair111Contains) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST( + this->run_single, + {0, 1}, + {0, 4}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{3, 3}}, + {0, 1}, + {0, 1}, + {0, 5}, + {P{-1, -1}, P{5, -1}, P{5, 5}, P{-1, 5}, P{-1, -1}}, // Polygon contains linestring + {0}); +} + +TYPED_TEST(PairwiseLinestringPolygonDistanceTest, OnePair111Crosses) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST(this->run_single, + {0, 1}, + {0, 4}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{3, 3}}, + {0, 1}, + {0, 1}, + {0, 5}, + {P{-1, 0}, P{1, 0}, P{0, 1}, P{-1, 0}}, + {0}); +} + +TYPED_TEST(PairwiseLinestringPolygonDistanceTest, OnePair112Contains) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST(this->run_single, + {0, 1}, + {0, 4}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{3, 3}}, + {0, 1}, + {0, 2}, + {0, 5, 9}, + {P{-1, -1}, + P{5, -1}, + P{5, 5}, + P{-1, 5}, + P{-1, -1}, + P{0, 0}, + P{0, -1}, + P{-1, -1}, + P{-1, 0}, + P{0, 0}}, + {0}); +} + +TYPED_TEST(PairwiseLinestringPolygonDistanceTest, OnePair112Disjoint) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST(this->run_single, + {0, 1}, + {0, 3}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{2, 3}}, + {0, 1}, + {0, 2}, + {0, 5, 10}, + {P{-1, -1}, + P{-4, -1}, + P{-4, -4}, + P{-1, -4}, + P{-1, -1}, + P{-2, -2}, + P{-3, -2}, + P{-3, -3}, + P{-2, -3}, + P{-2, -2}}, + {1.0}); +} + +TYPED_TEST(PairwiseLinestringPolygonDistanceTest, OnePair112Crosses) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST( + this->run_single, + {0, 1}, + {0, 3}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{2, 3}}, + {0, 1}, + {0, 2}, + {0, 4, 8}, + {P{-1, -1}, P{-2, -2}, P{-2, -1}, P{-1, -1}, P{0, 1}, P{2, 1}, P{2, 0}, P{0, 1}}, + {1.0}); +} + +TYPED_TEST(PairwiseLinestringPolygonDistanceTest, OnePair122Disjoint) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST( + this->run_single, + {0, 1}, + {0, 4}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{3, 3}}, + {0, 2}, + {0, 1, 2}, + {0, 4, 9}, + {P{-1, -1}, P{-2, -2}, P{-2, -1}, P{-1, -1}, P{3, 4}, P{3, 5}, P{4, 5}, P{4, 4}, P{3, 4}}, + {1.0}); +} + +TYPED_TEST(PairwiseLinestringPolygonDistanceTest, OnePair122Contains) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST(this->run_single, + {0, 1}, + {0, 4}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{3, 3}}, + {0, 2}, + {0, 1, 2}, + {0, 4, 9}, + { + P{-1, -1}, + P{-2, -2}, + P{-2, -1}, + P{-1, -1}, + P{-1, -1}, + P{5, -1}, + P{5, 5}, + P{-1, 5}, + P{-1, -1} // includes the multilinestring + }, + {0}); +} + +TYPED_TEST(PairwiseLinestringPolygonDistanceTest, OnePair122Crosses) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST( + this->run_single, + {0, 1}, + {0, 4}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{3, 3}}, + {0, 2}, + {0, 1, 2}, + {0, 4, 8}, + {P{-1, -1}, P{-2, -2}, P{-2, -1}, P{-1, -1}, P{0, 1}, P{2, 1}, P{2, 0}, P{0, 1}}, + {0}); +} + +TYPED_TEST(PairwiseLinestringPolygonDistanceTest, OnePair222Disjoint) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST( + this->run_single, + {0, 2}, + {0, 2, 4}, + {P{1, 1}, P{0, 0}, P{4, 6}, P{4, 7}}, + {0, 2}, + {0, 1, 2}, + {0, 4, 9}, + {P{-1, -1}, P{-2, -2}, P{-2, -1}, P{-1, -1}, P{3, 4}, P{3, 5}, P{4, 5}, P{4, 4}, P{3, 4}}, + {1.0}); +} + +TYPED_TEST(PairwiseLinestringPolygonDistanceTest, OnePair222Contains) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST(this->run_single, + {0, 1}, + {0, 2, 4}, + {P{1, 1}, P{0, 0}, P{6, 6}, P{6, 7}}, + {0, 2}, + {0, 1, 2}, + {0, 4, 9}, + { + P{-1, -1}, + P{-2, -2}, + P{-2, -1}, + P{-1, -1}, + P{-1, -1}, + P{5, -1}, + P{5, 5}, + P{-1, 5}, + P{-1, -1} // includes the multilinestring + }, + {0}); +} + +TYPED_TEST(PairwiseLinestringPolygonDistanceTest, OnePair222Crosses) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST( + this->run_single, + {0, 1}, + {0, 2, 4}, + {P{0, 0}, P{1, 1}, P{-1, 0}, P{0, -1}}, + {0, 2}, + {0, 1, 2}, + {0, 4, 8}, + {P{-1, -1}, P{-2, -2}, P{-2, -1}, P{-1, -1}, P{0, 1}, P{2, 1}, P{2, 0}, P{0, 1}}, + {0}); +} + TYPED_TEST(PairwiseLinestringPolygonDistanceTest, TwoPairs) { using T = TypeParam; From 7c3603c4970c83ebd0f207d59a1b55f2d0e5f56c Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Sat, 1 Apr 2023 22:29:32 -0700 Subject: [PATCH 21/54] fix broken multilinestring->multipoint constructor --- .../detail/ranges/multilinestring_range.cuh | 3 +- .../range/multilinestring_range_test.cu | 81 +++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh index 0cea9eb16..5e0e6bbc1 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multilinestring_range.cuh @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -300,7 +301,7 @@ multilinestring_range::as_multipoin { auto multipoint_geometry_it = thrust::make_permutation_iterator(_part_begin, _geometry_begin); return multipoint_range{multipoint_geometry_it, - multipoint_geometry_it + num_multilinestrings(), + multipoint_geometry_it + thrust::distance(_geometry_begin, _geometry_end), _point_begin, _point_end}; } diff --git a/cpp/tests/experimental/range/multilinestring_range_test.cu b/cpp/tests/experimental/range/multilinestring_range_test.cu index 2c11dfc3e..eff169df7 100644 --- a/cpp/tests/experimental/range/multilinestring_range_test.cu +++ b/cpp/tests/experimental/range/multilinestring_range_test.cu @@ -110,6 +110,35 @@ struct MultilinestringRangeTest : public BaseFixture { CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(d_expected, got); } + + void run_multilinestring_as_multipoint_test( + std::initializer_list geometry_offset, + std::initializer_list part_offset, + std::initializer_list> coordinates, + std::initializer_list>> expected) + { + auto multilinestring_array = + make_multilinestring_array(geometry_offset, part_offset, coordinates); + auto multilinestring_range = multilinestring_array.range(); + + auto multipoint_range = multilinestring_range.as_multipoint_range(); + + thrust::device_vector got_geometry_offset(multipoint_range.offsets_begin(), + multipoint_range.offsets_end()); + thrust::device_vector> got_coordinates(multipoint_range.point_begin(), + multipoint_range.point_end()); + + auto expected_multipoint = make_multipoints_array(expected); + auto expected_range = expected_multipoint.range(); + + thrust::device_vector expected_geometry_offset(expected_range.offsets_begin(), + expected_range.offsets_end()); + thrust::device_vector> expected_coordinates(expected_range.point_begin(), + expected_range.point_end()); + + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(expected_geometry_offset, got_geometry_offset); + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(expected_coordinates, got_coordinates); + } }; using TestTypes = ::testing::Types; @@ -323,3 +352,55 @@ TYPED_TEST(MultilinestringRangeTest, MultilinestringLinestringCountTest2) {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}, P{12, 12}, P{20, 20}, P{21, 21}, P{22, 22}}, {1, 2}); } + +TYPED_TEST(MultilinestringRangeTest, MultilinestringAsMultipointTest) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_multilinestring_as_multipoint_test, + {0, 1}, + {0, 3}, + {P{0, 0}, P{1, 1}, P{2, 2}}, + {{P{0, 0}, P{1, 1}, P{2, 2}}}); +} + +TYPED_TEST(MultilinestringRangeTest, MultilinestringAsMultipointTest2) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_multilinestring_as_multipoint_test, + {0, 2}, + {0, 3, 5}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{3, 3}, P{4, 4}}, + {{P{0, 0}, P{1, 1}, P{2, 2}, P{3, 3}, P{4, 4}}}); +} + +TYPED_TEST(MultilinestringRangeTest, MultilinestringAsMultipointTest3) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_multilinestring_as_multipoint_test, + {0, 1, 2}, + {0, 3, 5}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}}, + {{P{0, 0}, P{1, 1}, P{2, 2}}, {P{10, 10}, P{11, 11}}}); +} + +TYPED_TEST(MultilinestringRangeTest, MultilinestringAsMultipointTest4) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_multilinestring_as_multipoint_test, + {0, 1, 3}, + {0, 3, 5, 7}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}, P{12, 12}, P{13, 13}}, + {{P{0, 0}, P{1, 1}, P{2, 2}}, {P{10, 10}, P{11, 11}, P{12, 12}, P{13, 13}}}); +} From 70ca1b67c3663f05a48bc635a685b6f202c7641e Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Sat, 1 Apr 2023 22:29:32 -0700 Subject: [PATCH 22/54] fix broken multilinestring->multipoint constructor --- .../range/multilinestring_range_test.cu | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/cpp/tests/experimental/range/multilinestring_range_test.cu b/cpp/tests/experimental/range/multilinestring_range_test.cu index 2c11dfc3e..eff169df7 100644 --- a/cpp/tests/experimental/range/multilinestring_range_test.cu +++ b/cpp/tests/experimental/range/multilinestring_range_test.cu @@ -110,6 +110,35 @@ struct MultilinestringRangeTest : public BaseFixture { CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(d_expected, got); } + + void run_multilinestring_as_multipoint_test( + std::initializer_list geometry_offset, + std::initializer_list part_offset, + std::initializer_list> coordinates, + std::initializer_list>> expected) + { + auto multilinestring_array = + make_multilinestring_array(geometry_offset, part_offset, coordinates); + auto multilinestring_range = multilinestring_array.range(); + + auto multipoint_range = multilinestring_range.as_multipoint_range(); + + thrust::device_vector got_geometry_offset(multipoint_range.offsets_begin(), + multipoint_range.offsets_end()); + thrust::device_vector> got_coordinates(multipoint_range.point_begin(), + multipoint_range.point_end()); + + auto expected_multipoint = make_multipoints_array(expected); + auto expected_range = expected_multipoint.range(); + + thrust::device_vector expected_geometry_offset(expected_range.offsets_begin(), + expected_range.offsets_end()); + thrust::device_vector> expected_coordinates(expected_range.point_begin(), + expected_range.point_end()); + + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(expected_geometry_offset, got_geometry_offset); + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(expected_coordinates, got_coordinates); + } }; using TestTypes = ::testing::Types; @@ -323,3 +352,55 @@ TYPED_TEST(MultilinestringRangeTest, MultilinestringLinestringCountTest2) {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}, P{12, 12}, P{20, 20}, P{21, 21}, P{22, 22}}, {1, 2}); } + +TYPED_TEST(MultilinestringRangeTest, MultilinestringAsMultipointTest) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_multilinestring_as_multipoint_test, + {0, 1}, + {0, 3}, + {P{0, 0}, P{1, 1}, P{2, 2}}, + {{P{0, 0}, P{1, 1}, P{2, 2}}}); +} + +TYPED_TEST(MultilinestringRangeTest, MultilinestringAsMultipointTest2) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_multilinestring_as_multipoint_test, + {0, 2}, + {0, 3, 5}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{3, 3}, P{4, 4}}, + {{P{0, 0}, P{1, 1}, P{2, 2}, P{3, 3}, P{4, 4}}}); +} + +TYPED_TEST(MultilinestringRangeTest, MultilinestringAsMultipointTest3) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_multilinestring_as_multipoint_test, + {0, 1, 2}, + {0, 3, 5}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}}, + {{P{0, 0}, P{1, 1}, P{2, 2}}, {P{10, 10}, P{11, 11}}}); +} + +TYPED_TEST(MultilinestringRangeTest, MultilinestringAsMultipointTest4) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_multilinestring_as_multipoint_test, + {0, 1, 3}, + {0, 3, 5, 7}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}, P{12, 12}, P{13, 13}}, + {{P{0, 0}, P{1, 1}, P{2, 2}}, {P{10, 10}, P{11, 11}, P{12, 12}, P{13, 13}}}); +} From 92055330d5caab0632e35aa820b8886b891b9306 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 3 Apr 2023 09:48:25 -0700 Subject: [PATCH 23/54] passing existing tests --- .../detail/linestring_polygon_distance.cuh | 2 +- .../range/multilinestring_range_test.cu | 26 +++++++ .../linestring_polygon_distance_test.cu | 70 +++++++++++++++++-- 3 files changed, 90 insertions(+), 8 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh b/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh index 594fe5632..ea775163a 100644 --- a/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh @@ -86,7 +86,7 @@ struct point_in_multipolygon_test_functor { first_point.y, static_cast(poly.size())); bool res = is_point_in_polygon(point, poly); - printf("res: %d", res); + printf("res: %d\n", res); ++i; return res; }); diff --git a/cpp/tests/experimental/range/multilinestring_range_test.cu b/cpp/tests/experimental/range/multilinestring_range_test.cu index eff169df7..9fcb0097e 100644 --- a/cpp/tests/experimental/range/multilinestring_range_test.cu +++ b/cpp/tests/experimental/range/multilinestring_range_test.cu @@ -404,3 +404,29 @@ TYPED_TEST(MultilinestringRangeTest, MultilinestringAsMultipointTest4) {P{0, 0}, P{1, 1}, P{2, 2}, P{10, 10}, P{11, 11}, P{12, 12}, P{13, 13}}, {{P{0, 0}, P{1, 1}, P{2, 2}}, {P{10, 10}, P{11, 11}, P{12, 12}, P{13, 13}}}); } + +TYPED_TEST(MultilinestringRangeTest, MultilinestringAsMultipointTest5) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_multilinestring_as_multipoint_test, + {0, 1}, + {0, 4}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{2, 3}}, + {{P{0, 0}, P{1, 1}, P{2, 2}, P{2, 3}}}); +} + +TYPED_TEST(MultilinestringRangeTest, MultilinestringAsMultipointTest6) +{ + using T = TypeParam; + using P = vec_2d; + using S = segment; + + CUSPATIAL_RUN_TEST(this->run_multilinestring_as_multipoint_test, + {0, 2}, + {0, 2, 4}, + {P{1, 1}, P{0, 0}, P{6, 6}, P{6, 7}}, + {{P{1, 1}, P{0, 0}, P{6, 6}, P{6, 7}}}); +} diff --git a/cpp/tests/experimental/spatial/linestring_polygon_distance_test.cu b/cpp/tests/experimental/spatial/linestring_polygon_distance_test.cu index b843e1de0..09ab4602f 100644 --- a/cpp/tests/experimental/spatial/linestring_polygon_distance_test.cu +++ b/cpp/tests/experimental/spatial/linestring_polygon_distance_test.cu @@ -179,7 +179,7 @@ TYPED_TEST(PairwiseLinestringPolygonDistanceTest, OnePair112Disjoint) CUSPATIAL_RUN_TEST(this->run_single, {0, 1}, - {0, 3}, + {0, 4}, {P{0, 0}, P{1, 1}, P{2, 2}, P{2, 3}}, {0, 1}, {0, 2}, @@ -194,7 +194,7 @@ TYPED_TEST(PairwiseLinestringPolygonDistanceTest, OnePair112Disjoint) P{-3, -3}, P{-2, -3}, P{-2, -2}}, - {1.0}); + {std::sqrt(T{2})}); } TYPED_TEST(PairwiseLinestringPolygonDistanceTest, OnePair112Crosses) @@ -205,13 +205,13 @@ TYPED_TEST(PairwiseLinestringPolygonDistanceTest, OnePair112Crosses) CUSPATIAL_RUN_TEST( this->run_single, {0, 1}, - {0, 3}, + {0, 4}, {P{0, 0}, P{1, 1}, P{2, 2}, P{2, 3}}, {0, 1}, {0, 2}, {0, 4, 8}, {P{-1, -1}, P{-2, -2}, P{-2, -1}, P{-1, -1}, P{0, 1}, P{2, 1}, P{2, 0}, P{0, 1}}, - {1.0}); + {0.0}); } TYPED_TEST(PairwiseLinestringPolygonDistanceTest, OnePair122Disjoint) @@ -297,7 +297,7 @@ TYPED_TEST(PairwiseLinestringPolygonDistanceTest, OnePair222Contains) using P = vec_2d; CUSPATIAL_RUN_TEST(this->run_single, - {0, 1}, + {0, 2}, {0, 2, 4}, {P{1, 1}, P{0, 0}, P{6, 6}, P{6, 7}}, {0, 2}, @@ -324,7 +324,7 @@ TYPED_TEST(PairwiseLinestringPolygonDistanceTest, OnePair222Crosses) CUSPATIAL_RUN_TEST( this->run_single, - {0, 1}, + {0, 2}, {0, 2, 4}, {P{0, 0}, P{1, 1}, P{-1, 0}, P{0, -1}}, {0, 2}, @@ -355,5 +355,61 @@ TYPED_TEST(PairwiseLinestringPolygonDistanceTest, TwoPairs) P{-11, -11}, P{-11, -10}, P{-10, -10}}, - {0.0, 0.0}); + {std::sqrt(T{2}), 20 * std::sqrt(T{2})}); +} + +TYPED_TEST(PairwiseLinestringPolygonDistanceTest, TwoPairs2) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST( + this->run_single, + {0, 1, 3}, + {0, 4, 7, 9}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{3, 3}, P{10, 10}, P{11, 11}, P{12, 12}, P{20, 20}, P{20, 21}}, + {0, 1, 3}, + {0, 1, 2, 3}, + {0, 4, 9, 13}, + {P{-1, -1}, + P{-2, -2}, + P{-2, -1}, + P{-1, -1}, + P{-10, -10}, + P{-10, -11}, + P{-11, -11}, + P{-11, -10}, + P{-10, -10}, + P{20, -10}, + P{20, -20}, + P{30, -20}, + P{20, -10}}, + {std::sqrt(T{2}), 10 * std::sqrt(T{5})}); +} + +TYPED_TEST(PairwiseLinestringPolygonDistanceTest, TwoPairsCrosses) +{ + using T = TypeParam; + using P = vec_2d; + + CUSPATIAL_RUN_TEST(this->run_single, + {0, 1, 2}, + {0, 4, 6}, + {P{0, 0}, P{1, 1}, P{2, 2}, P{3, 3}, P{5, 5}, P{20, 20}}, + {0, 1, 2}, + {0, 1, 3}, + {0, 4, 8, 12}, + {P{-1, -1}, + P{-2, -2}, + P{-2, -1}, + P{-1, -1}, + P{0, 0}, + P{20, 0}, + P{0, 20}, + P{0, 0}, + P{5, 5}, + P{15, 5}, + P{5, 15}, + P{5, 5}}, + {std::sqrt(T{2}), 0.0}); } From 4bb3ca392263e38701c69d2e24f0a5dd0c415596 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 3 Apr 2023 10:25:41 -0700 Subject: [PATCH 24/54] fix wrong test setup and update docs --- .../experimental/detail/functors.cuh | 2 +- .../ranges/multipolygon_range.cuh | 2 + .../range/multipolygon_range_test.cu | 52 +++++++++---------- 3 files changed, 29 insertions(+), 27 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/functors.cuh b/cpp/include/cuspatial/experimental/detail/functors.cuh index 6cc0ab35e..7563020f8 100644 --- a/cpp/include/cuspatial/experimental/detail/functors.cuh +++ b/cpp/include/cuspatial/experimental/detail/functors.cuh @@ -44,7 +44,7 @@ struct offset_pair_to_count_functor { }; /** - * @brief Convert counts of points to counts of segments. + * @brief Convert counts of points to counts of segments in a linestring. * * A Multilinestring is composed of a series of Linestrings. Each Linestring is composed of a * segments. The number of segments in a multilinestring is the number of points in the diff --git a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh index 5daa5ad9f..96129145f 100644 --- a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh @@ -158,8 +158,10 @@ class multipolygon_range { IndexType2 geometry_idx); /// Returns an iterator to the number of points of the first multipolygon + /// @note The count includes the duplicate first and last point of the ring. CUSPATIAL_HOST_DEVICE auto multipolygon_point_count_begin(); /// Returns the one past the iterator to the number of points of the last multipolygon + /// @note The count includes the duplicate first and last point of the ring. CUSPATIAL_HOST_DEVICE auto multipolygon_point_count_end(); /// Returns an iterator to the number of rings of the first multipolygon diff --git a/cpp/tests/experimental/range/multipolygon_range_test.cu b/cpp/tests/experimental/range/multipolygon_range_test.cu index c4af51a03..b6b5bc340 100644 --- a/cpp/tests/experimental/range/multipolygon_range_test.cu +++ b/cpp/tests/experimental/range/multipolygon_range_test.cu @@ -161,28 +161,28 @@ TYPED_TEST(MultipolygonRangeTest, SegmentIterators4) {{11, 11}, {10, 10}}}); } -TYPED_TEST(MultipolygonRangeTest, PerMultipolygonCountIterator) +TYPED_TEST(MultipolygonRangeTest, MultipolygonCountIterator) { CUSPATIAL_RUN_TEST(this->run_multipolygon_point_count_iterator_single, {0, 1}, {0, 1}, {0, 4}, - {{0, 0}, {1, 0}, {1, 1}, {0, 1}}, + {{0, 0}, {1, 0}, {1, 1}, {0, 0}}, {4}); } -TYPED_TEST(MultipolygonRangeTest, PerMultipolygonCountIterator2) +TYPED_TEST(MultipolygonRangeTest, MultipolygonCountIterator2) { CUSPATIAL_RUN_TEST( this->run_multipolygon_point_count_iterator_single, {0, 1}, {0, 2}, {0, 4, 8}, - {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0.2, 0.2}, {0.2, 0.3}, {0.3, 0.3}, {0.3, 0.2}}, + {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {0.2, 0.2}, {0.2, 0.3}, {0.3, 0.3}, {0.3, 0.2}}, {8}); } -TYPED_TEST(MultipolygonRangeTest, PerMultipolygonCountIterator3) +TYPED_TEST(MultipolygonRangeTest, MultipolygonCountIterator3) { CUSPATIAL_RUN_TEST(this->run_multipolygon_point_count_iterator_single, {0, 2}, @@ -191,7 +191,7 @@ TYPED_TEST(MultipolygonRangeTest, PerMultipolygonCountIterator3) {{0, 0}, {1, 0}, {1, 1}, - {0, 1}, + {0, 0}, {0.2, 0.2}, {0.2, 0.3}, {0.3, 0.3}, @@ -203,7 +203,7 @@ TYPED_TEST(MultipolygonRangeTest, PerMultipolygonCountIterator3) {12}); } -TYPED_TEST(MultipolygonRangeTest, PerMultipolygonCountIterator4) +TYPED_TEST(MultipolygonRangeTest, MultipolygonCountIterator4) { CUSPATIAL_RUN_TEST(this->run_multipolygon_point_count_iterator_single, {0, 2, 3}, @@ -212,19 +212,19 @@ TYPED_TEST(MultipolygonRangeTest, PerMultipolygonCountIterator4) {{0, 0}, {1, 0}, {1, 1}, - {0, 1}, + {0, 0}, {0.2, 0.2}, {0.2, 0.3}, {0.3, 0.3}, - {0.3, 0.2}, + {0.2, 0.2}, {0, 0}, {1, 0}, {1, 1}, - {0, 1}, + {0, 0}, {0, 0}, {1, 0}, {1, 1}, - {0, 1}}, + {0, 0}}, {12, 4}); } @@ -234,7 +234,7 @@ TYPED_TEST(MultipolygonRangeTest, MultipolygonSegmentCount) {0, 1}, {0, 1}, {0, 4}, - {{0, 0}, {1, 0}, {1, 1}, {0, 1}}, + {{0, 0}, {1, 0}, {1, 1}, {0, 0}}, {3}); } @@ -245,7 +245,7 @@ TYPED_TEST(MultipolygonRangeTest, MultipolygonSegmentCount2) {0, 1}, {0, 2}, {0, 4, 8}, - {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0.2, 0.2}, {0.2, 0.3}, {0.3, 0.3}, {0.3, 0.2}}, + {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {0.2, 0.2}, {0.2, 0.3}, {0.3, 0.3}, {0.2, 0.2}}, {6}); } @@ -258,15 +258,15 @@ TYPED_TEST(MultipolygonRangeTest, MultipolygonSegmentCount3) {{0, 0}, {1, 0}, {1, 1}, - {0, 1}, + {0, 0}, {0.2, 0.2}, {0.2, 0.3}, {0.3, 0.3}, - {0.3, 0.2}, + {0.2, 0.2}, {0, 0}, {1, 0}, {1, 1}, - {0, 1}}, + {0, 0}}, {9}); } @@ -279,19 +279,19 @@ TYPED_TEST(MultipolygonRangeTest, MultipolygonSegmentCount4) {{0, 0}, {1, 0}, {1, 1}, - {0, 1}, + {0, 0}, {0.2, 0.2}, {0.2, 0.3}, {0.3, 0.3}, - {0.3, 0.2}, + {0.2, 0.2}, {0, 0}, {1, 0}, {1, 1}, - {0, 1}, + {0, 0}, {0, 0}, {1, 0}, {1, 1}, - {0, 1}}, + {0, 0}}, {9, 3}); } @@ -305,15 +305,15 @@ TYPED_TEST(MultipolygonRangeTest, DISABLED_MultipolygonSegmentCount_ConatainsEmp {{0, 0}, {1, 0}, {1, 1}, - {0, 1}, + {0, 0}, {0.2, 0.2}, {0.2, 0.3}, {0.3, 0.3}, - {0.3, 0.2}, + {0.2, 0.2}, {0, 0}, {1, 0}, {1, 1}, - {0, 1}}, + {0, 0}}, {6, 3}); } @@ -327,14 +327,14 @@ TYPED_TEST(MultipolygonRangeTest, DISABLED_MultipolygonSegmentCount_ConatainsEmp {{0, 0}, {1, 0}, {1, 1}, - {0, 1}, + {0, 0}, {0.2, 0.2}, {0.2, 0.3}, {0.3, 0.3}, - {0.3, 0.2}, + {0.2, 0.2}, {0, 0}, {1, 0}, {1, 1}, - {0, 1}}, + {0, 0}}, {6, 3}); } From 8f20feb242e94b069f751df12631ea2080e3e9bf Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 3 Apr 2023 10:36:36 -0700 Subject: [PATCH 25/54] style --- cpp/include/cuspatial_test/base_fixture.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cpp/include/cuspatial_test/base_fixture.hpp b/cpp/include/cuspatial_test/base_fixture.hpp index 244d0973d..10602c1b0 100644 --- a/cpp/include/cuspatial_test/base_fixture.hpp +++ b/cpp/include/cuspatial_test/base_fixture.hpp @@ -54,7 +54,8 @@ class RMMResourceMixin { * class MyTestFixture : public cuspatial::test::BaseFixture {}; * ``` */ -class BaseFixture : public RMMResourceMixin, public ::testing::Test {}; +class BaseFixture : public RMMResourceMixin, public ::testing::Test { +}; /** * @brief Base test fixture class from which libcuspatial test with only value parameterization @@ -78,7 +79,8 @@ class BaseFixture : public RMMResourceMixin, public ::testing::Test {}; */ template class BaseFixtureWithParam : public RMMResourceMixin, - public ::testing::TestWithParam> {}; + public ::testing::TestWithParam> { +}; /** * @brief Floating point types to be used in libcuspatial tests From 3ffe5ee4d33195e737053a49c48cfa478a395b6a Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 3 Apr 2023 10:49:43 -0700 Subject: [PATCH 26/54] Delete point_in_multipolygon.cuh --- .../experimental/detail/distance_utils/point_in_multipolygon.cuh | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 cpp/include/cuspatial/experimental/detail/distance_utils/point_in_multipolygon.cuh diff --git a/cpp/include/cuspatial/experimental/detail/distance_utils/point_in_multipolygon.cuh b/cpp/include/cuspatial/experimental/detail/distance_utils/point_in_multipolygon.cuh deleted file mode 100644 index e69de29bb..000000000 From 894f870c4e6e582e664d6d0679409d201bb59e80 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 3 Apr 2023 10:49:59 -0700 Subject: [PATCH 27/54] Delete linestring_polygon_distance.hpp --- cpp/include/cuspatial/distance/linestring_polygon_distance.hpp | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 cpp/include/cuspatial/distance/linestring_polygon_distance.hpp diff --git a/cpp/include/cuspatial/distance/linestring_polygon_distance.hpp b/cpp/include/cuspatial/distance/linestring_polygon_distance.hpp deleted file mode 100644 index e69de29bb..000000000 From 2b9955591e2487cc7f6123140f5734d68b2d235c Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 3 Apr 2023 11:31:37 -0700 Subject: [PATCH 28/54] cleanup - first round --- .../detail/linestring_polygon_distance.cuh | 78 ++++--------------- 1 file changed, 15 insertions(+), 63 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh b/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh index ea775163a..2b03469db 100644 --- a/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh @@ -28,12 +28,14 @@ #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -66,29 +68,11 @@ 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 - int i = 0; - bool intersects = thrust::any_of( - thrust::seq, - polys.begin(), - polys.end(), - [&point, &i, &pidx, &geometry_idx] __device__(auto poly) { - vec_2d first_point = poly.point_begin()[0]; - printf( - "pidx: %d, geometry_idx: %d, (%f, %f) in poly %d (first point: (%f %f), size: %d)?\n", - static_cast(pidx), - static_cast(geometry_idx), - point.x, - point.y, - i, - first_point.x, - first_point.y, - static_cast(poly.size())); - bool res = is_point_in_polygon(point, poly); - printf("res: %d\n", res); - ++i; - return res; + // 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); }); return static_cast(intersects); @@ -119,55 +103,26 @@ pairwise_linestring_polygon_distance_kernel(MultiLinestringRange multilinestring auto geometry_id = thrust::distance(thread_bounds.begin(), it); auto local_idx = idx - *it; - printf("idx: %d, geometry_id: %d, intersects?: %d, local_idx: %d\n", - static_cast(idx), - static_cast(geometry_id), - static_cast(intersects[geometry_id]), - static_cast(local_idx)); - if (intersects[geometry_id]) { distances[geometry_id] = 0.0f; continue; } + // Retrieve the number of segments in multilinestrings[geometry_id] auto num_segment_this_multilinestring = multilinestrings.multilinestring_segment_count_begin()[geometry_id]; + // The segment id from the multilinestring this thread is compmuting (local_id + global_offset) auto multilinestring_segment_id = local_idx % num_segment_this_multilinestring + multilinestrings_segment_offsets[geometry_id]; + // The segment id from the multipolygon this thread is computing (local_id + global_offset) auto multipolygon_segment_id = local_idx / num_segment_this_multilinestring + multipolygons_segment_offsets[geometry_id]; - printf( - "multilinestring_segment_id: %d, " - "multipolygon_segment_id: %d\n", - static_cast(multilinestring_segment_id), - static_cast(multipolygon_segment_id)); - auto [a, b] = multilinestrings.segment_begin()[multilinestring_segment_id]; auto [c, d] = multipolygons.segment_begin()[multipolygon_segment_id]; - auto distance = sqrt(squared_segment_distance(a, b, c, d)); - printf("ab: (%f, %f) -> (%f, %f), cd: (%f, %f) -> (%f, %f), dist: %f\n", - static_cast(a.x), - static_cast(a.y), - static_cast(b.x), - static_cast(b.y), - static_cast(c.x), - static_cast(c.y), - static_cast(d.x), - static_cast(d.y), - static_cast(distance)); - atomicMin(&distances[geometry_id], sqrt(squared_segment_distance(a, b, c, d))); } -} - -template -struct functor { - index_t __device__ operator()(thrust::tuple t) - { - return thrust::get<0>(t) * thrust::get<1>(t); - } }; } // namespace detail @@ -225,8 +180,11 @@ OutputIt pairwise_linestring_polygon_distance(MultiLinestringRange multilinestri auto segment_count_product_it = thrust::make_transform_iterator( thrust::make_zip_iterator(multilinestrings.multilinestring_segment_count_begin(), multipolygons.multipolygon_segment_count_begin()), - detail::functor{}); + thrust::make_zip_function(thrust::multiplies{}) + ); + // Computes the "thread boundary" of each pair. This array partitions the thread range by geometries. + // E.g. threadIdx within [thread_bounds[i], thread_bounds[i+1]) computes distances of the ith pair. auto thread_bounds = rmm::device_uvector(multilinestrings.size() + 1, stream); detail::zero_data_async(thread_bounds.begin(), thread_bounds.end(), stream); @@ -235,6 +193,7 @@ OutputIt pairwise_linestring_polygon_distance(MultiLinestringRange multilinestri segment_count_product_it + thread_bounds.size() - 1, thrust::next(thread_bounds.begin())); + // Compute offsets to the first segment of each multilinestring and multipolygon auto multilinestring_segment_offsets = rmm::device_uvector(multilinestrings.num_multilinestrings() + 1, stream); auto multipolygon_segment_offsets = @@ -252,14 +211,7 @@ OutputIt pairwise_linestring_polygon_distance(MultiLinestringRange multilinestri multipolygons.multipolygon_segment_count_begin() + multipolygon_segment_offsets.size(), multipolygon_segment_offsets.begin()); - std::cout << "multipoint intersect size: " << multipoint_intersects.size() << std::endl; - cuspatial::test::print_device_vector(multipoint_intersects, "multipoint_intersects: "); - cuspatial::test::print_device_vector(thread_bounds, "thread_bounds: "); - cuspatial::test::print_device_vector(multilinestring_segment_offsets, - "multilinestring_segment_offsets:"); - cuspatial::test::print_device_vector(multipolygon_segment_offsets, - "multipolygon_segment_offsets: "); - + // Initialize output range thrust::fill(rmm::exec_policy(stream), distances_first, distances_first + multilinestrings.num_multilinestrings(), From 62d821f06c01051cd13acfc847cf8236d92c645d Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 3 Apr 2023 12:56:20 -0700 Subject: [PATCH 29/54] merge duplicate code --- .../detail/linestring_polygon_distance.cuh | 90 +++++-------------- .../detail/point_polygon_distance.cuh | 65 +------------- .../detail/ranges/multipolygon_range.cuh | 21 ----- 3 files changed, 23 insertions(+), 153 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh b/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh index 2b03469db..6775120b3 100644 --- a/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh @@ -16,7 +16,7 @@ #pragma once -#include +#include "distance_utils.cuh" #include #include @@ -34,7 +34,6 @@ #include #include #include -#include #include #include @@ -47,38 +46,19 @@ namespace cuspatial { namespace detail { /** - * @brief For each point in the multipoint, compute point-in-multipolygon in corresponding pair. + * @brief Computes distances between the multilinestring and multipolygons + * + * @param multilinestrings Range to the multilinestring + * @param multipolygons Range to the multipolygon + * @param thread_bounds Range to the boundary of thread partitions + * @param multilinestrings_segment_offsets Range to the indices where the first segment of each + * multilinestring begins + * @param multipolygons_segment_offsets Range to the indices where the first segment of each + * multipolygon begins + * @param intersects A uint8_t array that indicates if the corresponding pair of multipoint and + * multipolygon intersects + * @param distances Output range of distances, pre-filled with std::numerical_limits::max() */ -template -struct point_in_multipolygon_test_functor { - using T = typename MultiPointRange::element_t; - - MultiPointRange multipoints; - MultiPolygonRange multipolygons; - - point_in_multipolygon_test_functor(MultiPointRange multipoints, MultiPolygonRange multipolygons) - : multipoints(multipoints), multipolygons(multipolygons) - { - } - - template - uint8_t __device__ operator()(IndexType pidx) - { - vec_2d const& point = multipoints.point(pidx); - 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); - }); - - return static_cast(intersects); - } -}; - template 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}); - - // `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); - - 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, - offset_as_key_it + multipoints.num_points(), - point_intersects.begin(), - thrust::make_discard_iterator(), - multipoint_intersects.begin(), - thrust::logical_or()); - - return multipoint_intersects; - }(); + // Create a multipoint range from multilinestrings, computes intersection + auto multipoints = multilinestrings.as_multipoint_range(); + auto multipoint_intersects = point_polygon_intersects(multipoints, multipolygons, stream); // Compute the "boundary" of threads. Threads are partitioned based on the number of linestrings // times the number of polygons in a multipoint-multipolygon pair. auto segment_count_product_it = thrust::make_transform_iterator( thrust::make_zip_iterator(multilinestrings.multilinestring_segment_count_begin(), multipolygons.multipolygon_segment_count_begin()), - thrust::make_zip_function(thrust::multiplies{}) - ); + thrust::make_zip_function(thrust::multiplies{})); - // Computes the "thread boundary" of each pair. This array partitions the thread range by geometries. - // E.g. threadIdx within [thread_bounds[i], thread_bounds[i+1]) computes distances of the ith pair. + // Computes the "thread boundary" of each pair. This array partitions the thread range by + // geometries. E.g. threadIdx within [thread_bounds[i], thread_bounds[i+1]) computes distances of + // the ith pair. auto thread_bounds = rmm::device_uvector(multilinestrings.size() + 1, stream); detail::zero_data_async(thread_bounds.begin(), thread_bounds.end(), stream); diff --git a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh index 704f67a6b..b015f09c8 100644 --- a/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/point_polygon_distance.cuh @@ -16,6 +16,8 @@ #pragma once +#include "distance_utils.cuh" + #include #include #include @@ -45,38 +47,6 @@ 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 { - using T = typename MultiPointRange::element_t; - - MultiPointRange multipoints; - MultiPolygonRange multipolygons; - - point_in_multipolygon_test_functor(MultiPointRange multipoints, MultiPolygonRange multipolygons) - : multipoints(multipoints), multipolygons(multipolygons) - { - } - - template - uint8_t __device__ operator()(IndexType pidx) - { - vec_2d const& point = multipoints.point(pidx); - 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); - }); - - return static_cast(intersects); - } -}; - /** * @brief Kernel to compute the distance between pairs of point and polygon. */ @@ -128,36 +98,7 @@ 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); - - thrust::tabulate(rmm::exec_policy(stream), - point_intersects.begin(), - point_intersects.end(), - detail::point_in_multipolygon_test_functor{multipoints, multipolygons}); - - // `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); - - 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, - offset_as_key_it + multipoints.num_points(), - point_intersects.begin(), - thrust::make_discard_iterator(), - multipoint_intersects.begin(), - thrust::logical_or()); - - return multipoint_intersects; - }(); + auto multipoint_intersects = point_polygon_intersects(multipoints, multipolygons, stream); thrust::fill(rmm::exec_policy(stream), 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 fcf5c5d41..455d4e995 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh @@ -205,27 +205,6 @@ multipolygon_range::p return _point_end; } -// template -// CUSPATIAL_HOST_DEVICE auto -// multipolygon_range::polygon_begin() -// { -// return detail::make_counting_transform_iterator( -// 0, to_polygon_functor{_part_begin, _ring_begin, point_begin, point_end}); -// } - -// template -// CUSPATIAL_HOST_DEVICE auto -// multipolygon_range::polygon_end() -// { -// return polygon_begin() + num_polygons(); -// } - template Date: Mon, 3 Apr 2023 14:36:52 -0700 Subject: [PATCH 30/54] initial --- cpp/CMakeLists.txt | 1 + .../distance/linestring_polygon_distance.hpp | 49 ++++ .../spatial/linestring_polygon_distance.cu | 127 ++++++++++ cpp/tests/CMakeLists.txt | 3 + .../linestring_polygon_distance_test.cpp | 218 ++++++++++++++++++ 5 files changed, 398 insertions(+) create mode 100644 cpp/include/cuspatial/distance/linestring_polygon_distance.hpp create mode 100644 cpp/src/spatial/linestring_polygon_distance.cu create mode 100644 cpp/tests/spatial/linestring_polygon_distance_test.cpp diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 74391b5ea..adea201dd 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -134,6 +134,7 @@ add_library(cuspatial src/spatial/point_distance.cu src/spatial/point_linestring_distance.cu src/spatial/point_polygon_distance.cu + src/spatial/linestring_polygon_distance.cu src/spatial/point_linestring_nearest_points.cu src/spatial/sinusoidal_projection.cu src/trajectory/derive_trajectories.cu diff --git a/cpp/include/cuspatial/distance/linestring_polygon_distance.hpp b/cpp/include/cuspatial/distance/linestring_polygon_distance.hpp new file mode 100644 index 000000000..07450e94d --- /dev/null +++ b/cpp/include/cuspatial/distance/linestring_polygon_distance.hpp @@ -0,0 +1,49 @@ +/* + * 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 + +namespace cuspatial { + +/** + * @ingroup distance + * @brief Compute pairwise (multi)linestring-to-(multi)polygon Cartesian distance + * + * @param multilinestrings Geometry column of multilinestrings + * @param multipolygons Geometry column of multipolygons + * @param mr Device memory resource used to allocate the returned column. + * @return Column of distances between each pair of input geometries, same type as input coordinate + * types. + * + * @throw cuspatial::logic_error if `multilinestrings` and `multipolygons` has different coordinate + * types. + * @throw cuspatial::logic_error if `multilinestrings` is not a linestring column and `multipolygons` is not a + * polygon column. + * @throw cuspatial::logic_error if input column sizes mismatch. + */ + +std::unique_ptr pairwise_linestring_polygon_distance( + geometry_column_view const& multilinestrings, + geometry_column_view const& multipolygons, + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); + +} // namespace cuspatial diff --git a/cpp/src/spatial/linestring_polygon_distance.cu b/cpp/src/spatial/linestring_polygon_distance.cu new file mode 100644 index 000000000..ada232ea1 --- /dev/null +++ b/cpp/src/spatial/linestring_polygon_distance.cu @@ -0,0 +1,127 @@ +/* + * 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/iterator.hpp" +#include "../utility/multi_geometry_dispatch.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace cuspatial { + +namespace detail { + +namespace { + +template +struct pairwise_linestring_polygon_distance_impl { + using SizeType = cudf::device_span::size_type; + + template )> + std::unique_ptr operator()(geometry_column_view const& multilinestrings, + geometry_column_view const& multipolygons, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) + { + auto multilinestrings_range = + make_multilinestring_range(multilinestrings); + auto multipolygons_range = + make_multipolygon_range(multipolygons); + + auto output = cudf::make_numeric_column(multilinestrings.coordinate_type(), + multilinestrings.size(), + cudf::mask_state::UNALLOCATED, + stream, + mr); + + cuspatial::pairwise_linestring_polygon_distance( + multilinestrings_range, multipolygons_range, output->mutable_view().begin(), stream); + return output; + } + + template ), typename... Args> + std::unique_ptr operator()(Args&&...) + + { + CUSPATIAL_FAIL("linestring-polygon distance API only supports floating point coordinates."); + } +}; + +} // namespace + +template +struct pairwise_linestring_polygon_distance { + std::unique_ptr operator()(geometry_column_view const& multilinestrings, + geometry_column_view const& multipolygons, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) + { + return cudf::type_dispatcher( + multilinestrings.coordinate_type(), + pairwise_linestring_polygon_distance_impl{}, + multilinestrings, + multipolygons, + stream, + mr); + } +}; + +} // namespace detail + +std::unique_ptr pairwise_linestring_polygon_distance( + geometry_column_view const& multilinestrings, + geometry_column_view const& multipolygons, + rmm::mr::device_memory_resource* mr) +{ + CUSPATIAL_EXPECTS(multilinestrings.geometry_type() == geometry_type_id::LINESTRING && + multipolygons.geometry_type() == geometry_type_id::POLYGON, + "Unexpected input geometry types."); + + CUSPATIAL_EXPECTS(multilinestrings.coordinate_type() == multipolygons.coordinate_type(), + "Input geometries must have the same coordinate data types."); + + return multi_geometry_double_dispatch( + multilinestrings.collection_type(), + multipolygons.collection_type(), + multilinestrings, + multipolygons, + rmm::cuda_stream_default, + mr); +} + +} // namespace cuspatial diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 6ace48180..62876b996 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -90,6 +90,9 @@ ConfigureTest(LINESTRING_DISTANCE_TEST ConfigureTest(POINT_POLYGON_DISTANCE_TEST spatial/point_polygon_distance_test.cpp) +ConfigureTest(LINESTRING_POLYGON_DISTANCE_TEST + spatial/linestring_polygon_distance_test.cpp) + ConfigureTest(LINESTRING_INTERSECTION_TEST spatial/linestring_intersection_test.cpp) diff --git a/cpp/tests/spatial/linestring_polygon_distance_test.cpp b/cpp/tests/spatial/linestring_polygon_distance_test.cpp new file mode 100644 index 000000000..97497fbe2 --- /dev/null +++ b/cpp/tests/spatial/linestring_polygon_distance_test.cpp @@ -0,0 +1,218 @@ +/* + * 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 +#include + +#include +#include + +#include + +#include +#include + +#include + +using namespace cuspatial; +using namespace cuspatial::test; + +using namespace cudf; +using namespace cudf::test; + +template +struct PairwiseLinestringPolygonDistanceTest : BaseFixture { + void run_single(geometry_column_view linestrings, + geometry_column_view polygons, + std::initializer_list expected) + { + auto got = pairwise_linestring_polygon_distance(linestrings, polygons); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*got, fixed_width_column_wrapper(expected)); + } +}; + +struct PairwiseLinestringPolygonDistanceTestUntyped : BaseFixture { +}; + +using TestTypes = ::testing::Types; + +TYPED_TEST_CASE(PairwiseLinestringPolygonDistanceTest, TestTypes); + +TYPED_TEST(PairwiseLinestringPolygonDistanceTest, SingleToSingleEmpty) +{ + using T = TypeParam; + + auto [ptype, points] = make_linestring_column({}, {}, {}, this->stream()); + + auto [polytype, polygons] = + make_polygon_column({0}, {0}, std::initializer_list{}, this->stream()); + + CUSPATIAL_RUN_TEST(this->run_single, + geometry_column_view(points->view(), ptype, geometry_type_id::POINT), + geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON), + {}); +}; + +// TYPED_TEST(PairwiseLinestringPolygonDistanceTest, SingleToMultiEmpty) +// { +// using T = TypeParam; + +// auto [ptype, points] = make_linestring_column(std::initializer_list{}, this->stream()); + +// auto [polytype, polygons] = +// make_polygon_column({0}, {0}, {0}, std::initializer_list{}, this->stream()); + +// CUSPATIAL_RUN_TEST(this->run_single, +// geometry_column_view(points->view(), ptype, geometry_type_id::POINT), +// geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON), +// {}); +// }; + +// TYPED_TEST(PairwiseLinestringPolygonDistanceTest, MultiToSingleEmpty) +// { +// using T = TypeParam; + +// auto [ptype, points] = make_linestring_column({0}, std::initializer_list{}, this->stream()); + +// auto [polytype, polygons] = +// make_polygon_column({0}, {0}, std::initializer_list{}, this->stream()); + +// CUSPATIAL_RUN_TEST(this->run_single, +// geometry_column_view(points->view(), ptype, geometry_type_id::POINT), +// geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON), +// {}); +// }; + +// TYPED_TEST(PairwiseLinestringPolygonDistanceTest, MultiToMultiEmpty) +// { +// using T = TypeParam; + +// auto [ptype, points] = make_linestring_column({0}, std::initializer_list{}, this->stream()); + +// auto [polytype, polygons] = +// make_polygon_column({0}, {0}, {0}, std::initializer_list{}, this->stream()); + +// CUSPATIAL_RUN_TEST(this->run_single, +// geometry_column_view(points->view(), ptype, geometry_type_id::POINT), +// geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON), +// {}); +// }; + +// TYPED_TEST(PairwiseLinestringPolygonDistanceTest, SingleToSingleOnePair) +// { +// using T = TypeParam; + +// auto [ptype, points] = make_linestring_column({0.0, 0.0}, this->stream()); + +// auto [polytype, polygons] = +// make_polygon_column({0, 1}, {0, 4}, {1, 1, 1, 2, 2, 2, 1, 1}, this->stream()); + +// CUSPATIAL_RUN_TEST(this->run_single, +// geometry_column_view(points->view(), ptype, geometry_type_id::POINT), +// geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON), +// {1.4142135623730951}); +// }; + +// TYPED_TEST(PairwiseLinestringPolygonDistanceTest, SingleToMultiOnePair) +// { +// using T = TypeParam; + +// auto [ptype, points] = make_linestring_column({0.0, 0.0}, this->stream()); + +// auto [polytype, polygons] = +// make_polygon_column({0, 1}, {0, 1}, {0, 4}, {1, 1, 1, 2, 2, 2, 1, 1}, this->stream()); + +// CUSPATIAL_RUN_TEST(this->run_single, +// geometry_column_view(points->view(), ptype, geometry_type_id::POINT), +// geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON), +// {1.4142135623730951}); +// }; + +// TYPED_TEST(PairwiseLinestringPolygonDistanceTest, MultiToSingleOnePair) +// { +// using T = TypeParam; + +// auto [ptype, points] = make_linestring_column({0, 1}, {0.0, 0.0}, this->stream()); + +// auto [polytype, polygons] = +// make_polygon_column({0, 1}, {0, 4}, {1, 1, 1, 2, 2, 2, 1, 1}, this->stream()); + +// CUSPATIAL_RUN_TEST(this->run_single, +// geometry_column_view(points->view(), ptype, geometry_type_id::POINT), +// geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON), +// {1.4142135623730951}); +// }; + +// TYPED_TEST(PairwiseLinestringPolygonDistanceTest, MultiToMultiOnePair) +// { +// using T = TypeParam; + +// auto [ptype, points] = make_linestring_column({0, 1}, {0.0, 0.0}, this->stream()); + +// auto [polytype, polygons] = +// make_polygon_column({0, 1}, {0, 1}, {0, 4}, {1, 1, 1, 2, 2, 2, 1, 1}, this->stream()); + +// CUSPATIAL_RUN_TEST(this->run_single, +// geometry_column_view(points->view(), ptype, geometry_type_id::POINT), +// geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON), +// {1.4142135623730951}); +// }; + +// TEST_F(PairwiseLinestringPolygonDistanceTestUntyped, SizeMismatch) +// { +// auto [ptype, points] = make_linestring_column({0, 1, 2}, {0.0, 0.0, 1.0, 1.0}, this->stream()); + +// auto [polytype, polygons] = +// make_polygon_column({0, 1}, {0, 1}, {0, 4}, {1, 1, 1, 2, 2, 2, 1, 1}, this->stream()); + +// auto points_view = geometry_column_view(points->view(), ptype, geometry_type_id::POINT); +// auto polygons_view = geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON); + +// EXPECT_THROW(pairwise_point_polygon_distance(points_view, polygons_view), cuspatial::logic_error); +// }; + +// TEST_F(PairwiseLinestringPolygonDistanceTestUntyped, TypeMismatch) +// { +// auto [ptype, points] = make_linestring_column({0, 1}, {0.0, 0.0}, this->stream()); + +// auto [polytype, polygons] = +// make_polygon_column({0, 1}, {0, 1}, {0, 4}, {1, 1, 1, 2, 2, 2, 1, 1}, this->stream()); + +// auto points_view = geometry_column_view(points->view(), ptype, geometry_type_id::POINT); +// auto polygons_view = geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON); + +// EXPECT_THROW(pairwise_point_polygon_distance(points_view, polygons_view), cuspatial::logic_error); +// }; + +// TEST_F(PairwiseLinestringPolygonDistanceTestUntyped, WrongGeometryType) +// { +// auto [ltype, lines] = make_linestring_column({0, 1}, {0, 1}, {0.0, 0.0}, this->stream()); + +// auto [polytype, polygons] = +// make_polygon_column({0, 1}, {0, 1}, {0, 4}, {1, 1, 1, 2, 2, 2, 1, 1}, this->stream()); + +// auto lines_view = geometry_column_view(lines->view(), ltype, geometry_type_id::LINESTRING); +// auto polygons_view = geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON); + +// EXPECT_THROW(pairwise_point_polygon_distance(lines_view, polygons_view), cuspatial::logic_error); +// }; From 7d227490dae1a514b7b75c69c964bccdcb9e79ab Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 3 Apr 2023 14:37:18 -0700 Subject: [PATCH 31/54] add missing header --- .../experimental/detail/distance_utils.cuh | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 cpp/include/cuspatial/experimental/detail/distance_utils.cuh diff --git a/cpp/include/cuspatial/experimental/detail/distance_utils.cuh b/cpp/include/cuspatial/experimental/detail/distance_utils.cuh new file mode 100644 index 000000000..d4eda5bc1 --- /dev/null +++ b/cpp/include/cuspatial/experimental/detail/distance_utils.cuh @@ -0,0 +1,115 @@ +/* + * 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 + +#include +#include +#include + +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 { + using T = typename MultiPointRange::element_t; + + MultiPointRange multipoints; + MultiPolygonRange multipolygons; + + point_in_multipolygon_test_functor(MultiPointRange multipoints, MultiPolygonRange multipolygons) + : multipoints(multipoints), multipolygons(multipolygons) + { + } + + template + uint8_t __device__ operator()(IndexType pidx) + { + vec_2d const& point = multipoints.point(pidx); + 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); + }); + + return static_cast(intersects); + } +}; + +/** + * @brief 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. + * Caveat: may have load unbalanced kernel if input is skewed with non-uniform distribution with the + * number of polygons in multipolygon. + * + * @tparam MultiPointRange An instantiation of multipoint_range + * @tparam MultiPolygonRange An instantiation of multipolygon_range + * @param multipoints The range to the multipoints to compute + * @param multipolygons The range + * @param stream The CUDA stream on which to perform computations + * @return A uint8_t array, `1` if the multipoint intersects with the multipolygon, `0` otherwise. + */ +template +rmm::device_uvector point_polygon_intersects(MultiPointRange multipoints, + MultiPolygonRange multipolygons, + rmm::cuda_stream_view stream) +{ + using index_t = iterator_value_type; + 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}); + + // `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); + + 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, + offset_as_key_it + multipoints.num_points(), + point_intersects.begin(), + thrust::make_discard_iterator(), + multipoint_intersects.begin(), + thrust::logical_or()); + + return multipoint_intersects; +} + +} // namespace detail +} // namespace cuspatial From 90519444bfc45e803fc9349ce4655cacdcb87bf3 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 3 Apr 2023 14:37:48 -0700 Subject: [PATCH 32/54] style --- cpp/include/cuspatial/experimental/detail/distance_utils.cuh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/distance_utils.cuh b/cpp/include/cuspatial/experimental/detail/distance_utils.cuh index d4eda5bc1..d96613bd7 100644 --- a/cpp/include/cuspatial/experimental/detail/distance_utils.cuh +++ b/cpp/include/cuspatial/experimental/detail/distance_utils.cuh @@ -16,16 +16,16 @@ #include #include -#include #include +#include #include #include #include +#include #include #include -#include namespace cuspatial { namespace detail { From 5ed7616b65b8207fcc610354e31fddd409632b52 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 3 Apr 2023 16:06:54 -0700 Subject: [PATCH 33/54] add tests --- .../detail/ranges/multipoint_range.cuh | 9 +- .../experimental/ranges/multipoint_range.cuh | 8 +- .../linestring_polygon_distance_test.cpp | 272 +++++++++--------- 3 files changed, 151 insertions(+), 138 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multipoint_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multipoint_range.cuh index 13814708e..5b1ab4ec8 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multipoint_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multipoint_range.cuh @@ -54,10 +54,11 @@ struct to_multipoint_functor { } // namespace detail template -multipoint_range::multipoint_range(GeometryIterator geometry_begin, - GeometryIterator geometry_end, - VecIterator points_begin, - VecIterator points_end) +CUSPATIAL_HOST_DEVICE multipoint_range::multipoint_range( + GeometryIterator geometry_begin, + GeometryIterator geometry_end, + VecIterator points_begin, + VecIterator points_end) : _geometry_begin(geometry_begin), _geometry_end(geometry_end), _points_begin(points_begin), diff --git a/cpp/include/cuspatial/experimental/ranges/multipoint_range.cuh b/cpp/include/cuspatial/experimental/ranges/multipoint_range.cuh index 7117d28cc..82323e9ed 100644 --- a/cpp/include/cuspatial/experimental/ranges/multipoint_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multipoint_range.cuh @@ -57,10 +57,10 @@ class multipoint_range { /** * @brief Construct a new multipoint array object */ - multipoint_range(GeometryIterator geometry_begin, - GeometryIterator geometry_end, - VecIterator points_begin, - VecIterator points_end); + CUSPATIAL_HOST_DEVICE multipoint_range(GeometryIterator geometry_begin, + GeometryIterator geometry_end, + VecIterator points_begin, + VecIterator points_end); /** * @brief Returns the number of multipoints in the array. */ diff --git a/cpp/tests/spatial/linestring_polygon_distance_test.cpp b/cpp/tests/spatial/linestring_polygon_distance_test.cpp index 97497fbe2..fdef0661b 100644 --- a/cpp/tests/spatial/linestring_polygon_distance_test.cpp +++ b/cpp/tests/spatial/linestring_polygon_distance_test.cpp @@ -16,7 +16,6 @@ #include #include -#include #include #include @@ -27,12 +26,12 @@ #include #include -#include +#include -#include -#include +#include #include +#include using namespace cuspatial; using namespace cuspatial::test; @@ -41,7 +40,42 @@ using namespace cudf; using namespace cudf::test; template -struct PairwiseLinestringPolygonDistanceTest : BaseFixture { +struct PairwiseLinestringPolygonDistanceTest : ::testing::Test { + rmm::cuda_stream_view stream() { return cudf::get_default_stream(); } + void SetUp() + { + collection_type_id _; + std::tie(_, empty_linestring_column) = make_linestring_column({0}, {}, stream()); + std::tie(_, empty_multilinestring_column) = make_linestring_column({0}, {0}, {}, stream()); + std::tie(_, empty_polygon_column) = make_polygon_column({0}, {0}, {}, stream()); + std::tie(_, empty_multipolygon_column) = make_polygon_column({0}, {0}, {0}, {}, stream()); + } + + geometry_column_view empty_linestring() + { + return geometry_column_view( + empty_linestring_column->view(), collection_type_id::SINGLE, geometry_type_id::LINESTRING); + } + + geometry_column_view empty_multilinestring() + { + return geometry_column_view(empty_multilinestring_column->view(), + collection_type_id::MULTI, + geometry_type_id::LINESTRING); + } + + geometry_column_view empty_polygon() + { + return geometry_column_view( + empty_polygon_column->view(), collection_type_id::SINGLE, geometry_type_id::POLYGON); + } + + geometry_column_view empty_multipolygon() + { + return geometry_column_view( + empty_multipolygon_column->view(), collection_type_id::MULTI, geometry_type_id::POLYGON); + } + void run_single(geometry_column_view linestrings, geometry_column_view polygons, std::initializer_list expected) @@ -49,9 +83,15 @@ struct PairwiseLinestringPolygonDistanceTest : BaseFixture { auto got = pairwise_linestring_polygon_distance(linestrings, polygons); CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*got, fixed_width_column_wrapper(expected)); } + + std::unique_ptr empty_linestring_column; + std::unique_ptr empty_multilinestring_column; + std::unique_ptr empty_polygon_column; + std::unique_ptr empty_multipolygon_column; }; -struct PairwiseLinestringPolygonDistanceTestUntyped : BaseFixture { +struct PairwiseLinestringPolygonDistanceTestUntyped : testing::Test { + rmm::cuda_stream_view stream() { return cudf::get_default_stream(); } }; using TestTypes = ::testing::Types; @@ -60,159 +100,131 @@ TYPED_TEST_CASE(PairwiseLinestringPolygonDistanceTest, TestTypes); TYPED_TEST(PairwiseLinestringPolygonDistanceTest, SingleToSingleEmpty) { - using T = TypeParam; - - auto [ptype, points] = make_linestring_column({}, {}, {}, this->stream()); - - auto [polytype, polygons] = - make_polygon_column({0}, {0}, std::initializer_list{}, this->stream()); - - CUSPATIAL_RUN_TEST(this->run_single, - geometry_column_view(points->view(), ptype, geometry_type_id::POINT), - geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON), - {}); + CUSPATIAL_RUN_TEST(this->run_single, this->empty_linestring(), this->empty_polygon(), {}); }; -// TYPED_TEST(PairwiseLinestringPolygonDistanceTest, SingleToMultiEmpty) -// { -// using T = TypeParam; - -// auto [ptype, points] = make_linestring_column(std::initializer_list{}, this->stream()); - -// auto [polytype, polygons] = -// make_polygon_column({0}, {0}, {0}, std::initializer_list{}, this->stream()); - -// CUSPATIAL_RUN_TEST(this->run_single, -// geometry_column_view(points->view(), ptype, geometry_type_id::POINT), -// geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON), -// {}); -// }; - -// TYPED_TEST(PairwiseLinestringPolygonDistanceTest, MultiToSingleEmpty) -// { -// using T = TypeParam; - -// auto [ptype, points] = make_linestring_column({0}, std::initializer_list{}, this->stream()); - -// auto [polytype, polygons] = -// make_polygon_column({0}, {0}, std::initializer_list{}, this->stream()); - -// CUSPATIAL_RUN_TEST(this->run_single, -// geometry_column_view(points->view(), ptype, geometry_type_id::POINT), -// geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON), -// {}); -// }; - -// TYPED_TEST(PairwiseLinestringPolygonDistanceTest, MultiToMultiEmpty) -// { -// using T = TypeParam; - -// auto [ptype, points] = make_linestring_column({0}, std::initializer_list{}, this->stream()); +TYPED_TEST(PairwiseLinestringPolygonDistanceTest, SingleToMultiEmpty) +{ + CUSPATIAL_RUN_TEST(this->run_single, this->empty_linestring(), this->empty_multipolygon(), {}); +}; -// auto [polytype, polygons] = -// make_polygon_column({0}, {0}, {0}, std::initializer_list{}, this->stream()); +TYPED_TEST(PairwiseLinestringPolygonDistanceTest, MultiToSingleEmpty) +{ + CUSPATIAL_RUN_TEST(this->run_single, this->empty_multilinestring(), this->empty_polygon(), {}); +}; -// CUSPATIAL_RUN_TEST(this->run_single, -// geometry_column_view(points->view(), ptype, geometry_type_id::POINT), -// geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON), -// {}); -// }; +TYPED_TEST(PairwiseLinestringPolygonDistanceTest, MultiToMultiEmpty) +{ + CUSPATIAL_RUN_TEST( + this->run_single, this->empty_multilinestring(), this->empty_multipolygon(), {}); +}; -// TYPED_TEST(PairwiseLinestringPolygonDistanceTest, SingleToSingleOnePair) -// { -// using T = TypeParam; +TYPED_TEST(PairwiseLinestringPolygonDistanceTest, SingleToSingleOnePair) +{ + using T = TypeParam; -// auto [ptype, points] = make_linestring_column({0.0, 0.0}, this->stream()); + auto [ptype, linestrings] = + make_linestring_column({0, 2}, {0.0, 0.0, 1.0, 0.0}, this->stream()); -// auto [polytype, polygons] = -// make_polygon_column({0, 1}, {0, 4}, {1, 1, 1, 2, 2, 2, 1, 1}, this->stream()); + auto [polytype, polygons] = + make_polygon_column({0, 1}, {0, 4}, {1, 1, 1, 2, 2, 2, 1, 1}, this->stream()); -// CUSPATIAL_RUN_TEST(this->run_single, -// geometry_column_view(points->view(), ptype, geometry_type_id::POINT), -// geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON), -// {1.4142135623730951}); -// }; + CUSPATIAL_RUN_TEST(this->run_single, + geometry_column_view(linestrings->view(), ptype, geometry_type_id::LINESTRING), + geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON), + {1.0}); +}; -// TYPED_TEST(PairwiseLinestringPolygonDistanceTest, SingleToMultiOnePair) -// { -// using T = TypeParam; +TYPED_TEST(PairwiseLinestringPolygonDistanceTest, SingleToMultiOnePair) +{ + using T = TypeParam; -// auto [ptype, points] = make_linestring_column({0.0, 0.0}, this->stream()); + auto [ptype, linestrings] = + make_linestring_column({0, 2}, {0.0, 0.0, 1.0, 0.0}, this->stream()); -// auto [polytype, polygons] = -// make_polygon_column({0, 1}, {0, 1}, {0, 4}, {1, 1, 1, 2, 2, 2, 1, 1}, this->stream()); + auto [polytype, polygons] = + make_polygon_column({0, 1}, {0, 1}, {0, 4}, {1, 1, 1, 2, 2, 2, 1, 1}, this->stream()); -// CUSPATIAL_RUN_TEST(this->run_single, -// geometry_column_view(points->view(), ptype, geometry_type_id::POINT), -// geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON), -// {1.4142135623730951}); -// }; + CUSPATIAL_RUN_TEST(this->run_single, + geometry_column_view(linestrings->view(), ptype, geometry_type_id::LINESTRING), + geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON), + {1.0}); +}; -// TYPED_TEST(PairwiseLinestringPolygonDistanceTest, MultiToSingleOnePair) -// { -// using T = TypeParam; +TYPED_TEST(PairwiseLinestringPolygonDistanceTest, MultiToSingleOnePair) +{ + using T = TypeParam; -// auto [ptype, points] = make_linestring_column({0, 1}, {0.0, 0.0}, this->stream()); + auto [ptype, linestrings] = + make_linestring_column({0, 1}, {0, 2}, {0.0, 0.0, 1.0, 0.0}, this->stream()); -// auto [polytype, polygons] = -// make_polygon_column({0, 1}, {0, 4}, {1, 1, 1, 2, 2, 2, 1, 1}, this->stream()); + auto [polytype, polygons] = + make_polygon_column({0, 1}, {0, 4}, {1, 1, 1, 2, 2, 2, 1, 1}, this->stream()); -// CUSPATIAL_RUN_TEST(this->run_single, -// geometry_column_view(points->view(), ptype, geometry_type_id::POINT), -// geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON), -// {1.4142135623730951}); -// }; + CUSPATIAL_RUN_TEST(this->run_single, + geometry_column_view(linestrings->view(), ptype, geometry_type_id::LINESTRING), + geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON), + {1.0}); +}; -// TYPED_TEST(PairwiseLinestringPolygonDistanceTest, MultiToMultiOnePair) -// { -// using T = TypeParam; +TYPED_TEST(PairwiseLinestringPolygonDistanceTest, MultiToMultiOnePair) +{ + using T = TypeParam; -// auto [ptype, points] = make_linestring_column({0, 1}, {0.0, 0.0}, this->stream()); + auto [ptype, linestrings] = + make_linestring_column({0, 1}, {0, 2}, {0.0, 0.0, 1.0, 0.0}, this->stream()); -// auto [polytype, polygons] = -// make_polygon_column({0, 1}, {0, 1}, {0, 4}, {1, 1, 1, 2, 2, 2, 1, 1}, this->stream()); + auto [polytype, polygons] = + make_polygon_column({0, 1}, {0, 1}, {0, 4}, {1, 1, 1, 2, 2, 2, 1, 1}, this->stream()); -// CUSPATIAL_RUN_TEST(this->run_single, -// geometry_column_view(points->view(), ptype, geometry_type_id::POINT), -// geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON), -// {1.4142135623730951}); -// }; + CUSPATIAL_RUN_TEST(this->run_single, + geometry_column_view(linestrings->view(), ptype, geometry_type_id::LINESTRING), + geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON), + {1.0}); +}; -// TEST_F(PairwiseLinestringPolygonDistanceTestUntyped, SizeMismatch) -// { -// auto [ptype, points] = make_linestring_column({0, 1, 2}, {0.0, 0.0, 1.0, 1.0}, this->stream()); +TEST_F(PairwiseLinestringPolygonDistanceTestUntyped, SizeMismatch) +{ + auto [ptype, linestrings] = + make_linestring_column({0, 1, 2}, {0, 1, 2}, {0.0, 0.0, 1.0, 1.0}, this->stream()); -// auto [polytype, polygons] = -// make_polygon_column({0, 1}, {0, 1}, {0, 4}, {1, 1, 1, 2, 2, 2, 1, 1}, this->stream()); + auto [polytype, polygons] = + make_polygon_column({0, 1}, {0, 1}, {0, 4}, {1, 1, 1, 2, 2, 2, 1, 1}, this->stream()); -// auto points_view = geometry_column_view(points->view(), ptype, geometry_type_id::POINT); -// auto polygons_view = geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON); + auto linestrings_view = + geometry_column_view(linestrings->view(), ptype, geometry_type_id::LINESTRING); + auto polygons_view = geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON); -// EXPECT_THROW(pairwise_point_polygon_distance(points_view, polygons_view), cuspatial::logic_error); -// }; + EXPECT_THROW(pairwise_linestring_polygon_distance(linestrings_view, polygons_view), + cuspatial::logic_error); +}; -// TEST_F(PairwiseLinestringPolygonDistanceTestUntyped, TypeMismatch) -// { -// auto [ptype, points] = make_linestring_column({0, 1}, {0.0, 0.0}, this->stream()); +TEST_F(PairwiseLinestringPolygonDistanceTestUntyped, TypeMismatch) +{ + auto [ptype, linestrings] = + make_linestring_column({0, 1}, {0, 1}, {0.0, 0.0}, this->stream()); -// auto [polytype, polygons] = -// make_polygon_column({0, 1}, {0, 1}, {0, 4}, {1, 1, 1, 2, 2, 2, 1, 1}, this->stream()); + auto [polytype, polygons] = + make_polygon_column({0, 1}, {0, 1}, {0, 4}, {1, 1, 1, 2, 2, 2, 1, 1}, this->stream()); -// auto points_view = geometry_column_view(points->view(), ptype, geometry_type_id::POINT); -// auto polygons_view = geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON); + auto linestrings_view = + geometry_column_view(linestrings->view(), ptype, geometry_type_id::LINESTRING); + auto polygons_view = geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON); -// EXPECT_THROW(pairwise_point_polygon_distance(points_view, polygons_view), cuspatial::logic_error); -// }; + EXPECT_THROW(pairwise_linestring_polygon_distance(linestrings_view, polygons_view), + cuspatial::logic_error); +}; -// TEST_F(PairwiseLinestringPolygonDistanceTestUntyped, WrongGeometryType) -// { -// auto [ltype, lines] = make_linestring_column({0, 1}, {0, 1}, {0.0, 0.0}, this->stream()); +TEST_F(PairwiseLinestringPolygonDistanceTestUntyped, WrongGeometryType) +{ + auto [ptype, points] = make_point_column({0, 1}, {0.0, 0.0}, this->stream()); -// auto [polytype, polygons] = -// make_polygon_column({0, 1}, {0, 1}, {0, 4}, {1, 1, 1, 2, 2, 2, 1, 1}, this->stream()); + auto [polytype, polygons] = + make_polygon_column({0, 1}, {0, 1}, {0, 4}, {1, 1, 1, 2, 2, 2, 1, 1}, this->stream()); -// auto lines_view = geometry_column_view(lines->view(), ltype, geometry_type_id::LINESTRING); -// auto polygons_view = geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON); + auto points_view = geometry_column_view(points->view(), ptype, geometry_type_id::POINT); + auto polygons_view = geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON); -// EXPECT_THROW(pairwise_point_polygon_distance(lines_view, polygons_view), cuspatial::logic_error); -// }; + EXPECT_THROW(pairwise_linestring_polygon_distance(points_view, polygons_view), + cuspatial::logic_error); +}; From a160f714a450dd9d8a55dd0709e96406d0023183 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 3 Apr 2023 16:14:04 -0700 Subject: [PATCH 34/54] style --- .../cuspatial/distance/linestring_polygon_distance.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/include/cuspatial/distance/linestring_polygon_distance.hpp b/cpp/include/cuspatial/distance/linestring_polygon_distance.hpp index 07450e94d..7fca8e09d 100644 --- a/cpp/include/cuspatial/distance/linestring_polygon_distance.hpp +++ b/cpp/include/cuspatial/distance/linestring_polygon_distance.hpp @@ -36,8 +36,8 @@ namespace cuspatial { * * @throw cuspatial::logic_error if `multilinestrings` and `multipolygons` has different coordinate * types. - * @throw cuspatial::logic_error if `multilinestrings` is not a linestring column and `multipolygons` is not a - * polygon column. + * @throw cuspatial::logic_error if `multilinestrings` is not a linestring column and + * `multipolygons` is not a polygon column. * @throw cuspatial::logic_error if input column sizes mismatch. */ From 23dca2144721c0005c715653bf01351891143880 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 3 Apr 2023 17:17:38 -0700 Subject: [PATCH 35/54] initial python bindings and tests --- python/cuspatial/cuspatial/__init__.py | 1 + python/cuspatial/cuspatial/_lib/distance.pyx | 30 +++++++ .../cuspatial/core/spatial/__init__.py | 2 + .../cuspatial/core/spatial/distance.py | 83 ++++++++++++++++++- 4 files changed, 115 insertions(+), 1 deletion(-) diff --git a/python/cuspatial/cuspatial/__init__.py b/python/cuspatial/cuspatial/__init__.py index 2f3a27267..b281c80c4 100644 --- a/python/cuspatial/cuspatial/__init__.py +++ b/python/cuspatial/cuspatial/__init__.py @@ -7,6 +7,7 @@ join_quadtree_and_bounding_boxes, linestring_bounding_boxes, pairwise_linestring_distance, + pairwise_linestring_polygon_distance, pairwise_point_distance, pairwise_point_linestring_distance, pairwise_point_linestring_nearest_points, diff --git a/python/cuspatial/cuspatial/_lib/distance.pyx b/python/cuspatial/cuspatial/_lib/distance.pyx index f3d68717e..972a112c8 100644 --- a/python/cuspatial/cuspatial/_lib/distance.pyx +++ b/python/cuspatial/cuspatial/_lib/distance.pyx @@ -13,6 +13,9 @@ from cuspatial._lib.cpp.column.geometry_column_view cimport ( from cuspatial._lib.cpp.distance.linestring_distance cimport ( pairwise_linestring_distance as c_pairwise_linestring_distance, ) +from cuspatial._lib.cpp.distance.linestring_polygon_distance cimport ( + pairwise_linestring_polygon_distance as c_pairwise_line_poly_dist, +) from cuspatial._lib.cpp.distance.point_distance cimport ( pairwise_point_distance as c_pairwise_point_distance, ) @@ -142,3 +145,30 @@ def pairwise_point_polygon_distance( )) return Column.from_unique_ptr(move(c_result)) + + +def pairwise_linestring_polygon_distance( + Column multilinestrings, + Column multipolygons +): + + cdef shared_ptr[geometry_column_view] c_multilinestrings = \ + make_shared[geometry_column_view]( + multilinestrings.view(), + collection_type_id.MULTI, + geometry_type_id.LINESTRING) + + cdef shared_ptr[geometry_column_view] c_multipolygons = \ + make_shared[geometry_column_view]( + multipolygons.view(), + collection_type_id.MULTI, + geometry_type_id.POLYGON) + + cdef unique_ptr[column] c_result + + with nogil: + c_result = move(c_pairwise_line_poly_dist( + c_multilinestrings.get()[0], c_multipolygons.get()[0] + )) + + return Column.from_unique_ptr(move(c_result)) diff --git a/python/cuspatial/cuspatial/core/spatial/__init__.py b/python/cuspatial/cuspatial/core/spatial/__init__.py index ee07838f9..756e5ec88 100644 --- a/python/cuspatial/cuspatial/core/spatial/__init__.py +++ b/python/cuspatial/cuspatial/core/spatial/__init__.py @@ -5,6 +5,7 @@ directed_hausdorff_distance, haversine_distance, pairwise_linestring_distance, + pairwise_linestring_polygon_distance, pairwise_point_distance, pairwise_point_linestring_distance, pairwise_point_polygon_distance, @@ -27,6 +28,7 @@ "sinusoidal_projection", "pairwise_point_distance", "pairwise_linestring_distance", + "pairwise_linestring_polygon_distance", "pairwise_point_polygon_distance", "pairwise_point_linestring_distance", "pairwise_point_linestring_nearest_points", diff --git a/python/cuspatial/cuspatial/core/spatial/distance.py b/python/cuspatial/cuspatial/core/spatial/distance.py index 5d61e0564..47d861f34 100644 --- a/python/cuspatial/cuspatial/core/spatial/distance.py +++ b/python/cuspatial/cuspatial/core/spatial/distance.py @@ -8,6 +8,7 @@ from cuspatial._lib.distance import ( pairwise_linestring_distance as cpp_pairwise_linestring_distance, + pairwise_linestring_polygon_distance as c_pairwise_line_poly_dist, pairwise_point_distance as cpp_pairwise_point_distance, pairwise_point_linestring_distance as c_pairwise_point_linestring_distance, pairwise_point_polygon_distance as c_pairwise_point_polygon_distance, @@ -446,7 +447,7 @@ def pairwise_point_polygon_distance(points: GeoSeries, polygons: GeoSeries): raise ValueError("`points` array must contain only points") if not contains_only_polygons(polygons): - raise ValueError("`linestrings` array must contain only linestrings") + raise ValueError("`polygons` array must contain only polygons") if len(points.points.xy) > 0 and len(points.multipoints.xy) > 0: raise NotImplementedError( @@ -483,6 +484,86 @@ def pairwise_point_polygon_distance(points: GeoSeries, polygons: GeoSeries): ) +def pairwise_linestring_polygon_distance( + linestrings: GeoSeries, polygons: GeoSeries +): + """Compute distance between pairs of (multi)linestrings and (multi)polygons + + The distance between a (multi)point and a (multi)polygon + is defined as the shortest distance between every point in the + multipoint and every edge of the (multi)polygon. If the multipoint and + multipolygon intersects, the distance is 0. + + This algorithm computes distance pairwise. The ith row in the result is + the distance between the ith (multi)point in `linestrings` and the ith + (multi)polygon in `polygons`. + + Parameters + ---------- + linestrings : GeoSeries + The (multi)linestrings to compute the distance from. + polygons : GeoSeries + The (multi)polygons to compute the distance from. + + Returns + ------- + distance : cudf.Series + + Notes + ----- + The input `GeoSeries` must contain a single type geometry. + For example, `linestrings` series cannot contain both linestrings and + polygons. + + Examples + -------- + Compute distance between a point and a polygon: + >>> from shapely.geometry import Point, LineString + >>> linestrings = cuspatial.GeoSeries([Point(0, 0)]) + >>> polygons = cuspatial.GeoSeries([Point(1, 1).buffer(0.5)]) + >>> cuspatial.pairwise_point_polygon_distance(linestrings, polygons) + 0 0.914214 + dtype: float64 + + Compute distance between a multipoint and a multipolygon + + >>> from shapely.geometry import MultiLineString + >>> mlinestrings = cuspatial.GeoSeries([ + MultiLinestring([[(0, 0), (1, 1)], [(10, 10), (11, 11)]])]) + >>> mpolys = cuspatial.GeoSeries([ + ... MultiPoint([Point(2, 2), Point(1, 2)]).buffer(0.5)]) + >>> cuspatial.pairwise_point_polygon_distance(mlinestrings, mpolys) + 0 0.5 + dtype: float64 + """ + + if len(linestrings) != len(polygons): + raise ValueError("Unmatched input geoseries length.") + + if len(linestrings) == 0: + return cudf.Series(dtype=linestrings.lines.xy.dtype) + + if not contains_only_linestrings(linestrings): + raise ValueError("`points` array must contain only points") + + if not contains_only_polygons(polygons): + raise ValueError("`polygon` array must contain only polygons") + + # Handle slicing in geoseries + linestrings_column = linestrings._column.lines._column.take( + linestrings._column._meta.union_offsets._column + ) + + polygon_column = polygons._column.polygons._column + polygon_column = polygon_column.take( + polygons._column._meta.union_offsets._column + ) + + return Series._from_data( + {None: c_pairwise_line_poly_dist(linestrings_column, polygon_column)} + ) + + def _flatten_point_series( points: GeoSeries, ) -> Tuple[ From 66e5206a9de6ec70e7374742e42efd80ab07b446 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 3 Apr 2023 17:22:24 -0700 Subject: [PATCH 36/54] add tests --- .../distance/linestring_polygon_distance.pxd | 17 +++++++ ...st_pairwise_linestring_polygon_distance.py | 49 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 python/cuspatial/cuspatial/_lib/cpp/distance/linestring_polygon_distance.pxd create mode 100644 python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_linestring_polygon_distance.py diff --git a/python/cuspatial/cuspatial/_lib/cpp/distance/linestring_polygon_distance.pxd b/python/cuspatial/cuspatial/_lib/cpp/distance/linestring_polygon_distance.pxd new file mode 100644 index 000000000..82a4f1834 --- /dev/null +++ b/python/cuspatial/cuspatial/_lib/cpp/distance/linestring_polygon_distance.pxd @@ -0,0 +1,17 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. + +from libcpp.memory cimport unique_ptr + +from cudf._lib.cpp.column.column cimport column + +from cuspatial._lib.cpp.column.geometry_column_view cimport ( + geometry_column_view, +) + + +cdef extern from "cuspatial/distance/linestring_polygon_distance.hpp" \ + namespace "cuspatial" nogil: + cdef unique_ptr[column] pairwise_linestring_polygon_distance( + const geometry_column_view & multilinestrings, + const geometry_column_view & multipolygons + ) except + diff --git a/python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_linestring_polygon_distance.py b/python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_linestring_polygon_distance.py new file mode 100644 index 000000000..3acb7d2fa --- /dev/null +++ b/python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_linestring_polygon_distance.py @@ -0,0 +1,49 @@ +import geopandas as gpd +import pytest +from shapely.geometry import MultiLineString, LineString, MultiPolygon, Polygon + +import cudf +from cudf.testing import assert_series_equal + +import cuspatial + + +def test_linestring_polygon_empty(): + lhs = cuspatial.GeoSeries.from_linestrings_xy([], [0], [0]) + rhs = cuspatial.GeoSeries.from_polygons_xy([], [0], [0], [0]) + + got = cuspatial.pairwise_linestring_polygon_distance(lhs, rhs) + + expect = cudf.Series([], dtype="f8") + + assert_series_equal(got, expect) + + +@pytest.mark.parametrize( + "linestrings", [[LineString([(0, 0), (1, 1)])], [MultiLineString([[(1, 1), (2, 2)], [(10, 10), (11, 11)]])]] +) +@pytest.mark.parametrize( + "polygons", + [ + [Polygon([(0, 1), (1, 0), (-1, 0), (0, 1)])], + [ + MultiPolygon( + [ + Polygon([(-2, 0), (-1, 0), (-1, -1), (-2, 0)]), + Polygon([(1, 0), (2, 0), (1, -1), (1, 0)]), + ] + ) + ], + ], +) +def test_one_pair(linestrings, polygons): + lhs = gpd.GeoSeries(linestrings) + rhs = gpd.GeoSeries(polygons) + + dlhs = cuspatial.GeoSeries(linestrings) + drhs = cuspatial.GeoSeries(polygons) + + expect = lhs.distance(rhs) + got = cuspatial.pairwise_linestring_polygon_distance(dlhs, drhs) + + assert_series_equal(got, cudf.Series(expect)) From 52d228ce2b7b953f81c811fe6af6a62b827b0644 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 3 Apr 2023 20:04:42 -0700 Subject: [PATCH 37/54] fix OB access error --- .../detail/linestring_polygon_distance.cuh | 50 ++++++++++++++++--- .../linestring_polygon_distance_test.cu | 2 +- ...st_pairwise_linestring_polygon_distance.py | 8 ++- 3 files changed, 49 insertions(+), 11 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh b/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh index 6775120b3..37e9b1273 100644 --- a/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/linestring_polygon_distance.cuh @@ -16,6 +16,8 @@ #pragma once +#include + #include "distance_utils.cuh" #include @@ -83,6 +85,13 @@ pairwise_linestring_polygon_distance_kernel(MultiLinestringRange multilinestring auto geometry_id = thrust::distance(thread_bounds.begin(), it); auto local_idx = idx - *it; + // if (geometry_id == 0) + // printf("idx: %d, geometry_id: %d, intersects?: %d, local_idx: %d\n", + // static_cast(idx), + // static_cast(geometry_id), + // static_cast(intersects[geometry_id]), + // static_cast(local_idx)); + if (intersects[geometry_id]) { distances[geometry_id] = 0.0f; continue; @@ -98,9 +107,29 @@ pairwise_linestring_polygon_distance_kernel(MultiLinestringRange multilinestring auto multipolygon_segment_id = local_idx / num_segment_this_multilinestring + multipolygons_segment_offsets[geometry_id]; + // if (geometry_id == 0) + // printf( + // "multilinestring_segment_id: %d, " + // "multipolygon_segment_id: %d\n", + // static_cast(multilinestring_segment_id), + // static_cast(multipolygon_segment_id)); + auto [a, b] = multilinestrings.segment_begin()[multilinestring_segment_id]; auto [c, d] = multipolygons.segment_begin()[multipolygon_segment_id]; + auto distance = sqrt(squared_segment_distance(a, b, c, d)); + // if (geometry_id == 0) + // printf("ab: (%f, %f) -> (%f, %f), cd: (%f, %f) -> (%f, %f), dist: %f\n", + // static_cast(a.x), + // static_cast(a.y), + // static_cast(b.x), + // static_cast(b.y), + // static_cast(c.x), + // static_cast(c.y), + // static_cast(d.x), + // static_cast(d.y), + // static_cast(distance)); + atomicMin(&distances[geometry_id], sqrt(squared_segment_distance(a, b, c, d))); } }; @@ -146,20 +175,25 @@ OutputIt pairwise_linestring_polygon_distance(MultiLinestringRange multilinestri // Compute offsets to the first segment of each multilinestring and multipolygon auto multilinestring_segment_offsets = rmm::device_uvector(multilinestrings.num_multilinestrings() + 1, stream); + detail::zero_data_async( + multilinestring_segment_offsets.begin(), multilinestring_segment_offsets.end(), stream); + auto multipolygon_segment_offsets = rmm::device_uvector(multipolygons.num_multipolygons() + 1, stream); + detail::zero_data_async( + multipolygon_segment_offsets.begin(), multipolygon_segment_offsets.end(), stream); - thrust::exclusive_scan( - rmm::exec_policy(stream), - multilinestrings.multilinestring_segment_count_begin(), - multilinestrings.multilinestring_segment_count_begin() + multilinestring_segment_offsets.size(), - multilinestring_segment_offsets.begin()); + thrust::inclusive_scan(rmm::exec_policy(stream), + multilinestrings.multilinestring_segment_count_begin(), + multilinestrings.multilinestring_segment_count_begin() + + multilinestrings.num_multilinestrings(), + thrust::next(multilinestring_segment_offsets.begin())); - thrust::exclusive_scan( + thrust::inclusive_scan( rmm::exec_policy(stream), multipolygons.multipolygon_segment_count_begin(), - multipolygons.multipolygon_segment_count_begin() + multipolygon_segment_offsets.size(), - multipolygon_segment_offsets.begin()); + multipolygons.multipolygon_segment_count_begin() + multipolygons.num_multipolygons(), + thrust::next(multipolygon_segment_offsets.begin())); // Initialize output range thrust::fill(rmm::exec_policy(stream), diff --git a/cpp/tests/experimental/spatial/linestring_polygon_distance_test.cu b/cpp/tests/experimental/spatial/linestring_polygon_distance_test.cu index 09ab4602f..cdc9f7651 100644 --- a/cpp/tests/experimental/spatial/linestring_polygon_distance_test.cu +++ b/cpp/tests/experimental/spatial/linestring_polygon_distance_test.cu @@ -142,7 +142,7 @@ TYPED_TEST(PairwiseLinestringPolygonDistanceTest, OnePair111Crosses) {P{0, 0}, P{1, 1}, P{2, 2}, P{3, 3}}, {0, 1}, {0, 1}, - {0, 5}, + {0, 4}, {P{-1, 0}, P{1, 0}, P{0, 1}, P{-1, 0}}, {0}); } diff --git a/python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_linestring_polygon_distance.py b/python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_linestring_polygon_distance.py index 3acb7d2fa..95eab95ab 100644 --- a/python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_linestring_polygon_distance.py +++ b/python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_linestring_polygon_distance.py @@ -1,6 +1,6 @@ import geopandas as gpd import pytest -from shapely.geometry import MultiLineString, LineString, MultiPolygon, Polygon +from shapely.geometry import LineString, MultiLineString, MultiPolygon, Polygon import cudf from cudf.testing import assert_series_equal @@ -20,7 +20,11 @@ def test_linestring_polygon_empty(): @pytest.mark.parametrize( - "linestrings", [[LineString([(0, 0), (1, 1)])], [MultiLineString([[(1, 1), (2, 2)], [(10, 10), (11, 11)]])]] + "linestrings", + [ + [LineString([(0, 0), (1, 1)])], + [MultiLineString([[(1, 1), (2, 2)], [(10, 10), (11, 11)]])], + ], ) @pytest.mark.parametrize( "polygons", From dbd2e97d1d65cd1449e81114a5e1fb3c6aa445b5 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 3 Apr 2023 20:05:26 -0700 Subject: [PATCH 38/54] add large test --- ...st_pairwise_linestring_polygon_distance.py | 62 +++++++++++++++++++ .../test_pairwise_point_polygon_distance.py | 7 --- 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_linestring_polygon_distance.py b/python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_linestring_polygon_distance.py index 95eab95ab..213149b3d 100644 --- a/python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_linestring_polygon_distance.py +++ b/python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_linestring_polygon_distance.py @@ -51,3 +51,65 @@ def test_one_pair(linestrings, polygons): got = cuspatial.pairwise_linestring_polygon_distance(dlhs, drhs) assert_series_equal(got, cudf.Series(expect)) + + +@pytest.mark.parametrize( + "linestrings", + [ + [LineString([(0, 0), (1, 1)]), LineString([(10, 10), (11, 11)])], + [ + MultiLineString([[(1, 1), (2, 2)], [(3, 3), (4, 4)]]), + MultiLineString([[(10, 10), (11, 11)], [(12, 12), (13, 13)]]), + ], + ], +) +@pytest.mark.parametrize( + "polygons", + [ + [ + Polygon([(0, 1), (1, 0), (-1, 0), (0, 1)]), + Polygon([(-4, -4), (-4, -5), (-5, -5), (-5, -4), (-5, -5)]), + ], + [ + MultiPolygon( + [ + Polygon([(0, 1), (1, 0), (-1, 0), (0, 1)]), + Polygon([(0, 1), (1, 0), (0, -1), (-1, 0), (0, 1)]), + ] + ), + MultiPolygon( + [ + Polygon( + [(-4, -4), (-4, -5), (-5, -5), (-5, -4), (-5, -5)] + ), + Polygon([(-2, 0), (-2, -2), (0, -2), (0, 0), (-2, 0)]), + ] + ), + ], + ], +) +def test_two_pair(linestrings, polygons): + lhs = gpd.GeoSeries(linestrings) + rhs = gpd.GeoSeries(polygons) + + dlhs = cuspatial.GeoSeries(linestrings) + drhs = cuspatial.GeoSeries(polygons) + + expect = lhs.distance(rhs) + got = cuspatial.pairwise_linestring_polygon_distance(dlhs, drhs) + + assert_series_equal(got, cudf.Series(expect)) + + +def test_linestring_polygon_large(linestring_generator, polygon_generator): + N = 100 + linestrings = gpd.GeoSeries(linestring_generator(N, 5)) + polygons = gpd.GeoSeries(polygon_generator(N, 10.0, 3.0)) + + dlinestrings = cuspatial.from_geopandas(linestrings) + dpolygons = cuspatial.from_geopandas(polygons) + + expect = linestrings.distance(polygons) + got = cuspatial.pairwise_linestring_polygon_distance(dlinestrings, dpolygons) + + assert_series_equal(got, cudf.Series(expect)) diff --git a/python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_point_polygon_distance.py b/python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_point_polygon_distance.py index 199c41208..49fabec39 100644 --- a/python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_point_polygon_distance.py +++ b/python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_point_polygon_distance.py @@ -124,13 +124,6 @@ def test_point_polygon_geocities(naturalearth_cities, naturalearth_lowres): gpu_cities = cuspatial.from_geopandas(naturalearth_cities.geometry) gpu_countries = cuspatial.from_geopandas(naturalearth_lowres.geometry) - print( - len(naturalearth_lowres), - len(naturalearth_lowres[: len(naturalearth_cities)]), - len(gpu_countries), - len(gpu_countries[: len(naturalearth_cities)]), - ) - expect = naturalearth_cities.geometry[:N].distance( naturalearth_lowres.geometry[:N] ) From 0792c4eca2f650ea80fc801f9426ab9e04278f1f Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 10 Apr 2023 10:12:43 -0700 Subject: [PATCH 39/54] initial --- .../experimental/detail/polygon_distance.cuh | 87 +++++++++++++++++++ .../experimental/polygon_distance.cuh | 47 ++++++++++ 2 files changed, 134 insertions(+) create mode 100644 cpp/include/cuspatial/experimental/detail/polygon_distance.cuh create mode 100644 cpp/include/cuspatial/experimental/polygon_distance.cuh diff --git a/cpp/include/cuspatial/experimental/detail/polygon_distance.cuh b/cpp/include/cuspatial/experimental/detail/polygon_distance.cuh new file mode 100644 index 000000000..80f7df855 --- /dev/null +++ b/cpp/include/cuspatial/experimental/detail/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 "distance_utils.cuh" +#include "linestring_distance.cuh" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace cuspatial { + +template +OutputIt pairwise_polygon_distance(MultiPolygonRangeA lhs, + MultiPolygonRangeB rhs, + OutputIt distances_first, + rmm::cuda_stream_view stream) +{ + using T = typename MultipolygonRangeA::element_t; + using index_t = typename MultipolygonRangeB::index_t; + + CUSPATIAL_EXPECTS(lhs.size() == rhs.size(), "Must have the same number of input rows."); + + if (lhs.size() == 0) return distances_first; + + auto lhs_as_multipoints = lhs.as_multipoint_range(); + auto rhs_as_multipoints = rhs.as_multipoint_range(); + + auto intersects = [&lhs, &rhs, &stream]() { + auto lhs_in_rhs = point_polygon_intersects(lhs_as_multipoints, rhs, stream); + auto rhs_in_lhs = point_polygon_intersects(rhs_as_multipoints, lhs, stream); + rmm::device_uvector intersects(lhs_in_rhs.size(), stream); + thrust::transform(rmm::exec_policy(stream), + lhs_in_rhs.begin(), + lhs_in_rhs.end(), + rhs_in_lhs.begin(), + intersects.begin(), + thrust::logical_or{}); + return intersects; + }(); + + + auto lhs_as_multilinestrings = lhs.as_multilinestring_range(); + auto rhs_as_multilinestrings = rhs.as_multilinestring_range(); + + return pairwise_linestring_distance(lhs_as_multilinestrings, rhs_as_multilinestrings, distances_first, stream); + +} + +} // namespace cuspatial diff --git a/cpp/include/cuspatial/experimental/polygon_distance.cuh b/cpp/include/cuspatial/experimental/polygon_distance.cuh new file mode 100644 index 000000000..0289396a8 --- /dev/null +++ b/cpp/include/cuspatial/experimental/polygon_distance.cuh @@ -0,0 +1,47 @@ +/* + * 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 + * @brief Computes pairwise multipoint to multipolygon distance + * + * @tparam MultiPolygonRangeA An instance of template type `multipolygon_range` + * @tparam MultiPolygonRangeB 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 multipolygons, one per computed distance pair. + * @param multipolygons Range of multipolygons, 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 + * "LegacyRandomAccessIterator" + */ +template +OutputIt pairwise_polygon_distance(MultiPolygonRangeA lhs, + MultiPolygonRangeB rhs, + OutputIt distances_first, + rmm::cuda_stream_view stream = rmm::cuda_stream_default); +} // namespace cuspatial + +#include From 1f7c67446280ce72f9d1d698ce32020fb13733ff Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 10 Apr 2023 13:25:32 -0700 Subject: [PATCH 40/54] add range cast methods --- .../detail/algorithm/linestring_distance.cuh | 73 +++++++++++ .../detail/linestring_distance.cuh | 45 +------ .../experimental/detail/polygon_distance.cuh | 9 +- .../detail/ranges/multipolygon_range.cuh | 35 +++++ .../ranges/multipolygon_range.cuh | 9 ++ .../cuspatial_test/vector_equality.hpp | 18 +++ .../cuspatial_test/vector_factories.cuh | 40 ++++++ .../range/multipolygon_range_test.cu | 120 ++++++++++++++++++ 8 files changed, 306 insertions(+), 43 deletions(-) create mode 100644 cpp/include/cuspatial/experimental/detail/algorithm/linestring_distance.cuh diff --git a/cpp/include/cuspatial/experimental/detail/algorithm/linestring_distance.cuh b/cpp/include/cuspatial/experimental/detail/algorithm/linestring_distance.cuh new file mode 100644 index 000000000..8ce192943 --- /dev/null +++ b/cpp/include/cuspatial/experimental/detail/algorithm/linestring_distance.cuh @@ -0,0 +1,73 @@ +/* + * 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 + +namespace cuspatial { +namespace detail { + +/** + * @internal + * @brief The kernel to compute linestring to linestring distance + * + * Each thread of the kernel computes the distance between a segment in a linestring in pair 1 to a + * linestring in pair 2. For a segment in pair 1, the linestring index is looked up from the offset + * array and mapped to the linestring in the pair 2. The segment is then computed with all segments + * in the corresponding linestring in pair 2. This forms a local minima of the shortest distance, + * which is then combined with other segment results via an atomic operation to form the global + * minimum distance between the linestrings. + * + * `intersects` is an optional pointer to a boolean range where the `i`th element indicates the `i`th + * output should be set to 0 and bypass distance computation. This argument is optional, if set to nullopt, + * no distance computation will be bypassed. + */ +template +__global__ void linestring_distance(MultiLinestringRange1 multilinestrings1, + MultiLinestringRange2 multilinestrings2, + thrust::optional intersects, + OutputIt distances_first) +{ + using T = typename MultiLinestringRange1::element_t; + + for (auto idx = threadIdx.x + blockIdx.x * blockDim.x; idx < multilinestrings1.num_points(); + idx += gridDim.x * blockDim.x) { + auto const part_idx = multilinestrings1.part_idx_from_point_idx(idx); + if (!multilinestrings1.is_valid_segment_id(idx, part_idx)) continue; + auto const geometry_idx = multilinestrings1.geometry_idx_from_part_idx(part_idx); + + if (intersects.has_value() && intersects.value()[geometry_idx]) + { + distances_first[geometry_idx] = 0; + continue; + } + + auto [a, b] = multilinestrings1.segment(idx); + T min_distance_squared = std::numeric_limits::max(); + + for (auto const& linestring2 : multilinestrings2[geometry_idx]) { + for (auto [c, d] : linestring2) { + min_distance_squared = min(min_distance_squared, squared_segment_distance(a, b, c, d)); + } + } + atomicMin(&distances_first[geometry_idx], static_cast(sqrt(min_distance_squared))); + } +} + +} // namespace detail +} // namespace cuspatial diff --git a/cpp/include/cuspatial/experimental/detail/linestring_distance.cuh b/cpp/include/cuspatial/experimental/detail/linestring_distance.cuh index 97648d74b..7077aaa6d 100644 --- a/cpp/include/cuspatial/experimental/detail/linestring_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/linestring_distance.cuh @@ -16,8 +16,7 @@ #pragma once -#include -#include +#include #include #include #include @@ -26,49 +25,13 @@ #include #include +#include #include #include namespace cuspatial { -namespace detail { -/** - * @internal - * @brief The kernel to compute linestring to linestring distance - * - * Each thread of the kernel computes the distance between a segment in a linestring in pair 1 to a - * linestring in pair 2. For a segment in pair 1, the linestring index is looked up from the offset - * array and mapped to the linestring in the pair 2. The segment is then computed with all segments - * in the corresponding linestring in pair 2. This forms a local minima of the shortest distance, - * which is then combined with other segment results via an atomic operation to form the globally - * minimum distance between the linestrings. - */ -template -__global__ void pairwise_linestring_distance_kernel(MultiLinestringRange1 multilinestrings1, - MultiLinestringRange2 multilinestrings2, - OutputIt distances_first) -{ - using T = typename MultiLinestringRange1::element_t; - - for (auto idx = threadIdx.x + blockIdx.x * blockDim.x; idx < multilinestrings1.num_points(); - idx += gridDim.x * blockDim.x) { - auto const part_idx = multilinestrings1.part_idx_from_point_idx(idx); - if (!multilinestrings1.is_valid_segment_id(idx, part_idx)) continue; - auto const geometry_idx = multilinestrings1.geometry_idx_from_part_idx(part_idx); - auto [a, b] = multilinestrings1.segment(idx); - T min_distance_squared = std::numeric_limits::max(); - - for (auto const& linestring2 : multilinestrings2[geometry_idx]) { - for (auto [c, d] : linestring2) { - min_distance_squared = min(min_distance_squared, squared_segment_distance(a, b, c, d)); - } - } - atomicMin(&distances_first[geometry_idx], static_cast(sqrt(min_distance_squared))); - } -} - -} // namespace detail template OutputIt pairwise_linestring_distance(MultiLinestringRange1 multilinestrings1, @@ -98,8 +61,8 @@ OutputIt pairwise_linestring_distance(MultiLinestringRange1 multilinestrings1, std::size_t const num_blocks = (multilinestrings1.num_points() + threads_per_block - 1) / threads_per_block; - detail::pairwise_linestring_distance_kernel<<>>( - multilinestrings1, multilinestrings2, distances_first); + detail::linestring_distance<<>>( + multilinestrings1, multilinestrings2, thrust::nullopt, distances_first); CUSPATIAL_CUDA_TRY(cudaGetLastError()); return distances_first + multilinestrings1.size(); diff --git a/cpp/include/cuspatial/experimental/detail/polygon_distance.cuh b/cpp/include/cuspatial/experimental/detail/polygon_distance.cuh index 80f7df855..54e7f8989 100644 --- a/cpp/include/cuspatial/experimental/detail/polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/polygon_distance.cuh @@ -76,12 +76,17 @@ OutputIt pairwise_polygon_distance(MultiPolygonRangeA lhs, return intersects; }(); - auto lhs_as_multilinestrings = lhs.as_multilinestring_range(); auto rhs_as_multilinestrings = rhs.as_multilinestring_range(); - return pairwise_linestring_distance(lhs_as_multilinestrings, rhs_as_multilinestrings, distances_first, stream); + std::size_t constexpr threads_per_block = 256; + std::size_t const num_blocks = (lhs.num_points() + threads_per_block - 1) / threads_per_block; + + detail::linestring_distance<<>>( + lhs_as_multilinestrings, rhs_as_multilinestrings, intersects, distances_first); + CUSPATIAL_CUDA_TRY(cudaGetLastError()); + return distances_first + lhs.size(); } } // namespace cuspatial diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh index ec6e466ce..0b63e8480 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh @@ -441,4 +441,39 @@ multipolygon_range:: return point_idx == _ring_begin[_part_begin[_geometry_begin[geometry_idx]]]; } +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range::as_multipoint_range() +{ + auto multipoint_geometry_it = thrust::make_permutation_iterator( + _ring_begin, thrust::make_permutation_iterator(_part_begin, _geometry_begin)); + + return multipoint_range{multipoint_geometry_it, + multipoint_geometry_it + thrust::distance(_geometry_begin, _geometry_end), + _point_begin, + _point_end}; +} + +template +CUSPATIAL_HOST_DEVICE auto +multipolygon_range:: + as_multilinestring_range() +{ + auto multilinestring_geometry_it = + thrust::make_permutation_iterator(_part_begin, _geometry_begin); + return multilinestring_range{ + multilinestring_geometry_it, + multilinestring_geometry_it + thrust::distance(_geometry_begin, _geometry_end), + _ring_begin, + _ring_end, + _point_begin, + _point_end}; +} + } // namespace cuspatial diff --git a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh index 96129145f..522641abb 100644 --- a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh @@ -180,6 +180,15 @@ class multipolygon_range { /// Returns an iterator to the end of the segment CUSPATIAL_HOST_DEVICE auto segment_end(); + + /// Range Casting + + /// Cast the range of multipolygons as a range of multipoints, ignoring all edge connections and ring relationships. + CUSPATIAL_HOST_DEVICE auto as_multipoint_range(); + + /// Cast the range of multipolygons as a range of multilinestrings, ignoring ring relationships. + CUSPATIAL_HOST_DEVICE auto as_multilinestring_range(); + protected: GeometryIterator _geometry_begin; GeometryIterator _geometry_end; diff --git a/cpp/include/cuspatial_test/vector_equality.hpp b/cpp/include/cuspatial_test/vector_equality.hpp index 204a1589d..f39ef99c2 100644 --- a/cpp/include/cuspatial_test/vector_equality.hpp +++ b/cpp/include/cuspatial_test/vector_equality.hpp @@ -197,6 +197,7 @@ inline void expect_vector_equivalent(Vector1 const& lhs, Vector2 const& rhs, U a } } + #define CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(lhs, rhs, ...) \ do { \ SCOPED_TRACE(" <-- line of failure\n"); \ @@ -238,6 +239,23 @@ void expect_vec_2d_pair_equivalent(PairVector1 const& expected, PairVector2 cons cuspatial::test::expect_vec_2d_pair_equivalent(lhs, rhs); \ } while (0) +template +void expect_multilinestring_array_equivalent(Array1 &lhs, Array2 &rhs) +{ + auto [lhs_geometry_offset, lhs_part_offset, lhs_coordinates] = lhs.release(); + auto [rhs_geometry_offset, rhs_part_offset, rhs_coordinates] = rhs.release(); + + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(lhs_geometry_offset, rhs_geometry_offset); + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(lhs_part_offset, rhs_part_offset); + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(lhs_coordinates, rhs_coordinates); +} + +#define CUSPATIAL_EXPECT_MULTILINESTRING_ARRAY_EQUIVALENT(lhs, rhs) \ + do { \ + SCOPED_TRACE(" <-- line of failure\n"); \ + cuspatial::test::expect_multilinestring_array_equivalent(lhs, rhs); \ + } while (0) + #define CUSPATIAL_RUN_TEST(FUNC, ...) \ do { \ SCOPED_TRACE(" <-- line of failure\n"); \ diff --git a/cpp/include/cuspatial_test/vector_factories.cuh b/cpp/include/cuspatial_test/vector_factories.cuh index 1644b915a..8d9912fda 100644 --- a/cpp/include/cuspatial_test/vector_factories.cuh +++ b/cpp/include/cuspatial_test/vector_factories.cuh @@ -234,12 +234,41 @@ class multilinestring_array { _coordinate_array.end()); } + auto release() + { + return std::tuple{std::move(_geometry_offset_array), + std::move(_part_offset_array), + std::move(_coordinate_array)}; + } + protected: GeometryArray _geometry_offset_array; PartArray _part_offset_array; CoordinateArray _coordinate_array; }; +/** + * @brief Construct an owning object of a multilinestring array from ranges + * + * @tparam T Type of coordinate + * @param geometry_inl Range of geometry offsets + * @param part_inl Range of part offsets + * @param coord_inl Ramge of coordinate + * @return multilinestring array object + */ +template +auto make_multilinestring_array(IndexRange geometry_inl, IndexRange part_inl, CoordRange coord_inl) +{ + using CoordType = typename CoordRange::value_type; + using DeviceIndexVector = thrust::device_vector; + using DeviceCoordVector = thrust::device_vector; + + return multilinestring_array( + make_device_vector(geometry_inl), make_device_vector(part_inl), make_device_vector(coord_inl)); +} + /** * @brief Construct an owning object of a multilinestring array from initializer lists * @@ -293,6 +322,17 @@ class multipoint_array { CoordinateArray _coordinates; }; +/** + * @brief Factory method to construct multipoint array from ranges of geometry offsets and coordintes + * + */ +template +auto make_multipoints_array(GeometryRange geometry_inl, CoordRange coordinates_inl) +{ + return multipoint_array{make_device_vector(geometry_inl), make_device_vector(coordinates_inl)}; +} + + /** * @brief Factory method to construct multipoint array from initializer list of multipoints. * diff --git a/cpp/tests/experimental/range/multipolygon_range_test.cu b/cpp/tests/experimental/range/multipolygon_range_test.cu index b6b5bc340..4ad6f7b54 100644 --- a/cpp/tests/experimental/range/multipolygon_range_test.cu +++ b/cpp/tests/experimental/range/multipolygon_range_test.cu @@ -99,6 +99,55 @@ struct MultipolygonRangeTest : public BaseFixture { CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(got, d_expected); } + + void test_multipolygon_as_multilinestring( + std::initializer_list multipolygon_geometry_offset, + std::initializer_list multipolygon_part_offset, + std::initializer_list ring_offset, + std::initializer_list> multipolygon_coordinates, + std::initializer_list multilinestring_geometry_offset, + std::initializer_list multilinestring_part_offset, + std::initializer_list> multilinestring_coordinates) + { + auto multipolygon_array = make_multipolygon_array(multipolygon_geometry_offset, + multipolygon_part_offset, + ring_offset, + multipolygon_coordinates); + auto rng = multipolygon_array.range().as_multilinestring_range(); + + auto got = make_multilinestring_array(range(rng.geometry_begin(), rng.geometry_end()), + range(rng.parts_begin(), rng.parts_end()), + range(rng.coordinates_begin(), rng.coordinates_end())); + + auto expected = make_multilinestring_array( + multilinestring_geometry_offset, multilinestring_part_offset, multilinestring_coordinates); + + CUSPATIAL_EXPECT_MULTILINESTRING_ARRAY_EQUIVALENT(expected, got); + } + + void test_multipolygon_as_multipoint( + std::initializer_list multipolygon_geometry_offset, + std::initializer_list multipolygon_part_offset, + std::initializer_list ring_offset, + std::initializer_list> multipolygon_coordinates, + std::initializer_list multipoint_geometry_offset, + std::initializer_list> multipoint_coordinates) + { + auto multipolygon_array = make_multipolygon_array(multipolygon_geometry_offset, + multipolygon_part_offset, + ring_offset, + multipolygon_coordinates); + auto rng = multipolygon_array.range().as_multipoint_range(); + + auto got = make_multipoint_range(range(rng.geometry_begin(), rng.geometry_end()), + range(rng.coordinates_begin(), rng.coordinates_end())); + + auto expected = make_multipoint_range( + range(multipoint_geometry_offset.begin(), multipoint_geometry_offset.end()), + range(multipoint_coordinates.begin(), multipoint_coordinates.end())); + + CUSPATIAL_EXPECT_MULTILINESTRING_ARRAY_EQUIVALENT(expected, got); + } }; TYPED_TEST_CASE(MultipolygonRangeTest, FloatingPointTypes); @@ -338,3 +387,74 @@ TYPED_TEST(MultipolygonRangeTest, DISABLED_MultipolygonSegmentCount_ConatainsEmp {0, 0}}, {6, 3}); } + + +TYPED_TEST(MultipolygonRangeTest, MultipolygonAsMultilinestring1) +{ + CUSPATIAL_RUN_TEST(this->test_multipolygon_as_multilinestring, + {0, 1, 2}, + {0, 1, 2}, + {0, 4, 8}, + {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {10, 10}, {11, 10}, {11, 11}, {10, 10}}, + {0, 1, 2}, + {0, 4, 8}, + {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {10, 10}, {11, 10}, {11, 11}, {10, 10}}); +} + +TYPED_TEST(MultipolygonRangeTest, MultipolygonAsMultilinestring2) +{ + CUSPATIAL_RUN_TEST(this->test_multipolygon_as_multilinestring, + {0, 1, 2}, + {0, 1, 3}, + {0, 4, 8, 12}, + {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {10, 10}, {11, 10}, {11, 11}, {10, 10}, {20, 20}, {21, 20}, {21, 21}, {20, 20}}, + {0, 1, 3}, + {0, 4, 8, 12}, + {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {10, 10}, {11, 10}, {11, 11}, {10, 10}, {20, 20}, {21, 20}, {21, 21}, {20, 20}}); +} + +TYPED_TEST(MultipolygonRangeTest, MultipolygonAsMultilinestring3) +{ + CUSPATIAL_RUN_TEST(this->test_multipolygon_as_multilinestring, + {0, 1, 2}, + {0, 2, 3}, + {0, 4, 8, 12}, + {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {10, 10}, {11, 10}, {11, 11}, {10, 10}, {20, 20}, {21, 20}, {21, 21}, {20, 20}}, + {0, 2, 3}, + {0, 4, 8, 12}, + {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {10, 10}, {11, 10}, {11, 11}, {10, 10}, {20, 20}, {21, 20}, {21, 21}, {20, 20}}); +} + +TYPED_TEST(MultipolygonRangeTest, MultipolygonAsMultiPoint1) +{ + CUSPATIAL_RUN_TEST(this->test_multipolygon_as_multipoint, + {0, 1, 2}, + {0, 1, 2}, + {0, 4, 8}, + {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {10, 10}, {11, 10}, {11, 11}, {10, 10}}, + {0, 4, 8}, + {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {10, 10}, {11, 10}, {11, 11}, {10, 10}}); +} + +TYPED_TEST(MultipolygonRangeTest, MultipolygonAsMultiPoint2) +{ + CUSPATIAL_RUN_TEST(this->test_multipolygon_as_multipoint, + {0, 1, 2}, + {0, 1, 3}, + {0, 4, 8, 12}, + {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {10, 10}, {11, 10}, {11, 11}, {10, 10}, {20, 20}, {21, 20}, {21, 21}, {20, 20}}, + {0, 4, 12}, + {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {10, 10}, {11, 10}, {11, 11}, {10, 10}, {20, 20}, {21, 20}, {21, 21}, {20, 20}}); +} + + +TYPED_TEST(MultipolygonRangeTest, MultipolygonAsMultiPoint3) +{ + CUSPATIAL_RUN_TEST(this->test_multipolygon_as_multipoint, + {0, 1, 2}, + {0, 2, 3}, + {0, 4, 8, 12}, + {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {10, 10}, {11, 10}, {11, 11}, {10, 10}, {20, 20}, {21, 20}, {21, 21}, {20, 20}}, + {0, 8, 12}, + {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {10, 10}, {11, 10}, {11, 11}, {10, 10}, {20, 20}, {21, 20}, {21, 21}, {20, 20}}); +} From c92656c0910b789028a0eb8a7c53dec53029dff4 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 10 Apr 2023 15:25:34 -0700 Subject: [PATCH 41/54] passing range cast tests --- .../detail/algorithm/linestring_distance.cuh | 23 ++-- .../detail/ranges/multipolygon_range.cuh | 1 + .../cuspatial_test/vector_equality.hpp | 23 +++- .../cuspatial_test/vector_factories.cuh | 17 +-- .../range/multipolygon_range_test.cu | 121 +++++++++++++++--- 5 files changed, 145 insertions(+), 40 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/algorithm/linestring_distance.cuh b/cpp/include/cuspatial/experimental/detail/algorithm/linestring_distance.cuh index 8ce192943..31b984551 100644 --- a/cpp/include/cuspatial/experimental/detail/algorithm/linestring_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/algorithm/linestring_distance.cuh @@ -17,6 +17,8 @@ #include #include +#include + #include namespace cuspatial { @@ -33,14 +35,14 @@ namespace detail { * which is then combined with other segment results via an atomic operation to form the global * minimum distance between the linestrings. * - * `intersects` is an optional pointer to a boolean range where the `i`th element indicates the `i`th - * output should be set to 0 and bypass distance computation. This argument is optional, if set to nullopt, - * no distance computation will be bypassed. + * `intersects` is an optional pointer to a boolean range where the `i`th element indicates the + * `i`th output should be set to 0 and bypass distance computation. This argument is optional, if + * set to nullopt, no distance computation will be bypassed. */ -template +template __global__ void linestring_distance(MultiLinestringRange1 multilinestrings1, MultiLinestringRange2 multilinestrings2, - thrust::optional intersects, + thrust::optional intersects, OutputIt distances_first) { using T = typename MultiLinestringRange1::element_t; @@ -51,14 +53,13 @@ __global__ void linestring_distance(MultiLinestringRange1 multilinestrings1, if (!multilinestrings1.is_valid_segment_id(idx, part_idx)) continue; auto const geometry_idx = multilinestrings1.geometry_idx_from_part_idx(part_idx); - if (intersects.has_value() && intersects.value()[geometry_idx]) - { - distances_first[geometry_idx] = 0; - continue; + if (intersects.has_value() && intersects.value()[geometry_idx]) { + distances_first[geometry_idx] = 0; + continue; } - auto [a, b] = multilinestrings1.segment(idx); - T min_distance_squared = std::numeric_limits::max(); + auto [a, b] = multilinestrings1.segment(idx); + T min_distance_squared = std::numeric_limits::max(); for (auto const& linestring2 : multilinestrings2[geometry_idx]) { for (auto [c, d] : linestring2) { diff --git a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh index 0b63e8480..2c563f915 100644 --- a/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/detail/ranges/multipolygon_range.cuh @@ -22,6 +22,7 @@ #include #include #include +#include #include #include diff --git a/cpp/include/cuspatial_test/vector_equality.hpp b/cpp/include/cuspatial_test/vector_equality.hpp index f39ef99c2..b4ce1ca68 100644 --- a/cpp/include/cuspatial_test/vector_equality.hpp +++ b/cpp/include/cuspatial_test/vector_equality.hpp @@ -197,7 +197,6 @@ inline void expect_vector_equivalent(Vector1 const& lhs, Vector2 const& rhs, U a } } - #define CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(lhs, rhs, ...) \ do { \ SCOPED_TRACE(" <-- line of failure\n"); \ @@ -240,7 +239,7 @@ void expect_vec_2d_pair_equivalent(PairVector1 const& expected, PairVector2 cons } while (0) template -void expect_multilinestring_array_equivalent(Array1 &lhs, Array2 &rhs) +void expect_multilinestring_array_equivalent(Array1& lhs, Array2& rhs) { auto [lhs_geometry_offset, lhs_part_offset, lhs_coordinates] = lhs.release(); auto [rhs_geometry_offset, rhs_part_offset, rhs_coordinates] = rhs.release(); @@ -251,11 +250,27 @@ void expect_multilinestring_array_equivalent(Array1 &lhs, Array2 &rhs) } #define CUSPATIAL_EXPECT_MULTILINESTRING_ARRAY_EQUIVALENT(lhs, rhs) \ - do { \ - SCOPED_TRACE(" <-- line of failure\n"); \ + do { \ + SCOPED_TRACE(" <-- line of failure\n"); \ cuspatial::test::expect_multilinestring_array_equivalent(lhs, rhs); \ } while (0) +template +void expect_multipoint_array_equivalent(Array1& lhs, Array2& rhs) +{ + auto [lhs_geometry_offset, lhs_coordinates] = lhs.release(); + auto [rhs_geometry_offset, rhs_coordinates] = rhs.release(); + + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(lhs_geometry_offset, rhs_geometry_offset); + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(lhs_coordinates, rhs_coordinates); +} + +#define CUSPATIAL_EXPECT_MULTIPOINT_ARRAY_EQUIVALENT(lhs, rhs) \ + do { \ + SCOPED_TRACE(" <-- line of failure\n"); \ + cuspatial::test::expect_multipoint_array_equivalent(lhs, rhs); \ + } while (0) + #define CUSPATIAL_RUN_TEST(FUNC, ...) \ do { \ SCOPED_TRACE(" <-- line of failure\n"); \ diff --git a/cpp/include/cuspatial_test/vector_factories.cuh b/cpp/include/cuspatial_test/vector_factories.cuh index 8d9912fda..59869d190 100644 --- a/cpp/include/cuspatial_test/vector_factories.cuh +++ b/cpp/include/cuspatial_test/vector_factories.cuh @@ -250,16 +250,18 @@ class multilinestring_array { /** * @brief Construct an owning object of a multilinestring array from ranges * - * @tparam T Type of coordinate * @param geometry_inl Range of geometry offsets * @param part_inl Range of part offsets * @param coord_inl Ramge of coordinate * @return multilinestring array object */ -template -auto make_multilinestring_array(IndexRange geometry_inl, IndexRange part_inl, CoordRange coord_inl) + typename IndexType = typename IndexRangeB::value_type> +auto make_multilinestring_array(IndexRangeA geometry_inl, + IndexRangeB part_inl, + CoordRange coord_inl) { using CoordType = typename CoordRange::value_type; using DeviceIndexVector = thrust::device_vector; @@ -323,16 +325,15 @@ class multipoint_array { }; /** - * @brief Factory method to construct multipoint array from ranges of geometry offsets and coordintes - * + * @brief Factory method to construct multipoint array from ranges of geometry offsets and + * coordinates */ -template +template auto make_multipoints_array(GeometryRange geometry_inl, CoordRange coordinates_inl) { return multipoint_array{make_device_vector(geometry_inl), make_device_vector(coordinates_inl)}; } - /** * @brief Factory method to construct multipoint array from initializer list of multipoints. * diff --git a/cpp/tests/experimental/range/multipolygon_range_test.cu b/cpp/tests/experimental/range/multipolygon_range_test.cu index 4ad6f7b54..094abd293 100644 --- a/cpp/tests/experimental/range/multipolygon_range_test.cu +++ b/cpp/tests/experimental/range/multipolygon_range_test.cu @@ -115,9 +115,10 @@ struct MultipolygonRangeTest : public BaseFixture { multipolygon_coordinates); auto rng = multipolygon_array.range().as_multilinestring_range(); - auto got = make_multilinestring_array(range(rng.geometry_begin(), rng.geometry_end()), - range(rng.parts_begin(), rng.parts_end()), - range(rng.coordinates_begin(), rng.coordinates_end())); + auto got = + make_multilinestring_array(range(rng.geometry_offsets_begin(), rng.geometry_offsets_end()), + range(rng.part_offsets_begin(), rng.part_offsets_end()), + range(rng.point_begin(), rng.point_end())); auto expected = make_multilinestring_array( multilinestring_geometry_offset, multilinestring_part_offset, multilinestring_coordinates); @@ -139,14 +140,14 @@ struct MultipolygonRangeTest : public BaseFixture { multipolygon_coordinates); auto rng = multipolygon_array.range().as_multipoint_range(); - auto got = make_multipoint_range(range(rng.geometry_begin(), rng.geometry_end()), - range(rng.coordinates_begin(), rng.coordinates_end())); + auto got = make_multipoints_array(range(rng.offsets_begin(), rng.offsets_end()), + range(rng.point_begin(), rng.point_end())); - auto expected = make_multipoint_range( + auto expected = make_multipoints_array( range(multipoint_geometry_offset.begin(), multipoint_geometry_offset.end()), range(multipoint_coordinates.begin(), multipoint_coordinates.end())); - CUSPATIAL_EXPECT_MULTILINESTRING_ARRAY_EQUIVALENT(expected, got); + CUSPATIAL_EXPECT_MULTIPOINT_ARRAY_EQUIVALENT(expected, got); } }; @@ -388,7 +389,6 @@ TYPED_TEST(MultipolygonRangeTest, DISABLED_MultipolygonSegmentCount_ConatainsEmp {6, 3}); } - TYPED_TEST(MultipolygonRangeTest, MultipolygonAsMultilinestring1) { CUSPATIAL_RUN_TEST(this->test_multipolygon_as_multilinestring, @@ -407,10 +407,32 @@ TYPED_TEST(MultipolygonRangeTest, MultipolygonAsMultilinestring2) {0, 1, 2}, {0, 1, 3}, {0, 4, 8, 12}, - {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {10, 10}, {11, 10}, {11, 11}, {10, 10}, {20, 20}, {21, 20}, {21, 21}, {20, 20}}, + {{0, 0}, + {1, 0}, + {1, 1}, + {0, 0}, + {10, 10}, + {11, 10}, + {11, 11}, + {10, 10}, + {20, 20}, + {21, 20}, + {21, 21}, + {20, 20}}, {0, 1, 3}, {0, 4, 8, 12}, - {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {10, 10}, {11, 10}, {11, 11}, {10, 10}, {20, 20}, {21, 20}, {21, 21}, {20, 20}}); + {{0, 0}, + {1, 0}, + {1, 1}, + {0, 0}, + {10, 10}, + {11, 10}, + {11, 11}, + {10, 10}, + {20, 20}, + {21, 20}, + {21, 21}, + {20, 20}}); } TYPED_TEST(MultipolygonRangeTest, MultipolygonAsMultilinestring3) @@ -419,10 +441,32 @@ TYPED_TEST(MultipolygonRangeTest, MultipolygonAsMultilinestring3) {0, 1, 2}, {0, 2, 3}, {0, 4, 8, 12}, - {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {10, 10}, {11, 10}, {11, 11}, {10, 10}, {20, 20}, {21, 20}, {21, 21}, {20, 20}}, + {{0, 0}, + {1, 0}, + {1, 1}, + {0, 0}, + {10, 10}, + {11, 10}, + {11, 11}, + {10, 10}, + {20, 20}, + {21, 20}, + {21, 21}, + {20, 20}}, {0, 2, 3}, {0, 4, 8, 12}, - {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {10, 10}, {11, 10}, {11, 11}, {10, 10}, {20, 20}, {21, 20}, {21, 21}, {20, 20}}); + {{0, 0}, + {1, 0}, + {1, 1}, + {0, 0}, + {10, 10}, + {11, 10}, + {11, 11}, + {10, 10}, + {20, 20}, + {21, 20}, + {21, 21}, + {20, 20}}); } TYPED_TEST(MultipolygonRangeTest, MultipolygonAsMultiPoint1) @@ -442,19 +486,62 @@ TYPED_TEST(MultipolygonRangeTest, MultipolygonAsMultiPoint2) {0, 1, 2}, {0, 1, 3}, {0, 4, 8, 12}, - {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {10, 10}, {11, 10}, {11, 11}, {10, 10}, {20, 20}, {21, 20}, {21, 21}, {20, 20}}, + {{0, 0}, + {1, 0}, + {1, 1}, + {0, 0}, + {10, 10}, + {11, 10}, + {11, 11}, + {10, 10}, + {20, 20}, + {21, 20}, + {21, 21}, + {20, 20}}, {0, 4, 12}, - {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {10, 10}, {11, 10}, {11, 11}, {10, 10}, {20, 20}, {21, 20}, {21, 21}, {20, 20}}); + {{0, 0}, + {1, 0}, + {1, 1}, + {0, 0}, + {10, 10}, + {11, 10}, + {11, 11}, + {10, 10}, + {20, 20}, + {21, 20}, + {21, 21}, + {20, 20}}); } - TYPED_TEST(MultipolygonRangeTest, MultipolygonAsMultiPoint3) { CUSPATIAL_RUN_TEST(this->test_multipolygon_as_multipoint, {0, 1, 2}, {0, 2, 3}, {0, 4, 8, 12}, - {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {10, 10}, {11, 10}, {11, 11}, {10, 10}, {20, 20}, {21, 20}, {21, 21}, {20, 20}}, + {{0, 0}, + {1, 0}, + {1, 1}, + {0, 0}, + {10, 10}, + {11, 10}, + {11, 11}, + {10, 10}, + {20, 20}, + {21, 20}, + {21, 21}, + {20, 20}}, {0, 8, 12}, - {{0, 0}, {1, 0}, {1, 1}, {0, 0}, {10, 10}, {11, 10}, {11, 11}, {10, 10}, {20, 20}, {21, 20}, {21, 21}, {20, 20}}); + {{0, 0}, + {1, 0}, + {1, 1}, + {0, 0}, + {10, 10}, + {11, 10}, + {11, 11}, + {10, 10}, + {20, 20}, + {21, 20}, + {21, 21}, + {20, 20}}); } From 7092c3b13fe5ba029a21a8a0173ea183797fd839 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 10 Apr 2023 17:37:22 -0700 Subject: [PATCH 42/54] passing one-pair, no-hole simple polygon tests --- .../experimental/detail/polygon_distance.cuh | 32 ++++++++----------- .../experimental/polygon_distance.cuh | 14 ++++---- .../ranges/multipolygon_range.cuh | 8 +++-- cpp/include/cuspatial_test/test_util.cuh | 5 +-- cpp/tests/CMakeLists.txt | 3 ++ 5 files changed, 31 insertions(+), 31 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/polygon_distance.cuh b/cpp/include/cuspatial/experimental/detail/polygon_distance.cuh index 54e7f8989..f1b36b2d1 100644 --- a/cpp/include/cuspatial/experimental/detail/polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/polygon_distance.cuh @@ -20,26 +20,15 @@ #include "linestring_distance.cuh" #include -#include -#include -#include -#include #include -#include -#include -#include #include #include #include #include -#include -#include -#include #include -#include -#include +#include #include #include @@ -47,14 +36,13 @@ namespace cuspatial { -template -OutputIt pairwise_polygon_distance(MultiPolygonRangeA lhs, - MultiPolygonRangeB rhs, +template +OutputIt pairwise_polygon_distance(MultipolygonRangeA lhs, + MultipolygonRangeB rhs, OutputIt distances_first, rmm::cuda_stream_view stream) { - using T = typename MultipolygonRangeA::element_t; - using index_t = typename MultipolygonRangeB::index_t; + using T = typename MultipolygonRangeA::element_t; CUSPATIAL_EXPECTS(lhs.size() == rhs.size(), "Must have the same number of input rows."); @@ -63,9 +51,10 @@ OutputIt pairwise_polygon_distance(MultiPolygonRangeA lhs, auto lhs_as_multipoints = lhs.as_multipoint_range(); auto rhs_as_multipoints = rhs.as_multipoint_range(); - auto intersects = [&lhs, &rhs, &stream]() { + auto intersects = [&]() { auto lhs_in_rhs = point_polygon_intersects(lhs_as_multipoints, rhs, stream); auto rhs_in_lhs = point_polygon_intersects(rhs_as_multipoints, lhs, stream); + rmm::device_uvector intersects(lhs_in_rhs.size(), stream); thrust::transform(rmm::exec_policy(stream), lhs_in_rhs.begin(), @@ -79,11 +68,16 @@ OutputIt pairwise_polygon_distance(MultiPolygonRangeA lhs, auto lhs_as_multilinestrings = lhs.as_multilinestring_range(); auto rhs_as_multilinestrings = rhs.as_multilinestring_range(); + thrust::fill(rmm::exec_policy(stream), + distances_first, + distances_first + lhs.size(), + std::numeric_limits::max()); + std::size_t constexpr threads_per_block = 256; std::size_t const num_blocks = (lhs.num_points() + threads_per_block - 1) / threads_per_block; detail::linestring_distance<<>>( - lhs_as_multilinestrings, rhs_as_multilinestrings, intersects, distances_first); + lhs_as_multilinestrings, rhs_as_multilinestrings, intersects.begin(), distances_first); CUSPATIAL_CUDA_TRY(cudaGetLastError()); return distances_first + lhs.size(); diff --git a/cpp/include/cuspatial/experimental/polygon_distance.cuh b/cpp/include/cuspatial/experimental/polygon_distance.cuh index 0289396a8..10404ea58 100644 --- a/cpp/include/cuspatial/experimental/polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/polygon_distance.cuh @@ -29,19 +29,19 @@ namespace cuspatial { * @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 multipolygons, one per computed distance pair. - * @param multipolygons Range of multipolygons, one per computed distance pair. + * @param + * @param * @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 * "LegacyRandomAccessIterator" */ -template -OutputIt pairwise_polygon_distance(MultiPolygonRangeA lhs, - MultiPolygonRangeB rhs, - OutputIt distances_first, - rmm::cuda_stream_view stream = rmm::cuda_stream_default); +template +OutputIt pairwise_polygon_distance(MultipolygonRangeA lhs, + MultipolygonRangeB rhs, + 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 index 522641abb..6c7ef781d 100644 --- a/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multipolygon_range.cuh @@ -62,7 +62,9 @@ class multipolygon_range { using ring_it_t = RingIterator; using point_it_t = VecIterator; using point_t = iterator_value_type; - using element_t = iterator_vec_base_type; + + using index_t = iterator_value_type; + using element_t = iterator_vec_base_type; int64_t static constexpr INVALID_INDEX = -1; @@ -180,10 +182,10 @@ class multipolygon_range { /// Returns an iterator to the end of the segment CUSPATIAL_HOST_DEVICE auto segment_end(); - /// Range Casting - /// Cast the range of multipolygons as a range of multipoints, ignoring all edge connections and ring relationships. + /// Cast the range of multipolygons as a range of multipoints, ignoring all edge connections and + /// ring relationships. CUSPATIAL_HOST_DEVICE auto as_multipoint_range(); /// Cast the range of multipolygons as a range of multilinestrings, ignoring ring relationships. diff --git a/cpp/include/cuspatial_test/test_util.cuh b/cpp/include/cuspatial_test/test_util.cuh index e30089ba4..43bc98afe 100644 --- a/cpp/include/cuspatial_test/test_util.cuh +++ b/cpp/include/cuspatial_test/test_util.cuh @@ -102,14 +102,15 @@ void print_device_range(Iter begin, /** * @brief */ -template +template void print_device_vector(Vector const& vec, std::string_view pre = "", std::string_view post = "\n") { using T = typename Vector::value_type; auto hvec = to_host(vec); std::cout << pre; - std::for_each(hvec.begin(), hvec.end(), [](auto const& x) { std::cout << x << " "; }); + std::for_each( + hvec.begin(), hvec.end(), [](auto const& x) { std::cout << static_cast(x) << " "; }); std::cout << post; } diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index aa37e25cb..d34472217 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -133,6 +133,9 @@ ConfigureTest(POINT_POLYGON_DISTANCE_TEST_EXP ConfigureTest(LINESTRING_POLYGON_DISTANCE_TEST_EXP experimental/spatial/linestring_polygon_distance_test.cu) +ConfigureTest(POLYGON_DISTANCE_TEST_EXP + experimental/spatial/polygon_distance_test.cu) + ConfigureTest(HAUSDORFF_TEST_EXP experimental/spatial/hausdorff_test.cu) From b74d180f73d2bae56ac7bfe403ec19a843456554 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 10 Apr 2023 17:51:44 -0700 Subject: [PATCH 43/54] add polygon test --- .../spatial/polygon_distance_test.cu | 157 ++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 cpp/tests/experimental/spatial/polygon_distance_test.cu diff --git a/cpp/tests/experimental/spatial/polygon_distance_test.cu b/cpp/tests/experimental/spatial/polygon_distance_test.cu new file mode 100644 index 000000000..ca2c0d17c --- /dev/null +++ b/cpp/tests/experimental/spatial/polygon_distance_test.cu @@ -0,0 +1,157 @@ +/* + * 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 + +#include +#include +#include +#include + +#include + +using namespace cuspatial; +using namespace cuspatial::test; + +template +struct PairwisePolygonDistanceTest : BaseFixture { + template + void run_test(MultipolygonRangeA lhs, + MultipolygonRangeB rhs, + rmm::device_uvector const& expected) + { + auto got = rmm::device_uvector(lhs.size(), stream()); + auto ret = pairwise_polygon_distance(lhs, rhs, got.begin(), stream()); + + CUSPATIAL_EXPECT_VECTORS_EQUIVALENT(expected, got); + EXPECT_EQ(thrust::distance(got.begin(), ret), expected.size()); + } + + void run_single(std::initializer_list lhs_multipolygon_geometry_offsets, + std::initializer_list lhs_multipolygon_part_offsets, + std::initializer_list lhs_multipolygon_ring_offsets, + std::initializer_list> lhs_multipolygon_coordinates, + std::initializer_list rhs_multipolygon_geometry_offsets, + std::initializer_list rhs_multipolygon_part_offsets, + std::initializer_list rhs_multipolygon_ring_offsets, + std::initializer_list> rhs_multipolygon_coordinates, + std::initializer_list expected) + { + auto lhs = make_multipolygon_array(lhs_multipolygon_geometry_offsets, + lhs_multipolygon_part_offsets, + lhs_multipolygon_ring_offsets, + lhs_multipolygon_coordinates); + + auto rhs = make_multipolygon_array(rhs_multipolygon_geometry_offsets, + rhs_multipolygon_part_offsets, + rhs_multipolygon_ring_offsets, + rhs_multipolygon_coordinates); + + auto lhs_range = lhs.range(); + auto rhs_range = rhs.range(); + + auto d_expected = make_device_uvector(expected, stream(), mr()); + + // Euclidean distance is symmetric + run_test(lhs_range, rhs_range, d_expected); + run_test(rhs_range, lhs_range, d_expected); + } +}; + +TYPED_TEST_CASE(PairwisePolygonDistanceTest, FloatingPointTypes); + +TYPED_TEST(PairwisePolygonDistanceTest, Empty) +{ + this->run_single({0}, {0}, {0}, {}, {0}, {0}, {0}, {}, {}); +} + +// Test Matrix +// One Pair: +// lhs-rhs Relationship: Disjoint, Touching, Overlapping, Contained, Within +// Holes: No, Yes +// Multipolygon: No, Yes + +TYPED_TEST(PairwisePolygonDistanceTest, OnePairSinglePolygonDisjointNoHole) +{ + this->run_single({0, 1}, + {0, 1}, + {0, 4}, + {{0, 0}, {0, 1}, {1, 1}, {1, 0}}, + {0, 1}, + {0, 1}, + {0, 4}, + {{-1, 0}, {-1, 1}, {-2, 0}, {-1, 0}}, + {1}); +} + +TYPED_TEST(PairwisePolygonDistanceTest, OnePairSinglePolygonTouchingNoHole) +{ + this->run_single({0, 1}, + {0, 1}, + {0, 4}, + {{0, 0}, {0, 1}, {1, 1}, {1, 0}}, + {0, 1}, + {0, 1}, + {0, 4}, + {{1, 0}, {2, 0}, {2, 1}, {1, 0}}, + {0}); +} + +TYPED_TEST(PairwisePolygonDistanceTest, OnePairSinglePolygonOverlappingNoHole) +{ + this->run_single({0, 1}, + {0, 1}, + {0, 4}, + {{0, 0}, {0, 1}, {1, 1}, {1, 0}}, + {0, 1}, + {0, 1}, + {0, 4}, + {{0.5, 0}, {2, 0}, {2, 1}, {0.5, 0}}, + {0}); +} + +TYPED_TEST(PairwisePolygonDistanceTest, OnePairSinglePolygonContainedNoHole) +{ + this->run_single({0, 1}, + {0, 1}, + {0, 5}, + {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}, + {0, 1}, + {0, 1}, + {0, 5}, + {{0.25, 0.25}, {0.75, 0.25}, {0.75, 0.75}, {0.25, 0.75}, {0.25, 0.25}}, + {0}); +} + +TYPED_TEST(PairwisePolygonDistanceTest, OnePairSinglePolygonWithinNoHole) +{ + this->run_single({0, 1}, + {0, 1}, + {0, 5}, + {{0.25, 0.25}, {0.75, 0.25}, {0.75, 0.75}, {0.25, 0.75}, {0.25, 0.25}}, + {0, 1}, + {0, 1}, + {0, 5}, + {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}, + {0}); +} From 0b77e3838f2e925e618b09f21b71ad287869c80d Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 11 Apr 2023 11:55:38 -0700 Subject: [PATCH 44/54] adds single pair test that has holes in polygons --- .../experimental/detail/distance_utils.cuh | 1 + .../spatial/polygon_distance_test.cu | 115 ++++++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/cpp/include/cuspatial/experimental/detail/distance_utils.cuh b/cpp/include/cuspatial/experimental/detail/distance_utils.cuh index db07dcb7d..b9722e4f2 100644 --- a/cpp/include/cuspatial/experimental/detail/distance_utils.cuh +++ b/cpp/include/cuspatial/experimental/detail/distance_utils.cuh @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include #include #include diff --git a/cpp/tests/experimental/spatial/polygon_distance_test.cu b/cpp/tests/experimental/spatial/polygon_distance_test.cu index ca2c0d17c..3d923977f 100644 --- a/cpp/tests/experimental/spatial/polygon_distance_test.cu +++ b/cpp/tests/experimental/spatial/polygon_distance_test.cu @@ -155,3 +155,118 @@ TYPED_TEST(PairwisePolygonDistanceTest, OnePairSinglePolygonWithinNoHole) {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}, {0}); } + +TYPED_TEST(PairwisePolygonDistanceTest, OnePairSinglePolygonDisjointHasHole) +{ + this->run_single({0, 1}, + {0, 2}, + {0, 4, 8}, + {{0.0, 0.0}, + {2.0, 0.0}, + {2.0, 2.0}, + {0.0, 0.0}, + {1.0, 0.75}, + {1.5, 0.75}, + {1.25, 1.0}, + {1.0, 0.75}}, + {0, 1}, + {0, 2}, + {0, 4, 8}, + {{-1.0, 0.0}, + {-1.0, -1.0}, + {-2.0, 0.0}, + {-1.0, 0.0}, + {-1.25, -0.25}, + {-1.25, -0.5}, + {-1.5, -0.25}, + {-1.25, -0.25}}, + {1}); +} + +TYPED_TEST(PairwisePolygonDistanceTest, OnePairSinglePolygonDisjointHasHole2) +{ + this->run_single({0, 1}, + {0, 2}, + {0, 5, 10}, + {{0.0, 0.0}, + {10.0, 0.0}, + {10.0, 10.0}, + {0.0, 10.0}, + {0.0, 0.0}, + {2.0, 2.0}, + {2.0, 6.0}, + {6.0, 6.0}, + {6.0, 2.0}, + {2.0, 2.0}}, + {0, 1}, + {0, 1}, + {0, 5}, + {{3.0, 3.0}, {3.0, 4.0}, {4.0, 4.0}, {4.0, 3.0}, {3.0, 3.0}}, + {1}); +} + +TYPED_TEST(PairwisePolygonDistanceTest, OnePairSinglePolygonTouchingHasHole) +{ + this->run_single({0, 1}, + {0, 2}, + {0, 4, 8}, + {{0.0, 0.0}, + {2.0, 0.0}, + {2.0, 2.0}, + {0.0, 0.0}, + {1.0, 0.75}, + {1.5, 0.75}, + {1.25, 1.0}, + {1.0, 0.75}}, + {0, 1}, + {0, 2}, + {0, 4, 8}, + {{2.0, 0.0}, + {3.0, 0.0}, + {3.0, 1.0}, + {2.0, 0.0}, + {2.5, 0.25}, + {2.75, 0.25}, + {2.75, 0.5}, + {2.5, 0.25}}, + {0}); +} + +TYPED_TEST(PairwisePolygonDistanceTest, OnePairSinglePolygonOverlappingHasHole) +{ + this->run_single({0, 1}, + {0, 2}, + {0, 5, 10}, + {{0, 0}, {4, 0}, {4, 4}, {0, 4}, {0, 0}, {2, 2}, {2, 3}, {3, 3}, {3, 2}, {2, 2}}, + {0, 1}, + {0, 2}, + {0, 4, 8}, + {{2, -1}, {5, 4}, {5, -1}, {2, -1}, {3, -0.5}, {4, 0}, {4, -0.5}, {3, -0.5}}, + {0}); +} + +TYPED_TEST(PairwisePolygonDistanceTest, OnePairSinglePolygonContainedHasHole) +{ + this->run_single({0, 1}, + {0, 2}, + {0, 5, 10}, + {{0, 0}, {4, 0}, {4, 4}, {0, 4}, {0, 0}, {1, 3}, {1, 1}, {3, 1}, {1, 3}, {1, 1}}, + {0, 1}, + {0, 1}, + {0, 4}, + {{1, 3}, {3, 1}, {3, 3}, {1, 3}}, + {0}); +} + +TYPED_TEST(PairwisePolygonDistanceTest, OnePairSinglePolygonWithinHasHole) +{ + this->run_single({0, 1}, + {0, 1}, + {0, 4}, + {{1, 3}, {3, 1}, {3, 3}, {1, 3}}, + {0, 1}, + {0, 2}, + {0, 5, 9}, + {{0, 0}, {4, 0}, {4, 4}, {0, 4}, {0, 0}, {1, 1}, {3, 1}, {1, 3}, {1, 1}}, + {0}); +} From 002ffa050446009cf218baf364fd4f95ba5aeba8 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 11 Apr 2023 15:02:55 -0700 Subject: [PATCH 45/54] fix with_param fixture doc, add multipolygon test --- cpp/include/cuspatial_test/base_fixture.hpp | 10 +-- .../spatial/polygon_distance_test.cu | 83 ++++++++++++++++++- 2 files changed, 87 insertions(+), 6 deletions(-) diff --git a/cpp/include/cuspatial_test/base_fixture.hpp b/cpp/include/cuspatial_test/base_fixture.hpp index ae92c9fd3..8f8344896 100644 --- a/cpp/include/cuspatial_test/base_fixture.hpp +++ b/cpp/include/cuspatial_test/base_fixture.hpp @@ -54,8 +54,7 @@ class RMMResourceMixin { * class MyTestFixture : public cuspatial::test::BaseFixture {}; * ``` */ -class BaseFixture : public RMMResourceMixin, public ::testing::Test { -}; +class BaseFixture : public RMMResourceMixin, public ::testing::Test {}; /** * @brief Base test fixture class from which libcuspatial test with only value parameterization @@ -67,7 +66,9 @@ class BaseFixture : public RMMResourceMixin, public ::testing::Test { * class MyTest : public cuspatial::test::BaseFixtureWithParam {}; * * TEST_P(MyTest, TestParamterGet) { - * auto [a, b, c] = GetParam(); + * auto a = std::get<0>(GetParam()); + * auto b = std::get<1>(GetParam()); + * auto c = std::get<2>(GetParam()); * ... * } * @@ -79,8 +80,7 @@ class BaseFixture : public RMMResourceMixin, public ::testing::Test { */ template class BaseFixtureWithParam : public RMMResourceMixin, - public ::testing::TestWithParam> { -}; + public ::testing::TestWithParam> {}; /** * @brief Floating point types to be used in libcuspatial tests diff --git a/cpp/tests/experimental/spatial/polygon_distance_test.cu b/cpp/tests/experimental/spatial/polygon_distance_test.cu index 3d923977f..a1d5c0e2d 100644 --- a/cpp/tests/experimental/spatial/polygon_distance_test.cu +++ b/cpp/tests/experimental/spatial/polygon_distance_test.cu @@ -23,11 +23,12 @@ #include #include -#include #include #include #include +#include + #include using namespace cuspatial; @@ -270,3 +271,83 @@ TYPED_TEST(PairwisePolygonDistanceTest, OnePairSinglePolygonWithinHasHole) {{0, 0}, {4, 0}, {4, 4}, {0, 4}, {0, 0}, {1, 1}, {3, 1}, {1, 3}, {1, 1}}, {0}); } + +TYPED_TEST(PairwisePolygonDistanceTest, OnePairMultiPolygonDisjointNoHole) +{ + this->run_single({0, 2}, + {0, 1, 2}, + {0, 4, 8}, + {{0.0, 0.0}, + {2.0, 0.0}, + {2.0, 2.0}, + {0.0, 0.0}, + {3.0, 3.0}, + {3.0, 4.0}, + {4.0, 4.0}, + {3.0, 3.0}}, + {0, 1}, + {0, 1}, + {0, 4}, + {{-1.0, 0.0}, {-1.0, -1.0}, {-2.0, 0.0}, {-1.0, 0.0}}, + {1}); +} + +TYPED_TEST(PairwisePolygonDistanceTest, OnePairMultiPolygonTouchingNoHole) +{ + this->run_single({0, 2}, + {0, 1, 2}, + {0, 4, 8}, + {{0.0, 0.0}, + {2.0, 0.0}, + {2.0, 2.0}, + {0.0, 0.0}, + {3.0, 3.0}, + {3.0, 4.0}, + {4.0, 4.0}, + {3.0, 3.0}}, + {0, 1}, + {0, 1}, + {0, 4}, + {{3.0, 3.0}, {2.0, 3.0}, {2.0, 2.0}, {3.0, 3.0}}, + {0}); +} + +TYPED_TEST(PairwisePolygonDistanceTest, OnePairMultiPolygonOverlappingNoHole) +{ + this->run_single({0, 2}, + {0, 1, 2}, + {0, 4, 8}, + {{0.0, 0.0}, + {2.0, 0.0}, + {2.0, 2.0}, + {0.0, 0.0}, + {3.0, 3.0}, + {3.0, 4.0}, + {4.0, 4.0}, + {3.0, 3.0}}, + {0, 1}, + {0, 1}, + {0, 4}, + {{1.0, 1.0}, {3.0, 1.0}, {3.0, 3.0}, {1.0, 1.0}}, + {0}); +} + +TYPED_TEST(PairwisePolygonDistanceTest, OnePairMultiPolygonContainedNoHole) +{ + this->run_single({0, 2}, + {0, 1, 2}, + {0, 4, 8}, + {{0.0, 0.0}, + {2.0, 0.0}, + {2.0, 2.0}, + {0.0, 0.0}, + {1.0, 1.0}, + {1.0, 2.0}, + {2.0, 2.0}, + {1.0, 1.0}}, + {0, 1}, + {0, 1}, + {0, 4}, + {{0.5, 0.25}, {1.5, 0.25}, {1.5, 1.25}, {0.5, 0.25}}, + {0}); +} From 137a56790663e474abc62aaaf52045c90f95c5fc Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 11 Apr 2023 15:43:18 -0700 Subject: [PATCH 46/54] add two pair tests --- .../spatial/polygon_distance_test.cu | 549 +++++++++++------- 1 file changed, 339 insertions(+), 210 deletions(-) diff --git a/cpp/tests/experimental/spatial/polygon_distance_test.cu b/cpp/tests/experimental/spatial/polygon_distance_test.cu index a1d5c0e2d..3d3e42b6e 100644 --- a/cpp/tests/experimental/spatial/polygon_distance_test.cu +++ b/cpp/tests/experimental/spatial/polygon_distance_test.cu @@ -48,15 +48,15 @@ struct PairwisePolygonDistanceTest : BaseFixture { EXPECT_EQ(thrust::distance(got.begin(), ret), expected.size()); } - void run_single(std::initializer_list lhs_multipolygon_geometry_offsets, - std::initializer_list lhs_multipolygon_part_offsets, - std::initializer_list lhs_multipolygon_ring_offsets, - std::initializer_list> lhs_multipolygon_coordinates, - std::initializer_list rhs_multipolygon_geometry_offsets, - std::initializer_list rhs_multipolygon_part_offsets, - std::initializer_list rhs_multipolygon_ring_offsets, - std::initializer_list> rhs_multipolygon_coordinates, - std::initializer_list expected) + void run(std::initializer_list lhs_multipolygon_geometry_offsets, + std::initializer_list lhs_multipolygon_part_offsets, + std::initializer_list lhs_multipolygon_ring_offsets, + std::initializer_list> lhs_multipolygon_coordinates, + std::initializer_list rhs_multipolygon_geometry_offsets, + std::initializer_list rhs_multipolygon_part_offsets, + std::initializer_list rhs_multipolygon_ring_offsets, + std::initializer_list> rhs_multipolygon_coordinates, + std::initializer_list expected) { auto lhs = make_multipolygon_array(lhs_multipolygon_geometry_offsets, lhs_multipolygon_part_offsets, @@ -83,7 +83,7 @@ TYPED_TEST_CASE(PairwisePolygonDistanceTest, FloatingPointTypes); TYPED_TEST(PairwisePolygonDistanceTest, Empty) { - this->run_single({0}, {0}, {0}, {}, {0}, {0}, {0}, {}, {}); + this->run({0}, {0}, {0}, {}, {0}, {0}, {0}, {}, {}); } // Test Matrix @@ -94,260 +94,389 @@ TYPED_TEST(PairwisePolygonDistanceTest, Empty) TYPED_TEST(PairwisePolygonDistanceTest, OnePairSinglePolygonDisjointNoHole) { - this->run_single({0, 1}, - {0, 1}, - {0, 4}, - {{0, 0}, {0, 1}, {1, 1}, {1, 0}}, - {0, 1}, - {0, 1}, - {0, 4}, - {{-1, 0}, {-1, 1}, {-2, 0}, {-1, 0}}, - {1}); + this->run({0, 1}, + {0, 1}, + {0, 4}, + {{0, 0}, {0, 1}, {1, 1}, {1, 0}}, + {0, 1}, + {0, 1}, + {0, 4}, + {{-1, 0}, {-1, 1}, {-2, 0}, {-1, 0}}, + {1}); } TYPED_TEST(PairwisePolygonDistanceTest, OnePairSinglePolygonTouchingNoHole) { - this->run_single({0, 1}, - {0, 1}, - {0, 4}, - {{0, 0}, {0, 1}, {1, 1}, {1, 0}}, - {0, 1}, - {0, 1}, - {0, 4}, - {{1, 0}, {2, 0}, {2, 1}, {1, 0}}, - {0}); + this->run({0, 1}, + {0, 1}, + {0, 4}, + {{0, 0}, {0, 1}, {1, 1}, {1, 0}}, + {0, 1}, + {0, 1}, + {0, 4}, + {{1, 0}, {2, 0}, {2, 1}, {1, 0}}, + {0}); } TYPED_TEST(PairwisePolygonDistanceTest, OnePairSinglePolygonOverlappingNoHole) { - this->run_single({0, 1}, - {0, 1}, - {0, 4}, - {{0, 0}, {0, 1}, {1, 1}, {1, 0}}, - {0, 1}, - {0, 1}, - {0, 4}, - {{0.5, 0}, {2, 0}, {2, 1}, {0.5, 0}}, - {0}); + this->run({0, 1}, + {0, 1}, + {0, 4}, + {{0, 0}, {0, 1}, {1, 1}, {1, 0}}, + {0, 1}, + {0, 1}, + {0, 4}, + {{0.5, 0}, {2, 0}, {2, 1}, {0.5, 0}}, + {0}); } TYPED_TEST(PairwisePolygonDistanceTest, OnePairSinglePolygonContainedNoHole) { - this->run_single({0, 1}, - {0, 1}, - {0, 5}, - {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}, - {0, 1}, - {0, 1}, - {0, 5}, - {{0.25, 0.25}, {0.75, 0.25}, {0.75, 0.75}, {0.25, 0.75}, {0.25, 0.25}}, - {0}); + this->run({0, 1}, + {0, 1}, + {0, 5}, + {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}, + {0, 1}, + {0, 1}, + {0, 5}, + {{0.25, 0.25}, {0.75, 0.25}, {0.75, 0.75}, {0.25, 0.75}, {0.25, 0.25}}, + {0}); } TYPED_TEST(PairwisePolygonDistanceTest, OnePairSinglePolygonWithinNoHole) { - this->run_single({0, 1}, - {0, 1}, - {0, 5}, - {{0.25, 0.25}, {0.75, 0.25}, {0.75, 0.75}, {0.25, 0.75}, {0.25, 0.25}}, - {0, 1}, - {0, 1}, - {0, 5}, - {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}, - {0}); + this->run({0, 1}, + {0, 1}, + {0, 5}, + {{0.25, 0.25}, {0.75, 0.25}, {0.75, 0.75}, {0.25, 0.75}, {0.25, 0.25}}, + {0, 1}, + {0, 1}, + {0, 5}, + {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}, + {0}); } TYPED_TEST(PairwisePolygonDistanceTest, OnePairSinglePolygonDisjointHasHole) { - this->run_single({0, 1}, - {0, 2}, - {0, 4, 8}, - {{0.0, 0.0}, - {2.0, 0.0}, - {2.0, 2.0}, - {0.0, 0.0}, - {1.0, 0.75}, - {1.5, 0.75}, - {1.25, 1.0}, - {1.0, 0.75}}, - {0, 1}, - {0, 2}, - {0, 4, 8}, - {{-1.0, 0.0}, - {-1.0, -1.0}, - {-2.0, 0.0}, - {-1.0, 0.0}, - {-1.25, -0.25}, - {-1.25, -0.5}, - {-1.5, -0.25}, - {-1.25, -0.25}}, - {1}); + this->run({0, 1}, + {0, 2}, + {0, 4, 8}, + {{0.0, 0.0}, + {2.0, 0.0}, + {2.0, 2.0}, + {0.0, 0.0}, + {1.0, 0.75}, + {1.5, 0.75}, + {1.25, 1.0}, + {1.0, 0.75}}, + {0, 1}, + {0, 2}, + {0, 4, 8}, + {{-1.0, 0.0}, + {-1.0, -1.0}, + {-2.0, 0.0}, + {-1.0, 0.0}, + {-1.25, -0.25}, + {-1.25, -0.5}, + {-1.5, -0.25}, + {-1.25, -0.25}}, + {1}); } TYPED_TEST(PairwisePolygonDistanceTest, OnePairSinglePolygonDisjointHasHole2) { - this->run_single({0, 1}, - {0, 2}, - {0, 5, 10}, - {{0.0, 0.0}, - {10.0, 0.0}, - {10.0, 10.0}, - {0.0, 10.0}, - {0.0, 0.0}, - {2.0, 2.0}, - {2.0, 6.0}, - {6.0, 6.0}, - {6.0, 2.0}, - {2.0, 2.0}}, - {0, 1}, - {0, 1}, - {0, 5}, - {{3.0, 3.0}, {3.0, 4.0}, {4.0, 4.0}, {4.0, 3.0}, {3.0, 3.0}}, - {1}); + this->run({0, 1}, + {0, 2}, + {0, 5, 10}, + {{0.0, 0.0}, + {10.0, 0.0}, + {10.0, 10.0}, + {0.0, 10.0}, + {0.0, 0.0}, + {2.0, 2.0}, + {2.0, 6.0}, + {6.0, 6.0}, + {6.0, 2.0}, + {2.0, 2.0}}, + {0, 1}, + {0, 1}, + {0, 5}, + {{3.0, 3.0}, {3.0, 4.0}, {4.0, 4.0}, {4.0, 3.0}, {3.0, 3.0}}, + {1}); } TYPED_TEST(PairwisePolygonDistanceTest, OnePairSinglePolygonTouchingHasHole) { - this->run_single({0, 1}, - {0, 2}, - {0, 4, 8}, - {{0.0, 0.0}, - {2.0, 0.0}, - {2.0, 2.0}, - {0.0, 0.0}, - {1.0, 0.75}, - {1.5, 0.75}, - {1.25, 1.0}, - {1.0, 0.75}}, - {0, 1}, - {0, 2}, - {0, 4, 8}, - {{2.0, 0.0}, - {3.0, 0.0}, - {3.0, 1.0}, - {2.0, 0.0}, - {2.5, 0.25}, - {2.75, 0.25}, - {2.75, 0.5}, - {2.5, 0.25}}, - {0}); + this->run({0, 1}, + {0, 2}, + {0, 4, 8}, + {{0.0, 0.0}, + {2.0, 0.0}, + {2.0, 2.0}, + {0.0, 0.0}, + {1.0, 0.75}, + {1.5, 0.75}, + {1.25, 1.0}, + {1.0, 0.75}}, + {0, 1}, + {0, 2}, + {0, 4, 8}, + {{2.0, 0.0}, + {3.0, 0.0}, + {3.0, 1.0}, + {2.0, 0.0}, + {2.5, 0.25}, + {2.75, 0.25}, + {2.75, 0.5}, + {2.5, 0.25}}, + {0}); } TYPED_TEST(PairwisePolygonDistanceTest, OnePairSinglePolygonOverlappingHasHole) { - this->run_single({0, 1}, - {0, 2}, - {0, 5, 10}, - {{0, 0}, {4, 0}, {4, 4}, {0, 4}, {0, 0}, {2, 2}, {2, 3}, {3, 3}, {3, 2}, {2, 2}}, - {0, 1}, - {0, 2}, - {0, 4, 8}, - {{2, -1}, {5, 4}, {5, -1}, {2, -1}, {3, -0.5}, {4, 0}, {4, -0.5}, {3, -0.5}}, - {0}); + this->run({0, 1}, + {0, 2}, + {0, 5, 10}, + {{0, 0}, {4, 0}, {4, 4}, {0, 4}, {0, 0}, {2, 2}, {2, 3}, {3, 3}, {3, 2}, {2, 2}}, + {0, 1}, + {0, 2}, + {0, 4, 8}, + {{2, -1}, {5, 4}, {5, -1}, {2, -1}, {3, -0.5}, {4, 0}, {4, -0.5}, {3, -0.5}}, + {0}); } TYPED_TEST(PairwisePolygonDistanceTest, OnePairSinglePolygonContainedHasHole) { - this->run_single({0, 1}, - {0, 2}, - {0, 5, 10}, - {{0, 0}, {4, 0}, {4, 4}, {0, 4}, {0, 0}, {1, 3}, {1, 1}, {3, 1}, {1, 3}, {1, 1}}, - {0, 1}, - {0, 1}, - {0, 4}, - {{1, 3}, {3, 1}, {3, 3}, {1, 3}}, - {0}); + this->run({0, 1}, + {0, 2}, + {0, 5, 10}, + {{0, 0}, {4, 0}, {4, 4}, {0, 4}, {0, 0}, {1, 3}, {1, 1}, {3, 1}, {1, 3}, {1, 1}}, + {0, 1}, + {0, 1}, + {0, 4}, + {{1, 3}, {3, 1}, {3, 3}, {1, 3}}, + {0}); } TYPED_TEST(PairwisePolygonDistanceTest, OnePairSinglePolygonWithinHasHole) { - this->run_single({0, 1}, - {0, 1}, - {0, 4}, - {{1, 3}, {3, 1}, {3, 3}, {1, 3}}, - {0, 1}, - {0, 2}, - {0, 5, 9}, - {{0, 0}, {4, 0}, {4, 4}, {0, 4}, {0, 0}, {1, 1}, {3, 1}, {1, 3}, {1, 1}}, - {0}); + this->run({0, 1}, + {0, 1}, + {0, 4}, + {{1, 3}, {3, 1}, {3, 3}, {1, 3}}, + {0, 1}, + {0, 2}, + {0, 5, 9}, + {{0, 0}, {4, 0}, {4, 4}, {0, 4}, {0, 0}, {1, 1}, {3, 1}, {1, 3}, {1, 1}}, + {0}); } TYPED_TEST(PairwisePolygonDistanceTest, OnePairMultiPolygonDisjointNoHole) { - this->run_single({0, 2}, - {0, 1, 2}, - {0, 4, 8}, - {{0.0, 0.0}, - {2.0, 0.0}, - {2.0, 2.0}, - {0.0, 0.0}, - {3.0, 3.0}, - {3.0, 4.0}, - {4.0, 4.0}, - {3.0, 3.0}}, - {0, 1}, - {0, 1}, - {0, 4}, - {{-1.0, 0.0}, {-1.0, -1.0}, {-2.0, 0.0}, {-1.0, 0.0}}, - {1}); + this->run({0, 2}, + {0, 1, 2}, + {0, 4, 8}, + {{0.0, 0.0}, + {2.0, 0.0}, + {2.0, 2.0}, + {0.0, 0.0}, + {3.0, 3.0}, + {3.0, 4.0}, + {4.0, 4.0}, + {3.0, 3.0}}, + {0, 1}, + {0, 1}, + {0, 4}, + {{-1.0, 0.0}, {-1.0, -1.0}, {-2.0, 0.0}, {-1.0, 0.0}}, + {1}); } TYPED_TEST(PairwisePolygonDistanceTest, OnePairMultiPolygonTouchingNoHole) { - this->run_single({0, 2}, - {0, 1, 2}, - {0, 4, 8}, - {{0.0, 0.0}, - {2.0, 0.0}, - {2.0, 2.0}, - {0.0, 0.0}, - {3.0, 3.0}, - {3.0, 4.0}, - {4.0, 4.0}, - {3.0, 3.0}}, - {0, 1}, - {0, 1}, - {0, 4}, - {{3.0, 3.0}, {2.0, 3.0}, {2.0, 2.0}, {3.0, 3.0}}, - {0}); + this->run({0, 2}, + {0, 1, 2}, + {0, 4, 8}, + {{0.0, 0.0}, + {2.0, 0.0}, + {2.0, 2.0}, + {0.0, 0.0}, + {3.0, 3.0}, + {3.0, 4.0}, + {4.0, 4.0}, + {3.0, 3.0}}, + {0, 1}, + {0, 1}, + {0, 4}, + {{3.0, 3.0}, {2.0, 3.0}, {2.0, 2.0}, {3.0, 3.0}}, + {0}); } TYPED_TEST(PairwisePolygonDistanceTest, OnePairMultiPolygonOverlappingNoHole) { - this->run_single({0, 2}, - {0, 1, 2}, - {0, 4, 8}, - {{0.0, 0.0}, - {2.0, 0.0}, - {2.0, 2.0}, - {0.0, 0.0}, - {3.0, 3.0}, - {3.0, 4.0}, - {4.0, 4.0}, - {3.0, 3.0}}, - {0, 1}, - {0, 1}, - {0, 4}, - {{1.0, 1.0}, {3.0, 1.0}, {3.0, 3.0}, {1.0, 1.0}}, - {0}); + this->run({0, 2}, + {0, 1, 2}, + {0, 4, 8}, + {{0.0, 0.0}, + {2.0, 0.0}, + {2.0, 2.0}, + {0.0, 0.0}, + {3.0, 3.0}, + {3.0, 4.0}, + {4.0, 4.0}, + {3.0, 3.0}}, + {0, 1}, + {0, 1}, + {0, 4}, + {{1.0, 1.0}, {3.0, 1.0}, {3.0, 3.0}, {1.0, 1.0}}, + {0}); } TYPED_TEST(PairwisePolygonDistanceTest, OnePairMultiPolygonContainedNoHole) { - this->run_single({0, 2}, - {0, 1, 2}, - {0, 4, 8}, - {{0.0, 0.0}, - {2.0, 0.0}, - {2.0, 2.0}, - {0.0, 0.0}, - {1.0, 1.0}, - {1.0, 2.0}, - {2.0, 2.0}, - {1.0, 1.0}}, - {0, 1}, - {0, 1}, - {0, 4}, - {{0.5, 0.25}, {1.5, 0.25}, {1.5, 1.25}, {0.5, 0.25}}, - {0}); + this->run({0, 2}, + {0, 1, 2}, + {0, 4, 8}, + {{0.0, 0.0}, + {2.0, 0.0}, + {2.0, 2.0}, + {0.0, 0.0}, + {1.0, 1.0}, + {1.0, 2.0}, + {2.0, 2.0}, + {1.0, 1.0}}, + {0, 1}, + {0, 1}, + {0, 4}, + {{0.5, 0.25}, {1.5, 0.25}, {1.5, 1.25}, {0.5, 0.25}}, + {0}); +} + +// Two Pair Tests + +TYPED_TEST(PairwisePolygonDistanceTest, TwoPairSinglePolygonNoHole) +{ + this->run({0, 1, 2}, + {0, 1, 2}, + {0, 4, 8}, + {{0.0, 0.0}, + {2.0, 0.0}, + {2.0, 2.0}, + {0.0, 0.0}, + {3.0, 3.0}, + {3.0, 4.0}, + {4.0, 4.0}, + {3.0, 3.0}}, + {0, 1, 2}, + {0, 1, 2}, + {0, 4, 8}, + {{3.0, 0.0}, + {4.0, 0.0}, + {4.0, 1.0}, + {3.0, 0.0}, + {3.0, 3.0}, + {3.0, 2.0}, + {2.0, 2.0}, + {3.0, 3.0}}, + {1, 0}); +} + +TYPED_TEST(PairwisePolygonDistanceTest, TwoPairSinglePolygonHasHole) +{ + this->run({0, 1, 2}, + {0, 1, 3}, + {0, 4, 8, 12}, + {{0.0, 0.0}, + {2.0, 0.0}, + {2.0, 2.0}, + {0.0, 0.0}, + {3.0, 3.0}, + {3.0, 4.0}, + {4.0, 4.0}, + {3.0, 3.0}, + {3.25, 3.5}, + {3.5, 3.5}, + {3.5, 3.75}, + {3.25, 3.5}}, + {0, 1, 2}, + {0, 1, 2}, + {0, 4, 8}, + {{3.0, 0.0}, + {4.0, 0.0}, + {4.0, 1.0}, + {3.0, 0.0}, + {3.0, 3.0}, + {3.0, 2.0}, + {2.0, 2.0}, + {3.0, 3.0}}, + {1, 0}); +} + +TYPED_TEST(PairwisePolygonDistanceTest, TwoPairMultiPolygonNoHole) +{ + this->run({0, 1, 3}, + {0, 1, 2, 3}, + {0, 4, 8, 12}, + {{0.0, 0.0}, + {2.0, 0.0}, + {2.0, 2.0}, + {0.0, 0.0}, + {3.0, 3.0}, + {3.0, 4.0}, + {4.0, 4.0}, + {3.0, 3.0}, + {3.0, 3.0}, + {5.0, 3.0}, + {4.0, 2.0}, + {3.0, 3.0}}, + {0, 1, 2}, + {0, 1, 2}, + {0, 4, 8}, + {{3.0, 0.0}, + {4.0, 0.0}, + {4.0, 1.0}, + {3.0, 0.0}, + {3.0, 3.0}, + {3.0, 2.0}, + {2.0, 2.0}, + {3.0, 3.0}}, + {1, 0}); +} + +TYPED_TEST(PairwisePolygonDistanceTest, TwoPairMultiPolygonHasHole) +{ + this->run({0, 1, 3}, + {0, 1, 2, 4}, + {0, 4, 8, 12, 16}, + {{0.0, 0.0}, + {2.0, 0.0}, + {2.0, 2.0}, + {0.0, 0.0}, + + {3.0, 3.0}, + {3.0, 4.0}, + {4.0, 4.0}, + {3.0, 3.0}, + + {3.0, 3.0}, + {5.0, 3.0}, + {4.0, 2.0}, + {3.0, 3.0}, + + {3.5, 2.9}, + {4.5, 2.9}, + {4, 2.4}, + {3.5, 2.9}}, + {0, 1, 2}, + {0, 1, 2}, + {0, 4, 8}, + {{3.0, 0.0}, + {4.0, 0.0}, + {4.0, 1.0}, + {3.0, 0.0}, + {3.0, 3.0}, + {3.0, 2.0}, + {2.0, 2.0}, + {3.0, 3.0}}, + {1, 0}); } From b77285cd028349fbeb520d39d2ca87bdf2e7e552 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 11 Apr 2023 15:49:31 -0700 Subject: [PATCH 47/54] style --- .../cuspatial/experimental/detail/distance_utils.cuh | 2 +- .../cuspatial/experimental/detail/linestring_distance.cuh | 3 +-- cpp/include/cuspatial_test/base_fixture.hpp | 6 ++++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/distance_utils.cuh b/cpp/include/cuspatial/experimental/detail/distance_utils.cuh index b9722e4f2..bf17985ac 100644 --- a/cpp/include/cuspatial/experimental/detail/distance_utils.cuh +++ b/cpp/include/cuspatial/experimental/detail/distance_utils.cuh @@ -14,8 +14,8 @@ * limitations under the License. */ -#include #include +#include #include #include #include diff --git a/cpp/include/cuspatial/experimental/detail/linestring_distance.cuh b/cpp/include/cuspatial/experimental/detail/linestring_distance.cuh index 7077aaa6d..bb146406a 100644 --- a/cpp/include/cuspatial/experimental/detail/linestring_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/linestring_distance.cuh @@ -16,8 +16,8 @@ #pragma once -#include #include +#include #include #include @@ -32,7 +32,6 @@ namespace cuspatial { - template OutputIt pairwise_linestring_distance(MultiLinestringRange1 multilinestrings1, MultiLinestringRange2 multilinestrings2, diff --git a/cpp/include/cuspatial_test/base_fixture.hpp b/cpp/include/cuspatial_test/base_fixture.hpp index 8f8344896..941bee951 100644 --- a/cpp/include/cuspatial_test/base_fixture.hpp +++ b/cpp/include/cuspatial_test/base_fixture.hpp @@ -54,7 +54,8 @@ class RMMResourceMixin { * class MyTestFixture : public cuspatial::test::BaseFixture {}; * ``` */ -class BaseFixture : public RMMResourceMixin, public ::testing::Test {}; +class BaseFixture : public RMMResourceMixin, public ::testing::Test { +}; /** * @brief Base test fixture class from which libcuspatial test with only value parameterization @@ -80,7 +81,8 @@ class BaseFixture : public RMMResourceMixin, public ::testing::Test {}; */ template class BaseFixtureWithParam : public RMMResourceMixin, - public ::testing::TestWithParam> {}; + public ::testing::TestWithParam> { +}; /** * @brief Floating point types to be used in libcuspatial tests From 9224c4aa4209e48dcbef2dbffce4ae0c0fbc1a2e Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 11 Apr 2023 16:14:09 -0700 Subject: [PATCH 48/54] address review comments --- .../experimental/detail/polygon_distance.cuh | 11 +++++++++-- .../cuspatial/experimental/polygon_distance.cuh | 6 +++--- cpp/include/cuspatial_test/test_util.cuh | 14 ++++++++++---- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/polygon_distance.cuh b/cpp/include/cuspatial/experimental/detail/polygon_distance.cuh index f1b36b2d1..d4053e98d 100644 --- a/cpp/include/cuspatial/experimental/detail/polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/polygon_distance.cuh @@ -36,6 +36,14 @@ namespace cuspatial { +/** + * @brief Implementation of pairwise distance between two multipolygon ranges. + * + * All points in lhs and rhs are tested for intersection its corresponding pair, + * and if any intersection is found, the distance between the two polygons is 0. + * Otherwise, the distance is the minimum distance between any two segments in the + * multipolygon pair. + */ template OutputIt pairwise_polygon_distance(MultipolygonRangeA lhs, MultipolygonRangeB rhs, @@ -73,8 +81,7 @@ OutputIt pairwise_polygon_distance(MultipolygonRangeA lhs, distances_first + lhs.size(), std::numeric_limits::max()); - std::size_t constexpr threads_per_block = 256; - std::size_t const num_blocks = (lhs.num_points() + threads_per_block - 1) / threads_per_block; + auto [threads_per_block, num_blocks] = grid_1d(lhs.num_points()); detail::linestring_distance<<>>( lhs_as_multilinestrings, rhs_as_multilinestrings, intersects.begin(), distances_first); diff --git a/cpp/include/cuspatial/experimental/polygon_distance.cuh b/cpp/include/cuspatial/experimental/polygon_distance.cuh index 10404ea58..63191aa71 100644 --- a/cpp/include/cuspatial/experimental/polygon_distance.cuh +++ b/cpp/include/cuspatial/experimental/polygon_distance.cuh @@ -22,15 +22,15 @@ namespace cuspatial { /** * @ingroup distance - * @brief Computes pairwise multipoint to multipolygon distance + * @brief Computes pairwise multipolygon to multipolygon distance * * @tparam MultiPolygonRangeA An instance of template type `multipolygon_range` * @tparam MultiPolygonRangeB 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 - * @param + * @param lhs The first multipolygon range to compute distance from + * @param rhs The second multipolygon range to compute distance to * @param stream The CUDA stream on which to perform computations * @return Output Iterator past the last distance computed * diff --git a/cpp/include/cuspatial_test/test_util.cuh b/cpp/include/cuspatial_test/test_util.cuh index 43bc98afe..2312fb85f 100644 --- a/cpp/include/cuspatial_test/test_util.cuh +++ b/cpp/include/cuspatial_test/test_util.cuh @@ -100,17 +100,23 @@ void print_device_range(Iter begin, } /** - * @brief + * @brief Print a device vector. + * + * @note Copies the device vector to host before printing. + * + * @tparam Vector The device vector type + * @param vec The device vector to print + * @param pre String to print before the device vector + * @param post String to print after the device vector */ -template +template void print_device_vector(Vector const& vec, std::string_view pre = "", std::string_view post = "\n") { using T = typename Vector::value_type; auto hvec = to_host(vec); std::cout << pre; - std::for_each( - hvec.begin(), hvec.end(), [](auto const& x) { std::cout << static_cast(x) << " "; }); + std::for_each(hvec.begin(), hvec.end(), [](auto const& x) { std::cout << x << " "; }); std::cout << post; } From cc66aeb027810a84edfb1702c83aaefb11ab810e Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 13 Apr 2023 10:58:45 -0700 Subject: [PATCH 49/54] add column API and tests, create host-device expect function --- cpp/CMakeLists.txt | 1 + cpp/include/cuspatial/assert.cuh | 35 ++++ .../cuspatial/detail/utility/validation.hpp | 43 ++-- .../cuspatial/distance/polygon_distance.hpp | 40 ++++ cpp/include/cuspatial/error.hpp | 29 ++- .../ranges/multilinestring_range.cuh | 12 +- .../experimental/ranges/multipoint_range.cuh | 8 +- cpp/src/spatial/polygon_distance.cu | 113 ++++++++++ cpp/tests/CMakeLists.txt | 3 + cpp/tests/spatial/polygon_distance_test.cpp | 197 ++++++++++++++++++ 10 files changed, 449 insertions(+), 32 deletions(-) create mode 100644 cpp/include/cuspatial/assert.cuh create mode 100644 cpp/include/cuspatial/distance/polygon_distance.hpp create mode 100644 cpp/src/spatial/polygon_distance.cu create mode 100644 cpp/tests/spatial/polygon_distance_test.cpp diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 1b6afb223..5c0193462 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -134,6 +134,7 @@ add_library(cuspatial src/spatial/point_distance.cu src/spatial/point_linestring_distance.cu src/spatial/point_polygon_distance.cu + src/spatial/polygon_distance.cu src/spatial/point_linestring_nearest_points.cu src/spatial/sinusoidal_projection.cu src/trajectory/derive_trajectories.cu diff --git a/cpp/include/cuspatial/assert.cuh b/cpp/include/cuspatial/assert.cuh new file mode 100644 index 000000000..c5e13d6f4 --- /dev/null +++ b/cpp/include/cuspatial/assert.cuh @@ -0,0 +1,35 @@ +/* + * 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 + +/** + * @brief `assert`-like macro for device code + * + * This is effectively the same as the standard `assert` macro, except it + * relies on the `__PRETTY_FUNCTION__` macro which is specific to GCC and Clang + * to produce better assert messages. + */ +#if !defined(NDEBUG) && defined(__CUDA_ARCH__) && (defined(__clang__) || defined(__GNUC__)) +#define __ASSERT_STR_HELPER(x) #x +#define cuspatial_assert(e) \ + ((e) ? static_cast(0) \ + : __assert_fail(__ASSERT_STR_HELPER(e), __FILE__, __LINE__, __PRETTY_FUNCTION__)) +#else +#define cuspatial_assert(e) (static_cast(0)) +#endif diff --git a/cpp/include/cuspatial/detail/utility/validation.hpp b/cpp/include/cuspatial/detail/utility/validation.hpp index 7fe5742ea..1a4bd2001 100644 --- a/cpp/include/cuspatial/detail/utility/validation.hpp +++ b/cpp/include/cuspatial/detail/utility/validation.hpp @@ -35,10 +35,10 @@ * [2]: https://arrow.apache.org/docs/format/Columnar.html#variable-size-binary-layout */ #define CUSPATIAL_EXPECTS_VALID_LINESTRING_SIZES(num_linestring_points, num_linestring_offsets) \ - CUSPATIAL_EXPECTS(num_linestring_offsets > 0, \ - "Polygon offsets must contain at least one (1) value"); \ - CUSPATIAL_EXPECTS(num_linestring_points >= 2 * (num_linestring_offsets - 1), \ - "Each linestring must have at least two vertices"); + CUSPATIAL_HOST_DEVICE_EXPECTS(num_linestring_offsets > 0, \ + "Polygon offsets must contain at least one (1) value"); \ + CUSPATIAL_HOST_DEVICE_EXPECTS(num_linestring_points >= 2 * (num_linestring_offsets - 1), \ + "Each linestring must have at least two vertices"); /** * @brief Macro for validating the data array sizes for multilinestrings. @@ -57,10 +57,10 @@ * [1]: https://github.com/geoarrow/geoarrow/blob/main/format.md * [2]: https://arrow.apache.org/docs/format/Columnar.html#variable-size-binary-layout */ -#define CUSPATIAL_EXPECTS_VALID_MULTILINESTRING_SIZES( \ - num_linestring_points, num_multilinestring_offsets, num_linestring_offsets) \ - CUSPATIAL_EXPECTS(num_multilinestring_offsets > 0, \ - "Multilinestring offsets must contain at least one (1) value"); \ +#define CUSPATIAL_EXPECTS_VALID_MULTILINESTRING_SIZES( \ + num_linestring_points, num_multilinestring_offsets, num_linestring_offsets) \ + CUSPATIAL_HOST_DEVICE_EXPECTS(num_multilinestring_offsets > 0, \ + "Multilinestring offsets must contain at least one (1) value"); \ CUSPATIAL_EXPECTS_VALID_LINESTRING_SIZES(num_linestring_points, num_linestring_offsets); /** @@ -84,15 +84,16 @@ * [1]: https://github.com/geoarrow/geoarrow/blob/main/format.md * [2]: https://arrow.apache.org/docs/format/Columnar.html#variable-size-binary-layout */ -#define CUSPATIAL_EXPECTS_VALID_POLYGON_SIZES( \ - num_poly_points, num_poly_offsets, num_poly_ring_offsets) \ - CUSPATIAL_EXPECTS(num_poly_offsets > 0, "Polygon offsets must contain at least one (1) value"); \ - CUSPATIAL_EXPECTS(num_poly_ring_offsets > 0, \ - "Polygon ring offsets must contain at least one (1) value"); \ - CUSPATIAL_EXPECTS(num_poly_ring_offsets >= num_poly_offsets, \ - "Each polygon must have at least one (1) ring"); \ - CUSPATIAL_EXPECTS(num_poly_points >= 4 * (num_poly_ring_offsets - 1), \ - "Each ring must have at least four (4) vertices"); +#define CUSPATIAL_EXPECTS_VALID_POLYGON_SIZES( \ + num_poly_points, num_poly_offsets, num_poly_ring_offsets) \ + CUSPATIAL_HOST_DEVICE_EXPECTS(num_poly_offsets > 0, \ + "Polygon offsets must contain at least one (1) value"); \ + CUSPATIAL_HOST_DEVICE_EXPECTS(num_poly_ring_offsets > 0, \ + "Polygon ring offsets must contain at least one (1) value"); \ + CUSPATIAL_HOST_DEVICE_EXPECTS(num_poly_ring_offsets >= num_poly_offsets, \ + "Each polygon must have at least one (1) ring"); \ + CUSPATIAL_HOST_DEVICE_EXPECTS(num_poly_points >= 4 * (num_poly_ring_offsets - 1), \ + "Each ring must have at least four (4) vertices"); /** * @brief Macro for validating the data array sizes for a multipolygon. @@ -116,8 +117,8 @@ * [1]: https://github.com/geoarrow/geoarrow/blob/main/format.md * [2]: https://arrow.apache.org/docs/format/Columnar.html#variable-size-binary-layout */ -#define CUSPATIAL_EXPECTS_VALID_MULTIPOLYGON_SIZES( \ - num_poly_points, num_multipoly_offsets, num_poly_offsets, num_poly_ring_offsets) \ - CUSPATIAL_EXPECTS(num_multipoly_offsets > 0, \ - "Multipolygon offsets must contain at least one (1) value"); \ +#define CUSPATIAL_EXPECTS_VALID_MULTIPOLYGON_SIZES( \ + num_poly_points, num_multipoly_offsets, num_poly_offsets, num_poly_ring_offsets) \ + CUSPATIAL_HOST_DEVICE_EXPECTS(num_multipoly_offsets > 0, \ + "Multipolygon offsets must contain at least one (1) value"); \ CUSPATIAL_EXPECTS_VALID_POLYGON_SIZES(num_poly_points, num_poly_offsets, num_poly_ring_offsets); diff --git a/cpp/include/cuspatial/distance/polygon_distance.hpp b/cpp/include/cuspatial/distance/polygon_distance.hpp new file mode 100644 index 000000000..ee0129992 --- /dev/null +++ b/cpp/include/cuspatial/distance/polygon_distance.hpp @@ -0,0 +1,40 @@ +/* + * 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 + +namespace cuspatial { + +/** + * @brief + * + * @param lhs + * @param rhs + * @param mr + * @return std::unique_ptr + */ +std::unique_ptr pairwise_polygon_distance( + geometry_column_view const& lhs, + geometry_column_view const& rhs, + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); + +} // namespace cuspatial diff --git a/cpp/include/cuspatial/error.hpp b/cpp/include/cuspatial/error.hpp index b1190c8ac..d2973349f 100644 --- a/cpp/include/cuspatial/error.hpp +++ b/cpp/include/cuspatial/error.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2022, 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. @@ -16,6 +16,8 @@ #pragma once +#include + #include #include @@ -76,6 +78,31 @@ struct cuda_error : public std::runtime_error { : throw cuspatial::logic_error("cuSpatial failure at: " __FILE__ \ ":" CUSPATIAL_STRINGIFY(__LINE__) ": " reason) +/**---------------------------------------------------------------------------* + * @brief Macro for checking (pre-)conditions that throws an exception when + * a condition is violated. + * + * Example usage: + * + * @code + * CUSPATIAL_HOST_DEVICE_EXPECTS(lhs->dtype == rhs->dtype, "Column type mismatch"); + * @endcode + * + * @param[in] cond Expression that evaluates to true or false + * @param[in] reason String literal description of the reason that cond is + * expected to be true + * + * (if on host) + * @throw cuspatial::logic_error if the condition evaluates to false. + * (if on device) + * program terminates and assertion error message is printed to stderr. + *---------------------------------------------------------------------------**/ +#ifndef __CUDA_ARCH__ +#define CUSPATIAL_HOST_DEVICE_EXPECTS(cond, reason) CUSPATIAL_EXPECTS(cond, reason) +#else +#define CUSPATIAL_HOST_DEVICE_EXPECTS(cond, reason) cuspatial_assert(cond&& reason) +#endif + /**---------------------------------------------------------------------------* * @brief Indicates that an erroneous code path has been taken. * diff --git a/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh b/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh index 3637c327e..3caa568e2 100644 --- a/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multilinestring_range.cuh @@ -59,12 +59,12 @@ class multilinestring_range { using point_t = iterator_value_type; using element_t = iterator_vec_base_type; - multilinestring_range(GeometryIterator geometry_begin, - GeometryIterator geometry_end, - PartIterator part_begin, - PartIterator part_end, - VecIterator points_begin, - VecIterator points_end); + CUSPATIAL_HOST_DEVICE multilinestring_range(GeometryIterator geometry_begin, + GeometryIterator geometry_end, + PartIterator part_begin, + PartIterator part_end, + VecIterator points_begin, + VecIterator points_end); /// Return the number of multilinestrings in the array. CUSPATIAL_HOST_DEVICE auto size() { return num_multilinestrings(); } diff --git a/cpp/include/cuspatial/experimental/ranges/multipoint_range.cuh b/cpp/include/cuspatial/experimental/ranges/multipoint_range.cuh index 7117d28cc..82323e9ed 100644 --- a/cpp/include/cuspatial/experimental/ranges/multipoint_range.cuh +++ b/cpp/include/cuspatial/experimental/ranges/multipoint_range.cuh @@ -57,10 +57,10 @@ class multipoint_range { /** * @brief Construct a new multipoint array object */ - multipoint_range(GeometryIterator geometry_begin, - GeometryIterator geometry_end, - VecIterator points_begin, - VecIterator points_end); + CUSPATIAL_HOST_DEVICE multipoint_range(GeometryIterator geometry_begin, + GeometryIterator geometry_end, + VecIterator points_begin, + VecIterator points_end); /** * @brief Returns the number of multipoints in the array. */ diff --git a/cpp/src/spatial/polygon_distance.cu b/cpp/src/spatial/polygon_distance.cu new file mode 100644 index 000000000..65143e137 --- /dev/null +++ b/cpp/src/spatial/polygon_distance.cu @@ -0,0 +1,113 @@ +/* + * 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/iterator.hpp" +#include "../utility/multi_geometry_dispatch.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace cuspatial { + +namespace detail { + +namespace { + +template +struct pairwise_polygon_distance_impl { + using SizeType = cudf::device_span::size_type; + + template )> + std::unique_ptr operator()(geometry_column_view const& lhs, + geometry_column_view const& rhs, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) + { + auto lhs_range = make_multipolygon_range(lhs); + auto rhs_range = make_multipolygon_range(rhs); + + auto output = cudf::make_numeric_column( + lhs.coordinate_type(), lhs.size(), cudf::mask_state::UNALLOCATED, stream, mr); + + cuspatial::pairwise_polygon_distance( + lhs_range, rhs_range, output->mutable_view().begin(), stream); + return output; + } + + template ), typename... Args> + std::unique_ptr operator()(Args&&...) + + { + CUSPATIAL_FAIL("polygon distance API only supports floating point coordinates."); + } +}; + +} // namespace + +template +struct pairwise_polygon_distance { + std::unique_ptr operator()(geometry_column_view const& lhs, + geometry_column_view const& rhs, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) + { + return cudf::type_dispatcher( + lhs.coordinate_type(), + pairwise_polygon_distance_impl{}, + lhs, + rhs, + stream, + mr); + } +}; + +} // namespace detail + +std::unique_ptr pairwise_polygon_distance(geometry_column_view const& lhs, + geometry_column_view const& rhs, + rmm::mr::device_memory_resource* mr) +{ + CUSPATIAL_EXPECTS(lhs.geometry_type() == geometry_type_id::POLYGON && + rhs.geometry_type() == geometry_type_id::POLYGON, + "Unexpected input geometry types."); + + CUSPATIAL_EXPECTS(lhs.coordinate_type() == rhs.coordinate_type(), + "Input geometries must have the same coordinate data types."); + + return multi_geometry_double_dispatch( + lhs.collection_type(), rhs.collection_type(), lhs, rhs, rmm::cuda_stream_default, mr); +} + +} // namespace cuspatial diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index d34472217..01a044116 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -90,6 +90,9 @@ ConfigureTest(LINESTRING_DISTANCE_TEST ConfigureTest(POINT_POLYGON_DISTANCE_TEST spatial/point_polygon_distance_test.cpp) +ConfigureTest(POLYGON_DISTANCE_TEST + spatial/polygon_distance_test.cpp) + ConfigureTest(LINESTRING_INTERSECTION_TEST spatial/linestring_intersection_test.cpp) diff --git a/cpp/tests/spatial/polygon_distance_test.cpp b/cpp/tests/spatial/polygon_distance_test.cpp new file mode 100644 index 000000000..a28d7a727 --- /dev/null +++ b/cpp/tests/spatial/polygon_distance_test.cpp @@ -0,0 +1,197 @@ +/* + * 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 + +#include +#include + +#include + +#include + +#include +#include + +using namespace cuspatial; +using namespace cuspatial::test; + +using namespace cudf; +using namespace cudf::test; + +template +struct PairwisePolygonDistanceTestBase : ::testing::Test { + void run_single(geometry_column_view lhs, + geometry_column_view rhs, + std::initializer_list expected) + { + auto got = pairwise_polygon_distance(lhs, rhs); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*got, fixed_width_column_wrapper(expected)); + } + rmm::cuda_stream_view stream() { return cudf::get_default_stream(); } +}; + +template +struct PairwisePolygonDistanceTestEmpty : PairwisePolygonDistanceTestBase { + void SetUp() + { + [[maybe_unused]] collection_type_id _; + std::tie(_, empty_polygon_column) = make_polygon_column({0}, {0}, {}, this->stream()); + std::tie(_, empty_multipolygon_column) = + make_polygon_column({0}, {0}, {0}, {}, this->stream()); + } + + geometry_column_view empty_polygon() + { + return geometry_column_view( + empty_polygon_column->view(), collection_type_id::SINGLE, geometry_type_id::POLYGON); + } + + geometry_column_view empty_multipolygon() + { + return geometry_column_view( + empty_multipolygon_column->view(), collection_type_id::MULTI, geometry_type_id::POLYGON); + } + + std::unique_ptr empty_polygon_column; + std::unique_ptr empty_multipolygon_column; +}; + +using TestTypes = ::testing::Types; +TYPED_TEST_CASE(PairwisePolygonDistanceTestEmpty, TestTypes); +TYPED_TEST_CASE(PairwisePolygonDistanceTestOnePair, TestTypes); + +template +struct PairwisePolygonDistanceTestOnePair : PairwisePolygonDistanceTestBase { + void SetUp() + { + [[maybe_unused]] collection_type_id _; + std::tie(_, one_polygon_column) = + make_polygon_column({0, 1}, {0, 4}, {1, 1, 1, 2, 2, 2, 1, 1}, this->stream()); + std::tie(_, one_multipolygon_column) = + make_polygon_column({0, 1}, {0, 1}, {0, 4}, {1, 1, 1, 2, 2, 2, 1, 1}, this->stream()); + } + + geometry_column_view one_polygon() + { + return geometry_column_view( + one_polygon_column->view(), collection_type_id::SINGLE, geometry_type_id::POLYGON); + } + + geometry_column_view one_multipolygon() + { + return geometry_column_view( + one_multipolygon_column->view(), collection_type_id::MULTI, geometry_type_id::POLYGON); + } + + std::unique_ptr one_polygon_column; + std::unique_ptr one_multipolygon_column; +}; + +struct PairwisePolygonDistanceTestUntyped : testing::Test { + rmm::cuda_stream_view stream() { return cudf::get_default_stream(); } +}; +TYPED_TEST(PairwisePolygonDistanceTestEmpty, SingleToSingleEmpty) +{ + CUSPATIAL_RUN_TEST(this->run_single, this->empty_polygon(), this->empty_polygon(), {}); +}; + +TYPED_TEST(PairwisePolygonDistanceTestEmpty, SingleToMultiEmpty) +{ + CUSPATIAL_RUN_TEST(this->run_single, this->empty_polygon(), this->empty_multipolygon(), {}); +}; + +TYPED_TEST(PairwisePolygonDistanceTestEmpty, MultiToSingleEmpty) +{ + CUSPATIAL_RUN_TEST(this->run_single, this->empty_multipolygon(), this->empty_polygon(), {}); +}; + +TYPED_TEST(PairwisePolygonDistanceTestEmpty, MultiToMultiEmpty) +{ + CUSPATIAL_RUN_TEST(this->run_single, this->empty_multipolygon(), this->empty_multipolygon(), {}); +}; + +TYPED_TEST(PairwisePolygonDistanceTestOnePair, SingleToSingleOnePair) +{ + CUSPATIAL_RUN_TEST(this->run_single, this->one_polygon(), this->one_polygon(), {0}); +}; + +TYPED_TEST(PairwisePolygonDistanceTestOnePair, SingleToMultiOnePair) +{ + CUSPATIAL_RUN_TEST(this->run_single, this->one_polygon(), this->one_multipolygon(), {0}); +}; + +TYPED_TEST(PairwisePolygonDistanceTestOnePair, MultiToSingleOnePair) +{ + CUSPATIAL_RUN_TEST(this->run_single, this->one_multipolygon(), this->one_polygon(), {0}); +}; + +TYPED_TEST(PairwisePolygonDistanceTestOnePair, MultiToMultiOnePair) +{ + CUSPATIAL_RUN_TEST(this->run_single, this->one_multipolygon(), this->one_multipolygon(), {0}); +}; + +TEST_F(PairwisePolygonDistanceTestUntyped, SizeMismatch) +{ + auto [ptype, polygons1] = make_polygon_column( + {0, 1, 2}, {0, 4, 8}, {0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0}, this->stream()); + + auto [polytype, polygons2] = + make_polygon_column({0, 1}, {0, 1}, {0, 4}, {1, 1, 1, 2, 2, 2, 1, 1}, this->stream()); + + auto polygons1_view = + geometry_column_view(polygons1->view(), ptype, geometry_type_id::LINESTRING); + auto polygons2_view = + geometry_column_view(polygons1->view(), polytype, geometry_type_id::POLYGON); + + EXPECT_THROW(pairwise_polygon_distance(polygons1_view, polygons2_view), cuspatial::logic_error); +}; + +TEST_F(PairwisePolygonDistanceTestUntyped, TypeMismatch) +{ + auto [ptype, polygons1] = make_polygon_column( + {0, 1, 2}, {0, 4, 8}, {0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0}, this->stream()); + + auto [polytype, polygons2] = + make_polygon_column({0, 1}, {0, 1}, {0, 4}, {1, 1, 1, 2, 2, 2, 1, 1}, this->stream()); + + auto polygons1_view = + geometry_column_view(polygons1->view(), ptype, geometry_type_id::LINESTRING); + auto polygons2_view = + geometry_column_view(polygons2->view(), polytype, geometry_type_id::POLYGON); + + EXPECT_THROW(pairwise_polygon_distance(polygons1_view, polygons2_view), cuspatial::logic_error); +}; + +TEST_F(PairwisePolygonDistanceTestUntyped, WrongGeometryType) +{ + auto [ptype, points] = make_point_column({0, 1}, {0.0, 0.0}, this->stream()); + + auto [polytype, polygons] = + make_polygon_column({0, 1}, {0, 1}, {0, 4}, {1, 1, 1, 2, 2, 2, 1, 1}, this->stream()); + + auto points_view = geometry_column_view(points->view(), ptype, geometry_type_id::POINT); + auto polygons_view = geometry_column_view(polygons->view(), polytype, geometry_type_id::POLYGON); + + EXPECT_THROW(pairwise_polygon_distance(points_view, polygons_view), cuspatial::logic_error); +}; From b77718b1e6bb586e2c66e65f8ebbfce6cf556f6e Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 13 Apr 2023 11:59:09 -0700 Subject: [PATCH 50/54] initial addition of python test and API --- python/cuspatial/cuspatial/__init__.py | 1 + .../_lib/cpp/distance/polygon_distance.pxd | 17 ++ python/cuspatial/cuspatial/_lib/distance.pyx | 26 +++ .../cuspatial/core/spatial/__init__.py | 2 + .../cuspatial/core/spatial/distance.py | 58 +++++++ .../test_pairwise_point_polygon_distance.py | 7 - .../test_pairwise_polygon_distance.py | 149 ++++++++++++++++++ 7 files changed, 253 insertions(+), 7 deletions(-) create mode 100644 python/cuspatial/cuspatial/_lib/cpp/distance/polygon_distance.pxd create mode 100644 python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_polygon_distance.py diff --git a/python/cuspatial/cuspatial/__init__.py b/python/cuspatial/cuspatial/__init__.py index 2f3a27267..6f68350f4 100644 --- a/python/cuspatial/cuspatial/__init__.py +++ b/python/cuspatial/cuspatial/__init__.py @@ -11,6 +11,7 @@ pairwise_point_linestring_distance, pairwise_point_linestring_nearest_points, pairwise_point_polygon_distance, + pairwise_polygon_distance, point_in_polygon, points_in_spatial_window, polygon_bounding_boxes, diff --git a/python/cuspatial/cuspatial/_lib/cpp/distance/polygon_distance.pxd b/python/cuspatial/cuspatial/_lib/cpp/distance/polygon_distance.pxd new file mode 100644 index 000000000..9d2bfab39 --- /dev/null +++ b/python/cuspatial/cuspatial/_lib/cpp/distance/polygon_distance.pxd @@ -0,0 +1,17 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. + +from libcpp.memory cimport unique_ptr + +from cudf._lib.cpp.column.column cimport column + +from cuspatial._lib.cpp.column.geometry_column_view cimport ( + geometry_column_view, +) + + +cdef extern from "cuspatial/distance/polygon_distance.hpp" \ + namespace "cuspatial" nogil: + cdef unique_ptr[column] pairwise_polygon_distance( + const geometry_column_view & lhs, + const geometry_column_view & rhs + ) except + diff --git a/python/cuspatial/cuspatial/_lib/distance.pyx b/python/cuspatial/cuspatial/_lib/distance.pyx index f3d68717e..780bb2fb6 100644 --- a/python/cuspatial/cuspatial/_lib/distance.pyx +++ b/python/cuspatial/cuspatial/_lib/distance.pyx @@ -22,6 +22,9 @@ from cuspatial._lib.cpp.distance.point_linestring_distance cimport ( from cuspatial._lib.cpp.distance.point_polygon_distance cimport ( pairwise_point_polygon_distance as c_pairwise_point_polygon_distance, ) +from cuspatial._lib.cpp.distance.polygon_distance cimport ( + pairwise_polygon_distance as c_pairwise_polygon_distance, +) from cuspatial._lib.cpp.optional cimport optional from cuspatial._lib.cpp.types cimport collection_type_id, geometry_type_id from cuspatial._lib.types cimport collection_type_py_to_c @@ -142,3 +145,26 @@ def pairwise_point_polygon_distance( )) return Column.from_unique_ptr(move(c_result)) + + +def pairwise_polygon_distance(Column lhs, Column rhs): + cdef shared_ptr[geometry_column_view] c_lhs = \ + make_shared[geometry_column_view]( + lhs.view(), + collection_type_id.MULTI, + geometry_type_id.POLYGON) + + cdef shared_ptr[geometry_column_view] c_rhs = \ + make_shared[geometry_column_view]( + rhs.view(), + collection_type_id.MULTI, + geometry_type_id.POLYGON) + + cdef unique_ptr[column] c_result + + with nogil: + c_result = move(c_pairwise_polygon_distance( + c_lhs.get()[0], c_rhs.get()[0] + )) + + return Column.from_unique_ptr(move(c_result)) diff --git a/python/cuspatial/cuspatial/core/spatial/__init__.py b/python/cuspatial/cuspatial/core/spatial/__init__.py index ee07838f9..c7738b57b 100644 --- a/python/cuspatial/cuspatial/core/spatial/__init__.py +++ b/python/cuspatial/cuspatial/core/spatial/__init__.py @@ -8,6 +8,7 @@ pairwise_point_distance, pairwise_point_linestring_distance, pairwise_point_polygon_distance, + pairwise_polygon_distance, ) from .filtering import points_in_spatial_window from .indexing import quadtree_on_points @@ -30,6 +31,7 @@ "pairwise_point_polygon_distance", "pairwise_point_linestring_distance", "pairwise_point_linestring_nearest_points", + "pairwise_polygon_distance", "polygon_bounding_boxes", "linestring_bounding_boxes", "point_in_polygon", diff --git a/python/cuspatial/cuspatial/core/spatial/distance.py b/python/cuspatial/cuspatial/core/spatial/distance.py index 5d61e0564..2ba995692 100644 --- a/python/cuspatial/cuspatial/core/spatial/distance.py +++ b/python/cuspatial/cuspatial/core/spatial/distance.py @@ -11,6 +11,7 @@ pairwise_point_distance as cpp_pairwise_point_distance, pairwise_point_linestring_distance as c_pairwise_point_linestring_distance, pairwise_point_polygon_distance as c_pairwise_point_polygon_distance, + pairwise_polygon_distance as c_pairwise_polygon_distance, ) from cuspatial._lib.hausdorff import ( directed_hausdorff_distance as cpp_directed_hausdorff_distance, @@ -483,6 +484,63 @@ def pairwise_point_polygon_distance(points: GeoSeries, polygons: GeoSeries): ) +def pairwise_polygon_distance(polygons1: GeoSeries, polygons2: GeoSeries): + """Compute distance between pairs of (multi)polygons and (multi)polygons + The distance between a (multi)polygon and a (multi)polygon + is defined as the shortest distance between every edge of the + (multi)polygon pair. If the multipolygon and multipolygon intersects, + the distance is 0. + + This algorithm computes distance pairwise. The ith row in the result is + the distance between the ith (multi)polygon in `polygons1` and the ith + (multi)polygon in `polygons2`. + + Parameters + ---------- + polygons1 : GeoSeries + The (multi)polygons to compute the distance from. + polygons2 : GeoSeries + The (multi)polygons to compute the distance from. + Returns + ------- + distance : cudf.Series + + Notes + ----- + The input `GeoSeries` must contain a single type geometry. + For example, `polygons1` series cannot contain both points and + polygons. + + Examples + -------- + Compute distance between a point and a polygon: + """ + + if len(polygons1) != len(polygons2): + raise ValueError("Unmatched input geoseries length.") + + if len(polygons1) == 0: + return cudf.Series(dtype=polygons1.lines.xy.dtype) + + if not contains_only_polygons(polygons1): + raise ValueError("`polygons1` array must contain only polygons") + + if not contains_only_polygons(polygons2): + raise ValueError("`polygons2` array must contain only polygons") + + # Handle slicing and aligns in geoseries + polygon1_column = polygons1._column.polygons._column.take( + polygons1._column._meta.union_offsets._column + ) + polygon2_column = polygons2._column.polygons._column.take( + polygons2._column._meta.union_offsets._column + ) + + return Series._from_data( + {None: c_pairwise_polygon_distance(polygon1_column, polygon2_column)} + ) + + def _flatten_point_series( points: GeoSeries, ) -> Tuple[ diff --git a/python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_point_polygon_distance.py b/python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_point_polygon_distance.py index 199c41208..49fabec39 100644 --- a/python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_point_polygon_distance.py +++ b/python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_point_polygon_distance.py @@ -124,13 +124,6 @@ def test_point_polygon_geocities(naturalearth_cities, naturalearth_lowres): gpu_cities = cuspatial.from_geopandas(naturalearth_cities.geometry) gpu_countries = cuspatial.from_geopandas(naturalearth_lowres.geometry) - print( - len(naturalearth_lowres), - len(naturalearth_lowres[: len(naturalearth_cities)]), - len(gpu_countries), - len(gpu_countries[: len(naturalearth_cities)]), - ) - expect = naturalearth_cities.geometry[:N].distance( naturalearth_lowres.geometry[:N] ) diff --git a/python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_polygon_distance.py b/python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_polygon_distance.py new file mode 100644 index 000000000..12095bebd --- /dev/null +++ b/python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_polygon_distance.py @@ -0,0 +1,149 @@ +import geopandas as gpd +import pytest +from shapely.geometry import MultiPolygon, Polygon + +import cudf +from cudf.testing import assert_series_equal + +import cuspatial + + +def test_polygon_empty(): + lhs = cuspatial.GeoSeries.from_polygons_xy([], [0], [0], [0]) + rhs = cuspatial.GeoSeries.from_polygons_xy([], [0], [0], [0]) + + got = cuspatial.pairwise_polygon_distance(lhs, rhs) + + expect = cudf.Series([], dtype="f8") + + assert_series_equal(got, expect) + + +@pytest.mark.parametrize( + "polygons1", + [ + [Polygon([(10, 11), (11, 10), (11, 11), (10, 11)])], + [ + MultiPolygon( + [ + Polygon([(12, 10), (11, 10), (11, 11), (12, 10)]), + Polygon([(11, 10), (12, 10), (11, 11), (11, 10)]), + ] + ) + ], + ], +) +@pytest.mark.parametrize( + "polygons2", + [ + [Polygon([(0, 1), (1, 0), (-1, 0), (0, 1)])], + [ + MultiPolygon( + [ + Polygon([(-2, 0), (-1, 0), (-1, -1), (-2, 0)]), + Polygon([(1, 0), (2, 0), (1, -1), (1, 0)]), + ] + ) + ], + ], +) +def test_one_pair(polygons1, polygons2): + lhs = gpd.GeoSeries(polygons1) + rhs = gpd.GeoSeries(polygons2) + + dlhs = cuspatial.GeoSeries(polygons1) + drhs = cuspatial.GeoSeries(polygons2) + + expect = lhs.distance(rhs) + got = cuspatial.pairwise_polygon_distance(dlhs, drhs) + + assert_series_equal(got, cudf.Series(expect)) + + +@pytest.mark.parametrize( + "polygons1", + [ + [ + Polygon([(0, 1), (1, 0), (-1, 0), (0, 1)]), + Polygon([(-4, -4), (-4, -5), (-5, -5), (-5, -4), (-5, -5)]), + ], + [ + MultiPolygon( + [ + Polygon([(0, 1), (1, 0), (-1, 0), (0, 1)]), + Polygon([(0, 1), (1, 0), (0, -1), (-1, 0), (0, 1)]), + ] + ), + MultiPolygon( + [ + Polygon( + [(-4, -4), (-4, -5), (-5, -5), (-5, -4), (-5, -5)] + ), + Polygon([(-2, 0), (-2, -2), (0, -2), (0, 0), (-2, 0)]), + ] + ), + ], + ], +) +@pytest.mark.parametrize( + "polygons2", + [ + [ + Polygon([(0, 1), (1, 0), (-1, 0), (0, 1)]), + Polygon([(-4, -4), (-4, -5), (-5, -5), (-5, -4), (-5, -5)]), + ], + [ + MultiPolygon( + [ + Polygon([(0, 1), (1, 0), (-1, 0), (0, 1)]), + Polygon([(0, 1), (1, 0), (0, -1), (-1, 0), (0, 1)]), + ] + ), + MultiPolygon( + [ + Polygon( + [(-4, -4), (-4, -5), (-5, -5), (-5, -4), (-5, -5)] + ), + Polygon([(-2, 0), (-2, -2), (0, -2), (0, 0), (-2, 0)]), + ] + ), + ], + ], +) +def test_two_pair(polygons1, polygons2): + lhs = gpd.GeoSeries(polygons1) + rhs = gpd.GeoSeries(polygons2) + + dlhs = cuspatial.GeoSeries(polygons1) + drhs = cuspatial.GeoSeries(polygons2) + + expect = lhs.distance(rhs) + got = cuspatial.pairwise_polygon_distance(dlhs, drhs) + + assert_series_equal(got, cudf.Series(expect)) + + +def test_linestring_polygon_large(polygon_generator): + N = 100 + polygons1 = gpd.GeoSeries(polygon_generator(N, 20.0, 5.0)) + polygons2 = gpd.GeoSeries(polygon_generator(N, 10.0, 3.0)) + + dpolygons1 = cuspatial.from_geopandas(polygons1) + dpolygons2 = cuspatial.from_geopandas(polygons2) + + expect = polygons1.distance(polygons2) + got = cuspatial.pairwise_polygon_distance(dpolygons1, dpolygons2) + + assert_series_equal(got, cudf.Series(expect)) + + +def test_point_polygon_geoboundaries(naturalearth_lowres): + N = 50 + + lhs = naturalearth_lowres.geometry[:N].reset_index(drop=True) + rhs = naturalearth_lowres.geometry[N : 2 * N].reset_index(drop=True) + expect = lhs.distance(rhs) + got = cuspatial.pairwise_polygon_distance( + cuspatial.GeoSeries(lhs), cuspatial.GeoSeries(rhs) + ) + assert_series_equal(cudf.Series(expect), got) From 4ad93b1d30860492a6343825a21595fe3d551159 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 13 Apr 2023 13:10:11 -0700 Subject: [PATCH 51/54] style --- .../distance/test_pairwise_linestring_polygon_distance.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_linestring_polygon_distance.py b/python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_linestring_polygon_distance.py index 213149b3d..f092b6023 100644 --- a/python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_linestring_polygon_distance.py +++ b/python/cuspatial/cuspatial/tests/spatial/distance/test_pairwise_linestring_polygon_distance.py @@ -110,6 +110,8 @@ def test_linestring_polygon_large(linestring_generator, polygon_generator): dpolygons = cuspatial.from_geopandas(polygons) expect = linestrings.distance(polygons) - got = cuspatial.pairwise_linestring_polygon_distance(dlinestrings, dpolygons) + got = cuspatial.pairwise_linestring_polygon_distance( + dlinestrings, dpolygons + ) assert_series_equal(got, cudf.Series(expect)) From b17d7e8df8ec17eefac521d73ecb4b87899fe0dc Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 13 Apr 2023 14:42:14 -0700 Subject: [PATCH 52/54] code change and use better style --- .../cuspatial/core/spatial/distance.py | 52 +++++++++++-------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/python/cuspatial/cuspatial/core/spatial/distance.py b/python/cuspatial/cuspatial/core/spatial/distance.py index 89fdfc25c..2e73aed1f 100644 --- a/python/cuspatial/cuspatial/core/spatial/distance.py +++ b/python/cuspatial/cuspatial/core/spatial/distance.py @@ -486,13 +486,13 @@ def pairwise_linestring_polygon_distance( ): """Compute distance between pairs of (multi)linestrings and (multi)polygons - The distance between a (multi)point and a (multi)polygon - is defined as the shortest distance between every point in the - multipoint and every edge of the (multi)polygon. If the multipoint and - multipolygon intersects, the distance is 0. + The distance between a (multi)linestrings and a (multi)polygon + is defined as the shortest distance between every segment in the + multilinestring and every edge of the (multi)polygon. If the + multilinestring and multipolygon intersects, the distance is 0. This algorithm computes distance pairwise. The ith row in the result is - the distance between the ith (multi)point in `linestrings` and the ith + the distance between the ith (multi)linestring in `linestrings` and the ith (multi)polygon in `polygons`. Parameters @@ -514,23 +514,31 @@ def pairwise_linestring_polygon_distance( Examples -------- - Compute distance between a point and a polygon: - >>> from shapely.geometry import Point, LineString - >>> linestrings = cuspatial.GeoSeries([Point(0, 0)]) - >>> polygons = cuspatial.GeoSeries([Point(1, 1).buffer(0.5)]) - >>> cuspatial.pairwise_point_polygon_distance(linestrings, polygons) - 0 0.914214 + Compute distance between a linestring and a polygon: + >>> from shapely.geometry import LineString, Polygon + >>> lines = cuspatial.GeoSeries([ + ... LineString([(0, 0), (1, 1)])]) + >>> polys = cuspatial.GeoSeries([ + ... Polygon([(-1, -1), (-1, 0), (-2, 0), (-1, -1)]) + ... ]) + >>> cuspatial.pairwise_linestring_polygon_distance(lines, polys) + 0 1.0 dtype: float64 Compute distance between a multipoint and a multipolygon - - >>> from shapely.geometry import MultiLineString - >>> mlinestrings = cuspatial.GeoSeries([ - MultiLinestring([[(0, 0), (1, 1)], [(10, 10), (11, 11)]])]) - >>> mpolys = cuspatial.GeoSeries([ - ... MultiPoint([Point(2, 2), Point(1, 2)]).buffer(0.5)]) - >>> cuspatial.pairwise_point_polygon_distance(mlinestrings, mpolys) - 0 0.5 + >>> from shapely.geometry import MultiLineString, MultiPolygon + >>> lines = cuspatial.GeoSeries([ + ... MultiLineString([ + ... LineString([(0, 0), (1, 1)]), + ... LineString([(1, 1), (2, 2)])]) + ... ]) + >>> polys = cuspatial.GeoSeries([ + ... MultiPolygon([ + ... Polygon([(-1, -1), (-1, 0), (-2, 0), (-1, -1)]), + ... Polygon([(-2, 0), (-3, 0), (-3, -1), (-2, 0)])]) + ... ]) + >>> cuspatial.pairwise_linestring_polygon_distance(lines, polys) + 0 1.0 dtype: float64 """ @@ -541,7 +549,7 @@ def pairwise_linestring_polygon_distance( return cudf.Series(dtype=linestrings.lines.xy.dtype) if not contains_only_linestrings(linestrings): - raise ValueError("`points` array must contain only points") + raise ValueError("`linestrings` array must contain only linestrings") if not contains_only_polygons(polygons): raise ValueError("`polygon` array must contain only polygons") @@ -550,9 +558,7 @@ def pairwise_linestring_polygon_distance( linestrings_column = linestrings._column.lines._column.take( linestrings._column._meta.union_offsets._column ) - - polygon_column = polygons._column.polygons._column - polygon_column = polygon_column.take( + polygon_column = polygons._column.polygons._column.take( polygons._column._meta.union_offsets._column ) From 3d650491bdb4592f2c3c3710b39e822dde1685b3 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 13 Apr 2023 17:11:57 -0700 Subject: [PATCH 53/54] [skip-ci] initial --- .../core/dispatch/geocolumn_binop_dispatch.py | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 python/cuspatial/cuspatial/core/dispatch/geocolumn_binop_dispatch.py diff --git a/python/cuspatial/cuspatial/core/dispatch/geocolumn_binop_dispatch.py b/python/cuspatial/cuspatial/core/dispatch/geocolumn_binop_dispatch.py new file mode 100644 index 000000000..5e1c06df0 --- /dev/null +++ b/python/cuspatial/cuspatial/core/dispatch/geocolumn_binop_dispatch.py @@ -0,0 +1,118 @@ +import cupy as cp + +import cudf + +from cuspatial.core._column.geocolumn import GeoColumn +from cuspatial.core._column.geometa import Feature_Enum + + +class GeoColumnBinopDispatch: + """Dispatch binary operations of two geocolumns to subkernels and + reassemble data. + """ + + def __init__(self, lhs: GeoColumn, rhs: GeoColumn): + if len(lhs) != len(rhs): + raise ValueError("Input is of different length.") + + self.lhs = lhs + self.rhs = rhs + self.dispatch_dict = None + + def __call__(self): + if self.dispatch_dict is None: + raise NotImplementedError( + "Binary op dispatch base class cannot be used directly, use a " + "derived class instead." + ) + self._sort_geocolumn_by_type_pairs() + self._compute_gather_offsets() + self._gather_and_dispatch_computation() + self._assemble_results() + return self._reordered_result + + def _sort_geocolumn_by_type_pairs(self): + """Given two geocolumn lhs and rhs, sort the offset buffer using + the pair of types buffer column as keys. + + This result in a reordered view into child column where each subtype + is contiguous in the offset buffer. + """ + + df = cudf.DataFrame( + { + "lhs_types": self.lhs._meta.input_types, + "rhs_types": self.rhs._meta.input_types, + "lhs_offsets": self.lhs._meta.union_offsets, + "rhs_offsets": self.rhs._meta.union_offsets, + "order": cp.arange(len(self.lhs)), + } + ) + + self.df_sorted = df.sort_values(by=["lhs_types", "rhs_types"]) + + def _compute_gather_offsets(self): + """From the sorted types buffer and offset buffer, compute the + boundaries of each contiguous section. + """ + self.binary_type_to_offsets = {} + self.orders = [] + for lhs_type, rhs_type in self.dispatch_dict: + mask = ( + self.df_sorted["lhs_types"] + == lhs_type.value & self.df_sorted["rhs_types"] + == rhs_type.value + ) + masked_df = self.df_sorted["lhs_offsets", "rhs_offsets", "order"][ + mask + ] + self.binary_type_to_offsets[(lhs_type, rhs_type)] = ( + masked_df["lhs_offsets"], + masked_df["rhs_offsets"], + ) + self.types_to_order[(lhs_type, rhs_type)] = masked_df["order"] + + def _gather_and_dispatch_computation(self): + """For each type combination, gather from the child columns and + dispatch to child kernel. + """ + self.results = [] + for lhs_type, rhs_type in self.dispatch_dict: + op, reflect = self.dispatch_dict[(lhs_type, rhs_type)] + lhs_offsets, rhs_offsets = self.binary_type_to_offsets[ + (lhs_type, rhs_type) + ] + if op == "Impossible": + self.result[(lhs_type, rhs_type)] = cp.full( + (len(lhs_offsets)), float("nan"), "f8" + ) + else: + lhs = self._gather_by_type(self.lhs, lhs_type, lhs_offsets) + rhs = self._gather_by_type(self.rhs, rhs_type, rhs_offsets) + self.results.append( + op(lhs, rhs) if not reflect else op(rhs, lhs) + ) + + def _assemble_results(self): + """After computed all results from each subkernel, concatenate the + result and make sure that the order matches that of original input. + """ + if len(self.results) == 0: + self.reordered_results = cudf.Series() + + results_concat = cudf.concat(self.results) + orders_concat = cudf.concat(self.orders) + self.reordered_results = cudf.Series(cp.zeros(results_concat)) + self.reordered_results[orders_concat] = results_concat + + def _gather_by_type(self, column, typ, offsets): + if typ == Feature_Enum.POINT: + return column.points._column.take(offsets) + elif typ == Feature_Enum.MULTIPOINT: + return column.mpoints._column.take(offsets) + elif typ == Feature_Enum.LINESTRING: + return column.lines._column.take(offsets) + elif typ == Feature_Enum.POLYGON: + return column.polygons._column.take(offsets) + else: + raise ValueError(f"Unrecognized type enum: {typ}") From 769194c1e552bcce1de2d3dc77de198c8d50957b Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 14 Apr 2023 12:24:33 -0700 Subject: [PATCH 54/54] implementing dispatches and test --- .../cuspatial/core/binops/distance.py | 47 +++++++++++++++ .../core/dispatch/geocolumn_binop_dispatch.py | 14 ++--- python/cuspatial/cuspatial/core/geoseries.py | 27 +++++++++ .../tests/spatial/distance/test_distance.py | 57 +++++++++++++++++++ 4 files changed, 138 insertions(+), 7 deletions(-) create mode 100644 python/cuspatial/cuspatial/core/binops/distance.py create mode 100644 python/cuspatial/cuspatial/tests/spatial/distance/test_distance.py diff --git a/python/cuspatial/cuspatial/core/binops/distance.py b/python/cuspatial/cuspatial/core/binops/distance.py new file mode 100644 index 000000000..fcd868190 --- /dev/null +++ b/python/cuspatial/cuspatial/core/binops/distance.py @@ -0,0 +1,47 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. + +from itertools import product + +from cuspatial._lib.distance import ( + pairwise_point_distance , + pairwise_point_linestring_distance , + pairwise_point_polygon_distance , + pairwise_linestring_distance , + pairwise_linestring_polygon_distance , + pairwise_polygon_distance , +) + +from cuspatial.core._column.geometa import Feature_Enum as F +from cuspatial.core._column.geocolumn import GeoColumn +from cuspatial.core.dispatch.geocolumn_binop_dispatch import GeoColumnBinopDispatch + + +class DistanceDispatch(GeoColumnBinopDispatch): + def __init__(self, lhs: GeoColumn, rhs: GeoColumn): + super().__init__(lhs, rhs) + self.dispatch_dict = { + (F.POINT, F.POINT): (pairwise_point_distance, False), + (F.POINT, F.MULTIPOINT): (pairwise_point_distance, False), + (F.POINT, F.LINESTRING): (pairwise_point_linestring_distance, False), + (F.POINT, F.POLYGON): (pairwise_point_polygon_distance, False), + + (F.MULTIPOINT, F.POINT): (pairwise_point_distance, False), + (F.MULTIPOINT, F.MULTIPOINT): (pairwise_point_distance, False), + (F.MULTIPOINT, F.LINESTRING): (pairwise_point_linestring_distance, False), + (F.MULTIPOINT, F.POLYGON): (pairwise_point_polygon_distance, False), + + (F.LINESTRING, F.POINT): (pairwise_point_linestring_distance, True), + (F.LINESTRING, F.MULTIPOINT): (pairwise_point_linestring_distance, True), + (F.LINESTRING, F.LINESTRING): (pairwise_linestring_distance, False), + (F.LINESTRING, F.POLYGON): (pairwise_linestring_polygon_distance, False), + + (F.POLYGON, F.POINT): (pairwise_point_polygon_distance, True), + (F.POLYGON, F.MULTIPOINT): (pairwise_point_polygon_distance, True), + (F.POLYGON, F.LINESTRING): (pairwise_linestring_polygon_distance, True), + (F.POLYGON, F.POLYGON): (pairwise_polygon_distance, False), + } + + none = [F.NONE] + other = [F.POINT, F.MULTIPOINT, F.LINESTRING, F.POLYGON] + for ltype, rtype in [*product(none, other), *product(other, none)]: + self.dispatch_dict[(ltype, rtype)] = ("Impossible", None) diff --git a/python/cuspatial/cuspatial/core/dispatch/geocolumn_binop_dispatch.py b/python/cuspatial/cuspatial/core/dispatch/geocolumn_binop_dispatch.py index 5e1c06df0..aa18e0cf0 100644 --- a/python/cuspatial/cuspatial/core/dispatch/geocolumn_binop_dispatch.py +++ b/python/cuspatial/cuspatial/core/dispatch/geocolumn_binop_dispatch.py @@ -59,18 +59,18 @@ def _compute_gather_offsets(self): self.orders = [] for lhs_type, rhs_type in self.dispatch_dict: mask = ( - self.df_sorted["lhs_types"] - == lhs_type.value & self.df_sorted["rhs_types"] - == rhs_type.value + (self.df_sorted["lhs_types"] + == lhs_type.value) & (self.df_sorted["rhs_types"] + == rhs_type.value) ) - masked_df = self.df_sorted["lhs_offsets", "rhs_offsets", "order"][ + masked_df = self.df_sorted[["lhs_offsets", "rhs_offsets", "order"]][ mask ] self.binary_type_to_offsets[(lhs_type, rhs_type)] = ( - masked_df["lhs_offsets"], - masked_df["rhs_offsets"], + masked_df["lhs_offsets"]._column, + masked_df["rhs_offsets"]._column, ) - self.types_to_order[(lhs_type, rhs_type)] = masked_df["order"] + self.orders.append(masked_df["order"]) def _gather_and_dispatch_computation(self): """For each type combination, gather from the child columns and diff --git a/python/cuspatial/cuspatial/core/geoseries.py b/python/cuspatial/cuspatial/core/geoseries.py index 9ff85a7dc..50d37cc6b 100644 --- a/python/cuspatial/cuspatial/core/geoseries.py +++ b/python/cuspatial/cuspatial/core/geoseries.py @@ -42,6 +42,7 @@ contains_only_points, contains_only_polygons, ) +from cuspatial.core.binops.distance import DistanceDispatch T = TypeVar("T", bound="GeoSeries") @@ -1240,3 +1241,29 @@ def disjoint(self, other, align=True): align=align ) return predicate(self, other) + + + def distance(self, other, align=True): + """Returns a Series containing the distance to aligned other. + + Parameters + ---------- + Parameters + other: cuspatial.GeoSeries + The Geoseries (elementwise) or geometric object to find the + distance to. + + align: bool (default True) + If True, automatically aligns GeoSeries based on their indices. + If False, the order of elements is preserved. + + Returns + ------- + result : cudf.Series + A series of floating point values of the distance between the + corresponding pair + """ + if align: + lhs, rhs = self.align(other) + + return DistanceDispatch(lhs._column, rhs._column)() diff --git a/python/cuspatial/cuspatial/tests/spatial/distance/test_distance.py b/python/cuspatial/cuspatial/tests/spatial/distance/test_distance.py new file mode 100644 index 000000000..be5053320 --- /dev/null +++ b/python/cuspatial/cuspatial/tests/spatial/distance/test_distance.py @@ -0,0 +1,57 @@ +import pytest + +from shapely.geometry import * + +from cudf.testing import assert_series_equal + +import cuspatial + +@pytest.mark.parametrize( + "lobj", [ + Point(0, 0), + MultiPoint([ + Point(1, 1), + Point(0, 0) + ]), + LineString([(0, 0), (1, 1)]), + MultiLineString([ + LineString([(0, 0), (1, 1)]), + LineString([(2, 2), (3, 3)]), + ]), + Polygon([(0, 0), (1, 0), (0, 1), (0, 0)]), + MultiPolygon([ + Polygon([(0, 0), (1, 0), (0, 1), (0, 0)]), + Polygon([(2, 2), (2, 3), (3, 2), (2, 2)]) + ]) + ] +) +@pytest.mark.parametrize( + "robj", [ + Point(10, 10), + MultiPoint([ + Point(11, 11), + Point(10, 10) + ]), + LineString([(10, 10), (11, 11)]), + MultiLineString([ + LineString([(10, 10), (11, 11)]), + LineString([(12, 12), (13, 13)]), + ]), + Polygon([(10, 0), (11, 10), (10, 11), (10, 10)]), + MultiPolygon([ + Polygon([(10, 10), (11, 10), (10, 11), (10, 10)]), + Polygon([(12, 12), (12, 13), (13, 12), (12, 12)]) + ]) + ] +) +def test_one_pair_all_combos_no_nulls_test(lobj, robj): + lhs = cuspatial.GeoSeries([lobj]) + rhs = cuspatial.GeoSeries([robj]) + + hlhs = lhs.to_geopandas() + hrhs = rhs.to_geopandas() + + got = lhs.distance(rhs) + expect = hlhs.distance(hrhs) + + assert_series_equal(cudf.Series(expect), got)