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.

Files changed (49) hide show
  1. mesa/__init__.py +3 -3
  2. mesa/agent.py +0 -48
  3. mesa/batchrunner.py +1 -14
  4. mesa/datacollection.py +6 -1
  5. mesa/examples/__init__.py +2 -2
  6. mesa/examples/advanced/epstein_civil_violence/app.py +0 -5
  7. mesa/examples/advanced/pd_grid/app.py +0 -5
  8. mesa/examples/advanced/sugarscape_g1mt/app.py +2 -7
  9. mesa/examples/basic/boid_flockers/app.py +0 -5
  10. mesa/examples/basic/boltzmann_wealth_model/app.py +5 -8
  11. mesa/examples/basic/boltzmann_wealth_model/st_app.py +1 -1
  12. mesa/examples/basic/conways_game_of_life/app.py +0 -5
  13. mesa/examples/basic/conways_game_of_life/st_app.py +2 -2
  14. mesa/examples/basic/schelling/app.py +0 -5
  15. mesa/examples/basic/virus_on_network/app.py +0 -5
  16. mesa/experimental/UserParam.py +67 -0
  17. mesa/experimental/__init__.py +10 -17
  18. mesa/experimental/cell_space/__init__.py +7 -19
  19. mesa/experimental/cell_space/cell.py +37 -22
  20. mesa/experimental/cell_space/cell_agent.py +1 -12
  21. mesa/experimental/cell_space/cell_collection.py +3 -18
  22. mesa/experimental/cell_space/discrete_space.py +64 -15
  23. mesa/experimental/cell_space/grid.py +4 -74
  24. mesa/experimental/cell_space/network.py +1 -13
  25. mesa/experimental/cell_space/voronoi.py +1 -13
  26. mesa/experimental/components/altair.py +81 -0
  27. mesa/experimental/components/matplotlib.py +242 -0
  28. mesa/experimental/devs/__init__.py +2 -20
  29. mesa/experimental/devs/eventlist.py +1 -19
  30. mesa/experimental/devs/examples/epstein_civil_violence.py +305 -0
  31. mesa/experimental/devs/examples/wolf_sheep.py +250 -0
  32. mesa/experimental/devs/simulator.py +8 -24
  33. mesa/experimental/solara_viz.py +453 -0
  34. mesa/model.py +23 -17
  35. mesa/visualization/__init__.py +2 -2
  36. mesa/visualization/mpl_space_drawing.py +2 -2
  37. mesa/visualization/solara_viz.py +5 -23
  38. {mesa-3.1.0.dist-info → mesa-3.1.0.dev0.dist-info}/METADATA +1 -1
  39. {mesa-3.1.0.dist-info → mesa-3.1.0.dev0.dist-info}/RECORD +43 -43
  40. {mesa-3.1.0.dist-info → mesa-3.1.0.dev0.dist-info}/WHEEL +1 -1
  41. mesa/experimental/cell_space/property_layer.py +0 -444
  42. mesa/experimental/mesa_signals/__init__.py +0 -23
  43. mesa/experimental/mesa_signals/mesa_signal.py +0 -485
  44. mesa/experimental/mesa_signals/observable_collections.py +0 -133
  45. mesa/experimental/mesa_signals/signals_util.py +0 -52
  46. mesa/mesa_logging.py +0 -190
  47. {mesa-3.1.0.dist-info → mesa-3.1.0.dev0.dist-info}/entry_points.txt +0 -0
  48. {mesa-3.1.0.dist-info → mesa-3.1.0.dev0.dist-info}/licenses/LICENSE +0 -0
  49. {mesa-3.1.0.dist-info → mesa-3.1.0.dev0.dist-info}/licenses/NOTICE +0 -0
mesa/__init__.py CHANGED
@@ -13,16 +13,16 @@ from mesa.datacollection import DataCollector
13
13
  from mesa.model import Model
14
14
 
