Skip to content

Commit

Permalink
Column API for pairwise_polygon_distance (#1073)
Browse files Browse the repository at this point in the history
This PR adds column API for `pairwise_polygon_distance`.

This PR also adds `CUSPATIAL_HOST_DEVICE_EXPECTS` macro to help error assertion in `__host__ __device__` functions.

closes #1053 

depends on #1065

Authors:
  - Michael Wang (https://github.com/isVoid)

Approvers:
  - H. Thomson Comer (https://github.com/thomcom)
  - Mark Harris (https://github.com/harrism)

URL: #1073
  • Loading branch information
isVoid authored May 2, 2023
1 parent 8c66988 commit 2d02073
Show file tree
Hide file tree
Showing 10 changed files with 404 additions and 31 deletions.
1 change: 1 addition & 0 deletions cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ add_library(cuspatial
src/spatial/point_linestring_distance.cu
src/spatial/point_polygon_distance.cu
src/spatial/linestring_polygon_distance.cu
src/spatial/polygon_distance.cu
src/spatial/point_linestring_nearest_points.cu
src/spatial/sinusoidal_projection.cu
src/trajectory/derive_trajectories.cu
Expand Down
35 changes: 35 additions & 0 deletions cpp/include/cuspatial/assert.cuh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2023, NVIDIA CORPORATION.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once

#include <cuda_runtime.h>

/**
* @brief `assert`-like macro for device code
*
* This is effectively the same as the standard `assert` macro, except it
* relies on the `__PRETTY_FUNCTION__` macro which is specific to GCC and Clang
* to produce better assert messages.
*/
#if !defined(NDEBUG) && defined(__CUDA_ARCH__) && (defined(__clang__) || defined(__GNUC__))
#define __ASSERT_STR_HELPER(x) #x
#define cuspatial_assert(e) \
((e) ? static_cast<void>(0) \
: __assert_fail(__ASSERT_STR_HELPER(e), __FILE__, __LINE__, __PRETTY_FUNCTION__))
#else
#define cuspatial_assert(e) (static_cast<void>(0))
#endif
43 changes: 22 additions & 21 deletions cpp/include/cuspatial/detail/utility/validation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@
* [2]: https://arrow.apache.org/docs/format/Columnar.html#variable-size-binary-layout
*/
#define CUSPATIAL_EXPECTS_VALID_LINESTRING_SIZES(num_linestring_points, num_linestring_offsets) \
CUSPATIAL_EXPECTS(num_linestring_offsets > 0, \
"Polygon offsets must contain at least one (1) value"); \
CUSPATIAL_EXPECTS(num_linestring_points >= 2 * (num_linestring_offsets - 1), \
"Each linestring must have at least two vertices");
CUSPATIAL_HOST_DEVICE_EXPECTS(num_linestring_offsets > 0, \
"Polygon offsets must contain at least one (1) value"); \
CUSPATIAL_HOST_DEVICE_EXPECTS(num_linestring_points >= 2 * (num_linestring_offsets - 1), \
"Each linestring must have at least two vertices");

/**
* @brief Macro for validating the data array sizes for multilinestrings.
Expand All @@ -57,10 +57,10 @@
* [1]: https://github.com/geoarrow/geoarrow/blob/main/format.md
* [2]: https://arrow.apache.org/docs/format/Columnar.html#variable-size-binary-layout
*/
#define CUSPATIAL_EXPECTS_VALID_MULTILINESTRING_SIZES( \
num_linestring_points, num_multilinestring_offsets, num_linestring_offsets) \
CUSPATIAL_EXPECTS(num_multilinestring_offsets > 0, \
"Multilinestring offsets must contain at least one (1) value"); \
#define CUSPATIAL_EXPECTS_VALID_MULTILINESTRING_SIZES( \
num_linestring_points, num_multilinestring_offsets, num_linestring_offsets) \
CUSPATIAL_HOST_DEVICE_EXPECTS(num_multilinestring_offsets > 0, \
"Multilinestring offsets must contain at least one (1) value"); \
CUSPATIAL_EXPECTS_VALID_LINESTRING_SIZES(num_linestring_points, num_linestring_offsets);

/**
Expand All @@ -84,15 +84,16 @@
* [1]: https://github.com/geoarrow/geoarrow/blob/main/format.md
* [2]: https://arrow.apache.org/docs/format/Columnar.html#variable-size-binary-layout
*/
#define CUSPATIAL_EXPECTS_VALID_POLYGON_SIZES( \
num_poly_points, num_poly_offsets, num_poly_ring_offsets) \
CUSPATIAL_EXPECTS(num_poly_offsets > 0, "Polygon offsets must contain at least one (1) value"); \
CUSPATIAL_EXPECTS(num_poly_ring_offsets > 0, \
"Polygon ring offsets must contain at least one (1) value"); \
CUSPATIAL_EXPECTS(num_poly_ring_offsets >= num_poly_offsets, \
"Each polygon must have at least one (1) ring"); \
CUSPATIAL_EXPECTS(num_poly_points >= 4 * (num_poly_ring_offsets - 1), \
"Each ring must have at least four (4) vertices");
#define CUSPATIAL_EXPECTS_VALID_POLYGON_SIZES( \
num_poly_points, num_poly_offsets, num_poly_ring_offsets) \
CUSPATIAL_HOST_DEVICE_EXPECTS(num_poly_offsets > 0, \
"Polygon offsets must contain at least one (1) value"); \
CUSPATIAL_HOST_DEVICE_EXPECTS(num_poly_ring_offsets > 0, \
"Polygon ring offsets must contain at least one (1) value"); \
CUSPATIAL_HOST_DEVICE_EXPECTS(num_poly_ring_offsets >= num_poly_offsets, \
"Each polygon must have at least one (1) ring"); \
CUSPATIAL_HOST_DEVICE_EXPECTS(num_poly_points >= 4 * (num_poly_ring_offsets - 1), \
"Each ring must have at least four (4) vertices");

/**
* @brief Macro for validating the data array sizes for a multipolygon.
Expand All @@ -116,8 +117,8 @@
* [1]: https://github.com/geoarrow/geoarrow/blob/main/format.md
* [2]: https://arrow.apache.org/docs/format/Columnar.html#variable-size-binary-layout
*/
#define CUSPATIAL_EXPECTS_VALID_MULTIPOLYGON_SIZES( \
num_poly_points, num_multipoly_offsets, num_poly_offsets, num_poly_ring_offsets) \
CUSPATIAL_EXPECTS(num_multipoly_offsets > 0, \
"Multipolygon offsets must contain at least one (1) value"); \
#define CUSPATIAL_EXPECTS_VALID_MULTIPOLYGON_SIZES( \
num_poly_points, num_multipoly_offsets, num_poly_offsets, num_poly_ring_offsets) \
CUSPATIAL_HOST_DEVICE_EXPECTS(num_multipoly_offsets > 0, \
"Multipolygon offsets must contain at least one (1) value"); \
CUSPATIAL_EXPECTS_VALID_POLYGON_SIZES(num_poly_points, num_poly_offsets, num_poly_ring_offsets);
44 changes: 44 additions & 0 deletions cpp/include/cuspatial/distance/polygon_distance.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2023, NVIDIA CORPORATION.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <cuspatial/column/geometry_column_view.hpp>

