-
Notifications
You must be signed in to change notification settings - Fork 1k
feat: Implement random activation by type #1162
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Codecov Report
@@ Coverage Diff @@
## main #1162 +/- ##
==========================================
+ Coverage 89.28% 89.36% +0.07%
==========================================
Files 19 19
Lines 1269 1297 +28
Branches 259 264 +5
==========================================
+ Hits 1133 1159 +26
Misses 100 100
- Partials 36 38 +2
Continue to review full report at Codecov.
|
From an architecture perspective I agree this is the right course of action and a nice first step to full multiple agent types support. Haven't looked closely at the exact implementation, I can do that tomorrow if you want (however I think I'm not going to find much). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR, I added two comments, one about custom step orders and a documentation nitpick.
mesa/time.py
Outdated
agent_class: Type[Agent] = type(agent) | ||
del self.agents_by_type[agent_class][agent.unique_id] | ||
|
||
def step(self, by_type: bool = True) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add something like an optional order
input that allows to input the order in which the agent types are executed? Maybe that input could be either the string "reversed"
to loop through reversed(self.agents_by_type)
or a custom list with agent types.
I suggested a code snipped below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good idea. Also good to compare with the API of NetLogo, MASON, and Agents.jl.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agents.jl has 2 params: shuffle_types
, shuffle_agents
: https://github.com/JuliaDynamics/Agents.jl/blob/5d1182dc7dd80001492c27333898c50462bdb20e/src/submodules/schedulers.jl#L92-L99.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NetLogo' leaves the order up to the user's implementation with ask [breed]
: https://github.com/NetLogo/models/blob/2bfb84e92b367f312e397338e27a4c85889f13a0/Sample%20Models/Biology/Wolf%20Sheep%20Predation.nlogo#L57-L75.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can allow three strings for order
: regular
(default), reverse
and shuffle
, and a list of agent types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't find a wolf-sheep model for MASON. It is also N/A in https://juliadynamics.github.io/Agents.jl/stable/comparison/.
mesa/time.py
Outdated
if by_type: | ||
for agent_class in self.agents_by_type: | ||
self.step_type(agent_class) | ||
self.steps += 1 | ||
self.time += 1 | ||
else: | ||
super().step() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if by_type: | |
for agent_class in self.agents_by_type: | |
self.step_type(agent_class) | |
self.steps += 1 | |
self.time += 1 | |
else: | |
super().step() | |
if by_type: | |
if order == "regular" | |
for agent_class in self.agents_by_type: | |
self.step_type(agent_class) | |
if order == "reverse" | |
for agent_class in reversed(self.agents_by_type): | |
self.step_type(agent_class) | |
else: | |
for agent_class in order: | |
self.step_type(agent_class) | |
self.steps += 1 | |
self.time += 1 | |
else: | |
super().step() |
2 points that need consensus: First, the Second, the step order of each type. I can't think of a use case when a modeler wants to do the reverse of the original insertion order of the agent types during the model initialization. I think a more general solution is to have an argument called |
Agreed, lets drop the
A custom order like |
Shuffling may be in demand, but I'm not sure about the reverse order. |
I forgot to say that I removed the unnecessary |
Bump. @tpike3 thoughts so far? I think this is ready to merge. Once you ack, I will squash and merge. After that, I will port my implementation of Sugarscape with trading ({G1}, {M, T}) and make a PR for it. |
Dang, I am way behind... @rht and @EwoutH this is awesome. This is the implementation I am most familiar with so am ready to merge and I agree the terms (e.g type vs breed and shuffle) are the preferred.
To @rht's point thinking future customizations in new PRs before the next release ... I will concur and support two additional features in later PRs
|
You can do this with: if fully_random:
self.schedule = RandomActivation(self)
else:
self.scheduler = RandomActivationByType(self) And so we can keep the API minimal. |
@rht That is perfect! I didn't even think of that...so obvious I looked right over it. Squash and merge at your discretion |
This is the least controversial way of putting in random activation by type into the library, by simply using RandomActivationByBreed found in sugarscape_cg and wolf_sheep (I have checked that the schedule.py in both examples are identical). Some differences: - replace the term "breed" by a more general "type" - add Mypy typings If people want to get logging specific to one agent type, they can either - loop through all agents, and filter by their type - loop through model.scheduler.agents_by_type[type_class] We don't support agents that change their type, because in Python, you can't have an object that changes its intrinsic type over its lifetime. There could perhaps be a `RandomActivationByAttribute`, that groups agents by their attributes, which may change throughout the course of a simulation.
Yay! Can't wait for the sugarscape variations being in the repo (at least the ones that doesn't require Multi-level Mesa). |
Tracks agents in the model with a defaultdict. This PR adds a new `agents` dictionary to the Mesa `Model` class, enabling native support for handling multiple agent types within models. This way all modules can know which agents and agents type are in the model at any given time, by calling `model.agents`. NetLogo has had agent types, called [`breeds`](https://ccl.northwestern.edu/netlogo/docs/dict/breed.html), built-in from the start. It works perfectly in all NetLogo components, because it's a first class citizen and all components need to be designed to consider different breeds. In Mesa, agent types are an afterthought at best. Almost nothing is currently designed with multiple agent types in mind. That has caused several issues and limitations over the years, including: - projectmesa#348 - projectmesa#1142 - projectmesa#1162 Especially in scheduling, space and datacollection, lack of a native, consistent construct for agent types severely limits the possibilities. With the discussion about patches and "empty" this discussion done again. You might want empty to refer to all agents or only a subset of types or single type. That's currently cumbersome to implement. Basically, by always having dictionary available of which agents of which types are in the model, you can always trust on a consistent construct to iterate over agents and agent types. - The `Model` class now uses a `defaultdict` to store agents, ensuring a set is automatically created for each new agent type. - The `Agent` class has been updated to leverage this feature, simplifying the registration process when an agent is created. - The `remove` method in the `Agent` class now uses `discard`, providing a safer way to remove agents from the model.
Tracks agents in the model with a defaultdict. This PR adds a new `agents` dictionary to the Mesa `Model` class, enabling native support for handling multiple agent types within models. This way all modules can know which agents and agents type are in the model at any given time, by calling `model.agents`. NetLogo has had agent types, called [`breeds`](https://ccl.northwestern.edu/netlogo/docs/dict/breed.html), built-in from the start. It works perfectly in all NetLogo components, because it's a first class citizen and all components need to be designed to consider different breeds. In Mesa, agent types are an afterthought at best. Almost nothing is currently designed with multiple agent types in mind. That has caused several issues and limitations over the years, including: - #348 - #1142 - #1162 Especially in scheduling, space and datacollection, lack of a native, consistent construct for agent types severely limits the possibilities. With the discussion about patches and "empty" this discussion done again. You might want empty to refer to all agents or only a subset of types or single type. That's currently cumbersome to implement. Basically, by always having dictionary available of which agents of which types are in the model, you can always trust on a consistent construct to iterate over agents and agent types. - The `Model` class now uses a `defaultdict` to store agents, ensuring a set is automatically created for each new agent type. - The `Agent` class has been updated to leverage this feature, simplifying the registration process when an agent is created. - The `remove` method in the `Agent` class now uses `discard`, providing a safer way to remove agents from the model.
This is the least controversial way of putting in random activation by
type into the library, by simply using RandomActivationByBreed found in
sugarscape_cg and wolf_sheep (I have checked that the schedule.py in
both examples are identical).
Some differences:
If people want to get logging specific to one agent type, they can
either
We don't support agents that change their type, because in Python, you
can't have an object that changes its intrinsic type over its lifetime.
There could perhaps be a
RandomActivationByAttribute
, that groupsagents by their attributes, which may change throughout the course of a
simulation.