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.
- package/CHANGELOG.md +9 -0
- package/LICENSE +21 -0
- package/README.md +345 -2
- package/dist/index.d.ts +868 -0
- package/dist/index.js +1390 -0
- package/dist/index.js.map +1 -0
- package/docs/README.md +20 -0
- package/docs/api.md +165 -0
- package/docs/concepts.md +143 -0
- package/docs/pitfalls.md +133 -0
- package/docs/recipes.md +151 -0
- package/llms.txt +36 -0
- package/package.json +91 -4
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,868 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scalar arithmetic over a value type `T`. Write a function once against this interface, generic
|
|
3
|
+
* in `T`, and run it three ways: on plain numbers ({@link plain}), recording a tape for gradients
|
|
4
|
+
* ({@link Tape}), or carrying tangents for directional derivatives ({@link Forward}).
|
|
5
|
+
*
|
|
6
|
+
* Every operation on a `T` must go through these methods. A `T` is opaque: for `Tape` and
|
|
7
|
+
* `Forward` it is an index into storage, and `Math.exp(x)` or `x * y` on it computes nonsense
|
|
8
|
+
* (the branded types make most such mistakes compile errors). Numbers enter through
|
|
9
|
+
* {@link Arithmetic.constant} and leave through {@link Arithmetic.value}.
|
|
10
|
+
*
|
|
11
|
+
* The values every implementation computes come from the same {@link primitives} table, so a
|
|
12
|
+
* function's value is bit-for-bit identical under all of them. Subgradient conventions (`abs` at
|
|
13
|
+
* `0`, ties in `max`/`min`, `pow` with a non-positive base) are documented there.
|
|
14
|
+
*
|
|
15
|
+
* @typeParam T - the value type: `number` for `plain`, `Var` for `Tape`, `Dual` for `Forward`.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* import { type Arithmetic, plain, gradient } from 'adjointly';
|
|
20
|
+
*
|
|
21
|
+
* // f(x, y) = x * e^y + 3
|
|
22
|
+
* function f<T>(A: Arithmetic<T>, [x, y]: readonly [T, T]): T {
|
|
23
|
+
* return A.addc(A.mul(x, A.exp(y)), 3);
|
|
24
|
+
* }
|
|
25
|
+
* f(plain, [2, 0]); //=> 5
|
|
26
|
+
* gradient(f, [2, 0]); //=> { value: 5, gradient: [1, 2] }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
interface Arithmetic<T> {
|
|
30
|
+
/**
|
|
31
|
+
* Lift a number into `T` as a constant: its derivative with respect to every input is zero.
|
|
32
|
+
* On a `Tape` it still appends a node (constants are leaves of the tape).
|
|
33
|
+
*/
|
|
34
|
+
constant(x: number): T;
|
|
35
|
+
/**
|
|
36
|
+
* The current numeric value of `a`. Use it to branch (`if (A.value(x) > 0)`), to log, or to
|
|
37
|
+
* return a result. The number it returns carries no derivative information: anything computed
|
|
38
|
+
* from it with ordinary arithmetic is a constant as far as differentiation is concerned.
|
|
39
|
+
*/
|
|
40
|
+
value(a: T): number;
|
|
41
|
+
/** `-a`. */
|
|
42
|
+
neg(a: T): T;
|
|
43
|
+
/** `a + b`. */
|
|
44
|
+
add(a: T, b: T): T;
|
|
45
|
+
/** `a - b`. */
|
|
46
|
+
sub(a: T, b: T): T;
|
|
47
|
+
/** `a * b`. */
|
|
48
|
+
mul(a: T, b: T): T;
|
|
49
|
+
/** `a / b`. */
|
|
50
|
+
div(a: T, b: T): T;
|
|
51
|
+
/** `1 / a`. */
|
|
52
|
+
recip(a: T): T;
|
|
53
|
+
/** `a * a`, one node instead of a `mul` of `a` with itself. */
|
|
54
|
+
square(a: T): T;
|
|
55
|
+
/** `sqrt(a)`; its derivative at `0` is `+Infinity`. */
|
|
56
|
+
sqrt(a: T): T;
|
|
57
|
+
/** `e^a`. */
|
|
58
|
+
exp(a: T): T;
|
|
59
|
+
/** `e^a - 1`, accurate for small `a`. */
|
|
60
|
+
expm1(a: T): T;
|
|
61
|
+
/** Natural logarithm. */
|
|
62
|
+
log(a: T): T;
|
|
63
|
+
/** `log(1 + a)`, accurate for small `a`. */
|
|
64
|
+
log1p(a: T): T;
|
|
65
|
+
/**
|
|
66
|
+
* `a ** b`, differentiable in both arguments. The derivative with respect to `b` is `0` wherever
|
|
67
|
+
* `a <= 0`; the derivative with respect to `a` is `0` wherever `b === 0`. For a fixed numeric
|
|
68
|
+
* exponent use {@link Arithmetic.powc}; for `b = 2` prefer {@link Arithmetic.square}, for
|
|
69
|
+
* `b = 0.5` prefer {@link Arithmetic.sqrt}.
|
|
70
|
+
*/
|
|
71
|
+
pow(a: T, b: T): T;
|
|
72
|
+
/** `|a|`; its derivative at `0` is `0`. */
|
|
73
|
+
abs(a: T): T;
|
|
74
|
+
/** The smaller of `a` and `b`; on a tie the derivative flows to `a`. */
|
|
75
|
+
min(a: T, b: T): T;
|
|
76
|
+
/** The larger of `a` and `b`; on a tie the derivative flows to `a`. */
|
|
77
|
+
max(a: T, b: T): T;
|
|
78
|
+
/** Sine. */
|
|
79
|
+
sin(a: T): T;
|
|
80
|
+
/** Cosine. */
|
|
81
|
+
cos(a: T): T;
|
|
82
|
+
/** Hyperbolic tangent. */
|
|
83
|
+
tanh(a: T): T;
|
|
84
|
+
/** Logistic sigmoid `1 / (1 + e^-a)`, stable for any `a`. */
|
|
85
|
+
sigmoid(a: T): T;
|
|
86
|
+
/** Softplus `log(1 + e^a)`, stable for any `a`; its derivative is `sigmoid(a)`. */
|
|
87
|
+
softplus(a: T): T;
|
|
88
|
+
/**
|
|
89
|
+
* `a + c` for a plain number `c`: `add(a, constant(c))` without the constant's tape node. Same
|
|
90
|
+
* value and derivative, bit for bit.
|
|
91
|
+
*/
|
|
92
|
+
addc(a: T, c: number): T;
|
|
93
|
+
/** `a - c` for a plain number `c`; `sub(a, constant(c))` in one node. */
|
|
94
|
+
subc(a: T, c: number): T;
|
|
95
|
+
/** `c - a` for a plain number `c`; `sub(constant(c), a)` in one node. */
|
|
96
|
+
csub(c: number, a: T): T;
|
|
97
|
+
/** `a * c` for a plain number `c`; `mul(a, constant(c))` in one node. */
|
|
98
|
+
mulc(a: T, c: number): T;
|
|
99
|
+
/** `a / c` for a plain number `c`; `div(a, constant(c))` in one node. */
|
|
100
|
+
divc(a: T, c: number): T;
|
|
101
|
+
/** `c / a` for a plain number `c`; `div(constant(c), a)` in one node. */
|
|
102
|
+
cdiv(c: number, a: T): T;
|
|
103
|
+
/**
|
|
104
|
+
* `a ** c` for a plain number `c`; `pow(a, constant(c))` in one node, with the same conventions
|
|
105
|
+
* (the derivative is `0` when `c === 0`).
|
|
106
|
+
*/
|
|
107
|
+
powc(a: T, c: number): T;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The inputs of a {@link ScalarFunction}: one `T` for each number in the point `X`. For a point
|
|
111
|
+
* written as a literal, `[3, 4]`, that is the tuple `readonly [T, T]`, so destructured inputs need
|
|
112
|
+
* no non-null assertions; for a `number[]` it is `readonly T[]`.
|
|
113
|
+
*/
|
|
114
|
+
type Inputs<T, X extends readonly number[]> = { readonly [K in keyof X]: T; };
|
|
115
|
+
/**
|
|
116
|
+
* A function of several inputs written once against {@link Arithmetic}, the shape the
|
|
117
|
+
* conveniences ({@link gradient}, {@link jvp}, {@link evaluate}, {@link checkGradient}) accept.
|
|
118
|
+
* The input array has exactly as many elements as the point it is evaluated at; `X` is the type of
|
|
119
|
+
* that point, so `ScalarFunction<[number, number]>` takes two inputs.
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```ts
|
|
123
|
+
* const rosenbrock: ScalarFunction<[number, number]> = (A, [x, y]) =>
|
|
124
|
+
* A.add(
|
|
125
|
+
* A.square(A.csub(1, x)),
|
|
126
|
+
* A.mulc(A.square(A.sub(y, A.square(x))), 100),
|
|
127
|
+
* );
|
|
128
|
+
* evaluate(rosenbrock, [1, 1]); //=> 0
|
|
129
|
+
* gradient(rosenbrock, [0, 0]).gradient; //=> [-2, 0]
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
type ScalarFunction<X extends readonly number[] = readonly number[]> = <T>(A: Arithmetic<T>, xs: Inputs<T, X>) => T;
|
|
133
|
+
/**
|
|
134
|
+
* {@link Arithmetic} on ordinary numbers: computes values only, with nothing recorded. Use it to
|
|
135
|
+
* evaluate a differentiable function at full speed when no derivative is needed.
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* ```ts
|
|
139
|
+
* import { plain } from 'adjointly';
|
|
140
|
+
* plain.add(plain.constant(1), plain.exp(0)); //=> 2
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
export declare const plain: Arithmetic<number>;
|
|
144
|
+
/** A primitive of one argument: `y = value(x)`, `dy/dx = deriv(x, y)`. */
|
|
145
|
+
interface UnaryPrimitive {
|
|
146
|
+
/** The primitive's name, equal to its key in {@link primitives} and its method on `Arithmetic`. */
|
|
147
|
+
readonly name: string;
|
|
148
|
+
/** The value of the operation at `x`. */
|
|
149
|
+
readonly value: (x: number) => number;
|
|
150
|
+
/** The derivative `dy/dx` at `x`, given `y = value(x)`. */
|
|
151
|
+
readonly deriv: (x: number, y: number) => number;
|
|
152
|
+
}
|
|
153
|
+
/** A primitive of two arguments: `y = value(a, b)`, with partials `da = dy/da` and `db = dy/db`. */
|
|
154
|
+
interface BinaryPrimitive {
|
|
155
|
+
/** The primitive's name, equal to its key in {@link primitives} and its method on `Arithmetic`. */
|
|
156
|
+
readonly name: string;
|
|
157
|
+
/** The value of the operation at `(a, b)`. */
|
|
158
|
+
readonly value: (a: number, b: number) => number;
|
|
159
|
+
/** The partial derivative `dy/da` at `(a, b)`, given `y = value(a, b)`. */
|
|
160
|
+
readonly da: (a: number, b: number, y: number) => number;
|
|
161
|
+
/** The partial derivative `dy/db` at `(a, b)`, given `y = value(a, b)`. */
|
|
162
|
+
readonly db: (a: number, b: number, y: number) => number;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Every primitive, keyed by name. Subgradient conventions at points where the derivative does not
|
|
166
|
+
* exist are part of the contract:
|
|
167
|
+
*
|
|
168
|
+
* | primitive | convention |
|
|
169
|
+
* |---|---|
|
|
170
|
+
* | `abs` | derivative at `0` is `0` |
|
|
171
|
+
* | `max`, `min` | on a tie (`a === b`) the whole derivative goes to the first argument `a` |
|
|
172
|
+
* | `pow` | `d/db` is `0` wherever `a <= 0`; `d/da` is `0` wherever `b === 0` |
|
|
173
|
+
* | `sqrt` | derivative at `0` (and `-0`) is `+Infinity` (the true one-sided limit) |
|
|
174
|
+
* | `max`, `min` | if either argument is `NaN`, the value and both partials are `NaN`, as for `abs` |
|
|
175
|
+
*
|
|
176
|
+
* Everywhere else the partials are the ordinary analytic derivatives, and outside a function's
|
|
177
|
+
* domain (`log` of a negative, `sqrt` of a negative) the value and partials are whatever IEEE
|
|
178
|
+
* arithmetic produces, usually `NaN`.
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```ts
|
|
182
|
+
* import { primitives } from 'adjointly';
|
|
183
|
+
* const x = 0.5;
|
|
184
|
+
* const y = primitives.tanh.value(x); //=> tanh(0.5)
|
|
185
|
+
* const dy = primitives.tanh.deriv(x, y); //=> 1 - tanh(0.5) ** 2
|
|
186
|
+
* ```
|
|
187
|
+
*/
|
|
188
|
+
export declare const primitives: Readonly<{
|
|
189
|
+
/** `-x`. */
|
|
190
|
+
neg: UnaryPrimitive;
|
|
191
|
+
/** `a + b`. */
|
|
192
|
+
add: BinaryPrimitive;
|
|
193
|
+
/** `a - b`. */
|
|
194
|
+
sub: BinaryPrimitive;
|
|
195
|
+
/** `a * b`. */
|
|
196
|
+
mul: BinaryPrimitive;
|
|
197
|
+
/** `a / b`. */
|
|
198
|
+
div: BinaryPrimitive;
|
|
199
|
+
/** `1 / x`. */
|
|
200
|
+
recip: UnaryPrimitive;
|
|
201
|
+
/** `x * x`. */
|
|
202
|
+
square: UnaryPrimitive;
|
|
203
|
+
/** `sqrt(x)`; derivative `+Infinity` at `0` and at `-0`. */
|
|
204
|
+
sqrt: UnaryPrimitive;
|
|
205
|
+
/** `e^x`. */
|
|
206
|
+
exp: UnaryPrimitive;
|
|
207
|
+
/** `e^x - 1`, accurate near `0`. */
|
|
208
|
+
expm1: UnaryPrimitive;
|
|
209
|
+
/** Natural logarithm. */
|
|
210
|
+
log: UnaryPrimitive;
|
|
211
|
+
/** `log(1 + x)`, accurate near `0`. */
|
|
212
|
+
log1p: UnaryPrimitive;
|
|
213
|
+
/**
|
|
214
|
+
* `a ** b`, differentiable in both. `d/da = b * a^(b-1)`, taken as `0` when `b === 0` (so
|
|
215
|
+
* `0^0` does not produce `0 * Infinity`). `d/db = a^b * log(a)` for `a > 0` and `0` for
|
|
216
|
+
* `a <= 0`, where the real power is not differentiable in the exponent.
|
|
217
|
+
*/
|
|
218
|
+
pow: BinaryPrimitive;
|
|
219
|
+
/** `|x|`; derivative `0` at `0`. */
|
|
220
|
+
abs: UnaryPrimitive;
|
|
221
|
+
/**
|
|
222
|
+
* The smaller of `a` and `b`; on a tie the derivative goes to `a`. If either is `NaN`, the value
|
|
223
|
+
* and both partials are `NaN`.
|
|
224
|
+
*/
|
|
225
|
+
min: BinaryPrimitive;
|
|
226
|
+
/**
|
|
227
|
+
* The larger of `a` and `b`; on a tie the derivative goes to `a`. If either is `NaN`, the value
|
|
228
|
+
* and both partials are `NaN`.
|
|
229
|
+
*/
|
|
230
|
+
max: BinaryPrimitive;
|
|
231
|
+
/** Sine. */
|
|
232
|
+
sin: UnaryPrimitive;
|
|
233
|
+
/** Cosine. */
|
|
234
|
+
cos: UnaryPrimitive;
|
|
235
|
+
/** Hyperbolic tangent. */
|
|
236
|
+
tanh: UnaryPrimitive;
|
|
237
|
+
/** Logistic sigmoid `1 / (1 + e^-x)`, stable for any `x`. */
|
|
238
|
+
sigmoid: UnaryPrimitive;
|
|
239
|
+
/**
|
|
240
|
+
* Softplus `log(1 + e^x)`, stable for any `x`: computed as `x + log1p(e^-x)` for positive `x`
|
|
241
|
+
* and `log1p(e^x)` otherwise, so it neither overflows nor rounds to `0` for very negative `x`.
|
|
242
|
+
* Its derivative is `sigmoid(x)`.
|
|
243
|
+
*/
|
|
244
|
+
softplus: UnaryPrimitive;
|
|
245
|
+
}>;
|
|
246
|
+
/** Names of the one-argument primitives. */
|
|
247
|
+
type UnaryName = { [K in keyof typeof primitives]: (typeof primitives)[K] extends {
|
|
248
|
+
deriv: unknown;
|
|
249
|
+
} ? K : never; }[keyof typeof primitives];
|
|
250
|
+
/** Names of the two-argument primitives. */
|
|
251
|
+
type BinaryName = Exclude<keyof typeof primitives, UnaryName>;
|
|
252
|
+
/** The names of the one-argument primitives, in table order. */
|
|
253
|
+
export declare const unaryNames: readonly UnaryName[];
|
|
254
|
+
/** The names of the two-argument primitives, in table order. */
|
|
255
|
+
export declare const binaryNames: readonly BinaryName[];
|
|
256
|
+
declare const varBrand: unique symbol;
|
|
257
|
+
declare const markBrand: unique symbol;
|
|
258
|
+
/**
|
|
259
|
+
* A variable on a {@link Tape}: at run time a plain integer, the index of its node. The brand makes
|
|
260
|
+
* it a compile-time error to pass a raw number where a `Var` is expected, or to use a `Var` from a
|
|
261
|
+
* {@link Forward} where a tape's is expected; numbers enter a tape through
|
|
262
|
+
* {@link Tape.input} or {@link Tape.constant}.
|
|
263
|
+
*
|
|
264
|
+
* A `Var` is only meaningful on the tape that created it, and only until that tape is cleared or
|
|
265
|
+
* rewound past it. Neither is checked at run time.
|
|
266
|
+
*
|
|
267
|
+
* Because it is a number underneath, `x + y` on two `Var`s type-checks (TypeScript allows
|
|
268
|
+
* arithmetic on number subtypes) and silently adds two indices. Always use `tape.add(x, y)`.
|
|
269
|
+
*/
|
|
270
|
+
type Var = number & {
|
|
271
|
+
readonly [varBrand]: 'adjointly.Var';
|
|
272
|
+
};
|
|
273
|
+
/** A checkpoint on a {@link Tape}, returned by {@link Tape.mark} and consumed by {@link Tape.rewind}. */
|
|
274
|
+
type Mark = number & {
|
|
275
|
+
readonly [markBrand]: 'adjointly.Mark';
|
|
276
|
+
};
|
|
277
|
+
/** Options for constructing a {@link Tape}. */
|
|
278
|
+
interface TapeOptions {
|
|
279
|
+
/**
|
|
280
|
+
* Initial capacity in nodes, a positive integer (default 1024; anything else throws a
|
|
281
|
+
* `RangeError`). The tape doubles whenever it fills, so this only matters to avoid the first few
|
|
282
|
+
* reallocations when the size of the computation is known.
|
|
283
|
+
*/
|
|
284
|
+
readonly capacity?: number;
|
|
285
|
+
/**
|
|
286
|
+
* Check every operand (default `false`). Each method then verifies that its variables are live
|
|
287
|
+
* variables of this Tape, and throws a `RangeError` naming the method and the argument for a
|
|
288
|
+
* plain number, a variable from before `clear()` or `rewind()`, or one from another Tape or Forward.
|
|
289
|
+
* Off, nothing is checked and nothing is slower: a debug Tape is a separate set of methods on
|
|
290
|
+
* that one instance. Its variables are no longer small integers (they encode which recording
|
|
291
|
+
* they belong to), which is invisible unless they are used as numbers.
|
|
292
|
+
*
|
|
293
|
+
* @example
|
|
294
|
+
* ```ts
|
|
295
|
+
* const t = new Tape({ debug: true });
|
|
296
|
+
* t.input(5);
|
|
297
|
+
* let caught = '';
|
|
298
|
+
* try {
|
|
299
|
+
* t.exp(1 as Var);
|
|
300
|
+
* } catch (e) {
|
|
301
|
+
* caught = (e as Error).message.split(',')[0]!;
|
|
302
|
+
* }
|
|
303
|
+
* caught; //=> 'Tape.exp: argument 1 is the plain number 1'
|
|
304
|
+
* ```
|
|
305
|
+
*/
|
|
306
|
+
readonly debug?: boolean;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Reverse-mode automatic differentiation: an {@link Arithmetic} over {@link Var} that records
|
|
310
|
+
* every operation as a node on a tape, then computes the derivative of one output with respect to
|
|
311
|
+
* every node in a single backward sweep.
|
|
312
|
+
*
|
|
313
|
+
* Each node stores its value, up to two parent indices and the local partial derivatives with
|
|
314
|
+
* respect to them, computed when the node is recorded, and its adjoint: six typed arrays, 40
|
|
315
|
+
* bytes a node. They double when full, so recording does not allocate once the tape has grown to
|
|
316
|
+
* the size of the computation, and a tape reused across evaluations (via {@link Tape.clear} or
|
|
317
|
+
* {@link Tape.rewind}) never allocates again.
|
|
318
|
+
*
|
|
319
|
+
* The backward sweep visits nodes from the output down to the first, adding
|
|
320
|
+
* `partial * adjoint(child)` into each parent's adjoint, under the zero rule: if the partial or
|
|
321
|
+
* the adjoint is exactly 0 the term is 0, even when the other is infinite or `NaN`, so an unused
|
|
322
|
+
* `sqrt(0)` or `log(0)` does not poison the gradient.
|
|
323
|
+
*
|
|
324
|
+
* @example
|
|
325
|
+
* ```ts
|
|
326
|
+
* import { Tape } from 'adjointly';
|
|
327
|
+
* const t = new Tape();
|
|
328
|
+
* const x = t.input(2);
|
|
329
|
+
* const y = t.input(3);
|
|
330
|
+
* const f = t.mul(x, t.sin(y)); // x * sin(y)
|
|
331
|
+
* t.backward(f);
|
|
332
|
+
* t.adjoint(x); //=> sin(3)
|
|
333
|
+
* t.adjoint(y); //=> 2 * cos(3)
|
|
334
|
+
* ```
|
|
335
|
+
*/
|
|
336
|
+
export declare class Tape implements Arithmetic<Var> {
|
|
337
|
+
private val;
|
|
338
|
+
private pa;
|
|
339
|
+
private pb;
|
|
340
|
+
private da;
|
|
341
|
+
private db;
|
|
342
|
+
private adj;
|
|
343
|
+
/** The next free slot; nodes occupy slots 1 to n - 1. */
|
|
344
|
+
private n;
|
|
345
|
+
/** An empty tape; see {@link TapeOptions}. */
|
|
346
|
+
constructor(options?: TapeOptions);
|
|
347
|
+
/** The number of nodes recorded so far. */
|
|
348
|
+
get size(): number;
|
|
349
|
+
/** The number of nodes the tape can hold before it next grows. */
|
|
350
|
+
get capacity(): number;
|
|
351
|
+
/** Forget every node, keeping the storage. Every `Var` previously issued becomes invalid. */
|
|
352
|
+
clear(): void;
|
|
353
|
+
/**
|
|
354
|
+
* A checkpoint at the current end of the tape. {@link Tape.rewind} to it discards everything
|
|
355
|
+
* recorded after it and keeps everything before, so a shared prefix (inputs, parameters,
|
|
356
|
+
* precomputed features) is recorded once and reused across many evaluations.
|
|
357
|
+
*
|
|
358
|
+
* @example
|
|
359
|
+
* ```ts
|
|
360
|
+
* const t = new Tape();
|
|
361
|
+
* const w = t.input(0.5);
|
|
362
|
+
* const m = t.mark();
|
|
363
|
+
* let grad = 0;
|
|
364
|
+
* for (const x of [1, 2, 3]) {
|
|
365
|
+
* t.rewind(m); // drop the previous sample's nodes, keep w
|
|
366
|
+
* const loss = t.square(t.subc(t.mulc(w, x), 1));
|
|
367
|
+
* t.backward(loss);
|
|
368
|
+
* grad += t.adjoint(w);
|
|
369
|
+
* }
|
|
370
|
+
* grad; //=> 2 * ((0.5 - 1) * 1 + (1 - 1) * 2 + (1.5 - 1) * 3)
|
|
371
|
+
* t.size; //=> 4
|
|
372
|
+
* ```
|
|
373
|
+
*/
|
|
374
|
+
mark(): Mark;
|
|
375
|
+
/**
|
|
376
|
+
* Truncate the tape back to `mark`. Vars created before the mark stay valid with their values;
|
|
377
|
+
* Vars created after it become invalid. Throws if `mark` is beyond the current end (the tape was
|
|
378
|
+
* already cleared or rewound further back).
|
|
379
|
+
*/
|
|
380
|
+
rewind(mark: Mark): void;
|
|
381
|
+
/**
|
|
382
|
+
* Record an independent variable with value `x`. Identical to {@link Tape.constant} on the tape
|
|
383
|
+
* (both are leaves and both accumulate an adjoint); the separate name says which leaves you
|
|
384
|
+
* intend to read a gradient for.
|
|
385
|
+
*/
|
|
386
|
+
input(x: number): Var;
|
|
387
|
+
/**
|
|
388
|
+
* Record several independent variables at once; see {@link Tape.input}. For a tuple of numbers
|
|
389
|
+
* the result is a tuple of the same length, so it destructures without non-null assertions.
|
|
390
|
+
*
|
|
391
|
+
* @example
|
|
392
|
+
* ```ts
|
|
393
|
+
* const t = new Tape();
|
|
394
|
+
* const [x, y] = t.inputs([2, 3]);
|
|
395
|
+
* t.value(t.mul(x, y)); //=> 6
|
|
396
|
+
* ```
|
|
397
|
+
*/
|
|
398
|
+
inputs<const X extends readonly number[]>(xs: X): { -readonly [K in keyof X]: Var; };
|
|
399
|
+
inputs(xs: ArrayLike<number>): Var[];
|
|
400
|
+
/**
|
|
401
|
+
* Lift a number into `T` as a constant: its derivative with respect to every input is zero.
|
|
402
|
+
* On a `Tape` it still appends a node (constants are leaves of the tape).
|
|
403
|
+
*/
|
|
404
|
+
constant(x: number): Var;
|
|
405
|
+
/**
|
|
406
|
+
* The current numeric value of `a`. Use it to branch (`if (A.value(x) > 0)`), to log, or to
|
|
407
|
+
* return a result. The number it returns carries no derivative information: anything computed
|
|
408
|
+
* from it with ordinary arithmetic is a constant as far as differentiation is concerned.
|
|
409
|
+
*/
|
|
410
|
+
value(a: Var): number;
|
|
411
|
+
/**
|
|
412
|
+
* Run one backward sweep from `output`, seeding its adjoint with `seed` (default 1). Afterwards
|
|
413
|
+
* {@link Tape.adjoint} of any node `v` is `seed * d(output)/d(v)`. Every call starts from zero
|
|
414
|
+
* adjoints, so call it once per output whose gradient you want; the recorded tape is not
|
|
415
|
+
* changed and can be swept any number of times.
|
|
416
|
+
*/
|
|
417
|
+
backward(output: Var, seed?: number): void;
|
|
418
|
+
/**
|
|
419
|
+
* Vector-Jacobian product: one backward sweep seeded with `weights[k]` at `outputs[k]`.
|
|
420
|
+
* Afterwards {@link Tape.adjoint} of `v` is `sum_k weights[k] * d(outputs[k])/d(v)`. An output
|
|
421
|
+
* listed twice gets the sum of its weights; with no outputs every adjoint is zero.
|
|
422
|
+
*
|
|
423
|
+
* @example
|
|
424
|
+
* ```ts
|
|
425
|
+
* const t = new Tape();
|
|
426
|
+
* const x = t.input(1);
|
|
427
|
+
* const f = t.square(x); // df/dx = 2x
|
|
428
|
+
* const g = t.exp(x); // dg/dx = e^x
|
|
429
|
+
* t.vjp([f, g], [2, 3]); // gradient of 2f + 3g in one sweep
|
|
430
|
+
* t.adjoint(x); //=> 2 * 2 + 3 * E
|
|
431
|
+
* ```
|
|
432
|
+
*/
|
|
433
|
+
vjp(outputs: readonly Var[], weights: ArrayLike<number>): void;
|
|
434
|
+
/**
|
|
435
|
+
* The adjoint of `v` from the last {@link Tape.backward} or {@link Tape.vjp}: the derivative of
|
|
436
|
+
* the seeded output(s) with respect to `v`. Zero for a node the output does not depend on.
|
|
437
|
+
* Meaningless for a node recorded after the output of that sweep.
|
|
438
|
+
*/
|
|
439
|
+
adjoint(v: Var): number;
|
|
440
|
+
/**
|
|
441
|
+
* Read the adjoints of `vars` into `out` (allocated if not given) and return it: the gradient
|
|
442
|
+
* restricted to those variables, in their order.
|
|
443
|
+
*/
|
|
444
|
+
adjoints(vars: readonly Var[], out?: Float64Array): Float64Array;
|
|
445
|
+
/**
|
|
446
|
+
* `backward(output)` then `adjoints(inputs, out)`: the gradient of `output` with respect to
|
|
447
|
+
* `inputs`, in their order.
|
|
448
|
+
*
|
|
449
|
+
* @example
|
|
450
|
+
* ```ts
|
|
451
|
+
* const t = new Tape();
|
|
452
|
+
* const [x, y] = t.inputs([1, 2]);
|
|
453
|
+
* t.gradient(t.mul(x, y), [x, y]); //=> [2, 1]
|
|
454
|
+
* ```
|
|
455
|
+
*/
|
|
456
|
+
gradient(output: Var, inputs: readonly Var[], out?: Float64Array): Float64Array;
|
|
457
|
+
private sweep;
|
|
458
|
+
private push;
|
|
459
|
+
private grow;
|
|
460
|
+
/** `-a`. */
|
|
461
|
+
neg(a: Var): Var;
|
|
462
|
+
/** `a + b`. */
|
|
463
|
+
add(a: Var, b: Var): Var;
|
|
464
|
+
/** `a - b`. */
|
|
465
|
+
sub(a: Var, b: Var): Var;
|
|
466
|
+
/** `a * b`. */
|
|
467
|
+
mul(a: Var, b: Var): Var;
|
|
468
|
+
/** `a / b`. */
|
|
469
|
+
div(a: Var, b: Var): Var;
|
|
470
|
+
/** `1 / a`. */
|
|
471
|
+
recip(a: Var): Var;
|
|
472
|
+
/** `a * a`, one node instead of a `mul` of `a` with itself. */
|
|
473
|
+
square(a: Var): Var;
|
|
474
|
+
/** `sqrt(a)`; its derivative at `0` is `+Infinity`. */
|
|
475
|
+
sqrt(a: Var): Var;
|
|
476
|
+
/** `e^a`. */
|
|
477
|
+
exp(a: Var): Var;
|
|
478
|
+
/** `e^a - 1`, accurate for small `a`. */
|
|
479
|
+
expm1(a: Var): Var;
|
|
480
|
+
/** Natural logarithm. */
|
|
481
|
+
log(a: Var): Var;
|
|
482
|
+
/** `log(1 + a)`, accurate for small `a`. */
|
|
483
|
+
log1p(a: Var): Var;
|
|
484
|
+
/**
|
|
485
|
+
* `a ** b`, differentiable in both arguments. The derivative with respect to `b` is `0` wherever
|
|
486
|
+
* `a <= 0`; the derivative with respect to `a` is `0` wherever `b === 0`. For a fixed numeric
|
|
487
|
+
* exponent use {@link Arithmetic.powc}; for `b = 2` prefer {@link Arithmetic.square}, for
|
|
488
|
+
* `b = 0.5` prefer {@link Arithmetic.sqrt}.
|
|
489
|
+
*/
|
|
490
|
+
pow(a: Var, b: Var): Var;
|
|
491
|
+
/** `|a|`; its derivative at `0` is `0`. */
|
|
492
|
+
abs(a: Var): Var;
|
|
493
|
+
/** The smaller of `a` and `b`; on a tie the derivative flows to `a`. */
|
|
494
|
+
min(a: Var, b: Var): Var;
|
|
495
|
+
/** The larger of `a` and `b`; on a tie the derivative flows to `a`. */
|
|
496
|
+
max(a: Var, b: Var): Var;
|
|
497
|
+
/** Sine. */
|
|
498
|
+
sin(a: Var): Var;
|
|
499
|
+
/** Cosine. */
|
|
500
|
+
cos(a: Var): Var;
|
|
501
|
+
/** Hyperbolic tangent. */
|
|
502
|
+
tanh(a: Var): Var;
|
|
503
|
+
/** Logistic sigmoid `1 / (1 + e^-a)`, stable for any `a`. */
|
|
504
|
+
sigmoid(a: Var): Var;
|
|
505
|
+
/** Softplus `log(1 + e^a)`, stable for any `a`; its derivative is `sigmoid(a)`. */
|
|
506
|
+
softplus(a: Var): Var;
|
|
507
|
+
/**
|
|
508
|
+
* `a + c` for a plain number `c`: `add(a, constant(c))` without the constant's tape node. Same
|
|
509
|
+
* value and derivative, bit for bit.
|
|
510
|
+
*/
|
|
511
|
+
addc(a: Var, c: number): Var;
|
|
512
|
+
/** `a - c` for a plain number `c`; `sub(a, constant(c))` in one node. */
|
|
513
|
+
subc(a: Var, c: number): Var;
|
|
514
|
+
/** `c - a` for a plain number `c`; `sub(constant(c), a)` in one node. */
|
|
515
|
+
csub(c: number, a: Var): Var;
|
|
516
|
+
/** `a * c` for a plain number `c`; `mul(a, constant(c))` in one node. */
|
|
517
|
+
mulc(a: Var, c: number): Var;
|
|
518
|
+
/** `a / c` for a plain number `c`; `div(a, constant(c))` in one node. */
|
|
519
|
+
divc(a: Var, c: number): Var;
|
|
520
|
+
/** `c / a` for a plain number `c`; `div(constant(c), a)` in one node. */
|
|
521
|
+
cdiv(c: number, a: Var): Var;
|
|
522
|
+
/**
|
|
523
|
+
* `a ** c` for a plain number `c`; `pow(a, constant(c))` in one node, with the same conventions
|
|
524
|
+
* (the derivative is `0` when `c === 0`).
|
|
525
|
+
*/
|
|
526
|
+
powc(a: Var, c: number): Var;
|
|
527
|
+
}
|
|
528
|
+
declare const dualBrand: unique symbol;
|
|
529
|
+
/**
|
|
530
|
+
* A value in a {@link Forward} computation: at run time a plain integer, the index of its slot.
|
|
531
|
+
* Branded separately from `Var`, so a forward value cannot be passed to a `Tape` or vice versa, and
|
|
532
|
+
* a raw number cannot be passed where a `Dual` is expected.
|
|
533
|
+
*
|
|
534
|
+
* Valid only on the `Forward` that created it, and only until that `Forward` is cleared.
|
|
535
|
+
*/
|
|
536
|
+
type Dual = number & {
|
|
537
|
+
readonly [dualBrand]: 'adjointly.Dual';
|
|
538
|
+
};
|
|
539
|
+
/** Options for constructing a {@link Forward}. */
|
|
540
|
+
interface ForwardOptions {
|
|
541
|
+
/**
|
|
542
|
+
* Initial capacity in values, a positive integer (default 1024; anything else throws a
|
|
543
|
+
* `RangeError`); storage doubles whenever it fills.
|
|
544
|
+
*/
|
|
545
|
+
readonly capacity?: number;
|
|
546
|
+
/**
|
|
547
|
+
* Check every operand (default `false`). Each method then verifies that its variables are live
|
|
548
|
+
* variables of this Forward, and throws a `RangeError` naming the method and the argument for a
|
|
549
|
+
* plain number, a variable from before `clear()`, or one from another Tape or Forward.
|
|
550
|
+
* Off, nothing is checked and nothing is slower: a debug Forward is a separate set of methods on
|
|
551
|
+
* that one instance. Its variables are no longer small integers (they encode which recording
|
|
552
|
+
* they belong to), which is invisible unless they are used as numbers.
|
|
553
|
+
*
|
|
554
|
+
* @example
|
|
555
|
+
* ```ts
|
|
556
|
+
* const t = new Forward({ debug: true });
|
|
557
|
+
* t.input(5, 1);
|
|
558
|
+
* let caught = '';
|
|
559
|
+
* try {
|
|
560
|
+
* t.exp(1 as Dual);
|
|
561
|
+
* } catch (e) {
|
|
562
|
+
* caught = (e as Error).message.split(',')[0]!;
|
|
563
|
+
* }
|
|
564
|
+
* caught; //=> 'Forward.exp: argument 1 is the plain number 1'
|
|
565
|
+
* ```
|
|
566
|
+
*/
|
|
567
|
+
readonly debug?: boolean;
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Forward-mode automatic differentiation: an {@link Arithmetic} over {@link Dual} where every value
|
|
571
|
+
* carries a tangent, its derivative along one direction chosen when the inputs are created. One
|
|
572
|
+
* evaluation gives the directional derivative `grad f(x) . v` (a Jacobian-vector product) for every
|
|
573
|
+
* output at once, with no backward sweep and nothing kept but a value and a tangent per slot.
|
|
574
|
+
*
|
|
575
|
+
* Tangents propagate as `t(y) = da * t(a) + db * t(b)` using the same partials the {@link Tape}
|
|
576
|
+
* records, under the same zero rule: if a partial or a tangent is exactly 0 the term is 0, even when
|
|
577
|
+
* the other is infinite or `NaN`, so an infinite partial such as `sqrt` at `0` does not turn a zero
|
|
578
|
+
* tangent into `NaN`.
|
|
579
|
+
*
|
|
580
|
+
* Use forward mode when there are few inputs (or one direction) and many outputs; use the tape
|
|
581
|
+
* when there are many inputs and one output.
|
|
582
|
+
*
|
|
583
|
+
* @example
|
|
584
|
+
* ```ts
|
|
585
|
+
* import { Forward } from 'adjointly';
|
|
586
|
+
* const F = new Forward();
|
|
587
|
+
* const x = F.input(2, 1); // seed: direction (1, 0)
|
|
588
|
+
* const y = F.input(3, 0);
|
|
589
|
+
* const f = F.mul(x, F.sin(y)); // x * sin(y)
|
|
590
|
+
* F.value(f); //=> 2 * sin(3)
|
|
591
|
+
* F.tangent(f); //=> sin(3)
|
|
592
|
+
* ```
|
|
593
|
+
*/
|
|
594
|
+
export declare class Forward implements Arithmetic<Dual> {
|
|
595
|
+
private val;
|
|
596
|
+
private tan;
|
|
597
|
+
private n;
|
|
598
|
+
/** An empty forward-mode context; see {@link ForwardOptions}. */
|
|
599
|
+
constructor(options?: ForwardOptions);
|
|
600
|
+
/** The number of values created so far. */
|
|
601
|
+
get size(): number;
|
|
602
|
+
/** Forget every value, keeping the storage. Every `Dual` previously issued becomes invalid. */
|
|
603
|
+
clear(): void;
|
|
604
|
+
/** An input with value `x` and tangent (seed) `tangent`: its component of the direction. */
|
|
605
|
+
input(x: number, tangent: number): Dual;
|
|
606
|
+
/**
|
|
607
|
+
* Lift a number into `T` as a constant: its derivative with respect to every input is zero.
|
|
608
|
+
* On a `Tape` it still appends a node (constants are leaves of the tape).
|
|
609
|
+
*/
|
|
610
|
+
constant(x: number): Dual;
|
|
611
|
+
/**
|
|
612
|
+
* The current numeric value of `a`. Use it to branch (`if (A.value(x) > 0)`), to log, or to
|
|
613
|
+
* return a result. The number it returns carries no derivative information: anything computed
|
|
614
|
+
* from it with ordinary arithmetic is a constant as far as differentiation is concerned.
|
|
615
|
+
*/
|
|
616
|
+
value(a: Dual): number;
|
|
617
|
+
/** The tangent of `a`: its derivative along the direction the inputs were seeded with. */
|
|
618
|
+
tangent(a: Dual): number;
|
|
619
|
+
private push;
|
|
620
|
+
private grow;
|
|
621
|
+
/** `-a`. */
|
|
622
|
+
neg(a: Dual): Dual;
|
|
623
|
+
/** `a + b`. */
|
|
624
|
+
add(a: Dual, b: Dual): Dual;
|
|
625
|
+
/** `a - b`. */
|
|
626
|
+
sub(a: Dual, b: Dual): Dual;
|
|
627
|
+
/** `a * b`. */
|
|
628
|
+
mul(a: Dual, b: Dual): Dual;
|
|
629
|
+
/** `a / b`. */
|
|
630
|
+
div(a: Dual, b: Dual): Dual;
|
|
631
|
+
/** `1 / a`. */
|
|
632
|
+
recip(a: Dual): Dual;
|
|
633
|
+
/** `a * a`, one node instead of a `mul` of `a` with itself. */
|
|
634
|
+
square(a: Dual): Dual;
|
|
635
|
+
/** `sqrt(a)`; its derivative at `0` is `+Infinity`. */
|
|
636
|
+
sqrt(a: Dual): Dual;
|
|
637
|
+
/** `e^a`. */
|
|
638
|
+
exp(a: Dual): Dual;
|
|
639
|
+
/** `e^a - 1`, accurate for small `a`. */
|
|
640
|
+
expm1(a: Dual): Dual;
|
|
641
|
+
/** Natural logarithm. */
|
|
642
|
+
log(a: Dual): Dual;
|
|
643
|
+
/** `log(1 + a)`, accurate for small `a`. */
|
|
644
|
+
log1p(a: Dual): Dual;
|
|
645
|
+
/**
|
|
646
|
+
* `a ** b`, differentiable in both arguments. The derivative with respect to `b` is `0` wherever
|
|
647
|
+
* `a <= 0`; the derivative with respect to `a` is `0` wherever `b === 0`. For a fixed numeric
|
|
648
|
+
* exponent use {@link Arithmetic.powc}; for `b = 2` prefer {@link Arithmetic.square}, for
|
|
649
|
+
* `b = 0.5` prefer {@link Arithmetic.sqrt}.
|
|
650
|
+
*/
|
|
651
|
+
pow(a: Dual, b: Dual): Dual;
|
|
652
|
+
/** `|a|`; its derivative at `0` is `0`. */
|
|
653
|
+
abs(a: Dual): Dual;
|
|
654
|
+
/** The smaller of `a` and `b`; on a tie the derivative flows to `a`. */
|
|
655
|
+
min(a: Dual, b: Dual): Dual;
|
|
656
|
+
/** The larger of `a` and `b`; on a tie the derivative flows to `a`. */
|
|
657
|
+
max(a: Dual, b: Dual): Dual;
|
|
658
|
+
/** Sine. */
|
|
659
|
+
sin(a: Dual): Dual;
|
|
660
|
+
/** Cosine. */
|
|
661
|
+
cos(a: Dual): Dual;
|
|
662
|
+
/** Hyperbolic tangent. */
|
|
663
|
+
tanh(a: Dual): Dual;
|
|
664
|
+
/** Logistic sigmoid `1 / (1 + e^-a)`, stable for any `a`. */
|
|
665
|
+
sigmoid(a: Dual): Dual;
|
|
666
|
+
/** Softplus `log(1 + e^a)`, stable for any `a`; its derivative is `sigmoid(a)`. */
|
|
667
|
+
softplus(a: Dual): Dual;
|
|
668
|
+
/**
|
|
669
|
+
* `a + c` for a plain number `c`: `add(a, constant(c))` without the constant's tape node. Same
|
|
670
|
+
* value and derivative, bit for bit.
|
|
671
|
+
*/
|
|
672
|
+
addc(a: Dual, c: number): Dual;
|
|
673
|
+
/** `a - c` for a plain number `c`; `sub(a, constant(c))` in one node. */
|
|
674
|
+
subc(a: Dual, c: number): Dual;
|
|
675
|
+
/** `c - a` for a plain number `c`; `sub(constant(c), a)` in one node. */
|
|
676
|
+
csub(c: number, a: Dual): Dual;
|
|
677
|
+
/** `a * c` for a plain number `c`; `mul(a, constant(c))` in one node. */
|
|
678
|
+
mulc(a: Dual, c: number): Dual;
|
|
679
|
+
/** `a / c` for a plain number `c`; `div(a, constant(c))` in one node. */
|
|
680
|
+
divc(a: Dual, c: number): Dual;
|
|
681
|
+
/** `c / a` for a plain number `c`; `div(constant(c), a)` in one node. */
|
|
682
|
+
cdiv(c: number, a: Dual): Dual;
|
|
683
|
+
/**
|
|
684
|
+
* `a ** c` for a plain number `c`; `pow(a, constant(c))` in one node, with the same conventions
|
|
685
|
+
* (the derivative is `0` when `c === 0`).
|
|
686
|
+
*/
|
|
687
|
+
powc(a: Dual, c: number): Dual;
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* The sum of `xs`, added left to right; `A.constant(0)` for an empty list.
|
|
691
|
+
*
|
|
692
|
+
* @example
|
|
693
|
+
* ```ts
|
|
694
|
+
* sum(plain, [1, 2, 3]); //=> 6
|
|
695
|
+
* ```
|
|
696
|
+
*/
|
|
697
|
+
export declare function sum<T>(A: Arithmetic<T>, xs: readonly T[]): T;
|
|
698
|
+
/**
|
|
699
|
+
* The dot product `sum_i xs[i] * ys[i]`; `A.constant(0)` for empty lists. Throws if the lengths
|
|
700
|
+
* differ.
|
|
701
|
+
*
|
|
702
|
+
* @example
|
|
703
|
+
* ```ts
|
|
704
|
+
* dot(plain, [1, 2], [3, 4]); //=> 11
|
|
705
|
+
* ```
|
|
706
|
+
*/
|
|
707
|
+
export declare function dot<T>(A: Arithmetic<T>, xs: readonly T[], ys: readonly T[]): T;
|
|
708
|
+
/**
|
|
709
|
+
* `log(sum_i exp(xs[i]))`, computed stably as `m + log(sum_i exp(xs[i] - m))` where `m` is the
|
|
710
|
+
* largest value. `m` enters as a constant: the identity holds for any `m`, so its derivative
|
|
711
|
+
* through `m` is zero and leaving it out keeps the gradient exact (it is `softmax(xs)`).
|
|
712
|
+
* Throws on an empty list.
|
|
713
|
+
*
|
|
714
|
+
* Infinite inputs give the limit of the finite case. If the largest input is `+Infinity`, or every
|
|
715
|
+
* input is `-Infinity`, the value is that infinity and the gradient is shared equally by the inputs
|
|
716
|
+
* equal to it, `0` elsewhere; a `-Infinity` among finite inputs has weight `0`. A `NaN` input
|
|
717
|
+
* makes everything `NaN`.
|
|
718
|
+
*
|
|
719
|
+
* @example
|
|
720
|
+
* ```ts
|
|
721
|
+
* // The naive log(exp(1000) + exp(1000)) overflows to Infinity.
|
|
722
|
+
* logsumexp(plain, [1000, 1000]); //=> 1000 + log(2)
|
|
723
|
+
* gradient((A, xs) => logsumexp(A, xs), [Infinity, 0]).gradient; //=> [1, 0]
|
|
724
|
+
* ```
|
|
725
|
+
*/
|
|
726
|
+
export declare function logsumexp<T>(A: Arithmetic<T>, xs: readonly T[]): T;
|
|
727
|
+
/**
|
|
728
|
+
* The softmax `exp(xs[i]) / sum_j exp(xs[j])`, computed stably by first subtracting the largest
|
|
729
|
+
* value as a constant (softmax is invariant to the shift, so the gradient stays exact). Throws on
|
|
730
|
+
* an empty list.
|
|
731
|
+
*
|
|
732
|
+
* Infinite inputs give the limit of the finite case. If the largest input is `+Infinity`, or every
|
|
733
|
+
* input is `-Infinity`, the weights are `1/k` on the `k` inputs equal to it and `0` elsewhere,
|
|
734
|
+
* returned as constants: the Jacobian there is `0`, which is also its limit. A `-Infinity` among
|
|
735
|
+
* finite inputs gets weight `0`. A `NaN` input makes every weight `NaN`.
|
|
736
|
+
*
|
|
737
|
+
* @example
|
|
738
|
+
* ```ts
|
|
739
|
+
* softmax(plain, [0, Math.log(3)]); //=> [0.25, 0.75]
|
|
740
|
+
* softmax(plain, [Infinity, 0, Infinity]); //=> [0.5, 0, 0.5]
|
|
741
|
+
* ```
|
|
742
|
+
*/
|
|
743
|
+
export declare function softmax<T>(A: Arithmetic<T>, xs: readonly T[]): T[];
|
|
744
|
+
/** The result of {@link gradient}. */
|
|
745
|
+
interface GradientResult {
|
|
746
|
+
/** `f(at)`. */
|
|
747
|
+
readonly value: number;
|
|
748
|
+
/** `df/d(at[i])` for each `i`. */
|
|
749
|
+
readonly gradient: number[];
|
|
750
|
+
}
|
|
751
|
+
/** The result of {@link jvp}. */
|
|
752
|
+
interface JvpResult {
|
|
753
|
+
/** `f(at)`. */
|
|
754
|
+
readonly value: number;
|
|
755
|
+
/** The directional derivative `sum_i df/d(at[i]) * direction[i]`. */
|
|
756
|
+
readonly derivative: number;
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Evaluate `f` at `at` on plain numbers, recording nothing. The value is bit-for-bit the one
|
|
760
|
+
* {@link gradient} and {@link jvp} report.
|
|
761
|
+
*
|
|
762
|
+
* @example
|
|
763
|
+
* ```ts
|
|
764
|
+
* evaluate((A, [x]) => A.exp(x), [0]); //=> 1
|
|
765
|
+
* ```
|
|
766
|
+
*/
|
|
767
|
+
export declare function evaluate<const X extends readonly number[]>(f: ScalarFunction<X>, at: X): number;
|
|
768
|
+
/**
|
|
769
|
+
* The value and gradient of `f` at `at`, by reverse mode: one forward pass recording a tape and
|
|
770
|
+
* one backward sweep, whatever the number of inputs.
|
|
771
|
+
*
|
|
772
|
+
* Pass `tape` to reuse its storage across calls in a loop; it is cleared first and left holding
|
|
773
|
+
* this evaluation.
|
|
774
|
+
*
|
|
775
|
+
* @example
|
|
776
|
+
* ```ts
|
|
777
|
+
* gradient((A, [x, y]) => A.mul(x, y), [3, 4]); //=> { value: 12, gradient: [4, 3] }
|
|
778
|
+
* ```
|
|
779
|
+
*/
|
|
780
|
+
export declare function gradient<const X extends readonly number[]>(f: ScalarFunction<X>, at: X, tape?: Tape): GradientResult;
|
|
781
|
+
/**
|
|
782
|
+
* The value of `f` at `at` and its derivative along `direction` (a Jacobian-vector product), by
|
|
783
|
+
* forward mode: one pass, no tape. Throws if `direction` and `at` differ in length.
|
|
784
|
+
*
|
|
785
|
+
* @example
|
|
786
|
+
* ```ts
|
|
787
|
+
* jvp((A, [x, y]) => A.mul(x, y), [3, 4], [1, 0]); //=> { value: 12, derivative: 4 }
|
|
788
|
+
* ```
|
|
789
|
+
*/
|
|
790
|
+
export declare function jvp<const X extends readonly number[]>(f: ScalarFunction<X>, at: X, direction: readonly number[], forward?: Forward): JvpResult;
|
|
791
|
+
/**
|
|
792
|
+
* Default relative step for {@link finiteDifference}: about the cube root of machine epsilon,
|
|
793
|
+
* which balances the central difference's `O(h^2)` truncation error against rounding error.
|
|
794
|
+
*/
|
|
795
|
+
export declare const DEFAULT_STEP = 0.000006;
|
|
796
|
+
/**
|
|
797
|
+
* The gradient of `f` at `at` by central differences on plain numbers:
|
|
798
|
+
* `(f(x + h_i e_i) - f(x - h_i e_i)) / (2 h_i)` with `h_i = h * max(1, |at[i]|)`.
|
|
799
|
+
* Costs two evaluations per input; expect agreement with the true gradient to roughly 1e-8
|
|
800
|
+
* relative for a smooth, well-scaled function, and nothing useful across a kink or a branch.
|
|
801
|
+
*
|
|
802
|
+
* @param h - relative step, default {@link DEFAULT_STEP}.
|
|
803
|
+
*
|
|
804
|
+
* @example
|
|
805
|
+
* ```ts
|
|
806
|
+
* finiteDifference((A, [x]) => A.square(x), [3]); //=> ~[6]
|
|
807
|
+
* ```
|
|
808
|
+
*/
|
|
809
|
+
export declare function finiteDifference<const X extends readonly number[]>(f: ScalarFunction<X>, at: X, h?: number): number[];
|
|
810
|
+
/** Options for {@link checkGradient}. */
|
|
811
|
+
interface CheckGradientOptions {
|
|
812
|
+
/** Relative finite-difference step; default {@link DEFAULT_STEP}. */
|
|
813
|
+
readonly h?: number;
|
|
814
|
+
/** Relative tolerance; default `1e-5`. */
|
|
815
|
+
readonly rtol?: number;
|
|
816
|
+
/** Absolute tolerance; default `1e-8`. */
|
|
817
|
+
readonly atol?: number;
|
|
818
|
+
}
|
|
819
|
+
/** The result of {@link checkGradient}. */
|
|
820
|
+
interface GradientCheck {
|
|
821
|
+
/** Whether every component is within tolerance (and none is `NaN`). */
|
|
822
|
+
readonly ok: boolean;
|
|
823
|
+
/** `f(at)`. */
|
|
824
|
+
readonly value: number;
|
|
825
|
+
/** The gradient by reverse mode. */
|
|
826
|
+
readonly analytic: number[];
|
|
827
|
+
/** The gradient by central differences. */
|
|
828
|
+
readonly numeric: number[];
|
|
829
|
+
/**
|
|
830
|
+
* The component furthest out of tolerance relative to its allowance (`-1` index when `at` is
|
|
831
|
+
* empty). `error` is `|analytic - numeric|`; `roundoff` the estimated rounding error of the
|
|
832
|
+
* finite difference; `allowed` is `atol + rtol * max(|analytic|, |numeric|) + roundoff`.
|
|
833
|
+
*/
|
|
834
|
+
readonly worst: {
|
|
835
|
+
readonly index: number;
|
|
836
|
+
readonly analytic: number;
|
|
837
|
+
readonly numeric: number;
|
|
838
|
+
readonly error: number;
|
|
839
|
+
readonly allowed: number;
|
|
840
|
+
readonly roundoff: number;
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
/**
|
|
844
|
+
* Compare `f`'s reverse-mode gradient at `at` with central finite differences. Component `i`
|
|
845
|
+
* passes when `|analytic - numeric| <= atol + rtol * max(|analytic|, |numeric|) + roundoff`.
|
|
846
|
+
*
|
|
847
|
+
* `roundoff` allows for the rounding error of the finite difference itself, which grows with
|
|
848
|
+
* `|f|` and shrinks with the step: `4 * eps * (|f(x + h)| + |f(x - h)|) / (2 h)`, four units in the
|
|
849
|
+
* last place for each evaluation. Without it an exact gradient of a function whose value is large
|
|
850
|
+
* next to its slope (`1e5 + 1e-3 * x`) would fail.
|
|
851
|
+
*
|
|
852
|
+
* Use it in your own tests to verify a function written against `Arithmetic`, typically at a few
|
|
853
|
+
* random points away from kinks and branch boundaries, where finite differences are meaningless.
|
|
854
|
+
*
|
|
855
|
+
* @example
|
|
856
|
+
* ```ts
|
|
857
|
+
* const loss: ScalarFunction = (A, xs) => logsumexp(A, xs.map((x) => A.square(x)));
|
|
858
|
+
* const r = checkGradient(loss, [0.3, -1.2, 2]);
|
|
859
|
+
* if (!r.ok) {
|
|
860
|
+
* const { index, analytic, numeric } = r.worst;
|
|
861
|
+
* throw new Error(`component ${String(index)}: ${String(analytic)} vs ${String(numeric)}`);
|
|
862
|
+
* }
|
|
863
|
+
* r.ok; //=> true
|
|
864
|
+
* ```
|
|
865
|
+
*/
|
|
866
|
+
export declare function checkGradient<const X extends readonly number[]>(f: ScalarFunction<X>, at: X, options?: CheckGradientOptions): GradientCheck;
|
|
867
|
+
export type { Arithmetic, BinaryName, BinaryPrimitive, CheckGradientOptions, Dual, ForwardOptions, GradientCheck, GradientResult, Inputs, JvpResult, Mark, ScalarFunction, TapeOptions, UnaryName, UnaryPrimitive, Var };
|
|
868
|
+
//# sourceMappingURL=index.d.ts.map
|