#include <cudf/column/column.hpp>

#include <rmm/mr/device/device_memory_resource.hpp>

#include <memory>

namespace cuspatial {

/**
* @brief Compute pairwise (multi)polygon-to-(multi)polygon Cartesian distance
*
* Computes the cartesian distance between each pair of the multipolygons.
*
* @param lhs Geometry column of the multipolygons to compute distance from
* @param rhs Geometry column of the multipolygons to compute distance to
* @param mr Device memory resource used to allocate the returned column.
*
* @return Column of distances between each pair of input geometries, same type as input coordinate
* types.
*/
std::unique_ptr<cudf::column> pairwise_polygon_distance(
geometry_column_view const& lhs,
geometry_column_view const& rhs,
rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource());

} // namespace cuspatial
29 changes: 28 additions & 1 deletion cpp/include/cuspatial/error.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020-2022, NVIDIA CORPORATION.
* Copyright (c) 2020-2023, NVIDIA CORPORATION.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,6 +16,8 @@

#pragma once

#include <cuspatial/assert.cuh>

#include <cuda_runtime_api.h>

#include <stdexcept>
Expand Down Expand Up @@ -76,6 +78,31 @@ struct cuda_error : public std::runtime_error {
: throw cuspatial::logic_error("cuSpatial failure at: " __FILE__ \
":" CUSPATIAL_STRINGIFY(__LINE__) ": " reason)

/**---------------------------------------------------------------------------*
* @brief Macro for checking (pre-)conditions that throws an exception when
* a condition is violated.
*
* Example usage:
*
* @code
* CUSPATIAL_HOST_DEVICE_EXPECTS(lhs->dtype == rhs->dtype, "Column type mismatch");
* @endcode
*
* @param[in] cond Expression that evaluates to true or false
* @param[in] reason String literal description of the reason that cond is
* expected to be true
*
* (if on host)
* @throw cuspatial::logic_error if the condition evaluates to false.
* (if on device)
* program terminates and assertion error message is printed to stderr.
*---------------------------------------------------------------------------**/
#ifndef __CUDA_ARCH__
#define CUSPATIAL_HOST_DEVICE_EXPECTS(cond, reason) CUSPATIAL_EXPECTS(cond, reason)
#else
#define CUSPATIAL_HOST_DEVICE_EXPECTS(cond, reason) cuspatial_assert(cond&& reason)
#endif

/**---------------------------------------------------------------------------*
* @brief Indicates that an erroneous code path has been taken.
*
Expand Down
12 changes: 6 additions & 6 deletions cpp/include/cuspatial/range/multilinestring_range.cuh
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,12 @@ class multilinestring_range {
using point_t = iterator_value_type<VecIterator>;
using element_t = iterator_vec_base_type<VecIterator>;

multilinestring_range(GeometryIterator geometry_begin,
GeometryIterator geometry_end,
PartIterator part_begin,
PartIterator part_end,
VecIterator points_begin,
VecIterator points_end);
CUSPATIAL_HOST_DEVICE multilinestring_range(GeometryIterator geometry_begin,
GeometryIterator geometry_end,
PartIterator part_begin,
PartIterator part_end,
VecIterator points_begin,
VecIterator points_end);

/// Return the number of multilinestrings in the array.
CUSPATIAL_HOST_DEVICE auto size() { return num_multilinestrings(); }
Expand Down
113 changes: 113 additions & 0 deletions cpp/src/spatial/polygon_distance.cu
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Copyright (c) 2023, NVIDIA CORPORATION.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "../utility/iterator.hpp"
#include "../utility/multi_geometry_dispatch.hpp"

#include <cudf/column/column.hpp>
#include <cudf/column/column_factories.hpp>
#include <cudf/column/column_view.hpp>
#include <cudf/copying.hpp>
#include <cudf/types.hpp>
#include <cudf/utilities/traits.hpp>
#include <cudf/utilities/type_dispatcher.hpp>

#include <rmm/cuda_stream_view.hpp>

#include <cuspatial/column/geometry_column_view.hpp>
#include <cuspatial/error.hpp>
#include <cuspatial/iterator_factory.cuh>
#include <cuspatial/polygon_distance.cuh>
#include <cuspatial/range/multipolygon_range.cuh>
#include <cuspatial/types.hpp>

#include <thrust/iterator/counting_iterator.h>

#include <memory>
#include <type_traits>

namespace cuspatial {

namespace detail {

namespace {

template <collection_type_id is_multi_polygon_lhs, collection_type_id is_multi_polygon_rhs>
struct pairwise_polygon_distance_impl {
using SizeType = cudf::device_span<cudf::size_type const>::size_type;

template <typename T, CUDF_ENABLE_IF(std::is_floating_point_v<T>)>
std::unique_ptr<cudf::column> operator()(geometry_column_view const& lhs,
geometry_column_view const& rhs,
rmm::cuda_stream_view stream,
rmm::mr::device_memory_resource* mr)
{
auto lhs_range = make_multipolygon_range<is_multi_polygon_lhs, T, cudf::size_type>(lhs);
auto rhs_range = make_multipolygon_range<is_multi_polygon_rhs, T, cudf::size_type>(rhs);

auto output = cudf::make_numeric_column(
lhs.coordinate_type(), lhs.size(), cudf::mask_state::UNALLOCATED, stream, mr);

cuspatial::pairwise_polygon_distance(
lhs_range, rhs_range, output->mutable_view().begin<T>(), stream);
return output;
}

template <typename T, CUDF_ENABLE_IF(!std::is_floating_point_v<T>), typename... Args>
std::unique_ptr<cudf::column> operator()(Args&&...)

{
CUSPATIAL_FAIL("polygon distance API only supports floating point coordinates.");
}
};

} // namespace

template <collection_type_id is_multi_polygon_lhs, collection_type_id is_multi_polygon_rhs>
struct pairwise_polygon_distance {
std::unique_ptr<cudf::column> operator()(geometry_column_view const& lhs,
geometry_column_view const& rhs,
rmm::cuda_stream_view stream,
rmm::mr::device_memory_resource* mr)
{
return cudf::type_dispatcher(
lhs.coordinate_type(),
pairwise_polygon_distance_impl<is_multi_polygon_lhs, is_multi_polygon_rhs>{},
lhs,
rhs,
stream,
mr);
}
};

} // namespace detail

std::unique_ptr<cudf::column> pairwise_polygon_distance(geometry_column_view const& lhs,
geometry_column_view const& rhs,
rmm::mr::device_memory_resource* mr)
{
CUSPATIAL_EXPECTS(lhs.geometry_type() == geometry_type_id::POLYGON &&
rhs.geometry_type() == geometry_type_id::POLYGON,
"Unexpected input geometry types.");

CUSPATIAL_EXPECTS(lhs.coordinate_type() == rhs.coordinate_type(),
"Input geometries must have the same coordinate data types.");

return multi_geometry_double_dispatch<detail::pairwise_polygon_distance>(
lhs.collection_type(), rhs.collection_type(), lhs, rhs, rmm::cuda_stream_default, mr);
}

} // namespace cuspatial
9 changes: 6 additions & 3 deletions cpp/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,15 @@ ConfigureTest(POINT_LINESTRING_DISTANCE_TEST
ConfigureTest(LINESTRING_DISTANCE_TEST
spatial/distance/linestring_distance_test.cpp)

ConfigureTest(LINESTRING_POLYGON_DISTANCE_TEST
spatial/linestring_polygon_distance_test.cpp)

ConfigureTest(POINT_POLYGON_DISTANCE_TEST
spatial/distance/point_polygon_distance_test.cpp)

ConfigureTest(LINESTRING_POLYGON_DISTANCE_TEST
spatial/distance/linestring_polygon_distance_test.cpp)

ConfigureTest(POLYGON_DISTANCE_TEST
spatial/distance/polygon_distance_test.cpp)

# equality
ConfigureTest(PAIRWISE_MULTIPOINT_EQUALS_COUNT_TEST
spatial/equality/pairwise_multipoint_equals_count_test.cpp)
Expand Down
Loading

0 comments on commit 2d02073

Please sign in to comment.