"examples/pytorch/vscode:/vscode.git/clone" did not exist on "269983dbcd221acb64d03817fb25f0e27788bbaf"
graph_index.py 19.9 KB
Newer Older
Minjie Wang's avatar
Minjie Wang committed
1
2
from __future__ import absolute_import

3
import ctypes
Minjie Wang's avatar
Minjie Wang committed
4
5
import numpy as np
import networkx as nx
6
import scipy
Minjie Wang's avatar
Minjie Wang committed
7

8
from ._ffi.base import c_array
Minjie Wang's avatar
Minjie Wang committed
9
from ._ffi.function import _init_api
Minjie Wang's avatar
Minjie Wang committed
10
from . import backend as F
Minjie Wang's avatar
Minjie Wang committed
11
from . import utils
Minjie Wang's avatar
Minjie Wang committed
12

13
14
GraphIndexHandle = ctypes.c_void_p

Minjie Wang's avatar
Minjie Wang committed
15
16
17
18
19
class GraphIndex(object):
    """Graph index object.

    Parameters
    ----------
20
21
    handle : GraphIndexHandle
        Handler
Minjie Wang's avatar
Minjie Wang committed
22
    """
23
24
    def __init__(self, handle):
        self._handle = handle
Minjie Wang's avatar
Minjie Wang committed
25
        self._cache = {}
Minjie Wang's avatar
Minjie Wang committed
26
27

    def __del__(self):
Minjie Wang's avatar
Minjie Wang committed
28
        """Free this graph index object."""
Minjie Wang's avatar
Minjie Wang committed
29
30
31
        _CAPI_DGLGraphFree(self._handle)

    def add_nodes(self, num):
Minjie Wang's avatar
Minjie Wang committed
32
33
34
35
36
37
38
        """Add nodes.
        
        Parameters
        ----------
        num : int
            Number of nodes to be added.
        """
Minjie Wang's avatar
Minjie Wang committed
39
        _CAPI_DGLGraphAddVertices(self._handle, num);
Minjie Wang's avatar
Minjie Wang committed
40
        self._cache.clear()
Minjie Wang's avatar
Minjie Wang committed
41
42

    def add_edge(self, u, v):
Minjie Wang's avatar
Minjie Wang committed
43
44
45
46
47
48
49
50
51
        """Add one edge.
        
        Parameters
        ----------
        u : int
            The src node.
        v : int
            The dst node.
        """
Minjie Wang's avatar
Minjie Wang committed
52
        _CAPI_DGLGraphAddEdge(self._handle, u, v);
Minjie Wang's avatar
Minjie Wang committed
53
        self._cache.clear()
Minjie Wang's avatar
Minjie Wang committed
54
55

    def add_edges(self, u, v):
Minjie Wang's avatar
Minjie Wang committed
56
57
58
59
60
61
62
63
64
65
66
        """Add many edges.
        
        Parameters
        ----------
        u : utils.Index
            The src nodes.
        v : utils.Index
            The dst nodes.
        """
        u_array = u.todgltensor()
        v_array = v.todgltensor()
Minjie Wang's avatar
Minjie Wang committed
67
        _CAPI_DGLGraphAddEdges(self._handle, u_array, v_array)
Minjie Wang's avatar
Minjie Wang committed
68
        self._cache.clear()
Minjie Wang's avatar
Minjie Wang committed
69
70

    def clear(self):
Minjie Wang's avatar
Minjie Wang committed
71
        """Clear the graph."""
Minjie Wang's avatar
Minjie Wang committed
72
        _CAPI_DGLGraphClear(self._handle)
Minjie Wang's avatar
Minjie Wang committed
73
        self._cache.clear()
Minjie Wang's avatar
Minjie Wang committed
74

75
76
77
78
79
80
81
82
83
84
    def is_multigraph(self):
        """Return whether the graph is a multigraph

        Returns
        -------
        bool
            True if it is a multigraph, False otherwise.
        """
        return bool(_CAPI_DGLGraphIsMultigraph(self._handle))

Minjie Wang's avatar
Minjie Wang committed
85
    def number_of_nodes(self):
Minjie Wang's avatar
Minjie Wang committed
86
87
88
89
90
91
92
        """Return the number of nodes.

        Returns
        -------
        int
            The number of nodes
        """
