From e641683c14ba87218d28e1b06e7ef92b52207481 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Wed, 16 Oct 2024 19:54:32 +0200 Subject: [PATCH 1/4] bugfix --- mesa/experimental/cell_space/cell.py | 9 +++++ .../experimental/cell_space/discrete_space.py | 8 +++- mesa/experimental/cell_space/network.py | 3 ++ tests/test_cell_space.py | 40 +++++++++++++++++++ 4 files changed, 59 insertions(+), 1 deletion(-) diff --git a/mesa/experimental/cell_space/cell.py b/mesa/experimental/cell_space/cell.py index 6c92afbe162..a4235c9cafd 100644 --- a/mesa/experimental/cell_space/cell.py +++ b/mesa/experimental/cell_space/cell.py @@ -211,3 +211,12 @@ def modify_property( self._mesa_property_layers[property_name].modify_cell( self.coordinate, operation, value ) + + + def __getstate__(self): + """Return state of the Cell with connections set to empty.""" + state = super().__getstate__() + state[1][ + "connections" + ] = {} # replace this with empty connections to avoid infinite recursion error in pickle/deepcopy + return state diff --git a/mesa/experimental/cell_space/discrete_space.py b/mesa/experimental/cell_space/discrete_space.py index 1cfaedeb167..c084e105fc1 100644 --- a/mesa/experimental/cell_space/discrete_space.py +++ b/mesa/experimental/cell_space/discrete_space.py @@ -22,7 +22,7 @@ class DiscreteSpace(Generic[T]): all_cells (CellCollection): The cells composing the discrete space random (Random): The random number generator cell_klass (Type) : the type of cell class - empties (CellCollection) : collecction of all cells that are empty + empties (CellCollection) : collection of all cells that are empty property_layers (dict[str, PropertyLayer]): the property layers of the discrete space """ @@ -55,6 +55,7 @@ def __init__( def cutoff_empties(self): # noqa return 7.953 * len(self._cells) ** 0.384 + def _connect_cells(self):... def _connect_single_cell(self, cell: T): ... @cached_property @@ -134,3 +135,8 @@ def modify_properties( condition: a function that takes a cell and returns a boolean (used to filter cells) """ self.property_layers[property_name].modify_cells(operation, value, condition) + + def __setstate__(self, state): + """Set the state of the discrete space and rebuild the connections.""" + self.__dict__ = state + self._connect_cells() diff --git a/mesa/experimental/cell_space/network.py b/mesa/experimental/cell_space/network.py index 551305267af..1d49f2ee1e5 100644 --- a/mesa/experimental/cell_space/network.py +++ b/mesa/experimental/cell_space/network.py @@ -34,6 +34,9 @@ def __init__( node_id, capacity, random=self.random ) + self._connect_cells() + + def _connect_cells(self) -> None: for cell in self.all_cells: self._connect_single_cell(cell) diff --git a/tests/test_cell_space.py b/tests/test_cell_space.py index 4d52e159045..7a13a736bae 100644 --- a/tests/test_cell_space.py +++ b/tests/test_cell_space.py @@ -691,3 +691,43 @@ def test_patch(): # noqa: D103 agent.remove() assert agent not in model._agents + + +def test_copying_discrete_spaces(): # noqa: D103 + # inspired by #2373 + # we use deepcopy but this also applies to pickle + import copy + + import networkx as nx + + grid = OrthogonalMooreGrid((100, 100)) + grid_copy = copy.deepcopy(grid) + + c1 = grid[(5,5)].connections + c2 = grid_copy[(5,5)].connections + + for c1, c2 in zip(grid.all_cells, grid_copy.all_cells): + for k, v in c1.connections.items(): + assert v.coordinate == c2.connections[k].coordinate + + n = 10 + m = 20 + seed = 42 + G = nx.gnm_random_graph(n, m, seed=seed) # noqa: N806 + grid = Network(G) + grid_copy = copy.deepcopy(grid) + + for c1, c2 in zip(grid.all_cells, grid_copy.all_cells): + for k, v in c1.connections.items(): + assert v.coordinate == c2.connections[k].coordinate + + + grid = HexGrid((100, 100)) + grid_copy = copy.deepcopy(grid) + + c1 = grid[(5,5)].connections + c2 = grid_copy[(5,5)].connections + + for c1, c2 in zip(grid.all_cells, grid_copy.all_cells): + for k, v in c1.connections.items(): + assert v.coordinate == c2.connections[k].coordinate From b0f99d811a0f45cf2c5ce2a6865318c2540654d6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 16 Oct 2024 17:58:32 +0000 Subject: [PATCH 2/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/experimental/cell_space/cell.py | 1 - mesa/experimental/cell_space/discrete_space.py | 2 +- tests/test_cell_space.py | 9 ++++----- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/mesa/experimental/cell_space/cell.py b/mesa/experimental/cell_space/cell.py index a4235c9cafd..e2c4a3721bf 100644 --- a/mesa/experimental/cell_space/cell.py +++ b/mesa/experimental/cell_space/cell.py @@ -212,7 +212,6 @@ def modify_property( self.coordinate, operation, value ) - def __getstate__(self): """Return state of the Cell with connections set to empty.""" state = super().__getstate__() diff --git a/mesa/experimental/cell_space/discrete_space.py b/mesa/experimental/cell_space/discrete_space.py index c084e105fc1..7ed06a84aae 100644 --- a/mesa/experimental/cell_space/discrete_space.py +++ b/mesa/experimental/cell_space/discrete_space.py @@ -55,7 +55,7 @@ def __init__( def cutoff_empties(self): # noqa return 7.953 * len(self._cells) ** 0.384 - def _connect_cells(self):... + def _connect_cells(self): ... def _connect_single_cell(self, cell: T): ... @cached_property diff --git a/tests/test_cell_space.py b/tests/test_cell_space.py index 7a13a736bae..fd6f946902f 100644 --- a/tests/test_cell_space.py +++ b/tests/test_cell_space.py @@ -703,8 +703,8 @@ def test_copying_discrete_spaces(): # noqa: D103 grid = OrthogonalMooreGrid((100, 100)) grid_copy = copy.deepcopy(grid) - c1 = grid[(5,5)].connections - c2 = grid_copy[(5,5)].connections + c1 = grid[(5, 5)].connections + c2 = grid_copy[(5, 5)].connections for c1, c2 in zip(grid.all_cells, grid_copy.all_cells): for k, v in c1.connections.items(): @@ -721,12 +721,11 @@ def test_copying_discrete_spaces(): # noqa: D103 for k, v in c1.connections.items(): assert v.coordinate == c2.connections[k].coordinate - grid = HexGrid((100, 100)) grid_copy = copy.deepcopy(grid) - c1 = grid[(5,5)].connections - c2 = grid_copy[(5,5)].connections + c1 = grid[(5, 5)].connections + c2 = grid_copy[(5, 5)].connections for c1, c2 in zip(grid.all_cells, grid_copy.all_cells): for k, v in c1.connections.items(): From 6c93cdc1acbba6875e24ce9ed1df32bdb423a29e Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Wed, 16 Oct 2024 20:30:49 +0200 Subject: [PATCH 3/4] fix for 3.10 --- mesa/experimental/cell_space/cell.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mesa/experimental/cell_space/cell.py b/mesa/experimental/cell_space/cell.py index e2c4a3721bf..e031504333e 100644 --- a/mesa/experimental/cell_space/cell.py +++ b/mesa/experimental/cell_space/cell.py @@ -214,7 +214,8 @@ def modify_property( def __getstate__(self): """Return state of the Cell with connections set to empty.""" - state = super().__getstate__() + # fixme, once we shift to 3.11, replace this with super. __getstate__ + state = (self.__dict__, {k:getattr(self, k) for k in self.__slots__}) state[1][ "connections" ] = {} # replace this with empty connections to avoid infinite recursion error in pickle/deepcopy From c50b0531c0f55b7126aaf2b771ec0691f127cd7b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 16 Oct 2024 18:31:15 +0000 Subject: [PATCH 4/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/experimental/cell_space/cell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesa/experimental/cell_space/cell.py b/mesa/experimental/cell_space/cell.py index e031504333e..4bc6e874a64 100644 --- a/mesa/experimental/cell_space/cell.py +++ b/mesa/experimental/cell_space/cell.py @@ -215,7 +215,7 @@ def modify_property( def __getstate__(self): """Return state of the Cell with connections set to empty.""" # fixme, once we shift to 3.11, replace this with super. __getstate__ - state = (self.__dict__, {k:getattr(self, k) for k in self.__slots__}) + state = (self.__dict__, {k: getattr(self, k) for k in self.__slots__}) state[1][ "connections" ] = {} # replace this with empty connections to avoid infinite recursion error in pickle/deepcopy