Skip to content

Commit

Permalink
Add DBSCAN dense box algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
aprokop committed Apr 28, 2021
1 parent 60a938d commit 5bdf418
Show file tree
Hide file tree
Showing 3 changed files with 688 additions and 101 deletions.
203 changes: 162 additions & 41 deletions src/ArborX_DBSCAN.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@

#include <ArborX_AccessTraits.hpp>
#include <ArborX_DetailsFDBSCAN.hpp>
#include <ArborX_DetailsFDBSCANDenseBox.hpp>
#include <ArborX_DetailsSortUtils.hpp>
#include <ArborX_DetailsUtils.hpp>
#include <ArborX_LinearBVH.hpp>

#include <map>
Expand Down Expand Up @@ -76,16 +76,28 @@ struct DBSCANCorePoints
}
};

enum class Implementation
{
FDBSCAN,
FDBSCAN_DenseBox
};

struct Parameters
{
// Print timers to standard output
bool _print_timers = false;
Implementation _implementation = Implementation::FDBSCAN;

Parameters &setPrintTimers(bool print_timers)
{
_print_timers = print_timers;
return *this;
}
Parameters &setImplementation(Implementation impl)
{
_implementation = impl;
return *this;
}
};

} // namespace DBSCAN
Expand Down Expand Up @@ -126,59 +138,166 @@ dbscan(ExecutionSpace const &exec_space, Primitives const &primitives,
return timer.seconds();
};

auto const predicates = buildPredicates(primitives, eps);

auto const n = Access::size(primitives);

// Build the tree
timer_start(timer);
Kokkos::Profiling::pushRegion("ArborX::dbscan::tree_construction");
ArborX::BVH<MemorySpace> bvh(exec_space, primitives);
Kokkos::Profiling::popRegion();
elapsed["construction"] = timer_seconds(timer);

timer_start(timer);
Kokkos::Profiling::pushRegion("ArborX::dbscan::clusters");

Kokkos::View<int *, MemorySpace> labels(
Kokkos::ViewAllocateWithoutInitializing("ArborX::DBSCAN::labels"), n);
ArborX::iota(exec_space, labels);
Details::UnionFind<MemorySpace> union_find{labels};
if (is_special_case)

if (parameters._implementation == DBSCAN::Implementation::FDBSCAN)
{
// Perform the queries and build clusters through callback
using CorePoints = DBSCAN::CCSCorePoints;
CorePoints core_points;
Kokkos::Profiling::pushRegion("ArborX::dbscan::clusters::query");
bvh.query(exec_space, predicates,
Details::FDBSCANCallback<MemorySpace, CorePoints>{union_find,
core_points});
auto const predicates = buildPredicates(primitives, eps);

// Build the tree
timer_start(timer);
Kokkos::Profiling::pushRegion("ArborX::dbscan::tree_construction");
ArborX::BVH<MemorySpace> bvh(exec_space, primitives);
Kokkos::Profiling::popRegion();
elapsed["construction"] = timer_seconds(timer);

timer_start(timer);
Kokkos::Profiling::pushRegion("ArborX::dbscan::clusters");
if (is_special_case)
{
// Perform the queries and build clusters through callback
using CorePoints = DBSCAN::CCSCorePoints;
CorePoints core_points;
Kokkos::Profiling::pushRegion("ArborX::dbscan::clusters::query");
bvh.query(exec_space, predicates,
Details::FDBSCANCallback<MemorySpace, CorePoints>{union_find,
core_points});
Kokkos::Profiling::popRegion();
}
else
{
// Determine core points
Kokkos::Timer timer_local;
timer_start(timer_local);
Kokkos::Profiling::pushRegion("ArborX::dbscan::clusters::num_neigh");
Kokkos::View<int *, MemorySpace> num_neigh(
"ArborX::dbscan::num_neighbors", n);
bvh.query(exec_space, predicates,
Details::FDBSCANNumNeighEarlyExitCallback<MemorySpace>{
num_neigh, core_min_size});
Kokkos::Profiling::popRegion();
elapsed["neigh"] = timer_seconds(timer_local);

using CorePoints = DBSCAN::DBSCANCorePoints<MemorySpace>;

// Perform the queries and build clusters through callback
timer_start(timer_local);
Kokkos::Profiling::pushRegion("ArborX::dbscan::clusters:query");
bvh.query(exec_space, predicates,
Details::FDBSCANCallback<MemorySpace, CorePoints>{
union_find, CorePoints{num_neigh, core_min_size}});
Kokkos::Profiling::popRegion();
elapsed["query"] = timer_seconds(timer_local);
}
}
else
else if (parameters._implementation ==
DBSCAN::Implementation::FDBSCAN_DenseBox)
{
// Determine core points
Kokkos::Timer timer_local;
timer_start(timer_local);
Kokkos::Profiling::pushRegion("ArborX::dbscan::clusters::num_neigh");
Kokkos::View<int *, MemorySpace> num_neigh("ArborX::dbscan::num_neighbors",
n);
bvh.query(exec_space, predicates,
Details::FDBSCANNumNeighEarlyExitCallback<MemorySpace>{
num_neigh, core_min_size});
Kokkos::Profiling::popRegion();
elapsed["neigh"] = timer_seconds(timer_local);
auto const predicates = buildPredicates(primitives, eps);

// Find dense boxes
timer_start(timer);
Kokkos::Profiling::pushRegion("ArborX::dbscan::dense_cells");
Box bounds;
Details::TreeConstruction::calculateBoundingBoxOfTheScene(
exec_space, primitives, bounds);
float const h = eps / std::sqrt(3); // 3D (for 2D change to std::sqrt(2))
auto const &min_corner = bounds.minCorner();
auto const &max_corner = bounds.maxCorner();
size_t nx = std::ceil((max_corner[0] - min_corner[0]) / h);
size_t ny = std::ceil((max_corner[1] - min_corner[1]) / h);
size_t nz = std::ceil((max_corner[2] - min_corner[2]) / h);
if (verbose)
printf("h = %e, nx = %zu, ny = %zu, nz = %zu\n", h, nx, ny, nz);

auto cell_indices = Details::computeCellIndices(exec_space, primitives, h,
nx, ny, nz, bounds);
auto permute = Details::sortObjects(exec_space, cell_indices);

auto mixed_offsets = Details::computeMixedOffsets(exec_space, core_min_size,
cell_indices, verbose);

Details::unionFindWithinEachDenseCell(
exec_space, core_min_size, mixed_offsets, union_find, permute, verbose);

auto box_primitives = Details::buildBoxPrimitives(
exec_space, primitives, core_min_size, h, nx, ny, nz, bounds,
mixed_offsets, cell_indices, permute);

Kokkos::View<int *, MemorySpace> point2offset(
Kokkos::ViewAllocateWithoutInitializing("ArborX::DBSCAN::point2offset"),
n);
Kokkos::parallel_for("ArborX::DBSCAN::compute_point2offset",
Kokkos::RangePolicy<ExecutionSpace>(
exec_space, 0, mixed_offsets.size() - 1),
KOKKOS_LAMBDA(int i) {
for (int j = mixed_offsets(i);
j < mixed_offsets(i + 1); ++j)
point2offset(permute(j)) = i;
});

using CorePoints = DBSCAN::DBSCANCorePoints<MemorySpace>;
Kokkos::Profiling::popRegion();
elapsed["dense_cells"] = timer_seconds(timer);

// Perform the queries and build clusters through callback
timer_start(timer_local);
Kokkos::Profiling::pushRegion("ArborX::dbscan::clusters:query");
bvh.query(exec_space, predicates,
Details::FDBSCANCallback<MemorySpace, CorePoints>{
union_find, CorePoints{num_neigh, core_min_size}});
// Build the tree
timer_start(timer);
Kokkos::Profiling::pushRegion("ArborX::dbscan::tree_construction");
BVH<MemorySpace> bvh(exec_space, box_primitives);
Kokkos::Profiling::popRegion();
elapsed["query"] = timer_seconds(timer_local);
elapsed["construction"] = timer_seconds(timer);

timer_start(timer);
Kokkos::Profiling::pushRegion("ArborX::dbscan::clusters");

if (is_special_case)
{
// Perform the queries and build clusters through callback
using CorePoints = DBSCAN::CCSCorePoints;
Kokkos::Profiling::pushRegion("ArborX::dbscan::clusters::query");
bvh.query(exec_space, predicates,
Details::FDBSCANDenseBoxCallback<
MemorySpace, decltype(union_find), CorePoints, Primitives,
decltype(mixed_offsets), decltype(point2offset),
decltype(permute)>{union_find, CorePoints{}, primitives,
mixed_offsets, point2offset, permute,
core_min_size, eps});
Kokkos::Profiling::popRegion();
}
else
{
// Determine core points
Kokkos::Timer timer_local;
timer_start(timer_local);
Kokkos::Profiling::pushRegion("ArborX::dbscan::clusters::num_neigh");
Kokkos::View<int *, MemorySpace> num_neigh(
"ArborX::dbscan::num_neighbors", n);
Details::computeNumNeighbors(exec_space, primitives, core_min_size, eps,
mixed_offsets, permute, num_neigh, bvh,
verbose);
Kokkos::Profiling::popRegion();
elapsed["neigh"] = timer_seconds(timer_local);

using CorePoints = DBSCAN::DBSCANCorePoints<MemorySpace>;

// Perform the queries and build clusters through callback
timer_start(timer_local);
Kokkos::Profiling::pushRegion("ArborX::dbscan::clusters:query");
bvh.query(exec_space, predicates,
Details::FDBSCANDenseBoxCallback<
MemorySpace, decltype(union_find), CorePoints, Primitives,
decltype(mixed_offsets), decltype(point2offset),
decltype(permute)>{union_find,
CorePoints{num_neigh, core_min_size},
primitives, mixed_offsets, point2offset,
permute, core_min_size, eps});
Kokkos::Profiling::popRegion();
elapsed["query"] = timer_seconds(timer_local);
}
}

// Per [1]:
Expand Down Expand Up @@ -216,6 +335,8 @@ dbscan(ExecutionSpace const &exec_space, Primitives const &primitives,

if (verbose)
{
if (parameters._implementation == DBSCAN::Implementation::FDBSCAN_DenseBox)
printf("-- dense cells : %10.3f\n", elapsed["dense_cells"]);
printf("-- construction : %10.3f\n", elapsed["construction"]);
printf("-- query+cluster : %10.3f\n", elapsed["query+cluster"]);
if (!is_special_case)
Expand Down
Loading

0 comments on commit 5bdf418

Please sign in to comment.