Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Header Only API pairwise_linestring_intersection #852

Merged
Merged
Show file tree
Hide file tree
Changes from 58 commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
12ce052
Initial port from feature/intersection_dev
isVoid Nov 22, 2022
7310405
More porting from range/ref objects and passing compilation
isVoid Nov 22, 2022
727a51f
header cleanup
isVoid Nov 22, 2022
4b2edee
style
isVoid Nov 22, 2022
01bc3a0
initial porting
isVoid Nov 23, 2022
6a7c0d2
rename with find
isVoid Nov 23, 2022
dfa1ce9
initial port from dev branch
isVoid Nov 23, 2022
bb5e0d4
Merge branch 'branch-23.02' of https://github.com/rapidsai/cuspatial …
isVoid Nov 27, 2022
99ed507
Merge branch 'branch-23.02' of https://github.com/rapidsai/cuspatial …
isVoid Nov 28, 2022
4d6617b
Merge branch 'branch-23.02' of https://github.com/rapidsai/cuspatial …
isVoid Nov 28, 2022
50e56ee
add docs for range
isVoid Nov 29, 2022
41da8e0
add docs for properties and methods
isVoid Nov 29, 2022
c6dc62c
update with constant integer range usage
isVoid Nov 29, 2022
8666629
use array access operator instead of pointer arithmetic
isVoid Nov 29, 2022
3cdf65c
Revert "update with constant integer range usage"
isVoid Nov 29, 2022
3d3b312
use unique_ptr for all non-temporary, owning objects
isVoid Dec 2, 2022
8534f17
style
isVoid Dec 2, 2022
7aa0aa0
add error handling
isVoid Dec 2, 2022
eaf22b4
add error handling
isVoid Dec 2, 2022
2482d25
Merge branch 'branch-23.02' of https://github.com/rapidsai/cuspatial …
isVoid Dec 5, 2022
f1731fe
address reviews
isVoid Dec 5, 2022
403b890
add more basic tests
isVoid Dec 5, 2022
279a50b
Merge branch 'branch-23.02' of https://github.com/rapidsai/cuspatial …
isVoid Dec 6, 2022
25e3496
apply review comments
isVoid Dec 7, 2022
500de19
address reviews
isVoid Dec 7, 2022
736c3f1
Merge branch 'branch-23.02' of https://github.com/rapidsai/cuspatial …
isVoid Dec 8, 2022
3450882
use segment methods
isVoid Dec 8, 2022
c9a7e89
zero initialize array in kernel
isVoid Dec 8, 2022
83f9b1a
simplify tests
isVoid Dec 8, 2022
2170e84
undef test macro
isVoid Dec 8, 2022
16c6a42
Use new macro
isVoid Dec 8, 2022
bfdd6bb
remove duplicate definition of segment arrays
isVoid Dec 8, 2022
877a12f
adding new bottom-up traverse helper; use ephemeral atomic_ref object
isVoid Dec 8, 2022
44b68de
add enumerate_range
isVoid Dec 9, 2022
975bda9
more adding to the enumerate range
isVoid Dec 9, 2022
c58cd33
a new zero data helper
isVoid Dec 9, 2022
bb1d792
Update cpp/include/doxygen_groups.h
isVoid Dec 9, 2022
9a82805
refactor tests to remove duplicates
isVoid Dec 9, 2022
8aef773
initial port from dev branch
isVoid Dec 10, 2022
a5ab622
replace wording: stencil with flag
isVoid Dec 10, 2022
40ee156
compile intersection tests
isVoid Dec 10, 2022
2bb7d7b
Merge branch 'feature/merge_segment_pr' into feature/header_only_inte…
isVoid Dec 10, 2022
6bbf858
Merge branch 'feature/merge_point_on_segment_pr' into feature/header_…
isVoid Dec 10, 2022
8ee7d5a
refactor tests to make simpler
isVoid Dec 10, 2022
5e490ba
revert comments https://github.com/rapidsai/cuspatial/pull/819#discus…
isVoid Dec 12, 2022
ba55a15
Merge branch 'feature/merge_point_on_segment_pr' into feature/header_…
isVoid Dec 12, 2022
ed85fac
fix bug in count
isVoid Dec 12, 2022
8f800a8
Merge branch 'branch-23.02' of https://github.com/rapidsai/cuspatial …
isVoid Dec 13, 2022
5c0ecc2
Revert "fix bug in count"
isVoid Dec 13, 2022
42c9b6f
Merge branch 'branch-23.02' of https://github.com/rapidsai/cuspatial …
isVoid Dec 13, 2022
8a9e70e
Update docstrings
isVoid Dec 14, 2022
3e4bcc1
Update cpp/tests/experimental/spatial/linestring_intersection_interme…
isVoid Dec 14, 2022
0c239dd
Merge branch 'feature/header_only_intersection/pr' of github.com:isVo…
isVoid Dec 14, 2022
eb4f767
Merge branch 'branch-23.02' of https://github.com/rapidsai/cuspatial …
isVoid Dec 14, 2022
c1efe0e
Merge branch 'branch-23.02' of https://github.com/rapidsai/cuspatial …
isVoid Dec 14, 2022
7613da1
remove bad merge function
isVoid Dec 15, 2022
fec6bef
remove bad merge function
isVoid Dec 15, 2022
3b11670
Add sorting function to intersection results
isVoid Dec 15, 2022
44faadf
rename find_point->find_points
isVoid Dec 15, 2022
30cfe9b
add more combinations to test and refactors
isVoid Dec 15, 2022
4472a7a
Merge branch 'branch-23.02' of https://github.com/rapidsai/cuspatial …
isVoid Dec 15, 2022
45a1f0f
fix broken builds
isVoid Dec 15, 2022
0de15d3
fix typo in intersection_util
isVoid Dec 16, 2022
9d14f27
remove stale include
isVoid Dec 16, 2022
3404d55
some print utility updates
isVoid Dec 16, 2022
940674c
remove synchronize
isVoid Dec 16, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,306 @@
/*
* 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 <cuspatial/cuda_utils.hpp>
#include <cuspatial/detail/utility/device_atomics.cuh>
#include <cuspatial/detail/utility/linestring.cuh>
#include <cuspatial/error.hpp>
#include <cuspatial/experimental/detail/find/find_and_combine_segment.cuh>
#include <cuspatial/experimental/detail/find/find_duplicate_points.cuh>
#include <cuspatial/experimental/detail/find/find_point_on_segments.cuh>
#include <cuspatial/experimental/detail/linestring_intersection_count.cuh>
#include <cuspatial/experimental/detail/linestring_intersection_with_duplicates.cuh>
#include <cuspatial/experimental/ranges/multipoint_range.cuh>
#include <cuspatial/experimental/ranges/range.cuh>
#include <cuspatial/traits.hpp>

#include <rmm/cuda_stream_view.hpp>
#include <rmm/device_uvector.hpp>
#include <rmm/exec_policy.hpp>
#include <rmm/mr/device/device_memory_resource.hpp>
#include <rmm/mr/device/per_device_resource.hpp>

#include <thrust/binary_search.h>
#include <thrust/iterator/discard_iterator.h>
#include <thrust/iterator/zip_iterator.h>
#include <thrust/reduce.h>
#include <thrust/remove.h>
#include <thrust/scan.h>
#include <thrust/sequence.h>
#include <thrust/sort.h>
#include <thrust/tabulate.h>
#include <thrust/tuple.h>
#include <thrust/uninitialized_fill.h>
#include <thrust/unique.h>

#include <cuda/atomic>

#include <cstdint>
#include <memory>

namespace cuspatial {
template <typename T, typename OffsetType>
struct linestring_intersection_result;

namespace detail {

/**
* @brief Functor to gather the look-back ids from the ids in intermediates.
*
* In the final step, intersection function is required to compute a single look-back id
* column for the union column rows. This functor looks at each row of the union
* column, determine its geometry type, and gather the look-back id from the
* corresponding id range.
*/
template <typename OffsetRange, typename TypeRange>
struct gather_ids_functor {
id_ranges<OffsetRange> points;
id_ranges<OffsetRange> segments;

TypeRange types_buffer;
OffsetRange offset_buffer;

gather_ids_functor(id_ranges<OffsetRange> points,
id_ranges<OffsetRange> segments,
TypeRange types_buffer,
OffsetRange offset_buffer)
: points(points), segments(segments), types_buffer(types_buffer), offset_buffer(offset_buffer)
{
}

template <typename IndexType>
auto __device__ operator()(IndexType i)
{
if (types_buffer[i] == IntersectionTypeCode::POINT) {
return points[offset_buffer[i]];
} else {
return segments[offset_buffer[i]];
}
}
};

