Skip to content

Commit

Permalink
Merge pull request #68 from liaopeiyuan/cpu_radius
Browse files Browse the repository at this point in the history
C++ CPU for radius and knn
  • Loading branch information
rusty1s authored Jun 22, 2020
2 parents 32fa325 + 29f9716 commit b75a19e
Show file tree
Hide file tree
Showing 22 changed files with 2,863 additions and 201 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ install:
- conda install pytorch=${TORCH_VERSION} ${TOOLKIT} -c pytorch --yes
- source script/torch.sh
- pip install flake8 codecov
- python setup.py install
- pip install scipy==1.4.1
- pip install .[test]
script:
- flake8 .
- python setup.py test
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.0)
project(torchcluster)
set(CMAKE_CXX_STANDARD 14)
set(TORCHCLUSTER_VERSION 1.5.4)
set(TORCHCLUSTER_VERSION 1.5.5)

option(WITH_CUDA "Enable CUDA support" OFF)

Expand Down
54 changes: 54 additions & 0 deletions csrc/cpu/knn_cpu.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#include "knn_cpu.h"

#include "utils.h"
#include "utils/neighbors.cpp"

torch::Tensor knn_cpu(torch::Tensor x, torch::Tensor y,
torch::optional<torch::Tensor> ptr_x,
torch::optional<torch::Tensor> ptr_y, int64_t k,
int64_t num_workers) {

CHECK_CPU(x);
CHECK_INPUT(x.dim() == 2);
CHECK_CPU(y);
CHECK_INPUT(y.dim() == 2);

if (ptr_x.has_value()) {
CHECK_CPU(ptr_x.value());
CHECK_INPUT(ptr_x.value().dim() == 1);
}
if (ptr_y.has_value()) {
CHECK_CPU(ptr_y.value());
CHECK_INPUT(ptr_y.value().dim() == 1);
}

std::vector<size_t> *out_vec = new std::vector<size_t>();

AT_DISPATCH_ALL_TYPES(x.scalar_type(), "radius_cpu", [&] {
auto x_data = x.data_ptr<scalar_t>();
auto y_data = y.data_ptr<scalar_t>();
auto x_vec = std::vector<scalar_t>(x_data, x_data + x.numel());
auto y_vec = std::vector<scalar_t>(y_data, y_data + y.numel());

if (!ptr_x.has_value()) {
nanoflann_neighbors<scalar_t>(y_vec, x_vec, out_vec, 0, x.size(-1), 0,
num_workers, k, 0);
} else {
auto sx = (ptr_x.value().narrow(0, 1, ptr_x.value().numel() - 1) -
ptr_x.value().narrow(0, 0, ptr_x.value().numel() - 1));
auto sy = (ptr_y.value().narrow(0, 1, ptr_y.value().numel() - 1) -
ptr_y.value().narrow(0, 0, ptr_y.value().numel() - 1));
auto sx_data = sx.data_ptr<int64_t>();
auto sy_data = sy.data_ptr<int64_t>();
auto sx_vec = std::vector<long>(sx_data, sx_data + sx.numel());
auto sy_vec = std::vector<long>(sy_data, sy_data + sy.numel());
batch_nanoflann_neighbors<scalar_t>(y_vec, x_vec, sy_vec, sx_vec, out_vec,
k, x.size(-1), 0, k, 0);
}
});

const int64_t size = out_vec->size() / 2;
auto out = torch::from_blob(out_vec->data(), {size, 2},
x.options().dtype(torch::kLong));
return out.t().index_select(0, torch::tensor({1, 0}));
}
8 changes: 8 additions & 0 deletions csrc/cpu/knn_cpu.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#pragma once

#include <torch/extension.h>

torch::Tensor knn_cpu(torch::Tensor x, torch::Tensor y,
torch::optional<torch::Tensor> ptr_x,
torch::optional<torch::Tensor> ptr_y, int64_t k,
int64_t num_workers);
55 changes: 55 additions & 0 deletions csrc/cpu/radius_cpu.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#include "radius_cpu.h"

#include "utils.h"
#include "utils/neighbors.cpp"

