Skip to content
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

Allow AgentSet.do() to take Callable function #2219

Merged
merged 14 commits into from
Aug 18, 2024

Conversation

quaquel
Copy link
Member

@quaquel quaquel commented Aug 17, 2024

This PR enhances AgentSet.do to take a callable or str. Currently, AgentSet.do takes a str which maps to a method on the agents in the set. This PR makes it possible to also use a Callable instead.

This callable will be called with the agent as the first argument.

⚠️ Breaking change ⚠️

A small breaking change is introduced here: the method_name parameter is renamed to method. For models that use this as a keyword argument this is a breaking change, and need to replace do(method_name="something") with do(method="something").

Usage examples

Here are a few examples of how to use do() with Callable functions.

Suppose we want to move each agent by a fixed offset:

def move_agent(agent, dx, dy):
    # Assume each agent has a 'pos' attribute which is a tuple (x, y)
    if agent.pos:
        agent.pos = (agent.pos[0] + dx, agent.pos[1] + dy)

# Usage with do()
agentset.do(move_agent, 1, -1)

You might want to increase the energy level of all agents by a fixed amount:

agentset.do(lambda agent: setattr(agent, 'energy', agent.energy + 10))

You can apply a function conditionally to agents that meet a certain criterion:

def heal_agent(agent):
    if agent.health < 50:
        agent.health += 20

# Apply the healing only to agents with health below 50
agentset.do(lambda agent: heal_agent(agent) if agent.health < 50 else None)

You may want to gather results from applying a function to each agent:

def get_agent_status(agent):
    return f"Agent {agent.unique_id}: {agent.status}"

statuses = agentset.do(get_agent_status, return_results=True)

If your callable needs additional arguments, you can pass them directly via do():

def update_wealth(agent, amount, tax_rate):
    agent.wealth += amount * (1 - tax_rate)

agentset.do(update_wealth, 100, 0.2)

Suppose you want to update each agent's position based on their velocity:

def update_position(agent):
    if agent.pos and hasattr(agent, 'velocity'):
        agent.pos = (agent.pos[0] + agent.velocity[0], agent.pos[1] + agent.velocity[1])

agentset.do(update_position)

And of course the current behavior of passing a string as method is still supported:

agentset.do("step")

@quaquel quaquel added the enhancement Release notes label label Aug 17, 2024
Copy link

Performance benchmarks:

Model Size Init time [95% CI] Run time [95% CI]
Schelling small 🔵 +3.0% [+2.7%, +3.3%] 🔵 +1.2% [+1.0%, +1.4%]
Schelling large 🔴 +3.5% [+3.2%, +3.8%] 🔵 +0.4% [-0.2%, +1.0%]
WolfSheep small 🔵 +2.5% [+1.7%, +3.4%] 🔵 +2.7% [+2.4%, +3.0%]
WolfSheep large 🔵 +2.7% [+2.5%, +2.9%] 🔵 +1.4% [+0.9%, +1.9%]
BoidFlockers small 🔵 +1.4% [+0.7%, +2.0%] 🔵 +0.7% [+0.1%, +1.3%]
BoidFlockers large 🔵 +1.4% [+1.0%, +1.9%] 🔵 -0.4% [-1.1%, +0.3%]

Copy link
Member

@EwoutH EwoutH left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome! Code wise and docstring wise it looks really good to me. Only a few tests are needed.

@EwoutH EwoutH added the breaking Release notes label label Aug 17, 2024
@EwoutH EwoutH changed the title Agentset do Allow AgentSet.do() to take Callable function Aug 17, 2024
@EwoutH EwoutH marked this pull request as ready for review August 17, 2024 19:17
@EwoutH
Copy link
Member

EwoutH commented Aug 17, 2024

I added tests and updated the Readme with a note about the breaking change and some usage examples.

@quaquel please check the tests and examples and @projectmesa/maintainers I would love one additional review.

Copy link

Performance benchmarks:

Model Size Init time [95% CI] Run time [95% CI]
Schelling small 🔵 +0.2% [-0.1%, +0.5%] 🔵 +0.4% [+0.1%, +0.6%]
Schelling large 🔵 -0.5% [-1.2%, +0.3%] 🔵 -0.6% [-1.7%, +0.6%]
WolfSheep small 🔵 -1.0% [-1.5%, -0.7%] 🔵 -1.2% [-1.5%, -1.0%]
WolfSheep large 🔵 -3.1% [-3.5%, -2.7%] 🟢 -5.7% [-7.1%, -4.1%]
BoidFlockers small 🔵 -0.3% [-0.8%, +0.1%] 🔵 +0.1% [-0.8%, +1.1%]
BoidFlockers large 🔵 +0.0% [-0.5%, +0.5%] 🔵 +0.1% [-0.6%, +0.8%]

@EwoutH EwoutH merged commit 3cf1b76 into projectmesa:main Aug 18, 2024
11 of 12 checks passed
quaquel added a commit to quaquel/mesa that referenced this pull request Aug 18, 2024
This PR enhances `AgentSet.do` to take a callable or str. Currently, AgentSet.do takes a `str` which maps to a method on the agents in the set. This PR makes it possible to also use a `Callable` instead.

This callable will be called with the `agent` as the first argument.

⚠️ Breaking change ⚠️
A small breaking change is introduced here: the `method_name` parameter is renamed to `method`. For models that use this as a keyword argument this is a breaking change, and need to replace `do(method_name="something")` with `do(method="something")`.
EwoutH pushed a commit that referenced this pull request Sep 1, 2024
This PR enhances `AgentSet.do` to take a callable or str. Currently, AgentSet.do takes a `str` which maps to a method on the agents in the set. This PR makes it possible to also use a `Callable` instead.

This callable will be called with the `agent` as the first argument.

⚠️ Breaking change ⚠️
A small breaking change is introduced here: the `method_name` parameter is renamed to `method`. For models that use this as a keyword argument this is a breaking change, and need to replace `do(method_name="something")` with `do(method="something")`.
EwoutH pushed a commit to EwoutH/mesa that referenced this pull request Sep 24, 2024
This PR enhances `AgentSet.do` to take a callable or str. Currently, AgentSet.do takes a `str` which maps to a method on the agents in the set. This PR makes it possible to also use a `Callable` instead.

This callable will be called with the `agent` as the first argument.

⚠️ Breaking change ⚠️
A small breaking change is introduced here: the `method_name` parameter is renamed to `method`. For models that use this as a keyword argument this is a breaking change, and need to replace `do(method_name="something")` with `do(method="something")`.
@quaquel quaquel deleted the agentset_do branch November 4, 2024 19:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking Release notes label enhancement Release notes label
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants