Studio#

Modelly Studio, the browser app behind modelly studio. How to use it is in Modelly Studio.

Module

Contents

modelly.studio.config

StudioConfig

modelly.studio.params

Parameter, parameters_of

modelly.studio.loader

load_model_class, load_portrayal

modelly.studio.session

StudioSession

modelly.studio.timeline

GuardedRecorder, ReplayTimeline, track

modelly.studio.data

series, agent table, figures and export files

modelly.studio.app

StudioApp, Page, new_session

What one Studio server shows: the model, its portrayal and the panels’ settings.

The command line builds a StudioConfig and hands it to the Solara app through one environment variable as JSON, so the app module finds it however Solara imports it.

class StudioConfig(target: str, portrayal: str | None = None, seed: int = 42, every: int = 1, profile_steps: int = 50, table_rows: int = 100, memory_budget_mib: int = 256)[source]#

The settings of a Studio session.

Variables:
  • target (str) – path/to/model.py:ModelClass or package.module:ModelClass.

  • portrayal (str | None) – path/to/app.py:function or package.module:function returning an AgentPortrayalStyle per agent; one colour per agent class when None.

  • seed (int) – The seed the first run starts from.

  • every (int) – Steps between two checkpoints of the timeline.

  • profile_steps (int) – How many of the last steps the profiler panel sums.

  • table_rows (int) – Rows the agent table shows.

  • memory_budget_mib (int) – Mebibytes the timeline’s checkpoints may take.

property memory_budget: int#

The checkpoint budget in bytes.

property source_file: Path | None#

The model’s .py file when the target names one, for the lint panel.

to_json() → str[source]#

The config as JSON.

classmethod from_json(text: str) → StudioConfig[source]#

A config from to_json output.

export() → None[source]#

Put the config in the environment, for the app module to read.

classmethod from_environment() → StudioConfig[source]#

The config export put in the environment.

Raises:

RuntimeError – When there is none, i.e. the app was not started by modelly studio.

The parameter panel’s fields, read from a model’s Scenario and constructor.

Every field of the Scenario class the model’s scenario argument defaults to, and every keyword its constructor takes with a default, becomes a Parameter. Its kind picks the widget: bool a checkbox, a Literal or an Enum a select, a float between 0 and 1 a slider, another number a number field, a string a text field. Anything else (a tuple, a dict, None) is shown but cannot be changed from the panel.

class Parameter(name: str, kind: str, default: Any, choices: tuple = ())[source]#

One field of the parameter panel.

Variables:
  • name (str) – The scenario field or constructor keyword.

  • kind (str) – The widget: one of bool, int, float, fraction (a 0 to 1 slider), choice, text or fixed (shown, not editable).

  • default (Any) – The value the model is built with unless it is changed.

  • choices (tuple) – The values a choice offers.

property editable: bool#

Whether the panel lets the value be changed.

kind_of(value: Any, annotation: Any = None) → tuple[str, tuple][source]#

The widget kind for value and, for a choice, the values it offers.

parameters_of(model_class: type[Model]) → list[Parameter][source]#

The panel’s fields for model_class: its constructor’s, then its Scenario’s.

Find the model class and the portrayal a modelly studio command line names.

A path/to/model.py is imported under its package’s dotted name when its folders are packages (modelly/examples/basic/schelling/model.py is modelly.examples.basic.schelling.model), else under its own name with its folder on sys.path. Either way the classes are importable by name, so checkpoints of the model load again, and a model’s from agents import Sheep finds the file next to it.

import_source(source: str) → ModuleType[source]#

Import source: a .py file or a dotted module name.

Raises:

TargetError – When the file does not exist or the module cannot be imported.

load_model_class(target: str) → type[Model][source]#

The Model subclass path/to/model.py:Class or package.module:Class names.

Raises:

TargetError – When it cannot be found or is not a Model subclass.

load_portrayal(target: str) → Callable[source]#

The agent portrayal function path/to/app.py:function names.

Raises:

TargetError – When it cannot be found or is not callable.

module_name(path: Path) → tuple[Path, str][source]#

The folder to put on sys.path and the dotted name to import path by.

One browser tab’s run: build, step, profile, record, scrub and replay a model.

StudioSession holds no widget: the Solara app calls it and redraws from what it holds, and the tests drive it directly. Every method that changes the run takes lock, so the play loop’s thread and the widgets’ callbacks never see a half-made step.

class StudioSession(model_class: type[Model], config: StudioConfig, agent_portrayal: Callable | None = None)[source]#

The state of one Studio run, independent of the widgets that show it.

