test_basics.py 19 KB
Newer Older
1
2
3
4
5
# Currently readonly graph construction only accepts sparse tensor in MXNet,
# and pytorch doesn't support readonly graph or graph creation from sparse
# tensor.  For now, readonly graph test is postponed until we have better
# readonly graph support.
import backend as F
6
import dgl
7
from dgl import DGLGraph
8
from collections import defaultdict as ddict
9
10
11
12

D = 5
reduce_msg_shapes = set()

13
def message_func(edges):
14
15
    assert F.ndim(edges.src['h']) == 2
    assert F.shape(edges.src['h'])[1] == D
16
    return {'m' : edges.src['h']}
17

18
19
def reduce_func(nodes):
    msgs = nodes.mailbox['m']
Minjie Wang's avatar
Minjie Wang committed
20
    reduce_msg_shapes.add(tuple(msgs.shape))
21
22
23
    assert F.ndim(msgs) == 3
    assert F.shape(msgs)[2] == D
    return {'accum' : F.sum(msgs, 1)}
Minjie Wang's avatar
Minjie Wang committed
24

25
26
def apply_node_func(nodes):
    return {'h' : nodes.data['h'] + nodes.data['accum']}
Minjie Wang's avatar
Minjie Wang committed
27

28
def generate_graph(grad=False):
29
    g = DGLGraph()
30
    g.add_nodes(10) # 10 nodes
31
    # create a graph where 0 is the source and 9 is the sink
Minjie Wang's avatar
Minjie Wang committed
32
    # 17 edges
33
34
35
36
37
    for i in range(1, 9):
        g.add_edge(0, i)
        g.add_edge(i, 9)
    # add a back flow from 9 to 0
    g.add_edge(9, 0)
38
39
40
41
42
43
    ncol = F.randn((10, D))
    ecol = F.randn((17, D))
    if grad:
        ncol = F.attach_grad(ncol)
        ecol = F.attach_grad(ecol)

44
45
    g.ndata['h'] = ncol
    g.edata['w'] = ecol
46
47
    g.set_n_initializer(dgl.init.zero_initializer)
    g.set_e_initializer(dgl.init.zero_initializer)
48
49
50
51
    return g

def test_batch_setter_getter():
    def _pfc(x):
52
        return list(F.zerocopy_to_numpy(x)[:,0])
53
54
    g = generate_graph()
    # set all nodes
55
56
    g.ndata['h'] = F.zeros((10, D))
    assert F.allclose(g.ndata['h'], F.zeros((10, D)))
Minjie Wang's avatar
Minjie Wang committed
57
    # pop nodes
58
    old_len = len(g.ndata)
Minjie Wang's avatar
Minjie Wang committed
59
    assert _pfc(g.pop_n_repr('h')) == [0.] * 10
60
    assert len(g.ndata) == old_len - 1
61
    g.ndata['h'] = F.zeros((10, D))
62
    # set partial nodes
63
64
    u = F.tensor([1, 3, 5])
    g.nodes[u].data['h'] = F.ones((3, D))
65
    assert _pfc(g.ndata['h']) == [0., 1., 0., 1., 0., 1., 0., 0., 0., 0.]
66
    # get partial nodes
67
    u = F.tensor([1, 2, 3])
68
    assert _pfc(g.nodes[u].data['h']) == [1., 0., 1.]
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

    '''
    s, d, eid
    0, 1, 0
    1, 9, 1
    0, 2, 2
    2, 9, 3
    0, 3, 4
    3, 9, 5
    0, 4, 6
    4, 9, 7
    0, 5, 8
    5, 9, 9
    0, 6, 10
    6, 9, 11
    0, 7, 12
    7, 9, 13
    0, 8, 14
    8, 9, 15
    9, 0, 16
    '''
    # set all edges
91
    g.edata['l'] = F.zeros((17, D))
92
    assert _pfc(g.edata['l']) == [0.] * 17
Minjie Wang's avatar
Minjie Wang committed
93
    # pop edges
94
    old_len = len(g.edata)
Minjie Wang's avatar
Minjie Wang committed
95
    assert _pfc(g.pop_e_repr('l')) == [0.] * 17
96
    assert len(g.edata) == old_len - 1
97
    g.edata['l'] = F.zeros((17, D))
Minjie Wang's avatar
Minjie Wang committed
98
    # set partial edges (many-many)