/**
* @brief Functor to compute types buffer in the final union column result.
*/
template <typename type_t, typename OffsetRangeA, typename OffsetRangeB>
struct types_buffer_functor {
OffsetRangeA geometric_column_offset;
OffsetRangeB points_offset;
OffsetRangeB segments_offset;

types_buffer_functor(OffsetRangeA geometric_column_offset,
OffsetRangeB points_offset,
OffsetRangeB segments_offset)
: geometric_column_offset(geometric_column_offset),
points_offset(points_offset),
segments_offset(segments_offset)
{
}

template <typename index_t>
type_t __device__ operator()(index_t i)
{
auto geometry_idx = thrust::distance(
geometric_column_offset.begin(),
thrust::prev(thrust::upper_bound(
thrust::seq, geometric_column_offset.begin(), geometric_column_offset.end(), i)));

auto num_points = points_offset[geometry_idx + 1] - points_offset[geometry_idx];
auto num_segments = segments_offset[geometry_idx + 1] - segments_offset[geometry_idx];
auto pair_offset = geometric_column_offset[geometry_idx];

// In each pair, points always precedes segment (arbitrarily).
if (pair_offset <= i && i < pair_offset + num_points)
return IntersectionTypeCode::POINT;
else
return IntersectionTypeCode::LINESTRING;
}
};

