volvoxai 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +145 -0
- package/bin/volvox.js +72 -0
- package/dist/v0.1.0/volvoxai.js +4664 -0
- package/dist/v0.1.0/volvoxai.min.js +1848 -0
- package/dist/v0.1.0/volvoxai.wasm +0 -0
- package/dist/volvoxai.js +4664 -0
- package/dist/volvoxai.min.js +1848 -0
- package/dist/volvoxai.wasm +0 -0
- package/docs/README.md +22 -0
- package/docs/browser-runtime.md +87 -0
- package/docs/efficientdet_tflite_vs_volvoxai.md +445 -0
- package/docs/microkernel_optimization_guide.md +153 -0
- package/docs/model-format.md +108 -0
- package/docs/models.md +103 -0
- package/docs/native-runtime.md +189 -0
- package/docs/operation_list.md +232 -0
- package/docs/operator_fusion_patterns.md +58 -0
- package/docs/quickstart.md +115 -0
- package/docs/roadmap.md +19 -0
- package/docs/testing.md +97 -0
- package/docs/textbook/01-foundations.md +233 -0
- package/docs/textbook/02-tinystories-language-model.md +300 -0
- package/docs/textbook/03-efficientdet-vision-model.md +281 -0
- package/docs/textbook/04-precision-and-quantization.md +208 -0
- package/docs/textbook/05-inside-the-engine.md +155 -0
- package/docs/textbook/06-native-engine-architecture.md +338 -0
- package/docs/textbook/07-glossary-and-next-steps.md +258 -0
- package/docs/textbook/README.md +85 -0
- package/docs/textbook/ko/01-foundations.md +231 -0
- package/docs/textbook/ko/02-tinystories-language-model.md +300 -0
- package/docs/textbook/ko/03-efficientdet-vision-model.md +277 -0
- package/docs/textbook/ko/04-precision-and-quantization.md +206 -0
- package/docs/textbook/ko/05-inside-the-engine.md +154 -0
- package/docs/textbook/ko/06-native-engine-architecture.md +333 -0
- package/docs/textbook/ko/07-glossary-and-next-steps.md +253 -0
- package/docs/textbook/ko/README.md +83 -0
- package/docs/xnnpack_optimization_guide.md +197 -0
- package/js/CPUEngine.js +241 -0
- package/js/Graph.js +49 -0
- package/js/GraphExecutor.js +1020 -0
- package/js/GraphLoader.js +282 -0
- package/js/ShaderLibrary.js +236 -0
- package/js/Tensor.js +25 -0
- package/js/Tokenizer.js +266 -0
- package/js/VolvoxAI.js +130 -0
- package/js/WasmEngine.js +378 -0
- package/js/WebNNEngine.js +169 -0
- package/js/index.js +11 -0
- package/js/ops/add.js +31 -0
- package/js/ops/argMax.js +33 -0
- package/js/ops/averagePool2D.js +38 -0
- package/js/ops/batchNorm2D.js +28 -0
- package/js/ops/cast.js +19 -0
- package/js/ops/clip.js +15 -0
- package/js/ops/concat2.js +18 -0
- package/js/ops/conv1D.js +35 -0
- package/js/ops/conv2D.js +70 -0
- package/js/ops/convTranspose2D.js +45 -0
- package/js/ops/crossAttention.js +69 -0
- package/js/ops/crossSDPA.js +41 -0
- package/js/ops/dequantizeLinear.js +9 -0
- package/js/ops/div.js +15 -0
- package/js/ops/embedding.js +14 -0
- package/js/ops/expand.js +24 -0
- package/js/ops/gELU.js +9 -0
- package/js/ops/gather.js +51 -0
- package/js/ops/gatherElements.js +33 -0
- package/js/ops/globalAveragePool.js +21 -0
- package/js/ops/hardSigmoid.js +12 -0
- package/js/ops/hardSwish.js +12 -0
- package/js/ops/interp1D.js +25 -0
- package/js/ops/layerNorm.js +25 -0
- package/js/ops/leakyReLU.js +10 -0
- package/js/ops/logSoftmax.js +15 -0
- package/js/ops/matMul.js +35 -0
- package/js/ops/maxPool2D.js +36 -0
- package/js/ops/meanHeight.js +17 -0
- package/js/ops/mul.js +31 -0
- package/js/ops/nonMaxSuppression.js +72 -0
- package/js/ops/pReLU.js +11 -0
- package/js/ops/pad.js +35 -0
- package/js/ops/profileX.js +22 -0
- package/js/ops/profileY.js +22 -0
- package/js/ops/rMSNorm.js +14 -0
- package/js/ops/reLU.js +8 -0
- package/js/ops/reduceMean.js +17 -0
- package/js/ops/reduceSum.js +19 -0
- package/js/ops/reshape.js +6 -0
- package/js/ops/resize.js +44 -0
- package/js/ops/sDPA.js +44 -0
- package/js/ops/siLU.js +8 -0
- package/js/ops/sigmoid.js +6 -0
- package/js/ops/slice.js +36 -0
- package/js/ops/softmax.js +18 -0
- package/js/ops/spatialSoftargmaxY.js +28 -0
- package/js/ops/split.js +24 -0
- package/js/ops/sub.js +11 -0
- package/js/ops/tanh.js +7 -0
- package/js/ops/transpose.js +34 -0
- package/js/ops/upsample2x.js +23 -0
- package/js/ops/where.js +15 -0
- package/package.json +33 -0
- package/shaders/add.wgsl +13 -0
- package/shaders/add3Relu.wgsl +23 -0
- package/shaders/addRelu.wgsl +22 -0
- package/shaders/averagePool2D.wgsl +24 -0
- package/shaders/batchNorm2D.wgsl +21 -0
- package/shaders/binaryBroadcast.wgsl +34 -0
- package/shaders/broadcastBinary.wgsl +26 -0
- package/shaders/clip.wgsl +10 -0
- package/shaders/concat2.wgsl +16 -0
- package/shaders/concatCopy.wgsl +10 -0
- package/shaders/concatSigmoidCopy.wgsl +16 -0
- package/shaders/conv1D.wgsl +37 -0
- package/shaders/conv2D.wgsl +80 -0
- package/shaders/conv2DDepthwise4.wgsl +74 -0
- package/shaders/conv2DDepthwise8.wgsl +66 -0
- package/shaders/conv2DPointwise16.wgsl +67 -0
- package/shaders/conv2DPointwise16Tile.wgsl +86 -0
- package/shaders/conv2DPointwise8.wgsl +85 -0
- package/shaders/conv2DPointwise8Vec2.wgsl +70 -0
- package/shaders/conv2DPointwise8Vec4.wgsl +65 -0
- package/shaders/conv2DRegularC3Out16.wgsl +75 -0
- package/shaders/convTranspose2D.wgsl +33 -0
- package/shaders/copy.wgsl +13 -0
- package/shaders/crossAttention.wgsl +140 -0
- package/shaders/crossAttentionF32.wgsl +98 -0
- package/shaders/crossSDPA.wgsl +74 -0
- package/shaders/dequantizeLinear.wgsl +14 -0
- package/shaders/div.wgsl +34 -0
- package/shaders/elementwise.wgsl +13 -0
- package/shaders/embedding.wgsl +22 -0
- package/shaders/expand.wgsl +18 -0
- package/shaders/gELU.wgsl +13 -0
- package/shaders/gather.wgsl +17 -0
- package/shaders/generalTranspose.wgsl +19 -0
- package/shaders/globalAveragePool.wgsl +19 -0
- package/shaders/hardSigmoid.wgsl +13 -0
- package/shaders/hardSwish.wgsl +13 -0
- package/shaders/interp1D.wgsl +28 -0
- package/shaders/layerNorm.wgsl +33 -0
- package/shaders/leakyReLU.wgsl +11 -0
- package/shaders/linearF32.wgsl +33 -0
- package/shaders/linearF32RowMajor.wgsl +24 -0
- package/shaders/linearInt8.wgsl +42 -0
- package/shaders/logSoftmax.wgsl +22 -0
- package/shaders/maxPool2D.wgsl +37 -0
- package/shaders/meanHeight.wgsl +18 -0
- package/shaders/mul.wgsl +32 -0
- package/shaders/nonMaxSuppression.wgsl +92 -0
- package/shaders/pReLU.wgsl +14 -0
- package/shaders/pad.wgsl +19 -0
- package/shaders/profileX.wgsl +28 -0
- package/shaders/profileY.wgsl +28 -0
- package/shaders/quantizeLinear.wgsl +69 -0
- package/shaders/rMSNorm.wgsl +21 -0
- package/shaders/reLU.wgsl +13 -0
- package/shaders/reduce.wgsl +17 -0
- package/shaders/resize.wgsl +52 -0
- package/shaders/sDPA.wgsl +71 -0
- package/shaders/siLU.wgsl +13 -0
- package/shaders/sigmoid.wgsl +13 -0
- package/shaders/slice.wgsl +26 -0
- package/shaders/softmax.wgsl +23 -0
- package/shaders/spatialSoftargmaxY.wgsl +32 -0
- package/shaders/split.wgsl +15 -0
- package/shaders/sub.wgsl +34 -0
- package/shaders/tanh.wgsl +13 -0
- package/shaders/upsample2x.wgsl +24 -0
- package/shaders/where.wgsl +12 -0
- package/volvoxai.wasm +0 -0
package/js/ops/matMul.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export function _cpuMatMul(node) {
|
|
2
|
+
|
|
3
|
+
const input = node.inputs.input;
|
|
4
|
+
const weight = node.inputs.weight;
|
|
5
|
+
const output = node.outputs.out;
|
|
6
|
+
const M = input.shape.slice(0, -1).reduce((a, b) => a * b, 1);
|
|
7
|
+
const K = input.shape[input.shape.length - 1];
|
|
8
|
+
const N = output.shape[output.shape.length - 1];
|
|
9
|
+
const inBuf = input.buffer;
|
|
10
|
+
const wBuf = weight.buffer;
|
|
11
|
+
const outBuf = output.buffer;
|
|
12
|
+
const scale = node.inputs.scale ? node.inputs.scale.buffer : null;
|
|
13
|
+
const bias = node.inputs.bias ? node.inputs.bias.buffer : null;
|
|
14
|
+
// Two weight layouts coexist: PyTorch Linear stores [d_out, d_in] (out = x·Wᵀ),
|
|
15
|
+
// while GPT-Neo/Conv1D stores [d_in, d_out] (out = x·W). Detect by shape so both
|
|
16
|
+
// model families work. INT8-quantized weights are always [d_out, d_in] + per-out scale.
|
|
17
|
+
const w0 = weight.shape.length >= 2 ? weight.shape[0] : N;
|
|
18
|
+
const w1 = weight.shape.length >= 2 ? weight.shape[1] : K;
|
|
19
|
+
// Prefer the per-node layout resolved at load (handles square weights); fall back to
|
|
20
|
+
// shape detection. 'dout' = [d_out, d_in] (x·Wᵀ), 'din' = [d_in, d_out] (x·W).
|
|
21
|
+
const doutFirst = scale ? true : (node.wLayout ? node.wLayout === "dout" : (w0 === N && w1 === K));
|
|
22
|
+
for (let i = 0; i < M; i++) {
|
|
23
|
+
for (let j = 0; j < N; j++) {
|
|
24
|
+
let sum = 0;
|
|
25
|
+
if (doutFirst) {
|
|
26
|
+
for (let k = 0; k < K; k++) sum += inBuf[i * K + k] * wBuf[j * K + k];
|
|
27
|
+
} else {
|
|
28
|
+
for (let k = 0; k < K; k++) sum += inBuf[i * K + k] * wBuf[k * N + j];
|
|
29
|
+
}
|
|
30
|
+
if (scale) sum *= scale[j];
|
|
31
|
+
if (bias) sum += bias[j];
|
|
32
|
+
outBuf[i * N + j] = sum;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export function _cpuMaxPool2D(node) {
|
|
2
|
+
|
|
3
|
+
const input = node.inputs.input;
|
|
4
|
+
const output = node.outputs.out;
|
|
5
|
+
const [n, h, w, c] = input.shape;
|
|
6
|
+
const ky = node.params.kernel[0];
|
|
7
|
+
const kx = node.params.kernel[1];
|
|
8
|
+
const sy = node.params.stride[0];
|
|
9
|
+
const sx = node.params.stride[1];
|
|
10
|
+
const pad_y = node.params.padding ? node.params.padding[0] : 0;
|
|
11
|
+
const pad_x = node.params.padding ? node.params.padding[1] : 0;
|
|
12
|
+
const out_h = output.shape[1];
|
|
13
|
+
const out_w = output.shape[2];
|
|
14
|
+
const inBuf = input.buffer;
|
|
15
|
+
const outBuf = output.buffer;
|
|
16
|
+
for (let b = 0; b < n; b++) {
|
|
17
|
+
for (let oy = 0; oy < out_h; oy++) {
|
|
18
|
+
for (let ox = 0; ox < out_w; ox++) {
|
|
19
|
+
for (let ch = 0; ch < c; ch++) {
|
|
20
|
+
let best = -Infinity;
|
|
21
|
+
for (let dy = 0; dy < ky; dy++) {
|
|
22
|
+
for (let dx = 0; dx < kx; dx++) {
|
|
23
|
+
const ih = oy * sy + dy - pad_y;
|
|
24
|
+
const iw = ox * sx + dx - pad_x;
|
|
25
|
+
if (ih >= 0 && ih < h && iw >= 0 && iw < w) {
|
|
26
|
+
const v = inBuf[((b * h + ih) * w + iw) * c + ch];
|
|
27
|
+
if (v > best) best = v;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
outBuf[((b * out_h + oy) * out_w + ox) * c + ch] = best;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function _cpuMeanHeight(node) {
|
|
2
|
+
|
|
3
|
+
const input = node.inputs.input;
|
|
4
|
+
const output = node.outputs.out;
|
|
5
|
+
const [, h, w, c] = input.shape;
|
|
6
|
+
const inBuf = input.buffer;
|
|
7
|
+
const outBuf = output.buffer;
|
|
8
|
+
for (let ch = 0; ch < c; ch++) {
|
|
9
|
+
for (let x = 0; x < w; x++) {
|
|
10
|
+
let sum = 0;
|
|
11
|
+
for (let y = 0; y < h; y++) {
|
|
12
|
+
sum += inBuf[(y * w + x) * c + ch];
|
|
13
|
+
}
|
|
14
|
+
outBuf[ch * w + x] = sum / h;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
package/js/ops/mul.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function _cpuMul(node) {
|
|
2
|
+
|
|
3
|
+
const a = node.inputs.a;
|
|
4
|
+
const b = node.inputs.b;
|
|
5
|
+
const outBuf = node.outputs.out.buffer;
|
|
6
|
+
let aBuf = a.buffer;
|
|
7
|
+
let bBuf = b.buffer;
|
|
8
|
+
let aShape = a.shape;
|
|
9
|
+
let bShape = b.shape;
|
|
10
|
+
|
|
11
|
+
// Swap to ensure a is the larger tensor
|
|
12
|
+
if (aBuf.length < bBuf.length) {
|
|
13
|
+
let temp = aBuf; aBuf = bBuf; bBuf = temp;
|
|
14
|
+
let tempS = aShape; aShape = bShape; bShape = tempS;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const elements = aBuf.length;
|
|
18
|
+
if (bBuf.length === 1) {
|
|
19
|
+
for (let i = 0; i < elements; i++) outBuf[i] = aBuf[i] * bBuf[0];
|
|
20
|
+
} else if (bBuf.length === elements) {
|
|
21
|
+
for (let i = 0; i < elements; i++) outBuf[i] = aBuf[i] * bBuf[i];
|
|
22
|
+
} else if (aShape.length === 4 && bBuf.length === aShape[3]) {
|
|
23
|
+
const c = aShape[3];
|
|
24
|
+
for (let i = 0; i < elements; i++) outBuf[i] = aBuf[i] * bBuf[i % c];
|
|
25
|
+
} else if (aShape.length === 4 && bShape.length === 4 && bShape[0] === 1 && bShape[1] === 1 && bShape[2] === 1 && bShape[3] === aShape[3]) {
|
|
26
|
+
const c = aShape[3];
|
|
27
|
+
for (let i = 0; i < elements; i++) outBuf[i] = aBuf[i] * bBuf[i % c];
|
|
28
|
+
} else {
|
|
29
|
+
console.warn("[VolvoxAI CPU] Executing Mul is not fully implemented yet for shapes", a.shape, b.shape);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export function _cpuNonMaxSuppression(node) {
|
|
2
|
+
|
|
3
|
+
const boxes = node.inputs.boxes;
|
|
4
|
+
const scores = node.inputs.scores;
|
|
5
|
+
const out = node.outputs.out;
|
|
6
|
+
|
|
7
|
+
let max_output_boxes_per_class = 0;
|
|
8
|
+
if (node.inputs.max_output_boxes_per_class) max_output_boxes_per_class = node.inputs.max_output_boxes_per_class.buffer[0];
|
|
9
|
+
let iou_threshold = 0.5;
|
|
10
|
+
if (node.inputs.iou_threshold) iou_threshold = node.inputs.iou_threshold.buffer[0];
|
|
11
|
+
let score_threshold = 0.0;
|
|
12
|
+
if (node.inputs.score_threshold) score_threshold = node.inputs.score_threshold.buffer[0];
|
|
13
|
+
|
|
14
|
+
const num_batches = boxes.shape[0];
|
|
15
|
+
const spatial_dimension = boxes.shape[1];
|
|
16
|
+
const num_classes = scores.shape[1];
|
|
17
|
+
|
|
18
|
+
let outIdx = 0;
|
|
19
|
+
for (let b = 0; b < num_batches; b++) {
|
|
20
|
+
for (let c = 0; c < num_classes; c++) {
|
|
21
|
+
let candidates = [];
|
|
22
|
+
for (let s = 0; s < spatial_dimension; s++) {
|
|
23
|
+
const score = scores.buffer[b * (num_classes * spatial_dimension) + c * spatial_dimension + s];
|
|
24
|
+
if (score >= score_threshold) {
|
|
25
|
+
const y1 = boxes.buffer[b * (spatial_dimension * 4) + s * 4 + 0];
|
|
26
|
+
const x1 = boxes.buffer[b * (spatial_dimension * 4) + s * 4 + 1];
|
|
27
|
+
const y2 = boxes.buffer[b * (spatial_dimension * 4) + s * 4 + 2];
|
|
28
|
+
const x2 = boxes.buffer[b * (spatial_dimension * 4) + s * 4 + 3];
|
|
29
|
+
candidates.push({s, score, y1, x1, y2, x2});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
candidates.sort((a, b_) => b_.score - a.score);
|
|
33
|
+
let selected = [];
|
|
34
|
+
for (let i = 0; i < candidates.length && selected.length < max_output_boxes_per_class; i++) {
|
|
35
|
+
const cand = candidates[i];
|
|
36
|
+
let keep = true;
|
|
37
|
+
for (let j = 0; j < selected.length; j++) {
|
|
38
|
+
const sel = selected[j];
|
|
39
|
+
const xx1 = Math.max(cand.x1, sel.x1);
|
|
40
|
+
const yy1 = Math.max(cand.y1, sel.y1);
|
|
41
|
+
const xx2 = Math.min(cand.x2, sel.x2);
|
|
42
|
+
const yy2 = Math.min(cand.y2, sel.y2);
|
|
43
|
+
const w = Math.max(0, xx2 - xx1);
|
|
44
|
+
const h = Math.max(0, yy2 - yy1);
|
|
45
|
+
const inter = w * h;
|
|
46
|
+
const areaCand = (cand.x2 - cand.x1) * (cand.y2 - cand.y1);
|
|
47
|
+
const areaSel = (sel.x2 - sel.x1) * (sel.y2 - sel.y1);
|
|
48
|
+
const iou = inter / (areaCand + areaSel - inter);
|
|
49
|
+
if (iou > iou_threshold) {
|
|
50
|
+
keep = false;
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (keep) {
|
|
55
|
+
selected.push(cand);
|
|
56
|
+
if (outIdx < out.buffer.length / 3) {
|
|
57
|
+
out.buffer[outIdx * 3 + 0] = b;
|
|
58
|
+
out.buffer[outIdx * 3 + 1] = c;
|
|
59
|
+
out.buffer[outIdx * 3 + 2] = cand.s;
|
|
60
|
+
outIdx++;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
while (outIdx < out.buffer.length / 3) {
|
|
67
|
+
out.buffer[outIdx * 3 + 0] = -1;
|
|
68
|
+
out.buffer[outIdx * 3 + 1] = -1;
|
|
69
|
+
out.buffer[outIdx * 3 + 2] = -1;
|
|
70
|
+
outIdx++;
|
|
71
|
+
}
|
|
72
|
+
}
|
package/js/ops/pReLU.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function _cpuPReLU(node) {
|
|
2
|
+
const inBuf = (node.inputs.input || node.inputs.x).buffer;
|
|
3
|
+
const slope = (node.inputs.slope || node.inputs.weight).buffer;
|
|
4
|
+
const outBuf = node.outputs.out.buffer;
|
|
5
|
+
const shape = (node.inputs.input || node.inputs.x).shape || [];
|
|
6
|
+
const channels = shape.length === 4 ? shape[3] : slope.length;
|
|
7
|
+
for (let i = 0; i < outBuf.length; i++) {
|
|
8
|
+
const alpha = slope.length === channels ? slope[i % channels] : slope[i % slope.length];
|
|
9
|
+
outBuf[i] = inBuf[i] < 0.0 ? inBuf[i] * alpha : inBuf[i];
|
|
10
|
+
}
|
|
11
|
+
}
|
package/js/ops/pad.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export function _cpuPad(node) {
|
|
2
|
+
const input = node.inputs.input || node.inputs.data;
|
|
3
|
+
const outBuf = node.outputs.out.buffer;
|
|
4
|
+
const pads = node.params.pads || [];
|
|
5
|
+
const val = node.params.value || 0.0;
|
|
6
|
+
|
|
7
|
+
let pt = 0, pb = 0, pl = 0, pr = 0;
|
|
8
|
+
if (pads.length === 8) {
|
|
9
|
+
pt = pads[1];
|
|
10
|
+
pl = pads[2];
|
|
11
|
+
pb = pads[5];
|
|
12
|
+
pr = pads[6];
|
|
13
|
+
} else if (pads.length === 4) {
|
|
14
|
+
pt = pads[0]; pl = pads[1]; pb = pads[2]; pr = pads[3];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const inShape = input.shape.length === 4 ? input.shape : [1, input.shape[0] || 1, input.shape[1] || 1, 1];
|
|
18
|
+
const [b, in_h, in_w, c] = inShape;
|
|
19
|
+
const out_h = in_h + pt + pb;
|
|
20
|
+
const out_w = in_w + pl + pr;
|
|
21
|
+
|
|
22
|
+
for (let i = 0; i < outBuf.length; i++) outBuf[i] = val;
|
|
23
|
+
|
|
24
|
+
for (let batch = 0; batch < b; batch++) {
|
|
25
|
+
for (let y = 0; y < in_h; y++) {
|
|
26
|
+
for (let x = 0; x < in_w; x++) {
|
|
27
|
+
for (let chan = 0; chan < c; chan++) {
|
|
28
|
+
const inIdx = ((batch * in_h + y) * in_w + x) * c + chan;
|
|
29
|
+
const outIdx = ((batch * out_h + (y + pt)) * out_w + (x + pl)) * c + chan;
|
|
30
|
+
outBuf[outIdx] = input.buffer[inIdx];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function _cpuProfileX(node) {
|
|
2
|
+
|
|
3
|
+
// Column (W) profile: collapse H, keep W -> [1, 2c, w]. Mirrors C profile_x_f32.
|
|
4
|
+
const input = node.inputs.input;
|
|
5
|
+
const output = node.outputs.out;
|
|
6
|
+
const [, h, w, c] = input.shape;
|
|
7
|
+
const inBuf = input.buffer;
|
|
8
|
+
const outBuf = output.buffer;
|
|
9
|
+
for (let ch = 0; ch < c; ch++) {
|
|
10
|
+
for (let x = 0; x < w; x++) {
|
|
11
|
+
let max_v = -Infinity;
|
|
12
|
+
let sum_v = 0;
|
|
13
|
+
for (let y = 0; y < h; y++) {
|
|
14
|
+
const v = inBuf[(y * w + x) * c + ch];
|
|
15
|
+
if (v > max_v) max_v = v;
|
|
16
|
+
sum_v += v;
|
|
17
|
+
}
|
|
18
|
+
outBuf[ch * w + x] = max_v;
|
|
19
|
+
outBuf[(c + ch) * w + x] = sum_v / h;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function _cpuProfileY(node) {
|
|
2
|
+
|
|
3
|
+
// Row (H) profile: collapse W, keep H -> [1, 2c, h]. Mirrors C profile_y_f32.
|
|
4
|
+
const input = node.inputs.input;
|
|
5
|
+
const output = node.outputs.out;
|
|
6
|
+
const [, h, w, c] = input.shape;
|
|
7
|
+
const inBuf = input.buffer;
|
|
8
|
+
const outBuf = output.buffer;
|
|
9
|
+
for (let ch = 0; ch < c; ch++) {
|
|
10
|
+
for (let y = 0; y < h; y++) {
|
|
11
|
+
let max_v = -Infinity;
|
|
12
|
+
let sum_v = 0;
|
|
13
|
+
for (let x = 0; x < w; x++) {
|
|
14
|
+
const v = inBuf[(y * w + x) * c + ch];
|
|
15
|
+
if (v > max_v) max_v = v;
|
|
16
|
+
sum_v += v;
|
|
17
|
+
}
|
|
18
|
+
outBuf[ch * h + y] = max_v;
|
|
19
|
+
outBuf[(c + ch) * h + y] = sum_v / w;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function _cpuRMSNorm(node) {
|
|
2
|
+
const input = node.inputs.input || node.inputs.x;
|
|
3
|
+
const weight = node.inputs.weight;
|
|
4
|
+
const outBuf = node.outputs.out.buffer;
|
|
5
|
+
const in_shape = input.shape.length === 2 ? input.shape : [1, input.buffer.length];
|
|
6
|
+
const b = in_shape[0], d = in_shape[1];
|
|
7
|
+
const eps = node.params.eps || 1e-5;
|
|
8
|
+
for (let i = 0; i < b; i++) {
|
|
9
|
+
let sq_sum = 0.0;
|
|
10
|
+
for (let j = 0; j < d; j++) sq_sum += input.buffer[i * d + j] * input.buffer[i * d + j];
|
|
11
|
+
const rms = Math.sqrt(sq_sum / d + eps);
|
|
12
|
+
for (let j = 0; j < d; j++) outBuf[i * d + j] = (input.buffer[i * d + j] / rms) * weight.buffer[j];
|
|
13
|
+
}
|
|
14
|
+
}
|
package/js/ops/reLU.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function _cpuReduceMean(node) {
|
|
2
|
+
const input = node.inputs.input || node.inputs.data;
|
|
3
|
+
const inBuf = input.buffer;
|
|
4
|
+
const outBuf = node.outputs.out.buffer;
|
|
5
|
+
|
|
6
|
+
const in_shape = input.shape.length === 2 ? input.shape : [1, input.buffer.length];
|
|
7
|
+
const b = in_shape[0];
|
|
8
|
+
const d = in_shape[1];
|
|
9
|
+
|
|
10
|
+
for (let i = 0; i < b; i++) {
|
|
11
|
+
let sum = 0.0;
|
|
12
|
+
for (let j = 0; j < d; j++) {
|
|
13
|
+
sum += inBuf[i * d + j];
|
|
14
|
+
}
|
|
15
|
+
outBuf[i] = sum / d;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function _cpuReduceSum(node) {
|
|
2
|
+
const input = node.inputs.input || node.inputs.data;
|
|
3
|
+
const inBuf = input.buffer;
|
|
4
|
+
const outBuf = node.outputs.out.buffer;
|
|
5
|
+
|
|
6
|
+
// Simplistic reduce: flatten outer dimensions as batch, and reduce inner dimension
|
|
7
|
+
// Typically used for [B, D] -> [B, 1] or similar
|
|
8
|
+
const in_shape = input.shape.length === 2 ? input.shape : [1, input.buffer.length];
|
|
9
|
+
const b = in_shape[0];
|
|
10
|
+
const d = in_shape[1];
|
|
11
|
+
|
|
12
|
+
for (let i = 0; i < b; i++) {
|
|
13
|
+
let sum = 0.0;
|
|
14
|
+
for (let j = 0; j < d; j++) {
|
|
15
|
+
sum += inBuf[i * d + j];
|
|
16
|
+
}
|
|
17
|
+
outBuf[i] = sum;
|
|
18
|
+
}
|
|
19
|
+
}
|
package/js/ops/resize.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export function _cpuResize(node) {
|
|
2
|
+
|
|
3
|
+
const inp = node.inputs.input, out = node.outputs.out;
|
|
4
|
+
const [b, inH, inW, c] = inp.shape;
|
|
5
|
+
const [, outH, outW] = out.shape;
|
|
6
|
+
const src = inp.buffer, dst = out.buffer;
|
|
7
|
+
if (node.opType === "ResizeNearest2D" || node.params.mode === "nearest") {
|
|
8
|
+
for (let n = 0; n < b; n++) {
|
|
9
|
+
for (let y = 0; y < outH; y++) {
|
|
10
|
+
let iy = Math.floor(y * inH / outH);
|
|
11
|
+
if (iy >= inH) iy = inH - 1;
|
|
12
|
+
for (let x = 0; x < outW; x++) {
|
|
13
|
+
let ix = Math.floor(x * inW / outW);
|
|
14
|
+
if (ix >= inW) ix = inW - 1;
|
|
15
|
+
for (let ch = 0; ch < c; ch++) {
|
|
16
|
+
dst[((n * outH + y) * outW + x) * c + ch] = src[((n * inH + iy) * inW + ix) * c + ch];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Bilinear, half-pixel (align_corners=False) to match PyTorch F.interpolate.
|
|
25
|
+
const sy = inH / outH, sx = inW / outW;
|
|
26
|
+
for (let n = 0; n < b; n++) {
|
|
27
|
+
for (let y = 0; y < outH; y++) {
|
|
28
|
+
let iy = (y + 0.5) * sy - 0.5; if (iy < 0) iy = 0;
|
|
29
|
+
const y0 = Math.min(inH - 1, Math.floor(iy)), y1 = Math.min(inH - 1, y0 + 1), dy = iy - y0;
|
|
30
|
+
for (let x = 0; x < outW; x++) {
|
|
31
|
+
let ix = (x + 0.5) * sx - 0.5; if (ix < 0) ix = 0;
|
|
32
|
+
const x0 = Math.min(inW - 1, Math.floor(ix)), x1 = Math.min(inW - 1, x0 + 1), dx = ix - x0;
|
|
33
|
+
for (let ch = 0; ch < c; ch++) {
|
|
34
|
+
const v00 = src[((n * inH + y0) * inW + x0) * c + ch];
|
|
35
|
+
const v01 = src[((n * inH + y0) * inW + x1) * c + ch];
|
|
36
|
+
const v10 = src[((n * inH + y1) * inW + x0) * c + ch];
|
|
37
|
+
const v11 = src[((n * inH + y1) * inW + x1) * c + ch];
|
|
38
|
+
dst[((n * outH + y) * outW + x) * c + ch] =
|
|
39
|
+
v00 * (1 - dy) * (1 - dx) + v01 * (1 - dy) * dx + v10 * dy * (1 - dx) + v11 * dy * dx;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
package/js/ops/sDPA.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export function _cpuSDPA(node) {
|
|
2
|
+
|
|
3
|
+
const qkv = node.inputs.qkv.buffer;
|
|
4
|
+
const outBuf = node.outputs.out.buffer;
|
|
5
|
+
const seq_len = node.inputs.qkv.shape[1];
|
|
6
|
+
const d_model = node.outputs.out.shape[2];
|
|
7
|
+
const num_heads = node.params.heads || 8;
|
|
8
|
+
const head_dim = d_model / num_heads;
|
|
9
|
+
const scale = node.params.scale !== undefined ? node.params.scale : 1 / Math.sqrt(head_dim);
|
|
10
|
+
// Causal self-attention: query q attends only to keys k <= q (matches the native
|
|
11
|
+
// sdpa_f32 kernel; required for autoregressive decoders).
|
|
12
|
+
for (let h = 0; h < num_heads; h++) {
|
|
13
|
+
for (let q = 0; q < seq_len; q++) {
|
|
14
|
+
const logits = new Float32Array(seq_len);
|
|
15
|
+
let max_logit = -Infinity;
|
|
16
|
+
for (let k = 0; k <= q; k++) {
|
|
17
|
+
let score = 0;
|
|
18
|
+
for (let d = 0; d < head_dim; d++) {
|
|
19
|
+
const q_val = qkv[q * (d_model * 3) + h * head_dim + d];
|
|
20
|
+
const k_val = qkv[k * (d_model * 3) + d_model + h * head_dim + d];
|
|
21
|
+
score += q_val * k_val;
|
|
22
|
+
}
|
|
23
|
+
score *= scale;
|
|
24
|
+
logits[k] = score;
|
|
25
|
+
if (score > max_logit) max_logit = score;
|
|
26
|
+
}
|
|
27
|
+
let sum_exp = 0;
|
|
28
|
+
for (let k = 0; k <= q; k++) {
|
|
29
|
+
const exp_val = Math.exp(logits[k] - max_logit);
|
|
30
|
+
logits[k] = exp_val;
|
|
31
|
+
sum_exp += exp_val;
|
|
32
|
+
}
|
|
33
|
+
for (let d = 0; d < head_dim; d++) {
|
|
34
|
+
let out_val = 0;
|
|
35
|
+
for (let k = 0; k <= q; k++) {
|
|
36
|
+
const w = logits[k] / sum_exp;
|
|
37
|
+
const v_val = qkv[k * (d_model * 3) + d_model * 2 + h * head_dim + d];
|
|
38
|
+
out_val += w * v_val;
|
|
39
|
+
}
|
|
40
|
+
outBuf[q * d_model + h * head_dim + d] = out_val;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
package/js/ops/siLU.js
ADDED
package/js/ops/slice.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export function _cpuSlice(node) {
|
|
2
|
+
const input = node.inputs.input || node.inputs.data;
|
|
3
|
+
const outBuf = node.outputs.out.buffer;
|
|
4
|
+
const starts = node.params.starts || [0, 0, 0, 0];
|
|
5
|
+
const steps = node.params.steps || [1, 1, 1, 1];
|
|
6
|
+
const axes = node.params.axes || [0, 1, 2, 3];
|
|
7
|
+
|
|
8
|
+
const in_s = [1, 1, 1, 1].slice(0, 4 - input.shape.length).concat(input.shape);
|
|
9
|
+
const out_s = [1, 1, 1, 1].slice(0, 4 - node.outputs.out.shape.length).concat(node.outputs.out.shape);
|
|
10
|
+
|
|
11
|
+
const st = [0, 0, 0, 0];
|
|
12
|
+
const sp = [1, 1, 1, 1];
|
|
13
|
+
for (let i = 0; i < axes.length; i++) {
|
|
14
|
+
let ax = axes[i];
|
|
15
|
+
if (ax < 0) ax += input.shape.length;
|
|
16
|
+
ax += (4 - input.shape.length);
|
|
17
|
+
st[ax] = starts[i] < 0 ? starts[i] + in_s[ax] : starts[i];
|
|
18
|
+
sp[ax] = steps[i];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let outIdx = 0;
|
|
22
|
+
for (let i0 = 0; i0 < out_s[0]; i0++) {
|
|
23
|
+
for (let i1 = 0; i1 < out_s[1]; i1++) {
|
|
24
|
+
for (let i2 = 0; i2 < out_s[2]; i2++) {
|
|
25
|
+
for (let i3 = 0; i3 < out_s[3]; i3++) {
|
|
26
|
+
const src0 = st[0] + i0 * sp[0];
|
|
27
|
+
const src1 = st[1] + i1 * sp[1];
|
|
28
|
+
const src2 = st[2] + i2 * sp[2];
|
|
29
|
+
const src3 = st[3] + i3 * sp[3];
|
|
30
|
+
const inIdx = src0 * (in_s[1] * in_s[2] * in_s[3]) + src1 * (in_s[2] * in_s[3]) + src2 * in_s[3] + src3;
|
|
31
|
+
outBuf[outIdx++] = input.buffer[inIdx];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function _cpuSoftmax(node) {
|
|
2
|
+
const input = node.inputs.input || node.inputs.x;
|
|
3
|
+
const outBuf = node.outputs.out.buffer;
|
|
4
|
+
const in_shape = input.shape.length === 2 ? input.shape : [1, input.buffer.length];
|
|
5
|
+
const b = in_shape[0];
|
|
6
|
+
const d = in_shape[1];
|
|
7
|
+
for (let i = 0; i < b; i++) {
|
|
8
|
+
let max = -Infinity;
|
|
9
|
+
for (let j = 0; j < d; j++) max = Math.max(max, input.buffer[i * d + j]);
|
|
10
|
+
let sum = 0.0;
|
|
11
|
+
for (let j = 0; j < d; j++) {
|
|
12
|
+
const val = Math.exp(input.buffer[i * d + j] - max);
|
|
13
|
+
outBuf[i * d + j] = val;
|
|
14
|
+
sum += val;
|
|
15
|
+
}
|
|
16
|
+
for (let j = 0; j < d; j++) outBuf[i * d + j] /= sum;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function _cpuSpatialSoftargmaxY(node) {
|
|
2
|
+
|
|
3
|
+
const input = node.inputs.input;
|
|
4
|
+
const output = node.outputs.out;
|
|
5
|
+
const inBuf = input.buffer;
|
|
6
|
+
const outBuf = output.buffer;
|
|
7
|
+
const h = input.shape[1];
|
|
8
|
+
const w = input.shape[2];
|
|
9
|
+
const c = input.shape[3];
|
|
10
|
+
for (let ch = 0; ch < c; ch++) {
|
|
11
|
+
const outBase = ch * w;
|
|
12
|
+
for (let x = 0; x < w; x++) {
|
|
13
|
+
let maxLogit = -Infinity;
|
|
14
|
+
for (let y = 0; y < h; y++) {
|
|
15
|
+
const v = inBuf[(y * w + x) * c + ch];
|
|
16
|
+
if (v > maxLogit) maxLogit = v;
|
|
17
|
+
}
|
|
18
|
+
let denom = 0;
|
|
19
|
+
let weighted = 0;
|
|
20
|
+
for (let y = 0; y < h; y++) {
|
|
21
|
+
const ev = Math.exp(inBuf[(y * w + x) * c + ch] - maxLogit);
|
|
22
|
+
denom += ev;
|
|
23
|
+
weighted += ev * ((y + 0.5) / h);
|
|
24
|
+
}
|
|
25
|
+
outBuf[outBase + x] = denom > 0 ? weighted / denom : 0;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
package/js/ops/split.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export function _cpuSplit(node) {
|
|
2
|
+
|
|
3
|
+
const input = node.inputs.input;
|
|
4
|
+
const inBuf = input.buffer;
|
|
5
|
+
const inShape = input.shape;
|
|
6
|
+
let axis = node.params.axis || 0;
|
|
7
|
+
if (axis < 0) axis += inShape.length;
|
|
8
|
+
const outKeys = Object.keys(node.outputs).sort();
|
|
9
|
+
const numOutputs = outKeys.length;
|
|
10
|
+
const splitSize = inShape[axis] / numOutputs;
|
|
11
|
+
let outerSize = 1;
|
|
12
|
+
for (let i = 0; i < axis; i++) outerSize *= inShape[i];
|
|
13
|
+
let innerSize = 1;
|
|
14
|
+
for (let i = axis + 1; i < inShape.length; i++) innerSize *= inShape[i];
|
|
15
|
+
const chunkSize = splitSize * innerSize;
|
|
16
|
+
for (let o = 0; o < numOutputs; o++) {
|
|
17
|
+
const outBuf = node.outputs[outKeys[o]].buffer;
|
|
18
|
+
for (let i = 0; i < outerSize; i++) {
|
|
19
|
+
const inOffset = (i * inShape[axis] + o * splitSize) * innerSize;
|
|
20
|
+
const outOffset = i * chunkSize;
|
|
21
|
+
outBuf.set(inBuf.subarray(inOffset, inOffset + chunkSize), outOffset);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
package/js/ops/sub.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function _cpuSub(node) {
|
|
2
|
+
const aBuf = node.inputs.a.buffer;
|
|
3
|
+
const bBuf = node.inputs.b.buffer;
|
|
4
|
+
const outBuf = node.outputs.out.buffer;
|
|
5
|
+
const elements = outBuf.length;
|
|
6
|
+
if (bBuf.length === 1) {
|
|
7
|
+
for (let i = 0; i < elements; i++) outBuf[i] = aBuf[i] - bBuf[0];
|
|
8
|
+
} else {
|
|
9
|
+
for (let i = 0; i < elements; i++) outBuf[i] = aBuf[i] - bBuf[i];
|
|
10
|
+
}
|
|
11
|
+
}
|
package/js/ops/tanh.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export function _cpuTranspose(node) {
|
|
2
|
+
|
|
3
|
+
const input = node.inputs.input;
|
|
4
|
+
const inBuf = input.buffer;
|
|
5
|
+
const inShape = input.shape;
|
|
6
|
+
const outBuf = node.outputs.out.buffer;
|
|
7
|
+
const perm = node.params.perm || [...Array(inShape.length).keys()].reverse();
|
|
8
|
+
|
|
9
|
+
const inStrides = new Array(inShape.length);
|
|
10
|
+
let s = 1;
|
|
11
|
+
for (let i = inShape.length - 1; i >= 0; i--) {
|
|
12
|
+
inStrides[i] = s;
|
|
13
|
+
s *= inShape[i];
|
|
14
|
+
}
|
|
15
|
+
const outShape = perm.map(p => inShape[p]);
|
|
16
|
+
const outStrides = new Array(outShape.length);
|
|
17
|
+
s = 1;
|
|
18
|
+
for (let i = outShape.length - 1; i >= 0; i--) {
|
|
19
|
+
outStrides[i] = s;
|
|
20
|
+
s *= outShape[i];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const elements = inBuf.length;
|
|
24
|
+
for (let i = 0; i < elements; i++) {
|
|
25
|
+
let inIdx = 0;
|
|
26
|
+
let temp = i;
|
|
27
|
+
for (let j = 0; j < outShape.length; j++) {
|
|
28
|
+
const outCoord = Math.floor(temp / outStrides[j]);
|
|
29
|
+
temp %= outStrides[j];
|
|
30
|
+
inIdx += outCoord * inStrides[perm[j]];
|
|
31
|
+
}
|
|
32
|
+
outBuf[i] = inBuf[inIdx];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function _cpuUpsample2x(node) {
|
|
2
|
+
|
|
3
|
+
const input = node.inputs.input;
|
|
4
|
+
const output = node.outputs.out;
|
|
5
|
+
const [n, h, w, c] = input.shape;
|
|
6
|
+
const inBuf = input.buffer;
|
|
7
|
+
const outBuf = output.buffer;
|
|
8
|
+
for (let b = 0; b < n; b++) {
|
|
9
|
+
for (let y = 0; y < h; y++) {
|
|
10
|
+
for (let x = 0; x < w; x++) {
|
|
11
|
+
for (let ch = 0; ch < c; ch++) {
|
|
12
|
+
const v = inBuf[((b * h + y) * w + x) * c + ch];
|
|
13
|
+
const oy = y * 2;
|
|
14
|
+
const ox = x * 2;
|
|
15
|
+
outBuf[((b * h * 2 + oy) * w * 2 + ox) * c + ch] = v;
|
|
16
|
+
outBuf[((b * h * 2 + oy) * w * 2 + ox + 1) * c + ch] = v;
|
|
17
|
+
outBuf[((b * h * 2 + oy + 1) * w * 2 + ox) * c + ch] = v;
|
|
18
|
+
outBuf[((b * h * 2 + oy + 1) * w * 2 + ox + 1) * c + ch] = v;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|