"src/array/cuda/vscode:/vscode.git/clone" did not exist on "4f5c3aa2344d3373f88a4cc74ba1c0e413b433b4"
test_subgraph.py 29.4 KB
Newer Older
1
2
import unittest

3
import backend as F
4
5

import dgl
6
7
8
9
import networkx as nx
import numpy as np
import pytest
import scipy.sparse as ssp
10
from utils import parametrize_idtype
Minjie Wang's avatar
Minjie Wang committed
11
12
13

D = 5

14

15
def generate_graph(grad=False, add_data=True):
16
    g = dgl.DGLGraph().to(F.ctx())
17
    g.add_nodes(10)
Minjie Wang's avatar
Minjie Wang committed
18
19
    # create a graph where 0 is the source and 9 is the sink
    for i in range(1, 9):
20
21
        g.add_edges(0, i)
        g.add_edges(i, 9)
Minjie Wang's avatar
Minjie Wang committed
22
    # add a back flow from 9 to 0
23
    g.add_edges(9, 0)
24
25
26
27
28
29
    if add_data:
        ncol = F.randn((10, D))
        ecol = F.randn((17, D))
        if grad:
            ncol = F.attach_grad(ncol)
            ecol = F.attach_grad(ecol)
30
31
        g.ndata["h"] = ncol
        g.edata["l"] = ecol
Minjie Wang's avatar
Minjie Wang committed
32
33
    return g

34

35
def test_edge_subgraph():
36
37
38
    # Test when the graph has no node data and edge data.
    g = generate_graph(add_data=False)
    eid = [0, 2, 3, 6, 7, 9]
39
40

    # relabel=True
41
    sg = g.edge_subgraph(eid)
42
43
44
    assert F.array_equal(
        sg.ndata[dgl.NID], F.tensor([0, 2, 4, 5, 1, 9], g.idtype)
    )
45
    assert F.array_equal(sg.edata[dgl.EID], F.tensor(eid, g.idtype))
Hongzhi (Steve), Chen's avatar
Hongzhi (Steve), Chen committed
46
47
    sg.ndata["h"] = F.arange(0, sg.num_nodes())
    sg.edata["h"] = F.arange(0, sg.num_edges())
48
49
50

    # relabel=False
    sg = g.edge_subgraph(eid, relabel_nodes=False)
Hongzhi (Steve), Chen's avatar
Hongzhi (Steve), Chen committed
51
    assert g.num_nodes() == sg.num_nodes()
52
    assert F.array_equal(sg.edata[dgl.EID], F.tensor(eid, g.idtype))
Hongzhi (Steve), Chen's avatar
Hongzhi (Steve), Chen committed
53
54
    sg.ndata["h"] = F.arange(0, sg.num_nodes())
    sg.edata["h"] = F.arange(0, sg.num_edges())
55

56

57
58
@pytest.mark.parametrize("relabel_nodes", [True, False])
def test_subgraph_relabel_nodes(relabel_nodes):
Minjie Wang's avatar
Minjie Wang committed
59
    g = generate_graph()
60
61
    h = g.ndata["h"]
    l = g.edata["l"]
Minjie Wang's avatar
Minjie Wang committed
62
    nid = [0, 2, 3, 6, 7, 9]
63
    sg = g.subgraph(nid, relabel_nodes=relabel_nodes)
64
    eid = {2, 3, 4, 5, 10, 11, 12, 13, 16}
65
66
    assert set(F.asnumpy(sg.edata[dgl.EID])) == eid
    eid = sg.edata[dgl.EID]
67
68
69
70
    # the subgraph is empty initially except for EID field
    # the subgraph is empty initially except for NID field if relabel_nodes
    if relabel_nodes:
        assert len(sg.ndata) == 2
71
    assert len(sg.edata) == 2
72
    sh = sg.ndata["h"]
73
74
75
76
77
78
79
80
81
82
    # The node number is not reduced if relabel_node=False.
    # The subgraph keeps the same node information as the original graph.
    if relabel_nodes:
        assert F.allclose(F.gather_row(h, F.tensor(nid)), sh)
    else:
        assert F.allclose(
            F.gather_row(h, F.tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])), sh
        )
    # The s,d,eid means the source node, destination node and edge id of the subgraph.
    # The edges labeled 1 are those selected by the subgraph.
83
    """
Minjie Wang's avatar
Minjie Wang committed
84
85
86
    s, d, eid
    0, 1, 0
    1, 9, 1
Minjie Wang's avatar
Minjie Wang committed
87
88
89
90
    0, 2, 2    1
    2, 9, 3    1
    0, 3, 4    1
    3, 9, 5    1
Minjie Wang's avatar
Minjie Wang committed
91
92
93
    0, 4, 6
    4, 9, 7
    0, 5, 8
Minjie Wang's avatar
Minjie Wang committed
94
95
96
97
98
    5, 9, 9       3
    0, 6, 10   1
    6, 9, 11   1  3
    0, 7, 12   1
    7, 9, 13   1  3
Minjie Wang's avatar
Minjie Wang committed
99
    0, 8, 14
Minjie Wang's avatar
Minjie Wang committed
100
101
    8, 9, 15      3
    9, 0, 16   1
102
103
    """
    assert F.allclose(F.gather_row(l, eid), sg.edata["l"])
