test_conv.py 33.3 KB
Newer Older
yan.yan's avatar
yan.yan committed
1
# Copyright 2021 Yan Yan
2
#
traveller59's avatar
traveller59 committed
3
4
5
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
6
#
traveller59's avatar
traveller59 committed
7
#     http://www.apache.org/licenses/LICENSE-2.0
8
#
traveller59's avatar
traveller59 committed
9
10
11
12
13
14
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

15
16
import time
import unittest
traveller59's avatar
traveller59 committed
17
from pathlib import Path
18
19

import numpy as np
traveller59's avatar
traveller59 committed
20
import torch
21
from torch import nn
yan.yan's avatar
v2.1  
yan.yan committed
22
from spconv.core import ConvAlgo
23

yan.yan's avatar
yan.yan committed
24
import spconv.pytorch as spconv
25
from spconv.test_utils import TestCase, generate_sparse_data, params_grid
yan.yan's avatar
yan.yan committed
26
from spconv.constants import FILTER_HWIO
27
28
# import sparseconvnet as scn

yan.yan's avatar
v2.1  
yan.yan committed
29
30
31
# we must disable tf32 to increase reference precision.
torch.backends.cuda.matmul.allow_tf32 = False
torch.backends.cudnn.allow_tf32 = False
traveller59's avatar
traveller59 committed
32

33

traveller59's avatar
traveller59 committed
34
class SparseConv3dTestTorch(nn.Module):
yanyan's avatar
yanyan committed
35
36
37
38
39
40
41
42
43
44
    def __init__(self,
                 num_layers,
                 ndim,
                 shape,
                 in_channels,
                 out_channels,
                 kernel_size,
                 stride,
                 padding,
                 dilation,
yan.yan's avatar
v2.1  
yan.yan committed
45
                 algo=spconv.ConvAlgo.MaskSplitImplicitGemm):
traveller59's avatar
traveller59 committed
46
        super().__init__()
yan.yan's avatar
v2.1  
yan.yan committed
47
        self.algo = algo
48
49
50
51
52
53
54
55
        layers = [
            spconv.SparseConv3d(in_channels,
                                out_channels,
                                kernel_size,
                                stride,
                                padding=padding,
                                dilation=dilation,
                                bias=False,
Yan Yan's avatar
Yan Yan committed
56
                                algo=algo)
57
        ]
traveller59's avatar
traveller59 committed
58
        for i in range(1, num_layers):
59
60
61
62
63
64
65
            layers.append(
                spconv.SparseConv3d(out_channels,
                                    out_channels,
                                    kernel_size,
                                    stride,
                                    padding=padding,
                                    dilation=dilation,
66
                                    bias=False,
Yan Yan's avatar
Yan Yan committed
67
                                    algo=algo))
68
        self.net = spconv.SparseSequential(*layers, )
traveller59's avatar
traveller59 committed
69
70
71
72
73
74
        # self.grid = torch.full([3, *shape], -1, dtype=torch.int32).cuda()
        self.grid = None
        self.shape = shape

    def forward(self, features, coors, batch_size):
        coors = coors.int()
75
76
77
78
        x = spconv.SparseConvTensor(features, coors, self.shape, batch_size,
                                    self.grid)
        return self.net(x)  # .dense()

traveller59's avatar
traveller59 committed
79
80

class SubMConv3dTestTorch(nn.Module):
yanyan's avatar
yanyan committed
81
82
83
84
85
86
87
88
89
90
91
    def __init__(self,
                 num_layers,
                 ndim,
                 shape,
                 in_channels,
                 out_channels,
                 kernel_size,
                 stride,
                 padding,
                 dilation,
                 algo=spconv.ConvAlgo.Native):
traveller59's avatar
traveller59 committed
92
        super().__init__()
93
94
95
96
97
98
99
        layers = [
            spconv.SubMConv3d(in_channels,
                              out_channels,
                              kernel_size,
                              stride,
                              padding=padding,
                              dilation=dilation,
Yan Yan's avatar
Yan Yan committed
100
101
                              bias=False,
                              algo=algo)
102
        ]
traveller59's avatar
traveller59 committed
103
        for i in range(1, num_layers):
104
105
106
107
108
109
110
            layers.append(
                spconv.SubMConv3d(out_channels,
                                  out_channels,
                                  kernel_size,
                                  stride,
                                  padding=padding,
                                  dilation=dilation,
Yan Yan's avatar
Yan Yan committed
111
                                  bias=False,
yanyan's avatar
yanyan committed
112
                                  algo=algo))
113
        self.net = spconv.SparseSequential(*layers, )
114
115
        # self.grid = torch.full([3, *shape], -1, dtype=torch.int32).cuda()
        self.grid = None
traveller59's avatar
traveller59 committed
116
117
118
        self.shape = shape

    def forward(self, features, coors, batch_size):
yanyan's avatar
yanyan committed
119
        coors = coors.int()  # .cpu()
120
121
122
        x = spconv.SparseConvTensor(features, coors, self.shape, batch_size,
                                    self.grid)
        return self.net(x)  # .dense()
traveller59's avatar
traveller59 committed
123
124
125


class Conv3dTestTorch(nn.Module):
126
127
    def __init__(self, num_layers, ndim, shape, in_channels, out_channels,
                 kernel_size, stride, padding, dilation):
traveller59's avatar
traveller59 committed
128
        super().__init__()
129
130
131
132
133
134
135
136
137
        layers = [
            nn.Conv3d(in_channels,
                      out_channels,
                      kernel_size,
                      stride,
                      padding=padding,
                      dilation=dilation,
                      bias=False)
        ]
