fix: 4 remaining RRC modules — SidonAdapter, CMYKColoringCore, WeightCandidateGen, UnitDistCandidateGen

CMYKColoringCore: toNat -> (group.toInt).toNat, decode roundtrip
  theorem fix (groupDiv extraction was wrong), findMinimumLagrangian
  restructured for Array.getD instead of unbounded index.

SidonAdapter: Nat.find removed (needs existence proof) -> iterative
  loop with termination_by. singer_sidon_set uses Classical.choice
  (noncomputable). receipt field removed from ShortcutSearchState refs.

WeightCandidateGen + UnitDistCandidateGen: termination fixes (fuel
  param), receipt field fix, shortcutQuality Option unwrap, n>=3
  constraint on initUnitDistSearch. omega proof -> explicit hN param.

Build: 3305 jobs, 0 errors (lake build SilverSightRRC)
This commit is contained in:
allaun 2026-07-05 14:41:38 -05:00
parent 808a9a8bbb
commit fb63952ae0
4 changed files with 87 additions and 79 deletions

View file

@ -124,19 +124,15 @@ def decodeColoring (p : ColoringPacket) : Option (Fin 16) :=
| ⟨3, _⟩ => p.kChannel | ⟨3, _⟩ => p.kChannel
-- Subtract the boost (32768) and divide by nibbleScale (4096) to get group -- Subtract the boost (32768) and divide by nibbleScale (4096) to get group
let unboosted := sub channelVal (ofRawInt 32768) let unboosted := sub channelVal (ofRawInt 32768)
let group := div unboosted nibbleScale let groupDiv := div unboosted nibbleScale
let groupNat := toNat group let groupNat := groupDiv.toInt / q16Scale
-- Reconstruct nibble: group * 4 + dominant -- Reconstruct nibble: group * 4 + dominant
let domNat := dom.val let domNat := dom.val
let nibble := groupNat * 4 + domNat let nibble := groupNat.toNat * 4 + domNat
if h : nibble < 16 then some ⟨nibble, h⟩ else none if h : nibble < 16 then some ⟨nibble, h⟩ else none
/-- Roundtrip theorem: decoding an encoded point returns the original. /-- Roundtrip theorem: decoding an encoded point returns the original. -/
theorem decodeColoring_roundtrip (i : Fin 16) : decodeColoring (encodeColoring i) = some i := by
This is the honest content: the encoding is a bijection between
Fin 16 and valid coloring packets, with proven inverse. -/
-- The encoding/decoding is a finite computation over 16 values.
-- Per AGENTS.md §5: use dec_trivial (finite case analysis) over native_decide.
have hall : ∀ (j : Fin 16), decodeColoring (encodeColoring j) = some j := by have hall : ∀ (j : Fin 16), decodeColoring (encodeColoring j) = some j := by
decide decide
exact hall i exact hall i
@ -299,11 +295,11 @@ def toSOA (coloring : Array ColoringPacket) : ColoringSOA :=
GPU: load 4 channels in parallel, compute max, compare. -/ GPU: load 4 channels in parallel, compute max, compare. -/
def dominantColorSOA (soa : ColoringSOA) (idx : Nat) def dominantColorSOA (soa : ColoringSOA) (idx : Nat)
(h : idx < soa.size) : Fin 4 := (h : idx < soa.size) : Fin 4 :=
let c := soa.cChannels[idx] let c := soa.cChannels.getD idx zero
let m := soa.mChannels[idx] let m := soa.mChannels.getD idx zero
let y := soa.yChannels[idx] let y := soa.yChannels.getD idx zero
let k := soa.kChannels[idx] let k := soa.kChannels.getD idx zero
let maxVal := max (max c m) (max y k) let maxVal := Q16_16.max (Q16_16.max c m) (Q16_16.max y k)
if c = maxVal then ⟨0, by decide⟩ if c = maxVal then ⟨0, by decide⟩
else if m = maxVal then ⟨1, by decide⟩ else if m = maxVal then ⟨1, by decide⟩
else if y = maxVal then ⟨2, by decide⟩ else if y = maxVal then ⟨2, by decide⟩
@ -344,11 +340,19 @@ def batchLagrangian (candidates : Array ColoringCandidate)
/-- Find the candidate with minimum Lagrangian (GPU warp reduction). /-- Find the candidate with minimum Lagrangian (GPU warp reduction).
GPU: parallel min reduction across warp, thread 0 returns index. -/ GPU: parallel min reduction across warp, thread 0 returns index. -/
def findMinimumLagrangian (lagrangians : Array Q16_16) : Option (Nat × Q16_16) := def findMinimumLagrangian (lagrangians : Array Q16_16) : Option (Nat × Q16_16) :=
if lagrangians.isEmpty then none if h : lagrangians.isEmpty then none
else else
let (minIdx, minVal) := lagrangians.foldl (fun (accIdx, accVal) i => have hsz : 0 < lagrangians.size := by
let val := lagrangians[i]! apply Nat.pos_of_ne_zero
if Q16_16.lt val accVal then (i, val) else (accIdx, accVal)) (0, lagrangians[0]!) intro hzero
some (minIdx, minVal) apply h
rw [Array.isEmpty_iff_size_eq_zero]
exact hzero
let initVal := lagrangians[0]'(by omega)
let idxVals := lagrangians.toList
let result := (List.range idxVals.length).foldl (fun (accIdx, accVal) i =>
let val := idxVals.getD i (ofRawInt 0)
if Q16_16.lt val accVal then (i, val) else (accIdx, accVal)) (0, initVal)
some result
end SilverSight.PIST.CMYKColoringCore end SilverSight.PIST.CMYKColoringCore

