test_heterograph.py 121 KB
Newer Older
1
2
3
4
5
6
7
import itertools
import multiprocessing as mp
import unittest
from collections import Counter

import backend as F

8
9
import dgl
import dgl.function as fn
10
import networkx as nx
11
import numpy as np
12
import pytest
13
import scipy.sparse as ssp
14
import test_utils
15
from dgl import DGLError
16
from scipy.sparse import rand
17
18
19
from test_utils import get_cases, parametrize_idtype
from utils import assert_is_identical_hetero

20

21
def create_test_heterograph(idtype):
22
    # test heterograph from the docstring, plus a user -- wishes -- game relation
Minjie Wang's avatar
Minjie Wang committed
23
24
25
26
27
28
    # 3 users, 2 games, 2 developers
    # metagraph:
    #    ('user', 'follows', 'user'),
    #    ('user', 'plays', 'game'),
    #    ('user', 'wishes', 'game'),
    #    ('developer', 'develops', 'game')])
29

30
31
32
33
34
35
36
37
38
39
    g = dgl.heterograph(
        {
            ("user", "follows", "user"): ([0, 1], [1, 2]),
            ("user", "plays", "game"): ([0, 1, 2, 1], [0, 0, 1, 1]),
            ("user", "wishes", "game"): ([0, 2], [1, 0]),
            ("developer", "develops", "game"): ([0, 1], [0, 1]),
        },
        idtype=idtype,
        device=F.ctx(),
    )
40
41
    assert g.idtype == idtype
    assert g.device == F.ctx()
42
43
    return g

44

45
def create_test_heterograph1(idtype):
Minjie Wang's avatar
Minjie Wang committed
46
    edges = []
47
48
49
50
51
    edges.extend([(0, 1), (1, 2)])  # follows
    edges.extend([(0, 3), (1, 3), (2, 4), (1, 4)])  # plays
    edges.extend([(0, 4), (2, 3)])  # wishes
    edges.extend([(5, 3), (6, 4)])  # develops
    edges = tuple(zip(*edges))
Minjie Wang's avatar
Minjie Wang committed
52
53
    ntypes = F.tensor([0, 0, 0, 1, 1, 2, 2])
    etypes = F.tensor([0, 0, 1, 1, 1, 1, 2, 2, 3, 3])
54
    g0 = dgl.graph(edges, idtype=idtype, device=F.ctx())
Minjie Wang's avatar
Minjie Wang committed
55
56
    g0.ndata[dgl.NTYPE] = ntypes
    g0.edata[dgl.ETYPE] = etypes
57
58
59
60
61
62
    return dgl.to_heterogeneous(
        g0,
        ["user", "game", "developer"],
        ["follows", "plays", "wishes", "develops"],
    )

Minjie Wang's avatar
Minjie Wang committed
63

64
def create_test_heterograph2(idtype):
65
66
67
68
69
70
71
72
73
74
75
76
77
    g = dgl.heterograph(
        {
            ("user", "follows", "user"): ([0, 1], [1, 2]),
            ("user", "plays", "game"): ([0, 1, 2, 1], [0, 0, 1, 1]),
            ("user", "wishes", "game"): ("csr", ([0, 1, 1, 2], [1, 0], [])),
            ("developer", "develops", "game"): (
                "csc",
                ([0, 1, 2], [0, 1], [0, 1]),
            ),
        },
        idtype=idtype,
        device=F.ctx(),
    )
78
79
    assert g.idtype == idtype
    assert g.device == F.ctx()
80
81
    return g

82

83
def create_test_heterograph3(idtype):
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
    g = dgl.heterograph(
        {
            ("user", "plays", "game"): (
                F.tensor([0, 1, 1, 2], dtype=idtype),
                F.tensor([0, 0, 1, 1], dtype=idtype),
            ),
            ("developer", "develops", "game"): (
                F.tensor([0, 1], dtype=idtype),
                F.tensor([0, 1], dtype=idtype),
            ),
        },
        idtype=idtype,
        device=F.ctx(),
    )

    g.nodes["user"].data["h"] = F.copy_to(
        F.tensor([1, 1, 1], dtype=idtype), ctx=F.ctx()
    )
    g.nodes["game"].data["h"] = F.copy_to(
        F.tensor([2, 2], dtype=idtype), ctx=F.ctx()
    )
    g.nodes["developer"].data["h"] = F.copy_to(
        F.tensor([3, 3], dtype=idtype), ctx=F.ctx()
    )
    g.edges["plays"].data["h"] = F.copy_to(
        F.tensor([1, 1, 1, 1], dtype=idtype), ctx=F.ctx()
    )
111
112
    return g

113

114
def create_test_heterograph4(idtype):
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
    g = dgl.heterograph(
        {
            ("user", "follows", "user"): (
                F.tensor([0, 1, 1, 2, 2, 2], dtype=idtype),
                F.tensor([0, 0, 1, 1, 2, 2], dtype=idtype),
            ),
            ("user", "plays", "game"): (
                F.tensor([0, 1], dtype=idtype),
                F.tensor([0, 1], dtype=idtype),
            ),
        },
        idtype=idtype,
        device=F.ctx(),
    )
    g.nodes["user"].data["h"] = F.copy_to(
        F.tensor([1, 1, 1], dtype=idtype), ctx=F.ctx()
    )
    g.nodes["game"].data["h"] = F.copy_to(
        F.tensor([2, 2], dtype=idtype), ctx=F.ctx()
    )
    g.edges["follows"].data["h"] = F.copy_to(
        F.tensor([1, 2, 3, 4, 5, 6], dtype=idtype), ctx=F.ctx()
    )
    g.edges["plays"].data["h"] = F.copy_to(
        F.tensor([1, 2], dtype=idtype), ctx=F.ctx()
    )
141
142
    return g

143

144
def create_test_heterograph5(idtype):
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
    g = dgl.heterograph(
        {
            ("user", "follows", "user"): (
                F.tensor([1, 2], dtype=idtype),
                F.tensor([0, 1], dtype=idtype),
            ),
            ("user", "plays", "game"): (
                F.tensor([0, 1], dtype=idtype),
                F.tensor([0, 1], dtype=idtype),
            ),
        },
        idtype=idtype,
        device=F.ctx(),
    )
    g.nodes["user"].data["h"] = F.copy_to(
        F.tensor([1, 1, 1], dtype=idtype), ctx=F.ctx()
    )
    g.nodes["game"].data["h"] = F.copy_to(
        F.tensor([2, 2], dtype=idtype), ctx=F.ctx()
    )
    g.edges["follows"].data["h"] = F.copy_to(
        F.tensor([1, 2], dtype=idtype), ctx=F.ctx()
    )
    g.edges["plays"].data["h"] = F.copy_to(
        F.tensor([1, 2], dtype=idtype), ctx=F.ctx()
    )
171
172
    return g

173

Minjie Wang's avatar
Minjie Wang committed
174
175
176
def get_redfn(name):
    return getattr(F, name)

177

nv-dlasalle's avatar
nv-dlasalle committed
178
@parametrize_idtype
179
180
181
182
183
def test_create(idtype):
    device = F.ctx()
    g0 = create_test_heterograph(idtype)
    g1 = create_test_heterograph1(idtype)
    g2 = create_test_heterograph2(idtype)
184
    assert set(g0.ntypes) == set(g1.ntypes) == set(g2.ntypes)
185
186
187
188
189
    assert (
        set(g0.canonical_etypes)
        == set(g1.canonical_etypes)
        == set(g2.canonical_etypes)
    )
Minjie Wang's avatar
Minjie Wang committed
190

191
192
193
194
195
    # Create a bipartite graph from a SciPy matrix
    src_ids = np.array([2, 3, 4])
    dst_ids = np.array([1, 2, 3])
    eweight = np.array([0.2, 0.3, 0.5])
    sp_mat = ssp.coo_matrix((eweight, (src_ids, dst_ids)))
196
197
198
199
200
201
202
203
    g = dgl.bipartite_from_scipy(
        sp_mat,
        utype="user",
        etype="plays",
        vtype="game",
        idtype=idtype,
        device=device,
    )
204
205
206
207
208
209
210
211
    assert g.idtype == idtype
    assert g.device == device
    assert g.num_src_nodes() == 5
    assert g.num_dst_nodes() == 4
    assert g.num_edges() == 3
    src, dst = g.edges()
    assert F.allclose(src, F.tensor([2, 3, 4], dtype=idtype))
    assert F.allclose(dst, F.tensor([1, 2, 3], dtype=idtype))
212
213
214
215
216
217
218
219
220
221
    g = dgl.bipartite_from_scipy(
        sp_mat,
        utype="_U",
        etype="_E",
        vtype="_V",
        eweight_name="w",
        idtype=idtype,
        device=device,
    )
    assert F.allclose(g.edata["w"], F.tensor(eweight))
222
223
224

    # Create a bipartite graph from a NetworkX graph
    nx_g = nx.DiGraph()
225
226
227
    nx_g.add_nodes_from(
        [1, 3], bipartite=0, feat1=np.zeros((2)), feat2=np.ones((2))
    )
228
229
230
    nx_g.add_nodes_from([2, 4, 5], bipartite=1, feat3=np.zeros((3)))
    nx_g.add_edge(1, 4, weight=np.ones((1)), eid=np.array([1]))
    nx_g.add_edge(3, 5, weight=np.ones((1)), eid=np.array([0]))
231
232
233
234
235
236
237
238
    g = dgl.bipartite_from_networkx(
        nx_g,
        utype="user",
        etype="plays",
        vtype="game",
        idtype=idtype,
        device=device,
    )
239
240
    assert g.idtype == idtype
    assert g.device == device
241
242
243
244
245
246
    assert g.num_src_nodes() == 2
    assert g.num_dst_nodes() == 3
    assert g.num_edges() == 2
    src, dst = g.edges()
    assert F.allclose(src, F.tensor([0, 1], dtype=idtype))
    assert F.allclose(dst, F.tensor([1, 2], dtype=idtype))
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
    g = dgl.bipartite_from_networkx(
        nx_g,
        utype="_U",
        etype="_E",
        vtype="V",
        u_attrs=["feat1", "feat2"],
        e_attrs=["weight"],
        v_attrs=["feat3"],
    )
    assert F.allclose(g.srcdata["feat1"], F.tensor(np.zeros((2, 2))))
    assert F.allclose(g.srcdata["feat2"], F.tensor(np.ones((2, 2))))
    assert F.allclose(g.dstdata["feat3"], F.tensor(np.zeros((3, 3))))
    assert F.allclose(g.edata["weight"], F.tensor(np.ones((2, 1))))
    g = dgl.bipartite_from_networkx(
        nx_g,
        utype="_U",
        etype="_E",
        vtype="V",
        edge_id_attr_name="eid",
        idtype=idtype,
        device=device,
    )
269
270
271
    src, dst = g.edges()
    assert F.allclose(src, F.tensor([1, 0], dtype=idtype))
    assert F.allclose(dst, F.tensor([2, 1], dtype=idtype))
Minjie Wang's avatar
Minjie Wang committed
272
273

    # create from scipy
274
    spmat = ssp.coo_matrix(([1, 1, 1], ([0, 0, 1], [2, 3, 2])), shape=(4, 4))
275
276
277
    g = dgl.from_scipy(spmat, idtype=idtype, device=device)
    assert g.num_nodes() == 4
    assert g.num_edges() == 3
278
279
    assert g.idtype == idtype
    assert g.device == device
Minjie Wang's avatar
Minjie Wang committed
280

281
    # test inferring number of nodes for heterograph
282
283
284
285
286
287
288
289
290
291
292
293
    g = dgl.heterograph(
        {
            ("l0", "e0", "l1"): ([0, 0], [1, 2]),
            ("l0", "e1", "l2"): ([2], [2]),
            ("l2", "e2", "l2"): ([1, 3], [1, 3]),
        },
        idtype=idtype,
        device=device,
    )
    assert g.num_nodes("l0") == 3
    assert g.num_nodes("l1") == 3
    assert g.num_nodes("l2") == 4
294
295
    assert g.idtype == idtype
    assert g.device == device
296

297
298
    # test if validate flag works
    # homo graph
299
    with pytest.raises(DGLError):
300
301
        g = dgl.graph(
            ([0, 0, 0, 1, 1, 2], [0, 1, 2, 0, 1, 2]),
302
            num_nodes=2,
303
304
            idtype=idtype,
            device=device,
305
        )
306

307
308
    # bipartite graph
    def _test_validate_bipartite(card):
309
        with pytest.raises(DGLError):
310
311
312
313
314
315
            g = dgl.heterograph(
                {("_U", "_E", "_V"): ([0, 0, 1, 1, 2], [1, 1, 2, 2, 3])},
                {"_U": card[0], "_V": card[1]},
                idtype=idtype,
                device=device,
            )
316
317
318
319

    _test_validate_bipartite((3, 3))
    _test_validate_bipartite((2, 4))

320
321
322
    # test from_scipy
    num_nodes = 10
    density = 0.25
323
    for fmt in ["csr", "coo", "csc"]:
324
        adj = rand(num_nodes, num_nodes, density=density, format=fmt)
325
        g = dgl.from_scipy(adj, eweight_name="w", idtype=idtype)
326
327
        assert g.idtype == idtype
        assert g.device == F.cpu()
328
329
330
331
        assert F.array_equal(
            g.edata["w"], F.copy_to(F.tensor(adj.data), F.cpu())
        )

332

333
334
335
336
337
338
339
340
def test_create2():
    mat = ssp.random(20, 30, 0.1)

    # coo
    mat = mat.tocoo()
    row = F.tensor(mat.row, dtype=F.int64)
    col = F.tensor(mat.col, dtype=F.int64)
    g = dgl.heterograph(
341
342
343
        {("A", "AB", "B"): ("coo", (row, col))},
        num_nodes_dict={"A": 20, "B": 30},
    )
344
345
346
347
348
349
350

    # csr
    mat = mat.tocsr()
    indptr = F.tensor(mat.indptr, dtype=F.int64)
    indices = F.tensor(mat.indices, dtype=F.int64)
    data = F.tensor([], dtype=F.int64)
    g = dgl.heterograph(
351
352
353
        {("A", "AB", "B"): ("csr", (indptr, indices, data))},
        num_nodes_dict={"A": 20, "B": 30},
    )
354
355
356
357
358
359
360

    # csc
    mat = mat.tocsc()
    indptr = F.tensor(mat.indptr, dtype=F.int64)
    indices = F.tensor(mat.indices, dtype=F.int64)
    data = F.tensor([], dtype=F.int64)
    g = dgl.heterograph(
361
362
363
364
        {("A", "AB", "B"): ("csc", (indptr, indices, data))},
        num_nodes_dict={"A": 20, "B": 30},
    )

365

nv-dlasalle's avatar
nv-dlasalle committed
366
@parametrize_idtype
367
368
def test_query(idtype):
    g = create_test_heterograph(idtype)
369

370
    ntypes = ["user", "game", "developer"]
Minjie Wang's avatar
Minjie Wang committed
371
    canonical_etypes = [
372
373
374
375
376
377
        ("user", "follows", "user"),
        ("user", "plays", "game"),
        ("user", "wishes", "game"),
        ("developer", "develops", "game"),
    ]
    etypes = ["follows", "plays", "wishes", "develops"]
378
379

    # node & edge types
Minjie Wang's avatar
Minjie Wang committed
380
381
382
    assert set(ntypes) == set(g.ntypes)
    assert set(etypes) == set(g.etypes)
    assert set(canonical_etypes) == set(g.canonical_etypes)
383
384

    # metagraph
385
    mg = g.metagraph()
Minjie Wang's avatar
Minjie Wang committed
386
    assert set(g.ntypes) == set(mg.nodes)
387
    etype_triplets = [(u, v, e) for u, v, e in mg.edges(keys=True)]
388
389
390
391
392
393
394
395
    assert set(
        [
            ("user", "user", "follows"),
            ("user", "game", "plays"),
            ("user", "game", "wishes"),
            ("developer", "game", "develops"),
        ]
    ) == set(etype_triplets)
Minjie Wang's avatar
Minjie Wang committed
396
397
    for i in range(len(etypes)):
        assert g.to_canonical_etype(etypes[i]) == canonical_etypes[i]
398

399
400
    def _test(g):
        # number of nodes
401
        assert [g.num_nodes(ntype) for ntype in ntypes] == [3, 2, 2]
402

403
        # number of edges
404
        assert [g.num_edges(etype) for etype in etypes] == [2, 4, 2, 2]
405

406
        # has_nodes
407
408
409
        for ntype in ntypes:
            n = g.number_of_nodes(ntype)
            for i in range(n):
410
411
                assert g.has_nodes(i, ntype)
            assert not g.has_nodes(n, ntype)
412
            assert np.array_equal(
413
414
                F.asnumpy(g.has_nodes([0, n], ntype)).astype("int32"), [1, 0]
            )
Minjie Wang's avatar
Minjie Wang committed
415

416
        assert not g.is_multigraph
Minjie Wang's avatar
Minjie Wang committed
417
418
419
420

        for etype in etypes:
            srcs, dsts = edges[etype]
            for src, dst in zip(srcs, dsts):
421
                assert g.has_edges_between(src, dst, etype)
Minjie Wang's avatar
Minjie Wang committed
422
423
424
425
            assert F.asnumpy(g.has_edges_between(srcs, dsts, etype)).all()

            srcs, dsts = negative_edges[etype]
            for src, dst in zip(srcs, dsts):
426
                assert not g.has_edges_between(src, dst, etype)
Minjie Wang's avatar
Minjie Wang committed
427
428
429
430
431
432
433
            assert not F.asnumpy(g.has_edges_between(srcs, dsts, etype)).any()

            srcs, dsts = edges[etype]
            n_edges = len(srcs)

            # predecessors & in_edges & in_degree
            pred = [s for s, d in zip(srcs, dsts) if d == 0]
434
435
436
            assert set(F.asnumpy(g.predecessors(0, etype)).tolist()) == set(
                pred
            )
Minjie Wang's avatar
Minjie Wang committed
437
438
439
            u, v = g.in_edges([0], etype=etype)
            assert F.asnumpy(v).tolist() == [0] * len(pred)
            assert set(F.asnumpy(u).tolist()) == set(pred)
440
            assert g.in_degrees(0, etype) == len(pred)
Minjie Wang's avatar
Minjie Wang committed
441
442
443
444
445
446
447

            # successors & out_edges & out_degree
            succ = [d for s, d in zip(srcs, dsts) if s == 0]
            assert set(F.asnumpy(g.successors(0, etype)).tolist()) == set(succ)
            u, v = g.out_edges([0], etype=etype)
            assert F.asnumpy(u).tolist() == [0] * len(succ)
            assert set(F.asnumpy(v).tolist()) == set(succ)
448
            assert g.out_degrees(0, etype) == len(succ)
Minjie Wang's avatar
Minjie Wang committed
449

450
            # edge_ids
Minjie Wang's avatar
Minjie Wang committed
451
            for i, (src, dst) in enumerate(zip(srcs, dsts)):
452
453
454
                assert g.edge_ids(src, dst, etype=etype) == i
                _, _, eid = g.edge_ids(src, dst, etype=etype, return_uv=True)
                assert eid == i
455
456
457
            assert F.asnumpy(
                g.edge_ids(srcs, dsts, etype=etype)
            ).tolist() == list(range(n_edges))
458
            u, v, e = g.edge_ids(srcs, dsts, etype=etype, return_uv=True)
459
460
461
            u, v, e = F.asnumpy(u), F.asnumpy(v), F.asnumpy(e)
            assert u[e].tolist() == srcs
            assert v[e].tolist() == dsts
462

Minjie Wang's avatar
Minjie Wang committed
463
            # find_edges
464
465
466
467
468
            for eid in [
                list(range(n_edges)),
                np.arange(n_edges),
                F.astype(F.arange(0, n_edges), g.idtype),
            ]:
469
                u, v = g.find_edges(eid, etype)
470
471
                assert F.asnumpy(u).tolist() == srcs
                assert F.asnumpy(v).tolist() == dsts
Minjie Wang's avatar
Minjie Wang committed
472
473

            # all_edges.
474
475
            for order in ["eid"]:
                u, v, e = g.edges("all", order, etype)
Minjie Wang's avatar
Minjie Wang committed
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
                assert F.asnumpy(u).tolist() == srcs
                assert F.asnumpy(v).tolist() == dsts
                assert F.asnumpy(e).tolist() == list(range(n_edges))

            # in_degrees & out_degrees
            in_degrees = F.asnumpy(g.in_degrees(etype=etype))
            out_degrees = F.asnumpy(g.out_degrees(etype=etype))
            src_count = Counter(srcs)
            dst_count = Counter(dsts)
            utype, _, vtype = g.to_canonical_etype(etype)
            for i in range(g.number_of_nodes(utype)):
                assert out_degrees[i] == src_count[i]
            for i in range(g.number_of_nodes(vtype)):
                assert in_degrees[i] == dst_count[i]

    edges = {
492
493
494
495
        "follows": ([0, 1], [1, 2]),
        "plays": ([0, 1, 2, 1], [0, 0, 1, 1]),
        "wishes": ([0, 2], [1, 0]),
        "develops": ([0, 1], [0, 1]),
Minjie Wang's avatar
Minjie Wang committed
496
497
498
    }
    # edges that does not exist in the graph
    negative_edges = {
499
500
501
502
        "follows": ([0, 1], [0, 1]),
        "plays": ([0, 2], [1, 0]),
        "wishes": ([0, 1], [0, 1]),
        "develops": ([0, 1], [1, 0]),
Minjie Wang's avatar
Minjie Wang committed
503
    }
504
    g = create_test_heterograph(idtype)
Minjie Wang's avatar
Minjie Wang committed
505
    _test(g)
506
    g = create_test_heterograph1(idtype)
507
    _test(g)
508
    if F._default_context_str != "gpu":
509
        # XXX: CUDA COO operators have not been live yet.
510
        g = create_test_heterograph2(idtype)
511
        _test(g)
