test_conv.py 33.1 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
355
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):
        np.random.seed(484)
yan.yan's avatar
v2.1  
yan.yan committed
356
        torch.manual_seed(48848)
yanyan's avatar
yanyan committed
357
        devices = ["cuda:0"]
traveller59's avatar
traveller59 committed
358
359
360
        shapes = [[19, 18, 17]]
        batchsizes = [1, 2]

yanyan's avatar
yanyan committed
361
        in_channels = [32]
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]
367
368
369
370
        algos = [
            ConvAlgo.Native, ConvAlgo.MaskImplicitGemm,
            ConvAlgo.MaskSplitImplicitGemm
        ]
yan.yan's avatar
v2.1  
yan.yan committed
371
        algos = [ConvAlgo.MaskSplitImplicitGemm]
traveller59's avatar
traveller59 committed
372

yan.yan's avatar
v2.1  
yan.yan committed
373
        for dev, shape, bs, IC, OC, k, s, p, d, al in params_grid(
374
                devices, shapes, batchsizes, in_channels, out_channels, ksizes,
yan.yan's avatar
v2.1  
yan.yan committed
375
                strides, paddings, dilations, algos):
traveller59's avatar
traveller59 committed
376
            if all([s > 1, d > 1]):
377
                continue  # don't support this.
yan.yan's avatar
v2.1  
yan.yan committed
378
            print(k, s, p, d)
traveller59's avatar
traveller59 committed
379
380
            device = torch.device(dev)
            num_points = [1000] * bs
yan.yan's avatar
v2.1  
yan.yan committed
381
            dtype = torch.float32
382
383
384
385
386
387
388
389
390
391
            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
392
393
394
            net_ref = Conv3dTestTorch(1, 3, shape, IC, OC, k, s, p,
                                      d).to(device).to(dtype)

traveller59's avatar
traveller59 committed
395
396
            sparse_dict = generate_sparse_data(shape, num_points, IC)

397
398
399
400
            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
401
402
            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
403
            features_t = torch.from_numpy(features).to(device).to(dtype)
traveller59's avatar
traveller59 committed
404
            features_t.requires_grad = True
405
406
            features_dense_t = torch.from_numpy(features_dense).to(device).to(
                dtype)
traveller59's avatar
traveller59 committed
407
            features_dense_t.requires_grad = True
yan.yan's avatar
v2.1  
yan.yan committed
408
409
            if net.algo == ConvAlgo.Native:
                if FILTER_HWIO:
410
411
412
                    filters = np.random.uniform(-1, 1,
                                                size=[k, k, k, IC,
                                                      OC]).astype(np.float32)
yan.yan's avatar
v2.1  
yan.yan committed
413
                else:
414
415
416
                    filters = np.random.uniform(-1, 1,
                                                size=[k, k, k, OC,
                                                      IC]).astype(np.float32)
yan.yan's avatar
v2.1  
yan.yan committed
417
418
                filters_t = torch.from_numpy(filters).to(device).to(dtype)
                if FILTER_HWIO:
419
420
                    net_ref.net[0].weight.data[:] = filters_t.permute(
                        4, 3, 0, 1, 2).contiguous()
yan.yan's avatar
v2.1  
yan.yan committed
421
                else:
422
423
                    net_ref.net[0].weight.data[:] = filters_t.permute(
                        3, 4, 0, 1, 2).contiguous()
yan.yan's avatar
yan.yan committed
424
            else:
425
426
427
                filters = np.random.uniform(-1, 1,
                                            size=[OC, k, k, k,
                                                  IC]).astype(np.float32)
yan.yan's avatar
v2.1  
yan.yan committed
428
                filters_t = torch.from_numpy(filters).to(device).to(dtype)
429
430
                net_ref.net[0].weight.data[:] = filters_t.permute(
                    0, 4, 1, 2, 3).contiguous()
yan.yan's avatar
v2.1  
yan.yan committed
431

traveller59's avatar
traveller59 committed
432
433
            net.net[0].weight.data[:] = filters_t
            out_ref = net_ref(features_dense_t)
434
            out = net(features_t, indices_t, bs).dense()
yan.yan's avatar
yan.yan committed
435
436
437
438
            out_np = out.detach().cpu().numpy()
            out_ref_np = out_ref.detach().cpu().numpy()
            self.assertAllClose(out_np, out_ref_np, atol=1e-4)

439
440
            dout = np.random.uniform(-0.2, 0.2,
                                     out_ref.shape).astype(features.dtype)
traveller59's avatar
traveller59 committed
441
442
443
            dout_t = torch.from_numpy(dout).to(device)
            out.backward(dout_t)
            out_ref.backward(dout_t)
444
445
            din_dense = features_dense_t.grad.detach().permute(0, 2, 3, 4,
                                                               1).contiguous()
traveller59's avatar
traveller59 committed
446
447
            din_sparse = gather_nd(din_dense, indices_t.long())
            din = features_t.grad.detach()
yan.yan's avatar
yan.yan committed
448

traveller59's avatar
traveller59 committed
449
450
451
452
453
            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
454
455
456
457
458
                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
459
                else:
yan.yan's avatar
v2.1  
yan.yan committed
460
461
                    # OHWI -> OIHW
                    dw = dw.transpose(0, 4, 1, 2, 3)
yan.yan's avatar
yan.yan committed
462

traveller59's avatar
traveller59 committed
463
                self.assertAllClose(dw, dw_ref, atol=1e-4)
yan.yan's avatar
yan.yan committed
464
            self.assertAllClose(din_np, din_sparse_np, atol=1e-4)
traveller59's avatar
traveller59 committed
465
466
467

    def testSpDeConv3d(self):
        np.random.seed(484)
Yan Yan's avatar
Yan Yan committed
468
        devices = ["cuda:0"]
traveller59's avatar
traveller59 committed
469
470
471
472
473
474
475
476
477
        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
478
479
480
481
482
        ksizes = [3]

        strides = [1]
        paddings = [0]
        dilations = [1]
traveller59's avatar
traveller59 committed
483
484

        for dev, shape, bs, IC, OC, k, s, p, d in params_grid(
485
486
                devices, shapes, batchsizes, in_channels, out_channels, ksizes,
                strides, paddings, dilations):
traveller59's avatar
traveller59 committed
487
            if all([s > 1, d > 1]):
488
                continue  # don't support this.
traveller59's avatar
traveller59 committed
489
490
491
492
493
            device = torch.device(dev)
            num_points = [1000] * bs

            sparse_dict = generate_sparse_data(shape, num_points, IC)

494
495
496
497
            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
498
            features_dense = sparse_dict["features_dense"].astype(np.float32)
yan.yan's avatar
bug fix  
yan.yan committed
499
500
501
502
503
504
505
            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
506
507
508
509
510
            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
511
512
513
514
            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
515
            filters_t = torch.from_numpy(filters).to(device)
yan.yan's avatar
bug fix  
yan.yan committed
516
517
            print(net_ref.net[0].weight.shape)
            if FILTER_HWIO:
518
519
                net_ref.net[0].weight.data[:] = filters_t.permute(
                    3, 4, 0, 1, 2).contiguous()
yan.yan's avatar
bug fix  
yan.yan committed
520
            else:
521
522
                net_ref.net[0].weight.data[:] = filters_t.permute(
                    4, 3, 0, 1, 2).contiguous()
traveller59's avatar
traveller59 committed
523
524
            net.net[0].weight.data[:] = filters_t
            out_ref = net_ref(features_dense_t)
525
            out = net(features_t, indices_t, bs).dense()
yan.yan's avatar
bug fix  
yan.yan committed
526
527
528
529
            out_np = out.detach().cpu().numpy()
            out_ref_np = out_ref.detach().cpu().numpy()
            self.assertAllClose(out_np, out_ref_np, atol=1e-4)

530
531
            dout = np.random.uniform(-0.2, 0.2,
                                     out_ref.shape).astype(features.dtype)
traveller59's avatar
traveller59 committed
532
533
534
            dout_t = torch.from_numpy(dout).to(device)
            out.backward(dout_t)
            out_ref.backward(dout_t)
535
536
            din_dense = features_dense_t.grad.detach().permute(0, 2, 3, 4,
                                                               1).contiguous()
traveller59's avatar
traveller59 committed
537
538
539
540
541
542
543
544
            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
545
546
547
548
                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
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
                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(
565
566
                devices, shapes, batchsizes, in_channels, out_channels, ksizes,
                strides):
traveller59's avatar
traveller59 committed
567
568
569
570
571
            device = torch.device(dev)
            num_points = [1000] * bs

            sparse_dict = generate_sparse_data(shape, num_points, IC)

572
573
574
575
            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
576
            features_dense = sparse_dict["features_dense"].astype(np.float32)
577
578
            filters = np.random.uniform(0, 1, size=[k, k, k, IC,
                                                    OC]).astype(np.float32)
traveller59's avatar
traveller59 committed
579
            indices_t = torch.from_numpy(indices).int().to(device)
580
581
            indices_scn_t = torch.from_numpy(
                indices[:, [1, 2, 3, 0]]).int().to(device)