Parameters:
  • model_class – The model to run.

  • config – The Studio settings.

  • agent_portrayal – How the space view draws agents; one colour per class when None.

Build the first run with the default parameters and config.seed.

property shown: Model#

the scrubbed-to state, else the live model.

Type:

The model the views show

property replaying: bool#

Whether scrubbing reruns the model instead of restoring checkpoints.

say(message: str) → None[source]#

Add a line to the run log.

reset() → None[source]#

Rebuild the model from the panel’s values and seed, at step 0.

step(n: int = 1) → int[source]#

Advance the live model n steps (fewer if it stops); the steps taken.

scrub(step: float) → None[source]#

Show the run at step; at or past the live step, show the live model.

replay() → None[source]#

Make the scrubbed-to state the live run, with the panel’s changed values.

profile_report(top: int = 15) → str[source]#

The profiler’s report over the last config.profile_steps steps.

source_file() → Path | None[source]#

The file the lint panel checks: the target’s, else the class’s source.

lint() → list[modelly.lint.Finding][source]#

The modelly lint findings of the model’s file.

The run Studio’s scrubber moves through: checkpoints when the model allows, else replay.

track records a model with a modelly.timetravel.Recorder: scrubbing restores a checkpoint, instantly. A model that cannot be checkpointed (it holds a lambda, say) gets a ReplayTimeline instead: the same steps / span / at interface, where at rebuilds the model from the start of the run and runs it on to the step. Seeded runs are reproducible, so the state is the same; it only takes longer.

class ReplayTimeline(start: Callable[[], Model], live: Model)[source]#

A timeline for a model that cannot be checkpointed: at reruns from the start.

Parameters:
  • start – Returns a new model in the state the run started from.

  • live – The model being run; the span ends at its current time.

Follow live, which started as start() returns.

property steps: list[float]#

Only the start of the run is held.

property span: tuple[float, float]#

From the start of the run to the live model’s time.

at(step: float) → Model[source]#

A new model at step: the run rebuilt and run on to step.

Raises:

ValueError – If step is outside span.

stop() → None[source]#

Nothing to stop: a replay timeline does not hook the model.

track(model: Model, start: Callable[[], Model], every: float, budget: int) → tuple[Timeline, str | None][source]#

Record model: a Recorder when it can be checkpointed, else a replay.

Returns:

The timeline and, when it is a replay, the reason checkpoints were not possible.

What Studio’s views and exports read from a model: series, agents, figures, files.

Plain functions of a model, so that the views, the exports and the tests share them.

model_series(model: Model) → pd.DataFrame | None[source]#

The numeric model reporters the DataCollector collected, or None.

series_figure(model: Model) → Figure | None[source]#

A line plot of every numeric model reporter, or None when there is none.

agent_classes(model: Model) → list[str][source]#

All and the names of the agent classes present, sorted.

agent_table(model: Model, class_name: str = 'All', rows: int = 100) → pd.DataFrame[source]#

The first rows agents (of class_name) by unique_id, one column per attribute.

figure_bytes(figure: Figure, fmt: str) → bytes[source]#

figure saved as png or svg.

Raises:

ValueError – For another format.

series_image(model: Model, fmt: str = 'png') → bytes | None[source]#

The reporters plot as an image file, or None when nothing was collected.

space_image(session: StudioSession, fmt: str = 'png') → bytes | None[source]#

The space view as shown, as an image file, or None when there is no space.

parquet_available() → bool[source]#

Whether pyarrow is installed, so the data can be exported as Parquet.

data_bytes(model: Model, fmt: str = 'csv') → bytes | None[source]#

The collected model data as csv or parquet, or None when none was collected.

checkpoint_file(model: Model) → bytes[source]#

The model as a Model.load checkpoint.

Raises:

CheckpointError – When the model holds something that cannot be saved.

timeline_steps(span: tuple[float, float]) → list[float][source]#

The steps an animation of span shows: every step, sampled to MAX_FRAMES.

replay_frames(timeline, steps: list[float]) → Iterator[Model][source]#

One model restored at the first step and run on through steps.

animation_bytes(session: StudioSession, suffix: str = '.gif') → bytes | None[source]#

The recorded run’s space view as a GIF or MP4, or None when there is no space.

The Studio page: the layout of the panels, and Page, which the server shows.

StudioApp(session) is the whole app for one session, which the tests render headless. Page is what modelly studio serves: it reads the StudioConfig from the environment, loads the model class once per server, and gives each browser tab a session.

new_session(config: StudioConfig) → StudioSession[source]#

A session for one browser tab.