A map of the engine
Five layers, one tape: where record-once/replay-twice actually lives in the codebase, before you write a line of it.
The last page was the idea. This page is where that idea actually lives —
one picture of the whole codebase, so that when a later module drops you
into nablatensor-quant or nablatensor-risk, you already know which floor
of the building you're on.
The whole story
Every GPU backend compiles and links whether or not its toolchain is even
installed, then gates itself at runtime through AadEngine.isAvailable() —
none of them is a build- or test-time dependency of the default cpu-jit
path. That's why mvn -o test is green offline, on a laptop with no GPU and
no native library installed: every GPU engine politely reports itself
unavailable instead of failing anything.
Five layers, bottom to top
The core: ADouble and the tape
nablatensor-core holds the ADouble type from the last page, the
AadRecorder that builds a tape as your code runs, and the AadTape that
results. Nothing here knows what an option or a bank is — it only knows how
to record arithmetic and replay it, forwards or backwards.
Six engines, one tape
The tape doesn't care which engine replays it. cpu is the scalar
interpreter — slow, dependency-free, and treated as the oracle every other
engine is checked against. cpu-jit compiles the tape into straight-line
JVM bytecode and is the default. simd replays it across the JDK's Vector
API lanes. vulkan, rocm, and cuda each compile the tape into a fused
GPU kernel — GLSL→SPIR-V, HIP via HIPRTC, and CUDA via NVRTC, respectively —
and only run if that vendor's driver is actually present. Same recording,
six different ways of running it; the choice is one string, .on("cpu-jit")
or similar, not a rewrite.
Those six engines aren't the same code recompiled six times — they're three
genuinely different compilers pointed at the same tape. cpu-jit emits JVM
bytecode through the Class-File API; Vulkan hands the same tape to a
GLSL→SPIR-V compute shader pipeline, the kind a game engine uses; ROCm
generates HIP C++ and compiles it with HIPRTC while the JVM is already
running. cpu-jit reproduces the scalar oracle bit-for-bit; the fp32 GPU
paths agree with it to five decimal places.
Quant: turning a replay into a price
nablatensor-quant is where finance enters: EquityMarket, Products,
MonteCarlo, model families like HestonModel and SabrModel, and a
gradient-based Calibrator. This is the layer Module 1 through Module 8
mostly live in — payoffs, models, calibration.
Risk: turning many prices into one number
nablatensor-risk takes the sensitivities that fall out of one trade's
tape and combines them across a book: RiskFactor, Sensitivities,
Portfolio, and NestedAggregation — the same
√(ΣK² + Σγ·SS)-shaped aggregation both FRTB and ISDA SIMM specify. Module 9
lives here.
Regulatory: the number a bank actually files
The top of the map isn't a separate module in the source tree — it's
nablatensor-quant and nablatensor-risk composed to match a specific
regulation's text: FRTB capital, SA-CVA, ISDA SIMM, SA-CCR, and the rest of
Module 10. NablaTensor computes the arithmetic a regulation asks for; as its
own README puts it, "sign-off is yours."
Try it yourself
Open ADouble.java
and count how many arithmetic methods it exposes — add, mul, exp, and
so on. Every one of them is a node type the reverse sweep in Module 1 knows
how to differentiate. There's nothing hidden past what you can see in that
one file.
▶️ Run it
Still nothing to run — Module 1 is next, and it starts with the file you just opened.
⚠️ What this doesn't do
This map skips the seams — custom ops, hooks like antithetic sampling, the scenario DSL, netting — the places you can extend or reconfigure one layer without touching the others. Those show up alongside the modules that actually use them, not here.
What's next
→ Deeper: the engine's own README
lists every module in the box, in more detail than fits on one page.
→ Next: ADouble, the number that remembers,
where the code starts.