test_sparse_ops-csr.py 9.62 KB
Newer Older
1
import backend as F
2

3
import dgl
4
import numpy as np
5
import pytest
6
import scipy.sparse as ssp
7
from utils import parametrize_idtype
8

9
if F.backend_name == "pytorch":
10
    import torch
11

12
13
    torch.backends.cuda.matmul.allow_tf32 = False

14
15
16
17

def _random_simple_graph(
    idtype, dtype, ctx, M, N, max_nnz, srctype, dsttype, etype
):
18
19
20
21
22
23
    src = np.random.randint(0, M, (max_nnz,))
    dst = np.random.randint(0, N, (max_nnz,))
    val = np.random.randn(max_nnz)
    a = ssp.csr_matrix((val, (src, dst)), shape=(M, N))
    a.sum_duplicates()
    a = a.tocoo()
24
25
26
27
28
29
30
    # shuffle edges
    perm = np.random.permutation(a.nnz)
    row = a.row[perm]
    col = a.col[perm]
    val = a.data[perm]
    a = ssp.csr_matrix((val, (row, col)), shape=(M, N))

31
    A = dgl.heterograph(
32
33
34
35
36
37
38
39
40
        {
            (srctype, etype, dsttype): (
                F.copy_to(F.tensor(row, dtype=idtype), ctx),
                F.copy_to(F.tensor(col, dtype=idtype), ctx),
            )
        },
        num_nodes_dict={srctype: a.shape[0], dsttype: a.shape[1]},
    )
    A.edata["w"] = F.copy_to(F.tensor(val, dtype=dtype), ctx)
41
42
    return a, A

43

nv-dlasalle's avatar
nv-dlasalle committed
44
@parametrize_idtype
45
@pytest.mark.parametrize("dtype", [F.float32, F.float64])
46
47
@pytest.mark.parametrize("return_edge_ids", [True, False])
def test_csrmm(idtype, dtype, return_edge_ids):
48
49
50
51
52
53
54
55
56
    a, A = _random_simple_graph(
        idtype, dtype, F.ctx(), 500, 600, 9000, "A", "B", "AB"
    )
    b, B = _random_simple_graph(
        idtype, dtype, F.ctx(), 600, 700, 9000, "B", "C", "BC"
    )
    C, C_weights = dgl._sparse_ops._csrmm(
        A._graph, A.edata["w"], B._graph, B.edata["w"], 2
    )
57
    C_adj = C.adjacency_matrix_scipy(0, False, "csr", return_edge_ids)
58
59
60
61
62
    C_adj.data = F.asnumpy(C_weights)
    C_adj = F.tensor(C_adj.todense(), dtype=dtype)
    c = F.tensor((a * b).todense(), dtype=dtype)
    assert F.allclose(C_adj, c)

63

nv-dlasalle's avatar
nv-dlasalle committed
64
@parametrize_idtype
65
66
@pytest.mark.parametrize("dtype", [F.float32, F.float64])
@pytest.mark.parametrize("num_vtypes", [1, 2])
67
def test_csrmm_backward(idtype, dtype, num_vtypes):
68
69
70
71
72
73
74
75
76
77
78
79
80
81
    a, A = _random_simple_graph(idtype, dtype, F.ctx(), 3, 4, 6, "A", "B", "AB")
    b, B = _random_simple_graph(
        idtype,
        dtype,
        F.ctx(),
        4,
        3,
        6,
        "B",
        "A" if num_vtypes == 1 else "C",
        "BA",
    )
    A_row, A_col = A.edges(order="eid")
    B_row, B_col = B.edges(order="eid")
82
83
84
85
86
87
88
    A_row = F.asnumpy(A_row)
    A_col = F.asnumpy(A_col)
    B_row = F.asnumpy(B_row)
    B_col = F.asnumpy(B_col)
    a_dense = F.attach_grad(F.tensor(a.todense(), dtype=dtype))
    b_dense = F.attach_grad(F.tensor(b.todense(), dtype=dtype))

89
90
    A.edata["w"] = F.attach_grad(A.edata["w"])
    B.edata["w"] = F.attach_grad(B.edata["w"])
91
92

    with F.record_grad():
93
        C = dgl.adj_product_graph(A, B, "w")
94
95
96
        assert len(C.ntypes) == num_vtypes
        assert len(C.etypes) == 1
        C_dense = np.zeros((3, 3))
97
        C_row, C_col = C.edges(order="eid")
98
99
        C_row = F.asnumpy(C_row)
        C_col = F.asnumpy(C_col)
100
        C_dense[C_row, C_col] = F.asnumpy(C.edata["w"])
101
102
103
        c_dense = F.matmul(a_dense, b_dense)
        assert np.allclose(C_dense, F.asnumpy(c_dense), rtol=1e-4, atol=1e-4)

104
        F.backward(F.reduce_sum(C.edata["w"]) + F.reduce_sum(c_dense))
105
106
        a_dense_grad = F.asnumpy(F.grad(a_dense))[A_row, A_col]
        b_dense_grad = F.asnumpy(F.grad(b_dense))[B_row, B_col]
107
108
        A_spspmm_grad = F.asnumpy(F.grad(A.edata["w"]))
        B_spspmm_grad = F.asnumpy(F.grad(B.edata["w"]))
109
110
111
        assert np.allclose(a_dense_grad, A_spspmm_grad, rtol=1e-4, atol=1e-4)
        assert np.allclose(b_dense_grad, B_spspmm_grad, rtol=1e-4, atol=1e-4)

112

nv-dlasalle's avatar
nv-dlasalle committed
113
@parametrize_idtype
114
@pytest.mark.parametrize("dtype", [F.float32, F.float64])
115
116
@pytest.mark.parametrize("return_edge_ids", [True, False])
def test_csrsum(idtype, dtype, return_edge_ids):
117
118
119
120
121
122
123
124
125
    a, A = _random_simple_graph(
        idtype, dtype, F.ctx(), 500, 600, 9000, "A", "B", "AB"
    )
    b, B = _random_simple_graph(
        idtype, dtype, F.ctx(), 500, 600, 9000, "A", "B", "AB"
    )
    C, C_weights = dgl._sparse_ops._csrsum(
        [A._graph, B._graph], [A.edata["w"], B.edata["w"]]
    )
126
    C_adj = C.adjacency_matrix_scipy(0, False, "csr", return_edge_ids)
127
128
129
130
131
    C_adj.data = F.asnumpy(C_weights)
    C_adj = F.tensor(C_adj.todense(), dtype=dtype)
    c = F.tensor((a + b).todense(), dtype=dtype)
    assert F.allclose(C_adj, c)

132

nv-dlasalle's avatar
nv-dlasalle committed
133
@parametrize_idtype
134
135
@pytest.mark.parametrize("dtype", [F.float32, F.float64])
@pytest.mark.parametrize("nelems", [1, 2])
136
def test_csrsum_backward(idtype, dtype, nelems):
137
138
139
140
    a, A = _random_simple_graph(idtype, dtype, F.ctx(), 3, 4, 6, "A", "B", "AB")
    b, B = _random_simple_graph(idtype, dtype, F.ctx(), 3, 4, 6, "A", "B", "AB")
    A_row, A_col = A.edges(order="eid")
    B_row, B_col = B.edges(order="eid")
141
142
143
144
145
146
147
    A_row = F.asnumpy(A_row)
    A_col = F.asnumpy(A_col)
    B_row = F.asnumpy(B_row)
    B_col = F.asnumpy(B_col)
    a_dense = F.attach_grad(F.tensor(a.todense(), dtype=dtype))
    b_dense = F.attach_grad(F.tensor(b.todense(), dtype=dtype))

148
149
    A.edata["w"] = F.attach_grad(A.edata["w"])
    B.edata["w"] = F.attach_grad(B.edata["w"])
150
151
152
153

    with F.record_grad():
        if nelems == 2:
            # Test for two element case
154
            C = dgl.adj_sum_graph([A, B], "w")
155
156
            assert C.canonical_etypes == A.canonical_etypes
            C_dense = np.zeros((3, 4))
157
            C_row, C_col = C.edges(order="eid")
158
159
            C_row = F.asnumpy(C_row)
            C_col = F.asnumpy(C_col)
160
            C_dense[C_row, C_col] = F.asnumpy(C.edata["w"])
161
            c_dense = a_dense + b_dense
162
163
164
            assert np.allclose(
                C_dense, F.asnumpy(c_dense), rtol=1e-4, atol=1e-4
            )
165

166
            F.backward(F.reduce_sum(C.edata["w"]) + F.reduce_sum(c_dense))
167
168
            a_dense_grad = F.asnumpy(F.grad(a_dense))[A_row, A_col]
            b_dense_grad = F.asnumpy(F.grad(b_dense))[B_row, B_col]
169
170
171
172
173
174
175
176
            A_spspmm_grad = F.asnumpy(F.grad(A.edata["w"]))
            B_spspmm_grad = F.asnumpy(F.grad(B.edata["w"]))
            assert np.allclose(
                a_dense_grad, A_spspmm_grad, rtol=1e-4, atol=1e-4
            )
            assert np.allclose(
                b_dense_grad, B_spspmm_grad, rtol=1e-4, atol=1e-4
            )
177
178
        elif nelems == 1:
            # Test for single element case
179
            C = dgl.adj_sum_graph([A], "w")
180
181
            assert C.canonical_etypes == A.canonical_etypes
            C_dense = np.zeros((3, 4))
182
            C_row, C_col = C.edges(order="eid")
