Generating a specific adjacency matrix in Python - python

I have a network with nodes and vertices and the following numbering scheme. I want to generate an adjacency matrix A for the nodes 0,1 as shown below. I tried to do using networkx. I present the current and expected outputs.
import networkx as nx
N=2
def pos():
x, y = 1, N + 3 - 1
for _ in range(N):
yield (x, y)
y -= (x + 2) // (N+1 )
x = (x + 2) % (N+1)
G = nx.Graph()
it_pos = pos()
for u in range(N):
G.add_node(u+1, pos=next(it_pos))
if u % (2 * N) < N:
for v in (u - 2 * N, u - N, u - N):
if G.has_node(v + 1):
G.add_edge(u + 2, v + 2)
elif u % (2 * N) == N:
G.add_edge(u + 1, u - N + 1)
elif u % (2 * N + 1) < 2 * N:
for v in (u - 1, u - N, u - N):
G.add_edge(u + 1, v + 1)
else:
for v in (u - 1, u - N - 1):
G.add_edge(u + 1, v + 1)
nx.draw(G, nx.get_node_attributes(G, 'pos'), with_labels=True, font_weight='bold')
Nodes=len(G.nodes)
A=nx.adjacency_matrix(G).todense()
The current output is
A=matrix([[0., 0.],
[0., 0.]])
The expected output is

You want the adjacency matrix between node and its edges, but the function you are using looks for neighbouring nodes.
In order to build your network and get your matrix, you could do the following:
import networkx as nx
import numpy as np
import pandas as pd
# build the network with relevant edges
G = nx.Graph()
points = {
0: (1, 1), 1: (2, 1),
'a':(1, 2), 'b':(2, 2),
'c':(0, 1), 'd':(3, 1),
'e':(1, 0), 'f':(2, 0)
}
for key, pos in points.items():
G.add_node(key, pos=pos)
G.add_edge('a', 0, name=0)
G.add_edge('b', 1, name=1)
G.add_edge('c', 0, name=2)
G.add_edge(0, 1, name=3)
G.add_edge(1, 'd', name=4)
G.add_edge(0, 'e', name=5)
G.add_edge(1, 'f', name=6)
# find connected edges to nodes 0 and 1
my_nodes = [0, 1] # could be more here
edges = {
node: [G.get_edge_data(*edge)['name'] for edge in G.edges(node)]
for node in my_nodes
}
# build matirx
mat = np.zeros((len(my_nodes), 7), dtype=np.uint8)
for i, node in enumerate(my_nodes)):
mat[i, edges[node]] = 1
mat[i, edges[node]] = 1
A = pd.DataFrame(mat)
A
Edit: generalize the connection search.

You can implement the matrix in Python using a nested list:
A = [[1, 0, 1, 1, 0, 1, 0], [0, 1, 0, 1, 1, 0, 1]]

Related

Arrows on edges in a graph using Networkx

I have a graph with nodes and edges. The code colors the array Edges with the array Weights as shown in the current output. Is it possible to put arrows on the array elements in Edges as displayed in the expected output? I want arrows on specific edges according to Edges, not all.
import networkx as nx
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.cm import ScalarMappable
N = 1
def pos():
x, y = 1, N + 3 - 1
for _ in range(2 * N * (N + 1)):
yield (x, y)
y -= (x + 2) // (N + 3)
x = (x + 2) % (N + 3)
G = nx.Graph()
it_pos = pos()
for u in range(2 * N * (N + 1)):
G.add_node(u + 1, pos=next(it_pos))
if u % (2 * N + 1) < N:
for v in (u - 2 * N - 1, u - N - 1, u - N):
if G.has_node(v + 1):
G.add_edge(u + 1, v + 1)
elif u % (2 * N + 1) == N:
G.add_edge(u + 1, u - N + 1)
elif u % (2 * N + 1) < 2 * N:
for v in (u - 1, u - N - 1, u - N):
G.add_edge(u + 1, v + 1)
else:
for v in (u - 1, u - N - 1):
G.add_edge(u + 1, v + 1)
nx.draw(G, nx.get_node_attributes(G, 'pos'), with_labels=True, font_weight='bold')
Edges=np.array([[1,2],[1,3],[1,4]])
Weights=np.array([[1.7],[2.9],[8.6]])
flat_weights = Weights.flatten()
weights_normalized = [x / max(flat_weights) for x in flat_weights]
edge_weight_map = dict(zip([tuple(e) for e in Edges.tolist()],weights_normalized))
my_cmap = plt.cm.get_cmap('Oranges')
colors = my_cmap([edge_weight_map.get(tuple(e), 0) for e in Edges.tolist()])
pos = nx.get_node_attributes(G, 'pos')
sm = ScalarMappable(cmap=my_cmap, norm=plt.Normalize(0,max(flat_weights)))
nx.draw_networkx_edges(G, pos, edge_color=colors,
edgelist=[tuple(e) for e in Edges.tolist()],
width=5);
plt.colorbar(sm)
The current output is
The expected output is
You are almost there.
First, you will need to create a directed graph instead of an undirected graph:
G = nx.DiGraph()
Second, DiGraph objects are plotted with arrow heads by default, so you need to specify arrows=False in the call to nx.draw(...).
nx.draw(G, nx.get_node_attributes(G, 'pos'), with_labels=True, font_weight='bold', arrows=False)
If you now plot your selected edges separately, they come with arrow heads (per default).

