Causal inferenceChapter 14
Bridge to Mathlib's probability
Translating finite designs into Mathlib probability: pmfs, measures, integrals, and independence.
Contents
The preceding chapters define expectations as finite sums over assignments. This is sufficient for the finite-population results developed there. Mathlib’s general probability library instead uses measurable spaces, measures, and Bochner integrals. An unfamiliar statement may therefore express unbiasedness as
theorem tau_hat_unbiased : μ[fun ω => tauHat ω] = tau
where μ[·] denotes an integral. This chapter provides the translation between
the finite design and Mathlib’s measure-theoretic language. It constructs a pmf
and measure from a design, relates finite expectations to integrals, and
transfers the earlier unbiasedness and variance results across that bridge.
The σ-algebra you never think about
A measure space needs a σ-algebra, and Assignment n = Finset (Fin n) is a
finite type with
instance instMeasurableSpaceAssignment : MeasurableSpace (Assignment n) := ⊤
theorem measurableSet_assignment (s : Set (Assignment n)) : MeasurableSet s := trivial
instance instMeasurableSingletonClassAssignment :
MeasurableSingletonClass (Assignment n) :=
⟨fun z => measurableSet_assignment {z}⟩
theorem measurable_of_assignment (f : Assignment n → ℝ) : Measurable f :=
measurable_from_topMeasurableSpace α is a typeclass — a structure Lean carries around implicitly
and finds by instance search — so declaring instMeasurableSpaceAssignment once
means every later statement about assignments silently uses ⊤, the largest
σ-algebra. Three consequences, and they are the reason this chapter is short:
measurableSet_assignmentprovesMeasurableSet sfor anys, bytrivial: in the⊤σ-algebra the predicateMeasurableSetisTrue.measurable_of_assignmentprovesMeasurable ffor anyf, bymeasurable_from_top.instMeasurableSingletonClassAssignmentrecords the consequence Mathlib asks for by name: singletons are measurable. Its single field ismeasurableSet_assignment {z}, i.e. the first fact at a one-element set. Trivial as a proof, load-bearing as an instance —PMF.integral_eq_sumbelow will not fire without it.
So every measurability side condition in every Mathlib probability lemma
discharges automatically here. When you read a statement about a finite
randomization distribution and it is festooned with Measurable,
AEStronglyMeasurable and MeasurableSet hypotheses, none of them are doing
mathematical work — but you still have to check they are satisfiable, which
they are only if the σ-algebra is the one you think it is. More on that at the
end.
ℝ≥0∞, and why probabilities live in it
Mathlib measures take values in ℝ≥0∞ (ENNReal), the interval ℝ≥0∞ is not a
field: subtraction is truncated,
example (x : ℝ) (hx : 0 ≤ x) : (ENNReal.ofReal x).toReal = x :=
ENNReal.toReal_ofReal hx
example : ENNReal.ofReal (-2 : ℝ) = 0 := by simp
example : (⊤ : ℝ≥0∞).toReal = 0 := ENNReal.toReal_top
example : (1 : ℝ≥0∞) - 2 = 0 := tsub_eq_zero_of_le (by norm_num)
example (x y : ℝ) (hx : 0 ≤ x) (hy : 0 ≤ y) :
ENNReal.ofReal (x + y) = ENNReal.ofReal x + ENNReal.ofReal y :=
ENNReal.ofReal_add hx hyENNReal.toReal_ofReal hxRound-tripping a nonnegative real is the identity. The hypothesis
0 ≤ xis the whole content: without itofRealhas clamped and nothing comes back.by simpENNReal.ofReal (-2) = 0. The clamping, witnessed.simpknowsENNReal.ofReal_eq_zero, which saysofReal x = 0 ↔ x ≤ 0, and closes the numeric side condition itself.ENNReal.toReal_top(⊤ : ℝ≥0∞).toReal = 0. Not a rounding convention but a definition, and the trap of the chapter: a real-valued conclusion obtained throughtoRealis satisfied by infinity as readily as by zero.tsub_eq_zero_of_le (by norm_num)(1 : ℝ≥0∞) - 2 = 0. Subtraction inℝ≥0∞is truncated:tsub_eq_zero_of_lesaysa - b = 0whenevera ≤ b, andby norm_numsupplies1 ≤ 2. This is why every algebraic manipulation in this chapter happens after atoReal.ENNReal.ofReal_add hx hyAddition, on the other hand, does commute with
ofReal— provided both summands are nonnegative, which is whathxandhysay. This is the lemma the pmf construction below leans on.
The two conversions are ENNReal.ofReal : ℝ → ℝ≥0∞, which clamps negatives to
0, and ENNReal.toReal : ℝ≥0∞ → ℝ, which sends ∞ to 0. They are mutually
inverse exactly on toReal ∞ = 0 is a trap worth memorizing now, because
it means a real-valued conclusion can be silently true for the wrong reason.
Measure.real μ s, written μ.real s, is just (μ s).toReal. It is the thing a
statistician means by “the probability of
A design is a probability mass function
Mathlib’s PMF α is a function α → ℝ≥0∞ whose unconditional sum (tsum,
written ∑') is 1. Our Design n carries the same data over ℝ, with
nonnegativity as a separate field. The translation in one direction is
ENNReal.ofReal:
Proposition. Every design determines a probability mass function on the
assignments, whose mass at
The design’s masses are nonnegative reals, so they sit inside
noncomputable def Design.toPMF (D : Design n) : PMF (Assignment n) :=
⟨fun z => ENNReal.ofReal (D.prob z), by
have h : HasSum (fun z : Assignment n => ENNReal.ofReal (D.prob z))
(∑ z, ENNReal.ofReal (D.prob z)) := hasSum_fintype _
rwa [← ENNReal.ofReal_sum_of_nonneg fun z _ => D.nonneg z, D.sum_one,
ENNReal.ofReal_one] at h⟩The Lean makes the same three moves, in the same order.
fun z => ENNReal.ofReal (D.prob z)A
PMFis a subtype: a function intoℝ≥0∞together with a proof that it sums to one, and the definition supplies both in an anonymous constructor. This is the first component — the design’s masses pushed throughofReal— and it is where nonnegativity is silently spent, sinceofRealclamps rather than complaining. The second component is thebyblock, whose goal is theHasSumabove.GoalD : Design n ⊢ HasSum (fun z => ENNReal.ofReal (D.prob z)) 1have h : HasSum (fun z : Assignment n => ENNReal.ofReal (D.prob z)) (∑ z, ENNReal.ofReal (D.prob z)) := hasSum_fintype _hasSum_fintypesays that on aFintypethe unconditional sum converges to the ordinaryFinset.sum. It is stated as aHasSum, so thishaveproduces a hypothesis of exactly the goal’s shape with the wrong value; the next three rewrites turn that value into1.Goalh : HasSum (fun z => ENNReal.ofReal (D.prob z)) (∑ z, ENNReal.ofReal (D.prob z)) ⊢ HasSum (fun z => ENNReal.ofReal (D.prob z)) 1rw [← ENNReal.ofReal_sum_of_nonneg fun z _ => D.nonneg z] at hENNReal.ofReal_sum_of_nonnegstates thatofRealcommutes with a finite sum of nonnegative reals. Used right-to-left, insideh, it pulls theofRealoutside the sum, so what is being summed is now the design’s own real-valued masses.Goalh : HasSum (fun z => ENNReal.ofReal (D.prob z)) (ENNReal.ofReal (∑ i, D.prob i)) ⊢ HasSum (fun z => ENNReal.ofReal (D.prob z)) 1rw [D.sum_one] at hThe design’s axiom: those masses sum to
1.Goalh : HasSum (fun z => ENNReal.ofReal (D.prob z)) (ENNReal.ofReal 1) ⊢ HasSum (fun z => ENNReal.ofReal (D.prob z)) 1rwa [ENNReal.ofReal_one] at hENNReal.ofReal_oneisofReal 1 = 1. After ithis the goal, and theainrwaisassumption— rewrite the hypothesis until it matches, then use it. (The three rewrites are onerwain the source; they are split here.)
The reverse direction reads the probabilities off with toReal:
noncomputable def PMF.toDesign (p : PMF (Assignment n)) : Design n where
prob z := (p z).toReal
nonneg _ := ENNReal.toReal_nonneg
sum_one := by
have h : ∑ z, p z = (1 : ℝ≥0∞) := by rw [← p.tsum_coe, tsum_fintype]
rw [← ENNReal.toReal_sum fun z _ => p.apply_ne_top z, h, ENNReal.toReal_one]The proof is not the mirror image of the one above, and the asymmetry is worth
a minute. ofReal is total, so going into ℝ≥0∞ needs no side condition;
coming back out needs to know that nothing was infinite.
prob z := (p z).toRealThe first field.
nonnegis the next line and is free:ENNReal.toReal_nonnegholds for every element ofℝ≥0∞, including⊤, whosetoRealis0. What is left is thesum_onefield, now a statement about real numbers.Goalp : PMF (Assignment n) ⊢ ∑ z, (p z).toReal = 1have h : ∑ z, p z = (1 : ℝ≥0∞) := by rw [← p.tsum_coe, tsum_fintype]A detour through the pmf’s own axiom, which is stated with a
tsum.PMF.tsum_coeis∑' a, p a = 1; rewriting the goal’s1with it right-to-left leaves a finite sum against an unconditional one, andtsum_fintype— the same fact used in the other direction above — identifies them. Sohis the pmf’s axiom inFinsetform.Goalp : PMF (Assignment n) ⊢ ∑ z, p z = ∑' (a : Assignment n), p arw [← ENNReal.toReal_sum fun z _ => p.apply_ne_top z]The step with real content.
ENNReal.toReal_sumsaystoRealcommutes with a finite sum provided no summand is⊤, and used right-to-left it pulls thetoRealoutside. Its hypothesis is discharged byPMF.apply_ne_top: a pmf’s values are at most1, hence finite. Without that hypothesis the lemma is false — one infinite mass makes the left-hand side collapse to0, sincetoReal ∞ = 0, while the right-hand side still counts the finite masses.Goalh : ∑ z, p z = 1 ⊢ (∑ a, p a).toReal = 1rw [← ENNReal.toReal_sum fun z _ => p.apply_ne_top z, h, ENNReal.toReal_one]The remaining two rewrites of the same
rw.hreplaces the inner sum by1, andENNReal.toReal_oneevaluates(1 : ℝ≥0∞).toRealto1, closing the goal by reflexivity.
Proposition (the correspondence). The two translations are mutually inverse: a design read as a pmf and back is the same design, and a pmf read as a design and back is the same pmf. Designs and probability mass functions on a finite set are therefore the same objects in two notations.
Both statements are pointwise, because a design is determined by its masses and
a pmf by its values. In one direction the composite sends
Both round trips are one line each once you have extensionality for designs, and they are the next two exercises.
ExerciseWarm-up: the round trip on a single probability
Going into ℝ≥0∞ and back is the identity on a single probability:
(D.toPMF z).toReal = D.prob z.
@[simp] theorem Design.toPMF_apply_toReal (D : Design n) (z : Assignment n) :
(D.toPMF z).toReal = D.prob z := by
sorryHint
Design.toPMF_apply unfolds the pmf to ENNReal.ofReal (D.prob z), and
ENNReal.toReal_ofReal needs exactly the nonnegativity that a Design carries
in its nonneg field. One rw with two lemmas in it.
Show solution
@[simp] theorem Design.toPMF_apply_toReal (D : Design n) (z : Assignment n) :
(D.toPMF z).toReal = D.prob z := by
rw [Design.toPMF_apply, ENNReal.toReal_ofReal (D.nonneg z)]ExerciseCore: both round trips
Two proofs: Design → PMF → Design and PMF → Design → PMF are both the identity.
@[ext] theorem Design.ext {D D' : Design n} (h : ∀ z, D.prob z = D'.prob z) : D = D' := by
cases D; cases D'
congr 1
funext z
exact h z
@[simp] theorem Design.toPMF_toDesign (D : Design n) : PMF.toDesign D.toPMF = D := by
sorry
@[simp] theorem PMF.toDesign_toPMF (p : PMF (Assignment n)) : (PMF.toDesign p).toPMF = p := by
sorryHint
For Design.toPMF_toDesign, ext z reduces equality of designs to equality of
probabilities (that is what the @[ext] attribute on Design.ext buys), and
the previous exercise is then exactly the goal. For PMF.toDesign_toPMF,
PMF.ext plays the same role, and the round trip runs the other way:
ENNReal.ofReal_toReal needs p z ≠ ∞, which is PMF.apply_ne_top.
Show solution
@[ext] theorem Design.ext {D D' : Design n} (h : ∀ z, D.prob z = D'.prob z) : D = D' := by
cases D; cases D'
congr 1
funext z
exact h z
@[simp] theorem Design.toPMF_toDesign (D : Design n) : PMF.toDesign D.toPMF = D := by
ext z
exact D.toPMF_apply_toReal z
@[simp] theorem PMF.toDesign_toPMF (p : PMF (Assignment n)) : (PMF.toDesign p).toPMF = p := by
refine PMF.ext fun z => ?_
rw [Design.toPMF_apply, PMF.toDesign_prob, ENNReal.ofReal_toReal (p.apply_ne_top z)]From pmf to measure
PMF.toMeasure turns a pmf into a genuine Measure by summing the mass over a
set: p.toMeasure s = ∑' z, s.indicator p z, promoted from an outer measure.
With the ⊤ σ-algebra every set is measurable, so the outer measure and the
measure agree everywhere and no subtleties survive. I write D.toPMF.toMeasure throughout.
That IsProbabilityMeasure μ — the typeclass that most Mathlib probability lemmas
require — is available without being asked for.
ExerciseWarm-up: total mass one, as a real number
The total mass of 1, stated as a real number:
μ.real Set.univ = 1.
theorem measureReal_univ (D : Design n) :
(D.toPMF.toMeasure).real (Set.univ : Set (Assignment n)) = 1 := by
sorryHint
Design.toMeasure_univ is μ Set.univ = 1 in ℝ≥0∞. To get the real-valued
version, unfold μ.real with measureReal_def, rewrite with it, and push
toReal through with ENNReal.toReal_one. Three rewrites, in that order.
Show solution
theorem measureReal_univ (D : Design n) :
(D.toPMF.toMeasure).real (Set.univ : Set (Assignment n)) = 1 := by
rw [measureReal_def, D.toMeasure_univ, ENNReal.toReal_one]ExerciseWarm-up: the probability of a single assignment
The measure of a single assignment, as a real number, is exactly D.prob z.
theorem Design.measureReal_singleton (D : Design n) (z : Assignment n) :
(D.toPMF.toMeasure).real {z} = D.prob z := by
sorryHint
Same shape as the previous exercise — measureReal_def, then
D.toMeasure_singleton — but the value is D.prob z rather than 1, so the
last step is ENNReal.toReal_ofReal with D.nonneg z.
Show solution
theorem Design.measureReal_singleton (D : Design n) (z : Assignment n) :
(D.toPMF.toMeasure).real {z} = D.prob z := by
rw [measureReal_def, D.toMeasure_singleton, ENNReal.toReal_ofReal (D.nonneg z)]An event was a decidable predicate on assignments, and its probability was a
sum over a Finset.filter. In Mathlib an event is a set and its probability is
the measure of that set. Design.toMeasure_setOf is the bridge, and the
propensity score comes out as the special case you would guess:
theorem Design.toMeasure_setOf (D : Design n) (p : Assignment n → Prop) [DecidablePred p] :
D.toPMF.toMeasure {z | p z} = ENNReal.ofReal (D.probOf p) := by
have hset : ({z | p z} : Set (Assignment n)) = ↑((univ : Finset (Assignment n)).filter p) := by
ext z
simp only [Set.mem_ofPred_eq, Finset.coe_filter, Finset.mem_univ, true_and]
rw [hset, PMF.toMeasure_apply_finset, Design.probOf,
ENNReal.ofReal_sum_of_nonneg fun z _ => D.nonneg z]
exact Finset.sum_congr rfl fun z _ => D.toPMF_apply z
theorem Design.measureReal_setOf (D : Design n) (p : Assignment n → Prop) [DecidablePred p] :
(D.toPMF.toMeasure).real {z | p z} = D.probOf p := by
rw [measureReal_def, D.toMeasure_setOf, ENNReal.toReal_ofReal (D.probOf_nonneg p)]
theorem Design.measureReal_treated (D : Design n) (i : Fin n) :
(D.toPMF.toMeasure).real {z : Assignment n | i ∈ z} = D.propensity i := by
rw [D.measureReal_setOf, D.propensity_eq_probOf]Three theorems, and only the first does any work: it identifies a set defined by
a predicate with the coercion of the Finset that probOf sums over.
have hset : ({z | p z} : Set (Assignment n)) = ↑((univ : Finset (Assignment n)).filter p) := by ext z simp only [Set.mem_ofPred_eq, Finset.coe_filter, Finset.mem_univ, true_and]The set identity, proved by extensionality at an arbitrary
z. The twosimplemmas are the two coercions involved:Set.mem_ofPred_eqsays membership in{z | p z}isp z, andFinset.coe_filtersays the underlying set ofuniv.filter pis{z ∈ univ | p z};Finset.mem_univandtrue_anddrop the vacuous membership inuniv. Both sides are thenp z.Goalz : Assignment n ⊢ z ∈ {z | p z} ↔ z ∈ ↑(filter p univ)rw [hset]Rewriting with it turns the measure of a set into the measure of a
Finset, which is the form Mathlib has a formula for.Goalhset : {z | p z} = ↑(filter p univ) ⊢ D.toPMF.toMeasure ↑(filter p univ) = ENNReal.ofReal (D.probOf p)rw [PMF.toMeasure_apply_finset]And that formula: the measure a pmf assigns to a finite set is the sum of the pmf over it. The goal is now
∑ x with p x, D.toPMF x = ENNReal.ofReal (D.probOf p)— an identity between two finite sums, one inℝ≥0∞and one inℝ.rw [hset, PMF.toMeasure_apply_finset, Design.probOf, ENNReal.ofReal_sum_of_nonneg fun z _ => D.nonneg z]The remaining two rewrites of the same
rw.Design.probOfunfolds the right-hand side into its own filtered sum, andENNReal.ofReal_sum_of_nonneg— the lemma from the pmf construction, needing nonnegativity again — pushesofRealinside it. Both sides are now sums over the sameFinset.Goal⊢ ∑ x with p x, D.toPMF x = ∑ i with p i, ENNReal.ofReal (D.prob i)exact Finset.sum_congr rfl fun z _ => D.toPMF_apply zFinset.sum_congrcompares two sums termwise over the same index set, and at each term the equation isD.toPMF z = ENNReal.ofReal (D.prob z), which is the definition oftoPMF.rw [measureReal_def, D.toMeasure_setOf, ENNReal.toReal_ofReal (D.probOf_nonneg p)]measureReal_setOfis the real-valued version, and three rewrites long.measureReal_defunfoldsμ.real sto(μ s).toReal; the theorem just proved replacesμ sbyofReal (D.probOf p); andENNReal.toReal_ofRealcancels the round trip, its nonnegativity hypothesis supplied byD.probOf_nonneg.rw [D.measureReal_setOf, D.propensity_eq_probOf]And
measureReal_treatedis two rewrites: the event “unitiis treated” is a set of the form{z | p z}, so the previous theorem turns its measure into aprobOf, andpropensity_eq_probOfsays that particularprobOfis the propensity score. This is the line quoted below.
Read Design.measureReal_treated slowly, because it is the single most useful
line in this chapter for reading other people’s Lean:
The left-hand side is how an autoformalization will write “the probability that
unit μ.real, not μ, because the right-hand side is a
real number and the left-hand side would otherwise be an ℝ≥0∞.)
The second-order version is worth doing yourself, because joint propensities are where designs actually differ from one another.
ExerciseCore: the joint propensity is the measure of an intersection
The joint propensity i and
j are treated. The displayed lemma jointPropensity_eq_probOf does the
elementary work: Z z i * Z z j is the indicator of “both treated”, so
Design.expect_indicator converts its expectation into a probOf, and
by_cases hi : i ∈ z <;> by_cases hj : j ∈ z with simp [Z, hi, hj] settles
the four membership cases.
theorem jointPropensity_eq_probOf (D : Design n) (i j : Fin n) :
D.jointPropensity i j = D.probOf (fun z => i ∈ z ∧ j ∈ z) := by
rw [Design.jointPropensity, ← D.expect_indicator (fun z => i ∈ z ∧ j ∈ z)]
refine D.expect_congr fun z => ?_
by_cases hi : i ∈ z <;> by_cases hj : j ∈ z <;> simp [Z, hi, hj]
theorem measureReal_both_treated (D : Design n) (i j : Fin n) :
(D.toPMF.toMeasure).real {z : Assignment n | i ∈ z ∧ j ∈ z}
= D.jointPropensity i j := by
sorryHint
One rewrite with Design.measureReal_setOf (which turns μ.real {z | p z}
into D.probOf p) and one with jointPropensity_eq_probOf.
Show solution
theorem jointPropensity_eq_probOf (D : Design n) (i j : Fin n) :
D.jointPropensity i j = D.probOf (fun z => i ∈ z ∧ j ∈ z) := by
rw [Design.jointPropensity, ← D.expect_indicator (fun z => i ∈ z ∧ j ∈ z)]
refine D.expect_congr fun z => ?_
by_cases hi : i ∈ z <;> by_cases hj : j ∈ z <;> simp [Z, hi, hj]
theorem measureReal_both_treated (D : Design n) (i j : Fin n) :
(D.toPMF.toMeasure).real {z : Assignment n | i ∈ z ∧ j ∈ z}
= D.jointPropensity i j := by
rw [D.measureReal_setOf, jointPropensity_eq_probOf]Expectation is integration
Here is the lemma the rest of the chapter is a corollary of.
Proposition. For every statistic
The space is finite and every subset is measurable, so
@[simp] theorem Design.integral_toMeasure (D : Design n) (f : Assignment n → ℝ) :
∫ z, f z ∂(D.toPMF.toMeasure) = D.expect f := by
rw [PMF.integral_eq_sum]
refine Finset.sum_congr rfl fun z _ => ?_
rw [smul_eq_mul, D.toPMF_apply_toReal]rw [PMF.integral_eq_sum]The collapse, done once and for all by Mathlib: against a pmf’s measure on a
Fintypewhose singletons are measurable, the Bochner integral is the finite sum of(p z).toReal • f z. Both typeclass hypotheses were installed by the σ-algebra section — this is what that section was for.Goal⊢ ∑ a, (D.toPMF a).toReal • f a = D.expect frefine Finset.sum_congr rfl fun z _ => ?_Two sums over the same index set, so compare them termwise. What is left is one assignment’s worth of the identity.
Goalz : Assignment n x✝ : z ∈ univ ⊢ (D.toPMF z).toReal • f z = D.prob z * f zrw [smul_eq_mul, D.toPMF_apply_toReal]smul_eq_mulsays that scalar multiplication of reals by reals is multiplication, andDesign.toPMF_apply_toReal— the warm-up exercise above — says(D.toPMF z).toReal = D.prob z. The two sides are then identical.
∫ z, f z ∂μ is Mathlib’s Bochner integral: it is defined by approximation with
simple functions, it is 0 by convention when f is not integrable, and on a
finite space it collapses to a finite weighted sum. PMF.integral_eq_sum does
that collapse for us — it needs Fintype and MeasurableSingletonClass, both of
which the σ-algebra section supplied — and the remaining step is
(D.toPMF z).toReal = D.prob z, the warm-up exercise from earlier.
The theorem is tagged @[simp], so from here on simp turns any integral
against a design’s measure into an elementary expectation. Design.expect_eq_integral
is the same fact in the other direction and in the notation Mathlib prefers:
μ[f], which is defined as ∫ z, f z ∂μ. When you see μ[f] in an
autoformalized statement, read it as
ExerciseCore: E[Z_i] = π_i, in Mathlib's vocabulary
The propensity score is the integral of the treatment indicator —
theorem integral_Z (D : Design n) (i : Fin n) :
∫ z, Z z i ∂(D.toPMF.toMeasure) = D.propensity i := by
sorryHint
Design.integral_toMeasure turns the goal into a statement about
D.expect (fun z => Z z i), and Design.expect_Z says that is the propensity —
by rfl, since that is how propensity was defined. Two lines.
Show solution
theorem integral_Z (D : Design n) (i : Fin n) :
∫ z, Z z i ∂(D.toPMF.toMeasure) = D.propensity i := by
rw [D.integral_toMeasure]
exact D.expect_Z iVariance transfers just as cleanly, though the definition on the Mathlib side
takes a detour. ProbabilityTheory.variance f μ, written Var[f; μ], is
defined as (evariance f μ).toReal, where evariance is an ℝ≥0∞-valued
Lebesgue integral — so that it exists for any function whatsoever, integrable or
not. variance_eq_integral recovers the familiar f is a.e.-measurable, which here is free. Keep the detour in mind: it is
the subject of one of the pitfalls at the end of the chapter.
ExerciseStretch: Var[f; μ] is Design.var
Mathlib’s ProbabilityTheory.variance f μ is Design.var f.
theorem Design.variance_toMeasure (D : Design n) (f : Assignment n → ℝ) :
variance f (D.toPMF.toMeasure) = D.var f := by
sorryHint
Rewrite with variance_eq_integral, whose hypothesis is discharged by
(measurable_of_assignment f).aemeasurable. After that rewrite there are two
integrals against μ — the outer one and the mean μ[f] sitting inside it —
which is why the next step is simp only [Design.integral_toMeasure] rather
than a single rw, which would only fire on the first occurrence. What remains
is Design.var unfolded, so rfl closes it.
Show solution
theorem Design.variance_toMeasure (D : Design n) (f : Assignment n → ℝ) :
variance f (D.toPMF.toMeasure) = D.var f := by
rw [variance_eq_integral (measurable_of_assignment f).aemeasurable]
simp only [Design.integral_toMeasure]
rflThe headline theorems, restated
With Design.integral_toMeasure in hand, the payoff theorems of Part II restate
themselves. Horvitz–Thompson first:
theorem HT_unbiased_integral (D : Design n) (P : Population n)
(hlo : ∀ i, 0 < D.propensity i) (hhi : ∀ i, D.propensity i < 1) :
∫ z, HT D P z ∂(D.toPMF.toMeasure) = P.tau := by
rw [D.integral_toMeasure]
exact HT_unbiased D P hlo hhirw [D.integral_toMeasure]The theorem above, applied to the integrand
HT D P. Every trace of measure theory leaves the goal at this line.Goal⊢ D.expect (HT D P) = P.tauexact HT_unbiased D P hlo hhiAnd what remains is the theorem of chapter 8, with its two hypotheses — every propensity strictly between
0and1— passed through unchanged. The translation adds no hypotheses and removes none.
Two lines: rewrite the integral into an expectation, quote HT_unbiased. The
mathematical content was done in the unbiasedness chapter and none of it is
repeated here — which is exactly the claim this chapter is making. A
measure-theoretic statement about a finite design is a notational variant of a
finite sum identity, and if it were anything more, the proof could not be two
lines.
ExerciseCore: difference in means, with an integral
Difference in means is unbiased under complete randomization, stated with an
integral. Note the hypotheses are unchanged by the translation — the
measure-theoretic version needs DiM divides by
theorem DiM_unbiased_completeRandomization_integral {n₁ : ℕ} (hle : n₁ ≤ n) (P : Population n)
(hpos : 0 < n₁) (hlt : n₁ < n) :
∫ z, DiM P z ∂((completeRandomization n n₁ hle).toPMF.toMeasure) = P.tau := by
sorryHint
Identical in shape to HT_unbiased_integral: one
rw [Design.integral_toMeasure] and then exact the core theorem
DiM_unbiased_completeRandomization applied to its three hypotheses.
Show solution
theorem DiM_unbiased_completeRandomization_integral {n₁ : ℕ} (hle : n₁ ≤ n) (P : Population n)
(hpos : 0 < n₁) (hlt : n₁ < n) :
∫ z, DiM P z ∂((completeRandomization n n₁ hle).toPMF.toMeasure) = P.tau := by
rw [Design.integral_toMeasure]
exact DiM_unbiased_completeRandomization hle P hpos hltIndependence, at last
Design-based inference does not need independence anywhere — that is one of its selling points — but Mathlib’s probability library is largely about independence, and the Bernoulli design is the one place in this tutorial where it genuinely holds. It is worth seeing the vocabulary on an example you can already compute.
Theorem. Under Bernoulli assignment with common treatment probability
Mutual independence of a family of events means that for every finite set
Fix such an
Quantified over all finite
ProbabilityTheory.iIndepSet is that predicate, and
iIndepSet_iff_meas_biInter is the characterization the proof just used.
theorem bernoulli_iIndepSet_treated {p : ℝ} (hp0 : 0 ≤ p) (hp1 : p ≤ 1) :
iIndepSet (fun i : Fin n => {z : Assignment n | i ∈ z})
((bernoulliDesign n p hp0 hp1).toPMF.toMeasure) := by
classical
set D := bernoulliDesign n p hp0 hp1 with hD
rw [iIndepSet_iff_meas_biInter fun i => measurableSet_assignment _]
intro S
have hInter : (⋂ i ∈ S, {z : Assignment n | i ∈ z}) = {z : Assignment n | S ⊆ z} := by
ext z
simp [Finset.subset_iff]
have hleft : D.toPMF.toMeasure (⋂ i ∈ S, {z : Assignment n | i ∈ z})
= ENNReal.ofReal (p ^ #S) := by
rw [hInter, D.toMeasure_setOf, ← D.inclusion_eq_probOf, hD, bernoulli_inclusion]
have hright : ∀ i : Fin n,
D.toPMF.toMeasure {z : Assignment n | i ∈ z} = ENNReal.ofReal p := by
intro i
rw [D.toMeasure_setOf, ← D.propensity_eq_probOf, hD, bernoulli_propensity]
rw [hleft, Finset.prod_congr rfl fun i _ => hright i, Finset.prod_const,
ENNReal.ofReal_pow hp0]classicalDefensive bookkeeping.
Design.probOfsums over aFinset.filter, which needs its predicate to be decidable, andclassicalmakes every proposition decidable so that no instance search can fail. As it happens the predicates below —i ∈ zandS ⊆ z— are decidable anyway and the proof goes through without this line; it changes no mathematics either way, and recognizing that is part of reading someone else’s Lean.set D := bernoulliDesign n p hp0 hp1 with hDAn abbreviation, and
hDremembers what it abbreviates so that it can be unfolded again when a Bernoulli-specific lemma is needed. Without it every goal below spellsbernoulliDesign n p hp0 hp1out in full.GoalD : Design n := bernoulliDesign n p hp0 hp1 hD : D = bernoulliDesign n p hp0 hp1 ⊢ ∀ (s : Finset (Fin n)), D.toPMF.toMeasure (⋂ i ∈ s, {z | i ∈ z}) = ∏ i ∈ s, D.toPMF.toMeasure {z | i ∈ z}rw [iIndepSet_iff_meas_biInter fun i => measurableSet_assignment _]The characterization quoted above, and the definition the informal proof used. Its hypothesis is that each event is measurable, which on the
⊤σ-algebra ismeasurableSet_assignment, i.e.trivial. The goal becomes the displayed product formula, universally quantified over finite index sets.intro SFix such an
S. Everything below is about this one finite set of units.have hInter : (⋂ i ∈ S, {z : Assignment n | i ∈ z}) = {z : Assignment n | S ⊆ z} := by ext z simp [Finset.subset_iff]The step that makes the rest possible, and the one a reader is most likely to skip.
⋂ i ∈ S, {z | i ∈ z}is an intersection of events;{z | S ⊆ z}is a single event. They are equal, andFinset.subset_iffis why:S ⊆ zunfolds to∀ a ∈ S, a ∈ z, which is precisely the membership condition for the intersection. This is the sentence “the intersection is the event that every unit ofSis treated”, and it is what lets the inclusion probability be used at all.GoalS : Finset (Fin n) z : Assignment n ⊢ z ∈ ⋂ i ∈ S, {z | i ∈ z} ↔ z ∈ {z | S ⊆ z}rw [hInter, D.toMeasure_setOf, ← D.inclusion_eq_probOf, hD, bernoulli_inclusion]The left-hand side.
hInterreplaces the intersection by the single event;Design.toMeasure_setOfturns its measure intoofRealof aprobOf;Design.inclusion_eq_probOf, used right-to-left, recognizes thatprobOfas the inclusion probability ofS— the probability that every unit ofSis treated; andhDthen unfoldsDso thatbernoulli_inclusion— the counting lemma from the designs chapter — can evaluate it asp ^ #S.Goal⊢ ENNReal.ofReal (D.inclusion S) = ENNReal.ofReal (p ^ #S)rw [D.toMeasure_setOf, ← D.propensity_eq_probOf, hD, bernoulli_propensity]The right-hand factors, by the same route one unit at a time: the event that
iis treated has measureofReal (D.propensity i), andbernoulli_propensitysays that propensity isp.rw [hleft, Finset.prod_congr rfl fun i _ => hright i, Finset.prod_const, ENNReal.ofReal_pow hp0]Assembling.
hleftreplaces the left-hand side byofReal (p ^ #S);Finset.prod_congr rfl fun i _ => hright ireplaces every factor of the product byofReal p;Finset.prod_constturns that product of a constant intoofReal p ^ #S; andENNReal.ofReal_pow, whose hypothesis is0 ≤ p, moves the power inside the conversion, closing the goal. The intermediate goal after the first two rewrites isENNReal.ofReal (p ^ #S) = ∏ i ∈ S, ENNReal.ofReal p.Goal⊢ ENNReal.ofReal (p ^ #S) = ENNReal.ofReal p ^ #S
Independence of events upgrades to independence of random variables with one
lemma, because our Z is literally an indicator:
theorem bernoulli_iIndepFun_Z {p : ℝ} (hp0 : 0 ≤ p) (hp1 : p ≤ 1) :
iIndepFun (fun (i : Fin n) (z : Assignment n) => Z z i)
((bernoulliDesign n p hp0 hp1).toPMF.toMeasure) := by
have hZ : (fun (i : Fin n) (z : Assignment n) => Z z i)
= fun i : Fin n => ({z : Assignment n | i ∈ z}).indicator fun _ => (1 : ℝ) := by
funext i z
by_cases h : i ∈ z <;> simp [Z, h]
rw [hZ]
exact (bernoulli_iIndepSet_treated hp0 hp1).iIndepFun_indicatorhave hZ : (fun (i : Fin n) (z : Assignment n) => Z z i) = fun i : Fin n => ({z : Assignment n | i ∈ z}).indicator fun _ => (1 : ℝ) := by funext i z by_cases h : i ∈ z <;> simp [Z, h]The only content: our
Zand Mathlib’sSet.indicatorare the same function.funext i zreduces the equality of two families of functions to agreement at one unit and one assignment, andby_cases h : i ∈ z <;> simp [Z, h]computes both branches —1on the treated side,0on the other — for each of the two definitions.Goali : Fin n z : Assignment n ⊢ Z z i = {z | i ∈ z}.indicator (fun x => 1) zrw [hZ]Rewriting the goal with that identity restates it about indicators, which is the vocabulary the next lemma speaks.
GoalhZ : (fun i z => Z z i) = fun i => {z | i ∈ z}.indicator fun x => 1 ⊢ iIndepFun (fun i => {z | i ∈ z}.indicator fun x => 1) (bernoulliDesign n p hp0 hp1).toPMF.toMeasureexact (bernoulli_iIndepSet_treated hp0 hp1).iIndepFun_indicatoriIndepSet.iIndepFun_indicatorupgrades independence of a family of events to independence of their indicator random variables. Nothing probabilistic happens here; the theorem above did that work.
ExerciseCore: independence written out as a product of probabilities
iIndepSet is an abstract predicate; this is the same statement for two units
of a Bernoulli design, written the way you would check it by hand: the
probability that both are treated is the product of the two marginal
probabilities.
theorem bernoulli_measureReal_both {p : ℝ} (hp0 : 0 ≤ p) (hp1 : p ≤ 1) {i j : Fin n}
(hij : i ≠ j) :
((bernoulliDesign n p hp0 hp1).toPMF.toMeasure).real
{z : Assignment n | i ∈ z ∧ j ∈ z}
= ((bernoulliDesign n p hp0 hp1).toPMF.toMeasure).real {z : Assignment n | i ∈ z}
* ((bernoulliDesign n p hp0 hp1).toPMF.toMeasure).real
{z : Assignment n | j ∈ z} := by
sorryHint
Rewrite the three probabilities into propensities using
measureReal_both_treated from earlier plus Design.measureReal_treated
(twice), then quote bernoulli_jointPropensity (which needs bernoulli_propensity. Finish by turning sq; all
of it fits in one rw.
Show solution
theorem bernoulli_measureReal_both {p : ℝ} (hp0 : 0 ≤ p) (hp1 : p ≤ 1) {i j : Fin n}
(hij : i ≠ j) :
((bernoulliDesign n p hp0 hp1).toPMF.toMeasure).real
{z : Assignment n | i ∈ z ∧ j ∈ z}
= ((bernoulliDesign n p hp0 hp1).toPMF.toMeasure).real {z : Assignment n | i ∈ z}
* ((bernoulliDesign n p hp0 hp1).toPMF.toMeasure).real
{z : Assignment n | j ∈ z} := by
rw [measureReal_both_treated, Design.measureReal_treated, Design.measureReal_treated,
bernoulli_jointPropensity hij, bernoulli_propensity, bernoulli_propensity, sq]Side conditions, and what ∀ᵐ really says
Mathlib’s probability statements carry four decorations that a statistician does not write down. On a finite space three of them are free; knowing why they are free is what lets you check whether a statement you are handed is about the space you think it is.
example (D : Design n) (f : Assignment n → ℝ) :
AEStronglyMeasurable f (D.toPMF.toMeasure) :=
(measurable_of_assignment f).aestronglyMeasurable
example (D : Design n) (f : Assignment n → ℝ) : Integrable f (D.toPMF.toMeasure) :=
D.integrable f
example (D : Design n) (T : Assignment n → ℝ) :
IsProbabilityMeasure ((D.toPMF.toMeasure).map T) :=
inferInstance
theorem integral_map_eq (D : Design n) (T : Assignment n → ℝ) {g : ℝ → ℝ}
(hg : Measurable g) :
∫ x, g x ∂((D.toPMF.toMeasure).map T) = D.expect (fun z => g (T z)) := by
rw [integral_map (measurable_of_assignment T).aemeasurable hg.aestronglyMeasurable,
Design.integral_toMeasure]AEStronglyMeasurable f μis the regularity hypothesis on every Bochner integral:fagrees -almost everywhere with a measurable function with separable range. Free here, because every function out ofAssignment nis measurable.Integrable f μis . Free here, byIntegrable.of_finite. It is not free in general, and it matters: the Bochner integral of a non-integrable function is defined to be0, exactly the wayx / 0 = 0. A statement∫ … = 0can therefore be true because the integral does not exist.IsProbabilityMeasure μisμ Set.univ = 1. Free here, fromsum_one, and inherited by the pushforwardμ.map T— which is why the third line isinferInstanceand not a proof.Measure.map T μis the pushforward: the distribution of the statisticT, i.e. the randomization distribution. The change-of-variables lemmaintegral_mapis the one place a measurability hypothesis is genuinely needed —g : ℝ → ℝlives on the Borel σ-algebra, not on⊤, soMeasurable gis a real assumption. Notice the asymmetry: measurability is free going out of our space, never into an arbitrary one.
Only the fourth is a tactic proof rather than a one-term citation, and it is two rewrites.
rw [integral_map (measurable_of_assignment T).aemeasurable hg.aestronglyMeasurable, Design.integral_toMeasure]integral_mapis the change-of-variables formula: integratinggagainst the pushforward ofμalongTis integratingg ∘ Tagainstμ. It takes two side conditions —Ta.e.-measurable, which is free here, andga.e.-strongly-measurable, which is not free and comes from the explicit hypothesishg : Measurable g. That asymmetry is the bullet above, in Lean. The second rewrite,Design.integral_toMeasure, then turns the remaining integral overAssignment ninto an expectation, which closes the goal by reflexivity.Goal⊢ ∫ (x : Assignment n), g (T x) ∂D.toPMF.toMeasure = D.expect fun z => g (T z)
∀ᵐ z ∂μ, p z means ”∀ z, p z — but only nearly, and the gap
is where mistranslations hide.
ExerciseCore: an a.e. statement says nothing off the support
An ∀ᵐ hypothesis says nothing about assignments the design never produces;
at an assignment with positive probability it does say what you want. The easy
direction, ae_of_forall, is displayed with its proof; the exercise is the
converse.
theorem ae_of_forall (D : Design n) (p : Assignment n → Prop) (h : ∀ z, p z) :
∀ᵐ z ∂(D.toPMF.toMeasure), p z :=
Filter.Eventually.of_forall h
theorem of_ae_of_prob_pos (D : Design n) (p : Assignment n → Prop)
(h : ∀ᵐ z ∂(D.toPMF.toMeasure), p z) {z : Assignment n} (hz : 0 < D.prob z) : p z := by
sorryHint
Start with by_contra hp, then rw [ae_iff] at h turns the hypothesis into
μ {w | ¬ p w} = 0. The singleton {z} is a subset of that null set, so
measure_mono bounds μ {z} by 0; nonpos_iff_eq_zero turns the bound into
an equation, and Design.toMeasure_singleton plus ENNReal.ofReal_pos
contradict 0 < D.prob z.
Show solution
theorem ae_of_forall (D : Design n) (p : Assignment n → Prop) (h : ∀ z, p z) :
∀ᵐ z ∂(D.toPMF.toMeasure), p z :=
Filter.Eventually.of_forall h
theorem of_ae_of_prob_pos (D : Design n) (p : Assignment n → Prop)
(h : ∀ᵐ z ∂(D.toPMF.toMeasure), p z) {z : Assignment n} (hz : 0 < D.prob z) : p z := by
by_contra hp
rw [ae_iff] at h
have hsub : ({z} : Set (Assignment n)) ⊆ {w | ¬ p w} := by
intro w hw
rw [Set.mem_singleton_iff] at hw
exact hw ▸ hp
have hle : D.toPMF.toMeasure {z} ≤ 0 := h ▸ measure_mono hsub
rw [D.toMeasure_singleton, nonpos_iff_eq_zero] at hle
exact (ENNReal.ofReal_pos.mpr hz).ne' hleThe last piece of vocabulary is ProbabilityTheory.IdentDistrib f g μ ν:
“identically distributed”, which unfolds to three things — f is
a.e.-measurable, g is a.e.-measurable, and the pushforwards agree,
μ.map f = ν.map g. Note what it does not say: nothing about
ExerciseStretch: equal propensities means identically distributed indicators
Two units with the same propensity have identically distributed treatment
indicators. IdentDistrib f g μ ν unfolds to three things — f is
a.e.-measurable, g is a.e.-measurable, and μ.map f = ν.map g.
theorem identDistrib_Z_of_propensity_eq (D : Design n) {i j : Fin n}
(h : D.propensity i = D.propensity j) :
IdentDistrib (fun z : Assignment n => Z z i) (fun z : Assignment n => Z z j)
(D.toPMF.toMeasure) (D.toPMF.toMeasure) := by
sorryHint
The two measurability fields are (measurable_of_assignment _).aemeasurable.
For map_eq, Measure.ext reduces to μ (f ⁻¹' s) = μ (g ⁻¹' s) for every
measurable s ⊆ ℝ, and Measure.map_apply performs the reduction; an indicator
takes only two values, so by_cases h1 : (1 : ℝ) ∈ s <;> by_cases h0 : (0 : ℝ) ∈ s
gives four cases in which the preimage is Set.univ, {z | k ∈ z}, its
complement, or ∅. Each identification is ext z; by_cases hk : k ∈ z <;> simp [Z, hk, h1, h0]; the middle two cases rewrite the treated set’s measure
with D.toMeasure_setOf and D.propensity_eq_probOf and then use h, and the
complement case additionally needs measure_compl.
Show solution
theorem identDistrib_Z_of_propensity_eq (D : Design n) {i j : Fin n}
(h : D.propensity i = D.propensity j) :
IdentDistrib (fun z : Assignment n => Z z i) (fun z : Assignment n => Z z j)
(D.toPMF.toMeasure) (D.toPMF.toMeasure) := by
have hA : ∀ k : Fin n, D.toPMF.toMeasure {z : Assignment n | k ∈ z}
= ENNReal.ofReal (D.propensity k) := by
intro k
rw [D.toMeasure_setOf, D.propensity_eq_probOf]
refine ⟨(measurable_of_assignment _).aemeasurable,
(measurable_of_assignment _).aemeasurable, Measure.ext fun s hs => ?_⟩
rw [Measure.map_apply (measurable_of_assignment _) hs,
Measure.map_apply (measurable_of_assignment _) hs]
by_cases h1 : (1 : ℝ) ∈ s <;> by_cases h0 : (0 : ℝ) ∈ s
· have e : ∀ k : Fin n, ((fun z : Assignment n => Z z k) ⁻¹' s) = Set.univ := by
intro k; ext z; by_cases hk : k ∈ z <;> simp [Z, hk, h1, h0]
rw [e i, e j]
· have e : ∀ k : Fin n,
((fun z : Assignment n => Z z k) ⁻¹' s) = {z : Assignment n | k ∈ z} := by
intro k; ext z; by_cases hk : k ∈ z <;> simp [Z, hk, h1, h0]
rw [e i, e j, hA i, hA j, h]
· have e : ∀ k : Fin n,
((fun z : Assignment n => Z z k) ⁻¹' s) = {z : Assignment n | k ∈ z}ᶜ := by
intro k; ext z; by_cases hk : k ∈ z <;> simp [Z, hk, h1, h0]
rw [e i, e j,
measure_compl (measurableSet_assignment _) (measure_ne_top _ _),
measure_compl (measurableSet_assignment _) (measure_ne_top _ _), hA i, hA j, h]
· have e : ∀ k : Fin n, ((fun z : Assignment n => Z z k) ⁻¹' s) = (∅ : Set (Assignment n)) := by
intro k; ext z; by_cases hk : k ∈ z <;> simp [Z, hk, h1, h0]
rw [e i, e j]Reading autoformalized Lean
You now have the whole vocabulary. Here is how to use it on something you did not write.
Strip an autoformalized unbiasedness claim of its decoration and this is what is left:
DefinitionWhat unbiasedness looks like in the abstract
open MeasureTheory in
def Unbiased {Ω : Type*} [MeasurableSpace Ω] (μ : Measure Ω) (T : Ω → ℝ) (θ : ℝ) : Prop :=
∫ ω, T ω ∂μ = θAsk a model to formalize “the difference-in-means estimator is unbiased under complete randomization” and you get that proposition dressed in a measure, a typeclass and a hypothesis. A plausible output:
theorem dim_unbiased {n n₁ : ℕ} (P : Population n) (hpos : 0 < n₁) (hlt : n₁ < n)
(μ : Measure (Assignment n)) [IsProbabilityMeasure μ]
(hμ : ∀ z : Assignment n, μ.real {z} = if #z = n₁ then ((n.choose n₁ : ℝ))⁻¹ else 0) :
μ[fun z => DiM P z] = P.tau := by
sorry
It is not obviously wrong. Reading it is a matter of checking four things, in this order.
- Is the measure a probability measure?
[IsProbabilityMeasure μ]says so. Without it nothing rules outμ = 0, against which every integral is0and the conclusion reads . The typeclass is what makesμ[·]an expectation rather than an arbitrary linear functional. - Is it the right probability measure? This is the check people skip.
[IsProbabilityMeasure μ]alone would make the statement a claim about every distribution over assignments, which is plainly false — put all the mass on one assignment and the difference in means is whatever that assignment gives. Herehμpins down to complete randomization, so the statement is the one intended. Whenever the measure is universally quantified, look for the hypothesis that ties it to the design; if there isn’t one, the statement is wrong. - Is the integrand integrable? Here, yes, automatically. In general, a
missing
Integrablehypothesis can make an integral identity true by the convention . - Is the σ-algebra the intended one? The statement above uses whatever
MeasurableSpace (Assignment n)instance is in scope. Ours is⊤. If the file instead opens withvariable [MeasurableSpace (Assignment n)], the σ-algebra is an unconstrained parameter,{z}need not be measurable, andhμmay be unsatisfiable — in which case the theorem is vacuous.
And #print axioms dim_unbiased at the end, always, to catch the sorry.
The pattern behind all four: in measure-theoretic notation the object — which measure, on which σ-algebra, with which finiteness — is carried by typeclasses and bound variables rather than by the prose around the formula. The formula almost always looks right. Read the binders first, the conclusion second.
End of Part II
That is the design-based half of the tutorial finished. What you have is a library of some thirty theorems — potential outcomes, designs and propensities, unbiasedness of Horvitz–Thompson and of difference in means, Neyman’s variance and its conservative estimator, blocking and clustering, regression as algebra, exact randomization tests, and the partial-identification bounds of the previous chapter — every one of them a finite statement about a fixed population, and every one of them machine-checked. None of it needed measure theory, and this chapter is the evidence: the measure-theoretic restatements are two-line corollaries, so the measure theory was never carrying any of the weight.
What the measure-theoretic vocabulary is good for is reading. You will be
handed statements written in the Mathlib dialect — by a colleague, by a paper’s
artefact, by a model — and the dictionary above is what lets you check whether
such a statement says the thing you would have wanted to prove. Four questions,
in order: is the measure a probability measure, is it the measure of the
design, is the integrand integrable, and is the σ-algebra the intended one. Then
#print axioms.
What remains is reference: a tactic cheatsheet, a Lean-to-statistics glossary, and an appendix on verification.