graph_index.py 19.8 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()
Minjie Wang's avatar
Minjie Wang committed
526
527
528
529
530
531
        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
532

Minjie Wang's avatar
Minjie Wang committed
533
534
535
536
537
538
539
540
541
        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()
542
543
544
545
546
547
548

        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
549
550
551
552
553
554
555
556
        num_nodes = nx_graph.number_of_nodes()
        self.add_nodes(num_nodes)
        has_edge_id = 'id' in next(iter(nx_graph.edges))
        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)
            for e, attr in nx_graph.edges.items:
557
                # MultiDiGraph returns a triplet in e while DiGraph returns a pair
Minjie Wang's avatar
Minjie Wang committed
558
                eid = attr['id']
559
560
                src[eid] = e[0]
                dst[eid] = e[1]
Minjie Wang's avatar
Minjie Wang committed
561
562
563
        else:
            src = []
            dst = []
564
565
566
            for e in nx_graph.edges:
                src.append(e[0])
                dst.append(e[1])
Minjie Wang's avatar
Minjie Wang committed
567
568
569
570
        src = utils.toindex(src)
        dst = utils.toindex(dst)
        self.add_edges(src, dst)

GaiYu0's avatar
GaiYu0 committed
571
572
573
574
575
    def from_scipy_sparse_matrix(self, adj):
        """Convert from scipy sparse matrix.

        Parameters
        ----------
GaiYu0's avatar
GaiYu0 committed
576
        adj : scipy sparse matrix
GaiYu0's avatar
GaiYu0 committed
577
578
579
580
581
582
583
584
        """
        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
585
    def line_graph(self, backtracking=True):
GaiYu0's avatar
GaiYu0 committed
586
587
        """Return the line graph of this graph.

GaiYu0's avatar
GaiYu0 committed
588
589
590
591
592
593
        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
594
595
596
597
598
        Returns
        -------
        GraphIndex
            The line graph of this graph.
        """
GaiYu0's avatar
cpp lg  
GaiYu0 committed
599
600
        handle = _CAPI_DGLGraphLineGraph(self._handle, backtracking)
        return GraphIndex(handle)
601
602

class SubgraphIndex(GraphIndex):
603
    """Graph index for subgraph.
604

605
606
607
608
609
610
611
612
613
614
615
616
617
    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)
618
619
620
621
622
        self._parent = parent
        self._induced_nodes = induced_nodes
        self._induced_edges = induced_edges

    def add_nodes(self, num):
623
        """Add nodes. Disabled because SubgraphIndex is read-only."""
624
625
626
        raise RuntimeError('Readonly graph. Mutation is not allowed.')

    def add_edge(self, u, v):
627
        """Add edges. Disabled because SubgraphIndex is read-only."""
628
629
630
        raise RuntimeError('Readonly graph. Mutation is not allowed.')

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

    @property
    def induced_nodes(self):
636
637
638
639
640
641
642
        """Return parent node ids.

        Returns
        -------
        utils.Index
            The parent node ids.
        """
643
644
        return self._induced_nodes

645
646
647
648
649
650
651
652
653
654
655
    @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
656
657
def disjoint_union(graphs):
    """Return a disjoint union of the input graphs.
658

Minjie Wang's avatar
Minjie Wang committed
659
660
661
662
663
    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.
664

Minjie Wang's avatar
Minjie Wang committed
665
666
667
668
    Parameters
    ----------
    graphs : iterable of GraphIndex
        The input graphs
669

Minjie Wang's avatar
Minjie Wang committed
670
671
672
673
674
675
676
677
678
    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)
679

Minjie Wang's avatar
Minjie Wang committed
680
681
682
683
684
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
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

714
def create_graph_index(graph_data=None, multigraph=False):
715
716
717
718
719
720
    """Create a graph index object.

    Parameters
    ----------
    graph_data : graph data, optional
        Data to initialize graph. Same as networkx's semantics.
721
722
    multigraph : bool, optional
        Whether the graph is multigraph (default is False)
723
    """
Minjie Wang's avatar
Minjie Wang committed
724
725
    if isinstance(graph_data, GraphIndex):
        return graph_data
726
727

    handle = _CAPI_DGLGraphCreate(multigraph)
728
    gi = GraphIndex(handle)
729
730
731
732
733
734
735
736
737
738
739
740
741
742

    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:
743
        gi.from_networkx(graph_data)
744
745
746
747
    except:
        raise Exception('Error while creating graph from input of type "%s".'
                         % type(graph_data))

748
749
    return gi

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