Mesa 3.2.0.dev0__py3-none-any.whl → 3.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of Mesa might be problematic. Click here for more details.
- mesa/__init__.py +1 -1
- mesa/agent.py +9 -7
- mesa/datacollection.py +1 -1
- mesa/examples/README.md +1 -1
- mesa/examples/__init__.py +2 -0
- mesa/examples/advanced/alliance_formation/Readme.md +50 -0
- mesa/examples/advanced/alliance_formation/__init__ .py +0 -0
- mesa/examples/advanced/alliance_formation/agents.py +20 -0
- mesa/examples/advanced/alliance_formation/app.py +71 -0
- mesa/examples/advanced/alliance_formation/model.py +184 -0
- mesa/examples/advanced/epstein_civil_violence/app.py +11 -11
- mesa/examples/advanced/pd_grid/Readme.md +4 -6
- mesa/examples/advanced/pd_grid/app.py +10 -11
- mesa/examples/advanced/sugarscape_g1mt/Readme.md +4 -5
- mesa/examples/advanced/sugarscape_g1mt/app.py +34 -16
- mesa/examples/advanced/wolf_sheep/Readme.md +2 -17
- mesa/examples/advanced/wolf_sheep/app.py +21 -18
- mesa/examples/basic/boid_flockers/Readme.md +6 -1
- mesa/examples/basic/boid_flockers/app.py +15 -11
- mesa/examples/basic/boltzmann_wealth_model/Readme.md +2 -12
- mesa/examples/basic/boltzmann_wealth_model/app.py +39 -32
- mesa/examples/basic/conways_game_of_life/Readme.md +1 -9
- mesa/examples/basic/conways_game_of_life/app.py +13 -16
- mesa/examples/basic/schelling/Readme.md +2 -10
- mesa/examples/basic/schelling/agents.py +9 -3
- mesa/examples/basic/schelling/app.py +50 -3
- mesa/examples/basic/schelling/model.py +2 -0
- mesa/examples/basic/schelling/resources/blue_happy.png +0 -0
- mesa/examples/basic/schelling/resources/blue_unhappy.png +0 -0
- mesa/examples/basic/schelling/resources/orange_happy.png +0 -0
- mesa/examples/basic/schelling/resources/orange_unhappy.png +0 -0
- mesa/examples/basic/virus_on_network/Readme.md +0 -4
- mesa/examples/basic/virus_on_network/app.py +31 -14
- mesa/experimental/__init__.py +2 -2
- mesa/experimental/continuous_space/continuous_space.py +1 -1
- mesa/experimental/meta_agents/__init__.py +25 -0
- mesa/experimental/meta_agents/meta_agent.py +387 -0
- mesa/model.py +3 -3
- mesa/space.py +4 -1
- mesa/visualization/__init__.py +2 -0
- mesa/visualization/backends/__init__.py +23 -0
- mesa/visualization/backends/abstract_renderer.py +97 -0
- mesa/visualization/backends/altair_backend.py +440 -0
- mesa/visualization/backends/matplotlib_backend.py +419 -0
- mesa/visualization/components/__init__.py +28 -8
- mesa/visualization/components/altair_components.py +86 -0
- mesa/visualization/components/matplotlib_components.py +4 -2
- mesa/visualization/components/portrayal_components.py +120 -0
- mesa/visualization/mpl_space_drawing.py +292 -129
- mesa/visualization/solara_viz.py +274 -32
- mesa/visualization/space_drawers.py +797 -0
- mesa/visualization/space_renderer.py +399 -0
- {mesa-3.2.0.dev0.dist-info → mesa-3.3.0.dist-info}/METADATA +13 -4
- {mesa-3.2.0.dev0.dist-info → mesa-3.3.0.dist-info}/RECORD +57 -40
- mesa/examples/advanced/sugarscape_g1mt/tests.py +0 -69
- {mesa-3.2.0.dev0.dist-info → mesa-3.3.0.dist-info}/WHEEL +0 -0
- {mesa-3.2.0.dev0.dist-info → mesa-3.3.0.dist-info}/licenses/LICENSE +0 -0
- {mesa-3.2.0.dev0.dist-info → mesa-3.3.0.dist-info}/licenses/NOTICE +0 -0
mesa/__init__.py
CHANGED
mesa/agent.py
CHANGED
|
@@ -53,13 +53,12 @@ class Agent:
|
|
|
53
53
|
|
|
54
54
|
Args:
|
|
55
55
|
model (Model): The model instance in which the agent exists.
|
|
56
|
-
args:
|
|
57
|
-
kwargs:
|
|
56
|
+
args: Passed on to super.
|
|
57
|
+
kwargs: Passed on to super.
|
|
58
58
|
|
|
59
59
|
Notes:
|
|
60
60
|
to make proper use of python's super, in each class remove the arguments and
|
|
61
61
|
keyword arguments you need and pass on the rest to super
|
|
62
|
-
|
|
63
62
|
"""
|
|
64
63
|
super().__init__(*args, **kwargs)
|
|
65
64
|
|
|
@@ -103,7 +102,10 @@ class Agent:
|
|
|
103
102
|
"""
|
|
104
103
|
|
|
105
104
|
class ListLike:
|
|
106
|
-
"""
|
|
105
|
+
"""Make default arguments act as if they are in a list of length N.
|
|
106
|
+
|
|
107
|
+
This is a helper class.
|
|
108
|
+
"""
|
|
107
109
|
|
|
108
110
|
def __init__(self, value):
|
|
109
111
|
self.value = value
|
|
@@ -183,7 +185,7 @@ class AgentSet(MutableSet, Sequence):
|
|
|
183
185
|
Random()
|
|
184
186
|
) # FIXME see issue 1981, how to get the central rng from model
|
|
185
187
|
self.random = random
|
|
186
|
-
self._agents = weakref.WeakKeyDictionary(
|
|
188
|
+
self._agents = weakref.WeakKeyDictionary(dict.fromkeys(agents))
|
|
187
189
|
|
|
188
190
|
def __len__(self) -> int:
|
|
189
191
|
"""Return the number of agents in the AgentSet."""
|
|
@@ -262,7 +264,7 @@ class AgentSet(MutableSet, Sequence):
|
|
|
262
264
|
self.random.shuffle(weakrefs)
|
|
263
265
|
|
|
264
266
|
if inplace:
|
|
265
|
-
self._agents.data =
|
|
267
|
+
self._agents.data = dict.fromkeys(weakrefs)
|
|
266
268
|
return self
|
|
267
269
|
else:
|
|
268
270
|
return AgentSet(
|
|
@@ -301,7 +303,7 @@ class AgentSet(MutableSet, Sequence):
|
|
|
301
303
|
|
|
302
304
|
This is a private method primarily used internally by other methods like select, shuffle, and sort.
|
|
303
305
|
"""
|
|
304
|
-
self._agents = weakref.WeakKeyDictionary(
|
|
306
|
+
self._agents = weakref.WeakKeyDictionary(dict.fromkeys(agents))
|
|
305
307
|
return self
|
|
306
308
|
|
|
307
309
|
def do(self, method: str | Callable, *args, **kwargs) -> AgentSet:
|
mesa/datacollection.py
CHANGED
mesa/examples/README.md
CHANGED
|
@@ -12,7 +12,7 @@ The examples are categorized into two groups:
|
|
|
12
12
|
The basic examples are relatively simple and only use stable Mesa features. They are good starting points for learning how to use Mesa.
|
|
13
13
|
|
|
14
14
|
### [Boltzmann Wealth Model](examples/basic/boltzmann_wealth_model)
|
|
15
|
-
Completed code to go along with the [tutorial](https://mesa.readthedocs.io/latest/tutorials/
|
|
15
|
+
Completed code to go along with the [tutorial](https://mesa.readthedocs.io/latest/tutorials/0_first_model.html) on making a simple model of how a highly-skewed wealth distribution can emerge from simple rules.
|
|
16
16
|
|
|
17
17
|
### [Boids Flockers Model](examples/basic/boid_flockers)
|
|
18
18
|
[Boids](https://en.wikipedia.org/wiki/Boids)-style flocking model, demonstrating the use of agents moving through a continuous space following direction vectors.
|
mesa/examples/__init__.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from mesa.examples.advanced.alliance_formation.model import MultiLevelAllianceModel
|
|
1
2
|
from mesa.examples.advanced.epstein_civil_violence.model import EpsteinCivilViolence
|
|
2
3
|
from mesa.examples.advanced.pd_grid.model import PdGrid
|
|
3
4
|
from mesa.examples.advanced.sugarscape_g1mt.model import SugarscapeG1mt
|
|
@@ -13,6 +14,7 @@ __all__ = [
|
|
|
13
14
|
"BoltzmannWealth",
|
|
14
15
|
"ConwaysGameOfLife",
|
|
15
16
|
"EpsteinCivilViolence",
|
|
17
|
+
"MultiLevelAllianceModel",
|
|
16
18
|
"PdGrid",
|
|
17
19
|
"Schelling",
|
|
18
20
|
"SugarscapeG1mt",
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Alliance Formation Model (Meta-Agent Example)
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
This model demonstrates Mesa's meta agent capability.
|
|
6
|
+
|
|
7
|
+
**Overview of meta agent:** Complex systems often have multiple levels of components. A city is not a single entity, but it is made of districts,neighborhoods, buildings, and people. A forest comprises an ecosystem of trees, plants, animals, and microorganisms. An organization is not one entity, but is made of departments, sub-departments, and people. A person is not a single entity, but it is made of micro biomes, organs and cells.
|
|
8
|
+
|
|
9
|
+
This reality is the motivation for meta-agents. It allows users to represent these multiple levels, where each level can have agents with sub-agents.
|
|
10
|
+
|
|
11
|
+
This model demonstrates Mesa's ability to dynamically create new classes of agents that are composed of existing agents. These meta-agents inherits functions and attributes from their sub-agents and users can specify new functionality or attributes they want the meta agent to have. For example, if a user is doing a factory simulation with autonomous systems, each major component of that system can be a sub-agent of the overall robot agent. Or, if someone is doing a simulation of an organization, individuals can be part of different organizational units that are working for some purpose.
|
|
12
|
+
|
|
13
|
+
To provide a simple demonstration of this capability is an alliance formation model.
|
|
14
|
+
|
|
15
|
+
In this simulation n agents are created, who have two attributes (1) power and (2) preference. Each attribute is a number between 0 and 1 over a gaussian distribution. Agents then randomly select other agents and use the [bilateral shapley value](https://en.wikipedia.org/wiki/Shapley_value) to determine if they should form an alliance. If the expected utility support an alliances, the agent creates a meta-agent. Subsequent steps may add agents to the meta-agent, create new instances of similar hierarchy, or create a new hierarchy level where meta-agents form an alliance of meta-agents. In this visualization of this model a new meta-agent hierarchy will be a larger node and a new color.
|
|
16
|
+
|
|
17
|
+
In MetaAgents current configuration, agents being part of multiple meta-agents is not supported.
|
|
18
|
+
|
|
19
|
+
If you would like to see an example of explicit meta-agent formation see the [warehouse model in the Mesa example's repository](https://github.com/projectmesa/mesa-examples/tree/main/examples/warehouse)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
This model requires Mesa's recommended install and scipy
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
$ pip install mesa[rec]
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## How to Run
|
|
31
|
+
|
|
32
|
+
To run the model interactively, in this directory, run the following command
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
$ solara run app.py
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Files
|
|
39
|
+
|
|
40
|
+
- `model.py`: Contains creation of agents, the network and management of agent execution.
|
|
41
|
+
- `agents.py`: Contains logic for forming alliances and creation of new agents
|
|
42
|
+
- `app.py`: Contains the code for the interactive Solara visualization.
|
|
43
|
+
|
|
44
|
+
## Further Reading
|
|
45
|
+
|
|
46
|
+
The full tutorial describing how the model is built can be found at:
|
|
47
|
+
https://mesa.readthedocs.io/en/latest/tutorials/intro_tutorial.html
|
|
48
|
+
|
|
49
|
+
An example of the bilateral shapley value in another model:
|
|
50
|
+
[Techno-Social Energy Infrastructure Siting: Sustainable Energy Modeling Programming (SEMPro)](https://www.jasss.org/16/3/6.html)
|
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import mesa
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class AllianceAgent(mesa.Agent):
|
|
5
|
+
"""
|
|
6
|
+
Agent has three attributes power (float), position (float) and level (int)
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
def __init__(self, model, power, position, level=0):
|
|
11
|
+
super().__init__(model)
|
|
12
|
+
self.power = power
|
|
13
|
+
self.position = position
|
|
14
|
+
self.level = level
|
|
15
|
+
|
|
16
|
+
"""
|
|
17
|
+
For this demo model agent only need attributes.
|
|
18
|
+
|
|
19
|
+
More complex models could have functions that define agent behavior.
|
|
20
|
+
"""
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import matplotlib.pyplot as plt
|
|
2
|
+
import networkx as nx
|
|
3
|
+
import solara
|
|
4
|
+
from matplotlib.figure import Figure
|
|
5
|
+
|
|
6
|
+
from mesa.examples.advanced.alliance_formation.model import MultiLevelAllianceModel
|
|
7
|
+
from mesa.visualization import SolaraViz
|
|
8
|
+
from mesa.visualization.utils import update_counter
|
|
9
|
+
|
|
10
|
+
model_params = {
|
|
11
|
+
"seed": {
|
|
12
|
+
"type": "InputText",
|
|
13
|
+
"value": 42,
|
|
14
|
+
"label": "Random Seed",
|
|
15
|
+
},
|
|
16
|
+
"n": {
|
|
17
|
+
"type": "SliderInt",
|
|
18
|
+
"value": 50,
|
|
19
|
+
"label": "Number of agents:",
|
|
20
|
+
"min": 10,
|
|
21
|
+
"max": 100,
|
|
22
|
+
"step": 1,
|
|
23
|
+
},
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
# Create visualization elements. The visualization elements are solara components
|
|
27
|
+
# that receive the model instance as a "prop" and display it in a certain way.
|
|
28
|
+
# Under the hood these are just classes that receive the model instance.
|
|
29
|
+
# You can also author your own visualization elements, which can also be functions
|
|
30
|
+
# that receive the model instance and return a valid solara component.
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@solara.component
|
|
34
|
+
def plot_network(model):
|
|
35
|
+
update_counter.get()
|
|
36
|
+
g = model.network
|
|
37
|
+
pos = nx.fruchterman_reingold_layout(g)
|
|
38
|
+
fig = Figure()
|
|
39
|
+
ax = fig.subplots()
|
|
40
|
+
labels = {agent.unique_id: agent.unique_id for agent in model.agents}
|
|
41
|
+
node_sizes = [g.nodes[node]["size"] for node in g.nodes]
|
|
42
|
+
node_colors = [g.nodes[node]["size"] for node in g.nodes()]
|
|
43
|
+
|
|
44
|
+
nx.draw(
|
|
45
|
+
g,
|
|
46
|
+
pos,
|
|
47
|
+
node_size=node_sizes,
|
|
48
|
+
node_color=node_colors,
|
|
49
|
+
cmap=plt.cm.coolwarm,
|
|
50
|
+
labels=labels,
|
|
51
|
+
ax=ax,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
solara.FigureMatplotlib(fig)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# Create initial model instance
|
|
58
|
+
model = MultiLevelAllianceModel(50)
|
|
59
|
+
|
|
60
|
+
# Create the SolaraViz page. This will automatically create a server and display the
|
|
61
|
+
# visualization elements in a web browser.
|
|
62
|
+
# Display it using the following command in the example directory:
|
|
63
|
+
# solara run app.py
|
|
64
|
+
# It will automatically update and display any changes made to this file
|
|
65
|
+
page = SolaraViz(
|
|
66
|
+
model,
|
|
67
|
+
components=[plot_network],
|
|
68
|
+
model_params=model_params,
|
|
69
|
+
name="Alliance Formation Model",
|
|
70
|
+
)
|
|
71
|
+
page # noqa
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import networkx as nx
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
import mesa
|
|
5
|
+
from mesa import Agent
|
|
6
|
+
from mesa.examples.advanced.alliance_formation.agents import AllianceAgent
|
|
7
|
+
from mesa.experimental.meta_agents.meta_agent import (
|
|
8
|
+
create_meta_agent,
|
|
9
|
+
find_combinations,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class MultiLevelAllianceModel(mesa.Model):
|
|
14
|
+
"""
|
|
15
|
+
Model for simulating multi-level alliances among agents.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, n=50, mean=0.5, std_dev=0.1, seed=42):
|
|
19
|
+
"""
|
|
20
|
+
Initialize the model.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
n (int): Number of agents.
|
|
24
|
+
mean (float): Mean value for normal distribution.
|
|
25
|
+
std_dev (float): Standard deviation for normal distribution.
|
|
26
|
+
seed (int): Random seed.
|
|
27
|
+
"""
|
|
28
|
+
super().__init__(seed=seed)
|
|
29
|
+
self.population = n
|
|
30
|
+
self.network = nx.Graph() # Initialize the network
|
|
31
|
+
self.datacollector = mesa.DataCollector(model_reporters={"Network": "network"})
|
|
32
|
+
|
|
33
|
+
# Create Agents
|
|
34
|
+
power = self.rng.normal(mean, std_dev, n)
|
|
35
|
+
power = np.clip(power, 0, 1)
|
|
36
|
+
position = self.rng.normal(mean, std_dev, n)
|
|
37
|
+
position = np.clip(position, 0, 1)
|
|
38
|
+
AllianceAgent.create_agents(self, n, power, position)
|
|
39
|
+
agent_ids = [
|
|
40
|
+
(agent.unique_id, {"size": 300, "level": 0}) for agent in self.agents
|
|
41
|
+
]
|
|
42
|
+
self.network.add_nodes_from(agent_ids)
|
|
43
|
+
|
|
44
|
+
def add_link(self, meta_agent, agents):
|
|
45
|
+
"""
|
|
46
|
+
Add links between a meta agent and its constituent agents in the network.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
meta_agent (MetaAgent): The meta agent.
|
|
50
|
+
agents (list): List of agents.
|
|
51
|
+
"""
|
|
52
|
+
for agent in agents:
|
|
53
|
+
self.network.add_edge(meta_agent.unique_id, agent.unique_id)
|
|
54
|
+
|
|
55
|
+
def calculate_shapley_value(self, agents):
|
|
56
|
+
"""
|
|
57
|
+
Calculate the Shapley value of the two agents.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
agents (list): List of agents.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
tuple: Potential utility, new position, and level.
|
|
64
|
+
"""
|
|
65
|
+
positions = agents.get("position")
|
|
66
|
+
new_position = 1 - (max(positions) - min(positions))
|
|
67
|
+
potential_utility = agents.agg("power", sum) * 1.2 * new_position
|
|
68
|
+
|
|
69
|
+
value_0 = 0.5 * agents[0].power + 0.5 * (potential_utility - agents[1].power)
|
|
70
|
+
value_1 = 0.5 * agents[1].power + 0.5 * (potential_utility - agents[0].power)
|
|
71
|
+
|
|
72
|
+
if value_0 > agents[0].power and value_1 > agents[1].power:
|
|
73
|
+
if agents[0].level > agents[1].level:
|
|
74
|
+
level = agents[0].level
|
|
75
|
+
elif agents[0].level == agents[1].level:
|
|
76
|
+
level = agents[0].level + 1
|
|
77
|
+
else:
|
|
78
|
+
level = agents[1].level
|
|
79
|
+
|
|
80
|
+
return potential_utility, new_position, level
|
|
81
|
+
|
|
82
|
+
def only_best_combination(self, combinations):
|
|
83
|
+
"""
|
|
84
|
+
Filter to keep only the best combination for each agent.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
combinations (list): List of combinations.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
dict: Unique combinations.
|
|
91
|
+
"""
|
|
92
|
+
best = {}
|
|
93
|
+
# Determine best option for EACH agent
|
|
94
|
+
for group, value in combinations:
|
|
95
|
+
agent_ids = sorted(group.get("unique_id")) # by default is bilateral
|
|
96
|
+
# Deal with all possibilities
|
|
97
|
+
if (
|
|
98
|
+
agent_ids[0] not in best and agent_ids[1] not in best
|
|
99
|
+
): # if neither in add both
|
|
100
|
+
best[agent_ids[0]] = [group, value, agent_ids]
|
|
101
|
+
best[agent_ids[1]] = [group, value, agent_ids]
|
|
102
|
+
elif (
|
|
103
|
+
agent_ids[0] in best and agent_ids[1] in best
|
|
104
|
+
): # if both in, see if both would be trading up
|
|
105
|
+
if (
|
|
106
|
+
value[0] > best[agent_ids[0]][1][0]
|
|
107
|
+
and value[0] > best[agent_ids[1]][1][0]
|
|
108
|
+
):
|
|
109
|
+
# Remove the old alliances
|
|
110
|
+
del best[best[agent_ids[0]][2][1]]
|
|
111
|
+
del best[best[agent_ids[1]][2][0]]
|
|
112
|
+
# Add the new alliance
|
|
113
|
+
best[agent_ids[0]] = [group, value, agent_ids]
|
|
114
|
+
best[agent_ids[1]] = [group, value, agent_ids]
|
|
115
|
+
elif (
|
|
116
|
+
agent_ids[0] in best
|
|
117
|
+
): # if only agent_ids[0] in, see if it would be trading up
|
|
118
|
+
if value[0] > best[agent_ids[0]][1][0]:
|
|
119
|
+
# Remove the old alliance for agent_ids[0]
|
|
120
|
+
del best[best[agent_ids[0]][2][1]]
|
|
121
|
+
# Add the new alliance
|
|
122
|
+
best[agent_ids[0]] = [group, value, agent_ids]
|
|
123
|
+
best[agent_ids[1]] = [group, value, agent_ids]
|
|
124
|
+
elif (
|
|
125
|
+
agent_ids[1] in best
|
|
126
|
+
): # if only agent_ids[1] in, see if it would be trading up
|
|
127
|
+
if value[0] > best[agent_ids[1]][1][0]:
|
|
128
|
+
# Remove the old alliance for agent_ids[1]
|
|
129
|
+
del best[best[agent_ids[1]][2][0]]
|
|
130
|
+
# Add the new alliance
|
|
131
|
+
best[agent_ids[0]] = [group, value, agent_ids]
|
|
132
|
+
best[agent_ids[1]] = [group, value, agent_ids]
|
|
133
|
+
|
|
134
|
+
# Create a unique dictionary of the best combinations
|
|
135
|
+
unique_combinations = {}
|
|
136
|
+
for group, value, agents_nums in best.values():
|
|
137
|
+
unique_combinations[tuple(agents_nums)] = [group, value]
|
|
138
|
+
|
|
139
|
+
return unique_combinations.values()
|
|
140
|
+
|
|
141
|
+
def step(self):
|
|
142
|
+
"""
|
|
143
|
+
Execute one step of the model.
|
|
144
|
+
"""
|
|
145
|
+
# Get all other agents of the same type
|
|
146
|
+
agent_types = list(self.agents_by_type.keys())
|
|
147
|
+
|
|
148
|
+
for agent_type in agent_types:
|
|
149
|
+
similar_agents = self.agents_by_type[agent_type]
|
|
150
|
+
|
|
151
|
+
# Find the best combinations using find_combinations
|
|
152
|
+
if (
|
|
153
|
+
len(similar_agents) > 1
|
|
154
|
+
): # only form alliances if there are more than 1 agent
|
|
155
|
+
combinations = find_combinations(
|
|
156
|
+
self,
|
|
157
|
+
similar_agents,
|
|
158
|
+
size=2,
|
|
159
|
+
evaluation_func=self.calculate_shapley_value,
|
|
160
|
+
filter_func=self.only_best_combination,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
for alliance, attributes in combinations:
|
|
164
|
+
class_name = f"MetaAgentLevel{attributes[2]}"
|
|
165
|
+
meta = create_meta_agent(
|
|
166
|
+
self,
|
|
167
|
+
class_name,
|
|
168
|
+
alliance,
|
|
169
|
+
Agent,
|
|
170
|
+
meta_attributes={
|
|
171
|
+
"level": attributes[2],
|
|
172
|
+
"power": attributes[0],
|
|
173
|
+
"position": attributes[1],
|
|
174
|
+
},
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
# Update the network if a new meta agent instance created
|
|
178
|
+
if meta:
|
|
179
|
+
self.network.add_node(
|
|
180
|
+
meta.unique_id,
|
|
181
|
+
size=(meta.level + 1) * 300,
|
|
182
|
+
level=meta.level,
|
|
183
|
+
)
|
|
184
|
+
self.add_link(meta, meta.agents)
|
|
@@ -7,9 +7,10 @@ from mesa.examples.advanced.epstein_civil_violence.model import EpsteinCivilViol
|
|
|
7
7
|
from mesa.visualization import (
|
|
8
8
|
Slider,
|
|
9
9
|
SolaraViz,
|
|
10
|
+
SpaceRenderer,
|
|
10
11
|
make_plot_component,
|
|
11
|
-
make_space_component,
|
|
12
12
|
)
|
|
13
|
+
from mesa.visualization.components import AgentPortrayalStyle
|
|
13
14
|
|
|
14
15
|
COP_COLOR = "#000000"
|
|
15
16
|
|
|
@@ -24,14 +25,12 @@ def citizen_cop_portrayal(agent):
|
|
|
24
25
|
if agent is None:
|
|
25
26
|
return
|
|
26
27
|
|
|
27
|
-
portrayal =
|
|
28
|
-
"size": 50,
|
|
29
|
-
}
|
|
28
|
+
portrayal = AgentPortrayalStyle(size=200)
|
|
30
29
|
|
|
31
30
|
if isinstance(agent, Citizen):
|
|
32
|
-
portrayal
|
|
31
|
+
portrayal.update(("color", agent_colors[agent.state]))
|
|
33
32
|
elif isinstance(agent, Cop):
|
|
34
|
-
portrayal
|
|
33
|
+
portrayal.update(("color", COP_COLOR))
|
|
35
34
|
|
|
36
35
|
return portrayal
|
|
37
36
|
|
|
@@ -51,7 +50,7 @@ model_params = {
|
|
|
51
50
|
},
|
|
52
51
|
"height": 40,
|
|
53
52
|
"width": 40,
|
|
54
|
-
"citizen_density": Slider("Initial Agent Density", 0.7, 0.
|
|
53
|
+
"citizen_density": Slider("Initial Agent Density", 0.7, 0.1, 0.9, 0.1),
|
|
55
54
|
"cop_density": Slider("Initial Cop Density", 0.04, 0.0, 0.1, 0.01),
|
|
56
55
|
"citizen_vision": Slider("Citizen Vision", 7, 1, 10, 1),
|
|
57
56
|
"cop_vision": Slider("Cop Vision", 7, 1, 10, 1),
|
|
@@ -59,19 +58,20 @@ model_params = {
|
|
|
59
58
|
"max_jail_term": Slider("Max Jail Term", 30, 0, 50, 1),
|
|
60
59
|
}
|
|
61
60
|
|
|
62
|
-
space_component = make_space_component(
|
|
63
|
-
citizen_cop_portrayal, post_process=post_process, draw_grid=False
|
|
64
|
-
)
|
|
65
61
|
|
|
66
62
|
chart_component = make_plot_component(
|
|
67
63
|
{state.name.lower(): agent_colors[state] for state in CitizenState}
|
|
68
64
|
)
|
|
69
65
|
|
|
70
66
|
epstein_model = EpsteinCivilViolence()
|
|
67
|
+
renderer = SpaceRenderer(epstein_model, backend="matplotlib")
|
|
68
|
+
renderer.draw_agents(citizen_cop_portrayal)
|
|
69
|
+
renderer.post_process = post_process
|
|
71
70
|
|
|
72
71
|
page = SolaraViz(
|
|
73
72
|
epstein_model,
|
|
74
|
-
|
|
73
|
+
renderer,
|
|
74
|
+
components=[chart_component],
|
|
75
75
|
model_params=model_params,
|
|
76
76
|
name="Epstein Civil Violence",
|
|
77
77
|
)
|
|
@@ -17,13 +17,11 @@ The Demographic Prisoner's Dilemma demonstrates how simple rules can lead to the
|
|
|
17
17
|
|
|
18
18
|
## How to Run
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
To run the model interactively, in this directory, run the following command
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
Launch the ``Demographic Prisoner's Dilemma Activation Schedule.ipynb`` notebook and run the code.
|
|
22
|
+
```
|
|
23
|
+
$ solara run app.py
|
|
24
|
+
```
|
|
27
25
|
|
|
28
26
|
## Files
|
|
29
27
|
|
|
@@ -6,20 +6,19 @@ from mesa.examples.advanced.pd_grid.model import PdGrid
|
|
|
6
6
|
from mesa.visualization import (
|
|
7
7
|
Slider,
|
|
8
8
|
SolaraViz,
|
|
9
|
+
SpaceRenderer,
|
|
9
10
|
make_plot_component,
|
|
10
|
-
make_space_component,
|
|
11
11
|
)
|
|
12
|
+
from mesa.visualization.components import AgentPortrayalStyle
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
def pd_agent_portrayal(agent):
|
|
15
16
|
"""
|
|
16
17
|
Portrayal function for rendering PD agents in the visualization.
|
|
17
18
|
"""
|
|
18
|
-
return
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"size": 25,
|
|
22
|
-
}
|
|
19
|
+
return AgentPortrayalStyle(
|
|
20
|
+
color="blue" if agent.move == "C" else "red", marker="s", size=25
|
|
21
|
+
)
|
|
23
22
|
|
|
24
23
|
|
|
25
24
|
# Model parameters
|
|
@@ -40,19 +39,19 @@ model_params = {
|
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
|
|
43
|
-
# Create grid visualization component using Altair
|
|
44
|
-
grid_viz = make_space_component(agent_portrayal=pd_agent_portrayal)
|
|
45
|
-
|
|
46
42
|
# Create plot for tracking cooperating agents over time
|
|
47
|
-
plot_component = make_plot_component("Cooperating_Agents")
|
|
43
|
+
plot_component = make_plot_component("Cooperating_Agents", backend="altair", grid=True)
|
|
48
44
|
|
|
49
45
|
# Initialize model
|
|
50
46
|
initial_model = PdGrid()
|
|
47
|
+
# Create grid and agent visualization component using Altair
|
|
48
|
+
renderer = SpaceRenderer(initial_model, backend="altair").render(pd_agent_portrayal)
|
|
51
49
|
|
|
52
50
|
# Create visualization with all components
|
|
53
51
|
page = SolaraViz(
|
|
54
52
|
model=initial_model,
|
|
55
|
-
|
|
53
|
+
renderer=renderer,
|
|
54
|
+
components=[plot_component],
|
|
56
55
|
model_params=model_params,
|
|
57
56
|
name="Spatial Prisoner's Dilemma",
|
|
58
57
|
)
|
|
@@ -42,14 +42,13 @@ The model demonstrates several Mesa concepts and features:
|
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
## How to Run
|
|
45
|
-
|
|
45
|
+
|
|
46
|
+
To run the model interactively, in this directory, run the following command
|
|
46
47
|
|
|
47
48
|
```
|
|
48
|
-
|
|
49
|
+
$ solara run app.py
|
|
49
50
|
```
|
|
50
51
|
|
|
51
|
-
Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and press Reset, then Run.
|
|
52
|
-
|
|
53
52
|
## Files
|
|
54
53
|
|
|
55
54
|
* `model.py`: The Sugarscape Constant Growback with Traders model.
|
|
@@ -58,7 +57,7 @@ Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and p
|
|
|
58
57
|
* `sugar_map.txt`: Provides sugar and spice landscape in raster type format.
|
|
59
58
|
* `tests.py`: Has tests to ensure that the model reproduces the results in shown in Growing Artificial Societies.
|
|
60
59
|
|
|
61
|
-
##
|
|
60
|
+
## Further Reading
|
|
62
61
|
|
|
63
62
|
- [Growing Artificial Societies](https://mitpress.mit.edu/9780262550253/growing-artificial-societies/)
|
|
64
63
|
- [Complexity Explorer Sugarscape with Traders Tutorial](https://www.complexityexplorer.org/courses/172-agent-based-models-with-python-an-introduction-to-mesa)
|
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
from mesa.examples.advanced.sugarscape_g1mt.model import SugarscapeG1mt
|
|
2
|
-
from mesa.visualization import Slider, SolaraViz, make_plot_component
|
|
3
|
-
from mesa.visualization.components
|
|
2
|
+
from mesa.visualization import Slider, SolaraViz, SpaceRenderer, make_plot_component
|
|
3
|
+
from mesa.visualization.components import AgentPortrayalStyle, PropertyLayerStyle
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def agent_portrayal(agent):
|
|
7
|
-
return
|
|
7
|
+
return AgentPortrayalStyle(
|
|
8
|
+
x=agent.cell.coordinate[0],
|
|
9
|
+
y=agent.cell.coordinate[1],
|
|
10
|
+
color="red",
|
|
11
|
+
marker="o",
|
|
12
|
+
size=10,
|
|
13
|
+
zorder=1,
|
|
14
|
+
)
|
|
8
15
|
|
|
9
16
|
|
|
10
|
-
propertylayer_portrayal
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
17
|
+
def propertylayer_portrayal(layer):
|
|
18
|
+
if layer.name == "sugar":
|
|
19
|
+
return PropertyLayerStyle(
|
|
20
|
+
color="blue", alpha=0.8, colorbar=True, vmin=0, vmax=10
|
|
21
|
+
)
|
|
22
|
+
return PropertyLayerStyle(color="red", alpha=0.8, colorbar=True, vmin=0, vmax=10)
|
|
14
23
|
|
|
15
24
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
draw_grid=False,
|
|
21
|
-
)
|
|
25
|
+
def post_process(chart):
|
|
26
|
+
chart = chart.properties(width=400, height=400)
|
|
27
|
+
return chart
|
|
28
|
+
|
|
22
29
|
|
|
23
30
|
model_params = {
|
|
24
31
|
"seed": {
|
|
@@ -47,12 +54,23 @@ model_params = {
|
|
|
47
54
|
|
|
48
55
|
model = SugarscapeG1mt()
|
|
49
56
|
|
|
57
|
+
# Here, the renderer uses the Altair backend, while the plot components
|
|
58
|
+
# use the Matplotlib backend.
|
|
59
|
+
# Both can be mixed and matched to enhance the visuals of your model.
|
|
60
|
+
renderer = SpaceRenderer(model, backend="altair").render(
|
|
61
|
+
agent_portrayal=agent_portrayal,
|
|
62
|
+
propertylayer_portrayal=propertylayer_portrayal,
|
|
63
|
+
post_process=post_process,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Note: It is advised to switch the pages after pausing the model
|
|
67
|
+
# on the Solara dashboard.
|
|
50
68
|
page = SolaraViz(
|
|
51
69
|
model,
|
|
70
|
+
renderer,
|
|
52
71
|
components=[
|
|
53
|
-
|
|
54
|
-
make_plot_component("
|
|
55
|
-
make_plot_component("Price"),
|
|
72
|
+
make_plot_component("#Traders", page=1),
|
|
73
|
+
make_plot_component("Price", page=1),
|
|
56
74
|
],
|
|
57
75
|
model_params=model_params,
|
|
58
76
|
name="Sugarscape {G1, M, T}",
|