Adjusting node numbering in Networkx

The following code generate adjacency matrix of a specific network. However, I want the node numbering to occur in a certain way and remain fixed and not fluctuate with every run. I present the current and expected output.
import matplotlib.pyplot as plt
import networkx as nx
N = 2
G = nx.Graph()
for u in range(2 * N * (N + 1)):
if u % (2 * N + 1) < N:
for v in (u - 2 * N - 1, u - N - 1, u - N):
if G.has_node(v):
G.add_edge(u, v)
elif u % (2 * N + 1) == N:
G.add_edge(u, u - N)
elif u % (2 * N + 1) < 2 * N:
for v in (u - 1, u - N - 1, u - N):
G.add_edge(u, v)
else:
for v in (u - 1, u - N - 1):
G.add_edge(u, v)
A=nx.adjacency_matrix(G).todense()
print([A])
nx.draw(G,with_labels=True, font_weight='bold')
The current output is
The expected output is
Here is a possible solution:
import networkx as nx
N = 2
def pos():
x, y = 1, N + 3 - 1
for _ in range(2 * N * (N + 1)):
yield (x, y)
y -= (x + 2) // (N + 3)
x = (x + 2) % (N + 3)
G = nx.Graph()
it_pos = pos()
for u in range(2 * N * (N + 1)):
G.add_node(u + 1, pos=next(it_pos))
if u % (2 * N + 1) < N:
for v in (u - 2 * N - 1, u - N - 1, u - N):
if G.has_node(v + 1):
G.add_edge(u + 1, v + 1)
elif u % (2 * N + 1) == N:
G.add_edge(u + 1, u - N + 1)
elif u % (2 * N + 1) < 2 * N:
for v in (u - 1, u - N - 1, u - N):
G.add_edge(u + 1, v + 1)
else:
for v in (u - 1, u - N - 1):
G.add_edge(u + 1, v + 1)
nx.draw(G, nx.get_node_attributes(G, 'pos'), with_labels=True, font_weight='bold')
This is the result of the code above:
Here I've used the pos() function to generate each node position. These positions are saved as tuples (x, y) within each node as a label. And they're eventually used to draw the graph.

Keeping the orientation same of graphs in Networkx

