Research-Stack/0-Core-Formalism/lean/Semantics/Semantics/MoECache.lean

175 lines
7.9 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
MoECache.lean — MoE Cache Data Structures and Eviction Policy
Replaces infra/moe_ene_cache.py cache logic with a formal Lean module.
Defines MoE (Mixture-of-Experts) cache structures and eviction policies.
Per AGENTS.md:
- Q16_16 for scoring (§1.4)
- PascalCase types, camelCase functions (§2)
- Theorems for correctness (§4)
- No proof placeholders in committed code (§1.6)
-/
import Mathlib.Data.Nat.Basic
import Mathlib.Data.List.Basic
import Std
namespace Semantics.MoECache
-- ═══════════════════════════════════════════════════════════════════════════
-- §0 Q16_16 Fixed-Point Arithmetic
-- ═══════════════════════════════════════════════════════════════════════════
structure Q16_16 where
raw : Int
deriving Repr, DecidableEq, Inhabited, BEq
namespace Q16_16
def zero : Q16_16 := ⟨0⟩
def one : Q16_16 := ⟨65536⟩
def ofFrac (num denom : Nat) : Q16_16 :=
if denom = 0 then zero else ⟨(num * 65536) / denom⟩
end Q16_16
-- ═══════════════════════════════════════════════════════════════════════════
-- §1 Expert Configuration Structure
-- ═══════════════════════════════════════════════════════════════════════════
structure ExpertConfiguration where
expertId : Nat
gatingWeight : Q16_16 -- g
qualityWeight : Q16_16 -- w
coherence : Q16_16 -- h
penaltyWeight : Q16_16 -- v
distortion : Q16_16 -- p
arity : Q16_16 -- N
costCoefficient : Q16_16 -- a
overhead : Q16_16 -- c
domain : String
version : String
deriving Repr, Inhabited
-- ═══════════════════════════════════════════════════════════════════════════
-- §2 Cache Entry Structure
-- ═══════════════════════════════════════════════════════════════════════════
structure MoECacheEntry where
cacheKey : String
expertIds : List Nat
etaMoEResult : Q16_16
iDiscarded : Q16_16
timestamp : Nat
confidence : Q16_16
hitCount : Nat
deriving Repr, Inhabited
-- ═══════════════════════════════════════════════════════════════════════════
-- §3 Cache Statistics
-- ═══════════════════════════════════════════════════════════════════════════
structure CacheStatistics where
expertCacheEntries : Nat
expertCacheHits : Nat
computationCacheEntries : Nat
computationCacheHits : Nat
rewiringProposals : Nat
deriving Repr, Inhabited
-- ═══════════════════════════════════════════════════════════════════════════
-- §4 LRU Eviction Policy
-- ═══════════════════════════════════════════════════════════════════════════
structure LRUEntry where
key : String
accessTime : Nat
deriving Repr, Inhabited
/-- LRU cache with maximum size -/
structure LRUCache where
entries : List LRUEntry
maxSize : Nat
currentTime : Nat
deriving Repr, Inhabited
/-- Initialize empty LRU cache -/
def initLRUCache (maxSize : Nat) : LRUCache :=
{ entries := [], maxSize := maxSize, currentTime := 0 }
/-- Update access time for a key -/
def lruAccess (cache : LRUCache) (key : String) : LRUCache :=
let newTime := cache.currentTime + 1
let updatedEntries := cache.entries.map (fun e =>
if e.key = key then { key := e.key, accessTime := newTime } else e
)
let keyExists := cache.entries.any (fun e => e.key = key)
let finalEntries :=
if keyExists then
updatedEntries
else
{ key := key, accessTime := newTime } :: updatedEntries
let trimmedEntries := List.take cache.maxSize finalEntries
{ entries := trimmedEntries, maxSize := cache.maxSize, currentTime := newTime }
/-- Get least recently used key for eviction -/
def getLRUEvictionKey (cache : LRUCache) : Option String :=
if cache.entries.isEmpty then
none
else
let minEntry := cache.entries.foldl (fun min e =>
if e.accessTime < min.accessTime then e else min
) cache.entries.head!
some minEntry.key
-- ═══════════════════════════════════════════════════════════════════════════
-- §5 Cache Operations
-- ═══════════════════════════════════════════════════════════════════════════
/-- Increment hit count for cache entry -/
def incrementHitCount (entry : MoECacheEntry) : MoECacheEntry :=
{ entry with hitCount := entry.hitCount + 1 }
/-- Compute cache hit rate -/
def computeHitRate (hits total : Nat) : Q16_16 :=
if total = 0 then Q16_16.zero else Q16_16.ofFrac hits total
-- ═══════════════════════════════════════════════════════════════════════════
-- §6 Theorems
-- ═══════════════════════════════════════════════════════════════════════════
/-- Hit count always increases after increment -/
theorem hitCountIncreases (entry : MoECacheEntry) :
(incrementHitCount entry).hitCount = entry.hitCount + 1 := by
simp [incrementHitCount]
/-- LRU cache size is bounded by max size after initialization -/
theorem lruInitBounded (maxSize : Nat) :
(initLRUCache maxSize).entries.length ≤ maxSize := by
simp [initLRUCache]
-- ═══════════════════════════════════════════════════════════════════════════
-- §7 Example Usage
-- ═══════════════════════════════════════════════════════════════════════════
#eval let cache := initLRUCache 10
let cache1 := lruAccess cache "key1"
let cache2 := lruAccess cache1 "key2"
let cache3 := lruAccess cache2 "key1"
getLRUEvictionKey cache3
#eval computeHitRate 85 100
#eval incrementHitCount {
cacheKey := "test",
expertIds := [1, 2, 3],
etaMoEResult := Q16_16.ofFrac 85 100,
iDiscarded := Q16_16.ofFrac 10 100,
timestamp := 12345,
confidence := Q16_16.ofFrac 95 100,
hitCount := 5
}
end Semantics.MoECache