Mesa 3.0.0a3__py3-none-any.whl → 3.0.0a5__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 (40) hide show
  1. mesa/__init__.py +2 -3
  2. mesa/agent.py +193 -75
  3. mesa/batchrunner.py +18 -23
  4. mesa/cookiecutter-mesa/hooks/post_gen_project.py +2 -0
  5. mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/__init__.py +1 -0
  6. mesa/datacollection.py +138 -27
  7. mesa/experimental/UserParam.py +67 -0
  8. mesa/experimental/__init__.py +5 -1
  9. mesa/experimental/cell_space/__init__.py +7 -0
  10. mesa/experimental/cell_space/cell.py +61 -20
  11. mesa/experimental/cell_space/cell_agent.py +12 -7
  12. mesa/experimental/cell_space/cell_collection.py +54 -17
  13. mesa/experimental/cell_space/discrete_space.py +16 -5
  14. mesa/experimental/cell_space/grid.py +19 -8
  15. mesa/experimental/cell_space/network.py +9 -7
  16. mesa/experimental/cell_space/voronoi.py +26 -33
  17. mesa/experimental/components/altair.py +81 -0
  18. mesa/experimental/components/matplotlib.py +242 -0
  19. mesa/experimental/devs/__init__.py +2 -0
  20. mesa/experimental/devs/eventlist.py +36 -15
  21. mesa/experimental/devs/examples/epstein_civil_violence.py +71 -39
  22. mesa/experimental/devs/examples/wolf_sheep.py +43 -44
  23. mesa/experimental/devs/simulator.py +55 -15
  24. mesa/experimental/solara_viz.py +453 -0
  25. mesa/main.py +6 -4
  26. mesa/model.py +64 -61
  27. mesa/space.py +154 -123
  28. mesa/time.py +57 -67
  29. mesa/visualization/UserParam.py +19 -6
  30. mesa/visualization/__init__.py +14 -2
  31. mesa/visualization/components/altair.py +18 -1
  32. mesa/visualization/components/matplotlib.py +26 -2
  33. mesa/visualization/solara_viz.py +231 -225
  34. mesa/visualization/utils.py +9 -0
  35. {mesa-3.0.0a3.dist-info → mesa-3.0.0a5.dist-info}/METADATA +2 -1
  36. mesa-3.0.0a5.dist-info/RECORD +44 -0
  37. mesa-3.0.0a3.dist-info/RECORD +0 -39
  38. {mesa-3.0.0a3.dist-info → mesa-3.0.0a5.dist-info}/WHEEL +0 -0
  39. {mesa-3.0.0a3.dist-info → mesa-3.0.0a5.dist-info}/entry_points.txt +0 -0
  40. {mesa-3.0.0a3.dist-info → mesa-3.0.0a5.dist-info}/licenses/LICENSE +0 -0
mesa/space.py CHANGED
@@ -1,16 +1,13 @@
1
- """
2
- Mesa Space Module
3
- =================
1
+ """Mesa Space Module.
4
2
 
5
3
  Objects used to add a spatial component to a model.
6
4
 