traveller59's avatar
traveller59 committed
138
        for i in range(1, num_layers):
139
140
141
142
143
144
145
146
147
            layers.append(
                nn.Conv3d(out_channels,
                          out_channels,
                          kernel_size,
                          stride,
                          padding=padding,
                          dilation=dilation,
                          bias=False))
        self.net = nn.Sequential(*layers, )
traveller59's avatar
traveller59 committed
148
149
150
        self.shape = shape

    def forward(self, x):
151
        return self.net(x)  # .dense()
traveller59's avatar
traveller59 committed
152
153
154


class SparseDeConv3dTestTorch(nn.Module):
155
156
    def __init__(self, num_layers, ndim, shape, in_channels, out_channels,
                 kernel_size, stride, padding, dilation):
traveller59's avatar
traveller59 committed
157
        super().__init__()
158
159
160
161
162
163
164
165
166
        layers = [
            spconv.SparseConvTranspose3d(in_channels,
                                         out_channels,
                                         kernel_size,
                                         stride,
                                         padding=padding,
                                         dilation=dilation,
                                         bias=False)
        ]
traveller59's avatar
traveller59 committed
167
        for i in range(1, num_layers):
168
169
170
171
172
173
174
175
176
            layers.append(
                spconv.SparseConvTranspose3d(out_channels,
                                             out_channels,
                                             kernel_size,
                                             stride,
                                             padding=padding,
                                             dilation=dilation,
                                             bias=False))
        self.net = spconv.SparseSequential(*layers, )
traveller59's avatar
traveller59 committed
177
178
179
180
        self.shape = shape

    def forward(self, features, coors, batch_size):
        coors = coors.int()
181
182
183
        x = spconv.SparseConvTensor(features, coors, self.shape, batch_size)
        return self.net(x)  # .dense()

traveller59's avatar
traveller59 committed
184
185

class DeConv3dTestTorch(nn.Module):
186
187
    def __init__(self, num_layers, ndim, shape, in_channels, out_channels,
                 kernel_size, stride, padding, dilation):
traveller59's avatar
traveller59 committed
188
        super().__init__()
189
190
191
192
193
194
195
196
197
        layers = [
            nn.ConvTranspose3d(in_channels,
                               out_channels,
                               kernel_size,
                               stride,
                               padding=padding,
                               dilation=dilation,
                               bias=False)
        ]
traveller59's avatar
traveller59 committed
198
        for i in range(1, num_layers):
199
200
201
202
203
204
205
206
207
            layers.append(
                nn.ConvTranspose3d(out_channels,
                                   out_channels,
                                   kernel_size,
                                   stride,
                                   padding=padding,
                                   dilation=dilation,
                                   bias=False))
        self.net = nn.Sequential(*layers, )
traveller59's avatar
traveller59 committed
208
209
210
        self.shape = shape

    def forward(self, x):
211
        return self.net(x)  # .dense()
traveller59's avatar
traveller59 committed
212
213
214


class SparseMaxPoolTestTorch(nn.Module):
215
216
    def __init__(self, num_layers, ndim, shape, kernel_size, stride, padding,
                 dilation):
traveller59's avatar
traveller59 committed
217
        super().__init__()
218
219
220
        layers = [
            spconv.SparseMaxPool3d(kernel_size, stride, padding, dilation)
        ]
traveller59's avatar
traveller59 committed
221
        for i in range(1, num_layers):
222
223
224
            layers.append(
                spconv.SparseMaxPool3d(kernel_size, stride, padding, dilation))
        self.net = spconv.SparseSequential(*layers, )
traveller59's avatar
traveller59 committed
225
226
227
228
        self.shape = shape

    def forward(self, features, coors, batch_size):
        coors = coors.int()
229
230
231
        x = spconv.SparseConvTensor(features, coors, self.shape, batch_size)
        return self.net(x)  # .dense()

traveller59's avatar
traveller59 committed
232
233

class MaxPool3dTestTorch(nn.Module):
234
235
    def __init__(self, num_layers, ndim, shape, kernel_size, stride, padding,
                 dilation):
traveller59's avatar
traveller59 committed
236
        super().__init__()
237
        layers = [nn.MaxPool3d(kernel_size, stride, padding, dilation)]
traveller59's avatar
traveller59 committed
238
        for i in range(1, num_layers):
239
240
            layers.append(nn.MaxPool3d(kernel_size, stride, padding, dilation))
        self.net = nn.Sequential(*layers, )
traveller59's avatar
traveller59 committed
241
242
243
        self.shape = shape

    def forward(self, x):
244
        return self.net(x)  # .dense()
traveller59's avatar
traveller59 committed
245
246
247


class SubmanifoldConvTestTorch(nn.Module):
248
249
    def __init__(self, num_layers, ndim, shape, in_channels, out_channels,
                 kernel_size, stride):
traveller59's avatar
traveller59 committed
250
        super().__init__()
251
252
253
254
255
256
257
        layers = [
            spconv.SubMConv3d(in_channels,
                              out_channels,
                              kernel_size,
                              bias=False,
                              indice_key="subm0")
        ]
traveller59's avatar
traveller59 committed
258
        for i in range(1, num_layers):
259
260
261
262
263
264
            layers.append(
                spconv.SubMConv3d(out_channels,
                                  out_channels,
                                  kernel_size,
                                  bias=False))
        self.net = nn.Sequential(*layers, )
