import Mathlib.Data.Finset.Card import Mathlib.Data.Finset.Powerset import Mathlib.Data.Matrix.Mul import Mathlib.Data.ZMod.Basic import Mathlib.Tactic open Finset Matrix -- ============================================================================ -- Hypergraphs and syndrome decoding -- ============================================================================ -- -- A hypergraph H = (V, E) gives rise to a linear code: its incidence matrix M -- (rows = edges, columns = vertices, entry = 1 if v ∈ e) is the parity-check -- matrix of the code. The syndrome decoding problem asks: given M and a -- syndrome σ = M·e with Hamming weight(e) ≤ t, recover e. -- -- For the worked hypergraph of V = {0,1,2,3}, -- edges e₁ = {0,1,2}, e₂ = {1,3}, and e₃ = {0,3}: -- that is the 3-edge-row × 4-vertex-col table: -- -- v₀ v₁ v₂ v₃ -- e₁ [ 1 1 1 0 ] -- 0,1,2 ∈ e₁, 3 ∉ e₁ -- e₂ [ 0 1 0 1 ] -- 1,3 ∈ e₂ -- e₃ [ 1 0 0 1 ] -- 0,3 ∈ e₃ -- -- This file sets up the hypergraph-to-code bridge, states the two conditions -- (k-uniformity, minimum distance) that make decoding well-posed, proves the -- uniqueness theorem, and marks the LPN-style hardness assumption that makes -- the problem cryptographically meaningful. -- -- The [7,4,3] Hamming code (Algebra/Code/Hamming.lean, using its parity-check -- matrix `H`) is a concrete 4-uniform hypergraph with minimum distance 3: it -- demonstrates the uniqueness property with t = 1. -- ============================================================================ -- Section 1: The structure -- ============================================================================ /-- A finite hypergraph on a vertex type `V`: a vertex set together with a family of hyperedges, each contained in the vertex set. -/ structure Hypergraph (V : Type*) [DecidableEq V] where vertices : Finset V edges : Finset (Finset V) mem_vertices : ∀ e ∈ edges, e ⊆ vertices namespace Hypergraph variable {V : Type*} [DecidableEq V] (H : Hypergraph V) /-- The hypergraph is k-uniform when every edge has exactly k vertices. -/ def IsUniform (k : ℕ) : Prop := ∀ e ∈ H.edges, e.card = k -- ============================================================================ -- Section 2: Incidence matrix over 𝔽₂ -- ============================================================================ /-- Rows of the incidence matrix: the hyperedges. -/ abbrev EdgeIdx := {e // e ∈ H.edges} /-- Columns of the incidence matrix: the vertices. -/ abbrev VertIdx := {v // v ∈ H.vertices} /-- The incidence matrix over 𝔽₂: entry (e, v) = 1 iff v ∈ e. -/ def incidence : Matrix H.EdgeIdx H.VertIdx (ZMod 2) := fun e v => if (v : V) ∈ (e : Finset V) then 1 else 0 -- ============================================================================ -- Section 3: The code and syndrome -- ============================================================================ /-- The syndrome of x under the incidence matrix: σ = M·x. -/ def syndrome (x : H.VertIdx → ZMod 2) : H.EdgeIdx → ZMod 2 := H.incidence.mulVec x /-- The code of the hypergraph: the kernel of the syndrome map, i.e. the set of words with syndrome 0. -/ def code : Submodule (ZMod 2) (H.VertIdx → ZMod 2) := LinearMap.ker H.incidence.mulVecLin theorem mem_code_iff (x : H.VertIdx → ZMod 2) : x ∈ code H ↔ H.syndrome x = 0 := by simp [code, syndrome, H.incidence.mulVecLin_apply] theorem syndrome_eq_iff_sub_mem_code (a b : H.VertIdx → ZMod 2) : H.syndrome a = H.syndrome b ↔ a - b ∈ code H := by rw [mem_code_iff, syndrome, syndrome] calc H.incidence.mulVecLin a = H.incidence.mulVecLin b ↔ H.incidence.mulVecLin a - H.incidence.mulVecLin b = 0 := sub_eq_zero.symm _ ↔ H.incidence.mulVecLin (a - b) = 0 := by rw [H.incidence.mulVecLin.map_sub a b] _ ↔ H.incidence *ᵥ (a - b) = 0 := by simp _ ↔ a - b ∈ code H := (mem_code_iff H (a - b)).symm -- ============================================================================ -- Section 4: Hamming weight and the uniqueness theorem -- ============================================================================ /-- Hamming weight of a binary vector: number of nonzero coordinates. -/ def hammingWeight {n : Type*} [Fintype n] [DecidableEq n] (x : n → ZMod 2) : ℕ := (Finset.univ.filter fun i => x i ≠ 0).card theorem hammingWeight_add_le {n : Type*} [Fintype n] [DecidableEq n] (x y : n → ZMod 2) : hammingWeight (x + y) ≤ hammingWeight x + hammingWeight y := by refine le_trans (Finset.card_le_card ?_) (Finset.card_union_le _ _) intro i hi simp only [Finset.mem_filter, Finset.mem_univ, true_and, Finset.mem_union, Pi.add_apply] at hi ⊢ by_contra h rw [not_or, not_not, not_not] at h exact hi (by rw [h.1, h.2, add_zero]) theorem hammingWeight_neg {n : Type*} [Fintype n] [DecidableEq n] (x : n → ZMod 2) : hammingWeight (-x) = hammingWeight x := by unfold hammingWeight; congr 1; ext i; simp /-- The syndrome decoding problem: find e of weight ≤ t with M·e = σ. -/ def IsSolution (σ : H.EdgeIdx → ZMod 2) (t : ℕ) (e : H.VertIdx → ZMod 2) : Prop := H.syndrome e = σ ∧ hammingWeight e ≤ t /-- Unique decoding within radius t (the t = ⌊(d−1)/2⌋ bound). If every nonzero codeword has weight ≥ 2t+1, two weight-≤t solutions for the same syndrome are equal. -/ theorem unique_solution {t : ℕ} {σ : H.EdgeIdx → ZMod 2} {e₁ e₂ : H.VertIdx → ZMod 2} (hmin : ∀ c ∈ code H, c ≠ 0 → 2 * t + 1 ≤ hammingWeight c) (h₁ : H.IsSolution σ t e₁) (h₂ : H.IsSolution σ t e₂) : e₁ = e₂ := by rcases h₁ with ⟨hsyn₁, hw₁⟩ rcases h₂ with ⟨hsyn₂, hw₂⟩ have hc : e₁ - e₂ ∈ code H := (syndrome_eq_iff_sub_mem_code H e₁ e₂).mp (by rw [hsyn₁, hsyn₂]) by_contra hne have hsub : e₁ - e₂ ≠ 0 := sub_ne_zero.mpr hne have hle : hammingWeight (e₁ - e₂) ≤ 2 * t := by calc hammingWeight (e₁ - e₂) = hammingWeight (e₁ + (-e₂)) := by simp [sub_eq_add_neg] _ ≤ hammingWeight e₁ + hammingWeight (-e₂) := hammingWeight_add_le e₁ (-e₂) _ = hammingWeight e₁ + hammingWeight e₂ := by rw [hammingWeight_neg e₂] _ ≤ t + t := by omega _ = 2 * t := by omega have hd := hmin _ hc hsub omega -- ============================================================================ -- Section 5: Hardness assumption -- ============================================================================ -- -- The uniqueness theorem guarantees a low-weight solution is unique if it -- exists. Finding it efficiently is a computational question. The structure -- and axiom below capture the LPN assumption in the parity-check regime: -- there exist k-uniform hypergraphs with minimum distance d whose -- syndrome-decoding problem is computationally infeasible. /-- A hypergraph suitable for cryptographic use: k-uniform, with minimum distance d (so decoding is well-posed), and such that the syndrome-decoding problem (find e of weight ≤ ⌊(d-1)/2⌋ from σ = H.syndrome e) is hard. Hardness is a complexity-theoretic claim that cannot be formalized in Lean; `SecureHypergraph` bundles the formalizable conditions and marks the computational claim as an axiom. -/ structure SecureHypergraph (k d : ℕ) where H : Hypergraph ℕ uniform : H.IsUniform k minDist : ∀ c ∈ code H, c ≠ 0 → d ≤ hammingWeight c /-- There exist secure hypergraphs for any security parameter k and minimum distance d. This is the LPN assumption. -/ axiom existsSecureHypergraph (k d : ℕ) : SecureHypergraph k d -- ============================================================================ -- Section 6: The Hamming [7,4,3] code as a 4-uniform hypergraph -- ============================================================================ -- -- The parity-check matrix of the [7,4,3] Hamming code -- (Algebra/Code/Hamming.lean) defines a 4-uniform hypergraph on 7 vertices -- with 3 edges. Its minimum distance is 3 = 2·1+1, so the uniqueness theorem -- holds with t = 1. section HammingExample /-- The parity-check matrix H of the [7,4,3] Hamming code. -/ def H_matrix : Matrix (Fin 3) (Fin 7) (ZMod 2) := !![1, 0, 1, 1, 1, 0, 0; 1, 1, 1, 0, 0, 1, 0; 0, 1, 1, 1, 0, 0, 1] /-- The hypergraph induced by H: vertices = column indices, edges = row supports. -/ def hammingHypergraph : Hypergraph (Fin 7) where vertices := Finset.univ edges := { Finset.filter (λ j => H_matrix 0 j = 1) Finset.univ, Finset.filter (λ j => H_matrix 1 j = 1) Finset.univ, Finset.filter (λ j => H_matrix 2 j = 1) Finset.univ } mem_vertices := by intro e he; simp at he; rcases he with (rfl|rfl|rfl) <;> simp -- It is 4-uniform. set_option maxRecDepth 1000000 in example : hammingHypergraph.IsUniform 4 := by unfold hammingHypergraph IsUniform; decide -- Its code (the kernel of the incidence matrix) has minimum distance 3. set_option maxRecDepth 1000000 in example : ∀ c : Fin 7 → ZMod 2, H_matrix.mulVec c = 0 → c ≠ 0 → 3 ≤ hammingWeight c := by decide -- Therefore, syndrome decoding for t = 1 is unique on the hypergraph. example (σ : hammingHypergraph.EdgeIdx → ZMod 2) (e₁ e₂ : hammingHypergraph.VertIdx → ZMod 2) (hmin : ∀ c ∈ code hammingHypergraph, c ≠ 0 → 2 * 1 + 1 ≤ hammingWeight c) (h₁ : hammingHypergraph.IsSolution σ 1 e₁) (h₂ : hammingHypergraph.IsSolution σ 1 e₂) : e₁ = e₂ := unique_solution hammingHypergraph hmin h₁ h₂ end HammingExample end Hypergraph