Causal inferenceChapter 09
Variance
Neyman's exact variance formula for difference in means and the conservativeness of its usual estimator.
Contents
Under complete randomization, the variance of difference in means is an exact
finite-population quantity rather than an asymptotic approximation. This chapter
derives Neyman’s formula by first proving a general expression for the variance
of a linear statistic
Three population variances
In a design-based setup there is no superpopulation, so the quantities
are not parameters of a distribution. They are fixed numbers attached to the
fixed population, exactly like
noncomputable def fpVar (v : Fin n → ℝ) : ℝ := (∑ i, (v i - mean v) ^ 2) / ((n : ℝ) - 1)
noncomputable def fpCov (u v : Fin n → ℝ) : ℝ :=
(∑ i, (u i - mean u) * (v i - mean v)) / ((n : ℝ) - 1)Two things to read off the Lean rather than the mathematics.
First, fpVar takes a plain vector v : Fin n → ℝ, not a population and not a
random variable. There is no probability anywhere in this definition, which is
the whole point: fpVar P.y1 is as deterministic as P.y1.
Second, the definition has no hypothesis 1 < n, even though the displayed
formula divides by x / 0 = 0 convention doing its usual
work: at fpVar v = 0, and at
The three named variances of the theorem, and the covariance that is the source of all the trouble:
noncomputable def S1sq : ℝ := fpVar P.y1
noncomputable def S0sq : ℝ := fpVar P.y0
noncomputable def Stausq : ℝ := fpVar P.effect
noncomputable def S10 : ℝ := fpCov P.y1 P.y0Proposition.
The individual effects are
and expanding the square termwise gives
which is the claim with
theorem Stausq_eq : P.Stausq = P.S1sq + P.S0sq - 2 * P.S10 := by
have h : P.effect = fun i => P.y1 i - P.y0 i := rfl
rw [Stausq, h, fpVar_sub]
rflhave h : P.effect = fun i => P.y1 i - P.y0 i := rfleffectis defined as the pointwise difference, so the two functions are equal byrfl. Thehaveexists only so that the nextrwhas an equation to work with:rwneeds a proof term, andrflproofs of definitional identities are the cheapest way to produce one.rw [Stausq, h]The first two rewrites of one
rwlist.Stausqunfolds the left-hand side tofpVar P.effect, andhreplacesP.effectby the explicit difference, so the goal is now about the variance of a difference of two outcome vectors.Goal⊢ (fpVar fun i => P.y1 i - P.y0 i) = P.S1sq + P.S0sq - 2 * P.S10fpVar_subThird rewrite.
fpVar_sub— the “variance of a difference” identity, itself proved as an exercise below by writing as and quoting the linear-combination lemma — turns that intofpVar P.y1 + fpVar P.y0 - 2 * fpCov P.y1 P.y0.Goal⊢ fpVar P.y1 + fpVar P.y0 - 2 * fpCov P.y1 P.y0 = P.S1sq + P.S0sq - 2 * P.S10rflThe remaining goal compares that with
P.S1sq + P.S0sq - 2 * P.S10, and those three names are definitions of exactly the three expressions on the left. The proof therefore ends where a textbook’s does: with the observation that both sides are the same thing under different names.
ExerciseScaling a vector (warm-up)
Scaling every entry of a vector by c multiplies its finite-population variance
by c ^ 2: the variance is quadratic, not linear, in the data.
theorem fpVar_const_mul (c : ℝ) (v : Fin n → ℝ) :
fpVar (fun i => c * v i) = c ^ 2 * fpVar v := by
sorryHint
simp only [fpVar, mean_const_mul] pulls the c out of the mean; after
← mul_div_assoc and congr 1 the goal is a sum equal to c ^ 2 times a sum,
so Finset.mul_sum followed by Finset.sum_congr rfl fun i _ => by ring proves
it termwise — every summand is c ^ 2 * (v i - mean v) ^ 2.
Show solution
theorem fpVar_const_mul (c : ℝ) (v : Fin n → ℝ) :
fpVar (fun i => c * v i) = c ^ 2 * fpVar v := by
simp only [fpVar, mean_const_mul]
rw [← mul_div_assoc]
congr 1
rw [Finset.mul_sum]
exact Finset.sum_congr rfl fun i _ => by ringExerciseVariance of a difference (core)
The variance of a difference: fpVar (u - v) = fpVar u + fpVar v - 2 fpCov u v.
This is the lemma that produces Stausq_eq above.
theorem fpVar_sub (u v : Fin n → ℝ) :
fpVar (fun i => u i - v i) = fpVar u + fpVar v - 2 * fpCov u v := by
sorryHint
Do not expand anything. Write u i - v i as 1 * u i + (-1) * v i as a have
closed by funext i; ring, rewrite with it and with fpVar_linear_comb, and
ring finishes.
Show solution
theorem fpVar_sub (u v : Fin n → ℝ) :
fpVar (fun i => u i - v i) = fpVar u + fpVar v - 2 * fpCov u v := by
have h : (fun i => u i - v i) = fun i => (1 : ℝ) * u i + (-1 : ℝ) * v i := by
funext i; ring
rw [h, fpVar_linear_comb]
ringExerciseShifting a vector (warm-up)
Shifting every entry of a vector by the same constant leaves its
finite-population variance unchanged. The hypothesis 0 < n is not decoration;
part of the exercise is seeing where it gets spent.
theorem fpVar_add_const {n : ℕ} (hn : 0 < n) (v : Fin n → ℝ) (c : ℝ) :
fpVar (fun i => v i + c) = fpVar v := by
sorryHint
mean_add and mean_const give mean (fun i => v i + c) = mean v + c as a
have — mean_const is where 0 < n is needed. After simp only [fpVar, hm]
and congr 1, each summand is (v i + c - (mean v + c)) ^ 2, which
Finset.sum_congr rfl fun i _ => by ring matches to (v i - mean v) ^ 2.
Show solution
theorem fpVar_add_const {n : ℕ} (hn : 0 < n) (v : Fin n → ℝ) (c : ℝ) :
fpVar (fun i => v i + c) = fpVar v := by
have hm : mean (fun i => v i + c) = mean v + c := by
rw [mean_add v (fun _ => c), mean_const hn c]
simp only [fpVar, hm]
congr 1
exact Finset.sum_congr rfl fun i _ => by ringThe one formula: variance of a linear statistic
Every estimator in this library has the shape
for coefficients
The variance and covariance being computed are the design’s own, defined in chapter 7 as finite sums over assignments:
noncomputable def var : ℝ := D.expect (fun z => (f z - D.expect f) ^ 2)D and f do not appear in the signature because they come from a variable
line further up the file — Lean’s way of declaring parameters once for a run of
declarations and letting each one pick up the ones it mentions. Design.cov is
the same thing with two statistics. Both are expectations, so both are sums over
all D.prob; there is no measure theory to check.
TheoremVariance of a linear statistic
For any design
The Lean says exactly this, and the types are what make it precise.
D.var : (Assignment n → ℝ) → ℝ is the design-based variance
fun z => ∑ i, a i * Z z i: z is the
random object, a i is not — and you can see that in the type, because a : Fin n → ℝ has no z argument to take. A formalization in which the coefficients
could depend on the assignment would be a different, and false, theorem.
theorem var_linear (D : Design n) (a : Fin n → ℝ) :
D.var (fun z => ∑ i, a i * Z z i)
= ∑ i, ∑ j, a i * a j * (D.jointPropensity i j - D.propensity i * D.propensity j) := by
rw [var_sum]
refine Finset.sum_congr rfl fun i _ => Finset.sum_congr rfl fun j _ => ?_
rw [cov_const_mul_left, cov_const_mul_right, cov_Z]
ringThe derivation is two lines of second-moment algebra. The variance of a sum is the double sum of covariances,
which is linearity of expectation applied to the expanded square. Covariance is
bilinear, so the constants come out:
by the definitions of the first- and second-order inclusion probabilities.∎
The proof is four tactic lines, and it is worth walking through because it is the template for every variance computation in the library.
rw [var_sum]Replace
by . This is where linearity of expectation is spent, and it is proved once, for an arbitrary index type, fromcov_sum_sum— which in turn is the expansion of a product of two sums,Finset.sum_mul_sum, under an expectation. No design and no indicator has been mentioned yet.Goal⊢ (∑ i, ∑ j, D.cov (fun z => a i * Z z i) fun z => a j * Z z j) = ∑ i, ∑ j, a i * a j * (D.jointPropensity i j - D.propensity i * D.propensity j)refine Finset.sum_congr rfl fun i _ => Finset.sum_congr rfl fun j _ => ?_Finset.sum_congris the workhorse for sums: to show two sums over the same index set (that is therfl) are equal, prove the summands equal one index at a time; the_is the unused proof thatiis in the set. Applying it twice, once per layer, leaves a goal about a single pair .Goali j : Fin n ⊢ (D.cov (fun z => a i * Z z i) fun z => a j * Z z j) = a i * a j * (D.jointPropensity i j - D.propensity i * D.propensity j)rw [cov_const_mul_left, cov_const_mul_right]Bilinearity, one argument at a time:
cov_const_mul_left : Cov(c·f, g) = c·Cov(f, g)pulls outa i, and its right-hand sibling pulls outa j. What is left inside the covariance is the pair of bare indicators.Goal⊢ a i * (a j * D.cov (fun z => Z z i) fun z => Z z j) = a i * a j * (D.jointPropensity i j - D.propensity i * D.propensity j)rw [cov_Z]Name what is left.
cov_Z : Cov(Z i, Z j) = πᵢⱼ - πᵢπⱼis proved bycov_eq_expect_mul_sub_mulfollowed byrfl, becausejointPropensityandpropensityare defined as the two expectations that appear.ringThe goal is now
a i * (a j * X) = a i * a j * XwithXan opaque real. Reassociating is exactly whatringis for, and using it here rather thanmul_assocby hand keeps the proof stable if the statement is later regrouped.
The textbook display splits the diagonal off.
Proposition. For any design and any fixed coefficients,
Split each inner sum of the previous theorem at
theorem var_linear_split (D : Design n) (a : Fin n → ℝ) :
D.var (fun z => ∑ i, a i * Z z i)
= (∑ i, (a i) ^ 2 * (D.propensity i * (1 - D.propensity i)))
+ ∑ i, ∑ j ∈ univ.erase i,
a i * a j * (D.jointPropensity i j - D.propensity i * D.propensity j) := by
rw [var_linear, ← Finset.sum_add_distrib]
refine Finset.sum_congr rfl fun i _ => ?_
rw [← Finset.add_sum_erase _ _ (Finset.mem_univ i)]
congr 1
rw [jointPropensity_self]
ringrw [var_linear, ← Finset.sum_add_distrib]Start from the double sum just proved, then run
Finset.sum_add_distrib : ∑ (f i + g i) = ∑ f i + ∑ g ibackwards, merging the two sums on the right-hand side into one sum whose summand isdiagonal i + offdiagonal i. Combining first means the rest of the proof compares two sums over the same index, one unit at a time.refine Finset.sum_congr rfl fun i _ => ?_One unit at a time, as before. The goal is now: the
-th row of the double sum equals its diagonal entry plus the row with removed.Goali : Fin n ⊢ ∑ j, a i * a j * (D.jointPropensity i j - D.propensity i * D.propensity j) = a i ^ 2 * (D.propensity i * (1 - D.propensity i)) + ∑ j ∈ univ.erase i, a i * a j * (D.jointPropensity i j - D.propensity i * D.propensity j)rw [← Finset.add_sum_erase _ _ (Finset.mem_univ i)]Finset.add_sum_erase : a ∈ s → f a + ∑ x ∈ s.erase a, f x = ∑ x ∈ s, f x, used right to left, splits the left-hand sum at .univ.erase iis Lean’s way of writing “all ” as aFinset, rather than a filter on a proposition; the two are equal, but theeraseform has the cardinality lemmas attached. The membership side condition isFinset.mem_univ i, which is free.congr 1Both sides are now
something + (the same erased sum), socongr 1discards the matching summand and leaves only the diagonal entries to be compared.Goalcase e_a ⊢ a i * a i * (D.jointPropensity i i - D.propensity i * D.propensity i) = a i ^ 2 * (D.propensity i * (1 - D.propensity i))rw [jointPropensity_self]; ringjointPropensity_self : πᵢᵢ = πᵢis the idempotence step of the informal proof (it is the warm-up exercise below, and its content is ). After it the goal isa i * a i * (π i - π i * π i) = a i ^ 2 * (π i * (1 - π i)), which is a polynomial identity, soring.
The first sum,
Exerciseπᵢᵢ = πᵢ (warm-up)
The joint propensity of a unit with itself is its propensity, because
theorem jointPropensity_self (D : Design n) (i : Fin n) :
D.jointPropensity i i = D.propensity i := by
sorryHint
Unfold jointPropensity to an expectation and use Design.expect_congr, which
says two statistics that agree pointwise have the same expectation; the
pointwise fact is Z_mul_self : Z z i * Z z i = Z z i and nothing else.
Show solution
theorem jointPropensity_self (D : Design n) (i : Fin n) :
D.jointPropensity i i = D.propensity i := by
rw [jointPropensity]
exact D.expect_congr fun z => Z_mul_self z iExerciseAdding a constant (warm-up)
Adding a constant to a statistic does not change its variance. This lemma is
what lets Neyman’s proof throw away the - (∑ y0)/n₀ tail of difference in
means.
theorem var_add_const (D : Design n) (f : Assignment n → ℝ) (c : ℝ) :
D.var (fun z => f z + c) = D.var f := by
sorryHint
Compute the new expectation with expect_add and expect_const first, as a
have. Then simp only [var, hE] puts the goal in the form
E[(f z + c - (E f + c))²] = E[(f z - E f)²], and expect_congr reduces it to
an identity ring can close.
Show solution
theorem var_add_const (D : Design n) (f : Assignment n → ℝ) (c : ℝ) :
D.var (fun z => f z + c) = D.var f := by
have hE : D.expect (fun z => f z + c) = D.expect f + c := by
rw [D.expect_add f (fun _ => c), D.expect_const c]
simp only [var, hE]
exact D.expect_congr fun z => by ringExerciseScaling a statistic (warm-up)
Scaling a statistic scales its variance by the square:
theorem var_const_mul (D : Design n) (c : ℝ) (f : Assignment n → ℝ) :
D.var (fun z => c * f z) = c ^ 2 * D.var f := by
sorryHint
Four rewrites and a ring. Go through var_eq_cov_self so that
cov_const_mul_left and cov_const_mul_right apply, then come back with
← var_eq_cov_self.
Show solution
theorem var_const_mul (D : Design n) (c : ℝ) (f : Assignment n → ℝ) :
D.var (fun z => c * f z) = c ^ 2 * D.var f := by
rw [var_eq_cov_self, cov_const_mul_left, cov_const_mul_right, ← var_eq_cov_self]
ringExchangeable designs, and the two we have
Both designs in the library are exchangeable: every unit has the same
propensity
Proposition. If
In the double sum
For the second double sum, the total sum_sum_diag_offdiag, and
it is the only thing the two designs below need.∎
theorem var_linear_of_exchangeable (D : Design n) (a : Fin n → ℝ) (p q : ℝ)
(hp : ∀ i, D.propensity i = p) (hq : ∀ i j, i ≠ j → D.jointPropensity i j = q) :
D.var (fun z => ∑ i, a i * Z z i)
= (p - p ^ 2) * (∑ i, (a i) ^ 2) + (q - p ^ 2) * ((∑ i, a i) ^ 2 - ∑ i, (a i) ^ 2) := by
rw [var_linear]
exact sum_sum_diag_offdiag a (p - p ^ 2) (q - p ^ 2)
(fun i j => D.jointPropensity i j - D.propensity i * D.propensity j)
(fun i => by rw [jointPropensity_self, hp i]; ring)
(fun i j hij => by rw [hq i j hij, hp i, hp j]; ring)rw [var_linear]Get to the double sum. Everything specific to an exchangeable design happens after this line; before it, the general theorem is doing all the work.
exact sum_sum_diag_offdiag a (p - p ^ 2) (q - p ^ 2) (fun i j => D.jointPropensity i j - D.propensity i * D.propensity j) …Apply the combinatorial identity, instantiating its kernel
Fwith the design’s own , its diagonal valuecwith and its off-diagonal valuedwith . The lemma then is the goal; there is nothing left but its two side conditions.fun i => by rw [jointPropensity_self, hp i]; ringThe diagonal side condition,
∀ i, F i i = c. Rewriting to and then to leavesp - p * p = p - p ^ 2, which isring.fun i j hij => by rw [hq i j hij, hp i, hp j]; ringThe off-diagonal side condition,
∀ i j, i ≠ j → F i j = d. The hypothesishij : i ≠ jis exactly whathqneeds; after the three rewrites the goal isq - p * p = q - p ^ 2. Note that the statement of the theorem quantifieshqover distinct pairs only — asking for at as well would be asking for , which holds for no interesting design.
Corollary (Bernoulli). Under independent assignment with common probability
theorem bernoulli_var_linear {p : ℝ} (hp0 : 0 ≤ p) (hp1 : p ≤ 1) (a : Fin n → ℝ) :
(bernoulliDesign n p hp0 hp1).var (fun z => ∑ i, a i * Z z i)
= p * (1 - p) * ∑ i, (a i) ^ 2 := by
rw [Design.var_linear_of_exchangeable _ a p (p ^ 2)
(fun i => bernoulli_propensity i) (fun i j hij => bernoulli_jointPropensity hij)]
ringrw [Design.var_linear_of_exchangeable _ a p (p ^ 2) (fun i => bernoulli_propensity i) (fun i j hij => bernoulli_jointPropensity hij)]One rewrite with the exchangeable formula, telling it that this design’s two constants are
and . The twofunarguments are the proofs —bernoulli_propensity : π i = pandbernoulli_jointPropensity : i ≠ j → πᵢⱼ = p ^ 2, both from chapter 7. Everything design-specific is in those two lemmas.ringThe goal is
(p - p ^ 2) * ∑ a i ^ 2 + (p ^ 2 - p ^ 2) * (…) = p * (1 - p) * ∑ a i ^ 2. The second coefficient is literally , soringdeletes that whole term and matches the first. That vanishing is what “independent assignment” means here; it is not assumed, it is computed.
Corollary (complete randomization). With
where
Here
so the off-diagonal term is negative: fixing the number of treated units makes
the indicators negatively dependent. The negative term is exactly the right size
to convert
theorem completeRandomization_var_linear {n₁ : ℕ} (hle : n₁ ≤ n) (hpos : 0 < n₁) (hlt : n₁ < n)
(a : Fin n → ℝ) :
(completeRandomization n n₁ hle).var (fun z => ∑ i, a i * Z z i)
= ((n₁ : ℝ) * ((n : ℝ) - (n₁ : ℝ)) / (n : ℝ)) * fpVar a := by
have hn2 : 2 ≤ n := by omega
have hn : (n : ℝ) ≠ 0 := Nat.cast_ne_zero.mpr (by omega)
have hn1 : (n : ℝ) - 1 ≠ 0 := by
have : (2 : ℝ) ≤ (n : ℝ) := by exact_mod_cast hn2
linarith
rw [Design.var_linear_of_exchangeable _ a ((n₁ : ℝ) / (n : ℝ))
((n₁ : ℝ) * ((n₁ : ℝ) - 1) / ((n : ℝ) * ((n : ℝ) - 1)))
(fun i => completeRandomization_propensity i)
(fun i j hij => completeRandomization_jointPropensity hij),
fpVar_eq_sum_sq hn]
field_simp
ringhave hn2 : 2 ≤ n := by omegaFrom
0 < n₁andn₁ < nthere are at least two units, which is what makes below.omegadecides goals of linear arithmetic overℕandℤfrom the hypotheses in context, and this is the kind of bookkeeping it exists for.have hn : (n : ℝ) ≠ 0 := Nat.cast_ne_zero.mpr (by omega); have hn1 : (n : ℝ) - 1 ≠ 0 := …The two non-vanishing denominators, in
. The second has to be done by castinghn2and callinglinarith, because(n : ℝ) - 1is real subtraction: the natural-number statementn - 1 ≠ 0is a different claim, and at it would be false while the real one would not.rw [Design.var_linear_of_exchangeable _ a ((n₁ : ℝ) / (n : ℝ)) ((n₁ : ℝ) * ((n₁ : ℝ) - 1) / ((n : ℝ) * ((n : ℝ) - 1))) …]The exchangeable formula again, this time with
and — the two propensity lemmas proved in chapter 7 by counting subsets. Everything from here on is algebra with no probability in it.Goal⊢ (↑n₁ / ↑n - (↑n₁ / ↑n) ^ 2) * ∑ i, a i ^ 2 + (↑n₁ * (↑n₁ - 1) / (↑n * (↑n - 1)) - (↑n₁ / ↑n) ^ 2) * ((∑ i, a i) ^ 2 - ∑ i, a i ^ 2) = ↑n₁ * (↑n - ↑n₁) / ↑n * fpVar afpVar_eq_sum_sq hnSecond entry of the same
rwlist: rewrite the target’sfpVar ainto the computational form . That form has no innermean, hence no nested/ n, which is what makes the next line possible.Goal⊢ (↑n₁ / ↑n - (↑n₁ / ↑n) ^ 2) * ∑ i, a i ^ 2 + (↑n₁ * (↑n₁ - 1) / (↑n * (↑n - 1)) - (↑n₁ / ↑n) ^ 2) * ((∑ i, a i) ^ 2 - ∑ i, a i ^ 2) = ↑n₁ * (↑n - ↑n₁) / ↑n * ((↑n * ∑ i, a i ^ 2 - (∑ i, a i) ^ 2) / (↑n * (↑n - 1)))field_simpClear all four denominators, using
hnandhn1from the context. What is left is a polynomial identity in , , and .ringCheck that identity. This is the cancellation the informal proof waved at, and it is the whole reason
comes out in front of a centered sum of squares rather than a raw one.
Neyman’s theorem
TheoremNeyman (1923)
Under complete randomization with
An exact identity for finite
theorem neyman_variance {n₁ : ℕ} (hle : n₁ ≤ n) (P : Population n) (hpos : 0 < n₁)
(hlt : n₁ < n) :
(completeRandomization n n₁ hle).var (DiM P)
= P.S1sq / (n₁ : ℝ) + P.S0sq / ((n : ℝ) - (n₁ : ℝ)) - P.Stausq / (n : ℝ) := by
have hn : (n : ℝ) ≠ 0 := Nat.cast_ne_zero.mpr (by omega)
have hn1 : (n₁ : ℝ) ≠ 0 := Nat.cast_ne_zero.mpr (by omega)
have hn0 : (n : ℝ) - (n₁ : ℝ) ≠ 0 := by
have : (n₁ : ℝ) < (n : ℝ) := by exact_mod_cast hlt
linarith
have hlin : (completeRandomization n n₁ hle).var (DiM P)
= (completeRandomization n n₁ hle).var
(fun z => ∑ i, dimCoef P (n₁ : ℝ) ((n : ℝ) - (n₁ : ℝ)) i * Z z i) := by
rw [(completeRandomization n n₁ hle).var_congr_of_support
(g := fun z => (∑ i, dimCoef P (n₁ : ℝ) ((n : ℝ) - (n₁ : ℝ)) i * Z z i)
+ -((∑ i, P.y0 i) / ((n : ℝ) - (n₁ : ℝ))))
(fun z hz => DiM_eq_linear_of_card P hlt (card_eq_of_prob_ne_zero hz))]
exact (completeRandomization n n₁ hle).var_add_const _ _
rw [hlin, completeRandomization_var_linear hle hpos hlt, fpVar_dimCoef,
P.Stausq_eq]
field_simp
ringRead the hypotheses. hle : n₁ ≤ n is not a mathematical assumption at all — it
is an argument to the design: completeRandomization n n₁ hle cannot be formed
without it, because there are no subsets of size hpos : 0 < n₁ and hlt : n₁ < n, and they are
there because both groups must be non-empty for the two sample averages to
mean anything. Drop hpos and the statement becomes false rather than
undefined: with S1sq / 0 = 0.
Read the arithmetic, too. The control group size appears as (n : ℝ) - (n₁ : ℝ),
real subtraction, not ((n - n₁ : ℕ) : ℝ). Natural subtraction truncates, and
mixing the two is the single most common way to get a formalized finite-population
statement subtly wrong. neyman_variance_nat is the same theorem with a natural
n₀ and a hypothesis n₁ + n₀ = n; it does the cast once, correctly, so that
downstream users do not have to.
The proof
Write
Complete randomization gives probability zero to every other assignment, and
adding a constant does not change a variance, so
It remains to expand
and multiplying by
Step 1 — make difference in means linear. On assignments with
noncomputable def dimCoef (P : Population n) (n₁ n₀ : ℝ) (i : Fin n) : ℝ :=
(1 / n₁) * P.y1 i + (1 / n₀) * P.y0 i
theorem DiM_eq_linear_of_card {n₁ : ℕ} (P : Population n) (hlt : n₁ < n)
{z : Assignment n} (hz : #z = n₁) :
DiM P z
= (∑ i, dimCoef P (n₁ : ℝ) ((n : ℝ) - (n₁ : ℝ)) i * Z z i)
+ -((∑ i, P.y0 i) / ((n : ℝ) - (n₁ : ℝ))) := by
have hcompl : ((#zᶜ : ℕ) : ℝ) = (n : ℝ) - (n₁ : ℝ) := by
rw [card_compl, hz, Nat.cast_sub hlt.le]
calc DiM P z
= (∑ i, Z z i * P.y1 i) / (n₁ : ℝ)
- (∑ i, (1 - Z z i) * P.y0 i) / ((n : ℝ) - (n₁ : ℝ)) := by
unfold DiM
rw [sum_mem_eq_sum_Z_mul, sum_compl_eq_sum_one_sub_Z_mul, hz, hcompl]
_ = ∑ i, (Z z i * P.y1 i / (n₁ : ℝ)
- (1 - Z z i) * P.y0 i / ((n : ℝ) - (n₁ : ℝ))) := by
rw [Finset.sum_div, Finset.sum_div, Finset.sum_sub_distrib]
_ = ∑ i, (dimCoef P (n₁ : ℝ) ((n : ℝ) - (n₁ : ℝ)) i * Z z i
+ -(P.y0 i / ((n : ℝ) - (n₁ : ℝ)))) :=
Finset.sum_congr rfl fun i _ => by unfold dimCoef; ring
_ = (∑ i, dimCoef P (n₁ : ℝ) ((n : ℝ) - (n₁ : ℝ)) i * Z z i)
+ ∑ i, -(P.y0 i / ((n : ℝ) - (n₁ : ℝ))) := Finset.sum_add_distrib
_ = (∑ i, dimCoef P (n₁ : ℝ) ((n : ℝ) - (n₁ : ℝ)) i * Z z i)
+ -((∑ i, P.y0 i) / ((n : ℝ) - (n₁ : ℝ))) := by
rw [Finset.sum_neg_distrib, Finset.sum_div]The sign is worth staring at: the control outcome enters
have hcompl : ((#zᶜ : ℕ) : ℝ) = (n : ℝ) - (n₁ : ℝ) := by rw [card_compl, hz, Nat.cast_sub hlt.le]The size of the control group as a real number, and the one place natural subtraction has to be handled.
card_compl : #zᶜ = n - #zlives inℕ, where subtraction truncates;hzreplaces#zbyn₁; andNat.cast_sub, whose hypothesis isn₁ ≤ n, is what licenses moving that subtraction across the cast.unfold DiM; rw [sum_mem_eq_sum_Z_mul, sum_compl_eq_sum_one_sub_Z_mul, hz, hcompl]First
calcstep: rewrite both group sums as indicator-weighted sums over the whole population.sum_mem_eq_sum_Z_mul : ∑ i ∈ z, v i = ∑ i, Z z i * v iand its control-side counterpartsum_compl_eq_sum_one_sub_Z_mulare the two directions this chapter keeps using;hzandhcomplthen replace the two group sizes by the constantsn₁and(n : ℝ) - (n₁ : ℝ). After this step the assignment appears only throughZ z i.Goal⊢ (∑ i ∈ z, P.y1 i) / ↑(#z) - (∑ i ∈ zᶜ, P.y0 i) / ↑(#zᶜ) = (∑ i, Z z i * P.y1 i) / ↑n₁ - (∑ i, (1 - Z z i) * P.y0 i) / (↑n - ↑n₁)rw [Finset.sum_div, Finset.sum_div, Finset.sum_sub_distrib]Second
calcstep: push the two divisions inside their sums (Finset.sum_div : (∑ f i) / c = ∑ (f i / c)), then match the two sides withFinset.sum_sub_distrib : ∑ (f i - g i) = ∑ f i - ∑ g i—rwrewrites wherever the pattern occurs, and the sum of differences it finds is on the right. Everything is now a single sum over units, which is the shape a linear statistic has to have.Finset.sum_congr rfl fun i _ => by unfold dimCoef; ringThird step, and the only one with content: regroup the
-th summand asdimCoef … i * Z z i + -(y0 i / n₀). UnfoldingdimCoefmakes the goal a polynomial identity inZ z i,y1 i,y0 iand the two group sizes, soringcloses it. This is where the plus sign in the coefficient comes from: the- (1 - Z z i) * y0 i / n₀of the previous line is-y0 i / n₀ + Z z i * y0 i / n₀.Finset.sum_add_distribFourth step: split the sum of two things into two sums — the linear part and a sum of copies of the constant.
rw [Finset.sum_neg_distrib, Finset.sum_div]Fifth step: pull the negation out of the second sum and recombine
∑ i, y0 i / n₀into(∑ i, y0 i) / n₀, which is the additive constant as the statement writes it. Thecalcchain ends syntactically equal to the goal.
Step 2 — get onto the support. The identity above holds only when Design.var sums over all DiM divides by zero and returns garbage. This is
what Design.var_congr_of_support is for: two statistics that agree wherever
D.prob z ≠ 0 have the same variance. card_eq_of_prob_ne_zero supplies
#z = n₁ from D.prob z ≠ 0. Then var_add_const deletes the constant tail.
Step 3 — quote the general formula. completeRandomization_var_linear turns
the variance into
Step 4 — expand. fpVar_dimCoef expands Stausq_eq replaces field_simp; ring does the algebra with the
three non-vanishing denominators established as haves at the top of the proof.
have hn : (n : ℝ) ≠ 0 := …; have hn1 : (n₁ : ℝ) ≠ 0 := …; have hn0 : (n : ℝ) - (n₁ : ℝ) ≠ 0 := …The three denominators of the statement, shown to be non-zero before anything else happens. The first two are
Nat.cast_ne_zero.mpr (by omega); the third has to be done inℝ, by castinghltand callinglinarith.rw [(completeRandomization n n₁ hle).var_congr_of_support (g := fun z => (∑ i, dimCoef P … i * Z z i) + -(…)) (fun z hz => DiM_eq_linear_of_card P hlt (card_eq_of_prob_ne_zero hz))]Step 2, first half.
var_congr_of_supportneeds the replacement statisticgspelled out — Lean cannot guess it — and a proof thatDiM Pequals it wherever the probability is non-zero. That proof is the composition of the support lemma with step 1:card_eq_of_prob_ne_zeroturnsD.prob z ≠ 0into#z = n₁, which is precisely the hypothesisDiM_eq_linear_of_cardwants.exact (completeRandomization n n₁ hle).var_add_const _ _Step 2, second half:
var_add_const : Var(f + c) = Var(f)throws the constant tail away. That finishes thehave hlin, which now equates the variance ofDiM Pwith the variance of the linear statistic.rw [hlin]Substitute it into the main goal. What is left is the variance of the linear statistic, with no
DiMin sight.Goal⊢ ((completeRandomization n n₁ hle).var fun z => ∑ i, dimCoef P (↑n₁) (↑n - ↑n₁) i * Z z i) = P.S1sq / ↑n₁ + P.S0sq / (↑n - ↑n₁) - P.Stausq / ↑ncompleteRandomization_var_linear hle hpos hltStep 3. The complete-randomization formula replaces that variance by
, with the coefficient vectordimCoef P n₁ (n - n₁). This is the last time the design is mentioned.Goal⊢ ↑n₁ * (↑n - ↑n₁) / ↑n * fpVar (dimCoef P (↑n₁) (↑n - ↑n₁)) = P.S1sq / ↑n₁ + P.S0sq / (↑n - ↑n₁) - P.Stausq / ↑nfpVar_dimCoefStep 4, first half.
fpVar_dimCoefis the quadratic-form expansion: it appliesfpVar_linear_combto and then recognizes the three pieces asS1sq,S0sqandS10byrfl.Goal⊢ ↑n₁ * (↑n - ↑n₁) / ↑n * ((1 / ↑n₁) ^ 2 * P.S1sq + (1 / (↑n - ↑n₁)) ^ 2 * P.S0sq + 2 * (1 / ↑n₁) * (1 / (↑n - ↑n₁)) * P.S10) = P.S1sq / ↑n₁ + P.S0sq / (↑n - ↑n₁) - P.Stausq / ↑nP.Stausq_eqStep 4, second half: eliminate the unestimable covariance by rewriting
— the identity proved at the top of the chapter — in the goal’s right-hand side. Both sides are now built from the same three quantities.field_simpClear the denominators
, and , which is possible only becausehn,hn1andhn0are in context. What is left is a polynomial identity in those three numbers and the three variances.Goal⊢ (↑n - ↑n₁) ^ 2 * P.S1sq + ↑n₁ ^ 2 * P.S0sq + ↑n₁ * (↑n - ↑n₁) * 2 * P.S10 = ↑n * ((↑n - ↑n₁) * P.S1sq + ↑n₁ * P.S0sq) - ↑n₁ * (↑n - ↑n₁) * (P.S1sq + P.S0sq - 2 * P.S10)ringCheck it. Reading the two sides of the goal above against the informal proof: the left-hand side is
with everything multiplied out, the right-hand side is the claimed formula with substituted, and the identity is the collection of coefficients performed there by hand.
A worked example you can check by hand
Four units, two treated. Two of the four units gain
noncomputable def exP : Population 4 where
y1 := ![0, 2, 2, 2]
y0 := ![0, 0, 0, 2]
theorem exP_tau : exP.tau = 1 := by
norm_num [Population.tau, Population.effect, exP, mean, Fin.sum_univ_four, Matrix.cons_val_two, Matrix.cons_val_three,
Matrix.head_cons, Matrix.tail_cons]y1 := ![0, 2, 2, 2]![…]is Mathlib’s notation for a vector literal, i.e. a functionFin 4 → ℝgiven by listing its values. It is built fromMatrix.vecCons, which is why the lemmas needed to evaluate it below are calledMatrix.cons_val_*.norm_num [Population.tau, Population.effect, exP, mean, Fin.sum_univ_four, Matrix.cons_val_two, Matrix.cons_val_three, Matrix.head_cons, Matrix.tail_cons]One call, with everything it needs named.
Population.tau,effect,exPandmeanare unfolded to expose the arithmetic;Fin.sum_univ_fourreplaces∑ i : Fin 4, f ibyf 0 + f 1 + f 2 + f 3, sincenorm_numcannot evaluate a sum over a generalFinset; and the fourMatrixlemmas evaluate![a, b, c, d] iat each index. The last two are needed becausecons_val_tworewrites![a, b, c, d] 2tovecHead (vecTail ![b, c, d])rather than straight toc, andtail_cons/head_consare what peel those off; none of the four is asimplemma by default, which is why they are listed. What is left, , is arithmetic.
ExerciseThe three variances of the example (warm-up)
theorem exP_variances : exP.S1sq = 1 ∧ exP.S0sq = 1 ∧ exP.Stausq = 4 / 3 := by
sorryHint
Split the conjunction with refine ⟨?_, ?_, ?_⟩ <;> and hand all three goals to
one norm_num. Everything in sight is a definition, so Population.S1sq,
Population.S0sq, Population.Stausq, Population.effect, fpVar, mean and
exP all go in its simp set, with Fin.sum_univ_four to expand the sums;
![a, b, c, d] 2 needs Matrix.cons_val_two (and its cons_val_three
sibling) to reduce — those are not simp lemmas.
Show solution
theorem exP_variances : exP.S1sq = 1 ∧ exP.S0sq = 1 ∧ exP.Stausq = 4 / 3 := by
refine ⟨?_, ?_, ?_⟩ <;>
norm_num [Population.S1sq, Population.S0sq, Population.Stausq, Population.effect,
fpVar, mean, exP, Fin.sum_univ_four, Matrix.cons_val_two, Matrix.cons_val_three,
Matrix.head_cons, Matrix.tail_cons]ExerciseNeyman's formula on the example (core)
With
theorem exP_var_DiM :
(completeRandomization 4 2 (by norm_num)).var (DiM exP) = 2 / 3 := by
sorryHint
obtain ⟨h1, h0, hτ⟩ := exP_variances, then rewrite with neyman_variance —
it wants four arguments, exP itself and three side conditions each discharged
by by norm_num — and with h1, h0, hτ; norm_num finishes. The
(by norm_num : 2 ≤ 4) in the goal and the one you pass are different terms but
the same proof — proofs of a Prop are interchangeable, so rw does not care.
Show solution
theorem exP_var_DiM :
(completeRandomization 4 2 (by norm_num)).var (DiM exP) = 2 / 3 := by
obtain ⟨h1, h0, hτ⟩ := exP_variances
rw [neyman_variance (by norm_num) exP (by norm_num) (by norm_num), h1, h0, hτ]
norm_numThere are
theorem exP_DiM_of_treated_two_three : DiM exP {2, 3} = 2 := by
have hcompl : ({2, 3} : Assignment 4)ᶜ = {0, 1} := by decide
unfold DiM
rw [hcompl, Finset.sum_pair (by decide : (2 : Fin 4) ≠ 3),
Finset.sum_pair (by decide : (0 : Fin 4) ≠ 1),
Finset.card_pair (by decide : (2 : Fin 4) ≠ 3),
Finset.card_pair (by decide : (0 : Fin 4) ≠ 1)]
norm_num [exP, Matrix.cons_val_two, Matrix.cons_val_three, Matrix.head_cons,
Matrix.tail_cons]
theorem exP_neymanVarEst_of_treated_two_three : neymanVarEst exP {2, 3} = 0 := by
have hcompl : ({2, 3} : Assignment 4)ᶜ = {0, 1} := by decide
unfold neymanVarEst sampleVar
rw [hcompl, Finset.sum_pair (by decide : (2 : Fin 4) ≠ 3),
Finset.sum_pair (by decide : (0 : Fin 4) ≠ 1),
Finset.sum_pair (by decide : (2 : Fin 4) ≠ 3),
Finset.sum_pair (by decide : (0 : Fin 4) ≠ 1),
Finset.card_pair (by decide : (2 : Fin 4) ≠ 3),
Finset.card_pair (by decide : (0 : Fin 4) ≠ 1)]
norm_num [exP, Matrix.cons_val_two, Matrix.cons_val_three, Matrix.head_cons,
Matrix.tail_cons]have hcompl : ({2, 3} : Assignment 4)ᶜ = {0, 1} := by decideThe control group of this particular assignment, computed rather than argued.
Assignment 4isFinset (Fin 4), equality of such sets is decidable, anddecideasks the kernel to run that decision procedure. This works only because everything is a closed term of a finite type.rw [hcompl, Finset.sum_pair (by decide : (2 : Fin 4) ≠ 3), Finset.sum_pair (by decide : (0 : Fin 4) ≠ 1), Finset.card_pair …, Finset.card_pair …]Evaluate the four ingredients of
DiM.Finset.sum_pair : a ≠ b → ∑ i ∈ {a, b}, f i = f a + f bandFinset.card_pair : a ≠ b → #{a, b} = 2both need the two elements to be distinct, and that side condition is again decidable. Writing(by decide : (2 : Fin 4) ≠ 3)with the type ascription is how you tell Lean which disequality to prove.norm_num [exP, Matrix.cons_val_two, Matrix.cons_val_three, Matrix.head_cons, Matrix.tail_cons]Arithmetic. The goal is now
once the vector literals are evaluated, which is what the fourMatrixlemmas do.exP_neymanVarEst_of_treated_two_threeis the same four lines applied toneymanVarEstinstead: both treated units have and both control units , so both sample variances are and so is the reported variance.
If the randomization puts units 2 and 3 in treatment, both treated units have
The variance estimator, and what “conservative” means
Nobody reports
noncomputable def sampleVar (v : Fin n → ℝ) (s : Finset (Fin n)) : ℝ :=
(∑ i ∈ s, (v i - (∑ j ∈ s, v j) / (#s : ℝ)) ^ 2) / ((#s : ℝ) - 1)
noncomputable def neymanVarEst (P : Population n) (z : Assignment n) : ℝ :=
sampleVar P.y1 z / (#z : ℝ) + sampleVar P.y0 zᶜ / ((#zᶜ : ℕ) : ℝ)This is a genuine statistic, and you can check that by reading the definition
rather than trusting the name: P.y1 is summed only over z, and P.y0 only
over zᶜ. Every outcome it touches is one the assignment reveals.
Why the sample variances are unbiased
The engine is a single design-agnostic lemma, and it covers both groups at once
by taking the group indicator as a parameter —
Theorem (sample variance of a simple random sample without replacement). Let
where
Write
For the second, expand the square as a double sum,
Subtracting one-
and
theorem expect_ss_of_exchangeable (D : Design n) (v : Fin n → ℝ)
(W : Assignment n → Fin n → ℝ) (m : ℝ)
(hn : (n : ℝ) ≠ 0) (hn1 : (n : ℝ) - 1 ≠ 0) (hm : m ≠ 0)
(hidem : ∀ z i, W z i * W z i = W z i)
(h1 : ∀ i, D.expect (fun z => W z i) = m / (n : ℝ))
(h2 : ∀ i j, i ≠ j →
D.expect (fun z => W z i * W z j) = m * (m - 1) / ((n : ℝ) * ((n : ℝ) - 1))) :
D.expect (fun z => (∑ i, W z i * (v i) ^ 2) - (∑ i, W z i * v i) ^ 2 / m)
= (m - 1) * fpVar v := by
have hA : D.expect (fun z => ∑ i, W z i * (v i) ^ 2) = (m / (n : ℝ)) * ∑ i, (v i) ^ 2 := by
rw [D.expect_sum univ (fun i z => W z i * (v i) ^ 2), Finset.mul_sum]
exact Finset.sum_congr rfl fun i _ => by
rw [D.expect_mul_const (fun z => W z i) ((v i) ^ 2), h1 i]
have hB : D.expect (fun z => (∑ i, W z i * v i) ^ 2)
= (m / (n : ℝ)) * (∑ i, (v i) ^ 2)
+ (m * (m - 1) / ((n : ℝ) * ((n : ℝ) - 1))) * ((∑ i, v i) ^ 2 - ∑ i, (v i) ^ 2) := by
have hsq : ∀ z : Assignment n,
(∑ i, W z i * v i) ^ 2 = ∑ i, ∑ j, v i * v j * (W z i * W z j) := by
intro z
rw [sq, Finset.sum_mul_sum]
exact Finset.sum_congr rfl fun i _ =>
Finset.sum_congr rfl fun j _ => by ring
rw [D.expect_congr hsq,
D.expect_sum univ (fun i z => ∑ j, v i * v j * (W z i * W z j)),
Finset.sum_congr rfl fun i _ =>
D.expect_sum univ (fun j z => v i * v j * (W z i * W z j)),
Finset.sum_congr rfl fun i _ => Finset.sum_congr rfl fun j _ =>
D.expect_const_mul (fun z => W z i * W z j) (v i * v j)]
exact sum_sum_diag_offdiag v (m / (n : ℝ)) (m * (m - 1) / ((n : ℝ) * ((n : ℝ) - 1)))
(fun i j => D.expect (fun z => W z i * W z j))
(fun i => by rw [D.expect_congr (fun z => hidem z i), h1 i])
(fun i j hij => h2 i j hij)
rw [D.expect_sub _ _, hA, D.expect_div_const _ m, hB, fpVar_eq_sum_sq hn]
field_simp
ringThe hypotheses are exactly the first two moments of sampling without
replacement — h1 says each unit is in the group with probability h2
says each pair is with probability hidem, which says
ring.
have hA : D.expect (fun z => ∑ i, W z i * (v i) ^ 2) = (m / (n : ℝ)) * ∑ i, (v i) ^ 2The first of the two moment computations, stated as a
havebecause both of them are needed before anything can be combined. Its statement is the informal proof’s ; the goal shown here is the theorem’s, before eitherhave.Goal⊢ (D.expect fun z => ∑ i, W z i * v i ^ 2 - (∑ i, W z i * v i) ^ 2 / m) = (m - 1) * fpVar vrw [D.expect_sum univ (fun i z => W z i * (v i) ^ 2), Finset.mul_sum]Exchange
and on the left, and push the constant into the sum on the right, so that both sides are sums over units and can be compared termwise.Goal⊢ (∑ i, D.expect fun z => W z i * v i ^ 2) = ∑ i, m / ↑n * v i ^ 2exact Finset.sum_congr rfl fun i _ => by rw [D.expect_mul_const (fun z => W z i) ((v i) ^ 2), h1 i]One unit at a time:
expect_mul_constpulls the constantv i ^ 2out of the expectation, andh1 ireplaces the remainingE[W i]bym / n. That is the whole ofhA.have hsq : ∀ z, (∑ i, W z i * v i) ^ 2 = ∑ i, ∑ j, v i * v j * (W z i * W z j)The algebraic identity behind the second moment, proved pointwise in
zso that it can be pushed through the expectation byexpect_congr.rw [sq, Finset.sum_mul_sum]turns the square into the double sum∑ i, ∑ j, (W z i * v i) * (W z j * v j), and the goal shown is what remains: reordering each factor, whichringdoes term by term inside two nestedFinset.sum_congrs.Goalz : Assignment n ⊢ ∑ i, ∑ j, W z i * v i * (W z j * v j) = ∑ i, ∑ j, v i * v j * (W z i * W z j)rw [D.expect_congr hsq, D.expect_sum univ (fun i z => ∑ j, v i * v j * (W z i * W z j)), Finset.sum_congr rfl fun i _ => D.expect_sum univ …]Move the expectation inside both sums.
expect_congr hsqreplaces the squared statistic by the double sum under the binder; the firstexpect_sumexchanges with the outer ; and thesum_congrappliesexpect_sumagain, once per row, to exchange it with the inner .Finset.sum_congr rfl fun i _ => Finset.sum_congr rfl fun j _ => D.expect_const_mul (fun z => W z i * W z j) (v i * v j)Pull the constant
v i * v jout of every one of the expectations. What is left inside is exactlyE[W i * W j], which is what the two moment hypotheses talk about.Goal⊢ (∑ i, ∑ j, v i * v j * D.expect fun z => W z i * W z j) = m / ↑n * ∑ i, v i ^ 2 + m * (m - 1) / (↑n * (↑n - 1)) * ((∑ i, v i) ^ 2 - ∑ i, v i ^ 2)exact sum_sum_diag_offdiag v (m / (n : ℝ)) (m * (m - 1) / ((n : ℝ) * ((n : ℝ) - 1))) (fun i j => D.expect (fun z => W z i * W z j)) … …The same combinatorial identity used for exchangeable designs, applied now with the kernel
E[W i * W j]: its diagonal value is and its off-diagonal value , and the identity supplies . The diagonal side condition isby rw [D.expect_congr (fun z => hidem z i), h1 i]— idempotence under the binder, thenh1— and the off-diagonal one ish2 i j hijdirectly. This finisheshB.rw [D.expect_sub _ _]Back to the main goal, and the last four rewrites are the informal proof’s final display.
expect_subsplits the expectation of the difference into the two pieceshAandhBwere computed for.Goal⊢ ((D.expect fun z => ∑ i, W z i * v i ^ 2) - D.expect fun z => (∑ i, W z i * v i) ^ 2 / m) = (m - 1) * fpVar vhA, D.expect_div_const _ m, hBSubstitute the first moment; move the
/ moutside the second expectation withexpect_div_const : E[f / c] = E[f] / c; substitute the second moment. The goal is now free of expectations altogether.fpVar_eq_sum_sq hnRewrite the target’s
fpVar vas , the same computational form used in the complete-randomization proof, so that both sides are rational functions of , , and .Goal⊢ m / ↑n * ∑ i, v i ^ 2 - (m / ↑n * ∑ i, v i ^ 2 + m * (m - 1) / (↑n * (↑n - 1)) * ((∑ i, v i) ^ 2 - ∑ i, v i ^ 2)) / m = (m - 1) * ((↑n * ∑ i, v i ^ 2 - (∑ i, v i) ^ 2) / (↑n * (↑n - 1)))field_simp; ringClear the denominators —
hn,hn1andhmare the three non-vanishing facts in the hypothesis list, and this is what they are for — and check the resulting polynomial identity. This step is the two lines of cancellation the informal proof performs between its last two displays.
The engine now applies twice. The treated group of a completely randomized
experiment is a simple random sample of size
Theorem (the sample variance is unbiased). Under complete randomization with
For the treated group, apply the previous theorem with
For the control group, take
which is the same formula with
theorem completeRandomization_expect_sampleVar_treated {n₁ : ℕ} (hle : n₁ ≤ n)
(P : Population n) (hn₁ : 1 < n₁) (hlt : n₁ < n) :
(completeRandomization n n₁ hle).expect (fun z => sampleVar P.y1 z) = P.S1sq := by
have hn : (n : ℝ) ≠ 0 := Nat.cast_ne_zero.mpr (by omega)
have hnm1 : (n : ℝ) - 1 ≠ 0 := by
have : (2 : ℝ) ≤ (n : ℝ) := by exact_mod_cast (by omega : 2 ≤ n)
linarith
have hm : (n₁ : ℝ) ≠ 0 := Nat.cast_ne_zero.mpr (by omega)
have hm1 : (n₁ : ℝ) - 1 ≠ 0 := by
have : (2 : ℝ) ≤ (n₁ : ℝ) := by exact_mod_cast hn₁
linarith
-- on the support the group size is `n₁`, so the statistic has the exchangeable shape
have hsupp : ∀ z, (completeRandomization n n₁ hle).prob z ≠ 0 →
sampleVar P.y1 z
= ((∑ i, Z z i * (P.y1 i) ^ 2) - (∑ i, Z z i * P.y1 i) ^ 2 / (n₁ : ℝ))
/ ((n₁ : ℝ) - 1) := by
intro z hz
have hcard : #z = n₁ := card_eq_of_prob_ne_zero hz
rw [sampleVar_eq P.y1 z (by rw [hcard]; exact hm), hcard,
sum_mem_eq_sum_Z_mul z (fun i => (P.y1 i) ^ 2), sum_mem_eq_sum_Z_mul z P.y1]
have hss : (completeRandomization n n₁ hle).expect
(fun z => (∑ i, Z z i * (P.y1 i) ^ 2) - (∑ i, Z z i * P.y1 i) ^ 2 / (n₁ : ℝ))
= ((n₁ : ℝ) - 1) * fpVar P.y1 :=
(completeRandomization n n₁ hle).expect_ss_of_exchangeable P.y1 Z (n₁ : ℝ) hn hnm1 hm
(fun z i => Z_mul_self z i)
(fun i => completeRandomization_propensity i)
(fun i j hij => completeRandomization_jointPropensity hij)
rw [(completeRandomization n n₁ hle).expect_congr_of_support hsupp,
(completeRandomization n n₁ hle).expect_div_const _ ((n₁ : ℝ) - 1), hss,
Population.S1sq]
field_simphave hn : (n : ℝ) ≠ 0 := …; have hnm1 : (n : ℝ) - 1 ≠ 0 := …; have hm : (n₁ : ℝ) ≠ 0 := …; have hm1 : (n₁ : ℝ) - 1 ≠ 0 := …Four non-vanishing facts, in the order the engine lemma and the final
field_simpwill want them.hm1is the one that needs the full strength of1 < n₁rather than0 < n₁: it is the denominator of the sample variance, and at a single treated unit that denominator is zero.rw [sampleVar_eq P.y1 z (by rw [hcard]; exact hm), hcard, sum_mem_eq_sum_Z_mul z (fun i => (P.y1 i) ^ 2), sum_mem_eq_sum_Z_mul z P.y1]The
hsuppstep: rewrite the sample variance, on an assignment of positive probability, into the shape the engine lemma expects.sampleVar_eqis the “sum of squares minus square of sum” identity over the groupz, and it turns the left-hand side into the state shown below — its side condition(#z : ℝ) ≠ 0is discharged by rewriting withhcard : #z = n₁first. Thenhcardreplaces the two remaining#z, and the twosum_mem_eq_sum_Z_mulrewrites turn the sums over the treated group into indicator-weighted sums over everybody, once fory1 ^ 2and once fory1.Goal⊢ (∑ i ∈ z, P.y1 i ^ 2 - (∑ i ∈ z, P.y1 i) ^ 2 / ↑(#z)) / (↑(#z) - 1) = (∑ i, Z z i * P.y1 i ^ 2 - (∑ i, Z z i * P.y1 i) ^ 2 / ↑n₁) / (↑n₁ - 1)have hss : … := (completeRandomization n n₁ hle).expect_ss_of_exchangeable P.y1 Z (n₁ : ℝ) hn hnm1 hm (fun z i => Z_mul_self z i) (fun i => completeRandomization_propensity i) (fun i j hij => completeRandomization_jointPropensity hij)The engine, instantiated.
W := Z,m := n₁,v := P.y1, and the three hypotheses are chapter 6’sZ_mul_self : Z z i * Z z i = Z z iand chapter 7’s two counting lemmas — the propensity is and the joint propensity is , which is exactly whath1andh2demand. That the counting lemmas match the hypotheses syntactically is not an accident:expect_ss_of_exchangeablewas stated in their shape.rw [(completeRandomization n n₁ hle).expect_congr_of_support hsupp, (completeRandomization n n₁ hle).expect_div_const _ ((n₁ : ℝ) - 1)]Transfer the expectation onto the rewritten statistic, then pull the constant
/ (n₁ - 1)outside it. The remaining expectation is literally the left-hand side ofhss.Goal⊢ ((completeRandomization n n₁ hle).expect fun z => ∑ i, Z z i * P.y1 i ^ 2 - (∑ i, Z z i * P.y1 i) ^ 2 / ↑n₁) / (↑n₁ - 1) = P.S1sqhss, Population.S1sqSubstitute the engine’s answer and unfold
S1sqtofpVar P.y1. The goal is(n₁ - 1) * fpVar P.y1 / (n₁ - 1) = fpVar P.y1.Goal⊢ (↑n₁ - 1) * fpVar P.y1 / (↑n₁ - 1) = fpVar P.y1field_simpCancel, using
hm1 : (n₁ : ℝ) - 1 ≠ 0. If the theorem had said1 ≤ n₁instead of1 < n₁this is the line that would fail — and it would fail for a real reason, since with a single treated unit the sample variance is .
theorem completeRandomization_expect_sampleVar_control {n₁ : ℕ} (hle : n₁ ≤ n)
(P : Population n) (hpos : 0 < n₁) (hn₀ : 1 < n - n₁) :
(completeRandomization n n₁ hle).expect (fun z => sampleVar P.y0 zᶜ) = P.S0sq := by
set D := completeRandomization n n₁ hle with hD
have hlt : n₁ < n := by omega
have hn : (n : ℝ) ≠ 0 := Nat.cast_ne_zero.mpr (by omega)
have hnm1 : (n : ℝ) - 1 ≠ 0 := by
have : (2 : ℝ) ≤ (n : ℝ) := by exact_mod_cast (by omega : 2 ≤ n)
linarith
have hltR : (n₁ : ℝ) < (n : ℝ) := by exact_mod_cast hlt
have hm : (n : ℝ) - (n₁ : ℝ) ≠ 0 := by linarith
have hm1 : ((n : ℝ) - (n₁ : ℝ)) - 1 ≠ 0 := by
have h2 : ((n₁ : ℝ) + 2) ≤ (n : ℝ) := by exact_mod_cast (by omega : n₁ + 2 ≤ n)
linarith
-- the two moment hypotheses for the control indicator
have hone : ∀ i, D.expect (fun z => 1 - Z z i) = ((n : ℝ) - (n₁ : ℝ)) / (n : ℝ) := by
intro i
rw [Design.expect_one_sub_Z, hD, completeRandomization_propensity]
field_simp
have hpair : ∀ i j, i ≠ j → D.expect (fun z => (1 - Z z i) * (1 - Z z j))
= ((n : ℝ) - (n₁ : ℝ)) * (((n : ℝ) - (n₁ : ℝ)) - 1) / ((n : ℝ) * ((n : ℝ) - 1)) := by
intro i j hij
have hexp : D.expect (fun z => (1 - Z z i) * (1 - Z z j))
= 1 - D.propensity i - D.propensity j + D.jointPropensity i j := by
rw [D.expect_congr (f := fun z => (1 - Z z i) * (1 - Z z j))
(g := fun z => (1 - Z z i - Z z j) + Z z i * Z z j) (fun z => by ring),
D.expect_add (fun z => 1 - Z z i - Z z j) (fun z => Z z i * Z z j),
D.expect_sub (fun z => 1 - Z z i) (fun z => Z z j),
Design.expect_one_sub_Z, Design.expect_Z]
rfl
rw [hexp, hD, completeRandomization_propensity, completeRandomization_propensity,
completeRandomization_jointPropensity hij]
field_simp
ring
-- on the support the control group has size `n - n₁`
have hsupp : ∀ z, D.prob z ≠ 0 →
sampleVar P.y0 zᶜ
= ((∑ i, (1 - Z z i) * (P.y0 i) ^ 2)
- (∑ i, (1 - Z z i) * P.y0 i) ^ 2 / ((n : ℝ) - (n₁ : ℝ)))
/ (((n : ℝ) - (n₁ : ℝ)) - 1) := by
intro z hz
have hcard : #z = n₁ := card_eq_of_prob_ne_zero hz
have hc : ((#zᶜ : ℕ) : ℝ) = (n : ℝ) - (n₁ : ℝ) := by
rw [card_compl, hcard, Nat.cast_sub hle]
rw [sampleVar_eq P.y0 zᶜ (by rw [hc]; exact hm), hc,
sum_compl_eq_sum_one_sub_Z_mul z (fun i => (P.y0 i) ^ 2),
sum_compl_eq_sum_one_sub_Z_mul z P.y0]
have hss : D.expect
(fun z => (∑ i, (1 - Z z i) * (P.y0 i) ^ 2)
- (∑ i, (1 - Z z i) * P.y0 i) ^ 2 / ((n : ℝ) - (n₁ : ℝ)))
= (((n : ℝ) - (n₁ : ℝ)) - 1) * fpVar P.y0 :=
D.expect_ss_of_exchangeable P.y0 (fun z i => 1 - Z z i) ((n : ℝ) - (n₁ : ℝ)) hn hnm1 hm
(fun z i => by rw [one_sub_Z]; exact Z_mul_self zᶜ i) hone hpair
rw [D.expect_congr_of_support hsupp,
D.expect_div_const _ (((n : ℝ) - (n₁ : ℝ)) - 1), hss, Population.S0sq]
field_simpThe control proof has the same five moves; only the two moment conditions are
new, because 1 - Z is not a primitive of the library the way Z is.
set D := completeRandomization n n₁ hle with hDAbbreviate the design.
setintroduces a local definitionDand replaces every occurrence in the goal, keeping the defining equation ashDso that design-specific lemmas can still be applied by rewriting with it. The treated proof did without this; here the design appears often enough to be worth naming.have hm1 : ((n : ℝ) - (n₁ : ℝ)) - 1 ≠ 0 := by have h2 : ((n₁ : ℝ) + 2) ≤ (n : ℝ) := by exact_mod_cast (by omega : n₁ + 2 ≤ n); linarithThe control-side counterpart of
hm1.hn₀ : 1 < n - n₁is a statement about natural subtraction, so it cannot be cast directly;omegaconverts it into the equivalentn₁ + 2 ≤ n, which casts cleanly, andlinarithfinishes in . Mixing the two subtractions is the mistake this two-step dance avoids.rw [Design.expect_one_sub_Z, hD, completeRandomization_propensity]; field_simpThe first moment condition,
hone.expect_one_sub_Z : E[1 - Z i] = 1 - π iis chapter 7’s; the propensity lemma makes that1 - n₁ / n; andfield_simpchecks that this equals(n - n₁) / n.rw [D.expect_congr (f := fun z => (1 - Z z i) * (1 - Z z j)) (g := fun z => (1 - Z z i - Z z j) + Z z i * Z z j) (fun z => by ring), D.expect_add …, D.expect_sub …, Design.expect_one_sub_Z, Design.expect_Z]The second moment condition,
hpair, and itshave hexp. Expanding the product pointwise withringunderexpect_congrturns into ; thenexpect_addandexpect_subsplit it and the two indicator-expectation lemmas evaluate the first three pieces. Therflthat follows identifies the leftoverE[Z i * Z j]withjointPropensity i j, which is its definition. The result is , the inclusion–exclusion line of the informal proof.rw [hexp, hD, completeRandomization_propensity, completeRandomization_propensity, completeRandomization_jointPropensity hij]; field_simp; ringSubstitute the three chapter-7 values into that identity and verify the resulting rational-function identity. The goal shown is the state before
field_simp: it is the middle display of the informal proof, andfield_simp; ringis the “which is” that follows it.Goal⊢ 1 - ↑n₁ / ↑n - ↑n₁ / ↑n + ↑n₁ * (↑n₁ - 1) / (↑n * (↑n - 1)) = (↑n - ↑n₁) * (↑n - ↑n₁ - 1) / (↑n * (↑n - 1))rw [sampleVar_eq P.y0 zᶜ (by rw [hc]; exact hm), hc, sum_compl_eq_sum_one_sub_Z_mul z (fun i => (P.y0 i) ^ 2), sum_compl_eq_sum_one_sub_Z_mul z P.y0]The support step,
hsupp, withzᶜin place ofz: the same “sum of squares minus square of sum” rewrite, the control-group sizehc : (#zᶜ : ℝ) = n - n₁obtained fromcard_complandNat.cast_subas before, andsum_compl_eq_sum_one_sub_Z_mulin place ofsum_mem_eq_sum_Z_mul.D.expect_ss_of_exchangeable P.y0 (fun z i => 1 - Z z i) ((n : ℝ) - (n₁ : ℝ)) hn hnm1 hm (fun z i => by rw [one_sub_Z]; exact Z_mul_self zᶜ i) hone hpairThe engine again, with
and . Idempotence of the control indicator is proved byone_sub_Z : 1 - Z z i = Z zᶜ i— the control indicator is the treated indicator of the complementary assignment — followed byZ_mul_selfatzᶜ. The last two arguments are the moment conditions just established.rw [D.expect_congr_of_support hsupp, D.expect_div_const _ (((n : ℝ) - (n₁ : ℝ)) - 1), hss, Population.S0sq]; field_simpIdentical to the treated ending: transfer onto the support, pull the constant denominator out, substitute the engine’s answer, unfold
S0sq, cancel.
Adding the two:
Proposition. Under complete randomization with
On the support the two group sizes are the constants
theorem completeRandomization_expect_neymanVarEst {n₁ : ℕ} (hle : n₁ ≤ n) (P : Population n)
(hn₁ : 1 < n₁) (hn₀ : 1 < n - n₁) :
(completeRandomization n n₁ hle).expect (neymanVarEst P)
= P.S1sq / (n₁ : ℝ) + P.S0sq / ((n : ℝ) - (n₁ : ℝ)) := by
set D := completeRandomization n n₁ hle with hD
have hlt : n₁ < n := by omega
have hsupp : ∀ z, D.prob z ≠ 0 →
neymanVarEst P z
= sampleVar P.y1 z / (n₁ : ℝ) + sampleVar P.y0 zᶜ / ((n : ℝ) - (n₁ : ℝ)) := by
intro z hz
have hcard : #z = n₁ := card_eq_of_prob_ne_zero hz
have hc : ((#zᶜ : ℕ) : ℝ) = (n : ℝ) - (n₁ : ℝ) := by
rw [card_compl, hcard, Nat.cast_sub hle]
rw [neymanVarEst, hcard, hc]
rw [D.expect_congr_of_support hsupp,
D.expect_add (fun z => sampleVar P.y1 z / (n₁ : ℝ))
(fun z => sampleVar P.y0 zᶜ / ((n : ℝ) - (n₁ : ℝ))),
D.expect_div_const _ (n₁ : ℝ), D.expect_div_const _ ((n : ℝ) - (n₁ : ℝ)), hD,
completeRandomization_expect_sampleVar_treated hle P hn₁ hlt,
completeRandomization_expect_sampleVar_control hle P (by omega) hn₀]rw [neymanVarEst, hcard, hc]The
hsuppstep.neymanVarEstdivides by the realized group sizes#zand#zᶜ; on the support those are the constantsn₁and(n : ℝ) - (n₁ : ℝ), andhcardandhcput them in. Until this rewrite the estimator is not a linear combination of anything, because its coefficients are random.rw [D.expect_congr_of_support hsupp, D.expect_add (fun z => sampleVar P.y1 z / (n₁ : ℝ)) (fun z => sampleVar P.y0 zᶜ / ((n : ℝ) - (n₁ : ℝ)))]Transfer the expectation onto that constant-coefficient version, then split it with linearity. The two
funs spell out the summands, which Lean cannot infer from the goal.D.expect_div_const _ (n₁ : ℝ), D.expect_div_const _ ((n : ℝ) - (n₁ : ℝ))Pull each constant denominator outside its expectation. What remains are exactly the two theorems just proved.
Goal⊢ D.expect (sampleVar P.y1) / ↑n₁ + (D.expect fun z => sampleVar P.y0 zᶜ) / (↑n - ↑n₁) = P.S1sq / ↑n₁ + P.S0sq / (↑n - ↑n₁)hD, completeRandomization_expect_sampleVar_treated hle P hn₁ hlt, completeRandomization_expect_sampleVar_control hle P (by omega) hn₀Substitute them.
hDfirst, to turnDback into the design those theorems are stated about — the price of thesetabove. The(by omega)supplies0 < n₁for the control theorem, which is derivable fromhn₁ : 1 < n₁.
Notice what is missing from the right-hand side: the
Proposition.
Subtract the two formulas:
theorem neymanVarEst_bias {n₁ : ℕ} (hle : n₁ ≤ n) (P : Population n)
(hn₁ : 1 < n₁) (hn₀ : 1 < n - n₁) :
(completeRandomization n n₁ hle).expect (neymanVarEst P)
= (completeRandomization n n₁ hle).var (DiM P) + P.Stausq / (n : ℝ) := by
rw [completeRandomization_expect_neymanVarEst hle P hn₁ hn₀,
neyman_variance hle P (by omega) (by omega)]
ringrw [completeRandomization_expect_neymanVarEst hle P hn₁ hn₀, neyman_variance hle P (by omega) (by omega)]Both formulas at once: the left-hand side becomes
and thevaron the right becomes Neyman’s three terms. The twoby omegaarguments derive0 < n₁andn₁ < nfrom the estimator’s stronger hypotheses1 < n₁and1 < n - n₁— note that the statement keeps the stronger ones, because the left-hand side needs them.Goal⊢ P.S1sq / ↑n₁ + P.S0sq / (↑n - ↑n₁) = P.S1sq / ↑n₁ + P.S0sq / (↑n - ↑n₁) - P.Stausq / ↑n + P.Stausq / ↑nringThe goal is
. Nothing is being proved here beyond the cancellation, which is the point: the bias is not estimated or bounded, it is the literal difference between two closed-form expressions.
Note the hypotheses have changed. Neyman’s theorem needed only 1 < n₁ and 1 < n - n₁, because both
sample variances divide by
Conservative, as an inequality between two numbers
ExerciseThe estimator is conservative (core)
On average, the reported variance is at least the true variance of difference in
means:
theorem var_DiM_le_expect_neymanVarEst {n₁ : ℕ} (hle : n₁ ≤ n) (P : Population n)
(hn₁ : 1 < n₁) (hn₀ : 1 < n - n₁) :
(completeRandomization n n₁ hle).var (DiM P)
≤ (completeRandomization n n₁ hle).expect (neymanVarEst P) := by
sorryHint
One rewrite and one linarith: neymanVarEst_bias turns the goal into
Var ≤ Var + Stausq / n, and fpVar_nonneg (which needs no hypothesis) with
div_nonneg says the added term is a non-negative real divided by a
non-negative real.
Show solution
theorem var_DiM_le_expect_neymanVarEst {n₁ : ℕ} (hle : n₁ ≤ n) (P : Population n)
(hn₁ : 1 < n₁) (hn₀ : 1 < n - n₁) :
(completeRandomization n n₁ hle).var (DiM P)
≤ (completeRandomization n n₁ hle).expect (neymanVarEst P) := by
rw [neymanVarEst_bias hle P hn₁ hn₀]
have hS : (0 : ℝ) ≤ P.Stausq := fpVar_nonneg _
have hnn : (0 : ℝ) ≤ (n : ℝ) := Nat.cast_nonneg n
have : (0 : ℝ) ≤ P.Stausq / (n : ℝ) := div_nonneg hS hnn
linarithHere “conservative” refers to the precise statement
an inequality between two real numbers, both of them fixed once the
population and the design are fixed. It is not a statement about a limit, not a
statement about coverage, and — this is the one people slide past — not a
statement about any particular realization of
theorem exP_expect_neymanVarEst :
(completeRandomization 4 2 (by norm_num)).expect (neymanVarEst exP) = 1 := by
obtain ⟨h1, h0, _⟩ := exP_variances
rw [completeRandomization_expect_neymanVarEst (by norm_num) exP (by norm_num) (by norm_num),
h1, h0]
norm_num
theorem exP_neyman_gap :
(completeRandomization 4 2 (by norm_num)).expect (neymanVarEst exP)
- (completeRandomization 4 2 (by norm_num)).var (DiM exP) = 1 / 3 := by
rw [exP_expect_neymanVarEst, exP_var_DiM]
norm_numobtain ⟨h1, h0, _⟩ := exP_variancesexP_variancesis a conjunction of three equations;obtainwith an anonymous constructor pattern destructures it into named hypotheses, and the_throws away the one about , which this theorem does not need.rw [completeRandomization_expect_neymanVarEst (by norm_num) exP (by norm_num) (by norm_num), h1, h0]; norm_numQuote the general formula at
, , discharging its three side conditions ( , , ) bynorm_num, then substitute the two variances. What is left is .rw [exP_expect_neymanVarEst, exP_var_DiM]; norm_numThe gap, as a subtraction of two numbers each already computed:
. Nothing here is a new mathematical fact; the value of the theorem is that the three numbers are pinned by the kernel rather than by arithmetic in the margin.
exP_neyman_gap is that
subtraction, machine-checked. Meanwhile the assignment that treats units 2 and 3
reports
Finally, the inequality is sharp, with an equality condition that is exactly the classical one.
Theorem. Under complete randomization with
that is, if and only if the treatment effect is the same for every unit.
By the previous proposition the two sides differ by exactly
theorem expect_neymanVarEst_eq_var_iff {n₁ : ℕ} (hle : n₁ ≤ n) (P : Population n)
(hn₁ : 1 < n₁) (hn₀ : 1 < n - n₁) :
(completeRandomization n n₁ hle).expect (neymanVarEst P)
= (completeRandomization n n₁ hle).var (DiM P)
↔ ∀ i j, P.effect i = P.effect j := by
have hn : 1 < n := by omega
have hnR : (0 : ℝ) < (n : ℝ) := by
have : (1 : ℝ) < (n : ℝ) := by exact_mod_cast hn
linarith
rw [neymanVarEst_bias hle P hn₁ hn₀]
constructor
· intro h
have hzero : P.Stausq = 0 := by
have hq : P.Stausq / (n : ℝ) = 0 := by linarith
exact (div_eq_zero_iff.mp hq).resolve_right hnR.ne'
exact (fpVar_eq_zero_iff hn P.effect).mp hzero
· intro h
have hzero : P.Stausq = 0 := (fpVar_eq_zero_iff hn P.effect).mpr h
rw [hzero, zero_div, add_zero]have hn : 1 < n := by omega; have hnR : (0 : ℝ) < (n : ℝ) := …Two facts about
: at least two units, from1 < n₁and1 < n - n₁, and its positivity in . The first is the hypothesis offpVar_eq_zero_iff, the second is what rules out dividing by zero in the forward direction.rw [neymanVarEst_bias hle P hn₁ hn₀]Replace the expectation by “the variance plus the gap” — the previous proposition, used as a rewrite. The left-hand side of the
↔is now visibly of the formX + c = X, and no expectation appears in it any more.Goal⊢ (completeRandomization n n₁ hle).var (DiM P) + P.Stausq / ↑n = (completeRandomization n n₁ hle).var (DiM P) ↔ ∀ (i j : Fin n), P.effect i = P.effect jconstructorSplit the
↔into its two implications,mpandmpr. From here the two branches are separate proofs, marked by the two·bullets.intro h; have hq : P.Stausq / (n : ℝ) = 0 := by linarithForward direction.
h : Var + Stausq / n = Varis a linear equation between reals, solinarithextractsStausq / n = 0from it directly — no cancellation lemma needed, becauselinarithworks over ordered fields and treatsStausq / nas an atom.exact (div_eq_zero_iff.mp hq).resolve_right hnR.ne'A quotient is zero when the numerator is zero or the denominator is;
div_eq_zero_iffgives that disjunction andresolve_rightdiscards the second case usinghnR.ne' : (n : ℝ) ≠ 0. This is the step that needs , and it is the reason the hypothesis was derived at the top.exact (fpVar_eq_zero_iff hn P.effect).mp hzeroConclude.
fpVar_eq_zero_iff : 1 < n → (fpVar v = 0 ↔ ∀ i j, v i = v j)applied to the effect vector turnsStausq = 0— which isfpVar P.effect = 0by definition — into constant effects.intro h; have hzero : P.Stausq = 0 := (fpVar_eq_zero_iff hn P.effect).mpr h; rw [hzero, zero_div, add_zero]Backward direction, the same lemma in the other direction: constant effects make the variance of the effect vector zero, and then
zero_divandadd_zerocollapseVar + 0 / ntoVar. Both branches go through the same↔, which is why the theorem is an↔rather than two separate results.
Constant effects is the assumption “the treatment effect is the same for every
unit”. It is strictly weaker than Fisher’s sharp null of no effect at all, which
is the special case ↔ is the interesting part: under
any effect heterogeneity whatsoever, the reported variance is strictly too
large, so intervals built from it over-cover. Conservative, but never
anti-conservative.
ExerciseConstant effects (stretch)
If
theorem var_DiM_of_constant_effect {n n₁ : ℕ} (hle : n₁ ≤ n) (P : Population n)
(hpos : 0 < n₁) (hlt : n₁ < n) (c : ℝ) (hc : ∀ i, P.effect i = c) :
(completeRandomization n n₁ hle).var (DiM P)
= P.S0sq * ((n : ℝ) / ((n₁ : ℝ) * ((n : ℝ) - (n₁ : ℝ)))) := by
sorryHint
Get P.y1 = fun i => P.y0 i + c by funext and linarith from hc, then
fpVar_add_const gives fpVar_eq_zero_iff gives
neyman_variance and field_simp; ring
finishes. 1 < n has to be derived from 0 < n₁ and n₁ < n by omega before
fpVar_eq_zero_iff will apply, and field_simp wants (n₁ : ℝ) ≠ 0 and
(n : ℝ) - n₁ ≠ 0 as haves.
Show solution
theorem var_DiM_of_constant_effect {n n₁ : ℕ} (hle : n₁ ≤ n) (P : Population n)
(hpos : 0 < n₁) (hlt : n₁ < n) (c : ℝ) (hc : ∀ i, P.effect i = c) :
(completeRandomization n n₁ hle).var (DiM P)
= P.S0sq * ((n : ℝ) / ((n₁ : ℝ) * ((n : ℝ) - (n₁ : ℝ)))) := by
have hn1 : 1 < n := by omega
have hnR : (n₁ : ℝ) ≠ 0 := Nat.cast_ne_zero.mpr (by omega)
have hn0R : (n : ℝ) - (n₁ : ℝ) ≠ 0 := by
have : (n₁ : ℝ) < (n : ℝ) := by exact_mod_cast hlt
linarith
have hy1 : P.y1 = fun i => P.y0 i + c := by
funext i
have := hc i
unfold Population.effect at this
linarith
have hS1 : P.S1sq = P.S0sq := by
rw [Population.S1sq, Population.S0sq, hy1, fpVar_add_const (by omega)]
have hSt : P.Stausq = 0 :=
(fpVar_eq_zero_iff hn1 P.effect).mpr fun i j => by rw [hc i, hc j]
rw [neyman_variance hle P hpos hlt, hS1, hSt]
field_simp
ringThe general Horvitz–Thompson variance
Nothing above used a property of complete randomization until step 3, so the same
route gives the Horvitz–Thompson variance for an arbitrary design. The difference
is step 2. Horvitz–Thompson is linear in the indicators on every assignment,
not just on a support, because it involves no group sizes: HT_eq_linear has no
hypothesis on z at all. The proof of var_HT still goes through
var_congr_of_support, but look at how it is called — fun z _ => HT_eq_linear D P z throws the support hypothesis away with an underscore. That is the shape
to look for. A _ where a proof of D.prob z ≠ 0 was expected tells you the
identity is unconditional and the support lemma is doing nothing but the
rewriting.
Proposition. For any design,
Regroup the estimator: collecting the terms in
with
noncomputable def htCoef (D : Design n) (P : Population n) (i : Fin n) : ℝ :=
(1 / (n : ℝ)) * (P.y1 i / D.propensity i + P.y0 i / (1 - D.propensity i))
theorem HT_eq_linear (D : Design n) (P : Population n) (z : Assignment n) :
HT D P z
= (∑ i, htCoef D P i * Z z i)
+ -((1 / (n : ℝ)) * ∑ i, P.y0 i / (1 - D.propensity i)) := by
have h1 : HT D P z = ∑ i, (1 / (n : ℝ)) * (Z z i * P.y1 i / D.propensity i
- (1 - Z z i) * P.y0 i / (1 - D.propensity i)) := Finset.mul_sum _ _ _
calc HT D P z
= ∑ i, (1 / (n : ℝ)) * (Z z i * P.y1 i / D.propensity i
- (1 - Z z i) * P.y0 i / (1 - D.propensity i)) := h1
_ = ∑ i, (htCoef D P i * Z z i
+ -((1 / (n : ℝ)) * (P.y0 i / (1 - D.propensity i)))) :=
Finset.sum_congr rfl fun i _ => by unfold htCoef; ring
_ = (∑ i, htCoef D P i * Z z i)
+ ∑ i, -((1 / (n : ℝ)) * (P.y0 i / (1 - D.propensity i))) := Finset.sum_add_distrib
_ = (∑ i, htCoef D P i * Z z i)
+ -((1 / (n : ℝ)) * ∑ i, P.y0 i / (1 - D.propensity i)) := by
rw [Finset.sum_neg_distrib, ← Finset.mul_sum]
theorem var_HT (D : Design n) (P : Population n) :
D.var (HT D P)
= ∑ i, ∑ j, htCoef D P i * htCoef D P j
* (D.jointPropensity i j - D.propensity i * D.propensity j) := by
have h := D.var_congr_of_support (f := HT D P)
(g := fun z => (∑ i, htCoef D P i * Z z i)
+ -((1 / (n : ℝ)) * ∑ i, P.y0 i / (1 - D.propensity i)))
(fun z _ => HT_eq_linear D P z)
rw [h, D.var_add_const, D.var_linear]have h1 : HT D P z = ∑ i, (1 / (n : ℝ)) * (…) := Finset.mul_sum _ _ _HT_eq_linearfirst. The definition ofHThas the1 / noutside the sum;Finset.mul_summoves it in, so that each summand can be regrouped independently. Thehaveis stated separately only because thecalcblock below wants it as a named first step._ = ∑ i, (htCoef D P i * Z z i + -((1 / (n : ℝ)) * (P.y0 i / (1 - D.propensity i)))) := Finset.sum_congr rfl fun i _ => by unfold htCoef; ringThe regrouping, one summand at a time. Unfolding
htCoefmakes the goal a polynomial identity inZ z iand the four constants, andringverifies it — the same move as inDiM_eq_linear_of_card, and again it is where the control outcome acquires its+sign._ = (∑ i, htCoef D P i * Z z i) + ∑ i, -(…) := Finset.sum_add_distribSplit into the linear part and the constant part.
rw [Finset.sum_neg_distrib, ← Finset.mul_sum]Tidy the constant part into the shape the statement gives it: pull the negation out of the sum, then pull the
1 / nback out. Thecalcchain now ends at the statement.have h := D.var_congr_of_support (f := HT D P) (g := fun z => (∑ i, htCoef D P i * Z z i) + -(…)) (fun z _ => HT_eq_linear D P z)var_HTproper. The support lemma is being used purely as a rewriting device: its third argument should be a proof valid only whereD.prob z ≠ 0, and what is supplied ignores that hypothesis entirely. The goal afterrw [h]is the variance of the linear-plus-constant form.Goal⊢ (D.var fun z => ∑ i, htCoef D P i * Z z i + -(1 / ↑n * ∑ i, P.y0 i / (1 - D.propensity i))) = ∑ i, ∑ j, htCoef D P i * htCoef D P j * (D.jointPropensity i j - D.propensity i * D.propensity j)rw [h, D.var_add_const, D.var_linear]Two lemmas and the theorem is done:
var_add_constdeletes the constant tail andvar_linear— the one formula this whole chapter is built on — evaluates what is left.
Specializing
ExerciseHorvitz–Thompson under Bernoulli (stretch)
Under the Bernoulli design the Horvitz–Thompson variance collapses to a single
sum,
theorem bernoulli_var_HT {p : ℝ} (hp0 : 0 ≤ p) (hp1 : p ≤ 1) (P : Population n) :
(bernoulliDesign n p hp0 hp1).var (HT (bernoulliDesign n p hp0 hp1) P)
= p * (1 - p) * ∑ i, (htCoef (bernoulliDesign n p hp0 hp1) P i) ^ 2 := by
sorryHint
Start from var_HT, then apply sum_sum_diag_offdiag with c = p - p² (the
diagonal, from Design.jointPropensity_self plus bernoulli_propensity) and
d = 0 (the off-diagonal, from bernoulli_jointPropensity and
bernoulli_propensity); ring closes. The d = 0 is independence, and it is
why the answer is a single sum.
Show solution
theorem bernoulli_var_HT {p : ℝ} (hp0 : 0 ≤ p) (hp1 : p ≤ 1) (P : Population n) :
(bernoulliDesign n p hp0 hp1).var (HT (bernoulliDesign n p hp0 hp1) P)
= p * (1 - p) * ∑ i, (htCoef (bernoulliDesign n p hp0 hp1) P i) ^ 2 := by
rw [var_HT, sum_sum_diag_offdiag (htCoef (bernoulliDesign n p hp0 hp1) P) (p - p ^ 2) 0
(fun i j => (bernoulliDesign n p hp0 hp1).jointPropensity i j
- (bernoulliDesign n p hp0 hp1).propensity i * (bernoulliDesign n p hp0 hp1).propensity j)
(fun i => by rw [Design.jointPropensity_self, bernoulli_propensity]; ring)
(fun i j hij => by
rw [bernoulli_jointPropensity hij, bernoulli_propensity, bernoulli_propensity]; ring)]
ringReading autoformalized Lean
Two formalizations of this chapter’s results that a language model will happily produce, and that a careful reader should reject.
Conservativeness, quantified over the wrong thing
theorem neyman_conservative_bad {n n₁ : ℕ} (hle : n₁ ≤ n) (P : Population n)
(hn₁ : 1 < n₁) (hn₀ : 1 < n - n₁) (z : Assignment n) :
(completeRandomization n n₁ hle).var (DiM P) ≤ neymanVarEst P z := by
sorry
This reads well in English — “the Neyman estimator is at least as large as the
true variance” — and it is false. The z in the binder makes it a claim about
every realization; the true theorem is a claim about the expectation, and
Design.expect is nowhere in the statement. The example population refutes it
outright: exP_neymanVarEst_of_treated_two_three says exP_var_DiM says the variance is
An “estimator” that is not a statistic
noncomputable def varEstBad {n : ℕ} (P : Population n) (n₁ : ℕ)
(_z : Assignment n) : ℝ :=
P.S1sq / (n₁ : ℝ) + P.S0sq / ((n : ℝ) - (n₁ : ℝ))
theorem varEstBad_conservative … :
(completeRandomization n n₁ hle).var (DiM P)
≤ (completeRandomization n n₁ hle).expect (varEstBad P n₁) := by
sorry
Every property you would check holds: it has the right type, its expectation is
Lean will not stop you. Population is in scope inside any definition, and the
type Assignment n → ℝ does not encode “depends only on what _z is the
conventional way to say so, and, less helpfully for a reader, it also silences
the unused-variable linter that would otherwise have flagged it — and the body
mentions P.y1 with no Z z i or ∑ i ∈ z restricting which units it is read
for. The library’s honest point estimators carry a lemma making this checkable
(HT_eq_observed and DiM_eq_observed, each showing the estimator factors
through Yobs); neymanVarEst has no such lemma, so there the check has to be
done by reading the definition — y1 summed over z, y0 over zᶜ.
Two habits worth keeping from this chapter. First, count the hypotheses and ask
what each one is for: n₁ ≤ n was data for the design, 0 < n₁ < n was needed
for Neyman, 1 < n₁ and 1 < n - n₁ were needed only once sample variances
appeared. Second, when a statement about an estimator holds unconditionally, look
for a division by zero that is quietly making a degenerate case true.