Skip to content

Commit 0e10871

Browse files
authored
Fix: error on instance from batch_x missing from batch_y in nearest() (#168)
* Error on batch_x instance missing from batch_y in nearest() (#163) * Error on unsorted batch_x/batch_y in nearest()
1 parent 82e9df9 commit 0e10871

File tree

2 files changed

+61
-0
lines changed

2 files changed

+61
-0
lines changed

test/test_nearest.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,38 @@ def test_nearest(dtype, device):
3333

3434
out = nearest(x, y)
3535
assert out.tolist() == [0, 0, 1, 1, 2, 2, 3, 3]
36+
37+
# Invalid input: instance 1 only in batch_x
38+
batch_x = tensor([0, 0, 0, 0, 1, 1, 1, 1], torch.long, device)
39+
batch_y = tensor([0, 0, 0, 0], torch.long, device)
40+
with pytest.raises(ValueError):
41+
out = nearest(x, y, batch_x, batch_y)
42+
43+
# Invalid input: instance 1 only in batch_x (implicitly as batch_y=None)
44+
with pytest.raises(ValueError):
45+
out = nearest(x, y, batch_x, batch_y=None)
46+
47+
# Valid input: instance 1 only in batch_y
48+
batch_x = tensor([0, 0, 0, 0, 0, 0, 0, 0], torch.long, device)
49+
batch_y = tensor([0, 0, 1, 1], torch.long, device)
50+
out = nearest(x, y, batch_x, batch_y)
51+
assert out.tolist() == [0, 0, 1, 1, 0, 0, 1, 1]
52+
53+
# Invalid input: instance 2 only in batch_x
54+
# (i.e.instance in the middle missing)
55+
batch_x = tensor([0, 0, 1, 1, 2, 2, 3, 3], torch.long, device)
56+
batch_y = tensor([0, 1, 3, 3], torch.long, device)
57+
with pytest.raises(ValueError):
58+
out = nearest(x, y, batch_x, batch_y)
59+
60+
# Invalid input: batch_x unsorted
61+
batch_x = tensor([0, 0, 1, 0, 0, 0, 0], torch.long, device)
62+
batch_y = tensor([0, 0, 1, 1], torch.long, device)
63+
with pytest.raises(ValueError):
64+
out = nearest(x, y, batch_x, batch_y)
65+
66+
# Invalid input: batch_y unsorted
67+
batch_x = tensor([0, 0, 0, 0, 1, 1, 1, 1], torch.long, device)
68+
batch_y = tensor([0, 0, 1, 0], torch.long, device)
69+
with pytest.raises(ValueError):
70+
out = nearest(x, y, batch_x, batch_y)

torch_cluster/nearest.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ def nearest(x: torch.Tensor, y: torch.Tensor,
4242
y = y.view(-1, 1) if y.dim() == 1 else y
4343
assert x.size(1) == y.size(1)
4444

45+
if batch_x is not None and (batch_x[1:] - batch_x[:-1] < 0).any():
46+
raise ValueError("batch_x is not sorted")
47+
if batch_y is not None and (batch_y[1:] - batch_y[:-1] < 0).any():
48+
raise ValueError("batch_y is not sorted")
49+
4550
if x.is_cuda:
4651
if batch_x is not None:
4752
assert x.size(0) == batch_x.numel()
@@ -67,10 +72,31 @@ def nearest(x: torch.Tensor, y: torch.Tensor,
6772
else:
6873
ptr_y = torch.tensor([0, y.size(0)], device=y.device)
6974

75+
# if an instance in batch_x is non-empty, it must be
76+
# non-empty in batch_y as well
77+
instance_nonempty_x = (ptr_x[:-1] != ptr_x[1:])
78+
instance_nonempty_y = (ptr_y[:len(ptr_x)-1] != ptr_y[1:len(ptr_x)])
79+
if (len(ptr_x) > len(ptr_y) or
80+
(instance_nonempty_x & ~instance_nonempty_y).any()):
81+
raise ValueError("Some batch index occurs in batch_x "
82+
"that does not occur in batch_y")
83+
7084
return torch.ops.torch_cluster.nearest(x, y, ptr_x, ptr_y)
7185
else:
86+
if (batch_x is None) != (batch_y is None):
87+
raise ValueError("Either both or none of batch_x, batch_y "
88+
"may be None")
89+
7290
# Translate and rescale x and y to [0, 1].
7391
if batch_x is not None and batch_y is not None:
92+
# if an instance in batch_x is non-empty, it must be
93+
# non-empty in batch_y as well
94+
if not torch.isin(torch.unique_consecutive(batch_x),
95+
torch.unique_consecutive(batch_y),
96+
assume_unique=True).all():
97+
raise ValueError("Some batch index occurs in batch_x "
98+
"that does not occur in batch_y")
99+
74100
assert x.dim() == 2 and batch_x.dim() == 1
75101
assert y.dim() == 2 and batch_y.dim() == 1
76102
assert x.size(0) == batch_x.size(0)

0 commit comments

Comments
 (0)