mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
96 lines
3.6 KiB
Text
96 lines
3.6 KiB
Text
/- Copyright (c) 2026 Sovereign Research Stack. All rights reserved.
|
|
Released under Apache 2.0 license as described in the file LICENSE.
|
|
Authors: Research Stack Team
|
|
|
|
SpectralField.lean — Local Field Accumulation and Interaction Metrics
|
|
|
|
Per AGENTS.md §2: PascalCase for types, camelCase for functions.
|
|
Lean 4 is the source of truth.
|
|
-/
|
|
|
|
import Semantics.FixedPoint
|
|
import Semantics.GeneticCode
|
|
import Semantics.ShellModel
|
|
import Semantics.Spectrum
|
|
|
|
namespace Semantics
|
|
|
|
open Semantics.GeneticCode
|
|
open Semantics.ShellModel
|
|
open Semantics.Spectrum
|
|
|
|
/-- Local field aggregates mass, polarity, and spectral contributions from a neighborhood. -/
|
|
structure LocalField where
|
|
massField : Q16_16
|
|
polarityField : Q16_16
|
|
spectrum : SpectralSignature
|
|
deriving Repr, DecidableEq
|
|
|
|
/-- Zero field (neutral element for accumulation). -/
|
|
def zeroField : LocalField :=
|
|
{ massField := Q16_16.zero
|
|
, polarityField := Q16_16.zero
|
|
, spectrum := SpectralSignature.empty }
|
|
|
|
/-- Add two fields (piecewise summation). -/
|
|
def addField (x y : LocalField) : LocalField :=
|
|
{ massField := Q16_16.add x.massField y.massField
|
|
, polarityField := Q16_16.add x.polarityField y.polarityField
|
|
, spectrum := x.spectrum.piecewiseMerge y.spectrum }
|
|
|
|
/-- Compute contribution of an event at distance d to a local field. -/
|
|
def fieldContribution (w : Q16_16) (tip : TipCoord) (col : SpectralSignature) : LocalField :=
|
|
{ massField := Q16_16.mul w (Q16_16.ofInt tip.mass)
|
|
, polarityField := Q16_16.mul w (Q16_16.ofInt tip.polarity)
|
|
, spectrum := { bins := col.bins.map (λ b => Q16_16.mul w b) } }
|
|
|
|
/-- Build local field at position n by looking up to maxN. -/
|
|
partial def buildFieldAtLoop (n maxN : Nat) (m : Nat) (acc : LocalField) : LocalField :=
|
|
if m > maxN then
|
|
acc
|
|
else
|
|
let acc' :=
|
|
if m > n then
|
|
let d := m - n
|
|
let w := tailWeight d
|
|
match eventAt m with
|
|
| some (_, _, tip, col) =>
|
|
if w.val.toNat = 0 then acc else addField acc (fieldContribution w tip col)
|
|
| none => acc
|
|
else acc
|
|
buildFieldAtLoop n maxN (m+1) acc'
|
|
|
|
def buildFieldAt (n maxN : Nat) : LocalField :=
|
|
buildFieldAtLoop n maxN (n+1) zeroField
|
|
|
|
/-- Compute interaction score between a tip and a local field. -/
|
|
def interactionScore (tip : TipCoord) (field : LocalField) (col : SpectralSignature) : Q16_16 :=
|
|
let massTerm := Q16_16.mul (Q16_16.ofInt tip.mass) field.massField
|
|
let polTerm := Q16_16.mul (Q16_16.ofInt tip.polarity) field.polarityField
|
|
let specTerm := SpectralSignature.spectralOverlap col field.spectrum
|
|
Q16_16.add (Q16_16.add massTerm polTerm) specTerm
|
|
|
|
/-- Interaction score with only spectral component. -/
|
|
def spectralInteractionOnly (sig1 sig2 : SpectralSignature) : Q16_16 :=
|
|
SpectralSignature.spectralOverlap sig1 sig2
|
|
|
|
/-- Compute field magnitude (L2 norm approximation). -/
|
|
def fieldMagnitude (field : LocalField) : Q16_16 :=
|
|
let m2 := Q16_16.mul field.massField field.massField
|
|
let p2 := Q16_16.mul field.polarityField field.polarityField
|
|
let sum := Q16_16.add m2 p2
|
|
if field.massField.val.toNat > field.polarityField.val.toNat then
|
|
Q16_16.div sum (Q16_16.add field.massField (Q16_16.ofInt 1))
|
|
else
|
|
Q16_16.div sum (Q16_16.add field.polarityField (Q16_16.ofInt 1))
|
|
|
|
/-- Check if field is non-trivial. -/
|
|
def fieldIsActive (field : LocalField) : Bool :=
|
|
field.massField.val.toNat ≠ 0 || field.polarityField.val.toNat ≠ 0 ||
|
|
field.spectrum.bins.any (λ b => b.val.toNat ≠ 0)
|
|
|
|
-- Verification
|
|
#eval buildFieldAt 1 10
|
|
#eval spectralInteractionOnly (SpectralSignature.eventSpectrum EventType.a) (SpectralSignature.eventSpectrum EventType.a)
|
|
|
|
end Semantics
|