traveller59's avatar
traveller59 committed
582
583
584
585
586
587
588
            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)
589
590
591
592
            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
593
594
            out_ref = net_ref(features_ref_t, indices_scn_t, bs)
            out = net(features_t, indices_t, bs)
595
596
            dout = np.random.uniform(-0.2, 0.2,
                                     out_ref.shape).astype(features.dtype)
traveller59's avatar
traveller59 committed
597
598
599
600
601
602
603
604
605
606
            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()
607
608
                dw_ref = layer_ref.weight.grad.detach().cpu().view(
                    *dw.shape).numpy()
traveller59's avatar
traveller59 committed
609
610
611
612
613
614
615
                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):
616
        np.random.seed(485)
617
        devices = ["cuda:0"]
traveller59's avatar
traveller59 committed
618
619
620
        shapes = [[19, 18, 17]]
        batchsizes = [1, 2]

yan.yan's avatar
yan.yan committed
621
622
        in_channels = [64]
        out_channels = [64]
traveller59's avatar
traveller59 committed
623
624
625
626
        ksizes = [2, 3]
        strides = [1, 2, 3]
        paddings = [0, 1]
        dilations = [1, 2, 3]
yan.yan's avatar
v2.1  
yan.yan committed
627
628
629
630
        # ksizes = [2]
        # strides = [2]
        # paddings = [0]
        # dilations = [1]
traveller59's avatar
traveller59 committed
631
632

        for dev, shape, bs, IC, OC, k, s, p, d in params_grid(
633
634
                devices, shapes, batchsizes, in_channels, out_channels, ksizes,
                strides, paddings, dilations):
traveller59's avatar
traveller59 committed
635
            if all([s > 1, d > 1]):
636
                continue  # don't support this.
traveller59's avatar
traveller59 committed
637
638
            device = torch.device(dev)
            num_points = [1000] * bs
yan.yan's avatar
yan.yan committed
639

traveller59's avatar
traveller59 committed
640
            # when data contains negative, sparse maxpool is not equal to dense maxpool.
641
642
643
644
645
646
647
648
649
            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
650
            features_dense = sparse_dict["features_dense"].astype(np.float32)
yan.yan's avatar
yan.yan committed
651
652
            filters = np.random.uniform(0, 1, size=[k, k, k, OC,
                                                    IC]).astype(np.float32)
traveller59's avatar
traveller59 committed
653
654
655
656
657
658
659
660
661
662
            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
663

traveller59's avatar
traveller59 committed
664
665
            outids = out.indices
            outfeatures = out.features
666
            outids_dev = outids.float()
667
            out_dense = out.dense(channels_first=False)
traveller59's avatar
traveller59 committed
668
            out = out_dense.permute(0, 4, 1, 2, 3).contiguous()
yan.yan's avatar
yan.yan committed
669
670
671
            out_np = out.detach().cpu().numpy()
            out_ref_np = out_ref.detach().cpu().numpy()
            self.assertAllClose(out_np, out_ref_np, atol=1e-4)
672
673
674

            dout_sparse = np.random.uniform(
                -0.2, 0.2, outfeatures.shape).astype(features.dtype)
traveller59's avatar
traveller59 committed
675
            dout_sparse_t = torch.from_numpy(dout_sparse).to(device)
676
677
            dout_t = scatter_nd(outids.long(), dout_sparse_t,
                                list(out_dense.shape))
traveller59's avatar
traveller59 committed
678
679
680
            dout_t = dout_t.permute(0, 4, 1, 2, 3).contiguous()
            out.backward(dout_t)
            out_ref.backward(dout_t)
681
682
            din_dense = features_dense_t.grad.detach().permute(0, 2, 3, 4,
                                                               1).contiguous()
traveller59's avatar
traveller59 committed
683
684
            din_sparse = gather_nd(din_dense, indices_t.long())
            din = features_t.grad.detach()
685

686
687
688
            din_np = din.cpu().numpy()
            din_sparse_np = din_sparse.cpu().numpy()
            self.assertAllClose(din_np, din_sparse_np, atol=1e-4)
689

traveller59's avatar
traveller59 committed
690

yanyan's avatar
yanyan committed
691
def main(algo=spconv.ConvAlgo.Native, dtype=torch.float32):
traveller59's avatar
traveller59 committed
692
693
    # function for develop.
    np.random.seed(484)
traveller59's avatar
traveller59 committed
694
    # devices = ["cuda:0"]
traveller59's avatar
traveller59 committed
695
    devices = ["cuda:0"]
