Formal proof in Lean
A proof from this package is already close to what a proof assistant wants. It is not an argument in prose: it is the statement written as a non-negative combination of inequalities, with exact rational multipliers, verified in rational arithmetic before it is returned. Handing that to Lean is a transcription rather than a search.
lean does the transcription.
using Xitip
print(lean("H(X,Y,Z) <= H(X,Y) + H(Z)"; imports = false))/-
H(X,Y,Z) <= H(X,Y) + H(Z)
Generated by Xitip.jl. Every hypothesis is an elemental inequality;
the goal is the statement with everything moved to one side.
-/
theorem xitip_proof
(h_Y h_X_Y h_Y_Z h_X_Y_Z h_Z : ℝ)
-- I(X;Z|Y) >= 0
(e1 : 0 ≤ -h_Y + h_X_Y + h_Y_Z - h_X_Y_Z)
-- I(Y;Z) >= 0
(e2 : 0 ≤ h_Y + h_Z - h_Y_Z)
: 0 ≤ h_X_Y + h_Z - h_X_Y_Z := by
linarithlinarith closes the goal because the combination it would have to find is the one the certificate already contains.
What this says, and what it does not
Being precise here matters more than the feature does.
The theorem says: the statement follows from those particular inequalities by linear arithmetic, and Lean has checked that it does. If the export dropped a term, mangled a coefficient or quietly weakened the goal, Lean would reject it.
The theorem does not say the variables are entropies. h_X_Y is a plain real number, and each hypothesis is an assumption rather than a theorem. So what is machine-checked is the part this package is responsible for — the linear algebra over the elemental inequalities — and not the information theory underneath it, which is assumed.
That split is deliberate. The arithmetic core is where a bug in this package would show up, and it is the layer a machine can check without a multi-gigabyte dependency. Closing the remaining gap needs a bridge, below.
Checking it
lean/verify.jl generates a set of proofs and compiles them against any Lean project that has Mathlib:
$ git clone https://github.com/teorth/pfr.git
$ cd pfr && lake exe cache get && cd ..
$ julia --project=. lean/verify.jl pfr
sub compiles
markov compiles
han compiles
identity compiles
constant compiles
four compiles
rational compiles
eight compiles
control rejected as it should beThose cover constraints, rational coefficients, a constant term, an identity that needs no assumptions at all, and an eight-variable statement.
The last line is the one that makes the rest mean something. A theorem can compile for the wrong reason — if the goal were vacuous, or a hypothesis unnecessary, Lean would still accept it. So the harness takes a proof, drops one hypothesis, and checks that Lean then rejects it. Compiling is only evidence when not compiling was possible.
The bridge, which is not written
To say the variables are entropies, each h_α has to become the entropy of a tuple of random variables, and each hypothesis has to be discharged from a library lemma rather than assumed:
have e1 : 0 ≤ H[⟨X, Z⟩] + H[⟨Y, Z⟩] - H[Z] - H[⟨X, Y, Z⟩] :=
by have := condMutualInfo_nonneg X Y Z ; ...The lemmas exist. Core Mathlib carries only binary entropy (Mathlib.Analysis.SpecialFunctions.BinaryEntropy), but Terence Tao's PFR project has the multivariate API in the ProbabilityTheory namespace — entropy, condEntropy, mutualInfo, condMutualInfo, with mutualInfo_nonneg and condMutualInfo_nonneg, which are exactly the elemental inequalities. The newer LeanInfoTheory is another candidate.
The work is not the nonnegativity lemmas but the bookkeeping around them. This package indexes entropies by subset, so H(X,Y) and H(Y,X) are the same coordinate; in Lean H[⟨X, Y⟩] and H[⟨Y, X⟩] are different terms that happen to be equal, and an associativity choice has to be made for three or more. A faithful bridge needs a canonical tuple order and the rewriting to reach it, per arity, before linarith sees one consistent set of atoms.
Until that exists, the honest description of lean is the one above: it exports the arithmetic, and the arithmetic is checked.
Limits
Only a provable statement can be exported — there is nothing to transcribe otherwise, and lean raises rather than emitting something misleading:
try
lean("I(X;Y|Z) <= I(X;Y)")
catch e
println(e.msg)
endonly a provable statement can be exported; this one has a counterexampleA verdict that came from the simplex fallback carries no certificate, so it cannot be exported either. An equality exports as two theorems, one per direction.