{ "cells": [ { "cell_type": "markdown", "id": "1cdacebd", "metadata": {}, "source": [ "# `transit-strategy` — Spiess & Florian's (1989) optimal strategies\n", "\n", "**What.** `transit-strategy` is a transit-assignment model where a passenger\n", "chooses not a single path but a \"strategy\" — a set of attractive lines to\n", "board whichever comes first — solved as a linear program by a label-setting\n", "pass in decreasing order of expected cost, with frequency-proportional\n", "splitting across the attractive set (common-lines waiting).\n", "\n", "**Why it is in the benchmark.** It is the first TRANSIT model — uncongested,\n", "frequency-based, hyperpath assignment, a genuinely different domain from road\n", "UE — and needed its own parallel scenario type (`TransitNetwork`, a directed\n", "multigraph with per-arc in-vehicle time and frequency; parallel arcs allowed\n", "so common lines are direct). See the\n", "[model compendium](../../docs/MODELS.md) (Spiess & Florian 1989) and\n", "[docs/design/adr-014-transit-strategy.md](../../docs/design/adr-014-transit-strategy.md) (P1).\n", "\n", "**Scope.** This notebook runs `optimal_strategy` on two built-in common-lines\n", "anchors — one where both lines are attractive, one where a slower line is\n", "excluded — and certifies both against the LP optimum.\n", "\n", "**Canon.** `[spiess1989optimal]`, [docs/REFERENCES.md](../../docs/REFERENCES.md) / [docs/references.bib](../../docs/references.bib)." ] }, { "cell_type": "markdown", "id": "e79f2a55", "metadata": {}, "source": [ "## How this notebook is graded\n", "\n", "**A notebook never claims a number it does not compute in that cell.** Every\n", "scored quantity below is recomputed live by the P1 `TransitEvaluator` from\n", "the emitted per-arc passenger volumes alone — the harness independently\n", "recomputes the LP optimum `Z*` from the scenario and scores the emitted primal\n", "cost against it; the solver's own labels/costs are never trusted\n", "([README](../../README.md), *Certified, not self-reported*)." ] }, { "cell_type": "code", "execution_count": 1, "id": "4155cc2e", "metadata": { "execution": { "iopub.execute_input": "2026-07-21T13:48:15.911002Z", "iopub.status.busy": "2026-07-21T13:48:15.910637Z", "iopub.status.idle": "2026-07-21T13:48:17.915891Z", "shell.execute_reply": "2026-07-21T13:48:17.914706Z" } }, "outputs": [], "source": [ "# Setup. `transit-strategy` is a core model: a plain `pip install -e .`\n", "# suffices — no optional extra, so no guard cell. The inline backend is\n", "# Agg-based (headless CI renders into the notebook); NEVER\n", "# matplotlib.use(\"Agg\") in-kernel — it silently suppresses inline capture.\n", "%matplotlib inline\n", "import numpy as np\n", "\n", "from tabench import (\n", " TransitEvaluator,\n", " common_lines_expected_cost,\n", " common_lines_scenario,\n", " common_lines_unattractive_scenario,\n", " optimal_strategy,\n", ")" ] }, { "cell_type": "markdown", "id": "29ad5db0", "metadata": {}, "source": [ "## Instance 1: two attractive common lines\n", "\n", "One boarding stop (node 0), one destination (node 1), two parallel lines: line\n", "0 (frequency 1/6, in-vehicle 21 min) and line 1 (frequency 1/12, in-vehicle 18\n", "min). `TransitScenario` is frozen and content-hashed (P2), domain-separated\n", "from the road scenario hash space." ] }, { "cell_type": "code", "execution_count": 2, "id": "2783351d", "metadata": { "execution": { "iopub.execute_input": "2026-07-21T13:48:17.921028Z", "iopub.status.busy": "2026-07-21T13:48:17.920664Z", "iopub.status.idle": "2026-07-21T13:48:17.926668Z", "shell.execute_reply": "2026-07-21T13:48:17.925808Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "scenario : transit-common-lines\n", "content hash : 27a419a857b96eab…\n", "lines (freq, time) : [(np.float64(0.16666666666666666), np.float64(21.0)), (np.float64(0.08333333333333333), np.float64(18.0))]\n", "demand : 1000.0\n", "task : optimal-strategy hyperpath transit assignment\n" ] } ], "source": [ "scenario = common_lines_scenario(demand=1000.0)\n", "print(f\"scenario : {scenario.name}\")\n", "print(f\"content hash : {scenario.content_hash()[:16]}…\")\n", "print(f\"lines (freq, time) : \"\n", " f\"{list(zip(scenario.network.freq, scenario.network.time))}\")\n", "print(f\"demand : {scenario.demand.total}\")\n", "print(\"task : optimal-strategy hyperpath transit assignment\")" ] }, { "cell_type": "markdown", "id": "98fb025a", "metadata": {}, "source": [ "## Solve\n", "\n", "No `Budget`/`RngBundle`/`Trace` — `optimal_strategy` is a pure function of the\n", "scenario (one label-setting + loading pass per destination, deterministic, no\n", "equilibration), emitting a `TransitStrategy` (per-arc passenger volumes, the\n", "P1-certifiable artifact)." ] }, { "cell_type": "code", "execution_count": 3, "id": "1234c2c1", "metadata": { "execution": { "iopub.execute_input": "2026-07-21T13:48:17.930479Z", "iopub.status.busy": "2026-07-21T13:48:17.930166Z", "iopub.status.idle": "2026-07-21T13:48:17.935154Z", "shell.execute_reply": "2026-07-21T13:48:17.934246Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "arc volumes (line 0, line 1) : [666.667 333.333]\n", "pair cost (self-reported) : [24.] (provenance only)\n" ] } ], "source": [ "strategy = optimal_strategy(scenario)\n", "print(f\"arc volumes (line 0, line 1) : {np.round(strategy.arc_volumes, 3)}\")\n", "print(f\"pair cost (self-reported) : {strategy.pair_costs} (provenance only)\")" ] }, { "cell_type": "markdown", "id": "47611775", "metadata": {}, "source": [ "## Certify (P1) — the LP optimum, recomputed\n", "\n", "The harness recomputes the LP-minimal expected cost independently from the\n", "scenario (label-setting, not the model's own labels) and scores\n", "`optimality_gap = (Z_emitted - Z*) / Z*` — zero iff the emitted strategy is\n", "optimal." ] }, { "cell_type": "code", "execution_count": 4, "id": "822604a3", "metadata": { "execution": { "iopub.execute_input": "2026-07-21T13:48:17.938967Z", "iopub.status.busy": "2026-07-21T13:48:17.938768Z", "iopub.status.idle": "2026-07-21T13:48:17.945431Z", "shell.execute_reply": "2026-07-21T13:48:17.944553Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "feasible : 1\n", "expected_cost : 24.0000\n", "optimality_gap : 0.000e+00\n", "conservation_residual : 0.000e+00\n", "closed-form C* : 24.000000 attractive lines: [0, 1]\n", "demand split (line 0, line 1) : [0.6667 0.3333] (closed form: 2/3, 1/3)\n" ] } ], "source": [ "evaluator = TransitEvaluator(scenario)\n", "metrics = evaluator.certify(strategy)\n", "print(f\"feasible : {metrics['feasible']:.0f}\")\n", "print(f\"expected_cost : {metrics['expected_cost']:.4f}\")\n", "print(f\"optimality_gap : {metrics['optimality_gap']:.3e}\")\n", "print(f\"conservation_residual : {metrics['conservation_residual']:.3e}\")\n", "assert metrics[\"feasible\"] == 1.0\n", "assert metrics[\"optimality_gap\"] < 1e-9\n", "assert np.isclose(metrics[\"expected_cost\"], 24.0, atol=1e-6) # wait 4 + ride 20\n", "\n", "# Recompute the closed form directly from the scenario's own lines, not quoted:\n", "# combined frequency F=1/6+1/12=1/4, C*=(1+sum f*t)/F.\n", "lines = [(1 / 6, 21.0), (1 / 12, 18.0)]\n", "c_star, attractive = common_lines_expected_cost(lines)\n", "print(f\"closed-form C* : {c_star:.6f} attractive lines: {attractive}\")\n", "assert np.isclose(c_star, 24.0, atol=1e-9)\n", "assert attractive == [0, 1] # both lines attractive\n", "\n", "# The frequency-share split: v0/v1 = f0/f1 = 2:1, recomputed from the emitted\n", "# arc volumes directly.\n", "split = strategy.arc_volumes / scenario.demand.total\n", "print(f\"demand split (line 0, line 1) : {np.round(split, 4)} (closed form: 2/3, 1/3)\")\n", "np.testing.assert_allclose(split, [2 / 3, 1 / 3], atol=1e-9)" ] }, { "cell_type": "markdown", "id": "65348e3d", "metadata": {}, "source": [ "## Instance 2: the attractiveness threshold\n", "\n", "Line 0 alone (frequency 1/6, in-vehicle 15 min) gives an expected cost\n", "`1/(1/6) + 15 = 21` min. Line 1 (frequency 1/12, in-vehicle 40 min) has an\n", "onward cost of 40, NOT below 21 — so it is excluded from the strategy\n", "entirely, and every passenger boards line 0." ] }, { "cell_type": "code", "execution_count": 5, "id": "faa23977", "metadata": { "execution": { "iopub.execute_input": "2026-07-21T13:48:17.949445Z", "iopub.status.busy": "2026-07-21T13:48:17.948902Z", "iopub.status.idle": "2026-07-21T13:48:17.955282Z", "shell.execute_reply": "2026-07-21T13:48:17.954422Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "feasible : 1\n", "expected_cost : 21.0000\n", "optimality_gap : 0.000e+00\n", "closed-form C* : 21.000000 attractive lines: [0]\n", "arc volumes (line 0, line 1) : [1000. 0.]\n" ] } ], "source": [ "scenario2 = common_lines_unattractive_scenario(demand=1000.0)\n", "strategy2 = optimal_strategy(scenario2)\n", "evaluator2 = TransitEvaluator(scenario2)\n", "metrics2 = evaluator2.certify(strategy2)\n", "print(f\"feasible : {metrics2['feasible']:.0f}\")\n", "print(f\"expected_cost : {metrics2['expected_cost']:.4f}\")\n", "print(f\"optimality_gap : {metrics2['optimality_gap']:.3e}\")\n", "assert metrics2[\"feasible\"] == 1.0\n", "assert metrics2[\"optimality_gap\"] < 1e-9\n", "assert np.isclose(metrics2[\"expected_cost\"], 21.0, atol=1e-6)\n", "\n", "lines2 = [(1 / 6, 15.0), (1 / 12, 40.0)]\n", "c_star2, attractive2 = common_lines_expected_cost(lines2)\n", "print(f\"closed-form C* : {c_star2:.6f} attractive lines: {attractive2}\")\n", "assert np.isclose(c_star2, 21.0, atol=1e-9)\n", "assert attractive2 == [0] # line 1 excluded\n", "\n", "# All demand on line 0 (line 1 carries zero volume).\n", "print(f\"arc volumes (line 0, line 1) : {np.round(strategy2.arc_volumes, 3)}\")\n", "np.testing.assert_allclose(strategy2.arc_volumes, [1000.0, 0.0], atol=1e-6)" ] }, { "cell_type": "markdown", "id": "725deea8", "metadata": {}, "source": [ "## Visualize\n", "\n", "`tabench.viz` draws the road `Network`; a transit common-lines scenario is a\n", "`TransitNetwork` (a different scenario type entirely, adr-014), so this\n", "notebook plots the two strategies' frequency-share splits directly." ] }, { "cell_type": "code", "execution_count": 6, "id": "b42f0a02", "metadata": { "execution": { "iopub.execute_input": "2026-07-21T13:48:17.959461Z", "iopub.status.busy": "2026-07-21T13:48:17.958951Z", "iopub.status.idle": "2026-07-21T13:48:18.107956Z", "shell.execute_reply": "2026-07-21T13:48:18.106922Z" } }, "outputs": [ { "data": { "image/png": "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iIiIiKjK0mtice/ePbRr1w6mpqbYvn07zp07h6+//hrVq1eX2syZMwcLFy5EbGwsEhMTYW1tjYCAADx+/FhqExISgrNnz2LXrl2Ij4/HwYMHER4ero8uERERERFVSSb63Pjs2bPh4eGBlStXSmVeXl7Sv4UQmD9/PiZMmIDevXsDAH744Qe4uLhg8+bNCA4Oxvnz57Fjxw4cP34cLVu2BAB888036NmzJ7766iu4u7tXbKeIiIiIiKogvR6x2LJlC1q2bIl3330Xzs7O8PX1xbJly6T6q1evQqFQwN/fXyqzs7ND69atkZCQAABISEiAvb29lFQAgL+/P4yMjJCYmFhxnSEiIiIiqsL0mlhcuXIFS5YsQf369fHHH39gxIgRGD16NFavXg0AUCgUAAAXFxeV5VxcXKQ6hUIBZ2dnlXoTExM4ODhIbZ6Vk5MDpVKp8iIiIiIiorLT66lQhYWFaNmyJWJiYgAAvr6+SElJQWxsLEJDQ8ttuzNnzsTUqVPLbf1ERERERFWNXo9YuLm5wcfHR6WsYcOGSE1NBQC4uroCANLS0lTapKWlSXWurq5IT09Xqc/Pz0dGRobU5llRUVHIysqSXjdu3NBJf4iIiIiIqiq9Jhbt2rXDhQsXVMouXrwIT09PAE8u5HZ1dcWePXukeqVSicTERMjlcgCAXC5HZmYmkpKSpDZ79+5FYWEhWrduXeJ2zc3NYWtrq/IiIiIiIqKy0+upUGPHjkXbtm0RExODfv364dixY1i6dCmWLl0KAJDJZBgzZgymT5+O+vXrw8vLCxMnToS7uzv69OkD4MkRju7du2P48OGIjY1FXl4eRo4cieDgYN4RioiIiIiogug1sXjttdewadMmREVFITo6Gl5eXpg/fz5CQkKkNp999hkePHiA8PBwZGZmon379tixYwcsLCykNmvWrMHIkSPRpUsXGBkZISgoCAsXLtRHl4iIiIiIqiS9JhYA8Oabb+LNN98stV4mkyE6OhrR0dGltnFwcEBcXFx5hEdERERERGrQ6zUWRERERET0cmBiQUREREREWmNiQUREREREWmNiQUREREREWmNiQUREREREWmNiQUREREREWmNiQUREREREWmNiQUREREREWmNiQUREREREWmNiQUREREREWmNiQUREREREWmNiQUREREREWmNiQUREREREWmNiQUREelVQ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C0NkZCQcHBxga2uLUaNGQS6Xo02bNgCAbt26wcfHB4MGDcKcOXOgUCgwYcIERERE8IgEEREREVEF0jixSEhIwJEjR7B9+/ZidZpevH3z5k0MGDAA//33H5ycnNC+fXv8+eefcHJyAgDMmzcPRkZGCAoKQk5ODgICArB48WJpeWNjY8THx2PEiBGQy+WwtrZGaGgooqOjNe0WERERERFpQePEYtSoURg4cCAmTpxY7I5Mmlq7du1z6y0sLLBo0SIsWrSo1Daenp7Ytm2bVnEQEREREZF2NL7G4r///sPYsWO1TiqIiIiIiOjloXFi0bdvX+zbt688YiEiIiIiokpK41OhXn31VURFReHw4cNo0qRJsYu3R48erbPgiIiIiIiocijTXaFsbGxw4MABHDhwQKVOJpMxsSAiIiIiqoI0TiyuXr1aHnEQEREREVElpvE1FkRERERERM/S+IjFsGHDnlv//ffflzkYIiIiIiKqnDROLO7du6fyPi8vDykpKcjMzETnzp11FhgREREREVUeGicWmzZtKlZWWFiIESNGoG7dujoJioiIiIiIKhedXGNhZGSEyMhIzJs3TxerIyIiIiKiSkZnF29fvnwZ+fn5ulodERERERFVIhqfChUZGanyXgiBO3fuYOvWrQgNDdVZYEREREREVHlonFicOnVK5b2RkRGcnJzw9ddfv/COUURERERE9HLSOLHYt29fecRBRERERESVGB+QR0REREREWlPriIWvry9kMplaKzx58qRWARERERERUeWjVmLRp0+fcg6DiIiIiIgqM7USi8mTJ5d3HEREREREVIlpfPF2kaSkJJw/fx4A0KhRI/j6+uosKCIiIiIiqlw0TizS09MRHByM/fv3w97eHgCQmZmJN954A2vXroWTk5OuYyQiIiIiIgOn8V2hRo0ahfv37+Ps2bPIyMhARkYGUlJSoFQqMXr06PKIkYiIiIiIDJzGRyx27NiB3bt3o2HDhlKZj48PFi1ahG7duuk0OCIiIiIiqhw0PmJRWFgIU1PTYuWmpqYoLCzUSVBERERERFS5aJxYdO7cGR9//DFu374tld26dQtjx45Fly5ddBocERERERFVDhonFt9++y2USiXq1KmDunXrom7duvDy8oJSqcQ333xT5kBmzZoFmUyGMWPGSGWPHz9GREQEHB0dYWNjg6CgIKSlpaks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EZHh0/hUqOrVq0t3ztixY4d00bYQAgUFBbqNjoiIiIiIKgWNj1j07dsX7733HurXr4///vsPPXr0AACcOnUK9erV03mARERERERk+DROLObNm4c6dergxo0bmDNnDmxsbAAAd+7cwUcffaTzAImIiIiIyPBpnFiYmpri008/LVY+duxYnQRERERERESVj8bXWKxevRpbt26V3n/22Wewt7dH27Ztcf36dZ0GR0RERERElYPGiUVMTAwsLS0BAAkJCVi0aBHmzJmDGjVq8KgFEREREVEVpfGpUDdu3JAu0t68eTOCgoIQHh6Odu3aoVOnTrqOj4iIiIiIKgGNj1jY2Njgv//+AwDs3LkTXbt2BfDkAUaPHj3SbXRERERERFQpaHzEomvXrnj//ffh6+uLixcvomfPngCAs2fPok6dOrqOj4iIiIiIKgGNj1gsWrQIcrkcd+/excaNG+Ho6AgASEpKwoABA3QeIBERERERGT6Nj1jY29vj22+/LVY+depUnQRERERERESVj8aJRZGHDx8iNTUVubm5KuVNmzbVOigiIiIiIqpcND4V6u7duwgMDES1atXQqFEj+Pr6qrw0MXPmTLz22muoVq0anJ2d0adPH1y4cEGlzePHjxEREQFHR0fY2NggKCgIaWlpKm1SU1MRGBgIKysrODs7Y9y4ccjPz9e0a0REREREVEYaJxZjxoxBVlYWEhMTYWlpiR07dmD16tWoX78+tmzZotG6Dhw4gIiICPz555/YtWsX8vLy0K1bNzx48EBqM3bsWPz+++/YsGEDDhw4gNu3b6Nv375SfUFBAQIDA5Gbm4ujR49i9erVWLVqFSZNmqRp14iIiIiIqIw0PhVq7969+O2339CyZUsYGRnB09MTXbt2ha2tLWbOnInAwEC117Vjxw6V96tWrYKzszOSkpLQoUMHZGVlYcWKFYiLi0Pnzp0BACtXrkTDhg3x559/ok2bNti5cyfOnTuH3bt3w8XFBc2bN8e0adMwfvx4TJkyBWZmZpp2kYiIiIiINKTxEYsHDx7A2dkZAFC9enXcvXsXANCkSROcPHlSq2CysrIAAA4ODgCe3GkqLy8P/v7+Uhtvb2/Url0bCQkJAJ48/btJkyZwcXGR2gQEBECpVOLs2bMlbicnJwdKpVLlRUREREREZadxYtGgQQPpOohmzZrhu+++w61btxAb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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import matplotlib.pyplot as plt\n", "\n", "fig, axes = plt.subplots(1, 2, figsize=(8.0, 3.4))\n", "axes[0].bar([\"line 0\\n(f=1/6, t=21)\", \"line 1\\n(f=1/12, t=18)\"], strategy.arc_volumes)\n", "axes[0].set_title(f\"instance 1: both attractive (C*={metrics['expected_cost']:.0f})\")\n", "axes[0].set_ylabel(\"passenger volume\")\n", "\n", "axes[1].bar([\"line 0\\n(f=1/6, t=15)\", \"line 1\\n(f=1/12, t=40)\"], strategy2.arc_volumes)\n", "axes[1].set_title(f\"instance 2: line 1 excluded (C*={metrics2['expected_cost']:.0f})\")\n", "fig.tight_layout()\n", "display(fig)\n", "plt.close(fig)\n" ] }, { "cell_type": "markdown", "id": "eaacb984", "metadata": {}, "source": [ "## Takeaways & pointers\n", "\n", "- **Certified, not self-reported.** Both instances' optimal costs came from\n", " `TransitEvaluator`'s own independent label-setting pass, not the model's\n", " self-reported labels.\n", "- **The attractiveness threshold is exact.** A line joins the strategy only\n", " while its onward cost stays below the running expected cost — instance 2\n", " shows the cut, not just the split.\n", "- **Where next.** the lineage in the [model compendium](../../docs/MODELS.md);\n", " the parallel-module pattern it shares with the DNL core\n", " [`ctm`](../05-dnl/01-ctm.ipynb)." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "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.10.12" }, "tabench": { "covers": [], "requires_extra": null, "track": "transit", "unit": "transit-strategy" } }, "nbformat": 4, "nbformat_minor": 5 }