99
100
101
    u = F.tensor([0, 0, 2, 5, 9])
    v = F.tensor([1, 3, 9, 9, 0])
    g.edges[u, v].data['l'] = F.ones((5, D))
Minjie Wang's avatar
Minjie Wang committed
102
103
    truth = [0.] * 17
    truth[0] = truth[4] = truth[3] = truth[9] = truth[16] = 1.
104
    assert _pfc(g.edata['l']) == truth
Minjie Wang's avatar
Minjie Wang committed
105
    # set partial edges (many-one)
106
107
108
    u = F.tensor([3, 4, 6])
    v = F.tensor([9])
    g.edges[u, v].data['l'] = F.ones((3, D))
Minjie Wang's avatar
Minjie Wang committed
109
    truth[5] = truth[7] = truth[11] = 1.
110
    assert _pfc(g.edata['l']) == truth
Minjie Wang's avatar
Minjie Wang committed
111
    # set partial edges (one-many)
112
113
114
    u = F.tensor([0])
    v = F.tensor([4, 5, 6])
    g.edges[u, v].data['l'] = F.ones((3, D))
Minjie Wang's avatar
Minjie Wang committed
115
    truth[6] = truth[8] = truth[10] = 1.
116
    assert _pfc(g.edata['l']) == truth
Minjie Wang's avatar
Minjie Wang committed
117
    # get partial edges (many-many)
118
119
    u = F.tensor([0, 6, 0])
    v = F.tensor([6, 9, 7])
120
    assert _pfc(g.edges[u, v].data['l']) == [1., 1., 0.]
Minjie Wang's avatar
Minjie Wang committed
121
    # get partial edges (many-one)
122
123
    u = F.tensor([5, 6, 7])
    v = F.tensor([9])
124
    assert _pfc(g.edges[u, v].data['l']) == [1., 1., 0.]
Minjie Wang's avatar
Minjie Wang committed
125
    # get partial edges (one-many)
126
127
    u = F.tensor([0])
    v = F.tensor([3, 4, 5])
128
    assert _pfc(g.edges[u, v].data['l']) == [1., 1., 1.]
129

130
131
def test_batch_setter_autograd():
    g = generate_graph(grad=True)
132
    h1 = g.ndata['h']
133
    # partial set
134
135
136
137
138
139
140
141
    v = F.tensor([1, 2, 8])
    hh = F.attach_grad(F.zeros((len(v), D)))
    with F.record_grad():
        g.nodes[v].data['h'] = hh
        h2 = g.ndata['h']
    F.backward(h2, F.ones((10, D)) * 2)
    assert F.array_equal(F.grad(h1)[:,0], F.tensor([2., 0., 0., 2., 2., 2., 2., 2., 0., 2.]))
    assert F.array_equal(F.grad(hh)[:,0], F.tensor([2., 2., 2.]))
142

143
144
145
146
def test_nx_conversion():
    # check conversion between networkx and DGLGraph

    def _check_nx_feature(nxg, nf, ef):
Minjie Wang's avatar
Minjie Wang committed
147
148
        # check node and edge feature of nxg
        # this is used to check to_networkx
149
150
151
152
153
154
155
        num_nodes = len(nxg)
        num_edges = nxg.size()
        if num_nodes > 0:
            node_feat = ddict(list)
            for nid, attr in nxg.nodes(data=True):
                assert len(attr) == len(nf)
                for k in nxg.nodes[nid]:
156
                    node_feat[k].append(F.unsqueeze(attr[k], 0))
157
            for k in node_feat:
158
159
                feat = F.cat(node_feat[k], 0)
                assert F.allclose(feat, nf[k])
160
161
162
163
164
165
166
167
        else:
            assert len(nf) == 0
        if num_edges > 0:
            edge_feat = ddict(lambda: [0] * num_edges)
            for u, v, attr in nxg.edges(data=True):
                assert len(attr) == len(ef) + 1 # extra id
                eid = attr['id']
                for k in ef:
168
                    edge_feat[k][eid] = F.unsqueeze(attr[k], 0)
169
            for k in edge_feat:
170
171
                feat = F.cat(edge_feat[k], 0)
                assert F.allclose(feat, ef[k])
172
173
174
        else:
            assert len(ef) == 0

