deepbox 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.
Files changed (173) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +344 -0
  3. package/dist/CSRMatrix-CwGwQRea.d.cts +219 -0
  4. package/dist/CSRMatrix-KzNt6QpS.d.ts +219 -0
  5. package/dist/Tensor-BQLk1ltW.d.cts +147 -0
  6. package/dist/Tensor-g8mUClel.d.ts +147 -0
  7. package/dist/chunk-4S73VUBD.js +677 -0
  8. package/dist/chunk-4S73VUBD.js.map +1 -0
  9. package/dist/chunk-5R4S63PF.js +2925 -0
  10. package/dist/chunk-5R4S63PF.js.map +1 -0
  11. package/dist/chunk-6AE5FKKQ.cjs +9264 -0
  12. package/dist/chunk-6AE5FKKQ.cjs.map +1 -0
  13. package/dist/chunk-AD436M45.js +3854 -0
  14. package/dist/chunk-AD436M45.js.map +1 -0
  15. package/dist/chunk-ALS7ETWZ.cjs +4263 -0
  16. package/dist/chunk-ALS7ETWZ.cjs.map +1 -0
  17. package/dist/chunk-AU7XHGKJ.js +2092 -0
  18. package/dist/chunk-AU7XHGKJ.js.map +1 -0
  19. package/dist/chunk-B5TNKUEY.js +1481 -0
  20. package/dist/chunk-B5TNKUEY.js.map +1 -0
  21. package/dist/chunk-BCR7G3A6.js +9136 -0
  22. package/dist/chunk-BCR7G3A6.js.map +1 -0
  23. package/dist/chunk-C4PKXY74.cjs +1917 -0
  24. package/dist/chunk-C4PKXY74.cjs.map +1 -0
  25. package/dist/chunk-DWZY6PIP.cjs +6400 -0
  26. package/dist/chunk-DWZY6PIP.cjs.map +1 -0
  27. package/dist/chunk-E3EU5FZO.cjs +2113 -0
  28. package/dist/chunk-E3EU5FZO.cjs.map +1 -0
  29. package/dist/chunk-F3JWBINJ.js +1054 -0
  30. package/dist/chunk-F3JWBINJ.js.map +1 -0
  31. package/dist/chunk-FJYLIGJX.js +1940 -0
  32. package/dist/chunk-FJYLIGJX.js.map +1 -0
  33. package/dist/chunk-JSCDE774.cjs +729 -0
  34. package/dist/chunk-JSCDE774.cjs.map +1 -0
  35. package/dist/chunk-LWECRCW2.cjs +2412 -0
  36. package/dist/chunk-LWECRCW2.cjs.map +1 -0
  37. package/dist/chunk-MLBMYKCG.js +6379 -0
  38. package/dist/chunk-MLBMYKCG.js.map +1 -0
  39. package/dist/chunk-OX6QXFMV.cjs +3874 -0
  40. package/dist/chunk-OX6QXFMV.cjs.map +1 -0
  41. package/dist/chunk-PHV2DKRS.cjs +1072 -0
  42. package/dist/chunk-PHV2DKRS.cjs.map +1 -0
  43. package/dist/chunk-PL7TAYKI.js +4056 -0
  44. package/dist/chunk-PL7TAYKI.js.map +1 -0
  45. package/dist/chunk-PR647I7R.js +1898 -0
  46. package/dist/chunk-PR647I7R.js.map +1 -0
  47. package/dist/chunk-QERHVCHC.cjs +2960 -0
  48. package/dist/chunk-QERHVCHC.cjs.map +1 -0
  49. package/dist/chunk-XEG44RF6.cjs +1514 -0
  50. package/dist/chunk-XEG44RF6.cjs.map +1 -0
  51. package/dist/chunk-XMWVME2W.js +2377 -0
  52. package/dist/chunk-XMWVME2W.js.map +1 -0
  53. package/dist/chunk-ZB75FESB.cjs +1979 -0
  54. package/dist/chunk-ZB75FESB.cjs.map +1 -0
  55. package/dist/chunk-ZLW62TJG.cjs +4061 -0
  56. package/dist/chunk-ZLW62TJG.cjs.map +1 -0
  57. package/dist/chunk-ZXKBDFP3.js +4235 -0
  58. package/dist/chunk-ZXKBDFP3.js.map +1 -0
  59. package/dist/core/index.cjs +204 -0
  60. package/dist/core/index.cjs.map +1 -0
  61. package/dist/core/index.d.cts +2 -0
  62. package/dist/core/index.d.ts +2 -0
  63. package/dist/core/index.js +3 -0
  64. package/dist/core/index.js.map +1 -0
  65. package/dist/dataframe/index.cjs +22 -0
  66. package/dist/dataframe/index.cjs.map +1 -0
  67. package/dist/dataframe/index.d.cts +3 -0
  68. package/dist/dataframe/index.d.ts +3 -0
  69. package/dist/dataframe/index.js +5 -0
  70. package/dist/dataframe/index.js.map +1 -0
  71. package/dist/datasets/index.cjs +134 -0
  72. package/dist/datasets/index.cjs.map +1 -0
  73. package/dist/datasets/index.d.cts +3 -0
  74. package/dist/datasets/index.d.ts +3 -0
  75. package/dist/datasets/index.js +5 -0
  76. package/dist/datasets/index.js.map +1 -0
  77. package/dist/index-74AB8Cyh.d.cts +1126 -0
  78. package/dist/index-9oQx1HgV.d.cts +1180 -0
  79. package/dist/index-BJY2SI4i.d.ts +483 -0
  80. package/dist/index-BWGhrDlr.d.ts +733 -0
  81. package/dist/index-B_DK4FKY.d.cts +242 -0
  82. package/dist/index-BbA2Gxfl.d.ts +456 -0
  83. package/dist/index-BgHYAoSS.d.cts +837 -0
  84. package/dist/index-BndMbqsM.d.ts +1439 -0
  85. package/dist/index-C1mfVYoo.d.ts +2517 -0
  86. package/dist/index-CCvlwAmL.d.cts +809 -0
  87. package/dist/index-CDw5CnOU.d.ts +785 -0
  88. package/dist/index-Cn3SdB0O.d.ts +1126 -0
  89. package/dist/index-CrqLlS-a.d.ts +776 -0
  90. package/dist/index-D61yaSMY.d.cts +483 -0
  91. package/dist/index-D9Loo1_A.d.cts +2517 -0
  92. package/dist/index-DIT_OO9C.d.cts +785 -0
  93. package/dist/index-DIp_RrRt.d.ts +242 -0
  94. package/dist/index-DbultU6X.d.cts +1427 -0
  95. package/dist/index-DmEg_LCm.d.cts +776 -0
  96. package/dist/index-DoPWVxPo.d.cts +1439 -0
  97. package/dist/index-DuCxd-8d.d.ts +837 -0
  98. package/dist/index-Dx42TZaY.d.ts +809 -0
  99. package/dist/index-DyZ4QQf5.d.cts +456 -0
  100. package/dist/index-GFAVyOWO.d.ts +1427 -0
  101. package/dist/index-WHQLn0e8.d.cts +733 -0
  102. package/dist/index-ZtI1Iy4L.d.ts +1180 -0
  103. package/dist/index-eJgeni9c.d.cts +1911 -0
  104. package/dist/index-tk4lSYod.d.ts +1911 -0
  105. package/dist/index.cjs +72 -0
  106. package/dist/index.cjs.map +1 -0
  107. package/dist/index.d.cts +17 -0
  108. package/dist/index.d.ts +17 -0
  109. package/dist/index.js +15 -0
  110. package/dist/index.js.map +1 -0
  111. package/dist/linalg/index.cjs +86 -0
  112. package/dist/linalg/index.cjs.map +1 -0
  113. package/dist/linalg/index.d.cts +3 -0
  114. package/dist/linalg/index.d.ts +3 -0
  115. package/dist/linalg/index.js +5 -0
  116. package/dist/linalg/index.js.map +1 -0
  117. package/dist/metrics/index.cjs +158 -0
  118. package/dist/metrics/index.cjs.map +1 -0
  119. package/dist/metrics/index.d.cts +3 -0
  120. package/dist/metrics/index.d.ts +3 -0
  121. package/dist/metrics/index.js +5 -0
  122. package/dist/metrics/index.js.map +1 -0
  123. package/dist/ml/index.cjs +87 -0
  124. package/dist/ml/index.cjs.map +1 -0
  125. package/dist/ml/index.d.cts +3 -0
  126. package/dist/ml/index.d.ts +3 -0
  127. package/dist/ml/index.js +6 -0
  128. package/dist/ml/index.js.map +1 -0
  129. package/dist/ndarray/index.cjs +501 -0
  130. package/dist/ndarray/index.cjs.map +1 -0
  131. package/dist/ndarray/index.d.cts +5 -0
  132. package/dist/ndarray/index.d.ts +5 -0
  133. package/dist/ndarray/index.js +4 -0
  134. package/dist/ndarray/index.js.map +1 -0
  135. package/dist/nn/index.cjs +142 -0
  136. package/dist/nn/index.cjs.map +1 -0
  137. package/dist/nn/index.d.cts +6 -0
  138. package/dist/nn/index.d.ts +6 -0
  139. package/dist/nn/index.js +5 -0
  140. package/dist/nn/index.js.map +1 -0
  141. package/dist/optim/index.cjs +77 -0
  142. package/dist/optim/index.cjs.map +1 -0
  143. package/dist/optim/index.d.cts +4 -0
  144. package/dist/optim/index.d.ts +4 -0
  145. package/dist/optim/index.js +4 -0
  146. package/dist/optim/index.js.map +1 -0
  147. package/dist/plot/index.cjs +114 -0
  148. package/dist/plot/index.cjs.map +1 -0
  149. package/dist/plot/index.d.cts +6 -0
  150. package/dist/plot/index.d.ts +6 -0
  151. package/dist/plot/index.js +5 -0
  152. package/dist/plot/index.js.map +1 -0
  153. package/dist/preprocess/index.cjs +82 -0
  154. package/dist/preprocess/index.cjs.map +1 -0
  155. package/dist/preprocess/index.d.cts +4 -0
  156. package/dist/preprocess/index.d.ts +4 -0
  157. package/dist/preprocess/index.js +5 -0
  158. package/dist/preprocess/index.js.map +1 -0
  159. package/dist/random/index.cjs +74 -0
  160. package/dist/random/index.cjs.map +1 -0
  161. package/dist/random/index.d.cts +3 -0
  162. package/dist/random/index.d.ts +3 -0
  163. package/dist/random/index.js +5 -0
  164. package/dist/random/index.js.map +1 -0
  165. package/dist/stats/index.cjs +142 -0
  166. package/dist/stats/index.cjs.map +1 -0
  167. package/dist/stats/index.d.cts +3 -0
  168. package/dist/stats/index.d.ts +3 -0
  169. package/dist/stats/index.js +5 -0
  170. package/dist/stats/index.js.map +1 -0
  171. package/dist/tensor-B96jjJLQ.d.cts +205 -0
  172. package/dist/tensor-B96jjJLQ.d.ts +205 -0
  173. package/package.json +226 -0
@@ -0,0 +1,2925 @@
1
+ import { GradTensor, relu, sigmoid, tanh, leakyRelu, elu, gelu, softmax2, softmax, logSoftmax2, logSoftmax, softplus, swish, mish, dropout, randn, mulScalar, parameter, zeros, reshape, dot, transpose, add, ones, variance2, noGrad, isContiguous, im2col2, Tensor, sub, pow, tensor, sum, mean, abs, clip, log, mul, neg, sqrt, computeStrides, offsetFromFlatIndex } from './chunk-BCR7G3A6.js';
2
+ import { __export, InvalidParameterError, DeepboxError, isDevice, IndexError, normalizeAxis, DTypeError, ShapeError, ensureNumericDType, NotFittedError, getBigIntElement, getNumericElement, dtypeToTypedArrayCtor, getElementAsNumber } from './chunk-4S73VUBD.js';
3
+
4
+ // src/nn/index.ts
5
+ var nn_exports = {};
6
+ __export(nn_exports, {
7
+ AvgPool2d: () => AvgPool2d,
8
+ BatchNorm1d: () => BatchNorm1d,
9
+ Conv1d: () => Conv1d,
10
+ Conv2d: () => Conv2d,
11
+ Dropout: () => Dropout,
12
+ ELU: () => ELU,
13
+ GELU: () => GELU,
14
+ GRU: () => GRU,
15
+ LSTM: () => LSTM,
16
+ LayerNorm: () => LayerNorm,
17
+ LeakyReLU: () => LeakyReLU,
18
+ Linear: () => Linear,
19
+ LogSoftmax: () => LogSoftmax,
20
+ MaxPool2d: () => MaxPool2d,
21
+ Mish: () => Mish,
22
+ Module: () => Module,
23
+ MultiheadAttention: () => MultiheadAttention,
24
+ RNN: () => RNN,
25
+ ReLU: () => ReLU,
26
+ Sequential: () => Sequential,
27
+ Sigmoid: () => Sigmoid,
28
+ Softmax: () => Softmax,
29
+ Softplus: () => Softplus,
30
+ Swish: () => Swish,
31
+ Tanh: () => Tanh,
32
+ TransformerEncoderLayer: () => TransformerEncoderLayer,
33
+ binaryCrossEntropyLoss: () => binaryCrossEntropyLoss,
34
+ binaryCrossEntropyWithLogitsLoss: () => binaryCrossEntropyWithLogitsLoss,
35
+ crossEntropyLoss: () => crossEntropyLoss,
36
+ huberLoss: () => huberLoss,
37
+ maeLoss: () => maeLoss,
38
+ mseLoss: () => mseLoss,
39
+ rmseLoss: () => rmseLoss
40
+ });
41
+
42
+ // src/nn/module/Module.ts
43
+ function shapesEqual(a, b) {
44
+ if (a.length !== b.length) return false;
45
+ for (let i = 0; i < a.length; i++) {
46
+ if ((a[i] ?? 0) !== (b[i] ?? 0)) return false;
47
+ }
48
+ return true;
49
+ }
50
+ function sizeFromShape(shape, context) {
51
+ let size = 1;
52
+ for (const dim of shape) {
53
+ if (!Number.isInteger(dim) || dim < 0) {
54
+ throw new ShapeError(`${context} contains invalid dimension ${String(dim)}`);
55
+ }
56
+ size *= dim;
57
+ }
58
+ return size;
59
+ }
60
+ function cloneTensorData(t) {
61
+ const data = t.data;
62
+ if (Array.isArray(data)) {
63
+ return data.slice();
64
+ }
65
+ if (data instanceof BigInt64Array) {
66
+ return Array.from(data);
67
+ }
68
+ const out = new Array(data.length);
69
+ for (let i = 0; i < data.length; i++) {
70
+ const value = data[i];
71
+ if (value === void 0) {
72
+ throw new DeepboxError("Internal error: tensor data access out of bounds");
73
+ }
74
+ out[i] = value;
75
+ }
76
+ return out;
77
+ }
78
+ function validateStateEntryShape(name, kind, entry) {
79
+ const size = sizeFromShape(entry.shape, `${kind} ${name} shape`);
80
+ if (entry.data.length !== size) {
81
+ throw new ShapeError(
82
+ `${kind} ${name} data length ${entry.data.length} does not match shape size ${size}`
83
+ );
84
+ }
85
+ }
86
+ function copyStateEntryIntoTensor(name, kind, target, entry) {
87
+ if (!shapesEqual(target.shape, entry.shape)) {
88
+ throw new ShapeError(
89
+ `${kind} ${name} shape mismatch: expected [${target.shape.join(", ")}], got [${entry.shape.join(", ")}]`
90
+ );
91
+ }
92
+ if (target.dtype !== entry.dtype) {
93
+ throw new DTypeError(
94
+ `${kind} ${name} dtype mismatch: expected ${target.dtype}, got ${entry.dtype}`
95
+ );
96
+ }
97
+ const size = sizeFromShape(entry.shape, `${kind} ${name} shape`);
98
+ const logicalStrides = computeStrides(target.shape);
99
+ const data = target.data;
100
+ if (target.dtype === "string") {
101
+ if (!Array.isArray(data)) {
102
+ throw new DTypeError(`${kind} ${name} expected string data`);
103
+ }
104
+ for (let i = 0; i < size; i++) {
105
+ const value = entry.data[i];
106
+ if (typeof value !== "string") {
107
+ throw new DTypeError(`${kind} ${name} expects string data`);
108
+ }
109
+ const offset = offsetFromFlatIndex(i, logicalStrides, target.strides, target.offset);
110
+ data[offset] = value;
111
+ }
112
+ return;
113
+ }
114
+ if (data instanceof BigInt64Array) {
115
+ for (let i = 0; i < size; i++) {
116
+ const value = entry.data[i];
117
+ if (typeof value !== "bigint") {
118
+ throw new DTypeError(`${kind} ${name} expects bigint data`);
119
+ }
120
+ const offset = offsetFromFlatIndex(i, logicalStrides, target.strides, target.offset);
121
+ data[offset] = value;
122
+ }
123
+ return;
124
+ }
125
+ if (Array.isArray(data)) {
126
+ throw new DTypeError(`${kind} ${name} expected numeric data`);
127
+ }
128
+ for (let i = 0; i < size; i++) {
129
+ const value = entry.data[i];
130
+ if (typeof value !== "number") {
131
+ throw new DTypeError(`${kind} ${name} expects numeric data`);
132
+ }
133
+ const offset = offsetFromFlatIndex(i, logicalStrides, target.strides, target.offset);
134
+ data[offset] = value;
135
+ }
136
+ }
137
+ var Module = class _Module {
138
+ /** Child modules registered to this module - stores nested layers/modules */
139
+ _modules = /* @__PURE__ */ new Map();
140
+ /** Parameters of this module - trainable tensors (weights, biases) wrapped as GradTensor */
141
+ _parameters = /* @__PURE__ */ new Map();
142
+ /** Buffers (non-trainable tensors) of this module - e.g., running stats in BatchNorm */
143
+ _buffers = /* @__PURE__ */ new Map();
144
+ /** Training mode flag - affects behavior of layers like Dropout and BatchNorm */
145
+ _training = true;
146
+ /** Forward pre-hooks registered on this module */
147
+ _forwardPreHooks = /* @__PURE__ */ new Map();
148
+ /** Forward hooks registered on this module */
149
+ _forwardHooks = /* @__PURE__ */ new Map();
150
+ /** Incrementing hook id */
151
+ _nextHookId = 0;
152
+ /**
153
+ * Makes the module callable (allows using `module(x)` instead of `module.forward(x)`).
154
+ *
155
+ * @param inputs - Input tensors (Tensor or GradTensor)
156
+ * @returns Output tensor
157
+ */
158
+ call(...inputs) {
159
+ let curInputs = inputs;
160
+ for (const hook of this._forwardPreHooks.values()) {
161
+ const result = hook(this, curInputs);
162
+ if (Array.isArray(result)) {
163
+ curInputs = result;
164
+ }
165
+ }
166
+ let output = this.forward(...curInputs);
167
+ for (const hook of this._forwardHooks.values()) {
168
+ const result = hook(this, curInputs, output);
169
+ if (result !== void 0) {
170
+ output = result;
171
+ }
172
+ }
173
+ return output;
174
+ }
175
+ /**
176
+ * Register a child module.
177
+ *
178
+ * @param name - Name of the module
179
+ * @param module - The module to register
180
+ */
181
+ registerModule(name, module) {
182
+ this._modules.set(name, module);
183
+ }
184
+ /**
185
+ * Register a parameter (trainable tensor).
186
+ *
187
+ * Parameters must be GradTensor instances with requiresGrad=true for
188
+ * proper gradient computation during backpropagation.
189
+ *
190
+ * @param name - Name of the parameter
191
+ * @param param - The parameter tensor (must be GradTensor)
192
+ */
193
+ registerParameter(name, param) {
194
+ this._parameters.set(name, param);
195
+ }
196
+ /**
197
+ * Register a buffer (non-trainable tensor).
198
+ *
199
+ * Buffers are typically used for running statistics in batch normalization.
200
+ *
201
+ * @param name - Name of the buffer
202
+ * @param buffer - The buffer tensor
203
+ */
204
+ registerBuffer(name, buffer) {
205
+ this._buffers.set(name, buffer);
206
+ }
207
+ /**
208
+ * Get all parameters of this module and its children.
209
+ *
210
+ * Returns GradTensor instances that are compatible with optimizers.
211
+ * This enables direct usage with optimizer constructors:
212
+ * ```ts
213
+ * const optimizer = new Adam(model.parameters());
214
+ * ```
215
+ *
216
+ * @param recurse - Whether to include parameters of child modules
217
+ * @returns Iterator of GradTensor parameters
218
+ */
219
+ *parameters(recurse = true) {
220
+ for (const param of this._parameters.values()) {
221
+ yield param;
222
+ }
223
+ if (recurse) {
224
+ for (const module of this._modules.values()) {
225
+ yield* module.parameters(true);
226
+ }
227
+ }
228
+ }
229
+ /**
230
+ * Get all named parameters of this module and its children.
231
+ *
232
+ * @param prefix - Prefix for parameter names
233
+ * @param recurse - Whether to include parameters of child modules
234
+ * @returns Iterator of [name, parameter] pairs
235
+ */
236
+ *namedParameters(prefix = "", recurse = true) {
237
+ for (const [name, param] of this._parameters.entries()) {
238
+ const fullName = prefix ? `${prefix}.${name}` : name;
239
+ yield [fullName, param];
240
+ }
241
+ if (recurse) {
242
+ for (const [moduleName, module] of this._modules.entries()) {
243
+ const fullPrefix = prefix ? `${prefix}.${moduleName}` : moduleName;
244
+ yield* module.namedParameters(fullPrefix, true);
245
+ }
246
+ }
247
+ }
248
+ /**
249
+ * Get all child modules.
250
+ *
251
+ * @param recurse - Whether to include nested child modules
252
+ * @returns Iterator of modules
253
+ */
254
+ *modules(recurse = true) {
255
+ yield this;
256
+ if (recurse) {
257
+ for (const module of this._modules.values()) {
258
+ yield* module.modules(true);
259
+ }
260
+ }
261
+ }
262
+ /**
263
+ * Get all named child modules.
264
+ *
265
+ * @param prefix - Prefix for module names
266
+ * @param recurse - Whether to include nested child modules
267
+ * @returns Iterator of [name, module] pairs
268
+ */
269
+ *namedModules(prefix = "", recurse = true) {
270
+ yield [prefix, this];
271
+ if (recurse) {
272
+ for (const [name, module] of this._modules.entries()) {
273
+ const fullName = prefix ? `${prefix}.${name}` : name;
274
+ yield* module.namedModules(fullName, true);
275
+ }
276
+ }
277
+ }
278
+ /**
279
+ * Set the module in training mode.
280
+ *
281
+ * This affects certain layers like Dropout and BatchNorm.
282
+ *
283
+ * @param mode - Training mode (true) or evaluation mode (false)
284
+ * @returns this
285
+ */
286
+ train(mode = true) {
287
+ this._training = mode;
288
+ for (const module of this._modules.values()) {
289
+ module.train(mode);
290
+ }
291
+ return this;
292
+ }
293
+ /**
294
+ * Set the module in evaluation mode.
295
+ *
296
+ * This is equivalent to calling `train(false)`.
297
+ *
298
+ * @returns this
299
+ */
300
+ eval() {
301
+ return this.train(false);
302
+ }
303
+ /**
304
+ * Check if the module is in training mode.
305
+ *
306
+ * @returns true if in training mode
307
+ */
308
+ get training() {
309
+ return this._training;
310
+ }
311
+ /**
312
+ * Zero out the gradients of all parameters.
313
+ *
314
+ * Call this before each training iteration to prevent gradient accumulation
315
+ * from previous iterations.
316
+ *
317
+ * For parameters wrapped in GradTensor, this calls zeroGrad() on each.
318
+ * For regular Tensors, this is a no-op until they are converted to GradTensor.
319
+ *
320
+ * @example
321
+ * ```ts
322
+ * model.zeroGrad();
323
+ * const output = model.forward(input);
324
+ * // ... compute loss and backward
325
+ * optimizer.step();
326
+ * ```
327
+ */
328
+ zeroGrad() {
329
+ for (const param of this.parameters()) {
330
+ param.zeroGrad();
331
+ }
332
+ }
333
+ /**
334
+ * Get all buffers of this module and its children.
335
+ */
336
+ *buffers(recurse = true) {
337
+ for (const buffer of this._buffers.values()) {
338
+ yield buffer;
339
+ }
340
+ if (recurse) {
341
+ for (const module of this._modules.values()) {
342
+ yield* module.buffers(true);
343
+ }
344
+ }
345
+ }
346
+ /**
347
+ * Get all named buffers of this module and its children.
348
+ */
349
+ *namedBuffers(prefix = "", recurse = true) {
350
+ for (const [name, buffer] of this._buffers.entries()) {
351
+ const fullName = prefix ? `${prefix}.${name}` : name;
352
+ yield [fullName, buffer];
353
+ }
354
+ if (recurse) {
355
+ for (const [moduleName, module] of this._modules.entries()) {
356
+ const fullPrefix = prefix ? `${prefix}.${moduleName}` : moduleName;
357
+ yield* module.namedBuffers(fullPrefix, true);
358
+ }
359
+ }
360
+ }
361
+ /**
362
+ * Freeze specific parameters by name (or all if none provided).
363
+ *
364
+ * **⚠️ IMPORTANT**: This method creates new GradTensor instances with updated
365
+ * `requiresGrad` flags. Any external references to the old parameter objects
366
+ * will become stale. If you're using an optimizer that holds parameter references,
367
+ * you should recreate the optimizer after freezing/unfreezing parameters.
368
+ *
369
+ * @param names - Array of parameter names to freeze (e.g., ['fc1.weight']). If undefined, freezes all parameters.
370
+ * @param recurse - Whether to include parameters from child modules (default: true)
371
+ *
372
+ * @example
373
+ * ```ts
374
+ * const model = new MyModel();
375
+ * // Freeze only the first layer's weights
376
+ * model.freezeParameters(['fc1.weight']);
377
+ * // Note: Recreate optimizer after freezing
378
+ * const optimizer = new Adam(model.parameters());
379
+ * ```
380
+ */
381
+ freezeParameters(names, recurse = true) {
382
+ this.setRequiresGradForNames(names, false, recurse);
383
+ }
384
+ /**
385
+ * Unfreeze specific parameters by name (or all if none provided).
386
+ *
387
+ * **⚠️ IMPORTANT**: This method creates new GradTensor instances with updated
388
+ * `requiresGrad` flags. Any external references to the old parameter objects
389
+ * will become stale. If you're using an optimizer that holds parameter references,
390
+ * you should recreate the optimizer after freezing/unfreezing parameters.
391
+ *
392
+ * @param names - Array of parameter names to unfreeze (e.g., ['fc1.weight']). If undefined, unfreezes all parameters.
393
+ * @param recurse - Whether to include parameters from child modules (default: true)
394
+ *
395
+ * @example
396
+ * ```ts
397
+ * const model = new MyModel();
398
+ * model.freezeParameters(); // Freeze all
399
+ * model.unfreezeParameters(['fc2.weight']); // Unfreeze only fc2 weights
400
+ * // Note: Recreate optimizer after unfreezing
401
+ * const optimizer = new Adam(model.parameters());
402
+ * ```
403
+ */
404
+ unfreezeParameters(names, recurse = true) {
405
+ this.setRequiresGradForNames(names, true, recurse);
406
+ }
407
+ setRequiresGradForNames(names, requiresGrad, recurse) {
408
+ const providedNames = names !== void 0;
409
+ const targetNames = names ?? Array.from(this.namedParameters("", recurse)).map(([name]) => name);
410
+ for (const name of targetNames) {
411
+ const resolved = this.resolveModuleAndName(name);
412
+ if (!resolved) {
413
+ if (providedNames) {
414
+ throw new InvalidParameterError(`Unknown parameter name: ${name}`, "names", name);
415
+ }
416
+ continue;
417
+ }
418
+ const { module, localName } = resolved;
419
+ const param = module._parameters.get(localName);
420
+ if (!param) {
421
+ if (providedNames) {
422
+ throw new InvalidParameterError(`Unknown parameter name: ${name}`, "names", name);
423
+ }
424
+ continue;
425
+ }
426
+ const nextParam = GradTensor.fromTensor(param.tensor, { requiresGrad });
427
+ module._parameters.set(localName, nextParam);
428
+ for (const [key, value] of Object.entries(module)) {
429
+ if (value === param) {
430
+ Reflect.set(module, key, nextParam);
431
+ }
432
+ }
433
+ }
434
+ }
435
+ resolveModuleAndName(fullName) {
436
+ const parts = fullName.split(".");
437
+ let module = this;
438
+ for (let i = 0; i < parts.length - 1; i++) {
439
+ const part = parts[i] ?? "";
440
+ const child = module._modules.get(part);
441
+ if (!child) return null;
442
+ module = child;
443
+ }
444
+ const localName = parts[parts.length - 1] ?? "";
445
+ return { module, localName };
446
+ }
447
+ static setTensorDeviceMetadata(target, device) {
448
+ if (!Reflect.set(target, "device", device)) {
449
+ throw new DeepboxError("Failed to update tensor device metadata");
450
+ }
451
+ }
452
+ /**
453
+ * Get the state dictionary of the module.
454
+ */
455
+ stateDict() {
456
+ const parameters = {};
457
+ const buffers = {};
458
+ for (const [name, param] of this.namedParameters()) {
459
+ const t = param.tensor;
460
+ const data = cloneTensorData(t);
461
+ parameters[name] = {
462
+ data,
463
+ shape: [...t.shape],
464
+ dtype: t.dtype
465
+ };
466
+ }
467
+ for (const [name, buffer] of this.namedBuffers()) {
468
+ const data = cloneTensorData(buffer);
469
+ buffers[name] = {
470
+ data,
471
+ shape: [...buffer.shape],
472
+ dtype: buffer.dtype
473
+ };
474
+ }
475
+ return { parameters, buffers };
476
+ }
477
+ /**
478
+ * Load state dictionary into the module.
479
+ */
480
+ loadStateDict(stateDict) {
481
+ const parameters = stateDict.parameters ?? {};
482
+ const buffers = stateDict.buffers ?? {};
483
+ const namedParams = new Map(this.namedParameters());
484
+ const namedBuffs = new Map(this.namedBuffers());
485
+ for (const name of namedParams.keys()) {
486
+ if (!(name in parameters)) {
487
+ throw new InvalidParameterError(`missing parameter: ${name}`, "stateDict.parameters", name);
488
+ }
489
+ }
490
+ for (const name of namedBuffs.keys()) {
491
+ if (!(name in buffers)) {
492
+ throw new InvalidParameterError(`missing buffer: ${name}`, "stateDict.buffers", name);
493
+ }
494
+ }
495
+ for (const name of Object.keys(parameters)) {
496
+ if (!namedParams.has(name)) {
497
+ throw new InvalidParameterError(
498
+ `unexpected parameter: ${name}`,
499
+ "stateDict.parameters",
500
+ name
501
+ );
502
+ }
503
+ }
504
+ for (const name of Object.keys(buffers)) {
505
+ if (!namedBuffs.has(name)) {
506
+ throw new InvalidParameterError(`unexpected buffer: ${name}`, "stateDict.buffers", name);
507
+ }
508
+ }
509
+ for (const [name, entry] of Object.entries(parameters)) {
510
+ const param = namedParams.get(name);
511
+ if (!param) continue;
512
+ validateStateEntryShape(name, "parameter", entry);
513
+ copyStateEntryIntoTensor(name, "parameter", param.tensor, entry);
514
+ }
515
+ for (const [name, entry] of Object.entries(buffers)) {
516
+ const buffer = namedBuffs.get(name);
517
+ if (!buffer) continue;
518
+ validateStateEntryShape(name, "buffer", entry);
519
+ copyStateEntryIntoTensor(name, "buffer", buffer, entry);
520
+ }
521
+ }
522
+ /**
523
+ * Move module to a specific device.
524
+ *
525
+ * **⚠️ WARNING**: This is a metadata-only operation. It updates the device
526
+ * property on parameters and buffers but does NOT actually transfer data
527
+ * between devices. Actual device data transfer requires device-specific
528
+ * memory management which is not yet implemented.
529
+ *
530
+ * This method is provided for API compatibility and future extensibility.
531
+ * Currently, it only updates the `device` metadata field.
532
+ *
533
+ * @param device - Target device identifier (e.g., 'cpu', 'webgpu', 'wasm')
534
+ * @returns this module for method chaining
535
+ *
536
+ * @example
537
+ * ```ts
538
+ * const model = new Linear(10, 5);
539
+ * model.to('webgpu'); // Updates device metadata only
540
+ * ```
541
+ */
542
+ to(device) {
543
+ if (!isDevice(device)) {
544
+ throw new InvalidParameterError("device must be one of: cpu, webgpu, wasm", "device", device);
545
+ }
546
+ for (const param of this.parameters()) {
547
+ _Module.setTensorDeviceMetadata(param.tensor, device);
548
+ }
549
+ for (const buffer of this.buffers()) {
550
+ _Module.setTensorDeviceMetadata(buffer, device);
551
+ }
552
+ return this;
553
+ }
554
+ /**
555
+ * Apply a function to all modules recursively.
556
+ */
557
+ apply(fn) {
558
+ for (const module of this.modules()) {
559
+ fn(module);
560
+ }
561
+ return this;
562
+ }
563
+ /**
564
+ * Register a forward pre-hook.
565
+ */
566
+ registerForwardPreHook(hook) {
567
+ const hookId = this._nextHookId++;
568
+ this._forwardPreHooks.set(hookId, hook);
569
+ return () => {
570
+ this._forwardPreHooks.delete(hookId);
571
+ };
572
+ }
573
+ /**
574
+ * Register a forward hook.
575
+ */
576
+ registerForwardHook(hook) {
577
+ const hookId = this._nextHookId++;
578
+ this._forwardHooks.set(hookId, hook);
579
+ return () => {
580
+ this._forwardHooks.delete(hookId);
581
+ };
582
+ }
583
+ /**
584
+ * Get string representation of the module.
585
+ *
586
+ * @returns Hierarchical string representation showing module structure
587
+ */
588
+ toString() {
589
+ const lines = [`${this.constructor.name}(`];
590
+ for (const [name, module] of this._modules.entries()) {
591
+ const childLines = module.toString().split("\n");
592
+ const moduleStr = childLines.map((line, i) => i === 0 ? line : ` ${line}`).join("\n");
593
+ lines.push(` (${name}): ${moduleStr}`);
594
+ }
595
+ lines.push(")");
596
+ return lines.join("\n");
597
+ }
598
+ };
599
+
600
+ // src/nn/containers/Sequential.ts
601
+ var Sequential = class extends Module {
602
+ /** Array of layers in sequential order */
603
+ layers;
604
+ /**
605
+ * Create a new Sequential container.
606
+ *
607
+ * @param layers - Variable number of Module instances to stack sequentially
608
+ * @throws {InvalidParameterError} If no layers are provided
609
+ * @throws {DeepboxError} If a layer is undefined
610
+ */
611
+ constructor(...layers) {
612
+ super();
613
+ if (layers.length === 0) {
614
+ throw new InvalidParameterError(
615
+ "Sequential requires at least one layer",
616
+ "layers",
617
+ layers.length
618
+ );
619
+ }
620
+ this.layers = layers;
621
+ for (let i = 0; i < layers.length; i++) {
622
+ const layer = layers[i];
623
+ if (!layer) {
624
+ throw new DeepboxError(`Layer at index ${i} is undefined`);
625
+ }
626
+ this.registerModule(String(i), layer);
627
+ }
628
+ }
629
+ /**
630
+ * Forward pass: sequentially apply all layers.
631
+ *
632
+ * The output of each layer becomes the input to the next layer.
633
+ *
634
+ * @param input - Input tensor (Tensor or GradTensor)
635
+ * @returns Output tensor after passing through all layers
636
+ * @throws {InvalidParameterError} If the input count is invalid or a layer returns multiple outputs
637
+ * @throws {DeepboxError} If a layer is undefined
638
+ */
639
+ forward(...inputs) {
640
+ if (inputs.length !== 1) {
641
+ throw new InvalidParameterError(
642
+ "Sequential.forward expects a single input tensor",
643
+ "inputs",
644
+ inputs.length
645
+ );
646
+ }
647
+ const input = inputs[0];
648
+ if (!input) {
649
+ throw new InvalidParameterError(
650
+ "Sequential.forward expects a single input tensor",
651
+ "input",
652
+ input
653
+ );
654
+ }
655
+ let output = input;
656
+ for (let i = 0; i < this.layers.length; i++) {
657
+ const layer = this.layers[i];
658
+ if (!layer) {
659
+ throw new DeepboxError(`Layer at index ${i} is undefined`);
660
+ }
661
+ const result = layer.call(output);
662
+ if (Array.isArray(result)) {
663
+ throw new InvalidParameterError(
664
+ `Sequential does not support layers that return multiple tensors (layer ${i})`,
665
+ "layer",
666
+ i
667
+ );
668
+ }
669
+ output = result;
670
+ }
671
+ return output;
672
+ }
673
+ /**
674
+ * Get a layer by index.
675
+ *
676
+ * @param index - Zero-based index of the layer
677
+ * @returns The layer at the specified index
678
+ * @throws {IndexError} If index is out of bounds
679
+ * @throws {DeepboxError} If a layer is undefined
680
+ */
681
+ getLayer(index) {
682
+ if (index < 0 || index >= this.layers.length) {
683
+ throw new IndexError(`Layer index ${index} out of bounds [0, ${this.layers.length})`, {
684
+ index,
685
+ validRange: [0, this.layers.length - 1]
686
+ });
687
+ }
688
+ const layer = this.layers[index];
689
+ if (!layer) {
690
+ throw new DeepboxError(`Layer at index ${index} is undefined`);
691
+ }
692
+ return layer;
693
+ }
694
+ /**
695
+ * Get the number of layers in the sequential container.
696
+ */
697
+ get length() {
698
+ return this.layers.length;
699
+ }
700
+ /**
701
+ * Get string representation showing all layers.
702
+ *
703
+ * @returns Multi-line string with each layer on a separate line
704
+ */
705
+ toString() {
706
+ const lines = ["Sequential("];
707
+ for (let i = 0; i < this.layers.length; i++) {
708
+ const layer = this.layers[i];
709
+ if (!layer) continue;
710
+ const childLines = layer.toString().split("\n");
711
+ const layerStr = childLines.map((line, idx) => idx === 0 ? line : ` ${line}`).join("\n");
712
+ lines.push(` (${i}): ${layerStr}`);
713
+ }
714
+ lines.push(")");
715
+ return lines.join("\n");
716
+ }
717
+ /**
718
+ * Iterate over all layers.
719
+ *
720
+ * @returns Iterator of layers
721
+ */
722
+ *[Symbol.iterator]() {
723
+ for (const layer of this.layers) {
724
+ yield layer;
725
+ }
726
+ }
727
+ };
728
+
729
+ // src/nn/layers/activations.ts
730
+ var ReLU = class extends Module {
731
+ forward(input) {
732
+ if (input instanceof GradTensor) return input.relu();
733
+ return relu(input);
734
+ }
735
+ toString() {
736
+ return "ReLU()";
737
+ }
738
+ };
739
+ var Sigmoid = class extends Module {
740
+ forward(input) {
741
+ if (input instanceof GradTensor) return input.sigmoid();
742
+ return sigmoid(input);
743
+ }
744
+ toString() {
745
+ return "Sigmoid()";
746
+ }
747
+ };
748
+ var Tanh = class extends Module {
749
+ forward(input) {
750
+ if (input instanceof GradTensor) return input.tanh();
751
+ return tanh(input);
752
+ }
753
+ toString() {
754
+ return "Tanh()";
755
+ }
756
+ };
757
+ var LeakyReLU = class extends Module {
758
+ alpha;
759
+ constructor(alpha = 0.01) {
760
+ super();
761
+ this.alpha = alpha;
762
+ }
763
+ forward(input) {
764
+ if (input instanceof GradTensor) return input.leakyRelu(this.alpha);
765
+ return leakyRelu(input, this.alpha);
766
+ }
767
+ toString() {
768
+ return `LeakyReLU(alpha=${this.alpha})`;
769
+ }
770
+ };
771
+ var ELU = class extends Module {
772
+ alpha;
773
+ constructor(alpha = 1) {
774
+ super();
775
+ this.alpha = alpha;
776
+ }
777
+ forward(input) {
778
+ if (input instanceof GradTensor) return input.elu(this.alpha);
779
+ return elu(input, this.alpha);
780
+ }
781
+ toString() {
782
+ return `ELU(alpha=${this.alpha})`;
783
+ }
784
+ };
785
+ var GELU = class extends Module {
786
+ forward(input) {
787
+ if (input instanceof GradTensor) return input.gelu();
788
+ return gelu(input);
789
+ }
790
+ toString() {
791
+ return "GELU()";
792
+ }
793
+ };
794
+ var Softmax = class extends Module {
795
+ axis;
796
+ constructor(axis = -1) {
797
+ super();
798
+ this.axis = axis;
799
+ }
800
+ forward(input) {
801
+ if (input instanceof GradTensor) {
802
+ return softmax2(input, normalizeAxis(this.axis, input.tensor.ndim));
803
+ }
804
+ return softmax(input, this.axis);
805
+ }
806
+ toString() {
807
+ return `Softmax(axis=${this.axis})`;
808
+ }
809
+ };
810
+ var LogSoftmax = class extends Module {
811
+ axis;
812
+ constructor(axis = -1) {
813
+ super();
814
+ this.axis = axis;
815
+ }
816
+ forward(input) {
817
+ if (input instanceof GradTensor) {
818
+ return logSoftmax2(input, normalizeAxis(this.axis, input.tensor.ndim));
819
+ }
820
+ return logSoftmax(input, this.axis);
821
+ }
822
+ toString() {
823
+ return `LogSoftmax(axis=${this.axis})`;
824
+ }
825
+ };
826
+ var Softplus = class extends Module {
827
+ forward(input) {
828
+ if (input instanceof GradTensor) {
829
+ return GradTensor.fromTensor(softplus(input.tensor), {
830
+ requiresGrad: false
831
+ });
832
+ }
833
+ return softplus(input);
834
+ }
835
+ toString() {
836
+ return "Softplus()";
837
+ }
838
+ };
839
+ var Swish = class extends Module {
840
+ forward(input) {
841
+ if (input instanceof GradTensor) {
842
+ return GradTensor.fromTensor(swish(input.tensor), {
843
+ requiresGrad: false
844
+ });
845
+ }
846
+ return swish(input);
847
+ }
848
+ toString() {
849
+ return "Swish()";
850
+ }
851
+ };
852
+ var Mish = class extends Module {
853
+ forward(input) {
854
+ if (input instanceof GradTensor) {
855
+ return GradTensor.fromTensor(mish(input.tensor), {
856
+ requiresGrad: false
857
+ });
858
+ }
859
+ return mish(input);
860
+ }
861
+ toString() {
862
+ return "Mish()";
863
+ }
864
+ };
865
+
866
+ // src/nn/layers/dropout.ts
867
+ var Dropout = class extends Module {
868
+ /** Probability of an element being zeroed (dropout rate) */
869
+ p;
870
+ /**
871
+ * Create a new Dropout layer.
872
+ *
873
+ * @param p - Probability of an element being zeroed (0 <= p < 1)
874
+ * @throws {InvalidParameterError} If p is not in valid range [0, 1)
875
+ */
876
+ constructor(p = 0.5) {
877
+ super();
878
+ if (!Number.isFinite(p) || p < 0 || p >= 1) {
879
+ throw new InvalidParameterError(`Dropout probability must be in [0, 1), got ${p}`, "p", p);
880
+ }
881
+ this.p = p;
882
+ }
883
+ /**
884
+ * Forward pass: apply dropout during training, identity during evaluation.
885
+ *
886
+ * @param input - Input tensor of any shape (Tensor or GradTensor)
887
+ * @returns Output tensor with same shape as input
888
+ */
889
+ forward(input) {
890
+ const inputTensor = input instanceof GradTensor ? input : GradTensor.fromTensor(input);
891
+ if (inputTensor.dtype === "string") {
892
+ throw new DTypeError("Dropout does not support string dtype");
893
+ }
894
+ return dropout(inputTensor, this.p, this.training);
895
+ }
896
+ /**
897
+ * Get string representation of the layer.
898
+ *
899
+ * @returns String representation with dropout probability
900
+ */
901
+ toString() {
902
+ return `Dropout(p=${this.p})`;
903
+ }
904
+ /**
905
+ * Get the dropout probability.
906
+ */
907
+ get dropoutRate() {
908
+ return this.p;
909
+ }
910
+ };
911
+
912
+ // src/nn/layers/linear.ts
913
+ var Linear = class extends Module {
914
+ /** Weight matrix of shape (out_features, in_features) */
915
+ weight;
916
+ weightParam;
917
+ /** Bias vector of shape (out_features,) */
918
+ bias;
919
+ biasParam;
920
+ /** Number of input features */
921
+ inFeatures;
922
+ /** Number of output features */
923
+ outFeatures;
924
+ /** Whether this layer has a bias */
925
+ useBias;
926
+ /**
927
+ * Create a new Linear layer.
928
+ *
929
+ * @param inFeatures - Size of each input sample
930
+ * @param outFeatures - Size of each output sample
931
+ * @param options - Configuration options
932
+ * @param options.bias - If true, add learnable bias (default: true)
933
+ * @param options.dtype - Data type for weights (default: 'float32')
934
+ * @param options.device - Device to place tensors on (default: 'cpu')
935
+ */
936
+ constructor(inFeatures, outFeatures, options = {}) {
937
+ super();
938
+ if (inFeatures <= 0 || !Number.isInteger(inFeatures)) {
939
+ throw new InvalidParameterError(
940
+ "inFeatures must be a positive integer",
941
+ "inFeatures",
942
+ inFeatures
943
+ );
944
+ }
945
+ if (outFeatures <= 0 || !Number.isInteger(outFeatures)) {
946
+ throw new InvalidParameterError(
947
+ "outFeatures must be a positive integer",
948
+ "outFeatures",
949
+ outFeatures
950
+ );
951
+ }
952
+ this.inFeatures = inFeatures;
953
+ this.outFeatures = outFeatures;
954
+ this.useBias = options.bias ?? true;
955
+ const stdDev = Math.sqrt(2 / inFeatures);
956
+ const weightTensor = randn([outFeatures, inFeatures], {
957
+ dtype: options.dtype ?? "float32",
958
+ device: options.device ?? "cpu"
959
+ });
960
+ const scaledWeight = mulScalar(weightTensor, stdDev);
961
+ this.weightParam = parameter(scaledWeight);
962
+ this.weight = this.weightParam.tensor;
963
+ this.registerParameter("weight", this.weightParam);
964
+ if (this.useBias) {
965
+ const biasTensor = zeros([outFeatures], {
966
+ dtype: options.dtype ?? "float32",
967
+ device: options.device ?? "cpu"
968
+ });
969
+ this.biasParam = parameter(biasTensor);
970
+ this.bias = this.biasParam.tensor;
971
+ this.registerParameter("bias", this.biasParam);
972
+ }
973
+ }
974
+ forward(input) {
975
+ const inputTensor = input instanceof GradTensor ? input.tensor : input;
976
+ if (inputTensor.dtype === "string") {
977
+ throw new DTypeError("Linear layer does not support string dtype");
978
+ }
979
+ if (inputTensor.ndim < 1) {
980
+ throw new ShapeError(`Linear layer expects at least 1D input; got ndim=${inputTensor.ndim}`);
981
+ }
982
+ const inputFeatures = inputTensor.shape[inputTensor.shape.length - 1] ?? 0;
983
+ if (inputFeatures !== this.inFeatures) {
984
+ throw new ShapeError(
985
+ `Linear layer expects ${this.inFeatures} input features; got ${inputFeatures}`
986
+ );
987
+ }
988
+ const isVectorInput = inputTensor.ndim === 1;
989
+ const batchSize = inputTensor.size / this.inFeatures;
990
+ const outputShape = isVectorInput ? [this.outFeatures] : [...inputTensor.shape.slice(0, -1), this.outFeatures];
991
+ if (input instanceof GradTensor) {
992
+ const input2d2 = input.reshape([batchSize, this.inFeatures]);
993
+ const output2d2 = input2d2.matmul(this.weightParam.transpose());
994
+ let output2 = output2d2.reshape(outputShape);
995
+ if (this.useBias && this.biasParam) {
996
+ output2 = output2.add(this.biasParam);
997
+ }
998
+ return output2;
999
+ }
1000
+ const input2d = reshape(inputTensor, [batchSize, this.inFeatures]);
1001
+ const output2d = dot(input2d, transpose(this.weight));
1002
+ const output = reshape(output2d, outputShape);
1003
+ if (this.useBias && this.bias) {
1004
+ return add(output, this.bias);
1005
+ }
1006
+ return output;
1007
+ }
1008
+ /**
1009
+ * Get extra representation string for this layer.
1010
+ *
1011
+ * @returns String representation of layer parameters
1012
+ */
1013
+ toString() {
1014
+ const biasStr = this.useBias ? "bias=true" : "bias=false";
1015
+ return `Linear(in_features=${this.inFeatures}, out_features=${this.outFeatures}, ${biasStr})`;
1016
+ }
1017
+ /**
1018
+ * Get the weight matrix.
1019
+ *
1020
+ * @returns Weight tensor of shape (out_features, in_features)
1021
+ */
1022
+ getWeight() {
1023
+ return this.weight;
1024
+ }
1025
+ /**
1026
+ * Get the bias vector.
1027
+ *
1028
+ * @returns Bias tensor of shape (out_features,) or undefined if no bias
1029
+ */
1030
+ getBias() {
1031
+ return this.bias;
1032
+ }
1033
+ /**
1034
+ * Get the number of input features.
1035
+ */
1036
+ get inputSize() {
1037
+ return this.inFeatures;
1038
+ }
1039
+ /**
1040
+ * Get the number of output features.
1041
+ */
1042
+ get outputSize() {
1043
+ return this.outFeatures;
1044
+ }
1045
+ };
1046
+
1047
+ // src/nn/layers/normalization.ts
1048
+ function toContiguousTensor(t) {
1049
+ if (isContiguous(t.shape, t.strides)) {
1050
+ return t;
1051
+ }
1052
+ if (t.dtype === "string") {
1053
+ throw new DTypeError("Normalization does not support string dtype");
1054
+ }
1055
+ const Ctor = dtypeToTypedArrayCtor(t.dtype);
1056
+ const out = new Ctor(t.size);
1057
+ const logicalStrides = computeStrides(t.shape);
1058
+ const data = t.data;
1059
+ if (Array.isArray(data)) {
1060
+ throw new DTypeError("Normalization does not support string dtype");
1061
+ }
1062
+ if (data instanceof BigInt64Array) {
1063
+ if (!(out instanceof BigInt64Array)) {
1064
+ throw new DTypeError("Expected int64 output buffer for int64 tensor");
1065
+ }
1066
+ for (let i = 0; i < t.size; i++) {
1067
+ const offset = offsetFromFlatIndex(i, logicalStrides, t.strides, t.offset);
1068
+ out[i] = getBigIntElement(data, offset);
1069
+ }
1070
+ } else {
1071
+ if (out instanceof BigInt64Array) {
1072
+ throw new DTypeError("Unexpected int64 output buffer for numeric tensor");
1073
+ }
1074
+ for (let i = 0; i < t.size; i++) {
1075
+ const offset = offsetFromFlatIndex(i, logicalStrides, t.strides, t.offset);
1076
+ out[i] = getNumericElement(data, offset);
1077
+ }
1078
+ }
1079
+ return Tensor.fromTypedArray({
1080
+ data: out,
1081
+ shape: t.shape,
1082
+ dtype: t.dtype,
1083
+ device: t.device
1084
+ });
1085
+ }
1086
+ var BatchNorm1d = class extends Module {
1087
+ numFeatures;
1088
+ eps;
1089
+ momentum;
1090
+ affine;
1091
+ trackRunningStats;
1092
+ gamma;
1093
+ beta;
1094
+ runningMean;
1095
+ runningVar;
1096
+ constructor(numFeatures, options = {}) {
1097
+ super();
1098
+ if (!Number.isFinite(numFeatures) || numFeatures <= 0 || Math.trunc(numFeatures) !== numFeatures) {
1099
+ throw new InvalidParameterError(
1100
+ "numFeatures must be a positive integer",
1101
+ "numFeatures",
1102
+ numFeatures
1103
+ );
1104
+ }
1105
+ this.numFeatures = numFeatures;
1106
+ this.eps = options.eps ?? 1e-5;
1107
+ if (!Number.isFinite(this.eps) || this.eps <= 0) {
1108
+ throw new InvalidParameterError("eps must be a positive number", "eps", this.eps);
1109
+ }
1110
+ this.momentum = options.momentum ?? 0.1;
1111
+ if (!Number.isFinite(this.momentum) || this.momentum < 0 || this.momentum > 1) {
1112
+ throw new InvalidParameterError(
1113
+ "momentum must be in range [0, 1]",
1114
+ "momentum",
1115
+ this.momentum
1116
+ );
1117
+ }
1118
+ this.affine = options.affine ?? true;
1119
+ this.trackRunningStats = options.trackRunningStats ?? true;
1120
+ if (this.affine) {
1121
+ const gamma = ones([numFeatures]);
1122
+ const beta = zeros([numFeatures]);
1123
+ this.gamma = parameter(gamma);
1124
+ this.beta = parameter(beta);
1125
+ this.registerParameter("weight", this.gamma);
1126
+ this.registerParameter("bias", this.beta);
1127
+ }
1128
+ this.runningMean = GradTensor.fromTensor(zeros([numFeatures]), {
1129
+ requiresGrad: false
1130
+ });
1131
+ this.runningVar = GradTensor.fromTensor(ones([numFeatures]), {
1132
+ requiresGrad: false
1133
+ });
1134
+ if (this.trackRunningStats) {
1135
+ this.registerBuffer("running_mean", this.runningMean.tensor);
1136
+ this.registerBuffer("running_var", this.runningVar.tensor);
1137
+ }
1138
+ }
1139
+ forward(x) {
1140
+ const input = x instanceof GradTensor ? x : GradTensor.fromTensor(x);
1141
+ const inputDtype = input.dtype;
1142
+ if (inputDtype === "string") {
1143
+ throw new DTypeError("BatchNorm1d does not support string dtype");
1144
+ }
1145
+ if (input.ndim !== 2 && input.ndim !== 3) {
1146
+ throw new ShapeError(`BatchNorm1d expects 2D or 3D input; got ndim=${input.ndim}`);
1147
+ }
1148
+ const nFeatures = input.shape[1] ?? 0;
1149
+ if (nFeatures !== this.numFeatures) {
1150
+ throw new ShapeError(`Expected ${this.numFeatures} features, got ${nFeatures}`);
1151
+ }
1152
+ const useBatchStats = this.training || !this.trackRunningStats;
1153
+ let mean2;
1154
+ let varVal;
1155
+ let inputReshaped = input;
1156
+ if (input.ndim === 3) {
1157
+ const batch = input.shape[0] ?? 0;
1158
+ const length = input.shape[2] ?? 0;
1159
+ const flat = batch * length;
1160
+ const numericInputDtype = ensureNumericDType(inputDtype, "BatchNorm1d");
1161
+ inputReshaped = input.transpose([0, 2, 1]).mul(GradTensor.scalar(1, { dtype: numericInputDtype })).reshape([flat, nFeatures]);
1162
+ }
1163
+ if (useBatchStats) {
1164
+ if (inputReshaped.shape[0] === 0) {
1165
+ throw new InvalidParameterError(
1166
+ "BatchNorm requires at least one element",
1167
+ "input",
1168
+ input.shape
1169
+ );
1170
+ }
1171
+ mean2 = inputReshaped.mean(0);
1172
+ varVal = variance2(inputReshaped, 0, 0);
1173
+ if (this.trackRunningStats) {
1174
+ noGrad(() => {
1175
+ const n = inputReshaped.shape[0] ?? 0;
1176
+ const unbiasedVar = n > 1 ? variance2(inputReshaped, 0, 1) : variance2(inputReshaped, 0, 0);
1177
+ const m = this.momentum;
1178
+ const statsDtype = this.runningMean.dtype;
1179
+ if (statsDtype === "string") {
1180
+ throw new DTypeError("BatchNorm running statistics must be numeric");
1181
+ }
1182
+ const oneMinusM = GradTensor.scalar(1 - m, { dtype: statsDtype });
1183
+ const mScalar = GradTensor.scalar(m, { dtype: statsDtype });
1184
+ const newMean = this.runningMean.mul(oneMinusM).add(mean2.mul(mScalar));
1185
+ const newVar = this.runningVar.mul(oneMinusM).add(unbiasedVar.mul(mScalar));
1186
+ this.runningMean = GradTensor.fromTensor(newMean.tensor, {
1187
+ requiresGrad: false
1188
+ });
1189
+ this.runningVar = GradTensor.fromTensor(newVar.tensor, {
1190
+ requiresGrad: false
1191
+ });
1192
+ this.registerBuffer("running_mean", this.runningMean.tensor);
1193
+ this.registerBuffer("running_var", this.runningVar.tensor);
1194
+ });
1195
+ }
1196
+ } else {
1197
+ mean2 = this.runningMean;
1198
+ varVal = this.runningVar;
1199
+ }
1200
+ let meanBroadcast = mean2;
1201
+ let varBroadcast = varVal;
1202
+ if (input.ndim === 3) {
1203
+ meanBroadcast = mean2.reshape([1, nFeatures, 1]);
1204
+ varBroadcast = varVal.reshape([1, nFeatures, 1]);
1205
+ } else {
1206
+ meanBroadcast = mean2.reshape([1, nFeatures]);
1207
+ varBroadcast = varVal.reshape([1, nFeatures]);
1208
+ }
1209
+ const epsTensor = GradTensor.scalar(this.eps, { dtype: inputDtype });
1210
+ const denom = varBroadcast.add(epsTensor).sqrt();
1211
+ let out = input.sub(meanBroadcast).div(denom);
1212
+ if (this.affine && this.gamma && this.beta) {
1213
+ let gammaB = this.gamma;
1214
+ let betaB = this.beta;
1215
+ if (input.ndim === 3) {
1216
+ gammaB = this.gamma.reshape([1, nFeatures, 1]);
1217
+ betaB = this.beta.reshape([1, nFeatures, 1]);
1218
+ } else {
1219
+ gammaB = this.gamma.reshape([1, nFeatures]);
1220
+ betaB = this.beta.reshape([1, nFeatures]);
1221
+ }
1222
+ out = out.mul(gammaB).add(betaB);
1223
+ }
1224
+ return out;
1225
+ }
1226
+ toString() {
1227
+ return `BatchNorm1d(${this.numFeatures}, eps=${this.eps}, momentum=${this.momentum}, affine=${this.affine})`;
1228
+ }
1229
+ };
1230
+ var LayerNorm = class extends Module {
1231
+ normalizedShape;
1232
+ eps;
1233
+ elementwiseAffine;
1234
+ gamma;
1235
+ beta;
1236
+ constructor(normalizedShape, options = {}) {
1237
+ super();
1238
+ this.normalizedShape = typeof normalizedShape === "number" ? [normalizedShape] : Array.from(normalizedShape);
1239
+ if (this.normalizedShape.length === 0) {
1240
+ throw new InvalidParameterError(
1241
+ "normalizedShape must contain at least one dimension",
1242
+ "normalizedShape",
1243
+ normalizedShape
1244
+ );
1245
+ }
1246
+ for (const dim of this.normalizedShape) {
1247
+ if (!Number.isFinite(dim) || dim <= 0 || Math.trunc(dim) !== dim) {
1248
+ throw new InvalidParameterError(
1249
+ "All dimensions in normalizedShape must be positive integers",
1250
+ "normalizedShape",
1251
+ normalizedShape
1252
+ );
1253
+ }
1254
+ }
1255
+ this.eps = options.eps ?? 1e-5;
1256
+ if (!Number.isFinite(this.eps) || this.eps <= 0) {
1257
+ throw new InvalidParameterError("eps must be a positive number", "eps", this.eps);
1258
+ }
1259
+ this.elementwiseAffine = options.elementwiseAffine ?? true;
1260
+ if (this.elementwiseAffine) {
1261
+ this.gamma = parameter(ones(this.normalizedShape));
1262
+ this.beta = parameter(zeros(this.normalizedShape));
1263
+ this.registerParameter("weight", this.gamma);
1264
+ this.registerParameter("bias", this.beta);
1265
+ }
1266
+ }
1267
+ forward(x) {
1268
+ const input = x instanceof GradTensor ? x : GradTensor.fromTensor(x);
1269
+ const inputDtype = input.dtype;
1270
+ if (inputDtype === "string") {
1271
+ throw new DTypeError("LayerNorm does not support string dtype");
1272
+ }
1273
+ let workingInput = input;
1274
+ if (!isContiguous(input.tensor.shape, input.tensor.strides)) {
1275
+ const contiguous = toContiguousTensor(input.tensor);
1276
+ workingInput = GradTensor.fromTensor(contiguous, {
1277
+ requiresGrad: input.requiresGrad
1278
+ });
1279
+ }
1280
+ const inputShape = workingInput.shape;
1281
+ const normShape = this.normalizedShape;
1282
+ if (normShape.length > inputShape.length) {
1283
+ throw new ShapeError(`Input shape ${inputShape} too small for normalizedShape ${normShape}`);
1284
+ }
1285
+ const suffixStart = inputShape.length - normShape.length;
1286
+ for (let i = 0; i < normShape.length; i++) {
1287
+ if (inputShape[suffixStart + i] !== normShape[i]) {
1288
+ throw new ShapeError(
1289
+ `Input shape ${inputShape} does not end with normalizedShape ${normShape}`
1290
+ );
1291
+ }
1292
+ }
1293
+ const outerDims = inputShape.slice(0, suffixStart);
1294
+ const normSize = normShape.reduce((a, b) => a * b, 1);
1295
+ const flattenedShape = [...outerDims, normSize];
1296
+ const inputReshaped = workingInput.reshape(flattenedShape);
1297
+ const mean2 = inputReshaped.mean(-1, true);
1298
+ const varVal = variance2(inputReshaped, -1, 0);
1299
+ const varReshaped = varVal.reshape(mean2.shape);
1300
+ const epsTensor = GradTensor.scalar(this.eps, { dtype: inputDtype });
1301
+ const denom = varReshaped.add(epsTensor).sqrt();
1302
+ const normalizedReshaped = inputReshaped.sub(mean2).div(denom);
1303
+ let out = normalizedReshaped.reshape(inputShape);
1304
+ if (this.elementwiseAffine && this.gamma && this.beta) {
1305
+ out = out.mul(this.gamma).add(this.beta);
1306
+ }
1307
+ return out;
1308
+ }
1309
+ toString() {
1310
+ return `LayerNorm(${this.normalizedShape}, eps=${this.eps}, elementwise_affine=${this.elementwiseAffine})`;
1311
+ }
1312
+ };
1313
+
1314
+ // src/nn/layers/attention.ts
1315
+ var MultiheadAttention = class extends Module {
1316
+ /** Embedding dimension */
1317
+ embedDim;
1318
+ /** Number of attention heads */
1319
+ numHeads;
1320
+ /** Dimension of each head */
1321
+ headDim;
1322
+ /** Scaling factor for dot product attention */
1323
+ scale;
1324
+ /** Whether to add bias to projections */
1325
+ useBias;
1326
+ /** Dropout probability applied to attention weights */
1327
+ dropout;
1328
+ /** Query projection weights (embedDim, embedDim) */
1329
+ wQ;
1330
+ bQ;
1331
+ /** Key projection weights (embedDim, embedDim) */
1332
+ wK;
1333
+ bK;
1334
+ /** Value projection weights (embedDim, embedDim) */
1335
+ wV;
1336
+ bV;
1337
+ /** Output projection weights (embedDim, embedDim) */
1338
+ wO;
1339
+ bO;
1340
+ /**
1341
+ * Create a new MultiheadAttention layer.
1342
+ *
1343
+ * @param embedDim - Total dimension of the model (must be divisible by numHeads)
1344
+ * @param numHeads - Number of parallel attention heads
1345
+ * @param options - Configuration options
1346
+ * @param options.bias - Whether to add bias to projections (default: true)
1347
+ * @param options.dropout - Dropout probability applied to attention weights (default: 0.0)
1348
+ */
1349
+ constructor(embedDim, numHeads, options = {}) {
1350
+ super();
1351
+ if (!Number.isInteger(embedDim) || embedDim <= 0) {
1352
+ throw new InvalidParameterError("embedDim must be a positive integer", "embedDim", embedDim);
1353
+ }
1354
+ if (!Number.isInteger(numHeads) || numHeads <= 0) {
1355
+ throw new InvalidParameterError("numHeads must be a positive integer", "numHeads", numHeads);
1356
+ }
1357
+ if (embedDim % numHeads !== 0) {
1358
+ throw new InvalidParameterError(
1359
+ `embedDim (${embedDim}) must be divisible by numHeads (${numHeads})`,
1360
+ "embedDim",
1361
+ embedDim
1362
+ );
1363
+ }
1364
+ const dropout2 = options.dropout ?? 0;
1365
+ if (!Number.isFinite(dropout2) || dropout2 < 0 || dropout2 >= 1) {
1366
+ throw new InvalidParameterError("dropout must be in [0, 1)", "dropout", dropout2);
1367
+ }
1368
+ this.embedDim = embedDim;
1369
+ this.numHeads = numHeads;
1370
+ this.headDim = embedDim / numHeads;
1371
+ this.scale = Math.sqrt(this.headDim);
1372
+ this.useBias = options.bias ?? true;
1373
+ this.dropout = dropout2;
1374
+ const stdDev = Math.sqrt(2 / (embedDim + embedDim));
1375
+ this.wQ = parameter(mulScalar(randn([embedDim, embedDim]), stdDev));
1376
+ this.wK = parameter(mulScalar(randn([embedDim, embedDim]), stdDev));
1377
+ this.wV = parameter(mulScalar(randn([embedDim, embedDim]), stdDev));
1378
+ this.wO = parameter(mulScalar(randn([embedDim, embedDim]), stdDev));
1379
+ this.registerParameter("in_proj_weight_q", this.wQ);
1380
+ this.registerParameter("in_proj_weight_k", this.wK);
1381
+ this.registerParameter("in_proj_weight_v", this.wV);
1382
+ this.registerParameter("out_proj_weight", this.wO);
1383
+ if (this.useBias) {
1384
+ this.bQ = parameter(zeros([embedDim]));
1385
+ this.bK = parameter(zeros([embedDim]));
1386
+ this.bV = parameter(zeros([embedDim]));
1387
+ this.bO = parameter(zeros([embedDim]));
1388
+ this.registerParameter("in_proj_bias_q", this.bQ);
1389
+ this.registerParameter("in_proj_bias_k", this.bK);
1390
+ this.registerParameter("in_proj_bias_v", this.bV);
1391
+ this.registerParameter("out_proj_bias", this.bO);
1392
+ }
1393
+ }
1394
+ /**
1395
+ * Forward pass of multi-head attention.
1396
+ *
1397
+ * @param query - Query tensor of shape (batch, seqLen, embedDim)
1398
+ * @param key - Key tensor of shape (batch, seqLen, embedDim)
1399
+ * @param value - Value tensor of shape (batch, seqLen, embedDim)
1400
+ * @returns Output tensor of same shape as query
1401
+ */
1402
+ forward(...inputs) {
1403
+ if (inputs.length < 1 || inputs.length > 3) {
1404
+ throw new InvalidParameterError(
1405
+ "MultiheadAttention.forward expects 1 to 3 input tensors",
1406
+ "inputs",
1407
+ inputs.length
1408
+ );
1409
+ }
1410
+ const queryInput = inputs[0];
1411
+ if (queryInput === void 0) {
1412
+ throw new InvalidParameterError("Query tensor is required", "query", queryInput);
1413
+ }
1414
+ const query = queryInput instanceof GradTensor ? queryInput : GradTensor.fromTensor(queryInput);
1415
+ const keyInput = inputs[1] ?? queryInput;
1416
+ const key = keyInput instanceof GradTensor ? keyInput : GradTensor.fromTensor(keyInput);
1417
+ const valueInput = inputs[2] ?? queryInput;
1418
+ const value = valueInput instanceof GradTensor ? valueInput : GradTensor.fromTensor(valueInput);
1419
+ if (query.dtype === "string") throw new DTypeError("String tensors are not supported");
1420
+ if (query.ndim !== key.ndim || query.ndim !== value.ndim) {
1421
+ throw new ShapeError("query, key, and value must have same rank");
1422
+ }
1423
+ if (query.ndim !== 2 && query.ndim !== 3) {
1424
+ throw new ShapeError(`Query must be 2D or 3D; got ndim=${query.ndim}`);
1425
+ }
1426
+ if (key.ndim !== 2 && key.ndim !== 3) {
1427
+ throw new ShapeError(`Key must be 2D or 3D; got ndim=${key.ndim}`);
1428
+ }
1429
+ if (value.ndim !== 2 && value.ndim !== 3) {
1430
+ throw new ShapeError(`Value must be 2D or 3D; got ndim=${value.ndim}`);
1431
+ }
1432
+ let q = query;
1433
+ let k = key;
1434
+ let v = value;
1435
+ if (q.ndim === 2) q = q.reshape([1, q.shape[0] ?? 0, q.shape[1] ?? 0]);
1436
+ if (k.ndim === 2) k = k.reshape([1, k.shape[0] ?? 0, k.shape[1] ?? 0]);
1437
+ if (v.ndim === 2) v = v.reshape([1, v.shape[0] ?? 0, v.shape[1] ?? 0]);
1438
+ const batchSize = q.shape[0] ?? 0;
1439
+ const seqLenQ = q.shape[1] ?? 0;
1440
+ const seqLenK = k.shape[1] ?? 0;
1441
+ const seqLenV = v.shape[1] ?? 0;
1442
+ const embedDim = q.shape[2] ?? 0;
1443
+ if (embedDim !== this.embedDim) {
1444
+ throw new ShapeError(`Query embedDim mismatch: expected ${this.embedDim}, got ${embedDim}`);
1445
+ }
1446
+ if (k.shape[2] !== this.embedDim) {
1447
+ throw new ShapeError(`Key embedDim mismatch: expected ${this.embedDim}, got ${k.shape[2]}`);
1448
+ }
1449
+ if (v.shape[2] !== this.embedDim) {
1450
+ throw new ShapeError(`Value embedDim mismatch: expected ${this.embedDim}, got ${v.shape[2]}`);
1451
+ }
1452
+ if (k.shape[0] !== batchSize || v.shape[0] !== batchSize) {
1453
+ throw new ShapeError(
1454
+ `batch size mismatch: query=${batchSize}, key=${k.shape[0]}, value=${v.shape[0]}`
1455
+ );
1456
+ }
1457
+ if (seqLenK !== seqLenV) {
1458
+ throw new ShapeError(`Key/value sequence length mismatch: key=${seqLenK}, value=${seqLenV}`);
1459
+ }
1460
+ let Q = q.matmul(this.wQ.transpose());
1461
+ if (this.bQ) Q = Q.add(this.bQ);
1462
+ let K = k.matmul(this.wK.transpose());
1463
+ if (this.bK) K = K.add(this.bK);
1464
+ let V = v.matmul(this.wV.transpose());
1465
+ if (this.bV) V = V.add(this.bV);
1466
+ const H = this.numHeads;
1467
+ const D = this.headDim;
1468
+ Q = Q.reshape([batchSize, seqLenQ, H, D]).transpose([0, 2, 1, 3]);
1469
+ K = K.reshape([batchSize, seqLenK, H, D]).transpose([0, 2, 1, 3]);
1470
+ V = V.reshape([batchSize, seqLenV, H, D]).transpose([0, 2, 1, 3]);
1471
+ let scores = Q.matmul(K.transpose([0, 1, 3, 2]));
1472
+ scores = scores.div(GradTensor.scalar(this.scale));
1473
+ let attn = softmax2(scores, -1);
1474
+ attn = dropout(attn, this.dropout, this.training);
1475
+ const context = attn.matmul(V);
1476
+ const contextDtype = ensureNumericDType(context.dtype, "MultiheadAttention");
1477
+ const contextReshaped = context.transpose([0, 2, 1, 3]).mul(GradTensor.scalar(1, { dtype: contextDtype })).reshape([batchSize, seqLenQ, this.embedDim]);
1478
+ let output = contextReshaped.matmul(this.wO.transpose());
1479
+ if (this.bO) output = output.add(this.bO);
1480
+ if (query.ndim === 2) {
1481
+ output = output.reshape([seqLenQ, this.embedDim]);
1482
+ }
1483
+ return output;
1484
+ }
1485
+ toString() {
1486
+ return `MultiheadAttention(embed_dim=${this.embedDim}, num_heads=${this.numHeads})`;
1487
+ }
1488
+ };
1489
+ var TransformerEncoderLayer = class extends Module {
1490
+ dModel;
1491
+ nHead;
1492
+ dFF;
1493
+ selfAttn;
1494
+ linear1;
1495
+ linear2;
1496
+ norm1;
1497
+ norm2;
1498
+ dropout;
1499
+ // We use functional dropout in forward, or could use Dropout module.
1500
+ // Using Dropout module is cleaner.
1501
+ dropout1;
1502
+ dropout2;
1503
+ dropout3;
1504
+ constructor(dModel, nHead, dFF, options = {}) {
1505
+ super();
1506
+ if (!Number.isInteger(dModel) || dModel <= 0) {
1507
+ throw new InvalidParameterError("dModel must be a positive integer", "dModel", dModel);
1508
+ }
1509
+ if (!Number.isInteger(nHead) || nHead <= 0) {
1510
+ throw new InvalidParameterError("nHead must be a positive integer", "nHead", nHead);
1511
+ }
1512
+ if (dModel % nHead !== 0) {
1513
+ throw new InvalidParameterError(
1514
+ `dModel (${dModel}) must be divisible by nHead (${nHead})`,
1515
+ "dModel",
1516
+ dModel
1517
+ );
1518
+ }
1519
+ if (!Number.isInteger(dFF) || dFF <= 0) {
1520
+ throw new InvalidParameterError("dFF must be a positive integer", "dFF", dFF);
1521
+ }
1522
+ const dropout2 = options.dropout ?? 0.1;
1523
+ const eps = options.eps ?? 1e-5;
1524
+ this.dModel = dModel;
1525
+ this.nHead = nHead;
1526
+ this.dFF = dFF;
1527
+ this.dropout = dropout2;
1528
+ this.selfAttn = new MultiheadAttention(dModel, nHead, { dropout: dropout2 });
1529
+ this.linear1 = new Linear(dModel, dFF);
1530
+ this.linear2 = new Linear(dFF, dModel);
1531
+ this.norm1 = new LayerNorm(dModel, { eps });
1532
+ this.norm2 = new LayerNorm(dModel, { eps });
1533
+ this.dropout1 = new Dropout(dropout2);
1534
+ this.dropout2 = new Dropout(dropout2);
1535
+ this.dropout3 = new Dropout(dropout2);
1536
+ this.registerModule("self_attn", this.selfAttn);
1537
+ this.registerModule("linear1", this.linear1);
1538
+ this.registerModule("linear2", this.linear2);
1539
+ this.registerModule("norm1", this.norm1);
1540
+ this.registerModule("norm2", this.norm2);
1541
+ this.registerModule("dropout1", this.dropout1);
1542
+ this.registerModule("dropout2", this.dropout2);
1543
+ this.registerModule("dropout3", this.dropout3);
1544
+ }
1545
+ /**
1546
+ * Forward pass of the Transformer encoder layer.
1547
+ *
1548
+ * @param src - Source sequence of shape (batch, seqLen, dModel)
1549
+ * @returns Output of same shape as input
1550
+ */
1551
+ forward(src) {
1552
+ const input = src instanceof GradTensor ? src : GradTensor.fromTensor(src);
1553
+ if (input.dtype === "string") {
1554
+ throw new DTypeError("TransformerEncoderLayer does not support string dtype");
1555
+ }
1556
+ let src2 = this.selfAttn.forward(input, input, input);
1557
+ src2 = this.dropout1.forward(src2);
1558
+ let out = input.add(src2);
1559
+ out = this.norm1.forward(out);
1560
+ let ffn = this.linear1.forward(out);
1561
+ ffn = ffn.relu();
1562
+ ffn = this.dropout2.forward(ffn);
1563
+ ffn = this.linear2.forward(ffn);
1564
+ ffn = this.dropout3.forward(ffn);
1565
+ out = out.add(ffn);
1566
+ out = this.norm2.forward(out);
1567
+ return out;
1568
+ }
1569
+ toString() {
1570
+ return `TransformerEncoderLayer(d_model=${this.dModel}, nhead=${this.nHead}, dim_feedforward=${this.dFF}, dropout=${this.dropout})`;
1571
+ }
1572
+ };
1573
+
1574
+ // src/nn/layers/conv.ts
1575
+ function normalizePair(name, value, allowZero, description) {
1576
+ const arr = typeof value === "number" ? [value, value] : value;
1577
+ const first = arr[0];
1578
+ const second = arr[1];
1579
+ if (arr.length !== 2 || first === void 0 || second === void 0 || !Number.isInteger(first) || !Number.isInteger(second) || (allowZero ? first < 0 || second < 0 : first <= 0 || second <= 0)) {
1580
+ throw new InvalidParameterError(`${name} must be ${description}`, name, value);
1581
+ }
1582
+ return [first, second];
1583
+ }
1584
+ var Conv1d = class extends Module {
1585
+ inChannels;
1586
+ outChannels;
1587
+ kernelSize;
1588
+ stride;
1589
+ padding;
1590
+ bias;
1591
+ weight_;
1592
+ bias_;
1593
+ constructor(inChannels, outChannels, kernelSize, options = {}) {
1594
+ super();
1595
+ if (inChannels <= 0 || !Number.isInteger(inChannels)) {
1596
+ throw new InvalidParameterError(
1597
+ "inChannels must be a positive integer",
1598
+ "inChannels",
1599
+ inChannels
1600
+ );
1601
+ }
1602
+ if (outChannels <= 0 || !Number.isInteger(outChannels)) {
1603
+ throw new InvalidParameterError(
1604
+ "outChannels must be a positive integer",
1605
+ "outChannels",
1606
+ outChannels
1607
+ );
1608
+ }
1609
+ if (kernelSize <= 0 || !Number.isInteger(kernelSize)) {
1610
+ throw new InvalidParameterError(
1611
+ "kernelSize must be a positive integer",
1612
+ "kernelSize",
1613
+ kernelSize
1614
+ );
1615
+ }
1616
+ const stride = options.stride ?? 1;
1617
+ if (stride <= 0 || !Number.isInteger(stride)) {
1618
+ throw new InvalidParameterError("stride must be a positive integer", "stride", stride);
1619
+ }
1620
+ const padding = options.padding ?? 0;
1621
+ if (padding < 0 || !Number.isInteger(padding)) {
1622
+ throw new InvalidParameterError("padding must be a non-negative integer", "padding", padding);
1623
+ }
1624
+ this.inChannels = inChannels;
1625
+ this.outChannels = outChannels;
1626
+ this.kernelSize = kernelSize;
1627
+ this.stride = stride;
1628
+ this.padding = padding;
1629
+ this.bias = options.bias ?? true;
1630
+ this.initializeParameters();
1631
+ }
1632
+ initializeParameters() {
1633
+ const k = 1 / Math.sqrt(this.inChannels * this.kernelSize);
1634
+ const weight = randn([this.outChannels, this.inChannels, this.kernelSize]);
1635
+ this.weight_ = parameter(mulScalar(weight, k));
1636
+ this.registerParameter("weight", this.weight_);
1637
+ if (this.bias) {
1638
+ const biasInit = randn([this.outChannels]);
1639
+ this.bias_ = parameter(mulScalar(biasInit, k));
1640
+ this.registerParameter("bias", this.bias_);
1641
+ }
1642
+ }
1643
+ forward(x) {
1644
+ const input = x instanceof GradTensor ? x : GradTensor.fromTensor(x);
1645
+ if (input.dtype === "string") {
1646
+ throw new DTypeError("String tensors are not supported");
1647
+ }
1648
+ if (input.ndim !== 3) {
1649
+ throw new ShapeError(`Conv1d expects 3D input (batch, channels, length), got ${input.ndim}D`);
1650
+ }
1651
+ const batch = input.shape[0] ?? 0;
1652
+ const inC = input.shape[1] ?? 0;
1653
+ const inL = input.shape[2] ?? 0;
1654
+ if (inC !== this.inChannels) {
1655
+ throw new ShapeError(`Expected ${this.inChannels} input channels, got ${inC}`);
1656
+ }
1657
+ const weight = this.weight_;
1658
+ if (!weight) throw new NotFittedError("Weight not initialized");
1659
+ const input2d = input.reshape([batch, inC, 1, inL]);
1660
+ const kernelSize = [1, this.kernelSize];
1661
+ const stride = [1, this.stride];
1662
+ const padding = [0, this.padding];
1663
+ const cols = im2col2(input2d, kernelSize, stride, padding);
1664
+ const weightFlat = weight.reshape([this.outChannels, this.inChannels * this.kernelSize]);
1665
+ const out = cols.matmul(weightFlat.transpose());
1666
+ const outTransposed = out.transpose([0, 2, 1]);
1667
+ if (this.bias && this.bias_) {
1668
+ const biasReshaped = this.bias_.reshape([1, this.outChannels, 1]);
1669
+ return outTransposed.add(biasReshaped);
1670
+ }
1671
+ return outTransposed;
1672
+ }
1673
+ get weight() {
1674
+ if (!this.weight_) {
1675
+ throw new NotFittedError("Weight not initialized");
1676
+ }
1677
+ return this.weight_;
1678
+ }
1679
+ };
1680
+ var Conv2d = class extends Module {
1681
+ inChannels;
1682
+ outChannels;
1683
+ kernelSize;
1684
+ stride;
1685
+ padding;
1686
+ useBias;
1687
+ weight_;
1688
+ bias_;
1689
+ constructor(inChannels, outChannels, kernelSize, options = {}) {
1690
+ super();
1691
+ if (inChannels <= 0 || !Number.isInteger(inChannels)) {
1692
+ throw new InvalidParameterError(
1693
+ "inChannels must be a positive integer",
1694
+ "inChannels",
1695
+ inChannels
1696
+ );
1697
+ }
1698
+ if (outChannels <= 0 || !Number.isInteger(outChannels)) {
1699
+ throw new InvalidParameterError(
1700
+ "outChannels must be a positive integer",
1701
+ "outChannels",
1702
+ outChannels
1703
+ );
1704
+ }
1705
+ const kernelArr = normalizePair(
1706
+ "kernelSize",
1707
+ kernelSize,
1708
+ false,
1709
+ "a positive integer or a tuple of two positive integers"
1710
+ );
1711
+ const stride = options.stride ?? 1;
1712
+ const strideArr = normalizePair(
1713
+ "stride",
1714
+ stride,
1715
+ false,
1716
+ "a positive integer or a tuple of two positive integers"
1717
+ );
1718
+ const padding = options.padding ?? 0;
1719
+ const paddingArr = normalizePair(
1720
+ "padding",
1721
+ padding,
1722
+ true,
1723
+ "a non-negative integer or a tuple of two non-negative integers"
1724
+ );
1725
+ this.inChannels = inChannels;
1726
+ this.outChannels = outChannels;
1727
+ this.kernelSize = kernelArr;
1728
+ this.stride = strideArr;
1729
+ this.padding = paddingArr;
1730
+ this.useBias = options.bias ?? true;
1731
+ this.initializeParameters();
1732
+ }
1733
+ initializeParameters() {
1734
+ const kH = this.kernelSize[0] ?? 1;
1735
+ const kW = this.kernelSize[1] ?? 1;
1736
+ const k = 1 / Math.sqrt(this.inChannels * kH * kW);
1737
+ const weight = randn([this.outChannels, this.inChannels, kH, kW]);
1738
+ this.weight_ = parameter(mulScalar(weight, k));
1739
+ this.registerParameter("weight", this.weight_);
1740
+ if (this.useBias) {
1741
+ const biasInit = randn([this.outChannels]);
1742
+ this.bias_ = parameter(mulScalar(biasInit, k));
1743
+ this.registerParameter("bias", this.bias_);
1744
+ }
1745
+ }
1746
+ forward(x) {
1747
+ const input = x instanceof GradTensor ? x : GradTensor.fromTensor(x);
1748
+ if (input.dtype === "string") {
1749
+ throw new DTypeError("String tensors are not supported");
1750
+ }
1751
+ if (input.ndim !== 4) {
1752
+ throw new ShapeError(
1753
+ `Conv2d expects 4D input (batch, channels, height, width), got ${input.ndim}D`
1754
+ );
1755
+ }
1756
+ const batch = input.shape[0] ?? 0;
1757
+ const inC = input.shape[1] ?? 0;
1758
+ const inH = input.shape[2] ?? 0;
1759
+ const inW = input.shape[3] ?? 0;
1760
+ if (inC !== this.inChannels) {
1761
+ throw new ShapeError(`Expected ${this.inChannels} input channels, got ${inC}`);
1762
+ }
1763
+ const weight = this.weight_;
1764
+ if (!weight) throw new NotFittedError("Weight not initialized");
1765
+ const [kH, kW] = this.kernelSize;
1766
+ const [sH, sW] = this.stride;
1767
+ const [pH, pW] = this.padding;
1768
+ const cols = im2col2(input, [kH, kW], [sH, sW], [pH, pW]);
1769
+ const outH = Math.floor((inH + 2 * pH - kH) / sH) + 1;
1770
+ const outW = Math.floor((inW + 2 * pW - kW) / sW) + 1;
1771
+ const weightFlat = weight.reshape([this.outChannels, this.inChannels * kH * kW]);
1772
+ const out = cols.matmul(weightFlat.transpose());
1773
+ const outTransposed = out.transpose([0, 2, 1]);
1774
+ const outReshaped = outTransposed.reshape([batch, this.outChannels, outH, outW]);
1775
+ if (this.useBias && this.bias_) {
1776
+ const biasReshaped = this.bias_.reshape([1, this.outChannels, 1, 1]);
1777
+ return outReshaped.add(biasReshaped);
1778
+ }
1779
+ return outReshaped;
1780
+ }
1781
+ get weight() {
1782
+ if (!this.weight_) {
1783
+ throw new NotFittedError("Weight not initialized");
1784
+ }
1785
+ return this.weight_;
1786
+ }
1787
+ };
1788
+ var MaxPool2d = class extends Module {
1789
+ kernelSizeValue;
1790
+ stride;
1791
+ padding;
1792
+ constructor(kernelSize, options = {}) {
1793
+ super();
1794
+ const kernelArr = normalizePair(
1795
+ "kernelSize",
1796
+ kernelSize,
1797
+ false,
1798
+ "a positive integer or a tuple of two positive integers"
1799
+ );
1800
+ this.kernelSizeValue = kernelArr;
1801
+ const strideArr = normalizePair(
1802
+ "stride",
1803
+ options.stride ?? kernelSize,
1804
+ false,
1805
+ "a positive integer or a tuple of two positive integers"
1806
+ );
1807
+ this.stride = strideArr;
1808
+ const paddingArr = normalizePair(
1809
+ "padding",
1810
+ options.padding ?? 0,
1811
+ true,
1812
+ "a non-negative integer or a tuple of two non-negative integers"
1813
+ );
1814
+ this.padding = paddingArr;
1815
+ }
1816
+ forward(x) {
1817
+ const input = x instanceof GradTensor ? x : GradTensor.fromTensor(x);
1818
+ if (input.dtype === "string") {
1819
+ throw new DTypeError("String tensors are not supported");
1820
+ }
1821
+ if (input.ndim !== 4) {
1822
+ throw new ShapeError(
1823
+ `MaxPool2d expects 4D input (batch, channels, height, width), got ${input.ndim}D`
1824
+ );
1825
+ }
1826
+ const batch = input.shape[0] ?? 0;
1827
+ const channels = input.shape[1] ?? 0;
1828
+ const inH = input.shape[2] ?? 0;
1829
+ const inW = input.shape[3] ?? 0;
1830
+ const [kH, kW] = this.kernelSizeValue;
1831
+ const [sH, sW] = this.stride;
1832
+ const [pH, pW] = this.padding;
1833
+ const inputReshaped = input.reshape([batch * channels, 1, inH, inW]);
1834
+ const cols = im2col2(inputReshaped, [kH, kW], [sH, sW], [pH, pW]);
1835
+ const maxVals = cols.max(2);
1836
+ const outH = Math.floor((inH + 2 * pH - kH) / sH) + 1;
1837
+ const outW = Math.floor((inW + 2 * pW - kW) / sW) + 1;
1838
+ return maxVals.reshape([batch, channels, outH, outW]);
1839
+ }
1840
+ };
1841
+ var AvgPool2d = class extends Module {
1842
+ kernelSizeValue;
1843
+ stride;
1844
+ padding;
1845
+ constructor(kernelSize, options = {}) {
1846
+ super();
1847
+ const kernelArr = normalizePair(
1848
+ "kernelSize",
1849
+ kernelSize,
1850
+ false,
1851
+ "a positive integer or a tuple of two positive integers"
1852
+ );
1853
+ this.kernelSizeValue = kernelArr;
1854
+ const strideArr = normalizePair(
1855
+ "stride",
1856
+ options.stride ?? kernelSize,
1857
+ false,
1858
+ "a positive integer or a tuple of two positive integers"
1859
+ );
1860
+ this.stride = strideArr;
1861
+ const paddingArr = normalizePair(
1862
+ "padding",
1863
+ options.padding ?? 0,
1864
+ true,
1865
+ "a non-negative integer or a tuple of two non-negative integers"
1866
+ );
1867
+ this.padding = paddingArr;
1868
+ }
1869
+ forward(x) {
1870
+ const input = x instanceof GradTensor ? x : GradTensor.fromTensor(x);
1871
+ if (input.dtype === "string") {
1872
+ throw new DTypeError("String tensors are not supported");
1873
+ }
1874
+ if (input.ndim !== 4) {
1875
+ throw new ShapeError(
1876
+ `AvgPool2d expects 4D input (batch, channels, height, width), got ${input.ndim}D`
1877
+ );
1878
+ }
1879
+ const batch = input.shape[0] ?? 0;
1880
+ const channels = input.shape[1] ?? 0;
1881
+ const inH = input.shape[2] ?? 0;
1882
+ const inW = input.shape[3] ?? 0;
1883
+ const [kH, kW] = this.kernelSizeValue;
1884
+ const [sH, sW] = this.stride;
1885
+ const [pH, pW] = this.padding;
1886
+ const inputReshaped = input.reshape([batch * channels, 1, inH, inW]);
1887
+ const cols = im2col2(inputReshaped, [kH, kW], [sH, sW], [pH, pW]);
1888
+ const meanVals = cols.mean(2);
1889
+ const outH = Math.floor((inH + 2 * pH - kH) / sH) + 1;
1890
+ const outW = Math.floor((inW + 2 * pW - kW) / sW) + 1;
1891
+ return meanVals.reshape([batch, channels, outH, outW]);
1892
+ }
1893
+ };
1894
+
1895
+ // src/nn/layers/recurrent.ts
1896
+ function ensureFloatTensor(t, context) {
1897
+ if (t.dtype === "string") {
1898
+ throw new DTypeError(`${context} does not support string dtype`);
1899
+ }
1900
+ if (t.dtype !== "float32" && t.dtype !== "float64") {
1901
+ throw new DTypeError(`${context} expects float32 or float64 dtype`);
1902
+ }
1903
+ }
1904
+ function readNumeric(t, offset) {
1905
+ const data = t.data;
1906
+ if (Array.isArray(data)) {
1907
+ throw new DTypeError("String tensors are not supported");
1908
+ }
1909
+ return getElementAsNumber(data, offset);
1910
+ }
1911
+ function createFloatBuffer(size, dtype) {
1912
+ return dtype === "float64" ? new Float64Array(size) : new Float32Array(size);
1913
+ }
1914
+ function validatePositiveInt(name, value) {
1915
+ if (!Number.isInteger(value) || value <= 0) {
1916
+ throw new InvalidParameterError(`${name} must be a positive integer`, name, value);
1917
+ }
1918
+ }
1919
+ function parseInput(input, batchFirst) {
1920
+ if (input.ndim === 2) {
1921
+ const seqLen = input.shape[0] ?? 0;
1922
+ const inputDim = input.shape[1] ?? 0;
1923
+ return {
1924
+ batch: 1,
1925
+ seqLen,
1926
+ inputDim,
1927
+ isUnbatched: true,
1928
+ batchStride: 0,
1929
+ seqStride: input.strides[0] ?? 0,
1930
+ featStride: input.strides[1] ?? 0
1931
+ };
1932
+ }
1933
+ if (input.ndim !== 3) {
1934
+ throw new ShapeError(`Recurrent layers expect 2D or 3D input; got ndim=${input.ndim}`);
1935
+ }
1936
+ if (batchFirst) {
1937
+ return {
1938
+ batch: input.shape[0] ?? 0,
1939
+ seqLen: input.shape[1] ?? 0,
1940
+ inputDim: input.shape[2] ?? 0,
1941
+ isUnbatched: false,
1942
+ batchStride: input.strides[0] ?? 0,
1943
+ seqStride: input.strides[1] ?? 0,
1944
+ featStride: input.strides[2] ?? 0
1945
+ };
1946
+ }
1947
+ return {
1948
+ batch: input.shape[1] ?? 0,
1949
+ seqLen: input.shape[0] ?? 0,
1950
+ inputDim: input.shape[2] ?? 0,
1951
+ isUnbatched: false,
1952
+ batchStride: input.strides[1] ?? 0,
1953
+ seqStride: input.strides[0] ?? 0,
1954
+ featStride: input.strides[2] ?? 0
1955
+ };
1956
+ }
1957
+ function outputIndex(batchFirst, isUnbatched, batch, seqLen, hiddenSize, b, t, j) {
1958
+ if (isUnbatched) {
1959
+ return t * hiddenSize + j;
1960
+ }
1961
+ if (batchFirst) {
1962
+ return b * (seqLen * hiddenSize) + t * hiddenSize + j;
1963
+ }
1964
+ return t * (batch * hiddenSize) + b * hiddenSize + j;
1965
+ }
1966
+ function extractTensor(arg, _name) {
1967
+ if (arg instanceof GradTensor) {
1968
+ return arg.tensor;
1969
+ }
1970
+ return arg;
1971
+ }
1972
+ function buildState(state, numLayers, batch, hiddenSize, isUnbatched, name) {
1973
+ const result = new Array(numLayers);
1974
+ for (let l = 0; l < numLayers; l++) {
1975
+ result[l] = new Float64Array(batch * hiddenSize);
1976
+ }
1977
+ if (!state) {
1978
+ return result;
1979
+ }
1980
+ ensureFloatTensor(state, name);
1981
+ if (state.ndim === 2) {
1982
+ if (!isUnbatched) {
1983
+ throw new ShapeError(`Expected ${name} with 3 dimensions for batched input`);
1984
+ }
1985
+ if ((state.shape[0] ?? 0) !== numLayers || (state.shape[1] ?? 0) !== hiddenSize) {
1986
+ throw new ShapeError(
1987
+ `Expected ${name} shape [${numLayers}, ${hiddenSize}], got [${state.shape.join(", ")}]`
1988
+ );
1989
+ }
1990
+ const stride02 = state.strides[0] ?? 0;
1991
+ const stride12 = state.strides[1] ?? 0;
1992
+ for (let l = 0; l < numLayers; l++) {
1993
+ const layerState = result[l];
1994
+ if (!layerState) {
1995
+ throw new ShapeError(`Internal error: missing ${name} layer state`);
1996
+ }
1997
+ const base = state.offset + l * stride02;
1998
+ for (let j = 0; j < hiddenSize; j++) {
1999
+ layerState[j] = readNumeric(state, base + j * stride12);
2000
+ }
2001
+ }
2002
+ return result;
2003
+ }
2004
+ if (state.ndim !== 3) {
2005
+ throw new ShapeError(`Expected ${name} with 2 or 3 dimensions; got ndim=${state.ndim}`);
2006
+ }
2007
+ const expectedBatch = isUnbatched ? 1 : batch;
2008
+ if ((state.shape[0] ?? 0) !== numLayers || (state.shape[1] ?? 0) !== expectedBatch || (state.shape[2] ?? 0) !== hiddenSize) {
2009
+ const expected = isUnbatched ? [numLayers, 1, hiddenSize] : [numLayers, batch, hiddenSize];
2010
+ throw new ShapeError(
2011
+ `Expected ${name} shape [${expected.join(", ")}], got [${state.shape.join(", ")}]`
2012
+ );
2013
+ }
2014
+ const stride0 = state.strides[0] ?? 0;
2015
+ const stride1 = state.strides[1] ?? 0;
2016
+ const stride2 = state.strides[2] ?? 0;
2017
+ for (let l = 0; l < numLayers; l++) {
2018
+ const layerState = result[l];
2019
+ if (!layerState) {
2020
+ throw new ShapeError(`Internal error: missing ${name} layer state`);
2021
+ }
2022
+ const baseLayer = state.offset + l * stride0;
2023
+ for (let b = 0; b < batch; b++) {
2024
+ const baseBatch = baseLayer + b * stride1;
2025
+ for (let j = 0; j < hiddenSize; j++) {
2026
+ layerState[b * hiddenSize + j] = readNumeric(state, baseBatch + j * stride2);
2027
+ }
2028
+ }
2029
+ }
2030
+ return result;
2031
+ }
2032
+ function packState(state, numLayers, batch, hiddenSize, dtype, device, isUnbatched) {
2033
+ const size = isUnbatched ? numLayers * hiddenSize : numLayers * batch * hiddenSize;
2034
+ const data = createFloatBuffer(size, dtype);
2035
+ if (isUnbatched) {
2036
+ for (let l = 0; l < numLayers; l++) {
2037
+ const layer = state[l];
2038
+ if (!layer) {
2039
+ throw new ShapeError("Internal error: missing packed state layer");
2040
+ }
2041
+ for (let j = 0; j < hiddenSize; j++) {
2042
+ data[l * hiddenSize + j] = layer[j] ?? 0;
2043
+ }
2044
+ }
2045
+ return Tensor.fromTypedArray({
2046
+ data,
2047
+ shape: [numLayers, hiddenSize],
2048
+ dtype,
2049
+ device
2050
+ });
2051
+ }
2052
+ for (let l = 0; l < numLayers; l++) {
2053
+ const layer = state[l];
2054
+ if (!layer) {
2055
+ throw new ShapeError("Internal error: missing packed state layer");
2056
+ }
2057
+ const layerOffset = l * batch * hiddenSize;
2058
+ for (let b = 0; b < batch; b++) {
2059
+ const batchOffset = layerOffset + b * hiddenSize;
2060
+ for (let j = 0; j < hiddenSize; j++) {
2061
+ data[batchOffset + j] = layer[b * hiddenSize + j] ?? 0;
2062
+ }
2063
+ }
2064
+ }
2065
+ return Tensor.fromTypedArray({
2066
+ data,
2067
+ shape: [numLayers, batch, hiddenSize],
2068
+ dtype,
2069
+ device
2070
+ });
2071
+ }
2072
+ var RNN = class extends Module {
2073
+ inputSize;
2074
+ hiddenSize;
2075
+ numLayers;
2076
+ nonlinearity;
2077
+ bias;
2078
+ batchFirst;
2079
+ weightsIh;
2080
+ weightsHh;
2081
+ biasIh;
2082
+ biasHh;
2083
+ constructor(inputSize, hiddenSize, options = {}) {
2084
+ super();
2085
+ validatePositiveInt("inputSize", inputSize);
2086
+ validatePositiveInt("hiddenSize", hiddenSize);
2087
+ const numLayers = options.numLayers ?? 1;
2088
+ validatePositiveInt("numLayers", numLayers);
2089
+ this.inputSize = inputSize;
2090
+ this.hiddenSize = hiddenSize;
2091
+ this.numLayers = numLayers;
2092
+ this.nonlinearity = options.nonlinearity ?? "tanh";
2093
+ this.bias = options.bias ?? true;
2094
+ this.batchFirst = options.batchFirst ?? true;
2095
+ const stdv = 1 / Math.sqrt(hiddenSize);
2096
+ this.weightsIh = [];
2097
+ this.weightsHh = [];
2098
+ this.biasIh = [];
2099
+ this.biasHh = [];
2100
+ for (let layer = 0; layer < this.numLayers; layer++) {
2101
+ const inputDim = layer === 0 ? inputSize : hiddenSize;
2102
+ const wIh = mulScalar(randn([hiddenSize, inputDim]), stdv);
2103
+ const wHh = mulScalar(randn([hiddenSize, hiddenSize]), stdv);
2104
+ this.weightsIh.push(wIh);
2105
+ this.weightsHh.push(wHh);
2106
+ this.registerParameter(`weight_ih_l${layer}`, parameter(wIh));
2107
+ this.registerParameter(`weight_hh_l${layer}`, parameter(wHh));
2108
+ if (this.bias) {
2109
+ const bIh = zeros([hiddenSize]);
2110
+ const bHh = zeros([hiddenSize]);
2111
+ this.biasIh.push(bIh);
2112
+ this.biasHh.push(bHh);
2113
+ this.registerParameter(`bias_ih_l${layer}`, parameter(bIh));
2114
+ this.registerParameter(`bias_hh_l${layer}`, parameter(bHh));
2115
+ }
2116
+ }
2117
+ }
2118
+ activation(x) {
2119
+ return this.nonlinearity === "tanh" ? Math.tanh(x) : Math.max(0, x);
2120
+ }
2121
+ run(input, hx) {
2122
+ ensureFloatTensor(input, "RNN");
2123
+ const parsed = parseInput(input, this.batchFirst);
2124
+ const { batch, seqLen, inputDim, isUnbatched, batchStride, seqStride, featStride } = parsed;
2125
+ if (inputDim !== this.inputSize) {
2126
+ throw new ShapeError(`Expected input size ${this.inputSize}, got ${inputDim}`);
2127
+ }
2128
+ if (seqLen <= 0) {
2129
+ throw new InvalidParameterError("Sequence length must be positive", "seqLen", seqLen);
2130
+ }
2131
+ if (!isUnbatched && batch <= 0) {
2132
+ throw new InvalidParameterError("Batch size must be positive", "batch", batch);
2133
+ }
2134
+ const h = buildState(hx, this.numLayers, batch, this.hiddenSize, isUnbatched, "hx");
2135
+ const outSize = (isUnbatched ? seqLen : batch * seqLen) * this.hiddenSize;
2136
+ const out = createFloatBuffer(outSize, input.dtype);
2137
+ const inputVec = new Float64Array(inputDim);
2138
+ for (let t = 0; t < seqLen; t++) {
2139
+ for (let b = 0; b < batch; b++) {
2140
+ const baseOffset = input.offset + b * batchStride + t * seqStride;
2141
+ for (let i = 0; i < inputDim; i++) {
2142
+ inputVec[i] = readNumeric(input, baseOffset + i * featStride);
2143
+ }
2144
+ let layerInput = inputVec;
2145
+ for (let l = 0; l < this.numLayers; l++) {
2146
+ const wIh = this.weightsIh[l];
2147
+ const wHh = this.weightsHh[l];
2148
+ if (!wIh || !wHh) {
2149
+ throw new ShapeError("Internal error: missing RNN weights");
2150
+ }
2151
+ const curInputSize = l === 0 ? this.inputSize : this.hiddenSize;
2152
+ const newH = new Float64Array(this.hiddenSize);
2153
+ const hLayer = h[l];
2154
+ if (!hLayer) {
2155
+ throw new ShapeError("Internal error: missing RNN hidden state");
2156
+ }
2157
+ const wIhStride0 = wIh.strides[0] ?? 0;
2158
+ const wIhStride1 = wIh.strides[1] ?? 0;
2159
+ const wHhStride0 = wHh.strides[0] ?? 0;
2160
+ const wHhStride1 = wHh.strides[1] ?? 0;
2161
+ const biasIh = this.biasIh[l];
2162
+ const biasHh = this.biasHh[l];
2163
+ const biasIhStride = biasIh ? biasIh.strides[0] ?? 0 : 0;
2164
+ const biasHhStride = biasHh ? biasHh.strides[0] ?? 0 : 0;
2165
+ for (let j = 0; j < this.hiddenSize; j++) {
2166
+ let sum2 = 0;
2167
+ const wIhBase = wIh.offset + j * wIhStride0;
2168
+ for (let k = 0; k < curInputSize; k++) {
2169
+ sum2 += (layerInput[k] ?? 0) * readNumeric(wIh, wIhBase + k * wIhStride1);
2170
+ }
2171
+ const wHhBase = wHh.offset + j * wHhStride0;
2172
+ for (let k = 0; k < this.hiddenSize; k++) {
2173
+ sum2 += (hLayer[b * this.hiddenSize + k] ?? 0) * readNumeric(wHh, wHhBase + k * wHhStride1);
2174
+ }
2175
+ if (this.bias && biasIh && biasHh) {
2176
+ sum2 += readNumeric(biasIh, biasIh.offset + j * biasIhStride);
2177
+ sum2 += readNumeric(biasHh, biasHh.offset + j * biasHhStride);
2178
+ }
2179
+ newH[j] = this.activation(sum2);
2180
+ }
2181
+ for (let j = 0; j < this.hiddenSize; j++) {
2182
+ hLayer[b * this.hiddenSize + j] = newH[j] ?? 0;
2183
+ }
2184
+ layerInput = newH;
2185
+ }
2186
+ for (let j = 0; j < this.hiddenSize; j++) {
2187
+ const idx = outputIndex(
2188
+ this.batchFirst,
2189
+ isUnbatched,
2190
+ batch,
2191
+ seqLen,
2192
+ this.hiddenSize,
2193
+ b,
2194
+ t,
2195
+ j
2196
+ );
2197
+ out[idx] = layerInput[j] ?? 0;
2198
+ }
2199
+ }
2200
+ }
2201
+ const outShape = isUnbatched ? [seqLen, this.hiddenSize] : this.batchFirst ? [batch, seqLen, this.hiddenSize] : [seqLen, batch, this.hiddenSize];
2202
+ return {
2203
+ output: Tensor.fromTypedArray({
2204
+ data: out,
2205
+ shape: outShape,
2206
+ dtype: input.dtype,
2207
+ device: input.device
2208
+ }),
2209
+ h: packState(
2210
+ h,
2211
+ this.numLayers,
2212
+ batch,
2213
+ this.hiddenSize,
2214
+ input.dtype,
2215
+ input.device,
2216
+ isUnbatched
2217
+ )
2218
+ };
2219
+ }
2220
+ forward(...inputs) {
2221
+ if (inputs.length < 1 || inputs.length > 2) {
2222
+ throw new InvalidParameterError("RNN.forward expects 1 or 2 inputs", "inputs", inputs.length);
2223
+ }
2224
+ const inputArg = inputs[0];
2225
+ if (inputArg === void 0) {
2226
+ throw new InvalidParameterError("RNN.forward requires an input tensor", "input", inputArg);
2227
+ }
2228
+ const input = extractTensor(inputArg);
2229
+ const hxArg = inputs.length === 2 ? inputs[1] : void 0;
2230
+ const hx = hxArg === void 0 ? void 0 : extractTensor(hxArg);
2231
+ return this.run(input, hx).output;
2232
+ }
2233
+ /**
2234
+ * Forward pass returning both output and hidden state.
2235
+ * Use this method when you need the hidden state.
2236
+ */
2237
+ forwardWithState(input, hx) {
2238
+ const inputTensor = extractTensor(input);
2239
+ const hxTensor = hx === void 0 ? void 0 : extractTensor(hx);
2240
+ const { output, h } = this.run(inputTensor, hxTensor);
2241
+ return [output, h];
2242
+ }
2243
+ toString() {
2244
+ return `RNN(${this.inputSize}, ${this.hiddenSize}, num_layers=${this.numLayers})`;
2245
+ }
2246
+ };
2247
+ var LSTM = class extends Module {
2248
+ inputSize;
2249
+ hiddenSize;
2250
+ numLayers;
2251
+ bias;
2252
+ batchFirst;
2253
+ weightsIh;
2254
+ weightsHh;
2255
+ biasIh;
2256
+ biasHh;
2257
+ constructor(inputSize, hiddenSize, options = {}) {
2258
+ super();
2259
+ validatePositiveInt("inputSize", inputSize);
2260
+ validatePositiveInt("hiddenSize", hiddenSize);
2261
+ const numLayers = options.numLayers ?? 1;
2262
+ validatePositiveInt("numLayers", numLayers);
2263
+ this.inputSize = inputSize;
2264
+ this.hiddenSize = hiddenSize;
2265
+ this.numLayers = numLayers;
2266
+ this.bias = options.bias ?? true;
2267
+ this.batchFirst = options.batchFirst ?? true;
2268
+ const stdv = 1 / Math.sqrt(hiddenSize);
2269
+ this.weightsIh = [];
2270
+ this.weightsHh = [];
2271
+ this.biasIh = [];
2272
+ this.biasHh = [];
2273
+ for (let layer = 0; layer < this.numLayers; layer++) {
2274
+ const inputDim = layer === 0 ? inputSize : hiddenSize;
2275
+ const wIh = mulScalar(randn([4 * hiddenSize, inputDim]), stdv);
2276
+ const wHh = mulScalar(randn([4 * hiddenSize, hiddenSize]), stdv);
2277
+ this.weightsIh.push(wIh);
2278
+ this.weightsHh.push(wHh);
2279
+ this.registerParameter(`weight_ih_l${layer}`, parameter(wIh));
2280
+ this.registerParameter(`weight_hh_l${layer}`, parameter(wHh));
2281
+ if (this.bias) {
2282
+ const bIh = zeros([4 * hiddenSize]);
2283
+ const bHh = zeros([4 * hiddenSize]);
2284
+ this.biasIh.push(bIh);
2285
+ this.biasHh.push(bHh);
2286
+ this.registerParameter(`bias_ih_l${layer}`, parameter(bIh));
2287
+ this.registerParameter(`bias_hh_l${layer}`, parameter(bHh));
2288
+ }
2289
+ }
2290
+ }
2291
+ sigmoid(x) {
2292
+ return 1 / (1 + Math.exp(-x));
2293
+ }
2294
+ run(input, hx, cx) {
2295
+ ensureFloatTensor(input, "LSTM");
2296
+ const parsed = parseInput(input, this.batchFirst);
2297
+ const { batch, seqLen, inputDim, isUnbatched, batchStride, seqStride, featStride } = parsed;
2298
+ if (inputDim !== this.inputSize) {
2299
+ throw new ShapeError(`Expected input size ${this.inputSize}, got ${inputDim}`);
2300
+ }
2301
+ if (seqLen <= 0) {
2302
+ throw new InvalidParameterError("Sequence length must be positive", "seqLen", seqLen);
2303
+ }
2304
+ if (!isUnbatched && batch <= 0) {
2305
+ throw new InvalidParameterError("Batch size must be positive", "batch", batch);
2306
+ }
2307
+ const h = buildState(hx, this.numLayers, batch, this.hiddenSize, isUnbatched, "hx");
2308
+ const c = buildState(cx, this.numLayers, batch, this.hiddenSize, isUnbatched, "cx");
2309
+ const outSize = (isUnbatched ? seqLen : batch * seqLen) * this.hiddenSize;
2310
+ const out = createFloatBuffer(outSize, input.dtype);
2311
+ const inputVec = new Float64Array(inputDim);
2312
+ const gates = new Float64Array(4 * this.hiddenSize);
2313
+ for (let t = 0; t < seqLen; t++) {
2314
+ for (let b = 0; b < batch; b++) {
2315
+ const baseOffset = input.offset + b * batchStride + t * seqStride;
2316
+ for (let i = 0; i < inputDim; i++) {
2317
+ inputVec[i] = readNumeric(input, baseOffset + i * featStride);
2318
+ }
2319
+ let layerInput = inputVec;
2320
+ for (let l = 0; l < this.numLayers; l++) {
2321
+ const wIh = this.weightsIh[l];
2322
+ const wHh = this.weightsHh[l];
2323
+ if (!wIh || !wHh) {
2324
+ throw new ShapeError("Internal error: missing LSTM weights");
2325
+ }
2326
+ const curInputSize = l === 0 ? this.inputSize : this.hiddenSize;
2327
+ const hLayer = h[l];
2328
+ const cLayer = c[l];
2329
+ if (!hLayer || !cLayer) {
2330
+ throw new ShapeError("Internal error: missing LSTM state");
2331
+ }
2332
+ const wIhStride0 = wIh.strides[0] ?? 0;
2333
+ const wIhStride1 = wIh.strides[1] ?? 0;
2334
+ const wHhStride0 = wHh.strides[0] ?? 0;
2335
+ const wHhStride1 = wHh.strides[1] ?? 0;
2336
+ const biasIh = this.biasIh[l];
2337
+ const biasHh = this.biasHh[l];
2338
+ const biasIhStride = biasIh ? biasIh.strides[0] ?? 0 : 0;
2339
+ const biasHhStride = biasHh ? biasHh.strides[0] ?? 0 : 0;
2340
+ for (let g = 0; g < 4 * this.hiddenSize; g++) {
2341
+ let sum2 = 0;
2342
+ const wIhBase = wIh.offset + g * wIhStride0;
2343
+ for (let k = 0; k < curInputSize; k++) {
2344
+ sum2 += (layerInput[k] ?? 0) * readNumeric(wIh, wIhBase + k * wIhStride1);
2345
+ }
2346
+ const wHhBase = wHh.offset + g * wHhStride0;
2347
+ for (let k = 0; k < this.hiddenSize; k++) {
2348
+ sum2 += (hLayer[b * this.hiddenSize + k] ?? 0) * readNumeric(wHh, wHhBase + k * wHhStride1);
2349
+ }
2350
+ if (this.bias && biasIh && biasHh) {
2351
+ sum2 += readNumeric(biasIh, biasIh.offset + g * biasIhStride);
2352
+ sum2 += readNumeric(biasHh, biasHh.offset + g * biasHhStride);
2353
+ }
2354
+ gates[g] = sum2;
2355
+ }
2356
+ const newH = new Float64Array(this.hiddenSize);
2357
+ const newC = new Float64Array(this.hiddenSize);
2358
+ for (let j = 0; j < this.hiddenSize; j++) {
2359
+ const iGate = this.sigmoid(gates[j] ?? 0);
2360
+ const fGate = this.sigmoid(gates[this.hiddenSize + j] ?? 0);
2361
+ const gGate = Math.tanh(gates[2 * this.hiddenSize + j] ?? 0);
2362
+ const oGate = this.sigmoid(gates[3 * this.hiddenSize + j] ?? 0);
2363
+ const prevC = cLayer[b * this.hiddenSize + j] ?? 0;
2364
+ const nextC = fGate * prevC + iGate * gGate;
2365
+ const nextH = oGate * Math.tanh(nextC);
2366
+ newC[j] = nextC;
2367
+ newH[j] = nextH;
2368
+ }
2369
+ for (let j = 0; j < this.hiddenSize; j++) {
2370
+ hLayer[b * this.hiddenSize + j] = newH[j] ?? 0;
2371
+ cLayer[b * this.hiddenSize + j] = newC[j] ?? 0;
2372
+ }
2373
+ layerInput = newH;
2374
+ }
2375
+ for (let j = 0; j < this.hiddenSize; j++) {
2376
+ const idx = outputIndex(
2377
+ this.batchFirst,
2378
+ isUnbatched,
2379
+ batch,
2380
+ seqLen,
2381
+ this.hiddenSize,
2382
+ b,
2383
+ t,
2384
+ j
2385
+ );
2386
+ out[idx] = layerInput[j] ?? 0;
2387
+ }
2388
+ }
2389
+ }
2390
+ const outShape = isUnbatched ? [seqLen, this.hiddenSize] : this.batchFirst ? [batch, seqLen, this.hiddenSize] : [seqLen, batch, this.hiddenSize];
2391
+ return {
2392
+ output: Tensor.fromTypedArray({
2393
+ data: out,
2394
+ shape: outShape,
2395
+ dtype: input.dtype,
2396
+ device: input.device
2397
+ }),
2398
+ h: packState(
2399
+ h,
2400
+ this.numLayers,
2401
+ batch,
2402
+ this.hiddenSize,
2403
+ input.dtype,
2404
+ input.device,
2405
+ isUnbatched
2406
+ ),
2407
+ c: packState(
2408
+ c,
2409
+ this.numLayers,
2410
+ batch,
2411
+ this.hiddenSize,
2412
+ input.dtype,
2413
+ input.device,
2414
+ isUnbatched
2415
+ )
2416
+ };
2417
+ }
2418
+ forward(...inputs) {
2419
+ if (inputs.length < 1 || inputs.length > 3) {
2420
+ throw new InvalidParameterError(
2421
+ "LSTM.forward expects 1 to 3 inputs",
2422
+ "inputs",
2423
+ inputs.length
2424
+ );
2425
+ }
2426
+ const inputArg = inputs[0];
2427
+ if (inputArg === void 0) {
2428
+ throw new InvalidParameterError("LSTM.forward requires an input tensor", "input", inputArg);
2429
+ }
2430
+ const input = extractTensor(inputArg);
2431
+ const hxArg = inputs.length >= 2 ? inputs[1] : void 0;
2432
+ const cxArg = inputs.length >= 3 ? inputs[2] : void 0;
2433
+ const hx = hxArg === void 0 ? void 0 : extractTensor(hxArg);
2434
+ const cx = cxArg === void 0 ? void 0 : extractTensor(cxArg);
2435
+ return this.run(input, hx, cx).output;
2436
+ }
2437
+ /**
2438
+ * Forward pass returning output, hidden state, and cell state.
2439
+ * Use this method when you need the hidden/cell states.
2440
+ */
2441
+ forwardWithState(input, hx, cx) {
2442
+ const inputTensor = extractTensor(input);
2443
+ const hxTensor = hx === void 0 ? void 0 : extractTensor(hx);
2444
+ const cxTensor = cx === void 0 ? void 0 : extractTensor(cx);
2445
+ const { output, h, c } = this.run(inputTensor, hxTensor, cxTensor);
2446
+ return [output, [h, c]];
2447
+ }
2448
+ toString() {
2449
+ return `LSTM(${this.inputSize}, ${this.hiddenSize}, num_layers=${this.numLayers})`;
2450
+ }
2451
+ };
2452
+ var GRU = class extends Module {
2453
+ inputSize;
2454
+ hiddenSize;
2455
+ numLayers;
2456
+ bias;
2457
+ batchFirst;
2458
+ weightsIh;
2459
+ weightsHh;
2460
+ biasIh;
2461
+ biasHh;
2462
+ constructor(inputSize, hiddenSize, options = {}) {
2463
+ super();
2464
+ validatePositiveInt("inputSize", inputSize);
2465
+ validatePositiveInt("hiddenSize", hiddenSize);
2466
+ const numLayers = options.numLayers ?? 1;
2467
+ validatePositiveInt("numLayers", numLayers);
2468
+ this.inputSize = inputSize;
2469
+ this.hiddenSize = hiddenSize;
2470
+ this.numLayers = numLayers;
2471
+ this.bias = options.bias ?? true;
2472
+ this.batchFirst = options.batchFirst ?? true;
2473
+ const stdv = 1 / Math.sqrt(hiddenSize);
2474
+ this.weightsIh = [];
2475
+ this.weightsHh = [];
2476
+ this.biasIh = [];
2477
+ this.biasHh = [];
2478
+ for (let layer = 0; layer < this.numLayers; layer++) {
2479
+ const inputDim = layer === 0 ? inputSize : hiddenSize;
2480
+ const wIh = mulScalar(randn([3 * hiddenSize, inputDim]), stdv);
2481
+ const wHh = mulScalar(randn([3 * hiddenSize, hiddenSize]), stdv);
2482
+ this.weightsIh.push(wIh);
2483
+ this.weightsHh.push(wHh);
2484
+ this.registerParameter(`weight_ih_l${layer}`, parameter(wIh));
2485
+ this.registerParameter(`weight_hh_l${layer}`, parameter(wHh));
2486
+ if (this.bias) {
2487
+ const bIh = zeros([3 * hiddenSize]);
2488
+ const bHh = zeros([3 * hiddenSize]);
2489
+ this.biasIh.push(bIh);
2490
+ this.biasHh.push(bHh);
2491
+ this.registerParameter(`bias_ih_l${layer}`, parameter(bIh));
2492
+ this.registerParameter(`bias_hh_l${layer}`, parameter(bHh));
2493
+ }
2494
+ }
2495
+ }
2496
+ sigmoid(x) {
2497
+ return 1 / (1 + Math.exp(-x));
2498
+ }
2499
+ run(input, hx) {
2500
+ ensureFloatTensor(input, "GRU");
2501
+ const parsed = parseInput(input, this.batchFirst);
2502
+ const { batch, seqLen, inputDim, isUnbatched, batchStride, seqStride, featStride } = parsed;
2503
+ if (inputDim !== this.inputSize) {
2504
+ throw new ShapeError(`Expected input size ${this.inputSize}, got ${inputDim}`);
2505
+ }
2506
+ if (seqLen <= 0) {
2507
+ throw new InvalidParameterError("Sequence length must be positive", "seqLen", seqLen);
2508
+ }
2509
+ if (!isUnbatched && batch <= 0) {
2510
+ throw new InvalidParameterError("Batch size must be positive", "batch", batch);
2511
+ }
2512
+ const h = buildState(hx, this.numLayers, batch, this.hiddenSize, isUnbatched, "hx");
2513
+ const outSize = (isUnbatched ? seqLen : batch * seqLen) * this.hiddenSize;
2514
+ const out = createFloatBuffer(outSize, input.dtype);
2515
+ const inputVec = new Float64Array(inputDim);
2516
+ const gatesIh = new Float64Array(3 * this.hiddenSize);
2517
+ const gatesHh = new Float64Array(3 * this.hiddenSize);
2518
+ for (let t = 0; t < seqLen; t++) {
2519
+ for (let b = 0; b < batch; b++) {
2520
+ const baseOffset = input.offset + b * batchStride + t * seqStride;
2521
+ for (let i = 0; i < inputDim; i++) {
2522
+ inputVec[i] = readNumeric(input, baseOffset + i * featStride);
2523
+ }
2524
+ let layerInput = inputVec;
2525
+ for (let l = 0; l < this.numLayers; l++) {
2526
+ const wIh = this.weightsIh[l];
2527
+ const wHh = this.weightsHh[l];
2528
+ if (!wIh || !wHh) {
2529
+ throw new ShapeError("Internal error: missing GRU weights");
2530
+ }
2531
+ const curInputSize = l === 0 ? this.inputSize : this.hiddenSize;
2532
+ const hLayer = h[l];
2533
+ if (!hLayer) {
2534
+ throw new ShapeError("Internal error: missing GRU hidden state");
2535
+ }
2536
+ const wIhStride0 = wIh.strides[0] ?? 0;
2537
+ const wIhStride1 = wIh.strides[1] ?? 0;
2538
+ const wHhStride0 = wHh.strides[0] ?? 0;
2539
+ const wHhStride1 = wHh.strides[1] ?? 0;
2540
+ const biasIh = this.biasIh[l];
2541
+ const biasHh = this.biasHh[l];
2542
+ const biasIhStride = biasIh ? biasIh.strides[0] ?? 0 : 0;
2543
+ const biasHhStride = biasHh ? biasHh.strides[0] ?? 0 : 0;
2544
+ for (let g = 0; g < 3 * this.hiddenSize; g++) {
2545
+ let sumIh = 0;
2546
+ let sumHh = 0;
2547
+ const wIhBase = wIh.offset + g * wIhStride0;
2548
+ for (let k = 0; k < curInputSize; k++) {
2549
+ sumIh += (layerInput[k] ?? 0) * readNumeric(wIh, wIhBase + k * wIhStride1);
2550
+ }
2551
+ const wHhBase = wHh.offset + g * wHhStride0;
2552
+ for (let k = 0; k < this.hiddenSize; k++) {
2553
+ sumHh += (hLayer[b * this.hiddenSize + k] ?? 0) * readNumeric(wHh, wHhBase + k * wHhStride1);
2554
+ }
2555
+ if (this.bias && biasIh && biasHh) {
2556
+ sumIh += readNumeric(biasIh, biasIh.offset + g * biasIhStride);
2557
+ sumHh += readNumeric(biasHh, biasHh.offset + g * biasHhStride);
2558
+ }
2559
+ gatesIh[g] = sumIh;
2560
+ gatesHh[g] = sumHh;
2561
+ }
2562
+ const newH = new Float64Array(this.hiddenSize);
2563
+ for (let j = 0; j < this.hiddenSize; j++) {
2564
+ const r = this.sigmoid((gatesIh[j] ?? 0) + (gatesHh[j] ?? 0));
2565
+ const z = this.sigmoid(
2566
+ (gatesIh[this.hiddenSize + j] ?? 0) + (gatesHh[this.hiddenSize + j] ?? 0)
2567
+ );
2568
+ const n = Math.tanh(
2569
+ (gatesIh[2 * this.hiddenSize + j] ?? 0) + r * (gatesHh[2 * this.hiddenSize + j] ?? 0)
2570
+ );
2571
+ newH[j] = (1 - z) * n + z * (hLayer[b * this.hiddenSize + j] ?? 0);
2572
+ }
2573
+ for (let j = 0; j < this.hiddenSize; j++) {
2574
+ hLayer[b * this.hiddenSize + j] = newH[j] ?? 0;
2575
+ }
2576
+ layerInput = newH;
2577
+ }
2578
+ for (let j = 0; j < this.hiddenSize; j++) {
2579
+ const idx = outputIndex(
2580
+ this.batchFirst,
2581
+ isUnbatched,
2582
+ batch,
2583
+ seqLen,
2584
+ this.hiddenSize,
2585
+ b,
2586
+ t,
2587
+ j
2588
+ );
2589
+ out[idx] = layerInput[j] ?? 0;
2590
+ }
2591
+ }
2592
+ }
2593
+ const outShape = isUnbatched ? [seqLen, this.hiddenSize] : this.batchFirst ? [batch, seqLen, this.hiddenSize] : [seqLen, batch, this.hiddenSize];
2594
+ return {
2595
+ output: Tensor.fromTypedArray({
2596
+ data: out,
2597
+ shape: outShape,
2598
+ dtype: input.dtype,
2599
+ device: input.device
2600
+ }),
2601
+ h: packState(
2602
+ h,
2603
+ this.numLayers,
2604
+ batch,
2605
+ this.hiddenSize,
2606
+ input.dtype,
2607
+ input.device,
2608
+ isUnbatched
2609
+ )
2610
+ };
2611
+ }
2612
+ forward(...inputs) {
2613
+ if (inputs.length < 1 || inputs.length > 2) {
2614
+ throw new InvalidParameterError("GRU.forward expects 1 or 2 inputs", "inputs", inputs.length);
2615
+ }
2616
+ const inputArg = inputs[0];
2617
+ if (inputArg === void 0) {
2618
+ throw new InvalidParameterError("GRU.forward requires an input tensor", "input", inputArg);
2619
+ }
2620
+ const input = extractTensor(inputArg);
2621
+ const hxArg = inputs.length === 2 ? inputs[1] : void 0;
2622
+ const hx = hxArg === void 0 ? void 0 : extractTensor(hxArg);
2623
+ return this.run(input, hx).output;
2624
+ }
2625
+ /**
2626
+ * Forward pass returning both output and hidden state.
2627
+ * Use this method when you need the hidden state.
2628
+ */
2629
+ forwardWithState(input, hx) {
2630
+ const inputTensor = extractTensor(input);
2631
+ const hxTensor = hx === void 0 ? void 0 : extractTensor(hx);
2632
+ const { output, h } = this.run(inputTensor, hxTensor);
2633
+ return [output, h];
2634
+ }
2635
+ toString() {
2636
+ return `GRU(${this.inputSize}, ${this.hiddenSize}, num_layers=${this.numLayers})`;
2637
+ }
2638
+ };
2639
+
2640
+ // src/nn/losses/crossEntropy.ts
2641
+ function toOneHot(indices, numClasses) {
2642
+ const nSamples = indices.size;
2643
+ const outData = new Float32Array(nSamples * numClasses);
2644
+ const data = indices.data;
2645
+ if (Array.isArray(data)) {
2646
+ throw new DTypeError("crossEntropyLoss target indices must be numeric");
2647
+ }
2648
+ const stride0 = indices.strides[0] ?? 0;
2649
+ const base = indices.offset;
2650
+ for (let i = 0; i < nSamples; i++) {
2651
+ const offset = base + i * stride0;
2652
+ let idx;
2653
+ if (data instanceof BigInt64Array) {
2654
+ const raw = getBigIntElement(data, offset);
2655
+ const asNumber = Number(raw);
2656
+ if (!Number.isSafeInteger(asNumber)) {
2657
+ throw new InvalidParameterError(
2658
+ `Class index ${raw.toString()} exceeds safe integer range`,
2659
+ "target",
2660
+ raw.toString()
2661
+ );
2662
+ }
2663
+ idx = asNumber;
2664
+ } else {
2665
+ idx = Number(getNumericElement(data, offset));
2666
+ }
2667
+ if (!Number.isFinite(idx) || !Number.isInteger(idx)) {
2668
+ throw new InvalidParameterError(`Class index ${idx} is not a valid integer`, "target", idx);
2669
+ }
2670
+ if (idx < 0 || idx >= numClasses) {
2671
+ throw new InvalidParameterError(
2672
+ `Class index ${idx} out of range [0, ${numClasses})`,
2673
+ "target",
2674
+ idx
2675
+ );
2676
+ }
2677
+ outData[i * numClasses + idx] = 1;
2678
+ }
2679
+ return Tensor.fromTypedArray({
2680
+ data: outData,
2681
+ shape: [nSamples, numClasses],
2682
+ dtype: "float32",
2683
+ device: indices.device
2684
+ });
2685
+ }
2686
+ function crossEntropyLoss(input, target) {
2687
+ const yPred = input instanceof GradTensor ? input : GradTensor.fromTensor(input);
2688
+ const targetIsGrad = target instanceof GradTensor;
2689
+ const yTrue = target instanceof GradTensor ? target : GradTensor.fromTensor(target, { requiresGrad: false });
2690
+ if (yPred.ndim !== 2) {
2691
+ throw new ShapeError(`Input must be 2-dimensional (batch, classes); got ${yPred.ndim}`);
2692
+ }
2693
+ const nSamples = yPred.shape[0] ?? 0;
2694
+ const nClasses = yPred.shape[1] ?? 0;
2695
+ let targetTensor = yTrue;
2696
+ if (yTrue.ndim === 1) {
2697
+ if (targetIsGrad) {
2698
+ throw new ShapeError("Target must be 2-dimensional when provided as GradTensor");
2699
+ }
2700
+ if (yTrue.shape[0] !== nSamples) {
2701
+ throw new ShapeError(
2702
+ `Target must have same number of samples as input; got ${yTrue.shape[0]} and ${nSamples}`
2703
+ );
2704
+ }
2705
+ const oneHot = toOneHot(yTrue.tensor, nClasses);
2706
+ targetTensor = GradTensor.fromTensor(oneHot, { requiresGrad: false });
2707
+ } else if (yTrue.ndim === 2) {
2708
+ if (yTrue.shape[0] !== nSamples || yTrue.shape[1] !== nClasses) {
2709
+ throw new ShapeError(
2710
+ "Target must be 1-dimensional class indices or have the same shape as input"
2711
+ );
2712
+ }
2713
+ } else {
2714
+ throw new ShapeError(`Target must be 1D (indices) or 2D (probs); got ${yTrue.ndim}D`);
2715
+ }
2716
+ const logProbs = logSoftmax2(yPred, 1);
2717
+ const weighted = logProbs.mul(targetTensor);
2718
+ const sampleLoss = weighted.sum(1);
2719
+ const meanLoss = sampleLoss.mean().neg();
2720
+ if (!(input instanceof GradTensor) && !targetIsGrad) {
2721
+ const data = meanLoss.tensor.data;
2722
+ if (Array.isArray(data)) {
2723
+ throw new DTypeError("crossEntropyLoss does not support string dtype");
2724
+ }
2725
+ if (data instanceof BigInt64Array) {
2726
+ const raw = getBigIntElement(data, meanLoss.tensor.offset);
2727
+ return Number(raw);
2728
+ }
2729
+ return getNumericElement(data, meanLoss.tensor.offset);
2730
+ }
2731
+ return meanLoss;
2732
+ }
2733
+ function binaryCrossEntropyWithLogitsLoss(input, target) {
2734
+ const yPred = input instanceof GradTensor ? input : GradTensor.fromTensor(input);
2735
+ const yTrue = target instanceof GradTensor ? target : GradTensor.fromTensor(target, { requiresGrad: false });
2736
+ let pred = yPred;
2737
+ let truth = yTrue;
2738
+ if (pred.ndim !== 1 && pred.ndim !== 2) {
2739
+ throw new ShapeError("Input must be 1 or 2-dimensional");
2740
+ }
2741
+ if (truth.ndim !== 1 && truth.ndim !== 2) {
2742
+ throw new ShapeError("Target must be 1 or 2-dimensional");
2743
+ }
2744
+ if (pred.ndim === 1) {
2745
+ pred = pred.reshape([pred.shape[0] ?? 0, 1]);
2746
+ }
2747
+ if (truth.ndim === 1) {
2748
+ truth = truth.reshape([truth.shape[0] ?? 0, 1]);
2749
+ }
2750
+ if (pred.ndim !== 2 || pred.shape[1] !== 1) {
2751
+ throw new ShapeError(`Input must have shape (N,) or (N, 1)`);
2752
+ }
2753
+ if (truth.ndim !== 2 || truth.shape[1] !== 1) {
2754
+ throw new ShapeError(`Target must be 1-dimensional or have shape (N, 1)`);
2755
+ }
2756
+ if ((pred.shape[0] ?? 0) !== (truth.shape[0] ?? 0)) {
2757
+ throw new ShapeError(`Batch size mismatch`);
2758
+ }
2759
+ const predDtype = pred.dtype;
2760
+ if (predDtype === "string") {
2761
+ throw new DTypeError("Binary cross entropy does not support string dtype");
2762
+ }
2763
+ const term1 = pred.relu();
2764
+ const term2 = pred.mul(truth);
2765
+ const negPred = pred.neg();
2766
+ const absPred = pred.relu().add(negPred.relu());
2767
+ const expNegAbs = absPred.neg().exp();
2768
+ const scalarDtype = expNegAbs.dtype;
2769
+ if (scalarDtype === "string") {
2770
+ throw new DTypeError("binaryCrossEntropyWithLogitsLoss does not support string dtype");
2771
+ }
2772
+ const one = GradTensor.scalar(1, { dtype: scalarDtype });
2773
+ const term3 = one.add(expNegAbs).log();
2774
+ const loss = term1.sub(term2).add(term3).mean();
2775
+ if (!(input instanceof GradTensor) && !(target instanceof GradTensor)) {
2776
+ const data = loss.tensor.data;
2777
+ if (Array.isArray(data)) {
2778
+ throw new DTypeError("binaryCrossEntropyWithLogitsLoss does not support string dtype");
2779
+ }
2780
+ if (data instanceof BigInt64Array) {
2781
+ const raw = getBigIntElement(data, loss.tensor.offset);
2782
+ return Number(raw);
2783
+ }
2784
+ return getNumericElement(data, loss.tensor.offset);
2785
+ }
2786
+ return loss;
2787
+ }
2788
+
2789
+ // src/nn/losses/index.ts
2790
+ function shapesEqual2(a, b) {
2791
+ if (a.length !== b.length) return false;
2792
+ for (let i = 0; i < a.length; i++) {
2793
+ if ((a[i] ?? 0) !== (b[i] ?? 0)) return false;
2794
+ }
2795
+ return true;
2796
+ }
2797
+ function ensureSameShape(a, b, context) {
2798
+ if (!shapesEqual2(a.shape, b.shape)) {
2799
+ throw new ShapeError(`Shape mismatch in ${context}: [${a.shape}] vs [${b.shape}]`);
2800
+ }
2801
+ }
2802
+ function ensureNumeric(t, context) {
2803
+ if (t.dtype === "string") {
2804
+ throw new DTypeError(`${context} does not support string dtype`);
2805
+ }
2806
+ }
2807
+ function validateReduction(reduction, context) {
2808
+ if (reduction !== "mean" && reduction !== "sum" && reduction !== "none") {
2809
+ throw new InvalidParameterError(
2810
+ `${context} reduction must be 'mean', 'sum', or 'none'`,
2811
+ "reduction",
2812
+ reduction
2813
+ );
2814
+ }
2815
+ }
2816
+ function readNumericFlat(data, flat, logicalStrides, strides, offset) {
2817
+ const dataOffset = offsetFromFlatIndex(flat, logicalStrides, strides, offset);
2818
+ return getElementAsNumber(data, dataOffset);
2819
+ }
2820
+ function mseLoss(predictions, targets, reduction = "mean") {
2821
+ validateReduction(reduction, "mseLoss");
2822
+ ensureNumeric(predictions, "mseLoss");
2823
+ ensureNumeric(targets, "mseLoss");
2824
+ ensureSameShape(predictions, targets, "mseLoss");
2825
+ const diff = sub(predictions, targets);
2826
+ const squaredDiff = pow(diff, tensor(2, { dtype: diff.dtype, device: diff.device }));
2827
+ if (reduction === "none") {
2828
+ return squaredDiff;
2829
+ }
2830
+ if (reduction === "sum") {
2831
+ return sum(squaredDiff);
2832
+ }
2833
+ return mean(squaredDiff);
2834
+ }
2835
+ function maeLoss(predictions, targets, reduction = "mean") {
2836
+ validateReduction(reduction, "maeLoss");
2837
+ ensureNumeric(predictions, "maeLoss");
2838
+ ensureNumeric(targets, "maeLoss");
2839
+ ensureSameShape(predictions, targets, "maeLoss");
2840
+ const diff = sub(predictions, targets);
2841
+ const absDiff = abs(diff);
2842
+ if (reduction === "none") {
2843
+ return absDiff;
2844
+ }
2845
+ if (reduction === "sum") {
2846
+ return sum(absDiff);
2847
+ }
2848
+ return mean(absDiff);
2849
+ }
2850
+ function binaryCrossEntropyLoss(predictions, targets, reduction = "mean") {
2851
+ validateReduction(reduction, "binaryCrossEntropyLoss");
2852
+ ensureNumeric(predictions, "binaryCrossEntropyLoss");
2853
+ ensureNumeric(targets, "binaryCrossEntropyLoss");
2854
+ ensureSameShape(predictions, targets, "binaryCrossEntropyLoss");
2855
+ const epsilon = 1e-7;
2856
+ const predClamped = clip(predictions, epsilon, 1 - epsilon);
2857
+ const logPred = log(predClamped);
2858
+ const term1 = mul(targets, logPred);
2859
+ const one = tensor(1, {
2860
+ dtype: predictions.dtype === "float64" ? "float64" : "float32",
2861
+ device: predictions.device
2862
+ });
2863
+ const oneMinusTargets = sub(one, targets);
2864
+ const oneMinusPred = sub(one, predClamped);
2865
+ const logOneMinusPred = log(oneMinusPred);
2866
+ const term2 = mul(oneMinusTargets, logOneMinusPred);
2867
+ const loss = neg(add(term1, term2));
2868
+ if (reduction === "none") {
2869
+ return loss;
2870
+ }
2871
+ if (reduction === "sum") {
2872
+ return sum(loss);
2873
+ }
2874
+ return mean(loss);
2875
+ }
2876
+ function rmseLoss(predictions, targets) {
2877
+ ensureNumeric(predictions, "rmseLoss");
2878
+ ensureNumeric(targets, "rmseLoss");
2879
+ ensureSameShape(predictions, targets, "rmseLoss");
2880
+ const mse = mseLoss(predictions, targets, "mean");
2881
+ return sqrt(mse);
2882
+ }
2883
+ function huberLoss(predictions, targets, delta = 1, reduction = "mean") {
2884
+ validateReduction(reduction, "huberLoss");
2885
+ ensureNumeric(predictions, "huberLoss");
2886
+ ensureNumeric(targets, "huberLoss");
2887
+ ensureSameShape(predictions, targets, "huberLoss");
2888
+ if (!Number.isFinite(delta) || delta <= 0) {
2889
+ throw new InvalidParameterError(`delta must be positive; got ${delta}`, "delta", delta);
2890
+ }
2891
+ const diff = sub(predictions, targets);
2892
+ const absDiff = abs(diff);
2893
+ const absData = absDiff.data;
2894
+ if (Array.isArray(absData)) {
2895
+ throw new DTypeError("huberLoss does not support string dtype");
2896
+ }
2897
+ const dtype = predictions.dtype === "float64" ? "float64" : "float32";
2898
+ const lossData = dtype === "float64" ? new Float64Array(diff.size) : new Float32Array(diff.size);
2899
+ const logicalStrides = computeStrides(absDiff.shape);
2900
+ for (let i = 0; i < diff.size; i++) {
2901
+ const absVal = readNumericFlat(absData, i, logicalStrides, absDiff.strides, absDiff.offset);
2902
+ if (absVal <= delta) {
2903
+ lossData[i] = 0.5 * absVal * absVal;
2904
+ } else {
2905
+ lossData[i] = delta * (absVal - 0.5 * delta);
2906
+ }
2907
+ }
2908
+ const loss = Tensor.fromTypedArray({
2909
+ data: lossData,
2910
+ shape: predictions.shape,
2911
+ dtype,
2912
+ device: predictions.device
2913
+ });
2914
+ if (reduction === "none") {
2915
+ return loss;
2916
+ }
2917
+ if (reduction === "sum") {
2918
+ return sum(loss);
2919
+ }
2920
+ return mean(loss);
2921
+ }
2922
+
2923
+ export { AvgPool2d, BatchNorm1d, Conv1d, Conv2d, Dropout, ELU, GELU, GRU, LSTM, LayerNorm, LeakyReLU, Linear, LogSoftmax, MaxPool2d, Mish, Module, MultiheadAttention, RNN, ReLU, Sequential, Sigmoid, Softmax, Softplus, Swish, Tanh, TransformerEncoderLayer, binaryCrossEntropyLoss, binaryCrossEntropyWithLogitsLoss, crossEntropyLoss, huberLoss, maeLoss, mseLoss, nn_exports, rmseLoss };
2924
+ //# sourceMappingURL=chunk-5R4S63PF.js.map
2925
+ //# sourceMappingURL=chunk-5R4S63PF.js.map