Mesa 1.1.0__py3-none-any.whl → 1.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of Mesa might be problematic. Click here for more details.
- {Mesa-1.1.0.dist-info → Mesa-1.2.0.dist-info}/LICENSE +1 -1
- {Mesa-1.1.0.dist-info → Mesa-1.2.0.dist-info}/METADATA +15 -14
- {Mesa-1.1.0.dist-info → Mesa-1.2.0.dist-info}/RECORD +41 -41
- {Mesa-1.1.0.dist-info → Mesa-1.2.0.dist-info}/WHEEL +1 -1
- mesa/__init__.py +8 -9
- mesa/agent.py +2 -3
- mesa/batchrunner.py +19 -28
- mesa/datacollection.py +15 -28
- mesa/main.py +4 -4
- mesa/model.py +2 -6
- mesa/space.py +379 -286
- mesa/time.py +21 -22
- mesa/visualization/ModularVisualization.py +11 -9
- mesa/visualization/TextVisualization.py +0 -3
- mesa/visualization/UserParam.py +8 -11
- mesa/visualization/__init__.py +0 -1
- mesa/visualization/modules/BarChartVisualization.py +7 -8
- mesa/visualization/modules/CanvasGridVisualization.py +1 -3
- mesa/visualization/modules/ChartVisualization.py +2 -3
- mesa/visualization/modules/HexGridVisualization.py +1 -3
- mesa/visualization/modules/NetworkVisualization.py +1 -2
- mesa/visualization/modules/PieChartVisualization.py +2 -6
- mesa/visualization/templates/js/GridDraw.js +6 -10
- mesa/visualization/templates/js/HexDraw.js +5 -9
- mesa/visualization/templates/js/InteractionHandler.js +0 -2
- tests/test_batchrunner.py +3 -4
- tests/test_batchrunnerMP.py +4 -4
- tests/test_datacollector.py +2 -2
- tests/test_examples.py +8 -5
- tests/test_grid.py +104 -37
- tests/test_import_namespace.py +0 -1
- tests/test_lifespan.py +4 -3
- tests/test_main.py +5 -1
- tests/test_scaffold.py +2 -1
- tests/test_space.py +13 -20
- tests/test_time.py +44 -14
- tests/test_tornado.py +4 -2
- tests/test_usersettableparam.py +4 -3
- tests/test_visualization.py +4 -8
- {Mesa-1.1.0.dist-info → Mesa-1.2.0.dist-info}/entry_points.txt +0 -0
- {Mesa-1.1.0.dist-info → Mesa-1.2.0.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: Mesa
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Agent-based modeling (ABM) in Python 3+
|
|
5
5
|
Home-page: https://github.com/projectmesa/mesa
|
|
6
6
|
Author: Project Mesa Team
|
|
@@ -12,7 +12,6 @@ Classifier: Topic :: Scientific/Engineering :: Artificial Life
|
|
|
12
12
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
13
13
|
Classifier: Intended Audience :: Science/Research
|
|
14
14
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.8
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.9
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.10
|
|
@@ -20,7 +19,7 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
|
20
19
|
Classifier: Operating System :: OS Independent
|
|
21
20
|
Classifier: Development Status :: 3 - Alpha
|
|
22
21
|
Classifier: Natural Language :: English
|
|
23
|
-
Requires-Python: >=3.
|
|
22
|
+
Requires-Python: >=3.8
|
|
24
23
|
License-File: LICENSE
|
|
25
24
|
Requires-Dist: click
|
|
26
25
|
Requires-Dist: cookiecutter
|
|
@@ -31,8 +30,8 @@ Requires-Dist: tornado
|
|
|
31
30
|
Requires-Dist: tqdm
|
|
32
31
|
Provides-Extra: dev
|
|
33
32
|
Requires-Dist: black ; extra == 'dev'
|
|
33
|
+
Requires-Dist: ruff ; extra == 'dev'
|
|
34
34
|
Requires-Dist: coverage ; extra == 'dev'
|
|
35
|
-
Requires-Dist: flake8 ; extra == 'dev'
|
|
36
35
|
Requires-Dist: pytest (>=4.6) ; extra == 'dev'
|
|
37
36
|
Requires-Dist: pytest-cov ; extra == 'dev'
|
|
38
37
|
Requires-Dist: sphinx ; extra == 'dev'
|
|
@@ -55,7 +54,7 @@ Mesa: Agent-based modeling in Python 3+
|
|
|
55
54
|
.. image:: https://img.shields.io/matrix/project-mesa:matrix.org?label=chat&logo=Matrix
|
|
56
55
|
:target: https://matrix.to/#/#project-mesa:matrix.org
|
|
57
56
|
|
|
58
|
-
|
|
57
|
+
Mesa allows users to quickly create agent-based models using built-in core components (such as spatial grids and agent schedulers) or customized implementations; visualize them using a browser-based interface; and analyze their results using Python's data analysis tools. Its goal is to be the Python 3-based alternative to NetLogo, Repast, or MASON.
|
|
59
58
|
|
|
60
59
|
|
|
61
60
|
.. image:: https://raw.githubusercontent.com/projectmesa/mesa/main/docs/images/Mesa_Screenshot.png
|
|
@@ -99,19 +98,21 @@ Or any other (development) branch on this repo or your own fork:
|
|
|
99
98
|
|
|
100
99
|
$ pip install -U -e git+https://github.com/YOUR_FORK/mesa@YOUR_BRANCH#egg=mesa
|
|
101
100
|
|
|
102
|
-
|
|
101
|
+
For resources or help on using Mesa, check out the following:
|
|
103
102
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
* `
|
|
107
|
-
* `Docs`_
|
|
108
|
-
* `
|
|
109
|
-
* `
|
|
103
|
+
* `Intro to Mesa Tutorial`_ (An introductory model, the Boltzmann Wealth Model, for beginners or those new to Mesa.)
|
|
104
|
+
* `Complexity Explorer Tutorial`_ (An advanced-beginner model, SugarScape with Traders, with instructional videos)
|
|
105
|
+
* `Mesa Examples`_ (A repository of seminal ABMs using Mesa and examples of employing specific Mesa Features)
|
|
106
|
+
* `Docs`_ (Mesa's documentation, API and useful snippets)
|
|
107
|
+
* `Discussions`_ (GitHub threaded discussions about Mesa)
|
|
108
|
+
* `Matrix Chat`_ (Chat Forum via Matrix to talk about Mesa)
|
|
110
109
|
|
|
111
110
|
.. _`Intro to Mesa Tutorial` : http://mesa.readthedocs.org/en/main/tutorials/intro_tutorial.html
|
|
111
|
+
.. _`Complexity Explorer Tutorial` : https://www.complexityexplorer.org/courses/172-agent-based-models-with-python-an-introduction-to-mesa
|
|
112
|
+
.. _`Mesa Examples` : https://github.com/projectmesa/mesa-examples/tree/main/examples
|
|
112
113
|
.. _`Docs` : http://mesa.readthedocs.org/en/main/
|
|
113
|
-
.. _`
|
|
114
|
-
.. _`
|
|
114
|
+
.. _`Discussions` : https://github.com/projectmesa/mesa/discussions
|
|
115
|
+
.. _`Matrix Chat` : https://matrix.to/#/#project-mesa:matrix.org
|
|
115
116
|
|
|
116
117
|
Running Mesa in Docker
|
|
117
118
|
------------------------
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
mesa/__init__.py,sha256=
|
|
2
|
-
mesa/agent.py,sha256=
|
|
3
|
-
mesa/batchrunner.py,sha256=
|
|
4
|
-
mesa/datacollection.py,sha256=
|
|
5
|
-
mesa/main.py,sha256=
|
|
6
|
-
mesa/model.py,sha256=
|
|
7
|
-
mesa/space.py,sha256=
|
|
8
|
-
mesa/time.py,sha256=
|
|
1
|
+
mesa/__init__.py,sha256=14vZcuboYtBmYU8mAk01zDJs7PzTvzX7McgKwHRniyY,664
|
|
2
|
+
mesa/agent.py,sha256=EtfTa33MMSkiZ66hXl0LBVSyFpbb1paf0CUUBQS7yao,1081
|
|
3
|
+
mesa/batchrunner.py,sha256=8a1Vw9Kz7VIPv6LCTOd02UZkY8TQSWTXhTMJB4bEbBE,27850
|
|
4
|
+
mesa/datacollection.py,sha256=jgwmvhH_1c8R0FX43DqOUrNLQ6KqTYWEjwb1G4V6Do4,10129
|
|
5
|
+
mesa/main.py,sha256=hy73jEXQSvq8z-0IIvC0lYGqQNLIrwZsWkvMP35KdLc,1147
|
|
6
|
+
mesa/model.py,sha256=lww4nK_A0K_zc5e_JL_dwDae1_2juvF3lZJ61yB7jeA,2595
|
|
7
|
+
mesa/space.py,sha256=dw4n5RNro2oA_yI6K0ZOmLTsRfXAyi0RsFyQq3340Y8,39673
|
|
8
|
+
mesa/time.py,sha256=ZTk9qkvPhgGfaKtX7DaOeeK8eabVutTKS9-iThNUEf4,10665
|
|
9
9
|
mesa/cookiecutter-mesa/cookiecutter.json,sha256=tBSWli39fOWUXGfiDCTKd92M7uKaBIswXbkOdbUufYY,337
|
|
10
10
|
mesa/flat/__init__.py,sha256=hSqQDjkfIgDu7B3aYtjPeNEUXdlKPHQuNN8HEV0XR6s,218
|
|
11
11
|
mesa/flat/visualization.py,sha256=WC01m3B6dduwV7-85rP9BdH2cxiCv6RJrsltv_oEtb0,298
|
|
12
|
-
mesa/visualization/ModularVisualization.py,sha256=
|
|
13
|
-
mesa/visualization/TextVisualization.py,sha256
|
|
14
|
-
mesa/visualization/UserParam.py,sha256=
|
|
15
|
-
mesa/visualization/__init__.py,sha256=
|
|
16
|
-
mesa/visualization/modules/BarChartVisualization.py,sha256=
|
|
17
|
-
mesa/visualization/modules/CanvasGridVisualization.py,sha256=
|
|
18
|
-
mesa/visualization/modules/ChartVisualization.py,sha256=
|
|
19
|
-
mesa/visualization/modules/HexGridVisualization.py,sha256=
|
|
20
|
-
mesa/visualization/modules/NetworkVisualization.py,sha256=
|
|
21
|
-
mesa/visualization/modules/PieChartVisualization.py,sha256=
|
|
12
|
+
mesa/visualization/ModularVisualization.py,sha256=0rcfkvtmEd7RXOW0H09Wea3fhZY0nepI1nPExd_po8A,14637
|
|
13
|
+
mesa/visualization/TextVisualization.py,sha256=qGlX6aROX9vyeygOwuGphK8ae0WfSF51qrgJ7YkHOnA,3792
|
|
14
|
+
mesa/visualization/UserParam.py,sha256=6eWCGJNhbS3avoOGc7n2h9QYRgwY1-_u2HOaZq64VyY,8688
|
|
15
|
+
mesa/visualization/__init__.py,sha256=E67QA_Aw2Prrr-uwKqWWtcl_7pNSLfpfJmt1dH6RlS8,268
|
|
16
|
+
mesa/visualization/modules/BarChartVisualization.py,sha256=0iFUT7itUWb6g_Op9DXbkPrW9LXViFr1Bxr3dGmYHuA,3698
|
|
17
|
+
mesa/visualization/modules/CanvasGridVisualization.py,sha256=O8m7OuvL8FvesLa2OOZwgS06NOpnQX4HilZlLZSykJc,4742
|
|
18
|
+
mesa/visualization/modules/ChartVisualization.py,sha256=qql1EzfaRaIx30GlHB9i_LRBIUzula4yqHXsqI3Lv2k,3144
|
|
19
|
+
mesa/visualization/modules/HexGridVisualization.py,sha256=e0gaxkwolnMb97PUFCidNRGgaix2K1URS1jpAouzay8,3541
|
|
20
|
+
mesa/visualization/modules/NetworkVisualization.py,sha256=HQeBsWwmxL1zNsWRYI_im88doa7sjaxWpvNXN4WIr78,838
|
|
21
|
+
mesa/visualization/modules/PieChartVisualization.py,sha256=v3tBnxdxVBXMp9P9nNRGzp_SR7w0FrMJgm6s3z1ynMg,2450
|
|
22
22
|
mesa/visualization/modules/__init__.py,sha256=GfB3QILVF6FmEY1Kdz6uZ8AToRAUfv_URR_3jALyNsc,726
|
|
23
23
|
mesa/visualization/templates/modular_template.html,sha256=0ZBBTVGI5jbBlT7hBJhMum8qI7cXvpdvBfnzPDrtg18,4546
|
|
24
24
|
mesa/visualization/templates/css/visualization.css,sha256=vOjdwmVKb7PTbqljH7dvosbnk3ycoOvVxZ_PgSu2HmI,377
|
|
@@ -136,9 +136,9 @@ mesa/visualization/templates/js/BarChartModule.js,sha256=kmR2dgssnXLyE7WOFqoyGGA
|
|
|
136
136
|
mesa/visualization/templates/js/CanvasHexModule.js,sha256=XzUf5zPPl3xjVqi1Dw3aNgJn_1kiTQ6CNZicV4k9QqY,1610
|
|
137
137
|
mesa/visualization/templates/js/CanvasModule.js,sha256=BxPirbRJPH41m5tqOklNf8BKlzzgh93AmzDD3D7LeL4,1689
|
|
138
138
|
mesa/visualization/templates/js/ChartModule.js,sha256=wAFHZlTfqtYv_S6Ajx0gswK4F-l2fxPAa59QrgZOydI,2175
|
|
139
|
-
mesa/visualization/templates/js/GridDraw.js,sha256=
|
|
140
|
-
mesa/visualization/templates/js/HexDraw.js,sha256=
|
|
141
|
-
mesa/visualization/templates/js/InteractionHandler.js,sha256=
|
|
139
|
+
mesa/visualization/templates/js/GridDraw.js,sha256=sm2_WFfqaG5jVSQtOLVvoPnROipO1G8w-i9DTa_DfLI,12943
|
|
140
|
+
mesa/visualization/templates/js/HexDraw.js,sha256=gfaqLDLdpe7-eV_qbBNwYACC4uC1Q63gHQe179WEb6o,8609
|
|
141
|
+
mesa/visualization/templates/js/InteractionHandler.js,sha256=89JxZtBeROT0y78guQmrTrmXdmelvpbAz1dwqkgOf-g,5295
|
|
142
142
|
mesa/visualization/templates/js/NetworkModule_d3.js,sha256=E7_qrYufXKMHPtgO-Ezqxxyo3UQGYCoWJNoWxZLAgR4,3002
|
|
143
143
|
mesa/visualization/templates/js/PieChartModule.js,sha256=WZFevQ3DPmOYNm48TjF03XtxvKvAlke9VRhXrGckNN4,2826
|
|
144
144
|
mesa/visualization/templates/js/TextModule.js,sha256=bCTQkGrJiQ64EwzaxlkHciyk8ekUomWUgnGKUjvxm3o,327
|
|
@@ -147,24 +147,24 @@ mesa/visualization/templates/js/external/chart-3.6.1.min.js,sha256=bj8JMRF_ShthP
|
|
|
147
147
|
mesa/visualization/templates/js/external/d3-7.4.3.min.js,sha256=-MpQAOeqA18DtHk7JETZadE17KoMl0jCS89tlo6OWGM,277072
|
|
148
148
|
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
149
149
|
tests/test_batch_run.py,sha256=WyfwEPKG-E4E6mH6ASjKSNXTRVog3UfB-vNRnZUcPx8,5052
|
|
150
|
-
tests/test_batchrunner.py,sha256=
|
|
151
|
-
tests/test_batchrunnerMP.py,sha256=
|
|
152
|
-
tests/test_datacollector.py,sha256
|
|
153
|
-
tests/test_examples.py,sha256=
|
|
154
|
-
tests/test_grid.py,sha256=
|
|
155
|
-
tests/test_import_namespace.py,sha256=
|
|
156
|
-
tests/test_lifespan.py,sha256=
|
|
157
|
-
tests/test_main.py,sha256=
|
|
150
|
+
tests/test_batchrunner.py,sha256=3G--CpUmBA7dr1fKCbzOhocufaUM2QeoQ_mkftMe4x0,11434
|
|
151
|
+
tests/test_batchrunnerMP.py,sha256=G2rXmD2HG9gfgY6GzJEshDFqh-uFPCpP8XtjMUo3UyU,9107
|
|
152
|
+
tests/test_datacollector.py,sha256=-SDMwmEzXRzL1a7I5X7WFWHk_BVJiW98Qsi2W0K5rjU,6616
|
|
153
|
+
tests/test_examples.py,sha256=kiNZfkXjJnrEV4Z5m78itnxv-N3TLII_O52-KzlU_eo,2506
|
|
154
|
+
tests/test_grid.py,sha256=ZOk_bJKrpZ-Ug_mYUDjaSdzwCraQ6U_iYYz8YziIeSI,16738
|
|
155
|
+
tests/test_import_namespace.py,sha256=OZ94QiaCKVsl64-rjWLfh1QYMpYokfpeRcfK_OwvI8w,724
|
|
156
|
+
tests/test_lifespan.py,sha256=5x4sNx19hEKVd4HHMEjIn5Jrk7rvMufKfp0iVA1MCfI,2834
|
|
157
|
+
tests/test_main.py,sha256=bAKTUPj4MWd7oDr6pNEyQoJrvJfeGEeCd6Jkl2GXT8U,984
|
|
158
158
|
tests/test_model.py,sha256=IyVs2IL1F_Pn54oEfSIDcU5b7kbi2AOSwFs8c4vpmg8,970
|
|
159
|
-
tests/test_scaffold.py,sha256=
|
|
160
|
-
tests/test_space.py,sha256=
|
|
161
|
-
tests/test_time.py,sha256=
|
|
162
|
-
tests/test_tornado.py,sha256=
|
|
163
|
-
tests/test_usersettableparam.py,sha256=
|
|
164
|
-
tests/test_visualization.py,sha256=
|
|
165
|
-
Mesa-1.
|
|
166
|
-
Mesa-1.
|
|
167
|
-
Mesa-1.
|
|
168
|
-
Mesa-1.
|
|
169
|
-
Mesa-1.
|
|
170
|
-
Mesa-1.
|
|
159
|
+
tests/test_scaffold.py,sha256=kZa0PGlCaZCxEYdFurcWEudSG3IaQdZnrHvcukmiBm0,539
|
|
160
|
+
tests/test_space.py,sha256=ZHMbzNAHtmp5j69wOiSA6KLUE137r4N1xZ02pEwW3FY,15928
|
|
161
|
+
tests/test_time.py,sha256=r25vm0ATW3YfQreBP8avkENJrBI56yBwHgWe3Y7RNyo,8561
|
|
162
|
+
tests/test_tornado.py,sha256=DInrFEL2n7k9x5S-ZClEFno4OlitjzyDk45lZ9Lgs5g,1270
|
|
163
|
+
tests/test_usersettableparam.py,sha256=NdEFjSrNvXuvkWvWInRtZ4u0aLO3DDxURHjW2ePJeRw,2564
|
|
164
|
+
tests/test_visualization.py,sha256=yMwzx2GrNpdqpGT3k6dh9DmBHrnrvTrIZh8-lViTe50,2783
|
|
165
|
+
Mesa-1.2.0.dist-info/LICENSE,sha256=rgf5rJXcrpdESYDM17Gew0097eH4JdWjqQ2EnLHnFHE,572
|
|
166
|
+
Mesa-1.2.0.dist-info/METADATA,sha256=c0AUmaRqoLepPzRed99vb62IwswvKUtxUF6gbDQbsdU,7156
|
|
167
|
+
Mesa-1.2.0.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
|
|
168
|
+
Mesa-1.2.0.dist-info/entry_points.txt,sha256=IOcQtetGF8l4wHpOs_hGb19Rz-FS__BMXOJR10IBPsA,39
|
|
169
|
+
Mesa-1.2.0.dist-info/top_level.txt,sha256=VYd8OkxFUc18-7nokv6pPVD0L_gwvqHDbacL8peWqXM,11
|
|
170
|
+
Mesa-1.2.0.dist-info/RECORD,,
|
mesa/__init__.py
CHANGED
|
@@ -2,18 +2,16 @@
|
|
|
2
2
|
Mesa Agent-Based Modeling Framework
|
|
3
3
|
|
|
4
4
|
Core Objects: Model, and Agent.
|
|
5
|
-
|
|
6
5
|
"""
|
|
7
6
|
import datetime
|
|
8
7
|
|
|
9
|
-
from mesa.model import Model
|
|
10
|
-
from mesa.agent import Agent
|
|
11
|
-
|
|
12
|
-
import mesa.time as time
|
|
13
|
-
import mesa.space as space
|
|
14
8
|
import mesa.flat.visualization as visualization
|
|
9
|
+
import mesa.space as space
|
|
10
|
+
import mesa.time as time
|
|
11
|
+
from mesa.agent import Agent
|
|
12
|
+
from mesa.batchrunner import batch_run
|
|
15
13
|
from mesa.datacollection import DataCollector
|
|
16
|
-
from mesa.
|
|
14
|
+
from mesa.model import Model
|
|
17
15
|
|
|
18
16
|
__all__ = [
|
|
19
17
|
"Model",
|
|
@@ -26,6 +24,7 @@ __all__ = [
|
|
|
26
24
|
]
|
|
27
25
|
|
|
28
26
|
__title__ = "mesa"
|
|
29
|
-
__version__ = "1.
|
|
27
|
+
__version__ = "1.2.0"
|
|
30
28
|
__license__ = "Apache 2.0"
|
|
31
|
-
|
|
29
|
+
_this_year = datetime.datetime.now(tz=datetime.timezone.utc).date().year
|
|
30
|
+
__copyright__ = f"Copyright {_this_year} Project Mesa Team"
|
mesa/agent.py
CHANGED
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
The agent class for Mesa framework.
|
|
3
3
|
|
|
4
4
|
Core Objects: Agent
|
|
5
|
-
|
|
6
5
|
"""
|
|
7
6
|
# Mypy; for the `|` operator purpose
|
|
8
7
|
# Remove this __future__ import once the oldest supported Python is 3.10
|
|
9
8
|
from __future__ import annotations
|
|
10
9
|
|
|
10
|
+
from random import Random
|
|
11
|
+
|
|
11
12
|
# mypy
|
|
12
13
|
from typing import TYPE_CHECKING
|
|
13
|
-
from random import Random
|
|
14
14
|
|
|
15
15
|
if TYPE_CHECKING:
|
|
16
16
|
# We ensure that these are not imported during runtime to prevent cyclic
|
|
@@ -35,7 +35,6 @@ class Agent:
|
|
|
35
35
|
|
|
36
36
|
def step(self) -> None:
|
|
37
37
|
"""A single step of the agent."""
|
|
38
|
-
pass
|
|
39
38
|
|
|
40
39
|
def advance(self) -> None:
|
|
41
40
|
pass
|
mesa/batchrunner.py
CHANGED
|
@@ -3,16 +3,13 @@ Batchrunner
|
|
|
3
3
|
===========
|
|
4
4
|
|
|
5
5
|
A single class to manage a batch run or parameter sweep of a given model.
|
|
6
|
-
|
|
7
6
|
"""
|
|
8
7
|
import copy
|
|
9
8
|
import itertools
|
|
10
9
|
import random
|
|
11
|
-
from collections import OrderedDict
|
|
12
10
|
from functools import partial
|
|
13
11
|
from itertools import count, product
|
|
14
12
|
from multiprocessing import Pool, cpu_count
|
|
15
|
-
from warnings import warn
|
|
16
13
|
from typing import (
|
|
17
14
|
Any,
|
|
18
15
|
Dict,
|
|
@@ -24,6 +21,7 @@ from typing import (
|
|
|
24
21
|
Type,
|
|
25
22
|
Union,
|
|
26
23
|
)
|
|
24
|
+
from warnings import warn
|
|
27
25
|
|
|
28
26
|
import pandas as pd
|
|
29
27
|
from tqdm import tqdm
|
|
@@ -285,14 +283,13 @@ class FixedBatchRunner:
|
|
|
285
283
|
collected at the level of each agent present in the model at
|
|
286
284
|
the end of the run.
|
|
287
285
|
display_progress: Display progress bar with time estimation?
|
|
288
|
-
|
|
289
286
|
"""
|
|
290
287
|
self.model_cls = model_cls
|
|
291
288
|
if parameters_list is None:
|
|
292
289
|
parameters_list = []
|
|
293
290
|
self.parameters_list = list(parameters_list)
|
|
294
291
|
self.fixed_parameters = fixed_parameters or {}
|
|
295
|
-
self._include_fixed = len(self.fixed_parameters
|
|
292
|
+
self._include_fixed = len(self.fixed_parameters) > 0
|
|
296
293
|
self.iterations = iterations
|
|
297
294
|
self.max_steps = max_steps
|
|
298
295
|
|
|
@@ -310,9 +307,8 @@ class FixedBatchRunner:
|
|
|
310
307
|
if self.agent_reporters:
|
|
311
308
|
self.agent_vars = {}
|
|
312
309
|
|
|
313
|
-
|
|
314
|
-
self.
|
|
315
|
-
self.datacollector_agent_reporters = OrderedDict()
|
|
310
|
+
self.datacollector_model_reporters = {}
|
|
311
|
+
self.datacollector_agent_reporters = {}
|
|
316
312
|
|
|
317
313
|
self.display_progress = display_progress
|
|
318
314
|
|
|
@@ -361,7 +357,7 @@ class FixedBatchRunner:
|
|
|
361
357
|
model = self.model_cls(**kwargs)
|
|
362
358
|
results = self.run_model(model)
|
|
363
359
|
if param_values is not None:
|
|
364
|
-
model_key = tuple(param_values)
|
|
360
|
+
model_key = (*tuple(param_values), run_count)
|
|
365
361
|
else:
|
|
366
362
|
model_key = (run_count,)
|
|
367
363
|
|
|
@@ -370,7 +366,7 @@ class FixedBatchRunner:
|
|
|
370
366
|
if self.agent_reporters:
|
|
371
367
|
agent_vars = self.collect_agent_vars(model)
|
|
372
368
|
for agent_id, reports in agent_vars.items():
|
|
373
|
-
agent_key = model_key
|
|
369
|
+
agent_key = (*model_key, agent_id)
|
|
374
370
|
self.agent_vars[agent_key] = reports
|
|
375
371
|
# Collects data from datacollector object in model
|
|
376
372
|
if results is not None:
|
|
@@ -395,7 +391,6 @@ class FixedBatchRunner:
|
|
|
395
391
|
|
|
396
392
|
If your model runs in a non-standard way, this is the method to modify
|
|
397
393
|
in your subclass.
|
|
398
|
-
|
|
399
394
|
"""
|
|
400
395
|
while model.running and model.schedule.steps < self.max_steps:
|
|
401
396
|
model.step()
|
|
@@ -407,19 +402,19 @@ class FixedBatchRunner:
|
|
|
407
402
|
|
|
408
403
|
def collect_model_vars(self, model):
|
|
409
404
|
"""Run reporters and collect model-level variables."""
|
|
410
|
-
model_vars =
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
405
|
+
model_vars = {
|
|
406
|
+
var: reporter(model) for var, reporter in self.model_reporters.items()
|
|
407
|
+
}
|
|
414
408
|
return model_vars
|
|
415
409
|
|
|
416
410
|
def collect_agent_vars(self, model):
|
|
417
411
|
"""Run reporters and collect agent-level variables."""
|
|
418
|
-
agent_vars =
|
|
412
|
+
agent_vars = {}
|
|
419
413
|
for agent in model.schedule._agents.values():
|
|
420
|
-
agent_record =
|
|
421
|
-
|
|
422
|
-
|
|
414
|
+
agent_record = {
|
|
415
|
+
var: getattr(agent, reporter)
|
|
416
|
+
for var, reporter in self.agent_reporters.items()
|
|
417
|
+
}
|
|
423
418
|
agent_vars[agent.unique_id] = agent_record
|
|
424
419
|
return agent_vars
|
|
425
420
|
|
|
@@ -471,12 +466,10 @@ class FixedBatchRunner:
|
|
|
471
466
|
|
|
472
467
|
df = pd.DataFrame(records)
|
|
473
468
|
rest_cols = set(df.columns) - set(index_cols)
|
|
474
|
-
ordered = df[index_cols +
|
|
469
|
+
ordered = df[index_cols + sorted(rest_cols)]
|
|
475
470
|
ordered.sort_values(by="Run", inplace=True)
|
|
476
471
|
if self._include_fixed:
|
|
477
|
-
for param in self.fixed_parameters.
|
|
478
|
-
val = self.fixed_parameters[param]
|
|
479
|
-
|
|
472
|
+
for param, val in self.fixed_parameters.items():
|
|
480
473
|
# avoid error when val is an iterable
|
|
481
474
|
vallist = [val for i in range(ordered.shape[0])]
|
|
482
475
|
ordered[param] = vallist
|
|
@@ -539,7 +532,6 @@ class BatchRunner(FixedBatchRunner):
|
|
|
539
532
|
Note that by default, the reporters only collect data at the *end* of the
|
|
540
533
|
run. To get step by step data, simply have a reporter store the model's
|
|
541
534
|
entire DataCollector object.
|
|
542
|
-
|
|
543
535
|
"""
|
|
544
536
|
|
|
545
537
|
def __init__(
|
|
@@ -582,7 +574,6 @@ class BatchRunner(FixedBatchRunner):
|
|
|
582
574
|
collected at the level of each agent present in the model at
|
|
583
575
|
the end of the run.
|
|
584
576
|
display_progress: Display progress bar with time estimation?
|
|
585
|
-
|
|
586
577
|
"""
|
|
587
578
|
warn(
|
|
588
579
|
"BatchRunner class has been replaced by batch_run function. Please see documentation.",
|
|
@@ -722,7 +713,7 @@ class BatchRunnerMP(BatchRunner): # pragma: no cover
|
|
|
722
713
|
if self.agent_reporters:
|
|
723
714
|
agent_vars = self.collect_agent_vars(model)
|
|
724
715
|
for agent_id, reports in agent_vars.items():
|
|
725
|
-
agent_key = model_key
|
|
716
|
+
agent_key = (*model_key, agent_id)
|
|
726
717
|
self.agent_vars[agent_key] = reports
|
|
727
718
|
if hasattr(model, "datacollector"):
|
|
728
719
|
if model.datacollector.model_reporters is not None:
|
|
@@ -735,9 +726,9 @@ class BatchRunnerMP(BatchRunner): # pragma: no cover
|
|
|
735
726
|
] = model.datacollector.get_agent_vars_dataframe()
|
|
736
727
|
|
|
737
728
|
# Make results consistent
|
|
738
|
-
if len(self.datacollector_model_reporters
|
|
729
|
+
if len(self.datacollector_model_reporters) == 0:
|
|
739
730
|
self.datacollector_model_reporters = None
|
|
740
|
-
if len(self.datacollector_agent_reporters
|
|
731
|
+
if len(self.datacollector_agent_reporters) == 0:
|
|
741
732
|
self.datacollector_agent_reporters = None
|
|
742
733
|
|
|
743
734
|
def run_all(self):
|
mesa/datacollection.py
CHANGED
|
@@ -33,13 +33,13 @@ The default DataCollector here makes several assumptions:
|
|
|
33
33
|
* The model has a schedule object called 'schedule'
|
|
34
34
|
* The schedule has an agent list called agents
|
|
35
35
|
* For collecting agent-level variables, agents must have a unique_id
|
|
36
|
-
|
|
37
36
|
"""
|
|
38
|
-
from functools import partial
|
|
39
37
|
import itertools
|
|
38
|
+
import types
|
|
39
|
+
from functools import partial
|
|
40
40
|
from operator import attrgetter
|
|
41
|
+
|
|
41
42
|
import pandas as pd
|
|
42
|
-
import types
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
class DataCollector:
|
|
@@ -50,11 +50,8 @@ class DataCollector:
|
|
|
50
50
|
functions which actually collect them. When the collect(...) method is
|
|
51
51
|
called, it collects these attributes and executes these functions one by
|
|
52
52
|
one and stores the results.
|
|
53
|
-
|
|
54
53
|
"""
|
|
55
54
|
|
|
56
|
-
model = None
|
|
57
|
-
|
|
58
55
|
def __init__(self, model_reporters=None, agent_reporters=None, tables=None):
|
|
59
56
|
"""Instantiate a DataCollector with lists of model and agent reporters.
|
|
60
57
|
Both model_reporters and agent_reporters accept a dictionary mapping a
|
|
@@ -87,13 +84,13 @@ class DataCollector:
|
|
|
87
84
|
Model reporters can take four types of arguments:
|
|
88
85
|
lambda like above:
|
|
89
86
|
{"agent_count": lambda m: m.schedule.get_agent_count() }
|
|
90
|
-
method
|
|
91
|
-
{"agent_count":
|
|
92
|
-
|
|
87
|
+
method of a class/instance:
|
|
88
|
+
{"agent_count": self.get_agent_count} # self here is a class instance
|
|
89
|
+
{"agent_count": Model.get_agent_count} # Model here is a class
|
|
90
|
+
class attributes of a model
|
|
93
91
|
{"model_attribute": "model_attribute"}
|
|
94
92
|
functions with parameters that have placed in a list
|
|
95
93
|
{"Model_Function":[function, [param_1, param_2]]}
|
|
96
|
-
|
|
97
94
|
"""
|
|
98
95
|
self.model_reporters = {}
|
|
99
96
|
self.agent_reporters = {}
|
|
@@ -122,8 +119,6 @@ class DataCollector:
|
|
|
122
119
|
reporter: Attribute string, or function object that returns the
|
|
123
120
|
variable when given a model instance.
|
|
124
121
|
"""
|
|
125
|
-
if type(reporter) is str:
|
|
126
|
-
reporter = partial(self._getattr, reporter)
|
|
127
122
|
self.model_reporters[name] = reporter
|
|
128
123
|
self.model_vars[name] = []
|
|
129
124
|
|
|
@@ -134,7 +129,6 @@ class DataCollector:
|
|
|
134
129
|
name: Name of the agent-level variable to collect.
|
|
135
130
|
reporter: Attribute string, or function object that returns the
|
|
136
131
|
variable when given a model instance.
|
|
137
|
-
|
|
138
132
|
"""
|
|
139
133
|
if type(reporter) is str:
|
|
140
134
|
attribute_name = reporter
|
|
@@ -148,7 +142,6 @@ class DataCollector:
|
|
|
148
142
|
Args:
|
|
149
143
|
table_name: Name of the new table.
|
|
150
144
|
table_columns: List of columns to add to the table.
|
|
151
|
-
|
|
152
145
|
"""
|
|
153
146
|
new_table = {column: [] for column in table_columns}
|
|
154
147
|
self.tables[table_name] = new_table
|
|
@@ -156,7 +149,7 @@ class DataCollector:
|
|
|
156
149
|
def _record_agents(self, model):
|
|
157
150
|
"""Record agents data in a mapping of functions and agents."""
|
|
158
151
|
rep_funcs = self.agent_reporters.values()
|
|
159
|
-
if all(
|
|
152
|
+
if all(hasattr(rep, "attribute_name") for rep in rep_funcs):
|
|
160
153
|
prefix = ["model.schedule.steps", "unique_id"]
|
|
161
154
|
attributes = [func.attribute_name for func in rep_funcs]
|
|
162
155
|
get_reports = attrgetter(*prefix + attributes)
|
|
@@ -170,25 +163,23 @@ class DataCollector:
|
|
|
170
163
|
agent_records = map(get_reports, model.schedule.agents)
|
|
171
164
|
return agent_records
|
|
172
165
|
|
|
173
|
-
def _reporter_decorator(self, reporter):
|
|
174
|
-
return reporter()
|
|
175
|
-
|
|
176
166
|
def collect(self, model):
|
|
177
167
|
"""Collect all the data for the given model object."""
|
|
178
168
|
if self.model_reporters:
|
|
179
|
-
|
|
180
169
|
for var, reporter in self.model_reporters.items():
|
|
181
170
|
# Check if Lambda operator
|
|
182
171
|
if isinstance(reporter, types.LambdaType):
|
|
183
172
|
self.model_vars[var].append(reporter(model))
|
|
184
173
|
# Check if model attribute
|
|
185
|
-
elif isinstance(reporter,
|
|
186
|
-
self.model_vars[var].append(
|
|
174
|
+
elif isinstance(reporter, str):
|
|
175
|
+
self.model_vars[var].append(getattr(model, reporter, None))
|
|
187
176
|
# Check if function with arguments
|
|
188
177
|
elif isinstance(reporter, list):
|
|
189
178
|
self.model_vars[var].append(reporter[0](*reporter[1]))
|
|
179
|
+
# TODO: Check if method of a class, as of now it is assumed
|
|
180
|
+
# implicitly if the other checks fail.
|
|
190
181
|
else:
|
|
191
|
-
self.model_vars[var].append(
|
|
182
|
+
self.model_vars[var].append(reporter())
|
|
192
183
|
|
|
193
184
|
if self.agent_reporters:
|
|
194
185
|
agent_records = self._record_agents(model)
|
|
@@ -202,7 +193,6 @@ class DataCollector:
|
|
|
202
193
|
row: A dictionary of the form {column_name: value...}
|
|
203
194
|
ignore_missing: If True, fill any missing columns with Nones;
|
|
204
195
|
if False, throw an error if any columns are missing
|
|
205
|
-
|
|
206
196
|
"""
|
|
207
197
|
if table_name not in self.tables:
|
|
208
198
|
raise Exception("Table does not exist.")
|
|
@@ -225,7 +215,6 @@ class DataCollector:
|
|
|
225
215
|
|
|
226
216
|
The DataFrame has one column for each model variable, and the index is
|
|
227
217
|
(implicitly) the model tick.
|
|
228
|
-
|
|
229
218
|
"""
|
|
230
219
|
return pd.DataFrame(self.model_vars)
|
|
231
220
|
|
|
@@ -234,16 +223,15 @@ class DataCollector:
|
|
|
234
223
|
|
|
235
224
|
The DataFrame has one column for each variable, with two additional
|
|
236
225
|
columns for tick and agent_id.
|
|
237
|
-
|
|
238
226
|
"""
|
|
239
227
|
all_records = itertools.chain.from_iterable(self._agent_records.values())
|
|
240
228
|
rep_names = list(self.agent_reporters)
|
|
241
229
|
|
|
242
230
|
df = pd.DataFrame.from_records(
|
|
243
231
|
data=all_records,
|
|
244
|
-
columns=["Step", "AgentID"
|
|
232
|
+
columns=["Step", "AgentID", *rep_names],
|
|
233
|
+
index=["Step", "AgentID"],
|
|
245
234
|
)
|
|
246
|
-
df = df.set_index(["Step", "AgentID"])
|
|
247
235
|
return df
|
|
248
236
|
|
|
249
237
|
def get_table_dataframe(self, table_name):
|
|
@@ -251,7 +239,6 @@ class DataCollector:
|
|
|
251
239
|
|
|
252
240
|
Args:
|
|
253
241
|
table_name: The name of the table to convert.
|
|
254
|
-
|
|
255
242
|
"""
|
|
256
243
|
if table_name not in self.tables:
|
|
257
244
|
raise Exception("No such table.")
|
mesa/main.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import sys
|
|
2
1
|
import os
|
|
3
|
-
import
|
|
2
|
+
import sys
|
|
4
3
|
from subprocess import call
|
|
5
4
|
|
|
5
|
+
import click
|
|
6
|
+
|
|
6
7
|
PROJECT_PATH = click.Path(
|
|
7
8
|
exists=True, file_okay=False, dir_okay=True, resolve_path=True
|
|
8
9
|
)
|
|
@@ -14,7 +15,6 @@ COOKIECUTTER_PATH = os.path.join(os.path.dirname(SCRIPTS_DIR), COOKIECUTTER_DIR)
|
|
|
14
15
|
@click.group()
|
|
15
16
|
def cli():
|
|
16
17
|
"Manage Mesa projects"
|
|
17
|
-
pass
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
@cli.command()
|
|
@@ -30,7 +30,7 @@ def runserver(project):
|
|
|
30
30
|
|
|
31
31
|
with open("run.py") as f:
|
|
32
32
|
code = compile(f.read(), "run.py", "exec")
|
|
33
|
-
exec(code, {}, {})
|
|
33
|
+
exec(code, {}, {}) # noqa: S102
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
@click.command()
|
mesa/model.py
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
The model class for Mesa framework.
|
|
3
3
|
|
|
4
4
|
Core Objects: Model
|
|
5
|
-
|
|
6
5
|
"""
|
|
7
6
|
# Mypy; for the `|` operator purpose
|
|
8
7
|
# Remove this __future__ import once the oldest supported Python is 3.10
|
|
@@ -10,11 +9,11 @@ from __future__ import annotations
|
|
|
10
9
|
|
|
11
10
|
import random
|
|
12
11
|
|
|
13
|
-
from mesa.datacollection import DataCollector
|
|
14
|
-
|
|
15
12
|
# mypy
|
|
16
13
|
from typing import Any
|
|
17
14
|
|
|
15
|
+
from mesa.datacollection import DataCollector
|
|
16
|
+
|
|
18
17
|
|
|
19
18
|
class Model:
|
|
20
19
|
"""Base class for models."""
|
|
@@ -33,7 +32,6 @@ class Model:
|
|
|
33
32
|
Attributes:
|
|
34
33
|
schedule: schedule object
|
|
35
34
|
running: a bool indicating if the model should continue running
|
|
36
|
-
|
|
37
35
|
"""
|
|
38
36
|
|
|
39
37
|
self.running = True
|
|
@@ -43,14 +41,12 @@ class Model:
|
|
|
43
41
|
def run_model(self) -> None:
|
|
44
42
|
"""Run the model until the end condition is reached. Overload as
|
|
45
43
|
needed.
|
|
46
|
-
|
|
47
44
|
"""
|
|
48
45
|
while self.running:
|
|
49
46
|
self.step()
|
|
50
47
|
|
|
51
48
|
def step(self) -> None:
|
|
52
49
|
"""A single step. Fill in here."""
|
|
53
|
-
pass
|
|
54
50
|
|
|
55
51
|
def next_id(self) -> int:
|
|
56
52
|
"""Return the next unique ID for agents, increment current_id"""
|