From 7c123aac08cc2a9b40da720a18308f5fac7b2249 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Tue, 18 Nov 2025 10:49:04 +0100 Subject: [PATCH 01/24] initial commit --- mesa/agent.py | 3 +++ .../basic/boltzmann_wealth_model/model.py | 4 ++++ mesa/model.py | 2 ++ mesa/util.py | 22 +++++++++++++++++++ 4 files changed, 31 insertions(+) create mode 100644 mesa/util.py diff --git a/mesa/agent.py b/mesa/agent.py index 93cd6287528..fda812d89b2 100644 --- a/mesa/agent.py +++ b/mesa/agent.py @@ -29,6 +29,8 @@ from mesa.model import Model from mesa.space import Position +from mesa.util import deprecate_kwarg + class Agent: """Base class for a model agent in Mesa. @@ -169,6 +171,7 @@ class AgentSet(MutableSet, Sequence): """ + @deprecate_kwarg("random") def __init__( self, agents: Iterable[Agent], diff --git a/mesa/examples/basic/boltzmann_wealth_model/model.py b/mesa/examples/basic/boltzmann_wealth_model/model.py index 53076cf0e24..ca1b4023d50 100644 --- a/mesa/examples/basic/boltzmann_wealth_model/model.py +++ b/mesa/examples/basic/boltzmann_wealth_model/model.py @@ -72,3 +72,7 @@ def compute_gini(self): # Calculate using the standard formula for Gini coefficient b = sum(xi * (n - i) for i, xi in enumerate(x)) / (n * sum(x)) return 1 + (1 / n) - 2 * b + + +if __name__ == "__main__": + model = BoltzmannWealth(seed=42) \ No newline at end of file diff --git a/mesa/model.py b/mesa/model.py index f53d92b1633..7754813ec24 100644 --- a/mesa/model.py +++ b/mesa/model.py @@ -18,6 +18,7 @@ from mesa.agent import Agent, AgentSet from mesa.mesa_logging import create_module_logger, method_logger +from mesa.util import deprecate_kwarg SeedLike = int | np.integer | Sequence[int] | np.random.SeedSequence RNGLike = np.random.Generator | np.random.BitGenerator @@ -46,6 +47,7 @@ class Model: """ + @deprecate_kwarg("seed") @method_logger(__name__) def __init__( self, diff --git a/mesa/util.py b/mesa/util.py new file mode 100644 index 00000000000..5aeebd82c97 --- /dev/null +++ b/mesa/util.py @@ -0,0 +1,22 @@ +"""Utilities used across mesa.""" +import warnings +from functools import wraps + + +def deprecate_kwarg(name: str): + def inner_wrapper(method): + """Deprecation warning wrapper for seed kwarg.""" + @wraps(method) + def wrapper(self, *args, **kwargs): + """Inner function.""" + if name in kwargs: + warnings.warn( + f"The use of {name} is deprecated, please use rng instead", + FutureWarning, + stacklevel=2, + ) + + return method(self, *args, **kwargs) + + return wrapper + return inner_wrapper From e35ec91d825d0710f5f05f8da50bfdfc49d3c439 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 18 Nov 2025 09:53:22 +0000 Subject: [PATCH 02/24] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/examples/basic/boltzmann_wealth_model/model.py | 2 +- mesa/util.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/mesa/examples/basic/boltzmann_wealth_model/model.py b/mesa/examples/basic/boltzmann_wealth_model/model.py index ca1b4023d50..107bece9b9f 100644 --- a/mesa/examples/basic/boltzmann_wealth_model/model.py +++ b/mesa/examples/basic/boltzmann_wealth_model/model.py @@ -75,4 +75,4 @@ def compute_gini(self): if __name__ == "__main__": - model = BoltzmannWealth(seed=42) \ No newline at end of file + model = BoltzmannWealth(seed=42) diff --git a/mesa/util.py b/mesa/util.py index 5aeebd82c97..90761019ac6 100644 --- a/mesa/util.py +++ b/mesa/util.py @@ -1,4 +1,5 @@ """Utilities used across mesa.""" + import warnings from functools import wraps @@ -6,6 +7,7 @@ def deprecate_kwarg(name: str): def inner_wrapper(method): """Deprecation warning wrapper for seed kwarg.""" + @wraps(method) def wrapper(self, *args, **kwargs): """Inner function.""" @@ -19,4 +21,5 @@ def wrapper(self, *args, **kwargs): return method(self, *args, **kwargs) return wrapper + return inner_wrapper From bf7a3c48f71ada488c6bc0549ca84f6be49bd84d Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Tue, 18 Nov 2025 11:01:42 +0100 Subject: [PATCH 03/24] additional warnings --- mesa/discrete_space/cell.py | 2 ++ mesa/discrete_space/cell_collection.py | 3 +++ mesa/discrete_space/discrete_space.py | 2 ++ mesa/discrete_space/network.py | 2 ++ mesa/experimental/continuous_space/continuous_space.py | 2 ++ mesa/util.py | 2 ++ 6 files changed, 13 insertions(+) diff --git a/mesa/discrete_space/cell.py b/mesa/discrete_space/cell.py index f8621ead808..744f6d49d43 100644 --- a/mesa/discrete_space/cell.py +++ b/mesa/discrete_space/cell.py @@ -20,6 +20,7 @@ from mesa.discrete_space.cell_agent import CellAgent from mesa.discrete_space.cell_collection import CellCollection +from mesa.util import deprecate_kwarg if TYPE_CHECKING: from mesa.agent import Agent @@ -48,6 +49,7 @@ class Cell: "random", ] + @deprecate_kwarg("random") def __init__( self, coordinate: Coordinate, diff --git a/mesa/discrete_space/cell_collection.py b/mesa/discrete_space/cell_collection.py index 049fb0eed4d..ec72d202ca4 100644 --- a/mesa/discrete_space/cell_collection.py +++ b/mesa/discrete_space/cell_collection.py @@ -22,6 +22,8 @@ from random import Random from typing import TYPE_CHECKING, TypeVar +from mesa.util import deprecate_kwarg + if TYPE_CHECKING: from mesa.discrete_space.cell import Cell from mesa.discrete_space.cell_agent import CellAgent @@ -45,6 +47,7 @@ class CellCollection[T: Cell]: """ + @deprecate_kwarg("random") def __init__( self, cells: Mapping[T, list[CellAgent]] | Iterable[T], diff --git a/mesa/discrete_space/discrete_space.py b/mesa/discrete_space/discrete_space.py index 770a82938ac..40fa8edbcd8 100644 --- a/mesa/discrete_space/discrete_space.py +++ b/mesa/discrete_space/discrete_space.py @@ -23,6 +23,7 @@ from mesa.agent import AgentSet from mesa.discrete_space.cell import Cell from mesa.discrete_space.cell_collection import CellCollection +from mesa.util import deprecate_kwarg T = TypeVar("T", bound=Cell) @@ -45,6 +46,7 @@ class DiscreteSpace[T: Cell]: """ + @deprecate_kwarg("random") def __init__( self, capacity: int | None = None, diff --git a/mesa/discrete_space/network.py b/mesa/discrete_space/network.py index 3fc0bc2ff14..b65dccd3faf 100644 --- a/mesa/discrete_space/network.py +++ b/mesa/discrete_space/network.py @@ -17,11 +17,13 @@ from mesa.discrete_space.cell import Cell from mesa.discrete_space.discrete_space import DiscreteSpace +from mesa.util import deprecate_kwarg class Network(DiscreteSpace[Cell]): """A networked discrete space.""" + @deprecate_kwarg("random") def __init__( self, G: Any, # noqa: N803 diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 3c99e0af464..330a22068ef 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -10,6 +10,7 @@ from scipy.spatial.distance import cdist from mesa.agent import Agent, AgentSet +from mesa.util import deprecate_kwarg class ContinuousSpace: @@ -45,6 +46,7 @@ def height(self): # noqa: D102 # compatibility with solara_viz return self.size[1] + @deprecate_kwarg("random") def __init__( self, dimensions: ArrayLike, diff --git a/mesa/util.py b/mesa/util.py index 90761019ac6..f79524f1ae4 100644 --- a/mesa/util.py +++ b/mesa/util.py @@ -5,6 +5,8 @@ def deprecate_kwarg(name: str): + """Deprecation warning wrapper for specified kwarg.""" + def inner_wrapper(method): """Deprecation warning wrapper for seed kwarg.""" From c5e62241bbaed3fc8c8e637954dea733ea0a20f7 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Tue, 18 Nov 2025 12:14:19 +0100 Subject: [PATCH 04/24] shift agentset to rng --- mesa/agent.py | 41 ++++++++++++------- .../basic/boltzmann_wealth_model/model.py | 8 ++-- mesa/model.py | 4 +- mesa/util.py | 2 + 4 files changed, 35 insertions(+), 20 deletions(-) diff --git a/mesa/agent.py b/mesa/agent.py index fda812d89b2..5aea721d86b 100644 --- a/mesa/agent.py +++ b/mesa/agent.py @@ -12,6 +12,7 @@ import functools import itertools import operator +import sys import warnings import weakref from collections import defaultdict @@ -22,6 +23,7 @@ from typing import TYPE_CHECKING, Any, Literal, overload import numpy as np +from numpy.random import SeedSequence, BitGenerator, Generator, RandomState if TYPE_CHECKING: # We ensure that these are not imported during runtime to prevent cyclic @@ -32,6 +34,8 @@ from mesa.util import deprecate_kwarg +SeedLike = int | np.ndarray[int] | SeedSequence | BitGenerator | Generator | RandomState + class Agent: """Base class for a model agent in Mesa. @@ -135,7 +139,7 @@ def __getitem__(self, i): instance_kwargs = {k: v[i] for k, v in listlike_kwargs.items()} agent = cls(model, *instance_args, **instance_kwargs) agents.append(agent) - return AgentSet(agents, random=model.random) + return AgentSet(agents, rng=model.rng) @property def random(self) -> Random: @@ -176,28 +180,34 @@ def __init__( self, agents: Iterable[Agent], random: Random | None = None, + rng: SeedLike | None = None, ): """Initializes the AgentSet with a collection of agents and a reference to the model. Args: agents (Iterable[Agent]): An iterable of Agent objects to be included in the set. random (Random | np.random.Generator | None): the random number generator + rng (SeedLike | None): the random number generator """ self._agents = weakref.WeakKeyDictionary(dict.fromkeys(agents)) - if (len(self._agents) == 0) and random is None: + if (len(self._agents) == 0) and (random is None and rng is None): warnings.warn( "No Agents specified in creation of AgentSet and no random number generator specified. " "This can make models non-reproducible. Please pass a random number generator explicitly", UserWarning, stacklevel=2, ) - random = Random() + rng = np.random.default_rng() if random is not None: - self.random = random - else: - # all agents in an AgentSet should share the same model, just take it from first - self.random = self._agents.keys().__next__().model.random + rng = np.random.default_rng(random.getstate()[1]) + + if rng is None: + rng = self._agents.keys().__next__().model.rng + + # if rng is a np.random.Generator, it will just return it, + # if rng can be used to seed a generator, a new generator is created with this seed + self.rng = np.random.default_rng(rng) def __len__(self) -> int: """Return the number of agents in the AgentSet.""" @@ -257,7 +267,7 @@ def agent_generator(filter_func, agent_type, at_most): agents = agent_generator(filter_func, agent_type, at_most) - return AgentSet(agents, self.random) if not inplace else self._update(agents) + return AgentSet(agents, rng=self.rng) if not inplace else self._update(agents) def shuffle(self, inplace: bool = False) -> AgentSet: """Randomly shuffle the order of agents in the AgentSet. @@ -273,14 +283,15 @@ def shuffle(self, inplace: bool = False) -> AgentSet: """ weakrefs = list(self._agents.keyrefs()) - self.random.shuffle(weakrefs) + + self.rng.shuffle(weakrefs) if inplace: self._agents.data = dict.fromkeys(weakrefs) return self else: return AgentSet( - (agent for ref in weakrefs if (agent := ref()) is not None), self.random + (agent for ref in weakrefs if (agent := ref()) is not None), rng=self.rng ) def sort( @@ -305,7 +316,7 @@ def sort( sorted_agents = sorted(self._agents.keys(), key=key, reverse=not ascending) return ( - AgentSet(sorted_agents, self.random) + AgentSet(sorted_agents, rng=self.rng) if not inplace else self._update(sorted_agents) ) @@ -351,7 +362,7 @@ def shuffle_do(self, method: str | Callable, *args, **kwargs) -> AgentSet: It's a fast, optimized version of calling shuffle() followed by do(). """ weakrefs = list(self._agents.keyrefs()) - self.random.shuffle(weakrefs) + self.rng.shuffle(weakrefs) if isinstance(method, str): for ref in weakrefs: @@ -560,7 +571,7 @@ def __getstate__(self): Returns: dict: A dictionary representing the state of the AgentSet. """ - return {"agents": list(self._agents.keys()), "random": self.random} + return {"agents": list(self._agents.keys()), "rng": self.rng} def __setstate__(self, state): """Set the state of the AgentSet during deserialization. @@ -568,7 +579,7 @@ def __setstate__(self, state): Args: state (dict): A dictionary representing the state to restore. """ - self.random = state["random"] + self.rng = state["rng"] self._update(state["agents"]) def groupby(self, by: Callable | str, result_type: str = "agentset") -> GroupBy: @@ -603,7 +614,7 @@ def groupby(self, by: Callable | str, result_type: str = "agentset") -> GroupBy: if result_type == "agentset": return GroupBy( - {k: AgentSet(v, random=self.random) for k, v in groups.items()} + {k: AgentSet(v, rng=self.rng) for k, v in groups.items()} ) else: return GroupBy(groups) diff --git a/mesa/examples/basic/boltzmann_wealth_model/model.py b/mesa/examples/basic/boltzmann_wealth_model/model.py index 107bece9b9f..fac74e2ab41 100644 --- a/mesa/examples/basic/boltzmann_wealth_model/model.py +++ b/mesa/examples/basic/boltzmann_wealth_model/model.py @@ -27,7 +27,7 @@ class BoltzmannWealth(Model): datacollector (DataCollector): Collects and stores model data """ - def __init__(self, n=100, width=10, height=10, seed=None): + def __init__(self, n=100, width=10, height=10, rng=None): """Initialize the model. Args: @@ -36,7 +36,7 @@ def __init__(self, n=100, width=10, height=10, seed=None): height (int, optional): Grid height. Defaults to 10. seed (int, optional): Random seed. Defaults to None. """ - super().__init__(seed=seed) + super().__init__(rng=rng) self.num_agents = n self.grid = OrthogonalMooreGrid((width, height), random=self.random) @@ -75,4 +75,6 @@ def compute_gini(self): if __name__ == "__main__": - model = BoltzmannWealth(seed=42) + model = BoltzmannWealth(rng=42) + + model.step() diff --git a/mesa/model.py b/mesa/model.py index 7754813ec24..fc2cdffb113 100644 --- a/mesa/model.py +++ b/mesa/model.py @@ -112,7 +112,7 @@ def __init__( type[Agent], AgentSet ] = {} # a dict with an agentset for each class of agents self._all_agents = AgentSet( - [], random=self.random + [], rng=self.rng ) # an agenset with all agents def _wrapped_step(self, *args: Any, **kwargs: Any) -> None: @@ -168,7 +168,7 @@ def register_agent(self, agent): [ agent, ], - random=self.random, + rng=self.rng, ) self._all_agents.add(agent) diff --git a/mesa/util.py b/mesa/util.py index f79524f1ae4..90595ca239b 100644 --- a/mesa/util.py +++ b/mesa/util.py @@ -19,6 +19,8 @@ def wrapper(self, *args, **kwargs): FutureWarning, stacklevel=2, ) + if "rng" in kwargs: + raise ValueError(f"you have to pass either rng or {name}, not both") return method(self, *args, **kwargs) From 1305c8291df558c5c7bca41707458f6188eec7b1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 18 Nov 2025 11:14:29 +0000 Subject: [PATCH 05/24] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/agent.py | 12 +++++------- mesa/model.py | 4 +--- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/mesa/agent.py b/mesa/agent.py index 5aea721d86b..5deea6e1bec 100644 --- a/mesa/agent.py +++ b/mesa/agent.py @@ -12,7 +12,6 @@ import functools import itertools import operator -import sys import warnings import weakref from collections import defaultdict @@ -23,7 +22,7 @@ from typing import TYPE_CHECKING, Any, Literal, overload import numpy as np -from numpy.random import SeedSequence, BitGenerator, Generator, RandomState +from numpy.random import BitGenerator, Generator, RandomState, SeedSequence if TYPE_CHECKING: # We ensure that these are not imported during runtime to prevent cyclic @@ -33,9 +32,9 @@ from mesa.util import deprecate_kwarg - SeedLike = int | np.ndarray[int] | SeedSequence | BitGenerator | Generator | RandomState + class Agent: """Base class for a model agent in Mesa. @@ -291,7 +290,8 @@ def shuffle(self, inplace: bool = False) -> AgentSet: return self else: return AgentSet( - (agent for ref in weakrefs if (agent := ref()) is not None), rng=self.rng + (agent for ref in weakrefs if (agent := ref()) is not None), + rng=self.rng, ) def sort( @@ -613,9 +613,7 @@ def groupby(self, by: Callable | str, result_type: str = "agentset") -> GroupBy: groups[getattr(agent, by)].append(agent) if result_type == "agentset": - return GroupBy( - {k: AgentSet(v, rng=self.rng) for k, v in groups.items()} - ) + return GroupBy({k: AgentSet(v, rng=self.rng) for k, v in groups.items()}) else: return GroupBy(groups) diff --git a/mesa/model.py b/mesa/model.py index fc2cdffb113..fc5a471d03f 100644 --- a/mesa/model.py +++ b/mesa/model.py @@ -111,9 +111,7 @@ def __init__( self._agents_by_type: dict[ type[Agent], AgentSet ] = {} # a dict with an agentset for each class of agents - self._all_agents = AgentSet( - [], rng=self.rng - ) # an agenset with all agents + self._all_agents = AgentSet([], rng=self.rng) # an agenset with all agents def _wrapped_step(self, *args: Any, **kwargs: Any) -> None: """Automatically increments time and steps after calling the user's step method.""" From f8c13a0a20311c537add8bb81ac207218a949884 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Tue, 18 Nov 2025 12:28:06 +0100 Subject: [PATCH 06/24] fixes for unit tests --- mesa/model.py | 4 +++- tests/test_agent.py | 6 +++--- tests/test_examples.py | 2 +- tests/test_model.py | 4 ++-- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/mesa/model.py b/mesa/model.py index fc5a471d03f..2d6fb2054b9 100644 --- a/mesa/model.py +++ b/mesa/model.py @@ -200,12 +200,14 @@ def run_model(self) -> None: def step(self) -> None: """A single step. Fill in here.""" - def reset_randomizer(self, seed: int | None = None) -> None: + @deprecate_kwarg("seed") + def reset_randomizer(self, seed: int | None = None, rng:SeedLike|None=None) -> None: """Reset the model random number generator. Args: seed: A new seed for the RNG; if None, reset using the current seed """ + # fixme if seed is None: seed = self._seed self.random.seed(seed) diff --git a/tests/test_agent.py b/tests/test_agent.py index 900533cf2d1..4f22b1f3bcc 100644 --- a/tests/test_agent.py +++ b/tests/test_agent.py @@ -143,14 +143,14 @@ def test_agentset_initialization(): def test_agentset_initialization_w_random(): """Test agentset initialization.""" model = Model() - empty_agentset = AgentSet([], random=model.random) + empty_agentset = AgentSet([], rng=model.rng) assert len(empty_agentset) == 0 - assert empty_agentset.random == model.random + assert empty_agentset.rng == model.rng agents = [AgentTest(model) for _ in range(10)] agentset = AgentSet(agents) assert len(agentset) == 10 - assert agentset.random == model.random + assert agentset.rng == model.rng def test_agentset_serialization(): diff --git a/tests/test_examples.py b/tests/test_examples.py index 0acb3c301dc..054a64fcab0 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -18,7 +18,7 @@ def test_boltzmann_model(): # noqa: D103 app.page # noqa: B018 - model = BoltzmannWealth(seed=42) + model = BoltzmannWealth(rng=42) for _i in range(10): model.step() diff --git a/tests/test_model.py b/tests/test_model.py index a7e054d12b3..8e5960c216c 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -102,8 +102,8 @@ class Sheep(Agent): wolf = Wolf(model) sheep = Sheep(model) - assert model.agents_by_type[Wolf] == AgentSet([wolf], model) - assert model.agents_by_type[Sheep] == AgentSet([sheep], model) + assert model.agents_by_type[Wolf] == AgentSet([wolf], rng=model.rng) + assert model.agents_by_type[Sheep] == AgentSet([sheep], rng=model.rng) assert len(model.agents_by_type) == 2 From 6806049442b2a69376c203e0f0c0dc038b6807b9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 18 Nov 2025 11:28:54 +0000 Subject: [PATCH 07/24] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/model.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mesa/model.py b/mesa/model.py index 2d6fb2054b9..2d309a8c750 100644 --- a/mesa/model.py +++ b/mesa/model.py @@ -201,7 +201,9 @@ def step(self) -> None: """A single step. Fill in here.""" @deprecate_kwarg("seed") - def reset_randomizer(self, seed: int | None = None, rng:SeedLike|None=None) -> None: + def reset_randomizer( + self, seed: int | None = None, rng: SeedLike | None = None + ) -> None: """Reset the model random number generator. Args: From 8702cdf6c582872e43bedfb9f5054388c28de140 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Tue, 18 Nov 2025 12:32:53 +0100 Subject: [PATCH 08/24] Update model.py --- mesa/examples/basic/boltzmann_wealth_model/model.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mesa/examples/basic/boltzmann_wealth_model/model.py b/mesa/examples/basic/boltzmann_wealth_model/model.py index fac74e2ab41..1a308cc0550 100644 --- a/mesa/examples/basic/boltzmann_wealth_model/model.py +++ b/mesa/examples/basic/boltzmann_wealth_model/model.py @@ -27,7 +27,8 @@ class BoltzmannWealth(Model): datacollector (DataCollector): Collects and stores model data """ - def __init__(self, n=100, width=10, height=10, rng=None): + # fixme seed just here for now, will be removed one PR is complete + def __init__(self, n=100, width=10, height=10, rng=None, seed=None): """Initialize the model. Args: @@ -36,7 +37,7 @@ def __init__(self, n=100, width=10, height=10, rng=None): height (int, optional): Grid height. Defaults to 10. seed (int, optional): Random seed. Defaults to None. """ - super().__init__(rng=rng) + super().__init__(rng=rng, seed=seed) self.num_agents = n self.grid = OrthogonalMooreGrid((width, height), random=self.random) From fe75c58906152cbc991b054b3775be5992010f6d Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Tue, 18 Nov 2025 12:38:25 +0100 Subject: [PATCH 09/24] Update util.py --- mesa/util.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mesa/util.py b/mesa/util.py index 90595ca239b..e66d9b1f5ad 100644 --- a/mesa/util.py +++ b/mesa/util.py @@ -19,7 +19,8 @@ def wrapper(self, *args, **kwargs): FutureWarning, stacklevel=2, ) - if "rng" in kwargs: + + if kwargs.get("rng", None) is not None: raise ValueError(f"you have to pass either rng or {name}, not both") return method(self, *args, **kwargs) From f528b9d29b4ad826b1b6f066e0251e7c55afd799 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 18 Nov 2025 11:38:35 +0000 Subject: [PATCH 10/24] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesa/util.py b/mesa/util.py index e66d9b1f5ad..88197ae4b80 100644 --- a/mesa/util.py +++ b/mesa/util.py @@ -20,7 +20,7 @@ def wrapper(self, *args, **kwargs): stacklevel=2, ) - if kwargs.get("rng", None) is not None: + if kwargs.get("rng") is not None: raise ValueError(f"you have to pass either rng or {name}, not both") return method(self, *args, **kwargs) From f788e0f3fe42440290f5e365a7ae067b1fe4fa38 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Tue, 18 Nov 2025 12:42:45 +0100 Subject: [PATCH 11/24] test fixes --- mesa/model.py | 1 + mesa/util.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mesa/model.py b/mesa/model.py index 2d309a8c750..72df7e74747 100644 --- a/mesa/model.py +++ b/mesa/model.py @@ -208,6 +208,7 @@ def reset_randomizer( Args: seed: A new seed for the RNG; if None, reset using the current seed + rng: A new seed for the RNG; if None, reset using the current seed """ # fixme if seed is None: diff --git a/mesa/util.py b/mesa/util.py index 88197ae4b80..e259beaff1f 100644 --- a/mesa/util.py +++ b/mesa/util.py @@ -13,7 +13,7 @@ def inner_wrapper(method): @wraps(method) def wrapper(self, *args, **kwargs): """Inner function.""" - if name in kwargs: + if kwargs.get(name) is not None: warnings.warn( f"The use of {name} is deprecated, please use rng instead", FutureWarning, From 4a4ae9aa8ed1c2b2c2027a9c5ee56fa443d9ed64 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Tue, 18 Nov 2025 13:37:52 +0100 Subject: [PATCH 12/24] update benchmark examples to use rng instead of seed --- benchmarks/global_benchmark.py | 4 ++-- mesa/examples/advanced/wolf_sheep/model.py | 6 +++--- mesa/examples/basic/boid_flockers/model.py | 7 +++---- mesa/examples/basic/boltzmann_wealth_model/model.py | 7 +++---- mesa/examples/basic/schelling/model.py | 6 +++--- 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/benchmarks/global_benchmark.py b/benchmarks/global_benchmark.py index b5b077e4ac0..f787d0af835 100644 --- a/benchmarks/global_benchmark.py +++ b/benchmarks/global_benchmark.py @@ -32,9 +32,9 @@ def run_model(model_class, seed, parameters): start_init = timeit.default_timer() if model_class.__name__ in uses_simulator: simulator = ABMSimulator() - model = model_class(simulator=simulator, seed=seed, **parameters) + model = model_class(simulator=simulator, rng=seed, **parameters) else: - model = model_class(seed=seed, **parameters) + model = model_class(rng=seed, **parameters) end_init_start_run = timeit.default_timer() diff --git a/mesa/examples/advanced/wolf_sheep/model.py b/mesa/examples/advanced/wolf_sheep/model.py index e93a0bffa4a..b212f814def 100644 --- a/mesa/examples/advanced/wolf_sheep/model.py +++ b/mesa/examples/advanced/wolf_sheep/model.py @@ -40,7 +40,7 @@ def __init__( grass=True, grass_regrowth_time=30, sheep_gain_from_food=4, - seed=None, + rng=None, simulator: ABMSimulator = None, ): """Create a new Wolf-Sheep model with the given parameters. @@ -57,10 +57,10 @@ def __init__( grass_regrowth_time: How long it takes for a grass patch to regrow once it is eaten sheep_gain_from_food: Energy sheep gain from grass, if enabled - seed: Random seed + rng: Random seed simulator: ABMSimulator instance for event scheduling """ - super().__init__(seed=seed) + super().__init__(seed=rng) self.simulator = simulator self.simulator.setup(self) diff --git a/mesa/examples/basic/boid_flockers/model.py b/mesa/examples/basic/boid_flockers/model.py index 58203b889ca..f2e2608d63c 100644 --- a/mesa/examples/basic/boid_flockers/model.py +++ b/mesa/examples/basic/boid_flockers/model.py @@ -8,7 +8,6 @@ import os import sys -sys.path.insert(0, os.path.abspath("../../../..")) import numpy as np @@ -32,7 +31,7 @@ def __init__( cohere=0.03, separate=0.015, match=0.05, - seed=None, + rng=None, ): """Create a new Boids Flocking model. @@ -46,9 +45,9 @@ def __init__( cohere: Weight of cohesion behavior (default: 0.03) separate: Weight of separation behavior (default: 0.015) match: Weight of alignment behavior (default: 0.05) - seed: Random seed for reproducibility (default: None) + rng: Random seed for reproducibility (default: None) """ - super().__init__(seed=seed) + super().__init__(rng=rng) self.agent_angles = np.zeros( population_size ) # holds the angle representing the direction of all agents at a given step diff --git a/mesa/examples/basic/boltzmann_wealth_model/model.py b/mesa/examples/basic/boltzmann_wealth_model/model.py index 1a308cc0550..53b3710611a 100644 --- a/mesa/examples/basic/boltzmann_wealth_model/model.py +++ b/mesa/examples/basic/boltzmann_wealth_model/model.py @@ -27,17 +27,16 @@ class BoltzmannWealth(Model): datacollector (DataCollector): Collects and stores model data """ - # fixme seed just here for now, will be removed one PR is complete - def __init__(self, n=100, width=10, height=10, rng=None, seed=None): + def __init__(self, n=100, width=10, height=10, rng=None): """Initialize the model. Args: n (int, optional): Number of agents. Defaults to 100. width (int, optional): Grid width. Defaults to 10. height (int, optional): Grid height. Defaults to 10. - seed (int, optional): Random seed. Defaults to None. + rng (int, optional): Random seed. Defaults to None. """ - super().__init__(rng=rng, seed=seed) + super().__init__(rng=rng) self.num_agents = n self.grid = OrthogonalMooreGrid((width, height), random=self.random) diff --git a/mesa/examples/basic/schelling/model.py b/mesa/examples/basic/schelling/model.py index 181ee1cc2a2..15b3514c415 100644 --- a/mesa/examples/basic/schelling/model.py +++ b/mesa/examples/basic/schelling/model.py @@ -15,7 +15,7 @@ def __init__( minority_pc: float = 0.5, homophily: float = 0.4, radius: int = 1, - seed=None, + rng=None, ): """Create a new Schelling model. @@ -26,9 +26,9 @@ def __init__( minority_pc: Chance for an agent to be in minority class (0-1) homophily: Minimum number of similar neighbors needed for happiness radius: Search radius for checking neighbor similarity - seed: Seed for reproducibility + rng: Seed for reproducibility """ - super().__init__(seed=seed) + super().__init__(rng=rng) # Model parameters self.density = density From ec6d8eaa20eed30de7cbb6859d45431363c5d601 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 18 Nov 2025 12:38:02 +0000 Subject: [PATCH 13/24] [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 | 5 ----- 1 file changed, 5 deletions(-) diff --git a/mesa/examples/basic/boid_flockers/model.py b/mesa/examples/basic/boid_flockers/model.py index f2e2608d63c..6d4ae8859c2 100644 --- a/mesa/examples/basic/boid_flockers/model.py +++ b/mesa/examples/basic/boid_flockers/model.py @@ -5,11 +5,6 @@ Uses numpy arrays to represent vectors. """ -import os -import sys - - - import numpy as np from mesa import Model From 6402f0ae9c07268658ea5caf579feaf858c67216 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Tue, 18 Nov 2025 13:41:02 +0100 Subject: [PATCH 14/24] fix other examples and tests --- .../advanced/alliance_formation/model.py | 4 ++-- .../advanced/epstein_civil_violence/model.py | 4 ++-- mesa/examples/advanced/pd_grid/model.py | 4 ++-- .../examples/advanced/sugarscape_g1mt/model.py | 4 ++-- .../basic/conways_game_of_life/model.py | 4 ++-- mesa/examples/basic/virus_on_network/model.py | 4 ++-- tests/test_examples.py | 18 +++++++++--------- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/mesa/examples/advanced/alliance_formation/model.py b/mesa/examples/advanced/alliance_formation/model.py index 6eaa21ab414..6072c760eae 100644 --- a/mesa/examples/advanced/alliance_formation/model.py +++ b/mesa/examples/advanced/alliance_formation/model.py @@ -15,7 +15,7 @@ class MultiLevelAllianceModel(mesa.Model): Model for simulating multi-level alliances among agents. """ - def __init__(self, n=50, mean=0.5, std_dev=0.1, seed=42): + def __init__(self, n=50, mean=0.5, std_dev=0.1, rng=42): """ Initialize the model. @@ -25,7 +25,7 @@ def __init__(self, n=50, mean=0.5, std_dev=0.1, seed=42): std_dev (float): Standard deviation for normal distribution. seed (int): Random seed. """ - super().__init__(seed=seed) + super().__init__(rng=rng) self.population = n self.network = nx.Graph() # Initialize the network self.datacollector = mesa.DataCollector(model_reporters={"Network": "network"}) diff --git a/mesa/examples/advanced/epstein_civil_violence/model.py b/mesa/examples/advanced/epstein_civil_violence/model.py index 171c0cffc75..ffcb7b10750 100644 --- a/mesa/examples/advanced/epstein_civil_violence/model.py +++ b/mesa/examples/advanced/epstein_civil_violence/model.py @@ -47,9 +47,9 @@ def __init__( arrest_prob_constant=2.3, movement=True, max_iters=1000, - seed=None, + rng=None, ): - super().__init__(seed=seed) + super().__init__(rng=rng) self.movement = movement self.max_iters = max_iters diff --git a/mesa/examples/advanced/pd_grid/model.py b/mesa/examples/advanced/pd_grid/model.py index 1970f662f26..17e80829ec4 100644 --- a/mesa/examples/advanced/pd_grid/model.py +++ b/mesa/examples/advanced/pd_grid/model.py @@ -14,7 +14,7 @@ class PdGrid(mesa.Model): payoff = {("C", "C"): 1, ("C", "D"): 0, ("D", "C"): 1.6, ("D", "D"): 0} def __init__( - self, width=50, height=50, activation_order="Random", payoffs=None, seed=None + self, width=50, height=50, activation_order="Random", payoffs=None, rng=rng ): """ Create a new Spatial Prisoners' Dilemma Model. @@ -25,7 +25,7 @@ def __init__( Determines the agent activation regime. payoffs: (optional) Dictionary of (move, neighbor_move) payoffs. """ - super().__init__(seed=seed) + super().__init__(rng=rng) self.activation_order = activation_order self.grid = OrthogonalMooreGrid((width, height), torus=True, random=self.random) diff --git a/mesa/examples/advanced/sugarscape_g1mt/model.py b/mesa/examples/advanced/sugarscape_g1mt/model.py index 465e7be240a..99e1f6a5ad7 100644 --- a/mesa/examples/advanced/sugarscape_g1mt/model.py +++ b/mesa/examples/advanced/sugarscape_g1mt/model.py @@ -53,9 +53,9 @@ def __init__( vision_min=1, vision_max=5, enable_trade=True, - seed=None, + rng=None, ): - super().__init__(seed=seed) + super().__init__(rng=rng) # Initiate width and height of sugarscape self.width = width self.height = height diff --git a/mesa/examples/basic/conways_game_of_life/model.py b/mesa/examples/basic/conways_game_of_life/model.py index 32b304b8f54..66fbf0fe340 100644 --- a/mesa/examples/basic/conways_game_of_life/model.py +++ b/mesa/examples/basic/conways_game_of_life/model.py @@ -6,9 +6,9 @@ class ConwaysGameOfLife(Model): """Represents the 2-dimensional array of cells in Conway's Game of Life.""" - def __init__(self, width=50, height=50, initial_fraction_alive=0.2, seed=None): + def __init__(self, width=50, height=50, initial_fraction_alive=0.2, rng=None): """Create a new playing area of (width, height) cells.""" - super().__init__(seed=seed) + super().__init__(rng=rng) # Use a simple grid, where edges wrap around. self.grid = OrthogonalMooreGrid((width, height), capacity=1, torus=True) diff --git a/mesa/examples/basic/virus_on_network/model.py b/mesa/examples/basic/virus_on_network/model.py index 81c1b9e4ade..100954dd967 100644 --- a/mesa/examples/basic/virus_on_network/model.py +++ b/mesa/examples/basic/virus_on_network/model.py @@ -36,9 +36,9 @@ def __init__( virus_check_frequency=0.4, recovery_chance=0.3, gain_resistance_chance=0.5, - seed=None, + rng=None, ): - super().__init__(seed=seed) + super().__init__(rng=rng) prob = avg_node_degree / num_nodes graph = nx.erdos_renyi_graph(n=num_nodes, p=prob) self.grid = Network(graph, capacity=1, random=self.random) diff --git a/tests/test_examples.py b/tests/test_examples.py index 054a64fcab0..b578da55a9e 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -29,7 +29,7 @@ def test_conways_game_model(): # noqa: D103 app.page # noqa: B018 - model = ConwaysGameOfLife(seed=42) + model = ConwaysGameOfLife(rng=42) for _i in range(10): model.step() @@ -39,7 +39,7 @@ def test_schelling_model(): # noqa: D103 app.page # noqa: B018 - model = Schelling(seed=42) + model = Schelling(rng=42) for _i in range(10): model.step() @@ -49,7 +49,7 @@ def test_virus_on_network(): # noqa: D103 app.page # noqa: B018 - model = VirusOnNetwork(seed=42) + model = VirusOnNetwork(rng=42) for _i in range(10): model.step() @@ -59,7 +59,7 @@ def test_boid_flockers(): # noqa: D103 app.page # noqa: B018 - model = BoidFlockers(seed=42) + model = BoidFlockers(rng=42) for _i in range(10): model.step() @@ -70,7 +70,7 @@ def test_epstein(): # noqa: D103 app.page # noqa: B018 - model = EpsteinCivilViolence(seed=42) + model = EpsteinCivilViolence(rng=42) for _i in range(10): model.step() @@ -81,7 +81,7 @@ def test_pd_grid(): # noqa: D103 app.page # noqa: B018 - model = PdGrid(seed=42) + model = PdGrid(rng=42) for _i in range(10): model.step() @@ -92,7 +92,7 @@ def test_sugarscape_g1mt(): # noqa: D103 app.page # noqa: B018 - model = SugarscapeG1mt(seed=42) + model = SugarscapeG1mt(rng=42) for _i in range(10): model.step() @@ -105,7 +105,7 @@ def test_wolf_sheep(): # noqa: D103 app.page # noqa: B018 simulator = ABMSimulator() - WolfSheep(seed=42, simulator=simulator) + WolfSheep(rng=42, simulator=simulator) simulator.run_for(10) @@ -114,7 +114,7 @@ def test_alliance_formation_model(): # noqa: D103 app.page # noqa: B018 - model = MultiLevelAllianceModel(50, seed=42) + model = MultiLevelAllianceModel(50, rng=42) for _i in range(10): model.step() From 930d4056766f94fe04bd740f5481f29bc617b51d Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Tue, 18 Nov 2025 15:25:37 +0100 Subject: [PATCH 15/24] Update model.py --- mesa/examples/advanced/pd_grid/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesa/examples/advanced/pd_grid/model.py b/mesa/examples/advanced/pd_grid/model.py index 17e80829ec4..a7b3bf3bd47 100644 --- a/mesa/examples/advanced/pd_grid/model.py +++ b/mesa/examples/advanced/pd_grid/model.py @@ -14,7 +14,7 @@ class PdGrid(mesa.Model): payoff = {("C", "C"): 1, ("C", "D"): 0, ("D", "C"): 1.6, ("D", "D"): 0} def __init__( - self, width=50, height=50, activation_order="Random", payoffs=None, rng=rng + self, width=50, height=50, activation_order="Random", payoffs=None, rng=None ): """ Create a new Spatial Prisoners' Dilemma Model. From 831e5c5fdd003a033163c328e78de37df90e7ad6 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Tue, 18 Nov 2025 15:48:10 +0100 Subject: [PATCH 16/24] Update test_examples_viz.py --- tests/test_examples_viz.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/test_examples_viz.py b/tests/test_examples_viz.py index 758f44a13fe..95dc5e2f2b8 100644 --- a/tests/test_examples_viz.py +++ b/tests/test_examples_viz.py @@ -104,7 +104,7 @@ def run_model_test( @pytest.mark.filterwarnings("ignore::DeprecationWarning") def test_schelling_model(solara_test, page_session: playwright.sync_api.Page): """Test schelling model behavior and visualization.""" - model = Schelling(seed=42) + model = Schelling(rng=42) def agent_portrayal(agent): return {"color": "tab:orange" if agent.type == 0 else "tab:blue"} @@ -130,7 +130,7 @@ def test_wolf_sheep_model(solara_test, page_session: playwright.sync_api.Page): ) from mesa.experimental.devs import ABMSimulator # noqa: PLC0415 - model = WolfSheep(simulator=ABMSimulator(), seed=42) + model = WolfSheep(simulator=ABMSimulator(), rng=42) def agent_portrayal(agent): if agent is None: @@ -172,7 +172,7 @@ def agent_portrayal(agent): @pytest.mark.filterwarnings("ignore::DeprecationWarning") def test_boid_flockers_model(solara_test, page_session: playwright.sync_api.Page): """Test boid flockers model behavior and visualization.""" - model = BoidFlockers(seed=42) + model = BoidFlockers(rng=42) def agent_portrayal(agent): return {"color": "tab:blue"} @@ -189,7 +189,7 @@ def agent_portrayal(agent): @pytest.mark.filterwarnings("ignore::DeprecationWarning") def test_boltzmann_wealth_model(solara_test, page_session: playwright.sync_api.Page): """Test Boltzmann wealth model behavior and visualization.""" - model = BoltzmannWealth(seed=42) + model = BoltzmannWealth(rng=42) def agent_portrayal(agent): color = agent.wealth # we are using a colormap to translate wealth to color @@ -211,7 +211,7 @@ def test_virus_on_network_model(solara_test, page_session: playwright.sync_api.P """Test virus on network model behavior and visualization.""" from mesa.examples.basic.virus_on_network.model import State # noqa: PLC0415 - model = VirusOnNetwork(seed=42) + model = VirusOnNetwork(rng=42) def agent_portrayal(agent): node_color_dict = { @@ -242,7 +242,7 @@ def test_conways_game_of_life_model( solara_test, page_session: playwright.sync_api.Page ): """Test Conway's Game of Life model behavior and visualization.""" - model = ConwaysGameOfLife(seed=42) + model = ConwaysGameOfLife(rng=42) def agent_portrayal(agent): return { @@ -277,7 +277,7 @@ def test_epstein_civil_violence_model( agent_colors, ) - model = EpsteinCivilViolence(seed=42) + model = EpsteinCivilViolence(rng=42) def agent_portrayal(agent): if agent is None: @@ -308,7 +308,7 @@ def agent_portrayal(agent): @pytest.mark.filterwarnings("ignore::DeprecationWarning") def test_sugarscape_g1mt_model(solara_test, page_session: playwright.sync_api.Page): """Test Sugarscape G1mt model behavior and visualization.""" - model = SugarscapeG1mt(seed=42) + model = SugarscapeG1mt(rng=42) def agent_portrayal(agent): return {"marker": "o", "color": "red", "size": 10} @@ -327,7 +327,7 @@ def agent_portrayal(agent): @pytest.mark.filterwarnings("ignore::DeprecationWarning") def test_pd_grid_model(solara_test, page_session: playwright.sync_api.Page): """Test Prisoner's Dilemma model behavior and visualization.""" - model = PdGrid(seed=42) + model = PdGrid(rng=42) def agent_portrayal(agent): return { From 6aefc7a24a64ec3a05552f578d28682962844594 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Tue, 18 Nov 2025 18:00:34 +0100 Subject: [PATCH 17/24] replace random/seed with rng in all spaces and some other places --- mesa/discrete_space/cell.py | 23 +++++++++++++++++-- mesa/discrete_space/cell_collection.py | 22 +++++++++++++----- mesa/discrete_space/discrete_space.py | 22 +++++++++++++----- mesa/discrete_space/grid.py | 12 ++++++++-- mesa/discrete_space/network.py | 10 ++++++-- mesa/discrete_space/voronoi.py | 11 +++++++-- mesa/examples/advanced/wolf_sheep/agents.py | 5 +++- .../basic/boltzmann_wealth_model/model.py | 4 ++-- mesa/examples/basic/schelling/model.py | 2 +- .../continuous_space/continuous_space.py | 16 +++++++++---- 10 files changed, 99 insertions(+), 28 deletions(-) diff --git a/mesa/discrete_space/cell.py b/mesa/discrete_space/cell.py index 744f6d49d43..e077b653e3b 100644 --- a/mesa/discrete_space/cell.py +++ b/mesa/discrete_space/cell.py @@ -17,6 +17,10 @@ from functools import cache, cached_property from random import Random from typing import TYPE_CHECKING +import warnings + +from numpy.random import BitGenerator, Generator, RandomState, SeedSequence +import numpy as np from mesa.discrete_space.cell_agent import CellAgent from mesa.discrete_space.cell_collection import CellCollection @@ -25,6 +29,7 @@ if TYPE_CHECKING: from mesa.agent import Agent +SeedLike = int | np.ndarray[int] | SeedSequence | BitGenerator | Generator | RandomState Coordinate = tuple[int, ...] @@ -47,6 +52,7 @@ class Cell: "coordinate", "properties", "random", + "rng" ] @deprecate_kwarg("random") @@ -55,6 +61,7 @@ def __init__( coordinate: Coordinate, capacity: int | None = None, random: Random | None = None, + rng: SeedLike | None = None, ) -> None: """Initialise the cell. @@ -62,6 +69,7 @@ def __init__( coordinate: coordinates of the cell capacity (int) : the capacity of the cell. If None, the capacity is infinite random (Random) : the random number generator to use + rng (SeedLike | None): the random number generator """ super().__init__() @@ -74,7 +82,18 @@ def __init__( self.properties: dict[ Coordinate, object ] = {} # fixme still used by voronoi mesh - self.random = random + + if (random is None and rng 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, + ) + rng = np.random.default_rng() + if random is not None: + rng = np.random.default_rng(random.getstate()[1]) + + self.rng = np.random.default_rng(rng) def connect(self, other: Cell, key: Coordinate | None = None) -> None: """Connects this cell to another cell. @@ -175,7 +194,7 @@ def get_neighborhood( """ return CellCollection[Cell]( self._neighborhood(radius=radius, include_center=include_center), - random=self.random, + rng=self.rng, ) # FIXME: Revisit caching strategy on methods diff --git a/mesa/discrete_space/cell_collection.py b/mesa/discrete_space/cell_collection.py index ec72d202ca4..69ff7e3db26 100644 --- a/mesa/discrete_space/cell_collection.py +++ b/mesa/discrete_space/cell_collection.py @@ -22,12 +22,17 @@ from random import Random from typing import TYPE_CHECKING, TypeVar +from numpy.random import BitGenerator, Generator, RandomState, SeedSequence +import numpy as np + from mesa.util import deprecate_kwarg if TYPE_CHECKING: from mesa.discrete_space.cell import Cell from mesa.discrete_space.cell_agent import CellAgent +SeedLike = int | np.ndarray[int] | SeedSequence | BitGenerator | Generator | RandomState + T = TypeVar("T", bound="Cell") @@ -52,12 +57,14 @@ def __init__( self, cells: Mapping[T, list[CellAgent]] | Iterable[T], random: Random | None = None, + rng: SeedLike | None = None, ) -> None: """Initialize a CellCollection. Args: cells: cells to add to the collection random: a seeded random number generator. + rng (SeedLike | None): the random number generator """ if isinstance(cells, dict): self._cells = cells @@ -69,14 +76,17 @@ def __init__( next(iter(self._cells.keys())).capacity if self._cells else None ) - if random is None: + if (random is None and rng 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 + rng = np.random.default_rng() + if random is not None: + rng = np.random.default_rng(random.getstate()[1]) + + self.rng = np.random.default_rng(rng) def __iter__(self): # noqa return iter(self._cells) @@ -101,7 +111,7 @@ def agents(self) -> Iterable[CellAgent]: # noqa def select_random_cell(self) -> T: """Select a random cell.""" - return self.random.choice(self.cells) + return self.rng.choice(self.cells, replace=False) def select_random_agent(self) -> CellAgent: """Select a random agent. @@ -111,7 +121,7 @@ def select_random_agent(self) -> CellAgent: """ - return self.random.choice(list(self.agents)) + return self.rng.choice(list(self.agents), replace=False) def select( self, @@ -145,4 +155,4 @@ def cell_generator(filter_func, at_most): yield cell count += 1 - return CellCollection(cell_generator(filter_func, at_most), random=self.random) + return CellCollection(cell_generator(filter_func, at_most), rng=self.rng) diff --git a/mesa/discrete_space/discrete_space.py b/mesa/discrete_space/discrete_space.py index 40fa8edbcd8..5a7deacc92c 100644 --- a/mesa/discrete_space/discrete_space.py +++ b/mesa/discrete_space/discrete_space.py @@ -20,11 +20,16 @@ from random import Random from typing import TypeVar +from numpy.random import BitGenerator, Generator, RandomState, SeedSequence +import numpy as np + from mesa.agent import AgentSet from mesa.discrete_space.cell import Cell from mesa.discrete_space.cell_collection import CellCollection from mesa.util import deprecate_kwarg + +SeedLike = int | np.ndarray[int] | SeedSequence | BitGenerator | Generator | RandomState T = TypeVar("T", bound=Cell) @@ -52,6 +57,7 @@ def __init__( capacity: int | None = None, cell_klass: type[T] = Cell, random: Random | None = None, + rng: SeedLike | None = None, ): """Instantiate a DiscreteSpace. @@ -59,18 +65,22 @@ def __init__( capacity: capacity of cells cell_klass: base class for all cells random: random number generator + rng (SeedLike | None): the random number generator """ super().__init__() self.capacity = capacity self._cells: dict[tuple[int, ...], T] = {} - if random is None: + if (random is None and rng 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 + rng = np.random.default_rng() + if random is not None: + rng = np.random.default_rng(random.getstate()[1]) + + self.rng = np.random.default_rng(rng) self.cell_klass = cell_klass self._empties: dict[tuple[int, ...], None] = {} @@ -82,7 +92,7 @@ def cutoff_empties(self): # noqa @property def agents(self) -> AgentSet: """Return an AgentSet with the agents in the space.""" - return AgentSet(self.all_cells.agents, random=self.random) + return AgentSet(self.all_cells.agents, rng=self.rng) def _connect_cells(self): ... def _connect_single_cell(self, cell: T): ... @@ -153,7 +163,7 @@ def remove_connection(self, cell1: T, cell2: T): def all_cells(self): """Return all cells in space.""" return CellCollection( - {cell: cell._agents for cell in self._cells.values()}, random=self.random + {cell: cell._agents for cell in self._cells.values()}, rng=self.rng ) def __iter__(self): # noqa @@ -169,7 +179,7 @@ def empties(self) -> CellCollection[T]: def select_random_empty_cell(self) -> T: """Select random empty cell.""" - return self.random.choice(list(self.empties)) + return self.rng.choice(list(self.empties)) def __setstate__(self, state): """Set the state of the discrete space and rebuild the connections.""" diff --git a/mesa/discrete_space/grid.py b/mesa/discrete_space/grid.py index 7b0c5f5a586..31fed798f60 100644 --- a/mesa/discrete_space/grid.py +++ b/mesa/discrete_space/grid.py @@ -19,12 +19,17 @@ from random import Random from typing import Any, TypeVar +from numpy.random import BitGenerator, Generator, RandomState, SeedSequence +import numpy as np + from mesa.discrete_space import Cell, DiscreteSpace from mesa.discrete_space.property_layer import ( HasPropertyLayers, PropertyDescriptor, ) +from mesa.util import deprecate_kwarg +SeedLike = int | np.ndarray[int] | SeedSequence | BitGenerator | Generator | RandomState T = TypeVar("T", bound=Cell) @@ -81,12 +86,14 @@ def height(self) -> int: """Convenience access to the height of the grid.""" return self.dimensions[1] + @deprecate_kwarg("random") def __init__( self, dimensions: Sequence[int], torus: bool = False, capacity: float | None = None, random: Random | None = None, + rng: SeedLike | None = None, cell_klass: type[T] = Cell, ) -> None: """Initialise the grid class. @@ -96,9 +103,10 @@ def __init__( torus: whether the space wraps capacity: capacity of the grid cell random: a random number generator + rng (SeedLike | None): the random number generator cell_klass: the base class to use for the cells """ - super().__init__(capacity=capacity, random=random, cell_klass=cell_klass) + super().__init__(capacity=capacity, rng=rng, random=random, cell_klass=cell_klass) self.torus = torus self.dimensions = dimensions self._try_random = True @@ -116,7 +124,7 @@ def __init__( coordinates = product(*(range(dim) for dim in self.dimensions)) self._cells = { - coord: self.cell_klass(coord, capacity, random=self.random) + coord: self.cell_klass(coord, capacity, rng=self.rng) for coord in coordinates } self._connect_cells() diff --git a/mesa/discrete_space/network.py b/mesa/discrete_space/network.py index b65dccd3faf..9f75c2a006e 100644 --- a/mesa/discrete_space/network.py +++ b/mesa/discrete_space/network.py @@ -15,10 +15,14 @@ from random import Random from typing import Any +from numpy.random import BitGenerator, Generator, RandomState, SeedSequence +import numpy as np + from mesa.discrete_space.cell import Cell from mesa.discrete_space.discrete_space import DiscreteSpace from mesa.util import deprecate_kwarg +SeedLike = int | np.ndarray[int] | SeedSequence | BitGenerator | Generator | RandomState class Network(DiscreteSpace[Cell]): """A networked discrete space.""" @@ -29,6 +33,7 @@ def __init__( G: Any, # noqa: N803 capacity: int | None = None, random: Random | None = None, + rng: SeedLike | None = None, cell_klass: type[Cell] = Cell, ) -> None: """A Networked grid. @@ -37,15 +42,16 @@ def __init__( G: a NetworkX Graph instance. capacity (int) : the capacity of the cell random (Random): a random number generator + rng (SeedLike | None): the random number generator cell_klass (type[Cell]): The base Cell class to use in the Network """ - super().__init__(capacity=capacity, random=random, cell_klass=cell_klass) + super().__init__(capacity=capacity, random=random, rng=rng, cell_klass=cell_klass) self.G = G for node_id in self.G.nodes: self._cells[node_id] = self.cell_klass( - node_id, capacity, random=self.random + node_id, capacity, rng=self.rng ) self._connect_cells() diff --git a/mesa/discrete_space/voronoi.py b/mesa/discrete_space/voronoi.py index 8ff195bac31..7d7147c233d 100644 --- a/mesa/discrete_space/voronoi.py +++ b/mesa/discrete_space/voronoi.py @@ -16,10 +16,14 @@ from itertools import combinations from random import Random +from numpy.random import BitGenerator, Generator, RandomState, SeedSequence import numpy as np from mesa.discrete_space.cell import Cell from mesa.discrete_space.discrete_space import DiscreteSpace +from mesa.util import deprecate_kwarg + +SeedLike = int | np.ndarray[int] | SeedSequence | BitGenerator | Generator | RandomState class Delaunay: @@ -179,11 +183,13 @@ class VoronoiGrid(DiscreteSpace): voronoi_coordinates: list regions: list + @deprecate_kwarg("seed") def __init__( self, centroids_coordinates: Sequence[Sequence[float]], capacity: float | None = None, random: Random | None = None, + rng: SeedLike | None = None, cell_klass: type[Cell] = Cell, capacity_function: callable = round_float, ) -> None: @@ -197,16 +203,17 @@ def __init__( centroids_coordinates: coordinates of centroids to build the tessellation space capacity (int) : capacity of the cells in the discrete space random (Random): random number generator + rng (SeedLike | None): the random number generator cell_klass (type[Cell]): type of cell class capacity_function (Callable): function to compute (int) capacity according to (float) area """ - super().__init__(capacity=capacity, random=random, cell_klass=cell_klass) + super().__init__(capacity=capacity, random=random, rng=rng, cell_klass=cell_klass) self.centroids_coordinates = centroids_coordinates self._validate_parameters() self._cells = { - i: cell_klass(self.centroids_coordinates[i], capacity, random=self.random) + i: cell_klass(self.centroids_coordinates[i], capacity, rng=self.random) for i in range(len(self.centroids_coordinates)) } diff --git a/mesa/examples/advanced/wolf_sheep/agents.py b/mesa/examples/advanced/wolf_sheep/agents.py index b14963442c2..344a6a0276e 100644 --- a/mesa/examples/advanced/wolf_sheep/agents.py +++ b/mesa/examples/advanced/wolf_sheep/agents.py @@ -52,6 +52,9 @@ def step(self): elif self.random.random() < self.p_reproduce: self.spawn_offspring() + def move(self): + """abstract method to be implemented by subclasses.""" + class Sheep(Animal): """A sheep that walks around, reproduces (asexually) and gets eaten.""" @@ -94,7 +97,7 @@ def feed(self): """If possible, eat a sheep at current location.""" sheep = [obj for obj in self.cell.agents if isinstance(obj, Sheep)] if sheep: # If there are any sheep present - sheep_to_eat = self.random.choice(sheep) + sheep_to_eat = self.rng.choice(sheep) if len(sheep) > 1 else sheep[0] self.energy += self.energy_from_food sheep_to_eat.remove() diff --git a/mesa/examples/basic/boltzmann_wealth_model/model.py b/mesa/examples/basic/boltzmann_wealth_model/model.py index 53b3710611a..8b5750bc1b6 100644 --- a/mesa/examples/basic/boltzmann_wealth_model/model.py +++ b/mesa/examples/basic/boltzmann_wealth_model/model.py @@ -39,7 +39,7 @@ def __init__(self, n=100, width=10, height=10, rng=None): super().__init__(rng=rng) self.num_agents = n - self.grid = OrthogonalMooreGrid((width, height), random=self.random) + self.grid = OrthogonalMooreGrid((width, height), rng=self.rng) # Set up data collection self.datacollector = DataCollector( @@ -49,7 +49,7 @@ def __init__(self, n=100, width=10, height=10, rng=None): MoneyAgent.create_agents( self, self.num_agents, - self.random.choices(self.grid.all_cells.cells, k=self.num_agents), + self.rng.choice(self.grid.all_cells.cells, size=self.num_agents), ) self.running = True diff --git a/mesa/examples/basic/schelling/model.py b/mesa/examples/basic/schelling/model.py index 15b3514c415..221fc9486c9 100644 --- a/mesa/examples/basic/schelling/model.py +++ b/mesa/examples/basic/schelling/model.py @@ -35,7 +35,7 @@ def __init__( self.minority_pc = minority_pc # Initialize grid - self.grid = OrthogonalMooreGrid((width, height), random=self.random, capacity=1) + self.grid = OrthogonalMooreGrid((width, height), rng=self.rng, capacity=1) # Track happiness self.happy = 0 diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index 330a22068ef..1b65f2a5df6 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -6,12 +6,15 @@ from random import Random import numpy as np +from numpy.random import BitGenerator, Generator, RandomState, SeedSequence + from numpy.typing import ArrayLike from scipy.spatial.distance import cdist from mesa.agent import Agent, AgentSet from mesa.util import deprecate_kwarg +SeedLike = int | np.ndarray[int] | SeedSequence | BitGenerator | Generator | RandomState class ContinuousSpace: """Continuous space where each agent can have an arbitrary position.""" @@ -52,6 +55,7 @@ def __init__( dimensions: ArrayLike, torus: bool = False, random: Random | None = None, + rng: SeedLike | None = None, n_agents: int = 100, ) -> None: """Create a new continuous space. @@ -60,6 +64,7 @@ def __init__( 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 + rng (SeedLike | None): the random number generator 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, @@ -67,14 +72,17 @@ def __init__( """ - if random is None: + if (random is None and rng 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 + rng = np.random.default_rng() + if random is not None: + rng = np.random.default_rng(random.getstate()[1]) + + self.rng = np.random.default_rng(rng) self.dimensions: np.array = np.asanyarray(dimensions) self.ndims: int = self.dimensions.shape[0] @@ -104,7 +112,7 @@ def __init__( @property def agents(self) -> AgentSet: """Return an AgentSet with the agents in the space.""" - return AgentSet(self.active_agents, random=self.random) + return AgentSet(self.active_agents, rng=self.rng) def _add_agent(self, agent: Agent) -> int: """Helper method for adding an agent to the space. From 08710e0b5281e1d9203bb39be8843e1bfa8475f1 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Tue, 18 Nov 2025 18:27:09 +0100 Subject: [PATCH 18/24] 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 1b65f2a5df6..eb0fe680ed4 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -72,7 +72,7 @@ def __init__( """ - if (random is None and rng is None): + if random is None and rng is None: warnings.warn( "Random number generator not specified, this can make models non-reproducible. Please pass a random number generator explicitly", UserWarning, From 9915d9df81eac668c8adea5ea1f7cda1675a763e Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Tue, 18 Nov 2025 20:11:42 +0100 Subject: [PATCH 19/24] replace rng.choice with list[rng.integers(0, len(list)] this is orders of magnitude faster if you want to sample only a single item from a list. --- mesa/discrete_space/cell_collection.py | 5 +++-- mesa/discrete_space/discrete_space.py | 3 ++- mesa/examples/advanced/wolf_sheep/model.py | 10 +++++----- mesa/examples/basic/boid_flockers/model.py | 2 +- mesa/examples/basic/boltzmann_wealth_model/agents.py | 2 +- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/mesa/discrete_space/cell_collection.py b/mesa/discrete_space/cell_collection.py index 69ff7e3db26..514236ced60 100644 --- a/mesa/discrete_space/cell_collection.py +++ b/mesa/discrete_space/cell_collection.py @@ -111,7 +111,7 @@ def agents(self) -> Iterable[CellAgent]: # noqa def select_random_cell(self) -> T: """Select a random cell.""" - return self.rng.choice(self.cells, replace=False) + return self.cells[self.rng.integers(0, len(self.cells))] def select_random_agent(self) -> CellAgent: """Select a random agent. @@ -121,7 +121,8 @@ def select_random_agent(self) -> CellAgent: """ - return self.rng.choice(list(self.agents), replace=False) + agents = list(self.agents) + return agents[self.rng.integers(0, len(agents))] def select( self, diff --git a/mesa/discrete_space/discrete_space.py b/mesa/discrete_space/discrete_space.py index 5a7deacc92c..2b6fb1404a3 100644 --- a/mesa/discrete_space/discrete_space.py +++ b/mesa/discrete_space/discrete_space.py @@ -179,7 +179,8 @@ def empties(self) -> CellCollection[T]: def select_random_empty_cell(self) -> T: """Select random empty cell.""" - return self.rng.choice(list(self.empties)) + empties = list(self.empties) + return empties[self.rng.integers(0, len(empties))] def __setstate__(self, state): """Set the state of the discrete space and rebuild the connections.""" diff --git a/mesa/examples/advanced/wolf_sheep/model.py b/mesa/examples/advanced/wolf_sheep/model.py index b212f814def..262f78e5e3f 100644 --- a/mesa/examples/advanced/wolf_sheep/model.py +++ b/mesa/examples/advanced/wolf_sheep/model.py @@ -60,7 +60,7 @@ def __init__( rng: Random seed simulator: ABMSimulator instance for event scheduling """ - super().__init__(seed=rng) + super().__init__(rng=rng) self.simulator = simulator self.simulator.setup(self) @@ -74,7 +74,7 @@ def __init__( [self.height, self.width], torus=True, capacity=math.inf, - random=self.random, + rng=self.rng, ) # Set up data collection @@ -96,7 +96,7 @@ def __init__( energy=self.rng.random((initial_sheep,)) * 2 * sheep_gain_from_food, p_reproduce=sheep_reproduce, energy_from_food=sheep_gain_from_food, - cell=self.random.choices(self.grid.all_cells.cells, k=initial_sheep), + cell=self.rng.choice(self.grid.all_cells.cells, size=initial_sheep), ) # Create Wolves: Wolf.create_agents( @@ -105,7 +105,7 @@ def __init__( energy=self.rng.random((initial_wolves,)) * 2 * wolf_gain_from_food, p_reproduce=wolf_reproduce, energy_from_food=wolf_gain_from_food, - cell=self.random.choices(self.grid.all_cells.cells, k=initial_wolves), + cell=self.rng.choice(self.grid.all_cells.cells, size=initial_wolves), ) # Create grass patches if enabled @@ -114,7 +114,7 @@ def __init__( for cell in self.grid: fully_grown = self.random.choice(possibly_fully_grown) countdown = ( - 0 if fully_grown else self.random.randrange(0, grass_regrowth_time) + 0 if fully_grown else self.rng.integers(low=0, high=grass_regrowth_time).item() ) GrassPatch(self, countdown, grass_regrowth_time, cell) diff --git a/mesa/examples/basic/boid_flockers/model.py b/mesa/examples/basic/boid_flockers/model.py index 6d4ae8859c2..0637c8aee7e 100644 --- a/mesa/examples/basic/boid_flockers/model.py +++ b/mesa/examples/basic/boid_flockers/model.py @@ -51,7 +51,7 @@ def __init__( self.space = ContinuousSpace( [[0, width], [0, height]], torus=True, - random=self.random, + rng=self.rng, n_agents=population_size, ) diff --git a/mesa/examples/basic/boltzmann_wealth_model/agents.py b/mesa/examples/basic/boltzmann_wealth_model/agents.py index f293b9068a5..f277576fef5 100644 --- a/mesa/examples/basic/boltzmann_wealth_model/agents.py +++ b/mesa/examples/basic/boltzmann_wealth_model/agents.py @@ -30,7 +30,7 @@ def give_money(self): cellmates = [a for a in self.cell.agents if a is not self] if cellmates: # Only give money if there are other agents present - other = self.random.choice(cellmates) + other = cellmates[self.rng.integers(0, len(cellmates))] other.wealth += 1 self.wealth -= 1 From f9133ce0ef591c19d9f0dcb687379746e76ccdd0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 18 Nov 2025 19:11:55 +0000 Subject: [PATCH 20/24] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/discrete_space/cell.py | 8 ++++---- mesa/discrete_space/cell_collection.py | 4 ++-- mesa/discrete_space/discrete_space.py | 5 ++--- mesa/discrete_space/grid.py | 6 ++++-- mesa/discrete_space/network.py | 11 ++++++----- mesa/discrete_space/voronoi.py | 6 ++++-- mesa/examples/advanced/wolf_sheep/model.py | 4 +++- .../experimental/continuous_space/continuous_space.py | 2 +- 8 files changed, 26 insertions(+), 20 deletions(-) diff --git a/mesa/discrete_space/cell.py b/mesa/discrete_space/cell.py index e077b653e3b..f58880f92bd 100644 --- a/mesa/discrete_space/cell.py +++ b/mesa/discrete_space/cell.py @@ -14,13 +14,13 @@ from __future__ import annotations +import warnings from functools import cache, cached_property from random import Random from typing import TYPE_CHECKING -import warnings -from numpy.random import BitGenerator, Generator, RandomState, SeedSequence import numpy as np +from numpy.random import BitGenerator, Generator, RandomState, SeedSequence from mesa.discrete_space.cell_agent import CellAgent from mesa.discrete_space.cell_collection import CellCollection @@ -52,7 +52,7 @@ class Cell: "coordinate", "properties", "random", - "rng" + "rng", ] @deprecate_kwarg("random") @@ -83,7 +83,7 @@ def __init__( Coordinate, object ] = {} # fixme still used by voronoi mesh - if (random is None and rng is None): + if random is None and rng is None: warnings.warn( "Random number generator not specified, this can make models non-reproducible. Please pass a random number generator explicitly", UserWarning, diff --git a/mesa/discrete_space/cell_collection.py b/mesa/discrete_space/cell_collection.py index 514236ced60..a8e0e8a33c3 100644 --- a/mesa/discrete_space/cell_collection.py +++ b/mesa/discrete_space/cell_collection.py @@ -22,8 +22,8 @@ from random import Random from typing import TYPE_CHECKING, TypeVar -from numpy.random import BitGenerator, Generator, RandomState, SeedSequence import numpy as np +from numpy.random import BitGenerator, Generator, RandomState, SeedSequence from mesa.util import deprecate_kwarg @@ -76,7 +76,7 @@ def __init__( next(iter(self._cells.keys())).capacity if self._cells else None ) - if (random is None and rng is None): + if random is None and rng is None: warnings.warn( "Random number generator not specified, this can make models non-reproducible. Please pass a random number generator explicitly", UserWarning, diff --git a/mesa/discrete_space/discrete_space.py b/mesa/discrete_space/discrete_space.py index 2b6fb1404a3..959d98c58fd 100644 --- a/mesa/discrete_space/discrete_space.py +++ b/mesa/discrete_space/discrete_space.py @@ -20,15 +20,14 @@ from random import Random from typing import TypeVar -from numpy.random import BitGenerator, Generator, RandomState, SeedSequence import numpy as np +from numpy.random import BitGenerator, Generator, RandomState, SeedSequence from mesa.agent import AgentSet from mesa.discrete_space.cell import Cell from mesa.discrete_space.cell_collection import CellCollection from mesa.util import deprecate_kwarg - SeedLike = int | np.ndarray[int] | SeedSequence | BitGenerator | Generator | RandomState T = TypeVar("T", bound=Cell) @@ -70,7 +69,7 @@ def __init__( super().__init__() self.capacity = capacity self._cells: dict[tuple[int, ...], T] = {} - if (random is None and rng is None): + if random is None and rng is None: warnings.warn( "Random number generator not specified, this can make models non-reproducible. Please pass a random number generator explicitly", UserWarning, diff --git a/mesa/discrete_space/grid.py b/mesa/discrete_space/grid.py index 31fed798f60..4e7f0efc527 100644 --- a/mesa/discrete_space/grid.py +++ b/mesa/discrete_space/grid.py @@ -19,8 +19,8 @@ from random import Random from typing import Any, TypeVar -from numpy.random import BitGenerator, Generator, RandomState, SeedSequence import numpy as np +from numpy.random import BitGenerator, Generator, RandomState, SeedSequence from mesa.discrete_space import Cell, DiscreteSpace from mesa.discrete_space.property_layer import ( @@ -106,7 +106,9 @@ def __init__( rng (SeedLike | None): the random number generator cell_klass: the base class to use for the cells """ - super().__init__(capacity=capacity, rng=rng, random=random, cell_klass=cell_klass) + super().__init__( + capacity=capacity, rng=rng, random=random, cell_klass=cell_klass + ) self.torus = torus self.dimensions = dimensions self._try_random = True diff --git a/mesa/discrete_space/network.py b/mesa/discrete_space/network.py index 9f75c2a006e..d42f5f4e20b 100644 --- a/mesa/discrete_space/network.py +++ b/mesa/discrete_space/network.py @@ -15,8 +15,8 @@ from random import Random from typing import Any -from numpy.random import BitGenerator, Generator, RandomState, SeedSequence import numpy as np +from numpy.random import BitGenerator, Generator, RandomState, SeedSequence from mesa.discrete_space.cell import Cell from mesa.discrete_space.discrete_space import DiscreteSpace @@ -24,6 +24,7 @@ SeedLike = int | np.ndarray[int] | SeedSequence | BitGenerator | Generator | RandomState + class Network(DiscreteSpace[Cell]): """A networked discrete space.""" @@ -46,13 +47,13 @@ def __init__( cell_klass (type[Cell]): The base Cell class to use in the Network """ - super().__init__(capacity=capacity, random=random, rng=rng, cell_klass=cell_klass) + super().__init__( + capacity=capacity, random=random, rng=rng, cell_klass=cell_klass + ) self.G = G for node_id in self.G.nodes: - self._cells[node_id] = self.cell_klass( - node_id, capacity, rng=self.rng - ) + self._cells[node_id] = self.cell_klass(node_id, capacity, rng=self.rng) self._connect_cells() diff --git a/mesa/discrete_space/voronoi.py b/mesa/discrete_space/voronoi.py index 7d7147c233d..7d895c4e79d 100644 --- a/mesa/discrete_space/voronoi.py +++ b/mesa/discrete_space/voronoi.py @@ -16,8 +16,8 @@ from itertools import combinations from random import Random -from numpy.random import BitGenerator, Generator, RandomState, SeedSequence import numpy as np +from numpy.random import BitGenerator, Generator, RandomState, SeedSequence from mesa.discrete_space.cell import Cell from mesa.discrete_space.discrete_space import DiscreteSpace @@ -208,7 +208,9 @@ def __init__( capacity_function (Callable): function to compute (int) capacity according to (float) area """ - super().__init__(capacity=capacity, random=random, rng=rng, cell_klass=cell_klass) + super().__init__( + capacity=capacity, random=random, rng=rng, cell_klass=cell_klass + ) self.centroids_coordinates = centroids_coordinates self._validate_parameters() diff --git a/mesa/examples/advanced/wolf_sheep/model.py b/mesa/examples/advanced/wolf_sheep/model.py index 262f78e5e3f..f4535c10f24 100644 --- a/mesa/examples/advanced/wolf_sheep/model.py +++ b/mesa/examples/advanced/wolf_sheep/model.py @@ -114,7 +114,9 @@ def __init__( for cell in self.grid: fully_grown = self.random.choice(possibly_fully_grown) countdown = ( - 0 if fully_grown else self.rng.integers(low=0, high=grass_regrowth_time).item() + 0 + if fully_grown + else self.rng.integers(low=0, high=grass_regrowth_time).item() ) GrassPatch(self, countdown, grass_regrowth_time, cell) diff --git a/mesa/experimental/continuous_space/continuous_space.py b/mesa/experimental/continuous_space/continuous_space.py index eb0fe680ed4..b3b176eca14 100644 --- a/mesa/experimental/continuous_space/continuous_space.py +++ b/mesa/experimental/continuous_space/continuous_space.py @@ -7,7 +7,6 @@ import numpy as np from numpy.random import BitGenerator, Generator, RandomState, SeedSequence - from numpy.typing import ArrayLike from scipy.spatial.distance import cdist @@ -16,6 +15,7 @@ SeedLike = int | np.ndarray[int] | SeedSequence | BitGenerator | Generator | RandomState + class ContinuousSpace: """Continuous space where each agent can have an arbitrary position.""" From 27b48ac98113de6872132dd90f16896b5387408a Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Wed, 19 Nov 2025 11:25:57 +0100 Subject: [PATCH 21/24] ongoing work --- docs/tutorials/9_batch_run.ipynb | 2 +- mesa/agent.py | 1 + mesa/discrete_space/cell.py | 3 +-- mesa/discrete_space/cell_collection.py | 3 ++- mesa/discrete_space/voronoi.py | 2 +- mesa/examples/advanced/epstein_civil_violence/model.py | 2 +- mesa/examples/advanced/pd_grid/model.py | 2 +- mesa/examples/advanced/sugarscape_g1mt/model.py | 2 +- mesa/examples/basic/boltzmann_wealth_model/agents.py | 2 +- mesa/examples/basic/conways_game_of_life/model.py | 6 ++++-- mesa/examples/basic/virus_on_network/agents.py | 2 +- mesa/examples/basic/virus_on_network/model.py | 6 +++--- mesa/experimental/meta_agents/meta_agent.py | 4 ++-- 13 files changed, 20 insertions(+), 17 deletions(-) diff --git a/docs/tutorials/9_batch_run.ipynb b/docs/tutorials/9_batch_run.ipynb index 98ee83e09bd..5b585f59ba6 100644 --- a/docs/tutorials/9_batch_run.ipynb +++ b/docs/tutorials/9_batch_run.ipynb @@ -410,7 +410,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.5" + "version": "3.12.2" }, "widgets": { "state": {}, diff --git a/mesa/agent.py b/mesa/agent.py index 5deea6e1bec..11a9f2e7544 100644 --- a/mesa/agent.py +++ b/mesa/agent.py @@ -143,6 +143,7 @@ def __getitem__(self, i): @property def random(self) -> Random: """Return a seeded stdlib rng.""" + warnings.warn("the use of random is deprecated, please use rng instead", FutureWarning, stacklevel=2) return self.model.random @property diff --git a/mesa/discrete_space/cell.py b/mesa/discrete_space/cell.py index f58880f92bd..bff17be1fba 100644 --- a/mesa/discrete_space/cell.py +++ b/mesa/discrete_space/cell.py @@ -51,8 +51,7 @@ class Cell: "connections", "coordinate", "properties", - "random", - "rng", + "rng" ] @deprecate_kwarg("random") diff --git a/mesa/discrete_space/cell_collection.py b/mesa/discrete_space/cell_collection.py index a8e0e8a33c3..14845bfc071 100644 --- a/mesa/discrete_space/cell_collection.py +++ b/mesa/discrete_space/cell_collection.py @@ -42,7 +42,8 @@ class CellCollection[T: Cell]: Attributes: cells (List[Cell]): The list of cells this collection represents agents (List[CellAgent]) : List of agents occupying the cells in this collection - random (Random) : The random number generator + random (Random) : The random number generator, deprecated + rng (Random) : The random number generator Notes: A `UserWarning` is issued if `random=None`. You can resolve this warning by explicitly diff --git a/mesa/discrete_space/voronoi.py b/mesa/discrete_space/voronoi.py index 7d895c4e79d..8ea400fee8c 100644 --- a/mesa/discrete_space/voronoi.py +++ b/mesa/discrete_space/voronoi.py @@ -215,7 +215,7 @@ def __init__( self._validate_parameters() self._cells = { - i: cell_klass(self.centroids_coordinates[i], capacity, rng=self.random) + i: cell_klass(self.centroids_coordinates[i], capacity, rng=self.rng) for i in range(len(self.centroids_coordinates)) } diff --git a/mesa/examples/advanced/epstein_civil_violence/model.py b/mesa/examples/advanced/epstein_civil_violence/model.py index ffcb7b10750..1a0fae77ccf 100644 --- a/mesa/examples/advanced/epstein_civil_violence/model.py +++ b/mesa/examples/advanced/epstein_civil_violence/model.py @@ -54,7 +54,7 @@ def __init__( self.max_iters = max_iters self.grid = mesa.discrete_space.OrthogonalVonNeumannGrid( - (width, height), capacity=1, torus=True, random=self.random + (width, height), capacity=1, torus=True, rng=self.rng ) model_reporters = { diff --git a/mesa/examples/advanced/pd_grid/model.py b/mesa/examples/advanced/pd_grid/model.py index a7b3bf3bd47..149fc8d03d0 100644 --- a/mesa/examples/advanced/pd_grid/model.py +++ b/mesa/examples/advanced/pd_grid/model.py @@ -27,7 +27,7 @@ def __init__( """ super().__init__(rng=rng) self.activation_order = activation_order - self.grid = OrthogonalMooreGrid((width, height), torus=True, random=self.random) + self.grid = OrthogonalMooreGrid((width, height), torus=True, rng=self.rng) if payoffs is not None: self.payoff = payoffs diff --git a/mesa/examples/advanced/sugarscape_g1mt/model.py b/mesa/examples/advanced/sugarscape_g1mt/model.py index 99e1f6a5ad7..3be02e08d86 100644 --- a/mesa/examples/advanced/sugarscape_g1mt/model.py +++ b/mesa/examples/advanced/sugarscape_g1mt/model.py @@ -66,7 +66,7 @@ def __init__( # initiate mesa grid class self.grid = OrthogonalVonNeumannGrid( - (self.width, self.height), torus=False, random=self.random + (self.width, self.height), torus=False, rng=self.rng ) # initiate datacollector self.datacollector = mesa.DataCollector( diff --git a/mesa/examples/basic/boltzmann_wealth_model/agents.py b/mesa/examples/basic/boltzmann_wealth_model/agents.py index f277576fef5..f3d79efc6a6 100644 --- a/mesa/examples/basic/boltzmann_wealth_model/agents.py +++ b/mesa/examples/basic/boltzmann_wealth_model/agents.py @@ -30,7 +30,7 @@ def give_money(self): cellmates = [a for a in self.cell.agents if a is not self] if cellmates: # Only give money if there are other agents present - other = cellmates[self.rng.integers(0, len(cellmates))] + other = cellmates[0] if len(cellmates)==1 else cellmates[self.rng.integers(0, len(cellmates))] other.wealth += 1 self.wealth -= 1 diff --git a/mesa/examples/basic/conways_game_of_life/model.py b/mesa/examples/basic/conways_game_of_life/model.py index 66fbf0fe340..a513f7f31a9 100644 --- a/mesa/examples/basic/conways_game_of_life/model.py +++ b/mesa/examples/basic/conways_game_of_life/model.py @@ -10,16 +10,18 @@ def __init__(self, width=50, height=50, initial_fraction_alive=0.2, rng=None): """Create a new playing area of (width, height) cells.""" super().__init__(rng=rng) # Use a simple grid, where edges wrap around. - self.grid = OrthogonalMooreGrid((width, height), capacity=1, torus=True) + self.grid = OrthogonalMooreGrid((width, height), capacity=1, torus=True, rng=rng) # Place a cell at each location, with some initialized to # ALIVE and some to DEAD. + random_floats = self.rng.uniform(0, 1, size=(width, height)) + for cell in self.grid.all_cells: Cell( self, cell, init_state=Cell.ALIVE - if self.random.random() < initial_fraction_alive + if random_floats[cell.coordinate] < initial_fraction_alive else Cell.DEAD, ) diff --git a/mesa/examples/basic/virus_on_network/agents.py b/mesa/examples/basic/virus_on_network/agents.py index 73a92b4fd99..d9c0a1a4af4 100644 --- a/mesa/examples/basic/virus_on_network/agents.py +++ b/mesa/examples/basic/virus_on_network/agents.py @@ -35,7 +35,7 @@ def __init__( def try_to_infect_neighbors(self): for agent in self.cell.neighborhood.agents: if (agent.state is State.SUSCEPTIBLE) and ( - self.random.random() < self.virus_spread_chance + self.rng.random() < self.virus_spread_chance ): agent.state = State.INFECTED diff --git a/mesa/examples/basic/virus_on_network/model.py b/mesa/examples/basic/virus_on_network/model.py index 100954dd967..839a837bcf9 100644 --- a/mesa/examples/basic/virus_on_network/model.py +++ b/mesa/examples/basic/virus_on_network/model.py @@ -41,7 +41,7 @@ def __init__( super().__init__(rng=rng) prob = avg_node_degree / num_nodes graph = nx.erdos_renyi_graph(n=num_nodes, p=prob) - self.grid = Network(graph, capacity=1, random=self.random) + self.grid = Network(graph, capacity=1, rng=self.rng) self.initial_outbreak_size = ( initial_outbreak_size if initial_outbreak_size <= num_nodes else num_nodes @@ -69,8 +69,8 @@ def __init__( # Infect some nodes infected_nodes = CellCollection( - self.random.sample(list(self.grid.all_cells), self.initial_outbreak_size), - random=self.random, + self.rng.choice(list(self.grid.all_cells), size=self.initial_outbreak_size), + rng=self.rng, ) for a in infected_nodes.agents: a.state = State.INFECTED diff --git a/mesa/experimental/meta_agents/meta_agent.py b/mesa/experimental/meta_agents/meta_agent.py index 7a5ffe049cf..7ff37d1f02d 100644 --- a/mesa/experimental/meta_agents/meta_agent.py +++ b/mesa/experimental/meta_agents/meta_agent.py @@ -63,7 +63,7 @@ def evaluate_combination( Optional[Tuple[AgentSet, float]]: The evaluated group and its value, or None. """ - group_set = AgentSet(candidate_group, random=model.random) + group_set = AgentSet(candidate_group, rng=model.rng) if evaluation_func: value = evaluation_func(group_set) return group_set, value @@ -287,7 +287,7 @@ def __init__( name (str, optional): The name of the MetaAgent. Defaults to "MetaAgent". """ super().__init__(model) - self._constituting_set = AgentSet(agents or [], random=model.random) + self._constituting_set = AgentSet(agents or [], rng=model.rng) self.name = name # Add ref to meta_agent in constituting_agents From 88f3999d69e90beb6d3c34b3d4f69490f6baaa41 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 19 Nov 2025 10:28:49 +0000 Subject: [PATCH 22/24] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/agent.py | 6 +++++- mesa/discrete_space/cell.py | 2 +- mesa/examples/basic/boltzmann_wealth_model/agents.py | 6 +++++- mesa/examples/basic/conways_game_of_life/model.py | 4 +++- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/mesa/agent.py b/mesa/agent.py index 11a9f2e7544..36e5679082d 100644 --- a/mesa/agent.py +++ b/mesa/agent.py @@ -143,7 +143,11 @@ def __getitem__(self, i): @property def random(self) -> Random: """Return a seeded stdlib rng.""" - warnings.warn("the use of random is deprecated, please use rng instead", FutureWarning, stacklevel=2) + warnings.warn( + "the use of random is deprecated, please use rng instead", + FutureWarning, + stacklevel=2, + ) return self.model.random @property diff --git a/mesa/discrete_space/cell.py b/mesa/discrete_space/cell.py index bff17be1fba..65b85dea808 100644 --- a/mesa/discrete_space/cell.py +++ b/mesa/discrete_space/cell.py @@ -51,7 +51,7 @@ class Cell: "connections", "coordinate", "properties", - "rng" + "rng", ] @deprecate_kwarg("random") diff --git a/mesa/examples/basic/boltzmann_wealth_model/agents.py b/mesa/examples/basic/boltzmann_wealth_model/agents.py index f3d79efc6a6..98442a2b8a5 100644 --- a/mesa/examples/basic/boltzmann_wealth_model/agents.py +++ b/mesa/examples/basic/boltzmann_wealth_model/agents.py @@ -30,7 +30,11 @@ def give_money(self): cellmates = [a for a in self.cell.agents if a is not self] if cellmates: # Only give money if there are other agents present - other = cellmates[0] if len(cellmates)==1 else cellmates[self.rng.integers(0, len(cellmates))] + other = ( + cellmates[0] + if len(cellmates) == 1 + else cellmates[self.rng.integers(0, len(cellmates))] + ) other.wealth += 1 self.wealth -= 1 diff --git a/mesa/examples/basic/conways_game_of_life/model.py b/mesa/examples/basic/conways_game_of_life/model.py index a513f7f31a9..31da0f153fd 100644 --- a/mesa/examples/basic/conways_game_of_life/model.py +++ b/mesa/examples/basic/conways_game_of_life/model.py @@ -10,7 +10,9 @@ def __init__(self, width=50, height=50, initial_fraction_alive=0.2, rng=None): """Create a new playing area of (width, height) cells.""" super().__init__(rng=rng) # Use a simple grid, where edges wrap around. - self.grid = OrthogonalMooreGrid((width, height), capacity=1, torus=True, rng=rng) + self.grid = OrthogonalMooreGrid( + (width, height), capacity=1, torus=True, rng=rng + ) # Place a cell at each location, with some initialized to # ALIVE and some to DEAD. From 5bdfa7eb3bc48b3d77a1999476ba6c59e26aa71f Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Wed, 19 Nov 2025 15:40:17 +0100 Subject: [PATCH 23/24] some further tweaks to the example models --- mesa/examples/advanced/wolf_sheep/agents.py | 2 +- mesa/examples/advanced/wolf_sheep/model.py | 12 +++++------- .../basic/boltzmann_wealth_model/model.py | 6 ------ mesa/examples/basic/schelling/model.py | 18 +++++++++++++++--- mesa/experimental/devs/simulator.py | 6 ++++-- 5 files changed, 25 insertions(+), 19 deletions(-) diff --git a/mesa/examples/advanced/wolf_sheep/agents.py b/mesa/examples/advanced/wolf_sheep/agents.py index 344a6a0276e..5bd5e0f7eff 100644 --- a/mesa/examples/advanced/wolf_sheep/agents.py +++ b/mesa/examples/advanced/wolf_sheep/agents.py @@ -49,7 +49,7 @@ def step(self): # Handle death and reproduction if self.energy < 0: self.remove() - elif self.random.random() < self.p_reproduce: + elif self.rng.random() < self.p_reproduce: self.spawn_offspring() def move(self): diff --git a/mesa/examples/advanced/wolf_sheep/model.py b/mesa/examples/advanced/wolf_sheep/model.py index f4535c10f24..352bd405f63 100644 --- a/mesa/examples/advanced/wolf_sheep/model.py +++ b/mesa/examples/advanced/wolf_sheep/model.py @@ -110,14 +110,12 @@ def __init__( # Create grass patches if enabled if grass: - possibly_fully_grown = [True, False] + possibly_fully_grown = self.rng.integers(0, 2, size=(height, width), dtype=bool) + regrowth_time = self.rng.integers(low=0, high=grass_regrowth_time, size=(height, width)) + regrowth_time[possibly_fully_grown] = 0 + for cell in self.grid: - fully_grown = self.random.choice(possibly_fully_grown) - countdown = ( - 0 - if fully_grown - else self.rng.integers(low=0, high=grass_regrowth_time).item() - ) + countdown = regrowth_time[cell.coordinate] GrassPatch(self, countdown, grass_regrowth_time, cell) # Collect initial data diff --git a/mesa/examples/basic/boltzmann_wealth_model/model.py b/mesa/examples/basic/boltzmann_wealth_model/model.py index 8b5750bc1b6..cbbc18de8d1 100644 --- a/mesa/examples/basic/boltzmann_wealth_model/model.py +++ b/mesa/examples/basic/boltzmann_wealth_model/model.py @@ -72,9 +72,3 @@ def compute_gini(self): # Calculate using the standard formula for Gini coefficient b = sum(xi * (n - i) for i, xi in enumerate(x)) / (n * sum(x)) return 1 + (1 / n) - 2 * b - - -if __name__ == "__main__": - model = BoltzmannWealth(rng=42) - - model.step() diff --git a/mesa/examples/basic/schelling/model.py b/mesa/examples/basic/schelling/model.py index 221fc9486c9..fffd37ca32b 100644 --- a/mesa/examples/basic/schelling/model.py +++ b/mesa/examples/basic/schelling/model.py @@ -3,6 +3,7 @@ from mesa.discrete_space import OrthogonalMooreGrid from mesa.examples.basic.schelling.agents import SchellingAgent +import numpy as np class Schelling(Model): """Model class for the Schelling segregation model.""" @@ -60,11 +61,14 @@ def __init__( ) # Create agents and place them on the grid + random_numbers = self.rng.random(size=(height, width)) + occupied = random_numbers < density + agent_type = iter(self.rng.random(size=np.sum(occupied)) < minority_pc) + for cell in self.grid.all_cells: - if self.random.random() < self.density: - agent_type = 1 if self.random.random() < minority_pc else 0 + if occupied[cell.coordinate]: SchellingAgent( - self, cell, agent_type, homophily=homophily, radius=radius + self, cell, next(agent_type).astype(int), homophily=homophily, radius=radius ) # Collect initial state @@ -78,3 +82,11 @@ def step(self): self.agents.do("assign_state") self.datacollector.collect(self) # Collect data self.running = self.happy < len(self.agents) # Continue until everyone is happy + + +if __name__ == '__main__': + agent = Schelling( + height=20, + width=20, + density=0.8, + ) \ No newline at end of file diff --git a/mesa/experimental/devs/simulator.py b/mesa/experimental/devs/simulator.py index 0b9a45eeb2b..caa960521ff 100644 --- a/mesa/experimental/devs/simulator.py +++ b/mesa/experimental/devs/simulator.py @@ -26,6 +26,8 @@ from collections.abc import Callable from typing import TYPE_CHECKING, Any +import numpy as np + from .eventlist import EventList, Priority, SimulationEvent if TYPE_CHECKING: @@ -302,9 +304,9 @@ def check_time_unit(self, time) -> bool: bool: whether the time is of the correct unit """ - if isinstance(time, int): + if isinstance(time, (int, np.integer)): return True - if isinstance(time, float): + if isinstance(time, (float, np.float)): return time.is_integer() else: return False From 9c293ba5bcf94bddf0aa1f986199a8e6f33e9f5d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 19 Nov 2025 14:40:28 +0000 Subject: [PATCH 24/24] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/examples/advanced/wolf_sheep/model.py | 8 ++++++-- mesa/examples/basic/schelling/model.py | 13 +++++++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/mesa/examples/advanced/wolf_sheep/model.py b/mesa/examples/advanced/wolf_sheep/model.py index 352bd405f63..eb30b490546 100644 --- a/mesa/examples/advanced/wolf_sheep/model.py +++ b/mesa/examples/advanced/wolf_sheep/model.py @@ -110,8 +110,12 @@ def __init__( # Create grass patches if enabled if grass: - possibly_fully_grown = self.rng.integers(0, 2, size=(height, width), dtype=bool) - regrowth_time = self.rng.integers(low=0, high=grass_regrowth_time, size=(height, width)) + possibly_fully_grown = self.rng.integers( + 0, 2, size=(height, width), dtype=bool + ) + regrowth_time = self.rng.integers( + low=0, high=grass_regrowth_time, size=(height, width) + ) regrowth_time[possibly_fully_grown] = 0 for cell in self.grid: diff --git a/mesa/examples/basic/schelling/model.py b/mesa/examples/basic/schelling/model.py index fffd37ca32b..2f1e4266633 100644 --- a/mesa/examples/basic/schelling/model.py +++ b/mesa/examples/basic/schelling/model.py @@ -1,9 +1,10 @@ +import numpy as np + from mesa import Model from mesa.datacollection import DataCollector from mesa.discrete_space import OrthogonalMooreGrid from mesa.examples.basic.schelling.agents import SchellingAgent -import numpy as np class Schelling(Model): """Model class for the Schelling segregation model.""" @@ -68,7 +69,11 @@ def __init__( for cell in self.grid.all_cells: if occupied[cell.coordinate]: SchellingAgent( - self, cell, next(agent_type).astype(int), homophily=homophily, radius=radius + self, + cell, + next(agent_type).astype(int), + homophily=homophily, + radius=radius, ) # Collect initial state @@ -84,9 +89,9 @@ def step(self): self.running = self.happy < len(self.agents) # Continue until everyone is happy -if __name__ == '__main__': +if __name__ == "__main__": agent = Schelling( height=20, width=20, density=0.8, - ) \ No newline at end of file + )