cubical-transport-hott-lean4/native/cubical/src/composition.rs
Maximus Gorog c2e3ecb3e3
Some checks are pending
Lean Action CI / build (push) Waiting to run
Initial commit: topolei — cubical-transport HoTT in Lean 4 + Rust FFI
Implements the cells-spec vision: a computation space that preserves
auditability, correctness, interactivity. Phase 1 (Lean kernel +
naga-IR Rust backend) is closed; foundation hypothesis stack
(Selection H1+H2, Subobject H3, Trace H5, Obs.Ctx C2, Cubical.Trace)
landed.

Highlights:
- Cubical-HoTT syntax + value/eval/readback in Lean
- naga-IR pipeline (no GLSL string crosses FFI; 17/17 probes pass)
- Honesty audit: every non-transport (sealed cells, vertex shader,
  Y-flip, presentation conventions) is documented as such
- Polymorphic Trace α as free monoid; Cubical.Trace gives
  CTerm → Trace CTerm by structural fold (homomorphism = definition)
- Selection as Huet zipper; Subobject as Boolean algebra over WCell
- All theorems proven; the proof IS the implementation

See STATUS.md for the resume guide.

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

269 lines
10 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! # composition
//!
//! Rust implementation of `vHCompValue`, `vCompAtTerm`, `vCompNAtTerm`
//! — CCHM composition at the value level (Cubical/Eval.lean).
//!
//! `vHCompValue A φ tube base` — homogeneous composition:
//! - φ = .top → `vPApp tube .one` (tube's 1-endpoint)
//! - A = .pi → `.vHCompFun codA φ tube base` (Π β later)
//! - else → `.vneu (.nhcomp A φ tube base)`
//!
//! `vCompAtTerm env i A φ u t` — heterogeneous composition (CTerm-input):
//! - φ = .top → `eval env (u.substDim i .one)` (C1)
//! - φ = .bot → `eval env (.transp i A .bot t)` (C2)
//! - A dim-absent from i → hcomp via `.plam i u` as tube (C3-like)
//! - A = .pi → `.vCompFun env i domA codA φ u t`
//! - else → `.vneu (.ncomp i A φ (eval u) (eval t))`
//!
//! `vCompNAtTerm env i A clauses t` — multi-clause comp. Scans the list:
//! - any `.top` clause fires (CCHM multi-clause full-system rule)
//! - strip `.bot` clauses (unsatisfiable)
//! - 0 live clauses → plain transport of base
//! - 1 live clause → delegate to `vCompAtTerm`
//! - multiple → `.vneu (.ncompN env i A (evaled_live) (evaled t))`
use crate::lean_runtime::*;
use crate::tags::*;
use crate::value::*;
const LIST_NIL: u32 = 0;
const LIST_CONS: u32 = 1;
const PROD_MK: u32 = 0;
// ── vHCompValue ─────────────────────────────────────────────────────────────
/// Homogeneous composition at the value level.
/// Takes borrowed `a`, `phi`; owned `tube` and `base`. Returns owned.
pub fn vhcomp_value(
a: LeanObj, phi: LeanObj, tube: LeanObjMut, base: LeanObjMut,
) -> LeanObjMut {
// (1) φ = .top → vPApp tube .one
if ctor_tag(phi) == FACE_TOP {
release(base as LeanObj);
// Construct DimExpr.one.
let one = lean_box_mut(DIM_ONE as usize);
// vPApp consumes tube and r.
return crate::eval::vpapp(tube, one as LeanObj);
}
// (2) A = .pi → .vHCompFun closure
if ctor_tag(a) == TY_PI {
let cod_a = ctor_field(a, 1);
retain(cod_a); retain(phi);
return mk_vhcompfun(cod_a, phi, tube as LeanObj, base as LeanObj);
}
// (3) Stuck — .vneu (.nhcomp A φ tube base)
retain(a); retain(phi);
let nhcomp = mk_nhcomp(a, phi, tube as LeanObj, base as LeanObj);
mk_vneu(nhcomp as LeanObj)
}
// ── vCompAtTerm ─────────────────────────────────────────────────────────────
/// Heterogeneous composition taking CTerm inputs (so C1 can substitute at
/// the term level).
/// All arguments borrowed; returns owned CVal.
pub fn vcomp_at_term(
env: LeanObj, i: LeanObj, a: LeanObj, phi: LeanObj,
u: LeanObj, t: LeanObj,
) -> LeanObjMut {
// (1) φ = .top → C1: eval env (u.substDim i .one)
if ctor_tag(phi) == FACE_TOP {
let one = lean_box_mut(DIM_ONE as usize);
let u_at_1 = crate::subst::cterm_subst_dim(i, one as LeanObj, u);
let result = crate::eval::eval(env, u_at_1 as LeanObj);
release(u_at_1 as LeanObj);
return result;
}
// (2) φ = .bot → C2: eval env (.transp i A .bot t)
if ctor_tag(phi) == FACE_BOT {
// Build the transp term.
retain(i); retain(a);
let bot = lean_box_mut(FACE_BOT as usize);
let transp_term = alloc_ctor(TERM_TRANSP, 4);
ctor_set_field(transp_term, 0, i);
ctor_set_field(transp_term, 1, a);
ctor_set_field(transp_term, 2, bot as LeanObj);
retain(t);
ctor_set_field(transp_term, 3, t);
let result = crate::eval::eval(env, transp_term as LeanObj);
release(transp_term as LeanObj);
return result;
}
// (3) A dim-absent from i → hcomp on A with `.plam i u` as tube
if crate::dim_absent::ctype_absent(i, a) {
// Build `.plam i u`.
retain(i); retain(u);
let plam_u = alloc_ctor(TERM_PLAM, 2);
ctor_set_field(plam_u, 0, i);
ctor_set_field(plam_u, 1, u);
let tube_val = crate::eval::eval(env, plam_u as LeanObj);
release(plam_u as LeanObj);
let base_val = crate::eval::eval(env, t);
return vhcomp_value(a, phi, tube_val, base_val);
}
// (4) A = .pi → .vCompFun closure
if ctor_tag(a) == TY_PI {
let dom_a = ctor_field(a, 0);
let cod_a = ctor_field(a, 1);
retain(env); retain(i);
retain(dom_a); retain(cod_a); retain(phi);
retain(u); retain(t);
return mk_vcompfun(env, i, dom_a, cod_a, phi, u, t);
}
// (5) Stuck — .vneu (.ncomp i A φ (eval u) (eval t))
let u_val = crate::eval::eval(env, u);
let t_val = crate::eval::eval(env, t);
retain(i); retain(a); retain(phi);
let ncomp = mk_ncomp(i, a, phi, u_val as LeanObj, t_val as LeanObj);
mk_vneu(ncomp as LeanObj)
}
// ── vCompNAtTerm ────────────────────────────────────────────────────────────
/// Find the first clause whose face is `.top`. Returns the matching body
/// CTerm (borrowed) or null pointer if none found.
fn find_top_clause(clauses: LeanObj) -> LeanObj {
let mut cur = clauses;
loop {
match ctor_tag(cur) {
LIST_NIL => return core::ptr::null(),
LIST_CONS => {
let head = ctor_field(cur, 0);
let face = ctor_field(head, 0);
if ctor_tag(face) == FACE_TOP {
return ctor_field(head, 1); // body
}
cur = ctor_field(cur, 1);
}
_ => return core::ptr::null(),
}
}
}
/// Build a new clause list containing only the non-`.bot` clauses of the
/// input. Fields retained. Returns the length as well.
fn filter_live_clauses(clauses: LeanObj) -> (LeanObjMut, u32) {
// Collect non-bot clauses via a simple recursive traversal.
build_live(clauses)
}
/// Recursive helper; returns `(list, length)`.
fn build_live(cur: LeanObj) -> (LeanObjMut, u32) {
match ctor_tag(cur) {
LIST_NIL => (lean_box_mut(LIST_NIL as usize), 0),
LIST_CONS => {
let head = ctor_field(cur, 0);
let face = ctor_field(head, 0);
let tail = ctor_field(cur, 1);
let (tail_list, tail_len) = build_live(tail);
if ctor_tag(face) == FACE_BOT {
// Drop this clause.
(tail_list, tail_len)
} else {
// Retain head pair fields and rebuild.
let body = ctor_field(head, 1);
retain(face); retain(body);
let new_pair = alloc_ctor(PROD_MK, 2);
ctor_set_field(new_pair, 0, face);
ctor_set_field(new_pair, 1, body);
let new_cons = alloc_ctor(LIST_CONS, 2);
ctor_set_field(new_cons, 0, new_pair as LeanObj);
ctor_set_field(new_cons, 1, tail_list as LeanObj);
(new_cons, tail_len + 1)
}
}
_ => (lean_box_mut(LIST_NIL as usize), 0),
}
}
/// Evaluate every clause's body CTerm under `env`, producing a new
/// `List (FaceFormula × CVal)`.
fn eval_clause_bodies(env: LeanObj, clauses: LeanObjMut) -> LeanObjMut {
eval_clauses_rec(env, clauses as LeanObj)
}
fn eval_clauses_rec(env: LeanObj, cur: LeanObj) -> LeanObjMut {
match ctor_tag(cur) {
LIST_NIL => lean_box_mut(LIST_NIL as usize),
LIST_CONS => {
let head = ctor_field(cur, 0);
let face = ctor_field(head, 0);
let body = ctor_field(head, 1);
let tail = ctor_field(cur, 1);
retain(face);
let body_val = crate::eval::eval(env, body);
let new_pair = alloc_ctor(PROD_MK, 2);
ctor_set_field(new_pair, 0, face);
ctor_set_field(new_pair, 1, body_val as LeanObj);
let new_tail = eval_clauses_rec(env, tail);
let new_cons = alloc_ctor(LIST_CONS, 2);
ctor_set_field(new_cons, 0, new_pair as LeanObj);
ctor_set_field(new_cons, 1, new_tail as LeanObj);
new_cons
}
_ => lean_box_mut(LIST_NIL as usize),
}
}
/// Multi-clause heterogeneous composition.
pub fn vcompn_at_term(
env: LeanObj, i: LeanObj, a: LeanObj,
clauses: LeanObj, t: LeanObj,
) -> LeanObjMut {
// (1) Any `.top` clause → fire it at i := 1.
let top_body = find_top_clause(clauses);
if !top_body.is_null() {
let one = lean_box_mut(DIM_ONE as usize);
let u_at_1 = crate::subst::cterm_subst_dim(i, one as LeanObj, top_body);
let result = crate::eval::eval(env, u_at_1 as LeanObj);
release(u_at_1 as LeanObj);
return result;
}
// (2) Strip `.bot` clauses.
let (live, len) = filter_live_clauses(clauses);
match len {
0 => {
// No clauses — plain transport of base.
release(live as LeanObj);
retain(i); retain(a);
let bot = lean_box_mut(FACE_BOT as usize);
let transp_term = alloc_ctor(TERM_TRANSP, 4);
ctor_set_field(transp_term, 0, i);
ctor_set_field(transp_term, 1, a);
ctor_set_field(transp_term, 2, bot as LeanObj);
retain(t);
ctor_set_field(transp_term, 3, t);
let result = crate::eval::eval(env, transp_term as LeanObj);
release(transp_term as LeanObj);
result
}
1 => {
// Single clause — delegate to vCompAtTerm.
let head = ctor_field(live as LeanObj, 0);
let face = ctor_field(head, 0);
let body = ctor_field(head, 1);
let result = vcomp_at_term(env, i, a, face, body, t);
release(live as LeanObj);
result
}
_ => {
// Stuck — produce ncompN with evaluated clause bodies.
let evaled_clauses = eval_clause_bodies(env, live);
let t_val = crate::eval::eval(env, t);
retain(env); retain(i); retain(a);
let ncompn = mk_ncompn(
env, i, a, evaled_clauses as LeanObj, t_val as LeanObj);
mk_vneu(ncompn as LeanObj)
}
}
}