mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
401 lines
14 KiB
Text
401 lines
14 KiB
Text
import Semantics.Bind
|
||
import Semantics.FixedPoint
|
||
import Semantics.Physics.Boundary
|
||
import Semantics.Physics.Conservation
|
||
import Semantics.Physics.BindPhysics
|
||
import Lean.Data.Json
|
||
|
||
namespace BindServer
|
||
|
||
open Lean Semantics Semantics.Physics
|
||
|
||
-- ============================================================================
|
||
-- JSON helpers
|
||
-- ============================================================================
|
||
|
||
def jsonObj (fields : List (String × Json)) : Json :=
|
||
Json.mkObj fields
|
||
|
||
@[inline]
|
||
def q16_16_of_float (f : Float) : UInt32 :=
|
||
if f.isNaN || f ≥ 32768.0 then 0xFFFFFFFF
|
||
else if f ≤ -32768.0 then 0x80000000
|
||
else (f * 65536.0).floor.toUInt32
|
||
|
||
@[inline]
|
||
def q16_16_to_float (u : UInt32) : Float :=
|
||
let signed : Int := if u ≥ 0x80000000 then (Int.ofNat (u.toUInt64.toNat) - 0x100000000) else Int.ofNat (u.toUInt64.toNat)
|
||
Float.ofInt signed / 65536.0
|
||
|
||
@[inline]
|
||
def jsonToQ16_16 (j : Json) : Except String Q16_16 :=
|
||
match j.getNum? with
|
||
| .ok n => .ok (Q16_16.ofFloat n.toFloat)
|
||
| .error _ => match j.getInt? with
|
||
| .ok n => .ok (Q16_16.ofInt n)
|
||
| .error e => .error e
|
||
|
||
def parseQ16_16Dict (j : Json) : Except String (List (String × Q16_16)) := do
|
||
let obj ← j.getObj?
|
||
obj.toList.mapM (fun (k, v) => do let q ← jsonToQ16_16 v; return (k, q))
|
||
|
||
def parseQ16_16List (j : Json) : Except String (List Q16_16) := do
|
||
let arr ← match j.getArr? with
|
||
| .ok a => .ok a
|
||
| .error _ => do
|
||
let obj ← j.getObjVal? "state_vector"
|
||
obj.getArr?
|
||
arr.toList.mapM jsonToQ16_16
|
||
|
||
@[inline]
|
||
def parseString (j : Json) : Except String String :=
|
||
j.getStr?
|
||
|
||
-- Quantity kind parsing
|
||
@[inline]
|
||
def parseQuantityKind (s : String) : QuantityKind :=
|
||
match s with
|
||
| "charge" => .charge
|
||
| "mass" => .mass
|
||
| "spin" => .spin
|
||
| "energy" => .energy
|
||
| "momentum" => .momentum
|
||
| "baryonNumber" => .baryonNumber
|
||
| "leptonNumber" => .leptonNumber
|
||
| _ => .charge
|
||
|
||
-- Simplified particle kind aliases for the bridge API
|
||
@[inline]
|
||
def parseParticleKind (s : String) : Except String ParticleKind :=
|
||
match s with
|
||
| "electron" => .ok (.lepton .electron false)
|
||
| "positron" => .ok (.lepton .electron true)
|
||
| "photon" => .ok (.gauge .photon)
|
||
| "proton" => .ok (.hadron .proton)
|
||
| "neutron" => .ok (.hadron .neutron)
|
||
| "neutrino" => .ok (.lepton .eNeutrino false)
|
||
| "up_quark" => .ok (.quark .up .red false)
|
||
| "down_quark" => .ok (.quark .down .blue false)
|
||
| _ => .error s!"Unknown particle kind: {s}"
|
||
|
||
def parseQuantity (k : String) (v : Json) : Except String Quantity := do
|
||
let n ← v.getInt?
|
||
return { kind := parseQuantityKind k, value := n }
|
||
|
||
def parseQuantities (j : Json) : Except String (List Quantity) := do
|
||
let obj ← j.getObj?
|
||
obj.toList.mapM (fun (k, v) => parseQuantity k v)
|
||
|
||
def parseParticle (j : Json) : Except String Particle := do
|
||
let kindJson ← j.getObjVal? "kind"
|
||
let kindStr ← parseString kindJson
|
||
let kind ← parseParticleKind kindStr
|
||
let quantities ← match j.getObjVal? "quantities" with
|
||
| .ok q => parseQuantities q
|
||
| .error _ => .ok []
|
||
return { kind := kind, quantities := quantities }
|
||
|
||
def parseParticles (j : Json) : Except String (List Particle) := do
|
||
-- Allow either a direct array or {"particles": [...]}
|
||
let arr ← match j.getArr? with
|
||
| .ok a => .ok a
|
||
| .error _ => do
|
||
let obj ← j.getObjVal? "particles"
|
||
obj.getArr?
|
||
arr.toList.mapM parseParticle
|
||
|
||
-- Float fallback: try JsonNumber first, then Int
|
||
@[inline]
|
||
def jsonToFloat (j : Json) : Except String Float :=
|
||
match j.getNum? with
|
||
| .ok n => .ok n.toFloat
|
||
| .error _ => match j.getInt? with
|
||
| .ok n => .ok (Float.ofInt n)
|
||
| .error e => .error e
|
||
|
||
def parseFloatDict (j : Json) : Except String (List (String × Float)) := do
|
||
let obj ← j.getObj?
|
||
obj.toList.mapM (fun (k, v) => do let f ← jsonToFloat v; return (k, f))
|
||
|
||
def parseFloatList (j : Json) : Except String (List Float) := do
|
||
-- Allow either a direct array or {"state_vector": [...]}
|
||
let arr ← match j.getArr? with
|
||
| .ok a => .ok a
|
||
| .error _ => do
|
||
let obj ← j.getObjVal? "state_vector"
|
||
obj.getArr?
|
||
arr.toList.mapM jsonToFloat
|
||
|
||
-- ============================================================================
|
||
-- Cost functions implemented in Lean (Q16.16 fixed-point)
|
||
-- ============================================================================
|
||
|
||
@[inline]
|
||
def euclideanCost (left right : List Q16_16) : UInt32 :=
|
||
let n := max left.length right.length
|
||
let a := left ++ List.replicate (n - left.length) Q16_16.zero
|
||
let b := right ++ List.replicate (n - right.length) Q16_16.zero
|
||
let sumSq := (List.zip a b).foldl (fun acc (x, y) => Q16_16.add acc (Q16_16.mul (Q16_16.sub x y) (Q16_16.sub x y))) Q16_16.zero
|
||
(Q16_16.sqrt sumSq).val
|
||
|
||
@[inline]
|
||
def klCost (left right : List (String × Float)) : UInt32 :=
|
||
-- log requires fixed-point lookup table or series expansion.
|
||
-- Keeping Float computation until Q16.16 log is implemented.
|
||
let total := left.foldl (fun acc (k, p) =>
|
||
let q := match right.lookup k with | some v => v | none => 1e-12
|
||
if p > 0.0 then acc + p * (Float.log (p / max q 1e-12)) else acc
|
||
) 0.0
|
||
q16_16_of_float total
|
||
|
||
@[inline]
|
||
def thermodynamicCost (left right :List (String × Q16_16)) : UInt32 :=
|
||
let entropyL := match left.lookup "entropy" with | some v => v | none => Q16_16.zero
|
||
let entropyR := match right.lookup "entropy" with | some v => v | none => Q16_16.zero
|
||
let temp := match left.lookup "temperature" with | some v => v | none => Q16_16.ofNat 300
|
||
let deltaS := Q16_16.sub entropyL entropyR
|
||
let kB := Q16_16.ofFloat 8.617e-5 -- Boltzmann constant in Q16.16
|
||
(Q16_16.abs (Q16_16.mul (Q16_16.mul deltaS temp) kB)).val
|
||
|
||
@[inline]
|
||
def controlCost (left right : List (String × Q16_16)) : UInt32 :=
|
||
let obs := match left.lookup "observation" with | some v => v | none => Q16_16.zero
|
||
let target := match right.lookup "setpoint" with | some v => v | none => Q16_16.zero
|
||
(Q16_16.abs (Q16_16.sub obs target)).val
|
||
|
||
@[inline]
|
||
def geodesicCost (left right : List Q16_16) (metric : Metric) : UInt32 :=
|
||
if metric.tensor == "identity" then
|
||
euclideanCost left right
|
||
else
|
||
let n := max left.length right.length
|
||
let a := left ++ List.replicate (n - left.length) Q16_16.zero
|
||
let b := right ++ List.replicate (n - right.length) Q16_16.zero
|
||
let scale := Q16_16.add Q16_16.one ⟨metric.cost⟩
|
||
let torsionPenalty := Q16_16.mul ⟨metric.torsion⟩ (Q16_16.ofFloat (3.1415926535 / 8.0))
|
||
let indices := List.range a.length
|
||
let dist := (List.zip a indices).foldl (fun acc (x, i) =>
|
||
let y := b.getD i Q16_16.zero
|
||
let delta := Q16_16.mul (Q16_16.sub x y) scale
|
||
let torsion := Q16_16.mul torsionPenalty (Q16_16.sin (Q16_16.ofInt i))
|
||
Q16_16.add acc (Q16_16.add (Q16_16.mul delta delta) (Q16_16.mul torsion torsion))
|
||
) Q16_16.zero
|
||
(Q16_16.sqrt dist).val
|
||
|
||
-- ============================================================================
|
||
-- Request / Response
|
||
-- ============================================================================
|
||
|
||
instance : Lean.FromJson UInt32 where
|
||
fromJson? j := match j.getNat? with | .ok n => .ok n.toUInt32 | .error e => .error e
|
||
|
||
instance : Lean.ToJson UInt32 where
|
||
toJson u := Json.num (Lean.JsonNumber.fromNat u.toNat)
|
||
|
||
structure BindRequest where
|
||
metricKind : String
|
||
left : Json
|
||
right : Json
|
||
useHistory : Bool := false
|
||
historyLen : Nat := 0
|
||
historyCost : UInt32 := 0x00000000
|
||
historyTorsion : UInt32 := 0x00000000
|
||
deriving FromJson, ToJson
|
||
|
||
structure BindResponse where
|
||
cost : UInt32
|
||
lawful : Bool
|
||
leftInvariant : String
|
||
rightInvariant : String
|
||
traceHash : String
|
||
metricTensor : String
|
||
metricTorsion : UInt32
|
||
metricHistoryLen : Nat
|
||
deriving ToJson
|
||
|
||
@[inline]
|
||
def buildMetric (req : BindRequest) : Metric :=
|
||
if req.useHistory && req.historyLen >= 2 then
|
||
{ cost := req.historyCost, tensor := req.metricKind, torsion := req.historyTorsion, reference := s!"nlocal_from_{req.historyLen}_binds", history_len := req.historyLen }
|
||
else
|
||
{ cost := 0x00000000, tensor := req.metricKind, torsion := 0x00000000, reference := "euclidean_baseline", history_len := req.historyLen }
|
||
|
||
@[inline]
|
||
def genericInvariant (j : Json) : String :=
|
||
j.compress
|
||
|
||
-- ============================================================================
|
||
-- Handlers
|
||
-- ============================================================================
|
||
|
||
def handlePhysical (req : BindRequest) : Except String BindResponse := do
|
||
let leftParticles ← parseParticles req.left
|
||
let rightParticles ← parseParticles req.right
|
||
let metric := buildMetric req
|
||
let invL := particleInvariant leftParticles
|
||
let invR := particleInvariant rightParticles
|
||
let b := physicalBindEval leftParticles rightParticles metric
|
||
return {
|
||
cost := b.cost,
|
||
lawful := b.lawful,
|
||
leftInvariant := invL,
|
||
rightInvariant := invR,
|
||
traceHash := b.witness.trace_hash,
|
||
metricTensor := metric.tensor,
|
||
metricTorsion := metric.torsion,
|
||
metricHistoryLen := metric.history_len
|
||
}
|
||
|
||
def handleInformational (req : BindRequest) : Except String BindResponse := do
|
||
let leftDict ← parseFloatDict req.left
|
||
let rightDict ← parseFloatDict req.right
|
||
let metric := buildMetric req
|
||
let cost := klCost leftDict rightDict
|
||
let invL := genericInvariant req.left
|
||
let invR := genericInvariant req.right
|
||
let lawful := invL == invR
|
||
return {
|
||
cost := cost,
|
||
lawful := lawful,
|
||
leftInvariant := invL,
|
||
rightInvariant := invR,
|
||
traceHash := if lawful then s!"lawful:{invL}={invR}" else "unlawful",
|
||
metricTensor := metric.tensor,
|
||
metricTorsion := metric.torsion,
|
||
metricHistoryLen := metric.history_len
|
||
}
|
||
|
||
def handleGeometric (req : BindRequest) : Except String BindResponse := do
|
||
let leftVec ← parseQ16_16List req.left
|
||
let rightVec ← parseQ16_16List req.right
|
||
let metric := buildMetric req
|
||
let cost := geodesicCost leftVec rightVec metric
|
||
let invL := genericInvariant req.left
|
||
let invR := genericInvariant req.right
|
||
let lawful := invL == invR
|
||
return {
|
||
cost := cost,
|
||
lawful := lawful,
|
||
leftInvariant := invL,
|
||
rightInvariant := invR,
|
||
traceHash := if lawful then s!"lawful:{invL}={invR}" else "unlawful",
|
||
metricTensor := metric.tensor,
|
||
metricTorsion := metric.torsion,
|
||
metricHistoryLen := metric.history_len
|
||
}
|
||
|
||
def handleThermodynamic (req : BindRequest) : Except String BindResponse := do
|
||
let leftDict ← parseQ16_16Dict req.left
|
||
let rightDict ← parseQ16_16Dict req.right
|
||
let metric := buildMetric req
|
||
let cost := thermodynamicCost leftDict rightDict
|
||
let invL := genericInvariant req.left
|
||
let invR := genericInvariant req.right
|
||
let lawful := invL == invR
|
||
return {
|
||
cost := cost,
|
||
lawful := lawful,
|
||
leftInvariant := invL,
|
||
rightInvariant := invR,
|
||
traceHash := if lawful then s!"lawful:{invL}={invR}" else "unlawful",
|
||
metricTensor := metric.tensor,
|
||
metricTorsion := metric.torsion,
|
||
metricHistoryLen := metric.history_len
|
||
}
|
||
|
||
def handleControl (req : BindRequest) : Except String BindResponse := do
|
||
let leftDict ← parseQ16_16Dict req.left
|
||
let rightDict ← parseQ16_16Dict req.right
|
||
let metric := buildMetric req
|
||
let cost := controlCost leftDict rightDict
|
||
let invL := genericInvariant req.left
|
||
let invR := genericInvariant req.right
|
||
let lawful := invL == invR
|
||
return {
|
||
cost := cost,
|
||
lawful := lawful,
|
||
leftInvariant := invL,
|
||
rightInvariant := invR,
|
||
traceHash := if lawful then s!"lawful:{invL}={invR}" else "unlawful",
|
||
metricTensor := metric.tensor,
|
||
metricTorsion := metric.torsion,
|
||
metricHistoryLen := metric.history_len
|
||
}
|
||
|
||
def handleRequest (req : BindRequest) : Except String BindResponse :=
|
||
match req.metricKind with
|
||
| "physical" => handlePhysical req
|
||
| "informational" => handleInformational req
|
||
| "geometric" | "riemannian" => handleGeometric req
|
||
| "thermodynamic" => handleThermodynamic req
|
||
| "control" => handleControl req
|
||
| _ => .error s!"Unknown metric kind: {req.metricKind}"
|
||
|
||
-- ============================================================================
|
||
-- Batch handlers
|
||
-- ============================================================================
|
||
|
||
structure BindBatchRequest where
|
||
requests : List BindRequest
|
||
deriving FromJson
|
||
|
||
structure BindBatchResponse where
|
||
results : List BindResponse
|
||
deriving ToJson
|
||
|
||
def handleBatchRequest (req : BindBatchRequest) : BindBatchResponse :=
|
||
{ results := req.requests.map (fun r => match handleRequest r with | .ok resp => resp | .error e => {
|
||
cost := 0xFFFFFFFF,
|
||
lawful := false,
|
||
leftInvariant := "",
|
||
rightInvariant := "",
|
||
traceHash := s!"error:{e}",
|
||
metricTensor := "",
|
||
metricTorsion := 0x00000000,
|
||
metricHistoryLen := 0
|
||
}) }
|
||
|
||
-- ============================================================================
|
||
-- I/O Loop
|
||
-- ============================================================================
|
||
|
||
partial def serve : IO Unit := do
|
||
let stdin ← IO.getStdin
|
||
let stdout ← IO.getStdout
|
||
let line ← stdin.getLine
|
||
if line.isEmpty || line == "\n" then
|
||
return ()
|
||
match Json.parse line with
|
||
| .error e =>
|
||
stdout.putStrLn (Json.compress (jsonObj [("error", Json.str e)]))
|
||
stdout.flush
|
||
| .ok j =>
|
||
-- Dispatch: if "requests" field exists, treat as batch; else single
|
||
let isBatch := match j.getObjVal? "requests" with | .ok _ => true | .error _ => false
|
||
if isBatch then
|
||
match fromJson? j with
|
||
| .error e =>
|
||
stdout.putStrLn (Json.compress (jsonObj [("error", Json.str e)]))
|
||
stdout.flush
|
||
| .ok batchReq =>
|
||
let batchResp := handleBatchRequest batchReq
|
||
stdout.putStrLn (Json.compress (toJson batchResp))
|
||
stdout.flush
|
||
else
|
||
match fromJson? j with
|
||
| .error e =>
|
||
stdout.putStrLn (Json.compress (jsonObj [("error", Json.str e)]))
|
||
stdout.flush
|
||
| .ok req =>
|
||
match handleRequest req with
|
||
| .error e =>
|
||
stdout.putStrLn (Json.compress (jsonObj [("error", Json.str e)]))
|
||
stdout.flush
|
||
| .ok resp =>
|
||
stdout.putStrLn (Json.compress (toJson resp))
|
||
stdout.flush
|
||
serve
|
||
|
||
end BindServer
|
||
|
||
def main : IO Unit := BindServer.serve
|