I am generating a graph with 12 nodes and the adjacency matrix. However, with every run, I see a different orientation though the adjacency matrix is the same. I want to have the same orientation (with the same adjacency matrix ofcourse!) in every run. I present the current and expected output.
import networkx as nx
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
N = 2
def pos():
x, y = 1, N + 3 - 1
for _ in range(2 * N * (N + 1)):
yield (x, y)
y -= (x + 2) // (N + 3)
x = (x + 2) % (N + 3)
G = nx.Graph()
it_pos = pos()
for u in range(2 * N * (N + 1)):
G.add_node(u + 1, pos=next(it_pos))
if u % (2 * N + 1) < N:
for v in (u - 2 * N - 1, u - N - 1, u - N):
if G.has_node(v + 1):
G.add_edge(u + 1, v + 1)
elif u % (2 * N + 1) == N:
G.add_edge(u + 1, u - N + 1)
elif u % (2 * N + 1) < 2 * N:
for v in (u - 1, u - N - 1, u - N):
G.add_edge(u + 1, v + 1)
else:
for v in (u - 1, u - N - 1):
G.add_edge(u + 1, v + 1)
nx.draw(G, nx.get_node_attributes(G, 'pos'), with_labels=True, font_weight='bold')
D=len(G.nodes)
print(D)
fig, ax = plt.subplots(1, 1)
P=np.array([[1.00000000e-04, 1.90053824e-05, 3.70041863e-05, 5.50029902e-05,
7.30017941e-05, 8.20011961e-05, 1.00059804e-05, 9.10005980e-05,
2.80047843e-05, 1.00657843e-06, 4.60035882e-05, 1.01000000e-04]])
D=nx.draw(G, with_labels=True, node_color=[P], node_size=1000, cmap='Blues')
# -------------------------------------
norm = matplotlib.colors.Normalize(vmin=np.min(P), vmax=np.max(P))
sm = plt.cm.ScalarMappable(cmap='Blues', norm=norm)
sm.set_array([])
clb=fig.colorbar(sm)
clb.set_label('r (m)',labelpad=-50, y=1.1, rotation=0)
# -------------------------------------
plt.show()
The current output is
The expected output is
You are plotting twice with nx.draw, the first time with the node positions specified (correctly), the second time without specifying the node positions.
Presumably, you are only saving (and hence seeing) the second plot.
Delete the first call to nx.draw and substitute the second call (D=nx.draw(...)) with:
nx.draw(G, nx.get_node_attributes(G, 'pos'), with_labels=True, font_weight='bold', node_color=[P], cmap='Blues')

Python shortest distance while escaping a binary maze using backtracking

Need help with finding shortest distance in a binary maze, which is a list of lists matrix, where 0 is an empty cell and 1 is a wall. The maze has x,y starting coordinates that default to 0,0 and it's end point is always bottom right corner. Shortest distance always includes the starting point (i.e., if there are four steps needed from the starting point, shortest distance will be 5)
I need to be using backtracking algorithm. So far I could come up with a function that determines if there is an escaping path at all. It works well:
def is_solution(maze, x=0, y=0):
n = len(maze)
m = len(maze[0])
if x == n - 1 and y == m - 1:
return True
maze[x][y] = 1
result = False
for a, b in [(x - 1, y), (x, y - 1), (x + 1, y), (x, y + 1)]:
if 0 <= a < n and 0 <= b < m and maze[a][b] == 0:
result = result or is_solution(maze, a, b)
maze[x][y] = 0
return result
maze = [
[0, 0, 1, 1],
[1, 0, 0, 0],
[1, 1, 1, 0]
]
is_solution(maze)
The above will result to True.
Now I am really struggling with finding the shortest distance. I think it should be relatively easy to update the code above so it showed distance if there is a path and inf if there isn't one. But I got stuck. In the example above shortest distance would be 6 (including the starting point)
I also need to add code to be able to get a list of all shortest distances and coordinates of each step in a list of lists format like [[(0, 0), (0, 1), (1, 1), (1, 2), (1, 3), (2, 3)]] . In this case there is only one path, but if there were two of distance six, that list would include also the list of second shortest path as well.
Thank you.
Simple change to your code to track shortest path
Shortest Path
def min_solution(maze, x = 0, y = 0, path = None):
def try_next(x, y):
' Next position we can try '
return [(a, b) for a, b in [(x - 1, y), (x, y - 1), (x + 1, y), (x, y + 1)] if 0 <= a < n and 0 <= b < m]
n = len(maze)
m = len(maze[0])
if path is None:
path = [(x, y)] # Init path to current position
# Reached destionation
if x == n - 1 and y == m - 1:
return path
maze[x][y] = 1 # Mark current position so we won't use this cell in recursion
# Recursively find shortest path
shortest_path = None
for a, b in try_next(x, y):
if not maze[a][b]:
last_path = min_solution(maze, a, b, path + [(a, b)]) # Solution going to a, b next
if not shortest_path:
shortest_path = last_path # Use since haven't found a path yet
elif last_path and len(last_path) < len(shortest_path):
shortest_path = last_path # Use since path is shorter
maze[x][y] = 0 # Unmark so we can use this cell
return shortest_path
maze = [
[0, 0, 1, 1],
[1, 0, 0, 0],
[1, 1, 1, 0]
]
t = min_solution(maze)
if t:
print(f'Shortest path {t} has length {len(t)}')
else:
print('Path not found')
Output:
Shortest path [(0, 0), (0, 1), (1, 1), (1, 2), (1, 3), (2, 3)] has length 6
All Paths
def all_paths(maze, x = 0, y = 0, path = None):
'''
All paths through Maze as a generator
'''
def try_next(x, y):
' Next position we can try '
return [(a, b) for a, b in [(x - 1, y), (x, y - 1), (x + 1, y), (x, y + 1)] if 0 <= a < n and 0 <= b < m]
n = len(maze)
m = len(maze[0])
if path is None:
path = [(x, y)]
# Reached destionation
if x == n - 1 and y == m - 1:
yield path
else:
maze[x][y] = 1 # Mark current position so we won't use this cell in recursion
# Recursively find pat
for a, b in try_next(x, y):
if not maze[a][b]:
yield from all_paths(maze, a, b, path + [(a, b)]) # Solution going to a, b next
maze[x][y] = 0 # Unmark so we can use this cell
maze = [[0, 0, 0],
[1, 0, 0],
[1, 1, 0]]
for t in all_paths(maze):
print(f'path {t} has length {len(t)}')
Output
path [(0, 0), (0, 1), (1, 1), (1, 2), (2, 2)] has length 5
path [(0, 0), (0, 1), (0, 2), (1, 2), (2, 2)] has length 5

