Mesa 2.2.3__tar.gz → 2.2.4__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 (114) hide show
  1. {mesa-2.2.3 → mesa-2.2.4}/HISTORY.md +16 -0
  2. {mesa-2.2.3 → mesa-2.2.4}/PKG-INFO +1 -1
  3. {mesa-2.2.3 → mesa-2.2.4}/mesa/__init__.py +1 -1
  4. {mesa-2.2.3 → mesa-2.2.4}/mesa/agent.py +1 -0
  5. {mesa-2.2.3 → mesa-2.2.4}/mesa/datacollection.py +9 -8
  6. {mesa-2.2.3 → mesa-2.2.4}/mesa/experimental/jupyter_viz.py +1 -1
  7. {mesa-2.2.3 → mesa-2.2.4}/mesa/model.py +11 -1
  8. {mesa-2.2.3 → mesa-2.2.4}/mesa/time.py +10 -3
  9. {mesa-2.2.3 → mesa-2.2.4}/tests/test_agent.py +13 -0
  10. {mesa-2.2.3 → mesa-2.2.4}/tests/test_time.py +22 -0
  11. {mesa-2.2.3 → mesa-2.2.4}/.codespellignore +0 -0
  12. {mesa-2.2.3 → mesa-2.2.4}/.coveragerc +0 -0
  13. {mesa-2.2.3 → mesa-2.2.4}/.github/ISSUE_TEMPLATE/asking-help.md +0 -0
  14. {mesa-2.2.3 → mesa-2.2.4}/.github/ISSUE_TEMPLATE/bug-report.md +0 -0
  15. {mesa-2.2.3 → mesa-2.2.4}/.github/ISSUE_TEMPLATE/feature-request.md +0 -0
  16. {mesa-2.2.3 → mesa-2.2.4}/.github/dependabot.yml +0 -0
  17. {mesa-2.2.3 → mesa-2.2.4}/.github/release.yml +0 -0
  18. {mesa-2.2.3 → mesa-2.2.4}/.github/workflows/build_lint.yml +0 -0
  19. {mesa-2.2.3 → mesa-2.2.4}/.github/workflows/codespell.yml +0 -0
  20. {mesa-2.2.3 → mesa-2.2.4}/.github/workflows/release.yml +0 -0
  21. {mesa-2.2.3 → mesa-2.2.4}/.gitignore +0 -0
  22. {mesa-2.2.3 → mesa-2.2.4}/.pre-commit-config.yaml +0 -0
  23. {mesa-2.2.3 → mesa-2.2.4}/.readthedocs.yml +0 -0
  24. {mesa-2.2.3 → mesa-2.2.4}/CITATION.bib +0 -0
  25. {mesa-2.2.3 → mesa-2.2.4}/CODE_OF_CONDUCT.md +0 -0
  26. {mesa-2.2.3 → mesa-2.2.4}/CONTRIBUTING.rst +0 -0
  27. {mesa-2.2.3 → mesa-2.2.4}/Dockerfile +0 -0
  28. {mesa-2.2.3 → mesa-2.2.4}/LICENSE +0 -0
  29. {mesa-2.2.3 → mesa-2.2.4}/README.md +0 -0
  30. {mesa-2.2.3 → mesa-2.2.4}/docker-compose.yml +0 -0
  31. {mesa-2.2.3 → mesa-2.2.4}/docs/Makefile +0 -0
  32. {mesa-2.2.3 → mesa-2.2.4}/docs/README.md +0 -0
  33. {mesa-2.2.3 → mesa-2.2.4}/docs/apis/api_main.rst +0 -0
  34. {mesa-2.2.3 → mesa-2.2.4}/docs/apis/batchrunner.rst +0 -0
  35. {mesa-2.2.3 → mesa-2.2.4}/docs/apis/datacollection.rst +0 -0
  36. {mesa-2.2.3 → mesa-2.2.4}/docs/apis/init.rst +0 -0
  37. {mesa-2.2.3 → mesa-2.2.4}/docs/apis/space.rst +0 -0
  38. {mesa-2.2.3 → mesa-2.2.4}/docs/apis/time.rst +0 -0
  39. {mesa-2.2.3 → mesa-2.2.4}/docs/apis/visualization.rst +0 -0
  40. {mesa-2.2.3 → mesa-2.2.4}/docs/best-practices.rst +0 -0
  41. {mesa-2.2.3 → mesa-2.2.4}/docs/conf.py +0 -0
  42. {mesa-2.2.3 → mesa-2.2.4}/docs/howto.rst +0 -0
  43. {mesa-2.2.3 → mesa-2.2.4}/docs/images/Mesa_Screenshot.png +0 -0
  44. {mesa-2.2.3 → mesa-2.2.4}/docs/images/mesa_logo.ico +0 -0
  45. {mesa-2.2.3 → mesa-2.2.4}/docs/images/mesa_logo.png +0 -0
  46. {mesa-2.2.3 → mesa-2.2.4}/docs/images/tutorial/br_ginis.png +0 -0
  47. {mesa-2.2.3 → mesa-2.2.4}/docs/images/tutorial/dc_endwealth.png +0 -0
  48. {mesa-2.2.3 → mesa-2.2.4}/docs/images/tutorial/dc_gini.png +0 -0
  49. {mesa-2.2.3 → mesa-2.2.4}/docs/images/tutorial/dc_oneagent.png +0 -0
  50. {mesa-2.2.3 → mesa-2.2.4}/docs/images/tutorial/first_hist.png +0 -0
  51. {mesa-2.2.3 → mesa-2.2.4}/docs/images/tutorial/multirun_hist.png +0 -0
  52. {mesa-2.2.3 → mesa-2.2.4}/docs/images/tutorial/numpy_grid.png +0 -0
  53. {mesa-2.2.3 → mesa-2.2.4}/docs/images/tutorial/viz_chart.png +0 -0
  54. {mesa-2.2.3 → mesa-2.2.4}/docs/images/tutorial/viz_empty.png +0 -0
  55. {mesa-2.2.3 → mesa-2.2.4}/docs/images/tutorial/viz_greycircles.png +0 -0
  56. {mesa-2.2.3 → mesa-2.2.4}/docs/images/tutorial/viz_histogram.png +0 -0
  57. {mesa-2.2.3 → mesa-2.2.4}/docs/images/tutorial/viz_redcircles.png +0 -0
  58. {mesa-2.2.3 → mesa-2.2.4}/docs/index.rst +0 -0
  59. {mesa-2.2.3 → mesa-2.2.4}/docs/make.bat +0 -0
  60. {mesa-2.2.3 → mesa-2.2.4}/docs/mesa.rst +0 -0
  61. {mesa-2.2.3 → mesa-2.2.4}/docs/mesa.visualization.modules.rst +0 -0
  62. {mesa-2.2.3 → mesa-2.2.4}/docs/mesa.visualization.rst +0 -0
  63. {mesa-2.2.3 → mesa-2.2.4}/docs/modular-visualization.rst +0 -0
  64. {mesa-2.2.3 → mesa-2.2.4}/docs/overview.rst +0 -0
  65. {mesa-2.2.3 → mesa-2.2.4}/docs/packages.rst +0 -0
  66. {mesa-2.2.3 → mesa-2.2.4}/docs/tutorials/MoneyModel.py +0 -0
  67. {mesa-2.2.3 → mesa-2.2.4}/docs/tutorials/adv_tutorial_legacy.ipynb +0 -0
  68. {mesa-2.2.3 → mesa-2.2.4}/docs/tutorials/files/viz_chart.png +0 -0
  69. {mesa-2.2.3 → mesa-2.2.4}/docs/tutorials/files/viz_empty.png +0 -0
  70. {mesa-2.2.3 → mesa-2.2.4}/docs/tutorials/files/viz_greycircles.png +0 -0
  71. {mesa-2.2.3 → mesa-2.2.4}/docs/tutorials/files/viz_histogram.png +0 -0
  72. {mesa-2.2.3 → mesa-2.2.4}/docs/tutorials/files/viz_redcircles.png +0 -0
  73. {mesa-2.2.3 → mesa-2.2.4}/docs/tutorials/files/viz_slider.png +0 -0
  74. {mesa-2.2.3 → mesa-2.2.4}/docs/tutorials/intro_tutorial.ipynb +0 -0
  75. {mesa-2.2.3 → mesa-2.2.4}/docs/tutorials/visualization_tutorial.ipynb +0 -0
  76. {mesa-2.2.3 → mesa-2.2.4}/mesa/batchrunner.py +0 -0
  77. {mesa-2.2.3 → mesa-2.2.4}/mesa/cookiecutter-mesa/cookiecutter.json +0 -0
  78. {mesa-2.2.3 → mesa-2.2.4}/mesa/cookiecutter-mesa/hooks/post_gen_project.py +0 -0
  79. {mesa-2.2.3 → mesa-2.2.4}/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/README.md +0 -0
  80. {mesa-2.2.3 → mesa-2.2.4}/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/run.pytemplate +0 -0
  81. {mesa-2.2.3 → mesa-2.2.4}/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.pytemplate +0 -0
  82. {mesa-2.2.3 → mesa-2.2.4}/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/__init__.py +0 -0
  83. {mesa-2.2.3 → mesa-2.2.4}/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/model.pytemplate +0 -0
  84. {mesa-2.2.3 → mesa-2.2.4}/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/server.pytemplate +0 -0
  85. {mesa-2.2.3 → mesa-2.2.4}/mesa/experimental/__init__.py +0 -0
  86. {mesa-2.2.3 → mesa-2.2.4}/mesa/experimental/components/matplotlib.py +0 -0
  87. {mesa-2.2.3 → mesa-2.2.4}/mesa/flat/__init__.py +0 -0
  88. {mesa-2.2.3 → mesa-2.2.4}/mesa/flat/visualization.py +0 -0
  89. {mesa-2.2.3 → mesa-2.2.4}/mesa/main.py +0 -0
  90. {mesa-2.2.3 → mesa-2.2.4}/mesa/space.py +0 -0
  91. {mesa-2.2.3 → mesa-2.2.4}/mesa/visualization/ModularVisualization.py +0 -0
  92. {mesa-2.2.3 → mesa-2.2.4}/mesa/visualization/TextVisualization.py +0 -0
  93. {mesa-2.2.3 → mesa-2.2.4}/mesa/visualization/UserParam.py +0 -0
  94. {mesa-2.2.3 → mesa-2.2.4}/mesa/visualization/__init__.py +0 -0
  95. {mesa-2.2.3 → mesa-2.2.4}/mesa/visualization/modules.py +0 -0
  96. {mesa-2.2.3 → mesa-2.2.4}/mypy.ini +0 -0
  97. {mesa-2.2.3 → mesa-2.2.4}/pyproject.toml +0 -0
  98. {mesa-2.2.3 → mesa-2.2.4}/tests/__init__.py +0 -0
  99. {mesa-2.2.3 → mesa-2.2.4}/tests/read_requirements.py +0 -0
  100. {mesa-2.2.3 → mesa-2.2.4}/tests/test_batch_run.py +0 -0
  101. {mesa-2.2.3 → mesa-2.2.4}/tests/test_datacollector.py +0 -0
  102. {mesa-2.2.3 → mesa-2.2.4}/tests/test_end_to_end_viz.sh +0 -0
  103. {mesa-2.2.3 → mesa-2.2.4}/tests/test_examples.py +0 -0
  104. {mesa-2.2.3 → mesa-2.2.4}/tests/test_grid.py +0 -0
  105. {mesa-2.2.3 → mesa-2.2.4}/tests/test_import_namespace.py +0 -0
  106. {mesa-2.2.3 → mesa-2.2.4}/tests/test_jupyter_viz.py +0 -0
  107. {mesa-2.2.3 → mesa-2.2.4}/tests/test_lifespan.py +0 -0
  108. {mesa-2.2.3 → mesa-2.2.4}/tests/test_main.py +0 -0
  109. {mesa-2.2.3 → mesa-2.2.4}/tests/test_model.py +0 -0
  110. {mesa-2.2.3 → mesa-2.2.4}/tests/test_scaffold.py +0 -0
  111. {mesa-2.2.3 → mesa-2.2.4}/tests/test_space.py +0 -0
  112. {mesa-2.2.3 → mesa-2.2.4}/tests/test_tornado.py +0 -0
  113. {mesa-2.2.3 → mesa-2.2.4}/tests/test_usersettableparam.py +0 -0
  114. {mesa-2.2.3 → mesa-2.2.4}/tests/test_visualization.py +0 -0
@@ -2,6 +2,22 @@
2
2
  title: Release History
3
3
  ---
4
4
 
5
+ # 2.2.4 (2024-01-26)
6
+
7
+ ## Highlights
8
+ Mesa v2.2.4 is a small but important bugfix release for the 2.2 release series. It fixes an essential bug in where agents weren't shuffled in the `RandomActivation` scheduler (effectively making it sequential activation)([#2007](https://github.com/projectmesa/mesa/pull/2007)). It also fixes a small behaviour change in `RandomActivationByType.agents_by_type()` ([#1996](https://github.com/projectmesa/mesa/pull/1996)). Furthermore, this release adds an internal clock to the `Model`, which allows to use a Mesa model without a scheduler (using the `AgentSet` API)([#1942](https://github.com/projectmesa/mesa/pull/1942)).
9
+
10
+ Updating from previous 2.2 releases is highly recommended, especially when using the `RandomActivation` scheduler.
11
+
12
+ ## What's Changed
13
+ ### 🛠 Enhancements made
14
+ * refactor: Remove dependence on model.schedule, add clock to Model by @rht in https://github.com/projectmesa/mesa/pull/1942
15
+ ### 🐛 Bugs fixed
16
+ * Fix AgentSet inplace shuffle (and thus RandomActivation), add tests by @EwoutH in https://github.com/projectmesa/mesa/pull/2007
17
+ * fix: Reverse dict key and value for agents_by_type by @rht in https://github.com/projectmesa/mesa/pull/1996
18
+
19
+ **Full Changelog**: https://github.com/projectmesa/mesa/compare/v2.2.3...v2.2.4
20
+
5
21
  # 2.2.3 (2024-01-22)
