Skip to content

Commit

Permalink
Merge pull request #13 from nicolas-chaulet/ci
Browse files Browse the repository at this point in the history
Ci
  • Loading branch information
nicolas-chaulet authored Jan 13, 2020
2 parents 9c1355e + 4b8fc03 commit 031a225
Show file tree
Hide file tree
Showing 9 changed files with 138 additions and 88 deletions.
29 changes: 29 additions & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Unittests

on: [push]

jobs:
unittests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Set up Python 3.6
uses: actions/setup-python@v1
with:
python-version: 3.6
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install torch flake8 setuptools
- name: Build package
run: |
python setup.py build_ext --inplace
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with unittest
run: |
python -m unittest -v
15 changes: 15 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
exclude: "build|egg-info|dist"

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v1.2.3
hooks:
- id: trailing-whitespace
- id: check-added-large-files
- id: end-of-file-fixer

- repo: https://github.com/ambv/black
rev: stable
hooks:
- id: black
language_version: python3.6
29 changes: 29 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Example configuration for Black.

# NOTE: you have to use single-quoted strings in TOML for regular expressions.
# It's the equivalent of r-strings in Python. Multiline strings are treated as
# verbose regular expressions by Black. Use [ ] to denote a significant space
# character.

[tool.black]
line-length = 120
target-version = ['py36', 'py37', 'py38']
include = '\.pyi?$'
exclude = '''
/(
\.eggs
| \.git
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| _build
| buck-out
| build
| dist
)/
'''

[build-system]
requires = ["setuptools>=41.0", "setuptools-scm", "wheel"]
build-backend = "setuptools.build_meta"
12 changes: 4 additions & 8 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@
import glob

ext_src_root = "cuda"
ext_sources = glob.glob("{}/src/*.cpp".format(ext_src_root)) + glob.glob(
"{}/src/*.cu".format(ext_src_root)
)
ext_sources = glob.glob("{}/src/*.cpp".format(ext_src_root)) + glob.glob("{}/src/*.cu".format(ext_src_root))

ext_modules = []
if CUDA_HOME:
Expand All @@ -32,17 +30,15 @@
CppExtension(
name="torch_points.points_cpu",
sources=cpu_ext_sources,
extra_compile_args={
"cxx": ["-O2", "-I{}".format("{}/include".format(cpu_ext_src_root))],
},
extra_compile_args={"cxx": ["-O2", "-I{}".format("{}/include".format(cpu_ext_src_root))],},
)
)

requirements = ["torch^1.1.0"]
requirements = ["torch>=1.1.0"]

