Mesa 3.1.0__py3-none-any.whl → 3.1.0.dev0__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/__init__.py +3 -3
- mesa/agent.py +0 -48
- mesa/batchrunner.py +1 -14
- mesa/datacollection.py +6 -1
- mesa/examples/__init__.py +2 -2
- mesa/examples/advanced/epstein_civil_violence/app.py +0 -5
- mesa/examples/advanced/pd_grid/app.py +0 -5
- mesa/examples/advanced/sugarscape_g1mt/app.py +2 -7
- mesa/examples/basic/boid_flockers/app.py +0 -5
- mesa/examples/basic/boltzmann_wealth_model/app.py +5 -8
- mesa/examples/basic/boltzmann_wealth_model/st_app.py +1 -1
- mesa/examples/basic/conways_game_of_life/app.py +0 -5
- mesa/examples/basic/conways_game_of_life/st_app.py +2 -2
- mesa/examples/basic/schelling/app.py +0 -5
- mesa/examples/basic/virus_on_network/app.py +0 -5
- mesa/experimental/UserParam.py +67 -0
- mesa/experimental/__init__.py +10 -17
- mesa/experimental/cell_space/__init__.py +7 -19
- mesa/experimental/cell_space/cell.py +37 -22
- mesa/experimental/cell_space/cell_agent.py +1 -12
- mesa/experimental/cell_space/cell_collection.py +3 -18
- mesa/experimental/cell_space/discrete_space.py +64 -15
- mesa/experimental/cell_space/grid.py +4 -74
- mesa/experimental/cell_space/network.py +1 -13
- mesa/experimental/cell_space/voronoi.py +1 -13
- mesa/experimental/components/altair.py +81 -0
- mesa/experimental/components/matplotlib.py +242 -0
- mesa/experimental/devs/__init__.py +2 -20
- mesa/experimental/devs/eventlist.py +1 -19
- mesa/experimental/devs/examples/epstein_civil_violence.py +305 -0
- mesa/experimental/devs/examples/wolf_sheep.py +250 -0
- mesa/experimental/devs/simulator.py +8 -24
- mesa/experimental/solara_viz.py +453 -0
- mesa/model.py +23 -17
- mesa/visualization/__init__.py +2 -2
- mesa/visualization/mpl_space_drawing.py +2 -2
- mesa/visualization/solara_viz.py +5 -23
- {mesa-3.1.0.dist-info → mesa-3.1.0.dev0.dist-info}/METADATA +1 -1
- {mesa-3.1.0.dist-info → mesa-3.1.0.dev0.dist-info}/RECORD +43 -43
- {mesa-3.1.0.dist-info → mesa-3.1.0.dev0.dist-info}/WHEEL +1 -1
- mesa/experimental/cell_space/property_layer.py +0 -444
- mesa/experimental/mesa_signals/__init__.py +0 -23
- mesa/experimental/mesa_signals/mesa_signal.py +0 -485
- mesa/experimental/mesa_signals/observable_collections.py +0 -133
- mesa/experimental/mesa_signals/signals_util.py +0 -52
- mesa/mesa_logging.py +0 -190
- {mesa-3.1.0.dist-info → mesa-3.1.0.dev0.dist-info}/entry_points.txt +0 -0
- {mesa-3.1.0.dist-info → mesa-3.1.0.dev0.dist-info}/licenses/LICENSE +0 -0
- {mesa-3.1.0.dist-info → mesa-3.1.0.dev0.dist-info}/licenses/NOTICE +0 -0
|
@@ -1,29 +1,28 @@
|
|
|
1
|
-
mesa/__init__.py,sha256=
|
|
2
|
-
mesa/agent.py,sha256=
|
|
3
|
-
mesa/batchrunner.py,sha256=
|
|
4
|
-
mesa/datacollection.py,sha256=
|
|
5
|
-
mesa/
|
|
6
|
-
mesa/model.py,sha256=oVcV-_OZ3GVv1sb3KqyGA9TAWCDQu9qEDWwRi_AXR40,8353
|
|
1
|
+
mesa/__init__.py,sha256=yfsF1GQ64lI5t02W_YGXJWnGEiiyGvxJeIKP8Gkc43I,615
|
|
2
|
+
mesa/agent.py,sha256=_ZI_cR9ZR_mzEBrOXorFe3UlmeJM7e1-ODV8MoW6tRk,24406
|
|
3
|
+
mesa/batchrunner.py,sha256=sMFLTxj5avP_-HGO0leLVuxXK2dH0xdPopvhAmawwRQ,7213
|
|
4
|
+
mesa/datacollection.py,sha256=xyb07aBpd-HSDh5bk-XcVqGiDu5bfaLlxj5eDlGIwqY,16138
|
|
5
|
+
mesa/model.py,sha256=EzDYnwkt9Bm5fEah8JztzjghFBzUmnIr4WknbaR7zzk,8573
|
|
7
6
|
mesa/space.py,sha256=cfzlRfy9chegp8d89k2aqI29jo9cb18USlz2G2iOZU4,64082
|
|
8
7
|
mesa/examples/README.md,sha256=dNn8kv0BNQem3NNhO5mbOANQoK8UUYOo7rnkCFV9tnE,2882
|
|
9
|
-
mesa/examples/__init__.py,sha256=
|
|
8
|
+
mesa/examples/__init__.py,sha256=fP1vcexySnKowS2CKhfeyYLt058VpIkK26EOo3btyO0,825
|
|
10
9
|
mesa/examples/advanced/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
10
|
mesa/examples/advanced/epstein_civil_violence/Epstein Civil Violence.ipynb,sha256=yh50ZAK2BVJyJIKsQTTxywnasqWn1IiQUVrwmZKue4w,29032
|
|
12
11
|
mesa/examples/advanced/epstein_civil_violence/Readme.md,sha256=RXuGIZAibz3KVkP51PGjwzcRx2R9Ettmh3qbDTPqDcg,1735
|
|
13
12
|
mesa/examples/advanced/epstein_civil_violence/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
13
|
mesa/examples/advanced/epstein_civil_violence/agents.py,sha256=0X4JLj2K2cl1bACkFLI6-K6tKbr2SLZlAy_kjgUDjzA,5863
|
|
15
|
-
mesa/examples/advanced/epstein_civil_violence/app.py,sha256=
|
|
14
|
+
mesa/examples/advanced/epstein_civil_violence/app.py,sha256=puJmjkEevHyilfDpnkE65Y2ax6JEyOALg-IEtK8jBKk,1787
|
|
16
15
|
mesa/examples/advanced/epstein_civil_violence/model.py,sha256=fcTkjCRhEhDerDC1W_lrezdoWD1y5xIublkGIhCak8w,3918
|
|
17
16
|
mesa/examples/advanced/pd_grid/Readme.md,sha256=UVUQxZRFdfymCKDdQEn3ZEwgSqgp9cKJPsU8oZpLFnY,2367
|
|
18
17
|
mesa/examples/advanced/pd_grid/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
18
|
mesa/examples/advanced/pd_grid/agents.py,sha256=DzGj4LZUjV0xMQAwkRsv2Aoap1cUvLscJswfA1PdVDs,1716
|
|
20
19
|
mesa/examples/advanced/pd_grid/analysis.ipynb,sha256=ReYtRe2JVyCCXoMBONvynXDQ_eGtQSWhNcuJY3CYTTI,82323
|
|
21
|
-
mesa/examples/advanced/pd_grid/app.py,sha256
|
|
20
|
+
mesa/examples/advanced/pd_grid/app.py,sha256=u8dnB3cXkTENU26O1XN3REFTxEwvXLFLD3EybgVQWP0,1354
|
|
22
21
|
mesa/examples/advanced/pd_grid/model.py,sha256=DiHGeX7fR1slvcAzbmDCxnmXJ2Txjmju_gII3e-EJgY,2345
|
|
23
22
|
mesa/examples/advanced/sugarscape_g1mt/Readme.md,sha256=x3kKw1Rre2FPkNhGDLtdzeThmH089mxsGYUPZUeu26k,3595
|
|
24
23
|
mesa/examples/advanced/sugarscape_g1mt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
24
|
mesa/examples/advanced/sugarscape_g1mt/agents.py,sha256=zE7zbO2_OaQiFDq_-t-0hv4ButBWv26dnS1zaPc3_ZE,10183
|
|
26
|
-
mesa/examples/advanced/sugarscape_g1mt/app.py,sha256=
|
|
25
|
+
mesa/examples/advanced/sugarscape_g1mt/app.py,sha256=ZB0jIBG6sLrVXxmuQyqqhJyaSCLvWmbe8oqgn7oaWcs,2781
|
|
27
26
|
mesa/examples/advanced/sugarscape_g1mt/model.py,sha256=NFQRqwuOwP3kz1fNNPEm2XkdbS0G3-TgneiJdQZHqck,6111
|
|
28
27
|
mesa/examples/advanced/sugarscape_g1mt/sugar-map.txt,sha256=zZtGYciBPT4miZVnbVuoQ5TugTmGrbDWV9yb5KH6tnU,5000
|
|
29
28
|
mesa/examples/advanced/sugarscape_g1mt/tests.py,sha256=UNahmZTgLquSqmoi_9GcE3JP0qBHjkrHFZ15NMm0ce8,2517
|
|
@@ -36,59 +35,60 @@ mesa/examples/basic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
|
|
|
36
35
|
mesa/examples/basic/boid_flockers/Readme.md,sha256=4KJinsLPtUciQSMzvaX3tU5r1HTUg3AFOFDKy73W5RE,894
|
|
37
36
|
mesa/examples/basic/boid_flockers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
37
|
mesa/examples/basic/boid_flockers/agents.py,sha256=72oL4jWEiJfSgshSEcL44irptIY6WAQDRAQ0cO2MxS8,3827
|
|
39
|
-
mesa/examples/basic/boid_flockers/app.py,sha256=
|
|
38
|
+
mesa/examples/basic/boid_flockers/app.py,sha256=CLXzUJ8-9FdrNVElGsAas6l37n_qSv8akH4hXEZQ8OY,1141
|
|
40
39
|
mesa/examples/basic/boid_flockers/model.py,sha256=scy0OaYzQVY5vVarsI7dUydynwoH3-uYoYcsBTtwtyA,3399
|
|
41
40
|
mesa/examples/basic/boltzmann_wealth_model/Readme.md,sha256=wl1ylO9KWoTiuIJKOnk2FGdcmyVUqJ5wiSbVUa3WWAc,2725
|
|
42
41
|
mesa/examples/basic/boltzmann_wealth_model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
43
42
|
mesa/examples/basic/boltzmann_wealth_model/agents.py,sha256=Jol2aspw--UtQn0EiReXVmlWtzpdC2o_YUTAyu1sDk4,1546
|
|
44
|
-
mesa/examples/basic/boltzmann_wealth_model/app.py,sha256=
|
|
43
|
+
mesa/examples/basic/boltzmann_wealth_model/app.py,sha256=JlGpOmkjLut0hM9rY_BSGiRfHnrwpzlOi8Cw2Drv3-s,2160
|
|
45
44
|
mesa/examples/basic/boltzmann_wealth_model/model.py,sha256=ED4EFHJlrrcF12WYOEpXpPt5asK2T6uzpq_S97EtfHo,2941
|
|
46
|
-
mesa/examples/basic/boltzmann_wealth_model/st_app.py,sha256=
|
|
45
|
+
mesa/examples/basic/boltzmann_wealth_model/st_app.py,sha256=X8C7w294UidciQqiiuBWkypaOQS79aS3kQKydT21V4c,3456
|
|
47
46
|
mesa/examples/basic/conways_game_of_life/Readme.md,sha256=VRgN6roF6leQ_IMYwxFypSfFjZo9jnCd-rkPTjpp7II,1453
|
|
48
47
|
mesa/examples/basic/conways_game_of_life/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
49
48
|
mesa/examples/basic/conways_game_of_life/agents.py,sha256=ETn87GV8it9p0nlSn6f1qu5CDEmqokkr6Rq7KWaT-5U,1627
|
|
50
|
-
mesa/examples/basic/conways_game_of_life/app.py,sha256=
|
|
49
|
+
mesa/examples/basic/conways_game_of_life/app.py,sha256=Q2Z16x2D9e8BYErn7xIvgdwyxKeeQKO2wNPbxi2acEw,1894
|
|
51
50
|
mesa/examples/basic/conways_game_of_life/model.py,sha256=8L88m8F_YauwWeDef6UA2myQoAwC0B5sH5jsfyLn-u8,1221
|
|
52
|
-
mesa/examples/basic/conways_game_of_life/st_app.py,sha256=
|
|
51
|
+
mesa/examples/basic/conways_game_of_life/st_app.py,sha256=_poqU2VR4rfiiq4WFXTbOUSW7liuM86iq913mW6LS50,2400
|
|
53
52
|
mesa/examples/basic/schelling/Readme.md,sha256=CRKBfYtnLJLlTKLsTRQ-7gsQRxVxDooOBN5uP8PEtaU,2296
|
|
54
53
|
mesa/examples/basic/schelling/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
55
54
|
mesa/examples/basic/schelling/agents.py,sha256=BzipyAU5d_H4DoTm3xkeZLDazsr49rbaVr_kyk1rDJA,937
|
|
56
55
|
mesa/examples/basic/schelling/analysis.ipynb,sha256=JDJy6-U6eO-LrHWxZr1c3lkvtoY0DNHa-kJ4J-Z-wwo,5804
|
|
57
|
-
mesa/examples/basic/schelling/app.py,sha256=
|
|
56
|
+
mesa/examples/basic/schelling/app.py,sha256=UDIL6MBIfFPLNT8D9Y0sV0aJwfgQR9aNZxq5png8Gbs,954
|
|
58
57
|
mesa/examples/basic/schelling/model.py,sha256=ruqiMNBBsWV81srYZgdsfDVwgMWYuw2VAzSQhsK5E98,2762
|
|
59
58
|
mesa/examples/basic/virus_on_network/Readme.md,sha256=qmXGx4Fo0tRBvJiiJ684bkWJPn2gcFaiikLwgc5APWI,2336
|
|
60
59
|
mesa/examples/basic/virus_on_network/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
61
60
|
mesa/examples/basic/virus_on_network/agents.py,sha256=a_WhqYblJlW6od67eXfU-nb7IMRyYpgxtf0le--VYoA,1975
|
|
62
|
-
mesa/examples/basic/virus_on_network/app.py,sha256=
|
|
61
|
+
mesa/examples/basic/virus_on_network/app.py,sha256=6bkIYXj1cdhhBksVT5UN8OrrZFVrHJXRzIknhlQcuu4,2450
|
|
63
62
|
mesa/examples/basic/virus_on_network/model.py,sha256=jQoCmvygwCvhUrlL0l7V8GcDLv94CgwtuK7DDGU8q8g,2813
|
|
64
|
-
mesa/experimental/
|
|
65
|
-
mesa/experimental/
|
|
66
|
-
mesa/experimental/
|
|
67
|
-
mesa/experimental/cell_space/
|
|
68
|
-
mesa/experimental/cell_space/
|
|
69
|
-
mesa/experimental/cell_space/
|
|
70
|
-
mesa/experimental/cell_space/
|
|
71
|
-
mesa/experimental/cell_space/
|
|
72
|
-
mesa/experimental/cell_space/
|
|
73
|
-
mesa/experimental/cell_space/
|
|
74
|
-
mesa/experimental/
|
|
75
|
-
mesa/experimental/
|
|
76
|
-
mesa/experimental/
|
|
77
|
-
mesa/experimental/
|
|
78
|
-
mesa/experimental/
|
|
79
|
-
mesa/experimental/
|
|
80
|
-
mesa/experimental/
|
|
81
|
-
mesa/
|
|
82
|
-
mesa/visualization/
|
|
83
|
-
mesa/visualization/
|
|
63
|
+
mesa/experimental/UserParam.py,sha256=f32nmFjroe76HpxU75ZCEOqFW2nAsDfmqIf8kQ1zt-E,2086
|
|
64
|
+
mesa/experimental/__init__.py,sha256=faBYyHSp-sFQPaBx8-WsLToVKCndpSzpt18HmNZtasM,385
|
|
65
|
+
mesa/experimental/solara_viz.py,sha256=uWrNQAX3oEWSftmyjNorN839dBCUp86hnhpL704dyGQ,15212
|
|
66
|
+
mesa/experimental/cell_space/__init__.py,sha256=-NtSCT7mA-aszLSAdqbICGqeFe2vBdb-GrcW562legY,999
|
|
67
|
+
mesa/experimental/cell_space/cell.py,sha256=lDm7NQhPDFf3-SZu5W594lDNGtzcdSPUSSsELFRg0bs,7166
|
|
68
|
+
mesa/experimental/cell_space/cell_agent.py,sha256=jvYOV9OIaBaqAsAG0YLV64X_f3BJe_wP7qfos_RXi0Y,3759
|
|
69
|
+
mesa/experimental/cell_space/cell_collection.py,sha256=ADK_42ykyWQpVGVSFE8GG_djqTgL8wfWwu2YTwXUPcs,4007
|
|
70
|
+
mesa/experimental/cell_space/discrete_space.py,sha256=A54lHbTo4udcU4zkJEnsWuWKOBrELg-1eU8-dPoLMPk,5826
|
|
71
|
+
mesa/experimental/cell_space/grid.py,sha256=oWTy6kaaHXLPneA-w5XdqzwA0YItMWYgCq_UjNH9iA8,7711
|
|
72
|
+
mesa/experimental/cell_space/network.py,sha256=_x0zKlI-odNCSRb_Zqh4nBPjqnW5iVj8sVheKPCLzmU,1321
|
|
73
|
+
mesa/experimental/cell_space/voronoi.py,sha256=lSY8zQhELvOy0RfDyZIek09UMwY9_20UY9SPqFWsNoM,10014
|
|
74
|
+
mesa/experimental/components/altair.py,sha256=49OHgrm1JncfrKqDfw_5ifPtsbMKdgVYCacL9SMwucc,2624
|
|
75
|
+
mesa/experimental/components/matplotlib.py,sha256=j477UBk_7yW5vzT3rjhnuTixpA7PedDNghoK9TLgHVY,8043
|
|
76
|
+
mesa/experimental/devs/__init__.py,sha256=EByaC66ikUIu9G9p1geLm6ESEMWZOPTO9r9627S83j0,211
|
|
77
|
+
mesa/experimental/devs/eventlist.py,sha256=fPj2jfW-jTe-UnIE6TsF1BM2ITKe3jGfVUhu3gBv7UQ,6250
|
|
78
|
+
mesa/experimental/devs/simulator.py,sha256=RHlJsri57zf22i4fdEdUKekuJUunB1oRzEbhDntMuOk,12107
|
|
79
|
+
mesa/experimental/devs/examples/epstein_civil_violence.py,sha256=E8YSV3O5ihKsntGtnltHM-4IyS8eg2DSRUqmIiw_1iU,10916
|
|
80
|
+
mesa/experimental/devs/examples/wolf_sheep.py,sha256=1eb1CfYNQoprqSJat-LPYPvwWH1ENQdj39viEqwSk0s,8103
|
|
81
|
+
mesa/visualization/__init__.py,sha256=o30F2VApnDJ6Zq0_U5_wBnj-6-Bu7FGPsOq3A6p2EqA,743
|
|
82
|
+
mesa/visualization/mpl_space_drawing.py,sha256=yf3Sc7pm5EJLxfWQdtR-9PcUtZveEEjar1WUr7qwI4o,20057
|
|
83
|
+
mesa/visualization/solara_viz.py,sha256=pGtfjFfFMLwF9vmy5btiHRZww6NGoiKu0tzNdhZaYj4,17947
|
|
84
84
|
mesa/visualization/user_param.py,sha256=Dl2WOwLYLf0pfLpabCZtIdFRyKZrK6Qtc3utZx5GPYg,2139
|
|
85
85
|
mesa/visualization/utils.py,sha256=lJHgRKF5BHLf72Tw3YpwyiWuRoIimaTKQ7xBCw_Rx3A,146
|
|
86
86
|
mesa/visualization/components/__init__.py,sha256=Bq3nrPikcaIo9BSs0O3zptWVLlUmAkLo3s0mEmpH1RE,3022
|
|
87
87
|
mesa/visualization/components/altair_components.py,sha256=wotpFFQgMY-ZR3lNVm_fRos-iDg0Wjnj6Tk67_7f1SQ,5847
|
|
88
88
|
mesa/visualization/components/matplotlib_components.py,sha256=xQETaFyHIfmL_9JwrLIgubuIQ7-pp7TMoXT1WMmozus,5441
|
|
89
|
-
mesa-3.1.0.dist-info/METADATA,sha256=
|
|
90
|
-
mesa-3.1.0.dist-info/WHEEL,sha256=
|
|
91
|
-
mesa-3.1.0.dist-info/entry_points.txt,sha256=IOcQtetGF8l4wHpOs_hGb19Rz-FS__BMXOJR10IBPsA,39
|
|
92
|
-
mesa-3.1.0.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
93
|
-
mesa-3.1.0.dist-info/licenses/NOTICE,sha256=GbsWoK0QWv1JyZ_xer2s-jNilv0RtWl-0UrtlJANHPg,578
|
|
94
|
-
mesa-3.1.0.dist-info/RECORD,,
|
|
89
|
+
mesa-3.1.0.dev0.dist-info/METADATA,sha256=iqUH3bKVcZW0XOY9KzYXKUBg3q3J2TO-EalHU9JIe-Q,9911
|
|
90
|
+
mesa-3.1.0.dev0.dist-info/WHEEL,sha256=3U_NnUcV_1B1kPkYaPzN-irRckL5VW_lytn0ytO_kRY,87
|
|
91
|
+
mesa-3.1.0.dev0.dist-info/entry_points.txt,sha256=IOcQtetGF8l4wHpOs_hGb19Rz-FS__BMXOJR10IBPsA,39
|
|
92
|
+
mesa-3.1.0.dev0.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
93
|
+
mesa-3.1.0.dev0.dist-info/licenses/NOTICE,sha256=GbsWoK0QWv1JyZ_xer2s-jNilv0RtWl-0UrtlJANHPg,578
|
|
94
|
+
mesa-3.1.0.dev0.dist-info/RECORD,,
|
|
@@ -1,444 +0,0 @@
|
|
|
1
|
-
"""Efficient storage and manipulation of cell properties across spaces.
|
|
2
|
-
|
|
3
|
-
PropertyLayers provide a way to associate properties with cells in a space efficiently.
|
|
4
|
-
The module includes:
|
|
5
|
-
- PropertyLayer class for managing grid-wide properties
|
|
6
|
-
- Property access descriptors for cells
|
|
7
|
-
- Batch operations for property modification
|
|
8
|
-
- Property-based cell selection
|
|
9
|
-
- Integration with numpy for efficient operations
|
|
10
|
-
|
|
11
|
-
This system separates property storage from cells themselves, enabling
|
|
12
|
-
fast bulk operations and sophisticated property-based behaviors while
|
|
13
|
-
maintaining an intuitive interface through cell attributes. Properties
|
|
14
|
-
can represent environmental factors, cell states, or any other grid-wide
|
|
15
|
-
attributes.
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
import warnings
|
|
19
|
-
from collections.abc import Callable, Sequence
|
|
20
|
-
from typing import Any, TypeVar
|
|
21
|
-
|
|
22
|
-
import numpy as np
|
|
23
|
-
|
|
24
|
-
from mesa.experimental.cell_space import Cell
|
|
25
|
-
|
|
26
|
-
Coordinate = Sequence[int]
|
|
27
|
-
T = TypeVar("T", bound=Cell)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class PropertyLayer:
|
|
31
|
-
"""A class representing a layer of properties in a two-dimensional grid.
|
|
32
|
-
|
|
33
|
-
Each cell in the grid can store a value of a specified data type.
|
|
34
|
-
|
|
35
|
-
Attributes:
|
|
36
|
-
name: The name of the property layer.
|
|
37
|
-
dimensions: The width of the grid (number of columns).
|
|
38
|
-
data: A NumPy array representing the grid data.
|
|
39
|
-
|
|
40
|
-
"""
|
|
41
|
-
|
|
42
|
-
# Fixme
|
|
43
|
-
# can't we simplify this a lot
|
|
44
|
-
# in essence, this is just a numpy array with a name and fixed dimensions
|
|
45
|
-
# all other functionality seems redundant to me?
|
|
46
|
-
|
|
47
|
-
@property
|
|
48
|
-
def data(self): # noqa: D102
|
|
49
|
-
return self._mesa_data
|
|
50
|
-
|
|
51
|
-
@data.setter
|
|
52
|
-
def data(self, value):
|
|
53
|
-
self.set_cells(value)
|
|
54
|
-
|
|
55
|
-
propertylayer_experimental_warning_given = False
|
|
56
|
-
|
|
57
|
-
def __init__(
|
|
58
|
-
self, name: str, dimensions: Sequence[int], default_value=0.0, dtype=float
|
|
59
|
-
):
|
|
60
|
-
"""Initializes a new PropertyLayer instance.
|
|
61
|
-
|
|
62
|
-
Args:
|
|
63
|
-
name: The name of the property layer.
|
|
64
|
-
dimensions: the dimensions of the property layer.
|
|
65
|
-
default_value: The default value to initialize each cell in the grid. Should ideally
|
|
66
|
-
be of the same type as specified by the dtype parameter.
|
|
67
|
-
dtype (data-type, optional): The desired data-type for the grid's elements. Default is float.
|
|
68
|
-
|
|
69
|
-
Notes:
|
|
70
|
-
A UserWarning is raised if the default_value is not of a type compatible with dtype.
|
|
71
|
-
The dtype parameter can accept both Python data types (like bool, int or float) and NumPy data types
|
|
72
|
-
(like np.int64 or np.float64).
|
|
73
|
-
"""
|
|
74
|
-
self.name = name
|
|
75
|
-
self.dimensions = dimensions
|
|
76
|
-
|
|
77
|
-
# Check if the dtype is suitable for the data
|
|
78
|
-
if not isinstance(default_value, dtype):
|
|
79
|
-
warnings.warn(
|
|
80
|
-
f"Default value {default_value} ({type(default_value).__name__}) might not be best suitable with dtype={dtype.__name__}.",
|
|
81
|
-
UserWarning,
|
|
82
|
-
stacklevel=2,
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
# fixme why not initialize with empty?
|
|
86
|
-
self._mesa_data = np.full(self.dimensions, default_value, dtype=dtype)
|
|
87
|
-
|
|
88
|
-
if not self.__class__.propertylayer_experimental_warning_given:
|
|
89
|
-
warnings.warn(
|
|
90
|
-
"The property layer functionality and associated classes are experimental. It may be changed or removed in any and all future releases, including patch releases.\n"
|
|
91
|
-
"We would love to hear what you think about this new feature. If you have any thoughts, share them with us here: https://github.com/projectmesa/mesa/discussions/1932",
|
|
92
|
-
FutureWarning,
|
|
93
|
-
stacklevel=2,
|
|
94
|
-
)
|
|
95
|
-
self.__class__.propertylayer_experimental_warning_given = True
|
|
96
|
-
|
|
97
|
-
@classmethod
|
|
98
|
-
def from_data(cls, name: str, data: np.ndarray):
|
|
99
|
-
"""Create a property layer from a NumPy array.
|
|
100
|
-
|
|
101
|
-
Args:
|
|
102
|
-
name: The name of the property layer.
|
|
103
|
-
data: A NumPy array representing the grid data.
|
|
104
|
-
|
|
105
|
-
"""
|
|
106
|
-
layer = cls(
|
|
107
|
-
name,
|
|
108
|
-
data.shape,
|
|
109
|
-
default_value=data[*[0 for _ in range(len(data.shape))]],
|
|
110
|
-
dtype=data.dtype.type,
|
|
111
|
-
)
|
|
112
|
-
layer.set_cells(data)
|
|
113
|
-
return layer
|
|
114
|
-
|
|
115
|
-
def set_cells(self, value, condition: Callable | None = None):
|
|
116
|
-
"""Perform a batch update either on the entire grid or conditionally, in-place.
|
|
117
|
-
|
|
118
|
-
Args:
|
|
119
|
-
value: The value to be used for the update.
|
|
120
|
-
condition: (Optional) A callable that returns a boolean array when applied to the data.
|
|
121
|
-
"""
|
|
122
|
-
if condition is None:
|
|
123
|
-
np.copyto(self._mesa_data, value) # In-place update
|
|
124
|
-
else:
|
|
125
|
-
vectorized_condition = np.vectorize(condition)
|
|
126
|
-
condition_result = vectorized_condition(self._mesa_data)
|
|
127
|
-
np.copyto(self._mesa_data, value, where=condition_result)
|
|
128
|
-
|
|
129
|
-
def modify_cells(
|
|
130
|
-
self,
|
|
131
|
-
operation: Callable,
|
|
132
|
-
value=None,
|
|
133
|
-
condition: Callable | None = None,
|
|
134
|
-
):
|
|
135
|
-
"""Modify cells using an operation, which can be a lambda function or a NumPy ufunc.
|
|
136
|
-
|
|
137
|
-
If a NumPy ufunc is used, an additional value should be provided.
|
|
138
|
-
|
|
139
|
-
Args:
|
|
140
|
-
operation: A function to apply. Can be a lambda function or a NumPy ufunc.
|
|
141
|
-
value: The value to be used if the operation is a NumPy ufunc. Ignored for lambda functions.
|
|
142
|
-
condition: (Optional) A callable that returns a boolean array when applied to the data.
|
|
143
|
-
"""
|
|
144
|
-
condition_array = np.ones_like(
|
|
145
|
-
self._mesa_data, dtype=bool
|
|
146
|
-
) # Default condition (all cells)
|
|
147
|
-
if condition is not None:
|
|
148
|
-
vectorized_condition = np.vectorize(condition)
|
|
149
|
-
condition_array = vectorized_condition(self._mesa_data)
|
|
150
|
-
|
|
151
|
-
# Check if the operation is a lambda function or a NumPy ufunc
|
|
152
|
-
if isinstance(operation, np.ufunc):
|
|
153
|
-
if ufunc_requires_additional_input(operation):
|
|
154
|
-
if value is None:
|
|
155
|
-
raise ValueError("This ufunc requires an additional input value.")
|
|
156
|
-
modified_data = operation(self._mesa_data, value)
|
|
157
|
-
else:
|
|
158
|
-
modified_data = operation(self._mesa_data)
|
|
159
|
-
else:
|
|
160
|
-
# Vectorize non-ufunc operations
|
|
161
|
-
vectorized_operation = np.vectorize(operation)
|
|
162
|
-
modified_data = vectorized_operation(self._mesa_data)
|
|
163
|
-
|
|
164
|
-
self._mesa_data = np.where(condition_array, modified_data, self._mesa_data)
|
|
165
|
-
|
|
166
|
-
def select_cells(self, condition: Callable, return_list=True):
|
|
167
|
-
"""Find cells that meet a specified condition using NumPy's boolean indexing, in-place.
|
|
168
|
-
|
|
169
|
-
Args:
|
|
170
|
-
condition: A callable that returns a boolean array when applied to the data.
|
|
171
|
-
return_list: (Optional) If True, return a list of (x, y) tuples. Otherwise, return a boolean array.
|
|
172
|
-
|
|
173
|
-
Returns:
|
|
174
|
-
A list of (x, y) tuples or a boolean array.
|
|
175
|
-
"""
|
|
176
|
-
# fixme: consider splitting into two separate functions
|
|
177
|
-
# select_cells_boolean
|
|
178
|
-
# select_cells_index
|
|
179
|
-
|
|
180
|
-
condition_array = condition(self._mesa_data)
|
|
181
|
-
if return_list:
|
|
182
|
-
return list(zip(*np.where(condition_array)))
|
|
183
|
-
else:
|
|
184
|
-
return condition_array
|
|
185
|
-
|
|
186
|
-
def aggregate(self, operation: Callable):
|
|
187
|
-
"""Perform an aggregate operation (e.g., sum, mean) on a property across all cells.
|
|
188
|
-
|
|
189
|
-
Args:
|
|
190
|
-
operation: A function to apply. Can be a lambda function or a NumPy ufunc.
|
|
191
|
-
"""
|
|
192
|
-
return operation(self._mesa_data)
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
class HasPropertyLayers:
|
|
196
|
-
"""Mixin-like class to add property layer functionality to Grids.
|
|
197
|
-
|
|
198
|
-
Property layers can be added to a grid using create_property_layer or add_property_layer. Once created, property
|
|
199
|
-
layers can be accessed as attributes if the name used for the layer is a valid python identifier.
|
|
200
|
-
|
|
201
|
-
"""
|
|
202
|
-
|
|
203
|
-
# fixme is there a way to indicate that a mixin only works with specific classes?
|
|
204
|
-
def __init__(self, *args, **kwargs):
|
|
205
|
-
"""Initialize a HasPropertyLayers instance."""
|
|
206
|
-
super().__init__(*args, **kwargs)
|
|
207
|
-
self._mesa_property_layers = {}
|
|
208
|
-
|
|
209
|
-
def create_property_layer(
|
|
210
|
-
self,
|
|
211
|
-
name: str,
|
|
212
|
-
default_value=0.0,
|
|
213
|
-
dtype=float,
|
|
214
|
-
):
|
|
215
|
-
"""Add a property layer to the grid.
|
|
216
|
-
|
|
217
|
-
Args:
|
|
218
|
-
name: The name of the property layer.
|
|
219
|
-
default_value: The default value of the property layer.
|
|
220
|
-
dtype: The data type of the property layer.
|
|
221
|
-
|
|
222
|
-
Returns:
|
|
223
|
-
Property layer instance.
|
|
224
|
-
|
|
225
|
-
"""
|
|
226
|
-
layer = PropertyLayer(
|
|
227
|
-
name, self.dimensions, default_value=default_value, dtype=dtype
|
|
228
|
-
)
|
|
229
|
-
self.add_property_layer(layer)
|
|
230
|
-
return layer
|
|
231
|
-
|
|
232
|
-
def add_property_layer(self, layer: PropertyLayer):
|
|
233
|
-
"""Add a predefined property layer to the grid.
|
|
234
|
-
|
|
235
|
-
Args:
|
|
236
|
-
layer: The property layer to add.
|
|
237
|
-
|
|
238
|
-
Raises:
|
|
239
|
-
ValueError: If the dimensions of the layer and the grid are not the same.
|
|
240
|
-
|
|
241
|
-
"""
|
|
242
|
-
if layer.dimensions != self.dimensions:
|
|
243
|
-
raise ValueError(
|
|
244
|
-
"Dimensions of property layer do not match the dimensions of the grid"
|
|
245
|
-
)
|
|
246
|
-
if layer.name in self._mesa_property_layers:
|
|
247
|
-
raise ValueError(f"Property layer {layer.name} already exists.")
|
|
248
|
-
if (
|
|
249
|
-
layer.name in self.cell_klass.__slots__
|
|
250
|
-
or layer.name in self.cell_klass.__dict__
|
|
251
|
-
):
|
|
252
|
-
raise ValueError(
|
|
253
|
-
f"Property layer {layer.name} clashes with existing attribute in {self.cell_klass.__name__}"
|
|
254
|
-
)
|
|
255
|
-
|
|
256
|
-
self._mesa_property_layers[layer.name] = layer
|
|
257
|
-
setattr(self.cell_klass, layer.name, PropertyDescriptor(layer))
|
|
258
|
-
self.cell_klass._mesa_properties.add(layer.name)
|
|
259
|
-
|
|
260
|
-
def remove_property_layer(self, property_name: str):
|
|
261
|
-
"""Remove a property layer from the grid.
|
|
262
|
-
|
|
263
|
-
Args:
|
|
264
|
-
property_name: the name of the property layer to remove
|
|
265
|
-
remove_from_cells: whether to remove the property layer from all cells (default: True)
|
|
266
|
-
"""
|
|
267
|
-
del self._mesa_property_layers[property_name]
|
|
268
|
-
delattr(self.cell_klass, property_name)
|
|
269
|
-
self.cell_klass._mesa_properties.remove(property_name)
|
|
270
|
-
|
|
271
|
-
def set_property(
|
|
272
|
-
self, property_name: str, value, condition: Callable[[T], bool] | None = None
|
|
273
|
-
):
|
|
274
|
-
"""Set the value of a property for all cells in the grid.
|
|
275
|
-
|
|
276
|
-
Args:
|
|
277
|
-
property_name: the name of the property to set
|
|
278
|
-
value: the value to set
|
|
279
|
-
condition: a function that takes a cell and returns a boolean
|
|
280
|
-
"""
|
|
281
|
-
self._mesa_property_layers[property_name].set_cells(value, condition)
|
|
282
|
-
|
|
283
|
-
def modify_properties(
|
|
284
|
-
self,
|
|
285
|
-
property_name: str,
|
|
286
|
-
operation: Callable,
|
|
287
|
-
value: Any = None,
|
|
288
|
-
condition: Callable[[T], bool] | None = None,
|
|
289
|
-
):
|
|
290
|
-
"""Modify the values of a specific property for all cells in the grid.
|
|
291
|
-
|
|
292
|
-
Args:
|
|
293
|
-
property_name: the name of the property to modify
|
|
294
|
-
operation: the operation to perform
|
|
295
|
-
value: the value to use in the operation
|
|
296
|
-
condition: a function that takes a cell and returns a boolean (used to filter cells)
|
|
297
|
-
"""
|
|
298
|
-
self._mesa_property_layers[property_name].modify_cells(
|
|
299
|
-
operation, value, condition
|
|
300
|
-
)
|
|
301
|
-
|
|
302
|
-
def get_neighborhood_mask(
|
|
303
|
-
self, coordinate: Coordinate, include_center: bool = True, radius: int = 1
|
|
304
|
-
) -> np.ndarray:
|
|
305
|
-
"""Generate a boolean mask representing the neighborhood.
|
|
306
|
-
|
|
307
|
-
Args:
|
|
308
|
-
coordinate: Center of the neighborhood.
|
|
309
|
-
include_center: Include the central cell in the neighborhood.
|
|
310
|
-
radius: The radius of the neighborhood.
|
|
311
|
-
|
|
312
|
-
Returns:
|
|
313
|
-
np.ndarray: A boolean mask representing the neighborhood.
|
|
314
|
-
"""
|
|
315
|
-
cell = self._cells[coordinate]
|
|
316
|
-
neighborhood = cell.get_neighborhood(
|
|
317
|
-
include_center=include_center, radius=radius
|
|
318
|
-
)
|
|
319
|
-
mask = np.zeros(self.dimensions, dtype=bool)
|
|
320
|
-
|
|
321
|
-
# Convert the neighborhood list to a NumPy array and use advanced indexing
|
|
322
|
-
coords = np.array([c.coordinate for c in neighborhood])
|
|
323
|
-
indices = [coords[:, i] for i in range(coords.shape[1])]
|
|
324
|
-
mask[*indices] = True
|
|
325
|
-
return mask
|
|
326
|
-
|
|
327
|
-
def select_cells(
|
|
328
|
-
self,
|
|
329
|
-
conditions: dict | None = None,
|
|
330
|
-
extreme_values: dict | None = None,
|
|
331
|
-
masks: np.ndarray | list[np.ndarray] = None,
|
|
332
|
-
only_empty: bool = False,
|
|
333
|
-
return_list: bool = True,
|
|
334
|
-
) -> list[Coordinate] | np.ndarray:
|
|
335
|
-
"""Select cells based on property conditions, extreme values, and/or masks, with an option to only select empty cells.
|
|
336
|
-
|
|
337
|
-
Args:
|
|
338
|
-
conditions (dict): A dictionary where keys are property names and values are callables that return a boolean when applied.
|
|
339
|
-
extreme_values (dict): A dictionary where keys are property names and values are either 'highest' or 'lowest'.
|
|
340
|
-
masks (np.ndarray | list[np.ndarray], optional): A mask or list of masks to restrict the selection.
|
|
341
|
-
only_empty (bool, optional): If True, only select cells that are empty. Default is False.
|
|
342
|
-
return_list (bool, optional): If True, return a list of coordinates, otherwise return a mask.
|
|
343
|
-
|
|
344
|
-
Returns:
|
|
345
|
-
Union[list[Coordinate], np.ndarray]: Coordinates where conditions are satisfied or the combined mask.
|
|
346
|
-
"""
|
|
347
|
-
# fixme: consider splitting into two separate functions
|
|
348
|
-
# select_cells_boolean
|
|
349
|
-
# select_cells_index
|
|
350
|
-
# also we might want to change the naming to avoid classes with PropertyLayer
|
|
351
|
-
|
|
352
|
-
# Initialize the combined mask
|
|
353
|
-
combined_mask = np.ones(self.dimensions, dtype=bool)
|
|
354
|
-
|
|
355
|
-
# Apply the masks
|
|
356
|
-
if masks is not None:
|
|
357
|
-
if isinstance(masks, list):
|
|
358
|
-
for mask in masks:
|
|
359
|
-
combined_mask = np.logical_and(combined_mask, mask)
|
|
360
|
-
else:
|
|
361
|
-
combined_mask = np.logical_and(combined_mask, masks)
|
|
362
|
-
|
|
363
|
-
# Apply the empty mask if only_empty is True
|
|
364
|
-
if only_empty:
|
|
365
|
-
combined_mask = np.logical_and(
|
|
366
|
-
combined_mask, self._mesa_property_layers["empty"]
|
|
367
|
-
)
|
|
368
|
-
|
|
369
|
-
# Apply conditions
|
|
370
|
-
if conditions:
|
|
371
|
-
for prop_name, condition in conditions.items():
|
|
372
|
-
prop_layer = self._mesa_property_layers[prop_name].data
|
|
373
|
-
prop_mask = condition(prop_layer)
|
|
374
|
-
combined_mask = np.logical_and(combined_mask, prop_mask)
|
|
375
|
-
|
|
376
|
-
# Apply extreme values
|
|
377
|
-
if extreme_values:
|
|
378
|
-
for property_name, mode in extreme_values.items():
|
|
379
|
-
prop_values = self._mesa_property_layers[property_name].data
|
|
380
|
-
|
|
381
|
-
# Create a masked array using the combined_mask
|
|
382
|
-
masked_values = np.ma.masked_array(prop_values, mask=~combined_mask)
|
|
383
|
-
|
|
384
|
-
if mode == "highest":
|
|
385
|
-
target_value = masked_values.max()
|
|
386
|
-
elif mode == "lowest":
|
|
387
|
-
target_value = masked_values.min()
|
|
388
|
-
else:
|
|
389
|
-
raise ValueError(
|
|
390
|
-
f"Invalid mode {mode}. Choose from 'highest' or 'lowest'."
|
|
391
|
-
)
|
|
392
|
-
|
|
393
|
-
extreme_value_mask = prop_values == target_value
|
|
394
|
-
combined_mask = np.logical_and(combined_mask, extreme_value_mask)
|
|
395
|
-
|
|
396
|
-
# Generate output
|
|
397
|
-
if return_list:
|
|
398
|
-
selected_cells = list(zip(*np.where(combined_mask)))
|
|
399
|
-
return selected_cells
|
|
400
|
-
else:
|
|
401
|
-
return combined_mask
|
|
402
|
-
|
|
403
|
-
def __getattr__(self, name: str) -> Any: # noqa: D105
|
|
404
|
-
try:
|
|
405
|
-
return self._mesa_property_layers[name]
|
|
406
|
-
except KeyError as e:
|
|
407
|
-
raise AttributeError(
|
|
408
|
-
f"'{type(self).__name__}' object has no property layer called '{name}'"
|
|
409
|
-
) from e
|
|
410
|
-
|
|
411
|
-
def __setattr__(self, key, value): # noqa: D105
|
|
412
|
-
# fixme
|
|
413
|
-
# this might be done more elegantly, the main problem is that _mesa_property_layers must already be defined to avoid infinite recursion errors from happening
|
|
414
|
-
# also, this protection only works if the attribute is added after the layer, not the other way around
|
|
415
|
-
try:
|
|
416
|
-
layers = self.__dict__["_mesa_property_layers"]
|
|
417
|
-
except KeyError:
|
|
418
|
-
super().__setattr__(key, value)
|
|
419
|
-
else:
|
|
420
|
-
if key in layers:
|
|
421
|
-
raise AttributeError(
|
|
422
|
-
f"'{type(self).__name__}' object already has a property layer with name '{key}'"
|
|
423
|
-
)
|
|
424
|
-
else:
|
|
425
|
-
super().__setattr__(key, value)
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
class PropertyDescriptor:
|
|
429
|
-
"""Descriptor for giving cells attribute like access to values defined in property layers."""
|
|
430
|
-
|
|
431
|
-
def __init__(self, property_layer: PropertyLayer): # noqa: D107
|
|
432
|
-
self.layer: PropertyLayer = property_layer
|
|
433
|
-
|
|
434
|
-
def __get__(self, instance: Cell, owner): # noqa: D105
|
|
435
|
-
return self.layer.data[instance.coordinate]
|
|
436
|
-
|
|
437
|
-
def __set__(self, instance: Cell, value): # noqa: D105
|
|
438
|
-
self.layer.data[instance.coordinate] = value
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
def ufunc_requires_additional_input(ufunc): # noqa: D103
|
|
442
|
-
# NumPy ufuncs have a 'nargs' attribute indicating the number of input arguments
|
|
443
|
-
# For binary ufuncs (like np.add), nargs is 2
|
|
444
|
-
return ufunc.nargs > 1
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
"""Mesa Signals (Observables) package that provides reactive programming capabilities.
|
|
2
|
-
|
|
3
|
-
This package enables tracking changes to properties and state in Mesa models through a
|
|
4
|
-
reactive programming paradigm. It enables building models where components can observe
|
|
5
|
-
and react to changes in other components' state.
|
|
6
|
-
|
|
7
|
-
The package provides the core Observable classes and utilities needed to implement
|
|
8
|
-
reactive patterns in agent-based models. This includes capabilities for watching changes
|
|
9
|
-
to attributes, computing derived values, and managing collections that emit signals
|
|
10
|
-
when modified.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
from .mesa_signal import All, Computable, Computed, HasObservables, Observable
|
|
14
|
-
from .observable_collections import ObservableList
|
|
15
|
-
|
|
16
|
-
__all__ = [
|
|
17
|
-
"All",
|
|
18
|
-
"Computable",
|
|
19
|
-
"Computed",
|
|
20
|
-
"HasObservables",
|
|
21
|
-
"Observable",
|
|
22
|
-
"ObservableList",
|
|
23
|
-
]
|