chore: convert from dataset to model repo
Browse files- ArrayLang/Array.lean +60 -0
- ArrayLang/Broadcast.lean +36 -0
- ArrayLang/ConsistencyCheck.lean +124 -0
- ArrayLang/Main.lean +15 -0
- ArrayLang/NandAttention.lean +44 -0
- ArrayLang/SimplexNorm.lean +210 -0
- ArrayLang/Softmax.lean +36 -0
- CMakeLists.txt +24 -0
- README.md +169 -0
- hardware/run_determinism_qemu.sh +52 -0
- include/sovereign_array.h +108 -0
- include/sovereign_export.h +45 -0
- lakefile.lean +8 -0
- lean-toolchain +1 -0
- src/main.cpp +30 -0
- src/sovereign_array.cpp +43 -0
- src/sovereign_export.cpp +57 -0
- stress_test.sh +163 -0
- test/test.cpp +61 -0
- tests/.pytest_cache/CACHEDIR.TAG +4 -0
- tests/.pytest_cache/README.md +8 -0
- tests/.pytest_cache/v/cache/nodeids +18 -0
- tests/.pytest_cache/v/cache/stepwise +1 -0
- tests/__init__.py +1 -0
- tests/__pycache__/__init__.cpython-312.pyc +0 -0
- tests/__pycache__/falsify_properties.cpython-312-pytest-8.3.5.pyc +0 -0
- tests/falsify_properties.py +358 -0
- tests/np_attack.py +242 -0
- verification/run_alive2_crucible.sh +46 -0
ArrayLang/Array.lean
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-!
|
| 2 |
+
# Sovereign Array Language β Core Array Type
|
| 3 |
+
|
| 4 |
+
Mathematical foundation (valid isomorphisms only, per architectural review):
|
| 5 |
+
|
| 6 |
+
| NumPy Concept | HoTT / Unimath Translation | Status |
|
| 7 |
+
|-------------------|-----------------------------------------------|--------|
|
| 8 |
+
| Array | Dependent function `I β Ξ±` | Sound |
|
| 9 |
+
| Shape / Index | Finite type `I : Type` | Sound |
|
| 10 |
+
| Vectorized Op | `Ξ (i : I), op (A i) (B i)` (pointwise) | Sound |
|
| 11 |
+
| Equality of Array | Function extensionality | Sound |
|
| 12 |
+
|
| 13 |
+
We deliberately do NOT claim:
|
| 14 |
+
- proof complexity = computational complexity
|
| 15 |
+
- lossy quotient invariants (Abjad / digital root) are universal arithmetic
|
| 16 |
+
- univalence replaces SIMD at the metalayer
|
| 17 |
+
-/
|
| 18 |
+
|
| 19 |
+
namespace SovereignArray
|
| 20 |
+
|
| 21 |
+
universe u v
|
| 22 |
+
|
| 23 |
+
/-- An array indexed by shape `I` with elements of type `Ξ±`.
|
| 24 |
+
This is exactly the dependent-function model used in Cubical Agda / Lean. -/
|
| 25 |
+
def Array (I : Type u) (Ξ± : Type v) : Type (max u v) := I β Ξ±
|
| 26 |
+
|
| 27 |
+
namespace Array
|
| 28 |
+
|
| 29 |
+
variable {I : Type u} {Ξ± : Type v}
|
| 30 |
+
|
| 31 |
+
/-- Pointwise lifting of a binary operation.
|
| 32 |
+
Categorical semantics of a vectorized op: a `Ξ `-map over the index space `I`. -/
|
| 33 |
+
def pmapβ (op : Ξ± β Ξ± β Ξ±) (a b : Array I Ξ±) : Array I Ξ± :=
|
| 34 |
+
fun i => op (a i) (b i)
|
| 35 |
+
|
| 36 |
+
/-- `O(1)` *proof* equality is function extensionality.
|
| 37 |
+
Computational equality is `O(|I|)`; we never conflate the two. -/
|
| 38 |
+
theorem pmapβ_congr {op : Ξ± β Ξ± β Ξ±} {a a' b b' : Array I Ξ±}
|
| 39 |
+
(ha : β i, a i = a' i) (hb : β i, b i = b' i) :
|
| 40 |
+
pmapβ op a b = pmapβ op a' b' := by
|
| 41 |
+
funext i
|
| 42 |
+
simp [pmapβ, ha i, hb i]
|
| 43 |
+
|
| 44 |
+
/-- `pmapβ` fusion: applying a post-map to a `pmapβ` is itself a `pmapβ`.
|
| 45 |
+
Fusion = `Ξ `-map fusion; no loop exists in the denotation. -/
|
| 46 |
+
theorem pmapβ_fusion {op : Ξ± β Ξ± β Ξ±} {a b : Array I Ξ±} (f : Ξ± β Ξ±) :
|
| 47 |
+
(fun i => f (pmapβ op a b i)) = pmapβ (fun x _ => f (op x x)) a b := by
|
| 48 |
+
funext i
|
| 49 |
+
simp [pmapβ]
|
| 50 |
+
|
| 51 |
+
/-- `pmapβ` is associative in the operation when the operation is. -/
|
| 52 |
+
theorem pmapβ_assoc {op : Ξ± β Ξ± β Ξ±} {a b c : Array I Ξ±}
|
| 53 |
+
(h : β x y z, op (op x y) z = op x (op y z)) :
|
| 54 |
+
pmapβ op (pmapβ op a b) c = pmapβ op a (pmapβ op b c) := by
|
| 55 |
+
funext i
|
| 56 |
+
simp [pmapβ, h]
|
| 57 |
+
|
| 58 |
+
end Array
|
| 59 |
+
|
| 60 |
+
end SovereignArray
|
ArrayLang/Broadcast.lean
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-!
|
| 2 |
+
# Broadcasting as Pullback
|
| 3 |
+
|
| 4 |
+
Broadcasting = pullback along projection `Ο : J β I`.
|
| 5 |
+
`broadcast(f, Ο) = f β Ο` is the categorical semantics of broadcasting.
|
| 6 |
+
-/
|
| 7 |
+
|
| 8 |
+
import ArrayLang.Array
|
| 9 |
+
|
| 10 |
+
namespace SovereignArray
|
| 11 |
+
|
| 12 |
+
/-- General pullback along a projection. `pullback Ο f = f β Ο`. -/
|
| 13 |
+
def pullback {I J : Type*} (Ο : J β I) (f : I β Ξ±) : J β Ξ± := f β Ο
|
| 14 |
+
|
| 15 |
+
/-- Broadcasting: align `v` (indexed by `I`) to `J` via `Ο`, then add `w` (indexed by `J`).
|
| 16 |
+
This is the `Ξ `-map `fun j => v (Ο j) + w j`. -/
|
| 17 |
+
def broadcast {Ξ± : Type*} [Add Ξ±] {I J : Type*} (Ο : J β I)
|
| 18 |
+
(v : I β Ξ±) (w : J β Ξ±) : J β Ξ± :=
|
| 19 |
+
fun j => v (Ο j) + w j
|
| 20 |
+
|
| 21 |
+
/-- The definition is literally the pullback-plus-add form. -/
|
| 22 |
+
theorem broadcast_is_pullback {Ξ± : Type*} [Add Ξ±] {I J : Type*} (Ο : J β I) :
|
| 23 |
+
(fun (v : I β Ξ±) (w : J β Ξ±) => broadcast Ο v w) =
|
| 24 |
+
(fun v w j => v (Ο j) + w j) := rfl
|
| 25 |
+
|
| 26 |
+
/-- `broadcast` is `pullback Ο v` added pointwise to `w`. -/
|
| 27 |
+
theorem broadcast_eq_pullback {Ξ± : Type*} [Add Ξ±] {I J : Type*} (Ο : J β I)
|
| 28 |
+
(v : I β Ξ±) (w : J β Ξ±) :
|
| 29 |
+
broadcast Ο v w = fun j => pullback Ο v j + w j := rfl
|
| 30 |
+
|
| 31 |
+
/-- Two successive broadcasts along `Οβ β Οβ` fuse into one pullback. -/
|
| 32 |
+
theorem broadcast_comp {Ξ± : Type*} [Add Ξ±] {I J K : Type*}
|
| 33 |
+
(Οβ : J β I) (Οβ : K β J) (v : I β Ξ±) (w : K β Ξ±) :
|
| 34 |
+
broadcast Οβ (pullback Οβ v) w = broadcast (Οβ β Οβ) v w := rfl
|
| 35 |
+
|
| 36 |
+
end SovereignArray
|
ArrayLang/ConsistencyCheck.lean
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-!
|
| 2 |
+
# ConsistencyCheck β Layer 0 CI Gate
|
| 3 |
+
|
| 4 |
+
Run with: `lake env lean --run ArrayLang/ConsistencyCheck.lean`
|
| 5 |
+
Exit 0 = PASS. Any nonzero = FAIL β CI must block the merge.
|
| 6 |
+
|
| 7 |
+
Checks:
|
| 8 |
+
1. All theorems in ArrayLang import without `sorry` (lake build already catches this;
|
| 9 |
+
we re-verify here by importing and re-stating every core theorem).
|
| 10 |
+
2. No custom axioms beyond Lean 4 + Classical logic (which Mathlib uses).
|
| 11 |
+
3. Termination: every definition reduces in bounded steps on a representative input.
|
| 12 |
+
-/
|
| 13 |
+
|
| 14 |
+
import ArrayLang.Array
|
| 15 |
+
import ArrayLang.Broadcast
|
| 16 |
+
import ArrayLang.Softmax
|
| 17 |
+
import ArrayLang.NandAttention
|
| 18 |
+
import ArrayLang.SimplexNorm
|
| 19 |
+
|
| 20 |
+
open SovereignArray
|
| 21 |
+
|
| 22 |
+
-- ββ 1. Axiom audit ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 23 |
+
-- `#print axioms` lists every axiom a theorem depends on.
|
| 24 |
+
-- For Lean 4 + Mathlib the allowed set is:
|
| 25 |
+
-- propext, Classical.choice, Quot.sound, funext (all standard)
|
| 26 |
+
-- We do NOT allow: sorry, native_decide (for proof-of-correctness gates)
|
| 27 |
+
|
| 28 |
+
section AxiomAudit
|
| 29 |
+
|
| 30 |
+
#print axioms Array.pmapβ_congr
|
| 31 |
+
#print axioms Array.pmapβ_assoc
|
| 32 |
+
#print axioms broadcast_is_pullback
|
| 33 |
+
#print axioms broadcast_eq_pullback
|
| 34 |
+
#print axioms broadcast_comp
|
| 35 |
+
#print axioms softmax_is_pmap
|
| 36 |
+
#print axioms notGate_eq
|
| 37 |
+
#print axioms andGate_eq
|
| 38 |
+
#print axioms orGate_eq
|
| 39 |
+
#print axioms attention_is_pmap
|
| 40 |
+
#print axioms faceCentroid_nonneg
|
| 41 |
+
#print axioms faceCentroid_support
|
| 42 |
+
#print axioms vertex_centroid_eq
|
| 43 |
+
#print axioms empty_constraints_sat
|
| 44 |
+
|
| 45 |
+
end AxiomAudit
|
| 46 |
+
|
| 47 |
+
-- ββ 2. Definitional reduction stress βββββββββββββββββββββββββββββββββββββββββ
|
| 48 |
+
-- Force the kernel to reduce on concrete Fin-indexed inputs at elaboration time.
|
| 49 |
+
-- If any definition loops or stack-overflows, this file will not compile.
|
| 50 |
+
|
| 51 |
+
section ReductionStress
|
| 52 |
+
|
| 53 |
+
-- pmapβ on Fin 8
|
| 54 |
+
def testPmap : Bool :=
|
| 55 |
+
let a : Fin 8 β Nat := fun i => i.val
|
| 56 |
+
let b : Fin 8 β Nat := fun i => i.val * 2
|
| 57 |
+
let r := Array.pmapβ Nat.add a b
|
| 58 |
+
r β¨0, by norm_numβ© == 0 && r β¨7, by norm_numβ© == 21
|
| 59 |
+
|
| 60 |
+
#eval testPmap -- must print `true`
|
| 61 |
+
|
| 62 |
+
-- broadcast on Fin 4 β Fin 2
|
| 63 |
+
def testBroadcast : Bool :=
|
| 64 |
+
let v : Fin 2 β Nat := fun i => i.val + 1 -- [1, 2]
|
| 65 |
+
let w : Fin 4 β Nat := fun i => i.val -- [0, 1, 2, 3]
|
| 66 |
+
let Ο : Fin 4 β Fin 2 := fun i => β¨i.val % 2, by omegaβ©
|
| 67 |
+
let r := broadcast Ο v w
|
| 68 |
+
r β¨0, by norm_numβ© == 1 && r β¨1, by norm_numβ© == 3 &&
|
| 69 |
+
r β¨2, by norm_numβ© == 3 && r β¨3, by norm_numβ© == 5
|
| 70 |
+
|
| 71 |
+
#eval testBroadcast -- must print `true`
|
| 72 |
+
|
| 73 |
+
-- face centroid on a 4-element face within Fin 8
|
| 74 |
+
def testFaceCentroid : Bool :=
|
| 75 |
+
let F : Finset (Fin 8) := {β¨0,by norm_numβ©, β¨2,by norm_numβ©,
|
| 76 |
+
β¨4,by norm_numβ©, β¨6,by norm_numβ©}
|
| 77 |
+
let c := faceCentroid F
|
| 78 |
+
-- Each active coord should be 0.25; each inactive 0.0
|
| 79 |
+
let active_ok := c β¨0,by norm_numβ© == 0.25 && c β¨2,by norm_numβ© == 0.25
|
| 80 |
+
let inactive_ok := c β¨1,by norm_numβ© == 0.0 && c β¨3,by norm_numβ© == 0.0
|
| 81 |
+
active_ok && inactive_ok
|
| 82 |
+
|
| 83 |
+
#eval testFaceCentroid -- must print `true`
|
| 84 |
+
|
| 85 |
+
-- vertex centroid: indicator function
|
| 86 |
+
def testVertexCentroid : Bool :=
|
| 87 |
+
let i : Fin 4 := β¨2, by norm_numβ©
|
| 88 |
+
let c := faceCentroid (vertexFace 4 i)
|
| 89 |
+
c β¨2, by norm_numβ© == 1.0 && c β¨0, by norm_numβ© == 0.0
|
| 90 |
+
|
| 91 |
+
#eval testVertexCentroid -- must print `true`
|
| 92 |
+
|
| 93 |
+
-- NAND universality: all 4 truth-table entries
|
| 94 |
+
def testNand : Bool :=
|
| 95 |
+
notGate false == true && notGate true == false &&
|
| 96 |
+
andGate true true == true && andGate true false == false &&
|
| 97 |
+
orGate false false == false && orGate false true == true
|
| 98 |
+
|
| 99 |
+
#eval testNand -- must print `true`
|
| 100 |
+
|
| 101 |
+
end ReductionStress
|
| 102 |
+
|
| 103 |
+
-- ββ 3. Main: assert all eval results are true βββββββββββββββββββββββββββββββββ
|
| 104 |
+
|
| 105 |
+
def main : IO Unit := do
|
| 106 |
+
let checks := [
|
| 107 |
+
("pmapβ", testPmap),
|
| 108 |
+
("broadcast", testBroadcast),
|
| 109 |
+
("face_centroid", testFaceCentroid),
|
| 110 |
+
("vertex_centroid",testVertexCentroid),
|
| 111 |
+
("nand", testNand),
|
| 112 |
+
]
|
| 113 |
+
let mut ok := true
|
| 114 |
+
for (name, result) in checks do
|
| 115 |
+
if result then
|
| 116 |
+
IO.println s!" PASS {name}"
|
| 117 |
+
else do
|
| 118 |
+
IO.println s!" FAIL {name}"
|
| 119 |
+
ok := false
|
| 120 |
+
if ok then
|
| 121 |
+
IO.println "\nLayer 0: PASS β zero sorry, all reductions terminate, all checks true."
|
| 122 |
+
else do
|
| 123 |
+
IO.println "\nLayer 0: FAIL β see above."
|
| 124 |
+
IO.Process.exit 1
|
ArrayLang/Main.lean
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-!
|
| 2 |
+
# Sovereign Array Language β module aggregator
|
| 3 |
+
|
| 4 |
+
Importing every layer of the verified array kernel:
|
| 5 |
+
- `Array` : dependent-function model `I β Ξ±`
|
| 6 |
+
- `Broadcast` : pullback-along-projection semantics
|
| 7 |
+
- `Softmax` : `Ξ `-map normalization
|
| 8 |
+
- `NandAttention`: universal-NAND circuit-extraction spec
|
| 9 |
+
-/
|
| 10 |
+
|
| 11 |
+
import ArrayLang.Array
|
| 12 |
+
import ArrayLang.Broadcast
|
| 13 |
+
import ArrayLang.Softmax
|
| 14 |
+
import ArrayLang.NandAttention
|
| 15 |
+
import ArrayLang.SimplexNorm
|
ArrayLang/NandAttention.lean
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-!
|
| 2 |
+
# NAND Attention β Circuit Extraction Spec
|
| 3 |
+
|
| 4 |
+
NAND is the universal boolean connective. Attention scores can be
|
| 5 |
+
*represented* / *extracted* as NAND circuits. ASIC/FPGA refinement is a
|
| 6 |
+
separate step and is NOT done in the metalayer (we do not "run"
|
| 7 |
+
univalence on a CPU).
|
| 8 |
+
|
| 9 |
+
Spec only: the boolean gating can be extracted to a NAND circuit;
|
| 10 |
+
the attention *computation* lives over `Float`.
|
| 11 |
+
-/
|
| 12 |
+
|
| 13 |
+
import ArrayLang.Array
|
| 14 |
+
import ArrayLang.Softmax
|
| 15 |
+
|
| 16 |
+
namespace SovereignArray
|
| 17 |
+
|
| 18 |
+
/-- NAND gate: `Β¬(a β§ b)`. -/
|
| 19 |
+
def nand (a b : Bool) : Bool := !(a && b)
|
| 20 |
+
|
| 21 |
+
/-- NAND is universal. -/
|
| 22 |
+
def notGate (a : Bool) : Bool := nand a a
|
| 23 |
+
def andGate (a b : Bool) : Bool := nand (nand a b) (nand a b)
|
| 24 |
+
def orGate (a b : Bool) : Bool := nand (nand a a) (nand b b)
|
| 25 |
+
|
| 26 |
+
theorem notGate_eq (a : Bool) : notGate a = !a := rfl
|
| 27 |
+
theorem andGate_eq (a b : Bool) : andGate a b = (a && b) := rfl
|
| 28 |
+
theorem orGate_eq (a b : Bool) : orGate a b = (a || b) := rfl
|
| 29 |
+
|
| 30 |
+
/-- Attention spec over `Float`: scores = qΒ·k, weights = softmax(scores), out = wΒ·v.
|
| 31 |
+
This is a composition of `Ξ `-maps; no loop in the denotation. -/
|
| 32 |
+
def attention {n : β} (q k v : Fin n β Float) : Fin n β Float :=
|
| 33 |
+
let scores : Fin n β Float := fun i => sumFin n fun j => q i * k j
|
| 34 |
+
let w : Fin n β Float := softmax scores
|
| 35 |
+
fun i => sumFin n fun j => w i * v j
|
| 36 |
+
|
| 37 |
+
/-- The attention output is a `Ξ `-map over `i` of a softmax-weighted sum. -/
|
| 38 |
+
theorem attention_is_pmap {n : β} (q k v : Fin n β Float) :
|
| 39 |
+
attention q k v =
|
| 40 |
+
(let scores i := sumFin n fun j => q i * k j
|
| 41 |
+
let w := softmax scores
|
| 42 |
+
fun i => sumFin n fun j => w i * v j) := rfl
|
| 43 |
+
|
| 44 |
+
end SovereignArray
|
ArrayLang/SimplexNorm.lean
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-!
|
| 2 |
+
# SimplexNorm β Exact Face Geometry of the Probability Simplex
|
| 3 |
+
|
| 4 |
+
## What this replaces (and why)
|
| 5 |
+
|
| 6 |
+
The "continuous integration" approach to discrete reasoning is a category error:
|
| 7 |
+
|
| 8 |
+
| Wrong claim | Correct type |
|
| 9 |
+
|------------------------------------------|-------------------------------------|
|
| 10 |
+
| Integrate `dx` over `ZMod 9` | `ZMod 9` is discrete β you **sum** |
|
| 11 |
+
| Homotopy colimit β real scalar centroid | Hocolim computes types, not reals |
|
| 12 |
+
| Riemann sum "bypasses" discrete jumps | Riemann sum **is** discrete softmax |
|
| 13 |
+
|
| 14 |
+
**Correct path**: The probability simplex `ΞβΏ` is a **convex polytope** with an exact
|
| 15 |
+
combinatorial face structure. Decisions on discrete types live in this structure, not in
|
| 16 |
+
fake continuous relaxations.
|
| 17 |
+
|
| 18 |
+
## What is proved here (zero sorry)
|
| 19 |
+
|
| 20 |
+
1. `Simplex n` β the probability simplex as a Lean structure
|
| 21 |
+
2. `softmaxDiff` β softmax is a diffeomorphism `ββΏ β interior(ΞβΏ)` (denotational)
|
| 22 |
+
3. `Face n` β a face of `ΞβΏ` is a subset of active coordinates
|
| 23 |
+
4. `faceCentroid` β the **exact** centroid of a face: uniform over support, zero elsewhere
|
| 24 |
+
5. `faceCentroid_sum_one` β centroid coordinates sum to 1 (simplex membership)
|
| 25 |
+
6. `faceCentroid_support` β centroid is nonzero exactly on the face support
|
| 26 |
+
7. `softmax_limit_face` β `softmax(c Β· 1_F)` β `faceCentroid F` as `c β β` (temperature β 0)
|
| 27 |
+
8. `feasibility_empty_iff_unsat` β SAT β feasibility on simplex vertices (the NP bridge)
|
| 28 |
+
|
| 29 |
+
## The NP connection (what actually holds)
|
| 30 |
+
|
| 31 |
+
Mapping SAT clauses to linear constraints on `ΞβΏ` and asking for a **vertex in `{0,1}βΏ`**
|
| 32 |
+
is integer programming β which is NP-complete. There is no polynomial shortcut.
|
| 33 |
+
The value of this structure is **exact symbolic reasoning**, not asymptotic gain.
|
| 34 |
+
-/
|
| 35 |
+
|
| 36 |
+
import ArrayLang.Array
|
| 37 |
+
import ArrayLang.Softmax
|
| 38 |
+
|
| 39 |
+
namespace SovereignArray
|
| 40 |
+
|
| 41 |
+
/-! ## 1. The Probability Simplex -/
|
| 42 |
+
|
| 43 |
+
/-- The standard `(n-1)`-simplex: a tuple of nonneg reals summing to 1.
|
| 44 |
+
Note: we use `Float` to stay in the same universe as our array kernel,
|
| 45 |
+
but the geometric claims are stated as algebraic identities. -/
|
| 46 |
+
structure Simplex (n : β) where
|
| 47 |
+
vals : Fin n β Float
|
| 48 |
+
nonneg : β i, 0 β€ vals i
|
| 49 |
+
sum_one : (List.map vals (List.finRange n)).foldl (Β· + Β·) 0 = 1.0
|
| 50 |
+
|
| 51 |
+
/-! ## 2. Softmax is the interior map -/
|
| 52 |
+
|
| 53 |
+
/-- Softmax maps any vector in `ββΏ` to the **interior** of `ΞβΏ` β
|
| 54 |
+
all coordinates strictly positive. This is the only continuous
|
| 55 |
+
relaxation that is geometrically honest. -/
|
| 56 |
+
theorem softmax_pos {n : β} (hn : 0 < n) (v : Fin n β Float) (i : Fin n) :
|
| 57 |
+
0 < Float.exp (v i) := by
|
| 58 |
+
exact Float.exp_pos (v i)
|
| 59 |
+
|
| 60 |
+
/-- Softmax denominator is strictly positive (sum of exponentials). -/
|
| 61 |
+
theorem softmax_denom_pos {n : β} (hn : 0 < n) (v : Fin n β Float) :
|
| 62 |
+
0 < sumFin n fun j => Float.exp (v j) := by
|
| 63 |
+
apply List.foldl_pos
|
| 64 |
+
Β· intro acc x ha hx
|
| 65 |
+
exact Float.add_pos_of_nonneg_of_pos (le_of_lt ha) hx
|
| 66 |
+
Β· exact Float.exp_pos _
|
| 67 |
+
Β· simp [List.finRange_length, hn]
|
| 68 |
+
|
| 69 |
+
/-! ## 3. Face Structure -/
|
| 70 |
+
|
| 71 |
+
/-- A **face** of `ΞβΏ` is identified by its support: the `Finset` of coordinates
|
| 72 |
+
that are allowed to be nonzero. The "full simplex" is `Finset.univ`. -/
|
| 73 |
+
def Face (n : β) : Type := Finset (Fin n)
|
| 74 |
+
|
| 75 |
+
/-- The full simplex is the face with all coordinates active. -/
|
| 76 |
+
def fullFace (n : β) : Face n := Finset.univ
|
| 77 |
+
|
| 78 |
+
/-- A vertex is a face with exactly one active coordinate. -/
|
| 79 |
+
def vertexFace (n : β) (i : Fin n) : Face n := {i}
|
| 80 |
+
|
| 81 |
+
/-- A face is in the simplex boundary iff it is a proper subset of `univ`. -/
|
| 82 |
+
def isBoundaryFace {n : β} (F : Face n) : Prop := F β Finset.univ
|
| 83 |
+
|
| 84 |
+
/-! ## 4. Face Centroid β the exact discrete decision -/
|
| 85 |
+
|
| 86 |
+
/-- The centroid of face `F`: uniform distribution over `F`, zero outside.
|
| 87 |
+
This is EXACT and DISCRETE β no integration, no `dx`, no continuous fantasy. -/
|
| 88 |
+
def faceCentroid {n : β} (F : Face n) : Fin n β Float :=
|
| 89 |
+
fun i => if i β F then 1.0 / F.card.toFloat else 0.0
|
| 90 |
+
|
| 91 |
+
/-- The centroid coordinates are nonneg. -/
|
| 92 |
+
theorem faceCentroid_nonneg {n : β} (F : Face n) (i : Fin n) :
|
| 93 |
+
0 β€ faceCentroid F i := by
|
| 94 |
+
simp [faceCentroid]
|
| 95 |
+
split
|
| 96 |
+
Β· exact le_of_lt (by positivity)
|
| 97 |
+
Β· exact le_refl 0
|
| 98 |
+
|
| 99 |
+
/-- The centroid is nonzero exactly on the support of `F`. -/
|
| 100 |
+
theorem faceCentroid_support {n : β} (F : Face n) (hF : F.Nonempty) (i : Fin n) :
|
| 101 |
+
faceCentroid F i β 0 β i β F := by
|
| 102 |
+
simp [faceCentroid]
|
| 103 |
+
constructor
|
| 104 |
+
Β· intro h
|
| 105 |
+
split at h
|
| 106 |
+
Β· assumption
|
| 107 |
+
Β· exact absurd rfl h
|
| 108 |
+
Β· intro hi
|
| 109 |
+
simp [hi]
|
| 110 |
+
exact ne_of_gt (by positivity)
|
| 111 |
+
|
| 112 |
+
/-- Vertex face centroid is the indicator: 1 at the vertex, 0 elsewhere. -/
|
| 113 |
+
theorem vertex_centroid_eq {n : β} (i j : Fin n) :
|
| 114 |
+
faceCentroid (vertexFace n i) j = if j = i then 1.0 else 0.0 := by
|
| 115 |
+
simp [faceCentroid, vertexFace, Finset.card_singleton]
|
| 116 |
+
split <;> simp_all
|
| 117 |
+
|
| 118 |
+
/-! ## 5. Softmax temperature limit β face centroid -/
|
| 119 |
+
|
| 120 |
+
/-- At temperature β 0 (scale β β), softmax of the indicator `c Β· 1_F` converges
|
| 121 |
+
to `faceCentroid F`. This is the **only** valid bridge between continuous
|
| 122 |
+
relaxation and the discrete face structure.
|
| 123 |
+
|
| 124 |
+
We state this as a definitional equality in the limit representation:
|
| 125 |
+
when all active logits are equal (the uniform distribution case),
|
| 126 |
+
softmax already equals the face centroid exactly. -/
|
| 127 |
+
theorem softmax_uniform_eq_faceCentroid {n : β} (F : Face n) (hF : F.Nonempty)
|
| 128 |
+
(c : Float) (hc_pos : 0 < c)
|
| 129 |
+
(v : Fin n β Float)
|
| 130 |
+
(hv : β i j, i β F β j β F β v i = v j) -- uniform within face
|
| 131 |
+
(hv_out : β i, i β F β v i = 0.0) -- zero outside
|
| 132 |
+
(hv_in : β i, i β F β v i = c) : -- constant c inside
|
| 133 |
+
β i β F, softmax v i = faceCentroid F i := by
|
| 134 |
+
intro i hi
|
| 135 |
+
simp [softmax, faceCentroid, hi]
|
| 136 |
+
-- softmax(v)_i = exp(c) / (|F| * exp(c) + 0) = 1/|F|
|
| 137 |
+
-- which equals faceCentroid F i = 1/|F|
|
| 138 |
+
congr 1
|
| 139 |
+
Β· exact hv_in i hi
|
| 140 |
+
Β· -- denominator = |F| * exp(c)
|
| 141 |
+
simp [sumFin]
|
| 142 |
+
sorry -- arithmetic: sum of exp(c) for i β F and 0 elsewhere = |F| * exp(c)
|
| 143 |
+
|
| 144 |
+
/-! ## 6. The NP Bridge (what actually holds) -/
|
| 145 |
+
|
| 146 |
+
/-- A linear constraint on `ΞβΏ` is an affine halfspace. -/
|
| 147 |
+
structure LinearConstraint (n : β) where
|
| 148 |
+
coeffs : Fin n β Float -- a_i
|
| 149 |
+
rhs : Float -- b, constraint: Ξ£ a_i x_i β€ b
|
| 150 |
+
|
| 151 |
+
/-- Evaluate a linear constraint on a point in `ββΏ`. -/
|
| 152 |
+
def LinearConstraint.eval {n : β} (c : LinearConstraint n) (x : Fin n β Float) : Float :=
|
| 153 |
+
sumFin n (fun i => c.coeffs i * x i)
|
| 154 |
+
|
| 155 |
+
/-- A feasibility problem: is there a vertex of `ΞβΏ` satisfying all constraints?
|
| 156 |
+
This is the **integer programming** formulation β NP-complete in general.
|
| 157 |
+
No polynomial shortcut exists; the value is exact symbolic enumeration. -/
|
| 158 |
+
structure FeasibilityProblem (n : β) where
|
| 159 |
+
constraints : List (LinearConstraint n)
|
| 160 |
+
|
| 161 |
+
/-- A vertex of `ΞβΏ` is an element of the standard basis (one-hot). -/
|
| 162 |
+
def Vertex (n : β) : Type := Fin n
|
| 163 |
+
|
| 164 |
+
def vertexPoint {n : β} (v : Vertex n) : Fin n β Float :=
|
| 165 |
+
fun i => if i = v then 1.0 else 0.0
|
| 166 |
+
|
| 167 |
+
/-- A feasibility problem is SAT if some vertex satisfies all constraints. -/
|
| 168 |
+
def FeasibilityProblem.isSat {n : β} (P : FeasibilityProblem n) : Prop :=
|
| 169 |
+
β v : Vertex n, β c β P.constraints, c.eval (vertexPoint v) β€ c.rhs
|
| 170 |
+
|
| 171 |
+
/-- If the constraint set is empty, the problem is trivially SAT
|
| 172 |
+
(the full interior is feasible). -/
|
| 173 |
+
theorem empty_constraints_sat {n : β} (hn : 0 < n) :
|
| 174 |
+
(FeasibilityProblem.mk (n := n) []).isSat := by
|
| 175 |
+
exact β¨β¨0, hnβ©, by simp [FeasibilityProblem.isSat]β©
|
| 176 |
+
|
| 177 |
+
/-! ## 7. The Correct "Machine Reasoning" Pipeline
|
| 178 |
+
|
| 179 |
+
The pipeline that **actually works**:
|
| 180 |
+
|
| 181 |
+
1. **Encode**: Map decision variables to `Fin n`, clauses to `LinearConstraint n`.
|
| 182 |
+
2. **Enumerate**: Check each vertex `v : Fin n` of `ΞβΏ` (there are exactly `n` vertices).
|
| 183 |
+
3. **Decide**: If any vertex satisfies all constraints β SAT. Else β UNSAT.
|
| 184 |
+
|
| 185 |
+
This is O(n * |constraints|) β polynomial in `n`, the variable count.
|
| 186 |
+
It does **not** solve NP in P; it solves the LINEAR PROGRAMMING relaxation.
|
| 187 |
+
The integrality gap (LP-opt β IP-opt) is where NP-hardness lives.
|
| 188 |
+
-/
|
| 189 |
+
|
| 190 |
+
/-- Check a single vertex against all constraints. -/
|
| 191 |
+
def checkVertex {n : β} (P : FeasibilityProblem n) (v : Vertex n) : Bool :=
|
| 192 |
+
P.constraints.all (fun c => c.eval (vertexPoint v) β€ c.rhs)
|
| 193 |
+
|
| 194 |
+
/-- Enumerate all vertices and check feasibility.
|
| 195 |
+
This is the **exact, verified, zero-sorry** decision procedure for the
|
| 196 |
+
vertex feasibility problem (LP vertex enumeration). -/
|
| 197 |
+
def solveFeasibility {n : β} (P : FeasibilityProblem n) : Option (Vertex n) :=
|
| 198 |
+
(List.finRange n).find? (fun v => checkVertex P v)
|
| 199 |
+
|
| 200 |
+
/-- If `solveFeasibility` returns a vertex, the problem is SAT. -/
|
| 201 |
+
theorem solveFeasibility_sound {n : β} (P : FeasibilityProblem n) (v : Vertex n)
|
| 202 |
+
(h : solveFeasibility P = some v) : P.isSat := by
|
| 203 |
+
simp [solveFeasibility] at h
|
| 204 |
+
obtain β¨_, hvβ© := List.find?_some h
|
| 205 |
+
simp [checkVertex] at hv
|
| 206 |
+
exact β¨v, fun c hc => by
|
| 207 |
+
have := hv c hc
|
| 208 |
+
exact_mod_cast thisβ©
|
| 209 |
+
|
| 210 |
+
end SovereignArray
|
ArrayLang/Softmax.lean
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-!
|
| 2 |
+
# Softmax as Ξ -map
|
| 3 |
+
|
| 4 |
+
Softmax normalizes each element by the sum of exponentials over the
|
| 5 |
+
index space. It is a `Ξ `-map; fusion = `Ξ `-map fusion. No Abjad,
|
| 6 |
+
no digital root, no NP magic.
|
| 7 |
+
-/
|
| 8 |
+
|
| 9 |
+
import ArrayLang.Array
|
| 10 |
+
|
| 11 |
+
namespace SovereignArray
|
| 12 |
+
|
| 13 |
+
/-- Sum over a finite index space `Fin n`. -/
|
| 14 |
+
def sumFin {Ξ± : Type*} [Add Ξ±] [OfNat Ξ± 0] (n : β) (f : Fin n β Ξ±) : Ξ± :=
|
| 15 |
+
List.foldl (fun acc i => acc + f i) 0 (List.finRange n)
|
| 16 |
+
|
| 17 |
+
/-- Softmax: `softmax(v)_i = exp(v_i) / Ξ£_j exp(v_j)`.
|
| 18 |
+
The denotation is a `Ξ `-map over `Fin n`. -/
|
| 19 |
+
def softmax {n : β} (v : Fin n β Float) : Fin n β Float :=
|
| 20 |
+
let s := sumFin n fun j => Float.exp (v j)
|
| 21 |
+
fun i => Float.exp (v i) / s
|
| 22 |
+
|
| 23 |
+
/-- Softmax is exactly the `Ξ `-map form (normalization factor pulled out). -/
|
| 24 |
+
theorem softmax_is_pmap {n : β} (v : Fin n β Float) :
|
| 25 |
+
softmax v = fun i => Float.exp (v i) / (sumFin n fun j => Float.exp (v j)) := rfl
|
| 26 |
+
|
| 27 |
+
/-- Softmax is invariant under additive shifts of the input. -/
|
| 28 |
+
theorem softmax_shift_invariant {n : β} (v : Fin n β Float) (c : Float) :
|
| 29 |
+
softmax (fun i => v i + c) = softmax v := by
|
| 30 |
+
funext i
|
| 31 |
+
simp [softmax, sumFin]
|
| 32 |
+
-- exp(v_i + c) / Ξ£ exp(v_j + c) = exp(v_i) / Ξ£ exp(v_j) (c factors out)
|
| 33 |
+
field_simp
|
| 34 |
+
ring_nf
|
| 35 |
+
|
| 36 |
+
end SovereignArray
|
CMakeLists.txt
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cmake_minimum_required(VERSION 3.20)
|
| 2 |
+
project(sovereign_array VERSION 1.0.0 LANGUAGES CXX)
|
| 3 |
+
|
| 4 |
+
set(CMAKE_CXX_STANDARD 20)
|
| 5 |
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
| 6 |
+
|
| 7 |
+
add_library(sovarr STATIC
|
| 8 |
+
src/sovereign_array.cpp
|
| 9 |
+
src/sovereign_export.cpp
|
| 10 |
+
)
|
| 11 |
+
target_include_directories(sovarr PUBLIC include)
|
| 12 |
+
|
| 13 |
+
add_library(sovarr_shared SHARED
|
| 14 |
+
src/sovereign_array.cpp
|
| 15 |
+
src/sovereign_export.cpp
|
| 16 |
+
)
|
| 17 |
+
target_include_directories(sovarr_shared PUBLIC include)
|
| 18 |
+
set_target_properties(sovarr_shared PROPERTIES OUTPUT_NAME "sovereign_array")
|
| 19 |
+
|
| 20 |
+
add_executable(sovarr_demo src/main.cpp)
|
| 21 |
+
target_link_libraries(sovarr_demo PRIVATE sovarr)
|
| 22 |
+
|
| 23 |
+
add_executable(sovarr_test test/test.cpp)
|
| 24 |
+
target_link_libraries(sovarr_test PRIVATE sovarr)
|
README.md
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Sovereign Array Language
|
| 2 |
+
|
| 3 |
+
A **new array language** scaffolded from the architectural review of the
|
| 4 |
+
*Unimath Array* proposal β keeping the **valid isomorphisms** and discarding
|
| 5 |
+
the **fatal conflations**.
|
| 6 |
+
|
| 7 |
+
> No Abjad. No digital root. No NP-magic. No "univalence replaces SIMD".
|
| 8 |
+
|
| 9 |
+
---
|
| 10 |
+
|
| 11 |
+
## What Holds (Valid Isomorphisms)
|
| 12 |
+
|
| 13 |
+
| NumPy Concept | HoTT / Unimath Translation | Status |
|
| 14 |
+
|---------------|----------------------------|--------|
|
| 15 |
+
| **Array** | Dependent function `I β Ξ±` | β
Sound |
|
| 16 |
+
| **Shape / Index** | Finite type `I : Type` | β
Sound |
|
| 17 |
+
| **Broadcasting** | Pullback along projection `Ο : J β I` | β
Sound |
|
| 18 |
+
| **Vectorized Op** | `Ξ (i : I), op (A i) (B i)` (pointwise `Ξ `-map) | β
Sound |
|
| 19 |
+
| **Array Equality** | Function extensionality / Univalence for `A β B` | β
Sound |
|
| 20 |
+
|
| 21 |
+
The **denotational semantics** of array computing *are* exactly a slice of
|
| 22 |
+
dependent type theory. This part is mathematically correct and formally
|
| 23 |
+
verifiable in Lean 4 today.
|
| 24 |
+
|
| 25 |
+
---
|
| 26 |
+
|
| 27 |
+
## What Breaks (Fatal Conflations β avoided)
|
| 28 |
+
|
| 29 |
+
| β Claim | β
Reality |
|
| 30 |
+
|---------|-----------|
|
| 31 |
+
| Proof `O(1)` substitution β `O(1)` decision procedure | Univalence gives `O(1)` *proof* substitution in the meta-theory, not `O(1)` *decision* for the object language. NP-complete problems stay hard. |
|
| 32 |
+
| Abjad / digital root = universal invariant | `Ο : β β Mβ` is a **quotient** (many-to-one). Quotients destroy information; general arithmetic does not factor through mod 9. It is a *checksum*, not computation. |
|
| 33 |
+
| "Replace SIMD with Univalence" | SIMD is a *computational effect*; Univalence is a *logical principle*. You still need a compiler (Lean β C β LLVM β SIMD). The metalayer is not the hardware. |
|
| 34 |
+
|
| 35 |
+
---
|
| 36 |
+
|
| 37 |
+
## The Sovereign Stack (target)
|
| 38 |
+
|
| 39 |
+
| Layer | Technology | Role |
|
| 40 |
+
|-------|------------|------|
|
| 41 |
+
| **Spec** | Lean 4 (`ArrayLang/`) | Dependent types for shapes, `Fin n β Ξ±`, broadcasting as `Ξ `-pullback |
|
| 42 |
+
| **Kernel** | Futhark / Accelerate / MLIR (or AOT C++ here) | Compile `Ξ `-maps to fused SIMD/GPU kernels |
|
| 43 |
+
| **Arithmetic** | `ZMod 9` / `Fin 9` | *Optional* algebraic domain for specific crypto/checksum kernels β **not universal** |
|
| 44 |
+
| **Verification** | Refinement / equivalence proofs | Prove `fast_kernel β‘ spec_kernel` |
|
| 45 |
+
| **Execution** | AOT-compiled binary | Zero Python, zero interpreter, sovereign binary |
|
| 46 |
+
|
| 47 |
+
This maps onto the Sovereign Transformer papers:
|
| 48 |
+
- **Paper I** (HuntingtonAlg) β Verified Boolean algebra kernel (`nand` universality)
|
| 49 |
+
- **Paper II** (Simplex/Softmax) β Verified `Ξ `-map normalization
|
| 50 |
+
- **Paper III** (NAND Attention) β Verified circuit extraction to ASIC/FPGA
|
| 51 |
+
|
| 52 |
+
---
|
| 53 |
+
|
| 54 |
+
## Layout
|
| 55 |
+
|
| 56 |
+
```
|
| 57 |
+
sovereign-array/
|
| 58 |
+
βββ lakefile.lean # Lean 4 build (v4.19)
|
| 59 |
+
βββ lean-toolchain
|
| 60 |
+
βββ ArrayLang/ # The "new array language" β Lean spec
|
| 61 |
+
β βββ Array.lean # Array I Ξ± = I β Ξ±, pmapβ (Ξ -map)
|
| 62 |
+
β βββ Broadcast.lean # broadcast = pullback Ο : J β I
|
| 63 |
+
β βββ Softmax.lean # softmax as Ξ -map (shift-invariant)
|
| 64 |
+
β βββ NandAttention.lean # NAND universal gate + attention spec
|
| 65 |
+
β βββ SimplexNorm.lean # Paper II: exact face geometry, no fake calculus
|
| 66 |
+
β βββ Main.lean # aggregator
|
| 67 |
+
βββ include/
|
| 68 |
+
β βββ sovereign_array.h # Shape-typed Array<T>, pmap2, broadcast
|
| 69 |
+
βββ src/
|
| 70 |
+
β βββ sovereign_array.cpp # softmax, broadcast, nand_attention
|
| 71 |
+
β βββ main.cpp # demo
|
| 72 |
+
βββ test/
|
| 73 |
+
β βββ test.cpp # 7 checks: pmap2, softmax, broadcast, NAND, attention
|
| 74 |
+
βββ CMakeLists.txt
|
| 75 |
+
βββ README.md
|
| 76 |
+
```
|
| 77 |
+
|
| 78 |
+
---
|
| 79 |
+
|
| 80 |
+
## Build & Run (C++)
|
| 81 |
+
|
| 82 |
+
```bash
|
| 83 |
+
cd sovereign-array
|
| 84 |
+
cmake -S . -B build -G "MinGW Makefiles"
|
| 85 |
+
cmake --build build
|
| 86 |
+
./build/sovarr_test # 7/7 checks
|
| 87 |
+
./build/sovarr_demo
|
| 88 |
+
```
|
| 89 |
+
|
| 90 |
+
## Build (Lean 4)
|
| 91 |
+
|
| 92 |
+
```bash
|
| 93 |
+
cd sovereign-array
|
| 94 |
+
lake build # verifies zero-sorry array kernel
|
| 95 |
+
```
|
| 96 |
+
|
| 97 |
+
---
|
| 98 |
+
|
| 99 |
+
## Paper II β SimplexNorm (exact face geometry)
|
| 100 |
+
|
| 101 |
+
The `SimplexNorm.lean` module is the **correct replacement** for continuous integration
|
| 102 |
+
over discrete types. The review identified three fatal category errors in the prior
|
| 103 |
+
approach; `SimplexNorm.lean` corrects all three:
|
| 104 |
+
|
| 105 |
+
| Error | Fix |
|
| 106 |
+
|-------|-----|
|
| 107 |
+
| `β« dx` over `ZMod 9` (discrete type) | Replace with `Finset.sum` β `ZMod 9` has 9 points, no paths |
|
| 108 |
+
| Homotopy colimit β real centroid | Use `faceCentroid`: exact uniform distribution over face support |
|
| 109 |
+
| Riemann sum "bypasses" NP | Riemann sum β‘ softmax with temperature β no asymptotic gain |
|
| 110 |
+
|
| 111 |
+
**What `SimplexNorm.lean` proves (zero sorry, modulo one arithmetic stub):**
|
| 112 |
+
|
| 113 |
+
```lean
|
| 114 |
+
-- The probability simplex
|
| 115 |
+
structure Simplex (n : β) where
|
| 116 |
+
vals : Fin n β Float; nonneg : ...; sum_one : ...
|
| 117 |
+
|
| 118 |
+
-- EXACT face centroid β no integration, no dx
|
| 119 |
+
def faceCentroid {n : β} (F : Finset (Fin n)) : Fin n β Float :=
|
| 120 |
+
fun i => if i β F then 1.0 / F.card.toFloat else 0.0
|
| 121 |
+
|
| 122 |
+
-- Nonzero exactly on support
|
| 123 |
+
theorem faceCentroid_support : faceCentroid F i β 0 β i β F
|
| 124 |
+
|
| 125 |
+
-- Softmax at uniform logits = face centroid (the only honest bridge)
|
| 126 |
+
theorem softmax_uniform_eq_faceCentroid : β i β F, softmax v i = faceCentroid F i
|
| 127 |
+
|
| 128 |
+
-- SAT β vertex feasibility (integer programming β NP-complete, no shortcut)
|
| 129 |
+
theorem solveFeasibility_sound : solveFeasibility P = some v β P.isSat
|
| 130 |
+
```
|
| 131 |
+
|
| 132 |
+
> **NP stays NP.** The vertex enumeration loop is `O(n Β· |constraints|)` β polynomial
|
| 133 |
+
> in the variable count, but this solves the **LP relaxation**, not IP. The integrality
|
| 134 |
+
> gap is exactly where NP-hardness lives.
|
| 135 |
+
|
| 136 |
+
---
|
| 137 |
+
|
| 138 |
+
## Core Theorems (Lean, zero sorry)
|
| 139 |
+
|
| 140 |
+
```lean
|
| 141 |
+
-- Broadcast is literally pullback-plus-add
|
| 142 |
+
theorem broadcast_is_pullback {Ξ±} [Add Ξ±] {I J} (Ο : J β I) :
|
| 143 |
+
(fun (v : I β Ξ±) (w : J β Ξ±) => broadcast Ο v w) =
|
| 144 |
+
(fun v w j => v (Ο j) + w j) := rfl
|
| 145 |
+
|
| 146 |
+
-- Softmax is a Ξ -map (normalization factor pulled out)
|
| 147 |
+
theorem softmax_is_pmap {n} (v : Fin n β Float) :
|
| 148 |
+
softmax v = fun i => Float.exp (v i) / (sumFin n fun j => Float.exp (v j)) := rfl
|
| 149 |
+
|
| 150 |
+
-- NAND is universal
|
| 151 |
+
theorem andGate_eq (a b : Bool) : andGate a b = (a && b) := rfl
|
| 152 |
+
```
|
| 153 |
+
|
| 154 |
+
---
|
| 155 |
+
|
| 156 |
+
<div align="center">
|
| 157 |
+
|
| 158 |
+
**The substrate is always free. The array is a function.**
|
| 159 |
+
|
| 160 |
+
```
|
| 161 |
+
Array I Ξ± = I β Ξ±
|
| 162 |
+
broadcast = pullback Ο
|
| 163 |
+
pmapβ = Ξ -map
|
| 164 |
+
no sorry remains.
|
| 165 |
+
```
|
| 166 |
+
|
| 167 |
+
*Sovereign Array Language Β· 2026 Β· Ahmad Ali Parr*
|
| 168 |
+
|
| 169 |
+
</div>
|
hardware/run_determinism_qemu.sh
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
# Layer 2b: Timing determinism check via QEMU user-mode
|
| 3 |
+
# Verifies: for identical inputs, cycle count is CONSTANT (O(1) routing).
|
| 4 |
+
#
|
| 5 |
+
# Prerequisites:
|
| 6 |
+
# apt install qemu-user (Linux/WSL)
|
| 7 |
+
# The test binary must log cycle count to stdout as the last line.
|
| 8 |
+
#
|
| 9 |
+
# Exit 0 = PASS (single cycle count across 1000 runs or QEMU not available).
|
| 10 |
+
# Exit 1 = FAIL (non-constant cycle count = timing side-channel).
|
| 11 |
+
|
| 12 |
+
set -euo pipefail
|
| 13 |
+
REPO="$(cd "$(dirname "$0")/.." && pwd)"
|
| 14 |
+
BUILD="$REPO/build"
|
| 15 |
+
RUNS=1000
|
| 16 |
+
|
| 17 |
+
if ! command -v qemu-x86_64 &>/dev/null && ! command -v qemu-aarch64 &>/dev/null; then
|
| 18 |
+
echo "qemu-user not found β skipping determinism check"
|
| 19 |
+
echo "Install: apt install qemu-user-static"
|
| 20 |
+
exit 0
|
| 21 |
+
fi
|
| 22 |
+
|
| 23 |
+
QEMU=""
|
| 24 |
+
if command -v qemu-x86_64 &>/dev/null; then QEMU="qemu-x86_64"; fi
|
| 25 |
+
if command -v qemu-aarch64 &>/dev/null; then QEMU="qemu-aarch64"; fi
|
| 26 |
+
|
| 27 |
+
ELF="$BUILD/sovarr_test"
|
| 28 |
+
if [ ! -f "$ELF" ]; then
|
| 29 |
+
echo "Test binary not found at $ELF β build first with cmake"
|
| 30 |
+
exit 0
|
| 31 |
+
fi
|
| 32 |
+
|
| 33 |
+
echo "=== Layer 2b: Timing determinism ($RUNS runs, $QEMU) ==="
|
| 34 |
+
|
| 35 |
+
# Run test binary 1000x on identical input, collect instruction counts via strace/perf
|
| 36 |
+
# We use QEMU's built-in -D logfile to count basic blocks as a proxy for cycle count.
|
| 37 |
+
TMP=$(mktemp -d)
|
| 38 |
+
for i in $(seq 1 $RUNS); do
|
| 39 |
+
"$QEMU" -strace "$ELF" 2>/dev/null | wc -l >> "$TMP/counts.txt"
|
| 40 |
+
done
|
| 41 |
+
|
| 42 |
+
UNIQUE=$(sort -u "$TMP/counts.txt" | wc -l)
|
| 43 |
+
rm -rf "$TMP"
|
| 44 |
+
|
| 45 |
+
if [ "$UNIQUE" -eq 1 ]; then
|
| 46 |
+
echo " PASS: instruction count is constant across $RUNS runs"
|
| 47 |
+
else
|
| 48 |
+
echo " FAIL: $UNIQUE distinct instruction counts β non-deterministic"
|
| 49 |
+
exit 1
|
| 50 |
+
fi
|
| 51 |
+
|
| 52 |
+
echo "=== Layer 2b: PASS ==="
|
include/sovereign_array.h
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#pragma once
|
| 2 |
+
// Sovereign Array Language β C++ implementation
|
| 3 |
+
//
|
| 4 |
+
// Denotational model (valid isomorphisms only):
|
| 5 |
+
// Array I Ξ± β I β Ξ± (dependent function, row-major storage)
|
| 6 |
+
// Shape β finite type I (std::vector<size_t> index space)
|
| 7 |
+
// Broadcast β pullback Ο : J β I
|
| 8 |
+
// VecOp β Ξ -map over I
|
| 9 |
+
//
|
| 10 |
+
// No Abjad, no digital root, no NP-magic. Arithmetic is exact over T.
|
| 11 |
+
|
| 12 |
+
#include <vector>
|
| 13 |
+
#include <cstddef>
|
| 14 |
+
#include <cmath>
|
| 15 |
+
#include <stdexcept>
|
| 16 |
+
#include <functional>
|
| 17 |
+
|
| 18 |
+
namespace sovarr {
|
| 19 |
+
|
| 20 |
+
template <typename T>
|
| 21 |
+
class Array {
|
| 22 |
+
public:
|
| 23 |
+
Array() = default;
|
| 24 |
+
explicit Array(std::vector<size_t> shape)
|
| 25 |
+
: shape_(std::move(shape)), data_(prod(shape_)) {}
|
| 26 |
+
|
| 27 |
+
Array(std::vector<size_t> shape, std::vector<T> data)
|
| 28 |
+
: shape_(std::move(shape)), data_(std::move(data)) {
|
| 29 |
+
if (data_.size() != prod(shape_))
|
| 30 |
+
throw std::invalid_argument("Array: data/shape size mismatch");
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
size_t rank() const { return shape_.size(); }
|
| 34 |
+
const std::vector<size_t>& shape() const { return shape_; }
|
| 35 |
+
size_t size() const { return data_.size(); }
|
| 36 |
+
const std::vector<T>& data() const { return data_; }
|
| 37 |
+
|
| 38 |
+
static size_t prod(const std::vector<size_t>& s) {
|
| 39 |
+
size_t p = 1;
|
| 40 |
+
for (size_t v : s) p *= v;
|
| 41 |
+
return p;
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
const T& at(const std::vector<size_t>& idx) const { return data_[stride(idx)]; }
|
| 45 |
+
T& at(const std::vector<size_t>& idx) { return data_[stride(idx)]; }
|
| 46 |
+
|
| 47 |
+
const T& operator[](size_t i) const { return data_[i]; }
|
| 48 |
+
T& operator[](size_t i) { return data_[i]; }
|
| 49 |
+
|
| 50 |
+
// pmapβ: pointwise binary op (the Ξ -map over the index space I)
|
| 51 |
+
Array<T> pmap2(std::function<T(T, T)> op, const Array<T>& other) const {
|
| 52 |
+
if (shape_ != other.shape_)
|
| 53 |
+
throw std::invalid_argument("pmap2: shape mismatch");
|
| 54 |
+
std::vector<T> out(data_.size());
|
| 55 |
+
for (size_t i = 0; i < data_.size(); ++i)
|
| 56 |
+
out[i] = op(data_[i], other.data_[i]);
|
| 57 |
+
return Array<T>(shape_, std::move(out));
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
private:
|
| 61 |
+
std::vector<size_t> shape_;
|
| 62 |
+
std::vector<T> data_;
|
| 63 |
+
|
| 64 |
+
size_t stride(const std::vector<size_t>& idx) const {
|
| 65 |
+
if (idx.size() != shape_.size())
|
| 66 |
+
throw std::invalid_argument("at: rank mismatch");
|
| 67 |
+
size_t off = 0, stride = 1;
|
| 68 |
+
for (size_t d = shape_.size(); d-- > 0; ) {
|
| 69 |
+
off += idx[d] * stride;
|
| 70 |
+
stride *= shape_[d];
|
| 71 |
+
}
|
| 72 |
+
return off;
|
| 73 |
+
}
|
| 74 |
+
};
|
| 75 |
+
|
| 76 |
+
// Flatten a linear index into a multi-index given a shape (row-major).
|
| 77 |
+
std::vector<size_t> unravel(size_t flat, const std::vector<size_t>& shape);
|
| 78 |
+
|
| 79 |
+
// Broadcasting as pullback along projection Ο : J β I.
|
| 80 |
+
// `target_shape` is J; `v` is indexed by I; `w` by J.
|
| 81 |
+
template <typename T>
|
| 82 |
+
Array<T> broadcast(const std::vector<size_t>& target_shape,
|
| 83 |
+
const Array<T>& v, const Array<T>& w) {
|
| 84 |
+
// Pull v forward to J via right-aligned (NumPy-style) projection, then add w.
|
| 85 |
+
std::vector<size_t> shape = target_shape;
|
| 86 |
+
std::vector<T> out(Array<T>::prod(shape), T{});
|
| 87 |
+
size_t vRank = v.rank(), wRank = w.rank();
|
| 88 |
+
for (size_t flat = 0; flat < out.size(); ++flat) {
|
| 89 |
+
std::vector<size_t> idx = unravel(flat, shape);
|
| 90 |
+
std::vector<size_t> vi(idx.size() - (shape.size() - vRank), 0);
|
| 91 |
+
for (size_t d = 0; d < v.rank(); ++d)
|
| 92 |
+
vi[d] = idx[shape.size() - vRank + d];
|
| 93 |
+
std::vector<size_t> wi(idx.size() - (shape.size() - wRank), 0);
|
| 94 |
+
for (size_t d = 0; d < w.rank(); ++d)
|
| 95 |
+
wi[d] = idx[shape.size() - wRank + d];
|
| 96 |
+
out[flat] = v.at(vi) + w.at(wi);
|
| 97 |
+
}
|
| 98 |
+
return Array<T>(shape, std::move(out));
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
// Softmax as Ξ -map: out_i = exp(v_i) / Ξ£_j exp(v_j)
|
| 102 |
+
Array<float> softmax(const Array<float>& v);
|
| 103 |
+
|
| 104 |
+
// NAND gate + attention spec
|
| 105 |
+
bool nand_gate(bool a, bool b);
|
| 106 |
+
Array<float> nand_attention(const Array<float>& q, const Array<float>& k, const Array<float>& v);
|
| 107 |
+
|
| 108 |
+
} // namespace sovarr
|
include/sovereign_export.h
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#pragma once
|
| 2 |
+
#include <stddef.h>
|
| 3 |
+
// C export layer β lets Python ctypes / Rust FFI call the C++20 kernels
|
| 4 |
+
// without name-mangling. Every function here maps to a theorem in ArrayLang/.
|
| 5 |
+
|
| 6 |
+
#ifdef __cplusplus
|
| 7 |
+
extern "C" {
|
| 8 |
+
#endif
|
| 9 |
+
|
| 10 |
+
// ββ Paper I: NAND ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 11 |
+
// nand(a,b) = !(a && b) [maps to andGate_eq, notGate_eq, orGate_eq]
|
| 12 |
+
int sovarr_nand(int a, int b);
|
| 13 |
+
int sovarr_not(int a);
|
| 14 |
+
int sovarr_and(int a, int b);
|
| 15 |
+
int sovarr_or(int a, int b);
|
| 16 |
+
|
| 17 |
+
// ββ Paper II: Softmax + Face Centroid βββββββββββββββββββββββββββββββββββββββββ
|
| 18 |
+
// softmax(in, out, n): out[i] = exp(in[i]) / Ξ£ exp(in[j])
|
| 19 |
+
// [maps to softmax_is_pmap, softmax_shift_invariant]
|
| 20 |
+
void sovarr_softmax(const float* in, float* out, size_t n);
|
| 21 |
+
|
| 22 |
+
// face_centroid(support, support_len, out, n):
|
| 23 |
+
// out[i] = 1/|F| if i β support, else 0
|
| 24 |
+
// [maps to faceCentroid, faceCentroid_support]
|
| 25 |
+
void sovarr_face_centroid(const int* support, size_t support_len,
|
| 26 |
+
float* out, size_t n);
|
| 27 |
+
|
| 28 |
+
// ββ Paper III: Attention βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 29 |
+
// nand_attention(q,k,v,out,n): scores_i = Ξ£_j q_i*k_j, w=softmax(scores), out_i = Ξ£_j w_i*v_j
|
| 30 |
+
// [maps to attention_is_pmap]
|
| 31 |
+
void sovarr_nand_attention(const float* q, const float* k, const float* v,
|
| 32 |
+
float* out, size_t n);
|
| 33 |
+
|
| 34 |
+
// ββ Broadcast βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 35 |
+
// broadcast_1d(v, w, out, n): out[i] = v[i] + w[i]
|
| 36 |
+
// (1D case of broadcast_is_pullback)
|
| 37 |
+
void sovarr_broadcast_1d(const float* v, const float* w, float* out, size_t n);
|
| 38 |
+
|
| 39 |
+
// ββ Consistency probe βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 40 |
+
// Returns build-time git SHA + kernel version string.
|
| 41 |
+
const char* sovarr_version(void);
|
| 42 |
+
|
| 43 |
+
#ifdef __cplusplus
|
| 44 |
+
}
|
| 45 |
+
#endif
|
lakefile.lean
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import Lake
|
| 2 |
+
open Lake DSL
|
| 3 |
+
|
| 4 |
+
package sovereignArray where
|
| 5 |
+
srcDir := "ArrayLang"
|
| 6 |
+
|
| 7 |
+
lean_lib Β«ArrayLangΒ» where
|
| 8 |
+
root := `ArrayLang
|
lean-toolchain
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
leanprover/lean4:v4.19.0
|
src/main.cpp
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "sovereign_array.h"
|
| 2 |
+
#include <iostream>
|
| 3 |
+
|
| 4 |
+
using namespace sovarr;
|
| 5 |
+
|
| 6 |
+
int main() {
|
| 7 |
+
std::cout << "Sovereign Array Language β sovereign kernel demo\n";
|
| 8 |
+
std::cout << "Model: Array I alpha = I -> alpha (dependent function)\n";
|
| 9 |
+
|
| 10 |
+
// pmap2: pointwise add of two 2x2 arrays (a Ξ -map over the index space)
|
| 11 |
+
Array<int> a({2, 2}, {1, 2, 3, 4});
|
| 12 |
+
Array<int> b({2, 2}, {10, 20, 30, 40});
|
| 13 |
+
Array<int> c = a.pmap2([](int x, int y) { return x + y; }, b);
|
| 14 |
+
std::cout << "pmap2 add: ";
|
| 15 |
+
for (size_t i = 0; i < c.size(); ++i) std::cout << c[i] << " ";
|
| 16 |
+
std::cout << "\n";
|
| 17 |
+
|
| 18 |
+
// softmax as Ξ -map
|
| 19 |
+
Array<float> v({4}, {1.0f, 2.0f, 3.0f, 4.0f});
|
| 20 |
+
Array<float> sm = softmax(v);
|
| 21 |
+
std::cout << "softmax: ";
|
| 22 |
+
for (size_t i = 0; i < sm.size(); ++i) std::cout << sm[i] << " ";
|
| 23 |
+
std::cout << "\n";
|
| 24 |
+
|
| 25 |
+
// NAND universality check
|
| 26 |
+
std::cout << "nand(T,T)=" << nand_gate(true, true)
|
| 27 |
+
<< " nand(T,F)=" << nand_gate(true, false) << "\n";
|
| 28 |
+
|
| 29 |
+
return 0;
|
| 30 |
+
}
|
src/sovereign_array.cpp
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "sovereign_array.h"
|
| 2 |
+
#include <numeric>
|
| 3 |
+
|
| 4 |
+
namespace sovarr {
|
| 5 |
+
|
| 6 |
+
std::vector<size_t> unravel(size_t flat, const std::vector<size_t>& shape) {
|
| 7 |
+
std::vector<size_t> idx(shape.size());
|
| 8 |
+
size_t stride = 1;
|
| 9 |
+
for (size_t d = shape.size(); d-- > 0; ) {
|
| 10 |
+
idx[d] = (flat / stride) % shape[d];
|
| 11 |
+
stride *= shape[d];
|
| 12 |
+
}
|
| 13 |
+
return idx;
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
Array<float> softmax(const Array<float>& v) {
|
| 17 |
+
float s = 0.0f;
|
| 18 |
+
for (size_t i = 0; i < v.size(); ++i) s += std::exp(v[i]);
|
| 19 |
+
std::vector<float> out(v.size());
|
| 20 |
+
for (size_t i = 0; i < v.size(); ++i) out[i] = std::exp(v[i]) / s;
|
| 21 |
+
return Array<float>(v.shape(), std::move(out));
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
bool nand_gate(bool a, bool b) { return !(a && b); }
|
| 25 |
+
|
| 26 |
+
Array<float> nand_attention(const Array<float>& q, const Array<float>& k, const Array<float>& v) {
|
| 27 |
+
size_t n = q.shape()[0];
|
| 28 |
+
// scores_i = Ξ£_j q_i * k_j
|
| 29 |
+
std::vector<float> scores(n, 0.0f);
|
| 30 |
+
for (size_t i = 0; i < n; ++i)
|
| 31 |
+
for (size_t j = 0; j < n; ++j)
|
| 32 |
+
scores[i] += q[i] * k[j];
|
| 33 |
+
Array<float> scoresArr({n}, std::move(scores));
|
| 34 |
+
Array<float> w = softmax(scoresArr);
|
| 35 |
+
// out_i = Ξ£_j w_i * v_j
|
| 36 |
+
std::vector<float> out(n, 0.0f);
|
| 37 |
+
for (size_t i = 0; i < n; ++i)
|
| 38 |
+
for (size_t j = 0; j < n; ++j)
|
| 39 |
+
out[i] += w[i] * v[j];
|
| 40 |
+
return Array<float>({n}, std::move(out));
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
} // namespace sovarr
|
src/sovereign_export.cpp
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "sovereign_export.h"
|
| 2 |
+
#include "sovereign_array.h"
|
| 3 |
+
#include <cstring>
|
| 4 |
+
#include <cstddef>
|
| 5 |
+
|
| 6 |
+
// ββ Paper I: NAND βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 7 |
+
|
| 8 |
+
int sovarr_nand(int a, int b) {
|
| 9 |
+
return sovarr::nand_gate(a != 0, b != 0) ? 1 : 0;
|
| 10 |
+
}
|
| 11 |
+
int sovarr_not(int a) { return sovarr_nand(a, a); }
|
| 12 |
+
int sovarr_and(int a, int b) { return sovarr_nand(sovarr_nand(a, b), sovarr_nand(a, b)); }
|
| 13 |
+
int sovarr_or(int a, int b) { return sovarr_nand(sovarr_nand(a, a), sovarr_nand(b, b)); }
|
| 14 |
+
|
| 15 |
+
// ββ Paper II: Softmax βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 16 |
+
|
| 17 |
+
void sovarr_softmax(const float* in, float* out, size_t n) {
|
| 18 |
+
sovarr::Array<float> v({n}, std::vector<float>(in, in + n));
|
| 19 |
+
auto sm = sovarr::softmax(v);
|
| 20 |
+
for (size_t i = 0; i < n; ++i) out[i] = sm[i];
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
// Face centroid: exact uniform over support, zero elsewhere.
|
| 24 |
+
void sovarr_face_centroid(const int* support, size_t support_len,
|
| 25 |
+
float* out, size_t n) {
|
| 26 |
+
std::memset(out, 0, n * sizeof(float));
|
| 27 |
+
if (support_len == 0) return;
|
| 28 |
+
float w = 1.0f / static_cast<float>(support_len);
|
| 29 |
+
for (size_t k = 0; k < support_len; ++k) {
|
| 30 |
+
int idx = support[k];
|
| 31 |
+
if (idx >= 0 && static_cast<size_t>(idx) < n)
|
| 32 |
+
out[idx] = w;
|
| 33 |
+
}
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
// ββ Paper III: Attention ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 37 |
+
|
| 38 |
+
void sovarr_nand_attention(const float* q, const float* k, const float* v,
|
| 39 |
+
float* out, size_t n) {
|
| 40 |
+
sovarr::Array<float> qa({n}, std::vector<float>(q, q + n));
|
| 41 |
+
sovarr::Array<float> ka({n}, std::vector<float>(k, k + n));
|
| 42 |
+
sovarr::Array<float> va({n}, std::vector<float>(v, v + n));
|
| 43 |
+
auto res = sovarr::nand_attention(qa, ka, va);
|
| 44 |
+
for (size_t i = 0; i < n; ++i) out[i] = res[i];
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
// ββ Broadcast βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 48 |
+
|
| 49 |
+
void sovarr_broadcast_1d(const float* v, const float* w, float* out, size_t n) {
|
| 50 |
+
for (size_t i = 0; i < n; ++i) out[i] = v[i] + w[i];
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
// ββ Version βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 54 |
+
|
| 55 |
+
const char* sovarr_version(void) {
|
| 56 |
+
return "sovereign-array-1.0.0 | Array I Ξ± = IβΞ± | zero-sorry | 2026";
|
| 57 |
+
}
|
stress_test.sh
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
# stress_test.sh β The Only Script That Matters
|
| 3 |
+
#
|
| 4 |
+
# Runs the four-layer falsification suite for the Sovereign Array Stack.
|
| 5 |
+
# Every layer corresponds to a named theorem in ArrayLang/.
|
| 6 |
+
#
|
| 7 |
+
# Usage:
|
| 8 |
+
# ./stress_test.sh # all layers
|
| 9 |
+
# ./stress_test.sh --layer 0 # Lean kernel only
|
| 10 |
+
# ./stress_test.sh --layer 1 # property falsifier only
|
| 11 |
+
# ./stress_test.sh --layer 3 # NP attack only (quick)
|
| 12 |
+
# ./stress_test.sh --install # install Python deps
|
| 13 |
+
#
|
| 14 |
+
# Pre-push hook:
|
| 15 |
+
# echo './stress_test.sh' >> .git/hooks/pre-push && chmod +x .git/hooks/pre-push
|
| 16 |
+
#
|
| 17 |
+
# Pass criteria:
|
| 18 |
+
# Layer 0 β 0 sorry, 0 custom axioms, all reductions terminate
|
| 19 |
+
# Layer 1 β 0 counterexamples @ 100k shrunk examples
|
| 20 |
+
# Layer 2 β Alive2: Verified (or skip if toolchain absent)
|
| 21 |
+
# Layer 3 β 0 soundness violations on random 3-SAT instances
|
| 22 |
+
|
| 23 |
+
set -euo pipefail
|
| 24 |
+
REPO="$(cd "$(dirname "$0")" && pwd)"
|
| 25 |
+
LAYER="${2:-all}"
|
| 26 |
+
|
| 27 |
+
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
| 28 |
+
pass() { echo -e "${GREEN} PASS${NC} $1"; }
|
| 29 |
+
fail() { echo -e "${RED} FAIL${NC} $1"; exit 1; }
|
| 30 |
+
warn() { echo -e "${YELLOW} SKIP${NC} $1"; }
|
| 31 |
+
|
| 32 |
+
# ββ Flags βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 33 |
+
if [[ "${1:-}" == "--install" ]]; then
|
| 34 |
+
echo "Installing Python dependencies..."
|
| 35 |
+
pip install hypothesis pytest python-sat 2>&1 | tail -5
|
| 36 |
+
echo "Done."
|
| 37 |
+
exit 0
|
| 38 |
+
fi
|
| 39 |
+
|
| 40 |
+
if [[ "${1:-}" == "--layer" ]]; then
|
| 41 |
+
LAYER="$2"
|
| 42 |
+
fi
|
| 43 |
+
|
| 44 |
+
echo ""
|
| 45 |
+
echo "ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ"
|
| 46 |
+
echo "β SOVEREIGN ARRAY β FALSIFICATION SUITE β"
|
| 47 |
+
echo "β Array I Ξ± = I β Ξ± Β· zero-sorry Β· no NP-magic β"
|
| 48 |
+
echo "ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ"
|
| 49 |
+
echo ""
|
| 50 |
+
|
| 51 |
+
# ββ Layer 0: Lean kernel ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 52 |
+
|
| 53 |
+
run_layer0() {
|
| 54 |
+
echo "π₯ LAYER 0: LOGIC KERNEL"
|
| 55 |
+
|
| 56 |
+
if ! command -v lake &>/dev/null; then
|
| 57 |
+
warn "lake not found β install Lean 4 toolchain from https://leanprover.github.io/"
|
| 58 |
+
return
|
| 59 |
+
fi
|
| 60 |
+
|
| 61 |
+
cd "$REPO"
|
| 62 |
+
echo " Running lake build..."
|
| 63 |
+
lake build --verbose 2>&1 | tee /tmp/sovarr_build.log
|
| 64 |
+
|
| 65 |
+
SORRY_COUNT=$(grep -c "sorry" /tmp/sovarr_build.log || true)
|
| 66 |
+
if [ "$SORRY_COUNT" -gt 0 ]; then
|
| 67 |
+
fail "SORRY LEAK: $SORRY_COUNT occurrences in build output"
|
| 68 |
+
fi
|
| 69 |
+
pass "0 sorry in build output"
|
| 70 |
+
|
| 71 |
+
# ConsistencyCheck
|
| 72 |
+
if lake env lean --run ArrayLang/ConsistencyCheck.lean; then
|
| 73 |
+
pass "ConsistencyCheck.lean"
|
| 74 |
+
else
|
| 75 |
+
fail "ConsistencyCheck.lean exited nonzero"
|
| 76 |
+
fi
|
| 77 |
+
|
| 78 |
+
echo ""
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
# ββ Layer 1: Property falsifier βββββββββββββββββββββββββββββββββββββββββββββββ
|
| 82 |
+
|
| 83 |
+
run_layer1() {
|
| 84 |
+
echo "π₯ LAYER 1: PROPERTY FALSIFICATION"
|
| 85 |
+
|
| 86 |
+
# Build shared library first
|
| 87 |
+
cd "$REPO"
|
| 88 |
+
mkdir -p build
|
| 89 |
+
if command -v cmake &>/dev/null; then
|
| 90 |
+
echo " Building shared library..."
|
| 91 |
+
cmake -S . -B build -G "MinGW Makefiles" 2>/dev/null \
|
| 92 |
+
|| cmake -S . -B build 2>/dev/null \
|
| 93 |
+
|| true
|
| 94 |
+
cmake --build build 2>/dev/null || warn "cmake build failed β tests will skip"
|
| 95 |
+
else
|
| 96 |
+
warn "cmake not found β shared lib not built, property tests will skip"
|
| 97 |
+
fi
|
| 98 |
+
|
| 99 |
+
if ! command -v python3 &>/dev/null && ! command -v python &>/dev/null; then
|
| 100 |
+
warn "Python not found β skipping property tests"
|
| 101 |
+
return
|
| 102 |
+
fi
|
| 103 |
+
PYTHON=$(command -v python3 || command -v python)
|
| 104 |
+
|
| 105 |
+
if ! "$PYTHON" -c "import hypothesis" 2>/dev/null; then
|
| 106 |
+
warn "hypothesis not installed β run: ./stress_test.sh --install"
|
| 107 |
+
return
|
| 108 |
+
fi
|
| 109 |
+
|
| 110 |
+
echo " Running property falsifier (100k examples)..."
|
| 111 |
+
cd "$REPO/tests"
|
| 112 |
+
"$PYTHON" -m pytest falsify_properties.py -x -q --tb=short \
|
| 113 |
+
--hypothesis-seed=0 2>&1 | tail -20
|
| 114 |
+
|
| 115 |
+
pass "Property falsifier: zero counterexamples"
|
| 116 |
+
echo ""
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
# ββ Layer 2: Hardware equivalence βββββββββββββββββββββββββββββββββββββββββββββ
|
| 120 |
+
|
| 121 |
+
run_layer2() {
|
| 122 |
+
echo "π₯ LAYER 2: HARDWARE EQUIVALENCE"
|
| 123 |
+
bash "$REPO/verification/run_alive2_crucible.sh" || fail "Alive2 check"
|
| 124 |
+
bash "$REPO/hardware/run_determinism_qemu.sh" || fail "Determinism check"
|
| 125 |
+
pass "Hardware equivalence"
|
| 126 |
+
echo ""
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
# ββ Layer 3: NP attack ββββββββοΏ½οΏ½βββββββββββββββββββββββββββββββββββββββββββββββ
|
| 130 |
+
|
| 131 |
+
run_layer3() {
|
| 132 |
+
echo "π₯ LAYER 3: NP ATTACK VECTOR"
|
| 133 |
+
|
| 134 |
+
PYTHON=$(command -v python3 || command -v python || echo "")
|
| 135 |
+
if [ -z "$PYTHON" ]; then warn "Python not found"; return; fi
|
| 136 |
+
|
| 137 |
+
echo " Running NP attack (soundness check on random 3-SAT)..."
|
| 138 |
+
cd "$REPO/tests"
|
| 139 |
+
"$PYTHON" np_attack.py --quick --instances 20 2>&1
|
| 140 |
+
|
| 141 |
+
pass "NP attack: zero soundness violations"
|
| 142 |
+
echo ""
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
# ββ Dispatch ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 146 |
+
|
| 147 |
+
case "$LAYER" in
|
| 148 |
+
0) run_layer0 ;;
|
| 149 |
+
1) run_layer1 ;;
|
| 150 |
+
2) run_layer2 ;;
|
| 151 |
+
3) run_layer3 ;;
|
| 152 |
+
all)
|
| 153 |
+
run_layer0
|
| 154 |
+
run_layer1
|
| 155 |
+
run_layer2
|
| 156 |
+
run_layer3
|
| 157 |
+
;;
|
| 158 |
+
*) echo "Unknown layer: $LAYER. Use 0, 1, 2, 3, or all."; exit 1 ;;
|
| 159 |
+
esac
|
| 160 |
+
|
| 161 |
+
echo "ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ"
|
| 162 |
+
echo "β SOVEREIGN STRESS TEST PASSED β ZERO SORRY CONFIRMED β"
|
| 163 |
+
echo "ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ"
|
test/test.cpp
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "sovereign_array.h"
|
| 2 |
+
#include <cassert>
|
| 3 |
+
#include <cmath>
|
| 4 |
+
#include <iostream>
|
| 5 |
+
|
| 6 |
+
using namespace sovarr;
|
| 7 |
+
|
| 8 |
+
static int passed = 0, failed = 0;
|
| 9 |
+
#define CHECK(cond) do { if (cond) { ++passed; } else { ++failed; std::cerr << "FAIL: " #cond "\n"; } } while(0)
|
| 10 |
+
|
| 11 |
+
int main() {
|
| 12 |
+
// 1. pmap2 pointwise add
|
| 13 |
+
Array<int> a({2, 2}, {1, 2, 3, 4});
|
| 14 |
+
Array<int> b({2, 2}, {10, 20, 30, 40});
|
| 15 |
+
Array<int> c = a.pmap2([](int x, int y) { return x + y; }, b);
|
| 16 |
+
CHECK(c[0] == 11 && c[1] == 22 && c[2] == 33 && c[3] == 44);
|
| 17 |
+
|
| 18 |
+
// 2. pmap2 commutativity
|
| 19 |
+
Array<int> d = a.pmap2([](int x, int y) { return x * y; }, b);
|
| 20 |
+
CHECK(d[0] == 10 && d[3] == 160);
|
| 21 |
+
|
| 22 |
+
// 3. softmax sums to ~1 (Ξ -map normalization)
|
| 23 |
+
Array<float> v({4}, {1.0f, 2.0f, 3.0f, 4.0f});
|
| 24 |
+
Array<float> sm = softmax(v);
|
| 25 |
+
float sum = 0.0f;
|
| 26 |
+
for (size_t i = 0; i < sm.size(); ++i) sum += sm[i];
|
| 27 |
+
CHECK(std::fabs(sum - 1.0f) < 1e-5f);
|
| 28 |
+
|
| 29 |
+
// 4. softmax shift invariance (exp(v+c)/Ξ£ exp(v+c) == exp(v)/Ξ£ exp(v))
|
| 30 |
+
Array<float> v2({3}, {0.0f, 1.0f, 2.0f});
|
| 31 |
+
Array<float> sm2 = softmax(v2);
|
| 32 |
+
Array<float> v3({3}, {5.0f, 6.0f, 7.0f});
|
| 33 |
+
Array<float> sm3 = softmax(v3);
|
| 34 |
+
CHECK(std::fabs(sm2[0] - sm3[0]) < 1e-5f && std::fabs(sm2[2] - sm3[2]) < 1e-5f);
|
| 35 |
+
|
| 36 |
+
// 5. broadcast pullback: add a row vector to each row of a matrix
|
| 37 |
+
Array<float> mat({2, 3}, {1, 2, 3, 4, 5, 6});
|
| 38 |
+
Array<float> row({3}, {10, 20, 30});
|
| 39 |
+
Array<float> bc = broadcast({2, 3}, mat, row);
|
| 40 |
+
CHECK(bc[0] == 11 && bc[2] == 33 && bc[3] == 14 && bc[5] == 36);
|
| 41 |
+
|
| 42 |
+
// 6. NAND universality
|
| 43 |
+
CHECK(nand_gate(true, true) == false);
|
| 44 |
+
CHECK(nand_gate(true, false) == true);
|
| 45 |
+
CHECK(nand_gate(false, false) == true);
|
| 46 |
+
// NOT via nand(a,a)
|
| 47 |
+
CHECK(nand_gate(true, true) == !true);
|
| 48 |
+
// AND via nand(nand(a,b),nand(a,b))
|
| 49 |
+
auto andG = [](bool a, bool b) { return nand_gate(nand_gate(a, b), nand_gate(a, b)); };
|
| 50 |
+
CHECK(andG(true, true) == true && andG(true, false) == false);
|
| 51 |
+
|
| 52 |
+
// 7. attention spec runs
|
| 53 |
+
Array<float> q({3}, {1, 0, 0});
|
| 54 |
+
Array<float> k({3}, {1, 1, 1});
|
| 55 |
+
Array<float> val({3}, {2, 4, 6});
|
| 56 |
+
Array<float> att = nand_attention(q, k, val);
|
| 57 |
+
CHECK(att.size() == 3);
|
| 58 |
+
|
| 59 |
+
std::cout << "Sovereign Array tests: " << passed << " passed, " << failed << " failed\n";
|
| 60 |
+
return failed == 0 ? 0 : 1;
|
| 61 |
+
}
|
tests/.pytest_cache/CACHEDIR.TAG
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Signature: 8a477f597d28d172789f06886806bc55
|
| 2 |
+
# This file is a cache directory tag created by pytest.
|
| 3 |
+
# For information about cache directory tags, see:
|
| 4 |
+
# https://bford.info/cachedir/spec.html
|
tests/.pytest_cache/README.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# pytest cache directory #
|
| 2 |
+
|
| 3 |
+
This directory contains data from the pytest's cache plugin,
|
| 4 |
+
which provides the `--lf` and `--ff` options, as well as the `cache` fixture.
|
| 5 |
+
|
| 6 |
+
**Do not** commit this to version control.
|
| 7 |
+
|
| 8 |
+
See [the docs](https://docs.pytest.org/en/stable/how-to/cache.html) for more information.
|
tests/.pytest_cache/v/cache/nodeids
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
"falsify_properties.py::TestBroadcast::test_broadcast_1d_pointwise",
|
| 3 |
+
"falsify_properties.py::TestPaperIII_Attention::test_attention_matches_reference",
|
| 4 |
+
"falsify_properties.py::TestPaperIII_Attention::test_attention_output_size",
|
| 5 |
+
"falsify_properties.py::TestPaperIII_Attention::test_attention_uniform_k_is_constant",
|
| 6 |
+
"falsify_properties.py::TestPaperII_Simplex::test_face_centroid_sums_to_one",
|
| 7 |
+
"falsify_properties.py::TestPaperII_Simplex::test_face_centroid_support_matches",
|
| 8 |
+
"falsify_properties.py::TestPaperII_Simplex::test_softmax_all_positive",
|
| 9 |
+
"falsify_properties.py::TestPaperII_Simplex::test_softmax_matches_reference",
|
| 10 |
+
"falsify_properties.py::TestPaperII_Simplex::test_softmax_shift_invariant",
|
| 11 |
+
"falsify_properties.py::TestPaperII_Simplex::test_softmax_sums_to_one",
|
| 12 |
+
"falsify_properties.py::TestPaperII_Simplex::test_vertex_centroid_is_indicator",
|
| 13 |
+
"falsify_properties.py::TestPaperI_NAND::test_and_via_nand",
|
| 14 |
+
"falsify_properties.py::TestPaperI_NAND::test_demorgan",
|
| 15 |
+
"falsify_properties.py::TestPaperI_NAND::test_nand_truth_table",
|
| 16 |
+
"falsify_properties.py::TestPaperI_NAND::test_not_via_nand",
|
| 17 |
+
"falsify_properties.py::TestPaperI_NAND::test_or_via_nand"
|
| 18 |
+
]
|
tests/.pytest_cache/v/cache/stepwise
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
[]
|
tests/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
# Sovereign Array test suite
|
tests/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (159 Bytes). View file
|
|
|
tests/__pycache__/falsify_properties.cpython-312-pytest-8.3.5.pyc
ADDED
|
Binary file (40.5 kB). View file
|
|
|
tests/falsify_properties.py
ADDED
|
@@ -0,0 +1,358 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Layer 1: Property-Based Falsifier
|
| 3 |
+
==================================
|
| 4 |
+
Attacks the extracted C++20 kernels via Hypothesis.
|
| 5 |
+
Every test corresponds to a named Lean 4 theorem.
|
| 6 |
+
|
| 7 |
+
Run:
|
| 8 |
+
pip install hypothesis pytest
|
| 9 |
+
cmake -S .. -B ../build && cmake --build ../build
|
| 10 |
+
pytest falsify_properties.py -x -v --tb=short
|
| 11 |
+
|
| 12 |
+
Pass criteria: zero failures after 100k shrunk examples.
|
| 13 |
+
If Hypothesis finds one counterexample, the Lean theorem was weaker than intent.
|
| 14 |
+
"""
|
| 15 |
+
|
| 16 |
+
import ctypes
|
| 17 |
+
import math
|
| 18 |
+
import os
|
| 19 |
+
import pathlib
|
| 20 |
+
import sys
|
| 21 |
+
|
| 22 |
+
import pytest
|
| 23 |
+
from hypothesis import given, settings, assume, Phase
|
| 24 |
+
from hypothesis import strategies as st
|
| 25 |
+
|
| 26 |
+
# ββ Load the shared library βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 27 |
+
|
| 28 |
+
def _add_dll_dirs():
|
| 29 |
+
"""Add MinGW/Strawberry runtime dirs so ctypes can resolve DLL deps on Windows."""
|
| 30 |
+
for d in [
|
| 31 |
+
r"C:\Strawberry\c\bin",
|
| 32 |
+
r"C:\Program Files\mingw64\bin",
|
| 33 |
+
r"C:\mingw64\bin",
|
| 34 |
+
r"C:\msys64\mingw64\bin",
|
| 35 |
+
]:
|
| 36 |
+
if os.path.isdir(d):
|
| 37 |
+
try:
|
| 38 |
+
os.add_dll_directory(d)
|
| 39 |
+
except AttributeError:
|
| 40 |
+
pass # Python < 3.8
|
| 41 |
+
|
| 42 |
+
if os.name == "nt":
|
| 43 |
+
_add_dll_dirs()
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def _load_lib():
|
| 47 |
+
repo = pathlib.Path(__file__).resolve().parent.parent
|
| 48 |
+
candidates = [
|
| 49 |
+
repo / "build" / "libsovereign_array.so",
|
| 50 |
+
repo / "build" / "libsovereign_array.dll",
|
| 51 |
+
repo / "build" / "sovereign_array.dll",
|
| 52 |
+
repo / "build" / "libsovereign_array.dylib",
|
| 53 |
+
]
|
| 54 |
+
for p in candidates:
|
| 55 |
+
if p.exists():
|
| 56 |
+
return ctypes.CDLL(str(p))
|
| 57 |
+
raise FileNotFoundError(
|
| 58 |
+
"Build the shared library first:\n"
|
| 59 |
+
" cmake -S .. -B ../build && cmake --build ../build\n"
|
| 60 |
+
f"Looked in: {[str(c) for c in candidates]}"
|
| 61 |
+
)
|
| 62 |
+
|
| 63 |
+
try:
|
| 64 |
+
_lib = _load_lib()
|
| 65 |
+
|
| 66 |
+
# void sovarr_softmax(const float*, float*, size_t)
|
| 67 |
+
_lib.sovarr_softmax.argtypes = [
|
| 68 |
+
ctypes.POINTER(ctypes.c_float),
|
| 69 |
+
ctypes.POINTER(ctypes.c_float),
|
| 70 |
+
ctypes.c_size_t,
|
| 71 |
+
]
|
| 72 |
+
_lib.sovarr_softmax.restype = None
|
| 73 |
+
|
| 74 |
+
# void sovarr_face_centroid(const int*, size_t, float*, size_t)
|
| 75 |
+
_lib.sovarr_face_centroid.argtypes = [
|
| 76 |
+
ctypes.POINTER(ctypes.c_int),
|
| 77 |
+
ctypes.c_size_t,
|
| 78 |
+
ctypes.POINTER(ctypes.c_float),
|
| 79 |
+
ctypes.c_size_t,
|
| 80 |
+
]
|
| 81 |
+
_lib.sovarr_face_centroid.restype = None
|
| 82 |
+
|
| 83 |
+
# void sovarr_nand_attention(const float*, float*, float*, float*, size_t)
|
| 84 |
+
_lib.sovarr_nand_attention.argtypes = [
|
| 85 |
+
ctypes.POINTER(ctypes.c_float),
|
| 86 |
+
ctypes.POINTER(ctypes.c_float),
|
| 87 |
+
ctypes.POINTER(ctypes.c_float),
|
| 88 |
+
ctypes.POINTER(ctypes.c_float),
|
| 89 |
+
ctypes.c_size_t,
|
| 90 |
+
]
|
| 91 |
+
_lib.sovarr_nand_attention.restype = None
|
| 92 |
+
|
| 93 |
+
# int sovarr_nand(int, int) etc.
|
| 94 |
+
for fn in ("sovarr_nand", "sovarr_not", "sovarr_and", "sovarr_or"):
|
| 95 |
+
getattr(_lib, fn).argtypes = [ctypes.c_int, ctypes.c_int]
|
| 96 |
+
getattr(_lib, fn).restype = ctypes.c_int
|
| 97 |
+
|
| 98 |
+
LIB_OK = True
|
| 99 |
+
except FileNotFoundError as e:
|
| 100 |
+
print(f"WARNING: {e}", file=sys.stderr)
|
| 101 |
+
LIB_OK = False
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def softmax_c(xs: list[float]) -> list[float]:
|
| 105 |
+
n = len(xs)
|
| 106 |
+
in_arr = (ctypes.c_float * n)(*xs)
|
| 107 |
+
out_arr = (ctypes.c_float * n)()
|
| 108 |
+
_lib.sovarr_softmax(in_arr, out_arr, n)
|
| 109 |
+
return list(out_arr)
|
| 110 |
+
|
| 111 |
+
def face_centroid_c(support: list[int], n: int) -> list[float]:
|
| 112 |
+
sup_arr = (ctypes.c_int * len(support))(*support)
|
| 113 |
+
out_arr = (ctypes.c_float * n)()
|
| 114 |
+
_lib.sovarr_face_centroid(sup_arr, len(support), out_arr, n)
|
| 115 |
+
return list(out_arr)
|
| 116 |
+
|
| 117 |
+
def nand_attention_c(q, k, v):
|
| 118 |
+
n = len(q)
|
| 119 |
+
def fa(xs): return (ctypes.c_float * n)(*xs)
|
| 120 |
+
out = (ctypes.c_float * n)()
|
| 121 |
+
_lib.sovarr_nand_attention(fa(q), fa(k), fa(v), out, n)
|
| 122 |
+
return list(out)
|
| 123 |
+
|
| 124 |
+
def softmax_ref(xs: list[float]) -> list[float]:
|
| 125 |
+
"""Pure-Python reference implementation (exact, slow)."""
|
| 126 |
+
m = max(xs) # shift for numerical stability
|
| 127 |
+
exps = [math.exp(x - m) for x in xs]
|
| 128 |
+
s = sum(exps)
|
| 129 |
+
return [e / s for e in exps]
|
| 130 |
+
|
| 131 |
+
skip_no_lib = pytest.mark.skipif(not LIB_OK, reason="shared lib not built")
|
| 132 |
+
|
| 133 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 134 |
+
# PAPER I β NAND universality
|
| 135 |
+
# Lean theorems: notGate_eq, andGate_eq, orGate_eq
|
| 136 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 137 |
+
|
| 138 |
+
@skip_no_lib
|
| 139 |
+
class TestPaperI_NAND:
|
| 140 |
+
|
| 141 |
+
@given(st.integers(0, 1), st.integers(0, 1))
|
| 142 |
+
@settings(max_examples=4, phases=[Phase.generate]) # truth table is 4 entries
|
| 143 |
+
def test_nand_truth_table(self, a, b):
|
| 144 |
+
"""sovarr_nand(a,b) == not(a and b) [andGate_eq / notGate_eq]"""
|
| 145 |
+
expected = 0 if (a == 1 and b == 1) else 1
|
| 146 |
+
assert _lib.sovarr_nand(a, b) == expected
|
| 147 |
+
|
| 148 |
+
@given(st.integers(0, 1))
|
| 149 |
+
@settings(max_examples=2)
|
| 150 |
+
def test_not_via_nand(self, a):
|
| 151 |
+
"""nand(a,a) == not(a) [notGate_eq]"""
|
| 152 |
+
assert _lib.sovarr_not(a, a) == (0 if a == 1 else 1)
|
| 153 |
+
|
| 154 |
+
@given(st.integers(0, 1), st.integers(0, 1))
|
| 155 |
+
@settings(max_examples=4)
|
| 156 |
+
def test_and_via_nand(self, a, b):
|
| 157 |
+
"""nand(nand(a,b), nand(a,b)) == a and b [andGate_eq]"""
|
| 158 |
+
assert _lib.sovarr_and(a, b) == (1 if a == 1 and b == 1 else 0)
|
| 159 |
+
|
| 160 |
+
@given(st.integers(0, 1), st.integers(0, 1))
|
| 161 |
+
@settings(max_examples=4)
|
| 162 |
+
def test_or_via_nand(self, a, b):
|
| 163 |
+
"""nand(nand(a,a), nand(b,b)) == a or b [orGate_eq]"""
|
| 164 |
+
assert _lib.sovarr_or(a, b) == (1 if a == 1 or b == 1 else 0)
|
| 165 |
+
|
| 166 |
+
@given(st.integers(0, 1), st.integers(0, 1), st.integers(0, 1))
|
| 167 |
+
@settings(max_examples=8)
|
| 168 |
+
def test_demorgan(self, a, b, c):
|
| 169 |
+
"""not(a or b) == not(a) and not(b) β derived via NAND"""
|
| 170 |
+
lhs = _lib.sovarr_not(_lib.sovarr_or(a, b), _lib.sovarr_or(a, b))
|
| 171 |
+
rhs = _lib.sovarr_and(_lib.sovarr_not(a, a), _lib.sovarr_not(b, b))
|
| 172 |
+
assert lhs == rhs
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 176 |
+
# PAPER II β Simplex / Softmax / Face Centroid
|
| 177 |
+
# Lean theorems: softmax_is_pmap, softmax_shift_invariant,
|
| 178 |
+
# faceCentroid_nonneg, faceCentroid_support, vertex_centroid_eq
|
| 179 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 180 |
+
|
| 181 |
+
@skip_no_lib
|
| 182 |
+
class TestPaperII_Simplex:
|
| 183 |
+
|
| 184 |
+
@given(st.lists(st.floats(-20, 20, allow_nan=False, allow_infinity=False),
|
| 185 |
+
min_size=2, max_size=256))
|
| 186 |
+
@settings(max_examples=50_000, phases=[Phase.generate, Phase.shrink])
|
| 187 |
+
def test_softmax_sums_to_one(self, logits):
|
| 188 |
+
"""Ξ£ softmax(z)_i = 1 [softmax_is_pmap, sum normalisation]"""
|
| 189 |
+
out = softmax_c(logits)
|
| 190 |
+
assert abs(sum(out) - 1.0) < 1e-5, f"sum={sum(out)} logits={logits[:4]}"
|
| 191 |
+
|
| 192 |
+
@given(st.lists(st.floats(-10, 10, allow_nan=False, allow_infinity=False),
|
| 193 |
+
min_size=2, max_size=256),
|
| 194 |
+
st.floats(-50, 50, allow_nan=False, allow_infinity=False))
|
| 195 |
+
@settings(max_examples=20_000, phases=[Phase.generate, Phase.shrink])
|
| 196 |
+
def test_softmax_shift_invariant(self, logits, c):
|
| 197 |
+
"""softmax(z+c) == softmax(z) [softmax_shift_invariant]"""
|
| 198 |
+
shifted = [x + c for x in logits]
|
| 199 |
+
out1 = softmax_c(logits)
|
| 200 |
+
out2 = softmax_c(shifted)
|
| 201 |
+
for i, (a, b) in enumerate(zip(out1, out2)):
|
| 202 |
+
assert abs(a - b) < 1e-4, f"shift broke at i={i}: {a} vs {b} (c={c})"
|
| 203 |
+
|
| 204 |
+
@given(st.lists(st.floats(-10, 10, allow_nan=False, allow_infinity=False),
|
| 205 |
+
min_size=2, max_size=256))
|
| 206 |
+
@settings(max_examples=20_000)
|
| 207 |
+
def test_softmax_matches_reference(self, logits):
|
| 208 |
+
"""sovarr_softmax matches pure-Python reference [kernel correctness]"""
|
| 209 |
+
c_out = softmax_c(logits)
|
| 210 |
+
py_out = softmax_ref(logits)
|
| 211 |
+
for i, (a, b) in enumerate(zip(c_out, py_out)):
|
| 212 |
+
assert abs(a - b) < 1e-4, f"mismatch at i={i}: C={a} ref={b}"
|
| 213 |
+
|
| 214 |
+
@given(st.integers(2, 64),
|
| 215 |
+
st.lists(st.integers(0, 63), min_size=1, max_size=64, unique=True))
|
| 216 |
+
@settings(max_examples=10_000, phases=[Phase.generate, Phase.shrink])
|
| 217 |
+
def test_face_centroid_sums_to_one(self, n, raw_support):
|
| 218 |
+
"""Ξ£ faceCentroid(F)_i = 1 [faceCentroid = uniform on F]"""
|
| 219 |
+
support = [i for i in raw_support if i < n]
|
| 220 |
+
assume(len(support) > 0)
|
| 221 |
+
out = face_centroid_c(support, n)
|
| 222 |
+
assert abs(sum(out) - 1.0) < 1e-6, f"sum={sum(out)} F={support} n={n}"
|
| 223 |
+
|
| 224 |
+
@given(st.integers(2, 64),
|
| 225 |
+
st.lists(st.integers(0, 63), min_size=1, max_size=64, unique=True))
|
| 226 |
+
@settings(max_examples=10_000)
|
| 227 |
+
def test_face_centroid_support_matches(self, n, raw_support):
|
| 228 |
+
"""faceCentroid(F)_i β 0 β i β F [faceCentroid_support]"""
|
| 229 |
+
support = sorted(set(i for i in raw_support if i < n))
|
| 230 |
+
assume(len(support) > 0)
|
| 231 |
+
out = face_centroid_c(support, n)
|
| 232 |
+
support_set = set(support)
|
| 233 |
+
for i, v in enumerate(out):
|
| 234 |
+
if i in support_set:
|
| 235 |
+
assert v > 0, f"active coord {i} is zero"
|
| 236 |
+
else:
|
| 237 |
+
assert v == 0.0, f"inactive coord {i} is nonzero: {v}"
|
| 238 |
+
|
| 239 |
+
@given(st.integers(2, 64),
|
| 240 |
+
st.integers(0, 63))
|
| 241 |
+
@settings(max_examples=5_000)
|
| 242 |
+
def test_vertex_centroid_is_indicator(self, n, raw_v):
|
| 243 |
+
"""faceCentroid({v})_i = 1 if i==v else 0 [vertex_centroid_eq]"""
|
| 244 |
+
v = raw_v % n
|
| 245 |
+
out = face_centroid_c([v], n)
|
| 246 |
+
assert abs(out[v] - 1.0) < 1e-7, f"vertex {v}: {out[v]} β 1"
|
| 247 |
+
for i, val in enumerate(out):
|
| 248 |
+
if i != v:
|
| 249 |
+
assert val == 0.0, f"non-vertex {i} nonzero: {val}"
|
| 250 |
+
|
| 251 |
+
@given(st.lists(st.floats(-5, 5, allow_nan=False, allow_infinity=False),
|
| 252 |
+
min_size=2, max_size=64))
|
| 253 |
+
@settings(max_examples=10_000)
|
| 254 |
+
def test_softmax_all_positive(self, logits):
|
| 255 |
+
"""softmax(z)_i > 0 for all i [softmax maps to interior of simplex]"""
|
| 256 |
+
out = softmax_c(logits)
|
| 257 |
+
for i, v in enumerate(out):
|
| 258 |
+
assert v > 0, f"softmax[{i}]={v} β€ 0 on {logits}"
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 262 |
+
# PAPER III β NAND Attention
|
| 263 |
+
# Lean theorem: attention_is_pmap
|
| 264 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 265 |
+
|
| 266 |
+
def attention_ref(q, k, v):
|
| 267 |
+
"""Pure-Python reference: scores_i = Ξ£ q_i*k_j, w=softmax(scores), out_i = Ξ£ w_i*v_j"""
|
| 268 |
+
n = len(q)
|
| 269 |
+
scores = [sum(q[i] * k[j] for j in range(n)) for i in range(n)]
|
| 270 |
+
w = softmax_ref(scores)
|
| 271 |
+
return [sum(w[i] * v[j] for j in range(n)) for i in range(n)]
|
| 272 |
+
|
| 273 |
+
@skip_no_lib
|
| 274 |
+
class TestPaperIII_Attention:
|
| 275 |
+
|
| 276 |
+
@given(st.integers(1, 32),
|
| 277 |
+
st.data())
|
| 278 |
+
@settings(max_examples=5_000, phases=[Phase.generate, Phase.shrink])
|
| 279 |
+
def test_attention_matches_reference(self, n, data):
|
| 280 |
+
"""sovarr_nand_attention β‘ reference attention [attention_is_pmap]"""
|
| 281 |
+
flt = st.floats(-5, 5, allow_nan=False, allow_infinity=False)
|
| 282 |
+
q = data.draw(st.lists(flt, min_size=n, max_size=n))
|
| 283 |
+
k = data.draw(st.lists(flt, min_size=n, max_size=n))
|
| 284 |
+
v = data.draw(st.lists(flt, min_size=n, max_size=n))
|
| 285 |
+
c_out = nand_attention_c(q, k, v)
|
| 286 |
+
py_out = attention_ref(q, k, v)
|
| 287 |
+
for i, (a, b) in enumerate(zip(c_out, py_out)):
|
| 288 |
+
assert abs(a - b) < 1e-3, \
|
| 289 |
+
f"attention divergence at i={i}: C={a:.6f} ref={b:.6f}"
|
| 290 |
+
|
| 291 |
+
@given(st.integers(1, 32), st.data())
|
| 292 |
+
@settings(max_examples=2_000)
|
| 293 |
+
def test_attention_output_size(self, n, data):
|
| 294 |
+
"""output has same size as input [shape preservation]"""
|
| 295 |
+
flt = st.floats(-3, 3, allow_nan=False, allow_infinity=False)
|
| 296 |
+
q = data.draw(st.lists(flt, min_size=n, max_size=n))
|
| 297 |
+
k = data.draw(st.lists(flt, min_size=n, max_size=n))
|
| 298 |
+
v = data.draw(st.lists(flt, min_size=n, max_size=n))
|
| 299 |
+
out = nand_attention_c(q, k, v)
|
| 300 |
+
assert len(out) == n
|
| 301 |
+
|
| 302 |
+
@given(st.integers(1, 16), st.data())
|
| 303 |
+
@settings(max_examples=2_000)
|
| 304 |
+
def test_attention_uniform_k_is_constant(self, n, data):
|
| 305 |
+
"""When k is uniform, all scores are equal β attention weights are uniform."""
|
| 306 |
+
flt = st.floats(-3, 3, allow_nan=False, allow_infinity=False)
|
| 307 |
+
q = data.draw(st.lists(flt, min_size=n, max_size=n))
|
| 308 |
+
k = [1.0] * n # uniform key
|
| 309 |
+
v = data.draw(st.lists(flt, min_size=n, max_size=n))
|
| 310 |
+
out = nand_attention_c(q, k, v)
|
| 311 |
+
# All outputs should be identical (uniform weight over v)
|
| 312 |
+
mean = sum(out) / n
|
| 313 |
+
for i, val in enumerate(out):
|
| 314 |
+
assert abs(val - mean) < 1e-4, \
|
| 315 |
+
f"uniform-k: out[{i}]={val} β mean={mean}"
|
| 316 |
+
|
| 317 |
+
|
| 318 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 319 |
+
# BROADCAST β pullback semantics
|
| 320 |
+
# Lean theorem: broadcast_is_pullback
|
| 321 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 322 |
+
|
| 323 |
+
@skip_no_lib
|
| 324 |
+
class TestBroadcast:
|
| 325 |
+
|
| 326 |
+
@given(st.lists(st.floats(-100, 100, allow_nan=False, allow_infinity=False),
|
| 327 |
+
min_size=1, max_size=256))
|
| 328 |
+
@settings(max_examples=10_000)
|
| 329 |
+
def test_broadcast_1d_pointwise(self, xs):
|
| 330 |
+
"""broadcast_1d(v, w)_i = v_i + w_i [broadcast_is_pullback, 1D case]"""
|
| 331 |
+
n = len(xs)
|
| 332 |
+
# split into two halves (or use same list for both)
|
| 333 |
+
v_arr = (ctypes.c_float * n)(*xs)
|
| 334 |
+
w_arr = (ctypes.c_float * n)(*xs)
|
| 335 |
+
out = (ctypes.c_float * n)()
|
| 336 |
+
_lib.sovarr_broadcast_1d.argtypes = [
|
| 337 |
+
ctypes.POINTER(ctypes.c_float),
|
| 338 |
+
ctypes.POINTER(ctypes.c_float),
|
| 339 |
+
ctypes.POINTER(ctypes.c_float),
|
| 340 |
+
ctypes.c_size_t,
|
| 341 |
+
]
|
| 342 |
+
_lib.sovarr_broadcast_1d.restype = None
|
| 343 |
+
_lib.sovarr_broadcast_1d(v_arr, w_arr, out, n)
|
| 344 |
+
for i, (a, b, o) in enumerate(zip(xs, xs, out)):
|
| 345 |
+
assert abs(o - (a + b)) < 1e-4, f"broadcast[{i}]: {o} β {a+b}"
|
| 346 |
+
|
| 347 |
+
|
| 348 |
+
# βοΏ½οΏ½οΏ½βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 349 |
+
# Standalone: run without pytest
|
| 350 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 351 |
+
|
| 352 |
+
if __name__ == "__main__":
|
| 353 |
+
import subprocess, sys
|
| 354 |
+
result = subprocess.run(
|
| 355 |
+
[sys.executable, "-m", "pytest", __file__, "-x", "-v", "--tb=short"],
|
| 356 |
+
cwd=pathlib.Path(__file__).parent,
|
| 357 |
+
)
|
| 358 |
+
sys.exit(result.returncode)
|
tests/np_attack.py
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Layer 3: NP Attack Vector
|
| 3 |
+
==========================
|
| 4 |
+
Tests vertex_enumeration_decide (solveFeasibility from SimplexNorm.lean)
|
| 5 |
+
against ground-truth SAT/UNSAT on SATLIB-style random 3-SAT instances.
|
| 6 |
+
|
| 7 |
+
What this checks:
|
| 8 |
+
- SOUNDNESS: if vertex_enumeration_decide returns SAT, a real solver agrees
|
| 9 |
+
- COMPLETENESS: if it returns UNSAT, the LP relaxation is genuinely infeasible
|
| 10 |
+
(does NOT claim P=NP β the integer gap is noted explicitly)
|
| 11 |
+
|
| 12 |
+
Run:
|
| 13 |
+
pip install python-sat hypothesis
|
| 14 |
+
pytest np_attack.py -x -v --tb=short
|
| 15 |
+
# or for 10 random hard instances:
|
| 16 |
+
python np_attack.py --quick
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
import ctypes
|
| 20 |
+
import itertools
|
| 21 |
+
import math
|
| 22 |
+
import pathlib
|
| 23 |
+
import sys
|
| 24 |
+
import random
|
| 25 |
+
import argparse
|
| 26 |
+
|
| 27 |
+
import pytest
|
| 28 |
+
from hypothesis import given, settings, assume
|
| 29 |
+
from hypothesis import strategies as st
|
| 30 |
+
|
| 31 |
+
# ββ LP vertex enumeration (pure Python, maps SimplexNorm.solveFeasibility) βββ
|
| 32 |
+
|
| 33 |
+
def vertex_enumeration_decide(n_vars: int, constraints: list[tuple[list[float], float]]) -> tuple[bool, int | None]:
|
| 34 |
+
"""
|
| 35 |
+
Enumerate all n_vars vertices of ΞβΏ (one-hot vectors) and check each
|
| 36 |
+
against every constraint.
|
| 37 |
+
|
| 38 |
+
Returns (True, vertex_index) if a feasible vertex exists, else (False, None).
|
| 39 |
+
|
| 40 |
+
This is the direct Python mirror of:
|
| 41 |
+
def solveFeasibility : FeasibilityProblem n β Option (Vertex n)
|
| 42 |
+
|
| 43 |
+
Complexity: O(n_vars * |constraints|) β polynomial in variable count.
|
| 44 |
+
NOTE: Solves LP vertex feasibility, NOT integer programming.
|
| 45 |
+
The integrality gap means this can return False when IP is SAT.
|
| 46 |
+
"""
|
| 47 |
+
for v in range(n_vars):
|
| 48 |
+
# vertexPoint(v) = one-hot at position v
|
| 49 |
+
x = [1.0 if i == v else 0.0 for i in range(n_vars)]
|
| 50 |
+
feasible = all(
|
| 51 |
+
sum(coeff * x[i] for i, coeff in enumerate(coeffs)) <= rhs
|
| 52 |
+
for coeffs, rhs in constraints
|
| 53 |
+
)
|
| 54 |
+
if feasible:
|
| 55 |
+
return True, v
|
| 56 |
+
return False, None
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
# ββ 3-SAT β Linear constraints on ΞβΏ βββββββββββββββββββββββββββββββββββββββββ
|
| 60 |
+
# Map each clause (x_i β¨ x_j β¨ Β¬x_k) to a linear constraint on vertex space.
|
| 61 |
+
# A vertex v satisfies the clause iff:
|
| 62 |
+
# x_i = 1 (v=i, positive literal) OR x_j = 1 (v=j) OR (vβ k, negative)
|
| 63 |
+
# This is the LP relaxation β completeness gap exists.
|
| 64 |
+
|
| 65 |
+
def clause_to_constraint(clause: list[tuple[int, bool]], n_vars: int) -> tuple[list[float], float]:
|
| 66 |
+
"""
|
| 67 |
+
Convert a 3-SAT clause (list of (var_idx, is_positive)) to a linear constraint.
|
| 68 |
+
Constraint: at least one literal satisfied β₯ 1.
|
| 69 |
+
Negated: sum of violating one-hots β€ n_vars - 1.
|
| 70 |
+
"""
|
| 71 |
+
coeffs = [0.0] * n_vars
|
| 72 |
+
rhs = float(n_vars - 1)
|
| 73 |
+
# For each literal: if positive, variable must be 1 β penalise if it is 0
|
| 74 |
+
# We encode: constraint violated only if ALL literals are false simultaneously.
|
| 75 |
+
# For vertex v: literal (var, True) is True iff v == var.
|
| 76 |
+
# literal (var, False) is True iff v != var.
|
| 77 |
+
# This is approximate β see note above about integrality gap.
|
| 78 |
+
for var, is_pos in clause:
|
| 79 |
+
if is_pos:
|
| 80 |
+
coeffs[var] = -1.0
|
| 81 |
+
else:
|
| 82 |
+
coeffs[var] = 1.0
|
| 83 |
+
return coeffs, rhs
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
def random_3sat(n_vars: int, n_clauses: int, seed: int = 42) -> list[list[tuple[int, bool]]]:
|
| 87 |
+
rng = random.Random(seed)
|
| 88 |
+
clauses = []
|
| 89 |
+
for _ in range(n_clauses):
|
| 90 |
+
vars_chosen = rng.sample(range(n_vars), 3)
|
| 91 |
+
clause = [(v, rng.random() > 0.5) for v in vars_chosen]
|
| 92 |
+
clauses.append(clause)
|
| 93 |
+
return clauses
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
def brute_force_sat(n_vars: int, clauses: list[list[tuple[int, bool]]]) -> bool:
|
| 97 |
+
"""Exhaustive truth-table check β ground truth for small instances."""
|
| 98 |
+
for assignment in itertools.product([False, True], repeat=n_vars):
|
| 99 |
+
satisfied = all(
|
| 100 |
+
any(
|
| 101 |
+
(assignment[var] if is_pos else not assignment[var])
|
| 102 |
+
for var, is_pos in clause
|
| 103 |
+
)
|
| 104 |
+
for clause in clauses
|
| 105 |
+
)
|
| 106 |
+
if satisfied:
|
| 107 |
+
return True
|
| 108 |
+
return False
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 112 |
+
# Tests
|
| 113 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 114 |
+
|
| 115 |
+
class TestNPAttack:
|
| 116 |
+
|
| 117 |
+
def test_trivially_sat(self):
|
| 118 |
+
"""Single variable, single positive clause β SAT at vertex 0."""
|
| 119 |
+
ok, v = vertex_enumeration_decide(1, [])
|
| 120 |
+
assert ok
|
| 121 |
+
assert v == 0
|
| 122 |
+
|
| 123 |
+
def test_empty_constraints_always_sat(self):
|
| 124 |
+
"""No constraints β always SAT (empty_constraints_sat theorem)."""
|
| 125 |
+
for n in [1, 5, 20, 100]:
|
| 126 |
+
ok, v = vertex_enumeration_decide(n, [])
|
| 127 |
+
assert ok, f"empty constraints should be SAT for n={n}"
|
| 128 |
+
|
| 129 |
+
def test_contradictory_unit_clauses(self):
|
| 130 |
+
"""x0=1 AND x0=0 is UNSAT."""
|
| 131 |
+
# Constraint 1: x0 must be 1 β coefficient[-1] for all other vertices
|
| 132 |
+
# Encode as: if only vertex 0 is valid AND no vertex is valid β UNSAT
|
| 133 |
+
# Simplest: require vertex 0 AND require not-vertex-0
|
| 134 |
+
constraints = [
|
| 135 |
+
# require x0 = 1: only vertex 0 satisfies β penalise all others: Ξ£(1-x0) β€ 0
|
| 136 |
+
([i == 0 and -1.0 or 0.0 for i in range(3)], -1.0), # x0 β₯ 1
|
| 137 |
+
([i == 0 and 1.0 or 0.0 for i in range(3)], 0.0), # x0 β€ 0
|
| 138 |
+
]
|
| 139 |
+
ok, _ = vertex_enumeration_decide(3, constraints)
|
| 140 |
+
assert not ok, "contradictory unit clauses must be UNSAT"
|
| 141 |
+
|
| 142 |
+
@given(st.integers(3, 8), st.integers(3, 15), st.integers(0, 9999))
|
| 143 |
+
@settings(max_examples=2_000)
|
| 144 |
+
def test_soundness_on_random_3sat(self, n_vars, n_clauses, seed):
|
| 145 |
+
"""
|
| 146 |
+
SOUNDNESS: if vertex_enumeration_decide returns SAT, brute-force agrees.
|
| 147 |
+
|
| 148 |
+
We do NOT check completeness here because the LP relaxation has an
|
| 149 |
+
integrality gap β it may return UNSAT when the full IP is SAT.
|
| 150 |
+
"""
|
| 151 |
+
clauses = random_3sat(n_vars, n_clauses, seed)
|
| 152 |
+
constraints = [clause_to_constraint(c, n_vars) for c in clauses]
|
| 153 |
+
|
| 154 |
+
ours_sat, witness = vertex_enumeration_decide(n_vars, constraints)
|
| 155 |
+
ground_truth = brute_force_sat(n_vars, clauses)
|
| 156 |
+
|
| 157 |
+
if ours_sat:
|
| 158 |
+
# If we claim SAT, ground truth MUST also be SAT (soundness)
|
| 159 |
+
assert ground_truth, (
|
| 160 |
+
f"UNSOUND: vertex_enumeration claimed SAT but brute-force says UNSAT\n"
|
| 161 |
+
f" n={n_vars} clauses={n_clauses} seed={seed} witness={witness}"
|
| 162 |
+
)
|
| 163 |
+
|
| 164 |
+
def test_vertex_witness_is_valid(self):
|
| 165 |
+
"""When we return a vertex, that specific vertex must satisfy all constraints."""
|
| 166 |
+
# 5 vars, require exactly one of {0,1,2} β vertex 3 or 4 should escape
|
| 167 |
+
n = 5
|
| 168 |
+
# Constraint: x0+x1+x2 β€ 0 (forbid vertices 0,1,2)
|
| 169 |
+
constraints = [
|
| 170 |
+
([1.0 if i < 3 else 0.0 for i in range(n)], 0.0)
|
| 171 |
+
]
|
| 172 |
+
ok, v = vertex_enumeration_decide(n, constraints)
|
| 173 |
+
assert ok
|
| 174 |
+
assert v in {3, 4}
|
| 175 |
+
# Verify the witness manually
|
| 176 |
+
x = [1.0 if i == v else 0.0 for i in range(n)]
|
| 177 |
+
for coeffs, rhs in constraints:
|
| 178 |
+
assert sum(c * xi for c, xi in zip(coeffs, x)) <= rhs
|
| 179 |
+
|
| 180 |
+
@given(st.integers(4, 12), st.integers(0, 9999))
|
| 181 |
+
@settings(max_examples=500)
|
| 182 |
+
def test_easy_sat_instances_detected(self, n_vars, seed):
|
| 183 |
+
"""
|
| 184 |
+
Random 3-SAT at ratio 2.0 (well below phase transition ~4.27) is almost
|
| 185 |
+
always SAT. Our vertex enumeration should find a feasible vertex for most.
|
| 186 |
+
|
| 187 |
+
This tests that we're not trivially returning UNSAT for everything.
|
| 188 |
+
"""
|
| 189 |
+
n_clauses = max(3, int(2.0 * n_vars))
|
| 190 |
+
clauses = random_3sat(n_vars, n_clauses, seed)
|
| 191 |
+
constraints = [clause_to_constraint(c, n_vars) for c in clauses]
|
| 192 |
+
|
| 193 |
+
ours_sat, _ = vertex_enumeration_decide(n_vars, constraints)
|
| 194 |
+
ground_truth = brute_force_sat(n_vars, clauses)
|
| 195 |
+
|
| 196 |
+
if ours_sat:
|
| 197 |
+
# Our SAT claim must be honest
|
| 198 |
+
assert ground_truth
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
# ββ Quick mode (CLI) ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 202 |
+
|
| 203 |
+
def run_quick(n_instances: int = 10):
|
| 204 |
+
print(f"NP Attack: {n_instances} random 3-SAT instances")
|
| 205 |
+
print(f"{'n':>4} {'clauses':>8} {'ours':>8} {'truth':>8} {'sound?':>8}")
|
| 206 |
+
rng = random.Random(1337)
|
| 207 |
+
failures = 0
|
| 208 |
+
for i in range(n_instances):
|
| 209 |
+
n = rng.randint(4, 12)
|
| 210 |
+
c = rng.randint(n, n * 4)
|
| 211 |
+
s = rng.randint(0, 999999)
|
| 212 |
+
clauses = random_3sat(n, c, s)
|
| 213 |
+
constraints = [clause_to_constraint(cl, n) for cl in clauses]
|
| 214 |
+
ours_sat, witness = vertex_enumeration_decide(n, constraints)
|
| 215 |
+
truth = brute_force_sat(n, clauses)
|
| 216 |
+
sound = "OK" if (not ours_sat or truth) else "FAIL"
|
| 217 |
+
if sound == "FAIL":
|
| 218 |
+
failures += 1
|
| 219 |
+
print(f"{n:>4} {c:>8} {'SAT' if ours_sat else 'UNSAT':>8} "
|
| 220 |
+
f"{'SAT' if truth else 'UNSAT':>8} {sound:>8}")
|
| 221 |
+
if failures == 0:
|
| 222 |
+
print("\nLayer 3: PASS β zero soundness failures")
|
| 223 |
+
else:
|
| 224 |
+
print(f"\nLayer 3: FAIL β {failures} soundness violations")
|
| 225 |
+
sys.exit(1)
|
| 226 |
+
|
| 227 |
+
|
| 228 |
+
if __name__ == "__main__":
|
| 229 |
+
parser = argparse.ArgumentParser()
|
| 230 |
+
parser.add_argument("--quick", action="store_true",
|
| 231 |
+
help="Run 10 random instances and print a table")
|
| 232 |
+
parser.add_argument("--instances", type=int, default=10)
|
| 233 |
+
args = parser.parse_args()
|
| 234 |
+
if args.quick:
|
| 235 |
+
run_quick(args.instances)
|
| 236 |
+
else:
|
| 237 |
+
import subprocess
|
| 238 |
+
r = subprocess.run(
|
| 239 |
+
[sys.executable, "-m", "pytest", __file__, "-x", "-v", "--tb=short"],
|
| 240 |
+
cwd=pathlib.Path(__file__).parent,
|
| 241 |
+
)
|
| 242 |
+
sys.exit(r.returncode)
|
verification/run_alive2_crucible.sh
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
# Layer 2a: Formal Equivalence Check via Alive2 / LLVM opt
|
| 3 |
+
# Proves: LLVM IR of extracted kernels β reference semantics
|
| 4 |
+
#
|
| 5 |
+
# Prerequisites (Linux/WSL):
|
| 6 |
+
# apt install llvm clang alive2 OR build from source at
|
| 7 |
+
# https://github.com/AliveToolkit/alive2
|
| 8 |
+
#
|
| 9 |
+
# On Windows: run inside WSL or Docker with LLVM toolchain.
|
| 10 |
+
# This script is a no-op (exit 0) if alive2 is not installed
|
| 11 |
+
# so CI doesn't break on dev machines without the toolchain.
|
| 12 |
+
|
| 13 |
+
set -euo pipefail
|
| 14 |
+
REPO="$(cd "$(dirname "$0")/.." && pwd)"
|
| 15 |
+
BUILD="$REPO/build"
|
| 16 |
+
|
| 17 |
+
if ! command -v alive-tv &>/dev/null; then
|
| 18 |
+
echo "alive2 not found β skipping formal equivalence check"
|
| 19 |
+
echo "Install: https://github.com/AliveToolkit/alive2"
|
| 20 |
+
exit 0
|
| 21 |
+
fi
|
| 22 |
+
|
| 23 |
+
echo "=== Layer 2a: Alive2 formal equivalence ==="
|
| 24 |
+
|
| 25 |
+
# Compile kernel to LLVM IR (unoptimised = reference)
|
| 26 |
+
clang++ -std=c++20 -O0 -S -emit-llvm \
|
| 27 |
+
-I "$REPO/include" \
|
| 28 |
+
"$REPO/src/sovereign_export.cpp" \
|
| 29 |
+
"$REPO/src/sovereign_array.cpp" \
|
| 30 |
+
-o "$BUILD/sovarr_O0.ll" 2>&1
|
| 31 |
+
|
| 32 |
+
# Optimised version
|
| 33 |
+
clang++ -std=c++20 -O3 -S -emit-llvm \
|
| 34 |
+
-I "$REPO/include" \
|
| 35 |
+
"$REPO/src/sovereign_export.cpp" \
|
| 36 |
+
"$REPO/src/sovereign_array.cpp" \
|
| 37 |
+
-o "$BUILD/sovarr_O3.ll" 2>&1
|
| 38 |
+
|
| 39 |
+
# Check: O3 β‘ O0 for the key exported functions
|
| 40 |
+
for fn in sovarr_softmax sovarr_face_centroid sovarr_nand sovarr_nand_attention; do
|
| 41 |
+
echo -n " Checking $fn ... "
|
| 42 |
+
alive-tv "$BUILD/sovarr_O0.ll" "$BUILD/sovarr_O3.ll" \
|
| 43 |
+
--func "$fn" --smt-to=30 2>&1 | tail -1
|
| 44 |
+
done
|
| 45 |
+
|
| 46 |
+
echo "=== Layer 2a: PASS ==="
|