{
 "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 45: Phase 3 Capstone: Doubly Robust (AIPW) Estimation\n",
    "\n",
    "*Beginner to Ninja: Math for Causal Inference*\n",
    "\n",
    "Combining an outcome model with a propensity model so that the estimate is right if either one is. The bridge from Phase 3 to double machine learning.\n",
    "\n",
    "**Learning outcomes**\n",
    "\n",
    "- Write the regression (outcome-model), IPW and augmented IPW (AIPW) estimators of the ATE.\n",
    "- Prove that AIPW is consistent if either the outcome model or the propensity model is correct.\n",
    "- Compute AIPW estimates and influence-function standard errors by hand.\n",
    "- Choose among Phase 3 designs and estimators for a given policy question."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Theory\n",
    "\n",
    "### Two ways to use unconfoundedness\n",
    "\n",
    "With $\\mu_t(x) = E[Y \\mid T = t, X = x]$ and $e(x) = P(T = 1 \\mid X = x)$:\n",
    "\n",
    "- **Outcome regression (imputation):** $\\hat\\tau_{REG} = \\frac1n\\sum_i\\big(\\hat\\mu_1(X_i) - \\hat\\mu_0(X_i)\\big)$. Right if the outcome models are right.\n",
    "- **IPW (Day 44):** right if the propensity model is right.\n",
    "\n",
    "### Augmented IPW\n",
    "\n",
    "Start from the regression estimate and add an IPW-weighted average of the outcome-model errors:\n",
    "\n",
    "$$\n",
    "\\hat\\tau_{AIPW} = \\frac1n\\sum_i\\Big[\\hat\\mu_1(X_i) - \\hat\\mu_0(X_i) + \\frac{T_i(Y_i - \\hat\\mu_1(X_i))}{\\hat e(X_i)} - \\frac{(1 - T_i)(Y_i - \\hat\\mu_0(X_i))}{1 - \\hat e(X_i)}\\Big].\n",
    "$$\n",
    "\n",
    "The correction terms repair whatever bias the outcome model leaves.\n",
    "\n",
    "### Double robustness\n",
    "\n",
    "$\\hat\\tau_{AIPW}$ is consistent if **either**\n",
    "\n",
    "- the outcome models $\\mu_0, \\mu_1$ are correct (whatever $\\hat e$ is), **or**\n",
    "- the propensity model $e$ is correct (whatever $\\hat\\mu$ is).\n",
    "\n",
    "When both are correct, it is also **efficient**: it attains the smallest possible asymptotic variance for the ATE under unconfoundedness (the semiparametric bound, Day 88).\n",
    "\n",
    "### Inference\n",
    "\n",
    "Write $\\hat\\psi_i$ for the bracketed term of unit $i$, so $\\hat\\tau_{AIPW} = \\frac1n\\sum_i\\hat\\psi_i$. Its standard error is approximately\n",
    "\n",
    "$$\n",
    "\\operatorname{SE} \\approx \\frac{\\operatorname{sd}(\\hat\\psi_i)}{\\sqrt n}.\n",
    "$$\n",
    "\n",
    "The $\\hat\\psi_i$ are (estimated) **influence-function** values (Day 87). With flexible machine-learning models for $\\mu$ and $e$, fit them on different folds from those used to evaluate $\\hat\\psi_i$ (**cross-fitting**): this is double/debiased machine learning (Day 90).\n",
    "\n",
    "### Phase 3 in one table\n",
    "\n",
    "| Situation | Tool (Day) |\n",
    "|---|---|\n",
    "| You control assignment | complete or blocked randomisation (35, 38), Neyman variance (36) |\n",
    "| Tiny sample or sharp hypothesis | Fisher randomisation test (37) |\n",
    "| Baseline covariates in an RCT | Lin adjustment (39) |\n",
    "| Treatment delivered to groups or spillovers | cluster randomisation (40), saturation designs (32) |\n",
    "| People ignore their assignment | ITT and complier effects (41) |\n",
    "| Observational data, rich covariates | unconfoundedness + overlap (42), propensity score (43), IPW (44), AIPW (45) |\n",
    "\n",
    "## Part 1 · Warm-up"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 1 · One treated unit's contribution\n",
    "\n",
    "A treated household has $Y = 20$, $\\hat\\mu_1 = 19$, $\\hat\\mu_0 = 15$, $\\hat e = 0.8$. Compute its AIPW term $\\hat\\psi_i$.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Regression part: $\\hat\\mu_1 - \\hat\\mu_0 = 4$.\n",
    "\n",
    "**Step 2.** Correction (treated): $(Y - \\hat\\mu_1)/\\hat e = (20 - 19)/0.8 = 1.25$.\n",
    "\n",
    "**Step 3.** $\\hat\\psi_i = 4 + 1.25 = 5.25$.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 2 · One control unit's contribution\n",
    "\n",
    "A control household has $Y = 14$, $\\hat\\mu_1 = 19$, $\\hat\\mu_0 = 15$, $\\hat e = 0.8$. Compute $\\hat\\psi_i$.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Regression part: $19 - 15 = 4$.\n",
    "\n",
    "**Step 2.** Correction (control): $-(Y - \\hat\\mu_0)/(1 - \\hat e) = -(14 - 15)/0.2 = +5$.\n",
    "\n",
    "**Step 3.** $\\hat\\psi_i = 4 + 5 = 9$.\n",
    "\n",
    "**Answer:** 9. A rare control (only 20% of similar households are untreated) with an outcome below its prediction pulls the estimate up strongly.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 3 · Reading the correction\n",
    "\n",
    "What does the correction term do if the outcome model predicts the treated outcomes perfectly?\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Then $Y_i - \\hat\\mu_1(X_i) = 0$ for every treated unit (and similarly for controls).\n",
    "\n",
    "**Answer:** the corrections vanish and AIPW equals the regression estimator; weights matter only to the extent the outcome model errs.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 4 · A standard error\n",
    "\n",
    "An AIPW analysis of 900 workers gives $\\hat\\psi_i$ values with sd 12 (\\$1,000). Compute the SE.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** SE $\\approx 12/\\sqrt{900} = 12/30 = 0.4$.\n",
    "\n",
    "**Answer:** \\$400.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 5 · Which model matters?\n",
    "\n",
    "You are confident about how a training programme selected participants (eligibility rules recorded in the files) but not about how earnings depend on covariates. Which estimator is safest?\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Knowledge of selection means the propensity model is likely right; the outcome model may be wrong.\n",
    "\n",
    "**Step 2.** IPW and AIPW both rely on a correct propensity model; AIPW also uses the outcome model to reduce variance.\n",
    "\n",
    "**Answer:** AIPW (or IPW): both stay consistent through the propensity model, and AIPW is more precise whenever the outcome model explains a good share of earnings variation.\n",
    "\n",
    "</details>\n",
    "\n",
    "## Part 2 · Proofs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 6 · Robust to a wrong propensity model\n",
    "\n",
    "Assume unconfoundedness and that $\\tilde\\mu_1(x) = \\mu_1(x)$ is correct, while $\\tilde e(x)$ is any function in $(0, 1)$. Prove $E\\big[\\tilde\\mu_1(X) + T(Y - \\tilde\\mu_1(X))/\\tilde e(X)\\big] = E[Y(1)]$.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** $E[\\mu_1(X)] = E[E[Y \\mid T = 1, X]] = E[Y(1)]$ (Day 42).\n",
    "\n",
    "**Step 2.** Correction term, by the LIE: $E\\Big[\\frac{T(Y - \\mu_1(X))}{\\tilde e(X)}\\Big] = E\\Big[\\frac{E[T(Y - \\mu_1(X)) \\mid X]}{\\tilde e(X)}\\Big]$.\n",
    "\n",
    "**Step 3.** $E[T(Y - \\mu_1(X)) \\mid X] = e(X)\\,\\big(E[Y \\mid T = 1, X] - \\mu_1(X)\\big) = 0$.\n",
    "\n",
    "**Step 4.** So the correction has mean 0 whatever $\\tilde e$ is, and the total is $E[Y(1)]$. $\\blacksquare$\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 7 · Robust to a wrong outcome model\n",
    "\n",
    "Now assume $\\tilde e(x) = e(x)$ is correct and $\\tilde\\mu_1$ is any function. Prove the same identity.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Rearrange: $\\tilde\\mu_1(X) + \\frac{T(Y - \\tilde\\mu_1(X))}{e(X)} = \\frac{TY}{e(X)} + \\tilde\\mu_1(X)\\Big(1 - \\frac{T}{e(X)}\\Big)$.\n",
    "\n",
    "**Step 2.** $E[TY/e(X)] = E[Y(1)]$ (Day 44, Exercise 6).\n",
    "\n",
    "**Step 3.** $E\\big[\\tilde\\mu_1(X)(1 - T/e(X)) \\mid X\\big] = \\tilde\\mu_1(X)\\big(1 - e(X)/e(X)\\big) = 0$.\n",
    "\n",
    "**Step 4.** So the total has mean $E[Y(1)]$. $\\blacksquare$ The same arguments for $Y(0)$ give double robustness for the ATE.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 8 · AIPW nests IPW\n",
    "\n",
    "Show that AIPW with $\\hat\\mu_0 = \\hat\\mu_1 = 0$ is the Horvitz–Thompson IPW estimator.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** With $\\hat\\mu \\equiv 0$, the regression part is 0.\n",
    "\n",
    "**Step 2.** The corrections become $T_iY_i/\\hat e_i - (1 - T_i)Y_i/(1 - \\hat e_i)$.\n",
    "\n",
    "**Step 3.** Their average is $\\hat\\tau_{HT}$ (Day 44). $\\blacksquare$\n",
    "\n",
    "A good outcome model shrinks the residuals that get weighted, which is why AIPW is typically less variable than IPW when the outcome model fits well.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 9 · AIPW in a randomised experiment\n",
    "\n",
    "In an RCT with known $e = p$ and linear outcome models fitted separately by arm, explain why AIPW is consistent whatever the outcome model, and how it relates to Lin's estimator (Day 39).\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** The propensity is known exactly ($e = p$), so Exercise 7 applies: consistent for any $\\hat\\mu$.\n",
    "\n",
    "**Step 2.** With linear $\\hat\\mu_t$ fitted by OLS in each arm, residuals average to zero within each arm, so the correction terms average to (nearly) zero.\n",
    "\n",
    "**Step 3.** AIPW then reduces to $\\frac1n\\sum(\\hat\\mu_1(X_i) - \\hat\\mu_0(X_i))$: each arm's regression line evaluated at every unit's covariates and averaged, which equals Lin's estimator.\n",
    "\n",
    "**Answer:** in an RCT, covariate adjustment is a special case of AIPW: it can only improve precision, never introduce bias asymptotically.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 10 · The product of errors\n",
    "\n",
    "Show that, for the $Y(1)$ part, the bias of AIPW with wrong models $\\tilde\\mu_1$ and $\\tilde e$ is $E\\Big[\\frac{(e(X) - \\tilde e(X))(\\mu_1(X) - \\tilde\\mu_1(X))}{\\tilde e(X)}\\Big]$.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Condition on $X$: $E[T(Y - \\tilde\\mu_1) \\mid X] = e(X)(\\mu_1(X) - \\tilde\\mu_1(X))$.\n",
    "\n",
    "**Step 2.** So $E\\big[\\tilde\\mu_1 + T(Y - \\tilde\\mu_1)/\\tilde e\\big] = E\\Big[\\tilde\\mu_1 + \\frac{e}{\\tilde e}(\\mu_1 - \\tilde\\mu_1)\\Big]$.\n",
    "\n",
    "**Step 3.** Subtract $E[\\mu_1(X)] = E[Y(1)]$: bias $= E\\Big[(\\mu_1 - \\tilde\\mu_1)\\Big(\\frac{e}{\\tilde e} - 1\\Big)\\Big] = E\\Big[\\frac{(e - \\tilde e)(\\mu_1 - \\tilde\\mu_1)}{\\tilde e}\\Big]$. $\\blacksquare$\n",
    "\n",
    "The bias is a *product* of the two errors: if each model is moderately good, the bias is very small. This is why AIPW works with machine-learning nuisance models (Day 90).\n",
    "\n",
    "</details>\n",
    "\n",
    "## Part 3 · Applied\n",
    "\n",
    "Return to Day 44's six households, now with outcome-model predictions:\n",
    "\n",
    "| Household | $T$ | $\\hat e$ | $Y$ | $\\hat\\mu_1$ | $\\hat\\mu_0$ |\n",
    "|---|---|---|---|---|---|\n",
    "| 1 | 1 | 0.8 | 20 | 19 | 15 |\n",
    "| 2 | 1 | 0.5 | 16 | 16 | 12 |\n",
    "| 3 | 1 | 0.2 | 10 | 12 | 9 |\n",
    "| 4 | 0 | 0.8 | 14 | 19 | 15 |\n",
    "| 5 | 0 | 0.5 | 12 | 16 | 12 |\n",
    "| 6 | 0 | 0.2 | 8 | 12 | 9 |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 11 · Regression and AIPW estimates\n",
    "\n",
    "Compute the regression estimate and the AIPW estimate of the ATE.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Regression: $\\hat\\mu_1 - \\hat\\mu_0$ = 4, 4, 3, 4, 4, 3; mean $22/6 \\approx 3.67$.\n",
    "\n",
    "**Step 2.** AIPW terms, treated ($+\\,(Y - \\hat\\mu_1)/\\hat e$): household 1: $4 + 1/0.8 = 5.25$; household 2: $4 + 0 = 4$; household 3: $3 + (-2)/0.2 = -7$.\n",
    "\n",
    "**Step 3.** Controls ($-\\,(Y - \\hat\\mu_0)/(1 - \\hat e)$): household 4: $4 - (-1)/0.2 = 9$; household 5: $4 - 0 = 4$; household 6: $3 - (-1)/0.8 = 4.25$.\n",
    "\n",
    "**Step 4.** Mean: $(5.25 + 4 - 7 + 9 + 4 + 4.25)/6 = 19.5/6 = 3.25$.\n",
    "\n",
    "**Answer:** regression 3.67, AIPW 3.25 (Day 44: naive 4.0, Hájek 0.36). With six households the estimators disagree a lot; their disagreement is itself a warning about model dependence and overlap.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 12 · A wrong outcome model, a right propensity model\n",
    "\n",
    "In simulation (code below), the true earnings model is non-linear in age, the analyst fits a linear outcome model, and the propensity model is correct. Which estimators are biased?\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Regression relies on the (wrong) linear outcome model: biased.\n",
    "\n",
    "**Step 2.** IPW relies on the (correct) propensity model: consistent.\n",
    "\n",
    "**Step 3.** AIPW is consistent through the propensity model (Exercise 7).\n",
    "\n",
    "**Step 4.** The simulation confirms it: regression averages about 1.44 against a truth of 2, while IPW and AIPW average about 1.98.\n",
    "\n",
    "**Step 5.** Precision: here AIPW's SD (≈ 0.43) is slightly *larger* than IPW's (≈ 0.38). The linear model misses the U-shape in age, so its residuals are large, and weighting large residuals adds noise.\n",
    "\n",
    "**Answer:** double robustness delivered consistency; the precision gain of AIPW needs a reasonably good outcome model. Flexible outcome models (splines, machine learning) fix this, which is the motivation for Day 90.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 13 · Both models wrong\n",
    "\n",
    "What happens if both models are misspecified, and how can analysts reduce that risk?\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** No guarantee: the bias is the product of the two errors (Exercise 10), which can still be substantial.\n",
    "\n",
    "**Step 2.** Remedies: flexible models (splines, interactions, machine learning) for both nuisances, with cross-fitting; balance diagnostics for the propensity model; and checking that regression, IPW and AIPW roughly agree.\n",
    "\n",
    "**Answer:** double robustness gives two chances, not immunity.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 14 · Writing the policy report\n",
    "\n",
    "A ministry asks whether a youth employment subsidy raised employment. You have administrative data on all eligible youths, rich pre-programme records and the eligibility rules. Outline the Phase 3 analysis plan.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** Estimand: ATT (is the subsidy worth it for those who used it?) and, for an expansion, the ATE or CATEs (Day 33).\n",
    "\n",
    "**Step 2.** Identification: argue unconfoundedness from the eligibility rules and include all variables officials used, plus lagged employment (Day 42).\n",
    "\n",
    "**Step 3.** Overlap: estimate propensities, inspect histograms and normalised differences, and trim if needed (Days 42–43).\n",
    "\n",
    "**Step 4.** Estimation: AIPW with flexible models and cross-fitting; report regression and IPW as checks (Days 44–45).\n",
    "\n",
    "**Step 5.** Credibility: placebo test on pre-programme employment, sensitivity analysis for unobserved confounding.\n",
    "\n",
    "**Answer:** a transparent chain from estimand to assumptions to diagnostics to estimator, with robustness checks.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Your work:*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 15 · Match the tool to the problem\n",
    "\n",
    "For each case, name the Phase 3 tool: (a) 12 clinics, 6 randomised to a new protocol, outcome is patient satisfaction; (b) a lottery for vouchers where 30% of winners do not use them; (c) a trial randomising households within villages where neighbours share information; (d) an observational study with 50 baseline covariates and good overlap; (e) an RCT with a strong baseline measure of the outcome.\n",
    "\n",
    "<details><summary>Show solution</summary>\n",
    "\n",
    "**Step 1.** (a) Cluster randomisation with clinic-level randomisation inference (Days 37, 40).\n",
    "\n",
    "**Step 2.** (b) ITT for the voucher offer and CACE = ITT_Y / ITT_D for users (Day 41).\n",
    "\n",
    "**Step 3.** (c) Spillovers: estimate direct and spillover effects, or randomise villages (Days 32, 40).\n",
    "\n",
    "**Step 4.** (d) Propensity score with AIPW and balance checks (Days 43–45).\n",
    "\n",
    "**Step 5.** (e) Lin's covariate adjustment, or block on the baseline measure (Days 38–39).\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",
    "from scipy import optimize\n",
    "\n",
    "T = np.array([1, 1, 1, 0, 0, 0]); e = np.array([.8, .5, .2, .8, .5, .2]); Y = np.array([20, 16, 10, 14, 12, 8.])\n",
    "m1 = np.array([19, 16, 12, 19, 16, 12.]); m0 = np.array([15, 12, 9, 15, 12, 9.])\n",
    "psi = m1 - m0 + T * (Y - m1) / e - (1 - T) * (Y - m0) / (1 - e)\n",
    "print(\"psi:\", psi, \" regression:\", round((m1 - m0).mean(), 3), \" AIPW:\", round(psi.mean(), 3))\n",
    "\n",
    "# Monte Carlo: wrong outcome model, correct propensity model\n",
    "rng = np.random.default_rng(45)\n",
    "out = {\"regression\": [], \"IPW (Hajek)\": [], \"AIPW\": []}\n",
    "for _ in range(1500):\n",
    "    n = 1000; age = rng.uniform(18, 60, n); X = np.column_stack([np.ones(n), age])\n",
    "    e_true = 1 / (1 + np.exp(-(3 - 0.1 * age))); T = rng.random(n) < e_true\n",
    "    y = 10 + 0.02 * (age - 35)**2 + 2 * T + rng.normal(0, 2, n)          # non-linear in age; effect 2\n",
    "    b = optimize.minimize(lambda b: -np.sum(T * (X @ b) - np.log1p(np.exp(X @ b))), np.zeros(2)).x\n",
    "    eh = 1 / (1 + np.exp(-X @ b))\n",
    "    b1 = np.linalg.lstsq(X[T], y[T], rcond=None)[0]; b0 = np.linalg.lstsq(X[~T], y[~T], rcond=None)[0]\n",
    "    mu1, mu0 = X @ b1, X @ b0                                              # linear (wrong) outcome models\n",
    "    out[\"regression\"].append((mu1 - mu0).mean())\n",
    "    out[\"IPW (Hajek)\"].append((T * y / eh).sum() / (T / eh).sum() - ((~T) * y / (1 - eh)).sum() / ((~T) / (1 - eh)).sum())\n",
    "    out[\"AIPW\"].append(np.mean(mu1 - mu0 + T * (y - mu1) / eh - (~T) * (y - mu0) / (1 - eh)))\n",
    "for k, v in out.items():\n",
    "    print(f\"{k:12s} mean {np.mean(v):.3f} (truth 2)  SD {np.std(v):.3f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Key takeaways\n",
    "\n",
    "- AIPW adds an inverse-probability-weighted correction to an outcome-model estimate.\n",
    "- It is consistent if either model is right, and efficient if both are; its bias is a product of the two models' errors.\n",
    "- Phase 3 gives you the design-based and model-based toolkit; Phase 4 adds graphs to decide what to condition on."
   ]
  }
 ]
}