From 2e47f56fbdae2e338ee0cd830c493c6fefd50df8 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Mon, 30 Dec 2024 20:52:12 +0100 Subject: [PATCH 01/84] initial draft --- .../continuous_space/continuous_space.py | 113 ++++++++++++++++++ .../continuous_space_agents.py | 61 ++++++++++ .../continuous_space/for_development.py | 26 ++++ 3 files changed, 200 insertions(+) create mode 100644 mesa/experimental/continuous_space/continuous_space.py create mode 100644 mesa/experimental/continuous_space/continuous_space_agents.py create mode 100644 mesa/experimental/continuous_space/for_development.py diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py new file mode 100644 index 00000000000..423519c121a --- /dev/null +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -0,0 +1,113 @@ +"""A Continous Space class.""" + + +import warnings +from collections.abc import Sequence +from random import Random + +import numpy as np +from scipy.spatial.distance import cdist + +from mesa.agent import Agent, AgentSet + + +class ContinuousSpace: + """Continuous space where each agent can have an arbitrary position. + + """ + + def __init__( + self, + dimensions: Sequence[Sequence[float]], + torus: bool = False, + random: Random | None = None, + ) -> None: + """Create a new continuous space.""" + if random is None: + warnings.warn( + "Random number generator not specified, this can make models non-reproducible. Please pass a random number generator explicitly", + UserWarning, + stacklevel=2, + ) + random = Random() + self.random = random + + self.dimensions = np.asarray(dimensions) + self.ndims = self.dimensions.shape[0] + self.size = self.dimensions[:, 1] - self.dimensions[:, 0] + self.center = np.sum(self.dimensions, axis=1) / 2 + + self.torus = torus + + + n = 100 + self._agent_positions = np.zeros((n, self.dimensions.shape[0])) + self._positions_in_use = np.zeros((n,), dtype=bool) # effectively a mask over _agent_positions + self._index_to_agent: dict[int, Agent] = {} + self._agent_to_index: dict[Agent, int | None] = {} + + @property + def agents(self) -> AgentSet: + """Return an AgentSet with the agents in the space.""" + return AgentSet(list(self._agent_to_index), random=self.random) + + @property + def agent_positions(self) -> np.ndarray: + """Return the positions of the agents in the space.""" + return self._agent_positions[self._positions_in_use] + + def _get_index_for_agent(self, agent: Agent) -> int: + """Helper method to get the index for the agent. + + This method manages the numpy array with the agent positions and ensuring it is + enlarged if and when needed. + + """ + try: + return self._agent_to_index[agent] + except KeyError: + indices = np.where(self._positions_in_use == False)[0] + + if indices.size: + index = indices[0] + else: + # we are out of space + fraction = 0.2 # we add 20% + n = int(round(fraction * self._agent_positions.shape[0])) + self._agent_positions = np.vstack([self._agent_positions, + np.zeros((n, self.dimensions.shape[0]),)] + ) + self._positions_in_use = np.hstack([self._positions_in_use, + np.zeros((n,), dtype=bool)]) + index = np.where(self._positions_in_use == False)[0][0] + + self._positions_in_use[index] = True + self._agent_to_index[agent] = index + self._index_to_agent[index] = agent + + return index + + def _remove_agent(self, agent: Agent) -> None: + """Remove an agent from the space.""" + index = self._get_index_for_agent(agent) + self._agent_to_index.pop(agent, None) + self._index_to_agent.pop(index, None) + self._positions_in_use[index] = False + + def calculate_distances(self, point): + """Calculate the distance between the point and all agents.""" + if self.torus: + delta = np.abs(point[np.newaxis, :] - self._agent_positions) + delta = np.minimum(delta, 1 - delta) + dists = np.linalg.norm(delta, axis=1) + else: + dists = cdist(point[np.newaxis, :], self._agent_positions) + return dists + + def in_bounds(self, point) -> bool: + """Check if point is inside the bounds of the space.""" + return ((point >= self.dimensions[:, 0]) & (point <= self.dimensions[:, 1])).all() + + def torus_correct(self, point) -> np.ndarray: + """Apply a torus correction to the point.""" + return self.dimensions[:, 0] + np.mod(point - self.dimensions[:, 0], self.size) diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py new file mode 100644 index 00000000000..51dd4788fa8 --- /dev/null +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -0,0 +1,61 @@ +from __future__ import annotations + +from typing import Protocol + +import numpy as np + +from mesa.agent import Agent +from mesa.experimental.continuous_space.continuous_space import ContinuousSpace + + +class HasPositionProtocol(Protocol): + """Protocol for continuous space position holders.""" + + position: np.ndarray + +class ContinuousSpaceAgent(Agent): + __slots__ = ["space", "_mesa_index"] + + @property + def position(self) -> np.ndarray: + return self.space._agent_positions[self._mesa_index] + + @position.setter + def position(self, value: np.ndarray) -> None: + if not self.space.in_bounds(value): + if self.space.torus: + value = self.space.torus_correct(value) + else: + raise ValueError(f"point {value} is outside the bounds of the space") + + self.space._agent_positions[self._mesa_index] = value + + def __init__(self, space: ContinuousSpace, model): + super().__init__(model) + self.space: ContinuousSpace = space + self._mesa_index = self.space._get_index_for_agent(self) + self.position[:] = np.NaN + + def remove(self) -> None: + super().remove() + self.space._remove_agent(self) + self._mesa_index = None + self.space = None + + def get_neigbors_in_radius(self, radius=1): + distances = self.space.calculate_distances(self.position) + indices = np.where(distances < radius)[0] + + # don't forget to remove our self + agents = [self.space._index_to_agent[index] for index in indices if index != self._mesa_index] + return agents + + def get_nearest_neighbors(self, k=1): + distances = self.space.calculate_distances(self.position) + + k += 1 # the distance calculation includes self, with a distance of 0, so we remove this later + indices = np.argpartition(distances, k)[:k] + + # don't forget to remove our self + agents = [self.space._index_to_agent[index] for index in indices if index != self._mesa_index] + return agents diff --git a/mesa/experimental/continuous_space/for_development.py b/mesa/experimental/continuous_space/for_development.py new file mode 100644 index 00000000000..e6e78b5b042 --- /dev/null +++ b/mesa/experimental/continuous_space/for_development.py @@ -0,0 +1,26 @@ + + +# to be removed once further into the development + + +from random import Random + +import numpy as np +from continuous_space import ContinuousSpace +from continuous_space_agents import ContinuousSpaceAgent + +from mesa import Model + +if __name__ == "__main__": + + dimensions = [[0,1], [0,1]] + + model = Model(seed=42) + space = ContinuousSpace(dimensions, random=Random(42), torus=True) + + for _ in range(101): + agent = ContinuousSpaceAgent(space, model, ) + agent.position = [agent.random.random(), agent.random.random()] + agent.position += [1, 0.01] + + print("blaat") From 6ef0b13d801354799f9513fdf4f0622e0957670d Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Mon, 30 Dec 2024 21:11:10 +0100 Subject: [PATCH 02/84] Update continuous_space.py --- mesa/experimental/continuous_space/continuous_space.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 423519c121a..dea37683384 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -97,11 +97,11 @@ def _remove_agent(self, agent: Agent) -> None: def calculate_distances(self, point): """Calculate the distance between the point and all agents.""" if self.torus: - delta = np.abs(point[np.newaxis, :] - self._agent_positions) + delta = np.abs(point[np.newaxis, :] - self.agent_positions) delta = np.minimum(delta, 1 - delta) dists = np.linalg.norm(delta, axis=1) else: - dists = cdist(point[np.newaxis, :], self._agent_positions) + dists = cdist(point[np.newaxis, :], self.agent_positions) return dists def in_bounds(self, point) -> bool: From a8bd211b677edd79542da11fa4579cb01e9bac3a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 30 Dec 2024 20:13:11 +0000 Subject: [PATCH 03/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../continuous_space/continuous_space.py | 32 +++++++++++-------- .../continuous_space_agents.py | 17 +++++++--- .../continuous_space/for_development.py | 11 +++---- 3 files changed, 37 insertions(+), 23 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index dea37683384..3ea195e2d17 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -1,6 +1,5 @@ """A Continous Space class.""" - import warnings from collections.abc import Sequence from random import Random @@ -12,9 +11,7 @@ class ContinuousSpace: - """Continuous space where each agent can have an arbitrary position. - - """ + """Continuous space where each agent can have an arbitrary position.""" def __init__( self, @@ -39,10 +36,11 @@ def __init__( self.torus = torus - n = 100 self._agent_positions = np.zeros((n, self.dimensions.shape[0])) - self._positions_in_use = np.zeros((n,), dtype=bool) # effectively a mask over _agent_positions + self._positions_in_use = np.zeros( + (n,), dtype=bool + ) # effectively a mask over _agent_positions self._index_to_agent: dict[int, Agent] = {} self._agent_to_index: dict[Agent, int | None] = {} @@ -64,7 +62,7 @@ def _get_index_for_agent(self, agent: Agent) -> int: """ try: - return self._agent_to_index[agent] + return self._agent_to_index[agent] except KeyError: indices = np.where(self._positions_in_use == False)[0] @@ -74,11 +72,17 @@ def _get_index_for_agent(self, agent: Agent) -> int: # we are out of space fraction = 0.2 # we add 20% n = int(round(fraction * self._agent_positions.shape[0])) - self._agent_positions = np.vstack([self._agent_positions, - np.zeros((n, self.dimensions.shape[0]),)] - ) - self._positions_in_use = np.hstack([self._positions_in_use, - np.zeros((n,), dtype=bool)]) + self._agent_positions = np.vstack( + [ + self._agent_positions, + np.zeros( + (n, self.dimensions.shape[0]), + ), + ] + ) + self._positions_in_use = np.hstack( + [self._positions_in_use, np.zeros((n,), dtype=bool)] + ) index = np.where(self._positions_in_use == False)[0][0] self._positions_in_use[index] = True @@ -106,7 +110,9 @@ def calculate_distances(self, point): def in_bounds(self, point) -> bool: """Check if point is inside the bounds of the space.""" - return ((point >= self.dimensions[:, 0]) & (point <= self.dimensions[:, 1])).all() + return ( + (point >= self.dimensions[:, 0]) & (point <= self.dimensions[:, 1]) + ).all() def torus_correct(self, point) -> np.ndarray: """Apply a torus correction to the point.""" diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index 51dd4788fa8..68b73bd4e92 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -13,8 +13,9 @@ class HasPositionProtocol(Protocol): position: np.ndarray + class ContinuousSpaceAgent(Agent): - __slots__ = ["space", "_mesa_index"] + __slots__ = ["_mesa_index", "space"] @property def position(self) -> np.ndarray: @@ -24,7 +25,7 @@ def position(self) -> np.ndarray: def position(self, value: np.ndarray) -> None: if not self.space.in_bounds(value): if self.space.torus: - value = self.space.torus_correct(value) + value = self.space.torus_correct(value) else: raise ValueError(f"point {value} is outside the bounds of the space") @@ -47,7 +48,11 @@ def get_neigbors_in_radius(self, radius=1): indices = np.where(distances < radius)[0] # don't forget to remove our self - agents = [self.space._index_to_agent[index] for index in indices if index != self._mesa_index] + agents = [ + self.space._index_to_agent[index] + for index in indices + if index != self._mesa_index + ] return agents def get_nearest_neighbors(self, k=1): @@ -57,5 +62,9 @@ def get_nearest_neighbors(self, k=1): indices = np.argpartition(distances, k)[:k] # don't forget to remove our self - agents = [self.space._index_to_agent[index] for index in indices if index != self._mesa_index] + agents = [ + self.space._index_to_agent[index] + for index in indices + if index != self._mesa_index + ] return agents diff --git a/mesa/experimental/continuous_space/for_development.py b/mesa/experimental/continuous_space/for_development.py index e6e78b5b042..175e6d1ac79 100644 --- a/mesa/experimental/continuous_space/for_development.py +++ b/mesa/experimental/continuous_space/for_development.py @@ -1,25 +1,24 @@ - - # to be removed once further into the development from random import Random -import numpy as np from continuous_space import ContinuousSpace from continuous_space_agents import ContinuousSpaceAgent from mesa import Model if __name__ == "__main__": - - dimensions = [[0,1], [0,1]] + dimensions = [[0, 1], [0, 1]] model = Model(seed=42) space = ContinuousSpace(dimensions, random=Random(42), torus=True) for _ in range(101): - agent = ContinuousSpaceAgent(space, model, ) + agent = ContinuousSpaceAgent( + space, + model, + ) agent.position = [agent.random.random(), agent.random.random()] agent.position += [1, 0.01] From 797e6810a796f1aaa366d061c7883f24aa31c890 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Mon, 30 Dec 2024 21:40:38 +0100 Subject: [PATCH 04/84] ruff --- mesa/experimental/continuous_space/continuous_space.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 3ea195e2d17..ad297263265 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -64,9 +64,9 @@ def _get_index_for_agent(self, agent: Agent) -> int: try: return self._agent_to_index[agent] except KeyError: - indices = np.where(self._positions_in_use == False)[0] + indices = np.where(not self._positions_in_use)[0] - if indices.size: + if indices.size>0: index = indices[0] else: # we are out of space From 27827366b1cae3e91756b202bcc5535994a494c2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 30 Dec 2024 21:25:59 +0000 Subject: [PATCH 05/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/experimental/continuous_space/continuous_space.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index ad297263265..10bcf87fcc8 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -66,7 +66,7 @@ def _get_index_for_agent(self, agent: Agent) -> int: except KeyError: indices = np.where(not self._positions_in_use)[0] - if indices.size>0: + if indices.size > 0: index = indices[0] else: # we are out of space From d0b05ec51f07cc8f07715191ff7e1e29ae1963df Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Mon, 30 Dec 2024 22:26:53 +0100 Subject: [PATCH 06/84] Update continuous_space.py --- mesa/experimental/continuous_space/continuous_space.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 10bcf87fcc8..01344c5af75 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -83,7 +83,7 @@ def _get_index_for_agent(self, agent: Agent) -> int: self._positions_in_use = np.hstack( [self._positions_in_use, np.zeros((n,), dtype=bool)] ) - index = np.where(self._positions_in_use == False)[0][0] + index = np.where(not self._positions_in_use)[0][0] self._positions_in_use[index] = True self._agent_to_index[agent] = index From 611af520b754c8a463db68f00fc4531403f54c32 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Tue, 31 Dec 2024 14:05:59 +0100 Subject: [PATCH 07/84] first tests --- mesa/experimental/__init__.py | 2 +- .../experimental/continuous_space/__init__.py | 4 + .../continuous_space/continuous_space.py | 16 +- .../continuous_space_agents.py | 4 +- tests/test_continuous_space.py | 166 ++++++++++++++++++ 5 files changed, 181 insertions(+), 11 deletions(-) create mode 100644 mesa/experimental/continuous_space/__init__.py create mode 100644 tests/test_continuous_space.py diff --git a/mesa/experimental/__init__.py b/mesa/experimental/__init__.py index 946b2ba53fc..3c6f50b0e28 100644 --- a/mesa/experimental/__init__.py +++ b/mesa/experimental/__init__.py @@ -15,6 +15,6 @@ - Features graduate from experimental status once their APIs are stabilized """ -from mesa.experimental import cell_space, devs, mesa_signals +from mesa.experimental import cell_space, devs, mesa_signals, continuous_space __all__ = ["cell_space", "devs", "mesa_signals"] diff --git a/mesa/experimental/continuous_space/__init__.py b/mesa/experimental/continuous_space/__init__.py new file mode 100644 index 00000000000..46bb85c4830 --- /dev/null +++ b/mesa/experimental/continuous_space/__init__.py @@ -0,0 +1,4 @@ + + +from mesa.experimental.continuous_space.continuous_space import ContinuousSpace +from mesa.experimental.continuous_space.continuous_space_agents import ContinuousSpaceAgent \ No newline at end of file diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 01344c5af75..577b688934c 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -1,4 +1,4 @@ -"""A Continous Space class.""" +"""A Continyous Space class.""" import warnings from collections.abc import Sequence @@ -18,6 +18,7 @@ def __init__( dimensions: Sequence[Sequence[float]], torus: bool = False, random: Random | None = None, + n_agents: int = 100, ) -> None: """Create a new continuous space.""" if random is None: @@ -36,10 +37,9 @@ def __init__( self.torus = torus - n = 100 - self._agent_positions = np.zeros((n, self.dimensions.shape[0])) + self._agent_positions = np.zeros((n_agents, self.dimensions.shape[0]), dtype=float) self._positions_in_use = np.zeros( - (n,), dtype=bool + (n_agents,), dtype=bool ) # effectively a mask over _agent_positions self._index_to_agent: dict[int, Agent] = {} self._agent_to_index: dict[Agent, int | None] = {} @@ -64,13 +64,13 @@ def _get_index_for_agent(self, agent: Agent) -> int: try: return self._agent_to_index[agent] except KeyError: - indices = np.where(not self._positions_in_use)[0] + indices = np.where(~self._positions_in_use)[0] if indices.size > 0: index = indices[0] else: # we are out of space - fraction = 0.2 # we add 20% + fraction = 0.2 # we add 20% Fixme n = int(round(fraction * self._agent_positions.shape[0])) self._agent_positions = np.vstack( [ @@ -83,7 +83,7 @@ def _get_index_for_agent(self, agent: Agent) -> int: self._positions_in_use = np.hstack( [self._positions_in_use, np.zeros((n,), dtype=bool)] ) - index = np.where(not self._positions_in_use)[0][0] + index = np.where(~self._positions_in_use)[0][0] self._positions_in_use[index] = True self._agent_to_index[agent] = index @@ -102,7 +102,7 @@ def calculate_distances(self, point): """Calculate the distance between the point and all agents.""" if self.torus: delta = np.abs(point[np.newaxis, :] - self.agent_positions) - delta = np.minimum(delta, 1 - delta) + delta = np.minimum(delta, 1 - delta) # fixme, should be based on size dists = np.linalg.norm(delta, axis=1) else: dists = cdist(point[np.newaxis, :], self.agent_positions) diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index 68b73bd4e92..1f379c09b17 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -5,7 +5,7 @@ import numpy as np from mesa.agent import Agent -from mesa.experimental.continuous_space.continuous_space import ContinuousSpace +from mesa.experimental.continuous_space import ContinuousSpace class HasPositionProtocol(Protocol): @@ -45,7 +45,7 @@ def remove(self) -> None: def get_neigbors_in_radius(self, radius=1): distances = self.space.calculate_distances(self.position) - indices = np.where(distances < radius)[0] + indices = np.where(distances <= radius)[0] # don't forget to remove our self agents = [ diff --git a/tests/test_continuous_space.py b/tests/test_continuous_space.py new file mode 100644 index 00000000000..19cdc8e95cc --- /dev/null +++ b/tests/test_continuous_space.py @@ -0,0 +1,166 @@ +import numpy as np +import pytest + +from mesa import Model + +from mesa.experimental.continuous_space import ContinuousSpace, ContinuousSpaceAgent + + + +def test_continuous_space(): + model = Model(seed=42) + + dimensions = np.asarray([[0, 1], + [-1, 0]]) + space = ContinuousSpace(dimensions, torus=False, random=model.random) + + # check some default fields + assert space.ndims == 2 + assert np.all(space.size == [1, 1]) + assert np.all(space.center == [0.5, -0.5]) + assert len(space.agents) == 0 + + # check in_bounds + assert space.in_bounds([0.5, -0.5]) + assert not space.in_bounds([-0.5, -0.5]) + assert not space.in_bounds([ 1.5, -0.5]) + assert not space.in_bounds([ 0.5, 0.5]) + assert not space.in_bounds([ 0.5, -1.5]) + + # check torus correction + space = ContinuousSpace(dimensions, torus=True, random=model.random) + assert np.all(space.torus_correct([-0.5, 0.5]) == [0.5, -0.5]) + assert np.all(space.torus_correct([0.5, -0.5]) == [0.5, -0.5]) + assert np.all(space.torus_correct([0.5, -0.5]) == [0.5, -0.5]) + assert np.all(space.torus_correct([1.5, -0.5]) == [0.5, -0.5]) + assert np.all(space.torus_correct([0.5, -1.5]) == [0.5, -0.5]) + + # check 3d + dimensions = np.asarray([[0, 2], + [-2, 0], + [-2, 2]]) + space = ContinuousSpace(dimensions, torus=False, random=model.random) + + # check some default fields + assert space.ndims == 3 + assert np.all(space.size == [2, 2, 4]) + assert np.all(space.center == [1, -1, 0]) + + # check in_bounds + assert space.in_bounds([1, -1, 0]) + assert not space.in_bounds([-0.5, -1, 0]) + assert not space.in_bounds([2.5, -1, 0]) + assert not space.in_bounds([ 1, 0.5, 0]) + assert not space.in_bounds([ 1, -2.5, 0]) + assert not space.in_bounds([ 1, -1, -3]) + assert not space.in_bounds([ 1, -1, 3]) + assert not space.in_bounds([-0.5, -1, 3]) + assert not space.in_bounds([1, 0.5, 3]) + +def test_continuous_agent(): + model = Model(seed=42) + + dimensions = np.asarray([[0, 1], + [0, 1]]) + space = ContinuousSpace(dimensions, torus=False, random=model.random) + + for _ in range(10): + agent = ContinuousSpaceAgent(space, model) + agent.position = [agent.random.random(), agent.random.random()] + + assert space.agent_positions.shape == (10, 2) + for agent in space.agents: + assert np.all(agent.position == space.agent_positions[space._get_index_for_agent(agent)]) + + # add more agents, triggering a resizeing of the array + for _ in range(100): + agent = ContinuousSpaceAgent(space, model) + agent.position = [agent.random.random(), agent.random.random()] + + assert space.agent_positions.shape == (110, 2) + for agent in space.agents: + assert np.all(agent.position == space.agent_positions[space._get_index_for_agent(agent)]) + + # remove all agents and check if the view is updated throughout correctly + for i, agent in enumerate(space.agents): + agent.remove() + assert space.agent_positions.shape == (110-1-i, 2) + + +# class TestSpaceToroidal(unittest.TestCase): +# """Testing a toroidal continuous space.""" +# +# def setUp(self): +# """Create a test space and populate with Mock Agents.""" +# self.space = ContinuousSpace(70, 20, True, -30, -30) +# self.agents = [] +# for i, pos in enumerate(TEST_AGENTS): +# a = MockAgent(i) +# self.agents.append(a) +# self.space.place_agent(a, pos) +# +# def test_agent_positions(self): +# """Ensure that the agents are all placed properly.""" +# for i, pos in enumerate(TEST_AGENTS): +# a = self.agents[i] +# assert a.pos == pos +# +# def test_agent_matching(self): +# """Ensure that the agents are all placed and indexed properly.""" +# for i, agent in self.space._index_to_agent.items(): +# assert agent.pos == tuple(self.space._agent_points[i, :]) +# assert i == self.space._agent_to_index[agent] +# +# def test_distance_calculations(self): +# """Test toroidal distance calculations.""" +# pos_1 = (-30, -30) +# pos_2 = (70, 20) +# assert self.space.get_distance(pos_1, pos_2) == 0 +# +# pos_3 = (-30, -20) +# assert self.space.get_distance(pos_1, pos_3) == 10 +# +# pos_4 = (20, -5) +# pos_5 = (20, -15) +# assert self.space.get_distance(pos_4, pos_5) == 10 +# +# pos_6 = (-30, -29) +# pos_7 = (21, -5) +# assert self.space.get_distance(pos_6, pos_7) == np.sqrt(49**2 + 24**2) +# +# def test_heading(self): # noqa: D102 +# pos_1 = (-30, -30) +# pos_2 = (70, 20) +# self.assertEqual((0, 0), self.space.get_heading(pos_1, pos_2)) +# +# pos_1 = (65, -25) +# pos_2 = (-25, -25) +# self.assertEqual((10, 0), self.space.get_heading(pos_1, pos_2)) +# +# def test_neighborhood_retrieval(self): +# """Test neighborhood retrieval.""" +# neighbors_1 = self.space.get_neighbors((-20, -20), 1) +# assert len(neighbors_1) == 2 +# +# neighbors_2 = self.space.get_neighbors((40, -10), 10) +# assert len(neighbors_2) == 0 +# +# neighbors_3 = self.space.get_neighbors((-30, -30), 10) +# assert len(neighbors_3) == 1 +# +# def test_bounds(self): +# """Test positions outside of boundary.""" +# boundary_agents = [] +# for i, pos in enumerate(OUTSIDE_POSITIONS): +# a = MockAgent(len(self.agents) + i) +# boundary_agents.append(a) +# self.space.place_agent(a, pos) +# +# for a, pos in zip(boundary_agents, OUTSIDE_POSITIONS): +# adj_pos = self.space.torus_adj(pos) +# assert a.pos == adj_pos +# +# a = self.agents[0] +# for pos in OUTSIDE_POSITIONS: +# assert self.space.out_of_bounds(pos) +# self.space.move_agent(a, pos) \ No newline at end of file From 85c0bc2a214786f1a01bb7938342b8b789b0376d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 31 Dec 2024 13:06:10 +0000 Subject: [PATCH 08/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/experimental/__init__.py | 2 +- .../experimental/continuous_space/__init__.py | 6 +-- .../continuous_space/continuous_space.py | 4 +- tests/test_continuous_space.py | 42 +++++++++---------- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/mesa/experimental/__init__.py b/mesa/experimental/__init__.py index 3c6f50b0e28..34111682b15 100644 --- a/mesa/experimental/__init__.py +++ b/mesa/experimental/__init__.py @@ -15,6 +15,6 @@ - Features graduate from experimental status once their APIs are stabilized """ -from mesa.experimental import cell_space, devs, mesa_signals, continuous_space +from mesa.experimental import cell_space, continuous_space, devs, mesa_signals __all__ = ["cell_space", "devs", "mesa_signals"] diff --git a/mesa/experimental/continuous_space/__init__.py b/mesa/experimental/continuous_space/__init__.py index 46bb85c4830..db7d4b39cd3 100644 --- a/mesa/experimental/continuous_space/__init__.py +++ b/mesa/experimental/continuous_space/__init__.py @@ -1,4 +1,4 @@ - - from mesa.experimental.continuous_space.continuous_space import ContinuousSpace -from mesa.experimental.continuous_space.continuous_space_agents import ContinuousSpaceAgent \ No newline at end of file +from mesa.experimental.continuous_space.continuous_space_agents import ( + ContinuousSpaceAgent, +) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 577b688934c..5b225a3decd 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -37,7 +37,9 @@ def __init__( self.torus = torus - self._agent_positions = np.zeros((n_agents, self.dimensions.shape[0]), dtype=float) + self._agent_positions = np.zeros( + (n_agents, self.dimensions.shape[0]), dtype=float + ) self._positions_in_use = np.zeros( (n_agents,), dtype=bool ) # effectively a mask over _agent_positions diff --git a/tests/test_continuous_space.py b/tests/test_continuous_space.py index 19cdc8e95cc..7c9d44f3295 100644 --- a/tests/test_continuous_space.py +++ b/tests/test_continuous_space.py @@ -1,17 +1,13 @@ import numpy as np -import pytest from mesa import Model - from mesa.experimental.continuous_space import ContinuousSpace, ContinuousSpaceAgent - def test_continuous_space(): model = Model(seed=42) - dimensions = np.asarray([[0, 1], - [-1, 0]]) + dimensions = np.asarray([[0, 1], [-1, 0]]) space = ContinuousSpace(dimensions, torus=False, random=model.random) # check some default fields @@ -23,9 +19,9 @@ def test_continuous_space(): # check in_bounds assert space.in_bounds([0.5, -0.5]) assert not space.in_bounds([-0.5, -0.5]) - assert not space.in_bounds([ 1.5, -0.5]) - assert not space.in_bounds([ 0.5, 0.5]) - assert not space.in_bounds([ 0.5, -1.5]) + assert not space.in_bounds([1.5, -0.5]) + assert not space.in_bounds([0.5, 0.5]) + assert not space.in_bounds([0.5, -1.5]) # check torus correction space = ContinuousSpace(dimensions, torus=True, random=model.random) @@ -36,9 +32,7 @@ def test_continuous_space(): assert np.all(space.torus_correct([0.5, -1.5]) == [0.5, -0.5]) # check 3d - dimensions = np.asarray([[0, 2], - [-2, 0], - [-2, 2]]) + dimensions = np.asarray([[0, 2], [-2, 0], [-2, 2]]) space = ContinuousSpace(dimensions, torus=False, random=model.random) # check some default fields @@ -50,18 +44,18 @@ def test_continuous_space(): assert space.in_bounds([1, -1, 0]) assert not space.in_bounds([-0.5, -1, 0]) assert not space.in_bounds([2.5, -1, 0]) - assert not space.in_bounds([ 1, 0.5, 0]) - assert not space.in_bounds([ 1, -2.5, 0]) - assert not space.in_bounds([ 1, -1, -3]) - assert not space.in_bounds([ 1, -1, 3]) + assert not space.in_bounds([1, 0.5, 0]) + assert not space.in_bounds([1, -2.5, 0]) + assert not space.in_bounds([1, -1, -3]) + assert not space.in_bounds([1, -1, 3]) assert not space.in_bounds([-0.5, -1, 3]) assert not space.in_bounds([1, 0.5, 3]) + def test_continuous_agent(): model = Model(seed=42) - dimensions = np.asarray([[0, 1], - [0, 1]]) + dimensions = np.asarray([[0, 1], [0, 1]]) space = ContinuousSpace(dimensions, torus=False, random=model.random) for _ in range(10): @@ -70,7 +64,9 @@ def test_continuous_agent(): assert space.agent_positions.shape == (10, 2) for agent in space.agents: - assert np.all(agent.position == space.agent_positions[space._get_index_for_agent(agent)]) + assert np.all( + agent.position == space.agent_positions[space._get_index_for_agent(agent)] + ) # add more agents, triggering a resizeing of the array for _ in range(100): @@ -79,12 +75,14 @@ def test_continuous_agent(): assert space.agent_positions.shape == (110, 2) for agent in space.agents: - assert np.all(agent.position == space.agent_positions[space._get_index_for_agent(agent)]) + assert np.all( + agent.position == space.agent_positions[space._get_index_for_agent(agent)] + ) # remove all agents and check if the view is updated throughout correctly for i, agent in enumerate(space.agents): agent.remove() - assert space.agent_positions.shape == (110-1-i, 2) + assert space.agent_positions.shape == (110 - 1 - i, 2) # class TestSpaceToroidal(unittest.TestCase): @@ -128,7 +126,7 @@ def test_continuous_agent(): # pos_7 = (21, -5) # assert self.space.get_distance(pos_6, pos_7) == np.sqrt(49**2 + 24**2) # -# def test_heading(self): # noqa: D102 +# def test_heading(self): # pos_1 = (-30, -30) # pos_2 = (70, 20) # self.assertEqual((0, 0), self.space.get_heading(pos_1, pos_2)) @@ -163,4 +161,4 @@ def test_continuous_agent(): # a = self.agents[0] # for pos in OUTSIDE_POSITIONS: # assert self.space.out_of_bounds(pos) -# self.space.move_agent(a, pos) \ No newline at end of file +# self.space.move_agent(a, pos) From 2e31c49826b04bcd1c6a6ffb9d6e36b97310d5b2 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Tue, 31 Dec 2024 14:40:33 +0100 Subject: [PATCH 09/84] docstring for agent --- .../continuous_space_agents.py | 90 ++++++++++++++++--- 1 file changed, 76 insertions(+), 14 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index 1f379c09b17..8c987d2029b 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Protocol +from typing import Protocol, overload import numpy as np @@ -15,10 +15,18 @@ class HasPositionProtocol(Protocol): class ContinuousSpaceAgent(Agent): + """A continuous space agent. + + Attributes: + space (ContinuousSpace): the continuous space in which the agent is located + position (np.ndarray): the position of the agent + + """ __slots__ = ["_mesa_index", "space"] @property def position(self) -> np.ndarray: + """Position of the agent.""" return self.space._agent_positions[self._mesa_index] @position.setter @@ -32,39 +40,93 @@ def position(self, value: np.ndarray) -> None: self.space._agent_positions[self._mesa_index] = value def __init__(self, space: ContinuousSpace, model): + """Initialize a continuous space agent. + + Args: + space: the continuous space in which the agent is located + model: the model to which the agent belongs + + """ super().__init__(model) self.space: ContinuousSpace = space self._mesa_index = self.space._get_index_for_agent(self) self.position[:] = np.NaN def remove(self) -> None: + """Remove and delete the agent from the model.""" super().remove() self.space._remove_agent(self) self._mesa_index = None self.space = None - def get_neigbors_in_radius(self, radius=1): + @overload + def get_neighbors_in_radius(self, radius:int=1, include_distance:bool=False) -> list[ContinuousSpaceAgent]: ... + + @overload + def get_neighbors_in_radius(self, radius=1, include_distance:bool=True) -> list[tuple[ContinuousSpaceAgent, float]]: ... + + + def get_neighbors_in_radius(self, radius=1, include_distance = False): + """Get neighbors within radius. + + Args: + radius: radius within which to look for neighbors + include_distance: include the distance information for each neighbor + + """ distances = self.space.calculate_distances(self.position) indices = np.where(distances <= radius)[0] - # don't forget to remove our self - agents = [ - self.space._index_to_agent[index] - for index in indices - if index != self._mesa_index - ] + if include_distance: + # don't forget to remove our self + agents = [ + (self.space._index_to_agent[index], distances[index]) + for index in indices + if index != self._mesa_index + ] + else: + # don't forget to remove our self + agents = [ + self.space._index_to_agent[index] + for index in indices + if index != self._mesa_index + ] return agents - def get_nearest_neighbors(self, k=1): + @overload + def get_nearest_neighbors(self, k:int=1, include_distance:bool=False) -> list[ContinuousSpaceAgent]: ... + + @overload + def get_nearest_neighbors(self, k=1, include_distance:bool=True) -> list[tuple[ContinuousSpaceAgent, float]]: ... + + + def get_nearest_neighbors(self, k=1, include_distance = False): + """Get neighbors within radius. + + Args: + k: the number of nearest neighbors to return + include_distance: include the distance information for each neighbor + + """ + distances = self.space.calculate_distances(self.position) k += 1 # the distance calculation includes self, with a distance of 0, so we remove this later indices = np.argpartition(distances, k)[:k] # don't forget to remove our self - agents = [ - self.space._index_to_agent[index] - for index in indices - if index != self._mesa_index - ] + if include_distance: + # don't forget to remove our self + agents = [ + (self.space._index_to_agent[index], distances[index]) + for index in indices + if index != self._mesa_index + ] + else: + # don't forget to remove our self + agents = [ + self.space._index_to_agent[index] + for index in indices + if index != self._mesa_index + ] return agents From 322a50e9d42a01a443829b0440fbdd86c7666db2 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Tue, 31 Dec 2024 14:40:50 +0100 Subject: [PATCH 10/84] Update continuous_space_agents.py --- mesa/experimental/continuous_space/continuous_space_agents.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index 8c987d2029b..a19cc2c000e 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -1,3 +1,5 @@ +"""Continuous space agents.""" + from __future__ import annotations from typing import Protocol, overload From aee0ff8afcc1327b686fb104c816f595e0898b9a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 31 Dec 2024 13:41:42 +0000 Subject: [PATCH 11/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../continuous_space_agents.py | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index a19cc2c000e..f361e4f4ed6 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -24,6 +24,7 @@ class ContinuousSpaceAgent(Agent): position (np.ndarray): the position of the agent """ + __slots__ = ["_mesa_index", "space"] @property @@ -62,13 +63,16 @@ def remove(self) -> None: self.space = None @overload - def get_neighbors_in_radius(self, radius:int=1, include_distance:bool=False) -> list[ContinuousSpaceAgent]: ... + def get_neighbors_in_radius( + self, radius: int = 1, include_distance: bool = False + ) -> list[ContinuousSpaceAgent]: ... @overload - def get_neighbors_in_radius(self, radius=1, include_distance:bool=True) -> list[tuple[ContinuousSpaceAgent, float]]: ... - + def get_neighbors_in_radius( + self, radius=1, include_distance: bool = True + ) -> list[tuple[ContinuousSpaceAgent, float]]: ... - def get_neighbors_in_radius(self, radius=1, include_distance = False): + def get_neighbors_in_radius(self, radius=1, include_distance=False): """Get neighbors within radius. Args: @@ -96,13 +100,16 @@ def get_neighbors_in_radius(self, radius=1, include_distance = False): return agents @overload - def get_nearest_neighbors(self, k:int=1, include_distance:bool=False) -> list[ContinuousSpaceAgent]: ... + def get_nearest_neighbors( + self, k: int = 1, include_distance: bool = False + ) -> list[ContinuousSpaceAgent]: ... @overload - def get_nearest_neighbors(self, k=1, include_distance:bool=True) -> list[tuple[ContinuousSpaceAgent, float]]: ... + def get_nearest_neighbors( + self, k=1, include_distance: bool = True + ) -> list[tuple[ContinuousSpaceAgent, float]]: ... - - def get_nearest_neighbors(self, k=1, include_distance = False): + def get_nearest_neighbors(self, k=1, include_distance=False): """Get neighbors within radius. Args: @@ -110,7 +117,6 @@ def get_nearest_neighbors(self, k=1, include_distance = False): include_distance: include the distance information for each neighbor """ - distances = self.space.calculate_distances(self.position) k += 1 # the distance calculation includes self, with a distance of 0, so we remove this later From 85665c5195a3344b6eafc2871f4598d71f275753 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Wed, 1 Jan 2025 15:22:56 +0100 Subject: [PATCH 12/84] add scipy as dependency --- mesa/experimental/continuous_space/continuous_space.py | 4 ++-- .../continuous_space/continuous_space_agents.py | 4 ++++ mesa/experimental/continuous_space/for_development.py | 7 ++++--- pyproject.toml | 1 + 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 5b225a3decd..442564427cb 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -103,8 +103,8 @@ def _remove_agent(self, agent: Agent) -> None: def calculate_distances(self, point): """Calculate the distance between the point and all agents.""" if self.torus: - delta = np.abs(point[np.newaxis, :] - self.agent_positions) - delta = np.minimum(delta, 1 - delta) # fixme, should be based on size + delta = np.abs(point[np.newaxis, :] - self._agent_positions) + delta = np.minimum(delta, 1 - delta) # fixme, should be based on size or maxima? dists = np.linalg.norm(delta, axis=1) else: dists = cdist(point[np.newaxis, :], self.agent_positions) diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index f361e4f4ed6..72d0c156cf6 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -122,6 +122,10 @@ def get_nearest_neighbors(self, k=1, include_distance=False): k += 1 # the distance calculation includes self, with a distance of 0, so we remove this later indices = np.argpartition(distances, k)[:k] + # fixme + # indices mismatch, because it is the index in the distances + # but this is not the same as the indices in agent_positions + # don't forget to remove our self if include_distance: # don't forget to remove our self diff --git a/mesa/experimental/continuous_space/for_development.py b/mesa/experimental/continuous_space/for_development.py index 175e6d1ac79..667a88e293a 100644 --- a/mesa/experimental/continuous_space/for_development.py +++ b/mesa/experimental/continuous_space/for_development.py @@ -1,5 +1,5 @@ # to be removed once further into the development - +import numpy as np from random import Random @@ -14,12 +14,13 @@ model = Model(seed=42) space = ContinuousSpace(dimensions, random=Random(42), torus=True) - for _ in range(101): + for _ in range(2): agent = ContinuousSpaceAgent( space, model, ) agent.position = [agent.random.random(), agent.random.random()] - agent.position += [1, 0.01] + + distances = space.calculate_distances(np.asarray([0.5, 0.5])) print("blaat") diff --git a/pyproject.toml b/pyproject.toml index 51265df9301..db68f3986c6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,6 +38,7 @@ dependencies = [ "numpy", "pandas", "tqdm", + "scipy" ] dynamic = ["version"] From cdbeb2abe906b8f9c482f3cbe677e31fa30461bd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 1 Jan 2025 14:23:13 +0000 Subject: [PATCH 13/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/experimental/continuous_space/continuous_space.py | 4 +++- mesa/experimental/continuous_space/for_development.py | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 442564427cb..73ad0b73df9 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -104,7 +104,9 @@ def calculate_distances(self, point): """Calculate the distance between the point and all agents.""" if self.torus: delta = np.abs(point[np.newaxis, :] - self._agent_positions) - delta = np.minimum(delta, 1 - delta) # fixme, should be based on size or maxima? + delta = np.minimum( + delta, 1 - delta + ) # fixme, should be based on size or maxima? dists = np.linalg.norm(delta, axis=1) else: dists = cdist(point[np.newaxis, :], self.agent_positions) diff --git a/mesa/experimental/continuous_space/for_development.py b/mesa/experimental/continuous_space/for_development.py index 667a88e293a..52704b667ca 100644 --- a/mesa/experimental/continuous_space/for_development.py +++ b/mesa/experimental/continuous_space/for_development.py @@ -1,8 +1,7 @@ # to be removed once further into the development -import numpy as np - from random import Random +import numpy as np from continuous_space import ContinuousSpace from continuous_space_agents import ContinuousSpaceAgent From 04f66f9921dead239a5a0d197354beff87825e02 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Wed, 1 Jan 2025 15:43:46 +0100 Subject: [PATCH 14/84] Update continuous_space_agents.py --- mesa/experimental/continuous_space/continuous_space_agents.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index 72d0c156cf6..e9b64147bfb 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -53,7 +53,7 @@ def __init__(self, space: ContinuousSpace, model): super().__init__(model) self.space: ContinuousSpace = space self._mesa_index = self.space._get_index_for_agent(self) - self.position[:] = np.NaN + self.position[:] = np.nan def remove(self) -> None: """Remove and delete the agent from the model.""" From a2d113efaa566b5fcd0f7c7ff2a39f984e3f96f5 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Wed, 1 Jan 2025 16:14:26 +0100 Subject: [PATCH 15/84] ensure aligntment between agents and distances --- .../continuous_space/continuous_space.py | 9 ++++++--- .../continuous_space/continuous_space_agents.py | 16 ++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 73ad0b73df9..f655b8d5e26 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -40,6 +40,7 @@ def __init__( self._agent_positions = np.zeros( (n_agents, self.dimensions.shape[0]), dtype=float ) + self._agents = np.zeros((n_agents, ), dtype=object) self._positions_in_use = np.zeros( (n_agents,), dtype=bool ) # effectively a mask over _agent_positions @@ -49,7 +50,7 @@ def __init__( @property def agents(self) -> AgentSet: """Return an AgentSet with the agents in the space.""" - return AgentSet(list(self._agent_to_index), random=self.random) + return AgentSet(self._agents[self._positions_in_use], random=self.random) @property def agent_positions(self) -> np.ndarray: @@ -88,6 +89,7 @@ def _get_index_for_agent(self, agent: Agent) -> int: index = np.where(~self._positions_in_use)[0][0] self._positions_in_use[index] = True + self._agents[index] = agent self._agent_to_index[agent] = index self._index_to_agent[index] = agent @@ -99,18 +101,19 @@ def _remove_agent(self, agent: Agent) -> None: self._agent_to_index.pop(agent, None) self._index_to_agent.pop(index, None) self._positions_in_use[index] = False + self._agents[index] = None def calculate_distances(self, point): """Calculate the distance between the point and all agents.""" if self.torus: - delta = np.abs(point[np.newaxis, :] - self._agent_positions) + delta = np.abs(point[np.newaxis, :] - self.agent_positions) delta = np.minimum( delta, 1 - delta ) # fixme, should be based on size or maxima? dists = np.linalg.norm(delta, axis=1) else: dists = cdist(point[np.newaxis, :], self.agent_positions) - return dists + return dists, self._agents[self._positions_in_use] def in_bounds(self, point) -> bool: """Check if point is inside the bounds of the space.""" diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index e9b64147bfb..58f6a1e03d5 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -80,20 +80,20 @@ def get_neighbors_in_radius(self, radius=1, include_distance=False): include_distance: include the distance information for each neighbor """ - distances = self.space.calculate_distances(self.position) - indices = np.where(distances <= radius)[0] + dists, agents = self.space.calculate_distances(self.position) + indices = np.where(dists <= radius)[0] if include_distance: # don't forget to remove our self agents = [ - (self.space._index_to_agent[index], distances[index]) + (agents[index], dists[index]) for index in indices if index != self._mesa_index ] else: # don't forget to remove our self agents = [ - self.space._index_to_agent[index] + agents[index] for index in indices if index != self._mesa_index ] @@ -117,10 +117,10 @@ def get_nearest_neighbors(self, k=1, include_distance=False): include_distance: include the distance information for each neighbor """ - distances = self.space.calculate_distances(self.position) + dists, agents = self.space.calculate_distances(self.position) k += 1 # the distance calculation includes self, with a distance of 0, so we remove this later - indices = np.argpartition(distances, k)[:k] + indices = np.argpartition(dists, k)[:k] # fixme # indices mismatch, because it is the index in the distances @@ -130,14 +130,14 @@ def get_nearest_neighbors(self, k=1, include_distance=False): if include_distance: # don't forget to remove our self agents = [ - (self.space._index_to_agent[index], distances[index]) + (agents[index], dists[index]) for index in indices if index != self._mesa_index ] else: # don't forget to remove our self agents = [ - self.space._index_to_agent[index] + agents[index] for index in indices if index != self._mesa_index ] From 76f3ccd665ed40a29d7e89fab761b1a01d5cdf52 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 1 Jan 2025 15:14:34 +0000 Subject: [PATCH 16/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../continuous_space/continuous_space.py | 2 +- .../continuous_space/continuous_space_agents.py | 12 ++---------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index f655b8d5e26..86c3c99ca2b 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -40,7 +40,7 @@ def __init__( self._agent_positions = np.zeros( (n_agents, self.dimensions.shape[0]), dtype=float ) - self._agents = np.zeros((n_agents, ), dtype=object) + self._agents = np.zeros((n_agents,), dtype=object) self._positions_in_use = np.zeros( (n_agents,), dtype=bool ) # effectively a mask over _agent_positions diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index 58f6a1e03d5..4643e8325c2 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -92,11 +92,7 @@ def get_neighbors_in_radius(self, radius=1, include_distance=False): ] else: # don't forget to remove our self - agents = [ - agents[index] - for index in indices - if index != self._mesa_index - ] + agents = [agents[index] for index in indices if index != self._mesa_index] return agents @overload @@ -136,9 +132,5 @@ def get_nearest_neighbors(self, k=1, include_distance=False): ] else: # don't forget to remove our self - agents = [ - agents[index] - for index in indices - if index != self._mesa_index - ] + agents = [agents[index] for index in indices if index != self._mesa_index] return agents From d8b62b096dd8558e9b432e21752e771ca3f18525 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Wed, 1 Jan 2025 16:31:15 +0100 Subject: [PATCH 17/84] Update continuous_space.py --- mesa/experimental/continuous_space/continuous_space.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 86c3c99ca2b..b1d68b04cb7 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -44,6 +44,7 @@ def __init__( self._positions_in_use = np.zeros( (n_agents,), dtype=bool ) # effectively a mask over _agent_positions + self._index_to_agent: dict[int, Agent] = {} self._agent_to_index: dict[Agent, int | None] = {} @@ -86,6 +87,9 @@ def _get_index_for_agent(self, agent: Agent) -> int: self._positions_in_use = np.hstack( [self._positions_in_use, np.zeros((n,), dtype=bool)] ) + self._agents = np.hstack( + [self._agents, np.zeros((n,), dtype=object)] + ) index = np.where(~self._positions_in_use)[0][0] self._positions_in_use[index] = True From c5ad8bb96f4a0e419a25ed94acd301a7fea95696 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 1 Jan 2025 16:04:26 +0000 Subject: [PATCH 18/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/experimental/continuous_space/continuous_space.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index b1d68b04cb7..5652d1aea9e 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -87,9 +87,7 @@ def _get_index_for_agent(self, agent: Agent) -> int: self._positions_in_use = np.hstack( [self._positions_in_use, np.zeros((n,), dtype=bool)] ) - self._agents = np.hstack( - [self._agents, np.zeros((n,), dtype=object)] - ) + self._agents = np.hstack([self._agents, np.zeros((n,), dtype=object)]) index = np.where(~self._positions_in_use)[0][0] self._positions_in_use[index] = True From 5695c3b71ae07ae49ebbf59da90399732cfc42f4 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Wed, 1 Jan 2025 17:12:07 +0100 Subject: [PATCH 19/84] typing fix --- mesa/experimental/continuous_space/continuous_space.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 5652d1aea9e..6c4d796373d 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -119,9 +119,9 @@ def calculate_distances(self, point): def in_bounds(self, point) -> bool: """Check if point is inside the bounds of the space.""" - return ( + return bool(( (point >= self.dimensions[:, 0]) & (point <= self.dimensions[:, 1]) - ).all() + ).all()) def torus_correct(self, point) -> np.ndarray: """Apply a torus correction to the point.""" From 3a985d807c5922977078adde8b8c156c7620a2f9 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Wed, 1 Jan 2025 17:34:03 +0100 Subject: [PATCH 20/84] tests for distance calculations --- .../continuous_space/continuous_space.py | 22 ++++---- tests/test_continuous_space.py | 53 +++++++++++++------ 2 files changed, 49 insertions(+), 26 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 6c4d796373d..1708483b827 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -30,18 +30,18 @@ def __init__( random = Random() self.random = random - self.dimensions = np.asarray(dimensions) - self.ndims = self.dimensions.shape[0] - self.size = self.dimensions[:, 1] - self.dimensions[:, 0] - self.center = np.sum(self.dimensions, axis=1) / 2 + self.dimensions: np.array = np.asarray(dimensions) + self.ndims : int = self.dimensions.shape[0] + self.size: np.array = self.dimensions[:, 1] - self.dimensions[:, 0] + self.center: np.array = np.sum(self.dimensions, axis=1) / 2 - self.torus = torus + self.torus: bool = torus - self._agent_positions = np.zeros( + self._agent_positions: np.array = np.zeros( (n_agents, self.dimensions.shape[0]), dtype=float ) - self._agents = np.zeros((n_agents,), dtype=object) - self._positions_in_use = np.zeros( + self._agents: np.array = np.zeros((n_agents,), dtype=object) + self._positions_in_use: np.array = np.zeros( (n_agents,), dtype=bool ) # effectively a mask over _agent_positions @@ -105,8 +105,10 @@ def _remove_agent(self, agent: Agent) -> None: self._positions_in_use[index] = False self._agents[index] = None - def calculate_distances(self, point): + def calculate_distances(self, point) -> tuple[np.ndarray, np.ndarray]: """Calculate the distance between the point and all agents.""" + point = np.asanyarray(point) + if self.torus: delta = np.abs(point[np.newaxis, :] - self.agent_positions) delta = np.minimum( @@ -114,7 +116,7 @@ def calculate_distances(self, point): ) # fixme, should be based on size or maxima? dists = np.linalg.norm(delta, axis=1) else: - dists = cdist(point[np.newaxis, :], self.agent_positions) + dists = cdist(point[np.newaxis, :], self.agent_positions)[:, 0] return dists, self._agents[self._positions_in_use] def in_bounds(self, point) -> bool: diff --git a/tests/test_continuous_space.py b/tests/test_continuous_space.py index 7c9d44f3295..889129e072c 100644 --- a/tests/test_continuous_space.py +++ b/tests/test_continuous_space.py @@ -84,6 +84,42 @@ def test_continuous_agent(): agent.remove() assert space.agent_positions.shape == (110 - 1 - i, 2) +def test_distances(): + # non torus + model = Model(seed=42) + dimensions = np.asarray([[0, 1], [0, 1]]) + space = ContinuousSpace(dimensions, torus=False, random=model.random) + + agent = ContinuousSpaceAgent(space, model) + agent.position = [0.1, 0.1] + + distances, agents = space.calculate_distances([0.1, 0.9]) + assert np.all(distances == [0.8,]) + assert np.all(agents == [agent,]) + + distances, agents = space.calculate_distances([0.9, 0.1]) + assert np.all(distances == [0.8,]) + assert np.all(agents == [agent, ]) + + # torus + model = Model(seed=42) + dimensions = np.asarray([[0, 1], [0, 1]]) + space = ContinuousSpace(dimensions, torus=True, random=model.random) + + agent = ContinuousSpaceAgent(space, model) + agent.position = [0.1, 0.1] + + distances, agents = space.calculate_distances([0.1, 0.9]) + assert np.all(np.isclose(distances, [0.2,])) + assert np.all(agents == [agent,]) + + distances, agents = space.calculate_distances([0.9, 0.1]) + assert np.all(np.isclose(distances, [0.2,])) + assert np.all(agents == [agent, ]) + + distances, agents = space.calculate_distances([0.9, 0.9]) + assert np.all(np.isclose(distances, [0.2*2**0.5,])) + assert np.all(agents == [agent, ]) # class TestSpaceToroidal(unittest.TestCase): # """Testing a toroidal continuous space.""" @@ -146,19 +182,4 @@ def test_continuous_agent(): # neighbors_3 = self.space.get_neighbors((-30, -30), 10) # assert len(neighbors_3) == 1 # -# def test_bounds(self): -# """Test positions outside of boundary.""" -# boundary_agents = [] -# for i, pos in enumerate(OUTSIDE_POSITIONS): -# a = MockAgent(len(self.agents) + i) -# boundary_agents.append(a) -# self.space.place_agent(a, pos) -# -# for a, pos in zip(boundary_agents, OUTSIDE_POSITIONS): -# adj_pos = self.space.torus_adj(pos) -# assert a.pos == adj_pos -# -# a = self.agents[0] -# for pos in OUTSIDE_POSITIONS: -# assert self.space.out_of_bounds(pos) -# self.space.move_agent(a, pos) + From 94963cdcdd2b48e0277bc53b49c542e6eace6717 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 1 Jan 2025 16:36:39 +0000 Subject: [PATCH 21/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../continuous_space/continuous_space.py | 8 +- tests/test_continuous_space.py | 79 ++++++++++++++++--- 2 files changed, 72 insertions(+), 15 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 1708483b827..886f40c0a67 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -31,7 +31,7 @@ def __init__( self.random = random self.dimensions: np.array = np.asarray(dimensions) - self.ndims : int = self.dimensions.shape[0] + self.ndims: int = self.dimensions.shape[0] self.size: np.array = self.dimensions[:, 1] - self.dimensions[:, 0] self.center: np.array = np.sum(self.dimensions, axis=1) / 2 @@ -121,9 +121,9 @@ def calculate_distances(self, point) -> tuple[np.ndarray, np.ndarray]: def in_bounds(self, point) -> bool: """Check if point is inside the bounds of the space.""" - return bool(( - (point >= self.dimensions[:, 0]) & (point <= self.dimensions[:, 1]) - ).all()) + return bool( + ((point >= self.dimensions[:, 0]) & (point <= self.dimensions[:, 1])).all() + ) def torus_correct(self, point) -> np.ndarray: """Apply a torus correction to the point.""" diff --git a/tests/test_continuous_space.py b/tests/test_continuous_space.py index 889129e072c..bb5405ad8ca 100644 --- a/tests/test_continuous_space.py +++ b/tests/test_continuous_space.py @@ -84,6 +84,7 @@ def test_continuous_agent(): agent.remove() assert space.agent_positions.shape == (110 - 1 - i, 2) + def test_distances(): # non torus model = Model(seed=42) @@ -94,12 +95,32 @@ def test_distances(): agent.position = [0.1, 0.1] distances, agents = space.calculate_distances([0.1, 0.9]) - assert np.all(distances == [0.8,]) - assert np.all(agents == [agent,]) + assert np.all( + distances + == [ + 0.8, + ] + ) + assert np.all( + agents + == [ + agent, + ] + ) distances, agents = space.calculate_distances([0.9, 0.1]) - assert np.all(distances == [0.8,]) - assert np.all(agents == [agent, ]) + assert np.all( + distances + == [ + 0.8, + ] + ) + assert np.all( + agents + == [ + agent, + ] + ) # torus model = Model(seed=42) @@ -110,16 +131,53 @@ def test_distances(): agent.position = [0.1, 0.1] distances, agents = space.calculate_distances([0.1, 0.9]) - assert np.all(np.isclose(distances, [0.2,])) - assert np.all(agents == [agent,]) + assert np.all( + np.isclose( + distances, + [ + 0.2, + ], + ) + ) + assert np.all( + agents + == [ + agent, + ] + ) distances, agents = space.calculate_distances([0.9, 0.1]) - assert np.all(np.isclose(distances, [0.2,])) - assert np.all(agents == [agent, ]) + assert np.all( + np.isclose( + distances, + [ + 0.2, + ], + ) + ) + assert np.all( + agents + == [ + agent, + ] + ) distances, agents = space.calculate_distances([0.9, 0.9]) - assert np.all(np.isclose(distances, [0.2*2**0.5,])) - assert np.all(agents == [agent, ]) + assert np.all( + np.isclose( + distances, + [ + 0.2 * 2**0.5, + ], + ) + ) + assert np.all( + agents + == [ + agent, + ] + ) + # class TestSpaceToroidal(unittest.TestCase): # """Testing a toroidal continuous space.""" @@ -182,4 +240,3 @@ def test_distances(): # neighbors_3 = self.space.get_neighbors((-30, -30), 10) # assert len(neighbors_3) == 1 # - From acd0ef0e4016b2221763e8498ea566fbb2880444 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Wed, 1 Jan 2025 19:09:36 +0100 Subject: [PATCH 22/84] ruff related fixes --- mesa/experimental/__init__.py | 2 +- mesa/experimental/continuous_space/__init__.py | 2 ++ mesa/experimental/continuous_space/continuous_space_agents.py | 4 ---- tests/test_continuous_space.py | 3 +++ 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/mesa/experimental/__init__.py b/mesa/experimental/__init__.py index 34111682b15..6898d06f403 100644 --- a/mesa/experimental/__init__.py +++ b/mesa/experimental/__init__.py @@ -17,4 +17,4 @@ from mesa.experimental import cell_space, continuous_space, devs, mesa_signals -__all__ = ["cell_space", "devs", "mesa_signals"] +__all__ = ["cell_space", "devs", "mesa_signals", "continuous_space"] diff --git a/mesa/experimental/continuous_space/__init__.py b/mesa/experimental/continuous_space/__init__.py index db7d4b39cd3..f5cfe92e57c 100644 --- a/mesa/experimental/continuous_space/__init__.py +++ b/mesa/experimental/continuous_space/__init__.py @@ -2,3 +2,5 @@ from mesa.experimental.continuous_space.continuous_space_agents import ( ContinuousSpaceAgent, ) + +__all__ = ["ContinuousSpace", "ContinuousSpaceAgent"] \ No newline at end of file diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index 4643e8325c2..2a878732dcb 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -118,10 +118,6 @@ def get_nearest_neighbors(self, k=1, include_distance=False): k += 1 # the distance calculation includes self, with a distance of 0, so we remove this later indices = np.argpartition(dists, k)[:k] - # fixme - # indices mismatch, because it is the index in the distances - # but this is not the same as the indices in agent_positions - # don't forget to remove our self if include_distance: # don't forget to remove our self diff --git a/tests/test_continuous_space.py b/tests/test_continuous_space.py index bb5405ad8ca..6c00f0be54d 100644 --- a/tests/test_continuous_space.py +++ b/tests/test_continuous_space.py @@ -5,6 +5,7 @@ def test_continuous_space(): + """Test ContinuousSpace class.""" model = Model(seed=42) dimensions = np.asarray([[0, 1], [-1, 0]]) @@ -53,6 +54,7 @@ def test_continuous_space(): def test_continuous_agent(): + """Test ContinuousSpaceAgent class.""" model = Model(seed=42) dimensions = np.asarray([[0, 1], [0, 1]]) @@ -86,6 +88,7 @@ def test_continuous_agent(): def test_distances(): + """Test ContinuousSpace.distance method.""" # non torus model = Model(seed=42) dimensions = np.asarray([[0, 1], [0, 1]]) From 5b676d0e4664365917340809b229117b4a6b5159 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 1 Jan 2025 18:09:45 +0000 Subject: [PATCH 23/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/experimental/__init__.py | 2 +- mesa/experimental/continuous_space/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mesa/experimental/__init__.py b/mesa/experimental/__init__.py index 6898d06f403..8f0ac91df48 100644 --- a/mesa/experimental/__init__.py +++ b/mesa/experimental/__init__.py @@ -17,4 +17,4 @@ from mesa.experimental import cell_space, continuous_space, devs, mesa_signals -__all__ = ["cell_space", "devs", "mesa_signals", "continuous_space"] +__all__ = ["cell_space", "continuous_space", "devs", "mesa_signals"] diff --git a/mesa/experimental/continuous_space/__init__.py b/mesa/experimental/continuous_space/__init__.py index f5cfe92e57c..56dab0ef693 100644 --- a/mesa/experimental/continuous_space/__init__.py +++ b/mesa/experimental/continuous_space/__init__.py @@ -3,4 +3,4 @@ ContinuousSpaceAgent, ) -__all__ = ["ContinuousSpace", "ContinuousSpaceAgent"] \ No newline at end of file +__all__ = ["ContinuousSpace", "ContinuousSpaceAgent"] From c8119ede2165fd5357d85796827765547c32f548 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Thu, 2 Jan 2025 16:24:35 +0100 Subject: [PATCH 24/84] wip --- mesa/examples/basic/boid_flockers/agents.py | 68 ++++++++----------- mesa/examples/basic/boid_flockers/app.py | 5 ++ mesa/examples/basic/boid_flockers/model.py | 53 +++++---------- .../continuous_space/continuous_space.py | 27 ++++++-- .../continuous_space_agents.py | 28 ++------ mesa/visualization/mpl_space_drawing.py | 14 ++-- tests/test_continuous_space.py | 2 + 7 files changed, 91 insertions(+), 106 deletions(-) diff --git a/mesa/examples/basic/boid_flockers/agents.py b/mesa/examples/basic/boid_flockers/agents.py index 2ff00cbaef2..531d70cac89 100644 --- a/mesa/examples/basic/boid_flockers/agents.py +++ b/mesa/examples/basic/boid_flockers/agents.py @@ -6,10 +6,10 @@ import numpy as np -from mesa import Agent +from mesa.experimental.continuous_space import ContinuousSpaceAgent -class Boid(Agent): +class Boid(ContinuousSpaceAgent): """A Boid-style flocker agent. The agent follows three behaviors to flock: @@ -23,13 +23,16 @@ class Boid(Agent): any other Boid. """ + def __init__( self, model, - speed, - direction, - vision, - separation, + space, + position=(0,0), + speed=1, + direction=(1,1), + vision=1, + separation=1, cohere=0.03, separate=0.015, match=0.05, @@ -46,7 +49,8 @@ def __init__( separate: Relative importance of avoiding close neighbors (default: 0.015) match: Relative importance of matching neighbors' directions (default: 0.05) """ - super().__init__(model) + super().__init__(space, model) + self.position = position self.speed = speed self.direction = direction self.vision = vision @@ -56,48 +60,36 @@ def __init__( self.match_factor = match self.neighbors = [] + @property + def pos(self): + return self.position + + @pos.setter + def pos(self, value): + pass + + def step(self): """Get the Boid's neighbors, compute the new vector, and move accordingly.""" - self.neighbors = self.model.space.get_neighbors(self.pos, self.vision, False) + neighbors, distances = self.get_neighbors_in_radius(radius=self.vision, include_distance=True) + self.neighbors = neighbors.tolist() # If no neighbors, maintain current direction - if not self.neighbors: - new_pos = self.pos + self.direction * self.speed - self.model.space.move_agent(self, new_pos) + if neighbors.size==0: + self.position += self.direction * self.speed return - # Initialize vectors for the three flocking behaviors - cohere = np.zeros(2) # Cohesion vector - match_vector = np.zeros(2) # Alignment vector - separation_vector = np.zeros(2) # Separation vector - - # Calculate the contribution of each neighbor to the three behaviors - for neighbor in self.neighbors: - heading = self.model.space.get_heading(self.pos, neighbor.pos) - distance = self.model.space.get_distance(self.pos, neighbor.pos) - - # Cohesion - steer towards the average position of neighbors - cohere += heading - - # Separation - avoid getting too close - if distance < self.separation: - separation_vector -= heading - - # Alignment - match neighbors' flying direction - match_vector += neighbor.direction + delta = self.space.calculate_difference_vector(self.position, [n._mesa_index for n in neighbors]) - # Weight each behavior by its factor and normalize by number of neighbors - n = len(self.neighbors) - cohere = cohere * self.cohere_factor - separation_vector = separation_vector * self.separate_factor - match_vector = match_vector * self.match_factor + cohere = np.sum(delta, axis=0) * self.cohere_factor + separation_vector = -1 * np.sum(delta[distances < self.separation], axis=0) * self.separate_factor + match_vector = np.sum(np.asarray([n.direction for n in neighbors]), axis=0) * self.match_factor # Update direction based on the three behaviors - self.direction += (cohere + separation_vector + match_vector) / n + self.direction += (cohere + separation_vector + match_vector) / len(neighbors) # Normalize direction vector self.direction /= np.linalg.norm(self.direction) # Move boid - new_pos = self.pos + self.direction * self.speed - self.model.space.move_agent(self, new_pos) + self.position += self.direction * self.speed diff --git a/mesa/examples/basic/boid_flockers/app.py b/mesa/examples/basic/boid_flockers/app.py index 16b1ccf7f68..f728550673a 100644 --- a/mesa/examples/basic/boid_flockers/app.py +++ b/mesa/examples/basic/boid_flockers/app.py @@ -1,3 +1,8 @@ +import sys, os + + +sys.path.insert(0, os.path.abspath('../../../..')) + from mesa.examples.basic.boid_flockers.model import BoidFlockers from mesa.visualization import Slider, SolaraViz, make_space_component diff --git a/mesa/examples/basic/boid_flockers/model.py b/mesa/examples/basic/boid_flockers/model.py index 5b4974f3a20..c1cb7b661db 100644 --- a/mesa/examples/basic/boid_flockers/model.py +++ b/mesa/examples/basic/boid_flockers/model.py @@ -9,7 +9,7 @@ from mesa import Model from mesa.examples.basic.boid_flockers.agents import Boid -from mesa.space import ContinuousSpace +from mesa.experimental.continuous_space import ContinuousSpace class BoidFlockers(Model): @@ -17,7 +17,7 @@ class BoidFlockers(Model): def __init__( self, - population=100, + population_size=100, width=100, height=100, speed=1, @@ -31,7 +31,7 @@ def __init__( """Create a new Boids Flocking model. Args: - population: Number of Boids in the simulation (default: 100) + population_size: Number of Boids in the simulation (default: 100) width: Width of the space (default: 100) height: Height of the space (default: 100) speed: How fast the Boids move (default: 1) @@ -44,48 +44,22 @@ def __init__( """ super().__init__(seed=seed) - # Model Parameters - self.population = population - self.vision = vision - self.speed = speed - self.separation = separation - # Set up the space - self.space = ContinuousSpace(width, height, torus=True) - - # Store flocking weights - self.factors = {"cohere": cohere, "separate": separate, "match": match} + self.space = ContinuousSpace([[0, width], + [0, height]], + torus=True, random=self.random) # Create and place the Boid agents - self.make_agents() + position = self.rng.random(size=(population_size, 2)) * self.space.size + direction = self.rng.uniform(-1, 1, size=(population_size, 2)) + Boid.create_agents(self, population_size, self.space, position=position, direction=direction, + cohere=cohere, separate=separate, match=match, speed=speed, vision=vision, + separation=separation) # For tracking statistics self.average_heading = None self.update_average_heading() - def make_agents(self): - """Create and place all Boid agents randomly in the space.""" - for _ in range(self.population): - # Random position - x = self.random.random() * self.space.x_max - y = self.random.random() * self.space.y_max - pos = np.array((x, y)) - - # Random initial direction - direction = np.random.random(2) * 2 - 1 # Random vector between -1 and 1 - direction /= np.linalg.norm(direction) # Normalize - - # Create and place the Boid - boid = Boid( - model=self, - speed=self.speed, - direction=direction, - vision=self.vision, - separation=self.separation, - **self.factors, - ) - self.space.place_agent(boid, pos) - def update_average_heading(self): """Calculate the average heading (direction) of all Boids.""" if not self.agents: @@ -103,3 +77,8 @@ def step(self): """ self.agents.shuffle_do("step") self.update_average_heading() + + +if __name__ == '__main__': + model = BoidFlockers() + model.step() \ No newline at end of file diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 886f40c0a67..7e640fff094 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -105,19 +105,36 @@ def _remove_agent(self, agent: Agent) -> None: self._positions_in_use[index] = False self._agents[index] = None - def calculate_distances(self, point) -> tuple[np.ndarray, np.ndarray]: + def calculate_difference_vector(self, point: np.ndarray, indices=None) -> np.ndarray: + """Calculate the difference vector between the point and all agents""" + point = np.asanyarray(point) + positions = self._agent_positions[indices] if indices is not None else self.agent_positions + + if self.torus: + delta = np.abs(point[np.newaxis, :] - positions) + delta = np.minimum( + delta, self.size - delta + ) # fixme, should be based on size or maxima? + else: + delta = point[np.newaxis, :] - positions + + return delta + + def calculate_distances(self, point, indices=None) -> tuple[np.ndarray, np.ndarray]: """Calculate the distance between the point and all agents.""" point = np.asanyarray(point) + positions = self._agent_positions[indices] if indices is not None else self.agent_positions + agents = self._agents[indices] if indices is not None else self._agents[self._positions_in_use] if self.torus: - delta = np.abs(point[np.newaxis, :] - self.agent_positions) + delta = np.abs(point[np.newaxis, :] - positions) delta = np.minimum( - delta, 1 - delta + delta, self.size - delta ) # fixme, should be based on size or maxima? dists = np.linalg.norm(delta, axis=1) else: - dists = cdist(point[np.newaxis, :], self.agent_positions)[:, 0] - return dists, self._agents[self._positions_in_use] + dists = cdist(point[np.newaxis, :], positions)[:, 0] + return dists, agents def in_bounds(self, point) -> bool: """Check if point is inside the bounds of the space.""" diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index 2a878732dcb..f55e88e9fe8 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -65,12 +65,12 @@ def remove(self) -> None: @overload def get_neighbors_in_radius( self, radius: int = 1, include_distance: bool = False - ) -> list[ContinuousSpaceAgent]: ... + ) -> np.ndarray[ContinuousSpaceAgent]: ... @overload def get_neighbors_in_radius( self, radius=1, include_distance: bool = True - ) -> list[tuple[ContinuousSpaceAgent, float]]: ... + ) -> tuple[np.ndarray[ContinuousSpaceAgent], np.ndarray[float]]: ... def get_neighbors_in_radius(self, radius=1, include_distance=False): """Get neighbors within radius. @@ -82,18 +82,12 @@ def get_neighbors_in_radius(self, radius=1, include_distance=False): """ dists, agents = self.space.calculate_distances(self.position) indices = np.where(dists <= radius)[0] + indices = indices[indices != self._mesa_index] if include_distance: - # don't forget to remove our self - agents = [ - (agents[index], dists[index]) - for index in indices - if index != self._mesa_index - ] + return agents[indices], dists[indices] else: - # don't forget to remove our self - agents = [agents[index] for index in indices if index != self._mesa_index] - return agents + return agents[indices] @overload def get_nearest_neighbors( @@ -118,15 +112,7 @@ def get_nearest_neighbors(self, k=1, include_distance=False): k += 1 # the distance calculation includes self, with a distance of 0, so we remove this later indices = np.argpartition(dists, k)[:k] - # don't forget to remove our self if include_distance: - # don't forget to remove our self - agents = [ - (agents[index], dists[index]) - for index in indices - if index != self._mesa_index - ] + return agents[indices], dists[indices] else: - # don't forget to remove our self - agents = [agents[index] for index in indices if index != self._mesa_index] - return agents + return agents[indices] diff --git a/mesa/visualization/mpl_space_drawing.py b/mesa/visualization/mpl_space_drawing.py index 784d370eba0..1adacce03f2 100644 --- a/mesa/visualization/mpl_space_drawing.py +++ b/mesa/visualization/mpl_space_drawing.py @@ -143,7 +143,7 @@ def draw_space( draw_orthogonal_grid(space, agent_portrayal, ax=ax, **space_drawing_kwargs) case mesa.space.NetworkGrid() | mesa.experimental.cell_space.Network(): draw_network(space, agent_portrayal, ax=ax, **space_drawing_kwargs) - case mesa.space.ContinuousSpace(): + case mesa.space.ContinuousSpace() | mesa.experimental.continuous_space.ContinuousSpace(): draw_continuous_space(space, agent_portrayal, ax=ax) case VoronoiGrid(): draw_voronoi_grid(space, agent_portrayal, ax=ax) @@ -472,9 +472,13 @@ def draw_continuous_space( fig, ax = plt.subplots() # space related setup - width = space.x_max - space.x_min + width = space.size[0] + height = space.size[1] + + + # width = space.x_max - space.x_min x_padding = width / 20 - height = space.y_max - space.y_min + # height = space.y_max - space.y_min y_padding = height / 20 # gather agent data @@ -491,8 +495,8 @@ def draw_continuous_space( spine.set_color("black") spine.set_linestyle(border_style) - ax.set_xlim(space.x_min - x_padding, space.x_max + x_padding) - ax.set_ylim(space.y_min - y_padding, space.y_max + y_padding) + ax.set_xlim(space.dimensions[0, 0] - x_padding, space.dimensions[0, 1] + x_padding) + ax.set_ylim(space.dimensions[1, 0] - y_padding, space.dimensions[1, 1] + y_padding) return ax diff --git a/tests/test_continuous_space.py b/tests/test_continuous_space.py index 6c00f0be54d..52fdd895fe7 100644 --- a/tests/test_continuous_space.py +++ b/tests/test_continuous_space.py @@ -1,3 +1,5 @@ +"""Tests for continuous space.""" + import numpy as np from mesa import Model From b7c39144597add921f8f0cdcc29e3c1dadc8ee97 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Thu, 2 Jan 2025 16:28:02 +0100 Subject: [PATCH 25/84] wip --- mesa/examples/basic/boid_flockers/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesa/examples/basic/boid_flockers/app.py b/mesa/examples/basic/boid_flockers/app.py index f728550673a..2a544c4ffec 100644 --- a/mesa/examples/basic/boid_flockers/app.py +++ b/mesa/examples/basic/boid_flockers/app.py @@ -22,7 +22,7 @@ def boid_draw(agent): "value": 42, "label": "Random Seed", }, - "population": Slider( + "population_size": Slider( label="Number of boids", value=100, min=10, From a08e29b81d232aacc186d83b9dd104628320b190 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 2 Jan 2025 15:28:56 +0000 Subject: [PATCH 26/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/examples/basic/boid_flockers/agents.py | 27 ++++++++++++------- mesa/examples/basic/boid_flockers/app.py | 6 ++--- mesa/examples/basic/boid_flockers/model.py | 26 ++++++++++++------ .../continuous_space/continuous_space.py | 22 ++++++++++++--- mesa/visualization/mpl_space_drawing.py | 6 +++-- 5 files changed, 61 insertions(+), 26 deletions(-) diff --git a/mesa/examples/basic/boid_flockers/agents.py b/mesa/examples/basic/boid_flockers/agents.py index 531d70cac89..e6144e0aaaa 100644 --- a/mesa/examples/basic/boid_flockers/agents.py +++ b/mesa/examples/basic/boid_flockers/agents.py @@ -23,14 +23,13 @@ class Boid(ContinuousSpaceAgent): any other Boid. """ - def __init__( self, model, space, - position=(0,0), + position=(0, 0), speed=1, - direction=(1,1), + direction=(1, 1), vision=1, separation=1, cohere=0.03, @@ -68,22 +67,32 @@ def pos(self): def pos(self, value): pass - def step(self): """Get the Boid's neighbors, compute the new vector, and move accordingly.""" - neighbors, distances = self.get_neighbors_in_radius(radius=self.vision, include_distance=True) + neighbors, distances = self.get_neighbors_in_radius( + radius=self.vision, include_distance=True + ) self.neighbors = neighbors.tolist() # If no neighbors, maintain current direction - if neighbors.size==0: + if neighbors.size == 0: self.position += self.direction * self.speed return - delta = self.space.calculate_difference_vector(self.position, [n._mesa_index for n in neighbors]) + delta = self.space.calculate_difference_vector( + self.position, [n._mesa_index for n in neighbors] + ) cohere = np.sum(delta, axis=0) * self.cohere_factor - separation_vector = -1 * np.sum(delta[distances < self.separation], axis=0) * self.separate_factor - match_vector = np.sum(np.asarray([n.direction for n in neighbors]), axis=0) * self.match_factor + separation_vector = ( + -1 + * np.sum(delta[distances < self.separation], axis=0) + * self.separate_factor + ) + match_vector = ( + np.sum(np.asarray([n.direction for n in neighbors]), axis=0) + * self.match_factor + ) # Update direction based on the three behaviors self.direction += (cohere + separation_vector + match_vector) / len(neighbors) diff --git a/mesa/examples/basic/boid_flockers/app.py b/mesa/examples/basic/boid_flockers/app.py index 2a544c4ffec..174e2736a2a 100644 --- a/mesa/examples/basic/boid_flockers/app.py +++ b/mesa/examples/basic/boid_flockers/app.py @@ -1,7 +1,7 @@ -import sys, os +import os +import sys - -sys.path.insert(0, os.path.abspath('../../../..')) +sys.path.insert(0, os.path.abspath("../../../..")) from mesa.examples.basic.boid_flockers.model import BoidFlockers from mesa.visualization import Slider, SolaraViz, make_space_component diff --git a/mesa/examples/basic/boid_flockers/model.py b/mesa/examples/basic/boid_flockers/model.py index c1cb7b661db..edae3b2c752 100644 --- a/mesa/examples/basic/boid_flockers/model.py +++ b/mesa/examples/basic/boid_flockers/model.py @@ -45,16 +45,26 @@ def __init__( super().__init__(seed=seed) # Set up the space - self.space = ContinuousSpace([[0, width], - [0, height]], - torus=True, random=self.random) + self.space = ContinuousSpace( + [[0, width], [0, height]], torus=True, random=self.random + ) # Create and place the Boid agents position = self.rng.random(size=(population_size, 2)) * self.space.size direction = self.rng.uniform(-1, 1, size=(population_size, 2)) - Boid.create_agents(self, population_size, self.space, position=position, direction=direction, - cohere=cohere, separate=separate, match=match, speed=speed, vision=vision, - separation=separation) + Boid.create_agents( + self, + population_size, + self.space, + position=position, + direction=direction, + cohere=cohere, + separate=separate, + match=match, + speed=speed, + vision=vision, + separation=separation, + ) # For tracking statistics self.average_heading = None @@ -79,6 +89,6 @@ def step(self): self.update_average_heading() -if __name__ == '__main__': +if __name__ == "__main__": model = BoidFlockers() - model.step() \ No newline at end of file + model.step() diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 7e640fff094..ff24115fb45 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -105,10 +105,16 @@ def _remove_agent(self, agent: Agent) -> None: self._positions_in_use[index] = False self._agents[index] = None - def calculate_difference_vector(self, point: np.ndarray, indices=None) -> np.ndarray: + def calculate_difference_vector( + self, point: np.ndarray, indices=None + ) -> np.ndarray: """Calculate the difference vector between the point and all agents""" point = np.asanyarray(point) - positions = self._agent_positions[indices] if indices is not None else self.agent_positions + positions = ( + self._agent_positions[indices] + if indices is not None + else self.agent_positions + ) if self.torus: delta = np.abs(point[np.newaxis, :] - positions) @@ -123,8 +129,16 @@ def calculate_difference_vector(self, point: np.ndarray, indices=None) -> np.nda def calculate_distances(self, point, indices=None) -> tuple[np.ndarray, np.ndarray]: """Calculate the distance between the point and all agents.""" point = np.asanyarray(point) - positions = self._agent_positions[indices] if indices is not None else self.agent_positions - agents = self._agents[indices] if indices is not None else self._agents[self._positions_in_use] + positions = ( + self._agent_positions[indices] + if indices is not None + else self.agent_positions + ) + agents = ( + self._agents[indices] + if indices is not None + else self._agents[self._positions_in_use] + ) if self.torus: delta = np.abs(point[np.newaxis, :] - positions) diff --git a/mesa/visualization/mpl_space_drawing.py b/mesa/visualization/mpl_space_drawing.py index 1adacce03f2..db7916258b0 100644 --- a/mesa/visualization/mpl_space_drawing.py +++ b/mesa/visualization/mpl_space_drawing.py @@ -143,7 +143,10 @@ def draw_space( draw_orthogonal_grid(space, agent_portrayal, ax=ax, **space_drawing_kwargs) case mesa.space.NetworkGrid() | mesa.experimental.cell_space.Network(): draw_network(space, agent_portrayal, ax=ax, **space_drawing_kwargs) - case mesa.space.ContinuousSpace() | mesa.experimental.continuous_space.ContinuousSpace(): + case ( + mesa.space.ContinuousSpace() + | mesa.experimental.continuous_space.ContinuousSpace() + ): draw_continuous_space(space, agent_portrayal, ax=ax) case VoronoiGrid(): draw_voronoi_grid(space, agent_portrayal, ax=ax) @@ -475,7 +478,6 @@ def draw_continuous_space( width = space.size[0] height = space.size[1] - # width = space.x_max - space.x_min x_padding = width / 20 # height = space.y_max - space.y_min From 238d105724975bf81ad222bb1c6bb14a057f2a42 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Thu, 2 Jan 2025 17:02:26 +0100 Subject: [PATCH 27/84] Update configurations.py --- benchmarks/configurations.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarks/configurations.py b/benchmarks/configurations.py index e1e193c4e6d..a865325e145 100644 --- a/benchmarks/configurations.py +++ b/benchmarks/configurations.py @@ -90,14 +90,14 @@ "seeds": 25, "replications": 3, "steps": 20, - "parameters": {"population": 200, "width": 100, "height": 100, "vision": 5}, + "parameters": {"population_size": 200, "width": 100, "height": 100, "vision": 5}, }, "large": { "seeds": 10, "replications": 3, "steps": 10, "parameters": { - "population": 400, + "population_size": 400, "width": 150, "height": 150, "vision": 15, From 07e5da4f057e640b2f7be55c4da565c7f47472cd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 2 Jan 2025 16:02:38 +0000 Subject: [PATCH 28/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- benchmarks/configurations.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/benchmarks/configurations.py b/benchmarks/configurations.py index a865325e145..62f52bf3181 100644 --- a/benchmarks/configurations.py +++ b/benchmarks/configurations.py @@ -90,7 +90,12 @@ "seeds": 25, "replications": 3, "steps": 20, - "parameters": {"population_size": 200, "width": 100, "height": 100, "vision": 5}, + "parameters": { + "population_size": 200, + "width": 100, + "height": 100, + "vision": 5, + }, }, "large": { "seeds": 10, From 1b5b08247f62ce7de2c77772f98245320315844a Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Thu, 2 Jan 2025 18:36:35 +0100 Subject: [PATCH 29/84] updates --- mesa/examples/basic/boid_flockers/model.py | 14 +++++++++++--- .../continuous_space/continuous_space.py | 14 +++++--------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/mesa/examples/basic/boid_flockers/model.py b/mesa/examples/basic/boid_flockers/model.py index edae3b2c752..c7fb65d1571 100644 --- a/mesa/examples/basic/boid_flockers/model.py +++ b/mesa/examples/basic/boid_flockers/model.py @@ -4,6 +4,12 @@ A Mesa implementation of Craig Reynolds's Boids flocker model. Uses numpy arrays to represent vectors. """ +import os +import sys + +sys.path.insert(0, os.path.abspath("../../../..")) + + import numpy as np @@ -46,7 +52,7 @@ def __init__( # Set up the space self.space = ContinuousSpace( - [[0, width], [0, height]], torus=True, random=self.random + [[0, width], [0, height]], torus=True, random=self.random, n_agents=population_size ) # Create and place the Boid agents @@ -90,5 +96,7 @@ def step(self): if __name__ == "__main__": - model = BoidFlockers() - model.step() + model = BoidFlockers(population_size=200, width=100, height=100, vision=5, seed=42) + + for i in range(100): + model.step() diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index ff24115fb45..906a73d7540 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -115,14 +115,13 @@ def calculate_difference_vector( if indices is not None else self.agent_positions ) + delta = point[np.newaxis, :] - positions if self.torus: - delta = np.abs(point[np.newaxis, :] - positions) + delta = np.abs(delta, out=delta) delta = np.minimum( - delta, self.size - delta - ) # fixme, should be based on size or maxima? - else: - delta = point[np.newaxis, :] - positions + delta, self.size - delta, out=delta + ) return delta @@ -141,10 +140,7 @@ def calculate_distances(self, point, indices=None) -> tuple[np.ndarray, np.ndarr ) if self.torus: - delta = np.abs(point[np.newaxis, :] - positions) - delta = np.minimum( - delta, self.size - delta - ) # fixme, should be based on size or maxima? + delta = self.calculate_difference_vector(point) dists = np.linalg.norm(delta, axis=1) else: dists = cdist(point[np.newaxis, :], positions)[:, 0] From c46c8901ac23074fe5393c8ce2634baa88b1f9a4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 2 Jan 2025 17:38:55 +0000 Subject: [PATCH 30/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/examples/basic/boid_flockers/model.py | 7 +++++-- mesa/experimental/continuous_space/continuous_space.py | 4 +--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/mesa/examples/basic/boid_flockers/model.py b/mesa/examples/basic/boid_flockers/model.py index c7fb65d1571..0902edd5bfe 100644 --- a/mesa/examples/basic/boid_flockers/model.py +++ b/mesa/examples/basic/boid_flockers/model.py @@ -4,13 +4,13 @@ A Mesa implementation of Craig Reynolds's Boids flocker model. Uses numpy arrays to represent vectors. """ + import os import sys sys.path.insert(0, os.path.abspath("../../../..")) - import numpy as np from mesa import Model @@ -52,7 +52,10 @@ def __init__( # Set up the space self.space = ContinuousSpace( - [[0, width], [0, height]], torus=True, random=self.random, n_agents=population_size + [[0, width], [0, height]], + torus=True, + random=self.random, + n_agents=population_size, ) # Create and place the Boid agents diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 906a73d7540..6474cb1fc33 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -119,9 +119,7 @@ def calculate_difference_vector( if self.torus: delta = np.abs(delta, out=delta) - delta = np.minimum( - delta, self.size - delta, out=delta - ) + delta = np.minimum(delta, self.size - delta, out=delta) return delta From 357bc750bf32fb936d3022347fdf5349f2445e62 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Thu, 2 Jan 2025 20:20:59 +0100 Subject: [PATCH 31/84] ongoing --- mesa/examples/basic/boid_flockers/profile.svg | 491 ++++++++++++++++++ .../continuous_space_agents.py | 1 + 2 files changed, 492 insertions(+) create mode 100644 mesa/examples/basic/boid_flockers/profile.svg diff --git a/mesa/examples/basic/boid_flockers/profile.svg b/mesa/examples/basic/boid_flockers/profile.svg new file mode 100644 index 00000000000..53d45336f85 --- /dev/null +++ b/mesa/examples/basic/boid_flockers/profile.svg @@ -0,0 +1,491 @@ +py-spy record -o profile.svg -- python model.py Reset ZoomSearch step (mesa/examples/basic/boid_flockers/agents.py:101) (2 samples, 1.71%)norm (numpy/linalg/linalg.py:2552) (1 samples, 0.85%)position (mesa/experimental/continuous_space/continuous_space_agents.py:37) (3 samples, 2.56%)po..in_bounds (mesa/experimental/continuous_space/continuous_space.py:154) (3 samples, 2.56%)in.._all (numpy/core/_methods.py:64) (1 samples, 0.85%)step (mesa/examples/basic/boid_flockers/agents.py:104) (11 samples, 9.40%)step (mesa/ex..position (mesa/experimental/continuous_space/continuous_space_agents.py:43) (3 samples, 2.56%)po..calculate_distances (mesa/experimental/continuous_space/continuous_space.py:136) (6 samples, 5.13%)calcul..agent_positions (mesa/experimental/continuous_space/continuous_space.py:59) (6 samples, 5.13%)agent_..calculate_difference_vector (mesa/experimental/continuous_space/continuous_space.py:116) (4 samples, 3.42%)cal..agent_positions (mesa/experimental/continuous_space/continuous_space.py:59) (4 samples, 3.42%)age..calculate_difference_vector (mesa/experimental/continuous_space/continuous_space.py:120) (1 samples, 0.85%)calculate_difference_vector (mesa/experimental/continuous_space/continuous_space.py:121) (1 samples, 0.85%)calculate_distances (mesa/experimental/continuous_space/continuous_space.py:145) (10 samples, 8.55%)calculate_di..calculate_difference_vector (mesa/experimental/continuous_space/continuous_space.py:123) (4 samples, 3.42%)cal..get_neighbors_in_radius (mesa/experimental/continuous_space/continuous_space_agents.py:83) (18 samples, 15.38%)get_neighbors_in_radius ..calculate_distances (mesa/experimental/continuous_space/continuous_space.py:146) (2 samples, 1.71%)norm (numpy/linalg/linalg.py:2583) (2 samples, 1.71%)get_neighbors_in_radius (mesa/experimental/continuous_space/continuous_space_agents.py:84) (1 samples, 0.85%)step (mesa/examples/basic/boid_flockers/agents.py:72) (28 samples, 23.93%)step (mesa/examples/basic/boid_flocker..get_neighbors_in_radius (mesa/experimental/continuous_space/continuous_space_agents.py:85) (9 samples, 7.69%)get_neighbo..step (mesa/examples/basic/boid_flockers/agents.py:79) (1 samples, 0.85%)position (mesa/experimental/continuous_space/continuous_space_agents.py:43) (1 samples, 0.85%)calculate_difference_vector (mesa/experimental/continuous_space/continuous_space.py:114) (1 samples, 0.85%)calculate_difference_vector (mesa/experimental/continuous_space/continuous_space.py:120) (1 samples, 0.85%)calculate_difference_vector (mesa/experimental/continuous_space/continuous_space.py:121) (1 samples, 0.85%)calculate_difference_vector (mesa/experimental/continuous_space/continuous_space.py:122) (1 samples, 0.85%)step (mesa/examples/basic/boid_flockers/agents.py:82) (6 samples, 5.13%)step (..calculate_difference_vector (mesa/experimental/continuous_space/continuous_space.py:123) (2 samples, 1.71%)step (mesa/examples/basic/boid_flockers/agents.py:83) (2 samples, 1.71%)<listcomp> (mesa/examples/basic/boid_flockers/agents.py:83) (2 samples, 1.71%)sum (numpy/core/fromnumeric.py:2313) (1 samples, 0.85%)_wrapreduction (numpy/core/fromnumeric.py:88) (1 samples, 0.85%)step (mesa/examples/basic/boid_flockers/agents.py:86) (3 samples, 2.56%)st..sum (numpy/core/fromnumeric.py:2314) (1 samples, 0.85%)step (mesa/examples/basic/boid_flockers/agents.py:89) (7 samples, 5.98%)step (me..sum (numpy/core/fromnumeric.py:2313) (2 samples, 1.71%)_wrapreduction (numpy/core/fromnumeric.py:88) (2 samples, 1.71%)step (mesa/examples/basic/boid_flockers/agents.py:90) (2 samples, 1.71%)<listcomp> (mesa/examples/basic/boid_flockers/agents.py:93) (2 samples, 1.71%)_wrapreduction (numpy/core/fromnumeric.py:72) (1 samples, 0.85%)<dictcomp> (numpy/core/fromnumeric.py:73) (1 samples, 0.85%)step (mesa/examples/basic/boid_flockers/agents.py:93) (11 samples, 9.40%)step (mesa/ex..sum (numpy/core/fromnumeric.py:2313) (3 samples, 2.56%)su.._wrapreduction (numpy/core/fromnumeric.py:88) (1 samples, 0.85%)step (mesa/examples/basic/boid_flockers/agents.py:94) (1 samples, 0.85%)step (mesa/examples/basic/boid_flockers/model.py:94) (79 samples, 67.52%)step (mesa/examples/basic/boid_flockers/model.py:94)shuffle_do (mesa/agent.py:345) (79 samples, 67.52%)shuffle_do (mesa/agent.py:345)step (mesa/examples/basic/boid_flockers/agents.py:98) (2 samples, 1.71%)<module> (mesa/examples/basic/boid_flockers/model.py:102) (80 samples, 68.38%)<module> (mesa/examples/basic/boid_flockers/model.py:102)_wrapped_step (mesa/model.py:122) (80 samples, 68.38%)_wrapped_step (mesa/model.py:122)step (mesa/examples/basic/boid_flockers/model.py:95) (1 samples, 0.85%)update_average_heading (mesa/examples/basic/boid_flockers/model.py:85) (1 samples, 0.85%)<module> (mesa/examples/basic/boid_flockers/model.py:14) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (numpy/__init__.py:159) (1 samples, 0.85%)_handle_fromlist (<frozen importlib._bootstrap>:1234) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (numpy/ma/__init__.py:45) (1 samples, 0.85%)_handle_fromlist (<frozen importlib._bootstrap>:1234) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:936) (1 samples, 0.85%)get_code (<frozen importlib._bootstrap_external>:1032) (1 samples, 0.85%)get_data (<frozen importlib._bootstrap_external>:1130) (1 samples, 0.85%)<module> (mesa/batchrunner.py:35) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (tqdm/auto.py:23) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (tqdm/asyncio.py:10) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (asyncio/__init__.py:8) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (asyncio/base_events.py:34) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (ssl.py:100) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:676) (2 samples, 1.71%)module_from_spec (<frozen importlib._bootstrap>:573) (2 samples, 1.71%)create_module (<frozen importlib._bootstrap_external>:1233) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)_joinrealpath (<frozen posixpath>:435) (1 samples, 0.85%)_joinrealpath (<frozen posixpath>:448) (1 samples, 0.85%)join (<frozen posixpath>:88) (1 samples, 0.85%)<module> (mesa/model.py:26) (4 samples, 3.42%)<mo..create_module_logger (mesa/mesa_logging.py:36) (4 samples, 3.42%)cre..stack (inspect.py:1760) (4 samples, 3.42%)sta..getouterframes (inspect.py:1735) (4 samples, 3.42%)get..getframeinfo (inspect.py:1697) (4 samples, 3.42%)get..findsource (inspect.py:1075) (4 samples, 3.42%)fin..getmodule (inspect.py:1001) (4 samples, 3.42%)get..realpath (<frozen posixpath>:415) (4 samples, 3.42%)rea.._joinrealpath (<frozen posixpath>:450) (2 samples, 1.71%)<module> (mesa/__init__.py:11) (7 samples, 5.98%)<module>.._find_and_load (<frozen importlib._bootstrap>:1178) (7 samples, 5.98%)_find_an.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (7 samples, 5.98%)_find_an.._load_unlocked (<frozen importlib._bootstrap>:690) (7 samples, 5.98%)_load_un..exec_module (<frozen importlib._bootstrap_external>:940) (7 samples, 5.98%)exec_mod.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (7 samples, 5.98%)_call_wi..<module> (mesa/batchrunner.py:37) (5 samples, 4.27%)<modu.._find_and_load (<frozen importlib._bootstrap>:1178) (5 samples, 4.27%)_find.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (5 samples, 4.27%)_find.._load_unlocked (<frozen importlib._bootstrap>:690) (5 samples, 4.27%)_load..exec_module (<frozen importlib._bootstrap_external>:940) (5 samples, 4.27%)exec_.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (5 samples, 4.27%)_call..<module> (mesa/model.py:29) (1 samples, 0.85%)Model (mesa/model.py:49) (1 samples, 0.85%)method_logger (mesa/mesa_logging.py:101) (1 samples, 0.85%)getouterframes (inspect.py:1735) (1 samples, 0.85%)getframeinfo (inspect.py:1693) (1 samples, 0.85%)getsourcefile (inspect.py:952) (1 samples, 0.85%)getmodule (inspect.py:992) (1 samples, 0.85%)<module> (pandas/__init__.py:135) (1 samples, 0.85%)_handle_fromlist (<frozen importlib._bootstrap>:1234) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (pandas/api/__init__.py:2) (1 samples, 0.85%)_handle_fromlist (<frozen importlib._bootstrap>:1234) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (pandas/api/typing/__init__.py:31) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1128) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (pandas/io/json/__init__.py:1) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (pandas/io/json/_json.py:500) (1 samples, 0.85%)decorator (pandas/util/_decorators.py:367) (1 samples, 0.85%)dedent (textwrap.py:436) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:676) (1 samples, 0.85%)module_from_spec (<frozen importlib._bootstrap>:573) (1 samples, 0.85%)create_module (<frozen importlib._bootstrap_external>:1233) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (pandas/__init__.py:136) (2 samples, 1.71%)_handle_fromlist (<frozen importlib._bootstrap>:1234) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (pandas/testing.py:6) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (pandas/_testing/__init__.py:61) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (pandas/_testing/asserters.py:14) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:1241) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1140) (1 samples, 0.85%)_find_spec (<frozen importlib._bootstrap>:1080) (1 samples, 0.85%)find_spec (<frozen importlib._bootstrap_external>:1504) (1 samples, 0.85%)_get_spec (<frozen importlib._bootstrap_external>:1476) (1 samples, 0.85%)find_spec (<frozen importlib._bootstrap_external>:1644) (1 samples, 0.85%)<module> (pandas/__init__.py:23) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (pandas/compat/__init__.py:27) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (pandas/compat/pyarrow.py:8) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (pyarrow/__init__.py:65) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:676) (1 samples, 0.85%)module_from_spec (<frozen importlib._bootstrap>:573) (1 samples, 0.85%)create_module (<frozen importlib._bootstrap_external>:1233) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (pandas/__init__.py:44) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (pandas/core/config_init.py:577) (1 samples, 0.85%)inner (pandas/_config/config.py:811) (1 samples, 0.85%)register_option (pandas/_config/config.py:538) (1 samples, 0.85%)match (re/__init__.py:166) (1 samples, 0.85%)_compile (re/__init__.py:274) (1 samples, 0.85%)<module> (pandas/_libs/__init__.py:16) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1140) (1 samples, 0.85%)_find_spec (<frozen importlib._bootstrap>:1080) (1 samples, 0.85%)find_spec (<frozen importlib._bootstrap_external>:1504) (1 samples, 0.85%)_get_spec (<frozen importlib._bootstrap_external>:1476) (1 samples, 0.85%)find_spec (<frozen importlib._bootstrap_external>:1616) (1 samples, 0.85%)_fill_cache (<frozen importlib._bootstrap_external>:1659) (1 samples, 0.85%)<module> (pandas/core/api.py:1) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (pandas/_libs/__init__.py:18) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:1241) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:1241) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:1241) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1128) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (pandas/_libs/tslibs/__init__.py:38) (1 samples, 0.85%)_handle_fromlist (<frozen importlib._bootstrap>:1234) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:1241) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:676) (1 samples, 0.85%)module_from_spec (<frozen importlib._bootstrap>:573) (1 samples, 0.85%)create_module (<frozen importlib._bootstrap_external>:1233) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_wrap_function (pyarrow/compute.py:301) (2 samples, 1.71%)_make_signature (pyarrow/compute.py:275) (2 samples, 1.71%)signature (inspect.py:3272) (2 samples, 1.71%)from_callable (inspect.py:3020) (2 samples, 1.71%)_signature_from_callable (inspect.py:2545) (2 samples, 1.71%)_signature_from_callable (inspect.py:2465) (2 samples, 1.71%)<module> (pandas/core/api.py:28) (3 samples, 2.56%)<m.._find_and_load (<frozen importlib._bootstrap>:1178) (3 samples, 2.56%)_f.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (3 samples, 2.56%)_f.._load_unlocked (<frozen importlib._bootstrap>:690) (3 samples, 2.56%)_l..exec_module (<frozen importlib._bootstrap_external>:940) (3 samples, 2.56%)ex.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (3 samples, 2.56%)_c..<module> (pandas/core/arrays/__init__.py:1) (3 samples, 2.56%)<m.._find_and_load (<frozen importlib._bootstrap>:1178) (3 samples, 2.56%)_f.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (3 samples, 2.56%)_f.._load_unlocked (<frozen importlib._bootstrap>:690) (3 samples, 2.56%)_l..exec_module (<frozen importlib._bootstrap_external>:940) (3 samples, 2.56%)ex.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (3 samples, 2.56%)_c..<module> (pandas/core/arrays/arrow/__init__.py:1) (3 samples, 2.56%)<m.._find_and_load (<frozen importlib._bootstrap>:1178) (3 samples, 2.56%)_f.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (3 samples, 2.56%)_f.._load_unlocked (<frozen importlib._bootstrap>:690) (3 samples, 2.56%)_l..exec_module (<frozen importlib._bootstrap_external>:940) (3 samples, 2.56%)ex.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (3 samples, 2.56%)_c..<module> (pandas/core/arrays/arrow/array.py:52) (3 samples, 2.56%)<m.._find_and_load (<frozen importlib._bootstrap>:1178) (3 samples, 2.56%)_f.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (3 samples, 2.56%)_f.._load_unlocked (<frozen importlib._bootstrap>:690) (3 samples, 2.56%)_l..exec_module (<frozen importlib._bootstrap_external>:940) (3 samples, 2.56%)ex.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (3 samples, 2.56%)_c..<module> (pandas/core/arrays/_arrow_string_mixins.py:11) (3 samples, 2.56%)<m.._find_and_load (<frozen importlib._bootstrap>:1178) (3 samples, 2.56%)_f.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (3 samples, 2.56%)_f.._load_unlocked (<frozen importlib._bootstrap>:690) (3 samples, 2.56%)_l..exec_module (<frozen importlib._bootstrap_external>:940) (3 samples, 2.56%)ex.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (3 samples, 2.56%)_c..<module> (pyarrow/compute.py:335) (3 samples, 2.56%)<m.._make_global_functions (pyarrow/compute.py:332) (3 samples, 2.56%)_m.._wrap_function (pyarrow/compute.py:303) (1 samples, 0.85%)_decorate_compute_function (pyarrow/compute.py:169) (1 samples, 0.85%)_scrape_options_class_doc (pyarrow/compute.py:116) (1 samples, 0.85%)__init__ (pyarrow/vendored/docscrape.py:154) (1 samples, 0.85%)_parse (pyarrow/vendored/docscrape.py:388) (1 samples, 0.85%)_read_sections (pyarrow/vendored/docscrape.py:218) (1 samples, 0.85%)_read_to_next_section (pyarrow/vendored/docscrape.py:206) (1 samples, 0.85%)read_to_next_empty_line (pyarrow/vendored/docscrape.py:90) (1 samples, 0.85%)read_to_condition (pyarrow/vendored/docscrape.py:79) (1 samples, 0.85%)<module> (pandas/core/generic.py:178) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (pandas/core/methods/describe.py:41) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (pandas/io/formats/format.py:91) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (pandas/io/common.py:26) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:676) (2 samples, 1.71%)module_from_spec (<frozen importlib._bootstrap>:573) (2 samples, 1.71%)create_module (<frozen importlib._bootstrap_external>:1233) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (pandas/core/window/ewm.py:11) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:1241) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (pandas/core/api.py:47) (5 samples, 4.27%)<modu.._find_and_load (<frozen importlib._bootstrap>:1178) (5 samples, 4.27%)_find.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (5 samples, 4.27%)_find.._load_unlocked (<frozen importlib._bootstrap>:690) (5 samples, 4.27%)_load..exec_module (<frozen importlib._bootstrap_external>:940) (5 samples, 4.27%)exec_.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (5 samples, 4.27%)_call..<module> (pandas/core/groupby/__init__.py:1) (5 samples, 4.27%)<modu.._find_and_load (<frozen importlib._bootstrap>:1178) (5 samples, 4.27%)_find.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (5 samples, 4.27%)_find.._load_unlocked (<frozen importlib._bootstrap>:690) (5 samples, 4.27%)_load..exec_module (<frozen importlib._bootstrap_external>:940) (5 samples, 4.27%)exec_.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (5 samples, 4.27%)_call..<module> (pandas/core/groupby/generic.py:67) (5 samples, 4.27%)<modu.._find_and_load (<frozen importlib._bootstrap>:1178) (5 samples, 4.27%)_find.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (5 samples, 4.27%)_find.._load_unlocked (<frozen importlib._bootstrap>:690) (5 samples, 4.27%)_load..exec_module (<frozen importlib._bootstrap_external>:940) (5 samples, 4.27%)exec_.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (5 samples, 4.27%)_call..<module> (pandas/core/frame.py:142) (5 samples, 4.27%)<modu.._find_and_load (<frozen importlib._bootstrap>:1178) (5 samples, 4.27%)_find.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (5 samples, 4.27%)_find.._load_unlocked (<frozen importlib._bootstrap>:690) (5 samples, 4.27%)_load..exec_module (<frozen importlib._bootstrap_external>:940) (5 samples, 4.27%)exec_.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (5 samples, 4.27%)_call..<module> (pandas/core/generic.py:187) (3 samples, 2.56%)<m.._find_and_load (<frozen importlib._bootstrap>:1178) (3 samples, 2.56%)_f.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (3 samples, 2.56%)_f.._load_unlocked (<frozen importlib._bootstrap>:690) (3 samples, 2.56%)_l..exec_module (<frozen importlib._bootstrap_external>:940) (3 samples, 2.56%)ex.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (3 samples, 2.56%)_c..<module> (pandas/core/window/__init__.py:1) (3 samples, 2.56%)<m.._find_and_load (<frozen importlib._bootstrap>:1178) (3 samples, 2.56%)_f.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (3 samples, 2.56%)_f.._load_unlocked (<frozen importlib._bootstrap>:690) (3 samples, 2.56%)_l..exec_module (<frozen importlib._bootstrap_external>:940) (3 samples, 2.56%)ex.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (3 samples, 2.56%)_c..<module> (pandas/core/window/ewm.py:49) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (pandas/core/window/rolling.py:1850) (1 samples, 0.85%)Rolling (pandas/core/window/rolling.py:2753) (1 samples, 0.85%)dedent (textwrap.py:466) (1 samples, 0.85%)sub (re/__init__.py:185) (1 samples, 0.85%)<module> (mesa/__init__.py:12) (16 samples, 13.68%)<module> (mesa/__init.._find_and_load (<frozen importlib._bootstrap>:1178) (16 samples, 13.68%)_find_and_load (<froz.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (16 samples, 13.68%)_find_and_load_unlock.._load_unlocked (<frozen importlib._bootstrap>:690) (16 samples, 13.68%)_load_unlocked (<froz..exec_module (<frozen importlib._bootstrap_external>:940) (16 samples, 13.68%)exec_module (<frozen .._call_with_frames_removed (<frozen importlib._bootstrap>:241) (16 samples, 13.68%)_call_with_frames_rem..<module> (mesa/datacollection.py:41) (16 samples, 13.68%)<module> (mesa/dataco.._find_and_load (<frozen importlib._bootstrap>:1178) (16 samples, 13.68%)_find_and_load (<froz.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (16 samples, 13.68%)_find_and_load_unlock.._load_unlocked (<frozen importlib._bootstrap>:690) (16 samples, 13.68%)_load_unlocked (<froz..exec_module (<frozen importlib._bootstrap_external>:940) (16 samples, 13.68%)exec_module (<frozen .._call_with_frames_removed (<frozen importlib._bootstrap>:241) (16 samples, 13.68%)_call_with_frames_rem..<module> (pandas/__init__.py:46) (11 samples, 9.40%)<module> (pan.._find_and_load (<frozen importlib._bootstrap>:1178) (11 samples, 9.40%)_find_and_loa.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (11 samples, 9.40%)_find_and_loa.._load_unlocked (<frozen importlib._bootstrap>:690) (11 samples, 9.40%)_load_unlocke..exec_module (<frozen importlib._bootstrap_external>:940) (11 samples, 9.40%)exec_module (.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (11 samples, 9.40%)_call_with_fr..<module> (pandas/core/api.py:9) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (pandas/core/dtypes/dtypes.py:24) (1 samples, 0.85%)_handle_fromlist (<frozen importlib._bootstrap>:1234) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:1241) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)__getitem__ (typing.py:486) (1 samples, 0.85%)inner (typing.py:360) (1 samples, 0.85%)Literal (typing.py:733) (1 samples, 0.85%)__init__ (typing.py:1343) (1 samples, 0.85%)_collect_parameters (typing_extensions.py:3077) (1 samples, 0.85%)_is_unpacked_typevartuple (typing_extensions.py:2977) (1 samples, 0.85%)get_origin (typing.py:2396) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1128) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (scipy/__init__.py:51) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1140) (2 samples, 1.71%)_find_spec (<frozen importlib._bootstrap>:1080) (2 samples, 1.71%)find_spec (<frozen importlib._bootstrap_external>:1504) (2 samples, 1.71%)_get_spec (<frozen importlib._bootstrap_external>:1476) (2 samples, 1.71%)find_spec (<frozen importlib._bootstrap_external>:1616) (2 samples, 1.71%)_fill_cache (<frozen importlib._bootstrap_external>:1659) (2 samples, 1.71%)get_data (<frozen importlib._bootstrap_external>:1130) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:936) (2 samples, 1.71%)get_code (<frozen importlib._bootstrap_external>:1032) (2 samples, 1.71%)get_data (<frozen importlib._bootstrap_external>:1131) (1 samples, 0.85%)<module> (numpy/testing/__init__.py:10) (1 samples, 0.85%)_handle_fromlist (<frozen importlib._bootstrap>:1234) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:936) (1 samples, 0.85%)get_code (<frozen importlib._bootstrap_external>:1032) (1 samples, 0.85%)get_data (<frozen importlib._bootstrap_external>:1130) (1 samples, 0.85%)<module> (scipy/sparse/__init__.py:294) (4 samples, 3.42%)<mo.._find_and_load (<frozen importlib._bootstrap>:1178) (4 samples, 3.42%)_fi.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (4 samples, 3.42%)_fi.._load_unlocked (<frozen importlib._bootstrap>:690) (4 samples, 3.42%)_lo..exec_module (<frozen importlib._bootstrap_external>:940) (4 samples, 3.42%)exe.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (4 samples, 3.42%)_ca..<module> (scipy/sparse/_base.py:5) (4 samples, 3.42%)<mo.._find_and_load (<frozen importlib._bootstrap>:1178) (4 samples, 3.42%)_fi.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (4 samples, 3.42%)_fi.._load_unlocked (<frozen importlib._bootstrap>:690) (4 samples, 3.42%)_lo..exec_module (<frozen importlib._bootstrap_external>:940) (4 samples, 3.42%)exe.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (4 samples, 3.42%)_ca..<module> (scipy/_lib/_util.py:18) (4 samples, 3.42%)<mo.._find_and_load (<frozen importlib._bootstrap>:1178) (4 samples, 3.42%)_fi.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (4 samples, 3.42%)_fi.._load_unlocked (<frozen importlib._bootstrap>:690) (4 samples, 3.42%)_lo..exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (scipy/_lib/_array_api.py:15) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (numpy/testing/__init__.py:11) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)<module> (scipy/sparse/__init__.py:295) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (scipy/sparse/_csr.py:11) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:676) (1 samples, 0.85%)module_from_spec (<frozen importlib._bootstrap>:573) (1 samples, 0.85%)create_module (<frozen importlib._bootstrap_external>:1233) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (scipy/sparse/__init__.py:308) (1 samples, 0.85%)_handle_fromlist (<frozen importlib._bootstrap>:1234) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:936) (1 samples, 0.85%)get_code (<frozen importlib._bootstrap_external>:1032) (1 samples, 0.85%)get_data (<frozen importlib._bootstrap_external>:1130) (1 samples, 0.85%)<module> (mesa/__init__.py:8) (9 samples, 7.69%)<module> (m.._find_and_load (<frozen importlib._bootstrap>:1178) (9 samples, 7.69%)_find_and_l.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (9 samples, 7.69%)_find_and_l.._load_unlocked (<frozen importlib._bootstrap>:690) (9 samples, 7.69%)_load_unloc..exec_module (<frozen importlib._bootstrap_external>:940) (9 samples, 7.69%)exec_module.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (9 samples, 7.69%)_call_with_..<module> (mesa/experimental/__init__.py:18) (9 samples, 7.69%)<module> (m.._handle_fromlist (<frozen importlib._bootstrap>:1234) (9 samples, 7.69%)_handle_fro.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (9 samples, 7.69%)_call_with_.._find_and_load (<frozen importlib._bootstrap>:1178) (9 samples, 7.69%)_find_and_l.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (9 samples, 7.69%)_find_and_l.._load_unlocked (<frozen importlib._bootstrap>:690) (9 samples, 7.69%)_load_unloc..exec_module (<frozen importlib._bootstrap_external>:940) (9 samples, 7.69%)exec_module.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (9 samples, 7.69%)_call_with_..<module> (mesa/experimental/continuous_space/__init__.py:1) (9 samples, 7.69%)<module> (m.._find_and_load (<frozen importlib._bootstrap>:1178) (9 samples, 7.69%)_find_and_l.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (9 samples, 7.69%)_find_and_l.._load_unlocked (<frozen importlib._bootstrap>:690) (9 samples, 7.69%)_load_unloc..exec_module (<frozen importlib._bootstrap_external>:940) (9 samples, 7.69%)exec_module.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (9 samples, 7.69%)_call_with_..<module> (mesa/experimental/continuous_space/continuous_space.py:8) (9 samples, 7.69%)<module> (m.._find_and_load (<frozen importlib._bootstrap>:1178) (9 samples, 7.69%)_find_and_l.._find_and_load_unlocked (<frozen importlib._bootstrap>:1128) (9 samples, 7.69%)_find_and_l.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (9 samples, 7.69%)_call_with_.._find_and_load (<frozen importlib._bootstrap>:1178) (9 samples, 7.69%)_find_and_l.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (7 samples, 5.98%)_find_an.._load_unlocked (<frozen importlib._bootstrap>:690) (7 samples, 5.98%)_load_un..exec_module (<frozen importlib._bootstrap_external>:940) (7 samples, 5.98%)exec_mod.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (7 samples, 5.98%)_call_wi..<module> (scipy/spatial/__init__.py:110) (7 samples, 5.98%)<module>.._find_and_load (<frozen importlib._bootstrap>:1178) (7 samples, 5.98%)_find_an.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (7 samples, 5.98%)_find_an.._load_unlocked (<frozen importlib._bootstrap>:690) (7 samples, 5.98%)_load_un..exec_module (<frozen importlib._bootstrap_external>:940) (7 samples, 5.98%)exec_mod.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (7 samples, 5.98%)_call_wi..<module> (scipy/spatial/_kdtree.py:4) (7 samples, 5.98%)<module>.._find_and_load (<frozen importlib._bootstrap>:1178) (7 samples, 5.98%)_find_an.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (7 samples, 5.98%)_find_an.._load_unlocked (<frozen importlib._bootstrap>:690) (7 samples, 5.98%)_load_un..exec_module (<frozen importlib._bootstrap_external>:1241) (7 samples, 5.98%)exec_mod.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (7 samples, 5.98%)_call_wi.._find_and_load (<frozen importlib._bootstrap>:1178) (7 samples, 5.98%)_find_an.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (7 samples, 5.98%)_find_an.._load_unlocked (<frozen importlib._bootstrap>:690) (7 samples, 5.98%)_load_un..exec_module (<frozen importlib._bootstrap_external>:940) (7 samples, 5.98%)exec_mod.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (7 samples, 5.98%)_call_wi..<module> (scipy/sparse/__init__.py:311) (1 samples, 0.85%)_handle_fromlist (<frozen importlib._bootstrap>:1234) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:936) (1 samples, 0.85%)get_code (<frozen importlib._bootstrap_external>:1026) (1 samples, 0.85%)path_stats (<frozen importlib._bootstrap_external>:1148) (1 samples, 0.85%)_path_stat (<frozen importlib._bootstrap_external>:147) (1 samples, 0.85%)get_data (<frozen importlib._bootstrap_external>:1130) (1 samples, 0.85%)<module> (networkx/__init__.py:21) (2 samples, 1.71%)_handle_fromlist (<frozen importlib._bootstrap>:1234) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (networkx/classes/__init__.py:5) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (networkx/classes/backends.py:63) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (importlib/metadata/__init__.py:17) (2 samples, 1.71%)_handle_fromlist (<frozen importlib._bootstrap>:1234) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:936) (2 samples, 1.71%)get_code (<frozen importlib._bootstrap_external>:1032) (2 samples, 1.71%)get_data (<frozen importlib._bootstrap_external>:1131) (1 samples, 0.85%)<module> (mesa/examples/basic/boid_flockers/model.py:16) (35 samples, 29.91%)<module> (mesa/examples/basic/boid_flockers/mode.._find_and_load (<frozen importlib._bootstrap>:1178) (35 samples, 29.91%)_find_and_load (<frozen importlib._bootstrap>:11.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (35 samples, 29.91%)_find_and_load_unlocked (<frozen importlib._boot.._load_unlocked (<frozen importlib._bootstrap>:690) (35 samples, 29.91%)_load_unlocked (<frozen importlib._bootstrap>:69..exec_module (<frozen importlib._bootstrap_external>:940) (35 samples, 29.91%)exec_module (<frozen importlib._bootstrap_extern.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (35 samples, 29.91%)_call_with_frames_removed (<frozen importlib._bo..<module> (mesa/__init__.py:9) (3 samples, 2.56%)<m.._find_and_load (<frozen importlib._bootstrap>:1178) (3 samples, 2.56%)_f.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (3 samples, 2.56%)_f.._load_unlocked (<frozen importlib._bootstrap>:690) (3 samples, 2.56%)_l..exec_module (<frozen importlib._bootstrap_external>:940) (3 samples, 2.56%)ex.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (3 samples, 2.56%)_c..<module> (mesa/space.py:40) (3 samples, 2.56%)<m.._find_and_load (<frozen importlib._bootstrap>:1178) (3 samples, 2.56%)_f.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (3 samples, 2.56%)_f.._load_unlocked (<frozen importlib._bootstrap>:690) (3 samples, 2.56%)_l..exec_module (<frozen importlib._bootstrap_external>:940) (3 samples, 2.56%)ex.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (3 samples, 2.56%)_c..<module> (networkx/__init__.py:42) (1 samples, 0.85%)_handle_fromlist (<frozen importlib._bootstrap>:1234) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (networkx/algorithms/__init__.py:3) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1140) (1 samples, 0.85%)_find_spec (<frozen importlib._bootstrap>:1080) (1 samples, 0.85%)find_spec (<frozen importlib._bootstrap_external>:1504) (1 samples, 0.85%)_get_spec (<frozen importlib._bootstrap_external>:1476) (1 samples, 0.85%)find_spec (<frozen importlib._bootstrap_external>:1612) (1 samples, 0.85%)_path_stat (<frozen importlib._bootstrap_external>:147) (1 samples, 0.85%)all (117 samples, 100%)<module> (mesa/examples/basic/boid_flockers/model.py:17) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1128) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1128) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1128) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (mesa/examples/__init__.py:3) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (mesa/examples/advanced/sugarscape_g1mt/model.py:6) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1140) (1 samples, 0.85%)_find_spec (<frozen importlib._bootstrap>:1080) (1 samples, 0.85%)find_spec (<frozen importlib._bootstrap_external>:1504) (1 samples, 0.85%)_get_spec (<frozen importlib._bootstrap_external>:1476) (1 samples, 0.85%)find_spec (<frozen importlib._bootstrap_external>:1645) (1 samples, 0.85%)_path_isfile (<frozen importlib._bootstrap_external>:161) (1 samples, 0.85%)_path_is_mode_type (<frozen importlib._bootstrap_external>:153) (1 samples, 0.85%)_path_stat (<frozen importlib._bootstrap_external>:147) (1 samples, 0.85%) \ No newline at end of file diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index f55e88e9fe8..f27dbda43d5 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -111,6 +111,7 @@ def get_nearest_neighbors(self, k=1, include_distance=False): k += 1 # the distance calculation includes self, with a distance of 0, so we remove this later indices = np.argpartition(dists, k)[:k] + indices = indices[indices != self._mesa_index] if include_distance: return agents[indices], dists[indices] From cc4a0959291531d29faee61035786426f7b915c4 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Thu, 2 Jan 2025 21:33:00 +0100 Subject: [PATCH 32/84] Update continuous_space.py --- .../continuous_space/continuous_space.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 6474cb1fc33..c3ee6e7c771 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -115,11 +115,15 @@ def calculate_difference_vector( if indices is not None else self.agent_positions ) - delta = point[np.newaxis, :] - positions + delta = positions - point[np.newaxis, :] if self.torus: - delta = np.abs(delta, out=delta) - delta = np.minimum(delta, self.size - delta, out=delta) + inverse_delta = delta - np.sign(delta) * self.size + logical = np.abs(delta) < np.abs(inverse_delta) + out = np.zeros(delta.shape) + out[logical] = delta[logical] + out[~logical] = inverse_delta[~logical] + delta = out return delta @@ -138,7 +142,13 @@ def calculate_distances(self, point, indices=None) -> tuple[np.ndarray, np.ndarr ) if self.torus: - delta = self.calculate_difference_vector(point) + delta = point[np.newaxis, :] - positions + + delta = np.abs(delta, out=delta) + delta = np.minimum( + delta, self.size - delta, out=delta + ) + dists = np.linalg.norm(delta, axis=1) else: dists = cdist(point[np.newaxis, :], positions)[:, 0] From f549eee8817e0539639ae374f7404cd66c82b492 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Fri, 3 Jan 2025 09:56:02 +0100 Subject: [PATCH 33/84] revert mpl changes --- mesa/visualization/mpl_space_drawing.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/mesa/visualization/mpl_space_drawing.py b/mesa/visualization/mpl_space_drawing.py index db7916258b0..2bfae564036 100644 --- a/mesa/visualization/mpl_space_drawing.py +++ b/mesa/visualization/mpl_space_drawing.py @@ -143,10 +143,7 @@ def draw_space( draw_orthogonal_grid(space, agent_portrayal, ax=ax, **space_drawing_kwargs) case mesa.space.NetworkGrid() | mesa.experimental.cell_space.Network(): draw_network(space, agent_portrayal, ax=ax, **space_drawing_kwargs) - case ( - mesa.space.ContinuousSpace() - | mesa.experimental.continuous_space.ContinuousSpace() - ): + case mesa.space.ContinuousSpace(): draw_continuous_space(space, agent_portrayal, ax=ax) case VoronoiGrid(): draw_voronoi_grid(space, agent_portrayal, ax=ax) @@ -475,12 +472,9 @@ def draw_continuous_space( fig, ax = plt.subplots() # space related setup - width = space.size[0] - height = space.size[1] - - # width = space.x_max - space.x_min + width = space.x_max - space.x_min x_padding = width / 20 - # height = space.y_max - space.y_min + height = space.y_max - space.y_min y_padding = height / 20 # gather agent data @@ -497,8 +491,8 @@ def draw_continuous_space( spine.set_color("black") spine.set_linestyle(border_style) - ax.set_xlim(space.dimensions[0, 0] - x_padding, space.dimensions[0, 1] + x_padding) - ax.set_ylim(space.dimensions[1, 0] - y_padding, space.dimensions[1, 1] + y_padding) + ax.set_xlim(space.x_min - x_padding, space.x_max + x_padding) + ax.set_ylim(space.y_min - y_padding, space.y_max + y_padding) return ax @@ -598,4 +592,4 @@ def _scatter(ax: Axes, arguments, **kwargs): zorder=z_order, **{k: v[logical] for k, v in arguments.items()}, **kwargs, - ) + ) \ No newline at end of file From 4b3bb91ee681ccfb5003ec076667e33c197109b9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 3 Jan 2025 08:56:10 +0000 Subject: [PATCH 34/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/experimental/continuous_space/continuous_space.py | 4 +--- mesa/visualization/mpl_space_drawing.py | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index c3ee6e7c771..8a3e70b3ca9 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -145,9 +145,7 @@ def calculate_distances(self, point, indices=None) -> tuple[np.ndarray, np.ndarr delta = point[np.newaxis, :] - positions delta = np.abs(delta, out=delta) - delta = np.minimum( - delta, self.size - delta, out=delta - ) + delta = np.minimum(delta, self.size - delta, out=delta) dists = np.linalg.norm(delta, axis=1) else: diff --git a/mesa/visualization/mpl_space_drawing.py b/mesa/visualization/mpl_space_drawing.py index 2bfae564036..784d370eba0 100644 --- a/mesa/visualization/mpl_space_drawing.py +++ b/mesa/visualization/mpl_space_drawing.py @@ -592,4 +592,4 @@ def _scatter(ax: Axes, arguments, **kwargs): zorder=z_order, **{k: v[logical] for k, v in arguments.items()}, **kwargs, - ) \ No newline at end of file + ) From 8f0431235838e6f7f8e02f9f9222753c3a2e130c Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Fri, 3 Jan 2025 10:39:27 +0100 Subject: [PATCH 35/84] fine tuning performance --- mesa/examples/basic/boid_flockers/agents.py | 16 +--- .../continuous_space/continuous_space.py | 76 +++++++++++++------ .../continuous_space_agents.py | 10 +++ 3 files changed, 68 insertions(+), 34 deletions(-) diff --git a/mesa/examples/basic/boid_flockers/agents.py b/mesa/examples/basic/boid_flockers/agents.py index e6144e0aaaa..96f12b4a585 100644 --- a/mesa/examples/basic/boid_flockers/agents.py +++ b/mesa/examples/basic/boid_flockers/agents.py @@ -59,20 +59,12 @@ def __init__( self.match_factor = match self.neighbors = [] - @property - def pos(self): - return self.position - - @pos.setter - def pos(self, value): - pass - def step(self): """Get the Boid's neighbors, compute the new vector, and move accordingly.""" neighbors, distances = self.get_neighbors_in_radius( radius=self.vision, include_distance=True ) - self.neighbors = neighbors.tolist() + self.neighbors = neighbors # If no neighbors, maintain current direction if neighbors.size == 0: @@ -80,10 +72,10 @@ def step(self): return delta = self.space.calculate_difference_vector( - self.position, [n._mesa_index for n in neighbors] + self.position, agents=neighbors ) - cohere = np.sum(delta, axis=0) * self.cohere_factor + cohere_vector = np.sum(delta, axis=0) * self.cohere_factor separation_vector = ( -1 * np.sum(delta[distances < self.separation], axis=0) @@ -95,7 +87,7 @@ def step(self): ) # Update direction based on the three behaviors - self.direction += (cohere + separation_vector + match_vector) / len(neighbors) + self.direction += (cohere_vector + separation_vector + match_vector) / len(neighbors) # Normalize direction vector self.direction /= np.linalg.norm(self.direction) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 8a3e70b3ca9..094a81c88bc 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -9,10 +9,41 @@ from mesa.agent import Agent, AgentSet +from line_profiler_pycharm import profile class ContinuousSpace: """Continuous space where each agent can have an arbitrary position.""" + @property + def x_min(self): + # compatability with solara_viz + return self.dimensions[0,0] + + @property + def x_max(self): + # compatability with solara_viz + return self.dimensions[0,1] + + @property + def y_min(self): + # compatability with solara_viz + return self.dimensions[1,0] + + @property + def y_max(self): + # compatability with solara_viz + return self.dimensions[1,1] + + @property + def width(self): + # compatability with solara_viz + return self.size[0] + + @property + def height(self): + # compatability with solara_viz + return self.size[1] + def __init__( self, dimensions: Sequence[Sequence[float]], @@ -53,10 +84,10 @@ def agents(self) -> AgentSet: """Return an AgentSet with the agents in the space.""" return AgentSet(self._agents[self._positions_in_use], random=self.random) - @property - def agent_positions(self) -> np.ndarray: - """Return the positions of the agents in the space.""" - return self._agent_positions[self._positions_in_use] + # @property + # def agent_positions(self) -> np.ndarray: + # """Return the positions of the agents in the space.""" + # return self._agent_positions[self._positions_in_use] def _get_index_for_agent(self, agent: Agent) -> int: """Helper method to get the index for the agent. @@ -95,6 +126,9 @@ def _get_index_for_agent(self, agent: Agent) -> int: self._agent_to_index[agent] = index self._index_to_agent[index] = agent + self.agent_positions = self._agent_positions[self._positions_in_use] + self.active_agents = self._agents[self._positions_in_use] + return index def _remove_agent(self, agent: Agent) -> None: @@ -106,40 +140,38 @@ def _remove_agent(self, agent: Agent) -> None: self._agents[index] = None def calculate_difference_vector( - self, point: np.ndarray, indices=None + self, point: np.ndarray, agents=None ) -> np.ndarray: - """Calculate the difference vector between the point and all agents""" + """Calculate the difference vector between the point and all agents.""" point = np.asanyarray(point) - positions = ( - self._agent_positions[indices] - if indices is not None - else self.agent_positions - ) + positions = self.agent_positions if agents is None else self._agent_positions[[self._agent_to_index[a] for a in agents]] + delta = positions - point[np.newaxis, :] if self.torus: inverse_delta = delta - np.sign(delta) * self.size + + # we need to use the lowest absolute value from delta and inverse delta logical = np.abs(delta) < np.abs(inverse_delta) + out = np.zeros(delta.shape) out[logical] = delta[logical] out[~logical] = inverse_delta[~logical] + delta = out return delta - def calculate_distances(self, point, indices=None) -> tuple[np.ndarray, np.ndarray]: + def calculate_distances(self, point, agents=None) -> tuple[np.ndarray, np.ndarray]: """Calculate the distance between the point and all agents.""" point = np.asanyarray(point) - positions = ( - self._agent_positions[indices] - if indices is not None - else self.agent_positions - ) - agents = ( - self._agents[indices] - if indices is not None - else self._agents[self._positions_in_use] - ) + + if agents is None: + positions = self.agent_positions + agents = self.active_agents + else: + positions = self._agent_positions[[self._agent_to_index[a] for a in agents]] + agents = np.asarray(agents) if self.torus: delta = point[np.newaxis, :] - positions diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index f27dbda43d5..5c0aea15b58 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -42,6 +42,16 @@ def position(self, value: np.ndarray) -> None: self.space._agent_positions[self._mesa_index] = value + @property + def pos(self): + # just here for compatability with solara_viz. + return self.position + + @pos.setter + def pos(self, value): + # just here for compatability solara_viz. + pass + def __init__(self, space: ContinuousSpace, model): """Initialize a continuous space agent. From 416455a1734fadb9b3426d369b37d1d013473bdc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 3 Jan 2025 09:42:49 +0000 Subject: [PATCH 36/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/examples/basic/boid_flockers/agents.py | 8 ++++---- .../continuous_space/continuous_space.py | 19 ++++++++++--------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/mesa/examples/basic/boid_flockers/agents.py b/mesa/examples/basic/boid_flockers/agents.py index 96f12b4a585..58151d442a0 100644 --- a/mesa/examples/basic/boid_flockers/agents.py +++ b/mesa/examples/basic/boid_flockers/agents.py @@ -71,9 +71,7 @@ def step(self): self.position += self.direction * self.speed return - delta = self.space.calculate_difference_vector( - self.position, agents=neighbors - ) + delta = self.space.calculate_difference_vector(self.position, agents=neighbors) cohere_vector = np.sum(delta, axis=0) * self.cohere_factor separation_vector = ( @@ -87,7 +85,9 @@ def step(self): ) # Update direction based on the three behaviors - self.direction += (cohere_vector + separation_vector + match_vector) / len(neighbors) + self.direction += (cohere_vector + separation_vector + match_vector) / len( + neighbors + ) # Normalize direction vector self.direction /= np.linalg.norm(self.direction) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 094a81c88bc..8dc78b1037e 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -9,7 +9,6 @@ from mesa.agent import Agent, AgentSet -from line_profiler_pycharm import profile class ContinuousSpace: """Continuous space where each agent can have an arbitrary position.""" @@ -17,22 +16,22 @@ class ContinuousSpace: @property def x_min(self): # compatability with solara_viz - return self.dimensions[0,0] + return self.dimensions[0, 0] @property def x_max(self): # compatability with solara_viz - return self.dimensions[0,1] + return self.dimensions[0, 1] @property def y_min(self): # compatability with solara_viz - return self.dimensions[1,0] + return self.dimensions[1, 0] @property def y_max(self): # compatability with solara_viz - return self.dimensions[1,1] + return self.dimensions[1, 1] @property def width(self): @@ -139,12 +138,14 @@ def _remove_agent(self, agent: Agent) -> None: self._positions_in_use[index] = False self._agents[index] = None - def calculate_difference_vector( - self, point: np.ndarray, agents=None - ) -> np.ndarray: + def calculate_difference_vector(self, point: np.ndarray, agents=None) -> np.ndarray: """Calculate the difference vector between the point and all agents.""" point = np.asanyarray(point) - positions = self.agent_positions if agents is None else self._agent_positions[[self._agent_to_index[a] for a in agents]] + positions = ( + self.agent_positions + if agents is None + else self._agent_positions[[self._agent_to_index[a] for a in agents]] + ) delta = positions - point[np.newaxis, :] From daa9b4797dbc8a460f9d9f5dc841390e437f4f7d Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Fri, 3 Jan 2025 11:16:33 +0100 Subject: [PATCH 37/84] fine tuning --- .../continuous_space/continuous_space.py | 12 ++++++------ .../continuous_space/continuous_space_agents.py | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 8dc78b1037e..7eff8e76ee2 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -124,12 +124,14 @@ def _get_index_for_agent(self, agent: Agent) -> int: self._agents[index] = agent self._agent_to_index[agent] = index self._index_to_agent[index] = agent + self._update_stuff() + return index + + def _update_stuff(self): self.agent_positions = self._agent_positions[self._positions_in_use] self.active_agents = self._agents[self._positions_in_use] - return index - def _remove_agent(self, agent: Agent) -> None: """Remove an agent from the space.""" index = self._get_index_for_agent(agent) @@ -137,6 +139,7 @@ def _remove_agent(self, agent: Agent) -> None: self._index_to_agent.pop(index, None) self._positions_in_use[index] = False self._agents[index] = None + self._update_stuff() def calculate_difference_vector(self, point: np.ndarray, agents=None) -> np.ndarray: """Calculate the difference vector between the point and all agents.""" @@ -175,11 +178,8 @@ def calculate_distances(self, point, agents=None) -> tuple[np.ndarray, np.ndarra agents = np.asarray(agents) if self.torus: - delta = point[np.newaxis, :] - positions - - delta = np.abs(delta, out=delta) + delta = np.abs(point[np.newaxis, :] - positions) delta = np.minimum(delta, self.size - delta, out=delta) - dists = np.linalg.norm(delta, axis=1) else: dists = cdist(point[np.newaxis, :], positions)[:, 0] diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index 5c0aea15b58..c0ac0a1ca85 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -9,6 +9,7 @@ from mesa.agent import Agent from mesa.experimental.continuous_space import ContinuousSpace +from line_profiler_pycharm import profile class HasPositionProtocol(Protocol): """Protocol for continuous space position holders.""" From 137162b3461c93e165d18bc4517cac0f53271517 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 3 Jan 2025 10:16:51 +0000 Subject: [PATCH 38/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/experimental/continuous_space/continuous_space_agents.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index c0ac0a1ca85..5c0aea15b58 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -9,7 +9,6 @@ from mesa.agent import Agent from mesa.experimental.continuous_space import ContinuousSpace -from line_profiler_pycharm import profile class HasPositionProtocol(Protocol): """Protocol for continuous space position holders.""" From 5ce3d55c3febf2d524405502b56937b329032d11 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Fri, 3 Jan 2025 11:22:53 +0100 Subject: [PATCH 39/84] Delete profile.svg --- mesa/examples/basic/boid_flockers/profile.svg | 491 ------------------ 1 file changed, 491 deletions(-) delete mode 100644 mesa/examples/basic/boid_flockers/profile.svg diff --git a/mesa/examples/basic/boid_flockers/profile.svg b/mesa/examples/basic/boid_flockers/profile.svg deleted file mode 100644 index 53d45336f85..00000000000 --- a/mesa/examples/basic/boid_flockers/profile.svg +++ /dev/null @@ -1,491 +0,0 @@ -py-spy record -o profile.svg -- python model.py Reset ZoomSearch step (mesa/examples/basic/boid_flockers/agents.py:101) (2 samples, 1.71%)norm (numpy/linalg/linalg.py:2552) (1 samples, 0.85%)position (mesa/experimental/continuous_space/continuous_space_agents.py:37) (3 samples, 2.56%)po..in_bounds (mesa/experimental/continuous_space/continuous_space.py:154) (3 samples, 2.56%)in.._all (numpy/core/_methods.py:64) (1 samples, 0.85%)step (mesa/examples/basic/boid_flockers/agents.py:104) (11 samples, 9.40%)step (mesa/ex..position (mesa/experimental/continuous_space/continuous_space_agents.py:43) (3 samples, 2.56%)po..calculate_distances (mesa/experimental/continuous_space/continuous_space.py:136) (6 samples, 5.13%)calcul..agent_positions (mesa/experimental/continuous_space/continuous_space.py:59) (6 samples, 5.13%)agent_..calculate_difference_vector (mesa/experimental/continuous_space/continuous_space.py:116) (4 samples, 3.42%)cal..agent_positions (mesa/experimental/continuous_space/continuous_space.py:59) (4 samples, 3.42%)age..calculate_difference_vector (mesa/experimental/continuous_space/continuous_space.py:120) (1 samples, 0.85%)calculate_difference_vector (mesa/experimental/continuous_space/continuous_space.py:121) (1 samples, 0.85%)calculate_distances (mesa/experimental/continuous_space/continuous_space.py:145) (10 samples, 8.55%)calculate_di..calculate_difference_vector (mesa/experimental/continuous_space/continuous_space.py:123) (4 samples, 3.42%)cal..get_neighbors_in_radius (mesa/experimental/continuous_space/continuous_space_agents.py:83) (18 samples, 15.38%)get_neighbors_in_radius ..calculate_distances (mesa/experimental/continuous_space/continuous_space.py:146) (2 samples, 1.71%)norm (numpy/linalg/linalg.py:2583) (2 samples, 1.71%)get_neighbors_in_radius (mesa/experimental/continuous_space/continuous_space_agents.py:84) (1 samples, 0.85%)step (mesa/examples/basic/boid_flockers/agents.py:72) (28 samples, 23.93%)step (mesa/examples/basic/boid_flocker..get_neighbors_in_radius (mesa/experimental/continuous_space/continuous_space_agents.py:85) (9 samples, 7.69%)get_neighbo..step (mesa/examples/basic/boid_flockers/agents.py:79) (1 samples, 0.85%)position (mesa/experimental/continuous_space/continuous_space_agents.py:43) (1 samples, 0.85%)calculate_difference_vector (mesa/experimental/continuous_space/continuous_space.py:114) (1 samples, 0.85%)calculate_difference_vector (mesa/experimental/continuous_space/continuous_space.py:120) (1 samples, 0.85%)calculate_difference_vector (mesa/experimental/continuous_space/continuous_space.py:121) (1 samples, 0.85%)calculate_difference_vector (mesa/experimental/continuous_space/continuous_space.py:122) (1 samples, 0.85%)step (mesa/examples/basic/boid_flockers/agents.py:82) (6 samples, 5.13%)step (..calculate_difference_vector (mesa/experimental/continuous_space/continuous_space.py:123) (2 samples, 1.71%)step (mesa/examples/basic/boid_flockers/agents.py:83) (2 samples, 1.71%)<listcomp> (mesa/examples/basic/boid_flockers/agents.py:83) (2 samples, 1.71%)sum (numpy/core/fromnumeric.py:2313) (1 samples, 0.85%)_wrapreduction (numpy/core/fromnumeric.py:88) (1 samples, 0.85%)step (mesa/examples/basic/boid_flockers/agents.py:86) (3 samples, 2.56%)st..sum (numpy/core/fromnumeric.py:2314) (1 samples, 0.85%)step (mesa/examples/basic/boid_flockers/agents.py:89) (7 samples, 5.98%)step (me..sum (numpy/core/fromnumeric.py:2313) (2 samples, 1.71%)_wrapreduction (numpy/core/fromnumeric.py:88) (2 samples, 1.71%)step (mesa/examples/basic/boid_flockers/agents.py:90) (2 samples, 1.71%)<listcomp> (mesa/examples/basic/boid_flockers/agents.py:93) (2 samples, 1.71%)_wrapreduction (numpy/core/fromnumeric.py:72) (1 samples, 0.85%)<dictcomp> (numpy/core/fromnumeric.py:73) (1 samples, 0.85%)step (mesa/examples/basic/boid_flockers/agents.py:93) (11 samples, 9.40%)step (mesa/ex..sum (numpy/core/fromnumeric.py:2313) (3 samples, 2.56%)su.._wrapreduction (numpy/core/fromnumeric.py:88) (1 samples, 0.85%)step (mesa/examples/basic/boid_flockers/agents.py:94) (1 samples, 0.85%)step (mesa/examples/basic/boid_flockers/model.py:94) (79 samples, 67.52%)step (mesa/examples/basic/boid_flockers/model.py:94)shuffle_do (mesa/agent.py:345) (79 samples, 67.52%)shuffle_do (mesa/agent.py:345)step (mesa/examples/basic/boid_flockers/agents.py:98) (2 samples, 1.71%)<module> (mesa/examples/basic/boid_flockers/model.py:102) (80 samples, 68.38%)<module> (mesa/examples/basic/boid_flockers/model.py:102)_wrapped_step (mesa/model.py:122) (80 samples, 68.38%)_wrapped_step (mesa/model.py:122)step (mesa/examples/basic/boid_flockers/model.py:95) (1 samples, 0.85%)update_average_heading (mesa/examples/basic/boid_flockers/model.py:85) (1 samples, 0.85%)<module> (mesa/examples/basic/boid_flockers/model.py:14) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (numpy/__init__.py:159) (1 samples, 0.85%)_handle_fromlist (<frozen importlib._bootstrap>:1234) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (numpy/ma/__init__.py:45) (1 samples, 0.85%)_handle_fromlist (<frozen importlib._bootstrap>:1234) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:936) (1 samples, 0.85%)get_code (<frozen importlib._bootstrap_external>:1032) (1 samples, 0.85%)get_data (<frozen importlib._bootstrap_external>:1130) (1 samples, 0.85%)<module> (mesa/batchrunner.py:35) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (tqdm/auto.py:23) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (tqdm/asyncio.py:10) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (asyncio/__init__.py:8) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (asyncio/base_events.py:34) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (ssl.py:100) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:676) (2 samples, 1.71%)module_from_spec (<frozen importlib._bootstrap>:573) (2 samples, 1.71%)create_module (<frozen importlib._bootstrap_external>:1233) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)_joinrealpath (<frozen posixpath>:435) (1 samples, 0.85%)_joinrealpath (<frozen posixpath>:448) (1 samples, 0.85%)join (<frozen posixpath>:88) (1 samples, 0.85%)<module> (mesa/model.py:26) (4 samples, 3.42%)<mo..create_module_logger (mesa/mesa_logging.py:36) (4 samples, 3.42%)cre..stack (inspect.py:1760) (4 samples, 3.42%)sta..getouterframes (inspect.py:1735) (4 samples, 3.42%)get..getframeinfo (inspect.py:1697) (4 samples, 3.42%)get..findsource (inspect.py:1075) (4 samples, 3.42%)fin..getmodule (inspect.py:1001) (4 samples, 3.42%)get..realpath (<frozen posixpath>:415) (4 samples, 3.42%)rea.._joinrealpath (<frozen posixpath>:450) (2 samples, 1.71%)<module> (mesa/__init__.py:11) (7 samples, 5.98%)<module>.._find_and_load (<frozen importlib._bootstrap>:1178) (7 samples, 5.98%)_find_an.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (7 samples, 5.98%)_find_an.._load_unlocked (<frozen importlib._bootstrap>:690) (7 samples, 5.98%)_load_un..exec_module (<frozen importlib._bootstrap_external>:940) (7 samples, 5.98%)exec_mod.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (7 samples, 5.98%)_call_wi..<module> (mesa/batchrunner.py:37) (5 samples, 4.27%)<modu.._find_and_load (<frozen importlib._bootstrap>:1178) (5 samples, 4.27%)_find.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (5 samples, 4.27%)_find.._load_unlocked (<frozen importlib._bootstrap>:690) (5 samples, 4.27%)_load..exec_module (<frozen importlib._bootstrap_external>:940) (5 samples, 4.27%)exec_.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (5 samples, 4.27%)_call..<module> (mesa/model.py:29) (1 samples, 0.85%)Model (mesa/model.py:49) (1 samples, 0.85%)method_logger (mesa/mesa_logging.py:101) (1 samples, 0.85%)getouterframes (inspect.py:1735) (1 samples, 0.85%)getframeinfo (inspect.py:1693) (1 samples, 0.85%)getsourcefile (inspect.py:952) (1 samples, 0.85%)getmodule (inspect.py:992) (1 samples, 0.85%)<module> (pandas/__init__.py:135) (1 samples, 0.85%)_handle_fromlist (<frozen importlib._bootstrap>:1234) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (pandas/api/__init__.py:2) (1 samples, 0.85%)_handle_fromlist (<frozen importlib._bootstrap>:1234) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (pandas/api/typing/__init__.py:31) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1128) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (pandas/io/json/__init__.py:1) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (pandas/io/json/_json.py:500) (1 samples, 0.85%)decorator (pandas/util/_decorators.py:367) (1 samples, 0.85%)dedent (textwrap.py:436) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:676) (1 samples, 0.85%)module_from_spec (<frozen importlib._bootstrap>:573) (1 samples, 0.85%)create_module (<frozen importlib._bootstrap_external>:1233) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (pandas/__init__.py:136) (2 samples, 1.71%)_handle_fromlist (<frozen importlib._bootstrap>:1234) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (pandas/testing.py:6) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (pandas/_testing/__init__.py:61) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (pandas/_testing/asserters.py:14) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:1241) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1140) (1 samples, 0.85%)_find_spec (<frozen importlib._bootstrap>:1080) (1 samples, 0.85%)find_spec (<frozen importlib._bootstrap_external>:1504) (1 samples, 0.85%)_get_spec (<frozen importlib._bootstrap_external>:1476) (1 samples, 0.85%)find_spec (<frozen importlib._bootstrap_external>:1644) (1 samples, 0.85%)<module> (pandas/__init__.py:23) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (pandas/compat/__init__.py:27) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (pandas/compat/pyarrow.py:8) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (pyarrow/__init__.py:65) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:676) (1 samples, 0.85%)module_from_spec (<frozen importlib._bootstrap>:573) (1 samples, 0.85%)create_module (<frozen importlib._bootstrap_external>:1233) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (pandas/__init__.py:44) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (pandas/core/config_init.py:577) (1 samples, 0.85%)inner (pandas/_config/config.py:811) (1 samples, 0.85%)register_option (pandas/_config/config.py:538) (1 samples, 0.85%)match (re/__init__.py:166) (1 samples, 0.85%)_compile (re/__init__.py:274) (1 samples, 0.85%)<module> (pandas/_libs/__init__.py:16) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1140) (1 samples, 0.85%)_find_spec (<frozen importlib._bootstrap>:1080) (1 samples, 0.85%)find_spec (<frozen importlib._bootstrap_external>:1504) (1 samples, 0.85%)_get_spec (<frozen importlib._bootstrap_external>:1476) (1 samples, 0.85%)find_spec (<frozen importlib._bootstrap_external>:1616) (1 samples, 0.85%)_fill_cache (<frozen importlib._bootstrap_external>:1659) (1 samples, 0.85%)<module> (pandas/core/api.py:1) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (pandas/_libs/__init__.py:18) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:1241) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:1241) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:1241) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1128) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (pandas/_libs/tslibs/__init__.py:38) (1 samples, 0.85%)_handle_fromlist (<frozen importlib._bootstrap>:1234) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:1241) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:676) (1 samples, 0.85%)module_from_spec (<frozen importlib._bootstrap>:573) (1 samples, 0.85%)create_module (<frozen importlib._bootstrap_external>:1233) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_wrap_function (pyarrow/compute.py:301) (2 samples, 1.71%)_make_signature (pyarrow/compute.py:275) (2 samples, 1.71%)signature (inspect.py:3272) (2 samples, 1.71%)from_callable (inspect.py:3020) (2 samples, 1.71%)_signature_from_callable (inspect.py:2545) (2 samples, 1.71%)_signature_from_callable (inspect.py:2465) (2 samples, 1.71%)<module> (pandas/core/api.py:28) (3 samples, 2.56%)<m.._find_and_load (<frozen importlib._bootstrap>:1178) (3 samples, 2.56%)_f.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (3 samples, 2.56%)_f.._load_unlocked (<frozen importlib._bootstrap>:690) (3 samples, 2.56%)_l..exec_module (<frozen importlib._bootstrap_external>:940) (3 samples, 2.56%)ex.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (3 samples, 2.56%)_c..<module> (pandas/core/arrays/__init__.py:1) (3 samples, 2.56%)<m.._find_and_load (<frozen importlib._bootstrap>:1178) (3 samples, 2.56%)_f.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (3 samples, 2.56%)_f.._load_unlocked (<frozen importlib._bootstrap>:690) (3 samples, 2.56%)_l..exec_module (<frozen importlib._bootstrap_external>:940) (3 samples, 2.56%)ex.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (3 samples, 2.56%)_c..<module> (pandas/core/arrays/arrow/__init__.py:1) (3 samples, 2.56%)<m.._find_and_load (<frozen importlib._bootstrap>:1178) (3 samples, 2.56%)_f.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (3 samples, 2.56%)_f.._load_unlocked (<frozen importlib._bootstrap>:690) (3 samples, 2.56%)_l..exec_module (<frozen importlib._bootstrap_external>:940) (3 samples, 2.56%)ex.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (3 samples, 2.56%)_c..<module> (pandas/core/arrays/arrow/array.py:52) (3 samples, 2.56%)<m.._find_and_load (<frozen importlib._bootstrap>:1178) (3 samples, 2.56%)_f.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (3 samples, 2.56%)_f.._load_unlocked (<frozen importlib._bootstrap>:690) (3 samples, 2.56%)_l..exec_module (<frozen importlib._bootstrap_external>:940) (3 samples, 2.56%)ex.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (3 samples, 2.56%)_c..<module> (pandas/core/arrays/_arrow_string_mixins.py:11) (3 samples, 2.56%)<m.._find_and_load (<frozen importlib._bootstrap>:1178) (3 samples, 2.56%)_f.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (3 samples, 2.56%)_f.._load_unlocked (<frozen importlib._bootstrap>:690) (3 samples, 2.56%)_l..exec_module (<frozen importlib._bootstrap_external>:940) (3 samples, 2.56%)ex.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (3 samples, 2.56%)_c..<module> (pyarrow/compute.py:335) (3 samples, 2.56%)<m.._make_global_functions (pyarrow/compute.py:332) (3 samples, 2.56%)_m.._wrap_function (pyarrow/compute.py:303) (1 samples, 0.85%)_decorate_compute_function (pyarrow/compute.py:169) (1 samples, 0.85%)_scrape_options_class_doc (pyarrow/compute.py:116) (1 samples, 0.85%)__init__ (pyarrow/vendored/docscrape.py:154) (1 samples, 0.85%)_parse (pyarrow/vendored/docscrape.py:388) (1 samples, 0.85%)_read_sections (pyarrow/vendored/docscrape.py:218) (1 samples, 0.85%)_read_to_next_section (pyarrow/vendored/docscrape.py:206) (1 samples, 0.85%)read_to_next_empty_line (pyarrow/vendored/docscrape.py:90) (1 samples, 0.85%)read_to_condition (pyarrow/vendored/docscrape.py:79) (1 samples, 0.85%)<module> (pandas/core/generic.py:178) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (pandas/core/methods/describe.py:41) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (pandas/io/formats/format.py:91) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (pandas/io/common.py:26) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:676) (2 samples, 1.71%)module_from_spec (<frozen importlib._bootstrap>:573) (2 samples, 1.71%)create_module (<frozen importlib._bootstrap_external>:1233) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (pandas/core/window/ewm.py:11) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:1241) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (pandas/core/api.py:47) (5 samples, 4.27%)<modu.._find_and_load (<frozen importlib._bootstrap>:1178) (5 samples, 4.27%)_find.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (5 samples, 4.27%)_find.._load_unlocked (<frozen importlib._bootstrap>:690) (5 samples, 4.27%)_load..exec_module (<frozen importlib._bootstrap_external>:940) (5 samples, 4.27%)exec_.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (5 samples, 4.27%)_call..<module> (pandas/core/groupby/__init__.py:1) (5 samples, 4.27%)<modu.._find_and_load (<frozen importlib._bootstrap>:1178) (5 samples, 4.27%)_find.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (5 samples, 4.27%)_find.._load_unlocked (<frozen importlib._bootstrap>:690) (5 samples, 4.27%)_load..exec_module (<frozen importlib._bootstrap_external>:940) (5 samples, 4.27%)exec_.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (5 samples, 4.27%)_call..<module> (pandas/core/groupby/generic.py:67) (5 samples, 4.27%)<modu.._find_and_load (<frozen importlib._bootstrap>:1178) (5 samples, 4.27%)_find.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (5 samples, 4.27%)_find.._load_unlocked (<frozen importlib._bootstrap>:690) (5 samples, 4.27%)_load..exec_module (<frozen importlib._bootstrap_external>:940) (5 samples, 4.27%)exec_.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (5 samples, 4.27%)_call..<module> (pandas/core/frame.py:142) (5 samples, 4.27%)<modu.._find_and_load (<frozen importlib._bootstrap>:1178) (5 samples, 4.27%)_find.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (5 samples, 4.27%)_find.._load_unlocked (<frozen importlib._bootstrap>:690) (5 samples, 4.27%)_load..exec_module (<frozen importlib._bootstrap_external>:940) (5 samples, 4.27%)exec_.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (5 samples, 4.27%)_call..<module> (pandas/core/generic.py:187) (3 samples, 2.56%)<m.._find_and_load (<frozen importlib._bootstrap>:1178) (3 samples, 2.56%)_f.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (3 samples, 2.56%)_f.._load_unlocked (<frozen importlib._bootstrap>:690) (3 samples, 2.56%)_l..exec_module (<frozen importlib._bootstrap_external>:940) (3 samples, 2.56%)ex.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (3 samples, 2.56%)_c..<module> (pandas/core/window/__init__.py:1) (3 samples, 2.56%)<m.._find_and_load (<frozen importlib._bootstrap>:1178) (3 samples, 2.56%)_f.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (3 samples, 2.56%)_f.._load_unlocked (<frozen importlib._bootstrap>:690) (3 samples, 2.56%)_l..exec_module (<frozen importlib._bootstrap_external>:940) (3 samples, 2.56%)ex.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (3 samples, 2.56%)_c..<module> (pandas/core/window/ewm.py:49) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (pandas/core/window/rolling.py:1850) (1 samples, 0.85%)Rolling (pandas/core/window/rolling.py:2753) (1 samples, 0.85%)dedent (textwrap.py:466) (1 samples, 0.85%)sub (re/__init__.py:185) (1 samples, 0.85%)<module> (mesa/__init__.py:12) (16 samples, 13.68%)<module> (mesa/__init.._find_and_load (<frozen importlib._bootstrap>:1178) (16 samples, 13.68%)_find_and_load (<froz.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (16 samples, 13.68%)_find_and_load_unlock.._load_unlocked (<frozen importlib._bootstrap>:690) (16 samples, 13.68%)_load_unlocked (<froz..exec_module (<frozen importlib._bootstrap_external>:940) (16 samples, 13.68%)exec_module (<frozen .._call_with_frames_removed (<frozen importlib._bootstrap>:241) (16 samples, 13.68%)_call_with_frames_rem..<module> (mesa/datacollection.py:41) (16 samples, 13.68%)<module> (mesa/dataco.._find_and_load (<frozen importlib._bootstrap>:1178) (16 samples, 13.68%)_find_and_load (<froz.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (16 samples, 13.68%)_find_and_load_unlock.._load_unlocked (<frozen importlib._bootstrap>:690) (16 samples, 13.68%)_load_unlocked (<froz..exec_module (<frozen importlib._bootstrap_external>:940) (16 samples, 13.68%)exec_module (<frozen .._call_with_frames_removed (<frozen importlib._bootstrap>:241) (16 samples, 13.68%)_call_with_frames_rem..<module> (pandas/__init__.py:46) (11 samples, 9.40%)<module> (pan.._find_and_load (<frozen importlib._bootstrap>:1178) (11 samples, 9.40%)_find_and_loa.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (11 samples, 9.40%)_find_and_loa.._load_unlocked (<frozen importlib._bootstrap>:690) (11 samples, 9.40%)_load_unlocke..exec_module (<frozen importlib._bootstrap_external>:940) (11 samples, 9.40%)exec_module (.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (11 samples, 9.40%)_call_with_fr..<module> (pandas/core/api.py:9) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (pandas/core/dtypes/dtypes.py:24) (1 samples, 0.85%)_handle_fromlist (<frozen importlib._bootstrap>:1234) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:1241) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)__getitem__ (typing.py:486) (1 samples, 0.85%)inner (typing.py:360) (1 samples, 0.85%)Literal (typing.py:733) (1 samples, 0.85%)__init__ (typing.py:1343) (1 samples, 0.85%)_collect_parameters (typing_extensions.py:3077) (1 samples, 0.85%)_is_unpacked_typevartuple (typing_extensions.py:2977) (1 samples, 0.85%)get_origin (typing.py:2396) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1128) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (scipy/__init__.py:51) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1140) (2 samples, 1.71%)_find_spec (<frozen importlib._bootstrap>:1080) (2 samples, 1.71%)find_spec (<frozen importlib._bootstrap_external>:1504) (2 samples, 1.71%)_get_spec (<frozen importlib._bootstrap_external>:1476) (2 samples, 1.71%)find_spec (<frozen importlib._bootstrap_external>:1616) (2 samples, 1.71%)_fill_cache (<frozen importlib._bootstrap_external>:1659) (2 samples, 1.71%)get_data (<frozen importlib._bootstrap_external>:1130) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:936) (2 samples, 1.71%)get_code (<frozen importlib._bootstrap_external>:1032) (2 samples, 1.71%)get_data (<frozen importlib._bootstrap_external>:1131) (1 samples, 0.85%)<module> (numpy/testing/__init__.py:10) (1 samples, 0.85%)_handle_fromlist (<frozen importlib._bootstrap>:1234) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:936) (1 samples, 0.85%)get_code (<frozen importlib._bootstrap_external>:1032) (1 samples, 0.85%)get_data (<frozen importlib._bootstrap_external>:1130) (1 samples, 0.85%)<module> (scipy/sparse/__init__.py:294) (4 samples, 3.42%)<mo.._find_and_load (<frozen importlib._bootstrap>:1178) (4 samples, 3.42%)_fi.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (4 samples, 3.42%)_fi.._load_unlocked (<frozen importlib._bootstrap>:690) (4 samples, 3.42%)_lo..exec_module (<frozen importlib._bootstrap_external>:940) (4 samples, 3.42%)exe.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (4 samples, 3.42%)_ca..<module> (scipy/sparse/_base.py:5) (4 samples, 3.42%)<mo.._find_and_load (<frozen importlib._bootstrap>:1178) (4 samples, 3.42%)_fi.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (4 samples, 3.42%)_fi.._load_unlocked (<frozen importlib._bootstrap>:690) (4 samples, 3.42%)_lo..exec_module (<frozen importlib._bootstrap_external>:940) (4 samples, 3.42%)exe.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (4 samples, 3.42%)_ca..<module> (scipy/_lib/_util.py:18) (4 samples, 3.42%)<mo.._find_and_load (<frozen importlib._bootstrap>:1178) (4 samples, 3.42%)_fi.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (4 samples, 3.42%)_fi.._load_unlocked (<frozen importlib._bootstrap>:690) (4 samples, 3.42%)_lo..exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (scipy/_lib/_array_api.py:15) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (numpy/testing/__init__.py:11) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)<module> (scipy/sparse/__init__.py:295) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (scipy/sparse/_csr.py:11) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:676) (1 samples, 0.85%)module_from_spec (<frozen importlib._bootstrap>:573) (1 samples, 0.85%)create_module (<frozen importlib._bootstrap_external>:1233) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (scipy/sparse/__init__.py:308) (1 samples, 0.85%)_handle_fromlist (<frozen importlib._bootstrap>:1234) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:936) (1 samples, 0.85%)get_code (<frozen importlib._bootstrap_external>:1032) (1 samples, 0.85%)get_data (<frozen importlib._bootstrap_external>:1130) (1 samples, 0.85%)<module> (mesa/__init__.py:8) (9 samples, 7.69%)<module> (m.._find_and_load (<frozen importlib._bootstrap>:1178) (9 samples, 7.69%)_find_and_l.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (9 samples, 7.69%)_find_and_l.._load_unlocked (<frozen importlib._bootstrap>:690) (9 samples, 7.69%)_load_unloc..exec_module (<frozen importlib._bootstrap_external>:940) (9 samples, 7.69%)exec_module.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (9 samples, 7.69%)_call_with_..<module> (mesa/experimental/__init__.py:18) (9 samples, 7.69%)<module> (m.._handle_fromlist (<frozen importlib._bootstrap>:1234) (9 samples, 7.69%)_handle_fro.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (9 samples, 7.69%)_call_with_.._find_and_load (<frozen importlib._bootstrap>:1178) (9 samples, 7.69%)_find_and_l.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (9 samples, 7.69%)_find_and_l.._load_unlocked (<frozen importlib._bootstrap>:690) (9 samples, 7.69%)_load_unloc..exec_module (<frozen importlib._bootstrap_external>:940) (9 samples, 7.69%)exec_module.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (9 samples, 7.69%)_call_with_..<module> (mesa/experimental/continuous_space/__init__.py:1) (9 samples, 7.69%)<module> (m.._find_and_load (<frozen importlib._bootstrap>:1178) (9 samples, 7.69%)_find_and_l.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (9 samples, 7.69%)_find_and_l.._load_unlocked (<frozen importlib._bootstrap>:690) (9 samples, 7.69%)_load_unloc..exec_module (<frozen importlib._bootstrap_external>:940) (9 samples, 7.69%)exec_module.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (9 samples, 7.69%)_call_with_..<module> (mesa/experimental/continuous_space/continuous_space.py:8) (9 samples, 7.69%)<module> (m.._find_and_load (<frozen importlib._bootstrap>:1178) (9 samples, 7.69%)_find_and_l.._find_and_load_unlocked (<frozen importlib._bootstrap>:1128) (9 samples, 7.69%)_find_and_l.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (9 samples, 7.69%)_call_with_.._find_and_load (<frozen importlib._bootstrap>:1178) (9 samples, 7.69%)_find_and_l.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (7 samples, 5.98%)_find_an.._load_unlocked (<frozen importlib._bootstrap>:690) (7 samples, 5.98%)_load_un..exec_module (<frozen importlib._bootstrap_external>:940) (7 samples, 5.98%)exec_mod.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (7 samples, 5.98%)_call_wi..<module> (scipy/spatial/__init__.py:110) (7 samples, 5.98%)<module>.._find_and_load (<frozen importlib._bootstrap>:1178) (7 samples, 5.98%)_find_an.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (7 samples, 5.98%)_find_an.._load_unlocked (<frozen importlib._bootstrap>:690) (7 samples, 5.98%)_load_un..exec_module (<frozen importlib._bootstrap_external>:940) (7 samples, 5.98%)exec_mod.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (7 samples, 5.98%)_call_wi..<module> (scipy/spatial/_kdtree.py:4) (7 samples, 5.98%)<module>.._find_and_load (<frozen importlib._bootstrap>:1178) (7 samples, 5.98%)_find_an.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (7 samples, 5.98%)_find_an.._load_unlocked (<frozen importlib._bootstrap>:690) (7 samples, 5.98%)_load_un..exec_module (<frozen importlib._bootstrap_external>:1241) (7 samples, 5.98%)exec_mod.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (7 samples, 5.98%)_call_wi.._find_and_load (<frozen importlib._bootstrap>:1178) (7 samples, 5.98%)_find_an.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (7 samples, 5.98%)_find_an.._load_unlocked (<frozen importlib._bootstrap>:690) (7 samples, 5.98%)_load_un..exec_module (<frozen importlib._bootstrap_external>:940) (7 samples, 5.98%)exec_mod.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (7 samples, 5.98%)_call_wi..<module> (scipy/sparse/__init__.py:311) (1 samples, 0.85%)_handle_fromlist (<frozen importlib._bootstrap>:1234) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:936) (1 samples, 0.85%)get_code (<frozen importlib._bootstrap_external>:1026) (1 samples, 0.85%)path_stats (<frozen importlib._bootstrap_external>:1148) (1 samples, 0.85%)_path_stat (<frozen importlib._bootstrap_external>:147) (1 samples, 0.85%)get_data (<frozen importlib._bootstrap_external>:1130) (1 samples, 0.85%)<module> (networkx/__init__.py:21) (2 samples, 1.71%)_handle_fromlist (<frozen importlib._bootstrap>:1234) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (networkx/classes/__init__.py:5) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (networkx/classes/backends.py:63) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:940) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)<module> (importlib/metadata/__init__.py:17) (2 samples, 1.71%)_handle_fromlist (<frozen importlib._bootstrap>:1234) (2 samples, 1.71%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (2 samples, 1.71%)_find_and_load (<frozen importlib._bootstrap>:1178) (2 samples, 1.71%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (2 samples, 1.71%)_load_unlocked (<frozen importlib._bootstrap>:690) (2 samples, 1.71%)exec_module (<frozen importlib._bootstrap_external>:936) (2 samples, 1.71%)get_code (<frozen importlib._bootstrap_external>:1032) (2 samples, 1.71%)get_data (<frozen importlib._bootstrap_external>:1131) (1 samples, 0.85%)<module> (mesa/examples/basic/boid_flockers/model.py:16) (35 samples, 29.91%)<module> (mesa/examples/basic/boid_flockers/mode.._find_and_load (<frozen importlib._bootstrap>:1178) (35 samples, 29.91%)_find_and_load (<frozen importlib._bootstrap>:11.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (35 samples, 29.91%)_find_and_load_unlocked (<frozen importlib._boot.._load_unlocked (<frozen importlib._bootstrap>:690) (35 samples, 29.91%)_load_unlocked (<frozen importlib._bootstrap>:69..exec_module (<frozen importlib._bootstrap_external>:940) (35 samples, 29.91%)exec_module (<frozen importlib._bootstrap_extern.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (35 samples, 29.91%)_call_with_frames_removed (<frozen importlib._bo..<module> (mesa/__init__.py:9) (3 samples, 2.56%)<m.._find_and_load (<frozen importlib._bootstrap>:1178) (3 samples, 2.56%)_f.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (3 samples, 2.56%)_f.._load_unlocked (<frozen importlib._bootstrap>:690) (3 samples, 2.56%)_l..exec_module (<frozen importlib._bootstrap_external>:940) (3 samples, 2.56%)ex.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (3 samples, 2.56%)_c..<module> (mesa/space.py:40) (3 samples, 2.56%)<m.._find_and_load (<frozen importlib._bootstrap>:1178) (3 samples, 2.56%)_f.._find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (3 samples, 2.56%)_f.._load_unlocked (<frozen importlib._bootstrap>:690) (3 samples, 2.56%)_l..exec_module (<frozen importlib._bootstrap_external>:940) (3 samples, 2.56%)ex.._call_with_frames_removed (<frozen importlib._bootstrap>:241) (3 samples, 2.56%)_c..<module> (networkx/__init__.py:42) (1 samples, 0.85%)_handle_fromlist (<frozen importlib._bootstrap>:1234) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (networkx/algorithms/__init__.py:3) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1140) (1 samples, 0.85%)_find_spec (<frozen importlib._bootstrap>:1080) (1 samples, 0.85%)find_spec (<frozen importlib._bootstrap_external>:1504) (1 samples, 0.85%)_get_spec (<frozen importlib._bootstrap_external>:1476) (1 samples, 0.85%)find_spec (<frozen importlib._bootstrap_external>:1612) (1 samples, 0.85%)_path_stat (<frozen importlib._bootstrap_external>:147) (1 samples, 0.85%)all (117 samples, 100%)<module> (mesa/examples/basic/boid_flockers/model.py:17) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1128) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1128) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1128) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (mesa/examples/__init__.py:3) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1149) (1 samples, 0.85%)_load_unlocked (<frozen importlib._bootstrap>:690) (1 samples, 0.85%)exec_module (<frozen importlib._bootstrap_external>:940) (1 samples, 0.85%)_call_with_frames_removed (<frozen importlib._bootstrap>:241) (1 samples, 0.85%)<module> (mesa/examples/advanced/sugarscape_g1mt/model.py:6) (1 samples, 0.85%)_find_and_load (<frozen importlib._bootstrap>:1178) (1 samples, 0.85%)_find_and_load_unlocked (<frozen importlib._bootstrap>:1140) (1 samples, 0.85%)_find_spec (<frozen importlib._bootstrap>:1080) (1 samples, 0.85%)find_spec (<frozen importlib._bootstrap_external>:1504) (1 samples, 0.85%)_get_spec (<frozen importlib._bootstrap_external>:1476) (1 samples, 0.85%)find_spec (<frozen importlib._bootstrap_external>:1645) (1 samples, 0.85%)_path_isfile (<frozen importlib._bootstrap_external>:161) (1 samples, 0.85%)_path_is_mode_type (<frozen importlib._bootstrap_external>:153) (1 samples, 0.85%)_path_stat (<frozen importlib._bootstrap_external>:147) (1 samples, 0.85%) \ No newline at end of file From 96fc316c2fc13eb8243dd85063746131ca2739fe Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Fri, 3 Jan 2025 11:25:11 +0100 Subject: [PATCH 40/84] ruff related --- mesa/examples/basic/boid_flockers/model.py | 2 +- .../continuous_space/continuous_space.py | 24 +++++++++---------- .../continuous_space/for_development.py | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/mesa/examples/basic/boid_flockers/model.py b/mesa/examples/basic/boid_flockers/model.py index 0902edd5bfe..497a69e9b06 100644 --- a/mesa/examples/basic/boid_flockers/model.py +++ b/mesa/examples/basic/boid_flockers/model.py @@ -101,5 +101,5 @@ def step(self): if __name__ == "__main__": model = BoidFlockers(population_size=200, width=100, height=100, vision=5, seed=42) - for i in range(100): + for _i in range(100): model.step() diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 7eff8e76ee2..f0266ff9fde 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -14,33 +14,33 @@ class ContinuousSpace: """Continuous space where each agent can have an arbitrary position.""" @property - def x_min(self): - # compatability with solara_viz + def x_min(self): # noqa: D102 + # compatibility with solara_viz return self.dimensions[0, 0] @property - def x_max(self): - # compatability with solara_viz + def x_max(self): # noqa: D102 + # compatibility with solara_viz return self.dimensions[0, 1] @property - def y_min(self): - # compatability with solara_viz + def y_min(self): # noqa: D102 + # compatibility with solara_viz return self.dimensions[1, 0] @property - def y_max(self): - # compatability with solara_viz + def y_max(self): # noqa: D102 + # compatibility with solara_viz return self.dimensions[1, 1] @property - def width(self): - # compatability with solara_viz + def width(self): # noqa: D102 + # compatibility with solara_viz return self.size[0] @property - def height(self): - # compatability with solara_viz + def height(self): # noqa: D102 + # compatibility with solara_viz return self.size[1] def __init__( diff --git a/mesa/experimental/continuous_space/for_development.py b/mesa/experimental/continuous_space/for_development.py index 52704b667ca..150c2fda7fb 100644 --- a/mesa/experimental/continuous_space/for_development.py +++ b/mesa/experimental/continuous_space/for_development.py @@ -1,4 +1,4 @@ -# to be removed once further into the development +"""to be removed once further into the development""" from random import Random import numpy as np From d6326fda83d559e816eee341dc0b7881ae9d2994 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Fri, 3 Jan 2025 11:25:36 +0100 Subject: [PATCH 41/84] Update continuous_space.py --- mesa/experimental/continuous_space/continuous_space.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index f0266ff9fde..76cb80cc363 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -129,6 +129,7 @@ def _get_index_for_agent(self, agent: Agent) -> int: return index def _update_stuff(self): + """Fixme: Needs a better name.""" self.agent_positions = self._agent_positions[self._positions_in_use] self.active_agents = self._agents[self._positions_in_use] From 8d57c629e8042418f59d9ee95ce776491de11b67 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 3 Jan 2025 10:27:20 +0000 Subject: [PATCH 42/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/experimental/continuous_space/for_development.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mesa/experimental/continuous_space/for_development.py b/mesa/experimental/continuous_space/for_development.py index 150c2fda7fb..2ec046dab18 100644 --- a/mesa/experimental/continuous_space/for_development.py +++ b/mesa/experimental/continuous_space/for_development.py @@ -1,4 +1,5 @@ """to be removed once further into the development""" + from random import Random import numpy as np From afe44c350a31f306c643ed151fa1c40faf076a89 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Fri, 3 Jan 2025 11:41:41 +0100 Subject: [PATCH 43/84] updates --- .../continuous_space/continuous_space.py | 12 ++++++------ tests/test_continuous_space.py | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 76cb80cc363..cf4cea05216 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -124,14 +124,14 @@ def _get_index_for_agent(self, agent: Agent) -> int: self._agents[index] = agent self._agent_to_index[agent] = index self._index_to_agent[index] = agent - self._update_stuff() + self.active_agents = self._agents[self._positions_in_use] return index - def _update_stuff(self): - """Fixme: Needs a better name.""" - self.agent_positions = self._agent_positions[self._positions_in_use] - self.active_agents = self._agents[self._positions_in_use] + @property + def agent_positions(self): + """Return the positions of the agents in the space.""" + return self._agent_positions[self._positions_in_use] def _remove_agent(self, agent: Agent) -> None: """Remove an agent from the space.""" @@ -140,7 +140,7 @@ def _remove_agent(self, agent: Agent) -> None: self._index_to_agent.pop(index, None) self._positions_in_use[index] = False self._agents[index] = None - self._update_stuff() + self.active_agents = self._agents[self._positions_in_use] def calculate_difference_vector(self, point: np.ndarray, agents=None) -> np.ndarray: """Calculate the difference vector between the point and all agents.""" diff --git a/tests/test_continuous_space.py b/tests/test_continuous_space.py index 52fdd895fe7..a28de53351e 100644 --- a/tests/test_continuous_space.py +++ b/tests/test_continuous_space.py @@ -68,9 +68,9 @@ def test_continuous_agent(): assert space.agent_positions.shape == (10, 2) for agent in space.agents: - assert np.all( - agent.position == space.agent_positions[space._get_index_for_agent(agent)] - ) + a = agent.position + b = space._agent_positions[space._get_index_for_agent(agent)] + assert np.all(a == b) # add more agents, triggering a resizeing of the array for _ in range(100): @@ -79,9 +79,9 @@ def test_continuous_agent(): assert space.agent_positions.shape == (110, 2) for agent in space.agents: - assert np.all( - agent.position == space.agent_positions[space._get_index_for_agent(agent)] - ) + a = agent.position + b = space._agent_positions[space._get_index_for_agent(agent)] + assert np.all(a == b) # remove all agents and check if the view is updated throughout correctly for i, agent in enumerate(space.agents): From b7bdd82c6a1b972f9d1b590b2aacb0277df36bd2 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Fri, 3 Jan 2025 11:45:57 +0100 Subject: [PATCH 44/84] make new continous spaces work with existing space drawing --- mesa/visualization/mpl_space_drawing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesa/visualization/mpl_space_drawing.py b/mesa/visualization/mpl_space_drawing.py index 784d370eba0..815adbaa4e6 100644 --- a/mesa/visualization/mpl_space_drawing.py +++ b/mesa/visualization/mpl_space_drawing.py @@ -143,7 +143,7 @@ def draw_space( draw_orthogonal_grid(space, agent_portrayal, ax=ax, **space_drawing_kwargs) case mesa.space.NetworkGrid() | mesa.experimental.cell_space.Network(): draw_network(space, agent_portrayal, ax=ax, **space_drawing_kwargs) - case mesa.space.ContinuousSpace(): + case mesa.space.ContinuousSpace() | mesa.experimental.continuous_space.ContinuousSpace(): draw_continuous_space(space, agent_portrayal, ax=ax) case VoronoiGrid(): draw_voronoi_grid(space, agent_portrayal, ax=ax) From 0db0a6fcc98acd6eb95558c74e46b6ab337029cd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 3 Jan 2025 10:46:57 +0000 Subject: [PATCH 45/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/visualization/mpl_space_drawing.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mesa/visualization/mpl_space_drawing.py b/mesa/visualization/mpl_space_drawing.py index 815adbaa4e6..6e7fa9a05d5 100644 --- a/mesa/visualization/mpl_space_drawing.py +++ b/mesa/visualization/mpl_space_drawing.py @@ -143,7 +143,10 @@ def draw_space( draw_orthogonal_grid(space, agent_portrayal, ax=ax, **space_drawing_kwargs) case mesa.space.NetworkGrid() | mesa.experimental.cell_space.Network(): draw_network(space, agent_portrayal, ax=ax, **space_drawing_kwargs) - case mesa.space.ContinuousSpace() | mesa.experimental.continuous_space.ContinuousSpace(): + case ( + mesa.space.ContinuousSpace() + | mesa.experimental.continuous_space.ContinuousSpace() + ): draw_continuous_space(space, agent_portrayal, ax=ax) case VoronoiGrid(): draw_voronoi_grid(space, agent_portrayal, ax=ax) From 625069c7524220764f753be85b6201578e97131b Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Fri, 3 Jan 2025 11:51:17 +0100 Subject: [PATCH 46/84] ruff related --- mesa/experimental/continuous_space/__init__.py | 2 ++ mesa/experimental/continuous_space/continuous_space_agents.py | 4 ++-- mesa/experimental/continuous_space/for_development.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/mesa/experimental/continuous_space/__init__.py b/mesa/experimental/continuous_space/__init__.py index 56dab0ef693..f9abc541413 100644 --- a/mesa/experimental/continuous_space/__init__.py +++ b/mesa/experimental/continuous_space/__init__.py @@ -1,3 +1,5 @@ +"""Continous space support.""" + from mesa.experimental.continuous_space.continuous_space import ContinuousSpace from mesa.experimental.continuous_space.continuous_space_agents import ( ContinuousSpaceAgent, diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index 5c0aea15b58..f86b6b79b09 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -43,12 +43,12 @@ def position(self, value: np.ndarray) -> None: self.space._agent_positions[self._mesa_index] = value @property - def pos(self): + def pos(self): # noqa: D102 # just here for compatability with solara_viz. return self.position @pos.setter - def pos(self, value): + def pos(self, value): # noqa: D102 # just here for compatability solara_viz. pass diff --git a/mesa/experimental/continuous_space/for_development.py b/mesa/experimental/continuous_space/for_development.py index 2ec046dab18..3e42959172c 100644 --- a/mesa/experimental/continuous_space/for_development.py +++ b/mesa/experimental/continuous_space/for_development.py @@ -1,4 +1,4 @@ -"""to be removed once further into the development""" +"""to be removed once further into the development.""" from random import Random From b56a970958818b833619f10710481bf54ba3c693 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 3 Jan 2025 10:51:32 +0000 Subject: [PATCH 47/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/experimental/continuous_space/continuous_space_agents.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index f86b6b79b09..9a85bd0568f 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -48,7 +48,7 @@ def pos(self): # noqa: D102 return self.position @pos.setter - def pos(self, value): # noqa: D102 + def pos(self, value): # just here for compatability solara_viz. pass From eb5abd6b884d978795c8648f9a889207fcf71e4d Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Fri, 3 Jan 2025 13:00:00 +0100 Subject: [PATCH 48/84] Update continuous_space.py --- mesa/experimental/continuous_space/continuous_space.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index cf4cea05216..1bfc3559236 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -151,7 +151,7 @@ def calculate_difference_vector(self, point: np.ndarray, agents=None) -> np.ndar else self._agent_positions[[self._agent_to_index[a] for a in agents]] ) - delta = positions - point[np.newaxis, :] + delta = positions - point if self.torus: inverse_delta = delta - np.sign(delta) * self.size @@ -179,7 +179,7 @@ def calculate_distances(self, point, agents=None) -> tuple[np.ndarray, np.ndarra agents = np.asarray(agents) if self.torus: - delta = np.abs(point[np.newaxis, :] - positions) + delta = np.abs(positions - point) delta = np.minimum(delta, self.size - delta, out=delta) dists = np.linalg.norm(delta, axis=1) else: From 53ffa3069849a82295822935987b06fa7101b78f Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Fri, 3 Jan 2025 17:09:20 +0100 Subject: [PATCH 49/84] Update continuous_space.py --- mesa/experimental/continuous_space/continuous_space.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 1bfc3559236..8e2b68509a7 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -1,4 +1,4 @@ -"""A Continyous Space class.""" +"""A Continuous Space class.""" import warnings from collections.abc import Sequence From 99bb9def2708d14fe61b2175e78d773e212b1874 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Fri, 3 Jan 2025 18:50:10 +0100 Subject: [PATCH 50/84] profiling --- mesa/experimental/continuous_space/continuous_space.py | 9 ++++++++- .../continuous_space/continuous_space_agents.py | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 8e2b68509a7..180aefebc59 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -9,6 +9,7 @@ from mesa.agent import Agent, AgentSet +from line_profiler_pycharm import profile class ContinuousSpace: """Continuous space where each agent can have an arbitrary position.""" @@ -125,12 +126,16 @@ def _get_index_for_agent(self, agent: Agent) -> int: self._agent_to_index[agent] = index self._index_to_agent[index] = agent self.active_agents = self._agents[self._positions_in_use] + self._is_full = bool(np.all(self._positions_in_use)) return index @property def agent_positions(self): """Return the positions of the agents in the space.""" + if self._is_full: + return self._agent_positions + return self._agent_positions[self._positions_in_use] def _remove_agent(self, agent: Agent) -> None: @@ -141,6 +146,7 @@ def _remove_agent(self, agent: Agent) -> None: self._positions_in_use[index] = False self._agents[index] = None self.active_agents = self._agents[self._positions_in_use] + self._is_full = False def calculate_difference_vector(self, point: np.ndarray, agents=None) -> np.ndarray: """Calculate the difference vector between the point and all agents.""" @@ -167,12 +173,13 @@ def calculate_difference_vector(self, point: np.ndarray, agents=None) -> np.ndar return delta + @profile def calculate_distances(self, point, agents=None) -> tuple[np.ndarray, np.ndarray]: """Calculate the distance between the point and all agents.""" point = np.asanyarray(point) if agents is None: - positions = self.agent_positions + positions = self._agent_positions agents = self.active_agents else: positions = self._agent_positions[[self._agent_to_index[a] for a in agents]] diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index 9a85bd0568f..fd5fd71763c 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -9,6 +9,7 @@ from mesa.agent import Agent from mesa.experimental.continuous_space import ContinuousSpace +from line_profiler_pycharm import profile class HasPositionProtocol(Protocol): """Protocol for continuous space position holders.""" @@ -82,6 +83,7 @@ def get_neighbors_in_radius( self, radius=1, include_distance: bool = True ) -> tuple[np.ndarray[ContinuousSpaceAgent], np.ndarray[float]]: ... + @profile def get_neighbors_in_radius(self, radius=1, include_distance=False): """Get neighbors within radius. From a1cb4cb46423efc8ad7c7e65a46eb7680f396b07 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sat, 4 Jan 2025 12:32:22 +0100 Subject: [PATCH 51/84] onging --- mesa/experimental/continuous_space/continuous_space.py | 10 ++++++++-- .../continuous_space/continuous_space_agents.py | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 180aefebc59..ea258ccbed3 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -7,6 +7,7 @@ import numpy as np from scipy.spatial.distance import cdist + from mesa.agent import Agent, AgentSet from line_profiler_pycharm import profile @@ -179,7 +180,7 @@ def calculate_distances(self, point, agents=None) -> tuple[np.ndarray, np.ndarra point = np.asanyarray(point) if agents is None: - positions = self._agent_positions + positions = self.agent_positions agents = self.active_agents else: positions = self._agent_positions[[self._agent_to_index[a] for a in agents]] @@ -188,7 +189,12 @@ def calculate_distances(self, point, agents=None) -> tuple[np.ndarray, np.ndarra if self.torus: delta = np.abs(positions - point) delta = np.minimum(delta, self.size - delta, out=delta) - dists = np.linalg.norm(delta, axis=1) + # this is obscure: see https://stackoverflow.com/questions/7741878/how-to-apply-numpy-linalg-norm-to-each-row-of-a-matrix + # Also, this might be highly numpy version dependent and even cpu architecture dependent. + # would be good to test again once numpy 2.x is default in anaconda. + # dists = np.linalg.norm(delta, axis=1) + delta_T = delta.T + dists = np.sqrt(np.einsum('ij,ij->j', delta_T, delta_T)) else: dists = cdist(point[np.newaxis, :], positions)[:, 0] return dists, agents diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index fd5fd71763c..a2898676864 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -9,7 +9,7 @@ from mesa.agent import Agent from mesa.experimental.continuous_space import ContinuousSpace -from line_profiler_pycharm import profile +# from line_profiler_pycharm import profile class HasPositionProtocol(Protocol): """Protocol for continuous space position holders.""" @@ -83,7 +83,7 @@ def get_neighbors_in_radius( self, radius=1, include_distance: bool = True ) -> tuple[np.ndarray[ContinuousSpaceAgent], np.ndarray[float]]: ... - @profile + # @profile def get_neighbors_in_radius(self, radius=1, include_distance=False): """Get neighbors within radius. From f7bdb8bc61863ec58c896fc3d03a53ec3dffc756 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sat, 4 Jan 2025 13:50:49 +0100 Subject: [PATCH 52/84] cleanup --- .../continuous_space/continuous_space.py | 23 ++++++++++++------- .../continuous_space_agents.py | 22 +++++++----------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index ea258ccbed3..cab13e6791d 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -7,10 +7,8 @@ import numpy as np from scipy.spatial.distance import cdist - from mesa.agent import Agent, AgentSet -from line_profiler_pycharm import profile class ContinuousSpace: """Continuous space where each agent can have an arbitrary position.""" @@ -85,11 +83,6 @@ def agents(self) -> AgentSet: """Return an AgentSet with the agents in the space.""" return AgentSet(self._agents[self._positions_in_use], random=self.random) - # @property - # def agent_positions(self) -> np.ndarray: - # """Return the positions of the agents in the space.""" - # return self._agent_positions[self._positions_in_use] - def _get_index_for_agent(self, agent: Agent) -> int: """Helper method to get the index for the agent. @@ -174,7 +167,6 @@ def calculate_difference_vector(self, point: np.ndarray, agents=None) -> np.ndar return delta - @profile def calculate_distances(self, point, agents=None) -> tuple[np.ndarray, np.ndarray]: """Calculate the distance between the point and all agents.""" point = np.asanyarray(point) @@ -199,6 +191,21 @@ def calculate_distances(self, point, agents=None) -> tuple[np.ndarray, np.ndarra dists = cdist(point[np.newaxis, :], positions)[:, 0] return dists, agents + def get_agents_in_radius(self, point, radius=1) -> tuple[np.ndarray, np.ndarray]: + """Return the agents and their distances within a radius for the point.""" + distances, agents = self.calculate_distances(point) + logical = distances <= radius + + return distances[logical], agents[logical] + + def get_k_nearest_agents(self, point, k=1) -> tuple[np.ndarray, np.ndarray]: + """Return the k nearest agents and their distances to the point.""" + dists, agents = self.calculate_distances(point) + + k += 1 # the distance calculation includes self, with a distance of 0, so we remove this later + indices = np.argpartition(dists, k)[:k] + return dists[indices], agents[indices] + def in_bounds(self, point) -> bool: """Check if point is inside the bounds of the space.""" return bool( diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index a2898676864..aea76752174 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -9,8 +9,6 @@ from mesa.agent import Agent from mesa.experimental.continuous_space import ContinuousSpace -# from line_profiler_pycharm import profile - class HasPositionProtocol(Protocol): """Protocol for continuous space position holders.""" @@ -92,14 +90,13 @@ def get_neighbors_in_radius(self, radius=1, include_distance=False): include_distance: include the distance information for each neighbor """ - dists, agents = self.space.calculate_distances(self.position) - indices = np.where(dists <= radius)[0] - indices = indices[indices != self._mesa_index] + dists, agents = self.space.get_agents_in_radius(self.position, radius=radius) + logical = agents != self if include_distance: - return agents[indices], dists[indices] + return agents[logical], dists[logical] else: - return agents[indices] + return agents[logical] @overload def get_nearest_neighbors( @@ -119,13 +116,10 @@ def get_nearest_neighbors(self, k=1, include_distance=False): include_distance: include the distance information for each neighbor """ - dists, agents = self.space.calculate_distances(self.position) - - k += 1 # the distance calculation includes self, with a distance of 0, so we remove this later - indices = np.argpartition(dists, k)[:k] - indices = indices[indices != self._mesa_index] + dists, agents = self.space.get_k_nearest_agents(self.position, k=k) + logical = agents != self if include_distance: - return agents[indices], dists[indices] + return agents[logical], dists[logical] else: - return agents[indices] + return agents[logical] From e2ae60ec63d07dc30931b3d4f5b95477776c0c46 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 4 Jan 2025 12:51:27 +0000 Subject: [PATCH 53/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/experimental/continuous_space/continuous_space.py | 2 +- mesa/experimental/continuous_space/continuous_space_agents.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index cab13e6791d..f5cff1bfa48 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -186,7 +186,7 @@ def calculate_distances(self, point, agents=None) -> tuple[np.ndarray, np.ndarra # would be good to test again once numpy 2.x is default in anaconda. # dists = np.linalg.norm(delta, axis=1) delta_T = delta.T - dists = np.sqrt(np.einsum('ij,ij->j', delta_T, delta_T)) + dists = np.sqrt(np.einsum("ij,ij->j", delta_T, delta_T)) else: dists = cdist(point[np.newaxis, :], positions)[:, 0] return dists, agents diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index aea76752174..c0d01256230 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -9,6 +9,7 @@ from mesa.agent import Agent from mesa.experimental.continuous_space import ContinuousSpace + class HasPositionProtocol(Protocol): """Protocol for continuous space position holders.""" From 45d5744a8bd0c705adbf7984131f0f710a69e794 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sat, 4 Jan 2025 13:51:33 +0100 Subject: [PATCH 54/84] ruff --- mesa/experimental/continuous_space/__init__.py | 2 +- mesa/experimental/continuous_space/continuous_space_agents.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mesa/experimental/continuous_space/__init__.py b/mesa/experimental/continuous_space/__init__.py index f9abc541413..13425cc0b8c 100644 --- a/mesa/experimental/continuous_space/__init__.py +++ b/mesa/experimental/continuous_space/__init__.py @@ -1,4 +1,4 @@ -"""Continous space support.""" +"""Continuous space support.""" from mesa.experimental.continuous_space.continuous_space import ContinuousSpace from mesa.experimental.continuous_space.continuous_space_agents import ( diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index c0d01256230..82eefc8a896 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -44,12 +44,12 @@ def position(self, value: np.ndarray) -> None: @property def pos(self): # noqa: D102 - # just here for compatability with solara_viz. + # just here for compatibility with solara_viz. return self.position @pos.setter def pos(self, value): - # just here for compatability solara_viz. + # just here for compatibility solara_viz. pass def __init__(self, space: ContinuousSpace, model): From cf94c46c599064a8a89413bd77d811b28a762896 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sat, 4 Jan 2025 13:57:37 +0100 Subject: [PATCH 55/84] cleaning --- mesa/experimental/continuous_space/continuous_space.py | 6 +++--- .../continuous_space/continuous_space_agents.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index f5cff1bfa48..f3588304de9 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -67,7 +67,7 @@ def __init__( self.torus: bool = torus - self._agent_positions: np.array = np.zeros( + self._agent_positions: np.array = np.empty( (n_agents, self.dimensions.shape[0]), dtype=float ) self._agents: np.array = np.zeros((n_agents,), dtype=object) @@ -104,7 +104,7 @@ def _get_index_for_agent(self, agent: Agent) -> int: self._agent_positions = np.vstack( [ self._agent_positions, - np.zeros( + np.empty( (n, self.dimensions.shape[0]), ), ] @@ -185,7 +185,7 @@ def calculate_distances(self, point, agents=None) -> tuple[np.ndarray, np.ndarra # Also, this might be highly numpy version dependent and even cpu architecture dependent. # would be good to test again once numpy 2.x is default in anaconda. # dists = np.linalg.norm(delta, axis=1) - delta_T = delta.T + delta_T = delta.T # noqa: N806 dists = np.sqrt(np.einsum("ij,ij->j", delta_T, delta_T)) else: dists = cdist(point[np.newaxis, :], positions)[:, 0] diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index 82eefc8a896..977f35a5902 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -63,7 +63,7 @@ def __init__(self, space: ContinuousSpace, model): super().__init__(model) self.space: ContinuousSpace = space self._mesa_index = self.space._get_index_for_agent(self) - self.position[:] = np.nan + # self.position[:] = np.nan def remove(self) -> None: """Remove and delete the agent from the model.""" From c5a53c6d1935026965eef290c91fcdc1d88b6328 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sun, 5 Jan 2025 11:24:36 +0100 Subject: [PATCH 56/84] fine tuning --- mesa/examples/basic/boid_flockers/agents.py | 3 +- .../continuous_space/continuous_space.py | 13 +++--- .../continuous_space_agents.py | 36 +++------------- .../continuous_space/for_development.py | 42 +++++++++++-------- mesa/space.py | 2 + 5 files changed, 38 insertions(+), 58 deletions(-) diff --git a/mesa/examples/basic/boid_flockers/agents.py b/mesa/examples/basic/boid_flockers/agents.py index 58151d442a0..24f57840c10 100644 --- a/mesa/examples/basic/boid_flockers/agents.py +++ b/mesa/examples/basic/boid_flockers/agents.py @@ -62,8 +62,7 @@ def __init__( def step(self): """Get the Boid's neighbors, compute the new vector, and move accordingly.""" neighbors, distances = self.get_neighbors_in_radius( - radius=self.vision, include_distance=True - ) + radius=self.vision) self.neighbors = neighbors # If no neighbors, maintain current direction diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index f3588304de9..4ecdb3519a4 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -9,7 +9,6 @@ from mesa.agent import Agent, AgentSet - class ContinuousSpace: """Continuous space where each agent can have an arbitrary position.""" @@ -181,14 +180,12 @@ def calculate_distances(self, point, agents=None) -> tuple[np.ndarray, np.ndarra if self.torus: delta = np.abs(positions - point) delta = np.minimum(delta, self.size - delta, out=delta) - # this is obscure: see https://stackoverflow.com/questions/7741878/how-to-apply-numpy-linalg-norm-to-each-row-of-a-matrix - # Also, this might be highly numpy version dependent and even cpu architecture dependent. - # would be good to test again once numpy 2.x is default in anaconda. - # dists = np.linalg.norm(delta, axis=1) - delta_T = delta.T # noqa: N806 - dists = np.sqrt(np.einsum("ij,ij->j", delta_T, delta_T)) + dists = delta[:, 0]**2 + for i in range(1, self.ndims): + dists += delta[:,i]**2 + dists = np.sqrt(dists) else: - dists = cdist(point[np.newaxis, :], positions)[:, 0] + dists = cdist(point[np.newaxis, :], positions)[0, :] return dists, agents def get_agents_in_radius(self, point, radius=1) -> tuple[np.ndarray, np.ndarray]: diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index 977f35a5902..b6dc696d18b 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -72,18 +72,7 @@ def remove(self) -> None: self._mesa_index = None self.space = None - @overload - def get_neighbors_in_radius( - self, radius: int = 1, include_distance: bool = False - ) -> np.ndarray[ContinuousSpaceAgent]: ... - - @overload - def get_neighbors_in_radius( - self, radius=1, include_distance: bool = True - ) -> tuple[np.ndarray[ContinuousSpaceAgent], np.ndarray[float]]: ... - - # @profile - def get_neighbors_in_radius(self, radius=1, include_distance=False): + def get_neighbors_in_radius(self, radius=1): """Get neighbors within radius. Args: @@ -94,22 +83,9 @@ def get_neighbors_in_radius(self, radius=1, include_distance=False): dists, agents = self.space.get_agents_in_radius(self.position, radius=radius) logical = agents != self - if include_distance: - return agents[logical], dists[logical] - else: - return agents[logical] + return agents[logical], dists[logical] - @overload - def get_nearest_neighbors( - self, k: int = 1, include_distance: bool = False - ) -> list[ContinuousSpaceAgent]: ... - - @overload - def get_nearest_neighbors( - self, k=1, include_distance: bool = True - ) -> list[tuple[ContinuousSpaceAgent, float]]: ... - - def get_nearest_neighbors(self, k=1, include_distance=False): + def get_nearest_neighbors(self, k=1): """Get neighbors within radius. Args: @@ -120,7 +96,5 @@ def get_nearest_neighbors(self, k=1, include_distance=False): dists, agents = self.space.get_k_nearest_agents(self.position, k=k) logical = agents != self - if include_distance: - return agents[logical], dists[logical] - else: - return agents[logical] + return agents[logical], dists[logical] + diff --git a/mesa/experimental/continuous_space/for_development.py b/mesa/experimental/continuous_space/for_development.py index 3e42959172c..b51fc84d593 100644 --- a/mesa/experimental/continuous_space/for_development.py +++ b/mesa/experimental/continuous_space/for_development.py @@ -1,26 +1,34 @@ """to be removed once further into the development.""" -from random import Random -import numpy as np -from continuous_space import ContinuousSpace -from continuous_space_agents import ContinuousSpaceAgent +if __name__ == "__main__": + from mesa import Model, Agent + from mesa.space import ContinuousSpace as OldStyleSpace -from mesa import Model + from mesa.experimental.continuous_space import ContinuousSpace as NewStyleSpace + from mesa.experimental.continuous_space import ContinuousSpaceAgent -if __name__ == "__main__": - dimensions = [[0, 1], [0, 1]] + n = 400 + + model = Model(rng=42) + space = OldStyleSpace(100, 100, torus=True) + + positions = model.rng.random((n, 2)) * n + for pos in positions: + agent = Agent(model) + space.place_agent(agent, pos) + + for _ in range(100): + space.get_neighbors([50,50], radius=5, include_center=False) - model = Model(seed=42) - space = ContinuousSpace(dimensions, random=Random(42), torus=True) - for _ in range(2): - agent = ContinuousSpaceAgent( - space, - model, - ) - agent.position = [agent.random.random(), agent.random.random()] + model = Model(rng=42) + space = NewStyleSpace([[0,100], [0,100]], torus=True, random=model.random, n_agents=n) - distances = space.calculate_distances(np.asarray([0.5, 0.5])) + positions = model.rng.random((n, 2)) * n + for pos in positions: + agent = ContinuousSpaceAgent(space, model) + agent.position = pos - print("blaat") + for _ in range(100): + space.get_agents_in_radius([50, 50], radius=5) \ No newline at end of file diff --git a/mesa/space.py b/mesa/space.py index 6e15426bf34..401181d1e5a 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -25,6 +25,7 @@ # Remove this __future__ import once the oldest supported Python is 3.10 from __future__ import annotations + import collections import contextlib import inspect @@ -1403,6 +1404,7 @@ def remove_agent(self, agent: Agent) -> None: self._invalidate_agent_cache() agent.pos = None + def get_neighbors( self, pos: FloatCoordinate, radius: float, include_center: bool = True ) -> list[Agent]: From a64ac3874155267c739717d2b8bcd422dd4ad222 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 5 Jan 2025 10:24:51 +0000 Subject: [PATCH 57/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/examples/basic/boid_flockers/agents.py | 3 +-- .../continuous_space/continuous_space.py | 5 +++-- .../continuous_space/continuous_space_agents.py | 3 +-- .../continuous_space/for_development.py | 15 +++++++-------- mesa/space.py | 2 -- 5 files changed, 12 insertions(+), 16 deletions(-) diff --git a/mesa/examples/basic/boid_flockers/agents.py b/mesa/examples/basic/boid_flockers/agents.py index 24f57840c10..901d822d859 100644 --- a/mesa/examples/basic/boid_flockers/agents.py +++ b/mesa/examples/basic/boid_flockers/agents.py @@ -61,8 +61,7 @@ def __init__( def step(self): """Get the Boid's neighbors, compute the new vector, and move accordingly.""" - neighbors, distances = self.get_neighbors_in_radius( - radius=self.vision) + neighbors, distances = self.get_neighbors_in_radius(radius=self.vision) self.neighbors = neighbors # If no neighbors, maintain current direction diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 4ecdb3519a4..bce944f2b05 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -9,6 +9,7 @@ from mesa.agent import Agent, AgentSet + class ContinuousSpace: """Continuous space where each agent can have an arbitrary position.""" @@ -180,9 +181,9 @@ def calculate_distances(self, point, agents=None) -> tuple[np.ndarray, np.ndarra if self.torus: delta = np.abs(positions - point) delta = np.minimum(delta, self.size - delta, out=delta) - dists = delta[:, 0]**2 + dists = delta[:, 0] ** 2 for i in range(1, self.ndims): - dists += delta[:,i]**2 + dists += delta[:, i] ** 2 dists = np.sqrt(dists) else: dists = cdist(point[np.newaxis, :], positions)[0, :] diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index b6dc696d18b..20977ec74ec 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Protocol, overload +from typing import Protocol import numpy as np @@ -97,4 +97,3 @@ def get_nearest_neighbors(self, k=1): logical = agents != self return agents[logical], dists[logical] - diff --git a/mesa/experimental/continuous_space/for_development.py b/mesa/experimental/continuous_space/for_development.py index b51fc84d593..525d3246641 100644 --- a/mesa/experimental/continuous_space/for_development.py +++ b/mesa/experimental/continuous_space/for_development.py @@ -1,12 +1,10 @@ """to be removed once further into the development.""" - if __name__ == "__main__": - from mesa import Model, Agent - from mesa.space import ContinuousSpace as OldStyleSpace - + from mesa import Agent, Model from mesa.experimental.continuous_space import ContinuousSpace as NewStyleSpace from mesa.experimental.continuous_space import ContinuousSpaceAgent + from mesa.space import ContinuousSpace as OldStyleSpace n = 400 @@ -19,11 +17,12 @@ space.place_agent(agent, pos) for _ in range(100): - space.get_neighbors([50,50], radius=5, include_center=False) - + space.get_neighbors([50, 50], radius=5, include_center=False) model = Model(rng=42) - space = NewStyleSpace([[0,100], [0,100]], torus=True, random=model.random, n_agents=n) + space = NewStyleSpace( + [[0, 100], [0, 100]], torus=True, random=model.random, n_agents=n + ) positions = model.rng.random((n, 2)) * n for pos in positions: @@ -31,4 +30,4 @@ agent.position = pos for _ in range(100): - space.get_agents_in_radius([50, 50], radius=5) \ No newline at end of file + space.get_agents_in_radius([50, 50], radius=5) diff --git a/mesa/space.py b/mesa/space.py index 401181d1e5a..6e15426bf34 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -25,7 +25,6 @@ # Remove this __future__ import once the oldest supported Python is 3.10 from __future__ import annotations - import collections import contextlib import inspect @@ -1404,7 +1403,6 @@ def remove_agent(self, agent: Agent) -> None: self._invalidate_agent_cache() agent.pos = None - def get_neighbors( self, pos: FloatCoordinate, radius: float, include_center: bool = True ) -> list[Agent]: From 6303863394eead8321dc3abb37ba43ce73a6f7d2 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sun, 5 Jan 2025 17:16:32 +0100 Subject: [PATCH 58/84] tweak --- mesa/examples/basic/boid_flockers/agents.py | 6 +++--- .../experimental/continuous_space/continuous_space.py | 8 ++++++-- .../continuous_space/continuous_space_agents.py | 11 ++++++----- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/mesa/examples/basic/boid_flockers/agents.py b/mesa/examples/basic/boid_flockers/agents.py index 901d822d859..2473d1f8410 100644 --- a/mesa/examples/basic/boid_flockers/agents.py +++ b/mesa/examples/basic/boid_flockers/agents.py @@ -71,14 +71,14 @@ def step(self): delta = self.space.calculate_difference_vector(self.position, agents=neighbors) - cohere_vector = np.sum(delta, axis=0) * self.cohere_factor + cohere_vector = delta.sum(axis=0) * self.cohere_factor separation_vector = ( -1 - * np.sum(delta[distances < self.separation], axis=0) + * delta[distances < self.separation].sum(axis=0) * self.separate_factor ) match_vector = ( - np.sum(np.asarray([n.direction for n in neighbors]), axis=0) + np.asarray([n.direction for n in neighbors]).sum(axis=0) * self.match_factor ) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index bce944f2b05..9a93aedcae8 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -74,6 +74,7 @@ def __init__( self._positions_in_use: np.array = np.zeros( (n_agents,), dtype=bool ) # effectively a mask over _agent_positions + self._is_full = False self._index_to_agent: dict[int, Agent] = {} self._agent_to_index: dict[Agent, int | None] = {} @@ -116,9 +117,11 @@ def _get_index_for_agent(self, agent: Agent) -> int: index = np.where(~self._positions_in_use)[0][0] self._positions_in_use[index] = True + self._agents[index] = agent self._agent_to_index[agent] = index self._index_to_agent[index] = agent + self.active_agents = self._agents[self._positions_in_use] self._is_full = bool(np.all(self._positions_in_use)) @@ -179,8 +182,10 @@ def calculate_distances(self, point, agents=None) -> tuple[np.ndarray, np.ndarra agents = np.asarray(agents) if self.torus: - delta = np.abs(positions - point) + delta = np.abs(point-positions) delta = np.minimum(delta, self.size - delta, out=delta) + + # + is much faster than np.sum or array.sum dists = delta[:, 0] ** 2 for i in range(1, self.ndims): dists += delta[:, i] ** 2 @@ -193,7 +198,6 @@ def get_agents_in_radius(self, point, radius=1) -> tuple[np.ndarray, np.ndarray] """Return the agents and their distances within a radius for the point.""" distances, agents = self.calculate_distances(point) logical = distances <= radius - return distances[logical], agents[logical] def get_k_nearest_agents(self, point, k=1) -> tuple[np.ndarray, np.ndarray]: diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index 20977ec74ec..a0ba7a370d1 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -72,25 +72,26 @@ def remove(self) -> None: self._mesa_index = None self.space = None - def get_neighbors_in_radius(self, radius=1): + def get_neighbors_in_radius(self, radius:float=1) -> tuple[np.ndarray, np.ndarray]: """Get neighbors within radius. Args: radius: radius within which to look for neighbors - include_distance: include the distance information for each neighbor """ dists, agents = self.space.get_agents_in_radius(self.position, radius=radius) - logical = agents != self + if dists.size == 0: + return dists, agents + + logical = np.asarray([True if agent is not self else False for agent in agents]) return agents[logical], dists[logical] - def get_nearest_neighbors(self, k=1): + def get_nearest_neighbors(self, k:int=1) -> tuple[np.ndarray, np.ndarray]: """Get neighbors within radius. Args: k: the number of nearest neighbors to return - include_distance: include the distance information for each neighbor """ dists, agents = self.space.get_k_nearest_agents(self.position, k=k) From 2b8ed2b11bb59b6f8b2bf8bfdeaa84df720c5f4a Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sun, 5 Jan 2025 20:03:26 +0100 Subject: [PATCH 59/84] substantial update --- .../continuous_space/continuous_space.py | 87 +++++++++---------- .../continuous_space_agents.py | 6 +- tests/test_continuous_space.py | 4 +- 3 files changed, 45 insertions(+), 52 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 9a93aedcae8..60c6e0e18b7 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -71,79 +71,72 @@ def __init__( (n_agents, self.dimensions.shape[0]), dtype=float ) self._agents: np.array = np.zeros((n_agents,), dtype=object) - self._positions_in_use: np.array = np.zeros( - (n_agents,), dtype=bool - ) # effectively a mask over _agent_positions - self._is_full = False + + self.active_agents: np.array # a view on _agents containing all active agents + self.agent_positions: np.array # a view on _agent_positions containing all active positions + + self._n_agents = 0 self._index_to_agent: dict[int, Agent] = {} self._agent_to_index: dict[Agent, int | None] = {} + @property def agents(self) -> AgentSet: """Return an AgentSet with the agents in the space.""" - return AgentSet(self._agents[self._positions_in_use], random=self.random) + return AgentSet(self.active_agents, random=self.random) - def _get_index_for_agent(self, agent: Agent) -> int: + + def _add_agent(self, agent: Agent) -> int: """Helper method to get the index for the agent. This method manages the numpy array with the agent positions and ensuring it is enlarged if and when needed. """ - try: - return self._agent_to_index[agent] - except KeyError: - indices = np.where(~self._positions_in_use)[0] - - if indices.size > 0: - index = indices[0] - else: - # we are out of space - fraction = 0.2 # we add 20% Fixme - n = int(round(fraction * self._agent_positions.shape[0])) - self._agent_positions = np.vstack( - [ - self._agent_positions, - np.empty( - (n, self.dimensions.shape[0]), - ), - ] - ) - self._positions_in_use = np.hstack( - [self._positions_in_use, np.zeros((n,), dtype=bool)] - ) - self._agents = np.hstack([self._agents, np.zeros((n,), dtype=object)]) - index = np.where(~self._positions_in_use)[0][0] - - self._positions_in_use[index] = True + index = self._n_agents + self._n_agents += 1 + + if self._agent_positions.shape[0] <= index: + # we are out of space + fraction = 0.2 # we add 20% Fixme + n = int(round(fraction * self._n_agents)) + self._agent_positions = np.vstack( + [ + self._agent_positions, + np.empty( + (n, self.dimensions.shape[0]), + ), + ] + ) + self._agents = np.hstack([self._agents, np.zeros((n,), dtype=object)]) self._agents[index] = agent self._agent_to_index[agent] = index self._index_to_agent[index] = agent - self.active_agents = self._agents[self._positions_in_use] - self._is_full = bool(np.all(self._positions_in_use)) + # we want to maintain a view rather than a copy on the active agents and positions + # this is essential for the performance of the rest of this code + self.active_agents = self._agents[0:self._n_agents] + self.agent_positions = self._agent_positions[0:self._n_agents] return index - @property - def agent_positions(self): - """Return the positions of the agents in the space.""" - if self._is_full: - return self._agent_positions - - return self._agent_positions[self._positions_in_use] - def _remove_agent(self, agent: Agent) -> None: """Remove an agent from the space.""" - index = self._get_index_for_agent(agent) + index = self._agent_to_index[agent] self._agent_to_index.pop(agent, None) self._index_to_agent.pop(index, None) - self._positions_in_use[index] = False - self._agents[index] = None - self.active_agents = self._agents[self._positions_in_use] - self._is_full = False + + for i in range(index, self._n_agents): + self._agent_to_index[agent] = i + self._index_to_agent[i] = agent + + # we move all data below the removed agent one row up + self._agent_positions[index:self._n_agents-1] = self._agent_positions[index+1:self._n_agents] + self._n_agents -= 1 + self.active_agents = self._agents[0:self._n_agents] + self.agent_positions = self._agent_positions[0:self._n_agents] def calculate_difference_vector(self, point: np.ndarray, agents=None) -> np.ndarray: """Calculate the difference vector between the point and all agents.""" diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index a0ba7a370d1..a9de73bf42d 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -30,7 +30,7 @@ class ContinuousSpaceAgent(Agent): @property def position(self) -> np.ndarray: """Position of the agent.""" - return self.space._agent_positions[self._mesa_index] + return self.space.agent_positions[self.space._agent_to_index[self]] @position.setter def position(self, value: np.ndarray) -> None: @@ -40,7 +40,7 @@ def position(self, value: np.ndarray) -> None: else: raise ValueError(f"point {value} is outside the bounds of the space") - self.space._agent_positions[self._mesa_index] = value + self.space.agent_positions[self.space._agent_to_index[self]] = value @property def pos(self): # noqa: D102 @@ -62,7 +62,7 @@ def __init__(self, space: ContinuousSpace, model): """ super().__init__(model) self.space: ContinuousSpace = space - self._mesa_index = self.space._get_index_for_agent(self) + self.space._add_agent(self) # self.position[:] = np.nan def remove(self) -> None: diff --git a/tests/test_continuous_space.py b/tests/test_continuous_space.py index a28de53351e..55193c2c33e 100644 --- a/tests/test_continuous_space.py +++ b/tests/test_continuous_space.py @@ -69,7 +69,7 @@ def test_continuous_agent(): assert space.agent_positions.shape == (10, 2) for agent in space.agents: a = agent.position - b = space._agent_positions[space._get_index_for_agent(agent)] + b = space._agent_positions[space._agent_to_index[agent]] assert np.all(a == b) # add more agents, triggering a resizeing of the array @@ -80,7 +80,7 @@ def test_continuous_agent(): assert space.agent_positions.shape == (110, 2) for agent in space.agents: a = agent.position - b = space._agent_positions[space._get_index_for_agent(agent)] + b = space._agent_positions[space._agent_to_index[agent]] assert np.all(a == b) # remove all agents and check if the view is updated throughout correctly From 4e9a39ba688673b0d55cdc44c0687899a21085fc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 5 Jan 2025 19:03:34 +0000 Subject: [PATCH 60/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/examples/basic/boid_flockers/agents.py | 7 ++----- .../continuous_space/continuous_space.py | 20 ++++++++++--------- .../continuous_space_agents.py | 6 ++++-- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/mesa/examples/basic/boid_flockers/agents.py b/mesa/examples/basic/boid_flockers/agents.py index 2473d1f8410..008bc516666 100644 --- a/mesa/examples/basic/boid_flockers/agents.py +++ b/mesa/examples/basic/boid_flockers/agents.py @@ -73,13 +73,10 @@ def step(self): cohere_vector = delta.sum(axis=0) * self.cohere_factor separation_vector = ( - -1 - * delta[distances < self.separation].sum(axis=0) - * self.separate_factor + -1 * delta[distances < self.separation].sum(axis=0) * self.separate_factor ) match_vector = ( - np.asarray([n.direction for n in neighbors]).sum(axis=0) - * self.match_factor + np.asarray([n.direction for n in neighbors]).sum(axis=0) * self.match_factor ) # Update direction based on the three behaviors diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 60c6e0e18b7..e2991f4114a 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -73,20 +73,20 @@ def __init__( self._agents: np.array = np.zeros((n_agents,), dtype=object) self.active_agents: np.array # a view on _agents containing all active agents - self.agent_positions: np.array # a view on _agent_positions containing all active positions + self.agent_positions: ( + np.array + ) # a view on _agent_positions containing all active positions self._n_agents = 0 self._index_to_agent: dict[int, Agent] = {} self._agent_to_index: dict[Agent, int | None] = {} - @property def agents(self) -> AgentSet: """Return an AgentSet with the agents in the space.""" return AgentSet(self.active_agents, random=self.random) - def _add_agent(self, agent: Agent) -> int: """Helper method to get the index for the agent. @@ -117,8 +117,8 @@ def _add_agent(self, agent: Agent) -> int: # we want to maintain a view rather than a copy on the active agents and positions # this is essential for the performance of the rest of this code - self.active_agents = self._agents[0:self._n_agents] - self.agent_positions = self._agent_positions[0:self._n_agents] + self.active_agents = self._agents[0 : self._n_agents] + self.agent_positions = self._agent_positions[0 : self._n_agents] return index @@ -133,10 +133,12 @@ def _remove_agent(self, agent: Agent) -> None: self._index_to_agent[i] = agent # we move all data below the removed agent one row up - self._agent_positions[index:self._n_agents-1] = self._agent_positions[index+1:self._n_agents] + self._agent_positions[index : self._n_agents - 1] = self._agent_positions[ + index + 1 : self._n_agents + ] self._n_agents -= 1 - self.active_agents = self._agents[0:self._n_agents] - self.agent_positions = self._agent_positions[0:self._n_agents] + self.active_agents = self._agents[0 : self._n_agents] + self.agent_positions = self._agent_positions[0 : self._n_agents] def calculate_difference_vector(self, point: np.ndarray, agents=None) -> np.ndarray: """Calculate the difference vector between the point and all agents.""" @@ -175,7 +177,7 @@ def calculate_distances(self, point, agents=None) -> tuple[np.ndarray, np.ndarra agents = np.asarray(agents) if self.torus: - delta = np.abs(point-positions) + delta = np.abs(point - positions) delta = np.minimum(delta, self.size - delta, out=delta) # + is much faster than np.sum or array.sum diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index a9de73bf42d..34c372bf725 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -72,7 +72,9 @@ def remove(self) -> None: self._mesa_index = None self.space = None - def get_neighbors_in_radius(self, radius:float=1) -> tuple[np.ndarray, np.ndarray]: + def get_neighbors_in_radius( + self, radius: float = 1 + ) -> tuple[np.ndarray, np.ndarray]: """Get neighbors within radius. Args: @@ -87,7 +89,7 @@ def get_neighbors_in_radius(self, radius:float=1) -> tuple[np.ndarray, np.ndarra logical = np.asarray([True if agent is not self else False for agent in agents]) return agents[logical], dists[logical] - def get_nearest_neighbors(self, k:int=1) -> tuple[np.ndarray, np.ndarray]: + def get_nearest_neighbors(self, k: int = 1) -> tuple[np.ndarray, np.ndarray]: """Get neighbors within radius. Args: From 102492a8c37d6e153f14a43f5eeb5c65f46db8b2 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sun, 5 Jan 2025 21:38:50 +0100 Subject: [PATCH 61/84] switch active agents to list --- mesa/examples/basic/boid_flockers/agents.py | 2 +- .../continuous_space/continuous_space.py | 35 +++++++++++-------- .../continuous_space_agents.py | 15 ++++---- 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/mesa/examples/basic/boid_flockers/agents.py b/mesa/examples/basic/boid_flockers/agents.py index 008bc516666..a7f2edffe49 100644 --- a/mesa/examples/basic/boid_flockers/agents.py +++ b/mesa/examples/basic/boid_flockers/agents.py @@ -65,7 +65,7 @@ def step(self): self.neighbors = neighbors # If no neighbors, maintain current direction - if neighbors.size == 0: + if not neighbors: self.position += self.direction * self.speed return diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index e2991f4114a..2320777e33c 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -3,6 +3,7 @@ import warnings from collections.abc import Sequence from random import Random +from itertools import compress import numpy as np from scipy.spatial.distance import cdist @@ -70,9 +71,9 @@ def __init__( self._agent_positions: np.array = np.empty( (n_agents, self.dimensions.shape[0]), dtype=float ) - self._agents: np.array = np.zeros((n_agents,), dtype=object) + # self._agents: np.array = np.zeros((n_agents,), dtype=object) - self.active_agents: np.array # a view on _agents containing all active agents + self.active_agents = [] self.agent_positions: ( np.array ) # a view on _agent_positions containing all active positions @@ -109,15 +110,14 @@ def _add_agent(self, agent: Agent) -> int: ), ] ) - self._agents = np.hstack([self._agents, np.zeros((n,), dtype=object)]) - self._agents[index] = agent + self._agent_to_index[agent] = index self._index_to_agent[index] = agent # we want to maintain a view rather than a copy on the active agents and positions # this is essential for the performance of the rest of this code - self.active_agents = self._agents[0 : self._n_agents] + self.active_agents.append(agent) self.agent_positions = self._agent_positions[0 : self._n_agents] return index @@ -127,17 +127,22 @@ def _remove_agent(self, agent: Agent) -> None: index = self._agent_to_index[agent] self._agent_to_index.pop(agent, None) self._index_to_agent.pop(index, None) + try: + del self.active_agents[index] + except IndexError as e: + raise e - for i in range(index, self._n_agents): - self._agent_to_index[agent] = i - self._index_to_agent[i] = agent + # we update all indices + for agent in self.active_agents[index::]: + old_index = self._agent_to_index[agent] + self._agent_to_index[agent] = old_index-1 + self._index_to_agent[old_index-1] = agent # we move all data below the removed agent one row up self._agent_positions[index : self._n_agents - 1] = self._agent_positions[ index + 1 : self._n_agents ] self._n_agents -= 1 - self.active_agents = self._agents[0 : self._n_agents] self.agent_positions = self._agent_positions[0 : self._n_agents] def calculate_difference_vector(self, point: np.ndarray, agents=None) -> np.ndarray: @@ -165,7 +170,7 @@ def calculate_difference_vector(self, point: np.ndarray, agents=None) -> np.ndar return delta - def calculate_distances(self, point, agents=None) -> tuple[np.ndarray, np.ndarray]: + def calculate_distances(self, point, agents=None) -> tuple[np.ndarray, list]: """Calculate the distance between the point and all agents.""" point = np.asanyarray(point) @@ -189,19 +194,21 @@ def calculate_distances(self, point, agents=None) -> tuple[np.ndarray, np.ndarra dists = cdist(point[np.newaxis, :], positions)[0, :] return dists, agents - def get_agents_in_radius(self, point, radius=1) -> tuple[np.ndarray, np.ndarray]: + def get_agents_in_radius(self, point, radius=1) -> tuple[np.ndarray, list]: """Return the agents and their distances within a radius for the point.""" distances, agents = self.calculate_distances(point) logical = distances <= radius - return distances[logical], agents[logical] + agents = list(compress(agents, logical)) + return distances[logical], agents - def get_k_nearest_agents(self, point, k=1) -> tuple[np.ndarray, np.ndarray]: + def get_k_nearest_agents(self, point, k=1) -> tuple[np.ndarray,list]: """Return the k nearest agents and their distances to the point.""" dists, agents = self.calculate_distances(point) k += 1 # the distance calculation includes self, with a distance of 0, so we remove this later indices = np.argpartition(dists, k)[:k] - return dists[indices], agents[indices] + agents = [agents[i] for i in indices] + return dists[indices], agents def in_bounds(self, point) -> bool: """Check if point is inside the bounds of the space.""" diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index 34c372bf725..a15f699d20f 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -2,6 +2,8 @@ from __future__ import annotations +from itertools import compress + from typing import Protocol import numpy as np @@ -74,7 +76,7 @@ def remove(self) -> None: def get_neighbors_in_radius( self, radius: float = 1 - ) -> tuple[np.ndarray, np.ndarray]: + ) -> tuple[list, np.ndarray]: """Get neighbors within radius. Args: @@ -87,9 +89,10 @@ def get_neighbors_in_radius( return dists, agents logical = np.asarray([True if agent is not self else False for agent in agents]) - return agents[logical], dists[logical] + agents = list(compress(agents, logical)) + return agents, dists[logical] - def get_nearest_neighbors(self, k: int = 1) -> tuple[np.ndarray, np.ndarray]: + def get_nearest_neighbors(self, k: int = 1) -> tuple[list, np.ndarray]: """Get neighbors within radius. Args: @@ -97,6 +100,6 @@ def get_nearest_neighbors(self, k: int = 1) -> tuple[np.ndarray, np.ndarray]: """ dists, agents = self.space.get_k_nearest_agents(self.position, k=k) - logical = agents != self - - return agents[logical], dists[logical] + logical = np.asarray([True if agent is not self else False for agent in agents]) + agents = list(compress(agents, logical)) + return agents, dists[logical] From 037bc3bd765ef7af95131e471000b45860a7604c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 5 Jan 2025 20:38:59 +0000 Subject: [PATCH 62/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/experimental/continuous_space/continuous_space.py | 9 ++++----- .../continuous_space/continuous_space_agents.py | 5 +---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 2320777e33c..548cb873fc9 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -2,8 +2,8 @@ import warnings from collections.abc import Sequence -from random import Random from itertools import compress +from random import Random import numpy as np from scipy.spatial.distance import cdist @@ -111,7 +111,6 @@ def _add_agent(self, agent: Agent) -> int: ] ) - self._agent_to_index[agent] = index self._index_to_agent[index] = agent @@ -135,8 +134,8 @@ def _remove_agent(self, agent: Agent) -> None: # we update all indices for agent in self.active_agents[index::]: old_index = self._agent_to_index[agent] - self._agent_to_index[agent] = old_index-1 - self._index_to_agent[old_index-1] = agent + self._agent_to_index[agent] = old_index - 1 + self._index_to_agent[old_index - 1] = agent # we move all data below the removed agent one row up self._agent_positions[index : self._n_agents - 1] = self._agent_positions[ @@ -201,7 +200,7 @@ def get_agents_in_radius(self, point, radius=1) -> tuple[np.ndarray, list]: agents = list(compress(agents, logical)) return distances[logical], agents - def get_k_nearest_agents(self, point, k=1) -> tuple[np.ndarray,list]: + def get_k_nearest_agents(self, point, k=1) -> tuple[np.ndarray, list]: """Return the k nearest agents and their distances to the point.""" dists, agents = self.calculate_distances(point) diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index a15f699d20f..3533b56c917 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -3,7 +3,6 @@ from __future__ import annotations from itertools import compress - from typing import Protocol import numpy as np @@ -74,9 +73,7 @@ def remove(self) -> None: self._mesa_index = None self.space = None - def get_neighbors_in_radius( - self, radius: float = 1 - ) -> tuple[list, np.ndarray]: + def get_neighbors_in_radius(self, radius: float = 1) -> tuple[list, np.ndarray]: """Get neighbors within radius. Args: From 3a4fa51c053625e89f612a12caf9b36264c10376 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sun, 5 Jan 2025 21:54:21 +0100 Subject: [PATCH 63/84] an additional test --- tests/test_continuous_space.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/test_continuous_space.py b/tests/test_continuous_space.py index 55193c2c33e..7cc7dfe44f6 100644 --- a/tests/test_continuous_space.py +++ b/tests/test_continuous_space.py @@ -64,27 +64,34 @@ def test_continuous_agent(): for _ in range(10): agent = ContinuousSpaceAgent(space, model) - agent.position = [agent.random.random(), agent.random.random()] + position = [agent.random.random(), agent.random.random()] + agent.position = position + agent.coordinate = position assert space.agent_positions.shape == (10, 2) for agent in space.agents: a = agent.position b = space._agent_positions[space._agent_to_index[agent]] assert np.all(a == b) + assert np.all(agent.position == agent.coordinate) # add more agents, triggering a resizeing of the array for _ in range(100): agent = ContinuousSpaceAgent(space, model) - agent.position = [agent.random.random(), agent.random.random()] + position = [agent.random.random(), agent.random.random()] + agent.position = position + agent.coordinate = position assert space.agent_positions.shape == (110, 2) for agent in space.agents: a = agent.position b = space._agent_positions[space._agent_to_index[agent]] assert np.all(a == b) + assert np.all(agent.position == agent.coordinate) # remove all agents and check if the view is updated throughout correctly for i, agent in enumerate(space.agents): + assert np.all(agent.position == agent.coordinate) ## check if updates of indices is correctly done agent.remove() assert space.agent_positions.shape == (110 - 1 - i, 2) From 06a8c348b0ce79c3f79b6331c72fa3e3c7d44588 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 5 Jan 2025 20:55:03 +0000 Subject: [PATCH 64/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_continuous_space.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_continuous_space.py b/tests/test_continuous_space.py index 7cc7dfe44f6..4d618fa5f7d 100644 --- a/tests/test_continuous_space.py +++ b/tests/test_continuous_space.py @@ -91,7 +91,9 @@ def test_continuous_agent(): # remove all agents and check if the view is updated throughout correctly for i, agent in enumerate(space.agents): - assert np.all(agent.position == agent.coordinate) ## check if updates of indices is correctly done + assert np.all( + agent.position == agent.coordinate + ) ## check if updates of indices is correctly done agent.remove() assert space.agent_positions.shape == (110 - 1 - i, 2) From 73b8777610ff02e85d7a37ec960aaf305e4180ba Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sun, 5 Jan 2025 21:58:58 +0100 Subject: [PATCH 65/84] Update continuous_space_agents.py --- mesa/experimental/continuous_space/continuous_space_agents.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index 3533b56c917..274ccfc3819 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -85,7 +85,7 @@ def get_neighbors_in_radius(self, radius: float = 1) -> tuple[list, np.ndarray]: if dists.size == 0: return dists, agents - logical = np.asarray([True if agent is not self else False for agent in agents]) + logical = np.asarray([agent is not self for agent in agents]) agents = list(compress(agents, logical)) return agents, dists[logical] @@ -97,6 +97,6 @@ def get_nearest_neighbors(self, k: int = 1) -> tuple[list, np.ndarray]: """ dists, agents = self.space.get_k_nearest_agents(self.position, k=k) - logical = np.asarray([True if agent is not self else False for agent in agents]) + logical = np.asarray([agent is not self for agent in agents]) agents = list(compress(agents, logical)) return agents, dists[logical] From a23b98287eb9f64b7a8ab217978dde1e59b3e2b1 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Mon, 6 Jan 2025 11:06:36 +0100 Subject: [PATCH 66/84] additional test --- .../continuous_space/continuous_space.py | 5 +-- tests/test_continuous_space.py | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 548cb873fc9..bbd42bbf8f9 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -126,10 +126,7 @@ def _remove_agent(self, agent: Agent) -> None: index = self._agent_to_index[agent] self._agent_to_index.pop(agent, None) self._index_to_agent.pop(index, None) - try: - del self.active_agents[index] - except IndexError as e: - raise e + del self.active_agents[index] # we update all indices for agent in self.active_agents[index::]: diff --git a/tests/test_continuous_space.py b/tests/test_continuous_space.py index 4d618fa5f7d..fb8f17855fb 100644 --- a/tests/test_continuous_space.py +++ b/tests/test_continuous_space.py @@ -193,6 +193,45 @@ def test_distances(): ) +def test_difference_vector(): + """Test ContinuousSpace.distance method.""" + # non torus + model = Model(seed=42) + dimensions = np.asarray([[0, 1], [0, 1]]) + space = ContinuousSpace(dimensions, torus=False, random=model.random) + + agent = ContinuousSpaceAgent(space, model) + agent.position = [0.1, 0.1] + + vector = space.calculate_difference_vector([0.1, 0.9]) + assert np.all( + vector + == [0, -0.8] + ) + + vector = space.calculate_difference_vector([0.9, 0.1]) + assert np.all( + vector + == [-0.8, 0] + ) + + # torus + model = Model(seed=42) + dimensions = np.asarray([[0, 1], [0, 1]]) + space = ContinuousSpace(dimensions, torus=True, random=model.random) + + agent = ContinuousSpaceAgent(space, model) + agent.position = [0.1, 0.1] + + vector = space.calculate_difference_vector([0.1, 0.9]) + assert np.allclose(vector, [0, 0.2]) + + vector = space.calculate_difference_vector([0.9, 0.1]) + assert np.allclose(vector, [0.2, 0]) + + vector = space.calculate_difference_vector([0.9, 0.9]) + assert np.allclose(vector, [0.2, 0.2]) + # class TestSpaceToroidal(unittest.TestCase): # """Testing a toroidal continuous space.""" # From b625e239e7d6d8a67070c0639f348cc5b73cc974 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 10:08:13 +0000 Subject: [PATCH 67/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_continuous_space.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/tests/test_continuous_space.py b/tests/test_continuous_space.py index fb8f17855fb..b758e7e4cfe 100644 --- a/tests/test_continuous_space.py +++ b/tests/test_continuous_space.py @@ -204,16 +204,10 @@ def test_difference_vector(): agent.position = [0.1, 0.1] vector = space.calculate_difference_vector([0.1, 0.9]) - assert np.all( - vector - == [0, -0.8] - ) + assert np.all(vector == [0, -0.8]) vector = space.calculate_difference_vector([0.9, 0.1]) - assert np.all( - vector - == [-0.8, 0] - ) + assert np.all(vector == [-0.8, 0]) # torus model = Model(seed=42) @@ -232,6 +226,7 @@ def test_difference_vector(): vector = space.calculate_difference_vector([0.9, 0.9]) assert np.allclose(vector, [0.2, 0.2]) + # class TestSpaceToroidal(unittest.TestCase): # """Testing a toroidal continuous space.""" # From ce0c311f703ac57a55eef5480a748886c4b196ae Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Mon, 6 Jan 2025 11:45:27 +0100 Subject: [PATCH 68/84] additional tests --- .../continuous_space/continuous_space.py | 16 +++-- .../continuous_space_agents.py | 5 +- tests/test_continuous_space.py | 58 ++++++++++++++++++- 3 files changed, 69 insertions(+), 10 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index bbd42bbf8f9..4f9604e4191 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -190,21 +190,25 @@ def calculate_distances(self, point, agents=None) -> tuple[np.ndarray, list]: dists = cdist(point[np.newaxis, :], positions)[0, :] return dists, agents - def get_agents_in_radius(self, point, radius=1) -> tuple[np.ndarray, list]: + def get_agents_in_radius(self, point, radius=1) -> tuple[list, np.ndarray]: """Return the agents and their distances within a radius for the point.""" distances, agents = self.calculate_distances(point) logical = distances <= radius agents = list(compress(agents, logical)) - return distances[logical], agents + return agents, distances[logical], - def get_k_nearest_agents(self, point, k=1) -> tuple[np.ndarray, list]: - """Return the k nearest agents and their distances to the point.""" + def get_k_nearest_agents(self, point, k=1) -> tuple[list, np.ndarray]: + """Return the k nearest agents and their distances to the point. + + Notes: + This method returns the k nearest agents and ignores ties. + + """ dists, agents = self.calculate_distances(point) - k += 1 # the distance calculation includes self, with a distance of 0, so we remove this later indices = np.argpartition(dists, k)[:k] agents = [agents[i] for i in indices] - return dists[indices], agents + return agents, dists[indices] def in_bounds(self, point) -> bool: """Check if point is inside the bounds of the space.""" diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index 274ccfc3819..9fadcff0352 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -80,7 +80,7 @@ def get_neighbors_in_radius(self, radius: float = 1) -> tuple[list, np.ndarray]: radius: radius within which to look for neighbors """ - dists, agents = self.space.get_agents_in_radius(self.position, radius=radius) + agents, dists = self.space.get_agents_in_radius(self.position, radius=radius) if dists.size == 0: return dists, agents @@ -96,7 +96,8 @@ def get_nearest_neighbors(self, k: int = 1) -> tuple[list, np.ndarray]: k: the number of nearest neighbors to return """ - dists, agents = self.space.get_k_nearest_agents(self.position, k=k) + # return includes self, so we need to get k+1 + agents, dists = self.space.get_k_nearest_agents(self.position, k=k+1) logical = np.asarray([agent is not self for agent in agents]) agents = list(compress(agents, logical)) return agents, dists[logical] diff --git a/tests/test_continuous_space.py b/tests/test_continuous_space.py index b758e7e4cfe..892e1dc0dc2 100644 --- a/tests/test_continuous_space.py +++ b/tests/test_continuous_space.py @@ -98,7 +98,7 @@ def test_continuous_agent(): assert space.agent_positions.shape == (110 - 1 - i, 2) -def test_distances(): +def test_continous_space_calculate_distances(): """Test ContinuousSpace.distance method.""" # non torus model = Model(seed=42) @@ -193,7 +193,7 @@ def test_distances(): ) -def test_difference_vector(): +def test_continous_space_difference_vector(): """Test ContinuousSpace.distance method.""" # non torus model = Model(seed=42) @@ -227,6 +227,60 @@ def test_difference_vector(): assert np.allclose(vector, [0.2, 0.2]) +def test_continuous_space_get_k_nearest_agents(): + # non torus + model = Model(seed=42) + dimensions = np.asarray([[0, 1], [0, 1]]) + space = ContinuousSpace(dimensions, torus=False, random=model.random) + + agent = ContinuousSpaceAgent(space, model) + agent.position = [0.1, 0.1] + + agent = ContinuousSpaceAgent(space, model) + agent.position = [0.1, 0.9] + + agent = ContinuousSpaceAgent(space, model) + agent.position = [0.9, 0.1] + + agent = ContinuousSpaceAgent(space, model) + agent.position = [0.9, 0.9] + + agent = ContinuousSpaceAgent(space, model) + agent.position = [0.5, 0.5] + + agents, distances = space.get_k_nearest_agents([0.1, 0.1], k=1) + assert len(agents)==1 + assert np.allclose(distances, [0,]) + + agents, distances = space.get_k_nearest_agents([0.5, 0.1], k=1) + assert len(agents)==1 + assert np.allclose(distances, [0.4, 0.4]) + + agents, distances = space.get_k_nearest_agents([0.5, 0.1], k=2) + assert len(agents)==2 + assert np.allclose(distances, [0.4, 0.4]) + + # torus + model = Model(seed=42) + dimensions = np.asarray([[0, 1], [0, 1]]) + space = ContinuousSpace(dimensions, torus=True, random=model.random) + + agent = ContinuousSpaceAgent(space, model) + agent.position = [0.1, 0.1] + + agent = ContinuousSpaceAgent(space, model) + agent.position = [0.9, 0.1] + + agent = ContinuousSpaceAgent(space, model) + agent.position = [0.9, 0.9] + + agents, distances = space.get_k_nearest_agents([0.0, 0.1], k=2) + assert len(agents)==2 + assert np.allclose(distances, [0.1, 0.1]) + +def test_continuous_space_get_agents_in_radius(): + pass + # class TestSpaceToroidal(unittest.TestCase): # """Testing a toroidal continuous space.""" # From 21c01ad835f574e4eb20fabcf3228e9325e7a17d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 10:45:36 +0000 Subject: [PATCH 69/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../continuous_space/continuous_space.py | 5 ++++- .../continuous_space/continuous_space_agents.py | 2 +- tests/test_continuous_space.py | 17 ++++++++++++----- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 4f9604e4191..96519edb10f 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -195,7 +195,10 @@ def get_agents_in_radius(self, point, radius=1) -> tuple[list, np.ndarray]: distances, agents = self.calculate_distances(point) logical = distances <= radius agents = list(compress(agents, logical)) - return agents, distances[logical], + return ( + agents, + distances[logical], + ) def get_k_nearest_agents(self, point, k=1) -> tuple[list, np.ndarray]: """Return the k nearest agents and their distances to the point. diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index 9fadcff0352..b828a3bee86 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -97,7 +97,7 @@ def get_nearest_neighbors(self, k: int = 1) -> tuple[list, np.ndarray]: """ # return includes self, so we need to get k+1 - agents, dists = self.space.get_k_nearest_agents(self.position, k=k+1) + agents, dists = self.space.get_k_nearest_agents(self.position, k=k + 1) logical = np.asarray([agent is not self for agent in agents]) agents = list(compress(agents, logical)) return agents, dists[logical] diff --git a/tests/test_continuous_space.py b/tests/test_continuous_space.py index 892e1dc0dc2..c18a2ed5a6c 100644 --- a/tests/test_continuous_space.py +++ b/tests/test_continuous_space.py @@ -249,15 +249,20 @@ def test_continuous_space_get_k_nearest_agents(): agent.position = [0.5, 0.5] agents, distances = space.get_k_nearest_agents([0.1, 0.1], k=1) - assert len(agents)==1 - assert np.allclose(distances, [0,]) + assert len(agents) == 1 + assert np.allclose( + distances, + [ + 0, + ], + ) agents, distances = space.get_k_nearest_agents([0.5, 0.1], k=1) - assert len(agents)==1 + assert len(agents) == 1 assert np.allclose(distances, [0.4, 0.4]) agents, distances = space.get_k_nearest_agents([0.5, 0.1], k=2) - assert len(agents)==2 + assert len(agents) == 2 assert np.allclose(distances, [0.4, 0.4]) # torus @@ -275,12 +280,14 @@ def test_continuous_space_get_k_nearest_agents(): agent.position = [0.9, 0.9] agents, distances = space.get_k_nearest_agents([0.0, 0.1], k=2) - assert len(agents)==2 + assert len(agents) == 2 assert np.allclose(distances, [0.1, 0.1]) + def test_continuous_space_get_agents_in_radius(): pass + # class TestSpaceToroidal(unittest.TestCase): # """Testing a toroidal continuous space.""" # From c7df9bedefb1b7e2030612eca8afe50c410b9aee Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Mon, 6 Jan 2025 14:40:26 +0100 Subject: [PATCH 70/84] Update test_continuous_space.py --- tests/test_continuous_space.py | 113 ++++++++++++++------------------- 1 file changed, 49 insertions(+), 64 deletions(-) diff --git a/tests/test_continuous_space.py b/tests/test_continuous_space.py index c18a2ed5a6c..ce63e7a1db3 100644 --- a/tests/test_continuous_space.py +++ b/tests/test_continuous_space.py @@ -285,67 +285,52 @@ def test_continuous_space_get_k_nearest_agents(): def test_continuous_space_get_agents_in_radius(): - pass - - -# class TestSpaceToroidal(unittest.TestCase): -# """Testing a toroidal continuous space.""" -# -# def setUp(self): -# """Create a test space and populate with Mock Agents.""" -# self.space = ContinuousSpace(70, 20, True, -30, -30) -# self.agents = [] -# for i, pos in enumerate(TEST_AGENTS): -# a = MockAgent(i) -# self.agents.append(a) -# self.space.place_agent(a, pos) -# -# def test_agent_positions(self): -# """Ensure that the agents are all placed properly.""" -# for i, pos in enumerate(TEST_AGENTS): -# a = self.agents[i] -# assert a.pos == pos -# -# def test_agent_matching(self): -# """Ensure that the agents are all placed and indexed properly.""" -# for i, agent in self.space._index_to_agent.items(): -# assert agent.pos == tuple(self.space._agent_points[i, :]) -# assert i == self.space._agent_to_index[agent] -# -# def test_distance_calculations(self): -# """Test toroidal distance calculations.""" -# pos_1 = (-30, -30) -# pos_2 = (70, 20) -# assert self.space.get_distance(pos_1, pos_2) == 0 -# -# pos_3 = (-30, -20) -# assert self.space.get_distance(pos_1, pos_3) == 10 -# -# pos_4 = (20, -5) -# pos_5 = (20, -15) -# assert self.space.get_distance(pos_4, pos_5) == 10 -# -# pos_6 = (-30, -29) -# pos_7 = (21, -5) -# assert self.space.get_distance(pos_6, pos_7) == np.sqrt(49**2 + 24**2) -# -# def test_heading(self): -# pos_1 = (-30, -30) -# pos_2 = (70, 20) -# self.assertEqual((0, 0), self.space.get_heading(pos_1, pos_2)) -# -# pos_1 = (65, -25) -# pos_2 = (-25, -25) -# self.assertEqual((10, 0), self.space.get_heading(pos_1, pos_2)) -# -# def test_neighborhood_retrieval(self): -# """Test neighborhood retrieval.""" -# neighbors_1 = self.space.get_neighbors((-20, -20), 1) -# assert len(neighbors_1) == 2 -# -# neighbors_2 = self.space.get_neighbors((40, -10), 10) -# assert len(neighbors_2) == 0 -# -# neighbors_3 = self.space.get_neighbors((-30, -30), 10) -# assert len(neighbors_3) == 1 -# + # non torus + model = Model(seed=42) + dimensions = np.asarray([[0, 1], [0, 1]]) + space = ContinuousSpace(dimensions, torus=False, random=model.random) + + positions = [[0.1, 0.1], + [0.1, 0.9], + [0.9, 0.1], + [0.9, 0.9], + [0.5, 0.5],] + + for position in positions: + agent = ContinuousSpaceAgent(space, model) + agent.position = position + + agents, distances = space.get_agents_in_radius([0.1, 0.1], radius=0.1) + assert len(agents) == 1 + assert np.allclose( + distances, + [ + 0, + ], + ) + + agents, distances = space.get_agents_in_radius([0.5, 0.1], radius=0.4) + assert len(agents) == 3 + assert np.allclose(distances, [0.4, 0.4, 0.4]) + + agents, distances = space.get_agents_in_radius([0.5, 0.5], radius=1) + assert len(agents) == 5 + + # torus + model = Model(seed=42) + dimensions = np.asarray([[0, 1], [0, 1]]) + space = ContinuousSpace(dimensions, torus=True, random=model.random) + + positions = [[0.1, 0.1], + [0.1, 0.9], + [0.9, 0.1], + [0.9, 0.9], + [0.5, 0.5],] + + for position in positions: + agent = ContinuousSpaceAgent(space, model) + agent.position = position + + agents, distances = space.get_agents_in_radius([0.0, 0.1], radius=0.1) + assert len(agents) == 2 + assert np.allclose(distances, [0.1, 0.1]) \ No newline at end of file From c0782ab86e31fa838c6fa7997837d14085d5c3da Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 13:40:35 +0000 Subject: [PATCH 71/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_continuous_space.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/tests/test_continuous_space.py b/tests/test_continuous_space.py index ce63e7a1db3..1546e83c161 100644 --- a/tests/test_continuous_space.py +++ b/tests/test_continuous_space.py @@ -290,11 +290,13 @@ def test_continuous_space_get_agents_in_radius(): dimensions = np.asarray([[0, 1], [0, 1]]) space = ContinuousSpace(dimensions, torus=False, random=model.random) - positions = [[0.1, 0.1], - [0.1, 0.9], - [0.9, 0.1], - [0.9, 0.9], - [0.5, 0.5],] + positions = [ + [0.1, 0.1], + [0.1, 0.9], + [0.9, 0.1], + [0.9, 0.9], + [0.5, 0.5], + ] for position in positions: agent = ContinuousSpaceAgent(space, model) @@ -321,11 +323,13 @@ def test_continuous_space_get_agents_in_radius(): dimensions = np.asarray([[0, 1], [0, 1]]) space = ContinuousSpace(dimensions, torus=True, random=model.random) - positions = [[0.1, 0.1], - [0.1, 0.9], - [0.9, 0.1], - [0.9, 0.9], - [0.5, 0.5],] + positions = [ + [0.1, 0.1], + [0.1, 0.9], + [0.9, 0.1], + [0.9, 0.9], + [0.5, 0.5], + ] for position in positions: agent = ContinuousSpaceAgent(space, model) @@ -333,4 +337,4 @@ def test_continuous_space_get_agents_in_radius(): agents, distances = space.get_agents_in_radius([0.0, 0.1], radius=0.1) assert len(agents) == 2 - assert np.allclose(distances, [0.1, 0.1]) \ No newline at end of file + assert np.allclose(distances, [0.1, 0.1]) From 6f0a46a19e344b26d00939e998cf0f8c06219404 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Mon, 6 Jan 2025 16:58:55 +0100 Subject: [PATCH 72/84] merge related fixes --- mesa/examples/basic/boid_flockers/agents.py | 2 +- mesa/experimental/continuous_space/continuous_space_agents.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mesa/examples/basic/boid_flockers/agents.py b/mesa/examples/basic/boid_flockers/agents.py index d5258747545..10f31cdc530 100644 --- a/mesa/examples/basic/boid_flockers/agents.py +++ b/mesa/examples/basic/boid_flockers/agents.py @@ -61,7 +61,7 @@ def __init__( def step(self): """Get the Boid's neighbors, compute the new vector, and move accordingly.""" - neighbors = self.model.space.get_neighbors(self.pos, self.vision, True) + neighbors, distances = self.get_neighbors_in_radius(radius=self.vision) self.neighbors = [n for n in neighbors if n is not self] # If no neighbors, maintain current direction diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index b828a3bee86..cc4f780f1ce 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -83,7 +83,7 @@ def get_neighbors_in_radius(self, radius: float = 1) -> tuple[list, np.ndarray]: agents, dists = self.space.get_agents_in_radius(self.position, radius=radius) if dists.size == 0: - return dists, agents + return agents, dists logical = np.asarray([agent is not self for agent in agents]) agents = list(compress(agents, logical)) From 7780b3a3bfd4025fe67b1ec423beaa05e3fb765a Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Wed, 8 Jan 2025 15:48:02 +0100 Subject: [PATCH 73/84] typing and tests --- mesa/examples/basic/boid_flockers/model.py | 2 +- .../continuous_space/continuous_space.py | 10 ++++++---- tests/test_continuous_space.py | 17 +++++++++++++++++ 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/mesa/examples/basic/boid_flockers/model.py b/mesa/examples/basic/boid_flockers/model.py index 497a69e9b06..b9e126a70eb 100644 --- a/mesa/examples/basic/boid_flockers/model.py +++ b/mesa/examples/basic/boid_flockers/model.py @@ -101,5 +101,5 @@ def step(self): if __name__ == "__main__": model = BoidFlockers(population_size=200, width=100, height=100, vision=5, seed=42) - for _i in range(100): + for _i in range(20): model.step() diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 96519edb10f..e05dfd0e843 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -10,6 +10,8 @@ from mesa.agent import Agent, AgentSet +from typing import Iterable, Sequence +from numpy.typing import ArrayLike class ContinuousSpace: """Continuous space where each agent can have an arbitrary position.""" @@ -166,7 +168,7 @@ def calculate_difference_vector(self, point: np.ndarray, agents=None) -> np.ndar return delta - def calculate_distances(self, point, agents=None) -> tuple[np.ndarray, list]: + def calculate_distances(self, point:ArrayLike, agents:Iterable[Agent]|None=None) -> tuple[np.ndarray, list]: """Calculate the distance between the point and all agents.""" point = np.asanyarray(point) @@ -190,7 +192,7 @@ def calculate_distances(self, point, agents=None) -> tuple[np.ndarray, list]: dists = cdist(point[np.newaxis, :], positions)[0, :] return dists, agents - def get_agents_in_radius(self, point, radius=1) -> tuple[list, np.ndarray]: + def get_agents_in_radius(self, point:ArrayLike, radius:float|int=1) -> tuple[list, np.ndarray]: """Return the agents and their distances within a radius for the point.""" distances, agents = self.calculate_distances(point) logical = distances <= radius @@ -200,11 +202,11 @@ def get_agents_in_radius(self, point, radius=1) -> tuple[list, np.ndarray]: distances[logical], ) - def get_k_nearest_agents(self, point, k=1) -> tuple[list, np.ndarray]: + def get_k_nearest_agents(self, point:ArrayLike, k:int=1) -> tuple[list, np.ndarray]: """Return the k nearest agents and their distances to the point. Notes: - This method returns the k nearest agents and ignores ties. + This method returns exactly k agents, ignoring ties """ dists, agents = self.calculate_distances(point) diff --git a/tests/test_continuous_space.py b/tests/test_continuous_space.py index 1546e83c161..78e043e4d71 100644 --- a/tests/test_continuous_space.py +++ b/tests/test_continuous_space.py @@ -192,6 +192,22 @@ def test_continous_space_calculate_distances(): ] ) + distances, agents = space.calculate_distances([0.9, 0.9], agents=[agent,]) + assert np.all( + np.isclose( + distances, + [ + 0.2 * 2**0.5, + ], + ) + ) + assert np.all( + agents + == [ + agent, + ] + ) + def test_continous_space_difference_vector(): """Test ContinuousSpace.distance method.""" @@ -318,6 +334,7 @@ def test_continuous_space_get_agents_in_radius(): agents, distances = space.get_agents_in_radius([0.5, 0.5], radius=1) assert len(agents) == 5 + # torus model = Model(seed=42) dimensions = np.asarray([[0, 1], [0, 1]]) From c2aa0f2205ed3f95d7ae78716cf4546c0ef3c0ae Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 14:48:12 +0000 Subject: [PATCH 74/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../continuous_space/continuous_space.py | 17 +++++++++++------ tests/test_continuous_space.py | 8 ++++++-- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index e05dfd0e843..c358893b4dc 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -1,17 +1,16 @@ """A Continuous Space class.""" import warnings -from collections.abc import Sequence +from collections.abc import Iterable, Sequence from itertools import compress from random import Random import numpy as np +from numpy.typing import ArrayLike from scipy.spatial.distance import cdist from mesa.agent import Agent, AgentSet -from typing import Iterable, Sequence -from numpy.typing import ArrayLike class ContinuousSpace: """Continuous space where each agent can have an arbitrary position.""" @@ -168,7 +167,9 @@ def calculate_difference_vector(self, point: np.ndarray, agents=None) -> np.ndar return delta - def calculate_distances(self, point:ArrayLike, agents:Iterable[Agent]|None=None) -> tuple[np.ndarray, list]: + def calculate_distances( + self, point: ArrayLike, agents: Iterable[Agent] | None = None + ) -> tuple[np.ndarray, list]: """Calculate the distance between the point and all agents.""" point = np.asanyarray(point) @@ -192,7 +193,9 @@ def calculate_distances(self, point:ArrayLike, agents:Iterable[Agent]|None=None) dists = cdist(point[np.newaxis, :], positions)[0, :] return dists, agents - def get_agents_in_radius(self, point:ArrayLike, radius:float|int=1) -> tuple[list, np.ndarray]: + def get_agents_in_radius( + self, point: ArrayLike, radius: float | int = 1 + ) -> tuple[list, np.ndarray]: """Return the agents and their distances within a radius for the point.""" distances, agents = self.calculate_distances(point) logical = distances <= radius @@ -202,7 +205,9 @@ def get_agents_in_radius(self, point:ArrayLike, radius:float|int=1) -> tuple[lis distances[logical], ) - def get_k_nearest_agents(self, point:ArrayLike, k:int=1) -> tuple[list, np.ndarray]: + def get_k_nearest_agents( + self, point: ArrayLike, k: int = 1 + ) -> tuple[list, np.ndarray]: """Return the k nearest agents and their distances to the point. Notes: diff --git a/tests/test_continuous_space.py b/tests/test_continuous_space.py index 78e043e4d71..85c8b36e115 100644 --- a/tests/test_continuous_space.py +++ b/tests/test_continuous_space.py @@ -192,7 +192,12 @@ def test_continous_space_calculate_distances(): ] ) - distances, agents = space.calculate_distances([0.9, 0.9], agents=[agent,]) + distances, agents = space.calculate_distances( + [0.9, 0.9], + agents=[ + agent, + ], + ) assert np.all( np.isclose( distances, @@ -334,7 +339,6 @@ def test_continuous_space_get_agents_in_radius(): agents, distances = space.get_agents_in_radius([0.5, 0.5], radius=1) assert len(agents) == 5 - # torus model = Model(seed=42) dimensions = np.asarray([[0, 1], [0, 1]]) From 528aab52bd50827bc419e562c943b4628f48fc6f Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Wed, 8 Jan 2025 17:17:08 +0100 Subject: [PATCH 75/84] more tests --- .../continuous_space_agents.py | 4 - tests/test_continuous_space.py | 78 ++++++++++++++++++- 2 files changed, 75 insertions(+), 7 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index cc4f780f1ce..3f2167e0ef2 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -81,10 +81,6 @@ def get_neighbors_in_radius(self, radius: float = 1) -> tuple[list, np.ndarray]: """ agents, dists = self.space.get_agents_in_radius(self.position, radius=radius) - - if dists.size == 0: - return agents, dists - logical = np.asarray([agent is not self for agent in agents]) agents = list(compress(agents, logical)) return agents, dists[logical] diff --git a/tests/test_continuous_space.py b/tests/test_continuous_space.py index 85c8b36e115..4184aa15ec3 100644 --- a/tests/test_continuous_space.py +++ b/tests/test_continuous_space.py @@ -1,6 +1,7 @@ """Tests for continuous space.""" import numpy as np +import pytest from mesa import Model from mesa.experimental.continuous_space import ContinuousSpace, ContinuousSpaceAgent @@ -97,6 +98,21 @@ def test_continuous_agent(): agent.remove() assert space.agent_positions.shape == (110 - 1 - i, 2) + model = Model(seed=42) + + dimensions = np.asarray([[0, 1], [0, 1]]) + space = ContinuousSpace(dimensions, torus=True, random=model.random) + agent = ContinuousSpaceAgent(space, model) + agent.position =[1.1, 1.1] + assert np.allclose(agent.position, [0.1, 0.1]) + + dimensions = np.asarray([[0, 1], [0, 1]]) + space = ContinuousSpace(dimensions, torus=False, random=model.random) + agent = ContinuousSpaceAgent(space, model) + with pytest.raises(ValueError): + agent.position =[1.1, 1.1] + + def test_continous_space_calculate_distances(): """Test ContinuousSpace.distance method.""" @@ -215,7 +231,7 @@ def test_continous_space_calculate_distances(): def test_continous_space_difference_vector(): - """Test ContinuousSpace.distance method.""" + """Test ContinuousSpace.get_difference_vector method.""" # non torus model = Model(seed=42) dimensions = np.asarray([[0, 1], [0, 1]]) @@ -248,7 +264,7 @@ def test_continous_space_difference_vector(): assert np.allclose(vector, [0.2, 0.2]) -def test_continuous_space_get_k_nearest_agents(): +def test_continuous_space_get_k_nearest_agents(): # noqa: D103 # non torus model = Model(seed=42) dimensions = np.asarray([[0, 1], [0, 1]]) @@ -305,7 +321,7 @@ def test_continuous_space_get_k_nearest_agents(): assert np.allclose(distances, [0.1, 0.1]) -def test_continuous_space_get_agents_in_radius(): +def test_continuous_space_get_agents_in_radius(): # noqa: D103 # non torus model = Model(seed=42) dimensions = np.asarray([[0, 1], [0, 1]]) @@ -359,3 +375,59 @@ def test_continuous_space_get_agents_in_radius(): agents, distances = space.get_agents_in_radius([0.0, 0.1], radius=0.1) assert len(agents) == 2 assert np.allclose(distances, [0.1, 0.1]) + + +def test_get_neighbor_methos(): # noqa: D103 + # non torus + model = Model(seed=42) + dimensions = np.asarray([[0, 1], [0, 1]]) + space = ContinuousSpace(dimensions, torus=False, random=model.random) + + positions = [ + [0.1, 0.1], + [0.1, 0.9], + [0.9, 0.1], + [0.9, 0.9], + [0.5, 0.5], + ] + + for position in positions: + agent = ContinuousSpaceAgent(space, model) + agent.position = position + + agent: ContinuousSpaceAgent = model.agents[-1] # 0.5, 0.5 + agents, distances = agent.get_neighbors_in_radius(1) + assert len(agents) == 4 + + agents, distances = agent.get_neighbors_in_radius(0.1) + assert len(agents) == 0 + + agent: ContinuousSpaceAgent = model.agents[0] # 0.1, 0.1 + agents, distances = agent.get_nearest_neighbors(k=2) + assert len(agents) == 2 + + # torus + model = Model(seed=42) + dimensions = np.asarray([[0, 1], [0, 1]]) + space = ContinuousSpace(dimensions, torus=True, random=model.random) + + positions = [ + [0.1, 0.1], + [0.1, 0.9], + [0.9, 0.1], + [0.9, 0.9], + [0.5, 0.5], + ] + + for position in positions: + agent = ContinuousSpaceAgent(space, model) + agent.position = position + + agent: ContinuousSpaceAgent = model.agents[-1] # 0.5, 0.5 + agents, distances = agent.get_neighbors_in_radius(1) + assert len(agents) == 4 + + agent: ContinuousSpaceAgent = model.agents[0] # 0.1, 0.1 + agents, distances = agent.get_nearest_neighbors(k=2) + assert len(agents) == 2 + assert np.allclose(distances, [0.2, 0.2]) \ No newline at end of file From e98eadf48d393545de2254530b268e494d654a09 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Wed, 8 Jan 2025 17:17:36 +0100 Subject: [PATCH 76/84] Delete for_development.py --- .../continuous_space/for_development.py | 33 ------------------- 1 file changed, 33 deletions(-) delete mode 100644 mesa/experimental/continuous_space/for_development.py diff --git a/mesa/experimental/continuous_space/for_development.py b/mesa/experimental/continuous_space/for_development.py deleted file mode 100644 index 525d3246641..00000000000 --- a/mesa/experimental/continuous_space/for_development.py +++ /dev/null @@ -1,33 +0,0 @@ -"""to be removed once further into the development.""" - -if __name__ == "__main__": - from mesa import Agent, Model - from mesa.experimental.continuous_space import ContinuousSpace as NewStyleSpace - from mesa.experimental.continuous_space import ContinuousSpaceAgent - from mesa.space import ContinuousSpace as OldStyleSpace - - n = 400 - - model = Model(rng=42) - space = OldStyleSpace(100, 100, torus=True) - - positions = model.rng.random((n, 2)) * n - for pos in positions: - agent = Agent(model) - space.place_agent(agent, pos) - - for _ in range(100): - space.get_neighbors([50, 50], radius=5, include_center=False) - - model = Model(rng=42) - space = NewStyleSpace( - [[0, 100], [0, 100]], torus=True, random=model.random, n_agents=n - ) - - positions = model.rng.random((n, 2)) * n - for pos in positions: - agent = ContinuousSpaceAgent(space, model) - agent.position = pos - - for _ in range(100): - space.get_agents_in_radius([50, 50], radius=5) From 983fbb9eecfad1b452fbee81ae071c66c0c2b9a3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 16:17:47 +0000 Subject: [PATCH 77/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_continuous_space.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/test_continuous_space.py b/tests/test_continuous_space.py index 4184aa15ec3..41d7ca33186 100644 --- a/tests/test_continuous_space.py +++ b/tests/test_continuous_space.py @@ -103,15 +103,14 @@ def test_continuous_agent(): dimensions = np.asarray([[0, 1], [0, 1]]) space = ContinuousSpace(dimensions, torus=True, random=model.random) agent = ContinuousSpaceAgent(space, model) - agent.position =[1.1, 1.1] + agent.position = [1.1, 1.1] assert np.allclose(agent.position, [0.1, 0.1]) dimensions = np.asarray([[0, 1], [0, 1]]) space = ContinuousSpace(dimensions, torus=False, random=model.random) agent = ContinuousSpaceAgent(space, model) with pytest.raises(ValueError): - agent.position =[1.1, 1.1] - + agent.position = [1.1, 1.1] def test_continous_space_calculate_distances(): @@ -430,4 +429,4 @@ def test_get_neighbor_methos(): # noqa: D103 agent: ContinuousSpaceAgent = model.agents[0] # 0.1, 0.1 agents, distances = agent.get_nearest_neighbors(k=2) assert len(agents) == 2 - assert np.allclose(distances, [0.2, 0.2]) \ No newline at end of file + assert np.allclose(distances, [0.2, 0.2]) From c4de8de45aae6e07988d7a202a961968ee6fdb74 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Wed, 8 Jan 2025 18:45:14 +0100 Subject: [PATCH 78/84] Update mesa/examples/basic/boid_flockers/model.py Co-authored-by: Ewout ter Hoeven --- mesa/examples/basic/boid_flockers/model.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mesa/examples/basic/boid_flockers/model.py b/mesa/examples/basic/boid_flockers/model.py index b9e126a70eb..cc7cfaa85e7 100644 --- a/mesa/examples/basic/boid_flockers/model.py +++ b/mesa/examples/basic/boid_flockers/model.py @@ -59,14 +59,14 @@ def __init__( ) # Create and place the Boid agents - position = self.rng.random(size=(population_size, 2)) * self.space.size - direction = self.rng.uniform(-1, 1, size=(population_size, 2)) + positions = self.rng.random(size=(population_size, 2)) * self.space.size + directions = self.rng.uniform(-1, 1, size=(population_size, 2)) Boid.create_agents( self, population_size, self.space, - position=position, - direction=direction, + position=positions, + direction=directions, cohere=cohere, separate=separate, match=match, From 23159bf450b0ea8efdf5c9e3ad0732022e25f9a5 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Wed, 8 Jan 2025 18:47:23 +0100 Subject: [PATCH 79/84] Update mesa/examples/basic/boid_flockers/model.py Co-authored-by: Ewout ter Hoeven --- mesa/examples/basic/boid_flockers/model.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/mesa/examples/basic/boid_flockers/model.py b/mesa/examples/basic/boid_flockers/model.py index cc7cfaa85e7..c5d46a12b85 100644 --- a/mesa/examples/basic/boid_flockers/model.py +++ b/mesa/examples/basic/boid_flockers/model.py @@ -96,10 +96,3 @@ def step(self): """ self.agents.shuffle_do("step") self.update_average_heading() - - -if __name__ == "__main__": - model = BoidFlockers(population_size=200, width=100, height=100, vision=5, seed=42) - - for _i in range(20): - model.step() From 0706667f30e730000e292db2c5a8e90d0a08bbfb Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Wed, 8 Jan 2025 19:20:11 +0100 Subject: [PATCH 80/84] expanded docstring and comments --- .../continuous_space/continuous_space.py | 70 ++++++++++++++----- .../continuous_space_agents.py | 4 +- 2 files changed, 55 insertions(+), 19 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index c358893b4dc..caac339ba84 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -47,12 +47,24 @@ def height(self): # noqa: D102 def __init__( self, - dimensions: Sequence[Sequence[float]], + dimensions: ArrayLike, torus: bool = False, random: Random | None = None, n_agents: int = 100, ) -> None: - """Create a new continuous space.""" + """Create a new continuous space. + + Args: + dimensions: a numpy array like object where eahc row specifies the minimum and maximum value of that dimesnion + torus: boolean for whether the space wraps around or not + random: a seeded stdlib random.Random instance + n_agents: the expected number of agents in the space + + Internally, a numpy array is used to store the positions of all agents. This is resized if needed, + but you can control the initial size explicitly by passing n_agents. + + + """ if random is None: warnings.warn( "Random number generator not specified, this can make models non-reproducible. Please pass a random number generator explicitly", @@ -62,25 +74,29 @@ def __init__( random = Random() self.random = random - self.dimensions: np.array = np.asarray(dimensions) + self.dimensions: np.array = np.asanarray(dimensions) self.ndims: int = self.dimensions.shape[0] self.size: np.array = self.dimensions[:, 1] - self.dimensions[:, 0] self.center: np.array = np.sum(self.dimensions, axis=1) / 2 self.torus: bool = torus + + # self._agent_positions is the array containing all agent positions + # plus potential extra empty rows + # agent_positions is a view into _agent_positions containing only the filled rows self._agent_positions: np.array = np.empty( (n_agents, self.dimensions.shape[0]), dtype=float ) - # self._agents: np.array = np.zeros((n_agents,), dtype=object) - - self.active_agents = [] self.agent_positions: ( np.array ) # a view on _agent_positions containing all active positions - self._n_agents = 0 + # the list of agents in the space + self.active_agents = [] + self._n_agents = 0 # the number of active agents in the space + # a mapping from agents to index and vice versa self._index_to_agent: dict[int, Agent] = {} self._agent_to_index: dict[Agent, int | None] = {} @@ -90,10 +106,10 @@ def agents(self) -> AgentSet: return AgentSet(self.active_agents, random=self.random) def _add_agent(self, agent: Agent) -> int: - """Helper method to get the index for the agent. + """Helper method for adding an agent to the space. This method manages the numpy array with the agent positions and ensuring it is - enlarged if and when needed. + enlarged if and when needed. It is called automatically by ContinousSpaceAgent when created. """ index = self._n_agents @@ -123,7 +139,11 @@ def _add_agent(self, agent: Agent) -> int: return index def _remove_agent(self, agent: Agent) -> None: - """Remove an agent from the space.""" + """Remove an agent from the space. + + This method is automatically called by ContinuousSpaceAgent.remove. + + """ index = self._agent_to_index[agent] self._agent_to_index.pop(agent, None) self._index_to_agent.pop(index, None) @@ -143,7 +163,15 @@ def _remove_agent(self, agent: Agent) -> None: self.agent_positions = self._agent_positions[0 : self._n_agents] def calculate_difference_vector(self, point: np.ndarray, agents=None) -> np.ndarray: - """Calculate the difference vector between the point and all agents.""" + """Calculate the difference vector between the point and all agenents. + + Args: + point: the point to calculate the difference vector for + agents: the agents to calculate the difference vector of point with. By default, + all agents are considered. + + + """ point = np.asanyarray(point) positions = ( self.agent_positions @@ -170,7 +198,14 @@ def calculate_difference_vector(self, point: np.ndarray, agents=None) -> np.ndar def calculate_distances( self, point: ArrayLike, agents: Iterable[Agent] | None = None ) -> tuple[np.ndarray, list]: - """Calculate the distance between the point and all agents.""" + """Calculate the distance between the point and all agents. + + Args: + point: the point to calculate the difference vector for + agents: the agents to calculate the difference vector of point with. By default, + all agents are considered. + + """ point = np.asanyarray(point) if agents is None: @@ -211,7 +246,8 @@ def get_k_nearest_agents( """Return the k nearest agents and their distances to the point. Notes: - This method returns exactly k agents, ignoring ties + This method returns exactly k agents, ignoring ties. In case of ties, the + earlier an agent is inserted the higher it will rank. """ dists, agents = self.calculate_distances(point) @@ -220,12 +256,12 @@ def get_k_nearest_agents( agents = [agents[i] for i in indices] return agents, dists[indices] - def in_bounds(self, point) -> bool: + def in_bounds(self, point:ArrayLike) -> bool: """Check if point is inside the bounds of the space.""" return bool( - ((point >= self.dimensions[:, 0]) & (point <= self.dimensions[:, 1])).all() + ((np.asanyarray(point) >= self.dimensions[:, 0]) & (point <= self.dimensions[:, 1])).all() ) - def torus_correct(self, point) -> np.ndarray: + def torus_correct(self, point:ArrayLike) -> np.ndarray: """Apply a torus correction to the point.""" - return self.dimensions[:, 0] + np.mod(point - self.dimensions[:, 0], self.size) + return self.dimensions[:, 0] + np.mod(np.asanyarray(point) - self.dimensions[:, 0], self.size) diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index 3f2167e0ef2..155fa275ff3 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -67,13 +67,13 @@ def __init__(self, space: ContinuousSpace, model): # self.position[:] = np.nan def remove(self) -> None: - """Remove and delete the agent from the model.""" + """Remove and delete the agent from the model and continuous space""" super().remove() self.space._remove_agent(self) self._mesa_index = None self.space = None - def get_neighbors_in_radius(self, radius: float = 1) -> tuple[list, np.ndarray]: + def get_neighbors_in_radius(self, radius: float|int = 1) -> tuple[list, np.ndarray]: """Get neighbors within radius. Args: From 0cbbbb504b4a61c8e52e42532db9bdd8940634cf Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 18:20:26 +0000 Subject: [PATCH 81/84] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../continuous_space/continuous_space.py | 16 ++++++++++------ .../continuous_space/continuous_space_agents.py | 4 +++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index caac339ba84..6efb2a1ba53 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -1,7 +1,7 @@ """A Continuous Space class.""" import warnings -from collections.abc import Iterable, Sequence +from collections.abc import Iterable from itertools import compress from random import Random @@ -81,7 +81,6 @@ def __init__( self.torus: bool = torus - # self._agent_positions is the array containing all agent positions # plus potential extra empty rows # agent_positions is a view into _agent_positions containing only the filled rows @@ -256,12 +255,17 @@ def get_k_nearest_agents( agents = [agents[i] for i in indices] return agents, dists[indices] - def in_bounds(self, point:ArrayLike) -> bool: + def in_bounds(self, point: ArrayLike) -> bool: """Check if point is inside the bounds of the space.""" return bool( - ((np.asanyarray(point) >= self.dimensions[:, 0]) & (point <= self.dimensions[:, 1])).all() + ( + (np.asanyarray(point) >= self.dimensions[:, 0]) + & (point <= self.dimensions[:, 1]) + ).all() ) - def torus_correct(self, point:ArrayLike) -> np.ndarray: + def torus_correct(self, point: ArrayLike) -> np.ndarray: """Apply a torus correction to the point.""" - return self.dimensions[:, 0] + np.mod(np.asanyarray(point) - self.dimensions[:, 0], self.size) + return self.dimensions[:, 0] + np.mod( + np.asanyarray(point) - self.dimensions[:, 0], self.size + ) diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index 155fa275ff3..02a7305bed4 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -73,7 +73,9 @@ def remove(self) -> None: self._mesa_index = None self.space = None - def get_neighbors_in_radius(self, radius: float|int = 1) -> tuple[list, np.ndarray]: + def get_neighbors_in_radius( + self, radius: float | int = 1 + ) -> tuple[list, np.ndarray]: """Get neighbors within radius. Args: From 418778651030b9e4c233b3cde301c55558fd2a9d Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Wed, 8 Jan 2025 19:25:11 +0100 Subject: [PATCH 82/84] Update continuous_space.py --- mesa/experimental/continuous_space/continuous_space.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 6efb2a1ba53..d847ef23ce7 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -74,7 +74,7 @@ def __init__( random = Random() self.random = random - self.dimensions: np.array = np.asanarray(dimensions) + self.dimensions: np.array = np.asanyarray(dimensions) self.ndims: int = self.dimensions.shape[0] self.size: np.array = self.dimensions[:, 1] - self.dimensions[:, 0] self.center: np.array = np.sum(self.dimensions, axis=1) / 2 From 1b8937f1de6bb21e01398193691d1ea36dffab8e Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Fri, 10 Jan 2025 08:20:53 +0100 Subject: [PATCH 83/84] final tweaks --- mesa/experimental/continuous_space/continuous_space.py | 6 ++++-- .../continuous_space/continuous_space_agents.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index d847ef23ce7..a88733fcb1c 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -195,7 +195,7 @@ def calculate_difference_vector(self, point: np.ndarray, agents=None) -> np.ndar return delta def calculate_distances( - self, point: ArrayLike, agents: Iterable[Agent] | None = None + self, point: ArrayLike, agents: Iterable[Agent] | None = None, **kwargs ) -> tuple[np.ndarray, list]: """Calculate the distance between the point and all agents. @@ -203,6 +203,8 @@ def calculate_distances( point: the point to calculate the difference vector for agents: the agents to calculate the difference vector of point with. By default, all agents are considered. + kwargs: any additional keyword arguments are passed to scipy's cdist, which is used + only if torus is False. This allows for non-Euclidian distance measures. """ point = np.asanyarray(point) @@ -224,7 +226,7 @@ def calculate_distances( dists += delta[:, i] ** 2 dists = np.sqrt(dists) else: - dists = cdist(point[np.newaxis, :], positions)[0, :] + dists = cdist(point[np.newaxis, :], positions, **kwargs)[0, :] return dists, agents def get_agents_in_radius( diff --git a/mesa/experimental/continuous_space/continuous_space_agents.py b/mesa/experimental/continuous_space/continuous_space_agents.py index 02a7305bed4..f4ea9c2885f 100644 --- a/mesa/experimental/continuous_space/continuous_space_agents.py +++ b/mesa/experimental/continuous_space/continuous_space_agents.py @@ -67,7 +67,7 @@ def __init__(self, space: ContinuousSpace, model): # self.position[:] = np.nan def remove(self) -> None: - """Remove and delete the agent from the model and continuous space""" + """Remove and delete the agent from the model and continuous space.""" super().remove() self.space._remove_agent(self) self._mesa_index = None From 897d62e3b818dd232764d47cd1cd44bff39f812b Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Fri, 10 Jan 2025 08:23:20 +0100 Subject: [PATCH 84/84] Update continuous_space.py --- mesa/experimental/continuous_space/continuous_space.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index a88733fcb1c..232eb38414b 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -55,7 +55,7 @@ def __init__( """Create a new continuous space. Args: - dimensions: a numpy array like object where eahc row specifies the minimum and maximum value of that dimesnion + dimensions: a numpy array like object where each row specifies the minimum and maximum value of that dimension. torus: boolean for whether the space wraps around or not random: a seeded stdlib random.Random instance n_agents: the expected number of agents in the space