Programs are point clouds. Convergence is a Betti number.
A DSL where persistent homology is a language primitive, loops terminate on topological invariants,
and the whole runtime compiles no_std down to bare metal.
🚀 Quick Start •
📊 Status •
📐 Theory •
📈 Results •
💀 Failures •
❓ FAQ •
Invented by Teerth Sharma · github.com/teerthsharma/Aether-Lang
teerthsharma@outlook.com
Orientation
- Before You Read This
- The 30-Second Pitch
- Abstract
- Why I Did This To Myself
- Honest Status Dashboard
- Quick Start — Five Minutes To A Betti Number
The language
The argument
- Background
- Theoretical Foundation — 15 numbered results, each implemented
- 1) The Vietoris–Rips filtration · 2) Persistence via Z₂ column reduction · 3) The stability theorem · 4) The regular-polygon chord
- 5) Bottleneck and Wasserstein · 6) Persistence landscapes · 7) The elder rule as a block score · 8) Takens' embedding theorem
- 9) The lazy witness complex · 10) Persistent entropy · 11) Persistence images · 12) The placement statistic
- 13) The routing gap ratio · 14) Numerically stable softmax · 15) Chebyshev's inequality as an allocator guard
The code
- Implementation
- The Persistence Engine In Detail
- The Attention Subsystem In Detail
- The Scheduled Attention Port In Detail
- The ML Subsystem
- The Runtime Substrate — memory · governor · aether · manifold · topology · state
- The Kernel In Detail
- The Lean Formalization
- Complexity Reference
- The Duplicate Crate Problem
- Contributing
- How To Read This Repository
The evidence
- The Test Suite, Test By Test
- Results
- Mutation Testing, Or How I Learned To Stop Trusting Green Checkmarks
- What We Got Wrong
- Reproducing Every Number In This Document
The rest
- Design Decisions That Seemed Good At 3 AM
- The FAQ Nobody Asked For
- Worked Examples
- Repository Layout
- Requirements
- Building And Testing
- Limitations
- Glossary Of Words I Made Up
- License
⚠️ Content warning: this README contains strong opinions about algebraic topology, an unreasonable number of tables, and at least one section where a headline result gets dismantled by its own test suite. If you believe that a passing test suite means the code is correct, the following may cause discomfort.
Aether-Lang is a programming language where persistent homology is a builtin. Not a library you import. A builtin, next to if and while, with a keyword and a parser rule and everything.
The pitch is one sentence: some loops should terminate when the shape of the data stops changing, not when a float gets small. Everything else here is consequence.
What this README promises:
- Every number is measured. If it was not run, it is marked unmeasured and stays out of the tables.
- Every claim names what it was compared against. A speedup with no baseline is a vibe.
- The negative results get their own section, near the top, with the numbers that killed them.
- Sarcasm, because I have spent real hours of my finite life discovering that
multiboot2::loadwas removed in 0.24.
What this README does not promise:
- Brevity. You saw the table of contents.
- That the language is production-ready. It is a research language. It has a seal emoji as a keyword.
- GPU acceleration of the language or the topology engine. There is a real GPU backend —
aether-gpu, 92 tests, measured on an RTX 4060 — and nothing inaether-coreoraether-langcalls it. The cost and precision of both candidate integrations are measured; the integrations are not made. An earlier version of this line said there was no GPU at all, which was true of thewgpudependency it described and is no longer true of the tree.
Grab a coffee. There are 24,180 lines of Rust and 10,474 lines of Lean below, and roughly a third of this document is about the ways I was wrong.
(Those two figures replaced 21,262 and 11,652, which appeared in this README until a rewrite re-counted them. Neither was reproducible. If a document is going to insist that every number carries a command, it has to survive that rule being pointed at itself. The command is in Reproducing Every Number.)
What if a while loop could ask "has the topology stabilised?" instead of "is the error small?"
🦭 until convergence(1e-6) {
regress { model: "polynomial", escalate: true }~
}
That is a seal loop. It runs until the Betti numbers of the residual manifold stop changing, not until a scalar dips below a threshold. The distinction matters when your loss is noisy but your structure is stable — the loop exits on the shape, and the shape is a discrete invariant, so it does not jitter.
Underneath, aether-core computes exact persistent homology over 𝔽₂ for H₀, H₁ and H₂, in no_std, with a bounded simplex budget so a runaway workload fails fast instead of eating your RAM.
Because 🦭 is a valid identifier in the lexer and I thought it was funny at 2 AM. It is still funny. seal until also works if your terminal has opinions.
| Capability | Aether-Lang | ripser | GUDHI | giotto-tda |
|---|---|---|---|---|
| Persistent homology H₀/H₁/H₂ | ✅ | ✅ | ✅ | ✅ |
| Bottleneck / Wasserstein distance | ✅ | ❌ | ✅ | ✅ |
| Landscapes / images | ✅ | ❌ | ✅ | ✅ |
| Topology as a language primitive | ✅ | ❌ | ❌ | ❌ |
| Convergence on Betti numbers | ✅ | ❌ | ❌ | ❌ |
no_std, runs with no OS |
✅ | ❌ | ❌ | ❌ |
| Bare-metal x86_64 kernel | ✅ | ❌ | ❌ | ❌ |
| Core math dependencies | 3 declared (libm, heapless, nalgebra), 2 used |
many | many | many |
| Mature, fast, widely used | ❌ | ✅ | ✅ | ✅ |
| Verified against the others | ❌ not yet | — | — | — |
That last row is the important one and it is deliberately in the table. External parity against a pinned ripser or gudhi is not done. The invariant suite is not parity — a self-consistently wrong implementation can satisfy every internal property. It is gated in the ledger and it is the single largest correctness debt in this repository.
Aether-Lang is a research programming language in which persistent homology is a first-class primitive rather than a library call. The core insight is that many iterative numerical procedures have a structural fixed point that arrives before, and is more stable than, their scalar one: the Betti numbers of the residual point cloud stop changing while the loss is still fluctuating. Aether-Lang exposes that as control flow — a seal until convergence(ε) loop terminates on a topological invariant. The implementation is a bounded exact 𝔽₂ persistence engine over Vietoris–Rips and lazy-witness complexes, supporting H₀ through H₂, written in no_std Rust with libm as its only mathematical dependency, so the same code runs under a CLI on Windows and inside a bare-metal x86_64 kernel with no operating system beneath it. On top of the engine sit the standard diagram metrics (exact bottleneck via threshold-graph matching, exact p-Wasserstein via Hungarian assignment) and vectorizations (landscapes, images, persistent entropy). The engine is validated by 11 property tests encoding the Cohen-Steiner–Edelsbrunner–Harer stability theorem and six other invariants, mutation-tested against 8 injected defects, and checked against a closed-form ground truth it reproduces to 1e-12. Indexing the simplex-face lookup reduced the scale test suite from 29.07 s to 1.10 s, a 26× improvement on identical assertions. A port of the topology-derived sparse attention kernel merged as triton-lang/kernels#22 reproduces its CSR block schedules exactly and achieves 58.8% block reduction at test scale — and a same-budget ablation against random and oracle selection finds that the schedule's cost reduction is real while its selection is not: the topological ranking recovers less attention mass than choosing blocks at random, the deficit widens as more budget is given to it, and inverting the ranking beats random by a margin that grows instead.
(a short, mildly unhinged history)
Somebody once looked at a training loop, watched the loss oscillate for the four hundredth epoch, and thought: the loss is lying to me. The answer stopped changing shape twenty epochs ago.
That somebody was me. It was not a healthy thought, but it was a correct one.
Topological data analysis has a beautiful property almost nobody exploits in control flow: Betti numbers are integers. They do not wobble. A loss of 0.0341 versus 0.0339 is noise; β₁ going from 3 to 1 is an event. If your convergence criterion is a discrete invariant, you get a stopping rule with no epsilon-tuning ritual attached.
So I built a language where that is a keyword.
Because I wanted it to run on a kernel with no OS, and scikit-tda has opinions about numpy that a bare-metal x86_64 target does not share. aether-core is no_std, computes its mathematics against libm, and compiles for thumbv7m-none-eabi — a Cortex-M3. Try that with gudhi.
(It also declares nalgebra, which it does not use. That is a defect, not a feature, and it is written up below rather than papered over.)
Also because a library call is a library call, but a keyword changes how you think. 🦭 until convergence(1e-6) reads as a loop. while not tda.has_converged(diagram, prev, 1e-6): reads as homework.
I should. You should. ripser is excellent, it is fast, and it has been checked by more people than have ever read this file. If you need production TDA, use ripser.
This repository exists because I wanted the primitives inside a runtime that also owns the scheduler and the allocator, and because I wanted to know what happens when topology makes execution decisions rather than describing data after the fact. Different questions. One of them has a market.
Partway through this project I ran an over-engineering audit on my own codebase and discovered:
- The CI workflow triggered on
mainanddevelop. This repository's branch ismaster. No CI run had ever executed. Not once, in the entire history of the project. - Consequently,
aether-kernelhad silently stopped compiling. - The
Dockerfilecopied crate directories from paths that have never existed. pyproject.tomlwas publishing the description "Current World's Fastest Agentic AI Language" to PyPI, three files away from a README section titled Evidence Policy that explicitly forbids unverified speedup claims.
All fixed; receipts in PR #177. The lesson, offered freely: a green checkmark you have never seen is not a green checkmark.
The rule: a row is Active only if a command in .github/workflows/ci.yml produces its evidence. Test count in a file is not evidence if the file never runs.
A third status was needed once the GPU backend arrived. 🖥️ Hardware-gated means the tests need an adapter no CI runner has. They were briefly worse than useless: a test that returns early on a missing adapter passes, so cargo test --workspace reported roughly forty GPU tests green while executing none of them — the exact green checkmark this document spends a section warning about, built by its own author. They are now #[ignore]d behind an off-by-default gpu feature, so the same run prints 0 passed; 38 ignored. CI still compiles them with cargo build -p aether-gpu --tests --features gpu, so a broken one is caught rather than hidden behind the ignore. Asking for the hardware tests and finding no adapter now fails rather than skipping, so the feature flag is the only switch and cannot be half-honoured. The honest reading of a hardware-gated row is "verified on one developer machine", which is weaker than every other Active row here.
| Subsystem | Status | Evidence |
|---|---|---|
| Lexer, parser, AST | ✅ Active | 11 tests, cargo test -p aether-lang |
| Interpreter — assignment, loops, functions, classes | ✅ Active | 11 tests |
topology.ph / betti / intervals |
✅ Active | interpreter topology tests |
| Persistent homology H₀/H₁/H₂ | ✅ Active | 9 in-module + 11 invariants |
| Lazy witness complex | ✅ Active | persistence.rs test |
| Bottleneck / Wasserstein / landscapes / images | ✅ Active | 17 tests |
| Sparse attention reference kernel | ✅ Active | 29 contracts |
| Scheduled attention (Triton port) | ✅ Active | 16 tests |
| Scale past 32 points | ✅ Active | 7 tests |
no_std on a real embedded target |
✅ Active | builds thumbv7m-none-eabi |
| Kernel compiles bare metal | ✅ Active | builds x86_64-unknown-none |
| Titan VM language parity | 🚧 Partial | needs per-construct tests |
| Static type checking | 🚧 Partial | checker exists, diagnostics thin |
| Sparse scheduler | ⛔ Ungated | 4 tests exist and never execute — no_std bin, no test harness |
| Kernel boots | ⛔ Ungated | compiles ≠ boots; needs QEMU logs |
| External TDA parity | ⛔ Ungated | no ripser/GUDHI fixture comparison |
| Lean 4 formalization | ⛔ Ungated | 10,474 lines, 48 theorems, no lake build in CI |
GPU compute backend (aether-gpu) |
🖥️ Hardware-gated | 92 tests, RTX 4060 / Vulkan. cargo test -p aether-gpu --features gpu --release. In CI they report as ignored, not passed |
GPU used by aether-core |
⛔ Ungated | nothing routes through it; cost and precision measured, integration not made |
| Attention backward pass | ❌ Does not exist | forward only; no gradcheck possible |
| Wall-clock speedup claims | ❌ Withdrawn | see What We Got Wrong |
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
WORKSPACE GATE branch master, nightly, Win11
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
cargo fmt --all -- --check clean
cargo clippy -D correctness -D suspicious clean
cargo test --workspace --exclude aether-kernel 223 / 223 passed
cargo build -p aether-kernel --target x86_64-unknown-none ok
cargo build -p aether-core --target thumbv7m-none-eabi ok
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Rust lines (crates/) 24,180
Lean lines (Aether/) 10,474 theorems 48 sorry 0
Test suites gated in CI 7
Claims withdrawn during audit 6
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
git clone https://github.com/teerthsharma/Aether-Lang.git
cd Aether-Lang
cargo build -p aether-cli --releaseWrite loop.aether:
import topology~
let data = [1.0, 2.0, 3.0, 2.0, 1.0, 2.0, 3.0, 2.0]~
manifold M = embed(data, tau=1)~
let diagram = topology.ph(M, max_dim=1, mode="vr", max_points=16)~
let b = topology.betti(diagram, radius=0.5)~
print(b)~
Run it:
cargo run -p aether-cli -- run loop.aether═══════════════════════════════════════════════════════════════
🛡️ AEGIS - Running: loop.aether
Mode: bio
═══════════════════════════════════════════════════════════════
List([Num(4.0), Num(0.0), Num(0.0)])
Bio-Script Execution complete. 🦭
β₀ = 4, β₁ = 0, β₂ = 0 at radius 0.5. Four connected components, no loops — the periodic signal, time-delay embedded and resolved into components at that scale.
Yes, print on a list emits the runtime's Debug form. It is ugly. It is on the list. The list is long.
Other commands:
cargo run -p aether-cli -- repl # interactive
cargo run -p aether-cli -- check f.aether # parse only, no executionStatements terminate with ~. Not ;. There is no deep reason. It looked like a wave, waves are manifolds if you squint, and by the time I reconsidered the lexer had opinions.
let x = 10~
let xs = [1.0, 2.0, 3.0]~
fn dist(a, b) {
return (a - b) * (a - b)~
}
if x > 5 { print("big")~ } else { print("small")~ }
for i in xs { print(i)~ }
Keywords the lexer actually recognises — a more honest list than "the language supports":
let fn return if else for while in break continue class new self import from as true false seal until convergence escalate manifold embed block cluster regress render project dim tau model center spread color axis format output
import topology~
manifold M = embed(data, dim=3, tau=5)~
let diagram = topology.ph(M, max_dim=2, mode="vr", max_points=16)~
let b = topology.betti(diagram, radius=0.4)~
let intervals = topology.intervals(diagram)~
topology.ph calls straight into the bounded persistence engine. mode selects "vr" (Vietoris–Rips) or the lazy-witness complex. max_points and the internal simplex cap are a budget, not a correctness limit — exceeding them returns TooManyPoints or TooManySimplices rather than quietly degrading or exhausting memory.
topology.betti(diagram, radius=r) returns [β₀, β₁, β₂] at that filtration value.
🦭 until convergence(1e-6) {
regress { model: "polynomial", escalate: true }~
}
The body runs; after each iteration the interpreter recomputes the residual manifold's homology and compares Betti numbers to the previous iteration. When they stop changing — and the scalar tolerance is also satisfied — the loop exits. escalate: true promotes the regression model when the current one plateaus.
seal until is the ASCII spelling, for people whose editors are cowards.
The AST is more honest than the pitch. ConvergenceCond has three variants:
pub enum ConvergenceCond {
Epsilon(Number), // scalar tolerance
BettiStable { epochs: u32 }, // topological
Custom(Expr), // arbitrary predicate
}convergence(1e-6) parses to Epsilon. The topological stopping rule is BettiStable { epochs } — hold the Betti vector constant for epochs iterations, then exit. That epochs count is the stability_window discussed in the convergence module, surfaced into the grammar.
Stating the consequence plainly, because the 30-second pitch overstates it: convergence(1e-6) is a scalar criterion. The seal loop's headline example uses the scalar variant. The topological variant exists, is parsed, and is implemented — but a reader who assumed every seal loop stops on topology would be wrong, and this document would rather say so than let the ambiguity ride.
Every variant the parser actually produces, from StmtKind:
| Statement | Form | Notes |
|---|---|---|
Var |
let x = expr~ |
|
Assign |
x = expr~ |
|
Fn |
fn name(a, b) { ... } |
|
Return |
return expr~ |
|
If |
if cond { ... } else { ... } |
|
While |
while cond { ... } |
|
For |
for i in iterable { ... } |
|
Loop |
🦭 until convergence(ε) { ... } |
The seal loop |
Break / Continue |
break~ / continue~ |
|
Class |
class Name { ... } |
With new and self |
Import |
import topology~, from x import y~ |
|
Manifold |
manifold M = embed(data, dim=3, tau=5)~ |
First-class declaration |
Block |
block { ... } |
|
Regress |
regress { model: "polynomial", escalate: true }~ |
|
Render |
render { ... }~ |
ASCII / WebGL export |
Expr |
any expression as a statement | |
Empty |
~ |
Manifold, Block, Regress and Render being statement kinds rather than function calls is the concrete meaning of "topology is a language primitive". They have parser rules and AST nodes, not entries in a builtin table.
From ExprKind:
Literal · Ident · BinaryOp · UnaryOp · FieldAccess · Call · MethodCall · Index · Config · New · Range · List
Binary operators: + - * / % == != < > <= >= && || — as Add Sub Mul Div Mod Eq Neq Lt Gt Le Ge And Or. Unary: Neg, Not.
Two variants worth calling out:
Config(Vec<ConfigPair>) is a first-class brace-delimited configuration literal — { model: "polynomial", escalate: true }. It is what makes regress read like a declaration rather than a call with six positional arguments.
CallArg::Named { name, value } means named arguments are in the grammar, not simulated with a config object. topology.ph(M, max_dim=1, mode="vr", max_points=16) parses natively, and a reader can tell max_dim from max_points at the call site without consulting a signature.
pub enum Number {
Int(i64),
Float { int_part: i64, frac_part: i64 },
}Floats are carried as integer and fractional parts rather than as f64 at the AST level. This preserves the literal exactly as written through parsing, so 0.1 round-trips as 0.1 rather than as the nearest double's decimal expansion. Evaluation converts to f64; the AST keeps the source form.
| File | Lines | Role |
|---|---|---|
interpreter.rs |
1,803 | Tree-walking evaluator; holds the topology builtins, seal-loop convergence, manifold primitives |
parser.rs |
1,107 | Recursive-descent, produces a positioned AST |
vm.rs |
806 | TitanVM bytecode VM — behind the interpreter on coverage |
lexer.rs |
444 | Tokeniser, including the four-byte 🦭 codepoint |
ast.rs |
343 | The node definitions above |
ascii_render.rs |
149 | Terminal rendering for render |
webgl_export.rs |
105 | WebGL export path for render |
python.rs |
70 | pyo3 surface; the bindings package is currently empty |
The AST carries source positions, which is why aether check reports line, column, message rather than a panic backtrace. That is a small thing that is disproportionately visible to anyone who actually uses the language.
Two implementations, honestly ranked. The tree-walking interpreter is the reference; TitanVM is a bytecode VM that is behind it on construct coverage. There is no per-construct parity suite, which is why the status dashboard lists Titan VM parity as 🚧 Partial rather than green. Two execution engines with no differential test between them is a standing invitation to divergence.
Numerical convergence criteria are almost always scalar: watch a residual, wait for it to drop below ε. This works, and it has two well-known failure modes anybody who has trained anything has felt.
Scalars are noisy where structure is not. A loss oscillating in the third decimal makes you either tune a patience parameter or add a moving average, both hyperparameters you now have to defend. β₁ going 3 → 3 → 3 → 1 is not noisy. It is an integer sequence with an event in it.
Scalars are one-dimensional. They compress the entire state into a single number, then ask you to threshold it. A persistence diagram keeps the multi-scale structure. Two residual fields with identical L₂ norms can have completely different topology, and if what you care about is whether the model found the shape of the data, the norm answers a different question than the one you asked.
Discrete invariants compose. Betti numbers are homotopy invariants — unchanged under continuous deformation. That is exactly the property you want in a stopping rule: robust to the wiggle, sensitive to the event.
The cost is bounded and knowable. For n points capped at homology dimension 2, the Rips complex has at most n + C(n,2) + C(n,3) + C(n,4) simplices. You can compute that before allocating, which is why the engine takes a budget and fails fast rather than discovering the problem at 40 GB resident.
Two reasons, one principled and one petty.
The principled one: no_std. The core math computes against libm, uses heapless for fixed-capacity containers, and compiles for targets with no operating system and no allocator by default. That is not retrofitted onto a Python library; it is designed for on line one. The same persistence code backing topology.ph in the CLI runs inside aether-kernel on bare x86_64, where it informs scheduling.
The petty one: a keyword changes how you reach for a thing. When persistence is a builtin, you use it in a loop condition. When it is from gudhi import RipsComplex, you use it in a plot at the end of the notebook.
Every system below is real, currently maintained, and better than this one at the thing it was built for.
| System | Language | H₀/H₁/H₂ | Metrics | Vectorizations | no_std |
Topology as control flow | Maturity |
|---|---|---|---|---|---|---|---|
| ripser | C++ | ✅ (Rips, fast) | ❌ | ❌ | ❌ | ❌ | Production |
| GUDHI | C++/Python | ✅ (many complexes) | ✅ | ✅ | ❌ | ❌ | Production |
| giotto-tda | Python/C++ | ✅ | ✅ | ✅ | ❌ | ❌ | Production |
| Dionysus | C++/Python | ✅ | ✅ | partial | ❌ | ❌ | Mature |
| Aether-Lang | Rust | ✅ (Rips + witness) | ✅ | ✅ | ✅ | ✅ | Research |
There is deliberately no timing column. I have not run ripser or GUDHI on the same hardware with the same inputs, and inventing a row saying "12× faster than ripser" is exactly the class of claim this repository spent an entire audit removing. When the parity harness exists, numbers go here. Until then the honest statement is:
Aether-Lang's persistence engine has not been benchmarked against ripser, GUDHI, giotto-tda or Dionysus, and has not been verified to agree with them on shared fixtures. Its correctness evidence is internal invariants plus mutation testing.
For what it is worth, the measured ceiling below (H₁ at n=300 in 131 s) strongly suggests it is substantially slower than ripser, which handles clouds orders of magnitude larger. This engine trades throughput for no_std and exactness-with-a-budget. If that is not the trade you need, use ripser.
Everything here is implemented in persistence.rs and diagram.rs. Nothing here is aspirational.
For a finite cloud
so a simplex's filtration value is its diameter:
The engine enumerates simplices up to dimension 3 (tetrahedra), giving H₀, H₁ and H₂. Faces always enter no later than their cofaces, since a face's diameter cannot exceed its coface's — every_face_is_present_and_precedes_its_coface asserts this on five complexes rather than trusting the argument.
Order simplices by
A column reducing to zero births a class; a column with surviving
The defining identity of the chain complex,
is asserted directly, on every simplex of five complexes, by boundary_of_boundary_is_zero_over_z2. If it fails, every rank downstream is meaningless — and it fails silently, which is the entire argument for testing it.
The result that makes persistence usable as a feature at all. Cohen-Steiner, Edelsbrunner and Harer: for clouds
for the Rips filtration, where
bottleneck_distance_respects_the_stability_bound asserts exactly this over 12 seed × ε combinations. It is the single most valuable test in the repository: wrong pairing, a dropped bar, a mishandled infinite death, and an early-terminating reduction all surface here and nowhere else.
The Rips complex of a circle of radius
equal to
The engine reproduces this to 1e-12 for
For diagrams
with the diagonal cost being half a bar's persistence,
Both exact. Bottleneck by binary search over the finite candidate-cost set, feasibility decided by Kuhn's augmenting-path matching on the threshold graph. Wasserstein by the Hungarian algorithm on the same ponytail: comment naming the ceiling and the upgrade path (auction or Sinkhorn), triggered when a PersistenceConfig preset raises max_points past 2048. The widest preset admits 512 points, so
Since
Each bar
and the
which is what makes a landscape a feature rather than a hash: small change in, small change out. landscape_is_one_lipschitz_in_the_bottleneck_distance asserts it.
In H₀, when two components merge the younger one dies. Run single-linkage over centroids and record, for each point, the merge distance at which its component was absorbed. That number is an H₀ death time, measuring how long the centroid stayed distinct from everything else.
This is the salience score in the scheduled attention kernel. Exactly one point scores 0 — the component never absorbed — which follows from an invariant every merge preserves: each component holds exactly one point that has never been written.
The justification for turning a scalar time series into a point cloud at all. For a dynamical system with a
is an embedding of the attractor whenever
This is the theorem that makes manifold M = embed(data, tau=1) more than a reshaping trick: it is why TimeDelayEmbedder<D>, where D is tau is
The caveat this project inherits and does not solve: the theorem is generic and asymptotic. It guarantees an embedding exists for
Rips on
where low_load() — 24 landmarks — viable on a Cortex-M3.
It is an approximation. It is not asserted anywhere in this repository to produce diagrams equal to the Rips diagram on the same cloud, and it should not be read as doing so.
Treat the normalised bar lengths of a diagram as a probability distribution. For bars
One long bar and many short ones gives low entropy — a diagram dominated by a single feature. Many comparable bars gives high entropy — no scale dominates. It is a one-number summary of how concentrated the topological signal is, which is exactly the thing a stopping rule wants to watch, and it is cheap once the diagram exists.
The other vectorization. Map each bar to birth–persistence coordinates
The weight persistence_image_weights_long_bars_more_than_short_ones pins the weighting, and sigma_controls_the_kernel_width pins
Not textbook mathematics, but the measurement instrument every attention ablation in this document is reported against, so it belongs here rather than in a footnote.
Given a selector
Placement 0 means indistinguishable from choosing keys uniformly at random. Placement 1 means matching a selector that computed every score before choosing. The normalisation is what makes numbers comparable across input distributions, since
Two ways this statistic lies, both encountered here:
Equal budget is load-bearing. A selector that declines to select — 1.00 keys per row where baselines take 5.53 — posts catastrophic placement without losing on mechanism. It lost on budget.
The denominator can vanish or invert. With a dense fallback,
The decision function that makes topological routing a conditional claim rather than a universal one. Given the H₀ barcode of the key directions, compare the merge height at which the cut is taken against the heights immediately below it:
A cloud with genuine cluster structure has a large jump at the cut — components stay separate until well past the within-cluster scale. A cloud with no structure chains: single-linkage absorbs one point at a time at nearly-equal heights, and the ratio sits near 1.
Measured separation, 6 trials each: structured minimum 2.70, chained maximum 1.04. No overlap. routing_plan uses this to decide, at runtime, whether to route or fall back to dense.
This is the sharpest form of the project's actual thesis. Not "topology makes attention faster" — that is false on uniform keys. Rather: the H₀ barcode is a sufficient statistic for deciding whether a topological method will pay, and it is cheap enough to consult before committing.
Implemented in every attention path, and stated here because the tests reference it. The identity
holds exactly — subtracting a constant from every logit leaves the result unchanged — while bounding every exponent at or below zero, so exp cannot overflow. large_logits_do_not_overflow_the_softmax and large_logits_stay_finite_across_scheduled_blocks assert it on both kernels.
The degenerate case the identity does not cover is an empty row, where the denominator is a sum over nothing and the result is 0/0. That is a policy decision, not a numerical one: this implementation returns zeros, and an_all_masked_row_returns_zeros_rather_than_nan pins the policy so a later refactor cannot quietly turn it into a NaN.
For any distribution with finite mean
The manifold heap uses this on the liveness distribution across spatial blocks. The reason it is the right inequality here is discussed in the runtime substrate section: the allocator cannot assume a distribution of object lifetimes, because that would be an assumption about user programs. Chebyshev is loose and unconditional, which is the correct trade for a guard where a false "safe" is a leak.
Platform-agnostic, no_std, one mathematical dependency.
| Module | What it computes | Notable decision |
|---|---|---|
persistence |
𝔽₂ reduction, Rips + lazy witness, H₀–H₂ | Face lookup is a BTreeMap on the zero-padded vertex array. It used to be a linear scan; see The 26x. |
diagram |
bottleneck, Wasserstein, landscapes, images, entropy | Exact, not approximate. Hungarian is cubic and says so in a comment. |
manifold |
time-delay embedding, sparse attention graphs | Takens embedding with configurable τ |
topology |
Betti numbers, shape verification | The cheap path, when you do not need a full diagram |
attention |
sparse-attention reference kernel + selectors | CPU reference. There is no GPU. |
scheduled |
CSR block-scheduled attention (Triton port) | Working set is one score tile, never seq × seq |
governor |
PID-on-manifold adaptive thresholds | The calibration knob the physical world needs |
memory |
manifold heap, entropy-regulated GC | Chebyshev's guard as a safety protocol |
ml |
regression, clustering, classification, MLP, tensors | Written from scratch, no_std |
The no_std story is not decorative:
cargo build -p aether-core --no-default-features --features no_std \
-Z build-std=core,alloc --target thumbv7m-none-eabiThat is a Cortex-M3. It builds.
Lexer → AST → parser → tree-walking interpreter, plus a bytecode VM (TitanVM) that is behind the interpreter on coverage and is honestly labelled as such.
The interpreter is 1,976 lines and holds the topology builtins, seal-loop convergence, and manifold primitives. The parser is 1,276 lines and produces a real AST with source positions, so aether check gives you line, column, message rather than a stack trace.
no_std x86_64 microkernel. Sparse event loop waking on deviation Δ ≥ ε rather than a timer tick, WFI power management, topological code authentication, and a scheduler using aether-core to decide.
cargo build -p aether-kernel -Z build-std=core,alloc --target x86_64-unknown-noneIt compiles. It is not asserted to boot — that needs QEMU logs and a hardware matrix. Compiling and booting are different claims, and conflating them is how projects acquire reputations.
Worth recording: this crate did not compile at all until the audit, because CI had never run. Four defects including multiboot2::load removed in 0.24, plus one real bug only visible once the file compiled — BootInfo::config_root returned a pointer to the eight-byte RSDP signature string where the ACPI root table address was intended.
repl, run, check. Rustyline for editing, clap for arguments. It accepts .aether and .ae; repository examples still use .aegis and .ag from an earlier name and print a warning — technical debt sitting in plain sight rather than hidden.
Everything in this section is crates/aether-core/src/persistence.rs, 783 lines, no_std.
A simplex is a zero-padded fixed vertex array plus a vertex count:
const SIMPLEX_VERTICES: usize = 4;Four slots, because the engine enumerates up to tetrahedra — which is what H₂ requires and no more. A triangle {3, 7, 11} is stored as ([3, 7, 11, 0], 3). The padding is not cosmetic: it makes the array a canonical key, which is the whole reason the BTreeMap face index works. simplex() zero-fills the unused slots and boundary_indices builds faces the same way, so two routes to the same simplex produce byte-identical keys. Combinations are generated once each, so keys are unique and the map never has to resolve a collision.
This is the single decision that bought the 26× speedup. The prior code linear-scanned simplices[..before] for every face of every simplex, which is O(m²) in the simplex count and put a hard ceiling of roughly 32 points on the entire engine.
pub struct PersistenceConfig {
pub max_homology_dim: usize,
pub max_points: usize,
pub max_simplices: usize,
pub max_radius: f64,
pub complex_kind: ComplexKind,
}Four presets ship, and their point caps are derived from the measured timings in Results, not guessed:
| Preset | max_homology_dim |
max_points |
max_simplices |
Complex | Rationale |
|---|---|---|---|---|---|
h2_default() |
2 | 48 | 1,000,000 | Vietoris–Rips | Tetrahedra are the O(n⁴) term |
h1_dense() |
1 | 128 | 1,000,000 | Vietoris–Rips | Dropping H₂ buys a much larger point budget |
h0_only() |
0 | 512 | 1,000,000 | Vietoris–Rips | Components only; cheapest useful configuration |
low_load() |
1 | 24 | 4,096 | Witness, 24 landmarks | The embedded/kernel profile |
The doc comment on h2_default states the policy directly: the caps are a fail-fast budget, not a statement about correctness. Raise them explicitly when the workload justifies the wait.
pub enum PersistenceError { /* TooManyPoints, TooManySimplices, ... */ }Exceeding a cap returns an error. It does not silently subsample, does not degrade to a coarser complex, and does not proceed until the allocator gives up. Two tests pin this behaviour explicitly — simplex_cap_still_fails_fast_rather_than_exhausting_memory and point_cap_is_still_enforced_when_configured — because the failure mode being prevented is the one that costs you a machine, and a cap nobody tested is a cap that quietly stopped working three refactors ago.
The simplex count is knowable before allocating. For n points capped at homology dimension 2, the Rips complex admits at most
simplices, so the check is arithmetic on the input, not a discovery made at 40 GB resident.
ComplexKind::VietorisRips is the exact filtration described in Theory §1.
ComplexKind::Witness { max_landmarks } is the lazy witness complex: choose max_landmarks landmark points, and admit a simplex on a landmark set when some data point witnesses it. Cost scales with the landmark count rather than the full cloud, which is what makes low_load() viable at 24 landmarks on a Cortex-M3. It is an approximation of the Rips complex and is labelled as one — it is not asserted to produce identical diagrams.
pub fn persistent_homology<const D: usize>(/* ... */) -> Result<PersistenceDiagram, PersistenceError>;
pub fn time_delay_persistence<const D: usize>(/* ... */) -> Result<PersistenceDiagram, PersistenceError>;time_delay_persistence is the composition the language actually uses: Takens-embed a scalar series, then run persistence on the resulting cloud. It exists as one function because that pairing is the common case and doing it in two steps invites a τ mismatch between the embedder and the engine.
pub struct PersistenceDiagram { /* pairs */ }
impl PersistenceDiagram {
pub fn betti_at(&self, radius: f64) -> BettiNumbers3;
}
pub struct BettiNumbers3 { /* beta_0, beta_1, beta_2 */ }betti_at is a query on a computed diagram, not a separate computation. Betti numbers at radius r are the count of bars whose interval contains r, per dimension. This is why topology.betti(diagram, radius=...) in the language takes a diagram rather than a point cloud — computing the diagram once and querying it at many radii is the cheap direction, and the API makes the cheap direction the obvious one.
crates/aether-core/src/attention.rs, 699 lines. This is a CPU reference kernel. There is no GPU, no SIMD, and no threading anywhere in it.
pub enum Selector {
Dense,
Local { window: usize },
Random { budget: usize, seed: u64 },
OracleTopK { budget: usize },
Topological { budget: usize, radius_scale: f64 },
TopologicalRouted { budget: usize, clusters: usize },
Adaptive { budget: usize, clusters: usize },
}Seven variants, and the two that are not selection strategies at all are the reason the ablations mean anything:
| Selector | Role | Why it exists |
|---|---|---|
Dense |
upper bound on quality, upper bound on cost | The thing every sparse method must beat on cost without losing quality |
Local { window } |
the trivial baseline | Sliding window; what you get with no cleverness at all |
Random { budget, seed } |
the floor | A selector that cannot beat random at equal budget has no mechanism |
OracleTopK { budget } |
the ceiling | Computes every score, then takes the true top-k. Not deployable — it is a ruler |
Topological { budget, radius_scale } |
Euclidean-proximity selection | The claim that measured negative |
TopologicalRouted { budget, clusters } |
H₀-cluster routing on key directions | The claim that survived, conditionally |
Adaptive { budget, clusters } |
routed when it pays, dense when it does not | The honest shipping default |
Random and OracleTopK are what make the placement statistic
a scale-free number rather than a raw attention mass whose magnitude depends on the input distribution. A selector at placement 0.0 is indistinguishable from random; at 1.0 it has matched an oracle that cheated. the_oracle_is_priced_as_the_diagnostic_it_is exists to stop the oracle being read as a result.
pub fn selection_dot_cost(/* ... */) -> f64;
pub fn dense_dot_cost(seq: usize, causal: bool) -> f64;These two functions exist because every test in the original suite measured how good a selection was and none measured what it cost. That omission let a selector post a +0.94 placement while examining 0.999× the dense dot-product count — dense attention wearing a clustering hat. The full autopsy is in What We Got Wrong §4.
The lesson generalises past this repository: a quality metric with no paired cost metric will eventually reward a method for doing more work.
pub struct RoutingPlan { /* gap_ratio, decision, predicted cost, ... */ }
pub fn routing_plan(/* ... */) -> RoutingPlan;routing_plan converts the conditional claim into a decision the code makes at runtime rather than an assumption the author makes at design time. It reads the H₀ barcode of the key directions, computes a gap_ratio, and decides whether routing will pay before committing to it.
The separation is clean and measured: structured minimum 2.70 against chained maximum 1.04, 6 trials each, no overlap. Three tests hold that line — the_plan_predicts_the_cost_it_will_actually_incur, the_plan_declines_to_route_exactly_when_routing_would_not_pay, and the_h0_barcode_alone_separates_the_two_regimes.
That last name is the interesting claim: the decision needs only the barcode, not the raw keys. The topology is sufficient statistics for the routing decision.
pub fn single_linkage_clusters(
points: &[f64], count: usize, dim: usize, clusters: usize, normalize: bool,
) -> (Vec<usize>, Vec<f64>);Returns (assignment, merge_heights). Two decisions worth naming:
Labels are canonicalised to first-occurrence order. Otherwise the labels depend on union-find internals and two runs on the same data are not comparable. Determinism here is not fastidiousness — it is what makes repeated_runs_are_bitwise_identical meaningful.
Ties are broken by index. The edge sort compares distance, then i, then j, so the merge order and therefore the cut do not depend on sort stability.
The merge heights are exactly the finite H₀ deaths of the Vietoris–Rips persistence of the same cloud. Rather than trusting two implementations of one theorem, single_linkage_merge_heights_equal_the_h0_persistence_deaths asserts one against the other. This is the most valuable kind of test available in a repository that implements the same mathematics twice for different reasons: cross-validation between independent code paths that must agree.
normalize projects points to the unit sphere first, making the clustering invariant to per-point rescaling — the property the routed selector needs and the nearest-neighbour selector conspicuously lacks. A zero vector has no direction, so it is left at the origin rather than having one invented for it.
crates/aether-core/src/scheduled.rs, 477 lines. A Rust port of triton-lang/kernels#22.
pub struct BlockSchedule { /* CSR: indptr + indices */ }
impl BlockSchedule {
pub fn from_rows(rows: &[Vec<usize>]) -> Result<Self, ScheduleError>;
pub fn row(&self, q_block: usize) -> &[usize];
pub fn num_blocks(&self) -> usize;
}The port preserves the original's separation of a combinatorial half from a numeric half, and that separation is what makes each half checkable alone:
| Half | Nature | Checkable against | Result |
|---|---|---|---|
| CSR block schedule | combinatorial — which blocks | the Python builder, exactly | [0,1,3,6,10] / [0,0,1,0,1,2,0,1,2,3] for 4 blocks |
| Kernel | numeric — what the blocks compute | dense masked attention | max abs diff < 1e-12, 4 schedules × 4 geometries |
A monolithic port would only be checkable end to end, where a schedule bug and a numeric bug are indistinguishable from a wrong output. Split, the schedule is compared for exact integer equality with the upstream builder — no tolerance, no float comparison, no judgement call.
pub fn dense_causal_block_schedule(num_blocks: usize) -> BlockSchedule;
pub fn block_salience(/* ... */);
pub fn topology_block_schedule(/* ... */);
pub fn scheduled_attention(/* ... */);
pub fn dense_masked_attention(/* ... */);
pub struct TopologyScheduleConfig { /* local_radius, sink, topk */ }
pub enum ScheduleError { /* ... */ }dense_masked_attention is in the shipping crate, not the test file, on purpose: it is the reference the sparse kernel is checked against, and a reference that lives only in tests tends to drift from the thing it references.
The schedule keeps one score tile live at a time, never a seq × seq matrix. This is the claim that distinguishes block-scheduled attention from masked dense attention with extra steps, and the_working_set_does_not_grow_with_the_sequence measures it rather than asserting it in a comment.
Reproduced: the CSR schedule (exactly), the numeric output (1e-12), the 58.8% block reduction at 16 blocks with local_radius=1 sink=1 topk=2, the salience-vs-H₀-deaths agreement (1e-9), and the invariant that exactly one block scores zero.
Not reproduced: the upstream wall-clock figures — 56.6% block reduction at seq 1024, 80.9% at seq 4096, and 1.04×–3.48× sparse-vs-dense-CSR — measured on an RTX 4060. This port is a scalar CPU kernel. It reproduces the answer and the block reduction, not the speed, and no table in this document claims otherwise.
crates/aether-core/src/ml/, 12 modules, ~3,990 lines, all no_std, all written from scratch against libm. The README previously gave this subtree a single table row, which undersold roughly a fifth of the codebase.
A blanket caveat that applies to everything in this section, stated once: these are correct-and-small implementations, not competitive ones. They exist so the language and the kernel have learning primitives that compile with no operating system underneath. Every one of them is outperformed by its scikit-learn equivalent by margins nobody has measured here and nobody should need to.
pub struct Tensor { /* data, shape */ }| Method | Signature | Note |
|---|---|---|
from_vec |
(Vec<f64>, Vec<usize>) -> Self |
Takes ownership |
new |
(&[f64], &[usize]) -> Self |
Copies |
zeros / ones |
(&[usize]) -> Self |
|
kaiming_uniform |
(&[usize]) -> Self |
He initialisation for ReLU-family nets |
get / set |
(&[usize]) -> f64 / (&[usize], f64) |
Multi-index |
matmul |
(&Tensor) -> Tensor |
Naive triple loop |
add / sub / mul / scale |
elementwise / scalar | |
transpose / flatten / sum / map |
map takes F: Fn(f64) -> f64 |
kaiming_uniform rather than plain uniform is the one non-obvious choice: with ReLU activations, naive initialisation puts the network in a regime where half the gradient signal is dead on arrival, and the failure looks like a bad learning rate rather than a bad initialiser.
The set method takes &self rather than &mut self — interior mutability. Worth flagging as a design wrinkle rather than leaving a reader to discover it from a type error.
There is a comment at ml/tensor.rs:6 reading "future hooks for wgpu". It is the only occurrence of wgpu in the entire tree, and it is the whole basis of a GPU dependency that ships in aether-lang's default feature set. See the FAQ.
pub enum Activation { /* ... */ }
impl Activation {
pub fn apply(&self, x: &Tensor) -> Tensor;
pub fn apply_scalar(&self, x: f64) -> f64;
pub fn derivative(&self, x: &Tensor) -> Tensor;
}
pub enum OptimizerConfig { /* ... */ }
pub enum OptimizerState { /* ... */ }
pub struct DenseLayer { /* ... */ }
impl DenseLayer {
pub fn new(/* ... */) -> Self;
pub fn init_optimizer(&mut self, config: &OptimizerConfig);
}The Activation::derivative method existing as a peer of apply rather than being folded into a backward pass is the design decision worth naming: it makes each activation's gradient independently testable, which is where activation bugs actually live. A wrong derivative does not crash — it trains to a worse optimum, slowly, and the loss curve looks fine.
OptimizerConfig and OptimizerState are split so that configuration is Copy-cheap and shareable while the per-parameter state (momentum buffers and similar) lives with the layer that owns the parameters.
302 lines. Reverse-mode automatic differentiation over the tensor type.
There is no gradcheck in CI for this module, which is stated here rather than left for a reader to notice. An autograd implementation without a finite-difference check against its own forward pass is the definition of an unverified claim, and the same argument this README makes about the missing attention backward pass applies with equal force here.
pub struct Conv2D { /* ... */ }
impl Conv2D {
pub fn new(/* ... */) -> Self;
pub fn forward(/* ... */);
}Bounded by compile-time constants, which is what makes it no_std-viable with no allocator:
const MAX_KERNEL_SIZE: usize = 5; // 3x3, 5x5
const MAX_CHANNELS_IN: usize = 3; // RGB
const MAX_CHANNELS_OUT: usize = 8;
const MAX_IMG_DIM: usize = 32;Forward only. A convolution with no backward pass is a feature extractor, not a trainable layer, and calling it a CNN would be an overclaim.
pub enum ModelType { /* ... */ }
impl ModelType { pub fn complexity(&self) -> u8; }
pub struct Coefficients { /* max 8 terms */ }
impl Coefficients { pub fn eval_polynomial(&self, x: f64) -> f64; }
pub struct ManifoldRegressor<const D: usize> { /* ... */ }
impl<const D: usize> ManifoldRegressor<D> {
pub fn fit(&mut self) -> f64;
pub fn predict(&self, point: &[f64; D]) -> f64;
pub fn upgrade_model(&mut self);
}This module is what regress { model: "polynomial", escalate: true } calls. upgrade_model promotes the model along the complexity() ordering when the current one plateaus — the machinery behind escalate: true in a seal loop.
Capped at MAX_DEGREE = 8 and MAX_POINTS = 256. The degree cap is not arbitrary: fitting a high-degree polynomial through few points is a numerically ill-conditioned way to overfit, and a cap is a cheaper defence than a condition-number check nobody will read.
pub struct KMeans<const D: usize> { /* builder: with_max_iter, with_tol, with_seed */ }
pub struct DBSCAN<const D: usize> { /* new(epsilon, min_samples) */ }
pub struct AgglomerativeClustering<const D: usize> { /* new(Linkage) */ }
pub enum Linkage { /* ... */ }
pub fn auto_k_selection<const D: usize>(data: &[[f64; D]], n: usize, epsilon: f64) -> usize;KMeans::with_seed matters more than it looks: k-means is initialisation-sensitive, and an unseeded k-means makes every downstream test flaky in a way that gets diagnosed as a tolerance problem for a week before anyone checks the initialiser.
auto_k_selection picks k from the data rather than requiring the caller to know it — the topological argument for this project in miniature, since the number of clusters is β₀ at the right scale.
AgglomerativeClustering::cut_tree(result, k) cuts the dendrogram to k components. This is the same operation as the H₀ cut in single_linkage_clusters, reached from the classical direction. Note that these are two separate implementations of overlapping mathematics in one crate — a real piece of duplication, distinct from the aegis/aether copy-paste, and not currently cross-checked against each other the way the attention clustering is cross-checked against the persistence engine.
| Type | Fit signature returns | Predicts |
|---|---|---|
LogisticRegression |
f64 (final loss) |
predict_proba → f64, predict → u32 |
KNNClassifier<D> |
— | u32 |
Perceptron |
— | i32 (sign convention) |
GaussianNB |
— | u32 |
DecisionStump |
— | i32 |
AdaBoost |
— | i32 |
The u32 / i32 split in return types is not an inconsistency to be tidied away: u32 is a class index, i32 is a ±1 margin label, and Perceptron, DecisionStump and AdaBoost are margin methods where the sign carries the meaning. DecisionStump exists specifically as AdaBoost's weak learner.
Bounded by MAX_CLASSES = 16, MAX_POINTS = 256, MAX_FEATURES = 32.
This is the module the entire language premise rests on.
pub struct BettiNumbers { /* beta_0, beta_1 */ }
impl BettiNumbers {
pub fn is_singular(&self) -> bool;
pub fn distance(&self, other: &Self) -> u32;
}
pub struct ConvergenceDetector { /* ... */ }
impl ConvergenceDetector {
pub fn new(epsilon: f64, stability_window: usize) -> Self;
pub fn record_epoch(&mut self, betti: BettiNumbers, drift: f64, error: f64);
pub fn is_converged(&self) -> bool;
pub fn convergence_score(&self) -> f64;
}
pub struct ResidualAnalyzer<const D: usize> { /* ... */ }
impl<const D: usize> ResidualAnalyzer<D> {
pub fn set_residuals(&mut self, residuals: &[f64]);
pub fn compute_betti(&self) -> BettiNumbers;
pub fn compute_drift(&self) -> f64;
pub fn is_collapsed(&self, threshold: f64) -> bool;
}
pub struct Answer { /* ... */ }
impl Answer {
pub fn from_detector(detector: &ConvergenceDetector, coefficients: [f64; 8]) -> Option<Self>;
pub fn is_perfect(&self, epsilon: f64) -> bool;
}BettiNumbers::distance is an integer metric — the L₁ distance between Betti vectors. That integrality is the entire pitch: the stopping signal cannot jitter in the third decimal because it has no decimals.
stability_window is the honest admission inside the design. Betti numbers are discrete, but a single matching pair does not establish stability — one coincidental repeat is not a fixed point. The detector requires the Betti vector to hold across a window. That window is a hyperparameter, which means topological convergence does not eliminate tuning; it moves it from a continuous threshold to a discrete count. A count is easier to reason about than an ε, and this project's claim should be read as that and not more.
ConvergenceDetector also takes drift and error alongside the Betti numbers, and is_converged consults all three. The seal loop is therefore not purely topological — it exits when the topology has stabilised and the scalar tolerance is met. This is more honest than the pitch and is stated here so the pitch cannot be read as stronger than the code.
Answer::from_detector returns Option, so "no answer has emerged yet" is representable rather than being encoded as a sentinel value someone eventually forgets to check.
| Module | Lines | Surface |
|---|---|---|
gossip |
203 | Distributed averaging, MAX_DIM = 3 |
dataloader |
127 | DataLoader, BatchIterator<'a>, batching + shuffle |
linalg |
254 | Scalar reductions and distance primitives underneath the rest |
benchmark |
255 | EscalatingBenchmark<D>, BenchmarkConfig, TestFunction, generate_test_function |
benchmark drives the model-escalation loop against standard TestFunction targets, capped at MAX_EPOCHS = 1000. It is an internal harness for the escalation policy, not a performance benchmark, and nothing in the Results tables comes from it. The name is misleading and is being flagged rather than defended.
The parts of aether-core that are not mathematics: how the runtime allocates, adapts, and prunes. These are the pieces that justify the phrase "a runtime that also owns the scheduler and the allocator".
480 lines. A no_std allocator that organises objects spatially rather than by free-list order.
pub struct Gc<T> { /* index + generation */ }
pub struct ObjectHeader { /* ... */ }
pub enum HeapSlot<T> { /* ... */ }
pub struct SpatialBlock<T> { /* N = 8, contiguous arrays */ }
pub struct SpatialNode { /* aggregates child statistics */ }
pub struct ManifoldHeap<T> { /* ... */ }
pub enum MemoryMode { /* ... */ }Gc<T> is a generational handle, not a pointer: an index plus a generation counter. A stale handle whose slot has been reused fails the generation check and returns None from get, instead of aliasing whatever now lives there. Copy/Clone are implemented manually to avoid an implicit T: Copy bound leaking onto the handle — a handle should be copyable whether or not the thing it refers to is.
SpatialBlock is fixed at N = 8 with contiguous arrays, and liveness is stored in the block rather than in the slot specifically so a liveness sweep reads one packed array instead of chasing headers. The comment in the source says this is for SIMD access; there is no SIMD in the crate today, so read it as a layout that permits vectorisation, not one that performs it.
impl<T> ManifoldHeap<T> {
pub fn alloc(&mut self, data: T) -> Gc<T>;
pub fn get(&self, handle: Gc<T>) -> Option<&T>;
pub fn get_mut(&mut self, handle: Gc<T>) -> Option<&mut T>;
pub fn touch(&mut self, handle: Gc<T>);
pub fn mark(&mut self, handle: Gc<T>);
pub fn active_count(&self) -> usize;
pub fn capacity(&self) -> usize;
}touch records access without reading the value — the recency signal the collector uses to decide which branches are cold.
pub struct ChebyshevGuard { /* ... */ }
impl ChebyshevGuard {
pub fn calculate<T>(heap: &ManifoldHeap<T>) -> Self;
pub fn is_safe(&self, liveness: f64) -> bool;
pub fn regulate_entropy<F>(&mut self, tracer: F) -> usize;
}Chebyshev's inequality bounds the fraction of a distribution lying more than k standard deviations from its mean, for any distribution, with no normality assumption:
Used here as a safety protocol: measure the liveness distribution across spatial blocks, and treat a block whose liveness sits within the Chebyshev bound as normal variation rather than as garbage. Collection triggers only outside the bound.
The reason this is the right inequality and not a stylistic one: the runtime does not know the distribution of object lifetimes, and it cannot assume one. A Gaussian assumption here would be an assumption about user programs, which is not knowledge the allocator has. Chebyshev holds regardless, at the cost of being loose — a trade that is correct for a guard, where a false "safe" is a leak and a false "unsafe" is only wasted work.
regulate_entropy takes a tracer closure and returns the number of objects reclaimed.
267 lines. The adaptive threshold controller, and the place this project takes its own advice about the physical world needing a calibration knob.
where the error is the difference between target and achieved effective tick rate,
Shipped constants, all named and all tunable:
| Constant | Value | Role |
|---|---|---|
TARGET_TICK_RATE |
1000.0 Hz | The balance point between responsiveness and efficiency |
ALPHA (α) |
0.01 | Proportional gain — response to instantaneous error |
BETA (β) |
0.05 | Derivative gain — damps oscillation |
EPSILON_MIN |
0.001 | Floor; prevents runaway sensitivity |
EPSILON_MAX |
10.0 | Ceiling; prevents the system sleeping through events |
EPSILON_INITIAL |
0.1 | Starting threshold |
impl GeometricGovernor {
pub fn with_epsilon(epsilon: f64) -> Self;
pub fn with_gains(alpha: f64, beta: f64) -> Self;
pub fn adapt(&mut self, deviation_delta: f64, dt: f64) -> f64;
pub fn should_trigger(&self, deviation: f64) -> bool;
}There is no integral term — this is PD, not PID, despite the source comment saying PID. Defensible for this application (an integral term on a threshold that is already clamped invites windup), but the comment and the code disagree and the code is right.
The stability properties claimed in the doc comment — bounded ε, asymptotic stability around the target, damped oscillation — are argued, not asserted by a test. with_gains exists precisely because the shipped α and β are a starting point for a real system, not a derivation. A clock drifts, a sensor reads off, and a workload is not the one the gains were picked against.
448 lines. A three-level tree of geometric summaries supporting query-time pruning.
const MAX_DIM: usize = 64;
const MAX_BLOCKS: usize = 128;
pub struct BlockMetadata<const D: usize> { /* ... */ }
impl<const D: usize> BlockMetadata<D> {
pub fn from_points(points: &[[f64; D]]) -> Self;
pub fn upper_bound_score(&self, query: &[f64; D]) -> f64;
pub fn can_prune(&self, query: &[f64; D], threshold: f64) -> bool;
}
pub struct HierarchicalBlockTree<const D: usize> { /* 3 levels */ }
impl<const D: usize> HierarchicalBlockTree<D> {
pub fn build_from_blocks(&mut self, blocks: &[BlockMetadata<D>]);
pub fn hierarchical_query(&self, query: &[f64; D], threshold: f64) -> [bool; MAX_BLOCKS];
pub fn pruning_ratio(&self, active_mask: &[bool; MAX_BLOCKS]) -> f64;
}| Level | Granularity | Fan-in |
|---|---|---|
| 0 | 64-token blocks | finest |
| 1 | 256-token clusters | 4 blocks |
| 2 | 1024-token super-clusters | 16 blocks |
The mechanism is an admissible upper bound: upper_bound_score computes a value no lower than any true score inside the block, so can_prune discards a subtree only when even its optimistic score falls below the threshold. Pruning is therefore exact — it changes cost, never the answer. This is the same argument branch-and-bound rests on, and it is why the bound must be an over-estimate rather than an estimate.
pruning_ratio is the honesty function: it reports what fraction was actually skipped, so a "hierarchical" query that prunes nothing is visible as such rather than being assumed effective.
pub enum CompressionStrategy { /* ... */ }
pub fn select_compression<const D: usize>(meta: &BlockMetadata<D>) -> CompressionStrategy;
pub fn estimate_compression_ratio<const D: usize>(meta: &BlockMetadata<D>) -> f64;
pub struct DriftDetector<const D: usize> { /* ... */ }
impl<const D: usize> DriftDetector<D> {
pub fn update(&mut self, centroid: &[f64; D]) -> f64;
pub fn is_drifting(&self, threshold: f64) -> bool;
pub fn velocity_magnitude(&self) -> f64;
}estimate_compression_ratio is named estimate because it is one. No measured compression figures appear anywhere in this document.
608 lines.
pub struct ManifoldPoint<const D: usize> { /* coords */ }
pub struct TimeDelayEmbedder<const D: usize> { /* new(tau) */ }
pub struct SparseAttentionGraph<const D: usize> { /* new(epsilon) */ }
pub struct GeometricConcentrator<const D: usize> { /* ... */ }
pub struct TopologicalPipeline<const D: usize> { /* new(tau, epsilon) */ }TimeDelayEmbedder::embed returns Option<ManifoldPoint<D>> — None until enough samples have arrived to fill the delay window. The type encodes the warm-up rather than returning a zero point that silently pollutes the first (D-1)·τ diagrams.
SparseAttentionGraph maintains an ε-neighbourhood graph incrementally with adjacency: [u64; MAX_POINTS] — a bitset row per point, capping the graph at 64 neighbours per node by construction. compute_betti_0 is exact (connected components); estimate_betti_1 is, as named, an estimate — a cycle-rank heuristic on the graph, not persistent homology. The naming distinction is deliberate and is the difference between this cheap path and the full engine.
GeometricConcentrator tracks which axis carries the most variance (principal_dimension) and projects to it (concentrate_1d), reporting concentration_ratio so a caller can tell whether the projection kept anything.
TopologicalPipeline::push is the whole streaming path in one call: push a scalar sample, get back Option<(β₀, β₁, u64)> once the embedder has warmed up. This is what makes the language's manifold M = embed(data, tau=1) a streaming construct rather than a batch one.
356 lines. This module shares vocabulary with the persistence engine and operates on completely different input, which is the most confusing thing in the crate and is therefore stated first:
pub fn compute_betti_0(data: &[u8]) -> u32;
pub fn compute_betti_1(data: &[u8]) -> u32;
pub fn compute_shape(data: &[u8]) -> TopologicalShape;&[u8]. Raw bytes, not point clouds. These functions compute a coarse topological signature of a byte sequence — the machinery behind the kernel's verify_binary_topology.
topology::compute_betti_0 |
persistence::persistent_homology |
|
|---|---|---|
| Input | &[u8] byte slice |
&[ManifoldPoint<D>] |
| Output | u32 |
Full PersistenceDiagram |
| Method | Windowed density clustering | Exact 𝔽₂ column reduction |
| Exact? | Heuristic | Exact, invariant-tested |
| Cost | O(len) | See complexity |
Two functions named compute_betti_0 exist in this crate — this one, and SparseAttentionGraph::compute_betti_0, which is an exact union-find over a point graph. Neither is the persistence engine. None of the 11 persistence invariants apply to this module, and a reader who assumed the phrase "Betti number" carried the same guarantees everywhere in the crate would be importing an assurance level that was never claimed.
Shipped constants:
const CLUSTER_THRESHOLD: i16 = 15;
const WINDOW_SIZE: usize = 64;
const DENSITY_MIN: f64 = 0.1;
const DENSITY_MAX: f64 = 0.6;
const MAX_BETTI_1: u32 = 10;MAX_BETTI_1 = 10 is a saturating cap, not a measurement — a byte sequence with more than ten detected cycles reports ten. Fine for a signature whose job is comparison against a reference; misleading if read as a homology computation.
pub struct TopologicalShape { /* betti_0, betti_1, data_len */ }
impl TopologicalShape { pub fn distance(&self, other: &Self) -> f64; }
pub enum VerifyResult { /* ... */ }
pub fn verify_shape(data: &[u8]) -> VerifyResult;
pub fn is_shape_valid(data: &[u8]) -> bool;
pub fn verify_against_reference(/* ... */);
pub fn verify_sliding_window(data: &[u8], window_size: usize) -> Result<(), usize>;verify_sliding_window returns Result<(), usize> — on failure, the usize is the offset where verification broke. Returning the position rather than a bare false is the difference between a check that tells you something failed and one that tells you where to look.
186 lines, and the smallest module carrying real weight.
pub struct SystemState<const D: usize> { /* vector, timestamp */ }
impl<const D: usize> SystemState<D> {
pub fn new(vector: [f64; D], timestamp: u64) -> Self;
pub fn deviation(&self, other: &Self) -> f64; // L2
pub fn max_deviation(&self, other: &Self) -> f64; // L-infinity
pub fn manhattan_deviation(&self, other: &Self) -> f64; // L1
pub fn magnitude(&self) -> f64;
pub fn elapsed_since(&self, other: &Self) -> u64;
}Three metrics, offered rather than chosen. L², L^∞ and L¹ each answer a different question about how far the system moved: total displacement, worst single component, and summed component change. The scheduler's trigger condition is Δ ≥ ε, and which Δ changes what wakes the machine — L^∞ fires when any one component spikes, L¹ fires on diffuse drift that L^∞ would sleep through.
Exposing all three rather than picking one is the right call for a substrate whose workload is not known at design time. It is also the calibration knob argument again: a real system is tuned against its actual dynamics, and a module that hard-codes L² has made that decision on the operator's behalf with no evidence.
timestamp travelling inside the state, with elapsed_since as a peer of the deviation metrics, is what lets the governor compute de/dt without a separate clock source — relevant in a kernel where "what time is it" is not a free question.
crates/aether-kernel. A no_std x86_64 microkernel that links aether-core and uses it to make scheduling decisions. It compiles for x86_64-unknown-none. It is not asserted to boot.
That distinction is maintained everywhere in this document because it is the distinction that gets blurred most often, and blurring it is how a project acquires a reputation. Compiling proves the code typechecks and links against a bare-metal target. Booting requires QEMU logs and a hardware matrix, neither of which exists here.
const HEAP_SIZE: usize = 64 * 1024;
static mut HEAP: [u8; HEAP_SIZE] = [0; HEAP_SIZE];
static ALLOCATOR: Mutex<BumpAllocator> = Mutex::new(BumpAllocator::new());
#[global_allocator]
static GLOBAL_ALLOCATOR: AegisAllocator = AegisAllocator;64 KB, bump-allocated, and it does not free. A bump allocator hands out sequential pointers and reclaims nothing until reset. This is the right allocator for a kernel that boots, sets up, and runs a fixed workload, and the wrong one for anything that allocates in a loop.
It is worth being explicit that this is a different allocator from the manifold heap in aether-core. The manifold heap is the sophisticated one with generational handles and entropy-regulated collection; the kernel's global allocator is 64 KB of bump. They serve different layers and the naming does not make that obvious.
static IDT: spin::Lazy<InterruptDescriptorTable> = /* ... */;
static CURRENT_STATE: Mutex<SystemState<STATE_DIMENSION>> = /* ... */;
static IRQ_COUNTER: spin::Mutex<u64> = spin::Mutex::new(0);
static TIMESTAMP: spin::Mutex<u64> = spin::Mutex::new(0);
pub fn get_current_state() -> SystemState<STATE_DIMENSION>;
pub fn update_state_component(index: usize, value: f64);The system state vector that the governor and scheduler read is maintained from interrupt context, behind spin mutexes. This is the load-bearing piece of the whole "topology makes execution decisions" claim: the state manifold is not a userspace abstraction observing the kernel, it is updated by the interrupt handlers themselves.
It also requires #![feature(abi_x86_interrupt)], which is why the toolchain is pinned to nightly independently of the -Z build-std requirement.
const DEFAULT_DT: f64 = 0.001;
const ENTROPY_MULTIPLIER: u64 = 6364136223846793005;
pub struct SparseScheduler<const D: usize> { /* ... */ }The scheduler wakes on deviation Δ ≥ ε rather than on a timer tick, with ε supplied by the geometric governor. Between events it issues WFI. A timer-tick scheduler does fixed work per unit time whether or not anything happened; a deviation-triggered one does work proportional to how much the state actually moved, which is the entire power argument.
ENTROPY_MULTIPLIER = 6364136223846793005 is the multiplier from Knuth's MMIX / PCG linear congruential generator. Naming it rather than leaving a magic constant matters, because a reader who does not recognise it cannot tell a well-chosen LCG multiplier from a number somebody typed.
Four tests in this file never execute. aether-kernel is a no_std binary with no test harness, so cargo test cannot reach them. They are listed as ⛔ Ungated in the status dashboard. Tests that cannot run are documentation with a misleading syntax highlight.
const ELF_MAGIC: [u8; 4] = [0x7F, b'E', b'L', b'F'];
pub enum LoadError { /* ... */ }
pub struct ElfInfo { /* ... */ }
pub fn verify_elf(data: &[u8]) -> Result<ElfInfo, LoadError>;
pub fn verify_binary_topology(data: &[u8]) -> bool;verify_elf is ordinary ELF header validation. verify_binary_topology is the unusual one: it computes a topological signature of the binary's byte distribution and checks it against an expected shape.
This is not a security mechanism and must not be read as one. It is a structural plausibility check — it would catch a truncated or corrupted image, not an adversary, because an adversary who knows the check exists can trivially pad a payload to match a byte-distribution signature. Cryptographic signing is what authenticates code. This is a cheap integrity heuristic with an interesting name, and calling it "authentication" in the source is an overclaim that this section is correcting rather than repeating.
pub struct BootInfo { /* ... */ }
pub struct MemoryRegion { /* ... */ }
pub enum MemoryRegionKind { /* ... */ }
pub struct Framebuffer { /* ... */ }
pub struct HardwareTopology { /* ... */ }
pub struct IoCaps { /* ... */ }This is where the audit found a real bug, and it is worth recording precisely because it was only visible after the crate was made to compile again.
BootInfo::config_root returned a pointer to the eight-byte RSDP signature string where the ACPI root table address was intended. The RSDP begins with the literal bytes "RSD PTR ", so the function returned a pointer to that text rather than to the table it introduces. Any ACPI enumeration built on it would have parsed ASCII as a table header.
The defect had been latent for the entire life of the repository because CI triggered on main and develop, and this repository's branch is master. No run had ever executed. The crate had stopped compiling, so the bug beneath the compile error was unreachable. Fixing the workflow surfaced four compile defects — including multiboot2::load being removed in 0.24 — and this fifth, genuine one underneath them.
The general lesson is in Why I Did This To Myself: a green checkmark you have never seen is not a green checkmark. The narrower one is that a compile error is not a bug's hiding place, it is its roof.
const COM1: u16 = 0x3F8;
static SERIAL: Mutex<SerialPort> = Mutex::new(SerialPort::new(COM1));COM1 at the standard port. This is how a booting kernel would report anything at all, and it is the channel the missing QEMU logs would arrive on. The path from "compiles" to "asserted to boot" runs through this file.
The workspace contains six crates. Two of them should not exist.
| Crate | Status | Note |
|---|---|---|
aether-core |
real | The math foundation |
aether-lang |
real | Lexer, parser, AST, interpreter, Titan VM |
aether-kernel |
real | Bare-metal microkernel |
aether-cli |
real | repl / run / check |
aegis-core |
⚠ duplicate | Queued for deletion |
aegis-cli |
⚠ duplicate | Queued for deletion |
The project was renamed from AEGIS to AETHER by copying directories rather than moving them. The consequences are all still visible:
aegis-cli/src/main.rsdiffers fromaether-cli/src/main.rsonly in string literals.aegis-clidoes not referenceaegis-coreat all — the duplicate CLI is not even wired to the duplicate core.- Half the doc comments across the tree still say AEGIS.
ml/tensor.rsopens with "AEGIS Tensor: N-dimensional array". The kernel's global allocator isAegisAllocator. The CLI banner prints🛡️ AEGIS. - Repository examples use
.aegisand.agextensions; the CLI accepts.aetherand.aeand prints a warning for the old ones.
This is documented here rather than quietly cleaned before publishing because the state of the repository is part of what a reader is evaluating, and a reviewer who discovers duplicate crates themselves reasonably wonders what else is unstated. It is the single most embarrassing thing in the tree and it is in the table of contents.
The claim ledger at docs/reference/status.md is the authority on what is gated, what is ungated, and which command produces each piece of evidence. Read it before opening anything.
Ranked by value, highest first — this ranking is the honest one, not the convenient one:
1. The external parity harness. Compare this engine against a pinned ripser or gudhi on shared fixtures. This is the largest correctness debt in the repository by a wide margin. Every correctness claim currently rests on internal invariants, and internal invariants are satisfiable by a self-consistently wrong implementation. Nothing else on this list is close.
2. The controlled convergence experiment. Does stopping on β-stability actually beat stopping on a tuned scalar residual, on real problems? This is the core premise of the language and it is unmeasured. A negative result here would be more valuable to this project than another green test suite.
3. QEMU boot logs. Move the kernel from "compiles" to "boots" with evidence.
4. Gate the Lean tree. 10,474 lines, 48 theorems, zero sorry, and no lake build in CI. Either gate it or cut it; the current state is neither.
5. Delete aegis-core and aegis-cli. Mechanical, uncontroversial, and removes the most embarrassing thing in the tree.
6. Wire Tensor::matmul to aether-gpu, or decide not to. The cost is measured (crossover n=128, 38x at n=512 with conversion) and the precision is measured (5e-7 relative, fine for training, not for 1e-9 assertions). What remains is the semantic decision about whether ml::Tensor may drop to f32.
7. A gradcheck for ml/autograd.rs. An autograd with no finite-difference verification is an unverified claim.
8. Differential tests between the interpreter and TitanVM. Two execution engines with no parity suite will diverge.
The house rule for any contribution that adds a number to this README: it must come with the command that reproduces it. A number without a reproduction command does not go in a table, and a benchmark measured on an implementation whose correctness is unestablished is not a result.
163 tests. Listing the ones that carry the correctness argument, because a count is not evidence and a reader deserves to see what is actually asserted.
The suite that makes the engine believable. Every entry is a property over generated inputs, not one hand-picked cloud with one expected number.
| Test | What it pins |
|---|---|
diagram_is_invariant_under_input_permutation |
Row order cannot change the diagram. 5 seeds, 1e-9 |
diagram_is_invariant_under_rotation_and_translation |
Isometry invariance — rotate 0.9128 rad, translate. Bottleneck < 1e-9 |
diagram_scales_linearly_with_the_point_cloud |
Scale equivariance for c ∈ {0.125, 0.5, 2, 37}. Exact |
a_scaled_radius_cap_selects_the_same_complex |
The cap must scale with the data, catching absolute-threshold bugs |
bottleneck_distance_respects_the_stability_bound |
CSEH stability, d_B ≤ 2ε, 12 seed×ε combinations |
circle_has_exactly_one_long_h1_bar_dying_at_sqrt3_times_the_radius |
The canonical positive control |
separated_clusters_produce_one_h0_bar_each_until_the_gap_closes |
β₀ tracks the actual component count |
gaussian_noise_produces_no_long_h1_bar |
The negative control. 4 seeds, zero long H₁ bars |
h0_matches_an_independent_union_find |
Cross-validation against separate code. 1e-9 |
a_single_point_has_one_essential_component_and_nothing_else |
Degenerate input |
duplicate_points_do_not_break_the_reduction |
Zero-distance pairs, the classic reduction crasher |
Two of these deserve emphasis. gaussian_noise_produces_no_long_h1_bar is worth as much as every positive case combined — a pipeline that finds structure in noise finds it everywhere, and every downstream claim built on it is unfalsifiable. And h0_matches_an_independent_union_find is why the engine deliberately does not special-case H₀ to union-find: doing so would make the check tautological, and the check is worth more than the speed.
Not listed above but present in the file: boundary_of_boundary_is_zero_over_z2 and every_face_is_present_and_precedes_its_coface, asserted across five complexes. ∂∂ = 0 fails silently — every rank downstream becomes meaningless with no crash — which is the entire argument for testing an identity that is true by construction.
| Group | Tests |
|---|---|
| Bottleneck as a metric | bottleneck_of_a_diagram_with_itself_is_zero, bottleneck_is_symmetric, bottleneck_satisfies_the_triangle_inequality |
| Bottleneck correctness | bottleneck_matches_a_hand_computed_pairing, bottleneck_projects_an_unmatched_bar_to_the_diagonal |
| Wasserstein | wasserstein_sums_where_bottleneck_takes_a_maximum, wasserstein_is_at_least_bottleneck |
| Cross-check | distances_respect_the_stability_theorem_on_real_diagrams |
| Landscapes | a_single_bar_gives_the_expected_tent_function, landscape_levels_are_ordered_pointwise, landscape_takes_the_kth_largest_tent_where_bars_cross, landscape_is_one_lipschitz_in_the_bottleneck_distance, an_empty_diagram_gives_a_zero_landscape |
| Images | persistence_image_has_the_requested_shape_and_is_nonnegative, persistence_image_weights_long_bars_more_than_short_ones, sigma_controls_the_kernel_width, persistence_image_is_translation_equivariant_in_birth |
wasserstein_sums_where_bottleneck_takes_a_maximum and landscape_takes_the_kth_largest_tent_where_bars_cross both exist because a mutant survived. See Mutation Testing — the original level-ordering test used nested bars, whose tent values already arrive sorted, so an implementation skipping the sort passed. Crossing bars kill that mutant. sigma_controls_the_kernel_width exists because no test referenced σ at all, so every image property held for any fixed kernel width whatsoever.
The largest file in the repository, and the shape of it tells the story: nine tests establish contracts every selector must satisfy, and the remaining twenty are the record of a claim being repaired three times.
Contracts — must hold for every selector:
a_full_mask_reproduces_dense_attention_exactly · attention_output_is_a_convex_combination_of_values · a_uniform_query_averages_the_values · a_masked_key_contributes_exactly_nothing · the_realized_pattern_equals_the_requested_pattern · a_budgeted_selector_respects_its_budget · a_single_position_attends_to_itself · shapes_around_the_block_boundary_are_handled · the_topological_selector_is_scale_equivariant
Causality — the class of bug that silently destroys a language model:
no_output_position_depends_on_a_later_position · causal_selectors_never_select_a_future_key
Two tests rather than one because they check different things: the first perturbs a future position and asserts the output does not move (behavioural), the second inspects the mask directly (structural). A kernel can pass one and fail the other.
Numerical safety:
an_all_masked_row_returns_zeros_rather_than_nan · large_logits_do_not_overflow_the_softmax · repeated_runs_are_bitwise_identical
The all-masked row is the sharp edge: softmax over an empty set is 0/0. Returning zeros is a decision, and a decision that is not tested is a decision that gets refactored into a NaN that surfaces four layers downstream as a loss of nan with no stack trace.
Measurement instruments:
oracle_top_k_upper_bounds_every_other_selector_at_the_same_budget · the_oracle_is_priced_as_the_diagnostic_it_is · the_topological_selector_is_placed_on_the_random_to_oracle_axis
The negative results, pinned so they cannot drift back:
the_topological_advantage_collapses_when_key_norms_vary · routing_is_sparse_only_when_the_keys_have_h0_structure · routing_buys_its_quality_at_a_real_discount_on_structured_keys · the_routed_selector_survives_the_key_norm_spread_that_broke_the_old_one
The routing decision:
the_plan_predicts_the_cost_it_will_actually_incur · the_plan_declines_to_route_exactly_when_routing_would_not_pay · the_h0_barcode_alone_separates_the_two_regimes · the_adaptive_selector_never_costs_more_than_dense · the_adaptive_selector_keeps_the_quality_of_whichever_path_it_picks
Cross-validation against the persistence engine:
single_linkage_merge_heights_equal_the_h0_persistence_deaths · routing_clusters_are_invariant_to_per_key_rescaling · the_routed_selector_obeys_every_contract_the_others_do
That last one is the guard against a common failure: a new selector added to an enum, tested only on the new behaviour, and never re-run against the contracts the old ones satisfy.
| Group | Tests |
|---|---|
| Schedule structure | the_dense_causal_schedule_is_lower_triangular_csr, a_csr_schedule_is_well_formed_at_every_size, the_topology_schedule_contains_sink_local_and_salient_blocks, the_topology_schedule_visits_fewer_blocks_than_the_dense_one |
| Salience | block_salience_is_the_elder_rule_over_centroids, the_salience_multiset_is_invariant_to_block_order, the_schedule_depends_on_block_order |
| Numeric parity | a_dense_schedule_reproduces_full_causal_attention, a_sparse_schedule_matches_its_own_dense_masked_reference, scheduling_a_block_actually_changes_what_the_row_sees |
| Safety | the_kernel_is_deterministic, large_logits_stay_finite_across_scheduled_blocks, an_empty_row_is_rejected_rather_than_producing_nan |
| Working set | the_working_set_does_not_grow_with_the_sequence |
| Input validation | the_builder_rejects_malformed_inputs, the_kernel_rejects_a_schedule_that_does_not_match_the_sequence |
the_salience_multiset_is_invariant_to_block_order sitting next to the_schedule_depends_on_block_order is not a contradiction — it is a caveat that was discovered by a test failing and is now pinned in both directions. The multiset of salience scores is invariant, being the H₀ barcode. The per-block assignment is not, because under component-size ties the union-find absorbs by index order. The Triton original shares the tie-breaking. The test was wrong, not the code.
scheduling_a_block_actually_changes_what_the_row_sees is the anti-tautology test: without it, a kernel that ignored the schedule entirely and computed dense attention would pass every numeric-parity check in the file.
h0_handles_five_hundred_points · h1_handles_one_hundred_points · circle_h1_dies_at_the_exact_regular_polygon_chord · circle_h1_death_converges_to_sqrt3_from_above · dense_sampling_does_not_manufacture_extra_loops · simplex_cap_still_fails_fast_rather_than_exhausting_memory · point_cap_is_still_enforced_when_configured
This is the file that went from 29.07 s to 1.10 s across the BTreeMap refactor on identical assertions. dense_sampling_does_not_manufacture_extra_loops is a second negative control: sampling a circle more densely must not invent H₁ classes that are not there.
circle_h1_death_converges_to_sqrt3_from_above is the test that taught its own author a sharper theorem by failing at n=24.
Stated plainly, because a test inventory that lists only what exists is an advertisement:
- No external parity. Nothing here compares against ripser, GUDHI, giotto-tda or Dionysus. Every test above is internal consistency, and internal consistency is satisfiable by a self-consistently wrong implementation.
- No gradcheck, anywhere.
ml/autograd.rshas no finite-difference verification, andattentionhas no backward pass to check. - No
ml/property tests. The clustering, classification and neural modules have far thinner coverage than the topology core. K-means is not tested for initialisation sensitivity, and the two independent single-linkage implementations are not cross-checked against each other. - Four kernel scheduler tests never execute. They exist in
aether-kernel/src/scheduler.rs, but the crate is ano_stdbinary with no test harness. - The Lean tree is not built. 48 theorems, no
lake buildin CI. - No fuzzing, no
proptest, no Miri. The property tests are hand-rolled loops over seeds, not a shrinking generator.
All measurements: single machine, Windows 11, Rust nightly, single core, release where noted. Single run, no confidence intervals — engineering measurements, not a study.
find_simplex linear-scanned simplices[..before] for every face of every simplex, making the reduction O(m²) in the simplex count. Now a BTreeMap keyed on the zero-padded vertex array.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
cargo test -p aether-core --test persistence_scale --release
Identical assertions either side of commit 27d70fa, same machine
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Before (linear face scan) 29.07 s
After (BTreeMap index) 1.10 s ← 26x reduction
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Invariant tests green across the refactor 11 / 11
Point cap lifted 32 → 512 (h0_only)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
The invariant suite existing before the refactor is the entire reason it was safe. That is what property tests buy: not bug-finding, permission to change things.
cargo run -p aether-core --example scale_probe --release, regular circle, single core.
| dim | n | pairs | seconds |
|---|---|---|---|
| 0 | 200 | 200 | 0.049 |
| 0 | 1,000 | 1,000 | 5.781 |
| 0 | 4,000 | 4,000 | 335.049 |
| 1 | 60 | 1,771 | 0.117 |
| 1 | 120 | 7,141 | 2.202 |
| 1 | 200 | 19,901 | 20.728 |
| 1 | 300 | 44,851 | 131.343 |
| 2 | 30 | 4,090 | 0.100 |
| 2 | 50 | 19,650 | 1.859 |
| 2 | 70 | 54,810 | 15.338 |
Presets are sized to those timings, not guessed: h2_default 48 points, h1_dense 128, h0_only 512. These caps are a time budget, not a correctness limit. Raise them explicitly and wait longer.
For context: ripser routinely handles clouds of tens of thousands of points. This engine does not. The trade purchased is no_std with one dependency, and the ability to run inside a kernel.
| Property | Assertion | Result |
|---|---|---|
| Circle H₁ death |
|
exact to 1e-12 |
| H₀ deaths | equal an independent union-find MST | exact to 1e-9 |
| Permutation invariance | shuffled rows give identical diagram | 1e-9, 5 seeds |
| Isometry invariance | rotate 0.9128 rad + translate | bottleneck < 1e-9 |
| Scale equivariance | exact | |
| Stability | 12 seed×ε cases pass | |
| Negative control | Gaussian blob | 0 long H₁ bars, 4 seeds |
| every simplex, 5 complexes | exact |
The Gaussian blob is worth as much as any positive case. A pipeline that finds structure in noise finds it everywhere, and every downstream claim built on it is unfalsifiable.
A Rust port of the topology-derived sparse attention kernel merged as triton-lang/kernels#22. The Python original requires CUDA; this compiles wherever aether-core does.
The port keeps the original's split, which is what makes each half checkable alone:
| Half | Nature | Checked against |
|---|---|---|
| CSR block schedule | combinatorial | exact equality with the Python builder — [0,1,3,6,10] / [0,0,1,0,1,2,0,1,2,3] for 4 blocks |
| Kernel | numeric | dense masked attention, 1e-12, 4 schedule configs × 4 block geometries |
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Block reduction, 16 blocks, local_radius=1 sink=1 topk=2
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Scheduled blocks 56 / 136 58.8% cut
Kernel vs dense masked reference max |Δ| < 1e-12
Salience vs persistence engine H0 deaths agrees, 1e-9
Blocks scoring zero exactly 1
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
The upstream PR measured 56.6% at seq 1024 and 80.9% at seq 4096 on an RTX 4060, with 1.04×–3.48× sparse-vs-dense-CSR wall-clock. Those timings are not reproduced here and are not claimed here. This port is a scalar CPU kernel with no SIMD, no threading, no GPU. It reproduces the answer and the block reduction, not the speed.
Everything above is a statement about cost. A 58.8% block reduction says the schedule visits fewer blocks; it says nothing about whether the blocks it visits are the right ones. A schedule choosing blocks uniformly at random is also 58.8% cheaper, and a model trained on either still converges. Cost is evidence about sparsity and not about topology.
The question that separates them is how much of the true attention mass each schedule keeps at an identical per-row budget, bracketed by random selection below and by an oracle that reads the dense scores above. Recovered mass, budget matched per row, cargo run -p aether-gpu --example selector_ablation --release:
| seq | density | random | topological | oracle | placement |
|---|---|---|---|---|---|
| 64 | 72.2% | 0.8151 | 0.8475 | 0.9483 | 24.3% |
| 128 | 36.8% | 0.4976 | 0.5674 | 0.6799 | 38.3% |
| 256 | 20.8% | 0.3208 | 0.3711 | 0.4728 | 33.1% |
| 512 | 12.0% | 0.2281 | 0.2337 | 0.3272 | 5.6% |
Placement is (topological − random) / (oracle − random) — the statistic every ablation here is reported against, defined in Theory §12: the share of the achievable gain the selector captures. It collapses as the sequence lengthens, which is the regime sparse attention exists for. Holding seq at 512 and raising only topk_topology_blocks drives it negative, to −109% at top-k 32, where the selector recovers 0.7446 against random's 0.8643. Spending more budget on topology makes the schedule worse, and the same holds on i.i.d. keys, the control for a fixture that merely rewards locality.
The signal is real and its sign is reversed. block_salience scores a block by H₀ death time under single-linkage merging, which measures how isolated it is. Attention mass concentrates where a key resembles the query, and a block unlike everything else is unlike the typical query too — the two rankings are anti-correlated by construction. Selecting the lowest-salience blocks at an identical budget beats random by a margin that grows with budget (26.9% at top-k 2, rising to 44.7% at 16), which is what an informative signal looks like.
topology_block_schedule is left as it is. Flipping the ranking changes what the method is, and that belongs to whoever is making the claim rather than to the ablation that found it; inverted_topology_block_schedule sits beside it so the choice is explicit.
Training does not rescue it either. With a query projection learned through the attention kernel — the concern being that a model could reshape its queries to suit whatever schedule it was handed — dense reaches 86% on an associative-recall task while every sparse arm plateaus between 56% and 61%, and the three sparse arms stay indistinguishable from one another. A model learns a great deal from blocks it can see and cannot learn its way to blocks it never sees.
Full tables, controls and the failures behind them are in crates/aether-gpu/FEATURES.md.
A passing suite tells you nothing about what it would catch. So: inject known defects, one at a time, and count.
Persistence invariants — 3 defects:
| Injected defect | New suite | Prior suite |
|---|---|---|
| Triangle filtration drops one of three edges | 4 / 11 | 0 / 6 |
Hardcoded +0.001 absolute epsilon in the filtration |
4 / 11 | 0 / 6 |
| Column reduction terminates after one operation | 7 / 11 | 1 / 6 |
The prior six example tests missed two of three defects entirely. That is the difference between "one hand-picked cloud with one expected number" and "a property that holds for every input".
That table was a record, not a command. It reported a run nobody could repeat: no harness in the tree injected those defects, so a reader could not check the counts and a later change could not invalidate them. In a document whose first promise is that every number is measured, a number with no command behind it is the one to distrust.
bash crates/aether-core/mutants.shnow injects all three, alongside nineteen covering the schedule, the salience mechanism and the attention backward pass, and reports which suites catch each.The harness reports how many tests in each suite fail, not just whether one does, so the fractions above are checkable rather than remembered. Two of the three reproduce exactly — the suite has since gained a test, so the denominators moved:
Injected defect claimed measured now Triangle filtration drops one of three edges 4 / 11 4 / 12 Column reduction terminates after one operation 7 / 11 7 / 12 Hardcoded +0.001absolute epsilon in the filtration4 / 11 1 / 12 The third had stopped being caught entirely. Before the test named below it survived every suite in the workspace, and the single test catching it now is that one.
The cause is not a weak assertion. Ten of the twelve tests build their config from a shared helper that sets
max_radius: f64::INFINITY, andINFINITY + 0.001 == INFINITY— for those ten the mutant is not merely undetected, it is not a mutation at all. Only two tests set a finite cap, and the older one,a_scaled_radius_cap_selects_the_same_complex, exercises it at 1.2 and 1.2 × 6 where no pairwise distance on a twelve-point circle falls in the thousandth of a unit above either. A defect that four tests once caught is now unreachable for ten of them, and that happened without any assertion being weakened — the default config drifted underneath them.This section's own lesson, arriving late and pointed at itself: test fixtures chosen for convenience rather than for discrimination produce suites that pass and prove nothing.
an_edge_just_beyond_the_radius_cap_is_excludedplaces one distance exactly on the cap and one 5×10⁻⁴ above it, so the boundary is the only thing the answer depends on, with a control raising the cap to confirm the merge does happen. With it, 0 of 22 mutants escape.
Diagram metrics — 5 defects, and this is the part I did not enjoy:
| Injected defect | Caught by |
|---|---|
| Landscape skips the per-sample descending sort | 2 / 17 |
| Image drops the linear persistence weight | 1 / 17 |
| Wasserstein returns the max instead of the sum | 1 / 17 |
| Image hardcodes the Gaussian width, ignoring σ | 1 / 17 |
| Bottleneck forbids diagonal projection | not run — infinite costs diverge the matching search |
All four runnable defects are in the harness and every count reproduces exactly: 2/17, 1/17, 1/17, 1/17. The fifth stays out rather than being quietly dropped — adding a mutant that hangs the run would trade one unverified claim for an unusable one.
Across the whole section that is six of seven reproducible claims landing on their original figures, and one that had rotted: the filtration epsilon above, now 1/12 against a claimed 4/11.
Three of the four here are caught by a single test of seventeen, which is the state the epsilon reached before it reached zero — so the harness also reports which tests catch each defect, since three defects at 1/17 could be three independent tests or one test carrying all three, and only the second is alarming. They are independent:
| defect | caught by |
|---|---|
| Wasserstein returns the max | wasserstein_sums_where_bottleneck_takes_a_maximum |
| Image ignores σ | sigma_controls_the_kernel_width |
| Image drops the persistence weight | persistence_image_weights_long_bars_more_than_short_ones |
Those are the tests this section says were written because a mutant survived, which is now a measurement rather than a memory. Across all 26 mutants no single test catches more than two, so nothing in the suite is load-bearing in the way the epsilon turned out to be.
Nothing is added to pad the fractions — a second test written to move a number tests the fixture, not the code. The harness is the guard: the epsilon slipped because no command re-ran it, and now one does.
Two of those five survived the first version of the suite.
The level-ordering test used nested bars, whose tent values already arrive in descending order, so an implementation skipping the sort passed anyway. And no test referenced σ at all, so every image property — shape, non-negativity, weighting, translation equivariance — held for any fixed kernel width whatsoever.
Both were rewritten with crossing bars and a concentration measurement until the mutants died. The lesson generalises unpleasantly:
Test fixtures chosen for convenience rather than for discrimination produce suites that pass and prove nothing. A suite you have not mutated is a suite of unknown strength.
First-class section, near the top, with the numbers that killed each claim. This is the part of a README I would want to read first about somebody else's project.
That string was the description field in pyproject.toml, published to PyPI. No benchmark in this repository supports it. It sat three files from a README section titled Evidence Policy which explicitly forbids unverified speedup claims. Removed.
The claim: keys geometrically near a query carry most of the attention mass, so you can select them without computing scores.
Measured against random and oracle top-k at the same budget, seq 32, head_dim 8, budget 6, 8 trials per row. Placement = (selector − random) / (oracle − random):
| key-norm spread | random | nearest-neighbour | oracle | placement |
|---|---|---|---|---|
| 0.0 | 0.4902 | 0.5667 | 0.5769 | +0.884 |
| 1.0 | 0.4899 | 0.5613 | 0.6242 | +0.533 |
| 2.0 | 0.4892 | 0.5265 | 0.6723 | +0.202 |
| 4.0 | 0.4873 | 0.4577 | 0.7616 | −0.109 |
| 8.0 | 0.4848 | 0.3725 | 0.8841 | −0.285 |
Past spread 4 the selector is worse than picking keys uniformly at random.
The reason is a one-line identity:
The +0.884 is not reported as a result. the_topological_advantage_collapses_when_key_norms_vary pins both ends so it cannot quietly drift back.
The first repair used an absolute radius of 0.6 against a median query-key distance of 2.395. It selected 1.00 keys per row where its same-budget baselines selected 5.53, and posted placements of −3.573 to −4.177.
It did not lose on mechanism. It lost on budget, by declining to select. Same bug class as the hardcoded epsilon the persistence scale-equivariance test exists to catch: an absolute threshold pretending to be relative. The radius is now relative to the row's median distance, and the ablation asserts equal mean budget before comparing anything.
The repaired selector clusters key directions and ranks by exact dot product. Placement held flat at +0.87 across the whole spread curve. Clean win, apparently.
Then the cost was measured:
| key distribution | H₀ component sizes | cost vs dense | placement |
|---|---|---|---|
| uniform random | [61, 1, 1, 1] |
0.999 | +0.942 |
| 4 real clusters | [16, 16, 16, 16] |
0.449 | +0.990 |
| 8 real clusters | [8, 8, 8, 8] |
0.528 | +0.995 |
| 16 real clusters | [4, 4, 4, 4] |
0.733 | +0.989 |
On uniform keys it examines 0.999× the dense dot-product count. It is dense attention with clustering overhead, and its +0.94 placement was bought by looking at every single key.
Single-linkage chains on a cloud with no density gaps — 61 of 64 keys in one component. That is not a clustering bug. It is H₀ correctly reporting that uniform data has no structure to route on. The persistence diagram was right; the claim was wrong.
Every test in the suite measured how good a selection was. None measured what it cost. selection_dot_cost and dense_dot_cost exist because of this, and the honest claim is now conditional:
Topological routing is a real sparsity win exactly when the key distribution has H₀ structure, and no win at all when it does not.
routing_plan makes that a runtime check rather than an assumption. Its gap_ratio, derived from the H₀ barcode alone, separates the regimes without overlap: structured minimum 2.70 against chained maximum 1.04, 6 trials each.
When routing does not pay, Selector::Adaptive must do something else. The first fallback was a budget-6 sliding window. It measured placement +0.014 on unstructured keys — random, to three decimals.
Not a fallback bug. When the keys have no structure there is no cheap-and-good option, because finding the top-k without computing the scores is precisely what the structure was supposed to make possible. The fallback is now dense, and the guarantee is never worse than dense, in cost or in quality.
Found while rewriting this README, by the unglamorous method of reading Cargo.toml instead of trusting the sentence next to it.
aether-core — the crate this document repeatedly holds up as having a minimal dependency surface — declares:
nalgebra = { version = "0.32", default-features = false, features = ["libm"] }Zero call sites. grep -r nalgebra crates/ --include=*.rs returns only the Cargo.toml lines and three stale cargo tree dumps checked into aether-kernel/. Not one .rs file references it.
Until this rewrite, the README asserted in four separate places that the core had "exactly one dependency, libm" and that libm plus heapless was "the whole mathematical surface". All four were false, and all four are now corrected.
This is the same defect class as the wgpu finding, with two aggravating differences. wgpu at least had a comment gesturing at intent ("future hooks for wgpu"); nalgebra has nothing. And wgpu sits in aether-lang, whereas this one sits in the crate whose minimality is a headline claim in the prior-art table.
Three things are worth extracting from it:
The no_std claim survives; the minimality claim does not. default-features = false with the libm feature is why thumbv7m-none-eabi still builds. The Cortex-M3 result stands. The "one dependency" line was decoration on top of a real result, and it was the decoration that was false.
The duplicate crate was better configured than the real one. aegis-core declares the same dependency as optional = true. The crate slated for deletion got this right and the maintained one did not, which is what happens when a rename is done by copying and then only one copy receives attention.
A dependency count is a claim, and claims need commands. Every performance number in this document carries a reproduction command. The dependency count did not, because it read like a fact about the file rather than a measurement — and that is precisely the category of statement that rots, since the file changes and the sentence does not. It now carries one, in Reproducing Every Number.
Queued for removal alongside wgpu, pollster and bytemuck.
With the dense fallback, the unstructured placement printed +7.614. Meaningless — dense recovers all the attention mass, which sits above the budget-limited oracle, so the ratio blows up. Left in a table it would read as a 700% win over an oracle it never competed with. The two regimes are now scored on different scales deliberately.
- A scale test asserted the circle H₁ death converges to
$\sqrt{3}r$ monotonically. It failed at n=24 because the engine returns$\sqrt{3}r$ exactly from n=12 onward whenever$3 \mid n$ . Trying to write the test found the sharper theorem. - A port test asserted per-block salience is permutation-equivariant. It failed at block 2 (
0vs1.724). Under component-size ties the union-find's absorb-smaller choice depends on index order. The multiset is invariant, being the H₀ barcode. The Triton original shares the tie-breaking. The test was wrong, not the code — and the caveat is now pinned.
The tilde. ~ terminates statements. Not ;, because ; is what everybody else uses and I wanted the source to look different at a glance. Not a technical argument. An aesthetic one, and I stand by it while acknowledging it costs every new reader four seconds.
The seal emoji. 🦭 until convergence(1e-6). A four-byte codepoint as a control-flow keyword. The lexer handles it fine. Your grep may not. seal until exists for that reason.
Two crate families. The workspace contains aether-core/aether-cli and aegis-core/aegis-cli, because the project was renamed by copying rather than moving. aegis-cli/src/main.rs differs from aether-cli/src/main.rs only in string literals, and aegis-cli does not reference aegis-core at all. Documented rather than hidden; deletion queued. The single most embarrassing thing in the tree.
Not special-casing H₀ to union-find. Union-find would make H₀ near-linear instead of the 335 s that n=4000 takes. Deliberately not done: the invariant suite tests the engine's H₀ against an independent union-find, and making the engine use union-find turns that test tautological. Speed is not worth deleting the check.
Denying only correctness and suspicious in clippy. The workspace carries 16 style-class warnings. A -D warnings gate that fails on first contact gets switched off within a week, and then the correctness lints stop being enforced too. Strict where it finds bugs, quiet where it finds taste.
Keeping a guard that provably never fires. boundary_indices checks idx < simplex_idx even though every_face_is_present_and_precedes_its_coface proves it always holds. Free, and a malformed complex degrades instead of lying.
10,474 lines of Lean. Aether/ contains a Lean 4 formalization with 48 theorems and zero sorry. It is also not built by CI, and 8,281 of those lines (Lexer, Parser, Pipeline, Static, VM) hold exactly one theorem between them — a second implementation of the language in Lean, not proofs about the first. Either gate it or cut it. It is in the ledger as ungated, which is the honest interim state.
Is this production-ready? No. It is a research language with a seal emoji as a keyword and a persistence engine that takes 335 seconds to do H₀ on 4,000 points. Use ripser.
Why is it called both AETHER and AEGIS?
Because it was renamed and the rename was done by copying directories. Half the doc comments say AEGIS, the examples use .aegis, and two duplicate crates are still in the workspace. Being fixed. It is in writing above precisely so nobody has to discover it.
Do the Betti numbers actually help convergence, or is this elaborate numerology? Honest answer: unmeasured. The seal loop works, the topology is computed correctly, and the machinery is tested. Whether stopping on β-stability beats stopping on a scalar residual, on real problems, against a properly tuned baseline, is a controlled experiment that has not been run. It should be. It is the most important missing number in this repository and no table above pretends otherwise.
Is the persistent homology correct? It satisfies 11 invariants including the stability theorem, reproduces a closed-form ground truth to 1e-12, agrees with an independent union-find on H₀, and survives mutation testing. It has never been compared against ripser or GUDHI. Those are different levels of assurance and I am not going to blur them.
Why no_std?
So the same persistence code that runs in the CLI runs in aether-kernel on bare metal, where it informs scheduling. Also because a two-dependency mathematical surface is a nice place to be — though the crate currently declares a third it never calls, which is logged as a defect.
Does the kernel boot?
It compiles for x86_64-unknown-none. Booting is not tested. Different claims.
Is there GPU acceleration?
There is a GPU backend — aether-gpu, 20 WGSL kernels, resident tensors, 92 tests, an RTX 4060 over Vulkan. There is no GPU acceleration of this project, because nothing in aether-core or aether-lang calls it. Both integrations are measured and neither is made: Tensor::matmul pays above n=128 and reaches 38× at n=512 with conversion counted; pairwise_sqdist never pays, because the persistence reduction is CPU-side and sequential so the matrix has to come back. Wiring the first one in means deciding whether ml::Tensor may drop to f32, which is a semantic change no benchmark authorises.
This answer used to read "No", and the wgpu/pollster/bytemuck entries it referred to — in aether-lang's default feature set with zero call sites — have since been deleted. The current backend is a separate crate on a current wgpu.
Why is there a CHANGELOG.md with almost nothing in it?
Fair.
How much of this README is the author admitting to mistakes? Roughly a third by line count. Intentional. A reader who finds an unstated limitation themselves discounts every other claim in the document.
Can I contribute?
Yes. Read docs/reference/status.md first — it lists exactly what is gated, what is ungated, and what command produces each piece of evidence. The highest-value contribution by a wide margin is the external parity harness against a pinned ripser. See Contributing for the ranked list.
Is convergence(1e-6) actually topological?
No. It parses to ConvergenceCond::Epsilon, a scalar tolerance. The topological variant is BettiStable { epochs }. Both exist in the grammar and both are implemented, but the headline example in the pitch uses the scalar one, and the language section says so rather than letting the ambiguity ride.
Does topological convergence eliminate hyperparameter tuning? No, and claiming so would be the easiest overclaim in this project to make. It replaces a continuous threshold ε with a discrete stability window — how many iterations the Betti vector must hold. That is still a hyperparameter. The argument for it is that an integer count is easier to reason about and does not interact with the scale of your loss, not that it disappeared.
Why is estimate_betti_1 an estimate when there is a full persistence engine right there?
Because they serve different layers. SparseAttentionGraph::estimate_betti_1 is a cycle-rank heuristic on an incrementally-maintained ε-neighbourhood graph — cheap enough to run per sample in a streaming pipeline. The full engine computes exact persistent homology and is far too slow for that path. The naming distinction is deliberate: one is exact and says so, one is an estimate and says so.
There are two single-linkage implementations in this crate. Is that a bug?
It is duplication, not a bug. attention::single_linkage_clusters and ml::clustering::AgglomerativeClustering implement overlapping mathematics for different callers. The attention one is cross-checked against the persistence engine; the ml one is not cross-checked against anything. They are also not checked against each other, which is the obvious missing test and is listed in what the suite does not cover.
Why is the kernel's allocator 64 KB of bump when aether-core has a sophisticated heap?
Different layers, and the naming does not make that clear. AegisAllocator is the kernel's #[global_allocator]: 64 KB, bump, never frees — correct for a kernel that boots and runs a fixed workload. The manifold heap is a data structure inside aether-core with generational handles and entropy-regulated collection. They are unrelated despite sitting in the same repository.
Is verify_binary_topology a security feature?
No. It is a structural plausibility check that would catch a truncated or corrupted image. An adversary who knows it exists can pad a payload to match a byte-distribution signature trivially. Cryptographic signing authenticates code; this does not. The word "authentication" in the source is an overclaim and the kernel section corrects it.
The governor's doc comment says PID. Is it?
It is PD — proportional and derivative, no integral term. Defensible for a threshold that is already clamped to [EPSILON_MIN, EPSILON_MAX], since an integral term on a saturating actuator invites windup. But the comment and the code disagree, and the code is right.
Why -A clippy::style when the project is this fussy about correctness?
Because a gate that fails on first contact gets switched off within a week, and when it goes, the correctness lints go with it. The workspace carries 16 style-class warnings. Strict where lints find bugs, quiet where they find taste. Design decisions has the longer version.
Why not make H₀ use union-find? It would be enormously faster.
Because h0_matches_an_independent_union_find compares the engine's H₀ against a union-find implementation. Making the engine use union-find turns the test tautological. H₀ at n=4,000 takes 335 s and that is the price of keeping a real cross-check. Speed is not worth deleting the check that says the answer is right.
Is the Lean formalization proving things about the Rust?
No, and this is the sharpest thing to be clear about. Aether/ is 10,474 lines with 48 theorems and zero sorry. But 47 of those 48 live in Core.lean alone, and the 8,281 lines of Lexer, Parser, Pipeline, Static and VM hold exactly one theorem between them. That is a second implementation of the language written in Lean, not proofs about the first. There is also no lake build in CI, so none of it is verified to still compile. Either gate it or cut it.
How fast is this compared to ripser? Unmeasured, and deliberately absent from the prior-art table. The honest inference from the measured ceiling — H₁ at n=300 in 131 s — is that it is substantially slower, since ripser routinely handles clouds orders of magnitude larger. That is an inference, not a benchmark, and it is phrased as one.
What would change your mind about this whole approach? A controlled experiment showing β-stability stopping does no better than a tuned scalar residual on real problems. That experiment has not been run, it is the second-highest-value contribution on the list, and a negative result would be worth more to this project than another green suite.
Aether-Lang/
├── crates/
│ ├── aether-core/ math foundation, no_std, libm + heapless
│ │ ├── src/
│ │ │ ├── persistence.rs F2 reduction, Rips + witness, H0-H2
│ │ │ ├── diagram.rs bottleneck, Wasserstein, landscapes, images
│ │ │ ├── attention.rs sparse attention reference + selectors
│ │ │ ├── scheduled.rs CSR block-scheduled attention (Triton port)
│ │ │ ├── manifold.rs time-delay embedding, sparse graphs
│ │ │ ├── topology.rs Betti numbers, shape verification
│ │ │ ├── governor.rs PID-on-manifold
│ │ │ ├── memory.rs manifold heap, entropy GC
│ │ │ └── ml/ regression, clustering, neural, tensors
│ │ ├── tests/ most of the topology tests live here
│ │ └── examples/ scale_probe, routing_cost — reproduce the tables
│ ├── aether-gpu/ wgpu compute backend — 20 WGSL kernels, f32
│ │ ├── src/shaders.wgsl matmul, tiled matmul, pairwise distance,
│ │ │ softmax, fused gradients, Adam, SGD
│ │ ├── tests/ 60 tests: parity, gradcheck, f32 topology
│ │ ├── mutants.sh mutation harness, 0 of 10 escape
│ │ └── FEATURES.md measurements, negative results, corrections
│ ├── aether-lang/ lexer, parser, AST, interpreter, Titan VM
│ ├── aether-kernel/ no_std x86_64 microkernel
│ ├── aether-cli/ repl / run / check
│ ├── aegis-core/ ⚠ duplicate, queued for deletion
│ └── aegis-cli/ ⚠ duplicate, queued for deletion
├── Aether/ Lean 4 formalization (ungated, see above)
├── docs/ MkDocs site + the claim ledger
├── examples/ .aegis / .ag scripts (extension debt)
└── .github/workflows/ci.yml the gate that had never run until recently
The crate the rest of the workspace is built on. 24 files, and the distribution is informative — the topology core and the ML subtree are comparable in size, which the old single-row table did not convey.
crates/aether-core/src/ lines
├── lib.rs 51 module exports, feature gates
├── persistence.rs 783 F2 reduction, Rips + witness, H0-H2
├── attention.rs 699 7 selectors, routing plan, cost model
├── manifold.rs 608 Takens embedding, sparse graph, pipeline
├── memory.rs 480 manifold heap, Chebyshev guard
├── scheduled.rs 477 CSR block schedule (Triton port)
├── aether.rs 448 hierarchical block tree, drift
├── diagram.rs 417 bottleneck, Wasserstein, landscapes, images
├── topology.rs 356 byte-level shape verification ⚠ not the engine
├── governor.rs 267 PD control on the state manifold
├── state.rs 186 SystemState, three deviation metrics
└── ml/ 3,992
├── classification.rs 672 6 classifiers
├── clustering.rs 558 KMeans, DBSCAN, agglomerative
├── neural.rs 489 activations, optimizers, dense layers
├── convergence.rs 339 BettiNumbers, detector, residuals ← the premise
├── regressor.rs 328 model escalation
├── autograd.rs 302 reverse mode ⚠ no gradcheck
├── benchmark.rs 255 escalation harness ⚠ not a benchmark
├── linalg.rs 254 reductions, distances
├── tensor.rs 254 N-d array
├── gossip.rs 203 distributed averaging
├── convolution.rs 160 Conv2D ⚠ forward only
├── dataloader.rs 127 batching, shuffle
└── mod.rs 51
crates/aether-core/tests/ lines
├── attention_contracts.rs 1,124 29 tests
├── persistence_invariants.rs 607 11 tests
├── scheduled_attention.rs 576 16 tests
├── diagram_distance.rs 381 17 tests
└── persistence_scale.rs 206 7 tests (--release)
─────
2,894
2,894 lines of tests against 7,472 lines of non-ml source. The topology core is well covered; the 4,000-line ml subtree has no dedicated test file at all, and that asymmetry is the honest shape of this repository's assurance.
crates/aether-lang/src/ lines
├── interpreter.rs 1,803 the reference implementation
├── parser.rs 1,107 recursive descent, positioned AST
├── vm.rs 806 TitanVM ⚠ no parity suite vs interpreter
├── lexer.rs 444 includes the 4-byte seal codepoint
├── ast.rs 343 node definitions
├── ascii_render.rs 149 terminal render
├── webgl_export.rs 105 WebGL export
├── python.rs 70 pyo3 surface ⚠ bindings package empty
├── lib.rs 56
└── mod.rs 41
crates/aether-kernel/src/
├── main.rs kernel_main, panic handler
├── interrupts.rs IDT, CURRENT_STATE behind spin mutexes
├── scheduler.rs SparseScheduler ⚠ 4 tests never execute
├── allocator.rs 64 KB bump, #[global_allocator]
├── loader.rs ELF verification + byte-topology check
├── serial.rs COM1 — the only output channel
└── boot/
├── bios.rs BootInfo, MemoryRegion, Framebuffer
└── topology.rs HardwareTopology, IoCaps
Depending on why you are here, different files are the point.
Evaluating whether the mathematics is right. Start at persistence_invariants.rs — 11 property tests, and the two that matter most are bottleneck_distance_respects_the_stability_bound (the CSEH theorem) and gaussian_noise_produces_no_long_h1_bar (the negative control). Then persistence.rs itself. Then note what is absent: no comparison against ripser or GUDHI exists, so every assurance here is internal.
Evaluating whether the engineering is sound. attention_contracts.rs, 1,124 lines, is the most informative file in the repository — not because the code is best there, but because the shape of the file records a claim being repaired three times, with the cost model that finally falsified it added at the end.
Deciding whether to use it. Read Limitations first, then Prior Art. If you need production TDA, the answer is ripser and this document says so in four places.
Looking for the interesting idea. Theory §13 — the H₀ barcode is a sufficient statistic for deciding whether a topological method will pay, and it is cheap enough to consult before committing. That is the one result here that generalises past this repository.
Wanting to contribute. docs/reference/status.md, then Contributing. The top two items are worth more than everything else combined.
Hiring, or otherwise judging the author. What We Got Wrong is roughly a third of this document and includes six claims that were killed by their own measurements, two tests that turned out to be wrong rather than the code, and two phantom dependencies found by reading Cargo.toml instead of trusting the sentence beside it. That section is the argument. The green checkmarks are not.
Aether/, 10,474 lines of Lean 4. Not built by CI, which is the first and most important thing to say about it.
| File | Lines | Theorems | sorry |
What it is |
|---|---|---|---|---|
Core.lean |
3,356 | 47 | 0 | The formalization proper |
VM.lean |
2,526 | 1 | 0 | A VM model in Lean |
Static.lean |
1,891 | 0 | 0 | A static checker in Lean |
Parser.lean |
1,569 | 0 | 0 | A parser in Lean |
Pipeline.lean |
1,442 | 0 | 0 | A pipeline in Lean |
Lexer.lean |
853 | 0 | 0 | A lexer in Lean |
| Total | 10,474 | 48 | 0 |
The shape of that table is the honest summary: 47 of 48 theorems live in one file. The other 8,281 lines hold exactly one theorem between them. They are a second implementation of the language — lexer, parser, static checker, VM — written in Lean, not proofs about the Rust one.
Zero sorry is real and worth having. A formalization with sorry scattered through it is a sketch; this one commits.
It does not verify the Rust. There is no extraction, no refinement proof, and no correspondence argument connecting Core.lean to crates/aether-core. Two independent implementations of overlapping mathematics, one of which has theorems attached, is a useful thing to have. It is not the same as a verified implementation, and the difference is exactly the difference this README keeps insisting on between compiles and boots, or between invariants and parity.
It is not checked to still compile. No lake build runs anywhere in CI. Given that this repository's CI had never executed at all until recently, an ungated tree should be assumed stale until a build says otherwise.
Gate it or cut it. Gating means adding lake build to ci.yml and accepting the toolchain-pinning cost. Cutting means acknowledging that 8,281 lines of un-theoremed re-implementation are a liability rather than an asset. The current state — present, impressive-looking in a line count, unverified — is the one state that is worse than either decision, and it is listed as ⛔ Ungated in the status dashboard rather than being quietly counted as a feature.
Asymptotics for every operation whose cost a caller can actually feel. n is point count, m simplex count, s sequence length, d dimension, b bar count.
| Operation | Cost | Note |
|---|---|---|
Rips simplex enumeration, max_dim = k |
O(nk+2) | Tetrahedra are the O(n⁴) term — the reason h2_default caps at 48 points |
| Pairwise distances | O(n²d) | |
Face lookup (BTreeMap) |
O(log m) per face | Was O(m) linear scan — the 26× |
| Z₂ column reduction | O(m³) worst case | Worst case is rarely hit; the practical driver is m |
betti_at(radius) |
O(b) | A query on a computed diagram, not a recomputation |
| Witness complex, ℓ landmarks | O(ℓk+2 + nℓ) | Decouples cost from n — Theory §9 |
| Operation | Cost | Note |
|---|---|---|
| Bottleneck distance | O(b³ log b) | Binary search over candidate costs × Kuhn's matching |
| p-Wasserstein | O(b³) | Hungarian, exact. ponytail: ceiling, trigger at max_points > 2048 |
| Landscape, r resolution, k levels | O(b·r + r·b log b) | Per-sample descending sort is the second term |
| Persistence image, p pixels | O(b·p) | |
| Persistent entropy | O(b) |
| Operation | Cost | Note |
|---|---|---|
| Dense attention | O(s²d) | The thing everything else is measured against |
single_linkage_clusters |
O(n²d + n² log n) | Edge enumeration + sort. ponytail: marked — not the binding term, since the mask is already Θ(s²) |
select_mask, any selector |
Θ(s²) memory | The dense [s, s] bool mask dominates every selector's asymptotics |
OracleTopK |
O(s²d) | Computes every score. A ruler, not a method |
routing_plan |
O(n² + n log n) | H₀ barcode of key directions |
Scheduled attention, nnz blocks |
O(nnz · B²d) | B = block size; working set is one tile |
That select_mask row is the one worth internalising. Every selector in this crate materialises a full [seq, seq] boolean mask, so no selector is sub-quadratic in memory regardless of how few keys it picks. The selection savings are real in dot products and absent in allocation. This is a property of a reference implementation written for checkability, and it is precisely the trigger recorded in the ponytail: comment on the clustering routine: a neighbour graph saves nothing until the mask stops being dense.
| Operation | Cost | Note |
|---|---|---|
ManifoldHeap::alloc |
O(1) amortised | |
ManifoldHeap::get |
O(1) | Index + generation check |
ChebyshevGuard::calculate |
O(blocks) | One pass over block statistics |
hierarchical_query |
O(active blocks) | Prunes whole subtrees via admissible bound |
GeometricGovernor::adapt |
O(1) | PD update |
TimeDelayEmbedder::push |
O(1) | Ring buffer |
SparseAttentionGraph::add_point |
O(n) | Bitset row per point, 64-neighbour cap |
compute_betti_0 |
O(n·α(n)) | Union-find, exact |
estimate_betti_1 |
O(n) | Cycle-rank heuristic, not persistent homology |
Asymptotics are not the measurement. The measured ceiling is:
- H₀ at n=4,000 — 335 s
- H₁ at n=300 — 131 s
- H₂ at n=70 — 15.3 s
All single-threaded, all --release. The engine is dominated by simplex enumeration and reduction, in that order, and nothing in it is parallelised or vectorised. The presets exist to keep a caller inside those numbers rather than discovering them.
examples/ holds 17 programs. They still use the pre-rename .aegis and .ag extensions and the CLI prints a warning for them — extension debt sitting in plain sight.
| File | Bytes | What it demonstrates |
|---|---|---|
simple.aegis |
32 | The smallest program that runs |
seal_demo.aegis |
549 | A seal loop, minimal |
llm_benchmark.aegis |
993 | LLM-shaped workload |
llm_demo.ag |
1,263 | |
hello_manifold.aegis |
1,407 | Embedding a series into a manifold |
ml_test.ag |
1,426 | ML module surface |
regression_demo.aegis |
1,602 | regress with model escalation |
3d_cluster.aegis |
1,660 | Clustering in 3D |
grand_benchmark.aegis |
2,724 | |
neural_topology.aegis |
2,895 | Neural net + topology together |
benchmark_seal_vs_linear.aegis |
4,638 | Seal loop against a linear baseline |
seal_loop_demo.aegis |
5,119 | The fullest seal-loop example |
visualization_demo.aegis |
6,191 | render to ASCII and WebGL |
benchmark_suite.aegis |
8,163 | |
titan_bench.ag |
615 | Titan VM |
benchmark_compare.py |
2,252 | Python-side comparison harness |
llm_benchmark.py |
1,347 |
A caveat that matters more than the table. Four of these files have benchmark in the name, and benchmark_compare.py exists to compare against something. No number from any of them appears anywhere in this README, and none of them is run by CI. They are demonstration programs and exploratory harnesses, not evidence. A reader who assumed benchmark_seal_vs_linear.aegis had produced a published comparison would be wrong — the core convergence claim is unmeasured, and that is stated in the FAQ, the Limitations, and the Contributing list precisely because a directory listing suggests otherwise.
Run any of them:
cargo run -p aether-cli -- run examples/hello_manifold.aegisThe two Rust examples under crates/aether-core/examples/ are different in kind — they are the probes that produce numbers this document actually quotes:
cargo run -p aether-core --example scale_probe --release # the scale ceiling table
cargo run -p aether-core --example routing_cost --release # the routing cost tablescale_probe.rs opens with a comment stating its own role: "Not a test: a probe that prints the numbers the docs are allowed to quote." That is the distinction this section is drawing, written into the source.
Toolchain. Rust nightly, pinned in rust-toolchain.toml. Nightly is required for -Z build-std (kernel and embedded targets) and #![feature(abi_x86_interrupt)] in the kernel. The CLI itself uses stable features only.
Components. rust-src and llvm-tools-preview for bare-metal builds; clippy and rustfmt for the gate.
Targets.
| Target | Purpose | Extra flags |
|---|---|---|
host (x86_64-pc-windows-msvc, Linux, macOS) |
CLI, tests | — |
x86_64-unknown-none |
kernel | -Z build-std=core,alloc |
thumbv7m-none-eabi |
no_std verification |
-Z build-std=core,alloc |
Dependencies. aether-core declares three: libm, heapless, and nalgebra.
libm = "0.2"
heapless = "0.8"
nalgebra = { version = "0.32", default-features = false, features = ["libm"] }libm and heapless are used. nalgebra has zero call sites — grep -r nalgebra crates/ --include=*.rs returns nothing. It is declared non-optionally, so every build of the core crate compiles a linear-algebra library that no line of code references. It is default-features = false with the libm feature, which is why the thumbv7m-none-eabi build still succeeds; the no_std claim survives, the minimality claim does not. See What We Got Wrong §6.
For contrast, aegis-core declares the same dependency as optional = true — the duplicate crate is, on this one point, better configured than the real one.
Feature flags (aether-core):
| Feature | Implies | Meaning |
|---|---|---|
std (default) |
alloc |
Standard library; CLI and tests |
alloc |
— | no_std with an allocator |
no_std |
alloc |
Bare-metal mode |
The CLI adds clap and rustyline.
aether-lang used to pull wgpu, reqwest, safetensors, pollster and bytemuck through its default feature set, all with zero call sites, so every default build compiled an HTTP client and a 2023-era GPU stack that nothing referenced. All five are deleted.
aether-gpu is the crate that actually uses a GPU, and it is deliberately separate: aether-core is no_std and builds for thumbv7m-none-eabi, while wgpu needs std and a driver stack. A feature flag would leave a no_std crate whose dependency graph only resolves on hosted targets.
Optional Python. pyproject.toml builds a pyo3 extension via maturin. The bindings directory is currently an empty package.
# the gate, exactly as CI runs it
cargo fmt --all -- --check
cargo clippy --workspace --exclude aether-kernel --all-targets -- \
-D warnings -D clippy::correctness -D clippy::suspicious \
-A clippy::style -A clippy::complexity -A clippy::perf
cargo test --workspace --exclude aether-kernel
# bare metal
cargo build -p aether-kernel -Z build-std=core,alloc --target x86_64-unknown-none
# no_std on a real embedded target
cargo build -p aether-core --no-default-features --features no_std \
-Z build-std=core,alloc --target thumbv7m-none-eabi
# reproduce the tables in this README
cargo run -p aether-core --example scale_probe --release
cargo run -p aether-core --example routing_cost --releaseConvenience aliases in .cargo/config.toml: cargo gate, cargo invariants, cargo kernel, cargo embedded, cargo cli.
Three workflows. ci.yml is the gate, and its header records why it exists in its current form:
Triggers on the branch this repository actually uses. The previous config listed
main/develop, neither of which exists here, so no run had ever executed and the workspace had drifted out of compiling.
| Job | Runner | What it proves |
|---|---|---|
test |
ubuntu · windows · macos, fail-fast: false |
fmt (ubuntu only), clippy, cargo test --workspace --exclude aether-kernel |
invariants |
ubuntu | The 6 topology suites, named separately so a failure names itself |
no_std_check |
ubuntu | Builds aether-core for thumbv7m-none-eabi |
kernel |
ubuntu | Builds aether-kernel for x86_64-unknown-none |
docker |
ubuntu | Builds the image and runs docker run --rm aether:test --help |
release |
ubuntu | Tag-gated, needs: all five above |
Three details worth naming:
invariants is a separate job on purpose. It re-runs suites the test job already covers. The redundancy buys a named check in the PR list: a failure reads Persistence Invariants rather than being one line inside a 163-test log. The workflow comment states the reasoning — a benchmark measured on a wrong implementation is not a result — which is the evidence policy expressed as CI structure.
fail-fast: false on the OS matrix. A Windows-only failure and a macOS-only failure are different bugs, and fail-fast would hide the second behind the first.
The docker job runs the binary, it does not just build it. docker run --rm aether:test --help is the difference between an image that builds and an image that works. The Dockerfile had been copying crate directories from paths that never existed, which a build-only job would have caught but a build-and-run job catches more usefully.
docs.yml builds the MkDocs site with mkdocs build --strict and deploys to GitHub Pages. --strict turns broken internal links into build failures, which is the same argument as the rest of this document applied to documentation: an unenforced claim decays.
publish.yml handles releases.
What CI does not run, restated here because the status dashboard depends on it: no lake build for the Lean tree, no QEMU boot, no external TDA parity, no gradcheck, and no execution of the four kernel scheduler tests.
aether-kernel is excluded from the host test job on purpose. It is a no_std bare-metal binary with no global allocator or panic handler and cannot link for a host target. It has its own CI job on x86_64-unknown-none. Running cargo test --workspace without the exclusion fails, and that failure is a property of the target, not a bug.
The evidence policy in one rule: a number without a reproduction command does not go in a table. This section is that rule discharged — every quantitative claim above, mapped to the command that produces it.
| Claim | Where | Command |
|---|---|---|
| 223 / 223 tests pass | Status | cargo test --workspace --exclude aether-kernel |
| 11 persistence invariants | Theory | cargo test -p aether-core --test persistence_invariants |
| 17 diagram-metric tests | Test suite | cargo test -p aether-core --test diagram_distance |
| 29 attention contracts | Test suite | cargo test -p aether-core --test attention_contracts -- --nocapture |
| 16 scheduled-attention tests | Test suite | cargo test -p aether-core --test scheduled_attention -- --nocapture |
| 7 scale tests, 1.10 s | The 26× | cargo test -p aether-core --test persistence_scale --release |
| Scale ceiling table | Results | cargo run -p aether-core --example scale_probe --release |
| Routing cost table | What We Got Wrong §4 | cargo run -p aether-core --example routing_cost --release |
| Placement / spread table | What We Got Wrong §2 | cargo test -p aether-core --test attention_contracts -- --nocapture |
| 58.8% block reduction | Scheduled attention | cargo test -p aether-core --test scheduled_attention -- --nocapture |
| gap_ratio 2.70 vs 1.04 | Theory §13 | cargo test -p aether-core --test attention_contracts -- --nocapture |
| Kernel compiles bare metal | Status | cargo build -p aether-kernel -Z build-std=core,alloc --target x86_64-unknown-none |
no_std on Cortex-M3 |
Status | cargo build -p aether-core --no-default-features --features no_std -Z build-std=core,alloc --target thumbv7m-none-eabi |
| Formatting clean | Status | cargo fmt --all -- --check |
| Clippy clean | Status | cargo clippy --workspace --exclude aether-kernel --all-targets -- -D warnings -D clippy::correctness -D clippy::suspicious -A clippy::style -A clippy::complexity -A clippy::perf |
| 24,180 Rust lines | Status | Get-ChildItem crates -Recurse -Filter *.rs | Get-Content | Measure-Object -Line |
nalgebra has zero call sites |
What We Got Wrong §6 | grep -rn nalgebra crates/ --include=*.rs |
| 60 GPU tests, RTX 4060 / Vulkan | Status | cargo test -p aether-gpu --release |
| 0 of 10 GPU mutants escape | FEATURES.md | ./crates/aether-gpu/mutants.sh |
| matmul crossover n=128, 38× at n=512 | FEATURES.md | cargo run -p aether-gpu --example tensor_crossover --release |
10,474 Lean lines, 48 theorems, 0 sorry |
Lean | Get-ChildItem Aether -Recurse -Filter *.lean | Get-Content | Measure-Object -Line, then Select-String "^\s*(theorem|lemma)\s" and Select-String "\bsorry\b" |
Listed separately rather than mixed into the table above, because the distinction is the point.
| Claim | Why it cannot be reproduced here | Where it came from |
|---|---|---|
| 56.6% / 80.9% block reduction at seq 1024 / 4096 | Requires CUDA | Upstream triton-lang/kernels#22 |
| 1.04×–3.48× sparse-vs-dense wall clock | Measured on an RTX 4060 | Same |
29.07 s before the BTreeMap refactor |
Requires checking out the parent of 27d70fa |
Historical, same machine |
The upstream GPU figures are cited, not claimed. This port is a scalar CPU kernel with no SIMD, no threading and no GPU; it reproduces the answer and the block reduction, not the speed.
Everything measured in this document ran on a single machine: Windows 11, Rust nightly, single core, --release where noted.
No confidence intervals. Single runs. No core pinning. No turbo control. These are engineering measurements taken to size caps and catch regressions, not a study. The 26× figure is robust to all of that because it is a 26× difference on identical assertions on the same machine within one session; the absolute scale-ceiling timings are not, and should be read as order-of-magnitude.
- Every number is measured. Projected, estimated and theoretical-peak figures are labelled as such in the same cell, or they are absent.
- Every baseline is named. "Faster than before" is unfalsifiable; "29.07 s → 1.10 s across commit
27d70fa, identical assertions" is checkable. - Every comparison has a control. The attention ablations report against
Random(floor) andOracleTopK(ceiling) at equal budget, because a selector measured against nothing is a selector measured against its author's hopes. - Negative results get the same typography as positive ones. An entire section, near the top, with the numbers that killed each claim.
- A count is not evidence. A test file with 40 tests that never runs contributes nothing. The status dashboard marks a row Active only when a command in
ci.ymlproduces its evidence. - Correctness precedes performance. A benchmark measured on an implementation whose correctness is unestablished is not a result. This is why the persistence invariants have their own named CI job rather than being folded into the general test run.
Longer than most projects' feature lists. That is the point.
No external parity. The persistence engine has never been compared against ripser, GUDHI, giotto-tda or Dionysus on shared fixtures. The invariant suite is not parity: a self-consistently wrong implementation can satisfy every internal property it tests. This is the largest correctness debt here.
Scale. H₀ at n=4,000 takes 335 s. H₁ at n=300 takes 131 s. The reduction is O(m²) in the simplex count and single-threaded. Production TDA libraries handle clouds orders of magnitude larger.
The core claim is unmeasured. Whether topological convergence beats scalar convergence on real problems, against a tuned baseline, has not been tested. The machinery is correct; its value is unestablished.
The GPU backend is not used by anything outside its own crate. aether-gpu
is real — 20 WGSL kernels, resident tensors, 93 tests, verified against finite
differences and a mutation matrix — but no line of aether-core or aether-lang
calls it, and none can: aether-core is no_std and wgpu is not. Within
aether-gpu, scheduled_attention_or_cpu and its backward counterpart route
between the two implementations by capability. Both
candidate integrations have been measured and neither has been made:
Tensor::matmul crosses over at n=128 and reaches 38× at n=512 with f64↔f32
conversion counted, and pairwise_sqdist never pays because the persistence
reduction is CPU-side, so the matrix must come back. See
crates/aether-gpu/FEATURES.md.
Two phantom dependencies. nalgebra is a non-optional dependency of aether-core with zero call sites, and wgpu/pollster/bytemuck ship in aether-lang's default features with one comment between them. Both are queued for removal; both are written up rather than quietly deleted before publication.
Every selector allocates a dense [seq, seq] mask. No selector in attention is sub-quadratic in memory, however few keys it picks. The savings are real in dot products and absent in allocation. This is a property of a reference implementation built for checkability, and it is the recorded trigger on the clustering ponytail: marker.
The ml subtree has no dedicated test file. 3,992 lines — clustering, classification, neural, autograd, convolution — against 2,894 lines of tests that target the topology core almost exclusively. K-means is not tested for initialisation sensitivity. The two independent single-linkage implementations in this crate are not checked against each other.
No gradcheck anywhere. ml/autograd.rs has no finite-difference verification against its own forward pass.
ml/convolution.rs is forward-only. A convolution with no backward pass is a feature extractor, not a trainable layer.
topology.rs shares vocabulary with the persistence engine and shares none of its guarantees. It computes Betti-like numbers over &[u8] by windowed density heuristics, with MAX_BETTI_1 saturating at 10. None of the 11 persistence invariants apply to it. Three functions in this crate are named some variant of compute_betti_0 and only one of them is exact persistent homology.
verify_binary_topology is not a security mechanism. It is a structural integrity heuristic that an adversary can defeat by padding. The word "authentication" in the source is an overclaim.
The governor is PD, not PID, despite its doc comment. Its stability properties are argued, not tested.
ml/benchmark.rs is not a benchmark in the sense this document uses the word. It is an internal harness for the model-escalation policy, and no number in any table comes from it. Four files in examples/ also have benchmark in their names and produce no published number either.
The Titan VM has no parity suite against the interpreter. Two execution engines, no differential test.
No backward pass. aether_core::attention is forward-only, so no gradient check accompanies it. A forward-correct kernel with a wrong backward trains to a plausible worse optimum that loss curves will not reveal, so any backward must land with its gradcheck in the same change.
Attention results are synthetic. Every ablation number above is measured on synthetic keys. Whether real attention key distributions carry H₀ structure is unmeasured, and it decides whether the routing result transfers at all.
The Triton port reproduces answers, not timings. Scalar CPU, no SIMD, no threading, no GPU. The upstream 1.04×–3.48× figures were measured on an RTX 4060 this workspace cannot reach.
The block schedule's cost reduction is real and its selection is not. At an identical per-row budget the topological ranking recovers less attention mass than choosing blocks at random, the deficit widens as more budget is given to it, and inverting the ranking beats random instead. Two synthetic fixtures, one machine; the shipped topology_block_schedule is unchanged, and the measurement is in Scheduled attention.
Per-block salience is order-dependent. Under component-size ties the absorbed component is chosen by index order, so the same centroid scores differently depending on sequence position. The multiset is invariant. A fix needs a tie-break on centroid content.
The kernel compiles but is not asserted to boot. No QEMU logs, no hardware matrix.
Four kernel scheduler tests never execute. They exist in scheduler.rs, but aether-kernel is a no_std binary with no test harness.
The Lean tree is ungated. 10,474 lines, 48 theorems, 0 sorry, and 8,281 lines holding one theorem between them. No lake build in CI.
Duplicate crates. aegis-core and aegis-cli are near-copies of their aether counterparts, left over from a rename done by copying.
Extension drift. The CLI accepts .aether and .ae; the repository examples use .aegis and .ag and print a warning.
All timings are single-machine, single-run, Windows 11, nightly, no confidence intervals, no turbo control, no core pinning.
Seal loop — a loop terminating on topological convergence rather than a scalar threshold. Named for 🦭. No further justification available.
Manifold heap — the no_std allocator in aether-core::memory, organising objects spatially and reclaiming cold branches, treating unused memory as entropy.
Bio mode — the CLI's default execution mode. It prints a shield emoji. It does nothing biological.
Titan VM — the bytecode VM in aether-lang::vm. Named at 3 AM. Behind the interpreter on coverage.
Geometric concentrator — the component in manifold.rs reducing a point cloud to the region carrying the most structure.
Elder rule — not made up; this one is real algebraic topology. When two components merge, the younger dies. It is the reason a block salience score is an H₀ death time.
Placement — the scale-free statistic every attention ablation is reported against: (selector − random) / (oracle − random) at equal budget. 0 is random, 1 is a cheating oracle. Theory §12 covers the two ways it lies.
Gap ratio — the H₀ barcode statistic routing_plan uses to decide, at runtime, whether topological routing will pay. Structured clouds measure ≥ 2.70; chained ones ≤ 1.04. Theory §13.
Chaining — the single-linkage failure mode where a cloud with no density gaps gets absorbed one point at a time into one giant component. [61, 1, 1, 1] out of 64 keys. Not a bug — H₀ correctly reporting that uniform data has no structure to route on.
Sink block — in the scheduled-attention config, the leading block every query attends to regardless of salience. Attention sinks are a real empirical phenomenon in transformers; the scheduler reserves them explicitly rather than hoping salience picks them up.
Budget — the number of keys a selector may examine per row. Every ablation in this document holds it equal across selectors, because a selector that declines to select posts catastrophic numbers without losing on mechanism.
Essential class — a homology class that never dies; its bar runs to infinity. A connected cloud has exactly one essential H₀ class. a_single_point_has_one_essential_component_and_nothing_else is the degenerate case.
Witness complex — the landmark-based approximation to Vietoris–Rips that makes persistence viable at 24 landmarks on a Cortex-M3. An approximation, labelled as one. Theory §9.
Generational handle — Gc<T> in the manifold heap: an index plus a generation counter, so a stale handle whose slot was reused fails the check and returns None instead of aliasing whatever now lives there.
Fail-fast budget — the persistence engine's caps. Exceeding one returns TooManyPoints or TooManySimplices rather than subsampling silently or exhausting memory. A time budget, not a correctness limit; raise it explicitly and wait longer.
ponytail: comment — the repository's convention for marking a deliberate shortcut with its ceiling and the trigger that should force revisiting it. Two exist, both in aether-core, both with concrete triggers. A marker naming a ceiling but no trigger is the kind that silently rots.
Ungated — a claim whose evidence exists but whose command does not run in CI. Distinguished throughout from ✅ Active (a CI command produces the evidence) and ❌ Does not exist. The Lean tree, the kernel boot, and four scheduler tests are ungated.
MIT. See LICENSE.
Copyright © 2026 Teerth Sharma. The Lean formalization, the persistence engine, the language, and every mistake catalogued above are original work.
The scheduled-attention module is a port of triton-lang/kernels#22, contributed by the same author to that repository under its license.
Invented by Teerth Sharma
github.com/teerthsharma/Aether-Lang · teerthsharma@outlook.com
Every number above was measured. Every claim names its control.
The section where I am wrong is a third of the document, and that is the feature.