175
176
177
178
179
    n1 = F.randn((5, 3))
    n2 = F.randn((5, 10))
    n3 = F.randn((5, 4))
    e1 = F.randn((4, 5))
    e2 = F.randn((4, 7))
180
181
182
183
184
185
186
187
188
189
190
191
    g = DGLGraph(multigraph=True)
    g.add_nodes(5)
    g.add_edges([0,1,3,4], [2,4,0,3])
    g.ndata.update({'n1': n1, 'n2': n2, 'n3': n3})
    g.edata.update({'e1': e1, 'e2': e2})

    # convert to networkx
    nxg = g.to_networkx(node_attrs=['n1', 'n3'], edge_attrs=['e1', 'e2'])
    assert len(nxg) == 5
    assert nxg.size() == 4
    _check_nx_feature(nxg, {'n1': n1, 'n3': n3}, {'e1': e1, 'e2': e2})

Minjie Wang's avatar
Minjie Wang committed
192
    # convert to DGLGraph, nx graph has id in edge feature
193
194
    # use id feature to test non-tensor copy
    g.from_networkx(nxg, node_attrs=['n1'], edge_attrs=['e1', 'id'])
Minjie Wang's avatar
Minjie Wang committed
195
    # check graph size
196
197
    assert g.number_of_nodes() == 5
    assert g.number_of_edges() == 4
Minjie Wang's avatar
Minjie Wang committed
198
199
200
201
202
    # check number of features
    # test with existing dglgraph (so existing features should be cleared)
    assert len(g.ndata) == 1
    assert len(g.edata) == 2
    # check feature values
203
    assert F.allclose(g.ndata['n1'], n1)
Minjie Wang's avatar
Minjie Wang committed
204
    # with id in nx edge feature, e1 should follow original order
205
206
    assert F.allclose(g.edata['e1'], e1)
    assert F.array_equal(g.get_e_repr()['id'], F.arange(0, 4))
207

Minjie Wang's avatar
Minjie Wang committed
208
209
    # test conversion after modifying DGLGraph
    g.pop_e_repr('id') # pop id so we don't need to provide id when adding edges
210
211
    new_n = F.randn((2, 3))
    new_e = F.randn((3, 5))
212
213
214
    g.add_nodes(2, data={'n1': new_n})
    # add three edges, one is a multi-edge
    g.add_edges([3, 6, 0], [4, 5, 2], data={'e1': new_e})
215
216
    n1 = F.cat((n1, new_n), 0)
    e1 = F.cat((e1, new_e), 0)
217
218
219
220
221
222
    # convert to networkx again
    nxg = g.to_networkx(node_attrs=['n1'], edge_attrs=['e1'])
    assert len(nxg) == 7
    assert nxg.size() == 7
    _check_nx_feature(nxg, {'n1': n1}, {'e1': e1})

Minjie Wang's avatar
Minjie Wang committed
223
224
225
226
227
228
229
230
231
232
233
234
235
236
    # now test convert from networkx without id in edge feature
    # first pop id in edge feature
    for _, _, attr in nxg.edges(data=True):
        attr.pop('id')
    # test with a new graph
    g = DGLGraph(multigraph=True)
    g.from_networkx(nxg, node_attrs=['n1'], edge_attrs=['e1'])
    # check graph size
    assert g.number_of_nodes() == 7
    assert g.number_of_edges() == 7
    # check number of features
    assert len(g.ndata) == 1
    assert len(g.edata) == 1
    # check feature values
237
    assert F.allclose(g.ndata['n1'], n1)
Minjie Wang's avatar
Minjie Wang committed
238
239
240
    # edge feature order follows nxg.edges()
    edge_feat = []
    for _, _, attr in nxg.edges(data=True):
241
242
243
        edge_feat.append(F.unsqueeze(attr['e1'], 0))
    edge_feat = F.cat(edge_feat, 0)
    assert F.allclose(g.edata['e1'], edge_feat)
Minjie Wang's avatar
Minjie Wang committed
244

245
246
def test_batch_send():
    g = generate_graph()
247
    def _fmsg(edges):
248
        assert tuple(F.shape(edges.src['h'])) == (5, D)
249
        return {'m' : edges.src['h']}
Minjie Wang's avatar
Minjie Wang committed
250
    g.register_message_func(_fmsg)
251
    # many-many send
