python-3.xnetworkxagent-based-modelingmesa

How to get the distance between nodes with Mesa and Networkx on Python?


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.


Solution

  • 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)