How to change nearest neighbors function to not work diagonally

I've working nearest neighbors function but I don't know how to make it work only horizontally and vertically right now it works in all directions. Code below:
nnlst = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
MAP_WIDTH = 3
MAP_HEIGHT = 3
def nearest_neighbors(map_x, map_y):
coordinates_list = []
for x_ in range(max(0, map_x - 1), min(MAP_WIDTH, map_x + 2)):
for y_ in range(max(0, map_y - 1), min(MAP_HEIGHT, map_y + 2)):
# we are ignoring result when x_ and y_ equals variable we ask for
if (map_x, map_y) == (x_, y_):
continue
coordinates_list.append([x_, y_])
return coordinates_list
print "function result"
print "nearest neighbors of", nnlst[0][1]
nearest_neighbor_coordinates_list = nearest_neighbors(0, 1)
for coordinates in nearest_neighbor_coordinates_list:
print coordinates, "=", nnlst[coordinates[0]][coordinates[1]]
As you can see right now it works in all directions.
You need to add one more condition to prevent inclusion of the diagonal ones:
def nearest_neighbors(map_x, map_y):
coordinates_list = []
for x_ in range(max(0, map_x - 1), min(MAP_WIDTH, map_x + 2)):
for y_ in range(max(0, map_y - 1), min(MAP_HEIGHT, map_y + 2)):
# we are ignoring result when x_ and y_ equals variable we ask for, also the diagonal neigbors that differ in both x & y coordinates
if (map_x, map_y) == (x_, y_) or (map_x != x_ and map_y != y_):
continue
coordinates_list.append([x_, y_])
return coordinates_list
to get the desired result:
function result
nearest neighbors of 2
[0, 0] = 1
[0, 2] = 3
[1, 1] = 5
alternatively, you could list all "admissible" displacements explicitly:
for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
x_ = min(MAP_WIDTH, max(0, map_x + dx))
y_ = min(MAP_HEIGHT, max(0, map_y + dy))
if (map_x, map_y) == (x_, y_):
continue
...
For a problem with such as small number of possibilities, I would just spell them all out and pre-compute the function results for every position. That way the function can be eliminated and the problem reduced to doing simple table look-up operation.
Here's what I mean:
nnlist = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
MAP_WIDTH = len(nnlist[0])
MAP_HEIGHT = len(nnlist)
nearest_neighbors = {} # is now a dictionary
for x in range(MAP_WIDTH):
for y in range(MAP_HEIGHT):
neighbors = [[nx, ny] for nx, ny in [(x-1, y), (x+1, y), (x, y-1), (x, y+1)]
if -1 < nx < MAP_WIDTH and -1 < ny < MAP_HEIGHT]
nearest_neighbors[(x, y)] = neighbors
print "look-up result"
print "nearest neighbors of", nnlist[0][1]
nearest_neighbor_coordinates_list = nearest_neighbors[(0, 1)]
for coordinates in nearest_neighbor_coordinates_list:
print coordinates, "=", nnlist[coordinates[0]][coordinates[1]]

Categories

Resources