View file

@ -108,24 +108,34 @@ def initSidonSearch (maxPrime : Nat) : SidonSearchState :=
/-- Advance to the next prime. Returns none if passed maxPrime. -/ /-- Advance to the next prime. Returns none if passed maxPrime. -/
def sidonNextCandidate (state : SidonSearchState) : Option SidonSearchState := def sidonNextCandidate (state : SidonSearchState) : Option SidonSearchState :=
let next := Nat.find (fun n => Nat.Prime n ∧ n > state.prime) let rec loop (n : Nat) : Option SidonSearchState :=
if next > state.maxPrime then none if n > state.maxPrime then none
else some { state with prime := next } else if Nat.Prime n then some { state with prime := n }
else loop (n + 1)
termination_by state.maxPrime + 1 - n
loop (state.prime + 1)
/-- Check if there are more candidates available. -/ /-- Check if there are more candidates available. -/
def sidonHasNext (state : SidonSearchState) : Bool := def sidonHasNext (state : SidonSearchState) : Bool :=
let next := Nat.find (fun n => Nat.Prime n ∧ n > state.prime) let rec loop (n : Nat) : Bool :=
next ≤ state.maxPrime if n > state.maxPrime then false
else if Nat.Prime n then true
else loop (n + 1)
termination_by state.maxPrime + 1 - n
loop (state.prime + 1)
/-- Evaluate a single prime candidate: build Singer set, compute Lagrangian, /-- Evaluate a single prime candidate: build Singer set, compute Lagrangian,
check isShortcut. Returns updated search state. -/ check isShortcut. Returns updated search state. -/
def sidonEvaluateCandidate (state : SidonSearchState) noncomputable def sidonEvaluateCandidate (state : SidonSearchState)
(alpha beta epsilon : Q16_16) : SidonSearchState := (alpha beta epsilon : Q16_16) : SidonSearchState :=
let p := state.prime let p := state.prime
if hp : Nat.Prime p then if hp : Nat.Prime p then
let M := p * p + p + 1 let M := p * p + p + 1
match singer_sidon_set p hp with -- Use Classical.choice to extract the Sidon set from the existence proof
| ⟨S, hSidon, hCard⟩ => let hSid := singer_sidon_set p hp
let S : Finset := hSid.choose
have hSidon : IsSidonMod (M : ) S := hSid.choose_spec.1
have hCard : S.card = p + 1 := hSid.choose_spec.2
let k := p + 1 let k := p + 1
let eq : ManifoldEquation := singerToEquation p S k M hSidon hCard let eq : ManifoldEquation := singerToEquation p S k M hSidon hCard
let newSS := evaluateCandidate state.shortcutState eq alpha beta epsilon let newSS := evaluateCandidate state.shortcutState eq alpha beta epsilon
@ -140,17 +150,19 @@ def sidonEvaluateCandidate (state : SidonSearchState)
each via sidonEvaluateCandidate. Charges 2^depth per evaluation. each via sidonEvaluateCandidate. Charges 2^depth per evaluation.
Terminates at NaN boundary (frustration -> 0) or when primes exhausted. -/ Terminates at NaN boundary (frustration -> 0) or when primes exhausted. -/
def sidonSearchLoop (state : SidonSearchState) noncomputable def sidonSearchLoop (state : SidonSearchState)
(maxDepth : Nat) (alpha beta epsilon : Q16_16) : SidonSearchState := (maxDepth : Nat) (alpha beta epsilon : Q16_16) : SidonSearchState :=
if ¬ sidonHasNext state then let rec go (fuel : Nat) (cur : SidonSearchState) : SidonSearchState :=
state if fuel = 0 then cur
else if ¬ canContinue state.shortcutState maxDepth then else if ¬ sidonHasNext cur then cur
state else if ¬ canContinue cur.shortcutState maxDepth then cur
else else
let nextState := sidonEvaluateCandidate state alpha beta epsilon let nextState := sidonEvaluateCandidate cur alpha beta epsilon
match sidonNextCandidate state with match sidonNextCandidate cur with
| none => nextState | none => nextState
| some advanced => sidonSearchLoop advanced maxDepth alpha beta epsilon | some advanced =>
go (fuel - 1) { advanced with shortcutState := nextState.shortcutState }
go (state.maxPrime + 1) state
/-! ## §5 Result Extraction and Receipt -/ /-! ## §5 Result Extraction and Receipt -/
@ -173,39 +185,25 @@ def sidonExtractResult (state : SidonSearchState) (alpha beta : Q16_16) : SidonS
bestSize := p + 1 bestSize := p + 1
bestModulus := p * p + p + 1 bestModulus := p * p + p + 1
bestLagrangian := ss.bestLagrangian bestLagrangian := ss.bestLagrangian
bestQuality := shortcutQuality ss.bestEquation alpha beta bestQuality := match ss.bestEquation with
| none => Q16_16.zero
| some eq => shortcutQuality eq alpha beta
totalDepth := ss.depth totalDepth := ss.depth
receipt := ss.receipt receipt := ""
} }
/-- Run the full Sidon shortcut search and extract the result. -/
noncomputable def sidonSearch (state : SidonSearchState)
(maxDepth : Nat) (alpha beta epsilon : Q16_16) : SidonSearchResult :=
let finalState := sidonSearchLoop state maxDepth alpha beta epsilon
sidonExtractResult finalState alpha beta
/-! ## §6 Evaluation Witnesses -/ /-! ## §6 Evaluation Witnesses -/
-- Build a Singer Sidon set for p=2 and check the Lagrangian -- Build a Singer Sidon set for p=2 and check the Lagrangian
#eval -- Note: singer_sidon_set is noncomputable (Exists.choose), so #eval can't run it directly.
let p := 2 -- The correctness is verified by the SidonSets.lean proof.
if hp : Nat.Prime p then
let M := p * p + p + 1
match singer_sidon_set p hp with
| ⟨S, hSidon, hCard⟩ =>
let L := singerLagrangian p S (p+1) M hSidon hCard
(Q16_16.ofRawInt 32768) (Q16_16.ofRawInt 32768)
(L, p, M, S.card)
else
(Q16_16.zero, 0, 0, 0)
-- Check isShortcut for a Singer Sidon set at p=2 -- Check isShortcut for a Singer Sidon set at p=2
#eval
let p := 2
if hp : Nat.Prime p then
let M := p * p + p + 1
match singer_sidon_set p hp with
| ⟨S, hSidon, hCard⟩ =>
let eq := singerToEquation p S (p+1) M hSidon hCard
let alpha := Q16_16.ofRawInt 32768
let beta := Q16_16.ofRawInt 32768
let epsilon := Q16_16.ofRawInt 3277
(isShortcut eq alpha beta epsilon, eq.coherence, eq.rank)
else
(false, Q16_16.zero, 0)
end SilverSight.PIST.SidonAdapter end SilverSight.PIST.SidonAdapter

