Skip to content

Commit 27e8d0a

Browse files
committed
support some operation can accept ME.SparseTensor/torch/numpy in utils_minkowski
1 parent 68746b6 commit 27e8d0a

1 file changed

Lines changed: 107 additions & 12 deletions

File tree

src/py_utils/utils_minkowski.py

Lines changed: 107 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import math
2+
3+
import MinkowskiEngine as ME
4+
import numpy as np
25
import torch
36
import torch.nn as nn
4-
import MinkowskiEngine as ME
7+
58
from . import utils_torch
69

710
####################
@@ -75,7 +78,7 @@ def _sparse_tensor_key_map(
7578

7679

7780
@torch.no_grad()
78-
def sparse_tensor_map(
81+
def _sparse_tensor_map(
7982
A: ME.SparseTensor,
8083
B: ME.SparseTensor,
8184
kernel_generator=get_cube_kernel_generator(1),
@@ -103,8 +106,54 @@ def sparse_tensor_map(
103106
return _sparse_tensor_key_map(ak, bk, kg, cm, device=A.device)
104107

105108

109+
def sparse_tensor_map(A, B, kernel_generator=get_cube_kernel_generator(1)):
110+
111+
# return two torch long integeter arrays
112+
if isinstance(A, ME.SparseTensor) and isinstance(B, ME.SparseTensor):
113+
return _sparse_tensor_map(A, B, kernel_generator)
114+
115+
if isinstance(A, np.ndarray) and isinstance(B, np.ndarray):
116+
assert (len(A.shape) == 2) and (A.shape[1] == 3)
117+
assert (len(B.shape) == 2) and (B.shape[1] == 3)
118+
_device = "cuda"
119+
_coord_man = ME.CoordinateManager(D=3)
120+
121+
_func = numpy_to_sparse_tensor
122+
_A = _func(A, np.ones((len(A), 1)), _device, _coord_man)
123+
_B = _func(B, np.ones((len(B), 1)), _device, _coord_man)
124+
A_keys, B_keys = _sparse_tensor_map(_A, _B, kernel_generator)
125+
A_keys = A_keys.cpu().numpy()
126+
B_keys = B_keys.cpu().numpy()
127+
128+
return A_keys, B_keys
129+
130+
if isinstance(A, torch.Tensor) and isinstance(B, torch.Tensor):
131+
assert (len(A.shape) == 2) and (A.shape[1] == 3)
132+
assert (len(B.shape) == 2) and (B.shape[1] == 3)
133+
_device = "cuda"
134+
_coord_man = ME.CoordinateManager(D=3)
135+
136+
_func = torch_to_sparse_tensor
137+
_A = _func(A, torch.ones((len(A), 1)), _device, _coord_man)
138+
_B = _func(B, torch.ones((len(B), 1)), _device, _coord_man)
139+
A_keys, B_keys = _sparse_tensor_map(_A, _B, kernel_generator)
140+
141+
return A_keys, B_keys
142+
143+
msg = (
144+
"A & B must be one of MinkowskiEngine.SparseTensor, "
145+
"numpy array, or torch tensor pair"
146+
)
147+
raise ValueError(msg)
148+
149+
150+
################
151+
# INTERSECTION #
152+
################
153+
154+
106155
@torch.no_grad()
107-
def _A_occupied_by_B(
156+
def _A_key_occupied_by_B_key(
108157
A: ME.CoordinateMapKey,
109158
B: ME.CoordinateMapKey,
110159
coordinate_manager: ME.CoordinateManager,
@@ -124,7 +173,7 @@ def _A_occupied_by_B(
124173

125174

126175
@torch.no_grad()
127-
def A_occupied_by_B(
176+
def _A_occupied_by_B(
128177
A: ME.SparseTensor,
129178
B: ME.SparseTensor,
130179
):
@@ -136,17 +185,32 @@ def A_occupied_by_B(
136185
raise ValueError("A and B must have the same tensor_stride.")
137186

138187
mask = torch.zeros(len(A), dtype=torch.bool, device=A.device)
139-
a_idx = _A_occupied_by_B(
140-
A.coordinate_map_key,
141-
B.coordinate_map_key,
142-
A.coordinate_manager,
143-
device=A.device,
144-
)
188+
a_idx, _ = sparse_tensor_map(A, B)
145189
mask[a_idx] = True
146190

147191
return mask
148192

149193

194+
@torch.no_grad()
195+
def A_occupied_by_B(A, B):
196+
197+
if isinstance(A, ME.SparseTensor) and isinstance(B, ME.SparseTensor):
198+
return _A_occupied_by_B(A, B)
199+
200+
if isinstance(A, np.ndarray) and isinstance(B, np.ndarray):
201+
a_idx, _ = sparse_tensor_map(A, B)
202+
mask = np.zeros(len(A), dtype=bool)
203+
mask[a_idx] = True
204+
return mask
205+
206+
if isinstance(A, torch.Tensor) and isinstance(B, torch.Tensor):
207+
mask = torch.zeros(len(A), dtype=bool, device=A.device)
208+
mask[a_idx] = True
209+
return mask
210+
211+
raise ValueError
212+
213+
150214
@torch.no_grad()
151215
def A_occupied_by_B_key(
152216
A: ME.SparseTensor,
@@ -170,7 +234,11 @@ def A_occupied_by_B_key(
170234

171235

172236
@torch.no_grad()
173-
def set_difference(A: ME.SparseTensor, B: ME.SparseTensor):
237+
def _set_difference(
238+
A: ME.SparseTensor,
239+
B: ME.SparseTensor,
240+
return_indices=False,
241+
):
174242
"""A - B"""
175243

176244
assert A.tensor_stride == B.tensor_stride, "tensor_stride mismatch"
@@ -180,7 +248,9 @@ def set_difference(A: ME.SparseTensor, B: ME.SparseTensor):
180248
return A
181249

182250
if torch.all(occupied):
183-
return None # A - B is empty; avoid constructing 0-voxel SparseTensor
251+
# A - B is empty
252+
# avoid constructing 0-voxel SparseTensor (will raise error)
253+
return None
184254

185255
keep = ~occupied
186256

@@ -190,9 +260,34 @@ def set_difference(A: ME.SparseTensor, B: ME.SparseTensor):
190260
tensor_stride=A.tensor_stride,
191261
coordinate_manager=A.coordinate_manager,
192262
)
263+
if return_indices:
264+
return out, torch.nonzero(keep)[:, 0]
193265
return out
194266

195267

268+
@torch.no_grad()
269+
def set_difference(A, B, return_indices=False):
270+
271+
if isinstance(A, ME.SparseTensor) and isinstance(B, ME.SparseTensor):
272+
return _set_difference(A, B, return_indices=return_indices)
273+
274+
if isinstance(A, np.ndarray) and isinstance(B, np.ndarray):
275+
occupied = A_occupied_by_B(A, B)
276+
keep = ~occupied
277+
if return_indices:
278+
return A[keep], np.nonzero(keep)[0]
279+
return A[keep]
280+
281+
if isinstance(A, torch.Tensor) and isinstance(B, torch.Tensor):
282+
occupied = A_occupied_by_B(A, B)
283+
keep = ~occupied
284+
if return_indices:
285+
return A[keep], torch.nonzero(keep)[:, 0]
286+
return A[keep]
287+
288+
raise ValueError
289+
290+
196291
@torch.no_grad()
197292
def set_disjoint_union(A: ME.SparseTensor, B: ME.SparseTensor):
198293
"""A U B, assume A and B don't have intersection"""

0 commit comments

Comments
 (0)