traveller59's avatar
traveller59 committed
265
266
267
268
        self.shape = shape

    def forward(self, features, coors, batch_size):
        coors = coors.int()
269
        x = spconv.SparseConvTensor(features, coors, self.shape, batch_size)
traveller59's avatar
traveller59 committed
270
271
        return self.net(x)

272

traveller59's avatar
traveller59 committed
273
class SCNCoupleDeConvTest(nn.Module):
274
275
    def __init__(self, num_layers, ndim, shape, in_channels, out_channels,
                 kernel_size, stride):
traveller59's avatar
traveller59 committed
276
277
278
        super().__init__()
        self.scn_input = scn.InputLayer(ndim, shape, mode=0)
        self.net = nn.Sequential(
279
280
281
282
283
284
285
286
287
288
289
290
            scn.Convolution(ndim,
                            in_channels,
                            out_channels,
                            kernel_size,
                            stride,
                            bias=False),
            scn.Deconvolution(ndim,
                              out_channels,
                              in_channels,
                              kernel_size,
                              stride,
                              bias=False),
traveller59's avatar
traveller59 committed
291
292
293
294
295
296
297
298
            scn.SparseToDense(ndim, in_channels),
        )

    def forward(self, features, coors, batch_size):
        coors = coors.long().cpu()
        x = self.scn_input((coors, features))
        return self.net(x)

299

traveller59's avatar
traveller59 committed
300
class SparseCoupleDeConvTest(nn.Module):
301
302
    def __init__(self, num_layers, ndim, shape, in_channels, out_channels,
                 kernel_size, stride):
traveller59's avatar
traveller59 committed
303
304
        super().__init__()
        self.net = spconv.SparseSequential(
305
306
307
308
309
310
311
312
313
314
315
            spconv.SparseConv3d(in_channels,
                                out_channels,
                                kernel_size,
                                stride,
                                indice_key="cp0",
                                bias=False),
            spconv.SparseInverseConv3d(out_channels,
                                       in_channels,
                                       kernel_size,
                                       indice_key="cp0",
                                       bias=False),
traveller59's avatar
traveller59 committed
316
317
318
319
320
321
        )
        self.todense = spconv.ToDense()
        self.shape = shape

    def forward(self, features, coors, batch_size):
        coors = coors.int()
322
323
        x = spconv.SparseConvTensor(features, coors, self.shape, batch_size)
        return self.todense(self.net(x))  # .dense()
traveller59's avatar
traveller59 committed
324
325
326
327
328


def gather_nd(params, indices):
    # this function has a limit that MAX_ADVINDEX_CALC_DIMS=5
    ndim = indices.shape[-1]
329
330
    output_shape = list(indices.shape[:-1]) + list(
        params.shape[indices.shape[-1]:])
traveller59's avatar
traveller59 committed
331
332
333
334
335
    flatted_indices = indices.view(-1, ndim)
    slices = [flatted_indices[:, i] for i in range(ndim)]
    slices += [Ellipsis]
    return params[slices].view(*output_shape)

336

traveller59's avatar
traveller59 committed
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
def scatter_nd(indices, updates, shape):
    """pytorch edition of tensorflow scatter_nd.
    this function don't contain except handle code. so use this carefully
    when indice repeats, don't support repeat add which is supported
    in tensorflow.
    """
    ret = torch.zeros(*shape, dtype=updates.dtype, device=updates.device)
    ndim = indices.shape[-1]
    output_shape = list(indices.shape[:-1]) + shape[indices.shape[-1]:]
    flatted_indices = indices.view(-1, ndim)
    slices = [flatted_indices[:, i] for i in range(ndim)]
    slices += [Ellipsis]
    ret[slices] = updates.view(*output_shape)
    return ret


class TestSpConv(TestCase):
    def testSpConv3d(self):
yan.yan's avatar
sync  
yan.yan committed
355
356
        np.random.seed(71)
        torch.manual_seed(705)
yanyan's avatar
yanyan committed
357
        devices = ["cuda:0"]
yan.yan's avatar
sync  
yan.yan committed
358
        shapes = [[4, 4, 4]]
traveller59's avatar
traveller59 committed
359
360
        batchsizes = [1, 2]

yan.yan's avatar
sync  
yan.yan committed
361
        in_channels = [4]
traveller59's avatar
traveller59 committed
362
363
364
365
366
        out_channels = [32, 48, 64]
        ksizes = [2, 3]
        strides = [1, 2, 3]
        paddings = [0, 1, 2]
        dilations = [1, 2, 3]
yan.yan's avatar
sync  
yan.yan committed
367
368
369
370
371
        ksizes = [3]
        strides = [1]
        paddings = [0]
        dilations = [1]

372
        algos = [
yan.yan's avatar
sync  
yan.yan committed
373
374
            ConvAlgo.MaskImplicitGemm,
            # ConvAlgo.MaskSplitImplicitGemm
375
        ]
yan.yan's avatar
sync  
yan.yan committed
376
        # algos = [ConvAlgo.MaskSplitImplicitGemm]
traveller59's avatar
traveller59 committed
377

yan.yan's avatar
v2.1  
yan.yan committed
378
        for dev, shape, bs, IC, OC, k, s, p, d, al in params_grid(
379
                devices, shapes, batchsizes, in_channels, out_channels, ksizes,
yan.yan's avatar
v2.1  
yan.yan committed
380
                strides, paddings, dilations, algos):
traveller59's avatar
traveller59 committed
381
            if all([s > 1, d > 1]):
382
                continue  # don't support this.
traveller59's avatar
traveller59 committed
383
            device = torch.device(dev)
yan.yan's avatar
sync  
yan.yan committed
384
            num_points = [10] * bs
yan.yan's avatar
v2.1  
yan.yan committed
385
            dtype = torch.float32
386
387
388
389
390
391
392
393
394
395
            net = SparseConv3dTestTorch(1,
                                        3,
                                        shape,
                                        IC,
                                        OC,
                                        k,
                                        s,
                                        p,
                                        d,
                                        algo=al).to(device).to(dtype)
yan.yan's avatar
v2.1  
yan.yan committed
396
397
398
            net_ref = Conv3dTestTorch(1, 3, shape, IC, OC, k, s, p,
                                      d).to(device).to(dtype)

traveller59's avatar
traveller59 committed
399
400
            sparse_dict = generate_sparse_data(shape, num_points, IC)

401
402
403
404
            features = np.ascontiguousarray(sparse_dict["features"]).astype(
                np.float32)
            indices = np.ascontiguousarray(
                sparse_dict["indices"][:, [3, 0, 1, 2]]).astype(np.int32)
yan.yan's avatar
sync  
yan.yan committed
405
406
407
            # print(k, s, p, d, features.mean(), indices.mean())
            # if k == 2 and s == 2 and p == 0 and d == 1:
            #     breakpoint()
traveller59's avatar
traveller59 committed
408
409
            features_dense = sparse_dict["features_dense"].astype(np.float32)
            indices_t = torch.from_numpy(indices).int().to(device)
yan.yan's avatar
bug fix  
yan.yan committed
410
            features_t = torch.from_numpy(features).to(device).to(dtype)
traveller59's avatar
traveller59 committed
411
            features_t.requires_grad = True
412
413
            features_dense_t = torch.from_numpy(features_dense).to(device).to(
                dtype)
traveller59's avatar
traveller59 committed
414
            features_dense_t.requires_grad = True
yan.yan's avatar
v2.1  
yan.yan committed
415
416
            if net.algo == ConvAlgo.Native:
                if FILTER_HWIO:
417
418
419
                    filters = np.random.uniform(-1, 1,
                                                size=[k, k, k, IC,
                                                      OC]).astype(np.float32)
yan.yan's avatar
v2.1  
yan.yan committed
420
                else:
421
422
423
                    filters = np.random.uniform(-1, 1,
                                                size=[k, k, k, OC,
                                                      IC]).astype(np.float32)
yan.yan's avatar
v2.1  
yan.yan committed
424
425
                filters_t = torch.from_numpy(filters).to(device).to(dtype)
                if FILTER_HWIO:
426
427
                    net_ref.net[0].weight.data[:] = filters_t.permute(
                        4, 3, 0, 1, 2).contiguous()
yan.yan's avatar
v2.1  
yan.yan committed
428
                else:
429
430
                    net_ref.net[0].weight.data[:] = filters_t.permute(
                        3, 4, 0, 1, 2).contiguous()
yan.yan's avatar
yan.yan committed
431
            else:
432
433
434
                filters = np.random.uniform(-1, 1,
                                            size=[OC, k, k, k,
                                                  IC]).astype(np.float32)
yan.yan's avatar
v2.1  
yan.yan committed
435
                filters_t = torch.from_numpy(filters).to(device).to(dtype)
436
437
                net_ref.net[0].weight.data[:] = filters_t.permute(
                    0, 4, 1, 2, 3).contiguous()
yan.yan's avatar
v2.1  
yan.yan committed
438

traveller59's avatar
traveller59 committed
439
440
            net.net[0].weight.data[:] = filters_t
            out_ref = net_ref(features_dense_t)
441
            out = net(features_t, indices_t, bs).dense()
yan.yan's avatar
yan.yan committed
442
443
444
445
            out_np = out.detach().cpu().numpy()
            out_ref_np = out_ref.detach().cpu().numpy()
            self.assertAllClose(out_np, out_ref_np, atol=1e-4)

446
447
            dout = np.random.uniform(-0.2, 0.2,
                                     out_ref.shape).astype(features.dtype)
traveller59's avatar
traveller59 committed
448
449
450
            dout_t = torch.from_numpy(dout).to(device)
            out.backward(dout_t)
            out_ref.backward(dout_t)
451
452
            din_dense = features_dense_t.grad.detach().permute(0, 2, 3, 4,
                                                               1).contiguous()
traveller59's avatar
traveller59 committed
453
454
            din_sparse = gather_nd(din_dense, indices_t.long())
            din = features_t.grad.detach()
yan.yan's avatar
yan.yan committed
455

traveller59's avatar
traveller59 committed
456
457
458
459
460
            din_np = din.cpu().numpy()
            din_sparse_np = din_sparse.cpu().numpy()
            for layer, layer_ref in zip(net.net, net_ref.net):
                dw = layer.weight.grad.detach().cpu().numpy()
                dw_ref = layer_ref.weight.grad.detach().cpu().numpy()
yan.yan's avatar
v2.1  
yan.yan committed
461
462
463
464
465
                if net.algo == ConvAlgo.Native:
                    if FILTER_HWIO:
                        dw = dw.transpose(4, 3, 0, 1, 2)
                    else:
                        dw = dw.transpose(3, 4, 0, 1, 2)
yan.yan's avatar
yan.yan committed
466
                else:
yan.yan's avatar
v2.1  
yan.yan committed
467
468
                    # OHWI -> OIHW
                    dw = dw.transpose(0, 4, 1, 2, 3)
yan.yan's avatar
yan.yan committed
469

traveller59's avatar
traveller59 committed
470
                self.assertAllClose(dw, dw_ref, atol=1e-4)
yan.yan's avatar
yan.yan committed
471
            self.assertAllClose(din_np, din_sparse_np, atol=1e-4)
traveller59's avatar
traveller59 committed
472
473
474

    def testSpDeConv3d(self):
        np.random.seed(484)
Yan Yan's avatar
Yan Yan committed
475
        devices = ["cuda:0"]
traveller59's avatar
traveller59 committed
476
477
478
479
480
481
482
483
484
        shapes = [[19, 18, 17]]
        batchsizes = [1, 2]

        in_channels = [64]
        out_channels = [32, 48, 64]
        ksizes = [2, 3]
        strides = [2, 3]
        paddings = [0, 1, 2]
        dilations = [1, 2, 3]
yan.yan's avatar
bug fix  
yan.yan committed
485
486
487
488
489
        ksizes = [3]

        strides = [1]
        paddings = [0]
        dilations = [1]
traveller59's avatar
traveller59 committed
490
491

        for dev, shape, bs, IC, OC, k, s, p, d in params_grid(
492
493
                devices, shapes, batchsizes, in_channels, out_channels, ksizes,
                strides, paddings, dilations):
traveller59's avatar
traveller59 committed
494
            if all([s > 1, d > 1]):
495
                continue  # don't support this.
traveller59's avatar
traveller59 committed
496
497
498
499
500
            device = torch.device(dev)
            num_points = [1000] * bs

            sparse_dict = generate_sparse_data(shape, num_points, IC)

501
502
503
504
            features = np.ascontiguousarray(sparse_dict["features"]).astype(
                np.float32)
            indices = np.ascontiguousarray(
                sparse_dict["indices"][:, [3, 0, 1, 2]]).astype(np.int32)
traveller59's avatar
traveller59 committed
505
            features_dense = sparse_dict["features_dense"].astype(np.float32)
yan.yan's avatar
bug fix  
yan.yan committed
506
507
508
509
510
511
512
            if FILTER_HWIO:
                filters = np.random.uniform(0, 1, size=[k, k, k, IC,
                                                        OC]).astype(np.float32)
            else:
                filters = np.random.uniform(0, 1, size=[k, k, k, OC,
                                                        IC]).astype(np.float32)

traveller59's avatar
traveller59 committed
513
514
515
516
517
            indices_t = torch.from_numpy(indices).int().to(device)
            features_t = torch.from_numpy(features).to(device)
            features_t.requires_grad = True
            features_dense_t = torch.from_numpy(features_dense).to(device)
            features_dense_t.requires_grad = True
518
519
520
521
            net = SparseDeConv3dTestTorch(1, 3, shape, IC, OC, k, s, p,
                                          d).to(device)
            net_ref = DeConv3dTestTorch(1, 3, shape, IC, OC, k, s, p,
                                        d).to(device)
traveller59's avatar
traveller59 committed
522
            filters_t = torch.from_numpy(filters).to(device)
yan.yan's avatar
bug fix  
yan.yan committed
523
524
            print(net_ref.net[0].weight.shape)
            if FILTER_HWIO:
525
526
                net_ref.net[0].weight.data[:] = filters_t.permute(
                    3, 4, 0, 1, 2).contiguous()
yan.yan's avatar
bug fix  
yan.yan committed
527
            else:
528
529
                net_ref.net[0].weight.data[:] = filters_t.permute(
                    4, 3, 0, 1, 2).contiguous()
traveller59's avatar
traveller59 committed
530
531
            net.net[0].weight.data[:] = filters_t
            out_ref = net_ref(features_dense_t)
532
            out = net(features_t, indices_t, bs).dense()
yan.yan's avatar
bug fix  
yan.yan committed
533
534
535
536
            out_np = out.detach().cpu().numpy()
            out_ref_np = out_ref.detach().cpu().numpy()
            self.assertAllClose(out_np, out_ref_np, atol=1e-4)

537
538
            dout = np.random.uniform(-0.2, 0.2,
                                     out_ref.shape).astype(features.dtype)
traveller59's avatar
traveller59 committed
539
540
541
            dout_t = torch.from_numpy(dout).to(device)
            out.backward(dout_t)
            out_ref.backward(dout_t)
542
543
            din_dense = features_dense_t.grad.detach().permute(0, 2, 3, 4,
                                                               1).contiguous()
traveller59's avatar
traveller59 committed
544
545
546
547
548
549
550
551
            din_sparse = gather_nd(din_dense, indices_t.long())
            din = features_t.grad.detach()
            din_np = din.cpu().numpy()
            din_sparse_np = din_sparse.cpu().numpy()
            self.assertAllClose(din_np, din_sparse_np, atol=1e-4)
            for layer, layer_ref in zip(net.net, net_ref.net):
                dw = layer.weight.grad.detach().cpu().numpy()
                dw_ref = layer_ref.weight.grad.detach().cpu().numpy()
yan.yan's avatar
bug fix  
yan.yan committed
552
553
554
555
                if FILTER_HWIO:
                    dw = dw.transpose(3, 4, 0, 1, 2)
                else:
                    dw = dw.transpose(4, 3, 0, 1, 2)
traveller59's avatar
traveller59 committed
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
                self.assertAllClose(dw, dw_ref, atol=1e-4)

    def testSpCpConv3d(self):
        np.random.seed(484)
        devices = ["cuda:0", "cpu:0"]
        shapes = [[20, 20, 20]]
        batchsizes = [1, 2]

        in_channels = [64]
        out_channels = [32, 48, 64]
        ksizes = [2]
        strides = [2]
        paddings = [0, 1, 2]
        dilations = [1, 2, 3]

        for dev, shape, bs, IC, OC, k, s in params_grid(
572
573
                devices, shapes, batchsizes, in_channels, out_channels, ksizes,
                strides):
traveller59's avatar
traveller59 committed
574
575
576
577
578
            device = torch.device(dev)
            num_points = [1000] * bs

            sparse_dict = generate_sparse_data(shape, num_points, IC)

579
580
581
582
            features = np.ascontiguousarray(sparse_dict["features"]).astype(
                np.float32)
            indices = np.ascontiguousarray(
                sparse_dict["indices"][:, [3, 0, 1, 2]]).astype(np.int32)
traveller59's avatar
traveller59 committed
583
            features_dense = sparse_dict["features_dense"].astype(np.float32)
584
585
            filters = np.random.uniform(0, 1, size=[k, k, k, IC,
                                                    OC]).astype(np.float32)
traveller59's avatar
traveller59 committed
586
            indices_t = torch.from_numpy(indices).int().to(device)
587
588
            indices_scn_t = torch.from_numpy(
                indices[:, [1, 2, 3, 0]]).int().to(device)
traveller59's avatar
traveller59 committed
589
590
591
592
593
594
595
            features_t = torch.from_numpy(features).to(device)
            features_t.requires_grad = True
            features_ref_t = torch.from_numpy(features).to(device)
            features_ref_t.requires_grad = True

            net_ref = SCNCoupleDeConvTest(1, 3, shape, IC, OC, k, s).to(device)
            net = SparseCoupleDeConvTest(1, 3, shape, IC, OC, k, s).to(device)
596
597
598
599
            net_ref.net[0].weight.data[:] = net.net[0].weight.data[:].view(
                *net_ref.net[0].weight.shape)
            net_ref.net[1].weight.data[:] = net.net[1].weight.data[:].view(
                *net_ref.net[1].weight.shape)
traveller59's avatar
traveller59 committed
600
601
            out_ref = net_ref(features_ref_t, indices_scn_t, bs)
            out = net(features_t, indices_t, bs)
602
603
            dout = np.random.uniform(-0.2, 0.2,
                                     out_ref.shape).astype(features.dtype)
traveller59's avatar
traveller59 committed
604
605
606
607
608
609
610
611
612
613
            dout_t = torch.from_numpy(dout).to(device)
            out.backward(dout_t)
            out_ref.backward(dout_t)
            din = features_t.grad.detach()
            din_ref = features_ref_t.grad.detach()
            din_np = din.cpu().numpy()
            din_ref_np = din_ref.cpu().numpy()
            self.assertAllClose(din_ref_np, din_np, atol=1e-4)
            for layer, layer_ref in zip(net.net, net_ref.net):
                dw = layer.weight.grad.detach().cpu().numpy()
614
615
                dw_ref = layer_ref.weight.grad.detach().cpu().view(
                    *dw.shape).numpy()
traveller59's avatar
traveller59 committed
616
617
618
619
620
621
622
                self.assertAllClose(dw, dw_ref, atol=1e-4)

            out_np = out.detach().cpu().numpy()
            out_ref_np = out_ref.detach().cpu().numpy()
            self.assertAllClose(out_np, out_ref_np, atol=1e-4)

    def testSpMaxPool3d(self):
623
        np.random.seed(485)
624
        devices = ["cuda:0"]
traveller59's avatar
traveller59 committed
625
626
627
        shapes = [[19, 18, 17]]
        batchsizes = [1, 2]

yan.yan's avatar
yan.yan committed
628
629
        in_channels = [64]
        out_channels = [64]
traveller59's avatar
traveller59 committed
630
631
632
633
        ksizes = [2, 3]
        strides = [1, 2, 3]
        paddings = [0, 1]
        dilations = [1, 2, 3]
yan.yan's avatar
v2.1  
yan.yan committed
634
635
636
637
        # ksizes = [2]
        # strides = [2]
        # paddings = [0]
        # dilations = [1]
traveller59's avatar
traveller59 committed
638
639

        for dev, shape, bs, IC, OC, k, s, p, d in params_grid(
640
641
                devices, shapes, batchsizes, in_channels, out_channels, ksizes,
                strides, paddings, dilations):
traveller59's avatar
traveller59 committed
642
            if all([s > 1, d > 1]):
643
                continue  # don't support this.
traveller59's avatar
traveller59 committed
644
645
            device = torch.device(dev)
            num_points = [1000] * bs
yan.yan's avatar
yan.yan committed
646

traveller59's avatar
traveller59 committed
647
            # when data contains negative, sparse maxpool is not equal to dense maxpool.
648
649
650
651
652
653
654
655
656
            sparse_dict = generate_sparse_data(shape,
                                               num_points,
                                               IC,
                                               data_range=[0.1, 1])

            features = np.ascontiguousarray(sparse_dict["features"]).astype(
                np.float32)
            indices = np.ascontiguousarray(
                sparse_dict["indices"][:, [3, 0, 1, 2]]).astype(np.int32)
traveller59's avatar
traveller59 committed
657
            features_dense = sparse_dict["features_dense"].astype(np.float32)
yan.yan's avatar
yan.yan committed
658
659
            filters = np.random.uniform(0, 1, size=[k, k, k, OC,
                                                    IC]).astype(np.float32)
