@danielsimonjr/mathts-autograd 0.1.3 → 0.2.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/dist/index.d.ts +110 -0
- package/dist/index.js +281 -0
- package/package.json +58 -58
package/dist/index.d.ts
CHANGED
|
@@ -51,6 +51,68 @@ declare class DualTensor {
|
|
|
51
51
|
mul(other: DualTensor): DualTensor;
|
|
52
52
|
/** Scalar multiplication following dual-number rule: (k·a, k·a'). */
|
|
53
53
|
scale(k: number): DualTensor;
|
|
54
|
+
/**
|
|
55
|
+
* Elementwise division following the dual-number quotient rule:
|
|
56
|
+
* (a, a') / (b, b') = (a/b, (a'·b − a·b') / b²).
|
|
57
|
+
* The aliased case a.divide(a) is (1, 0).
|
|
58
|
+
*/
|
|
59
|
+
divide(other: DualTensor): DualTensor;
|
|
60
|
+
private _unaryElementwise;
|
|
61
|
+
/** Exponential. f'(x) = eˣ = y. */
|
|
62
|
+
exp(): DualTensor;
|
|
63
|
+
/** Natural logarithm. f'(x) = 1/x. */
|
|
64
|
+
log(): DualTensor;
|
|
65
|
+
/** Sine. f'(x) = cos(x). */
|
|
66
|
+
sin(): DualTensor;
|
|
67
|
+
/** Cosine. f'(x) = −sin(x). */
|
|
68
|
+
cos(): DualTensor;
|
|
69
|
+
/** Tangent. f'(x) = 1/cos²(x). */
|
|
70
|
+
tan(): DualTensor;
|
|
71
|
+
/** Square root. f'(x) = 1/(2·y) where y = √x. */
|
|
72
|
+
sqrt(): DualTensor;
|
|
73
|
+
/** Square (x²). f'(x) = 2x. */
|
|
74
|
+
square(): DualTensor;
|
|
75
|
+
/** Reciprocal (1/x). f'(x) = −1/x². */
|
|
76
|
+
reciprocal(): DualTensor;
|
|
77
|
+
/** Absolute value. f'(x) = sign(x) (subgradient 0 at exact zero). */
|
|
78
|
+
abs(): DualTensor;
|
|
79
|
+
/** Fixed-exponent power x^k. f'(x) = k·x^(k−1). */
|
|
80
|
+
pow(k: number): DualTensor;
|
|
81
|
+
/** Hyperbolic sine. f'(x) = cosh(x). */
|
|
82
|
+
sinh(): DualTensor;
|
|
83
|
+
/** Hyperbolic cosine. f'(x) = sinh(x). */
|
|
84
|
+
cosh(): DualTensor;
|
|
85
|
+
/** Hyperbolic tangent. f'(x) = 1 − tanh²(x) = 1 − y². */
|
|
86
|
+
tanh(): DualTensor;
|
|
87
|
+
/** Arcsine. f'(x) = 1/√(1 − x²). */
|
|
88
|
+
asin(): DualTensor;
|
|
89
|
+
/** Arccosine. f'(x) = −1/√(1 − x²). */
|
|
90
|
+
acos(): DualTensor;
|
|
91
|
+
/** Arctangent. f'(x) = 1/(1 + x²). */
|
|
92
|
+
atan(): DualTensor;
|
|
93
|
+
/** Inverse hyperbolic sine. f'(x) = 1/√(x² + 1). */
|
|
94
|
+
asinh(): DualTensor;
|
|
95
|
+
/** Inverse hyperbolic cosine (x ≥ 1). f'(x) = 1/√(x² − 1). */
|
|
96
|
+
acosh(): DualTensor;
|
|
97
|
+
/** Inverse hyperbolic tangent (|x| < 1). f'(x) = 1/(1 − x²). */
|
|
98
|
+
atanh(): DualTensor;
|
|
99
|
+
/** Base-2 logarithm. f'(x) = 1/(x·ln2). */
|
|
100
|
+
log2(): DualTensor;
|
|
101
|
+
/** Base-10 logarithm. f'(x) = 1/(x·ln10). */
|
|
102
|
+
log10(): DualTensor;
|
|
103
|
+
/** log(1 + x). f'(x) = 1/(1 + x). */
|
|
104
|
+
log1p(): DualTensor;
|
|
105
|
+
/** exp(x) − 1. f'(x) = eˣ = y + 1. */
|
|
106
|
+
expm1(): DualTensor;
|
|
107
|
+
/** Cube root. f'(x) = 1/(3·y²) where y = x^(1/3) (→∞ at x = 0). */
|
|
108
|
+
cbrt(): DualTensor;
|
|
109
|
+
/** Sign (−1/0/+1). f'(x) = 0 almost everywhere. */
|
|
110
|
+
sign(): DualTensor;
|
|
111
|
+
/**
|
|
112
|
+
* Two-argument arctangent: atan2(this, other), this = y, other = x.
|
|
113
|
+
* Tangent: (b·a' − a·b') / (a² + b²), with a = this, b = other.
|
|
114
|
+
*/
|
|
115
|
+
atan2(other: DualTensor): DualTensor;
|
|
54
116
|
private checkSameShape;
|
|
55
117
|
}
|
|
56
118
|
|
|
@@ -350,6 +412,54 @@ declare class TapedTensor {
|
|
|
350
412
|
* Subgradient at exact zero is defined as 0 (rather than undefined).
|
|
351
413
|
*/
|
|
352
414
|
abs(): TapedTensor;
|
|
415
|
+
/**
|
|
416
|
+
* Build a reverse-mode node for an elementwise unary op.
|
|
417
|
+
* @param primalFn forward value y = f(x)
|
|
418
|
+
* @param localGrad local derivative dy/dx, given x and the computed y
|
|
419
|
+
*/
|
|
420
|
+
private _unaryElementwise;
|
|
421
|
+
/** Hyperbolic sine. Adjoint: dX = dY · cosh(x). */
|
|
422
|
+
sinh(): TapedTensor;
|
|
423
|
+
/** Hyperbolic cosine. Adjoint: dX = dY · sinh(x). */
|
|
424
|
+
cosh(): TapedTensor;
|
|
425
|
+
/** Hyperbolic tangent. Adjoint: dX = dY · (1 − tanh²(x)) = dY · (1 − y²). */
|
|
426
|
+
tanh(): TapedTensor;
|
|
427
|
+
/** Arcsine. Adjoint: dX = dY / √(1 − x²). */
|
|
428
|
+
asin(): TapedTensor;
|
|
429
|
+
/** Arccosine. Adjoint: dX = −dY / √(1 − x²). */
|
|
430
|
+
acos(): TapedTensor;
|
|
431
|
+
/** Arctangent. Adjoint: dX = dY / (1 + x²). */
|
|
432
|
+
atan(): TapedTensor;
|
|
433
|
+
/** Inverse hyperbolic sine. Adjoint: dX = dY / √(x² + 1). */
|
|
434
|
+
asinh(): TapedTensor;
|
|
435
|
+
/** Inverse hyperbolic cosine (x ≥ 1). Adjoint: dX = dY / √(x² − 1). */
|
|
436
|
+
acosh(): TapedTensor;
|
|
437
|
+
/** Inverse hyperbolic tangent (|x| < 1). Adjoint: dX = dY / (1 − x²). */
|
|
438
|
+
atanh(): TapedTensor;
|
|
439
|
+
/** Base-2 logarithm. Adjoint: dX = dY / (x · ln2). */
|
|
440
|
+
log2(): TapedTensor;
|
|
441
|
+
/** Base-10 logarithm. Adjoint: dX = dY / (x · ln10). */
|
|
442
|
+
log10(): TapedTensor;
|
|
443
|
+
/** log(1 + x) (accurate near 0). Adjoint: dX = dY / (1 + x). */
|
|
444
|
+
log1p(): TapedTensor;
|
|
445
|
+
/** exp(x) − 1 (accurate near 0). Adjoint: dX = dY · eˣ = dY · (y + 1). */
|
|
446
|
+
expm1(): TapedTensor;
|
|
447
|
+
/** Cube root. Adjoint: dX = dY / (3 · y²) where y = x^(1/3) (→∞ at x = 0). */
|
|
448
|
+
cbrt(): TapedTensor;
|
|
449
|
+
/** Sign of x (−1/0/+1). Adjoint: 0 almost everywhere. */
|
|
450
|
+
sign(): TapedTensor;
|
|
451
|
+
/**
|
|
452
|
+
* Two-argument arctangent: atan2(this, other), with this = y, other = x.
|
|
453
|
+
*
|
|
454
|
+
* Adjoints (a = this, b = other, d = a² + b²):
|
|
455
|
+
* dA = dY · b / d
|
|
456
|
+
* dB = −dY · a / d
|
|
457
|
+
*
|
|
458
|
+
* The aliased case atan2(a, a) is locally constant (±π/4 or ±3π/4), so its
|
|
459
|
+
* gradient is 0 — produced naturally by contributing nothing when aliased
|
|
460
|
+
* (dA + dB = dY · (b − a)/d = 0 at a = b).
|
|
461
|
+
*/
|
|
462
|
+
atan2(other: TapedTensor): TapedTensor;
|
|
353
463
|
/**
|
|
354
464
|
* Reverse-mode AD over `Tensor.tensordot`.
|
|
355
465
|
*
|
package/dist/index.js
CHANGED
|
@@ -98,6 +98,172 @@ var DualTensor = class _DualTensor {
|
|
|
98
98
|
}
|
|
99
99
|
return new _DualTensor(this.shape, p, t);
|
|
100
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Elementwise division following the dual-number quotient rule:
|
|
103
|
+
* (a, a') / (b, b') = (a/b, (a'·b − a·b') / b²).
|
|
104
|
+
* The aliased case a.divide(a) is (1, 0).
|
|
105
|
+
*/
|
|
106
|
+
divide(other) {
|
|
107
|
+
this.checkSameShape(other, "divide");
|
|
108
|
+
const p = new Float64Array(this.primal.length);
|
|
109
|
+
const t = new Float64Array(this.tangent.length);
|
|
110
|
+
if (this === other) {
|
|
111
|
+
for (let i = 0; i < p.length; i++) {
|
|
112
|
+
p[i] = 1;
|
|
113
|
+
t[i] = 0;
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
for (let i = 0; i < p.length; i++) {
|
|
117
|
+
const a = this.primal[i];
|
|
118
|
+
const b = other.primal[i];
|
|
119
|
+
p[i] = a / b;
|
|
120
|
+
t[i] = (this.tangent[i] * b - a * other.tangent[i]) / (b * b);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return new _DualTensor(this.shape, p, t);
|
|
124
|
+
}
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
// Elementwise unary ops (parity with reverse-mode TapedTensor). Each follows
|
|
127
|
+
// the dual-number rule (a, a') ↦ (f(a), f'(a)·a'), built via the
|
|
128
|
+
// `_unaryElementwise` helper. `deriv(x, y)` is f'(x) given input x and the
|
|
129
|
+
// computed output y (so adjoints needing y — exp, tanh, sqrt — stay uniform).
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
_unaryElementwise(primalFn, deriv) {
|
|
132
|
+
const p = new Float64Array(this.primal.length);
|
|
133
|
+
const t = new Float64Array(this.tangent.length);
|
|
134
|
+
for (let i = 0; i < p.length; i++) {
|
|
135
|
+
const y = primalFn(this.primal[i]);
|
|
136
|
+
p[i] = y;
|
|
137
|
+
t[i] = deriv(this.primal[i], y) * this.tangent[i];
|
|
138
|
+
}
|
|
139
|
+
return new _DualTensor(this.shape, p, t);
|
|
140
|
+
}
|
|
141
|
+
/** Exponential. f'(x) = eˣ = y. */
|
|
142
|
+
exp() {
|
|
143
|
+
return this._unaryElementwise(Math.exp, (_x, y) => y);
|
|
144
|
+
}
|
|
145
|
+
/** Natural logarithm. f'(x) = 1/x. */
|
|
146
|
+
log() {
|
|
147
|
+
return this._unaryElementwise(Math.log, (x) => 1 / x);
|
|
148
|
+
}
|
|
149
|
+
/** Sine. f'(x) = cos(x). */
|
|
150
|
+
sin() {
|
|
151
|
+
return this._unaryElementwise(Math.sin, (x) => Math.cos(x));
|
|
152
|
+
}
|
|
153
|
+
/** Cosine. f'(x) = −sin(x). */
|
|
154
|
+
cos() {
|
|
155
|
+
return this._unaryElementwise(Math.cos, (x) => -Math.sin(x));
|
|
156
|
+
}
|
|
157
|
+
/** Tangent. f'(x) = 1/cos²(x). */
|
|
158
|
+
tan() {
|
|
159
|
+
return this._unaryElementwise(Math.tan, (x) => 1 / (Math.cos(x) * Math.cos(x)));
|
|
160
|
+
}
|
|
161
|
+
/** Square root. f'(x) = 1/(2·y) where y = √x. */
|
|
162
|
+
sqrt() {
|
|
163
|
+
return this._unaryElementwise(Math.sqrt, (_x, y) => 1 / (2 * y));
|
|
164
|
+
}
|
|
165
|
+
/** Square (x²). f'(x) = 2x. */
|
|
166
|
+
square() {
|
|
167
|
+
return this._unaryElementwise(
|
|
168
|
+
(x) => x * x,
|
|
169
|
+
(x) => 2 * x
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
/** Reciprocal (1/x). f'(x) = −1/x². */
|
|
173
|
+
reciprocal() {
|
|
174
|
+
return this._unaryElementwise(
|
|
175
|
+
(x) => 1 / x,
|
|
176
|
+
(x) => -1 / (x * x)
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
/** Absolute value. f'(x) = sign(x) (subgradient 0 at exact zero). */
|
|
180
|
+
abs() {
|
|
181
|
+
return this._unaryElementwise(Math.abs, (x) => Math.sign(x));
|
|
182
|
+
}
|
|
183
|
+
/** Fixed-exponent power x^k. f'(x) = k·x^(k−1). */
|
|
184
|
+
pow(k) {
|
|
185
|
+
return this._unaryElementwise(
|
|
186
|
+
(x) => Math.pow(x, k),
|
|
187
|
+
(x) => k * Math.pow(x, k - 1)
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
/** Hyperbolic sine. f'(x) = cosh(x). */
|
|
191
|
+
sinh() {
|
|
192
|
+
return this._unaryElementwise(Math.sinh, (x) => Math.cosh(x));
|
|
193
|
+
}
|
|
194
|
+
/** Hyperbolic cosine. f'(x) = sinh(x). */
|
|
195
|
+
cosh() {
|
|
196
|
+
return this._unaryElementwise(Math.cosh, (x) => Math.sinh(x));
|
|
197
|
+
}
|
|
198
|
+
/** Hyperbolic tangent. f'(x) = 1 − tanh²(x) = 1 − y². */
|
|
199
|
+
tanh() {
|
|
200
|
+
return this._unaryElementwise(Math.tanh, (_x, y) => 1 - y * y);
|
|
201
|
+
}
|
|
202
|
+
/** Arcsine. f'(x) = 1/√(1 − x²). */
|
|
203
|
+
asin() {
|
|
204
|
+
return this._unaryElementwise(Math.asin, (x) => 1 / Math.sqrt(1 - x * x));
|
|
205
|
+
}
|
|
206
|
+
/** Arccosine. f'(x) = −1/√(1 − x²). */
|
|
207
|
+
acos() {
|
|
208
|
+
return this._unaryElementwise(Math.acos, (x) => -1 / Math.sqrt(1 - x * x));
|
|
209
|
+
}
|
|
210
|
+
/** Arctangent. f'(x) = 1/(1 + x²). */
|
|
211
|
+
atan() {
|
|
212
|
+
return this._unaryElementwise(Math.atan, (x) => 1 / (1 + x * x));
|
|
213
|
+
}
|
|
214
|
+
/** Inverse hyperbolic sine. f'(x) = 1/√(x² + 1). */
|
|
215
|
+
asinh() {
|
|
216
|
+
return this._unaryElementwise(Math.asinh, (x) => 1 / Math.sqrt(x * x + 1));
|
|
217
|
+
}
|
|
218
|
+
/** Inverse hyperbolic cosine (x ≥ 1). f'(x) = 1/√(x² − 1). */
|
|
219
|
+
acosh() {
|
|
220
|
+
return this._unaryElementwise(Math.acosh, (x) => 1 / Math.sqrt(x * x - 1));
|
|
221
|
+
}
|
|
222
|
+
/** Inverse hyperbolic tangent (|x| < 1). f'(x) = 1/(1 − x²). */
|
|
223
|
+
atanh() {
|
|
224
|
+
return this._unaryElementwise(Math.atanh, (x) => 1 / (1 - x * x));
|
|
225
|
+
}
|
|
226
|
+
/** Base-2 logarithm. f'(x) = 1/(x·ln2). */
|
|
227
|
+
log2() {
|
|
228
|
+
return this._unaryElementwise(Math.log2, (x) => 1 / (x * Math.LN2));
|
|
229
|
+
}
|
|
230
|
+
/** Base-10 logarithm. f'(x) = 1/(x·ln10). */
|
|
231
|
+
log10() {
|
|
232
|
+
return this._unaryElementwise(Math.log10, (x) => 1 / (x * Math.LN10));
|
|
233
|
+
}
|
|
234
|
+
/** log(1 + x). f'(x) = 1/(1 + x). */
|
|
235
|
+
log1p() {
|
|
236
|
+
return this._unaryElementwise(Math.log1p, (x) => 1 / (1 + x));
|
|
237
|
+
}
|
|
238
|
+
/** exp(x) − 1. f'(x) = eˣ = y + 1. */
|
|
239
|
+
expm1() {
|
|
240
|
+
return this._unaryElementwise(Math.expm1, (_x, y) => y + 1);
|
|
241
|
+
}
|
|
242
|
+
/** Cube root. f'(x) = 1/(3·y²) where y = x^(1/3) (→∞ at x = 0). */
|
|
243
|
+
cbrt() {
|
|
244
|
+
return this._unaryElementwise(Math.cbrt, (_x, y) => 1 / (3 * y * y));
|
|
245
|
+
}
|
|
246
|
+
/** Sign (−1/0/+1). f'(x) = 0 almost everywhere. */
|
|
247
|
+
sign() {
|
|
248
|
+
return this._unaryElementwise(Math.sign, () => 0);
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Two-argument arctangent: atan2(this, other), this = y, other = x.
|
|
252
|
+
* Tangent: (b·a' − a·b') / (a² + b²), with a = this, b = other.
|
|
253
|
+
*/
|
|
254
|
+
atan2(other) {
|
|
255
|
+
this.checkSameShape(other, "atan2");
|
|
256
|
+
const p = new Float64Array(this.primal.length);
|
|
257
|
+
const t = new Float64Array(this.tangent.length);
|
|
258
|
+
for (let i = 0; i < p.length; i++) {
|
|
259
|
+
const a = this.primal[i];
|
|
260
|
+
const b = other.primal[i];
|
|
261
|
+
p[i] = Math.atan2(a, b);
|
|
262
|
+
const d = a * a + b * b;
|
|
263
|
+
t[i] = (b * this.tangent[i] - a * other.tangent[i]) / d;
|
|
264
|
+
}
|
|
265
|
+
return new _DualTensor(this.shape, p, t);
|
|
266
|
+
}
|
|
101
267
|
checkSameShape(other, op) {
|
|
102
268
|
if (this.shape.length !== other.shape.length || !this.shape.every((v, i) => v === other.shape[i])) {
|
|
103
269
|
throw new Error(`DualTensor.${op}: shape mismatch [${this.shape}] vs [${other.shape}]`);
|
|
@@ -1037,6 +1203,121 @@ var TapedTensor = class _TapedTensor {
|
|
|
1037
1203
|
return new _TapedTensor(this.shape, out, this.tape, id);
|
|
1038
1204
|
}
|
|
1039
1205
|
// ---------------------------------------------------------------------------
|
|
1206
|
+
// Extended elementwise transcendentals (hyperbolic, inverse-trig, log/exp
|
|
1207
|
+
// variants, sign, cbrt, atan2). Each builds a reverse-mode node via the
|
|
1208
|
+
// `_unaryElementwise` helper; `localGrad(x, y)` is dy/dx given input x and
|
|
1209
|
+
// output y. The helper caches both x and y so adjoints that need either
|
|
1210
|
+
// (e.g. tanh needs y, log needs x) are uniform.
|
|
1211
|
+
// ---------------------------------------------------------------------------
|
|
1212
|
+
/**
|
|
1213
|
+
* Build a reverse-mode node for an elementwise unary op.
|
|
1214
|
+
* @param primalFn forward value y = f(x)
|
|
1215
|
+
* @param localGrad local derivative dy/dx, given x and the computed y
|
|
1216
|
+
*/
|
|
1217
|
+
_unaryElementwise(primalFn, localGrad) {
|
|
1218
|
+
const xData = new Float64Array(this.primal);
|
|
1219
|
+
const out = new Float64Array(xData.length);
|
|
1220
|
+
for (let i = 0; i < out.length; i++) out[i] = primalFn(xData[i]);
|
|
1221
|
+
const yData = new Float64Array(out);
|
|
1222
|
+
const thisGradSlot = this.tape.getInputGrad(this.id);
|
|
1223
|
+
const { id } = this.tape.record([this.id], out.length, (outputGrad) => {
|
|
1224
|
+
for (let i = 0; i < outputGrad.length; i++) {
|
|
1225
|
+
thisGradSlot[i] += outputGrad[i] * localGrad(xData[i], yData[i]);
|
|
1226
|
+
}
|
|
1227
|
+
});
|
|
1228
|
+
return new _TapedTensor(this.shape, out, this.tape, id);
|
|
1229
|
+
}
|
|
1230
|
+
/** Hyperbolic sine. Adjoint: dX = dY · cosh(x). */
|
|
1231
|
+
sinh() {
|
|
1232
|
+
return this._unaryElementwise(Math.sinh, (x) => Math.cosh(x));
|
|
1233
|
+
}
|
|
1234
|
+
/** Hyperbolic cosine. Adjoint: dX = dY · sinh(x). */
|
|
1235
|
+
cosh() {
|
|
1236
|
+
return this._unaryElementwise(Math.cosh, (x) => Math.sinh(x));
|
|
1237
|
+
}
|
|
1238
|
+
/** Hyperbolic tangent. Adjoint: dX = dY · (1 − tanh²(x)) = dY · (1 − y²). */
|
|
1239
|
+
tanh() {
|
|
1240
|
+
return this._unaryElementwise(Math.tanh, (_x, y) => 1 - y * y);
|
|
1241
|
+
}
|
|
1242
|
+
/** Arcsine. Adjoint: dX = dY / √(1 − x²). */
|
|
1243
|
+
asin() {
|
|
1244
|
+
return this._unaryElementwise(Math.asin, (x) => 1 / Math.sqrt(1 - x * x));
|
|
1245
|
+
}
|
|
1246
|
+
/** Arccosine. Adjoint: dX = −dY / √(1 − x²). */
|
|
1247
|
+
acos() {
|
|
1248
|
+
return this._unaryElementwise(Math.acos, (x) => -1 / Math.sqrt(1 - x * x));
|
|
1249
|
+
}
|
|
1250
|
+
/** Arctangent. Adjoint: dX = dY / (1 + x²). */
|
|
1251
|
+
atan() {
|
|
1252
|
+
return this._unaryElementwise(Math.atan, (x) => 1 / (1 + x * x));
|
|
1253
|
+
}
|
|
1254
|
+
/** Inverse hyperbolic sine. Adjoint: dX = dY / √(x² + 1). */
|
|
1255
|
+
asinh() {
|
|
1256
|
+
return this._unaryElementwise(Math.asinh, (x) => 1 / Math.sqrt(x * x + 1));
|
|
1257
|
+
}
|
|
1258
|
+
/** Inverse hyperbolic cosine (x ≥ 1). Adjoint: dX = dY / √(x² − 1). */
|
|
1259
|
+
acosh() {
|
|
1260
|
+
return this._unaryElementwise(Math.acosh, (x) => 1 / Math.sqrt(x * x - 1));
|
|
1261
|
+
}
|
|
1262
|
+
/** Inverse hyperbolic tangent (|x| < 1). Adjoint: dX = dY / (1 − x²). */
|
|
1263
|
+
atanh() {
|
|
1264
|
+
return this._unaryElementwise(Math.atanh, (x) => 1 / (1 - x * x));
|
|
1265
|
+
}
|
|
1266
|
+
/** Base-2 logarithm. Adjoint: dX = dY / (x · ln2). */
|
|
1267
|
+
log2() {
|
|
1268
|
+
return this._unaryElementwise(Math.log2, (x) => 1 / (x * Math.LN2));
|
|
1269
|
+
}
|
|
1270
|
+
/** Base-10 logarithm. Adjoint: dX = dY / (x · ln10). */
|
|
1271
|
+
log10() {
|
|
1272
|
+
return this._unaryElementwise(Math.log10, (x) => 1 / (x * Math.LN10));
|
|
1273
|
+
}
|
|
1274
|
+
/** log(1 + x) (accurate near 0). Adjoint: dX = dY / (1 + x). */
|
|
1275
|
+
log1p() {
|
|
1276
|
+
return this._unaryElementwise(Math.log1p, (x) => 1 / (1 + x));
|
|
1277
|
+
}
|
|
1278
|
+
/** exp(x) − 1 (accurate near 0). Adjoint: dX = dY · eˣ = dY · (y + 1). */
|
|
1279
|
+
expm1() {
|
|
1280
|
+
return this._unaryElementwise(Math.expm1, (_x, y) => y + 1);
|
|
1281
|
+
}
|
|
1282
|
+
/** Cube root. Adjoint: dX = dY / (3 · y²) where y = x^(1/3) (→∞ at x = 0). */
|
|
1283
|
+
cbrt() {
|
|
1284
|
+
return this._unaryElementwise(Math.cbrt, (_x, y) => 1 / (3 * y * y));
|
|
1285
|
+
}
|
|
1286
|
+
/** Sign of x (−1/0/+1). Adjoint: 0 almost everywhere. */
|
|
1287
|
+
sign() {
|
|
1288
|
+
return this._unaryElementwise(Math.sign, () => 0);
|
|
1289
|
+
}
|
|
1290
|
+
/**
|
|
1291
|
+
* Two-argument arctangent: atan2(this, other), with this = y, other = x.
|
|
1292
|
+
*
|
|
1293
|
+
* Adjoints (a = this, b = other, d = a² + b²):
|
|
1294
|
+
* dA = dY · b / d
|
|
1295
|
+
* dB = −dY · a / d
|
|
1296
|
+
*
|
|
1297
|
+
* The aliased case atan2(a, a) is locally constant (±π/4 or ±3π/4), so its
|
|
1298
|
+
* gradient is 0 — produced naturally by contributing nothing when aliased
|
|
1299
|
+
* (dA + dB = dY · (b − a)/d = 0 at a = b).
|
|
1300
|
+
*/
|
|
1301
|
+
atan2(other) {
|
|
1302
|
+
this.checkSameShape(other, "atan2");
|
|
1303
|
+
const a = new Float64Array(this.primal);
|
|
1304
|
+
const b = new Float64Array(other.primal);
|
|
1305
|
+
const out = new Float64Array(a.length);
|
|
1306
|
+
for (let i = 0; i < out.length; i++) out[i] = Math.atan2(a[i], b[i]);
|
|
1307
|
+
const thisGradSlot = this.tape.getInputGrad(this.id);
|
|
1308
|
+
const otherGradSlot = this.tape.getInputGrad(other.id);
|
|
1309
|
+
const isAliased = this === other;
|
|
1310
|
+
const { id } = this.tape.record([this.id, other.id], out.length, (outputGrad) => {
|
|
1311
|
+
if (isAliased) return;
|
|
1312
|
+
for (let i = 0; i < outputGrad.length; i++) {
|
|
1313
|
+
const d = a[i] * a[i] + b[i] * b[i];
|
|
1314
|
+
thisGradSlot[i] += outputGrad[i] * b[i] / d;
|
|
1315
|
+
otherGradSlot[i] += -outputGrad[i] * a[i] / d;
|
|
1316
|
+
}
|
|
1317
|
+
});
|
|
1318
|
+
return new _TapedTensor(this.shape, out, this.tape, id);
|
|
1319
|
+
}
|
|
1320
|
+
// ---------------------------------------------------------------------------
|
|
1040
1321
|
// tensordot — generalised dot product over explicit axis pairs.
|
|
1041
1322
|
// ---------------------------------------------------------------------------
|
|
1042
1323
|
/**
|
package/package.json
CHANGED
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@danielsimonjr/mathts-autograd",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Forward + reverse-mode automatic differentiation for MathTS rank-N Tensor",
|
|
5
|
-
"author": "Daniel Simon Jr.",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"type": "module",
|
|
8
|
-
"main": "./dist/index.js",
|
|
9
|
-
"module": "./dist/index.js",
|
|
10
|
-
"types": "./dist/index.d.ts",
|
|
11
|
-
"exports": {
|
|
12
|
-
".": {
|
|
13
|
-
"import": "./dist/index.js",
|
|
14
|
-
"types": "./dist/index.d.ts"
|
|
15
|
-
}
|
|
16
|
-
},
|
|
17
|
-
"files": [
|
|
18
|
-
"dist",
|
|
19
|
-
"README.md"
|
|
20
|
-
],
|
|
21
|
-
"scripts": {
|
|
22
|
-
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
23
|
-
"dev": "tsup src/index.ts --format esm --dts --watch",
|
|
24
|
-
"test": "vitest run",
|
|
25
|
-
"test:watch": "vitest",
|
|
26
|
-
"test:coverage": "vitest run --coverage",
|
|
27
|
-
"typecheck": "tsc --noEmit",
|
|
28
|
-
"lint": "eslint src --ext .ts",
|
|
29
|
-
"lint:fix": "eslint src --ext .ts --fix",
|
|
30
|
-
"clean": "rm -rf dist",
|
|
31
|
-
"build:prod": "tsup src/index.ts --format esm --dts --clean --minify --treeshake"
|
|
32
|
-
},
|
|
33
|
-
"dependencies": {
|
|
34
|
-
"@danielsimonjr/mathts-core": "^0.1.4",
|
|
35
|
-
"@danielsimonjr/mathts-tensor": "^0.1.3"
|
|
36
|
-
},
|
|
37
|
-
"devDependencies": {
|
|
38
|
-
"@types/node": "^25.5.2",
|
|
39
|
-
"tsup": "^8.0.0",
|
|
40
|
-
"typescript": "^5.3.0",
|
|
41
|
-
"vitest": "^4.1.5"
|
|
42
|
-
},
|
|
43
|
-
"publishConfig": {
|
|
44
|
-
"access": "public"
|
|
45
|
-
},
|
|
46
|
-
"repository": {
|
|
47
|
-
"type": "git",
|
|
48
|
-
"url": "https://github.com/danielsimonjr/mathts",
|
|
49
|
-
"directory": "autograd"
|
|
50
|
-
},
|
|
51
|
-
"keywords": [
|
|
52
|
-
"math",
|
|
53
|
-
"autograd",
|
|
54
|
-
"automatic-differentiation",
|
|
55
|
-
"tensor",
|
|
56
|
-
"typescript"
|
|
57
|
-
]
|
|
58
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@danielsimonjr/mathts-autograd",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Forward + reverse-mode automatic differentiation for MathTS rank-N Tensor",
|
|
5
|
+
"author": "Daniel Simon Jr.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"module": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
23
|
+
"dev": "tsup src/index.ts --format esm --dts --watch",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"test:watch": "vitest",
|
|
26
|
+
"test:coverage": "vitest run --coverage",
|
|
27
|
+
"typecheck": "tsc --noEmit",
|
|
28
|
+
"lint": "eslint src --ext .ts",
|
|
29
|
+
"lint:fix": "eslint src --ext .ts --fix",
|
|
30
|
+
"clean": "rm -rf dist",
|
|
31
|
+
"build:prod": "tsup src/index.ts --format esm --dts --clean --minify --treeshake"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@danielsimonjr/mathts-core": "^0.1.4",
|
|
35
|
+
"@danielsimonjr/mathts-tensor": "^0.1.3"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^25.5.2",
|
|
39
|
+
"tsup": "^8.0.0",
|
|
40
|
+
"typescript": "^5.3.0",
|
|
41
|
+
"vitest": "^4.1.5"
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "https://github.com/danielsimonjr/mathts",
|
|
49
|
+
"directory": "autograd"
|
|
50
|
+
},
|
|
51
|
+
"keywords": [
|
|
52
|
+
"math",
|
|
53
|
+
"autograd",
|
|
54
|
+
"automatic-differentiation",
|
|
55
|
+
"tensor",
|
|
56
|
+
"typescript"
|
|
57
|
+
]
|
|
58
|
+
}
|