Domain stream: the EBM Langevin loop through the MLIR/LLVM backbone — CPU JIT and one cooperative kernel on gfx1151/gfx1201/sm_120 (W4-PRODUCT-1 / AD-SOLVER-IFT-1) - #765
Merged
Conversation
…VM backbone
The energy E(y, x) = 0.5 * sum((x - y)^2) is a Graph IR function marked for
reverse-mode; inside libtessera_jit the paired autodiff pass derives @E__bwd,
the new EBM lowering (tessera-ebm-lower-langevin, the dialect's first pass
that lowers rather than annotates) turns energy / inner_step / langevin_step
into arith + linalg over that gradient with Philox-4x32-10 / Box-Muller noise
generated in a linalg.generic, and the whole K-step scf.for compiles as one
function: no per-step host gradient or noise transfers. langevin_step gains a
variadic `captures` operand so an energy can close over its context.
Declared RNG policy (mirrored bit-for-bit by native_langevin.reference_langevin_loop):
philox key = (lo32, hi32) of key[0]; counter = (flat index, lo32, hi32 of
key[1], 0); z = sqrt(-2 ln u0) cos(2 pi u1); next key = (key[0], key[1] + 1).
Fail-closed: non-euclidean manifolds, a missing @E__bwd, mistyped operands.
Three compiler gaps this slice surfaced and closes:
- `tessera.sub` had only a tangent rule; reverse-mode stopped on any energy
or loss with a subtraction. It now has an adjoint (dy, 0 - dy).
- `tessera.unsqueeze` / `tessera.broadcast` -- what the sum-reduce adjoint
emits -- had no linalg lowering, so no reduce-sum gradient could reach the
CPU JIT. Both lower now (expand_shape; a stride-0 linalg.generic).
- The JIT's DPS rewrite changed callee signatures without following
intra-module call sites; call sites now allocate the out-buffers and pass
them trailing. The paired autodiff pass runs in the JIT only for
reverse-marked functions with no paired partner yet.
Consumers: native_langevin.{native_langevin_loop, native_quadratic_energy,
package_ebm_langevin_cpu}, execution-matrix row cpu / cpu_ebm_langevin_llvm_jit.
Fixtures: EBM lower_langevin_quadratic + lower_langevin_rejects, autodiff_paired_sub.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…6) in the plan, log, review, domain audit, queues and CLAUDE.md Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
…and manifold energies docs/audit/domain/EBM_NATIVE_LOOP_ARCHITECTURE.md records the measured position of every existing device route on the lowered Langevin loop (the arena skeleton cannot host a tensor-level gradient; the serial native-tape route rejects the module for exactly two admission reasons -- four functions instead of one and no EBM owner marker; the cooperative route has no EBM Schedule op; the chain needs three drivers and generic printing) and plans the slices: G1 serial device residency, M1 sphere (tangent projection + retraction, per-row status word for the norm and retraction singularities, declared reduction order), M2 bivector (grade projection via the Clifford lowering), N1 nonlinear energies as an adjoint-coverage question with the custom-adjoint placeholder refused, G2 a schedule/tile Langevin contract with a body region and the upstream linalg->gpu spine as its measurement baseline, T1 one compiler driver. Linked from the plan (W4-PRODUCT-1), the log, the GA/EBM review and the domain audit. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…program emitter
The K-step loop over the compiler-derived gradient now reaches the device
through the compiler alone. tessera-opt registers the EBM and Clifford
dialects and passes (feature flags TESSERA_HAVE_EBM / TESSERA_HAVE_CLIFFORD)
plus convert-elementwise-to-linalg, so one driver invocation runs paired
autodiff -> EBM canonicalize -> lower-langevin -> tessera-to-linalg ->
inline -> elementwise-to-linalg -> canonicalize -> cse -> the new
tessera-row-program-to-gpu pass.
RowProgramToGPUPass turns a [rows, features] tensor row program (parallel
linalg.generic bodies, feature-axis linalg.reduce, small uniform integer
vectors, scf.for) into one gpu.func: one block per row, one lane per
feature (lanes = next pow2 >= F, <= 1024), Lane/Row/Uniform/Scalar value
classes, ordered shared-memory reductions with barriers, loop-carried state
in registers, guarded loads/stores over !llvm.ptr<1> arguments, and the
tile.alloc_shared marker the arena pipeline sizes. Calls are refused with
a diagnostic (inline first); anything outside the envelope fails closed.
Python: native_host_program.py factors the host-array device runner out of
the Clifford route (CliffordDeviceProgram is now a subclass);
native_langevin.py gains langevin_device_source / bind_ebm_langevin_gpu /
ebm_langevin_program / native_langevin_loop_device /
package_ebm_langevin_native; the contract producer merges into a module
that already carries compiler provenance attributes; runtime rows
rocm/rocm_ebm_langevin_native_compiled and
nvidia_sm120/nvidia_ebm_langevin_native_compiled launch the packaged loop.
Tests: tests/unit/test_ebm_native_langevin_gpu.py (host-free chain shape,
arena replay for both backends, envelope refusals; device half bit-exact
against the declared numpy policy for K in {1,5,12,4}, T=0 descent, and
the launch rows). Mac: 39 passed / device lanes skipped.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
benchmarks/record_ebm_langevin_native_gpu.py records, on the owning device, the loop's deviation from the declared numpy policy over six (shape, K, T) cases up to 1024 features plus the emitted kernel structure taken from the arena IR. tests/tessera-ir/phase2_autodiff/row_program_to_gpu_langevin.mlir runs the whole single-driver chain host-free and checks the cooperative kernel shape, and a second RUN line checks the emitter refuses an un-inlined call. lit gains a `tessera-ebm` feature probed from the driver. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The arena pipeline hoists the loop-invariant key word out of the scf.for, so the packaged kernel carries (f32, i64), not the pre-arena (f32, i64, i64). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
--inline builds its interface collection over every loaded dialect; the row-program emitter loads llvm as a dependent dialect before the inliner runs, and registerAllExtensions does not provide the interface the LLVM dialect promises. The assertions-enabled MLIR on Tajasarus aborted with "checking for an interface (mlir::DialectInlinerInterface) that was promised by dialect 'llvm' but never implemented"; every NDEBUG driver in the fleet ran the same chain green. Decision #19 standing lesson, third instance. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The kernel skeleton carries the tile.alloc_shared arena marker; without the declaration an assertions-enabled MLIR aborts with "Loading a dialect (tile) while in a multi-threaded execution context" (Tajasarus), while NDEBUG drivers load it silently. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
compiler/native_row_program.py owns the compile -> contract -> package -> cached HostArrayProgram chain for any [rows, features] program; the Langevin route builds its module and specs on top of it. A row-normalization program (x / sqrt(sum_f x^2)) exercises the emitter's ordered shared-memory reduction, which the quadratic gradient never does: a host-free lit fixture (phase_f5/row_program_to_gpu_reduce.mlir, with entry-missing and too-wide refusals) and a device test that requires bit-exactness with the sequential f32 fold the emitter declares. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ttributes
The emitted module carries tessera.row_program.* / tessera.autodiff.*
attributes; parsing them loads the Tessera dialect, which an input with no
Tessera ops (the reduction fixture) never loaded. The assertions-enabled
driver on Tajasarus aborted ("Loading a dialect (tessera) while in a
multi-threaded execution context"); NDEBUG drivers loaded it silently.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
On the NVVM route math.sqrt lowers to libdevice's __nv_sqrtf, whose precise path is gated on an NVVM reflect flag MLIR's pipeline never sets (LLVM 23 exposes only nvvm-reflect-ftz), so the kernel ran MUFU.SQRT and one row of the row-normalization proof was 1 ulp off on sm_120 while the division was div.rn and both RDNA parts were exact. llvm.intr.sqrt lowers to sqrt.rn on NVPTX and to the correctly rounded expansion on AMDGPU; the emitter's contract is IEEE arithmetic, so it pins the intrinsic. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
convert-gpu-to-nvvm marks the LLVM math intrinsics illegal (it routes them to libdevice), so on NVIDIA the emitter calls libdevice's rounding-explicit __nv_fsqrt_rn / __nv_dsqrt_rn, which are correctly rounded whatever the NVVM reflect flags say; on ROCm llvm.intr.sqrt lowers to AMDGPU's correctly rounded expansion. The fixture checks both backends. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…-09-16) Architecture doc: G2 landed as the row-program emitter over the lowered loop (not the scoped Tile contract), T1 landed, G1 skipped as the fallback outside the envelope; the reduction-order and libm risks resolved by measurement; the four defects only the assertions driver and sm_120 could show. Log entry, plan gate + Latest, GA/EBM review, domain audit, four backend todos (rocm parity on gfx1151 + gfx1201, nvidia parity on sm_120 with the libdevice finding, apple/x86 not applicable), CLAUDE.md. Packets: benchmarks/baselines/ebm_langevin_native_gpu_20260916/ — six (shape, K, T) rows per device, worst abs error 0 on gfx1151 (Princess-Luna), gfx1201 (Tajasarus, assertions-ON driver) and sm_120 (Super-Bear), with the kernel structure read from the arena IR and source fingerprints. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…guard tessera.sub gained its adjoint on this branch (EBM quadratic energy); the ledger guard pins the native set exactly and CI caught the drift. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Domain stream, fourth and fifth slices, owner W4-PRODUCT-1 / AD-SOLVER-IFT-1. Sync keys
EBM-NATIVE-QUADRATIC-2026-09-16(CPU lane) andEBM-NATIVE-GPU-2026-09-16(device lane). The GA/EBM review's first acceptance clause for "an energy is a typed program" is met on the CPU lane and on gfx1151, gfx1201 and sm_120: the energy is a Graph IR function, its gradient is the compiler's, and the K-step Langevin loop with on-device Philox noise runs as one native call on the CPU and as one cooperative kernel on each GPU, bit-exact with the declared policy everywhere.CPU lane (fourth slice)
E(y, x) = 0.5·Σ(x − y)²is a Graph IR function markedtessera.autodiff = "reverse"; the paired autodiff pass derives@E__bwd; nothing hand-writes the gradient.tessera-ebm-lower-langevinturnsenergy/inner_step/langevin_step(euclidean) into arith + linalg over that gradient with Philox-4x32-10 / Box-Muller noise in alinalg.generic, so a K-stepscf.forcompiles as one function.langevin_stepgains a variadiccapturesoperand. Non-euclidean manifolds, a missing@E__bwdand mistyped operands fail closed. The RNG policy is stated in the pass header and mirrored bit-for-bit bynative_langevin.reference_langevin_loop.native_langevin.{native_langevin_loop, native_quadratic_energy, package_ebm_langevin_cpu}, rowcpu/cpu_ebm_langevin_llvm_jit.tessera.subhad no adjoint;tessera.unsqueeze/tessera.broadcast(the sum-reduce adjoint's output) had no linalg lowering; the JIT's DPS out-param rewrite did not follow intra-module call sites (and must not re-pair already-paired modules).Device lane (fifth slice) — the tensor-level gradient lowered inside a kernel
tessera-optregisters the EBM and Clifford dialects and passes when built with them (TESSERA_HAVE_EBM/TESSERA_HAVE_CLIFFORD) plusconvert-elementwise-to-linalg; lit gains atessera-ebmfeature. Chain:--tessera-autodiff-paired --tessera-ebm-canonicalize --tessera-ebm-lower-langevin --tessera-to-linalg --inline --convert-elementwise-to-linalg --canonicalize --cse --tessera-row-program-to-gpu.src/transforms/lib/RowProgramToGPUPass.cpp. A new core pass over upstream dialects: a[rows, features]row program (parallellinalg.genericbodies, feature-axislinalg.reduce, small uniform integer vectors,scf.for) becomes onegpu.func— one block per row, one lane per feature (≤ 1024), the K-step loop carried in registers (state, gradient and noise never leave them), Philox inside the loop, ordered shared-memory reductions (sequential over the feature index, so reduced quantities are bit-exact with a host f32 fold), guarded!llvm.ptr<1>loads/stores, the arena marker, andtessera.row_program.*provenance. Calls are refused with "inline every call before lowering"; anything outside the envelope names the op. The scopedschedule.ebm_langevin/tile.langevin_kernelcontract was not needed: after lowering, the loop function already is the scalar body, and one pass maps it (Decision Apple GPU: Gumbel-max inference sampler (#18-safe, reproducible) #31 — no second scalar emitter). G1 serial residency stays scoped only as the fallback outside the envelope.compiler/native_row_program.py(compile → tensor contract →build_native_gpu_storagewith replay check → cachedHostArrayProgram) is what any[rows, features]domain program packages through;native_host_program.pyfactors the host-array runner out of the Clifford route (CliffordDeviceProgramis now a subclass); the contract producer merges into a module that already carries compiler provenance. Rowsrocm/rocm_ebm_langevin_native_compiledandnvidia_sm120/nvidia_ebm_langevin_native_compiled;package_ebm_langevin_native.x / sqrt(Σ_f x²), F up to 1024) proves the emitter's ordered fold bit-exact with the sequential f32 fold on all three devices, host-free inphase_f5/row_program_to_gpu_reduce.mlir(with entry-missing and too-wide refusals and both backends' sqrt lowering).Four defects only the assertions driver and sm_120 could show
--inlinebuilds its interface collection over every loaded dialect; the LLVM dialect promises aDialectInlinerInterfacethatregisterAllExtensionsdoes not provide →tessera-optnow callsLLVM::registerInlinerInterface.tile.alloc_sharedwithout declaring the tile dialect ("Loading a dialect (tile) while in a multi-threaded execution context").tessera.*provenance attributes load the Tessera dialect, which an op-free input (the reduction fixture) never loaded.math.sqrtlowers to libdevice's__nv_sqrtf, whose precise branch is gated on__CUDA_PREC_SQRT, which MLIR never sets (LLVM 23 exposes onlynvvm-reflect-ftz): the kernel ranMUFU.SQRTand one row of the reduction proof was 1 ulp off on sm_120 whiledivwasdiv.rnand both RDNA parts were exact.convert-gpu-to-nvvmoutlawsllvm.intr.sqrt, so the emitter calls libdevice's rounding-explicit__nv_fsqrt_rnon NVIDIA and pins the intrinsic on ROCm. Every other libdevice f32 path on that route is recorded as unmeasured (docs/audit/backend/nvidia/todo.md).Items 1–3 ran green on every NDEBUG driver in the fleet (Decision #19's standing lesson, third instance, recorded in CLAUDE.md).
Evidence
benchmarks/baselines/ebm_langevin_native_gpu_20260916/(README, source fingerprints): six(shape, K, T)rows per device — K ∈ {1,4,5,8,12}, F ∈ {5,8,33,100,1024}, T ∈ {0,0.3,0.5,0.7} — worst abs error 0 on gfx1151 (Princess-Luna), gfx1201 (Tajasarus,TESSERA_ROCM_CHIP=gfx1201, assertions-ON driver) and sm_120 (Super-Bear), the device f64log/cosincluded; kernel structure read from the arena IR (onegpu.func, onescf.forcarrying(f32, i64), 18 Philoxmului_extended, nolinalg/tensorop).tests/unit/test_ebm_native_langevin_gpu.py, all unsandboxed: 16/16 on Princess-Luna, 16/16 on Tajasarus under the assertions compiler, 16/16 on Super-Bear (bothbuild/and the reconfiguredbuild-nvidia-cuda/, which had been configured without the EBM/Clifford backends). Host-free half on the Mac (chain shape, arena replay for both backends, envelope refusals, both fixtures).phase2_autodiff/row_program_to_gpu_langevin.mlir+phase_f5/row_program_to_gpu_reduce.mlirpass on the Mac, both ROCm boxes and both Super-Bear trees; phase2_autodiff + phase8 + phase_f5 under Tajasarus's assertions driver 61 pass / 55 unsupported / 0 fail.Records
EBM_NATIVE_LOOP_ARCHITECTURE.md(G2 as shipped and why the Tile contract was not needed, T1, measured risks), log entry, plan gate + Latest, GA/EBM review, domain audit (EBM ladder row gainsrocmandnvidia_sm120from the matrix), four backend todos underEBM-NATIVE-GPU-2026-09-16, CLAUDE.md.Not claimed / next
No performance claim — per-call host transfers; the Python-emitted
rocm_ebm_langevin_compiled/x86_ebm_langevin_compiled/ Apple kernels remain the lanes until the dispatch/allocation/traffic/kernel-time comparison is recorded withrouteandlatency_source. Rows wider than 1024 features, data-dependent control flow and cross-row-coupled energies fail closed. Nonlinear (N1) and manifold (M1 sphere, M2 bivector) energies are the next slices and are row programs the same emitter maps; opaque callbacks keep the reference path; no Apple package route (the Apple arena lane is MSL).🤖 Generated with Claude Code