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.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())
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.
G.get_edge_data('node1', 'node4')
for id_edge, edge_data in G.get_edge_data('node1', 'node4').items():
print('edge %s: attribute prop = %s' % (id_edge, edge_data['prop']))
Concepts:
list(G.neighbors('node2'))
list(G.predecessors('node2'))
list(nx.ancestors(G, 'node2'))
list(nx.descendants(G, 'node2'))
list(G.successors('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
import itertools
import geometry as geo
M = nx.MultiDiGraph()
for i, j in itertools.product(range(H), range(W)):
node_name = (i, j)
q = geo.SE2_from_translation_angle((i, j), 0)
M.add_node(node_name, q=q)
# let's plot them where they are supposed to go
def position_for_node(node):
q = M.nodes[node]['q']
t, _ = geo.translation_angle_from_SE2(q)
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:
for i, j in itertools.product(range(H), range(W)):
# connect to neighbors
for d in [(-1,0),(+1, 0),(0, +1), (0,-1)]:
i2, j2 = i+d[0], j+d[1]
# if neighbor exists
if (i2,j2) in M:
q1 = M.nodes[(i,j)]['q']
q2 = M.nodes[(i2,j2)]['q']
relative_pose = geo.SE2.multiply(geo.SE2.inverse(q1), q2)
label = geo.SE2.friendly(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.
node1, node2 = (1,3), (2,1)
path = nx.shortest_path(M, node1, node2)
print(path)
Get the edges from this sequence of nodes:
edges = zip(path[1:],path[:-1])
print(edges)
We can recover the relative pose using get_edge_data
:
for a, b in edges:
R = M.get_edge_data(a, b)[0]['relative_pose']
print('edge %s to %s: relative pose: %s' % (a, b, geo.SE2.friendly(R)))
def sum_pose_along_edges(G, edges):
S = geo.SE2.identity()
for a, b in edges:
R = G.get_edge_data(a, b)[0]['relative_pose']
S = geo.SE2.multiply(S, R)
return S
S = sum_pose_along_edges(M, edges)
print(geo.SE2.friendly(S))