test_conv.py 26.7 KB
Newer Older
traveller59's avatar
traveller59 committed
1
# Copyright 2019 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
22
23
24
25
26
27
from torch import nn

import spconv
from spconv.test_utils import TestCase, generate_sparse_data, params_grid

# import sparseconvnet as scn

traveller59's avatar
traveller59 committed
28
29

class SparseConv3dTestTorch(nn.Module):
30
31
    def __init__(self, num_layers, ndim, shape, in_channels, out_channels,
                 kernel_size, stride, padding, dilation):
traveller59's avatar
traveller59 committed
32
        super().__init__()
33
34
35
36
37
38
39
40
41
42
        layers = [
            spconv.SparseConv3d(in_channels,
                                out_channels,
                                kernel_size,
                                stride,
                                padding=padding,
                                dilation=dilation,
                                bias=False,
                                use_hash=True)
        ]
traveller59's avatar
traveller59 committed
43
        for i in range(1, num_layers):
44
45
46
47
48
49
50
51
52
            layers.append(
                spconv.SparseConv3d(out_channels,
                                    out_channels,
                                    kernel_size,
                                    stride,
                                    padding=padding,
                                    dilation=dilation,
                                    bias=False))
        self.net = spconv.SparseSequential(*layers, )
traveller59's avatar
traveller59 committed
53
54
55
56
57
58
        # 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()
59
60
61
62
        x = spconv.SparseConvTensor(features, coors, self.shape, batch_size,
                                    self.grid)
        return self.net(x)  # .dense()

traveller59's avatar
traveller59 committed
63
64

class SubMConv3dTestTorch(nn.Module):
65
66
    def __init__(self, num_layers, ndim, shape, in_channels, out_channels,
                 kernel_size, stride, padding, dilation):
traveller59's avatar
traveller59 committed
67
        super().__init__()
68
69
70
71
72
73
74
75
76
        layers = [
            spconv.SubMConv3d(in_channels,
                              out_channels,
                              kernel_size,
                              stride,
                              padding=padding,
                              dilation=dilation,
                              bias=False)
        ]
traveller59's avatar
traveller59 committed
77
        for i in range(1, num_layers):
78
79
80
81
82
83
84
85
86
            layers.append(
                spconv.SubMConv3d(out_channels,
                                  out_channels,
                                  kernel_size,
                                  stride,
                                  padding=padding,
                                  dilation=dilation,
                                  bias=False))
        self.net = spconv.SparseSequential(*layers, )
traveller59's avatar
traveller59 committed
87
88
89
90
91
        self.grid = torch.full([3, *shape], -1, dtype=torch.int32).cuda()
        self.shape = shape

    def forward(self, features, coors, batch_size):
        coors = coors.int()
92
93
94
        x = spconv.SparseConvTensor(features, coors, self.shape, batch_size,
                                    self.grid)
        return self.net(x)  # .dense()
traveller59's avatar
traveller59 committed
95
96
97


class Conv3dTestTorch(nn.Module):
98
99
    def __init__(self, num_layers, ndim, shape, in_channels, out_channels,
                 kernel_size, stride, padding, dilation):
traveller59's avatar
traveller59 committed
100
        super().__init__()
101
102
103
104
105
106
107
108
109
        layers = [
            nn.Conv3d(in_channels,
                      out_channels,
                      kernel_size,
                      stride,
                      padding=padding,
                      dilation=dilation,
                      bias=False)
        ]
traveller59's avatar
traveller59 committed
110
        for i in range(1, num_layers):
111
112
113
114
115
116
117
118
119
            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
120
121
122
        self.shape = shape

    def forward(self, x):
123
        return self.net(x)  # .dense()
traveller59's avatar
traveller59 committed
124
125
126


class SparseDeConv3dTestTorch(nn.Module):
127
128
    def __init__(self, num_layers, ndim, shape, in_channels, out_channels,
                 kernel_size, stride, padding, dilation):
traveller59's avatar
traveller59 committed
129
        super().__init__()
130
131
132
133
134
135
136
137
138
        layers = [
            spconv.SparseConvTranspose3d(in_channels,
                                         out_channels,
                                         kernel_size,
                                         stride,
                                         padding=padding,
                                         dilation=dilation,
                                         bias=False)
        ]
traveller59's avatar
traveller59 committed
139
        for i in range(1, num_layers):
140
141
142
143
144
145
146
147
148
            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
