{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "5814fc08",
   "metadata": {},
   "source": [
    "# DCT Laboratory — Volume I, Chapter 9\n",
    "## Enterprise State Architecture\n",
    "**Seed `26109`** · Companion to the chapter and AXIOM Module **AXIOM-09**\n",
    "\n",
    "The state vector gets its wiring diagram: **which coordinates depend on which**.\n",
    "Six components — Treasury, Operations, Technology, Sales, HR, Risk — a weighted\n",
    "dependency graph, its topology metrics, a shock propagated through it, and path\n",
    "counting via matrix powers. Mirrored in `DCT_V1_Ch09_Lab.xlsx`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "55888b72",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-13T22:42:41.911226Z",
     "iopub.status.busy": "2026-07-13T22:42:41.910094Z",
     "iopub.status.idle": "2026-07-13T22:42:42.410842Z",
     "shell.execute_reply": "2026-07-13T22:42:42.409629Z"
    }
   },
   "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 = 26109\n",
    "NODES = [\"Treasury\",\"Operations\",\"Technology\",\"Sales\",\"HR\",\"Risk\"]\n",
    "# W[i,j] = strength with which a shock to j impacts i (column-source convention)\n",
    "W = np.array([\n",
    " #  Tre  Ops  Tec  Sal  HR   Rsk\n",
    " [0.00,0.30,0.10,0.40,0.00,0.20],  # Treasury\n",
    " [0.10,0.00,0.45,0.20,0.25,0.00],  # Operations\n",
    " [0.15,0.05,0.00,0.00,0.20,0.00],  # Technology\n",
    " [0.00,0.35,0.30,0.00,0.10,0.00],  # Sales\n",
    " [0.05,0.10,0.00,0.00,0.00,0.00],  # HR\n",
    " [0.20,0.15,0.25,0.15,0.05,0.00],  # Risk\n",
    "])\n",
    "ADJ = (W > 0).astype(int)\n",
    "\n",
    "def density():\n",
    "    n = len(NODES)\n",
    "    return ADJ.sum()/(n*(n-1))\n",
    "\n",
    "def propagate(shock_node=\"Technology\", rounds=3, size=10.0):\n",
    "    e = np.zeros(6); e[NODES.index(shock_node)] = size\n",
    "    impacts = [e]\n",
    "    for _ in range(rounds):\n",
    "        impacts.append(W @ impacts[-1])\n",
    "    return np.array(impacts)          # (rounds+1, 6)\n",
    "\n",
    "def paths_len3(src=\"Technology\", dst=\"Treasury\"):\n",
    "    A3 = np.linalg.matrix_power(ADJ, 3)\n",
    "    return int(A3[NODES.index(dst), NODES.index(src)])\n",
    "\n",
    "def reference_values():\n",
    "    imp = propagate()\n",
    "    cum = imp[1:].sum(axis=0)         # cumulative over rounds 1..3\n",
    "    return {\n",
    "        \"density\":         round(float(density()), 4),\n",
    "        \"out_degree_tech\": int(ADJ[:, NODES.index(\"Technology\")].sum()),\n",
    "        \"ops_round1\":      round(float(imp[1, NODES.index(\"Operations\")]), 4),\n",
    "        \"treasury_round2\": round(float(imp[2, NODES.index(\"Treasury\")]), 4),\n",
    "        \"cum_impact_total\":round(float(cum.sum()), 4),\n",
    "        \"cum_impact_risk\": round(float(cum[NODES.index(\"Risk\")]), 4),\n",
    "        \"paths3_tech_treasury\": paths_len3(),\n",
    "        \"spectral_radius_W\":   round(float(max(abs(np.linalg.eigvals(W)))), 4),\n",
    "    }\n",
    "if __name__ == \"__main__\":\n",
    "    [print(f\"{k:24s} {v}\") for k,v in reference_values().items()]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1688f738",
   "metadata": {},
   "source": [
    "## Panel 1 — The dependency graph, as a matrix\n",
    "$W_{ij}$ = the strength with which a shock to component $j$ impacts component\n",
    "$i$. The unweighted skeleton `ADJ` is the **Enterprise Topology** (Def.); the\n",
    "Dependency Graph Is Behaviorally Minimal and Sufficient (Prop.) — it carries\n",
    "exactly the propagation structure, nothing else."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e4c09d93",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-13T22:42:42.412956Z",
     "iopub.status.busy": "2026-07-13T22:42:42.412684Z",
     "iopub.status.idle": "2026-07-13T22:42:42.672644Z",
     "shell.execute_reply": "2026-07-13T22:42:42.671484Z"
    }
   },
   "outputs": [],
   "source": [
    "fig, ax = plt.subplots(figsize=(6.6,5.4))\n",
    "im = ax.imshow(W, cmap=\"Greens\", vmin=0, vmax=0.5)\n",
    "ax.set_xticks(range(6), NODES, rotation=35, ha=\"right\")\n",
    "ax.set_yticks(range(6), NODES)\n",
    "for i in range(6):\n",
    "    for j in range(6):\n",
    "        if W[i,j]>0: ax.text(j, i, f\"{W[i,j]:.2f}\", ha=\"center\", va=\"center\",\n",
    "                             color=\"white\" if W[i,j]>0.3 else \"#0B3D2E\", fontsize=9)\n",
    "ax.set(title=\"W: shock to column j hits row i (seed 26109)\")\n",
    "plt.colorbar(im, shrink=.8); plt.tight_layout(); plt.show()\n",
    "print(f\"density: {density():.4f}   out-degree(Technology): {ADJ[:,NODES.index('Technology')].sum()}\")\n",
    "print(f\"spectral radius of W: {max(abs(np.linalg.eigvals(W))):.4f}  (<1: shocks die out)\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a0d8e982",
   "metadata": {},
   "source": [
    "## Panel 2 — A shock, propagated\n",
    "Size-10 shock to **Technology**; rounds are $s_k = W s_{k-1}$ (Structural\n",
    "Dependency Theorem: propagation follows the graph, and only the graph).\n",
    "Operations takes 4.5 in round 1; Treasury takes 3.05 in round 2 — **two hops\n",
    "from a technology event to a funding event**, along edges anyone could have\n",
    "read off the matrix in advance."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6d73dc2d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-13T22:42:42.674534Z",
     "iopub.status.busy": "2026-07-13T22:42:42.674338Z",
     "iopub.status.idle": "2026-07-13T22:42:42.881063Z",
     "shell.execute_reply": "2026-07-13T22:42:42.879946Z"
    }
   },
   "outputs": [],
   "source": [
    "imp = propagate()\n",
    "fig, ax = plt.subplots(figsize=(8.4,4.4))\n",
    "bottom = np.zeros(4)\n",
    "colors = [\"#0B3D2E\",\"#1B6B52\",\"#C8A24B\",\"#8A8F8B\",\"#D9BE7A\",\"#B0532F\"]\n",
    "for i,(nm,c) in enumerate(zip(NODES,colors)):\n",
    "    ax.bar(range(4), imp[:,i], bottom=bottom, label=nm, color=c, width=.6)\n",
    "    bottom += imp[:,i]\n",
    "ax.set_xticks(range(4), [\"shock (r0)\",\"round 1\",\"round 2\",\"round 3\"])\n",
    "ax.set(ylabel=\"impact\", title=\"Shock to Technology: who absorbs it, round by round\")\n",
    "ax.legend(frameon=False, ncols=3, fontsize=9); ax.grid(alpha=.25, axis=\"y\")\n",
    "plt.tight_layout(); plt.show()\n",
    "cum = imp[1:].sum(axis=0)\n",
    "for nm, v in zip(NODES, cum): print(f\"cumulative impact on {nm:12s} {v:7.4f}\")\n",
    "print(f\"total propagated impact (3 rounds): {cum.sum():.4f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9c3ad11a",
   "metadata": {},
   "source": [
    "## Panel 3 — Paths and complexity\n",
    "$(A^3)_{ij}$ counts length-3 dependency paths — **8 distinct three-hop routes**\n",
    "from Technology to Treasury. Complexity Grows with Interdependence (Prop.):\n",
    "density 0.70 on six nodes already generates this routing richness, which is\n",
    "exactly what makes architectural analysis non-optional at enterprise scale."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dfb7b0fe",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-13T22:42:42.883007Z",
     "iopub.status.busy": "2026-07-13T22:42:42.882799Z",
     "iopub.status.idle": "2026-07-13T22:42:42.889512Z",
     "shell.execute_reply": "2026-07-13T22:42:42.888395Z"
    }
   },
   "outputs": [],
   "source": [
    "A3 = np.linalg.matrix_power(ADJ, 3)\n",
    "print(\"A^3 (length-3 path counts):\")\n",
    "print(\"            \" + \"  \".join(f\"{n[:4]:>5s}\" for n in NODES))\n",
    "for i,nm in enumerate(NODES):\n",
    "    print(f\"{nm:12s}\" + \"  \".join(f\"{A3[i,j]:5d}\" for j in range(6)))\n",
    "print(f\"\\npaths of length 3, Technology → Treasury: {paths_len3()}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8058496a",
   "metadata": {},
   "source": [
    "## Validation — agrees with `DCT_V1_Ch09_Lab.xlsx`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b9859e2e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-13T22:42:42.891345Z",
     "iopub.status.busy": "2026-07-13T22:42:42.891170Z",
     "iopub.status.idle": "2026-07-13T22:42:42.897485Z",
     "shell.execute_reply": "2026-07-13T22:42:42.896622Z"
    }
   },
   "outputs": [],
   "source": [
    "ref = reference_values()\n",
    "expected = {\"density\":0.7,\"out_degree_tech\":4,\"ops_round1\":4.5,\"treasury_round2\":3.05,\n",
    " \"cum_impact_total\":22.8738,\"cum_impact_risk\":4.895,\"paths3_tech_treasury\":8,\"spectral_radius_W\":0.625}\n",
    "for k,v in expected.items():\n",
    "    assert abs(ref[k]-v)<5e-4, f\"MISMATCH {k}\"\n",
    "    print(f\"PASS  {k:24s} {ref[k]}\")\n",
    "print(\"\\nAll checkpoints agree — seed 26109.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2a8fc8c0",
   "metadata": {},
   "source": [
    "**Next**: Exercises 9.9–9.12 (Part C) rewire the graph and re-propagate; AXIOM-09's architecture canvas makes the matrix draggable. Solutions: IM Ch. 9."
   ]
  }
 ],
 "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
}
