Repository navigation
NttR AArch64: the 16-bit NTTs and inverse NTTs by an interpreter the kernel runs - #3041
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FoYWDDEDFq1DFev5X7n2aq
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FoYWDDEDFq1DFev5X7n2aq
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FoYWDDEDFq1DFev5X7n2aq
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FoYWDDEDFq1DFev5X7n2aq
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FoYWDDEDFq1DFev5X7n2aq
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FoYWDDEDFq1DFev5X7n2aq
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FoYWDDEDFq1DFev5X7n2aq
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FoYWDDEDFq1DFev5X7n2aq
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FoYWDDEDFq1DFev5X7n2aq
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FoYWDDEDFq1DFev5X7n2aq
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FoYWDDEDFq1DFev5X7n2aq
|
Build-time numbers from this PR's own CI (run 39649160162, the assembly step's New machine modules (48.4 s):
Trimmed forward modules (was → now):
On CI this PR costs about 60 s, against 34 s for the hand proofs it replaces: +25 s. My local profile sums had suggested the opposite. They undercounted, because they leave out the time a module spends waiting on its imports under 6 workers. Of the 60 s, about 24 s is fixed cost of the machine:
The other 24.5 s is the per-transform runs and top proofs:
Moving the inverse NTT onto the machine would delete Generated by Claude Code |
|
Let's not regress on proof perf if we can avoid it. |
|
Agreed. As it stands this PR costs about 25 s of CI build. The forward NTTs are the only thing on the machine so far, and its fixed cost (about 24 s: The kernel's runs themselves are cheap: So I'll make this PR a net win rather than wait for the follow-ups. I'll move the three inverse NTTs ( I'll update this PR with the new CI module times once it's done. Until then, please hold off merging. Generated by Claude Code |
The machine follows NTT⁻¹ too (Rules.inv): Gentleman–Sande butterflies, the reductions between layers and the multiplication by 3303, with the final reduction to [0, q). ModelI relates its levels to nttInv, and the kernel checks the runs of nttInv, Readonly.inv and Readonly.invW (RunI). InU/InW, ctxU/ctxW and initU_rel/initW_rel now take the table's symbol, the table and the model, so the forward and inverse proofs share them. Deleted: InvPass1, InvPass2, InvIdx, InvRows, W.InvPass1, W.InvPass2, Pass2, Last and most of Gs; CoeffsIn moves to Widen16, its only other user. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FoYWDDEDFq1DFev5X7n2aq
|
Build times from this PR's CI run at 926300f (run 39678153997, the Lean assembly job's per-module times): Machine (
What is left of So the NTT proofs, forward and inverse, now take about 47 s of module time. Before this PR, from run 39649160162 and
So the PR now saves roughly 25–30 s, where the forward-only version cost about 25 s. These come from two runs, and module times on CI vary between runs: Generated by Claude Code |
The AArch64 ML-KEM NTTs and inverse NTTs are now proven by an abstract interpreter that the kernel runs over the code:
vg_mlkem_ntt,Readonly.fwdandReadonly.fwdW;vg_mlkem_ntt_inv,Readonly.invandReadonly.invW.This replaces the hand proofs of their passes, about 3,000 lines. This change is proof only:
src/asm/is unchanged (Emit.lean --check), and so are the TCB, the Spec, the contracts and the statements ofNtt16.correct,Ntt16.correctInv,Readonly.fwd_ok,Readonly.inv_ok,W.fwdW_okandW.invW_ok.The machine (
Proof/MlKem/AArch64/NttR/)Sym.lean: the interpreter.pafterstlevels;tw;q;Nats. The kernel evaluates them natively.NTT(fwdRules), a butterfly is accepted only when its two terms are the pair the Cooley–Tukey layerstcombines, with that block's zeta (bflyOk).NTT⁻¹(invRules), the levels are the Gentleman–Sande layers (gsPair,gsZeta), the reductions between them and the multiplication by 3303. The final reduction to[0, q)is collapsed too.Sound.lean,Step.lean: soundness.Hd, given the coefficients after each number of levels (L) and their reductions (O).Relbetween the machine's state and the real one (step_ok).run_ok,loop_ok,runP_ok).Ctxand built byCtx.of.Model.lean: the coefficients the butterflies compute (Lf) obeybflyOk(model). They representNTT's within(st + 1) q(Lf_rep), and their reductions areNTT's coefficients (Of_ntt).ModelI.lean: the same forNTT⁻¹. Its levels (Li) obey the inverse's operations (modelI) and representnttInvL's coefficients within their bounds (Li_rep). Their reductions arenttInv's coefficients (Oi_nttInv).RunW.lean,RunU.lean,RunI.lean: the regions and starting terms of each transform, and the kernel's runs.checkW_ok,checkU_ok,checkN_ok.checkIW_ok,checkIU_ok,checkIN_ok, the last for the end ofnttInv.fwdW,invW) or asu32s (fwd,ntt,inv,nttInv). Foru32s the high halfwords are junk on input and zero on output.Natliteral (tabT,itabT). Its lanes are read by a general lemma (slot_foldr), not evaluated.FwdW.lean,FwdU.lean,Inv.lean: the top proofs.initW_rel,initU_rel).ctxW,ctxU,ctxN).NTT(fwdW_run,fwd_run,ntt_run) orNTT⁻¹(invW_run,inv_run,nttInv_run) of the input in the polynomial.nttandnttInvfirst store their table into the working space with the existinghalves_ok. Then the machine runs the rest (nttTail,nttInvTail).Deleted
Ntt16/W/Pass2.lean,Pass2.lean,Last.lean,InvPass1.lean,InvPass2.lean,InvIdx.lean,InvRows.lean,W/InvPass1.leanandW/InvPass2.lean.Ntt16/Pass1.lean,W/Pass1.leanandFwd.lean:p1_bodyand its loads and arithmetic;Gs.leanbut the lane lemmags_lane.CoeffsInmoves toWiden16.lean, its only remaining user.Build time
The first version of this PR (forward NTTs only) made CI's build about 25 s slower: 48.4 s of new machine modules against 22.9 s of forward proofs saved (CI run 39649160162).
The inverse on the machine adds
ModelI,RunI,LitIandInv. It deletes:InvPass1,InvPass2,InvIdx,InvRows,W.InvPass1,W.InvPass2);Pass2andLast.I'll post the module times from this PR's own CI run as a comment.
No
maxHeartbeatsor other limits.Checks
lake buildandEmit.lean --check(no change tosrc/asm/).check_lean_imports,check_lean_modulesandcheck_lean_public_imports;check_registrationsandcheck_lean_speed;check_vectorsandcheck_arch_gates;check_variantsandcheck_mcdt.🤖 Generated with Claude Code
https://claude.ai/code/session_01FoYWDDEDFq1DFev5X7n2aq