7
- Grid: base grid, which creates a rectangular grid.
8
- SingleGrid: extension to Grid which strictly enforces one agent per cell.
9
- MultiGrid: extension to Grid where each cell can contain a set of agents.
10
- HexGrid: extension to Grid to handle hexagonal neighbors.
11
- ContinuousSpace: a two-dimensional space where each agent has an arbitrary
12
- position of `float`'s.
13
- NetworkGrid: a network where each node contains zero or more agents.
5
+ * Grid: base grid, which creates a rectangular grid.
6
+ * SingleGrid: extension to Grid which strictly enforces one agent per cell.
7
+ * MultiGrid: extension to Grid where each cell can contain a set of agents.
8
+ * HexGrid: extension to Grid to handle hexagonal neighbors.
9
+ * ContinuousSpace: a two-dimensional space where each agent has an arbitrary position of `float`'s.
10
+ * NetworkGrid: a network where each node contains zero or more agents.
14
11
  """
15
12
 
16
13
  # Mypy; for the `|` operator purpose
@@ -54,9 +51,10 @@ F = TypeVar("F", bound=Callable[..., Any])
54
51
 
55
52
 
56
53
  def accept_tuple_argument(wrapped_function: F) -> F:
57
- """Decorator to allow grid methods that take a list of (x, y) coord tuples
58
- to also handle a single position, by automatically wrapping tuple in
59
- single-item list rather than forcing user to do it."""
54
+ """Decorator to allow grid methods that take a list of (x, y) coord tuples to also handle a single position.
55
+
56
+ Tuples are wrapped in a single-item list rather than forcing user to do it.
57
+ """
60
58
 
61
59
  def wrapper(grid_instance, positions) -> Any:
62
60
  if len(positions) == 2 and not isinstance(positions[0], tuple):
@@ -67,11 +65,13 @@ def accept_tuple_argument(wrapped_function: F) -> F:
67
65
 
68
66
 
69
67
  def is_integer(x: Real) -> bool:
70
- # Check if x is either a CPython integer or Numpy integer.
68
+ """Check if x is either a CPython integer or Numpy integer."""
71
69
  return isinstance(x, _types_integer)
72
70
 
73
71
 
74
72
  def warn_if_agent_has_position_already(placement_func):
73
+ """Decorator to give warning if agent has position already set."""
74
+
75
75
  def wrapper(self, agent, *args, **kwargs):
76
76
  if agent.pos is not None:
77
77
  warnings.warn(
@@ -102,7 +102,8 @@ class _Grid:
102
102
  """Create a new grid.
103
103
 
104
104
  Args:
105
- width, height: The width and height of the grid
105
+ width: The grid's width.
106
+ height: The grid's height.
106
107
  torus: Boolean whether the grid wraps or not.
