Mesa 3.0.0b2__py3-none-any.whl → 3.0.0rc0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of Mesa might be problematic. Click here for more details.

Files changed (28) hide show
  1. mesa/__init__.py +1 -1
  2. mesa/batchrunner.py +26 -1
  3. mesa/examples/README.md +11 -11
  4. mesa/examples/advanced/epstein_civil_violence/agents.py +44 -38
  5. mesa/examples/advanced/epstein_civil_violence/app.py +29 -28
  6. mesa/examples/advanced/epstein_civil_violence/model.py +33 -65
  7. mesa/examples/advanced/pd_grid/app.py +8 -4
  8. mesa/examples/advanced/sugarscape_g1mt/app.py +5 -13
  9. mesa/examples/advanced/wolf_sheep/app.py +25 -18
  10. mesa/examples/basic/boid_flockers/app.py +2 -2
  11. mesa/examples/basic/boltzmann_wealth_model/app.py +14 -10
  12. mesa/examples/basic/conways_game_of_life/app.py +15 -3
  13. mesa/examples/basic/schelling/app.py +5 -5
  14. mesa/examples/basic/virus_on_network/app.py +25 -47
  15. mesa/space.py +0 -30
  16. mesa/visualization/__init__.py +16 -5
  17. mesa/visualization/components/__init__.py +83 -0
  18. mesa/visualization/components/{altair.py → altair_components.py} +34 -2
  19. mesa/visualization/components/matplotlib_components.py +176 -0
  20. mesa/visualization/mpl_space_drawing.py +558 -0
  21. mesa/visualization/solara_viz.py +30 -20
  22. {mesa-3.0.0b2.dist-info → mesa-3.0.0rc0.dist-info}/METADATA +1 -1
  23. {mesa-3.0.0b2.dist-info → mesa-3.0.0rc0.dist-info}/RECORD +27 -25
  24. mesa/visualization/components/matplotlib.py +0 -386
  25. {mesa-3.0.0b2.dist-info → mesa-3.0.0rc0.dist-info}/WHEEL +0 -0
  26. {mesa-3.0.0b2.dist-info → mesa-3.0.0rc0.dist-info}/entry_points.txt +0 -0
  27. {mesa-3.0.0b2.dist-info → mesa-3.0.0rc0.dist-info}/licenses/LICENSE +0 -0
  28. {mesa-3.0.0b2.dist-info → mesa-3.0.0rc0.dist-info}/licenses/NOTICE +0 -0
@@ -1,18 +1,14 @@
1
1
  from mesa.examples.basic.boltzmann_wealth_model.model import BoltzmannWealthModel
2
2
  from mesa.visualization import (
3
3
  SolaraViz,
4
- make_plot_measure,
5
- make_space_matplotlib,
4
+ make_plot_component,
5
+ make_space_component,
6
6
  )
7
7
 
8
8
 
9
9
  def agent_portrayal(agent):
10
- size = 10
11
- color = "tab:red"
12
- if agent.wealth > 0:
13
- size = 50
14
- color = "tab:blue"
15
- return {"size": size, "color": color}
10
+ color = agent.wealth # we are using a colormap to translate wealth to color
11
+ return {"color": color}
16
12
 
17
13
 
