def __init__(self, kernel_size, stride=1, dilation=1, kernel_generator=None, out_coords_key=None, is_transpose=False, average=True, dimension=-1): super(MinkowskiPoolingBase, self).__init__() if out_coords_key is not None: assert isinstance(out_coords_key, CoordsKey) assert dimension > 0, f"dimension must be a positive integer, {dimension}" stride = convert_to_int_tensor(stride, dimension) kernel_size = convert_to_int_tensor(kernel_size, dimension) dilation = convert_to_int_tensor(dilation, dimension) if torch.prod(kernel_size) == 1 and torch.prod(stride) == 1: raise ValueError('Trivial input output mapping') if kernel_generator is None: kernel_generator = KernelGenerator(kernel_size=kernel_size, stride=stride, dilation=dilation, dimension=dimension) self.is_transpose = is_transpose self.average = average self.kernel_size = kernel_size self.stride = stride self.dilation = dilation self.kernel_generator = kernel_generator self.out_coords_key = out_coords_key self.dimension = dimension
def __init__(self, in_channels, out_channels, kernel_size=-1, stride=1, dilation=1, has_bias=False, kernel_generator=None, out_coords_key=None, is_transpose=False, dimension=-1): super(MinkowskiConvolutionBase, self).__init__() assert dimension > 0, f"dimension must be a positive integer, {dimension}" if out_coords_key is not None: assert isinstance(out_coords_key, CoordsKey) if kernel_generator is None: kernel_generator = KernelGenerator( kernel_size=kernel_size, stride=stride, dilation=dilation, dimension=dimension) else: kernel_size = kernel_generator.kernel_size stride = convert_to_int_tensor(stride, dimension) kernel_size = convert_to_int_tensor(kernel_size, dimension) dilation = convert_to_int_tensor(dilation, dimension) kernel_volume = kernel_generator.kernel_volume self.is_transpose = is_transpose self.in_channels = in_channels self.out_channels = out_channels self.kernel_size = kernel_size self.kernel_volume = kernel_volume self.stride = stride self.dilation = dilation self.kernel_generator = kernel_generator self.out_coords_key = out_coords_key self.dimension = dimension self.use_mm = False # use matrix multiplication when kernel is 1 Tensor = torch.FloatTensor if torch.prod(kernel_size) == 1 and torch.prod(stride) == 1: self.kernel_shape = (self.in_channels, self.out_channels) self.use_mm = True else: self.kernel_shape = (self.kernel_volume, self.in_channels, self.out_channels) self.kernel = Parameter(Tensor(*self.kernel_shape)) self.bias = Parameter(Tensor(1, out_channels)) if has_bias else None self.has_bias = has_bias
def get_kernel_map(self, in_key_or_tensor_strides, out_key_or_tensor_strides, stride=1, kernel_size=3, dilation=1, region_type=0, region_offset=None, is_transpose=False, is_pool=False, on_gpu=False): r"""Get kernel in-out maps for the specified coords keys or tensor strides. """ # region type 1 iteration with kernel_size 1 is invalid assert kernel_size > 0, "Invalid kernel size." if kernel_size == 1: region_type = 0 if isinstance(in_key_or_tensor_strides, CoordsKey): in_tensor_strides = in_key_or_tensor_strides.getTensorStride() else: in_tensor_strides = in_key_or_tensor_strides if region_offset is None: region_offset = torch.IntTensor() in_coords_key = self._get_coords_key(in_key_or_tensor_strides) out_coords_key = self._get_coords_key(out_key_or_tensor_strides) tensor_strides = convert_to_int_tensor(in_tensor_strides, self.D) strides = convert_to_int_tensor(stride, self.D) kernel_sizes = convert_to_int_tensor(kernel_size, self.D) dilations = convert_to_int_tensor(dilation, self.D) D = in_coords_key.D tensor_strides, strides, kernel_sizes, dilations, region_type = prep_args( tensor_strides, strides, kernel_sizes, dilations, region_type, D) kernel_map_fn = self.CPPCoordsManager.getKernelMapGPU \ if on_gpu else self.CPPCoordsManager.getKernelMapGPU kernel_map = kernel_map_fn( convert_to_int_list(tensor_strides, D), # convert_to_int_list(strides, D), # convert_to_int_list(kernel_sizes, D), # convert_to_int_list(dilations, D), # region_type, region_offset, in_coords_key.CPPCoordsKey, out_coords_key.CPPCoordsKey, is_transpose, is_pool) return kernel_map
def get_kernel_map(self, in_key_or_tensor_strides, out_key_or_tensor_strides, stride=1, kernel_size=3, dilation=1, region_type=0, is_transpose=False, is_pool=False): r"""Get kernel in-out maps for the specified coords keys or tensor strides. """ if isinstance(in_key_or_tensor_strides, CoordsKey): in_tensor_strides = in_key_or_tensor_strides.getTensorStride() else: in_tensor_strides = in_key_or_tensor_strides in_coords_key = self._get_coords_key(in_key_or_tensor_strides) out_coords_key = self._get_coords_key(out_key_or_tensor_strides) tensor_strides = convert_to_int_tensor(in_tensor_strides, self.D) strides = convert_to_int_tensor(stride, self.D) kernel_sizes = convert_to_int_tensor(kernel_size, self.D) dilations = convert_to_int_tensor(dilation, self.D) D = in_coords_key.D tensor_strides, strides, kernel_sizes, dilations, region_type = prep_args( tensor_strides, strides, kernel_sizes, dilations, region_type, D) kernel_map = self.CPPCoordsManager.getKernelMap( convert_to_int_list(tensor_strides, D), # convert_to_int_list(strides, D), # convert_to_int_list(kernel_sizes, D), # convert_to_int_list(dilations, D), # region_type, in_coords_key.CPPCoordsKey, out_coords_key.CPPCoordsKey, is_transpose, is_pool) return kernel_map
def permute_feature(self, feat, tensor_stride, dtype=np.float32): tensor_stride = convert_to_int_tensor(tensor_stride, self.D) permutation = self.get_permutation(tensor_stride, 1) nrows = self.get_nrows(tensor_stride) feat_np = feat.contiguous().numpy() warped_feat = np.zeros((nrows, feat.size(1)), dtype=dtype) counter = np.zeros((nrows, 1), dtype='int32') for j in range(feat.size(1)): np.add.at(warped_feat, (permutation, j), feat_np[:, j]) np.add.at(counter, permutation, 1) warped_feat = warped_feat / counter return torch.from_numpy(warped_feat)
def permute_label(self, label, max_label, tensor_stride): if tensor_stride == 1 or np.prod(tensor_stride) == 1: return label tensor_stride = convert_to_int_tensor(tensor_stride, self.D) permutation = self.get_permutation(tensor_stride, 1) nrows = self.get_nrows(tensor_stride) label = label.contiguous().numpy() permutation = permutation.numpy() counter = np.zeros((nrows, max_label), dtype='int32') np.add.at(counter, (permutation, label), 1) return torch.from_numpy(np.argmax(counter, 1))
def get_index_map(self, coords, tensor_stride): """ Get the current coords (with duplicates) index map. If tensor_stride > 1, use coords = torch.cat(((coords[:, :D] / tensor_stride) * tensor_stride, coords[:, D:]), dim=1) """ assert isinstance(coords, torch.IntTensor), "Coord must be IntTensor" index_map = torch.IntTensor() tensor_stride = convert_to_int_tensor(tensor_stride, self.D) success = MEB.get_index_map(coords.contiguous(), index_map, tensor_stride, self.D, self.net_metadata.ffi) if success < 0: raise ValueError('get_index_map failed') return index_map
def get_kernel_map(self, in_key_or_tensor_strides, out_key_or_tensor_strides, stride=1, kernel_size=3, dilation=1, region_type=0, region_offset=None, is_transpose=False, is_pool=False, on_gpu=False): r"""Get kernel in-out maps for the specified coords keys or tensor strides. """ # region type 1 iteration with kernel_size 1 is invalid if isinstance(kernel_size, torch.Tensor): assert (kernel_size > 0).all(), f"Invalid kernel size: {kernel_size}" if (kernel_size == 1).all() == 1: region_type = 0 elif isinstance(kernel_size, int): assert kernel_size > 0, f"Invalid kernel size: {kernel_size}" if kernel_size == 1: region_type = 0 if isinstance(in_key_or_tensor_strides, CoordsKey): in_tensor_strides = in_key_or_tensor_strides.getTensorStride() else: in_tensor_strides = in_key_or_tensor_strides if region_offset is None: region_offset = torch.IntTensor() in_coords_key = self._get_coords_key(in_key_or_tensor_strides) out_coords_key = self._get_coords_key(out_key_or_tensor_strides) tensor_strides = convert_to_int_tensor(in_tensor_strides, self.D) strides = convert_to_int_tensor(stride, self.D) kernel_sizes = convert_to_int_tensor(kernel_size, self.D) dilations = convert_to_int_tensor(dilation, self.D) D = in_coords_key.D tensor_strides, strides, kernel_sizes, dilations, region_type = prep_args( tensor_strides, strides, kernel_sizes, dilations, region_type, D) if on_gpu: assert hasattr( self.CPPCoordsManager, 'getKernelMapGPU' ), f"Function getKernelMapGPU not available. Please compile MinkowskiEngine where `torch.cuda.is_available()` is `True`." kernel_map_fn = getattr(self.CPPCoordsManager, 'getKernelMapGPU') else: kernel_map_fn = self.CPPCoordsManager.getKernelMap kernel_map = kernel_map_fn( convert_to_int_list(tensor_strides, D), # convert_to_int_list(strides, D), # convert_to_int_list(kernel_sizes, D), # convert_to_int_list(dilations, D), # region_type, region_offset, in_coords_key.CPPCoordsKey, out_coords_key.CPPCoordsKey, is_transpose, is_pool) return kernel_map
def __init__(self, in_channels, kernel_size=-1, stride=1, dilation=1, has_bias=False, kernel_generator=None, dimension=-1): r"""convolution on a sparse tensor Args: :attr:`in_channels` (int): the number of input channels in the input tensor. :attr:`kernel_size` (int, optional): the size of the kernel in the output tensor. If not provided, :attr:`region_offset` should be :attr:`RegionType.CUSTOM` and :attr:`region_offset` should be a 2D matrix with size :math:`N\times D` such that it lists all :math:`N` offsets in D-dimension. :attr:`stride` (int, or list, optional): stride size of the convolution layer. If non-identity is used, the output coordinates will be at least :attr:`stride` :math:`\times` :attr:`tensor_stride` away. When a list is given, the length must be D; each element will be used for stride size for the specific axis. :attr:`dilation` (int, or list, optional): dilation size for the convolution kernel. When a list is given, the length must be D and each element is an axis specific dilation. All elements must be > 0. :attr:`has_bias` (bool, optional): if True, the convolution layer has a bias. :attr:`kernel_generator` (:attr:`MinkowskiEngine.KernelGenerator`, optional): defines the custom kernel shape. :attr:`dimension` (int): the spatial dimension of the space where all the inputs and the network are defined. For example, images are in a 2D space, meshes and 3D shapes are in a 3D space. """ super(MinkowskiChannelwiseConvolution, self).__init__() assert dimension > 0, f"dimension must be a positive integer, {dimension}" if kernel_generator is None: kernel_generator = KernelGenerator(kernel_size=kernel_size, stride=stride, dilation=dilation, dimension=dimension) else: kernel_size = kernel_generator.kernel_size stride = convert_to_int_tensor(stride, dimension) kernel_size = convert_to_int_tensor(kernel_size, dimension) dilation = convert_to_int_tensor(dilation, dimension) kernel_volume = kernel_generator.kernel_volume self.in_channels = in_channels self.kernel_size = kernel_size self.kernel_volume = kernel_volume self.stride = stride self.dilation = dilation self.kernel_generator = kernel_generator self.dimension = dimension self.use_mm = False # use matrix multiplication when kernel is 1 Tensor = torch.FloatTensor self.kernel_shape = (self.kernel_volume, self.in_channels) self.kernel = Parameter(Tensor(*self.kernel_shape)) self.bias = Parameter(Tensor(1, in_channels)) if has_bias else None self.has_bias = has_bias self.reset_parameters()