traveller59's avatar
traveller59 committed
660
661
662
663
664
665
666
667
668
669
            indices_t = torch.from_numpy(indices).int().to(device)
            features_t = torch.from_numpy(features).to(device)
            features_t.requires_grad = True
            features_dense_t = torch.from_numpy(features_dense).to(device)
            features_dense_t.requires_grad = True
            net = SparseMaxPoolTestTorch(1, 3, shape, k, s, p, d).to(device)
            net_ref = MaxPool3dTestTorch(1, 3, shape, k, s, p, d).to(device)

            out_ref = net_ref(features_dense_t)
            out = net(features_t, indices_t, bs)
yan.yan's avatar
yan.yan committed
670

traveller59's avatar
traveller59 committed
671
672
            outids = out.indices
            outfeatures = out.features
673
            outids_dev = outids.float()
674
            out_dense = out.dense(channels_first=False)
traveller59's avatar
traveller59 committed
675
            out = out_dense.permute(0, 4, 1, 2, 3).contiguous()
yan.yan's avatar
yan.yan committed
676
677
678
            out_np = out.detach().cpu().numpy()
            out_ref_np = out_ref.detach().cpu().numpy()
            self.assertAllClose(out_np, out_ref_np, atol=1e-4)
679
680
681

            dout_sparse = np.random.uniform(
                -0.2, 0.2, outfeatures.shape).astype(features.dtype)
traveller59's avatar
traveller59 committed
682
            dout_sparse_t = torch.from_numpy(dout_sparse).to(device)
683
684
            dout_t = scatter_nd(outids.long(), dout_sparse_t,
                                list(out_dense.shape))
traveller59's avatar
traveller59 committed
685
686
687
            dout_t = dout_t.permute(0, 4, 1, 2, 3).contiguous()
            out.backward(dout_t)
            out_ref.backward(dout_t)
688
689
            din_dense = features_dense_t.grad.detach().permute(0, 2, 3, 4,
                                                               1).contiguous()
traveller59's avatar
traveller59 committed
690
691
            din_sparse = gather_nd(din_dense, indices_t.long())
            din = features_t.grad.detach()
692

693
694
695
            din_np = din.cpu().numpy()
            din_sparse_np = din_sparse.cpu().numpy()
            self.assertAllClose(din_np, din_sparse_np, atol=1e-4)
696

traveller59's avatar
traveller59 committed
697

yanyan's avatar
yanyan committed
698
def main(algo=spconv.ConvAlgo.Native, dtype=torch.float32):
traveller59's avatar
traveller59 committed
699
700
    # function for develop.
    np.random.seed(484)
traveller59's avatar
traveller59 committed
701
    # devices = ["cuda:0"]
traveller59's avatar
traveller59 committed
702
    devices = ["cuda:0"]
yanyan's avatar
yanyan committed
703
    shapes = [[400, 400, 15]]
yanyan's avatar
yanyan committed
704
    batchsizes = [2]
traveller59's avatar
traveller59 committed
705

yanyan's avatar
yanyan committed
706
707
    in_channels = [19]
    out_channels = [17]
Yan Yan's avatar
Yan Yan committed
708
    ksizes = [(3, 3, 3)]
traveller59's avatar
traveller59 committed
709
710
711
712
713
    strides = [1]
    paddings = [0]
    dilations = [1]

    for dev, shape, bs, IC, OC, k, s, p, d in params_grid(
714
715
            devices, shapes, batchsizes, in_channels, out_channels, ksizes,
            strides, paddings, dilations):
traveller59's avatar
traveller59 committed
716
717
718
        if all([s > 1, d > 1]):
            continue
        device = torch.device(dev)
yanyan's avatar
yanyan committed
719
        num_points = [30000] * bs
traveller59's avatar
traveller59 committed
720
721
722

        sparse_dict = generate_sparse_data(shape, num_points, IC)

723
724
725
726
        features = np.ascontiguousarray(sparse_dict["features"]).astype(
            np.float32)
        indices = np.ascontiguousarray(
            sparse_dict["indices"][:, [3, 0, 1, 2]]).astype(np.int32)
traveller59's avatar
traveller59 committed
727
728
        features_dense = sparse_dict["features_dense"].astype(np.float32)
        indices_t = torch.from_numpy(indices)
729
730
        filters = np.random.uniform(0, 1, size=[k[0], 1, 1, IC,
                                                OC]).astype(np.float32)
yanyan's avatar
yanyan committed
731
732
        indices_t = torch.from_numpy(indices).int().to(device).to(dtype)
        features_t = torch.from_numpy(features).to(device).to(dtype)
733

yanyan's avatar
yanyan committed
734
735
        features_dense_t = torch.from_numpy(features_dense).to(device).to(
            dtype)
yanyan's avatar
yanyan committed
736
        net = SparseConv3dTestTorch(1, 3, shape, IC, OC, k, s, p, d,
yanyan's avatar
yanyan committed
737
                                    algo=algo).to(device).to(dtype)
738
        net_ref = Conv3dTestTorch(1, 3, shape, IC, OC, k, s, p,
yanyan's avatar
yanyan committed
739
740
                                  d).to(device).to(dtype)
        filters_t = torch.from_numpy(filters).to(device).to(dtype)
741
742
        net_ref.net[0].weight[:] = filters_t.permute(4, 3, 0, 1,
                                                     2).contiguous()
traveller59's avatar
traveller59 committed
743
744
745
        net.net[0].weight[:] = filters_t
        out_ref = net_ref(features_dense_t)
        times = []