yanyan's avatar
yanyan committed
696
    shapes = [[400, 400, 15]]
yanyan's avatar
yanyan committed
697
    batchsizes = [2]
traveller59's avatar
traveller59 committed
698

yanyan's avatar
yanyan committed
699
700
    in_channels = [19]
    out_channels = [17]
Yan Yan's avatar
Yan Yan committed
701
    ksizes = [(3, 3, 3)]
traveller59's avatar
traveller59 committed
702
703
704
705
706
    strides = [1]
    paddings = [0]
    dilations = [1]

    for dev, shape, bs, IC, OC, k, s, p, d in params_grid(
707
708
            devices, shapes, batchsizes, in_channels, out_channels, ksizes,
            strides, paddings, dilations):
traveller59's avatar
traveller59 committed
709
710
711
        if all([s > 1, d > 1]):
            continue
        device = torch.device(dev)
yanyan's avatar
yanyan committed
712
        num_points = [30000] * bs
traveller59's avatar
traveller59 committed
713
714
715

        sparse_dict = generate_sparse_data(shape, num_points, IC)

716
717
718
719
        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
720
721
        features_dense = sparse_dict["features_dense"].astype(np.float32)
        indices_t = torch.from_numpy(indices)
722
723
        filters = np.random.uniform(0, 1, size=[k[0], 1, 1, IC,
                                                OC]).astype(np.float32)
yanyan's avatar
yanyan committed
724
725
        indices_t = torch.from_numpy(indices).int().to(device).to(dtype)
        features_t = torch.from_numpy(features).to(device).to(dtype)
726

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

752
753
754
        print(
            np.linalg.norm(out.detach().cpu().numpy() -
                           out_ref.detach().cpu().numpy()))
yanyan's avatar
yanyan committed
755
756
        print(out_numpy.min(), out_numpy.max(), out_numpy.mean(),
              out_numpy.sum())
traveller59's avatar
traveller59 committed
757
758


yanyan's avatar
yanyan committed
759
def main_subm(algo, dtype=torch.float32):
760
761
    # function for develop.
    np.random.seed(484)
Yan Yan's avatar
Yan Yan committed
762
    torch.manual_seed(50051)
763
764
    # devices = ["cuda:0"]
    devices = ["cuda:0"]
yanyan's avatar
yanyan committed
765
    shapes = [[400, 400, 15]]
766
767
768
769
770
771
772
773
774
775
776
777
778
779
    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
780
        num_points = [120000] * bs
781
782
783
784
785
786
787
788
789
790
791

        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
792
793
        indices_t = torch.from_numpy(indices).int().to(device).to(dtype)
        features_t = torch.from_numpy(features).to(device).to(dtype)
794

yanyan's avatar
yanyan committed
795
796
        features_dense_t = torch.from_numpy(features_dense).to(device).to(
            dtype)
yanyan's avatar
yanyan committed
797
        net = SubMConv3dTestTorch(1, 3, shape, IC, OC, k, s, p, d,
yanyan's avatar
yanyan committed
798
                                  algo=algo).to(device).to(dtype)
799
        net_ref = Conv3dTestTorch(1, 3, shape, IC, OC, k, s, p,
yanyan's avatar
yanyan committed
800
801
                                  d).to(device).to(dtype)
        filters_t = torch.from_numpy(filters).to(device).to(dtype)
802
803
804
805
806
        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
807
        for i in range(20):
808
809
810
811
812
813
814
815
816
817
            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
818
        out_numpy = out.detach().cpu().numpy()
yanyan's avatar
yanyan committed
819
820
821
        # print(
        #     np.linalg.norm(out.detach().cpu().numpy() -
        #                    out_ref.detach().cpu().numpy()))
yanyan's avatar
yanyan committed
822
823
        print(out_numpy.min(), out_numpy.max(), out_numpy.mean(),
              out_numpy.sum())
Yan Yan's avatar
Yan Yan committed
824
    return out_numpy
825

yanyan's avatar
yanyan committed
826

traveller59's avatar
traveller59 committed
827
if __name__ == '__main__':
yanyan's avatar
yanyan committed
828
829
    # main_subm(algo=spconv.ConvAlgo.SparseConvNet, dtype=torch.float32)
    # main(algo=spconv.ConvAlgo.SparseConvNet, dtype=torch.float32)
Yan Yan's avatar
Yan Yan committed
830
    # TestCase().assertAllClose(out_my, out_ref)
traveller59's avatar
traveller59 committed
831
    # unittest.main()
yan.yan's avatar
v2.1  
yan.yan committed
832
    TestSpConv().testSpMaxPool3d()