def test_coords_manager(self): key = CoordsKey(D=1) key.setTensorStride(1) cm = CoordsManager(D=1) coords = (torch.rand(5, 1) * 100).int() cm.initialize(coords, key) print(key) print(cm)
def forward(ctx, input_features, average=True, mode=GlobalPoolingMode.AUTO, in_coords_key=None, out_coords_key=None, coords_manager=None): if out_coords_key is None: out_coords_key = CoordsKey(in_coords_key.D) assert isinstance(mode, GlobalPoolingMode), \ f"Mode must be an instance of GlobalPoolingMode, {mode}" ctx.in_coords_key = in_coords_key ctx.out_coords_key = out_coords_key ctx.in_feat = input_features ctx.average = average ctx.coords_manager = coords_manager ctx.mode = mode.value fw_fn = getattr(MEB, 'GlobalPoolingForward' + get_postfix(input_features)) out_feat, num_nonzero = fw_fn(ctx.in_feat, ctx.in_coords_key.CPPCoordsKey, ctx.out_coords_key.CPPCoordsKey, ctx.coords_manager.CPPCoordsManager, ctx.average, ctx.mode) ctx.num_nonzero = num_nonzero return out_feat
def forward(self, input): assert isinstance(input, SparseTensor) assert input.D == self.dimension # Create a region_offset self.region_type_, self.region_offset_, _ = \ self.kernel_generator.get_kernel(input.tensor_stride, self.is_transpose) if self.out_coords_key is None: out_coords_key = CoordsKey(input.coords_key.D) else: out_coords_key = self.out_coords_key # If the kernel_size == 1, the convolution is simply a matrix # multiplication if self.use_mm: outfeat = input.F.mm(self.kernel) out_coords_key = input.coords_key else: outfeat = self.conv.apply( input.F, self.kernel, input.tensor_stride, self.stride, self.kernel_size, self.dilation, self.region_type_, self.region_offset_, self.generate_new_coords, input.coords_key, out_coords_key, input.coords_man) if self.has_bias: outfeat += self.bias return SparseTensor(outfeat, coords_key=out_coords_key, coords_manager=input.coords_man)
def __truediv__(self, other): r""" Divide its feature by the corresponding feature of the other :attr:`MinkowskiEngine.SparseTensor` or a :attr:`torch.Tensor` element-wise. For coordinates that exist on one sparse tensor but not on the other, features of the counterpart that do not exist will be set to 0. """ assert isinstance(other, (SparseTensor, torch.Tensor)) if isinstance(other, SparseTensor): assert self.coords_man == other.coords_man, COORDS_MAN_DIFFERENT_ERROR if self.coords_key == other.coords_key: return SparseTensor(self._F / other.F, coords_key=self.coords_key, coords_manager=self.coords_man) else: # Generate union maps out_key = CoordsKey(self.coords_man.D) ins, outs = self.coords_man.get_union_map( (self.coords_key, other.coords_key), out_key) N_out = self.coords_man.get_coords_size_by_coords_key(out_key) out_F = torch.zeros((N_out, self._F.size(1)), dtype=self.dtype, device=self.device) out_F[outs[0]] = self._F[ins[0]] out_F[outs[1]] /= other._F[ins[1]] return SparseTensor(out_F, coords_key=out_key, coords_manager=self.coords_man) else: # when it is a torch.Tensor return SparseTensor(self._F / other, coords_key=self.coords_key, coords_manager=self.coords_man)
def forward(ctx, input_features, batch_size=0, average=True, in_coords_key=None, out_coords_key=None, coords_manager=None): if out_coords_key is None: out_coords_key = CoordsKey(in_coords_key.D) ctx.in_coords_key = in_coords_key ctx.out_coords_key = out_coords_key ctx.in_feat = input_features out_feat = input_features.new() ctx.average = average ctx.num_nonzero = input_features.new() ctx.coords_manager = coords_manager D = in_coords_key.D fw_fn = getattr(MEB, 'GlobalPoolingForward' + get_postfix(input_features)) fw_fn(D, ctx.in_feat, out_feat, ctx.num_nonzero, ctx.in_coords_key.CPPCoordsKey, ctx.out_coords_key.CPPCoordsKey, ctx.coords_manager.CPPCoordsManager, batch_size, ctx.average) return out_feat
def forward(self, input, mask): r""" Args: :attr:`input` (:attr:`MinkowskiEnigne.SparseTensor`): a sparse tensor to remove coordinates from. :attr:`mask` (:attr:`torch.BoolTensor`): mask vector that specifies which one to keep. Coordinates with False will be removed. Returns: A :attr:`MinkowskiEngine.SparseTensor` with C = coordinates corresponding to `mask == True` F = copy of the features from `mask == True`. Example:: >>> # Define inputs >>> input = SparseTensor(feats, coords=coords) >>> # Any boolean tensor can be used as the filter >>> mask = torch.rand(feats.size(0)) < 0.5 >>> pruning = MinkowskiPruning() >>> output = pruning(input, mask) """ assert isinstance(input, SparseTensor) out_coords_key = CoordsKey(input.coords_key.D) output = self.pruning.apply(input.F, mask, input.coords_key, out_coords_key, input.coords_man) return SparseTensor(output, coords_key=out_coords_key, coords_manager=input.coords_man)
def forward(self, input): assert isinstance(input, SparseTensor) out_coords_key = CoordsKey(input.coords_key.D) output = self.pooling.apply(input.F, input.coords_key, out_coords_key, input.coords_man) return SparseTensor(output, coords_key=out_coords_key, coords_manager=input.coords_man)
def forward(self, input, use_feat): assert isinstance(input, SparseTensor) assert input.D == self.dimension out_coords_key = CoordsKey(input.coords_key.D) output = self.pruning.apply(input.F, use_feat, input.coords_key, out_coords_key, input.coords_man) return SparseTensor(output, coords_key=out_coords_key, coords_manager=input.coords_man)
def forward(ctx, input_features, kernel, tensor_stride=1, stride=1, kernel_size=-1, dilation=1, region_type=0, region_offset=None, generate_new_coords=False, in_coords_key=None, out_coords_key=None, coords_manager=None): """ region_type=0 HyperCube """ # Prep arguments # Kernel shape (n_spatial_kernels, in_nfeat, out_nfeat) assert input_features.shape[1] == kernel.shape[1], \ "The input shape " + str(list(input_features.shape)) + \ " does not match the kernel shape " + str(list(kernel.shape)) if out_coords_key is None: out_coords_key = CoordsKey(in_coords_key.D) assert in_coords_key.D == out_coords_key.D assert input_features.type() == kernel.type(), \ f"Type mismatch input: {input_features.type()} != kernel: {kernel.type()}" if not input_features.is_contiguous(): input_features = input_features.contiguous() tensor_stride, stride, kernel_size, dilation, region_type = prep_args( tensor_stride, stride, kernel_size, dilation, region_type, in_coords_key.D) if region_offset is None: region_offset = torch.IntTensor() ctx.in_feat = input_features ctx.kernel = kernel ctx = save_ctx(ctx, tensor_stride, stride, kernel_size, dilation, region_type, in_coords_key, out_coords_key, coords_manager) D = in_coords_key.D out_feat = input_features.new() fw_fn = getattr( MEB, 'ConvolutionTransposeForward' + get_postfix(input_features)) fw_fn(ctx.in_feat, out_feat, kernel, convert_to_int_list(ctx.tensor_stride, D), convert_to_int_list(ctx.stride, D), convert_to_int_list(ctx.kernel_size, D), convert_to_int_list(ctx.dilation, D), region_type, region_offset, ctx.in_coords_key.CPPCoordsKey, ctx.out_coords_key.CPPCoordsKey, ctx.coords_man.CPPCoordsManager, generate_new_coords) return out_feat
def forward(self, input): assert isinstance(input, SparseTensor) assert input.D == self.dimension out_coords_key = CoordsKey(input.coords_key.D) output = self.pooling.apply(input.F, self.batch_size, self.average, input.coords_key, out_coords_key, input.coords_man) return SparseTensor( output, coords_key=out_coords_key, coords_manager=input.coords_man)
def forward(ctx, in_feat, mode=GlobalPoolingMode.AUTO, in_coords_key=None, glob_coords_key=None, coords_manager=None): assert isinstance(mode, GlobalPoolingMode), \ f"Mode must be an instance of GlobalPoolingMode, {mode}" if glob_coords_key is None: glob_coords_key = CoordsKey(in_coords_key.D) gpool_forward = getattr(MEB, 'GlobalPoolingForward' + get_postfix(in_feat)) broadcast_forward = getattr(MEB, 'BroadcastForward' + get_postfix(in_feat)) add = operation_type_to_int(OperationType.ADDITION) multiply = operation_type_to_int(OperationType.MULTIPLICATION) mean = in_feat.new() num_nonzero = in_feat.new() cpp_in_coords_key = in_coords_key.CPPCoordsKey cpp_glob_coords_key = glob_coords_key.CPPCoordsKey cpp_coords_manager = coords_manager.CPPCoordsManager mean, num_nonzero = gpool_forward(in_feat, cpp_in_coords_key, cpp_glob_coords_key, cpp_coords_manager, True, mode.value) # X - \mu centered_feat = broadcast_forward(in_feat, -mean, add, cpp_in_coords_key, cpp_glob_coords_key, cpp_coords_manager) # Variance = 1/N \sum (X - \mu) ** 2 variance, num_nonzero = gpool_forward(centered_feat**2, cpp_in_coords_key, cpp_glob_coords_key, cpp_coords_manager, True, mode.value) # norm_feat = (X - \mu) / \sigma inv_std = 1 / (variance + 1e-8).sqrt() norm_feat = broadcast_forward(centered_feat, inv_std, multiply, cpp_in_coords_key, cpp_glob_coords_key, cpp_coords_manager) ctx.mode = mode ctx.in_coords_key, ctx.glob_coords_key = in_coords_key, glob_coords_key ctx.coords_manager = coords_manager # For GPU tensors, must use save_for_backward. ctx.save_for_backward(inv_std, norm_feat) return norm_feat
def forward(ctx, in_feat, batch_size=0, in_coords_key=None, glob_coords_key=None, coords_manager=None): if glob_coords_key is None: glob_coords_key = CoordsKey(in_coords_key.D) gpool_forward = getattr(MEB, 'GlobalPoolingForward' + get_postfix(in_feat)) broadcast_forward = getattr(MEB, 'BroadcastForward' + get_postfix(in_feat)) add = operation_type_to_int(OperationType.ADDITION) multiply = operation_type_to_int(OperationType.MULTIPLICATION) mean = in_feat.new() num_nonzero = in_feat.new() D = in_coords_key.D cpp_in_coords_key = in_coords_key.CPPCoordsKey cpp_glob_coords_key = glob_coords_key.CPPCoordsKey cpp_coords_manager = coords_manager.CPPCoordsManager gpool_forward(D, in_feat, mean, num_nonzero, cpp_in_coords_key, cpp_glob_coords_key, cpp_coords_manager, batch_size, True) # X - \mu centered_feat = in_feat.new() broadcast_forward(D, in_feat, -mean, centered_feat, add, cpp_in_coords_key, cpp_glob_coords_key, cpp_coords_manager) # Variance = 1/N \sum (X - \mu) ** 2 variance = in_feat.new() gpool_forward(D, centered_feat**2, variance, num_nonzero, cpp_in_coords_key, cpp_glob_coords_key, cpp_coords_manager, batch_size, True) # norm_feat = (X - \mu) / \sigma inv_std = 1 / (variance + 1e-8).sqrt() norm_feat = in_feat.new() broadcast_forward(D, centered_feat, inv_std, norm_feat, multiply, cpp_in_coords_key, cpp_glob_coords_key, cpp_coords_manager) ctx.batch_size = batch_size ctx.in_coords_key, ctx.glob_coords_key = in_coords_key, glob_coords_key ctx.coords_manager = coords_manager # For GPU tensors, must use save_for_backward. ctx.save_for_backward(inv_std, norm_feat) return norm_feat
def test_hash(self): try: import numpy as np except: return N, M = 1000, 1000 I, J = np.meshgrid(np.arange(N), np.arange(M)) I = I.reshape(-1, 1) - 100 J = J.reshape(-1, 1) - 100 K = np.zeros_like(I) C = np.hstack((I, J, K)) coords_manager = CoordsManager(D=2) coords_key = CoordsKey(2) coords_manager.initialize(torch.from_numpy(C).int(), coords_key)
def forward(ctx, input_features, kernel, tensor_stride=1, stride=1, kernel_size=-1, dilation=1, region_type=0, region_offset=None, in_coords_key=None, out_coords_key=None, coords_manager=None): """ region_type=0 HyperCube """ # Prep arguments # Kernel shape (n_spatial_kernels, in_nfeat, out_nfeat) assert input_features.shape[1] == kernel.shape[1] if out_coords_key is None: out_coords_key = CoordsKey(in_coords_key.D) assert in_coords_key.D == out_coords_key.D assert input_features.type() == kernel.type() tensor_stride, stride, kernel_size, dilation, region_type = prep_args( tensor_stride, stride, kernel_size, dilation, region_type, in_coords_key.D) if region_offset is None: region_offset = torch.IntTensor() ctx.in_feat = input_features ctx.kernel = kernel ctx = save_ctx(ctx, tensor_stride, stride, kernel_size, dilation, region_type, in_coords_key, out_coords_key, coords_manager) D = in_coords_key.D out_feat = input_features.new() fw_fn = getattr(MEB, 'ConvolutionForward' + get_postfix(input_features)) fw_fn(D, ctx.in_feat, out_feat, kernel, convert_to_int_list(ctx.tensor_stride, D), convert_to_int_list(ctx.stride, D), convert_to_int_list(ctx.kernel_size, D), convert_to_int_list(ctx.dilation, D), region_type, region_offset, ctx.in_coords_key.CPPCoordsKey, ctx.out_coords_key.CPPCoordsKey, ctx.coords_man.CPPCoordsManager) return out_feat
def forward(ctx, input_features, tensor_stride=1, stride=1, kernel_size=-1, dilation=1, region_type=0, region_offset=None, average=True, in_coords_key=None, out_coords_key=None, coords_manager=None): assert isinstance(region_type, RegionType) if out_coords_key is None: out_coords_key = CoordsKey(in_coords_key.D) assert in_coords_key.D == out_coords_key.D if not input_features.is_contiguous(): input_features = input_features.contiguous() tensor_stride, stride, kernel_size, dilation, region_type = prep_args( tensor_stride, stride, kernel_size, dilation, region_type, in_coords_key.D) if region_offset is None: region_offset = torch.IntTensor() ctx.in_feat = input_features ctx = save_ctx(ctx, tensor_stride, stride, kernel_size, dilation, region_type, in_coords_key, out_coords_key, coords_manager) ctx.use_avg = average D = in_coords_key.D out_feat = input_features.new() ctx.num_nonzero = input_features.new() fw_fn = get_minkowski_function('AvgPoolingForward', input_features) fw_fn(ctx.in_feat, out_feat, ctx.num_nonzero, convert_to_int_list(ctx.tensor_stride, D), convert_to_int_list(ctx.stride, D), convert_to_int_list(ctx.kernel_size, D), convert_to_int_list(ctx.dilation, D), region_type, region_offset, ctx.in_coords_key.CPPCoordsKey, ctx.out_coords_key.CPPCoordsKey, ctx.coords_man.CPPCoordsManager, ctx.use_avg) return out_feat
def forward(ctx, input_features, tensor_stride=1, stride=1, kernel_size=-1, dilation=1, region_type=0, region_offset=None, in_coords_key=None, out_coords_key=None, coords_manager=None): assert isinstance(region_type, RegionType) if out_coords_key is None: out_coords_key = CoordsKey(in_coords_key.D) assert in_coords_key.D == out_coords_key.D if not input_features.is_contiguous(): input_features = input_features.contiguous() tensor_stride, stride, kernel_size, dilation, region_type = prep_args( tensor_stride, stride, kernel_size, dilation, region_type, in_coords_key.D) if region_offset is None: region_offset = torch.IntTensor() ctx.in_feat = input_features ctx = save_ctx(ctx, tensor_stride, stride, kernel_size, dilation, region_type, in_coords_key, out_coords_key, coords_manager) D = in_coords_key.D out_feat = input_features.new() max_index = input_features.new().int() ctx.max_index = max_index fw_fn = getattr(MEB, 'MaxPoolingForward' + get_postfix(input_features)) fw_fn(input_features, out_feat, max_index, convert_to_int_list(ctx.tensor_stride, D), convert_to_int_list(ctx.stride, D), convert_to_int_list(ctx.kernel_size, D), convert_to_int_list(ctx.dilation, D), region_type, region_offset, ctx.in_coords_key.CPPCoordsKey, ctx.out_coords_key.CPPCoordsKey, ctx.coords_man.CPPCoordsManager) return out_feat
def forward(self, input): assert isinstance(input, SparseTensor) assert input.D == self.dimension # Create a region_offset self.region_type_, self.region_offset_, _ = \ self.kernel_generator.get_kernel(input.tensor_stride, self.is_transpose) if self.out_coords_key is None: out_coords_key = CoordsKey(input.coords_key.D) else: out_coords_key = self.out_coords_key output = self.pooling.apply(input.F, input.tensor_stride, self.stride, self.kernel_size, self.dilation, self.region_type_, self.region_offset_, input.coords_key, out_coords_key, input.coords_man) return SparseTensor( output, coords_key=out_coords_key, coords_manager=input.coords_man)
def forward(ctx, input_features, tensor_stride=1, stride=1, kernel_size=-1, dilation=1, region_type=-1, region_offset=None, average=False, in_coords_key=None, out_coords_key=None, coords_manager=None): assert isinstance(region_type, RegionType) if out_coords_key is None: out_coords_key = CoordsKey(in_coords_key.D) assert in_coords_key.D == out_coords_key.D tensor_stride, stride, kernel_size, dilation, region_type = prep_args( tensor_stride, stride, kernel_size, dilation, region_type, in_coords_key.D) if region_offset is None: region_offset = torch.IntTensor() ctx.in_feat = input_features out_feat = input_features.new() ctx.num_nonzero = input_features.new() ctx = save_ctx(ctx, tensor_stride, stride, kernel_size, dilation, region_type, in_coords_key, out_coords_key, coords_manager) D = in_coords_key.D fw_fn = getattr( MEB, 'PoolingTransposeForward' + get_postfix(input_features)) fw_fn(ctx.in_feat, out_feat, ctx.num_nonzero, convert_to_int_list(ctx.tensor_stride, D), convert_to_int_list(ctx.stride, D), convert_to_int_list(ctx.kernel_size, D), convert_to_int_list(ctx.dilation, D), region_type, region_offset, ctx.in_coords_key.CPPCoordsKey, ctx.out_coords_key.CPPCoordsKey, ctx.coords_man.CPPCoordsManager) return out_feat
def forward(self, *inputs): r""" Args: A variable number of :attr:`MinkowskiEngine.SparseTensor`'s. Returns: A :attr:`MinkowskiEngine.SparseTensor` with coordinates = union of all input coordinates, and features = sum of all features corresponding to the coordinate. Example:: >>> # Define inputs >>> input1 = SparseTensor( >>> torch.rand(N, in_channels, dtype=torch.double), coords=coords) >>> # All inputs must share the same coordinate manager >>> input2 = SparseTensor( >>> torch.rand(N, in_channels, dtype=torch.double), >>> coords=coords + 1, >>> coords_manager=input1.coords_man, # Must use same coords manager >>> force_creation=True # The tensor stride [1, 1] already exists. >>> ) >>> union = MinkowskiUnion() >>> output = union(input1, iput2) """ for s in inputs: assert isinstance(s, SparseTensor), "Inputs must be sparse tensors." assert len(inputs) > 1, \ "input must be a set with at least 2 SparseTensors" out_coords_key = CoordsKey(inputs[0].coords_key.D) output = self.union.apply([input.coords_key for input in inputs], out_coords_key, inputs[0].coords_man, *[input.F for input in inputs]) return SparseTensor(output, coords_key=out_coords_key, coords_manager=inputs[0].coords_man)
def _get_coords_key(input: SparseTensor, coords: Union[torch.IntTensor, CoordsKey, SparseTensor] = None, tensor_stride: Union[Sequence, np.ndarray, torch.IntTensor] = 1): r"""Process coords according to its type. """ if coords is not None: assert isinstance(coords, (CoordsKey, torch.IntTensor, SparseTensor)) if isinstance(coords, torch.IntTensor): coords_key = input.coords_man.create_coords_key( coords, tensor_stride=tensor_stride, force_creation=True, force_remap=True, allow_duplicate_coords=True) elif isinstance(coords, SparseTensor): coords_key = coords.coords_key else: # CoordsKey type due to the previous assertion coords_key = coords else: coords_key = CoordsKey(input.D) return coords_key
def forward(ctx, input_features, in_coords_key=None, out_coords_key=None, coords_manager=None): if out_coords_key is None: out_coords_key = CoordsKey(in_coords_key.D) ctx.in_coords_key = in_coords_key ctx.out_coords_key = out_coords_key ctx.in_feat = input_features out_feat = input_features.new() max_index = input_features.new().int() ctx.max_index = max_index ctx.coords_manager = coords_manager fw_fn = getattr( MEB, 'GlobalMaxPoolingForward' + get_postfix(input_features)) fw_fn(ctx.in_feat, out_feat, ctx.max_index, ctx.in_coords_key.CPPCoordsKey, ctx.out_coords_key.CPPCoordsKey, ctx.coords_manager.CPPCoordsManager) return out_feat
def __init__(self, feats, coords=None, coords_key=None, coords_manager=None, tensor_stride=1): r""" Args: :attr:`feats` (:attr:`torch.FloatTensor`, :attr:`torch.DoubleTensor`, :attr:`torch.cuda.FloatTensor`, or :attr:`torch.cuda.DoubleTensor`): The features of the sparse tensor. :attr:`coords` (:attr:`torch.IntTensor`): The coordinates associated to the features. If not provided, :attr:`coords_key` must be provided. :attr:`coords_key` (:attr:`MinkowskiEngine.CoordsKey`): When the coordinates are already cached in the MinkowskiEngine, we could reuse the same coordinates by simply providing the coordinate hash key. In most case, this process is done automatically. If you provide one, make sure you understand what you are doing. :attr:`coords_manager` (:attr:`MinkowskiEngine.CoordsManager`): The MinkowskiEngine creates a dynamic computation graph using an input coordinates. If not provided, the MinkowskiEngine will create a new computation graph, so make sure to provide the same :attr:`CoordsManager` when you want to use the same computation graph. To use a sparse tensor within the same computation graph that you are using before, feed the :attr:`CoordsManager` of the sparse tensor that you want to use by :attr:`sparse_tensor.coords_man`. In most cases, this process is handled automatically. When you use it, make sure you understand what you are doing. :attr:`tensor_stride` (:attr:`int`, :attr:`list`, :attr:`numpy.array`, or :attr:`tensor.Tensor`): The tensor stride of the current sparse tensor. By default, it is 1. """ assert isinstance(feats, torch.Tensor), "Features must be a torch.Tensor" if coords is None and coords_key is None: raise ValueError('Either coords or coords_key must be provided') if coords_key is None: assert coords_manager is not None or coords is not None D = -1 if coords_manager is None: D = coords.size(1) - 1 else: D = coords_manager.D coords_key = CoordsKey(D) coords_key.setTensorStride(convert_to_int_list(tensor_stride, D)) else: assert isinstance(coords_key, CoordsKey) if coords is not None: assert isinstance(coords, torch.Tensor), \ "Coordinate must be of type torch.Tensor" if not isinstance(coords, torch.IntTensor): warnings.warn( 'Coords implicitly converted to torch.IntTensor. ' + 'To remove this warning, use `.int()` to convert the ' + 'coords into an torch.IntTensor') coords = coords.int() assert feats.shape[0] == coords.shape[0], \ "Number of rows in features and coordinates do not match." coords = coords.contiguous() if coords_manager is None: assert coords is not None, "Initial coordinates must be given" D = coords.size(1) - 1 coords_manager = CoordsManager(D=D) coords_manager.initialize(coords, coords_key) else: assert isinstance(coords_manager, CoordsManager) self._F = feats.contiguous() self._C = coords self.coords_key = coords_key self.coords_man = coords_manager
def test_coords_key(self): key = CoordsKey(D=1) key.setKey(1) self.assertTrue(key.getKey() == 1) key.setTensorStride([1]) print(key)
def __init__(self, feats, coords=None, coords_key=None, coords_manager=None, force_creation=False, allow_duplicate_coords=False, tensor_stride=1): r""" Args: :attr:`feats` (:attr:`torch.FloatTensor`, :attr:`torch.DoubleTensor`, :attr:`torch.cuda.FloatTensor`, or :attr:`torch.cuda.DoubleTensor`): The features of the sparse tensor. :attr:`coords` (:attr:`torch.IntTensor`): The coordinates associated to the features. If not provided, :attr:`coords_key` must be provided. :attr:`coords_key` (:attr:`MinkowskiEngine.CoordsKey`): When the coordinates are already cached in the MinkowskiEngine, we could reuse the same coordinates by simply providing the coordinate hash key. In most case, this process is done automatically. If you provide one, make sure you understand what you are doing. :attr:`coords_manager` (:attr:`MinkowskiEngine.CoordsManager`): The MinkowskiEngine creates a dynamic computation graph and all coordinates inside the same computation graph are managed by a CoordsManager object. If not provided, the MinkowskiEngine will create a new computation graph. In most cases, this process is handled automatically and you do not need to use this. When you use it, make sure you understand what you are doing. :attr:`force_creation` (:attr:`bool`): Force creation of the coordinates. This allows generating a new set of coordinates even when there exists another set of coordinates with the same tensor stride. This could happen when you manually feed the same :attr:`coords_manager`. :attr:`allow_duplicate_coords` (:attr:`bool`): Allow duplicate coordinates when creating the sparse tensor. Internally, it will generate a new unique set of coordinates and use features of at the corresponding unique coordinates. In general, setting `allow_duplicate_coords=True` is not recommended as it could hide obvious errors in your data loading and preprocessing steps. Please refer to the quantization and data loading tutorial on `here <https://stanfordvl.github.io/MinkowskiEngine/demo/training.html>`_ for more details. :attr:`tensor_stride` (:attr:`int`, :attr:`list`, :attr:`numpy.array`, or :attr:`tensor.Tensor`): The tensor stride of the current sparse tensor. By default, it is 1. """ assert isinstance(feats, torch.Tensor), "Features must be a torch.Tensor" if coords is None and coords_key is None: raise ValueError('Either coords or coords_key must be provided') if coords_key is None: assert coords_manager is not None or coords is not None D = -1 if coords_manager is None: D = coords.size(1) - 1 else: D = coords_manager.D coords_key = CoordsKey(D) coords_key.setTensorStride(convert_to_int_list(tensor_stride, D)) else: assert isinstance(coords_key, CoordsKey) if coords is not None: assert isinstance(coords, torch.Tensor), \ "Coordinate must be of type torch.Tensor" if not isinstance(coords, torch.IntTensor): warnings.warn( 'Coords implicitly converted to torch.IntTensor. ' + 'To remove this warning, use `.int()` to convert the ' + 'coords into an torch.IntTensor') coords = coords.int() if coords.device.type != 'cpu': warnings.warn( 'Coords implicitly converted to CPU type. ' + 'To remove this warning, use `.cpu()` to convert the ' + 'coords into a CPU type') coords = coords.cpu() assert feats.shape[0] == coords.shape[0], \ "Number of rows in features and coordinates do not match." coords = coords.contiguous() if coords_manager is None: # If set to share the coords man, use the global coords man global _sparse_tensor_operation_mode, _global_coords_man if _sparse_tensor_operation_mode == SparseTensorOperationMode.SHARE_COORDS_MANAGER: if _global_coords_man is None: _global_coords_man = CoordsManager(D=coords.size(1) - 1) coords_manager = _global_coords_man else: assert coords is not None, "Initial coordinates must be given" coords_manager = CoordsManager(D=coords.size(1) - 1) if not coords_key.isKeySet(): self.mapping = coords_manager.initialize( coords, coords_key, force_creation=force_creation, force_remap=allow_duplicate_coords, allow_duplicate_coords=allow_duplicate_coords) if len(self.mapping) > 0: coords = coords[self.mapping] feats = feats[self.mapping] else: assert isinstance(coords_manager, CoordsManager) if not coords_key.isKeySet(): assert coords is not None self.mapping = coords_manager.initialize( coords, coords_key, force_creation=force_creation, force_remap=allow_duplicate_coords, allow_duplicate_coords=allow_duplicate_coords) if len(self.mapping) > 0: coords = coords[self.mapping] feats = feats[self.mapping] self._F = feats.contiguous() self._C = coords self.coords_key = coords_key self.coords_man = coords_manager
def __init__( self, feats, coords=None, coords_key=None, coords_manager=None, force_creation=False, allow_duplicate_coords=False, quantization_mode=SparseTensorQuantizationMode.RANDOM_SUBSAMPLE, tensor_stride=1): r""" Args: :attr:`feats` (:attr:`torch.FloatTensor`, :attr:`torch.DoubleTensor`, :attr:`torch.cuda.FloatTensor`, or :attr:`torch.cuda.DoubleTensor`): The features of the sparse tensor. :attr:`coords` (:attr:`torch.IntTensor`): The coordinates associated to the features. If not provided, :attr:`coords_key` must be provided. :attr:`coords_key` (:attr:`MinkowskiEngine.CoordsKey`): When the coordinates are already cached in the MinkowskiEngine, we could reuse the same coordinates by simply providing the coordinate hash key. In most case, this process is done automatically. When you provide a `coords_key`, all other arguments will be be ignored. :attr:`coords_manager` (:attr:`MinkowskiEngine.CoordsManager`): The MinkowskiEngine creates a dynamic computation graph and all coordinates inside the same computation graph are managed by a CoordsManager object. If not provided, the MinkowskiEngine will create a new computation graph. In most cases, this process is handled automatically and you do not need to use this. When you use it, make sure you understand what you are doing. :attr:`force_creation` (:attr:`bool`): Force creation of the coordinates. This allows generating a new set of coordinates even when there exists another set of coordinates with the same tensor stride. This could happen when you manually feed the same :attr:`coords_manager`. :attr:`allow_duplicate_coords` (:attr:`bool`): Allow duplicate coordinates when creating the sparse tensor. Internally, it will generate a new unique set of coordinates and use features of at the corresponding unique coordinates. In general, setting `allow_duplicate_coords=True` is not recommended as it could hide obvious errors in your data loading and preprocessing steps. Please refer to the quantization and data loading tutorial on `here <https://stanfordvl.github.io/MinkowskiEngine/demo/training.html>`_ for more details. :attr:`quantizatino_mode` (:attr:`MinkowskiEngine.SparseTensorQuantizationMode`): Defines the quantization method and how to define features of a sparse tensor. Please refer to :attr:`SparseTensorQuantizationMode` for details. :attr:`tensor_stride` (:attr:`int`, :attr:`list`, :attr:`numpy.array`, or :attr:`tensor.Tensor`): The tensor stride of the current sparse tensor. By default, it is 1. """ assert isinstance(feats, torch.Tensor), "Features must be a torch.Tensor" assert feats.ndim == 2, f"The feature should be a matrix, The input feature is an order-{feats.ndim} tensor." assert isinstance(quantization_mode, SparseTensorQuantizationMode) self.quantization_mode = quantization_mode if coords is None and coords_key is None: raise ValueError('Either coords or coords_key must be provided') if coords_key is None: assert coords_manager is not None or coords is not None D = -1 if coords_manager is None: D = coords.size(1) - 1 else: D = coords_manager.D coords_key = CoordsKey(D) coords_key.setTensorStride(convert_to_int_list(tensor_stride, D)) else: assert isinstance(coords_key, CoordsKey) if coords is not None: assert isinstance(coords, torch.Tensor), \ "Coordinate must be of type torch.Tensor" if not isinstance(coords, torch.IntTensor): warnings.warn( 'Coords implicitly converted to torch.IntTensor. ' + 'To remove this warning, use `.int()` to convert the ' + 'coords into an torch.IntTensor') coords = torch.floor(coords).int() if coords.device.type != 'cpu': warnings.warn( 'Coords implicitly converted to CPU type. ' + 'To remove this warning, use `.cpu()` to convert the ' + 'coords into a CPU type') coords = coords.cpu() assert feats.shape[0] == coords.shape[0], \ "The number of rows in features and coordinates do not match." coords = coords.contiguous() ########################## # Setup CoordsManager ########################## if coords_manager is None: # If set to share the coords man, use the global coords man global _sparse_tensor_operation_mode, _global_coords_man if _sparse_tensor_operation_mode == SparseTensorOperationMode.SHARE_COORDS_MANAGER: if _global_coords_man is None: _global_coords_man = CoordsManager(D=coords.size(1) - 1) coords_manager = _global_coords_man else: assert coords is not None, "Initial coordinates must be given" coords_manager = CoordsManager(D=coords.size(1) - 1) else: assert isinstance(coords_manager, CoordsManager) ########################## # Initialize coords ########################## if not coords_key.isKeySet() and coords is not None and len( coords) > 0: if quantization_mode == SparseTensorQuantizationMode.RANDOM_SUBSAMPLE: force_remap = True return_inverse = False elif quantization_mode == SparseTensorQuantizationMode.UNWEIGHTED_AVERAGE: force_remap = True return_inverse = True self.unique_index, self.inverse_mapping = coords_manager.initialize( coords, coords_key, force_creation=force_creation, force_remap=force_remap, allow_duplicate_coords=allow_duplicate_coords, return_inverse=return_inverse) if quantization_mode == SparseTensorQuantizationMode.UNWEIGHTED_AVERAGE: self._CF = feats self._CC = coords feats = MEB.quantization_average_features( feats, torch.arange(len(feats)), self.inverse_mapping, len(self.unique_index), 0) coords = coords[self.unique_index] elif force_remap: assert len(self.unique_index) > 0 self._CC = coords self._CF = feats coords = coords[self.unique_index] feats = feats[self.unique_index] elif coords is not None: # empty / invalid coords assert isinstance(coords, torch.IntTensor) assert coords.ndim == 2 coords_manager.initialize(coords, coords_key, force_creation=force_creation, force_remap=False, allow_duplicate_coords=False, return_inverse=False) elif coords_key is not None: assert coords_key.isKeySet() self._F = feats.contiguous() self._C = coords self.coords_key = coords_key self.coords_man = coords_manager