Benchmarking Running Experiments¶
This tutorial shows how to design and run reproducible experiments that compare solvers across multiple scenes and parameter configurations. You’ll learn to collect structured results, save them as CSV files, and process large batches efficiently.
Goal¶
By the end of this tutorial, you will:
Design experiment scenarios (solver + scene + parameters).
Run batch experiments and collect results.
Use CSV output for analysis and performance comparison.
Scale experiments across multiple chunks for parallel processing.
What is an experiment?¶
An experiment is a repeatable comparison of one or more solvers on one or more scenes. Instead of manually clicking “Solve” in the Solver Viewer for each combination, you define a list of scenarios — each one specifies a solver, a scene, and parameter settings — and the framework runs them all, collects metrics, and saves the results to CSV.
This is essential when you want to:
Compare two solvers on the same scene.
Test parameter sensitivity (e.g., does increasing
num_landmarksactually help?).Run the same benchmark across different hardware.
Generate publication-ready data tables.
Understanding scenarios¶
A Scenario is a single experiment case that combines:
A solver class or instance.
A scene file (JSON path).
A dictionary of solver parameters.
When you run a batch of scenarios, each one executes independently, and the results are stored in separate CSV files.
Default metrics recorded¶
Every scenario automatically records:
total_path_length— sum of all robot path lengths in the solution.makespan— total time taken by the solution (maximum time of any single robot).calc_time— wall-clock time spent computing the solution.
You can add custom metrics via result handlers.
Custom result handlers¶
Sometimes you need measurements beyond the defaults. Use result handlers to compute any metric you want from the solver’s roadmap or path collection.
Each handler is a function that receives:
The path collection (solution).
The solver object (so you can access internal state like roadmap size).
It should return a single numeric value.
Common examples:
Roadmap size (number of nodes or edges).
Graph density or connectivity measures.
Distance from start to goal in the roadmap.
Step 1: Create a scenarios file¶
Start by creating a Python file that defines your experiments. Here’s a minimal example:
from discopygal.experiments.scenarios_runner import Scenario
from discopygal.solvers.rrt.drrt_star import dRRT_star
SCENARIOS = [
Scenario(
dRRT_star,
"examples/scenes/2_discs_corridor.json",
{"num_landmarks": 150}
),
]
That’s it! One scenario: run dRRT_star on the corridor scene with 150 landmarks.
Scaling up: multiple scenes and parameters¶
For a real benchmark, you probably want to test many combinations. Use itertools.product
to create the cross-product of solvers, scenes, and parameters:
import itertools
from discopygal.experiments.scenarios_runner import Scenario
from discopygal.solvers.rrt.drrt_star import dRRT_star
from discopygal.solvers.rrt.rrt_star import RRT_star
# Define parameter values to test
landmark_counts = [50, 100, 150, 200]
scenes = [
"examples/scenes/2_discs_corridor.json",
"examples/scenes/2_pocket_maze.json",
"examples/scenes/coffee_shop/coffee_shop.json",
]
solvers = [dRRT_star, RRT_star]
# Create all combinations
SCENARIOS = [
Scenario(solver, scene, {"num_landmarks": landmarks})
for solver, scene, landmarks in itertools.product(solvers, scenes, landmark_counts)
]
This creates 2 solvers × 3 scenes × 4 parameter values = 24 scenarios.
Adding custom metrics¶
Extend your scenarios file with a RESULT_HANDLERS dictionary:
RESULT_HANDLERS = {
"roadmap_nodes": lambda path_collection, solver: len(solver.roadmap.nodes),
"roadmap_edges": lambda path_collection, solver: len(solver.roadmap.edges),
"solution_robots": lambda path_collection, solver: len(path_collection.paths),
}
These handlers run after every scenario and their results are added to the CSV output.
Real-world example¶
Here’s a complete, realistic scenarios file:
import itertools
from discopygal.experiments.scenarios_runner import Scenario
from discopygal.solvers.rrt.drrt_star import dRRT_star
# Test different parameter configurations
random_sample_counters = [0, 1, 10, 20, 50, 100]
landmark_counts = [100, 500, 1000]
scenes = [
"examples/scenes/tunnels_disc.json",
"examples/scenes/2_discs_corridor.json",
"examples/scenes/2_pocket_maze_tight.json",
]
SCENARIOS = [
Scenario(
dRRT_star,
scene,
{
"prm_num_landmarks": landmarks,
"num_landmarks": 100,
"random_sample_counter": random_sample_counter,
},
)
for random_sample_counter, scene, landmarks in itertools.product(
random_sample_counters, scenes, landmark_counts
)
]
RESULT_HANDLERS = {
"roadmap_size": lambda _, solver: len(solver.roadmap.nodes),
"solution_is_optimal": lambda paths, _: 1 if paths.is_optimal() else 0,
}
This creates 6 × 3 × 3 = 54 scenarios. When run, it produces detailed CSV results showing how the random sampling counter affects planning performance.
Step 2: Run scenarios from Python¶
If you’re working in a Jupyter notebook or a standalone script, run experiments directly:
from discopygal.experiments.scenarios_runner import run_scenarios
from my_scenarios import SCENARIOS, RESULT_HANDLERS
run_scenarios(
SCENARIOS,
result_root_dir="results",
result_handlers=RESULT_HANDLERS
)
This creates a results/ directory with timestamped subdirectories, each containing:
all.csv— Summary of all scenarios.Per-scenario result files.
Log files with solver output.
Step 3: Run from the command line¶
The CLI entry point is scenarios_runner. First, activate your environment, then:
scenarios_runner results my_scenarios.py
scenarios_runner.exe results my_scenarios.py
python src/discopygal/experiments/run_experiment.py results my_scenarios.py
This runs all scenarios defined in my_scenarios.py and saves results to the results/ directory.
Step 4: Advanced workload management¶
Resuming interrupted runs¶
If an experiment crashes halfway through, resume it without re-running completed scenarios:
scenarios_runner results my_scenarios.py resume
The runner skips scenarios that already have output files and continues from where it left off.
Splitting large experiments across machines¶
If you have 1000 scenarios and only one machine, break the work into chunks and run them in parallel:
# Split into 10 chunks, run chunk 3 on this machine
scenarios_runner results my_scenarios.py 10 3
# (On other machines, run chunks 1, 2, 4, 5, ..., 10)
Each machine produces results in its own subdirectory.
Merging chunk results¶
After all chunks finish, merge them into a single results directory:
scenarios_runner results my_scenarios.py 10 end
This consolidates all chunk CSV files into one all.csv summary.
Understanding output¶
CSV structure¶
Each run creates an all.csv with one row per scenario. Columns include:
scenario_id— unique identifier for this scenario.solver— solver class name.scene— path to the scene file.total_path_length,makespan,calc_time— default metrics.Custom handler columns (e.g.,
roadmap_size) if you defined them.
You can load this in Pandas, Excel, or any analysis tool:
import pandas as pd
results = pd.read_csv("results/20240427_103020/all.csv")
print(results.groupby("solver")["calc_time"].mean()) # average compute time per solver
Tips and tricks¶
Start small: Run 5–10 scenarios first to catch configuration errors before launching 1000.
Parameter sweeps: Use
itertools.productto explore high-dimensional parameter spaces systematically.Named scenarios: Add a
namefield to scenarios for clearer CSV output (if supported by your version).Version control: Commit your scenarios file to git so results are always reproducible.
Parallel runs: Use the chunk mode on multiple machines or CPU cores to speed up large experiments.