Experiments

The experiments interface allows the user to easily run and compare a number of different scenarios.

import passengersim as pax

pax.versions()
passengersim 0.80
passengersim.core 0.80

The experimentation starts with a base config.

cfg = pax.Config.from_yaml(pax.demo_network("3MKT/08-untrunc-em"))
cfg.simulation_controls.num_trials = 1

Then, we create an Experiments object, which will manage our set of experiments somewhat automatically. This object takes our baseline config as an argument, as well as allows us to set a directory where all experimental output will be stored.

from passengersim.experiments import Experiments

experiments = Experiments(cfg, output_dir="demo-output")

Now we can define one or more experiments, although as you’ll see quickly, the real power of this interface comes when there is more than one experiment.

Each experiment is defined by a function that accepts a Config as an argument, and returns a possibly modified Config. Within this function, you can make any modifications desired: changing simulation controls, swapping out revenue management systems for one or more carriers, or even changing the network structure itself. Each experiment function is prefixed by the experiments object as decorator, and each should have a unique function name that will be used to identify it.

@experiments
def baseline(cfg: pax.Config) -> pax.Config:
    return cfg


@experiments
def low_dmd(cfg: pax.Config) -> pax.Config:
    cfg.simulation_controls.demand_multiplier = 0.9
    return cfg


@experiments
def high_dmd(cfg):
    cfg.simulation_controls.demand_multiplier = 1.1
    return cfg

Finally, we can run all the experiments as a batch using the run command on the Experiments object.

summaries = experiments.run()
   ╭─────────────────────────────────────╮                                                                         
   │ licensed for jpn                    │                                                                         
   │ until 2036-05-28 15:49:32+00:00 UTC │                                                                         
   │ maximum 42000 legs in network       │                                                                         
   ╰─────────────────────────────────────╯                                                                         
database filename ':memory:' is not supported, using temporary file-based database at /var/folders/sb/mb_3f0t16lx0wm3tr8r5g9rm0000gn/T/tmp6pd3vub_/passengersim_temp_db.sqlite
database filename ':memory:' is not supported, using temporary file-based database at /var/folders/sb/mb_3f0t16lx0wm3tr8r5g9rm0000gn/T/tmpq2jv5kng/passengersim_temp_db.sqlite
database filename ':memory:' is not supported, using temporary file-based database at /var/folders/sb/mb_3f0t16lx0wm3tr8r5g9rm0000gn/T/tmpciri2987/passengersim_temp_db.sqlite

The return value from this batch run is a passengersim Contrast object, which can be used to review the results interactively in a Jupyter notebook.

summaries.fig_carrier_revenues()
summaries.fig_fare_class_mix()

When we ran the experiments, the demo-output directory was populated with outputs from each experiment, including a pickle file storing the summary results, as well as an HTML output file that includes key figures and metadata describing the results from that experiment.

from passengersim.utils.show_dir import display_directory_contents

display_directory_contents("demo-output")
demo-output/
  high_dmd/
    high_dmd.20260612-141012.pxsim
    passengersim_output.20260612-141012.html
  low_dmd/
    low_dmd.20260612-141012.pxsim
    passengersim_output.20260612-141012.html
  baseline/
    baseline.20260612-141012.pxsim
    passengersim_output.20260612-141012.html
  experiments-summary.20260612-141018.html

We can change an existing experiment explicitly by writing a new experiment with the same tag, or implicitly by editing the Jupyter notebook and re-running the entire notebook. Here, we will just edit one experiment by overwriting it (note we do get a warning when we do this).

@experiments
def high_dmd(cfg):  # noqa: F811
    cfg.simulation_controls.demand_multiplier = 1.2
    return cfg
/var/folders/sb/mb_3f0t16lx0wm3tr8r5g9rm0000gn/T/ipykernel_14591/1986293878.py:1: OverwriteExperimentWarning: Overwriting existing experiment tag: high_dmd
  @experiments

If we now re-run the set of experiments, PassengerSim will detect that some of the experiments are have been run already, and not re-run them in favor of simply reloading from disk. The loaded results configurations are compared against the experiment configuration, to confirm it is still the same. For the first two experiments, this is the case and the simulation is not re-run. The change we made in the last experiment is detected, and the loaded results are then discarded in favor of re-running the simulation.

summaries2 = experiments.run()
Using cached results for experiment baseline
Using cached results for experiment low_dmd
Loaded high_dmd from demo-output/high_dmd/high_dmd.20260612-141012.pxsim, but the config has changed:
{'simulation_controls': {'demand_multiplier': '1.2 != 1.1'}}
database filename ':memory:' is not supported, using temporary file-based database at /var/folders/sb/mb_3f0t16lx0wm3tr8r5g9rm0000gn/T/tmp0vrlttz0/passengersim_temp_db.sqlite

summaries2.fig_carrier_revenues()
summaries2.fig_fare_class_mix()

If we inspect the demo-output directory, we will see that the results from the old experiment are still available if needed, but they are timestamped so we can clearly identify them as older.

display_directory_contents("demo-output")
demo-output/
  high_dmd/
    high_dmd.20260612-141012.pxsim
    passengersim_output.20260612-141018.html
    high_dmd.20260612-141018.pxsim
    passengersim_output.20260612-141012.html
  low_dmd/
    low_dmd.20260612-141012.pxsim
    passengersim_output.20260612-141012.html
  baseline/
    baseline.20260612-141012.pxsim
    passengersim_output.20260612-141012.html
  experiments-summary.20260612-141023.html
  experiments-summary.20260612-141018.html

We can write out a report of the experiments, which contains a variety of standard outputs.

out_file = summaries2.write_report(
    "demo-output/meta-summary.html", base_config=cfg
)
from passengersim.utils.iframe import preview_html

preview_html(out_file)