2_basics.py 5.55 KB
Newer Older
1
"""
Minjie Wang's avatar
Minjie Wang committed
2
3
.. currentmodule:: dgl

4
5
6
DGL Basics
==========

Minjie Wang's avatar
Minjie Wang committed
7
8
**Author**: `Minjie Wang <https://jermainewang.github.io/>`_, Quan Gan, Yu Gai,
Zheng Zhang
9
10
11
12
13
14
15
16
17
18

The Goal of this tutorial:

* To create a graph.
* To read and write node and edge representations.
"""

###############################################################################
# Graph Creation
# --------------
Minjie Wang's avatar
Minjie Wang committed
19
20
# The design of :class:`DGLGraph` was influenced by other graph libraries. Indeed,
# you can create a graph from networkx, and convert it into a :class:`DGLGraph` and
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# vice versa:

import networkx as nx
import dgl

g_nx = nx.petersen_graph()
g_dgl = dgl.DGLGraph(g_nx)

import matplotlib.pyplot as plt
plt.subplot(121)
nx.draw(g_nx, with_labels=True)
plt.subplot(122)
nx.draw(g_dgl.to_networkx(), with_labels=True)

plt.show()


###############################################################################
Minjie Wang's avatar
Minjie Wang committed
39
# They are the same graph, except that :class:`DGLGraph` is *always* directional.
40
41
42
#
# One can also create a graph by calling DGL's own interface.
# 
Minjie Wang's avatar
Minjie Wang committed
43
44
45
46
# Now let's build a star graph. :class:`DGLGraph` nodes are consecutive range of
# integers between 0 and :func:`number_of_nodes() <DGLGraph.number_of_nodes>`
# and can grow by calling :func:`add_nodes <DGLGraph.add_nodes>`.
# :class:`DGLGraph` edges are in order of their additions. Note that
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# edges are accessed in much the same way as nodes, with one extra feature
# of *edge broadcasting*:

import dgl
import torch as th

g = dgl.DGLGraph()
g.add_nodes(10)
# a couple edges one-by-one
for i in range(1, 4):
    g.add_edge(i, 0)
# a few more with a paired list
src = list(range(5, 8)); dst = [0]*3
g.add_edges(src, dst)
# finish with a pair of tensors
src = th.tensor([8, 9]); dst = th.tensor([0, 0])
g.add_edges(src, dst)

# edge broadcasting will do star graph in one go!
g.clear(); g.add_nodes(10)
src = th.tensor(list(range(1, 10)));
g.add_edges(src, 0)

import networkx as nx
import matplotlib.pyplot as plt
nx.draw(g.to_networkx(), with_labels=True)
plt.show()


###############################################################################
# Feature Assignment
# ------------------
Minjie Wang's avatar
Minjie Wang committed
79
# One can also assign features to nodes and edges of a :class:`DGLGraph`.  The
80
81
82
# features are represented as dictionary of names (strings) and tensors,
# called **fields**.
#
Minjie Wang's avatar
Minjie Wang committed
83
# The following code snippet assigns each node a vector (len=3).
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#
# .. note::
#
#    DGL aims to be framework-agnostic, and currently it supports PyTorch and
#    MXNet tensors. From now on, we use PyTorch as an example.

import dgl
import torch as th

x = th.randn(10, 3)
g.ndata['x'] = x


###############################################################################
Minjie Wang's avatar
Minjie Wang committed
98
99
100
# :func:`ndata <DGLGraph.ndata>` is a syntax sugar to access states of all nodes,
# states are stored
# in a container ``data`` that hosts user defined dictionary.
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145

print(g.ndata['x'] == g.nodes[:].data['x'])

# access node set with integer, list, or integer tensor
g.nodes[0].data['x'] = th.zeros(1, 3)
g.nodes[[0, 1, 2]].data['x'] = th.zeros(3, 3)
g.nodes[th.tensor([0, 1, 2])].data['x'] = th.zeros(3, 3)


###############################################################################
# Assigning edge features is in a similar fashion to that of node features,
# except that one can also do it by specifying endpoints of the edges.

g.edata['w'] = th.randn(9, 2)

# access edge set with IDs in integer, list, or integer tensor
g.edges[1].data['w'] = th.randn(1, 2)
g.edges[[0, 1, 2]].data['w'] = th.zeros(3, 2)
g.edges[th.tensor([0, 1, 2])].data['w'] = th.zeros(3, 2)

# one can also access the edges by giving endpoints
g.edges[1, 0].data['w'] = th.ones(1, 2)                 # edge 1 -> 0
g.edges[[1, 2, 3], [0, 0, 0]].data['w'] = th.ones(3, 2) # edges [1, 2, 3] -> 0


###############################################################################
# After assignments, each node/edge field will be associated with a scheme
# containing the shape and data type (dtype) of its field value.

print(g.node_attr_schemes())
g.ndata['x'] = th.zeros((10, 4))
print(g.node_attr_schemes())


###############################################################################
# One can also remove node/edge states from the graph. This is particularly
# useful to save memory during inference.

g.ndata.pop('x')
g.edata.pop('w')


###############################################################################
# Multigraphs
# ~~~~~~~~~~~
Minjie Wang's avatar
Minjie Wang committed
146
# Many graph applications need multi-edges. To enable this, construct :class:`DGLGraph`
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# with ``multigraph=True``.

g_multi = dgl.DGLGraph(multigraph=True)
g_multi.add_nodes(10)
g_multi.ndata['x'] = th.randn(10, 2)

g_multi.add_edges(list(range(1, 10)), 0)
g_multi.add_edge(1, 0) # two edges on 1->0

g_multi.edata['w'] = th.randn(10, 2)
g_multi.edges[1].data['w'] = th.zeros(1, 2)
print(g_multi.edges())


###############################################################################
# An edge in multi-graph cannot be uniquely identified using its incident nodes
# :math:`u` and :math:`v`; query their edge ids use ``edge_id`` interface.

eid_10 = g_multi.edge_id(1, 0)
g_multi.edges[eid_10].data['w'] = th.ones(len(eid_10), 2)
print(g_multi.edata['w'])


###############################################################################
# .. note::
#
#    * Nodes and edges can be added but not removed; we will support removal in
#      the future.
#    * Updating a feature of different schemes raise error on indivdual node (or
#      node subset).


###############################################################################
# Next steps
# ----------
# In the :doc:`next tutorial <3_pagerank>`, we will go through the
# DGL message passing interface by implementing PageRank.