/**
* @brief Compute types buffer in the final union column result.
*/
template <typename types_t, typename index_t, typename OffsetRangeA, typename OffsetRangeB>
std::unique_ptr<rmm::device_uvector<types_t>> compute_types_buffer(
index_t union_column_size,
OffsetRangeA geometric_column_offset,
OffsetRangeB points_offset,
OffsetRangeB segments_offset,
rmm::cuda_stream_view stream,
rmm::mr::device_memory_resource* mr)
{
auto types_buffer = std::make_unique<rmm::device_uvector<types_t>>(union_column_size, stream, mr);
thrust::tabulate(rmm::exec_policy(stream),
types_buffer->begin(),
types_buffer->end(),
types_buffer_functor<types_t, OffsetRangeA, OffsetRangeB>(
geometric_column_offset, points_offset, segments_offset));
return types_buffer;
}

/**
* @brief Compute union column's offset buffer
*
* This is performing a group-by cummulative sum (pandas semantic) operation
* to an "all 1s vector", using `types_buffer` as the key column.
*/
template <typename index_t>
std::unique_ptr<rmm::device_uvector<index_t>> compute_offset_buffer(
rmm::device_uvector<uint8_t> const& types_buffer,
rmm::mr::device_memory_resource* mr,
rmm::cuda_stream_view stream)
{
auto N = types_buffer.size();
auto keys_copy = rmm::device_uvector(types_buffer, stream);
auto indices_temp = rmm::device_uvector<index_t>(N, stream);
thrust::sequence(rmm::exec_policy(stream), indices_temp.begin(), indices_temp.end());
thrust::stable_sort_by_key(
rmm::exec_policy(stream), keys_copy.begin(), keys_copy.end(), indices_temp.begin());

auto offset_buffer = std::make_unique<rmm::device_uvector<index_t>>(N, stream, mr);
thrust::uninitialized_fill_n(rmm::exec_policy(stream), offset_buffer->begin(), N, 1);
thrust::exclusive_scan_by_key(rmm::exec_policy(stream),
keys_copy.begin(),
keys_copy.end(),
offset_buffer->begin(),
offset_buffer->begin());
thrust::scatter(rmm::exec_policy(stream),
offset_buffer->begin(),
offset_buffer->end(),
indices_temp.begin(),
offset_buffer->begin());
return offset_buffer;
}

} // namespace detail

