Mesa 3.0.0a3__tar.gz → 3.0.0a4__tar.gz

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 (141) hide show
  1. {mesa-3.0.0a3 → mesa-3.0.0a4}/.github/ISSUE_TEMPLATE/bug-report.md +2 -1
  2. mesa-3.0.0a4/.github/ISSUE_TEMPLATE/config.yml +5 -0
  3. {mesa-3.0.0a3 → mesa-3.0.0a4}/.pre-commit-config.yaml +1 -1
  4. {mesa-3.0.0a3 → mesa-3.0.0a4}/HISTORY.md +53 -0
  5. {mesa-3.0.0a3 → mesa-3.0.0a4}/PKG-INFO +2 -1
  6. {mesa-3.0.0a3 → mesa-3.0.0a4}/benchmarks/BoltzmannWealth/boltzmann_wealth.py +4 -4
  7. {mesa-3.0.0a3 → mesa-3.0.0a4}/benchmarks/Flocking/flocking.py +2 -5
  8. {mesa-3.0.0a3 → mesa-3.0.0a4}/benchmarks/Schelling/schelling.py +3 -6
  9. {mesa-3.0.0a3 → mesa-3.0.0a4}/benchmarks/WolfSheep/wolf_sheep.py +7 -12
  10. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/conf.py +2 -0
  11. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/index.md +2 -2
  12. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/overview.md +5 -5
  13. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/tutorials/MoneyModel.py +2 -2
  14. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/tutorials/visualization_tutorial.ipynb +42 -23
  15. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/__init__.py +1 -1
  16. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/agent.py +92 -19
  17. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/batchrunner.py +3 -0
  18. mesa-3.0.0a4/mesa/experimental/__init__.py +5 -0
  19. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/experimental/cell_space/cell_agent.py +2 -2
  20. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/experimental/devs/examples/epstein_civil_violence.py +6 -10
  21. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/experimental/devs/examples/wolf_sheep.py +7 -12
  22. {mesa-3.0.0a3/mesa/visualization → mesa-3.0.0a4/mesa/experimental}/solara_viz.py +3 -3
  23. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/model.py +24 -19
  24. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/space.py +9 -3
  25. mesa-3.0.0a4/mesa/visualization/UserParam.py +56 -0
  26. mesa-3.0.0a4/mesa/visualization/__init__.py +14 -0
  27. mesa-3.0.0a4/mesa/visualization/components/altair.py +86 -0
  28. mesa-3.0.0a4/mesa/visualization/components/matplotlib.py +246 -0
  29. mesa-3.0.0a4/mesa/visualization/solara_viz.py +394 -0
  30. mesa-3.0.0a4/mesa/visualization/utils.py +7 -0
  31. {mesa-3.0.0a3 → mesa-3.0.0a4}/pyproject.toml +1 -0
  32. {mesa-3.0.0a3 → mesa-3.0.0a4}/tests/test_agent.py +123 -32
  33. {mesa-3.0.0a3 → mesa-3.0.0a4}/tests/test_batch_run.py +18 -19
  34. {mesa-3.0.0a3 → mesa-3.0.0a4}/tests/test_cell_space.py +7 -7
  35. {mesa-3.0.0a3 → mesa-3.0.0a4}/tests/test_datacollector.py +4 -12
  36. {mesa-3.0.0a3 → mesa-3.0.0a4}/tests/test_lifespan.py +4 -8
  37. {mesa-3.0.0a3 → mesa-3.0.0a4}/tests/test_model.py +18 -5
  38. {mesa-3.0.0a3 → mesa-3.0.0a4}/tests/test_solara_viz.py +11 -40
  39. {mesa-3.0.0a3 → mesa-3.0.0a4}/tests/test_space.py +17 -0
  40. {mesa-3.0.0a3 → mesa-3.0.0a4}/tests/test_time.py +12 -12
  41. mesa-3.0.0a3/.github/ISSUE_TEMPLATE/asking-help.md +0 -9
  42. mesa-3.0.0a3/mesa/experimental/__init__.py +0 -3
  43. mesa-3.0.0a3/mesa/visualization/__init__.py +0 -3
  44. {mesa-3.0.0a3 → mesa-3.0.0a4}/.codespellignore +0 -0
  45. {mesa-3.0.0a3 → mesa-3.0.0a4}/.coveragerc +0 -0
  46. {mesa-3.0.0a3 → mesa-3.0.0a4}/.github/ISSUE_TEMPLATE/feature-request.md +0 -0
  47. {mesa-3.0.0a3 → mesa-3.0.0a4}/.github/PULL_REQUEST_TEMPLATE/bug.md +0 -0
  48. {mesa-3.0.0a3 → mesa-3.0.0a4}/.github/PULL_REQUEST_TEMPLATE/feature.md +0 -0
  49. {mesa-3.0.0a3 → mesa-3.0.0a4}/.github/dependabot.yml +0 -0
  50. {mesa-3.0.0a3 → mesa-3.0.0a4}/.github/release.yml +0 -0
  51. {mesa-3.0.0a3 → mesa-3.0.0a4}/.github/workflows/benchmarks.yml +0 -0
  52. {mesa-3.0.0a3 → mesa-3.0.0a4}/.github/workflows/build_lint.yml +0 -0
  53. {mesa-3.0.0a3 → mesa-3.0.0a4}/.github/workflows/release.yml +0 -0
  54. {mesa-3.0.0a3 → mesa-3.0.0a4}/.gitignore +0 -0
  55. {mesa-3.0.0a3 → mesa-3.0.0a4}/.readthedocs.yml +0 -0
  56. {mesa-3.0.0a3 → mesa-3.0.0a4}/CITATION.bib +0 -0
  57. {mesa-3.0.0a3 → mesa-3.0.0a4}/CODE_OF_CONDUCT.md +0 -0
  58. {mesa-3.0.0a3 → mesa-3.0.0a4}/CONTRIBUTING.md +0 -0
  59. {mesa-3.0.0a3 → mesa-3.0.0a4}/Dockerfile +0 -0
  60. {mesa-3.0.0a3 → mesa-3.0.0a4}/LICENSE +0 -0
  61. {mesa-3.0.0a3 → mesa-3.0.0a4}/README.md +0 -0
  62. {mesa-3.0.0a3 → mesa-3.0.0a4}/benchmarks/BoltzmannWealth/__init__.py +0 -0
  63. {mesa-3.0.0a3 → mesa-3.0.0a4}/benchmarks/Flocking/__init__.py +0 -0
  64. {mesa-3.0.0a3 → mesa-3.0.0a4}/benchmarks/Schelling/__init__.py +0 -0
  65. {mesa-3.0.0a3 → mesa-3.0.0a4}/benchmarks/WolfSheep/__init__.py +0 -0
  66. {mesa-3.0.0a3 → mesa-3.0.0a4}/benchmarks/compare_timings.py +0 -0
  67. {mesa-3.0.0a3 → mesa-3.0.0a4}/benchmarks/configurations.py +0 -0
  68. {mesa-3.0.0a3 → mesa-3.0.0a4}/benchmarks/global_benchmark.py +0 -0
  69. {mesa-3.0.0a3 → mesa-3.0.0a4}/codecov.yaml +0 -0
  70. {mesa-3.0.0a3 → mesa-3.0.0a4}/docker-compose.yml +0 -0
  71. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/Makefile +0 -0
  72. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/README.md +0 -0
  73. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/apis/api_main.md +0 -0
  74. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/apis/batchrunner.md +0 -0
  75. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/apis/datacollection.md +0 -0
  76. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/apis/experimental.md +0 -0
  77. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/apis/init.md +0 -0
  78. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/apis/space.md +0 -0
  79. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/apis/time.md +0 -0
  80. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/apis/visualization.md +0 -0
  81. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/best-practices.md +0 -0
  82. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/howto.md +0 -0
  83. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/images/Mesa_Screenshot.png +0 -0
  84. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/images/mesa_logo.ico +0 -0
  85. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/images/mesa_logo.png +0 -0
  86. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/images/tutorial/br_ginis.png +0 -0
  87. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/images/tutorial/dc_endwealth.png +0 -0
  88. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/images/tutorial/dc_gini.png +0 -0
  89. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/images/tutorial/dc_oneagent.png +0 -0
  90. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/images/tutorial/first_hist.png +0 -0
  91. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/images/tutorial/multirun_hist.png +0 -0
  92. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/images/tutorial/numpy_grid.png +0 -0
  93. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/images/tutorial/viz_chart.png +0 -0
  94. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/images/tutorial/viz_empty.png +0 -0
  95. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/images/tutorial/viz_greycircles.png +0 -0
  96. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/images/tutorial/viz_histogram.png +0 -0
  97. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/images/tutorial/viz_redcircles.png +0 -0
  98. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/make.bat +0 -0
  99. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/mesa.md +0 -0
  100. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/migration_guide.md +0 -0
  101. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/packages.md +0 -0
  102. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/tutorials/files/viz_chart.png +0 -0
  103. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/tutorials/files/viz_empty.png +0 -0
  104. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/tutorials/files/viz_greycircles.png +0 -0
  105. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/tutorials/files/viz_histogram.png +0 -0
  106. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/tutorials/files/viz_redcircles.png +0 -0
  107. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/tutorials/files/viz_slider.png +0 -0
  108. {mesa-3.0.0a3 → mesa-3.0.0a4}/docs/tutorials/intro_tutorial.ipynb +0 -0
  109. {mesa-3.0.0a3 → mesa-3.0.0a4}/maintenance/fetch_unlabeled_prs.py +0 -0
  110. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/cookiecutter-mesa/cookiecutter.json +0 -0
  111. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/cookiecutter-mesa/hooks/post_gen_project.py +0 -0
  112. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/README.md +0 -0
  113. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/app.pytemplate +0 -0
  114. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.pytemplate +0 -0
  115. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/__init__.py +0 -0
  116. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/model.pytemplate +0 -0
  117. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/datacollection.py +0 -0
  118. {mesa-3.0.0a3/mesa/visualization → mesa-3.0.0a4/mesa/experimental}/UserParam.py +0 -0
  119. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/experimental/cell_space/__init__.py +0 -0
  120. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/experimental/cell_space/cell.py +0 -0
  121. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/experimental/cell_space/cell_collection.py +0 -0
  122. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/experimental/cell_space/discrete_space.py +0 -0
  123. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/experimental/cell_space/grid.py +0 -0
  124. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/experimental/cell_space/network.py +0 -0
  125. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/experimental/cell_space/voronoi.py +0 -0
  126. {mesa-3.0.0a3/mesa/visualization → mesa-3.0.0a4/mesa/experimental}/components/altair.py +0 -0
  127. {mesa-3.0.0a3/mesa/visualization → mesa-3.0.0a4/mesa/experimental}/components/matplotlib.py +0 -0
  128. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/experimental/devs/__init__.py +0 -0
  129. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/experimental/devs/eventlist.py +0 -0
  130. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/experimental/devs/simulator.py +0 -0
  131. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/main.py +0 -0
  132. {mesa-3.0.0a3 → mesa-3.0.0a4}/mesa/time.py +0 -0
  133. {mesa-3.0.0a3 → mesa-3.0.0a4}/mypy.ini +0 -0
  134. {mesa-3.0.0a3 → mesa-3.0.0a4}/tests/__init__.py +0 -0
  135. {mesa-3.0.0a3 → mesa-3.0.0a4}/tests/read_requirements.py +0 -0
  136. {mesa-3.0.0a3 → mesa-3.0.0a4}/tests/test_devs.py +0 -0
  137. {mesa-3.0.0a3 → mesa-3.0.0a4}/tests/test_end_to_end_viz.sh +0 -0
  138. {mesa-3.0.0a3 → mesa-3.0.0a4}/tests/test_examples.py +0 -0
  139. {mesa-3.0.0a3 → mesa-3.0.0a4}/tests/test_grid.py +0 -0
  140. {mesa-3.0.0a3 → mesa-3.0.0a4}/tests/test_import_namespace.py +0 -0
  141. {mesa-3.0.0a3 → mesa-3.0.0a4}/tests/test_scaffold.py +0 -0