149
150
151
152
        self.shape = shape

    def forward(self, features, coors, batch_size):
        coors = coors.int()
153
154
155
        x = spconv.SparseConvTensor(features, coors, self.shape, batch_size)
        return self.net(x)  # .dense()

traveller59's avatar
traveller59 committed
156
157

class DeConv3dTestTorch(nn.Module):
158
159
    def __init__(self, num_layers, ndim, shape, in_channels, out_channels,
                 kernel_size, stride, padding, dilation):
traveller59's avatar
traveller59 committed
160
        super().__init__()
161
162
163
164
165
166
167
168
169
        layers = [
            nn.ConvTranspose3d(in_channels,
                               out_channels,
                               kernel_size,
                               stride,
                               padding=padding,
                               dilation=dilation,
                               bias=False)
        ]
traveller59's avatar
traveller59 committed
170
        for i in range(1, num_layers):
171
172
173
174
175
176
177
178
179
            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
180
181
182
        self.shape = shape

    def forward(self, x):
183
        return self.net(x)  # .dense()
traveller59's avatar
traveller59 committed
184
185
186


class SparseMaxPoolTestTorch(nn.Module):
187
188
    def __init__(self, num_layers, ndim, shape, kernel_size, stride, padding,
                 dilation):
traveller59's avatar
traveller59 committed
189
        super().__init__()
190
191
192
        layers = [
            spconv.SparseMaxPool3d(kernel_size, stride, padding, dilation)
        ]
traveller59's avatar
traveller59 committed
193
        for i in range(1, num_layers):
194
195
196
            layers.append(
                spconv.SparseMaxPool3d(kernel_size, stride, padding, dilation))
        self.net = spconv.SparseSequential(*layers, )
traveller59's avatar
traveller59 committed
197
198
199
200
        self.shape = shape

    def forward(self, features, coors, batch_size):
        coors = coors.int()
201
202
203
        x = spconv.SparseConvTensor(features, coors, self.shape, batch_size)
        return self.net(x)  # .dense()

traveller59's avatar
traveller59 committed
204
205

class MaxPool3dTestTorch(nn.Module):
206
207
    def __init__(self, num_layers, ndim, shape, kernel_size, stride, padding,
                 dilation):
traveller59's avatar
traveller59 committed
208
        super().__init__()
209
        layers = [nn.MaxPool3d(kernel_size, stride, padding, dilation)]
traveller59's avatar
traveller59 committed
210
        for i in range(1, num_layers):
211
212
            layers.append(nn.MaxPool3d(kernel_size, stride, padding, dilation))
        self.net = nn.Sequential(*layers, )
traveller59's avatar
traveller59 committed
213
214
215
        self.shape = shape

    def forward(self, x):
216
        return self.net(x)  # .dense()
traveller59's avatar
traveller59 committed
217
218
219


class SubmanifoldConvTestTorch(nn.Module):
220
221
    def __init__(self, num_layers, ndim, shape, in_channels, out_channels,
                 kernel_size, stride):
traveller59's avatar
traveller59 committed
222
        super().__init__()
223
224
225
226
227
228
229
        layers = [
            spconv.SubMConv3d(in_channels,
                              out_channels,
                              kernel_size,
                              bias=False,
                              indice_key="subm0")
        ]
traveller59's avatar
traveller59 committed
230
        for i in range(1, num_layers):
231
232
233
234
235
236
            layers.append(
                spconv.SubMConv3d(out_channels,
                                  out_channels,
                                  kernel_size,
                                  bias=False))
        self.net = nn.Sequential(*layers, )
traveller59's avatar
traveller59 committed
237
238
239
240
        self.shape = shape

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

244

traveller59's avatar
traveller59 committed
245
class SCNCoupleDeConvTest(nn.Module):
246
247
    def __init__(self, num_layers, ndim, shape, in_channels, out_channels,
                 kernel_size, stride):
traveller59's avatar
traveller59 committed
248
249
250
        super().__init__()
        self.scn_input = scn.InputLayer(ndim, shape, mode=0)
        self.net = nn.Sequential(
251
252
253
254
255
256
257
258
259
260
261
262
            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
263
264
265
266
267
268
269
270
            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)

271

traveller59's avatar
traveller59 committed
272
class SparseCoupleDeConvTest(nn.Module):
273
274
    def __init__(self, num_layers, ndim, shape, in_channels, out_channels,
                 kernel_size, stride):
