From 8739099b28a52768f7f9c8bf7dc138b86d3268e1 Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 15 Sep 2024 17:01:19 -0400 Subject: [PATCH] model: Move random seed and random to __init__ (#1940) * model: Move random seed and random to __init__ Given that `super().__init__()` is now necessary for user's model class `__init__()`, this simplifies the `Model` construct. And `model.random` can be initialized with other RNG objects (`np.random.default_rng(...)`, or any other RNG). Prior discussion https://github.com/projectmesa/mesa/discussions/1938.# Please enter the commit message for your changes. Lines starting * model: Set seed as keyword argument * make_model: Set seed after model initialization This is because the seed param is no longer always passed by the user to mesa.Model.__init__. * add super call in model.__init__ * attempted debug * Update test_time.py * Update test_time.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Ensure object.__init__ has no other argument * fix: Remove need of __new__ in ModelCreator * Remove obsolete attributes * Remove superfluous code --------- Co-authored-by: Jan Kwakkel Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mesa/model.py | 21 ++++++++------------- mesa/visualization/solara_viz.py | 6 ++---- tests/test_solara_viz.py | 1 - tests/test_time.py | 4 +++- 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/mesa/model.py b/mesa/model.py index f859aea314f..836f7445ca7 100644 --- a/mesa/model.py +++ b/mesa/model.py @@ -54,29 +54,24 @@ class Model: """ - def __new__(cls, *args: Any, **kwargs: Any) -> Any: - """Create a new model object and instantiate its RNG automatically.""" - obj = object.__new__(cls) - obj._seed = kwargs.get("seed") - if obj._seed is None: - # We explicitly specify the seed here so that we know its value in - # advance. - obj._seed = random.random() - obj.random = random.Random(obj._seed) - return obj - - def __init__(self, *args: Any, **kwargs: Any) -> None: + def __init__(self, *args: Any, seed: float | None = None, **kwargs: Any) -> None: """Create a new model. Overload this method with the actual code to start the model. Always start with super().__init__() to initialize the model object properly. """ - self.running = True self.schedule = None self.steps: int = 0 self._setup_agent_registration() + self._seed = seed + if self._seed is None: + # We explicitly specify the seed here so that we know its value in + # advance. + self._seed = random.random() + self.random = random.Random(self._seed) + # Wrap the user-defined step method self._user_step = self.step self.step = self._wrapped_step diff --git a/mesa/visualization/solara_viz.py b/mesa/visualization/solara_viz.py index 1187373f2ef..a21ce67dea2 100644 --- a/mesa/visualization/solara_viz.py +++ b/mesa/visualization/solara_viz.py @@ -260,10 +260,8 @@ def on_change(name, value): set_model_parameters({**model_parameters, name: value}) def create_model(): - model.value = model.value.__class__.__new__( - model.value.__class__, **model_parameters, seed=reactive_seed.value - ) - model.value.__init__(**model_parameters) + model.value = model.value.__class__(**model_parameters) + model.value._seed = reactive_seed.value solara.use_effect(create_model, [model_parameters, reactive_seed.value]) diff --git a/tests/test_solara_viz.py b/tests/test_solara_viz.py index 3ff8164065e..cd5ffe1b5dd 100644 --- a/tests/test_solara_viz.py +++ b/tests/test_solara_viz.py @@ -89,7 +89,6 @@ def test_call_space_drawer(mocker): ) model = mesa.Model() - mocker.patch.object(mesa.Model, "__new__", return_value=model) mocker.patch.object(mesa.Model, "__init__", return_value=None) agent_portrayal = { diff --git a/tests/test_time.py b/tests/test_time.py index 13d71d22eb0..b21b7b9555d 100644 --- a/tests/test_time.py +++ b/tests/test_time.py @@ -5,7 +5,8 @@ import unittest from unittest import TestCase, mock -from mesa import Agent, Model +from mesa.agent import Agent +from mesa.model import Model from mesa.time import ( BaseScheduler, RandomActivation, @@ -111,6 +112,7 @@ def test_no_shuffle(self): """ Testing the staged activation without shuffling. """ + model = MockModel(shuffle=False) model.step() model.step()