252
253
    u = F.tensor([0, 0, 0, 0, 0])
    v = F.tensor([1, 2, 3, 4, 5])
254
    g.send((u, v))
255
    # one-many send
256
257
    u = F.tensor([0])
    v = F.tensor([1, 2, 3, 4, 5])
258
    g.send((u, v))
259
    # many-one send
260
261
    u = F.tensor([1, 2, 3, 4, 5])
    v = F.tensor([9])
262
    g.send((u, v))
263

264
def test_batch_recv():
Minjie Wang's avatar
Minjie Wang committed
265
    # basic recv test
266
    g = generate_graph()
Minjie Wang's avatar
Minjie Wang committed
267
268
269
    g.register_message_func(message_func)
    g.register_reduce_func(reduce_func)
    g.register_apply_node_func(apply_node_func)
270
271
    u = F.tensor([0, 0, 0, 4, 5, 6])
    v = F.tensor([1, 2, 3, 9, 9, 9])
Minjie Wang's avatar
Minjie Wang committed
272
    reduce_msg_shapes.clear()
273
    g.send((u, v))
274
    g.recv(F.unique(v))
Minjie Wang's avatar
Minjie Wang committed
275
276
277
    assert(reduce_msg_shapes == {(1, 3, D), (3, 1, D)})
    reduce_msg_shapes.clear()

278
279
280
281
282
283
284
def test_apply_nodes():
    def _upd(nodes):
        return {'h' : nodes.data['h'] * 2}
    g = generate_graph()
    g.register_apply_node_func(_upd)
    old = g.ndata['h']
    g.apply_nodes()
285
286
    assert F.allclose(old * 2, g.ndata['h'])
    u = F.tensor([0, 3, 4, 6])
287
    g.apply_nodes(lambda nodes : {'h' : nodes.data['h'] * 0.}, u)
288
    assert F.allclose(F.gather_row(g.ndata['h'], u), F.zeros((4, D)))
289

290
def test_apply_edges():
291
292
293
    def _upd(edges):
        return {'w' : edges.data['w'] * 2}
    g = generate_graph()
294
    g.register_apply_edge_func(_upd)
295
    old = g.edata['w']
296
    g.apply_edges()
297
298
299
    assert F.allclose(old * 2, g.edata['w'])
    u = F.tensor([0, 0, 0, 4, 5, 6])
    v = F.tensor([1, 2, 3, 9, 9, 9])
300
    g.apply_edges(lambda edges : {'w' : edges.data['w'] * 0.}, (u, v))
301
    eid = g.edge_ids(u, v)
302
    assert F.allclose(F.gather_row(g.edata['w'], eid), F.zeros((6, D)))
303

304
305
def test_update_routines():
    g = generate_graph()
Minjie Wang's avatar
Minjie Wang committed
306
307
308
    g.register_message_func(message_func)
    g.register_reduce_func(reduce_func)
    g.register_apply_node_func(apply_node_func)
309

310
    # send_and_recv
311
    reduce_msg_shapes.clear()
312
313
    u = [0, 0, 0, 4, 5, 6]
    v = [1, 2, 3, 9, 9, 9]
314
    g.send_and_recv((u, v))
315
316
    assert(reduce_msg_shapes == {(1, 3, D), (3, 1, D)})
    reduce_msg_shapes.clear()
317
318
319
    try:
        g.send_and_recv([u, v])
        assert False
320
    except:
321
        pass
322

323
    # pull
324
    v = F.tensor([1, 2, 3, 9])
325
    reduce_msg_shapes.clear()
326
    g.pull(v)
327
328
329
    assert(reduce_msg_shapes == {(1, 8, D), (3, 1, D)})
    reduce_msg_shapes.clear()

330
    # push
331
    v = F.tensor([0, 1, 2, 3])
332
    reduce_msg_shapes.clear()
333
    g.push(v)
334
335
336
337
338
339
340
341
342
    assert(reduce_msg_shapes == {(1, 3, D), (8, 1, D)})
    reduce_msg_shapes.clear()

    # update_all
    reduce_msg_shapes.clear()
    g.update_all()
    assert(reduce_msg_shapes == {(1, 8, D), (9, 1, D)})
    reduce_msg_shapes.clear()

