From 728456c14949ce3b29550e16fbc831c96af5cd02 Mon Sep 17 00:00:00 2001 From: Ewout ter Hoeven <15776622+EwoutH@users.noreply.github.com> Date: Tue, 7 Oct 2025 20:12:02 +0200 Subject: [PATCH 01/19] Fix: Auto-increment seed across batch_run iterations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When using batch_run() with a single seed value and multiple iterations, all iterations were using the same seed, producing identical results instead of independent replications. This defeats the purpose of running multiple iterations. This commit modifies _model_run_func to automatically increment the seed for each iteration (seed, seed+1, seed+2, ...) when a numeric seed is provided. This ensures: - Each iteration produces different random outcomes - Results remain reproducible (same base seed → same sequence) - Backward compatibility with seed arrays (no modification if seed is already an iterable passed via parameters) - Unchanged behavior when no seed is specified (each iteration gets random seed from OS) The fix only applies when: 1. A 'seed' parameter exists in kwargs 2. The seed value is not None 3. The iteration number is > 0 4. The seed is a single numeric value (int/float, not bool) --- mesa/batchrunner.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mesa/batchrunner.py b/mesa/batchrunner.py index 83bc79061b6..4eb801cf967 100644 --- a/mesa/batchrunner.py +++ b/mesa/batchrunner.py @@ -170,6 +170,14 @@ def _model_run_func( Return model_data, agent_data from the reporters """ run_id, iteration, kwargs = run + + # Handle seed uniqueness across iterations + if 'seed' in kwargs and kwargs['seed'] is not None and iteration > 0: + seed_value = kwargs['seed'] + if isinstance(seed_value, (int, float)) and not isinstance(seed_value, bool): + kwargs = kwargs.copy() + kwargs['seed'] = int(seed_value) + iteration + model = model_cls(**kwargs) while model.running and model.steps <= max_steps: model.step() From c03c6fa7a3e62bb49adb1e7d5e2225163289093e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 7 Oct 2025 18:18:10 +0000 Subject: [PATCH 02/19] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/batchrunner.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mesa/batchrunner.py b/mesa/batchrunner.py index 4eb801cf967..5499aa2d5f9 100644 --- a/mesa/batchrunner.py +++ b/mesa/batchrunner.py @@ -172,11 +172,11 @@ def _model_run_func( run_id, iteration, kwargs = run # Handle seed uniqueness across iterations - if 'seed' in kwargs and kwargs['seed'] is not None and iteration > 0: - seed_value = kwargs['seed'] + if "seed" in kwargs and kwargs["seed"] is not None and iteration > 0: + seed_value = kwargs["seed"] if isinstance(seed_value, (int, float)) and not isinstance(seed_value, bool): kwargs = kwargs.copy() - kwargs['seed'] = int(seed_value) + iteration + kwargs["seed"] = int(seed_value) + iteration model = model_cls(**kwargs) while model.running and model.steps <= max_steps: From 7f456af959eb67a4ba575e6374e27b63a889dea2 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Wed, 12 Nov 2025 10:38:36 +0100 Subject: [PATCH 03/19] add rng as kwarg and deprecate iterations --- mesa/batchrunner.py | 30 +++++++---- tests/test_batch_run.py | 112 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 130 insertions(+), 12 deletions(-) diff --git a/mesa/batchrunner.py b/mesa/batchrunner.py index 5499aa2d5f9..9b53458c385 100644 --- a/mesa/batchrunner.py +++ b/mesa/batchrunner.py @@ -30,27 +30,32 @@ import itertools import multiprocessing -from collections.abc import Iterable, Mapping +import warnings +from collections.abc import Iterable, Mapping, Sequence from functools import partial from multiprocessing import Pool from typing import Any +import numpy as np from tqdm.auto import tqdm from mesa.model import Model multiprocessing.set_start_method("spawn", force=True) +SeedLike = int | np.integer | Sequence[int] | np.random.SeedSequence + def batch_run( model_cls: type[Model], parameters: Mapping[str, Any | Iterable[Any]], # We still retain the Optional[int] because users may set it to None (i.e. use all CPUs) number_processes: int | None = 1, - iterations: int = 1, + iterations: int|None = None, data_collection_period: int = -1, max_steps: int = 1000, display_progress: bool = True, + rng: SeedLike | Iterable[SeedLike] | None = None, ) -> list[dict[str, Any]]: """Batch run a mesa model with a set of parameter values. @@ -62,6 +67,7 @@ def batch_run( data_collection_period (int, optional): Number of steps after which data gets collected, by default -1 (end of episode) max_steps (int, optional): Maximum number of model steps after which the model halts, by default 1000 display_progress (bool, optional): Display batch run process, by default True + rng : a valid value or iterable of values for seeding the random number generator in the model Returns: List[Dict[str, Any]] @@ -70,11 +76,20 @@ def batch_run( batch_run assumes the model has a `datacollector` attribute that has a DataCollector object initialized. """ + if iterations is not None and rng is not None: + raise ValueError("you cannont use both iterations and rng at the same time. Please only use rng.") + if iterations is not None: + warnings.warn("iterations is deprecated, please use rgn instead", DeprecationWarning, stacklevel=2) + rng = [None,] * iterations + if not isinstance(rng, Iterable): + rng = [rng] + runs_list = [] run_id = 0 - for iteration in range(iterations): + for i, rng_i in enumerate(rng): for kwargs in _make_model_kwargs(parameters): - runs_list.append((run_id, iteration, kwargs)) + kwargs["rng"] = rng_i + runs_list.append((run_id, i, kwargs)) run_id += 1 process_func = partial( @@ -171,13 +186,6 @@ def _model_run_func( """ run_id, iteration, kwargs = run - # Handle seed uniqueness across iterations - if "seed" in kwargs and kwargs["seed"] is not None and iteration > 0: - seed_value = kwargs["seed"] - if isinstance(seed_value, (int, float)) and not isinstance(seed_value, bool): - kwargs = kwargs.copy() - kwargs["seed"] = int(seed_value) + iteration - model = model_cls(**kwargs) while model.running and model.steps <= max_steps: model.step() diff --git a/tests/test_batch_run.py b/tests/test_batch_run.py index aec13bc4ccc..6e4fe831897 100644 --- a/tests/test_batch_run.py +++ b/tests/test_batch_run.py @@ -1,4 +1,5 @@ """Test Batchrunner.""" +import pytest import mesa from mesa.agent import Agent @@ -130,7 +131,7 @@ def step(self): # noqa: D102 def test_batch_run(): # noqa: D103 - result = mesa.batch_run(MockModel, {}, number_processes=2) + result = mesa.batch_run(MockModel, {}, number_processes=2, rng=42) assert result == [ { "RunId": 0, @@ -140,6 +141,7 @@ def test_batch_run(): # noqa: D103 "AgentID": 1, "agent_id": 1, "agent_local": 250.0, + "rng":42, }, { "RunId": 0, @@ -149,6 +151,7 @@ def test_batch_run(): # noqa: D103 "AgentID": 2, "agent_id": 2, "agent_local": 250.0, + "rng": 42, }, { "RunId": 0, @@ -158,9 +161,114 @@ def test_batch_run(): # noqa: D103 "AgentID": 3, "agent_id": 3, "agent_local": 250.0, + "rng": 42, }, ] + result = mesa.batch_run(MockModel, {}, number_processes=2, iterations=1) + assert result == [ + { + "RunId": 0, + "iteration": 0, + "Step": 1000, + "reported_model_param": 42, + "AgentID": 1, + "agent_id": 1, + "agent_local": 250.0, + "rng":None, + }, + { + "RunId": 0, + "iteration": 0, + "Step": 1000, + "reported_model_param": 42, + "AgentID": 2, + "agent_id": 2, + "agent_local": 250.0, + "rng": None, + }, + { + "RunId": 0, + "iteration": 0, + "Step": 1000, + "reported_model_param": 42, + "AgentID": 3, + "agent_id": 3, + "agent_local": 250.0, + "rng": None, + }, + ] + + + result = mesa.batch_run(MockModel, {}, number_processes=2, rng=[42, 31415]) + assert result == [ + { + "RunId": 0, + "iteration": 0, + "Step": 1000, + "reported_model_param": 42, + "AgentID": 1, + "agent_id": 1, + "agent_local": 250.0, + "rng":42, + }, + { + "RunId": 0, + "iteration": 0, + "Step": 1000, + "reported_model_param": 42, + "AgentID": 2, + "agent_id": 2, + "agent_local": 250.0, + "rng": 42, + }, + { + "RunId": 0, + "iteration": 0, + "Step": 1000, + "reported_model_param": 42, + "AgentID": 3, + "agent_id": 3, + "agent_local": 250.0, + "rng": 42, + }, + { + "RunId": 1, + "iteration": 1, + "Step": 1000, + "reported_model_param": 42, + "AgentID": 1, + "agent_id": 1, + "agent_local": 250.0, + "rng": 31415, + }, + { + "RunId": 1, + "iteration": 1, + "Step": 1000, + "reported_model_param": 42, + "AgentID": 2, + "agent_id": 2, + "agent_local": 250.0, + "rng": 31415, + }, + { + "RunId": 1, + "iteration": 1, + "Step": 1000, + "reported_model_param": 42, + "AgentID": 3, + "agent_id": 3, + "agent_local": 250.0, + "rng": 31415, + }, + + ] + + with pytest.raises(ValueError): + mesa.batch_run(MockModel, {}, number_processes=2, rng=42, iterations=1) + + def test_batch_run_with_params(): # noqa: D103 mesa.batch_run( @@ -185,6 +293,7 @@ def test_batch_run_no_agent_reporters(): # noqa: D103 "Step": 1000, "enable_agent_reporters": False, "reported_model_param": 42, + "rng": None, } ] @@ -208,6 +317,7 @@ def test_batch_run_unhashable_param(): # noqa: D103 "agent_local": 250.0, "n_agents": 2, "variable_model_params": {"key": "value"}, + "rng": None, } assert result == [ From 10136f97a30ae95c69f7df6a3bbb3000d733d6dc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 12 Nov 2025 09:39:04 +0000 Subject: [PATCH 04/19] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/batchrunner.py | 16 ++++++++++++---- tests/test_batch_run.py | 10 ++++------ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/mesa/batchrunner.py b/mesa/batchrunner.py index 9b53458c385..6d9fcde5c9c 100644 --- a/mesa/batchrunner.py +++ b/mesa/batchrunner.py @@ -51,7 +51,7 @@ def batch_run( parameters: Mapping[str, Any | Iterable[Any]], # We still retain the Optional[int] because users may set it to None (i.e. use all CPUs) number_processes: int | None = 1, - iterations: int|None = None, + iterations: int | None = None, data_collection_period: int = -1, max_steps: int = 1000, display_progress: bool = True, @@ -77,10 +77,18 @@ def batch_run( """ if iterations is not None and rng is not None: - raise ValueError("you cannont use both iterations and rng at the same time. Please only use rng.") + raise ValueError( + "you cannont use both iterations and rng at the same time. Please only use rng." + ) if iterations is not None: - warnings.warn("iterations is deprecated, please use rgn instead", DeprecationWarning, stacklevel=2) - rng = [None,] * iterations + warnings.warn( + "iterations is deprecated, please use rgn instead", + DeprecationWarning, + stacklevel=2, + ) + rng = [ + None, + ] * iterations if not isinstance(rng, Iterable): rng = [rng] diff --git a/tests/test_batch_run.py b/tests/test_batch_run.py index 6e4fe831897..dcbc63079c2 100644 --- a/tests/test_batch_run.py +++ b/tests/test_batch_run.py @@ -1,4 +1,5 @@ """Test Batchrunner.""" + import pytest import mesa @@ -141,7 +142,7 @@ def test_batch_run(): # noqa: D103 "AgentID": 1, "agent_id": 1, "agent_local": 250.0, - "rng":42, + "rng": 42, }, { "RunId": 0, @@ -175,7 +176,7 @@ def test_batch_run(): # noqa: D103 "AgentID": 1, "agent_id": 1, "agent_local": 250.0, - "rng":None, + "rng": None, }, { "RunId": 0, @@ -199,7 +200,6 @@ def test_batch_run(): # noqa: D103 }, ] - result = mesa.batch_run(MockModel, {}, number_processes=2, rng=[42, 31415]) assert result == [ { @@ -210,7 +210,7 @@ def test_batch_run(): # noqa: D103 "AgentID": 1, "agent_id": 1, "agent_local": 250.0, - "rng":42, + "rng": 42, }, { "RunId": 0, @@ -262,14 +262,12 @@ def test_batch_run(): # noqa: D103 "agent_local": 250.0, "rng": 31415, }, - ] with pytest.raises(ValueError): mesa.batch_run(MockModel, {}, number_processes=2, rng=42, iterations=1) - def test_batch_run_with_params(): # noqa: D103 mesa.batch_run( MockModel, From 904e796f5a469729e78380b782b723da6dba1f9e Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Wed, 12 Nov 2025 10:41:37 +0100 Subject: [PATCH 05/19] fix for typo in value error message --- mesa/batchrunner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesa/batchrunner.py b/mesa/batchrunner.py index 6d9fcde5c9c..094361c826f 100644 --- a/mesa/batchrunner.py +++ b/mesa/batchrunner.py @@ -78,7 +78,7 @@ def batch_run( """ if iterations is not None and rng is not None: raise ValueError( - "you cannont use both iterations and rng at the same time. Please only use rng." + "you cannot use both iterations and rng at the same time. Please only use rng." ) if iterations is not None: warnings.warn( From 797263818150b7584bae19d5c5f0e13e0777c2ef Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Wed, 12 Nov 2025 10:42:36 +0100 Subject: [PATCH 06/19] Update batchrunner.py --- mesa/batchrunner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesa/batchrunner.py b/mesa/batchrunner.py index 094361c826f..38c847c8882 100644 --- a/mesa/batchrunner.py +++ b/mesa/batchrunner.py @@ -82,7 +82,7 @@ def batch_run( ) if iterations is not None: warnings.warn( - "iterations is deprecated, please use rgn instead", + "iterations is deprecated, please use rng instead", DeprecationWarning, stacklevel=2, ) From 529f3acfeaa1604c0462e25a747eee4dea52091b Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sun, 16 Nov 2025 16:28:50 +0100 Subject: [PATCH 07/19] Update migration_guide.md --- docs/migration_guide.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/docs/migration_guide.md b/docs/migration_guide.md index 06d2b9f65c9..5aa81acd910 100644 --- a/docs/migration_guide.md +++ b/docs/migration_guide.md @@ -3,6 +3,38 @@ This guide contains breaking changes between major Mesa versions and how to reso Non-breaking changes aren't included, for those see our [Release history](https://github.com/projectmesa/mesa/releases). +## Mesa 3.4.0 + +### batch run +`batch_run` has been updated to offer explicit control over the random seeds that are used to run +multiple replications of a given experiment. For this a new keyword argument, `rng` has been +added and `iterations` will issue a `DeprecatinWarning`. The new `rng` keyword argument +takes a valid value for seeding or a list of valid values. If you want to run multiple iterations/replications +of a given experiment, you need to pass the required seeds explicitly. + +Below is a simple example of the new recommeded usage of `batch_run`. Note how we first +create 5 random integers which we then use as seed values for the new `rng` keyword argument. + +```python +import numpy as np +import sys + +rng = np.random.default_rng(42) +rng_values = rng.integers(0, sys.maxsize, size=(5,)) + +results = mesa.batch_run( + MoneyModel, + parameters=params, + rng=rng_values, + max_steps=100, + number_processes=1, + data_collection_period=1, + display_progress=True, +) + + +``` + ## Mesa 3.3.0 Mesa 3.3.0 is a visualization upgrade introducing a new and improved API, full support for both `altair` and `matplotlib` backends, and resolving several recurring issues from previous versions. From 7b6eaef0798ae84fa01f2d5f7db61c73fc0bcfa9 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sun, 16 Nov 2025 16:29:39 +0100 Subject: [PATCH 08/19] Update migration_guide.md --- docs/migration_guide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/migration_guide.md b/docs/migration_guide.md index 5aa81acd910..a4bdbd736d3 100644 --- a/docs/migration_guide.md +++ b/docs/migration_guide.md @@ -8,11 +8,11 @@ Non-breaking changes aren't included, for those see our [Release history](https: ### batch run `batch_run` has been updated to offer explicit control over the random seeds that are used to run multiple replications of a given experiment. For this a new keyword argument, `rng` has been -added and `iterations` will issue a `DeprecatinWarning`. The new `rng` keyword argument +added and `iterations` will issue a `DeprecationWarning`. The new `rng` keyword argument takes a valid value for seeding or a list of valid values. If you want to run multiple iterations/replications of a given experiment, you need to pass the required seeds explicitly. -Below is a simple example of the new recommeded usage of `batch_run`. Note how we first +Below is a simple example of the new recommended usage of `batch_run`. Note how we first create 5 random integers which we then use as seed values for the new `rng` keyword argument. ```python From e53e16b7f01516d356d52f3d577603ebaa9cc4e4 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sun, 16 Nov 2025 16:30:24 +0100 Subject: [PATCH 09/19] Update migration_guide.md --- docs/migration_guide.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/migration_guide.md b/docs/migration_guide.md index a4bdbd736d3..ab8746e6947 100644 --- a/docs/migration_guide.md +++ b/docs/migration_guide.md @@ -19,13 +19,14 @@ create 5 random integers which we then use as seed values for the new `rng` keyw import numpy as np import sys +# let's create 5 random integers rng = np.random.default_rng(42) rng_values = rng.integers(0, sys.maxsize, size=(5,)) results = mesa.batch_run( MoneyModel, parameters=params, - rng=rng_values, + rng=rng_values, # we pass the 5 seed values to rng max_steps=100, number_processes=1, data_collection_period=1, From 4113a11d7be10eadf8f354e32e7392e38cbbd8a9 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sun, 16 Nov 2025 16:37:15 +0100 Subject: [PATCH 10/19] Update 9_batch_run.ipynb --- docs/tutorials/9_batch_run.ipynb | 127 ++++++++++++++++++++++++++----- 1 file changed, 109 insertions(+), 18 deletions(-) diff --git a/docs/tutorials/9_batch_run.ipynb b/docs/tutorials/9_batch_run.ipynb index 98ee83e09bd..d6706325d09 100644 --- a/docs/tutorials/9_batch_run.ipynb +++ b/docs/tutorials/9_batch_run.ipynb @@ -33,6 +33,17 @@ "### IN COLAB? - Run the next cell " ] }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "\n", + "sys.path.insert(0, \"../../\")\n" + ] + }, { "cell_type": "raw", "metadata": { @@ -54,7 +65,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": { "collapsed": false, "jupyter": { @@ -94,7 +105,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -133,8 +144,8 @@ "class MoneyModel(mesa.Model):\n", " \"\"\"A model with some number of agents.\"\"\"\n", "\n", - " def __init__(self, n, width, height, seed=None):\n", - " super().__init__(seed=seed)\n", + " def __init__(self, n, width, height, rng=None):\n", + " super().__init__(rng=rng)\n", " self.num_agents = n\n", " self.grid = OrthogonalMooreGrid(\n", " (width, height), torus=True, capacity=10, random=self.random\n", @@ -162,7 +173,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -199,8 +210,8 @@ "* `number_processes`\n", " If not specified, defaults to 1. Set it to `None` to use all the available processors.\n", " Note: Multiprocessing does make debugging challenging. If your parameter sweeps are resulting in unexpected errors set `number_processes=1`.\n", - "* `iterations`\n", - " The number of iterations to run each parameter combination for. Optional. If not specified, defaults to 1.\n", + "* `rng`\n", + " a valid value or iterable of values for seeding the random number generator in the model. Defaults to a single None value meaning the model is ran once.\n", "* `data_collection_period`\n", " The length of the period (number of steps) after which the model and agent reporters collect data. Optional. If not specified, defaults to -1, i.e. only at the end of each episode.\n", "* `max_steps`\n", @@ -227,16 +238,42 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": {}, "outputs": [], + "source": [ + "import sys\n", + "rng = np.random.default_rng(42)\n", + "seed_values= rng.integers(0, sys.maxsize, size=(5,))" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "c7797bd28cfb4f24bd3a95fdfd78b2b9", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + " 0%| | 0/100 [00:00 Date: Sun, 16 Nov 2025 16:51:00 +0100 Subject: [PATCH 11/19] add support for both seed and rng --- docs/tutorials/9_batch_run.ipynb | 65 +++++++++++++------------------- mesa/batchrunner.py | 10 ++++- 2 files changed, 35 insertions(+), 40 deletions(-) diff --git a/docs/tutorials/9_batch_run.ipynb b/docs/tutorials/9_batch_run.ipynb index d6706325d09..62e1dc02da0 100644 --- a/docs/tutorials/9_batch_run.ipynb +++ b/docs/tutorials/9_batch_run.ipynb @@ -33,17 +33,6 @@ "### IN COLAB? - Run the next cell " ] }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "import sys\n", - "\n", - "sys.path.insert(0, \"../../\")\n" - ] - }, { "cell_type": "raw", "metadata": { @@ -65,7 +54,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 2, "metadata": { "collapsed": false, "jupyter": { @@ -105,7 +94,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -144,8 +133,8 @@ "class MoneyModel(mesa.Model):\n", " \"\"\"A model with some number of agents.\"\"\"\n", "\n", - " def __init__(self, n, width, height, rng=None):\n", - " super().__init__(rng=rng)\n", + " def __init__(self, n, width, height, seed=None):\n", + " super().__init__(seed=seed)\n", " self.num_agents = n\n", " self.grid = OrthogonalMooreGrid(\n", " (width, height), torus=True, capacity=10, random=self.random\n", @@ -173,7 +162,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -238,7 +227,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -249,13 +238,13 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "c7797bd28cfb4f24bd3a95fdfd78b2b9", + "model_id": "2a208c5972c84a9ebc77fbc828763c94", "version_major": 2, "version_minor": 0 }, @@ -273,7 +262,7 @@ "results = mesa.batch_run(\n", " MoneyModel,\n", " parameters=params,\n", - " rng=seed_values,\n", + " rng=seed_values.tolist(),\n", " max_steps=100,\n", " number_processes=1,\n", " data_collection_period=1,\n", @@ -297,7 +286,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -305,7 +294,7 @@ "output_type": "stream", "text": [ "The results have 525100 rows.\n", - "The columns of the data frame are ['RunId', 'iteration', 'Step', 'width', 'height', 'n', 'rng', 'Gini', 'AgentID', 'Wealth', 'Steps_not_given'].\n" + "The columns of the data frame are ['RunId', 'iteration', 'Step', 'width', 'height', 'n', 'seed', 'Gini', 'AgentID', 'Wealth', 'Steps_not_given'].\n" ] } ], @@ -324,7 +313,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -351,7 +340,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "metadata": { "collapsed": false, "jupyter": { @@ -382,7 +371,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -399,8 +388,8 @@ " 100 6.0 1.0\n", " 100 7.0 1.0\n", " 100 8.0 1.0\n", - " 100 9.0 1.0\n", - " 100 10.0 1.0\n" + " 100 9.0 2.0\n", + " 100 10.0 0.0\n" ] } ], @@ -429,7 +418,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -437,17 +426,17 @@ "output_type": "stream", "text": [ " Step Gini\n", - " 1 0.0\n", - " 2 0.0\n", - " 3 0.0\n", - " 4 0.0\n", - " 5 0.0\n", + " 1 0.00\n", + " 2 0.00\n", + " 3 0.00\n", + " 4 0.00\n", + " 5 0.00\n", " ... ...\n", - " 96 0.0\n", - " 97 0.0\n", - " 98 0.0\n", - " 99 0.0\n", - " 100 0.0\n" + " 96 0.18\n", + " 97 0.18\n", + " 98 0.18\n", + " 99 0.18\n", + " 100 0.18\n" ] } ], diff --git a/mesa/batchrunner.py b/mesa/batchrunner.py index 38c847c8882..64488c49927 100644 --- a/mesa/batchrunner.py +++ b/mesa/batchrunner.py @@ -27,7 +27,7 @@ ) """ - +import inspect import itertools import multiprocessing import warnings @@ -92,11 +92,17 @@ def batch_run( if not isinstance(rng, Iterable): rng = [rng] + # establish to use seed or rng as name for parameter + model_parameters = inspect.signature(Model).parameters + rng_kwarg_name = "rng" + if "seed" in model_parameters: + rng_kwarg_name = "seed" + runs_list = [] run_id = 0 for i, rng_i in enumerate(rng): for kwargs in _make_model_kwargs(parameters): - kwargs["rng"] = rng_i + kwargs[rng_kwarg_name] = rng_i runs_list.append((run_id, i, kwargs)) run_id += 1 From 0778ff48ca904b98f131d93a196567bd9f4ccf9e Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sun, 16 Nov 2025 16:51:29 +0100 Subject: [PATCH 12/19] Update migration_guide.md --- docs/migration_guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migration_guide.md b/docs/migration_guide.md index ab8746e6947..d1787f9e868 100644 --- a/docs/migration_guide.md +++ b/docs/migration_guide.md @@ -26,7 +26,7 @@ rng_values = rng.integers(0, sys.maxsize, size=(5,)) results = mesa.batch_run( MoneyModel, parameters=params, - rng=rng_values, # we pass the 5 seed values to rng + rng=rng_values.tolist(), # we pass the 5 seed values to rng max_steps=100, number_processes=1, data_collection_period=1, From 5db0058239e643a67cdd90d51821fe277dfcb410 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 16 Nov 2025 17:29:18 +0000 Subject: [PATCH 13/19] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- docs/tutorials/9_batch_run.ipynb | 3 ++- mesa/batchrunner.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/tutorials/9_batch_run.ipynb b/docs/tutorials/9_batch_run.ipynb index 62e1dc02da0..e4e79c2d37a 100644 --- a/docs/tutorials/9_batch_run.ipynb +++ b/docs/tutorials/9_batch_run.ipynb @@ -232,8 +232,9 @@ "outputs": [], "source": [ "import sys\n", + "\n", "rng = np.random.default_rng(42)\n", - "seed_values= rng.integers(0, sys.maxsize, size=(5,))" + "seed_values = rng.integers(0, sys.maxsize, size=(5,))" ] }, { diff --git a/mesa/batchrunner.py b/mesa/batchrunner.py index 64488c49927..31d582a55f0 100644 --- a/mesa/batchrunner.py +++ b/mesa/batchrunner.py @@ -27,6 +27,7 @@ ) """ + import inspect import itertools import multiprocessing From 27a777dad1b4866480c5db5238081113cd7d475a Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Mon, 17 Nov 2025 10:09:56 +0100 Subject: [PATCH 14/19] Update test_batch_run.py --- tests/test_batch_run.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/test_batch_run.py b/tests/test_batch_run.py index dcbc63079c2..b5b661c6d67 100644 --- a/tests/test_batch_run.py +++ b/tests/test_batch_run.py @@ -142,7 +142,7 @@ def test_batch_run(): # noqa: D103 "AgentID": 1, "agent_id": 1, "agent_local": 250.0, - "rng": 42, + "seed": 42, }, { "RunId": 0, @@ -152,7 +152,7 @@ def test_batch_run(): # noqa: D103 "AgentID": 2, "agent_id": 2, "agent_local": 250.0, - "rng": 42, + "seed": 42, }, { "RunId": 0, @@ -162,7 +162,7 @@ def test_batch_run(): # noqa: D103 "AgentID": 3, "agent_id": 3, "agent_local": 250.0, - "rng": 42, + "seed": 42, }, ] @@ -176,7 +176,7 @@ def test_batch_run(): # noqa: D103 "AgentID": 1, "agent_id": 1, "agent_local": 250.0, - "rng": None, + "seed": None, }, { "RunId": 0, @@ -186,7 +186,7 @@ def test_batch_run(): # noqa: D103 "AgentID": 2, "agent_id": 2, "agent_local": 250.0, - "rng": None, + "seed": None, }, { "RunId": 0, @@ -196,7 +196,7 @@ def test_batch_run(): # noqa: D103 "AgentID": 3, "agent_id": 3, "agent_local": 250.0, - "rng": None, + "seed": None, }, ] @@ -210,7 +210,7 @@ def test_batch_run(): # noqa: D103 "AgentID": 1, "agent_id": 1, "agent_local": 250.0, - "rng": 42, + "seed": 42, }, { "RunId": 0, @@ -220,7 +220,7 @@ def test_batch_run(): # noqa: D103 "AgentID": 2, "agent_id": 2, "agent_local": 250.0, - "rng": 42, + "seed": 42, }, { "RunId": 0, @@ -230,7 +230,7 @@ def test_batch_run(): # noqa: D103 "AgentID": 3, "agent_id": 3, "agent_local": 250.0, - "rng": 42, + "seed": 42, }, { "RunId": 1, @@ -240,7 +240,7 @@ def test_batch_run(): # noqa: D103 "AgentID": 1, "agent_id": 1, "agent_local": 250.0, - "rng": 31415, + "seed": 31415, }, { "RunId": 1, @@ -250,7 +250,7 @@ def test_batch_run(): # noqa: D103 "AgentID": 2, "agent_id": 2, "agent_local": 250.0, - "rng": 31415, + "seed": 31415, }, { "RunId": 1, @@ -260,7 +260,7 @@ def test_batch_run(): # noqa: D103 "AgentID": 3, "agent_id": 3, "agent_local": 250.0, - "rng": 31415, + "seed": 31415, }, ] @@ -291,7 +291,7 @@ def test_batch_run_no_agent_reporters(): # noqa: D103 "Step": 1000, "enable_agent_reporters": False, "reported_model_param": 42, - "rng": None, + "seed": None, } ] @@ -315,7 +315,7 @@ def test_batch_run_unhashable_param(): # noqa: D103 "agent_local": 250.0, "n_agents": 2, "variable_model_params": {"key": "value"}, - "rng": None, + "seed": None, } assert result == [ From 0a4ea994ad1260398be373271aa7fe6b12f5b088 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Mon, 17 Nov 2025 10:21:15 +0100 Subject: [PATCH 15/19] Update test_batch_run.py --- tests/test_batch_run.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_batch_run.py b/tests/test_batch_run.py index b5b661c6d67..92d7c487963 100644 --- a/tests/test_batch_run.py +++ b/tests/test_batch_run.py @@ -201,6 +201,10 @@ def test_batch_run(): # noqa: D103 ] result = mesa.batch_run(MockModel, {}, number_processes=2, rng=[42, 31415]) + + # we use 2 processes, so we are not guaranteed the order of the return + result = sorted(result, key=lambda x: (x["RunId"], x["AgentID"])) + assert result == [ { "RunId": 0, From 472692061072c0de27a40dc7b8066a12ea29359a Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Mon, 17 Nov 2025 12:37:30 +0100 Subject: [PATCH 16/19] Update docs/migration_guide.md Co-authored-by: Ewout ter Hoeven <15776622+EwoutH@users.noreply.github.com> --- docs/migration_guide.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/migration_guide.md b/docs/migration_guide.md index d1787f9e868..885e1cff47a 100644 --- a/docs/migration_guide.md +++ b/docs/migration_guide.md @@ -32,8 +32,6 @@ results = mesa.batch_run( data_collection_period=1, display_progress=True, ) - - ``` ## Mesa 3.3.0 From 7ba67df58c788ce17ff498b022f4c3c680f31805 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Mon, 17 Nov 2025 12:38:10 +0100 Subject: [PATCH 17/19] Update mesa/batchrunner.py Co-authored-by: Ewout ter Hoeven <15776622+EwoutH@users.noreply.github.com> --- mesa/batchrunner.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mesa/batchrunner.py b/mesa/batchrunner.py index 31d582a55f0..014496d0f27 100644 --- a/mesa/batchrunner.py +++ b/mesa/batchrunner.py @@ -83,7 +83,8 @@ def batch_run( ) if iterations is not None: warnings.warn( - "iterations is deprecated, please use rng instead", + "The `iterations` keyword argument is deprecated, please use `rng` instead. + See https://mesa.readthedocs.io/latest/migration_guide.html#batch-run", DeprecationWarning, stacklevel=2, ) From 088425a7ee9eccc747cc2c540571038a3191bbad Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Mon, 17 Nov 2025 12:39:07 +0100 Subject: [PATCH 18/19] Update mesa/batchrunner.py Co-authored-by: Ewout ter Hoeven <15776622+EwoutH@users.noreply.github.com> --- mesa/batchrunner.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mesa/batchrunner.py b/mesa/batchrunner.py index 014496d0f27..e6101f2f069 100644 --- a/mesa/batchrunner.py +++ b/mesa/batchrunner.py @@ -88,9 +88,7 @@ def batch_run( DeprecationWarning, stacklevel=2, ) - rng = [ - None, - ] * iterations + rng = [None] * iterations if not isinstance(rng, Iterable): rng = [rng] From 435399c6445f98f60304326d40947c6e6e7d8024 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Mon, 17 Nov 2025 12:46:25 +0100 Subject: [PATCH 19/19] Update batchrunner.py --- mesa/batchrunner.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesa/batchrunner.py b/mesa/batchrunner.py index e6101f2f069..37d971f8fb8 100644 --- a/mesa/batchrunner.py +++ b/mesa/batchrunner.py @@ -83,8 +83,8 @@ def batch_run( ) if iterations is not None: warnings.warn( - "The `iterations` keyword argument is deprecated, please use `rng` instead. - See https://mesa.readthedocs.io/latest/migration_guide.html#batch-run", + "The `iterations` keyword argument is deprecated, please use `rng` instead." + "See https://mesa.readthedocs.io/latest/migration_guide.html#batch-run", DeprecationWarning, stacklevel=2, )