Mesa 3.1.5__py3-none-any.whl → 3.2.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.
Files changed (64) hide show
  1. mesa/__init__.py +3 -1
  2. mesa/agent.py +26 -9
  3. mesa/discrete_space/__init__.py +50 -0
  4. mesa/{experimental/cell_space → discrete_space}/cell.py +29 -10
  5. mesa/{experimental/cell_space → discrete_space}/cell_agent.py +1 -1
  6. mesa/{experimental/cell_space → discrete_space}/cell_collection.py +3 -3
  7. mesa/{experimental/cell_space → discrete_space}/discrete_space.py +65 -3
  8. mesa/{experimental/cell_space → discrete_space}/grid.py +2 -2
  9. mesa/{experimental/cell_space → discrete_space}/network.py +22 -2
  10. mesa/{experimental/cell_space → discrete_space}/property_layer.py +1 -10
  11. mesa/{experimental/cell_space → discrete_space}/voronoi.py +2 -2
  12. mesa/examples/README.md +1 -1
  13. mesa/examples/__init__.py +2 -0
  14. mesa/examples/advanced/alliance_formation/Readme.md +50 -0
  15. mesa/examples/advanced/alliance_formation/__init__ .py +0 -0
  16. mesa/examples/advanced/alliance_formation/agents.py +20 -0
  17. mesa/examples/advanced/alliance_formation/app.py +71 -0
  18. mesa/examples/advanced/alliance_formation/model.py +184 -0
  19. mesa/examples/advanced/epstein_civil_violence/agents.py +1 -1
  20. mesa/examples/advanced/epstein_civil_violence/model.py +1 -1
  21. mesa/examples/advanced/pd_grid/Readme.md +4 -6
  22. mesa/examples/advanced/pd_grid/agents.py +1 -1
  23. mesa/examples/advanced/pd_grid/model.py +1 -1
  24. mesa/examples/advanced/sugarscape_g1mt/Readme.md +4 -5
  25. mesa/examples/advanced/sugarscape_g1mt/agents.py +1 -1
  26. mesa/examples/advanced/sugarscape_g1mt/model.py +2 -2
  27. mesa/examples/advanced/wolf_sheep/Readme.md +2 -17
  28. mesa/examples/advanced/wolf_sheep/agents.py +1 -1
  29. mesa/examples/advanced/wolf_sheep/app.py +2 -1
  30. mesa/examples/advanced/wolf_sheep/model.py +1 -1
  31. mesa/examples/basic/boid_flockers/Readme.md +6 -1
  32. mesa/examples/basic/boid_flockers/agents.py +1 -0
  33. mesa/examples/basic/boid_flockers/app.py +17 -2
  34. mesa/examples/basic/boid_flockers/model.py +12 -0
  35. mesa/examples/basic/boltzmann_wealth_model/Readme.md +2 -12
  36. mesa/examples/basic/boltzmann_wealth_model/agents.py +6 -11
  37. mesa/examples/basic/boltzmann_wealth_model/app.py +2 -2
  38. mesa/examples/basic/boltzmann_wealth_model/model.py +7 -11
  39. mesa/examples/basic/conways_game_of_life/Readme.md +1 -9
  40. mesa/examples/basic/conways_game_of_life/agents.py +13 -5
  41. mesa/examples/basic/conways_game_of_life/model.py +10 -7
  42. mesa/examples/basic/schelling/Readme.md +0 -8
  43. mesa/examples/basic/schelling/agents.py +13 -8
  44. mesa/examples/basic/schelling/model.py +6 -9
  45. mesa/examples/basic/virus_on_network/Readme.md +0 -4
  46. mesa/examples/basic/virus_on_network/agents.py +13 -17
  47. mesa/examples/basic/virus_on_network/model.py +20 -24
  48. mesa/experimental/__init__.py +2 -2
  49. mesa/experimental/cell_space/__init__.py +18 -8
  50. mesa/experimental/meta_agents/__init__.py +25 -0
  51. mesa/experimental/meta_agents/meta_agent.py +387 -0
  52. mesa/model.py +3 -3
  53. mesa/space.py +1 -12
  54. mesa/visualization/__init__.py +2 -0
  55. mesa/visualization/command_console.py +482 -0
  56. mesa/visualization/components/altair_components.py +276 -16
  57. mesa/visualization/mpl_space_drawing.py +17 -9
  58. mesa/visualization/solara_viz.py +150 -21
  59. {mesa-3.1.5.dist-info → mesa-3.2.0.dist-info}/METADATA +12 -8
  60. mesa-3.2.0.dist-info/RECORD +105 -0
  61. mesa-3.1.5.dist-info/RECORD +0 -96
  62. {mesa-3.1.5.dist-info → mesa-3.2.0.dist-info}/WHEEL +0 -0
  63. {mesa-3.1.5.dist-info → mesa-3.2.0.dist-info}/licenses/LICENSE +0 -0
  64. {mesa-3.1.5.dist-info → mesa-3.2.0.dist-info}/licenses/NOTICE +0 -0
