Reputation: 31
I am trying to create a MultiDiGraph in NetworkX that can be automatically adjusted through other entries for gates, depots, connecting points or parking spots.
My problem is that the nodes and edges are created, but I always get this runtime warning and I do not know how to fix it and what it is with this two expected doubles.
MultiDiGraph with 10 nodes and 20 edges
RuntimeWarning: Error: node g1, position (1, 30), expected two doubles
Error: node ni1, position (1, 20), expected two doubles
Error: node bd1, position (3, 30), expected two doubles
Error: node ni2, position (2, 20), expected two doubles
Error: node ni5, position (5, 20), expected two doubles
Error: node p1, position (1, 10), expected two doubles
Error: node p2, position (2, 10), expected two doubles
Error: node ni4, position (4, 20), expected two doubles
Error: node ni3, position (3, 20), expected two doubles
Error: node g2, position (2, 30), expected two doubles
Another problem is the '-2' when I add the 'bd' node because it is manually inserted and I wish to do it with '-i' but then it just creates one more 'bd' node for this example than needed.
Maybe you also have first ideas how to build automatic connections between the created points. I am open to any suggestions how to fix this points. Thank you very much!
G = nx.MultiDiGraph()
Gates = 2
Depots = 1
ConnectingNodes = 5
Parking = 2
for i in range(1, Gates+1, 1):
for j in range(Gates+1, Gates+Depots+1, 1):
for m in range(1, ConnectingNodes+1, 1):
for n in range(1, Parking+1, 1):
G.add_node('g'+str(i), pos=(i, 30), color='turquoise', type='gates')
G.add_node('bd'+str(j-2), pos=(j, 30), color='green', type='depots') #fix -2
G.add_node('ni'+str(m), pos=(m, 20), color='blue')
G.add_node('p'+str(n), pos=(n, 10), color='red', type='parking')
G.add_edge('g1', 'ni1')
G.add_edge('ni1', 'g1')
G.add_edge('ni1', 'ni2')
G.add_edge('ni2', 'ni1')
G.add_edge('bd1', 'ni2')
G.add_edge('ni2', 'bd1')
G.add_edge('ni2', 'ni3')
G.add_edge('ni3', 'ni2')
G.add_edge('ni5', 'ni4')
G.add_edge('ni4', 'ni5')
G.add_edge('p1', 'ni5')
G.add_edge('ni5', 'p1')
G.add_edge('ni4', 'p2')
G.add_edge('p2', 'ni4')
G.add_edge('g2', 'ni3')
G.add_edge('ni3', 'g2')
G.add_edge('ni1', 'ni5')
G.add_edge('ni5', 'ni1')
G.add_edge('ni3', 'ni4')
G.add_edge('ni4', 'ni3')
print(G)
K = nx.nx_agraph.to_agraph(G)
K.layout('neato')
K.draw('Test_basic.png')
plt.show()
Upvotes: 2
Views: 350
Reputation: 949
This error RuntimeWarning: Error: node g1, position (1, 30), expected two doubles
is due to the fact that after the execution of the string K = nx.nx_agraph.to_agraph(G)
, the position of the node in the object is written as "(x,y)"
, then the call K.draw('Test_basic.png')
which requires the graph to be drawn with pygraphviz
according to the DOT language, and in the DOT language the position of node is written as (x,y)
or (x,y!)
. Format mismatch causes a conflict.
This can be corrected by changing the position entry in the graph object before converting to AGraph object:
for node in G:
G.nodes[node]['pos'] = "{},{}!".format(
G.nodes[node]['pos'][0], G.nodes[node]['pos'][1])
Full example:
import networkx as nx
import matplotlib.pyplot as plt
G = nx.MultiDiGraph()
Gates = 2
Depots = 1
ConnectingNodes = 5
Parking = 2
for i in range(1, Gates+1, 1):
for j in range(Gates+1, Gates+Depots+1, 1):
for m in range(1, ConnectingNodes+1, 1):
for n in range(1, Parking+1, 1):
G.add_node('g'+str(i), pos=(i, 30), color='turquoise', type='gates')
G.add_node('bd'+str(j-2), pos=(j, 30), color='green', type='depots') #fix -2
G.add_node('ni'+str(m), pos=(m, 20), color='blue')
G.add_node('p'+str(n), pos=(n, 10), color='red', type='parking')
G.add_edge('g1', 'ni1')
G.add_edge('ni1', 'g1')
G.add_edge('ni1', 'ni2')
G.add_edge('ni2', 'ni1')
G.add_edge('bd1', 'ni2')
G.add_edge('ni2', 'bd1')
G.add_edge('ni2', 'ni3')
G.add_edge('ni3', 'ni2')
G.add_edge('ni5', 'ni4')
G.add_edge('ni4', 'ni5')
G.add_edge('p1', 'ni5')
G.add_edge('ni5', 'p1')
G.add_edge('ni4', 'p2')
G.add_edge('p2', 'ni4')
G.add_edge('g2', 'ni3')
G.add_edge('ni3', 'g2')
G.add_edge('ni1', 'ni5')
G.add_edge('ni5', 'ni1')
G.add_edge('ni3', 'ni4')
G.add_edge('ni4', 'ni3')
print(G)
for node in G:
G.nodes[node]['pos'] = "{},{}!".format(
G.nodes[node]['pos'][0], G.nodes[node]['pos'][1])
K = nx.nx_agraph.to_agraph(G)
K.layout('neato')
K.draw('Test_basic.png')
plt.show()
Upvotes: 0
Reputation: 31
Thank you guys for your comments! It helped me to improve my code.
This is my updated version:
Gates = 2
Depots = 2
ConnectingNodes = 6
Parking = 2
G = nx.MultiDiGraph()
for i in range(1, Gates+1, 1):
G.add_node('g' + str(i), pos=(i, 30), color='turquoise', type='gates')
for j in range(1, Depots+1, 1):
G.add_node('bd' + str(j), pos=(j+Gates, 30), color='green', type='depots')
for m in range(1, ConnectingNodes+1, 1):
G.add_node('ni' + str(m), pos=(m, 20), color='blue')
for n in range(1, Parking+1, 1):
G.add_node('p' + str(n), pos=(n+Gates+Depots, 10), color='red', type='parking')
for i in range(1, Gates+1, 1):
G.add_edge('ni' + str(i), 'g' + str(i))
G.add_edge('g' + str(i), 'ni' + str(i))
for j in range(1, Depots+1, 1):
G.add_edge('ni' + str(j + Gates), 'bd' + str(j))
G.add_edge('bd' + str(j), 'ni' + str(j + Gates))
for z in range(1, Parkping+1, 1):
G.add_edge('ni' + str(z + Gates + Depots), 'p' + str(z))
G.add_edge('p' + str(z), 'ni' + str(z + Gates + Depots))
for r in range(1, ConnectingNodes, 1):
G.add_edge('ni' + str(r), 'ni' + str(r+1))
G.add_edge('ni' + str(r+1), 'ni' + str(r))
print(G)
for all in G.edges():
weight = 200
#positions and colors from nodes for drawing
pos = {node: G.nodes[node]['pos'] for node in G}
colors = [G.nodes[node]['color'] for node in G]
nx.draw(G, pos, node_size=400, node_color=colors, with_labels=True)
plt.show()
Important is that the amount of Connecting Nodes is bigger than Gates+Depots+Parking Positions. I fixed this in my extended code with a 'while not' condition when reading the input.
There is also no condition between the amount of depots needed for a gate or such a thing. It should be a flexible structure that can be adjusted by the needs of the client.
Maybe there is an easier way to create edges but this works quite good.
So now I can move on with my project and create different sized instances thanks!
Upvotes: 0
Reputation: 1888
Your question did not make clear whether the network needed to include one depot per gate (and 5 connecting nodes and 2 parking lots per gate), or just one of each. I couldn't figure it out from the code in the question either, because the nested for loops seem to indicate one depot per gate, but the resulting graph just created each gate, depot, etc. many times.
I assumed you really wanted 1 depot, and the nested for loops were
just a mistake. I changed the logic at the end to use standard
networkx
drawing, instead of graphviz
. This is just a choice,
but I thought it made sense to make that simplification; it also
takes care of the warning messages about doubles, since nx.draw
converts effortless between integer positions and floats (or
doubles).
So how to add edges in some automatic way? Well, it seems that all edges are bi-directional, so you could "save" quite some code by first listing the pairs, then creating edges in both directions.
There might be some more that can be done, but I do not have enough information about the underlying rules of the graph. It seems that each node connects to one and only one connecting node, and all connecting nodes connect to their numeric neighbors. If that's the case, then it would be fairly easy to write a routine that does that.
import networkx as nx
import matplotlib.pyplot as plt
Gates = 2
Depots = 1
ConnectingNodes = 5
Parking = 2
G = nx.MultiDiGraph()
for i in range(1, Gates+1, 1):
G.add_node('g'+str(i), pos=(i, 40), color='turquoise', type='gates')
for j in range(1, Depots+1, 1):
G.add_node('bd'+str(j), pos=(j+0.5, 30), color='green', type='depots')
for m in range(1, ConnectingNodes+1, 1):
G.add_node('ni'+str(m), pos=(m, 20), color='blue')
for n in range(1, Parking+1, 1):
G.add_node('p'+str(n), pos=(n, 10), color='red', type='parking')
G.add_edge('g1', 'ni1')
G.add_edge('ni1', 'g1')
G.add_edge('ni1', 'ni2')
G.add_edge('ni2', 'ni1')
G.add_edge('bd1', 'ni2')
G.add_edge('ni2', 'bd1')
G.add_edge('ni2', 'ni3')
G.add_edge('ni3', 'ni2')
G.add_edge('ni5', 'ni4')
G.add_edge('ni4', 'ni5')
G.add_edge('p1', 'ni5')
G.add_edge('ni5', 'p1')
G.add_edge('ni4', 'p2')
G.add_edge('p2', 'ni4')
G.add_edge('g2', 'ni3')
G.add_edge('ni3', 'g2')
G.add_edge('ni1', 'ni5')
G.add_edge('ni5', 'ni1')
G.add_edge('ni3', 'ni4')
G.add_edge('ni4', 'ni3')
# Extract positions and colors from nodes for drawing
pos = {node: G.nodes[node]['pos'] for node in G}
colors = [G.nodes[node]['color'] for node in G]
nx.draw(G, pos, node_size=400, node_color=colors, with_labels=True)
plt.show()
The resulting drawing of the graph looks like this:
Upvotes: 2