← learnModule 1 · Your first pricer3 min read

ADouble, the number that remembers

What ADouble actually is under the hood, why it looks exactly like a double from the outside, and why two of them can refuse to work together.

A double forgets the instant it's computed. x.add(y) runs the addition, hands you back a number, and by the time you look at it, x, y, and the add itself are gone — there's no way to ask a double how it got its value. What if a number didn't forget?

The whole story

A double computes immediately and forgets everything except the final value. An ADouble records each operation as a node on a tape instead, keeping only a handle to that node — the value doesn't exist until the tape is replayed, and it can be replayed forward or backward, once or millions of times.

Did you know?

An ADouble never stores the number it represents — not even the one it was created with. Its entire field list is a reference to the recorder that made it and one int: which node on the tape it is. rec.input("S0", 100.0) records 100.0 as that node's default replay value, but the handle you get back, the ADouble itself, carries no number at all until something actually replays the tape.

A double computes; an ADouble records

Valuation code written against ADouble looks identical to code written against double — that's deliberate, it's the whole reason a payoff is a "seam" you can swap in three lines. But the two types do opposite things when you call the same method:

double x = 100.0;
double y = x + 37.0;      // computes 137.0 right now, on the spot
ADouble x = rec.input("S0", 100.0);
ADouble y = x.add(37.0);  // appends an ADD node to the tape, returns a
                           // handle to it — no arithmetic happens yet

ADouble.add, straight from the source, doesn't touch a number at all — it asks the recorder for a new node and wraps the result:

public ADouble add(ADouble other) {
  return binary(AadOp.ADD, other);
}

private ADouble binary(AadOp op, ADouble other) {
  if (other.recorder != recorder) {
    throw new IllegalArgumentException("operands come from different recordings");
  }
  return recorder.node(op, node, other.node);
}

sub, mul, div, exp, log, sqrt, abs, max, and min all follow the same shape — every one of them is binary(...) or unary(...) under the hood, appending exactly one node.

Did you know?

That other.recorder != recorder check is the whole reason you can't accidentally mix up two valuations. Every AadRecorder.record(...) call starts a fresh recording with its own tape; an ADouble from one recording carries a reference back to the recorder that made it, and combining it with one from a different recording throws immediately — a plain IllegalArgumentException at the exact line that mixed them, not a silently wrong number three modules later.

Try it yourself

Open ADouble.java and sort its methods into two piles: the ones that take another ADouble (binary, under the hood) and the ones that take a plain double (add(double), sub(double), and so on). Now guess what the second pile actually does before you scroll down — hint: an ADouble and a raw double can't share a recorder, so something has to turn that double into an ADouble first. (It calls recorder.constant(value), the same method that made a constant node for the earlier example.)

▶️ Run it

There's nothing to run yet — an ADouble on its own is just a handle to one node. Module 1.2 is where a handful of these turn into a whole tape you can actually replay.

⚠️ What this doesn't do

This page is the whole ADouble type, and it's small on purpose: eleven arithmetic operations (add, sub, mul, div, neg, exp, log, sqrt, abs, max, min — a few doubled up to also take a plain double), no trig, no comparisons beyond max/min. It doesn't show how a sequence of these becomes an AadTape, how that tape gets replayed, or how a reverse sweep turns a tape into Greeks — that's the next two pages. And a single ADouble can't tell you anything about a market, a payoff, or a bank; it's a building block, not a pricer.

What's next

→ Deeper: Adjoint AD for dummies walks the same (x+2)*x idea all the way through to a hand-computed reverse sweep, node by node. → Next: Recording a tape, turning a few ADouble operations into something you can actually replay.


Questions or corrections? open an issue