Minjie Wang's avatar
Minjie Wang committed
104
105
    # update the node/edge features on the subgraph should NOT
    # reflect to the parent graph.
106
107
108
109
    if relabel_nodes:
        sg.ndata["h"] = F.zeros((6, D))
    else:
        sg.ndata["h"] = F.zeros((10, D))
110
111
    assert F.allclose(h, g.ndata["h"])

Minjie Wang's avatar
Minjie Wang committed
112

113
114
def _test_map_to_subgraph():
    g = dgl.DGLGraph()
Quan (Andy) Gan's avatar
Quan (Andy) Gan committed
115
116
117
118
119
    g.add_nodes(10)
    g.add_edges(F.arange(0, 9), F.arange(1, 10))
    h = g.subgraph([0, 1, 2, 5, 8])
    v = h.map_to_subgraph_nid([0, 8, 2])
    assert np.array_equal(F.asnumpy(v), np.array([0, 4, 2]))
120

121

122
123
124
125
126
127
128
129
130
def create_test_heterograph(idtype):
    # test heterograph from the docstring, plus a user -- wishes -- game relation
    # 3 users, 2 games, 2 developers
    # metagraph:
    #    ('user', 'follows', 'user'),
    #    ('user', 'plays', 'game'),
    #    ('user', 'wishes', 'game'),
    #    ('developer', 'develops', 'game')])

131
132
133
134
135
136
137
138
139
140
    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(),
    )
141
    for etype in g.etypes:
142
        g.edges[etype].data["weight"] = F.randn((g.num_edges(etype),))
143
144
145
146
    assert g.idtype == idtype
    assert g.device == F.ctx()
    return g

147

148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
def create_test_heterograph2(idtype):
    """test heterograph from the docstring, with an empty relation"""
    # 3 users, 2 games, 2 developers
    # metagraph:
    #    ('user', 'follows', 'user'),
    #    ('user', 'plays', 'game'),
    #    ('user', 'wishes', 'game'),
    #    ('developer', 'develops', 'game')

    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"): ([], []),
        },
        idtype=idtype,
        device=F.ctx(),
    )
    for etype in g.etypes:
        g.edges[etype].data["weight"] = F.randn((g.num_edges(etype),))
    assert g.idtype == idtype
    assert g.device == F.ctx()
    return g


174
175
176
177
@unittest.skipIf(
    dgl.backend.backend_name == "mxnet",
    reason="MXNet doesn't support bool tensor",
)
nv-dlasalle's avatar
nv-dlasalle committed
178
@parametrize_idtype
179
180
def test_subgraph_mask(idtype):
    g = create_test_heterograph(idtype)
181
182
    g_graph = g["follows"]
    g_bipartite = g["plays"]
183
184
185

    x = F.randn((3, 5))
    y = F.randn((2, 4))
186
187
    g.nodes["user"].data["h"] = x
    g.edges["follows"].data["h"] = y
188
189
190
191
192
193
194

    def _check_subgraph(g, sg):
        assert sg.idtype == g.idtype
        assert sg.device == g.device
        assert sg.ntypes == g.ntypes
        assert sg.etypes == g.etypes
        assert sg.canonical_etypes == g.canonical_etypes
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
        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)
        )
Hongzhi (Steve), Chen's avatar
Hongzhi (Steve), Chen committed
210
211
        assert sg.num_nodes("developer") == 0
        assert sg.num_edges("develops") == 0
212
213
214
215
216
217
218
219
220
221
222
223
224
        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),
        }
    )
225
    _check_subgraph(g, sg1)
226
227
228
229
230
231
232
    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),
        }
    )
233
    _check_subgraph(g, sg2)
234

235

nv-dlasalle's avatar
nv-dlasalle committed
236
@parametrize_idtype
237
238
def test_subgraph1(idtype):
    g = create_test_heterograph(idtype)
239
240
    g_graph = g["follows"]
    g_bipartite = g["plays"]
241
242
243

    x = F.randn((3, 5))
    y = F.randn((2, 4))
244
245
    g.nodes["user"].data["h"] = x
    g.edges["follows"].data["h"] = y
246
247
248
249
250
251
252

    def _check_subgraph(g, sg):
        assert sg.idtype == g.idtype
        assert sg.device == g.device
        assert sg.ntypes == g.ntypes
        assert sg.etypes == g.etypes
        assert sg.canonical_etypes == g.canonical_etypes
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
        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)
        )
Hongzhi (Steve), Chen's avatar
Hongzhi (Steve), Chen committed
268
269
        assert sg.num_nodes("developer") == 0
        assert sg.num_edges("develops") == 0
270
271
272
273
274
275
276
277
        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]})
278
    _check_subgraph(g, sg1)
279
    sg2 = g.edge_subgraph({"follows": [1], "plays": [1], "wishes": [1]})
280
    _check_subgraph(g, sg2)
281
282

    # backend tensor input
283
284
285
286
287
288
    sg1 = g.subgraph(
        {
            "user": F.tensor([1, 2], dtype=idtype),
            "game": F.tensor([0], dtype=idtype),
        }
    )
289
    _check_subgraph(g, sg1)
290
291
292
293
294
295
296
    sg2 = g.edge_subgraph(
        {
            "follows": F.tensor([1], dtype=idtype),
            "plays": F.tensor([1], dtype=idtype),
            "wishes": F.tensor([1], dtype=idtype),
        }
    )
297
    _check_subgraph(g, sg2)
298
299

    # numpy input
300
    sg1 = g.subgraph({"user": np.array([1, 2]), "game": np.array([0])})
301
    _check_subgraph(g, sg1)
302
303
304
305
306
307
308
    sg2 = g.edge_subgraph(
        {
            "follows": np.array([1]),
            "plays": np.array([1]),
            "wishes": np.array([1]),
        }
    )
309
    _check_subgraph(g, sg2)
310
311
312
313
314
315
316
317
318

    def _check_subgraph_single_ntype(g, sg, preserve_nodes=False):
        assert sg.idtype == g.idtype
        assert sg.device == g.device
        assert sg.ntypes == g.ntypes
        assert sg.etypes == g.etypes
        assert sg.canonical_etypes == g.canonical_etypes

        if not preserve_nodes:
319
320
321
322
            assert F.array_equal(
                F.tensor(sg.nodes["user"].data[dgl.NID]),
                F.tensor([1, 2], g.idtype),
            )
323
324
        else:
            for ntype in sg.ntypes:
Hongzhi (Steve), Chen's avatar
Hongzhi (Steve), Chen committed
325
                assert g.num_nodes(ntype) == sg.num_nodes(ntype)
326

327
328
329
        assert F.array_equal(
            F.tensor(sg.edges["follows"].data[dgl.EID]), F.tensor([1], g.idtype)
        )
330
331

        if not preserve_nodes:
332
333
334
335
336
337
            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]
        )
338
339
340
341
342
343
344

    def _check_subgraph_single_etype(g, sg, preserve_nodes=False):
        assert sg.ntypes == g.ntypes
        assert sg.etypes == g.etypes
        assert sg.canonical_etypes == g.canonical_etypes

        if not preserve_nodes:
345
346
347
348
349
350
351
352
            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),
            )
353
354
        else:
            for ntype in sg.ntypes:
Hongzhi (Steve), Chen's avatar
Hongzhi (Steve), Chen committed
355
                assert g.num_nodes(ntype) == sg.num_nodes(ntype)
356

357
358
359
360
        assert F.array_equal(
            F.tensor(sg.edges["plays"].data[dgl.EID]),
            F.tensor([0, 1], g.idtype),
        )
361
362
363

    sg1_graph = g_graph.subgraph([1, 2])
    _check_subgraph_single_ntype(g_graph, sg1_graph)
364
365
366
367
368
369
370
371
    sg1_graph = g_graph.edge_subgraph([1])
    _check_subgraph_single_ntype(g_graph, sg1_graph)
    sg1_graph = g_graph.edge_subgraph([1], relabel_nodes=False)
    _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)
    sg2_bipartite = g_bipartite.edge_subgraph([0, 1], relabel_nodes=False)
    _check_subgraph_single_etype(g_bipartite, sg2_bipartite, True)
372
373
374
375

    def _check_typed_subgraph1(g, sg):
        assert g.idtype == sg.idtype
        assert g.device == sg.device
376
377
        assert set(sg.ntypes) == {"user", "game"}
        assert set(sg.etypes) == {"follows", "plays", "wishes"}
378
        for ntype in sg.ntypes:
Hongzhi (Steve), Chen's avatar
Hongzhi (Steve), Chen committed
379
            assert sg.num_nodes(ntype) == g.num_nodes(ntype)
380
        for etype in sg.etypes:
381
382
            src_sg, dst_sg = sg.all_edges(etype=etype, order="eid")
            src_g, dst_g = g.all_edges(etype=etype, order="eid")
383
384
            assert F.array_equal(src_sg, src_g)
            assert F.array_equal(dst_sg, dst_g)
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
        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"]
        )
403
404

    def _check_typed_subgraph2(g, sg):
405
406
        assert set(sg.ntypes) == {"developer", "game"}
        assert set(sg.etypes) == {"develops"}
407
        for ntype in sg.ntypes:
Hongzhi (Steve), Chen's avatar
Hongzhi (Steve), Chen committed
408
            assert sg.num_nodes(ntype) == g.num_nodes(ntype)