107
108
  """
108
109
  self.height = height
@@ -159,7 +160,6 @@ class _Grid:
159
160
 
160
161
  def __getitem__(self, index):
161
162
  """Access contents from the grid."""
162
-
163
163
  if isinstance(index, int):
164
164
  # grid[x]
165
165
  return self._grid[index]
@@ -192,8 +192,7 @@ class _Grid:
192
192
  return [cell for rows in self._grid[x] for cell in rows[y]]
193
193
 
194
194
  def __iter__(self) -> Iterator[GridContent]:
195
- """Create an iterator that chains the rows of the grid together
196
- as if it is one list:"""
195
+ """Create an iterator that chains the rows of the grid together as if it is one list."""
197
196
  return itertools.chain(*self._grid)
198
197
 
199
198
  def coord_iter(self) -> Iterator[tuple[GridContent, Coordinate]]:
@@ -209,8 +208,7 @@ class _Grid:
209
208
  include_center: bool = False,
210
209
  radius: int = 1,
211
210
  ) -> Iterator[Coordinate]:
212
- """Return an iterator over cell coordinates that are in the
213
- neighborhood of a certain point.
211
+ """Return an iterator over cell coordinates that are in the neighborhood of a certain point.
214
212
 
215
213
  Args:
216
214
  pos: Coordinate tuple for the neighborhood to get.
@@ -237,8 +235,7 @@ class _Grid:
237
235
  include_center: bool = False,
238
236
  radius: int = 1,
239
237
  ) -> Sequence[Coordinate]:
240
- """Return a list of cells that are in the neighborhood of a
241
- certain point.
238
+ """Return a list of cells that are in the neighborhood of a certain point.
242
239
 
243
240
  Args:
244
241
  pos: Coordinate tuple for the neighborhood to get.
@@ -381,8 +378,7 @@ class _Grid:
381
378
  return pos[0] % self.width, pos[1] % self.height
382
379
 
383
380
  def out_of_bounds(self, pos: Coordinate) -> bool:
384
- """Determines whether position is off the grid, returns the out of
385
- bounds coordinate."""
381
+ """Determines whether position is off the grid, returns the out of bounds coordinate."""
386
382
  x, y = pos
387
383
  return x < 0 or x >= self.width or y < 0 or y >= self.height
388
384
 
@@ -390,8 +386,7 @@ class _Grid:
390
386
  def iter_cell_list_contents(
391
387
  self, cell_list: Iterable[Coordinate]
392
388
  ) -> Iterator[Agent]:
393
- """Returns an iterator of the agents contained in the cells identified
394
- in `cell_list`; cells with empty content are excluded.
389
+ """Returns an iterator of the agents contained in the cells identified in `cell_list`; cells with empty content are excluded.
395
390
 
396
391
  Args:
397
392
  cell_list: Array-like of (x, y) tuples, or single tuple.
@@ -407,8 +402,7 @@ class _Grid:
407
402
 
408
403
  @accept_tuple_argument
409
404
  def get_cell_list_contents(self, cell_list: Iterable[Coordinate]) -> list[Agent]:
410
- """Returns an iterator of the agents contained in the cells identified
411
- in `cell_list`; cells with empty content are excluded.
405
+ """Returns an iterator of the agents contained in the cells identified in `cell_list`; cells with empty content are excluded.
412
406
 
413
407
  Args:
414
408
  cell_list: Array-like of (x, y) tuples, or single tuple.
@@ -441,8 +435,7 @@ class _Grid:
441
435
  selection: str = "random",
442
436
  handle_empty: str | None = None,
443
437
  ) -> None:
444
- """
445
- Move an agent to one of the given positions.
438
+ """Move an agent to one of the given positions.
446
439
 
447
440
  Args:
448
441
  agent: Agent object to move. Assumed to have its current location stored in a 'pos' tuple.
@@ -459,15 +452,21 @@ class _Grid:
459
452
  elif selection == "closest":
460
453
  current_pos = agent.pos
461
454
  # Find the closest position without sorting all positions
462
- closest_pos = None
455
+ # TODO: See if this method can be optimized further
456
+ closest_pos = []
463
457
  min_distance = float("inf")
464
458
  agent.random.shuffle(pos)
465
459
  for p in pos:
466
460
  distance = self._distance_squared(p, current_pos)
467
461
  if distance < min_distance:
468
462
  min_distance = distance
469
- closest_pos = p
470
- chosen_pos = closest_pos
463
+ closest_pos.clear()
464
+ closest_pos.append(p)
465
+ elif distance == min_distance:
466
+ closest_pos.append(p)
467
+
468
+ chosen_pos = agent.random.choice(closest_pos)
469
+
471
470
  else:
472
471
  raise ValueError(
473
472
  f"Invalid selection method {selection}. Choose 'random' or 'closest'."
@@ -488,9 +487,7 @@ class _Grid:
488
487
  )
489
488
 
490
489
  def _distance_squared(self, pos1: Coordinate, pos2: Coordinate) -> float:
491
- """
492
- Calculate the squared Euclidean distance between two points for performance.
493
- """
490
+ """Calculate the squared Euclidean distance between two points for performance."""
494
491
  # Use squared Euclidean distance to avoid sqrt operation
495
492
  dx, dy = abs(pos1[0] - pos2[0]), abs(pos1[1] - pos2[1])
496
493
  if self.torus:
@@ -499,7 +496,7 @@ class _Grid:
499
496
  return dx**2 + dy**2
500
497
 
501
498
  def swap_pos(self, agent_a: Agent, agent_b: Agent) -> None:
502
- """Swap agents positions"""
499
+ """Swap agents positions."""
503
500
  agents_no_pos = []
504
501
  if (pos_a := agent_a.pos) is None:
505
502
  agents_no_pos.append(agent_a)
@@ -560,16 +557,16 @@ def is_single_argument_function(function):
560
557
  )
561
558
 
562
559
 
563
- def ufunc_requires_additional_input(ufunc):
560
+ def ufunc_requires_additional_input(ufunc): # noqa: D103
564
561
  # NumPy ufuncs have a 'nargs' attribute indicating the number of input arguments
565
562
  # For binary ufuncs (like np.add), nargs is 2
566
563
  return ufunc.nargs > 1
567
564
 
568
565
 
569
566
  class PropertyLayer:
570
- """
571
- A class representing a layer of properties in a two-dimensional grid. Each cell in the grid
572
- can store a value of a specified data type.
567
+ """A class representing a layer of properties in a two-dimensional grid.
568
+
569
+ Each cell in the grid can store a value of a specified data type.
573
570
 
574
571
  Attributes:
575
572
  name (str): The name of the property layer.
@@ -577,13 +574,6 @@ class PropertyLayer:
577
574
  height (int): The height of the grid (number of rows).
578
575
  data (numpy.ndarray): A NumPy array representing the grid data.
579
576
 
580
- Methods:
581
- set_cell(position, value): Sets the value of a single cell.
582
- set_cells(value, condition=None): Sets the values of multiple cells, optionally based on a condition.
583
- modify_cell(position, operation, value): Modifies the value of a single cell using an operation.
584
- modify_cells(operation, value, condition_function): Modifies the values of multiple cells using an operation.
585
- select_cells(condition, return_list): Selects cells that meet a specified condition.
586
- aggregate_property(operation): Performs an aggregate operation over all cells.
587
577
  """
