/- BindingSiteEntropy.lean — Information Entropy for Protein Binding Sites Computes the information entropy profile of a binding site using the Fisher information metric (guaranteed unique by Chentsov). This is the direct protein-structure analog of the spectral profile pipeline (eigensolid_pipeline.py) for equations. References: - Yang, Yuan, Chou 2025 (Void-X): Eq. 3 (information entropy) - Giani, Win, Conti 2025: quantum discrimination via PVGS - Research-Stack library/ChentsovFinite.lean: metric uniqueness -/} import Mathlib import BindingSiteHachimoji namespace BindingSiteEntropy open BindingSiteHachimoji -- ================================================================= -- §1. INFORMATION ENTROPY PER RESIDUE SITE (Void-X Eq. 3) -- ================================================================= /-- Information entropy of a single residue site, from Void-X Eq. 3: S_i = -Σ_j p(a_j | context) log p(a_j | context) where p(a_j | context) is the conditional probability of atom type a_j given the structural context (neighboring atoms). In Void-X, this is computed from the diffusion model's output distribution over 50 atom types. Here we formalize it as a probability distribution over the Hachimoji state space. -/ def siteEntropy (probDist : Fin 50 → ℝ) : ℝ := -∑ i, if probDist i > 0 then probDist i * Real.log (probDist i) else 0 /-- The maximum possible entropy for 50 states (uniform distribution). S_max = log(50) ≈ 3.912. -/ def maxEntropy50 : ℝ := Real.log 50 /-- Normalized entropy: S* = S_i / S_max ∈ [0, 1]. This is what Void-X uses for the bindability score. -/ def normalizedEntropy (probDist : Fin 50 → ℝ) : ℝ := siteEntropy probDist / maxEntropy50 -- ================================================================= -- §2. ENTROPY FROM PDB STRUCTURE -- ================================================================= /-- Extract the amino acid distribution at a residue position from a PDB structure. This reads the B-factors (temperature factors) as a proxy for positional uncertainty, which maps to entropy. High B-factor → high uncertainty → high entropy → Π or Λ state Low B-factor → ordered → low entropy → Φ state The B-factor is already in the PDB file — no ML model needed for the baseline entropy computation. -/ def entropyFromBFactor (bFactor : ℝ) (neighborBFactors : List ℝ) : ℝ := -- Local average B-factor normalizes by neighborhood context let localAvg := (bFactor + List.sum neighborBFactors) / (1 + neighborBFactors.length) -- Map to [0, 1]: higher B-factor = higher entropy Real.log (1 + localAvg) / Real.log (1 + 100) -- Dividing by log(101) since B-factors typically range 0-100 /-- Alternative: entropy from theclusters-by-entity-40 sequence cluster identity. Residues in the same cluster have similar structural contexts and thus similar entropy profiles. -/ def entropyFromCluster (clusterSize : ℕ) (sequenceIdentity : ℝ) : ℝ := -- High sequence identity within cluster → low entropy (conserved) -- Large cluster size → high diversity → higher entropy let diversity := Real.log (1 + clusterSize) let conservation := sequenceIdentity diversity * (1 - conservation) -- ================================================================= -- §3. BINDING SITE ENTROPY PROFILE -- ================================================================= /-- Compute the full entropy profile of a binding site from a sequence of residue data (PDB-derived or Void-X-generated). This is the protein-structure analog of `spectralProfile` in eigensolid_pipeline.py. -/ def bindingSiteEntropyProfile (residues : List (String × String × ℝ × List ℝ)) : List (AminoAcidToken × ℝ × BindingSiteState) := residues.map (λ (residueType, mod, bFactor, neighborBFs) => let token := residueToToken residueType mod let entropy := entropyFromBFactor bFactor neighborBFs let state := entropyToHachimoji entropy false true (token, entropy, state) ) /-- Average entropy of a binding site (the main metric from Void-X). -/ def averageSiteEntropy (profile : List (AminoAcidToken × ℝ × BindingSiteState)) : ℝ := let entropies := profile.map (λ (_, e, _) => e) match entropies with | [] => 0 | es => List.sum es / es.length /-- Bindability score B* from Yang et al. 2025 (Fig. S8): B* = 100 × [1 - (S* - min(S)) / (max(S) - min(S))] High B* = low entropy relative to the protein surface = ordered pocket suitable for ligand binding. -/ def bindabilityScore (profile : List (AminoAcidToken × ℝ × BindingSiteState)) (globalMin globalMax : ℝ) : ℝ := let avg := averageSiteEntropy profile 100 * (1 - (avg - globalMin) / (globalMax - globalMin)) -- ================================================================= -- §4. SIDON ADDRESS FOR BINDING SITE (from existing library) -- ================================================================= /-- A binding site gets a Sidon address from its entropy profile, exactly like an equation gets a Sidon address from its spectral profile (eigensolid_pipeline.py). The 8 dominant entropy values are the "observables" that feed into the 8×8 PIST adjacency matrix, which eigendecomposes to an 8D spectral profile → Sidon address. -/ def entropyToSidonAddress (profile : List (AminoAcidToken × ℝ × BindingSiteState)) : List ℕ := -- Extract top 8 entropy values (one per Hachimoji state category) let stateEntropies := List.filterMap (λ (_, e, s) => match s with | .Φ => some (0, e) | .Λ => some (1, e) | .Ρ => some (2, e) | .Κ => some (3, e) | .Ω => some (4, e) | .Σ => some (5, e) | .Π => some (6, e) | .Ζ => some (7, e) ) profile -- Map to Sidon powers {1, 2, 4, 8, 16, 32, 64, 128} -- weighted by entropy magnitude stateEntropies.map (λ (idx, e) => Nat.pow 2 idx * (min (Nat.floor (e * 10)) 16) ) -- ================================================================= -- §5. FISHER METRIC ON BINDING SITE MANIFOLD -- ================================================================= /-- The binding site manifold: probability distributions over residue tokens, equipped with the Fisher metric. Geodesics on this manifold are evolutionarily optimal paths. -/ structure BindingSiteManifold where distribution : AminoAcidDistribution metric : Fin 50 → Fin 50 → ℝ := fisherMetric50 distribution entropy : ℝ := siteEntropy distribution.val geodesicDistance : BindingSiteManifold → ℝ := sorry -- Geodesic distance requires solving the geodesic equation on Δ^49. -- This is computationally intensive; use approximation for now. /-- Approximate Fisher-Rao distance between two binding sites using the Bhattacharyya coefficient (efficient approximation). -/ def fisherRaoApprox (p q : AminoAcidDistribution) : ℝ := Real.sqrt (2 * Real.log (1 / ∑ i, Real.sqrt (p.val i * q.val i))) /-- Theorem: nearby binding sites (small Fisher distance) have similar druggability profiles. This is what enables transfer learning across protein families. -/ theorem fisher_implies_similar_druggability (p q : AminoAcidDistribution) (sites : List (AminoAcidToken × ℝ × BindingSiteState)) (h : fisherRaoApprox p q < 0.1) : -- Sites with similar distributions have similar dominant states (dominantState p sites = dominantState q sites) ∨ (bothDruggable p q sites) := by sorry -- Proof: relies on continuity of entropy w.r.t. Fisher metric -- and the classification threshold structure of entropyToHachimoji. where dominantState := λ d _ => entropyToHachimoji (siteEntropy d.val) false true bothDruggable := λ d1 d2 _ => let s1 := entropyToHachimoji (siteEntropy d1.val) false true let s2 := entropyToHachimoji (siteEntropy d2.val) false true (s1 = .Π ∨ s1 = .Λ) ∧ (s2 = .Π ∨ s2 = .Λ) end BindingSiteEntropy