{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "4a9b9203",
   "metadata": {},
   "source": [
    "# DCT Laboratory — Volume II, Chapter 15\n",
    "## Enterprise Digital Twins and Autonomous Transformation\n",
    "**Seed `26215`** · Companion to the chapter and AXIOM Module **AXIOM-15 (Vol. II)**\n",
    "\n",
    "The autonomous twin, in three certified pieces. **Synchronization**: a gain of\n",
    "0.5 contracts twin error at factor $0.45$ per step — the synchronized twin\n",
    "tracks through the $k = 8$ shock the pure-model twin never sees (RMSE 0.93 vs\n",
    "2.88). **Decision quality**: the drifted twin recommends the *wrong* switching\n",
    "policy ($m^* = 0$ against the truth's $m^* = 1$), billed at true state:\n",
    "**regret 1.36**. **Autonomous stability**: the feedback loop's factor\n",
    "$|0.9 - c|$ — deadbeat at $c = 0.9$, unstable past the grid's 1.8.\n",
    "Mirrored in `DCT_V2_Ch15_Lab.xlsx`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ab271314",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-14T01:05:22.196920Z",
     "iopub.status.busy": "2026-07-14T01:05:22.195554Z",
     "iopub.status.idle": "2026-07-14T01:05:22.798755Z",
     "shell.execute_reply": "2026-07-14T01:05:22.797431Z"
    }
   },
   "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 = 26215\n",
    "A, U, K0, T = 0.9, 1.0, 10.0, 12\n",
    "SHOCK_T, SHOCK = 8, -6.0\n",
    "rng = np.random.default_rng([2, SEED])\n",
    "NOISE = np.round(rng.normal(0, 0.4, T), 4)   # frozen observation noise = canonical\n",
    "def truth_path():\n",
    "    K, path = K0, [K0]\n",
    "    for k in range(T):\n",
    "        K = A*K + U + (SHOCK if k == SHOCK_T else 0.0)\n",
    "        path.append(K)\n",
    "    return np.array(path)\n",
    "def twin_path(g):\n",
    "    \"\"\"twin_{k+1} = (1-g)*(A*twin_k + U) + g*y_{k+1}, y = truth + noise.\"\"\"\n",
    "    tr = truth_path()\n",
    "    tw = [K0]\n",
    "    for k in range(T):\n",
    "        y = tr[k+1] + NOISE[k]\n",
    "        tw.append((1-g)*(A*tw[k] + U) + g*y)\n",
    "    return np.array(tw)\n",
    "def rmse(g):\n",
    "    return float(np.sqrt(np.mean((twin_path(g)-truth_path())**2)))\n",
    "# decision layer: switch family from estimated terminal state\n",
    "def J_switch(m, K): return 3.0*(6-m)*np.sqrt(K + 3.0*m)\n",
    "def m_star(K): return int(np.argmax([J_switch(m, K) for m in range(7)]))\n",
    "def decision_block():\n",
    "    Kt = truth_path()[-1]\n",
    "    Ks, Ko = twin_path(0.5)[-1], twin_path(0.0)[-1]\n",
    "    ms, mo = m_star(Ks), m_star(Ko)\n",
    "    return Kt, Ks, Ko, ms, mo, J_switch(ms, Kt) - J_switch(mo, Kt)\n",
    "# autonomous loop: K_{k+1} = A*K + u, u = 0.1*Ktar + c*(Ktar - K)  =>  gap factor |A - c|\n",
    "KTAR = 8.0\n",
    "def gap_after(n, c, K0_=10.0):\n",
    "    gap = K0_ - KTAR\n",
    "    return abs(gap*(A-c)**n)\n",
    "def c_max_stable(step=0.2):\n",
    "    cs = [round(step*i,1) for i in range(0, 13)]\n",
    "    return max(c for c in cs if abs(A-c) < 1.0)\n",
    "\n",
    "def reference_values():\n",
    "    Kt, Ks, Ko, ms, mo, reg = decision_block()\n",
    "    return {\n",
    "        \"rmse_open\": round(rmse(0.0),4),\n",
    "        \"rmse_sync\": round(rmse(0.5),4),\n",
    "        \"sync_advantage\": round(rmse(0.0)/rmse(0.5),4),\n",
    "        \"contraction_factor\": round((1-0.5)*A,4),\n",
    "        \"K_true_T\": round(Kt,4), \"K_sync_T\": round(Ks,4), \"K_open_T\": round(Ko,4),\n",
    "        \"mstar_sync\": ms, \"mstar_open\": mo,\n",
    "        \"regret_open_twin\": round(reg,4),\n",
    "        \"c_fastest\": 0.9, \"factor_at_09\": 0.0,\n",
    "        \"c_max_stable\": c_max_stable(),\n",
    "        \"gap3_c05\": round(gap_after(3, 0.5),4),\n",
    "    }\n",
    "if __name__ == \"__main__\":\n",
    "    print(\"NOISE:\", list(NOISE))\n",
    "    print(\"truth:\", [round(x,4) for x in truth_path()])\n",
    "    [print(f\"{k:20s} {v}\") for k,v in reference_values().items()]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c6939b5d",
   "metadata": {},
   "source": [
    "## Panel 1 — Synchronization: the twin that looks\n",
    "The truth runs $K_{k+1} = 0.9K_k + 1$ with a $-6$ shock at $k = 8$; the twin\n",
    "blends its model with noisy observations: $\\hat{K}_{k+1} = (1-g)(0.9\\hat{K}_k\n",
    "+ 1) + g\\,y_{k+1}$ (Enterprise State Synchronization Operator, Def.). At\n",
    "$g = 0$ the twin is a simulation with a memory of initial conditions — it\n",
    "never learns of the shock. At $g = 0.5$ the error dynamics contract at\n",
    "$(1-g)\\cdot 0.9 = 0.45$ per step (Enterprise State Synchronization Theorem):\n",
    "the twin re-acquires the enterprise within a few observations. RMSE: 2.88\n",
    "open, **0.93 synchronized** — a 3.1× fidelity ratio, and The Twin Continuously\n",
    "Approximates the Evolving Enterprise State (Prop.) with its constant shown."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fcdcc83e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-14T01:05:22.801129Z",
     "iopub.status.busy": "2026-07-14T01:05:22.800794Z",
     "iopub.status.idle": "2026-07-14T01:05:23.040025Z",
     "shell.execute_reply": "2026-07-14T01:05:23.038714Z"
    }
   },
   "outputs": [],
   "source": [
    "tr = truth_path()\n",
    "fig, ax = plt.subplots(figsize=(7.8,4.2))\n",
    "ax.plot(tr, \"k--\", lw=2, label=\"enterprise truth (shock −6 at k=8)\")\n",
    "ax.plot(twin_path(0.0), \"s-\", c=\"#8A8F8B\", lw=1.8, ms=4, label=f\"pure-model twin g=0 → RMSE {rmse(0.0):.2f}\")\n",
    "ax.plot(twin_path(0.5), \"o-\", c=\"#C8A24B\", lw=2.2, ms=4, label=f\"synchronized twin g=0.5 → RMSE {rmse(0.5):.2f}\")\n",
    "ax.axvline(SHOCK_T+1, c=\"#B0532F\", ls=\":\", lw=1.4)\n",
    "ax.set(xlabel=\"step\", ylabel=\"capital K\", title=\"The twin that looks vs the twin that remembers — seed 26215\")\n",
    "ax.legend(frameon=False, fontsize=9); ax.grid(alpha=.25); plt.tight_layout(); plt.show()\n",
    "print(f\"contraction factor (1-g)*A = {(1-0.5)*A:.4f}   sync advantage: {rmse(0.0)/rmse(0.5):.4f}x\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7855b9e7",
   "metadata": {},
   "source": [
    "## Panel 2 — Decision quality: the drift, billed\n",
    "Simulation-Based Enterprise Optimization (§15.6): each twin hands its terminal\n",
    "estimate to the Chapter 5 switching optimizer. The synchronized twin\n",
    "($\\hat{K} = 5.86$) recommends **invest first** ($m^* = 1$) — matching what\n",
    "the true state ($K = 5.63$) demands. The drifted twin still believes\n",
    "$K = 10$ and recommends **harvest now** ($m^* = 0$). Billed at the TRUE state,\n",
    "the drifted recommendation costs **1.3605** — Continuous Synchronization\n",
    "Improves Enterprise Decision Quality (Prop.) as a regret with a decimal point,\n",
    "and the whole reason twins exist: stale state is not a data problem, it is a\n",
    "DECISION problem."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5b7f0914",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-14T01:05:23.042334Z",
     "iopub.status.busy": "2026-07-14T01:05:23.042096Z",
     "iopub.status.idle": "2026-07-14T01:05:23.271199Z",
     "shell.execute_reply": "2026-07-14T01:05:23.269391Z"
    }
   },
   "outputs": [],
   "source": [
    "Kt, Ks, Ko, ms, mo, reg = decision_block()\n",
    "ms_grid = range(7)\n",
    "fig, ax = plt.subplots(figsize=(7.8,4.0))\n",
    "ax.plot(list(ms_grid), [J_switch(m, Kt) for m in ms_grid], \"o-\", c=\"#0B3D2E\", lw=2.2, label=f\"J(m) at TRUE K = {Kt:.2f}\")\n",
    "ax.scatter([ms],[J_switch(ms,Kt)], s=140, c=\"#C8A24B\", zorder=5, label=f\"sync twin's pick m={ms}\")\n",
    "ax.scatter([mo],[J_switch(mo,Kt)], s=140, c=\"#B0532F\", marker=\"X\", zorder=5, label=f\"drifted twin's pick m={mo}\")\n",
    "ax.set(xlabel=\"m (build periods before harvest)\", ylabel=\"value at true state\",\n",
    "       title=f\"Same optimizer, different twins: regret {reg:.4f} — seed 26215\")\n",
    "ax.legend(frameon=False, fontsize=9); ax.grid(alpha=.25); plt.tight_layout(); plt.show()\n",
    "print(f\"K true {Kt:.4f} | sync est {Ks:.4f} -> m*={ms} | open est {Ko:.4f} -> m*={mo} | regret {reg:.4f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "46e16145",
   "metadata": {},
   "source": [
    "## Panel 3 — Autonomous stability: the loop's dial\n",
    "Autonomous Enterprise Transformation (Def.): the twin recommends, the\n",
    "enterprise applies, the twin re-syncs. With feedback\n",
    "$u = 0.1K^{tar} + c(K^{tar} - K)$ the tracking gap contracts at $|0.9 - c|$\n",
    "per step (Autonomous Enterprise Stability Theorem's scalar face): $c = 0.9$\n",
    "is deadbeat (gap dead in one step), the grid's last stable setting is\n",
    "$c = 1.8$, and beyond $|0.9-c| \\geq 1$ the autonomous loop AMPLIFIES its own\n",
    "corrections — the mathematical form of an automation that oversteers."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "246e8433",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-14T01:05:23.273096Z",
     "iopub.status.busy": "2026-07-14T01:05:23.272875Z",
     "iopub.status.idle": "2026-07-14T01:05:23.488397Z",
     "shell.execute_reply": "2026-07-14T01:05:23.487074Z"
    }
   },
   "outputs": [],
   "source": [
    "cs = [round(0.2*i,1) for i in range(13)]\n",
    "facs = [abs(A-c) for c in cs]\n",
    "fig, ax = plt.subplots(figsize=(7.8,4.0))\n",
    "ax.plot(cs, facs, \"o-\", c=\"#C8A24B\", lw=2.2, ms=5)\n",
    "ax.axhline(1.0, c=\"#B0532F\", ls=\":\", lw=1.5, label=\"stability boundary\")\n",
    "ax.scatter([0.9],[0.0], s=130, c=\"#0B3D2E\", zorder=5, label=\"deadbeat c = 0.9\")\n",
    "ax.set(xlabel=\"feedback gain c\", ylabel=\"gap factor |0.9 − c|\", title=\"Stable below the line; oversteering above — seed 26215\")\n",
    "ax.legend(frameon=False, fontsize=9); ax.grid(alpha=.25); plt.tight_layout(); plt.show()\n",
    "print(f\"fastest c: 0.9 (factor 0.0)   last stable on grid: {c_max_stable()}   gap after 3 steps at c=0.5: {gap_after(3,0.5):.4f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9995b03e",
   "metadata": {},
   "source": [
    "## Validation — agrees with `DCT_V2_Ch15_Lab.xlsx`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "95feaa0d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-14T01:05:23.490606Z",
     "iopub.status.busy": "2026-07-14T01:05:23.490343Z",
     "iopub.status.idle": "2026-07-14T01:05:23.497958Z",
     "shell.execute_reply": "2026-07-14T01:05:23.496915Z"
    }
   },
   "outputs": [],
   "source": [
    "ref = reference_values()\n",
    "expected = {\"rmse_open\":2.8811,\"rmse_sync\":0.9313,\"sync_advantage\":3.0938,\n",
    " \"contraction_factor\":0.45,\"K_true_T\":5.626,\"K_sync_T\":5.8598,\"K_open_T\":10.0,\n",
    " \"mstar_sync\":1,\"mstar_open\":0,\"regret_open_twin\":1.3605,\n",
    " \"c_fastest\":0.9,\"factor_at_09\":0.0,\"c_max_stable\":1.8,\"gap3_c05\":0.128}\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 26215.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1d4549e8",
   "metadata": {},
   "source": [
    "**Next**: Exercises 15.5–15.9 (Part C) sweep the gain g against noise levels and find the fidelity optimum; AXIOM-15's twin cockpit runs truth and twin side by side with a shock button. Chapter 16 is the capstone: every arc, in the field. Solutions: IM Vol. II, Ch. 15."
   ]
  }
 ],
 "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
}