588
578
 
589
579
  propertylayer_experimental_warning_given = False
@@ -591,8 +581,7 @@ class PropertyLayer:
591
581
  def __init__(
592
582
  self, name: str, width: int, height: int, default_value, dtype=np.float64
593
583
  ):
594
- """
595
- Initializes a new PropertyLayer instance.
584
+ """Initializes a new PropertyLayer instance.
596
585
 
597
586
  Args:
598
587
  name (str): The name of the property layer.
@@ -643,14 +632,11 @@ class PropertyLayer:
643
632
  self.__class__.propertylayer_experimental_warning_given = True
644
633
 
645
634
  def set_cell(self, position: Coordinate, value):
646
- """
647
- Update a single cell's value in-place.
648
- """
635
+ """Update a single cell's value in-place."""
649
636
  self.data[position] = value
650
637
 
651
638
  def set_cells(self, value, condition=None):
652
- """
653
- Perform a batch update either on the entire grid or conditionally, in-place.
639
+ """Perform a batch update either on the entire grid or conditionally, in-place.
654
640
 
655
641
  Args:
656
642
  value: The value to be used for the update.
@@ -679,8 +665,8 @@ class PropertyLayer:
679
665
  np.copyto(self.data, value, where=condition_result)
680
666
 
681
667
  def modify_cell(self, position: Coordinate, operation, value=None):
682
- """
683
- Modify a single cell using an operation, which can be a lambda function or a NumPy ufunc.
668
+ """Modify a single cell using an operation, which can be a lambda function or a NumPy ufunc.
669
+
684
670
  If a NumPy ufunc is used, an additional value should be provided.
685
671
 
686
672
  Args:
@@ -701,8 +687,8 @@ class PropertyLayer:
701
687
  raise ValueError("Invalid operation or missing value for NumPy ufunc.")
702
688
 
703
689
  def modify_cells(self, operation, value=None, condition_function=None):
704
- """
705
- Modify cells using an operation, which can be a lambda function or a NumPy ufunc.
690
+ """Modify cells using an operation, which can be a lambda function or a NumPy ufunc.
691
+
706
692
  If a NumPy ufunc is used, an additional value should be provided.
707
693
 
708
694
  Args:
@@ -736,8 +722,7 @@ class PropertyLayer:
736
722
  self.data = np.where(condition_array, modified_data, self.data)
737
723
 
738
724
  def select_cells(self, condition, return_list=True):
739
- """
740
- Find cells that meet a specified condition using NumPy's boolean indexing, in-place.
725
+ """Find cells that meet a specified condition using NumPy's boolean indexing, in-place.
741
726
 
742
727
  Args:
743
728
  condition: A callable that returns a boolean array when applied to the data.
@@ -762,9 +747,9 @@ class PropertyLayer:
762
747
 
763
748
 
764
749
  class _PropertyGrid(_Grid):
765
- """
766
- A private subclass of _Grid that supports the addition of property layers, enabling
767
- the representation and manipulation of additional data layers on the grid. This class is
750
+ """A private subclass of _Grid that supports the addition of property layers.
751
+
752
+ This enables the representation and manipulation of additional data layers on the grid. This class is
768
753
  intended for internal use within the Mesa framework and is currently utilized by SingleGrid
769
754
  and MultiGrid classes to provide enhanced grid functionality.
770
755
 
@@ -777,22 +762,15 @@ class _PropertyGrid(_Grid):
777
762
  properties (dict): A dictionary mapping property layer names to PropertyLayer instances.