traveller59's avatar
traveller59 committed
275
276
        super().__init__()
        self.net = spconv.SparseSequential(
277
278
279
280
281
282
283
284
285
286
287
            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
288
289
290
291
292
293
        )
        self.todense = spconv.ToDense()
        self.shape = shape

    def forward(self, features, coors, batch_size):
        coors = coors.int()
294
295
        x = spconv.SparseConvTensor(features, coors, self.shape, batch_size)
        return self.todense(self.net(x))  # .dense()
traveller59's avatar
traveller59 committed
296
297
298
299
300


def gather_nd(params, indices):
    # this function has a limit that MAX_ADVINDEX_CALC_DIMS=5
    ndim = indices.shape[-1]
301
302
    output_shape = list(indices.shape[:-1]) + list(
        params.shape[indices.shape[-1]:])
traveller59's avatar
traveller59 committed
303
304
305
306
307
    flatted_indices = indices.view(-1, ndim)
    slices = [flatted_indices[:, i] for i in range(ndim)]
    slices += [Ellipsis]
    return params[slices].view(*output_shape)

308

traveller59's avatar
traveller59 committed
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
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)
        devices = ["cpu:0"]
        shapes = [[19, 18, 17]]
        batchsizes = [1, 2]

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

        for dev, shape, bs, IC, OC, k, s, p, d in params_grid(
340
341
                devices, shapes, batchsizes, in_channels, out_channels, ksizes,
                strides, paddings, dilations):
traveller59's avatar
traveller59 committed
342
            if all([s > 1, d > 1]):
343
                continue  # don't support this.
traveller59's avatar
traveller59 committed
344
345
346
347
348
            device = torch.device(dev)
            num_points = [1000] * bs

            sparse_dict = generate_sparse_data(shape, num_points, IC)

349
350
351
352
            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
353
            features_dense = sparse_dict["features_dense"].astype(np.float32)
354
355
            filters = np.random.uniform(0, 1, size=[k, k, k, IC,
                                                    OC]).astype(np.float32)
traveller59's avatar
traveller59 committed
356
357
358
359
360
            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
361
362
363
364
            net = SparseConv3dTestTorch(1, 3, shape, IC, OC, k, s, p,
                                        d).to(device)
            net_ref = Conv3dTestTorch(1, 3, shape, IC, OC, k, s, p,
                                      d).to(device)
traveller59's avatar
traveller59 committed
365
            filters_t = torch.from_numpy(filters).to(device)
366
367
            net_ref.net[0].weight.data[:] = filters_t.permute(4, 3, 0, 1,
                                                              2).contiguous()
traveller59's avatar
traveller59 committed
368
369
            net.net[0].weight.data[:] = filters_t
            out_ref = net_ref(features_dense_t)
370
            out = net(features_t, indices_t, bs).dense()
371
372
            dout = np.random.uniform(-0.2, 0.2,
                                     out_ref.shape).astype(features.dtype)
traveller59's avatar
traveller59 committed
373
374
375
            dout_t = torch.from_numpy(dout).to(device)
            out.backward(dout_t)
            out_ref.backward(dout_t)
376
377
            din_dense = features_dense_t.grad.detach().permute(0, 2, 3, 4,
                                                               1).contiguous()
traveller59's avatar
traveller59 committed
378
379
380
381
382
383
384
385
386
387
388
389
390
391
            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()
                dw = dw.transpose(4, 3, 0, 1, 2)
                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)
392

traveller59's avatar
traveller59 committed
393
394
395
396
397
398
399
400
401
402
403
404
405
406
    def testSpDeConv3d(self):
        np.random.seed(484)
        devices = ["cuda:0", "cpu:0"]
        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]

        for dev, shape, bs, IC, OC, k, s, p, d in params_grid(
407
408
                devices, shapes, batchsizes, in_channels, out_channels, ksizes,
                strides, paddings, dilations):
traveller59's avatar
traveller59 committed
409
            if all([s > 1, d > 1]):
410
                continue  # don't support this.
traveller59's avatar
traveller59 committed
411
412
413
414
415
            device = torch.device(dev)
            num_points = [1000] * bs

            sparse_dict = generate_sparse_data(shape, num_points, IC)

416
417
418
419
            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
420
            features_dense = sparse_dict["features_dense"].astype(np.float32)
