Mesa 3.0.0a5__py3-none-any.whl → 3.0.0b1__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.
- examples/README.md +37 -0
- examples/__init__.py +0 -0
- examples/advanced/__init__.py +0 -0
- examples/advanced/epstein_civil_violence/Epstein Civil Violence.ipynb +116 -0
- examples/advanced/epstein_civil_violence/Readme.md +33 -0
- examples/advanced/epstein_civil_violence/epstein_civil_violence/__init__.py +0 -0
- examples/advanced/epstein_civil_violence/epstein_civil_violence/agent.py +158 -0
- examples/advanced/epstein_civil_violence/epstein_civil_violence/model.py +146 -0
- examples/advanced/epstein_civil_violence/epstein_civil_violence/portrayal.py +33 -0
- examples/advanced/epstein_civil_violence/epstein_civil_violence/server.py +81 -0
- examples/advanced/epstein_civil_violence/requirements.txt +3 -0
- examples/advanced/epstein_civil_violence/run.py +3 -0
- examples/advanced/pd_grid/analysis.ipynb +228 -0
- examples/advanced/pd_grid/pd_grid/__init__.py +0 -0
- examples/advanced/pd_grid/pd_grid/agent.py +50 -0
- examples/advanced/pd_grid/pd_grid/model.py +72 -0
- examples/advanced/pd_grid/pd_grid/portrayal.py +19 -0
- examples/advanced/pd_grid/pd_grid/server.py +21 -0
- examples/advanced/pd_grid/readme.md +42 -0
- examples/advanced/pd_grid/requirements.txt +3 -0
- examples/advanced/pd_grid/run.py +3 -0
- examples/advanced/sugarscape_g1mt/Readme.md +87 -0
- examples/advanced/sugarscape_g1mt/app.py +61 -0
- examples/advanced/sugarscape_g1mt/requirements.txt +6 -0
- examples/advanced/sugarscape_g1mt/run.py +105 -0
- examples/advanced/sugarscape_g1mt/sugarscape_g1mt/__init__.py +0 -0
- examples/advanced/sugarscape_g1mt/sugarscape_g1mt/model.py +180 -0
- examples/advanced/sugarscape_g1mt/sugarscape_g1mt/resource_agents.py +26 -0
- examples/advanced/sugarscape_g1mt/sugarscape_g1mt/server.py +61 -0
- examples/advanced/sugarscape_g1mt/sugarscape_g1mt/sugar-map.txt +50 -0
- examples/advanced/sugarscape_g1mt/sugarscape_g1mt/trader_agents.py +321 -0
- examples/advanced/sugarscape_g1mt/tests.py +72 -0
- examples/advanced/wolf_sheep/Readme.md +57 -0
- examples/advanced/wolf_sheep/__init__.py +0 -0
- examples/advanced/wolf_sheep/requirements.txt +1 -0
- examples/advanced/wolf_sheep/run.py +3 -0
- examples/advanced/wolf_sheep/wolf_sheep/__init__.py +0 -0
- examples/advanced/wolf_sheep/wolf_sheep/agents.py +102 -0
- examples/advanced/wolf_sheep/wolf_sheep/model.py +136 -0
- examples/advanced/wolf_sheep/wolf_sheep/resources/sheep.png +0 -0
- examples/advanced/wolf_sheep/wolf_sheep/resources/wolf.png +0 -0
- examples/advanced/wolf_sheep/wolf_sheep/server.py +78 -0
- examples/basic/__init__.py +13 -0
- examples/basic/boid_flockers/Readme.md +43 -0
- examples/basic/boid_flockers/agents.py +71 -0
- examples/basic/boid_flockers/app.py +59 -0
- examples/basic/boid_flockers/model.py +70 -0
- examples/basic/boltzmann_wealth_model/Readme.md +60 -0
- examples/basic/boltzmann_wealth_model/agents.py +31 -0
- examples/basic/boltzmann_wealth_model/app.py +66 -0
- examples/basic/boltzmann_wealth_model/model.py +44 -0
- examples/basic/boltzmann_wealth_model/st_app.py +115 -0
- examples/basic/conways_game_of_life/Readme.md +35 -0
- examples/basic/conways_game_of_life/agents.py +47 -0
- examples/basic/conways_game_of_life/model.py +32 -0
- examples/basic/conways_game_of_life/portrayal.py +18 -0
- examples/basic/conways_game_of_life/requirements.txt +1 -0
- examples/basic/conways_game_of_life/server.py +11 -0
- examples/basic/conways_game_of_life/st_app.py +71 -0
- examples/basic/schelling/README.md +47 -0
- examples/basic/schelling/agents.py +26 -0
- examples/basic/schelling/analysis.ipynb +205 -0
- examples/basic/schelling/app.py +43 -0
- examples/basic/schelling/model.py +60 -0
- examples/basic/virus_on_network/README.md +61 -0
- examples/basic/virus_on_network/agents.py +69 -0
- examples/basic/virus_on_network/app.py +133 -0
- examples/basic/virus_on_network/model.py +99 -0
- mesa/__init__.py +4 -1
- mesa/agent.py +24 -43
- mesa/batchrunner.py +7 -0
- mesa/examples.py +3 -0
- mesa/experimental/__init__.py +8 -2
- mesa/experimental/cell_space/__init__.py +7 -1
- mesa/experimental/cell_space/cell.py +35 -6
- mesa/experimental/cell_space/cell_agent.py +114 -23
- mesa/experimental/cell_space/discrete_space.py +70 -3
- mesa/experimental/cell_space/grid.py +13 -0
- mesa/experimental/cell_space/network.py +3 -0
- mesa/experimental/devs/examples/wolf_sheep.py +2 -1
- mesa/model.py +71 -21
- mesa/time.py +7 -5
- mesa/visualization/components/matplotlib.py +184 -90
- mesa/visualization/solara_viz.py +25 -61
- {mesa-3.0.0a5.dist-info → mesa-3.0.0b1.dist-info}/METADATA +55 -13
- mesa-3.0.0b1.dist-info/RECORD +114 -0
- mesa-3.0.0b1.dist-info/licenses/LICENSE +202 -0
- mesa-3.0.0a5.dist-info/licenses/LICENSE → mesa-3.0.0b1.dist-info/licenses/NOTICE +2 -2
- mesa-3.0.0a5.dist-info/RECORD +0 -44
- {mesa-3.0.0a5.dist-info → mesa-3.0.0b1.dist-info}/WHEEL +0 -0
- {mesa-3.0.0a5.dist-info → mesa-3.0.0b1.dist-info}/entry_points.txt +0 -0
|
@@ -2,41 +2,132 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from typing import TYPE_CHECKING
|
|
5
|
+
from typing import TYPE_CHECKING, Protocol
|
|
6
6
|
|
|
7
|
-
from mesa import Agent
|
|
7
|
+
from mesa.agent import Agent
|
|
8
8
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
10
|
-
from mesa.experimental.cell_space
|
|
10
|
+
from mesa.experimental.cell_space import Cell
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
class
|
|
14
|
-
"""
|
|
13
|
+
class HasCellProtocol(Protocol):
|
|
14
|
+
"""Protocol for discrete space cell holders."""
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
unique_id (int): A unique identifier for this agent.
|
|
18
|
-
model (Model): The model instance to which the agent belongs
|
|
19
|
-
pos: (Position | None): The position of the agent in the space
|
|
20
|
-
cell: (Cell | None): the cell which the agent occupies
|
|
21
|
-
"""
|
|
16
|
+
cell: Cell
|
|
22
17
|
|
|
23
|
-
def __init__(self, model: Model) -> None:
|
|
24
|
-
"""Create a new agent.
|
|
25
18
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"""
|
|
29
|
-
super().__init__(model)
|
|
30
|
-
self.cell: Cell | None = None
|
|
19
|
+
class HasCell:
|
|
20
|
+
"""Descriptor for cell movement behavior."""
|
|
31
21
|
|
|
32
|
-
|
|
33
|
-
"""Move agent to cell.
|
|
22
|
+
_mesa_cell: Cell | None = None
|
|
34
23
|
|
|
35
|
-
|
|
36
|
-
|
|
24
|
+
@property
|
|
25
|
+
def cell(self) -> Cell | None: # noqa: D102
|
|
26
|
+
return self._mesa_cell
|
|
37
27
|
|
|
38
|
-
|
|
28
|
+
@cell.setter
|
|
29
|
+
def cell(self, cell: Cell | None) -> None:
|
|
30
|
+
# remove from current cell
|
|
39
31
|
if self.cell is not None:
|
|
40
32
|
self.cell.remove_agent(self)
|
|
33
|
+
|
|
34
|
+
# update private attribute
|
|
35
|
+
self._mesa_cell = cell
|
|
36
|
+
|
|
37
|
+
# add to new cell
|
|
38
|
+
if cell is not None:
|
|
39
|
+
cell.add_agent(self)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class BasicMovement:
|
|
43
|
+
"""Mixin for moving agents in discrete space."""
|
|
44
|
+
|
|
45
|
+
def move_to(self: HasCellProtocol, cell: Cell) -> None:
|
|
46
|
+
"""Move to a new cell."""
|
|
41
47
|
self.cell = cell
|
|
48
|
+
|
|
49
|
+
def move_relative(self: HasCellProtocol, direction: tuple[int, ...]):
|
|
50
|
+
"""Move to a cell relative to the current cell.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
direction: The direction to move in.
|
|
54
|
+
"""
|
|
55
|
+
new_cell = self.cell.connections.get(direction)
|
|
56
|
+
if new_cell is not None:
|
|
57
|
+
self.cell = new_cell
|
|
58
|
+
else:
|
|
59
|
+
raise ValueError(f"No cell in direction {direction}")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class FixedCell(HasCell):
|
|
63
|
+
"""Mixin for agents that are fixed to a cell."""
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def cell(self) -> Cell | None: # noqa: D102
|
|
67
|
+
return self._mesa_cell
|
|
68
|
+
|
|
69
|
+
@cell.setter
|
|
70
|
+
def cell(self, cell: Cell) -> None:
|
|
71
|
+
if self.cell is not None:
|
|
72
|
+
raise ValueError("Cannot move agent in FixedCell")
|
|
73
|
+
self._mesa_cell = cell
|
|
74
|
+
|
|
42
75
|
cell.add_agent(self)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class CellAgent(Agent, HasCell, BasicMovement):
|
|
79
|
+
"""Cell Agent is an extension of the Agent class and adds behavior for moving in discrete spaces.
|
|
80
|
+
|
|
81
|
+
Attributes:
|
|
82
|
+
cell (Cell): The cell the agent is currently in.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
def remove(self):
|
|
86
|
+
"""Remove the agent from the model."""
|
|
87
|
+
super().remove()
|
|
88
|
+
self.cell = None # ensures that we are also removed from cell
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class FixedAgent(Agent, FixedCell):
|
|
92
|
+
"""A patch in a 2D grid."""
|
|
93
|
+
|
|
94
|
+
def remove(self):
|
|
95
|
+
"""Remove the agent from the model."""
|
|
96
|
+
super().remove()
|
|
97
|
+
|
|
98
|
+
# fixme we leave self._mesa_cell on the original value
|
|
99
|
+
# so you cannot hijack remove() to move patches
|
|
100
|
+
self.cell.remove_agent(self)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class Grid2DMovingAgent(CellAgent):
|
|
104
|
+
"""Mixin for moving agents in 2D grids."""
|
|
105
|
+
|
|
106
|
+
# fmt: off
|
|
107
|
+
DIRECTION_MAP = {
|
|
108
|
+
"n": (-1, 0), "north": (-1, 0), "up": (-1, 0),
|
|
109
|
+
"s": (1, 0), "south": (1, 0), "down": (1, 0),
|
|
110
|
+
"e": (0, 1), "east": (0, 1), "right": (0, 1),
|
|
111
|
+
"w": (0, -1), "west": (0, -1), "left": (0, -1),
|
|
112
|
+
"ne": (-1, 1), "northeast": (-1, 1), "upright": (-1, 1),
|
|
113
|
+
"nw": (-1, -1), "northwest": (-1, -1), "upleft": (-1, -1),
|
|
114
|
+
"se": (1, 1), "southeast": (1, 1), "downright": (1, 1),
|
|
115
|
+
"sw": (1, -1), "southwest": (1, -1), "downleft": (1, -1)
|
|
116
|
+
}
|
|
117
|
+
# fmt: on
|
|
118
|
+
|
|
119
|
+
def move(self, direction: str, distance: int = 1):
|
|
120
|
+
"""Move the agent in a cardinal direction.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
direction: The cardinal direction to move in.
|
|
124
|
+
distance: The distance to move.
|
|
125
|
+
"""
|
|
126
|
+
direction = direction.lower() # Convert direction to lowercase
|
|
127
|
+
|
|
128
|
+
if direction not in self.DIRECTION_MAP:
|
|
129
|
+
raise ValueError(f"Invalid direction: {direction}")
|
|
130
|
+
|
|
131
|
+
move_vector = self.DIRECTION_MAP[direction]
|
|
132
|
+
for _ in range(distance):
|
|
133
|
+
self.move_relative(move_vector)
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from collections.abc import Callable
|
|
5
6
|
from functools import cached_property
|
|
6
7
|
from random import Random
|
|
7
|
-
from typing import Generic, TypeVar
|
|
8
|
+
from typing import Any, Generic, TypeVar
|
|
8
9
|
|
|
9
10
|
from mesa.experimental.cell_space.cell import Cell
|
|
10
11
|
from mesa.experimental.cell_space.cell_collection import CellCollection
|
|
12
|
+
from mesa.space import PropertyLayer
|
|
11
13
|
|
|
12
14
|
T = TypeVar("T", bound=Cell)
|
|
13
15
|
|
|
@@ -20,8 +22,8 @@ class DiscreteSpace(Generic[T]):
|
|
|
20
22
|
all_cells (CellCollection): The cells composing the discrete space
|
|
21
23
|
random (Random): The random number generator
|
|
22
24
|
cell_klass (Type) : the type of cell class
|
|
23
|
-
empties (CellCollection) :
|
|
24
|
-
|
|
25
|
+
empties (CellCollection) : collection of all cells that are empty
|
|
26
|
+
property_layers (dict[str, PropertyLayer]): the property layers of the discrete space
|
|
25
27
|
"""
|
|
26
28
|
|
|
27
29
|
def __init__(
|
|
@@ -47,11 +49,13 @@ class DiscreteSpace(Generic[T]):
|
|
|
47
49
|
|
|
48
50
|
self._empties: dict[tuple[int, ...], None] = {}
|
|
49
51
|
self._empties_initialized = False
|
|
52
|
+
self.property_layers: dict[str, PropertyLayer] = {}
|
|
50
53
|
|
|
51
54
|
@property
|
|
52
55
|
def cutoff_empties(self): # noqa
|
|
53
56
|
return 7.953 * len(self._cells) ** 0.384
|
|
54
57
|
|
|
58
|
+
def _connect_cells(self): ...
|
|
55
59
|
def _connect_single_cell(self, cell: T): ...
|
|
56
60
|
|
|
57
61
|
@cached_property
|
|
@@ -73,3 +77,66 @@ class DiscreteSpace(Generic[T]):
|
|
|
73
77
|
def select_random_empty_cell(self) -> T:
|
|
74
78
|
"""Select random empty cell."""
|
|
75
79
|
return self.random.choice(list(self.empties))
|
|
80
|
+
|
|
81
|
+
# PropertyLayer methods
|
|
82
|
+
def add_property_layer(
|
|
83
|
+
self, property_layer: PropertyLayer, add_to_cells: bool = True
|
|
84
|
+
):
|
|
85
|
+
"""Add a property layer to the grid.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
property_layer: the property layer to add
|
|
89
|
+
add_to_cells: whether to add the property layer to all cells (default: True)
|
|
90
|
+
"""
|
|
91
|
+
if property_layer.name in self.property_layers:
|
|
92
|
+
raise ValueError(f"Property layer {property_layer.name} already exists.")
|
|
93
|
+
self.property_layers[property_layer.name] = property_layer
|
|
94
|
+
if add_to_cells:
|
|
95
|
+
for cell in self._cells.values():
|
|
96
|
+
cell._mesa_property_layers[property_layer.name] = property_layer
|
|
97
|
+
|
|
98
|
+
def remove_property_layer(self, property_name: str, remove_from_cells: bool = True):
|
|
99
|
+
"""Remove a property layer from the grid.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
property_name: the name of the property layer to remove
|
|
103
|
+
remove_from_cells: whether to remove the property layer from all cells (default: True)
|
|
104
|
+
"""
|
|
105
|
+
del self.property_layers[property_name]
|
|
106
|
+
if remove_from_cells:
|
|
107
|
+
for cell in self._cells.values():
|
|
108
|
+
del cell._mesa_property_layers[property_name]
|
|
109
|
+
|
|
110
|
+
def set_property(
|
|
111
|
+
self, property_name: str, value, condition: Callable[[T], bool] | None = None
|
|
112
|
+
):
|
|
113
|
+
"""Set the value of a property for all cells in the grid.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
property_name: the name of the property to set
|
|
117
|
+
value: the value to set
|
|
118
|
+
condition: a function that takes a cell and returns a boolean
|
|
119
|
+
"""
|
|
120
|
+
self.property_layers[property_name].set_cells(value, condition)
|
|
121
|
+
|
|
122
|
+
def modify_properties(
|
|
123
|
+
self,
|
|
124
|
+
property_name: str,
|
|
125
|
+
operation: Callable,
|
|
126
|
+
value: Any = None,
|
|
127
|
+
condition: Callable[[T], bool] | None = None,
|
|
128
|
+
):
|
|
129
|
+
"""Modify the values of a specific property for all cells in the grid.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
property_name: the name of the property to modify
|
|
133
|
+
operation: the operation to perform
|
|
134
|
+
value: the value to use in the operation
|
|
135
|
+
condition: a function that takes a cell and returns a boolean (used to filter cells)
|
|
136
|
+
"""
|
|
137
|
+
self.property_layers[property_name].modify_cells(operation, value, condition)
|
|
138
|
+
|
|
139
|
+
def __setstate__(self, state):
|
|
140
|
+
"""Set the state of the discrete space and rebuild the connections."""
|
|
141
|
+
self.__dict__ = state
|
|
142
|
+
self._connect_cells()
|
|
@@ -22,8 +22,21 @@ class Grid(DiscreteSpace[T], Generic[T]):
|
|
|
22
22
|
random (Random): the random number generator
|
|
23
23
|
_try_random (bool): whether to get empty cell be repeatedly trying random cell
|
|
24
24
|
|
|
25
|
+
Notes:
|
|
26
|
+
width and height are accessible via properties, higher dimensions can be retrieved via dimensions
|
|
27
|
+
|
|
25
28
|
"""
|
|
26
29
|
|
|
30
|
+
@property
|
|
31
|
+
def width(self) -> int:
|
|
32
|
+
"""Convenience access to the width of the grid."""
|
|
33
|
+
return self.dimensions[0]
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def height(self) -> int:
|
|
37
|
+
"""Convenience access to the height of the grid."""
|
|
38
|
+
return self.dimensions[1]
|
|
39
|
+
|
|
27
40
|
def __init__(
|
|
28
41
|
self,
|
|
29
42
|
dimensions: Sequence[int],
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Example of using ABM simulator for Wolf-Sheep Predation Model."""
|
|
2
2
|
|
|
3
3
|
import mesa
|
|
4
|
+
from mesa.experimental.cell_space import FixedAgent
|
|
4
5
|
from mesa.experimental.devs.simulator import ABMSimulator
|
|
5
6
|
|
|
6
7
|
|
|
@@ -90,7 +91,7 @@ class Wolf(Animal):
|
|
|
90
91
|
sheep_to_eat.die()
|
|
91
92
|
|
|
92
93
|
|
|
93
|
-
class GrassPatch(
|
|
94
|
+
class GrassPatch(FixedAgent):
|
|
94
95
|
"""A patch of grass that grows at a fixed rate and it is eaten by sheep."""
|
|
95
96
|
|
|
96
97
|
@property
|
mesa/model.py
CHANGED
|
@@ -8,14 +8,21 @@ Core Objects: Model
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
10
|
import random
|
|
11
|
+
import sys
|
|
11
12
|
import warnings
|
|
13
|
+
from collections.abc import Sequence
|
|
12
14
|
|
|
13
15
|
# mypy
|
|
14
16
|
from typing import Any
|
|
15
17
|
|
|
18
|
+
import numpy as np
|
|
19
|
+
|
|
16
20
|
from mesa.agent import Agent, AgentSet
|
|
17
21
|
from mesa.datacollection import DataCollector
|
|
18
22
|
|
|
23
|
+
SeedLike = int | np.integer | Sequence[int] | np.random.SeedSequence
|
|
24
|
+
RNGLike = np.random.Generator | np.random.BitGenerator
|
|
25
|
+
|
|
19
26
|
|
|
20
27
|
class Model:
|
|
21
28
|
"""Base class for models in the Mesa ABM library.
|
|
@@ -28,7 +35,8 @@ class Model:
|
|
|
28
35
|
running: A boolean indicating if the model should continue running.
|
|
29
36
|
schedule: An object to manage the order and execution of agent steps.
|
|
30
37
|
steps: the number of times `model.step()` has been called.
|
|
31
|
-
random: a seeded random number generator.
|
|
38
|
+
random: a seeded python.random number generator.
|
|
39
|
+
rng : a seeded numpy.random.Generator
|
|
32
40
|
|
|
33
41
|
Notes:
|
|
34
42
|
Model.agents returns the AgentSet containing all agents registered with the model. Changing
|
|
@@ -37,7 +45,13 @@ class Model:
|
|
|
37
45
|
|
|
38
46
|
"""
|
|
39
47
|
|
|
40
|
-
def __init__(
|
|
48
|
+
def __init__(
|
|
49
|
+
self,
|
|
50
|
+
*args: Any,
|
|
51
|
+
seed: float | None = None,
|
|
52
|
+
rng: RNGLike | SeedLike | None = None,
|
|
53
|
+
**kwargs: Any,
|
|
54
|
+
) -> None:
|
|
41
55
|
"""Create a new model.
|
|
42
56
|
|
|
43
57
|
Overload this method with the actual code to initialize the model. Always start with super().__init__()
|
|
@@ -46,25 +60,51 @@ class Model:
|
|
|
46
60
|
Args:
|
|
47
61
|
args: arguments to pass onto super
|
|
48
62
|
seed: the seed for the random number generator
|
|
63
|
+
rng : Pseudorandom number generator state. When `rng` is None, a new `numpy.random.Generator` is created
|
|
64
|
+
using entropy from the operating system. Types other than `numpy.random.Generator` are passed to
|
|
65
|
+
`numpy.random.default_rng` to instantiate a `Generator`.
|
|
49
66
|
kwargs: keyword arguments to pass onto super
|
|
67
|
+
|
|
68
|
+
Notes:
|
|
69
|
+
you have to pass either seed or rng, but not both.
|
|
70
|
+
|
|
50
71
|
"""
|
|
72
|
+
super().__init__(*args, **kwargs)
|
|
51
73
|
self.running = True
|
|
52
|
-
self.schedule = None
|
|
53
74
|
self.steps: int = 0
|
|
54
75
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
76
|
+
if (seed is not None) and (rng is not None):
|
|
77
|
+
raise ValueError("you have to pass either rng or seed, not both")
|
|
78
|
+
elif seed is None:
|
|
79
|
+
self.rng: np.random.Generator = np.random.default_rng(rng)
|
|
80
|
+
self._rng = (
|
|
81
|
+
self.rng.bit_generator.state
|
|
82
|
+
) # this allows for reproducing the rng
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
self.random = random.Random(rng)
|
|
86
|
+
except TypeError:
|
|
87
|
+
seed = int(self.rng.integers(np.iinfo(np.int32).max))
|
|
88
|
+
self.random = random.Random(seed)
|
|
89
|
+
self._seed = seed # this allows for reproducing stdlib.random
|
|
90
|
+
elif rng is None:
|
|
91
|
+
self.random = random.Random(seed)
|
|
92
|
+
self._seed = seed # this allows for reproducing stdlib.random
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
self.rng: np.random.Generator = np.random.default_rng(rng)
|
|
96
|
+
except TypeError:
|
|
97
|
+
rng = self.random.randint(0, sys.maxsize)
|
|
98
|
+
self.rng: np.random.Generator = np.random.default_rng(rng)
|
|
99
|
+
self._rng = self.rng.bit_generator.state
|
|
63
100
|
|
|
64
101
|
# Wrap the user-defined step method
|
|
65
102
|
self._user_step = self.step
|
|
66
103
|
self.step = self._wrapped_step
|
|
67
104
|
|
|
105
|
+
# setup agent registration data structures
|
|
106
|
+
self._setup_agent_registration()
|
|
107
|
+
|
|
68
108
|
def _wrapped_step(self, *args: Any, **kwargs: Any) -> None:
|
|
69
109
|
"""Automatically increments time and steps after calling the user's step method."""
|
|
70
110
|
# Automatically increment time and step counters
|
|
@@ -119,7 +159,9 @@ class Model:
|
|
|
119
159
|
self._agents_by_type: dict[
|
|
120
160
|
type[Agent], AgentSet
|
|
121
161
|
] = {} # a dict with an agentset for each class of agents
|
|
122
|
-
self._all_agents = AgentSet(
|
|
162
|
+
self._all_agents = AgentSet(
|
|
163
|
+
[], random=self.random
|
|
164
|
+
) # an agenset with all agents
|
|
123
165
|
|
|
124
166
|
def register_agent(self, agent):
|
|
125
167
|
"""Register the agent with the model.
|
|
@@ -153,7 +195,7 @@ class Model:
|
|
|
153
195
|
[
|
|
154
196
|
agent,
|
|
155
197
|
],
|
|
156
|
-
self,
|
|
198
|
+
random=self.random,
|
|
157
199
|
)
|
|
158
200
|
|
|
159
201
|
self._all_agents.add(agent)
|
|
@@ -194,6 +236,15 @@ class Model:
|
|
|
194
236
|
self.random.seed(seed)
|
|
195
237
|
self._seed = seed
|
|
196
238
|
|
|
239
|
+
def reset_rng(self, rng: RNGLike | SeedLike | None = None) -> None:
|
|
240
|
+
"""Reset the model random number generator.
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
rng: A new seed for the RNG; if None, reset using the current seed
|
|
244
|
+
"""
|
|
245
|
+
self.rng = np.random.default_rng(rng)
|
|
246
|
+
self._rng = self.rng.bit_generator.state
|
|
247
|
+
|
|
197
248
|
def initialize_data_collector(
|
|
198
249
|
self,
|
|
199
250
|
model_reporters=None,
|
|
@@ -210,14 +261,13 @@ class Model:
|
|
|
210
261
|
tables: tables to collect
|
|
211
262
|
|
|
212
263
|
"""
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
)
|
|
264
|
+
warnings.warn(
|
|
265
|
+
"initialize_data_collector() is deprecated. Please use the DataCollector class directly. "
|
|
266
|
+
"by using `self.datacollector = DataCollector(...)`.",
|
|
267
|
+
DeprecationWarning,
|
|
268
|
+
stacklevel=2,
|
|
269
|
+
)
|
|
270
|
+
|
|
221
271
|
self.datacollector = DataCollector(
|
|
222
272
|
model_reporters=model_reporters,
|
|
223
273
|
agent_reporters=agent_reporters,
|
mesa/time.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
.. warning::
|
|
4
4
|
The time module and all its Schedulers are deprecated and will be removed in a future version.
|
|
5
5
|
They can be replaced with AgentSet functionality. See the migration guide for details:
|
|
6
|
-
https://mesa.readthedocs.io/
|
|
6
|
+
https://mesa.readthedocs.io/latest/migration_guide.html#time-and-schedulers
|
|
7
7
|
|
|
8
8
|
Objects for handling the time component of a model. In particular, this module
|
|
9
9
|
contains Schedulers, which handle agent activation. A Scheduler is an object
|
|
@@ -65,7 +65,7 @@ class BaseScheduler:
|
|
|
65
65
|
warnings.warn(
|
|
66
66
|
"The time module and all its Schedulers are deprecated and will be removed in a future version. "
|
|
67
67
|
"They can be replaced with AgentSet functionality. See the migration guide for details. "
|
|
68
|
-
"https://mesa.readthedocs.io/
|
|
68
|
+
"https://mesa.readthedocs.io/latest/migration_guide.html#time-and-schedulers",
|
|
69
69
|
DeprecationWarning,
|
|
70
70
|
stacklevel=2,
|
|
71
71
|
)
|
|
@@ -77,7 +77,7 @@ class BaseScheduler:
|
|
|
77
77
|
if agents is None:
|
|
78
78
|
agents = []
|
|
79
79
|
|
|
80
|
-
self._agents: AgentSet = AgentSet(agents, model)
|
|
80
|
+
self._agents: AgentSet = AgentSet(agents, model.random)
|
|
81
81
|
|
|
82
82
|
self._remove_warning_given = False
|
|
83
83
|
self._agents_key_warning_given = False
|
|
@@ -312,7 +312,9 @@ class RandomActivationByType(BaseScheduler):
|
|
|
312
312
|
try:
|
|
313
313
|
self._agents_by_type[type(agent)].add(agent)
|
|
314
314
|
except KeyError:
|
|
315
|
-
self._agents_by_type[type(agent)] = AgentSet(
|
|
315
|
+
self._agents_by_type[type(agent)] = AgentSet(
|
|
316
|
+
[agent], self.model.random
|
|
317
|
+
)
|
|
316
318
|
|
|
317
319
|
def add(self, agent: Agent) -> None:
|
|
318
320
|
"""Add an Agent object to the schedule.
|
|
@@ -325,7 +327,7 @@ class RandomActivationByType(BaseScheduler):
|
|
|
325
327
|
try:
|
|
326
328
|
self._agents_by_type[type(agent)].add(agent)
|
|
327
329
|
except KeyError:
|
|
328
|
-
self._agents_by_type[type(agent)] = AgentSet([agent], self.model)
|
|
330
|
+
self._agents_by_type[type(agent)] = AgentSet([agent], self.model.random)
|
|
329
331
|
|
|
330
332
|
def remove(self, agent: Agent) -> None:
|
|
331
333
|
"""Remove all instances of a given agent from the schedule.
|