@@ -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)
@@ -10,7 +10,7 @@ class CitizenState(Enum):
10
10
  ARRESTED = 3
11
11
 
12
12
 
13
- class EpsteinAgent(mesa.experimental.cell_space.CellAgent):
13
+ class EpsteinAgent(mesa.discrete_space.CellAgent):
14
14
  def update_neighbors(self):
15
15
  """
16
16
  Look around and see who my neighbors are
@@ -53,7 +53,7 @@ class EpsteinCivilViolence(mesa.Model):
53
53
  self.movement = movement
54
54
  self.max_iters = max_iters
55
55
 
56
- self.grid = mesa.experimental.cell_space.OrthogonalVonNeumannGrid(
56
+ self.grid = mesa.discrete_space.OrthogonalVonNeumannGrid(
57
57
  (width, height), capacity=1, torus=True, random=self.random
58
58
  )
59
59
 
@@ -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
- ##### Web based model simulation
20
+ To run the model interactively, in this directory, run the following command
21
21
 
22
- To run the model interactively, run ``solara run app.py`` in this directory.
23
-
24
- ##### Jupyter Notebook
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
 
@@ -1,4 +1,4 @@
1
- from mesa.experimental.cell_space import CellAgent
1
+ from mesa.discrete_space import CellAgent
2
2
 
3
3
 
4
4
  class PDAgent(CellAgent):
@@ -1,6 +1,6 @@
1
1
  import mesa
2
+ from mesa.discrete_space import OrthogonalMooreGrid
2
3
  from mesa.examples.advanced.pd_grid.agents import PDAgent
3
- from mesa.experimental.cell_space import OrthogonalMooreGrid
4
4
 
5
5
 
6
6
  class PdGrid(mesa.Model):
@@ -42,14 +42,13 @@ The model demonstrates several Mesa concepts and features:
42
42
 
43
43
 
44
44
  ## How to Run
45
- To run the model interactively:
45
+
46
+ To run the model interactively, in this directory, run the following command
46
47
 
47
48
  ```
48
- $ solara run app.py
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
- ## Additional Resources
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,6 +1,6 @@
1
1
  import math
2
2
 
3
- from mesa.experimental.cell_space import CellAgent
3
+ from mesa.discrete_space import CellAgent
4
4
 
5
5
 
6
6
  # Helper function
@@ -3,9 +3,9 @@ from pathlib import Path
3
3
  import numpy as np
4
4
 
5
5
  import mesa
6
+ from mesa.discrete_space import OrthogonalVonNeumannGrid
7
+ from mesa.discrete_space.property_layer import PropertyLayer
6
8
  from mesa.examples.advanced.sugarscape_g1mt.agents import Trader
7
- from mesa.experimental.cell_space import OrthogonalVonNeumannGrid
8
- from mesa.experimental.cell_space.property_layer import PropertyLayer
9
9
 
10
10
 
11
11
  # Helper Functions
@@ -14,29 +14,14 @@ The model is tests and demonstrates several Mesa concepts and features:
14
14
  - Writing a model composed of multiple files.
15
15
  - Dynamically adding and removing agents from the schedule
16
16
 
17
- ## Installation
18
-
19
- To install the dependencies use pip and the requirements.txt in this directory. e.g.
20
-
21
- ```
22
- # First, we clone the Mesa repo
23
- $ git clone https://github.com/projectmesa/mesa.git
24
- $ cd mesa
25
- # Then we cd to the example directory
26
- $ cd examples/wolf_sheep
27
- $ pip install -r requirements.txt
28
- ```
29
-
30
17
  ## How to Run
31
18
 
32
- To run the model interactively, run ``mesa runserver`` in this directory. e.g.
19
+ To run the model interactively, in this directory, run the following command
33
20
 
34
21
  ```
35
- $ mesa runserver
22
+ $ solara run app.py
36
23
  ```
37
24
 
38
- Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and press Reset, then Run.
39
-
40
25
  ## Files
41
26
 
42
27
  * ``wolf_sheep/random_walk.py``: This defines the ``RandomWalker`` agent, which implements the behavior of moving randomly across a grid, one cell at a time. Both the Wolf and Sheep agents will inherit from it.
@@ -1,4 +1,4 @@
1
- from mesa.experimental.cell_space import CellAgent, FixedAgent
1
+ from mesa.discrete_space import CellAgent, FixedAgent
2
2
 
3
3
 
4
4
  class Animal(CellAgent):
@@ -2,6 +2,7 @@ from mesa.examples.advanced.wolf_sheep.agents import GrassPatch, Sheep, Wolf
2
2
  from mesa.examples.advanced.wolf_sheep.model import WolfSheep
3
3
  from mesa.experimental.devs import ABMSimulator
4
4
  from mesa.visualization import (
5
+ CommandConsole,
5
6
  Slider,
6
7
  SolaraViz,
7
8
  make_plot_component,
@@ -87,7 +88,7 @@ model = WolfSheep(simulator=simulator, grass=True)
87
88
 
88
89
  page = SolaraViz(
89
90
  model,
90
- components=[space_component, lineplot_component],
91
+ components=[space_component, lineplot_component, CommandConsole],
91
92
  model_params=model_params,
92
93
  name="Wolf Sheep",
93
94
  simulator=simulator,
@@ -13,8 +13,8 @@ import math
13
13
 
14
14
  from mesa import Model
15
15
  from mesa.datacollection import DataCollector
16
+ from mesa.discrete_space import OrthogonalVonNeumannGrid
16
17
  from mesa.examples.advanced.wolf_sheep.agents import GrassPatch, Sheep, Wolf
17
- from mesa.experimental.cell_space import OrthogonalVonNeumannGrid
18
18
  from mesa.experimental.devs import ABMSimulator
19
19
 
20
20
 
@@ -8,7 +8,12 @@ This model tests Mesa's continuous space feature, and uses numpy arrays to repre
8
8
 
9
9
  ## How to Run
10
10
 
11
- * To launch the visualization interactively, run ``solara run app.py`` in this directory.It will automatically open a browser page.
11
+ To run the model interactively, in this directory, run the following command
12
+
13
+ ```
14
+ $ solara run app.py
15
+ ```
16
+
12
17
 
13
18
  ## Files
14
19
 
@@ -58,6 +58,7 @@ class Boid(ContinuousSpaceAgent):
58
58
  self.separate_factor = separate
59
59
  self.match_factor = match
60
60
  self.neighbors = []
61
+ self.angle = 0.0 # represents the angle at which the boid is moving
61
62
 
62
63
  def step(self):
63
64
  """Get the Boid's neighbors, compute the new vector, and move accordingly."""
@@ -1,19 +1,34 @@
1
1
  import os
2
2
  import sys
3
3
 
4
+ from matplotlib.markers import MarkerStyle
5
+
4
6
  sys.path.insert(0, os.path.abspath("../../../.."))
5
7
 
6
8
  from mesa.examples.basic.boid_flockers.model import BoidFlockers
7
9
  from mesa.visualization import Slider, SolaraViz, make_space_component
8
10
 
11
+ # Pre-compute markers for different angles (e.g., every 10 degrees)
12
+ MARKER_CACHE = {}
13
+ for angle in range(0, 360, 10):
14
+ marker = MarkerStyle(10)
15
+ marker._transform = marker.get_transform().rotate_deg(angle)
16
+ MARKER_CACHE[angle] = marker
17
+
9
18
 
10
19
  def boid_draw(agent):
11
20
  neighbors = len(agent.neighbors)
12
21
 
22
+ # Calculate the angle
23
+ deg = agent.angle
24
+ # Round to nearest 10 degrees
25
+ rounded_deg = round(deg / 10) * 10 % 360
26
+
27
+ # using cached markers to speed things up
13
28
  if neighbors <= 1:
14
- return {"color": "red", "size": 20}
29
+ return {"color": "red", "size": 20, "marker": MARKER_CACHE[rounded_deg]}
15
30
  elif neighbors >= 2:
