{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "3a9d633b",
   "metadata": {},
   "source": [
    "# Operator/Benchmark Sweep\n",
    "\n",
    "Run the benchmark suite with multiple operator presets and compare objective/runtime metrics."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "fccaeabc",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-11-14T10:35:02.323211Z",
     "iopub.status.busy": "2025-11-14T10:35:02.322286Z",
     "iopub.status.idle": "2025-11-14T10:35:03.440650Z",
     "shell.execute_reply": "2025-11-14T10:35:03.439990Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-style: italic\">                         FHOPS Benchmark Summary                          </span>\n",
       "┏━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━┓\n",
       "┃<span style=\"font-weight: bold\"> scenario </span>┃<span style=\"font-weight: bold\"> solver </span>┃<span style=\"font-weight: bold\"> preset_label </span>┃<span style=\"font-weight: bold\"> objective </span>┃<span style=\"font-weight: bold\"> runtime_s </span>┃<span style=\"font-weight: bold\"> assignments </span>┃\n",
       "┡━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━┩\n",
       "│ user-1   │ sa     │ default      │ 89.370    │ 0.04      │ 9           │\n",
       "│ user-1   │ sa     │ explore      │ 94.371    │ 0.09      │ 9           │\n",
       "│ user-1   │ sa     │ stabilise    │ 94.908    │ 0.08      │ 9           │\n",
       "└──────────┴────────┴──────────────┴───────────┴───────────┴─────────────┘\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[3m                         FHOPS Benchmark Summary                          \u001b[0m\n",
       "┏━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━┓\n",
       "┃\u001b[1m \u001b[0m\u001b[1mscenario\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1msolver\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mpreset_label\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mobjective\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mruntime_s\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1massignments\u001b[0m\u001b[1m \u001b[0m┃\n",
       "┡━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━┩\n",
       "│ user-1   │ sa     │ default      │ 89.370    │ 0.04      │ 9           │\n",
       "│ user-1   │ sa     │ explore      │ 94.371    │ 0.09      │ 9           │\n",
       "│ user-1   │ sa     │ stabilise    │ 94.908    │ 0.08      │ 9           │\n",
       "└──────────┴────────┴──────────────┴───────────┴───────────┴─────────────┘\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>scenario</th>\n",
       "      <th>scenario_path</th>\n",
       "      <th>solver</th>\n",
       "      <th>objective</th>\n",
       "      <th>runtime_s</th>\n",
       "      <th>assignments</th>\n",
       "      <th>kpi_total_production</th>\n",
       "      <th>kpi_completed_blocks</th>\n",
       "      <th>kpi_utilisation_ratio_mean_shift</th>\n",
       "      <th>kpi_utilisation_ratio_weighted_shift</th>\n",
       "      <th>...</th>\n",
       "      <th>operators_stats</th>\n",
       "      <th>scenario_key</th>\n",
       "      <th>objective_vs_mip_gap</th>\n",
       "      <th>objective_vs_mip_ratio</th>\n",
       "      <th>solver_category</th>\n",
       "      <th>best_heuristic_solver</th>\n",
       "      <th>best_heuristic_objective</th>\n",
       "      <th>best_heuristic_runtime_s</th>\n",
       "      <th>objective_gap_vs_best_heuristic</th>\n",
       "      <th>runtime_ratio_vs_best_heuristic</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>user-1</td>\n",
       "      <td>/home/gep/projects/fhops/examples/synthetic/me...</td>\n",
       "      <td>sa</td>\n",
       "      <td>89.370</td>\n",
       "      <td>0.043104</td>\n",
       "      <td>9</td>\n",
       "      <td>89.370</td>\n",
       "      <td>5.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>...</td>\n",
       "      <td>{\"move\": {\"acceptance_rate\": 1.0, \"accepted\": ...</td>\n",
       "      <td>user-1</td>\n",
       "      <td>&lt;NA&gt;</td>\n",
       "      <td>&lt;NA&gt;</td>\n",
       "      <td>heuristic</td>\n",
       "      <td>sa</td>\n",
       "      <td>89.37</td>\n",
       "      <td>0.043104</td>\n",
       "      <td>0.000</td>\n",
       "      <td>1.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>user-1</td>\n",
       "      <td>/home/gep/projects/fhops/examples/synthetic/me...</td>\n",
       "      <td>sa</td>\n",
       "      <td>94.371</td>\n",
       "      <td>0.093004</td>\n",
       "      <td>9</td>\n",
       "      <td>94.371</td>\n",
       "      <td>5.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>...</td>\n",
       "      <td>{\"block_insertion\": {\"acceptance_rate\": 1.0, \"...</td>\n",
       "      <td>user-1</td>\n",
       "      <td>&lt;NA&gt;</td>\n",
       "      <td>&lt;NA&gt;</td>\n",
       "      <td>heuristic</td>\n",
       "      <td>sa</td>\n",
       "      <td>89.37</td>\n",
       "      <td>0.043104</td>\n",
       "      <td>5.001</td>\n",
       "      <td>2.157648</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>user-1</td>\n",
       "      <td>/home/gep/projects/fhops/examples/synthetic/me...</td>\n",
       "      <td>sa</td>\n",
       "      <td>94.908</td>\n",
       "      <td>0.075353</td>\n",
       "      <td>9</td>\n",
       "      <td>94.908</td>\n",
       "      <td>6.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>...</td>\n",
       "      <td>{\"block_insertion\": {\"acceptance_rate\": 1.0, \"...</td>\n",
       "      <td>user-1</td>\n",
       "      <td>&lt;NA&gt;</td>\n",
       "      <td>&lt;NA&gt;</td>\n",
       "      <td>heuristic</td>\n",
       "      <td>sa</td>\n",
       "      <td>89.37</td>\n",
       "      <td>0.043104</td>\n",
       "      <td>5.538</td>\n",
       "      <td>1.748160</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>3 rows × 35 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "  scenario                                      scenario_path solver  \\\n",
       "0   user-1  /home/gep/projects/fhops/examples/synthetic/me...     sa   \n",
       "1   user-1  /home/gep/projects/fhops/examples/synthetic/me...     sa   \n",
       "2   user-1  /home/gep/projects/fhops/examples/synthetic/me...     sa   \n",
       "\n",
       "   objective  runtime_s  assignments  kpi_total_production  \\\n",
       "0     89.370   0.043104            9                89.370   \n",
       "1     94.371   0.093004            9                94.371   \n",
       "2     94.908   0.075353            9                94.908   \n",
       "\n",
       "   kpi_completed_blocks  kpi_utilisation_ratio_mean_shift  \\\n",
       "0                   5.0                               1.0   \n",
       "1                   5.0                               1.0   \n",
       "2                   6.0                               1.0   \n",
       "\n",
       "   kpi_utilisation_ratio_weighted_shift  ...  \\\n",
       "0                                   1.0  ...   \n",
       "1                                   1.0  ...   \n",
       "2                                   1.0  ...   \n",
       "\n",
       "                                     operators_stats scenario_key  \\\n",
       "0  {\"move\": {\"acceptance_rate\": 1.0, \"accepted\": ...       user-1   \n",
       "1  {\"block_insertion\": {\"acceptance_rate\": 1.0, \"...       user-1   \n",
       "2  {\"block_insertion\": {\"acceptance_rate\": 1.0, \"...       user-1   \n",
       "\n",
       "   objective_vs_mip_gap  objective_vs_mip_ratio  solver_category  \\\n",
       "0                  <NA>                    <NA>        heuristic   \n",
       "1                  <NA>                    <NA>        heuristic   \n",
       "2                  <NA>                    <NA>        heuristic   \n",
       "\n",
       "  best_heuristic_solver  best_heuristic_objective  best_heuristic_runtime_s  \\\n",
       "0                    sa                     89.37                  0.043104   \n",
       "1                    sa                     89.37                  0.043104   \n",
       "2                    sa                     89.37                  0.043104   \n",
       "\n",
       "   objective_gap_vs_best_heuristic  runtime_ratio_vs_best_heuristic  \n",
       "0                            0.000                         1.000000  \n",
       "1                            5.001                         2.157648  \n",
       "2                            5.538                         1.748160  \n",
       "\n",
       "[3 rows x 35 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",
    "from fhops.cli.benchmarks import run_benchmark_suite\n",
    "\n",
    "BENCH_DIR = PROJECT_ROOT / \"docs/examples/analytics/data/operator_sweep\"\n",
    "summary = run_benchmark_suite(\n",
    "    [PROJECT_ROOT / \"examples/synthetic/medium/scenario.yaml\"],\n",
    "    BENCH_DIR,\n",
    "    time_limit=5,\n",
    "    sa_iters=150,\n",
    "    include_mip=False,\n",
    "    preset_comparisons=[\"explore\", \"stabilise\"],\n",
    ")\n",
    "summary"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "acc185b5",
   "metadata": {},
   "source": [
    "## Objective Comparison"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "081fe895",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-11-14T10:35:03.443837Z",
     "iopub.status.busy": "2025-11-14T10:35:03.443675Z",
     "iopub.status.idle": "2025-11-14T10:35:03.451212Z",
     "shell.execute_reply": "2025-11-14T10:35:03.450633Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>objective</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>preset_label</th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>default</th>\n",
       "      <td>89.370</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>explore</th>\n",
       "      <td>94.371</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>stabilise</th>\n",
       "      <td>94.908</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "              objective\n",
       "preset_label           \n",
       "default          89.370\n",
       "explore          94.371\n",
       "stabilise        94.908"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "objective_pivot = summary.pivot_table(index=\"preset_label\", values=\"objective\")\n",
    "objective_pivot"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "de60cddd",
   "metadata": {},
   "source": [
    "## Runtime Comparison"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "31b27eec",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-11-14T10:35:03.454122Z",
     "iopub.status.busy": "2025-11-14T10:35:03.453972Z",
     "iopub.status.idle": "2025-11-14T10:35:03.461222Z",
     "shell.execute_reply": "2025-11-14T10:35:03.460629Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>runtime_s</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>preset_label</th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>default</th>\n",
       "      <td>0.043104</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>explore</th>\n",
       "      <td>0.093004</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>stabilise</th>\n",
       "      <td>0.075353</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "              runtime_s\n",
       "preset_label           \n",
       "default        0.043104\n",
       "explore        0.093004\n",
       "stabilise      0.075353"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "runtime_pivot = summary.pivot_table(index=\"preset_label\", values=\"runtime_s\")\n",
    "runtime_pivot"
   ]
  }
 ],
 "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.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
