1- # Copyright (c) Chris Choy (chrischoy@ai.stanford.edu).
1+ # Copyright (c) 2020 NVIDIA CORPORATION.
2+ # Copyright (c) 2018-2020 Chris Choy (chrischoy@ai.stanford.edu).
23#
34# Permission is hereby granted, free of charge, to any person obtaining a copy of
45# this software and associated documentation files (the "Software"), to deal in
2728import torch
2829from torch .nn import Parameter
2930
30- from SparseTensor import SparseTensor
31- from Common import RegionType , MinkowskiModuleBase , KernelGenerator , \
32- prep_args , convert_to_int_list , convert_to_int_tensor
33- from MinkowskiCoords import CoordsKey
31+ from MinkowskiSparseTensor import SparseTensor
32+ from MinkowskiEngineBackend . _C import CoordinateMapKey , RegionType
33+ from MinkowskiCommon import MinkowskiModuleBase
34+ from MinkowskiKernelGenerator import KernelGenerator
3435
3536
3637class MinkowskiChannelwiseConvolution (MinkowskiModuleBase ):
38+
39+ __slots__ = (
40+ "in_channels" ,
41+ "out_channels" ,
42+ "kernel_generator" ,
43+ "dimension" ,
44+ "kernel" ,
45+ "bias" ,
46+ "conv" ,
47+ )
48+
3749 r"""Channelwise (Depthwise) Convolution layer for a sparse tensor.
3850
3951
@@ -57,14 +69,16 @@ class MinkowskiChannelwiseConvolution(MinkowskiModuleBase):
5769
5870 """
5971
60- def __init__ (self ,
61- in_channels ,
62- kernel_size = - 1 ,
63- stride = 1 ,
64- dilation = 1 ,
65- has_bias = False ,
66- kernel_generator = None ,
67- dimension = - 1 ):
72+ def __init__ (
73+ self ,
74+ in_channels ,
75+ kernel_size = - 1 ,
76+ stride = 1 ,
77+ dilation = 1 ,
78+ bias = False ,
79+ kernel_generator = None ,
80+ dimension = - 1 ,
81+ ):
6882 r"""convolution on a sparse tensor
6983
7084 Args:
@@ -87,7 +101,7 @@ def __init__(self,
87101 convolution kernel. When a list is given, the length must be D and
88102 each element is an axis specific dilation. All elements must be > 0.
89103
90- :attr:`has_bias ` (bool, optional): if True, the convolution layer
104+ :attr:`bias ` (bool, optional): if True, the convolution layer
91105 has a bias.
92106
93107 :attr:`kernel_generator` (:attr:`MinkowskiEngine.KernelGenerator`,
@@ -107,97 +121,93 @@ def __init__(self,
107121 kernel_size = kernel_size ,
108122 stride = stride ,
109123 dilation = dilation ,
110- dimension = dimension )
111- else :
112- kernel_size = kernel_generator .kernel_size
113-
114- stride = convert_to_int_tensor (stride , dimension )
115- kernel_size = convert_to_int_tensor (kernel_size , dimension )
116- dilation = convert_to_int_tensor (dilation , dimension )
124+ dimension = dimension ,
125+ )
117126
118- kernel_volume = kernel_generator . kernel_volume
127+ self . kernel_generator = kernel_generator
119128
120129 self .in_channels = in_channels
121- self .kernel_size = kernel_size
122- self .kernel_volume = kernel_volume
123- self .stride = stride
124- self .dilation = dilation
125- self .kernel_generator = kernel_generator
126130 self .dimension = dimension
127- self .use_mm = False # use matrix multiplication when kernel is 1
128131
129- Tensor = torch .FloatTensor
130- self .kernel_shape = (self .kernel_volume , self .in_channels )
132+ self .kernel_shape = (kernel_generator .kernel_volume , self .in_channels )
131133
134+ Tensor = torch .FloatTensor
132135 self .kernel = Parameter (Tensor (* self .kernel_shape ))
133- self .bias = Parameter (Tensor (1 , in_channels )) if has_bias else None
134- self . has_bias = has_bias
136+ self .bias = Parameter (Tensor (1 , in_channels )) if bias else None
137+
135138 self .reset_parameters ()
136139
137- def forward (self ,
138- input : SparseTensor ,
139- coords : Union [torch .IntTensor , CoordsKey , SparseTensor ] = None ):
140+ def forward (
141+ self ,
142+ input : SparseTensor ,
143+ coords : Union [torch .IntTensor , CoordinateMapKey , SparseTensor ] = None ,
144+ ):
140145 r"""
141146 :attr:`input` (`MinkowskiEngine.SparseTensor`): Input sparse tensor to apply a
142147 convolution on.
143148
144- :attr:`coords` ((`torch.IntTensor`, `MinkowskiEngine.CoordsKey `,
149+ :attr:`coords` ((`torch.IntTensor`, `MinkowskiEngine.CoordinateMapKey `,
145150 `MinkowskiEngine.SparseTensor`), optional): If provided, generate
146151 results on the provided coordinates. None by default.
147152
148153 """
149154 assert isinstance (input , SparseTensor )
150155 assert input .D == self .dimension
156+ assert (
157+ self .in_channels == input .shape [1 ]
158+ ), f"Channel size mismatch { self .in_channels } != { input .shape [1 ]} "
151159
152160 # Create a region_offset
153- self .region_type_ , self .region_offset_ , _ = \
154- self .kernel_generator .get_kernel (input .tensor_stride , False )
161+ region_type_ , region_offset_ , _ = self .kernel_generator .get_kernel (
162+ input .tensor_stride , False
163+ )
155164
156- cm = input .coords_man
157- in_key = input .coords_key
158- on_gpu = input .device .type != 'cpu'
165+ cm = input .coordinate_manager
166+ in_key = input .coordinate_map_key
159167
160- out_key = cm .stride (in_key , self .stride )
161- N_out = cm .get_coords_size_by_coords_key (out_key )
168+ out_key = cm .stride (in_key , self .kernel_generator . kernel_stride )
169+ N_out = cm .size (out_key )
162170 out_F = input ._F .new (N_out , self .in_channels ).zero_ ()
163171
164- in_maps , out_maps = cm .get_kernel_map (
172+ kernel_map = cm .get_kernel_map (
165173 in_key ,
166174 out_key ,
167- self .stride ,
168- self .kernel_size ,
169- self .dilation ,
170- self .region_type_ ,
171- self .region_offset_ ,
172- is_transpose = False ,
173- is_pool = False ,
174- on_gpu = on_gpu )
175-
176- for k in range (self .kernel_volume ):
177- out_F [out_maps [k ]] += input .F [in_maps [k ]] * self .kernel [k ]
178-
179- if self .has_bias :
175+ self .kernel_generator .kernel_stride ,
176+ self .kernel_generator .kernel_size ,
177+ self .kernel_generator .kernel_dilation ,
178+ region_type = region_type_ ,
179+ region_offset = region_offset_ ,
180+ )
181+
182+ for k , in_out in kernel_map .items ():
183+ in_out = in_out .long ().to (input .device )
184+ out_F [in_out [1 ]] += input .F [in_out [0 ]] * self .kernel [k ]
185+
186+ if self .bias is not None :
180187 out_F += self .bias
181188
182- return SparseTensor (out_F , coords_key = out_key , coords_manager = cm )
189+ return SparseTensor (out_F , coordinate_map_key = out_key , coordinate_manager = cm )
183190
184191 def reset_parameters (self , is_transpose = False ):
185- n = (self .out_channels
186- if is_transpose else self .in_channels ) * self .kernel_volume
187- stdv = 1. / math .sqrt (n )
188- self .kernel .data .uniform_ (- stdv , stdv )
189- if self .bias is not None :
190- self .bias .data .uniform_ (- stdv , stdv )
192+ with torch .no_grad ():
193+ n = (
194+ self .out_channels if is_transpose else self .in_channels
195+ ) * self .kernel_generator .kernel_volume
196+ stdv = 1.0 / math .sqrt (n )
197+ self .kernel .data .uniform_ (- stdv , stdv )
198+ if self .bias is not None :
199+ self .bias .data .uniform_ (- stdv , stdv )
191200
192201 def __repr__ (self ):
193- s = '(in={}, region_type={}, ' .format (self .in_channels ,
194- self .kernel_generator .region_type )
195- if self .kernel_generator .region_type in [
196- RegionType .HYBRID , RegionType .CUSTOM
197- ]:
198- s += 'kernel_volume={}, ' .format (self .kernel_volume )
202+ s = "(in={}, region_type={}, " .format (
203+ self .in_channels , self .kernel_generator .region_type
204+ )
205+ if self .kernel_generator .region_type in [RegionType .CUSTOM ]:
206+ s += "kernel_volume={}, " .format (self .kernel_generator .kernel_volume )
199207 else :
200- s += 'kernel_size={}, ' .format (self .kernel_size .tolist ())
201- s += 'stride={}, dilation={})' .format (self .stride .tolist (),
202- self .dilation .tolist ())
208+ s += "kernel_size={}, " .format (self .kernel_generator .kernel_size )
209+ s += "stride={}, dilation={})" .format (
210+ self .kernel_generator .kernel_stride ,
211+ self .kernel_generator .kernel_dilation ,
212+ )
203213 return self .__class__ .__name__ + s
0 commit comments