Skip to content

Commit 83ac174

Browse files
Merge pull request #45 from nicolas-chaulet/bug/ballquery
Fix various annoying bugs
2 parents 7994646 + e7ac33c commit 83ac174

File tree

6 files changed

+72
-24
lines changed

6 files changed

+72
-24
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ repos:
1212
rev: stable
1313
hooks:
1414
- id: black
15-
language_version: python3.6
15+
language_version: python3.7
1616
args: ["--config", ".black.toml"]
1717
- repo: local
1818
hooks:

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
# 0.6.6
2+
## Additions
3+
- Windows support
4+
5+
6+
## Change
7+
- Develop with python 3.7
8+
9+
## Bug fix
10+
- Fixed bug in region growing related to batching
11+
- Ball query for partial dense data on GPU was returning only the first point. Fixed now
12+
13+
114
# 0.6.5
215

316
## Additions

cuda/src/ball_query_gpu.cu

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ __global__ void query_ball_point_kernel_partial_dense(
5959
// taken from
6060
// https://github.com/rusty1s/pytorch_cluster/blob/master/cuda/radius_kernel.cu
6161
const ptrdiff_t batch_idx = blockIdx.x;
62-
const ptrdiff_t idx = threadIdx.x;
6362

6463
const ptrdiff_t start_idx_x = batch_x[batch_idx];
6564
const ptrdiff_t end_idx_x = batch_x[batch_idx + 1];
@@ -68,10 +67,10 @@ __global__ void query_ball_point_kernel_partial_dense(
6867
const ptrdiff_t end_idx_y = batch_y[batch_idx + 1];
6968
float radius2 = radius * radius;
7069

71-
for (ptrdiff_t n_x = start_idx_x + idx; n_x < end_idx_x; n_x += TOTAL_THREADS_SPARSE)
70+
for (ptrdiff_t n_y = start_idx_y + threadIdx.x; n_y < end_idx_y; n_y += blockDim.x)
7271
{
7372
int64_t count = 0;
74-
for (ptrdiff_t n_y = start_idx_y; n_y < end_idx_y; n_y++)
73+
for (ptrdiff_t n_x = start_idx_x; n_x < end_idx_x; n_x++)
7574
{
7675
float dist = 0;
7776
for (ptrdiff_t d = 0; d < 3; d++)

setup.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ def get_ext_modules():
2828
extra_compile_args += ["-DVERSION_GE_1_3"]
2929

3030
ext_src_root = "cuda"
31-
ext_sources = glob.glob("{}/src/*.cpp".format(ext_src_root)) + glob.glob("{}/src/*.cu".format(ext_src_root))
31+
ext_sources = glob.glob("{}/src/*.cpp".format(ext_src_root)) + glob.glob(
32+
"{}/src/*.cu".format(ext_src_root)
33+
)
3234

3335
ext_modules = []
3436
if CUDA_HOME:
@@ -37,7 +39,10 @@ def get_ext_modules():
3739
name="torch_points_kernels.points_cuda",
3840
sources=ext_sources,
3941
include_dirs=["{}/include".format(ext_src_root)],
40-
extra_compile_args={"cxx": extra_compile_args, "nvcc": extra_compile_args,},
42+
extra_compile_args={
43+
"cxx": extra_compile_args,
44+
"nvcc": extra_compile_args,
45+
},
4146
)
4247
)
4348

@@ -67,7 +72,7 @@ def get_cmdclass():
6772
requirements = ["torch>=1.1.0", "numba", "scikit-learn"]
6873

6974
url = "https://github.com/nicolas-chaulet/torch-points-kernels"
70-
__version__ = "0.6.5"
75+
__version__ = "0.6.6"
7176
setup(
7277
name="torch-points-kernels",
7378
version=__version__,
@@ -81,5 +86,8 @@ def get_cmdclass():
8186
cmdclass=get_cmdclass(),
8287
long_description=long_description,
8388
long_description_content_type="text/markdown",
84-
classifiers=["Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License",],
89+
classifiers=[
90+
"Programming Language :: Python :: 3",
91+
"License :: OSI Approved :: MIT License",
92+
],
8593
)

test/test_ballquerry.py

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -61,21 +61,18 @@ def test_cpu_gpu_equality(self):
6161
class TestBallPartial(unittest.TestCase):
6262
@run_if_cuda
6363
def test_simple_gpu(self):
64-
x = torch.tensor([[10, 0, 0], [0.1, 0, 0], [10, 0, 0], [0.1, 0, 0]]).to(torch.float).cuda()
64+
x = torch.tensor([[10, 0, 0], [0.1, 0, 0], [0.2, 0, 0], [0.1, 0, 0]]).to(torch.float).cuda()
6565
y = torch.tensor([[0, 0, 0]]).to(torch.float).cuda()
66-
batch_x = torch.from_numpy(np.asarray([0, 0, 1, 1])).long().cuda()
66+
batch_x = torch.from_numpy(np.asarray([0, 0, 0, 1])).long().cuda()
6767
batch_y = torch.from_numpy(np.asarray([0])).long().cuda()
6868

69-
batch_x = torch.from_numpy(np.asarray([0, 0, 1, 1])).long().cuda()
70-
batch_y = torch.from_numpy(np.asarray([0])).long().cuda()
71-
72-
idx, dist2 = ball_query(1.0, 2, x, y, mode="PARTIAL_DENSE", batch_x=batch_x, batch_y=batch_y)
69+
idx, dist2 = ball_query(0.2, 4, x, y, mode="PARTIAL_DENSE", batch_x=batch_x, batch_y=batch_y)
7370

7471
idx = idx.detach().cpu().numpy()
7572
dist2 = dist2.detach().cpu().numpy()
7673

77-
idx_answer = np.asarray([[1, -1]])
78-
dist2_answer = np.asarray([[0.0100, -1.0000]]).astype(np.float32)
74+
idx_answer = np.asarray([[1, 2, -1, -1]])
75+
dist2_answer = np.asarray([[0.0100, 0.04, -1, -1]]).astype(np.float32)
7976

8077
npt.assert_array_almost_equal(idx, idx_answer)
8178
npt.assert_array_almost_equal(dist2, dist2_answer)
@@ -98,30 +95,29 @@ def test_simple_cpu(self):
9895
npt.assert_array_almost_equal(idx, idx_answer)
9996
npt.assert_array_almost_equal(dist2, dist2_answer)
10097

101-
10298
def test_breaks(self):
10399
x = torch.tensor([[10, 0, 0], [0.1, 0, 0], [10, 0, 0], [10.1, 0, 0]]).to(torch.float)
104100
y = torch.tensor([[0, 0, 0]]).to(torch.float)
105101

106102
batch_x = torch.from_numpy(np.asarray([0, 0, 1, 1])).long()
107103
batch_y = torch.from_numpy(np.asarray([0])).long()
108-
104+
109105
with self.assertRaises(RuntimeError):
110106
idx, dist2 = ball_query(1.0, 2, x, y, mode="PARTIAL_DENSE", batch_x=batch_x, batch_y=batch_y)
111107

112-
def test_random_cpu(self):
108+
def test_random_cpu(self, cuda=False):
113109
a = torch.randn(100, 3).to(torch.float)
114110
b = torch.randn(50, 3).to(torch.float)
115111
batch_a = torch.tensor([0 for i in range(a.shape[0] // 2)] + [1 for i in range(a.shape[0] // 2, a.shape[0])])
116112
batch_b = torch.tensor([0 for i in range(b.shape[0] // 2)] + [1 for i in range(b.shape[0] // 2, b.shape[0])])
117113
R = 1
118114

119-
idx, dist = ball_query(R, 15, a, b, mode="PARTIAL_DENSE", batch_x=batch_a, batch_y=batch_b, sort=True)
120-
idx1, dist = ball_query(R, 15, a, b, mode="PARTIAL_DENSE", batch_x=batch_a, batch_y=batch_b, sort=True)
115+
idx, dist = ball_query(R, 15, a, b, mode="PARTIAL_DENSE", batch_x=batch_a, batch_y=batch_b, sort=True,)
116+
idx1, dist = ball_query(R, 15, a, b, mode="PARTIAL_DENSE", batch_x=batch_a, batch_y=batch_b, sort=True,)
121117
torch.testing.assert_allclose(idx1, idx)
122118
with self.assertRaises(AssertionError):
123-
idx, dist = ball_query(R, 15, a, b, mode="PARTIAL_DENSE", batch_x=batch_a, batch_y=batch_b, sort=False)
124-
idx1, dist = ball_query(R, 15, a, b, mode="PARTIAL_DENSE", batch_x=batch_a, batch_y=batch_b, sort=False)
119+
idx, dist = ball_query(R, 15, a, b, mode="PARTIAL_DENSE", batch_x=batch_a, batch_y=batch_b, sort=False,)
120+
idx1, dist = ball_query(R, 15, a, b, mode="PARTIAL_DENSE", batch_x=batch_a, batch_y=batch_b, sort=False,)
125121
torch.testing.assert_allclose(idx1, idx)
126122

127123
self.assertEqual(idx.shape[0], b.shape[0])
@@ -136,6 +132,28 @@ def test_random_cpu(self):
136132
if p >= 0 and p < len(batch_a):
137133
assert p in idx3_sk[i]
138134

135+
@run_if_cuda
136+
def test_random_gpu(self):
137+
a = torch.randn(100, 3).to(torch.float).cuda()
138+
b = torch.randn(50, 3).to(torch.float).cuda()
139+
batch_a = torch.tensor(
140+
[0 for i in range(a.shape[0] // 2)] + [1 for i in range(a.shape[0] // 2, a.shape[0])]
141+
).cuda()
142+
batch_b = torch.tensor(
143+
[0 for i in range(b.shape[0] // 2)] + [1 for i in range(b.shape[0] // 2, b.shape[0])]
144+
).cuda()
145+
R = 1
146+
147+
idx, dist = ball_query(R, 15, a, b, mode="PARTIAL_DENSE", batch_x=batch_a, batch_y=batch_b, sort=False,)
148+
149+
# Comparison to see if we have the same result
150+
tree = KDTree(a.cpu().detach().numpy())
151+
idx3_sk = tree.query_radius(b.cpu().detach().numpy(), r=R)
152+
i = np.random.randint(len(batch_b))
153+
for p in idx[i].cpu().detach().numpy():
154+
if p >= 0 and p < len(batch_a):
155+
assert p in idx3_sk[i]
156+
139157

140158
if __name__ == "__main__":
141159
unittest.main()

torch_points_kernels/cluster.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,19 @@ def region_grow(
8686
# Build clusters for a given label (ignore other points)
8787
label_mask = labels == l
8888
local_ind = ind[label_mask]
89+
90+
# Remap batch to a continuous sequence
91+
label_batch = batch[label_mask]
92+
unique_in_batch = torch.unique(label_batch)
93+
remaped_batch = torch.empty_like(label_batch)
94+
for new, old in enumerate(unique_in_batch):
95+
mask = label_batch == old
96+
remaped_batch[mask] = new
97+
98+
# Cluster
8999
label_clusters = grow_proximity(
90100
pos[label_mask, :],
91-
batch[label_mask],
101+
remaped_batch,
92102
nsample=nsample,
93103
radius=radius,
94104
min_cluster_size=min_cluster_size,

0 commit comments

Comments
 (0)