DGLGraph – Graph with node/edge features

class dgl.DGLGraph(graph_data=None, node_frame=None, edge_frame=None, multigraph=False, readonly=False)[source]

Base graph class.

The graph stores nodes, edges and also their features.

DGL graph is always directional. Undirected graph can be represented using two bi-directional edges.

Nodes are identified by consecutive integers starting from zero.

Edges can be specified by two end points (u, v) or the integer id assigned 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 to the feature data (in tensor).

Parameters:
  • graph_data (graph data, optional) – Data to initialize graph. Same as networkx’s semantics.
  • node_frame (FrameRef, optional) – Node feature storage.
  • edge_frame (FrameRef, optional) – Edge feature storage.
  • multigraph (bool, optional) – Whether the graph would be a multigraph (default: False)
  • readonly (bool, optional) – Whether the graph structure is read-only (default: False).

Examples

Create an empty graph with no nodes and edges.

>>> G = dgl.DGLGraph()

G can be grown in several ways.

Nodes:

Add N nodes:

>>> G.add_nodes(10)  # 10 isolated nodes are added

Edges:

Add one edge at a time,

>>> G.add_edge(0, 1)

or multiple edges,

>>> G.add_edges([1, 2, 3], [3, 4, 5])  # three edges: 1->3, 2->4, 3->5

or multiple edges starting from the same node,

>>> G.add_edges(4, [7, 8, 9])  # three edges: 4->7, 4->8, 4->9

or multiple edges pointing to the same node,

>>> G.add_edges([2, 6, 8], 5)  # three edges: 2->5, 6->5, 8->5

or multiple edges using tensor type

Note

Here we use pytorch syntax for demo. The general idea applies to other frameworks with minor syntax change (e.g. replace torch.tensor with mxnet.ndarray).

>>> import torch as th
>>> G.add_edges(th.tensor([3, 4, 5]), 1)  # three edges: 3->1, 4->1, 5->1

NOTE: Removing nodes and edges is not supported by DGLGraph.

Features:

Both nodes and edges can have feature data. Features are stored as key/value pair. The key must be hashable while the value must be tensor type. Features are batched on the first dimension.

Use G.ndata to get/set features for all nodes.

>>> G = dgl.DGLGraph()
>>> G.add_nodes(3)
>>> G.ndata['x'] = th.zeros((3, 5))  # init 3 nodes with zero vector(len=5)
>>> G.ndata
{'x' : tensor([[0., 0., 0., 0., 0.],
               [0., 0., 0., 0., 0.],
               [0., 0., 0., 0., 0.]])}

Use G.nodes to get/set features for some nodes.

>>> G.nodes[[0, 2]].data['x'] = th.ones((2, 5))
>>> G.ndata
{'x' : tensor([[1., 1., 1., 1., 1.],
               [0., 0., 0., 0., 0.],
               [1., 1., 1., 1., 1.]])}

Similarly, use G.edata and G.edges to get/set features for edges.

>>> G.add_edges([0, 1], 2)  # 0->2, 1->2
>>> G.edata['y'] = th.zeros((2, 4))  # init 2 edges with zero vector(len=4)
>>> G.edata
{'y' : tensor([[0., 0., 0., 0.],
               [0., 0., 0., 0.]])}
>>> G.edges[1, 2].data['y'] = th.ones((1, 4))
>>> G.edata
{'y' : tensor([[0., 0., 0., 0.],
               [1., 1., 1., 1.]])}

Note that each edge is assigned a unique id equal to its adding order. So edge 1->2 has id=1. DGL supports directly use edge id to access edge features.

>>> G.edges[0].data['y'] += 2.
>>> G.edata
{'y' : tensor([[2., 2., 2., 2.],
               [1., 1., 1., 1.]])}

Message Passing:

One common operation for updating node features is message passing, where the source nodes send messages through edges to the destinations. With DGLGraph, we can do this with send() and recv().

In the example below, the source nodes add 1 to their node features as the messages and send the messages to the destinations.

>>> # Define the function for sending messages.
>>> def send_source(edges): return {'m': edges.src['x'] + 1}
>>> # Set the function defined to be the default message function.
>>> G.register_message_func(send_source)
>>> # Send messages through all edges.
>>> G.send(G.edges())

Just like you need to go to your mailbox for retrieving mails, the destination nodes also need to receive the messages and potentially update their features.

>>> # Define a function for summing messages received and replacing the original feature.
>>> def simple_reduce(nodes): return {'x': nodes.mailbox['m'].sum(1)}
>>> # Set the function defined to be the default message reduce function.
>>> G.register_reduce_func(simple_reduce)
>>> # All existing edges have node 2 as the destination.
>>> # Receive the messages for node 2 and update its feature.
>>> G.recv(v=2)
>>> G.ndata
{'x': tensor([[1., 1., 1., 1., 1.],
              [0., 0., 0., 0., 0.],
              [3., 3., 3., 3., 3.]])} # 3 = (1 + 1) + (0 + 1)

For more examples about message passing, please read our tutorials.

Adding nodes and edges

DGLGraph.add_nodes(num[, data]) Add multiple new nodes.
DGLGraph.add_edge(u, v[, data]) Add one new edge between u and v.
DGLGraph.add_edges(u, v[, data]) Add multiple edges for list of source nodes u and destination nodes v.
DGLGraph.clear() Remove all nodes and edges, as well as their features, from the graph.