421
422
            filters = np.random.uniform(0, 1, size=[k, k, k, IC,
                                                    OC]).astype(np.float32)
traveller59's avatar
traveller59 committed
423
424
425
426
427
            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
428
429
430
431
            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
432
            filters_t = torch.from_numpy(filters).to(device)
433
434
            net_ref.net[0].weight.data[:] = filters_t.permute(3, 4, 0, 1,
                                                              2).contiguous()
traveller59's avatar
traveller59 committed
435
436
            net.net[0].weight.data[:] = filters_t
            out_ref = net_ref(features_dense_t)
437
            out = net(features_t, indices_t, bs).dense()
438
439
            dout = np.random.uniform(-0.2, 0.2,
                                     out_ref.shape).astype(features.dtype)
traveller59's avatar
traveller59 committed
440
441
442
            dout_t = torch.from_numpy(dout).to(device)
            out.backward(dout_t)
            out_ref.backward(dout_t)
443
444
            din_dense = features_dense_t.grad.detach().permute(0, 2, 3, 4,
                                                               1).contiguous()
traveller59's avatar
traveller59 committed
445
446
447
448
449
450
451
452
453
454
455
456
457
458
            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()
                dw = dw.transpose(3, 4, 0, 1, 2)
                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)
459

traveller59's avatar
traveller59 committed
460
461
462
463
464
465
466
467
468
469
470
471
472
473
    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(
474
475
                devices, shapes, batchsizes, in_channels, out_channels, ksizes,
                strides):
traveller59's avatar
traveller59 committed
476
477
478
479
480
            device = torch.device(dev)
            num_points = [1000] * bs

            sparse_dict = generate_sparse_data(shape, num_points, IC)

481
482
483
484
            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
485
            features_dense = sparse_dict["features_dense"].astype(np.float32)
486
487
            filters = np.random.uniform(0, 1, size=[k, k, k, IC,
                                                    OC]).astype(np.float32)
traveller59's avatar
traveller59 committed
488
            indices_t = torch.from_numpy(indices).int().to(device)
489
490
            indices_scn_t = torch.from_numpy(
                indices[:, [1, 2, 3, 0]]).int().to(device)
traveller59's avatar
traveller59 committed
491
492
493
494
495
496
497
            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)
498
499
500
501
            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
502
503
            out_ref = net_ref(features_ref_t, indices_scn_t, bs)
            out = net(features_t, indices_t, bs)
504
505
            dout = np.random.uniform(-0.2, 0.2,
                                     out_ref.shape).astype(features.dtype)
traveller59's avatar
traveller59 committed
506
507
508
509
510
511
512
513
514
515
            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()
516
517
                dw_ref = layer_ref.weight.grad.detach().cpu().view(
                    *dw.shape).numpy()
traveller59's avatar
traveller59 committed
518
519
520
521
522
523
524
                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):
525
        np.random.seed(485)
traveller59's avatar
traveller59 committed
526
527
528
529
530
531
532
533
534
535
536
537
        devices = ["cuda:0", "cpu:0"]
        shapes = [[19, 18, 17]]
        batchsizes = [1, 2]

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

        for dev, shape, bs, IC, OC, k, s, p, d in params_grid(
538
539
                devices, shapes, batchsizes, in_channels, out_channels, ksizes,
                strides, paddings, dilations):
traveller59's avatar
traveller59 committed
540
            if all([s > 1, d > 1]):
541
                continue  # don't support this.
traveller59's avatar
traveller59 committed
542
543
544
            device = torch.device(dev)
            num_points = [1000] * bs
            # when data contains negative, sparse maxpool is not equal to dense maxpool.
545
546
547
548
549
550
551
552
553
            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
554
            features_dense = sparse_dict["features_dense"].astype(np.float32)
555
556
            filters = np.random.uniform(0, 1, size=[k, k, k, IC,
                                                    OC]).astype(np.float32)
traveller59's avatar
traveller59 committed
557
558
559
560
561
562
563
564
565
566
567
568
            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)
            outids = out.indices
            outfeatures = out.features
569
            out_dense = out.dense(channels_first=False)
traveller59's avatar
traveller59 committed
570
            out = out_dense.permute(0, 4, 1, 2, 3).contiguous()
571
572
573

            dout_sparse = np.random.uniform(
                -0.2, 0.2, outfeatures.shape).astype(features.dtype)
traveller59's avatar
traveller59 committed
574
            dout_sparse_t = torch.from_numpy(dout_sparse).to(device)
575
576
            dout_t = scatter_nd(outids.long(), dout_sparse_t,
                                list(out_dense.shape))
traveller59's avatar
traveller59 committed
577
578
579
            dout_t = dout_t.permute(0, 4, 1, 2, 3).contiguous()
            out.backward(dout_t)
            out_ref.backward(dout_t)
580
581
            din_dense = features_dense_t.grad.detach().permute(0, 2, 3, 4,
                                                               1).contiguous()
traveller59's avatar
traveller59 committed
582
583
584
585
586
            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)
587

traveller59's avatar
traveller59 committed
588
589
590
            out_np = out.detach().cpu().numpy()
            out_ref_np = out_ref.detach().cpu().numpy()
            self.assertAllClose(out_np, out_ref_np, atol=1e-4)
591

traveller59's avatar
traveller59 committed
592
593
594
595

def main():
    # function for develop.
    np.random.seed(484)
traveller59's avatar
traveller59 committed
596
    # devices = ["cuda:0"]
traveller59's avatar
traveller59 committed
597
598
    devices = ["cuda:0"]
    shapes = [[50, 30, 30]]
traveller59's avatar
traveller59 committed
599
    batchsizes = [2]
traveller59's avatar
traveller59 committed
600
601
602

    in_channels = [256]
    out_channels = [256]
traveller59's avatar
traveller59 committed
603
    ksizes = [(3, 1, 1)]
traveller59's avatar
traveller59 committed
604
605
606
607
608
    strides = [1]
    paddings = [0]
    dilations = [1]

    for dev, shape, bs, IC, OC, k, s, p, d in params_grid(
609
610
            devices, shapes, batchsizes, in_channels, out_channels, ksizes,
            strides, paddings, dilations):
traveller59's avatar
traveller59 committed
611
612
613
        if all([s > 1, d > 1]):
            continue
        device = torch.device(dev)
tusimple's avatar
tusimple committed
614
        num_points = [500] * bs
traveller59's avatar
traveller59 committed
615
616
617

        sparse_dict = generate_sparse_data(shape, num_points, IC)

618
619
620
621
        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
622
623
        features_dense = sparse_dict["features_dense"].astype(np.float32)
        indices_t = torch.from_numpy(indices)
624
625
        filters = np.random.uniform(0, 1, size=[k[0], 1, 1, IC,
                                                OC]).astype(np.float32)
traveller59's avatar
traveller59 committed
626
627
        indices_t = torch.from_numpy(indices).int().to(device).float()
        features_t = torch.from_numpy(features).to(device).float()
628

traveller59's avatar
traveller59 committed
629
        features_dense_t = torch.from_numpy(features_dense).to(device).float()
630
631
632
633
        net = SparseConv3dTestTorch(1, 3, shape, IC, OC, k, s, p,
                                    d).to(device).float()
        net_ref = Conv3dTestTorch(1, 3, shape, IC, OC, k, s, p,
                                  d).to(device).float()
traveller59's avatar
traveller59 committed
634
        filters_t = torch.from_numpy(filters).to(device).float()
635
636
        net_ref.net[0].weight[:] = filters_t.permute(4, 3, 0, 1,
                                                     2).contiguous()
traveller59's avatar
traveller59 committed
637
638
639
        net.net[0].weight[:] = filters_t
        out_ref = net_ref(features_dense_t)
        times = []
tusimple's avatar
tusimple committed
640
        for i in range(10):
traveller59's avatar
traveller59 committed
641
642
643
644
            t = time.time()
            out = net(features_t, indices_t, bs)
            torch.cuda.synchronize()
            times.append(time.time() - t)
645
        # print((net.grid == -1).float().sum(), net.grid.numel())
646
        # print("spconv time", time.time() - t)
traveller59's avatar
traveller59 committed
647
        print("spconv time", np.mean(times[2:]))
traveller59's avatar
traveller59 committed
648
649
650
        out = net(features_t, indices_t, bs)
        # print(out.indices)
        out = out.dense()
651
652
653
        print(
            np.linalg.norm(out.detach().cpu().numpy() -
                           out_ref.detach().cpu().numpy()))
traveller59's avatar
traveller59 committed
654
655
656


if __name__ == '__main__':
traveller59's avatar
traveller59 committed
657
658
    main()
    # unittest.main()