Minjie Wang's avatar
Minjie Wang committed
93
94
95
        return _CAPI_DGLGraphNumVertices(self._handle)

    def number_of_edges(self):
Minjie Wang's avatar
Minjie Wang committed
96
97
98
99
100
101
102
        """Return the number of edges.

        Returns
        -------
        int
            The number of edges
        """
Minjie Wang's avatar
Minjie Wang committed
103
104
        return _CAPI_DGLGraphNumEdges(self._handle)

Minjie Wang's avatar
Minjie Wang committed
105
106
107
108
109
110
111
112
113
114
115
    def has_node(self, vid):
        """Return true if the node exists.

        Parameters
        ----------
        vid : int
            The nodes

        Returns
        -------
        bool
116
            True if the node exists, False otherwise.
Minjie Wang's avatar
Minjie Wang committed
117
        """
118
        return bool(_CAPI_DGLGraphHasVertex(self._handle, vid))
Minjie Wang's avatar
Minjie Wang committed
119

Minjie Wang's avatar
Minjie Wang committed
120
121
122
123
124
125
126
127
128
129
130
131
132
133
    def has_nodes(self, vids):
        """Return true if the nodes exist.

        Parameters
        ----------
        vid : utils.Index
            The nodes

        Returns
        -------
        utils.Index
            0-1 array indicating existence
        """
        vid_array = vids.todgltensor()
Minjie Wang's avatar
Minjie Wang committed
134
        return utils.toindex(_CAPI_DGLGraphHasVertices(self._handle, vid_array))
Minjie Wang's avatar
Minjie Wang committed
135

136
    def has_edge_between(self, u, v):
Minjie Wang's avatar
Minjie Wang committed
137
138
139
140
141
142
143
144
145
146
147
148
        """Return true if the edge exists.

        Parameters
        ----------
        u : int
            The src node.
        v : int
            The dst node.

        Returns
        -------
        bool
149
            True if the edge exists, False otherwise
Minjie Wang's avatar
Minjie Wang committed
150
        """
151
        return bool(_CAPI_DGLGraphHasEdgeBetween(self._handle, u, v))
Minjie Wang's avatar
Minjie Wang committed
152

153
    def has_edges_between(self, u, v):
Minjie Wang's avatar
Minjie Wang committed
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
        """Return true if the edge exists.

        Parameters
        ----------
        u : utils.Index
            The src nodes.
        v : utils.Index
            The dst nodes.

        Returns
        -------
        utils.Index
            0-1 array indicating existence
        """
        u_array = u.todgltensor()
        v_array = v.todgltensor()
170
        return utils.toindex(_CAPI_DGLGraphHasEdgesBetween(self._handle, u_array, v_array))
Minjie Wang's avatar
Minjie Wang committed
171
172

    def predecessors(self, v, radius=1):
Minjie Wang's avatar
Minjie Wang committed
173
174
175
176
177
178
179
180
181
182
183
184
185
186
        """Return the predecessors of the node.

        Parameters
        ----------
        v : int
            The node.
        radius : int, optional
            The radius of the neighborhood.

        Returns
        -------
        utils.Index
            Array of predecessors
        """
Minjie Wang's avatar
Minjie Wang committed
187
        return utils.toindex(_CAPI_DGLGraphPredecessors(self._handle, v, radius))
Minjie Wang's avatar
Minjie Wang committed
188
189

    def successors(self, v, radius=1):
Minjie Wang's avatar
Minjie Wang committed
190
191
192
193
194
195
196
197
198
199
200
201
202
203
        """Return the successors of the node.

        Parameters
        ----------
        v : int
            The node.
        radius : int, optional
            The radius of the neighborhood.

        Returns
        -------
        utils.Index
            Array of successors
        """
Minjie Wang's avatar
Minjie Wang committed
204
        return utils.toindex(_CAPI_DGLGraphSuccessors(self._handle, v, radius))
Minjie Wang's avatar
Minjie Wang committed
205
206

    def edge_id(self, u, v):
