{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "ccaf2e55",
   "metadata": {},
   "source": [
    "# DCT Laboratory — Volume II, Chapter 5\n",
    "## Dynamic Enterprise Optimization\n",
    "**Seed `26205`** · Companion to the chapter and AXIOM Module **AXIOM-05 (Vol. II)**\n",
    "\n",
    "The clock becomes load-bearing. Three instruments: the **invest-then-harvest\n",
    "switching family** (neither myopia nor maximal patience wins — the optimum\n",
    "switches at $m^* = 1$), **policy versus plan under a mid-course shock** (the\n",
    "adaptive rule recovers +1.75 the blind plan cannot), and the **trajectory-space\n",
    "equivalence check** — all 64 binary control sequences enumerated, confirming\n",
    "the switch structure is truly optimal. Mirrored in `DCT_V2_Ch05_Lab.xlsx`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e968e0d4",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-14T00:00:11.179419Z",
     "iopub.status.busy": "2026-07-14T00:00:11.179255Z",
     "iopub.status.idle": "2026-07-14T00:00:11.699218Z",
     "shell.execute_reply": "2026-07-14T00:00:11.697933Z"
    }
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "plt.rcParams['figure.dpi']=110\n",
    "\n",
    "import numpy as np\n",
    "SEED = 26205\n",
    "K0, BUDGET, N = 4.0, 3.0, 6\n",
    "# Panel 1: switch family — invest fully for m periods, then consume fully\n",
    "def J_switch(m):\n",
    "    return 3.0*(N-m)*np.sqrt(K0+3.0*m)\n",
    "# Panel 2: shock at start of period 3 (K -= 2); payoff each period = c*sqrt(K)\n",
    "SHOCK_T, SHOCK = 1, 3.0\n",
    "def run(policy, shock=True):\n",
    "    K, J = K0, 0.0\n",
    "    for k in range(N):\n",
    "        if shock and k == SHOCK_T: K -= SHOCK\n",
    "        i = policy(k, K); i = min(max(i, 0.0), BUDGET)\n",
    "        J += (BUDGET-i)*np.sqrt(K)\n",
    "        K = K + i\n",
    "    return J, K\n",
    "open_loop = lambda k, K: 3.0 if k == 0 else 0.0        # the no-shock optimum, replayed blind\n",
    "# adaptive: re-decide each period with the state it sees — invest iff one more\n",
    "# build period beats consuming now, over the remaining horizon\n",
    "def feedback(k, K):\n",
    "    T = N - k\n",
    "    return 3.0 if (T-1)*np.sqrt(K+3.0) > T*np.sqrt(K) else 0.0\n",
    "# Panel 3: all 64 binary sequences (i in {0, 3})\n",
    "def enumerate_binary():\n",
    "    best, arg = -1.0, None\n",
    "    for b in range(2**N):\n",
    "        bits = [(b >> k) & 1 for k in range(N)]\n",
    "        K, J = K0, 0.0\n",
    "        for k in range(N):\n",
    "            i = 3.0*bits[k]\n",
    "            J += (BUDGET-i)*np.sqrt(K)\n",
    "            K += i\n",
    "        if J > best: best, arg = J, bits\n",
    "    return best, arg\n",
    "\n",
    "def reference_values():\n",
    "    Js = [J_switch(m) for m in range(N+1)]\n",
    "    m_star = int(np.argmax(Js))\n",
    "    Jo, Ko = run(open_loop);  Jf, Kf = run(feedback)\n",
    "    Jb, bits = enumerate_binary()\n",
    "    return {\n",
    "        \"J_myopic\": round(J_switch(0),4),\n",
    "        \"m_star\": m_star, \"J_star\": round(max(Js),4),\n",
    "        \"gap_vs_myopic\": round(max(Js)-J_switch(0),4),\n",
    "        \"J_open_shock\": round(Jo,4), \"J_feedback_shock\": round(Jf,4),\n",
    "        \"feedback_advantage\": round(Jf-Jo,4),\n",
    "        \"K_end_adaptive\": round(Kf,4),\n",
    "        \"binary_best\": round(Jb,4),\n",
    "        \"binary_equals_mstar\": int(abs(Jb-max(Js))<1e-9),\n",
    "    }\n",
    "if __name__ == \"__main__\":\n",
    "    [print(f\"{k:20s} {v}\") for k,v in reference_values().items()]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "651af4a2",
   "metadata": {},
   "source": [
    "## Panel 1 — Intertemporal trade-offs: the switch family\n",
    "Six periods, budget 3 each: invest (build $K$) or consume (earn $c\\sqrt{K}$).\n",
    "The family: invest fully for $m$ periods, harvest after — $J(m) = 3(6-m)\\sqrt{4+3m}$.\n",
    "Myopia ($m = 0$) earns 36; maximal patience ($m = 5$) earns 26. The optimum is\n",
    "**$m^* = 1$: J = 39.69** — Intertemporal Trade-Offs Influence Enterprise\n",
    "Optimization Outcomes (Prop.), with the trade-off's interior visible: one\n",
    "period of sacrifice raises every later period's base."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "15be8194",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-14T00:00:11.701837Z",
     "iopub.status.busy": "2026-07-14T00:00:11.701014Z",
     "iopub.status.idle": "2026-07-14T00:00:11.924631Z",
     "shell.execute_reply": "2026-07-14T00:00:11.923338Z"
    }
   },
   "outputs": [],
   "source": [
    "ms = np.arange(0, N+1)\n",
    "Js = [J_switch(m) for m in ms]\n",
    "fig, ax = plt.subplots(figsize=(7.8,4.2))\n",
    "ax.plot(ms, Js, \"o-\", c=\"#C8A24B\", lw=2.2, ms=6)\n",
    "ax.scatter([np.argmax(Js)],[max(Js)], c=\"#0B3D2E\", s=120, zorder=5, label=f\"m* = {int(np.argmax(Js))}, J* = {max(Js):.4f}\")\n",
    "ax.set(xlabel=\"m (periods of full investment before harvest)\", ylabel=\"J(m)\",\n",
    "       title=\"Neither myopia (36.0) nor patience wins — seed 26205\")\n",
    "ax.legend(frameon=False); ax.grid(alpha=.25); plt.tight_layout(); plt.show()\n",
    "print(f\"gap over the myopic policy: {max(Js)-J_switch(0):.4f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f9b8ccde",
   "metadata": {},
   "source": [
    "## Panel 2 — Policy versus plan: the shock test\n",
    "Both start with the optimal no-shock decision (invest at $k = 0$). At $k = 1$ a\n",
    "shock erases the build ($K: 7 \\to 4$). The **open-loop plan** replays its\n",
    "schedule blind and earns 30.00. The **adaptive policy** — re-deciding each\n",
    "period with the state it actually sees — rebuilds for one period and earns\n",
    "**31.75**. Optimal Enterprise Decisions Depend upon Future Enterprise States\n",
    "(Prop.); Enterprise Policies Determine Enterprise Trajectories (Prop.) — and a\n",
    "policy that reads the state beats a plan that remembers one."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a0560bde",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-14T00:00:11.927028Z",
     "iopub.status.busy": "2026-07-14T00:00:11.926792Z",
     "iopub.status.idle": "2026-07-14T00:00:12.120879Z",
     "shell.execute_reply": "2026-07-14T00:00:12.119680Z"
    }
   },
   "outputs": [],
   "source": [
    "Jo, Ko = run(open_loop); Jf, Kf = run(feedback)\n",
    "def trace(policy):\n",
    "    K, path = K0, [K0]\n",
    "    for k in range(N):\n",
    "        Kk = K - (SHOCK if k == SHOCK_T else 0)\n",
    "        i = min(max(policy(k, Kk), 0), BUDGET)\n",
    "        K = Kk + i; path.append(K)\n",
    "    return path\n",
    "fig, ax = plt.subplots(figsize=(7.8,4.0))\n",
    "ax.plot(trace(open_loop), \"s--\", c=\"#8A8F8B\", lw=2, label=f\"open-loop plan → J = {Jo:.2f}\")\n",
    "ax.plot(trace(feedback), \"o-\", c=\"#C8A24B\", lw=2.2, label=f\"adaptive policy → J = {Jf:.2f}\")\n",
    "ax.axvline(SHOCK_T, c=\"#B0532F\", ls=\":\", lw=1.4, label=\"shock (K −3)\")\n",
    "ax.set(xlabel=\"period\", ylabel=\"capital K\", title=\"The plan remembers; the policy sees\")\n",
    "ax.legend(frameon=False, fontsize=9); ax.grid(alpha=.25); plt.tight_layout(); plt.show()\n",
    "print(f\"adaptive advantage under the shock: {Jf-Jo:+.4f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bc31e231",
   "metadata": {},
   "source": [
    "## Panel 3 — The equivalence check: all 64 sequences\n",
    "The Dynamic Enterprise Optimization Equivalence Theorem licenses solving in\n",
    "trajectory space. Executed literally: every binary control sequence\n",
    "($i_k \\in \\{0, 3\\}$, $2^6 = 64$ of them) evaluated. The best is **39.6863 —\n",
    "exactly $J(m^*)$**, and its bit pattern is `100000`: the switch structure is\n",
    "not a convenient family, it is the global optimum over the full sequence space."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4a38f5ed",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-14T00:00:12.122966Z",
     "iopub.status.busy": "2026-07-14T00:00:12.122755Z",
     "iopub.status.idle": "2026-07-14T00:00:12.128660Z",
     "shell.execute_reply": "2026-07-14T00:00:12.127616Z"
    }
   },
   "outputs": [],
   "source": [
    "Jb, bits = enumerate_binary()\n",
    "print(f\"best over all 64 binary sequences: {Jb:.4f}\")\n",
    "print(f\"optimal bit pattern (1 = invest): {''.join(str(b) for b in bits)}\")\n",
    "print(f\"equals the switch-family optimum: {abs(Jb-max(J_switch(m) for m in range(N+1)))<1e-9}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9a7b38d0",
   "metadata": {},
   "source": [
    "## Validation — agrees with `DCT_V2_Ch05_Lab.xlsx`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2708816f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-14T00:00:12.131047Z",
     "iopub.status.busy": "2026-07-14T00:00:12.130293Z",
     "iopub.status.idle": "2026-07-14T00:00:12.137384Z",
     "shell.execute_reply": "2026-07-14T00:00:12.136393Z"
    }
   },
   "outputs": [],
   "source": [
    "ref = reference_values()\n",
    "expected = {\"J_myopic\":36.0,\"m_star\":1,\"J_star\":39.6863,\"gap_vs_myopic\":3.6863,\n",
    " \"J_open_shock\":30.0,\"J_feedback_shock\":31.749,\"feedback_advantage\":1.749,\n",
    " \"K_end_adaptive\":7.0,\"binary_best\":39.6863,\"binary_equals_mstar\":1}\n",
    "for k,v in expected.items():\n",
    "    assert abs(ref[k]-v)<5e-4, f\"MISMATCH {k}\"\n",
    "    print(f\"PASS  {k:20s} {ref[k]}\")\n",
    "print(\"\\nAll checkpoints agree — seed 26205.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3f317c6a",
   "metadata": {},
   "source": [
    "**Next**: Exercises 5.5–5.9 (Part C) vary the shock's timing and watch adaptation's value appear and vanish; AXIOM-05's trajectory theater animates the 64 sequences. Chapter 6 gives the switch structure its theory: Pontryagin. Solutions: IM Vol. II, Ch. 5."
   ]
  }
 ],
 "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.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
