projectmesa / mesa

Mesa is an open-source Python library for agent-based modeling, ideal for simulating complex systems and exploring emergent behaviors.
https://mesa.readthedocs.io
Apache License 2.0
2.51k stars 879 forks source link

SolaraViz: Use `_get_agent_data` consistently in visualisation of all spaces #2407

Closed EwoutH closed 2 weeks ago

EwoutH commented 3 weeks ago

The Visualize PropertyLayers PR (#2336) added a new internal function _get_agent_data to the visualization/components/matplotlib.py module. This function gathers data from the agent_portrayal dict, while handling synonyms and ommitted data.

Currently only drawing the grid uses it, but ideally, all spaces would use it.

A concrete example, is that currently if you omit "size" in your agent_portrayal dict, you get this non-obvious error:

  File "C:\Users\Ewout\Documents\GitHub\mesa\mesa\visualization\components\matplotlib.py", line 67, in SpaceMatplotlib
    _draw_discrete_space_grid(space, space_ax, agent_portrayal)
  File "C:\Users\Ewout\Documents\GitHub\mesa\mesa\visualization\components\matplotlib.py", line 335, in _draw_discrete_space_grid
    space_ax.scatter(**portray(space))
  File "C:\Users\Ewout\.virtualenvs\Py312\Lib\site-packages\matplotlib\__init__.py", line 1473, in inner
    return func(
           ^^^^^
  File "C:\Users\Ewout\.virtualenvs\Py312\Lib\site-packages\matplotlib\axes\_axes.py", line 4796, in scatter
    raise ValueError(
ValueError: s must be a scalar, or float array-like with the same size as x and y

Adding something like

    portrayal = {
        "s": 25,
        ...

does not fix the issue, since you need to add "size", since we internally translate that.

_get_agent_data does not only also check for "s", but it also includes default values for when it's omitted.

This issue is probably related to:

quaquel commented 3 weeks ago

This interacts with #2401. Currently, the Solara code is reasonable from a user's point of view. However, under the hood, it is a mess. _get_agent_data, for example, mixes getting data from agents in general with how it is to be visualized in an orthogonal grid because it does an offset of 0.5.

            x.append(pos[0] + 0.5)  # Center the agent in the cell
            y.append(pos[1] + 0.5)  # Center the agent in the cell

Moveover, because for agents, pos in space.coord_iter(): this method only works for old style subclasses of _Grid.

I started trying to clean up this mess today and have some notes and code. But basically, a full refactor is needed that cleanly handles the different types of spaces because each has its weird things when plotting with matplotlib. At the same time, there is underlying code for getting data from agents that is useful across all (and Altair plotting as well).

As implicitly alluded to in #2401, depending on which type of space needs plotting, the manner of getting the data is different. For example, Orthogonal Grids, HexGrids, ContinuousSpace, and VoronoiGrid need x,y coordinates. Networks don't. OrthogonalGrids need the 0.5 offsets. HexGrids also need offsets, which are different for odd and even rows.

Next to the data implications, there are also other complexities like drawing grids/hexes for the respective spaces. The Voronoi mesh for VoronoiGrid, and the presence of property layers for certain spaces.

In short: its complicated.

quaquel commented 2 weeks ago

2430 closes this.