Files
zeekayandHanzo Dev 638fba4c90 Publish under the org, not a person
Bylines, paper authorship, correspondence addresses and the founder blurb under
each post named an individual. They name the organisation that published the
work instead: the brand on posts and pages, the research byline on papers, and
research@ in place of a personal address. Both are what these repositories
already used elsewhere — nothing new was coined.

Co-authors are untouched. Where a paper named someone else alongside, they stay
named; only the one byline changed. Bibliography entries, running heads,
PDF metadata and affiliations move with it, so a citation still resolves to
whoever the cited paper names.

Sample and fixture data uses the placeholder name these tests already use
elsewhere, so no assertion changed meaning.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-01 08:58:14 -07:00

303 lines
12 KiB
Lean4
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/-
Authors: Antje Worring, Zoo Labs Foundation Research
Energy-Based Model Agent Proofs
Formal verification of energy-based models for agent decision-making.
EBMs define a scalar energy function over (state, action) pairs;
low-energy configurations correspond to preferred behaviors.
Maps to:
- zoo/papers/hllm-training-free-grpo.tex: energy landscape for TF-GRPO
- zoo/papers/gym-training-platform.tex: Gym environment scoring
- zoo/papers/poai-consensus.tex: compute attestation quality scoring
Key results:
- Energy function is bounded below (no infinite negative energy)
- Partition function is finite (Gibbs distribution well-defined)
- Gradient descent on energy decreases it monotonically
- MCMC sampling reaches all energy modes given sufficient steps
- Contrastive divergence approximation error bounded by k
Author: Zoo Labs Foundation Research
-/
import Mathlib.Data.Nat.Defs
import Mathlib.Data.List.Basic
import Mathlib.Data.Finset.Basic
import Mathlib.Tactic
namespace Agent.EBM
/-! ## Core Structures -/
/-- An energy function maps configurations to energy values.
Lower energy = more preferred. We use Nat (non-negative) to ensure
boundedness below, with 0 representing the ground state. -/
structure EnergyFn where
/-- The energy at a given configuration index -/
energy : Nat → Nat
/-- Number of configurations in the state space -/
numConfigs : Nat
/-- Energy floor: minimum possible energy -/
floor : Nat
/-- All energies are at least the floor -/
hFloor : ∀ i, i < numConfigs → energy i ≥ floor
/-- A configuration in the energy landscape. -/
structure Config where
/-- Configuration index in the state space -/
index : Nat
/-- Energy at this configuration -/
energy : Nat
deriving DecidableEq, Repr
/-- The Gibbs distribution's partition function: Z = sum of exp(-E(x)/T).
We approximate with integer arithmetic: Z = sum(maxE - E(x) + 1)
so that lower-energy configs get higher weight. -/
def partitionFn (ef : EnergyFn) (maxE : Nat) : Nat :=
List.range ef.numConfigs |>.foldl (fun acc i =>
acc + (maxE - min (ef.energy i) maxE + 1)) 0
/-- A gradient descent step: move to a neighbor with lower energy. -/
def gradientStep (ef : EnergyFn) (current : Nat) (neighbor : Nat) : Nat :=
if ef.energy neighbor ≤ ef.energy current then neighbor
else current
/-- An energy mode: a local minimum where no neighbor has lower energy. -/
def isMode (ef : EnergyFn) (idx : Nat) (neighbors : List Nat) : Prop :=
∀ n ∈ neighbors, ef.energy idx ≤ ef.energy n
/-- MCMC state: current position and number of steps taken. -/
structure MCMCState where
position : Nat
steps : Nat
visited : Finset Nat
deriving DecidableEq
/-- One MCMC step: accept or reject a proposed move based on energy. -/
def mcmcStep (_ef : EnergyFn) (state : MCMCState) (proposal : Nat)
(accepted : Bool) : MCMCState :=
if accepted then
{ position := proposal
steps := state.steps + 1
visited := state.visited ∪ {proposal} }
else
{ state with steps := state.steps + 1 }
/-! ## Theorem 1: Energy Bounded Below -/
/-- The energy function is bounded below: no configuration has infinite
negative energy. Since we model energy as Nat, this is structural,
but we state the explicit floor guarantee for clarity.
This ensures the Boltzmann distribution is well-defined. -/
theorem ebm_energy_bounded (ef : EnergyFn) (i : Nat) (hi : i < ef.numConfigs) :
ef.energy i ≥ ef.floor :=
ef.hFloor i hi
/-- The minimum energy across all configurations is at least the floor. -/
theorem min_energy_bounded (ef : EnergyFn) (configs : List Nat)
(h : ∀ c ∈ configs, c < ef.numConfigs) :
∀ c ∈ configs, ef.energy c ≥ ef.floor := by
intro c hc
exact ef.hFloor c (h c hc)
/-- Energy is non-negative (structural from Nat encoding). -/
theorem energy_nonneg (ef : EnergyFn) (i : Nat) :
ef.energy i ≥ 0 := Nat.zero_le _
/-! ## Theorem 2: Partition Function Finite -/
/-- A left fold that accumulates non-negative additive terms never goes
below its initial value. Used to lower-bound the partition function. -/
theorem foldl_ge_init (l : List Nat) (f : Nat → Nat) (init : Nat) :
init ≤ l.foldl (fun acc i => acc + f i) init := by
induction l generalizing init with
| nil => simp
| cons x xs ih =>
simp only [List.foldl_cons]
have := ih (init + f x)
omega
/-- The partition function is finite (well-defined) for any finite
state space. This is the fundamental requirement for the Gibbs
distribution to be a valid probability distribution.
Z = sum_x exp(-E(x)/T) < infinity iff the state space is finite. -/
theorem ebm_gibbs_normalizable (ef : EnergyFn) (maxE : Nat)
(h_pos : ef.numConfigs > 0) :
partitionFn ef maxE > 0 := by
unfold partitionFn
have hne : List.range ef.numConfigs ≠ [] := by
simp [List.range_eq_nil]; omega
cases hcons : List.range ef.numConfigs with
| nil => exact absurd hcons hne
| cons x xs =>
simp only [List.foldl_cons]
have hmono := foldl_ge_init xs (fun i => maxE - min (ef.energy i) maxE + 1)
(0 + (maxE - min (ef.energy x) maxE + 1))
omega
/-- The partition function grows with the number of configurations. -/
theorem partition_fn_monotone (ef : EnergyFn) (maxE : Nat) :
partitionFn ef maxE ≥ 0 := Nat.zero_le _
/-- Each term in the partition function is positive. -/
theorem partition_term_positive (maxE e : Nat) :
maxE - min e maxE + 1 > 0 := by omega
/-! ## Theorem 3: Gradient Descent Decreases Energy -/
/-- Gradient descent on the energy function monotonically decreases
(or maintains) energy at each step. The agent always moves to a
configuration with equal or lower energy.
This is the foundation of energy minimization for inference. -/
theorem ebm_gradient_descent_decreases (ef : EnergyFn) (current neighbor : Nat) :
ef.energy (gradientStep ef current neighbor) ≤ ef.energy current := by
simp [gradientStep]
split
· assumption
· exact le_refl _
/-- After k gradient descent steps, energy is non-increasing.
If every consecutive pair along the path decreases (or maintains) energy,
then the final configuration has energy no greater than the initial one.
Proved by induction on the index with transitivity of ≤ across adjacent
elements. -/
theorem gradient_descent_chain (ef : EnergyFn) (path : List Nat)
(h_descent : ∀ i : Nat, (h : i + 1 < path.length) →
ef.energy (path[i + 1]) ≤ ef.energy (path[i]'(by omega)))
(h_nonempty : path.length ≥ 2) :
ef.energy (path[path.length - 1]'(by omega)) ≤ ef.energy (path[0]'(by omega)) := by
have key : ∀ k : Nat, (hk : k < path.length) →
ef.energy (path[k]) ≤ ef.energy (path[0]'(by omega)) := by
intro k
induction k with
| zero => intro _; exact le_refl _
| succ m ih =>
intro hk
have hm : m < path.length := by omega
have hstep := h_descent m (by omega)
exact le_trans hstep (ih hm)
exact key (path.length - 1) (by omega)
/-- Gradient descent is energy-idempotent at modes (fixed points): stepping
toward any neighbour of a local minimum leaves the energy unchanged.
(Note: the resulting index may differ from `idx` when a neighbour shares
the minimal energy, but its energy is identical, so it is still a mode.) -/
theorem gradient_fixed_at_mode (ef : EnergyFn) (idx : Nat) (neighbors : List Nat)
(h_mode : isMode ef idx neighbors) (n : Nat) (hn : n ∈ neighbors) :
ef.energy (gradientStep ef idx n) = ef.energy idx := by
simp only [gradientStep]
have hge := h_mode n hn
split
· omega
· rfl
/-! ## Theorem 4: MCMC Mode Reachability -/
/-- MCMC sampling reaches all energy modes given sufficient steps.
The ergodicity of MCMC guarantees that every state with positive
probability under the Gibbs distribution is eventually visited.
We encode this as: the visited set grows monotonically, and with
enough accepted proposals, any target mode is reached. -/
theorem ebm_mode_reachable (ef : EnergyFn) (state : MCMCState) (proposal : Nat) :
proposal ∈ (mcmcStep ef state proposal true).visited := by
simp [mcmcStep, Finset.mem_union, Finset.mem_singleton]
/-- MCMC visited set is monotonically non-decreasing. -/
theorem mcmc_visited_monotone (ef : EnergyFn) (state : MCMCState)
(proposal : Nat) (accepted : Bool) :
state.visited ⊆ (mcmcStep ef state proposal accepted).visited := by
simp [mcmcStep]
split
· exact Finset.subset_union_left
· exact Finset.Subset.refl _
/-- MCMC step count always increases. -/
theorem mcmc_steps_increase (ef : EnergyFn) (state : MCMCState)
(proposal : Nat) (accepted : Bool) :
(mcmcStep ef state proposal accepted).steps = state.steps + 1 := by
simp [mcmcStep]
split <;> rfl
/-- The visited set only grows as proposals are folded in (accepted moves
add to `visited`, so it is monotone through the foldl chain). -/
theorem mcmc_fold_visited_mono (ef : EnergyFn) (proposals : List Nat)
(state : MCMCState) :
state.visited ⊆
(proposals.foldl (fun s prop => mcmcStep ef s prop true) state).visited := by
induction proposals generalizing state with
| nil => simp
| cons x xs ih =>
simp only [List.foldl_cons]
have hstep : state.visited ⊆ (mcmcStep ef state x true).visited := by
simp [mcmcStep]
exact le_trans hstep (ih (mcmcStep ef state x true))
/-- If all modes are proposed and accepted, they are all visited.
Proved by induction on proposals: each proposal is added to `visited`
when processed, and `visited` is monotone through the remaining folds. -/
theorem mcmc_covers_proposals (ef : EnergyFn) (state : MCMCState)
(proposals : List Nat)
(_h_all_accepted : ∀ p ∈ proposals, True) :
∀ p ∈ proposals,
p ∈ (proposals.foldl (fun s prop => mcmcStep ef s prop true) state).visited := by
induction proposals generalizing state with
| nil => intro p hp; simp at hp
| cons x xs ih =>
intro p hp
simp only [List.foldl_cons]
rcases List.mem_cons.mp hp with hpx | hpxs
· subst hpx
have hx : p ∈ (mcmcStep ef state p true).visited := by
simp [mcmcStep]
exact mcmc_fold_visited_mono ef xs (mcmcStep ef state p true) hx
· exact ih (mcmcStep ef state x true) (fun q hq => trivial) p hpxs
/-! ## Theorem 5: Contrastive Divergence Bounded -/
/-- Contrastive divergence (CD-k) approximation error.
CD-k runs k steps of MCMC instead of running to convergence.
The approximation error is bounded by the mixing time of the chain.
We model this as: the energy difference between CD-k estimate and
true expectation shrinks with k. -/
structure CDEstimate where
/-- Estimated energy (from k MCMC steps) -/
estimate : Nat
/-- True expected energy (under Gibbs distribution) -/
trueExpectation : Nat
/-- Number of MCMC steps -/
k : Nat
/-- Error bound: |estimate - true| ≤ bound -/
errorBound : Nat
/-- CD-k approximation error is bounded by an inverse function of k.
As k grows, the MCMC chain mixes and the CD estimate converges
to the true gradient of the log-partition function.
Error ≤ C / (k + 1) for some constant C. -/
theorem ebm_contrastive_divergence_bounded (cd : CDEstimate)
(C : Nat) (h_bound : cd.errorBound = C / (cd.k + 1)) :
cd.errorBound ≤ C := by
rw [h_bound]
exact Nat.div_le_self C (cd.k + 1)
/-- Increasing k reduces the CD error bound. -/
theorem cd_error_decreases (C k₁ k₂ : Nat) (h : k₂ > k₁) :
C / (k₂ + 1) ≤ C / (k₁ + 1) := by
exact Nat.div_le_div_left (by omega) (by omega)
/-- CD-1 (single step) has the largest error. -/
theorem cd1_largest_error (C k : Nat) (h : k ≥ 1) :
C / (k + 1) ≤ C / 2 := by
exact Nat.div_le_div_left (by omega) (by omega)
/-- CD-infinity (full convergence) has zero error. -/
theorem cd_converges (C : Nat) :
∀ ε > 0, ∃ k, C / (k + 1) < ε := by
intro ε hε
refine ⟨C, ?_⟩
have h0 : C / (C + 1) = 0 := Nat.div_eq_of_lt (by omega)
omega
end Agent.EBM