Mesa 2.4.0__py3-none-any.whl → 3.0.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 (110) hide show
  1. mesa/__init__.py +3 -5
  2. mesa/agent.py +105 -92
  3. mesa/batchrunner.py +55 -31
  4. mesa/datacollection.py +10 -14
  5. mesa/examples/README.md +37 -0
  6. mesa/examples/__init__.py +21 -0
  7. mesa/examples/advanced/epstein_civil_violence/Epstein Civil Violence.ipynb +116 -0
  8. mesa/examples/advanced/epstein_civil_violence/Readme.md +34 -0
  9. mesa/examples/advanced/epstein_civil_violence/__init__.py +0 -0
  10. mesa/examples/advanced/epstein_civil_violence/agents.py +164 -0
  11. mesa/examples/advanced/epstein_civil_violence/app.py +73 -0
  12. mesa/examples/advanced/epstein_civil_violence/model.py +114 -0
  13. mesa/examples/advanced/pd_grid/Readme.md +43 -0
  14. mesa/examples/advanced/pd_grid/__init__.py +0 -0
  15. mesa/examples/advanced/pd_grid/agents.py +50 -0
  16. mesa/examples/advanced/pd_grid/analysis.ipynb +228 -0
  17. mesa/examples/advanced/pd_grid/app.py +54 -0
  18. mesa/examples/advanced/pd_grid/model.py +71 -0
  19. mesa/examples/advanced/sugarscape_g1mt/Readme.md +64 -0
  20. mesa/examples/advanced/sugarscape_g1mt/__init__.py +0 -0
  21. mesa/examples/advanced/sugarscape_g1mt/agents.py +344 -0
  22. mesa/examples/advanced/sugarscape_g1mt/app.py +62 -0
  23. mesa/examples/advanced/sugarscape_g1mt/model.py +180 -0
  24. mesa/examples/advanced/sugarscape_g1mt/sugar-map.txt +50 -0
  25. mesa/examples/advanced/sugarscape_g1mt/tests.py +69 -0
  26. mesa/examples/advanced/wolf_sheep/Readme.md +57 -0
  27. mesa/examples/advanced/wolf_sheep/__init__.py +0 -0
  28. mesa/examples/advanced/wolf_sheep/agents.py +102 -0
  29. mesa/examples/advanced/wolf_sheep/app.py +84 -0
  30. mesa/examples/advanced/wolf_sheep/model.py +137 -0
  31. mesa/examples/basic/__init__.py +0 -0
  32. mesa/examples/basic/boid_flockers/Readme.md +22 -0
  33. mesa/examples/basic/boid_flockers/__init__.py +0 -0
  34. mesa/examples/basic/boid_flockers/agents.py +71 -0
  35. mesa/examples/basic/boid_flockers/app.py +58 -0
  36. mesa/examples/basic/boid_flockers/model.py +69 -0
  37. mesa/examples/basic/boltzmann_wealth_model/Readme.md +56 -0
  38. mesa/examples/basic/boltzmann_wealth_model/__init__.py +0 -0
  39. mesa/examples/basic/boltzmann_wealth_model/agents.py +31 -0
  40. mesa/examples/basic/boltzmann_wealth_model/app.py +74 -0
  41. mesa/examples/basic/boltzmann_wealth_model/model.py +43 -0
  42. mesa/examples/basic/boltzmann_wealth_model/st_app.py +115 -0
  43. mesa/examples/basic/conways_game_of_life/Readme.md +39 -0
  44. mesa/examples/basic/conways_game_of_life/__init__.py +0 -0
  45. mesa/examples/basic/conways_game_of_life/agents.py +47 -0
  46. mesa/examples/basic/conways_game_of_life/app.py +51 -0
  47. mesa/examples/basic/conways_game_of_life/model.py +31 -0
  48. mesa/examples/basic/conways_game_of_life/st_app.py +72 -0
  49. mesa/examples/basic/schelling/Readme.md +40 -0
  50. mesa/examples/basic/schelling/__init__.py +0 -0
  51. mesa/examples/basic/schelling/agents.py +26 -0
  52. mesa/examples/basic/schelling/analysis.ipynb +205 -0
  53. mesa/examples/basic/schelling/app.py +42 -0
  54. mesa/examples/basic/schelling/model.py +59 -0
  55. mesa/examples/basic/virus_on_network/Readme.md +61 -0
  56. mesa/examples/basic/virus_on_network/__init__.py +0 -0
  57. mesa/examples/basic/virus_on_network/agents.py +69 -0
  58. mesa/examples/basic/virus_on_network/app.py +114 -0
  59. mesa/examples/basic/virus_on_network/model.py +96 -0
  60. mesa/experimental/UserParam.py +18 -7
  61. mesa/experimental/__init__.py +10 -2
  62. mesa/experimental/cell_space/__init__.py +16 -1
  63. mesa/experimental/cell_space/cell.py +93 -23
  64. mesa/experimental/cell_space/cell_agent.py +117 -21
  65. mesa/experimental/cell_space/cell_collection.py +56 -19
  66. mesa/experimental/cell_space/discrete_space.py +92 -8
  67. mesa/experimental/cell_space/grid.py +33 -9
  68. mesa/experimental/cell_space/network.py +15 -10
  69. mesa/experimental/cell_space/voronoi.py +257 -0
  70. mesa/experimental/components/altair.py +11 -2
  71. mesa/experimental/components/matplotlib.py +132 -26
  72. mesa/experimental/devs/__init__.py +2 -0
  73. mesa/experimental/devs/eventlist.py +54 -15
  74. mesa/experimental/devs/examples/epstein_civil_violence.py +69 -38
  75. mesa/experimental/devs/examples/wolf_sheep.py +42 -43
  76. mesa/experimental/devs/simulator.py +57 -16
  77. mesa/experimental/{jupyter_viz.py → solara_viz.py} +151 -99
  78. mesa/model.py +136 -78
  79. mesa/space.py +208 -148
  80. mesa/time.py +63 -80
  81. mesa/visualization/__init__.py +25 -6
  82. mesa/visualization/components/__init__.py +83 -0
  83. mesa/visualization/components/altair_components.py +188 -0
  84. mesa/visualization/components/matplotlib_components.py +175 -0
  85. mesa/visualization/mpl_space_drawing.py +593 -0
  86. mesa/visualization/solara_viz.py +458 -0
  87. mesa/visualization/user_param.py +69 -0
  88. mesa/visualization/utils.py +9 -0
  89. {mesa-2.4.0.dist-info → mesa-3.0.0.dist-info}/METADATA +62 -17
  90. mesa-3.0.0.dist-info/RECORD +95 -0
  91. mesa-3.0.0.dist-info/licenses/LICENSE +202 -0
  92. mesa-2.4.0.dist-info/licenses/LICENSE → mesa-3.0.0.dist-info/licenses/NOTICE +2 -2
  93. mesa/cookiecutter-mesa/cookiecutter.json +0 -8
  94. mesa/cookiecutter-mesa/hooks/post_gen_project.py +0 -11
  95. mesa/cookiecutter-mesa/{{cookiecutter.snake}}/README.md +0 -4
  96. mesa/cookiecutter-mesa/{{cookiecutter.snake}}/run.pytemplate +0 -3
  97. mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.pytemplate +0 -11
  98. mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/model.pytemplate +0 -60
  99. mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/server.pytemplate +0 -36
  100. mesa/flat/__init__.py +0 -6
  101. mesa/flat/visualization.py +0 -5
  102. mesa/main.py +0 -63
  103. mesa/visualization/ModularVisualization.py +0 -1
  104. mesa/visualization/TextVisualization.py +0 -1
  105. mesa/visualization/UserParam.py +0 -1
  106. mesa/visualization/modules.py +0 -1
  107. mesa-2.4.0.dist-info/RECORD +0 -45
  108. /mesa/{cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}} → examples/advanced}/__init__.py +0 -0
  109. {mesa-2.4.0.dist-info → mesa-3.0.0.dist-info}/WHEEL +0 -0
  110. {mesa-2.4.0.dist-info → mesa-3.0.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,22 @@
1
+ # Boids Flockers
2
+
3
+ ## Summary
4
+
5
+ An implementation of Craig Reynolds's Boids flocker model. Agents (simulated birds) try to fly towards the average position of their neighbors and in the same direction as them, while maintaining a minimum distance. This produces flocking behavior.
6
+
7
+ This model tests Mesa's continuous space feature, and uses numpy arrays to represent vectors.
8
+
9
+ ## How to Run
10
+
11
+ * To launch the visualization interactively, run ``solara run app.py`` in this directory.It will automatically open a browser page.
12
+
13
+ ## Files
14
+
15
+ * [model.py](model.py): Ccntains the Boid Model
16
+ * [agents.py](agents.py): Contains the Boid agent
17
+ * [app.py](app.py): Solara based Visualization code.
18
+
19
+ ## Further Reading
20
+
21
+ The following link can be visited for more information on the boid flockers model:
22
+ https://cs.stanford.edu/people/eroberts/courses/soco/projects/2008-09/modeling-natural-systems/boids.html
File without changes
@@ -0,0 +1,71 @@
1
+ import numpy as np
2
+
3
+ from mesa import Agent
4
+
5
+
6
+ class Boid(Agent):
7
+ """A Boid-style flocker agent.
8
+
9
+ The agent follows three behaviors to flock:
10
+ - Cohesion: steering towards neighboring agents.
11
+ - Separation: avoiding getting too close to any other agent.
12
+ - Alignment: try to fly in the same direction as the neighbors.
13
+
14
+ Boids have a vision that defines the radius in which they look for their
15
+ neighbors to flock with. Their speed (a scalar) and direction (a vector)
16
+ define their movement. Separation is their desired minimum distance from
17
+ any other Boid.
18
+ """
19
+
20
+ def __init__(
21
+ self,
22
+ model,
23
+ speed,
24
+ direction,
25
+ vision,
26
+ separation,
27
+ cohere=0.03,
28
+ separate=0.015,
29
+ match=0.05,
30
+ ):
31
+ """Create a new Boid flocker agent.
32
+
33
+ Args:
34
+ speed: Distance to move per step.
35
+ direction: numpy vector for the Boid's direction of movement.
36
+ vision: Radius to look around for nearby Boids.
37
+ separation: Minimum distance to maintain from other Boids.
38
+ cohere: the relative importance of matching neighbors' positions
39
+ separate: the relative importance of avoiding close neighbors
40
+ match: the relative importance of matching neighbors' headings
41
+ """
42
+ super().__init__(model)
43
+ self.speed = speed
44
+ self.direction = direction
45
+ self.vision = vision
46
+ self.separation = separation
47
+ self.cohere_factor = cohere
48
+ self.separate_factor = separate
49
+ self.match_factor = match
50
+ self.neighbors = None
51
+
52
+ def step(self):
53
+ """Get the Boid's neighbors, compute the new vector, and move accordingly."""
54
+ self.neighbors = self.model.space.get_neighbors(self.pos, self.vision, False)
55
+ n = 0
56
+ match_vector, separation_vector, cohere = np.zeros((3, 2))
57
+ for neighbor in self.neighbors:
58
+ n += 1
59
+ heading = self.model.space.get_heading(self.pos, neighbor.pos)
60
+ cohere += heading
61
+ if self.model.space.get_distance(self.pos, neighbor.pos) < self.separation:
62
+ separation_vector -= heading
63
+ match_vector += neighbor.direction
64
+ n = max(n, 1)
65
+ cohere = cohere * self.cohere_factor
66
+ separation_vector = separation_vector * self.separate_factor
67
+ match_vector = match_vector * self.match_factor
68
+ self.direction += (cohere + separation_vector + match_vector) / n
69
+ self.direction /= np.linalg.norm(self.direction)
70
+ new_pos = self.pos + self.direction * self.speed
71
+ self.model.space.move_agent(self, new_pos)
@@ -0,0 +1,58 @@
1
+ from mesa.examples.basic.boid_flockers.model import BoidFlockers
2
+ from mesa.visualization import Slider, SolaraViz, make_space_component
3
+
4
+
5
+ def boid_draw(agent):
6
+ if not agent.neighbors: # Only for the first Frame
7
+ neighbors = len(agent.model.space.get_neighbors(agent.pos, agent.vision, False))
8
+ else:
9
+ neighbors = len(agent.neighbors)
10
+
11
+ if neighbors <= 1:
12
+ return {"color": "red", "size": 20}
13
+ elif neighbors >= 2:
14
+ return {"color": "green", "size": 20}
15
+
16
+
17
+ model_params = {
18
+ "population": Slider(
19
+ label="Number of boids",
20
+ value=100,
21
+ min=10,
22
+ max=200,
23
+ step=10,
24
+ ),
25
+ "width": 100,
26
+ "height": 100,
27
+ "speed": Slider(
28
+ label="Speed of Boids",
29
+ value=5,
30
+ min=1,
31
+ max=20,
32
+ step=1,
33
+ ),
34
+ "vision": Slider(
35
+ label="Vision of Bird (radius)",
36
+ value=10,
37
+ min=1,
38
+ max=50,
39
+ step=1,
40
+ ),
41
+ "separation": Slider(
42
+ label="Minimum Separation",
43
+ value=2,
44
+ min=1,
45
+ max=20,
46
+ step=1,
47
+ ),
48
+ }
49
+
50
+ model = BoidFlockers()
51
+
52
+ page = SolaraViz(
53
+ model,
54
+ [make_space_component(agent_portrayal=boid_draw, backend="matplotlib")],
55
+ model_params=model_params,
56
+ name="Boid Flocking Model",
57
+ )
58
+ page # noqa
@@ -0,0 +1,69 @@
1
+ """Flockers.
2
+ =============================================================
3
+ A Mesa implementation of Craig Reynolds's Boids flocker model.
4
+ Uses numpy arrays to represent vectors.
5
+ """
6
+
7
+ import numpy as np
8
+
9
+ import mesa
10
+ from mesa.examples.basic.boid_flockers.agents import Boid
11
+
12
+
13
+ class BoidFlockers(mesa.Model):
14
+ """Flocker model class. Handles agent creation, placement and scheduling."""
15
+
16
+ def __init__(
17
+ self,
18
+ seed=None,
19
+ population=100,
20
+ width=100,
21
+ height=100,
22
+ vision=10,
23
+ speed=1,
24
+ separation=1,
25
+ cohere=0.03,
26
+ separate=0.015,
27
+ match=0.05,
28
+ ):
29
+ """Create a new Flockers model.
30
+
31
+ Args:
32
+ population: Number of Boids
33
+ width, height: Size of the space.
34
+ speed: How fast should the Boids move.
35
+ vision: How far around should each Boid look for its neighbors
36
+ separation: What's the minimum distance each Boid will attempt to
37
+ keep from any other
38
+ cohere, separate, match: factors for the relative importance of
39
+ the three drives.
40
+ """
41
+ super().__init__(seed=seed)
42
+ self.population = population
43
+ self.vision = vision
44
+ self.speed = speed
45
+ self.separation = separation
46
+
47
+ self.space = mesa.space.ContinuousSpace(width, height, True)
48
+ self.factors = {"cohere": cohere, "separate": separate, "match": match}
49
+ self.make_agents()
50
+
51
+ def make_agents(self):
52
+ """Create self.population agents, with random positions and starting headings."""
53
+ for _ in range(self.population):
54
+ x = self.random.random() * self.space.x_max
55
+ y = self.random.random() * self.space.y_max
56
+ pos = np.array((x, y))
57
+ direction = np.random.random(2) * 2 - 1
58
+ boid = Boid(
59
+ model=self,
60
+ speed=self.speed,
61
+ direction=direction,
62
+ vision=self.vision,
63
+ separation=self.separation,
64
+ **self.factors,
65
+ )
66
+ self.space.place_agent(boid, pos)
67
+
68
+ def step(self):
69
+ self.agents.shuffle_do("step")
@@ -0,0 +1,56 @@
1
+ # Boltzmann Wealth Model (Tutorial)
2
+
3
+ ## Summary
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.
8
+
9
+ 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
+
11
+ ## How to Run
12
+
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:
17
+
18
+ ```
19
+ $ solara run app.py
20
+ ```
21
+
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
+
25
+ ## Files
26
+
27
+ * ``model.py``: Final version of the model.
28
+ * ``agents.py``: Final version of the agent.
29
+ * ``app.py``: Code for the interactive visualization.
30
+
31
+ ## Optional
32
+
33
+ An optional visualization is also provided using Streamlit, which is another popular Python library for creating interactive web applications.
34
+
35
+ To run the Streamlit app, you will need to install the `streamlit` and `altair` libraries:
36
+
37
+ ```
38
+ $ pip install streamlit altair
39
+ ```
40
+
41
+ Then, you can run the Streamlit app using the following command:
42
+
43
+ ```
44
+ $ streamlit run st_app.py
45
+ ```
46
+
47
+ ## Further Reading
48
+
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
+ 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
+
54
+ [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)
55
+
56
+ [Dragulescu, A and Yakovenko, V. Statistical Mechanics of Money, Income, and Wealth: A Short Survey. November, 2002](http://arxiv.org/pdf/cond-mat/0211175v1.pdf)
File without changes
@@ -0,0 +1,31 @@
1
+ from mesa import Agent
2
+
3
+
4
+ class MoneyAgent(Agent):
5
+ """An agent with fixed initial wealth."""
6
+
7
+ def __init__(self, model):
8
+ super().__init__(model)
9
+ self.wealth = 1
10
+
11
+ def move(self):
12
+ possible_steps = self.model.grid.get_neighborhood(
13
+ self.pos, moore=True, include_center=False
14
+ )
15
+ new_position = self.random.choice(possible_steps)
16
+ self.model.grid.move_agent(self, new_position)
17
+
18
+ def give_money(self):
19
+ cellmates = self.model.grid.get_cell_list_contents([self.pos])
20
+ cellmates.pop(
21
+ cellmates.index(self)
22
+ ) # Ensure agent is not giving money to itself
23
+ if len(cellmates) > 0:
24
+ other = self.random.choice(cellmates)
25
+ other.wealth += 1
26
+ self.wealth -= 1
27
+
28
+ def step(self):
29
+ self.move()
30
+ if self.wealth > 0:
31
+ self.give_money()
@@ -0,0 +1,74 @@
1
+ from mesa.examples.basic.boltzmann_wealth_model.model import BoltzmannWealthModel
2
+ from mesa.visualization import (
3
+ SolaraViz,
4
+ make_plot_component,
5
+ make_space_component,
6
+ )
7
+
8
+
9
+ def agent_portrayal(agent):
10
+ color = agent.wealth # we are using a colormap to translate wealth to color
11
+ return {"color": color}
12
+
13
+
14
+ model_params = {
15
+ "n": {
16
+ "type": "SliderInt",
17
+ "value": 50,
18
+ "label": "Number of agents:",
19
+ "min": 10,
20
+ "max": 100,
21
+ "step": 1,
22
+ },
23
+ "seed": {
24
+ "type": "InputText",
25
+ "value": 42,
26
+ "label": "Random Seed",
27
+ },
28
+ "width": 10,
29
+ "height": 10,
30
+ }
31
+
32
+
33
+ def post_process(ax):
34
+ ax.get_figure().colorbar(ax.collections[0], label="wealth", ax=ax)
35
+
36
+
37
+ # Create initial model instance
38
+ model = BoltzmannWealthModel(50, 10, 10)
39
+
40
+ # Create visualization elements. The visualization elements are solara components
41
+ # that receive the model instance as a "prop" and display it in a certain way.
42
+ # Under the hood these are just classes that receive the model instance.
43
+ # You can also author your own visualization elements, which can also be functions
44
+ # that receive the model instance and return a valid solara component.
45
+
46
+ SpaceGraph = make_space_component(
47
+ agent_portrayal, cmap="viridis", vmin=0, vmax=10, post_process=post_process
48
+ )
49
+ GiniPlot = make_plot_component("Gini")
50
+
51
+ # Create the SolaraViz page. This will automatically create a server and display the
52
+ # visualization elements in a web browser.
53
+ # Display it using the following command in the example directory:
54
+ # solara run app.py
55
+ # It will automatically update and display any changes made to this file
56
+ page = SolaraViz(
57
+ model,
58
+ components=[SpaceGraph, GiniPlot],
59
+ model_params=model_params,
60
+ name="Boltzmann Wealth Model",
61
+ )
62
+ page # noqa
63
+
64
+
65
+ # In a notebook environment, we can also display the visualization elements directly
66
+ # SpaceGraph(model1)
67
+ # GiniPlot(model1)
68
+
69
+ # The plots will be static. If you want to pick up model steps,
70
+ # you have to make the model reactive first
71
+ # reactive_model = solara.reactive(model1)
72
+ # SpaceGraph(reactive_model)
73
+ # In a different notebook block:
74
+ # reactive_model.value.step()
@@ -0,0 +1,43 @@
1
+ import mesa
2
+ from mesa.examples.basic.boltzmann_wealth_model.agents import MoneyAgent
3
+
4
+
5
+ class BoltzmannWealthModel(mesa.Model):
6
+ """A simple model of an economy where agents exchange currency at random.
7
+
8
+ All the agents begin with one unit of currency, and each time step can give
9
+ a unit of currency to another agent. Note how, over time, this produces a
10
+ highly skewed distribution of wealth.
11
+ """
12
+
13
+ def __init__(self, n=100, width=10, height=10, seed=None):
14
+ super().__init__(seed=seed)
15
+ self.num_agents = n
16
+ self.grid = mesa.space.MultiGrid(width, height, True)
17
+
18
+ self.datacollector = mesa.DataCollector(
19
+ model_reporters={"Gini": self.compute_gini},
20
+ agent_reporters={"Wealth": "wealth"},
21
+ )
22
+ # Create agents
23
+ for _ in range(self.num_agents):
24
+ a = MoneyAgent(self)
25
+
26
+ # Add the agent to a random grid cell
27
+ x = self.random.randrange(self.grid.width)
28
+ y = self.random.randrange(self.grid.height)
29
+ self.grid.place_agent(a, (x, y))
30
+
31
+ self.running = True
32
+ self.datacollector.collect(self)
33
+
34
+ def step(self):
35
+ self.agents.shuffle_do("step")
36
+ self.datacollector.collect(self)
37
+
38
+ def compute_gini(self):
39
+ agent_wealths = [agent.wealth for agent in self.agents]
40
+ x = sorted(agent_wealths)
41
+ n = self.num_agents
42
+ b = sum(xi * (n - i) for i, xi in enumerate(x)) / (n * sum(x))
43
+ return 1 + (1 / n) - 2 * b
@@ -0,0 +1,115 @@
1
+ # Run with streamlit run st_app.py
2
+
3
+ import time
4
+
5
+ import altair as alt
6
+ import pandas as pd
7
+ import streamlit as st
8
+ from model import BoltzmannWealthModel
9
+
10
+ model = st.title("Boltzman Wealth Model")
11
+ num_agents = st.slider(
12
+ "Choose how many agents to include in the model",
13
+ min_value=1,
14
+ max_value=100,
15
+ value=50,
16
+ )
17
+ num_ticks = st.slider(
18
+ "Select number of Simulation Runs", min_value=1, max_value=100, value=50
19
+ )
20
+ height = st.slider("Select Grid Height", min_value=10, max_value=100, step=10, value=15)
21
+ width = st.slider("Select Grid Width", min_value=10, max_value=100, step=10, value=20)
22
+ model = BoltzmannWealthModel(num_agents, height, width)
23
+
24
+
25
+ status_text = st.empty()
26
+ run = st.button("Run Simulation")
27
+
28
+
29
+ if run:
30
+ tick = time.time()
31
+ step = 0
32
+ # init grid
33
+ df_grid = pd.DataFrame()
34
+ df_gini = pd.DataFrame({"step": [0], "gini": [-1]})
35
+ for x in range(width):
36
+ for y in range(height):
37
+ df_grid = pd.concat(
38
+ [df_grid, pd.DataFrame({"x": [x], "y": [y], "agent_count": 0})],
39
+ ignore_index=True,
40
+ )
41
+
42
+ heatmap = (
43
+ alt.Chart(df_grid)
44
+ .mark_point(size=100)
45
+ .encode(x="x", y="y", color=alt.Color("agent_count"))
46
+ .interactive()
47
+ .properties(width=800, height=600)
48
+ )
49
+
50
+ line = (
51
+ alt.Chart(df_gini)
52
+ .mark_line(point=True)
53
+ .encode(x="step", y="gini")
54
+ .properties(width=800, height=600)
55
+ )
56
+
57
+ # init progress bar
58
+ my_bar = st.progress(0, text="Simulation Progress") # progress
59
+ placeholder = st.empty()
60
+ st.subheader("Agent Grid")
61
+ chart = st.altair_chart(heatmap)
62
+ st.subheader("Gini Values")
63
+ line_chart = st.altair_chart(line)
64
+
65
+ color_scale = alt.Scale(
66
+ domain=[0, 1, 2, 3, 4], range=["red", "cyan", "white", "white", "blue"]
67
+ )
68
+ for i in range(num_ticks):
69
+ model.step()
70
+ my_bar.progress((i / num_ticks), text="Simulation progress")
71
+ placeholder.text("Step = %d" % i)
72
+ for cell in model.grid.coord_iter():
73
+ cell_content, (x, y) = cell
74
+ agent_count = len(cell_content)
75
+ selected_row = df_grid[(df_grid["x"] == x) & (df_grid["y"] == y)]
76
+ df_grid.loc[selected_row.index, "agent_count"] = (
77
+ agent_count # random.choice([1,2])
78
+ )
79
+
80
+ df_gini = pd.concat(
81
+ [
82
+ df_gini,
83
+ pd.DataFrame(
84
+ {"step": [i], "gini": [model.datacollector.model_vars["Gini"][i]]}
85
+ ),
86
+ ]
87
+ )
88
+ # st.table(df_grid)
89
+ heatmap = (
90
+ alt.Chart(df_grid)
91
+ .mark_circle(size=100)
92
+ .encode(x="x", y="y", color=alt.Color("agent_count", scale=color_scale))
93
+ .interactive()
94
+ .properties(width=800, height=600)
95
+ )
96
+ chart.altair_chart(heatmap)
97
+
98
+ line = (
99
+ alt.Chart(df_gini)
100
+ .mark_line(point=True)
101
+ .encode(x="step", y="gini")
102
+ .properties(width=800, height=600)
103
+ )
104
+ line_chart.altair_chart(line)
105
+
106
+ time.sleep(0.01)
107
+
108
+ tock = time.time()
109
+ st.success(f"Simulation completed in {tock - tick:.2f} secs")
110
+
111
+ # st.subheader('Agent Grid')
112
+ # fig = px.imshow(agent_counts,labels={'color':'Agent Count'})
113
+ # st.plotly_chart(fig)
114
+ # st.subheader('Gini value over sim ticks (Plotly)')
115
+ # chart = st.line_chart(model.datacollector.model_vars['Gini'])
@@ -0,0 +1,39 @@
1
+ # Conway's Game Of "Life"
2
+
3
+ ## Summary
4
+
5
+ [The Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life), also known simply as "Life", is a cellular automaton devised by the British mathematician John Horton Conway in 1970.
6
+
7
+ The "game" is a zero-player game, meaning that its evolution is determined by its initial state, requiring no further input by a human. One interacts with the Game of "Life" by creating an initial configuration and observing how it evolves, or, for advanced "players", by creating patterns with particular properties.
8
+
9
+
10
+ ## How to Run
11
+
12
+ To run the model interactively you can use either the streamlit or solara version. For solara, you use
13
+
14
+ ```
15
+ $ solara run app.py
16
+ ```
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
+ ## Files
27
+
28
+ * ``agents.py``: Defines the behavior of an individual cell, which can be in two states: DEAD or ALIVE.
29
+ * ``model.py``: Defines the model itself, initialized with a random configuration of alive and dead cells.
30
+ * ``app.py``: Defines an interactive visualization using solara.
31
+ * ``st_app.py``: Defines an interactive visualization using Streamlit.
32
+
33
+ ## Optional
34
+
35
+ * For the streamlit version, you need to have streamlit installed (can be done via pip install streamlit)
36
+
37
+
38
+ ## Further Reading
39
+ [Conway's Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life)
File without changes
@@ -0,0 +1,47 @@
1
+ from mesa import Agent
2
+
3
+
4
+ class Cell(Agent):
5
+ """Represents a single ALIVE or DEAD cell in the simulation."""
6
+
7
+ DEAD = 0
8
+ ALIVE = 1
9
+
10
+ def __init__(self, pos, model, init_state=DEAD):
11
+ """Create a cell, in the given state, at the given x, y position."""
12
+ super().__init__(model)
13
+ self.x, self.y = pos
14
+ self.state = init_state
15
+ self._next_state = None
16
+
17
+ @property
18
+ def is_alive(self):
19
+ return self.state == self.ALIVE
20
+
21
+ @property
22
+ def neighbors(self):
23
+ return self.model.grid.iter_neighbors((self.x, self.y), True)
24
+
25
+ def determine_state(self):
26
+ """Compute if the cell will be dead or alive at the next tick. This is
27
+ based on the number of alive or dead neighbors. The state is not
28
+ changed here, but is just computed and stored in self._nextState,
29
+ because our current state may still be necessary for our neighbors
30
+ to calculate their next state.
31
+ """
32
+ # Get the neighbors and apply the rules on whether to be alive or dead
33
+ # at the next tick.
34
+ live_neighbors = sum(neighbor.is_alive for neighbor in self.neighbors)
35
+
36
+ # Assume nextState is unchanged, unless changed below.
37
+ self._next_state = self.state
38
+ if self.is_alive:
39
+ if live_neighbors < 2 or live_neighbors > 3:
40
+ self._next_state = self.DEAD
41
+ else:
42
+ if live_neighbors == 3:
43
+ self._next_state = self.ALIVE
44
+
45
+ def assume_state(self):
46
+ """Set the state to the new computed state -- computed in step()."""
47
+ self.state = self._next_state