adjointly 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["P","P","P"],"sources":["../src/primitives.ts","../src/arithmetic.ts","../src/capacity.ts","../src/debug.ts","../src/tape.ts","../src/forward.ts","../src/composites.ts","../src/convenience.ts","../src/check.ts"],"sourcesContent":["/*\n * The table of primitive operations: the single source of truth for every operation's value and\n * its local partial derivatives.\n *\n * `plain`, {@link Tape} and {@link Forward} all compute a primitive's value by calling the same\n * `value` function from this table, so a program evaluates to bit-for-bit the same number under\n * all three. They differ only in what they do with the partials: `plain` ignores them, `Tape`\n * records them for the backward sweep, `Forward` multiplies them into tangents on the spot.\n *\n * Each partial receives the operation's own result `y` as well as its arguments, so derivatives\n * that are cheapest in terms of the result (`exp`, `tanh`, `sqrt`, ...) reuse it instead of\n * recomputing.\n */\n\n/** A primitive of one argument: `y = value(x)`, `dy/dx = deriv(x, y)`. */\nexport interface UnaryPrimitive {\n /** The primitive's name, equal to its key in {@link primitives} and its method on `Arithmetic`. */\n readonly name: string;\n /** The value of the operation at `x`. */\n readonly value: (x: number) => number;\n /** The derivative `dy/dx` at `x`, given `y = value(x)`. */\n readonly deriv: (x: number, y: number) => number;\n}\n\n/** A primitive of two arguments: `y = value(a, b)`, with partials `da = dy/da` and `db = dy/db`. */\nexport interface BinaryPrimitive {\n /** The primitive's name, equal to its key in {@link primitives} and its method on `Arithmetic`. */\n readonly name: string;\n /** The value of the operation at `(a, b)`. */\n readonly value: (a: number, b: number) => number;\n /** The partial derivative `dy/da` at `(a, b)`, given `y = value(a, b)`. */\n readonly da: (a: number, b: number, y: number) => number;\n /** The partial derivative `dy/db` at `(a, b)`, given `y = value(a, b)`. */\n readonly db: (a: number, b: number, y: number) => number;\n}\n\nfunction unary(\n name: string,\n value: (x: number) => number,\n deriv: (x: number, y: number) => number,\n): UnaryPrimitive {\n return Object.freeze({ name, value, deriv });\n}\n\nfunction binary(\n name: string,\n value: (a: number, b: number) => number,\n da: (a: number, b: number, y: number) => number,\n db: (a: number, b: number, y: number) => number,\n): BinaryPrimitive {\n return Object.freeze({ name, value, da, db });\n}\n\n/** Whether `a` or `b` is `NaN`: the one case where neither `a <= b` nor `a > b` holds. */\nfunction eitherNaN(a: number, b: number): boolean {\n return Number.isNaN(a) || Number.isNaN(b);\n}\n\n/**\n * Logistic sigmoid `1 / (1 + e^-x)`, evaluated without overflow for any `x`: `exp` is only ever\n * called on `-|x|`.\n */\nfunction sigmoidValue(x: number): number {\n // Stryker disable next-line EqualityOperator: at x = 0 both branches give exactly 0.5.\n if (x >= 0) return 1 / (1 + Math.exp(-x));\n const e = Math.exp(x);\n return e / (1 + e);\n}\n\n/**\n * `sigmoid'(x) = sigmoid(x) * sigmoid(-x) = e / (1 + e)^2` with `e = exp(-|x|)`. Computed from `x`\n * rather than as `y * (1 - y)`, which loses all precision for large positive `x` where `y` rounds\n * to 1.\n */\nfunction sigmoidDeriv(x: number): number {\n const e = Math.exp(-Math.abs(x));\n const s = 1 + e;\n return e / (s * s);\n}\n\n/**\n * Every primitive, keyed by name. Subgradient conventions at points where the derivative does not\n * exist are part of the contract:\n *\n * | primitive | convention |\n * |---|---|\n * | `abs` | derivative at `0` is `0` |\n * | `max`, `min` | on a tie (`a === b`) the whole derivative goes to the first argument `a` |\n * | `pow` | `d/db` is `0` wherever `a <= 0`; `d/da` is `0` wherever `b === 0` |\n * | `sqrt` | derivative at `0` (and `-0`) is `+Infinity` (the true one-sided limit) |\n * | `max`, `min` | if either argument is `NaN`, the value and both partials are `NaN`, as for `abs` |\n *\n * Everywhere else the partials are the ordinary analytic derivatives, and outside a function's\n * domain (`log` of a negative, `sqrt` of a negative) the value and partials are whatever IEEE\n * arithmetic produces, usually `NaN`.\n *\n * @example\n * ```ts\n * import { primitives } from 'adjointly';\n * const x = 0.5;\n * const y = primitives.tanh.value(x); //=> tanh(0.5)\n * const dy = primitives.tanh.deriv(x, y); //=> 1 - tanh(0.5) ** 2\n * ```\n */\nexport const primitives = Object.freeze({\n /** `-x`. */\n neg: unary(\n 'neg',\n (x) => -x,\n () => -1,\n ),\n /** `a + b`. */\n add: binary(\n 'add',\n (a, b) => a + b,\n () => 1,\n () => 1,\n ),\n /** `a - b`. */\n sub: binary(\n 'sub',\n (a, b) => a - b,\n () => 1,\n () => -1,\n ),\n /** `a * b`. */\n mul: binary(\n 'mul',\n (a, b) => a * b,\n (_a, b) => b,\n (a) => a,\n ),\n /** `a / b`. */\n div: binary(\n 'div',\n (a, b) => a / b,\n (_a, b) => 1 / b,\n (_a, b, y) => -y / b,\n ),\n /** `1 / x`. */\n recip: unary(\n 'recip',\n (x) => 1 / x,\n (_x, y) => -y * y,\n ),\n /** `x * x`. */\n square: unary(\n 'square',\n (x) => x * x,\n (x) => 2 * x,\n ),\n /** `sqrt(x)`; derivative `+Infinity` at `0` and at `-0`. */\n sqrt: unary(\n 'sqrt',\n (x) => Math.sqrt(x),\n // `y + 0` turns sqrt(-0) = -0 into +0, so the derivative there is +Infinity, not -Infinity.\n (_x, y) => 0.5 / (y + 0),\n ),\n /** `e^x`. */\n exp: unary(\n 'exp',\n (x) => Math.exp(x),\n (_x, y) => y,\n ),\n /** `e^x - 1`, accurate near `0`. */\n expm1: unary(\n 'expm1',\n (x) => Math.expm1(x),\n (x) => Math.exp(x),\n ),\n /** Natural logarithm. */\n log: unary(\n 'log',\n (x) => Math.log(x),\n (x) => 1 / x,\n ),\n /** `log(1 + x)`, accurate near `0`. */\n log1p: unary(\n 'log1p',\n (x) => Math.log1p(x),\n (x) => 1 / (1 + x),\n ),\n /**\n * `a ** b`, differentiable in both. `d/da = b * a^(b-1)`, taken as `0` when `b === 0` (so\n * `0^0` does not produce `0 * Infinity`). `d/db = a^b * log(a)` for `a > 0` and `0` for\n * `a <= 0`, where the real power is not differentiable in the exponent.\n */\n pow: binary(\n 'pow',\n (a, b) => Math.pow(a, b),\n (a, b) => (b === 0 ? 0 : b * Math.pow(a, b - 1)),\n (a, _b, y) => (a > 0 ? y * Math.log(a) : 0),\n ),\n /** `|x|`; derivative `0` at `0`. */\n abs: unary(\n 'abs',\n (x) => Math.abs(x),\n (x) => (x > 0 ? 1 : x < 0 ? -1 : x === 0 ? 0 : NaN),\n ),\n /**\n * The smaller of `a` and `b`; on a tie the derivative goes to `a`. If either is `NaN`, the value\n * and both partials are `NaN`.\n */\n min: binary(\n 'min',\n (a, b) => Math.min(a, b),\n (a, b) => (a <= b ? 1 : eitherNaN(a, b) ? NaN : 0),\n (a, b) => (a <= b ? 0 : eitherNaN(a, b) ? NaN : 1),\n ),\n /**\n * The larger of `a` and `b`; on a tie the derivative goes to `a`. If either is `NaN`, the value\n * and both partials are `NaN`.\n */\n max: binary(\n 'max',\n (a, b) => Math.max(a, b),\n (a, b) => (a >= b ? 1 : eitherNaN(a, b) ? NaN : 0),\n (a, b) => (a >= b ? 0 : eitherNaN(a, b) ? NaN : 1),\n ),\n /** Sine. */\n sin: unary(\n 'sin',\n (x) => Math.sin(x),\n (x) => Math.cos(x),\n ),\n /** Cosine. */\n cos: unary(\n 'cos',\n (x) => Math.cos(x),\n (x) => -Math.sin(x),\n ),\n /** Hyperbolic tangent. */\n tanh: unary(\n 'tanh',\n (x) => Math.tanh(x),\n // 1 - tanh(x)^2, written as 4e / (1 + e)^2 with e = exp(-2|x|): `1 - y * y` cancels to\n // nothing in the tails, where y rounds to 1.\n (x) => {\n const e = Math.exp(-2 * Math.abs(x));\n const s = 1 + e;\n return (4 * e) / (s * s);\n },\n ),\n /** Logistic sigmoid `1 / (1 + e^-x)`, stable for any `x`. */\n sigmoid: unary('sigmoid', sigmoidValue, sigmoidDeriv),\n /**\n * Softplus `log(1 + e^x)`, stable for any `x`: computed as `x + log1p(e^-x)` for positive `x`\n * and `log1p(e^x)` otherwise, so it neither overflows nor rounds to `0` for very negative `x`.\n * Its derivative is `sigmoid(x)`.\n */\n softplus: unary(\n 'softplus',\n // Stryker disable next-line EqualityOperator: at x = 0 both branches give exactly log(2).\n (x) => (x > 0 ? x + Math.log1p(Math.exp(-x)) : Math.log1p(Math.exp(x))),\n (x) => sigmoidValue(x),\n ),\n});\n\n/** Names of the one-argument primitives. */\nexport type UnaryName = {\n [K in keyof typeof primitives]: (typeof primitives)[K] extends { deriv: unknown } ? K : never;\n}[keyof typeof primitives];\n\n/** Names of the two-argument primitives. */\nexport type BinaryName = Exclude<keyof typeof primitives, UnaryName>;\n\n/** The names of the one-argument primitives, in table order. */\nexport const unaryNames: readonly UnaryName[] = [\n 'neg',\n 'recip',\n 'square',\n 'sqrt',\n 'exp',\n 'expm1',\n 'log',\n 'log1p',\n 'abs',\n 'sin',\n 'cos',\n 'tanh',\n 'sigmoid',\n 'softplus',\n];\n\n/** The names of the two-argument primitives, in table order. */\nexport const binaryNames: readonly BinaryName[] = ['add', 'sub', 'mul', 'div', 'pow', 'min', 'max'];\n","/*\n * The arithmetic interface a differentiable function is written against, and the `plain`\n * implementation of it on ordinary numbers.\n */\n\nimport { primitives as P } from './primitives.ts';\n\n/**\n * Scalar arithmetic over a value type `T`. Write a function once against this interface, generic\n * in `T`, and run it three ways: on plain numbers ({@link plain}), recording a tape for gradients\n * ({@link Tape}), or carrying tangents for directional derivatives ({@link Forward}).\n *\n * Every operation on a `T` must go through these methods. A `T` is opaque: for `Tape` and\n * `Forward` it is an index into storage, and `Math.exp(x)` or `x * y` on it computes nonsense\n * (the branded types make most such mistakes compile errors). Numbers enter through\n * {@link Arithmetic.constant} and leave through {@link Arithmetic.value}.\n *\n * The values every implementation computes come from the same {@link primitives} table, so a\n * function's value is bit-for-bit identical under all of them. Subgradient conventions (`abs` at\n * `0`, ties in `max`/`min`, `pow` with a non-positive base) are documented there.\n *\n * @typeParam T - the value type: `number` for `plain`, `Var` for `Tape`, `Dual` for `Forward`.\n *\n * @example\n * ```ts\n * import { type Arithmetic, plain, gradient } from 'adjointly';\n *\n * // f(x, y) = x * e^y + 3\n * function f<T>(A: Arithmetic<T>, [x, y]: readonly [T, T]): T {\n * return A.addc(A.mul(x, A.exp(y)), 3);\n * }\n * f(plain, [2, 0]); //=> 5\n * gradient(f, [2, 0]); //=> { value: 5, gradient: [1, 2] }\n * ```\n */\nexport interface Arithmetic<T> {\n /**\n * Lift a number into `T` as a constant: its derivative with respect to every input is zero.\n * On a `Tape` it still appends a node (constants are leaves of the tape).\n */\n constant(x: number): T;\n /**\n * The current numeric value of `a`. Use it to branch (`if (A.value(x) > 0)`), to log, or to\n * return a result. The number it returns carries no derivative information: anything computed\n * from it with ordinary arithmetic is a constant as far as differentiation is concerned.\n */\n value(a: T): number;\n\n /** `-a`. */\n neg(a: T): T;\n /** `a + b`. */\n add(a: T, b: T): T;\n /** `a - b`. */\n sub(a: T, b: T): T;\n /** `a * b`. */\n mul(a: T, b: T): T;\n /** `a / b`. */\n div(a: T, b: T): T;\n /** `1 / a`. */\n recip(a: T): T;\n /** `a * a`, one node instead of a `mul` of `a` with itself. */\n square(a: T): T;\n /** `sqrt(a)`; its derivative at `0` is `+Infinity`. */\n sqrt(a: T): T;\n /** `e^a`. */\n exp(a: T): T;\n /** `e^a - 1`, accurate for small `a`. */\n expm1(a: T): T;\n /** Natural logarithm. */\n log(a: T): T;\n /** `log(1 + a)`, accurate for small `a`. */\n log1p(a: T): T;\n /**\n * `a ** b`, differentiable in both arguments. The derivative with respect to `b` is `0` wherever\n * `a <= 0`; the derivative with respect to `a` is `0` wherever `b === 0`. For a fixed numeric\n * exponent use {@link Arithmetic.powc}; for `b = 2` prefer {@link Arithmetic.square}, for\n * `b = 0.5` prefer {@link Arithmetic.sqrt}.\n */\n pow(a: T, b: T): T;\n /** `|a|`; its derivative at `0` is `0`. */\n abs(a: T): T;\n /** The smaller of `a` and `b`; on a tie the derivative flows to `a`. */\n min(a: T, b: T): T;\n /** The larger of `a` and `b`; on a tie the derivative flows to `a`. */\n max(a: T, b: T): T;\n /** Sine. */\n sin(a: T): T;\n /** Cosine. */\n cos(a: T): T;\n /** Hyperbolic tangent. */\n tanh(a: T): T;\n /** Logistic sigmoid `1 / (1 + e^-a)`, stable for any `a`. */\n sigmoid(a: T): T;\n /** Softplus `log(1 + e^a)`, stable for any `a`; its derivative is `sigmoid(a)`. */\n softplus(a: T): T;\n\n /**\n * `a + c` for a plain number `c`: `add(a, constant(c))` without the constant's tape node. Same\n * value and derivative, bit for bit.\n */\n addc(a: T, c: number): T;\n /** `a - c` for a plain number `c`; `sub(a, constant(c))` in one node. */\n subc(a: T, c: number): T;\n /** `c - a` for a plain number `c`; `sub(constant(c), a)` in one node. */\n csub(c: number, a: T): T;\n /** `a * c` for a plain number `c`; `mul(a, constant(c))` in one node. */\n mulc(a: T, c: number): T;\n /** `a / c` for a plain number `c`; `div(a, constant(c))` in one node. */\n divc(a: T, c: number): T;\n /** `c / a` for a plain number `c`; `div(constant(c), a)` in one node. */\n cdiv(c: number, a: T): T;\n /**\n * `a ** c` for a plain number `c`; `pow(a, constant(c))` in one node, with the same conventions\n * (the derivative is `0` when `c === 0`).\n */\n powc(a: T, c: number): T;\n}\n\n/**\n * The operations with a plain-number operand, each the binary primitive `of` with one operand a\n * constant: the first one when `constantFirst`, otherwise the second. Their values and partials\n * are that primitive's, from the table.\n */\nexport const constantOperandOps = {\n addc: { of: 'add', constantFirst: false },\n subc: { of: 'sub', constantFirst: false },\n csub: { of: 'sub', constantFirst: true },\n mulc: { of: 'mul', constantFirst: false },\n divc: { of: 'div', constantFirst: false },\n cdiv: { of: 'div', constantFirst: true },\n powc: { of: 'pow', constantFirst: false },\n} as const;\n\n/** The names of the operations with a plain-number operand. */\nexport type ConstantOperandName = keyof typeof constantOperandOps;\n\n/**\n * The inputs of a {@link ScalarFunction}: one `T` for each number in the point `X`. For a point\n * written as a literal, `[3, 4]`, that is the tuple `readonly [T, T]`, so destructured inputs need\n * no non-null assertions; for a `number[]` it is `readonly T[]`.\n */\nexport type Inputs<T, X extends readonly number[]> = { readonly [K in keyof X]: T };\n\n/**\n * A function of several inputs written once against {@link Arithmetic}, the shape the\n * conveniences ({@link gradient}, {@link jvp}, {@link evaluate}, {@link checkGradient}) accept.\n * The input array has exactly as many elements as the point it is evaluated at; `X` is the type of\n * that point, so `ScalarFunction<[number, number]>` takes two inputs.\n *\n * @example\n * ```ts\n * const rosenbrock: ScalarFunction<[number, number]> = (A, [x, y]) =>\n * A.add(\n * A.square(A.csub(1, x)),\n * A.mulc(A.square(A.sub(y, A.square(x))), 100),\n * );\n * evaluate(rosenbrock, [1, 1]); //=> 0\n * gradient(rosenbrock, [0, 0]).gradient; //=> [-2, 0]\n * ```\n */\nexport type ScalarFunction<X extends readonly number[] = readonly number[]> = <T>(\n A: Arithmetic<T>,\n xs: Inputs<T, X>,\n) => T;\n\n/**\n * {@link Arithmetic} on ordinary numbers: computes values only, with nothing recorded. Use it to\n * evaluate a differentiable function at full speed when no derivative is needed.\n *\n * @example\n * ```ts\n * import { plain } from 'adjointly';\n * plain.add(plain.constant(1), plain.exp(0)); //=> 2\n * ```\n */\nexport const plain: Arithmetic<number> = {\n constant: (x) => x,\n value: (a) => a,\n neg: P.neg.value,\n add: P.add.value,\n sub: P.sub.value,\n mul: P.mul.value,\n div: P.div.value,\n recip: P.recip.value,\n square: P.square.value,\n sqrt: P.sqrt.value,\n exp: P.exp.value,\n expm1: P.expm1.value,\n log: P.log.value,\n log1p: P.log1p.value,\n pow: P.pow.value,\n abs: P.abs.value,\n min: P.min.value,\n max: P.max.value,\n sin: P.sin.value,\n cos: P.cos.value,\n tanh: P.tanh.value,\n sigmoid: P.sigmoid.value,\n softplus: P.softplus.value,\n // The constant-operand operations take their arguments in the same order as the primitive.\n addc: P.add.value,\n subc: P.sub.value,\n csub: P.sub.value,\n mulc: P.mul.value,\n divc: P.div.value,\n cdiv: P.div.value,\n powc: P.pow.value,\n};\n","/*\n * The `capacity` option shared by Tape and Forward.\n */\n\n/** `capacity` if it is a positive integer, the default if it is not given; otherwise throws. */\nexport function initialCapacity(capacity: number | undefined, owner: string): number {\n if (capacity === undefined) return 1024;\n if (!Number.isInteger(capacity) || capacity < 1) {\n throw new RangeError(`${owner}: capacity must be a positive integer, not ${String(capacity)}`);\n }\n return capacity;\n}\n","/*\n * The opt-in debug mode of Tape and Forward (`{ debug: true }`).\n *\n * A Var or Dual is a plain number at run time, the index of its slot, so nothing stops a raw\n * number, a stale handle or another tape's handle from being used as one: it silently reads\n * whatever slot has that index. In debug mode the handles a tape gives out are instead\n * `epoch * 2^31 + index`, where the epoch is unique to the tape and to the stretch of recording\n * since its last clear() or rewind(), and every method checks and decodes its operands before\n * running the ordinary code. Each slot remembers the epoch it was written in, so a handle whose\n * slot has since been discarded, or reused, is caught too.\n *\n * The checks are installed as own properties of the debug instance, wrapping the class's methods;\n * the class itself is untouched, so a tape without `debug` runs exactly the code it ran before.\n */\n\nimport { constantOperandOps } from './arithmetic.ts';\nimport { binaryNames, unaryNames } from './primitives.ts';\n\n/** Indices fit in 31 bits (parents are stored in an Int32Array). */\nconst STRIDE = 2 ** 31;\n/** Epochs run from 1 to EPOCHS - 1, so that a handle stays below 2^52, an exact integer. */\nconst EPOCHS = 2 ** 21;\n\nlet lastEpoch = 0;\n\n/**\n * A fresh epoch, unique among all debug tapes and Forwards until 2^21 - 1 epochs have been used,\n * after which numbering wraps around; a stale handle could then be mistaken for a live one only if\n * it were used exactly that many clears later, at the same index.\n */\nexport function nextEpoch(): number {\n lastEpoch = (lastEpoch % (EPOCHS - 1)) + 1;\n return lastEpoch;\n}\n\n/** The handles of one debug Tape or Forward: issuing them and checking them. */\nclass Handles {\n private epoch = 0;\n /** Every epoch this instance has used, to tell its own stale handles from another's. */\n private readonly own = new Set<number>();\n /** The epoch each slot was last written in. */\n private slotEpoch = new Int32Array(64);\n\n private readonly kind: string;\n private readonly first: number;\n private readonly target: { readonly size: number };\n\n constructor(kind: string, first: number, target: { readonly size: number }) {\n this.kind = kind;\n this.first = first;\n this.target = target;\n this.begin();\n }\n\n /** Start a new epoch: handles issued from now on are told apart from earlier ones. */\n begin(): void {\n this.epoch = nextEpoch();\n this.own.add(this.epoch);\n }\n\n /** The handle for the slot `index` just written. */\n issue(index: number): number {\n while (index >= this.slotEpoch.length) {\n const grown = new Int32Array(this.slotEpoch.length * 2);\n grown.set(this.slotEpoch);\n this.slotEpoch = grown;\n }\n this.slotEpoch[index] = this.epoch;\n return this.epoch * STRIDE + index;\n }\n\n /**\n * Throw if `value`, given where a plain number belongs, is one of this instance's variables: what\n * a variable mistaken for a number looks like, typically after a `typeof x === 'number'` test,\n * which every variable passes. Only this instance's handles are recognised, so that no ordinary\n * number is ever refused: a handle is a whole number of 2^31 or more, encoding an epoch of this\n * instance and a slot written in that epoch.\n */\n checkNumber(value: unknown, where: string): void {\n if (typeof value !== 'number' || !Number.isSafeInteger(value)) return;\n const epoch = Math.floor(value / STRIDE);\n const index = value - epoch * STRIDE;\n if (this.own.has(epoch) && this.slotEpoch[index] === epoch) {\n throw new RangeError(\n `${where} is a variable, where a plain number belongs. A variable is a number at run time, ` +\n \"so typeof x === 'number' is true for it too: keep variables and numbers apart instead.\",\n );\n }\n }\n\n /** The slot index of `handle`, or a `RangeError` saying what is wrong with it. */\n resolve(handle: unknown, where: string): number {\n if (typeof handle !== 'number' || !Number.isSafeInteger(handle) || handle < 0) {\n throw new RangeError(`${where} is not a variable (it is ${String(handle)})`);\n }\n const epoch = Math.floor(handle / STRIDE);\n const index = handle - epoch * STRIDE;\n if (epoch === 0) {\n throw new RangeError(\n `${where} is the plain number ${String(handle)}, not a variable of this ${this.kind}. ` +\n 'Numbers enter through constant() or input(), or as the number operand of addc(), ' +\n 'mulc() and the like. Do not tell variables from numbers with typeof: both are numbers.',\n );\n }\n if (!this.own.has(epoch)) {\n throw new RangeError(`${where} is a variable of another Tape or Forward`);\n }\n if (\n // (A slot below the first is never written, so the epoch test covers it.)\n index >= this.first + this.target.size ||\n this.slotEpoch[index] !== epoch\n ) {\n throw new RangeError(`${where} is stale: its node was discarded by clear() or rewind()`);\n }\n return index;\n }\n}\n\n/**\n * How a method's arguments and result are treated: a variable, a list of them, a plain number, a\n * list of them, or anything (passed through).\n */\ntype Arg = 'v' | 'vs' | 'n' | 'ns' | '-';\ntype Signature = readonly [readonly Arg[], Arg];\n\nconst arithmetic: Record<string, Signature> = {\n constant: [['n'], 'v'],\n value: [['v'], '-'],\n};\nfor (const name of unaryNames) arithmetic[name] = [['v'], 'v'];\nfor (const name of binaryNames) arithmetic[name] = [['v', 'v'], 'v'];\nfor (const [name, { constantFirst }] of Object.entries(constantOperandOps)) {\n arithmetic[name] = [constantFirst ? ['n', 'v'] : ['v', 'n'], 'v'];\n}\n\nconst signatures: Record<'Tape' | 'Forward', Record<string, Signature>> = {\n Tape: {\n ...arithmetic,\n input: [['n'], 'v'],\n inputs: [['ns'], 'vs'],\n backward: [['v', 'n'], '-'],\n vjp: [['vs', '-'], '-'],\n adjoint: [['v'], '-'],\n adjoints: [['vs', '-'], '-'],\n gradient: [['v', 'vs', '-'], '-'],\n },\n Forward: {\n ...arithmetic,\n input: [['n', 'n'], 'v'],\n tangent: [['v'], '-'],\n },\n};\n\ntype Method = (...args: unknown[]) => unknown;\n\n/**\n * Turn `target`, a freshly constructed Tape or Forward, into its debug version. `first` is the\n * index of its first slot (1 for a Tape, whose slot 0 is reserved; 0 for a Forward).\n */\nexport function installChecks(\n target: { readonly size: number },\n kind: 'Tape' | 'Forward',\n first: number,\n): void {\n const handles = new Handles(kind, first, target);\n const self = target as unknown as Record<string, Method>;\n const proto = Object.getPrototypeOf(target) as Record<string, Method>;\n\n const element = (where: string, k: number): string => `${where}, element ${String(k + 1)}`;\n const decode = (arg: Arg, value: unknown, where: string): unknown => {\n switch (arg) {\n case 'v':\n return handles.resolve(value, where);\n case 'vs':\n return (value as readonly unknown[]).map((h, k) => handles.resolve(h, element(where, k)));\n case 'n':\n handles.checkNumber(value, where);\n return value;\n case 'ns':\n Array.from(value as ArrayLike<unknown>, (x, k) => {\n handles.checkNumber(x, element(where, k));\n });\n return value;\n case '-':\n return value;\n }\n };\n const encode = (arg: Arg, value: unknown): unknown =>\n arg === 'v'\n ? handles.issue(value as number)\n : arg === 'vs'\n ? (value as number[]).map((i) => handles.issue(i))\n : value;\n\n for (const [name, [args, result]] of Object.entries(signatures[kind])) {\n // gradient() calls backward() and adjoints() on `this`, which would then see decoded\n // indices; the decoded call goes to the class's own methods instead.\n const run: Method =\n name === 'gradient'\n ? (output, inputs, out) => {\n proto.backward!.call(target, output);\n return proto.adjoints!.call(target, inputs, out);\n }\n : (...decoded) => proto[name]!.apply(target, decoded);\n self[name] = (...params: unknown[]) => {\n const decoded = params.map((p, i) =>\n decode(args[i] ?? '-', p, `${kind}.${name}: argument ${String(i + 1)}`),\n );\n return encode(result, run(...decoded));\n };\n }\n // Every clear() and rewind() starts a new epoch, retiring the handles of discarded slots.\n self.clear = () => {\n proto.clear!.call(target);\n handles.begin();\n };\n if (kind === 'Tape') {\n self.rewind = (mark) => {\n proto.rewind!.call(target, mark);\n handles.begin();\n };\n }\n}\n","/*\n * Reverse mode: the {@link Tape}, which records a computation into typed arrays and sweeps it\n * backwards for gradients.\n */\n\nimport type { Arithmetic } from './arithmetic.ts';\nimport { initialCapacity } from './capacity.ts';\nimport { installChecks } from './debug.ts';\nimport { primitives as P } from './primitives.ts';\n\ndeclare const varBrand: unique symbol;\ndeclare const markBrand: unique symbol;\n\n/**\n * A variable on a {@link Tape}: at run time a plain integer, the index of its node. The brand makes\n * it a compile-time error to pass a raw number where a `Var` is expected, or to use a `Var` from a\n * {@link Forward} where a tape's is expected; numbers enter a tape through\n * {@link Tape.input} or {@link Tape.constant}.\n *\n * A `Var` is only meaningful on the tape that created it, and only until that tape is cleared or\n * rewound past it. Neither is checked at run time.\n *\n * Because it is a number underneath, `x + y` on two `Var`s type-checks (TypeScript allows\n * arithmetic on number subtypes) and silently adds two indices. Always use `tape.add(x, y)`.\n */\nexport type Var = number & { readonly [varBrand]: 'adjointly.Var' };\n\n/** A checkpoint on a {@link Tape}, returned by {@link Tape.mark} and consumed by {@link Tape.rewind}. */\nexport type Mark = number & { readonly [markBrand]: 'adjointly.Mark' };\n\n/** Options for constructing a {@link Tape}. */\nexport interface TapeOptions {\n /**\n * Initial capacity in nodes, a positive integer (default 1024; anything else throws a\n * `RangeError`). The tape doubles whenever it fills, so this only matters to avoid the first few\n * reallocations when the size of the computation is known.\n */\n readonly capacity?: number;\n /**\n * Check every operand (default `false`). Each method then verifies that its variables are live\n * variables of this Tape, and throws a `RangeError` naming the method and the argument for a\n * plain number, a variable from before `clear()` or `rewind()`, or one from another Tape or Forward.\n * Off, nothing is checked and nothing is slower: a debug Tape is a separate set of methods on\n * that one instance. Its variables are no longer small integers (they encode which recording\n * they belong to), which is invisible unless they are used as numbers.\n *\n * @example\n * ```ts\n * const t = new Tape({ debug: true });\n * t.input(5);\n * let caught = '';\n * try {\n * t.exp(1 as Var);\n * } catch (e) {\n * caught = (e as Error).message.split(',')[0]!;\n * }\n * caught; //=> 'Tape.exp: argument 1 is the plain number 1'\n * ```\n */\n readonly debug?: boolean;\n}\n\n/**\n * Slot 0 of every array is a sink, not a node: leaves name it as both parents, and unary nodes as\n * their second, always with a zero partial. Every parent index is therefore a valid slot, and the\n * backward sweep's zero rule never writes to the sink.\n */\nconst SINK = 0;\n\n/**\n * Reverse-mode automatic differentiation: an {@link Arithmetic} over {@link Var} that records\n * every operation as a node on a tape, then computes the derivative of one output with respect to\n * every node in a single backward sweep.\n *\n * Each node stores its value, up to two parent indices and the local partial derivatives with\n * respect to them, computed when the node is recorded, and its adjoint: six typed arrays, 40\n * bytes a node. They double when full, so recording does not allocate once the tape has grown to\n * the size of the computation, and a tape reused across evaluations (via {@link Tape.clear} or\n * {@link Tape.rewind}) never allocates again.\n *\n * The backward sweep visits nodes from the output down to the first, adding\n * `partial * adjoint(child)` into each parent's adjoint, under the zero rule: if the partial or\n * the adjoint is exactly 0 the term is 0, even when the other is infinite or `NaN`, so an unused\n * `sqrt(0)` or `log(0)` does not poison the gradient.\n *\n * @example\n * ```ts\n * import { Tape } from 'adjointly';\n * const t = new Tape();\n * const x = t.input(2);\n * const y = t.input(3);\n * const f = t.mul(x, t.sin(y)); // x * sin(y)\n * t.backward(f);\n * t.adjoint(x); //=> sin(3)\n * t.adjoint(y); //=> 2 * cos(3)\n * ```\n */\nexport class Tape implements Arithmetic<Var> {\n private val: Float64Array;\n private pa: Int32Array;\n private pb: Int32Array;\n private da: Float64Array;\n private db: Float64Array;\n private adj: Float64Array;\n /** The next free slot; nodes occupy slots 1 to n - 1. */\n private n = 1;\n\n /** An empty tape; see {@link TapeOptions}. */\n constructor(options: TapeOptions = {}) {\n const cap = initialCapacity(options.capacity, 'Tape') + 1; // + the sink\n this.val = new Float64Array(cap);\n this.pa = new Int32Array(cap);\n this.pb = new Int32Array(cap);\n this.da = new Float64Array(cap);\n this.db = new Float64Array(cap);\n this.adj = new Float64Array(cap);\n if (options.debug === true) installChecks(this, 'Tape', SINK + 1);\n }\n\n /** The number of nodes recorded so far. */\n get size(): number {\n return this.n - 1;\n }\n\n /** The number of nodes the tape can hold before it next grows. */\n get capacity(): number {\n return this.val.length - 1;\n }\n\n /** Forget every node, keeping the storage. Every `Var` previously issued becomes invalid. */\n clear(): void {\n this.n = 1;\n }\n\n /**\n * A checkpoint at the current end of the tape. {@link Tape.rewind} to it discards everything\n * recorded after it and keeps everything before, so a shared prefix (inputs, parameters,\n * precomputed features) is recorded once and reused across many evaluations.\n *\n * @example\n * ```ts\n * const t = new Tape();\n * const w = t.input(0.5);\n * const m = t.mark();\n * let grad = 0;\n * for (const x of [1, 2, 3]) {\n * t.rewind(m); // drop the previous sample's nodes, keep w\n * const loss = t.square(t.subc(t.mulc(w, x), 1));\n * t.backward(loss);\n * grad += t.adjoint(w);\n * }\n * grad; //=> 2 * ((0.5 - 1) * 1 + (1 - 1) * 2 + (1.5 - 1) * 3)\n * t.size; //=> 4\n * ```\n */\n mark(): Mark {\n return this.n as Mark;\n }\n\n /**\n * Truncate the tape back to `mark`. Vars created before the mark stay valid with their values;\n * Vars created after it become invalid. Throws if `mark` is beyond the current end (the tape was\n * already cleared or rewound further back).\n */\n rewind(mark: Mark): void {\n if (mark > this.n || mark < 1) {\n throw new RangeError(`Tape.rewind: mark ${mark} is outside the tape (size ${this.size})`);\n }\n this.n = mark;\n }\n\n /**\n * Record an independent variable with value `x`. Identical to {@link Tape.constant} on the tape\n * (both are leaves and both accumulate an adjoint); the separate name says which leaves you\n * intend to read a gradient for.\n */\n input(x: number): Var {\n return this.push(x, SINK, 0, SINK, 0);\n }\n\n /**\n * Record several independent variables at once; see {@link Tape.input}. For a tuple of numbers\n * the result is a tuple of the same length, so it destructures without non-null assertions.\n *\n * @example\n * ```ts\n * const t = new Tape();\n * const [x, y] = t.inputs([2, 3]);\n * t.value(t.mul(x, y)); //=> 6\n * ```\n */\n inputs<const X extends readonly number[]>(xs: X): { -readonly [K in keyof X]: Var };\n inputs(xs: ArrayLike<number>): Var[];\n inputs(xs: ArrayLike<number>): Var[] {\n return Array.from(xs, (x) => this.push(x, SINK, 0, SINK, 0));\n }\n\n /**\n * Lift a number into `T` as a constant: its derivative with respect to every input is zero.\n * On a `Tape` it still appends a node (constants are leaves of the tape).\n */\n constant(x: number): Var {\n return this.push(x, SINK, 0, SINK, 0);\n }\n\n /**\n * The current numeric value of `a`. Use it to branch (`if (A.value(x) > 0)`), to log, or to\n * return a result. The number it returns carries no derivative information: anything computed\n * from it with ordinary arithmetic is a constant as far as differentiation is concerned.\n */\n value(a: Var): number {\n return this.val[a]!;\n }\n\n // ---- backward ----\n\n /**\n * Run one backward sweep from `output`, seeding its adjoint with `seed` (default 1). Afterwards\n * {@link Tape.adjoint} of any node `v` is `seed * d(output)/d(v)`. Every call starts from zero\n * adjoints, so call it once per output whose gradient you want; the recorded tape is not\n * changed and can be swept any number of times.\n */\n backward(output: Var, seed = 1): void {\n if (output < 1 || output >= this.n) {\n throw new RangeError(\n `Tape.backward: ${output} is not a node of this tape (size ${this.size})`,\n );\n }\n const adj = this.adj;\n adj.fill(0, 0, output + 1);\n adj[output] = seed;\n this.sweep(output);\n }\n\n /**\n * Vector-Jacobian product: one backward sweep seeded with `weights[k]` at `outputs[k]`.\n * Afterwards {@link Tape.adjoint} of `v` is `sum_k weights[k] * d(outputs[k])/d(v)`. An output\n * listed twice gets the sum of its weights; with no outputs every adjoint is zero.\n *\n * @example\n * ```ts\n * const t = new Tape();\n * const x = t.input(1);\n * const f = t.square(x); // df/dx = 2x\n * const g = t.exp(x); // dg/dx = e^x\n * t.vjp([f, g], [2, 3]); // gradient of 2f + 3g in one sweep\n * t.adjoint(x); //=> 2 * 2 + 3 * E\n * ```\n */\n vjp(outputs: readonly Var[], weights: ArrayLike<number>): void {\n if (outputs.length !== weights.length) {\n throw new RangeError(`Tape.vjp: ${outputs.length} outputs but ${weights.length} weights`);\n }\n let top = 0; // the latest output, where the sweep starts\n for (const o of outputs) {\n if (o < 1 || o >= this.n) {\n throw new RangeError(`Tape.vjp: ${o} is not a node of this tape (size ${this.size})`);\n }\n top = Math.max(top, o);\n }\n const adj = this.adj;\n if (outputs.length === 0) {\n adj.fill(0);\n return;\n }\n adj.fill(0, 0, top + 1);\n outputs.forEach((o, k) => {\n adj[o] = adj[o]! + weights[k]!;\n });\n this.sweep(top);\n }\n\n /**\n * The adjoint of `v` from the last {@link Tape.backward} or {@link Tape.vjp}: the derivative of\n * the seeded output(s) with respect to `v`. Zero for a node the output does not depend on.\n * Meaningless for a node recorded after the output of that sweep.\n */\n adjoint(v: Var): number {\n return this.adj[v]!;\n }\n\n /**\n * Read the adjoints of `vars` into `out` (allocated if not given) and return it: the gradient\n * restricted to those variables, in their order.\n */\n adjoints(vars: readonly Var[], out: Float64Array = new Float64Array(vars.length)): Float64Array {\n const adj = this.adj;\n vars.forEach((v, k) => {\n out[k] = adj[v]!;\n });\n return out;\n }\n\n /**\n * `backward(output)` then `adjoints(inputs, out)`: the gradient of `output` with respect to\n * `inputs`, in their order.\n *\n * @example\n * ```ts\n * const t = new Tape();\n * const [x, y] = t.inputs([1, 2]);\n * t.gradient(t.mul(x, y), [x, y]); //=> [2, 1]\n * ```\n */\n gradient(output: Var, inputs: readonly Var[], out?: Float64Array): Float64Array {\n this.backward(output);\n return this.adjoints(inputs, out);\n }\n\n private sweep(top: number): void {\n const adj = this.adj,\n pa = this.pa,\n pb = this.pb,\n da = this.da,\n db = this.db;\n // A leaf's partials are 0 and so is a unary node's second, so the zero rule below also keeps\n // every write out of the sink.\n // Stryker disable next-line EqualityOperator: also visiting the sink changes nothing read.\n for (let i = top; i > SINK; i--) {\n // 0 times anything is 0: a zero adjoint or a zero partial passes nothing on, even where the\n // other factor is infinite or NaN. Forward mode applies the same rule (`times` there).\n const g = adj[i]!;\n if (g === 0) continue;\n const dA = da[i]!;\n if (dA !== 0) {\n const a = pa[i]!;\n adj[a] = adj[a]! + dA * g;\n }\n const dB = db[i]!;\n if (dB !== 0) {\n const b = pb[i]!;\n adj[b] = adj[b]! + dB * g;\n }\n }\n }\n\n // ---- recording ----\n\n private push(y: number, a: number, dA: number, b: number, dB: number): Var {\n const i = this.n;\n if (i === this.val.length) this.grow();\n this.val[i] = y;\n this.pa[i] = a;\n this.da[i] = dA;\n this.pb[i] = b;\n this.db[i] = dB;\n this.n = i + 1;\n return i as Var;\n }\n\n private grow(): void {\n const cap = this.val.length * 2;\n const val = new Float64Array(cap);\n val.set(this.val);\n this.val = val;\n const pa = new Int32Array(cap);\n pa.set(this.pa);\n this.pa = pa;\n const pb = new Int32Array(cap);\n pb.set(this.pb);\n this.pb = pb;\n const da = new Float64Array(cap);\n da.set(this.da);\n this.da = da;\n const db = new Float64Array(cap);\n db.set(this.db);\n this.db = db;\n const adj = new Float64Array(cap);\n adj.set(this.adj); // adjoints of the last sweep stay readable while recording continues\n this.adj = adj;\n }\n\n // One method per primitive, each calling its own table entry directly. A shared helper taking\n // the table entry as an argument was 2x slower: its call sites see every primitive, so V8 cannot\n // inline them, while these are monomorphic. Keep the table the only place a formula is written.\n\n /** `-a`. */\n neg(a: Var): Var {\n const x = this.val[a]!;\n const y = P.neg.value(x);\n return this.push(y, a, P.neg.deriv(x, y), SINK, 0);\n }\n /** `a + b`. */\n add(a: Var, b: Var): Var {\n const x = this.val[a]!;\n const z = this.val[b]!;\n const y = P.add.value(x, z);\n return this.push(y, a, P.add.da(x, z, y), b, P.add.db(x, z, y));\n }\n /** `a - b`. */\n sub(a: Var, b: Var): Var {\n const x = this.val[a]!;\n const z = this.val[b]!;\n const y = P.sub.value(x, z);\n return this.push(y, a, P.sub.da(x, z, y), b, P.sub.db(x, z, y));\n }\n /** `a * b`. */\n mul(a: Var, b: Var): Var {\n const x = this.val[a]!;\n const z = this.val[b]!;\n const y = P.mul.value(x, z);\n return this.push(y, a, P.mul.da(x, z, y), b, P.mul.db(x, z, y));\n }\n /** `a / b`. */\n div(a: Var, b: Var): Var {\n const x = this.val[a]!;\n const z = this.val[b]!;\n const y = P.div.value(x, z);\n return this.push(y, a, P.div.da(x, z, y), b, P.div.db(x, z, y));\n }\n /** `1 / a`. */\n recip(a: Var): Var {\n const x = this.val[a]!;\n const y = P.recip.value(x);\n return this.push(y, a, P.recip.deriv(x, y), SINK, 0);\n }\n /** `a * a`, one node instead of a `mul` of `a` with itself. */\n square(a: Var): Var {\n const x = this.val[a]!;\n const y = P.square.value(x);\n return this.push(y, a, P.square.deriv(x, y), SINK, 0);\n }\n /** `sqrt(a)`; its derivative at `0` is `+Infinity`. */\n sqrt(a: Var): Var {\n const x = this.val[a]!;\n const y = P.sqrt.value(x);\n return this.push(y, a, P.sqrt.deriv(x, y), SINK, 0);\n }\n /** `e^a`. */\n exp(a: Var): Var {\n const x = this.val[a]!;\n const y = P.exp.value(x);\n return this.push(y, a, P.exp.deriv(x, y), SINK, 0);\n }\n /** `e^a - 1`, accurate for small `a`. */\n expm1(a: Var): Var {\n const x = this.val[a]!;\n const y = P.expm1.value(x);\n return this.push(y, a, P.expm1.deriv(x, y), SINK, 0);\n }\n /** Natural logarithm. */\n log(a: Var): Var {\n const x = this.val[a]!;\n const y = P.log.value(x);\n return this.push(y, a, P.log.deriv(x, y), SINK, 0);\n }\n /** `log(1 + a)`, accurate for small `a`. */\n log1p(a: Var): Var {\n const x = this.val[a]!;\n const y = P.log1p.value(x);\n return this.push(y, a, P.log1p.deriv(x, y), SINK, 0);\n }\n /**\n * `a ** b`, differentiable in both arguments. The derivative with respect to `b` is `0` wherever\n * `a <= 0`; the derivative with respect to `a` is `0` wherever `b === 0`. For a fixed numeric\n * exponent use {@link Arithmetic.powc}; for `b = 2` prefer {@link Arithmetic.square}, for\n * `b = 0.5` prefer {@link Arithmetic.sqrt}.\n */\n pow(a: Var, b: Var): Var {\n const x = this.val[a]!;\n const z = this.val[b]!;\n const y = P.pow.value(x, z);\n return this.push(y, a, P.pow.da(x, z, y), b, P.pow.db(x, z, y));\n }\n /** `|a|`; its derivative at `0` is `0`. */\n abs(a: Var): Var {\n const x = this.val[a]!;\n const y = P.abs.value(x);\n return this.push(y, a, P.abs.deriv(x, y), SINK, 0);\n }\n /** The smaller of `a` and `b`; on a tie the derivative flows to `a`. */\n min(a: Var, b: Var): Var {\n const x = this.val[a]!;\n const z = this.val[b]!;\n const y = P.min.value(x, z);\n return this.push(y, a, P.min.da(x, z, y), b, P.min.db(x, z, y));\n }\n /** The larger of `a` and `b`; on a tie the derivative flows to `a`. */\n max(a: Var, b: Var): Var {\n const x = this.val[a]!;\n const z = this.val[b]!;\n const y = P.max.value(x, z);\n return this.push(y, a, P.max.da(x, z, y), b, P.max.db(x, z, y));\n }\n /** Sine. */\n sin(a: Var): Var {\n const x = this.val[a]!;\n const y = P.sin.value(x);\n return this.push(y, a, P.sin.deriv(x, y), SINK, 0);\n }\n /** Cosine. */\n cos(a: Var): Var {\n const x = this.val[a]!;\n const y = P.cos.value(x);\n return this.push(y, a, P.cos.deriv(x, y), SINK, 0);\n }\n /** Hyperbolic tangent. */\n tanh(a: Var): Var {\n const x = this.val[a]!;\n const y = P.tanh.value(x);\n return this.push(y, a, P.tanh.deriv(x, y), SINK, 0);\n }\n /** Logistic sigmoid `1 / (1 + e^-a)`, stable for any `a`. */\n sigmoid(a: Var): Var {\n const x = this.val[a]!;\n const y = P.sigmoid.value(x);\n return this.push(y, a, P.sigmoid.deriv(x, y), SINK, 0);\n }\n /** Softplus `log(1 + e^a)`, stable for any `a`; its derivative is `sigmoid(a)`. */\n softplus(a: Var): Var {\n const x = this.val[a]!;\n const y = P.softplus.value(x);\n return this.push(y, a, P.softplus.deriv(x, y), SINK, 0);\n }\n\n // The constant-operand operations: the binary primitive with one operand a plain number, one\n // node with one parent.\n\n /**\n * `a + c` for a plain number `c`: `add(a, constant(c))` without the constant's tape node. Same\n * value and derivative, bit for bit.\n */\n addc(a: Var, c: number): Var {\n const x = this.val[a]!;\n const y = P.add.value(x, c);\n return this.push(y, a, P.add.da(x, c, y), SINK, 0);\n }\n\n /** `a - c` for a plain number `c`; `sub(a, constant(c))` in one node. */\n subc(a: Var, c: number): Var {\n const x = this.val[a]!;\n const y = P.sub.value(x, c);\n return this.push(y, a, P.sub.da(x, c, y), SINK, 0);\n }\n\n /** `c - a` for a plain number `c`; `sub(constant(c), a)` in one node. */\n csub(c: number, a: Var): Var {\n const x = this.val[a]!;\n const y = P.sub.value(c, x);\n return this.push(y, a, P.sub.db(c, x, y), SINK, 0);\n }\n\n /** `a * c` for a plain number `c`; `mul(a, constant(c))` in one node. */\n mulc(a: Var, c: number): Var {\n const x = this.val[a]!;\n const y = P.mul.value(x, c);\n return this.push(y, a, P.mul.da(x, c, y), SINK, 0);\n }\n\n /** `a / c` for a plain number `c`; `div(a, constant(c))` in one node. */\n divc(a: Var, c: number): Var {\n const x = this.val[a]!;\n const y = P.div.value(x, c);\n return this.push(y, a, P.div.da(x, c, y), SINK, 0);\n }\n\n /** `c / a` for a plain number `c`; `div(constant(c), a)` in one node. */\n cdiv(c: number, a: Var): Var {\n const x = this.val[a]!;\n const y = P.div.value(c, x);\n return this.push(y, a, P.div.db(c, x, y), SINK, 0);\n }\n\n /**\n * `a ** c` for a plain number `c`; `pow(a, constant(c))` in one node, with the same conventions\n * (the derivative is `0` when `c === 0`).\n */\n powc(a: Var, c: number): Var {\n const x = this.val[a]!;\n const y = P.pow.value(x, c);\n return this.push(y, a, P.pow.da(x, c, y), SINK, 0);\n }\n}\n","/*\n * Forward mode: {@link Forward}, which carries a tangent alongside every value.\n */\n\nimport type { Arithmetic } from './arithmetic.ts';\nimport { initialCapacity } from './capacity.ts';\nimport { installChecks } from './debug.ts';\nimport { primitives as P } from './primitives.ts';\n\ndeclare const dualBrand: unique symbol;\n\n/**\n * A value in a {@link Forward} computation: at run time a plain integer, the index of its slot.\n * Branded separately from `Var`, so a forward value cannot be passed to a `Tape` or vice versa, and\n * a raw number cannot be passed where a `Dual` is expected.\n *\n * Valid only on the `Forward` that created it, and only until that `Forward` is cleared.\n */\nexport type Dual = number & { readonly [dualBrand]: 'adjointly.Dual' };\n\n/**\n * One term of the chain rule, `d * t`, except that a zero factor makes the term zero even when the\n * other factor is infinite or `NaN`. The tape's backward sweep applies the same rule.\n */\nfunction times(d: number, t: number): number {\n return d === 0 || t === 0 ? 0 : d * t;\n}\n\n/** Options for constructing a {@link Forward}. */\nexport interface ForwardOptions {\n /**\n * Initial capacity in values, a positive integer (default 1024; anything else throws a\n * `RangeError`); storage doubles whenever it fills.\n */\n readonly capacity?: number;\n /**\n * Check every operand (default `false`). Each method then verifies that its variables are live\n * variables of this Forward, and throws a `RangeError` naming the method and the argument for a\n * plain number, a variable from before `clear()`, or one from another Tape or Forward.\n * Off, nothing is checked and nothing is slower: a debug Forward is a separate set of methods on\n * that one instance. Its variables are no longer small integers (they encode which recording\n * they belong to), which is invisible unless they are used as numbers.\n *\n * @example\n * ```ts\n * const t = new Forward({ debug: true });\n * t.input(5, 1);\n * let caught = '';\n * try {\n * t.exp(1 as Dual);\n * } catch (e) {\n * caught = (e as Error).message.split(',')[0]!;\n * }\n * caught; //=> 'Forward.exp: argument 1 is the plain number 1'\n * ```\n */\n readonly debug?: boolean;\n}\n\n/**\n * Forward-mode automatic differentiation: an {@link Arithmetic} over {@link Dual} where every value\n * carries a tangent, its derivative along one direction chosen when the inputs are created. One\n * evaluation gives the directional derivative `grad f(x) . v` (a Jacobian-vector product) for every\n * output at once, with no backward sweep and nothing kept but a value and a tangent per slot.\n *\n * Tangents propagate as `t(y) = da * t(a) + db * t(b)` using the same partials the {@link Tape}\n * records, under the same zero rule: if a partial or a tangent is exactly 0 the term is 0, even when\n * the other is infinite or `NaN`, so an infinite partial such as `sqrt` at `0` does not turn a zero\n * tangent into `NaN`.\n *\n * Use forward mode when there are few inputs (or one direction) and many outputs; use the tape\n * when there are many inputs and one output.\n *\n * @example\n * ```ts\n * import { Forward } from 'adjointly';\n * const F = new Forward();\n * const x = F.input(2, 1); // seed: direction (1, 0)\n * const y = F.input(3, 0);\n * const f = F.mul(x, F.sin(y)); // x * sin(y)\n * F.value(f); //=> 2 * sin(3)\n * F.tangent(f); //=> sin(3)\n * ```\n */\nexport class Forward implements Arithmetic<Dual> {\n private val: Float64Array;\n private tan: Float64Array;\n private n = 0;\n\n /** An empty forward-mode context; see {@link ForwardOptions}. */\n constructor(options: ForwardOptions = {}) {\n const cap = initialCapacity(options.capacity, 'Forward');\n this.val = new Float64Array(cap);\n this.tan = new Float64Array(cap);\n if (options.debug === true) installChecks(this, 'Forward', 0);\n }\n\n /** The number of values created so far. */\n get size(): number {\n return this.n;\n }\n\n /** Forget every value, keeping the storage. Every `Dual` previously issued becomes invalid. */\n clear(): void {\n this.n = 0;\n }\n\n /** An input with value `x` and tangent (seed) `tangent`: its component of the direction. */\n input(x: number, tangent: number): Dual {\n return this.push(x, tangent);\n }\n\n /**\n * Lift a number into `T` as a constant: its derivative with respect to every input is zero.\n * On a `Tape` it still appends a node (constants are leaves of the tape).\n */\n constant(x: number): Dual {\n return this.push(x, 0);\n }\n\n /**\n * The current numeric value of `a`. Use it to branch (`if (A.value(x) > 0)`), to log, or to\n * return a result. The number it returns carries no derivative information: anything computed\n * from it with ordinary arithmetic is a constant as far as differentiation is concerned.\n */\n value(a: Dual): number {\n return this.val[a]!;\n }\n\n /** The tangent of `a`: its derivative along the direction the inputs were seeded with. */\n tangent(a: Dual): number {\n return this.tan[a]!;\n }\n\n private push(y: number, t: number): Dual {\n const i = this.n;\n if (i === this.val.length) this.grow();\n this.val[i] = y;\n this.tan[i] = t;\n this.n = i + 1;\n return i as Dual;\n }\n\n private grow(): void {\n const cap = this.val.length * 2;\n const val = new Float64Array(cap);\n val.set(this.val);\n this.val = val;\n const tan = new Float64Array(cap);\n tan.set(this.tan);\n this.tan = tan;\n }\n\n // One method per primitive, each calling its own table entry directly, so every call site is\n // monomorphic and inlined; see the same note in tape.ts. neg, add and sub multiply directly:\n // their partials are always 1 or -1, never 0, so `times` would change nothing.\n\n /** `-a`. */\n neg(a: Dual): Dual {\n const x = this.val[a]!;\n const ta = this.tan[a]!;\n const y = P.neg.value(x);\n return this.push(y, P.neg.deriv(x, y) * ta);\n }\n /** `a + b`. */\n add(a: Dual, b: Dual): Dual {\n const x = this.val[a]!;\n const z = this.val[b]!;\n const ta = this.tan[a]!;\n const tb = this.tan[b]!;\n const y = P.add.value(x, z);\n return this.push(y, P.add.da(x, z, y) * ta + P.add.db(x, z, y) * tb);\n }\n /** `a - b`. */\n sub(a: Dual, b: Dual): Dual {\n const x = this.val[a]!;\n const z = this.val[b]!;\n const ta = this.tan[a]!;\n const tb = this.tan[b]!;\n const y = P.sub.value(x, z);\n return this.push(y, P.sub.da(x, z, y) * ta + P.sub.db(x, z, y) * tb);\n }\n /** `a * b`. */\n mul(a: Dual, b: Dual): Dual {\n const x = this.val[a]!;\n const z = this.val[b]!;\n const ta = this.tan[a]!;\n const tb = this.tan[b]!;\n const y = P.mul.value(x, z);\n return this.push(y, times(P.mul.da(x, z, y), ta) + times(P.mul.db(x, z, y), tb));\n }\n /** `a / b`. */\n div(a: Dual, b: Dual): Dual {\n const x = this.val[a]!;\n const z = this.val[b]!;\n const ta = this.tan[a]!;\n const tb = this.tan[b]!;\n const y = P.div.value(x, z);\n return this.push(y, times(P.div.da(x, z, y), ta) + times(P.div.db(x, z, y), tb));\n }\n /** `1 / a`. */\n recip(a: Dual): Dual {\n const x = this.val[a]!;\n const ta = this.tan[a]!;\n const y = P.recip.value(x);\n return this.push(y, times(P.recip.deriv(x, y), ta));\n }\n /** `a * a`, one node instead of a `mul` of `a` with itself. */\n square(a: Dual): Dual {\n const x = this.val[a]!;\n const ta = this.tan[a]!;\n const y = P.square.value(x);\n return this.push(y, times(P.square.deriv(x, y), ta));\n }\n /** `sqrt(a)`; its derivative at `0` is `+Infinity`. */\n sqrt(a: Dual): Dual {\n const x = this.val[a]!;\n const ta = this.tan[a]!;\n const y = P.sqrt.value(x);\n return this.push(y, times(P.sqrt.deriv(x, y), ta));\n }\n /** `e^a`. */\n exp(a: Dual): Dual {\n const x = this.val[a]!;\n const ta = this.tan[a]!;\n const y = P.exp.value(x);\n return this.push(y, times(P.exp.deriv(x, y), ta));\n }\n /** `e^a - 1`, accurate for small `a`. */\n expm1(a: Dual): Dual {\n const x = this.val[a]!;\n const ta = this.tan[a]!;\n const y = P.expm1.value(x);\n return this.push(y, times(P.expm1.deriv(x, y), ta));\n }\n /** Natural logarithm. */\n log(a: Dual): Dual {\n const x = this.val[a]!;\n const ta = this.tan[a]!;\n const y = P.log.value(x);\n return this.push(y, times(P.log.deriv(x, y), ta));\n }\n /** `log(1 + a)`, accurate for small `a`. */\n log1p(a: Dual): Dual {\n const x = this.val[a]!;\n const ta = this.tan[a]!;\n const y = P.log1p.value(x);\n return this.push(y, times(P.log1p.deriv(x, y), ta));\n }\n /**\n * `a ** b`, differentiable in both arguments. The derivative with respect to `b` is `0` wherever\n * `a <= 0`; the derivative with respect to `a` is `0` wherever `b === 0`. For a fixed numeric\n * exponent use {@link Arithmetic.powc}; for `b = 2` prefer {@link Arithmetic.square}, for\n * `b = 0.5` prefer {@link Arithmetic.sqrt}.\n */\n pow(a: Dual, b: Dual): Dual {\n const x = this.val[a]!;\n const z = this.val[b]!;\n const ta = this.tan[a]!;\n const tb = this.tan[b]!;\n const y = P.pow.value(x, z);\n return this.push(y, times(P.pow.da(x, z, y), ta) + times(P.pow.db(x, z, y), tb));\n }\n /** `|a|`; its derivative at `0` is `0`. */\n abs(a: Dual): Dual {\n const x = this.val[a]!;\n const ta = this.tan[a]!;\n const y = P.abs.value(x);\n return this.push(y, times(P.abs.deriv(x, y), ta));\n }\n /** The smaller of `a` and `b`; on a tie the derivative flows to `a`. */\n min(a: Dual, b: Dual): Dual {\n const x = this.val[a]!;\n const z = this.val[b]!;\n const ta = this.tan[a]!;\n const tb = this.tan[b]!;\n const y = P.min.value(x, z);\n return this.push(y, times(P.min.da(x, z, y), ta) + times(P.min.db(x, z, y), tb));\n }\n /** The larger of `a` and `b`; on a tie the derivative flows to `a`. */\n max(a: Dual, b: Dual): Dual {\n const x = this.val[a]!;\n const z = this.val[b]!;\n const ta = this.tan[a]!;\n const tb = this.tan[b]!;\n const y = P.max.value(x, z);\n return this.push(y, times(P.max.da(x, z, y), ta) + times(P.max.db(x, z, y), tb));\n }\n /** Sine. */\n sin(a: Dual): Dual {\n const x = this.val[a]!;\n const ta = this.tan[a]!;\n const y = P.sin.value(x);\n return this.push(y, times(P.sin.deriv(x, y), ta));\n }\n /** Cosine. */\n cos(a: Dual): Dual {\n const x = this.val[a]!;\n const ta = this.tan[a]!;\n const y = P.cos.value(x);\n return this.push(y, times(P.cos.deriv(x, y), ta));\n }\n /** Hyperbolic tangent. */\n tanh(a: Dual): Dual {\n const x = this.val[a]!;\n const ta = this.tan[a]!;\n const y = P.tanh.value(x);\n return this.push(y, times(P.tanh.deriv(x, y), ta));\n }\n /** Logistic sigmoid `1 / (1 + e^-a)`, stable for any `a`. */\n sigmoid(a: Dual): Dual {\n const x = this.val[a]!;\n const ta = this.tan[a]!;\n const y = P.sigmoid.value(x);\n return this.push(y, times(P.sigmoid.deriv(x, y), ta));\n }\n /** Softplus `log(1 + e^a)`, stable for any `a`; its derivative is `sigmoid(a)`. */\n softplus(a: Dual): Dual {\n const x = this.val[a]!;\n const ta = this.tan[a]!;\n const y = P.softplus.value(x);\n return this.push(y, times(P.softplus.deriv(x, y), ta));\n }\n\n // The constant-operand operations: the binary primitive with one operand a plain number.\n\n /**\n * `a + c` for a plain number `c`: `add(a, constant(c))` without the constant's tape node. Same\n * value and derivative, bit for bit.\n */\n addc(a: Dual, c: number): Dual {\n const x = this.val[a]!;\n const y = P.add.value(x, c);\n return this.push(y, times(P.add.da(x, c, y), this.tan[a]!));\n }\n\n /** `a - c` for a plain number `c`; `sub(a, constant(c))` in one node. */\n subc(a: Dual, c: number): Dual {\n const x = this.val[a]!;\n const y = P.sub.value(x, c);\n return this.push(y, times(P.sub.da(x, c, y), this.tan[a]!));\n }\n\n /** `c - a` for a plain number `c`; `sub(constant(c), a)` in one node. */\n csub(c: number, a: Dual): Dual {\n const x = this.val[a]!;\n const y = P.sub.value(c, x);\n return this.push(y, times(P.sub.db(c, x, y), this.tan[a]!));\n }\n\n /** `a * c` for a plain number `c`; `mul(a, constant(c))` in one node. */\n mulc(a: Dual, c: number): Dual {\n const x = this.val[a]!;\n const y = P.mul.value(x, c);\n return this.push(y, times(P.mul.da(x, c, y), this.tan[a]!));\n }\n\n /** `a / c` for a plain number `c`; `div(a, constant(c))` in one node. */\n divc(a: Dual, c: number): Dual {\n const x = this.val[a]!;\n const y = P.div.value(x, c);\n return this.push(y, times(P.div.da(x, c, y), this.tan[a]!));\n }\n\n /** `c / a` for a plain number `c`; `div(constant(c), a)` in one node. */\n cdiv(c: number, a: Dual): Dual {\n const x = this.val[a]!;\n const y = P.div.value(c, x);\n return this.push(y, times(P.div.db(c, x, y), this.tan[a]!));\n }\n\n /**\n * `a ** c` for a plain number `c`; `pow(a, constant(c))` in one node, with the same conventions\n * (the derivative is `0` when `c === 0`).\n */\n powc(a: Dual, c: number): Dual {\n const x = this.val[a]!;\n const y = P.pow.value(x, c);\n return this.push(y, times(P.pow.da(x, c, y), this.tan[a]!));\n }\n}\n","/*\n * Composite operations built from primitives, generic over any {@link Arithmetic}: they record\n * ordinary primitive nodes on a tape and need no derivative rules of their own.\n */\n\nimport type { Arithmetic } from './arithmetic.ts';\n\n/**\n * The sum of `xs`, added left to right; `A.constant(0)` for an empty list.\n *\n * @example\n * ```ts\n * sum(plain, [1, 2, 3]); //=> 6\n * ```\n */\nexport function sum<T>(A: Arithmetic<T>, xs: readonly T[]): T {\n if (xs.length === 0) return A.constant(0);\n let s: T = xs[0]!;\n for (let i = 1; i < xs.length; i++) s = A.add(s, xs[i]!);\n return s;\n}\n\n/**\n * The dot product `sum_i xs[i] * ys[i]`; `A.constant(0)` for empty lists. Throws if the lengths\n * differ.\n *\n * @example\n * ```ts\n * dot(plain, [1, 2], [3, 4]); //=> 11\n * ```\n */\nexport function dot<T>(A: Arithmetic<T>, xs: readonly T[], ys: readonly T[]): T {\n if (xs.length !== ys.length) {\n throw new RangeError(`dot: lengths differ (${xs.length} and ${ys.length})`);\n }\n if (xs.length === 0) return A.constant(0);\n let s = A.mul(xs[0]!, ys[0]!);\n for (let i = 1; i < xs.length; i++) s = A.add(s, A.mul(xs[i]!, ys[i]!));\n return s;\n}\n\n/** The largest of the values of `xs` as a plain number; `NaN` if any of them is `NaN`. */\nfunction largest<T>(A: Arithmetic<T>, xs: readonly T[]): number {\n let m = -Infinity;\n for (const x of xs) m = Math.max(m, A.value(x));\n return m;\n}\n\n/**\n * `log(sum_i exp(xs[i]))`, computed stably as `m + log(sum_i exp(xs[i] - m))` where `m` is the\n * largest value. `m` enters as a constant: the identity holds for any `m`, so its derivative\n * through `m` is zero and leaving it out keeps the gradient exact (it is `softmax(xs)`).\n * Throws on an empty list.\n *\n * Infinite inputs give the limit of the finite case. If the largest input is `+Infinity`, or every\n * input is `-Infinity`, the value is that infinity and the gradient is shared equally by the inputs\n * equal to it, `0` elsewhere; a `-Infinity` among finite inputs has weight `0`. A `NaN` input\n * makes everything `NaN`.\n *\n * @example\n * ```ts\n * // The naive log(exp(1000) + exp(1000)) overflows to Infinity.\n * logsumexp(plain, [1000, 1000]); //=> 1000 + log(2)\n * gradient((A, xs) => logsumexp(A, xs), [Infinity, 0]).gradient; //=> [1, 0]\n * ```\n */\nexport function logsumexp<T>(A: Arithmetic<T>, xs: readonly T[]): T {\n if (xs.length === 0) throw new RangeError('logsumexp: empty list');\n const m = largest(A, xs);\n if (m === Infinity || m === -Infinity) {\n // mean(top) + log(k) is that infinity, with gradient 1/k on each of the k extreme inputs.\n const top = xs.filter((x) => A.value(x) === m);\n return A.add(A.mul(sum(A, top), A.constant(1 / top.length)), A.constant(Math.log(top.length)));\n }\n const c = A.constant(m);\n let s: T = A.exp(A.sub(xs[0]!, c));\n for (let i = 1; i < xs.length; i++) s = A.add(s, A.exp(A.sub(xs[i]!, c)));\n return A.add(c, A.log(s));\n}\n\n/**\n * The softmax `exp(xs[i]) / sum_j exp(xs[j])`, computed stably by first subtracting the largest\n * value as a constant (softmax is invariant to the shift, so the gradient stays exact). Throws on\n * an empty list.\n *\n * Infinite inputs give the limit of the finite case. If the largest input is `+Infinity`, or every\n * input is `-Infinity`, the weights are `1/k` on the `k` inputs equal to it and `0` elsewhere,\n * returned as constants: the Jacobian there is `0`, which is also its limit. A `-Infinity` among\n * finite inputs gets weight `0`. A `NaN` input makes every weight `NaN`.\n *\n * @example\n * ```ts\n * softmax(plain, [0, Math.log(3)]); //=> [0.25, 0.75]\n * softmax(plain, [Infinity, 0, Infinity]); //=> [0.5, 0, 0.5]\n * ```\n */\nexport function softmax<T>(A: Arithmetic<T>, xs: readonly T[]): T[] {\n if (xs.length === 0) throw new RangeError('softmax: empty list');\n const m = largest(A, xs);\n if (m === Infinity || m === -Infinity) {\n const k = xs.filter((x) => A.value(x) === m).length;\n return xs.map((x) => A.constant(A.value(x) === m ? 1 / k : 0));\n }\n const c = A.constant(m);\n const es = xs.map((x) => A.exp(A.sub(x, c)));\n const inv = A.recip(sum(A, es));\n return es.map((e) => A.mul(e, inv));\n}\n","/*\n * One-call entry points for a {@link ScalarFunction}: value, gradient, directional derivative.\n */\n\nimport { type Inputs, plain, type ScalarFunction } from './arithmetic.ts';\nimport { type Dual, Forward } from './forward.ts';\nimport { Tape, type Var } from './tape.ts';\n\n/** The result of {@link gradient}. */\nexport interface GradientResult {\n /** `f(at)`. */\n readonly value: number;\n /** `df/d(at[i])` for each `i`. */\n readonly gradient: number[];\n}\n\n/** The result of {@link jvp}. */\nexport interface JvpResult {\n /** `f(at)`. */\n readonly value: number;\n /** The directional derivative `sum_i df/d(at[i]) * direction[i]`. */\n readonly derivative: number;\n}\n\n/**\n * Evaluate `f` at `at` on plain numbers, recording nothing. The value is bit-for-bit the one\n * {@link gradient} and {@link jvp} report.\n *\n * @example\n * ```ts\n * evaluate((A, [x]) => A.exp(x), [0]); //=> 1\n * ```\n */\nexport function evaluate<const X extends readonly number[]>(f: ScalarFunction<X>, at: X): number {\n return f(plain, at as Inputs<number, X>);\n}\n\n/**\n * The value and gradient of `f` at `at`, by reverse mode: one forward pass recording a tape and\n * one backward sweep, whatever the number of inputs.\n *\n * Pass `tape` to reuse its storage across calls in a loop; it is cleared first and left holding\n * this evaluation.\n *\n * @example\n * ```ts\n * gradient((A, [x, y]) => A.mul(x, y), [3, 4]); //=> { value: 12, gradient: [4, 3] }\n * ```\n */\nexport function gradient<const X extends readonly number[]>(\n f: ScalarFunction<X>,\n at: X,\n tape: Tape = new Tape(),\n): GradientResult {\n tape.clear();\n const xs: Var[] = tape.inputs(at);\n const y = f(tape, xs as Inputs<Var, X>);\n tape.backward(y);\n return { value: tape.value(y), gradient: xs.map((x) => tape.adjoint(x)) };\n}\n\n/**\n * The value of `f` at `at` and its derivative along `direction` (a Jacobian-vector product), by\n * forward mode: one pass, no tape. Throws if `direction` and `at` differ in length.\n *\n * @example\n * ```ts\n * jvp((A, [x, y]) => A.mul(x, y), [3, 4], [1, 0]); //=> { value: 12, derivative: 4 }\n * ```\n */\nexport function jvp<const X extends readonly number[]>(\n f: ScalarFunction<X>,\n at: X,\n direction: readonly number[],\n forward: Forward = new Forward(),\n): JvpResult {\n if (direction.length !== at.length) {\n throw new RangeError(`jvp: ${at.length} inputs but a direction of length ${direction.length}`);\n }\n forward.clear();\n const xs = at.map((x, i) => forward.input(x, direction[i]!));\n const y = f(forward, xs as Inputs<Dual, X>);\n return { value: forward.value(y), derivative: forward.tangent(y) };\n}\n","/*\n * Numerical checking: central finite differences, and a gradient check comparing them with\n * reverse mode.\n */\n\nimport { type Inputs, plain, type ScalarFunction } from './arithmetic.ts';\nimport { gradient } from './convenience.ts';\n\n/**\n * Default relative step for {@link finiteDifference}: about the cube root of machine epsilon,\n * which balances the central difference's `O(h^2)` truncation error against rounding error.\n */\nexport const DEFAULT_STEP = 6e-6;\n\n/**\n * The gradient of `f` at `at` by central differences on plain numbers:\n * `(f(x + h_i e_i) - f(x - h_i e_i)) / (2 h_i)` with `h_i = h * max(1, |at[i]|)`.\n * Costs two evaluations per input; expect agreement with the true gradient to roughly 1e-8\n * relative for a smooth, well-scaled function, and nothing useful across a kink or a branch.\n *\n * @param h - relative step, default {@link DEFAULT_STEP}.\n *\n * @example\n * ```ts\n * finiteDifference((A, [x]) => A.square(x), [3]); //=> ~[6]\n * ```\n */\nexport function finiteDifference<const X extends readonly number[]>(\n f: ScalarFunction<X>,\n at: X,\n h: number = DEFAULT_STEP,\n): number[] {\n return centralDifferences(f, at, h).map((d) => d.slope);\n}\n\n/** Units in the last place of error allowed for in each evaluation of `f`. */\nconst ROUNDING_ULPS = 4;\n\n/**\n * Each component's central difference, and an estimate of the error that rounding in the two\n * evaluations of `f` puts into it: `ROUNDING_ULPS * eps * (|f(x + h)| + |f(x - h)|) / (2 h)`.\n */\nfunction centralDifferences<X extends readonly number[]>(\n f: ScalarFunction<X>,\n at: X,\n h: number,\n): { slope: number; roundoff: number }[] {\n const x: number[] = at.slice(); // perturbed one component at a time\n const point = x as unknown as Inputs<number, X>; // the same array, seen as f's inputs\n return at.map((xi, i) => {\n const step = h * Math.max(1, Math.abs(xi));\n x[i] = xi + step;\n const up = f(plain, point);\n x[i] = xi - step;\n const down = f(plain, point);\n x[i] = xi;\n // Divide by the step actually taken, which rounding may have changed.\n const span = xi + step - (xi - step);\n return {\n slope: (up - down) / span,\n roundoff: (ROUNDING_ULPS * Number.EPSILON * (Math.abs(up) + Math.abs(down))) / span,\n };\n });\n}\n\n/** Options for {@link checkGradient}. */\nexport interface CheckGradientOptions {\n /** Relative finite-difference step; default {@link DEFAULT_STEP}. */\n readonly h?: number;\n /** Relative tolerance; default `1e-5`. */\n readonly rtol?: number;\n /** Absolute tolerance; default `1e-8`. */\n readonly atol?: number;\n}\n\n/** The result of {@link checkGradient}. */\nexport interface GradientCheck {\n /** Whether every component is within tolerance (and none is `NaN`). */\n readonly ok: boolean;\n /** `f(at)`. */\n readonly value: number;\n /** The gradient by reverse mode. */\n readonly analytic: number[];\n /** The gradient by central differences. */\n readonly numeric: number[];\n /**\n * The component furthest out of tolerance relative to its allowance (`-1` index when `at` is\n * empty). `error` is `|analytic - numeric|`; `roundoff` the estimated rounding error of the\n * finite difference; `allowed` is `atol + rtol * max(|analytic|, |numeric|) + roundoff`.\n */\n readonly worst: {\n readonly index: number;\n readonly analytic: number;\n readonly numeric: number;\n readonly error: number;\n readonly allowed: number;\n readonly roundoff: number;\n };\n}\n\n/**\n * Compare `f`'s reverse-mode gradient at `at` with central finite differences. Component `i`\n * passes when `|analytic - numeric| <= atol + rtol * max(|analytic|, |numeric|) + roundoff`.\n *\n * `roundoff` allows for the rounding error of the finite difference itself, which grows with\n * `|f|` and shrinks with the step: `4 * eps * (|f(x + h)| + |f(x - h)|) / (2 h)`, four units in the\n * last place for each evaluation. Without it an exact gradient of a function whose value is large\n * next to its slope (`1e5 + 1e-3 * x`) would fail.\n *\n * Use it in your own tests to verify a function written against `Arithmetic`, typically at a few\n * random points away from kinks and branch boundaries, where finite differences are meaningless.\n *\n * @example\n * ```ts\n * const loss: ScalarFunction = (A, xs) => logsumexp(A, xs.map((x) => A.square(x)));\n * const r = checkGradient(loss, [0.3, -1.2, 2]);\n * if (!r.ok) {\n * const { index, analytic, numeric } = r.worst;\n * throw new Error(`component ${String(index)}: ${String(analytic)} vs ${String(numeric)}`);\n * }\n * r.ok; //=> true\n * ```\n */\nexport function checkGradient<const X extends readonly number[]>(\n f: ScalarFunction<X>,\n at: X,\n options: CheckGradientOptions = {},\n): GradientCheck {\n const { h = DEFAULT_STEP, rtol = 1e-5, atol = 1e-8 } = options;\n const { value, gradient: analytic } = gradient(f, at);\n const differences = centralDifferences(f, at, h);\n const numeric = differences.map((d) => d.slope);\n let ok = true;\n let worst = { index: -1, analytic: NaN, numeric: NaN, error: 0, allowed: 0, roundoff: 0 };\n let worstRatio = -1;\n for (let i = 0; i < analytic.length; i++) {\n const a = analytic[i]!;\n const n = numeric[i]!;\n const roundoff = differences[i]!.roundoff;\n const error = Math.abs(a - n);\n const allowed = atol + rtol * Math.max(Math.abs(a), Math.abs(n)) + roundoff;\n // A NaN error ranks as infinitely bad (and fails, NaN comparing false); an exact match ranks 0\n // even against a zero allowance, where 0 / 0 would be NaN.\n const ratio = Number.isNaN(error) ? Infinity : error === 0 ? 0 : error / allowed;\n if (!(error <= allowed)) ok = false;\n if (ratio > worstRatio) {\n worstRatio = ratio;\n worst = { index: i, analytic: a, numeric: n, error, allowed, roundoff };\n }\n }\n return { ok, value, analytic, numeric, worst };\n}\n"],"mappings":"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"}
package/docs/README.md ADDED
@@ -0,0 +1,20 @@
1
+ # adjointly documentation
2
+
3
+ Focused pages, each standing on its own. They ship in the npm package, so an agent or a person
4
+ working offline has them beside the code. Every `ts` example on these pages is type-checked and run
5
+ by the test suite, and a line ending in `//=> value` is asserted.
6
+
7
+ | Page | Read it for |
8
+ | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
9
+ | [concepts.md](concepts.md) | How reverse and forward mode work here, what the tape stores, and when to use which |
10
+ | [api.md](api.md) | Every export, grouped by topic, with its contract |
11
+ | [recipes.md](recipes.md) | Worked patterns: inner loops, shared prefixes, several outputs, Jacobians, parameters with constraints, testing your gradient |
12
+ | [pitfalls.md](pitfalls.md) | The mistakes that silently give a wrong derivative, and how to avoid them |
13
+
14
+ A worked, end-to-end example lives in
15
+ [examples/production-line/](https://github.com/feldgendler/adjointly/tree/main/examples/production-line) (in the repository, not the npm package): maintenance scheduling on a
16
+ simulated production line, using the gradient to rank discrete moves and forward mode for a
17
+ parameter sensitivity.
18
+
19
+ The package's [README](../README.md) has the overview, and [llms.txt](../llms.txt) a compact
20
+ summary for language models.
package/docs/api.md ADDED
@@ -0,0 +1,165 @@
1
+ # API
2
+
3
+ Everything is exported from the package root, `adjointly`. The TSDoc on each export is the
4
+ authoritative contract; this page groups them by topic. The committed API report,
5
+ `etc/adjointly.api.md` in the repository, lists every signature.
6
+
7
+ ## Writing a differentiable function
8
+
9
+ ### `interface Arithmetic<T>`
10
+
11
+ | member | contract |
12
+ | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------ |
13
+ | `constant(x: number): T` | a number as a constant: zero derivative. Appends a node on a tape |
14
+ | `value(a: T): number` | the current value; a plain number with no derivative attached |
15
+ | `neg, recip, square, sqrt, exp, expm1, log, log1p, abs, sin, cos, tanh, sigmoid, softplus` `(a: T): T` | one-argument primitives |
16
+ | `addc, subc, mulc, divc, powc` `(a: T, c: number): T`; `csub, cdiv` `(c: number, a: T): T` | the binary primitive with a plain-number operand, in one tape node |
17
+ | `add, sub, mul, div, pow, min, max` `(a: T, b: T): T` | two-argument primitives |
18
+
19
+ Every operation on a `T` must go through these methods. The ones with a plain-number operand are
20
+ the binary primitive with that operand a constant, in one tape node instead of two, and with the
21
+ same value and derivative, bit for bit:
22
+
23
+ ```ts
24
+ import { plain, Tape } from 'adjointly';
25
+
26
+ [plain.addc(5, 2), plain.subc(5, 2), plain.csub(2, 5), plain.mulc(5, 2)]; //=> [7, 3, -3, 10]
27
+ [plain.divc(5, 2), plain.cdiv(2, 5), plain.powc(5, 2)]; //=> [2.5, 0.4, 25]
28
+
29
+ const t = new Tape();
30
+ const x = t.input(3);
31
+ const y = t.mulc(x, 2); // one node; t.mul(x, t.constant(2)) would record two
32
+ t.size; //=> 2
33
+ t.backward(y);
34
+ t.adjoint(x); //=> 2
35
+ ```
36
+
37
+ ### `type ScalarFunction<X = readonly number[]> = <T>(A: Arithmetic<T>, xs: Inputs<T, X>) => T`
38
+
39
+ The shape the conveniences accept: a function of a list of inputs, generic in `T`. `X` is the type
40
+ of the point it is evaluated at, and `Inputs<T, X>` maps it to one `T` per number: called with a
41
+ literal point such as `[3, 4]`, the inputs are the tuple `readonly [T, T]` and destructure without
42
+ `!`; with a `number[]` they are `readonly T[]`. `Tape.inputs([1, 2])` returns a tuple the same way.
43
+
44
+ ### `const plain: Arithmetic<number>`
45
+
46
+ Values only, nothing recorded.
47
+
48
+ ```ts
49
+ import { plain } from 'adjointly';
50
+
51
+ plain.softplus(plain.constant(-800)); //=> 0
52
+ plain.softplus(800); //=> 800
53
+ ```
54
+
55
+ ### Composites: `sum`, `dot`, `logsumexp`, `softmax`
56
+
57
+ Generic over any `Arithmetic`, built from primitives, so they need no derivative rules.
58
+
59
+ | function | contract |
60
+ | --------------------- | ------------------------------------------------------------------------------ |
61
+ | `sum(A, xs): T` | left-to-right sum; `constant(0)` for an empty list |
62
+ | `dot(A, xs, ys): T` | `sum_i xs[i] * ys[i]`; throws `RangeError` on different lengths |
63
+ | `logsumexp(A, xs): T` | `log(sum_i exp(xs[i]))`, shifted by the largest value; throws on an empty list |
64
+ | `softmax(A, xs): T[]` | `exp(xs[i]) / sum_j exp(xs[j])`, shifted likewise; throws on an empty list |
65
+
66
+ With infinite inputs both return the limit of the finite case: if the largest input is
67
+ `+Infinity`, or every input is `-Infinity`, the weight (the gradient of `logsumexp`, the value of
68
+ `softmax`) is shared equally by the inputs equal to it and is `0` elsewhere; `softmax` then
69
+ returns constants, so its Jacobian is `0`. `NaN` in gives `NaN` out.
70
+
71
+ ```ts
72
+ import { gradient, logsumexp, softmax, plain } from 'adjointly';
73
+
74
+ logsumexp(plain, [1000, 1000]); //=> 1000 + log(2)
75
+ softmax(plain, [0, Math.log(3)]); //=> [0.25, 0.75]
76
+ // The gradient of logsumexp is the softmax.
77
+ gradient((A, xs) => logsumexp(A, xs), [0, Math.log(3)]).gradient; //=> [0.25, 0.75]
78
+ ```
79
+
80
+ ## Conveniences
81
+
82
+ | function | returns |
83
+ | --------------------------------- | -------------------------------------------------------------------------------------------------------- |
84
+ | `evaluate(f, at)` | `f(at)` on plain numbers |
85
+ | `gradient(f, at, tape?)` | `{ value, gradient: number[] }` by reverse mode; reuses `tape` if given |
86
+ | `jvp(f, at, direction, forward?)` | `{ value, derivative }`, the derivative along `direction`, by forward mode; throws if the lengths differ |
87
+
88
+ ```ts
89
+ import { evaluate, gradient, jvp, type ScalarFunction } from 'adjointly';
90
+
91
+ const f: ScalarFunction<[number, number]> = (A, [x, y]) => A.div(x, y);
92
+ evaluate(f, [3, 4]); //=> 0.75
93
+ gradient(f, [3, 4]); //=> { value: 0.75, gradient: [0.25, -3 / 16] }
94
+ jvp(f, [3, 4], [1, 1]); //=> { value: 0.75, derivative: 0.25 - 3 / 16 }
95
+ ```
96
+
97
+ ## Reverse mode: `class Tape implements Arithmetic<Var>`
98
+
99
+ | member | contract |
100
+ | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
101
+ | `new Tape({ capacity?, debug? })` | an empty tape; capacity in nodes, default 1024, doubles when full; `debug: true` checks every operand and throws at a plain number, a stale variable, another tape's, or a variable where a number belongs |
102
+ | `size`, `capacity` | nodes recorded; nodes it can hold before growing |
103
+ | `input(x)`, `inputs(xs)` | leaves to differentiate with respect to |
104
+ | `constant(x)` | a leaf too; the name only says you do not care for its adjoint |
105
+ | `value(v)` | the value of a node |
106
+ | `backward(output, seed = 1)` | one sweep: afterwards `adjoint(v)` is `seed * d(output)/d(v)`. Throws if `output` is not on the tape |
107
+ | `vjp(outputs, weights)` | one sweep seeded with every `weights[k]` at `outputs[k]`. Throws on mismatched lengths or a foreign output |
108
+ | `adjoint(v)` | the adjoint from the last sweep; zero for nodes the output does not depend on |
109
+ | `adjoints(vars, out?)` | the adjoints of `vars` into a `Float64Array` |
110
+ | `gradient(output, inputs, out?)` | `backward(output)` then `adjoints(inputs, out)` |
111
+ | `clear()` | forget every node, keep the storage |
112
+ | `mark()`, `rewind(mark)` | truncate back to a checkpoint, keeping the nodes before it. `rewind` throws if the mark is beyond the end |
113
+
114
+ Sweeps leave the tape unchanged: sweep it as often as you like, from different outputs.
115
+
116
+ ### `type Var`, `type Mark`
117
+
118
+ `Var` is `number & { brand }`: an index into its tape. A raw number is not assignable to it, and
119
+ neither is a `Dual`. `Mark` is a branded tape length.
120
+
121
+ ## Forward mode: `class Forward implements Arithmetic<Dual>`
122
+
123
+ | member | contract |
124
+ | ------------------------------------ | -------------------------------------------------------- |
125
+ | `new Forward({ capacity?, debug? })` | an empty context; `debug` as for `Tape` |
126
+ | `input(x, tangent)` | an input with its component of the direction |
127
+ | `constant(x)` | tangent 0 |
128
+ | `value(d)`, `tangent(d)` | the value, and the derivative along the seeded direction |
129
+ | `size`, `clear()` | values created; forget them |
130
+
131
+ ## Checking: `finiteDifference`, `checkGradient`, `DEFAULT_STEP`
132
+
133
+ | function | contract |
134
+ | ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
135
+ | `finiteDifference(f, at, h = DEFAULT_STEP)` | central differences with step `h * max(1, abs(at[i]))`; two evaluations per input |
136
+ | `checkGradient(f, at, { h, rtol = 1e-5, atol = 1e-8 })` | reverse mode against central differences; component `i` passes when `abs(analytic - numeric) <= atol + rtol * max(abs(analytic), abs(numeric)) + roundoff`, where `roundoff` is `4 * eps * (abs(f(x + h)) + abs(f(x - h))) / (2 h)`, the finite difference's own rounding error; `NaN` fails |
137
+
138
+ `checkGradient` returns `{ ok, value, analytic, numeric, worst }`, where `worst` is the component
139
+ furthest outside its allowance, with `index`, `analytic`, `numeric`, `error`, `roundoff` and
140
+ `allowed`.
141
+
142
+ ```ts
143
+ import { checkGradient, type ScalarFunction } from 'adjointly';
144
+
145
+ // A gradient bug: reading a value out and back in as a constant.
146
+ const buggy: ScalarFunction<[number]> = (A, [x]) => A.mul(x, A.constant(A.value(x)));
147
+ const r = checkGradient(buggy, [3]);
148
+ r.ok; //=> false
149
+ r.worst.analytic; //=> 3
150
+ r.worst.numeric; //=> ~6
151
+ ```
152
+
153
+ ## The primitive table: `primitives`, `unaryNames`, `binaryNames`
154
+
155
+ `primitives[name]` is a frozen `UnaryPrimitive` (`value(x)`, `deriv(x, y)`) or `BinaryPrimitive`
156
+ (`value(a, b)`, `da(a, b, y)`, `db(a, b, y)`), where `y` is the operation's own result. It is the
157
+ single source of every value and partial derivative in the library.
158
+
159
+ ```ts
160
+ import { binaryNames, primitives, unaryNames } from 'adjointly';
161
+
162
+ primitives.pow.db(0, 2, 0); //=> 0
163
+ primitives.abs.deriv(0, 0); //=> 0
164
+ unaryNames.length + binaryNames.length; //=> 21
165
+ ```
@@ -0,0 +1,143 @@
1
+ # Concepts
2
+
3
+ ## Functions written once
4
+
5
+ A function to differentiate is written against `Arithmetic<T>`, generic in the value type `T`:
6
+
7
+ ```ts
8
+ import { type Arithmetic, Forward, plain, Tape } from 'adjointly';
9
+
10
+ function f<T>(A: Arithmetic<T>, x: T, y: T): T {
11
+ return A.add(A.mul(x, y), A.sin(x)); // x*y + sin(x)
12
+ }
13
+
14
+ f(plain, 2, 3); //=> 6 + sin(2)
15
+
16
+ const t = new Tape();
17
+ const x = t.input(2);
18
+ const y = t.input(3);
19
+ t.backward(f(t, x, y));
20
+ t.adjoint(x); //=> 3 + cos(2)
21
+
22
+ const F = new Forward();
23
+ const out = f(F, F.input(2, 0), F.input(3, 1)); // direction: along y
24
+ F.tangent(out); //=> 2
25
+ ```
26
+
27
+ The three implementations compute every value with the same functions from the `primitives`
28
+ table, so `f(plain, ...)`, the tape's value and the forward value are bit-for-bit identical. The
29
+ tests hold this for random expressions over every primitive.
30
+
31
+ ## The tape
32
+
33
+ A `Tape` appends a node for every operation, `constant` and `input` included. Node `i` stores:
34
+
35
+ | array | holds |
36
+ | ---------- | ------------------------------------------------------------------------------------------- |
37
+ | `val` | the value |
38
+ | `pa`, `pb` | the indices of the one or two arguments; a missing one is slot 0, a sink that is never read |
39
+ | `da`, `db` | the local partial derivatives with respect to them, computed when the node was recorded |
40
+
41
+ A `Var` is just that index, branded so the type checker can tell it from a plain number. Because
42
+ the partials are computed during recording, the backward sweep does not need to know which
43
+ operation a node was:
44
+
45
+ ```text
46
+ adj[0..output] = 0; adj[output] = seed
47
+ for i from output down to 1:
48
+ if adj[i] == 0 or i is a leaf: continue
49
+ adj[pa[i]] += da[i] * adj[i]
50
+ adj[pb[i]] += db[i] * adj[i] # for a unary node pb[i] is the sink and db[i] is 0
51
+ ```
52
+
53
+ After the sweep `adj[v]` is d(output)/d(v) for every node `v` the output depends on. One sweep
54
+ gives the derivative with respect to every input at once, which is why reverse mode is the right
55
+ tool for a scalar loss of many parameters. The cost is a small constant times one evaluation,
56
+ independent of the number of inputs; the memory is six numbers per node, counting its adjoint
57
+ (40 bytes: four 8-byte floats and two 4-byte indices).
58
+
59
+ `vjp(outputs, weights)` seeds several outputs in one sweep, giving the gradient of
60
+ `sum_k weights[k] * outputs[k]`; `backward` is the one-output case.
61
+
62
+ ## Forward mode
63
+
64
+ `Forward` keeps a value and a tangent per slot. Inputs are seeded with the components of a
65
+ direction `v`; each operation computes its tangent as `da * tangent(a) + db * tangent(b)` from the
66
+ same partials. One evaluation gives `grad f . v` for every output. It needs no tape and no second
67
+ pass, so it is the better tool when there are few inputs, or only one direction of interest, and
68
+ possibly many outputs.
69
+
70
+ | inputs | outputs | what you want | cheapest |
71
+ | ------ | ------- | --------------------------- | ---------------------------- |
72
+ | many | one | the full gradient | one reverse sweep |
73
+ | few | many | the Jacobian | one forward pass per input |
74
+ | many | many | one Jacobian-vector product | one forward pass |
75
+ | many | many | one vector-Jacobian product | one reverse sweep with `vjp` |
76
+
77
+ ## Control flow
78
+
79
+ The tape records what ran. Branches on `A.value(x)`, loops whose trip count depends on values,
80
+ early returns, recursion: all are fine, and the derivative is that of the path taken at this point.
81
+ At a point exactly on a branch boundary the derivative is the taken branch's one-sided derivative.
82
+
83
+ ```ts
84
+ import { type Arithmetic, gradient } from 'adjointly';
85
+
86
+ // Newton's method for sqrt(a), iterated until it converges. Differentiating through the
87
+ // iterations gives the derivative of the converged result: 1 / (2 sqrt(a)).
88
+ function newtonSqrt<T>(A: Arithmetic<T>, [a]: readonly [T]): T {
89
+ let x = A.constant(1);
90
+ for (;;) {
91
+ const next = A.mulc(A.add(x, A.div(a, x)), 0.5);
92
+ if (Math.abs(A.value(next) - A.value(x)) < 1e-15) return next;
93
+ x = next;
94
+ }
95
+ }
96
+ gradient(newtonSqrt, [4]); //=> { value: 2, gradient: [0.25] }
97
+ ```
98
+
99
+ What the tape cannot see is a dependence that goes through `A.value`: a number read out and fed
100
+ back in with `A.constant` is a constant. That is the right behaviour for a branch condition or a
101
+ step size, and the wrong one anywhere a derivative should flow (see [pitfalls.md](pitfalls.md)).
102
+
103
+ ## The zero rule
104
+
105
+ Every term of the chain rule is a product of two factors: a local partial, and the adjoint flowing
106
+ back into the node (reverse mode) or the tangent flowing forward out of an argument (forward mode).
107
+ **If either factor is exactly 0, the term is 0**, even when the other factor is infinite or `NaN`.
108
+ Both modes apply the rule. Without it, `0 * Infinity` would turn an infinite partial on a path that
109
+ does not matter into `NaN`:
110
+
111
+ ```ts
112
+ import { type Arithmetic, gradient, jvp } from 'adjointly';
113
+
114
+ // sqrt at 0 has an infinite derivative, but here it is multiplied by 0.
115
+ function f<T>(A: Arithmetic<T>, [x, y]: readonly [T, T]): T {
116
+ return A.add(A.mulc(A.sqrt(x), 0), y);
117
+ }
118
+ gradient(f, [0, 1]).gradient; //=> [0, 1]
119
+ jvp(f, [0, 1], [1, 0]).derivative; //=> 0
120
+ jvp(f, [0, 1], [0, 1]).derivative; //=> 1
121
+
122
+ // The zero comes before the singular point: 0 * x is 0 for every x, so sqrt(0 * x) is constant.
123
+ function g<T>(A: Arithmetic<T>, [x]: readonly [T]): T {
124
+ return A.sqrt(A.mulc(x, 0));
125
+ }
126
+ gradient(g, [1]).gradient; //=> [0]
127
+ jvp(g, [1], [1]).derivative; //=> 0
128
+ ```
129
+
130
+ The two modes multiply the same partials, grouped differently: forward mode from the inputs
131
+ towards the output, reverse mode from the output back. The rule makes them agree, but two things
132
+ can still tell the groupings apart:
133
+
134
+ - **Cancellation.** The rule looks at factors, not sums. In `sqrt(x - x)` the tangent of `x - x`
135
+ is `1 - 1 = 0` before it meets the infinite partial of `sqrt` at 0, so forward mode gives 0;
136
+ reverse mode multiplies the infinite partial into each of the two terms first and adds
137
+ `∞ - ∞ = NaN`.
138
+ - **Underflow and overflow.** A product of tiny (or huge) finite partials can round to 0 (or
139
+ overflow to `Infinity`) in one grouping and not in the other, and a 0 produced that way then
140
+ annihilates whatever it meets.
141
+
142
+ Where neither happens, the modes agree, `NaN` for `NaN`; a property test checks this on random
143
+ expressions at 0, -0, ±Infinity, `NaN` and subnormal inputs.