778
763
  empty_mask (np.ndarray): A boolean array indicating empty cells on the grid.
779
764
 
780
- Methods:
781
- add_property_layer(property_layer): Adds a new property layer to the grid.
782
- remove_property_layer(property_name): Removes a property layer from the grid by its name.
783
- get_neighborhood_mask(pos, moore, include_center, radius): Generates a boolean mask of the neighborhood.
784
- select_cells(conditions, extreme_values, masks, only_empty, return_list): Selects cells based on multiple conditions,
785
- extreme values, masks, with an option to select only empty cells, returning either a list of coordinates or a mask.
786
-
787
- Mask Usage:
788
- Several methods in this class accept a mask as an input, which is a NumPy ndarray of boolean values. This mask
789
- specifies the cells to be considered (True) or ignored (False) in operations. Users can create custom masks,
790
- including neighborhood masks, to apply specific conditions or constraints. Additionally, methods that deal with
791
- cell selection or agent movement can return either a list of cell coordinates or a mask, based on the 'return_list'
792
- parameter. This flexibility allows for more nuanced control and customization of grid operations, catering to a wide
793
- range of modeling requirements and scenarios.
794
-
795
- Note:
765
+
766
+ Several methods in this class accept a mask as an input, which is a NumPy ndarray of boolean values. This mask
767
+ specifies the cells to be considered (True) or ignored (False) in operations. Users can create custom masks,
768
+ including neighborhood masks, to apply specific conditions or constraints. Additionally, methods that deal with
769
+ cell selection or agent movement can return either a list of cell coordinates or a mask, based on the 'return_list'
770
+ parameter. This flexibility allows for more nuanced control and customization of grid operations, catering to a wide
771
+ range of modeling requirements and scenarios.
772
+
773
+ Notes:
796
774
  This class is not intended for direct use in user models but is currently used by the SingleGrid and MultiGrid.