6
22
 
7
23
  ## Highlights
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: Mesa
3
- Version: 2.2.3
3
+ Version: 2.2.4
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
@@ -25,7 +25,7 @@ __all__ = [
25
25
  ]
26
26
 
27
27
  __title__ = "mesa"
28
- __version__ = "2.2.3"
28
+ __version__ = "2.2.4"
29
29
  __license__ = "Apache 2.0"
30
30
  _this_year = datetime.datetime.now(tz=datetime.timezone.utc).date().year
31
31
  __copyright__ = f"Copyright {_this_year} Project Mesa Team"
@@ -54,6 +54,7 @@ class Agent:
54
54
  except AttributeError:
55
55
  # model super has not been called
56
56
  self.model.agents_ = defaultdict(dict)
57
+ self.model.agents_[type(self)][self] = None
57
58
  self.model.agentset_experimental_warning_given = False
58
59
 
59
60
  warnings.warn(
@@ -14,8 +14,7 @@ name.
14
14
 
15
15
  When the collect() method is called, each model-level function is called, with
16
16
  the model as the argument, and the results associated with the relevant
17
- variable. Then the agent-level functions are called on each agent in the model
18
- scheduler.
17
+ variable. Then the agent-level functions are called on each agent.
19
18
 
20
19
  Additionally, other objects can write directly to tables by passing in an
21
20
  appropriate dictionary object for a table row.
@@ -30,8 +29,7 @@ The DataCollector then stores the data it collects in dictionaries:
30
29
  Finally, DataCollector can create a pandas DataFrame from each collection.
31
30
 
32
31
  The default DataCollector here makes several assumptions:
33
- * The model has a schedule object called 'schedule'
34
- * The schedule has an agent list called agents
32
+ * The model has an agent list called agents
35
33
  * For collecting agent-level variables, agents must have a unique_id
36
34
  """
37
35
  import contextlib
@@ -67,7 +65,7 @@ class DataCollector:
67
65
 
68
66
  Model reporters can take four types of arguments:
69
67
  1. Lambda function:
70
- {"agent_count": lambda m: m.schedule.get_agent_count()}
68
+ {"agent_count": lambda m: len(m.agents)}
71
69
  2. Method of a class/instance:
72
70
  {"agent_count": self.get_agent_count} # self here is a class instance
73
71
  {"agent_count": Model.get_agent_count} # Model here is a class
@@ -180,11 +178,14 @@ class DataCollector:
180
178
  rep_funcs = self.agent_reporters.values()
181
179
 
182
180
  def get_reports(agent):
183
- _prefix = (agent.model.schedule.steps, agent.unique_id)
181
+ _prefix = (agent.model._steps, agent.unique_id)
184
182
  reports = tuple(rep(agent) for rep in rep_funcs)
185
183
  return _prefix + reports
186
184
 
187
- agent_records = map(get_reports, model.schedule.agents)
185
+ agent_records = map(
186
+ get_reports,
187
+ model.schedule.agents if hasattr(model, "schedule") else model.agents,
188
+ )
188
189
  return agent_records
189
190
 
190
191
  def collect(self, model):
@@ -207,7 +208,7 @@ class DataCollector:
207
208
 
208
209
  if self.agent_reporters:
209
210
  agent_records = self._record_agents(model)
210
- self._agent_records[model.schedule.steps] = list(agent_records)
211
+ self._agent_records[model._steps] = list(agent_records)
211
212
 
212
213
  def add_table_row(self, table_name, row, ignore_missing=False):
213
214
  """Add a row dictionary to a specific table.
@@ -178,7 +178,7 @@ def ModelController(model, play_interval, current_step, reset_counter):
178
178
  def do_step():
179
179
  model.step()
180
180
  previous_step.value = current_step.value
181
- current_step.value += 1
181
+ current_step.value = model._steps
182
182
 
183
183
  def do_play():
184
184
  model.running = True
@@ -13,11 +13,13 @@ import warnings
13
13
  from collections import defaultdict
14
14
 
15
15
  # mypy
16
- from typing import Any
16
+ from typing import Any, Union
17
17
 
18
18
  from mesa.agent import Agent, AgentSet
19
19
  from mesa.datacollection import DataCollector
20
20
 
21
+ TimeT = Union[float, int]
22
+
21
23
 
22
24
  class Model:
23
25
  """Base class for models in the Mesa ABM library.
@@ -68,6 +70,9 @@ class Model:
68
70
  self.current_id = 0
69
71
  self.agents_: defaultdict[type, dict] = defaultdict(dict)
70
72
 
73
+ self._steps: int = 0
74
+ self._time: TimeT = 0 # the model's clock
75
+
71
76
  # Warning flags for current experimental features. These make sure a warning is only printed once per model.
72
77
  self.agentset_experimental_warning_given = False
73
78
 
@@ -112,6 +117,11 @@ class Model:
112
117
  def step(self) -> None:
113
118
  """A single step. Fill in here."""
114
119
 
120
+ def _advance_time(self, deltat: TimeT = 1):
121
+ """Increment the model's steps counter and clock."""
122
+ self._steps += 1
123
+ self._time += deltat
124
+
115
125
  def next_id(self) -> int:
116
126
  """Return the next unique ID for agents, increment current_id"""
117
127
  self.current_id += 1
@@ -73,6 +73,8 @@ class BaseScheduler:
73
73
  self.model = model
74
74
  self.steps = 0
75
75
  self.time: TimeT = 0
76
+ self._original_step = self.step
77
+ self.step = self._wrapped_step
76
78
 
77
79
  if agents is None:
78
80
  agents = []
@@ -115,6 +117,11 @@ class BaseScheduler:
115
117
  self.steps += 1
116
118
  self.time += 1
117
119
 
120
+ def _wrapped_step(self):
121
+ """Wrapper for the step method to include time and step updating."""
122
+ self._original_step()
123
+ self.model._advance_time()
124
+
118
125
  def get_agent_count(self) -> int:
119
126
  """Returns the current number of agents in the queue."""
120
127
  return len(self._agents)
@@ -143,8 +150,8 @@ class BaseScheduler:
143
150
 
144
151
  def do_each(self, method, shuffle=False):
145
152
  if shuffle:
146
- self.agents.shuffle(inplace=True)
147
- self.agents.do(method)
153
+ self._agents.shuffle(inplace=True)
154
+ self._agents.do(method)
148
155
 
149
156
 
150
157
  class RandomActivation(BaseScheduler):
@@ -299,7 +306,7 @@ class RandomActivationByType(BaseScheduler):
299
306
 
300
307
  agentsbytype = defaultdict(dict)
301
308
  for k, v in self._agents_by_type.items():
302
- agentsbytype[k] = {agent: agent.unique_id for agent in v}
309
+ agentsbytype[k] = {agent.unique_id: agent for agent in v}
303
310
 
304
311
  return agentsbytype
305
312
 
@@ -251,3 +251,16 @@ def test_agentset_select_by_type():
251
251
  # Test with no type specified (should select all agents)
252
252
  all_agents = agentset.select()
253
253
  assert len(all_agents) == len(mixed_agents)
254
+
255
+
256
+ def test_agentset_shuffle():
257
+ model = Model()
258
+ test_agents = [TestAgent(model.next_id(), model) for _ in range(12)]
259
+
260
+ agentset = AgentSet(test_agents, model=model)
261
+ agentset = agentset.shuffle()
262
+ assert not all(a1 == a2 for a1, a2 in zip(test_agents, agentset))
263
+
264
+ agentset = AgentSet(test_agents, model=model)
265
+ agentset.shuffle(inplace=True)
266
+ assert not all(a1 == a2 for a1, a2 in zip(test_agents, agentset))
@@ -224,6 +224,28 @@ class TestRandomActivation(TestCase):
224
224
  agent_ids = {agent.unique_id for agent in model.agents}
225
225
  assert all(entry in agent_ids for entry in keys)
226
226
 
227
+ def test_not_sequential(self):
228
+ model = MockModel(activation=RANDOM)
229
+ # Create 10 agents
230
+ for _ in range(10):
231
+ model.schedule.add(MockAgent(model.next_id(), model))
232
+ # Run 3 steps
233
+ for _ in range(3):
234
+ model.step()
235
+ # Filter out non-integer elements from the log
236
+ filtered_log = [item for item in model.log if isinstance(item, int)]
237
+
238
+ # Check that there are no 18 consecutive agents id's in the filtered log
239
+ total_agents = 10
240
+ assert not any(
241
+ all(
242
+ (filtered_log[(i + j) % total_agents] - filtered_log[i]) % total_agents
243
+ == j % total_agents
244
+ for j in range(18)
245
+ )
246
+ for i in range(len(filtered_log))
247
+ ), f"Agents are activated sequentially:\n{filtered_log}"
248
+
227
249
 
228
250
  class TestSimultaneousActivation(TestCase):
229
251
  """
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes