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

Visualisation: Allow specifying Agent shapes in agent_portrayal #2214

Merged
merged 8 commits into from
Aug 21, 2024

Conversation

rmhopkins4
Copy link
Contributor

@rmhopkins4 rmhopkins4 commented Aug 15, 2024

This PR allows specifying an "shape" in the agent_portrayal dictionary used by matplotlib component of the Solara visualisation. In short, it allows you represent an Agent in any matplotlib marker, by adding a "shape" key-value pair to the agent_portrayal dictionary.

This is especially useful when you're using the default shape drawer for grid or continuous space.

For example:

def agent_portrayal(cell):
    return {
        "color": "blue"
        "size": 5,
        "shape": "h"  # marker is a hexagon!
    }

Partial resolution of #2164.

Examples

In Conway's Game of Life life, you can now use black squares for the agents, as how it's traditionally displayed:

def agent_portrayal(cell):
    return {
        "color": "white" if cell.alive else "black",
        "size": 45,
        "shape": "s" # marker is a square
    }

image

You can also conditionally modify the shape, just like you can with the color and size:

COP_COLOR = "#000000"
AGENT_QUIET_COLOR = "#648FFF"
AGENT_REBEL_COLOR = "#FE6100"
JAIL_COLOR = "#808080"
JAIL_SHAPE = "s"


def citizen_cop_portrayal(agent):
    portrayal = {
        "Shape": "circle",
        "x": agent.pos[0],
        "y": agent.pos[1],
        "Filled": "true",
    }

    if isinstance(agent, Citizen):
        color = (
            AGENT_QUIET_COLOR if agent.condition == "Quiescent" else AGENT_REBEL_COLOR
        )
        if agent.jail_sentence:
            color = JAIL_COLOR
            shape = JAIL_SHAPE
        else:
            shape = "."
        color = JAIL_COLOR if agent.jail_sentence else color
        portrayal["color"] = color
        portrayal["shape"] = shape
        portrayal["size"] = 15.0

    else:
        assert isinstance(agent, Cop)
        portrayal["color"] = COP_COLOR
        portrayal["shape"] = "."
        portrayal["size"] = 25.0

    return portrayal

image

"shape" in agent_portrayal corresponds to "marker" in matplotlib's scatter function.
Copy link

Performance benchmarks:

Model Size Init time [95% CI] Run time [95% CI]
Schelling small 🔵 -0.0% [-0.4%, +0.3%] 🔵 +0.7% [+0.5%, +0.9%]
Schelling large 🔵 +0.5% [-0.4%, +1.4%] 🔴 +5.5% [+3.2%, +8.0%]
WolfSheep small 🔵 -0.1% [-1.2%, +1.1%] 🔵 +0.4% [+0.1%, +0.7%]
WolfSheep large 🔵 +0.3% [-0.5%, +1.4%] 🔵 +2.8% [+1.4%, +4.2%]
BoidFlockers small 🔵 +0.0% [-0.7%, +0.9%] 🔵 +0.5% [-0.3%, +1.3%]
BoidFlockers large 🔵 -0.0% [-0.4%, +0.3%] 🔵 -0.6% [-1.2%, -0.1%]

@rht
Copy link
Contributor

rht commented Aug 15, 2024

Implementation LGTM, thank you for implementing this feature. I could test it on the Epstein civil violence model if I have the time. But if you don't mind, showing that the jailed citizens as square in that example would be helpful. You can used the code from projectmesa/mesa-examples#158 and undo the experimental data collector part.

Previously, if only some agent_portrayals provided info like size, color, matplotlib would not accept the parameters and the program would crash.
Additionally, edited some names for coherence.
@rmhopkins4
Copy link
Contributor Author

image
Here is an example of jailed citizens as squares.
While working on this I made matplotlib.py provide default visualization values so that users only need to provide overrides

@rmhopkins4
Copy link
Contributor Author

image Here is an example of jailed citizens as squares. While working on this I made matplotlib.py provide default visualization values so that users only need to provide overrides

Matplotlib's 's' and 'c' (size, color) accept no values (all default), one value (all the same), and lists exactly as long as the number of points provided.
When the list lengths were not aligned problems arose.

@rmhopkins4 rmhopkins4 marked this pull request as ready for review August 15, 2024 20:54
Copy link

Performance benchmarks:

Model Size Init time [95% CI] Run time [95% CI]
Schelling small 🔵 +0.5% [+0.1%, +0.9%] 🔵 +0.3% [+0.1%, +0.5%]
Schelling large 🔵 -0.0% [-0.4%, +0.4%] 🔴 +4.8% [+3.0%, +6.5%]
WolfSheep small 🔵 +0.8% [-0.4%, +2.0%] 🔵 +0.5% [+0.3%, +0.8%]
WolfSheep large 🔵 +0.9% [+0.5%, +1.2%] 🔵 +2.2% [+1.5%, +2.8%]
BoidFlockers small 🔵 +2.3% [+1.5%, +2.9%] 🔵 -0.2% [-0.9%, +0.5%]
BoidFlockers large 🔵 +2.1% [+1.4%, +2.7%] 🔵 -1.1% [-1.5%, -0.6%]

@EwoutH
Copy link
Member

EwoutH commented Aug 15, 2024

Thanks a lot for this PR! I will review it somewhere tomorrow.

rmhopkins4 and others added 2 commits August 16, 2024 10:23
error message shows public names of visualization attributes.
default color and marker fixed to "tab:blue" & "o" respectively
@EwoutH
Copy link
Member

EwoutH commented Aug 16, 2024

@rht could you give a short summary what you already have sufficiently reviewed and what could use a second pair of eyes?

@rht
Copy link
Contributor

rht commented Aug 16, 2024

Actually, this PR is already LGTM, and I didn't merge because you still wanted to review. If any, maybe the perf consideration of an array of defaults instead of a single default value scalar, but this is minor because any loop done without calling Matplotlib many times is going to be fast.

@EwoutH
Copy link
Member

EwoutH commented Aug 16, 2024

Thanks. I would have expected this feature taking less complexity / lines of code, let me take a closer look tomorrow morning.

@EwoutH
Copy link
Member

EwoutH commented Aug 17, 2024

Okay, I'm largely good with this PR. It's a bit weird matplotlib/matplotlib#11155 was never implemented, but since the only thing changes for our users is that they can now optionally pass a matplotlib shape in a portrayal dict (right @rht?), we can always implement the back-end later in a more (performance) efficient way.

I would like the documentation around this PR to improve a little:

  • Update the docstring to note that a "shape" can be passed, and add a link to the list with allowed values (I think it's this one)
  • Update the first comment of this PR with the screenshot, one or two more examples, and a link to
  • Mention that you can pass "shape" in the visualization_tutorial (source).

@rht
Copy link
Contributor

rht commented Aug 17, 2024

Okay, I'm largely good with this PR. It's a bit weird matplotlib/matplotlib#11155 was never implemented, but since the only thing changes for our users is that they can now optionally pass a matplotlib shape in a portrayal dict (right @rht?), we can always implement the back-end later in a more (performance) efficient way.

Yes, it's optional and is not a breaking change.

While it's good to update the docstring now, I think the tutorial change can happen in a separate PR. Otherwise it makes Mesa too bureaucratic to contribute to (that every new feature needs to be in the tutorial in the same PR).

@EwoutH
Copy link
Member

EwoutH commented Aug 17, 2024

Yeah I see your perspective and also considered that. But otherwise I'm afraid it doesn't get done. Especially with our user base changes need to be (very) visible.

@rmhopkins4 let me know if you want to do these things yourself, or otherwise allow us to do it within this PR.

@rmhopkins4
Copy link
Contributor Author

Yeah I see your perspective and also considered that. But otherwise I'm afraid it doesn't get done. Especially with our user base changes need to be (very) visible.

@rmhopkins4 let me know if you want to do these things yourself, or otherwise allow us to do it within this PR.

You should have the ability to edit the PR already, no?

@EwoutH
Copy link
Member

EwoutH commented Aug 19, 2024

Yes I do, but I don't prefer to edit other peoples PRs without their explicit permission :)

@EwoutH
Copy link
Member

EwoutH commented Aug 19, 2024

@rmhopkins4 Could you let me know if you want to take up (any of) the remaining points yourself, or if you want us as maintainers to do it?

@rmhopkins4
Copy link
Contributor Author

I can take up the remaining points. If I understand correctly it's just documentation-related.

@EwoutH
Copy link
Member

EwoutH commented Aug 19, 2024

Perfect, and that’s correct!

Viz tutorial now mentions that in addition to size and color, default drawer allows for custom shape.
@rmhopkins4
Copy link
Contributor Author

Is there a docstring that currently mentions the available options for the agent_portrayal? They seem to just state that agent_portrayal lets you specify agents' display options but not how.

@EwoutH
Copy link
Member

EwoutH commented Aug 19, 2024

I think you’re right, it’s weird we don’t have that documented.

It might have gone lost with the transition from the old to the new visualization.

I think this would be the most logical place to add it:

agent_portrayal: Options for rendering agents (dictionary)

But is also shows how important PR, release and tutorial documentation is - that’s how users discover new features!

@EwoutH
Copy link
Member

EwoutH commented Aug 20, 2024

@rmhopkins4 are you done working on it? It looks good!

@EwoutH EwoutH changed the title Matplotlib shape vizualization agent portrayal Visualisation: Allow specifying Agent shapes in agent_portrayal Aug 20, 2024
@EwoutH
Copy link
Member

EwoutH commented Aug 20, 2024

Here is an example of jailed citizens as squares.

@rmhopkins4 I'm ready to merge. Could you add the portrayal code you used to the first PR post?

@EwoutH EwoutH merged commit 3ca9098 into projectmesa:main Aug 21, 2024
9 of 11 checks passed
@EwoutH EwoutH added the enhancement Release notes label label Aug 21, 2024
@EwoutH
Copy link
Member

EwoutH commented Aug 21, 2024

Congratulations of getting your first PR into Mesa @rmhopkins4!

@EwoutH
Copy link
Member

EwoutH commented Aug 27, 2024

@rmhopkins4 would you like to update a few example models to use the shapes?

If so, this issue includes a list of models already using the new visualization:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Release notes label
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants