1
0
mirror of https://github.com/gsi-upm/soil synced 2025-08-24 03:52:20 +00:00

WIP: all tests pass

Documentation needs some improvement

The API has been simplified to only allow for ONE topology per
NetworkEnvironment.
This covers the main use case, and simplifies the code.
This commit is contained in:
J. Fernando Sánchez
2022-10-16 17:54:03 +02:00
parent cd62c23cb9
commit d9947c2c52
34 changed files with 693 additions and 736 deletions

View File

@@ -8,17 +8,12 @@ interval: 1
seed: '1'
model_class: social_wealth.MoneyEnv
model_params:
topologies:
default:
params:
generator: social_wealth.graph_generator
n: 5
generator: social_wealth.graph_generator
agents:
topology: true
distribution:
- agent_class: social_wealth.SocialMoneyAgent
topology: default
weight: 1
mesa_agent_class: social_wealth.MoneyAgent
N: 10
width: 50
height: 50

View File

@@ -2,6 +2,7 @@ from mesa.visualization.ModularVisualization import ModularServer
from soil.visualization import UserSettableParameter
from mesa.visualization.modules import ChartModule, NetworkModule, CanvasGrid
from social_wealth import MoneyEnv, graph_generator, SocialMoneyAgent
import networkx as nx
class MyNetwork(NetworkModule):
@@ -13,15 +14,16 @@ def network_portrayal(env):
# The model ensures there is 0 or 1 agent per node
portrayal = dict()
wealths = {node_id: data['agent'].wealth for (node_id, data) in env.G.nodes(data=True)}
portrayal["nodes"] = [
{
"id": agent_id,
"size": env.get_agent(agent_id).wealth,
# "color": "#CC0000" if not agents or agents[0].wealth == 0 else "#007959",
"color": "#CC0000",
"label": f"{agent_id}: {env.get_agent(agent_id).wealth}",
}
for (agent_id) in env.G.nodes
"id": node_id,
"size": 2*(wealth+1),
"color": "#CC0000" if wealth == 0 else "#007959",
# "color": "#CC0000",
"label": f"{node_id}: {wealth}",
} for (node_id, wealth) in wealths.items()
]
portrayal["edges"] = [
@@ -29,7 +31,6 @@ def network_portrayal(env):
for edge_id, (source, target) in enumerate(env.G.edges)
]
return portrayal
@@ -55,7 +56,7 @@ def gridPortrayal(agent):
}
grid = MyNetwork(network_portrayal, 500, 500, library="sigma")
grid = MyNetwork(network_portrayal, 500, 500)
chart = ChartModule(
[{"Label": "Gini", "Color": "Black"}], data_collector_name="datacollector"
)
@@ -70,7 +71,6 @@ model_params = {
1,
description="Choose how many agents to include in the model",
),
"network_agents": [{"agent_class": SocialMoneyAgent}],
"height": UserSettableParameter(
"slider",
"height",
@@ -89,12 +89,15 @@ model_params = {
1,
description="Grid width",
),
"network_params": {
'generator': graph_generator
},
"agent_class": UserSettableParameter('choice', 'Agent class', value='MoneyAgent',
choices=['MoneyAgent', 'SocialMoneyAgent']),
"generator": graph_generator,
}
canvas_element = CanvasGrid(gridPortrayal, model_params["width"].value, model_params["height"].value, 500, 500)
canvas_element = CanvasGrid(gridPortrayal,
model_params["width"].value,
model_params["height"].value, 500, 500)
server = ModularServer(

View File

@@ -10,7 +10,7 @@ from mesa.batchrunner import BatchRunner
import networkx as nx
from soil import NetworkAgent, Environment
from soil import NetworkAgent, Environment, serialization
def compute_gini(model):
agent_wealths = [agent.wealth for agent in model.agents]
@@ -19,15 +19,16 @@ def compute_gini(model):
B = sum( xi * (N-i) for i,xi in enumerate(x) ) / (N*sum(x))
return (1 + (1/N) - 2*B)
class MoneyAgent(MesaAgent):
"""
A MESA agent with fixed initial wealth.
It will only share wealth with neighbors based on grid proximity
"""
def __init__(self, unique_id, model):
def __init__(self, unique_id, model, wealth=1):
super().__init__(unique_id=unique_id, model=model)
self.wealth = 1
self.wealth = wealth
def move(self):
possible_steps = self.model.grid.get_neighborhood(
@@ -45,7 +46,7 @@ class MoneyAgent(MesaAgent):
self.wealth -= 1
def step(self):
self.info("Crying wolf", self.pos)
print("Crying wolf", self.pos)
self.move()
if self.wealth > 0:
self.give_money()
@@ -58,8 +59,8 @@ class SocialMoneyAgent(NetworkAgent, MoneyAgent):
cellmates = set(self.model.grid.get_cell_list_contents([self.pos]))
friends = set(self.get_neighboring_agents())
self.info("Trying to give money")
self.debug("Cellmates: ", cellmates)
self.debug("Friends: ", friends)
self.info("Cellmates: ", cellmates)
self.info("Friends: ", friends)
nearby_friends = list(cellmates & friends)
@@ -68,14 +69,29 @@ class SocialMoneyAgent(NetworkAgent, MoneyAgent):
other.wealth += 1
self.wealth -= 1
def graph_generator(n=5):
G = nx.Graph()
for ix in range(n):
G.add_edge(0, ix)
return G
class MoneyEnv(Environment):
"""A model with some number of agents."""
def __init__(self, width, height, *args, topologies, **kwargs):
def __init__(self, width, height, N, generator=graph_generator,
agent_class=SocialMoneyAgent,
topology=None, **kwargs):
super().__init__(*args, topologies=topologies, **kwargs)
generator = serialization.deserialize(generator)
agent_class = serialization.deserialize(agent_class, globs=globals())
topology = generator(n=N)
super().__init__(topology=topology,
N=N,
**kwargs)
self.grid = MultiGrid(width, height, False)
self.populate_network(agent_class=agent_class)
# Create agents
for agent in self.agents:
x = self.random.randrange(self.grid.width)
@@ -87,17 +103,9 @@ class MoneyEnv(Environment):
agent_reporters={"Wealth": "wealth"})
def graph_generator(n=5):
G = nx.Graph()
for ix in range(n):
G.add_edge(0, ix)
return G
if __name__ == '__main__':
G = graph_generator()
fixed_params = {"topology": G,
fixed_params = {"generator": nx.complete_graph,
"width": 10,
"network_agents": [{"agent_class": SocialMoneyAgent,
'weight': 1}],
@@ -116,4 +124,3 @@ if __name__ == '__main__':
run_data = batch_run.get_model_vars_dataframe()
run_data.head()
print(run_data.Gini)