409
        for etype in sg.etypes:
410
411
            src_sg, dst_sg = sg.all_edges(etype=etype, order="eid")
            src_g, dst_g = g.all_edges(etype=etype, order="eid")
412
413
414
            assert F.array_equal(src_sg, src_g)
            assert F.array_equal(dst_sg, dst_g)

415
    sg3 = g.node_type_subgraph(["user", "game"])
416
    _check_typed_subgraph1(g, sg3)
417
    sg4 = g.edge_type_subgraph(["develops"])
418
    _check_typed_subgraph2(g, sg4)
419
    sg5 = g.edge_type_subgraph(["follows", "plays", "wishes"])
420
421
422
    _check_typed_subgraph1(g, sg5)

    # Test for restricted format
423
    for fmt in ["csr", "csc", "coo"]:
424
425
426
427
        g = dgl.graph(([0, 1], [1, 2])).formats(fmt)
        sg = g.subgraph({g.ntypes[0]: [1, 0]})
        nids = F.asnumpy(sg.ndata[dgl.NID])
        assert np.array_equal(nids, np.array([1, 0]))
428
        src, dst = sg.edges(order="eid")
429
430
431
432
        src = F.asnumpy(src)
        dst = F.asnumpy(dst)
        assert np.array_equal(src, np.array([1]))

433

nv-dlasalle's avatar
nv-dlasalle committed
434
@parametrize_idtype
435
def test_in_subgraph(idtype):
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
    hg = dgl.heterograph(
        {
            ("user", "follow", "user"): (
                [1, 2, 3, 0, 2, 3, 0],
                [0, 0, 0, 1, 1, 1, 2],
            ),
            ("user", "play", "game"): ([0, 0, 1, 3], [0, 1, 2, 2]),
            ("game", "liked-by", "user"): (
                [2, 2, 2, 1, 1, 0],
                [0, 1, 2, 0, 3, 0],
            ),
            ("user", "flips", "coin"): ([0, 1, 2, 3], [0, 0, 0, 0]),
        },
        idtype=idtype,
        num_nodes_dict={"user": 5, "game": 10, "coin": 8},
    ).to(F.ctx())
    subg = dgl.in_subgraph(hg, {"user": [0, 1], "game": 0})
453
454
455
    assert subg.idtype == idtype
    assert len(subg.ntypes) == 3
    assert len(subg.etypes) == 4
456
    u, v = subg["follow"].edges()
457
    edge_set = set(zip(list(F.asnumpy(u)), list(F.asnumpy(v))))
458
459
460
461
462
    assert F.array_equal(
        hg["follow"].edge_ids(u, v), subg["follow"].edata[dgl.EID]
    )
    assert edge_set == {(1, 0), (2, 0), (3, 0), (0, 1), (2, 1), (3, 1)}
    u, v = subg["play"].edges()
463
    edge_set = set(zip(list(F.asnumpy(u)), list(F.asnumpy(v))))
464
465
466
    assert F.array_equal(hg["play"].edge_ids(u, v), subg["play"].edata[dgl.EID])
    assert edge_set == {(0, 0)}
    u, v = subg["liked-by"].edges()
467
    edge_set = set(zip(list(F.asnumpy(u)), list(F.asnumpy(v))))
468
469
470
471
    assert F.array_equal(
        hg["liked-by"].edge_ids(u, v), subg["liked-by"].edata[dgl.EID]
    )
    assert edge_set == {(2, 0), (2, 1), (1, 0), (0, 0)}
Hongzhi (Steve), Chen's avatar
Hongzhi (Steve), Chen committed
472
    assert subg["flips"].num_edges() == 0
473
474
475
476
    for ntype in subg.ntypes:
        assert dgl.NID not in subg.nodes[ntype].data

    # Test store_ids
477
478
    subg = dgl.in_subgraph(hg, {"user": [0, 1], "game": 0}, store_ids=False)
    for etype in ["follow", "play", "liked-by"]:
479
480
481
482
483
        assert dgl.EID not in subg.edges[etype].data
    for ntype in subg.ntypes:
        assert dgl.NID not in subg.nodes[ntype].data

    # Test relabel nodes
484
    subg = dgl.in_subgraph(hg, {"user": [0, 1], "game": 0}, relabel_nodes=True)
485
486
487
488
    assert subg.idtype == idtype
    assert len(subg.ntypes) == 3
    assert len(subg.etypes) == 4

489
490
491
492
493
494
    u, v = subg["follow"].edges()
    old_u = F.gather_row(subg.nodes["user"].data[dgl.NID], u)
    old_v = F.gather_row(subg.nodes["user"].data[dgl.NID], v)
    assert F.array_equal(
        hg["follow"].edge_ids(old_u, old_v), subg["follow"].edata[dgl.EID]
    )
495
    edge_set = set(zip(list(F.asnumpy(old_u)), list(F.asnumpy(old_v))))