tusimple's avatar
tusimple committed
746
        for i in range(10):
traveller59's avatar
traveller59 committed
747
748
749
750
            t = time.time()
            out = net(features_t, indices_t, bs)
            torch.cuda.synchronize()
            times.append(time.time() - t)
751
        # print((net.grid == -1).float().sum(), net.grid.numel())
752
        # print("spconv time", time.time() - t)
traveller59's avatar
traveller59 committed
753
        print("spconv time", np.mean(times[2:]))
traveller59's avatar
traveller59 committed
754
755
756
        out = net(features_t, indices_t, bs)
        # print(out.indices)
        out = out.dense()
Yan Yan's avatar
Yan Yan committed
757
758
        out_numpy = out.detach().cpu().numpy()

759
760
761
        print(
            np.linalg.norm(out.detach().cpu().numpy() -
                           out_ref.detach().cpu().numpy()))
yanyan's avatar
yanyan committed
762
763
        print(out_numpy.min(), out_numpy.max(), out_numpy.mean(),
              out_numpy.sum())
traveller59's avatar
traveller59 committed
764
765


yanyan's avatar
yanyan committed
766
def main_subm(algo, dtype=torch.float32):
767
768
    # function for develop.
    np.random.seed(484)
Yan Yan's avatar
Yan Yan committed
769
    torch.manual_seed(50051)
770
771
    # devices = ["cuda:0"]
    devices = ["cuda:0"]
yanyan's avatar
yanyan committed
772
    shapes = [[400, 400, 15]]
773
774
775
776
777
778
779
780
781
782
783
784
785
786
    batchsizes = [2]

    in_channels = [32]
    out_channels = [64]
    ksizes = [(3, 3, 3)]
    strides = [1]
    paddings = [1]
    dilations = [1]
    for dev, shape, bs, IC, OC, k, s, p, d in params_grid(
            devices, shapes, batchsizes, in_channels, out_channels, ksizes,
            strides, paddings, dilations):
        if all([s > 1, d > 1]):
            continue
        device = torch.device(dev)
yanyan's avatar
yanyan committed
787
        num_points = [120000] * bs
788
789
790
791
792
793
794
795
796
797
798

        sparse_dict = generate_sparse_data(shape, num_points, IC)

        features = np.ascontiguousarray(sparse_dict["features"]).astype(
            np.float32)
        indices = np.ascontiguousarray(
            sparse_dict["indices"][:, [3, 0, 1, 2]]).astype(np.int32)
        features_dense = sparse_dict["features_dense"].astype(np.float32)
        indices_t = torch.from_numpy(indices)
        filters = np.random.uniform(0, 1, size=[k[0], 1, 1, IC,
                                                OC]).astype(np.float32)
yanyan's avatar
yanyan committed
799
800
        indices_t = torch.from_numpy(indices).int().to(device).to(dtype)
        features_t = torch.from_numpy(features).to(device).to(dtype)
801

yanyan's avatar
yanyan committed
802
803
        features_dense_t = torch.from_numpy(features_dense).to(device).to(
            dtype)
yanyan's avatar
yanyan committed
804
        net = SubMConv3dTestTorch(1, 3, shape, IC, OC, k, s, p, d,
yanyan's avatar
yanyan committed
805
                                  algo=algo).to(device).to(dtype)
806
        net_ref = Conv3dTestTorch(1, 3, shape, IC, OC, k, s, p,
yanyan's avatar
yanyan committed
807
808
                                  d).to(device).to(dtype)
        filters_t = torch.from_numpy(filters).to(device).to(dtype)
809
810
811
812
813
        net_ref.net[0].weight[:] = filters_t.permute(4, 3, 0, 1,
                                                     2).contiguous()
        net.net[0].weight[:] = filters_t
        out_ref = net_ref(features_dense_t)
        times = []
yanyan's avatar
yanyan committed
814
        for i in range(20):
815
816
817
818
819
820
821
822
823
824
            t = time.time()
            out = net(features_t, indices_t, bs)
            torch.cuda.synchronize()
            times.append(time.time() - t)
        # print((net.grid == -1).float().sum(), net.grid.numel())
        # print("spconv time", time.time() - t)
        print("spconv time", np.mean(times[10:]))
        out = net(features_t, indices_t, bs)
        # print(out.indices)
        out = out.dense()
Yan Yan's avatar
Yan Yan committed
825
        out_numpy = out.detach().cpu().numpy()
yanyan's avatar
yanyan committed
826
827
828
        # print(
        #     np.linalg.norm(out.detach().cpu().numpy() -
        #                    out_ref.detach().cpu().numpy()))
yanyan's avatar
yanyan committed
829
830
        print(out_numpy.min(), out_numpy.max(), out_numpy.mean(),
              out_numpy.sum())
Yan Yan's avatar
Yan Yan committed
831
    return out_numpy
832

yanyan's avatar
yanyan committed
833

traveller59's avatar
traveller59 committed
834
if __name__ == '__main__':
yanyan's avatar
yanyan committed
835
836
    # main_subm(algo=spconv.ConvAlgo.SparseConvNet, dtype=torch.float32)
    # main(algo=spconv.ConvAlgo.SparseConvNet, dtype=torch.float32)
Yan Yan's avatar
Yan Yan committed
837
    # TestCase().assertAllClose(out_my, out_ref)
traveller59's avatar
traveller59 committed
838
    # unittest.main()
yan.yan's avatar
sync  
yan.yan committed
839
    TestSpConv().testSpConv3d()