Skip to content

Commit

Permalink
Fix unintended behavior change in RandomActivationByType.agents_by_ty…
Browse files Browse the repository at this point in the history
…pe (projectmesa#1965)

* Make RandomActivationByType.agents_by_type backward compatible

As #pointed out by @Corvince, RandomActivationByType.agents_by_type in 2.2 behaved differently from 2.1.5. This is a simple fix for this, with a warning added to raise awareness of future changes.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* updated warning
  • Loading branch information
quaquel authored Jan 15, 2024
1 parent f21d242 commit d3a0e2f
Showing 1 changed file with 25 additions and 9 deletions.
34 changes: 25 additions & 9 deletions mesa/time.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import heapq
import warnings
import weakref
from collections import defaultdict
from collections.abc import Iterable

# mypy
Expand Down Expand Up @@ -287,6 +288,21 @@ class RandomActivationByType(BaseScheduler):
- get_type_count: Returns the count of agents of a specific type.
"""

@property
def agents_by_type(self):
warnings.warn(
"Because of the shift to using AgentSet, in the future this attribute will return a dict with"
"type as key as AgentSet as value. Future behavior is available via RandomActivationByType._agents_by_type",
DeprecationWarning,
stacklevel=2,
)

agentsbytype = defaultdict(dict)
for k, v in self._agents_by_type.items():
agentsbytype[k] = {agent: agent.unique_id for agent in v}

return agentsbytype

def __init__(self, model: Model, agents: Iterable[Agent] | None = None) -> None:
super().__init__(model, agents)
"""
Expand All @@ -297,14 +313,14 @@ def __init__(self, model: Model, agents: Iterable[Agent] | None = None) -> None:
"""

# can't be a defaultdict because we need to pass model to AgentSet
self.agents_by_type: [type, AgentSet] = {}
self._agents_by_type: [type, AgentSet] = {}

if agents is not None:
for agent in agents:
try:
self.agents_by_type[type(agent)].add(agent)
self._agents_by_type[type(agent)].add(agent)
except KeyError:
self.agents_by_type[type(agent)] = AgentSet([agent], self.model)
self._agents_by_type[type(agent)] = AgentSet([agent], self.model)

def add(self, agent: Agent) -> None:
"""
Expand All @@ -316,16 +332,16 @@ def add(self, agent: Agent) -> None:
super().add(agent)

try:
self.agents_by_type[type(agent)].add(agent)
self._agents_by_type[type(agent)].add(agent)
except KeyError:
self.agents_by_type[type(agent)] = AgentSet([agent], self.model)
self._agents_by_type[type(agent)] = AgentSet([agent], self.model)

def remove(self, agent: Agent) -> None:
"""
Remove all instances of a given agent from the schedule.
"""
super().remove(agent)
self.agents_by_type[type(agent)].remove(agent)
self._agents_by_type[type(agent)].remove(agent)

def step(self, shuffle_types: bool = True, shuffle_agents: bool = True) -> None:
"""
Expand All @@ -339,7 +355,7 @@ def step(self, shuffle_types: bool = True, shuffle_agents: bool = True) -> None:
"""
# To be able to remove and/or add agents during stepping
# it's necessary to cast the keys view to a list.
type_keys: list[type[Agent]] = list(self.agents_by_type.keys())
type_keys: list[type[Agent]] = list(self._agents_by_type.keys())
if shuffle_types:
self.model.random.shuffle(type_keys)
for agent_class in type_keys:
Expand All @@ -355,7 +371,7 @@ def step_type(self, agenttype: type[Agent], shuffle_agents: bool = True) -> None
Args:
agenttype: Class object of the type to run.
"""
agents = self.agents_by_type[agenttype]
agents = self._agents_by_type[agenttype]

if shuffle_agents:
agents.shuffle(inplace=True)
Expand All @@ -365,7 +381,7 @@ def get_type_count(self, agenttype: type[Agent]) -> int:
"""
Returns the current number of agents of certain type in the queue.
"""
return len(self.agents_by_type[agenttype])
return len(self._agents_by_type[agenttype])


class DiscreteEventScheduler(BaseScheduler):
Expand Down

0 comments on commit d3a0e2f

Please sign in to comment.