Skip to content

Commit e992329

Browse files
authored
Pairwise Multipoint Equals Count function (#1022)
This contribution adds `pairwise_multipoint_equals_count` to the column and header-only APIs. `pairwise_multipoint_equals_count` counts the number of times that each point in the lhs occurs in the rhs. ``` auto result = pairwise_multipoint_equals_count( {{{0, 0}},{{1, 1, 2, 2}},{{0, 0}, {1, 1}, {2, 2}}}, { {{0, 0}, {1, 1}, {2, 2}} {{0, 0}, {1, 1}, {2, 2}} {{0, 0}, {1, 1}, {2, 2}} } ) result = {1, 2, 3} ``` Written while pairing with @isVoid. Authors: - H. Thomson Comer (https://github.com/thomcom) - Michael Wang (https://github.com/isVoid) Approvers: - Michael Wang (https://github.com/isVoid) - Mark Harris (https://github.com/harrism) URL: #1022
1 parent 86e8912 commit e992329

16 files changed

+897
-6
lines changed

cpp/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ add_library(cuspatial
122122
src/join/quadtree_point_in_polygon.cu
123123
src/join/quadtree_point_to_nearest_linestring.cu
124124
src/join/quadtree_bbox_filtering.cu
125+
src/spatial/pairwise_multipoint_equals_count.cu
125126
src/spatial/polygon_bounding_box.cu
126127
src/spatial/linestring_bounding_box.cu
127128
src/spatial/point_in_polygon.cu

cpp/include/cuspatial/detail/geometry_collection/multipoint_ref.cuh

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
#pragma once
1717
#include <cuspatial/cuda_utils.hpp>
18+
#include <cuspatial/detail/iterator.hpp>
1819

1920
#include <thrust/distance.h>
2021

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright (c) 2023, NVIDIA CORPORATION.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include <cuspatial/cuda_utils.hpp>
20+
#include <cuspatial/detail/utility/zero_data.cuh>
21+
#include <cuspatial/error.hpp>
22+
#include <cuspatial/geometry/vec_2d.hpp>
23+
#include <cuspatial/iterator_factory.cuh>
24+
#include <cuspatial/range/multipoint_range.cuh>
25+
#include <cuspatial/range/range.cuh>
26+
#include <cuspatial/traits.hpp>
27+
28+
#include <rmm/cuda_stream_view.hpp>
29+
#include <rmm/device_uvector.hpp>
30+
#include <rmm/exec_policy.hpp>
31+
32+
#include <thrust/binary_search.h>
33+
#include <thrust/sort.h>
34+
#include <thrust/transform.h>
35+
36+
#include <iterator>
37+
#include <type_traits>
38+
39+
namespace cuspatial {
40+
41+
namespace detail {
42+
43+
template <class MultiPointRangeA, class MultiPointRangeB, class OutputIt>
44+
void __global__ pairwise_multipoint_equals_count_kernel(MultiPointRangeA lhs,
45+
MultiPointRangeB rhs,
46+
OutputIt output)
47+
{
48+
using T = typename MultiPointRangeA::point_t::value_type;
49+
50+
for (auto idx = threadIdx.x + blockIdx.x * blockDim.x; idx < lhs.num_points();
51+
idx += gridDim.x * blockDim.x) {
52+
auto geometry_id = lhs.geometry_idx_from_point_idx(idx);
53+
vec_2d<T> lhs_point = lhs.point_begin()[idx];
54+
auto rhs_multipoint = rhs[geometry_id];
55+
56+
atomicAdd(
57+
&output[geometry_id],
58+
thrust::binary_search(thrust::seq, rhs_multipoint.begin(), rhs_multipoint.end(), lhs_point));
59+
}
60+
}
61+
62+
} // namespace detail
63+
64+
template <class MultiPointRangeA, class MultiPointRangeB, class OutputIt>
65+
OutputIt pairwise_multipoint_equals_count(MultiPointRangeA lhs,
66+
MultiPointRangeB rhs,
67+
OutputIt output,
68+
rmm::cuda_stream_view stream)
69+
{
70+
using T = typename MultiPointRangeA::point_t::value_type;
71+
using index_t = typename MultiPointRangeB::index_t;
72+
73+
static_assert(is_same_floating_point<T, typename MultiPointRangeB::point_t::value_type>(),
74+
"Origin and input must have the same base floating point type.");
75+
76+
CUSPATIAL_EXPECTS(lhs.size() == rhs.size(), "lhs and rhs inputs should have the same size.");
77+
78+
if (lhs.size() == 0) return output;
79+
80+
// Create a sorted copy of the rhs points.
81+
auto key_it = make_geometry_id_iterator<index_t>(rhs.offsets_begin(), rhs.offsets_end());
82+
83+
rmm::device_uvector<index_t> rhs_keys(rhs.num_points(), stream);
84+
rmm::device_uvector<vec_2d<T>> rhs_point_sorted(rhs.num_points(), stream);
85+
86+
thrust::copy(rmm::exec_policy(stream), key_it, key_it + rhs.num_points(), rhs_keys.begin());
87+
thrust::copy(
88+
rmm::exec_policy(stream), rhs.point_begin(), rhs.point_end(), rhs_point_sorted.begin());
89+
90+
auto rhs_with_keys =
91+
thrust::make_zip_iterator(thrust::make_tuple(rhs_keys.begin(), rhs_point_sorted.begin()));
92+
93+
thrust::sort(rmm::exec_policy(stream), rhs_with_keys, rhs_with_keys + rhs.num_points());
94+
95+
auto rhs_sorted = multipoint_range{
96+
rhs.offsets_begin(), rhs.offsets_end(), rhs_point_sorted.begin(), rhs_point_sorted.end()};
97+
98+
detail::zero_data_async(output, output + lhs.size(), stream);
99+
auto [tpb, n_blocks] = grid_1d(lhs.num_points());
100+
detail::pairwise_multipoint_equals_count_kernel<<<n_blocks, tpb, 0, stream.value()>>>(
101+
lhs, rhs_sorted, output);
102+
103+
CUSPATIAL_CHECK_CUDA(stream.value());
104+
105+
return output + lhs.size();
106+
}
107+
108+
} // namespace cuspatial

cpp/include/cuspatial/geometry_collection/multipoint_ref.cuh

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ namespace cuspatial {
2626
*/
2727
template <typename VecIterator>
2828
class multipoint_ref {
29+
public:
2930
using point_t = iterator_value_type<VecIterator>;
3031

31-
public:
3232
CUSPATIAL_HOST_DEVICE multipoint_ref(VecIterator begin, VecIterator end);
3333

3434
/// Return iterator to the starting point of the multipoint.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright (c) 2023, NVIDIA CORPORATION.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include <cuspatial/geometry/vec_2d.hpp>
20+
21+
#include <rmm/cuda_stream_view.hpp>
22+
23+
#include <iterator>
24+
25+
namespace cuspatial {
26+
27+
/**
28+
* @brief Count the number of equal points in multipoint pairs.
29+
*
30+
* Given two ranges of multipoints, this function counts points in the left-hand
31+
* multipoint that exist in the corresponding right-hand multipoint.
32+
*
33+
* @example
34+
*
35+
* lhs: { {0, 0} }
36+
* rhs: { {0, 0}, {1, 1}, {2, 2}, {3, 3} }
37+
* count: { 1 }
38+
39+
* lhs: { {0, 0}, {1, 1}, {2, 2}, {3, 3} }
40+
* rhs: { {0, 0} }
41+
* count: { 1 }
42+
43+
* lhs: { { {3, 3}, {3, 3}, {0, 0} }, { {0, 0}, {1, 1}, {2, 2} }, { {0, 0} } }
44+
* rhs: { { {0, 0}, {2, 2}, {1, 1} }, { {2, 2}, {0, 0}, {1, 1} }, { {1, 1} } }
45+
* count: { 1, 3, 0 }
46+
*
47+
* @note All input iterators must conform to the specification defined by
48+
* `multipoint_range.cuh` and the output iterator must be able to accept for
49+
* storage values of type
50+
* `uint32_t`.
51+
*
52+
* @param[in] lhs_first multipoint_range of first array of multipoints
53+
* @param[in] rhs_first multipoint_range of second array of multipoints
54+
* @param[out] count_first: beginning of range of uint32_t counts
55+
* @param[in] stream: The CUDA stream on which to perform computations and allocate memory.
56+
* @tparam MultiPointRangeA The multipolygon range to compare point equality from
57+
* @tparam MultiPointRangeB The multipolygon range to compare point equality to
58+
* @tparam OutputIt Iterator over uint32_t. Must meet the requirements of
59+
* [LegacyRandomAccessIterator][LinkLRAI] and be device-accessible and mutable.
60+
*
61+
* @return Output iterator to the element past the last count result written.
62+
*
63+
* [LinkLRAI]: https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator
64+
* "LegacyRandomAccessIterator"
65+
*/
66+
template <class MultiPointRangeA, class MultiPointRangeB, class OutputIt>
67+
OutputIt pairwise_multipoint_equals_count(MultiPointRangeA lhs_first,
68+
MultiPointRangeB rhs_first,
69+
OutputIt count_first,
70+
rmm::cuda_stream_view stream = rmm::cuda_stream_default);
71+
72+
} // namespace cuspatial
73+
74+
#include <cuspatial/detail/pairwise_multipoint_equals_count.cuh>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright (c) 2023, NVIDIA CORPORATION.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include <cudf/types.hpp>
20+
21+
#include <cuspatial/column/geometry_column_view.hpp>
22+
23+
#include <rmm/mr/device/per_device_resource.hpp>
24+
25+
#include <memory>
26+
27+
namespace cuspatial {
28+
29+
/**
30+
* @addtogroup spatial
31+
* @brief Count the number of equal points in pairs of multipoints..
32+
*
33+
* Given two columns of multipoints, returns a column containing the
34+
* count of points in each multipoint from `lhs` that exist in the
35+
* corresponding multipoint in `rhs`.
36+
*
37+
* @param lhs Geometry column of multipoints with interleaved coordinates
38+
* @param rhs Geometry column of multipoints with interleaved coordinates
39+
* @param mr Device memory resource used to allocate the returned column.
40+
* @return A column of size len(lhs) containing the number of points in each
41+
* multipoint from `lhs` that are equal to a point in the corresponding
42+
* multipoint in `rhs`.
43+
*
44+
* @throw cuspatial::logic_error if `lhs` and `rhs` have different coordinate
45+
* types or lengths.
46+
*
47+
* @example
48+
* ```
49+
* lhs: MultiPoint(0, 0)
50+
* rhs: MultiPoint((0, 0), (1, 1), (2, 2), (3, 3))
51+
* result: 1
52+
53+
* lhs: MultiPoint((0, 0), (1, 1), (2, 2), (3, 3))
54+
* rhs: MultiPoint((0, 0))
55+
* result: 1
56+
57+
* lhs: (
58+
* MultiPoint((3, 3), (3, 3), (0, 0)),
59+
* MultiPoint((0, 0), (1, 1), (2, 2)),
60+
* MultiPoint((0, 0))
61+
* )
62+
* rhs: (
63+
* MultiPoint((0, 0), (2, 2), (1, 1)),
64+
* MultiPoint((2, 2), (0, 0), (1, 1)),
65+
* MultiPoint((1, 1))
66+
* )
67+
* result: ( 1, 3, 0 )
68+
*/
69+
std::unique_ptr<cudf::column> pairwise_multipoint_equals_count(
70+
geometry_column_view const& lhs,
71+
geometry_column_view const& rhs,
72+
rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource());
73+
74+
} // namespace cuspatial

cpp/include/cuspatial_test/base_fixture.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
* limitations under the License.
1515
*/
1616

17+
#pragma once
18+
1719
#include <rmm/cuda_stream_view.hpp>
1820
#include <rmm/mr/device/per_device_resource.hpp>
1921

0 commit comments

Comments
 (0)