18
14
  model_params = {
@@ -28,6 +24,11 @@ model_params = {
28
24
  "height": 10,
29
25
  }
30
26
 
27
+
28
+ def post_process(ax):
29
+ ax.get_figure().colorbar(ax.collections[0], label="wealth", ax=ax)
30
+
31
+
31
32
  # Create initial model instance
32
33
  model1 = BoltzmannWealthModel(50, 10, 10)
33
34
 
@@ -36,8 +37,11 @@ model1 = BoltzmannWealthModel(50, 10, 10)
36
37
  # Under the hood these are just classes that receive the model instance.
37
38
  # You can also author your own visualization elements, which can also be functions
38
39
  # that receive the model instance and return a valid solara component.
39
- SpaceGraph = make_space_matplotlib(agent_portrayal)
40
- GiniPlot = make_plot_measure("Gini")
40
+
41
+ SpaceGraph = make_space_component(
42
+ agent_portrayal, cmap="viridis", vmin=0, vmax=10, post_process=post_process
43
+ )
44
+ GiniPlot = make_plot_component("Gini")
41
45
 
42
46
  # Create the SolaraViz page. This will automatically create a server and display the
43
47
  # visualization elements in a web browser.
@@ -1,12 +1,22 @@
1
1
  from mesa.examples.basic.conways_game_of_life.model import ConwaysGameOfLife
2
2
  from mesa.visualization import (
3
3
  SolaraViz,
4
- make_space_matplotlib,
4
+ make_space_component,
5
5
  )
6
6
 
7
7
 
8
8
  def agent_portrayal(agent):
9
- return {"color": "white" if agent.state == 0 else "black"}
9
+ return {
10
+ "color": "white" if agent.state == 0 else "black",
11
+ "marker": "s",
12
+ "size": 25,
13
+ }
14
+
15
+
16
+ def post_process(ax):
17
+ ax.set_aspect("equal")
18
+ ax.set_xticks([])
19
+ ax.set_yticks([])
10
20
 
11
21
 
12
22
  model_params = {
@@ -22,7 +32,9 @@ model1 = ConwaysGameOfLife(50, 50)
22
32
  # Under the hood these are just classes that receive the model instance.
23
33
  # You can also author your own visualization elements, which can also be functions
24
34
  # that receive the model instance and return a valid solara component.
25
- SpaceGraph = make_space_matplotlib(agent_portrayal)
35
+ SpaceGraph = make_space_component(
36
+ agent_portrayal, post_process=post_process, draw_grid=False
37
+ )
26
38
 
27
39
 
28
40
  # Create the SolaraViz page. This will automatically create a server and display the
@@ -4,8 +4,8 @@ from mesa.examples.basic.schelling.model import Schelling
4
4
  from mesa.visualization import (
5
5
  Slider,
6
6
  SolaraViz,
7
- make_plot_measure,
8
- make_space_matplotlib,
7
+ make_plot_component,
8
+ make_space_component,
9
9
  )
10
10
 
11
11
 
@@ -28,13 +28,13 @@ model_params = {
28
28
 
29
29
  model1 = Schelling(20, 20, 0.8, 0.2, 3)
30
30
 
31
- HappyPlot = make_plot_measure("happy")
31
+ HappyPlot = make_plot_component({"happy": "tab:green"})
32
32
 
33
33
  page = SolaraViz(
34
34
  model1,
35
35
  components=[
36
- make_space_matplotlib(agent_portrayal),
37
- make_plot_measure("happy"),
36
+ make_space_component(agent_portrayal),
37
+ HappyPlot,
38
38
  get_happy_agents,
39
39
  ],
40
40
  model_params=model_params,
@@ -1,44 +1,27 @@
1
1
  import math
2
2
 
3
3
  import solara
4
- from matplotlib.figure import Figure
5
- from matplotlib.ticker import MaxNLocator
6
4
 
7
5
  from mesa.examples.basic.virus_on_network.model import (
8
6
  State,
9
7
  VirusOnNetwork,
10
8
  number_infected,
11
9
  )
12
- from mesa.visualization import Slider, SolaraViz, make_space_matplotlib
13
-
10
+ from mesa.visualization import (
11
+ Slider,
12
+ SolaraViz,
13
+ make_plot_component,
14
+ make_space_component,
15
+ )
14
16
 
15
- def agent_portrayal(graph):
16
- def get_agent(node):
17
- return graph.nodes[node]["agent"][0]
18
17
 
19
- edge_width = []
20
- edge_color = []
21
- for u, v in graph.edges():
22
- agent1 = get_agent(u)
23
- agent2 = get_agent(v)
24
- w = 2
25
- ec = "#e8e8e8"
26
- if State.RESISTANT in (agent1.state, agent2.state):
27
- w = 3
28
- ec = "black"
29
- edge_width.append(w)
30
- edge_color.append(ec)
18
+ def agent_portrayal(agent):
31
19
  node_color_dict = {
32
20
  State.INFECTED: "tab:red",
33
21
  State.SUSCEPTIBLE: "tab:green",
34
22
  State.RESISTANT: "tab:gray",
35
23
  }
36
- node_color = [node_color_dict[get_agent(node).state] for node in graph.nodes()]
37
- return {
38
- "width": edge_width,
39
- "edge_color": edge_color,
40
- "node_color": node_color,
41
- }
24
+ return {"color": node_color_dict[agent.state], "size": 10}
42
25
 
43
26
 
44
27
  def get_resistant_susceptible_ratio(model):
@@ -46,25 +29,9 @@ def get_resistant_susceptible_ratio(model):
46
29
  ratio_text = r"$\infty$" if ratio is math.inf else f"{ratio:.2f}"
47
30
  infected_text = str(number_infected(model))
48
31
 
49
- return f"Resistant/Susceptible Ratio: {ratio_text}<br>Infected Remaining: {infected_text}"
50
-
51
-
52
- def make_plot(model):
53
- # This is for the case when we want to plot multiple measures in 1 figure.
54
- fig = Figure()
55
- ax = fig.subplots()
56
- measures = ["Infected", "Susceptible", "Resistant"]
57
- colors = ["tab:red", "tab:green", "tab:gray"]
58
- for i, m in enumerate(measures):
59
- color = colors[i]
60
- df = model.datacollector.get_model_vars_dataframe()
61
- ax.plot(df.loc[:, m], label=m, color=color)
62
- fig.legend()
63
- # Set integer x axis
64
- ax.xaxis.set_major_locator(MaxNLocator(integer=True))
65
- ax.set_xlabel("Step")
66
- ax.set_ylabel("Number of Agents")
67
- return solara.FigureMatplotlib(fig)
32
+ return solara.Markdown(
33
+ f"Resistant/Susceptible Ratio: {ratio_text}<br>Infected Remaining: {infected_text}"
34
+ )
68
35
 
69
36
 
70
37
  model_params = {
@@ -119,7 +86,18 @@ model_params = {
119
86
  ),
120
87
  }
121
88
 
122
- SpacePlot = make_space_matplotlib(agent_portrayal)
89
+
90
+ def post_process_lineplot(ax):
91
+ ax.set_ylim(ymin=0)
92
+ ax.set_ylabel("# people")
93
+ ax.legend(bbox_to_anchor=(1.05, 1.0), loc="upper left")
94
+
95
+
96
+ SpacePlot = make_space_component(agent_portrayal)
97
+ StatePlot = make_plot_component(
98
+ {"Infected": "tab:red", "Susceptible": "tab:green", "Resistant": "tab:gray"},
99
+ post_process=post_process_lineplot,
100
+ )
123
101
 
124
102
  model1 = VirusOnNetwork()
125
103
 
@@ -127,8 +105,8 @@ page = SolaraViz(
127
105
  model1,
128
106
  [
129
107
  SpacePlot,
130
- make_plot,
131
- # get_resistant_susceptible_ratio, # TODO: Fix and uncomment
108
+ StatePlot,
109
+ get_resistant_susceptible_ratio,
132
110
  ],
133
111
  model_params=model_params,
134
112
  name="Virus Model",
mesa/space.py CHANGED
@@ -1287,36 +1287,6 @@ class HexMultiGrid(_HexGrid, MultiGrid):
1287
1287
  """
1288
1288
 
1289
1289
 
1290
- class HexGrid(HexSingleGrid):
1291
- """Hexagonal Grid: a Grid where neighbors are computed according to a hexagonal tiling of the grid.
1292
-
1293
- Functions according to odd-q rules.
1294
- See http://www.redblobgames.com/grids/hexagons/#coordinates for more.
1295
-
1296
- Properties:
1297
- width, height: The grid's width and height.
1298
- torus: Boolean which determines whether to treat the grid as a torus.
1299
- """
1300
-
1301
- def __init__(self, width: int, height: int, torus: bool) -> None:
1302
- """Initializes a HexGrid, deprecated.
1303
-
1304
- Args:
1305
- width: the width of the grid
1306
- height: the height of the grid
1307
- torus: whether the grid wraps
1308
- """
1309
- super().__init__(width, height, torus)
1310
- warn(
1311
- (
1312
- "HexGrid is being deprecated; use instead HexSingleGrid or HexMultiGrid "
1313
- "depending on your use case."
1314
- ),
1315
- DeprecationWarning,
1316
- stacklevel=2,
1317
- )
1318
-
1319
-
1320
1290
  class ContinuousSpace:
1321
1291
  """Continuous space where each agent can have an arbitrary position.
1322
1292
 
@@ -1,7 +1,17 @@
1
- """Solara based visualization for Mesa models."""
1
+ """Solara based visualization for Mesa models.
2
2
 
3
- from .components.altair import make_space_altair
4
- from .components.matplotlib import make_plot_measure, make_space_matplotlib
3
+ .. note::
4
+ SolaraViz is experimental and still in active development for Mesa 3.0. While we attempt to minimize them, there might be API breaking changes between Mesa 3.0 and 3.1.
5
+
6
+ There won't be breaking changes between Mesa 3.0.x patch releases.
7
+ """
8
+
9
+ from mesa.visualization.mpl_space_drawing import (
10
+ draw_space,
11
+ )
12
+
13
+ from .components import make_plot_component, make_space_component
14
+ from .components.altair_components import make_space_altair
5
15
  from .solara_viz import JupyterViz, SolaraViz
6
16
  from .UserParam import Slider
7
17
 
@@ -10,6 +20,7 @@ __all__ = [
10
20
  "SolaraViz",
11
21
  "Slider",
12
22
  "make_space_altair",
13
- "make_space_matplotlib",
14
- "make_plot_measure",
23
+ "draw_space",
24
+ "make_plot_component",
25
+ "make_space_component",
15
26
  ]
@@ -0,0 +1,83 @@
1
+ """custom solara components."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+
7
+ from .altair_components import SpaceAltair, make_altair_space
8
+ from .matplotlib_components import (
9
+ SpaceMatplotlib,
10
+ make_mpl_plot_component,
11
+ make_mpl_space_component,
12
+ )
13
+
14
+
15
+ def make_space_component(
16
+ agent_portrayal: Callable | None = None,
17
+ propertylayer_portrayal: dict | None = None,
18
+ post_process: Callable | None = None,
19
+ backend: str = "matplotlib",
20
+ **space_drawing_kwargs,
21
+ ) -> SpaceMatplotlib | SpaceAltair:
22
+ """Create a Matplotlib-based space visualization component.
23
+
24
+ Args:
25
+ agent_portrayal: Function to portray agents.
26
+ propertylayer_portrayal: Dictionary of PropertyLayer portrayal specifications
27
+ post_process : a callable that will be called with the Axes instance. Allows for fine-tuning plots (e.g., control ticks)
28
+ backend: the backend to use {"matplotlib", "altair"}
29
+ space_drawing_kwargs : additional keyword arguments to be passed on to the underlying backend specific space drawer function. See
30
+ the functions for drawing the various spaces for the appropriate backend further details.
31
+
32
+
33
+ Returns:
34
+ function: A function that creates a space component
35
+ """
36
+ if backend == "matplotlib":
37
+ return make_mpl_space_component(
38
+ agent_portrayal,
39
+ propertylayer_portrayal,
40
+ post_process,
41
+ **space_drawing_kwargs,
42
+ )
43
+ elif backend == "altair":
44
+ return make_altair_space(
45
+ agent_portrayal,
46
+ propertylayer_portrayal,
47
+ post_process,
48
+ **space_drawing_kwargs,
49
+ )
50
+ else:
51
+ raise ValueError(
52
+ f"unknown backend {backend}, must be one of matplotlib, altair"
53
+ )
54
+
55
+
56
+ def make_plot_component(
57
+ measure: str | dict[str, str] | list[str] | tuple[str],
58
+ post_process: Callable | None = None,
59
+ backend: str = "matplotlib",
60
+ **plot_drawing_kwargs,
61
+ ):
62
+ """Create a plotting function for a specified measure using the specified backend.
63
+
64
+ Args:
65
+ measure (str | dict[str, str] | list[str] | tuple[str]): Measure(s) to plot.
66
+ post_process: a user-specified callable to do post-processing called with the Axes instance.
67
+ backend: the backend to use {"matplotlib", "altair"}
68
+ plot_drawing_kwargs: additional keyword arguments to pass onto the backend specific function for making a plotting component
69
+
70
+ Notes:
71
+ altair plotting backend is not yet implemented and planned for mesa 3.1.
72
+
73
+ Returns:
74
+ function: A function that creates a plot component
75
+ """
76
+ if backend == "matplotlib":
77
+ return make_mpl_plot_component(measure, post_process, **plot_drawing_kwargs)
78
+ elif backend == "altair":
79
+ raise NotImplementedError("altair line plots are not yet implemented")
80
+ else:
81
+ raise ValueError(
82
+ f"unknown backend {backend}, must be one of matplotlib, altair"
83
+ )
@@ -1,6 +1,7 @@
1
1
  """Altair based solara components for visualization mesa spaces."""
2
2
 
3
3
  import contextlib
4
+ import warnings
4
5
 
5
6
  import solara
6
7
 
@@ -12,7 +13,33 @@ from mesa.space import ContinuousSpace, _Grid
12
13
  from mesa.visualization.utils import update_counter
13
14
 
14
15
 
15
- def make_space_altair(agent_portrayal=None): # noqa: D103
16
+ def make_space_altair(*args, **kwargs): # noqa: D103
17
+ warnings.warn(
18
+ "make_space_altair has been renamed to make_altair_space",
19
+ DeprecationWarning,
20
+ stacklevel=2,
21
+ )
22
+ return make_altair_space(*args, **kwargs)
23
+
24
+
25
+ def make_altair_space(
26
+ agent_portrayal, propertylayer_portrayal, post_process, **space_drawing_kwargs
27
+ ):
28
+ """Create an Altair-based space visualization component.
29
+
30
+ Args:
31
+ agent_portrayal: Function to portray agents.
32
+ propertylayer_portrayal: not yet implemented
33
+ post_process :not yet implemented
34
+ space_drawing_kwargs : not yet implemented
35
+
36
+ ``agent_portrayal`` is called with an agent and should return a dict. Valid fields in this dict are "color",
37
+ "size", "marker", and "zorder". Other field are ignored and will result in a user warning.
38
+
39
+
40
+ Returns:
41
+ function: A function that creates a SpaceMatplotlib component
42
+ """
16
43
  if agent_portrayal is None:
17
44
 
18
45
  def agent_portrayal(a):
@@ -25,7 +52,12 @@ def make_space_altair(agent_portrayal=None): # noqa: D103
25
52
 
26
53
 
27
54
  @solara.component
28
- def SpaceAltair(model, agent_portrayal, dependencies: list[any] | None = None): # noqa: D103
55
+ def SpaceAltair(model, agent_portrayal, dependencies: list[any] | None = None):
56
+ """Create an Altair-based space visualization component.
57
+
58
+ Returns:
59
+ a solara FigureAltair instance
60
+ """
29
61
  update_counter.get()
30
62
  space = getattr(model, "grid", None)
31
63
  if space is None:
@@ -0,0 +1,176 @@
1
+ """Matplotlib based solara components for visualization MESA spaces and plots."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import warnings
6
+ from collections.abc import Callable
7
+
8
+ import matplotlib.pyplot as plt
9
+ import solara
10
+ from matplotlib.figure import Figure
11
+
12
+ from mesa.visualization.mpl_space_drawing import draw_space
13
+ from mesa.visualization.utils import update_counter
14
+
15
+
16
+ def make_space_matplotlib(*args, **kwargs): # noqa: D103
17
+ warnings.warn(
18
+ "make_space_matplotlib has been renamed to make_mpl_space_component",
19
+ DeprecationWarning,
20
+ stacklevel=2,
21
+ )
22
+ return make_mpl_space_component(*args, **kwargs)
23
+
24
+
25
+ def make_mpl_space_component(
26
+ agent_portrayal: Callable | None = None,
27
+ propertylayer_portrayal: dict | None = None,
28
+ post_process: Callable | None = None,
29
+ **space_drawing_kwargs,
30
+ ) -> SpaceMatplotlib:
31
+ """Create a Matplotlib-based space visualization component.
32
+
33
+ Args:
34
+ agent_portrayal: Function to portray agents.
35
+ propertylayer_portrayal: Dictionary of PropertyLayer portrayal specifications
36
+ post_process : a callable that will be called with the Axes instance. Allows for fine tuning plots (e.g., control ticks)
37
+ space_drawing_kwargs : additional keyword arguments to be passed on to the underlying space drawer function. See
38
+ the functions for drawing the various spaces for further details.
39
+
40
+ ``agent_portrayal`` is called with an agent and should return a dict. Valid fields in this dict are "color",
41
+ "size", "marker", and "zorder". Other field are ignored and will result in a user warning.
42
+
43
+
44
+ Returns:
45
+ function: A function that creates a SpaceMatplotlib component
46
+ """
47
+ if agent_portrayal is None:
48
+
49
+ def agent_portrayal(a):
50
+ return {}
51
+
52
+ def MakeSpaceMatplotlib(model):
53
+ return SpaceMatplotlib(
54
+ model,
55
+ agent_portrayal,
56
+ propertylayer_portrayal,
57
+ post_process=post_process,
58
+ **space_drawing_kwargs,
59
+ )
60
+
61
+ return MakeSpaceMatplotlib
62
+
63
+
64
+ @solara.component
65
+ def SpaceMatplotlib(
66
+ model,
67
+ agent_portrayal,
68
+ propertylayer_portrayal,
69
+ dependencies: list[any] | None = None,
70
+ post_process: Callable | None = None,
71
+ **space_drawing_kwargs,
72
+ ):
73
+ """Create a Matplotlib-based space visualization component."""
74
+ update_counter.get()
75
+
76
+ space = getattr(model, "grid", None)
77
+ if space is None:
78
+ space = getattr(model, "space", None)
79
+
80
+ fig = Figure()
81
+ ax = fig.add_subplot()
82
+
83
+ draw_space(
84
+ space,
85
+ agent_portrayal,
86
+ propertylayer_portrayal=propertylayer_portrayal,
87
+ ax=ax,
88
+ **space_drawing_kwargs,
89
+ )
90
+
91
+ if post_process is not None:
92
+ post_process(ax)
93
+
94
+ solara.FigureMatplotlib(
95
+ fig, format="png", bbox_inches="tight", dependencies=dependencies
96
+ )
97
+
98
+
99
+ def make_plot_measure(*args, **kwargs): # noqa: D103
100
+ warnings.warn(
101
+ "make_plot_measure has been renamed to make_plot_component",
102
+ DeprecationWarning,
103
+ stacklevel=2,
104
+ )
105
+ return make_mpl_plot_component(*args, **kwargs)
106
+
107
+
108
+ def make_mpl_plot_component(
109
+ measure: str | dict[str, str] | list[str] | tuple[str],
110
+ post_process: Callable | None = None,
111
+ save_format="png",
112
+ ):
113
+ """Create a plotting function for a specified measure.
114
+
115
+ Args:
116
+ measure (str | dict[str, str] | list[str] | tuple[str]): Measure(s) to plot.
117
+ post_process: a user-specified callable to do post-processing called with the Axes instance.
118
+ save_format: save format of figure in solara backend
119
+
120
+ Returns:
121
+ function: A function that creates a PlotMatplotlib component.
122
+ """
123
+
124
+ def MakePlotMatplotlib(model):
125
+ return PlotMatplotlib(
126
+ model, measure, post_process=post_process, save_format=save_format
127
+ )
128
+
129
+ return MakePlotMatplotlib
130
+
131
+
132
+ @solara.component
133
+ def PlotMatplotlib(
134
+ model,
135
+ measure,
136
+ dependencies: list[any] | None = None,
137
+ post_process: Callable | None = None,
138
+ save_format="png",
139
+ ):
140
+ """Create a Matplotlib-based plot for a measure or measures.
141
+
142
+ Args:
143
+ model (mesa.Model): The model instance.
144
+ measure (str | dict[str, str] | list[str] | tuple[str]): Measure(s) to plot.
145
+ dependencies (list[any] | None): Optional dependencies for the plot.
146
+ post_process: a user-specified callable to do post-processing called with the Axes instance.
147
+ save_format: format used for saving the figure.
148
+
149
+ Returns:
150
+ solara.FigureMatplotlib: A component for rendering the plot.
151
+ """
152
+ update_counter.get()
153
+ fig = Figure()
154
+ ax = fig.subplots()
155
+ df = model.datacollector.get_model_vars_dataframe()
156
+ if isinstance(measure, str):
157
+ ax.plot(df.loc[:, measure])
158
+ ax.set_ylabel(measure)
159
+ elif isinstance(measure, dict):
160
+ for m, color in measure.items():
161
+ ax.plot(df.loc[:, m], label=m, color=color)
162
+ ax.legend(loc="best")
163
+ elif isinstance(measure, list | tuple):
164
+ for m in measure:
165
+ ax.plot(df.loc[:, m], label=m)
166
+ ax.legend(loc="best")
167
+
168
+ if post_process is not None:
169
+ post_process(ax)
170
+
171
+ ax.set_xlabel("Step")
172
+ # Set integer x axis
173
+ ax.xaxis.set_major_locator(plt.MaxNLocator(integer=True))
174
+ solara.FigureMatplotlib(
175
+ fig, format=save_format, bbox_inches="tight", dependencies=dependencies
176
+ )