Core mechanism
The generator emits class-file instructions corresponding to tape nodes, loads inputs, evaluates primitives and stores outputs or adjoints. The JVM then applies its own optimisation and compilation pipeline.
TeaVM records the actual differentiable Java tape and displays its operations. This browser run does not emit a JVM class file; the Learn JVM example explains that separate cpu-jit step.
This exact source runs in TeaVM. Form changes update its Java literals and reset manual edits.
import com.nablatensor.engine.ADouble;
import com.nablatensor.engine.AadRecorder;
import com.nablatensor.engine.AadTape;
import com.nablatensor.quant.EquityMarket;
public final class BytecodeKernelRiskStudio {
public static void main(String[] args) {
double maturity = 1;
int steps = 1;
EquityMarket market = EquityMarket.of()
.spot(100)
.strike(100)
.vol(.2)
.rate(.03)
.maturity(maturity)
.build();
AadTape tape = AadRecorder.record(rec -> {
ADouble spot = rec.input("spot", market.spot()); ADouble strike = rec.input("strike", market.strike()); ADouble vol = rec.input("vol",
market.vol()); ADouble rate = rec.input("rate", market.rate()); ADouble t = rec.input("maturity",
market.maturity()); ADouble dt = t.div(steps); ADouble drift = rate.sub(vol.mul(vol)
.mul(.5))
.mul(dt); ADouble diffusion = vol.mul(dt.sqrt()); ADouble terminal = spot; for (int i = 0; i < steps; i++) terminal = terminal.mul(drift.add(diffusion.mul(rec.randn()))
.exp()); rec.output(terminal.sub(strike)
.max(0)
.mul(rate.neg()
.mul(t)
.exp()));
});
int active = 0;
for (int i = 0; i < tape.size(); i++) {
if (tape.isActive(i)) active++;
String op = tape.op(i)
.name();
int a = tape.argA(i), b = tape.argB(i);
System.out.println("NODE|" + i + "|" + op + "|" + a + "|" + b + "|" + tape.isActive(i));
}
System.out.println("RESULT|" + tape.size() + "|" + active + "|" + steps + "|" + maturity);
}
}
JIT-oriented replay can generate a small class specialised to the recorded graph, avoiding interpretation overhead on repeated evaluations.
The generator emits class-file instructions corresponding to tape nodes, loads inputs, evaluates primitives and stores outputs or adjoints. The JVM then applies its own optimisation and compilation pipeline.
Inspect generated size and instruction mix, separate first-call warm-up from steady-state timing, keep a scalar reference for parity, and profile representative tapes rather than toy expressions.
Generated bytecode is an implementation technique, not a guaranteed speedup. JVM version, workload shape and warm-up behaviour affect the result.