Start Here — Price, Then Differentiate

One Sweep, Every Greek: How Adjoint AD Works

Run the Java Monte Carlo engine in this browser: one adjoint sweep yields the price and every first-order sensitivity, then ten price-only revaluations check the Greeks.

Market data
SimulationOptional
Java source
WhyAdjointAd.java

This exact source runs in TeaVM. Form changes update its Java literals and reset manual edits.

package com.nablatensor.examples.learn;

import com.nablatensor.engine.Nabla;
import com.nablatensor.quant.EquityMarket;
import com.nablatensor.quant.GreekFactorEnum;
import com.nablatensor.quant.MonteCarlo;
import com.nablatensor.quant.Products;

/** One adjoint replay against ten central bump revaluations of the same call. */
public final class WhyAdjointAd {
  private WhyAdjointAd() {}

  public static void main(String[] args) {
    EquityMarket market = EquityMarket.of()
        .spot(100)
        .strike(100)
        .vol(20 / 100.0)
        .rate(3 / 100.0)
        .maturity(1)
        .build();
    long scenarios = 20000L;
    long seed = 42L;
    double relativeBump = 0.005;
    try (MonteCarlo<EquityMarket> adjoint = MonteCarlo.of(Products.europeanCall())
        .market(market)
        .steps(1)
        .fp64()
        .greeks()
        .on("cpu")
        .build(); MonteCarlo<EquityMarket> priceOnly = MonteCarlo.of(Products.europeanCall())
        .market(market)
        .steps(1)
        .fp64()
        .priceOnly()
        .on("cpu")
        .build()) {
      Nabla.TypedValuation<EquityMarket> result = adjoint.run(scenarios, seed);
      System.out.println("ROW|price|" + result.price() + "|" + result.price());
      GreekFactorEnum[] factors = GreekFactorEnum.values();
      double bumpSeconds = 0.0;
      for (GreekFactorEnum factor : factors) {
        double value = factor.value(market);
        double h = relativeBump * Math.max(1.0, Math.abs(value));
        Nabla.TypedValuation<EquityMarket> up = priceOnly.run(factor.bump(market, h), scenarios,
            seed);
        Nabla.TypedValuation<EquityMarket> down = priceOnly.run(factor.bump(market, -h), scenarios,
            seed);
        bumpSeconds += up.seconds() + down.seconds();
        double centralBump = (up.price() - down.price()) / (2.0 * h);
        System.out.println("ROW|" + factor.label + "|" + factor.greek(result) + "|" + centralBump);
      }
      System.out.println("TIME|" + result.seconds() + "|" + bumpSeconds + "|10");
      System.out.println("All bump legs used the same seed: " + seed);
    }
  }
}
TeaVM compiles and runs the Java source above in this browser.
Implementation guide

Reverse-mode AD: one output, many inputs

This is the computational idea behind the studio: record a valuation once, then propagate its output sensitivity backward to every active input.

Core mechanism

The forward pass records primitive operations. The reverse pass starts with an adjoint of one at the chosen output and applies the chain rule in reverse graph order. For one price and many market inputs, its cost is roughly a small multiple of one valuation, rather than one full revaluation per Greek.

Practical workflow

Mark the market quantities whose risk you need as active; record the payoff; replay for the required scenarios; then map the gradient vector back to named market factors. Compare selected results to independent bumps when validating the implementation.

Scope and review point

Reverse mode is best for few outputs and many inputs. A Hessian, discontinuous payoff, model calibration loop or multi-output portfolio report needs additional treatment, not merely another reverse pass.