207
        """Return the id array of all edges between u and v.
Minjie Wang's avatar
Minjie Wang committed
208
209
210
211
212
213
214
215
216
217

        Parameters
        ----------
        u : int
            The src node.
        v : int
            The dst node.

        Returns
        -------
218
219
        utils.Index
            The edge id array.
Minjie Wang's avatar
Minjie Wang committed
220
        """
221
        return utils.toindex(_CAPI_DGLGraphEdgeId(self._handle, u, v))
Minjie Wang's avatar
Minjie Wang committed
222
223

    def edge_ids(self, u, v):
224
        """Return a triplet of arrays that contains the edge IDs.
Minjie Wang's avatar
Minjie Wang committed
225
226
227
228
229
230
231
232
233
234
235

        Parameters
        ----------
        u : utils.Index
            The src nodes.
        v : utils.Index
            The dst nodes.

        Returns
        -------
        utils.Index
236
237
238
239
240
            The src nodes.
        utils.Index
            The dst nodes.
        utils.Index
            The edge ids.
Minjie Wang's avatar
Minjie Wang committed
241
242
243
        """
        u_array = u.todgltensor()
        v_array = v.todgltensor()
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
        edge_array = _CAPI_DGLGraphEdgeIds(self._handle, u_array, v_array)

        src = utils.toindex(edge_array(0))
        dst = utils.toindex(edge_array(1))
        eid = utils.toindex(edge_array(2))

        return src, dst, eid

    def find_edges(self, eid):
        """Return a triplet of arrays that contains the edge IDs.

        Parameters
        ----------
        eid : utils.Index
            The edge ids.

        Returns
        -------
        utils.Index
            The src nodes.
        utils.Index
            The dst nodes.
        utils.Index
            The edge ids.
        """
        eid_array = eid.todgltensor()
        edge_array = _CAPI_DGLGraphFindEdges(self._handle, eid_array)

        src = utils.toindex(edge_array(0))
        dst = utils.toindex(edge_array(1))
        eid = utils.toindex(edge_array(2))

        return src, dst, eid
Minjie Wang's avatar
Minjie Wang committed
277

278
    def in_edges(self, v):
Minjie Wang's avatar
Minjie Wang committed
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
        """Return the in edges of the node(s).

        Parameters
        ----------
        v : utils.Index
            The node(s).
        
        Returns
        -------
        utils.Index
            The src nodes.
        utils.Index
            The dst nodes.
        utils.Index
            The edge ids.
        """
        if len(v) == 1:
            edge_array = _CAPI_DGLGraphInEdges_1(self._handle, v[0])
Minjie Wang's avatar
Minjie Wang committed
297
        else:
Minjie Wang's avatar
Minjie Wang committed
298
            v_array = v.todgltensor()
299
            edge_array = _CAPI_DGLGraphInEdges_2(self._handle, v_array)
Minjie Wang's avatar
Minjie Wang committed
300
301
302
        src = utils.toindex(edge_array(0))
        dst = utils.toindex(edge_array(1))
        eid = utils.toindex(edge_array(2))
303
        return src, dst, eid
Minjie Wang's avatar
Minjie Wang committed
304

305
    def out_edges(self, v):
Minjie Wang's avatar
Minjie Wang committed
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
        """Return the out edges of the node(s).

        Parameters
        ----------
        v : utils.Index
            The node(s).
        
        Returns
        -------
        utils.Index
            The src nodes.
        utils.Index
            The dst nodes.
        utils.Index
            The edge ids.
        """
        if len(v) == 1:
            edge_array = _CAPI_DGLGraphOutEdges_1(self._handle, v[0])
Minjie Wang's avatar
Minjie Wang committed
324
        else:
Minjie Wang's avatar
Minjie Wang committed
325
            v_array = v.todgltensor()
326
            edge_array = _CAPI_DGLGraphOutEdges_2(self._handle, v_array)
Minjie Wang's avatar
Minjie Wang committed
327
328
329
        src = utils.toindex(edge_array(0))
        dst = utils.toindex(edge_array(1))
        eid = utils.toindex(edge_array(2))
330
        return src, dst, eid
Minjie Wang's avatar
Minjie Wang committed
331

332
    def edges(self, sorted=False):
