19.4. The method ladder: GUM ⊂ Willink ⊂ SSPRC
For a model Y = f(X_1,\dots,X_k) with mutually independent, uncertain inputs, three methods
estimate the uncertainty of Y, in increasing fidelity and cost. They are one pipeline —
extract each input's contribution, combine the independent contributions, summarize — differing
only in what a contribution is:
-
GUM / law of propagation. Linearize and combine variances only:
u_c(Y) = \sqrt{\textstyle\sum_i c_i^2\, u^2(X_i)}, \qquad c_i = \partial f/\partial X_i.A symmetric, Gaussian answer. -
Willink's cumulants method (Willink, 2005)Robin Willink (2005). “A procedure for the evaluation of measurement uncertainty based on moments”. Metrologia. 42(5).. The same linearization, but combine the second and fourth cumulants,
u_Y^2 = \sum_i c_i^2 u_i^2andw_Y = \sum_i c_i^4 w_i, form the excess\gamma_Y = w_Y/u_Y^4, and read an expanded-uncertainty half-width off the Pearson family via the coverage-factork_p(\gamma_Y). This recovers the tail shape (kurtosis) GUM discards. -
The SSPRC method (Degenhardt et al., 2025)Johannes Degenhardt, Rainer Tutsch, Xiukun Hu, and Gaoliang Dai (2025). “A practically oriented, efficient alternative to the Monte Carlo method for measurement uncertainty estimation”. Metrologia. 62(2).. No linearization: systematically sample each input, propagate each separately through the model, reconstruct each input's deviation distribution, and combine them by convolution. It captures full non-linearity and each input's individual uncertainty contribution, at a fraction of the model evaluations a random Monte Carlo needs.
The organizing fact is that these are provably nested. All three rest on the additivity of
cumulants under independent summation — Willink's
\kappa_r\!\bigl(\sum_j a_j Z_j\bigr) = \sum_j a_j^{\,r}\,\kappa_r(Z_j). GUM keeps only
\kappa_2; Willink keeps \kappa_2 and \kappa_4; SSPRC's convolution combines all
cumulants numerically (Willink himself calls his method "convolution of the components in the
linearized formulation"). So the ladder is a chain of homomorphisms between the methods'
contribution types, and each coarser rung is a projection of the finer one — the claim the
ladder theorems below discharge, all five now proved over ℝ: T1 and T2 relate GUM and Willink;
T5 shows convolution adds cumulants; T3 uses it to identify Willink with the (\kappa_2,\kappa_4)
truncation of the linearized SSPRC; and T4 pins the SSPRC reference to the mean for an affine model.
The sensitivity coefficients c_i the two linearized rungs need come from the autograd bridge
below, not by hand.
The GUM combined standard uncertainty \sqrt{\sum_i c_i^2 u_i^2}, over a list of sensitivity /
moment-data pairs. The coarsest rung.
Lean code for Definition19.4.1●1 definition
Associated Lean declarations
-
defdefined in PropertyKindCalculus/Uncertainty/Combine.leancomplete
def PropertyKindCalculus.Uncertainty.gumStdUnc (terms : List (Float × PropertyKindCalculus.Uncertainty.MomentData Float)) : Float
def PropertyKindCalculus.Uncertainty.gumStdUnc (terms : List (Float × PropertyKindCalculus.Uncertainty.MomentData Float)) : Float
**GUM / law-of-propagation combined standard uncertainty:** `√(Σ cᵢ² uᵢ²)`.
Realized. gumStdUnc folds the \kappa_2 contributions. Stage 0 supplied the c_i explicitly;
Stage 1 now sources them from the autograd carrier (one reverse pass over the write-once model at
the input means), leaving this fold unchanged — the Degenhardt example checks the two agree, both
giving u_c = 1.662.
Willink's cumulant combine → (u_Y, \gamma_Y), with the Pearson coverage-factor fits
k_{0.95}, k_{0.99} giving the expanded-uncertainty half-width. The middle rung.
Lean code for Definition19.4.2●1 definition
Associated Lean declarations
-
defdefined in PropertyKindCalculus/Uncertainty/Combine.leancomplete
def PropertyKindCalculus.Uncertainty.willinkCombine (terms : List (Float × PropertyKindCalculus.Uncertainty.MomentData Float)) : Float × Float
def PropertyKindCalculus.Uncertainty.willinkCombine (terms : List (Float × PropertyKindCalculus.Uncertainty.MomentData Float)) : Float × Float
**Willink cumulant combine** → `(u_Y, γ_Y)`: the combined standard uncertainty and the coefficient of excess (kurtosis) `γ_Y = w_Y / u_Y⁴`, with `u_Y² = Σ cᵢ² uᵢ²` and `w_Y = Σ cᵢ⁴ wᵢ`.
Realized. willinkCombine combines \kappa_2 and \kappa_4; willinkK95/willinkK99 are the
rational Pearson-percentile fits (Willink eqs. 6/7); willinkHalfWidth99 composes them.
-
PropertyKindCalculus.Uncertainty.Mcm.run[complete]
The Monte Carlo reference propagator: sample the joint input distribution, push each draw through the model, report the sample mean and standard uncertainty of the output. Makes no linearization and no combination assumption, so it is the yardstick the cheaper rungs are validated against.
Lean code for Definition19.4.3●1 definition
Associated Lean declarations
-
PropertyKindCalculus.Uncertainty.Mcm.run[complete]
-
PropertyKindCalculus.Uncertainty.Mcm.run[complete]
-
defdefined in PropertyKindCalculus/Uncertainty/Mcm.leancomplete
def PropertyKindCalculus.Uncertainty.Mcm.run (model : List Float → Float) (inputs : List (PropertyKindCalculus.Uncertainty.InputDist Float)) (n : ℕ) (seed : UInt64) : Float × Float
def PropertyKindCalculus.Uncertainty.Mcm.run (model : List Float → Float) (inputs : List (PropertyKindCalculus.Uncertainty.InputDist Float)) (n : ℕ) (seed : UInt64) : Float × Float
**Monte Carlo propagation.** Returns `(E(Y), u(Y))` — the sample mean and (population) standard deviation of `model` over `n` joint draws of `inputs`, seeded by `seed`.
Realized. Mcm.run over the Float carrier, drawing each input through its invCDF. Efficiency
is not its job — fidelity is.
The sensitivity bridge: the coefficients c_i = \partial f/\partial X_i that GUM and Willink
need are obtained by instantiating the write-once model at an autograd carrier and running one
reverse pass at the input means — not by hand. This is the extract step of the two linearized
rungs, and the narrow, precise role of automatic differentiation here, since the SSPRC method is
derivative-free.
Lean code for Definition19.4.4●1 definition
Associated Lean declarations
-
defdefined in PropertyKindCalculus/Uncertainty/Sensitivity.leancomplete
def PropertyKindCalculus.Uncertainty.Sensitivity.gradient (model : PropertyKindCalculus.Uncertainty.Sensitivity.ScalarModel) (point : List Float) : Except String (List Float)
def PropertyKindCalculus.Uncertainty.Sensitivity.gradient (model : PropertyKindCalculus.Uncertainty.Sensitivity.ScalarModel) (point : List Float) : Except String (List Float)
**Reverse-mode gradient of a scalar model at a point.** Instantiates the write-once kernel at the tape carrier `TapeBuilder .scalar`, creating one differentiable input leaf per coordinate of `point`, records the forward tape, runs a single reverse pass from the scalar output, and reads back `∂f/∂xᵢ` at `point` in input order. These are the sensitivity coefficients `cᵢ` the linearized GUM/Willink methods consume (UNCERTAINTY.md §3.2 item 1). **Stage 3.5 retarget.** The reverse pass is the *total dense* engine entry `Tape.backwardDenseFrom` — the very function TorchLean's soundness theorem `backwardDenseFrom_lowerGraphToTape_adjoint_fderiv` (`NN/Proofs/Autograd/Runtime/Link/FDeriv.lean`) characterizes: on a compiled graph at the `ℝ` carrier, its input-prefix output is the adjoint of the Fréchet derivative of the forward evaluation. The seed is `1` at the scalar output and `0` everywhere else, which on a topologically-ordered tape computes the same accumulation as the previous reachability-pruned `backwardScalar` path (a dead subexpression now contributes an exact `0` instead of being skipped). The two honest gaps that remain are recorded in UNCERTAINTY.md §3.2/§6: this tape is built eagerly rather than by `lowerGraphToTape` (provenance), and it runs at `Float` while the theorem speaks at `ℝ` (the deviation is this workstream's own Adequacy claim).
Realized (Stage 1, Sensitivity.gradient; retargeted in Stage 3.5). The model kernel — the same
source run at Float for Monte Carlo — is interpreted at TorchLean's reverse-mode TapeBuilder
carrier; each input enters as a differentiable leaf, and one total dense reverse pass
(Tape.backwardDenseFrom, seeded 1 at the scalar output) reads every c_i, with no model
rewrite. Since Stage 3.5 this is the very entry point TorchLean's soundness theorem
backwardDenseFrom_lowerGraphToTape_adjoint_fderiv characterizes: on a compiled graph at the ℝ
carrier its input-prefix output is the adjoint of the Fréchet derivative of the forward
evaluation (the eager-tape provenance — narrowed at Stage 3.6 to a closure-provenance question,
see the direct-simulation theorem below — and the Float-vs-ℝ deviation remain the two
documented gaps). On the Degenhardt fictive model the bridge returns (c_1, c_2, c_3) = (5, 5, 2.25)
and reproduces the GUM u_c = 1.662 exactly (the DegenhardtSensitivity example). The op class is
exp/log/sqrt with arithmetic; trigonometric models await tape VJP nodes.
-
PRSim.direct_PR_soundness_compiled[complete]
Direct runtime simulation, closed for compiled tapes (Stage 3.6). The executable dense reverse
pass on a compiled tape succeeds, and the input-prefix of its gradient array — the runtime's own
shape-erased representation, block by block (ArrCorr) — realises exactly the adjoint of the
Fréchet derivative of the forward evaluation applied to the seed. Separately, and for arbitrary
(not just compiled) value-correct tapes: the reverse pass returns .ok provided every backward
closure is shape-total — a hypothesis the eager leaf/add/mul tape constructors are proved
to provide, constructor by constructor.
Lean code for Theorem19.4.5●1 theorem
Associated Lean declarations
-
PRSim.direct_PR_soundness_compiled[complete]
-
PRSim.direct_PR_soundness_compiled[complete]
-
theoremdefined in PropertyKindCalculus/Uncertainty/Experiments/PRSimulation.leancomplete
theorem PRSim.direct_PR_soundness_compiled {Γ ss : List Spec.Shape} (g : Proofs.Autograd.Graph Γ ss) (hg : Proofs.Autograd.GraphFDerivCorrect g) (x : TorchLean.TensorPack ℝ Γ) (seed : TorchLean.TensorPack ℝ (Γ ++ ss)) : ∃ out, (g.toAlgebra.lowerGraphToTape x ()).1.backwardDenseFrom seed.toShapeErasedArray = Except.ok out ∧ PRSim.ArrCorr ((ContinuousLinearMap.adjoint (fderiv ℝ g.evalVec (Proofs.Autograd.flattenCtx x))) (Proofs.Autograd.flattenCtx seed)) (out.extract 0 Γ.length)
theorem PRSim.direct_PR_soundness_compiled {Γ ss : List Spec.Shape} (g : Proofs.Autograd.Graph Γ ss) (hg : Proofs.Autograd.GraphFDerivCorrect g) (x : TorchLean.TensorPack ℝ Γ) (seed : TorchLean.TensorPack ℝ (Γ ++ ss)) : ∃ out, (g.toAlgebra.lowerGraphToTape x ()).1.backwardDenseFrom seed.toShapeErasedArray = Except.ok out ∧ PRSim.ArrCorr ((ContinuousLinearMap.adjoint (fderiv ℝ g.evalVec (Proofs.Autograd.flattenCtx x))) (Proofs.Autograd.flattenCtx seed)) (out.extract 0 Γ.length)
**Runtime reverse pass on a compiled tape = `(fderiv ℝ evalVec x)†`, in `ArrCorr` form.** (The closure of the original `direct_PR_soundness`, for compiled tapes: a corollary of the Stage-3.5 endpoint; the `Γ`-prefix of the output array realises exactly the adjoint of the Fréchet derivative of the P graph's forward evaluation.)
Realized (PRSim.direct_PR_soundness_compiled, with backwardDenseFrom_ok for the totality half
and forwardSim_lowerGraphToTape inhabiting the simulation relation; Experiments/PRSimulation.lean,
sorry-free). The endpoint is the input-prefix projection of the Stage-3.5 theorem transported to
the runtime array by two erasure lemmas (block-reads invert context flattening; Array.extract is
the erased typed prefix) — no reverse-pass fold argument is re-derived, so the adjoint mathematics
enters exactly once, through the Stage-3.5 bridge. The totality half threads the "every slot
carries its node's shape" invariant through the accumulation fold, value-free; the shape-totality
hypothesis is honest and necessary, since a value-correct tape with an erroring closure refutes
the unconditioned claim — the closure-provenance identification for the eager path is the next
theorem. The AutogradDirectSim example instantiates everything concretely and pins the axiom
profiles to the classical trio.
-
PRSim.direct_PR_soundness_eager[complete]
Eager provenance (Stage 3.6 addendum). The tape the eager runtime constructors actually build —
input leaves, then one add/mul call per graph node, which is the construction pattern the
sensitivity bridge uses — runs the same total dense reverse pass as the compiled tape of the
corresponding graph: same result, same gradient array, from any erased-context seed. Therefore
the Fréchet-adjoint endpoint above holds on the eagerly built tape itself, with no compilation
anywhere in the trusted path.
Lean code for Theorem19.4.6●1 theorem
Associated Lean declarations
-
PRSim.direct_PR_soundness_eager[complete]
-
PRSim.direct_PR_soundness_eager[complete]
-
theoremdefined in PropertyKindCalculus/Uncertainty/Experiments/EagerProvenance.leancomplete
theorem PRSim.direct_PR_soundness_eager {Γ ss : List Spec.Shape} (g : Proofs.Autograd.Graph Γ ss) (hg : Proofs.Autograd.GraphFDerivCorrect g) (x : TorchLean.TensorPack ℝ Γ) {t : PRSim.RTape} (ht : PRSim.EagerBuilds g x t) (seed : TorchLean.TensorPack ℝ (Γ ++ ss)) : ∃ out, Runtime.Autograd.Tape.backwardDenseFrom t seed.toShapeErasedArray = Except.ok out ∧ PRSim.ArrCorr ((ContinuousLinearMap.adjoint (fderiv ℝ g.evalVec (Proofs.Autograd.flattenCtx x))) (Proofs.Autograd.flattenCtx seed)) (out.extract 0 Γ.length)
theorem PRSim.direct_PR_soundness_eager {Γ ss : List Spec.Shape} (g : Proofs.Autograd.Graph Γ ss) (hg : Proofs.Autograd.GraphFDerivCorrect g) (x : TorchLean.TensorPack ℝ Γ) {t : PRSim.RTape} (ht : PRSim.EagerBuilds g x t) (seed : TorchLean.TensorPack ℝ (Γ ++ ss)) : ∃ out, Runtime.Autograd.Tape.backwardDenseFrom t seed.toShapeErasedArray = Except.ok out ∧ PRSim.ArrCorr ((ContinuousLinearMap.adjoint (fderiv ℝ g.evalVec (Proofs.Autograd.flattenCtx x))) (Proofs.Autograd.flattenCtx seed)) (out.extract 0 Γ.length)
**The Stage-3.5/3.6 endpoint, on the eagerly built tape**: the dense reverse pass on the runtime-constructed `leaf`/`add`/`mul` tape succeeds, and the input-prefix of its output realises the adjoint of the Fréchet derivative of the graph's forward evaluation.
Realized (PRSim.direct_PR_soundness_eager, via backwardDenseFrom_eager_eq_compiled;
Experiments/EagerProvenance.lean, sorry-free). EagerBuilds is the provenance relation — its
base tape is the compiled tape of the empty graph, definitionally — and the identification is
pure accounting, never a re-derived fold: the compiled node's dense contribution list is folded by
the upstream accumulation bridge, the eager node's sparse two-element list by a new single-slot
lemma (one accumulation call is a one-hot context addition), and the two context updates coincide
by flatten-injectivity through the vectorization homomorphisms — the eager closure's stored
product-rule payloads are exactly the ofVec-authored node's Hadamard vjp blocks. Push-invariance
lemmas restrict the extended tape's loop to its prefix, so the structural induction consumes its
hypothesis directly. Honest residuals: ops beyond add/mul (one more crank of the same machine
per op), the TapeM state-monad sugar (a one-line wrapper per op), and the Float-vs-ℝ
deviation — the workstream's own Adequacy claim. The AutogradDirectSim example witnesses
EagerBuilds for the concrete product tape and pins the classical-trio axiom profile.
Cumulant additivity (T1). Combining independent contributions adds their cumulants: the
measurand's combined (\kappa_2, \kappa_4) for an input list is the head input's contribution
plus the combined cumulants of the tail. Equivalently the contribution type is a commutative monoid
under combine — and that it is a monoid is the independence assumption made explicit. This is
Willink's \kappa_r(\sum_j a_j Z_j) = \sum_j a_j^{\,r}\,\kappa_r(Z_j) for r \in \{2, 4\}, the
fact the whole ladder rests on.
Lean code for Theorem19.4.7●1 theorem
Associated Lean declarations
-
theoremdefined in PropertyKindCalculus/Uncertainty/Ladder.leancomplete
theorem PropertyKindCalculus.Uncertainty.willinkCumulants_cons (t : ℝ × PropertyKindCalculus.Uncertainty.MomentData ℝ) (ts : List (ℝ × PropertyKindCalculus.Uncertainty.MomentData ℝ)) : PropertyKindCalculus.Uncertainty.willinkCumulants (t :: ts) = PropertyKindCalculus.Uncertainty.termCumulants t + PropertyKindCalculus.Uncertainty.willinkCumulants ts
theorem PropertyKindCalculus.Uncertainty.willinkCumulants_cons (t : ℝ × PropertyKindCalculus.Uncertainty.MomentData ℝ) (ts : List (ℝ × PropertyKindCalculus.Uncertainty.MomentData ℝ)) : PropertyKindCalculus.Uncertainty.willinkCumulants (t :: ts) = PropertyKindCalculus.Uncertainty.termCumulants t + PropertyKindCalculus.Uncertainty.willinkCumulants ts
**T1 (fold form).** Prepending an input adds its contribution: the combined cumulants of `t :: ts` are `termCumulants t + willinkCumulants ts`. Independent contributions add — the cumulant-additivity theorem, realized through the `Cumulants` monoid.
Realized over ℝ: willinkCumulants_cons, with the Cumulants AddCommMonoid instance (the
combine/empty monoid laws) providing the associativity/commutativity/unit that make the
independent fold well-defined.
GUM is the \kappa_2-projection of Willink (T2). The second cumulant of the Willink combine is
exactly the GUM combined variance, so \mathrm{gum} is the cumulant-forgetting coarsening of
\mathrm{willink}. And when every input's fourth cumulant vanishes (w_i = 0, as for normal
inputs) the excess \gamma_Y is zero, the Pearson coverage factor collapses to the standard-normal
percentile (k_{0.95}(0) = 1.96), and the Willink expanded-uncertainty half-width equals GUM's
Gaussian half-width 1.96\,u_c. Uses the GUM combine and
Willink's combine, resting on cumulant additivity.
Lean code for Theorem19.4.8●1 theorem
Associated Lean declarations
-
theoremdefined in PropertyKindCalculus/Uncertainty/Ladder.leancomplete
theorem PropertyKindCalculus.Uncertainty.gum_eq_willink_of_normal (terms : List (ℝ × PropertyKindCalculus.Uncertainty.MomentData ℝ)) (h : ∀ t ∈ terms, t.2.fourthCumulant = 0) : PropertyKindCalculus.Uncertainty.willinkHalf95 terms = PropertyKindCalculus.Uncertainty.gumHalf95 terms
theorem PropertyKindCalculus.Uncertainty.gum_eq_willink_of_normal (terms : List (ℝ × PropertyKindCalculus.Uncertainty.MomentData ℝ)) (h : ∀ t ∈ terms, t.2.fourthCumulant = 0) : PropertyKindCalculus.Uncertainty.willinkHalf95 terms = PropertyKindCalculus.Uncertainty.gumHalf95 terms
**T2 — GUM = Willink at κ₄ = 0.** For all-Gaussian inputs the Willink cumulants method's 95% expanded-uncertainty half-width equals the GUM Gaussian half-width. The finer method's extra structure (the fourth cumulant, the Pearson closure) contributes nothing precisely when the coarser method's Gaussian assumption already holds — so `gum` is the faithful `κ₄ = 0` restriction of `willink`, not a different answer.
Realized over ℝ (theorem T2 of UNCERTAINTY.md, gum_eq_willink_of_normal; #print axioms
shows it sorry-free — no sorryAx). Two parts: willinkCumulants_kappa2 — the variance fold is
literally the GUM one — and the shape collapse w_i = 0 \Rightarrow \gamma_Y = 0 \Rightarrow k_p
is the standard-normal percentile, so the half-widths coincide. Establishes the top edge of the
ladder; the lower edge — that Willink is the fourth-cumulant truncation of the linearized SSPRC,
because convolution adds cumulants — is theorem T3 below.