Querying graph structure

DGLGraph.number_of_nodes() Return the number of nodes in the graph.
DGLGraph.number_of_edges() Return the number of edges in the graph.
DGLGraph.__len__() Return the number of nodes in the graph.
DGLGraph.is_multigraph True if the graph is a multigraph, False otherwise.
DGLGraph.has_node(vid) Return True if the graph contains node vid.
DGLGraph.has_nodes(vids) Return a 0-1 array a given the node ID array vids.
DGLGraph.__contains__(vid) Return True if the graph contains node vid.
DGLGraph.has_edge_between(u, v) Return True if the edge (u, v) is in the graph.
DGLGraph.has_edges_between(u, v) Return a 0-1 array a given the source node ID array u and destination node ID array v.
DGLGraph.predecessors(v) Return the predecessors of node v in the graph.
DGLGraph.successors(v) Return the successors of node v in the graph.
DGLGraph.edge_id(u, v[, force_multi]) Return the edge ID, or an array of edge IDs, between source node u and destination node v.
DGLGraph.edge_ids(u, v[, force_multi]) Return all edge IDs between source node array u and destination node array v.
DGLGraph.find_edges(eid) Given an edge ID array, return the source and destination node ID array s and d.
DGLGraph.in_edges(v[, form]) Return the inbound edges of the node(s).
DGLGraph.out_edges(v[, form]) Return the outbound edges of the node(s).
DGLGraph.all_edges([form, order]) Return all the edges.
DGLGraph.in_degree(v) Return the in-degree of node v.
DGLGraph.in_degrees([v]) Return the array d of in-degrees of the node array v.
DGLGraph.out_degree(v) Return the out-degree of node v.
DGLGraph.out_degrees([v]) Return the array d of out-degrees of the node array v.

Transforming graph

DGLGraph.subgraph(nodes) Return the subgraph induced on given nodes.
DGLGraph.subgraphs(nodes) Return a list of subgraphs, each induced in the corresponding given nodes in the list.
DGLGraph.edge_subgraph(edges) Return the subgraph induced on given edges.
DGLGraph.line_graph([backtracking, shared]) Return the line graph of this graph.
DGLGraph.reverse([share_ndata, share_edata]) Return the reverse of this graph.
DGLGraph.readonly([readonly_state]) Set this graph’s readonly state in-place.

Converting from/to other format

DGLGraph.to_networkx([node_attrs, edge_attrs]) Convert to networkx graph.
DGLGraph.from_networkx(nx_graph[, …]) Convert from networkx graph.
DGLGraph.from_scipy_sparse_matrix(spmat) Convert from scipy sparse matrix.
DGLGraph.adjacency_matrix([transpose, ctx]) Return the adjacency matrix representation of this graph.
DGLGraph.adjacency_matrix_scipy([transpose, fmt]) Return the scipy adjacency matrix representation of this graph.
DGLGraph.incidence_matrix(typestr[, ctx]) Return the incidence matrix representation of this graph.

Using Node/edge features

DGLGraph.nodes Return a node view that can used to set/get feature data.
DGLGraph.edges Return a edges view that can used to set/get feature data.
DGLGraph.ndata Return the data view of all the nodes.
DGLGraph.edata Return the data view of all the edges.
DGLGraph.node_attr_schemes() Return the node feature schemes.
DGLGraph.edge_attr_schemes() Return the edge feature schemes.
DGLGraph.set_n_initializer(initializer[, field]) Set the initializer for empty node features.
DGLGraph.set_e_initializer(initializer[, field]) Set the initializer for empty edge features.

Computing with DGLGraph

DGLGraph.register_message_func(func) Register global message function.
DGLGraph.register_reduce_func(func) Register global message reduce function.
DGLGraph.register_apply_node_func(func) Register global node apply function.
DGLGraph.register_apply_edge_func(func) Register global edge apply function.
DGLGraph.apply_nodes([func, v, inplace]) Apply the function on the nodes to update their features.
DGLGraph.apply_edges([func, edges, inplace]) Apply the function on the edges to update their features.
DGLGraph.group_apply_edges(group_by, func[, …]) Group the edges by nodes and apply the function on the grouped edges to
DGLGraph.send([edges, message_func]) Send messages along the given edges.
DGLGraph.recv([v, reduce_func, …]) Receive and reduce incoming messages and update the features of node(s) \(v\).
DGLGraph.send_and_recv(edges[, …]) Send messages along edges and let destinations receive them.
DGLGraph.pull(v[, message_func, …]) Pull messages from the node(s)’ predecessors and then update their features.
DGLGraph.push(u[, message_func, …]) Send message from the node(s) to their successors and update them.
DGLGraph.update_all([message_func, …]) Send messages through all edges and update all nodes.
DGLGraph.prop_nodes(nodes_generator[, …]) Propagate messages using graph traversal by triggering pull() on nodes.
DGLGraph.prop_edges(edges_generator[, …]) Propagate messages using graph traversal by triggering send_and_recv() on edges.
DGLGraph.filter_nodes(predicate[, nodes]) Return a tensor of node IDs that satisfy the given predicate.
DGLGraph.filter_edges(predicate[, edges]) Return a tensor of edge IDs that satisfy the given predicate.