Minjie Wang's avatar
Minjie Wang committed
333
334
335
336
337
        """Return all the edges

        Parameters
        ----------
        sorted : bool
Minjie Wang's avatar
Minjie Wang committed
338
            True if the returned edges are sorted by their src and dst ids.
Minjie Wang's avatar
Minjie Wang committed
339
340
341
342
343
344
345
346
347
348
        
        Returns
        -------
        utils.Index
            The src nodes.
        utils.Index
            The dst nodes.
        utils.Index
            The edge ids.
        """
349
        edge_array = _CAPI_DGLGraphEdges(self._handle, sorted)
Minjie Wang's avatar
Minjie Wang committed
350
351
352
        src = utils.toindex(edge_array(0))
        dst = utils.toindex(edge_array(1))
        eid = utils.toindex(edge_array(2))
353
354
355
        return src, dst, eid

    def in_degree(self, v):
Minjie Wang's avatar
Minjie Wang committed
356
357
358
359
360
361
362
363
364
365
366
367
        """Return the in degree of the node.

        Parameters
        ----------
        v : int
            The node.

        Returns
        -------
        int
            The in degree.
        """
Minjie Wang's avatar
Minjie Wang committed
368
369
        return _CAPI_DGLGraphInDegree(self._handle, v)

370
    def in_degrees(self, v):
Minjie Wang's avatar
Minjie Wang committed
371
372
373
374
375
376
377
378
379
380
381
382
383
        """Return the in degrees of the nodes.

        Parameters
        ----------
        v : utils.Index
            The nodes.

        Returns
        -------
        int
            The in degree array.
        """
        v_array = v.todgltensor()
Minjie Wang's avatar
Minjie Wang committed
384
        return utils.toindex(_CAPI_DGLGraphInDegrees(self._handle, v_array))
Minjie Wang's avatar
Minjie Wang committed
385

386
    def out_degree(self, v):
Minjie Wang's avatar
Minjie Wang committed
387
388
389
390
391
392
393
394
395
396
397
398
        """Return the out degree of the node.

        Parameters
        ----------
        v : int
            The node.

        Returns
        -------
        int
            The out degree.
        """
Minjie Wang's avatar
Minjie Wang committed
399
400
        return _CAPI_DGLGraphOutDegree(self._handle, v)

401
    def out_degrees(self, v):
Minjie Wang's avatar
Minjie Wang committed
402
403
404
405
406
407
408
409
410
411
412
413
414
        """Return the out degrees of the nodes.

        Parameters
        ----------
        v : utils.Index
            The nodes.

        Returns
        -------
        int
            The out degree array.
        """
        v_array = v.todgltensor()
Minjie Wang's avatar
Minjie Wang committed
415
        return utils.toindex(_CAPI_DGLGraphOutDegrees(self._handle, v_array))
Minjie Wang's avatar
Minjie Wang committed
416

Minjie Wang's avatar
Minjie Wang committed
417
418
419
420
421
422
423
424
425
426
    def node_subgraph(self, v):
        """Return the induced node subgraph.

        Parameters
        ----------
        v : utils.Index
            The nodes.

        Returns
        -------
427
        SubgraphIndex
Minjie Wang's avatar
Minjie Wang committed
428
429
430
431
432
            The subgraph index.
        """
        v_array = v.todgltensor()
        rst = _CAPI_DGLGraphVertexSubgraph(self._handle, v_array)
        induced_edges = utils.toindex(rst(2))
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
        return SubgraphIndex(rst(0), self, v, induced_edges)

    def edge_subgraph(self, e):
        """Return the induced edge subgraph.

        Parameters
        ----------
        e : utils.Index
            The edges.

        Returns
        -------
        SubgraphIndex
            The subgraph index.
        """
        e_array = e.todgltensor()
        rst = _CAPI_DGLGraphEdgeSubgraph(self._handle, e_array)
        gi = GraphIndex(rst(0))
        induced_nodes = utils.toindex(rst(1))
        return SubgraphIndex(rst(0), self, induced_nodes, e)
Minjie Wang's avatar
Minjie Wang committed
453

