From a7a2c872b0e42a6d20d77d3a501e2cf0529448ad Mon Sep 17 00:00:00 2001 From: Ewout ter Hoeven Date: Fri, 26 Jan 2024 13:24:08 +0100 Subject: [PATCH 1/8] tests: Add test to check if RandomActivation is not sequential Adds a test that checks if the RandomActivation doesn't trigger agents in a sequential order. In theory this could give false positives (a test passing when it shouldn't, but that chance is around ~0.1^18). --- tests/test_time.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/test_time.py b/tests/test_time.py index 3cfc2f35078..53967d1a8ef 100644 --- a/tests/test_time.py +++ b/tests/test_time.py @@ -224,6 +224,28 @@ def test_get_agent_keys(self): agent_ids = {agent.unique_id for agent in model.agents} assert all(entry in agent_ids for entry in keys) + def test_not_sequential(self): + model = MockModel(activation=RANDOM) + # Create 10 agents + for _ in range(10): + model.schedule.add(MockAgent(model.next_id(), model)) + # Run 3 steps + for _ in range(3): + model.step() + # Filter out non-integer elements from the log + filtered_log = [item for item in model.log if isinstance(item, int)] + + # Check that there are no 18 consecutive agents id's in the filtered log + total_agents = 10 + assert not any( + all( + (filtered_log[(i + j) % total_agents] - filtered_log[i]) % total_agents + == j % total_agents + for j in range(18) + ) + for i in range(len(filtered_log)) + ), f"Agents are activated sequentially:\n{filtered_log}" + class TestSimultaneousActivation(TestCase): """ From 90f04087ce04ab1a6cc6261f4d33581f08092f49 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Fri, 26 Jan 2024 13:54:36 +0100 Subject: [PATCH 2/8] fix for RandomActivation bug fixes #2006 --- mesa/time.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesa/time.py b/mesa/time.py index 87a68949cdd..7a253076614 100644 --- a/mesa/time.py +++ b/mesa/time.py @@ -143,8 +143,8 @@ def get_agent_keys(self, shuffle: bool = False) -> list[int]: def do_each(self, method, shuffle=False): if shuffle: - self.agents.shuffle(inplace=True) - self.agents.do(method) + self._agents.shuffle(inplace=True) + self._agents.do(method) class RandomActivation(BaseScheduler): From 1b62560f2f112fbe79687577b9fd538c4b1d29ea Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Fri, 26 Jan 2024 15:10:57 +0100 Subject: [PATCH 3/8] add agentset.shuffle unittest --- tests/test_agent.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/test_agent.py b/tests/test_agent.py index 5861038d793..5e6e7eaf58e 100644 --- a/tests/test_agent.py +++ b/tests/test_agent.py @@ -251,3 +251,15 @@ def test_agentset_select_by_type(): # Test with no type specified (should select all agents) all_agents = agentset.select() assert len(all_agents) == len(mixed_agents) + +def test_agentset_shuffle(): + model = Model() + test_agents = [TestAgent(model.next_id(), model) for _ in range(4)] + agentset = AgentSet(test_agents, model=model) + + agentset = agentset.shuffle() + assert not all([a1==a2 for a1, a2 in zip(test_agents, agentset)]) + + agentset = AgentSet(test_agents, model=model) + agentset.shuffle(inplace=True) + assert not all([a1 == a2 for a1, a2 in zip(test_agents, agentset)]) \ No newline at end of file From 6c2c5adcb9621e10fd479b6a819026e56ce4cb02 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 26 Jan 2024 14:12:49 +0000 Subject: [PATCH 4/8] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_agent.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_agent.py b/tests/test_agent.py index 5e6e7eaf58e..2a3a41e4036 100644 --- a/tests/test_agent.py +++ b/tests/test_agent.py @@ -252,14 +252,15 @@ def test_agentset_select_by_type(): all_agents = agentset.select() assert len(all_agents) == len(mixed_agents) + def test_agentset_shuffle(): model = Model() test_agents = [TestAgent(model.next_id(), model) for _ in range(4)] agentset = AgentSet(test_agents, model=model) agentset = agentset.shuffle() - assert not all([a1==a2 for a1, a2 in zip(test_agents, agentset)]) + assert not all([a1 == a2 for a1, a2 in zip(test_agents, agentset)]) agentset = AgentSet(test_agents, model=model) agentset.shuffle(inplace=True) - assert not all([a1 == a2 for a1, a2 in zip(test_agents, agentset)]) \ No newline at end of file + assert not all([a1 == a2 for a1, a2 in zip(test_agents, agentset)]) From 3abd882e0d89275b43da4821503daf54bc9fe007 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Fri, 26 Jan 2024 15:26:59 +0100 Subject: [PATCH 5/8] ruff fix --- tests/test_agent.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_agent.py b/tests/test_agent.py index 5e6e7eaf58e..8890e76a192 100644 --- a/tests/test_agent.py +++ b/tests/test_agent.py @@ -258,8 +258,8 @@ def test_agentset_shuffle(): agentset = AgentSet(test_agents, model=model) agentset = agentset.shuffle() - assert not all([a1==a2 for a1, a2 in zip(test_agents, agentset)]) + assert not all(a1 == a2 for a1, a2 in zip(test_agents, agentset)) agentset = AgentSet(test_agents, model=model) agentset.shuffle(inplace=True) - assert not all([a1 == a2 for a1, a2 in zip(test_agents, agentset)]) \ No newline at end of file + assert not all(a1 == a2 for a1, a2 in zip(test_agents, agentset)) From 8c0d9c19b0b3be475f673ca465dca2bc3caa5d07 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 26 Jan 2024 14:30:13 +0000 Subject: [PATCH 6/8] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_agent.py b/tests/test_agent.py index 68841554abb..349daf2f0ab 100644 --- a/tests/test_agent.py +++ b/tests/test_agent.py @@ -256,7 +256,7 @@ def test_agentset_select_by_type(): def test_agentset_shuffle(): model = Model() test_agents = [TestAgent(model.next_id(), model) for _ in range(4)] - + agentset = AgentSet(test_agents, model=model) agentset = agentset.shuffle() assert not all(a1 == a2 for a1, a2 in zip(test_agents, agentset)) From 430d6f77261d9e9f6e9f884da0a4238745f2d48f Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Fri, 26 Jan 2024 16:52:44 +0100 Subject: [PATCH 7/8] add agent to model.agent correctly even if super was not called fix for the second issue in #2006. If super is not present, we create the data structure but forget to add the agent to it. This is just a backward compatibility fix. --- mesa/agent.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mesa/agent.py b/mesa/agent.py index fcf643d3289..6155284ba42 100644 --- a/mesa/agent.py +++ b/mesa/agent.py @@ -54,6 +54,7 @@ def __init__(self, unique_id: int, model: Model) -> None: except AttributeError: # model super has not been called self.model.agents_ = defaultdict(dict) + self.model.agents_[type(self)][self] = None self.model.agentset_experimental_warning_given = False warnings.warn( From ed371abd3165cf3d284ff7caccbfe24ade5c9692 Mon Sep 17 00:00:00 2001 From: Ewout ter Hoeven Date: Fri, 26 Jan 2024 17:38:28 +0100 Subject: [PATCH 8/8] test: Shuffle more agents to prevent false negatives No the chance on a false negative is one in 12! instead of 4! (40 million instead of 24) --- tests/test_agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_agent.py b/tests/test_agent.py index 349daf2f0ab..60a0eec15fa 100644 --- a/tests/test_agent.py +++ b/tests/test_agent.py @@ -255,7 +255,7 @@ def test_agentset_select_by_type(): def test_agentset_shuffle(): model = Model() - test_agents = [TestAgent(model.next_id(), model) for _ in range(4)] + test_agents = [TestAgent(model.next_id(), model) for _ in range(12)] agentset = AgentSet(test_agents, model=model) agentset = agentset.shuffle()