797
775
  """
798
776
 
@@ -803,8 +781,7 @@ class _PropertyGrid(_Grid):
803
781
  torus: bool,
804
782
  property_layers: None | PropertyLayer | list[PropertyLayer] = None,
805
783
  ):
806
- """
807
- Initializes a new _PropertyGrid instance with specified dimensions and optional property layers.
784
+ """Initializes a new _PropertyGrid instance with specified dimensions and optional property layers.
808
785
 
809
786
  Args:
810
787
  width (int): The width of the grid (number of columns).
@@ -833,15 +810,12 @@ class _PropertyGrid(_Grid):
833
810
 
834
811
  @property
835
812
  def empty_mask(self) -> np.ndarray:
836
- """
837
- Returns a boolean mask indicating empty cells on the grid.
838
- """
813
+ """Returns a boolean mask indicating empty cells on the grid."""
839
814
  return self._empty_mask
840
815
 
841
816
  # Add and remove properties to the grid
842
817
  def add_property_layer(self, property_layer: PropertyLayer):
843
- """
844
- Adds a new property layer to the grid.
818
+ """Adds a new property layer to the grid.
845
819
 
846
820
  Args:
847
821
  property_layer (PropertyLayer): The PropertyLayer instance to be added to the grid.
@@ -859,8 +833,7 @@ class _PropertyGrid(_Grid):
859
833
  self.properties[property_layer.name] = property_layer
860
834
 
861
835
  def remove_property_layer(self, property_name: str):
862
- """
863
- Removes a property layer from the grid by its name.
836
+ """Removes a property layer from the grid by its name.
864
837
 
865
838
  Args:
866
839
  property_name (str): The name of the property layer to be removed.
@@ -875,8 +848,8 @@ class _PropertyGrid(_Grid):
875
848
  def get_neighborhood_mask(
876
849
  self, pos: Coordinate, moore: bool, include_center: bool, radius: int
877
850
  ) -> np.ndarray:
878
- """
879
- Generate a boolean mask representing the neighborhood.
851
+ """Generate a boolean mask representing the neighborhood.
852
+
880
853
  Helper method for select_cells_multi_properties() and move_agent_to_random_cell()
881
854
 
882
855
  Args:
@@ -904,8 +877,7 @@ class _PropertyGrid(_Grid):
904
877
  only_empty: bool = False,
905
878
  return_list: bool = True,
906
879
  ) -> list[Coordinate] | np.ndarray:
907
- """
908
- Select cells based on property conditions, extreme values, and/or masks, with an option to only select empty cells.
880
+ """Select cells based on property conditions, extreme values, and/or masks, with an option to only select empty cells.
909
881
 
910
882
  Args:
911
883
  conditions (dict): A dictionary where keys are property names and values are callables that return a boolean when applied.
@@ -1058,7 +1030,7 @@ class MultiGrid(_PropertyGrid):
1058
1030
  self._empty_mask[agent.pos] = False
1059
1031
  agent.pos = None
1060
1032
 
1061
- def iter_neighbors(
1033
+ def iter_neighbors( # noqa: D102
1062
1034
  self,
1063
1035
  pos: Coordinate,
1064
1036
  moore: bool,
@@ -1073,8 +1045,9 @@ class MultiGrid(_PropertyGrid):
1073
1045
  def iter_cell_list_contents(
1074
1046
  self, cell_list: Iterable[Coordinate]
1075
1047
  ) -> Iterator[Agent]:
1076
- """Returns an iterator of the agents contained in the cells identified
1077
- in `cell_list`; cells with empty content are excluded.
1048
+ """Returns an iterator of the agents contained in the cells identified in `cell_list`.
1049
+
1050
+ Cells with empty content are excluded.
1078
1051
 
1079
1052
  Args:
1080
1053
  cell_list: Array-like of (x, y) tuples, or single tuple.
@@ -1112,9 +1085,9 @@ class _HexGrid:
1112
1085
  def get_neighborhood(
1113
1086
  self, pos: Coordinate, include_center: bool = False, radius: int = 1
1114
1087
  ) -> list[Coordinate]:
1115
- """Return a list of coordinates that are in the
1116
- neighborhood of a certain point. To calculate the neighborhood
1117
- for a HexGrid the parity of the x coordinate of the point is
1088
+ """Return a list of coordinates that are in the neighborhood of a certain point.
1089
+
1090
+ To calculate the neighborhood for a HexGrid the parity of the x coordinate of the point is
1118
1091
  important, the neighborhood can be sketched as:
1119
1092
 
1120
1093
  Always: (0,-), (0,+)
@@ -1200,8 +1173,7 @@ class _HexGrid:
1200
1173
  def iter_neighborhood(
1201
1174
  self, pos: Coordinate, include_center: bool = False, radius: int = 1
1202
1175
  ) -> Iterator[Coordinate]:
1203
- """Return an iterator over cell coordinates that are in the
1204
- neighborhood of a certain point.
1176
+ """Return an iterator over cell coordinates that are in the neighborhood of a certain point.
1205
1177
 
1206
1178
  Args:
1207
1179
  pos: Coordinate tuple for the neighborhood to get.
@@ -1251,8 +1223,7 @@ class _HexGrid:
1251
1223
 
1252
1224
 
1253
1225
  class HexSingleGrid(_HexGrid, SingleGrid):
1254
- """Hexagonal SingleGrid: a SingleGrid where neighbors are computed
1255
- according to a hexagonal tiling of the grid.
1226
+ """Hexagonal SingleGrid: a SingleGrid where neighbors are computed according to a hexagonal tiling of the grid.
1256
1227
 
1257
1228
  Functions according to odd-q rules.
1258
1229
  See http://www.redblobgames.com/grids/hexagons/#coordinates for more.
@@ -1268,8 +1239,7 @@ class HexSingleGrid(_HexGrid, SingleGrid):
1268
1239
 
1269
1240
 
1270
1241
  class HexMultiGrid(_HexGrid, MultiGrid):
1271
- """Hexagonal MultiGrid: a MultiGrid where neighbors are computed
1272
- according to a hexagonal tiling of the grid.
1242
+ """Hexagonal MultiGrid: a MultiGrid where neighbors are computed according to a hexagonal tiling of the grid.
1273
1243
 
1274
1244
  Functions according to odd-q rules.
1275
1245
  See http://www.redblobgames.com/grids/hexagons/#coordinates for more.
@@ -1287,8 +1257,7 @@ class HexMultiGrid(_HexGrid, MultiGrid):
1287
1257
 
1288
1258
 
1289
1259
  class HexGrid(HexSingleGrid):
1290
- """Hexagonal Grid: a Grid where neighbors are computed
1291
- according to a hexagonal tiling of the grid.
1260
+ """Hexagonal Grid: a Grid where neighbors are computed according to a hexagonal tiling of the grid.
1292
1261
 
1293
1262
  Functions according to odd-q rules.
1294
1263
  See http://www.redblobgames.com/grids/hexagons/#coordinates for more.
@@ -1299,6 +1268,13 @@ class HexGrid(HexSingleGrid):
1299
1268
  """
1300
1269
 
1301
1270
  def __init__(self, width: int, height: int, torus: bool) -> None:
1271
+ """Initializes a HexGrid, deprecated.
1272
+
1273
+ Args:
1274
+ width: the width of the grid
1275
+ height: the height of the grid
1276
+ torus: whether the grid wraps
1277
+ """
1302
1278
  super().__init__(width, height, torus)
1303
1279
  warn(
1304
1280
  (
@@ -1335,11 +1311,13 @@ class ContinuousSpace:
1335
1311
  """Create a new continuous space.
1336
1312
 
1337
1313
  Args:
1338
- x_max, y_max: Maximum x and y coordinates for the space.
1314
+ x_max: the maximum x-coordinate
1315
+ y_max: the maximum y-coordinate.
1339
1316
  torus: Boolean for whether the edges loop around.
1340
- x_min, y_min: (default 0) If provided, set the minimum x and y
1341
- coordinates for the space. Below them, values loop to
1342
- the other edge (if torus=True) or raise an exception.
1317
+ x_min: (default 0) If provided, set the minimum x -coordinate for the space. Below them, values loop to
1318
+ the other edge (if torus=True) or raise an exception.
1319
+ y_min: (default 0) If provided, set the minimum y -coordinate for the space. Below them, values loop to
1320
+ the other edge (if torus=True) or raise an exception.
1343
1321
  """
1344
1322
  self.x_min = x_min
1345
1323
  self.x_max = x_max
@@ -1442,11 +1420,13 @@ class ContinuousSpace:
1442
1420
  self, pos_1: FloatCoordinate, pos_2: FloatCoordinate
1443
1421
  ) -> FloatCoordinate:
1444
1422
  """Get the heading vector between two points, accounting for toroidal space.
1423
+
1445
1424
  It is possible to calculate the heading angle by applying the atan2 function to the
1446
1425
  result.
1447
1426
 
1448
1427
  Args:
1449
- pos_1, pos_2: Coordinate tuples for both points.
1428
+ pos_1: Coordinate tuples for both points.
1429
+ pos_2: Coordinate tuples for both points.
1450
1430
  """
1451
1431
  one = np.array(pos_1)
1452
1432
  two = np.array(pos_2)
@@ -1472,7 +1452,8 @@ class ContinuousSpace:
1472
1452
  """Get the distance between two point, accounting for toroidal space.
1473
1453
 
1474
1454
  Args:
1475
- pos_1, pos_2: Coordinate tuples for both points.
1455
+ pos_1: Coordinate tuples for point1.
1456
+ pos_2: Coordinate tuples for point2.
1476
1457
  """
1477
1458
  x1, y1 = pos_1
1478
1459
  x2, y2 = pos_2
@@ -1519,7 +1500,7 @@ class NetworkGrid:
1519
1500
  """Create a new network.
1520
1501
 
1521
1502
  Args:
1522
- G: a NetworkX graph instance.
1503
+ g: a NetworkX graph instance.
1523
1504
  """
1524
1505
  self.G = g
1525
1506
  for node_id in self.G.nodes:
@@ -1539,7 +1520,16 @@ class NetworkGrid:
1539
1520
  def get_neighborhood(
1540
1521
  self, node_id: int, include_center: bool = False, radius: int = 1
1541
1522
  ) -> list[int]:
1542
- """Get all adjacent nodes within a certain radius"""
1523
+ """Get all adjacent nodes within a certain radius.
1524
+
1525
+ Args:
1526
+ node_id: node id for which to get neighborhood
1527
+ include_center: boolean to include node itself or not
1528
+ radius: size of neighborhood
1529
+
1530
+ Returns:
1531
+ a list
1532
+ """
1543
1533
  if radius == 1:
1544
1534
  neighborhood = list(self.G.neighbors(node_id))
1545
1535
  if include_center:
@@ -1556,28 +1546,61 @@ class NetworkGrid:
1556
1546
  def get_neighbors(
1557
1547
  self, node_id: int, include_center: bool = False, radius: int = 1
1558
1548
  ) -> list[Agent]:
1559
- """Get all agents in adjacent nodes (within a certain radius)."""
1549
+ """Get all agents in adjacent nodes (within a certain radius).
1550
+
1551
+ Args:
1552
+ node_id: node id for which to get neighbors
1553
+ include_center: whether to include node itself or not
1554
+ radius: size of neighborhood in which to find neighbors
1555
+
1556
+ Returns:
1557
+ list of agents in neighborhood.
1558
+ """
1560
1559
  neighborhood = self.get_neighborhood(node_id, include_center, radius)
1561
1560
  return self.get_cell_list_contents(neighborhood)
1562
1561
 
1563
1562
  def move_agent(self, agent: Agent, node_id: int) -> None:
1564
- """Move an agent from its current node to a new node."""
1563
+ """Move an agent from its current node to a new node.
1564
+
1565
+ Args:
1566
+ agent: agent instance
1567
+ node_id: id of node
1568
+
1569
+ """
1565
1570
  self.remove_agent(agent)
1566
1571
  self.place_agent(agent, node_id)
1567
1572
 
1568
1573
  def remove_agent(self, agent: Agent) -> None:
1569
- """Remove the agent from the network and set its pos attribute to None."""
1574
+ """Remove the agent from the network and set its pos attribute to None.
1575
+
1576
+ Args:
1577
+ agent: agent instance
1578
+
1579
+ """
1570
1580
  node_id = agent.pos
1571
1581
  self.G.nodes[node_id]["agent"].remove(agent)
1572
1582
  agent.pos = None
1573
1583
 
1574
1584
  def is_cell_empty(self, node_id: int) -> bool:
1575
- """Returns a bool of the contents of a cell."""
1585
+ """Returns a bool of the contents of a cell.
1586
+
1587
+ Args:
1588
+ node_id: id of node
1589
+
1590
+ """
1576
1591
  return self.G.nodes[node_id]["agent"] == self.default_val()
1577
1592
 
1578
1593
  def get_cell_list_contents(self, cell_list: list[int]) -> list[Agent]:
1579
- """Returns a list of the agents contained in the nodes identified
1580
- in `cell_list`; nodes with empty content are excluded.
1594
+ """Returns a list of the agents contained in the nodes identified in `cell_list`.
1595
+
1596
+ Nodes with empty content are excluded.
1597
+
1598
+ Args:
1599
+ cell_list: list of cell ids.
1600
+
1601
+ Returns:
1602
+ list of the agents contained in the nodes identified in `cell_list`.
1603
+
1581
1604
  """
1582
1605
  return list(self.iter_cell_list_contents(cell_list))
1583
1606
 
@@ -1586,8 +1609,16 @@ class NetworkGrid:
1586
1609
  return self.get_cell_list_contents(self.G)
1587
1610
 
1588
1611
  def iter_cell_list_contents(self, cell_list: list[int]) -> Iterator[Agent]:
1589
- """Returns an iterator of the agents contained in the nodes identified
1590
- in `cell_list`; nodes with empty content are excluded.
1612
+ """Returns an iterator of the agents contained in the nodes identified in `cell_list`.
1613
+
1614
+ Nodes with empty content are excluded.
1615
+
1616
+ Args:
1617
+ cell_list: list of cell ids.
1618
+
1619
+ Returns:
1620
+ iterator of the agents contained in the nodes identified in `cell_list`.
1621
+
1591
1622
  """
1592
1623
  return itertools.chain.from_iterable(
1593
1624
  self.G.nodes[node_id]["agent"]