Minjie Wang's avatar
Minjie Wang committed
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
    def adjacency_matrix(self):
        """Return the adjacency matrix representation of this graph.

        Returns
        -------
        utils.CtxCachedObject
            An object that returns tensor given context.
        """
        if not 'adj' in self._cache:
            src, dst, _ = self.edges(sorted=False)
            src = F.unsqueeze(src.tousertensor(), 0)
            dst = F.unsqueeze(dst.tousertensor(), 0)
            idx = F.pack([dst, src])
            n = self.number_of_nodes()
            dat = F.ones((self.number_of_edges(),))
            mat = F.sparse_tensor(idx, dat, [n, n])
            self._cache['adj'] = utils.CtxCachedObject(lambda ctx: F.to_context(mat, ctx))
        return self._cache['adj']

GaiYu0's avatar
GaiYu0 committed
473
    def incidence_matrix(self, oriented=False):
GaiYu0's avatar
GaiYu0 committed
474
        """Return the incidence matrix representation of this graph.
GaiYu0's avatar
GaiYu0 committed
475
476
477
478
479
        
        Parameters
        ----------
        oriented : bool, optional (default=False)
          Whether the returned incidence matrix is oriented.
GaiYu0's avatar
GaiYu0 committed
480
481
482
483
484
485

        Returns
        -------
        utils.CtxCachedObject
            An object that returns tensor given context.
        """
GaiYu0's avatar
GaiYu0 committed
486
        key = ('oriented ' if oriented else '') + 'incidence matrix'
GaiYu0's avatar
cpp lg  
GaiYu0 committed
487
        if not key in self._cache:
GaiYu0's avatar
GaiYu0 committed
488
            src, dst, _ = self.edges(sorted=False)
GaiYu0's avatar
GaiYu0 committed
489
490
491
492
493
494
495
496
            src = src.tousertensor()
            dst = dst.tousertensor()
            m = self.number_of_edges()
            eid = F.arange(m, dtype=F.int64)
            row = F.pack([src, dst])
            col = F.pack([eid, eid])
            idx = F.stack([row, col])

GaiYu0's avatar
cpp lg  
GaiYu0 committed
497
498
499
500
501
502
503
504
505
506
507
            diagonal = (src == dst)
            if oriented:
                x = -F.ones((m,))
                y = F.ones((m,))
                x[diagonal] = 0
                y[diagonal] = 0
                dat = F.pack([x, y])
            else:
                x = F.ones((m,))
                x[diagonal] = 0
                dat = F.pack([x, x])
GaiYu0's avatar
GaiYu0 committed
508
509
            n = self.number_of_nodes()
            mat = F.sparse_tensor(idx, dat, [n, m])
GaiYu0's avatar
cpp lg  
GaiYu0 committed
510
            self._cache[key] = utils.CtxCachedObject(lambda ctx: F.to_context(mat, ctx))
GaiYu0's avatar
GaiYu0 committed
511

GaiYu0's avatar
cpp lg  
GaiYu0 committed
512
        return self._cache[key]
GaiYu0's avatar
GaiYu0 committed
513

Minjie Wang's avatar
Minjie Wang committed
514
    def to_networkx(self):
Minjie Wang's avatar
Minjie Wang committed
515
516
        """Convert to networkx graph.

Minjie Wang's avatar
Minjie Wang committed
517
518
        The edge id will be saved as the 'id' edge attribute.

Minjie Wang's avatar
Minjie Wang committed
519
520
521
522
523
        Returns
        -------
        networkx.DiGraph
            The nx graph
        """
Minjie Wang's avatar
Minjie Wang committed
524
        src, dst, eid = self.edges()
525
        ret = nx.MultiDiGraph() if self.is_multigraph() else nx.DiGraph()
526
        ret.add_nodes_from(range(self.number_of_nodes()))
Minjie Wang's avatar
Minjie Wang committed
527
528
529
530
531
532
        for u, v, id in zip(src, dst, eid):
            ret.add_edge(u, v, id=id)
        return ret

    def from_networkx(self, nx_graph):
        """Convert from networkx graph.
Minjie Wang's avatar
Minjie Wang committed
533

Minjie Wang's avatar
Minjie Wang committed
534
535
536
537
538
539
540
541
542
        If 'id' edge attribute exists, the edge will be added follows
        the edge id order. Otherwise, order is undefined.
        
        Parameters
        ----------
        nx_graph : networkx.DiGraph
            The nx graph
        """
        self.clear()
