parakeet.ts 1.0.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 (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +286 -0
  3. package/dist/alignment.d.ts +36 -0
  4. package/dist/alignment.d.ts.map +1 -0
  5. package/dist/alignment.js +202 -0
  6. package/dist/alignment.js.map +1 -0
  7. package/dist/audio.d.ts +59 -0
  8. package/dist/audio.d.ts.map +1 -0
  9. package/dist/audio.js +324 -0
  10. package/dist/audio.js.map +1 -0
  11. package/dist/backend.d.ts +61 -0
  12. package/dist/backend.d.ts.map +1 -0
  13. package/dist/backend.js +14 -0
  14. package/dist/backend.js.map +1 -0
  15. package/dist/decode.d.ts +38 -0
  16. package/dist/decode.d.ts.map +1 -0
  17. package/dist/decode.js +124 -0
  18. package/dist/decode.js.map +1 -0
  19. package/dist/hub.d.ts +18 -0
  20. package/dist/hub.d.ts.map +1 -0
  21. package/dist/hub.js +86 -0
  22. package/dist/hub.js.map +1 -0
  23. package/dist/index.d.ts +13 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +19 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/load.d.ts +51 -0
  28. package/dist/load.d.ts.map +1 -0
  29. package/dist/load.js +34 -0
  30. package/dist/load.js.map +1 -0
  31. package/dist/mlx/attention.d.ts +61 -0
  32. package/dist/mlx/attention.d.ts.map +1 -0
  33. package/dist/mlx/attention.js +330 -0
  34. package/dist/mlx/attention.js.map +1 -0
  35. package/dist/mlx/audio.d.ts +46 -0
  36. package/dist/mlx/audio.d.ts.map +1 -0
  37. package/dist/mlx/audio.js +309 -0
  38. package/dist/mlx/audio.js.map +1 -0
  39. package/dist/mlx/backend.d.ts +22 -0
  40. package/dist/mlx/backend.d.ts.map +1 -0
  41. package/dist/mlx/backend.js +67 -0
  42. package/dist/mlx/backend.js.map +1 -0
  43. package/dist/mlx/cache.d.ts +23 -0
  44. package/dist/mlx/cache.d.ts.map +1 -0
  45. package/dist/mlx/cache.js +98 -0
  46. package/dist/mlx/cache.js.map +1 -0
  47. package/dist/mlx/cli.d.ts +12 -0
  48. package/dist/mlx/cli.d.ts.map +1 -0
  49. package/dist/mlx/cli.js +145 -0
  50. package/dist/mlx/cli.js.map +1 -0
  51. package/dist/mlx/conformer.d.ts +81 -0
  52. package/dist/mlx/conformer.d.ts.map +1 -0
  53. package/dist/mlx/conformer.js +315 -0
  54. package/dist/mlx/conformer.js.map +1 -0
  55. package/dist/mlx/index.d.ts +6 -0
  56. package/dist/mlx/index.d.ts.map +1 -0
  57. package/dist/mlx/index.js +9 -0
  58. package/dist/mlx/index.js.map +1 -0
  59. package/dist/mlx/load.d.ts +27 -0
  60. package/dist/mlx/load.d.ts.map +1 -0
  61. package/dist/mlx/load.js +161 -0
  62. package/dist/mlx/load.js.map +1 -0
  63. package/dist/mlx/nn.d.ts +121 -0
  64. package/dist/mlx/nn.d.ts.map +1 -0
  65. package/dist/mlx/nn.js +511 -0
  66. package/dist/mlx/nn.js.map +1 -0
  67. package/dist/mlx/rnnt.d.ts +59 -0
  68. package/dist/mlx/rnnt.d.ts.map +1 -0
  69. package/dist/mlx/rnnt.js +233 -0
  70. package/dist/mlx/rnnt.js.map +1 -0
  71. package/dist/mlx/server.d.ts +34 -0
  72. package/dist/mlx/server.d.ts.map +1 -0
  73. package/dist/mlx/server.js +115 -0
  74. package/dist/mlx/server.js.map +1 -0
  75. package/dist/mlx/utils.d.ts +19 -0
  76. package/dist/mlx/utils.d.ts.map +1 -0
  77. package/dist/mlx/utils.js +103 -0
  78. package/dist/mlx/utils.js.map +1 -0
  79. package/dist/model.d.ts +112 -0
  80. package/dist/model.d.ts.map +1 -0
  81. package/dist/model.js +196 -0
  82. package/dist/model.js.map +1 -0
  83. package/dist/onnx/backend.d.ts +55 -0
  84. package/dist/onnx/backend.d.ts.map +1 -0
  85. package/dist/onnx/backend.js +111 -0
  86. package/dist/onnx/backend.js.map +1 -0
  87. package/dist/onnx/index.d.ts +7 -0
  88. package/dist/onnx/index.d.ts.map +1 -0
  89. package/dist/onnx/index.js +6 -0
  90. package/dist/onnx/index.js.map +1 -0
  91. package/dist/onnx/parakeet.d.ts +44 -0
  92. package/dist/onnx/parakeet.d.ts.map +1 -0
  93. package/dist/onnx/parakeet.js +135 -0
  94. package/dist/onnx/parakeet.js.map +1 -0
  95. package/dist/tokenizer.d.ts +6 -0
  96. package/dist/tokenizer.d.ts.map +1 -0
  97. package/dist/tokenizer.js +8 -0
  98. package/dist/tokenizer.js.map +1 -0
  99. package/package.json +85 -0
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Neural network primitives implemented on top of @mlx-node/core MxArray.
3
+ *
4
+ * Design notes:
5
+ * - Each layer stores its weights as MxArray fields.
6
+ * - Conv1d and Conv2d are implemented via im2col (take + reshape + matmul),
7
+ * keeping all computation on-device.
8
+ * - LSTM is implemented with manual gate arithmetic so no native LSTM kernel
9
+ * is required.
10
+ * - Weight loading uses a flat Map<string, {data, shape}> that mirrors the
11
+ * safetensors key structure produced by mlx-python's tree_flatten.
12
+ */
13
+ import { MxArray } from '@mlx-node/core';
14
+ export interface TensorEntry {
15
+ data: Float32Array;
16
+ shape: number[];
17
+ }
18
+ export type WeightMap = Map<string, TensorEntry>;
19
+ export declare abstract class Module {
20
+ abstract loadWeights(weights: WeightMap, prefix: string): void;
21
+ }
22
+ export declare class Linear extends Module {
23
+ inFeatures: number;
24
+ outFeatures: number;
25
+ useBias: boolean;
26
+ weight: MxArray;
27
+ bias: MxArray | null;
28
+ constructor(inFeatures: number, outFeatures: number, useBias?: boolean);
29
+ forward(x: MxArray): MxArray;
30
+ loadWeights(weights: WeightMap, prefix: string): void;
31
+ }
32
+ export declare class Embedding extends Module {
33
+ numEmbeddings: number;
34
+ embeddingDim: number;
35
+ weight: MxArray;
36
+ constructor(numEmbeddings: number, embeddingDim: number);
37
+ forward(indices: MxArray): MxArray;
38
+ loadWeights(weights: WeightMap, prefix: string): void;
39
+ }
40
+ export declare class LayerNorm extends Module {
41
+ features: number;
42
+ eps: number;
43
+ weight: MxArray;
44
+ bias: MxArray;
45
+ constructor(features: number, eps?: number);
46
+ forward(x: MxArray): MxArray;
47
+ loadWeights(weights: WeightMap, prefix: string): void;
48
+ }
49
+ export declare class BatchNorm extends Module {
50
+ features: number;
51
+ eps: number;
52
+ weight: MxArray;
53
+ bias: MxArray;
54
+ runningMean: MxArray;
55
+ runningVar: MxArray;
56
+ constructor(features: number, eps?: number);
57
+ forward(x: MxArray): MxArray;
58
+ loadWeights(weights: WeightMap, prefix: string): void;
59
+ }
60
+ export declare class Conv1d extends Module {
61
+ inChannels: number;
62
+ outChannels: number;
63
+ kernelSize: number;
64
+ stride: number;
65
+ padding: number;
66
+ groups: number;
67
+ useBias: boolean;
68
+ weight: MxArray;
69
+ bias: MxArray | null;
70
+ constructor(inChannels: number, outChannels: number, kernelSize: number, stride?: number, padding?: number, groups?: number, useBias?: boolean);
71
+ forward(x: MxArray): MxArray;
72
+ loadWeights(weights: WeightMap, prefix: string): void;
73
+ }
74
+ /** Im2col-based Conv1d. weight: [out_ch, kernel_size, in_ch] */
75
+ export declare function conv1d(x: MxArray, weight: MxArray, bias: MxArray | null, stride: number, padding: number, groups: number): MxArray;
76
+ export declare class Conv2d extends Module {
77
+ inChannels: number;
78
+ outChannels: number;
79
+ kernelSize: number;
80
+ stride: number;
81
+ padding: number;
82
+ groups: number;
83
+ useBias: boolean;
84
+ weight: MxArray;
85
+ bias: MxArray | null;
86
+ constructor(inChannels: number, outChannels: number, kernelSize: number, stride?: number, padding?: number, groups?: number, useBias?: boolean);
87
+ forward(x: MxArray): MxArray;
88
+ loadWeights(weights: WeightMap, prefix: string): void;
89
+ }
90
+ /** Im2col-based Conv2d. weight: [out_ch, kH, kW, in_ch], input: [batch, H, W, in_ch] */
91
+ export declare function conv2d(x: MxArray, weight: MxArray, bias: MxArray | null, stride: number, padding: number, groups: number): MxArray;
92
+ export declare function sigmoid(x: MxArray): MxArray;
93
+ export declare function silu(x: MxArray): MxArray;
94
+ export declare function relu(x: MxArray): MxArray;
95
+ export declare function tanh(x: MxArray): MxArray;
96
+ export declare function glu(x: MxArray, axis: number): MxArray;
97
+ export declare function softmax(x: MxArray, axis: number): MxArray;
98
+ export declare function logSoftmax(x: MxArray, axis: number): MxArray;
99
+ export declare class LSTMLayer extends Module {
100
+ inputSize: number;
101
+ hiddenSize: number;
102
+ useBias: boolean;
103
+ Wx: MxArray;
104
+ Wh: MxArray;
105
+ b: MxArray | null;
106
+ constructor(inputSize: number, hiddenSize: number, useBias?: boolean);
107
+ /** x: [seq, batch, input], h: [batch, hidden] | null, c: [batch, hidden] | null */
108
+ forward(x: MxArray, h: MxArray | null, c: MxArray | null): [MxArray, MxArray];
109
+ loadWeights(weights: WeightMap, prefix: string): void;
110
+ }
111
+ export declare class LSTM extends Module {
112
+ layers: LSTMLayer[];
113
+ constructor(inputSize: number, hiddenSize: number, numLayers: number, useBias?: boolean);
114
+ /**
115
+ * x: [batch, seq, input] (batch_first=true as in Python code)
116
+ * Returns [output: [batch, seq, hidden], [h: [numLayers, batch, H], c: [numLayers, batch, H]]]
117
+ */
118
+ forward(x: MxArray, hc?: [MxArray, MxArray] | null): [MxArray, [MxArray, MxArray]];
119
+ loadWeights(weights: WeightMap, prefix: string): void;
120
+ }
121
+ //# sourceMappingURL=nn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nn.d.ts","sourceRoot":"","sources":["../../src/mlx/nn.ts"],"names":[],"mappings":"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"}
package/dist/mlx/nn.js ADDED
@@ -0,0 +1,511 @@
1
+ /**
2
+ * Neural network primitives implemented on top of @mlx-node/core MxArray.
3
+ *
4
+ * Design notes:
5
+ * - Each layer stores its weights as MxArray fields.
6
+ * - Conv1d and Conv2d are implemented via im2col (take + reshape + matmul),
7
+ * keeping all computation on-device.
8
+ * - LSTM is implemented with manual gate arithmetic so no native LSTM kernel
9
+ * is required.
10
+ * - Weight loading uses a flat Map<string, {data, shape}> that mirrors the
11
+ * safetensors key structure produced by mlx-python's tree_flatten.
12
+ */
13
+ import { MxArray } from '@mlx-node/core';
14
+ // ---------------------------------------------------------------------------
15
+ // Helpers
16
+ // ---------------------------------------------------------------------------
17
+ function s(...dims) {
18
+ return BigInt64Array.from(dims.map(BigInt));
19
+ }
20
+ function loadParam(weights, key) {
21
+ const entry = weights.get(key);
22
+ if (!entry)
23
+ throw new Error(`Weight not found: "${key}"`);
24
+ return MxArray.fromFloat32(entry.data, s(...entry.shape));
25
+ }
26
+ function tryLoadParam(weights, key) {
27
+ const entry = weights.get(key);
28
+ if (!entry)
29
+ return null;
30
+ return MxArray.fromFloat32(entry.data, s(...entry.shape));
31
+ }
32
+ // ---------------------------------------------------------------------------
33
+ // Module base
34
+ // ---------------------------------------------------------------------------
35
+ export class Module {
36
+ }
37
+ // ---------------------------------------------------------------------------
38
+ // Linear
39
+ // ---------------------------------------------------------------------------
40
+ export class Linear extends Module {
41
+ inFeatures;
42
+ outFeatures;
43
+ useBias;
44
+ weight; // [out, in]
45
+ bias = null;
46
+ constructor(inFeatures, outFeatures, useBias = true) {
47
+ super();
48
+ this.inFeatures = inFeatures;
49
+ this.outFeatures = outFeatures;
50
+ this.useBias = useBias;
51
+ }
52
+ forward(x) {
53
+ // x: [..., in] → [..., out]
54
+ const out = x.matmul(this.weight.transpose(new Int32Array([1, 0])));
55
+ return this.bias ? out.add(this.bias) : out;
56
+ }
57
+ loadWeights(weights, prefix) {
58
+ this.weight = loadParam(weights, `${prefix}.weight`);
59
+ if (this.useBias) {
60
+ this.bias = tryLoadParam(weights, `${prefix}.bias`);
61
+ }
62
+ }
63
+ }
64
+ // ---------------------------------------------------------------------------
65
+ // Embedding
66
+ // ---------------------------------------------------------------------------
67
+ export class Embedding extends Module {
68
+ numEmbeddings;
69
+ embeddingDim;
70
+ weight; // [num_embeddings, embedding_dim]
71
+ constructor(numEmbeddings, embeddingDim) {
72
+ super();
73
+ this.numEmbeddings = numEmbeddings;
74
+ this.embeddingDim = embeddingDim;
75
+ }
76
+ forward(indices) {
77
+ // indices: [...] (int32) → [..., embedding_dim]
78
+ return this.weight.take(indices, 0);
79
+ }
80
+ loadWeights(weights, prefix) {
81
+ this.weight = loadParam(weights, `${prefix}.weight`);
82
+ }
83
+ }
84
+ // ---------------------------------------------------------------------------
85
+ // LayerNorm
86
+ // ---------------------------------------------------------------------------
87
+ export class LayerNorm extends Module {
88
+ features;
89
+ eps;
90
+ weight; // [features]
91
+ bias; // [features]
92
+ constructor(features, eps = 1e-5) {
93
+ super();
94
+ this.features = features;
95
+ this.eps = eps;
96
+ }
97
+ forward(x) {
98
+ // x: [..., features]
99
+ const ndim = x.ndim();
100
+ const axes = new Int32Array([ndim - 1]);
101
+ const mean = x.mean(axes, true);
102
+ const variance = x.var(axes, true);
103
+ const norm = x.sub(mean).div(variance.addScalar(this.eps).sqrt());
104
+ return norm.mul(this.weight).add(this.bias);
105
+ }
106
+ loadWeights(weights, prefix) {
107
+ this.weight = loadParam(weights, `${prefix}.weight`);
108
+ this.bias = loadParam(weights, `${prefix}.bias`);
109
+ }
110
+ }
111
+ // ---------------------------------------------------------------------------
112
+ // BatchNorm (inference mode: uses running stats)
113
+ // ---------------------------------------------------------------------------
114
+ export class BatchNorm extends Module {
115
+ features;
116
+ eps;
117
+ weight; // [features]
118
+ bias; // [features]
119
+ runningMean; // [features]
120
+ runningVar; // [features]
121
+ constructor(features, eps = 1e-5) {
122
+ super();
123
+ this.features = features;
124
+ this.eps = eps;
125
+ }
126
+ forward(x) {
127
+ // x: [batch, seq, features] (after Conv1d in Conformer)
128
+ const norm = x.sub(this.runningMean).div(this.runningVar.addScalar(this.eps).sqrt());
129
+ return norm.mul(this.weight).add(this.bias);
130
+ }
131
+ loadWeights(weights, prefix) {
132
+ this.weight = loadParam(weights, `${prefix}.weight`);
133
+ this.bias = loadParam(weights, `${prefix}.bias`);
134
+ this.runningMean = loadParam(weights, `${prefix}.running_mean`);
135
+ this.runningVar = loadParam(weights, `${prefix}.running_var`);
136
+ }
137
+ }
138
+ // ---------------------------------------------------------------------------
139
+ // Conv1d — implemented via im2col + matmul, all on-device
140
+ //
141
+ // MLX weight layout for Conv1d: [out_ch, kernel_size, in_ch]
142
+ // Input layout: [batch, seq, in_ch]
143
+ // Output layout: [batch, out_seq, out_ch]
144
+ // ---------------------------------------------------------------------------
145
+ export class Conv1d extends Module {
146
+ inChannels;
147
+ outChannels;
148
+ kernelSize;
149
+ stride;
150
+ padding;
151
+ groups;
152
+ useBias;
153
+ weight; // [out_ch, kernel_size, in_ch]
154
+ bias = null;
155
+ constructor(inChannels, outChannels, kernelSize, stride = 1, padding = 0, groups = 1, useBias = true) {
156
+ super();
157
+ this.inChannels = inChannels;
158
+ this.outChannels = outChannels;
159
+ this.kernelSize = kernelSize;
160
+ this.stride = stride;
161
+ this.padding = padding;
162
+ this.groups = groups;
163
+ this.useBias = useBias;
164
+ }
165
+ forward(x) {
166
+ return conv1d(x, this.weight, this.bias, this.stride, this.padding, this.groups);
167
+ }
168
+ loadWeights(weights, prefix) {
169
+ this.weight = loadParam(weights, `${prefix}.weight`);
170
+ if (this.useBias) {
171
+ this.bias = tryLoadParam(weights, `${prefix}.bias`);
172
+ }
173
+ }
174
+ }
175
+ /** Im2col-based Conv1d. weight: [out_ch, kernel_size, in_ch] */
176
+ export function conv1d(x, weight, bias, stride, padding, groups) {
177
+ const xShape = x.shape();
178
+ const batch = Number(xShape[0]);
179
+ const seq = Number(xShape[1]);
180
+ const inCh = Number(xShape[2]);
181
+ const wShape = weight.shape();
182
+ const outCh = Number(wShape[0]);
183
+ const kernelSize = Number(wShape[1]);
184
+ // Pad the sequence dimension
185
+ let xPad = x;
186
+ if (padding > 0) {
187
+ xPad = x.pad(new Int32Array([0, 0, padding, padding, 0, 0]), 0.0);
188
+ }
189
+ const paddedSeq = Number(xPad.shape()[1]);
190
+ const outLen = Math.floor((paddedSeq - kernelSize) / stride) + 1;
191
+ if (groups === 1) {
192
+ // Standard conv: build gather indices [outLen * kernelSize]
193
+ const idxData = new Int32Array(outLen * kernelSize);
194
+ for (let t = 0; t < outLen; t++) {
195
+ for (let k = 0; k < kernelSize; k++) {
196
+ idxData[t * kernelSize + k] = t * stride + k;
197
+ }
198
+ }
199
+ const idx = MxArray.fromInt32(idxData, s(outLen * kernelSize));
200
+ // Gather along seq axis: [batch, outLen*kernelSize, inCh]
201
+ const gathered = xPad.take(idx, 1);
202
+ // Reshape to [batch, outLen, kernelSize, inCh]
203
+ const unfolded4d = gathered.reshape(s(batch, outLen, kernelSize, inCh));
204
+ // Reshape to [batch, outLen, kernelSize * inCh]
205
+ const unfolded = unfolded4d.reshape(s(batch, outLen, kernelSize * inCh));
206
+ // weight: [outCh, kernelSize, inCh] → transpose → [kernelSize, inCh, outCh]
207
+ // → reshape → [kernelSize*inCh, outCh]
208
+ const wT = weight
209
+ .transpose(new Int32Array([1, 2, 0]))
210
+ .reshape(s(kernelSize * inCh, outCh));
211
+ let out = unfolded.matmul(wT); // [batch, outLen, outCh]
212
+ if (bias)
213
+ out = out.add(bias);
214
+ return out;
215
+ }
216
+ if (groups === inCh && outCh === inCh) {
217
+ // Depthwise conv: each output channel depends on exactly one input channel
218
+ const idxData = new Int32Array(outLen * kernelSize);
219
+ for (let t = 0; t < outLen; t++) {
220
+ for (let k = 0; k < kernelSize; k++) {
221
+ idxData[t * kernelSize + k] = t * stride + k;
222
+ }
223
+ }
224
+ const idx = MxArray.fromInt32(idxData, s(outLen * kernelSize));
225
+ // Gather: [batch, outLen*kernelSize, inCh]
226
+ const gathered = xPad.take(idx, 1);
227
+ // Reshape: [batch, outLen, kernelSize, inCh]
228
+ const unfolded4d = gathered.reshape(s(batch, outLen, kernelSize, inCh));
229
+ // weight: [outCh, kernelSize, 1] → squeeze last dim → [inCh, kernelSize]
230
+ // then transpose → [kernelSize, inCh]
231
+ const wSquzd = weight.squeeze(new Int32Array([2])); // [inCh, kernelSize]
232
+ const wT = wSquzd.transpose(new Int32Array([1, 0])); // [kernelSize, inCh]
233
+ // Element-wise multiply + sum over kernel axis
234
+ const weighted = unfolded4d.mul(wT); // [batch, outLen, kernelSize, inCh]
235
+ let out = weighted.sum(new Int32Array([2])); // [batch, outLen, inCh]
236
+ if (bias)
237
+ out = out.add(bias);
238
+ return out;
239
+ }
240
+ throw new Error(`conv1d: groups=${groups} not supported (inCh=${inCh}, outCh=${outCh})`);
241
+ }
242
+ // ---------------------------------------------------------------------------
243
+ // Conv2d — same im2col strategy, in 2D
244
+ //
245
+ // MLX weight layout: [out_ch, kH, kW, in_ch]
246
+ // Input layout (MLX NHW C): [batch, H, W, in_ch]
247
+ // ---------------------------------------------------------------------------
248
+ export class Conv2d extends Module {
249
+ inChannels;
250
+ outChannels;
251
+ kernelSize;
252
+ stride;
253
+ padding;
254
+ groups;
255
+ useBias;
256
+ weight;
257
+ bias = null;
258
+ constructor(inChannels, outChannels, kernelSize, stride = 1, padding = 0, groups = 1, useBias = true) {
259
+ super();
260
+ this.inChannels = inChannels;
261
+ this.outChannels = outChannels;
262
+ this.kernelSize = kernelSize;
263
+ this.stride = stride;
264
+ this.padding = padding;
265
+ this.groups = groups;
266
+ this.useBias = useBias;
267
+ }
268
+ forward(x) {
269
+ return conv2d(x, this.weight, this.bias, this.stride, this.padding, this.groups);
270
+ }
271
+ loadWeights(weights, prefix) {
272
+ this.weight = loadParam(weights, `${prefix}.weight`);
273
+ if (this.useBias) {
274
+ this.bias = tryLoadParam(weights, `${prefix}.bias`);
275
+ }
276
+ }
277
+ }
278
+ /** Im2col-based Conv2d. weight: [out_ch, kH, kW, in_ch], input: [batch, H, W, in_ch] */
279
+ export function conv2d(x, weight, bias, stride, padding, groups) {
280
+ const xShape = x.shape();
281
+ const batch = Number(xShape[0]);
282
+ const H = Number(xShape[1]);
283
+ const W = Number(xShape[2]);
284
+ const inCh = Number(xShape[3]);
285
+ const wShape = weight.shape();
286
+ const outCh = Number(wShape[0]);
287
+ const kH = Number(wShape[1]);
288
+ const kW = Number(wShape[2]);
289
+ // Pad
290
+ let xPad = x;
291
+ if (padding > 0) {
292
+ xPad = x.pad(new Int32Array([0, 0, padding, padding, padding, padding, 0, 0]), 0.0);
293
+ }
294
+ const pH = Number(xPad.shape()[1]);
295
+ const pW = Number(xPad.shape()[2]);
296
+ const outH = Math.floor((pH - kH) / stride) + 1;
297
+ const outW = Math.floor((pW - kW) / stride) + 1;
298
+ // Build gather indices for height and width dimensions
299
+ const numPositions = outH * outW * kH * kW;
300
+ const hIdx = new Int32Array(numPositions);
301
+ const wIdx = new Int32Array(numPositions);
302
+ let pos = 0;
303
+ for (let oh = 0; oh < outH; oh++) {
304
+ for (let ow = 0; ow < outW; ow++) {
305
+ for (let kh = 0; kh < kH; kh++) {
306
+ for (let kw = 0; kw < kW; kw++) {
307
+ hIdx[pos] = oh * stride + kh;
308
+ wIdx[pos] = ow * stride + kw;
309
+ pos++;
310
+ }
311
+ }
312
+ }
313
+ }
314
+ // We need to gather at (h, w) pairs. Build flat indices into the H*W grid.
315
+ const hwIdx = new Int32Array(numPositions);
316
+ for (let i = 0; i < numPositions; i++) {
317
+ hwIdx[i] = hIdx[i] * pW + wIdx[i];
318
+ }
319
+ // Reshape x to [batch, H*W, inCh] then gather
320
+ const xFlat = xPad.reshape(s(batch, pH * pW, inCh));
321
+ const gathered = xFlat.take(MxArray.fromInt32(hwIdx, s(numPositions)), 1);
322
+ // gathered: [batch, outH*outW*kH*kW, inCh]
323
+ let out;
324
+ if (groups > 1) {
325
+ // Depthwise conv2d: weight shape is [outCh, kH, kW, 1]
326
+ // gathered: [batch, outH*outW*kH*kW, inCh]
327
+ // → [batch, outH*outW, kH*kW, inCh] → transpose → [batch, outH*outW, inCh, kH*kW]
328
+ const unfolded4d = gathered.reshape(s(batch, outH * outW, kH * kW, inCh));
329
+ const unfoldedT = unfolded4d.transpose(new Int32Array([0, 1, 3, 2]));
330
+ // weight: [outCh, kH, kW, 1] → [1, 1, outCh, kH*kW]
331
+ const wBcast = weight.reshape(s(1, 1, outCh, kH * kW));
332
+ // element-wise mul + sum over kernel dim → [batch, outH*outW, inCh]
333
+ out = unfoldedT.mul(wBcast).sum(new Int32Array([3]));
334
+ }
335
+ else {
336
+ // Standard conv2d
337
+ // Reshape to [batch, outH*outW, kH*kW*inCh]
338
+ const unfolded = gathered.reshape(s(batch, outH * outW, kH * kW * inCh));
339
+ // weight: [outCh, kH, kW, inCh] → reshape → [outCh, kH*kW*inCh]
340
+ // → transpose → [kH*kW*inCh, outCh]
341
+ const wFlat = weight.reshape(s(outCh, kH * kW * inCh));
342
+ const wT = wFlat.transpose(new Int32Array([1, 0]));
343
+ out = unfolded.matmul(wT); // [batch, outH*outW, outCh]
344
+ }
345
+ // Reshape to [batch, outH, outW, outCh]
346
+ out = out.reshape(s(batch, outH, outW, outCh));
347
+ if (bias)
348
+ out = out.add(bias);
349
+ return out;
350
+ }
351
+ // ---------------------------------------------------------------------------
352
+ // Activation helpers
353
+ // ---------------------------------------------------------------------------
354
+ export function sigmoid(x) {
355
+ // 1 / (1 + exp(-x))
356
+ return x.negative().exp().addScalar(1.0).reciprocal();
357
+ }
358
+ export function silu(x) {
359
+ return x.mul(sigmoid(x));
360
+ }
361
+ export function relu(x) {
362
+ return x.maximum(MxArray.zeros(x.shape(), null));
363
+ }
364
+ export function tanh(x) {
365
+ return x.tanh();
366
+ }
367
+ export function glu(x, axis) {
368
+ // Split in half along axis, apply sigmoid to the second half
369
+ const shape = x.shape();
370
+ const dim = Number(shape[axis]);
371
+ const half = dim / 2;
372
+ const starts1 = new Array(shape.length).fill(0n);
373
+ const stops1 = shape.map((d, i) => (i === axis ? BigInt(half) : d));
374
+ const starts2 = new Array(shape.length).fill(0n);
375
+ starts2[axis] = BigInt(half);
376
+ const stops2 = shape.map(d => d);
377
+ const a = x.slice(BigInt64Array.from(starts1), BigInt64Array.from(stops1));
378
+ const b = x.slice(BigInt64Array.from(starts2), BigInt64Array.from(stops2));
379
+ return a.mul(sigmoid(b));
380
+ }
381
+ export function softmax(x, axis) {
382
+ const maxVal = x.max(new Int32Array([axis]), true);
383
+ const shifted = x.sub(maxVal);
384
+ const expd = shifted.exp();
385
+ const sumExp = expd.sum(new Int32Array([axis]), true);
386
+ return expd.div(sumExp);
387
+ }
388
+ export function logSoftmax(x, axis) {
389
+ return x.logSoftmax(axis);
390
+ }
391
+ // ---------------------------------------------------------------------------
392
+ // LSTM — manual gate implementation
393
+ //
394
+ // MLX LSTM weight keys (per layer):
395
+ // lstm.{i}.Wx: [4*hidden, input] (gates ordered: i, f, g, o)
396
+ // lstm.{i}.Wh: [4*hidden, hidden]
397
+ // lstm.{i}.b: [4*hidden]
398
+ // ---------------------------------------------------------------------------
399
+ export class LSTMLayer extends Module {
400
+ inputSize;
401
+ hiddenSize;
402
+ useBias;
403
+ Wx; // [4*hidden, input]
404
+ Wh; // [4*hidden, hidden]
405
+ b = null;
406
+ constructor(inputSize, hiddenSize, useBias = true) {
407
+ super();
408
+ this.inputSize = inputSize;
409
+ this.hiddenSize = hiddenSize;
410
+ this.useBias = useBias;
411
+ }
412
+ /** x: [seq, batch, input], h: [batch, hidden] | null, c: [batch, hidden] | null */
413
+ forward(x, h, c) {
414
+ // x: [seq, batch, input]
415
+ const xShape = x.shape();
416
+ const seqLen = Number(xShape[0]);
417
+ const batch = Number(xShape[1]);
418
+ const H = this.hiddenSize;
419
+ // Initialize h, c if null
420
+ if (h === null)
421
+ h = MxArray.zeros(s(batch, H), null);
422
+ if (c === null)
423
+ c = MxArray.zeros(s(batch, H), null);
424
+ // Precompute input projections for all time steps: [seq, batch, 4H]
425
+ // x: [seq, batch, input] → reshape [seq*batch, input]
426
+ const xFlat = x.reshape(s(seqLen * batch, this.inputSize));
427
+ // Wx: [4H, input] → xFlat @ Wx.T → [seq*batch, 4H]
428
+ let xProj = xFlat.matmul(this.Wx.transpose(new Int32Array([1, 0])));
429
+ if (this.b)
430
+ xProj = xProj.add(this.b);
431
+ // Reshape: [seq, batch, 4H]
432
+ const xProjSeq = xProj.reshape(s(seqLen, batch, 4 * H));
433
+ const allH = [];
434
+ const allC = [];
435
+ for (let t = 0; t < seqLen; t++) {
436
+ // Get x projection for this step: [batch, 4H]
437
+ const xt = xProjSeq.slice(s(t, 0, 0), s(t + 1, batch, 4 * H)).squeeze(new Int32Array([0]));
438
+ // Hidden projection: h @ Wh.T → [batch, 4H]
439
+ const hProj = h.matmul(this.Wh.transpose(new Int32Array([1, 0])));
440
+ const gates = xt.add(hProj); // [batch, 4H]
441
+ // Split gates: i, f, g, o each [batch, H]
442
+ const gate = (idx) => gates.slice(s(0, idx * H), s(batch, (idx + 1) * H));
443
+ const gi = sigmoid(gate(0)); // input gate
444
+ const gf = sigmoid(gate(1)); // forget gate
445
+ const gg = gate(2).tanh(); // cell gate
446
+ const go = sigmoid(gate(3)); // output gate
447
+ c = gf.mul(c).add(gi.mul(gg)); // new cell
448
+ h = go.mul(c.tanh()); // new hidden
449
+ // Force evaluation to avoid unbounded graph growth
450
+ h.eval();
451
+ c.eval();
452
+ allH.push(h);
453
+ allC.push(c);
454
+ }
455
+ // Stack: [seq, batch, H]
456
+ const hStack = MxArray.stack(allH, 0);
457
+ // We return all hidden states and the last c
458
+ return [hStack, c];
459
+ }
460
+ loadWeights(weights, prefix) {
461
+ this.Wx = loadParam(weights, `${prefix}.Wx`);
462
+ this.Wh = loadParam(weights, `${prefix}.Wh`);
463
+ if (this.useBias) {
464
+ this.b = tryLoadParam(weights, `${prefix}.bias`) ?? tryLoadParam(weights, `${prefix}.b`);
465
+ }
466
+ }
467
+ }
468
+ export class LSTM extends Module {
469
+ layers;
470
+ constructor(inputSize, hiddenSize, numLayers, useBias = true) {
471
+ super();
472
+ this.layers = [];
473
+ for (let i = 0; i < numLayers; i++) {
474
+ this.layers.push(new LSTMLayer(i === 0 ? inputSize : hiddenSize, hiddenSize, useBias));
475
+ }
476
+ }
477
+ /**
478
+ * x: [batch, seq, input] (batch_first=true as in Python code)
479
+ * Returns [output: [batch, seq, hidden], [h: [numLayers, batch, H], c: [numLayers, batch, H]]]
480
+ */
481
+ forward(x, hc = null) {
482
+ // x: [batch, seq, input] → [seq, batch, input]
483
+ let current = x.transpose(new Int32Array([1, 0, 2]));
484
+ const xShape = x.shape();
485
+ const batch = Number(xShape[0]);
486
+ const H = this.layers[0].hiddenSize;
487
+ const nextHList = [];
488
+ const nextCList = [];
489
+ for (let i = 0; i < this.layers.length; i++) {
490
+ const h = hc ? hc[0].slice(s(i, 0, 0), s(i + 1, batch, H)).squeeze(new Int32Array([0])) : null;
491
+ const c = hc ? hc[1].slice(s(i, 0, 0), s(i + 1, batch, H)).squeeze(new Int32Array([0])) : null;
492
+ const [allH, lastC] = this.layers[i].forward(current, h, c);
493
+ const seqLen = Number(allH.shape()[0]);
494
+ const lastH = allH.slice(s(seqLen - 1, 0, 0), allH.shape()).squeeze(new Int32Array([0]));
495
+ current = allH; // pass all hidden states to next layer
496
+ nextHList.push(lastH);
497
+ nextCList.push(lastC);
498
+ }
499
+ // output: [seq, batch, H] → [batch, seq, H]
500
+ const output = current.transpose(new Int32Array([1, 0, 2]));
501
+ const finalH = MxArray.stack(nextHList, 0); // [numLayers, batch, H]
502
+ const finalC = MxArray.stack(nextCList, 0); // [numLayers, batch, H]
503
+ return [output, [finalH, finalC]];
504
+ }
505
+ loadWeights(weights, prefix) {
506
+ for (let i = 0; i < this.layers.length; i++) {
507
+ this.layers[i].loadWeights(weights, `${prefix}.${i}`);
508
+ }
509
+ }
510
+ }
511
+ //# sourceMappingURL=nn.js.map