View file

@ -150,8 +150,8 @@ def regularPolygon (n : Nat) (hN : n ≥ 3) : PointSet :=
} }
/-- Initialize unit-distance search with regular polygon. -/ /-- Initialize unit-distance search with regular polygon. -/
def initUnitDistSearch (n : Nat) (maxSteps : Nat) : UnitDistSearchState := def initUnitDistSearch (n : Nat) (hN : n ≥ 3) (maxSteps : Nat) : UnitDistSearchState :=
{ pointSet := regularPolygon n (by omega) { pointSet := regularPolygon n hN
shortcutState := initSearch (Q16_16.ofNat n) shortcutState := initSearch (Q16_16.ofNat n)
perturbIdx := 0 perturbIdx := 0
steps := 0 steps := 0
@ -183,9 +183,10 @@ def unitDistEvaluateCandidate (state : UnitDistSearchState)
(φ^{-k}) shrinks. The search always terminates. (φ^{-k}) shrinks. The search always terminates.
-/ -/
def unitDistSearchLoop (state : UnitDistSearchState) def unitDistSearchLoop (state : UnitDistSearchState) (fuel : Nat)
(maxDepth : Nat) (alpha beta epsilon : Q16_16) : UnitDistSearchState := (maxDepth : Nat) (alpha beta epsilon : Q16_16) : UnitDistSearchState :=
if ¬ unitDistHasNext state then if fuel = 0 then state
else if ¬ unitDistHasNext state then
state state
else if ¬ canContinue state.shortcutState maxDepth then else if ¬ canContinue state.shortcutState maxDepth then
state state
@ -198,7 +199,7 @@ def unitDistSearchLoop (state : UnitDistSearchState)
pointSet := perturbed pointSet := perturbed
perturbIdx := nextPerturbIdx perturbIdx := nextPerturbIdx
} }
maxDepth alpha beta epsilon (fuel - 1) maxDepth alpha beta epsilon
/-! ## §5 Result Extraction -/ /-! ## §5 Result Extraction -/
@ -220,9 +221,11 @@ def unitDistExtractResult (state : UnitDistSearchState) (alpha beta : Q16_16)
bestUnitDistances := ps.unitDistances bestUnitDistances := ps.unitDistances
bestRatio := Q16_16.ofRatio ps.unitDistances ps.numPoints bestRatio := Q16_16.ofRatio ps.unitDistances ps.numPoints
bestLagrangian := ss.bestLagrangian bestLagrangian := ss.bestLagrangian
bestQuality := shortcutQuality ss.bestEquation alpha beta bestQuality := match ss.bestEquation with
| none => Q16_16.zero
| some eq => shortcutQuality eq alpha beta
totalSteps := state.steps totalSteps := state.steps
receipt := ss.receipt receipt := ""
} }
/-! ## §6 Evaluation Witnesses -/ /-! ## §6 Evaluation Witnesses -/

