import networkx as nx
Another tutorial is available here.
Use the class MultiDiGraph
to create a directed graph with multiple edges and self-loops.
G = nx.DiGraph() # Directed
G = nx.MultiDiGraph()
Add nodes using add_node
:
G.add_node('node1')
G.add_node('node2')
G.add_node('node3')
Check that a node is in the graph:
assert 'node1' in G
Get all nodes:
list(G.nodes())
Add some edges:
G.add_edge('node1', 'node2');
Nodes get automatically added:
G.add_edge('node2', 'another1');
G.add_edge('another1', 'another2');
List edges:
list(G.edges()) # list of 2-tuples (from, to)
for a,b in G.edges():
print('edge from %s to %s' % (a, b))
There are some minimal plotting capabilities.
def draw_graph(G0, pos=None):
from matplotlib import pyplot as plt
pos = pos or nx.spring_layout(G0)
plt.figure(figsize=(12, 12))
nx.draw(G0,pos,labels={node:node for node in G0.nodes()})
def edge_label(a, b):
datas = G0.get_edge_data(a, b)
s = '%d edge%s' % (len(datas), 's' if len(datas)>=2 else '')
for k, v in datas.items():
if v:
if 'label' in v:
s += '\n %s' % v['label']
else:
s += '\n %s' %v
return s
edge_labels = dict([ ((a,b), edge_label(a,b)) for a,b in G0.edges()])
nx.draw_networkx_edge_labels(G0,pos,edge_labels=edge_labels,font_color='red')
plt.axis('off')
plt.show()
draw_graph(G)
You can add a second edge between the nodes:
G.add_edge('node1', 'node2');
Notice now how there are two copies of the same edge:
list(G.edges())
Multiple edges in the graph:
draw_graph(G)
You can use the optional parameters of the add_edge
method to attach some pieces of data.
For example we add two more edges between node1
and node4
with property prop
set to 12 and 15.
G.add_edge('node1', 'node4', prop=12);
G.add_edge('node1', 'node4', prop=15);
draw_graph(G)
Using the function get_edge_data
we can get the data attached to the edge.
The function returns a dictionary 'edge-label -> properties', where edge-label
is just an integer.
data = G.get_edge_data('node1', 'node4');
print(data)
for id_edge, edge_data in data.items():
print('edge %s: attribute prop = %s' % (id_edge, edge_data['prop']))
Concepts:
list(G.predecessors('node2'))
list(nx.ancestors(G, 'another2'))
list(G.successors('node2'))
list(nx.descendants(G, 'node2'))
Use the function nx.simple_cycles
to get the cycles in the graph:
list(nx.simple_cycles(G))
Let's create a more interesting graph:
G2 = nx.MultiDiGraph()
edges = [
('A', 'B'),
('B', 'C'),
('C', 'D'),
('D', 'E'),
('E', 'F'),
('F', 'A'),
('A', 'G'),
('G', 'H'),
('H', 'A')]
G2.add_edges_from(edges);
draw_graph(G2)
The function returns a list of lists:
list(nx.simple_cycles(G2))
Use has_path
to check that two nodes are connected:
nx.has_path(G2, 'A', 'C')
nx.has_path(G2, 'C', 'A')
But what are these paths? Use nx.shortest_path
to find out:
nx.shortest_path(G2, 'A', 'C')
nx.shortest_path(G2, 'C', 'A')
Use kwargs of add_node
to add attributes to nodes:
M = nx.DiGraph()
M.add_node('a', q=2)
Get it back using this syntax:
M.nodes['a']
We create a pose network: a graph where each node represents a pose and each edge is a measurement.
Let's create a grid-like network:
H, W = 4, 4
grid_size = 0.61
import itertools
import geometry as geo
M = nx.MultiDiGraph()
for i, j in itertools.product(range(H), range(W)):
# node name is a tuple
node_name = (i, j)
# create a pose
q = geo.SE2_from_translation_angle((i*grid_size, j*grid_size), 0)
M.add_node(node_name, q=q) # q as property
# let's plot them where they are supposed to go
def position_for_node(node):
# node is a tuple (i,j)
# query the node properties
properties = M.nodes[node]
# get the pose set before
q = properties['q']
# extract the translation
t, _ = geo.translation_angle_from_SE2(q)
# that's my position
return t
pos = dict([(node, position_for_node(node)) for node in M])
draw_graph(M, pos=pos)
Now let's create the network connections:
geo.SE2.friendly(geo.SE2.identity())
for i, j in itertools.product(range(H), range(W)):
# for each node
# connect to neighbors
for d in [ (+1, 0), (0, +1), (+1,+1)]:
# neighbors coordinatex
i2, j2 = i+d[0], j+d[1]
# if neighbor exists
if (i2,j2) in M:
# add the connection
# pose of the first node
q1 = M.nodes[(i,j)]['q']
# pose of the second node
q2 = M.nodes[(i2,j2)]['q']
# relative pose
relative_pose = geo.SE2.multiply(geo.SE2.inverse(q1), q2)
# label
label = geo.SE2.friendly(relative_pose)
# add the edge with two properties "label" and "relative_pose"
M.add_edge( (i,j), (i2,j2), label=label, relative_pose=relative_pose)
draw_graph(M, pos=pos)
Now let's find the relative position between two nodes using the graph functions.
# let's add inverse edges
for node1, node2 in M.edges():
for id_edge, edge_data in M.get_edge_data(node1, node2).items():
r = edge_data['relative_pose']
rinv = geo.SE2.inverse(r)
label = geo.SE2.friendly(rinv)
M.add_edge(node2, node1, relative_pose=rinv, label=label)
draw_graph(M, pos=pos)
node1, node2 = (1,3), (2,1)
path = nx.shortest_path(M, node1, node2)
print(path)
Get the edges from this sequence of nodes:
zip(path[1:], path[:-1])
edges = zip(path[1:],path[:-1])
for a, b in edges:
print('edge from %s to %s' % (a,b ))
We can recover the relative pose using get_edge_data
:
deltas = []
for a, b in edges:
R = M.get_edge_data(a, b)[0]['relative_pose']
deltas.append(R)
print('edge %s to %s: relative pose: %s' % (a, b, geo.SE2.friendly(R)))
print(deltas)
def multiply_deltas(G, deltas):
S = geo.SE2.identity()
for R in deltas:
S = geo.SE2.multiply(S, R) # multiply on the right
return S
S = multiply_deltas(M, deltas)
print(geo.SE2.friendly(S))