Commit 5680f8dc authored by Gan Quan's avatar Gan Quan Committed by Minjie Wang
Browse files

[DOC] Documentation strings for graph mutation, queries, and subgraphing (#207)

* docstrings for graph mutation & queries

* [Doc] contrib.sampler. (#200)

* subgraph docs

* updates
parent 57daf9c9
...@@ -10,4 +10,5 @@ API Reference ...@@ -10,4 +10,5 @@ API Reference
traversal traversal
propagate propagate
udf udf
sampler
data data
.. apisampler
Sampling subgraphs from a large graph
=====================================
.. currentmodule:: dgl.contrib.sampling
NeighborSampler
---------------
A function that generates a subgraph loader for sampling subgraphs.
...@@ -102,7 +102,8 @@ def NeighborSampler(g, batch_size, expand_factor, num_hops=1, ...@@ -102,7 +102,8 @@ def NeighborSampler(g, batch_size, expand_factor, num_hops=1,
node_prob: the probability that a neighbor node is sampled. node_prob: the probability that a neighbor node is sampled.
1D Tensor. None means uniform sampling. Otherwise, the number of elements 1D Tensor. None means uniform sampling. Otherwise, the number of elements
should be the same as the number of vertices in the graph. should be the same as the number of vertices in the graph.
seed_nodes: a list of nodes where we sample subgraphs from. If it's None, the seed vertices are all vertices in the graph. seed_nodes: a list of nodes where we sample subgraphs from.
If it's None, the seed vertices are all vertices in the graph.
shuffle: indicates the sampled subgraphs are shuffled. shuffle: indicates the sampled subgraphs are shuffled.
num_workers: the number of worker threads that sample subgraphs in parallel. num_workers: the number of worker threads that sample subgraphs in parallel.
max_subgraph_size: the maximal subgraph size in terms of the number of nodes. max_subgraph_size: the maximal subgraph size in terms of the number of nodes.
...@@ -110,7 +111,8 @@ def NeighborSampler(g, batch_size, expand_factor, num_hops=1, ...@@ -110,7 +111,8 @@ def NeighborSampler(g, batch_size, expand_factor, num_hops=1,
Returns Returns
------- -------
A subgraph generator. A subgraph loader that returns a batch of subgraphs and
the Ids of the seed vertices used in the batch.
''' '''
return NSSubgraphLoader(g, batch_size, expand_factor, num_hops, neighbor_type, node_prob, return NSSubgraphLoader(g, batch_size, expand_factor, num_hops, neighbor_type, node_prob,
seed_nodes, shuffle, num_workers, max_subgraph_size) seed_nodes, shuffle, num_workers, max_subgraph_size)
...@@ -27,7 +27,9 @@ class DGLGraph(object): ...@@ -27,7 +27,9 @@ class DGLGraph(object):
Nodes are identified by consecutive integers starting from zero. Nodes are identified by consecutive integers starting from zero.
Edges can be specified by two end points (u, v) or the integer id assigned Edges can be specified by two end points (u, v) or the integer id assigned
when the edges are added. when the edges are added. Edge IDs are automatically assigned by the order
of addition, i.e. the first edge being added has an ID of 0, the second
being 1, so on so forth.
Node and edge features are stored as a dictionary from the feature name Node and edge features are stored as a dictionary from the feature name
to the feature data (in tensor). to the feature data (in tensor).
...@@ -158,14 +160,43 @@ class DGLGraph(object): ...@@ -158,14 +160,43 @@ class DGLGraph(object):
self._apply_edge_func = None self._apply_edge_func = None
def add_nodes(self, num, data=None): def add_nodes(self, num, data=None):
"""Add nodes. """Add multiple new nodes.
Parameters Parameters
---------- ----------
num : int num : int
Number of nodes to be added. Number of nodes to be added.
data : dict data : dict, optional
Optional node feature data. Feature data of the added nodes.
Notes
-----
If new nodes are added with features, and any of the old nodes
do not have some of the feature fields, those fields are filled
by initializers defined with ``set_n_initializer`` (default filling
with zeros).
Examples
--------
>>> G = dgl.DGLGraph()
>>> g.add_nodes(2)
>>> g.number_of_nodes()
2
>>> g.add_nodes(3)
>>> g.number_of_nodes()
5
Adding new nodes with features (using PyTorch as example):
>>> import torch as th
>>> g.add_nodes(2, {'x': th.ones(2, 4)}) # default zero initializer
>>> g.ndata['x']
tensor([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]])
""" """
self._graph.add_nodes(num) self._graph.add_nodes(num)
self._msg_graph.add_nodes(num) self._msg_graph.add_nodes(num)
...@@ -176,16 +207,41 @@ class DGLGraph(object): ...@@ -176,16 +207,41 @@ class DGLGraph(object):
self._node_frame.append(data) self._node_frame.append(data)
def add_edge(self, u, v, data=None): def add_edge(self, u, v, data=None):
"""Add one edge. """Add one new edge between u and v.
Parameters Parameters
---------- ----------
u : int u : int
The src node. The source node ID. Must exist in the graph.
v : int v : int
The dst node. The destination node ID. Must exist in the graph.
data : dict data : dict, optional
Optional node feature data. Feature data of the added edges.
Notes
-----
If new edges are added with features, and any of the old edges
do not have some of the feature fields, those fields are filled
by initializers defined with ``set_e_initializer`` (default filling
with zeros).
Examples
--------
The following example uses PyTorch backend.
>>> G = dgl.DGLGraph()
>>> G.add_nodes(3)
>>> G.add_edge(0, 1)
Adding new edge with features
>>> import torch as th
>>> G.add_edge(0, 2, {'x': th.ones(1, 4)})
>>> G.edges()
(tensor([0, 0]), tensor([1, 2]))
>>> G.edata['x']
tensor([[0., 0., 0., 0.],
[1., 1., 1., 1.]])
>>> G.edges[0, 2].data['x']
tensor([[1., 1., 1., 1.]])
See Also See Also
-------- --------
...@@ -199,16 +255,40 @@ class DGLGraph(object): ...@@ -199,16 +255,40 @@ class DGLGraph(object):
self._edge_frame.append(data) self._edge_frame.append(data)
def add_edges(self, u, v, data=None): def add_edges(self, u, v, data=None):
"""Add many edges. """Add multiple edges for list of source nodes u and destination nodes
v. A single edge is added between every pair of ``u[i]`` and ``v[i]``.
Parameters Parameters
---------- ----------
u : list, tensor u : list, tensor
The src nodes. The source node IDs. All nodes must exist in the graph.
v : list, tensor v : list, tensor
The dst nodes. The destination node IDs. All nodes must exist in the graph.
reprs : dict data : dict, optional
Optional node representations. Feature data of the added edges.
Notes
-----
If new edges are added with features, and any of the old edges
do not have some of the feature fields, those fields are filled
by initializers defined with ``set_e_initializer`` (default filling
with zeros).
Examples
--------
The following example uses PyTorch backend.
>>> G = dgl.DGLGraph()
>>> G.add_nodes(4)
>>> G.add_edges([0, 2], [1, 3]) # add edges (0, 1) and (2, 3)
Adding new edges with features
>>> import torch as th
>>> G.add_edges([1, 3], [2, 0], {'x': th.ones(2, 4)}) # (1, 2), (3, 0)
>>> G.edata['x']
tensor([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]])
See Also See Also
-------- --------
...@@ -225,7 +305,24 @@ class DGLGraph(object): ...@@ -225,7 +305,24 @@ class DGLGraph(object):
self._edge_frame.append(data) self._edge_frame.append(data)
def clear(self): def clear(self):
"""Clear the graph and its storage.""" """Remove all nodes and edges, as well as their features, from the
graph.
Examples
--------
>>> G = dgl.DGLGraph()
>>> G.add_nodes(4)
>>> G.add_edges([0, 1, 2, 3], [1, 2, 3, 0])
>>> G.number_of_nodes()
4
>>> G.number_of_edges()
4
>>> G.clear()
>>> G.number_of_nodes()
0
>>> G.number_of_edges()
0
"""
self._graph.clear() self._graph.clear()
self._node_frame.clear() self._node_frame.clear()
self._edge_frame.clear() self._edge_frame.clear()
...@@ -239,7 +336,7 @@ class DGLGraph(object): ...@@ -239,7 +336,7 @@ class DGLGraph(object):
self._msg_graph.add_nodes(self.number_of_nodes()) self._msg_graph.add_nodes(self.number_of_nodes())
def number_of_nodes(self): def number_of_nodes(self):
"""Return the number of nodes. """Return the number of nodes in the graph.
Returns Returns
------- -------
...@@ -249,17 +346,17 @@ class DGLGraph(object): ...@@ -249,17 +346,17 @@ class DGLGraph(object):
return self._graph.number_of_nodes() return self._graph.number_of_nodes()
def __len__(self): def __len__(self):
"""Return the number of nodes.""" """Return the number of nodes in the graph."""
return self.number_of_nodes() return self.number_of_nodes()
@property @property
def is_multigraph(self): def is_multigraph(self):
"""Whether the graph is a multigraph. """True if the graph is a multigraph, False otherwise.
""" """
return self._graph.is_multigraph() return self._graph.is_multigraph()
def number_of_edges(self): def number_of_edges(self):
"""Return the number of edges. """Return the number of edges in the graph.
Returns Returns
------- -------
...@@ -269,18 +366,33 @@ class DGLGraph(object): ...@@ -269,18 +366,33 @@ class DGLGraph(object):
return self._graph.number_of_edges() return self._graph.number_of_edges()
def has_node(self, vid): def has_node(self, vid):
"""Return true if the node exists. """Return True if the graph contains node `vid`.
Identical to `vid in G`.
Parameters Parameters
---------- ----------
vid : int vid : int
The nodes The node ID.
Returns Returns
------- -------
bool bool
True if the node exists True if the node exists
Examples
--------
>>> G = dgl.DGLGraph()
>>> G.add_nodes(3)
>>> G.has_node(0)
True
>>> G.has_node(4)
False
Equivalently,
>>> 0 in G
True
See Also See Also
-------- --------
has_nodes has_nodes
...@@ -288,22 +400,40 @@ class DGLGraph(object): ...@@ -288,22 +400,40 @@ class DGLGraph(object):
return self._graph.has_node(vid) return self._graph.has_node(vid)
def __contains__(self, vid): def __contains__(self, vid):
"""Same as has_node.""" """Return True if the graph contains node `vid`.
Examples
--------
>>> G = dgl.DGLGraph()
>>> G.add_nodes(3)
>>> 0 in G
True
"""
return self._graph.has_node(vid) return self._graph.has_node(vid)
def has_nodes(self, vids): def has_nodes(self, vids):
"""Return true if the nodes exist. """Return a 0-1 array `a` given the node ID array `vids`.
`a[i]` is 1 if the graph contains node `vids[i]`, 0 otherwise.
Parameters Parameters
---------- ----------
vid : list, tensor vid : list or tensor
The nodes The array of node IDs.
Returns Returns
------- -------
tensor a : tensor
0-1 array indicating existence 0-1 array indicating existence
Examples
--------
The following example uses PyTorch backend.
>>> G = dgl.DGLGraph()
>>> G.add_nodes(3)
>>> G.has_nodes([0, 1, 2, 3, 4])
tensor([1, 1, 1, 0, 0])
See Also See Also
-------- --------
has_node has_node
...@@ -313,19 +443,29 @@ class DGLGraph(object): ...@@ -313,19 +443,29 @@ class DGLGraph(object):
return rst.tousertensor() return rst.tousertensor()
def has_edge_between(self, u, v): def has_edge_between(self, u, v):
"""Return true if the edge exists. """Return True if the edge (u, v) is in the graph.
Parameters Parameters
---------- ----------
u : int u : int
The src node. The source node ID.
v : int v : int
The dst node. The destination node ID.
Returns Returns
------- -------
bool bool
True if the edge exists True if the edge is in the graph, False otherwise.
Examples
--------
>>> G = dgl.DGLGraph()
>>> G.add_nodes(3)
>>> G.add_edge(0, 1)
>>> G.has_edge_between(0, 1)
True
>>> G.has_edge_between(1, 0)
False
See Also See Also
-------- --------
...@@ -334,19 +474,33 @@ class DGLGraph(object): ...@@ -334,19 +474,33 @@ class DGLGraph(object):
return self._graph.has_edge_between(u, v) return self._graph.has_edge_between(u, v)
def has_edges_between(self, u, v): def has_edges_between(self, u, v):
"""Return true if the edge exists. """Return a 0-1 array `a` given the source node ID array `u` and
destination node ID array `v`.
`a[i]` is 1 if the graph contains edge `(u[i], v[i])`, 0 otherwise.
Parameters Parameters
---------- ----------
u : list, tensor u : list, tensor
The src nodes. The source node ID array.
v : list, tensor v : list, tensor
The dst nodes. The destination node ID array.
Returns Returns
------- -------
tensor a : tensor
0-1 array indicating existence 0-1 array indicating existence.
Examples
--------
The following example uses PyTorch backend.
>>> G = dgl.DGLGraph()
>>> G.add_nodes(3)
>>> G.add_edges([0, 0], [1, 2]) # (0, 1), (0, 2)
Check if (0, 1), (0, 2), (1, 0), (2, 0) exist in the graph above:
>>> G.has_edges_between([0, 0, 1, 2], [1, 2, 0, 0])
tensor([1, 1, 0, 0])
See Also See Also
-------- --------
...@@ -357,49 +511,78 @@ class DGLGraph(object): ...@@ -357,49 +511,78 @@ class DGLGraph(object):
rst = self._graph.has_edges_between(u, v) rst = self._graph.has_edges_between(u, v)
return rst.tousertensor() return rst.tousertensor()
def predecessors(self, v, radius=1): def predecessors(self, v):
"""Return the predecessors of the node. """Return the predecessors of node `v` in the graph.
Node `u` is a predecessor of `v` if an edge `(u, v)` exist in the
graph.
Parameters Parameters
---------- ----------
v : int v : int
The node. The node.
radius : int, optional
The radius of the neighborhood.
Returns Returns
------- -------
tensor tensor
Array of predecessors Array of predecessor node IDs.
Examples
--------
The following example uses PyTorch backend.
>>> G = dgl.DGLGraph()
>>> G.add_nodes(3)
>>> G.add_edges([1, 2], [0, 0]) # (1, 0), (2, 0)
>>> G.predecessors(0)
tensor([1, 2])
See Also
--------
successors
""" """
return self._graph.predecessors(v).tousertensor() return self._graph.predecessors(v).tousertensor()
def successors(self, v, radius=1): def successors(self, v):
"""Return the successors of the node. """Return the successors of node `v` in the graph.
Node `u` is a successor of `v` if an edge `(v, u)` exist in the
graph.
Parameters Parameters
---------- ----------
v : int v : int
The node. The node.
radius : int, optional
The radius of the neighborhood.
Returns Returns
------- -------
tensor tensor
Array of successors Array of successor node IDs.
Examples
--------
The following example uses PyTorch backend.
>>> G = dgl.DGLGraph()
>>> G.add_nodes(3)
>>> G.add_edges([0, 0], [1, 2]) # (0, 1), (0, 2)
>>> G.successors(0)
tensor([1, 2])
See Also
--------
predecessors
""" """
return self._graph.successors(v).tousertensor() return self._graph.successors(v).tousertensor()
def edge_id(self, u, v, force_multi=False): def edge_id(self, u, v, force_multi=False):
"""Return the id of the edge. """Return the edge ID, or an array of edge IDs, between source node
`u` and destination node `v`.
Parameters Parameters
---------- ----------
u : int u : int
The src node. The source node ID.
v : int v : int
The dst node. The destination node ID.
force_multi : bool force_multi : bool
If False, will return a single edge ID if the graph is a simple graph. If False, will return a single edge ID if the graph is a simple graph.
If True, will always return an array. If True, will always return an array.
...@@ -407,8 +590,31 @@ class DGLGraph(object): ...@@ -407,8 +590,31 @@ class DGLGraph(object):
Returns Returns
------- -------
int or tensor int or tensor
The edge id if force_multi == True and the graph is a simple graph. The edge ID if force_multi == True and the graph is a simple graph.
The edge id array otherwise. The edge ID array otherwise.
Examples
--------
The following example uses PyTorch backend.
For simple graphs:
>>> G = dgl.DGLGraph()
>>> G.add_node(3)
>>> G.add_edges([0, 0], [1, 2]) # (0, 1), (0, 2)
>>> G.edge_id(0, 2)
1
>>> G.edge_id(0, 1)
0
For multigraphs:
>>> G = dgl.DGLGraph(multigraph=True)
>>> G.add_nodes(3)
Adding edges (0, 1), (0, 2), (0, 1), (0, 2), so edge ID 0 and 2 both
connect from 0 and 1, while edge ID 1 and 3 both connect from 0 and 2.
>>> G.add_edges([0, 0, 0, 0], [1, 2, 1, 2])
>>> G.edge_id(0, 1)
tensor([0, 2])
See Also See Also
-------- --------
...@@ -418,23 +624,54 @@ class DGLGraph(object): ...@@ -418,23 +624,54 @@ class DGLGraph(object):
return idx.tousertensor() if force_multi or self.is_multigraph else idx[0] return idx.tousertensor() if force_multi or self.is_multigraph else idx[0]
def edge_ids(self, u, v, force_multi=False): def edge_ids(self, u, v, force_multi=False):
"""Return the edge ids. """Return all edge IDs between source node array `u` and destination
node array `v`.
Parameters Parameters
---------- ----------
u : list, tensor u : list, tensor
The src nodes. The source node ID array.
v : list, tensor v : list, tensor
The dst nodes. The destination node ID array.
force_multi : bool force_multi : bool
If False, will return a single edge ID array if the graph is a simple graph. Whether to always treat the graph as a multigraph.
If True, will always return 3 arrays (src nodes, dst nodes, edge ids).
Returns Returns
------- -------
tensor, or (tensor, tensor, tensor) tensor, or (tensor, tensor, tensor)
If force_multi is True or the graph is multigraph, return (src nodes, dst nodes, edge ids) If the graph is a simple graph and `force_multi` is False, return
Otherwise, return a single tensor of edge ids. a single edge ID array `e`. `e[i]` is the edge ID between `u[i]`
and `v[i]`.
Otherwise, return three arrays `(eu, ev, e)`. `e[i]` is the ID
of an edge between `eu[i]` and `ev[i]`. All edges between `u[i]`
and `v[i]` are returned.
Notes
-----
If the graph is a simple graph, `force_multi` is False, and no edge
exist between some pairs of `u[i]` and `v[i]`, the result is undefined.
Examples
--------
The following example uses PyTorch backend.
For simple graphs:
>>> G = dgl.DGLGraph()
>>> G.add_nodes(3)
>>> G.add_edges([0, 0], [1, 2]) # (0, 1), (0, 2)
>>> G.edge_ids([0, 0], [2, 1]) # get edge ID of (0, 2) and (0, 1)
>>> G.edge_ids([0, 0], [2, 1])
tensor([1, 0])
For multigraphs
>>> G = dgl.DGLGraph(multigraph=True)
>>> G.add_nodes(4)
>>> G.add_edges([0, 0, 0], [1, 1, 2]) # (0, 1), (0, 1), (0, 2)
Get all edges between (0, 1), (0, 2), (0, 3). Note that there is no
edge between 0 and 3:
>>> G.edge_ids([0, 0, 0], [1, 2, 3])
(tensor([0, 0, 0]), tensor([1, 1, 2]), tensor([0, 1, 2]))
See Also See Also
-------- --------
...@@ -449,26 +686,38 @@ class DGLGraph(object): ...@@ -449,26 +686,38 @@ class DGLGraph(object):
return eid.tousertensor() return eid.tousertensor()
def find_edges(self, eid): def find_edges(self, eid):
"""Given the edge ids, return their source and destination node ids. """Given an edge ID array, return the source and destination node ID
array `s` and `d`. `s[i]` and `d[i]` are source and destination node
ID for edge `eid[i]`.
Parameters Parameters
---------- ----------
eid : list, tensor eid : list, tensor
The edge ids. The edge ID array.
Returns Returns
------- -------
tensor tensor
The source nodes. The source node ID array.
tensor tensor
The destination nodes. The destination node ID array.
Examples
--------
The following example uses PyTorch backend.
>>> G = dgl.DGLGraph()
>>> G.add_nodes(3)
>>> G.add_edges([0, 0, 1], [1, 2, 2]) # (0, 1), (0, 2), (1, 2)
>>> G.find_edges([0, 2])
(tensor([0, 1]), tensor([1, 2]))
""" """
eid = utils.toindex(eid) eid = utils.toindex(eid)
src, dst, _ = self._graph.find_edges(eid) src, dst, _ = self._graph.find_edges(eid)
return src.tousertensor(), dst.tousertensor() return src.tousertensor(), dst.tousertensor()
def in_edges(self, v, form='uv'): def in_edges(self, v, form='uv'):
"""Return the in edges of the node(s). """Return the inbound edges of the node(s).
Parameters Parameters
---------- ----------
...@@ -482,9 +731,35 @@ class DGLGraph(object): ...@@ -482,9 +731,35 @@ class DGLGraph(object):
Returns Returns
------- -------
A tuple of Tensors (u, v, eid) if form == 'all' A tuple of Tensors `(eu, ev, eid)` if `form == 'all'`.
A pair of Tensors (u, v) if form == 'uv' `eid[i]` is the ID of an inbound edge to `ev[i]` from `eu[i]`.
All inbound edges to `v` are returned.
A pair of Tensors (eu, ev) if form == 'uv'
`eu[i]` is the source node of an inbound edge to `ev[i]`.
All inbound edges to `v` are returned.
One Tensor if form == 'eid' One Tensor if form == 'eid'
`eid[i]` is ID of an inbound edge to any of the nodes in `v`.
Examples
--------
The following example uses PyTorch backend.
>>> G = dgl.DGLGraph()
>>> G.add_nodes(3)
>>> G.add_edges([0, 0, 1], [1, 2, 2]) # (0, 1), (0, 2), (1, 2)
For a single node:
>>> G.in_edges(2)
(tensor([0, 1]), tensor([2, 2]))
>>> G.in_edges(2, 'all')
(tensor([0, 1]), tensor([2, 2]), tensor([1, 2]))
>>> G.in_edges(2, 'eid')
tensor([1, 2])
For multiple nodes:
>>> G.in_edges([1, 2])
(tensor([0, 0, 1]), tensor([1, 2, 2]))
>>> G.in_edges([1, 2], 'all')
(tensor([0, 0, 1]), tensor([1, 2, 2]), tensor([0, 1, 2]))
""" """
v = utils.toindex(v) v = utils.toindex(v)
src, dst, eid = self._graph.in_edges(v) src, dst, eid = self._graph.in_edges(v)
...@@ -498,7 +773,7 @@ class DGLGraph(object): ...@@ -498,7 +773,7 @@ class DGLGraph(object):
raise DGLError('Invalid form:', form) raise DGLError('Invalid form:', form)
def out_edges(self, v, form='uv'): def out_edges(self, v, form='uv'):
"""Return the out edges of the node(s). """Return the outbound edges of the node(s).
Parameters Parameters
---------- ----------
...@@ -512,9 +787,35 @@ class DGLGraph(object): ...@@ -512,9 +787,35 @@ class DGLGraph(object):
Returns Returns
------- -------
A tuple of Tensors (u, v, eid) if form == 'all' A tuple of Tensors `(eu, ev, eid)` if `form == 'all'`.
A pair of Tensors (u, v) if form == 'uv' `eid[i]` is the ID of an outbound edge from `eu[i]` from `ev[i]`.
All outbound edges from `v` are returned.
A pair of Tensors (eu, ev) if form == 'uv'
`ev[i]` is the destination node of an outbound edge from `eu[i]`.
All outbound edges from `v` are returned.
One Tensor if form == 'eid' One Tensor if form == 'eid'
`eid[i]` is ID of an outbound edge from any of the nodes in `v`.
Examples
--------
The following example uses PyTorch backend.
>>> G = dgl.DGLGraph()
>>> G.add_nodes(3)
>>> G.add_edges([0, 0, 1], [1, 2, 2]) # (0, 1), (0, 2), (1, 2)
For a single node:
>>> G.out_edges(0)
(tensor([0, 0]), tensor([1, 2]))
>>> G.out_edges(0, 'all')
(tensor([0, 0]), tensor([1, 2]), tensor([0, 1]))
>>> G.out_edges(0, 'eid')
tensor([0, 1])
For multiple nodes:
>>> G.out_edges([0, 1])
(tensor([0, 0, 1]), tensor([1, 2, 2]))
>>> G.out_edges([0, 1], 'all')
(tensor([0, 0, 1]), tensor([1, 2, 2]), tensor([0, 1, 2]))
""" """
v = utils.toindex(v) v = utils.toindex(v)
src, dst, eid = self._graph.out_edges(v) src, dst, eid = self._graph.out_edges(v)
...@@ -543,8 +844,25 @@ class DGLGraph(object): ...@@ -543,8 +844,25 @@ class DGLGraph(object):
Returns Returns
------- -------
A tuple of Tensors (u, v, eid) if form == 'all' A tuple of Tensors (u, v, eid) if form == 'all'
`eid[i]` is the ID of an edge between `u[i]` and `v[i]`.
All edges are returned.
A pair of Tensors (u, v) if form == 'uv' A pair of Tensors (u, v) if form == 'uv'
An edge exists between `u[i]` and `v[i]`.
If `n` edges exist between `u` and `v`, then `u` and `v` as a pair
will appear `n` times.
One Tensor if form == 'eid' One Tensor if form == 'eid'
`eid[i]` is the ID of an edge in the graph.
Examples
--------
The following example uses PyTorch backend.
>>> G = dgl.DGLGraph()
>>> G.add_nodes(3)
>>> G.add_edges([0, 0, 1], [1, 2, 2]) # (0, 1), (0, 2), (1, 2)
>>> G.all_edges()
(tensor([0, 0, 1]), tensor([1, 2, 2]))
>>> G.all_edges('all')
(tensor([0, 0, 1]), tensor([1, 2, 2]), tensor([0, 1, 2]))
""" """
src, dst, eid = self._graph.edges(sorted) src, dst, eid = self._graph.edges(sorted)
if form == 'all': if form == 'all':
...@@ -557,17 +875,25 @@ class DGLGraph(object): ...@@ -557,17 +875,25 @@ class DGLGraph(object):
raise DGLError('Invalid form:', form) raise DGLError('Invalid form:', form)
def in_degree(self, v): def in_degree(self, v):
"""Return the in degree of the node. """Return the in-degree of node `v`.
Parameters Parameters
---------- ----------
v : int v : int
The node. The node ID.
Returns Returns
------- -------
int int
The in degree. The in-degree.
Examples
--------
>>> G = dgl.DGLGraph()
>>> G.add_nodes(3)
>>> G.add_edges([0, 0, 1], [1, 2, 2]) # (0, 1), (0, 2), (1, 2)
>>> G.in_degree(2)
2
See Also See Also
-------- --------
...@@ -576,17 +902,28 @@ class DGLGraph(object): ...@@ -576,17 +902,28 @@ class DGLGraph(object):
return self._graph.in_degree(v) return self._graph.in_degree(v)
def in_degrees(self, v=ALL): def in_degrees(self, v=ALL):
"""Return the in degrees of the nodes. """Return the array `d` of in-degrees of the node array `v`.
`d[i]` is the in-degree of node `v[i]`.
Parameters Parameters
---------- ----------
v : list, tensor v : list, tensor, optional.
The nodes. The node ID array. Default is to return the degrees of all the nodes.
Returns Returns
------- -------
tensor d : tensor
The in degree array. The in-degree array.
Examples
--------
The following example uses PyTorch backend.
>>> G = dgl.DGLGraph()
>>> G.add_nodes(3)
>>> G.add_edges([0, 0, 1], [1, 2, 2]) # (0, 1), (0, 2), (1, 2)
>>> G.in_degrees([1, 2])
tensor([1, 2])
See Also See Also
-------- --------
...@@ -599,17 +936,25 @@ class DGLGraph(object): ...@@ -599,17 +936,25 @@ class DGLGraph(object):
return self._graph.in_degrees(v).tousertensor() return self._graph.in_degrees(v).tousertensor()
def out_degree(self, v): def out_degree(self, v):
"""Return the out degree of the node. """Return the out-degree of node `v`.
Parameters Parameters
---------- ----------
v : int v : int
The node. The node ID.
Returns Returns
------- -------
int int
The out degree. The out-degree.
Examples
--------
>>> G = dgl.DGLGraph()
>>> G.add_nodes(3)
>>> G.add_edges([0, 0, 1], [1, 2, 2]) # (0, 1), (0, 2), (1, 2)
>>> G.out_degree(0)
2
See Also See Also
-------- --------
...@@ -618,17 +963,28 @@ class DGLGraph(object): ...@@ -618,17 +963,28 @@ class DGLGraph(object):
return self._graph.out_degree(v) return self._graph.out_degree(v)
def out_degrees(self, v=ALL): def out_degrees(self, v=ALL):
"""Return the out degrees of the nodes. """Return the array `d` of out-degrees of the node array `v`.
`d[i]` is the out-degree of node `v[i]`.
Parameters Parameters
---------- ----------
v : list, tensor v : list, tensor
The nodes. The node ID array. Default is to return the degrees of all the nodes.
Returns Returns
------- -------
tensor d : tensor
The out degree array. The out-degree array.
Examples
--------
The following example uses PyTorch backend.
>>> G = dgl.DGLGraph()
>>> G.add_nodes(3)
>>> G.add_edges([0, 0, 1], [1, 2, 2]) # (0, 1), (0, 2), (1, 2)
>>> G.out_degrees([0, 1])
tensor([2, 1])
See Also See Also
-------- --------
...@@ -1384,33 +1740,63 @@ class DGLGraph(object): ...@@ -1384,33 +1740,63 @@ class DGLGraph(object):
message_func, reduce_func, apply_node_func) message_func, reduce_func, apply_node_func)
def subgraph(self, nodes): def subgraph(self, nodes):
"""Generate the subgraph among the given nodes. """Return the subgraph induced on given nodes.
Parameters Parameters
---------- ----------
nodes : list, or iterable nodes : list, or iterable
A container of the nodes to construct subgraph. A node ID array to construct subgraph.
All nodes must exist in the graph.
Returns Returns
------- -------
G : DGLSubGraph G : DGLSubGraph
The subgraph. The subgraph.
The nodes are relabeled so that node `i` in the subgraph is mapped
to node `nodes[i]` in the original graph.
The edges are also relabeled.
One can retrieve the mapping from subgraph node/edge ID to parent
node/edge ID via `parent_nid` and `parent_eid` properties of the
subgraph.
Examples
--------
The following example uses PyTorch backend.
>>> G = dgl.DGLGraph()
>>> G.add_nodes(5)
>>> G.add_edges([0, 1, 2, 3, 4], [1, 2, 3, 4, 0]) # 5-node cycle
>>> SG = G.subgraph([0, 1, 4])
>>> SG.nodes()
tensor([0, 1, 2])
>>> SG.edges()
(tensor([0, 2]), tensor([1, 0]))
>>> SG.parent_nid
tensor([0, 1, 4])
>>> SG.parent_eid
tensor([0, 4])
See Also See Also
-------- --------
DGLSubGraph
subgraphs subgraphs
edge_subgraph
""" """
induced_nodes = utils.toindex(nodes) induced_nodes = utils.toindex(nodes)
sgi = self._graph.node_subgraph(induced_nodes) sgi = self._graph.node_subgraph(induced_nodes)
return dgl.DGLSubGraph(self, sgi.induced_nodes, sgi.induced_edges, sgi) return dgl.DGLSubGraph(self, sgi.induced_nodes, sgi.induced_edges, sgi)
def subgraphs(self, nodes): def subgraphs(self, nodes):
"""Generate the subgraphs among the given nodes. """Return a list of subgraphs, each induced in the corresponding given
nodes in the list.
Equivalent to
[self.subgraph(nodes_list) for nodes_list in nodes]
Parameters Parameters
---------- ----------
nodes : a list of lists or iterable nodes : a list of lists or iterable
A list of the nodes to construct subgraph. A list of node ID arrays to construct corresponding subgraphs.
All nodes in all the list items must exist in the graph.
Returns Returns
------- -------
...@@ -1419,6 +1805,7 @@ class DGLGraph(object): ...@@ -1419,6 +1805,7 @@ class DGLGraph(object):
See Also See Also
-------- --------
DGLSubGraph
subgraph subgraph
""" """
induced_nodes = [utils.toindex(n) for n in nodes] induced_nodes = [utils.toindex(n) for n in nodes]
...@@ -1427,64 +1814,50 @@ class DGLGraph(object): ...@@ -1427,64 +1814,50 @@ class DGLGraph(object):
sgi) for sgi in sgis] sgi) for sgi in sgis]
def edge_subgraph(self, edges): def edge_subgraph(self, edges):
"""Generate the subgraph among the given edges. """Return the subgraph induced on given edges.
Parameters Parameters
---------- ----------
edges : list, or iterable edges : list, or iterable
A container of the edges to construct subgraph. An edge ID array to construct subgraph.
All edges must exist in the subgraph.
Returns Returns
------- -------
G : DGLSubGraph G : DGLSubGraph
The subgraph. The subgraph.
The edges are relabeled so that edge `i` in the subgraph is mapped
to edge `edges[i]` in the original graph.
The nodes are also relabeled.
One can retrieve the mapping from subgraph node/edge ID to parent
node/edge ID via `parent_nid` and `parent_eid` properties of the
subgraph.
Examples
--------
The following example uses PyTorch backend.
>>> G = dgl.DGLGraph()
>>> G.add_nodes(5)
>>> G.add_edges([0, 1, 2, 3, 4], [1, 2, 3, 4, 0]) # 5-node cycle
>>> SG = G.edge_subgraph([0, 4])
>>> SG.nodes()
tensor([0, 1, 2])
>>> SG.edges()
(tensor([0, 2]), tensor([1, 0]))
>>> SG.parent_nid
tensor([0, 1, 4])
>>> SG.parent_eid
tensor([0, 4])
See Also
--------
DGLSubGraph
subgraph
""" """
induced_edges = utils.toindex(edges) induced_edges = utils.toindex(edges)
sgi = self._graph.edge_subgraph(induced_edges) sgi = self._graph.edge_subgraph(induced_edges)
return dgl.DGLSubGraph(self, sgi.induced_nodes, sgi.induced_edges, sgi) return dgl.DGLSubGraph(self, sgi.induced_nodes, sgi.induced_edges, sgi)
def merge(self, subgraphs, reduce_func='sum'):
"""Merge subgraph features back to this parent graph.
Parameters
----------
subgraphs : iterator of DGLSubGraph
The subgraphs to be merged.
reduce_func : str
The reduce function (only 'sum' is supported currently)
"""
# sanity check: all the subgraphs and the parent graph
# should have the same node/edge feature schemes.
# merge node features
to_merge = []
for sg in subgraphs:
if len(sg.node_attr_schemes()) == 0:
continue
if sg.node_attr_schemes() != self.node_attr_schemes():
raise RuntimeError('Subgraph and parent graph do not '
'have the same node attribute schemes.')
to_merge.append(sg)
self._node_frame = merge_frames(
[sg._node_frame for sg in to_merge],
[sg._parent_nid for sg in to_merge],
self._node_frame.num_rows,
reduce_func)
# merge edge features
to_merge.clear()
for sg in subgraphs:
if len(sg.edge_attr_schemes()) == 0:
continue
if sg.edge_attr_schemes() != self.edge_attr_schemes():
raise RuntimeError('Subgraph and parent graph do not '
'have the same edge attribute schemes.')
to_merge.append(sg)
self._edge_frame = merge_frames(
[sg._edge_frame for sg in to_merge],
[sg._parent_eid for sg in to_merge],
self._edge_frame.num_rows,
reduce_func)
def adjacency_matrix(self, transpose=False, ctx=F.cpu()): def adjacency_matrix(self, transpose=False, ctx=F.cpu()):
"""Return the adjacency matrix representation of this graph. """Return the adjacency matrix representation of this graph.
......
...@@ -29,7 +29,7 @@ class DGLSubGraph(DGLGraph): ...@@ -29,7 +29,7 @@ class DGLSubGraph(DGLGraph):
The "shared" mode is currently not supported. The "shared" mode is currently not supported.
The subgraph is read-only so mutation is not allowed. The subgraph is read-only on structure; graph mutation is not allowed.
Parameters Parameters
---------- ----------
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment