cubical-transport-hott-lean4/CubicalBench.lean
Maximus Gorog 31d19f655e
Some checks are pending
Lean Action CI / build (push) Waiting to run
Split: engine = cubical-transport HoTT only
Restructure to engine-only contents.  Application code (Topolei.*
namespace, canvas-rs / render Rust crates, Main / ProbeTest, naga IR
pipeline, Selection / Subobject / Trace / Obs.Ctx hypothesis stack,
cells-spec / HYPOTHESES / STATUS / NAGA_IR_PLAN docs) moves to the
sibling repo max/topolei.

What moved:
- `Topolei/Cubical/*.lean` (22 files) → `CubicalTransport/*.lean`
  with namespace `Topolei.Cubical.*` renamed to `CubicalTransport.*`.
  Fully-qualified test types `TopoleiCubical{FFI,Property}Test` →
  `CubicalTransport{FFI,Property}Test` for consistency.
- New root file `CubicalTransport.lean` re-exporting all 22 modules.
- Lakefile: package `cubicalTransport`; lib `CubicalTransport`; only
  `cubical-test` and `cubical-bench` exes (no GPU link path).

The split criterion: anything an AI shortcut could break that would
cascade-corrupt downstream proofs lives here.  Anything that would
only break the application stays in the topolei interface repo.

cubical-test passes 62/62 (smoke + properties) on the renamed engine.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:35:01 -06:00

128 lines
4.6 KiB
Text

import CubicalTransport.Readback
import CubicalTransport.FFI
/-!
CubicalBench.lean — Phase D.2 performance benchmarks.
Measures throughput of the Rust-backed cubical evaluator. Uses an
`IO.Ref` accumulator to prevent dead-code elimination (the discarded
results of pure functions can otherwise be optimized away, even
through `extern` boundaries).
Invoke: `./.lake/build/bin/cubical-bench`
-/
-- ── Helpers ────────────────────────────────────────────────────────────────
/-- Encode a CVal into a single-byte tag for accumulator accounting.
Forces the full `eval` result to be observed. -/
def cvalTag : CVal → UInt8
| .vneu _ => 2
| .vlam _ _ _ => 0
| .vplam _ _ _ => 1
| .vpair _ _ => 8
| _ => 255
def dimExprTag : DimExpr → UInt8
| .zero => 0
| .one => 1
| .var _ => 2
| .inv _ => 3
| .meet _ _ => 4
| .join _ _ => 5
def faceTag : FaceFormula → UInt8
| .bot => 0 | .top => 1
| .eq0 _ => 2 | .eq1 _ => 3
| .meet _ _ => 4 | .join _ _ => 5
def ctermTag : CTerm → UInt8
| .var _ => 0
| .lam _ _ => 1
| .app _ _ => 2
| .plam _ _ => 3
| .papp _ _ => 4
| .pair _ _ => 10
| _ => 255
def timedRun (label : String) (iters : Nat) (work : IO UInt8) :
IO UInt64 := do
let t0 ← IO.monoNanosNow
let mut acc : UInt64 := 0
for _ in [0:iters] do
let x ← work
acc := acc + x.toUInt64
let t1 ← IO.monoNanosNow
let totalNs := (t1 - t0)
let nsPerIter := if iters > 0 then totalNs / iters else totalNs
let totalMs := totalNs.toFloat / 1_000_000.0
IO.println s!" {label}: {iters} iters in {totalMs}ms ({nsPerIter} ns/iter)"
return acc
-- ── Workloads (each returns a UInt8 derived from the result) ──────────────
def work_dimexpr_normalize : IO UInt8 := do
let exprs : List DimExpr := [
.inv .zero,
.inv (.inv (.var ⟨"i"⟩)),
.meet (.var ⟨"i"⟩) (.inv .zero),
.join (.inv (.var ⟨"j"⟩)) .zero,
.inv (.meet (.var ⟨"i"⟩) (.join (.var ⟨"j"⟩) .one))
]
let mut acc : UInt8 := 0
for e in exprs do
acc := acc ^^^ (dimExprTag (DimExpr.normalize e))
return acc
def work_face_normalize : IO UInt8 := do
let faces : List FaceFormula := [
.meet .top (.eq0 ⟨"i"⟩),
.meet (.eq0 ⟨"i"⟩) .bot,
.join .bot (.eq1 ⟨"j"⟩),
.join (.eq0 ⟨"i"⟩) .top,
.meet (.meet .top (.eq0 ⟨"i"⟩)) (.meet (.eq1 ⟨"j"⟩) .top)
]
let mut acc : UInt8 := 0
for φ in faces do
acc := acc ^^^ (faceTag (FaceFormula.normalize φ))
return acc
def work_lambda_beta : IO UInt8 := do
let v1 := eval .nil (.app (.lam "x" (.var "x")) (.var "y"))
let v2 := eval .nil
(.app (.app (.lam "x" (.lam "y" (.var "x"))) (.var "a")) (.var "b"))
let v3 := eval .nil
(.app (.app (.lam "f" (.lam "x" (.app (.var "f") (.var "x"))))
(.var "g")) (.var "z"))
return (cvalTag v1) ^^^ (cvalTag v2) ^^^ (cvalTag v3)
def work_sigma : IO UInt8 := do
let v1 := eval .nil (.fst (.pair (.var "a") (.var "b")))
let v2 := eval .nil (.snd (.pair (.var "a") (.var "b")))
let v3 := eval .nil (.fst (.snd (.pair (.var "x") (.pair (.var "y") (.var "z")))))
return (cvalTag v1) ^^^ (cvalTag v2) ^^^ (cvalTag v3)
def work_readback : IO UInt8 := do
let t : CTerm := .lam "f" (.lam "x" (.app (.var "f") (.var "x")))
let v := eval .nil t
let back := readback v
return (cvalTag v) ^^^ (ctermTag back)
-- ── Driver ─────────────────────────────────────────────────────────────────
def main : IO Unit := do
IO.println "── Topolei cubical perf benchmarks ──"
IO.println "Rust-backed evaluator (native staticlib). Accumulator prevents DCE."
IO.println ""
let iters := 100_000
let mut tally : UInt64 := 0
tally := tally + (← timedRun "DimExpr.normalize (5 ops/iter)" iters work_dimexpr_normalize)
tally := tally + (← timedRun "FaceFormula.normalize (5 ops/iter)" iters work_face_normalize)
tally := tally + (← timedRun "λ β-reductions (3 ops/iter)" iters work_lambda_beta)
tally := tally + (← timedRun "Σ projections (3 ops/iter)" iters work_sigma)
tally := tally + (← timedRun "readback λ-form (1 op/iter)" iters work_readback)
IO.println ""
IO.println s!"Accumulator (forces observation): {tally}"
IO.println ""
IO.println "ns/iter divides total elapsed by iterations; each iter"
IO.println "does N operations listed in parens, plus accumulator work."