496
497
498
499
500
501
502
503
    assert edge_set == {(1, 0), (2, 0), (3, 0), (0, 1), (2, 1), (3, 1)}

    u, v = subg["play"].edges()
    old_u = F.gather_row(subg.nodes["user"].data[dgl.NID], u)
    old_v = F.gather_row(subg.nodes["game"].data[dgl.NID], v)
    assert F.array_equal(
        hg["play"].edge_ids(old_u, old_v), subg["play"].edata[dgl.EID]
    )
504
    edge_set = set(zip(list(F.asnumpy(old_u)), list(F.asnumpy(old_v))))
505
506
507
508
509
510
511
512
    assert edge_set == {(0, 0)}

    u, v = subg["liked-by"].edges()
    old_u = F.gather_row(subg.nodes["game"].data[dgl.NID], u)
    old_v = F.gather_row(subg.nodes["user"].data[dgl.NID], v)
    assert F.array_equal(
        hg["liked-by"].edge_ids(old_u, old_v), subg["liked-by"].edata[dgl.EID]
    )
513
    edge_set = set(zip(list(F.asnumpy(old_u)), list(F.asnumpy(old_v))))
514
515
516
517
518
519
    assert edge_set == {(2, 0), (2, 1), (1, 0), (0, 0)}

    assert subg.num_nodes("user") == 4
    assert subg.num_nodes("game") == 3
    assert subg.num_nodes("coin") == 0
    assert subg.num_edges("flips") == 0
520

521

nv-dlasalle's avatar
nv-dlasalle committed
522
@parametrize_idtype
523
def test_out_subgraph(idtype):
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
    hg = dgl.heterograph(
        {
            ("user", "follow", "user"): (
                [1, 2, 3, 0, 2, 3, 0],
                [0, 0, 0, 1, 1, 1, 2],
            ),
            ("user", "play", "game"): ([0, 0, 1, 3], [0, 1, 2, 2]),
            ("game", "liked-by", "user"): (
                [2, 2, 2, 1, 1, 0],
                [0, 1, 2, 0, 3, 0],
            ),
            ("user", "flips", "coin"): ([0, 1, 2, 3], [0, 0, 0, 0]),
        },
        idtype=idtype,
    ).to(F.ctx())
    subg = dgl.out_subgraph(hg, {"user": [0, 1], "game": 0})
540
541
542
    assert subg.idtype == idtype
    assert len(subg.ntypes) == 3
    assert len(subg.etypes) == 4
543
    u, v = subg["follow"].edges()
544
    edge_set = set(zip(list(F.asnumpy(u)), list(F.asnumpy(v))))
545
546
547
548
549
    assert edge_set == {(1, 0), (0, 1), (0, 2)}
    assert F.array_equal(
        hg["follow"].edge_ids(u, v), subg["follow"].edata[dgl.EID]
    )
    u, v = subg["play"].edges()
550
    edge_set = set(zip(list(F.asnumpy(u)), list(F.asnumpy(v))))
551
552
553
    assert edge_set == {(0, 0), (0, 1), (1, 2)}
    assert F.array_equal(hg["play"].edge_ids(u, v), subg["play"].edata[dgl.EID])
    u, v = subg["liked-by"].edges()
554
    edge_set = set(zip(list(F.asnumpy(u)), list(F.asnumpy(v))))
555
556
557
558
559
    assert edge_set == {(0, 0)}
    assert F.array_equal(
        hg["liked-by"].edge_ids(u, v), subg["liked-by"].edata[dgl.EID]
    )
    u, v = subg["flips"].edges()
560
    edge_set = set(zip(list(F.asnumpy(u)), list(F.asnumpy(v))))
561
562
563
564
    assert edge_set == {(0, 0), (1, 0)}
    assert F.array_equal(
        hg["flips"].edge_ids(u, v), subg["flips"].edata[dgl.EID]
    )
565
566
567
568
    for ntype in subg.ntypes:
        assert dgl.NID not in subg.nodes[ntype].data

    # Test store_ids
569
    subg = dgl.out_subgraph(hg, {"user": [0, 1], "game": 0}, store_ids=False)
570
571
572
573
574
575
    for etype in subg.canonical_etypes:
        assert dgl.EID not in subg.edges[etype].data
    for ntype in subg.ntypes:
        assert dgl.NID not in subg.nodes[ntype].data

    # Test relabel nodes
576
    subg = dgl.out_subgraph(hg, {"user": [1], "game": 0}, relabel_nodes=True)
577
578
579
580
    assert subg.idtype == idtype
    assert len(subg.ntypes) == 3
    assert len(subg.etypes) == 4

581
582
583
    u, v = subg["follow"].edges()
    old_u = F.gather_row(subg.nodes["user"].data[dgl.NID], u)
    old_v = F.gather_row(subg.nodes["user"].data[dgl.NID], v)
584
585
    edge_set = set(zip(list(F.asnumpy(old_u)), list(F.asnumpy(old_v))))
    assert edge_set == {(1, 0)}
586
587
588
    assert F.array_equal(
        hg["follow"].edge_ids(old_u, old_v), subg["follow"].edata[dgl.EID]
    )
589

590
591
592
    u, v = subg["play"].edges()
    old_u = F.gather_row(subg.nodes["user"].data[dgl.NID], u)
    old_v = F.gather_row(subg.nodes["game"].data[dgl.NID], v)
593
594
    edge_set = set(zip(list(F.asnumpy(old_u)), list(F.asnumpy(old_v))))
    assert edge_set == {(1, 2)}
595
596
597
    assert F.array_equal(
        hg["play"].edge_ids(old_u, old_v), subg["play"].edata[dgl.EID]
    )
598

599
600
601
    u, v = subg["liked-by"].edges()
    old_u = F.gather_row(subg.nodes["game"].data[dgl.NID], u)
    old_v = F.gather_row(subg.nodes["user"].data[dgl.NID], v)
602
    edge_set = set(zip(list(F.asnumpy(old_u)), list(F.asnumpy(old_v))))
603
604
605
606
607
608
609
610
    assert edge_set == {(0, 0)}
    assert F.array_equal(
        hg["liked-by"].edge_ids(old_u, old_v), subg["liked-by"].edata[dgl.EID]
    )

    u, v = subg["flips"].edges()
    old_u = F.gather_row(subg.nodes["user"].data[dgl.NID], u)
    old_v = F.gather_row(subg.nodes["coin"].data[dgl.NID], v)
611
    edge_set = set(zip(list(F.asnumpy(old_u)), list(F.asnumpy(old_v))))
612
613
614
615
616
617
618
619
    assert edge_set == {(1, 0)}
    assert F.array_equal(
        hg["flips"].edge_ids(old_u, old_v), subg["flips"].edata[dgl.EID]
    )
    assert subg.num_nodes("user") == 2
    assert subg.num_nodes("game") == 2
    assert subg.num_nodes("coin") == 1

620
621
622
623

def test_subgraph_message_passing():
    # Unit test for PR #2055
    g = dgl.graph(([0, 1, 2], [2, 3, 4])).to(F.cpu())
624
    g.ndata["x"] = F.copy_to(F.randn((5, 6)), F.cpu())
625
    sg = g.subgraph([1, 2, 3]).to(F.ctx())
626
627
628
629
630
    sg.update_all(
        lambda edges: {"x": edges.src["x"]},
        lambda nodes: {"y": F.sum(nodes.mailbox["x"], 1)},
    )

631

nv-dlasalle's avatar
nv-dlasalle committed
632
@parametrize_idtype
633
def test_khop_in_subgraph(idtype):
634
635
636
637
    g = dgl.graph(
        ([1, 1, 2, 3, 4], [0, 2, 0, 4, 2]), idtype=idtype, device=F.ctx()
    )
    g.edata["w"] = F.tensor([[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]])
Mufei Li's avatar
Mufei Li committed
638
    sg, inv = dgl.khop_in_subgraph(g, 0, k=2)
639
640
641
    assert sg.idtype == g.idtype
    u, v = sg.edges()
    edge_set = set(zip(list(F.asnumpy(u)), list(F.asnumpy(v))))
642
643
644
645
646
647
648
    assert edge_set == {(1, 0), (1, 2), (2, 0), (3, 2)}
    assert F.array_equal(
        sg.edata[dgl.EID], F.tensor([0, 1, 2, 4], dtype=idtype)
    )
    assert F.array_equal(
        sg.edata["w"], F.tensor([[0, 1], [2, 3], [4, 5], [8, 9]])
    )
Mufei Li's avatar
Mufei Li committed
649
    assert F.array_equal(F.astype(inv, idtype), F.tensor([0], idtype))
650
651

    # Test multiple nodes
Mufei Li's avatar
Mufei Li committed
652
    sg, inv = dgl.khop_in_subgraph(g, [0, 2], k=1)
653
654
    assert sg.num_edges() == 4

Mufei Li's avatar
Mufei Li committed
655
    sg, inv = dgl.khop_in_subgraph(g, F.tensor([0, 2], idtype), k=1)
656
657
658
    assert sg.num_edges() == 4

    # Test isolated node
Mufei Li's avatar
Mufei Li committed
659
    sg, inv = dgl.khop_in_subgraph(g, 1, k=2)
660
661
662
    assert sg.idtype == g.idtype
    assert sg.num_nodes() == 1
    assert sg.num_edges() == 0
Mufei Li's avatar
Mufei Li committed
663
    assert F.array_equal(F.astype(inv, idtype), F.tensor([0], idtype))
664

665
666
667
668
669
670
671
672
673
    g = 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(),
    )
    sg, inv = dgl.khop_in_subgraph(g, {"game": 0}, k=2)
674
    assert sg.idtype == idtype
675
676
    assert sg.num_nodes("game") == 1
    assert sg.num_nodes("user") == 2
677
678
    assert len(sg.ntypes) == 2
    assert len(sg.etypes) == 2
679
    u, v = sg["follows"].edges()
680
681
    edge_set = set(zip(list(F.asnumpy(u)), list(F.asnumpy(v))))
    assert edge_set == {(0, 1)}
682
    u, v = sg["plays"].edges()
683
684
    edge_set = set(zip(list(F.asnumpy(u)), list(F.asnumpy(v))))
    assert edge_set == {(0, 0), (1, 0)}
685
    assert F.array_equal(F.astype(inv["game"], idtype), F.tensor([0], idtype))
686
687

    # Test isolated node
688
    sg, inv = dgl.khop_in_subgraph(g, {"user": 0}, k=2)
689
    assert sg.idtype == idtype
690
691
692
693
694
    assert sg.num_nodes("game") == 0
    assert sg.num_nodes("user") == 1
    assert sg.num_edges("follows") == 0
    assert sg.num_edges("plays") == 0
    assert F.array_equal(F.astype(inv["user"], idtype), F.tensor([0], idtype))
695
696

    # Test multiple nodes
697
698
699
700
    sg, inv = dgl.khop_in_subgraph(
        g, {"user": F.tensor([0, 1], idtype), "game": 0}, k=1
    )
    u, v = sg["follows"].edges()
701
702
    edge_set = set(zip(list(F.asnumpy(u)), list(F.asnumpy(v))))
    assert edge_set == {(0, 1)}
703
    u, v = sg["plays"].edges()
704
705
    edge_set = set(zip(list(F.asnumpy(u)), list(F.asnumpy(v))))
    assert edge_set == {(0, 0), (1, 0)}
706
707
708
709
710
    assert F.array_equal(
        F.astype(inv["user"], idtype), F.tensor([0, 1], idtype)
    )
    assert F.array_equal(F.astype(inv["game"], idtype), F.tensor([0], idtype))

711

nv-dlasalle's avatar
nv-dlasalle committed
712
@parametrize_idtype
713
def test_khop_out_subgraph(idtype):
714
715
716
717
    g = dgl.graph(
        ([0, 2, 0, 4, 2], [1, 1, 2, 3, 4]), idtype=idtype, device=F.ctx()
    )
    g.edata["w"] = F.tensor([[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]])
Mufei Li's avatar
Mufei Li committed
718
    sg, inv = dgl.khop_out_subgraph(g, 0, k=2)
719
720
721
    assert sg.idtype == g.idtype
    u, v = sg.edges()
    edge_set = set(zip(list(F.asnumpy(u)), list(F.asnumpy(v))))
722
723
724
725
726
727
728
    assert edge_set == {(0, 1), (2, 1), (0, 2), (2, 3)}
    assert F.array_equal(
        sg.edata[dgl.EID], F.tensor([0, 2, 1, 4], dtype=idtype)
    )
    assert F.array_equal(
        sg.edata["w"], F.tensor([[0, 1], [4, 5], [2, 3], [8, 9]])
    )
Mufei Li's avatar
Mufei Li committed
729
    assert F.array_equal(F.astype(inv, idtype), F.tensor([0], idtype))
730
731

    # Test multiple nodes
Mufei Li's avatar
Mufei Li committed
732
    sg, inv = dgl.khop_out_subgraph(g, [0, 2], k=1)
733
734
    assert sg.num_edges() == 4

Mufei Li's avatar
Mufei Li committed
735
    sg, inv = dgl.khop_out_subgraph(g, F.tensor([0, 2], idtype), k=1)
736
737
738
    assert sg.num_edges() == 4

    # Test isolated node
Mufei Li's avatar
Mufei Li committed
739
    sg, inv = dgl.khop_out_subgraph(g, 1, k=2)
740
741
742
    assert sg.idtype == g.idtype
    assert sg.num_nodes() == 1
    assert sg.num_edges() == 0
Mufei Li's avatar
Mufei Li committed
743
    assert F.array_equal(F.astype(inv, idtype), F.tensor([0], idtype))
744

745
746
747
748
749
750
751
752
753
    g = dgl.heterograph(
        {
            ("user", "plays", "game"): ([0, 1, 1, 2], [0, 0, 2, 1]),
            ("user", "follows", "user"): ([0, 1], [1, 3]),
        },
        idtype=idtype,
        device=F.ctx(),
    )
    sg, inv = dgl.khop_out_subgraph(g, {"user": 0}, k=2)
754
    assert sg.idtype == idtype
755
756
    assert sg.num_nodes("game") == 2
    assert sg.num_nodes("user") == 3
757
758
    assert len(sg.ntypes) == 2
    assert len(sg.etypes) == 2
759
    u, v = sg["follows"].edges()
760
761
    edge_set = set(zip(list(F.asnumpy(u)), list(F.asnumpy(v))))
    assert edge_set == {(0, 1), (1, 2)}