Minjie Wang's avatar
Minjie Wang committed
512
513
514

    etypes = canonical_etypes
    edges = {
515
516
517
518
        ("user", "follows", "user"): ([0, 1], [1, 2]),
        ("user", "plays", "game"): ([0, 1, 2, 1], [0, 0, 1, 1]),
        ("user", "wishes", "game"): ([0, 2], [1, 0]),
        ("developer", "develops", "game"): ([0, 1], [0, 1]),
Minjie Wang's avatar
Minjie Wang committed
519
520
521
    }
    # edges that does not exist in the graph
    negative_edges = {
522
523
524
525
526
        ("user", "follows", "user"): ([0, 1], [0, 1]),
        ("user", "plays", "game"): ([0, 2], [1, 0]),
        ("user", "wishes", "game"): ([0, 1], [0, 1]),
        ("developer", "develops", "game"): ([0, 1], [1, 0]),
    }
527
    g = create_test_heterograph(idtype)
Minjie Wang's avatar
Minjie Wang committed
528
    _test(g)
529
    g = create_test_heterograph1(idtype)
530
    _test(g)
531
    if F._default_context_str != "gpu":
532
        # XXX: CUDA COO operators have not been live yet.
533
        g = create_test_heterograph2(idtype)
534
        _test(g)
Minjie Wang's avatar
Minjie Wang committed
535
536
537
538

    # test repr
    print(g)

539

nv-dlasalle's avatar
nv-dlasalle committed
540
@parametrize_idtype
541
542
543
544
545
546
547
548
549
550
551
552
def test_empty_query(idtype):
    g = dgl.graph(([1, 2, 3], [0, 4, 5]), idtype=idtype, device=F.ctx())
    g.add_nodes(0)
    g.add_edges([], [])
    g.remove_edges([])
    g.remove_nodes([])
    assert F.shape(g.has_nodes([])) == (0,)
    assert F.shape(g.has_edges_between([], [])) == (0,)
    g.edge_ids([], [])
    g.edge_ids([], [], return_uv=True)
    g.find_edges([])

553
554
    assert F.shape(g.in_edges([], form="eid")) == (0,)
    u, v = g.in_edges([], form="uv")
555
556
    assert F.shape(u) == (0,)
    assert F.shape(v) == (0,)
557
    u, v, e = g.in_edges([], form="all")
558
559
560
561
    assert F.shape(u) == (0,)
    assert F.shape(v) == (0,)
    assert F.shape(e) == (0,)

562
563
    assert F.shape(g.out_edges([], form="eid")) == (0,)
    u, v = g.out_edges([], form="uv")
564
565
    assert F.shape(u) == (0,)
    assert F.shape(v) == (0,)
566
    u, v, e = g.out_edges([], form="all")
567
568
569
570
571
572
573
    assert F.shape(u) == (0,)
    assert F.shape(v) == (0,)
    assert F.shape(e) == (0,)

    assert F.shape(g.in_degrees([])) == (0,)
    assert F.shape(g.out_degrees([])) == (0,)

Quan (Andy) Gan's avatar
Quan (Andy) Gan committed
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
    g = dgl.graph(([], []), idtype=idtype, device=F.ctx())
    error_thrown = True
    try:
        g.in_degrees([0])
        fail = False
    except:
        pass
    assert error_thrown
    error_thrown = True
    try:
        g.out_degrees([0])
        fail = False
    except:
        pass
    assert error_thrown

590
591
592
593

@unittest.skipIf(
    F._default_context_str == "gpu", reason="GPU does not have COO impl."
)
594
def _test_hypersparse():
595
    N1 = 1 << 50  # should crash if allocated a CSR
596
597
    N2 = 1 << 48

598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
    g = dgl.heterograph(
        {
            ("user", "follows", "user"): (
                F.tensor([0], F.int64),
                F.tensor([1], F.int64),
            ),
            ("user", "plays", "game"): (
                F.tensor([0], F.int64),
                F.tensor([N2], F.int64),
            ),
        },
        {"user": N1, "game": N1},
        device=F.ctx(),
    )
    assert g.number_of_nodes("user") == N1
    assert g.number_of_nodes("game") == N1
    assert g.number_of_edges("follows") == 1
    assert g.number_of_edges("plays") == 1

    assert g.has_edges_between(0, 1, "follows")
    assert not g.has_edges_between(0, 0, "follows")
    mask = F.asnumpy(g.has_edges_between([0, 0], [0, 1], "follows")).tolist()
620
621
    assert mask == [0, 1]

622
623
624
    assert g.has_edges_between(0, N2, "plays")
    assert not g.has_edges_between(0, 0, "plays")
    mask = F.asnumpy(g.has_edges_between([0, 0], [0, N2], "plays")).tolist()
625
626
    assert mask == [0, 1]

627
628
629
630
    assert F.asnumpy(g.predecessors(0, "follows")).tolist() == []
    assert F.asnumpy(g.successors(0, "follows")).tolist() == [1]
    assert F.asnumpy(g.predecessors(1, "follows")).tolist() == [0]
    assert F.asnumpy(g.successors(1, "follows")).tolist() == []
631

632
633
634
635
    assert F.asnumpy(g.predecessors(0, "plays")).tolist() == []
    assert F.asnumpy(g.successors(0, "plays")).tolist() == [N2]
    assert F.asnumpy(g.predecessors(N2, "plays")).tolist() == [0]
    assert F.asnumpy(g.successors(N2, "plays")).tolist() == []
636

637
638
    assert g.edge_ids(0, 1, etype="follows") == 0
    assert g.edge_ids(0, N2, etype="plays") == 0
639

640
    u, v = g.find_edges([0], "follows")
641
642
    assert F.asnumpy(u).tolist() == [0]
    assert F.asnumpy(v).tolist() == [1]
643
    u, v = g.find_edges([0], "plays")
644
645
    assert F.asnumpy(u).tolist() == [0]
    assert F.asnumpy(v).tolist() == [N2]
646
    u, v, e = g.all_edges("all", "eid", "follows")
647
648
649
    assert F.asnumpy(u).tolist() == [0]
    assert F.asnumpy(v).tolist() == [1]
    assert F.asnumpy(e).tolist() == [0]
650
    u, v, e = g.all_edges("all", "eid", "plays")
651
652
653
654
    assert F.asnumpy(u).tolist() == [0]
    assert F.asnumpy(v).tolist() == [N2]
    assert F.asnumpy(e).tolist() == [0]

655
656
657
658
659
660
661
662
663
664
665
666
667
    assert g.in_degrees(0, "follows") == 0
    assert g.in_degrees(1, "follows") == 1
    assert F.asnumpy(g.in_degrees([0, 1], "follows")).tolist() == [0, 1]
    assert g.in_degrees(0, "plays") == 0
    assert g.in_degrees(N2, "plays") == 1
    assert F.asnumpy(g.in_degrees([0, N2], "plays")).tolist() == [0, 1]
    assert g.out_degrees(0, "follows") == 1
    assert g.out_degrees(1, "follows") == 0
    assert F.asnumpy(g.out_degrees([0, 1], "follows")).tolist() == [1, 0]
    assert g.out_degrees(0, "plays") == 1
    assert g.out_degrees(N2, "plays") == 0
    assert F.asnumpy(g.out_degrees([0, N2], "plays")).tolist() == [1, 0]

668

669
def _test_edge_ids():
670
    N1 = 1 << 50  # should crash if allocated a CSR
671
672
    N2 = 1 << 48

673
674
675
676
677
678
679
680
681
682
683
684
685
    g = dgl.heterograph(
        {
            ("user", "follows", "user"): (
                F.tensor([0], F.int64),
                F.tensor([1], F.int64),
            ),
            ("user", "plays", "game"): (
                F.tensor([0], F.int64),
                F.tensor([N2], F.int64),
            ),
        },
        {"user": N1, "game": N1},
    )
686
    with pytest.raises(DGLError):
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
        eid = g.edge_ids(0, 0, etype="follows")

    g2 = dgl.heterograph(
        {
            ("user", "follows", "user"): (
                F.tensor([0, 0], F.int64),
                F.tensor([1, 1], F.int64),
            ),
            ("user", "plays", "game"): (
                F.tensor([0], F.int64),
                F.tensor([N2], F.int64),
            ),
        },
        {"user": N1, "game": N1},
        device=F.cpu(),
    )

    eid = g2.edge_ids(0, 1, etype="follows")
705
    assert eid == 0
706

707

nv-dlasalle's avatar
nv-dlasalle committed
708
@parametrize_idtype
709
710
def test_adj(idtype):
    g = create_test_heterograph(idtype)
711
    adj = F.sparse_to_numpy(g.adj(transpose=True, etype="follows"))
Minjie Wang's avatar
Minjie Wang committed
712
    assert np.allclose(
713
714
715
        adj, np.array([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]])
    )
    adj = F.sparse_to_numpy(g.adj(transpose=False, etype="follows"))
Minjie Wang's avatar
Minjie Wang committed
716
    assert np.allclose(
717
718
719
720
721
722
723
724
        adj, np.array([[0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [0.0, 0.0, 0.0]])
    )
    adj = F.sparse_to_numpy(g.adj(transpose=True, etype="plays"))
    assert np.allclose(adj, np.array([[1.0, 1.0, 0.0], [0.0, 1.0, 1.0]]))
    adj = F.sparse_to_numpy(g.adj(transpose=False, etype="plays"))
    assert np.allclose(adj, np.array([[1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]))

    adj = g.adj(transpose=True, scipy_fmt="csr", etype="follows")
Minjie Wang's avatar
Minjie Wang committed
725
    assert np.allclose(
726
727
728
729
        adj.todense(),
        np.array([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]),
    )
    adj = g.adj(transpose=True, scipy_fmt="coo", etype="follows")
Minjie Wang's avatar
Minjie Wang committed
730
    assert np.allclose(
731
732
733
734
        adj.todense(),
        np.array([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]),
    )
    adj = g.adj(transpose=True, scipy_fmt="csr", etype="plays")
Minjie Wang's avatar
Minjie Wang committed
735
    assert np.allclose(
736
737
738
        adj.todense(), np.array([[1.0, 1.0, 0.0], [0.0, 1.0, 1.0]])
    )
    adj = g.adj(transpose=True, scipy_fmt="coo", etype="plays")
Minjie Wang's avatar
Minjie Wang committed
739
    assert np.allclose(
740
741
742
        adj.todense(), np.array([[1.0, 1.0, 0.0], [0.0, 1.0, 1.0]])
    )
    adj = F.sparse_to_numpy(g["follows"].adj(transpose=True))
Minjie Wang's avatar
Minjie Wang committed
743
    assert np.allclose(
744
745
746
        adj, np.array([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]])
    )

Minjie Wang's avatar
Minjie Wang committed
747

nv-dlasalle's avatar
nv-dlasalle committed
748
@parametrize_idtype
749
750
def test_inc(idtype):
    g = create_test_heterograph(idtype)
751
752
753
754
755
756
757
    adj = F.sparse_to_numpy(g["follows"].inc("in"))
    assert np.allclose(adj, np.array([[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]]))
    adj = F.sparse_to_numpy(g["follows"].inc("out"))
    assert np.allclose(adj, np.array([[1.0, 0.0], [0.0, 1.0], [0.0, 0.0]]))
    adj = F.sparse_to_numpy(g["follows"].inc("both"))
    assert np.allclose(adj, np.array([[-1.0, 0.0], [1.0, -1.0], [0.0, 1.0]]))
    adj = F.sparse_to_numpy(g.inc("in", etype="plays"))
Minjie Wang's avatar
Minjie Wang committed
758
    assert np.allclose(
759
760
761
        adj, np.array([[1.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 1.0]])
    )
    adj = F.sparse_to_numpy(g.inc("out", etype="plays"))
Minjie Wang's avatar
Minjie Wang committed
762
    assert np.allclose(
763
764
765
766
767
768
769
770
        adj,
        np.array(
            [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 1.0], [0.0, 0.0, 1.0, 0.0]]
        ),
    )
    adj = F.sparse_to_numpy(g.inc("both", etype="follows"))
    assert np.allclose(adj, np.array([[-1.0, 0.0], [1.0, -1.0], [0.0, 1.0]]))

771

nv-dlasalle's avatar
nv-dlasalle committed
772
@parametrize_idtype
773
def test_view(idtype):
774
    # test single node type
775
776
777
778
779
    g = dgl.heterograph(
        {("user", "follows", "user"): ([0, 1], [1, 2])},
        idtype=idtype,
        device=F.ctx(),
    )
780
    f1 = F.randn((3, 6))
781
782
    g.ndata["h"] = f1
    f2 = g.nodes["user"].data["h"]
783
784
785
    assert F.array_equal(f1, f2)
    fail = False
    try:
786
        g.ndata["h"] = {"user": f1}
787
788
789
790
791
792
    except Exception:
        fail = True
    assert fail

    # test single edge type
    f3 = F.randn((2, 4))
793
794
    g.edata["h"] = f3
    f4 = g.edges["follows"].data["h"]
795
796
797
    assert F.array_equal(f3, f4)
    fail = False
    try:
798
        g.edata["h"] = {"follows": f3}
799
800
801
802
    except Exception:
        fail = True
    assert fail

Minjie Wang's avatar
Minjie Wang committed
803
    # test data view
804
    g = create_test_heterograph(idtype)
805
806

    f1 = F.randn((3, 6))
807
808
    g.nodes["user"].data["h"] = f1  # ok
    f2 = g.nodes["user"].data["h"]
809
    assert F.array_equal(f1, f2)
810
811
    assert F.array_equal(g.nodes("user"), F.arange(0, 3, idtype))
    g.nodes["user"].data.pop("h")
812
813
814
815
816
817

    # multi type ndata
    f1 = F.randn((3, 6))
    f2 = F.randn((2, 6))
    fail = False
    try:
818
        g.ndata["h"] = f1
819
820
821
    except Exception:
        fail = True
    assert fail
822
823

    f3 = F.randn((2, 4))
824
825
826
    g.edges["user", "follows", "user"].data["h"] = f3
    f4 = g.edges["user", "follows", "user"].data["h"]
    f5 = g.edges["follows"].data["h"]
827
    assert F.array_equal(f3, f4)
Minjie Wang's avatar
Minjie Wang committed
828
    assert F.array_equal(f3, f5)
829
830
831
832
    assert F.array_equal(
        g.edges(etype="follows", form="eid"), F.arange(0, 2, idtype)
    )
    g.edges["follows"].data.pop("h")
833
834
835
836

    f3 = F.randn((2, 4))
    fail = False
    try:
837
        g.edata["h"] = f3
838
839
840
841
842
843
    except Exception:
        fail = True
    assert fail

    # test srcdata
    f1 = F.randn((3, 6))
844
845
    g.srcnodes["user"].data["h"] = f1  # ok
    f2 = g.srcnodes["user"].data["h"]
846
    assert F.array_equal(f1, f2)
847
848
    assert F.array_equal(g.srcnodes("user"), F.arange(0, 3, idtype))
    g.srcnodes["user"].data.pop("h")
849
850
851

    # test dstdata
    f1 = F.randn((3, 6))
852
853
    g.dstnodes["user"].data["h"] = f1  # ok
    f2 = g.dstnodes["user"].data["h"]
854
    assert F.array_equal(f1, f2)
855
856
857
    assert F.array_equal(g.dstnodes("user"), F.arange(0, 3, idtype))
    g.dstnodes["user"].data.pop("h")

858

nv-dlasalle's avatar
nv-dlasalle committed
859
@parametrize_idtype
860
def test_view1(idtype):
Minjie Wang's avatar
Minjie Wang committed
861
    # test relation view
862
    HG = create_test_heterograph(idtype)
863
    ntypes = ["user", "game", "developer"]
Minjie Wang's avatar
Minjie Wang committed
864
    canonical_etypes = [
865
866
867
868
869
870
        ("user", "follows", "user"),
        ("user", "plays", "game"),
        ("user", "wishes", "game"),
        ("developer", "develops", "game"),
    ]
    etypes = ["follows", "plays", "wishes", "develops"]
Minjie Wang's avatar
Minjie Wang committed
871
872
873
874
875
876
877

    def _test_query():
        for etype in etypes:
            utype, _, vtype = HG.to_canonical_etype(etype)
            g = HG[etype]
            srcs, dsts = edges[etype]
            for src, dst in zip(srcs, dsts):
878
                assert g.has_edges_between(src, dst)
Minjie Wang's avatar
Minjie Wang committed
879
880
881
882
            assert F.asnumpy(g.has_edges_between(srcs, dsts)).all()

            srcs, dsts = negative_edges[etype]
            for src, dst in zip(srcs, dsts):
883
                assert not g.has_edges_between(src, dst)
Minjie Wang's avatar
Minjie Wang committed
884
885
886
887
888
889
890
891
892
893
894
            assert not F.asnumpy(g.has_edges_between(srcs, dsts)).any()

            srcs, dsts = edges[etype]
            n_edges = len(srcs)

            # predecessors & in_edges & in_degree
            pred = [s for s, d in zip(srcs, dsts) if d == 0]
            assert set(F.asnumpy(g.predecessors(0)).tolist()) == set(pred)
            u, v = g.in_edges([0])
            assert F.asnumpy(v).tolist() == [0] * len(pred)
            assert set(F.asnumpy(u).tolist()) == set(pred)
895
            assert g.in_degrees(0) == len(pred)
Minjie Wang's avatar
Minjie Wang committed
896
897
898
899
900
901
902

            # successors & out_edges & out_degree
            succ = [d for s, d in zip(srcs, dsts) if s == 0]
            assert set(F.asnumpy(g.successors(0)).tolist()) == set(succ)
            u, v = g.out_edges([0])
            assert F.asnumpy(u).tolist() == [0] * len(succ)
            assert set(F.asnumpy(v).tolist()) == set(succ)
903
            assert g.out_degrees(0) == len(succ)
Minjie Wang's avatar
Minjie Wang committed
904

905
            # edge_ids
Minjie Wang's avatar
Minjie Wang committed
906
            for i, (src, dst) in enumerate(zip(srcs, dsts)):
907
908
909
                assert g.edge_ids(src, dst, etype=etype) == i
                _, _, eid = g.edge_ids(src, dst, etype=etype, return_uv=True)
                assert eid == i
910
911
912
            assert F.asnumpy(g.edge_ids(srcs, dsts)).tolist() == list(
                range(n_edges)
            )
913
            u, v, e = g.edge_ids(srcs, dsts, return_uv=True)
914
915
916
            u, v, e = F.asnumpy(u), F.asnumpy(v), F.asnumpy(e)
            assert u[e].tolist() == srcs
            assert v[e].tolist() == dsts
Minjie Wang's avatar
Minjie Wang committed
917
918
919
920
921
922
923

            # find_edges
            u, v = g.find_edges(list(range(n_edges)))
            assert F.asnumpy(u).tolist() == srcs
            assert F.asnumpy(v).tolist() == dsts

            # all_edges.
924
925
            for order in ["eid"]:
                u, v, e = g.all_edges(form="all", order=order)
Minjie Wang's avatar
Minjie Wang committed
926
927
928
929
930
931
932
933
934
935
936
937
                assert F.asnumpy(u).tolist() == srcs
                assert F.asnumpy(v).tolist() == dsts
                assert F.asnumpy(e).tolist() == list(range(n_edges))

            # in_degrees & out_degrees
            in_degrees = F.asnumpy(g.in_degrees())
            out_degrees = F.asnumpy(g.out_degrees())
            src_count = Counter(srcs)
            dst_count = Counter(dsts)
            for i in range(g.number_of_nodes(utype)):
                assert out_degrees[i] == src_count[i]
            for i in range(g.number_of_nodes(vtype)):
938
                assert in_degrees[i] == dst_count[i]
Minjie Wang's avatar
Minjie Wang committed
939
940

    edges = {
941
942
943
944
        "follows": ([0, 1], [1, 2]),
        "plays": ([0, 1, 2, 1], [0, 0, 1, 1]),
        "wishes": ([0, 2], [1, 0]),
        "develops": ([0, 1], [0, 1]),
Minjie Wang's avatar
Minjie Wang committed
945
946
947
    }
    # edges that does not exist in the graph
    negative_edges = {
948
949
950
951
        "follows": ([0, 1], [0, 1]),
        "plays": ([0, 2], [1, 0]),
        "wishes": ([0, 1], [0, 1]),
        "develops": ([0, 1], [1, 0]),
Minjie Wang's avatar
Minjie Wang committed
952
953
954
955
    }
    _test_query()
    etypes = canonical_etypes
    edges = {
956
957
958
959
        ("user", "follows", "user"): ([0, 1], [1, 2]),
        ("user", "plays", "game"): ([0, 1, 2, 1], [0, 0, 1, 1]),
        ("user", "wishes", "game"): ([0, 2], [1, 0]),
        ("developer", "develops", "game"): ([0, 1], [0, 1]),
Minjie Wang's avatar
Minjie Wang committed
960
961
962
    }
    # edges that does not exist in the graph
    negative_edges = {
963
964
965
966
967
        ("user", "follows", "user"): ([0, 1], [0, 1]),
        ("user", "plays", "game"): ([0, 2], [1, 0]),
        ("user", "wishes", "game"): ([0, 1], [0, 1]),
        ("developer", "develops", "game"): ([0, 1], [1, 0]),
    }
Minjie Wang's avatar
Minjie Wang committed
968
969
970
    _test_query()

    # test features
971
972
    HG.nodes["user"].data["h"] = F.ones((HG.number_of_nodes("user"), 5))
    HG.nodes["game"].data["m"] = F.ones((HG.number_of_nodes("game"), 3)) * 2
Minjie Wang's avatar
Minjie Wang committed
973
974

    # test only one node type
975
    g = HG["follows"]
Minjie Wang's avatar
Minjie Wang committed
976
977
978
979
    assert g.number_of_nodes() == 3

    # test ndata and edata
    f1 = F.randn((3, 6))
980
981
    g.ndata["h"] = f1  # ok
    f2 = HG.nodes["user"].data["h"]
Minjie Wang's avatar
Minjie Wang committed
982
    assert F.array_equal(f1, f2)
983
    assert F.array_equal(g.nodes(), F.arange(0, 3, g.idtype))
Minjie Wang's avatar
Minjie Wang committed
984
985

    f3 = F.randn((2, 4))
986
987
    g.edata["h"] = f3
    f4 = HG.edges["follows"].data["h"]
Minjie Wang's avatar
Minjie Wang committed
988
    assert F.array_equal(f3, f4)
989
990
    assert F.array_equal(g.edges(form="eid"), F.arange(0, 2, g.idtype))

Minjie Wang's avatar
Minjie Wang committed
991

nv-dlasalle's avatar
nv-dlasalle committed
992
@parametrize_idtype
993
def test_flatten(idtype):
Minjie Wang's avatar
Minjie Wang committed
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
    def check_mapping(g, fg):
        if len(fg.ntypes) == 1:
            SRC = DST = fg.ntypes[0]
        else:
            SRC = fg.ntypes[0]
            DST = fg.ntypes[1]

        etypes = F.asnumpy(fg.edata[dgl.ETYPE]).tolist()
        eids = F.asnumpy(fg.edata[dgl.EID]).tolist()

        for i, (etype, eid) in enumerate(zip(etypes, eids)):
            src_g, dst_g = g.find_edges([eid], g.canonical_etypes[etype])
            src_fg, dst_fg = fg.find_edges([i])
            # TODO(gq): I feel this code is quite redundant; can we just add new members (like
            # "induced_srcid") to returned heterograph object and not store them as features?
1009
1010
1011
1012
1013
1014
            assert F.asnumpy(src_g) == F.asnumpy(
                F.gather_row(fg.nodes[SRC].data[dgl.NID], src_fg)[0]
            )
            tid = F.asnumpy(
                F.gather_row(fg.nodes[SRC].data[dgl.NTYPE], src_fg)
            ).item()
Minjie Wang's avatar
Minjie Wang committed
1015
            assert g.canonical_etypes[etype][0] == g.ntypes[tid]
1016
1017
1018
1019
1020
1021
            assert F.asnumpy(dst_g) == F.asnumpy(
                F.gather_row(fg.nodes[DST].data[dgl.NID], dst_fg)[0]
            )
            tid = F.asnumpy(
                F.gather_row(fg.nodes[DST].data[dgl.NTYPE], dst_fg)
            ).item()
Minjie Wang's avatar
Minjie Wang committed
1022
1023
1024
            assert g.canonical_etypes[etype][2] == g.ntypes[tid]

    # check for wildcard slices
1025
    g = create_test_heterograph(idtype)
1026
1027
1028
1029
1030
    g.nodes["user"].data["h"] = F.ones((3, 5))
    g.nodes["game"].data["i"] = F.ones((2, 5))
    g.edges["plays"].data["e"] = F.ones((4, 4))
    g.edges["wishes"].data["e"] = F.ones((2, 4))
    g.edges["wishes"].data["f"] = F.ones((2, 4))
Minjie Wang's avatar
Minjie Wang committed
1031

1032
    fg = g["user", :, "game"]  # user--plays->game and user--wishes->game
Minjie Wang's avatar
Minjie Wang committed
1033
    assert len(fg.ntypes) == 2
1034
1035
    assert fg.ntypes == ["user", "game"]
    assert fg.etypes == ["plays+wishes"]
1036
1037
    assert fg.idtype == g.idtype
    assert fg.device == g.device
1038
    etype = fg.etypes[0]
1039
    assert fg[etype] is not None  # Issue #2166
Minjie Wang's avatar
Minjie Wang committed
1040

1041
1042
1043
1044
    assert F.array_equal(fg.nodes["user"].data["h"], F.ones((3, 5)))
    assert F.array_equal(fg.nodes["game"].data["i"], F.ones((2, 5)))
    assert F.array_equal(fg.edata["e"], F.ones((6, 4)))
    assert "f" not in fg.edata
Minjie Wang's avatar
Minjie Wang committed
1045
1046
1047

    etypes = F.asnumpy(fg.edata[dgl.ETYPE]).tolist()
    eids = F.asnumpy(fg.edata[dgl.EID]).tolist()
1048
1049
1050
    assert set(zip(etypes, eids)) == set(
        [(3, 0), (3, 1), (2, 1), (2, 0), (2, 3), (2, 2)]
    )
Minjie Wang's avatar
Minjie Wang committed
1051
1052
1053

    check_mapping(g, fg)

1054
    fg = g["user", :, "user"]
1055
1056
    assert fg.idtype == g.idtype
    assert fg.device == g.device
Minjie Wang's avatar
Minjie Wang committed
1057
1058
    # NOTE(gq): The node/edge types from the parent graph is returned if there is only one
    # node/edge type.  This differs from the behavior above.
1059
1060
1061
1062
    assert fg.ntypes == ["user"]
    assert fg.etypes == ["follows"]
    u1, v1 = g.edges(etype="follows", order="eid")
    u2, v2 = fg.edges(etype="follows", order="eid")
Minjie Wang's avatar
Minjie Wang committed
1063
1064
1065
    assert F.array_equal(u1, u2)
    assert F.array_equal(v1, v2)

1066
    fg = g["developer", :, "game"]
1067
1068
    assert fg.idtype == g.idtype
    assert fg.device == g.device
1069
1070
1071
1072
    assert fg.ntypes == ["developer", "game"]
    assert fg.etypes == ["develops"]
    u1, v1 = g.edges(etype="develops", order="eid")
    u2, v2 = fg.edges(etype="develops", order="eid")
Minjie Wang's avatar
Minjie Wang committed
1073
1074
1075
1076
    assert F.array_equal(u1, u2)
    assert F.array_equal(v1, v2)

    fg = g[:, :, :]
1077
1078
    assert fg.idtype == g.idtype
    assert fg.device == g.device
1079
1080
    assert fg.ntypes == ["developer+user", "game+user"]
    assert fg.etypes == ["develops+follows+plays+wishes"]
Minjie Wang's avatar
Minjie Wang committed
1081
1082
1083
    check_mapping(g, fg)

    # Test another heterograph
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
    g = dgl.heterograph(
        {
            ("user", "follows", "user"): ([0, 1, 2], [1, 2, 3]),
            ("user", "knows", "user"): ([0, 2], [2, 3]),
        },
        idtype=idtype,
        device=F.ctx(),
    )
    g.nodes["user"].data["h"] = F.randn((4, 3))
    g.edges["follows"].data["w"] = F.randn((3, 2))
    g.nodes["user"].data["hh"] = F.randn((4, 5))
    g.edges["knows"].data["ww"] = F.randn((2, 10))

    fg = g["user", :, "user"]
1098
1099
    assert fg.idtype == g.idtype
    assert fg.device == g.device
1100
1101
    assert fg.ntypes == ["user"]
    assert fg.etypes == ["follows+knows"]
Minjie Wang's avatar
Minjie Wang committed
1102
1103
    check_mapping(g, fg)

1104
    fg = g["user", :, :]
1105
1106
    assert fg.idtype == g.idtype
    assert fg.device == g.device
1107
1108
    assert fg.ntypes == ["user"]
    assert fg.etypes == ["follows+knows"]
Minjie Wang's avatar
Minjie Wang committed
1109
1110
    check_mapping(g, fg)

1111
1112
1113
1114

@unittest.skipIf(
    F._default_context_str == "cpu", reason="Need gpu for this test"
)
nv-dlasalle's avatar
nv-dlasalle committed
1115
@parametrize_idtype
1116
1117
1118
1119
def test_to_device(idtype):
    # TODO: rewrite this test case to accept different graphs so we
    #  can test reverse graph and batched graph
    g = create_test_heterograph(idtype)
1120
1121
1122
    g.nodes["user"].data["h"] = F.ones((3, 5))
    g.nodes["game"].data["i"] = F.ones((2, 5))
    g.edges["plays"].data["e"] = F.ones((4, 4))
1123
1124
1125
    assert g.device == F.ctx()
    g = g.to(F.cpu())
    assert g.device == F.cpu()
1126
1127
1128
    assert F.context(g.nodes["user"].data["h"]) == F.cpu()
    assert F.context(g.nodes["game"].data["i"]) == F.cpu()
    assert F.context(g.edges["plays"].data["e"]) == F.cpu()
1129
1130
1131
1132
1133
    for ntype in g.ntypes:
        assert F.context(g.batch_num_nodes(ntype)) == F.cpu()
    for etype in g.canonical_etypes:
        assert F.context(g.batch_num_edges(etype)) == F.cpu()

1134
    if F.is_cuda_available():
1135
        g1 = g.to(F.cuda())
1136
        assert g1.device == F.cuda()
1137
1138
1139
        assert F.context(g1.nodes["user"].data["h"]) == F.cuda()
        assert F.context(g1.nodes["game"].data["i"]) == F.cuda()
        assert F.context(g1.edges["plays"].data["e"]) == F.cuda()
1140
1141
1142
1143
        for ntype in g1.ntypes:
            assert F.context(g1.batch_num_nodes(ntype)) == F.cuda()
        for etype in g1.canonical_etypes:
            assert F.context(g1.batch_num_edges(etype)) == F.cuda()
1144
1145
1146
        assert F.context(g.nodes["user"].data["h"]) == F.cpu()
        assert F.context(g.nodes["game"].data["i"]) == F.cpu()
        assert F.context(g.edges["plays"].data["e"]) == F.cpu()
1147
1148
1149
1150
1151
        for ntype in g.ntypes:
            assert F.context(g.batch_num_nodes(ntype)) == F.cpu()
        for etype in g.canonical_etypes:
            assert F.context(g.batch_num_edges(etype)) == F.cpu()
        with pytest.raises(DGLError):
1152
            g1.nodes["user"].data["h"] = F.copy_to(F.ones((3, 5)), F.cpu())
1153
        with pytest.raises(DGLError):
1154
            g1.edges["plays"].data["e"] = F.copy_to(F.ones((4, 4)), F.cpu())
1155

1156
1157
1158
1159

@unittest.skipIf(
    F._default_context_str == "cpu", reason="Need gpu for this test"
)
nv-dlasalle's avatar
nv-dlasalle committed
1160
@parametrize_idtype
1161
@pytest.mark.parametrize("g", get_cases(["block"]))
1162
1163
1164
1165
def test_to_device2(g, idtype):
    g = g.astype(idtype)
    g = g.to(F.cpu())
    assert g.device == F.cpu()
1166
1167
    if F.is_cuda_available():
        g1 = g.to(F.cuda())
1168
1169
1170
1171
        assert g1.device == F.cuda()
        assert g1.ntypes == g.ntypes
        assert g1.etypes == g.etypes
        assert g1.canonical_etypes == g.canonical_etypes
1172

1173
1174
1175
1176
1177
1178
1179
1180

@unittest.skipIf(
    F._default_context_str == "cpu", reason="Need gpu for this test"
)
@unittest.skipIf(
    dgl.backend.backend_name != "pytorch",
    reason="Pinning graph inplace only supported for PyTorch",
)
nv-dlasalle's avatar
nv-dlasalle committed
1181
@parametrize_idtype
1182
1183
1184
1185
def test_pin_memory_(idtype):
    # TODO: rewrite this test case to accept different graphs so we
    #  can test reverse graph and batched graph
    g = create_test_heterograph(idtype)
1186
1187
1188
    g.nodes["user"].data["h"] = F.ones((3, 5))
    g.nodes["game"].data["i"] = F.ones((2, 5))
    g.edges["plays"].data["e"] = F.ones((4, 4))
1189
1190
1191
    g = g.to(F.cpu())
    assert not g.is_pinned()

1192
1193
1194
1195
    # unpin an unpinned CPU graph, directly return
    g.unpin_memory_()
    assert not g.is_pinned()
    assert g.device == F.cpu()
1196

1197
1198
1199
1200
    # pin a CPU graph
    g.pin_memory_()
    assert g.is_pinned()
    assert g.device == F.cpu()
1201
1202
1203
1204
1205
1206
    assert g.nodes["user"].data["h"].is_pinned()
    assert g.nodes["game"].data["i"].is_pinned()
    assert g.edges["plays"].data["e"].is_pinned()
    assert F.context(g.nodes["user"].data["h"]) == F.cpu()
    assert F.context(g.nodes["game"].data["i"]) == F.cpu()
    assert F.context(g.edges["plays"].data["e"]) == F.cpu()
1207
1208
1209
1210
    for ntype in g.ntypes:
        assert F.context(g.batch_num_nodes(ntype)) == F.cpu()
    for etype in g.canonical_etypes:
        assert F.context(g.batch_num_edges(etype)) == F.cpu()
1211

1212
1213
1214
    # it's fine to clone with new formats, but new graphs are not pinned
    # >>> g.formats()
    # {'created': ['coo'], 'not created': ['csr', 'csc']}
1215
1216
    assert not g.formats("csc").is_pinned()
    assert not g.formats("csr").is_pinned()
1217
    # 'coo' formats is already created and thus not cloned
1218
    assert g.formats("coo").is_pinned()
1219
1220
1221
1222
1223

    # pin a pinned graph, directly return
    g.pin_memory_()
    assert g.is_pinned()
    assert g.device == F.cpu()
1224

1225
1226
1227
1228
    # unpin a pinned graph
    g.unpin_memory_()
    assert not g.is_pinned()
    assert g.device == F.cpu()
1229

1230
    g1 = g.to(F.cuda())
1231

1232
1233
1234
1235
    # unpin an unpinned GPU graph, directly return
    g1.unpin_memory_()
    assert not g1.is_pinned()
    assert g1.device == F.cuda()
1236

1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
    # error pinning a GPU graph
    with pytest.raises(DGLError):
        g1.pin_memory_()

    # test pin empty homograph
    g2 = dgl.graph(([], []))
    g2.pin_memory_()
    assert g2.is_pinned()
    g2.unpin_memory_()
    assert not g2.is_pinned()

    # test pin heterograph with 0 edge of one relation type
1249
1250
1251
    g3 = dgl.heterograph(
        {("a", "b", "c"): ([0, 1], [1, 2]), ("c", "d", "c"): ([], [])}
    ).astype(idtype)
1252
1253
1254
1255
    g3.pin_memory_()
    assert g3.is_pinned()
    g3.unpin_memory_()
    assert not g3.is_pinned()
1256

1257

nv-dlasalle's avatar
nv-dlasalle committed
1258
@parametrize_idtype
1259
def test_convert_bound(idtype):
1260
    def _test_bipartite_bound(data, card):
1261
        with pytest.raises(DGLError):
1262
1263
1264
1265
1266
1267
            dgl.heterograph(
                {("_U", "_E", "_V"): data},
                {"_U": card[0], "_V": card[1]},
                idtype=idtype,
                device=F.ctx(),
            )
1268
1269

    def _test_graph_bound(data, card):
1270
1271
        with pytest.raises(DGLError):
            dgl.graph(data, num_nodes=card, idtype=idtype, device=F.ctx())
1272

1273
1274
1275
1276
    _test_bipartite_bound(([1, 2], [1, 2]), (2, 3))
    _test_bipartite_bound(([0, 1], [1, 4]), (2, 3))
    _test_graph_bound(([1, 3], [1, 2]), 3)
    _test_graph_bound(([0, 1], [1, 3]), 3)
1277
1278


nv-dlasalle's avatar
nv-dlasalle committed
1279
@parametrize_idtype
1280
1281
def test_convert(idtype):
    hg = create_test_heterograph(idtype)
Minjie Wang's avatar
Minjie Wang committed
1282
1283
1284
    hs = []
    for ntype in hg.ntypes:
        h = F.randn((hg.number_of_nodes(ntype), 5))
1285
        hg.nodes[ntype].data["h"] = h
Minjie Wang's avatar
Minjie Wang committed
1286
        hs.append(h)
1287
    hg.nodes["user"].data["x"] = F.randn((3, 3))
Minjie Wang's avatar
Minjie Wang committed
1288
1289
1290
    ws = []
    for etype in hg.canonical_etypes:
        w = F.randn((hg.number_of_edges(etype), 5))
1291
        hg.edges[etype].data["w"] = w
Minjie Wang's avatar
Minjie Wang committed
1292
        ws.append(w)
1293
    hg.edges["plays"].data["x"] = F.randn((4, 3))
Minjie Wang's avatar
Minjie Wang committed
1294

1295
    g = dgl.to_homogeneous(hg, ndata=["h"], edata=["w"])
1296
1297
    assert g.idtype == idtype
    assert g.device == hg.device
1298
1299
1300
1301
    assert F.array_equal(F.cat(hs, dim=0), g.ndata["h"])
    assert "x" not in g.ndata
    assert F.array_equal(F.cat(ws, dim=0), g.edata["w"])
    assert "x" not in g.edata
Minjie Wang's avatar
Minjie Wang committed
1302

1303
    src, dst = g.all_edges(order="eid")
Minjie Wang's avatar
Minjie Wang committed
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
    src = F.asnumpy(src)
    dst = F.asnumpy(dst)
    etype_id, eid = F.asnumpy(g.edata[dgl.ETYPE]), F.asnumpy(g.edata[dgl.EID])
    ntype_id, nid = F.asnumpy(g.ndata[dgl.NTYPE]), F.asnumpy(g.ndata[dgl.NID])
    for i in range(g.number_of_edges()):
        srctype = hg.ntypes[ntype_id[src[i]]]
        dsttype = hg.ntypes[ntype_id[dst[i]]]
        etype = hg.etypes[etype_id[i]]
        src_i, dst_i = hg.find_edges([eid[i]], (srctype, etype, dsttype))
        assert np.asscalar(F.asnumpy(src_i)) == nid[src[i]]
        assert np.asscalar(F.asnumpy(dst_i)) == nid[dst[i]]

1316
1317
1318
1319
1320
1321
1322
1323
    mg = nx.MultiDiGraph(
        [
            ("user", "user", "follows"),
            ("user", "game", "plays"),
            ("user", "game", "wishes"),
            ("developer", "game", "develops"),
        ]
    )
Minjie Wang's avatar
Minjie Wang committed
1324
1325

    for _mg in [None, mg]:
1326
        hg2 = dgl.to_heterogeneous(
1327
1328
1329
1330
1331
1332
1333
            g,
            hg.ntypes,
            hg.etypes,
            ntype_field=dgl.NTYPE,
            etype_field=dgl.ETYPE,
            metagraph=_mg,
        )
1334
1335
        assert hg2.idtype == hg.idtype
        assert hg2.device == hg.device
Minjie Wang's avatar
Minjie Wang committed
1336
1337
1338
1339
        assert set(hg.ntypes) == set(hg2.ntypes)
        assert set(hg.canonical_etypes) == set(hg2.canonical_etypes)
        for ntype in hg.ntypes:
            assert hg.number_of_nodes(ntype) == hg2.number_of_nodes(ntype)
1340
1341
1342
            assert F.array_equal(
                hg.nodes[ntype].data["h"], hg2.nodes[ntype].data["h"]
            )
Minjie Wang's avatar
Minjie Wang committed
1343
        for canonical_etype in hg.canonical_etypes:
1344
1345
            src, dst = hg.all_edges(etype=canonical_etype, order="eid")
            src2, dst2 = hg2.all_edges(etype=canonical_etype, order="eid")
Minjie Wang's avatar
Minjie Wang committed
1346
1347
            assert F.array_equal(src, src2)
            assert F.array_equal(dst, dst2)
1348
1349
1350
1351
            assert F.array_equal(
                hg.edges[canonical_etype].data["w"],
                hg2.edges[canonical_etype].data["w"],
            )
Minjie Wang's avatar
Minjie Wang committed
1352
1353

    # hetero_from_homo test case 2
1354
    g = dgl.graph(([0, 1, 2, 0], [2, 2, 3, 3]), idtype=idtype, device=F.ctx())
Minjie Wang's avatar
Minjie Wang committed
1355
1356
    g.ndata[dgl.NTYPE] = F.tensor([0, 0, 1, 2])
    g.edata[dgl.ETYPE] = F.tensor([0, 0, 1, 2])
1357
    hg = dgl.to_heterogeneous(g, ["l0", "l1", "l2"], ["e0", "e1", "e2"])
1358
1359
    assert hg.idtype == idtype
    assert hg.device == g.device
Minjie Wang's avatar
Minjie Wang committed
1360
    assert set(hg.canonical_etypes) == set(
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
        [("l0", "e0", "l1"), ("l1", "e1", "l2"), ("l0", "e2", "l2")]
    )
    assert hg.number_of_nodes("l0") == 2
    assert hg.number_of_nodes("l1") == 1
    assert hg.number_of_nodes("l2") == 1
    assert hg.number_of_edges("e0") == 2
    assert hg.number_of_edges("e1") == 1
    assert hg.number_of_edges("e2") == 1
    assert F.array_equal(hg.ndata[dgl.NID]["l0"], F.tensor([0, 1], F.int64))
    assert F.array_equal(hg.ndata[dgl.NID]["l1"], F.tensor([2], F.int64))
    assert F.array_equal(hg.ndata[dgl.NID]["l2"], F.tensor([3], F.int64))
    assert F.array_equal(
        hg.edata[dgl.EID][("l0", "e0", "l1")], F.tensor([0, 1], F.int64)
    )
    assert F.array_equal(
        hg.edata[dgl.EID][("l0", "e2", "l2")], F.tensor([3], F.int64)
    )
    assert F.array_equal(
        hg.edata[dgl.EID][("l1", "e1", "l2")], F.tensor([2], F.int64)
    )
Minjie Wang's avatar
Minjie Wang committed
1381
1382

    # hetero_from_homo test case 3
1383
1384
1385
    mg = nx.MultiDiGraph(
        [("user", "movie", "watches"), ("user", "TV", "watches")]
    )
1386
    g = dgl.graph(((0, 0), (1, 2)), idtype=idtype, device=F.ctx())
Minjie Wang's avatar
Minjie Wang committed
1387
1388
1389
    g.ndata[dgl.NTYPE] = F.tensor([0, 1, 2])
    g.edata[dgl.ETYPE] = F.tensor([0, 0])
    for _mg in [None, mg]:
1390
1391
1392
        hg = dgl.to_heterogeneous(
            g, ["user", "TV", "movie"], ["watches"], metagraph=_mg
        )
1393
1394
        assert hg.idtype == g.idtype
        assert hg.device == g.device
Minjie Wang's avatar
Minjie Wang committed
1395
        assert set(hg.canonical_etypes) == set(
1396
1397
1398
1399
1400
1401
1402
            [("user", "watches", "movie"), ("user", "watches", "TV")]
        )
        assert hg.number_of_nodes("user") == 1
        assert hg.number_of_nodes("TV") == 1
        assert hg.number_of_nodes("movie") == 1
        assert hg.number_of_edges(("user", "watches", "TV")) == 1
        assert hg.number_of_edges(("user", "watches", "movie")) == 1
Minjie Wang's avatar
Minjie Wang committed
1403
1404
        assert len(hg.etypes) == 2

1405
    # hetero_to_homo test case 2
1406
1407
1408
1409
1410
1411
    hg = dgl.heterograph(
        {("_U", "_E", "_V"): ([0, 1], [0, 1])},
        {"_U": 2, "_V": 3},
        idtype=idtype,
        device=F.ctx(),
    )
1412
    g = dgl.to_homogeneous(hg)
1413
1414
    assert hg.idtype == g.idtype
    assert hg.device == g.device
1415
1416
    assert g.number_of_nodes() == 5

1417
    # hetero_to_subgraph_to_homo
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
    hg = dgl.heterograph(
        {
            ("user", "plays", "game"): ([0, 1, 1, 2], [0, 0, 2, 1]),
            ("user", "follows", "user"): ([0, 1, 1], [1, 2, 2]),
        },
        idtype=idtype,
        device=F.ctx(),
    )
    hg.nodes["user"].data["h"] = F.copy_to(
        F.tensor([[1, 0], [0, 1], [1, 1]], dtype=idtype), ctx=F.ctx()
    )
    sg = dgl.node_subgraph(hg, {"user": [1, 2]})
1430
1431
    assert len(sg.ntypes) == 2
    assert len(sg.etypes) == 2
1432
1433
1434
1435
    assert sg.num_nodes("user") == 2
    assert sg.num_nodes("game") == 0
    g = dgl.to_homogeneous(sg, ndata=["h"])
    assert "h" in g.ndata.keys()
1436
1437
    assert g.num_nodes() == 2

1438
1439
1440
1441

@unittest.skipIf(
    F._default_context_str == "gpu", reason="Test on cpu is enough"
)
nv-dlasalle's avatar
nv-dlasalle committed
1442
@parametrize_idtype
1443
1444
def test_to_homo_zero_nodes(idtype):
    # Fix gihub issue #2870
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
    g = dgl.heterograph(
        {
            ("A", "AB", "B"): (
                np.random.randint(0, 200, (1000,)),
                np.random.randint(0, 200, (1000,)),
            ),
            ("B", "BA", "A"): (
                np.random.randint(0, 200, (1000,)),
                np.random.randint(0, 200, (1000,)),
            ),
        },
        num_nodes_dict={"A": 200, "B": 200, "C": 0},
        idtype=idtype,
    )
    g.nodes["A"].data["x"] = F.randn((200, 3))
    g.nodes["B"].data["x"] = F.randn((200, 3))
    gg = dgl.to_homogeneous(g, ["x"])
    assert "x" in gg.ndata

1464

nv-dlasalle's avatar
nv-dlasalle committed
1465
@parametrize_idtype
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
def test_to_homo2(idtype):
    # test the result homogeneous graph has nodes and edges sorted by their types
    hg = create_test_heterograph(idtype)
    g = dgl.to_homogeneous(hg)
    ntypes = F.asnumpy(g.ndata[dgl.NTYPE])
    etypes = F.asnumpy(g.edata[dgl.ETYPE])
    p = 0
    for tid, ntype in enumerate(hg.ntypes):
        num_nodes = hg.num_nodes(ntype)
        for i in range(p, p + num_nodes):
            assert ntypes[i] == tid
        p += num_nodes
    p = 0
    for tid, etype in enumerate(hg.canonical_etypes):
        num_edges = hg.num_edges(etype)
        for i in range(p, p + num_edges):
            assert etypes[i] == tid
        p += num_edges
    # test store_type=False
    g = dgl.to_homogeneous(hg, store_type=False)
    assert dgl.NTYPE not in g.ndata
    assert dgl.ETYPE not in g.edata
    # test return_count=True
    g, ntype_count, etype_count = dgl.to_homogeneous(hg, return_count=True)
    for i, count in enumerate(ntype_count):
        assert count == hg.num_nodes(hg.ntypes[i])
    for i, count in enumerate(etype_count):
        assert count == hg.num_edges(hg.canonical_etypes[i])

1495

nv-dlasalle's avatar
nv-dlasalle committed
1496
@parametrize_idtype
1497
1498
1499
1500
1501
1502
1503
def test_invertible_conversion(idtype):
    # Test whether to_homogeneous and to_heterogeneous are invertible
    hg = create_test_heterograph(idtype)
    g = dgl.to_homogeneous(hg)
    hg2 = dgl.to_heterogeneous(g, hg.ntypes, hg.etypes)
    assert_is_identical_hetero(hg, hg2, True)

1504

nv-dlasalle's avatar
nv-dlasalle committed
1505
@parametrize_idtype
1506
1507
def test_metagraph_reachable(idtype):
    g = create_test_heterograph(idtype)
Mufei Li's avatar
Mufei Li committed
1508
    x = F.randn((3, 5))
1509
    g.nodes["user"].data["h"] = x
Mufei Li's avatar
Mufei Li committed
1510

1511
    new_g = dgl.metapath_reachable_graph(g, ["follows", "plays"])
1512
    assert new_g.idtype == idtype
1513
    assert new_g.ntypes == ["game", "user"]
Mufei Li's avatar
Mufei Li committed
1514
1515
1516
    assert new_g.number_of_edges() == 3
    assert F.asnumpy(new_g.has_edges_between([0, 0, 1], [0, 1, 1])).all()

1517
    new_g = dgl.metapath_reachable_graph(g, ["follows"])
1518
    assert new_g.idtype == idtype
1519
    assert new_g.ntypes == ["user"]
Mufei Li's avatar
Mufei Li committed
1520
1521
1522
    assert new_g.number_of_edges() == 2
    assert F.asnumpy(new_g.has_edges_between([0, 1], [1, 2])).all()

1523
1524
1525
1526
1527

@unittest.skipIf(
    dgl.backend.backend_name == "mxnet",
    reason="MXNet doesn't support bool tensor",
)
nv-dlasalle's avatar
nv-dlasalle committed
1528
@parametrize_idtype
1529
1530
def test_subgraph_mask(idtype):
    g = create_test_heterograph(idtype)
1531
1532
    g_graph = g["follows"]
    g_bipartite = g["plays"]
1533
1534
1535

    x = F.randn((3, 5))
    y = F.randn((2, 4))
1536
1537
    g.nodes["user"].data["h"] = x
    g.edges["follows"].data["h"] = y
1538
1539

    def _check_subgraph(g, sg):
1540
1541
        assert sg.idtype == g.idtype
        assert sg.device == g.device
1542
1543
1544
        assert sg.ntypes == g.ntypes
        assert sg.etypes == g.etypes
        assert sg.canonical_etypes == g.canonical_etypes
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
        assert F.array_equal(
            F.tensor(sg.nodes["user"].data[dgl.NID]), F.tensor([1, 2], idtype)
        )
        assert F.array_equal(
            F.tensor(sg.nodes["game"].data[dgl.NID]), F.tensor([0], idtype)
        )
        assert F.array_equal(
            F.tensor(sg.edges["follows"].data[dgl.EID]), F.tensor([1], idtype)
        )
        assert F.array_equal(
            F.tensor(sg.edges["plays"].data[dgl.EID]), F.tensor([1], idtype)
        )
        assert F.array_equal(
            F.tensor(sg.edges["wishes"].data[dgl.EID]), F.tensor([1], idtype)
        )
        assert sg.number_of_nodes("developer") == 0
        assert sg.number_of_edges("develops") == 0
        assert F.array_equal(
            sg.nodes["user"].data["h"], g.nodes["user"].data["h"][1:3]
        )
        assert F.array_equal(
            sg.edges["follows"].data["h"], g.edges["follows"].data["h"][1:2]
        )

    sg1 = g.subgraph(
        {
            "user": F.tensor([False, True, True], dtype=F.bool),
            "game": F.tensor([True, False, False, False], dtype=F.bool),
        }
    )
1575
    _check_subgraph(g, sg1)
1576
    if F._default_context_str != "gpu":
1577
        # TODO(minjie): enable this later
1578
1579
1580
1581
1582
1583
1584
        sg2 = g.edge_subgraph(
            {
                "follows": F.tensor([False, True], dtype=F.bool),
                "plays": F.tensor([False, True, False, False], dtype=F.bool),
                "wishes": F.tensor([False, True], dtype=F.bool),
            }
        )
1585
        _check_subgraph(g, sg2)
1586

1587

nv-dlasalle's avatar
nv-dlasalle committed
1588
@parametrize_idtype
1589
1590
def test_subgraph(idtype):
    g = create_test_heterograph(idtype)
1591
1592
    g_graph = g["follows"]
    g_bipartite = g["plays"]
Quan (Andy) Gan's avatar
Quan (Andy) Gan committed
1593

Minjie Wang's avatar
Minjie Wang committed
1594
1595
    x = F.randn((3, 5))
    y = F.randn((2, 4))
1596
1597
    g.nodes["user"].data["h"] = x
    g.edges["follows"].data["h"] = y
Minjie Wang's avatar
Minjie Wang committed
1598
1599

    def _check_subgraph(g, sg):
1600
1601
        assert sg.idtype == g.idtype
        assert sg.device == g.device
1602
1603
1604
        assert sg.ntypes == g.ntypes
        assert sg.etypes == g.etypes
        assert sg.canonical_etypes == g.canonical_etypes
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
        assert F.array_equal(
            F.tensor(sg.nodes["user"].data[dgl.NID]), F.tensor([1, 2], g.idtype)
        )
        assert F.array_equal(
            F.tensor(sg.nodes["game"].data[dgl.NID]), F.tensor([0], g.idtype)
        )
        assert F.array_equal(
            F.tensor(sg.edges["follows"].data[dgl.EID]), F.tensor([1], g.idtype)
        )
        assert F.array_equal(
            F.tensor(sg.edges["plays"].data[dgl.EID]), F.tensor([1], g.idtype)
        )
        assert F.array_equal(
            F.tensor(sg.edges["wishes"].data[dgl.EID]), F.tensor([1], g.idtype)
        )
        assert sg.number_of_nodes("developer") == 0
        assert sg.number_of_edges("develops") == 0
        assert F.array_equal(
            sg.nodes["user"].data["h"], g.nodes["user"].data["h"][1:3]
        )
        assert F.array_equal(
            sg.edges["follows"].data["h"], g.edges["follows"].data["h"][1:2]
        )

    sg1 = g.subgraph({"user": [1, 2], "game": [0]})
Minjie Wang's avatar
Minjie Wang committed
1630
    _check_subgraph(g, sg1)
1631
    if F._default_context_str != "gpu":
1632
        # TODO(minjie): enable this later
1633
        sg2 = g.edge_subgraph({"follows": [1], "plays": [1], "wishes": [1]})
1634
        _check_subgraph(g, sg2)
Minjie Wang's avatar
Minjie Wang committed
1635

1636
    # backend tensor input
1637
1638
1639
1640
1641
1642
    sg1 = g.subgraph(
        {
            "user": F.tensor([1, 2], dtype=idtype),
            "game": F.tensor([0], dtype=idtype),
        }
    )
1643
    _check_subgraph(g, sg1)
1644
    if F._default_context_str != "gpu":
1645
        # TODO(minjie): enable this later
1646
1647
1648
1649
1650
1651
1652
        sg2 = g.edge_subgraph(
            {
                "follows": F.tensor([1], dtype=idtype),
                "plays": F.tensor([1], dtype=idtype),
                "wishes": F.tensor([1], dtype=idtype),
            }
        )
1653
        _check_subgraph(g, sg2)
1654
1655

    # numpy input
1656
    sg1 = g.subgraph({"user": np.array([1, 2]), "game": np.array([0])})
1657
    _check_subgraph(g, sg1)
1658
    if F._default_context_str != "gpu":
1659
        # TODO(minjie): enable this later
1660
1661
1662
1663
1664
1665
1666
        sg2 = g.edge_subgraph(
            {
                "follows": np.array([1]),
                "plays": np.array([1]),
                "wishes": np.array([1]),
            }
        )
1667
        _check_subgraph(g, sg2)
1668

Quan (Andy) Gan's avatar
Quan (Andy) Gan committed
1669
    def _check_subgraph_single_ntype(g, sg, preserve_nodes=False):
1670
1671
        assert sg.idtype == g.idtype
        assert sg.device == g.device
Quan (Andy) Gan's avatar
Quan (Andy) Gan committed
1672
1673
1674
        assert sg.ntypes == g.ntypes
        assert sg.etypes == g.etypes
        assert sg.canonical_etypes == g.canonical_etypes
Quan (Andy) Gan's avatar
Quan (Andy) Gan committed
1675
1676

        if not preserve_nodes:
1677
1678
1679
1680
            assert F.array_equal(
                F.tensor(sg.nodes["user"].data[dgl.NID]),
                F.tensor([1, 2], g.idtype),
            )
Quan (Andy) Gan's avatar
Quan (Andy) Gan committed
1681
1682
1683
1684
        else:
            for ntype in sg.ntypes:
                assert g.number_of_nodes(ntype) == sg.number_of_nodes(ntype)

1685
1686
1687
        assert F.array_equal(
            F.tensor(sg.edges["follows"].data[dgl.EID]), F.tensor([1], g.idtype)
        )
Quan (Andy) Gan's avatar
Quan (Andy) Gan committed
1688
1689

        if not preserve_nodes:
1690
1691
1692
1693
1694
1695
            assert F.array_equal(
                sg.nodes["user"].data["h"], g.nodes["user"].data["h"][1:3]
            )
        assert F.array_equal(
            sg.edges["follows"].data["h"], g.edges["follows"].data["h"][1:2]
        )
Quan (Andy) Gan's avatar
Quan (Andy) Gan committed
1696

Quan (Andy) Gan's avatar
Quan (Andy) Gan committed
1697
    def _check_subgraph_single_etype(g, sg, preserve_nodes=False):
Quan (Andy) Gan's avatar
Quan (Andy) Gan committed
1698
1699
1700
        assert sg.ntypes == g.ntypes
        assert sg.etypes == g.etypes
        assert sg.canonical_etypes == g.canonical_etypes
Quan (Andy) Gan's avatar
Quan (Andy) Gan committed
1701
1702

        if not preserve_nodes:
1703
1704
1705
1706
1707
1708
1709
1710
            assert F.array_equal(
                F.tensor(sg.nodes["user"].data[dgl.NID]),
                F.tensor([0, 1], g.idtype),
            )
            assert F.array_equal(
                F.tensor(sg.nodes["game"].data[dgl.NID]),
                F.tensor([0], g.idtype),
            )
Quan (Andy) Gan's avatar
Quan (Andy) Gan committed
1711
1712
1713
1714
        else:
            for ntype in sg.ntypes:
                assert g.number_of_nodes(ntype) == sg.number_of_nodes(ntype)

1715
1716
1717
1718
        assert F.array_equal(
            F.tensor(sg.edges["plays"].data[dgl.EID]),
            F.tensor([0, 1], g.idtype),
        )
Quan (Andy) Gan's avatar
Quan (Andy) Gan committed
1719
1720
1721

    sg1_graph = g_graph.subgraph([1, 2])
    _check_subgraph_single_ntype(g_graph, sg1_graph)
1722
    if F._default_context_str != "gpu":
1723
1724
1725
        # TODO(minjie): enable this later
        sg1_graph = g_graph.edge_subgraph([1])
        _check_subgraph_single_ntype(g_graph, sg1_graph)
1726
        sg1_graph = g_graph.edge_subgraph([1], relabel_nodes=False)
1727
1728
1729
        _check_subgraph_single_ntype(g_graph, sg1_graph, True)
        sg2_bipartite = g_bipartite.edge_subgraph([0, 1])
        _check_subgraph_single_etype(g_bipartite, sg2_bipartite)
1730
        sg2_bipartite = g_bipartite.edge_subgraph([0, 1], relabel_nodes=False)
1731
        _check_subgraph_single_etype(g_bipartite, sg2_bipartite, True)
Quan (Andy) Gan's avatar
Quan (Andy) Gan committed
1732

1733
    def _check_typed_subgraph1(g, sg):
1734
1735
        assert g.idtype == sg.idtype
        assert g.device == sg.device
1736
1737
        assert set(sg.ntypes) == {"user", "game"}
        assert set(sg.etypes) == {"follows", "plays", "wishes"}
Minjie Wang's avatar
Minjie Wang committed
1738
1739
1740
        for ntype in sg.ntypes:
            assert sg.number_of_nodes(ntype) == g.number_of_nodes(ntype)
        for etype in sg.etypes:
1741
1742
            src_sg, dst_sg = sg.all_edges(etype=etype, order="eid")
            src_g, dst_g = g.all_edges(etype=etype, order="eid")
Minjie Wang's avatar
Minjie Wang committed
1743
1744
            assert F.array_equal(src_sg, src_g)
            assert F.array_equal(dst_sg, dst_g)
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
        assert F.array_equal(
            sg.nodes["user"].data["h"], g.nodes["user"].data["h"]
        )
        assert F.array_equal(
            sg.edges["follows"].data["h"], g.edges["follows"].data["h"]
        )
        g.nodes["user"].data["h"] = F.scatter_row(
            g.nodes["user"].data["h"], F.tensor([2]), F.randn((1, 5))
        )
        g.edges["follows"].data["h"] = F.scatter_row(
            g.edges["follows"].data["h"], F.tensor([1]), F.randn((1, 4))
        )
        assert F.array_equal(
            sg.nodes["user"].data["h"], g.nodes["user"].data["h"]
        )
        assert F.array_equal(
            sg.edges["follows"].data["h"], g.edges["follows"].data["h"]
        )
Minjie Wang's avatar
Minjie Wang committed
1763

1764
    def _check_typed_subgraph2(g, sg):
1765
1766
        assert set(sg.ntypes) == {"developer", "game"}
        assert set(sg.etypes) == {"develops"}
1767
1768
1769
        for ntype in sg.ntypes:
            assert sg.number_of_nodes(ntype) == g.number_of_nodes(ntype)
        for etype in sg.etypes:
1770
1771
            src_sg, dst_sg = sg.all_edges(etype=etype, order="eid")
            src_g, dst_g = g.all_edges(etype=etype, order="eid")
1772
1773
1774
            assert F.array_equal(src_sg, src_g)
            assert F.array_equal(dst_sg, dst_g)

1775
    sg3 = g.node_type_subgraph(["user", "game"])
1776
    _check_typed_subgraph1(g, sg3)
1777
    sg4 = g.edge_type_subgraph(["develops"])
1778
    _check_typed_subgraph2(g, sg4)
1779
    sg5 = g.edge_type_subgraph(["follows", "plays", "wishes"])
1780
    _check_typed_subgraph1(g, sg5)
1781

1782

nv-dlasalle's avatar
nv-dlasalle committed
1783
@parametrize_idtype
1784
def test_apply(idtype):
1785
    def node_udf(nodes):
1786
1787
        return {"h": nodes.data["h"] * 2}

1788
    def node_udf2(nodes):
1789
1790
        return {"h": F.sum(nodes.data["h"], dim=1, keepdims=True)}

1791
    def edge_udf(edges):
1792
        return {"h": edges.data["h"] * 2 + edges.src["h"]}
1793

1794
    g = create_test_heterograph(idtype)
1795
1796
1797
    g.nodes["user"].data["h"] = F.ones((3, 5))
    g.apply_nodes(node_udf, ntype="user")
    assert F.array_equal(g.nodes["user"].data["h"], F.ones((3, 5)) * 2)
Minjie Wang's avatar
Minjie Wang committed
1798

1799
1800
1801
    g["plays"].edata["h"] = F.ones((4, 5))
    g.apply_edges(edge_udf, etype=("user", "plays", "game"))
    assert F.array_equal(g["plays"].edata["h"], F.ones((4, 5)) * 4)
Minjie Wang's avatar
Minjie Wang committed
1802
1803

    # test apply on graph with only one type
1804
1805
    g["follows"].apply_nodes(node_udf)
    assert F.array_equal(g.nodes["user"].data["h"], F.ones((3, 5)) * 4)
1806

1807
1808
    g["plays"].apply_edges(edge_udf)
    assert F.array_equal(g["plays"].edata["h"], F.ones((4, 5)) * 12)
Minjie Wang's avatar
Minjie Wang committed
1809

1810
    # Test the case that feature size changes
1811
1812
1813
    g.nodes["user"].data["h"] = F.ones((3, 5))
    g.apply_nodes(node_udf2, ntype="user")
    assert F.array_equal(g.nodes["user"].data["h"], F.ones((3, 1)) * 5)
1814

Minjie Wang's avatar
Minjie Wang committed
1815
1816
    # test fail case
    # fail due to multiple types
1817
    with pytest.raises(DGLError):
Minjie Wang's avatar
Minjie Wang committed
1818
1819
        g.apply_nodes(node_udf)

1820
    with pytest.raises(DGLError):
Minjie Wang's avatar
Minjie Wang committed
1821
1822
        g.apply_edges(edge_udf)

1823

nv-dlasalle's avatar
nv-dlasalle committed
1824
@parametrize_idtype
1825
def test_level2(idtype):
1826
    # edges = {
Minjie Wang's avatar
Minjie Wang committed
1827
1828
1829
1830
    #    'follows': ([0, 1], [1, 2]),
    #    'plays': ([0, 1, 2, 1], [0, 0, 1, 1]),
    #    'wishes': ([0, 2], [1, 0]),
    #    'develops': ([0, 1], [0, 1]),
1831
    # }
1832
    g = create_test_heterograph(idtype)
1833

Minjie Wang's avatar
Minjie Wang committed
1834
    def rfunc(nodes):
1835
1836
        return {"y": F.sum(nodes.mailbox["m"], 1)}

Minjie Wang's avatar
Minjie Wang committed
1837
    def rfunc2(nodes):
1838
1839
        return {"y": F.max(nodes.mailbox["m"], 1)}

Minjie Wang's avatar
Minjie Wang committed
1840
    def mfunc(edges):
1841
1842
        return {"m": edges.src["h"]}

Minjie Wang's avatar
Minjie Wang committed
1843
    def afunc(nodes):
1844
        return {"y": nodes.data["y"] + 1}
Minjie Wang's avatar
Minjie Wang committed
1845
1846
1847
1848
1849

    #############################################################
    #  send_and_recv
    #############################################################

1850
1851
1852
1853
    g.nodes["user"].data["h"] = F.ones((3, 2))
    g.send_and_recv([2, 3], mfunc, rfunc, etype="plays")
    y = g.nodes["game"].data["y"]
    assert F.array_equal(y, F.tensor([[0.0, 0.0], [2.0, 2.0]]))
Minjie Wang's avatar
Minjie Wang committed
1854
1855

    # only one type
1856
1857
1858
    g["plays"].send_and_recv([2, 3], mfunc, rfunc)
    y = g.nodes["game"].data["y"]
    assert F.array_equal(y, F.tensor([[0.0, 0.0], [2.0, 2.0]]))
1859

Minjie Wang's avatar
Minjie Wang committed
1860
1861
    # test fail case
    # fail due to multiple types
1862
    with pytest.raises(DGLError):
Minjie Wang's avatar
Minjie Wang committed
1863
1864
        g.send_and_recv([2, 3], mfunc, rfunc)

1865
    g.nodes["game"].data.clear()
Minjie Wang's avatar
Minjie Wang committed
1866
1867
1868
1869
1870

    #############################################################
    #  pull
    #############################################################

1871
1872
1873
1874
    g.nodes["user"].data["h"] = F.ones((3, 2))
    g.pull(1, mfunc, rfunc, etype="plays")
    y = g.nodes["game"].data["y"]
    assert F.array_equal(y, F.tensor([[0.0, 0.0], [2.0, 2.0]]))
Minjie Wang's avatar
Minjie Wang committed
1875
1876

    # only one type
1877
1878
1879
    g["plays"].pull(1, mfunc, rfunc)
    y = g.nodes["game"].data["y"]
    assert F.array_equal(y, F.tensor([[0.0, 0.0], [2.0, 2.0]]))
Minjie Wang's avatar
Minjie Wang committed
1880
1881

    # test fail case
1882
    with pytest.raises(DGLError):
Minjie Wang's avatar
Minjie Wang committed
1883
1884
        g.pull(1, mfunc, rfunc)

1885
    g.nodes["game"].data.clear()
Minjie Wang's avatar
Minjie Wang committed
1886
1887
1888
1889
1890

    #############################################################
    #  update_all
    #############################################################

1891
1892
1893
1894
    g.nodes["user"].data["h"] = F.ones((3, 2))
    g.update_all(mfunc, rfunc, etype="plays")
    y = g.nodes["game"].data["y"]
    assert F.array_equal(y, F.tensor([[2.0, 2.0], [2.0, 2.0]]))
Minjie Wang's avatar
Minjie Wang committed
1895
1896

    # only one type
1897
1898
1899
    g["plays"].update_all(mfunc, rfunc)
    y = g.nodes["game"].data["y"]
    assert F.array_equal(y, F.tensor([[2.0, 2.0], [2.0, 2.0]]))
Minjie Wang's avatar
Minjie Wang committed
1900
1901
1902

    # test fail case
    # fail due to multiple types
1903
    with pytest.raises(DGLError):
Minjie Wang's avatar
Minjie Wang committed
1904
1905
1906
1907
        g.update_all(mfunc, rfunc)

    # test multi
    g.multi_update_all(
1908
1909
1910
1911
1912
1913
        {"plays": (mfunc, rfunc), ("user", "wishes", "game"): (mfunc, rfunc2)},
        "sum",
    )
    assert F.array_equal(
        g.nodes["game"].data["y"], F.tensor([[3.0, 3.0], [3.0, 3.0]])
    )
Minjie Wang's avatar
Minjie Wang committed
1914
1915
1916

    # test multi
    g.multi_update_all(
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
        {
            "plays": (mfunc, rfunc, afunc),
            ("user", "wishes", "game"): (mfunc, rfunc2),
        },
        "sum",
        afunc,
    )
    assert F.array_equal(
        g.nodes["game"].data["y"], F.tensor([[5.0, 5.0], [5.0, 5.0]])
    )
Minjie Wang's avatar
Minjie Wang committed
1927
1928

    # test cross reducer
1929
1930
    g.nodes["user"].data["h"] = F.randn((3, 2))
    for cred in ["sum", "max", "min", "mean", "stack"]:
Minjie Wang's avatar
Minjie Wang committed
1931
        g.multi_update_all(
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
            {"plays": (mfunc, rfunc, afunc), "wishes": (mfunc, rfunc2)},
            cred,
            afunc,
        )
        y = g.nodes["game"].data["y"]
        g["plays"].update_all(mfunc, rfunc, afunc)
        y1 = g.nodes["game"].data["y"]
        g["wishes"].update_all(mfunc, rfunc2)
        y2 = g.nodes["game"].data["y"]
        if cred == "stack":
1942
1943
1944
1945
            # stack has an internal order by edge type id
            yy = F.stack([y1, y2], 1)
            yy = yy + 1  # final afunc
            assert F.array_equal(y, yy)
Minjie Wang's avatar
Minjie Wang committed
1946
1947
1948
1949
1950
1951
1952
        else:
            yy = get_redfn(cred)(F.stack([y1, y2], 0), 0)
            yy = yy + 1  # final afunc
            assert F.array_equal(y, yy)

    # test fail case
    # fail because cannot infer ntype
1953
    with pytest.raises(DGLError):
Minjie Wang's avatar
Minjie Wang committed
1954
        g.update_all(
1955
1956
1957
1958
            {"plays": (mfunc, rfunc), "follows": (mfunc, rfunc2)}, "sum"
        )

    g.nodes["game"].data.clear()
Minjie Wang's avatar
Minjie Wang committed
1959

1960

nv-dlasalle's avatar
nv-dlasalle committed
1961
@parametrize_idtype
1962
1963
1964
@unittest.skipIf(
    F._default_context_str == "cpu", reason="Need gpu for this test"
)
1965
def test_more_nnz(idtype):
1966
1967
1968
1969
1970
1971
    g = dgl.graph(
        ([0, 0, 0, 0, 0], [1, 1, 1, 1, 1]), idtype=idtype, device=F.ctx()
    )
    g.ndata["x"] = F.copy_to(F.ones((2, 5)), ctx=F.ctx())
    g.update_all(fn.copy_u("x", "m"), fn.sum("m", "y"))
    y = g.ndata["y"]
1972
1973
1974
1975
1976
    ans = np.zeros((2, 5))
    ans[1] = 5
    ans = F.copy_to(F.tensor(ans, dtype=F.dtype(y)), ctx=F.ctx())
    assert F.array_equal(y, ans)

1977

nv-dlasalle's avatar
nv-dlasalle committed
1978
@parametrize_idtype
1979
def test_updates(idtype):
1980
    def msg_func(edges):
1981
1982
        return {"m": edges.src["h"]}

1983
    def reduce_func(nodes):
1984
1985
        return {"y": F.sum(nodes.mailbox["m"], 1)}

1986
    def apply_func(nodes):
1987
1988
        return {"y": nodes.data["y"] * 2}

1989
    g = create_test_heterograph(idtype)
1990
    x = F.randn((3, 5))
1991
    g.nodes["user"].data["h"] = x
1992
1993

    for msg, red, apply in itertools.product(
1994
1995
1996
1997
        [fn.copy_u("h", "m"), msg_func],
        [fn.sum("m", "y"), reduce_func],
        [None, apply_func],
    ):
1998
1999
        multiplier = 1 if apply is None else 2

2000
2001
        g["user", "plays", "game"].update_all(msg, red, apply)
        y = g.nodes["game"].data["y"]
2002
2003
        assert F.array_equal(y[0], (x[0] + x[1]) * multiplier)
        assert F.array_equal(y[1], (x[1] + x[2]) * multiplier)
2004
        del g.nodes["game"].data["y"]
2005

2006
2007
2008
2009
        g["user", "plays", "game"].send_and_recv(
            ([0, 1, 2], [0, 1, 1]), msg, red, apply
        )
        y = g.nodes["game"].data["y"]
2010
2011
        assert F.array_equal(y[0], x[0] * multiplier)
        assert F.array_equal(y[1], (x[1] + x[2]) * multiplier)
2012
        del g.nodes["game"].data["y"]
2013
2014

        # pulls from destination (game) node 0
2015
2016
        g["user", "plays", "game"].pull(0, msg, red, apply)
        y = g.nodes["game"].data["y"]
2017
        assert F.array_equal(y[0], (x[0] + x[1]) * multiplier)
2018
        del g.nodes["game"].data["y"]
2019
2020

        # pushes from source (user) node 0
2021
2022
        g["user", "plays", "game"].push(0, msg, red, apply)
        y = g.nodes["game"].data["y"]
2023
        assert F.array_equal(y[0], x[0] * multiplier)
2024
        del g.nodes["game"].data["y"]
Minjie Wang's avatar
Minjie Wang committed
2025

2026

nv-dlasalle's avatar
nv-dlasalle committed
2027
@parametrize_idtype
2028
2029
def test_backward(idtype):
    g = create_test_heterograph(idtype)
Minjie Wang's avatar
Minjie Wang committed
2030
2031
    x = F.randn((3, 5))
    F.attach_grad(x)
2032
    g.nodes["user"].data["h"] = x
Minjie Wang's avatar
Minjie Wang committed
2033
2034
    with F.record_grad():
        g.multi_update_all(
2035
2036
2037
2038
2039
2040
2041
            {
                "plays": (fn.copy_u("h", "m"), fn.sum("m", "y")),
                "wishes": (fn.copy_u("h", "m"), fn.sum("m", "y")),
            },
            "sum",
        )
        y = g.nodes["game"].data["y"]
Minjie Wang's avatar
Minjie Wang committed
2042
2043
        F.backward(y, F.ones(y.shape))
    print(F.grad(x))
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
    assert F.array_equal(
        F.grad(x),
        F.tensor(
            [
                [2.0, 2.0, 2.0, 2.0, 2.0],
                [2.0, 2.0, 2.0, 2.0, 2.0],
                [2.0, 2.0, 2.0, 2.0, 2.0],
            ]
        ),
    )
2054

2055

nv-dlasalle's avatar
nv-dlasalle committed
2056
@parametrize_idtype
2057
def test_empty_heterograph(idtype):
2058
    def assert_empty(g):
2059
2060
2061
        assert g.number_of_nodes("user") == 0
        assert g.number_of_edges("plays") == 0
        assert g.number_of_nodes("game") == 0
2062
2063

    # empty src-dst pair
2064
    assert_empty(dgl.heterograph({("user", "plays", "game"): ([], [])}))
2065

2066
2067
2068
    g = dgl.heterograph(
        {("user", "follows", "user"): ([], [])}, idtype=idtype, device=F.ctx()
    )
2069
2070
    assert g.idtype == idtype
    assert g.device == F.ctx()
2071
2072
    assert g.number_of_nodes("user") == 0
    assert g.number_of_edges("follows") == 0
2073
2074

    # empty relation graph with others
2075
2076
2077
2078
2079
2080
2081
2082
    g = dgl.heterograph(
        {
            ("user", "plays", "game"): ([], []),
            ("developer", "develops", "game"): ([0, 1], [0, 1]),
        },
        idtype=idtype,
        device=F.ctx(),
    )
2083
2084
    assert g.idtype == idtype
    assert g.device == F.ctx()
2085
2086
2087
2088
2089
2090
    assert g.number_of_nodes("user") == 0
    assert g.number_of_edges("plays") == 0
    assert g.number_of_nodes("game") == 2
    assert g.number_of_edges("develops") == 2
    assert g.number_of_nodes("developer") == 2

2091

nv-dlasalle's avatar
nv-dlasalle committed
2092
@parametrize_idtype
2093
def test_types_in_function(idtype):
2094
    def mfunc1(edges):
2095
        assert edges.canonical_etype == ("user", "follow", "user")
2096
2097
2098
        return {}

    def rfunc1(nodes):
2099
        assert nodes.ntype == "user"
2100
2101
2102
        return {}

    def filter_nodes1(nodes):
2103
        assert nodes.ntype == "user"
2104
2105
2106
        return F.zeros((3,))

    def filter_edges1(edges):
2107
        assert edges.canonical_etype == ("user", "follow", "user")
2108
2109
2110
        return F.zeros((2,))

    def mfunc2(edges):
2111
        assert edges.canonical_etype == ("user", "plays", "game")
2112
2113
2114
        return {}

    def rfunc2(nodes):
2115
        assert nodes.ntype == "game"
2116
2117
2118
        return {}

    def filter_nodes2(nodes):
2119
        assert nodes.ntype == "game"
2120
2121
2122
        return F.zeros((3,))

    def filter_edges2(edges):
2123
        assert edges.canonical_etype == ("user", "plays", "game")
2124
2125
        return F.zeros((2,))

2126
2127
2128
2129
2130
    g = dgl.heterograph(
        {("user", "follow", "user"): ((0, 1), (1, 2))},
        idtype=idtype,
        device=F.ctx(),
    )
2131
2132
2133
2134
2135
2136
2137
2138
2139
    g.apply_nodes(rfunc1)
    g.apply_edges(mfunc1)
    g.update_all(mfunc1, rfunc1)
    g.send_and_recv([0, 1], mfunc1, rfunc1)
    g.push([0], mfunc1, rfunc1)
    g.pull([1], mfunc1, rfunc1)
    g.filter_nodes(filter_nodes1)
    g.filter_edges(filter_edges1)

2140
2141
2142
2143
2144
2145
    g = dgl.heterograph(
        {("user", "plays", "game"): ([0, 1], [1, 2])},
        idtype=idtype,
        device=F.ctx(),
    )
    g.apply_nodes(rfunc2, ntype="game")
2146
2147
2148
2149
2150
    g.apply_edges(mfunc2)
    g.update_all(mfunc2, rfunc2)
    g.send_and_recv([0, 1], mfunc2, rfunc2)
    g.push([0], mfunc2, rfunc2)
    g.pull([1], mfunc2, rfunc2)
2151
    g.filter_nodes(filter_nodes2, ntype="game")
2152
2153
    g.filter_edges(filter_edges2)

2154

nv-dlasalle's avatar
nv-dlasalle committed
2155
@parametrize_idtype
2156
def test_stack_reduce(idtype):
2157
    # edges = {
2158
2159
2160
2161
    #    'follows': ([0, 1], [1, 2]),
    #    'plays': ([0, 1, 2, 1], [0, 0, 1, 1]),
    #    'wishes': ([0, 2], [1, 0]),
    #    'develops': ([0, 1], [0, 1]),
2162
    # }
2163
    g = create_test_heterograph(idtype)
2164
2165
    g.nodes["user"].data["h"] = F.randn((3, 200))

2166
    def rfunc(nodes):
2167
2168
        return {"y": F.sum(nodes.mailbox["m"], 1)}

2169
    def rfunc2(nodes):
2170
2171
        return {"y": F.max(nodes.mailbox["m"], 1)}

2172
    def mfunc(edges):
2173
2174
        return {"m": edges.src["h"]}

2175
    g.multi_update_all(
2176
2177
2178
2179
2180
2181
2182
        {"plays": (mfunc, rfunc), "wishes": (mfunc, rfunc2)}, "stack"
    )
    assert g.nodes["game"].data["y"].shape == (
        g.number_of_nodes("game"),
        2,
        200,
    )
2183
    # only one type-wise update_all, stack still adds one dimension
2184
2185
2186
2187
2188
2189
2190
    g.multi_update_all({"plays": (mfunc, rfunc)}, "stack")
    assert g.nodes["game"].data["y"].shape == (
        g.number_of_nodes("game"),
        1,
        200,
    )

2191

nv-dlasalle's avatar
nv-dlasalle committed
2192
@parametrize_idtype
2193
def test_isolated_ntype(idtype):
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
    g = dgl.heterograph(
        {("A", "AB", "B"): ([0, 1, 2], [1, 2, 3])},
        num_nodes_dict={"A": 3, "B": 4, "C": 4},
        idtype=idtype,
        device=F.ctx(),
    )
    assert g.number_of_nodes("A") == 3
    assert g.number_of_nodes("B") == 4
    assert g.number_of_nodes("C") == 4

    g = dgl.heterograph(
        {("A", "AC", "C"): ([0, 1, 2], [1, 2, 3])},
        num_nodes_dict={"A": 3, "B": 4, "C": 4},
        idtype=idtype,
        device=F.ctx(),
    )
    assert g.number_of_nodes("A") == 3
    assert g.number_of_nodes("B") == 4
    assert g.number_of_nodes("C") == 4

    G = dgl.graph(
        ([0, 1, 2], [4, 5, 6]), num_nodes=11, idtype=idtype, device=F.ctx()
    )
    G.ndata[dgl.NTYPE] = F.tensor(
        [0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2], dtype=F.int64
    )
2220
    G.edata[dgl.ETYPE] = F.tensor([0, 0, 0], dtype=F.int64)
2221
2222
2223
2224
    g = dgl.to_heterogeneous(G, ["A", "B", "C"], ["AB"])
    assert g.number_of_nodes("A") == 3
    assert g.number_of_nodes("B") == 4
    assert g.number_of_nodes("C") == 4
2225

2226

nv-dlasalle's avatar
nv-dlasalle committed
2227
@parametrize_idtype
2228
def test_ismultigraph(idtype):
2229
2230
2231
2232
2233
2234
    g1 = dgl.heterograph(
        {("A", "AB", "B"): ([0, 0, 1, 2], [1, 2, 5, 5])},
        {"A": 6, "B": 6},
        idtype=idtype,
        device=F.ctx(),
    )
2235
    assert g1.is_multigraph == False
2236
2237
2238
2239
2240
2241
    g2 = dgl.heterograph(
        {("A", "AC", "C"): ([0, 0, 0, 1], [1, 1, 2, 5])},
        {"A": 6, "C": 6},
        idtype=idtype,
        device=F.ctx(),
    )
2242
    assert g2.is_multigraph == True
2243
    g3 = dgl.graph(((0, 1), (1, 2)), num_nodes=6, idtype=idtype, device=F.ctx())
2244
    assert g3.is_multigraph == False
2245
2246
2247
    g4 = dgl.graph(
        ([0, 0, 1], [1, 1, 2]), num_nodes=6, idtype=idtype, device=F.ctx()
    )
2248
    assert g4.is_multigraph == True
2249
2250
2251
2252
2253
2254
2255
2256
2257
    g = dgl.heterograph(
        {
            ("A", "AB", "B"): ([0, 0, 1, 2], [1, 2, 5, 5]),
            ("A", "AA", "A"): ([0, 1], [1, 2]),
        },
        {"A": 6, "B": 6},
        idtype=idtype,
        device=F.ctx(),
    )
2258
    assert g.is_multigraph == False
2259
2260
2261
2262
2263
2264
2265
2266
2267
    g = dgl.heterograph(
        {
            ("A", "AB", "B"): ([0, 0, 1, 2], [1, 2, 5, 5]),
            ("A", "AC", "C"): ([0, 0, 0, 1], [1, 1, 2, 5]),
        },
        {"A": 6, "B": 6, "C": 6},
        idtype=idtype,
        device=F.ctx(),
    )
2268
    assert g.is_multigraph == True
2269
2270
2271
2272
2273
2274
2275
2276
2277
    g = dgl.heterograph(
        {
            ("A", "AB", "B"): ([0, 0, 1, 2], [1, 2, 5, 5]),
            ("A", "AA", "A"): ([0, 0, 1], [1, 1, 2]),
        },
        {"A": 6, "B": 6},
        idtype=idtype,
        device=F.ctx(),
    )
2278
    assert g.is_multigraph == True
2279
2280
2281
2282
2283
2284
2285
2286
2287
    g = dgl.heterograph(
        {
            ("A", "AC", "C"): ([0, 0, 0, 1], [1, 1, 2, 5]),
            ("A", "AA", "A"): ([0, 1], [1, 2]),
        },
        {"A": 6, "C": 6},
        idtype=idtype,
        device=F.ctx(),
    )
2288
2289
    assert g.is_multigraph == True

2290
2291
2292

@parametrize_idtype
def test_graph_index_is_unibipartite(idtype):
2293
2294
2295
2296
2297
    g1 = dgl.heterograph(
        {("A", "AB", "B"): ([0, 0, 1], [1, 2, 5])},
        idtype=idtype,
        device=F.ctx(),
    )
2298
2299
2300
    assert g1._graph.is_metagraph_unibipartite()

    # more complicated bipartite
2301
2302
2303
2304
2305
2306
2307
2308
    g2 = dgl.heterograph(
        {
            ("A", "AB", "B"): ([0, 0, 1], [1, 2, 5]),
            ("A", "AC", "C"): ([1, 0], [0, 0]),
        },
        idtype=idtype,
        device=F.ctx(),
    )
2309
2310
    assert g2._graph.is_metagraph_unibipartite()

2311
2312
2313
2314
2315
2316
2317
2318
2319
    g3 = dgl.heterograph(
        {
            ("A", "AB", "B"): ([0, 0, 1], [1, 2, 5]),
            ("A", "AC", "C"): ([1, 0], [0, 0]),
            ("A", "AA", "A"): ([0, 1], [0, 1]),
        },
        idtype=idtype,
        device=F.ctx(),
    )
2320
2321
    assert not g3._graph.is_metagraph_unibipartite()

