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.

Files changed (58) hide show
  1. mesa/__init__.py +1 -1
  2. mesa/agent.py +9 -7
  3. mesa/datacollection.py +1 -1
  4. mesa/examples/README.md +1 -1
  5. mesa/examples/__init__.py +2 -0
  6. mesa/examples/advanced/alliance_formation/Readme.md +50 -0
  7. mesa/examples/advanced/alliance_formation/__init__ .py +0 -0
  8. mesa/examples/advanced/alliance_formation/agents.py +20 -0
  9. mesa/examples/advanced/alliance_formation/app.py +71 -0
  10. mesa/examples/advanced/alliance_formation/model.py +184 -0
  11. mesa/examples/advanced/epstein_civil_violence/app.py +11 -11
  12. mesa/examples/advanced/pd_grid/Readme.md +4 -6
  13. mesa/examples/advanced/pd_grid/app.py +10 -11
  14. mesa/examples/advanced/sugarscape_g1mt/Readme.md +4 -5
  15. mesa/examples/advanced/sugarscape_g1mt/app.py +34 -16
  16. mesa/examples/advanced/wolf_sheep/Readme.md +2 -17
  17. mesa/examples/advanced/wolf_sheep/app.py +21 -18
  18. mesa/examples/basic/boid_flockers/Readme.md +6 -1
  19. mesa/examples/basic/boid_flockers/app.py +15 -11
  20. mesa/examples/basic/boltzmann_wealth_model/Readme.md +2 -12
  21. mesa/examples/basic/boltzmann_wealth_model/app.py +39 -32
  22. mesa/examples/basic/conways_game_of_life/Readme.md +1 -9
  23. mesa/examples/basic/conways_game_of_life/app.py +13 -16
  24. mesa/examples/basic/schelling/Readme.md +2 -10
  25. mesa/examples/basic/schelling/agents.py +9 -3
  26. mesa/examples/basic/schelling/app.py +50 -3
  27. mesa/examples/basic/schelling/model.py +2 -0
  28. mesa/examples/basic/schelling/resources/blue_happy.png +0 -0
  29. mesa/examples/basic/schelling/resources/blue_unhappy.png +0 -0
  30. mesa/examples/basic/schelling/resources/orange_happy.png +0 -0
  31. mesa/examples/basic/schelling/resources/orange_unhappy.png +0 -0
  32. mesa/examples/basic/virus_on_network/Readme.md +0 -4
  33. mesa/examples/basic/virus_on_network/app.py +31 -14
  34. mesa/experimental/__init__.py +2 -2
  35. mesa/experimental/continuous_space/continuous_space.py +1 -1
  36. mesa/experimental/meta_agents/__init__.py +25 -0
  37. mesa/experimental/meta_agents/meta_agent.py +387 -0
  38. mesa/model.py +3 -3
  39. mesa/space.py +4 -1
  40. mesa/visualization/__init__.py +2 -0
  41. mesa/visualization/backends/__init__.py +23 -0
  42. mesa/visualization/backends/abstract_renderer.py +97 -0
  43. mesa/visualization/backends/altair_backend.py +440 -0
  44. mesa/visualization/backends/matplotlib_backend.py +419 -0
  45. mesa/visualization/components/__init__.py +28 -8
  46. mesa/visualization/components/altair_components.py +86 -0
  47. mesa/visualization/components/matplotlib_components.py +4 -2
  48. mesa/visualization/components/portrayal_components.py +120 -0
  49. mesa/visualization/mpl_space_drawing.py +292 -129
  50. mesa/visualization/solara_viz.py +274 -32
  51. mesa/visualization/space_drawers.py +797 -0
  52. mesa/visualization/space_renderer.py +399 -0
  53. {mesa-3.2.0.dev0.dist-info → mesa-3.3.0.dist-info}/METADATA +13 -4
  54. {mesa-3.2.0.dev0.dist-info → mesa-3.3.0.dist-info}/RECORD +57 -40
  55. mesa/examples/advanced/sugarscape_g1mt/tests.py +0 -69
  56. {mesa-3.2.0.dev0.dist-info → mesa-3.3.0.dist-info}/WHEEL +0 -0
  57. {mesa-3.2.0.dev0.dist-info → mesa-3.3.0.dist-info}/licenses/LICENSE +0 -0
  58. {mesa-3.2.0.dev0.dist-info → mesa-3.3.0.dist-info}/licenses/NOTICE +0 -0
mesa/__init__.py CHANGED
@@ -24,7 +24,7 @@ __all__ = [
24
24
  ]
25
25
 
26
26
  __title__ = "mesa"
27
- __version__ = "3.2.0.dev"
27
+ __version__ = "3.3.0"
28
28
  __license__ = "Apache 2.0"
29
29
  _this_year = datetime.datetime.now(tz=datetime.UTC).date().year
30
30
  __copyright__ = f"Copyright {_this_year} Project Mesa Team"
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: passed on to super
57
- kwargs: passed on to super
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
- """Helper class to make default arguments act as if they are in a list of length N."""
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({agent: None for agent in agents})
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 = {entry: None for entry in weakrefs}
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({agent: None for agent in agents})
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
@@ -300,7 +300,7 @@ class DataCollector:
300
300
  if agent_type in agent_types:
301
301
  agents = model.agents_by_type[agent_type]
302
302
  else:
303
- from mesa import Agent
303
+ from mesa import Agent # noqa: PLC0415
304
304
 
305
305
  if issubclass(agent_type, Agent):
306
306
  agents = [
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/intro_tutorial.html) on making a simple model of how a highly-skewed wealth distribution can emerge from simple rules.
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["color"] = agent_colors[agent.state]
31
+ portrayal.update(("color", agent_colors[agent.state]))
33
32
  elif isinstance(agent, Cop):
34
- portrayal["color"] = COP_COLOR
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.0, 0.9, 0.1),
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
- components=[space_component, chart_component],
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
- ##### 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
 
@@ -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
- "color": "blue" if agent.move == "C" else "red",
20
- "marker": "s", # square marker
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
- components=[grid_viz, plot_component],
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
- 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,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.matplotlib_components import make_mpl_space_component
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 {"marker": "o", "color": "red", "size": 10}
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
- "sugar": {"color": "blue", "alpha": 0.8, "colorbar": True, "vmin": 0, "vmax": 10},
12
- "spice": {"color": "red", "alpha": 0.8, "colorbar": True, "vmin": 0, "vmax": 10},
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
- sugarscape_space = make_mpl_space_component(
17
- agent_portrayal=agent_portrayal,
18
- propertylayer_portrayal=propertylayer_portrayal,
19
- post_process=None,
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
- sugarscape_space,
54
- make_plot_component("#Traders"),
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}",