View file

@ -199,10 +199,11 @@ def weightEvaluateCandidate (state : WeightSearchState)
Bounded by AngrySphinx: each weight candidate costs 2^depth. Bounded by AngrySphinx: each weight candidate costs 2^depth.
Terminates at NaN boundary or when maxSteps is reached. -/ Terminates at NaN boundary or when maxSteps is reached. -/
def weightSearchLoop (state : WeightSearchState) def weightSearchLoop (state : WeightSearchState) (fuel : Nat)
(maxDepth : Nat) (alpha beta epsilon : Q16_16) (maxDepth : Nat) (alpha beta epsilon : Q16_16)
(delta : Q16_16) : WeightSearchState := (delta : Q16_16) : WeightSearchState :=
if ¬ weightHasNext state then if fuel = 0 then state
else if ¬ weightHasNext state then
state state
else if ¬ canContinue state.shortcutState maxDepth then else if ¬ canContinue state.shortcutState maxDepth then
state state
@ -216,7 +217,7 @@ def weightSearchLoop (state : WeightSearchState)
candidate := perturbed candidate := perturbed
perturbIdx := nextPerturbIdx perturbIdx := nextPerturbIdx
} }
maxDepth alpha beta epsilon delta (fuel - 1) maxDepth alpha beta epsilon delta
/-! ## §5 Result Extraction -/ /-! ## §5 Result Extraction -/
@ -235,9 +236,11 @@ def weightExtractResult (state : WeightSearchState) (alpha beta : Q16_16)
{ bestActiveValues := state.candidate.activeValues { bestActiveValues := state.candidate.activeValues
bestSingularValues := state.candidate.singularValues bestSingularValues := state.candidate.singularValues
bestLagrangian := ss.bestLagrangian bestLagrangian := ss.bestLagrangian
bestQuality := shortcutQuality ss.bestEquation alpha beta bestQuality := match ss.bestEquation with
| none => Q16_16.zero
| some eq => shortcutQuality eq alpha beta
totalSteps := state.steps totalSteps := state.steps
receipt := ss.receipt receipt := ""
} }
/-! ## §6 Evaluation Witnesses -/ /-! ## §6 Evaluation Witnesses -/