2322
2323
2324
2325
2326
2327
2328
2329
    g4 = dgl.heterograph(
        {
            ("A", "AB", "B"): ([0, 0, 1], [1, 2, 5]),
            ("C", "CA", "A"): ([1, 0], [0, 0]),
        },
        idtype=idtype,
        device=F.ctx(),
    )
2330
2331
2332
2333

    assert not g4._graph.is_metagraph_unibipartite()


nv-dlasalle's avatar
nv-dlasalle committed
2334
@parametrize_idtype
2335
def test_bipartite(idtype):
2336
2337
2338
2339
2340
    g1 = dgl.heterograph(
        {("A", "AB", "B"): ([0, 0, 1], [1, 2, 5])},
        idtype=idtype,
        device=F.ctx(),
    )
2341
2342
    assert g1.is_unibipartite
    assert len(g1.ntypes) == 2
2343
2344
2345
2346
2347
2348
    assert g1.etypes == ["AB"]
    assert g1.srctypes == ["A"]
    assert g1.dsttypes == ["B"]
    assert g1.number_of_nodes("A") == 2
    assert g1.number_of_nodes("B") == 6
    assert g1.number_of_src_nodes("A") == 2
2349
    assert g1.number_of_src_nodes() == 2
2350
    assert g1.number_of_dst_nodes("B") == 6
2351
    assert g1.number_of_dst_nodes() == 6
2352
    assert g1.number_of_edges() == 3
2353
2354
2355
2356
2357
2358
2359
2360
    g1.srcdata["h"] = F.randn((2, 5))
    assert F.array_equal(g1.srcnodes["A"].data["h"], g1.srcdata["h"])
    assert F.array_equal(g1.nodes["A"].data["h"], g1.srcdata["h"])
    assert F.array_equal(g1.nodes["SRC/A"].data["h"], g1.srcdata["h"])
    g1.dstdata["h"] = F.randn((6, 3))
    assert F.array_equal(g1.dstnodes["B"].data["h"], g1.dstdata["h"])
    assert F.array_equal(g1.nodes["B"].data["h"], g1.dstdata["h"])
    assert F.array_equal(g1.nodes["DST/B"].data["h"], g1.dstdata["h"])
2361
2362

    # more complicated bipartite
2363
2364
2365
2366
2367
2368
2369
2370
    g2 = dgl.heterograph(
        {
            ("A", "AB", "B"): ([0, 0, 1], [1, 2, 5]),
            ("A", "AC", "C"): ([1, 0], [0, 0]),
        },
        idtype=idtype,
        device=F.ctx(),
    )
2371
2372

    assert g2.is_unibipartite
2373
2374
2375
2376
2377
2378
    assert g2.srctypes == ["A"]
    assert set(g2.dsttypes) == {"B", "C"}
    assert g2.number_of_nodes("A") == 2
    assert g2.number_of_nodes("B") == 6
    assert g2.number_of_nodes("C") == 1
    assert g2.number_of_src_nodes("A") == 2
2379
    assert g2.number_of_src_nodes() == 2
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
    assert g2.number_of_dst_nodes("B") == 6
    assert g2.number_of_dst_nodes("C") == 1
    g2.srcdata["h"] = F.randn((2, 5))
    assert F.array_equal(g2.srcnodes["A"].data["h"], g2.srcdata["h"])
    assert F.array_equal(g2.nodes["A"].data["h"], g2.srcdata["h"])
    assert F.array_equal(g2.nodes["SRC/A"].data["h"], g2.srcdata["h"])

    g3 = dgl.heterograph(
        {
            ("A", "AB", "B"): ([0, 0, 1], [1, 2, 5]),
            ("A", "AC", "C"): ([1, 0], [0, 0]),
            ("A", "AA", "A"): ([0, 1], [0, 1]),
        },
        idtype=idtype,
        device=F.ctx(),
    )
2396
    assert not g3.is_unibipartite
2397

2398
2399
2400
2401
2402
2403
2404
2405
    g4 = dgl.heterograph(
        {
            ("A", "AB", "B"): ([0, 0, 1], [1, 2, 5]),
            ("C", "CA", "A"): ([1, 0], [0, 0]),
        },
        idtype=idtype,
        device=F.ctx(),
    )
2406
2407
2408

    assert not g4.is_unibipartite

2409

nv-dlasalle's avatar
nv-dlasalle committed
2410
@parametrize_idtype
2411
def test_dtype_cast(idtype):
2412
    g = dgl.graph(([0, 1, 0, 2], [0, 1, 1, 0]), idtype=idtype, device=F.ctx())
2413
    assert g.idtype == idtype
2414
2415
    g.ndata["feat"] = F.tensor([3, 4, 5])
    g.edata["h"] = F.tensor([3, 4, 5, 6])
2416
    if idtype == "int32":
2417
        g_cast = g.long()
2418
        assert g_cast.idtype == F.int64
2419
2420
    else:
        g_cast = g.int()
2421
2422
        assert g_cast.idtype == F.int32
    test_utils.check_graph_equal(g, g_cast, check_idtype=False)
2423

2424

2425
2426
2427
def test_float_cast():
    for t in [F.float16, F.float32, F.float64]:
        idtype = F.int32
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
        g = dgl.heterograph(
            {
                ("user", "follows", "user"): (
                    F.tensor([0, 1, 1, 2, 2, 3], dtype=idtype),
                    F.tensor([0, 0, 1, 1, 2, 2], dtype=idtype),
                ),
                ("user", "plays", "game"): (
                    F.tensor([0, 1, 1], dtype=idtype),
                    F.tensor([0, 0, 1], dtype=idtype),
                ),
            },
            idtype=idtype,
            device=F.ctx(),
        )
2442
2443
2444
2445
2446
        uvalues = [1, 2, 3, 4]
        gvalues = [5, 6]
        fvalues = [7, 8, 9, 10, 11, 12]
        pvalues = [13, 14, 15]
        dataNamesTypes = [
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
            ("a", F.float16),
            ("b", F.float32),
            ("c", F.float64),
            ("d", F.int32),
            ("e", F.int64),
        ]
        for name, type in dataNamesTypes:
            g.nodes["user"].data[name] = F.copy_to(
                F.tensor(uvalues, dtype=type), ctx=F.ctx()
            )
        for name, type in dataNamesTypes:
            g.nodes["game"].data[name] = F.copy_to(
                F.tensor(gvalues, dtype=type), ctx=F.ctx()
            )
        for name, type in dataNamesTypes:
            g.edges["follows"].data[name] = F.copy_to(
                F.tensor(fvalues, dtype=type), ctx=F.ctx()
            )
        for name, type in dataNamesTypes:
            g.edges["plays"].data[name] = F.copy_to(
                F.tensor(pvalues, dtype=type), ctx=F.ctx()
            )
2469
2470
2471
2472
2473
2474
2475
2476

        if t == F.float16:
            g = dgl.transforms.functional.to_half(g)
        if t == F.float32:
            g = dgl.transforms.functional.to_float(g)
        if t == F.float64:
            g = dgl.transforms.functional.to_double(g)

2477
        for name, origType in dataNamesTypes:
2478
            # integer tensors shouldn't be converted
2479
2480
2481
2482
2483
            reqType = (
                t
                if (origType in [F.float16, F.float32, F.float64])
                else origType
            )
2484

2485
            values = g.nodes["user"].data[name]
2486
2487
2488
2489
            assert values.dtype == reqType
            assert len(values) == len(uvalues)
            assert F.allclose(values, F.tensor(uvalues), 0, 0)

2490
            values = g.nodes["game"].data[name]
2491
2492
2493
2494
            assert values.dtype == reqType
            assert len(values) == len(gvalues)
            assert F.allclose(values, F.tensor(gvalues), 0, 0)

2495
            values = g.edges["follows"].data[name]
2496
2497
2498
2499
            assert values.dtype == reqType
            assert len(values) == len(fvalues)
            assert F.allclose(values, F.tensor(fvalues), 0, 0)

2500
            values = g.edges["plays"].data[name]
2501
2502
2503
2504
            assert values.dtype == reqType
            assert len(values) == len(pvalues)
            assert F.allclose(values, F.tensor(pvalues), 0, 0)

2505

nv-dlasalle's avatar
nv-dlasalle committed
2506
@parametrize_idtype
2507
def test_format(idtype):
2508
    # single relation
2509
    g = dgl.graph(([0, 1, 0, 2], [0, 1, 1, 0]), idtype=idtype, device=F.ctx())
2510
2511
2512
    assert g.formats()["created"] == ["coo"]
    g1 = g.formats(["coo", "csr", "csc"])
    assert len(g1.formats()["created"]) + len(g1.formats()["not created"]) == 3
2513
    g1.create_formats_()
2514
2515
    assert len(g1.formats()["created"]) == 3
    assert g.formats()["created"] == ["coo"]
2516
2517

    # multiple relation
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
    g = dgl.heterograph(
        {
            ("user", "follows", "user"): ([0, 1], [1, 2]),
            ("user", "plays", "game"): ([0, 1, 1, 2], [0, 0, 1, 1]),
            ("developer", "develops", "game"): ([0, 1], [0, 1]),
        },
        idtype=idtype,
        device=F.ctx(),
    )
    user_feat = F.randn((g["follows"].number_of_src_nodes(), 5))
    g["follows"].srcdata["h"] = user_feat
    g1 = g.formats("csc")
2530
    # test frame
2531
    assert F.array_equal(g1["follows"].srcdata["h"], user_feat)
2532
    # test each relation graph
2533
2534
    assert g1.formats()["created"] == ["csc"]
    assert len(g1.formats()["not created"]) == 0
2535

2536
2537
2538
2539
2540
2541
    # in_degrees
    g = dgl.rand_graph(100, 2340).to(F.ctx())
    ind_arr = []
    for vid in range(0, 100):
        ind_arr.append(g.in_degrees(vid))
    in_degrees = g.in_degrees()
2542
    g = g.formats("coo")
2543
2544
2545
2546
    for vid in range(0, 100):
        assert g.in_degrees(vid) == ind_arr[vid]
    assert F.array_equal(in_degrees, g.in_degrees())

2547

nv-dlasalle's avatar
nv-dlasalle committed
2548
@parametrize_idtype
2549
def test_edges_order(idtype):
2550
    # (0, 2), (1, 2), (0, 1), (0, 1), (2, 1)
2551
2552
2553
2554
2555
    g = dgl.graph(
        (np.array([0, 1, 0, 0, 2]), np.array([2, 2, 1, 1, 1])),
        idtype=idtype,
        device=F.ctx(),
    )
2556

2557
    print(g.formats())
2558
    src, dst = g.all_edges(order="srcdst")
2559
2560
    assert F.array_equal(src, F.tensor([0, 0, 0, 1, 2], dtype=idtype))
    assert F.array_equal(dst, F.tensor([1, 1, 2, 2, 1], dtype=idtype))
2561

2562

nv-dlasalle's avatar
nv-dlasalle committed
2563
@parametrize_idtype
2564
def test_reverse(idtype):
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
    g = dgl.heterograph(
        {
            ("user", "follows", "user"): (
                [0, 1, 2, 4, 3, 1, 3],
                [1, 2, 3, 2, 0, 0, 1],
            )
        },
        idtype=idtype,
        device=F.ctx(),
    )
2575
    gidx = g._graph
2576
    r_gidx = gidx.reverse()
2577
2578
2579
2580
2581

    assert gidx.number_of_nodes(0) == r_gidx.number_of_nodes(0)
    assert gidx.number_of_edges(0) == r_gidx.number_of_edges(0)
    g_s, g_d, _ = gidx.edges(0)
    rg_s, rg_d, _ = r_gidx.edges(0)
2582
2583
    assert F.array_equal(g_s, rg_d)
    assert F.array_equal(g_d, rg_s)
2584
2585

    # force to start with 'csr'
2586
2587
    gidx = gidx.formats("csr")
    gidx = gidx.formats(["coo", "csr", "csc"])
2588
    r_gidx = gidx.reverse()
2589
2590
    assert "csr" in gidx.formats()["created"]
    assert "csc" in r_gidx.formats()["created"]
2591
2592
2593
2594
    assert gidx.number_of_nodes(0) == r_gidx.number_of_nodes(0)
    assert gidx.number_of_edges(0) == r_gidx.number_of_edges(0)
    g_s, g_d, _ = gidx.edges(0)
    rg_s, rg_d, _ = r_gidx.edges(0)
2595
2596
    assert F.array_equal(g_s, rg_d)
    assert F.array_equal(g_d, rg_s)
2597
2598

    # force to start with 'csc'
2599
2600
    gidx = gidx.formats("csc")
    gidx = gidx.formats(["coo", "csr", "csc"])
2601
    r_gidx = gidx.reverse()
2602
2603
    assert "csc" in gidx.formats()["created"]
    assert "csr" in r_gidx.formats()["created"]
2604
2605
2606
2607
    assert gidx.number_of_nodes(0) == r_gidx.number_of_nodes(0)
    assert gidx.number_of_edges(0) == r_gidx.number_of_edges(0)
    g_s, g_d, _ = gidx.edges(0)
    rg_s, rg_d, _ = r_gidx.edges(0)
2608
2609
    assert F.array_equal(g_s, rg_d)
    assert F.array_equal(g_d, rg_s)
2610

2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
    g = dgl.heterograph(
        {
            ("user", "follows", "user"): (
                [0, 1, 2, 4, 3, 1, 3],
                [1, 2, 3, 2, 0, 0, 1],
            ),
            ("user", "plays", "game"): (
                [0, 0, 2, 3, 3, 4, 1],
                [1, 0, 1, 0, 1, 0, 0],
            ),
            ("developer", "develops", "game"): ([0, 1, 1, 2], [0, 0, 1, 1]),
        },
        idtype=idtype,
        device=F.ctx(),
    )
2626
    gidx = g._graph
2627
2628
2629
2630
2631
2632
2633
2634
    r_gidx = gidx.reverse()

    # metagraph
    mg = gidx.metagraph
    r_mg = r_gidx.metagraph
    for etype in range(3):
        assert mg.find_edge(etype) == r_mg.find_edge(etype)[::-1]

2635
2636
2637
2638
2639
2640
2641
2642
2643
    # three node types and three edge types
    assert gidx.number_of_nodes(0) == r_gidx.number_of_nodes(0)
    assert gidx.number_of_nodes(1) == r_gidx.number_of_nodes(1)
    assert gidx.number_of_nodes(2) == r_gidx.number_of_nodes(2)
    assert gidx.number_of_edges(0) == r_gidx.number_of_edges(0)
    assert gidx.number_of_edges(1) == r_gidx.number_of_edges(1)
    assert gidx.number_of_edges(2) == r_gidx.number_of_edges(2)
    g_s, g_d, _ = gidx.edges(0)
    rg_s, rg_d, _ = r_gidx.edges(0)
2644
2645
    assert F.array_equal(g_s, rg_d)
    assert F.array_equal(g_d, rg_s)
2646
2647
    g_s, g_d, _ = gidx.edges(1)
    rg_s, rg_d, _ = r_gidx.edges(1)
2648
2649
    assert F.array_equal(g_s, rg_d)
    assert F.array_equal(g_d, rg_s)
2650
2651
    g_s, g_d, _ = gidx.edges(2)
    rg_s, rg_d, _ = r_gidx.edges(2)
2652
2653
    assert F.array_equal(g_s, rg_d)
    assert F.array_equal(g_d, rg_s)
2654
2655

    # force to start with 'csr'
2656
2657
    gidx = gidx.formats("csr")
    gidx = gidx.formats(["coo", "csr", "csc"])
2658
    r_gidx = gidx.reverse()
2659
    # three node types and three edge types
2660
2661
    assert "csr" in gidx.formats()["created"]
    assert "csc" in r_gidx.formats()["created"]
2662
2663
2664
2665
2666
2667
2668
2669
    assert gidx.number_of_nodes(0) == r_gidx.number_of_nodes(0)
    assert gidx.number_of_nodes(1) == r_gidx.number_of_nodes(1)
    assert gidx.number_of_nodes(2) == r_gidx.number_of_nodes(2)
    assert gidx.number_of_edges(0) == r_gidx.number_of_edges(0)
    assert gidx.number_of_edges(1) == r_gidx.number_of_edges(1)
    assert gidx.number_of_edges(2) == r_gidx.number_of_edges(2)
    g_s, g_d, _ = gidx.edges(0)
    rg_s, rg_d, _ = r_gidx.edges(0)
2670
2671
    assert F.array_equal(g_s, rg_d)
    assert F.array_equal(g_d, rg_s)
2672
2673
    g_s, g_d, _ = gidx.edges(1)
    rg_s, rg_d, _ = r_gidx.edges(1)
2674
2675
    assert F.array_equal(g_s, rg_d)
    assert F.array_equal(g_d, rg_s)
2676
2677
    g_s, g_d, _ = gidx.edges(2)
    rg_s, rg_d, _ = r_gidx.edges(2)
2678
2679
    assert F.array_equal(g_s, rg_d)
    assert F.array_equal(g_d, rg_s)
2680
2681

    # force to start with 'csc'
2682
2683
    gidx = gidx.formats("csc")
    gidx = gidx.formats(["coo", "csr", "csc"])
2684
    r_gidx = gidx.reverse()
2685
    # three node types and three edge types
2686
2687
    assert "csc" in gidx.formats()["created"]
    assert "csr" in r_gidx.formats()["created"]
2688
2689
2690
2691
2692
2693
2694
2695
    assert gidx.number_of_nodes(0) == r_gidx.number_of_nodes(0)
    assert gidx.number_of_nodes(1) == r_gidx.number_of_nodes(1)
    assert gidx.number_of_nodes(2) == r_gidx.number_of_nodes(2)
    assert gidx.number_of_edges(0) == r_gidx.number_of_edges(0)
    assert gidx.number_of_edges(1) == r_gidx.number_of_edges(1)
    assert gidx.number_of_edges(2) == r_gidx.number_of_edges(2)
    g_s, g_d, _ = gidx.edges(0)
    rg_s, rg_d, _ = r_gidx.edges(0)
2696
2697
    assert F.array_equal(g_s, rg_d)
    assert F.array_equal(g_d, rg_s)
2698
2699
    g_s, g_d, _ = gidx.edges(1)
    rg_s, rg_d, _ = r_gidx.edges(1)
2700
2701
    assert F.array_equal(g_s, rg_d)
    assert F.array_equal(g_d, rg_s)
2702
2703
    g_s, g_d, _ = gidx.edges(2)
    rg_s, rg_d, _ = r_gidx.edges(2)
2704
2705
2706
    assert F.array_equal(g_s, rg_d)
    assert F.array_equal(g_d, rg_s)

2707

nv-dlasalle's avatar
nv-dlasalle committed
2708
@parametrize_idtype
2709
2710
def test_clone(idtype):
    g = dgl.graph(([0, 1], [1, 2]), idtype=idtype, device=F.ctx())
2711
2712
    g.ndata["h"] = F.copy_to(F.tensor([1, 1, 1], dtype=idtype), ctx=F.ctx())
    g.edata["h"] = F.copy_to(F.tensor([1, 1], dtype=idtype), ctx=F.ctx())
2713
2714
2715
2716
2717
2718

    new_g = g.clone()
    assert g.number_of_nodes() == new_g.number_of_nodes()
    assert g.number_of_edges() == new_g.number_of_edges()
    assert g.device == new_g.device
    assert g.idtype == new_g.idtype
2719
2720
    assert F.array_equal(g.ndata["h"], new_g.ndata["h"])
    assert F.array_equal(g.edata["h"], new_g.edata["h"])
2721
    # data change
2722
2723
2724
2725
    new_g.ndata["h"] = F.copy_to(F.tensor([2, 2, 2], dtype=idtype), ctx=F.ctx())
    assert F.array_equal(g.ndata["h"], new_g.ndata["h"]) == False
    g.edata["h"] = F.copy_to(F.tensor([2, 2], dtype=idtype), ctx=F.ctx())
    assert F.array_equal(g.edata["h"], new_g.edata["h"]) == False
2726
2727
2728
2729
2730
2731
2732
    # graph structure change
    g.add_nodes(1)
    assert g.number_of_nodes() != new_g.number_of_nodes()
    new_g.add_edges(1, 1)
    assert g.number_of_edges() != new_g.number_of_edges()

    # zero data graph
2733
    g = dgl.graph(([], []), num_nodes=0, idtype=idtype, device=F.ctx())
2734
2735
2736
2737
2738
    new_g = g.clone()
    assert g.number_of_nodes() == new_g.number_of_nodes()
    assert g.number_of_edges() == new_g.number_of_edges()

    # heterograph
2739
    g = create_test_heterograph3(idtype)
2740
2741
2742
    g.edges["plays"].data["h"] = F.copy_to(
        F.tensor([1, 2, 3, 4], dtype=idtype), ctx=F.ctx()
    )
2743
    new_g = g.clone()
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
    assert g.number_of_nodes("user") == new_g.number_of_nodes("user")
    assert g.number_of_nodes("game") == new_g.number_of_nodes("game")
    assert g.number_of_nodes("developer") == new_g.number_of_nodes("developer")
    assert g.number_of_edges("plays") == new_g.number_of_edges("plays")
    assert g.number_of_edges("develops") == new_g.number_of_edges("develops")
    assert F.array_equal(
        g.nodes["user"].data["h"], new_g.nodes["user"].data["h"]
    )
    assert F.array_equal(
        g.nodes["game"].data["h"], new_g.nodes["game"].data["h"]
    )
    assert F.array_equal(
        g.edges["plays"].data["h"], new_g.edges["plays"].data["h"]
    )
2758
2759
    assert g.device == new_g.device
    assert g.idtype == new_g.idtype
2760
2761
    u, v = g.edges(form="uv", order="eid", etype="plays")
    nu, nv = new_g.edges(form="uv", order="eid", etype="plays")
2762
2763
2764
2765
2766
    assert F.array_equal(u, nu)
    assert F.array_equal(v, nv)
    # graph structure change
    u = F.tensor([0, 4], dtype=idtype)
    v = F.tensor([2, 6], dtype=idtype)
2767
2768
    g.add_edges(u, v, etype="plays")
    u, v = g.edges(form="uv", order="eid", etype="plays")
2769
2770
    assert u.shape[0] != nu.shape[0]
    assert v.shape[0] != nv.shape[0]
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
    assert (
        g.nodes["user"].data["h"].shape[0]
        != new_g.nodes["user"].data["h"].shape[0]
    )
    assert (
        g.nodes["game"].data["h"].shape[0]
        != new_g.nodes["game"].data["h"].shape[0]
    )
    assert (
        g.edges["plays"].data["h"].shape[0]
        != new_g.edges["plays"].data["h"].shape[0]
    )
2783
2784


nv-dlasalle's avatar
nv-dlasalle committed
2785
@parametrize_idtype
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
def test_add_edges(idtype):
    # homogeneous graph
    g = dgl.graph(([0, 1], [1, 2]), idtype=idtype, device=F.ctx())
    u = 0
    v = 1
    g.add_edges(u, v)
    assert g.device == F.ctx()
    assert g.number_of_nodes() == 3
    assert g.number_of_edges() == 3
    u = [0]
    v = [1]
    g.add_edges(u, v)
    assert g.device == F.ctx()
    assert g.number_of_nodes() == 3
    assert g.number_of_edges() == 4
    u = F.tensor(u, dtype=idtype)
    v = F.tensor(v, dtype=idtype)
    g.add_edges(u, v)
    assert g.device == F.ctx()
    assert g.number_of_nodes() == 3
    assert g.number_of_edges() == 5
2807
    u, v = g.edges(form="uv", order="eid")
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
    assert F.array_equal(u, F.tensor([0, 1, 0, 0, 0], dtype=idtype))
    assert F.array_equal(v, F.tensor([1, 2, 1, 1, 1], dtype=idtype))

    # node id larger than current max node id
    g = dgl.graph(([0, 1], [1, 2]), idtype=idtype, device=F.ctx())
    u = F.tensor([0, 1], dtype=idtype)
    v = F.tensor([2, 3], dtype=idtype)
    g.add_edges(u, v)
    assert g.number_of_nodes() == 4
    assert g.number_of_edges() == 4
2818
    u, v = g.edges(form="uv", order="eid")
2819
2820
2821
2822
2823
    assert F.array_equal(u, F.tensor([0, 1, 0, 1], dtype=idtype))
    assert F.array_equal(v, F.tensor([1, 2, 2, 3], dtype=idtype))

    # has data
    g = dgl.graph(([0, 1], [1, 2]), idtype=idtype, device=F.ctx())
2824
2825
    g.ndata["h"] = F.copy_to(F.tensor([1, 1, 1], dtype=idtype), ctx=F.ctx())
    g.edata["h"] = F.copy_to(F.tensor([1, 1], dtype=idtype), ctx=F.ctx())
2826
2827
    u = F.tensor([0, 1], dtype=idtype)
    v = F.tensor([2, 3], dtype=idtype)
2828
2829
2830
2831
    e_feat = {
        "h": F.copy_to(F.tensor([2, 2], dtype=idtype), ctx=F.ctx()),
        "hh": F.copy_to(F.tensor([2, 2], dtype=idtype), ctx=F.ctx()),
    }
2832
2833
2834
    g.add_edges(u, v, e_feat)
    assert g.number_of_nodes() == 4
    assert g.number_of_edges() == 4
2835
    u, v = g.edges(form="uv", order="eid")
2836
2837
    assert F.array_equal(u, F.tensor([0, 1, 0, 1], dtype=idtype))
    assert F.array_equal(v, F.tensor([1, 2, 2, 3], dtype=idtype))
2838
2839
2840
    assert F.array_equal(g.ndata["h"], F.tensor([1, 1, 1, 0], dtype=idtype))
    assert F.array_equal(g.edata["h"], F.tensor([1, 1, 2, 2], dtype=idtype))
    assert F.array_equal(g.edata["hh"], F.tensor([0, 0, 2, 2], dtype=idtype))
2841
2842

    # zero data graph
2843
    g = dgl.graph(([], []), num_nodes=0, idtype=idtype, device=F.ctx())
2844
2845
    u = F.tensor([0, 1], dtype=idtype)
    v = F.tensor([2, 2], dtype=idtype)
2846
2847
2848
2849
    e_feat = {
        "h": F.copy_to(F.tensor([2, 2], dtype=idtype), ctx=F.ctx()),
        "hh": F.copy_to(F.tensor([2, 2], dtype=idtype), ctx=F.ctx()),
    }
2850
2851
2852
    g.add_edges(u, v, e_feat)
    assert g.number_of_nodes() == 3
    assert g.number_of_edges() == 2
2853
    u, v = g.edges(form="uv", order="eid")
2854
2855
    assert F.array_equal(u, F.tensor([0, 1], dtype=idtype))
    assert F.array_equal(v, F.tensor([2, 2], dtype=idtype))
2856
2857
    assert F.array_equal(g.edata["h"], F.tensor([2, 2], dtype=idtype))
    assert F.array_equal(g.edata["hh"], F.tensor([2, 2], dtype=idtype))
2858
2859

    # bipartite graph
2860
2861
2862
2863
2864
    g = dgl.heterograph(
        {("user", "plays", "game"): ([0, 1], [1, 2])},
        idtype=idtype,
        device=F.ctx(),
    )
2865
2866
2867
2868
    u = 0
    v = 1
    g.add_edges(u, v)
    assert g.device == F.ctx()
2869
2870
    assert g.number_of_nodes("user") == 2
    assert g.number_of_nodes("game") == 3
2871
2872
2873
2874
2875
    assert g.number_of_edges() == 3
    u = [0]
    v = [1]
    g.add_edges(u, v)
    assert g.device == F.ctx()
2876
2877
    assert g.number_of_nodes("user") == 2
    assert g.number_of_nodes("game") == 3
2878
2879
2880
2881
2882
    assert g.number_of_edges() == 4
    u = F.tensor(u, dtype=idtype)
    v = F.tensor(v, dtype=idtype)
    g.add_edges(u, v)
    assert g.device == F.ctx()
2883
2884
    assert g.number_of_nodes("user") == 2
    assert g.number_of_nodes("game") == 3
2885
    assert g.number_of_edges() == 5
2886
    u, v = g.edges(form="uv")
2887
2888
2889
2890
    assert F.array_equal(u, F.tensor([0, 1, 0, 0, 0], dtype=idtype))
    assert F.array_equal(v, F.tensor([1, 2, 1, 1, 1], dtype=idtype))

    # node id larger than current max node id
2891
2892
2893
2894
2895
    g = dgl.heterograph(
        {("user", "plays", "game"): ([0, 1], [1, 2])},
        idtype=idtype,
        device=F.ctx(),
    )
2896
2897
2898
2899
    u = F.tensor([0, 2], dtype=idtype)
    v = F.tensor([2, 3], dtype=idtype)
    g.add_edges(u, v)
    assert g.device == F.ctx()
2900
2901
    assert g.number_of_nodes("user") == 3
    assert g.number_of_nodes("game") == 4
2902
    assert g.number_of_edges() == 4
2903
    u, v = g.edges(form="uv", order="eid")
2904
2905
2906
2907
    assert F.array_equal(u, F.tensor([0, 1, 0, 2], dtype=idtype))
    assert F.array_equal(v, F.tensor([1, 2, 2, 3], dtype=idtype))

    # has data
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
    g = dgl.heterograph(
        {("user", "plays", "game"): ([0, 1], [1, 2])},
        idtype=idtype,
        device=F.ctx(),
    )
    g.nodes["user"].data["h"] = F.copy_to(
        F.tensor([1, 1], dtype=idtype), ctx=F.ctx()
    )
    g.nodes["game"].data["h"] = F.copy_to(
        F.tensor([2, 2, 2], dtype=idtype), ctx=F.ctx()
    )
    g.edata["h"] = F.copy_to(F.tensor([1, 1], dtype=idtype), ctx=F.ctx())
2920
2921
    u = F.tensor([0, 2], dtype=idtype)
    v = F.tensor([2, 3], dtype=idtype)
2922
2923
2924
2925
    e_feat = {
        "h": F.copy_to(F.tensor([2, 2], dtype=idtype), ctx=F.ctx()),
        "hh": F.copy_to(F.tensor([2, 2], dtype=idtype), ctx=F.ctx()),
    }
2926
    g.add_edges(u, v, e_feat)
2927
2928
    assert g.number_of_nodes("user") == 3
    assert g.number_of_nodes("game") == 4
2929
    assert g.number_of_edges() == 4
2930
    u, v = g.edges(form="uv", order="eid")
2931
2932
    assert F.array_equal(u, F.tensor([0, 1, 0, 2], dtype=idtype))
    assert F.array_equal(v, F.tensor([1, 2, 2, 3], dtype=idtype))
2933
2934
2935
2936
2937
2938
2939
2940
    assert F.array_equal(
        g.nodes["user"].data["h"], F.tensor([1, 1, 0], dtype=idtype)
    )
    assert F.array_equal(
        g.nodes["game"].data["h"], F.tensor([2, 2, 2, 0], dtype=idtype)
    )
    assert F.array_equal(g.edata["h"], F.tensor([1, 1, 2, 2], dtype=idtype))
    assert F.array_equal(g.edata["hh"], F.tensor([0, 0, 2, 2], dtype=idtype))
2941
2942

    # heterogeneous graph
2943
    g = create_test_heterograph3(idtype)
2944
2945
    u = F.tensor([0, 2], dtype=idtype)
    v = F.tensor([2, 3], dtype=idtype)
2946
2947
2948
2949
2950
2951
2952
    g.add_edges(u, v, etype="plays")
    assert g.number_of_nodes("user") == 3
    assert g.number_of_nodes("game") == 4
    assert g.number_of_nodes("developer") == 2
    assert g.number_of_edges("plays") == 6
    assert g.number_of_edges("develops") == 2
    u, v = g.edges(form="uv", order="eid", etype="plays")
2953
2954
    assert F.array_equal(u, F.tensor([0, 1, 1, 2, 0, 2], dtype=idtype))
    assert F.array_equal(v, F.tensor([0, 0, 1, 1, 2, 3], dtype=idtype))
2955
2956
2957
2958
2959
2960
2961
2962
2963
    assert F.array_equal(
        g.nodes["user"].data["h"], F.tensor([1, 1, 1], dtype=idtype)
    )
    assert F.array_equal(
        g.nodes["game"].data["h"], F.tensor([2, 2, 0, 0], dtype=idtype)
    )
    assert F.array_equal(
        g.edges["plays"].data["h"], F.tensor([1, 1, 1, 1, 0, 0], dtype=idtype)
    )
2964
2965

    # add with feature
2966
    e_feat = {"h": F.copy_to(F.tensor([2, 2], dtype=idtype), ctx=F.ctx())}
2967
2968
    u = F.tensor([0, 2], dtype=idtype)
    v = F.tensor([2, 3], dtype=idtype)
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
    g.nodes["game"].data["h"] = F.copy_to(
        F.tensor([2, 2, 1, 1], dtype=idtype), ctx=F.ctx()
    )
    g.add_edges(u, v, data=e_feat, etype="develops")
    assert g.number_of_nodes("user") == 3
    assert g.number_of_nodes("game") == 4
    assert g.number_of_nodes("developer") == 3
    assert g.number_of_edges("plays") == 6
    assert g.number_of_edges("develops") == 4
    u, v = g.edges(form="uv", order="eid", etype="develops")
2979
2980
    assert F.array_equal(u, F.tensor([0, 1, 0, 2], dtype=idtype))
    assert F.array_equal(v, F.tensor([0, 1, 2, 3], dtype=idtype))
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
    assert F.array_equal(
        g.nodes["developer"].data["h"], F.tensor([3, 3, 0], dtype=idtype)
    )
    assert F.array_equal(
        g.nodes["game"].data["h"], F.tensor([2, 2, 1, 1], dtype=idtype)
    )
    assert F.array_equal(
        g.edges["develops"].data["h"], F.tensor([0, 0, 2, 2], dtype=idtype)
    )

2991

nv-dlasalle's avatar
nv-dlasalle committed
2992
@parametrize_idtype
2993
2994
2995
def test_add_nodes(idtype):
    # homogeneous Graphs
    g = dgl.graph(([0, 1], [1, 2]), idtype=idtype, device=F.ctx())
2996
    g.ndata["h"] = F.copy_to(F.tensor([1, 1, 1], dtype=idtype), ctx=F.ctx())
2997
2998
    g.add_nodes(1)
    assert g.number_of_nodes() == 4
2999
    assert F.array_equal(g.ndata["h"], F.tensor([1, 1, 1, 0], dtype=idtype))
3000
3001

    # zero node graph
3002
    g = dgl.graph(([], []), num_nodes=3, idtype=idtype, device=F.ctx())
3003
3004
3005
3006
    g.ndata["h"] = F.copy_to(F.tensor([1, 1, 1], dtype=idtype), ctx=F.ctx())
    g.add_nodes(
        1, data={"h": F.copy_to(F.tensor([2], dtype=idtype), ctx=F.ctx())}
    )
3007
    assert g.number_of_nodes() == 4
3008
    assert F.array_equal(g.ndata["h"], F.tensor([1, 1, 1, 2], dtype=idtype))
3009
3010

    # bipartite graph
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
    g = dgl.heterograph(
        {("user", "plays", "game"): ([0, 1], [1, 2])},
        idtype=idtype,
        device=F.ctx(),
    )
    g.add_nodes(
        2,
        data={"h": F.copy_to(F.tensor([2, 2], dtype=idtype), ctx=F.ctx())},
        ntype="user",
    )
    assert g.number_of_nodes("user") == 4
    assert F.array_equal(
        g.nodes["user"].data["h"], F.tensor([0, 0, 2, 2], dtype=idtype)
    )
    g.add_nodes(2, ntype="game")
    assert g.number_of_nodes("game") == 5
3027
3028

    # heterogeneous graph
3029
    g = create_test_heterograph3(idtype)
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
    g.add_nodes(1, ntype="user")
    g.add_nodes(
        2,
        data={"h": F.copy_to(F.tensor([2, 2], dtype=idtype), ctx=F.ctx())},
        ntype="game",
    )
    g.add_nodes(0, ntype="developer")
    assert g.number_of_nodes("user") == 4
    assert g.number_of_nodes("game") == 4
    assert g.number_of_nodes("developer") == 2
    assert F.array_equal(
        g.nodes["user"].data["h"], F.tensor([1, 1, 1, 0], dtype=idtype)
    )
    assert F.array_equal(
        g.nodes["game"].data["h"], F.tensor([2, 2, 2, 2], dtype=idtype)
    )


@unittest.skipIf(
    dgl.backend.backend_name == "mxnet",
    reason="MXNet has error with (0,) shape tensor.",
)
nv-dlasalle's avatar
nv-dlasalle committed
3052
@parametrize_idtype
3053
3054
3055
3056
3057
3058
def test_remove_edges(idtype):
    # homogeneous Graphs
    g = dgl.graph(([0, 1], [1, 2]), idtype=idtype, device=F.ctx())
    e = 0
    g.remove_edges(e)
    assert g.number_of_edges() == 1
3059
    u, v = g.edges(form="uv", order="eid")
3060
3061
3062
3063
3064
3065
    assert F.array_equal(u, F.tensor([1], dtype=idtype))
    assert F.array_equal(v, F.tensor([2], dtype=idtype))
    g = dgl.graph(([0, 1], [1, 2]), idtype=idtype, device=F.ctx())
    e = [0]
    g.remove_edges(e)
    assert g.number_of_edges() == 1
3066
    u, v = g.edges(form="uv", order="eid")
3067
3068
3069
3070
3071
3072
3073
3074
    assert F.array_equal(u, F.tensor([1], dtype=idtype))
    assert F.array_equal(v, F.tensor([2], dtype=idtype))
    e = F.tensor([0], dtype=idtype)
    g.remove_edges(e)
    assert g.number_of_edges() == 0

    # has node data
    g = dgl.graph(([0, 1], [1, 2]), idtype=idtype, device=F.ctx())
3075
    g.ndata["h"] = F.copy_to(F.tensor([1, 2, 3], dtype=idtype), ctx=F.ctx())
3076
3077
    g.remove_edges(1)
    assert g.number_of_edges() == 1
3078
    assert F.array_equal(g.ndata["h"], F.tensor([1, 2, 3], dtype=idtype))
3079
3080
3081

    # has edge data
    g = dgl.graph(([0, 1], [1, 2]), idtype=idtype, device=F.ctx())
3082
    g.edata["h"] = F.copy_to(F.tensor([1, 2], dtype=idtype), ctx=F.ctx())
3083
3084
    g.remove_edges(0)
    assert g.number_of_edges() == 1
3085
    assert F.array_equal(g.edata["h"], F.tensor([2], dtype=idtype))
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095

    # invalid eid
    assert_fail = False
    try:
        g.remove_edges(1)
    except:
        assert_fail = True
    assert assert_fail

    # bipartite graph
3096
3097
3098
3099
3100
    g = dgl.heterograph(
        {("user", "plays", "game"): ([0, 1], [1, 2])},
        idtype=idtype,
        device=F.ctx(),
    )
3101
3102
3103
    e = 0
    g.remove_edges(e)
    assert g.number_of_edges() == 1
3104
    u, v = g.edges(form="uv", order="eid")
3105
3106
    assert F.array_equal(u, F.tensor([1], dtype=idtype))
    assert F.array_equal(v, F.tensor([2], dtype=idtype))
3107
    g = dgl.heterograph(
3108
3109
3110
3111
        {("user", "plays", "game"): ([0, 1], [1, 2])},
        idtype=idtype,
        device=F.ctx(),
    )
3112
3113
3114
    e = [0]
    g.remove_edges(e)
    assert g.number_of_edges() == 1
3115
    u, v = g.edges(form="uv", order="eid")
3116
3117
3118
3119
3120
3121
3122
    assert F.array_equal(u, F.tensor([1], dtype=idtype))
    assert F.array_equal(v, F.tensor([2], dtype=idtype))
    e = F.tensor([0], dtype=idtype)
    g.remove_edges(e)
    assert g.number_of_edges() == 0

    # has data
3123
    g = dgl.heterograph(
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
        {("user", "plays", "game"): ([0, 1], [1, 2])},
        idtype=idtype,
        device=F.ctx(),
    )
    g.nodes["user"].data["h"] = F.copy_to(
        F.tensor([1, 1], dtype=idtype), ctx=F.ctx()
    )
    g.nodes["game"].data["h"] = F.copy_to(
        F.tensor([2, 2, 2], dtype=idtype), ctx=F.ctx()
    )
    g.edata["h"] = F.copy_to(F.tensor([1, 2], dtype=idtype), ctx=F.ctx())
3135
3136
    g.remove_edges(1)
    assert g.number_of_edges() == 1
3137
3138
3139
3140
3141
3142
3143
    assert F.array_equal(
        g.nodes["user"].data["h"], F.tensor([1, 1], dtype=idtype)
    )
    assert F.array_equal(
        g.nodes["game"].data["h"], F.tensor([2, 2, 2], dtype=idtype)
    )
    assert F.array_equal(g.edata["h"], F.tensor([1], dtype=idtype))
3144
3145

    # heterogeneous graph
3146
    g = create_test_heterograph3(idtype)
3147
3148
3149
3150
3151
3152
    g.edges["plays"].data["h"] = F.copy_to(
        F.tensor([1, 2, 3, 4], dtype=idtype), ctx=F.ctx()
    )
    g.remove_edges(1, etype="plays")
    assert g.number_of_edges("plays") == 3
    u, v = g.edges(form="uv", order="eid", etype="plays")
3153
3154
    assert F.array_equal(u, F.tensor([0, 1, 2], dtype=idtype))
    assert F.array_equal(v, F.tensor([0, 1, 1], dtype=idtype))
3155
3156
3157
    assert F.array_equal(
        g.edges["plays"].data["h"], F.tensor([1, 3, 4], dtype=idtype)
    )
3158
    # remove all edges of 'develops'
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
    g.remove_edges([0, 1], etype="develops")
    assert g.number_of_edges("develops") == 0
    assert F.array_equal(
        g.nodes["user"].data["h"], F.tensor([1, 1, 1], dtype=idtype)
    )
    assert F.array_equal(
        g.nodes["game"].data["h"], F.tensor([2, 2], dtype=idtype)
    )
    assert F.array_equal(
        g.nodes["developer"].data["h"], F.tensor([3, 3], dtype=idtype)
    )

3171

nv-dlasalle's avatar
nv-dlasalle committed
3172
@parametrize_idtype
3173
3174
3175
3176
3177
3178
3179
def test_remove_nodes(idtype):
    # homogeneous Graphs
    g = dgl.graph(([0, 1], [1, 2]), idtype=idtype, device=F.ctx())
    n = 0
    g.remove_nodes(n)
    assert g.number_of_nodes() == 2
    assert g.number_of_edges() == 1
3180
    u, v = g.edges(form="uv", order="eid")
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
    assert F.array_equal(u, F.tensor([0], dtype=idtype))
    assert F.array_equal(v, F.tensor([1], dtype=idtype))
    g = dgl.graph(([0, 1], [1, 2]), idtype=idtype, device=F.ctx())
    n = [1]
    g.remove_nodes(n)
    assert g.number_of_nodes() == 2
    assert g.number_of_edges() == 0
    g = dgl.graph(([0, 1], [1, 2]), idtype=idtype, device=F.ctx())
    n = F.tensor([2], dtype=idtype)
    g.remove_nodes(n)
    assert g.number_of_nodes() == 2
    assert g.number_of_edges() == 1
3193
    u, v = g.edges(form="uv", order="eid")
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
    assert F.array_equal(u, F.tensor([0], dtype=idtype))
    assert F.array_equal(v, F.tensor([1], dtype=idtype))

    # invalid nid
    assert_fail = False
    try:
        g.remove_nodes(3)
    except:
        assert_fail = True
    assert assert_fail

    # has node and edge data
    g = dgl.graph(([0, 0, 2], [0, 1, 2]), idtype=idtype, device=F.ctx())
3207
3208
    g.ndata["hv"] = F.copy_to(F.tensor([1, 2, 3], dtype=idtype), ctx=F.ctx())
    g.edata["he"] = F.copy_to(F.tensor([1, 2, 3], dtype=idtype), ctx=F.ctx())
3209
3210
3211
    g.remove_nodes(F.tensor([0], dtype=idtype))
    assert g.number_of_nodes() == 2
    assert g.number_of_edges() == 1
3212
    u, v = g.edges(form="uv", order="eid")
3213
3214
    assert F.array_equal(u, F.tensor([1], dtype=idtype))
    assert F.array_equal(v, F.tensor([1], dtype=idtype))
3215
3216
    assert F.array_equal(g.ndata["hv"], F.tensor([2, 3], dtype=idtype))
    assert F.array_equal(g.edata["he"], F.tensor([3], dtype=idtype))
3217
3218

    # node id larger than current max node id
3219
    g = dgl.heterograph(
3220
3221
3222
3223
        {("user", "plays", "game"): ([0, 1], [1, 2])},
        idtype=idtype,
        device=F.ctx(),
    )
3224
    n = 0
3225
3226
3227
    g.remove_nodes(n, ntype="user")
    assert g.number_of_nodes("user") == 1
    assert g.number_of_nodes("game") == 3
3228
    assert g.number_of_edges() == 1
3229
    u, v = g.edges(form="uv", order="eid")
3230
3231
    assert F.array_equal(u, F.tensor([0], dtype=idtype))
    assert F.array_equal(v, F.tensor([2], dtype=idtype))
3232
    g = dgl.heterograph(
3233
3234
3235
3236
        {("user", "plays", "game"): ([0, 1], [1, 2])},
        idtype=idtype,
        device=F.ctx(),
    )
3237
    n = [1]
3238
3239
3240
    g.remove_nodes(n, ntype="user")
    assert g.number_of_nodes("user") == 1
    assert g.number_of_nodes("game") == 3
3241
    assert g.number_of_edges() == 1
3242
    u, v = g.edges(form="uv", order="eid")
3243
3244
    assert F.array_equal(u, F.tensor([0], dtype=idtype))
    assert F.array_equal(v, F.tensor([1], dtype=idtype))
3245
    g = dgl.heterograph(
3246
3247
3248
3249
        {("user", "plays", "game"): ([0, 1], [1, 2])},
        idtype=idtype,
        device=F.ctx(),
    )
3250
    n = F.tensor([0], dtype=idtype)
3251
3252
3253
    g.remove_nodes(n, ntype="game")
    assert g.number_of_nodes("user") == 2
    assert g.number_of_nodes("game") == 2
3254
    assert g.number_of_edges() == 2
3255
    u, v = g.edges(form="uv", order="eid")
3256
    assert F.array_equal(u, F.tensor([0, 1], dtype=idtype))
3257
    assert F.array_equal(v, F.tensor([0, 1], dtype=idtype))
3258
3259

    # heterogeneous graph
3260
    g = create_test_heterograph3(idtype)
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
    g.edges["plays"].data["h"] = F.copy_to(
        F.tensor([1, 2, 3, 4], dtype=idtype), ctx=F.ctx()
    )
    g.remove_nodes(0, ntype="game")
    assert g.number_of_nodes("user") == 3
    assert g.number_of_nodes("game") == 1
    assert g.number_of_nodes("developer") == 2
    assert g.number_of_edges("plays") == 2
    assert g.number_of_edges("develops") == 1
    assert F.array_equal(
        g.nodes["user"].data["h"], F.tensor([1, 1, 1], dtype=idtype)
    )
    assert F.array_equal(g.nodes["game"].data["h"], F.tensor([2], dtype=idtype))
    assert F.array_equal(
        g.nodes["developer"].data["h"], F.tensor([3, 3], dtype=idtype)
    )
    u, v = g.edges(form="uv", order="eid", etype="plays")
3278
3279
    assert F.array_equal(u, F.tensor([1, 2], dtype=idtype))
    assert F.array_equal(v, F.tensor([0, 0], dtype=idtype))
3280
3281
3282
3283
    assert F.array_equal(
        g.edges["plays"].data["h"], F.tensor([3, 4], dtype=idtype)
    )
    u, v = g.edges(form="uv", order="eid", etype="develops")
3284
3285
    assert F.array_equal(u, F.tensor([1], dtype=idtype))
    assert F.array_equal(v, F.tensor([0], dtype=idtype))
3286

3287

nv-dlasalle's avatar
nv-dlasalle committed
3288
@parametrize_idtype
3289
3290
def test_frame(idtype):
    g = dgl.graph(([0, 1, 2], [1, 2, 3]), idtype=idtype, device=F.ctx())
3291
3292
    g.ndata["h"] = F.copy_to(F.tensor([0, 1, 2, 3], dtype=idtype), ctx=F.ctx())
    g.edata["h"] = F.copy_to(F.tensor([0, 1, 2], dtype=idtype), ctx=F.ctx())
3293
3294
3295
3296

    # remove nodes
    sg = dgl.remove_nodes(g, [3])
    # check for lazy update
3297
3298
3299
3300
    assert F.array_equal(sg._node_frames[0]._columns["h"].storage, g.ndata["h"])
    assert F.array_equal(sg._edge_frames[0]._columns["h"].storage, g.edata["h"])
    assert sg.ndata["h"].shape[0] == 3
    assert sg.edata["h"].shape[0] == 2
3301
    # update after read
3302
3303
3304
3305
3306
3307
3308
    assert F.array_equal(
        sg._node_frames[0]._columns["h"].storage,
        F.tensor([0, 1, 2], dtype=idtype),
    )
    assert F.array_equal(
        sg._edge_frames[0]._columns["h"].storage, F.tensor([0, 1], dtype=idtype)
    )
3309
3310

    ng = dgl.add_nodes(sg, 1)
3311
3312
3313
3314
3315
    assert ng.ndata["h"].shape[0] == 4
    assert F.array_equal(
        ng._node_frames[0]._columns["h"].storage,
        F.tensor([0, 1, 2, 0], dtype=idtype),
    )
3316
    ng = dgl.add_edges(ng, [3], [1])
3317
3318
3319
3320
3321
    assert ng.edata["h"].shape[0] == 3
    assert F.array_equal(
        ng._edge_frames[0]._columns["h"].storage,
        F.tensor([0, 1, 0], dtype=idtype),
    )
3322
3323
3324

    # multi level lazy update
    sg = dgl.remove_nodes(g, [3])
3325
3326
    assert F.array_equal(sg._node_frames[0]._columns["h"].storage, g.ndata["h"])
    assert F.array_equal(sg._edge_frames[0]._columns["h"].storage, g.edata["h"])
3327
    ssg = dgl.remove_nodes(sg, [1])
3328
3329
3330
3331
3332
3333
    assert F.array_equal(
        ssg._node_frames[0]._columns["h"].storage, g.ndata["h"]
    )
    assert F.array_equal(
        ssg._edge_frames[0]._columns["h"].storage, g.edata["h"]
    )
3334
    # ssg is changed
3335
3336
3337
3338
3339
3340
    assert ssg.ndata["h"].shape[0] == 2
    assert ssg.edata["h"].shape[0] == 0
    assert F.array_equal(
        ssg._node_frames[0]._columns["h"].storage,
        F.tensor([0, 2], dtype=idtype),
    )
3341
    # sg still in lazy model
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
    assert F.array_equal(sg._node_frames[0]._columns["h"].storage, g.ndata["h"])
    assert F.array_equal(sg._edge_frames[0]._columns["h"].storage, g.edata["h"])


@unittest.skipIf(
    dgl.backend.backend_name == "tensorflow",
    reason="TensorFlow always create a new tensor",
)
@unittest.skipIf(
    F._default_context_str == "cpu",
    reason="cpu do not have context change problem",
)
nv-dlasalle's avatar
nv-dlasalle committed
3354
@parametrize_idtype
3355
def test_frame_device(idtype):
3356
3357
3358
3359
    g = dgl.graph(([0, 1, 2], [2, 3, 1]))
    g.ndata["h"] = F.copy_to(F.tensor([1, 1, 1, 2], dtype=idtype), ctx=F.cpu())
    g.ndata["hh"] = F.copy_to(F.ones((4, 3), dtype=idtype), ctx=F.cpu())
    g.edata["h"] = F.copy_to(F.tensor([1, 2, 3], dtype=idtype), ctx=F.cpu())
3360
3361
3362

    g = g.to(F.ctx())
    # lazy device copy
3363
3364
3365
3366
3367
3368
    assert F.context(g._node_frames[0]._columns["h"].storage) == F.cpu()
    assert F.context(g._node_frames[0]._columns["hh"].storage) == F.cpu()
    print(g.ndata["h"])
    assert F.context(g._node_frames[0]._columns["h"].storage) == F.ctx()
    assert F.context(g._node_frames[0]._columns["hh"].storage) == F.cpu()
    assert F.context(g._edge_frames[0]._columns["h"].storage) == F.cpu()
3369
3370

    # lazy device copy in subgraph
3371
3372
3373
3374
3375
3376
3377
    sg = dgl.node_subgraph(g, [0, 1, 2])
    assert F.context(sg._node_frames[0]._columns["h"].storage) == F.ctx()
    assert F.context(sg._node_frames[0]._columns["hh"].storage) == F.cpu()
    assert F.context(sg._edge_frames[0]._columns["h"].storage) == F.cpu()
    print(sg.ndata["hh"])
    assert F.context(sg._node_frames[0]._columns["hh"].storage) == F.ctx()
    assert F.context(sg._edge_frames[0]._columns["h"].storage) == F.cpu()
3378
3379
3380

    # back to cpu
    sg = sg.to(F.cpu())
3381
3382
3383
3384
3385
3386
3387
3388
3389
    assert F.context(sg._node_frames[0]._columns["h"].storage) == F.ctx()
    assert F.context(sg._node_frames[0]._columns["hh"].storage) == F.ctx()
    assert F.context(sg._edge_frames[0]._columns["h"].storage) == F.cpu()
    print(sg.ndata["h"])
    print(sg.ndata["hh"])
    print(sg.edata["h"])
    assert F.context(sg._node_frames[0]._columns["h"].storage) == F.cpu()
    assert F.context(sg._node_frames[0]._columns["hh"].storage) == F.cpu()
    assert F.context(sg._edge_frames[0]._columns["h"].storage) == F.cpu()
3390
3391
3392

    # set some field
    sg = sg.to(F.ctx())
3393
3394
3395
3396
3397
    assert F.context(sg._node_frames[0]._columns["h"].storage) == F.cpu()
    sg.ndata["h"][0] = 5
    assert F.context(sg._node_frames[0]._columns["h"].storage) == F.ctx()
    assert F.context(sg._node_frames[0]._columns["hh"].storage) == F.cpu()
    assert F.context(sg._edge_frames[0]._columns["h"].storage) == F.cpu()
3398
3399
3400

    # add nodes
    ng = dgl.add_nodes(sg, 3)
3401
3402
3403
3404
    assert F.context(ng._node_frames[0]._columns["h"].storage) == F.ctx()
    assert F.context(ng._node_frames[0]._columns["hh"].storage) == F.ctx()
    assert F.context(ng._edge_frames[0]._columns["h"].storage) == F.cpu()

3405

nv-dlasalle's avatar
nv-dlasalle committed
3406
@parametrize_idtype
3407
def test_create_block(idtype):
3408
3409
3410
    block = dgl.create_block(
        ([0, 1, 2], [1, 2, 3]), idtype=idtype, device=F.ctx()
    )
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
    assert block.num_src_nodes() == 3
    assert block.num_dst_nodes() == 4
    assert block.num_edges() == 3

    block = dgl.create_block(([], []), idtype=idtype, device=F.ctx())
    assert block.num_src_nodes() == 0
    assert block.num_dst_nodes() == 0
    assert block.num_edges() == 0

    block = dgl.create_block(([], []), 3, 4, idtype=idtype, device=F.ctx())
    assert block.num_src_nodes() == 3
    assert block.num_dst_nodes() == 4
    assert block.num_edges() == 0

3425
3426
3427
    block = dgl.create_block(
        ([0, 1, 2], [1, 2, 3]), 4, 5, idtype=idtype, device=F.ctx()
    )
3428
3429
3430
3431
3432
3433
3434
    assert block.num_src_nodes() == 4
    assert block.num_dst_nodes() == 5
    assert block.num_edges() == 3

    sx = F.randn((4, 5))
    dx = F.randn((5, 6))
    ex = F.randn((3, 4))
3435
3436
3437
    block.srcdata["x"] = sx
    block.dstdata["x"] = dx
    block.edata["x"] = ex
3438
3439
3440
3441
3442

    g = dgl.block_to_graph(block)
    assert g.num_src_nodes() == 4
    assert g.num_dst_nodes() == 5
    assert g.num_edges() == 3
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
    assert g.srcdata["x"] is sx
    assert g.dstdata["x"] is dx
    assert g.edata["x"] is ex

    block = dgl.create_block(
        {
            ("A", "AB", "B"): ([1, 2, 3], [2, 1, 0]),
            ("B", "BA", "A"): ([2, 3], [3, 4]),
        },
        idtype=idtype,
        device=F.ctx(),
    )
    assert block.num_src_nodes("A") == 4
    assert block.num_src_nodes("B") == 4
    assert block.num_dst_nodes("B") == 3
    assert block.num_dst_nodes("A") == 5
    assert block.num_edges("AB") == 3
    assert block.num_edges("BA") == 2

    block = dgl.create_block(
        {("A", "AB", "B"): ([], []), ("B", "BA", "A"): ([], [])},
        idtype=idtype,
        device=F.ctx(),
    )
    assert block.num_src_nodes("A") == 0
    assert block.num_src_nodes("B") == 0
    assert block.num_dst_nodes("B") == 0
    assert block.num_dst_nodes("A") == 0
    assert block.num_edges("AB") == 0
    assert block.num_edges("BA") == 0

    block = dgl.create_block(
        {("A", "AB", "B"): ([], []), ("B", "BA", "A"): ([], [])},
        num_src_nodes={"A": 5, "B": 5},
        num_dst_nodes={"A": 6, "B": 4},
        idtype=idtype,
        device=F.ctx(),
    )
    assert block.num_src_nodes("A") == 5
    assert block.num_src_nodes("B") == 5
    assert block.num_dst_nodes("B") == 4
    assert block.num_dst_nodes("A") == 6
    assert block.num_edges("AB") == 0
    assert block.num_edges("BA") == 0

    block = dgl.create_block(
        {
            ("A", "AB", "B"): ([1, 2, 3], [2, 1, 0]),
            ("B", "BA", "A"): ([2, 3], [3, 4]),
        },
        num_src_nodes={"A": 5, "B": 5},
        num_dst_nodes={"A": 6, "B": 4},
        idtype=idtype,
        device=F.ctx(),
    )
    assert block.num_src_nodes("A") == 5
    assert block.num_src_nodes("B") == 5
    assert block.num_dst_nodes("B") == 4
    assert block.num_dst_nodes("A") == 6
    assert block.num_edges(("A", "AB", "B")) == 3
    assert block.num_edges(("B", "BA", "A")) == 2
3504
3505
3506
3507
3508
3509
3510

    sax = F.randn((5, 3))
    sbx = F.randn((5, 4))
    dax = F.randn((6, 5))
    dbx = F.randn((4, 6))
    eabx = F.randn((3, 7))
    ebax = F.randn((2, 8))
3511
3512
3513
3514
3515
3516
    block.srcnodes["A"].data["x"] = sax
    block.srcnodes["B"].data["x"] = sbx
    block.dstnodes["A"].data["x"] = dax
    block.dstnodes["B"].data["x"] = dbx
    block.edges["AB"].data["x"] = eabx
    block.edges["BA"].data["x"] = ebax
3517
3518

    hg = dgl.block_to_graph(block)
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
    assert hg.num_nodes("A_src") == 5
    assert hg.num_nodes("B_src") == 5
    assert hg.num_nodes("A_dst") == 6
    assert hg.num_nodes("B_dst") == 4
    assert hg.num_edges(("A_src", "AB", "B_dst")) == 3
    assert hg.num_edges(("B_src", "BA", "A_dst")) == 2
    assert hg.nodes["A_src"].data["x"] is sax
    assert hg.nodes["B_src"].data["x"] is sbx
    assert hg.nodes["A_dst"].data["x"] is dax
    assert hg.nodes["B_dst"].data["x"] is dbx
    assert hg.edges["AB"].data["x"] is eabx
    assert hg.edges["BA"].data["x"] is ebax

3532

nv-dlasalle's avatar
nv-dlasalle committed
3533
@parametrize_idtype
3534
@pytest.mark.parametrize("fmt", ["coo", "csr", "csc"])
3535
def test_adj_sparse(idtype, fmt):
3536
    if fmt == "coo":
3537
3538
3539
3540
3541
        A = ssp.random(10, 10, 0.2).tocoo()
        A.data = np.arange(20)
        row = F.tensor(A.row, idtype)
        col = F.tensor(A.col, idtype)
        g = dgl.graph((row, col))
3542
    elif fmt == "csr":
3543
3544
3545
3546
        A = ssp.random(10, 10, 0.2).tocsr()
        A.data = np.arange(20)
        indptr = F.tensor(A.indptr, idtype)
        indices = F.tensor(A.indices, idtype)
3547
        g = dgl.graph(("csr", (indptr, indices, [])))
3548
        with pytest.raises(DGLError):
3549
3550
            g2 = dgl.graph(("csr", (indptr[:-1], indices, [])), num_nodes=10)
    elif fmt == "csc":
3551
3552
3553
3554
        A = ssp.random(10, 10, 0.2).tocsc()
        A.data = np.arange(20)
        indptr = F.tensor(A.indptr, idtype)
        indices = F.tensor(A.indices, idtype)
3555
        g = dgl.graph(("csc", (indptr, indices, [])))
3556
        with pytest.raises(DGLError):
3557
            g2 = dgl.graph(("csr", (indptr[:-1], indices, [])), num_nodes=10)
3558
3559
3560
3561

    A_coo = A.tocoo()
    A_csr = A.tocsr()
    A_csc = A.tocsc()
3562
    row, col = g.adj_sparse("coo")
3563
3564
3565
    assert np.array_equal(F.asnumpy(row), A_coo.row)
    assert np.array_equal(F.asnumpy(col), A_coo.col)

3566
    indptr, indices, eids = g.adj_sparse("csr")
3567
    assert np.array_equal(F.asnumpy(indptr), A_csr.indptr)
3568
    if fmt == "csr":
3569
3570
3571
3572
3573
3574
3575
3576
3577
        assert len(eids) == 0
        assert np.array_equal(F.asnumpy(indices), A_csr.indices)
    else:
        indices_sorted = F.zeros(len(indices), idtype)
        indices_sorted = F.scatter_row(indices_sorted, eids, indices)
        indices_sorted_np = np.zeros(len(indices), dtype=A_csr.indices.dtype)
        indices_sorted_np[A_csr.data] = A_csr.indices
        assert np.array_equal(F.asnumpy(indices_sorted), indices_sorted_np)

3578
    indptr, indices, eids = g.adj_sparse("csc")
3579
    assert np.array_equal(F.asnumpy(indptr), A_csc.indptr)
3580
    if fmt == "csc":
3581
3582
3583
3584
3585
3586
3587
3588
3589
        assert len(eids) == 0
        assert np.array_equal(F.asnumpy(indices), A_csc.indices)
    else:
        indices_sorted = F.zeros(len(indices), idtype)
        indices_sorted = F.scatter_row(indices_sorted, eids, indices)
        indices_sorted_np = np.zeros(len(indices), dtype=A_csc.indices.dtype)
        indices_sorted_np[A_csc.data] = A_csc.indices
        assert np.array_equal(F.asnumpy(indices_sorted), indices_sorted_np)

3590

3591
3592
3593
def _test_forking_pickler_entry(g, q):
    q.put(g.formats())

3594
3595
3596
3597

@unittest.skipIf(
    dgl.backend.backend_name == "mxnet", reason="MXNet doesn't support spawning"
)
3598
def test_forking_pickler():
3599
3600
    ctx = mp.get_context("spawn")
    g = dgl.graph(([0, 1, 2], [1, 2, 3]))
3601
3602
3603
3604
    g.create_formats_()
    q = ctx.Queue(1)
    proc = ctx.Process(target=_test_forking_pickler_entry, args=(g, q))
    proc.start()
3605
    fmt = q.get()["created"]
3606
    proc.join()
3607
3608
3609
    assert "coo" in fmt
    assert "csr" in fmt
    assert "csc" in fmt
3610
3611


3612
if __name__ == "__main__":
3613
3614
3615
3616
3617
    # test_create()
    # test_query()
    # test_hypersparse()
    # test_adj("int32")
    # test_inc()
3618
    # test_view("int32")
3619
    # test_view1("int32")
3620
    # test_flatten(F.int32)
3621
3622
    # test_convert_bound()
    # test_convert()
3623
    # test_to_device("int32")
3624
    # test_transform("int32")
3625
3626
    # test_subgraph("int32")
    # test_subgraph_mask("int32")
3627
3628
3629
3630
3631
    # test_apply()
    # test_level1()
    # test_level2()
    # test_updates()
    # test_backward()
3632
    # test_empty_heterograph('int32')
3633
3634
3635
3636
    # test_types_in_function()
    # test_stack_reduce()
    # test_isolated_ntype()
    # test_bipartite()
3637
    # test_dtype_cast()
3638
    # test_float_cast()
3639
    # test_reverse("int32")
3640
    # test_format()
3641
3642
3643
3644
3645
3646
3647
3648
3649
    # test_add_edges(F.int32)
    # test_add_nodes(F.int32)
    # test_remove_edges(F.int32)
    # test_remove_nodes(F.int32)
    # test_clone(F.int32)
    # test_frame(F.int32)
    # test_frame_device(F.int32)
    # test_empty_query(F.int32)
    # test_create_block(F.int32)
3650
    pass