setup(
name="torch_points",
version="0.1.5",
version="0.1.6",
author="Nicolas Chaulet",
packages=find_packages(),
install_requires=requirements,
Expand Down
11 changes: 11 additions & 0 deletions test/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import torch


def run_if_cuda(func):
def wrapped_func(*args, **kwargs):
if torch.cuda.is_available():
return func(*args, **kwargs)
else:
return

return wrapped_func
58 changes: 23 additions & 35 deletions test/test_ballquerry.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,24 @@
import numpy.testing as npt
import numpy as np

from . import run_if_cuda


class TestBall(unittest.TestCase):
@run_if_cuda
def test_simple_gpu(self):
a = torch.tensor([[[0, 0, 0], [1, 0, 0], [2, 0, 0]]]).to(torch.float).cuda()
b = torch.tensor([[[0, 0, 0]]]).to(torch.float).cuda()

npt.assert_array_equal(
ball_query(1, 2, a, b).detach().cpu().numpy(), np.array([[[0, 0]]])
)
npt.assert_array_equal(ball_query(1, 2, a, b).detach().cpu().numpy(), np.array([[[0, 0]]]))

@run_if_cuda
def test_larger_gpu(self):
a = torch.randn(32, 4096, 3).to(torch.float).cuda()
idx = ball_query(1, 64, a, a).detach().cpu().numpy()
self.assertGreaterEqual(idx.min(), 0)

@run_if_cuda
def test_cpu_gpu_equality(self):
a = torch.randn(5, 1000, 3)
res_cpu = ball_query(0.1, 17, a, a).detach().numpy()
Expand All @@ -30,22 +33,17 @@ def test_cpu_gpu_equality(self):


class TestBallPartial(unittest.TestCase):
@run_if_cuda
def test_simple_gpu(self):
x = (
torch.tensor([[10, 0, 0], [0.1, 0, 0], [10, 0, 0], [0.1, 0, 0]])
.to(torch.float)
.cuda()
)
x = torch.tensor([[10, 0, 0], [0.1, 0, 0], [10, 0, 0], [0.1, 0, 0]]).to(torch.float).cuda()
y = torch.tensor([[0, 0, 0]]).to(torch.float).cuda()
batch_x = torch.from_numpy(np.asarray([0, 0, 1, 1])).long().cuda()
batch_y = torch.from_numpy(np.asarray([0])).long().cuda()

batch_x = torch.from_numpy(np.asarray([0, 0, 1, 1])).long().cuda()
batch_y = torch.from_numpy(np.asarray([0])).long().cuda()

idx, dist2 = ball_query(
1.0, 2, x, y, mode="PARTIAL_DENSE", batch_x=batch_x, batch_y=batch_y
)
idx, dist2 = ball_query(1.0, 2, x, y, mode="PARTIAL_DENSE", batch_x=batch_x, batch_y=batch_y)

idx = idx.detach().cpu().numpy()
dist2 = dist2.detach().cpu().numpy()
Expand All @@ -56,41 +54,31 @@ def test_simple_gpu(self):
npt.assert_array_almost_equal(idx, idx_answer)
npt.assert_array_almost_equal(dist2, dist2_answer)

def test_simple_cpu(self):
x = torch.tensor([[10, 0, 0], [0.1, 0, 0], [10, 0, 0], [0.1, 0, 0]]).to(
torch.float
)
y = torch.tensor([[0, 0, 0]]).to(torch.float)
# def test_simple_cpu(self):
# x = torch.tensor([[10, 0, 0], [0.1, 0, 0], [10, 0, 0], [0.1, 0, 0]]).to(torch.float)
# y = torch.tensor([[0, 0, 0]]).to(torch.float)

batch_x = torch.from_numpy(np.asarray([0, 0, 1, 1])).long()
batch_y = torch.from_numpy(np.asarray([0])).long()
# batch_x = torch.from_numpy(np.asarray([0, 0, 1, 1])).long()
# batch_y = torch.from_numpy(np.asarray([0])).long()

idx, dist2 = ball_query(
1.0, 2, x, y, mode="PARTIAL_DENSE", batch_x=batch_x, batch_y=batch_y
)
# idx, dist2 = ball_query(1.0, 2, x, y, mode="PARTIAL_DENSE", batch_x=batch_x, batch_y=batch_y)

idx = idx.detach().cpu().numpy()
dist2 = dist2.detach().cpu().numpy()
# idx = idx.detach().cpu().numpy()
# dist2 = dist2.detach().cpu().numpy()

idx_answer = np.asarray([[1, 1], [0, 1], [1, 1], [1, 1]])
dist2_answer = np.asarray([[-1, -1], [0.01, -1], [-1, -1], [-1, -1]]).astype(
np.float32
)
# idx_answer = np.asarray([[1, 1], [0, 1], [1, 1], [1, 1]])
# dist2_answer = np.asarray([[-1, -1], [0.01, -1], [-1, -1], [-1, -1]]).astype(np.float32)

npt.assert_array_almost_equal(idx, idx_answer)
npt.assert_array_almost_equal(dist2, dist2_answer)
# npt.assert_array_almost_equal(idx, idx_answer)
# npt.assert_array_almost_equal(dist2, dist2_answer)

def test_random_cpu(self):
a = torch.randn(1000, 3).to(torch.float)
b = torch.randn(1500, 3).to(torch.float)
batch_a = torch.randint(1, (1000,)).sort(0)[0].long()
batch_b = torch.randint(1, (1500,)).sort(0)[0].long()
idx, dist = ball_query(
1.0, 12, a, b, mode="PARTIAL_DENSE", batch_x=batch_a, batch_y=batch_b
)
idx2, dist2 = ball_query(
1.0, 12, b, a, mode="PARTIAL_DENSE", batch_x=batch_b, batch_y=batch_a
)
idx, dist = ball_query(1.0, 12, a, b, mode="PARTIAL_DENSE", batch_x=batch_a, batch_y=batch_b)
idx2, dist2 = ball_query(1.0, 12, b, a, mode="PARTIAL_DENSE", batch_x=batch_b, batch_y=batch_a)


if __name__ == "__main__":
Expand Down
45 changes: 17 additions & 28 deletions test/test_grouping.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,46 +10,35 @@ class TestGroup(unittest.TestCase):
# input: points(b, c, n) idx(b, npoints, nsample)
# output: out(b, c, npoints, nsample)
def test_simple(self):
features = torch.tensor([
[[0, 10, 0], [1, 11, 0], [2, 12, 0]],
features = torch.tensor(
[
[100, 110, 120], # x-coordinates
[101, 111, 121], # y-coordinates
[102, 112, 122], # z-coordinates
[[0, 10, 0], [1, 11, 0], [2, 12, 0]],
[[100, 110, 120], [101, 111, 121], [102, 112, 122],], # x-coordinates # y-coordinates # z-coordinates
]
]).type(torch.float)
idx = torch.tensor([
[[1, 0], [0, 0]],
[[0, 1], [1, 2]]
]).type(torch.int)
).type(torch.float)
idx = torch.tensor([[[1, 0], [0, 0]], [[0, 1], [1, 2]]]).type(torch.int)

expected = np.array([
expected = np.array(
[
[[10, 0], [0, 0]],
[[11, 1], [1, 1]],
[[12, 2], [2, 2]]
],
[ # 2nd batch
[ # x-coordinates
[100, 110], # x-coordinates of samples for point 0
[110, 120], # x-coordinates of samples for point 1
[[[10, 0], [0, 0]], [[11, 1], [1, 1]], [[12, 2], [2, 2]]],
[ # 2nd batch
[ # x-coordinates
[100, 110], # x-coordinates of samples for point 0
[110, 120], # x-coordinates of samples for point 1
],
[[101, 111], [111, 121]], # y-coordinates
[[102, 112], [112, 122]], # z-coordinates
],
[[101, 111], [111, 121]], # y-coordinates
[[102, 112], [112, 122]], # z-coordinates
]
])
)

cpu_output = grouping_operation(features, idx).detach().cpu().numpy()

npt.assert_array_equal(expected, cpu_output)

if torch.cuda.is_available():
npt.assert_array_equal(
grouping_operation(
features.cuda(),
idx.cuda()
).detach().cpu().numpy(), expected)
npt.assert_array_equal(grouping_operation(features.cuda(), idx.cuda()).detach().cpu().numpy(), expected)


if __name__ == '__main__':
if __name__ == "__main__":
unittest.main()
2 changes: 1 addition & 1 deletion torch_points/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"gather_operation",
"grouping_operation",
"three_interpolate",
"three_nn"
"three_nn",
]
25 changes: 9 additions & 16 deletions torch_points/torchpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from torch.autograd import Function
import torch.nn as nn
import sys
from typing import Optional
from typing import Optional, Any, Tuple

import torch_points.points_cpu as tpcpu

Expand Down Expand Up @@ -159,9 +159,7 @@ def backward(ctx, grad_out):
idx, weight, m = ctx.three_interpolate_for_backward

if grad_out.is_cuda:
grad_features = tpcuda.three_interpolate_grad(
grad_out.contiguous(), idx, weight, m
)
grad_features = tpcuda.three_interpolate_grad(grad_out.contiguous(), idx, weight, m)
else:
raise NotImplementedError

Expand Down Expand Up @@ -265,13 +263,9 @@ class BallQueryPartialDense(Function):
def forward(ctx, radius, nsample, x, y, batch_x, batch_y):
# type: (Any, float, int, torch.Tensor, torch.Tensor) -> torch.Tensor
if x.is_cuda:
return tpcuda.ball_query_partial_dense(
x, y, batch_x, batch_y, radius, nsample
)
return tpcuda.ball_query_partial_dense(x, y, batch_x, batch_y, radius, nsample)
else:
ind, dist = tpcpu.batch_ball_query(
x, y, batch_x, batch_y, radius, nsample, mode=0
)
ind, dist = tpcpu.batch_ball_query(x, y, batch_x, batch_y, radius, nsample, mode=0)
return ind, dist

@staticmethod
Expand All @@ -288,19 +282,19 @@ def ball_query(
batch_x: Optional[torch.tensor] = None,
batch_y: Optional[torch.tensor] = None,
) -> torch.Tensor:
"""
"""
Arguments:
radius {float} -- radius of the balls
nsample {int} -- maximum number of features in the balls
x {torch.Tensor} --
x {torch.Tensor} --
(M, 3) [partial_dense] or (B, M, 3) [dense] xyz coordinates of the features
y {torch.Tensor} --
y {torch.Tensor} --
(npoint, 3) [partial_dense] or or (B, npoint, 3) [dense] centers of the ball query
mode {str} -- switch between "dense" or "partial_dense" data layout
Keyword Arguments:
batch_x -- (M, ) [partial_dense] or (B, M, 3) [dense] Contains indexes to indicate within batch it belongs to.
batch_y -- (N, ) Contains indexes to indicate within batch it belongs to
batch_x -- (M, ) [partial_dense] or (B, M, 3) [dense] Contains indexes to indicate within batch it belongs to.
batch_y -- (N, ) Contains indexes to indicate within batch it belongs to
Returns:
Expand All @@ -326,4 +320,3 @@ def ball_query(
return BallQueryDense.apply(radius, nsample, x, y)
else:
raise Exception("unrecognized mode {}".format(mode))

0 comments on commit 031a225

Please sign in to comment.