{
"cells": [
{
"cell_type": "markdown",
"id": "fc622bc9",
"metadata": {},
"source": [
"# Benchmark Summary Notebook\n",
"\n",
"Summarise solver performance across multiple benchmark scenarios."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "abdcbd8e",
"metadata": {
"execution": {
"iopub.execute_input": "2026-10-05T12:57:40.320317Z",
"iopub.status.busy": "2026-10-05T12:57:40.320162Z",
"iopub.status.idle": "2026-10-05T12:57:40.510490Z",
"shell.execute_reply": "2026-10-05T12:57:40.509545Z"
}
},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" scenario | \n",
" scenario_path | \n",
" solver | \n",
" objective | \n",
" runtime_s | \n",
" assignments | \n",
" kpi_total_production | \n",
" kpi_completed_blocks | \n",
" kpi_mobilisation_cost | \n",
" kpi_mobilisation_cost_by_machine | \n",
" ... | \n",
" scenario_key | \n",
" objective_vs_mip_gap | \n",
" objective_vs_mip_ratio | \n",
" solver_category | \n",
" best_heuristic_solver | \n",
" best_heuristic_objective | \n",
" best_heuristic_runtime_s | \n",
" objective_gap_vs_best_heuristic | \n",
" runtime_ratio_vs_best_heuristic | \n",
" kpi_utilisation_ratio_by_role | \n",
"
\n",
" \n",
" \n",
" \n",
" | 0 | \n",
" user-1 | \n",
" examples/tiny7/scenario.yaml | \n",
" sa | \n",
" 13.000 | \n",
" 0.020893 | \n",
" 16 | \n",
" 45.500 | \n",
" 1.0 | \n",
" 65.0 | \n",
" {\"H2\": 65.0} | \n",
" ... | \n",
" user-1 | \n",
" NaN | \n",
" NaN | \n",
" heuristic | \n",
" sa | \n",
" 13.000 | \n",
" 0.020893 | \n",
" 0.0 | \n",
" 1.0 | \n",
" NaN | \n",
"
\n",
" \n",
" | 1 | \n",
" user-1 | \n",
" examples/synthetic/small/scenario.yaml | \n",
" sa | \n",
" 31.492 | \n",
" 0.011216 | \n",
" 3 | \n",
" 31.492 | \n",
" 3.0 | \n",
" NaN | \n",
" NaN | \n",
" ... | \n",
" user-1 | \n",
" NaN | \n",
" NaN | \n",
" heuristic | \n",
" sa | \n",
" 31.492 | \n",
" 0.011216 | \n",
" 0.0 | \n",
" 1.0 | \n",
" {\"forwarder\": 1.0, \"harvester\": 1.0} | \n",
"
\n",
" \n",
" | 2 | \n",
" user-1 | \n",
" examples/synthetic/medium/scenario.yaml | \n",
" sa | \n",
" 89.370 | \n",
" 0.024256 | \n",
" 9 | \n",
" 89.370 | \n",
" 5.0 | \n",
" NaN | \n",
" NaN | \n",
" ... | \n",
" user-1 | \n",
" NaN | \n",
" NaN | \n",
" heuristic | \n",
" sa | \n",
" 89.370 | \n",
" 0.024256 | \n",
" 0.0 | \n",
" 1.0 | \n",
" {\"forwarder\": 1.0, \"harvester\": 1.0} | \n",
"
\n",
" \n",
" | 3 | \n",
" user-1 | \n",
" examples/synthetic/large/scenario.yaml | \n",
" sa | \n",
" 176.808 | \n",
" 0.047626 | \n",
" 15 | \n",
" 176.808 | \n",
" 9.0 | \n",
" NaN | \n",
" NaN | \n",
" ... | \n",
" user-1 | \n",
" NaN | \n",
" NaN | \n",
" heuristic | \n",
" sa | \n",
" 176.808 | \n",
" 0.047626 | \n",
" 0.0 | \n",
" 1.0 | \n",
" {\"forwarder\": 1.0, \"harvester\": 1.0} | \n",
"
\n",
" \n",
"
\n",
"
4 rows × 38 columns
\n",
"
"
],
"text/plain": [
" scenario scenario_path solver objective \\\n",
"0 user-1 examples/tiny7/scenario.yaml sa 13.000 \n",
"1 user-1 examples/synthetic/small/scenario.yaml sa 31.492 \n",
"2 user-1 examples/synthetic/medium/scenario.yaml sa 89.370 \n",
"3 user-1 examples/synthetic/large/scenario.yaml sa 176.808 \n",
"\n",
" runtime_s assignments kpi_total_production kpi_completed_blocks \\\n",
"0 0.020893 16 45.500 1.0 \n",
"1 0.011216 3 31.492 3.0 \n",
"2 0.024256 9 89.370 5.0 \n",
"3 0.047626 15 176.808 9.0 \n",
"\n",
" kpi_mobilisation_cost kpi_mobilisation_cost_by_machine ... scenario_key \\\n",
"0 65.0 {\"H2\": 65.0} ... user-1 \n",
"1 NaN NaN ... user-1 \n",
"2 NaN NaN ... user-1 \n",
"3 NaN NaN ... user-1 \n",
"\n",
" objective_vs_mip_gap objective_vs_mip_ratio solver_category \\\n",
"0 NaN NaN heuristic \n",
"1 NaN NaN heuristic \n",
"2 NaN NaN heuristic \n",
"3 NaN NaN heuristic \n",
"\n",
" best_heuristic_solver best_heuristic_objective best_heuristic_runtime_s \\\n",
"0 sa 13.000 0.020893 \n",
"1 sa 31.492 0.011216 \n",
"2 sa 89.370 0.024256 \n",
"3 sa 176.808 0.047626 \n",
"\n",
" objective_gap_vs_best_heuristic runtime_ratio_vs_best_heuristic \\\n",
"0 0.0 1.0 \n",
"1 0.0 1.0 \n",
"2 0.0 1.0 \n",
"3 0.0 1.0 \n",
"\n",
" kpi_utilisation_ratio_by_role \n",
"0 NaN \n",
"1 {\"forwarder\": 1.0, \"harvester\": 1.0} \n",
"2 {\"forwarder\": 1.0, \"harvester\": 1.0} \n",
"3 {\"forwarder\": 1.0, \"harvester\": 1.0} \n",
"\n",
"[4 rows x 38 columns]"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import sys\n",
"from pathlib import Path\n",
"\n",
"PROJECT_ROOT = Path.cwd().resolve()\n",
"while PROJECT_ROOT != PROJECT_ROOT.parent and not (PROJECT_ROOT / \"pyproject.toml\").exists():\n",
" PROJECT_ROOT = PROJECT_ROOT.parent\n",
"if not (PROJECT_ROOT / \"pyproject.toml\").exists():\n",
" raise RuntimeError(\n",
" \"Notebook must be executed within a FHOPS checkout (pyproject.toml not found).\"\n",
" )\n",
"if str(PROJECT_ROOT) not in sys.path:\n",
" sys.path.insert(0, str(PROJECT_ROOT))\n",
"\n",
"\n",
"import pandas as pd\n",
"\n",
"SUMMARY_CSV = PROJECT_ROOT / \"docs/examples/analytics/data/benchmark_summary.csv\"\n",
"summary_df = pd.read_csv(SUMMARY_CSV)\n",
"summary_df"
]
},
{
"cell_type": "markdown",
"id": "02e57593",
"metadata": {},
"source": [
"## Objective vs Scenario"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "70585a87",
"metadata": {
"execution": {
"iopub.execute_input": "2026-10-05T12:57:40.512030Z",
"iopub.status.busy": "2026-10-05T12:57:40.511889Z",
"iopub.status.idle": "2026-10-05T12:57:40.519452Z",
"shell.execute_reply": "2026-10-05T12:57:40.518452Z"
}
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" objective | \n",
"
\n",
" \n",
" | scenario_path | \n",
" | \n",
"
\n",
" \n",
" \n",
" \n",
" | examples/synthetic/large/scenario.yaml | \n",
" 176.808 | \n",
"
\n",
" \n",
" | examples/synthetic/medium/scenario.yaml | \n",
" 89.370 | \n",
"
\n",
" \n",
" | examples/synthetic/small/scenario.yaml | \n",
" 31.492 | \n",
"
\n",
" \n",
" | examples/tiny7/scenario.yaml | \n",
" 13.000 | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" objective\n",
"scenario_path \n",
"examples/synthetic/large/scenario.yaml 176.808\n",
"examples/synthetic/medium/scenario.yaml 89.370\n",
"examples/synthetic/small/scenario.yaml 31.492\n",
"examples/tiny7/scenario.yaml 13.000"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"objective_pivot = summary_df.pivot_table(index=\"scenario_path\", values=\"objective\", aggfunc=\"first\")\n",
"objective_pivot"
]
},
{
"cell_type": "markdown",
"id": "cf65f1f3",
"metadata": {},
"source": [
"## Runtime Overview"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "40cbb01b",
"metadata": {
"execution": {
"iopub.execute_input": "2026-10-05T12:57:40.520726Z",
"iopub.status.busy": "2026-10-05T12:57:40.520617Z",
"iopub.status.idle": "2026-10-05T12:57:40.525440Z",
"shell.execute_reply": "2026-10-05T12:57:40.524581Z"
}
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" scenario_path | \n",
" runtime_s | \n",
"
\n",
" \n",
" \n",
" \n",
" | 0 | \n",
" examples/tiny7/scenario.yaml | \n",
" 0.020893 | \n",
"
\n",
" \n",
" | 1 | \n",
" examples/synthetic/small/scenario.yaml | \n",
" 0.011216 | \n",
"
\n",
" \n",
" | 2 | \n",
" examples/synthetic/medium/scenario.yaml | \n",
" 0.024256 | \n",
"
\n",
" \n",
" | 3 | \n",
" examples/synthetic/large/scenario.yaml | \n",
" 0.047626 | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" scenario_path runtime_s\n",
"0 examples/tiny7/scenario.yaml 0.020893\n",
"1 examples/synthetic/small/scenario.yaml 0.011216\n",
"2 examples/synthetic/medium/scenario.yaml 0.024256\n",
"3 examples/synthetic/large/scenario.yaml 0.047626"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"runtime_table = summary_df[[\"scenario_path\", \"runtime_s\"]]\n",
"runtime_table"
]
}
],
"metadata": {
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.16"
}
},
"nbformat": 4,
"nbformat_minor": 5
}