/**
* @brief Compute intersections between multilnestrings with duplicates.
*/
template <typename T,
typename index_t,
typename MultiLinestringRange1,
typename MultiLinestringRange2>
linestring_intersection_result<T, index_t> pairwise_linestring_intersection(
MultiLinestringRange1 multilinestrings1,
MultiLinestringRange2 multilinestrings2,
rmm::mr::device_memory_resource* mr,
rmm::cuda_stream_view stream)
{
using types_t = typename linestring_intersection_result<T, index_t>::types_t;

static_assert(is_same_floating_point<T, typename MultiLinestringRange2::element_t>(),
"Inputs and output must have the same floating point value type.");

static_assert(is_same<vec_2d<T>,
typename MultiLinestringRange1::point_t,
typename MultiLinestringRange2::point_t>(),
"All input types must be cuspatial::vec_2d with the same value type");

CUSPATIAL_EXPECTS(multilinestrings1.size() == multilinestrings2.size(),
"The size input multilinestrings mismatch.");

auto const num_pairs = multilinestrings1.size();

// Phase 1 and 2: Estimate and compute duplicates
auto [points, segments] = detail::pairwise_linestring_intersection_with_duplicates<index_t, T>(
multilinestrings1, multilinestrings2, mr, stream);
auto num_points = points.num_geoms();
auto num_segments = segments.num_geoms();

// Phase 3: Remove duplicate points from intermediates
// TODO: improve memory usage by using IIFE to
// Remove the duplicate points
rmm::device_uvector<int32_t> point_flags(num_points, stream);
detail::find_duplicate_points(
make_multipoint_range(points.offset_range(), points.geom_range()), point_flags.begin(), stream);

points.remove_if(range(point_flags.begin(), point_flags.end()), stream);
point_flags.resize(points.geoms->size(), stream);

// Merge mergable segments
rmm::device_uvector<uint8_t> segment_flags(num_segments, stream);
detail::find_and_combine_segment(
segments.offset_range(), segments.geom_range(), segment_flags.begin(), stream);
segments.remove_if(range(segment_flags.begin(), segment_flags.end()), stream);

// Merge point on segments
detail::find_point_on_segments(make_multipoint_range(points.offset_range(), points.geom_range()),
segments.offset_range(),
segments.geom_range(),
point_flags.begin(),
stream);

points.remove_if(range(point_flags.begin(), point_flags.end()), stream);

// Phase 4: Assemble results as union column
auto num_union_column_rows = points.geoms->size() + segments.geoms->size();
auto geometry_collection_offsets =
std::make_unique<rmm::device_uvector<index_t>>(num_pairs + 1, stream, mr);

thrust::transform(rmm::exec_policy(stream),
points.offsets->begin(),
points.offsets->end(),
segments.offsets->begin(),
geometry_collection_offsets->begin(),
thrust::plus<index_t>());

auto types_buffer = detail::compute_types_buffer<types_t>(
num_union_column_rows,
range(geometry_collection_offsets->begin(), geometry_collection_offsets->end()),
points.offset_range(),
segments.offset_range(),
stream,
mr);

auto offsets_buffer = detail::compute_offset_buffer<index_t>(*types_buffer, mr, stream);

// Assemble the look-back ids.
auto lhs_linestring_id =
std::make_unique<rmm::device_uvector<index_t>>(num_union_column_rows, stream, mr);
auto lhs_segment_id =
std::make_unique<rmm::device_uvector<index_t>>(num_union_column_rows, stream, mr);
auto rhs_linestring_id =
std::make_unique<rmm::device_uvector<index_t>>(num_union_column_rows, stream, mr);
auto rhs_segment_id =
std::make_unique<rmm::device_uvector<index_t>>(num_union_column_rows, stream, mr);

auto id_iter = thrust::make_zip_iterator(lhs_linestring_id->begin(),
lhs_segment_id->begin(),
rhs_linestring_id->begin(),
rhs_segment_id->begin());
thrust::tabulate(rmm::exec_policy(stream),
id_iter,
id_iter + num_union_column_rows,
gather_ids_functor{points.get_id_ranges(),
segments.get_id_ranges(),
range(types_buffer->begin(), types_buffer->end()),
range(offsets_buffer->begin(), offsets_buffer->end())});

return linestring_intersection_result<T, index_t>{std::move(geometry_collection_offsets),
std::move(types_buffer),
std::move(offsets_buffer),
std::move(points.geoms),
std::move(segments.geoms),
std::move(lhs_linestring_id),
std::move(lhs_segment_id),
std::move(rhs_linestring_id),
std::move(rhs_segment_id)};
}

} // namespace cuspatial
Loading