From 0f2c62cf101fa5f40398c46009e9e79203f5883b Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 31 Mar 2022 15:16:33 -0700 Subject: [PATCH 01/67] Initial pass of linestring distance and test --- cpp/CMakeLists.txt | 1 + .../distances/linestring_distance.hpp | 54 ++++ cpp/src/spatial/linestring_distance.cu | 289 ++++++++++++++++++ cpp/tests/CMakeLists.txt | 3 + .../spatial/linestring_distance_test.cpp | 66 ++++ 5 files changed, 413 insertions(+) create mode 100644 cpp/include/cuspatial/distances/linestring_distance.hpp create mode 100644 cpp/src/spatial/linestring_distance.cu create mode 100644 cpp/tests/spatial/linestring_distance_test.cpp diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index d5a0a026f..ea9167a8d 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -131,6 +131,7 @@ add_library(cuspatial SHARED src/spatial_window/spatial_window.cu src/spatial/haversine.cu src/spatial/hausdorff.cu + src/spatial/linestring_distance.cu src/spatial/lonlat_to_cartesian.cu src/trajectory/derive_trajectories.cu src/trajectory/trajectory_bounding_boxes.cu diff --git a/cpp/include/cuspatial/distances/linestring_distance.hpp b/cpp/include/cuspatial/distances/linestring_distance.hpp new file mode 100644 index 000000000..bbbcd606e --- /dev/null +++ b/cpp/include/cuspatial/distances/linestring_distance.hpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2022, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include + +namespace cuspatial { + +/** + * @brief Compute distance between pairs of linestrings + * + * @param linestring1_offsets Indices to the start coordinate to the first linestring of the pair + * @param linestring1_points_x x component for points consisting linestrings 1 + * @param linestring1_points_y y component for points consisting linestrings 1 + * @param linestring2_offsets Indices to the start coordinate to the second linestring of the pair + * @param linestring2_points_x x component for points consisting linestrings 2 + * @param linestring2_points_y y component for points consisting linestrings 2 + * @param mr Device memory resource used to allocate the returned column's device memory + * @return A column of shortest distances between the pair of linestrings + * + * @throw cuspatial::logic_error if `linestring1_offsets.size() != linestring2_offsets.size()` + * @throw cuspatial::logic_error if size mismatch between the x, y components of the linestring + * points. + * @throw cuspatial::logic_error if any of the point arrays have mismatch types. + * @throw cuspatial::logic_error if any linestring has less than 2 end points. + * + */ +std::unique_ptr pairwise_linestring_distance( + cudf::device_span linestring1_offsets, + cudf::column_view const& linestring1_points_x, + cudf::column_view const& linestring1_points_y, + cudf::device_span linestring2_offsets, + cudf::column_view const& linestring2_points_x, + cudf::column_view const& linestring2_points_y, + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); + +} // namespace cuspatial diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu new file mode 100644 index 000000000..afa972034 --- /dev/null +++ b/cpp/src/spatial/linestring_distance.cu @@ -0,0 +1,289 @@ + + +/* + * Copyright (c) 2022, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +namespace cuspatial { +namespace { + +template +double __device__ point_to_segment_distance(coord_2d const& P, + coord_2d const& A, + coord_2d const& B) +{ + // Subject 1.02 of https://www.inf.pucrs.br/~pinho/CG/faq.html + // Project the point to the segment, if it lands on the segment, + // the distance is the length of proejction, otherwise it's the + // length to one of the end points. + + double L_squared = (A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y); + if (L_squared == 0) { return hypot(P.x - A.x, P.y - A.y); } + double r = ((P.x - A.x) * (B.x - A.x) + (P.y - A.y) * (B.y - A.y)) / L_squared; + if (r <= 0 or r >= 1) { + return std::min(hypot(P.x - A.x, P.y - A.y), hypot(P.x - B.x, P.y - A.y)); + } + double s = ((A.y - P.y) * (B.x - A.x) - (A.x - P.x) * (B.y - A.y)) / L_squared; + return fabs(s) * sqrt(L_squared); +} + +template +double __device__ segment_distance_no_intersect(coord_2d const& A, + coord_2d const& B, + coord_2d const& C, + coord_2d const& D) +{ + return std::min(std::min(point_to_segment_distance(A, C, D), point_to_segment_distance(B, C, D)), + std::min(point_to_segment_distance(C, A, B), point_to_segment_distance(D, A, B))); +} + +/** + * @brief Computes shortest distance between two segments. + * + * If two segment intersects, distance is 0. + */ +template +double __device__ segment_distance(coord_2d const& A, + coord_2d const& B, + coord_2d const& C, + coord_2d const& D) +{ + // Subject 1.03 of https://www.inf.pucrs.br/~pinho/CG/faq.html + // Construct a parametrized ray of AB and CD, solve for the parameters. + // If both parameters are within [0, 1], the intersection exists. + + double r_denom = (B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x); + if (r_denom == 0) { return 0.0; } // parallel or conincide + double r = ((A.y - C.y) * (D.x - C.x) - (A.x - C.x) * (D.y - C.y)) / r_denom; + double s = ((A.y - C.y) * (B.x - A.x) - (A.x - C.x) * (B.y - A.x)) / + ((B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x)); + if (r >= 0 and r <= 1 and s >= 0 and s <= 1) { return 0.0; } + return segment_distance_no_intersect(A, B, C, D); +} + +template +struct linestirngs_pairs_min_distance_functor { + using T = typename std::iterator_traits::value_type; + + cudf::size_type num_linestrings; + cudf::size_type linestring1_num_points; + cudf::size_type linestring2_num_points; + OffsetIterator linestring1_offsets; + CoordinateIterator linestring1_points_xs; + CoordinateIterator linestring1_points_ys; + OffsetIterator linestring2_offsets; + CoordinateIterator linestring2_points_xs; + CoordinateIterator linestring2_points_ys; + + linestirngs_pairs_min_distance_functor(cudf::size_type num_linestrings, + cudf::size_type linestring1_num_points, + cudf::size_type linestring2_num_points, + OffsetIterator linestring1_offsets, + CoordinateIterator linestring1_points_xs, + CoordinateIterator linestring1_points_ys, + OffsetIterator linestring2_offsets, + CoordinateIterator linestring2_points_xs, + CoordinateIterator linestring2_points_ys) + : num_linestrings(num_linestrings), + linestring1_num_points(linestring1_num_points), + linestring2_num_points(linestring2_num_points), + linestring1_offsets(linestring1_offsets), + linestring1_points_xs(linestring1_points_xs), + linestring1_points_ys(linestring1_points_ys), + linestring2_offsets(linestring2_offsets), + linestring2_points_xs(linestring2_points_xs), + linestring2_points_ys(linestring2_points_ys) + { + } + + T __device__ operator()(cudf::size_type idx) + { + auto const l1pts_start = linestring1_offsets[idx]; + auto const l1pts_end = + idx == (num_linestrings - 1) ? linestring1_num_points : linestring1_offsets[idx + 1]; + auto const l2pts_start = linestring2_offsets[idx]; + auto const l2pts_end = + idx == (num_linestrings - 1) ? linestring2_num_points : linestring2_offsets[idx + 1]; + printf("idx: %d\n", idx); + printf("num_points_ls1: %d\n", linestring1_num_points); + printf("num_points_ls2: %d\n", linestring2_num_points); + printf("l1pts: %d, %d\n", l1pts_start, l1pts_end); + printf("l2pts: %d, %d\n", l2pts_start, l2pts_end); + double min_distance = std::numeric_limits::max(); + for (cudf::size_type i = l1pts_start; i < l1pts_end - 1; i++) { + for (cudf::size_type j = l2pts_start; j < l2pts_end - 1; j++) { + coord_2d A{linestring1_points_xs[i], linestring1_points_ys[i]}; + coord_2d B{linestring1_points_xs[i + 1], linestring1_points_ys[i + 1]}; + coord_2d C{linestring2_points_xs[j], linestring2_points_ys[j]}; + coord_2d D{linestring2_points_xs[j + 1], linestring2_points_ys[j + 1]}; + min_distance = std::min(segment_distance(A, B, C, D), min_distance); + + printf("%d %d, %f\n", i, j, min_distance); + } + } + return min_distance; + } +}; + +} // anonymous namespace + +namespace detail { +struct pariwise_linestring_distance_functor { + template + std::enable_if_t::value, std::unique_ptr> operator()( + cudf::device_span linestring1_offsets, + cudf::column_view const& linestring1_points_x, + cudf::column_view const& linestring1_points_y, + cudf::device_span linestring2_offsets, + cudf::column_view const& linestring2_points_x, + cudf::column_view const& linestring2_points_y, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) + { + using namespace cudf; + + auto const num_strings = static_cast(linestring1_offsets.size()); + + auto min_distances = + make_numeric_column(data_type{type_to_id()}, num_strings, mask_state::UNALLOCATED); + + auto functor = linestirngs_pairs_min_distance_functor(num_strings, + linestring1_points_x.size(), + linestring2_points_x.size(), + linestring1_offsets.begin(), + linestring1_points_x.begin(), + linestring1_points_y.begin(), + linestring2_offsets.begin(), + linestring2_points_x.begin(), + linestring2_points_y.begin()); + + std::cout << "number of strings: " << num_strings << std::endl; + thrust::transform(rmm::exec_policy(stream), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_strings), + min_distances->mutable_view().begin(), + functor); + return min_distances; + } + + template + std::enable_if_t::value, std::unique_ptr> operator()( + Args&&...) + { + CUSPATIAL_FAIL("Linestring distances only supports floating point coordinates."); + } +}; + +/** + * @brief Check if every linestring in the input contains at least 1 segment. + */ +bool validate_linestring(cudf::device_span linestring_offsets, + cudf::column_view const& linestring_points_x, + rmm::cuda_stream_view stream) +{ + if (linestring_offsets.size() == 1) { return linestring_points_x.size() >= 2; } + auto linestring_validate_iter = thrust::make_transform_iterator( + thrust::make_zip_iterator(linestring_offsets.begin(), linestring_offsets.begin() + 1), + [] __device__(thrust::tuple indices) { + return (indices.get<1>() - indices.get<0>()) >= 2; + }); + return thrust::reduce(rmm::exec_policy(stream), + linestring_validate_iter, + linestring_validate_iter + linestring_offsets.size() - 1, + true, + thrust::logical_and()); +} + +std::unique_ptr pairwise_linestring_distance( + cudf::device_span linestring1_offsets, + cudf::column_view const& linestring1_points_x, + cudf::column_view const& linestring1_points_y, + cudf::device_span linestring2_offsets, + cudf::column_view const& linestring2_points_x, + cudf::column_view const& linestring2_points_y, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) +{ + CUSPATIAL_EXPECTS(linestring1_offsets.size() == linestring2_offsets.size(), + "Mismatch number of linestrings in the linestring pair array."); + + CUSPATIAL_EXPECTS(linestring1_points_x.size() == linestring1_points_y.size() and + linestring2_points_x.size() == linestring2_points_y.size(), + "The lengths of linestring coordinates arrays mismatch."); + + CUSPATIAL_EXPECTS(linestring1_points_x.type() == linestring1_points_y.type() and + linestring2_points_x.type() == linestring2_points_y.type() and + linestring1_points_x.type() == linestring2_points_x.type(), + "The types of linestring coordinates arrays mismatch."); + + if (linestring1_offsets.size() == 0) { return cudf::empty_like(linestring1_points_x); } + + CUSPATIAL_EXPECTS(validate_linestring(linestring1_offsets, linestring1_points_x, stream), + "Each item of linestring1 should contain at least 2 end points."); + CUSPATIAL_EXPECTS(validate_linestring(linestring2_offsets, linestring2_points_x, stream), + "Each item of linestring2 should contain at least 2 end points."); + + return cudf::type_dispatcher(linestring1_points_x.type(), + pariwise_linestring_distance_functor{}, + linestring1_offsets, + linestring1_points_x, + linestring1_points_y, + linestring2_offsets, + linestring2_points_x, + linestring2_points_y, + stream, + mr); +} + +} // namespace detail + +std::unique_ptr pairwise_linestring_distance( + cudf::device_span linestring1_offsets, + cudf::column_view const& linestring1_points_x, + cudf::column_view const& linestring1_points_y, + cudf::device_span linestring2_offsets, + cudf::column_view const& linestring2_points_x, + cudf::column_view const& linestring2_points_y, + rmm::mr::device_memory_resource* mr) +{ + return detail::pairwise_linestring_distance(linestring1_offsets, + linestring1_points_x, + linestring1_points_y, + linestring2_offsets, + linestring2_points_x, + linestring2_points_y, + rmm::cuda_stream_default, + mr); +} + +} // namespace cuspatial diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 1b6605e2b..5a325237f 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -77,6 +77,9 @@ ConfigureTest(POLYLINE_BBOX_TEST ConfigureTest(POLYGON_BBOX_TEST spatial/polygon_bbox_test.cu) +ConfigureTest(LINESTRING_DISTANCE_TEST + spatial/linestring_distance_test.cpp) + ConfigureTest(SHAPEFILE_READER_TEST io/shp/polygon_shapefile_reader_test.cpp) diff --git a/cpp/tests/spatial/linestring_distance_test.cpp b/cpp/tests/spatial/linestring_distance_test.cpp new file mode 100644 index 000000000..d923770cd --- /dev/null +++ b/cpp/tests/spatial/linestring_distance_test.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2022, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include +#include + +namespace cuspatial { +namespace test { + +using namespace cudf; +using namespace cudf::test; + +template +using wrapper = fixed_width_column_wrapper; + +template +struct PairwiseLinestringDistanceTest : public BaseFixture { +}; + +// float and double are logically the same but would require separate tests due to precision. +using TestTypes = FloatingPointTypes; +TYPED_TEST_CASE(PairwiseLinestringDistanceTest, TestTypes); + +constexpr cudf::test::debug_output_level verbosity{cudf::test::debug_output_level::ALL_ERRORS}; + +TYPED_TEST(PairwiseLinestringDistanceTest, OnePairLineString) +{ + using T = TypeParam; + wrapper linestring1_offsets{0}; + wrapper linestring1_points_x{0.0, 1.0}; + wrapper linestring1_points_y{0.0, 1.0}; + wrapper linestring2_offsets{0}; + wrapper linestring2_points_x{1.0, 2.0}; + wrapper linestring2_points_y{0.0, 0.0}; + + wrapper expected{0.7071067811865476}; + + auto got = pairwise_linestring_distance(column_view(linestring1_offsets), + linestring1_points_x, + linestring1_points_y, + column_view(linestring2_offsets), + linestring2_points_x, + linestring2_points_y); + expect_columns_equivalent(expected, *got, verbosity); +} + +} // namespace test +} // namespace cuspatial From 29f1d62d5d2b1c29384d1d27a1716478561275dc Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 31 Mar 2022 16:43:54 -0700 Subject: [PATCH 02/67] Add more one pair linestring tests --- cpp/src/spatial/linestring_distance.cu | 18 +++- .../spatial/linestring_distance_test.cpp | 98 ++++++++++++++++++- 2 files changed, 111 insertions(+), 5 deletions(-) diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index afa972034..c549619e7 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -34,6 +34,10 @@ #include #include +#ifndef DEBUG +#define DEBUG 1 +#endif + namespace cuspatial { namespace { @@ -83,8 +87,13 @@ double __device__ segment_distance(coord_2d const& A, // If both parameters are within [0, 1], the intersection exists. double r_denom = (B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x); - if (r_denom == 0) { return 0.0; } // parallel or conincide - double r = ((A.y - C.y) * (D.x - C.x) - (A.x - C.x) * (D.y - C.y)) / r_denom; + double r_numer = (A.y - C.y) * (D.x - C.x) - (A.x - C.x) * (D.y - C.y); + if (r_denom == 0) { + if (r_numer == 0) { return 0.0; } // Segments coincides + // Segments parallel + return segment_distance_no_intersect(A, B, C, D); + } + double r = r_numer / r_denom; double s = ((A.y - C.y) * (B.x - A.x) - (A.x - C.x) * (B.y - A.x)) / ((B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x)); if (r >= 0 and r <= 1 and s >= 0 and s <= 1) { return 0.0; } @@ -134,11 +143,13 @@ struct linestirngs_pairs_min_distance_functor { auto const l2pts_start = linestring2_offsets[idx]; auto const l2pts_end = idx == (num_linestrings - 1) ? linestring2_num_points : linestring2_offsets[idx + 1]; +#ifdef DEBUG printf("idx: %d\n", idx); printf("num_points_ls1: %d\n", linestring1_num_points); printf("num_points_ls2: %d\n", linestring2_num_points); printf("l1pts: %d, %d\n", l1pts_start, l1pts_end); printf("l2pts: %d, %d\n", l2pts_start, l2pts_end); +#endif double min_distance = std::numeric_limits::max(); for (cudf::size_type i = l1pts_start; i < l1pts_end - 1; i++) { for (cudf::size_type j = l2pts_start; j < l2pts_end - 1; j++) { @@ -147,8 +158,9 @@ struct linestirngs_pairs_min_distance_functor { coord_2d C{linestring2_points_xs[j], linestring2_points_ys[j]}; coord_2d D{linestring2_points_xs[j + 1], linestring2_points_ys[j + 1]}; min_distance = std::min(segment_distance(A, B, C, D), min_distance); - +#ifdef DEBUG printf("%d %d, %f\n", i, j, min_distance); +#endif } } return min_distance; diff --git a/cpp/tests/spatial/linestring_distance_test.cpp b/cpp/tests/spatial/linestring_distance_test.cpp index d923770cd..bbc16b225 100644 --- a/cpp/tests/spatial/linestring_distance_test.cpp +++ b/cpp/tests/spatial/linestring_distance_test.cpp @@ -41,15 +41,17 @@ TYPED_TEST_CASE(PairwiseLinestringDistanceTest, TestTypes); constexpr cudf::test::debug_output_level verbosity{cudf::test::debug_output_level::ALL_ERRORS}; -TYPED_TEST(PairwiseLinestringDistanceTest, OnePairLineString) +TYPED_TEST(PairwiseLinestringDistanceTest, OnePairLinestringParallel) { using T = TypeParam; + // Linestring 1: (0.0, 0.0), (1.0, 1.0) + // Linestring 2: (1.0, 0.0), (2.0, 1.0) wrapper linestring1_offsets{0}; wrapper linestring1_points_x{0.0, 1.0}; wrapper linestring1_points_y{0.0, 1.0}; wrapper linestring2_offsets{0}; wrapper linestring2_points_x{1.0, 2.0}; - wrapper linestring2_points_y{0.0, 0.0}; + wrapper linestring2_points_y{0.0, 1.0}; wrapper expected{0.7071067811865476}; @@ -62,5 +64,97 @@ TYPED_TEST(PairwiseLinestringDistanceTest, OnePairLineString) expect_columns_equivalent(expected, *got, verbosity); } +TYPED_TEST(PairwiseLinestringDistanceTest, OnePairLinestringEndpointsDistance) +{ + using T = TypeParam; + // Linestring 1: (0.0, 0.0), (1.0, 1.0), (2.0, 2.0) + // Linestring 2: (2.0, 0.0), (1.0, -1.0), (0.0, -1.0) + wrapper linestring1_offsets{0}; + wrapper linestring1_points_x{0.0, 1.0, 2.0}; + wrapper linestring1_points_y{0.0, 1.0, 2.0}; + wrapper linestring2_offsets{0}; + wrapper linestring2_points_x{2.0, 1.0, 0.0}; + wrapper linestring2_points_y{0.0, -1.0, -1.0}; + + wrapper expected{1.0}; + + auto got = pairwise_linestring_distance(column_view(linestring1_offsets), + linestring1_points_x, + linestring1_points_y, + column_view(linestring2_offsets), + linestring2_points_x, + linestring2_points_y); + expect_columns_equivalent(expected, *got, verbosity); +} + +TYPED_TEST(PairwiseLinestringDistanceTest, OnePairLinestringIntersects) +{ + using T = TypeParam; + // Linestring 1: (0.0, 0.0), (1.0, 1.0) + // Linestring 2: (0.0, 1.0), (1.0, 0.0) + wrapper linestring1_offsets{0}; + wrapper linestring1_points_x{0.0, 1.0}; + wrapper linestring1_points_y{0.0, 1.0}; + wrapper linestring2_offsets{0}; + wrapper linestring2_points_x{0.0, 1.0}; + wrapper linestring2_points_y{1.0, 0.0}; + + wrapper expected{0.0}; + + auto got = pairwise_linestring_distance(column_view(linestring1_offsets), + linestring1_points_x, + linestring1_points_y, + column_view(linestring2_offsets), + linestring2_points_x, + linestring2_points_y); + expect_columns_equivalent(expected, *got, verbosity); +} + +TYPED_TEST(PairwiseLinestringDistanceTest, OnePairLinestringSharedVertex) +{ + using T = TypeParam; + // Linestring 1: (0.0, 0.0), (0.0, 2.0), (2.0, 2.0) + // Linestring 2: (2.0, 2.0), (2.0, 1.0), (1.0, 1.0), (2.5, 0.0) + wrapper linestring1_offsets{0}; + wrapper linestring1_points_x{0.0, 0.0, 2.0}; + wrapper linestring1_points_y{0.0, 2.0, 2.0}; + wrapper linestring2_offsets{0}; + wrapper linestring2_points_x{2.0, 2.0, 1.0, 2.5}; + wrapper linestring2_points_y{2.0, 1.0, 1.0, 0.0}; + + wrapper expected{0.0}; + + auto got = pairwise_linestring_distance(column_view(linestring1_offsets), + linestring1_points_x, + linestring1_points_y, + column_view(linestring2_offsets), + linestring2_points_x, + linestring2_points_y); + expect_columns_equivalent(expected, *got, verbosity); +} + +TYPED_TEST(PairwiseLinestringDistanceTest, OnePairLinestringCoincide) +{ + using T = TypeParam; + // Linestring 1: (0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0) + // Linestring 2: (2.0, 1.0), (1.0, 1.0), (1.0, 0.0), (2.0, 0.0), (2.0, 0.5) + wrapper linestring1_offsets{0}; + wrapper linestring1_points_x{0.0, 1.0, 1.0, 0.0}; + wrapper linestring1_points_y{0.0, 0.0, 1.0, 1.0}; + wrapper linestring2_offsets{0}; + wrapper linestring2_points_x{2.0, 1.0, 1.0, 2.0, 2.0}; + wrapper linestring2_points_y{1.0, 1.0, 0.0, 0.0, 0.5}; + + wrapper expected{0.0}; + + auto got = pairwise_linestring_distance(column_view(linestring1_offsets), + linestring1_points_x, + linestring1_points_y, + column_view(linestring2_offsets), + linestring2_points_x, + linestring2_points_y); + expect_columns_equivalent(expected, *got, verbosity); +} + } // namespace test } // namespace cuspatial From 954bbfccafced54417242a1f4d95002f55d446db Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 1 Apr 2022 11:40:32 -0700 Subject: [PATCH 03/67] More single pair testing --- .../spatial/linestring_distance_test.cpp | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/cpp/tests/spatial/linestring_distance_test.cpp b/cpp/tests/spatial/linestring_distance_test.cpp index bbc16b225..56afcd258 100644 --- a/cpp/tests/spatial/linestring_distance_test.cpp +++ b/cpp/tests/spatial/linestring_distance_test.cpp @@ -87,6 +87,52 @@ TYPED_TEST(PairwiseLinestringDistanceTest, OnePairLinestringEndpointsDistance) expect_columns_equivalent(expected, *got, verbosity); } +TYPED_TEST(PairwiseLinestringDistanceTest, OnePairLinestringProjectionNotOnLine) +{ + using T = TypeParam; + // Linestring 1: (0.0, 0.0), (1.0, 1.0) + // Linestring 2: (3.0, 1.5), (3.0, 2.0) + wrapper linestring1_offsets{0}; + wrapper linestring1_points_x{0.0, 1.0}; + wrapper linestring1_points_y{0.0, 1.0}; + wrapper linestring2_offsets{0}; + wrapper linestring2_points_x{3.0, 3.0}; + wrapper linestring2_points_y{1.5, 2.0}; + + wrapper expected{2.0615528128088303}; + + auto got = pairwise_linestring_distance(column_view(linestring1_offsets), + linestring1_points_x, + linestring1_points_y, + column_view(linestring2_offsets), + linestring2_points_x, + linestring2_points_y); + expect_columns_equivalent(expected, *got, verbosity); +} + +TYPED_TEST(PairwiseLinestringDistanceTest, OnePairLinestringPerpendicular) +{ + using T = TypeParam; + // Linestring 1: (0.0, 0.0), (2.0, 0.0) + // Linestring 2: (1.0, 1.0), (1.0, 2.0) + wrapper linestring1_offsets{0}; + wrapper linestring1_points_x{0.0, 2.0}; + wrapper linestring1_points_y{0.0, 0.0}; + wrapper linestring2_offsets{0}; + wrapper linestring2_points_x{1.0, 1.0}; + wrapper linestring2_points_y{1.0, 2.0}; + + wrapper expected{1.0}; + + auto got = pairwise_linestring_distance(column_view(linestring1_offsets), + linestring1_points_x, + linestring1_points_y, + column_view(linestring2_offsets), + linestring2_points_x, + linestring2_points_y); + expect_columns_equivalent(expected, *got, verbosity); +} + TYPED_TEST(PairwiseLinestringDistanceTest, OnePairLinestringIntersects) { using T = TypeParam; From 03748eaadc130d8315af1765887084d143159f1e Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 1 Apr 2022 11:44:56 -0700 Subject: [PATCH 04/67] update docstring --- cpp/src/spatial/linestring_distance.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index c549619e7..2b934cc5e 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -217,7 +217,7 @@ struct pariwise_linestring_distance_functor { }; /** - * @brief Check if every linestring in the input contains at least 1 segment. + * @brief Check if every linestring in the input contains at least 2 end points. */ bool validate_linestring(cudf::device_span linestring_offsets, cudf::column_view const& linestring_points_x, From 3b52203a185315dfd3787bd27686c3ea33b13c7b Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 6 Apr 2022 19:20:47 -0700 Subject: [PATCH 05/67] Add medium test --- cpp/src/spatial/linestring_distance.cu | 88 +- cpp/tests/CMakeLists.txt | 2 +- .../spatial/linestring_distance_test.cpp | 68 ++ .../linestring_distance_test_medium.cpp | 1056 +++++++++++++++++ 4 files changed, 1198 insertions(+), 16 deletions(-) create mode 100644 cpp/tests/spatial/linestring_distance_test_medium.cpp diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index 2b934cc5e..8f35333b1 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -36,13 +37,26 @@ #ifndef DEBUG #define DEBUG 1 + +template +void __device__ print(cuspatial::coord_2d const& point) +{ + printf("POINT (%f, %f)\n", point.x, point.y); +} + +template +void __device__ print(cuspatial::coord_2d const& A, cuspatial::coord_2d const& B) +{ + printf("SEGMENT (%f, %f) -> (%f, %f)\n", A.x, A.y, B.x, B.y); +} + #endif namespace cuspatial { namespace { template -double __device__ point_to_segment_distance(coord_2d const& P, +double __device__ point_to_segment_distance(coord_2d const& C, coord_2d const& A, coord_2d const& B) { @@ -52,13 +66,15 @@ double __device__ point_to_segment_distance(coord_2d const& P, // length to one of the end points. double L_squared = (A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y); - if (L_squared == 0) { return hypot(P.x - A.x, P.y - A.y); } - double r = ((P.x - A.x) * (B.x - A.x) + (P.y - A.y) * (B.y - A.y)) / L_squared; + if (L_squared == 0) { return hypot(C.x - A.x, C.y - A.y); } + double r = ((C.x - A.x) * (B.x - A.x) + (C.y - A.y) * (B.y - A.y)) / L_squared; + printf("\t r=%f\n", r); if (r <= 0 or r >= 1) { - return std::min(hypot(P.x - A.x, P.y - A.y), hypot(P.x - B.x, P.y - A.y)); + return std::min(hypot(C.x - A.x, C.y - A.y), hypot(C.x - B.x, C.y - B.y)); } - double s = ((A.y - P.y) * (B.x - A.x) - (A.x - P.x) * (B.y - A.y)) / L_squared; - return fabs(s) * sqrt(L_squared); + double Px = A.x + r * (B.x - A.x); + double Py = A.y + r * (B.y - A.y); + return hypot(C.x - Px, C.y - Py); } template @@ -67,6 +83,29 @@ double __device__ segment_distance_no_intersect(coord_2d const& A, coord_2d const& C, coord_2d const& D) { + printf("From: \n"); + print(A); + printf("To: \n"); + print(C, D); + printf("Distance %f\n", point_to_segment_distance(A, C, D)); + + printf("From: \n"); + print(B); + printf("To: \n"); + print(C, D); + printf("Distance %f\n", point_to_segment_distance(B, C, D)); + + printf("From: \n"); + print(C); + printf("To: \n"); + print(A, B); + printf("Distance %f\n", point_to_segment_distance(C, A, B)); + + printf("From: \n"); + print(D); + printf("To: \n"); + print(A, B); + printf("Distance %f\n", point_to_segment_distance(D, A, B)); return std::min(std::min(point_to_segment_distance(A, C, D), point_to_segment_distance(B, C, D)), std::min(point_to_segment_distance(C, A, B), point_to_segment_distance(D, A, B))); } @@ -224,16 +263,35 @@ bool validate_linestring(cudf::device_span linestring_off rmm::cuda_stream_view stream) { if (linestring_offsets.size() == 1) { return linestring_points_x.size() >= 2; } - auto linestring_validate_iter = thrust::make_transform_iterator( - thrust::make_zip_iterator(linestring_offsets.begin(), linestring_offsets.begin() + 1), - [] __device__(thrust::tuple indices) { - return (indices.get<1>() - indices.get<0>()) >= 2; + // auto linestring_validate_iter = cudf::detail::make_counting_transform_iterator( + // 0, + // [offsets = linestring_offsets.begin(), + // num_points = static_cast(linestring_points_x.size()), + // num_offsets = + // static_cast(linestring_offsets.size())] __device__(cudf::size_type idx) { + // cudf::size_type begin = offsets[idx]; + // cudf::size_type end = idx == num_offsets ? num_points : offsets[idx + 1]; + // return (end - begin) >= 2; + // }); + // return thrust::reduce(rmm::exec_policy(stream), + // linestring_validate_iter, + // linestring_validate_iter + linestring_offsets.size(), + // true, + // [](auto i, auto j) { return i && j; }); + + return thrust::reduce( + rmm::exec_policy(stream), + thrust::make_counting_iterator(cudf::size_type{0}), + thrust::make_counting_iterator(static_cast(linestring_offsets.size())), + true, + [offsets = linestring_offsets.begin(), + num_offsets = static_cast(linestring_offsets.size()), + num_points = static_cast( + linestring_points_x.size())] __device__(bool prev, cudf::size_type i) { + cudf::size_type begin = offsets[i]; + cudf::size_type end = i == num_offsets ? num_points : offsets[i + 1]; + return prev && (end - begin); }); - return thrust::reduce(rmm::exec_policy(stream), - linestring_validate_iter, - linestring_validate_iter + linestring_offsets.size() - 1, - true, - thrust::logical_and()); } std::unique_ptr pairwise_linestring_distance( diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 5a325237f..3040430b7 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -78,7 +78,7 @@ ConfigureTest(POLYGON_BBOX_TEST spatial/polygon_bbox_test.cu) ConfigureTest(LINESTRING_DISTANCE_TEST - spatial/linestring_distance_test.cpp) + spatial/linestring_distance_test.cpp spatial/linestring_distance_test_medium.cpp) ConfigureTest(SHAPEFILE_READER_TEST io/shp/polygon_shapefile_reader_test.cpp) diff --git a/cpp/tests/spatial/linestring_distance_test.cpp b/cpp/tests/spatial/linestring_distance_test.cpp index 56afcd258..480a1c805 100644 --- a/cpp/tests/spatial/linestring_distance_test.cpp +++ b/cpp/tests/spatial/linestring_distance_test.cpp @@ -202,5 +202,73 @@ TYPED_TEST(PairwiseLinestringDistanceTest, OnePairLinestringCoincide) expect_columns_equivalent(expected, *got, verbosity); } +TYPED_TEST(PairwiseLinestringDistanceTest, OnePairRandom1) +{ + using T = TypeParam; + wrapper linestring1_offsets{0}; + wrapper linestring1_points_x{-22556.235212018168, -16375.655690574613, -20082.724633593425}; + wrapper linestring1_points_y{41094.0501840996, 42992.319790050366, 33759.13529113619}; + wrapper linestring2_offsets{0}; + wrapper linestring2_points_x{4365.496374409238, 1671.0269165650761}; + wrapper linestring2_points_y{-59857.47177852941, -54931.9723439855}; + + wrapper expected{91319.97744223749}; + + auto got = pairwise_linestring_distance(column_view(linestring1_offsets), + linestring1_points_x, + linestring1_points_y, + column_view(linestring2_offsets), + linestring2_points_x, + linestring2_points_y); + expect_columns_equivalent(expected, *got, verbosity); +} + +TYPED_TEST(PairwiseLinestringDistanceTest, TwoPairs) +{ + using T = TypeParam; + wrapper linestring1_offsets{0, 4}; + wrapper linestring1_points_x{ + 41658.902315589876, + 46600.70359801489, + 47079.510547637154, + 51498.48049880379, + -27429.917796286478, + -21764.269974046114, + -14460.71813363161, + -18226.13032712476, + }; + wrapper linestring1_points_y{14694.11814724456, + 8771.431887804214, + 10199.68027155776, + 17049.62665643919, + -33240.8339287343, + -37974.45515744517, + -31333.481529957502, + -30181.03842467982}; + wrapper linestring2_offsets{0, 2}; + wrapper linestring2_points_x{ + 24046.170375947084, + 20614.007047185743, + 48381.39607717942, + 53346.77764665915, + }; + wrapper linestring2_points_y{ + 27878.56737867571, + 26489.74880629428, + -8366.313156569413, + -2066.3869793077383, + }; + + wrapper expected{22000.86425379464, 66907.56415814416}; + + auto got = pairwise_linestring_distance(column_view(linestring1_offsets), + linestring1_points_x, + linestring1_points_y, + column_view(linestring2_offsets), + linestring2_points_x, + linestring2_points_y); + expect_columns_equivalent(expected, *got, verbosity); +} + } // namespace test } // namespace cuspatial diff --git a/cpp/tests/spatial/linestring_distance_test_medium.cpp b/cpp/tests/spatial/linestring_distance_test_medium.cpp new file mode 100644 index 000000000..d99433f1d --- /dev/null +++ b/cpp/tests/spatial/linestring_distance_test_medium.cpp @@ -0,0 +1,1056 @@ +/* + * Copyright (c) 2022, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include +#include + +namespace cuspatial { +namespace test { + +using namespace cudf; +using namespace cudf::test; + +template +using wrapper = fixed_width_column_wrapper; + +template +struct PairwiseLinestringDistanceTest : public BaseFixture { +}; + +// float and double are logically the same but would require separate tests due to precision. +using TestTypes = FloatingPointTypes; +TYPED_TEST_CASE(PairwiseLinestringDistanceTest, TestTypes); + +constexpr cudf::test::debug_output_level verbosity{cudf::test::debug_output_level::ALL_ERRORS}; + +TYPED_TEST(PairwiseLinestringDistanceTest, RandomDataset100) +{ + using T = TypeParam; + wrapper linestring1_offsets{ + 0, 4, 8, 11, 13, 16, 19, 22, 25, 28, 32, 36, 40, 44, 48, 51, 54, + 57, 61, 63, 65, 67, 71, 74, 78, 82, 85, 87, 89, 92, 96, 99, 101, 104, + 108, 112, 115, 119, 123, 127, 130, 132, 135, 137, 141, 145, 147, 150, 153, 155, 158, + 160, 164, 167, 170, 173, 176, 179, 182, 184, 188, 192, 194, 198, 202, 206, 209, 212, + 215, 217, 220, 222, 226, 228, 230, 233, 237, 239, 243, 247, 250, 252, 254, 257, 259, + 263, 266, 269, 273, 276, 280, 283, 286, 290, 294, 298, 300, 302, 306, 308}; + wrapper linestring1_points_x{ + 41658.902315589876, 46600.70359801489, 47079.510547637154, 51498.48049880379, + -27429.917796286478, -21764.269974046114, -14460.71813363161, -18226.13032712476, + -22556.235212018168, -16375.655690574613, -20082.724633593425, 26809.34874258138, + 26656.809525288947, 37540.565197328295, 36984.75276605113, 41981.578395349155, + 24973.02375952172, 19250.902968701903, 20165.256326220195, -30532.18325215106, + -32135.262234862337, -22826.652288925623, -17439.962727300197, -19359.179053658157, + -20575.92273561565, 9500.159032106982, 6568.682357949907, 4981.961021893829, + -21889.03381442112, -23236.68404277722, -20541.236096645694, -20778.38844417717, + -57875.32424881095, -51612.57611847265, -47437.091112564274, -50197.81791907972, + -56989.35149478826, -50735.07273259827, -53459.1335853405, -47317.19439336836, + -6829.050709717398, -2732.682737868703, -5966.9880402461695, 385.7819993385874, + -36083.360810904385, -41081.45575117335, -35733.010967268594, -36247.474479077006, + -46951.78590147328, -47262.641809761204, -47684.95897589372, 38981.49756281363, + 35164.86749845841, 35579.262138939965, 59176.59704717483, 58533.33560306961, + 58725.469004504834, 54054.39482381586, 48926.88038791325, 51732.8876978247, + 53489.896558495035, -44876.184453934664, -47637.45499043422, -58724.07892996805, + -54674.126544740386, 60635.37331725002, 62105.58031557918, -5514.764938002292, + -6644.854901584, -10776.990254158663, -10887.375444705547, -16308.6192426665, + -17943.665926700574, -18590.60450856736, -54635.44517293027, -54223.234081035494, + -55991.88318145398, -55476.50649203067, -16828.926382989048, -20874.261277556023, + -21556.654292334166, -21941.300770415983, 19602.183464230096, 20124.423199257864, + 22857.521915468602, -40252.218652315016, -40615.87902328277, -13227.295520406573, + -13394.780584665317, -41859.97648749311, -41313.45210522428, -38298.22583610271, + 19267.410152329292, 11889.896156569695, 13200.16827668008, 19386.646807861616, + -60021.453586243275, -56555.20891834299, -58162.220568779645, -21564.27114522246, + -26413.185516939815, -8121.46414404954, -2680.751998537391, -2222.8256113393886, + -43496.28457602764, -48670.608599013554, -51456.69654638918, -52025.6273644588, + -49836.99743938117, -46727.06695784684, -37385.58991765429, -37794.42212261821, + 36860.87308416238, 42873.67992713083, 39313.39701688439, 11431.213542784448, + 7647.058473635509, 11107.49195048625, 11643.093760750395, -45871.68320713149, + -45986.98076235849, -46085.965532968825, -46181.67123182361, 25363.379368344074, + 17717.231571577973, 24580.31394401792, 29134.693387320112, 43080.7529227066, + 42441.16001053327, 47976.84240392968, -11900.910297907809, -13187.917157702086, + -9792.284371446513, -11269.14276649128, -5660.728920526542, 11253.80330805089, + 10466.359767099839, -53252.1312259309, -47799.02030720808, -51379.57629207311, + -54427.60141284903, -42057.65623242821, -42664.076601666646, -45804.81458842004, + -45478.89283457703, 47075.997568824576, 53844.0068681668, -22445.721811471412, + -20797.59708432936, -22012.560374600253, -25647.96389715412, -24850.718783507502, + -23637.972304660132, 34882.04958914149, 26678.65752711494, -45859.8782408588, + -51933.96309224814, -52099.29330373669, 34549.11186911959, 28645.239864180556, + 29729.362290067365, 27092.22625228292, 27270.331741995822, 27375.014737974416, + 51556.57410969483, 52515.470545460434, 46638.54343817407, 11226.929260803649, + 12712.496694281563, 8563.557289261611, -45375.67258113653, -44857.41642126795, + -51428.472725871936, 11948.653896420248, 20671.682144587314, 19203.379064891342, + 54438.42614275502, 57867.96483019572, 54260.19148082872, -43454.69314619985, + -49657.47783440756, -48847.36106727273, -12962.581910456334, -11078.09318399874, + 33050.658148590956, 33853.16958578488, 30155.859199122297, 28350.944868052207, + -19670.72089334298, -19640.1421625446, -15653.90150936818, -5820.125752277603, + 60491.9147275399, 61673.61389764642, 3010.22884329714, 7247.472877158092, + 4851.4337295382575, 4824.133938941079, 23504.266167053705, 22480.863815579512, + 24342.637759549954, 24137.50967310437, -34053.41026040829, -32110.577393759428, + -33924.15109989719, -31672.687599811205, 959.7215557354066, 312.56653151161856, + -44.98033879071096, 296.376589694366, 3994.3950419367184, 7696.985767894743, + -6387.18605725986, -8051.394792809615, -10110.413864642014, -19847.050217164695, + -14080.09841584025, -22849.163678809855, -28963.47174786276, -31847.193110264172, + 7105.9171509791195, 11460.775360755784, 58942.469515078075, 58886.86547401938, + 58740.01428115593, 61283.97291509698, -63061.08699755168, -65536.0, + -50035.37045591002, -50081.11003362366, 62116.35229456691, 61865.80519887989, + 62647.124391193276, 56654.5141685885, 63070.85134584749, 61230.41706849119, + 60415.518333547, 55656.31580105885, 46134.25777282618, 968.041379812712, + 5360.019466310152, 3662.5289196361346, 5459.910428930167, 53419.848320399906, + 54341.74882672624, 56149.34611529181, 63518.943955564784, 63041.55390377034, + 55648.26343059204, 56045.45020144504, -14313.208636322757, -14485.732740432964, + -13268.3777837889, -13231.945672727452, -5885.21758056189, -5131.159534487515, + -9074.90834159211, -43855.71514428074, -48053.80969115072, 40831.07071986032, + 43149.63435633618, 47679.105915171785, 48400.939321371276, -9650.464759378563, + -7249.958103471297, -5082.082168062301, -63939.909793905674, -63781.97553502955, + -65311.39108408277, -5848.2556516926925, 317.32328031093675, -812.6531797336165, + -3364.6381580844895, -52564.38671359332, -58093.41581969953, -59668.83618647987, + 14353.60218457671, 13841.579397954565, 9137.396816561348, 8610.296312929777, + -7055.315970087351, -549.2613965357859, 2300.4279681102403, 55435.82976249409, + 55192.80876167619, 50532.24048212488, -49374.35589090375, -47508.15225702733, + -50682.16982805728, -50920.08172662605, 45260.20728762291, 48517.03233951705, + 46124.822155460206, 54828.216215604654, 44771.72815631704, 40575.91154447274, + 40875.303023973545, 45065.28856600735, 15683.489154164097, 23392.984508151996, + 31539.806796075267, 31791.109097684202, -23000.113733128106, -18094.479708501316, + -11357.201200332272, -9345.838925153008, -55479.287132797836, -52287.397277835524, + 10818.487974743519, 9714.158308423997, 3850.7591574230764}; + + wrapper linestring1_points_y{14694.11814724456, + 8771.431887804214, + 10199.68027155776, + 17049.62665643919, + -33240.8339287343, + -37974.45515744517, + -31333.481529957502, + -30181.03842467982, + 41094.0501840996, + 42992.319790050366, + 33759.13529113619, + -44038.61029577575, + -43753.338844714024, + 15638.266868017672, + 17679.849722737585, + 13344.429419617965, + 58735.45872458056, + 57585.91906288884, + 59003.13030366512, + -37453.38802730886, + -36689.9824564741, + -33888.1663960553, + 57339.487213868444, + 51513.83286101961, + 46633.76851694451, + 9451.145716270374, + 6486.956110744754, + 4957.81628227334, + 29939.875040449275, + 29478.765053663003, + 29043.078772533325, + 35716.735488752434, + -59612.15306180189, + -59294.38873712498, + -59127.907287882874, + -55907.11718469079, + -12026.40853005452, + -15241.391847088236, + -10462.08756300849, + -12371.368617841707, + 37044.87138428305, + 33021.25289675412, + 34610.915204761666, + 40011.84905629743, + 25902.54295777333, + 25768.144614748064, + 20564.043901966274, + 22714.109069724822, + 12875.546088919305, + 13893.756714962012, + 13490.506594342487, + 14727.611347924598, + 10689.244931054465, + 17538.521291377492, + 23982.713915777626, + 24390.498281955253, + 24762.865678427796, + -45064.46593058627, + -37966.64921236699, + -46059.58460159569, + -49120.96511713206, + -44360.42961138337, + -45008.80023662591, + 2094.812598127086, + 2085.895936089219, + 2573.1373108541593, + 1104.7361987014294, + 3308.886287290079, + 698.8137866817374, + 5393.567671690889, + 5707.221420960841, + 55013.396940812236, + 55544.470228068916, + 57224.00315655217, + 34758.586091369114, + 31679.59372669221, + 33674.127549160745, + 33712.78136073219, + -31333.200170105112, + -29605.973372869845, + -35481.31451239508, + -35017.537957551656, + 63130.3997634869, + 56201.98002258745, + 59459.34956278938, + 27897.179108036376, + 34867.09647977925, + 49218.47853186974, + 41205.40159441225, + 34123.82321041233, + 29333.279117753587, + 29621.64904140896, + 50324.3631913836, + 49593.72552280869, + 49929.12541850021, + 55527.05140347529, + 55909.86001855173, + 47223.27157870629, + 46241.01648214377, + -43556.091011791155, + -37643.13228249952, + -56220.36407542113, + -62305.120350499754, + -61076.48504318068, + -17972.088889636892, + -21976.442708667066, + -19176.50505543119, + -18904.22729632036, + -19263.800263562145, + -12538.24458831298, + -9827.528489899312, + -10414.915634760691, + 23911.34358298873, + 23596.14312044066, + 17829.62627312683, + 43915.88050116565, + 50879.02714367026, + 49993.15637259206, + 52166.07547584346, + 8607.802738756523, + 2245.0909069522613, + 3368.0297663885935, + 3239.560649157949, + -10327.329631677007, + -14227.688657963068, + -20651.84812237891, + -20154.540583292346, + -53708.2135814389, + -53731.92238527414, + -46216.28781449849, + 29882.832150688133, + 31025.252493533244, + 26838.581081892436, + 27429.718875382947, + 24574.667692997973, + -25008.651718108995, + -25641.114108769514, + -20391.068111358632, + -22136.879760045675, + -22500.143694694954, + -22433.79562519107, + -21445.145056313435, + -12631.148780933434, + -5175.118656235392, + -4974.014748854219, + 27174.33433031141, + 26125.565202999907, + 5947.414031173117, + 4231.314002562816, + 5514.803686442698, + -10242.011092887318, + -5594.456938161081, + -8240.143243956536, + 29690.35080922088, + 28639.788268322274, + -40730.76985862218, + -45679.557409096116, + -45875.37994370673, + 4411.4930198940565, + 254.8442404411453, + 51400.29090932576, + 57412.291246702465, + 61044.43950242366, + 60938.90864558535, + -6739.644077840145, + -8987.498835679566, + -5237.371348479321, + 48448.32078503046, + 40259.71525300039, + 36673.04699761284, + 36335.05760810802, + 36616.26156028952, + 35645.440993282224, + -24706.94842684488, + -21833.197922278516, + -20118.5370559781, + -29144.161044259497, + -28841.39017934311, + -33361.50604871394, + 50273.6558612583, + 53577.41459418893, + 62172.47019181349, + 56440.56024064429, + 51644.12024048194, + -58920.77248277155, + -58722.31903774217, + -61546.90999392407, + -61763.752766007725, + 22684.517798921064, + 22687.978622624778, + 17615.43390847237, + 17014.434363396143, + -44454.646506475365, + -40670.4296948947, + 9528.274005783183, + 15124.702546599536, + 11243.865962023567, + 11227.68978808053, + 33179.85253067702, + 28906.810162650283, + 28007.697772657713, + 27353.98686292031, + -64265.88574718186, + -63130.20151200131, + -65536.0, + -64581.179304206045, + 24452.397751976227, + 23231.56579123238, + 23394.965100601486, + -6558.590930642116, + -3700.7274032742203, + -3191.0326641728143, + 19774.367993523556, + 27694.462346775483, + 29492.85073406865, + -14028.154415987468, + -18353.14544453502, + -36813.45891756001, + -36692.297960659314, + -35588.66043701308, + -39033.80716796027, + -40282.86309943496, + 6787.262920580673, + 5863.573743095374, + 222.51042578771012, + 3999.5819537617244, + -2575.8629341373526, + -9285.81901768746, + 20537.513037427605, + 20464.263706784554, + 45745.60016019519, + 40883.516880131516, + 40468.27973749191, + 16862.303937885663, + 23300.195766204517, + 19510.84090013759, + 11527.62347437564, + 30108.422086075327, + 32185.644483843284, + -30593.64787082193, + -31328.665492243064, + -23533.58815929447, + -18274.379953033775, + -64165.83874253259, + -65536.0, + -65536.0, + -65536.0, + -52961.48936175517, + -54114.30757859672, + -51233.966551424906, + 54248.41327512142, + 55940.25897158489, + 49763.46359174742, + 44703.96058500886, + 52829.13296404347, + 52609.69351861534, + 43460.75312509922, + 9158.508068670606, + 9979.665281410318, + 11222.362498340124, + 13070.612773966097, + 14130.01460673544, + 14714.801284683483, + -57391.84915603173, + -58226.95924387508, + -58745.47381533096, + 15155.582389704374, + 15037.11344237593, + 15343.059647357972, + -31139.67098988675, + -36811.17803062407, + -36621.07177150081, + -34954.03004912029, + -3600.7938606276584, + -4872.609255570671, + -5828.29321364815, + -5228.363965939694, + -4650.89050636443, + 1036.6629270363055, + 6139.780821150885, + 46568.89864530167, + 40189.53608848613, + 49413.6690830616, + 28866.760874538086, + 27631.122043076637, + 31651.373637217555, + 51743.92736162846, + 50894.0772057776, + 54730.606260802306, + 53139.583948321655, + 4527.564954490968, + 12189.838122257905, + 10814.215470535108, + 14090.173961864257, + -38155.22830457595, + -40869.613927092214, + -38550.35052437827, + -39694.9360220942, + 43498.596550403585, + 49508.0115361974, + 17528.07416632102, + 18452.718659632777, + 5502.854897138503, + -1477.9959109331921, + 984.4075834326218, + 1497.5246961748412, + 16890.74546327388, + 11323.02179734331, + 7195.558456095547, + 5524.004040319631, + 12912.134627293335}; + wrapper linestring2_offsets{ + 0, 2, 4, 6, 8, 10, 13, 15, 17, 20, 23, 25, 29, 33, 35, 37, 41, + 45, 47, 51, 53, 56, 59, 63, 66, 69, 71, 73, 76, 79, 82, 84, 86, 89, + 93, 96, 98, 100, 103, 105, 107, 109, 113, 116, 119, 123, 126, 129, 133, 137, 139, + 143, 147, 149, 151, 154, 156, 159, 163, 166, 168, 170, 174, 178, 181, 183, 187, 191, + 195, 197, 200, 203, 205, 209, 213, 216, 220, 224, 226, 229, 233, 236, 239, 241, 245, + 248, 250, 252, 254, 256, 258, 260, 262, 264, 267, 269, 271, 275, 279, 281}; + wrapper linestring2_points_x{24046.170375947084, + 20614.007047185743, + 48381.39607717942, + 53346.77764665915, + 4365.496374409238, + 1671.0269165650761, + 60135.10576713836, + 58117.0215375236, + -51494.78094358275, + -59291.16312346822, + 17527.070262173787, + 16140.128075758206, + 21178.29035078248, + -46247.99797412497, + -41770.15815787812, + 52439.4128584852, + 50908.38296602024, + 64834.271974236326, + 61946.67345113476, + 62022.52832061858, + 34288.18665219756, + 28300.340620382718, + 27656.378043110635, + 30794.000810035635, + 32431.166581550326, + 33756.666022670746, + 29664.709989066603, + 28856.873418914183, + 36401.141148038514, + 17079.944290857253, + 13233.118633426348, + 18010.65672629887, + 21733.55299266447, + -17589.12911703354, + -13615.42999872126, + -16962.899142052956, + -14652.153991738582, + 21114.93723313787, + 12910.922082281972, + 3453.1479188972626, + 9769.675794028683, + 1835.0514383492991, + 7756.834125617577, + 7640.602544577206, + 13341.58730161298, + -28356.501919974988, + -28174.220475611586, + 46825.329124044074, + 44931.59733150601, + 38981.41926874932, + 47795.98186123269, + -11635.295606788517, + -9497.235815058624, + 62099.48615805716, + 65535.0, + 65535.0, + -21737.429366590455, + -29042.47786349059, + -31559.31294702657, + 50336.084698268794, + 54641.49898855221, + 54523.92417903646, + 59810.65384878393, + 26109.768995279854, + 33260.44533620665, + 32718.50253715998, + -17046.478062460723, + -13637.902612846676, + -4048.4940472536837, + -1295.089691202047, + -7280.143953298165, + 23307.510964401692, + 21057.78812228635, + 61422.584748784095, + 64125.26236976497, + 63035.70068773205, + 13293.405087485866, + 8301.321717837485, + 5998.566973034347, + 56807.6229253363, + 57284.20136469141, + 50134.09712451558, + 62815.690002302246, + 65535.0, + 18587.476388271883, + 12698.56647818971, + -11373.280290722301, + -8161.778140116089, + -8351.822845788342, + 47561.334575670524, + 48323.03876944549, + 47791.130474422665, + 51987.019421589604, + -19455.18463768006, + -23739.805394183597, + -22568.658462124997, + 23024.079178510918, + 28644.583218064923, + 7298.377778265756, + 3625.7721728530732, + -21594.37878249857, + -22515.531339327168, + -20627.42673352121, + 6099.222612896439, + 8638.445704296702, + 32011.353490737194, + 33208.75937007324, + 926.164443453119, + 5399.2313731423565, + 28186.645319993637, + 28427.65541715838, + 28433.54857881469, + 28309.21054975733, + 49744.365575383126, + 41951.7110218391, + 40709.44234829844, + 59428.37014680178, + 60774.58995358354, + 57864.97505832654, + -12909.579379351642, + -14169.062767062256, + -9472.376817936885, + -8594.555656534549, + 43445.5342784347, + 44146.51386658679, + 44254.395924099306, + 56695.86940969313, + 57328.3459955809, + 59869.09421652202, + -48476.01256692005, + -40572.476190618385, + -41372.611441455665, + -38102.79223698782, + -34160.875071055634, + -31851.944660451976, + -33560.36758089234, + -38956.59298786371, + 47875.729875354155, + 47054.15007723312, + -15763.28154735428, + -15022.415565328583, + -15505.259361620036, + -19433.918706189022, + 21039.330729276393, + 25459.011149795857, + 24228.2200937306, + 24172.896324114823, + 37641.61613795927, + 40093.808079273986, + -64986.04229495289, + -65536.0, + 28865.9861058433, + 31404.205373670076, + 32824.92139989007, + 44076.96662980942, + 42816.334617183755, + -2337.065372176352, + -2454.820165021252, + -21.24489065693888, + 25862.23076924747, + 19792.249382429145, + 19725.81703503653, + 13486.339170409647, + -14132.32043340936, + -13264.825037065333, + -19408.894071066446, + 61758.285121524066, + 61694.84484358259, + 61864.79059036865, + 65535.0, + -6015.210967517021, + -625.9021770409745, + -1591.306712085287, + 3500.835626990062, + 52952.65614611846, + 47855.49296434603, + 45493.81677973004, + 43215.72762748768, + -62101.353403045774, + -61218.466272761565, + -61234.41962391241, + 21279.822206688157, + 28494.696527078828, + 23243.054165080714, + 22547.3541411588, + 15981.433995230766, + 25826.998040730832, + -57172.87410962715, + -54766.5759124097, + -52573.1116877078, + -49403.2785052861, + -7186.542641911306, + -6831.412373191683, + -5756.376457916045, + 3071.2106591016236, + -40420.610893629855, + -48596.10517985245, + 31670.60357638674, + 28210.377929556533, + 23561.64629790607, + -7393.259429801445, + -7262.980859354975, + -14498.642533421951, + -34278.03300122521, + -27990.859726387403, + -35069.67232490826, + -37033.13416444959, + -37012.08647477349, + -40242.36130845048, + 32002.517735259913, + 30237.44459095604, + 31104.766970006913, + 25197.350474127827, + -41345.515497687134, + -42036.75176539078, + -37844.484580749326, + -63374.289355131164, + -63381.35611551266, + -62896.82969782667, + -65536.0, + 19624.504208243205, + 26889.54650881527, + 27539.646814152253, + 26048.399472263092, + 56254.770257432174, + 56748.84925441912, + 9885.400008618206, + 11165.624242290589, + 14734.845074957582, + 64190.81654132414, + 61300.439943480895, + 65535.0, + 65535.0, + 39049.40622870173, + 35265.00781139653, + 35113.170257716505, + -39954.000140931545, + -35513.47414803935, + -34441.82917511126, + 34088.60614329163, + 28700.380052950874, + -36862.33271057148, + -32859.01583612004, + -31191.871795637373, + -32003.331177652333, + -13107.83700891627, + -14688.500860708387, + -18245.151473017104, + -32525.725628515836, + -33993.91682329715, + -20285.86810731778, + -20033.131856054162, + 27300.596012974405, + 26442.988504072695, + 51574.33506953686, + 48117.52341383863, + -21268.443508450517, + -24169.969300778987, + -333.7059961781342, + 1154.802032476863, + 14199.246880886509, + 20052.224683135122, + 9983.515767545046, + 11151.159484611626, + -56914.308516713114, + -54641.0075204614, + -59020.748909382295, + 44354.7875333885, + 45229.63048249438, + 17233.635105441615, + 22050.24381358763, + -28857.15840988976, + -27129.837207157525, + -27069.304704059105, + -23631.070550460405, + -10729.591221458599, + -8664.132241471627, + -12718.241381101057, + -14303.205344482918, + 55134.27218389536, + 55223.4192757591, + 35717.66938371734, + 35472.01653855634}; + wrapper linestring2_points_y{27878.56737867571, + 26489.74880629428, + -8366.313156569413, + -2066.3869793077383, + -59857.47177852941, + -54931.9723439855, + 38267.53117681052, + 36251.55819542643, + -35492.093406511354, + -30996.522241808714, + -8086.285571402303, + -8722.081551830386, + -10309.220371703479, + -61650.13799282254, + -57748.869143045675, + 36589.77853063609, + 42382.411142883866, + -42009.03475077734, + -43330.160502115265, + -43384.801359486424, + 54315.73045932353, + 51572.7217483208, + 49660.694514521245, + -17613.940599695612, + -25461.00124870416, + 2930.21018621228, + 7242.93476191668, + 7089.2529879860085, + 7935.493948240266, + -16918.249798314413, + -14646.772596539959, + -15149.208298184116, + -14858.399234970673, + 8582.92848550067, + 9399.867477813053, + 65388.53291753703, + 60708.706373685935, + -47038.67484287539, + -44436.974813653, + -46995.81345855274, + -49609.90494990711, + 48347.77610172224, + 44246.251316360154, + 47019.96704512191, + 42963.21484589453, + -63963.528608225024, + -65536.0, + 9787.392704550977, + 13439.672239478117, + 14496.797392844519, + 10148.60153259868, + -22651.800929712503, + -16649.619216575462, + 4650.1450472288125, + 5266.110859827297, + 3936.628491570741, + -53166.445914731725, + -49617.99069091295, + -47231.27985078154, + 8788.390044775617, + 17752.918413051135, + 17785.48180855107, + 10113.020826958591, + -26392.86781490936, + -31199.62771961264, + -23532.925723013777, + 57219.46266513846, + 60642.27166155047, + 61672.0437816363, + -41852.75719372246, + -43849.18942821618, + -717.0576789254337, + -2232.2731039226383, + -22610.261973542678, + -19523.462020800576, + -18869.673007678663, + -2103.483618004, + -10147.547174846575, + -16730.549997477177, + -27317.888774417457, + -27086.391416319588, + -22462.000404223618, + 64266.97812828068, + 65181.25034103983, + -15484.952003835955, + -18101.653694566507, + 17916.24799952947, + 24701.144709586413, + 16320.106016260504, + -61771.384861674625, + -52782.31111036583, + -51611.64259346673, + -58989.588758878, + -7478.666862188162, + -11380.37022343936, + -12709.082125210938, + 5900.72118584835, + 6395.841589041828, + -55978.826431399255, + -55062.60168794985, + 37912.85012154847, + 38262.034642766, + 34956.5884614418, + -35145.05363149654, + -35939.357720932756, + 20628.088617133108, + 11686.499722223638, + -24627.362833606363, + -31482.750775167715, + 31249.7777028766, + 40956.78233333402, + 41382.55735505147, + 40949.902974637465, + -10.964158675378712, + 4770.269588717389, + 3198.097209205037, + 14411.554889602106, + 11842.033811629492, + 9460.225339476907, + 55717.70516195832, + 53617.874969892895, + 54665.959995777615, + 53988.3093805043, + 41378.23077159966, + 40781.74245924522, + 40657.70325237871, + -29453.542015459017, + -28477.80164078998, + -33718.83540943996, + -29773.72523753837, + -31506.14700839999, + -31968.71507990374, + -34365.65716442856, + 52633.69733453568, + 50296.82323369251, + 51932.64482266117, + 51395.3773978117, + -42824.239844062744, + -48565.279343794704, + -29759.144315022422, + -28573.755091528295, + -29102.584349813173, + -26602.17892560728, + -28092.517122859063, + -30812.128281190624, + -32209.51925460922, + -31934.77114210906, + -16784.00232362718, + -16102.651271585919, + 35908.33715749516, + 30399.598745360658, + 34047.384049482804, + 28209.48536627418, + 31638.569927646782, + -43893.614732363516, + -49376.46085597309, + 34161.781762612896, + 34097.1586408793, + 25069.348380772386, + 18388.771898399136, + 19068.829437310942, + 19711.266488167785, + 24161.905632044534, + 44472.64135889089, + 43720.18641032237, + 48340.64725467226, + 6192.966639155318, + 6203.925100411371, + 50177.877934877455, + 47460.44816783662, + -12024.648545975433, + -4535.826811613984, + -3650.5896577858566, + -5506.66998864992, + 29128.118385679612, + 33226.99065949976, + 23780.999647643548, + 28153.639507364693, + 78.10279978545441, + 5285.053409188442, + 5290.62055375181, + 60345.148716865806, + 59151.976112230004, + -50613.88913731222, + -43699.717849091336, + -39309.64770588452, + -39663.51573174535, + 20706.3739822825, + 21403.946896398676, + 22086.225322397677, + 26273.919710552575, + -61710.11359807797, + -60698.66473027073, + -62824.68291452278, + -61261.88209984583, + 18127.01517363607, + 19695.781188654408, + -41486.82735240817, + -41060.330490692075, + -45516.04807930786, + 40633.40566135118, + 41663.87208196797, + 38449.17335973677, + -56644.6926276484, + -63116.90663080004, + 37361.362547205834, + 30364.58784041123, + 30681.304342515174, + 28855.462978012318, + -21987.243717104902, + -27379.028811674485, + -27074.127128785443, + -33362.541325203565, + -58053.93959030788, + -61657.50738405434, + -63873.143833855625, + 37016.129171396096, + 33524.920248630624, + 33784.191693610635, + 39523.342546361375, + -58150.579505434085, + -57528.51151392309, + -56378.35863513876, + -54132.85083611531, + 45463.00317181047, + 43746.11772809386, + 23991.38982378789, + 24328.329001705166, + 24658.683567616314, + -9205.219837602555, + -12202.040491158052, + -8368.457799985794, + -3823.856228930549, + -25743.589685013503, + -24495.380332734436, + -24633.63627818454, + -58013.64463594009, + -65536.0, + -65536.0, + 43594.20653120625, + 42070.82051457739, + 8307.234739860229, + 10956.658157004455, + 15817.792373324339, + 20551.44779746299, + 42872.525866358264, + 38601.73767513557, + 33305.31165731295, + 21915.76596169251, + 22187.083798396245, + 8878.050464641972, + 7329.012948283676, + -8923.000585077694, + -5957.730299055878, + 42988.068045101856, + 41995.973894229064, + 606.5265997462848, + 4584.574914234327, + -15000.66037914269, + -23481.51835062977, + -34169.010785194405, + -27270.698407036958, + -47272.24666691125, + -45805.26551921326, + 37722.89763812795, + 42314.87487608766, + 39496.29702855225, + 53499.2552621721, + 47617.793038744414, + 38604.48437014686, + 44812.19238923583, + -49424.475982191696, + -45186.618133268355, + -45232.76215178083, + -54597.0418612454, + -12119.938636133666, + -15205.054401436506, + -17599.196968326814, + -18083.072472929212, + 56421.5370066538, + 56480.56370818126, + -59201.01835964643, + -59182.15595508969}; + + wrapper expected{ + 22000.86425379464, 66907.56415814416, 91319.97744223749, 85968.18301126428, + 102672.3263797747, 65694.82522031936, 23155.596407172834, 69965.78080466804, + 74404.84663547449, 50401.990020141246, 86674.09733792665, 78620.63597017915, + 50270.741668061564, 21742.759144551062, 57053.338441131185, 59274.33513507705, + 48859.389221588026, 81538.69583988942, 102451.30976584884, 48907.78040391256, + 2541.1783777563724, 54018.73837654963, 80019.79050636332, 99125.18435170795, + 86909.77084040688, 100366.96181026987, 68313.25509221756, 97214.36651018754, + 60564.99623503783, 79060.78098596593, 120581.50538980225, 42683.374059987655, + 72540.83585301196, 97288.29179292868, 13733.85425546725, 15638.255020170804, + 99285.77366088502, 36490.14927299419, 22055.190136582223, 59756.41455959068, + 55999.06431045312, 34499.301972964764, 40783.02779733117, 110287.1568555378, + 66432.78557813511, 13775.437319014114, 84497.96964551898, 25978.924986056714, + 62408.80191071194, 93243.74615580632, 52325.4457234104, 79966.38779743231, + 12679.929187884523, 73553.57489706302, 73768.1449667809, 32163.1673140078, + 76843.7399743497, 62642.67444253966, 7647.299340786608, 70644.00936419012, + 75372.8193039762, 67974.71683210952, 38255.30745023753, 86968.76019213336, + 134523.96397533367, 64474.156010664876, 59565.33783639761, 80474.25882097102, + 38173.63396698884, 47219.681710204335, 80438.60559838371, 107397.04958322046, + 38840.989826087316, 92404.38320717831, 142972.7142246719, 120743.00851595176, + 88624.62602444092, 80480.41611758419, 96883.04928998687, 39384.08522694212, + 93051.51572055934, 106136.58472613667, 35435.02384720026, 7045.002778172422, + 61903.57835829537, 82540.74978755743, 43930.005897185314, 38805.25027105973, + 110525.61810095713, 30289.12339639058, 55190.61741153193, 65184.895015007314, + 113100.29945781764, 105574.82641833455, 85774.24359854191, 2878.1066075084723, + 85871.70969639975, 12523.482980018343, 116504.46834362095, 69644.48579719076}; + + auto got = pairwise_linestring_distance(column_view(linestring1_offsets), + linestring1_points_x, + linestring1_points_y, + column_view(linestring2_offsets), + linestring2_points_x, + linestring2_points_y); + expect_columns_equivalent(expected, *got, verbosity); +} + +} // namespace test +} // namespace cuspatial From 6deb2613694376cf8ca13eb5c5a1da6b6a269397 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 6 Apr 2022 19:22:54 -0700 Subject: [PATCH 06/67] Wrapping debug prints --- cpp/src/spatial/linestring_distance.cu | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index 8f35333b1..d4e1096ba 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -37,6 +37,7 @@ #ifndef DEBUG #define DEBUG 1 +#endif template void __device__ print(cuspatial::coord_2d const& point) @@ -50,8 +51,6 @@ void __device__ print(cuspatial::coord_2d const& A, cuspatial::coord_2d co printf("SEGMENT (%f, %f) -> (%f, %f)\n", A.x, A.y, B.x, B.y); } -#endif - namespace cuspatial { namespace { @@ -83,6 +82,7 @@ double __device__ segment_distance_no_intersect(coord_2d const& A, coord_2d const& C, coord_2d const& D) { +#ifdef DEBUG printf("From: \n"); print(A); printf("To: \n"); @@ -106,6 +106,7 @@ double __device__ segment_distance_no_intersect(coord_2d const& A, printf("To: \n"); print(A, B); printf("Distance %f\n", point_to_segment_distance(D, A, B)); +#endif return std::min(std::min(point_to_segment_distance(A, C, D), point_to_segment_distance(B, C, D)), std::min(point_to_segment_distance(C, A, B), point_to_segment_distance(D, A, B))); } From 61716c63b0017d99c0519e6fb3eb6c74783ccc88 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 7 Apr 2022 12:04:27 -0700 Subject: [PATCH 07/67] Initial --- cpp/benchmarks/CMakeLists.txt | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/cpp/benchmarks/CMakeLists.txt b/cpp/benchmarks/CMakeLists.txt index 801bed2bc..00fbba2f1 100644 --- a/cpp/benchmarks/CMakeLists.txt +++ b/cpp/benchmarks/CMakeLists.txt @@ -40,7 +40,7 @@ target_include_directories(cuspatial_benchmark_common function(ConfigureBench CMAKE_BENCH_NAME) add_executable(${CMAKE_BENCH_NAME} ${ARGN}) set_target_properties(${CMAKE_BENCH_NAME} - PROPERTIES RUNTIME_OUTPUT_DIRECTORY "$" + PROPERTIES RUNTIME_OUTPUT_DIRECTORY "$" INSTALL_RPATH "\$ORIGIN/../../../lib" ) target_link_libraries(${CMAKE_BENCH_NAME} PRIVATE benchmark::benchmark_main cuspatial_benchmark_common) @@ -52,6 +52,25 @@ function(ConfigureBench CMAKE_BENCH_NAME) ) endfunction(ConfigureBench) +function(ConfigureNVBench CMAKE_BENCH_NAME) + add_executable(${CMAKE_BENCH_NAME} ${ARGN}) + set_target_properties( + ${CMAKE_BENCH_NAME} + PROPERTIES RUNTIME_OUTPUT_DIRECTORY "$" + INSTALL_RPATH "\$ORIGIN/../../../lib" + ) + target_link_libraries( + ${CMAKE_BENCH_NAME} PRIVATE cuspatial_benchmark_common nvbench::main + ) + install( + TARGETS ${CMAKE_BENCH_NAME} + COMPONENT testing + DESTINATION bin/benchmarks/libcuspatial + EXCLUDE_FROM_ALL + ) + +endfunction(ConfigureNVBench) + ################################################################################################### ### benchmark sources ############################################################################# ################################################################################################### From 3cab72d6b1a268c3b80195064e1204f118bcb7cd Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 7 Apr 2022 16:38:27 -0700 Subject: [PATCH 08/67] initial --- cpp/include/cuspatial/error.hpp | 33 ++++++++++++++++++++++++++++- cpp/src/interpolate/cubic_spline.cu | 2 +- cpp/src/spatial/hausdorff.cu | 2 +- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/cpp/include/cuspatial/error.hpp b/cpp/include/cuspatial/error.hpp index 1747bb08f..9b8cc21ef 100644 --- a/cpp/include/cuspatial/error.hpp +++ b/cpp/include/cuspatial/error.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, NVIDIA CORPORATION. + * Copyright (c) 2020-2022, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ #pragma once +#include +#include #include #include @@ -33,6 +35,12 @@ struct logic_error : public std::logic_error { logic_error(std::string const& message) : std::logic_error(message) {} }; +/** + * @brief Exception thrown when a CUDA error is encountered. + */ +struct cuda_error : public std::runtime_error { + cuda_error(std::string const& message) : std::runtime_error(message) {} +}; } // namespace cuspatial #define STRINGIFY_DETAIL(x) #x @@ -78,5 +86,28 @@ struct logic_error : public std::logic_error { namespace cuspatial { namespace detail { +inline void throw_cuda_error(cudaError_t error, const char* file, unsigned int line) +{ + throw cuspatial::cuda_error(std::string{ + "CUDA error encountered at: " + std::string{file} + ":" + std::to_string(line) + ": " + + std::to_string(error) + " " + cudaGetErrorName(error) + " " + cudaGetErrorString(error)}); +} + } // namespace detail } // namespace cuspatial + +/** + * @brief Error checking macro for CUDA runtime API functions. + * + * Invokes a CUDA runtime API function call, if the call does not return + * cudaSuccess, invokes cudaGetLastError() to clear the error and throws an + * exception detailing the CUDA error that occurred + */ +#define CUSPATIAL_CUDA_TRY(call) \ + do { \ + cudaError_t const status = (call); \ + if (cudaSuccess != status) { \ + cudaGetLastError(); \ + cuspatial::detail::throw_cuda_error(status, __FILE__, __LINE__); \ + } \ + } while (0); diff --git a/cpp/src/interpolate/cubic_spline.cu b/cpp/src/interpolate/cubic_spline.cu index c213496aa..80c59919a 100644 --- a/cpp/src/interpolate/cubic_spline.cu +++ b/cpp/src/interpolate/cubic_spline.cu @@ -407,7 +407,7 @@ std::unique_ptr cubicspline_coefficients(cudf::column_view const& t // pBuffer: get size of thisu by gtsv2_bufferSizeExt cusparseHandle_t handle; - CUDF_CUDA_TRY(cudaMalloc(&handle, sizeof(cusparseHandle_t))); + CUSPATIAL_CUDA_TRY(cudaMalloc(&handle, sizeof(cusparseHandle_t))); CUSPARSE_TRY(cusparseCreate(&handle)); size_t pBufferSize; diff --git a/cpp/src/spatial/hausdorff.cu b/cpp/src/spatial/hausdorff.cu index 6a696ab9b..df9dd6bca 100644 --- a/cpp/src/spatial/hausdorff.cu +++ b/cpp/src/spatial/hausdorff.cu @@ -178,7 +178,7 @@ struct hausdorff_functor { space_offsets.begin(), result_view.begin()); - CUDF_CUDA_TRY(cudaGetLastError()); + CUSPATIAL_CUDA_TRY(cudaGetLastError()); return result; } From f6b571add1af654ff34ca5ede3f810eb5a21c597 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 7 Apr 2022 16:56:23 -0700 Subject: [PATCH 09/67] Update synchonization.cpp --- .../synchronization/synchronization.cpp | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/cpp/benchmarks/synchronization/synchronization.cpp b/cpp/benchmarks/synchronization/synchronization.cpp index bd8a4d1de..a6a552a03 100644 --- a/cpp/benchmarks/synchronization/synchronization.cpp +++ b/cpp/benchmarks/synchronization/synchronization.cpp @@ -16,7 +16,7 @@ #include "synchronization.hpp" -#include +#include #include #include @@ -29,32 +29,33 @@ cuda_event_timer::cuda_event_timer(benchmark::State& state, // flush all of L2$ if (flush_l2_cache) { int current_device = 0; - CUDA_TRY(cudaGetDevice(¤t_device)); + CUSPATIAL_CUDA_TRY(cudaGetDevice(¤t_device)); int l2_cache_bytes = 0; - CUDA_TRY(cudaDeviceGetAttribute(&l2_cache_bytes, cudaDevAttrL2CacheSize, current_device)); + CUSPATIAL_CUDA_TRY( + cudaDeviceGetAttribute(&l2_cache_bytes, cudaDevAttrL2CacheSize, current_device)); if (l2_cache_bytes > 0) { const int memset_value = 0; rmm::device_buffer l2_cache_buffer(l2_cache_bytes, stream); - CUDA_TRY( + CUSPATIAL_CUDA_TRY( cudaMemsetAsync(l2_cache_buffer.data(), memset_value, l2_cache_bytes, stream.value())); } } - CUDA_TRY(cudaEventCreate(&start)); - CUDA_TRY(cudaEventCreate(&stop)); - CUDA_TRY(cudaEventRecord(start, stream.value())); + CUSPATIAL_CUDA_TRY(cudaEventCreate(&start)); + CUSPATIAL_CUDA_TRY(cudaEventCreate(&stop)); + CUSPATIAL_CUDA_TRY(cudaEventRecord(start, stream.value())); } cuda_event_timer::~cuda_event_timer() { - CUDA_TRY(cudaEventRecord(stop, stream.value())); - CUDA_TRY(cudaEventSynchronize(stop)); + CUSPATIAL_CUDA_TRY(cudaEventRecord(stop, stream.value())); + CUSPATIAL_CUDA_TRY(cudaEventSynchronize(stop)); float milliseconds = 0.0f; - CUDA_TRY(cudaEventElapsedTime(&milliseconds, start, stop)); + CUSPATIAL_CUDA_TRY(cudaEventElapsedTime(&milliseconds, start, stop)); p_state->SetIterationTime(milliseconds / (1000.0f)); - CUDA_TRY(cudaEventDestroy(start)); - CUDA_TRY(cudaEventDestroy(stop)); + CUSPATIAL_CUDA_TRY(cudaEventDestroy(start)); + CUSPATIAL_CUDA_TRY(cudaEventDestroy(stop)); } From 90de8cee2083c82528c7e901edbbf154d807709f Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 7 Apr 2022 18:00:07 -0700 Subject: [PATCH 10/67] Replace one more usage of `CUDF_CUDA_TRY` --- cpp/src/join/quadtree_point_to_nearest_polyline.cu | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cpp/src/join/quadtree_point_to_nearest_polyline.cu b/cpp/src/join/quadtree_point_to_nearest_polyline.cu index ba9924c40..5d60fc1e4 100644 --- a/cpp/src/join/quadtree_point_to_nearest_polyline.cu +++ b/cpp/src/join/quadtree_point_to_nearest_polyline.cu @@ -27,7 +27,6 @@ #include #include #include -#include #include #include @@ -248,7 +247,7 @@ struct compute_quadtree_point_to_nearest_polyline { rmm::device_uvector distances(point_x.size(), stream); // Fill distances with 0 - CUDF_CUDA_TRY( + CUSPATIAL_CUDA_TRY( cudaMemsetAsync(distances.data(), 0, distances.size() * sizeof(T), stream.value())); // Reduce the intermediate point/polyline indices to lists of point/polyline index pairs and From d82f8876f7154e366df7d6be009f80d0fcf6202b Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 8 Apr 2022 00:29:58 -0700 Subject: [PATCH 11/67] Add nvbench cpm --- cpp/CMakeLists.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 1bdb1758f..1b9963e88 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -218,6 +218,22 @@ if(CUSPATIAL_BUILD_BENCHMARKS) GIT_SHALLOW TRUE OPTIONS "BENCHMARK_ENABLE_TESTING OFF" "BENCHMARK_ENABLE_INSTALL OFF") + + set(nvbench_with_nvml "OFF") + if(TARGET CUDA::nvml) + set(nvbench_with_nvml "ON") + endif() + + message("nvbench configured with nvml ${nvbench_with_nvml}") + # Find or install NVBench + CPMFindPackage(NAME NVBench + VERSION 0.1.0 + GIT_REPOSITORY https://github.com/NVIDIA/nvbench.git + GIT_TAG main + GIT_SHALLOW TRUE + OPTIONS "NVBench_ENABLE_EXAMPLES OFF" + "NVBench_ENABLE_TESTING OFF" + "NVBench_ENABLE_NVML ${nvbench_with_nvml}") add_subdirectory(benchmarks) endif() From 813178caea2858d281e80f4059b5786d8dfee797 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 8 Apr 2022 12:20:52 -0700 Subject: [PATCH 12/67] Add runnable nvbench --- cpp/benchmarks/CMakeLists.txt | 3 + cpp/benchmarks/fixture/rmm_pool_raii.hpp | 71 +++++++++++ .../pairwise_linestring_distance.cu | 110 ++++++++++++++++++ 3 files changed, 184 insertions(+) create mode 100644 cpp/benchmarks/fixture/rmm_pool_raii.hpp create mode 100644 cpp/benchmarks/pairwise_linestring_distance.cu diff --git a/cpp/benchmarks/CMakeLists.txt b/cpp/benchmarks/CMakeLists.txt index 00fbba2f1..e46995c95 100644 --- a/cpp/benchmarks/CMakeLists.txt +++ b/cpp/benchmarks/CMakeLists.txt @@ -80,3 +80,6 @@ endfunction(ConfigureNVBench) ConfigureBench(HAUSDORFF_BENCH hausdorff_benchmark.cpp) + +ConfigureNVBench(DISTANCES_BENCH + pairwise_linestring_distance.cu) diff --git a/cpp/benchmarks/fixture/rmm_pool_raii.hpp b/cpp/benchmarks/fixture/rmm_pool_raii.hpp new file mode 100644 index 000000000..444b9a7cb --- /dev/null +++ b/cpp/benchmarks/fixture/rmm_pool_raii.hpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021, 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 An RAII class setting up RMM memory pool for `nvbench` benchmarks + * + * This is a temporary solution before templated fixtures tests are supported + * in `nvbench`. Similarly to `cuspatial::benchmark`, creating this RAII object in + * each benchmark will ensure that the RAPIDS Memory Manager pool mode is used + * in benchmarks, which eliminates memory allocation / deallocation performance + * overhead from the benchmark. + * + * Example: + * + * void my_benchmark(nvbench::state& state) { + * cuspatial::rmm_pool_raii pool_raii; + * state.exec([](nvbench::launch& launch) { + * // benchmark stuff + * }); + * } + * + * NVBENCH_BENCH(my_benchmark); + */ +class rmm_pool_raii { + private: + // memory resource factory helpers + inline auto make_cuda() { return std::make_shared(); } + + inline auto make_pool() + { + return rmm::mr::make_owning_wrapper(make_cuda()); + } + + public: + rmm_pool_raii() + { + mr = make_pool(); + rmm::mr::set_current_device_resource(mr.get()); // set default resource to pool + } + + ~rmm_pool_raii() + { + rmm::mr::set_current_device_resource(nullptr); + mr.reset(); + } + + private: + std::shared_ptr mr; +}; + +} // namespace cuspatial diff --git a/cpp/benchmarks/pairwise_linestring_distance.cu b/cpp/benchmarks/pairwise_linestring_distance.cu new file mode 100644 index 000000000..478e9c04d --- /dev/null +++ b/cpp/benchmarks/pairwise_linestring_distance.cu @@ -0,0 +1,110 @@ +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include + +namespace cuspatial { + +template +std::tuple, + std::unique_ptr, + rmm::device_uvector> +generate_linestring(cudf::size_type num_strings, + cudf::size_type num_segments_per_string, + T segment_length, + T init_xy, + rmm::cuda_stream_view stream) +{ + cudf::size_type num_points = num_strings * (num_segments_per_string + 1); + rmm::device_uvector offsets(num_points, stream); + thrust::transform( + rmm::exec_policy(stream), + thrust::make_counting_iterator(static_cast(0)), + thrust::make_counting_iterator(static_cast(num_points)), + offsets.begin(), + [num_segments_per_string] __device__(auto i) { return i * num_segments_per_string; }); + auto points_x = cudf::make_fixed_width_column( + cudf::data_type{cudf::type_to_id()}, num_points, cudf::mask_state::UNALLOCATED); + auto points_y = cudf::make_fixed_width_column( + cudf::data_type{cudf::type_to_id()}, num_points, cudf::mask_state::UNALLOCATED); + thrust::transform(rmm::exec_policy(stream), + thrust::counting_iterator(static_cast(0)), + thrust::counting_iterator(static_cast(num_points)), + points_x->mutable_view().begin(), + [] __device__(auto i) { return cos(i); }); + thrust::exclusive_scan( + rmm::exec_policy(stream), + points_x->view().begin(), + points_x->view().end(), + points_x->mutable_view().begin(), + init_xy, + [segment_length] __device__(T prev, T rad) { return prev + segment_length * rad; }); + thrust::transform(rmm::exec_policy(stream), + thrust::counting_iterator(static_cast(0)), + thrust::counting_iterator(static_cast(num_points)), + points_y->mutable_view().begin(), + [] __device__(auto i) { return sin(i); }); + thrust::exclusive_scan( + rmm::exec_policy(stream), + points_y->view().begin(), + points_y->view().end(), + points_y->mutable_view().begin(), + init_xy, + [segment_length] __device__(T prev, T rad) { return prev + segment_length * rad; }); + + return std::tuple(std::move(points_x), std::move(points_y), std::move(offsets)); +} + +template +void pairwise_linestring_distance_benchmark(nvbench::state& state, nvbench::type_list) +{ + // TODO: to be replaced by nvbench fixture once it's ready + cuspatial::rmm_pool_raii rmm_pool; + + auto const num_string_pairs{state.get_int64("NumStrings")}, + num_segments_per_string{state.get_int64("NumSegmentsPerString")}; + auto stream = rmm::cuda_stream_default; + + auto [ls1_x, ls1_y, ls1_offset] = + generate_linestring(num_string_pairs, num_segments_per_string, 1, 0, stream); + auto [ls2_x, ls2_y, ls2_offset] = + generate_linestring(num_string_pairs, num_segments_per_string, 1, 100, stream); + + auto const total_points = ls1_x->size() + ls2_x->size(); + + state.add_element_count(num_string_pairs, "LineStringPairs"); + state.add_element_count(total_points, "NumPoints"); + state.add_global_memory_reads(total_points * 2, "CoordinatesDataSize"); + state.add_global_memory_writes(num_string_pairs); + + state.exec(nvbench::exec_tag::sync, + [ls1_offset = cudf::device_span(ls1_offset), + ls1_x = ls1_x->view(), + ls1_y = ls1_y->view(), + ls2_offset = cudf::device_span(ls2_offset), + ls2_x = ls2_x->view(), + ls2_y = ls2_y->view()](nvbench::launch& launch) { + cuspatial::pairwise_linestring_distance( + ls1_offset, ls1_x, ls1_y, ls2_offset, ls2_x, ls2_y); + }); +} + +using floating_point_types = nvbench::type_list; +NVBENCH_BENCH_TYPES(pairwise_linestring_distance_benchmark, NVBENCH_TYPE_AXES(floating_point_types)) + .set_type_axes_names({"CoordsType"}) + .add_int64_axis("NumStrings", {100'000, 1'000'000, 10'000'000}) + .add_int64_axis("NumSegmentsPerString", {100, 1'000, 10'000}); + +} // namespace cuspatial From aa65c757d3295d9515a00897e4aaea80931f26ba Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 8 Apr 2022 13:42:03 -0700 Subject: [PATCH 13/67] Add data gen synchronizer --- cpp/benchmarks/pairwise_linestring_distance.cu | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpp/benchmarks/pairwise_linestring_distance.cu b/cpp/benchmarks/pairwise_linestring_distance.cu index 478e9c04d..d79a53541 100644 --- a/cpp/benchmarks/pairwise_linestring_distance.cu +++ b/cpp/benchmarks/pairwise_linestring_distance.cu @@ -82,6 +82,8 @@ void pairwise_linestring_distance_benchmark(nvbench::state& state, nvbench::type auto [ls2_x, ls2_y, ls2_offset] = generate_linestring(num_string_pairs, num_segments_per_string, 1, 100, stream); + cudaStreamSynchronize(stream.value()); + auto const total_points = ls1_x->size() + ls2_x->size(); state.add_element_count(num_string_pairs, "LineStringPairs"); From 511dd5d9b6d12637368dbbe0b34c91f9f23a111f Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 8 Apr 2022 17:38:45 -0700 Subject: [PATCH 14/67] Rewrites kernel to schedule on num points --- cpp/src/spatial/linestring_distance.cu | 216 ++++++++++++++----------- 1 file changed, 123 insertions(+), 93 deletions(-) diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index d4e1096ba..0805e2bd2 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -23,11 +23,13 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -36,7 +38,7 @@ #include #ifndef DEBUG -#define DEBUG 1 +// #define DEBUG 1 #endif template @@ -67,7 +69,9 @@ double __device__ point_to_segment_distance(coord_2d const& C, double L_squared = (A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y); if (L_squared == 0) { return hypot(C.x - A.x, C.y - A.y); } double r = ((C.x - A.x) * (B.x - A.x) + (C.y - A.y) * (B.y - A.y)) / L_squared; +#ifdef DEBUG printf("\t r=%f\n", r); +#endif if (r <= 0 or r >= 1) { return std::min(hypot(C.x - A.x, C.y - A.y), hypot(C.x - B.x, C.y - B.y)); } @@ -140,72 +144,87 @@ double __device__ segment_distance(coord_2d const& A, return segment_distance_no_intersect(A, B, C, D); } -template -struct linestirngs_pairs_min_distance_functor { +template +void __global__ kernel(OffsetIterator linestring1_offsets_begin, + OffsetIterator linestring1_offsets_end, + CoordinateIterator linestring1_points_xs_begin, + CoordinateIterator linestring1_points_xs_end, + CoordinateIterator linestring1_points_ys_begin, + OffsetIterator linestring2_offsets_begin, + CoordinateIterator linestring2_points_xs_begin, + CoordinateIterator linestring2_points_xs_end, + CoordinateIterator linestring2_points_ys_begin, + OutputIterator min_distances) +{ using T = typename std::iterator_traits::value_type; - cudf::size_type num_linestrings; - cudf::size_type linestring1_num_points; - cudf::size_type linestring2_num_points; - OffsetIterator linestring1_offsets; - CoordinateIterator linestring1_points_xs; - CoordinateIterator linestring1_points_ys; - OffsetIterator linestring2_offsets; - CoordinateIterator linestring2_points_xs; - CoordinateIterator linestring2_points_ys; - - linestirngs_pairs_min_distance_functor(cudf::size_type num_linestrings, - cudf::size_type linestring1_num_points, - cudf::size_type linestring2_num_points, - OffsetIterator linestring1_offsets, - CoordinateIterator linestring1_points_xs, - CoordinateIterator linestring1_points_ys, - OffsetIterator linestring2_offsets, - CoordinateIterator linestring2_points_xs, - CoordinateIterator linestring2_points_ys) - : num_linestrings(num_linestrings), - linestring1_num_points(linestring1_num_points), - linestring2_num_points(linestring2_num_points), - linestring1_offsets(linestring1_offsets), - linestring1_points_xs(linestring1_points_xs), - linestring1_points_ys(linestring1_points_ys), - linestring2_offsets(linestring2_offsets), - linestring2_points_xs(linestring2_points_xs), - linestring2_points_ys(linestring2_points_ys) - { + auto const p1Idx = threadIdx.x + blockIdx.x * blockDim.x; + cudf::size_type const num_linestrings = + thrust::distance(linestring1_offsets_begin, linestring1_offsets_end); + cudf::size_type const linestring1_num_points = + thrust::distance(linestring1_points_xs_begin, linestring1_points_xs_end); + cudf::size_type const linestring2_num_points = + thrust::distance(linestring2_points_xs_begin, linestring2_points_xs_end); + +#ifdef DEBUG + printf("p1Idx: %d\n", p1Idx); + printf("linestring1_num_points: %d\n", linestring1_num_points); +#endif + + if (p1Idx >= linestring1_num_points) { return; } + + cudf::size_type const linestring_idx = + thrust::distance( + linestring1_offsets_begin, + thrust::upper_bound(thrust::seq, linestring1_offsets_begin, linestring1_offsets_end, p1Idx)) - + 1; + + cudf::size_type ls1End = + (linestring_idx == (num_linestrings - 1) ? (linestring1_num_points) + : *(linestring1_offsets_begin + linestring_idx + 1)) - + 1; + +#ifdef DEBUG + printf("p1Idx: %d\n", p1Idx); + printf("ls1End: %d\n", ls1End); + printf("linestring_idx: %d\n", linestring_idx); + printf("num_linestrings: %d\n", num_linestrings); +#endif + if (p1Idx == ls1End) { + // Current point is the end point of the line string. + return; } - T __device__ operator()(cudf::size_type idx) - { - auto const l1pts_start = linestring1_offsets[idx]; - auto const l1pts_end = - idx == (num_linestrings - 1) ? linestring1_num_points : linestring1_offsets[idx + 1]; - auto const l2pts_start = linestring2_offsets[idx]; - auto const l2pts_end = - idx == (num_linestrings - 1) ? linestring2_num_points : linestring2_offsets[idx + 1]; + cudf::size_type ls2Start = *(linestring2_offsets_begin + linestring_idx); + cudf::size_type ls2End = + (linestring_idx == (num_linestrings - 1) ? linestring2_num_points + : *(linestring2_offsets_begin + linestring_idx + 1)) - + 1; + + coord_2d A{linestring1_points_xs_begin[p1Idx], linestring1_points_ys_begin[p1Idx]}; + coord_2d B{linestring1_points_xs_begin[p1Idx + 1], linestring1_points_ys_begin[p1Idx + 1]}; + #ifdef DEBUG - printf("idx: %d\n", idx); - printf("num_points_ls1: %d\n", linestring1_num_points); - printf("num_points_ls2: %d\n", linestring2_num_points); - printf("l1pts: %d, %d\n", l1pts_start, l1pts_end); - printf("l2pts: %d, %d\n", l2pts_start, l2pts_end); + printf("linestring_idx: %d\n", linestring_idx); + printf("num_linestrings: %d\n", num_linestrings); + printf("linestring1_num_points: %d\n", linestring1_num_points); + printf("linestring2_num_points: %d\n", linestring2_num_points); + printf("p1Idx: %d\n", p1Idx); + printf("ls2Start: %d\n", ls2Start); + printf("ls2End: %d\n", ls2End); + print(A, B); #endif - double min_distance = std::numeric_limits::max(); - for (cudf::size_type i = l1pts_start; i < l1pts_end - 1; i++) { - for (cudf::size_type j = l2pts_start; j < l2pts_end - 1; j++) { - coord_2d A{linestring1_points_xs[i], linestring1_points_ys[i]}; - coord_2d B{linestring1_points_xs[i + 1], linestring1_points_ys[i + 1]}; - coord_2d C{linestring2_points_xs[j], linestring2_points_ys[j]}; - coord_2d D{linestring2_points_xs[j + 1], linestring2_points_ys[j + 1]}; - min_distance = std::min(segment_distance(A, B, C, D), min_distance); + double min_distance = std::numeric_limits::max(); + for (cudf::size_type p2Idx = ls2Start; p2Idx < ls2End; p2Idx++) { + coord_2d C{linestring2_points_xs_begin[p2Idx], linestring2_points_ys_begin[p2Idx]}; + coord_2d D{linestring2_points_xs_begin[p2Idx + 1], linestring2_points_ys_begin[p2Idx + 1]}; #ifdef DEBUG - printf("%d %d, %f\n", i, j, min_distance); + print(C, D); #endif - } - } - return min_distance; + min_distance = std::min(min_distance, segment_distance(A, B, C, D)); } -}; + atomicMin(min_distances + linestring_idx, static_cast(min_distance)); +} } // anonymous namespace @@ -224,27 +243,54 @@ struct pariwise_linestring_distance_functor { { using namespace cudf; - auto const num_strings = static_cast(linestring1_offsets.size()); + auto const num_string_pairs = static_cast(linestring1_offsets.size()); auto min_distances = - make_numeric_column(data_type{type_to_id()}, num_strings, mask_state::UNALLOCATED); - - auto functor = linestirngs_pairs_min_distance_functor(num_strings, - linestring1_points_x.size(), - linestring2_points_x.size(), - linestring1_offsets.begin(), - linestring1_points_x.begin(), - linestring1_points_y.begin(), - linestring2_offsets.begin(), - linestring2_points_x.begin(), - linestring2_points_y.begin()); - - std::cout << "number of strings: " << num_strings << std::endl; - thrust::transform(rmm::exec_policy(stream), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(num_strings), - min_distances->mutable_view().begin(), - functor); + make_numeric_column(data_type{type_to_id()}, num_string_pairs, mask_state::UNALLOCATED); + + // auto functor = linestirngs_pairs_min_distance_functor(num_string_pairs, + // linestring1_points_x.size(), + // linestring2_points_x.size(), + // linestring1_offsets.begin(), + // linestring1_points_x.begin(), + // linestring1_points_y.begin(), + // linestring2_offsets.begin(), + // linestring2_points_x.begin(), + // linestring2_points_y.begin()); + + // thrust::transform(rmm::exec_policy(stream), + // thrust::make_counting_iterator(0), + // thrust::make_counting_iterator(num_string_pairs), + // min_distances->mutable_view().begin(), + // functor); + + thrust::fill(rmm::exec_policy(stream), + min_distances->mutable_view().begin(), + min_distances->mutable_view().end(), + std::numeric_limits::max()); + + std::size_t const threads_per_block = 64; + std::size_t const num_blocks = + (linestring1_points_x.size() + threads_per_block - 1) / threads_per_block; + +#ifdef DEBUG + std::cout << "number of strings: " << num_string_pairs << std::endl; + std::cout << "num blocks" << num_blocks << std::endl; +#endif + kernel<<>>( + linestring1_offsets.begin(), + linestring1_offsets.end(), + linestring1_points_x.begin(), + linestring1_points_x.end(), + linestring1_points_y.begin(), + linestring2_offsets.begin(), + linestring2_points_x.begin(), + linestring2_points_x.end(), + linestring2_points_y.begin(), + min_distances->mutable_view().begin()); + + CUSPATIAL_CUDA_TRY(cudaGetLastError()); + return min_distances; } @@ -264,22 +310,6 @@ bool validate_linestring(cudf::device_span linestring_off rmm::cuda_stream_view stream) { if (linestring_offsets.size() == 1) { return linestring_points_x.size() >= 2; } - // auto linestring_validate_iter = cudf::detail::make_counting_transform_iterator( - // 0, - // [offsets = linestring_offsets.begin(), - // num_points = static_cast(linestring_points_x.size()), - // num_offsets = - // static_cast(linestring_offsets.size())] __device__(cudf::size_type idx) { - // cudf::size_type begin = offsets[idx]; - // cudf::size_type end = idx == num_offsets ? num_points : offsets[idx + 1]; - // return (end - begin) >= 2; - // }); - // return thrust::reduce(rmm::exec_policy(stream), - // linestring_validate_iter, - // linestring_validate_iter + linestring_offsets.size(), - // true, - // [](auto i, auto j) { return i && j; }); - return thrust::reduce( rmm::exec_policy(stream), thrust::make_counting_iterator(cudf::size_type{0}), From cee2c1f119db2f753bb47ac923b219c469fa08d5 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 8 Apr 2022 17:38:59 -0700 Subject: [PATCH 15/67] Change problem size in benchmarks --- cpp/benchmarks/pairwise_linestring_distance.cu | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cpp/benchmarks/pairwise_linestring_distance.cu b/cpp/benchmarks/pairwise_linestring_distance.cu index d79a53541..6fbd2f55b 100644 --- a/cpp/benchmarks/pairwise_linestring_distance.cu +++ b/cpp/benchmarks/pairwise_linestring_distance.cu @@ -89,6 +89,7 @@ void pairwise_linestring_distance_benchmark(nvbench::state& state, nvbench::type state.add_element_count(num_string_pairs, "LineStringPairs"); state.add_element_count(total_points, "NumPoints"); state.add_global_memory_reads(total_points * 2, "CoordinatesDataSize"); + state.add_global_memory_reads(num_string_pairs * 2, "OffsetsDataSize"); state.add_global_memory_writes(num_string_pairs); state.exec(nvbench::exec_tag::sync, @@ -106,7 +107,7 @@ void pairwise_linestring_distance_benchmark(nvbench::state& state, nvbench::type using floating_point_types = nvbench::type_list; NVBENCH_BENCH_TYPES(pairwise_linestring_distance_benchmark, NVBENCH_TYPE_AXES(floating_point_types)) .set_type_axes_names({"CoordsType"}) - .add_int64_axis("NumStrings", {100'000, 1'000'000, 10'000'000}) - .add_int64_axis("NumSegmentsPerString", {100, 1'000, 10'000}); + .add_int64_axis("NumStrings", {1'000, 10'000, 100'000}) + .add_int64_axis("NumSegmentsPerString", {10, 100, 1'000}); } // namespace cuspatial From 3bf3d446b6bbfec09a39f8c869ee40ea1dd0490e Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 13 Apr 2022 17:08:43 -0700 Subject: [PATCH 16/67] In the middle of getting the compiler to include the file. --- .../detail/linestring_distance.cuh | 256 ++++++++++++++++ .../experimental/linestring_distance.cuh | 43 +++ .../cuspatial/experimental/type_utils.hpp | 52 ++++ cpp/include/cuspatial/types.hpp | 14 +- cpp/src/spatial/linestring_distance.cu | 284 ++---------------- 5 files changed, 382 insertions(+), 267 deletions(-) create mode 100644 cpp/include/cuspatial/experimental/detail/linestring_distance.cuh create mode 100644 cpp/include/cuspatial/experimental/linestring_distance.cuh create mode 100644 cpp/include/cuspatial/experimental/type_utils.hpp diff --git a/cpp/include/cuspatial/experimental/detail/linestring_distance.cuh b/cpp/include/cuspatial/experimental/detail/linestring_distance.cuh new file mode 100644 index 000000000..e81a80678 --- /dev/null +++ b/cpp/include/cuspatial/experimental/detail/linestring_distance.cuh @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2022, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +namespace cuspatital { +namespace detail { + +template +constexpr bool is_same() { + return std::conjunction_v...>; +} + +template +constexpr bool is_floating_point() { + return std::conjunction_v...>; +} + +template +double __device__ point_to_segment_distance(cuspatial::cart_2d const& C, + cuspatial::cart_2d const& A, + cuspatial::cart_2d const& B) +{ + // Subject 1.02 of https://www.inf.pucrs.br/~pinho/CG/faq.html + // Project the point to the segment, if it lands on the segment, + // the distance is the length of proejction, otherwise it's the + // length to one of the end points. + + double L_squared = (A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y); + if (L_squared == 0) { return hypot(C.x - A.x, C.y - A.y); } + double r = ((C.x - A.x) * (B.x - A.x) + (C.y - A.y) * (B.y - A.y)) / L_squared; + if (r <= 0 or r >= 1) { + return std::min(hypot(C.x - A.x, C.y - A.y), hypot(C.x - B.x, C.y - B.y)); + } + double Px = A.x + r * (B.x - A.x); + double Py = A.y + r * (B.y - A.y); + return hypot(C.x - Px, C.y - Py); +} + +template +double __device__ segment_distance_no_intersect(cuspatial::cart_2d const& A, + cuspatial::cart_2d const& B, + cuspatial::cart_2d const& C, + cuspatial::cart_2d const& D) +{ + return std::min(std::min(point_to_segment_distance(A, C, D), point_to_segment_distance(B, C, D)), + std::min(point_to_segment_distance(C, A, B), point_to_segment_distance(D, A, B))); +} + +/** + * @brief Computes shortest distance between two segments. + * + * If two segment intersects, distance is 0. + */ +template +double __device__ segment_distance(cuspatial::cart_2d const& A, + cuspatial::cart_2d const& B, + cuspatial::cart_2d const& C, + cuspatial::cart_2d const& D) +{ + // Subject 1.03 of https://www.inf.pucrs.br/~pinho/CG/faq.html + // Construct a parametrized ray of AB and CD, solve for the parameters. + // If both parameters are within [0, 1], the intersection exists. + + double r_denom = (B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x); + double r_numer = (A.y - C.y) * (D.x - C.x) - (A.x - C.x) * (D.y - C.y); + if (r_denom == 0) { + if (r_numer == 0) { return 0.0; } // Segments coincides + // Segments parallel + return segment_distance_no_intersect(A, B, C, D); + } + double r = r_numer / r_denom; + double s = ((A.y - C.y) * (B.x - A.x) - (A.x - C.x) * (B.y - A.x)) / + ((B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x)); + if (r >= 0 and r <= 1 and s >= 0 and s <= 1) { return 0.0; } + return segment_distance_no_intersect(A, B, C, D); +} + +template ::value_type> +void __global__ kernel(OffsetIterator linestring1_offsets_begin, + OffsetIterator linestring1_offsets_end, + Cart2dItA linestring1_points_begin, + Cart2dItA linestring1_points_end, + OffsetIterator linestring2_offsets_begin, + Cart2dItB linestring2_points_begin, + Cart2dItB linestring2_points_end, + OutputIterator min_distances) +{ + auto const p1Idx = threadIdx.x + blockIdx.x * blockDim.x; + cudf::size_type const num_linestrings = + thrust::distance(linestring1_offsets_begin, linestring1_offsets_end); + cudf::size_type const linestring1_num_points = + thrust::distance(linestring1_points_begin, linestring1_points_end); + cudf::size_type const linestring2_num_points = + thrust::distance(linestring2_points_begin, linestring2_points_end); + + if (p1Idx >= linestring1_num_points) { return; } + + cudf::size_type const linestring_idx = + thrust::distance( + linestring1_offsets_begin, + thrust::upper_bound(thrust::seq, linestring1_offsets_begin, linestring1_offsets_end, p1Idx)) - + 1; + + cudf::size_type ls1End = + (linestring_idx == (num_linestrings - 1) ? (linestring1_num_points) + : *(linestring1_offsets_begin + linestring_idx + 1)) - + 1; + + if (p1Idx == ls1End) { + // Current point is the end point of the line string. + return; + } + + cudf::size_type ls2Start = *(linestring2_offsets_begin + linestring_idx); + cudf::size_type ls2End = + (linestring_idx == (num_linestrings - 1) ? linestring2_num_points + : *(linestring2_offsets_begin + linestring_idx + 1)) - + 1; + + cuspatial::cart_2d const& A = linestring1_points_begin[p1Idx]; + cuspatial::cart_2d const& B = linestring1_points_begin[p1Idx + 1]; + + double min_distance = std::numeric_limits::max(); + for (cudf::size_type p2Idx = ls2Start; p2Idx < ls2End; p2Idx++) { + cuspatial::cart_2d const& C = linestring2_points_begin[p2Idx]; + cuspatial::cart_2d const& D = linestring2_points_begin[p2Idx + 1]; + min_distance = std::min(min_distance, segment_distance(A, B, C, D)); + } + atomicMin(min_distances + linestring_idx, static_cast(min_distance)); +} + + +} // namespace detail + +/** + * @brief Check if every linestring in the input contains at least 2 end points. + */ +template +bool validate_linestring(OffsetIterator linestring_offsets_first, + cudf::size_type num_linestring_pairs, + Cart2dIt linestring_points_x_first, + cudf::size_type num_points, + rmm::cuda_stream_view stream) +{ + if (num_linestring_pairs == 1) { + return num_points >= 2; + } + + return thrust::reduce( + rmm::exec_policy(stream), + thrust::make_counting_iterator(cudf::size_type{0}), + thrust::make_counting_iterator(num_linestring_pairs), + true, + [linestring_offsets_first, + num_linestring_pairs, + num_points] __device__(bool prev, cudf::size_type i) { + cudf::size_type begin = linestring_offsets_first[i]; + cudf::size_type end = i == num_linestring_pairs ? num_points : linestring_offsets_first[i + 1]; + return prev && (end - begin); + }); +} + +template + void pairwise_linestring_distance( + OffsetIterator linestring1_offsets_first, + OffsetIterator linestring1_offsets_last, + Cart2dItA linestring1_points_first, + Cart2dItA linestring1_points_last, + OffsetIterator linestring2_offsets_first, + Cart2dItB linestring2_points_first, + Cart2dItB linestring2_points_last, + OutputIt distances_first, + rmm::cuda_stream_view stream) { + using Cart2dB = typename std::iterator_traits::value_type; + static_assert( + detail::is_same(), "Inputs must be cuspatial::cart_2d" + ); + static_assert( + detail::is_floating_point::value_type>(), + "Inputs must be floating point types." + ); + + auto const num_string_pairs = thrust::distance(linestring1_offsets_first, linestring1_offsets_last); + auto const num_linestring1_points = thrust::distance(linestring1_points_first, linestring1_points_last); + auto const num_linestring2_points = thrust::distance(linestring2_points_first, linestring2_points_last); + + CUSPATIAL_EXPECTS(validate_linestring( + linestring1_offsets_first, num_string_pairs, linestring1_points_first, num_linestring1_points, stream), + "Each item of linestring1 should contain at least 2 end points."); + CUSPATIAL_EXPECTS(validate_linestring( + linestring2_offsets_first, num_string_pairs, linestring2_points_first, num_linestring2_points, stream), + "Each item of linestring2 should contain at least 2 end points."); + + + thrust::fill(rmm::exec_policy(stream), + distances_first, + distances_first + num_string_pairs, + std::numeric_limits::max()); + + std::size_t const threads_per_block = 64; + std::size_t const num_blocks = (num_linestring1_points + threads_per_block - 1) / threads_per_block; + + kernel<<>>( + linestring1_offsets_first, + linestring1_offsets_last, + linestring1_points_first, + linestring1_points_last, + linestring2_offsets_first, + linestring2_points_first, + linestring2_points_last, + distances_first + ); + + CUSPATIAL_CUDA_TRY(cudaGetLastError()); + } + +} // namespace cuspatial diff --git a/cpp/include/cuspatial/experimental/linestring_distance.cuh b/cpp/include/cuspatial/experimental/linestring_distance.cuh new file mode 100644 index 000000000..a936b8a7c --- /dev/null +++ b/cpp/include/cuspatial/experimental/linestring_distance.cuh @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#pragma once + +#include + +namespace cuspatial { + +template::value_type, + class T = typename Cart2d::value_type> + void pairwise_linestring_distance( + OffsetIterator linestring1_offsets_first, + OffsetIterator linestring1_offsets_last, + Cart2dItA linestring1_points_first, + Cart2dItA linestring1_points_last, + OffsetIterator linestring2_offsets_first, + Cart2dItB linestring2_points_first, + Cart2dItB linestring2_points_last, + OutputIt distances_first, + rmm::cuda_stream_view stream); + +} + +#include "detail/linestring_distance.cuh" diff --git a/cpp/include/cuspatial/experimental/type_utils.hpp b/cpp/include/cuspatial/experimental/type_utils.hpp new file mode 100644 index 000000000..eb02c388c --- /dev/null +++ b/cpp/include/cuspatial/experimental/type_utils.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include + +namespace cuspatial { + +namespace detail { + +template +struct tuple_to_coord_2d { + __device__ CoordType operator()(thrust::tuple pos) + { + static_assert(std::is_base_of_v, CoordType>(), + "Can only convert to coord_2d type."); + return CoordType{thrust::get<0>(pos), thrust::get<1>(pos)}; + } +}; + +} // namespace detail + +template +auto make_coord_2d_iterator(FirstIter first, SecondIter second) +{ + using T = typename std::iterator_traits::value_type; + static_assert(std::is_same_v::value_type>, + "Iterator value_type mismatch"); + + auto zipped = thrust::make_zip_iterator(thrust::make_tuple(first, second)); + return thrust::make_transform_iterator(zipped, detail::tuple_to_coord_2d()); +} + +} // namespace cuspatial diff --git a/cpp/include/cuspatial/types.hpp b/cpp/include/cuspatial/types.hpp index bf1ca5bc9..dffd796a1 100644 --- a/cpp/include/cuspatial/types.hpp +++ b/cpp/include/cuspatial/types.hpp @@ -31,16 +31,26 @@ struct location_3d { }; /** - * @brief A 2D Cartesian location (x, y) + * @brief A 2D coordinate (x, y) * * @tparam T the base type for the coordinates */ template -struct coord_2d { +struct alignas(2 * sizeof(T)) coord_2d { + using value_type = T; T x; T y; }; +// Longtitude/Latitude coordinates +template +struct lonlat_2d : coord_2d { +}; + +// Cartesian coordinates +template +struct cart_2d : coord_2d { +}; /** * @brief A timestamp * diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index 0805e2bd2..af9796d90 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -18,6 +18,8 @@ #include #include +#include +#include #include #include @@ -37,197 +39,7 @@ #include #include -#ifndef DEBUG -// #define DEBUG 1 -#endif - -template -void __device__ print(cuspatial::coord_2d const& point) -{ - printf("POINT (%f, %f)\n", point.x, point.y); -} - -template -void __device__ print(cuspatial::coord_2d const& A, cuspatial::coord_2d const& B) -{ - printf("SEGMENT (%f, %f) -> (%f, %f)\n", A.x, A.y, B.x, B.y); -} - namespace cuspatial { -namespace { - -template -double __device__ point_to_segment_distance(coord_2d const& C, - coord_2d const& A, - coord_2d const& B) -{ - // Subject 1.02 of https://www.inf.pucrs.br/~pinho/CG/faq.html - // Project the point to the segment, if it lands on the segment, - // the distance is the length of proejction, otherwise it's the - // length to one of the end points. - - double L_squared = (A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y); - if (L_squared == 0) { return hypot(C.x - A.x, C.y - A.y); } - double r = ((C.x - A.x) * (B.x - A.x) + (C.y - A.y) * (B.y - A.y)) / L_squared; -#ifdef DEBUG - printf("\t r=%f\n", r); -#endif - if (r <= 0 or r >= 1) { - return std::min(hypot(C.x - A.x, C.y - A.y), hypot(C.x - B.x, C.y - B.y)); - } - double Px = A.x + r * (B.x - A.x); - double Py = A.y + r * (B.y - A.y); - return hypot(C.x - Px, C.y - Py); -} - -template -double __device__ segment_distance_no_intersect(coord_2d const& A, - coord_2d const& B, - coord_2d const& C, - coord_2d const& D) -{ -#ifdef DEBUG - printf("From: \n"); - print(A); - printf("To: \n"); - print(C, D); - printf("Distance %f\n", point_to_segment_distance(A, C, D)); - - printf("From: \n"); - print(B); - printf("To: \n"); - print(C, D); - printf("Distance %f\n", point_to_segment_distance(B, C, D)); - - printf("From: \n"); - print(C); - printf("To: \n"); - print(A, B); - printf("Distance %f\n", point_to_segment_distance(C, A, B)); - - printf("From: \n"); - print(D); - printf("To: \n"); - print(A, B); - printf("Distance %f\n", point_to_segment_distance(D, A, B)); -#endif - return std::min(std::min(point_to_segment_distance(A, C, D), point_to_segment_distance(B, C, D)), - std::min(point_to_segment_distance(C, A, B), point_to_segment_distance(D, A, B))); -} - -/** - * @brief Computes shortest distance between two segments. - * - * If two segment intersects, distance is 0. - */ -template -double __device__ segment_distance(coord_2d const& A, - coord_2d const& B, - coord_2d const& C, - coord_2d const& D) -{ - // Subject 1.03 of https://www.inf.pucrs.br/~pinho/CG/faq.html - // Construct a parametrized ray of AB and CD, solve for the parameters. - // If both parameters are within [0, 1], the intersection exists. - - double r_denom = (B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x); - double r_numer = (A.y - C.y) * (D.x - C.x) - (A.x - C.x) * (D.y - C.y); - if (r_denom == 0) { - if (r_numer == 0) { return 0.0; } // Segments coincides - // Segments parallel - return segment_distance_no_intersect(A, B, C, D); - } - double r = r_numer / r_denom; - double s = ((A.y - C.y) * (B.x - A.x) - (A.x - C.x) * (B.y - A.x)) / - ((B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x)); - if (r >= 0 and r <= 1 and s >= 0 and s <= 1) { return 0.0; } - return segment_distance_no_intersect(A, B, C, D); -} - -template -void __global__ kernel(OffsetIterator linestring1_offsets_begin, - OffsetIterator linestring1_offsets_end, - CoordinateIterator linestring1_points_xs_begin, - CoordinateIterator linestring1_points_xs_end, - CoordinateIterator linestring1_points_ys_begin, - OffsetIterator linestring2_offsets_begin, - CoordinateIterator linestring2_points_xs_begin, - CoordinateIterator linestring2_points_xs_end, - CoordinateIterator linestring2_points_ys_begin, - OutputIterator min_distances) -{ - using T = typename std::iterator_traits::value_type; - - auto const p1Idx = threadIdx.x + blockIdx.x * blockDim.x; - cudf::size_type const num_linestrings = - thrust::distance(linestring1_offsets_begin, linestring1_offsets_end); - cudf::size_type const linestring1_num_points = - thrust::distance(linestring1_points_xs_begin, linestring1_points_xs_end); - cudf::size_type const linestring2_num_points = - thrust::distance(linestring2_points_xs_begin, linestring2_points_xs_end); - -#ifdef DEBUG - printf("p1Idx: %d\n", p1Idx); - printf("linestring1_num_points: %d\n", linestring1_num_points); -#endif - - if (p1Idx >= linestring1_num_points) { return; } - - cudf::size_type const linestring_idx = - thrust::distance( - linestring1_offsets_begin, - thrust::upper_bound(thrust::seq, linestring1_offsets_begin, linestring1_offsets_end, p1Idx)) - - 1; - - cudf::size_type ls1End = - (linestring_idx == (num_linestrings - 1) ? (linestring1_num_points) - : *(linestring1_offsets_begin + linestring_idx + 1)) - - 1; - -#ifdef DEBUG - printf("p1Idx: %d\n", p1Idx); - printf("ls1End: %d\n", ls1End); - printf("linestring_idx: %d\n", linestring_idx); - printf("num_linestrings: %d\n", num_linestrings); -#endif - if (p1Idx == ls1End) { - // Current point is the end point of the line string. - return; - } - - cudf::size_type ls2Start = *(linestring2_offsets_begin + linestring_idx); - cudf::size_type ls2End = - (linestring_idx == (num_linestrings - 1) ? linestring2_num_points - : *(linestring2_offsets_begin + linestring_idx + 1)) - - 1; - - coord_2d A{linestring1_points_xs_begin[p1Idx], linestring1_points_ys_begin[p1Idx]}; - coord_2d B{linestring1_points_xs_begin[p1Idx + 1], linestring1_points_ys_begin[p1Idx + 1]}; - -#ifdef DEBUG - printf("linestring_idx: %d\n", linestring_idx); - printf("num_linestrings: %d\n", num_linestrings); - printf("linestring1_num_points: %d\n", linestring1_num_points); - printf("linestring2_num_points: %d\n", linestring2_num_points); - printf("p1Idx: %d\n", p1Idx); - printf("ls2Start: %d\n", ls2Start); - printf("ls2End: %d\n", ls2End); - print(A, B); -#endif - double min_distance = std::numeric_limits::max(); - for (cudf::size_type p2Idx = ls2Start; p2Idx < ls2End; p2Idx++) { - coord_2d C{linestring2_points_xs_begin[p2Idx], linestring2_points_ys_begin[p2Idx]}; - coord_2d D{linestring2_points_xs_begin[p2Idx + 1], linestring2_points_ys_begin[p2Idx + 1]}; -#ifdef DEBUG - print(C, D); -#endif - min_distance = std::min(min_distance, segment_distance(A, B, C, D)); - } - atomicMin(min_distances + linestring_idx, static_cast(min_distance)); -} - -} // anonymous namespace - namespace detail { struct pariwise_linestring_distance_functor { template @@ -241,55 +53,25 @@ struct pariwise_linestring_distance_functor { rmm::cuda_stream_view stream, rmm::mr::device_memory_resource* mr) { - using namespace cudf; - - auto const num_string_pairs = static_cast(linestring1_offsets.size()); + auto const num_string_pairs = static_cast(linestring1_offsets.size()); auto min_distances = - make_numeric_column(data_type{type_to_id()}, num_string_pairs, mask_state::UNALLOCATED); - - // auto functor = linestirngs_pairs_min_distance_functor(num_string_pairs, - // linestring1_points_x.size(), - // linestring2_points_x.size(), - // linestring1_offsets.begin(), - // linestring1_points_x.begin(), - // linestring1_points_y.begin(), - // linestring2_offsets.begin(), - // linestring2_points_x.begin(), - // linestring2_points_y.begin()); - - // thrust::transform(rmm::exec_policy(stream), - // thrust::make_counting_iterator(0), - // thrust::make_counting_iterator(num_string_pairs), - // min_distances->mutable_view().begin(), - // functor); - - thrust::fill(rmm::exec_policy(stream), - min_distances->mutable_view().begin(), - min_distances->mutable_view().end(), - std::numeric_limits::max()); - - std::size_t const threads_per_block = 64; - std::size_t const num_blocks = - (linestring1_points_x.size() + threads_per_block - 1) / threads_per_block; - -#ifdef DEBUG - std::cout << "number of strings: " << num_string_pairs << std::endl; - std::cout << "num blocks" << num_blocks << std::endl; -#endif - kernel<<>>( - linestring1_offsets.begin(), - linestring1_offsets.end(), - linestring1_points_x.begin(), - linestring1_points_x.end(), - linestring1_points_y.begin(), - linestring2_offsets.begin(), - linestring2_points_x.begin(), - linestring2_points_x.end(), - linestring2_points_y.begin(), - min_distances->mutable_view().begin()); - - CUSPATIAL_CUDA_TRY(cudaGetLastError()); + cudf::make_numeric_column(cudf::data_type{cudf::type_to_id()}, num_string_pairs, cudf::mask_state::UNALLOCATED, stream, mr); + + auto linestring1_coords_it = make_coord_2d_iterator>(linestring1_points_x.begin(), linestring1_points_y.begin()); + auto linestring2_coords_it = make_coord_2d_iterator>(linestring2_points_x.begin(), linestring2_points_y.begin()); + + pairwise_linestring_distance( + linestring1_offsets.begin(), + linestring1_offsets.end(), + linestring1_coords_it, + linestring1_coords_it + linestring1_points_x.size(), + linestring2_offsets.begin(), + linestring2_coords_it, + linestring2_coords_it + linestring2_points_x.size(), + min_distances->mutable_view().begin(), + stream + ); return min_distances; } @@ -302,29 +84,6 @@ struct pariwise_linestring_distance_functor { } }; -/** - * @brief Check if every linestring in the input contains at least 2 end points. - */ -bool validate_linestring(cudf::device_span linestring_offsets, - cudf::column_view const& linestring_points_x, - rmm::cuda_stream_view stream) -{ - if (linestring_offsets.size() == 1) { return linestring_points_x.size() >= 2; } - return thrust::reduce( - rmm::exec_policy(stream), - thrust::make_counting_iterator(cudf::size_type{0}), - thrust::make_counting_iterator(static_cast(linestring_offsets.size())), - true, - [offsets = linestring_offsets.begin(), - num_offsets = static_cast(linestring_offsets.size()), - num_points = static_cast( - linestring_points_x.size())] __device__(bool prev, cudf::size_type i) { - cudf::size_type begin = offsets[i]; - cudf::size_type end = i == num_offsets ? num_points : offsets[i + 1]; - return prev && (end - begin); - }); -} - std::unique_ptr pairwise_linestring_distance( cudf::device_span linestring1_offsets, cudf::column_view const& linestring1_points_x, @@ -349,11 +108,6 @@ std::unique_ptr pairwise_linestring_distance( if (linestring1_offsets.size() == 0) { return cudf::empty_like(linestring1_points_x); } - CUSPATIAL_EXPECTS(validate_linestring(linestring1_offsets, linestring1_points_x, stream), - "Each item of linestring1 should contain at least 2 end points."); - CUSPATIAL_EXPECTS(validate_linestring(linestring2_offsets, linestring2_points_x, stream), - "Each item of linestring2 should contain at least 2 end points."); - return cudf::type_dispatcher(linestring1_points_x.type(), pariwise_linestring_distance_functor{}, linestring1_offsets, From 2006ac14cc616330193bc076fdf553eb4c622b6f Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 14 Apr 2022 09:33:32 -0700 Subject: [PATCH 17/67] passes compilation --- .../detail/linestring_distance.cuh | 180 +++++++++--------- .../experimental/linestring_distance.cuh | 34 ++-- .../cuspatial/experimental/type_utils.hpp | 3 +- 3 files changed, 110 insertions(+), 107 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/linestring_distance.cuh b/cpp/include/cuspatial/experimental/detail/linestring_distance.cuh index e81a80678..923ffdf5f 100644 --- a/cpp/include/cuspatial/experimental/detail/linestring_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/linestring_distance.cuh @@ -32,17 +32,19 @@ #include #include -namespace cuspatital { +namespace cuspatial { namespace detail { -template -constexpr bool is_same() { - return std::conjunction_v...>; +template +constexpr bool is_same() +{ + return std::conjunction_v...>; } -template -constexpr bool is_floating_point() { - return std::conjunction_v...>; +template +constexpr bool is_floating_point() +{ + return std::conjunction_v...>; } template @@ -109,7 +111,8 @@ template ::value_type> + typename Cart2d = typename std::iterator_traits::value_type, + typename T = typename Cart2d::value_type> void __global__ kernel(OffsetIterator linestring1_offsets_begin, OffsetIterator linestring1_offsets_end, Cart2dItA linestring1_points_begin, @@ -158,99 +161,102 @@ void __global__ kernel(OffsetIterator linestring1_offsets_begin, for (cudf::size_type p2Idx = ls2Start; p2Idx < ls2End; p2Idx++) { cuspatial::cart_2d const& C = linestring2_points_begin[p2Idx]; cuspatial::cart_2d const& D = linestring2_points_begin[p2Idx + 1]; - min_distance = std::min(min_distance, segment_distance(A, B, C, D)); + min_distance = std::min(min_distance, segment_distance(A, B, C, D)); } atomicMin(min_distances + linestring_idx, static_cast(min_distance)); } - -} // namespace detail +} // namespace detail /** * @brief Check if every linestring in the input contains at least 2 end points. */ -template +template bool validate_linestring(OffsetIterator linestring_offsets_first, cudf::size_type num_linestring_pairs, Cart2dIt linestring_points_x_first, cudf::size_type num_points, rmm::cuda_stream_view stream) { - if (num_linestring_pairs == 1) { - return num_points >= 2; - } - - return thrust::reduce( - rmm::exec_policy(stream), - thrust::make_counting_iterator(cudf::size_type{0}), - thrust::make_counting_iterator(num_linestring_pairs), - true, - [linestring_offsets_first, - num_linestring_pairs, - num_points] __device__(bool prev, cudf::size_type i) { - cudf::size_type begin = linestring_offsets_first[i]; - cudf::size_type end = i == num_linestring_pairs ? num_points : linestring_offsets_first[i + 1]; - return prev && (end - begin); - }); + if (num_linestring_pairs == 1) { return num_points >= 2; } + + return thrust::reduce(rmm::exec_policy(stream), + thrust::make_counting_iterator(cudf::size_type{0}), + thrust::make_counting_iterator(num_linestring_pairs), + true, + [linestring_offsets_first, num_linestring_pairs, num_points] __device__( + bool prev, cudf::size_type i) { + cudf::size_type begin = linestring_offsets_first[i]; + cudf::size_type end = i == num_linestring_pairs + ? num_points + : linestring_offsets_first[i + 1]; + return prev && (end - begin); + }); } -template - void pairwise_linestring_distance( - OffsetIterator linestring1_offsets_first, - OffsetIterator linestring1_offsets_last, - Cart2dItA linestring1_points_first, - Cart2dItA linestring1_points_last, - OffsetIterator linestring2_offsets_first, - Cart2dItB linestring2_points_first, - Cart2dItB linestring2_points_last, - OutputIt distances_first, - rmm::cuda_stream_view stream) { - using Cart2dB = typename std::iterator_traits::value_type; - static_assert( - detail::is_same(), "Inputs must be cuspatial::cart_2d" - ); - static_assert( - detail::is_floating_point::value_type>(), - "Inputs must be floating point types." - ); - - auto const num_string_pairs = thrust::distance(linestring1_offsets_first, linestring1_offsets_last); - auto const num_linestring1_points = thrust::distance(linestring1_points_first, linestring1_points_last); - auto const num_linestring2_points = thrust::distance(linestring2_points_first, linestring2_points_last); - - CUSPATIAL_EXPECTS(validate_linestring( - linestring1_offsets_first, num_string_pairs, linestring1_points_first, num_linestring1_points, stream), - "Each item of linestring1 should contain at least 2 end points."); - CUSPATIAL_EXPECTS(validate_linestring( - linestring2_offsets_first, num_string_pairs, linestring2_points_first, num_linestring2_points, stream), - "Each item of linestring2 should contain at least 2 end points."); - - - thrust::fill(rmm::exec_policy(stream), - distances_first, - distances_first + num_string_pairs, - std::numeric_limits::max()); - - std::size_t const threads_per_block = 64; - std::size_t const num_blocks = (num_linestring1_points + threads_per_block - 1) / threads_per_block; - - kernel<<>>( - linestring1_offsets_first, - linestring1_offsets_last, - linestring1_points_first, - linestring1_points_last, - linestring2_offsets_first, - linestring2_points_first, - linestring2_points_last, - distances_first - ); - - CUSPATIAL_CUDA_TRY(cudaGetLastError()); - } +template +void pairwise_linestring_distance(OffsetIterator linestring1_offsets_first, + OffsetIterator linestring1_offsets_last, + Cart2dItA linestring1_points_first, + Cart2dItA linestring1_points_last, + OffsetIterator linestring2_offsets_first, + Cart2dItB linestring2_points_first, + Cart2dItB linestring2_points_last, + OutputIt distances_first, + rmm::cuda_stream_view stream) +{ + using Cart2dB = typename std::iterator_traits::value_type; + static_assert(detail::is_same, Cart2d, Cart2dB>(), + "Inputs must be cuspatial::cart_2d"); + static_assert(detail::is_floating_point::value_type>(), + "Inputs must be floating point types."); + + auto const num_string_pairs = + thrust::distance(linestring1_offsets_first, linestring1_offsets_last); + auto const num_linestring1_points = + thrust::distance(linestring1_points_first, linestring1_points_last); + auto const num_linestring2_points = + thrust::distance(linestring2_points_first, linestring2_points_last); + + CUSPATIAL_EXPECTS(validate_linestring(linestring1_offsets_first, + num_string_pairs, + linestring1_points_first, + num_linestring1_points, + stream), + "Each item of linestring1 should contain at least 2 end points."); + CUSPATIAL_EXPECTS(validate_linestring(linestring2_offsets_first, + num_string_pairs, + linestring2_points_first, + num_linestring2_points, + stream), + "Each item of linestring2 should contain at least 2 end points."); + + thrust::fill(rmm::exec_policy(stream), + distances_first, + distances_first + num_string_pairs, + std::numeric_limits::max()); + + std::size_t const threads_per_block = 64; + std::size_t const num_blocks = + (num_linestring1_points + threads_per_block - 1) / threads_per_block; + + kernel<<>>(linestring1_offsets_first, + linestring1_offsets_last, + linestring1_points_first, + linestring1_points_last, + linestring2_offsets_first, + linestring2_points_first, + linestring2_points_last, + distances_first); + + CUSPATIAL_CUDA_TRY(cudaGetLastError()); +} -} // namespace cuspatial +} // namespace cuspatial diff --git a/cpp/include/cuspatial/experimental/linestring_distance.cuh b/cpp/include/cuspatial/experimental/linestring_distance.cuh index a936b8a7c..460b05c52 100644 --- a/cpp/include/cuspatial/experimental/linestring_distance.cuh +++ b/cpp/include/cuspatial/experimental/linestring_distance.cuh @@ -14,30 +14,28 @@ * limitations under the License. */ - #pragma once #include namespace cuspatial { -template::value_type, - class T = typename Cart2d::value_type> - void pairwise_linestring_distance( - OffsetIterator linestring1_offsets_first, - OffsetIterator linestring1_offsets_last, - Cart2dItA linestring1_points_first, - Cart2dItA linestring1_points_last, - OffsetIterator linestring2_offsets_first, - Cart2dItB linestring2_points_first, - Cart2dItB linestring2_points_last, - OutputIt distances_first, - rmm::cuda_stream_view stream); +template ::value_type, + class T = typename Cart2d::value_type> +void pairwise_linestring_distance(OffsetIterator linestring1_offsets_first, + OffsetIterator linestring1_offsets_last, + Cart2dItA linestring1_points_first, + Cart2dItA linestring1_points_last, + OffsetIterator linestring2_offsets_first, + Cart2dItB linestring2_points_first, + Cart2dItB linestring2_points_last, + OutputIt distances_first, + rmm::cuda_stream_view stream); } -#include "detail/linestring_distance.cuh" +#include diff --git a/cpp/include/cuspatial/experimental/type_utils.hpp b/cpp/include/cuspatial/experimental/type_utils.hpp index eb02c388c..ba33bfcb5 100644 --- a/cpp/include/cuspatial/experimental/type_utils.hpp +++ b/cpp/include/cuspatial/experimental/type_utils.hpp @@ -30,8 +30,7 @@ template struct tuple_to_coord_2d { __device__ CoordType operator()(thrust::tuple pos) { - static_assert(std::is_base_of_v, CoordType>(), - "Can only convert to coord_2d type."); + static_assert(std::is_base_of_v, CoordType>, "Can only convert to coord_2d type."); return CoordType{thrust::get<0>(pos), thrust::get<1>(pos)}; } }; From f463bd809851f7f58987bb4581f76abc1caa1bbe Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 14 Apr 2022 11:11:47 -0700 Subject: [PATCH 18/67] Revert "passes compilation" This reverts commit 2006ac14cc616330193bc076fdf553eb4c622b6f. --- .../detail/linestring_distance.cuh | 180 +++++++++--------- .../experimental/linestring_distance.cuh | 34 ++-- .../cuspatial/experimental/type_utils.hpp | 3 +- 3 files changed, 107 insertions(+), 110 deletions(-) diff --git a/cpp/include/cuspatial/experimental/detail/linestring_distance.cuh b/cpp/include/cuspatial/experimental/detail/linestring_distance.cuh index 923ffdf5f..e81a80678 100644 --- a/cpp/include/cuspatial/experimental/detail/linestring_distance.cuh +++ b/cpp/include/cuspatial/experimental/detail/linestring_distance.cuh @@ -32,19 +32,17 @@ #include #include -namespace cuspatial { +namespace cuspatital { namespace detail { -template -constexpr bool is_same() -{ - return std::conjunction_v...>; +template +constexpr bool is_same() { + return std::conjunction_v...>; } -template -constexpr bool is_floating_point() -{ - return std::conjunction_v...>; +template +constexpr bool is_floating_point() { + return std::conjunction_v...>; } template @@ -111,8 +109,7 @@ template ::value_type, - typename T = typename Cart2d::value_type> + typename T = typename std::iterator_traits::value_type> void __global__ kernel(OffsetIterator linestring1_offsets_begin, OffsetIterator linestring1_offsets_end, Cart2dItA linestring1_points_begin, @@ -161,102 +158,99 @@ void __global__ kernel(OffsetIterator linestring1_offsets_begin, for (cudf::size_type p2Idx = ls2Start; p2Idx < ls2End; p2Idx++) { cuspatial::cart_2d const& C = linestring2_points_begin[p2Idx]; cuspatial::cart_2d const& D = linestring2_points_begin[p2Idx + 1]; - min_distance = std::min(min_distance, segment_distance(A, B, C, D)); + min_distance = std::min(min_distance, segment_distance(A, B, C, D)); } atomicMin(min_distances + linestring_idx, static_cast(min_distance)); } -} // namespace detail + +} // namespace detail /** * @brief Check if every linestring in the input contains at least 2 end points. */ -template +template bool validate_linestring(OffsetIterator linestring_offsets_first, cudf::size_type num_linestring_pairs, Cart2dIt linestring_points_x_first, cudf::size_type num_points, rmm::cuda_stream_view stream) { - if (num_linestring_pairs == 1) { return num_points >= 2; } - - return thrust::reduce(rmm::exec_policy(stream), - thrust::make_counting_iterator(cudf::size_type{0}), - thrust::make_counting_iterator(num_linestring_pairs), - true, - [linestring_offsets_first, num_linestring_pairs, num_points] __device__( - bool prev, cudf::size_type i) { - cudf::size_type begin = linestring_offsets_first[i]; - cudf::size_type end = i == num_linestring_pairs - ? num_points - : linestring_offsets_first[i + 1]; - return prev && (end - begin); - }); -} + if (num_linestring_pairs == 1) { + return num_points >= 2; + } -template -void pairwise_linestring_distance(OffsetIterator linestring1_offsets_first, - OffsetIterator linestring1_offsets_last, - Cart2dItA linestring1_points_first, - Cart2dItA linestring1_points_last, - OffsetIterator linestring2_offsets_first, - Cart2dItB linestring2_points_first, - Cart2dItB linestring2_points_last, - OutputIt distances_first, - rmm::cuda_stream_view stream) -{ - using Cart2dB = typename std::iterator_traits::value_type; - static_assert(detail::is_same, Cart2d, Cart2dB>(), - "Inputs must be cuspatial::cart_2d"); - static_assert(detail::is_floating_point::value_type>(), - "Inputs must be floating point types."); - - auto const num_string_pairs = - thrust::distance(linestring1_offsets_first, linestring1_offsets_last); - auto const num_linestring1_points = - thrust::distance(linestring1_points_first, linestring1_points_last); - auto const num_linestring2_points = - thrust::distance(linestring2_points_first, linestring2_points_last); - - CUSPATIAL_EXPECTS(validate_linestring(linestring1_offsets_first, - num_string_pairs, - linestring1_points_first, - num_linestring1_points, - stream), - "Each item of linestring1 should contain at least 2 end points."); - CUSPATIAL_EXPECTS(validate_linestring(linestring2_offsets_first, - num_string_pairs, - linestring2_points_first, - num_linestring2_points, - stream), - "Each item of linestring2 should contain at least 2 end points."); - - thrust::fill(rmm::exec_policy(stream), - distances_first, - distances_first + num_string_pairs, - std::numeric_limits::max()); - - std::size_t const threads_per_block = 64; - std::size_t const num_blocks = - (num_linestring1_points + threads_per_block - 1) / threads_per_block; - - kernel<<>>(linestring1_offsets_first, - linestring1_offsets_last, - linestring1_points_first, - linestring1_points_last, - linestring2_offsets_first, - linestring2_points_first, - linestring2_points_last, - distances_first); - - CUSPATIAL_CUDA_TRY(cudaGetLastError()); + return thrust::reduce( + rmm::exec_policy(stream), + thrust::make_counting_iterator(cudf::size_type{0}), + thrust::make_counting_iterator(num_linestring_pairs), + true, + [linestring_offsets_first, + num_linestring_pairs, + num_points] __device__(bool prev, cudf::size_type i) { + cudf::size_type begin = linestring_offsets_first[i]; + cudf::size_type end = i == num_linestring_pairs ? num_points : linestring_offsets_first[i + 1]; + return prev && (end - begin); + }); } -} // namespace cuspatial +template + void pairwise_linestring_distance( + OffsetIterator linestring1_offsets_first, + OffsetIterator linestring1_offsets_last, + Cart2dItA linestring1_points_first, + Cart2dItA linestring1_points_last, + OffsetIterator linestring2_offsets_first, + Cart2dItB linestring2_points_first, + Cart2dItB linestring2_points_last, + OutputIt distances_first, + rmm::cuda_stream_view stream) { + using Cart2dB = typename std::iterator_traits::value_type; + static_assert( + detail::is_same(), "Inputs must be cuspatial::cart_2d" + ); + static_assert( + detail::is_floating_point::value_type>(), + "Inputs must be floating point types." + ); + + auto const num_string_pairs = thrust::distance(linestring1_offsets_first, linestring1_offsets_last); + auto const num_linestring1_points = thrust::distance(linestring1_points_first, linestring1_points_last); + auto const num_linestring2_points = thrust::distance(linestring2_points_first, linestring2_points_last); + + CUSPATIAL_EXPECTS(validate_linestring( + linestring1_offsets_first, num_string_pairs, linestring1_points_first, num_linestring1_points, stream), + "Each item of linestring1 should contain at least 2 end points."); + CUSPATIAL_EXPECTS(validate_linestring( + linestring2_offsets_first, num_string_pairs, linestring2_points_first, num_linestring2_points, stream), + "Each item of linestring2 should contain at least 2 end points."); + + + thrust::fill(rmm::exec_policy(stream), + distances_first, + distances_first + num_string_pairs, + std::numeric_limits::max()); + + std::size_t const threads_per_block = 64; + std::size_t const num_blocks = (num_linestring1_points + threads_per_block - 1) / threads_per_block; + + kernel<<>>( + linestring1_offsets_first, + linestring1_offsets_last, + linestring1_points_first, + linestring1_points_last, + linestring2_offsets_first, + linestring2_points_first, + linestring2_points_last, + distances_first + ); + + CUSPATIAL_CUDA_TRY(cudaGetLastError()); + } + +} // namespace cuspatial diff --git a/cpp/include/cuspatial/experimental/linestring_distance.cuh b/cpp/include/cuspatial/experimental/linestring_distance.cuh index 460b05c52..a936b8a7c 100644 --- a/cpp/include/cuspatial/experimental/linestring_distance.cuh +++ b/cpp/include/cuspatial/experimental/linestring_distance.cuh @@ -14,28 +14,30 @@ * limitations under the License. */ + #pragma once #include namespace cuspatial { -template ::value_type, - class T = typename Cart2d::value_type> -void pairwise_linestring_distance(OffsetIterator linestring1_offsets_first, - OffsetIterator linestring1_offsets_last, - Cart2dItA linestring1_points_first, - Cart2dItA linestring1_points_last, - OffsetIterator linestring2_offsets_first, - Cart2dItB linestring2_points_first, - Cart2dItB linestring2_points_last, - OutputIt distances_first, - rmm::cuda_stream_view stream); +template::value_type, + class T = typename Cart2d::value_type> + void pairwise_linestring_distance( + OffsetIterator linestring1_offsets_first, + OffsetIterator linestring1_offsets_last, + Cart2dItA linestring1_points_first, + Cart2dItA linestring1_points_last, + OffsetIterator linestring2_offsets_first, + Cart2dItB linestring2_points_first, + Cart2dItB linestring2_points_last, + OutputIt distances_first, + rmm::cuda_stream_view stream); } -#include +#include "detail/linestring_distance.cuh" diff --git a/cpp/include/cuspatial/experimental/type_utils.hpp b/cpp/include/cuspatial/experimental/type_utils.hpp index ba33bfcb5..eb02c388c 100644 --- a/cpp/include/cuspatial/experimental/type_utils.hpp +++ b/cpp/include/cuspatial/experimental/type_utils.hpp @@ -30,7 +30,8 @@ template struct tuple_to_coord_2d { __device__ CoordType operator()(thrust::tuple pos) { - static_assert(std::is_base_of_v, CoordType>, "Can only convert to coord_2d type."); + static_assert(std::is_base_of_v, CoordType>(), + "Can only convert to coord_2d type."); return CoordType{thrust::get<0>(pos), thrust::get<1>(pos)}; } }; From 08ef02825a232aff7be419069a9701afd52e5eba Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 14 Apr 2022 11:11:50 -0700 Subject: [PATCH 19/67] Revert "In the middle of getting the compiler to include the file." This reverts commit 3bf3d446b6bbfec09a39f8c869ee40ea1dd0490e. --- .../detail/linestring_distance.cuh | 256 ---------------- .../experimental/linestring_distance.cuh | 43 --- .../cuspatial/experimental/type_utils.hpp | 52 ---- cpp/include/cuspatial/types.hpp | 14 +- cpp/src/spatial/linestring_distance.cu | 284 ++++++++++++++++-- 5 files changed, 267 insertions(+), 382 deletions(-) delete mode 100644 cpp/include/cuspatial/experimental/detail/linestring_distance.cuh delete mode 100644 cpp/include/cuspatial/experimental/linestring_distance.cuh delete mode 100644 cpp/include/cuspatial/experimental/type_utils.hpp diff --git a/cpp/include/cuspatial/experimental/detail/linestring_distance.cuh b/cpp/include/cuspatial/experimental/detail/linestring_distance.cuh deleted file mode 100644 index e81a80678..000000000 --- a/cpp/include/cuspatial/experimental/detail/linestring_distance.cuh +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Copyright (c) 2022, NVIDIA CORPORATION. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include - -#include -#include - -#include -#include -#include - -#include -#include -#include - -namespace cuspatital { -namespace detail { - -template -constexpr bool is_same() { - return std::conjunction_v...>; -} - -template -constexpr bool is_floating_point() { - return std::conjunction_v...>; -} - -template -double __device__ point_to_segment_distance(cuspatial::cart_2d const& C, - cuspatial::cart_2d const& A, - cuspatial::cart_2d const& B) -{ - // Subject 1.02 of https://www.inf.pucrs.br/~pinho/CG/faq.html - // Project the point to the segment, if it lands on the segment, - // the distance is the length of proejction, otherwise it's the - // length to one of the end points. - - double L_squared = (A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y); - if (L_squared == 0) { return hypot(C.x - A.x, C.y - A.y); } - double r = ((C.x - A.x) * (B.x - A.x) + (C.y - A.y) * (B.y - A.y)) / L_squared; - if (r <= 0 or r >= 1) { - return std::min(hypot(C.x - A.x, C.y - A.y), hypot(C.x - B.x, C.y - B.y)); - } - double Px = A.x + r * (B.x - A.x); - double Py = A.y + r * (B.y - A.y); - return hypot(C.x - Px, C.y - Py); -} - -template -double __device__ segment_distance_no_intersect(cuspatial::cart_2d const& A, - cuspatial::cart_2d const& B, - cuspatial::cart_2d const& C, - cuspatial::cart_2d const& D) -{ - return std::min(std::min(point_to_segment_distance(A, C, D), point_to_segment_distance(B, C, D)), - std::min(point_to_segment_distance(C, A, B), point_to_segment_distance(D, A, B))); -} - -/** - * @brief Computes shortest distance between two segments. - * - * If two segment intersects, distance is 0. - */ -template -double __device__ segment_distance(cuspatial::cart_2d const& A, - cuspatial::cart_2d const& B, - cuspatial::cart_2d const& C, - cuspatial::cart_2d const& D) -{ - // Subject 1.03 of https://www.inf.pucrs.br/~pinho/CG/faq.html - // Construct a parametrized ray of AB and CD, solve for the parameters. - // If both parameters are within [0, 1], the intersection exists. - - double r_denom = (B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x); - double r_numer = (A.y - C.y) * (D.x - C.x) - (A.x - C.x) * (D.y - C.y); - if (r_denom == 0) { - if (r_numer == 0) { return 0.0; } // Segments coincides - // Segments parallel - return segment_distance_no_intersect(A, B, C, D); - } - double r = r_numer / r_denom; - double s = ((A.y - C.y) * (B.x - A.x) - (A.x - C.x) * (B.y - A.x)) / - ((B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x)); - if (r >= 0 and r <= 1 and s >= 0 and s <= 1) { return 0.0; } - return segment_distance_no_intersect(A, B, C, D); -} - -template ::value_type> -void __global__ kernel(OffsetIterator linestring1_offsets_begin, - OffsetIterator linestring1_offsets_end, - Cart2dItA linestring1_points_begin, - Cart2dItA linestring1_points_end, - OffsetIterator linestring2_offsets_begin, - Cart2dItB linestring2_points_begin, - Cart2dItB linestring2_points_end, - OutputIterator min_distances) -{ - auto const p1Idx = threadIdx.x + blockIdx.x * blockDim.x; - cudf::size_type const num_linestrings = - thrust::distance(linestring1_offsets_begin, linestring1_offsets_end); - cudf::size_type const linestring1_num_points = - thrust::distance(linestring1_points_begin, linestring1_points_end); - cudf::size_type const linestring2_num_points = - thrust::distance(linestring2_points_begin, linestring2_points_end); - - if (p1Idx >= linestring1_num_points) { return; } - - cudf::size_type const linestring_idx = - thrust::distance( - linestring1_offsets_begin, - thrust::upper_bound(thrust::seq, linestring1_offsets_begin, linestring1_offsets_end, p1Idx)) - - 1; - - cudf::size_type ls1End = - (linestring_idx == (num_linestrings - 1) ? (linestring1_num_points) - : *(linestring1_offsets_begin + linestring_idx + 1)) - - 1; - - if (p1Idx == ls1End) { - // Current point is the end point of the line string. - return; - } - - cudf::size_type ls2Start = *(linestring2_offsets_begin + linestring_idx); - cudf::size_type ls2End = - (linestring_idx == (num_linestrings - 1) ? linestring2_num_points - : *(linestring2_offsets_begin + linestring_idx + 1)) - - 1; - - cuspatial::cart_2d const& A = linestring1_points_begin[p1Idx]; - cuspatial::cart_2d const& B = linestring1_points_begin[p1Idx + 1]; - - double min_distance = std::numeric_limits::max(); - for (cudf::size_type p2Idx = ls2Start; p2Idx < ls2End; p2Idx++) { - cuspatial::cart_2d const& C = linestring2_points_begin[p2Idx]; - cuspatial::cart_2d const& D = linestring2_points_begin[p2Idx + 1]; - min_distance = std::min(min_distance, segment_distance(A, B, C, D)); - } - atomicMin(min_distances + linestring_idx, static_cast(min_distance)); -} - - -} // namespace detail - -/** - * @brief Check if every linestring in the input contains at least 2 end points. - */ -template -bool validate_linestring(OffsetIterator linestring_offsets_first, - cudf::size_type num_linestring_pairs, - Cart2dIt linestring_points_x_first, - cudf::size_type num_points, - rmm::cuda_stream_view stream) -{ - if (num_linestring_pairs == 1) { - return num_points >= 2; - } - - return thrust::reduce( - rmm::exec_policy(stream), - thrust::make_counting_iterator(cudf::size_type{0}), - thrust::make_counting_iterator(num_linestring_pairs), - true, - [linestring_offsets_first, - num_linestring_pairs, - num_points] __device__(bool prev, cudf::size_type i) { - cudf::size_type begin = linestring_offsets_first[i]; - cudf::size_type end = i == num_linestring_pairs ? num_points : linestring_offsets_first[i + 1]; - return prev && (end - begin); - }); -} - -template - void pairwise_linestring_distance( - OffsetIterator linestring1_offsets_first, - OffsetIterator linestring1_offsets_last, - Cart2dItA linestring1_points_first, - Cart2dItA linestring1_points_last, - OffsetIterator linestring2_offsets_first, - Cart2dItB linestring2_points_first, - Cart2dItB linestring2_points_last, - OutputIt distances_first, - rmm::cuda_stream_view stream) { - using Cart2dB = typename std::iterator_traits::value_type; - static_assert( - detail::is_same(), "Inputs must be cuspatial::cart_2d" - ); - static_assert( - detail::is_floating_point::value_type>(), - "Inputs must be floating point types." - ); - - auto const num_string_pairs = thrust::distance(linestring1_offsets_first, linestring1_offsets_last); - auto const num_linestring1_points = thrust::distance(linestring1_points_first, linestring1_points_last); - auto const num_linestring2_points = thrust::distance(linestring2_points_first, linestring2_points_last); - - CUSPATIAL_EXPECTS(validate_linestring( - linestring1_offsets_first, num_string_pairs, linestring1_points_first, num_linestring1_points, stream), - "Each item of linestring1 should contain at least 2 end points."); - CUSPATIAL_EXPECTS(validate_linestring( - linestring2_offsets_first, num_string_pairs, linestring2_points_first, num_linestring2_points, stream), - "Each item of linestring2 should contain at least 2 end points."); - - - thrust::fill(rmm::exec_policy(stream), - distances_first, - distances_first + num_string_pairs, - std::numeric_limits::max()); - - std::size_t const threads_per_block = 64; - std::size_t const num_blocks = (num_linestring1_points + threads_per_block - 1) / threads_per_block; - - kernel<<>>( - linestring1_offsets_first, - linestring1_offsets_last, - linestring1_points_first, - linestring1_points_last, - linestring2_offsets_first, - linestring2_points_first, - linestring2_points_last, - distances_first - ); - - CUSPATIAL_CUDA_TRY(cudaGetLastError()); - } - -} // namespace cuspatial diff --git a/cpp/include/cuspatial/experimental/linestring_distance.cuh b/cpp/include/cuspatial/experimental/linestring_distance.cuh deleted file mode 100644 index a936b8a7c..000000000 --- a/cpp/include/cuspatial/experimental/linestring_distance.cuh +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2022, NVIDIA CORPORATION. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#pragma once - -#include - -namespace cuspatial { - -template::value_type, - class T = typename Cart2d::value_type> - void pairwise_linestring_distance( - OffsetIterator linestring1_offsets_first, - OffsetIterator linestring1_offsets_last, - Cart2dItA linestring1_points_first, - Cart2dItA linestring1_points_last, - OffsetIterator linestring2_offsets_first, - Cart2dItB linestring2_points_first, - Cart2dItB linestring2_points_last, - OutputIt distances_first, - rmm::cuda_stream_view stream); - -} - -#include "detail/linestring_distance.cuh" diff --git a/cpp/include/cuspatial/experimental/type_utils.hpp b/cpp/include/cuspatial/experimental/type_utils.hpp deleted file mode 100644 index eb02c388c..000000000 --- a/cpp/include/cuspatial/experimental/type_utils.hpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2022, NVIDIA CORPORATION. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include -#include -#include - -#include - -namespace cuspatial { - -namespace detail { - -template -struct tuple_to_coord_2d { - __device__ CoordType operator()(thrust::tuple pos) - { - static_assert(std::is_base_of_v, CoordType>(), - "Can only convert to coord_2d type."); - return CoordType{thrust::get<0>(pos), thrust::get<1>(pos)}; - } -}; - -} // namespace detail - -template -auto make_coord_2d_iterator(FirstIter first, SecondIter second) -{ - using T = typename std::iterator_traits::value_type; - static_assert(std::is_same_v::value_type>, - "Iterator value_type mismatch"); - - auto zipped = thrust::make_zip_iterator(thrust::make_tuple(first, second)); - return thrust::make_transform_iterator(zipped, detail::tuple_to_coord_2d()); -} - -} // namespace cuspatial diff --git a/cpp/include/cuspatial/types.hpp b/cpp/include/cuspatial/types.hpp index dffd796a1..bf1ca5bc9 100644 --- a/cpp/include/cuspatial/types.hpp +++ b/cpp/include/cuspatial/types.hpp @@ -31,26 +31,16 @@ struct location_3d { }; /** - * @brief A 2D coordinate (x, y) + * @brief A 2D Cartesian location (x, y) * * @tparam T the base type for the coordinates */ template -struct alignas(2 * sizeof(T)) coord_2d { - using value_type = T; +struct coord_2d { T x; T y; }; -// Longtitude/Latitude coordinates -template -struct lonlat_2d : coord_2d { -}; - -// Cartesian coordinates -template -struct cart_2d : coord_2d { -}; /** * @brief A timestamp * diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index af9796d90..0805e2bd2 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -18,8 +18,6 @@ #include #include -#include -#include #include #include @@ -39,7 +37,197 @@ #include #include +#ifndef DEBUG +// #define DEBUG 1 +#endif + +template +void __device__ print(cuspatial::coord_2d const& point) +{ + printf("POINT (%f, %f)\n", point.x, point.y); +} + +template +void __device__ print(cuspatial::coord_2d const& A, cuspatial::coord_2d const& B) +{ + printf("SEGMENT (%f, %f) -> (%f, %f)\n", A.x, A.y, B.x, B.y); +} + namespace cuspatial { +namespace { + +template +double __device__ point_to_segment_distance(coord_2d const& C, + coord_2d const& A, + coord_2d const& B) +{ + // Subject 1.02 of https://www.inf.pucrs.br/~pinho/CG/faq.html + // Project the point to the segment, if it lands on the segment, + // the distance is the length of proejction, otherwise it's the + // length to one of the end points. + + double L_squared = (A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y); + if (L_squared == 0) { return hypot(C.x - A.x, C.y - A.y); } + double r = ((C.x - A.x) * (B.x - A.x) + (C.y - A.y) * (B.y - A.y)) / L_squared; +#ifdef DEBUG + printf("\t r=%f\n", r); +#endif + if (r <= 0 or r >= 1) { + return std::min(hypot(C.x - A.x, C.y - A.y), hypot(C.x - B.x, C.y - B.y)); + } + double Px = A.x + r * (B.x - A.x); + double Py = A.y + r * (B.y - A.y); + return hypot(C.x - Px, C.y - Py); +} + +template +double __device__ segment_distance_no_intersect(coord_2d const& A, + coord_2d const& B, + coord_2d const& C, + coord_2d const& D) +{ +#ifdef DEBUG + printf("From: \n"); + print(A); + printf("To: \n"); + print(C, D); + printf("Distance %f\n", point_to_segment_distance(A, C, D)); + + printf("From: \n"); + print(B); + printf("To: \n"); + print(C, D); + printf("Distance %f\n", point_to_segment_distance(B, C, D)); + + printf("From: \n"); + print(C); + printf("To: \n"); + print(A, B); + printf("Distance %f\n", point_to_segment_distance(C, A, B)); + + printf("From: \n"); + print(D); + printf("To: \n"); + print(A, B); + printf("Distance %f\n", point_to_segment_distance(D, A, B)); +#endif + return std::min(std::min(point_to_segment_distance(A, C, D), point_to_segment_distance(B, C, D)), + std::min(point_to_segment_distance(C, A, B), point_to_segment_distance(D, A, B))); +} + +/** + * @brief Computes shortest distance between two segments. + * + * If two segment intersects, distance is 0. + */ +template +double __device__ segment_distance(coord_2d const& A, + coord_2d const& B, + coord_2d const& C, + coord_2d const& D) +{ + // Subject 1.03 of https://www.inf.pucrs.br/~pinho/CG/faq.html + // Construct a parametrized ray of AB and CD, solve for the parameters. + // If both parameters are within [0, 1], the intersection exists. + + double r_denom = (B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x); + double r_numer = (A.y - C.y) * (D.x - C.x) - (A.x - C.x) * (D.y - C.y); + if (r_denom == 0) { + if (r_numer == 0) { return 0.0; } // Segments coincides + // Segments parallel + return segment_distance_no_intersect(A, B, C, D); + } + double r = r_numer / r_denom; + double s = ((A.y - C.y) * (B.x - A.x) - (A.x - C.x) * (B.y - A.x)) / + ((B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x)); + if (r >= 0 and r <= 1 and s >= 0 and s <= 1) { return 0.0; } + return segment_distance_no_intersect(A, B, C, D); +} + +template +void __global__ kernel(OffsetIterator linestring1_offsets_begin, + OffsetIterator linestring1_offsets_end, + CoordinateIterator linestring1_points_xs_begin, + CoordinateIterator linestring1_points_xs_end, + CoordinateIterator linestring1_points_ys_begin, + OffsetIterator linestring2_offsets_begin, + CoordinateIterator linestring2_points_xs_begin, + CoordinateIterator linestring2_points_xs_end, + CoordinateIterator linestring2_points_ys_begin, + OutputIterator min_distances) +{ + using T = typename std::iterator_traits::value_type; + + auto const p1Idx = threadIdx.x + blockIdx.x * blockDim.x; + cudf::size_type const num_linestrings = + thrust::distance(linestring1_offsets_begin, linestring1_offsets_end); + cudf::size_type const linestring1_num_points = + thrust::distance(linestring1_points_xs_begin, linestring1_points_xs_end); + cudf::size_type const linestring2_num_points = + thrust::distance(linestring2_points_xs_begin, linestring2_points_xs_end); + +#ifdef DEBUG + printf("p1Idx: %d\n", p1Idx); + printf("linestring1_num_points: %d\n", linestring1_num_points); +#endif + + if (p1Idx >= linestring1_num_points) { return; } + + cudf::size_type const linestring_idx = + thrust::distance( + linestring1_offsets_begin, + thrust::upper_bound(thrust::seq, linestring1_offsets_begin, linestring1_offsets_end, p1Idx)) - + 1; + + cudf::size_type ls1End = + (linestring_idx == (num_linestrings - 1) ? (linestring1_num_points) + : *(linestring1_offsets_begin + linestring_idx + 1)) - + 1; + +#ifdef DEBUG + printf("p1Idx: %d\n", p1Idx); + printf("ls1End: %d\n", ls1End); + printf("linestring_idx: %d\n", linestring_idx); + printf("num_linestrings: %d\n", num_linestrings); +#endif + if (p1Idx == ls1End) { + // Current point is the end point of the line string. + return; + } + + cudf::size_type ls2Start = *(linestring2_offsets_begin + linestring_idx); + cudf::size_type ls2End = + (linestring_idx == (num_linestrings - 1) ? linestring2_num_points + : *(linestring2_offsets_begin + linestring_idx + 1)) - + 1; + + coord_2d A{linestring1_points_xs_begin[p1Idx], linestring1_points_ys_begin[p1Idx]}; + coord_2d B{linestring1_points_xs_begin[p1Idx + 1], linestring1_points_ys_begin[p1Idx + 1]}; + +#ifdef DEBUG + printf("linestring_idx: %d\n", linestring_idx); + printf("num_linestrings: %d\n", num_linestrings); + printf("linestring1_num_points: %d\n", linestring1_num_points); + printf("linestring2_num_points: %d\n", linestring2_num_points); + printf("p1Idx: %d\n", p1Idx); + printf("ls2Start: %d\n", ls2Start); + printf("ls2End: %d\n", ls2End); + print(A, B); +#endif + double min_distance = std::numeric_limits::max(); + for (cudf::size_type p2Idx = ls2Start; p2Idx < ls2End; p2Idx++) { + coord_2d C{linestring2_points_xs_begin[p2Idx], linestring2_points_ys_begin[p2Idx]}; + coord_2d D{linestring2_points_xs_begin[p2Idx + 1], linestring2_points_ys_begin[p2Idx + 1]}; +#ifdef DEBUG + print(C, D); +#endif + min_distance = std::min(min_distance, segment_distance(A, B, C, D)); + } + atomicMin(min_distances + linestring_idx, static_cast(min_distance)); +} + +} // anonymous namespace + namespace detail { struct pariwise_linestring_distance_functor { template @@ -53,25 +241,55 @@ struct pariwise_linestring_distance_functor { rmm::cuda_stream_view stream, rmm::mr::device_memory_resource* mr) { - auto const num_string_pairs = static_cast(linestring1_offsets.size()); + using namespace cudf; + + auto const num_string_pairs = static_cast(linestring1_offsets.size()); auto min_distances = - cudf::make_numeric_column(cudf::data_type{cudf::type_to_id()}, num_string_pairs, cudf::mask_state::UNALLOCATED, stream, mr); - - auto linestring1_coords_it = make_coord_2d_iterator>(linestring1_points_x.begin(), linestring1_points_y.begin()); - auto linestring2_coords_it = make_coord_2d_iterator>(linestring2_points_x.begin(), linestring2_points_y.begin()); - - pairwise_linestring_distance( - linestring1_offsets.begin(), - linestring1_offsets.end(), - linestring1_coords_it, - linestring1_coords_it + linestring1_points_x.size(), - linestring2_offsets.begin(), - linestring2_coords_it, - linestring2_coords_it + linestring2_points_x.size(), - min_distances->mutable_view().begin(), - stream - ); + make_numeric_column(data_type{type_to_id()}, num_string_pairs, mask_state::UNALLOCATED); + + // auto functor = linestirngs_pairs_min_distance_functor(num_string_pairs, + // linestring1_points_x.size(), + // linestring2_points_x.size(), + // linestring1_offsets.begin(), + // linestring1_points_x.begin(), + // linestring1_points_y.begin(), + // linestring2_offsets.begin(), + // linestring2_points_x.begin(), + // linestring2_points_y.begin()); + + // thrust::transform(rmm::exec_policy(stream), + // thrust::make_counting_iterator(0), + // thrust::make_counting_iterator(num_string_pairs), + // min_distances->mutable_view().begin(), + // functor); + + thrust::fill(rmm::exec_policy(stream), + min_distances->mutable_view().begin(), + min_distances->mutable_view().end(), + std::numeric_limits::max()); + + std::size_t const threads_per_block = 64; + std::size_t const num_blocks = + (linestring1_points_x.size() + threads_per_block - 1) / threads_per_block; + +#ifdef DEBUG + std::cout << "number of strings: " << num_string_pairs << std::endl; + std::cout << "num blocks" << num_blocks << std::endl; +#endif + kernel<<>>( + linestring1_offsets.begin(), + linestring1_offsets.end(), + linestring1_points_x.begin(), + linestring1_points_x.end(), + linestring1_points_y.begin(), + linestring2_offsets.begin(), + linestring2_points_x.begin(), + linestring2_points_x.end(), + linestring2_points_y.begin(), + min_distances->mutable_view().begin()); + + CUSPATIAL_CUDA_TRY(cudaGetLastError()); return min_distances; } @@ -84,6 +302,29 @@ struct pariwise_linestring_distance_functor { } }; +/** + * @brief Check if every linestring in the input contains at least 2 end points. + */ +bool validate_linestring(cudf::device_span linestring_offsets, + cudf::column_view const& linestring_points_x, + rmm::cuda_stream_view stream) +{ + if (linestring_offsets.size() == 1) { return linestring_points_x.size() >= 2; } + return thrust::reduce( + rmm::exec_policy(stream), + thrust::make_counting_iterator(cudf::size_type{0}), + thrust::make_counting_iterator(static_cast(linestring_offsets.size())), + true, + [offsets = linestring_offsets.begin(), + num_offsets = static_cast(linestring_offsets.size()), + num_points = static_cast( + linestring_points_x.size())] __device__(bool prev, cudf::size_type i) { + cudf::size_type begin = offsets[i]; + cudf::size_type end = i == num_offsets ? num_points : offsets[i + 1]; + return prev && (end - begin); + }); +} + std::unique_ptr pairwise_linestring_distance( cudf::device_span linestring1_offsets, cudf::column_view const& linestring1_points_x, @@ -108,6 +349,11 @@ std::unique_ptr pairwise_linestring_distance( if (linestring1_offsets.size() == 0) { return cudf::empty_like(linestring1_points_x); } + CUSPATIAL_EXPECTS(validate_linestring(linestring1_offsets, linestring1_points_x, stream), + "Each item of linestring1 should contain at least 2 end points."); + CUSPATIAL_EXPECTS(validate_linestring(linestring2_offsets, linestring2_points_x, stream), + "Each item of linestring2 should contain at least 2 end points."); + return cudf::type_dispatcher(linestring1_points_x.type(), pariwise_linestring_distance_functor{}, linestring1_offsets, From 53e822a518ce6320233f7d68fd28a45b9d0b5c7e Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 14 Apr 2022 11:41:54 -0700 Subject: [PATCH 20/67] Code cleanups and completes docstrings --- .../distances/linestring_distance.hpp | 6 +- cpp/src/spatial/linestring_distance.cu | 185 +++++++----------- 2 files changed, 75 insertions(+), 116 deletions(-) diff --git a/cpp/include/cuspatial/distances/linestring_distance.hpp b/cpp/include/cuspatial/distances/linestring_distance.hpp index bbbcd606e..918647393 100644 --- a/cpp/include/cuspatial/distances/linestring_distance.hpp +++ b/cpp/include/cuspatial/distances/linestring_distance.hpp @@ -24,7 +24,11 @@ namespace cuspatial { /** - * @brief Compute distance between pairs of linestrings + * @brief Compute shortest distance between pairs of linestrings + * + * The shortest distances between two linestrings are defined as the shortest distances + * between all pairs of segments between the linestrings. If any of the segments intersects, + * the distance is 0. * * @param linestring1_offsets Indices to the start coordinate to the first linestring of the pair * @param linestring1_points_x x component for points consisting linestrings 1 diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index 0805e2bd2..66917d9d5 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -37,41 +37,20 @@ #include #include -#ifndef DEBUG -// #define DEBUG 1 -#endif - -template -void __device__ print(cuspatial::coord_2d const& point) -{ - printf("POINT (%f, %f)\n", point.x, point.y); -} - -template -void __device__ print(cuspatial::coord_2d const& A, cuspatial::coord_2d const& B) -{ - printf("SEGMENT (%f, %f) -> (%f, %f)\n", A.x, A.y, B.x, B.y); -} - namespace cuspatial { namespace { +/** + * @brief Computes shortest distance between @p C and segment @p A @p B + */ template double __device__ point_to_segment_distance(coord_2d const& C, coord_2d const& A, coord_2d const& B) { - // Subject 1.02 of https://www.inf.pucrs.br/~pinho/CG/faq.html - // Project the point to the segment, if it lands on the segment, - // the distance is the length of proejction, otherwise it's the - // length to one of the end points. - double L_squared = (A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y); if (L_squared == 0) { return hypot(C.x - A.x, C.y - A.y); } double r = ((C.x - A.x) * (B.x - A.x) + (C.y - A.y) * (B.y - A.y)) / L_squared; -#ifdef DEBUG - printf("\t r=%f\n", r); -#endif if (r <= 0 or r >= 1) { return std::min(hypot(C.x - A.x, C.y - A.y), hypot(C.x - B.x, C.y - B.y)); } @@ -80,37 +59,15 @@ double __device__ point_to_segment_distance(coord_2d const& C, return hypot(C.x - Px, C.y - Py); } +/** + * @brief Computes shortest distance between two segments that doesn't intersect. + */ template double __device__ segment_distance_no_intersect(coord_2d const& A, coord_2d const& B, coord_2d const& C, coord_2d const& D) { -#ifdef DEBUG - printf("From: \n"); - print(A); - printf("To: \n"); - print(C, D); - printf("Distance %f\n", point_to_segment_distance(A, C, D)); - - printf("From: \n"); - print(B); - printf("To: \n"); - print(C, D); - printf("Distance %f\n", point_to_segment_distance(B, C, D)); - - printf("From: \n"); - print(C); - printf("To: \n"); - print(A, B); - printf("Distance %f\n", point_to_segment_distance(C, A, B)); - - printf("From: \n"); - print(D); - printf("To: \n"); - print(A, B); - printf("Distance %f\n", point_to_segment_distance(D, A, B)); -#endif return std::min(std::min(point_to_segment_distance(A, C, D), point_to_segment_distance(B, C, D)), std::min(point_to_segment_distance(C, A, B), point_to_segment_distance(D, A, B))); } @@ -118,7 +75,8 @@ double __device__ segment_distance_no_intersect(coord_2d const& A, /** * @brief Computes shortest distance between two segments. * - * If two segment intersects, distance is 0. + * If two segment intersects, distance is 0. Otherwise compute the shortest point + * to segment distance. */ template double __device__ segment_distance(coord_2d const& A, @@ -126,10 +84,6 @@ double __device__ segment_distance(coord_2d const& A, coord_2d const& C, coord_2d const& D) { - // Subject 1.03 of https://www.inf.pucrs.br/~pinho/CG/faq.html - // Construct a parametrized ray of AB and CD, solve for the parameters. - // If both parameters are within [0, 1], the intersection exists. - double r_denom = (B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x); double r_numer = (A.y - C.y) * (D.x - C.x) - (A.x - C.x) * (D.y - C.y); if (r_denom == 0) { @@ -144,17 +98,58 @@ double __device__ segment_distance(coord_2d const& A, return segment_distance_no_intersect(A, B, C, D); } +/** + * @brief The kernel to compute point 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 linestringin 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. + * + * @tparam CoordinateIterator Iterator to coordinates. Must meet requirements of + * [LegacyRandomAccessIterator][https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator] + * and is device-accessible. + * @tparam OffsetIterator Iterator to linestring offsets. Must meet requirements of + * [LegacyRandomAccessIterator][https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator] + * and is device-accessible. + * @tparam OutputIterator Iterator to output distances. Must meet requirements of + * [LegacyRandomAccessIterator][https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator] + * and is device-accessible. + * + * @param[in] linestring1_offsets_begin Iterator to the begin of the range of linestring offsets + * in pair 1. + * @param[in] linestring1_offsets_end Iterator to the end of the range of linestring offsets + * in pair 1. + * @param[in] linestring1_points_xs_begin Iterator to the begin of the range of x coordinates of + * points in pair 1. + * @param[in] linestring1_points_xs_end Iterator to the end of the range of x coordiantes of points + * in pair 1. + * @param[in] linestring1_points_ys_begin Iterator to the begin of the range of y coordinates of + * points in pair 1. + * @param[in] linestring2_offsets_begin Iterator to the begin of the range of linestring offsets + * in pair 2. + * @param[in] linestring2_points_xs_begin Iterator to the begin of the range of x coordinates of + * points in pair 2. + * @param[in] linestring2_points_xs_end Iterator to the end of the range of x coordiantes of points + * in pair 2. + * @param[in] linestring2_points_ys_begin Iterator to the begin of the range of y coordinates of + * points in pair 2. + * @param[out] distances Iterator to the output range of shortest distances between pairs. + * @return + */ template -void __global__ kernel(OffsetIterator linestring1_offsets_begin, - OffsetIterator linestring1_offsets_end, - CoordinateIterator linestring1_points_xs_begin, - CoordinateIterator linestring1_points_xs_end, - CoordinateIterator linestring1_points_ys_begin, - OffsetIterator linestring2_offsets_begin, - CoordinateIterator linestring2_points_xs_begin, - CoordinateIterator linestring2_points_xs_end, - CoordinateIterator linestring2_points_ys_begin, - OutputIterator min_distances) +void __global__ pairwise_linestring_distance_kernel(OffsetIterator linestring1_offsets_begin, + OffsetIterator linestring1_offsets_end, + CoordinateIterator linestring1_points_xs_begin, + CoordinateIterator linestring1_points_xs_end, + CoordinateIterator linestring1_points_ys_begin, + OffsetIterator linestring2_offsets_begin, + CoordinateIterator linestring2_points_xs_begin, + CoordinateIterator linestring2_points_xs_end, + CoordinateIterator linestring2_points_ys_begin, + OutputIterator distances) { using T = typename std::iterator_traits::value_type; @@ -166,11 +161,6 @@ void __global__ kernel(OffsetIterator linestring1_offsets_begin, cudf::size_type const linestring2_num_points = thrust::distance(linestring2_points_xs_begin, linestring2_points_xs_end); -#ifdef DEBUG - printf("p1Idx: %d\n", p1Idx); - printf("linestring1_num_points: %d\n", linestring1_num_points); -#endif - if (p1Idx >= linestring1_num_points) { return; } cudf::size_type const linestring_idx = @@ -184,12 +174,6 @@ void __global__ kernel(OffsetIterator linestring1_offsets_begin, : *(linestring1_offsets_begin + linestring_idx + 1)) - 1; -#ifdef DEBUG - printf("p1Idx: %d\n", p1Idx); - printf("ls1End: %d\n", ls1End); - printf("linestring_idx: %d\n", linestring_idx); - printf("num_linestrings: %d\n", num_linestrings); -#endif if (p1Idx == ls1End) { // Current point is the end point of the line string. return; @@ -204,31 +188,22 @@ void __global__ kernel(OffsetIterator linestring1_offsets_begin, coord_2d A{linestring1_points_xs_begin[p1Idx], linestring1_points_ys_begin[p1Idx]}; coord_2d B{linestring1_points_xs_begin[p1Idx + 1], linestring1_points_ys_begin[p1Idx + 1]}; -#ifdef DEBUG - printf("linestring_idx: %d\n", linestring_idx); - printf("num_linestrings: %d\n", num_linestrings); - printf("linestring1_num_points: %d\n", linestring1_num_points); - printf("linestring2_num_points: %d\n", linestring2_num_points); - printf("p1Idx: %d\n", p1Idx); - printf("ls2Start: %d\n", ls2Start); - printf("ls2End: %d\n", ls2End); - print(A, B); -#endif double min_distance = std::numeric_limits::max(); for (cudf::size_type p2Idx = ls2Start; p2Idx < ls2End; p2Idx++) { coord_2d C{linestring2_points_xs_begin[p2Idx], linestring2_points_ys_begin[p2Idx]}; coord_2d D{linestring2_points_xs_begin[p2Idx + 1], linestring2_points_ys_begin[p2Idx + 1]}; -#ifdef DEBUG - print(C, D); -#endif min_distance = std::min(min_distance, segment_distance(A, B, C, D)); } - atomicMin(min_distances + linestring_idx, static_cast(min_distance)); + atomicMin(distances + linestring_idx, static_cast(min_distance)); } } // anonymous namespace namespace detail { + +/** + * @brief Functor that launches the kernel to compute pairwise linestring distances. + */ struct pariwise_linestring_distance_functor { template std::enable_if_t::value, std::unique_ptr> operator()( @@ -245,39 +220,19 @@ struct pariwise_linestring_distance_functor { auto const num_string_pairs = static_cast(linestring1_offsets.size()); - auto min_distances = + auto distances = make_numeric_column(data_type{type_to_id()}, num_string_pairs, mask_state::UNALLOCATED); - // auto functor = linestirngs_pairs_min_distance_functor(num_string_pairs, - // linestring1_points_x.size(), - // linestring2_points_x.size(), - // linestring1_offsets.begin(), - // linestring1_points_x.begin(), - // linestring1_points_y.begin(), - // linestring2_offsets.begin(), - // linestring2_points_x.begin(), - // linestring2_points_y.begin()); - - // thrust::transform(rmm::exec_policy(stream), - // thrust::make_counting_iterator(0), - // thrust::make_counting_iterator(num_string_pairs), - // min_distances->mutable_view().begin(), - // functor); - thrust::fill(rmm::exec_policy(stream), - min_distances->mutable_view().begin(), - min_distances->mutable_view().end(), + distances->mutable_view().begin(), + distances->mutable_view().end(), std::numeric_limits::max()); std::size_t const threads_per_block = 64; std::size_t const num_blocks = (linestring1_points_x.size() + threads_per_block - 1) / threads_per_block; -#ifdef DEBUG - std::cout << "number of strings: " << num_string_pairs << std::endl; - std::cout << "num blocks" << num_blocks << std::endl; -#endif - kernel<<>>( + pairwise_linestring_distance_kernel<<>>( linestring1_offsets.begin(), linestring1_offsets.end(), linestring1_points_x.begin(), @@ -287,11 +242,11 @@ struct pariwise_linestring_distance_functor { linestring2_points_x.begin(), linestring2_points_x.end(), linestring2_points_y.begin(), - min_distances->mutable_view().begin()); + distances->mutable_view().begin()); CUSPATIAL_CUDA_TRY(cudaGetLastError()); - return min_distances; + return distances; } template From 5dd6a4cc60ae4c497dec52f24746b25dce78c5ca Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 14 Apr 2022 12:07:05 -0700 Subject: [PATCH 21/67] Revert "Change problem size in benchmarks" This reverts commit cee2c1f119db2f753bb47ac923b219c469fa08d5. --- cpp/benchmarks/pairwise_linestring_distance.cu | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cpp/benchmarks/pairwise_linestring_distance.cu b/cpp/benchmarks/pairwise_linestring_distance.cu index 6fbd2f55b..d79a53541 100644 --- a/cpp/benchmarks/pairwise_linestring_distance.cu +++ b/cpp/benchmarks/pairwise_linestring_distance.cu @@ -89,7 +89,6 @@ void pairwise_linestring_distance_benchmark(nvbench::state& state, nvbench::type state.add_element_count(num_string_pairs, "LineStringPairs"); state.add_element_count(total_points, "NumPoints"); state.add_global_memory_reads(total_points * 2, "CoordinatesDataSize"); - state.add_global_memory_reads(num_string_pairs * 2, "OffsetsDataSize"); state.add_global_memory_writes(num_string_pairs); state.exec(nvbench::exec_tag::sync, @@ -107,7 +106,7 @@ void pairwise_linestring_distance_benchmark(nvbench::state& state, nvbench::type using floating_point_types = nvbench::type_list; NVBENCH_BENCH_TYPES(pairwise_linestring_distance_benchmark, NVBENCH_TYPE_AXES(floating_point_types)) .set_type_axes_names({"CoordsType"}) - .add_int64_axis("NumStrings", {1'000, 10'000, 100'000}) - .add_int64_axis("NumSegmentsPerString", {10, 100, 1'000}); + .add_int64_axis("NumStrings", {100'000, 1'000'000, 10'000'000}) + .add_int64_axis("NumSegmentsPerString", {100, 1'000, 10'000}); } // namespace cuspatial From 2b1da4a5dd95fcfd7ee7d103e4b870b066ef43c3 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 14 Apr 2022 12:09:18 -0700 Subject: [PATCH 22/67] Revert "Add data gen synchronizer" This reverts commit aa65c757d3295d9515a00897e4aaea80931f26ba. --- cpp/benchmarks/pairwise_linestring_distance.cu | 2 -- 1 file changed, 2 deletions(-) diff --git a/cpp/benchmarks/pairwise_linestring_distance.cu b/cpp/benchmarks/pairwise_linestring_distance.cu index d79a53541..478e9c04d 100644 --- a/cpp/benchmarks/pairwise_linestring_distance.cu +++ b/cpp/benchmarks/pairwise_linestring_distance.cu @@ -82,8 +82,6 @@ void pairwise_linestring_distance_benchmark(nvbench::state& state, nvbench::type auto [ls2_x, ls2_y, ls2_offset] = generate_linestring(num_string_pairs, num_segments_per_string, 1, 100, stream); - cudaStreamSynchronize(stream.value()); - auto const total_points = ls1_x->size() + ls2_x->size(); state.add_element_count(num_string_pairs, "LineStringPairs"); From d8e690259de171dfd06c4bf038c437fde3dec0ff Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 14 Apr 2022 12:09:21 -0700 Subject: [PATCH 23/67] Revert "Add runnable nvbench" This reverts commit 813178caea2858d281e80f4059b5786d8dfee797. --- cpp/benchmarks/CMakeLists.txt | 3 - cpp/benchmarks/fixture/rmm_pool_raii.hpp | 71 ----------- .../pairwise_linestring_distance.cu | 110 ------------------ 3 files changed, 184 deletions(-) delete mode 100644 cpp/benchmarks/fixture/rmm_pool_raii.hpp delete mode 100644 cpp/benchmarks/pairwise_linestring_distance.cu diff --git a/cpp/benchmarks/CMakeLists.txt b/cpp/benchmarks/CMakeLists.txt index e46995c95..00fbba2f1 100644 --- a/cpp/benchmarks/CMakeLists.txt +++ b/cpp/benchmarks/CMakeLists.txt @@ -80,6 +80,3 @@ endfunction(ConfigureNVBench) ConfigureBench(HAUSDORFF_BENCH hausdorff_benchmark.cpp) - -ConfigureNVBench(DISTANCES_BENCH - pairwise_linestring_distance.cu) diff --git a/cpp/benchmarks/fixture/rmm_pool_raii.hpp b/cpp/benchmarks/fixture/rmm_pool_raii.hpp deleted file mode 100644 index 444b9a7cb..000000000 --- a/cpp/benchmarks/fixture/rmm_pool_raii.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2021, 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 An RAII class setting up RMM memory pool for `nvbench` benchmarks - * - * This is a temporary solution before templated fixtures tests are supported - * in `nvbench`. Similarly to `cuspatial::benchmark`, creating this RAII object in - * each benchmark will ensure that the RAPIDS Memory Manager pool mode is used - * in benchmarks, which eliminates memory allocation / deallocation performance - * overhead from the benchmark. - * - * Example: - * - * void my_benchmark(nvbench::state& state) { - * cuspatial::rmm_pool_raii pool_raii; - * state.exec([](nvbench::launch& launch) { - * // benchmark stuff - * }); - * } - * - * NVBENCH_BENCH(my_benchmark); - */ -class rmm_pool_raii { - private: - // memory resource factory helpers - inline auto make_cuda() { return std::make_shared(); } - - inline auto make_pool() - { - return rmm::mr::make_owning_wrapper(make_cuda()); - } - - public: - rmm_pool_raii() - { - mr = make_pool(); - rmm::mr::set_current_device_resource(mr.get()); // set default resource to pool - } - - ~rmm_pool_raii() - { - rmm::mr::set_current_device_resource(nullptr); - mr.reset(); - } - - private: - std::shared_ptr mr; -}; - -} // namespace cuspatial diff --git a/cpp/benchmarks/pairwise_linestring_distance.cu b/cpp/benchmarks/pairwise_linestring_distance.cu deleted file mode 100644 index 478e9c04d..000000000 --- a/cpp/benchmarks/pairwise_linestring_distance.cu +++ /dev/null @@ -1,110 +0,0 @@ -#include -#include - -#include - -#include -#include -#include - -#include -#include -#include - -#include - -#include - -namespace cuspatial { - -template -std::tuple, - std::unique_ptr, - rmm::device_uvector> -generate_linestring(cudf::size_type num_strings, - cudf::size_type num_segments_per_string, - T segment_length, - T init_xy, - rmm::cuda_stream_view stream) -{ - cudf::size_type num_points = num_strings * (num_segments_per_string + 1); - rmm::device_uvector offsets(num_points, stream); - thrust::transform( - rmm::exec_policy(stream), - thrust::make_counting_iterator(static_cast(0)), - thrust::make_counting_iterator(static_cast(num_points)), - offsets.begin(), - [num_segments_per_string] __device__(auto i) { return i * num_segments_per_string; }); - auto points_x = cudf::make_fixed_width_column( - cudf::data_type{cudf::type_to_id()}, num_points, cudf::mask_state::UNALLOCATED); - auto points_y = cudf::make_fixed_width_column( - cudf::data_type{cudf::type_to_id()}, num_points, cudf::mask_state::UNALLOCATED); - thrust::transform(rmm::exec_policy(stream), - thrust::counting_iterator(static_cast(0)), - thrust::counting_iterator(static_cast(num_points)), - points_x->mutable_view().begin(), - [] __device__(auto i) { return cos(i); }); - thrust::exclusive_scan( - rmm::exec_policy(stream), - points_x->view().begin(), - points_x->view().end(), - points_x->mutable_view().begin(), - init_xy, - [segment_length] __device__(T prev, T rad) { return prev + segment_length * rad; }); - thrust::transform(rmm::exec_policy(stream), - thrust::counting_iterator(static_cast(0)), - thrust::counting_iterator(static_cast(num_points)), - points_y->mutable_view().begin(), - [] __device__(auto i) { return sin(i); }); - thrust::exclusive_scan( - rmm::exec_policy(stream), - points_y->view().begin(), - points_y->view().end(), - points_y->mutable_view().begin(), - init_xy, - [segment_length] __device__(T prev, T rad) { return prev + segment_length * rad; }); - - return std::tuple(std::move(points_x), std::move(points_y), std::move(offsets)); -} - -template -void pairwise_linestring_distance_benchmark(nvbench::state& state, nvbench::type_list) -{ - // TODO: to be replaced by nvbench fixture once it's ready - cuspatial::rmm_pool_raii rmm_pool; - - auto const num_string_pairs{state.get_int64("NumStrings")}, - num_segments_per_string{state.get_int64("NumSegmentsPerString")}; - auto stream = rmm::cuda_stream_default; - - auto [ls1_x, ls1_y, ls1_offset] = - generate_linestring(num_string_pairs, num_segments_per_string, 1, 0, stream); - auto [ls2_x, ls2_y, ls2_offset] = - generate_linestring(num_string_pairs, num_segments_per_string, 1, 100, stream); - - auto const total_points = ls1_x->size() + ls2_x->size(); - - state.add_element_count(num_string_pairs, "LineStringPairs"); - state.add_element_count(total_points, "NumPoints"); - state.add_global_memory_reads(total_points * 2, "CoordinatesDataSize"); - state.add_global_memory_writes(num_string_pairs); - - state.exec(nvbench::exec_tag::sync, - [ls1_offset = cudf::device_span(ls1_offset), - ls1_x = ls1_x->view(), - ls1_y = ls1_y->view(), - ls2_offset = cudf::device_span(ls2_offset), - ls2_x = ls2_x->view(), - ls2_y = ls2_y->view()](nvbench::launch& launch) { - cuspatial::pairwise_linestring_distance( - ls1_offset, ls1_x, ls1_y, ls2_offset, ls2_x, ls2_y); - }); -} - -using floating_point_types = nvbench::type_list; -NVBENCH_BENCH_TYPES(pairwise_linestring_distance_benchmark, NVBENCH_TYPE_AXES(floating_point_types)) - .set_type_axes_names({"CoordsType"}) - .add_int64_axis("NumStrings", {100'000, 1'000'000, 10'000'000}) - .add_int64_axis("NumSegmentsPerString", {100, 1'000, 10'000}); - -} // namespace cuspatial From 234130158aed952354115d1b52c156b6654171bb Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 14 Apr 2022 12:10:16 -0700 Subject: [PATCH 24/67] Revert "Add nvbench cpm" This reverts commit d82f8876f7154e366df7d6be009f80d0fcf6202b. --- cpp/CMakeLists.txt | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 1913f104b..81a572ec7 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -219,22 +219,6 @@ if(CUSPATIAL_BUILD_BENCHMARKS) GIT_SHALLOW TRUE OPTIONS "BENCHMARK_ENABLE_TESTING OFF" "BENCHMARK_ENABLE_INSTALL OFF") - - set(nvbench_with_nvml "OFF") - if(TARGET CUDA::nvml) - set(nvbench_with_nvml "ON") - endif() - - message("nvbench configured with nvml ${nvbench_with_nvml}") - # Find or install NVBench - CPMFindPackage(NAME NVBench - VERSION 0.1.0 - GIT_REPOSITORY https://github.com/NVIDIA/nvbench.git - GIT_TAG main - GIT_SHALLOW TRUE - OPTIONS "NVBench_ENABLE_EXAMPLES OFF" - "NVBench_ENABLE_TESTING OFF" - "NVBench_ENABLE_NVML ${nvbench_with_nvml}") add_subdirectory(benchmarks) endif() From cc6f1a289fe0fdb85d66aef808e253ee7561ad6f Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 14 Apr 2022 12:17:19 -0700 Subject: [PATCH 25/67] Revert benchmark cmake introduction of nvbench. --- cpp/benchmarks/CMakeLists.txt | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/cpp/benchmarks/CMakeLists.txt b/cpp/benchmarks/CMakeLists.txt index 00fbba2f1..801bed2bc 100644 --- a/cpp/benchmarks/CMakeLists.txt +++ b/cpp/benchmarks/CMakeLists.txt @@ -40,7 +40,7 @@ target_include_directories(cuspatial_benchmark_common function(ConfigureBench CMAKE_BENCH_NAME) add_executable(${CMAKE_BENCH_NAME} ${ARGN}) set_target_properties(${CMAKE_BENCH_NAME} - PROPERTIES RUNTIME_OUTPUT_DIRECTORY "$" + PROPERTIES RUNTIME_OUTPUT_DIRECTORY "$" INSTALL_RPATH "\$ORIGIN/../../../lib" ) target_link_libraries(${CMAKE_BENCH_NAME} PRIVATE benchmark::benchmark_main cuspatial_benchmark_common) @@ -52,25 +52,6 @@ function(ConfigureBench CMAKE_BENCH_NAME) ) endfunction(ConfigureBench) -function(ConfigureNVBench CMAKE_BENCH_NAME) - add_executable(${CMAKE_BENCH_NAME} ${ARGN}) - set_target_properties( - ${CMAKE_BENCH_NAME} - PROPERTIES RUNTIME_OUTPUT_DIRECTORY "$" - INSTALL_RPATH "\$ORIGIN/../../../lib" - ) - target_link_libraries( - ${CMAKE_BENCH_NAME} PRIVATE cuspatial_benchmark_common nvbench::main - ) - install( - TARGETS ${CMAKE_BENCH_NAME} - COMPONENT testing - DESTINATION bin/benchmarks/libcuspatial - EXCLUDE_FROM_ALL - ) - -endfunction(ConfigureNVBench) - ################################################################################################### ### benchmark sources ############################################################################# ################################################################################################### From decac9467af8e0cc7992baf210baebfd274012f3 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 14 Apr 2022 12:35:22 -0700 Subject: [PATCH 26/67] Add small helper to retrieve endpoint index. --- cpp/src/spatial/linestring_distance.cu | 30 +++++++++++++++++++------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index 66917d9d5..4f8a707e0 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -40,6 +40,24 @@ namespace cuspatial { namespace { +/** @brief Get the index that is one-past the end point of linestring at @p linestring_idx + * + * @note The last endpoint of the linestring is not included in the offset array, thus + * @p num_points is returned. + */ +template +inline cudf::size_type __device__ +endpoint_index_of_linestring(cudf::size_type const& linestring_idx, + OffsetIterator const& linestring_offsets_begin, + cudf::size_type const& num_linestrings, + cudf::size_type const& num_points) +{ + return (linestring_idx == (num_linestrings - 1) + ? (num_points) + : *(linestring_offsets_begin + linestring_idx + 1)) - + 1; +} + /** * @brief Computes shortest distance between @p C and segment @p A @p B */ @@ -169,10 +187,8 @@ void __global__ pairwise_linestring_distance_kernel(OffsetIterator linestring1_o thrust::upper_bound(thrust::seq, linestring1_offsets_begin, linestring1_offsets_end, p1Idx)) - 1; - cudf::size_type ls1End = - (linestring_idx == (num_linestrings - 1) ? (linestring1_num_points) - : *(linestring1_offsets_begin + linestring_idx + 1)) - - 1; + cudf::size_type ls1End = endpoint_index_of_linestring( + linestring_idx, linestring1_offsets_begin, num_linestrings, linestring1_num_points); if (p1Idx == ls1End) { // Current point is the end point of the line string. @@ -180,10 +196,8 @@ void __global__ pairwise_linestring_distance_kernel(OffsetIterator linestring1_o } cudf::size_type ls2Start = *(linestring2_offsets_begin + linestring_idx); - cudf::size_type ls2End = - (linestring_idx == (num_linestrings - 1) ? linestring2_num_points - : *(linestring2_offsets_begin + linestring_idx + 1)) - - 1; + cudf::size_type ls2End = endpoint_index_of_linestring( + linestring_idx, linestring2_offsets_begin, num_linestrings, linestring2_num_points); coord_2d A{linestring1_points_xs_begin[p1Idx], linestring1_points_ys_begin[p1Idx]}; coord_2d B{linestring1_points_xs_begin[p1Idx + 1], linestring1_points_ys_begin[p1Idx + 1]}; From 0f139e0a5f27a5355d2b7b2fe5c8a2e30dffba92 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 14 Apr 2022 12:39:17 -0700 Subject: [PATCH 27/67] More cleanups --- cpp/src/spatial/linestring_distance.cu | 2 -- cpp/tests/spatial/linestring_distance_test.cpp | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index 4f8a707e0..4f5e4cf02 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2022, NVIDIA CORPORATION. * diff --git a/cpp/tests/spatial/linestring_distance_test.cpp b/cpp/tests/spatial/linestring_distance_test.cpp index 480a1c805..6d22d4021 100644 --- a/cpp/tests/spatial/linestring_distance_test.cpp +++ b/cpp/tests/spatial/linestring_distance_test.cpp @@ -202,7 +202,7 @@ TYPED_TEST(PairwiseLinestringDistanceTest, OnePairLinestringCoincide) expect_columns_equivalent(expected, *got, verbosity); } -TYPED_TEST(PairwiseLinestringDistanceTest, OnePairRandom1) +TYPED_TEST(PairwiseLinestringDistanceTest, OnePairRandom) { using T = TypeParam; wrapper linestring1_offsets{0}; From 30ac95c66966b6676723bd4c7889a7d22208f06d Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 25 Apr 2022 13:22:05 -0700 Subject: [PATCH 28/67] Add single test sample from geolife --- .../spatial/linestring_distance_test.cpp | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/cpp/tests/spatial/linestring_distance_test.cpp b/cpp/tests/spatial/linestring_distance_test.cpp index 6d22d4021..12de3faf5 100644 --- a/cpp/tests/spatial/linestring_distance_test.cpp +++ b/cpp/tests/spatial/linestring_distance_test.cpp @@ -223,6 +223,46 @@ TYPED_TEST(PairwiseLinestringDistanceTest, OnePairRandom) expect_columns_equivalent(expected, *got, verbosity); } +TYPED_TEST(PairwiseLinestringDistanceTest, OnePairGeolife) +{ + // Example extracted from a pair of trajectry in geolife dataset + using T = TypeParam; + wrapper linestring1_offsets{0}; + wrapper linestring1_points_x{39.97551667, 39.97585, 39.97598333, 39.9761, 39.97623333}; + wrapper linestring1_points_y{116.33028333, 116.3304, 116.33046667, 116.3305, 116.33056667}; + wrapper linestring2_offsets{0}; + wrapper linestring2_points_x{ + 39.97381667, 39.97341667, 39.9731, 39.97293333, 39.97233333, 39.97218333, 39.97218333, + 39.97215, 39.97168333, 39.97093333, 39.97073333, 39.9705, 39.96991667, 39.96961667, + 39.96918333, 39.96891667, 39.97531667, 39.97533333, 39.97535, 39.97515, 39.97506667, + 39.97508333, 39.9751, 39.97513333, 39.97511667, 39.97503333, 39.97513333, 39.97523333, + 39.97521667, 39.97503333, 39.97463333, 39.97443333, 39.96838333, 39.96808333, 39.96771667, + 39.96745, 39.96735, 39.9673, 39.96718333, 39.96751667, 39.9678, 39.9676, + 39.96741667, 39.9672, 39.97646667, 39.9764, 39.97625, 39.9762, 39.97603333, + 39.97581667, 39.9757, 39.97551667, 39.97535, 39.97543333, 39.97538333}; + wrapper linestring2_points_y{ + 116.34211667, 116.34215, 116.34218333, 116.34221667, 116.34225, 116.34243333, + 116.34296667, 116.34478333, 116.34486667, 116.34485, 116.34468333, 116.34461667, + 116.34465, 116.34465, 116.34466667, 116.34465, 116.33036667, 116.32961667, + 116.3292, 116.32903333, 116.32985, 116.33128333, 116.33195, 116.33618333, + 116.33668333, 116.33818333, 116.34, 116.34045, 116.34183333, 116.342, + 116.34203333, 116.3422, 116.3445, 116.34451667, 116.3445, 116.34453333, + 116.34493333, 116.34506667, 116.3451, 116.34483333, 116.3448, 116.3449, + 116.345, 116.34506667, 116.33006667, 116.33015, 116.33026667, 116.33038333, + 116.33036667, 116.3303, 116.33033333, 116.33035, 116.3304, 116.33078333, + 116.33066667}; + + wrapper expected{0.0}; + + auto got = pairwise_linestring_distance(column_view(linestring1_offsets), + linestring1_points_x, + linestring1_points_y, + column_view(linestring2_offsets), + linestring2_points_x, + linestring2_points_y); + expect_columns_equivalent(expected, *got, verbosity); +} + TYPED_TEST(PairwiseLinestringDistanceTest, TwoPairs) { using T = TypeParam; From 53a4135e98d4cb19c17191561b23222f45721d7f Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 25 Apr 2022 14:35:15 -0700 Subject: [PATCH 29/67] Fix typo --- cpp/src/spatial/linestring_distance.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index 4f5e4cf02..0bc6a2778 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -108,7 +108,7 @@ double __device__ segment_distance(coord_2d const& A, return segment_distance_no_intersect(A, B, C, D); } double r = r_numer / r_denom; - double s = ((A.y - C.y) * (B.x - A.x) - (A.x - C.x) * (B.y - A.x)) / + double s = ((A.y - C.y) * (B.x - A.x) - (A.x - C.x) * (B.y - A.y)) / ((B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x)); if (r >= 0 and r <= 1 and s >= 0 and s <= 1) { return 0.0; } return segment_distance_no_intersect(A, B, C, D); From b606956dc7a93c27a9e8cede75236c1288d3983a Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 25 Apr 2022 17:47:59 -0700 Subject: [PATCH 30/67] Fix bug where collinear line segments are taken as intersect segments --- cpp/src/spatial/linestring_distance.cu | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index 0bc6a2778..c92779ec5 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -79,10 +79,10 @@ double __device__ point_to_segment_distance(coord_2d const& C, * @brief Computes shortest distance between two segments that doesn't intersect. */ template -double __device__ segment_distance_no_intersect(coord_2d const& A, - coord_2d const& B, - coord_2d const& C, - coord_2d const& D) +double __device__ segment_distance_no_intersect_or_collinear(coord_2d const& A, + coord_2d const& B, + coord_2d const& C, + coord_2d const& D) { return std::min(std::min(point_to_segment_distance(A, C, D), point_to_segment_distance(B, C, D)), std::min(point_to_segment_distance(C, A, B), point_to_segment_distance(D, A, B))); @@ -103,15 +103,14 @@ double __device__ segment_distance(coord_2d const& A, double r_denom = (B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x); double r_numer = (A.y - C.y) * (D.x - C.x) - (A.x - C.x) * (D.y - C.y); if (r_denom == 0) { - if (r_numer == 0) { return 0.0; } // Segments coincides - // Segments parallel - return segment_distance_no_intersect(A, B, C, D); + // Segments parallel or collinear + return segment_distance_no_intersect_or_collinear(A, B, C, D); } double r = r_numer / r_denom; double s = ((A.y - C.y) * (B.x - A.x) - (A.x - C.x) * (B.y - A.y)) / ((B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x)); if (r >= 0 and r <= 1 and s >= 0 and s <= 1) { return 0.0; } - return segment_distance_no_intersect(A, B, C, D); + return segment_distance_no_intersect_or_collinear(A, B, C, D); } /** From 6811cb6d6fe8a9e34d492b3f0ebc1dd245c75dc8 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 25 Apr 2022 17:42:00 -0700 Subject: [PATCH 31/67] Add test case for various collinear line segments and an geolife example --- .../spatial/linestring_distance_test.cpp | 87 ++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/cpp/tests/spatial/linestring_distance_test.cpp b/cpp/tests/spatial/linestring_distance_test.cpp index 12de3faf5..a54421ea8 100644 --- a/cpp/tests/spatial/linestring_distance_test.cpp +++ b/cpp/tests/spatial/linestring_distance_test.cpp @@ -202,7 +202,70 @@ TYPED_TEST(PairwiseLinestringDistanceTest, OnePairLinestringCoincide) expect_columns_equivalent(expected, *got, verbosity); } -TYPED_TEST(PairwiseLinestringDistanceTest, OnePairRandom) +TYPED_TEST(PairwiseLinestringDistanceTest, OnePairDegenerateCollinearNoIntersect) +{ + using T = TypeParam; + wrapper linestring1_offsets{0}; + wrapper linestring1_points_x{0.0, 0.0}; + wrapper linestring1_points_y{0.0, 1.0}; + wrapper linestring2_offsets{0}; + wrapper linestring2_points_x{0.0, 0.0}; + wrapper linestring2_points_y{2.0, 3.0}; + + wrapper expected{1.0}; + + auto got = pairwise_linestring_distance(column_view(linestring1_offsets), + linestring1_points_x, + linestring1_points_y, + column_view(linestring2_offsets), + linestring2_points_x, + linestring2_points_y); + expect_columns_equivalent(expected, *got, verbosity); +} + +TYPED_TEST(PairwiseLinestringDistanceTest, OnePairCollinearNoIntersect) +{ + using T = TypeParam; + wrapper linestring1_offsets{0}; + wrapper linestring1_points_x{0.0, 1.0}; + wrapper linestring1_points_y{0.0, 1.0}; + wrapper linestring2_offsets{0}; + wrapper linestring2_points_x{2.0, 3.0}; + wrapper linestring2_points_y{2.0, 3.0}; + + wrapper expected{1.4142135623730951}; + + auto got = pairwise_linestring_distance(column_view(linestring1_offsets), + linestring1_points_x, + linestring1_points_y, + column_view(linestring2_offsets), + linestring2_points_x, + linestring2_points_y); + expect_columns_equivalent(expected, *got, verbosity); +} + +TYPED_TEST(PairwiseLinestringDistanceTest, OnePairDegenerateCollinearIntersect) +{ + using T = TypeParam; + wrapper linestring1_offsets{0}; + wrapper linestring1_points_x{0.0, 2.0}; + wrapper linestring1_points_y{0.0, 2.0}; + wrapper linestring2_offsets{0}; + wrapper linestring2_points_x{1.0, 3.0}; + wrapper linestring2_points_y{1.0, 3.0}; + + wrapper expected{0.0}; + + auto got = pairwise_linestring_distance(column_view(linestring1_offsets), + linestring1_points_x, + linestring1_points_y, + column_view(linestring2_offsets), + linestring2_points_x, + linestring2_points_y); + expect_columns_equivalent(expected, *got, verbosity); +} + +TYPED_TEST(PairwiseLinestringDistanceTest, OnePairRandom1) { using T = TypeParam; wrapper linestring1_offsets{0}; @@ -263,6 +326,28 @@ TYPED_TEST(PairwiseLinestringDistanceTest, OnePairGeolife) expect_columns_equivalent(expected, *got, verbosity); } +TYPED_TEST(PairwiseLinestringDistanceTest, OnePairGeolife2) +{ + // Example extracted from a pair of trajectry in geolife dataset + using T = TypeParam; + wrapper linestring1_offsets{0}; + wrapper linestring1_points_x{39.9752666666667, 39.9752666666667}; + wrapper linestring1_points_y{116.334316666667, 116.334533333333}; + wrapper linestring2_offsets{0}; + wrapper linestring2_points_x{39.9752666666667, 39.9752666666667}; + wrapper linestring2_points_y{116.323966666667, 116.3236}; + + wrapper expected = + std::is_same_v ? wrapper{0.01035308837890625} : wrapper{0.010349999999988313}; + auto got = pairwise_linestring_distance(column_view(linestring1_offsets), + linestring1_points_x, + linestring1_points_y, + column_view(linestring2_offsets), + linestring2_points_x, + linestring2_points_y); + expect_columns_equivalent(expected, *got, verbosity); +} + TYPED_TEST(PairwiseLinestringDistanceTest, TwoPairs) { using T = TypeParam; From 90a5102f6fac4ecebe316a5087167957d5734229 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 27 Apr 2022 14:51:20 -0700 Subject: [PATCH 32/67] Multiple update with docstrings. Co-authored-by: Mark Harris --- .../distances/linestring_distance.hpp | 26 +++++++++---------- cpp/src/spatial/linestring_distance.cu | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cpp/include/cuspatial/distances/linestring_distance.hpp b/cpp/include/cuspatial/distances/linestring_distance.hpp index 918647393..9f647a5b6 100644 --- a/cpp/include/cuspatial/distances/linestring_distance.hpp +++ b/cpp/include/cuspatial/distances/linestring_distance.hpp @@ -26,24 +26,24 @@ namespace cuspatial { /** * @brief Compute shortest distance between pairs of linestrings * - * The shortest distances between two linestrings are defined as the shortest distances - * between all pairs of segments between the linestrings. If any of the segments intersects, + * The shortest distance between two linestrings is defined as the shortest distance + * between all pairs of segments of the two linestrings. If any of the segments intersect, * the distance is 0. * - * @param linestring1_offsets Indices to the start coordinate to the first linestring of the pair - * @param linestring1_points_x x component for points consisting linestrings 1 - * @param linestring1_points_y y component for points consisting linestrings 1 - * @param linestring2_offsets Indices to the start coordinate to the second linestring of the pair - * @param linestring2_points_x x component for points consisting linestrings 2 - * @param linestring2_points_y y component for points consisting linestrings 2 - * @param mr Device memory resource used to allocate the returned column's device memory - * @return A column of shortest distances between the pair of linestrings + * @param linestring1_offsets Indices of the first point of the first linestring of each pair. + * @param linestring1_points_x x-components of points in the first linestring of each pair. + * @param linestring1_points_y y-component of points in the first linestring of each pair. + * @param linestring2_offsets Indices of the first point of the second linestring of each pair. + * @param linestring2_points_x x-component of points in the first linestring of each pair. + * @param linestring2_points_y y-component of points in the first linestring of each pair. + * @param mr Device memory resource used to allocate the returned column's device memory. + * @return A column of shortest distances between each pair of linestrings. * * @throw cuspatial::logic_error if `linestring1_offsets.size() != linestring2_offsets.size()` - * @throw cuspatial::logic_error if size mismatch between the x, y components of the linestring + * @throw cuspatial::logic_error if there is a size mismatch between the x- and y-coordinates of the linestring * points. - * @throw cuspatial::logic_error if any of the point arrays have mismatch types. - * @throw cuspatial::logic_error if any linestring has less than 2 end points. + * @throw cuspatial::logic_error if any of the point arrays have mismatched types. + * @throw cuspatial::logic_error if any linestring has fewer than 2 points. * */ std::unique_ptr pairwise_linestring_distance( diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index c92779ec5..a62071cad 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -91,7 +91,7 @@ double __device__ segment_distance_no_intersect_or_collinear(coord_2d const& /** * @brief Computes shortest distance between two segments. * - * If two segment intersects, distance is 0. Otherwise compute the shortest point + * If two segments intersect, the distance is 0. Otherwise compute the shortest point * to segment distance. */ template From 77b27a9fe2d750a1826d5812104d77938676576e Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 27 Apr 2022 15:05:37 -0700 Subject: [PATCH 33/67] style fix --- cpp/include/cuspatial/distances/linestring_distance.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/include/cuspatial/distances/linestring_distance.hpp b/cpp/include/cuspatial/distances/linestring_distance.hpp index 9f647a5b6..04617b4a6 100644 --- a/cpp/include/cuspatial/distances/linestring_distance.hpp +++ b/cpp/include/cuspatial/distances/linestring_distance.hpp @@ -40,8 +40,8 @@ namespace cuspatial { * @return A column of shortest distances between each pair of linestrings. * * @throw cuspatial::logic_error if `linestring1_offsets.size() != linestring2_offsets.size()` - * @throw cuspatial::logic_error if there is a size mismatch between the x- and y-coordinates of the linestring - * points. + * @throw cuspatial::logic_error if there is a size mismatch between the x- and y-coordinates of the + * linestring points. * @throw cuspatial::logic_error if any of the point arrays have mismatched types. * @throw cuspatial::logic_error if any linestring has fewer than 2 points. * From 468a5e92600362f62f2d1ae0c2a62a5bbb34da71 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 27 Apr 2022 15:11:32 -0700 Subject: [PATCH 34/67] Switch to snake case. --- cpp/src/spatial/linestring_distance.cu | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index a62071cad..08fa08db0 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -168,7 +168,7 @@ void __global__ pairwise_linestring_distance_kernel(OffsetIterator linestring1_o { using T = typename std::iterator_traits::value_type; - auto const p1Idx = threadIdx.x + blockIdx.x * blockDim.x; + auto const p1_idx = threadIdx.x + blockIdx.x * blockDim.x; cudf::size_type const num_linestrings = thrust::distance(linestring1_offsets_begin, linestring1_offsets_end); cudf::size_type const linestring1_num_points = @@ -176,33 +176,33 @@ void __global__ pairwise_linestring_distance_kernel(OffsetIterator linestring1_o cudf::size_type const linestring2_num_points = thrust::distance(linestring2_points_xs_begin, linestring2_points_xs_end); - if (p1Idx >= linestring1_num_points) { return; } + if (p1_idx >= linestring1_num_points) { return; } cudf::size_type const linestring_idx = - thrust::distance( - linestring1_offsets_begin, - thrust::upper_bound(thrust::seq, linestring1_offsets_begin, linestring1_offsets_end, p1Idx)) - + thrust::distance(linestring1_offsets_begin, + thrust::upper_bound( + thrust::seq, linestring1_offsets_begin, linestring1_offsets_end, p1_idx)) - 1; - cudf::size_type ls1End = endpoint_index_of_linestring( + cudf::size_type ls1_end = endpoint_index_of_linestring( linestring_idx, linestring1_offsets_begin, num_linestrings, linestring1_num_points); - if (p1Idx == ls1End) { + if (p1_idx == ls1_end) { // Current point is the end point of the line string. return; } - cudf::size_type ls2Start = *(linestring2_offsets_begin + linestring_idx); - cudf::size_type ls2End = endpoint_index_of_linestring( + cudf::size_type ls2_start = *(linestring2_offsets_begin + linestring_idx); + cudf::size_type ls2_end = endpoint_index_of_linestring( linestring_idx, linestring2_offsets_begin, num_linestrings, linestring2_num_points); - coord_2d A{linestring1_points_xs_begin[p1Idx], linestring1_points_ys_begin[p1Idx]}; - coord_2d B{linestring1_points_xs_begin[p1Idx + 1], linestring1_points_ys_begin[p1Idx + 1]}; + coord_2d A{linestring1_points_xs_begin[p1_idx], linestring1_points_ys_begin[p1_idx]}; + coord_2d B{linestring1_points_xs_begin[p1_idx + 1], linestring1_points_ys_begin[p1_idx + 1]}; double min_distance = std::numeric_limits::max(); - for (cudf::size_type p2Idx = ls2Start; p2Idx < ls2End; p2Idx++) { - coord_2d C{linestring2_points_xs_begin[p2Idx], linestring2_points_ys_begin[p2Idx]}; - coord_2d D{linestring2_points_xs_begin[p2Idx + 1], linestring2_points_ys_begin[p2Idx + 1]}; + for (cudf::size_type p2_idx = ls2_start; p2_idx < ls2_end; p2_idx++) { + coord_2d C{linestring2_points_xs_begin[p2_idx], linestring2_points_ys_begin[p2_idx]}; + coord_2d D{linestring2_points_xs_begin[p2_idx + 1], linestring2_points_ys_begin[p2_idx + 1]}; min_distance = std::min(min_distance, segment_distance(A, B, C, D)); } atomicMin(distances + linestring_idx, static_cast(min_distance)); From c5df642e5766fd32a7d75f192deb78aad5cdd8a4 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 27 Apr 2022 15:25:44 -0700 Subject: [PATCH 35/67] fix typo --- cpp/src/spatial/linestring_distance.cu | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index 08fa08db0..7db41dab0 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -215,7 +215,7 @@ namespace detail { /** * @brief Functor that launches the kernel to compute pairwise linestring distances. */ -struct pariwise_linestring_distance_functor { +struct pairwise_linestring_distance_functor { template std::enable_if_t::value, std::unique_ptr> operator()( cudf::device_span linestring1_offsets, @@ -321,7 +321,7 @@ std::unique_ptr pairwise_linestring_distance( "Each item of linestring2 should contain at least 2 end points."); return cudf::type_dispatcher(linestring1_points_x.type(), - pariwise_linestring_distance_functor{}, + pairwise_linestring_distance_functor{}, linestring1_offsets, linestring1_points_x, linestring1_points_y, From 777a01a0361a7498ef6a956e9628a7c5d05eed2f Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 27 Apr 2022 17:33:38 -0700 Subject: [PATCH 36/67] Rename coord_2d to vec_2d. Add vec_2d operators, rewrites `point_to_segment_distance` to `point_to_segment_distance_squared` and make use of vector operators. --- cpp/include/cuspatial/types.hpp | 2 +- cpp/src/spatial/linestring_distance.cu | 96 +++++++++++++++++++------- 2 files changed, 71 insertions(+), 27 deletions(-) diff --git a/cpp/include/cuspatial/types.hpp b/cpp/include/cuspatial/types.hpp index bf1ca5bc9..d3837e99c 100644 --- a/cpp/include/cuspatial/types.hpp +++ b/cpp/include/cuspatial/types.hpp @@ -36,7 +36,7 @@ struct location_3d { * @tparam T the base type for the coordinates */ template -struct coord_2d { +struct vec_2d { T x; T y; }; diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index 7db41dab0..b9fef003f 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -38,6 +38,48 @@ namespace cuspatial { namespace { +template +vec_2d __device__ operator+(vec_2d const& a, vec_2d const& b) +{ + return vec_2d{a.x + b.x, a.y + b.y}; +} + +template +vec_2d __device__ operator-(vec_2d const& a, vec_2d const& b) +{ + return vec_2d{a.x - b.x, a.y - b.y}; +} + +template +vec_2d __device__ operator*(vec_2d const& a, vec_2d const& b) +{ + return vec_2d{a.x * b.x, a.y * b.y}; +} + +template +vec_2d __device__ operator*(vec_2d vec, T const& r) +{ + return vec_2d{vec.x * r, vec.y * r}; +} + +template +vec_2d __device__ operator*(T const& r, vec_2d vec) +{ + return vec * r; +} + +template +T __device__ dot(vec_2d const& a, vec_2d const& b) +{ + return a.x * b.x + a.y * b.y; +} + +template +T __device__ det(vec_2d const& a, vec_2d const& b) +{ + return a.x * b.y - a.y * b.x; +} + /** @brief Get the index that is one-past the end point of linestring at @p linestring_idx * * @note The last endpoint of the linestring is not included in the offset array, thus @@ -60,32 +102,36 @@ endpoint_index_of_linestring(cudf::size_type const& linestring_idx, * @brief Computes shortest distance between @p C and segment @p A @p B */ template -double __device__ point_to_segment_distance(coord_2d const& C, - coord_2d const& A, - coord_2d const& B) +T __device__ point_to_segment_distance_squared(vec_2d const& c, + vec_2d const& a, + vec_2d const& b) { - double L_squared = (A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y); - if (L_squared == 0) { return hypot(C.x - A.x, C.y - A.y); } - double r = ((C.x - A.x) * (B.x - A.x) + (C.y - A.y) * (B.y - A.y)) / L_squared; - if (r <= 0 or r >= 1) { - return std::min(hypot(C.x - A.x, C.y - A.y), hypot(C.x - B.x, C.y - B.y)); - } - double Px = A.x + r * (B.x - A.x); - double Py = A.y + r * (B.y - A.y); - return hypot(C.x - Px, C.y - Py); + vec_2d ab = b - a; + auto ac = c - a; + auto bc = c - b; + T l_squared = dot(ab, ab); + if (l_squared == 0) { return dot(ac, ac); } + T r = dot(ac, ab) / l_squared; + if (r <= 0 or r >= 1) { return std::min(dot(ac, ac), dot(bc, bc)); } + auto p = a + r * ab; + auto pc = c - p; + return dot(pc, pc); } /** * @brief Computes shortest distance between two segments that doesn't intersect. */ template -double __device__ segment_distance_no_intersect_or_collinear(coord_2d const& A, - coord_2d const& B, - coord_2d const& C, - coord_2d const& D) +double __device__ segment_distance_no_intersect_or_collinear(vec_2d const& A, + vec_2d const& B, + vec_2d const& C, + vec_2d const& D) { - return std::min(std::min(point_to_segment_distance(A, C, D), point_to_segment_distance(B, C, D)), - std::min(point_to_segment_distance(C, A, B), point_to_segment_distance(D, A, B))); + auto dist_sqr = std::min(std::min(point_to_segment_distance_squared(A, C, D), + point_to_segment_distance_squared(B, C, D)), + std::min(point_to_segment_distance_squared(C, A, B), + point_to_segment_distance_squared(D, A, B))); + return std::sqrt(dist_sqr); } /** @@ -95,10 +141,8 @@ double __device__ segment_distance_no_intersect_or_collinear(coord_2d const& * to segment distance. */ template -double __device__ segment_distance(coord_2d const& A, - coord_2d const& B, - coord_2d const& C, - coord_2d const& D) +double __device__ +segment_distance(vec_2d const& A, vec_2d const& B, vec_2d const& C, vec_2d const& D) { double r_denom = (B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x); double r_numer = (A.y - C.y) * (D.x - C.x) - (A.x - C.x) * (D.y - C.y); @@ -196,13 +240,13 @@ void __global__ pairwise_linestring_distance_kernel(OffsetIterator linestring1_o cudf::size_type ls2_end = endpoint_index_of_linestring( linestring_idx, linestring2_offsets_begin, num_linestrings, linestring2_num_points); - coord_2d A{linestring1_points_xs_begin[p1_idx], linestring1_points_ys_begin[p1_idx]}; - coord_2d B{linestring1_points_xs_begin[p1_idx + 1], linestring1_points_ys_begin[p1_idx + 1]}; + vec_2d A{linestring1_points_xs_begin[p1_idx], linestring1_points_ys_begin[p1_idx]}; + vec_2d B{linestring1_points_xs_begin[p1_idx + 1], linestring1_points_ys_begin[p1_idx + 1]}; double min_distance = std::numeric_limits::max(); for (cudf::size_type p2_idx = ls2_start; p2_idx < ls2_end; p2_idx++) { - coord_2d C{linestring2_points_xs_begin[p2_idx], linestring2_points_ys_begin[p2_idx]}; - coord_2d D{linestring2_points_xs_begin[p2_idx + 1], linestring2_points_ys_begin[p2_idx + 1]}; + vec_2d C{linestring2_points_xs_begin[p2_idx], linestring2_points_ys_begin[p2_idx]}; + vec_2d D{linestring2_points_xs_begin[p2_idx + 1], linestring2_points_ys_begin[p2_idx + 1]}; min_distance = std::min(min_distance, segment_distance(A, B, C, D)); } atomicMin(distances + linestring_idx, static_cast(min_distance)); From aa938299e624ba2c7e630cc030c31592f369a315 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 28 Apr 2022 11:39:55 -0700 Subject: [PATCH 37/67] Merge master from upstream --- .gitignore | 6 + CHANGELOG.md | 19 +- build.sh | 4 +- ci/release/update-version.sh | 3 + cpp/CMakeLists.txt | 111 ++++--- cpp/cmake/Modules/ConfigureCUDA.cmake | 13 - cpp/cmake/Modules/EvalGPUArchs.cmake | 70 ----- cpp/cmake/Modules/SetGPUArchs.cmake | 55 ---- cpp/cmake/cuspatial-build-config.cmake.in | 60 ---- cpp/cmake/cuspatial-config.cmake.in | 75 ----- cpp/cmake/thirdparty/CUSPATIAL_GetCPM.cmake | 33 --- cpp/cmake/thirdparty/CUSPATIAL_GetCUDF.cmake | 24 +- cpp/doc/libcuspatial_refactoring_guide.md | 280 ++++++++++++++++++ cpp/include/cuspatial/error.hpp | 5 +- .../experimental/detail/haversine.cuh | 89 ++++++ .../cuspatial/experimental/haversine.cuh | 85 ++++++ .../cuspatial/experimental/type_utils.hpp | 92 ++++++ cpp/include/cuspatial/types.hpp | 51 ++-- cpp/src/spatial/haversine.cu | 58 +--- cpp/tests/CMakeLists.txt | 6 +- .../experimental/spatial/haversine_test.cu | 143 +++++++++ fetch_rapids.cmake | 17 ++ java/src/main/native/CMakeLists.txt | 35 +-- 23 files changed, 862 insertions(+), 472 deletions(-) delete mode 100644 cpp/cmake/Modules/EvalGPUArchs.cmake delete mode 100644 cpp/cmake/Modules/SetGPUArchs.cmake delete mode 100644 cpp/cmake/cuspatial-build-config.cmake.in delete mode 100644 cpp/cmake/cuspatial-config.cmake.in delete mode 100644 cpp/cmake/thirdparty/CUSPATIAL_GetCPM.cmake create mode 100644 cpp/doc/libcuspatial_refactoring_guide.md create mode 100644 cpp/include/cuspatial/experimental/detail/haversine.cuh create mode 100644 cpp/include/cuspatial/experimental/haversine.cuh create mode 100644 cpp/include/cuspatial/experimental/type_utils.hpp create mode 100644 cpp/tests/experimental/spatial/haversine_test.cu create mode 100644 fetch_rapids.cmake diff --git a/.gitignore b/.gitignore index e5628cf20..aaa293799 100644 --- a/.gitignore +++ b/.gitignore @@ -137,3 +137,9 @@ ENV/ # mypy .mypy_cache/ + +# notebook/example generated files +notebooks/taxi2016.csv +notebooks/taxi2017.csv +notebooks/tzones_lonlat.json +notebooks/cu_taxi_zones.* diff --git a/CHANGELOG.md b/CHANGELOG.md index bad64deb6..bdf74f5a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,24 @@ Please see https://github.com/rapidsai/cuspatial/releases/tag/v22.06.00a for the latest changes to this development branch. -# cuSpatial 22.04.00 (Date TBD) +# cuSpatial 22.04.00 (6 Apr 2022) -Please see https://github.com/rapidsai/cuspatial/releases/tag/v22.04.00a for the latest changes to this development branch. +## 🐛 Bug Fixes + +- Swap NumericalColumn.values_host for now removed to_array ([#485](https://github.com/rapidsai/cuspatial/pull/485)) [@vyasr](https://github.com/vyasr) + +## 📖 Documentation + +- Improve point_in_polygon documentation regarding poly_ring_offsets ([#497](https://github.com/rapidsai/cuspatial/pull/497)) [@harrism](https://github.com/harrism) +- Fix documentation of return type of quadtree_point_in_polygon ([#490](https://github.com/rapidsai/cuspatial/pull/490)) [@harrism](https://github.com/harrism) + +## 🛠️ Improvements + +- Temporarily disable new `ops-bot` functionality ([#501](https://github.com/rapidsai/cuspatial/pull/501)) [@ajschmidt8](https://github.com/ajschmidt8) +- Pin gtest/gmock to 1.10.0 in dev envs ([#498](https://github.com/rapidsai/cuspatial/pull/498)) [@trxcllnt](https://github.com/trxcllnt) +- Add `.github/ops-bot.yaml` config file ([#496](https://github.com/rapidsai/cuspatial/pull/496)) [@ajschmidt8](https://github.com/ajschmidt8) +- Add CMake `install` rule for tests ([#488](https://github.com/rapidsai/cuspatial/pull/488)) [@ajschmidt8](https://github.com/ajschmidt8) +- replace `ccache` with `sccache` ([#483](https://github.com/rapidsai/cuspatial/pull/483)) [@AyodeAwe](https://github.com/AyodeAwe) # cuSpatial 22.02.00 (2 Feb 2022) diff --git a/build.sh b/build.sh index eafe261d1..b6546b399 100755 --- a/build.sh +++ b/build.sh @@ -107,10 +107,10 @@ if hasArg clean; then fi if (( ${BUILD_ALL_GPU_ARCH} == 0 )); then - CUSPATIAL_CMAKE_CUDA_ARCHITECTURES="-DCMAKE_CUDA_ARCHITECTURES=" + CUSPATIAL_CMAKE_CUDA_ARCHITECTURES="-DCMAKE_CUDA_ARCHITECTURES=NATIVE" echo "Building for the architecture of the GPU in the system..." else - CUSPATIAL_CMAKE_CUDA_ARCHITECTURES="" + CUSPATIAL_CMAKE_CUDA_ARCHITECTURES="-DCMAKE_CUDA_ARCHITECTURES=ALL" echo "Building for *ALL* supported GPU architectures..." fi diff --git a/ci/release/update-version.sh b/ci/release/update-version.sh index 5d5befcb7..e9452bfcd 100755 --- a/ci/release/update-version.sh +++ b/ci/release/update-version.sh @@ -36,6 +36,9 @@ sed_runner 's/'"CUSPATIAL VERSION .* LANGUAGES"'/'"CUSPATIAL VERSION ${NEXT_FULL sed_runner 's/version = .*/version = '"'${NEXT_SHORT_TAG}'"'/g' docs/source/conf.py sed_runner 's/release = .*/release = '"'${NEXT_FULL_TAG}'"'/g' docs/source/conf.py +# rapids-cmake version +sed_runner 's/'"branch-.*\/RAPIDS.cmake"'/'"branch-${NEXT_SHORT_TAG}\/RAPIDS.cmake"'/g' fetch_rapids.cmake + # bump cudf for FILE in conda/environments/*.yml; do sed_runner "s/cudf=${CURRENT_SHORT_TAG}/cudf=${NEXT_SHORT_TAG}/g" ${FILE}; diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 81a572ec7..375403f1e 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -14,21 +14,18 @@ # limitations under the License. #============================================================================= -cmake_minimum_required(VERSION 3.18 FATAL_ERROR) - -# If `CMAKE_CUDA_ARCHITECTURES` is not defined, build for all supported architectures. If -# `CMAKE_CUDA_ARCHITECTURES` is set to an empty string (""), build for only the current -# architecture. If `CMAKE_CUDA_ARCHITECTURES` is specified by the user, use user setting. - -# This needs to be run before enabling the CUDA language due to the default initialization behavior -# of `CMAKE_CUDA_ARCHITECTURES`, https://gitlab.kitware.com/cmake/cmake/-/issues/21302 -if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES OR CMAKE_CUDA_ARCHITECTURES STREQUAL "ALL") - set(CUSPATIAL_BUILD_FOR_ALL_ARCHS TRUE) -elseif(CMAKE_CUDA_ARCHITECTURES STREQUAL "") - set(CUSPATIAL_BUILD_FOR_DETECTED_ARCHS TRUE) -endif() +cmake_minimum_required(VERSION 3.20.1 FATAL_ERROR) + +include(../fetch_rapids.cmake) +include(rapids-cmake) +include(rapids-cpm) +include(rapids-cuda) +include(rapids-export) +include(rapids-find) -project(CUSPATIAL VERSION 22.06.00 LANGUAGES C CXX) +rapids_cuda_init_architectures(CUSPATIAL) + +project(CUSPATIAL VERSION 22.06.00 LANGUAGES C CXX CUDA) # Needed because GoogleBenchmark changes the state of FindThreads.cmake, # causing subsequent runs to have different values for the `Threads::Threads` target. @@ -58,7 +55,7 @@ message(STATUS "CUSPATIAL: Enable the -lineinfo option for nvcc (useful for cuda message(STATUS "CUSPATIAL: Statically link the CUDA runtime: ${CUDA_STATIC_RUNTIME}") # Set a default build type if none was specified -set(DEFAULT_BUILD_TYPE "Release") +rapids_cmake_build_type("Release") set(CUSPATIAL_BUILD_TESTS ${BUILD_TESTS}) set(CUSPATIAL_BUILD_BENCHMARKS ${BUILD_BENCHMARKS}) @@ -67,15 +64,6 @@ set(CUSPATIAL_CUDA_FLAGS "") set(CUSPATIAL_CXX_DEFINITIONS "") set(CUSPATIAL_CUDA_DEFINITIONS "") -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - message(STATUS "Setting build type to '${DEFAULT_BUILD_TYPE}' since none specified.") - set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE - STRING "Choose the type of build." FORCE) - # Set the possible values of build type for cmake-gui - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Release" "MinSizeRel" "RelWithDebInfo") -endif() - # Set RMM logging level set(RMM_LOGGING_LEVEL "INFO" CACHE STRING "Choose the logging level.") set_property(CACHE RMM_LOGGING_LEVEL PROPERTY STRINGS "TRACE" "DEBUG" "INFO" "WARN" "ERROR" "CRITICAL" "OFF") @@ -84,21 +72,16 @@ message(STATUS "CUSPATIAL: RMM_LOGGING_LEVEL = '${RMM_LOGGING_LEVEL}'.") ################################################################################################### # - conda environment ----------------------------------------------------------------------------- -if("$ENV{CONDA_BUILD}" STREQUAL "1") - set(CMAKE_PREFIX_PATH "$ENV{BUILD_PREFIX};$ENV{PREFIX};${CMAKE_PREFIX_PATH}") - set(CONDA_INCLUDE_DIRS "$ENV{BUILD_PREFIX}/include" "$ENV{PREFIX}/include") - set(CONDA_LINK_DIRS "$ENV{BUILD_PREFIX}/lib" "$ENV{PREFIX}/lib") - message(VERBOSE "CUSPATIAL: Conda build detected, CMAKE_PREFIX_PATH set to: ${CMAKE_PREFIX_PATH}") -elseif(DEFINED ENV{CONDA_PREFIX}) - set(CMAKE_PREFIX_PATH "$ENV{CONDA_PREFIX};${CMAKE_PREFIX_PATH}") - set(CONDA_INCLUDE_DIRS "$ENV{CONDA_PREFIX}/include") - set(CONDA_LINK_DIRS "$ENV{CONDA_PREFIX}/lib") - message(VERBOSE "CUSPATIAL: Conda environment detected, CMAKE_PREFIX_PATH set to: ${CMAKE_PREFIX_PATH}") -endif() +rapids_cmake_support_conda_env(conda_env MODIFY_PREFIX_PATH) ################################################################################################### # - compiler options ------------------------------------------------------------------------------ +rapids_find_package( + CUDAToolkit REQUIRED + BUILD_EXPORT_SET cuspatial-exports + INSTALL_EXPORT_SET cuspatial-exports +) # * find CUDAToolkit package # * determine GPU architectures # * enable the CMake CUDA language @@ -109,9 +92,13 @@ include(cmake/Modules/ConfigureCUDA.cmake) # - dependencies ---------------------------------------------------------------------------------- # find gdal -find_package(GDAL REQUIRED) +rapids_find_package( + GDAL REQUIRED + BUILD_EXPORT_SET cuspatial-exports + INSTALL_EXPORT_SET cuspatial-exports +) # add third party dependencies using CPM -include(cmake/thirdparty/CUSPATIAL_GetCPM.cmake) +rapids_cpm_init() # find or add cuDF include(cmake/thirdparty/CUSPATIAL_GetCUDF.cmake) @@ -234,37 +221,43 @@ set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME cuspatial) install(TARGETS cuspatial DESTINATION lib - EXPORT cuspatial-targets) + EXPORT cuspatial-exports) install(DIRECTORY ${CUSPATIAL_SOURCE_DIR}/include/cuspatial DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -include(CMakePackageConfigHelpers) +set(doc_string + [=[ +Provide targets for the cuSpatial library. -configure_package_config_file(cmake/cuspatial-config.cmake.in "${CUSPATIAL_BINARY_DIR}/cmake/cuspatial-config.cmake" - INSTALL_DESTINATION "${INSTALL_CONFIGDIR}") +cuSpatial is a GPU-accelerated library for spatial data management and analytics. -write_basic_package_version_file("${CUSPATIAL_BINARY_DIR}/cmake/cuspatial-config-version.cmake" - COMPATIBILITY SameMinorVersion) +Imported Targets +^^^^^^^^^^^^^^^^ -install(FILES "${CUSPATIAL_BINARY_DIR}/cmake/cuspatial-config.cmake" - "${CUSPATIAL_BINARY_DIR}/cmake/cuspatial-config-version.cmake" - DESTINATION "${INSTALL_CONFIGDIR}") +If cuspatial is found, this module defines the following IMPORTED GLOBAL +targets: -install(EXPORT cuspatial-targets - FILE cuspatial-targets.cmake - NAMESPACE cuspatial:: - DESTINATION "${INSTALL_CONFIGDIR}") + cuspatial::cuspatial - The main cuspatial library. + ]=] +) -################################################################################################ -# - build export ------------------------------------------------------------------------------- +rapids_export( + INSTALL cuspatial + EXPORT_SET cuspatial-exports + GLOBAL_TARGETS cuspatial + NAMESPACE cuspatial:: + DOCUMENTATION doc_string +) -configure_package_config_file(cmake/cuspatial-build-config.cmake.in ${CUSPATIAL_BINARY_DIR}/cuspatial-config.cmake - INSTALL_DESTINATION ${CUSPATIAL_BINARY_DIR}) -write_basic_package_version_file(${CUSPATIAL_BINARY_DIR}/cuspatial-config-version.cmake - COMPATIBILITY SameMinorVersion) +################################################################################################ +# - build export ------------------------------------------------------------------------------- -export(EXPORT cuspatial-targets - FILE ${CUSPATIAL_BINARY_DIR}/cuspatial-targets.cmake - NAMESPACE cuspatial::) +rapids_export( + BUILD cuspatial + EXPORT_SET cuspatial-exports + GLOBAL_TARGETS cuspatial + NAMESPACE cuspatial:: + DOCUMENTATION doc_string +) diff --git a/cpp/cmake/Modules/ConfigureCUDA.cmake b/cpp/cmake/Modules/ConfigureCUDA.cmake index e17464c7c..0c04f5240 100644 --- a/cpp/cmake/Modules/ConfigureCUDA.cmake +++ b/cpp/cmake/Modules/ConfigureCUDA.cmake @@ -14,19 +14,6 @@ # limitations under the License. #============================================================================= -# Find the CUDAToolkit -find_package(CUDAToolkit REQUIRED) - -# Auto-detect available GPU compute architectures -include(${CMAKE_CURRENT_LIST_DIR}/SetGPUArchs.cmake) -message(STATUS "CUSPATIAL: Building CUSPATIAL for GPU architectures: ${CMAKE_CUDA_ARCHITECTURES}") - -# Must come after find_package(CUDAToolkit) because we symlink -# sccache as a compiler front-end for nvcc in gpuCI CPU builds. -# Must also come after we detect and potentially rewrite -# CMAKE_CUDA_ARCHITECTURES -enable_language(CUDA) - if(CMAKE_COMPILER_IS_GNUCXX) list(APPEND CUSPATIAL_CXX_FLAGS -Wall -Werror -Wno-unknown-pragmas -Wno-error=deprecated-declarations) if(CUSPATIAL_BUILD_TESTS OR CUSPATIAL_BUILD_BENCHMARKS) diff --git a/cpp/cmake/Modules/EvalGPUArchs.cmake b/cpp/cmake/Modules/EvalGPUArchs.cmake deleted file mode 100644 index d0d833bea..000000000 --- a/cpp/cmake/Modules/EvalGPUArchs.cmake +++ /dev/null @@ -1,70 +0,0 @@ -#============================================================================= -# Copyright (c) 2019-2021, 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. -#============================================================================= - -# Unset this first in case it's set to -set(CMAKE_CUDA_ARCHITECTURES OFF) - -# Enable CUDA so we can invoke nvcc -enable_language(CUDA) - -# Function uses the CUDA runtime API to query the compute capability of the device, so if a user -# doesn't pass any architecture options to CMake we only build the current architecture -function(evaluate_gpu_archs gpu_archs) - set(eval_file ${PROJECT_BINARY_DIR}/eval_gpu_archs.cu) - set(eval_exe ${PROJECT_BINARY_DIR}/eval_gpu_archs) - set(error_file ${PROJECT_BINARY_DIR}/eval_gpu_archs.stderr.log) - file( - WRITE ${eval_file} - " -#include -#include -#include -using namespace std; -int main(int argc, char** argv) { - set archs; - int nDevices; - if((cudaGetDeviceCount(&nDevices) == cudaSuccess) && (nDevices > 0)) { - for(int dev=0;dev::value_type, + class T = typename Location::value_type> +OutputIt haversine_distance(LonLatItA a_lonlat_first, + LonLatItA a_lonlat_last, + LonLatItB b_lonlat_first, + OutputIt distance_first, + T const radius = EARTH_RADIUS_KM, + rmm::cuda_stream_view stream = rmm::cuda_stream_default); +``` + +There are a few key points to notice. + + 1. The API is very similar to STL algorithms such as `std::transform`. + 2. All array inputs and outputs are iterator type templates. + 3. Longitude/Latitude data is passed as array of structures, using the `cuspatial::lonlat_2d` + type (include/cuspatial/types.hpp). This is enforced using a `static_assert` in the function + body (discussed later). + 4. The `Location` type is a template that is by default equal to the `value_type` of the input + iterators. + 5. The floating point type is a template (`T`) that is by default equal to the `value_type` of + `Location`. + 6. The iterator types for the two input ranges (A and B) are distinct templates. This is crucial + to enable composition of fancy iterators that may be different types for A and B. + 7. The size of the input and output ranges in the example API are equal, so the start and end of + only the A range is provided (`a_lonlat_first` and `a_lonlat_last`). This mirrors STL APIs. + 8. This API returns an iterator to the element past the last element written to the output. This + is inspired by `std::transform`, even though as with `transform`, many uses of + `haversine_distance` will not need this returned iterator. + 9. All APIs that run CUDA device code (including Thrust algorithms) or allocate memory take a CUDA + stream on which to execute the device code and allocate memory. + +## Example Documentation + +Following is the (Doxygen) documentation for the above `cuspatial::haversine_distance`. + +```C++ +/** + * @brief Compute haversine distances between points in set A to the corresponding points in set B. + * + * Computes N haversine distances, where N is `std::distance(a_lonlat_first, a_lonlat_last)`. + * The distance for each `a_lonlat[i]` and `b_lonlat[i]` point pair is assigned to + * `distance_first[i]`. `distance_first` must be an iterator to output storage allocated for N + * distances. + * + * Computed distances will have the same units as `radius`. + * + * https://en.wikipedia.org/wiki/Haversine_formula + * + * @param[in] a_lonlat_first: beginning of range of (longitude, latitude) locations in set A + * @param[in] a_lonlat_last: end of range of (longitude, latitude) locations in set A + * @param[in] b_lonlat_first: beginning of range of (longitude, latitude) locations in set B + * @param[out] distance_first: beginning of output range of haversine distances + * @param[in] radius: radius of the sphere on which the points reside. default: 6371.0 + * (approximate radius of Earth in km) + * @param[in] stream: The CUDA stream on which to perform computations and allocate memory. + * + * @tparam LonLatItA Iterator to input location set A. Must meet the requirements of + * [LegacyRandomAccessIterator][LinkLRAI] and be device-accessible. + * @tparam LonLatItB Iterator to input location set B. Must meet the requirements of + * [LegacyRandomAccessIterator][LinkLRAI] and be device-accessible. + * @tparam OutputIt Output iterator. Must meet the requirements of + * [LegacyRandomAccessIterator][LinkLRAI] and be device-accessible. + * @tparam Location The `value_type` of `LonLatItA` and `LonLatItB`. Must be + * `cuspatial::lonlat_2d`. + * @tparam T The underlying coordinate type. Must be a floating-point type. + * + * @pre `a_lonlat_first` may equal `distance_first`, but the range `[a_lonlat_first, a_lonlat_last)` + * shall not overlap the range `[distance_first, distance_first + (a_lonlat_last - a_lonlat_last)) + * otherwise. + * @pre `b_lonlat_first` may equal `distance_first`, but the range `[b_lonlat_first, b_lonlat_last)` + * shall not overlap the range `[distance_first, distance_first + (b_lonlat_last - b_lonlat_last)) + * otherwise. + * @pre All iterators must have the same `Location` type, with the same underlying floating-point + * coordinate type (e.g. `cuspatial::lonlat_2d`). + * + * @return Output iterator to the element past the last distance computed. + * + * [LinkLRAI]: https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator + * "LegacyRandomAccessIterator" + */ +``` + +Key points: + + 1. Precisely and succinctly documents what the API computes, and provides references. + 2. All parameters and all template parameters are documented. + 3. States the C++ standard iterator concepts that must be implemented, and that iterators must be + device-accessible. + 4. Documents requirements as preconditions using `@pre`. + 5. Uses preconditions to explicitly document what input ranges are allowed to overlap. + 6. Documents the units of any inputs or outputs that have them. + +## cuSpatial libcudf-based C++ API (legacy API) + +This is the existing API, unchanged by refactoring. Here is the existing +`cuspatial::haversine_distance`: + +```C++ +std::unique_ptr haversine_distance( + cudf::column_view const& a_lon, + cudf::column_view const& a_lat, + cudf::column_view const& b_lon, + cudf::column_view const& b_lat, + double const radius = EARTH_RADIUS_KM, + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); +``` + +key points: + 1. All input data are `cudf::column_view`. This is a type-erased container so determining the + type of data must be done at run time. + 2. All inputs are arrays of scalars. Longitude and latitude are separate. + 3. The output is a returned `unique_ptr`. + 4. The output is allocated inside the function using the passed memory resource. + 5. The public API does not take a stream. There is a `detail` version of the API that takes a + stream. This follows libcudf, and may change in the future. + +## File Structure + +For now, libcuspatial APIs should be defined in a header file in the +`cpp/include/cuspatial/experimental/` directory. Later, as we adopt the new API, we will rename +the `experimental` directory. The API header should be named after the API. In the example, +`haversine.hpp` defines the `cuspatial::haversine_distance` API. + +The implementation must also be in a header, but should be in the `cuspatial/experimental/detail` +directory. The implementation should be included from the API definition file, at the end of the +file. Example: + +```C++ +... // declaration of API above this point +#include +``` + +## Namespaces + +Public APIs are in the `cuspatial` namespace. Note that both the header-only API and the libcudf- +based API can live in the same namespace, because they are non-ambiguous (very different +parameters). + +Implementation of the header-only API should be in a `cuspatial::detail` namespace. + +## Implementation + +The main implementation should be in detail headers. + +### Header-only API Implementation + +Because it is a statically typed API, the header-only implementation can be much simpler than the +libcudf-based API, which requires run-time type dispatching. In the case of `haversine_distance`, it is +a simple matter of a few static asserts and dynamic expectation checks, followed by a call to +`thrust::transform` with a custom transform functor. + +```C++ +template ::value_type, + class T = typename Location::value_type> +OutputIt haversine_distance(LonLatItA a_lonlat_first, + LonLatItA a_lonlat_last, + LonLatItB b_lonlat_first, + OutputIt distance_first, + T const radius, + rmm::cuda_stream_view stream) +{ + using LocationB = typename std::iterator_traits::value_type; + static_assert(std::conjunction_v, Location>, + std::is_same, LocationB>>, + "Inputs must be cuspatial::lonlat_2d"); + static_assert( + std::conjunction_v, + std::is_floating_point, + std::is_floating_point::value_type>>, + "Haversine distance supports only floating-point coordinates."); + + CUSPATIAL_EXPECTS(radius > 0, "radius must be positive."); + + return thrust::transform(rmm::exec_policy(stream), + a_lonlat_first, + a_lonlat_last, + b_lonlat_first, + distance_first, + detail::haversine_distance_functor(radius)); +} +``` + +Note that we `static_assert` that the types of the iterator inputs match documented expectations. +We also do a runtime check that the radius is positive. Finally we just call `thrust::transform`, +passing it an instance of `haversine_distance_functor`, which is a function of two `lonlat_2d` +inputs that implements the Haversine distance formula. + +### libcudf-based API Implementation + +The substance of the refactoring is making the libcudf-based API a wrapper around the header-only +API. This mostly involves replacing business logic implementation in the type-dispatched functor +with a call to the header-only API. We also need to convert disjoint latitude and longitude inputs +into `lonlat_2d` structs. This is easily done using the `cuspatial::make_lonlat_iterator` utility +provided in `type_utils.hpp`. + +So, to refactor the libcudf-based API, we remove the following code. + +```C++ +auto input_tuple = thrust::make_tuple(thrust::make_constant_iterator(static_cast(radius)), + a_lon.begin(), + a_lat.begin(), + b_lon.begin(), + b_lat.begin()); + +auto input_iter = thrust::make_zip_iterator(input_tuple); + +thrust::transform(rmm::exec_policy(stream), + input_iter, + input_iter + result->size(), + result->mutable_view().begin(), + [] __device__(auto inputs) { + return calculate_haversine_distance(thrust::get<0>(inputs), + thrust::get<1>(inputs), + thrust::get<2>(inputs), + thrust::get<3>(inputs), + thrust::get<4>(inputs)); + }); +``` + +And replace it with the following code. + +```C++ +auto lonlat_a = cuspatial::make_lonlat_iterator(a_lon.begin(), a_lat.begin()); +auto lonlat_b = cuspatial::make_lonlat_iterator(b_lon.begin(), b_lat.begin()); + +cuspatial::haversine_distance(lonlat_a, + lonlat_a + a_lon.size(), + lonlat_b, + static_cast(*result).begin(), + T{radius}, + stream); +``` + +## Testing + +Existing libcudf-based API tests can mostly be left alone. New tests should be added to exercise +the header-only API separately in case the libcudf-based API is removed. + +Note that tests, like the header-only API, should not depend on libcudf or libcudf_test. The +cuDF-based API made the mistake of depending on libcudf_test, which results in breakages +of cuSpatial sometimes when libcudf_test changes. diff --git a/cpp/include/cuspatial/error.hpp b/cpp/include/cuspatial/error.hpp index 75c3da3b0..b06c6c565 100644 --- a/cpp/include/cuspatial/error.hpp +++ b/cpp/include/cuspatial/error.hpp @@ -41,10 +41,11 @@ struct logic_error : public std::logic_error { struct cuda_error : public std::runtime_error { cuda_error(std::string const& message) : std::runtime_error(message) {} }; + } // namespace cuspatial -#define STRINGIFY_DETAIL(x) #x -#define CUSPATIAL_STRINGIFY(x) STRINGIFY_DETAIL(x) +#define CUSPATIAL_STRINGIFY_DETAIL(x) #x +#define CUSPATIAL_STRINGIFY(x) CUSPATIAL_STRINGIFY_DETAIL(x) /**---------------------------------------------------------------------------* * @brief Macro for checking (pre-)conditions that throws an exception when diff --git a/cpp/include/cuspatial/experimental/detail/haversine.cuh b/cpp/include/cuspatial/experimental/detail/haversine.cuh new file mode 100644 index 000000000..938809683 --- /dev/null +++ b/cpp/include/cuspatial/experimental/detail/haversine.cuh @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2022, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include +#include + +#include +#include + +#include + +namespace cuspatial { + +namespace detail { + +template +struct haversine_distance_functor { + haversine_distance_functor(T radius) : radius_(radius) {} + + __device__ T operator()(lonlat_2d point_a, lonlat_2d point_b) + { + auto ax = point_a.x * DEGREE_TO_RADIAN; + auto ay = point_a.y * DEGREE_TO_RADIAN; + auto bx = point_b.x * DEGREE_TO_RADIAN; + auto by = point_b.y * DEGREE_TO_RADIAN; + + // haversine formula + auto x = (bx - ax) / 2; + auto y = (by - ay) / 2; + auto sinysqrd = sin(y) * sin(y); + auto sinxsqrd = sin(x) * sin(x); + auto scale = cos(ay) * cos(by); + + return 2 * radius_ * asin(sqrt(sinysqrd + sinxsqrd * scale)); + } + + T radius_{}; +}; + +} // namespace detail + +template +OutputIt haversine_distance(LonLatItA a_lonlat_first, + LonLatItA a_lonlat_last, + LonLatItB b_lonlat_first, + OutputIt distance_first, + T const radius, + rmm::cuda_stream_view stream) +{ + using LocationB = typename std::iterator_traits::value_type; + static_assert( + std::conjunction_v, Location>, std::is_same, LocationB>>, + "Inputs must be cuspatial::lonlat_2d"); + static_assert( + std::conjunction_v, + std::is_floating_point, + std::is_floating_point::value_type>>, + "Haversine distance supports only floating-point coordinates."); + + CUSPATIAL_EXPECTS(radius > 0, "radius must be positive."); + + return thrust::transform(rmm::exec_policy(stream), + a_lonlat_first, + a_lonlat_last, + b_lonlat_first, + distance_first, + detail::haversine_distance_functor(radius)); +} + +} // namespace cuspatial diff --git a/cpp/include/cuspatial/experimental/haversine.cuh b/cpp/include/cuspatial/experimental/haversine.cuh new file mode 100644 index 000000000..1820258f7 --- /dev/null +++ b/cpp/include/cuspatial/experimental/haversine.cuh @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2022, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include + +#include + +namespace cuspatial { + +/** + * @brief Compute haversine distances between points in set A to the corresponding points in set B. + * + * Computes N haversine distances, where N is `std::distance(a_lonlat_first, a_lonlat_last)`. + * The distance for each `a_lonlat[i]` and `b_lonlat[i]` point pair is assigned to + * `distance_first[i]`. `distance_first` must be an iterator to output storage allocated for N + * distances. + * + * Computed distances will have the same units as `radius`. + * + * https://en.wikipedia.org/wiki/Haversine_formula + * + * @param[in] a_lonlat_first: beginning of range of (longitude, latitude) locations in set A + * @param[in] a_lonlat_last: end of range of (longitude, latitude) locations in set A + * @param[in] b_lonlat_first: beginning of range of (longitude, latitude) locations in set B + * @param[out] distance_first: beginning of output range of haversine distances + * @param[in] radius: radius of the sphere on which the points reside. default: 6371.0 + * (approximate radius of Earth in km) + * @param[in] stream: The CUDA stream on which to perform computations and allocate memory. + * + * @tparam LonLatItA Iterator to input location set A. Must meet the requirements of + * [LegacyRandomAccessIterator][LinkLRAI] and be device-accessible. + * @tparam LonLatItB Iterator to input location set B. Must meet the requirements of + * [LegacyRandomAccessIterator][LinkLRAI] and be device-accessible. + * @tparam OutputIt Output iterator. Must meet the requirements of + * [LegacyRandomAccessIterator][LinkLRAI] and be device-accessible. + * @tparam Location The `value_type` of `LonLatItA` and `LonLatItB`. Must be + * `cuspatial::lonlat_2d`. + * @tparam T The underlying coordinate type. Must be a floating-point type. + * + * @pre `a_lonlat_first` may equal `distance_first`, but the range `[a_lonlat_first, a_lonlat_last)` + * shall not overlap the range `[distance_first, distance_first + (a_lonlat_last - a_lonlat_last)) + * otherwise. + * @pre `b_lonlat_first` may equal `distance_first`, but the range `[b_lonlat_first, b_lonlat_last)` + * shall not overlap the range `[distance_first, distance_first + (b_lonlat_last - b_lonlat_last)) + * otherwise. + * @pre All iterators must have the same `Location` type, with the same underlying floating-point + * coordinate type (e.g. `cuspatial::lonlat_2d`). + * + * @return Output iterator to the element past the last distance computed. + * + * [LinkLRAI]: https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator + * "LegacyRandomAccessIterator" + */ +template ::value_type, + class T = typename Location::value_type> +OutputIt haversine_distance(LonLatItA a_lonlat_first, + LonLatItA a_lonlat_last, + LonLatItB b_lonlat_first, + OutputIt distance_first, + T const radius = EARTH_RADIUS_KM, + rmm::cuda_stream_view stream = rmm::cuda_stream_default); + +} // namespace cuspatial + +#include diff --git a/cpp/include/cuspatial/experimental/type_utils.hpp b/cpp/include/cuspatial/experimental/type_utils.hpp new file mode 100644 index 000000000..fcaf212fa --- /dev/null +++ b/cpp/include/cuspatial/experimental/type_utils.hpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2022, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +namespace cuspatial { + +namespace detail { +template +struct tuple_to_vec_2d { + __device__ VectorType operator()(thrust::tuple const& pos) + { + return VectorType{thrust::get<0>(pos), thrust::get<1>(pos)}; + } +}; + +template +struct vec_2d_to_tuple { + __device__ thrust::tuple operator()(VectorType const& xy) + { + return thrust::make_tuple(xy.x, xy.y); + } +}; + +} // namespace detail + +template +auto make_vec_2d_iterator(FirstIter first, SecondIter second) +{ + using T = typename std::iterator_traits::value_type; + static_assert(std::is_same_v::value_type>, + "Iterator value_type mismatch"); + + auto zipped = thrust::make_zip_iterator(thrust::make_tuple(first, second)); + return thrust::make_transform_iterator(zipped, detail::tuple_to_vec_2d()); +} + +template +auto make_lonlat_iterator(FirstIter first, SecondIter second) +{ + using T = typename std::iterator_traits::value_type; + return make_vec_2d_iterator>(first, second); +} + +template +auto make_cartesian_2d_iterator(FirstIter first, SecondIter second) +{ + using T = typename std::iterator_traits::value_type; + return make_vec_2d_iterator>(first, second); +} + +template +auto make_zipped_vec_2d_output_iterator(FirstIter first, SecondIter second) +{ + using T = typename std::iterator_traits::value_type; + auto zipped_out = thrust::make_zip_iterator(thrust::make_tuple(first, second)); + return thrust::make_transform_output_iterator(zipped_out, + detail::vec_2d_to_tuple()); +} + +template +auto make_zipped_lonlat_output_iterator(FirstIter first, SecondIter second) +{ + using T = typename std::iterator_traits::value_type; + return make_zipped_vec_2d_output_iterator>(first, second); +} + +template +auto make_zipped_cartesian_2d_output_iterator(FirstIter first, SecondIter second) +{ + using T = typename std::iterator_traits::value_type; + return make_zipped_vec_2d_output_iterator>(first, second); +} + +} // namespace cuspatial diff --git a/cpp/include/cuspatial/types.hpp b/cpp/include/cuspatial/types.hpp index d3837e99c..2b1adbfb8 100644 --- a/cpp/include/cuspatial/types.hpp +++ b/cpp/include/cuspatial/types.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, NVIDIA CORPORATION. + * Copyright (c) 2019-2022, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,29 +16,32 @@ #pragma once +#include + namespace cuspatial { /** - * @brief A 3D location: latitude, longitude, altitude + * @brief A 2D vector + * + * Used in cuspatial for both Longitude/Latitude (LonLat) coordinate pairs and Cartesian (X/Y) + * coordinate pairs. For LonLat pairs, the `x` member represents Longitude, and `y` represents + * Latitude. * * @tparam T the base type for the coordinates */ template -struct location_3d { - T latitude; - T longitude; - T altitude; +struct alignas(2 * sizeof(T)) vec_2d { + using value_type = T; + value_type x; + value_type y; +}; + +template +struct alignas(2 * sizeof(T)) lonlat_2d : vec_2d { }; -/** - * @brief A 2D Cartesian location (x, y) - * - * @tparam T the base type for the coordinates - */ template -struct vec_2d { - T x; - T y; +struct alignas(2 * sizeof(T)) cartesian_2d : vec_2d { }; /** @@ -46,16 +49,16 @@ struct vec_2d { * */ struct its_timestamp { - uint32_t y : 6; - uint32_t m : 4; - uint32_t d : 5; - uint32_t hh : 5; - uint32_t mm : 6; - uint32_t ss : 6; - uint32_t wd : 3; - uint32_t yd : 9; - uint32_t ms : 10; - uint32_t pid : 10; + std::uint32_t y : 6; + std::uint32_t m : 4; + std::uint32_t d : 5; + std::uint32_t hh : 5; + std::uint32_t mm : 6; + std::uint32_t ss : 6; + std::uint32_t wd : 3; + std::uint32_t yd : 9; + std::uint32_t ms : 10; + std::uint32_t pid : 10; }; } // namespace cuspatial diff --git a/cpp/src/spatial/haversine.cu b/cpp/src/spatial/haversine.cu index aa120ed4d..1185e2d6e 100644 --- a/cpp/src/spatial/haversine.cu +++ b/cpp/src/spatial/haversine.cu @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, NVIDIA CORPORATION. + * Copyright (c) 2020-2022, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,16 +16,17 @@ #include #include +#include +#include +#include #include -#include #include #include #include #include #include -#include #include #include @@ -33,30 +34,12 @@ namespace { -template -__device__ T calculate_haversine_distance(T radius, T a_lon, T a_lat, T b_lon, T b_lat) -{ - auto ax = a_lon * DEGREE_TO_RADIAN; - auto ay = a_lat * DEGREE_TO_RADIAN; - auto bx = b_lon * DEGREE_TO_RADIAN; - auto by = b_lat * DEGREE_TO_RADIAN; - - // haversine formula - auto x = (bx - ax) / 2; - auto y = (by - ay) / 2; - auto sinysqrd = sin(y) * sin(y); - auto sinxsqrd = sin(x) * sin(x); - auto scale = cos(ay) * cos(by); - - return 2 * radius * asin(sqrt(sinysqrd + sinxsqrd * scale)); -}; - struct haversine_functor { template std::enable_if_t::value, std::unique_ptr> operator()( Args&&...) { - CUSPATIAL_FAIL("haversine_distance does not support non-floating-point types."); + CUSPATIAL_FAIL("haversine_distance supports only floating-point types."); } template @@ -65,7 +48,7 @@ struct haversine_functor { cudf::column_view const& a_lat, cudf::column_view const& b_lon, cudf::column_view const& b_lat, - double radius, + T radius, rmm::cuda_stream_view stream, rmm::mr::device_memory_resource* mr) { @@ -74,25 +57,15 @@ struct haversine_functor { auto mask_policy = cudf::mask_allocation_policy::NEVER; auto result = cudf::allocate_like(a_lon, a_lon.size(), mask_policy, mr); - auto input_tuple = thrust::make_tuple(thrust::make_constant_iterator(static_cast(radius)), - a_lon.begin(), - a_lat.begin(), - b_lon.begin(), - b_lat.begin()); - - auto input_iter = thrust::make_zip_iterator(input_tuple); - - thrust::transform(rmm::exec_policy(stream), - input_iter, - input_iter + result->size(), - result->mutable_view().begin(), - [] __device__(auto inputs) { - return calculate_haversine_distance(thrust::get<0>(inputs), - thrust::get<1>(inputs), - thrust::get<2>(inputs), - thrust::get<3>(inputs), - thrust::get<4>(inputs)); - }); + auto lonlat_a = cuspatial::make_lonlat_iterator(a_lon.begin(), a_lat.begin()); + auto lonlat_b = cuspatial::make_lonlat_iterator(b_lon.begin(), b_lat.begin()); + + cuspatial::haversine_distance(lonlat_a, + lonlat_a + a_lon.size(), + lonlat_b, + static_cast(*result).begin(), + T{radius}, + stream); return result; } @@ -101,7 +74,6 @@ struct haversine_functor { } // anonymous namespace namespace cuspatial { - namespace detail { std::unique_ptr haversine_distance(cudf::column_view const& a_lon, diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 3040430b7..b6857003c 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -1,5 +1,5 @@ #============================================================================= -# Copyright (c) 2019-2021, NVIDIA CORPORATION. +# Copyright (c) 2019-2022, NVIDIA CORPORATION. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -100,3 +100,7 @@ ConfigureTest(TRAJECTORY_BOUNDING_BOXES_TEST ConfigureTest(SPATIAL_WINDOW_POINT_TEST spatial_window/spatial_window_test.cpp) + +# Experimental API +ConfigureTest(HAVERSINE_TEST_EXP + experimental/spatial/haversine_test.cu) diff --git a/cpp/tests/experimental/spatial/haversine_test.cu b/cpp/tests/experimental/spatial/haversine_test.cu new file mode 100644 index 000000000..13371e1f5 --- /dev/null +++ b/cpp/tests/experimental/spatial/haversine_test.cu @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2022, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include + +#include + +#include + +template +struct HaversineTest : public ::testing::Test { +}; + +// float and double are logically the same but would require seperate tests due to precision. +using TestTypes = ::testing::Types; +TYPED_TEST_CASE(HaversineTest, TestTypes); + +TYPED_TEST(HaversineTest, Empty) +{ + using T = TypeParam; + using Location = cuspatial::lonlat_2d; + + auto a_lonlat = rmm::device_vector{}; + auto b_lonlat = rmm::device_vector{}; + + auto distance = rmm::device_vector{}; + auto expected = rmm::device_vector{}; + + auto distance_end = cuspatial::haversine_distance( + a_lonlat.begin(), a_lonlat.end(), b_lonlat.begin(), distance.begin()); + + EXPECT_EQ(distance, expected); + EXPECT_EQ(0, std::distance(distance.begin(), distance_end)); +} + +TYPED_TEST(HaversineTest, Zero) +{ + using T = TypeParam; + using Location = cuspatial::lonlat_2d; + using LocVec = std::vector; + + auto a_lonlat = rmm::device_vector(1, Location{0, 0}); + auto b_lonlat = rmm::device_vector(1, Location{0, 0}); + + auto distance = rmm::device_vector{1, -1}; + auto expected = rmm::device_vector{1, 0}; + + auto distance_end = cuspatial::haversine_distance( + a_lonlat.begin(), a_lonlat.end(), b_lonlat.begin(), distance.begin()); + + EXPECT_EQ(expected, distance); + EXPECT_EQ(1, std::distance(distance.begin(), distance_end)); +} + +TYPED_TEST(HaversineTest, NegativeRadius) +{ + using T = TypeParam; + using Location = cuspatial::lonlat_2d; + using LocVec = std::vector; + + auto a_lonlat = rmm::device_vector(LocVec({Location{1, 1}, Location{0, 0}})); + auto b_lonlat = rmm::device_vector(LocVec({Location{1, 1}, Location{0, 0}})); + + auto distance = rmm::device_vector{1, -1}; + auto expected = rmm::device_vector{1, 0}; + + EXPECT_THROW(cuspatial::haversine_distance( + a_lonlat.begin(), a_lonlat.end(), b_lonlat.begin(), distance.begin(), T{-10}), + cuspatial::logic_error); +} + +TYPED_TEST(HaversineTest, EquivalentPoints) +{ + using T = TypeParam; + using Location = cuspatial::lonlat_2d; + + auto h_a_lonlat = std::vector({{-180, 0}, {180, 30}}); + auto h_b_lonlat = std::vector({{180, 0}, {-180, 30}}); + + auto h_expected = std::vector({1.5604449514735574e-12, 1.3513849691832763e-12}); + + auto a_lonlat = rmm::device_vector{h_a_lonlat}; + auto b_lonlat = rmm::device_vector{h_b_lonlat}; + + auto distance = rmm::device_vector{2, -1}; + auto expected = rmm::device_vector{h_expected}; + + auto distance_end = cuspatial::haversine_distance( + a_lonlat.begin(), a_lonlat.end(), b_lonlat.begin(), distance.begin()); + + EXPECT_EQ(expected, distance); + EXPECT_EQ(2, std::distance(distance.begin(), distance_end)); +} + +template +struct identity_xform { + using Location = cuspatial::lonlat_2d; + __device__ Location operator()(Location const& loc) { return loc; }; +}; + +// This test verifies that fancy iterators can be passed by using a pass-through transform_iterator +TYPED_TEST(HaversineTest, TransformIterator) +{ + using T = TypeParam; + using Location = cuspatial::lonlat_2d; + + auto h_a_lonlat = std::vector({{-180, 0}, {180, 30}}); + auto h_b_lonlat = std::vector({{180, 0}, {-180, 30}}); + + auto h_expected = std::vector({1.5604449514735574e-12, 1.3513849691832763e-12}); + + auto a_lonlat = rmm::device_vector{h_a_lonlat}; + auto b_lonlat = rmm::device_vector{h_b_lonlat}; + + auto distance = rmm::device_vector{2, -1}; + auto expected = rmm::device_vector{h_expected}; + + auto xform_begin = thrust::make_transform_iterator(a_lonlat.begin(), identity_xform{}); + auto xform_end = thrust::make_transform_iterator(a_lonlat.end(), identity_xform{}); + + auto distance_end = + cuspatial::haversine_distance(xform_begin, xform_end, b_lonlat.begin(), distance.begin()); + + EXPECT_EQ(expected, distance); + EXPECT_EQ(2, std::distance(distance.begin(), distance_end)); +} diff --git a/fetch_rapids.cmake b/fetch_rapids.cmake new file mode 100644 index 000000000..2382abe38 --- /dev/null +++ b/fetch_rapids.cmake @@ -0,0 +1,17 @@ +# ============================================================================= +# Copyright (c) 2018-2022, NVIDIA CORPORATION. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except +# in compliance with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed under the License +# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +# or implied. See the License for the specific language governing permissions and limitations under +# the License. +# ============================================================================= +file(DOWNLOAD https://raw.githubusercontent.com/rapidsai/rapids-cmake/branch-22.06/RAPIDS.cmake + ${CMAKE_BINARY_DIR}/RAPIDS.cmake +) +include(${CMAKE_BINARY_DIR}/RAPIDS.cmake) diff --git a/java/src/main/native/CMakeLists.txt b/java/src/main/native/CMakeLists.txt index de9676445..1f71879a1 100755 --- a/java/src/main/native/CMakeLists.txt +++ b/java/src/main/native/CMakeLists.txt @@ -13,7 +13,13 @@ # See the License for the specific language governing permissions and # limitations under the License. #============================================================================= -cmake_minimum_required(VERSION 3.12 FATAL_ERROR) +cmake_minimum_required(VERSION 3.20.1 FATAL_ERROR) + +include(../../../../fetch_rapids.cmake) + +# TODO: The logic for setting the architectures was previously not here. Was +# that just an oversight, or is there a reason not to include this here? +rapids_cuda_init_architectures(CUSPATIAL_JNI) project(CUDF_JNI VERSION 0.7.0 LANGUAGES C CXX CUDA) @@ -21,16 +27,7 @@ project(CUDF_JNI VERSION 0.7.0 LANGUAGES C CXX CUDA) # - build type ------------------------------------------------------------------------------------ # Set a default build type if none was specified -set(DEFAULT_BUILD_TYPE "Release") - -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - message(STATUS "Setting build type to '${DEFAULT_BUILD_TYPE}' since none specified.") - set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE - STRING "Choose the type of build." FORCE) - # Set the possible values of build type for cmake-gui - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Release" "MinSizeRel" "RelWithDebInfo") -endif() +rapids_cmake_build_type("Release") ################################################################################################### # - compiler options ------------------------------------------------------------------------------ @@ -54,8 +51,8 @@ if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GLIBCXX_USE_CXX11_ABI=0") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_GLIBCXX_USE_CXX11_ABI=0") set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Xcompiler -D_GLIBCXX_USE_CXX11_ABI=0") - endif(CMAKE_CXX11_ABI) -endif(CMAKE_COMPILER_IS_GNUCXX) + endif() +endif() #set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -gencode=arch=compute_60,code=sm_60 -gencode=arch=compute_61,code=sm_61") set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -gencode=arch=compute_60,code=sm_60") @@ -71,13 +68,13 @@ set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Werror cross-execution-space-call -Xc option(CMAKE_CUDA_LINEINFO "Enable the -lineinfo option for nvcc (useful for cuda-memcheck / profiler" OFF) if (CMAKE_CUDA_LINEINFO) set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") -endif(CMAKE_CUDA_LINEINFO) +endif() # Debug options if(CMAKE_BUILD_TYPE MATCHES Debug) message(STATUS "Building with debugging flags") set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G -Xcompiler -rdynamic") -endif(CMAKE_BUILD_TYPE MATCHES Debug) +endif() option(BUILD_TESTS "Configure CMake to build tests" ON) @@ -93,7 +90,7 @@ if(CUDA_STATIC_RUNTIME) set(CUDART_LIBRARY "cudart_static") else() set(CUDART_LIBRARY "cudart") -endif(CUDA_STATIC_RUNTIME) +endif() ################################################################################################### # - cmake modules --------------------------------------------------------------------------------- @@ -174,7 +171,7 @@ if(JNI_FOUND) message(STATUS "JDK with JNI in ${JNI_INCLUDE_DIRS}") else() message(FATAL_ERROR "JDK with JNI not found, please check your settings.") -endif(JNI_FOUND) +endif() ################################################################################################### # - include paths --------------------------------------------------------------------------------- @@ -213,13 +210,13 @@ if(USE_NVTX) find_library(NVTX_LIBRARY nvToolsExt PATH ${CMAKE_CUDA_IMPLICIT_LINK_DIRECTORIES}) target_link_libraries(cuspatialjni ${NVTX_LIBRARY}) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_NVTX") -endif(USE_NVTX) +endif() option(PER_THREAD_DEFAULT_STREAM "Build with per-thread default stream" OFF) if(PER_THREAD_DEFAULT_STREAM) message(STATUS "Using per-thread default stream") add_compile_definitions(CUDA_API_PER_THREAD_DEFAULT_STREAM) -endif(PER_THREAD_DEFAULT_STREAM) +endif() ################################################################################################### # - link libraries -------------------------------------------------------------------------------- From e0a15d80d84d53af47d0a612ef899ce8aa6cda6d Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 28 Apr 2022 11:50:26 -0700 Subject: [PATCH 38/67] Migrate vec2d operators to vec_2d.cuh --- cpp/include/cuspatial/utility/vec_2d.cuh | 48 ++++++++++++++++++++++++ cpp/src/spatial/linestring_distance.cu | 44 +--------------------- 2 files changed, 50 insertions(+), 42 deletions(-) create mode 100644 cpp/include/cuspatial/utility/vec_2d.cuh diff --git a/cpp/include/cuspatial/utility/vec_2d.cuh b/cpp/include/cuspatial/utility/vec_2d.cuh new file mode 100644 index 000000000..ca351ab27 --- /dev/null +++ b/cpp/include/cuspatial/utility/vec_2d.cuh @@ -0,0 +1,48 @@ +#pragma once +#include + +namespace cuspatial { + +template +vec_2d __device__ operator+(vec_2d const& a, vec_2d const& b) +{ + return vec_2d{a.x + b.x, a.y + b.y}; +} + +template +vec_2d __device__ operator-(vec_2d const& a, vec_2d const& b) +{ + return vec_2d{a.x - b.x, a.y - b.y}; +} + +template +vec_2d __device__ operator*(vec_2d const& a, vec_2d const& b) +{ + return vec_2d{a.x * b.x, a.y * b.y}; +} + +template +vec_2d __device__ operator*(vec_2d vec, T const& r) +{ + return vec_2d{vec.x * r, vec.y * r}; +} + +template +vec_2d __device__ operator*(T const& r, vec_2d vec) +{ + return vec * r; +} + +template +T __device__ dot(vec_2d const& a, vec_2d const& b) +{ + return a.x * b.x + a.y * b.y; +} + +template +T __device__ det(vec_2d const& a, vec_2d const& b) +{ + return a.x * b.y - a.y * b.x; +} + +} // namespace cuspatial diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index b9fef003f..bcffb17d4 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -31,6 +31,8 @@ #include #include +#include + #include #include #include @@ -38,48 +40,6 @@ namespace cuspatial { namespace { -template -vec_2d __device__ operator+(vec_2d const& a, vec_2d const& b) -{ - return vec_2d{a.x + b.x, a.y + b.y}; -} - -template -vec_2d __device__ operator-(vec_2d const& a, vec_2d const& b) -{ - return vec_2d{a.x - b.x, a.y - b.y}; -} - -template -vec_2d __device__ operator*(vec_2d const& a, vec_2d const& b) -{ - return vec_2d{a.x * b.x, a.y * b.y}; -} - -template -vec_2d __device__ operator*(vec_2d vec, T const& r) -{ - return vec_2d{vec.x * r, vec.y * r}; -} - -template -vec_2d __device__ operator*(T const& r, vec_2d vec) -{ - return vec * r; -} - -template -T __device__ dot(vec_2d const& a, vec_2d const& b) -{ - return a.x * b.x + a.y * b.y; -} - -template -T __device__ det(vec_2d const& a, vec_2d const& b) -{ - return a.x * b.y - a.y * b.x; -} - /** @brief Get the index that is one-past the end point of linestring at @p linestring_idx * * @note The last endpoint of the linestring is not included in the offset array, thus From 877d74ef04d85ff6d7b547f01bda9029b0e1e927 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 28 Apr 2022 11:57:17 -0700 Subject: [PATCH 39/67] Add docstrings for operators --- cpp/include/cuspatial/utility/vec_2d.cuh | 21 +++++++++++++++++++++ cpp/src/spatial/linestring_distance.cu | 3 +-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/cpp/include/cuspatial/utility/vec_2d.cuh b/cpp/include/cuspatial/utility/vec_2d.cuh index ca351ab27..7fc9392f5 100644 --- a/cpp/include/cuspatial/utility/vec_2d.cuh +++ b/cpp/include/cuspatial/utility/vec_2d.cuh @@ -3,42 +3,63 @@ namespace cuspatial { +/** + * @brief Element-wise add of two 2d vectors. + */ template vec_2d __device__ operator+(vec_2d const& a, vec_2d const& b) { return vec_2d{a.x + b.x, a.y + b.y}; } +/** + * @brief Element-wise subtract of two 2d vectors. + */ template vec_2d __device__ operator-(vec_2d const& a, vec_2d const& b) { return vec_2d{a.x - b.x, a.y - b.y}; } +/** + * @brief Element-wise multiply of two 2d vectors. + */ template vec_2d __device__ operator*(vec_2d const& a, vec_2d const& b) { return vec_2d{a.x * b.x, a.y * b.y}; } +/** + * @brief Scale a 2d vector by ratio @p r. + */ template vec_2d __device__ operator*(vec_2d vec, T const& r) { return vec_2d{vec.x * r, vec.y * r}; } +/** + * @brief Scale a 2d vector by ratio @p r. + */ template vec_2d __device__ operator*(T const& r, vec_2d vec) { return vec * r; } +/** + * @brief Compute dot product of two 2d vectors. + */ template T __device__ dot(vec_2d const& a, vec_2d const& b) { return a.x * b.x + a.y * b.y; } +/** + * @brief Compute 2d determinant of two 2d vectors. + */ template T __device__ det(vec_2d const& a, vec_2d const& b) { diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index bcffb17d4..5e862f2be 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -31,8 +32,6 @@ #include #include -#include - #include #include #include From 2a58b0eab01160acfbc281de2bc4551f8091d264 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 28 Apr 2022 11:59:20 -0700 Subject: [PATCH 40/67] Add macro for portable host_device code --- cpp/include/cuspatial/types.hpp | 6 ++++++ cpp/include/cuspatial/utility/vec_2d.cuh | 14 +++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/cpp/include/cuspatial/types.hpp b/cpp/include/cuspatial/types.hpp index 2b1adbfb8..7b5004ffa 100644 --- a/cpp/include/cuspatial/types.hpp +++ b/cpp/include/cuspatial/types.hpp @@ -18,6 +18,12 @@ #include +#ifdef __CUDACC__ +#define CUSPATIAL_HOST_DEVICE __host__ __device__ +#else +#define CUSPATIAL_HOST_DEVICE +#endif + namespace cuspatial { /** diff --git a/cpp/include/cuspatial/utility/vec_2d.cuh b/cpp/include/cuspatial/utility/vec_2d.cuh index 7fc9392f5..5e0df3675 100644 --- a/cpp/include/cuspatial/utility/vec_2d.cuh +++ b/cpp/include/cuspatial/utility/vec_2d.cuh @@ -7,7 +7,7 @@ namespace cuspatial { * @brief Element-wise add of two 2d vectors. */ template -vec_2d __device__ operator+(vec_2d const& a, vec_2d const& b) +vec_2d CUSPATIAL_HOST_DEVICE operator+(vec_2d const& a, vec_2d const& b) { return vec_2d{a.x + b.x, a.y + b.y}; } @@ -16,7 +16,7 @@ vec_2d __device__ operator+(vec_2d const& a, vec_2d const& b) * @brief Element-wise subtract of two 2d vectors. */ template -vec_2d __device__ operator-(vec_2d const& a, vec_2d const& b) +vec_2d CUSPATIAL_HOST_DEVICE operator-(vec_2d const& a, vec_2d const& b) { return vec_2d{a.x - b.x, a.y - b.y}; } @@ -25,7 +25,7 @@ vec_2d __device__ operator-(vec_2d const& a, vec_2d const& b) * @brief Element-wise multiply of two 2d vectors. */ template -vec_2d __device__ operator*(vec_2d const& a, vec_2d const& b) +vec_2d CUSPATIAL_HOST_DEVICE operator*(vec_2d const& a, vec_2d const& b) { return vec_2d{a.x * b.x, a.y * b.y}; } @@ -34,7 +34,7 @@ vec_2d __device__ operator*(vec_2d const& a, vec_2d const& b) * @brief Scale a 2d vector by ratio @p r. */ template -vec_2d __device__ operator*(vec_2d vec, T const& r) +vec_2d CUSPATIAL_HOST_DEVICE operator*(vec_2d vec, T const& r) { return vec_2d{vec.x * r, vec.y * r}; } @@ -43,7 +43,7 @@ vec_2d __device__ operator*(vec_2d vec, T const& r) * @brief Scale a 2d vector by ratio @p r. */ template -vec_2d __device__ operator*(T const& r, vec_2d vec) +vec_2d CUSPATIAL_HOST_DEVICE operator*(T const& r, vec_2d vec) { return vec * r; } @@ -52,7 +52,7 @@ vec_2d __device__ operator*(T const& r, vec_2d vec) * @brief Compute dot product of two 2d vectors. */ template -T __device__ dot(vec_2d const& a, vec_2d const& b) +T CUSPATIAL_HOST_DEVICE dot(vec_2d const& a, vec_2d const& b) { return a.x * b.x + a.y * b.y; } @@ -61,7 +61,7 @@ T __device__ dot(vec_2d const& a, vec_2d const& b) * @brief Compute 2d determinant of two 2d vectors. */ template -T __device__ det(vec_2d const& a, vec_2d const& b) +T CUSPATIAL_HOST_DEVICE det(vec_2d const& a, vec_2d const& b) { return a.x * b.y - a.y * b.x; } From 6fdd25aab2cbc27a88710cb446e668ae12021167 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 28 Apr 2022 12:03:35 -0700 Subject: [PATCH 41/67] Revert "Merge master from upstream" This reverts commit aa938299e624ba2c7e630cc030c31592f369a315. --- .gitignore | 6 - CHANGELOG.md | 19 +- build.sh | 4 +- ci/release/update-version.sh | 3 - cpp/CMakeLists.txt | 111 +++---- cpp/cmake/Modules/ConfigureCUDA.cmake | 13 + cpp/cmake/Modules/EvalGPUArchs.cmake | 70 +++++ cpp/cmake/Modules/SetGPUArchs.cmake | 55 ++++ cpp/cmake/cuspatial-build-config.cmake.in | 60 ++++ cpp/cmake/cuspatial-config.cmake.in | 75 +++++ cpp/cmake/thirdparty/CUSPATIAL_GetCPM.cmake | 33 +++ cpp/cmake/thirdparty/CUSPATIAL_GetCUDF.cmake | 24 +- cpp/doc/libcuspatial_refactoring_guide.md | 280 ------------------ cpp/include/cuspatial/error.hpp | 5 +- .../experimental/detail/haversine.cuh | 89 ------ .../cuspatial/experimental/haversine.cuh | 85 ------ .../cuspatial/experimental/type_utils.hpp | 92 ------ cpp/include/cuspatial/types.hpp | 6 - cpp/src/spatial/haversine.cu | 58 +++- cpp/tests/CMakeLists.txt | 6 +- .../experimental/spatial/haversine_test.cu | 143 --------- fetch_rapids.cmake | 17 -- java/src/main/native/CMakeLists.txt | 35 ++- 23 files changed, 448 insertions(+), 841 deletions(-) create mode 100644 cpp/cmake/Modules/EvalGPUArchs.cmake create mode 100644 cpp/cmake/Modules/SetGPUArchs.cmake create mode 100644 cpp/cmake/cuspatial-build-config.cmake.in create mode 100644 cpp/cmake/cuspatial-config.cmake.in create mode 100644 cpp/cmake/thirdparty/CUSPATIAL_GetCPM.cmake delete mode 100644 cpp/doc/libcuspatial_refactoring_guide.md delete mode 100644 cpp/include/cuspatial/experimental/detail/haversine.cuh delete mode 100644 cpp/include/cuspatial/experimental/haversine.cuh delete mode 100644 cpp/include/cuspatial/experimental/type_utils.hpp delete mode 100644 cpp/tests/experimental/spatial/haversine_test.cu delete mode 100644 fetch_rapids.cmake diff --git a/.gitignore b/.gitignore index aaa293799..e5628cf20 100644 --- a/.gitignore +++ b/.gitignore @@ -137,9 +137,3 @@ ENV/ # mypy .mypy_cache/ - -# notebook/example generated files -notebooks/taxi2016.csv -notebooks/taxi2017.csv -notebooks/tzones_lonlat.json -notebooks/cu_taxi_zones.* diff --git a/CHANGELOG.md b/CHANGELOG.md index bdf74f5a4..bad64deb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,24 +2,9 @@ Please see https://github.com/rapidsai/cuspatial/releases/tag/v22.06.00a for the latest changes to this development branch. -# cuSpatial 22.04.00 (6 Apr 2022) +# cuSpatial 22.04.00 (Date TBD) -## 🐛 Bug Fixes - -- Swap NumericalColumn.values_host for now removed to_array ([#485](https://github.com/rapidsai/cuspatial/pull/485)) [@vyasr](https://github.com/vyasr) - -## 📖 Documentation - -- Improve point_in_polygon documentation regarding poly_ring_offsets ([#497](https://github.com/rapidsai/cuspatial/pull/497)) [@harrism](https://github.com/harrism) -- Fix documentation of return type of quadtree_point_in_polygon ([#490](https://github.com/rapidsai/cuspatial/pull/490)) [@harrism](https://github.com/harrism) - -## 🛠️ Improvements - -- Temporarily disable new `ops-bot` functionality ([#501](https://github.com/rapidsai/cuspatial/pull/501)) [@ajschmidt8](https://github.com/ajschmidt8) -- Pin gtest/gmock to 1.10.0 in dev envs ([#498](https://github.com/rapidsai/cuspatial/pull/498)) [@trxcllnt](https://github.com/trxcllnt) -- Add `.github/ops-bot.yaml` config file ([#496](https://github.com/rapidsai/cuspatial/pull/496)) [@ajschmidt8](https://github.com/ajschmidt8) -- Add CMake `install` rule for tests ([#488](https://github.com/rapidsai/cuspatial/pull/488)) [@ajschmidt8](https://github.com/ajschmidt8) -- replace `ccache` with `sccache` ([#483](https://github.com/rapidsai/cuspatial/pull/483)) [@AyodeAwe](https://github.com/AyodeAwe) +Please see https://github.com/rapidsai/cuspatial/releases/tag/v22.04.00a for the latest changes to this development branch. # cuSpatial 22.02.00 (2 Feb 2022) diff --git a/build.sh b/build.sh index b6546b399..eafe261d1 100755 --- a/build.sh +++ b/build.sh @@ -107,10 +107,10 @@ if hasArg clean; then fi if (( ${BUILD_ALL_GPU_ARCH} == 0 )); then - CUSPATIAL_CMAKE_CUDA_ARCHITECTURES="-DCMAKE_CUDA_ARCHITECTURES=NATIVE" + CUSPATIAL_CMAKE_CUDA_ARCHITECTURES="-DCMAKE_CUDA_ARCHITECTURES=" echo "Building for the architecture of the GPU in the system..." else - CUSPATIAL_CMAKE_CUDA_ARCHITECTURES="-DCMAKE_CUDA_ARCHITECTURES=ALL" + CUSPATIAL_CMAKE_CUDA_ARCHITECTURES="" echo "Building for *ALL* supported GPU architectures..." fi diff --git a/ci/release/update-version.sh b/ci/release/update-version.sh index e9452bfcd..5d5befcb7 100755 --- a/ci/release/update-version.sh +++ b/ci/release/update-version.sh @@ -36,9 +36,6 @@ sed_runner 's/'"CUSPATIAL VERSION .* LANGUAGES"'/'"CUSPATIAL VERSION ${NEXT_FULL sed_runner 's/version = .*/version = '"'${NEXT_SHORT_TAG}'"'/g' docs/source/conf.py sed_runner 's/release = .*/release = '"'${NEXT_FULL_TAG}'"'/g' docs/source/conf.py -# rapids-cmake version -sed_runner 's/'"branch-.*\/RAPIDS.cmake"'/'"branch-${NEXT_SHORT_TAG}\/RAPIDS.cmake"'/g' fetch_rapids.cmake - # bump cudf for FILE in conda/environments/*.yml; do sed_runner "s/cudf=${CURRENT_SHORT_TAG}/cudf=${NEXT_SHORT_TAG}/g" ${FILE}; diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 375403f1e..81a572ec7 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -14,18 +14,21 @@ # limitations under the License. #============================================================================= -cmake_minimum_required(VERSION 3.20.1 FATAL_ERROR) - -include(../fetch_rapids.cmake) -include(rapids-cmake) -include(rapids-cpm) -include(rapids-cuda) -include(rapids-export) -include(rapids-find) - -rapids_cuda_init_architectures(CUSPATIAL) +cmake_minimum_required(VERSION 3.18 FATAL_ERROR) + +# If `CMAKE_CUDA_ARCHITECTURES` is not defined, build for all supported architectures. If +# `CMAKE_CUDA_ARCHITECTURES` is set to an empty string (""), build for only the current +# architecture. If `CMAKE_CUDA_ARCHITECTURES` is specified by the user, use user setting. + +# This needs to be run before enabling the CUDA language due to the default initialization behavior +# of `CMAKE_CUDA_ARCHITECTURES`, https://gitlab.kitware.com/cmake/cmake/-/issues/21302 +if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES OR CMAKE_CUDA_ARCHITECTURES STREQUAL "ALL") + set(CUSPATIAL_BUILD_FOR_ALL_ARCHS TRUE) +elseif(CMAKE_CUDA_ARCHITECTURES STREQUAL "") + set(CUSPATIAL_BUILD_FOR_DETECTED_ARCHS TRUE) +endif() -project(CUSPATIAL VERSION 22.06.00 LANGUAGES C CXX CUDA) +project(CUSPATIAL VERSION 22.06.00 LANGUAGES C CXX) # Needed because GoogleBenchmark changes the state of FindThreads.cmake, # causing subsequent runs to have different values for the `Threads::Threads` target. @@ -55,7 +58,7 @@ message(STATUS "CUSPATIAL: Enable the -lineinfo option for nvcc (useful for cuda message(STATUS "CUSPATIAL: Statically link the CUDA runtime: ${CUDA_STATIC_RUNTIME}") # Set a default build type if none was specified -rapids_cmake_build_type("Release") +set(DEFAULT_BUILD_TYPE "Release") set(CUSPATIAL_BUILD_TESTS ${BUILD_TESTS}) set(CUSPATIAL_BUILD_BENCHMARKS ${BUILD_BENCHMARKS}) @@ -64,6 +67,15 @@ set(CUSPATIAL_CUDA_FLAGS "") set(CUSPATIAL_CXX_DEFINITIONS "") set(CUSPATIAL_CUDA_DEFINITIONS "") +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to '${DEFAULT_BUILD_TYPE}' since none specified.") + set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE + STRING "Choose the type of build." FORCE) + # Set the possible values of build type for cmake-gui + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Release" "MinSizeRel" "RelWithDebInfo") +endif() + # Set RMM logging level set(RMM_LOGGING_LEVEL "INFO" CACHE STRING "Choose the logging level.") set_property(CACHE RMM_LOGGING_LEVEL PROPERTY STRINGS "TRACE" "DEBUG" "INFO" "WARN" "ERROR" "CRITICAL" "OFF") @@ -72,16 +84,21 @@ message(STATUS "CUSPATIAL: RMM_LOGGING_LEVEL = '${RMM_LOGGING_LEVEL}'.") ################################################################################################### # - conda environment ----------------------------------------------------------------------------- -rapids_cmake_support_conda_env(conda_env MODIFY_PREFIX_PATH) +if("$ENV{CONDA_BUILD}" STREQUAL "1") + set(CMAKE_PREFIX_PATH "$ENV{BUILD_PREFIX};$ENV{PREFIX};${CMAKE_PREFIX_PATH}") + set(CONDA_INCLUDE_DIRS "$ENV{BUILD_PREFIX}/include" "$ENV{PREFIX}/include") + set(CONDA_LINK_DIRS "$ENV{BUILD_PREFIX}/lib" "$ENV{PREFIX}/lib") + message(VERBOSE "CUSPATIAL: Conda build detected, CMAKE_PREFIX_PATH set to: ${CMAKE_PREFIX_PATH}") +elseif(DEFINED ENV{CONDA_PREFIX}) + set(CMAKE_PREFIX_PATH "$ENV{CONDA_PREFIX};${CMAKE_PREFIX_PATH}") + set(CONDA_INCLUDE_DIRS "$ENV{CONDA_PREFIX}/include") + set(CONDA_LINK_DIRS "$ENV{CONDA_PREFIX}/lib") + message(VERBOSE "CUSPATIAL: Conda environment detected, CMAKE_PREFIX_PATH set to: ${CMAKE_PREFIX_PATH}") +endif() ################################################################################################### # - compiler options ------------------------------------------------------------------------------ -rapids_find_package( - CUDAToolkit REQUIRED - BUILD_EXPORT_SET cuspatial-exports - INSTALL_EXPORT_SET cuspatial-exports -) # * find CUDAToolkit package # * determine GPU architectures # * enable the CMake CUDA language @@ -92,13 +109,9 @@ include(cmake/Modules/ConfigureCUDA.cmake) # - dependencies ---------------------------------------------------------------------------------- # find gdal -rapids_find_package( - GDAL REQUIRED - BUILD_EXPORT_SET cuspatial-exports - INSTALL_EXPORT_SET cuspatial-exports -) +find_package(GDAL REQUIRED) # add third party dependencies using CPM -rapids_cpm_init() +include(cmake/thirdparty/CUSPATIAL_GetCPM.cmake) # find or add cuDF include(cmake/thirdparty/CUSPATIAL_GetCUDF.cmake) @@ -221,43 +234,37 @@ set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME cuspatial) install(TARGETS cuspatial DESTINATION lib - EXPORT cuspatial-exports) + EXPORT cuspatial-targets) install(DIRECTORY ${CUSPATIAL_SOURCE_DIR}/include/cuspatial DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -set(doc_string - [=[ -Provide targets for the cuSpatial library. +include(CMakePackageConfigHelpers) -cuSpatial is a GPU-accelerated library for spatial data management and analytics. +configure_package_config_file(cmake/cuspatial-config.cmake.in "${CUSPATIAL_BINARY_DIR}/cmake/cuspatial-config.cmake" + INSTALL_DESTINATION "${INSTALL_CONFIGDIR}") -Imported Targets -^^^^^^^^^^^^^^^^ +write_basic_package_version_file("${CUSPATIAL_BINARY_DIR}/cmake/cuspatial-config-version.cmake" + COMPATIBILITY SameMinorVersion) -If cuspatial is found, this module defines the following IMPORTED GLOBAL -targets: - - cuspatial::cuspatial - The main cuspatial library. - ]=] -) - -rapids_export( - INSTALL cuspatial - EXPORT_SET cuspatial-exports - GLOBAL_TARGETS cuspatial - NAMESPACE cuspatial:: - DOCUMENTATION doc_string -) +install(FILES "${CUSPATIAL_BINARY_DIR}/cmake/cuspatial-config.cmake" + "${CUSPATIAL_BINARY_DIR}/cmake/cuspatial-config-version.cmake" + DESTINATION "${INSTALL_CONFIGDIR}") +install(EXPORT cuspatial-targets + FILE cuspatial-targets.cmake + NAMESPACE cuspatial:: + DESTINATION "${INSTALL_CONFIGDIR}") ################################################################################################ # - build export ------------------------------------------------------------------------------- -rapids_export( - BUILD cuspatial - EXPORT_SET cuspatial-exports - GLOBAL_TARGETS cuspatial - NAMESPACE cuspatial:: - DOCUMENTATION doc_string -) +configure_package_config_file(cmake/cuspatial-build-config.cmake.in ${CUSPATIAL_BINARY_DIR}/cuspatial-config.cmake + INSTALL_DESTINATION ${CUSPATIAL_BINARY_DIR}) + +write_basic_package_version_file(${CUSPATIAL_BINARY_DIR}/cuspatial-config-version.cmake + COMPATIBILITY SameMinorVersion) + +export(EXPORT cuspatial-targets + FILE ${CUSPATIAL_BINARY_DIR}/cuspatial-targets.cmake + NAMESPACE cuspatial::) diff --git a/cpp/cmake/Modules/ConfigureCUDA.cmake b/cpp/cmake/Modules/ConfigureCUDA.cmake index 0c04f5240..e17464c7c 100644 --- a/cpp/cmake/Modules/ConfigureCUDA.cmake +++ b/cpp/cmake/Modules/ConfigureCUDA.cmake @@ -14,6 +14,19 @@ # limitations under the License. #============================================================================= +# Find the CUDAToolkit +find_package(CUDAToolkit REQUIRED) + +# Auto-detect available GPU compute architectures +include(${CMAKE_CURRENT_LIST_DIR}/SetGPUArchs.cmake) +message(STATUS "CUSPATIAL: Building CUSPATIAL for GPU architectures: ${CMAKE_CUDA_ARCHITECTURES}") + +# Must come after find_package(CUDAToolkit) because we symlink +# sccache as a compiler front-end for nvcc in gpuCI CPU builds. +# Must also come after we detect and potentially rewrite +# CMAKE_CUDA_ARCHITECTURES +enable_language(CUDA) + if(CMAKE_COMPILER_IS_GNUCXX) list(APPEND CUSPATIAL_CXX_FLAGS -Wall -Werror -Wno-unknown-pragmas -Wno-error=deprecated-declarations) if(CUSPATIAL_BUILD_TESTS OR CUSPATIAL_BUILD_BENCHMARKS) diff --git a/cpp/cmake/Modules/EvalGPUArchs.cmake b/cpp/cmake/Modules/EvalGPUArchs.cmake new file mode 100644 index 000000000..d0d833bea --- /dev/null +++ b/cpp/cmake/Modules/EvalGPUArchs.cmake @@ -0,0 +1,70 @@ +#============================================================================= +# Copyright (c) 2019-2021, 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. +#============================================================================= + +# Unset this first in case it's set to +set(CMAKE_CUDA_ARCHITECTURES OFF) + +# Enable CUDA so we can invoke nvcc +enable_language(CUDA) + +# Function uses the CUDA runtime API to query the compute capability of the device, so if a user +# doesn't pass any architecture options to CMake we only build the current architecture +function(evaluate_gpu_archs gpu_archs) + set(eval_file ${PROJECT_BINARY_DIR}/eval_gpu_archs.cu) + set(eval_exe ${PROJECT_BINARY_DIR}/eval_gpu_archs) + set(error_file ${PROJECT_BINARY_DIR}/eval_gpu_archs.stderr.log) + file( + WRITE ${eval_file} + " +#include +#include +#include +using namespace std; +int main(int argc, char** argv) { + set archs; + int nDevices; + if((cudaGetDeviceCount(&nDevices) == cudaSuccess) && (nDevices > 0)) { + for(int dev=0;dev::value_type, - class T = typename Location::value_type> -OutputIt haversine_distance(LonLatItA a_lonlat_first, - LonLatItA a_lonlat_last, - LonLatItB b_lonlat_first, - OutputIt distance_first, - T const radius = EARTH_RADIUS_KM, - rmm::cuda_stream_view stream = rmm::cuda_stream_default); -``` - -There are a few key points to notice. - - 1. The API is very similar to STL algorithms such as `std::transform`. - 2. All array inputs and outputs are iterator type templates. - 3. Longitude/Latitude data is passed as array of structures, using the `cuspatial::lonlat_2d` - type (include/cuspatial/types.hpp). This is enforced using a `static_assert` in the function - body (discussed later). - 4. The `Location` type is a template that is by default equal to the `value_type` of the input - iterators. - 5. The floating point type is a template (`T`) that is by default equal to the `value_type` of - `Location`. - 6. The iterator types for the two input ranges (A and B) are distinct templates. This is crucial - to enable composition of fancy iterators that may be different types for A and B. - 7. The size of the input and output ranges in the example API are equal, so the start and end of - only the A range is provided (`a_lonlat_first` and `a_lonlat_last`). This mirrors STL APIs. - 8. This API returns an iterator to the element past the last element written to the output. This - is inspired by `std::transform`, even though as with `transform`, many uses of - `haversine_distance` will not need this returned iterator. - 9. All APIs that run CUDA device code (including Thrust algorithms) or allocate memory take a CUDA - stream on which to execute the device code and allocate memory. - -## Example Documentation - -Following is the (Doxygen) documentation for the above `cuspatial::haversine_distance`. - -```C++ -/** - * @brief Compute haversine distances between points in set A to the corresponding points in set B. - * - * Computes N haversine distances, where N is `std::distance(a_lonlat_first, a_lonlat_last)`. - * The distance for each `a_lonlat[i]` and `b_lonlat[i]` point pair is assigned to - * `distance_first[i]`. `distance_first` must be an iterator to output storage allocated for N - * distances. - * - * Computed distances will have the same units as `radius`. - * - * https://en.wikipedia.org/wiki/Haversine_formula - * - * @param[in] a_lonlat_first: beginning of range of (longitude, latitude) locations in set A - * @param[in] a_lonlat_last: end of range of (longitude, latitude) locations in set A - * @param[in] b_lonlat_first: beginning of range of (longitude, latitude) locations in set B - * @param[out] distance_first: beginning of output range of haversine distances - * @param[in] radius: radius of the sphere on which the points reside. default: 6371.0 - * (approximate radius of Earth in km) - * @param[in] stream: The CUDA stream on which to perform computations and allocate memory. - * - * @tparam LonLatItA Iterator to input location set A. Must meet the requirements of - * [LegacyRandomAccessIterator][LinkLRAI] and be device-accessible. - * @tparam LonLatItB Iterator to input location set B. Must meet the requirements of - * [LegacyRandomAccessIterator][LinkLRAI] and be device-accessible. - * @tparam OutputIt Output iterator. Must meet the requirements of - * [LegacyRandomAccessIterator][LinkLRAI] and be device-accessible. - * @tparam Location The `value_type` of `LonLatItA` and `LonLatItB`. Must be - * `cuspatial::lonlat_2d`. - * @tparam T The underlying coordinate type. Must be a floating-point type. - * - * @pre `a_lonlat_first` may equal `distance_first`, but the range `[a_lonlat_first, a_lonlat_last)` - * shall not overlap the range `[distance_first, distance_first + (a_lonlat_last - a_lonlat_last)) - * otherwise. - * @pre `b_lonlat_first` may equal `distance_first`, but the range `[b_lonlat_first, b_lonlat_last)` - * shall not overlap the range `[distance_first, distance_first + (b_lonlat_last - b_lonlat_last)) - * otherwise. - * @pre All iterators must have the same `Location` type, with the same underlying floating-point - * coordinate type (e.g. `cuspatial::lonlat_2d`). - * - * @return Output iterator to the element past the last distance computed. - * - * [LinkLRAI]: https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator - * "LegacyRandomAccessIterator" - */ -``` - -Key points: - - 1. Precisely and succinctly documents what the API computes, and provides references. - 2. All parameters and all template parameters are documented. - 3. States the C++ standard iterator concepts that must be implemented, and that iterators must be - device-accessible. - 4. Documents requirements as preconditions using `@pre`. - 5. Uses preconditions to explicitly document what input ranges are allowed to overlap. - 6. Documents the units of any inputs or outputs that have them. - -## cuSpatial libcudf-based C++ API (legacy API) - -This is the existing API, unchanged by refactoring. Here is the existing -`cuspatial::haversine_distance`: - -```C++ -std::unique_ptr haversine_distance( - cudf::column_view const& a_lon, - cudf::column_view const& a_lat, - cudf::column_view const& b_lon, - cudf::column_view const& b_lat, - double const radius = EARTH_RADIUS_KM, - rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); -``` - -key points: - 1. All input data are `cudf::column_view`. This is a type-erased container so determining the - type of data must be done at run time. - 2. All inputs are arrays of scalars. Longitude and latitude are separate. - 3. The output is a returned `unique_ptr`. - 4. The output is allocated inside the function using the passed memory resource. - 5. The public API does not take a stream. There is a `detail` version of the API that takes a - stream. This follows libcudf, and may change in the future. - -## File Structure - -For now, libcuspatial APIs should be defined in a header file in the -`cpp/include/cuspatial/experimental/` directory. Later, as we adopt the new API, we will rename -the `experimental` directory. The API header should be named after the API. In the example, -`haversine.hpp` defines the `cuspatial::haversine_distance` API. - -The implementation must also be in a header, but should be in the `cuspatial/experimental/detail` -directory. The implementation should be included from the API definition file, at the end of the -file. Example: - -```C++ -... // declaration of API above this point -#include -``` - -## Namespaces - -Public APIs are in the `cuspatial` namespace. Note that both the header-only API and the libcudf- -based API can live in the same namespace, because they are non-ambiguous (very different -parameters). - -Implementation of the header-only API should be in a `cuspatial::detail` namespace. - -## Implementation - -The main implementation should be in detail headers. - -### Header-only API Implementation - -Because it is a statically typed API, the header-only implementation can be much simpler than the -libcudf-based API, which requires run-time type dispatching. In the case of `haversine_distance`, it is -a simple matter of a few static asserts and dynamic expectation checks, followed by a call to -`thrust::transform` with a custom transform functor. - -```C++ -template ::value_type, - class T = typename Location::value_type> -OutputIt haversine_distance(LonLatItA a_lonlat_first, - LonLatItA a_lonlat_last, - LonLatItB b_lonlat_first, - OutputIt distance_first, - T const radius, - rmm::cuda_stream_view stream) -{ - using LocationB = typename std::iterator_traits::value_type; - static_assert(std::conjunction_v, Location>, - std::is_same, LocationB>>, - "Inputs must be cuspatial::lonlat_2d"); - static_assert( - std::conjunction_v, - std::is_floating_point, - std::is_floating_point::value_type>>, - "Haversine distance supports only floating-point coordinates."); - - CUSPATIAL_EXPECTS(radius > 0, "radius must be positive."); - - return thrust::transform(rmm::exec_policy(stream), - a_lonlat_first, - a_lonlat_last, - b_lonlat_first, - distance_first, - detail::haversine_distance_functor(radius)); -} -``` - -Note that we `static_assert` that the types of the iterator inputs match documented expectations. -We also do a runtime check that the radius is positive. Finally we just call `thrust::transform`, -passing it an instance of `haversine_distance_functor`, which is a function of two `lonlat_2d` -inputs that implements the Haversine distance formula. - -### libcudf-based API Implementation - -The substance of the refactoring is making the libcudf-based API a wrapper around the header-only -API. This mostly involves replacing business logic implementation in the type-dispatched functor -with a call to the header-only API. We also need to convert disjoint latitude and longitude inputs -into `lonlat_2d` structs. This is easily done using the `cuspatial::make_lonlat_iterator` utility -provided in `type_utils.hpp`. - -So, to refactor the libcudf-based API, we remove the following code. - -```C++ -auto input_tuple = thrust::make_tuple(thrust::make_constant_iterator(static_cast(radius)), - a_lon.begin(), - a_lat.begin(), - b_lon.begin(), - b_lat.begin()); - -auto input_iter = thrust::make_zip_iterator(input_tuple); - -thrust::transform(rmm::exec_policy(stream), - input_iter, - input_iter + result->size(), - result->mutable_view().begin(), - [] __device__(auto inputs) { - return calculate_haversine_distance(thrust::get<0>(inputs), - thrust::get<1>(inputs), - thrust::get<2>(inputs), - thrust::get<3>(inputs), - thrust::get<4>(inputs)); - }); -``` - -And replace it with the following code. - -```C++ -auto lonlat_a = cuspatial::make_lonlat_iterator(a_lon.begin(), a_lat.begin()); -auto lonlat_b = cuspatial::make_lonlat_iterator(b_lon.begin(), b_lat.begin()); - -cuspatial::haversine_distance(lonlat_a, - lonlat_a + a_lon.size(), - lonlat_b, - static_cast(*result).begin(), - T{radius}, - stream); -``` - -## Testing - -Existing libcudf-based API tests can mostly be left alone. New tests should be added to exercise -the header-only API separately in case the libcudf-based API is removed. - -Note that tests, like the header-only API, should not depend on libcudf or libcudf_test. The -cuDF-based API made the mistake of depending on libcudf_test, which results in breakages -of cuSpatial sometimes when libcudf_test changes. diff --git a/cpp/include/cuspatial/error.hpp b/cpp/include/cuspatial/error.hpp index b06c6c565..75c3da3b0 100644 --- a/cpp/include/cuspatial/error.hpp +++ b/cpp/include/cuspatial/error.hpp @@ -41,11 +41,10 @@ struct logic_error : public std::logic_error { struct cuda_error : public std::runtime_error { cuda_error(std::string const& message) : std::runtime_error(message) {} }; - } // namespace cuspatial -#define CUSPATIAL_STRINGIFY_DETAIL(x) #x -#define CUSPATIAL_STRINGIFY(x) CUSPATIAL_STRINGIFY_DETAIL(x) +#define STRINGIFY_DETAIL(x) #x +#define CUSPATIAL_STRINGIFY(x) STRINGIFY_DETAIL(x) /**---------------------------------------------------------------------------* * @brief Macro for checking (pre-)conditions that throws an exception when diff --git a/cpp/include/cuspatial/experimental/detail/haversine.cuh b/cpp/include/cuspatial/experimental/detail/haversine.cuh deleted file mode 100644 index 938809683..000000000 --- a/cpp/include/cuspatial/experimental/detail/haversine.cuh +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2022, NVIDIA CORPORATION. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include -#include - -#include -#include - -#include - -namespace cuspatial { - -namespace detail { - -template -struct haversine_distance_functor { - haversine_distance_functor(T radius) : radius_(radius) {} - - __device__ T operator()(lonlat_2d point_a, lonlat_2d point_b) - { - auto ax = point_a.x * DEGREE_TO_RADIAN; - auto ay = point_a.y * DEGREE_TO_RADIAN; - auto bx = point_b.x * DEGREE_TO_RADIAN; - auto by = point_b.y * DEGREE_TO_RADIAN; - - // haversine formula - auto x = (bx - ax) / 2; - auto y = (by - ay) / 2; - auto sinysqrd = sin(y) * sin(y); - auto sinxsqrd = sin(x) * sin(x); - auto scale = cos(ay) * cos(by); - - return 2 * radius_ * asin(sqrt(sinysqrd + sinxsqrd * scale)); - } - - T radius_{}; -}; - -} // namespace detail - -template -OutputIt haversine_distance(LonLatItA a_lonlat_first, - LonLatItA a_lonlat_last, - LonLatItB b_lonlat_first, - OutputIt distance_first, - T const radius, - rmm::cuda_stream_view stream) -{ - using LocationB = typename std::iterator_traits::value_type; - static_assert( - std::conjunction_v, Location>, std::is_same, LocationB>>, - "Inputs must be cuspatial::lonlat_2d"); - static_assert( - std::conjunction_v, - std::is_floating_point, - std::is_floating_point::value_type>>, - "Haversine distance supports only floating-point coordinates."); - - CUSPATIAL_EXPECTS(radius > 0, "radius must be positive."); - - return thrust::transform(rmm::exec_policy(stream), - a_lonlat_first, - a_lonlat_last, - b_lonlat_first, - distance_first, - detail::haversine_distance_functor(radius)); -} - -} // namespace cuspatial diff --git a/cpp/include/cuspatial/experimental/haversine.cuh b/cpp/include/cuspatial/experimental/haversine.cuh deleted file mode 100644 index 1820258f7..000000000 --- a/cpp/include/cuspatial/experimental/haversine.cuh +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2022, NVIDIA CORPORATION. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include - -#include - -namespace cuspatial { - -/** - * @brief Compute haversine distances between points in set A to the corresponding points in set B. - * - * Computes N haversine distances, where N is `std::distance(a_lonlat_first, a_lonlat_last)`. - * The distance for each `a_lonlat[i]` and `b_lonlat[i]` point pair is assigned to - * `distance_first[i]`. `distance_first` must be an iterator to output storage allocated for N - * distances. - * - * Computed distances will have the same units as `radius`. - * - * https://en.wikipedia.org/wiki/Haversine_formula - * - * @param[in] a_lonlat_first: beginning of range of (longitude, latitude) locations in set A - * @param[in] a_lonlat_last: end of range of (longitude, latitude) locations in set A - * @param[in] b_lonlat_first: beginning of range of (longitude, latitude) locations in set B - * @param[out] distance_first: beginning of output range of haversine distances - * @param[in] radius: radius of the sphere on which the points reside. default: 6371.0 - * (approximate radius of Earth in km) - * @param[in] stream: The CUDA stream on which to perform computations and allocate memory. - * - * @tparam LonLatItA Iterator to input location set A. Must meet the requirements of - * [LegacyRandomAccessIterator][LinkLRAI] and be device-accessible. - * @tparam LonLatItB Iterator to input location set B. Must meet the requirements of - * [LegacyRandomAccessIterator][LinkLRAI] and be device-accessible. - * @tparam OutputIt Output iterator. Must meet the requirements of - * [LegacyRandomAccessIterator][LinkLRAI] and be device-accessible. - * @tparam Location The `value_type` of `LonLatItA` and `LonLatItB`. Must be - * `cuspatial::lonlat_2d`. - * @tparam T The underlying coordinate type. Must be a floating-point type. - * - * @pre `a_lonlat_first` may equal `distance_first`, but the range `[a_lonlat_first, a_lonlat_last)` - * shall not overlap the range `[distance_first, distance_first + (a_lonlat_last - a_lonlat_last)) - * otherwise. - * @pre `b_lonlat_first` may equal `distance_first`, but the range `[b_lonlat_first, b_lonlat_last)` - * shall not overlap the range `[distance_first, distance_first + (b_lonlat_last - b_lonlat_last)) - * otherwise. - * @pre All iterators must have the same `Location` type, with the same underlying floating-point - * coordinate type (e.g. `cuspatial::lonlat_2d`). - * - * @return Output iterator to the element past the last distance computed. - * - * [LinkLRAI]: https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator - * "LegacyRandomAccessIterator" - */ -template ::value_type, - class T = typename Location::value_type> -OutputIt haversine_distance(LonLatItA a_lonlat_first, - LonLatItA a_lonlat_last, - LonLatItB b_lonlat_first, - OutputIt distance_first, - T const radius = EARTH_RADIUS_KM, - rmm::cuda_stream_view stream = rmm::cuda_stream_default); - -} // namespace cuspatial - -#include diff --git a/cpp/include/cuspatial/experimental/type_utils.hpp b/cpp/include/cuspatial/experimental/type_utils.hpp deleted file mode 100644 index fcaf212fa..000000000 --- a/cpp/include/cuspatial/experimental/type_utils.hpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2022, NVIDIA CORPORATION. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include -#include -#include - -namespace cuspatial { - -namespace detail { -template -struct tuple_to_vec_2d { - __device__ VectorType operator()(thrust::tuple const& pos) - { - return VectorType{thrust::get<0>(pos), thrust::get<1>(pos)}; - } -}; - -template -struct vec_2d_to_tuple { - __device__ thrust::tuple operator()(VectorType const& xy) - { - return thrust::make_tuple(xy.x, xy.y); - } -}; - -} // namespace detail - -template -auto make_vec_2d_iterator(FirstIter first, SecondIter second) -{ - using T = typename std::iterator_traits::value_type; - static_assert(std::is_same_v::value_type>, - "Iterator value_type mismatch"); - - auto zipped = thrust::make_zip_iterator(thrust::make_tuple(first, second)); - return thrust::make_transform_iterator(zipped, detail::tuple_to_vec_2d()); -} - -template -auto make_lonlat_iterator(FirstIter first, SecondIter second) -{ - using T = typename std::iterator_traits::value_type; - return make_vec_2d_iterator>(first, second); -} - -template -auto make_cartesian_2d_iterator(FirstIter first, SecondIter second) -{ - using T = typename std::iterator_traits::value_type; - return make_vec_2d_iterator>(first, second); -} - -template -auto make_zipped_vec_2d_output_iterator(FirstIter first, SecondIter second) -{ - using T = typename std::iterator_traits::value_type; - auto zipped_out = thrust::make_zip_iterator(thrust::make_tuple(first, second)); - return thrust::make_transform_output_iterator(zipped_out, - detail::vec_2d_to_tuple()); -} - -template -auto make_zipped_lonlat_output_iterator(FirstIter first, SecondIter second) -{ - using T = typename std::iterator_traits::value_type; - return make_zipped_vec_2d_output_iterator>(first, second); -} - -template -auto make_zipped_cartesian_2d_output_iterator(FirstIter first, SecondIter second) -{ - using T = typename std::iterator_traits::value_type; - return make_zipped_vec_2d_output_iterator>(first, second); -} - -} // namespace cuspatial diff --git a/cpp/include/cuspatial/types.hpp b/cpp/include/cuspatial/types.hpp index 7b5004ffa..2b1adbfb8 100644 --- a/cpp/include/cuspatial/types.hpp +++ b/cpp/include/cuspatial/types.hpp @@ -18,12 +18,6 @@ #include -#ifdef __CUDACC__ -#define CUSPATIAL_HOST_DEVICE __host__ __device__ -#else -#define CUSPATIAL_HOST_DEVICE -#endif - namespace cuspatial { /** diff --git a/cpp/src/spatial/haversine.cu b/cpp/src/spatial/haversine.cu index 1185e2d6e..aa120ed4d 100644 --- a/cpp/src/spatial/haversine.cu +++ b/cpp/src/spatial/haversine.cu @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2022, NVIDIA CORPORATION. + * Copyright (c) 2020, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,17 +16,16 @@ #include #include -#include -#include -#include #include +#include #include #include #include #include #include +#include #include #include @@ -34,12 +33,30 @@ namespace { +template +__device__ T calculate_haversine_distance(T radius, T a_lon, T a_lat, T b_lon, T b_lat) +{ + auto ax = a_lon * DEGREE_TO_RADIAN; + auto ay = a_lat * DEGREE_TO_RADIAN; + auto bx = b_lon * DEGREE_TO_RADIAN; + auto by = b_lat * DEGREE_TO_RADIAN; + + // haversine formula + auto x = (bx - ax) / 2; + auto y = (by - ay) / 2; + auto sinysqrd = sin(y) * sin(y); + auto sinxsqrd = sin(x) * sin(x); + auto scale = cos(ay) * cos(by); + + return 2 * radius * asin(sqrt(sinysqrd + sinxsqrd * scale)); +}; + struct haversine_functor { template std::enable_if_t::value, std::unique_ptr> operator()( Args&&...) { - CUSPATIAL_FAIL("haversine_distance supports only floating-point types."); + CUSPATIAL_FAIL("haversine_distance does not support non-floating-point types."); } template @@ -48,7 +65,7 @@ struct haversine_functor { cudf::column_view const& a_lat, cudf::column_view const& b_lon, cudf::column_view const& b_lat, - T radius, + double radius, rmm::cuda_stream_view stream, rmm::mr::device_memory_resource* mr) { @@ -57,15 +74,25 @@ struct haversine_functor { auto mask_policy = cudf::mask_allocation_policy::NEVER; auto result = cudf::allocate_like(a_lon, a_lon.size(), mask_policy, mr); - auto lonlat_a = cuspatial::make_lonlat_iterator(a_lon.begin(), a_lat.begin()); - auto lonlat_b = cuspatial::make_lonlat_iterator(b_lon.begin(), b_lat.begin()); - - cuspatial::haversine_distance(lonlat_a, - lonlat_a + a_lon.size(), - lonlat_b, - static_cast(*result).begin(), - T{radius}, - stream); + auto input_tuple = thrust::make_tuple(thrust::make_constant_iterator(static_cast(radius)), + a_lon.begin(), + a_lat.begin(), + b_lon.begin(), + b_lat.begin()); + + auto input_iter = thrust::make_zip_iterator(input_tuple); + + thrust::transform(rmm::exec_policy(stream), + input_iter, + input_iter + result->size(), + result->mutable_view().begin(), + [] __device__(auto inputs) { + return calculate_haversine_distance(thrust::get<0>(inputs), + thrust::get<1>(inputs), + thrust::get<2>(inputs), + thrust::get<3>(inputs), + thrust::get<4>(inputs)); + }); return result; } @@ -74,6 +101,7 @@ struct haversine_functor { } // anonymous namespace namespace cuspatial { + namespace detail { std::unique_ptr haversine_distance(cudf::column_view const& a_lon, diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index b6857003c..3040430b7 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -1,5 +1,5 @@ #============================================================================= -# Copyright (c) 2019-2022, NVIDIA CORPORATION. +# Copyright (c) 2019-2021, NVIDIA CORPORATION. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -100,7 +100,3 @@ ConfigureTest(TRAJECTORY_BOUNDING_BOXES_TEST ConfigureTest(SPATIAL_WINDOW_POINT_TEST spatial_window/spatial_window_test.cpp) - -# Experimental API -ConfigureTest(HAVERSINE_TEST_EXP - experimental/spatial/haversine_test.cu) diff --git a/cpp/tests/experimental/spatial/haversine_test.cu b/cpp/tests/experimental/spatial/haversine_test.cu deleted file mode 100644 index 13371e1f5..000000000 --- a/cpp/tests/experimental/spatial/haversine_test.cu +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (c) 2022, NVIDIA CORPORATION. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - -#include - -#include - -#include - -template -struct HaversineTest : public ::testing::Test { -}; - -// float and double are logically the same but would require seperate tests due to precision. -using TestTypes = ::testing::Types; -TYPED_TEST_CASE(HaversineTest, TestTypes); - -TYPED_TEST(HaversineTest, Empty) -{ - using T = TypeParam; - using Location = cuspatial::lonlat_2d; - - auto a_lonlat = rmm::device_vector{}; - auto b_lonlat = rmm::device_vector{}; - - auto distance = rmm::device_vector{}; - auto expected = rmm::device_vector{}; - - auto distance_end = cuspatial::haversine_distance( - a_lonlat.begin(), a_lonlat.end(), b_lonlat.begin(), distance.begin()); - - EXPECT_EQ(distance, expected); - EXPECT_EQ(0, std::distance(distance.begin(), distance_end)); -} - -TYPED_TEST(HaversineTest, Zero) -{ - using T = TypeParam; - using Location = cuspatial::lonlat_2d; - using LocVec = std::vector; - - auto a_lonlat = rmm::device_vector(1, Location{0, 0}); - auto b_lonlat = rmm::device_vector(1, Location{0, 0}); - - auto distance = rmm::device_vector{1, -1}; - auto expected = rmm::device_vector{1, 0}; - - auto distance_end = cuspatial::haversine_distance( - a_lonlat.begin(), a_lonlat.end(), b_lonlat.begin(), distance.begin()); - - EXPECT_EQ(expected, distance); - EXPECT_EQ(1, std::distance(distance.begin(), distance_end)); -} - -TYPED_TEST(HaversineTest, NegativeRadius) -{ - using T = TypeParam; - using Location = cuspatial::lonlat_2d; - using LocVec = std::vector; - - auto a_lonlat = rmm::device_vector(LocVec({Location{1, 1}, Location{0, 0}})); - auto b_lonlat = rmm::device_vector(LocVec({Location{1, 1}, Location{0, 0}})); - - auto distance = rmm::device_vector{1, -1}; - auto expected = rmm::device_vector{1, 0}; - - EXPECT_THROW(cuspatial::haversine_distance( - a_lonlat.begin(), a_lonlat.end(), b_lonlat.begin(), distance.begin(), T{-10}), - cuspatial::logic_error); -} - -TYPED_TEST(HaversineTest, EquivalentPoints) -{ - using T = TypeParam; - using Location = cuspatial::lonlat_2d; - - auto h_a_lonlat = std::vector({{-180, 0}, {180, 30}}); - auto h_b_lonlat = std::vector({{180, 0}, {-180, 30}}); - - auto h_expected = std::vector({1.5604449514735574e-12, 1.3513849691832763e-12}); - - auto a_lonlat = rmm::device_vector{h_a_lonlat}; - auto b_lonlat = rmm::device_vector{h_b_lonlat}; - - auto distance = rmm::device_vector{2, -1}; - auto expected = rmm::device_vector{h_expected}; - - auto distance_end = cuspatial::haversine_distance( - a_lonlat.begin(), a_lonlat.end(), b_lonlat.begin(), distance.begin()); - - EXPECT_EQ(expected, distance); - EXPECT_EQ(2, std::distance(distance.begin(), distance_end)); -} - -template -struct identity_xform { - using Location = cuspatial::lonlat_2d; - __device__ Location operator()(Location const& loc) { return loc; }; -}; - -// This test verifies that fancy iterators can be passed by using a pass-through transform_iterator -TYPED_TEST(HaversineTest, TransformIterator) -{ - using T = TypeParam; - using Location = cuspatial::lonlat_2d; - - auto h_a_lonlat = std::vector({{-180, 0}, {180, 30}}); - auto h_b_lonlat = std::vector({{180, 0}, {-180, 30}}); - - auto h_expected = std::vector({1.5604449514735574e-12, 1.3513849691832763e-12}); - - auto a_lonlat = rmm::device_vector{h_a_lonlat}; - auto b_lonlat = rmm::device_vector{h_b_lonlat}; - - auto distance = rmm::device_vector{2, -1}; - auto expected = rmm::device_vector{h_expected}; - - auto xform_begin = thrust::make_transform_iterator(a_lonlat.begin(), identity_xform{}); - auto xform_end = thrust::make_transform_iterator(a_lonlat.end(), identity_xform{}); - - auto distance_end = - cuspatial::haversine_distance(xform_begin, xform_end, b_lonlat.begin(), distance.begin()); - - EXPECT_EQ(expected, distance); - EXPECT_EQ(2, std::distance(distance.begin(), distance_end)); -} diff --git a/fetch_rapids.cmake b/fetch_rapids.cmake deleted file mode 100644 index 2382abe38..000000000 --- a/fetch_rapids.cmake +++ /dev/null @@ -1,17 +0,0 @@ -# ============================================================================= -# Copyright (c) 2018-2022, NVIDIA CORPORATION. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except -# in compliance with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed under the License -# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express -# or implied. See the License for the specific language governing permissions and limitations under -# the License. -# ============================================================================= -file(DOWNLOAD https://raw.githubusercontent.com/rapidsai/rapids-cmake/branch-22.06/RAPIDS.cmake - ${CMAKE_BINARY_DIR}/RAPIDS.cmake -) -include(${CMAKE_BINARY_DIR}/RAPIDS.cmake) diff --git a/java/src/main/native/CMakeLists.txt b/java/src/main/native/CMakeLists.txt index 1f71879a1..de9676445 100755 --- a/java/src/main/native/CMakeLists.txt +++ b/java/src/main/native/CMakeLists.txt @@ -13,13 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. #============================================================================= -cmake_minimum_required(VERSION 3.20.1 FATAL_ERROR) - -include(../../../../fetch_rapids.cmake) - -# TODO: The logic for setting the architectures was previously not here. Was -# that just an oversight, or is there a reason not to include this here? -rapids_cuda_init_architectures(CUSPATIAL_JNI) +cmake_minimum_required(VERSION 3.12 FATAL_ERROR) project(CUDF_JNI VERSION 0.7.0 LANGUAGES C CXX CUDA) @@ -27,7 +21,16 @@ project(CUDF_JNI VERSION 0.7.0 LANGUAGES C CXX CUDA) # - build type ------------------------------------------------------------------------------------ # Set a default build type if none was specified -rapids_cmake_build_type("Release") +set(DEFAULT_BUILD_TYPE "Release") + +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to '${DEFAULT_BUILD_TYPE}' since none specified.") + set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE + STRING "Choose the type of build." FORCE) + # Set the possible values of build type for cmake-gui + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Release" "MinSizeRel" "RelWithDebInfo") +endif() ################################################################################################### # - compiler options ------------------------------------------------------------------------------ @@ -51,8 +54,8 @@ if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GLIBCXX_USE_CXX11_ABI=0") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_GLIBCXX_USE_CXX11_ABI=0") set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Xcompiler -D_GLIBCXX_USE_CXX11_ABI=0") - endif() -endif() + endif(CMAKE_CXX11_ABI) +endif(CMAKE_COMPILER_IS_GNUCXX) #set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -gencode=arch=compute_60,code=sm_60 -gencode=arch=compute_61,code=sm_61") set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -gencode=arch=compute_60,code=sm_60") @@ -68,13 +71,13 @@ set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Werror cross-execution-space-call -Xc option(CMAKE_CUDA_LINEINFO "Enable the -lineinfo option for nvcc (useful for cuda-memcheck / profiler" OFF) if (CMAKE_CUDA_LINEINFO) set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") -endif() +endif(CMAKE_CUDA_LINEINFO) # Debug options if(CMAKE_BUILD_TYPE MATCHES Debug) message(STATUS "Building with debugging flags") set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G -Xcompiler -rdynamic") -endif() +endif(CMAKE_BUILD_TYPE MATCHES Debug) option(BUILD_TESTS "Configure CMake to build tests" ON) @@ -90,7 +93,7 @@ if(CUDA_STATIC_RUNTIME) set(CUDART_LIBRARY "cudart_static") else() set(CUDART_LIBRARY "cudart") -endif() +endif(CUDA_STATIC_RUNTIME) ################################################################################################### # - cmake modules --------------------------------------------------------------------------------- @@ -171,7 +174,7 @@ if(JNI_FOUND) message(STATUS "JDK with JNI in ${JNI_INCLUDE_DIRS}") else() message(FATAL_ERROR "JDK with JNI not found, please check your settings.") -endif() +endif(JNI_FOUND) ################################################################################################### # - include paths --------------------------------------------------------------------------------- @@ -210,13 +213,13 @@ if(USE_NVTX) find_library(NVTX_LIBRARY nvToolsExt PATH ${CMAKE_CUDA_IMPLICIT_LINK_DIRECTORIES}) target_link_libraries(cuspatialjni ${NVTX_LIBRARY}) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_NVTX") -endif() +endif(USE_NVTX) option(PER_THREAD_DEFAULT_STREAM "Build with per-thread default stream" OFF) if(PER_THREAD_DEFAULT_STREAM) message(STATUS "Using per-thread default stream") add_compile_definitions(CUDA_API_PER_THREAD_DEFAULT_STREAM) -endif() +endif(PER_THREAD_DEFAULT_STREAM) ################################################################################################### # - link libraries -------------------------------------------------------------------------------- From 9fa81365c8338492f074bd4b8c7b958a0258e81d Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 28 Apr 2022 12:05:32 -0700 Subject: [PATCH 42/67] Add macro for portable host-device operators --- cpp/include/cuspatial/types.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cpp/include/cuspatial/types.hpp b/cpp/include/cuspatial/types.hpp index 2b1adbfb8..7b5004ffa 100644 --- a/cpp/include/cuspatial/types.hpp +++ b/cpp/include/cuspatial/types.hpp @@ -18,6 +18,12 @@ #include +#ifdef __CUDACC__ +#define CUSPATIAL_HOST_DEVICE __host__ __device__ +#else +#define CUSPATIAL_HOST_DEVICE +#endif + namespace cuspatial { /** From cc0f5263884d43447baa3a64592510214d7fd82e Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 28 Apr 2022 12:25:47 -0700 Subject: [PATCH 43/67] Optimize `segment_distance` method --- cpp/src/spatial/linestring_distance.cu | 37 ++++++++++++++------------ 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index 5e862f2be..77fc4f4e2 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -81,15 +81,15 @@ T __device__ point_to_segment_distance_squared(vec_2d const& c, * @brief Computes shortest distance between two segments that doesn't intersect. */ template -double __device__ segment_distance_no_intersect_or_collinear(vec_2d const& A, - vec_2d const& B, - vec_2d const& C, - vec_2d const& D) +double __device__ segment_distance_no_intersect_or_collinear(vec_2d const& a, + vec_2d const& b, + vec_2d const& c, + vec_2d const& d) { - auto dist_sqr = std::min(std::min(point_to_segment_distance_squared(A, C, D), - point_to_segment_distance_squared(B, C, D)), - std::min(point_to_segment_distance_squared(C, A, B), - point_to_segment_distance_squared(D, A, B))); + auto dist_sqr = std::min(std::min(point_to_segment_distance_squared(a, c, d), + point_to_segment_distance_squared(b, c, d)), + std::min(point_to_segment_distance_squared(c, a, b), + point_to_segment_distance_squared(d, a, b))); return std::sqrt(dist_sqr); } @@ -101,19 +101,22 @@ double __device__ segment_distance_no_intersect_or_collinear(vec_2d const& A, */ template double __device__ -segment_distance(vec_2d const& A, vec_2d const& B, vec_2d const& C, vec_2d const& D) +segment_distance(vec_2d const& a, vec_2d const& b, vec_2d const& c, vec_2d const& d) { - double r_denom = (B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x); - double r_numer = (A.y - C.y) * (D.x - C.x) - (A.x - C.x) * (D.y - C.y); - if (r_denom == 0) { + auto ab = b - a; + auto ac = c - a; + auto cd = d - c; + auto denom = det(ab, cd); + + if (denom == 0) { // Segments parallel or collinear - return segment_distance_no_intersect_or_collinear(A, B, C, D); + return segment_distance_no_intersect_or_collinear(a, b, c, d); } - double r = r_numer / r_denom; - double s = ((A.y - C.y) * (B.x - A.x) - (A.x - C.x) * (B.y - A.y)) / - ((B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x)); + double r_numer = det(ac, cd); + double r = r_numer / denom; + double s = det(ac, ab) / denom; if (r >= 0 and r <= 1 and s >= 0 and s <= 1) { return 0.0; } - return segment_distance_no_intersect_or_collinear(A, B, C, D); + return segment_distance_no_intersect_or_collinear(a, b, c, d); } /** From f55db251a733661c56b7ce8637af34d63b1ed928 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 28 Apr 2022 12:28:53 -0700 Subject: [PATCH 44/67] Avoid using `double` for tempeoraries --- cpp/src/spatial/linestring_distance.cu | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index 77fc4f4e2..b3ae2eed9 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -81,10 +81,10 @@ T __device__ point_to_segment_distance_squared(vec_2d const& c, * @brief Computes shortest distance between two segments that doesn't intersect. */ template -double __device__ segment_distance_no_intersect_or_collinear(vec_2d const& a, - vec_2d const& b, - vec_2d const& c, - vec_2d const& d) +T __device__ segment_distance_no_intersect_or_collinear(vec_2d const& a, + vec_2d const& b, + vec_2d const& c, + vec_2d const& d) { auto dist_sqr = std::min(std::min(point_to_segment_distance_squared(a, c, d), point_to_segment_distance_squared(b, c, d)), @@ -100,7 +100,7 @@ double __device__ segment_distance_no_intersect_or_collinear(vec_2d const& a, * to segment distance. */ template -double __device__ +T __device__ segment_distance(vec_2d const& a, vec_2d const& b, vec_2d const& c, vec_2d const& d) { auto ab = b - a; @@ -112,9 +112,9 @@ segment_distance(vec_2d const& a, vec_2d const& b, vec_2d const& c, vec // Segments parallel or collinear return segment_distance_no_intersect_or_collinear(a, b, c, d); } - double r_numer = det(ac, cd); - double r = r_numer / denom; - double s = det(ac, ab) / denom; + auto r_numer = det(ac, cd); + auto r = r_numer / denom; + auto s = det(ac, ab) / denom; if (r >= 0 and r <= 1 and s >= 0 and s <= 1) { return 0.0; } return segment_distance_no_intersect_or_collinear(a, b, c, d); } @@ -205,7 +205,7 @@ void __global__ pairwise_linestring_distance_kernel(OffsetIterator linestring1_o vec_2d A{linestring1_points_xs_begin[p1_idx], linestring1_points_ys_begin[p1_idx]}; vec_2d B{linestring1_points_xs_begin[p1_idx + 1], linestring1_points_ys_begin[p1_idx + 1]}; - double min_distance = std::numeric_limits::max(); + T min_distance = std::numeric_limits::max(); for (cudf::size_type p2_idx = ls2_start; p2_idx < ls2_end; p2_idx++) { vec_2d C{linestring2_points_xs_begin[p2_idx], linestring2_points_ys_begin[p2_idx]}; vec_2d D{linestring2_points_xs_begin[p2_idx + 1], linestring2_points_ys_begin[p2_idx + 1]}; From 22ed7ecd03deb3a70a6cbf1d69a37fc308bb4a75 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 28 Apr 2022 12:31:04 -0700 Subject: [PATCH 45/67] Remove vec2d elementwise add --- cpp/include/cuspatial/utility/vec_2d.cuh | 9 --------- 1 file changed, 9 deletions(-) diff --git a/cpp/include/cuspatial/utility/vec_2d.cuh b/cpp/include/cuspatial/utility/vec_2d.cuh index 5e0df3675..ec5dd1e98 100644 --- a/cpp/include/cuspatial/utility/vec_2d.cuh +++ b/cpp/include/cuspatial/utility/vec_2d.cuh @@ -21,15 +21,6 @@ vec_2d CUSPATIAL_HOST_DEVICE operator-(vec_2d const& a, vec_2d const& b return vec_2d{a.x - b.x, a.y - b.y}; } -/** - * @brief Element-wise multiply of two 2d vectors. - */ -template -vec_2d CUSPATIAL_HOST_DEVICE operator*(vec_2d const& a, vec_2d const& b) -{ - return vec_2d{a.x * b.x, a.y * b.y}; -} - /** * @brief Scale a 2d vector by ratio @p r. */ From a477f2bae72bd0776e1ccd0566c87f664b5e0aec Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 28 Apr 2022 14:24:45 -0700 Subject: [PATCH 46/67] remove validation --- .../distances/linestring_distance.hpp | 2 ++ cpp/src/spatial/linestring_distance.cu | 28 ------------------- 2 files changed, 2 insertions(+), 28 deletions(-) diff --git a/cpp/include/cuspatial/distances/linestring_distance.hpp b/cpp/include/cuspatial/distances/linestring_distance.hpp index 04617b4a6..ee1ae0232 100644 --- a/cpp/include/cuspatial/distances/linestring_distance.hpp +++ b/cpp/include/cuspatial/distances/linestring_distance.hpp @@ -39,6 +39,8 @@ namespace cuspatial { * @param mr Device memory resource used to allocate the returned column's device memory. * @return A column of shortest distances between each pair of linestrings. * + * @note If any of the linestring contains less than 2 points, the behavior is undefined. + * * @throw cuspatial::logic_error if `linestring1_offsets.size() != linestring2_offsets.size()` * @throw cuspatial::logic_error if there is a size mismatch between the x- and y-coordinates of the * linestring points. diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index b3ae2eed9..6c83100f3 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -274,29 +274,6 @@ struct pairwise_linestring_distance_functor { } }; -/** - * @brief Check if every linestring in the input contains at least 2 end points. - */ -bool validate_linestring(cudf::device_span linestring_offsets, - cudf::column_view const& linestring_points_x, - rmm::cuda_stream_view stream) -{ - if (linestring_offsets.size() == 1) { return linestring_points_x.size() >= 2; } - return thrust::reduce( - rmm::exec_policy(stream), - thrust::make_counting_iterator(cudf::size_type{0}), - thrust::make_counting_iterator(static_cast(linestring_offsets.size())), - true, - [offsets = linestring_offsets.begin(), - num_offsets = static_cast(linestring_offsets.size()), - num_points = static_cast( - linestring_points_x.size())] __device__(bool prev, cudf::size_type i) { - cudf::size_type begin = offsets[i]; - cudf::size_type end = i == num_offsets ? num_points : offsets[i + 1]; - return prev && (end - begin); - }); -} - std::unique_ptr pairwise_linestring_distance( cudf::device_span linestring1_offsets, cudf::column_view const& linestring1_points_x, @@ -321,11 +298,6 @@ std::unique_ptr pairwise_linestring_distance( if (linestring1_offsets.size() == 0) { return cudf::empty_like(linestring1_points_x); } - CUSPATIAL_EXPECTS(validate_linestring(linestring1_offsets, linestring1_points_x, stream), - "Each item of linestring1 should contain at least 2 end points."); - CUSPATIAL_EXPECTS(validate_linestring(linestring2_offsets, linestring2_points_x, stream), - "Each item of linestring2 should contain at least 2 end points."); - return cudf::type_dispatcher(linestring1_points_x.type(), pairwise_linestring_distance_functor{}, linestring1_offsets, From 708906d554a703ddcb7de543ee1a705f26fd2678 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 28 Apr 2022 14:24:12 -0700 Subject: [PATCH 47/67] Add aka polylines --- cpp/include/cuspatial/distances/linestring_distance.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/include/cuspatial/distances/linestring_distance.hpp b/cpp/include/cuspatial/distances/linestring_distance.hpp index ee1ae0232..86573dd28 100644 --- a/cpp/include/cuspatial/distances/linestring_distance.hpp +++ b/cpp/include/cuspatial/distances/linestring_distance.hpp @@ -24,7 +24,7 @@ namespace cuspatial { /** - * @brief Compute shortest distance between pairs of linestrings + * @brief Compute shortest distance between pairs of linestrings (a.k.a. polylines) * * The shortest distance between two linestrings is defined as the shortest distance * between all pairs of segments of the two linestrings. If any of the segments intersect, From 2dc421bda2e384c6b0166ec36492b5c97f00fef0 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 28 Apr 2022 15:20:07 -0700 Subject: [PATCH 48/67] Add examples in docstring and test for it --- .../distances/linestring_distance.hpp | 50 +++++++++++++++++++ .../spatial/linestring_distance_test.cpp | 21 ++++++++ 2 files changed, 71 insertions(+) diff --git a/cpp/include/cuspatial/distances/linestring_distance.hpp b/cpp/include/cuspatial/distances/linestring_distance.hpp index 86573dd28..bf747818d 100644 --- a/cpp/include/cuspatial/distances/linestring_distance.hpp +++ b/cpp/include/cuspatial/distances/linestring_distance.hpp @@ -30,6 +30,56 @@ namespace cuspatial { * between all pairs of segments of the two linestrings. If any of the segments intersect, * the distance is 0. * + * The following example contains 4 pairs of linestrings. + * + * First pair: + * (0, 1) -> (1, 0) -> (-1, 0) + * (1, 1) -> (2, 1) -> (2, 0) -> (3, 0) + * + * | + * * #---# + * | \ | + * ----O---*---#---# + * | / + * * + * | + * + * The shortest distance between the two linstrings are the distance + * from point (1, 1) to segment (0, 1) -> (1, 0), which is sqrt(2)/2. + * + * Second pair: + * + * (0, 0) -> (0, 1) + * (0, 2) -> (0, 3) -> (1, 3) + * + * Both linestrings contains degenerate segments, their distance is 1. + * + * Third pair: + * + * (0, 0) -> (2, 2) -> (-2, 0) + * (2, 0) -> (0, 2) + * + * These linestrings intersects, their distance is 0 + * + * Forth pair: + * + * (2, 2) -> (-2, -2) + * (1, 1) -> (5, 5) -> (10, 0) + * + * These linestrings contain collinear and overlapping sections, + * their distance is 0. + * + * The input of above example is: + * linestring1_offsets: {0, 3, 5, 8} + * linestring1_points_x: {0, 1, -1, 0, 0, 0, 2, -2, 2, -2} + * linestring1_points_y: {1, 0, 0, 0, 1, 0, 2, 0, 2, -2} + * linestring2_offsets: {0, 4, 7, 9} + * linestring2_points_x: {1, 2, 2, 3, 0, 0, 1, 2, 0, 1, 5, 10} + * linestring2_points_y: {1, 1, 0, 0, 2, 3, 3, 0, 2, 1, 5, 0} + * + * Result: + * {sqrt(2.0)/2, 1, 0, 0} + * * @param linestring1_offsets Indices of the first point of the first linestring of each pair. * @param linestring1_points_x x-components of points in the first linestring of each pair. * @param linestring1_points_y y-component of points in the first linestring of each pair. diff --git a/cpp/tests/spatial/linestring_distance_test.cpp b/cpp/tests/spatial/linestring_distance_test.cpp index a54421ea8..78b623013 100644 --- a/cpp/tests/spatial/linestring_distance_test.cpp +++ b/cpp/tests/spatial/linestring_distance_test.cpp @@ -395,5 +395,26 @@ TYPED_TEST(PairwiseLinestringDistanceTest, TwoPairs) expect_columns_equivalent(expected, *got, verbosity); } +TYPED_TEST(PairwiseLinestringDistanceTest, FourPairs) +{ + using T = TypeParam; + wrapper linestring1_offsets{0, 3, 5, 8}; + wrapper linestring1_points_x{0, 1, -1, 0, 0, 0, 2, -2, 2, -2}; + wrapper linestring1_points_y{1, 0, 0, 0, 1, 0, 2, 0, 2, -2}; + wrapper linestring2_offsets{0, 4, 7, 9}; + wrapper linestring2_points_x{1, 2, 2, 3, 0, 0, 1, 2, 0, 1, 5, 10}; + wrapper linestring2_points_y{1, 1, 0, 0, 2, 3, 3, 0, 2, 1, 5, 0}; + + wrapper expected{std::sqrt(2.0) * 0.5, 1.0, 0.0, 0.0}; + + auto got = pairwise_linestring_distance(column_view(linestring1_offsets), + linestring1_points_x, + linestring1_points_y, + column_view(linestring2_offsets), + linestring2_points_x, + linestring2_points_y); + expect_columns_equivalent(expected, *got, verbosity); +} + } // namespace test } // namespace cuspatial From 497f1d1debbfaf5e44ba998013c9b86d48174235 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 28 Apr 2022 16:48:22 -0700 Subject: [PATCH 49/67] update docstring of `det` --- cpp/include/cuspatial/utility/vec_2d.cuh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/include/cuspatial/utility/vec_2d.cuh b/cpp/include/cuspatial/utility/vec_2d.cuh index ec5dd1e98..5e4bf0881 100644 --- a/cpp/include/cuspatial/utility/vec_2d.cuh +++ b/cpp/include/cuspatial/utility/vec_2d.cuh @@ -49,7 +49,7 @@ T CUSPATIAL_HOST_DEVICE dot(vec_2d const& a, vec_2d const& b) } /** - * @brief Compute 2d determinant of two 2d vectors. + * @brief Compute 2d determinant of a 2x2 matrix with column vectors @p a and @p b. */ template T CUSPATIAL_HOST_DEVICE det(vec_2d const& a, vec_2d const& b) From c09689a13ab3b527b20407663aef15ca8f48fefc Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 28 Apr 2022 18:16:24 -0700 Subject: [PATCH 50/67] Add denormalized determinants test --- .../spatial/linestring_distance_test.cpp | 55 ++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/cpp/tests/spatial/linestring_distance_test.cpp b/cpp/tests/spatial/linestring_distance_test.cpp index 78b623013..2913627bf 100644 --- a/cpp/tests/spatial/linestring_distance_test.cpp +++ b/cpp/tests/spatial/linestring_distance_test.cpp @@ -35,6 +35,9 @@ template struct PairwiseLinestringDistanceTest : public BaseFixture { }; +struct PairwiseLinestringDistanceTestUntyped : public BaseFixture { +}; + // float and double are logically the same but would require separate tests due to precision. using TestTypes = FloatingPointTypes; TYPED_TEST_CASE(PairwiseLinestringDistanceTest, TestTypes); @@ -265,6 +268,54 @@ TYPED_TEST(PairwiseLinestringDistanceTest, OnePairDegenerateCollinearIntersect) expect_columns_equivalent(expected, *got, verbosity); } +TEST_F(PairwiseLinestringDistanceTestUntyped, OnePairDeterminantDoublePrecisionDenormalized) +{ + // Vector ab: (1e-155, 2e-155) + // Vector cd: (2e-155, 1e-155) + // deternminant of matrix [a, b] = -3e-310, a denormalized number + + wrapper linestring1_offsets{0}; + wrapper linestring1_points_x{0.0, 1e-155}; + wrapper linestring1_points_y{0.0, 2e-155}; + wrapper linestring2_offsets{0}; + wrapper linestring2_points_x{4e-155, 6e-155}; + wrapper linestring2_points_y{5e-155, 6e-155}; + + wrapper expected{4.24264068711929e-155}; + + auto got = pairwise_linestring_distance(column_view(linestring1_offsets), + linestring1_points_x, + linestring1_points_y, + column_view(linestring2_offsets), + linestring2_points_x, + linestring2_points_y); + expect_columns_equivalent(expected, *got, verbosity); +} + +TEST_F(PairwiseLinestringDistanceTestUntyped, OnePairDeterminantSinglePrecisionDenormalized) +{ + // Vector ab: (1e-20, 2e-20) + // Vector cd: (2e-20, 1e-20) + // deternminant of matrix [ab, cd] = -3e-40, a denormalized number + + wrapper linestring1_offsets{0}; + wrapper linestring1_points_x{0.0, 1e-20}; + wrapper linestring1_points_y{0.0, 2e-20}; + wrapper linestring2_offsets{0}; + wrapper linestring2_points_x{4e-20, 6e-20}; + wrapper linestring2_points_y{5e-20, 6e-20}; + + wrapper expected{4.2426405524813e-20}; + + auto got = pairwise_linestring_distance(column_view(linestring1_offsets), + linestring1_points_x, + linestring1_points_y, + column_view(linestring2_offsets), + linestring2_points_x, + linestring2_points_y); + expect_columns_equivalent(expected, *got, verbosity); +} + TYPED_TEST(PairwiseLinestringDistanceTest, OnePairRandom1) { using T = TypeParam; @@ -286,7 +337,7 @@ TYPED_TEST(PairwiseLinestringDistanceTest, OnePairRandom1) expect_columns_equivalent(expected, *got, verbosity); } -TYPED_TEST(PairwiseLinestringDistanceTest, OnePairGeolife) +TYPED_TEST(PairwiseLinestringDistanceTest, OnePairIntersectFromRealData1) { // Example extracted from a pair of trajectry in geolife dataset using T = TypeParam; @@ -326,7 +377,7 @@ TYPED_TEST(PairwiseLinestringDistanceTest, OnePairGeolife) expect_columns_equivalent(expected, *got, verbosity); } -TYPED_TEST(PairwiseLinestringDistanceTest, OnePairGeolife2) +TYPED_TEST(PairwiseLinestringDistanceTest, OnePairFromRealData) { // Example extracted from a pair of trajectry in geolife dataset using T = TypeParam; From 7023faa13c0eae2009d81b5b5be5d2eff02ebbac Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 2 May 2022 10:47:50 -0700 Subject: [PATCH 51/67] adopt almost always auto --- cpp/src/spatial/linestring_distance.cu | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index 6c83100f3..8c26f2efa 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -65,12 +65,12 @@ T __device__ point_to_segment_distance_squared(vec_2d const& c, vec_2d const& a, vec_2d const& b) { - vec_2d ab = b - a; - auto ac = c - a; - auto bc = c - b; - T l_squared = dot(ab, ab); + vec_2d ab = b - a; + auto ac = c - a; + auto bc = c - b; + auto l_squared = dot(ab, ab); if (l_squared == 0) { return dot(ac, ac); } - T r = dot(ac, ab) / l_squared; + auto r = dot(ac, ab) / l_squared; if (r <= 0 or r >= 1) { return std::min(dot(ac, ac), dot(bc, bc)); } auto p = a + r * ab; auto pc = c - p; From 8004ec9016955ae295f1bc04826b5811ecb432f8 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 2 May 2022 11:00:25 -0700 Subject: [PATCH 52/67] doc updates --- cpp/include/cuspatial/distances/linestring_distance.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cpp/include/cuspatial/distances/linestring_distance.hpp b/cpp/include/cuspatial/distances/linestring_distance.hpp index bf747818d..038e7244e 100644 --- a/cpp/include/cuspatial/distances/linestring_distance.hpp +++ b/cpp/include/cuspatial/distances/linestring_distance.hpp @@ -77,8 +77,7 @@ namespace cuspatial { * linestring2_points_x: {1, 2, 2, 3, 0, 0, 1, 2, 0, 1, 5, 10} * linestring2_points_y: {1, 1, 0, 0, 2, 3, 3, 0, 2, 1, 5, 0} * - * Result: - * {sqrt(2.0)/2, 1, 0, 0} + * Result: {sqrt(2.0)/2, 1, 0, 0} * * @param linestring1_offsets Indices of the first point of the first linestring of each pair. * @param linestring1_points_x x-components of points in the first linestring of each pair. From 654c08b5c7c2a16feb782f46e1d0c070816bb63a Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 3 May 2022 11:22:28 -0700 Subject: [PATCH 53/67] Several docstring updates Co-authored-by: Mark Harris --- cpp/include/cuspatial/distances/linestring_distance.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp/include/cuspatial/distances/linestring_distance.hpp b/cpp/include/cuspatial/distances/linestring_distance.hpp index 038e7244e..d8ffdc8fb 100644 --- a/cpp/include/cuspatial/distances/linestring_distance.hpp +++ b/cpp/include/cuspatial/distances/linestring_distance.hpp @@ -44,7 +44,7 @@ namespace cuspatial { * * * | * - * The shortest distance between the two linstrings are the distance + * The shortest distance between the two linestrings is the distance * from point (1, 1) to segment (0, 1) -> (1, 0), which is sqrt(2)/2. * * Second pair: @@ -52,21 +52,21 @@ namespace cuspatial { * (0, 0) -> (0, 1) * (0, 2) -> (0, 3) -> (1, 3) * - * Both linestrings contains degenerate segments, their distance is 1. + * Both linestrings contain degenerate segments. Their distance is 1 (point (0, 1) to point (0, 2)). * * Third pair: * * (0, 0) -> (2, 2) -> (-2, 0) * (2, 0) -> (0, 2) * - * These linestrings intersects, their distance is 0 + * These linestrings intersect, so their distance is 0. * * Forth pair: * * (2, 2) -> (-2, -2) * (1, 1) -> (5, 5) -> (10, 0) * - * These linestrings contain collinear and overlapping sections, + * These linestrings contain colinear and overlapping sections, so * their distance is 0. * * The input of above example is: From c864dca2d4b43acf0098f3a596ee1e014cf66bdc Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 3 May 2022 11:26:07 -0700 Subject: [PATCH 54/67] update colinear names --- cpp/src/spatial/linestring_distance.cu | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index 8c26f2efa..8396de62b 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -81,7 +81,7 @@ T __device__ point_to_segment_distance_squared(vec_2d const& c, * @brief Computes shortest distance between two segments that doesn't intersect. */ template -T __device__ segment_distance_no_intersect_or_collinear(vec_2d const& a, +T __device__ segment_distance_no_intersect_or_colinear(vec_2d const& a, vec_2d const& b, vec_2d const& c, vec_2d const& d) @@ -110,13 +110,13 @@ segment_distance(vec_2d const& a, vec_2d const& b, vec_2d const& c, vec if (denom == 0) { // Segments parallel or collinear - return segment_distance_no_intersect_or_collinear(a, b, c, d); + return segment_distance_no_intersect_or_colinear(a, b, c, d); } auto r_numer = det(ac, cd); auto r = r_numer / denom; auto s = det(ac, ab) / denom; if (r >= 0 and r <= 1 and s >= 0 and s <= 1) { return 0.0; } - return segment_distance_no_intersect_or_collinear(a, b, c, d); + return segment_distance_no_intersect_or_colinear(a, b, c, d); } /** From ddf497e6755281df44be62c13784fb0e5063c62e Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 3 May 2022 11:27:11 -0700 Subject: [PATCH 55/67] fix typo --- cpp/tests/spatial/linestring_distance_test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/tests/spatial/linestring_distance_test.cpp b/cpp/tests/spatial/linestring_distance_test.cpp index 2913627bf..2f3724083 100644 --- a/cpp/tests/spatial/linestring_distance_test.cpp +++ b/cpp/tests/spatial/linestring_distance_test.cpp @@ -272,7 +272,7 @@ TEST_F(PairwiseLinestringDistanceTestUntyped, OnePairDeterminantDoublePrecisionD { // Vector ab: (1e-155, 2e-155) // Vector cd: (2e-155, 1e-155) - // deternminant of matrix [a, b] = -3e-310, a denormalized number + // determinant of matrix [a, b] = -3e-310, a denormalized number wrapper linestring1_offsets{0}; wrapper linestring1_points_x{0.0, 1e-155}; @@ -296,7 +296,7 @@ TEST_F(PairwiseLinestringDistanceTestUntyped, OnePairDeterminantSinglePrecisionD { // Vector ab: (1e-20, 2e-20) // Vector cd: (2e-20, 1e-20) - // deternminant of matrix [ab, cd] = -3e-40, a denormalized number + // determinant of matrix [ab, cd] = -3e-40, a denormalized number wrapper linestring1_offsets{0}; wrapper linestring1_points_x{0.0, 1e-20}; From 2da3a66ed95bf023056bf381ed8ff740c67052a5 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 3 May 2022 11:32:25 -0700 Subject: [PATCH 56/67] Template out the sizetype --- cpp/src/spatial/linestring_distance.cu | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index 8396de62b..c26a2b98a 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -44,12 +44,12 @@ namespace { * @note The last endpoint of the linestring is not included in the offset array, thus * @p num_points is returned. */ -template -inline cudf::size_type __device__ -endpoint_index_of_linestring(cudf::size_type const& linestring_idx, +template +inline SizeType __device__ +endpoint_index_of_linestring(SizeType const& linestring_idx, OffsetIterator const& linestring_offsets_begin, - cudf::size_type const& num_linestrings, - cudf::size_type const& num_points) + SizeType const& num_linestrings, + SizeType const& num_points) { return (linestring_idx == (num_linestrings - 1) ? (num_points) @@ -82,9 +82,9 @@ T __device__ point_to_segment_distance_squared(vec_2d const& c, */ template T __device__ segment_distance_no_intersect_or_colinear(vec_2d const& a, - vec_2d const& b, - vec_2d const& c, - vec_2d const& d) + vec_2d const& b, + vec_2d const& c, + vec_2d const& d) { auto dist_sqr = std::min(std::min(point_to_segment_distance_squared(a, c, d), point_to_segment_distance_squared(b, c, d)), From 338a5ad7d3c0f3c01c8dc978647ca7fae27ec33f Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 3 May 2022 11:38:24 -0700 Subject: [PATCH 57/67] Move 2d vector type definitions to `vec_2d.hpp` --- cpp/include/cuspatial/types.hpp | 24 ------------------- .../utility/{vec_2d.cuh => vec_2d.hpp} | 24 +++++++++++++++++++ cpp/src/spatial/linestring_distance.cu | 2 +- 3 files changed, 25 insertions(+), 25 deletions(-) rename cpp/include/cuspatial/utility/{vec_2d.cuh => vec_2d.hpp} (68%) diff --git a/cpp/include/cuspatial/types.hpp b/cpp/include/cuspatial/types.hpp index 7b5004ffa..08dc36bca 100644 --- a/cpp/include/cuspatial/types.hpp +++ b/cpp/include/cuspatial/types.hpp @@ -26,30 +26,6 @@ namespace cuspatial { -/** - * @brief A 2D vector - * - * Used in cuspatial for both Longitude/Latitude (LonLat) coordinate pairs and Cartesian (X/Y) - * coordinate pairs. For LonLat pairs, the `x` member represents Longitude, and `y` represents - * Latitude. - * - * @tparam T the base type for the coordinates - */ -template -struct alignas(2 * sizeof(T)) vec_2d { - using value_type = T; - value_type x; - value_type y; -}; - -template -struct alignas(2 * sizeof(T)) lonlat_2d : vec_2d { -}; - -template -struct alignas(2 * sizeof(T)) cartesian_2d : vec_2d { -}; - /** * @brief A timestamp * diff --git a/cpp/include/cuspatial/utility/vec_2d.cuh b/cpp/include/cuspatial/utility/vec_2d.hpp similarity index 68% rename from cpp/include/cuspatial/utility/vec_2d.cuh rename to cpp/include/cuspatial/utility/vec_2d.hpp index 5e4bf0881..33490a946 100644 --- a/cpp/include/cuspatial/utility/vec_2d.cuh +++ b/cpp/include/cuspatial/utility/vec_2d.hpp @@ -3,6 +3,30 @@ namespace cuspatial { +/** + * @brief A 2D vector + * + * Used in cuspatial for both Longitude/Latitude (LonLat) coordinate pairs and Cartesian (X/Y) + * coordinate pairs. For LonLat pairs, the `x` member represents Longitude, and `y` represents + * Latitude. + * + * @tparam T the base type for the coordinates + */ +template +struct alignas(2 * sizeof(T)) vec_2d { + using value_type = T; + value_type x; + value_type y; +}; + +template +struct alignas(2 * sizeof(T)) lonlat_2d : vec_2d { +}; + +template +struct alignas(2 * sizeof(T)) cartesian_2d : vec_2d { +}; + /** * @brief Element-wise add of two 2d vectors. */ diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index c26a2b98a..bd1aa3f80 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -16,7 +16,7 @@ #include #include -#include +#include #include #include From 94b78f3dff1dade549d58315bbf7a58f4b53fda2 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 3 May 2022 13:08:12 -0700 Subject: [PATCH 58/67] Include vector types in haversine method --- cpp/include/cuspatial/experimental/detail/haversine.cuh | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/include/cuspatial/experimental/detail/haversine.cuh b/cpp/include/cuspatial/experimental/detail/haversine.cuh index 938809683..e9831e794 100644 --- a/cpp/include/cuspatial/experimental/detail/haversine.cuh +++ b/cpp/include/cuspatial/experimental/detail/haversine.cuh @@ -19,6 +19,7 @@ #include #include #include +#include #include #include From a3e84ee38d237393f6d7a25482a02bc55c92f248 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 3 May 2022 18:23:55 -0700 Subject: [PATCH 59/67] update example and test --- cpp/include/cuspatial/distances/linestring_distance.hpp | 8 ++++---- cpp/tests/spatial/linestring_distance_test.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cpp/include/cuspatial/distances/linestring_distance.hpp b/cpp/include/cuspatial/distances/linestring_distance.hpp index d8ffdc8fb..d5f3f87f9 100644 --- a/cpp/include/cuspatial/distances/linestring_distance.hpp +++ b/cpp/include/cuspatial/distances/linestring_distance.hpp @@ -50,9 +50,9 @@ namespace cuspatial { * Second pair: * * (0, 0) -> (0, 1) - * (0, 2) -> (0, 3) -> (1, 3) + * (1, 0) -> (1, 1) -> (1, 2) * - * Both linestrings contain degenerate segments. Their distance is 1 (point (0, 1) to point (0, 2)). + * These linestrings are parallel. Their distance is 1 (point (0, 0) to point (1, 0)). * * Third pair: * @@ -74,8 +74,8 @@ namespace cuspatial { * linestring1_points_x: {0, 1, -1, 0, 0, 0, 2, -2, 2, -2} * linestring1_points_y: {1, 0, 0, 0, 1, 0, 2, 0, 2, -2} * linestring2_offsets: {0, 4, 7, 9} - * linestring2_points_x: {1, 2, 2, 3, 0, 0, 1, 2, 0, 1, 5, 10} - * linestring2_points_y: {1, 1, 0, 0, 2, 3, 3, 0, 2, 1, 5, 0} + * linestring2_points_x: {1, 2, 2, 3, 1, 1, 1, 2, 0, 1, 5, 10} + * linestring2_points_y: {1, 1, 0, 0, 0, 1, 2, 0, 2, 1, 5, 0} * * Result: {sqrt(2.0)/2, 1, 0, 0} * diff --git a/cpp/tests/spatial/linestring_distance_test.cpp b/cpp/tests/spatial/linestring_distance_test.cpp index 2f3724083..87d77e6e9 100644 --- a/cpp/tests/spatial/linestring_distance_test.cpp +++ b/cpp/tests/spatial/linestring_distance_test.cpp @@ -453,8 +453,8 @@ TYPED_TEST(PairwiseLinestringDistanceTest, FourPairs) wrapper linestring1_points_x{0, 1, -1, 0, 0, 0, 2, -2, 2, -2}; wrapper linestring1_points_y{1, 0, 0, 0, 1, 0, 2, 0, 2, -2}; wrapper linestring2_offsets{0, 4, 7, 9}; - wrapper linestring2_points_x{1, 2, 2, 3, 0, 0, 1, 2, 0, 1, 5, 10}; - wrapper linestring2_points_y{1, 1, 0, 0, 2, 3, 3, 0, 2, 1, 5, 0}; + wrapper linestring2_points_x{1, 2, 2, 3, 1, 1, 1, 2, 0, 1, 5, 10}; + wrapper linestring2_points_y{1, 1, 0, 0, 0, 1, 2, 0, 2, 1, 5, 0}; wrapper expected{std::sqrt(2.0) * 0.5, 1.0, 0.0, 0.0}; From c40b81f819a30b45bf7a1622741e7b68dc5ef512 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 5 May 2022 10:31:58 -0700 Subject: [PATCH 60/67] Lift sqrt outside of the segment distance. --- cpp/src/spatial/linestring_distance.cu | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index bd1aa3f80..40c225e3c 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -90,7 +90,7 @@ T __device__ segment_distance_no_intersect_or_colinear(vec_2d const& a, point_to_segment_distance_squared(b, c, d)), std::min(point_to_segment_distance_squared(c, a, b), point_to_segment_distance_squared(d, a, b))); - return std::sqrt(dist_sqr); + return dist_sqr; } /** @@ -100,8 +100,10 @@ T __device__ segment_distance_no_intersect_or_colinear(vec_2d const& a, * to segment distance. */ template -T __device__ -segment_distance(vec_2d const& a, vec_2d const& b, vec_2d const& c, vec_2d const& d) +T __device__ squared_segment_distance(vec_2d const& a, + vec_2d const& b, + vec_2d const& c, + vec_2d const& d) { auto ab = b - a; auto ac = c - a; @@ -205,13 +207,13 @@ void __global__ pairwise_linestring_distance_kernel(OffsetIterator linestring1_o vec_2d A{linestring1_points_xs_begin[p1_idx], linestring1_points_ys_begin[p1_idx]}; vec_2d B{linestring1_points_xs_begin[p1_idx + 1], linestring1_points_ys_begin[p1_idx + 1]}; - T min_distance = std::numeric_limits::max(); + T min_squared_distance = std::numeric_limits::max(); for (cudf::size_type p2_idx = ls2_start; p2_idx < ls2_end; p2_idx++) { vec_2d C{linestring2_points_xs_begin[p2_idx], linestring2_points_ys_begin[p2_idx]}; vec_2d D{linestring2_points_xs_begin[p2_idx + 1], linestring2_points_ys_begin[p2_idx + 1]}; - min_distance = std::min(min_distance, segment_distance(A, B, C, D)); + min_squared_distance = std::min(min_squared_distance, squared_segment_distance(A, B, C, D)); } - atomicMin(distances + linestring_idx, static_cast(min_distance)); + atomicMin(distances + linestring_idx, static_cast(std::sqrt(min_squared_distance))); } } // anonymous namespace From 65a835ce6328f74a843b2eea2cc9e64138f25555 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 5 May 2022 11:41:21 -0700 Subject: [PATCH 61/67] Update docstring LRAI links. --- cpp/src/spatial/linestring_distance.cu | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index 40c225e3c..75256ac34 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -132,14 +132,11 @@ T __device__ squared_segment_distance(vec_2d const& a, * to form the globally minimum distance between the linestrings. * * @tparam CoordinateIterator Iterator to coordinates. Must meet requirements of - * [LegacyRandomAccessIterator][https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator] - * and is device-accessible. - * @tparam OffsetIterator Iterator to linestring offsets. Must meet requirements of - * [LegacyRandomAccessIterator][https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator] - * and is device-accessible. - * @tparam OutputIterator Iterator to output distances. Must meet requirements of - * [LegacyRandomAccessIterator][https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator] - * and is device-accessible. + * [LegacyRandomAccessIterator][LinkLRAI] and be device-accessible. + * @tparam OffsetIterator Iterator to linestring offsets. Must meet requirements of + * [LegacyRandomAccessIterator][LinkLRAI] and be device-accessible. + * @tparam OutputIterator Iterator to output distances. Must meet requirements of + * [LegacyRandomAccessIterator][LinkLRAI] and be device-accessible. * * @param[in] linestring1_offsets_begin Iterator to the begin of the range of linestring offsets * in pair 1. @@ -160,7 +157,9 @@ T __device__ squared_segment_distance(vec_2d const& a, * @param[in] linestring2_points_ys_begin Iterator to the begin of the range of y coordinates of * points in pair 2. * @param[out] distances Iterator to the output range of shortest distances between pairs. - * @return + * + * [LinkLRAI]: https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator + * "LegacyRandomAccessIterator" */ template void __global__ pairwise_linestring_distance_kernel(OffsetIterator linestring1_offsets_begin, From 23875ee9286f8ad0210b80a85d8a3220f9f0c0e6 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 5 May 2022 15:48:29 -0700 Subject: [PATCH 62/67] Replace type declaration with `auto` Co-authored-by: Vyas Ramasubramani --- cpp/src/spatial/linestring_distance.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index 75256ac34..633705b00 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -65,7 +65,7 @@ T __device__ point_to_segment_distance_squared(vec_2d const& c, vec_2d const& a, vec_2d const& b) { - vec_2d ab = b - a; + auto ab = b - a; auto ac = c - a; auto bc = c - b; auto l_squared = dot(ab, ab); From ac53f8de83248dba8632b7cad85ec6c16ba00a1c Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 5 May 2022 15:49:14 -0700 Subject: [PATCH 63/67] Delay computations Co-authored-by: Vyas Ramasubramani --- cpp/src/spatial/linestring_distance.cu | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index 633705b00..3337aa046 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -70,9 +70,9 @@ T __device__ point_to_segment_distance_squared(vec_2d const& c, auto bc = c - b; auto l_squared = dot(ab, ab); if (l_squared == 0) { return dot(ac, ac); } - auto r = dot(ac, ab) / l_squared; - if (r <= 0 or r >= 1) { return std::min(dot(ac, ac), dot(bc, bc)); } - auto p = a + r * ab; + auto r = dot(ac, ab); + if (r <= 0 or r >= l_squared) { return std::min(dot(ac, ac), dot(bc, bc)); } + auto p = a + (r / l_squared) * ab; auto pc = c - p; return dot(pc, pc); } @@ -106,7 +106,6 @@ T __device__ squared_segment_distance(vec_2d const& a, vec_2d const& d) { auto ab = b - a; - auto ac = c - a; auto cd = d - c; auto denom = det(ab, cd); @@ -114,6 +113,8 @@ T __device__ squared_segment_distance(vec_2d const& a, // Segments parallel or collinear return segment_distance_no_intersect_or_colinear(a, b, c, d); } + + auto ac = c - a; auto r_numer = det(ac, cd); auto r = r_numer / denom; auto s = det(ac, ab) / denom; From b546e7afd112e943ed968f7550fffd5a72fff17a Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 5 May 2022 15:49:53 -0700 Subject: [PATCH 64/67] Use constexpr for `tpb` Co-authored-by: Vyas Ramasubramani --- cpp/src/spatial/linestring_distance.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index 3337aa046..e9b80c7d9 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -247,7 +247,7 @@ struct pairwise_linestring_distance_functor { distances->mutable_view().end(), std::numeric_limits::max()); - std::size_t const threads_per_block = 64; + std::size_t constexpr threads_per_block = 64; std::size_t const num_blocks = (linestring1_points_x.size() + threads_per_block - 1) / threads_per_block; From 26fa8d433c0cf4a67907c2262ae275073bf8e38d Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 5 May 2022 15:58:10 -0700 Subject: [PATCH 65/67] Replace division with multiply denom reciprocals --- cpp/src/spatial/linestring_distance.cu | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index e9b80c7d9..fbefe6437 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -65,7 +65,7 @@ T __device__ point_to_segment_distance_squared(vec_2d const& c, vec_2d const& a, vec_2d const& b) { - auto ab = b - a; + auto ab = b - a; auto ac = c - a; auto bc = c - b; auto l_squared = dot(ab, ab); @@ -114,10 +114,11 @@ T __device__ squared_segment_distance(vec_2d const& a, return segment_distance_no_intersect_or_colinear(a, b, c, d); } - auto ac = c - a; - auto r_numer = det(ac, cd); - auto r = r_numer / denom; - auto s = det(ac, ab) / denom; + auto ac = c - a; + auto r_numer = det(ac, cd); + auto denom_reciprocal = 1 / denom; + auto r = r_numer * denom_reciprocal; + auto s = det(ac, ab) * denom_reciprocal; if (r >= 0 and r <= 1 and s >= 0 and s <= 1) { return 0.0; } return segment_distance_no_intersect_or_colinear(a, b, c, d); } From b7dda2cf18829dcf6bb3f6a7ec6d0886f0cc4d37 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 5 May 2022 16:00:51 -0700 Subject: [PATCH 66/67] delay computing `bc` --- cpp/src/spatial/linestring_distance.cu | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index fbefe6437..8bcfc031f 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -67,10 +67,10 @@ T __device__ point_to_segment_distance_squared(vec_2d const& c, { auto ab = b - a; auto ac = c - a; - auto bc = c - b; auto l_squared = dot(ab, ab); if (l_squared == 0) { return dot(ac, ac); } - auto r = dot(ac, ab); + auto r = dot(ac, ab); + auto bc = c - b; if (r <= 0 or r >= l_squared) { return std::min(dot(ac, ac), dot(bc, bc)); } auto p = a + (r / l_squared) * ab; auto pc = c - p; From 2c60a5d8eb4e29eee03c927c53caeb202c7e93be Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 5 May 2022 16:04:39 -0700 Subject: [PATCH 67/67] docstring fixes --- cpp/src/spatial/linestring_distance.cu | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cpp/src/spatial/linestring_distance.cu b/cpp/src/spatial/linestring_distance.cu index 8bcfc031f..036086ea2 100644 --- a/cpp/src/spatial/linestring_distance.cu +++ b/cpp/src/spatial/linestring_distance.cu @@ -58,7 +58,7 @@ endpoint_index_of_linestring(SizeType const& linestring_idx, } /** - * @brief Computes shortest distance between @p C and segment @p A @p B + * @brief Computes shortest distance between @p c and segment ab */ template T __device__ point_to_segment_distance_squared(vec_2d const& c, @@ -71,6 +71,7 @@ T __device__ point_to_segment_distance_squared(vec_2d const& c, if (l_squared == 0) { return dot(ac, ac); } auto r = dot(ac, ab); auto bc = c - b; + // If the projection of `c` is outside of segment `ab`, compute point-point distance. if (r <= 0 or r >= l_squared) { return std::min(dot(ac, ac), dot(bc, bc)); } auto p = a + (r / l_squared) * ab; auto pc = c - p; @@ -78,7 +79,8 @@ T __device__ point_to_segment_distance_squared(vec_2d const& c, } /** - * @brief Computes shortest distance between two segments that doesn't intersect. + * @brief Computes shortest distance between two segments (ab and cd) that + * doesn't intersect. */ template T __device__ segment_distance_no_intersect_or_colinear(vec_2d const& a,