343
344
345
346
347
348
349
350
def test_recv_0deg():
    # test recv with 0deg nodes;
    g = DGLGraph()
    g.add_nodes(2)
    g.add_edge(0, 1)
    def _message(edges):
        return {'m' : edges.src['h']}
    def _reduce(nodes):
351
        return {'h' : nodes.data['h'] + F.sum(nodes.mailbox['m'], 1)}
352
353
354
    def _apply(nodes):
        return {'h' : nodes.data['h'] * 2}
    def _init2(shape, dtype, ctx, ids):
355
        return 2 + F.zeros(shape, dtype, ctx)
356
357
358
359
360
    g.register_message_func(_message)
    g.register_reduce_func(_reduce)
    g.register_apply_node_func(_apply)
    g.set_n_initializer(_init2, 'h')
    # test#1: recv both 0deg and non-0deg nodes
361
    old = F.randn((2, 5))
362
363
364
365
366
    g.ndata['h'] = old
    g.send((0, 1))
    g.recv([0, 1])
    new = g.ndata.pop('h')
    # 0deg check: initialized with the func and got applied
367
    assert F.allclose(new[0], F.full_1d(5, 4, F.float32))
368
    # non-0deg check
369
    assert F.allclose(new[1], F.sum(old, 0) * 2)
370
371

    # test#2: recv only 0deg node is equal to apply
372
    old = F.randn((2, 5))
373
374
375
376
377
    g.ndata['h'] = old
    g.send((0, 1))
    g.recv(0)
    new = g.ndata.pop('h')
    # 0deg check: equal to apply_nodes
378
    assert F.allclose(new[0], 2 * old[0])
379
    # non-0deg check: untouched
380
    assert F.allclose(new[1], old[1])
381
382
383
384
385
386
387
388
389

def test_recv_0deg_newfld():
    # test recv with 0deg nodes; the reducer also creates a new field
    g = DGLGraph()
    g.add_nodes(2)
    g.add_edge(0, 1)
    def _message(edges):
        return {'m' : edges.src['h']}
    def _reduce(nodes):
390
        return {'h1' : nodes.data['h'] + F.sum(nodes.mailbox['m'], 1)}
391
392
393
    def _apply(nodes):
        return {'h1' : nodes.data['h1'] * 2}
    def _init2(shape, dtype, ctx, ids):
394
        return 2 + F.zeros(shape, dtype=dtype, ctx=ctx)
395
396
397
398
    g.register_message_func(_message)
    g.register_reduce_func(_reduce)
    g.register_apply_node_func(_apply)
    # test#1: recv both 0deg and non-0deg nodes
399
    old = F.randn((2, 5))
400
401
402
403
404
405
    g.set_n_initializer(_init2, 'h1')
    g.ndata['h'] = old
    g.send((0, 1))
    g.recv([0, 1])
    new = g.ndata.pop('h1')
    # 0deg check: initialized with the func and got applied
406
    assert F.allclose(new[0], F.full_1d(5, 4, dtype=F.float32))
407
    # non-0deg check
408
    assert F.allclose(new[1], F.sum(old, 0) * 2)
409
410

    # test#2: recv only 0deg node
411
    old = F.randn((2, 5))
412
    g.ndata['h'] = old
413
    g.ndata['h1'] = F.full((2, 5), -1, F.int64)  # this is necessary
414
415
416
417
    g.send((0, 1))
    g.recv(0)
    new = g.ndata.pop('h1')
    # 0deg check: fallback to apply
418
    assert F.allclose(new[0], F.full_1d(5, -2, F.int64))
419
    # non-0deg check: not changed
420
    assert F.allclose(new[1], F.full_1d(5, -1, F.int64))
421
422
423

def test_update_all_0deg():
    # test#1
424
    g = DGLGraph()
Minjie Wang's avatar
Minjie Wang committed
425
    g.add_nodes(5)
426
427
428
429
    g.add_edge(1, 0)
    g.add_edge(2, 0)
    g.add_edge(3, 0)
    g.add_edge(4, 0)
430
431
432
    def _message(edges):
        return {'m' : edges.src['h']}
    def _reduce(nodes):
433
        return {'h' : nodes.data['h'] + F.sum(nodes.mailbox['m'], 1)}
434
435
    def _apply(nodes):
        return {'h' : nodes.data['h'] * 2}
436
    def _init2(shape, dtype, ctx, ids):