183
184
            C_row = F.asnumpy(C_row)
            C_col = F.asnumpy(C_col)
185
            C_dense[C_row, C_col] = F.asnumpy(C.edata["w"])
186
            c_dense = a_dense
187
188
189
            assert np.allclose(
                C_dense, F.asnumpy(c_dense), rtol=1e-4, atol=1e-4
            )
190

191
            F.backward(F.reduce_sum(C.edata["w"]) + F.reduce_sum(c_dense))
192
            a_dense_grad = F.asnumpy(F.grad(a_dense))[A_row, A_col]
193
194
195
196
197
            A_spspmm_grad = F.asnumpy(F.grad(A.edata["w"]))
            assert np.allclose(
                a_dense_grad, A_spspmm_grad, rtol=1e-4, atol=1e-4
            )

198

nv-dlasalle's avatar
nv-dlasalle committed
199
@parametrize_idtype
200
201
202
@pytest.mark.parametrize("dtype", [F.float32, F.float64])
@pytest.mark.parametrize("A_nnz", [9000, 0])
@pytest.mark.parametrize("B_nnz", [9000, 0])
203
def test_csrmask(idtype, dtype, A_nnz, B_nnz):
204
205
206
207
208
209
210
211
    a, A = _random_simple_graph(
        idtype, dtype, F.ctx(), 500, 600, A_nnz, "A", "B", "AB"
    )
    b, B = _random_simple_graph(
        idtype, dtype, F.ctx(), 500, 600, B_nnz, "A", "B", "AB"
    )
    C = dgl._sparse_ops._csrmask(A._graph, A.edata["w"], B._graph)
    B_row, B_col = B.edges(order="eid")
212
213
214
215
    B_row = F.asnumpy(B_row)
    B_col = F.asnumpy(B_col)
    c = F.tensor(a.todense()[B_row, B_col], dtype)
    assert F.allclose(C, c)
216

217

nv-dlasalle's avatar
nv-dlasalle committed
218
@parametrize_idtype
219
@pytest.mark.parametrize("dtype", [F.float32, F.float64])
220
def test_csrmask_backward(idtype, dtype):
221
222
223
224
    a, A = _random_simple_graph(idtype, dtype, F.ctx(), 3, 4, 6, "A", "B", "AB")
    b, B = _random_simple_graph(idtype, dtype, F.ctx(), 3, 4, 6, "A", "B", "AB")
    A_row, A_col = A.edges(order="eid")
    B_row, B_col = B.edges(order="eid")
225
226
227
228
229
230
    A_row = F.asnumpy(A_row)
    A_col = F.asnumpy(A_col)
    B_row = F.asnumpy(B_row)
    B_col = F.asnumpy(B_col)
    a_dense = F.attach_grad(F.tensor(a.todense(), dtype=dtype))

231
    A.edata["w"] = F.attach_grad(A.edata["w"])
232
233
234

    with F.record_grad():
        # Test for two element case
235
236
        C1 = F.csrmask(A._graph, A.edata["w"], B._graph)
        if dgl.backend.backend_name == "tensorflow":
237
            import tensorflow as tf
238

239
240
241
242
243
244
245
            C2 = tf.gather_nd(a_dense, tf.stack([B_row, B_col], 1))
        else:
            C2 = a_dense[B_row, B_col]
        assert F.allclose(C1, C2, rtol=1e-4, atol=1e-4)

        F.backward(F.reduce_sum(C1) + F.reduce_sum(C2))
        a_dense_grad = F.asnumpy(F.grad(a_dense))[A_row, A_col]
246
        A_spspmm_grad = F.asnumpy(F.grad(A.edata["w"]))
247
248
        assert np.allclose(a_dense_grad, A_spspmm_grad, rtol=1e-4, atol=1e-4)

249

250
if __name__ == "__main__":
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
    test_csrmm(F.int32, F.float32)
    test_csrmm(F.int64, F.float32)
    test_csrsum(F.int32, F.float32)
    test_csrsum(F.int64, F.float32)
    test_csrmask(F.int32, F.float32, 9000, 9000)
    test_csrmask(F.int64, F.float32, 9000, 0)
    test_csrmask(F.int32, F.float32, 0, 9000)
    test_csrmask(F.int64, F.float32, 0, 0)
    test_csrmm_backward(F.int32, F.float32, 1)
    test_csrmm_backward(F.int64, F.float32, 1)
    test_csrmm_backward(F.int32, F.float32, 2)
    test_csrmm_backward(F.int64, F.float32, 2)
    test_csrsum_backward(F.int32, F.float32, 1)
    test_csrsum_backward(F.int64, F.float32, 1)
    test_csrsum_backward(F.int32, F.float32, 2)
    test_csrsum_backward(F.int64, F.float32, 2)
    test_csrmask_backward(F.int32, F.float32)
    test_csrmask_backward(F.int64, F.float32)