{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "2ab30008",
   "metadata": {},
   "source": [
    "# DCT Laboratory — Volume II, Chapter 4\n",
    "## Nonlinear Enterprise Optimization\n",
    "**Seed `26204`** · Companion to the chapter and AXIOM Module **AXIOM-04 (Vol. II)**\n",
    "\n",
    "Outside the convex kingdom: a quartic with **two local minima of very different\n",
    "depths** (local ≠ global, and gradient descent's starting point decides which\n",
    "story it tells), a **nonlinear KKT point solved exactly** on a circular capacity\n",
    "boundary, and the **Enterprise Sensitivity Theorem** verified — the multiplier\n",
    "is the derivative of optimal value. Mirrored in `DCT_V2_Ch04_Lab.xlsx`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1e166597",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-13T23:49:18.761040Z",
     "iopub.status.busy": "2026-07-13T23:49:18.760272Z",
     "iopub.status.idle": "2026-07-13T23:49:19.252605Z",
     "shell.execute_reply": "2026-07-13T23:49:19.251417Z"
    }
   },
   "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 = 26204\n",
    "# --- Panel 1: two local minima ---\n",
    "def f1(x): return x**4 - 8*x**2 + 3*x + 20\n",
    "GRID1 = np.arange(-3.0, 3.05, 0.05)\n",
    "# --- Panel 2: nonlinear KKT on the circle x^2+y^2 <= c ---\n",
    "# min (x-2)^2+(y-2)^2 ; optimum on boundary: x=y=sqrt(c/2), lam = 2/sqrt(2c) - 1\n",
    "C0 = 2.0\n",
    "def fstar(c):\n",
    "    t = np.sqrt(c/2.0)\n",
    "    return 2*(t-2)**2\n",
    "def lam_of(c): return 2/np.sqrt(c/2.0) - 1\n",
    "\n",
    "def local_minima():\n",
    "    xs = GRID1; ys = f1(xs)\n",
    "    neg = xs < 0; pos = xs > 0\n",
    "    xn = float(xs[neg][np.argmin(ys[neg])]); xp = float(xs[pos][np.argmin(ys[pos])])\n",
    "    return (xn, float(f1(xn))), (xp, float(f1(xp)))\n",
    "\n",
    "def reference_values():\n",
    "    (xn, fn), (xp, fp) = local_minima()\n",
    "    fd = (fstar(C0+0.2)-fstar(C0))/0.2\n",
    "    return {\n",
    "        \"xmin_neg\": round(xn,4), \"f_neg\": round(fn,4),\n",
    "        \"xmin_pos\": round(xp,4), \"f_pos\": round(fp,4),\n",
    "        \"global_is_neg\": int(fn < fp),\n",
    "        \"local_gap\": round(abs(fp-fn),4),\n",
    "        \"x_kkt\": round(float(np.sqrt(C0/2)),4), \"y_kkt\": round(float(np.sqrt(C0/2)),4),\n",
    "        \"lambda_kkt\": round(float(lam_of(C0)),4),\n",
    "        \"f_kkt\": round(float(fstar(C0)),4),\n",
    "        \"fd_sensitivity\": round(float(fd),4),\n",
    "        \"envelope_neg_lambda\": round(float(-lam_of(C0)),4),\n",
    "    }\n",
    "if __name__ == \"__main__\":\n",
    "    [print(f\"{k:20s} {v}\") for k,v in reference_values().items()]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "337fa072",
   "metadata": {},
   "source": [
    "## Panel 1 — Two basins, one truth\n",
    "$f(x) = x^4 - 8x^2 + 3x + 20$: local minima near $x = -2.1$ (value $-2.13$) and\n",
    "$x = +1.9$ (value $+9.85$) — a gap of **11.98** between the stories. Nonlinear\n",
    "Enterprise Problems May Possess Multiple Local Optima (Prop.): a descent method\n",
    "started at $x > 0.2$ converges, certifies first-order optimality, and reports a\n",
    "value 12 worse than the truth. The grid — exhaustive, humble — finds the global."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "26eed8bb",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-13T23:49:19.254812Z",
     "iopub.status.busy": "2026-07-13T23:49:19.254398Z",
     "iopub.status.idle": "2026-07-13T23:49:19.497911Z",
     "shell.execute_reply": "2026-07-13T23:49:19.496743Z"
    }
   },
   "outputs": [],
   "source": [
    "(xn, fn), (xp, fp) = local_minima()\n",
    "fig, ax = plt.subplots(figsize=(8.2,4.4))\n",
    "ax.plot(GRID1, f1(GRID1), c=\"#1B6B52\", lw=2.2)\n",
    "ax.scatter([xn],[fn], c=\"#0B3D2E\", s=110, zorder=5, label=f\"global: f({xn:.1f}) = {fn:.2f}\")\n",
    "ax.scatter([xp],[fp], c=\"#C8A24B\", s=110, zorder=5, label=f\"local trap: f({xp:.1f}) = {fp:.2f}\")\n",
    "ax.set(xlabel=\"x\", ylabel=\"f(x)\", title=\"x⁴ − 8x² + 3x + 20 — two basins (seed 26204)\")\n",
    "ax.legend(frameon=False); ax.grid(alpha=.25); plt.tight_layout(); plt.show()\n",
    "print(f\"gap between local stories: {abs(fp-fn):.4f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "091a7ff3",
   "metadata": {},
   "source": [
    "## Panel 2 — KKT, nonlinear case, solved exactly\n",
    "Minimize $(x-2)^2 + (y-2)^2$ subject to $x^2 + y^2 \\le 2$. The target $(2,2)$\n",
    "is infeasible; the optimum sits on the boundary at $(1,1)$ with $f^* = 2$.\n",
    "KKT: $\\nabla f + \\lambda \\nabla g = 0$ gives $\\lambda^* = 1$ (residual\n",
    "$0.0000$), complementary slackness holds ($g = 0$, $\\lambda > 0$), and the\n",
    "constraint qualification is satisfied ($\\nabla g \\ne 0$ on the boundary) —\n",
    "every clause of the KKT Optimality Theorem, checkable on one circle."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "85ded3c8",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-13T23:49:19.499920Z",
     "iopub.status.busy": "2026-07-13T23:49:19.499717Z",
     "iopub.status.idle": "2026-07-13T23:49:19.702157Z",
     "shell.execute_reply": "2026-07-13T23:49:19.701103Z"
    }
   },
   "outputs": [],
   "source": [
    "th = np.linspace(0, np.pi/2, 100)\n",
    "fig, ax = plt.subplots(figsize=(6.4,5.0))\n",
    "ax.plot(np.sqrt(2)*np.cos(th), np.sqrt(2)*np.sin(th), c=\"#1B6B52\", lw=2, label=\"capacity boundary x²+y²=2\")\n",
    "ax.scatter([2],[2], c=\"#8A8F8B\", s=90, label=\"target (2,2): infeasible\")\n",
    "ax.scatter([1],[1], c=\"#0B3D2E\", s=120, zorder=5, label=\"KKT point (1,1), λ=1\")\n",
    "ax.plot([1,2],[1,2], c=\"#C8A24B\", lw=1.5, ls=\"--\")\n",
    "ax.set(xlim=(0,2.4), ylim=(0,2.4), xlabel=\"x\", ylabel=\"y\", title=\"The gradient pushes; the wall pushes back — λ measures how hard\")\n",
    "ax.legend(frameon=False, fontsize=9); ax.grid(alpha=.2); plt.tight_layout(); plt.show()\n",
    "gfx, ggx = 2*(1-2), 2*1\n",
    "print(f\"KKT stationarity residual at (1,1), λ=1: {gfx + 1*ggx}\")\n",
    "print(f\"f* = {fstar(C0):.4f}   complementary slackness: g(1,1) = {1+1-C0:.4f}, λ = {lam_of(C0):.4f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eb75e44c",
   "metadata": {},
   "source": [
    "## Panel 3 — The multiplier is a derivative\n",
    "Enlarge the capacity: $c = 2 \\to 2.2$. The optimal value falls from 2 to\n",
    "1.8095 — finite-difference rate $-0.9524 \\approx -\\lambda^* = -1$ (the\n",
    "Enterprise Sensitivity Theorem: multipliers are the derivatives of optimal\n",
    "value in constraint resources). Chapter 3's shadow price and this envelope\n",
    "computation are the same fact wearing dual and primal clothes — the volume's\n",
    "recurring number, met from its second side."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fb34edfe",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-13T23:49:19.704189Z",
     "iopub.status.busy": "2026-07-13T23:49:19.703992Z",
     "iopub.status.idle": "2026-07-13T23:49:19.882036Z",
     "shell.execute_reply": "2026-07-13T23:49:19.880972Z"
    }
   },
   "outputs": [],
   "source": [
    "cs = np.arange(1.0, 4.05, 0.1)\n",
    "fig, ax = plt.subplots(figsize=(7.8,4.0))\n",
    "ax.plot(cs, [fstar(c) for c in cs], c=\"#C8A24B\", lw=2.4, label=\"optimal value f*(c)\")\n",
    "ax.scatter([C0],[fstar(C0)], c=\"#0B3D2E\", s=100, zorder=5)\n",
    "ax.annotate(f\"slope ≈ −λ* = −1\", (C0+0.06, fstar(C0)+0.25), fontsize=10, color=\"#0B3D2E\")\n",
    "ax.set(xlabel=\"capacity c\", ylabel=\"f*(c)\", title=\"Value falls as the wall recedes — at the multiplier's rate\")\n",
    "ax.legend(frameon=False); ax.grid(alpha=.25); plt.tight_layout(); plt.show()\n",
    "print(f\"fd sensitivity (h=0.2): {(fstar(C0+0.2)-fstar(C0))/0.2:.4f}   vs −λ* = {-lam_of(C0):.4f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c5135221",
   "metadata": {},
   "source": [
    "## Validation — agrees with `DCT_V2_Ch04_Lab.xlsx`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a807cc83",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-13T23:49:19.883924Z",
     "iopub.status.busy": "2026-07-13T23:49:19.883729Z",
     "iopub.status.idle": "2026-07-13T23:49:19.890036Z",
     "shell.execute_reply": "2026-07-13T23:49:19.888968Z"
    }
   },
   "outputs": [],
   "source": [
    "ref = reference_values()\n",
    "expected = {\"xmin_neg\":-2.1,\"f_neg\":-2.1319,\"xmin_pos\":1.9,\"f_pos\":9.8521,\"global_is_neg\":1,\n",
    " \"local_gap\":11.984,\"x_kkt\":1.0,\"y_kkt\":1.0,\"lambda_kkt\":1.0,\"f_kkt\":2.0,\n",
    " \"fd_sensitivity\":-0.9524,\"envelope_neg_lambda\":-1.0}\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 26204.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1d9ada5e",
   "metadata": {},
   "source": [
    "**Next**: Exercises 4.9–4.12 (Part C) move the quartic's basins and re-run the trap; AXIOM-04's KKT console checks every clause on your problem. Chapter 5 puts the clock back in. Solutions: IM Vol. II, Ch. 4."
   ]
  }
 ],
 "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
}