torch::Tensor radius_cpu(torch::Tensor x, torch::Tensor y,
torch::optional<torch::Tensor> ptr_x,
torch::optional<torch::Tensor> ptr_y, double r,
int64_t max_num_neighbors, int64_t num_workers) {

CHECK_CPU(x);
CHECK_INPUT(x.dim() == 2);
CHECK_CPU(y);
CHECK_INPUT(y.dim() == 2);

if (ptr_x.has_value()) {
CHECK_CPU(ptr_x.value());
CHECK_INPUT(ptr_x.value().dim() == 1);
}
if (ptr_y.has_value()) {
CHECK_CPU(ptr_y.value());
CHECK_INPUT(ptr_y.value().dim() == 1);
}

std::vector<size_t> *out_vec = new std::vector<size_t>();

AT_DISPATCH_ALL_TYPES(x.scalar_type(), "radius_cpu", [&] {
auto x_data = x.data_ptr<scalar_t>();
auto y_data = y.data_ptr<scalar_t>();
auto x_vec = std::vector<scalar_t>(x_data, x_data + x.numel());
auto y_vec = std::vector<scalar_t>(y_data, y_data + y.numel());

if (!ptr_x.has_value()) {
nanoflann_neighbors<scalar_t>(y_vec, x_vec, out_vec, r, x.size(-1),
max_num_neighbors, num_workers, 0, 1);
} else {
auto sx = (ptr_x.value().narrow(0, 1, ptr_x.value().numel() - 1) -
ptr_x.value().narrow(0, 0, ptr_x.value().numel() - 1));
auto sy = (ptr_y.value().narrow(0, 1, ptr_y.value().numel() - 1) -
ptr_y.value().narrow(0, 0, ptr_y.value().numel() - 1));
auto sx_data = sx.data_ptr<int64_t>();
auto sy_data = sy.data_ptr<int64_t>();
auto sx_vec = std::vector<long>(sx_data, sx_data + sx.numel());
auto sy_vec = std::vector<long>(sy_data, sy_data + sy.numel());
batch_nanoflann_neighbors<scalar_t>(y_vec, x_vec, sy_vec, sx_vec, out_vec,
r, x.size(-1), max_num_neighbors, 0,
1);
}
});

const int64_t size = out_vec->size() / 2;
auto out = torch::from_blob(out_vec->data(), {size, 2},
x.options().dtype(torch::kLong));
return out.t().index_select(0, torch::tensor({1, 0}));
}
8 changes: 8 additions & 0 deletions csrc/cpu/radius_cpu.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#pragma once

#include <torch/extension.h>

torch::Tensor radius_cpu(torch::Tensor x, torch::Tensor y,
torch::optional<torch::Tensor> ptr_x,
torch::optional<torch::Tensor> ptr_y, double r,
int64_t max_num_neighbors, int64_t num_workers);
64 changes: 64 additions & 0 deletions csrc/cpu/utils/cloud.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#pragma once

#include <ATen/ATen.h>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <map>
#include <numeric>
#include <unordered_map>
#include <vector>

#include <time.h>

template <typename scalar_t> struct PointCloud {
std::vector<std::vector<scalar_t> *> pts;

void set(std::vector<scalar_t> new_pts, int dim) {

std::vector<std::vector<scalar_t> *> temp(new_pts.size() / dim);
for (size_t i = 0; i < new_pts.size(); i++) {
if (i % dim == 0) {
std::vector<scalar_t> *point = new std::vector<scalar_t>(dim);

for (size_t j = 0; j < (size_t)dim; j++) {
(*point)[j] = new_pts[i + j];
}
temp[i / dim] = point;
}
}

pts = temp;
}
void set_batch(std::vector<scalar_t> new_pts, size_t begin, long size,
int dim) {
std::vector<std::vector<scalar_t> *> temp(size);
for (size_t i = 0; i < (size_t)size; i++) {
std::vector<scalar_t> *point = new std::vector<scalar_t>(dim);
for (size_t j = 0; j < (size_t)dim; j++) {
(*point)[j] = new_pts[dim * (begin + i) + j];
}

temp[i] = point;
}
pts = temp;
}

// Must return the number of data points.
inline size_t kdtree_get_point_count() const { return pts.size(); }

// Returns the dim'th component of the idx'th point in the class:
inline scalar_t kdtree_get_pt(const size_t idx, const size_t dim) const {
return (*pts[idx])[dim];
}

// Optional bounding-box computation: return false to default to a standard
// bbox computation loop.
// Return true if the BBOX was already computed by the class and returned in
// "bb" so it can be avoided to redo it again. Look at bb.size() to find out
// the expected dimensionality (e.g. 2 or 3 for point clouds)
template <class BBOX> bool kdtree_get_bbox(BBOX & /* bb */) const {
return false;
}
};
Loading

0 comments on commit b75a19e

Please sign in to comment.