I am following this example called Virus on Network from the Mesa library which creates network graphs using Networkx.
Here is its function screening a node's neighbors to try to infect them with a virus.
def try_to_infect_neighbors(self):
neighbors_nodes = self.model.grid.get_neighbors(self.pos, include_center=False)
susceptible_neighbors = [agent for agent in self.model.grid.get_cell_list_contents(neighbors_nodes) if
agent.state is State.SUSCEPTIBLE]
for a in susceptible_neighbors:
if self.random.random() < self.virus_spread_chance:
a.state = State.INFECTED
However, I like to get the distance between a node and its neighbor. And here is another example called Sugarscape from Mesa that seems to do just that.
So I modified the code into:
def try_to_infect_neighbors(self):
neighbors_nodes = self.model.grid.get_neighbors(self.pos, include_center=False)
susceptible_neighbors = [agent for agent in self.model.grid.get_cell_list_contents(neighbors_nodes) if
agent.state is State.SUSCEPTIBLE]
for a in susceptible_neighbors:
print('Self position:', self.pos, 'Neightbor position:', neighbor_agent.pos)
# Output: Self position: 52 Neightbor position: 13
neightbor_distance = get_distance(self.pos, neighbor_agent.pos)
# TypeError: 'int' object is not iterable
print(neightbor_distance)
if neightbor_distance <= 1:
if self.random.random() < self.virus_spread_chance:
a.state = State.INFECTED
def get_distance(pos_1, pos_2):
""" Get the distance between two point
Args:
pos_1, pos_2: Coordinate tuples for both points.
"""
x1, y1 = pos_1
x2, y2 = pos_2
dx = x1 - x2
dy = y1 - y2
return math.sqrt(dx**2 + dy**2)
In the Sugarscape example, a.pos
gives a tuple of x and y locations. But in the Virus on Network, a.pos
gives the agent's ID. How can I access agent's x and y location in the Virus on Network example? I tried searching for them from within a.model.G
and a.model.grid
via the variables: self.G = nx.erdos_renyi_graph(n=self.num_nodes, p=prob)
and self.grid = NetworkGrid(self.G)
, but I couldn't identify them and am thinking it shouldn't be that hidden.
Here is some code to add edge weights or get random node positions (maybe you want to scale them).
import networkx as nx
import random
num_nodes = 10
prob = .25
G = nx.erdos_renyi_graph(n=num_nodes, p=prob)
# you need to add this in the __init__ of VirusOnNetwork
for u, v in G.edges():
# add random weights between 0 and 10
G[u][v]["weight"] = random.random() * 10
# you can access these weights in the same way (G[from_node][target_node]["weight"]
print(G.edges(data=True))
# [(0, 5, {'weight': 2.3337749464751454}), (0, 9, {'weight': 6.127630949347937}), (1, 4, {'weight': 9.048896640242369}), (2, 4, {'weight': 1.4236964132196228}), (2, 6, {'weight': 4.749936581386136}), (2, 9, {'weight': 2.037644705935693}), (3, 5, {'weight': 2.296192134297448}), (3, 7, {'weight': 1.5250362478641677}), (3, 9, {'weight': 7.362866019415747}), (4, 6, {'weight': 7.365668938333058}), (5, 6, {'weight': 1.1855367672698724}), (5, 8, {'weight': 3.219373770451519}), (7, 9, {'weight': 4.025563800958256})]
# alternative node positions
# you can store them in the graph or as separate attribute of your model
pos = nx.random_layout(G)
print(pos)
#{0: array([0.8604371 , 0.19834588], dtype=float32), 1: array([0.13099413, 0.97313595], dtype=float32), 2: array([0.30455875, 0.8844262 ], dtype=float32), 3: array([0.575425, 0.517468], dtype=float32), 4: array([0.7437008 , 0.89525336], dtype=float32), 5: array([0.9664812 , 0.21694745], dtype=float32), 6: array([0.89979964, 0.33603832], dtype=float32), 7: array([0.7894464, 0.7614578], dtype=float32), 8: array([0.44350627, 0.9081728 ], dtype=float32), 9: array([0.8049214 , 0.20761919], dtype=float32)}
# you can use this position for visualisation with networkx
nx.draw(G, pos)