437
        return 2 + F.zeros(shape, dtype, ctx)
438
    g.set_n_initializer(_init2, 'h')
439
    old_repr = F.randn((5, 5))
440
    g.ndata['h'] = old_repr
441
    g.update_all(_message, _reduce, _apply)
442
    new_repr = g.ndata['h']
443
444
    # the first row of the new_repr should be the sum of all the node
    # features; while the 0-deg nodes should be initialized by the
445
    # initializer and applied with UDF.
446
447
    assert F.allclose(new_repr[1:], 2*(2+F.zeros((4,5))))
    assert F.allclose(new_repr[0], 2 * F.sum(old_repr, 0))
448

449
450
451
452
453
454
455
456
    # test#2: graph with no edge
    g = DGLGraph()
    g.add_nodes(5)
    g.set_n_initializer(_init2, 'h')
    g.ndata['h'] = old_repr
    g.update_all(_message, _reduce, _apply)
    new_repr = g.ndata['h']
    # should fallback to apply
457
    assert F.allclose(new_repr, 2*old_repr)
458

459
def test_pull_0deg():
460
    g = DGLGraph()
Minjie Wang's avatar
Minjie Wang committed
461
    g.add_nodes(2)
462
    g.add_edge(0, 1)
463
464
465
    def _message(edges):
        return {'m' : edges.src['h']}
    def _reduce(nodes):
466
        return {'h' : nodes.data['h'] + F.sum(nodes.mailbox['m'], 1)}
467
468
469
    def _apply(nodes):
        return {'h' : nodes.data['h'] * 2}
    def _init2(shape, dtype, ctx, ids):
470
        return 2 + F.zeros(shape, dtype, ctx)
471
472
473
474
475
    g.register_message_func(_message)
    g.register_reduce_func(_reduce)
    g.register_apply_node_func(_apply)
    g.set_n_initializer(_init2, 'h')
    # test#1: pull both 0deg and non-0deg nodes
476
    old = F.randn((2, 5))
477
478
479
480
    g.ndata['h'] = old
    g.pull([0, 1])
    new = g.ndata.pop('h')
    # 0deg check: initialized with the func and got applied
481
    assert F.allclose(new[0], F.full_1d(5, 4, dtype=F.float32))
482
    # non-0deg check
483
    assert F.allclose(new[1], F.sum(old, 0) * 2)
484
485

    # test#2: pull only 0deg node
486
    old = F.randn((2, 5))
487
488
489
490
    g.ndata['h'] = old
    g.pull(0)
    new = g.ndata.pop('h')
    # 0deg check: fallback to apply
491
    assert F.allclose(new[0], 2*old[0])
492
    # non-0deg check: not touched
493
    assert F.allclose(new[1], old[1])
494

495
496
497
498
499
500
501
502
def test_send_multigraph():
    g = DGLGraph(multigraph=True)
    g.add_nodes(3)
    g.add_edge(0, 1)
    g.add_edge(0, 1)
    g.add_edge(0, 1)
    g.add_edge(2, 1)

503
504
505
506
507
    def _message_a(edges):
        return {'a': edges.data['a']}
    def _message_b(edges):
        return {'a': edges.data['a'] * 3}
    def _reduce(nodes):
508
        return {'a': F.max(nodes.mailbox['a'], 1)}
509
510

    def answer(*args):
511
        return F.max(F.stack(args, 0), 0)
512
513

    # send by eid
514
515
    old_repr = F.randn((4, 5))
    g.ndata['a'] = F.zeros((3, 5))
516
517
518
519
    g.edata['a'] = old_repr
    g.send([0, 2], message_func=_message_a)
    g.recv(1, _reduce)
    new_repr = g.ndata['a']
520
    assert F.allclose(new_repr[1], answer(old_repr[0], old_repr[2]))
521

522
    g.ndata['a'] = F.zeros((3, 5))
523
524
525
526
    g.edata['a'] = old_repr
    g.send([0, 2, 3], message_func=_message_a)
    g.recv(1, _reduce)
    new_repr = g.ndata['a']
527
    assert F.allclose(new_repr[1], answer(old_repr[0], old_repr[2], old_repr[3]))
528
529

    # send on multigraph
530
    g.ndata['a'] = F.zeros((3, 5))