@@ -11,7 +11,8 @@ about: Let us know if something is broken on Mesa
11
11
  <!-- A clear and concise description of what you expected to happen -->
12
12
 
13
13
  **To Reproduce**
14
- <!-- Steps to reproduce the bug, or a link to a project where the bug is visible -->
14
+ <!-- Steps to reproduce the bug, or a link to a project where the bug is visible
15
+ Include a Minimal reproducible example: https://stackoverflow.com/help/minimal-reproducible-example -->
15
16
 
16
17
  **Additional context**
17
18
  <!--
@@ -0,0 +1,5 @@
1
+ blank_issues_enabled: true
2
+ contact_links:
3
+ - name: Questions, ideas, showcases and more
4
+ url: https://github.com/projectmesa/mesa/discussions
5
+ about: Discuss Mesa, ask questions, share ideas, and showcase your projects
@@ -4,7 +4,7 @@ ci:
4
4
  repos:
5
5
  - repo: https://github.com/astral-sh/ruff-pre-commit
6
6
  # Ruff version.
7
- rev: v0.5.6
7
+ rev: v0.6.3
8
8
  hooks:
9
9
  # Run the linter.
10
10
  - id: ruff
@@ -1,6 +1,59 @@
1
1
  ---
2
2
  title: Release History
3
3
  ---
