@danielsimonjr/mathts-parallel 0.6.10 → 0.6.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1093 -1744
- package/dist/matrix.worker.js +88 -110
- package/package.json +17 -6
package/dist/index.js
CHANGED
|
@@ -1,1885 +1,1234 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
MathWorkerPool,
|
|
11
|
-
Transfer
|
|
12
|
-
} from "@danielsimonjr/mathts-workerpool";
|
|
13
|
-
|
|
14
|
-
// src/strategies/chunk.ts
|
|
15
|
-
var DEFAULT_CHUNK_OPTIONS = {
|
|
16
|
-
minChunkSize: 1e3,
|
|
17
|
-
maxChunks: typeof navigator !== "undefined" ? navigator.hardwareConcurrency || 4 : 4,
|
|
18
|
-
targetChunkSize: 0,
|
|
19
|
-
// 0 = auto
|
|
20
|
-
balanced: true
|
|
1
|
+
import { norm2, pairwiseDot, pairwiseSum, scaledDistance, sumSquaredDeviations } from "@danielsimonjr/mathts-core";
|
|
2
|
+
import { MathWorkerPool, Transfer } from "@danielsimonjr/mathts-workerpool";
|
|
3
|
+
//#region src/strategies/chunk.ts
|
|
4
|
+
const DEFAULT_CHUNK_OPTIONS = {
|
|
5
|
+
minChunkSize: 1e3,
|
|
6
|
+
maxChunks: typeof navigator !== "undefined" ? navigator.hardwareConcurrency || 4 : 4,
|
|
7
|
+
targetChunkSize: 0,
|
|
8
|
+
balanced: true
|
|
21
9
|
};
|
|
22
10
|
function calculateOptimalChunks(totalElements, options = {}) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const maxPossibleChunks = Math.floor(totalElements / minChunkSize);
|
|
32
|
-
return Math.max(1, Math.min(maxChunks, maxPossibleChunks));
|
|
11
|
+
const { minChunkSize, maxChunks, targetChunkSize } = {
|
|
12
|
+
...DEFAULT_CHUNK_OPTIONS,
|
|
13
|
+
...options
|
|
14
|
+
};
|
|
15
|
+
if (totalElements === 0) return 0;
|
|
16
|
+
if (targetChunkSize > 0) return Math.min(maxChunks, Math.ceil(totalElements / targetChunkSize));
|
|
17
|
+
const maxPossibleChunks = Math.floor(totalElements / minChunkSize);
|
|
18
|
+
return Math.max(1, Math.min(maxChunks, maxPossibleChunks));
|
|
33
19
|
}
|
|
34
20
|
function chunkFloat64Array(data, options = {}) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
totalElements,
|
|
101
|
-
numChunks: chunks.length
|
|
102
|
-
};
|
|
21
|
+
const { minChunkSize, balanced } = {
|
|
22
|
+
...DEFAULT_CHUNK_OPTIONS,
|
|
23
|
+
...options
|
|
24
|
+
};
|
|
25
|
+
const totalElements = data.length;
|
|
26
|
+
if (totalElements === 0) return {
|
|
27
|
+
chunks: [],
|
|
28
|
+
chunkInfo: [],
|
|
29
|
+
totalElements: 0,
|
|
30
|
+
numChunks: 0
|
|
31
|
+
};
|
|
32
|
+
const numChunks = calculateOptimalChunks(totalElements, options);
|
|
33
|
+
if (numChunks <= 1) return {
|
|
34
|
+
chunks: [data],
|
|
35
|
+
chunkInfo: [{
|
|
36
|
+
startIndex: 0,
|
|
37
|
+
endIndex: totalElements,
|
|
38
|
+
length: totalElements,
|
|
39
|
+
chunkIndex: 0
|
|
40
|
+
}],
|
|
41
|
+
totalElements,
|
|
42
|
+
numChunks: 1
|
|
43
|
+
};
|
|
44
|
+
const chunks = [];
|
|
45
|
+
const chunkInfo = [];
|
|
46
|
+
if (balanced) {
|
|
47
|
+
const baseChunkSize = Math.floor(totalElements / numChunks);
|
|
48
|
+
const remainder = totalElements % numChunks;
|
|
49
|
+
let currentIndex = 0;
|
|
50
|
+
for (let i = 0; i < numChunks; i++) {
|
|
51
|
+
const thisChunkSize = baseChunkSize + (i < remainder ? 1 : 0);
|
|
52
|
+
const endIndex = currentIndex + thisChunkSize;
|
|
53
|
+
chunks.push(data.subarray(currentIndex, endIndex));
|
|
54
|
+
chunkInfo.push({
|
|
55
|
+
startIndex: currentIndex,
|
|
56
|
+
endIndex,
|
|
57
|
+
length: thisChunkSize,
|
|
58
|
+
chunkIndex: i
|
|
59
|
+
});
|
|
60
|
+
currentIndex = endIndex;
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
const chunkSize = Math.max(minChunkSize, Math.ceil(totalElements / numChunks));
|
|
64
|
+
let currentIndex = 0;
|
|
65
|
+
let chunkIndex = 0;
|
|
66
|
+
while (currentIndex < totalElements) {
|
|
67
|
+
const endIndex = Math.min(currentIndex + chunkSize, totalElements);
|
|
68
|
+
const thisChunkSize = endIndex - currentIndex;
|
|
69
|
+
chunks.push(data.subarray(currentIndex, endIndex));
|
|
70
|
+
chunkInfo.push({
|
|
71
|
+
startIndex: currentIndex,
|
|
72
|
+
endIndex,
|
|
73
|
+
length: thisChunkSize,
|
|
74
|
+
chunkIndex
|
|
75
|
+
});
|
|
76
|
+
currentIndex = endIndex;
|
|
77
|
+
chunkIndex++;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
chunks,
|
|
82
|
+
chunkInfo,
|
|
83
|
+
totalElements,
|
|
84
|
+
numChunks: chunks.length
|
|
85
|
+
};
|
|
103
86
|
}
|
|
104
87
|
function chunkArray(data, options = {}) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
totalElements,
|
|
171
|
-
numChunks: chunks.length
|
|
172
|
-
};
|
|
88
|
+
const { balanced } = {
|
|
89
|
+
...DEFAULT_CHUNK_OPTIONS,
|
|
90
|
+
...options
|
|
91
|
+
};
|
|
92
|
+
const totalElements = data.length;
|
|
93
|
+
if (totalElements === 0) return {
|
|
94
|
+
chunks: [],
|
|
95
|
+
chunkInfo: [],
|
|
96
|
+
totalElements: 0,
|
|
97
|
+
numChunks: 0
|
|
98
|
+
};
|
|
99
|
+
const numChunks = calculateOptimalChunks(totalElements, options);
|
|
100
|
+
if (numChunks <= 1) return {
|
|
101
|
+
chunks: [data],
|
|
102
|
+
chunkInfo: [{
|
|
103
|
+
startIndex: 0,
|
|
104
|
+
endIndex: totalElements,
|
|
105
|
+
length: totalElements,
|
|
106
|
+
chunkIndex: 0
|
|
107
|
+
}],
|
|
108
|
+
totalElements,
|
|
109
|
+
numChunks: 1
|
|
110
|
+
};
|
|
111
|
+
const chunks = [];
|
|
112
|
+
const chunkInfo = [];
|
|
113
|
+
if (balanced) {
|
|
114
|
+
const baseChunkSize = Math.floor(totalElements / numChunks);
|
|
115
|
+
const remainder = totalElements % numChunks;
|
|
116
|
+
let currentIndex = 0;
|
|
117
|
+
for (let i = 0; i < numChunks; i++) {
|
|
118
|
+
const thisChunkSize = baseChunkSize + (i < remainder ? 1 : 0);
|
|
119
|
+
const endIndex = currentIndex + thisChunkSize;
|
|
120
|
+
chunks.push(data.slice(currentIndex, endIndex));
|
|
121
|
+
chunkInfo.push({
|
|
122
|
+
startIndex: currentIndex,
|
|
123
|
+
endIndex,
|
|
124
|
+
length: thisChunkSize,
|
|
125
|
+
chunkIndex: i
|
|
126
|
+
});
|
|
127
|
+
currentIndex = endIndex;
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
const chunkSize = Math.ceil(totalElements / numChunks);
|
|
131
|
+
let currentIndex = 0;
|
|
132
|
+
let chunkIndex = 0;
|
|
133
|
+
while (currentIndex < totalElements) {
|
|
134
|
+
const endIndex = Math.min(currentIndex + chunkSize, totalElements);
|
|
135
|
+
const thisChunkSize = endIndex - currentIndex;
|
|
136
|
+
chunks.push(data.slice(currentIndex, endIndex));
|
|
137
|
+
chunkInfo.push({
|
|
138
|
+
startIndex: currentIndex,
|
|
139
|
+
endIndex,
|
|
140
|
+
length: thisChunkSize,
|
|
141
|
+
chunkIndex
|
|
142
|
+
});
|
|
143
|
+
currentIndex = endIndex;
|
|
144
|
+
chunkIndex++;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
chunks,
|
|
149
|
+
chunkInfo,
|
|
150
|
+
totalElements,
|
|
151
|
+
numChunks: chunks.length
|
|
152
|
+
};
|
|
173
153
|
}
|
|
174
154
|
function mergeFloat64Chunks(chunks) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
155
|
+
if (chunks.length === 0) return /* @__PURE__ */ new Float64Array(0);
|
|
156
|
+
if (chunks.length === 1) return chunks[0];
|
|
157
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
158
|
+
const result = new Float64Array(totalLength);
|
|
159
|
+
let offset = 0;
|
|
160
|
+
for (const chunk of chunks) {
|
|
161
|
+
result.set(chunk, offset);
|
|
162
|
+
offset += chunk.length;
|
|
163
|
+
}
|
|
164
|
+
return result;
|
|
185
165
|
}
|
|
186
166
|
function mergeArrayChunks(chunks) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
167
|
+
if (chunks.length === 0) return [];
|
|
168
|
+
if (chunks.length === 1) return chunks[0];
|
|
169
|
+
return chunks.flat();
|
|
190
170
|
}
|
|
191
171
|
function shouldParallelize(elementCount, threshold = 1e4) {
|
|
192
|
-
|
|
172
|
+
return elementCount >= threshold;
|
|
193
173
|
}
|
|
194
174
|
function partitionRange(start, end, options = {}) {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
return partitions;
|
|
175
|
+
const totalElements = end - start;
|
|
176
|
+
const numChunks = calculateOptimalChunks(totalElements, options);
|
|
177
|
+
if (numChunks <= 1) return [[start, end]];
|
|
178
|
+
const partitions = [];
|
|
179
|
+
const baseChunkSize = Math.floor(totalElements / numChunks);
|
|
180
|
+
const remainder = totalElements % numChunks;
|
|
181
|
+
let currentIndex = start;
|
|
182
|
+
for (let i = 0; i < numChunks; i++) {
|
|
183
|
+
const thisChunkSize = baseChunkSize + (i < remainder ? 1 : 0);
|
|
184
|
+
const endIndex = currentIndex + thisChunkSize;
|
|
185
|
+
partitions.push([currentIndex, endIndex]);
|
|
186
|
+
currentIndex = endIndex;
|
|
187
|
+
}
|
|
188
|
+
return partitions;
|
|
211
189
|
}
|
|
212
190
|
function partition2D(rows, cols, options = {}) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
191
|
+
return partitionRange(0, rows, options).map(([rowStart, rowEnd]) => [
|
|
192
|
+
rowStart,
|
|
193
|
+
rowEnd,
|
|
194
|
+
0,
|
|
195
|
+
cols
|
|
196
|
+
]);
|
|
197
|
+
}
|
|
198
|
+
//#endregion
|
|
199
|
+
//#region src/ops/bitwise.ts
|
|
218
200
|
function applyBinaryChunk(a, b, out, start, length, outStart, op) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
201
|
+
const end = start + length;
|
|
202
|
+
switch (op) {
|
|
203
|
+
case "bitAnd":
|
|
204
|
+
for (let i = start; i < end; i++) out[outStart + (i - start)] = a[i] & b[i];
|
|
205
|
+
return;
|
|
206
|
+
case "bitOr":
|
|
207
|
+
for (let i = start; i < end; i++) out[outStart + (i - start)] = a[i] | b[i];
|
|
208
|
+
return;
|
|
209
|
+
case "bitXor":
|
|
210
|
+
for (let i = start; i < end; i++) out[outStart + (i - start)] = a[i] ^ b[i];
|
|
211
|
+
return;
|
|
212
|
+
case "leftShift":
|
|
213
|
+
for (let i = start; i < end; i++) out[outStart + (i - start)] = a[i] << b[i];
|
|
214
|
+
return;
|
|
215
|
+
case "rightArithShift":
|
|
216
|
+
for (let i = start; i < end; i++) out[outStart + (i - start)] = a[i] >> b[i];
|
|
217
|
+
return;
|
|
218
|
+
case "rightLogShift":
|
|
219
|
+
for (let i = start; i < end; i++) out[outStart + (i - start)] = a[i] >>> b[i];
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
240
222
|
}
|
|
241
223
|
function runBinary(a, b, op, options = {}) {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
}
|
|
257
|
-
return out;
|
|
224
|
+
if (a.length !== b.length) throw new Error(`Array lengths must match: ${a.length} vs ${b.length}`);
|
|
225
|
+
const out = new Int32Array(a.length);
|
|
226
|
+
if (a.length === 0) return out;
|
|
227
|
+
const numChunks = Math.max(1, calculateOptimalChunks(a.length, options));
|
|
228
|
+
const base = Math.floor(a.length / numChunks);
|
|
229
|
+
const remainder = a.length % numChunks;
|
|
230
|
+
let start = 0;
|
|
231
|
+
for (let c = 0; c < numChunks; c++) {
|
|
232
|
+
const len = base + (c < remainder ? 1 : 0);
|
|
233
|
+
if (len === 0) continue;
|
|
234
|
+
applyBinaryChunk(a, b, out, start, len, start, op);
|
|
235
|
+
start += len;
|
|
236
|
+
}
|
|
237
|
+
return out;
|
|
258
238
|
}
|
|
259
239
|
function bitAnd(a, b, options) {
|
|
260
|
-
|
|
240
|
+
return runBinary(a, b, "bitAnd", options);
|
|
261
241
|
}
|
|
262
242
|
function bitOr(a, b, options) {
|
|
263
|
-
|
|
243
|
+
return runBinary(a, b, "bitOr", options);
|
|
264
244
|
}
|
|
265
245
|
function bitXor(a, b, options) {
|
|
266
|
-
|
|
246
|
+
return runBinary(a, b, "bitXor", options);
|
|
267
247
|
}
|
|
268
248
|
function leftShift(a, b, options) {
|
|
269
|
-
|
|
270
|
-
|
|
249
|
+
if (typeof b === "number") return runBinaryScalar(a, b, "leftShift", options);
|
|
250
|
+
return runBinary(a, b, "leftShift", options);
|
|
271
251
|
}
|
|
272
252
|
function rightArithShift(a, b, options) {
|
|
273
|
-
|
|
274
|
-
|
|
253
|
+
if (typeof b === "number") return runBinaryScalar(a, b, "rightArithShift", options);
|
|
254
|
+
return runBinary(a, b, "rightArithShift", options);
|
|
275
255
|
}
|
|
276
256
|
function rightLogShift(a, b, options) {
|
|
277
|
-
|
|
278
|
-
|
|
257
|
+
if (typeof b === "number") return runBinaryScalar(a, b, "rightLogShift", options);
|
|
258
|
+
return runBinary(a, b, "rightLogShift", options);
|
|
279
259
|
}
|
|
280
260
|
function bitNot(a, options = {}) {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
261
|
+
const out = new Int32Array(a.length);
|
|
262
|
+
if (a.length === 0) return out;
|
|
263
|
+
const numChunks = Math.max(1, calculateOptimalChunks(a.length, options));
|
|
264
|
+
const base = Math.floor(a.length / numChunks);
|
|
265
|
+
const remainder = a.length % numChunks;
|
|
266
|
+
let start = 0;
|
|
267
|
+
for (let c = 0; c < numChunks; c++) {
|
|
268
|
+
const len = base + (c < remainder ? 1 : 0);
|
|
269
|
+
if (len === 0) continue;
|
|
270
|
+
const end = start + len;
|
|
271
|
+
for (let i = start; i < end; i++) out[i] = ~a[i];
|
|
272
|
+
start += len;
|
|
273
|
+
}
|
|
274
|
+
return out;
|
|
295
275
|
}
|
|
296
276
|
function runBinaryScalar(a, scalar, op, options = {}) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
return out;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
// src/ComputePool.ts
|
|
277
|
+
const out = new Int32Array(a.length);
|
|
278
|
+
if (a.length === 0) return out;
|
|
279
|
+
const numChunks = Math.max(1, calculateOptimalChunks(a.length, options));
|
|
280
|
+
const base = Math.floor(a.length / numChunks);
|
|
281
|
+
const remainder = a.length % numChunks;
|
|
282
|
+
let start = 0;
|
|
283
|
+
for (let c = 0; c < numChunks; c++) {
|
|
284
|
+
const len = base + (c < remainder ? 1 : 0);
|
|
285
|
+
if (len === 0) continue;
|
|
286
|
+
const end = start + len;
|
|
287
|
+
switch (op) {
|
|
288
|
+
case "leftShift":
|
|
289
|
+
for (let i = start; i < end; i++) out[i] = a[i] << scalar;
|
|
290
|
+
break;
|
|
291
|
+
case "rightArithShift":
|
|
292
|
+
for (let i = start; i < end; i++) out[i] = a[i] >> scalar;
|
|
293
|
+
break;
|
|
294
|
+
case "rightLogShift":
|
|
295
|
+
for (let i = start; i < end; i++) out[i] = a[i] >>> scalar;
|
|
296
|
+
break;
|
|
297
|
+
case "bitAnd":
|
|
298
|
+
for (let i = start; i < end; i++) out[i] = a[i] & scalar;
|
|
299
|
+
break;
|
|
300
|
+
case "bitOr":
|
|
301
|
+
for (let i = start; i < end; i++) out[i] = a[i] | scalar;
|
|
302
|
+
break;
|
|
303
|
+
case "bitXor": for (let i = start; i < end; i++) out[i] = a[i] ^ scalar;
|
|
304
|
+
}
|
|
305
|
+
start += len;
|
|
306
|
+
}
|
|
307
|
+
return out;
|
|
308
|
+
}
|
|
309
|
+
//#endregion
|
|
310
|
+
//#region src/ComputePool.ts
|
|
335
311
|
function resolveOpThreshold(t) {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
// Added 2026-07-04 (WS-2 completion): the five OpNames that were absent from
|
|
399
|
-
// this map and silently rode the untested 50 000 global default. Benchmarked
|
|
400
|
-
// (tools/benchmarks/ws2-missing-ops.mjs, medians of 9 interleaved reps):
|
|
401
|
-
// histogram 0.23-0.56x, transpose 0.01-0.28x, matvec 0.00-0.05x, outer
|
|
402
|
-
// 0.01-0.50x at every size up to 4.2M elements - memory-bound / copy-dominated,
|
|
403
|
-
// no break-even -> 'never'. Their ComputePool methods previously dispatched to
|
|
404
|
-
// the worker pool UNCONDITIONALLY (no shouldParallelize gate); they now gate
|
|
405
|
-
// with an inline sequential fallback like the reductions above.
|
|
406
|
-
histogram: "never",
|
|
407
|
-
transpose: "never",
|
|
408
|
-
matvec: "never",
|
|
409
|
-
outer: "never",
|
|
410
|
-
// integrateFanOut loses ~50-100x for cheap integrands (0.01-0.02x at 128-32768
|
|
411
|
-
// evals). The fan-out is CALLER-OPT-IN (integration.ts dispatches only when the
|
|
412
|
-
// caller passes workerCount > 1), so this entry documents the default posture;
|
|
413
|
-
// no shouldParallelize call consults it. Expensive (ms-scale) integrands are the
|
|
414
|
-
// one case where the fan-out pays off.
|
|
415
|
-
integrateChunk: "never",
|
|
416
|
-
// Added 2026-07-05 (WS-2 addendum): the bitwise family gated via a NAMELESS
|
|
417
|
-
// shouldParallelize(len) -> the untested global 50k. Benchmarked
|
|
418
|
-
// (tools/benchmarks/ws2-bitwise-ops.mjs, medians of 9 interleaved reps):
|
|
419
|
-
// bitAnd 0.07-0.15x, bitNot 0.06-0.09x, leftShift 0.04-0.07x at every size up
|
|
420
|
-
// to 4M elements - Int32Array element-wise is maximally memory-bound; the
|
|
421
|
-
// worker path loses 7-25x. All seven -> 'never'; the methods now pass their
|
|
422
|
-
// op name so these entries actually govern.
|
|
423
|
-
bitAnd: "never",
|
|
424
|
-
bitOr: "never",
|
|
425
|
-
bitXor: "never",
|
|
426
|
-
bitNot: "never",
|
|
427
|
-
leftShift: "never",
|
|
428
|
-
rightArithShift: "never",
|
|
429
|
-
rightLogShift: "never",
|
|
430
|
-
// signal: overhead dominates or break-even never reached
|
|
431
|
-
parallelFFT: "never",
|
|
432
|
-
parallelConv: "never",
|
|
433
|
-
fft2d: "never",
|
|
434
|
-
// geometry: overhead dominates
|
|
435
|
-
distanceMatrix: "never",
|
|
436
|
-
// special functions: parallel wins above these sizes
|
|
437
|
-
erfc: 1e5,
|
|
438
|
-
besselJ: 1e6,
|
|
439
|
-
spectrogram: 65536,
|
|
440
|
-
// linear algebra: parallel wins above these element counts
|
|
441
|
-
matmul: 4096,
|
|
442
|
-
// break-even at 64×64 (4,096 elements)
|
|
443
|
-
matrixPower: 9216,
|
|
444
|
-
// break-even at ~96×96
|
|
445
|
-
characteristicPolynomial: 9216,
|
|
446
|
-
// break-even at ~96×96
|
|
447
|
-
// hypothesis tests: element-wise reduction (chiSquare) and post-sort
|
|
448
|
-
// dot-product (KS/MW/SW). Threshold 4096 per Slice 3.10 spec.
|
|
449
|
-
chiSquareTest: 4096,
|
|
450
|
-
kolmogorovSmirnovTest: 4096,
|
|
451
|
-
mannWhitneyTest: 4096,
|
|
452
|
-
shapiroWilkTest: 4096,
|
|
453
|
-
// distribution batch sampling: per-sample cost (rejection/transcendental) amortises
|
|
454
|
-
// worker overhead above 100 000 samples (Slice 5.12).
|
|
455
|
-
sampleChunk: 1e5
|
|
312
|
+
if (t === "never") return Number.MAX_SAFE_INTEGER;
|
|
313
|
+
if (t === "always") return 0;
|
|
314
|
+
return t;
|
|
315
|
+
}
|
|
316
|
+
const DEFAULT_THRESHOLD_BY_OP = {
|
|
317
|
+
add: "never",
|
|
318
|
+
subtract: "never",
|
|
319
|
+
multiply: "never",
|
|
320
|
+
divide: "never",
|
|
321
|
+
pow: "never",
|
|
322
|
+
scale: "never",
|
|
323
|
+
abs: "never",
|
|
324
|
+
negate: "never",
|
|
325
|
+
exp: "never",
|
|
326
|
+
log: "never",
|
|
327
|
+
sin: "never",
|
|
328
|
+
cos: "never",
|
|
329
|
+
tan: "never",
|
|
330
|
+
sign: "never",
|
|
331
|
+
sqrt: "never",
|
|
332
|
+
square: "never",
|
|
333
|
+
tensordot: 8192,
|
|
334
|
+
sum: "never",
|
|
335
|
+
mean: "never",
|
|
336
|
+
variance: "never",
|
|
337
|
+
parallelStatProd: "never",
|
|
338
|
+
normalCDF: "never",
|
|
339
|
+
norm: "never",
|
|
340
|
+
dot: "never",
|
|
341
|
+
min: "never",
|
|
342
|
+
max: "never",
|
|
343
|
+
minMax: "never",
|
|
344
|
+
std: "never",
|
|
345
|
+
prod: "never",
|
|
346
|
+
distance: "never",
|
|
347
|
+
histogram: "never",
|
|
348
|
+
transpose: "never",
|
|
349
|
+
matvec: "never",
|
|
350
|
+
outer: "never",
|
|
351
|
+
integrateChunk: "never",
|
|
352
|
+
bitAnd: "never",
|
|
353
|
+
bitOr: "never",
|
|
354
|
+
bitXor: "never",
|
|
355
|
+
bitNot: "never",
|
|
356
|
+
leftShift: "never",
|
|
357
|
+
rightArithShift: "never",
|
|
358
|
+
rightLogShift: "never",
|
|
359
|
+
parallelFFT: "never",
|
|
360
|
+
parallelConv: "never",
|
|
361
|
+
fft2d: "never",
|
|
362
|
+
distanceMatrix: "never",
|
|
363
|
+
erfc: 1e5,
|
|
364
|
+
besselJ: 1e6,
|
|
365
|
+
spectrogram: 65536,
|
|
366
|
+
matmul: 4096,
|
|
367
|
+
matrixPower: 9216,
|
|
368
|
+
characteristicPolynomial: 9216,
|
|
369
|
+
chiSquareTest: 4096,
|
|
370
|
+
kolmogorovSmirnovTest: 4096,
|
|
371
|
+
mannWhitneyTest: 4096,
|
|
372
|
+
shapiroWilkTest: 4096,
|
|
373
|
+
sampleChunk: 1e5
|
|
456
374
|
};
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
// 5 minutes
|
|
375
|
+
const DEFAULT_POOL_CONFIG = {
|
|
376
|
+
enabled: true,
|
|
377
|
+
minWorkers: 1,
|
|
378
|
+
maxWorkers: typeof navigator !== "undefined" ? navigator.hardwareConcurrency || 4 : 4,
|
|
379
|
+
thresholdElements: 5e4,
|
|
380
|
+
thresholdByOp: DEFAULT_THRESHOLD_BY_OP,
|
|
381
|
+
chunkSize: 1e4,
|
|
382
|
+
workerType: "auto",
|
|
383
|
+
workerIdleTimeout: 6e4,
|
|
384
|
+
taskTimeout: 3e5
|
|
468
385
|
};
|
|
469
386
|
function mergeThresholds(base, override) {
|
|
470
|
-
|
|
387
|
+
return override === void 0 ? base : {
|
|
388
|
+
...base,
|
|
389
|
+
...override
|
|
390
|
+
};
|
|
471
391
|
}
|
|
472
392
|
function toParallelResult(result) {
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
393
|
+
return {
|
|
394
|
+
result: result.result,
|
|
395
|
+
duration: result.duration,
|
|
396
|
+
chunks: result.chunks,
|
|
397
|
+
parallelized: result.parallelized
|
|
398
|
+
};
|
|
479
399
|
}
|
|
480
400
|
function toWorkerConfig(config) {
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
401
|
+
return {
|
|
402
|
+
enabled: config.enabled,
|
|
403
|
+
minWorkers: config.minWorkers,
|
|
404
|
+
maxWorkers: config.maxWorkers,
|
|
405
|
+
parallelThreshold: config.thresholdElements,
|
|
406
|
+
chunkSize: config.chunkSize,
|
|
407
|
+
workerType: config.workerType,
|
|
408
|
+
idleTimeout: config.workerIdleTimeout,
|
|
409
|
+
taskTimeout: config.taskTimeout
|
|
410
|
+
};
|
|
491
411
|
}
|
|
492
412
|
function _rowMajorStrides(shape) {
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
413
|
+
const strides = new Array(shape.length);
|
|
414
|
+
let stride = 1;
|
|
415
|
+
for (let i = shape.length - 1; i >= 0; i--) {
|
|
416
|
+
strides[i] = stride;
|
|
417
|
+
stride *= shape[i];
|
|
418
|
+
}
|
|
419
|
+
return strides;
|
|
500
420
|
}
|
|
501
421
|
function _flatIndex(idx, strides) {
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
422
|
+
let flat = 0;
|
|
423
|
+
for (let i = 0; i < idx.length; i++) flat += idx[i] * strides[i];
|
|
424
|
+
return flat;
|
|
505
425
|
}
|
|
506
426
|
function _forEachIndex(sizes, cb) {
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
427
|
+
const rank = sizes.length;
|
|
428
|
+
if (rank === 0) {
|
|
429
|
+
cb([]);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
const idx = new Array(rank).fill(0);
|
|
433
|
+
const total = sizes.reduce((a, b) => a * b, 1);
|
|
434
|
+
for (let n = 0; n < total; n++) {
|
|
435
|
+
cb(idx.slice());
|
|
436
|
+
for (let d = rank - 1; d >= 0; d--) {
|
|
437
|
+
idx[d]++;
|
|
438
|
+
if (idx[d] < sizes[d]) break;
|
|
439
|
+
idx[d] = 0;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
522
442
|
}
|
|
523
443
|
function tensordotChunkKernel(a, aShape, b, bShape, axesA, axesB, resultShape, outStart, outEnd) {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
}
|
|
564
|
-
return result;
|
|
444
|
+
const aStrides = _rowMajorStrides(aShape);
|
|
445
|
+
const bStrides = _rowMajorStrides(bShape);
|
|
446
|
+
const outStrides = _rowMajorStrides(resultShape);
|
|
447
|
+
const aRank = aShape.length;
|
|
448
|
+
const bRank = bShape.length;
|
|
449
|
+
const contractedAxesA = Array.from(axesA);
|
|
450
|
+
const contractedAxesB = Array.from(axesB);
|
|
451
|
+
const contractedSetA = new Set(contractedAxesA);
|
|
452
|
+
const contractedSetB = new Set(contractedAxesB);
|
|
453
|
+
const aFreeAxes = [];
|
|
454
|
+
const bFreeAxes = [];
|
|
455
|
+
for (let i = 0; i < aRank; i++) if (!contractedSetA.has(i)) aFreeAxes.push(i);
|
|
456
|
+
for (let i = 0; i < bRank; i++) if (!contractedSetB.has(i)) bFreeAxes.push(i);
|
|
457
|
+
const contractedSizes = contractedAxesA.map((ax) => aShape[ax]);
|
|
458
|
+
const chunkLen = outEnd - outStart;
|
|
459
|
+
const result = new Float64Array(chunkLen);
|
|
460
|
+
const outRank = resultShape.length;
|
|
461
|
+
const outIdx = new Array(outRank);
|
|
462
|
+
for (let outFlat = outStart; outFlat < outEnd; outFlat++) {
|
|
463
|
+
let rem = outFlat;
|
|
464
|
+
for (let d = 0; d < outRank; d++) {
|
|
465
|
+
outIdx[d] = Math.floor(rem / outStrides[d]);
|
|
466
|
+
rem -= outIdx[d] * outStrides[d];
|
|
467
|
+
}
|
|
468
|
+
const aFreeVals = outIdx.slice(0, aFreeAxes.length);
|
|
469
|
+
const bFreeVals = outIdx.slice(aFreeAxes.length);
|
|
470
|
+
let acc = 0;
|
|
471
|
+
_forEachIndex(contractedSizes, (contractVals) => {
|
|
472
|
+
const aIdx = new Array(aRank).fill(0);
|
|
473
|
+
for (let fi = 0; fi < aFreeAxes.length; fi++) aIdx[aFreeAxes[fi]] = aFreeVals[fi];
|
|
474
|
+
for (let ci = 0; ci < contractedAxesA.length; ci++) aIdx[contractedAxesA[ci]] = contractVals[ci];
|
|
475
|
+
const bIdx = new Array(bRank).fill(0);
|
|
476
|
+
for (let fi = 0; fi < bFreeAxes.length; fi++) bIdx[bFreeAxes[fi]] = bFreeVals[fi];
|
|
477
|
+
for (let ci = 0; ci < contractedAxesB.length; ci++) bIdx[contractedAxesB[ci]] = contractVals[ci];
|
|
478
|
+
acc += a[_flatIndex(aIdx, aStrides)] * b[_flatIndex(bIdx, bStrides)];
|
|
479
|
+
});
|
|
480
|
+
result[outFlat - outStart] = acc;
|
|
481
|
+
}
|
|
482
|
+
return result;
|
|
565
483
|
}
|
|
566
484
|
var ComputePool = class {
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
}
|
|
1069
|
-
const result = await this.workerPool.outer(a, b);
|
|
1070
|
-
return toParallelResult(result);
|
|
1071
|
-
}
|
|
1072
|
-
// =========================================================================
|
|
1073
|
-
// Search and Sort Operations
|
|
1074
|
-
// =========================================================================
|
|
1075
|
-
/**
|
|
1076
|
-
* Parallel find operation
|
|
1077
|
-
*/
|
|
1078
|
-
async find(data, predicate) {
|
|
1079
|
-
const result = await this.workerPool.find(data, predicate);
|
|
1080
|
-
return toParallelResult(result);
|
|
1081
|
-
}
|
|
1082
|
-
/**
|
|
1083
|
-
* Parallel sort operation
|
|
1084
|
-
*/
|
|
1085
|
-
async sort(data, compare) {
|
|
1086
|
-
const result = await this.workerPool.sort(data, compare);
|
|
1087
|
-
return toParallelResult(result);
|
|
1088
|
-
}
|
|
1089
|
-
// =========================================================================
|
|
1090
|
-
// Convenience Methods
|
|
1091
|
-
// =========================================================================
|
|
1092
|
-
/**
|
|
1093
|
-
* Parallel addition of two arrays
|
|
1094
|
-
*/
|
|
1095
|
-
async add(a, b) {
|
|
1096
|
-
return this.elementwise(a, b, "add");
|
|
1097
|
-
}
|
|
1098
|
-
/**
|
|
1099
|
-
* Parallel subtraction of two arrays
|
|
1100
|
-
*/
|
|
1101
|
-
async subtract(a, b) {
|
|
1102
|
-
return this.elementwise(a, b, "subtract");
|
|
1103
|
-
}
|
|
1104
|
-
/**
|
|
1105
|
-
* Parallel element-wise multiplication
|
|
1106
|
-
*/
|
|
1107
|
-
async multiply(a, b) {
|
|
1108
|
-
return this.elementwise(a, b, "multiply");
|
|
1109
|
-
}
|
|
1110
|
-
/**
|
|
1111
|
-
* Parallel element-wise division
|
|
1112
|
-
*/
|
|
1113
|
-
async divide(a, b) {
|
|
1114
|
-
return this.elementwise(a, b, "divide");
|
|
1115
|
-
}
|
|
1116
|
-
/**
|
|
1117
|
-
* Parallel element-wise exponentiation.
|
|
1118
|
-
*
|
|
1119
|
-
* `out[i] = a[i] ** b[i]`
|
|
1120
|
-
*
|
|
1121
|
-
* Delegates to `applyKernel2` with the `Math.pow` kernel. The default
|
|
1122
|
-
* per-op threshold for `pow` is `'never'` (overhead dominates); the
|
|
1123
|
-
* sequential path is taken unless the caller overrides `thresholdByOp.pow`.
|
|
1124
|
-
*/
|
|
1125
|
-
async pow(a, b) {
|
|
1126
|
-
if (a.length !== b.length) {
|
|
1127
|
-
throw new Error(`Array lengths must match: ${a.length} vs ${b.length}`);
|
|
1128
|
-
}
|
|
1129
|
-
if (this.shouldParallelize(a.length, "pow")) {
|
|
1130
|
-
return this.applyKernel2(a, b, "(a, b) => Math.pow(a, b)");
|
|
1131
|
-
}
|
|
1132
|
-
const start = performance.now();
|
|
1133
|
-
const result = new Float64Array(a.length);
|
|
1134
|
-
for (let i = 0; i < a.length; i++) {
|
|
1135
|
-
result[i] = a[i] ** b[i];
|
|
1136
|
-
}
|
|
1137
|
-
return {
|
|
1138
|
-
result,
|
|
1139
|
-
duration: performance.now() - start,
|
|
1140
|
-
chunks: 1,
|
|
1141
|
-
parallelized: false
|
|
1142
|
-
};
|
|
1143
|
-
}
|
|
1144
|
-
/**
|
|
1145
|
-
* Parallel element-wise sign function.
|
|
1146
|
-
*
|
|
1147
|
-
* `out[i] = Math.sign(data[i])` → -1 | 0 | 1, with NaN → NaN (IEEE 754).
|
|
1148
|
-
*
|
|
1149
|
-
* Delegates to `applyKernel` with the `Math.sign` kernel. The default
|
|
1150
|
-
* per-op threshold for `sign` is `'never'` (overhead dominates); the
|
|
1151
|
-
* sequential path is taken unless the caller overrides `thresholdByOp.sign`.
|
|
1152
|
-
*/
|
|
1153
|
-
async sign(data) {
|
|
1154
|
-
if (this.shouldParallelize(data.length, "sign")) {
|
|
1155
|
-
return this.applyKernel(data, "(x) => Math.sign(x)");
|
|
1156
|
-
}
|
|
1157
|
-
const start = performance.now();
|
|
1158
|
-
const result = new Float64Array(data.length);
|
|
1159
|
-
for (let i = 0; i < data.length; i++) {
|
|
1160
|
-
result[i] = Math.sign(data[i]);
|
|
1161
|
-
}
|
|
1162
|
-
return {
|
|
1163
|
-
result,
|
|
1164
|
-
duration: performance.now() - start,
|
|
1165
|
-
chunks: 1,
|
|
1166
|
-
parallelized: false
|
|
1167
|
-
};
|
|
1168
|
-
}
|
|
1169
|
-
/**
|
|
1170
|
-
* General tensor contraction (tensordot) over specified axis pairs.
|
|
1171
|
-
*
|
|
1172
|
-
* Contracts axis `axesA[i]` of `a` with axis `axesB[i]` of `b` for each
|
|
1173
|
-
* pair index `i`. The result shape is the non-contracted axes of `a` followed
|
|
1174
|
-
* by the non-contracted axes of `b` (NumPy convention).
|
|
1175
|
-
*
|
|
1176
|
-
* Parallelization threshold: contracted-axis volume (product of contracted
|
|
1177
|
-
* dimensions) >= 8 192. When parallelized, output elements are distributed
|
|
1178
|
-
* in contiguous slices across workers via the `tensordotChunk` kernel.
|
|
1179
|
-
*
|
|
1180
|
-
* @param a - Flat row-major Float64Array for tensor A
|
|
1181
|
-
* @param aShape - Shape of tensor A
|
|
1182
|
-
* @param b - Flat row-major Float64Array for tensor B
|
|
1183
|
-
* @param bShape - Shape of tensor B
|
|
1184
|
-
* @param axesA - Axes of A to contract (same length as axesB)
|
|
1185
|
-
* @param axesB - Axes of B to contract (same length as axesA)
|
|
1186
|
-
* @returns `{ data: Float64Array; shape: number[] }` — flat result + result shape
|
|
1187
|
-
*/
|
|
1188
|
-
async tensordot(a, aShape, b, bShape, axesA, axesB) {
|
|
1189
|
-
if (axesA.length !== axesB.length) {
|
|
1190
|
-
throw new Error(
|
|
1191
|
-
`tensordot: axesA.length (${axesA.length}) !== axesB.length (${axesB.length})`
|
|
1192
|
-
);
|
|
1193
|
-
}
|
|
1194
|
-
const aRank = aShape.length;
|
|
1195
|
-
const bRank = bShape.length;
|
|
1196
|
-
const aContracted = /* @__PURE__ */ new Set();
|
|
1197
|
-
const bContracted = /* @__PURE__ */ new Set();
|
|
1198
|
-
for (let i = 0; i < axesA.length; i++) {
|
|
1199
|
-
const ax = axesA[i];
|
|
1200
|
-
const bx = axesB[i];
|
|
1201
|
-
if (!Number.isInteger(ax) || ax < 0 || ax >= aRank) {
|
|
1202
|
-
throw new Error(`tensordot: axesA[${i}]=${ax} is out of range for aShape (rank ${aRank})`);
|
|
1203
|
-
}
|
|
1204
|
-
if (!Number.isInteger(bx) || bx < 0 || bx >= bRank) {
|
|
1205
|
-
throw new Error(`tensordot: axesB[${i}]=${bx} is out of range for bShape (rank ${bRank})`);
|
|
1206
|
-
}
|
|
1207
|
-
if (aShape[ax] !== bShape[bx]) {
|
|
1208
|
-
throw new Error(
|
|
1209
|
-
`tensordot: dimension mismatch on contracted axes: aShape[${ax}]=${aShape[ax]} vs bShape[${bx}]=${bShape[bx]}`
|
|
1210
|
-
);
|
|
1211
|
-
}
|
|
1212
|
-
aContracted.add(ax);
|
|
1213
|
-
bContracted.add(bx);
|
|
1214
|
-
}
|
|
1215
|
-
const resultShape = [];
|
|
1216
|
-
for (let i = 0; i < aRank; i++) {
|
|
1217
|
-
if (!aContracted.has(i)) resultShape.push(aShape[i]);
|
|
1218
|
-
}
|
|
1219
|
-
for (let i = 0; i < bRank; i++) {
|
|
1220
|
-
if (!bContracted.has(i)) resultShape.push(bShape[i]);
|
|
1221
|
-
}
|
|
1222
|
-
const resultSize = resultShape.reduce((acc, d) => acc * d, 1);
|
|
1223
|
-
const contractedVolume = Array.from(axesA).reduce((acc, ax) => acc * aShape[ax], 1);
|
|
1224
|
-
const start = performance.now();
|
|
1225
|
-
if (this.shouldParallelize(contractedVolume, "tensordot") && resultSize > 0) {
|
|
1226
|
-
const chunkSize = this.config.chunkSize;
|
|
1227
|
-
const chunks = [];
|
|
1228
|
-
for (let i = 0; i < resultSize; i += chunkSize) {
|
|
1229
|
-
chunks.push([i, Math.min(i + chunkSize, resultSize)]);
|
|
1230
|
-
}
|
|
1231
|
-
const aShapeArr = Array.from(aShape);
|
|
1232
|
-
const bShapeArr = Array.from(bShape);
|
|
1233
|
-
const axesAArr = Array.from(axesA);
|
|
1234
|
-
const axesBArr = Array.from(axesB);
|
|
1235
|
-
const chunkResults = await Promise.all(
|
|
1236
|
-
chunks.map(
|
|
1237
|
-
([outStart, outEnd]) => this.workerPool.exec("tensordotChunk", [
|
|
1238
|
-
a.buffer,
|
|
1239
|
-
aShapeArr,
|
|
1240
|
-
b.buffer,
|
|
1241
|
-
bShapeArr,
|
|
1242
|
-
axesAArr,
|
|
1243
|
-
axesBArr,
|
|
1244
|
-
resultShape,
|
|
1245
|
-
outStart,
|
|
1246
|
-
outEnd
|
|
1247
|
-
])
|
|
1248
|
-
)
|
|
1249
|
-
);
|
|
1250
|
-
const data2 = new Float64Array(resultSize);
|
|
1251
|
-
let offset = 0;
|
|
1252
|
-
for (const buf of chunkResults) {
|
|
1253
|
-
const chunk = new Float64Array(buf);
|
|
1254
|
-
data2.set(chunk, offset);
|
|
1255
|
-
offset += chunk.length;
|
|
1256
|
-
}
|
|
1257
|
-
return {
|
|
1258
|
-
result: { data: data2, shape: resultShape },
|
|
1259
|
-
duration: performance.now() - start,
|
|
1260
|
-
chunks: chunks.length,
|
|
1261
|
-
parallelized: true
|
|
1262
|
-
};
|
|
1263
|
-
}
|
|
1264
|
-
const data = resultSize === 0 ? new Float64Array(0) : tensordotChunkKernel(a, aShape, b, bShape, axesA, axesB, resultShape, 0, resultSize);
|
|
1265
|
-
return {
|
|
1266
|
-
result: { data, shape: resultShape },
|
|
1267
|
-
duration: performance.now() - start,
|
|
1268
|
-
chunks: 1,
|
|
1269
|
-
parallelized: false
|
|
1270
|
-
};
|
|
1271
|
-
}
|
|
1272
|
-
/**
|
|
1273
|
-
* Compute mean in parallel (uses variance internally)
|
|
1274
|
-
*/
|
|
1275
|
-
async mean(data) {
|
|
1276
|
-
const result = await this.variance(data);
|
|
1277
|
-
return {
|
|
1278
|
-
...result,
|
|
1279
|
-
result: result.result.mean
|
|
1280
|
-
};
|
|
1281
|
-
}
|
|
1282
|
-
/**
|
|
1283
|
-
* Compute standard deviation in parallel
|
|
1284
|
-
*/
|
|
1285
|
-
async std(data) {
|
|
1286
|
-
const result = await this.variance(data);
|
|
1287
|
-
return {
|
|
1288
|
-
...result,
|
|
1289
|
-
result: result.result.std
|
|
1290
|
-
};
|
|
1291
|
-
}
|
|
1292
|
-
/**
|
|
1293
|
-
* Find minimum value in parallel
|
|
1294
|
-
*/
|
|
1295
|
-
async min(data) {
|
|
1296
|
-
const result = await this.minMax(data);
|
|
1297
|
-
return {
|
|
1298
|
-
...result,
|
|
1299
|
-
result: result.result.min
|
|
1300
|
-
};
|
|
1301
|
-
}
|
|
1302
|
-
/**
|
|
1303
|
-
* Find maximum value in parallel
|
|
1304
|
-
*/
|
|
1305
|
-
async max(data) {
|
|
1306
|
-
const result = await this.minMax(data);
|
|
1307
|
-
return {
|
|
1308
|
-
...result,
|
|
1309
|
-
result: result.result.max
|
|
1310
|
-
};
|
|
1311
|
-
}
|
|
1312
|
-
// =========================================================================
|
|
1313
|
-
// Bitwise Operations (Int32Array)
|
|
1314
|
-
// =========================================================================
|
|
1315
|
-
//
|
|
1316
|
-
// Above the elementwise threshold these dispatch to the `Int32Array`-keyed
|
|
1317
|
-
// worker kernels in `@danielsimonjr/mathts-workerpool` (`bitwiseChunk`,
|
|
1318
|
-
// `bitwiseScalarChunk`, `bitwiseNotChunk`). Below threshold they fall back
|
|
1319
|
-
// to the pure in-process drivers in `./ops/bitwise.ts`. The fallback path
|
|
1320
|
-
// uses those drivers verbatim so the algorithm lives in one place.
|
|
1321
|
-
/**
|
|
1322
|
-
* Element-wise bitwise AND on two `Int32Array`s.
|
|
1323
|
-
*/
|
|
1324
|
-
async bitAnd(a, b) {
|
|
1325
|
-
if (a.length !== b.length) {
|
|
1326
|
-
throw new Error(`Array lengths must match: ${a.length} vs ${b.length}`);
|
|
1327
|
-
}
|
|
1328
|
-
if (this.shouldParallelize(a.length, "bitAnd")) {
|
|
1329
|
-
return toParallelResult(await this.workerPool.bitwiseBinary(a, b, "bitAnd"));
|
|
1330
|
-
}
|
|
1331
|
-
const start = performance.now();
|
|
1332
|
-
const result = bitAnd(a, b);
|
|
1333
|
-
return {
|
|
1334
|
-
result,
|
|
1335
|
-
duration: performance.now() - start,
|
|
1336
|
-
chunks: 1,
|
|
1337
|
-
parallelized: false
|
|
1338
|
-
};
|
|
1339
|
-
}
|
|
1340
|
-
/**
|
|
1341
|
-
* Element-wise bitwise OR on two `Int32Array`s.
|
|
1342
|
-
*/
|
|
1343
|
-
async bitOr(a, b) {
|
|
1344
|
-
if (a.length !== b.length) {
|
|
1345
|
-
throw new Error(`Array lengths must match: ${a.length} vs ${b.length}`);
|
|
1346
|
-
}
|
|
1347
|
-
if (this.shouldParallelize(a.length, "bitOr")) {
|
|
1348
|
-
return toParallelResult(await this.workerPool.bitwiseBinary(a, b, "bitOr"));
|
|
1349
|
-
}
|
|
1350
|
-
const start = performance.now();
|
|
1351
|
-
const result = bitOr(a, b);
|
|
1352
|
-
return {
|
|
1353
|
-
result,
|
|
1354
|
-
duration: performance.now() - start,
|
|
1355
|
-
chunks: 1,
|
|
1356
|
-
parallelized: false
|
|
1357
|
-
};
|
|
1358
|
-
}
|
|
1359
|
-
/**
|
|
1360
|
-
* Element-wise bitwise XOR on two `Int32Array`s.
|
|
1361
|
-
*/
|
|
1362
|
-
async bitXor(a, b) {
|
|
1363
|
-
if (a.length !== b.length) {
|
|
1364
|
-
throw new Error(`Array lengths must match: ${a.length} vs ${b.length}`);
|
|
1365
|
-
}
|
|
1366
|
-
if (this.shouldParallelize(a.length, "bitXor")) {
|
|
1367
|
-
return toParallelResult(await this.workerPool.bitwiseBinary(a, b, "bitXor"));
|
|
1368
|
-
}
|
|
1369
|
-
const start = performance.now();
|
|
1370
|
-
const result = bitXor(a, b);
|
|
1371
|
-
return {
|
|
1372
|
-
result,
|
|
1373
|
-
duration: performance.now() - start,
|
|
1374
|
-
chunks: 1,
|
|
1375
|
-
parallelized: false
|
|
1376
|
-
};
|
|
1377
|
-
}
|
|
1378
|
-
/**
|
|
1379
|
-
* Unary bitwise NOT on an `Int32Array`.
|
|
1380
|
-
*/
|
|
1381
|
-
async bitNot(a) {
|
|
1382
|
-
if (this.shouldParallelize(a.length, "bitNot")) {
|
|
1383
|
-
return toParallelResult(await this.workerPool.bitwiseNot(a));
|
|
1384
|
-
}
|
|
1385
|
-
const start = performance.now();
|
|
1386
|
-
const result = bitNot(a);
|
|
1387
|
-
return {
|
|
1388
|
-
result,
|
|
1389
|
-
duration: performance.now() - start,
|
|
1390
|
-
chunks: 1,
|
|
1391
|
-
parallelized: false
|
|
1392
|
-
};
|
|
1393
|
-
}
|
|
1394
|
-
/**
|
|
1395
|
-
* Element-wise left shift. `b` may be a per-element `Int32Array` of
|
|
1396
|
-
* shift counts or a single scalar `number` applied uniformly.
|
|
1397
|
-
*/
|
|
1398
|
-
async leftShift(a, b) {
|
|
1399
|
-
if (typeof b !== "number" && a.length !== b.length) {
|
|
1400
|
-
throw new Error(`Array lengths must match: ${a.length} vs ${b.length}`);
|
|
1401
|
-
}
|
|
1402
|
-
if (this.shouldParallelize(a.length, "leftShift")) {
|
|
1403
|
-
const result2 = typeof b === "number" ? await this.workerPool.bitwiseScalar(a, b, "leftShift") : await this.workerPool.bitwiseBinary(a, b, "leftShift");
|
|
1404
|
-
return toParallelResult(result2);
|
|
1405
|
-
}
|
|
1406
|
-
const start = performance.now();
|
|
1407
|
-
const result = leftShift(a, b);
|
|
1408
|
-
return {
|
|
1409
|
-
result,
|
|
1410
|
-
duration: performance.now() - start,
|
|
1411
|
-
chunks: 1,
|
|
1412
|
-
parallelized: false
|
|
1413
|
-
};
|
|
1414
|
-
}
|
|
1415
|
-
/**
|
|
1416
|
-
* Element-wise arithmetic (sign-preserving) right shift.
|
|
1417
|
-
*/
|
|
1418
|
-
async rightArithShift(a, b) {
|
|
1419
|
-
if (typeof b !== "number" && a.length !== b.length) {
|
|
1420
|
-
throw new Error(`Array lengths must match: ${a.length} vs ${b.length}`);
|
|
1421
|
-
}
|
|
1422
|
-
if (this.shouldParallelize(a.length, "rightArithShift")) {
|
|
1423
|
-
const result2 = typeof b === "number" ? await this.workerPool.bitwiseScalar(a, b, "rightArithShift") : await this.workerPool.bitwiseBinary(a, b, "rightArithShift");
|
|
1424
|
-
return toParallelResult(result2);
|
|
1425
|
-
}
|
|
1426
|
-
const start = performance.now();
|
|
1427
|
-
const result = rightArithShift(a, b);
|
|
1428
|
-
return {
|
|
1429
|
-
result,
|
|
1430
|
-
duration: performance.now() - start,
|
|
1431
|
-
chunks: 1,
|
|
1432
|
-
parallelized: false
|
|
1433
|
-
};
|
|
1434
|
-
}
|
|
1435
|
-
/**
|
|
1436
|
-
* Element-wise logical (zero-filling) right shift. Results are stored
|
|
1437
|
-
* as `Int32Array`, so values ≥ 2^31 wrap into the negative range —
|
|
1438
|
-
* matching `(x >>> n) | 0` JavaScript semantics.
|
|
1439
|
-
*/
|
|
1440
|
-
async rightLogShift(a, b) {
|
|
1441
|
-
if (typeof b !== "number" && a.length !== b.length) {
|
|
1442
|
-
throw new Error(`Array lengths must match: ${a.length} vs ${b.length}`);
|
|
1443
|
-
}
|
|
1444
|
-
if (this.shouldParallelize(a.length, "rightLogShift")) {
|
|
1445
|
-
const result2 = typeof b === "number" ? await this.workerPool.bitwiseScalar(a, b, "rightLogShift") : await this.workerPool.bitwiseBinary(a, b, "rightLogShift");
|
|
1446
|
-
return toParallelResult(result2);
|
|
1447
|
-
}
|
|
1448
|
-
const start = performance.now();
|
|
1449
|
-
const result = rightLogShift(a, b);
|
|
1450
|
-
return {
|
|
1451
|
-
result,
|
|
1452
|
-
duration: performance.now() - start,
|
|
1453
|
-
chunks: 1,
|
|
1454
|
-
parallelized: false
|
|
1455
|
-
};
|
|
1456
|
-
}
|
|
1457
|
-
/**
|
|
1458
|
-
* Terminate the worker pool
|
|
1459
|
-
*/
|
|
1460
|
-
async terminate(force = false) {
|
|
1461
|
-
await this.workerPool.terminate(force);
|
|
1462
|
-
}
|
|
1463
|
-
/**
|
|
1464
|
-
* Update configuration
|
|
1465
|
-
*/
|
|
1466
|
-
updateConfig(config) {
|
|
1467
|
-
this.config = {
|
|
1468
|
-
...this.config,
|
|
1469
|
-
...config,
|
|
1470
|
-
thresholdByOp: mergeThresholds(this.config.thresholdByOp, config.thresholdByOp)
|
|
1471
|
-
};
|
|
1472
|
-
this.workerPool.updateConfig(toWorkerConfig(this.config));
|
|
1473
|
-
}
|
|
1474
|
-
/**
|
|
1475
|
-
* Get current configuration
|
|
1476
|
-
*/
|
|
1477
|
-
getConfig() {
|
|
1478
|
-
return { ...this.config };
|
|
1479
|
-
}
|
|
1480
|
-
/**
|
|
1481
|
-
* Maximum number of workers configured for this pool.
|
|
1482
|
-
* Used by distribution sampling fan-out to determine chunk count.
|
|
1483
|
-
*/
|
|
1484
|
-
get workerCount() {
|
|
1485
|
-
return this.config.maxWorkers;
|
|
1486
|
-
}
|
|
1487
|
-
/**
|
|
1488
|
-
* Get the underlying MathWorkerPool for advanced operations
|
|
1489
|
-
*/
|
|
1490
|
-
getWorkerPool() {
|
|
1491
|
-
return this.workerPool;
|
|
1492
|
-
}
|
|
485
|
+
workerPool;
|
|
486
|
+
config;
|
|
487
|
+
constructor(config = {}) {
|
|
488
|
+
this.config = {
|
|
489
|
+
...DEFAULT_POOL_CONFIG,
|
|
490
|
+
...config,
|
|
491
|
+
thresholdByOp: mergeThresholds(DEFAULT_POOL_CONFIG.thresholdByOp, config.thresholdByOp)
|
|
492
|
+
};
|
|
493
|
+
this.workerPool = new MathWorkerPool(toWorkerConfig(this.config));
|
|
494
|
+
}
|
|
495
|
+
async initialize() {
|
|
496
|
+
await this.workerPool.initialize();
|
|
497
|
+
}
|
|
498
|
+
isReady() {
|
|
499
|
+
return this.workerPool.isReady();
|
|
500
|
+
}
|
|
501
|
+
shouldParallelize(elementCount, op) {
|
|
502
|
+
const byOp = this.config.thresholdByOp;
|
|
503
|
+
if (op !== void 0 && byOp !== void 0 && op in byOp) {
|
|
504
|
+
const raw = byOp[op];
|
|
505
|
+
if (raw !== void 0) {
|
|
506
|
+
const threshold = resolveOpThreshold(raw);
|
|
507
|
+
return this.config.enabled && elementCount >= threshold && this.workerPool.isReady();
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
return this.workerPool.shouldParallelize(elementCount);
|
|
511
|
+
}
|
|
512
|
+
seqResult(result) {
|
|
513
|
+
return {
|
|
514
|
+
result,
|
|
515
|
+
duration: 0,
|
|
516
|
+
chunks: 1,
|
|
517
|
+
parallelized: false
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
async exec(method, params, options) {
|
|
521
|
+
return this.workerPool.exec(method, params, options);
|
|
522
|
+
}
|
|
523
|
+
stats() {
|
|
524
|
+
return this.workerPool.stats();
|
|
525
|
+
}
|
|
526
|
+
async sum(data) {
|
|
527
|
+
if (!this.shouldParallelize(data.length, "sum")) return this.seqResult(pairwiseSum(data));
|
|
528
|
+
return toParallelResult(await this.workerPool.sum(data));
|
|
529
|
+
}
|
|
530
|
+
async prod(data) {
|
|
531
|
+
if (!this.shouldParallelize(data.length, "prod")) {
|
|
532
|
+
let p = 1;
|
|
533
|
+
for (let i = 0; i < data.length; i++) p *= data[i];
|
|
534
|
+
return this.seqResult(p);
|
|
535
|
+
}
|
|
536
|
+
return toParallelResult(await this.workerPool.prod(data));
|
|
537
|
+
}
|
|
538
|
+
async dot(a, b) {
|
|
539
|
+
if (!this.shouldParallelize(a.length, "dot")) {
|
|
540
|
+
if (a.length !== b.length) throw new Error("dot: vectors must have the same length");
|
|
541
|
+
return this.seqResult(pairwiseDot(a, b));
|
|
542
|
+
}
|
|
543
|
+
return toParallelResult(await this.workerPool.dot(a, b));
|
|
544
|
+
}
|
|
545
|
+
poolOptions(elementCount, op) {
|
|
546
|
+
return this.shouldParallelize(elementCount, op) ? { forceParallel: true } : { forceSequential: true };
|
|
547
|
+
}
|
|
548
|
+
async elementwise(a, b, op) {
|
|
549
|
+
return toParallelResult(await this.workerPool.elementwise(a, b, op, this.poolOptions(a.length, op)));
|
|
550
|
+
}
|
|
551
|
+
async scale(data, scalar) {
|
|
552
|
+
return toParallelResult(await this.workerPool.scale(data, scalar, this.poolOptions(data.length, "scale")));
|
|
553
|
+
}
|
|
554
|
+
async matmul(a, aRows, aCols, b, bCols) {
|
|
555
|
+
return toParallelResult(await this.workerPool.matmul(a, aRows, aCols, b, bCols, this.poolOptions(aRows * bCols, "matmul")));
|
|
556
|
+
}
|
|
557
|
+
async transpose(data, rows, cols) {
|
|
558
|
+
if (!this.shouldParallelize(data.length, "transpose")) {
|
|
559
|
+
if (data.length !== rows * cols) throw new Error(`transpose: data length ${data.length} !== rows*cols (${rows * cols})`);
|
|
560
|
+
const out = new Float64Array(rows * cols);
|
|
561
|
+
for (let i = 0; i < rows; i++) for (let j = 0; j < cols; j++) out[j * rows + i] = data[i * cols + j];
|
|
562
|
+
return this.seqResult(out);
|
|
563
|
+
}
|
|
564
|
+
return toParallelResult(await this.workerPool.transpose(data, rows, cols));
|
|
565
|
+
}
|
|
566
|
+
async map(data, fn) {
|
|
567
|
+
return toParallelResult(await this.workerPool.map(data, fn));
|
|
568
|
+
}
|
|
569
|
+
async reduce(data, fn, initial) {
|
|
570
|
+
return toParallelResult(await this.workerPool.reduce(data, fn, initial));
|
|
571
|
+
}
|
|
572
|
+
async filter(data, predicate) {
|
|
573
|
+
return toParallelResult(await this.workerPool.filter(data, predicate));
|
|
574
|
+
}
|
|
575
|
+
async minMax(data) {
|
|
576
|
+
if (!this.shouldParallelize(data.length, "minMax") && !this.shouldParallelize(data.length, "min")) {
|
|
577
|
+
let min = Infinity;
|
|
578
|
+
let max = -Infinity;
|
|
579
|
+
let minIdx = -1;
|
|
580
|
+
let maxIdx = -1;
|
|
581
|
+
for (let i = 0; i < data.length; i++) {
|
|
582
|
+
if (data[i] < min) {
|
|
583
|
+
min = data[i];
|
|
584
|
+
minIdx = i;
|
|
585
|
+
}
|
|
586
|
+
if (data[i] > max) {
|
|
587
|
+
max = data[i];
|
|
588
|
+
maxIdx = i;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return this.seqResult({
|
|
592
|
+
min,
|
|
593
|
+
max,
|
|
594
|
+
minIdx,
|
|
595
|
+
maxIdx
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
return toParallelResult(await this.workerPool.minMax(data));
|
|
599
|
+
}
|
|
600
|
+
async variance(data) {
|
|
601
|
+
if (!this.shouldParallelize(data.length, "variance")) {
|
|
602
|
+
const n = data.length;
|
|
603
|
+
const mean = n > 0 ? pairwiseSum(data) / n : 0;
|
|
604
|
+
const variance = n > 0 ? sumSquaredDeviations(data) / n : 0;
|
|
605
|
+
return this.seqResult({
|
|
606
|
+
mean,
|
|
607
|
+
variance,
|
|
608
|
+
std: Math.sqrt(variance)
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
return toParallelResult(await this.workerPool.variance(data));
|
|
612
|
+
}
|
|
613
|
+
async norm(data) {
|
|
614
|
+
if (!this.shouldParallelize(data.length, "norm")) return this.seqResult(norm2(data));
|
|
615
|
+
return toParallelResult(await this.workerPool.norm(data));
|
|
616
|
+
}
|
|
617
|
+
async distance(a, b) {
|
|
618
|
+
if (!this.shouldParallelize(a.length, "distance")) {
|
|
619
|
+
if (a.length !== b.length) throw new Error("distance: vectors must have the same length");
|
|
620
|
+
return this.seqResult(scaledDistance(a, b));
|
|
621
|
+
}
|
|
622
|
+
return toParallelResult(await this.workerPool.distance(a, b));
|
|
623
|
+
}
|
|
624
|
+
async histogram(data, bins, min, max) {
|
|
625
|
+
if (!this.shouldParallelize(data.length, "histogram")) {
|
|
626
|
+
let lo = min;
|
|
627
|
+
let hi = max;
|
|
628
|
+
if (lo === void 0 || hi === void 0) {
|
|
629
|
+
lo = Infinity;
|
|
630
|
+
hi = -Infinity;
|
|
631
|
+
for (let i = 0; i < data.length; i++) {
|
|
632
|
+
if (data[i] < lo) lo = data[i];
|
|
633
|
+
if (data[i] > hi) hi = data[i];
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
const counts = new Array(bins).fill(0);
|
|
637
|
+
const width = (hi - lo) / bins || 1;
|
|
638
|
+
for (let i = 0; i < data.length; i++) {
|
|
639
|
+
let b = Math.floor((data[i] - lo) / width);
|
|
640
|
+
if (b >= bins) b = bins - 1;
|
|
641
|
+
if (b < 0) b = 0;
|
|
642
|
+
counts[b]++;
|
|
643
|
+
}
|
|
644
|
+
return this.seqResult(counts);
|
|
645
|
+
}
|
|
646
|
+
return toParallelResult(await this.workerPool.histogram(data, bins, min, max));
|
|
647
|
+
}
|
|
648
|
+
async unary(data, fn) {
|
|
649
|
+
return toParallelResult(await this.workerPool.unary(data, fn, this.poolOptions(data.length, fn)));
|
|
650
|
+
}
|
|
651
|
+
async applyKernel(data, fnSource) {
|
|
652
|
+
return toParallelResult(await this.workerPool.applyKernel(data, fnSource));
|
|
653
|
+
}
|
|
654
|
+
async applyKernel2(a, b, fnSource) {
|
|
655
|
+
return toParallelResult(await this.workerPool.applyKernel2(a, b, fnSource));
|
|
656
|
+
}
|
|
657
|
+
async integrateFanOut(fnSource, a, b, workerCount, nodes, weights) {
|
|
658
|
+
const h = (b - a) / workerCount;
|
|
659
|
+
const tasks = [];
|
|
660
|
+
for (let k = 0; k < workerCount; k++) {
|
|
661
|
+
const subA = a + k * h;
|
|
662
|
+
const subB = a + (k + 1) * h;
|
|
663
|
+
tasks.push(this.workerPool.exec("integrateChunk", [
|
|
664
|
+
fnSource,
|
|
665
|
+
subA,
|
|
666
|
+
subB,
|
|
667
|
+
nodes,
|
|
668
|
+
weights
|
|
669
|
+
]));
|
|
670
|
+
}
|
|
671
|
+
return (await Promise.all(tasks)).reduce((acc, v) => acc + v, 0);
|
|
672
|
+
}
|
|
673
|
+
async distributionSampleFanOut(distName, params, n, baseSeed, workerCount) {
|
|
674
|
+
const SPLIT_MIX_CONST = 2654435769;
|
|
675
|
+
const baseChunkSize = Math.floor(n / workerCount);
|
|
676
|
+
const tasks = [];
|
|
677
|
+
for (let k = 0; k < workerCount; k++) {
|
|
678
|
+
const chunkCount = k < workerCount - 1 ? baseChunkSize : n - baseChunkSize * (workerCount - 1);
|
|
679
|
+
const chunkSeed = (baseSeed ^ (Math.imul(k, SPLIT_MIX_CONST) | 0)) >>> 0;
|
|
680
|
+
tasks.push(this.workerPool.exec("sampleChunk", [
|
|
681
|
+
distName,
|
|
682
|
+
params,
|
|
683
|
+
chunkSeed,
|
|
684
|
+
chunkCount
|
|
685
|
+
]));
|
|
686
|
+
}
|
|
687
|
+
const buffers = await Promise.all(tasks);
|
|
688
|
+
const out = new Float64Array(n);
|
|
689
|
+
let offset = 0;
|
|
690
|
+
for (const buf of buffers) {
|
|
691
|
+
const chunk = new Float64Array(buf);
|
|
692
|
+
out.set(chunk, offset);
|
|
693
|
+
offset += chunk.length;
|
|
694
|
+
}
|
|
695
|
+
return out;
|
|
696
|
+
}
|
|
697
|
+
async fftBatch(real, imag, frameCount, frameLength, inverse = false) {
|
|
698
|
+
return toParallelResult(await this.workerPool.fftBatch(real, imag, frameCount, frameLength, inverse));
|
|
699
|
+
}
|
|
700
|
+
async distanceMatrix(points, n, dim) {
|
|
701
|
+
return toParallelResult(await this.workerPool.distanceMatrix(points, n, dim));
|
|
702
|
+
}
|
|
703
|
+
async abs(data) {
|
|
704
|
+
return this.unary(data, "abs");
|
|
705
|
+
}
|
|
706
|
+
async sqrt(data) {
|
|
707
|
+
return this.unary(data, "sqrt");
|
|
708
|
+
}
|
|
709
|
+
async exp(data) {
|
|
710
|
+
return this.unary(data, "exp");
|
|
711
|
+
}
|
|
712
|
+
async log(data) {
|
|
713
|
+
return this.unary(data, "log");
|
|
714
|
+
}
|
|
715
|
+
async sin(data) {
|
|
716
|
+
return this.unary(data, "sin");
|
|
717
|
+
}
|
|
718
|
+
async cos(data) {
|
|
719
|
+
return this.unary(data, "cos");
|
|
720
|
+
}
|
|
721
|
+
async tan(data) {
|
|
722
|
+
return this.unary(data, "tan");
|
|
723
|
+
}
|
|
724
|
+
async negate(data) {
|
|
725
|
+
return this.unary(data, "negate");
|
|
726
|
+
}
|
|
727
|
+
async square(data) {
|
|
728
|
+
return this.unary(data, "square");
|
|
729
|
+
}
|
|
730
|
+
async matvec(matrix, rows, cols, vector) {
|
|
731
|
+
if (!this.shouldParallelize(matrix.length, "matvec")) {
|
|
732
|
+
if (matrix.length !== rows * cols) throw new Error(`matvec: matrix length ${matrix.length} !== rows*cols (${rows * cols})`);
|
|
733
|
+
if (vector.length !== cols) throw new Error(`matvec: vector length ${vector.length} !== cols (${cols})`);
|
|
734
|
+
const out = new Float64Array(rows);
|
|
735
|
+
for (let i = 0; i < rows; i++) {
|
|
736
|
+
let s = 0;
|
|
737
|
+
for (let j = 0; j < cols; j++) s += matrix[i * cols + j] * vector[j];
|
|
738
|
+
out[i] = s;
|
|
739
|
+
}
|
|
740
|
+
return this.seqResult(out);
|
|
741
|
+
}
|
|
742
|
+
return toParallelResult(await this.workerPool.matvec(matrix, rows, cols, vector));
|
|
743
|
+
}
|
|
744
|
+
async outer(a, b) {
|
|
745
|
+
if (!this.shouldParallelize(a.length * b.length, "outer")) {
|
|
746
|
+
const out = new Float64Array(a.length * b.length);
|
|
747
|
+
for (let i = 0; i < a.length; i++) for (let j = 0; j < b.length; j++) out[i * b.length + j] = a[i] * b[j];
|
|
748
|
+
return this.seqResult(out);
|
|
749
|
+
}
|
|
750
|
+
return toParallelResult(await this.workerPool.outer(a, b));
|
|
751
|
+
}
|
|
752
|
+
async find(data, predicate) {
|
|
753
|
+
return toParallelResult(await this.workerPool.find(data, predicate));
|
|
754
|
+
}
|
|
755
|
+
async sort(data, compare) {
|
|
756
|
+
return toParallelResult(await this.workerPool.sort(data, compare));
|
|
757
|
+
}
|
|
758
|
+
async add(a, b) {
|
|
759
|
+
return this.elementwise(a, b, "add");
|
|
760
|
+
}
|
|
761
|
+
async subtract(a, b) {
|
|
762
|
+
return this.elementwise(a, b, "subtract");
|
|
763
|
+
}
|
|
764
|
+
async multiply(a, b) {
|
|
765
|
+
return this.elementwise(a, b, "multiply");
|
|
766
|
+
}
|
|
767
|
+
async divide(a, b) {
|
|
768
|
+
return this.elementwise(a, b, "divide");
|
|
769
|
+
}
|
|
770
|
+
async pow(a, b) {
|
|
771
|
+
if (a.length !== b.length) throw new Error(`Array lengths must match: ${a.length} vs ${b.length}`);
|
|
772
|
+
if (this.shouldParallelize(a.length, "pow")) return this.applyKernel2(a, b, "(a, b) => Math.pow(a, b)");
|
|
773
|
+
const start = performance.now();
|
|
774
|
+
const result = new Float64Array(a.length);
|
|
775
|
+
for (let i = 0; i < a.length; i++) result[i] = a[i] ** b[i];
|
|
776
|
+
return {
|
|
777
|
+
result,
|
|
778
|
+
duration: performance.now() - start,
|
|
779
|
+
chunks: 1,
|
|
780
|
+
parallelized: false
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
async sign(data) {
|
|
784
|
+
if (this.shouldParallelize(data.length, "sign")) return this.applyKernel(data, "(x) => Math.sign(x)");
|
|
785
|
+
const start = performance.now();
|
|
786
|
+
const result = new Float64Array(data.length);
|
|
787
|
+
for (let i = 0; i < data.length; i++) result[i] = Math.sign(data[i]);
|
|
788
|
+
return {
|
|
789
|
+
result,
|
|
790
|
+
duration: performance.now() - start,
|
|
791
|
+
chunks: 1,
|
|
792
|
+
parallelized: false
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
async tensordot(a, aShape, b, bShape, axesA, axesB) {
|
|
796
|
+
if (axesA.length !== axesB.length) throw new Error(`tensordot: axesA.length (${axesA.length}) !== axesB.length (${axesB.length})`);
|
|
797
|
+
const aRank = aShape.length;
|
|
798
|
+
const bRank = bShape.length;
|
|
799
|
+
const aContracted = /* @__PURE__ */ new Set();
|
|
800
|
+
const bContracted = /* @__PURE__ */ new Set();
|
|
801
|
+
for (let i = 0; i < axesA.length; i++) {
|
|
802
|
+
const ax = axesA[i];
|
|
803
|
+
const bx = axesB[i];
|
|
804
|
+
if (!Number.isInteger(ax) || ax < 0 || ax >= aRank) throw new Error(`tensordot: axesA[${i}]=${ax} is out of range for aShape (rank ${aRank})`);
|
|
805
|
+
if (!Number.isInteger(bx) || bx < 0 || bx >= bRank) throw new Error(`tensordot: axesB[${i}]=${bx} is out of range for bShape (rank ${bRank})`);
|
|
806
|
+
if (aShape[ax] !== bShape[bx]) throw new Error(`tensordot: dimension mismatch on contracted axes: aShape[${ax}]=${aShape[ax]} vs bShape[${bx}]=${bShape[bx]}`);
|
|
807
|
+
aContracted.add(ax);
|
|
808
|
+
bContracted.add(bx);
|
|
809
|
+
}
|
|
810
|
+
const resultShape = [];
|
|
811
|
+
for (let i = 0; i < aRank; i++) if (!aContracted.has(i)) resultShape.push(aShape[i]);
|
|
812
|
+
for (let i = 0; i < bRank; i++) if (!bContracted.has(i)) resultShape.push(bShape[i]);
|
|
813
|
+
const resultSize = resultShape.reduce((acc, d) => acc * d, 1);
|
|
814
|
+
const contractedVolume = Array.from(axesA).reduce((acc, ax) => acc * aShape[ax], 1);
|
|
815
|
+
const start = performance.now();
|
|
816
|
+
if (this.shouldParallelize(contractedVolume, "tensordot") && resultSize > 0) {
|
|
817
|
+
const chunkSize = this.config.chunkSize;
|
|
818
|
+
const chunks = [];
|
|
819
|
+
for (let i = 0; i < resultSize; i += chunkSize) chunks.push([i, Math.min(i + chunkSize, resultSize)]);
|
|
820
|
+
const aShapeArr = Array.from(aShape);
|
|
821
|
+
const bShapeArr = Array.from(bShape);
|
|
822
|
+
const axesAArr = Array.from(axesA);
|
|
823
|
+
const axesBArr = Array.from(axesB);
|
|
824
|
+
const chunkResults = await Promise.all(chunks.map(([outStart, outEnd]) => this.workerPool.exec("tensordotChunk", [
|
|
825
|
+
a.buffer,
|
|
826
|
+
aShapeArr,
|
|
827
|
+
b.buffer,
|
|
828
|
+
bShapeArr,
|
|
829
|
+
axesAArr,
|
|
830
|
+
axesBArr,
|
|
831
|
+
resultShape,
|
|
832
|
+
outStart,
|
|
833
|
+
outEnd
|
|
834
|
+
])));
|
|
835
|
+
const data = new Float64Array(resultSize);
|
|
836
|
+
let offset = 0;
|
|
837
|
+
for (const buf of chunkResults) {
|
|
838
|
+
const chunk = new Float64Array(buf);
|
|
839
|
+
data.set(chunk, offset);
|
|
840
|
+
offset += chunk.length;
|
|
841
|
+
}
|
|
842
|
+
return {
|
|
843
|
+
result: {
|
|
844
|
+
data,
|
|
845
|
+
shape: resultShape
|
|
846
|
+
},
|
|
847
|
+
duration: performance.now() - start,
|
|
848
|
+
chunks: chunks.length,
|
|
849
|
+
parallelized: true
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
return {
|
|
853
|
+
result: {
|
|
854
|
+
data: resultSize === 0 ? /* @__PURE__ */ new Float64Array(0) : tensordotChunkKernel(a, aShape, b, bShape, axesA, axesB, resultShape, 0, resultSize),
|
|
855
|
+
shape: resultShape
|
|
856
|
+
},
|
|
857
|
+
duration: performance.now() - start,
|
|
858
|
+
chunks: 1,
|
|
859
|
+
parallelized: false
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
async mean(data) {
|
|
863
|
+
const result = await this.variance(data);
|
|
864
|
+
return {
|
|
865
|
+
...result,
|
|
866
|
+
result: result.result.mean
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
async std(data) {
|
|
870
|
+
const result = await this.variance(data);
|
|
871
|
+
return {
|
|
872
|
+
...result,
|
|
873
|
+
result: result.result.std
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
async min(data) {
|
|
877
|
+
const result = await this.minMax(data);
|
|
878
|
+
return {
|
|
879
|
+
...result,
|
|
880
|
+
result: result.result.min
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
async max(data) {
|
|
884
|
+
const result = await this.minMax(data);
|
|
885
|
+
return {
|
|
886
|
+
...result,
|
|
887
|
+
result: result.result.max
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
async bitAnd(a, b) {
|
|
891
|
+
if (a.length !== b.length) throw new Error(`Array lengths must match: ${a.length} vs ${b.length}`);
|
|
892
|
+
if (this.shouldParallelize(a.length, "bitAnd")) return toParallelResult(await this.workerPool.bitwiseBinary(a, b, "bitAnd"));
|
|
893
|
+
const start = performance.now();
|
|
894
|
+
return {
|
|
895
|
+
result: bitAnd(a, b),
|
|
896
|
+
duration: performance.now() - start,
|
|
897
|
+
chunks: 1,
|
|
898
|
+
parallelized: false
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
async bitOr(a, b) {
|
|
902
|
+
if (a.length !== b.length) throw new Error(`Array lengths must match: ${a.length} vs ${b.length}`);
|
|
903
|
+
if (this.shouldParallelize(a.length, "bitOr")) return toParallelResult(await this.workerPool.bitwiseBinary(a, b, "bitOr"));
|
|
904
|
+
const start = performance.now();
|
|
905
|
+
return {
|
|
906
|
+
result: bitOr(a, b),
|
|
907
|
+
duration: performance.now() - start,
|
|
908
|
+
chunks: 1,
|
|
909
|
+
parallelized: false
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
async bitXor(a, b) {
|
|
913
|
+
if (a.length !== b.length) throw new Error(`Array lengths must match: ${a.length} vs ${b.length}`);
|
|
914
|
+
if (this.shouldParallelize(a.length, "bitXor")) return toParallelResult(await this.workerPool.bitwiseBinary(a, b, "bitXor"));
|
|
915
|
+
const start = performance.now();
|
|
916
|
+
return {
|
|
917
|
+
result: bitXor(a, b),
|
|
918
|
+
duration: performance.now() - start,
|
|
919
|
+
chunks: 1,
|
|
920
|
+
parallelized: false
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
async bitNot(a) {
|
|
924
|
+
if (this.shouldParallelize(a.length, "bitNot")) return toParallelResult(await this.workerPool.bitwiseNot(a));
|
|
925
|
+
const start = performance.now();
|
|
926
|
+
return {
|
|
927
|
+
result: bitNot(a),
|
|
928
|
+
duration: performance.now() - start,
|
|
929
|
+
chunks: 1,
|
|
930
|
+
parallelized: false
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
async leftShift(a, b) {
|
|
934
|
+
if (typeof b !== "number" && a.length !== b.length) throw new Error(`Array lengths must match: ${a.length} vs ${b.length}`);
|
|
935
|
+
if (this.shouldParallelize(a.length, "leftShift")) return toParallelResult(typeof b === "number" ? await this.workerPool.bitwiseScalar(a, b, "leftShift") : await this.workerPool.bitwiseBinary(a, b, "leftShift"));
|
|
936
|
+
const start = performance.now();
|
|
937
|
+
return {
|
|
938
|
+
result: leftShift(a, b),
|
|
939
|
+
duration: performance.now() - start,
|
|
940
|
+
chunks: 1,
|
|
941
|
+
parallelized: false
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
async rightArithShift(a, b) {
|
|
945
|
+
if (typeof b !== "number" && a.length !== b.length) throw new Error(`Array lengths must match: ${a.length} vs ${b.length}`);
|
|
946
|
+
if (this.shouldParallelize(a.length, "rightArithShift")) return toParallelResult(typeof b === "number" ? await this.workerPool.bitwiseScalar(a, b, "rightArithShift") : await this.workerPool.bitwiseBinary(a, b, "rightArithShift"));
|
|
947
|
+
const start = performance.now();
|
|
948
|
+
return {
|
|
949
|
+
result: rightArithShift(a, b),
|
|
950
|
+
duration: performance.now() - start,
|
|
951
|
+
chunks: 1,
|
|
952
|
+
parallelized: false
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
async rightLogShift(a, b) {
|
|
956
|
+
if (typeof b !== "number" && a.length !== b.length) throw new Error(`Array lengths must match: ${a.length} vs ${b.length}`);
|
|
957
|
+
if (this.shouldParallelize(a.length, "rightLogShift")) return toParallelResult(typeof b === "number" ? await this.workerPool.bitwiseScalar(a, b, "rightLogShift") : await this.workerPool.bitwiseBinary(a, b, "rightLogShift"));
|
|
958
|
+
const start = performance.now();
|
|
959
|
+
return {
|
|
960
|
+
result: rightLogShift(a, b),
|
|
961
|
+
duration: performance.now() - start,
|
|
962
|
+
chunks: 1,
|
|
963
|
+
parallelized: false
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
async terminate(force = false) {
|
|
967
|
+
await this.workerPool.terminate(force);
|
|
968
|
+
}
|
|
969
|
+
updateConfig(config) {
|
|
970
|
+
this.config = {
|
|
971
|
+
...this.config,
|
|
972
|
+
...config,
|
|
973
|
+
thresholdByOp: mergeThresholds(this.config.thresholdByOp, config.thresholdByOp)
|
|
974
|
+
};
|
|
975
|
+
this.workerPool.updateConfig(toWorkerConfig(this.config));
|
|
976
|
+
}
|
|
977
|
+
getConfig() {
|
|
978
|
+
return { ...this.config };
|
|
979
|
+
}
|
|
980
|
+
get workerCount() {
|
|
981
|
+
return this.config.maxWorkers;
|
|
982
|
+
}
|
|
983
|
+
getWorkerPool() {
|
|
984
|
+
return this.workerPool;
|
|
985
|
+
}
|
|
1493
986
|
};
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
987
|
+
const computePool = new ComputePool();
|
|
988
|
+
//#endregion
|
|
989
|
+
//#region src/operations/matmul.ts
|
|
1497
990
|
async function parallelMatmul(a, aRows, aCols, b, bCols, options = {}) {
|
|
1498
|
-
|
|
1499
|
-
return pool.matmul(a, aRows, aCols, b, bCols);
|
|
991
|
+
return (options.pool ?? computePool).matmul(a, aRows, aCols, b, bCols);
|
|
1500
992
|
}
|
|
1501
993
|
async function parallelMatvec(matrix, rows, cols, vector, options = {}) {
|
|
1502
|
-
|
|
1503
|
-
return pool.matvec(matrix, rows, cols, vector);
|
|
994
|
+
return (options.pool ?? computePool).matvec(matrix, rows, cols, vector);
|
|
1504
995
|
}
|
|
1505
996
|
async function parallelTranspose(data, rows, cols, options = {}) {
|
|
1506
|
-
|
|
1507
|
-
return pool.transpose(data, rows, cols);
|
|
997
|
+
return (options.pool ?? computePool).transpose(data, rows, cols);
|
|
1508
998
|
}
|
|
1509
999
|
async function parallelOuter(a, b, options = {}) {
|
|
1510
|
-
|
|
1511
|
-
return pool.outer(a, b);
|
|
1000
|
+
return (options.pool ?? computePool).outer(a, b);
|
|
1512
1001
|
}
|
|
1513
1002
|
async function parallelDot(a, b, options = {}) {
|
|
1514
|
-
|
|
1515
|
-
return pool.dot(a, b);
|
|
1003
|
+
return (options.pool ?? computePool).dot(a, b);
|
|
1516
1004
|
}
|
|
1517
|
-
|
|
1518
|
-
|
|
1005
|
+
//#endregion
|
|
1006
|
+
//#region src/operations/elementwise.ts
|
|
1519
1007
|
async function parallelAdd(a, b, options = {}) {
|
|
1520
|
-
|
|
1521
|
-
return pool.add(a, b);
|
|
1008
|
+
return (options.pool ?? computePool).add(a, b);
|
|
1522
1009
|
}
|
|
1523
1010
|
async function parallelSubtract(a, b, options = {}) {
|
|
1524
|
-
|
|
1525
|
-
return pool.subtract(a, b);
|
|
1011
|
+
return (options.pool ?? computePool).subtract(a, b);
|
|
1526
1012
|
}
|
|
1527
1013
|
async function parallelMultiply(a, b, options = {}) {
|
|
1528
|
-
|
|
1529
|
-
return pool.multiply(a, b);
|
|
1014
|
+
return (options.pool ?? computePool).multiply(a, b);
|
|
1530
1015
|
}
|
|
1531
1016
|
async function parallelDivide(a, b, options = {}) {
|
|
1532
|
-
|
|
1533
|
-
return pool.divide(a, b);
|
|
1017
|
+
return (options.pool ?? computePool).divide(a, b);
|
|
1534
1018
|
}
|
|
1535
1019
|
async function parallelScale(data, scalar, options = {}) {
|
|
1536
|
-
|
|
1537
|
-
return pool.scale(data, scalar);
|
|
1020
|
+
return (options.pool ?? computePool).scale(data, scalar);
|
|
1538
1021
|
}
|
|
1539
1022
|
async function parallelAbs(data, options = {}) {
|
|
1540
|
-
|
|
1541
|
-
return pool.abs(data);
|
|
1023
|
+
return (options.pool ?? computePool).abs(data);
|
|
1542
1024
|
}
|
|
1543
1025
|
async function parallelNegate(data, options = {}) {
|
|
1544
|
-
|
|
1545
|
-
return pool.negate(data);
|
|
1026
|
+
return (options.pool ?? computePool).negate(data);
|
|
1546
1027
|
}
|
|
1547
1028
|
async function parallelSquare(data, options = {}) {
|
|
1548
|
-
|
|
1549
|
-
return pool.square(data);
|
|
1029
|
+
return (options.pool ?? computePool).square(data);
|
|
1550
1030
|
}
|
|
1551
1031
|
async function parallelSqrt(data, options = {}) {
|
|
1552
|
-
|
|
1553
|
-
return pool.sqrt(data);
|
|
1032
|
+
return (options.pool ?? computePool).sqrt(data);
|
|
1554
1033
|
}
|
|
1555
1034
|
async function parallelExp(data, options = {}) {
|
|
1556
|
-
|
|
1557
|
-
return pool.exp(data);
|
|
1035
|
+
return (options.pool ?? computePool).exp(data);
|
|
1558
1036
|
}
|
|
1559
1037
|
async function parallelLog(data, options = {}) {
|
|
1560
|
-
|
|
1561
|
-
return pool.log(data);
|
|
1038
|
+
return (options.pool ?? computePool).log(data);
|
|
1562
1039
|
}
|
|
1563
1040
|
async function parallelSin(data, options = {}) {
|
|
1564
|
-
|
|
1565
|
-
return pool.sin(data);
|
|
1041
|
+
return (options.pool ?? computePool).sin(data);
|
|
1566
1042
|
}
|
|
1567
1043
|
async function parallelCos(data, options = {}) {
|
|
1568
|
-
|
|
1569
|
-
return pool.cos(data);
|
|
1044
|
+
return (options.pool ?? computePool).cos(data);
|
|
1570
1045
|
}
|
|
1571
1046
|
async function parallelTan(data, options = {}) {
|
|
1572
|
-
|
|
1573
|
-
return pool.tan(data);
|
|
1047
|
+
return (options.pool ?? computePool).tan(data);
|
|
1574
1048
|
}
|
|
1575
1049
|
async function parallelElementwise(a, b, op, options = {}) {
|
|
1576
|
-
|
|
1577
|
-
return pool.elementwise(a, b, op);
|
|
1050
|
+
return (options.pool ?? computePool).elementwise(a, b, op);
|
|
1578
1051
|
}
|
|
1579
1052
|
async function parallelUnary(data, fn, options = {}) {
|
|
1580
|
-
|
|
1581
|
-
return pool.unary(data, fn);
|
|
1053
|
+
return (options.pool ?? computePool).unary(data, fn);
|
|
1582
1054
|
}
|
|
1583
|
-
|
|
1584
|
-
|
|
1055
|
+
//#endregion
|
|
1056
|
+
//#region src/operations/reduce.ts
|
|
1585
1057
|
async function parallelSum(data, options = {}) {
|
|
1586
|
-
|
|
1587
|
-
return pool.sum(data);
|
|
1058
|
+
return (options.pool ?? computePool).sum(data);
|
|
1588
1059
|
}
|
|
1589
1060
|
async function parallelMean(data, options = {}) {
|
|
1590
|
-
|
|
1591
|
-
return pool.mean(data);
|
|
1061
|
+
return (options.pool ?? computePool).mean(data);
|
|
1592
1062
|
}
|
|
1593
1063
|
async function parallelMin(data, options = {}) {
|
|
1594
|
-
|
|
1595
|
-
return pool.min(data);
|
|
1064
|
+
return (options.pool ?? computePool).min(data);
|
|
1596
1065
|
}
|
|
1597
1066
|
async function parallelMax(data, options = {}) {
|
|
1598
|
-
|
|
1599
|
-
return pool.max(data);
|
|
1067
|
+
return (options.pool ?? computePool).max(data);
|
|
1600
1068
|
}
|
|
1601
1069
|
async function parallelMinMax(data, options = {}) {
|
|
1602
|
-
|
|
1603
|
-
return pool.minMax(data);
|
|
1070
|
+
return (options.pool ?? computePool).minMax(data);
|
|
1604
1071
|
}
|
|
1605
1072
|
async function parallelVariance(data, options = {}) {
|
|
1606
|
-
|
|
1607
|
-
return pool.variance(data);
|
|
1073
|
+
return (options.pool ?? computePool).variance(data);
|
|
1608
1074
|
}
|
|
1609
1075
|
async function parallelStd(data, options = {}) {
|
|
1610
|
-
|
|
1611
|
-
return pool.std(data);
|
|
1076
|
+
return (options.pool ?? computePool).std(data);
|
|
1612
1077
|
}
|
|
1613
1078
|
async function parallelNorm(data, options = {}) {
|
|
1614
|
-
|
|
1615
|
-
return pool.norm(data);
|
|
1079
|
+
return (options.pool ?? computePool).norm(data);
|
|
1616
1080
|
}
|
|
1617
1081
|
async function parallelDistance(a, b, options = {}) {
|
|
1618
|
-
|
|
1619
|
-
return pool.distance(a, b);
|
|
1082
|
+
return (options.pool ?? computePool).distance(a, b);
|
|
1620
1083
|
}
|
|
1621
1084
|
async function parallelHistogram(data, bins, min, max, options = {}) {
|
|
1622
|
-
|
|
1623
|
-
return pool.histogram(data, bins, min, max);
|
|
1085
|
+
return (options.pool ?? computePool).histogram(data, bins, min, max);
|
|
1624
1086
|
}
|
|
1625
1087
|
async function parallelReduce(data, fn, initial, options = {}) {
|
|
1626
|
-
|
|
1627
|
-
return pool.reduce(data, fn, initial);
|
|
1088
|
+
return (options.pool ?? computePool).reduce(data, fn, initial);
|
|
1628
1089
|
}
|
|
1629
|
-
|
|
1630
|
-
|
|
1090
|
+
//#endregion
|
|
1091
|
+
//#region src/operations/map.ts
|
|
1631
1092
|
async function parallelMap(data, fn, options = {}) {
|
|
1632
|
-
|
|
1633
|
-
return pool.map(data, fn);
|
|
1093
|
+
return (options.pool ?? computePool).map(data, fn);
|
|
1634
1094
|
}
|
|
1635
1095
|
async function parallelFilter(data, predicate, options = {}) {
|
|
1636
|
-
|
|
1637
|
-
return pool.filter(data, predicate);
|
|
1096
|
+
return (options.pool ?? computePool).filter(data, predicate);
|
|
1638
1097
|
}
|
|
1639
1098
|
async function parallelFind(data, predicate, options = {}) {
|
|
1640
|
-
|
|
1641
|
-
return pool.find(data, predicate);
|
|
1099
|
+
return (options.pool ?? computePool).find(data, predicate);
|
|
1642
1100
|
}
|
|
1643
1101
|
async function parallelSort(data, compare, options = {}) {
|
|
1644
|
-
|
|
1645
|
-
return pool.sort(data, compare);
|
|
1102
|
+
return (options.pool ?? computePool).sort(data, compare);
|
|
1646
1103
|
}
|
|
1647
1104
|
async function parallelForEach(data, fn, options = {}) {
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
result: result.result
|
|
1656
|
-
};
|
|
1105
|
+
const result = await (options.pool ?? computePool).map(data, (item) => {
|
|
1106
|
+
fn(item);
|
|
1107
|
+
});
|
|
1108
|
+
return {
|
|
1109
|
+
...result,
|
|
1110
|
+
result: result.result
|
|
1111
|
+
};
|
|
1657
1112
|
}
|
|
1658
1113
|
async function parallelSome(data, predicate, options = {}) {
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
};
|
|
1114
|
+
const findResult = await (options.pool ?? computePool).find(data, predicate);
|
|
1115
|
+
return {
|
|
1116
|
+
...findResult,
|
|
1117
|
+
result: findResult.result.found
|
|
1118
|
+
};
|
|
1665
1119
|
}
|
|
1666
1120
|
async function parallelEvery(data, predicate, options = {}) {
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
};
|
|
1121
|
+
const findResult = await (options.pool ?? computePool).find(data, (item) => !predicate(item));
|
|
1122
|
+
return {
|
|
1123
|
+
...findResult,
|
|
1124
|
+
result: !findResult.result.found
|
|
1125
|
+
};
|
|
1673
1126
|
}
|
|
1674
1127
|
async function parallelCount(data, predicate, options = {}) {
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
// src/strategies/threshold.ts
|
|
1128
|
+
const pool = options.pool ?? computePool;
|
|
1129
|
+
if (!predicate) return {
|
|
1130
|
+
result: data.length,
|
|
1131
|
+
duration: 0,
|
|
1132
|
+
chunks: 1,
|
|
1133
|
+
parallelized: false
|
|
1134
|
+
};
|
|
1135
|
+
const filterResult = await pool.filter(data, predicate);
|
|
1136
|
+
return {
|
|
1137
|
+
...filterResult,
|
|
1138
|
+
result: filterResult.result.length
|
|
1139
|
+
};
|
|
1140
|
+
}
|
|
1141
|
+
//#endregion
|
|
1142
|
+
//#region src/strategies/threshold.ts
|
|
1692
1143
|
function opThreshold(op, fallback) {
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
}
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
// ~10K elements
|
|
1705
|
-
sort: 5e3,
|
|
1706
|
-
// ~5K elements
|
|
1707
|
-
decomposition: 2500,
|
|
1708
|
-
// ~50x50 matrix (no single canonical op; heuristic)
|
|
1709
|
-
general: 5e4
|
|
1710
|
-
// Default ~50K
|
|
1144
|
+
const raw = DEFAULT_THRESHOLD_BY_OP[op];
|
|
1145
|
+
return typeof raw === "number" ? raw : fallback;
|
|
1146
|
+
}
|
|
1147
|
+
const DEFAULT_THRESHOLDS = {
|
|
1148
|
+
matmul: opThreshold("matmul", 4096),
|
|
1149
|
+
elementwise: 5e4,
|
|
1150
|
+
reduce: 1e5,
|
|
1151
|
+
map: 1e4,
|
|
1152
|
+
sort: 5e3,
|
|
1153
|
+
decomposition: 2500,
|
|
1154
|
+
general: 5e4
|
|
1711
1155
|
};
|
|
1712
1156
|
var ThresholdDispatcher = class {
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
shouldParallelize(elementCount, category = "general") {
|
|
1779
|
-
return this.dispatch(elementCount, category).mode === "parallel";
|
|
1780
|
-
}
|
|
1781
|
-
/**
|
|
1782
|
-
* Calculate optimal chunk count based on operation and data size
|
|
1783
|
-
*/
|
|
1784
|
-
calculateChunks(elementCount, category = "general") {
|
|
1785
|
-
const dispatch2 = this.dispatch(elementCount, category);
|
|
1786
|
-
if (dispatch2.mode === "sequential") {
|
|
1787
|
-
return 1;
|
|
1788
|
-
}
|
|
1789
|
-
const stats = this.pool.stats();
|
|
1790
|
-
const workerCount = Math.max(1, stats.totalWorkers);
|
|
1791
|
-
switch (category) {
|
|
1792
|
-
case "matmul":
|
|
1793
|
-
case "decomposition":
|
|
1794
|
-
return workerCount;
|
|
1795
|
-
case "elementwise":
|
|
1796
|
-
case "map":
|
|
1797
|
-
return Math.min(workerCount * 2, Math.ceil(elementCount / 1e4));
|
|
1798
|
-
case "reduce":
|
|
1799
|
-
return Math.min(workerCount, Math.ceil(elementCount / 5e4));
|
|
1800
|
-
case "sort":
|
|
1801
|
-
return Math.min(workerCount, 8);
|
|
1802
|
-
default:
|
|
1803
|
-
return workerCount;
|
|
1804
|
-
}
|
|
1805
|
-
}
|
|
1157
|
+
thresholds;
|
|
1158
|
+
pool;
|
|
1159
|
+
constructor(thresholds = {}, pool) {
|
|
1160
|
+
this.thresholds = {
|
|
1161
|
+
...DEFAULT_THRESHOLDS,
|
|
1162
|
+
...thresholds
|
|
1163
|
+
};
|
|
1164
|
+
this.pool = pool ?? computePool;
|
|
1165
|
+
}
|
|
1166
|
+
getThreshold(category) {
|
|
1167
|
+
return this.thresholds[category];
|
|
1168
|
+
}
|
|
1169
|
+
setThresholds(thresholds) {
|
|
1170
|
+
this.thresholds = {
|
|
1171
|
+
...this.thresholds,
|
|
1172
|
+
...thresholds
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
getThresholds() {
|
|
1176
|
+
return { ...this.thresholds };
|
|
1177
|
+
}
|
|
1178
|
+
dispatch(elementCount, category = "general") {
|
|
1179
|
+
const threshold = this.thresholds[category];
|
|
1180
|
+
if (!this.pool.isReady()) return {
|
|
1181
|
+
mode: "sequential",
|
|
1182
|
+
reason: "Worker pool not initialized",
|
|
1183
|
+
threshold,
|
|
1184
|
+
elementCount
|
|
1185
|
+
};
|
|
1186
|
+
if (!this.pool.getConfig().enabled) return {
|
|
1187
|
+
mode: "sequential",
|
|
1188
|
+
reason: "Parallel processing disabled",
|
|
1189
|
+
threshold,
|
|
1190
|
+
elementCount
|
|
1191
|
+
};
|
|
1192
|
+
if (elementCount < threshold) return {
|
|
1193
|
+
mode: "sequential",
|
|
1194
|
+
reason: `Element count (${elementCount}) below threshold (${threshold})`,
|
|
1195
|
+
threshold,
|
|
1196
|
+
elementCount
|
|
1197
|
+
};
|
|
1198
|
+
return {
|
|
1199
|
+
mode: "parallel",
|
|
1200
|
+
reason: `Element count (${elementCount}) exceeds threshold (${threshold})`,
|
|
1201
|
+
threshold,
|
|
1202
|
+
elementCount
|
|
1203
|
+
};
|
|
1204
|
+
}
|
|
1205
|
+
shouldParallelize(elementCount, category = "general") {
|
|
1206
|
+
return this.dispatch(elementCount, category).mode === "parallel";
|
|
1207
|
+
}
|
|
1208
|
+
calculateChunks(elementCount, category = "general") {
|
|
1209
|
+
if (this.dispatch(elementCount, category).mode === "sequential") return 1;
|
|
1210
|
+
const stats = this.pool.stats();
|
|
1211
|
+
const workerCount = Math.max(1, stats.totalWorkers);
|
|
1212
|
+
switch (category) {
|
|
1213
|
+
case "matmul":
|
|
1214
|
+
case "decomposition": return workerCount;
|
|
1215
|
+
case "elementwise":
|
|
1216
|
+
case "map": return Math.min(workerCount * 2, Math.ceil(elementCount / 1e4));
|
|
1217
|
+
case "reduce": return Math.min(workerCount, Math.ceil(elementCount / 5e4));
|
|
1218
|
+
case "sort": return Math.min(workerCount, 8);
|
|
1219
|
+
default: return workerCount;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1806
1222
|
};
|
|
1807
|
-
|
|
1808
|
-
function
|
|
1809
|
-
|
|
1223
|
+
const thresholdDispatcher = new ThresholdDispatcher();
|
|
1224
|
+
function shouldParallelize$1(elementCount, category = "general") {
|
|
1225
|
+
return thresholdDispatcher.shouldParallelize(elementCount, category);
|
|
1810
1226
|
}
|
|
1811
1227
|
function dispatch(elementCount, category = "general") {
|
|
1812
|
-
|
|
1228
|
+
return thresholdDispatcher.dispatch(elementCount, category);
|
|
1813
1229
|
}
|
|
1814
1230
|
function calculateChunks(elementCount, category = "general") {
|
|
1815
|
-
|
|
1816
|
-
}
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
DEFAULT_POOL_CONFIG,
|
|
1820
|
-
DEFAULT_THRESHOLDS,
|
|
1821
|
-
DEFAULT_THRESHOLD_BY_OP,
|
|
1822
|
-
ThresholdDispatcher,
|
|
1823
|
-
Transfer,
|
|
1824
|
-
bitAnd,
|
|
1825
|
-
bitNot,
|
|
1826
|
-
bitOr,
|
|
1827
|
-
bitXor,
|
|
1828
|
-
calculateChunks,
|
|
1829
|
-
calculateOptimalChunks,
|
|
1830
|
-
chunkArray,
|
|
1831
|
-
chunkFloat64Array,
|
|
1832
|
-
computePool,
|
|
1833
|
-
dispatch,
|
|
1834
|
-
leftShift,
|
|
1835
|
-
mergeArrayChunks,
|
|
1836
|
-
mergeFloat64Chunks,
|
|
1837
|
-
parallelAbs,
|
|
1838
|
-
parallelAdd,
|
|
1839
|
-
parallelCos,
|
|
1840
|
-
parallelCount,
|
|
1841
|
-
parallelDistance,
|
|
1842
|
-
parallelDivide,
|
|
1843
|
-
parallelDot,
|
|
1844
|
-
parallelElementwise,
|
|
1845
|
-
parallelEvery,
|
|
1846
|
-
parallelExp,
|
|
1847
|
-
parallelFilter,
|
|
1848
|
-
parallelFind,
|
|
1849
|
-
parallelForEach,
|
|
1850
|
-
parallelHistogram,
|
|
1851
|
-
parallelLog,
|
|
1852
|
-
parallelMap,
|
|
1853
|
-
parallelMatmul,
|
|
1854
|
-
parallelMatvec,
|
|
1855
|
-
parallelMax,
|
|
1856
|
-
parallelMean,
|
|
1857
|
-
parallelMin,
|
|
1858
|
-
parallelMinMax,
|
|
1859
|
-
parallelMultiply,
|
|
1860
|
-
parallelNegate,
|
|
1861
|
-
parallelNorm,
|
|
1862
|
-
parallelOuter,
|
|
1863
|
-
parallelReduce,
|
|
1864
|
-
parallelScale,
|
|
1865
|
-
parallelSin,
|
|
1866
|
-
parallelSome,
|
|
1867
|
-
parallelSort,
|
|
1868
|
-
parallelSqrt,
|
|
1869
|
-
parallelSquare,
|
|
1870
|
-
parallelStd,
|
|
1871
|
-
parallelSubtract,
|
|
1872
|
-
parallelSum,
|
|
1873
|
-
parallelTan,
|
|
1874
|
-
parallelTranspose,
|
|
1875
|
-
parallelUnary,
|
|
1876
|
-
parallelVariance,
|
|
1877
|
-
partition2D,
|
|
1878
|
-
partitionRange,
|
|
1879
|
-
resolveOpThreshold,
|
|
1880
|
-
rightArithShift,
|
|
1881
|
-
rightLogShift,
|
|
1882
|
-
shouldParallelize as shouldChunkParallelize,
|
|
1883
|
-
shouldParallelize2 as shouldParallelize,
|
|
1884
|
-
thresholdDispatcher
|
|
1885
|
-
};
|
|
1231
|
+
return thresholdDispatcher.calculateChunks(elementCount, category);
|
|
1232
|
+
}
|
|
1233
|
+
//#endregion
|
|
1234
|
+
export { ComputePool, DEFAULT_POOL_CONFIG, DEFAULT_THRESHOLDS, DEFAULT_THRESHOLD_BY_OP, ThresholdDispatcher, Transfer, bitAnd, bitNot, bitOr, bitXor, calculateChunks, calculateOptimalChunks, chunkArray, chunkFloat64Array, computePool, dispatch, leftShift, mergeArrayChunks, mergeFloat64Chunks, parallelAbs, parallelAdd, parallelCos, parallelCount, parallelDistance, parallelDivide, parallelDot, parallelElementwise, parallelEvery, parallelExp, parallelFilter, parallelFind, parallelForEach, parallelHistogram, parallelLog, parallelMap, parallelMatmul, parallelMatvec, parallelMax, parallelMean, parallelMin, parallelMinMax, parallelMultiply, parallelNegate, parallelNorm, parallelOuter, parallelReduce, parallelScale, parallelSin, parallelSome, parallelSort, parallelSqrt, parallelSquare, parallelStd, parallelSubtract, parallelSum, parallelTan, parallelTranspose, parallelUnary, parallelVariance, partition2D, partitionRange, resolveOpThreshold, rightArithShift, rightLogShift, shouldParallelize as shouldChunkParallelize, shouldParallelize$1 as shouldParallelize, thresholdDispatcher };
|