15
15
  __all__ = [
16
+ "Model",
16
17
  "Agent",
18
+ "space",
17
19
  "DataCollector",
18
- "Model",
19
20
  "batch_run",
20
21
  "experimental",
21
- "space",
22
22
  ]
23
23
 
24
24
  __title__ = "mesa"
25
- __version__ = "3.1.0"
25
+ __version__ = "3.1.0.dev"
26
26
  __license__ = "Apache 2.0"
27
27
  _this_year = datetime.datetime.now(tz=datetime.UTC).date().year
28
28
  __copyright__ = f"Copyright {_this_year} Project Mesa Team"
mesa/agent.py CHANGED
@@ -85,54 +85,6 @@ class Agent:
85
85
  def advance(self) -> None: # noqa: D102
86
86
  pass
87
87
 
88
- @classmethod
89
- def create_agents(cls, model: Model, n: int, *args, **kwargs) -> AgentSet[Agent]:
90
- """Create N agents.
91
-
92
- Args:
93
- model: the model to which the agents belong
94
- args: arguments to pass onto agent instances
95
- each arg is either a single object or a sequence of length n
96
- n: the number of agents to create
97
- kwargs: keyword arguments to pass onto agent instances
98
- each keyword arg is either a single object or a sequence of length n
99
-
100
- Returns:
101
- AgentSet containing the agents created.
102
-
103
- """
104
-
105
- class ListLike:
106
- """Helper class to make default arguments act as if they are in a list of length N."""
107
-
108
- def __init__(self, value):
109
- self.value = value
110
-
111
- def __getitem__(self, i):
112
- return self.value
113
-
114
- listlike_args = []
115
- for arg in args:
116
- if isinstance(arg, (list | np.ndarray | tuple)) and len(arg) == n:
117
- listlike_args.append(arg)
118
- else:
119
- listlike_args.append(ListLike(arg))
120
-
121
- listlike_kwargs = {}
122
- for k, v in kwargs.items():
123
- if isinstance(v, (list | np.ndarray | tuple)) and len(v) == n:
124
- listlike_kwargs[k] = v
125
- else:
126
- listlike_kwargs[k] = ListLike(v)
127
-
128
- agents = []
129
- for i in range(n):
130
- instance_args = [arg[i] for arg in listlike_args]
131
- instance_kwargs = {k: v[i] for k, v in listlike_kwargs.items()}
132
- agent = cls(model, *instance_args, **instance_kwargs)
133
- agents.append(agent)
134
- return AgentSet(agents, random=model.random)
135
-
136
88
  @property
137
89
  def random(self) -> Random:
138
90
  """Return a seeded stdlib rng."""
mesa/batchrunner.py CHANGED
@@ -106,14 +106,7 @@ def _make_model_kwargs(
106
106
  Parameters
107
107
  ----------
108
108
  parameters : Mapping[str, Union[Any, Iterable[Any]]]
109
- Single or multiple values for each model parameter name.
110
-
111
- Allowed values for each parameter:
112
- - A single value (e.g., `32`, `"relu"`).
113
- - A non-empty iterable (e.g., `[0.01, 0.1]`, `["relu", "sigmoid"]`).
114
-
115
- Not allowed:
116
- - Empty lists or empty iterables (e.g., `[]`, `()`, etc.). These should be removed manually.
109
+ Single or multiple values for each model parameter name
117
110
 
118
111
  Returns:
119
112
  -------
@@ -125,12 +118,6 @@ def _make_model_kwargs(
125
118
  if isinstance(values, str):
126
119
  # The values is a single string, so we shouldn't iterate over it.
127
120
  all_values = [(param, values)]
128
- elif isinstance(values, list | tuple | set) and len(values) == 0:
129
- # If it's an empty iterable, raise an error
130
- raise ValueError(
131
- f"Parameter '{param}' contains an empty iterable, which is not allowed."
132
- )
133
-
134
121
  else:
135
122
  try:
136
123
  all_values = [(param, value) for value in values]
mesa/datacollection.py CHANGED
@@ -228,7 +228,12 @@ class DataCollector:
228
228
  reports = tuple(rep(agent) for rep in rep_funcs)
229
229
  return _prefix + reports
230
230
 
231
- agent_records = map(get_reports, model.agents)
231
+ agent_records = map(
232
+ get_reports,
233
+ model.schedule.agents
234
+ if hasattr(model, "schedule") and model.schedule is not None
235
+ else model.agents,
236
+ )
232
237
  return agent_records
233
238
 
234
239
  def _record_agenttype(self, model, agent_type):
mesa/examples/__init__.py CHANGED
@@ -12,10 +12,10 @@ __all__ = [
12
12
  "BoidFlockers",
13
13
  "BoltzmannWealth",
14
14
  "ConwaysGameOfLife",
15
+ "Schelling",
16
+ "VirusOnNetwork",
15
17
  "EpsteinCivilViolence",
16
18
  "PdGrid",
17
- "Schelling",
18
19
  "SugarscapeG1mt",
19
- "VirusOnNetwork",
20
20
  "WolfSheep",
21
21
  ]
@@ -44,11 +44,6 @@ def post_process(ax):
44
44
 
45
45
 
46
46
  model_params = {
47
- "seed": {
48
- "type": "InputText",
49
- "value": 42,
50
- "label": "Random Seed",
51
- },
52
47
  "height": 40,
53
48
  "width": 40,
54
49
  "citizen_density": Slider("Initial Agent Density", 0.7, 0.0, 0.9, 0.1),
@@ -24,11 +24,6 @@ def pd_agent_portrayal(agent):
24
24
 
25
25
  # Model parameters
26
26
  model_params = {
27
- "seed": {
28
- "type": "InputText",
29
- "value": 42,
30
- "label": "Random Seed",
31
- },
32
27
  "width": Slider("Grid Width", value=50, min=10, max=100, step=1),
33
28
  "height": Slider("Grid Height", value=50, min=10, max=100, step=1),
34
29
  "activation_order": {
@@ -47,11 +47,6 @@ def SpaceDrawer(model):
47
47
 
48
48
 
49
49
  model_params = {
50
- "seed": {
51
- "type": "InputText",
52
- "value": 42,
53
- "label": "Random Seed",
54
- },
55
50
  "width": 50,
56
51
  "height": 50,
57
52
  # Population parameters
@@ -71,10 +66,10 @@ model_params = {
71
66
  "enable_trade": {"type": "Checkbox", "value": True, "label": "Enable Trading"},
72
67
  }
73
68
 
74
- model = SugarscapeG1mt()
69
+ model1 = SugarscapeG1mt()
75
70
 
76
71
  page = SolaraViz(
77
- model,
72
+ model1,
78
73
  components=[SpaceDrawer, make_plot_component(["Trader", "Price"])],
79
74
  model_params=model_params,
80
75
  name="Sugarscape {G1, M, T}",
@@ -12,11 +12,6 @@ def boid_draw(agent):
12
12
 
13
13
 
14
14
  model_params = {
15
- "seed": {
16
- "type": "InputText",
17
- "value": 42,
18
- "label": "Random Seed",
19
- },
20
15
  "population": Slider(
21
16
  label="Number of boids",
22
17
  value=100,
@@ -1,13 +1,10 @@
1
1
  from mesa.examples.basic.boltzmann_wealth_model.model import BoltzmannWealth
2
- from mesa.mesa_logging import DEBUG, log_to_stderr
3
2
  from mesa.visualization import (
4
3
  SolaraViz,
5
4
  make_plot_component,
6
5
  make_space_component,
7
6
  )
8
7
 
9
- log_to_stderr(DEBUG)
10
-
11
8
 
12
9
  def agent_portrayal(agent):
13
10
  color = agent.wealth # we are using a colormap to translate wealth to color
@@ -15,11 +12,6 @@ def agent_portrayal(agent):
15
12
 
16
13
 
17
14
  model_params = {
18
- "seed": {
19
- "type": "InputText",
20
- "value": 42,
21
- "label": "Random Seed",
22
- },
23
15
  "n": {
24
16
  "type": "SliderInt",
25
17
  "value": 50,
@@ -28,6 +20,11 @@ model_params = {
28
20
  "max": 100,
29
21
  "step": 1,
30
22
  },
23
+ "seed": {
24
+ "type": "InputText",
25
+ "value": 42,
26
+ "label": "Random Seed",
27
+ },
31
28
  "width": 10,
32
29
  "height": 10,
33
30
  }
@@ -68,7 +68,7 @@ if run:
68
68
  for i in range(num_ticks):
69
69
  model.step()
70
70
  my_bar.progress((i / num_ticks), text="Simulation progress")
71
- placeholder.text(f"Step = {i}")
71
+ placeholder.text("Step = %d" % i)
72
72
  for cell in model.grid.coord_iter():
73
73
  cell_content, (x, y) = cell
74
74
  agent_count = len(cell_content)
@@ -20,11 +20,6 @@ def post_process(ax):
20
20
 
21
21
 
22
22
  model_params = {
23
- "seed": {
24
- "type": "InputText",
25
- "value": 42,
26
- "label": "Random Seed",
27
- },
28
23
  "width": {
29
24
  "type": "SliderInt",
30
25
  "value": 50,
@@ -49,9 +49,9 @@ if run:
49
49
  for i in range(num_ticks):
50
50
  model.step()
51
51
  my_bar.progress((i / num_ticks), text="Simulation progress")
52
- placeholder.text(f"Step = {i}")
52
+ placeholder.text("Step = %d" % i)
53
53
  for contents, (x, y) in model.grid.coord_iter():
54
- # print(f"x: {x}, y: {y}, state: {contents}")
54
+ # print('x:',x,'y:',y, 'state:',contents)
55
55
  selected_row = df_grid[(df_grid["x"] == x) & (df_grid["y"] == y)]
56
56
  df_grid.loc[selected_row.index, "state"] = (
57
57
  contents.state
@@ -19,11 +19,6 @@ def agent_portrayal(agent):
19
19
 
20
20
 
21
21
  model_params = {
22
- "seed": {
23
- "type": "InputText",
24
- "value": 42,
25
- "label": "Random Seed",
26
- },
27
22
  "density": Slider("Agent density", 0.8, 0.1, 1.0, 0.1),
28
23
  "minority_pc": Slider("Fraction minority", 0.2, 0.0, 1.0, 0.05),
29
24
  "homophily": Slider("Homophily", 3, 0, 8, 1),
@@ -35,11 +35,6 @@ def get_resistant_susceptible_ratio(model):
35
35
 
36
36
 
37
37
  model_params = {
38
- "seed": {
39
- "type": "InputText",
40
- "value": 42,
41
- "label": "Random Seed",
42
- },
43
38
  "num_nodes": Slider(
44
39
  label="Number of agents",
45
40
  value=10,
@@ -0,0 +1,67 @@
1
+ """helper classes."""
2
+
3
+
4
+ class UserParam: # noqa: D101
5
+ _ERROR_MESSAGE = "Missing or malformed inputs for '{}' Option '{}'"
6
+
7
+ def maybe_raise_error(self, param_type, valid): # noqa: D102
8
+ if valid:
9
+ return
10
+ msg = self._ERROR_MESSAGE.format(param_type, self.label)
11
+ raise ValueError(msg)
12
+
13
+
14
+ class Slider(UserParam):
15
+ """A number-based slider input with settable increment.
16
+
17
+ Example:
18
+ slider_option = Slider("My Slider", value=123, min=10, max=200, step=0.1)
19
+
20
+ Args:
21
+ label: The displayed label in the UI
22
+ value: The initial value of the slider
23
+ min: The minimum possible value of the slider
24
+ max: The maximum possible value of the slider
25
+ step: The step between min and max for a range of possible values
26
+ dtype: either int or float
27
+ """
28
+
29
+ def __init__(
30
+ self,
31
+ label="",
32
+ value=None,
33
+ min=None,
34
+ max=None,
35
+ step=1,
36
+ dtype=None,
37
+ ):
38
+ """Slider class.
39
+
40
+ Args:
41
+ label: The displayed label in the UI
42
+ value: The initial value of the slider
43
+ min: The minimum possible value of the slider
44
+ max: The maximum possible value of the slider
45
+ step: The step between min and max for a range of possible values
46
+ dtype: either int or float
47
+ """
48
+ self.label = label
49
+ self.value = value
50
+ self.min = min
51
+ self.max = max
52
+ self.step = step
53
+
54
+ # Validate option type to make sure values are supplied properly
55
+ valid = not (self.value is None or self.min is None or self.max is None)
56
+ self.maybe_raise_error("slider", valid)
57
+
58
+ if dtype is None:
59
+ self.is_float_slider = self._check_values_are_float(value, min, max, step)
60
+ else:
61
+ self.is_float_slider = dtype is float
62
+
63
+ def _check_values_are_float(self, value, min, max, step):
64
+ return any(isinstance(n, float) for n in (value, min, max, step))
65
+
66
+ def get(self, attr): # noqa: D102
67
+ return getattr(self, attr)
@@ -1,20 +1,13 @@
1
- """Experimental features package for Mesa.
1
+ """Experimental init."""
2
2
 
3
- This package contains modules that are under active development and testing. These
4
- features are provided to allow early access and feedback from the Mesa community, but
5
- their APIs may change between releases without following semantic versioning.
3
+ from mesa.experimental import cell_space
6
4
 
7
- Current experimental modules:
8
- cell_space: Alternative API for discrete spaces with cell-centric functionality
9
- devs: Discrete event simulation system for scheduling events at arbitrary times
10
- mesa_signals: Reactive programming capabilities for tracking state changes
5
+ try:
6
+ from .solara_viz import JupyterViz, Slider, SolaraViz, make_text
11
7
 
12
- Notes:
13
- - Features in this package may be changed or removed without notice
14
- - APIs are not guaranteed to be stable between releases
15
- - Features graduate from experimental status once their APIs are stabilized
16
- """
17
-
18
- from mesa.experimental import cell_space, devs, mesa_signals
19
-
20
- __all__ = ["cell_space", "devs", "mesa_signals"]
8
+ __all__ = ["cell_space", "JupyterViz", "Slider", "SolaraViz", "make_text"]
9
+ except ImportError:
10
+ print(
11
+ "Could not import SolaraViz. If you need it, install with 'pip install --pre mesa[viz]'"
12
+ )
13
+ __all__ = ["cell_space"]
@@ -1,18 +1,8 @@
1
- """Cell spaces for active, property-rich spatial modeling in Mesa.
1
+ """Cell spaces.
2
2
 
3
- Cell spaces extend Mesa's spatial modeling capabilities by making the space itself active -
4
- each position (cell) can have properties and behaviors rather than just containing agents.
5
- This enables more sophisticated environmental modeling and agent-environment interactions.
3
+ Cell spaces offer an alternative API for discrete spaces. It is experimental and under development. The API is more
4
+ expressive that the default grids available in `mesa.space`.
6
5
 
7
- Key components:
8
- - Cells: Active positions that can have properties and contain agents
9
- - CellAgents: Agents that understand how to interact with cells
10
- - Spaces: Different cell organization patterns (grids, networks, etc.)
11
- - PropertyLayers: Efficient property storage and manipulation
12
-
13
- This is particularly useful for models where the environment plays an active role,
14
- like resource growth, pollution diffusion, or infrastructure networks. The cell
15
- space system is experimental and under active development.
16
6
  """
17
7
 
18
8
  from mesa.experimental.cell_space.cell import Cell
@@ -30,21 +20,19 @@ from mesa.experimental.cell_space.grid import (
30
20
  OrthogonalVonNeumannGrid,
31
21
  )
32
22
  from mesa.experimental.cell_space.network import Network
33
- from mesa.experimental.cell_space.property_layer import PropertyLayer
34
23
  from mesa.experimental.cell_space.voronoi import VoronoiGrid
35
24
 
36
25
  __all__ = [
26
+ "CellCollection",
37
27
  "Cell",
38
28
  "CellAgent",
39
- "CellCollection",
40
- "DiscreteSpace",
29
+ "Grid2DMovingAgent",
41
30
  "FixedAgent",
31
+ "DiscreteSpace",
42
32
  "Grid",
43
- "Grid2DMovingAgent",
44
33
  "HexGrid",
45
- "Network",
46
34
  "OrthogonalMooreGrid",
47
35
  "OrthogonalVonNeumannGrid",
48
- "PropertyLayer",
36
+ "Network",
49
37
  "VoronoiGrid",
50
38
  ]
@@ -1,25 +1,15 @@
1
- """Cells are positions in space that can have properties and contain agents.
2
-
3
- A cell represents a location that can:
4
- - Have properties (like temperature or resources)
5
- - Track and limit the agents it contains
6
- - Connect to neighboring cells
7
- - Provide neighborhood information
8
-
9
- Cells form the foundation of the cell space system, enabling rich spatial
10
- environments where both location properties and agent behaviors matter. They're
11
- useful for modeling things like varying terrain, infrastructure capacity, or
12
- environmental conditions.
13
- """
1
+ """The Cell in a cell space."""
14
2
 
15
3
  from __future__ import annotations
16
4
 
5
+ from collections.abc import Callable
17
6
  from functools import cache, cached_property
18
7
  from random import Random
19
- from typing import TYPE_CHECKING
8
+ from typing import TYPE_CHECKING, Any
20
9
 
21
10
  from mesa.experimental.cell_space.cell_agent import CellAgent
22
11
  from mesa.experimental.cell_space.cell_collection import CellCollection
12
+ from mesa.space import PropertyLayer
23
13
 
24
14
  if TYPE_CHECKING:
25
15
  from mesa.agent import Agent
@@ -34,20 +24,31 @@ class Cell:
34
24
  coordinate (Tuple[int, int]) : the position of the cell in the discrete space
35
25
  agents (List[Agent]): the agents occupying the cell
36
26
  capacity (int): the maximum number of agents that can simultaneously occupy the cell
27
+ properties (dict[str, Any]): the properties of the cell
37
28
  random (Random): the random number generator
38
29
 
39
30
  """
40
31
 
41
32
  __slots__ = [
42
- "__dict__",
33
+ "coordinate",
34
+ "connections",
43
35
  "agents",
44
36
  "capacity",
45
- "connections",
46
- "coordinate",
47
37
  "properties",
48
38
  "random",
39
+ "_mesa_property_layers",
40
+ "__dict__",
49
41
  ]
50
42
 
43
+ # def __new__(cls,
44
+ # coordinate: tuple[int, ...],
45
+ # capacity: float | None = None,
46
+ # random: Random | None = None,):
47
+ # if capacity != 1:
48
+ # return object.__new__(cls)
49
+ # else:
50
+ # return object.__new__(SingleAgentCell)
51
+
51
52
  def __init__(
52
53
  self,
53
54
  coordinate: Coordinate,
@@ -69,10 +70,9 @@ class Cell:
69
70
  Agent
70
71
  ] = [] # TODO:: change to AgentSet or weakrefs? (neither is very performant, )
71
72
  self.capacity: int | None = capacity
72
- self.properties: dict[
73
- Coordinate, object
74
- ] = {} # fixme still used by voronoi mesh
73
+ self.properties: dict[Coordinate, object] = {}
75
74
  self.random = random
75
+ self._mesa_property_layers: dict[str, PropertyLayer] = {}
76
76
 
77
77
  def connect(self, other: Cell, key: Coordinate | None = None) -> None:
78
78
  """Connects this cell to another cell.
@@ -105,7 +105,6 @@ class Cell:
105
105
 
106
106
  """
107
107
  n = len(self.agents)
108
- self.empty = False
109
108
 
110
109
  if self.capacity and n >= self.capacity:
111
110
  raise Exception(
@@ -122,7 +121,6 @@ class Cell:
122
121
 
123
122
  """
124
123
  self.agents.remove(agent)
125
- self.empty = self.is_empty
126
124
 
127
125
  @property
128
126
  def is_empty(self) -> bool:
@@ -197,6 +195,23 @@ class Cell:
197
195
  neighborhood.pop(self, None)
198
196
  return neighborhood
199
197
 
198
+ # PropertyLayer methods
199
+ def get_property(self, property_name: str) -> Any:
200
+ """Get the value of a property."""
201
+ return self._mesa_property_layers[property_name].data[self.coordinate]
202
+
203
+ def set_property(self, property_name: str, value: Any):
204
+ """Set the value of a property."""
205
+ self._mesa_property_layers[property_name].set_cell(self.coordinate, value)
206
+
207
+ def modify_property(
208
+ self, property_name: str, operation: Callable, value: Any = None
209
+ ):
210
+ """Modify the value of a property."""
211
+ self._mesa_property_layers[property_name].modify_cell(
212
+ self.coordinate, operation, value
213
+ )
214
+
200
215
  def __getstate__(self):
201
216
  """Return state of the Cell with connections set to empty."""
202
217
  # fixme, once we shift to 3.11, replace this with super. __getstate__
@@ -1,15 +1,4 @@
1
- """Agents that understand how to exist in and move through cell spaces.
2
-
3
- Provides specialized agent classes that handle cell occupation, movement, and
4
- proper registration:
5
- - CellAgent: Mobile agents that can move between cells
6
- - FixedAgent: Immobile agents permanently fixed to cells
7
- - Grid2DMovingAgent: Agents with grid-specific movement capabilities
8
-
9
- These classes ensure consistent agent-cell relationships and proper state management
10
- as agents move through the space. They can be used directly or as examples for
11
- creating custom cell-aware agents.
12
- """
1
+ """An agent with movement methods for cell spaces."""
13
2
 
14
3
  from __future__ import annotations
15
4
 
@@ -1,17 +1,4 @@
1
- """Collection class for managing and querying groups of cells.
2
-
3
- The CellCollection class provides a consistent interface for operating on multiple
4
- cells, supporting:
5
- - Filtering and selecting cells based on conditions
6
- - Random cell and agent selection
7
- - Access to contained agents
8
- - Group operations
9
-
10
- This is useful for implementing area effects, zones, or any operation that needs
11
- to work with multiple cells as a unit. The collection handles efficient iteration
12
- and agent access across cells. The class is used throughout the cell space
13
- implementation to represent neighborhoods, selections, and other cell groupings.
14
- """
1
+ """CellCollection class."""
15
2
 
16
3
  from __future__ import annotations
17
4
 
@@ -61,10 +48,8 @@ class CellCollection(Generic[T]):
61
48
  else:
62
49
  self._cells = {cell: cell.agents for cell in cells}
63
50
 
64
- # Get capacity from first cell if collection is not empty
65
- self._capacity: int | None = (
66
- next(iter(self._cells.keys())).capacity if self._cells else None
67
- )
51
+ #
52
+ self._capacity: int = next(iter(self._cells.keys())).capacity
68
53
 
69
54
  if random is None:
70
55
  warnings.warn(