{
 "nbformat": 4,
 "nbformat_minor": 5,
 "metadata": {
  "kernelspec": {
   "name": "python3",
   "display_name": "Python 3"
  },
  "language_info": {
   "name": "python"
  },
  "colab": {
   "provenance": []
  }
 },
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Day 60: Phase 4 Capstone: DAGs Meet Potential Outcomes (SWIGs)\n",
    "\n",
    "*Beginner to Ninja: Math for Causal Inference*\n",
    "\n",
    "Single-world intervention graphs split each treated node into the value it would have taken and the value we set, so potential outcomes appear in the graph. d-separation in the split graph reads off Phase 3's ignorability assumptions directly, unifying the two languages of causal inference.\n",
    "\n",
    "**Learning outcomes**\n",
    "\n",
    "- Construct the single-world intervention graph (SWIG) for one or several interventions.\n",
    "- Read conditional ignorability, and its failure, from d-separation in a SWIG.\n",
    "- Derive the adjustment formula and the g-formula from SWIG independences plus consistency.\n",
    "- Carry a policy question through the whole Phase 4 toolkit (DAG, identification, adjustment set, bad controls, selection and mediation)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Theory\n",
    "\n",
    "### Two languages, one model\n",
    "\n",
    "Phase 3 wrote assumptions with potential outcomes ($Y(a) \\perp\\!\\!\\!\\perp A \\mid L$). Phase 4 wrote them with graphs (back-door paths). **Single-world intervention graphs** (Richardson and Robins, 2013) put the potential outcomes into the graph, so ignorability can be checked by d-separation.\n",
    "\n",
    "### Node splitting\n",
    "\n",
    "To intervene on $A$ and set it to $a$:\n",
    "\n",
    "1. Split $A$ into two halves: the **random half** $A$ (the value it would naturally take), which keeps all incoming arrows, and the **fixed half** $a$, which keeps all outgoing arrows.\n",
    "2. Relabel every descendant $V$ of $a$ as the potential outcome $V(a)$.\n",
    "\n",
    "Example: $L \\to A \\to Y$, $L \\to Y$ becomes\n",
    "\n",
    "$$\n",
    "L \\to A, \\qquad a \\to Y(a), \\qquad L \\to Y(a).\n",
    "$$\n",
    "\n",
    "There is no edge between $A$ and $Y(a)$: the natural treatment and the potential outcome are linked only through $L$.\n",
    "\n",
    "### Reading ignorability\n",
    "\n",
    "Apply d-separation in the SWIG (fixed nodes are constants and can be treated as conditioned on):\n",
    "\n",
    "$$\n",
    "\\big(Y(a) \\perp\\!\\!\\!\\perp A \\mid L\\big)_{\\text{SWIG}} \\;\\Longrightarrow\\; Y(a) \\perp\\!\\!\\!\\perp A \\mid L.\n",
    "$$\n",
    "\n",
    "With consistency ($A = a \\Rightarrow Y(a) = Y$) and positivity, this gives the adjustment formula\n",
    "\n",
    "$$\n",
    "E[Y(a)] = \\sum_l E[Y \\mid A = a, L = l]\\,P(l).\n",
    "$$\n",
    "\n",
    "The back-door criterion holds for $L$ exactly when $L$ d-separates $A$ from $Y(a)$ in the SWIG.\n",
    "\n",
    "### Several interventions\n",
    "\n",
    "For sequential treatments $A_1 \\to L \\to A_2 \\to Y$, split both: the SWIG contains $L(a_1)$, $A_2(a_1)$ and $Y(a_1, a_2)$. **Sequential ignorability**, $Y(a_1, a_2) \\perp\\!\\!\\!\\perp A_1$ and $Y(a_1, a_2) \\perp\\!\\!\\!\\perp A_2(a_1) \\mid L(a_1), A_1$, can be read off, and it yields the **g-formula** of Day 55.\n",
    "\n",
    "### Single world, by design\n",
    "\n",
    "A SWIG represents one intervention at a time. It cannot express **cross-world** statements like $Y(1, m) \\perp\\!\\!\\!\\perp M(0)$, which natural mediation effects require (Day 58). That is a feature: assumptions that no experiment could ever check stay visibly separate.\n",
    "\n",
    "### Phase 4 in one table\n",
    "\n",
    "| Question | Tool (Day) |\n",
    "|---|---|\n",
    "| What does my causal story imply? | DAG, factorisation, d-separation (46–49) |\n",
    "| What happens if we intervene? | SCM and the do-operator (50–51) |\n",
    "| Which covariates should I adjust for? | back-door criterion, good and bad controls (52–53) |\n",
    "| Confounder unobserved? | front door, do-calculus, ID algorithm (54–56) |\n",
    "| Who is in my sample? | selection nodes and recoverability (57) |\n",
    "| Through which channel? | mediation (58) |\n",
    "| What would have happened to this unit? | counterfactuals (59) |\n",
    "| Does my ignorability assumption hold in my graph? | SWIGs (60) |\n",
    "\n",
    "## Part 1 · Warm-up"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 1 · Draw a SWIG\n",
    "\n",
    "Draw the SWIG for intervening on $A$ in $L \\to A \\to Y$, $L \\to Y$, $A \\to M \\to Y$.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Split $A$: random half $A$ keeps $L \\to A$; fixed half $a$ keeps $a \\to Y$ and $a \\to M$.\n",
    "\n",
    "**Step 2.** Descendants of $a$: $M$ and $Y$ become $M(a)$ and $Y(a)$.\n",
    "\n",
    "**Answer:** $L \\to A$; $L \\to Y(a)$; $a \\to M(a) \\to Y(a)$; $a \\to Y(a)$.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 2 · Read ignorability\n",
    "\n",
    "In the SWIG of Exercise 1, is $Y(a) \\perp\\!\\!\\!\\perp A$? Is $Y(a) \\perp\\!\\!\\!\\perp A \\mid L$?\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Paths from $A$ to $Y(a)$: $A \\leftarrow L \\to Y(a)$ only ($A$ has no outgoing edges).\n",
    "\n",
    "**Step 2.** Unconditioned, the fork at $L$ is open: not independent.\n",
    "\n",
    "**Step 3.** Given $L$, it is blocked: $Y(a) \\perp\\!\\!\\!\\perp A \\mid L$.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 3 · Unobserved confounding\n",
    "\n",
    "In $U \\to A \\to Y$, $U \\to Y$ ($U$ unobserved), draw the SWIG and decide whether any observed variable makes $A$ ignorable.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** SWIG: $U \\to A$, $U \\to Y(a)$, $a \\to Y(a)$.\n",
    "\n",
    "**Step 2.** The path $A \\leftarrow U \\to Y(a)$ is open unless $U$ is conditioned on.\n",
    "\n",
    "**Answer:** no observed set works; $Y(a) \\not\\perp\\!\\!\\!\\perp A$. This is the bow, non-identified (Day 56).\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 4 · Two interventions\n",
    "\n",
    "Draw the SWIG for intervening on $A_1$ and $A_2$ in $A_1 \\to L \\to A_2 \\to Y$, $A_1 \\to Y$, $U \\to L$, $U \\to Y$.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Split $A_1$ into $A_1 \\mid a_1$; $A_1$ has no parents, $a_1$ points to $L(a_1)$ and $Y$.\n",
    "\n",
    "**Step 2.** Split $A_2$: the random half is $A_2(a_1)$ (a descendant of $a_1$), keeping $L(a_1) \\to A_2(a_1)$; the fixed half $a_2$ points to $Y$.\n",
    "\n",
    "**Step 3.** $Y$ becomes $Y(a_1, a_2)$, with parents $a_1, a_2, U$.\n",
    "\n",
    "**Answer:** $a_1 \\to L(a_1) \\to A_2(a_1)$, $U \\to L(a_1)$, $U \\to Y(a_1, a_2)$, $a_1 \\to Y(a_1, a_2)$, $a_2 \\to Y(a_1, a_2)$, and $A_1$ isolated.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 5 · What a SWIG cannot show\n",
    "\n",
    "Why can't a SWIG display the assumption $Y(1, m) \\perp\\!\\!\\!\\perp M(0)$ used for natural direct effects?\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** A SWIG contains variables from a single intervention: either $M(0)$ (intervening on $A$ with $a = 0$) or $Y(1, m)$ (intervening on $A$ and $M$ with $a = 1$), not both.\n",
    "\n",
    "**Step 2.** The assumption relates two different interventions on the same unit.\n",
    "\n",
    "**Answer:** it is a cross-world assumption; no single-world graph contains both variables, and no experiment can test it.\n",
    "\n",
    "</details>\n",
    "\n",
    "## Part 2 · Proofs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 6 · From ignorability to the adjustment formula\n",
    "\n",
    "Assume $Y(a) \\perp\\!\\!\\!\\perp A \\mid L$, consistency and positivity. Prove $E[Y(a)] = \\sum_l E[Y \\mid A = a, L = l]P(l)$.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** LIE: $E[Y(a)] = \\sum_l E[Y(a) \\mid L = l]P(l)$.\n",
    "\n",
    "**Step 2.** Ignorability: $E[Y(a) \\mid L = l] = E[Y(a) \\mid A = a, L = l]$ (positivity makes the conditioning event possible).\n",
    "\n",
    "**Step 3.** Consistency: on $\\{A = a\\}$, $Y(a) = Y$, so this is $E[Y \\mid A = a, L = l]$.\n",
    "\n",
    "**Step 4.** Substitute. $\\blacksquare$\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 7 · Back-door implies SWIG separation\n",
    "\n",
    "Suppose $L$ satisfies the back-door criterion for $(A, Y)$ in $G$. Argue that $L$ d-separates $A$ from $Y(a)$ in the SWIG.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** In the SWIG, the random half $A$ has only incoming edges, so every path from $A$ to $Y(a)$ starts with an edge into $A$: these are exactly the back-door paths of $G$ (with descendants of $a$ relabelled).\n",
    "\n",
    "**Step 2.** Directed paths from $A$ in $G$ now start at the fixed node $a$, not at $A$, so they are not $A$–$Y(a)$ paths.\n",
    "\n",
    "**Step 3.** $L$ contains no descendant of $A$, so in the SWIG it contains no potential outcome; conditioning on it opens or blocks the same nodes as in $G$.\n",
    "\n",
    "**Step 4.** $L$ blocks every back-door path in $G$, hence every $A$–$Y(a)$ path in the SWIG. $\\blacksquare$\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 8 · The g-formula from sequential ignorability\n",
    "\n",
    "For the SWIG of Exercise 4 (with no $U$–$A_2$ edge), assume $Y(a_1, a_2) \\perp\\!\\!\\!\\perp A_1$, $Y(a_1, a_2) \\perp\\!\\!\\!\\perp A_2(a_1) \\mid A_1, L(a_1)$, $L(a_1) \\perp\\!\\!\\!\\perp A_1$, and consistency. Prove $E[Y(a_1, a_2)] = \\sum_l E[Y \\mid a_1, l, a_2]P(l \\mid a_1)$.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** By the first assumption and consistency of $L$: $E[Y(a_1, a_2)] = E[Y(a_1, a_2) \\mid A_1 = a_1] = \\sum_l E[Y(a_1, a_2) \\mid A_1 = a_1, L = l]P(l \\mid a_1)$.\n",
    "\n",
    "**Step 2.** Second assumption: condition additionally on $A_2 = a_2$ (which equals $A_2(a_1)$ when $A_1 = a_1$): $E[Y(a_1, a_2) \\mid a_1, l] = E[Y(a_1, a_2) \\mid a_1, l, A_2 = a_2]$.\n",
    "\n",
    "**Step 3.** Consistency: when $A_1 = a_1$ and $A_2 = a_2$, $Y(a_1, a_2) = Y$.\n",
    "\n",
    "**Step 4.** Substitute. $\\blacksquare$\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 9 · M-bias in a SWIG\n",
    "\n",
    "In $U_1 \\to A$, $U_1 \\to M$, $U_2 \\to M$, $U_2 \\to Y$, $A \\to Y$, show with the SWIG that $Y(a) \\perp\\!\\!\\!\\perp A$ but not $Y(a) \\perp\\!\\!\\!\\perp A \\mid M$.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** SWIG: $U_1 \\to A$, $U_1 \\to M$, $U_2 \\to M$, $U_2 \\to Y(a)$, $a \\to Y(a)$. $M$ is not a descendant of $a$, so it is not relabelled.\n",
    "\n",
    "**Step 2.** The only $A$–$Y(a)$ path: $A \\leftarrow U_1 \\to M \\leftarrow U_2 \\to Y(a)$, with a collider at $M$.\n",
    "\n",
    "**Step 3.** Unconditioned: blocked, so $Y(a) \\perp\\!\\!\\!\\perp A$. Given $M$: open.\n",
    "\n",
    "**Answer:** treatment is marginally ignorable but not given $M$: the potential-outcome version of M-bias. $\\blacksquare$\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 10 · Why the front door is not \"ignorability\"\n",
    "\n",
    "In the front-door graph $U \\to A$, $U \\to Y$, $A \\to M \\to Y$, show that no observed set makes $Y(a) \\perp\\!\\!\\!\\perp A$, and explain how identification still succeeds.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** SWIG: $U \\to A$, $U \\to Y(a)$, $a \\to M(a) \\to Y(a)$.\n",
    "\n",
    "**Step 2.** The path $A \\leftarrow U \\to Y(a)$ is open, and the only observed candidate, $M(a)$, is not on it.\n",
    "\n",
    "**Step 3.** So no conditioning set gives ignorability of $A$.\n",
    "\n",
    "**Step 4.** Identification works through two separate ignorabilities: $M(a) \\perp\\!\\!\\!\\perp A$ (in this SWIG) and $Y(m) \\perp\\!\\!\\!\\perp M \\mid A$ (in the SWIG for intervening on $M$), chained as in Day 54. $\\blacksquare$\n",
    "\n",
    "</details>\n",
    "\n",
    "## Part 3 · Applied"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 11 · Microfinance and women's decision-making\n",
    "\n",
    "An NGO asks whether microloans to women ($A$) increase their say in household decisions ($Y$). Available variables: baseline education $E$, baseline household assets $H$, village $V$ (loan officers choose villages based on market access, which also affects bargaining norms), business started after the loan $B$, and survey participation $S$ (husbands sometimes refuse interviews when the wife's power rises). Lay out a Phase 4 analysis plan.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** DAG: $E, H, V \\to A$; $E, H, V \\to Y$; $A \\to B \\to Y$; $A \\to Y$; $Y \\to S$.\n",
    "\n",
    "**Step 2.** Identification: $\\{E, H, V\\}$ blocks the back-door paths and contains no descendants: admissible (SWIG: $Y(a) \\perp\\!\\!\\!\\perp A \\mid E, H, V$).\n",
    "\n",
    "**Step 3.** Bad controls: business $B$ is a mediator; exclude for the total effect.\n",
    "\n",
    "**Step 4.** Selection: $S$ depends on $Y$, so analysing respondents biases results; collect refusal data and use bounds or weights based on predictors of refusal (Day 57).\n",
    "\n",
    "**Step 5.** Mechanism: the share via business start is a mediation question needing $B$–$Y$ unconfoundedness (Day 58).\n",
    "\n",
    "**Answer:** adjust for $E, H, V$; don't adjust for $B$; address non-response explicitly; treat mediation as secondary with sensitivity checks.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 12 · Auditing a regression\n",
    "\n",
    "A paper estimates the return to a vocational degree $D$ by regressing earnings $Y$ on $D$, age, parental income, current occupation and current job tenure. Using a DAG, list which controls are appropriate and which are not.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Age and parental income precede the degree and affect earnings: confounders; keep.\n",
    "\n",
    "**Step 2.** Occupation and tenure are affected by the degree: mediators (and colliders if ability affects them): exclude for the total return.\n",
    "\n",
    "**Step 3.** Missing: ability or school grades, which affect both $D$ and $Y$; if unobserved, the estimate remains biased, and a sensitivity analysis is needed.\n",
    "\n",
    "**Answer:** keep age and parental income; drop occupation and tenure; flag ability.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 13 · Sequential HIV treatment, by simulation\n",
    "\n",
    "In the simulated clinic below, treatment at visit 1 ($A_1$, randomised) raises CD4 counts $L$; doctors give treatment at visit 2 ($A_2$) mainly to patients with low CD4; unobserved health $U$ affects CD4 and viral suppression $Y$. The true effect of \"always treat\" versus \"never treat\" is 0.45. Compare the naive comparison, a regression on $A_1, L, A_2$, and the g-formula.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Naive comparison (always-treated versus never-treated patients): biased, because $A_2$ depends on $L$, which depends on $U$. The code gives about 0.38.\n",
    "\n",
    "**Step 2.** Regression on $A_1, L, A_2$: $L$ is a mediator for $A_1$ and a collider ($A_1 \\to L \\leftarrow U$); the sum of coefficients is about 0.41.\n",
    "\n",
    "**Step 3.** g-formula: $\\sum_l E[Y \\mid a_1, l, a_2]P(l \\mid a_1)$ at $(1, 1)$ minus $(0, 0)$: about 0.45.\n",
    "\n",
    "**Answer:** only the g-formula, justified by sequential ignorability in the SWIG, recovers the effect; Day 96 develops it and its weighting cousin.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 14 · One question, several designs\n",
    "\n",
    "A health system asks for the effect of ICU admission $A$ on 30-day mortality $Y$. For each data situation, name the Phase 4 strategy or say \"not identified\": (a) severity scores used for admission are recorded; (b) severity is not recorded, but admission affects mortality only through hours of ventilation, which are not confounded with mortality given admission; (c) severity is not recorded and nothing else is known; (d) only patients who survived 24 hours are in the data.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** (a) Back-door adjustment for severity (and other admission criteria) (Day 52).\n",
    "\n",
    "**Step 2.** (b) Front door through ventilation hours (Day 54), if its strong assumptions are credible.\n",
    "\n",
    "**Step 3.** (c) The bow: not identified (Day 56); bounds, sensitivity, or an instrument such as bed availability (Phase 5).\n",
    "\n",
    "**Step 4.** (d) Selection on a variable affected by both admission and severity: a collider (Day 57); estimates for survivors are biased unless selection can be modelled.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 15 · Reading a SWIG for a vaccine study\n",
    "\n",
    "Observational vaccine data: age $G \\to$ vaccination $A$, $G \\to$ infection $Y$; health-seeking behaviour $H$ (unobserved) $\\to A$ and $H \\to$ testing $T$; $Y \\to T$; only tested people are analysed. Draw the SWIG for $A$ and decide whether $Y(a) \\perp\\!\\!\\!\\perp A \\mid G$, and what the testing restriction does.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** SWIG: $G \\to A$, $H \\to A$, $G \\to Y(a)$, $a \\to Y(a)$, $H \\to T(a)$, $Y(a) \\to T(a)$ (testing is a descendant of $a$ through $Y$).\n",
    "\n",
    "**Step 2.** Paths from $A$ to $Y(a)$: $A \\leftarrow G \\to Y(a)$ (blocked by $G$) and $A \\leftarrow H \\to T(a) \\leftarrow Y(a)$ (collider at $T(a)$, blocked if not conditioned).\n",
    "\n",
    "**Step 3.** So $Y(a) \\perp\\!\\!\\!\\perp A \\mid G$ in the full population.\n",
    "\n",
    "**Step 4.** Restricting to tested people conditions on $T$, opening $A \\leftarrow H \\to T \\leftarrow Y$.\n",
    "\n",
    "**Answer:** age adjustment suffices in population data, but tested-only data are biased by health-seeking behaviour (the motivation for test-negative designs, which make further assumptions).\n",
    "\n",
    "</details>\n",
    "\n",
    "## Verify in Python"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import networkx as nx\n",
    "\n",
    "dsep = getattr(nx, \"is_d_separator\", None) or nx.d_separated   # name changed in networkx 3.3\n",
    "\n",
    "def swig(G, A):\n",
    "    \"\"\"Split node A into random half A and fixed half 'a'; relabel descendants of A as V(a).\"\"\"\n",
    "    desc = nx.descendants(G, A)\n",
    "    name = lambda v: f\"{v}(a)\" if v in desc else v\n",
    "    S = nx.DiGraph(); S.add_nodes_from([name(v) for v in G] + [\"a\"])\n",
    "    for u, v in G.edges:\n",
    "        S.add_edge(\"a\" if u == A else name(u), name(v))\n",
    "    return S\n",
    "\n",
    "G = nx.DiGraph([(\"L\", \"A\"), (\"L\", \"Y\"), (\"A\", \"Y\"), (\"A\", \"M\"), (\"M\", \"Y\")])\n",
    "S = swig(G, \"A\")\n",
    "print(\"SWIG edges:\", sorted(S.edges))\n",
    "print(\"Y(a) _||_ A:\", dsep(S, {\"Y(a)\"}, {\"A\"}, {\"a\"}), \"| given L:\", dsep(S, {\"Y(a)\"}, {\"A\"}, {\"a\", \"L\"}))\n",
    "\n",
    "Mb = swig(nx.DiGraph([(\"U1\", \"A\"), (\"U1\", \"M\"), (\"U2\", \"M\"), (\"U2\", \"Y\"), (\"A\", \"Y\")]), \"A\")\n",
    "print(\"M-bias: Y(a) _||_ A:\", dsep(Mb, {\"Y(a)\"}, {\"A\"}, {\"a\"}), \"| given M:\", dsep(Mb, {\"Y(a)\"}, {\"A\"}, {\"a\", \"M\"}))\n",
    "\n",
    "V = swig(nx.DiGraph([(\"G\", \"A\"), (\"H\", \"A\"), (\"G\", \"Y\"), (\"A\", \"Y\"), (\"H\", \"T\"), (\"Y\", \"T\")]), \"A\")\n",
    "print(\"vaccine: Y(a) _||_ A | G:\", dsep(V, {\"Y(a)\"}, {\"A\"}, {\"a\", \"G\"}), \"| G, T(a):\", dsep(V, {\"Y(a)\"}, {\"A\"}, {\"a\", \"G\", \"T(a)\"}))\n",
    "\n",
    "# Exercise 13: sequential treatment and the g-formula\n",
    "rng = np.random.default_rng(60); n = 2_000_000\n",
    "def clinic(a1=None, a2=None):\n",
    "    U = rng.random(n) < .5\n",
    "    A1 = rng.random(n) < .5 if a1 is None else np.full(n, a1, bool)\n",
    "    L = rng.random(n) < .2 + .4 * A1 + .3 * U                       # high CD4\n",
    "    A2 = rng.random(n) < (.8 - .6 * L) if a2 is None else np.full(n, a2, bool)\n",
    "    Y = rng.random(n) < .1 + .2 * A1 + .25 * A2 + .3 * U             # viral suppression\n",
    "    return A1, L, A2, Y\n",
    "A1, L, A2, Y = clinic()\n",
    "truth = clinic(1, 1)[3].mean() - clinic(0, 0)[3].mean()\n",
    "g = lambda a1, a2: sum(Y[(A1 == a1) & (L == l) & (A2 == a2)].mean() * (L[A1 == a1] == l).mean() for l in (0, 1))\n",
    "b = np.linalg.lstsq(np.column_stack([np.ones(n), A1, L, A2]).astype(float), Y.astype(float), rcond=None)[0]\n",
    "print(f\"truth {truth:.3f}  naive {Y[A1 & A2].mean() - Y[~A1 & ~A2].mean():.3f}  regression {b[1] + b[3]:.3f}  g-formula {g(1, 1) - g(0, 0):.3f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Key takeaways\n",
    "\n",
    "- A SWIG splits each intervened node into its natural and fixed halves and relabels descendants as potential outcomes.\n",
    "- Ignorability is d-separation in the SWIG; with consistency and positivity it yields the adjustment formula and, for sequential treatments, the g-formula.\n",
    "- SWIGs keep single-world assumptions testable in principle and cross-world assumptions visibly separate. Phase 5 now turns to designs (instruments, differences-in-differences, discontinuities) for when no admissible set is observed."
   ]
  }
 ]
}