11import math
2+
3+ import MinkowskiEngine as ME
4+ import numpy as np
25import torch
36import torch .nn as nn
4- import MinkowskiEngine as ME
7+
58from . 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 ()
151215def 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 ()
197292def set_disjoint_union (A : ME .SparseTensor , B : ME .SparseTensor ):
198293 """A U B, assume A and B don't have intersection"""
0 commit comments