762
    u, v = sg["plays"].edges()
763
    edge_set = set(zip(list(F.asnumpy(u)), list(F.asnumpy(v))))
764
765
    assert edge_set == {(0, 0), (1, 0), (1, 1)}
    assert F.array_equal(F.astype(inv["user"], idtype), F.tensor([0], idtype))
766
767

    # Test isolated node
768
    sg, inv = dgl.khop_out_subgraph(g, {"user": 3}, k=2)
769
    assert sg.idtype == idtype
770
771
772
773
774
    assert sg.num_nodes("game") == 0
    assert sg.num_nodes("user") == 1
    assert sg.num_edges("follows") == 0
    assert sg.num_edges("plays") == 0
    assert F.array_equal(F.astype(inv["user"], idtype), F.tensor([0], idtype))
775
776

    # Test multiple nodes
777
778
779
780
781
    sg, inv = dgl.khop_out_subgraph(
        g, {"user": F.tensor([2], idtype), "game": 0}, k=1
    )
    assert sg.num_edges("follows") == 0
    u, v = sg["plays"].edges()
782
783
    edge_set = set(zip(list(F.asnumpy(u)), list(F.asnumpy(v))))
    assert edge_set == {(0, 1)}
784
785
    assert F.array_equal(F.astype(inv["user"], idtype), F.tensor([0], idtype))
    assert F.array_equal(F.astype(inv["game"], idtype), F.tensor([0], idtype))
786

787
788

@unittest.skipIf(not F.gpu_ctx(), "only necessary with GPU")
789
@pytest.mark.parametrize(
790
791
792
793
    "parent_idx_device",
    [("cpu", F.cpu()), ("cuda", F.cuda()), ("uva", F.cpu()), ("uva", F.cuda())],
)
@pytest.mark.parametrize("child_device", [F.cpu(), F.cuda()])
794
795
def test_subframes(parent_idx_device, child_device):
    parent_device, idx_device = parent_idx_device
796
797
798
    g = dgl.graph(
        (F.tensor([1, 2, 3], dtype=F.int64), F.tensor([2, 3, 4], dtype=F.int64))
    )
799
    print(g.device)
800
801
    g.ndata["x"] = F.randn((5, 4))
    g.edata["a"] = F.randn((3, 6))
802
    idx = F.tensor([1, 2], dtype=F.int64)
803
    if parent_device == "cuda":
804
        g = g.to(F.cuda())
805
806
    elif parent_device == "uva":
        if F.backend_name != "pytorch":
807
            pytest.skip("UVA only supported for PyTorch")
808
809
810
        g = g.to(F.cpu())
        g.create_formats_()
        g.pin_memory_()
811
    elif parent_device == "cpu":
812
813
814
        g = g.to(F.cpu())
    idx = F.copy_to(idx, idx_device)
    sg = g.sample_neighbors(idx, 2).to(child_device)
815
816
    assert sg.device == F.context(sg.ndata["x"])
    assert sg.device == F.context(sg.edata["a"])
817
    assert sg.device == child_device
818
819
820
821
822
823
    if parent_device != "uva":
        sg = g.to(child_device).sample_neighbors(
            F.copy_to(idx, child_device), 2
        )
        assert sg.device == F.context(sg.ndata["x"])
        assert sg.device == F.context(sg.edata["a"])
824
        assert sg.device == child_device
825
    if parent_device == "uva":
826
827
        g.unpin_memory_()

828
829
830
831
832
833
834
835

@unittest.skipIf(
    F._default_context_str != "gpu", reason="UVA only available on GPU"
)
@unittest.skipIf(
    dgl.backend.backend_name != "pytorch",
    reason="UVA only supported for PyTorch",
)
836
@pytest.mark.parametrize("device", [F.cpu(), F.cuda()])
nv-dlasalle's avatar
nv-dlasalle committed
837
@parametrize_idtype
838
def test_uva_subgraph(idtype, device):
839
    g = create_test_heterograph2(idtype)
840
841
842
    g = g.to(F.cpu())
    g.create_formats_()
    g.pin_memory_()
843
844
    indices = {"user": F.copy_to(F.tensor([0], idtype), device)}
    edge_indices = {"follows": F.copy_to(F.tensor([0], idtype), device)}
845
846
847
848
    assert g.subgraph(indices).device == device
    assert g.edge_subgraph(edge_indices).device == device
    assert g.in_subgraph(indices).device == device
    assert g.out_subgraph(indices).device == device
849
850
    assert g.khop_in_subgraph(indices, 1)[0].device == device
    assert g.khop_out_subgraph(indices, 1)[0].device == device
851
852
853
    assert g.sample_neighbors(indices, 1).device == device
    g.unpin_memory_()

854
855

if __name__ == "__main__":
856
    test_edge_subgraph()
857
858
    test_uva_subgraph(F.int64, F.cpu())
    test_uva_subgraph(F.int64, F.cuda())