Causal inferenceChapter 07
Designs
Assignment mechanisms as probability mass functions, with expectations, propensities, and standard designs.
Contents
- A design is its pmf
- Expectation is a weighted sum, and that is all
- Indicators, events, probabilities
- Propensities
- Variance and covariance
- Computing with a design you can see
- The Bernoulli design
- Complete randomization
- Two designs that exist to break things
- How a probabilist would set this up
- Reading autoformalized Lean
- Footnotes
In the design-based model, the population and potential outcomes are fixed. The
treatment assignment is random according to a known design: a probability
mass function on the
This chapter defines designs, expectations, marginal and joint propensities, complete randomization, and Bernoulli randomization. Because the assignment space is finite, all probabilities and expectations are finite sums.
A design is its pmf
Informally: a design assigns to each possible treated set
DefinitionDesign
A design for
structure Design (n : ℕ) where
prob : Assignment n → ℝ
nonneg : ∀ z, 0 ≤ prob z
sum_one : ∑ z, prob z = 1The definition illustrates three patterns used throughout the library.
It is a structure, so the axioms travel with the data. A design is not “a
function prob, plus two hypotheses I will remember to write down in every
theorem”. It is one bundle with three fields, two of which happen to be proofs.
You cannot construct a Design without supplying them, and once you have a
D : Design n you can use them: D.sum_one is the proof that the
probabilities sum to one, ready to be rewritten with.
The proof fields cost nothing at the point of use. nonneg and sum_one
live in Prop, and Lean’s proof irrelevance means any two proofs of the same
proposition are interchangeable. So a design carries exactly one piece of
information, its pmf: two designs with the same prob are the same design.1
There is no sample space beyond the treated sets. Assignment n is an
abbreviation for Finset (Fin n), the type of finite sets of units, which Lean
knows is a finite type with ∑ z, D.prob z in the sum_one
field is a sum over all ∑ z, f z means ∑ z ∈ Finset.univ, f z, and univ here is the set of all treated
sets. A “random variable” is then any function Assignment n → ℝ, and no
measurability condition is needed, because on a finite type there is nothing to
measure.
Expectation is a weighted sum, and that is all
noncomputable def expect : ℝ := ∑ z, D.prob z * f zThis is the definition a statistician already has in mind: average the
statistic over the randomization, weighting by the design. f ranges over
functions of the assignment alone, which is exactly right for a design-based
theory — an estimator, an indicator, a squared deviation are all functions of
Everything the tutorial needs about expectation follows from three facts about finite sums. Here is linearity, in full.
Proposition (linearity of expectation). For every design and every pair of
statistics
Both sides are finite sums over the
by distributivity in the summand and then by regrouping a finite sum of sums.
The two remaining sums are
theorem expect_add : D.expect (fun z => f z + g z) = D.expect f + D.expect g := by
simp only [expect, mul_add, Finset.sum_add_distrib]The proof is one tactic with three rewrite rules, and it is worth walking
through slowly, because simp only is the workhorse of the whole library.
simp only [h₁, h₂, …] rewrites the goal left-to-right with the given lemmas,
repeatedly, and with nothing else — unlike a bare simp, which is free to use
several thousand lemmas you did not name and can therefore break when Mathlib
changes.
The goal starts as
D.expect (fun z => f z + g z) = D.expect f + D.expect g
Naming a definition in a simp only list unfolds it (Lean generates an
equation lemma expect D f = ∑ z, D.prob z * f z when the definition is made),
so after expect the goal is
∑ z, D.prob z * (f z + g z) = (∑ z, D.prob z * f z) + ∑ z, D.prob z * g z
mul_add : a * (b + c) = a * b + a * c then rewrites the summand. Note that it
rewrites under the binder: the body of ∑ z, … is a function of z, and
simp descends into it using congruence lemmas. The goal becomes
∑ z, (D.prob z * f z + D.prob z * g z) = (∑ z, D.prob z * f z) + ∑ z, D.prob z * g z
and Finset.sum_add_distrib : ∑ i ∈ s, (f i + g i) = ∑ i ∈ s, f i + ∑ i ∈ s, g i
turns the left-hand side into the right-hand side. The two sides are now
syntactically equal, and simp only closes any goal that becomes rfl.
That is the entire content of “expectation is linear” in a finite population:
distributivity, plus the fact that a finite sum of sums is a sum of finite sums.
No integrability, no dominated convergence, no hypotheses at all — expect_add
holds for every design and every pair of statistics.
The second axiom of a design first earns its keep in one small lemma.
Lemma. For every design and every constant
theorem expect_const (c : ℝ) : D.expect (fun _ => c) = c := by
simp only [expect, ← Finset.sum_mul, D.sum_one, one_mul]Four entries, one per step of that display. expect unfolds the definition,
leaving ∑ z, D.prob z * c. ← Finset.sum_mul pulls the constant c out of
the sum (the lemma is stated as (∑ i ∈ s, f i) * b = ∑ i ∈ s, f i * b, so the
arrow uses it backwards) to give (∑ z, D.prob z) * c. D.sum_one — the
structure field, used as a rewrite rule — replaces ∑ z, D.prob z by 1, and
one_mul finishes. If you ever wondered what work the “probabilities sum to
one” axiom actually does in a proof of unbiasedness, this is it: it makes
The form of linearity that unbiasedness proofs actually use is the one for a sum over units, since every estimator in this tutorial is a sum over units.
Lemma. For any finite index set and any family of statistics
Write both sides out: the left is
theorem expect_sum {ι : Type*} (s : Finset ι) (F : ι → Assignment n → ℝ) :
D.expect (fun z => ∑ i ∈ s, F i z) = ∑ i ∈ s, D.expect (F i) := by
simp only [expect, Finset.mul_sum]
exact Finset.sum_commsimp only [expect, Finset.mul_sum]expectunfolds both sides, andFinset.mul_sum : b * ∑ i ∈ s, f i = ∑ i ∈ s, b * f ipushes the factorD.prob zinside the sum over units — the “taking inside” of the informal proof. What is left is one double sum on each side, summed in opposite orders.Goal⊢ ∑ x, ∑ i ∈ s, D.prob x * F i x = ∑ i ∈ s, ∑ z, D.prob z * F i zexact Finset.sum_commFinset.sum_comm : ∑ x ∈ s, ∑ y ∈ t, f x y = ∑ y ∈ t, ∑ x ∈ s, f x yis the exchange, and it matches the goal exactly. That exchange is the only “theorem” in the whole of our probability theory, and in a finite population it is free.
ExerciseScalar linearity
Warm-up. Pull a constant out of an expectation:
theorem expect_const_mul (c : ℝ) : D.expect (fun z => c * f z) = c * D.expect f := by
sorryHint
The same shape as expect_add: simp only [expect, Finset.mul_sum] unfolds
and moves c inside the right-hand sum, leaving summands D.prob z * (c * f z)
and c * (D.prob z * f z) to be matched.
Finset.sum_congr rfl fun z _ => by ring is the honest way to reassociate them
inside the binder.
Show solution
theorem expect_const_mul (c : ℝ) : D.expect (fun z => c * f z) = c * D.expect f := by
simp only [expect, Finset.mul_sum]
exact Finset.sum_congr rfl fun z _ => by ringExerciseStatistics that agree on the support
Core. Two statistics that agree wherever the design puts positive probability have the same expectation. This is the lemma that lets chapter 8 replace difference-in-means by Horvitz–Thompson on the assignments complete randomization can actually produce.
theorem expect_congr_of_support {f g : Assignment n → ℝ}
(h : ∀ z, D.prob z ≠ 0 → f z = g z) : D.expect f = D.expect g := by
sorryHint
Reduce to one assignment at a time with
refine Finset.sum_congr rfl fun z _ => ?_, then by_cases hz : D.prob z = 0:
in the zero branch both sides are 0 * _ (rw [hz, zero_mul, zero_mul]), and
otherwise h z hz rewrites f z to g z.
Show solution
theorem expect_congr_of_support {f g : Assignment n → ℝ}
(h : ∀ z, D.prob z ≠ 0 → f z = g z) : D.expect f = D.expect g := by
refine Finset.sum_congr rfl fun z _ => ?_
by_cases hz : D.prob z = 0
· rw [hz, zero_mul, zero_mul]
· rw [h z hz]Indicators, events, probabilities
The bridge between algebra and probability is that an event’s probability is the expectation of its indicator. Both sides exist in the library:
noncomputable def probOf (p : Assignment n → Prop) [DecidablePred p] : ℝ :=
∑ z ∈ univ.filter p, D.prob zLemma. For every event
theorem expect_indicator (p : Assignment n → Prop) [DecidablePred p] :
D.expect (fun z => if p z then 1 else 0) = D.probOf p := by
simp only [expect, probOf, mul_ite, mul_one, mul_zero]
exact (Finset.sum_filter _ _).symmprobOf sums the pmf over the assignments satisfying a decidable predicate;
expect_indicator says that summing P(z) · 1{p(z)} over everything gives the
same number.
simp only [expect, probOf, mul_ite, mul_one, mul_zero]Both definitions are unfolded, and then three lemmas move the multiplication inside the
if:mul_ite : a * (if c then b else d) = if c then a * b else a * ddistributes it, andmul_one/mul_zeroevaluate the two branches, so the summandD.prob z * (if p z then 1 else 0)becomesif p z then D.prob z else 0. That is the informal “zero off the event,P(z)on it”.Goal⊢ (∑ x, if p x then D.prob x else 0) = ∑ z with p z, D.prob zexact (Finset.sum_filter _ _).symmFinset.sum_filter : ∑ i ∈ s with p i, f i = ∑ i ∈ s, if p i then f i else 0states exactly the remaining equality, but the other way round, so.symmflips it. Dropping the zero summands from a finite sum is the one and only content of the lemma.
The [DecidablePred p] in square brackets is an instance argument: univ.filter p has to know which assignments to keep, so Lean finds, by itself, the
algorithm that decides p. For predicates like fun z => i ∈ z it always can,
and you will never have to supply it.
Propensities
A design is a distribution over
noncomputable def propensity (i : Fin n) : ℝ := D.expect (fun z => Z z i)noncomputable def jointPropensity (i j : Fin n) : ℝ := D.expect (fun z => Z z i * Z z j)In a design-based setting the propensity score
That these two are the same object at different arities is worth making explicit, and the library does so with the inclusion probability of an arbitrary set of units:
noncomputable def inclusion (S : Finset (Fin n)) : ℝ :=
D.expect (fun z => ∏ i ∈ S, Z z i)The inclusion probability Z is
real-valued, so a product of indicators is again a number and
prod_Z : ∏ i ∈ S, Z z i = if S ⊆ z then 1 else 0 turns it into a counting
problem. Specializing gives the two summaries back: the propensity is the
inclusion probability of the singleton
theorem propensity_eq_inclusion (i : Fin n) : D.propensity i = D.inclusion {i} := by
simp only [propensity, inclusion, Finset.prod_singleton]simp only [propensity, inclusion, Finset.prod_singleton]The goal shown is the state after the first two names have unfolded the two definitions: both sides are expectations, of
Z z ion the left and of a one-factor product on the right.Finset.prod_singleton : ∏ x ∈ {a}, f x = f acollapses that product — under the binder, since it sits insideD.expect (fun z => …)— and the two sides become syntactically equal, which is whatsimp onlyneeds to finish.Goal⊢ (D.expect fun z => Z z i) = D.expect fun z => ∏ i ∈ {i}, Z z i
The propensity is also a probability in the ordinary sense.
Lemma.
theorem propensity_eq_probOf (i : Fin n) : D.propensity i = D.probOf (fun z => i ∈ z) := by
unfold propensity Z
exact D.expect_indicator (fun z => i ∈ z)unfold propensity ZUnfolding
propensityexposes the expectation, and unfoldingZturns its argument from an indicator function into a literalif p z then 1 else 0. That syntactic shape is the whole point:expect_indicatoris stated about theif, and untilZis out of the way it cannot match.Goal⊢ (D.expect fun z => if i ∈ z then 1 else 0) = D.probOf fun z => i ∈ zexact D.expect_indicator (fun z => i ∈ z)The previous lemma, applied to the event “unit
iis treated”. The predicate has to be given explicitly because Lean cannot guess how to split the goal’sifback into a predicate; everything else, including theDecidablePredinstance, is found for it.
ExerciseThe control indicator
Warm-up. The expected value of the control indicator is
@[simp] theorem expect_one_sub_Z (i : Fin n) :
D.expect (fun z => 1 - Z z i) = 1 - D.propensity i := by
sorryHint
D.expect_sub (fun _ => (1 : ℝ)) (fun z => Z z i) is the statement with E[1]
and E[Z i] still unevaluated; simpa only [D.expect_const 1, expect_Z] using
that fact simplifies both it and the goal until they coincide.
Show solution
@[simp] theorem expect_one_sub_Z (i : Fin n) :
D.expect (fun z => 1 - Z z i) = 1 - D.propensity i := by
have hsub := D.expect_sub (fun _ => (1 : ℝ)) (fun z => Z z i)
simpa only [D.expect_const 1, expect_Z] using hsubExercisePropensities sum to the expected sample size
Core.
theorem sum_propensity : ∑ i, D.propensity i = D.expect (fun z => (#z : ℝ)) := by
sorryHint
Unfold propensity, then use expect_sum backwards —
rw [← D.expect_sum Finset.univ (fun i z => Z z i)] — to turn the sum of
expectations into the expectation of ∑ i, Z z i;
D.expect_congr fun z => sum_Z z then identifies that summand with #z.
Show solution
theorem sum_propensity : ∑ i, D.propensity i = D.expect (fun z => (#z : ℝ)) := by
simp only [propensity]
rw [← D.expect_sum Finset.univ (fun i z => Z z i)]
exact D.expect_congr fun z => sum_Z zVariance and covariance
The two second-order summaries are defined the way they always are, as expectations of centred quantities.
noncomputable def cov : ℝ :=
D.expect (fun z => (f z - D.expect f) * (g z - D.expect g))noncomputable def var : ℝ := D.expect (fun z => (f z - D.expect f) ^ 2)Nothing here is surprising; what matters is what the variance is over. It is
over the randomization and nothing else. There is no superpopulation, so
var_const).
Proposition (the shortcut formula). For any design and any statistics
Write
and taking expectations term by term — by linearity, and using
The variance is the case
theorem cov_eq_expect_mul_sub_mul :
D.cov f g = D.expect (fun z => f z * g z) - D.expect f * D.expect g := by
unfold cov expect
have h : ∀ z ∈ (univ : Finset (Assignment n)),
D.prob z * ((f z - ∑ w, D.prob w * f w) * (g z - ∑ w, D.prob w * g w))
= D.prob z * (f z * g z)
- (∑ w, D.prob w * g w) * (D.prob z * f z)
- (∑ w, D.prob w * f w) * (D.prob z * g z)
+ ((∑ w, D.prob w * f w) * (∑ w, D.prob w * g w)) * D.prob z :=
fun z _ => by ring
rw [Finset.sum_congr rfl h, Finset.sum_add_distrib, Finset.sum_sub_distrib,
Finset.sum_sub_distrib, ← Finset.mul_sum, ← Finset.mul_sum, ← Finset.mul_sum, D.sum_one]
ringIt is the longest proof in Design.lean and it is still a dozen lines, which is
a fair summary of how much probability theory the finite-population view needs.
The reason it is not three lines is that it does not use the expect lemmas at
all: it drops to the sums underneath them, so that a single ring can do the
expansion that the informal proof does in one line of algebra.
unfold cov expectDown to sums. Every
D.expecton both sides — the outer one and the two inner centring constants — becomes an explicit∑ z, D.prob z * …. The goal is ugly and that is deliberate: it is now a statement about finite sums of reals, and finite sums of reals are whatFinset.sum_congrandringknow about.Goal⊢ ∑ z, D.prob z * (fun z => (f z - ∑ z, D.prob z * f z) * (g z - ∑ z, D.prob z * g z)) z = ∑ z, D.prob z * (fun z => f z * g z) z - (∑ z, D.prob z * f z) * ∑ z, D.prob z * g zhave h : ∀ z ∈ univ, … := fun z _ => by ringThe expansion, done once, at a fixed assignment. Writing
for the sum∑ w, D.prob w * f wand for∑ w, D.prob w * g w,hsays that the summandD.prob z * ((f z - μ) * (g z - ν))equals the four-term expression whose terms areD.prob z * (f z * g z), thenν * (D.prob z * f z)andμ * (D.prob z * g z)subtracted, then(μ * ν) * D.prob zadded — which is the informal multiplied through by , with each term arranged so that the constant stands to the left of aD.prob z * …factor. That arrangement is not fussiness: it is what lets the next two steps pull the constants out. Because is fixed, and are just numbers, andringproves the identity without knowing anything about them. The goal shown is the state afterFinset.sum_congr rfl hhas replaced every summand by that expansion; therflthere says the index set is unchanged.Goal⊢ ∑ x, (D.prob x * (f x * g x) - (∑ w, D.prob w * g w) * (D.prob x * f x) - (∑ w, D.prob w * f w) * (D.prob x * g x) + ((∑ w, D.prob w * f w) * ∑ w, D.prob w * g w) * D.prob x) = ∑ z, D.prob z * (fun z => f z * g z) z - (∑ z, D.prob z * f z) * ∑ z, D.prob z * g zFinset.sum_add_distrib, Finset.sum_sub_distrib, Finset.sum_sub_distribThree splittings, applied outside in. The expression is
at the top level, sosum_add_distribsplits off the+ Dfirst, and the twosum_sub_distribs then peel- Cand- B. Four separate sums remain, one per term of the expansion.Goal⊢ ∑ x, D.prob x * (f x * g x) - ∑ x, (∑ w, D.prob w * g w) * (D.prob x * f x) - ∑ x, (∑ w, D.prob w * f w) * (D.prob x * g x) + ∑ x, ((∑ w, D.prob w * f w) * ∑ w, D.prob w * g w) * D.prob x = ∑ z, D.prob z * (fun z => f z * g z) z - (∑ z, D.prob z * f z) * ∑ z, D.prob z * g z← Finset.mul_sum, ← Finset.mul_sum, ← Finset.mul_sumFinset.mul_sum : b * ∑ i ∈ s, f i = ∑ i ∈ s, b * f i, used backwards — the arrow — so each of the three sums with a constant in front of it has that constant pulled out. This is ” and are constants, so they come out of the expectation”, spelled at the level of sums.Goal⊢ ∑ x, D.prob x * (f x * g x) - (∑ w, D.prob w * g w) * ∑ i, D.prob i * f i - (∑ w, D.prob w * f w) * ∑ i, D.prob i * g i + ((∑ w, D.prob w * f w) * ∑ w, D.prob w * g w) * ∑ i, D.prob i = ∑ z, D.prob z * (fun z => f z * g z) z - (∑ z, D.prob z * f z) * ∑ z, D.prob z * g zD.sum_oneThe last of the three pulled-out sums is
with nothing else in it, and the design’s second axiom replaces it by1. This is the only place in the proof where the object being manipulated is a design rather than an arbitrary family of weights.ringWhat remains is
with , and standing for three particular sums.ringtreats each sum as an opaque atom and does the cancellation, exactly as the last line of the informal proof does.
The variance form is then a corollary, and the two rewrites that get there are
worth reading because the same manoeuvre — turn f z * f z into f z ^ 2
inside a binder — recurs whenever a squared statistic meets a product lemma.
theorem var_eq_expect_sq_sub_sq :
D.var f = D.expect (fun z => f z ^ 2) - (D.expect f) ^ 2 := by
rw [var_eq_cov_self, cov_eq_expect_mul_sub_mul]
rw [D.expect_congr (f := fun z => f z * f z) (g := fun z => f z ^ 2) fun z => (sq (f z)).symm]
rw [sq]rw [var_eq_cov_self, cov_eq_expect_mul_sub_mul]var_eq_cov_self : D.var f = D.cov f fis proved just above it in the file bysimp only [var, cov, sq]: a square is a product of a thing with itself, so the two definitions coincide. Rewriting with it and then with the shortcut formula turns the left-hand side into the covariance offwith itself, written out.Goal⊢ (D.expect fun z => f z * f z) - D.expect f * D.expect f = (D.expect fun z => f z ^ 2) - D.expect f ^ 2rw [D.expect_congr (f := fun z => f z * f z) (g := fun z => f z ^ 2) fun z => (sq (f z)).symm]expect_congrsays two statistics that agree pointwise have the same expectation; here the pointwise fact issq (f z) : f z ^ 2 = f z * f z, reversed with.symm, so the product inside the expectation becomes a square. The two statistics have to be named explicitly — the(f := …)syntax — because a plainrwcannot do this: rewriting under a binder is beyondrw, andexpect_congrexists precisely to smuggle a pointwise rewrite past one.Goal⊢ (D.expect fun z => f z ^ 2) - D.expect f * D.expect f = (D.expect fun z => f z ^ 2) - D.expect f ^ 2rw [sq]The two sides now differ in one place only:
D.expect f * D.expect fon the left againstD.expect f ^ 2on the right.rw [sq]rewrites the first occurrence of a square it can reach — not thef z ^ 2under the binder, which it cannot get at — so it hitsD.expect f ^ 2, turns it into the product, and the two sides become identical.
ExerciseVariance of a treatment indicator
Core.
theorem ex_var_Z (D : Design n) (i : Fin n) :
D.var (fun z => Z z i) = D.propensity i * (1 - D.propensity i) := by
sorryHint
Start from Design.var_eq_expect_sq_sub_sq. The squared indicator is the
indicator (Z_sq), so D.expect_congr fun z => Z_sq z i turns E[Z i ^ 2]
into E[Z i], Design.expect_Z makes that a propensity, and ring finishes.
Show solution
theorem ex_var_Z (D : Design n) (i : Fin n) :
D.var (fun z => Z z i) = D.propensity i * (1 - D.propensity i) := by
rw [Design.var_eq_expect_sq_sub_sq, D.expect_congr fun z => Z_sq z i, Design.expect_Z]
ringExerciseCovariance of two treatment indicators
Core.
theorem ex_cov_Z (D : Design n) (i j : Fin n) :
D.cov (fun z => Z z i) (fun z => Z z j)
= D.jointPropensity i j - D.propensity i * D.propensity j := by
sorryHint
Rewrite with Design.cov_eq_expect_mul_sub_mul and then Design.expect_Z
twice, once per marginal; the remaining expectation E[Z i * Z j] is
Design.jointPropensity i j by definition, so rfl closes it.
Show solution
theorem ex_cov_Z (D : Design n) (i j : Fin n) :
D.cov (fun z => Z z i) (fun z => Z z j)
= D.jointPropensity i j - D.propensity i * D.propensity j := by
rw [Design.cov_eq_expect_mul_sub_mul, Design.expect_Z, Design.expect_Z]
rflComputing with a design you can see
Three units, eight assignments. At this size a design is a table, and Lean will evaluate it for you.
#eval (univ : Finset (Assignment 3))
theorem card_assignments_three : Fintype.card (Assignment 3) = 8 := by decidedecide is the tactic for a goal that is decidable: Lean compiles the
proposition to a Boolean-valued computation, runs it, and accepts the goal if
the answer is true. It works here because everything in sight is finite — sets
of Fin 3, cardinalities, memberships — and it fails immediately on anything
involving ℝ, where equality is not decidable. Keep that division in mind: the
combinatorial half of a design computation is decide’s job, the
arithmetic half is norm_num’s.
To compute an expectation we need the eight-term expansion:
theorem sum_assignments_three (f : Assignment 3 → ℝ) :
∑ z, f z
= f ∅ + f {0} + f {1} + f {2} + f {0, 1} + f {0, 2} + f {1, 2} + f {0, 1, 2} := by
rw [show (univ : Finset (Assignment 3))
= {∅, {0}, {1}, {2}, {0, 1}, {0, 2}, {1, 2}, {0, 1, 2}} from by decide]
repeat rw [Finset.sum_insert (by decide)]
rw [Finset.sum_singleton]
ringrw [show (univ : Finset (Assignment 3)) = {∅, {0}, {1}, {2}, {0, 1}, {0, 2}, {1, 2}, {0, 1, 2}} from by decide]show T from eis a proof ofTsupplied inline, and hereTis theFinsetequality “the eight assignments listed are all of them”.decidechecks it by computation. Rewriting with it replacesunivin the goal’s sum by the explicit list, so the sum now has a syntactic head to peel.repeat rw [Finset.sum_insert (by decide)]{a, b, c}is notation forinsert a (insert b {c}), andFinset.sum_insert : a ∉ s → ∑ x ∈ insert a s, f x = f a + ∑ x ∈ s, f xpeels one element off the front, provided that element is not in the rest — a decidable side condition, hence the inline(by decide).repeatapplies the rewrite until it fails, which is after all seven insertions.rw [Finset.sum_singleton]What is left of the sum is the last element on its own, and
Finset.sum_singleton : ∑ x ∈ {a}, f x = f aremoves the sum sign.ringBoth sides are now the same eight terms added up, but the peeling produced them right-nested,
f ∅ + (f {0} + (…)), while the statement is written left-nested.ringreassociates. It is the only arithmetic in the proof, and it does no arithmetic.
Now a real design, written out as a table:
noncomputable def oneOfThree : Design 3 where
prob z := if #z = 1 then 1 / 3 else 0
nonneg z := by split <;> norm_num
sum_one := by
rw [sum_assignments_three fun z => if #z = 1 then (1 : ℝ) / 3 else 0]
norm_num [Finset.card_insert_of_notMem, Fin.ext_iff]
@[simp] theorem oneOfThree_prob (z : Assignment 3) :
oneOfThree.prob z = if #z = 1 then 1 / 3 else 0 := rflThe two proof obligations are exactly the two axioms.
nonneg z := by split <;> norm_numspliton theifin the pmf gives two goals,0 ≤ 1 / 3and0 ≤ 0, andnorm_numcloses both. A field of a structure can be given by a tactic proof like any other goal;nonneg z := by …is the field, with the assignmentzalready in scope.rw [sum_assignments_three fun z => if #z = 1 then (1 : ℝ) / 3 else 0]The
sum_onefield. The lemma just proved is applied to this particular summand, turning the sum overunivinto the eight explicit terms. The lambda has to be written out because Lean will not guess which function ofzis meant.norm_num [Finset.card_insert_of_notMem, Fin.ext_iff]Eight
ifs, each conditional on a cardinality:#∅ = 1is false,#{0} = 1is true,#{0, 1} = 1is false, and so on. Two lemmas are needed to settle them.Finset.card_insert_of_notMem : a ∉ s → #(insert a s) = #s + 1peels an element off a literal set, and its side condition is a disequality ofFin 3values;Fin.ext_iffreduces(0 : Fin 3) ≠ 2to0 ≠ 2inℕ, whichnorm_numcan decide. Without it the side conditions stick, the cardinalities never evaluate, and the sum stops half-way. With them, three terms are1 / 3and five are0.@[simp] theorem oneOfThree_prob (z : Assignment 3) : oneOfThree.prob z = if #z = 1 then 1 / 3 else 0 := rflThe pmf as a rewrite rule. It is true by
rfl—probis thatifby definition — and it is tagged@[simp]so that later proofs can get at the table without unfolding the structure by hand.
Then the propensity is a computation rather than a theorem:
theorem oneOfThree_propensity (i : Fin 3) : oneOfThree.propensity i = 1 / 3 := by
rw [Design.propensity, Design.expect,
sum_assignments_three fun z => oneOfThree.prob z * Z z i]
fin_cases i <;>
norm_num [Z, Fin.ext_iff, Finset.card_insert_of_notMem]
theorem oneOfThree_prob_eq (z : Assignment 3) :
oneOfThree.prob z = (completeRandomization 3 1 (by norm_num)).prob z := by
rw [oneOfThree_prob, completeRandomization_prob]
norm_numrw [Design.propensity, Design.expect, sum_assignments_three fun z => oneOfThree.prob z * Z z i]Unfold the propensity to an expectation, the expectation to a sum, and expand that sum into its eight terms. Every quantity in the goal is now a number written out.
Goali : Fin 3 ⊢ oneOfThree.prob ∅ * Z ∅ i + oneOfThree.prob {0} * Z {0} i + oneOfThree.prob {1} * Z {1} i + oneOfThree.prob {2} * Z {2} i + oneOfThree.prob {0, 1} * Z {0, 1} i + oneOfThree.prob {0, 2} * Z {0, 2} i + oneOfThree.prob {1, 2} * Z {1, 2} i + oneOfThree.prob {0, 1, 2} * Z {0, 1, 2} i = 1 / 3fin_cases ii : Fin 3still stands for an arbitrary unit, andZ {0, 1} icannot be evaluated until it is known which unit that is.fin_casesproduces the three concrete goals.<;> norm_num [Z, Fin.ext_iff, Finset.card_insert_of_notMem]Each branch is eight probabilities times eight indicator evaluations.
Zunfolds the indicator to itsif,Fin.ext_iffdecides the memberships ((0 : Fin 3) ∈ {1, 2}reduces to two disequalities inℕ), andFinset.card_insert_of_notMemsettles the cardinalities insideoneOfThree’s ownif, as it did above. Exactly one of the eight terms survives: five assignments have probability0because their size is not1, two of the three singletons do not containiand so contribute1 / 3 * 0, and the singleton{i}contributes1 / 3 * 1.rw [oneOfThree_prob, completeRandomization_prob]The second theorem, which checks the hand-written design against the library’s. Both rewrites replace a pmf by its defining
if, leaving(if #z = 1 then 1 / 3 else 0) = if #z = 1 then (↑(Nat.choose 3 1))⁻¹ else 0.norm_num and , so the two branches agree and theifs are equal. Note what this second theorem buys:completeRandomization 3 1has pmf on the singletons, so the general propensity theorem gives with no enumeration at all — and the brute-force computation above is then a check on the general theorem, not a substitute for it.
Splitting into the three units and evaluating eight indicators apiece is the honest way to check a design on a small example, and it is also a good way to convince yourself that a general theorem you are about to read says what you think it says.
The Bernoulli design
Flip an independent coin per unit.
noncomputable def bernoulliDesign (n : ℕ) (p : ℝ) (hp0 : 0 ≤ p) (hp1 : p ≤ 1) : Design n where
prob z := p ^ #z * (1 - p) ^ (n - #z)
nonneg z := mul_nonneg (pow_nonneg hp0 _) (pow_nonneg (by linarith) _)
sum_one := by
have h := sum_bernoulli_weight_mul_subset n p (∅ : Finset (Fin n))
simpa using hnonneg z := mul_nonneg (pow_nonneg hp0 _) (pow_nonneg (by linarith) _)The first axiom, in term mode.
mul_nonnegneeds both factors nonnegative, and each factor is a power, sopow_nonnegreduces each to a statement about its base:hp0 : 0 ≤ pfor the first, and for the second0 ≤ 1 - p, whichlinarithderives fromhp1 : p ≤ 1. The_s are the exponents, which Lean reads off the goal.have h := sum_bernoulli_weight_mul_subset n p (∅ : Finset (Fin n))The second axiom is the binomial theorem, and it is the
case of the one lemma this design needs. Instantiated there, that lemma reads∑ z, p ^ #z * (1 - p) ^ (n - #z) * (if ∅ ⊆ z then 1 else 0) = p ^ #∅.simpa using hsimpa … using hsimplifies bothhand the goal and then matches them. Here the simplification empties out the empty set on both sides ofh: the condition∅ ⊆ zholds always, so the indicator is1and the factor disappears, and#∅ = 0makes the right-hand sidep ^ 0 = 1. What is left is the goal.
That one lemma is worth the page it takes.
theorem sum_bernoulli_weight_mul_subset (n : ℕ) (p : ℝ) (S : Finset (Fin n)) :
∑ z : Assignment n, p ^ #z * (1 - p) ^ (n - #z) * (if S ⊆ z then 1 else 0) = p ^ #S := by
classical
set g : Fin n → ℝ := fun i => if i ∈ S then 0 else (1 - p) with hg
have key := Finset.prod_add (fun _ : Fin n => p) g univ
rw [Finset.powerset_univ] at key
have hL : ∏ _i : Fin n, (p + g _i) = p ^ #S := by
have hpt : ∀ i : Fin n, p + g i = if i ∈ S then p else 1 := by
intro i
simp only [hg]
by_cases h : i ∈ S <;> simp [h]
rw [Finset.prod_congr rfl fun i _ => hpt i, Finset.prod_ite_mem, Finset.univ_inter,
Finset.prod_const]
have hR : ∀ t : Assignment n,
(∏ _i ∈ t, p) * (∏ i ∈ univ \ t, g i)
= p ^ #t * (1 - p) ^ (n - #t) * (if S ⊆ t then 1 else 0) := by
intro t
have hcard : #(univ \ t) = n - #t := by
rw [← Finset.compl_eq_univ_sdiff, Finset.card_compl, Fintype.card_fin]
rw [Finset.prod_const]
by_cases h : S ⊆ t
·
rw [ite_eq_left h, mul_one]
have : ∀ i ∈ univ \ t, g i = 1 - p := by
intro i hi
simp only [hg]
exact ite_eq_right fun hiS => absurd (h hiS) (Finset.mem_sdiff.mp hi).2
rw [Finset.prod_congr rfl this, Finset.prod_const, hcard]
·
rw [ite_eq_right h, mul_zero]
refine mul_eq_zero_of_right _ ?_
obtain ⟨i, hiS, hit⟩ := Finset.not_subset.mp h
refine Finset.prod_eq_zero (Finset.mem_sdiff.mpr ⟨Finset.mem_univ i, hit⟩) ?_
simp only [hg]
exact ite_eq_left hiS
rw [← hL, key]
exact (Finset.sum_congr rfl fun t _ => hR t).symmThe mechanism is Finset.prod_add, which is the “multiply out the brackets”
step of a first probability course:
A sum over subsets of the population is a sum over assignments — this is the
payoff of representing an assignment as a treated set rather than as a vector of
indicators. Take
and the right-hand side becomes sum_one field is a one-liner citing this lemma.
The Lean is that argument with the two sides of Finset.prod_add identified
separately — the left as hL, the right termwise as hR — and then joined.
classicalThe family
gbelow is defined by anifoni ∈ S, andFinset.prod_addwants aDecidableEqinstance.classicalsupplies decidability for everything in scope so that neither has to be threaded through by hand. It changes no mathematics; it changes which instances Lean will accept.set g : Fin n → ℝ := fun i => if i ∈ S then 0 else (1 - p) with hgThe
of the informal derivation: on , off it.setintroduces the abbreviation and replaces any matching expression in the goal by the new name, andwith hgkeeps the defining equation around, which the sub-proofs below need in order to unfoldgagain.have key := Finset.prod_add (fun _ : Fin n => p) g univ“Multiply out the brackets”, instantiated at
and :∏ i ∈ s, (f i + g i) = ∑ t ∈ s.powerset, (∏ i ∈ t, f i) * ∏ i ∈ s \ t, g i. Nothing is proved by this step; it puts the identity into the context, where the two rewrites at the end can use it.rw [Finset.powerset_univ] at keyprod_addsums overuniv.powerset, the set of subsets of the universe;Finset.powerset_univ : univ.powerset = univsays that this is the same as the universe of the typeFinset (Fin n)— the set of all assignments. This is the step where “a sum over subsets of the population is a sum over assignments” becomes a rewrite rather than a remark.Goalg : Fin n → ℝ := fun i => if i ∈ S then 0 else 1 - p key : ∏ i, (p + g i) = ∑ t, (∏ i ∈ t, p) * ∏ i ∈ univ \ t, g i ⊢ (∑ z, p ^ #z * (1 - p) ^ (n - #z) * if S ⊆ z then 1 else 0) = p ^ #Shave hpt : ∀ i : Fin n, p + g i = if i ∈ S then p else 1The left-hand side of
prod_add, factor by factor. Inside thehave,simp only [hg]unfoldsgandby_cases h : i ∈ S <;> simp [h]checks the two cases: on the factor is , off it .rw [Finset.prod_congr rfl fun i _ => hpt i, Finset.prod_ite_mem, Finset.univ_inter, Finset.prod_const]Four rewrites finishing
hL, and the goal shown is the state after the first.Finset.prod_congr rfl (…)replaces every factor usinghpt(therflsays the index set is unchanged).Finset.prod_ite_mem : ∏ i ∈ s, (if i ∈ t then f i else 1) = ∏ i ∈ s ∩ t, f idrops the factors equal to1, leaving a product overuniv ∩ S;Finset.univ_intersimplifies that toS; andFinset.prod_const : ∏ _x ∈ s, b = b ^ #sgivesp ^ #S. That is the informal “the left-hand side collapses to ”.Goalhpt : ∀ (i : Fin n), p + g i = if i ∈ S then p else 1 ⊢ (∏ i, if i ∈ S then p else 1) = p ^ #Shave hcard : #(univ \ t) = n - #tNow
hR, the right-hand side, one assignmenttat a time; the goal shown is its statement withhcardalready available. The little cardinality fact is proved byrw [← Finset.compl_eq_univ_sdiff, Finset.card_compl, Fintype.card_fin]: “everything exceptt” is the complement oft, whose size isn - #t. It is needed because the exponent on the right-hand side is writtenn - #twhile the product on the left ranges overuniv \ t.Goalt : Assignment n hcard : #(univ \ t) = n - #t ⊢ (∏ _i ∈ t, p) * ∏ i ∈ univ \ t, g i = p ^ #t * (1 - p) ^ (n - #t) * if S ⊆ t then 1 else 0rw [Finset.prod_const]The first factor
∏ _i ∈ t, pis a constant product, so it isp ^ #t— which is already the first factor on the right.by_cases h : S ⊆ tthen splits into the two branches shown, in which the indicator has been evaluated byite_eq_left handmul_one, respectivelyite_eq_right handmul_zero.Goalcase pos h : S ⊆ t ⊢ p ^ #t * ∏ i ∈ univ \ t, g i = p ^ #t * (1 - p) ^ (n - #t) case neg h : ¬S ⊆ t ⊢ p ^ #t * ∏ i ∈ univ \ t, g i = 0rw [Finset.prod_congr rfl this, Finset.prod_const, hcard]The branch where
. Then no outside lies in — that is what the littlehaveabove this line proves, usingFinset.mem_sdiffto geti ∉ tandhto rule outi ∈ S— so every factor of∏ i ∈ univ \ t, g iis1 - p.prod_constmakes that(1 - p) ^ #(univ \ t)andhcardrewrites the exponent ton - #t.refine Finset.prod_eq_zero (Finset.mem_sdiff.mpr ⟨Finset.mem_univ i, hit⟩) ?_The branch where
. The right-hand side is0, andmul_eq_zero_of_rightreduces the goal to showing that the second factor, the product overuniv \ t, vanishes.Finset.not_subset.mp hproduces a witness: someiwithhiS : i ∈ Sandhit : i ∉ t. Thatilies inuniv \ t, andg i = 0there becausei ∈ S— the remaining?_, closed byite_eq_left hiS.Finset.prod_eq_zerothen kills the whole product: this is the informal “the factor kills exactly the assignments that fail to treat all of ”.rw [← hL, key]Back in the main goal.
← hLrewrites the right-hand sidep ^ #Sbackwards into the product∏ i, (p + g i), andkeythen turns that product into the sum over all assignments. Both sides are now sums over the same index set.Goal⊢ (∑ z, p ^ #z * (1 - p) ^ (n - #z) * if S ⊆ z then 1 else 0) = ∑ t, (∏ i ∈ t, p) * ∏ i ∈ univ \ t, g iexact (Finset.sum_congr rfl fun t _ => hR t).symmTwo sums over the same set whose summands agree are equal, which is
Finset.sum_congr;hRis the termwise agreement. It proves the equation in the opposite direction to the goal, hence.symm.
Everything else about the Bernoulli design is a corollary.
Lemma. Under the Bernoulli design with parameter
theorem bernoulli_propensity (i : Fin n) : (bernoulliDesign n p hp0 hp1).propensity i = p := by
rw [Design.propensity_eq_inclusion, bernoulli_inclusion, Finset.card_singleton, pow_one]rw [Design.propensity_eq_inclusion, bernoulli_inclusion, Finset.card_singleton, pow_one]Four rewrites, in the order of the informal proof.
propensity_eq_inclusionturns into the inclusion probability of{i}.bernoulli_inclusion, which is the master computation transported to this design by unfoldinginclusionandexpectand applyingprod_Z, evaluates that top ^ #{i}.Finset.card_singletoncomputes#{i} = 1, andpow_onefinishes. Every later Bernoulli fact — the joint propensity, the expected number treated — is this same chain with a different set in place of{i}.
ExerciseIndependence: π ij = p²
Core. Distinct units are independent under the Bernoulli design: the
probability that both
theorem bernoulli_jointPropensity {i j : Fin n} (hij : i ≠ j) :
(bernoulliDesign n p hp0 hp1).jointPropensity i j = p ^ 2 := by
sorryHint
One rw chain. Design.jointPropensity_eq_inclusion _ hij (which needs
i ≠ j) and bernoulli_inclusion reduce the goal to p ^ #{i, j} = p ^ 2;
then Finset.card_insert_of_notMem, whose side condition i ∉ {j} is
by simpa using hij, and Finset.card_singleton compute
Show solution
theorem bernoulli_jointPropensity {i j : Fin n} (hij : i ≠ j) :
(bernoulliDesign n p hp0 hp1).jointPropensity i j = p ^ 2 := by
rw [Design.jointPropensity_eq_inclusion _ hij, bernoulli_inclusion,
Finset.card_insert_of_notMem (by simpa using hij), Finset.card_singleton]ExerciseExpected number of treated units
Core.
theorem ex_bernoulli_expected_treated :
(bernoulliDesign n p hp0 hp1).expect (fun z => (#z : ℝ)) = n * p := by
sorryHint
rw [← Design.sum_propensity] turns the goal into ∑ i, π i = n * p; then one
simp only with bernoulli_propensity, Finset.sum_const, Finset.card_univ,
Fintype.card_fin and nsmul_eq_mul counts the n constant summands.
Show solution
theorem ex_bernoulli_expected_treated :
(bernoulliDesign n p hp0 hp1).expect (fun z => (#z : ℝ)) = n * p := by
rw [← Design.sum_propensity]
simp only [bernoulli_propensity, Finset.sum_const, Finset.card_univ, Fintype.card_fin,
nsmul_eq_mul]Complete randomization
Fix
noncomputable def completeRandomization (n n₁ : ℕ) (h : n₁ ≤ n) : Design n where
prob z := if #z = n₁ then ((n.choose n₁ : ℕ) : ℝ)⁻¹ else 0
nonneg z := by
by_cases hz : #z = n₁
· simp only [hz, ite_eq_left]
exact inv_nonneg.mpr (Nat.cast_nonneg (n.choose n₁))
· simp [hz]
sum_one := by
have hpos : 0 < n.choose n₁ := Nat.choose_pos h
have hne : ((n.choose n₁ : ℕ) : ℝ) ≠ 0 := Nat.cast_ne_zero.mpr hpos.ne'
calc ∑ z : Assignment n, (if #z = n₁ then ((n.choose n₁ : ℕ) : ℝ)⁻¹ else 0)
= ∑ z ∈ (univ : Finset (Fin n)).powersetCard n₁, ((n.choose n₁ : ℕ) : ℝ)⁻¹ := by
rw [← Finset.sum_filter]
congr 1
ext z
simp [Finset.mem_powersetCard]
_ = ((n.choose n₁ : ℕ) : ℝ) * ((n.choose n₁ : ℕ) : ℝ)⁻¹ := by
rw [Finset.sum_const, nsmul_eq_mul, Finset.card_powersetCard, Finset.card_univ,
Fintype.card_fin]
_ = 1 := mul_inv_cancel₀ hneThe pmf is
Lemma. Suppose
The assignments of nonzero probability are exactly the
The hypothesis is doing that one job and no other.
by_cases hz : #z = n₁The
nonnegfield. In the branch where#z = n₁,simp only [hz, ite_eq_left]selects the then branch andinv_nonneg.mpr (Nat.cast_nonneg _)observes that the inverse of a nonnegative real is nonnegative — a cast of a natural number is nonnegative whatever it is, so nothing about is needed here. Otherwisesimp [hz]selects0.have hne : ((n.choose n₁ : ℕ) : ℝ) ≠ 0 := Nat.cast_ne_zero.mpr hpos.ne'The
sum_onefield opens by recording the one fact the hypothesis buys:Nat.choose_pos h : 0 < n.choose n₁(this is whereh : n₁ ≤ nis used), and thenNat.cast_ne_zeromoves that fromℕtoℝ. Without it the final cancellation is false, not merely unprovable.rw [← Finset.sum_filter]The
calcchain begins. The sum hasif #z = n₁ then _ else 0as its summand, andFinset.sum_filter, used backwards, drops the zero terms: the sum is now over the assignments satisfying the condition. What has to be shown is that this filtered set ispowersetCard n₁ univ.Goal⊢ ∑ a with #a = n₁, (↑(n.choose n₁))⁻¹ = ∑ z ∈ powersetCard n₁ univ, (↑(n.choose n₁))⁻¹congr 1The two sides are sums of the same constant over two index sets, so
congr 1peels off the summand and leaves the index sets to be compared;ext zthen reduces set equality to membership, one assignment at a time, andsimp [Finset.mem_powersetCard]finishes —z ∈ powersetCard n₁ univunfolds toz ⊆ univ ∧ #z = n₁, whose first half is free.Goalz : Assignment n ⊢ z ∈ {a | #a = n₁} ↔ z ∈ powersetCard n₁ univrw [Finset.sum_const, nsmul_eq_mul, Finset.card_powersetCard, Finset.card_univ, Fintype.card_fin]The counting step.
Finset.sum_const : ∑ _x ∈ s, b = #s • breplaces the sum of a constant by a scalar multiple,nsmul_eq_multurns thatℕ-action into ordinary multiplication by a cast, andFinset.card_powersetCard : #(powersetCard n s) = (#s).choose nsupplies the count.card_univandFintype.card_finthen rewrite#(univ : Finset (Fin n))asn, giving ._ = 1 := mul_inv_cancel₀ hnemul_inv_cancel₀ : a ≠ 0 → a * a⁻¹ = 1, withhneas the side condition. The_iscalcnotation for the previous right-hand side.
The inclusion probability is again a counting problem, and Mathlib already knows the count.
Lemma. Let
A subset of size
theorem card_filter_subset_powersetCard (S : Finset (Fin n)) (k : ℕ) (hS : #S ≤ k) :
#(((univ : Finset (Fin n)).powersetCard k).filter (S ⊆ ·)) = (n - #S).choose (k - #S) := by
have h := Finset.card_filter_powersetCard_subset S (univ : Finset (Fin n)) k
(Finset.subset_univ S) hS
rwa [Finset.card_univ, Fintype.card_fin] at hhave h := Finset.card_filter_powersetCard_subset S (univ : Finset (Fin n)) k (Finset.subset_univ S) hSMathlib states the count for subsets of an arbitrary finite set
t:#{x ∈ powersetCard n t | s ⊆ x} = (#t - #s).choose (n - #s), givens ⊆ tand#s ≤ n. Instantiatingt := univneeds the first side condition (Finset.subset_univ, free) and the second (hS, the hypothesis).rwa [Finset.card_univ, Fintype.card_fin] at hhnow says the count is(#univ - #S).choose (k - #S)and the goal says(n - #S).choose (k - #S). The two#univrewrites —Finset.card_univtoFintype.card (Fin n), thenFintype.card_finton— close the gap, and theaofrwasupplies the resulting hypothesis as the proof. The whole lemma is bookkeeping over the fact that the population isuniv.
Having committed to treating all of
which for Nat.choose algebra:
theorem choose_ratio_one (h1 : 1 ≤ n₁) (hn : n₁ ≤ n) :
(((n - 1).choose (n₁ - 1) : ℕ) : ℝ) / ((n.choose n₁ : ℕ) : ℝ) = (n₁ : ℝ) / (n : ℝ) := by
obtain ⟨j, rfl⟩ : ∃ j, n₁ = j + 1 := ⟨n₁ - 1, by omega⟩
obtain ⟨m, rfl⟩ : ∃ m, n = m + 1 := ⟨n - 1, by omega⟩
have hjm : j ≤ m := by omega
have hpos : 0 < (m + 1).choose (j + 1) := Nat.choose_pos (by omega)
have hne : ((m + 1).choose (j + 1) : ℝ) ≠ 0 := Nat.cast_ne_zero.mpr hpos.ne'
have hm : ((m : ℝ) + 1) ≠ 0 := by positivity
have key : ((m + 1) * m.choose j : ℕ) = ((m + 1).choose (j + 1) * (j + 1) : ℕ) :=
Nat.add_one_mul_choose_eq m j
have key' : ((m : ℝ) + 1) * (m.choose j : ℝ) = ((m + 1).choose (j + 1) : ℝ) * ((j : ℝ) + 1) := by
exact_mod_cast key
simp only [Nat.add_sub_cancel]
push_cast
rw [div_eq_div_iff hne hm]
linear_combination key'On paper the ratio is one cancellation. In Lean the obstacle is that n - 1
and n₁ - 1 are natural subtractions, and the proof’s whole strategy is to
make sure they never have to be reasoned about.
obtain ⟨j, rfl⟩ : ∃ j, n₁ = j + 1 := ⟨n₁ - 1, by omega⟩The trick. Rather than reason about
n₁ - 1, produce ajwithn₁ = j + 1and let therflpattern substitute it away: after this line the variablen₁no longer exists, and every occurrence has becomej + 1. The witness isn₁ - 1and the equation is arithmetic inℕ, whichomegadecides usingh1 : 1 ≤ n₁. The secondobtaindoes the same forn, and the goal shown is the state after both.Goalj m : ℕ h1 : 1 ≤ j + 1 hn : j + 1 ≤ m + 1 ⊢ ↑((m + 1 - 1).choose (j + 1 - 1)) / ↑((m + 1).choose (j + 1)) = ↑(j + 1) / ↑(m + 1)have hpos : 0 < (m + 1).choose (j + 1) := Nat.choose_pos (by omega)Side conditions, gathered before the algebra. The line before this one,
have hjm : j ≤ m := by omega, ishnrestated without the+ 1s, foromega’s convenience. ThenNat.choose_posneedsj + 1 ≤ m + 1, whichomegasupplies;hnecasts that positivity intoℝas a non-vanishing denominator; andhm : (m : ℝ) + 1 ≠ 0is the other denominator, bypositivity. Both are needed by name in the rewrite below — clearing a denominator is only legitimate when it is nonzero, and Lean will not take that on trust.have key' : ((m : ℝ) + 1) * (m.choose j : ℝ) = ((m + 1).choose (j + 1) : ℝ) * ((j : ℝ) + 1) := by exact_mod_cast keyThe one real fact about binomial coefficients:
Nat.add_one_mul_choose_eq : (n + 1) * C(n, k) = C(n + 1, k + 1) * (k + 1), which iskey, stated inℕ.exact_mod_castrestates it inℝ, moving every cast to where the goal wants it — the sort of step that is invisible on paper and unavoidable here.simp only [Nat.add_sub_cancel]Nat.add_sub_cancel : n + m - m = n. Now thatn₁isj + 1andnism + 1, the truncated subtractions in the statement are literallyj + 1 - 1andm + 1 - 1, and this rewrites them tojandm. Truncation never gets a chance to bite.push_castThe casts are still arranged as
↑(j + 1)— a cast of a sum.push_castpushes every cast towards the leaves, giving↑j + 1, which is the shapekey'andhmare stated in.rw [div_eq_div_iff hne hm]div_eq_div_iff : b ≠ 0 → d ≠ 0 → (a / b = c / d ↔ a * d = c * b). This is where the two non-vanishing facts are spent, and what is left is a polynomial identity in the two binomial coefficients and↑m,↑j.Goal⊢ ↑(m.choose j) * (↑m + 1) = (↑j + 1) * ↑((m + 1).choose (j + 1))linear_combination key'linear_combination eproves a goallhs = rhsby checking thatlhs - rhs - (e.lhs - e.rhs) = 0as a polynomial identity — here with coefficient1onkey'. The goal andkey'are the same equation with the factors written in a different order, so the difference is zero andringinsidelinear_combinationsees it. No division and no cancellation is performed anywhere inℕ.
The two-element version, choose_ratio_two, is the same proof with + 2 for
+ 1 and two applications of Nat.add_one_mul_choose_eq chained by a calc
instead of one.
And the payoff, twice.
Theorem. Suppose
By the counting lemma, the number of
using
TheoremPropensity under complete randomization
The propensity half of that theorem, as the library states it: for every unit
Note what is not assumed: nothing beyond
theorem completeRandomization_propensity (i : Fin n) :
(completeRandomization n n₁ h).propensity i = (n₁ : ℝ) / (n : ℝ) := by
have hn : 0 < n := Nat.lt_of_le_of_lt (Nat.zero_le (i : ℕ)) i.isLt
have hcardS : #({i} : Finset (Fin n)) = 1 := Finset.card_singleton i
rw [Design.propensity_eq_inclusion, completeRandomization_inclusion]
rcases Nat.lt_or_ge n₁ 1 with hlt | hge
·
have hz : n₁ = 0 := by omega
rw [card_filter_subset_powersetCard_of_lt _ _ (by omega)]
simp [hz]
· rw [card_filter_subset_powersetCard _ _ (by omega), hcardS, ← div_eq_mul_inv]
exact choose_ratio_one hge hhave hn : 0 < n := Nat.lt_of_le_of_lt (Nat.zero_le (i : ℕ)) i.isLtThere is a unit, so there is at least one unit:
i.isLtis the proof thati’s underlying natural number is< n. This is the Lean way of saying “the statement quantifies over a unit, so the population is non-empty” — a fact a textbook would not stop for and a proof about needs. The next line,have hcardS : #({i} : Finset (Fin n)) = 1 := Finset.card_singleton i, records the other triviality the counting lemma will hand back: a singleton has one element.rw [Design.propensity_eq_inclusion, completeRandomization_inclusion]The same two moves as under the Bernoulli design: propensity to inclusion probability of
{i}, then this design’s inclusion lemma. That lemma is the counting statement in probability form — the number of -sets containing{i}, times — and its own proof is the pmf’sifpushed throughprod_ZandFinset.sum_ite_mem.Goali : Fin n hn : 0 < n hcardS : #{i} = 1 ⊢ ↑(#({x ∈ powersetCard n₁ univ | {i} ⊆ x})) * (↑(n.choose n₁))⁻¹ = ↑n₁ / ↑nrcases Nat.lt_or_ge n₁ 1 with hlt | hgeThe case distinction the counting lemma forces: it needs
#S ≤ n₁, which forS = {i}means1 ≤ n₁.rcasesonNat.lt_or_gegives two branches, and what is shown is each branch after its opening rewrites: on the leftcard_filter_subset_powersetCard_of_lthas returned a count of0, on the right the three rewrites of step 5 have turned the count into a ratio of binomial coefficients.Goalcase inl hlt : n₁ < 1 hz : n₁ = 0 ⊢ ↑0 * (↑(n.choose n₁))⁻¹ = ↑n₁ / ↑n case inr hge : n₁ ≥ 1 ⊢ ↑((n - 1).choose (n₁ - 1)) / ↑(n.choose n₁) = ↑n₁ / ↑nsimp [hz]The degenerate branch, where
hz : n₁ = 0. The left side is0because the count is, and the right side is0 / n, which is0. Nothing here needshnor the value of .rw [card_filter_subset_powersetCard _ _ (by omega), hcardS, ← div_eq_mul_inv]The real branch. The count is
(n - #{i}).choose (n₁ - #{i}),hcardSrewrites#{i}to1, and← div_eq_mul_invturnsa * b⁻¹back intoa / b, which is the shapechoose_ratio_oneis stated in. Rewriting into a quotient rather than out of one is the usual direction here: Mathlib prefersa * b⁻¹for computation anda / bfor reading.exact choose_ratio_one hge hThe binomial algebra proved above, applied with
hge : 1 ≤ n₁andh : n₁ ≤ n. Both hypotheses ofchoose_ratio_oneare exactly the two facts this branch has.
The joint propensity is the same proof at
theorem completeRandomization_jointPropensity {i j : Fin n} (hij : i ≠ j) :
(completeRandomization n n₁ h).jointPropensity i j
= ((n₁ : ℝ) * ((n₁ : ℝ) - 1)) / ((n : ℝ) * ((n : ℝ) - 1)) := by
have hcardS : #({i, j} : Finset (Fin n)) = 2 := by
rw [Finset.card_insert_of_notMem (by simpa using hij), Finset.card_singleton]
have hn2 : 2 ≤ n := by
have := Finset.card_le_card (Finset.subset_univ ({i, j} : Finset (Fin n)))
rwa [hcardS, Finset.card_univ, Fintype.card_fin] at this
rw [Design.jointPropensity_eq_inclusion _ hij, completeRandomization_inclusion]
rcases Nat.lt_or_ge n₁ 2 with hlt | hge
·
rw [card_filter_subset_powersetCard_of_lt _ _ (by omega)]
interval_cases n₁ <;> norm_num
· rw [card_filter_subset_powersetCard _ _ (by omega), hcardS, ← div_eq_mul_inv]
exact choose_ratio_two hge hhave hcardS : #({i, j} : Finset (Fin n)) = 2{i, j}isinsert i {j}, soFinset.card_insert_of_notMemgives#{j} + 1providedi ∉ {j}— which ishij : i ≠ jaftersimpatranslates membership in a singleton into equality — andFinset.card_singletonfinishes. This is where the hypothesisi ≠ jis spent, and it is why the theorem is false without it: ati = jthe set has one element, not two.have hn2 : 2 ≤ nThere are two distinct units, so there are at least two units. The proof is the same shape as
hnin the previous theorem but has to go through the set:Finset.card_le_card (Finset.subset_univ {i, j})says#{i, j} ≤ #univ, and rewriting withhcardS,Finset.card_univandFintype.card_finturns that into2 ≤ n.rw [Design.jointPropensity_eq_inclusion _ hij, completeRandomization_inclusion]As before, with the pair version of the bridge lemma —
jointPropensity_eq_inclusionneedsi ≠ jbecause∏ k ∈ {i, j}, Z z khas two factors only when the two units are distinct.rcases Nat.lt_or_ge n₁ 2 with hlt | hgeThe counting lemma now needs
2 ≤ n₁, so the split is at2. Again the goals shown are the two branches after their opening rewrites, the count being0on one side and on the other.Goalcase inl hlt : n₁ < 2 ⊢ ↑0 * (↑(n.choose n₁))⁻¹ = ↑n₁ * (↑n₁ - 1) / (↑n * (↑n - 1)) case inr hge : n₁ ≥ 2 ⊢ ↑((n - 2).choose (n₁ - 2)) / ↑(n.choose n₁) = ↑n₁ * (↑n₁ - 1) / (↑n * (↑n - 1))interval_cases n₁ <;> norm_numThe degenerate branch, and the reason it is not a one-line
simpthis time: the right-hand side is zero for two different reasons.hlt : n₁ < 2boundsn₁between0and1, sointerval_casesenumerates those two values, andnorm_numchecks each: atn₁ = 0the factorn₁is zero, atn₁ = 1the factorn₁ - 1is.exact choose_ratio_two hge hThe real branch, closed by the two-element ratio lemma exactly as the propensity was closed by the one-element one.
The contrast between the two designs is central to design-based
variance. They can be made to agree on every propensity — set
Fixing the number of treated units makes units negatively dependent: if
ExerciseThe support of complete randomization
Warm-up. Only assignments with exactly expect_congr_of_support to replace
difference-in-means by Horvitz–Thompson.
theorem card_eq_of_prob_ne_zero {z : Assignment n}
(hz : (completeRandomization n n₁ h).prob z ≠ 0) : #z = n₁ := by
sorryHint
Argue by contradiction: after by_contra hc you hold hc : #z ≠ n₁, and
simp [completeRandomization_prob, hc] shows the if in the pmf takes its
else branch, so the probability is 0 — which hz rules out.
Show solution
theorem card_eq_of_prob_ne_zero {z : Assignment n}
(hz : (completeRandomization n n₁ h).prob z ≠ 0) : #z = n₁ := by
by_contra hc
exact hz (by simp [completeRandomization_prob, hc])ExerciseThe degenerate count
Core. When fewer units are treated than
theorem card_filter_subset_powersetCard_of_lt (S : Finset (Fin n)) (k : ℕ) (hk : k < #S) :
#(((univ : Finset (Fin n)).powersetCard k).filter (S ⊆ ·)) = 0 := by
sorryHint
Finset.card_eq_zero and Finset.filter_eq_empty_iff reduce the goal to “no
k-subset z contains S”. After intro z hz hSz,
Finset.mem_powersetCard.mp hz gives #z = k and Finset.card_le_card hSz
gives #S ≤ #z, which together contradict k < #S; omega closes the
arithmetic.
Show solution
theorem card_filter_subset_powersetCard_of_lt (S : Finset (Fin n)) (k : ℕ) (hk : k < #S) :
#(((univ : Finset (Fin n)).powersetCard k).filter (S ⊆ ·)) = 0 := by
rw [Finset.card_eq_zero, Finset.filter_eq_empty_iff]
intro z hz hSz
have hcard : #z = k := (Finset.mem_powersetCard.mp hz).2
exact absurd (hcard ▸ Finset.card_le_card hSz) (by omega)ExerciseNegative dependence
Stretch. Prove the inequality above: for
theorem ex_negative_dependence {n₁ : ℕ} (hle : n₁ ≤ n) (hpos : 0 < n₁) (hlt : n₁ < n)
{i j : Fin n} (hij : i ≠ j) :
(completeRandomization n n₁ hle).jointPropensity i j
< (completeRandomization n n₁ hle).propensity i
* (completeRandomization n n₁ hle).propensity j := by
sorryHint
Rewrite with completeRandomization_jointPropensity hij and
completeRandomization_propensity, put the product over a common denominator
with div_mul_div_comm, clear the denominators with div_lt_div_iff₀ (which
wants both positive — nlinarith supplies each), and hand the polynomial
inequality to nlinarith. It will not find the witness unaided: the two sides
differ by exactly (1 : ℝ) ≤ n₁,
(n₁ : ℝ) < n and (2 : ℝ) ≤ n by exact_mod_cast, prove that product
positive with mul_pos, and pass it as nlinarith [hkey].
Show solution
theorem ex_negative_dependence {n₁ : ℕ} (hle : n₁ ≤ n) (hpos : 0 < n₁) (hlt : n₁ < n)
{i j : Fin n} (hij : i ≠ j) :
(completeRandomization n n₁ hle).jointPropensity i j
< (completeRandomization n n₁ hle).propensity i
* (completeRandomization n n₁ hle).propensity j := by
have hn1 : (1 : ℝ) ≤ (n₁ : ℝ) := by exact_mod_cast hpos
have hn1n : (n₁ : ℝ) < (n : ℝ) := by exact_mod_cast hlt
have hn2 : (2 : ℝ) ≤ (n : ℝ) := by exact_mod_cast (by omega : 2 ≤ n)
have hkey : 0 < (n₁ : ℝ) * (n : ℝ) * ((n : ℝ) - (n₁ : ℝ)) :=
mul_pos (mul_pos (by linarith) (by linarith)) (by linarith)
rw [completeRandomization_jointPropensity hij, completeRandomization_propensity,
completeRandomization_propensity, div_mul_div_comm,
div_lt_div_iff₀ (by nlinarith) (by nlinarith)]
nlinarith [hkey]Two designs that exist to break things
A design is any pmf on assignments, and the two axioms rule out very little. It is worth seeing what gets through, because chapter 8’s hypotheses are exactly the fence.
First, a design with impeccable propensities and no usable variation. Flip one fair coin for the whole population; treat everybody, or nobody.
noncomputable def allOrNothing (n : ℕ) (hn : 0 < n) : Design n where
prob z := if z = ∅ then 1 / 2 else if z = univ then 1 / 2 else 0
nonneg z := by split_ifs <;> norm_num
sum_one := by
have hne : (∅ : Assignment n) ≠ univ := by
intro h
have hmem : (⟨0, hn⟩ : Fin n) ∈ (∅ : Finset (Fin n)) := h ▸ Finset.mem_univ _
simp at hmem
have key : ∀ z : Assignment n,
(if z = ∅ then (1 : ℝ) / 2 else if z = univ then 1 / 2 else 0)
= (if z = ∅ then (1 : ℝ) / 2 else 0) + (if z = univ then (1 : ℝ) / 2 else 0) := by
intro z
rcases eq_or_ne z ∅ with h1 | h1
· have h2 : z ≠ univ := fun hc => hne (h1.symm.trans hc)
simp only [ite_eq_left h1, ite_eq_right h2, add_zero]
· rcases eq_or_ne z univ with h2 | h2
· simp only [ite_eq_right h1, ite_eq_left h2, zero_add]
· simp only [ite_eq_right h1, ite_eq_right h2, zero_add]
rw [Finset.sum_congr rfl fun z _ => key z, Finset.sum_add_distrib,
Finset.sum_ite_eq' univ (∅ : Assignment n) fun _ => (1 : ℝ) / 2,
Finset.sum_ite_eq' univ (univ : Assignment n) fun _ => (1 : ℝ) / 2]
norm_num
@[simp] theorem allOrNothing_prob {n : ℕ} {hn : 0 < n} (z : Assignment n) :
(allOrNothing n hn).prob z = if z = ∅ then 1 / 2 else if z = univ then 1 / 2 else 0 := rflnonneg z := by split_ifs <;> norm_numsplit_ifssplits on every condition in the goal at once — there are two nestedifs here — leaving three goals,0 ≤ 1 / 2twice and0 ≤ 0, all numeric.have hne : (∅ : Assignment n) ≠ univThe
sum_onefield, and the first thing it needs: the two assignments carrying probability are different assignments. This is wherehn : 0 < nearns its place in the definition — with no units,∅andunivare the same set, the two halves of the pmf would land on the same assignment and the probabilities would sum to1 / 2. The proof takes the unit⟨0, hn⟩, which lies inuniv, and derives a contradiction from its lying in∅.have key : ∀ z, (if z = ∅ then 1 / 2 else if z = univ then 1 / 2 else 0) = (if z = ∅ then 1 / 2 else 0) + (if z = univ then 1 / 2 else 0)Nested
ifs do not split under a sum; a sum of two singleifs does. So the pmf is rewritten as one term per atom. The proof is three cases —z = ∅,z = univ, neither — and in the firsthneis what rules out the secondif, which is exactly the step that would fail if the two assignments coincided.rw [Finset.sum_congr rfl fun z _ => key z, Finset.sum_add_distrib, Finset.sum_ite_eq' univ (∅ : Assignment n) fun _ => (1 : ℝ) / 2, Finset.sum_ite_eq' univ (univ : Assignment n) fun _ => (1 : ℝ) / 2]sum_congrapplieskeyunder the sum,sum_add_distribsplits it into two, and thenFinset.sum_ite_eq' s a b : (∑ x ∈ s, if x = a then b x else 0) = if a ∈ s then b a else 0collapses each: a sum with one surviving term is that term. Both conditions∅ ∈ univanduniv ∈ univare true, sonorm_numfinishes with1 / 2 + 1 / 2 = 1.
Only two assignments carry probability, so every expectation collapses to an average of two numbers.
Lemma. Under the all-or-nothing design, every statistic
Only
theorem allOrNothing_expect (f : Assignment n → ℝ) :
(allOrNothing n hn).expect f = f ∅ / 2 + f univ / 2 := by
have hne : (∅ : Assignment n) ≠ univ := by
intro h
have hmem : (⟨0, hn⟩ : Fin n) ∈ (∅ : Finset (Fin n)) := h ▸ Finset.mem_univ _
simp at hmem
have key : ∀ z : Assignment n,
(allOrNothing n hn).prob z * f z
= (if z = ∅ then f z / 2 else 0) + (if z = univ then f z / 2 else 0) := by
intro z
rw [allOrNothing_prob]
rcases eq_or_ne z ∅ with h1 | h1
· have h2 : z ≠ univ := fun hc => hne (h1.symm.trans hc)
simp only [ite_eq_left h1, ite_eq_right h2, add_zero]
ring
· rcases eq_or_ne z univ with h2 | h2
· simp only [ite_eq_right h1, ite_eq_left h2, zero_add]
ring
· simp only [ite_eq_right h1, ite_eq_right h2, zero_add, zero_mul]
rw [Design.expect, Finset.sum_congr rfl fun z _ => key z, Finset.sum_add_distrib,
Finset.sum_ite_eq' univ (∅ : Assignment n) fun z => f z / 2,
Finset.sum_ite_eq' univ (univ : Assignment n) fun z => f z / 2]
norm_numhave key : ∀ z, (allOrNothing n hn).prob z * f z = (if z = ∅ then f z / 2 else 0) + (if z = univ then f z / 2 else 0)The same decomposition as in
sum_one, now withf zcarried along: three cases,hneagain ruling out the secondifin the first of them, and aringin each of the two live branches to turn1 / 2 * f zintof z / 2. The goal shown is the main goal afterrw [Design.expect]andsum_congrhave applied it under the sum.Goal⊢ ∑ z, ((if z = ∅ then f z / 2 else 0) + if z = univ then f z / 2 else 0) = f ∅ / 2 + f univ / 2rw [Finset.sum_add_distrib]One sum of two
ifs becomes two sums of oneifeach — the shapeFinset.sum_ite_eq'recognizes.Goal⊢ ((∑ x, if x = ∅ then f x / 2 else 0) + ∑ x, if x = univ then f x / 2 else 0) = f ∅ / 2 + f univ / 2rw [Finset.sum_ite_eq' univ (∅ : Assignment n) fun z => f z / 2, Finset.sum_ite_eq' univ (univ : Assignment n) fun z => f z / 2]Each sum collapses to its one surviving term, guarded by a membership condition that is trivially true.
Goal⊢ ((if ∅ ∈ univ then f ∅ / 2 else 0) + if univ ∈ univ then f univ / 2 else 0) = f ∅ / 2 + f univ / 2norm_num∅ ∈ univanduniv ∈ univare both true, so bothifs take their first branch and the goal is an identity.
theorem allOrNothing_propensity (i : Fin n) : (allOrNothing n hn).propensity i = 1 / 2 := by
rw [Design.propensity, allOrNothing_expect,
Z_of_not_mem (by simp : i ∉ (∅ : Assignment n)), Z_of_mem (Finset.mem_univ i)]
norm_num
theorem allOrNothing_jointPropensity (i j : Fin n) :
(allOrNothing n hn).jointPropensity i j = 1 / 2 := by
rw [Design.jointPropensity, allOrNothing_expect,
Z_of_not_mem (by simp : i ∉ (∅ : Assignment n)), Z_of_mem (Finset.mem_univ i),
Z_of_mem (Finset.mem_univ j)]
norm_num
theorem allOrNothing_not_independent (i j : Fin n) :
(allOrNothing n hn).jointPropensity i j
≠ (allOrNothing n hn).propensity i * (allOrNothing n hn).propensity j := by
rw [allOrNothing_jointPropensity, allOrNothing_propensity, allOrNothing_propensity]
norm_numrw [Design.propensity, allOrNothing_expect, Z_of_not_mem (by simp : i ∉ (∅ : Assignment n)), Z_of_mem (Finset.mem_univ i)]Unfold the propensity to an expectation, apply the lemma just proved to get
Z ∅ i / 2 + Z univ i / 2, and then evaluate the two indicators with the two lemmas from chapter 6: nobody is in∅, everybody is inuniv.norm_numthen computes0 / 2 + 1 / 2.rw [Design.jointPropensity, allOrNothing_expect, Z_of_not_mem (by simp : i ∉ (∅ : Assignment n)), Z_of_mem (Finset.mem_univ i), Z_of_mem (Finset.mem_univ j)]The joint propensity is the same chain with one more indicator. The statistic is
Z z i * Z z j, so the term at∅isZ ∅ i * Z ∅ j; only the first factor is rewritten, to0, and the second is never evaluated at all —norm_numdiscards the product because of the zero in front of it. Atunivboth factors become1, and1 * 1 / 2 = 1 / 2.rw [allOrNothing_jointPropensity, allOrNothing_propensity, allOrNothing_propensity]Non-independence, and it is a calculation rather than an argument: rewrite all three quantities to their values and let
norm_numobserve that1 / 2 ≠ 1 / 2 * (1 / 2). The theorem holds for every pair of units, includingi = j— where it is not a failure of independence but the ordinary fact that unless the variance is zero.
Every unit has HT_unbiased are
theorem allOrNothing_HT_unbiased (P : Population n) :
(allOrNothing n hn).expect (HT (allOrNothing n hn) P) = P.tau :=
HT_unbiased _ P
(fun i => by rw [allOrNothing_propensity]; norm_num)
(fun i => by rw [allOrNothing_propensity]; norm_num)HT_unbiased _ P (fun i => …) (fun i => …)A term-mode proof: the general theorem, applied. Its first
_is the design, which Lean reads off the goal; then the population; then its two hypotheses, each a function from a unit to a proof about that unit’s propensity.fun i => by rw [allOrNothing_propensity]; norm_numEach hypothesis is discharged by the same two-tactic script inside a
byblock: rewrite(allOrNothing n hn).propensity ito1 / 2, then letnorm_numcheck0 < 1 / 2in the first case and1 / 2 < 1in the second. Reading a term-mode proof means reading its arguments as the obligations they discharge; here the obligations are the two sides of “the propensity is strictly between zero and one”.
And
Second, the degenerate case. A point mass is a design:
noncomputable def pointMass (z₀ : Assignment n) : Design n where
prob z := if z = z₀ then 1 else 0
nonneg z := by split <;> norm_num
sum_one := by simp
theorem pointMass_expect (z₀ : Assignment n) (f : Assignment n → ℝ) :
(pointMass z₀).expect f = f z₀ := by
show ∑ z, (if z = z₀ then (1 : ℝ) else 0) * f z = f z₀
simp
theorem pointMass_propensity (z₀ : Assignment n) (i : Fin n) :
(pointMass z₀).propensity i = Z z₀ i :=
pointMass_expect z₀ _
theorem pointMass_propensity_not_interior (z₀ : Assignment n) (i : Fin n) :
¬(0 < (pointMass z₀).propensity i ∧ (pointMass z₀).propensity i < 1) := by
rw [pointMass_propensity]
unfold Z
split <;> rintro ⟨h0, h1⟩ <;> linarithnonneg z := by split <;> norm_numThe first axiom, as for
oneOfThree: split theif, and both branches are numeric.sum_one := by simpThe pmf is
if z = z₀ then 1 else 0, and a sum of that shape overunivhas one surviving term, which is1.simpknows the lemma (Finset.sum_ite_eq'again) without being told.show ∑ z, (if z = z₀ then (1 : ℝ) else 0) * f z = f z₀pointMass_expect.showrestates the goal in a definitionally equal form — here unfoldingexpectandprobby hand — becausesimpneeds to see theifbefore it can pick out the surviving term.simpAnd it does: one term of the sum survives, with the factor
1in front off z₀.pointMass_expect z₀ _pointMass_propensityis that lemma at the statisticfun z => Z z i, and the proof is the term itself: the propensity ofiunder a point mass atz₀isZ z₀ i, one ifi ∈ z₀and zero otherwise. No design is more informative about its own propensities and none is less useful.rw [pointMass_propensity]; unfold Z; split <;> rintro ⟨h0, h1⟩ <;> linarithpointMass_propensity_not_interior, the theorem that the hypotheses ofHT_unbiasedfail. After rewriting and unfolding,splitgives the two values of the indicator;rintro ⟨h0, h1⟩then assumes the conjunction0 < π ∧ π < 1and destructures it in one move — the goal is a negation, so assuming the statement and deriving a contradiction is the proof — andlinarithfinds the contradiction:0 < 0in one branch,1 < 1in the other.
Every propensity is HT_unbiased fails — and the failure is not a technicality. Take the point
mass at “everybody treated”, where all of them are
Proposition. Let
Under this design
theorem pointMass_univ_propensity (i : Fin n) :
(pointMass (univ : Assignment n)).propensity i = 1 := by
rw [pointMass_propensity, Z_of_mem (Finset.mem_univ i)]
theorem pointMass_univ_expect_HT (P : Population n) :
(pointMass (univ : Assignment n)).expect (HT (pointMass univ) P) = mean P.y1 := by
rw [pointMass_expect]
have hZ : ∀ i : Fin n, Z (univ : Assignment n) i = 1 := fun i => Z_of_mem (Finset.mem_univ i)
simp only [HT, pointMass_univ_propensity, hZ, one_mul, div_one, sub_self, zero_mul,
zero_div, sub_zero]
rw [mean]
ring
theorem pointMass_univ_HT_bias (P : Population n) :
(pointMass (univ : Assignment n)).expect (HT (pointMass univ) P) - P.tau = mean P.y0 := by
rw [pointMass_univ_expect_HT, P.tau_eq_mean_sub_mean]
ringrw [pointMass_propensity, Z_of_mem (Finset.mem_univ i)]pointMass_univ_propensity: the propensity isZ univ i, and everybody belongs touniv, so it is1. This is the hypothesis ofHT_unbiasedfailing, stated as an equation.rw [pointMass_expect]The expectation of anything under a point mass is its value at the one assignment. From here on there is no probability left in the problem — what remains is arithmetic in the definition of the estimator.
GoalP : Population n ⊢ HT (pointMass univ) P univ = mean P.y1have hZ : ∀ i : Fin n, Z (univ : Assignment n) i = 1 := fun i => Z_of_mem (Finset.mem_univ i)Everybody is treated, recorded as a fact about every unit so that the
simp onlybelow can use it under the binder of the estimator’s sum.Z_of_memis the chapter-6 lemma;Finset.mem_univ iis the proof that unitibelongs to the full set.simp only [HT, pointMass_univ_propensity, hZ, one_mul, div_one, sub_self, zero_mul, zero_div, sub_zero]The estimator, evaluated.
HTunfolds it;pointMass_univ_propensityandhZ(the local fact that every indicator is1) put1everywhere; thenone_mulanddiv_oneclear the treated term down toP.y1 i, and the control term dies in three steps —sub_selfmakes both1 - Z z iand1 - π iinto0,zero_mulandzero_divmake the term0, andsub_zerodrops it. Thezero_divis the division convention doing its work in plain sight: it is what turns into rather than into an error.GoalhZ : ∀ (i : Fin n), Z univ i = 1 ⊢ 1 / ↑n * ∑ x, P.y1 x = mean P.y1rw [mean]The goal is
1 / ↑n * ∑ x, P.y1 x = mean P.y1. Unfoldingmeanon the right puts both sides in the same vocabulary,1 / ntimes a sum against a sum overn.ring(1/n) · s = s/nis an identity in a field, with the sum as an atom.rw [pointMass_univ_expect_HT, P.tau_eq_mean_sub_mean]The bias theorem. The expectation just computed is
, and is the identity from chapter 6, so the goal becomesmean P.y1 - (mean P.y1 - mean P.y0) = mean P.y0, and the closingringdoes the cancellation.
Note what the division convention did: it turned a statement that should be
undefined into a false-but-well-typed one. That is the right trade — every
theorem stays an unconditional equation — but it means a hypothesis such as
ExerciseAffine statistics
Warm-up. Expectation is affine:
theorem ex_expect_affine (D : Design n) (f : Assignment n → ℝ) (a b : ℝ) :
D.expect (fun z => a * f z + b) = a * D.expect f + b := by
sorryHint
Three rewrites: D.expect_add (fun z => a * f z) (fun _ => b), then
D.expect_const_mul f a on the first piece and D.expect_const b on the
second. The only difficulty is writing the two statistics as explicit lambdas so
that rw can match them.
Show solution
theorem ex_expect_affine (D : Design n) (f : Assignment n → ℝ) (a b : ℝ) :
D.expect (fun z => a * f z + b) = a * D.expect f + b := by
rw [D.expect_add (fun z => a * f z) (fun _ => b), D.expect_const_mul f a, D.expect_const b]How a probabilist would set this up
They would begin with a probability space PMF (Assignment n)
with values in ℝ≥0∞, its induced Measure, and ∫ z, f z ∂μ, with
Integrable and Measurable side conditions on every lemma.
For a finite population, all of that machinery is doing nothing:
- the σ-algebra is
, so every function is measurable; is finite, so every function is integrable;- the integral is the finite sum
∑ z, D.prob z * f z.
So the elementary development in this chapter is not a simplification that will
have to be redone later; it is the same objects with the bureaucracy removed.
Chapter 14 makes that precise — Design.toPMF, the theorem ∫ z, f z ∂μ = D.expect f, and HT_unbiased restated in MeasureTheory vocabulary — and
proves the Bernoulli design’s units are iIndepFun in Mathlib’s sense, which is
bernoulli_inclusion and nothing more. You need to be able to read that
statement; you do not need it to prove anything in between.
Reading autoformalized Lean
Two ways a “design” can be formalized into something that is not a design.
A design given by its propensities. Here is a plausible-looking structure:
structure Design' (n : ℕ) where
propensity : Fin n → ℝ
nonneg : ∀ i, 0 ≤ propensity i
le_one : ∀ i, propensity i ≤ 1
Every field is true of a real design. It even supports a definition of
Horvitz–Thompson, and it is what a paper’s notation looks like on the page. But
it is a strictly weaker object: it cannot express “exactly
An expectation with the wrong weights. This one type-checks, is provable, and is wrong:
noncomputable def expect' (D : Design n) (f : Assignment n → ℝ) : ℝ :=
(∑ z, f z) / (2 ^ n : ℝ)
D appears in the signature and is never used, so every theorem proved about
expect' is a theorem about the uniform design dressed up as a theorem about
an arbitrary one. It would even satisfy linearity, and expect_mono — all the lemmas of this chapter go through. The check is not to
read the theorems; it is to read the definition and ask whether each argument is
used. #print expect' shows you the body.
Next chapter: with a design in hand and its propensities computed, the payoff.
Horvitz–Thompson is unbiased for any design with
Footnotes
-
That statement is itself a theorem,
Design.ext, and it lives in the bridge chapter rather than here because nothing before it needs to compare two designs for equality. ↩