543
544
545
546
547
548
549

        if not isinstance(nx_graph, nx.Graph):
            nx_graph = (nx.MultiDiGraph(nx_graph) if self.is_multigraph()
                    else nx.DiGraph(nx_graph))
        else:
            nx_graph = nx_graph.to_directed()

Minjie Wang's avatar
Minjie Wang committed
550
551
        num_nodes = nx_graph.number_of_nodes()
        self.add_nodes(num_nodes)
552
553
554
555
556
557

        if nx_graph.number_of_edges() == 0:
            return

        # nx_graph.edges(data=True) returns src, dst, attr_dict
        has_edge_id = 'id' in next(iter(nx_graph.edges(data=True)))[-1]
Minjie Wang's avatar
Minjie Wang committed
558
559
560
561
        if has_edge_id:
            num_edges = nx_graph.number_of_edges()
            src = np.zeros((num_edges,), dtype=np.int64)
            dst = np.zeros((num_edges,), dtype=np.int64)
562
            for u, v, attr in nx_graph.edges(data=True):
Minjie Wang's avatar
Minjie Wang committed
563
                eid = attr['id']
564
565
                src[eid] = u
                dst[eid] = v
Minjie Wang's avatar
Minjie Wang committed
566
567
568
        else:
            src = []
            dst = []
569
570
571
            for e in nx_graph.edges:
                src.append(e[0])
                dst.append(e[1])
Minjie Wang's avatar
Minjie Wang committed
572
573
574
575
        src = utils.toindex(src)
        dst = utils.toindex(dst)
        self.add_edges(src, dst)

GaiYu0's avatar
GaiYu0 committed
576
577
578
579
580
    def from_scipy_sparse_matrix(self, adj):
        """Convert from scipy sparse matrix.

        Parameters
        ----------
GaiYu0's avatar
GaiYu0 committed
581
        adj : scipy sparse matrix
GaiYu0's avatar
GaiYu0 committed
582
583
584
585
586
587
588
589
        """
        self.clear()
        self.add_nodes(adj.shape[0])
        adj_coo = adj.tocoo()
        src = utils.toindex(adj_coo.row)
        dst = utils.toindex(adj_coo.col)
        self.add_edges(src, dst)

GaiYu0's avatar
GaiYu0 committed
590
    def line_graph(self, backtracking=True):
GaiYu0's avatar
GaiYu0 committed
591
592
        """Return the line graph of this graph.

GaiYu0's avatar
GaiYu0 committed
593
594
595
596
597
598
        Parameters
        ----------
        backtracking : bool, optional (default=False)
          Whether (i, j) ~ (j, i) in L(G).
          (i, j) ~ (j, i) is the behavior of networkx.line_graph.

GaiYu0's avatar
GaiYu0 committed
599
600
601
602
603
        Returns
        -------
        GraphIndex
            The line graph of this graph.
        """
GaiYu0's avatar
cpp lg  
GaiYu0 committed
604
605
        handle = _CAPI_DGLGraphLineGraph(self._handle, backtracking)
        return GraphIndex(handle)
606
607

class SubgraphIndex(GraphIndex):
608
    """Graph index for subgraph.
609

610
611
612
613
614
615
616
617
618
619
620
621
622
    Parameters
    ----------
    handle : GraphIndexHandle
        The capi handle.
    paranet : GraphIndex
        The parent graph index.
    induced_nodes : utils.Index
        The parent node ids in this subgraph.
    induced_edges : utils.Index
        The parent edge ids in this subgraph.
    """
    def __init__(self, handle, parent, induced_nodes, induced_edges):
        super(SubgraphIndex, self).__init__(handle)
623
624
625
626
627
        self._parent = parent
        self._induced_nodes = induced_nodes
        self._induced_edges = induced_edges

    def add_nodes(self, num):
628
        """Add nodes. Disabled because SubgraphIndex is read-only."""
629
630
631
        raise RuntimeError('Readonly graph. Mutation is not allowed.')

    def add_edge(self, u, v):
632
        """Add edges. Disabled because SubgraphIndex is read-only."""
633
634
635
        raise RuntimeError('Readonly graph. Mutation is not allowed.')

    def add_edges(self, u, v):