531
532
533
534
    g.edata['a'] = old_repr
    g.send(([0, 2], [1, 1]), _message_a)
    g.recv(1, _reduce)
    new_repr = g.ndata['a']
535
    assert F.allclose(new_repr[1], F.max(old_repr, 0))
536
537

    # consecutive send and send_on
538
    g.ndata['a'] = F.zeros((3, 5))
539
540
541
542
543
    g.edata['a'] = old_repr
    g.send((2, 1), _message_a)
    g.send([0, 1], message_func=_message_b)
    g.recv(1, _reduce)
    new_repr = g.ndata['a']
544
    assert F.allclose(new_repr[1], answer(old_repr[0] * 3, old_repr[1] * 3, old_repr[3]))
545
546

    # consecutive send_on
547
    g.ndata['a'] = F.zeros((3, 5))
548
549
550
551
552
    g.edata['a'] = old_repr
    g.send(0, message_func=_message_a)
    g.send(1, message_func=_message_b)
    g.recv(1, _reduce)
    new_repr = g.ndata['a']
553
    assert F.allclose(new_repr[1], answer(old_repr[0], old_repr[1] * 3))
554
555

    # send_and_recv_on
556
    g.ndata['a'] = F.zeros((3, 5))
557
558
559
    g.edata['a'] = old_repr
    g.send_and_recv([0, 2, 3], message_func=_message_a, reduce_func=_reduce)
    new_repr = g.ndata['a']
560
561
    assert F.allclose(new_repr[1], answer(old_repr[0], old_repr[2], old_repr[3]))
    assert F.allclose(new_repr[[0, 2]], F.zeros((2, 5)))
562

563
564
565
566
567
568
569
570
def test_dynamic_addition():
    N = 3
    D = 1

    g = DGLGraph()

    # Test node addition
    g.add_nodes(N)
571
572
    g.ndata.update({'h1': F.randn((N, D)),
                    'h2': F.randn((N, D))})
573
    g.add_nodes(3)
574
    assert g.ndata['h1'].shape[0] == g.ndata['h2'].shape[0] == N + 3
575
576
577
578

    # Test edge addition
    g.add_edge(0, 1)
    g.add_edge(1, 0)
579
580
    g.edata.update({'h1': F.randn((2, D)),
                    'h2': F.randn((2, D))})
581
    assert g.edata['h1'].shape[0] == g.edata['h2'].shape[0] == 2
582
583

    g.add_edges([0, 2], [2, 0])
584
    g.edata['h1'] = F.randn((4, D))
585
    assert g.edata['h1'].shape[0] == g.edata['h2'].shape[0] == 4
586
587

    g.add_edge(1, 2)
588
    g.edges[4].data['h1'] = F.randn((1, D))
589
    assert g.edata['h1'].shape[0] == g.edata['h2'].shape[0] == 5
590

591
    # test add edge with part of the features
592
    g.add_edge(2, 1, {'h1': F.randn((1, D))})
593
594
    assert len(g.edata['h1']) == len(g.edata['h2'])

595

Haibin Lin's avatar
Haibin Lin committed
596
597
598
599
600
def test_repr():
    G = dgl.DGLGraph()
    G.add_nodes(10)
    G.add_edge(0, 1)
    repr_string = G.__repr__()
Minjie Wang's avatar
Minjie Wang committed
601
    print(repr_string)
602
    G.ndata['x'] = F.zeros((10, 5))
Haibin Lin's avatar
Haibin Lin committed
603
    G.add_edges([0, 1], 2)
604
    G.edata['y'] = F.zeros((3, 4))
Haibin Lin's avatar
Haibin Lin committed
605
    repr_string = G.__repr__()
Minjie Wang's avatar
Minjie Wang committed
606
    print(repr_string)
Haibin Lin's avatar
Haibin Lin committed
607

608
if __name__ == '__main__':
609
    test_nx_conversion()
610
    test_batch_setter_getter()
611
    test_batch_setter_autograd()
612
    test_batch_send()
613
    test_batch_recv()
614
    test_apply_nodes()
615
    test_apply_edges()
616
    test_update_routines()
617
618
619
    test_recv_0deg()
    test_recv_0deg_newfld()
    test_update_all_0deg()
620
    test_pull_0deg()
621
    test_send_multigraph()
622
    test_dynamic_addition()
Haibin Lin's avatar
Haibin Lin committed
623
    test_repr()