4
+ # 3.0.0a4 (2024-09-09)
5
+ ## Highlights
6
+ Mesa 3.0.0a4 contains two major breaking changes:
7
+ 1. The Agent's `unique_id` is now automatically assigned, so doesn't need to be passed to the Agent class anymore. In a subclassed custom Agent, like normally used, this now looks like this:
8
+ ```diff
9
+ class Wolf(Agent):
10
+ - def __init__(self, unique_id, model, energy=None):
11
+ + def __init__(self, model, energy=None):
12
+ # When initializing the super class (Agent), passing unique_id isn't needed anymore
13
+ - super().__init__(unique_id, model)
14
+ + super().__init__(model)
15
+
16
+ - wolf = Wolf(unique_id, model)
17
+ + wolf = Wolf(model)
18
+ ```
19
+ Example models were updated in [mesa-examples#194](https://github.com/projectmesa/mesa-examples/pull/194), which shows more examples on how to update existing models.
20
+
21
+ 2. Our visualisation API is being overhauled, to be more flexible and powerful. For more details, see [#2278](https://github.com/projectmesa/mesa/pull/2278).
22
+ - An initial update to the tutorial was made in [#2289](https://github.com/projectmesa/mesa/pull/2289) and is [available here](https://mesa.readthedocs.io/en/latest/tutorials/visualization_tutorial.html).
23
+ - An initial example model was updated in [mesa-examples#195](https://github.com/projectmesa/mesa-examples/pull/195), and more examples will be updated in [mesa-examples#195](https://github.com/projectmesa/mesa-examples/pull/193).
24
+ - The old SolaraViz API is still available at `mesa.experimental`, but might be removed in future releases.
25
+
26
+ Furthermore, the AgentSet has a new `agg` method to quickly get an aggerate value (for example `min_energy = model.agents.agg("energy", min)`) ([#2266](https://github.com/projectmesa/mesa/pull/2266)), The Model `get_agents_of_type` function is replaced by directly exposing the `agents_by_type` property (which can be accessed as a dict) ([#2267](https://github.com/projectmesa/mesa/pull/2267), [mesa-examples#190](https://github.com/projectmesa/mesa-examples/pull/190)) and the AgentSet get() methods can now handle missing values by replacing it with a default value ([#2279](https://github.com/projectmesa/mesa/pull/2279)).
27
+
28
+ Finally, it fixes a bug in which the Grid's `move_agent_to_one_of` method with `selection="closest"` selected a location deterministically, instead of randomly ([#2118](https://github.com/projectmesa/mesa/pull/2118)).
29
+
30
+ ## What's Changed
31
+ ### ⚠️ Breaking changes
32
+ * move solara_viz back to experimental by @Corvince in https://github.com/projectmesa/mesa/pull/2278
33
+ * track unique_id automatically by @quaquel in https://github.com/projectmesa/mesa/pull/2260
34
+ ### 🎉 New features added
35
+ * AgentSet: Add `agg` method by @EwoutH in https://github.com/projectmesa/mesa/pull/2266
36
+ * Implement new SolaraViz API by @Corvince in https://github.com/projectmesa/mesa/pull/2263
37
+ ### 🛠 Enhancements made
38
+ * Model: Replace `get_agents_of_type` method with `agents_by_type` property by @EwoutH in https://github.com/projectmesa/mesa/pull/2267
39
+ * add default SolaraViz by @Corvince in https://github.com/projectmesa/mesa/pull/2280
40
+ * Simplify ModelController by @Corvince in https://github.com/projectmesa/mesa/pull/2282
41
+ * Add default values and missing value handling to `agentset.get` by @quaquel in https://github.com/projectmesa/mesa/pull/2279
42
+ ### 🐛 Bugs fixed
43
+ * Fix deterministic behavior in `move_agent_to_one_of` with `selection="closest"` by @OrenBochman in https://github.com/projectmesa/mesa/pull/2118
44
+ ### 📜 Documentation improvements
45
+ * docs: Fix Visualization Tutorial (main branch) by @EwoutH in https://github.com/projectmesa/mesa/pull/2271
46
+ * Docs: Fix broken relative links by removing `.html` suffix by @EwoutH in https://github.com/projectmesa/mesa/pull/2274
47
+ * Readthedocs: Don't let notebook failures pass silently by @EwoutH in https://github.com/projectmesa/mesa/pull/2276
48
+ * Update viz tutorial to the new API by @Corvince in https://github.com/projectmesa/mesa/pull/2289
49
+ ### 🔧 Maintenance
50
+ * Resolve multiprocessing warning, state Python 3.13 support by @rht in https://github.com/projectmesa/mesa/pull/2246
51
+
52
+ ## New Contributors
53
+ * @OrenBochman made their first contribution in https://github.com/projectmesa/mesa/pull/2118
54
+
55
+ **Full Changelog**: https://github.com/projectmesa/mesa/compare/v3.0.0a3...v3.0.0a4
56
+
4
57
  # 3.0.0a3 (2024-08-30)
5
58
  ## Highlights
6
59
  Developments toward Mesa 3.0 are steaming ahead, and our fourth alpha release is packed with features and updates - only 8 days after our third.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: Mesa
3
- Version: 3.0.0a3
3
+ Version: 3.0.0a4
4
4
  Summary: Agent-based modeling (ABM) in Python
5
5
  Project-URL: homepage, https://github.com/projectmesa/mesa
6
6
  Project-URL: repository, https://github.com/projectmesa/mesa
@@ -17,6 +17,7 @@ Classifier: Programming Language :: Python :: 3 :: Only
17
17
  Classifier: Programming Language :: Python :: 3.10
18
18
  Classifier: Programming Language :: Python :: 3.11
19
19
  Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
20
21
  Classifier: Topic :: Scientific/Engineering
21
22
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
22
23
  Classifier: Topic :: Scientific/Engineering :: Artificial Life
@@ -27,8 +27,8 @@ class BoltzmannWealth(mesa.Model):
27
27
  model_reporters={"Gini": compute_gini}, agent_reporters={"Wealth": "wealth"}
28
28
  )
29
29
  # Create agents
30
- for i in range(self.num_agents):
31
- a = MoneyAgent(i, self)
30
+ for _ in range(self.num_agents):
31
+ a = MoneyAgent(self)
32
32
  # Add the agent to a random grid cell
33
33
  x = self.random.randrange(self.grid.width)
34
34
  y = self.random.randrange(self.grid.height)
@@ -50,8 +50,8 @@ class BoltzmannWealth(mesa.Model):
50
50
  class MoneyAgent(mesa.Agent):
51
51
  """An agent with fixed initial wealth."""
52
52
 
53
- def __init__(self, unique_id, model):
54
- super().__init__(unique_id, model)
53
+ def __init__(self, model):
54
+ super().__init__(model)
55
55
  self.wealth = 1
56
56
 
57
57
  def move(self):
@@ -27,7 +27,6 @@ class Boid(mesa.Agent):
27
27
 
28
28
  def __init__(
29
29
  self,
30
- unique_id,
31
30
  model,
32
31
  speed,
33
32
  direction,
@@ -41,7 +40,6 @@ class Boid(mesa.Agent):
41
40
  Create a new Boid flocker agent.
42
41
 
43
42
  Args:
44
- unique_id: Unique agent identifier.
45
43
  speed: Distance to move per step.
46
44
  direction: numpy vector for the Boid's direction of movement.
47
45
  vision: Radius to look around for nearby Boids.
@@ -51,7 +49,7 @@ class Boid(mesa.Agent):
51
49
  match: the relative importance of matching neighbors' directions
52
50
 
53
51
  """
54
- super().__init__(unique_id, model)
52
+ super().__init__(model)
55
53
  self.speed = speed
56
54
  self.direction = direction
57
55
  self.vision = vision
@@ -131,13 +129,12 @@ class BoidFlockers(mesa.Model):
131
129
  "match": match,
132
130
  }
133
131
 
134
- for i in range(self.population):
132
+ for _ in range(self.population):
135
133
  x = self.random.random() * self.space.x_max
136
134
  y = self.random.random() * self.space.y_max
137
135
  pos = np.array((x, y))
138
136
  direction = np.random.random(2) * 2 - 1
139
137
  boid = Boid(
140
- unique_id=i,
141
138
  model=self,
142
139
  speed=speed,
143
140
  direction=direction,
@@ -8,15 +8,14 @@ class SchellingAgent(CellAgent):
8
8
  Schelling segregation agent
9
9
  """
10
10
 
11
- def __init__(self, unique_id, model, agent_type, radius, homophily):
11
+ def __init__(self, model, agent_type, radius, homophily):
12
12
  """
13
13
  Create a new Schelling agent.
14
14
  Args:
15
- unique_id: Unique identifier for the agent.
16
15
  x, y: Agent initial location.
17
16
  agent_type: Indicator for the agent's type (minority=1, majority=0)
18
17
  """
19
- super().__init__(unique_id, model)
18
+ super().__init__(model)
20
19
  self.type = agent_type
21
20
  self.radius = radius
22
21
  self.homophily = homophily
@@ -81,9 +80,7 @@ class Schelling(Model):
81
80
  for cell in self.grid:
82
81
  if self.random.random() < density:
83
82
  agent_type = 1 if self.random.random() < self.minority_pc else 0
84
- agent = SchellingAgent(
85
- self.next_id(), self, agent_type, radius, homophily
86
- )
83
+ agent = SchellingAgent(self, agent_type, radius, homophily)
87
84
  agent.move_to(cell)
88
85
  self.schedule.add(agent)
89
86
 
@@ -17,8 +17,8 @@ from mesa.experimental.devs import ABMSimulator
17
17
 
18
18
 
19
19
  class Animal(CellAgent):
20
- def __init__(self, unique_id, model, energy, p_reproduce, energy_from_food):
21
- super().__init__(unique_id, model)
20
+ def __init__(self, model, energy, p_reproduce, energy_from_food):
21
+ super().__init__(model)
22
22
  self.energy = energy
23
23
  self.p_reproduce = p_reproduce
24
24
  self.energy_from_food = energy_from_food
@@ -29,7 +29,6 @@ class Animal(CellAgent):
29
29
  def spawn_offspring(self):
30
30
  self.energy /= 2
31
31
  offspring = self.__class__(
32
- self.model.next_id(),
33
32
  self.model,
34
33
  self.energy,
35
34
  self.p_reproduce,
@@ -107,7 +106,7 @@ class GrassPatch(CellAgent):
107
106
  function_args=[self, "fully_grown", True],
108
107
  )
109
108
 
110
- def __init__(self, unique_id, model, fully_grown, countdown, grass_regrowth_time):
109
+ def __init__(self, model, fully_grown, countdown, grass_regrowth_time):
111
110
  """
112
111
  TODO:: fully grown can just be an int --> so one less param (i.e. countdown)
113
112
 
@@ -119,7 +118,7 @@ class GrassPatch(CellAgent):
119
118
  grass_regrowth_time : time to fully regrow grass
120
119
  countdown : Time for the patch of grass to be fully regrown if fully grown is False
121
120
  """
122
- super().__init__(unique_id, model)
121
+ super().__init__(model)
123
122
  self._fully_grown = fully_grown
124
123
  self.grass_regrowth_time = grass_regrowth_time
125
124
 
@@ -189,7 +188,6 @@ class WolfSheep(Model):
189
188
  )
190
189
  energy = self.random.randrange(2 * sheep_gain_from_food)
191
190
  sheep = Sheep(
192
- self.next_id(),
193
191
  self,
194
192
  energy,
195
193
  sheep_reproduce,
@@ -205,7 +203,6 @@ class WolfSheep(Model):
205
203
  )
206
204
  energy = self.random.randrange(2 * wolf_gain_from_food)
207
205
  wolf = Wolf(
208
- self.next_id(),
209
206
  self,
210
207
  energy,
211
208
  wolf_reproduce,
@@ -221,14 +218,12 @@ class WolfSheep(Model):
221
218
  countdown = grass_regrowth_time
222
219
  else:
223
220
  countdown = self.random.randrange(grass_regrowth_time)
224
- patch = GrassPatch(
225
- self.next_id(), self, fully_grown, countdown, grass_regrowth_time
226
- )
221
+ patch = GrassPatch(self, fully_grown, countdown, grass_regrowth_time)
227
222
  patch.move_to(cell)
228
223
 
229
224
  def step(self):
230
- self.get_agents_of_type(Sheep).shuffle(inplace=True).do("step")
231
- self.get_agents_of_type(Wolf).shuffle(inplace=True).do("step")
225
+ self.agents_by_type[Sheep].shuffle(inplace=True).do("step")
226
+ self.agents_by_type[Wolf].shuffle(inplace=True).do("step")
232
227
 
233
228
 
234
229
  if __name__ == "__main__":
@@ -113,6 +113,8 @@ pygments_style = "gruvbox-dark"
113
113
 
114
114
  nb_execution_timeout = 60
115
115
  nb_execution_mode = "cache"
116
+ nb_execution_allow_errors = False
117
+ nb_execution_raise_on_error = True
116
118
 
117
119
  # -- Options for HTML output ----------------------------------------------
118
120
 
@@ -99,7 +99,7 @@ Mesa Packages <packages>
99
99
  [github issue tracker]: https://github.com/projectmesa/mesa/issues
100
100
  [matrix chat room]: https://matrix.to/#/#project-mesa:matrix.org
101
101
  [mesa]: https://github.com/projectmesa/mesa/
102
- [mesa introductory tutorial]: tutorials/intro_tutorial.html
103
- [mesa visualization tutorial]: tutorials/visualization_tutorial.html
102
+ [mesa introductory tutorial]: tutorials/intro_tutorial
103
+ [mesa visualization tutorial]: tutorials/visualization_tutorial
104
104
  [pypi]: https://pypi.python.org/pypi/Mesa/
105
105
  [ticket]: https://github.com/projectmesa/mesa/issues
@@ -17,8 +17,8 @@ Mesa is modular, meaning that its modeling, analysis and visualization component
17
17
  Most models consist of one class to represent the model itself; one class (or more) for agents; a scheduler to handle time (what order the agents act in), and possibly a space for the agents to inhabit and move through. These are implemented in Mesa's modeling modules:
18
18
 
19
19
  - `mesa.Model`, `mesa.Agent`
20
- - [mesa.time](apis/time.html)
21
- - [mesa.space](apis/space.html)
20
+ - [mesa.time](apis/time)
21
+ - [mesa.space](apis/space)
22
22
 
23
23
  The skeleton of a model might look like this:
24
24
 
@@ -56,7 +56,7 @@ model = MyModel(5)
56
56
  model.step()
57
57
  ```
58
58
 
59
- You should see agents 0-4, activated in random order. See the [tutorial](tutorials/intro_tutorial.html) or API documentation for more detail on how to add model functionality.
59
+ You should see agents 0-4, activated in random order. See the [tutorial](tutorials/intro_tutorial) or API documentation for more detail on how to add model functionality.
60
60
 
61
61
  To bootstrap a new model install mesa and run `mesa startproject`
62
62
 
@@ -64,8 +64,8 @@ To bootstrap a new model install mesa and run `mesa startproject`
64
64
 
65
65
  If you're using modeling for research, you'll want a way to collect the data each model run generates. You'll probably also want to run the model multiple times, to see how some output changes with different parameters. Data collection and batch running are implemented in the appropriately-named analysis modules:
66
66
 
67
- - [mesa.datacollection](apis/datacollection.html)
68
- - [mesa.batchrunner](apis/batchrunner.html)
67
+ - [mesa.datacollection](apis/datacollection)
68
+ - [mesa.batchrunner](apis/batchrunner)
69
69
 
70
70
  You'd add a data collector to the model like this:
71
71
 
@@ -15,8 +15,8 @@ def compute_gini(model):
15
15
  class MoneyAgent(mesa.Agent):
16
16
  """An agent with fixed initial wealth."""
17
17
 
18
- def __init__(self, unique_id, model):
19
- super().__init__(unique_id, model)
18
+ def __init__(self, model):
19
+ super().__init__(model)
20
20
  self.wealth = 1
21
21
 
22
22
  def move(self):
@@ -11,7 +11,7 @@
11
11
  "cell_type": "markdown",
12
12
  "metadata": {},
13
13
  "source": [
14
- "*This version of the visualisation tutorial is updated for Mesa 3.0, and works with Mesa `3.0.0a1` and above. If you are using Mesa 2.3.x, check out the [stable version](https://mesa.readthedocs.io/en/stable/tutorials/visualization_tutorial.html) of this tutorial on Readthedocs.*\n",
14
+ "*This version of the visualisation tutorial is updated for Mesa 3.0, and works with Mesa `3.0.0a4` and above. If you are using Mesa 2.3.x, check out the [stable version](https://mesa.readthedocs.io/en/stable/tutorials/visualization_tutorial.html) of this tutorial on Readthedocs.*\n",
15
15
  "\n",
16
16
  "**Important:** \n",
17
17
  "- If you are just exploring Mesa and want the fastest way to execute the code we recommend executing this tutorial online in a Colab notebook. [![Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/projectmesa/mesa/blob/main/docs/tutorials/visualization_tutorial.ipynb)\n",
@@ -21,7 +21,7 @@
21
21
  "\n",
22
22
  "So far, we've built a model, run it, and analyzed some output afterwards. However, one of the advantages of agent-based models is that we can often watch them run step by step, potentially spotting unexpected patterns, behaviors or bugs, or developing new intuitions, hypotheses, or insights. Other times, watching a model run can explain it to an unfamiliar audience better than static explanations. Like many ABM frameworks, Mesa allows you to create an interactive visualization of the model. In this section we'll walk through creating a visualization using built-in components, and (for advanced users) how to create a new visualization element.\n",
23
23
  "\n",
24
- "First, a quick explanation of how Mesa's interactive visualization works. The visualization is done in a browser window, using the [Solara](https://solara.dev/) framework, a pure Python, React-style web framework. Running `solara run app.py` will launch a web server, which runs the model, and displays model detail at each step via the Matplotlib plotting library. Alternatively, you can execute everything inside a Jupyter environment."
24
+ "First, a quick explanation of how Mesa's interactive visualization works. The visualization is done in a browser window, using the [Solara](https://solara.dev/) framework, a pure Python, React-style web framework. Running `solara run app.py` will launch a web server, which runs the model, and displays model detail at each step via the Matplotlib plotting library. Alternatively, you can execute everything inside a notebook environment and display it inline."
25
25
  ]
26
26
  },
27
27
  {
@@ -105,7 +105,7 @@
105
105
  "cell_type": "markdown",
106
106
  "metadata": {},
107
107
  "source": [
108
- "Next, we instantiate the visualization object which (by default) displays the grid containing the agents, and timeseries of of values computed by the model's data collector. In this example, we specify the Gini coefficient.\n",
108
+ "Next, we instantiate the visualization object which (by default) displays the grid containing the agents, and timeseries of values computed by the model's data collector. In this example, we specify the Gini coefficient.\n",
109
109
  "\n",
110
110
  "There are 3 buttons:\n",
111
111
  "- the step button, which advances the model by 1 step\n",
@@ -123,14 +123,19 @@
123
123
  },
124
124
  "outputs": [],
125
125
  "source": [
126
- "from mesa.visualization import SolaraViz\n",
126
+ "from mesa.visualization import SolaraViz, make_plot_measure, make_space_matplotlib\n",
127
+ "\n",
128
+ "# Create initial model instance\n",
129
+ "model1 = BoltzmannWealthModel(50, 10, 10)\n",
130
+ "\n",
131
+ "SpaceGraph = make_space_matplotlib(agent_portrayal)\n",
132
+ "GiniPlot = make_plot_measure(\"Gini\")\n",
127
133
  "\n",
128
134
  "page = SolaraViz(\n",
129
- " BoltzmannWealthModel,\n",
130
- " model_params,\n",
131
- " measures=[\"Gini\"],\n",
132
- " name=\"Money Model\",\n",
133
- " agent_portrayal=agent_portrayal,\n",
135
+ " model1,\n",
136
+ " components=[SpaceGraph, GiniPlot],\n",
137
+ " model_params=model_params,\n",
138
+ " name=\"Boltzmann Wealth Model\",\n",
134
139
  ")\n",
135
140
  "# This is required to render the visualization in the Jupyter notebook\n",
136
141
  "page"
@@ -170,11 +175,10 @@
170
175
  "outputs": [],
171
176
  "source": [
172
177
  "page = SolaraViz(\n",
173
- " BoltzmannWealthModel,\n",
174
- " model_params,\n",
175
- " measures=[\"Gini\"],\n",
176
- " name=\"Money Model\",\n",
177
- " agent_portrayal=agent_portrayal,\n",
178
+ " model1,\n",
179
+ " components=[SpaceGraph, GiniPlot],\n",
180
+ " model_params=model_params,\n",
181
+ " name=\"Boltzmann Wealth Model\",\n",
178
182
  ")\n",
179
183
  "# This is required to render the visualization in the Jupyter notebook\n",
180
184
  "page"
@@ -202,13 +206,13 @@
202
206
  "import solara\n",
203
207
  "from matplotlib.figure import Figure\n",
204
208
  "\n",
205
- "\n",
206
- "def make_histogram(model):\n",
209
+ "@solara.component\n",
210
+ "def Histogram(model):\n",
207
211
  " # Note: you must initialize a figure using this method instead of\n",
208
212
  " # plt.figure(), for thread safety purpose\n",
209
213
  " fig = Figure()\n",
210
214
  " ax = fig.subplots()\n",
211
- " wealth_vals = [agent.wealth for agent in model.schedule.agents]\n",
215
+ " wealth_vals = [agent.wealth for agent in model.agents]\n",
212
216
  " # Note: you have to use Matplotlib's OOP API instead of plt.hist\n",
213
217
  " # because plt.hist is not thread-safe.\n",
214
218
  " ax.hist(wealth_vals, bins=10)\n",
@@ -219,7 +223,23 @@
219
223
  "cell_type": "markdown",
220
224
  "metadata": {},
221
225
  "source": [
222
- "Next, we reinitialize the visualization object, but this time with the histogram (see the measures argument)."
226
+ "In a notebook environment we can directly display the visualization by calling it with the model instance"
227
+ ]
228
+ },
229
+ {
230
+ "cell_type": "code",
231
+ "execution_count": null,
232
+ "metadata": {},
233
+ "outputs": [],
234
+ "source": [
235
+ "Histogram(model1)"
236
+ ]
237
+ },
238
+ {
239
+ "cell_type": "markdown",
240
+ "metadata": {},
241
+ "source": [
242
+ "Next, we update our solara frontend to use this new component"
223
243
  ]
224
244
  },
225
245
  {
@@ -229,11 +249,10 @@
229
249
  "outputs": [],
230
250
  "source": [
231
251
  "page = SolaraViz(\n",
232
- " BoltzmannWealthModel,\n",
233
- " model_params,\n",
234
- " measures=[\"Gini\", make_histogram],\n",
235
- " name=\"Money Model\",\n",
236
- " agent_portrayal=agent_portrayal,\n",
252
+ " model1,\n",
253
+ " components=[SpaceGraph, GiniPlot, Histogram],\n",
254
+ " model_params=model_params,\n",
255
+ " name=\"Boltzmann Wealth Model\",\n",
237
256
  ")\n",
238
257
  "# This is required to render the visualization in the Jupyter notebook\n",
239
258
  "page"
@@ -24,7 +24,7 @@ __all__ = [
24
24
  ]
25
25
 
26
26
  __title__ = "mesa"
27
- __version__ = "3.0.0a3"
27
+ __version__ = "3.0.0a4"
28
28
  __license__ = "Apache 2.0"
29
29
  _this_year = datetime.datetime.now(tz=datetime.timezone.utc).date().year
30
30
  __copyright__ = f"Copyright {_this_year} Project Mesa Team"
@@ -10,6 +10,8 @@ from __future__ import annotations
10
10
 
11
11
  import contextlib
12
12
  import copy
13
+ import functools
14
+ import itertools
13
15
  import operator
14
16
  import warnings
15
17
  import weakref
@@ -18,7 +20,7 @@ from collections.abc import Callable, Iterable, Iterator, MutableSet, Sequence
18
20
  from random import Random
19
21
 
20
22
  # mypy
21
- from typing import TYPE_CHECKING, Any
23
+ from typing import TYPE_CHECKING, Any, Literal
22
24
 
23
25
  if TYPE_CHECKING:
24
26
  # We ensure that these are not imported during runtime to prevent cyclic
@@ -32,21 +34,49 @@ class Agent:
32
34
  Base class for a model agent in Mesa.
33
35
 
34
36
  Attributes:
35
- unique_id (int): A unique identifier for this agent.
36
37
  model (Model): A reference to the model instance.
37
- self.pos: Position | None = None
38
+ unique_id (int): A unique identifier for this agent.
39
+ pos (Position): A reference to the position where this agent is located.
40
+
41
+ Notes:
42
+ unique_id is unique relative to a model instance and starts from 1
43
+
38
44
  """
39
45
 
40
- def __init__(self, unique_id: int, model: Model) -> None:
46
+ # this is a class level attribute
47
+ # it is a dictionary, indexed by model instance
48
+ # so, unique_id is unique relative to a model, and counting starts from 1
49
+ _ids = defaultdict(functools.partial(itertools.count, 1))
50
+
51
+ def __init__(self, *args, **kwargs) -> None:
41
52
  """
42
53
  Create a new agent.
43
54
 
44
55
  Args:
45
- unique_id (int): A unique identifier for this agent.
46
56
  model (Model): The model instance in which the agent exists.
47
57
  """
48
- self.unique_id = unique_id
49
- self.model = model
58
+ # TODO: Cleanup in future Mesa version (3.1+)
59
+ match args:
60
+ # Case 1: Only the model is provided. The new correct behavior.
61
+ case [model]:
62
+ self.model = model
63
+ self.unique_id = next(self._ids[model])
64
+ # Case 2: Both unique_id and model are provided, deprecated
65
+ case [_, model]:
66
+ warnings.warn(
67
+ "unique ids are assigned automatically to Agents in Mesa 3. The use of custom unique_id is "
68
+ "deprecated. Only input a model when calling `super()__init__(model)`. The unique_id inputted is not used.",
69
+ DeprecationWarning,
70
+ stacklevel=2,
71
+ )
72
+ self.model = model
73
+ self.unique_id = next(self._ids[model])
74
+ # Case 3: Anything else, raise an error
75
+ case _:
76
+ raise ValueError(
77
+ "Invalid arguments provided to initialize the Agent. Only input a model: `super()__init__(model)`."
78
+ )
79
+
50
80
  self.pos: Position | None = None
51
81
 
52
82
  self.model.register_agent(self)
@@ -304,29 +334,72 @@ class AgentSet(MutableSet, Sequence):
304
334
 
305
335
  return res
306
336
 
307
- def get(self, attr_names: str | list[str]) -> list[Any]:
337
+ def agg(self, attribute: str, func: Callable) -> Any:
338
+ """
339
+ Aggregate an attribute of all agents in the AgentSet using a specified function.
340
+
341
+ Args:
342
+ attribute (str): The name of the attribute to aggregate.
343
+ func (Callable): The function to apply to the attribute values (e.g., min, max, sum, np.mean).
344
+
345
+ Returns:
346
+ Any: The result of applying the function to the attribute values. Often a single value.
347
+ """
348
+ values = self.get(attribute)
349
+ return func(values)
350
+
351
+ def get(
352
+ self,
353
+ attr_names: str | list[str],
354
+ handle_missing: Literal["error", "default"] = "error",
355
+ default_value: Any = None,
356
+ ) -> list[Any] | list[list[Any]]:
308
357
  """
309
358
  Retrieve the specified attribute(s) from each agent in the AgentSet.
310
359
 
311
360
  Args:
312
361
  attr_names (str | list[str]): The name(s) of the attribute(s) to retrieve from each agent.
362
+ handle_missing (str, optional): How to handle missing attributes. Can be:
363
+ - 'error' (default): raises an AttributeError if attribute is missing.
364
+ - 'default': returns the specified default_value.
365
+ default_value (Any, optional): The default value to return if 'handle_missing' is set to 'default'
366
+ and the agent does not have the attribute.
313
367
 
314
368
  Returns:
315
- list[Any]: A list with the attribute value for each agent in the set if attr_names is a str
316
- list[list[Any]]: A list with a list of attribute values for each agent in the set if attr_names is a list of str
369
+ list[Any]: A list with the attribute value for each agent if attr_names is a str.
370
+ list[list[Any]]: A list with a lists of attribute values for each agent if attr_names is a list of str.
317
371
 
318
372
  Raises:
319
- AttributeError if an agent does not have the specified attribute(s)
320
-
321
- """
373
+ AttributeError: If 'handle_missing' is 'error' and the agent does not have the specified attribute(s).
374
+ ValueError: If an unknown 'handle_missing' option is provided.
375
+ """
376
+ is_single_attr = isinstance(attr_names, str)
377
+
378
+ if handle_missing == "error":
379
+ if is_single_attr:
380
+ return [getattr(agent, attr_names) for agent in self._agents]
381
+ else:
382
+ return [
383
+ [getattr(agent, attr) for attr in attr_names]
384
+ for agent in self._agents
385
+ ]
386
+
387
+ elif handle_missing == "default":
388
+ if is_single_attr:
389
+ return [
390
+ getattr(agent, attr_names, default_value) for agent in self._agents
391
+ ]
392
+ else:
393
+ return [
394
+ [getattr(agent, attr, default_value) for attr in attr_names]
395
+ for agent in self._agents
396
+ ]
322
397
 
323
- if isinstance(attr_names, str):
324
- return [getattr(agent, attr_names) for agent in self._agents]
325
398
  else:
326
- return [
327
- [getattr(agent, attr_name) for attr_name in attr_names]
328
- for agent in self._agents
329
- ]
399
+ raise ValueError(
400
+ f"Unknown handle_missing option: {handle_missing}, "
401
+ "should be one of 'error' or 'default'"
402
+ )
330
403
 
331
404
  def set(self, attr_name: str, value: Any) -> AgentSet:
332
405
  """
@@ -1,4 +1,5 @@
1
1
  import itertools
2
+ import multiprocessing
2
3
  from collections.abc import Iterable, Mapping
3
4
  from functools import partial
4
5
  from multiprocessing import Pool
@@ -8,6 +9,8 @@ from tqdm.auto import tqdm
8
9
 
9
10
  from mesa.model import Model
10
11
 
12
+ multiprocessing.set_start_method("spawn", force=True)
13
+
11
14
 
12
15
  def batch_run(
13
16
  model_cls: type[Model],
@@ -0,0 +1,5 @@
1
+ from mesa.experimental import cell_space
2
+
3
+ from .solara_viz import JupyterViz, Slider, SolaraViz, make_text
4
+
5
+ __all__ = ["cell_space", "JupyterViz", "SolaraViz", "make_text", "Slider"]