{
 "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 52: The Back-Door Criterion\n",
    "\n",
    "*Beginner to Ninja: Math for Causal Inference*\n",
    "\n",
    "A graphical test for which covariates to adjust for. A set Z is admissible if it contains no descendant of the treatment and blocks every path that enters the treatment through an arrow pointing into it; then the adjustment formula identifies the causal effect.\n",
    "\n",
    "**Learning outcomes**\n",
    "\n",
    "- Identify back-door paths and test whether a set satisfies the back-door criterion.\n",
    "- Find all minimal admissible adjustment sets in small DAGs.\n",
    "- Compute causal effects with the back-door adjustment formula and its weighting form.\n",
    "- Connect the criterion to Phase 3's unconfoundedness and to regression adjustment."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Theory\n",
    "\n",
    "### Back-door paths\n",
    "\n",
    "A **back-door path** from $X$ to $Y$ is a path that starts with an arrow **into** $X$: $X \\leftarrow \\cdots Y$. Directed paths $X \\to \\cdots \\to Y$ carry the effect; back-door paths carry confounding.\n",
    "\n",
    "### The criterion\n",
    "\n",
    "A set of variables $\\mathbf Z$ satisfies the **back-door criterion** relative to $(X, Y)$ if\n",
    "\n",
    "1. no node in $\\mathbf Z$ is a descendant of $X$, and\n",
    "2. $\\mathbf Z$ blocks every back-door path from $X$ to $Y$ (in the sense of d-separation).\n",
    "\n",
    "### The back-door adjustment formula\n",
    "\n",
    "If $\\mathbf Z$ satisfies the criterion, the causal effect is identified:\n",
    "\n",
    "$$\n",
    "P(y \\mid \\operatorname{do}(x)) = \\sum_{\\mathbf z} P(y \\mid x, \\mathbf z)\\,P(\\mathbf z).\n",
    "$$\n",
    "\n",
    "Equivalently, in weighting form,\n",
    "\n",
    "$$\n",
    "P(y \\mid \\operatorname{do}(x)) = \\sum_{\\mathbf z} \\frac{P(x, y, \\mathbf z)}{P(x \\mid \\mathbf z)},\n",
    "$$\n",
    "\n",
    "which is IPW (Day 44) with propensity $P(x \\mid \\mathbf z)$.\n",
    "\n",
    "### Why the two conditions?\n",
    "\n",
    "- Condition 2 removes all non-causal association.\n",
    "- Condition 1 keeps the causal paths intact: conditioning on a mediator would block part of the effect; conditioning on a descendant of $X$ that is also affected by $Y$'s causes could open new paths.\n",
    "\n",
    "### Link to potential outcomes\n",
    "\n",
    "In SCM terms, $\\mathbf Z$ satisfying the back-door criterion implies **conditional ignorability**, $Y(x) \\perp\\!\\!\\!\\perp X \\mid \\mathbf Z$ (generalising Day 51, Exercise 9). The criterion is a way to *check* Phase 3's unconfoundedness assumption from a graph, and to choose which $X$ variables to include.\n",
    "\n",
    "### Several valid sets\n",
    "\n",
    "There may be many admissible sets, or none among the observed variables. The parents of $X$ always qualify if they are observed. Different admissible sets give the same estimand but different precision (Day 53).\n",
    "\n",
    "### Linear models\n",
    "\n",
    "If $E[Y \\mid x, \\mathbf z]$ is linear, $E[Y \\mid x, \\mathbf z] = a + bx + \\mathbf c^\\top\\mathbf z$, then by the adjustment formula $E[Y \\mid \\operatorname{do}(x)] = a + bx + \\mathbf c^\\top E[\\mathbf Z]$: the regression coefficient $b$ is the causal effect when $\\mathbf Z$ is admissible.\n",
    "\n",
    "## Part 1 · Warm-up\n",
    "\n",
    "Exercises 1–3 and 5 use this DAG: $Z_1 \\to Z_3 \\leftarrow Z_2$, $Z_1 \\to X$, $Z_2 \\to Y$, $Z_3 \\to X$, $Z_3 \\to Y$, $X \\to W \\to Y$."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 1 · Find the back-door paths\n",
    "\n",
    "List every back-door path from $X$ to $Y$ and say which are open given no conditioning.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** $X \\leftarrow Z_3 \\to Y$: fork at $Z_3$: open.\n",
    "\n",
    "**Step 2.** $X \\leftarrow Z_1 \\to Z_3 \\to Y$: fork at $Z_1$, chain at $Z_3$: open.\n",
    "\n",
    "**Step 3.** $X \\leftarrow Z_3 \\leftarrow Z_2 \\to Y$: chain at $Z_3$, fork at $Z_2$: open.\n",
    "\n",
    "**Step 4.** $X \\leftarrow Z_1 \\to Z_3 \\leftarrow Z_2 \\to Y$: collider at $Z_3$: blocked.\n",
    "\n",
    "**Answer:** four back-door paths, three open.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 2 · Is $\\{Z_3\\}$ enough?\n",
    "\n",
    "Does $\\{Z_3\\}$ satisfy the back-door criterion?\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** $Z_3$ blocks the first three paths (it is a fork or chain middle on each).\n",
    "\n",
    "**Step 2.** But $Z_3$ is the collider on the fourth path; conditioning on it opens $X \\leftarrow Z_1 \\to Z_3 \\leftarrow Z_2 \\to Y$.\n",
    "\n",
    "**Answer:** no. $Z_3$ is both a confounder and a collider.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 3 · All minimal admissible sets\n",
    "\n",
    "Find every minimal set (no proper subset admissible) among $\\{Z_1, Z_2, Z_3\\}$.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** $Z_3$ must be included: otherwise $X \\leftarrow Z_3 \\to Y$ stays open.\n",
    "\n",
    "**Step 2.** With $Z_3$ included, the fourth path is open unless $Z_1$ or $Z_2$ is also conditioned (each is a fork on it).\n",
    "\n",
    "**Step 3.** $\\{Z_1, Z_3\\}$: blocks paths 1, 2 (at $Z_1$ or $Z_3$), 3 (at $Z_3$) and 4 (at $Z_1$). Admissible.\n",
    "\n",
    "**Step 4.** $\\{Z_2, Z_3\\}$: blocks paths 1, 2 and 3 at $Z_3$, path 4 at $Z_2$. Admissible.\n",
    "\n",
    "**Answer:** $\\{Z_1, Z_3\\}$ and $\\{Z_2, Z_3\\}$ (and their superset $\\{Z_1, Z_2, Z_3\\}$).\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 4 · A one-variable adjustment\n",
    "\n",
    "$Z$ is binary with $P(Z = 1) = 0.3$ and satisfies the back-door criterion. $E[Y \\mid X = 1, Z = 1] = 10$, $E[Y \\mid X = 1, Z = 0] = 6$, $E[Y \\mid X = 0, Z = 1] = 8$, $E[Y \\mid X = 0, Z = 0] = 5$. Compute the average causal effect.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** $E[Y \\mid \\operatorname{do}(X = 1)] = 0.3(10) + 0.7(6) = 3 + 4.2 = 7.2$.\n",
    "\n",
    "**Step 2.** $E[Y \\mid \\operatorname{do}(X = 0)] = 0.3(8) + 0.7(5) = 2.4 + 3.5 = 5.9$.\n",
    "\n",
    "**Answer:** $7.2 - 5.9 = 1.3$.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 5 · Adding $W$\n",
    "\n",
    "Is $\\{Z_1, Z_3, W\\}$ admissible? What does adjusting for it estimate?\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** $W$ is a descendant of $X$, so condition 1 fails.\n",
    "\n",
    "**Step 2.** $W$ is the middle of the only causal path $X \\to W \\to Y$; conditioning on it blocks the effect.\n",
    "\n",
    "**Answer:** not admissible; the adjusted contrast is 0 in population (no direct effect remains), however large the true effect.\n",
    "\n",
    "</details>\n",
    "\n",
    "## Part 2 · Proofs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 6 · Deriving the adjustment formula\n",
    "\n",
    "Assume $\\mathbf Z$ satisfies the back-door criterion. Prove $P(y \\mid \\operatorname{do}(x)) = \\sum_{\\mathbf z} P(y \\mid x, \\mathbf z)P(\\mathbf z)$, using two facts: (a) interventions do not change the distribution of non-descendants of $X$ (Day 51), and (b) if $\\mathbf Z$ blocks all back-door paths, then $P(y \\mid \\operatorname{do}(x), \\mathbf z) = P(y \\mid x, \\mathbf z)$ (proved formally on Day 55).\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Law of total probability in the intervened model: $P(y \\mid \\operatorname{do}(x)) = \\sum_{\\mathbf z} P(y \\mid \\operatorname{do}(x), \\mathbf z)\\,P(\\mathbf z \\mid \\operatorname{do}(x))$.\n",
    "\n",
    "**Step 2.** $\\mathbf Z$ contains no descendant of $X$ (condition 1), so by (a) $P(\\mathbf z \\mid \\operatorname{do}(x)) = P(\\mathbf z)$.\n",
    "\n",
    "**Step 3.** By (b), $P(y \\mid \\operatorname{do}(x), \\mathbf z) = P(y \\mid x, \\mathbf z)$: once $\\mathbf Z$ blocks the back-door paths, the only remaining association between $X$ and $Y$ is causal, so observing $x$ and setting $x$ are the same.\n",
    "\n",
    "**Step 4.** Substitute. $\\blacksquare$\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 7 · Parents are admissible\n",
    "\n",
    "Prove that $\\operatorname{pa}(X)$ satisfies the back-door criterion (assuming $Y \\notin \\operatorname{pa}(X)$).\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Condition 1: a parent of $X$ cannot be a descendant of $X$ (that would create a cycle).\n",
    "\n",
    "**Step 2.** Condition 2: every back-door path starts $X \\leftarrow P$ with $P \\in \\operatorname{pa}(X)$.\n",
    "\n",
    "**Step 3.** At $P$ the path continues out of $P$ ($P \\to X$ points away), so $P$ is a chain or fork middle, not a collider.\n",
    "\n",
    "**Step 4.** $P$ is conditioned, so the path is blocked. $\\blacksquare$\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 8 · The weighting form\n",
    "\n",
    "Prove $\\sum_{\\mathbf z} P(y \\mid x, \\mathbf z)P(\\mathbf z) = \\sum_{\\mathbf z} P(x, y, \\mathbf z)/P(x \\mid \\mathbf z)$.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** $P(x, y, \\mathbf z) = P(y \\mid x, \\mathbf z)P(x \\mid \\mathbf z)P(\\mathbf z)$.\n",
    "\n",
    "**Step 2.** Divide by $P(x \\mid \\mathbf z)$ (positive by overlap): $P(x, y, \\mathbf z)/P(x \\mid \\mathbf z) = P(y \\mid x, \\mathbf z)P(\\mathbf z)$.\n",
    "\n",
    "**Step 3.** Sum over $\\mathbf z$. $\\blacksquare$\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 9 · Regression coefficients under linearity\n",
    "\n",
    "Suppose $\\mathbf Z$ is admissible and $E[Y \\mid x, \\mathbf z] = a + bx + \\mathbf c^\\top\\mathbf z$. Prove $E[Y \\mid \\operatorname{do}(x + 1)] - E[Y \\mid \\operatorname{do}(x)] = b$.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Take expectations in the adjustment formula: $E[Y \\mid \\operatorname{do}(x)] = \\sum_{\\mathbf z} E[Y \\mid x, \\mathbf z]P(\\mathbf z) = E_{\\mathbf Z}[a + bx + \\mathbf c^\\top\\mathbf Z]$.\n",
    "\n",
    "**Step 2.** $= a + bx + \\mathbf c^\\top E[\\mathbf Z]$.\n",
    "\n",
    "**Step 3.** The difference between $x + 1$ and $x$ is $b$. $\\blacksquare$\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 10 · Why descendants are excluded\n",
    "\n",
    "In the chain $X \\to M \\to Y$ with $M := aX + U_M$ and $Y := bM + U_Y$, there are no back-door paths, so $\\{M\\}$ blocks all of them. Show that \"adjusting\" for $M$ gives the wrong answer.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** True effect: $E[Y \\mid \\operatorname{do}(x)] = abx$, slope $ab$.\n",
    "\n",
    "**Step 2.** $E[Y \\mid x, m] = bm$ (given $M$, $X$ adds nothing: chain).\n",
    "\n",
    "**Step 3.** \"Adjusted\" formula: $\\sum_m E[Y \\mid x, m]P(m) = bE[M]$, which does not depend on $x$: slope 0.\n",
    "\n",
    "**Answer:** condition 1 is necessary in general; conditioning on a mediator removes the effect it carries. $\\blacksquare$\n",
    "\n",
    "</details>\n",
    "\n",
    "## Part 3 · Applied"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 11 · Returns to schooling, revisited\n",
    "\n",
    "In Day 46's DAG ($F \\to A$, $F \\to S$, $A \\to S$, $A \\to W$, $S \\to O$, $S \\to W$, $O \\to W$), which sets identify the effect of schooling $S$ on wages $W$? What if ability $A$ is unobserved?\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Back-door paths: $S \\leftarrow A \\to W$ and $S \\leftarrow F \\to A \\to W$.\n",
    "\n",
    "**Step 2.** $\\{A\\}$ blocks both (fork or chain middle on each), and $A$ is not a descendant of $S$: admissible. So is $\\{A, F\\}$.\n",
    "\n",
    "**Step 3.** $\\{F\\}$ blocks the second but not the first path: not admissible. Occupation $O$ is a descendant: never include it.\n",
    "\n",
    "**Answer:** you must observe ability. Without it no back-door set exists among $\\{F, O\\}$, and other strategies are needed (instruments, Phase 5; or bounds, Day 79).\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 12 · A training programme with a sign reversal\n",
    "\n",
    "Half the jobseekers were previously employed ($Z = 1$). Caseworkers send 30% of previously employed and 70% of others to training $T$. Employment rates $P(E = 1 \\mid T, Z)$: previously employed 0.60 untrained, 0.70 trained; others 0.20 untrained, 0.35 trained. $Z$ is the only confounder. Compute the naive and adjusted effects.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** $P(T = 1) = 0.5(0.3) + 0.5(0.7) = 0.5$.\n",
    "\n",
    "**Step 2.** Naive, trained: $\\dfrac{0.5(0.3)(0.70) + 0.5(0.7)(0.35)}{0.5} = \\dfrac{0.105 + 0.1225}{0.5} = 0.455$.\n",
    "\n",
    "**Step 3.** Naive, untrained: $\\dfrac{0.5(0.7)(0.60) + 0.5(0.3)(0.20)}{0.5} = \\dfrac{0.21 + 0.03}{0.5} = 0.480$.\n",
    "\n",
    "**Step 4.** Adjusted: $P(E \\mid \\operatorname{do}(T = 1)) = 0.5(0.70) + 0.5(0.35) = 0.525$ and $P(E \\mid \\operatorname{do}(T = 0)) = 0.5(0.60) + 0.5(0.20) = 0.40$.\n",
    "\n",
    "**Answer:** naive $-2.5$ points; adjusted $+12.5$ points. Training helps, but caseworkers send those with worse prospects.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 13 · Breastfeeding and child health\n",
    "\n",
    "A DAG: mother's education $E \\to$ income $I$, $E \\to$ breastfeeding $B$, $I \\to B$, $I \\to$ child health $H$, $B \\to$ infections $N \\to H$, and $B \\to$ clinic visits $C \\leftarrow H$. Which minimal set identifies the effect of breastfeeding on child health? Which candidate controls must be excluded, and why?\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Back-door paths from $B$: $B \\leftarrow I \\to H$ and $B \\leftarrow E \\to I \\to H$.\n",
    "\n",
    "**Step 2.** $\\{I\\}$ blocks both (fork on the first, chain on the second): minimal admissible set. $\\{E, I\\}$ is also admissible; $\\{E\\}$ alone leaves $B \\leftarrow I \\to H$ open.\n",
    "\n",
    "**Step 3.** Exclude infections $N$: a mediator (descendant of $B$) on the causal path.\n",
    "\n",
    "**Step 4.** Exclude clinic visits $C$: a descendant of $B$ and a collider ($B \\to C \\leftarrow H$); conditioning opens a non-causal path.\n",
    "\n",
    "**Answer:** adjust for income (optionally education); never for infections or clinic visits.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 14 · Over-controlling a wage regression\n",
    "\n",
    "In a simulated programme (code below), prior earnings $P$ confound training $T$ and later earnings $Y$; training raises job tenure $J$, which raises earnings. True total effect of training: 1.0 (\\$1,000), of which 0.4 runs through tenure. What do regressions of $Y$ on $T$ with controls $\\varnothing$, $\\{P\\}$ and $\\{P, J\\}$ estimate?\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** $\\varnothing$: the back-door path $T \\leftarrow P \\to Y$ is open: badly biased (the code finds about 4.4).\n",
    "\n",
    "**Step 2.** $\\{P\\}$: admissible: about 1.0, the total effect.\n",
    "\n",
    "**Step 3.** $\\{P, J\\}$: tenure is a descendant (mediator): the coefficient drops to about 0.6, the effect not running through tenure.\n",
    "\n",
    "**Answer:** only $\\{P\\}$ answers \"what does training do to earnings?\". The \"control for everything\" regression answers a different question, and only if tenure is itself unconfounded (Day 58).\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 15 · Hospital report cards\n",
    "\n",
    "A regulator compares 30-day mortality across hospitals. Candidate adjustment variables: patient severity at admission, age, insurance type, length of stay, and severity at discharge. Draw the relevant DAG fragment and choose the adjustment set for \"the effect of being treated at hospital $H$\".\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Pre-treatment (non-descendants of hospital choice): admission severity, age, insurance. They affect which hospital patients use and their mortality: back-door paths through them must be blocked.\n",
    "\n",
    "**Step 2.** Length of stay and discharge severity are affected by the hospital (descendants), often mediators of quality.\n",
    "\n",
    "**Step 3.** Adjusting for discharge severity would credit a hospital for patients it made sicker; length of stay is also affected by deaths themselves.\n",
    "\n",
    "**Answer:** adjust for admission severity, age and insurance; exclude length of stay and discharge severity. If unmeasured severity remains, hospital comparisons stay confounded, and a sensitivity analysis (Day 95) is needed.\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 itertools\n",
    "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 backdoor_ok(G, x, y, Z):\n",
    "    \"\"\"Back-door criterion: no descendant of x, and Z d-separates x and y once x's outgoing edges are removed.\"\"\"\n",
    "    if Z & nx.descendants(G, x):\n",
    "        return False\n",
    "    H = G.copy(); H.remove_edges_from(list(G.out_edges(x)))\n",
    "    return dsep(H, {x}, {y}, set(Z))\n",
    "\n",
    "G = nx.DiGraph([(\"Z1\", \"Z3\"), (\"Z2\", \"Z3\"), (\"Z1\", \"X\"), (\"Z2\", \"Y\"), (\"Z3\", \"X\"), (\"Z3\", \"Y\"), (\"X\", \"W\"), (\"W\", \"Y\")])\n",
    "cands = [\"Z1\", \"Z2\", \"Z3\", \"W\"]\n",
    "valid = [set(c) for k in range(5) for c in itertools.combinations(cands, k) if backdoor_ok(G, \"X\", \"Y\", set(c))]\n",
    "print(\"admissible sets:\", [sorted(v) for v in valid])\n",
    "\n",
    "# Exercise 12: sign reversal\n",
    "pT = {1: .3, 0: .7}; pE = {(1, 0): .60, (1, 1): .70, (0, 0): .20, (0, 1): .35}     # key (z, t)\n",
    "naive = {t: sum(.5 * (pT[z] if t else 1 - pT[z]) * pE[z, t] for z in (0, 1)) / .5 for t in (0, 1)}\n",
    "adj = {t: sum(.5 * pE[z, t] for z in (0, 1)) for t in (0, 1)}\n",
    "print(\"naive:\", round(naive[1] - naive[0], 4), \" adjusted:\", round(adj[1] - adj[0], 4))\n",
    "\n",
    "# Exercise 14: over-control\n",
    "rng = np.random.default_rng(52); n = 200_000\n",
    "P = rng.normal(size=n)\n",
    "T = (P + rng.normal(size=n) > 0).astype(float)\n",
    "J = 2 * T + rng.normal(size=n)\n",
    "Y = 0.6 * T + 0.2 * J + 3 * P + rng.normal(size=n)          # total effect 0.6 + 0.2*2 = 1.0\n",
    "for name, cols in [(\"none\", []), (\"P\", [P]), (\"P, J\", [P, J])]:\n",
    "    X = np.column_stack([np.ones(n), T] + cols)\n",
    "    print(f\"controls {name:5s}: coefficient on T = {np.linalg.lstsq(X, Y, rcond=None)[0][1]:.3f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Key takeaways\n",
    "\n",
    "- Back-door paths start with an arrow into the treatment; they carry confounding.\n",
    "- A set is admissible if it blocks them all and contains no descendant of the treatment; then $P(y \\mid \\operatorname{do}(x)) = \\sum_{\\mathbf z} P(y \\mid x, \\mathbf z)P(\\mathbf z)$.\n",
    "- A variable can be both a confounder and a collider; the criterion, not intuition, decides what to add."
   ]
  }
 ]
}