16
- return {"color": "green", "size": 20}
31
+ return {"color": "green", "size": 20, "marker": MARKER_CACHE[rounded_deg]}
17
32
 
18
33
 
19
34
  model_params = {
@@ -49,6 +49,9 @@ class BoidFlockers(Model):
49
49
  seed: Random seed for reproducibility (default: None)
50
50
  """
51
51
  super().__init__(seed=seed)
52
+ self.agent_angles = np.zeros(
53
+ population_size
54
+ ) # holds the angle representing the direction of all agents at a given step
52
55
 
53
56
  # Set up the space
54
57
  self.space = ContinuousSpace(
@@ -79,6 +82,14 @@ class BoidFlockers(Model):
79
82
  self.average_heading = None
80
83
  self.update_average_heading()
81
84
 
85
+ # vectorizing the calculation of angles for all agents
86
+ def calculate_angles(self):
87
+ d1 = np.array([agent.direction[0] for agent in self.agents])
88
+ d2 = np.array([agent.direction[1] for agent in self.agents])
89
+ self.agent_angles = np.degrees(np.arctan2(d1, d2))
90
+ for agent, angle in zip(self.agents, self.agent_angles):
91
+ agent.angle = angle
92
+
82
93
  def update_average_heading(self):
83
94
  """Calculate the average heading (direction) of all Boids."""
84
95
  if not self.agents:
@@ -96,3 +107,4 @@ class BoidFlockers(Model):
96
107
  """
97
108
  self.agents.shuffle_do("step")
98
109
  self.update_average_heading()
110
+ self.calculate_angles()
@@ -2,25 +2,18 @@
2
2
 
3
3
  ## Summary
4
4
 
5
- A simple model of agents exchanging wealth. All agents start with the same amount of money. Every step, each agent with one unit of money or more gives one unit of wealth to another random agent. This is the model described in the [Intro Tutorial](https://mesa.readthedocs.io/en/latest/tutorials/intro_tutorial.html), with the completed code.
6
-
7
- If you want to go over the step-by-step tutorial, please go and run the [Jupyter Notebook](https://github.com/projectmesa/mesa/blob/main/docs/tutorials/intro_tutorial.ipynb). The code here runs the finalized code in the last cells directly.
5
+ A simple model of agents exchanging wealth. All agents start with the same amount of money. Every step, each agent with one unit of money or more gives one unit of wealth to another random agent. Mesa's [Getting Started](https://mesa.readthedocs.io/latest/getting_started.html) section walks through the Boltzmann Wealth Model in a series of short introductory tutorials, starting with[Creating your First Model](https://mesa.readthedocs.io/latest/tutorials/0_first_model.html).
8
6
 
9
7
  As the model runs, the distribution of wealth among agents goes from being perfectly uniform (all agents have the same starting wealth), to highly skewed -- a small number have high wealth, more have none at all.
10
8
 
11
9
  ## How to Run
12
10
 
13
- To follow the tutorial example, launch the Jupyter Notebook and run the code in ``Introduction to Mesa Tutorial Code.ipynb`` which you can find in the main mesa repo [here](https://github.com/projectmesa/mesa/blob/main/docs/tutorials/intro_tutorial.ipynb)
14
-
15
-
16
- To launch the interactive server, as described in the [last section of the tutorial](https://mesa.readthedocs.io/en/latest/tutorials/intro_tutorial.html#adding-visualization), run:
11
+ To run the model interactively, in this directory, run the following command
17
12
 
18
13
  ```
19
14
  $ solara run app.py
20
15
  ```
21
16
 
22
- If your browser doesn't open automatically, point it to [http://127.0.0.1:8765/](http://127.0.0.1:8765/). When the visualization loads, click on the Play button.
23
-
24
17
 
25
18
  ## Files
26
19
 
@@ -46,9 +39,6 @@ Then, you can run the Streamlit app using the following command:
46
39
 
47
40
  ## Further Reading
48
41
 
49
- The full tutorial describing how the model is built can be found at:
50
- https://mesa.readthedocs.io/en/latest/tutorials/intro_tutorial.html
51
-
52
42
  This model is drawn from econophysics and presents a statistical mechanics approach to wealth distribution. Some examples of further reading on the topic can be found at:
53
43
 
54
44
  [Milakovic, M. A Statistical Equilibrium Model of Wealth Distribution. February, 2001.](https://editorialexpress.com/cgi-bin/conference/download.cgi?db_name=SCE2001&paper_id=214)
@@ -1,7 +1,7 @@
1
- from mesa import Agent
1
+ from mesa.discrete_space import CellAgent
2
2
 
3
3
 
4
- class MoneyAgent(Agent):
4
+ class MoneyAgent(CellAgent):
5
5
  """An agent with fixed initial wealth.
6
6
 
7
7
  Each agent starts with 1 unit of wealth and can give 1 unit to other agents
@@ -11,28 +11,23 @@ class MoneyAgent(Agent):
11
11
  wealth (int): The agent's current wealth (starts at 1)
12
12
  """
13
13
 
14
- def __init__(self, model):
14
+ def __init__(self, model, cell):
15
15
  """Create a new agent.
16
16
 
17
17
  Args:
18
18
  model (Model): The model instance that contains the agent
19
19
  """
20
20
  super().__init__(model)
21
+ self.cell = cell
21
22
  self.wealth = 1
22
23
 
23
24
  def move(self):
24
25
  """Move the agent to a random neighboring cell."""
25
- possible_steps = self.model.grid.get_neighborhood(
26
- self.pos, moore=True, include_center=False
27
- )
28
- new_position = self.random.choice(possible_steps)
29
- self.model.grid.move_agent(self, new_position)
26
+ self.cell = self.cell.neighborhood.select_random_cell()
30
27
 
31
28
  def give_money(self):
32
29
  """Give 1 unit of wealth to a random agent in the same cell."""
33
- cellmates = self.model.grid.get_cell_list_contents([self.pos])
34
- # Remove self from potential recipients
35
- cellmates.pop(cellmates.index(self))
30
+ cellmates = [a for a in self.cell.agents if a is not self]
36
31
 
37
32
  if cellmates: # Only give money if there are other agents present
38
33
  other = self.random.choice(cellmates)
@@ -1,12 +1,12 @@
1
1
  from mesa.examples.basic.boltzmann_wealth_model.model import BoltzmannWealth
2
- from mesa.mesa_logging import DEBUG, log_to_stderr
2
+ from mesa.mesa_logging import INFO, log_to_stderr
3
3
  from mesa.visualization import (
4
4
  SolaraViz,
5
5
  make_plot_component,
6
6
  make_space_component,
7
7
  )
8
8
 
9
- log_to_stderr(DEBUG)
9
+ log_to_stderr(INFO)
10
10
 
11
11
 
12
12
  def agent_portrayal(agent):
@@ -9,8 +9,8 @@ when they occupy the same cell.
9
9
 
10
10
  from mesa import Model
11
11
  from mesa.datacollection import DataCollector
12
+ from mesa.discrete_space import OrthogonalMooreGrid
12
13
  from mesa.examples.basic.boltzmann_wealth_model.agents import MoneyAgent
13
- from mesa.space import MultiGrid
14
14
 
15
15
 
16
16
  class BoltzmannWealth(Model):
@@ -39,22 +39,18 @@ class BoltzmannWealth(Model):
39
39
  super().__init__(seed=seed)
40
40
 
41
41
  self.num_agents = n
42
- self.grid = MultiGrid(width, height, torus=True)
42
+ self.grid = OrthogonalMooreGrid((width, height), random=self.random)
43
43
 
44
44
  # Set up data collection
45
45
  self.datacollector = DataCollector(
46
46
  model_reporters={"Gini": self.compute_gini},
47
47
  agent_reporters={"Wealth": "wealth"},
48
48
  )
49
-
50
- # Create and place the agents
51
- for _ in range(self.num_agents):
52
- agent = MoneyAgent(self)
53
-
54
- # Add agent to random grid cell
55
- x = self.random.randrange(self.grid.width)
56
- y = self.random.randrange(self.grid.height)
57
- self.grid.place_agent(agent, (x, y))
49
+ MoneyAgent.create_agents(
50
+ self,
51
+ self.num_agents,
52
+ self.random.choices(self.grid.all_cells.cells, k=self.num_agents),
53
+ )
58
54
 
59
55
  self.running = True
60
56
  self.datacollector.collect(self)
@@ -9,20 +9,12 @@ The "game" is a zero-player game, meaning that its evolution is determined by it
9
9
 
10
10
  ## How to Run
11
11
 
12
- To run the model interactively you can use either the streamlit or solara version. For solara, you use
12
+ To run the model interactively, in this directory, run the following command
13
13
 
14
14
  ```
15
15
  $ solara run app.py
16
16
  ```
17
17
 
18
- For streamlit, you need
19
-
20
- ```
21
- $ streamlit run st_app.py
22
- ```
23
-
24
- This will open your browser and show you the controls. You can start the model by hitting the run button.
25
-
26
18
  ## Files
27
19
 
28
20
  * ``agents.py``: Defines the behavior of an individual cell, which can be in two states: DEAD or ALIVE.
@@ -1,16 +1,24 @@
1
- from mesa import Agent
1
+ from mesa.discrete_space import FixedAgent
2
2
 
3
3
 
4
- class Cell(Agent):
4
+ class Cell(FixedAgent):
5
5
  """Represents a single ALIVE or DEAD cell in the simulation."""
6
6
 
7
7
  DEAD = 0
8
8
  ALIVE = 1
9
9
 
10
- def __init__(self, pos, model, init_state=DEAD):
10
+ @property
11
+ def x(self):
12
+ return self.cell.coordinate[0]
13
+
14
+ @property
15
+ def y(self):
16
+ return self.cell.coordinate[1]
17
+
18
+ def __init__(self, model, cell, init_state=DEAD):
11
19
  """Create a cell, in the given state, at the given x, y position."""
12
20
  super().__init__(model)
13
- self.x, self.y = pos
21
+ self.cell = cell
14
22
  self.state = init_state
15
23
  self._next_state = None
16
24
 
@@ -20,7 +28,7 @@ class Cell(Agent):
20
28
 
21
29
  @property
22
30
  def neighbors(self):
23
- return self.model.grid.iter_neighbors((self.x, self.y), True)
31
+ return self.cell.neighborhood.agents
24
32
 
25
33
  def determine_state(self):
26
34
  """Compute if the cell will be dead or alive at the next tick. This is