636
        """Add edges. Disabled because SubgraphIndex is read-only."""
637
638
639
640
        raise RuntimeError('Readonly graph. Mutation is not allowed.')

    @property
    def induced_nodes(self):
641
642
643
644
645
646
647
        """Return parent node ids.

        Returns
        -------
        utils.Index
            The parent node ids.
        """
648
649
        return self._induced_nodes

650
651
652
653
654
655
656
657
658
659
660
    @property
    def induced_edges(self):
        """Return parent edge ids.

        Returns
        -------
        utils.Index
            The parent edge ids.
        """
        return self._induced_edges

Minjie Wang's avatar
Minjie Wang committed
661
662
def disjoint_union(graphs):
    """Return a disjoint union of the input graphs.
663

Minjie Wang's avatar
Minjie Wang committed
664
665
666
667
668
    The new graph will include all the nodes/edges in the given graphs.
    Nodes/Edges will be relabled by adding the cumsum of the previous graph sizes
    in the given sequence order. For example, giving input [g1, g2, g3], where
    they have 5, 6, 7 nodes respectively. Then node#2 of g2 will become node#7
    in the result graph. Edge ids are re-assigned similarly.
669

Minjie Wang's avatar
Minjie Wang committed
670
671
672
673
    Parameters
    ----------
    graphs : iterable of GraphIndex
        The input graphs
674

Minjie Wang's avatar
Minjie Wang committed
675
676
677
678
679
680
681
682
683
    Returns
    -------
    GraphIndex
        The disjoint union
    """
    inputs = c_array(GraphIndexHandle, [gr._handle for gr in graphs])
    inputs = ctypes.cast(inputs, ctypes.c_void_p)
    handle = _CAPI_DGLDisjointUnion(inputs, len(graphs))
    return GraphIndex(handle)
684

Minjie Wang's avatar
Minjie Wang committed
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
def disjoint_partition(graph, num_or_size_splits):
    """Partition the graph disjointly.
   
    This is a reverse operation of DisjointUnion. The graph will be partitioned
    into num graphs. This requires the given number of partitions to evenly
    divides the number of nodes in the graph. If the a size list is given,
    the sum of the given sizes is equal.

    Parameters
    ----------
    graph : GraphIndex
        The graph to be partitioned
    num_or_size_splits : int or utils.Index
        The partition number of size splits

    Returns
    -------
    list of GraphIndex
        The partitioned graphs
    """
    if isinstance(num_or_size_splits, utils.Index):
        rst = _CAPI_DGLDisjointPartitionBySizes(
                graph._handle,
                num_or_size_splits.todgltensor())
    else:
        rst = _CAPI_DGLDisjointPartitionByNum(
                graph._handle,
                int(num_or_size_splits))
    graphs = []
    for val in rst.asnumpy():
        handle = ctypes.cast(int(val), ctypes.c_void_p)
        graphs.append(GraphIndex(handle))
    return graphs

719
def create_graph_index(graph_data=None, multigraph=False):
720
721
722
723
724
725
    """Create a graph index object.

    Parameters
    ----------
    graph_data : graph data, optional
        Data to initialize graph. Same as networkx's semantics.
726
727
    multigraph : bool, optional
        Whether the graph is multigraph (default is False)
728
    """
Minjie Wang's avatar
Minjie Wang committed
729
730
    if isinstance(graph_data, GraphIndex):
        return graph_data
731
732

    handle = _CAPI_DGLGraphCreate(multigraph)
733
    gi = GraphIndex(handle)
734
735
736
737
738
739
740
741
742
743
744
745
746
747

    if graph_data is None:
        return gi

    # scipy format
    if isinstance(graph_data, scipy.sparse.spmatrix):
        try:
            gi.from_scipy_sparse_matrix(graph_data)
            return gi
        except:
            raise Exception('Graph data is not a valid scipy sparse matrix.')

    # networkx - any format
    try:
748
        gi.from_networkx(graph_data)
749
750
751
752
    except:
        raise Exception('Error while creating graph from input of type "%s".'
                         % type(graph_data))

753
754
    return gi

Minjie Wang's avatar
Minjie Wang committed
755
_init_api("dgl.graph_index")