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
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function _cpuBatchNorm2D(node) {
|
|
2
|
+
const input = node.inputs.input || node.inputs.x;
|
|
3
|
+
const weight = node.inputs.weight || node.inputs.scale;
|
|
4
|
+
const bias = node.inputs.bias || node.inputs.b;
|
|
5
|
+
const running_mean = node.inputs.running_mean || node.inputs.mean;
|
|
6
|
+
const running_var = node.inputs.running_var || node.inputs.var;
|
|
7
|
+
const outBuf = node.outputs.out.buffer;
|
|
8
|
+
|
|
9
|
+
const [b, h, w, c] = input.shape;
|
|
10
|
+
const eps = node.params.eps || 1e-5;
|
|
11
|
+
|
|
12
|
+
for (let batch = 0; batch < b; batch++) {
|
|
13
|
+
for (let chan = 0; chan < c; chan++) {
|
|
14
|
+
const w_val = weight.buffer[chan];
|
|
15
|
+
const b_val = bias ? bias.buffer[chan] : 0.0;
|
|
16
|
+
const rm_val = running_mean.buffer[chan];
|
|
17
|
+
const rv_val = running_var.buffer[chan];
|
|
18
|
+
|
|
19
|
+
for (let y = 0; y < h; y++) {
|
|
20
|
+
for (let x = 0; x < w; x++) {
|
|
21
|
+
const idx = ((batch * h + y) * w + x) * c + chan;
|
|
22
|
+
const val = input.buffer[idx];
|
|
23
|
+
outBuf[idx] = ((val - rm_val) / Math.sqrt(rv_val + eps)) * w_val + b_val;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
package/js/ops/cast.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function _cpuCast(node) {
|
|
2
|
+
|
|
3
|
+
// Activations are Float32 throughout this engine, so a Cast is a plain copy.
|
|
4
|
+
// If the target is an integer ONNX dtype we truncate toward zero to match
|
|
5
|
+
// the numeric result of a real cast (e.g. float indices -> int32).
|
|
6
|
+
const input = node.inputs.input || node.inputs.data;
|
|
7
|
+
const output = node.outputs.out;
|
|
8
|
+
const inBuf = input.buffer;
|
|
9
|
+
const outBuf = output.buffer;
|
|
10
|
+
const to = node.params.to;
|
|
11
|
+
const intCast = to === "int32" || to === "int64" || to === "int8"
|
|
12
|
+
|| to === 3 || to === 5 || to === 6 || to === 7;
|
|
13
|
+
const n = Math.min(inBuf.length, outBuf.length);
|
|
14
|
+
if (intCast) {
|
|
15
|
+
for (let i = 0; i < n; i++) outBuf[i] = Math.trunc(inBuf[i]);
|
|
16
|
+
} else {
|
|
17
|
+
outBuf.set(inBuf.subarray(0, n));
|
|
18
|
+
}
|
|
19
|
+
}
|
package/js/ops/clip.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function _cpuClip(node) {
|
|
2
|
+
|
|
3
|
+
const input = node.inputs.input;
|
|
4
|
+
const output = node.outputs.out;
|
|
5
|
+
let min = node.params.min !== undefined ? node.params.min : -Infinity;
|
|
6
|
+
let max = node.params.max !== undefined ? node.params.max : Infinity;
|
|
7
|
+
if (node.inputs.min) min = node.inputs.min.buffer[0];
|
|
8
|
+
if (node.inputs.max) max = node.inputs.max.buffer[0];
|
|
9
|
+
for (let i = 0; i < input.buffer.length; i++) {
|
|
10
|
+
let v = input.buffer[i];
|
|
11
|
+
if (v < min) v = min;
|
|
12
|
+
if (v > max) v = max;
|
|
13
|
+
output.buffer[i] = v;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function _cpuConcat2(node) {
|
|
2
|
+
|
|
3
|
+
// N-way concat for the blueprint keys emitted by exporters: input0, input1, ...
|
|
4
|
+
// Current model packages concatenate flattened detection heads, so flat append is
|
|
5
|
+
// enough for the supported axis layouts.
|
|
6
|
+
const outBuf = node.outputs.out.buffer;
|
|
7
|
+
let off = 0;
|
|
8
|
+
const entries = Object.entries(node.inputs).sort(([a], [b]) => {
|
|
9
|
+
const ai = /^input(\d+)$/.exec(a);
|
|
10
|
+
const bi = /^input(\d+)$/.exec(b);
|
|
11
|
+
if (ai && bi) return Number(ai[1]) - Number(bi[1]);
|
|
12
|
+
return a.localeCompare(b);
|
|
13
|
+
});
|
|
14
|
+
for (const [, t] of entries) {
|
|
15
|
+
outBuf.set(t.buffer, off);
|
|
16
|
+
off += t.buffer.length;
|
|
17
|
+
}
|
|
18
|
+
}
|
package/js/ops/conv1D.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { _pair } from '../Tensor.js';
|
|
2
|
+
|
|
3
|
+
export function _cpuConv1D(node) {
|
|
4
|
+
|
|
5
|
+
const input = node.inputs.input;
|
|
6
|
+
const weight = node.inputs.weight;
|
|
7
|
+
const bias = node.inputs.bias ? node.inputs.bias.buffer : null;
|
|
8
|
+
const output = node.outputs.out;
|
|
9
|
+
const [in_c, in_l] = input.shape.slice(1);
|
|
10
|
+
const [out_c, k_c, k] = weight.shape;
|
|
11
|
+
const out_l = output.shape[2];
|
|
12
|
+
const stride = _pair(node.params.stride, 1)[0];
|
|
13
|
+
const padding = _pair(node.params.padding, 0)[0];
|
|
14
|
+
const relu = node.params.relu;
|
|
15
|
+
const inBuf = input.buffer;
|
|
16
|
+
const wBuf = weight.buffer;
|
|
17
|
+
const outBuf = output.buffer;
|
|
18
|
+
for (let oc = 0; oc < out_c; oc++) {
|
|
19
|
+
for (let x = 0; x < out_l; x++) {
|
|
20
|
+
let sum = bias ? bias[oc] : 0;
|
|
21
|
+
for (let ic = 0; ic < in_c; ic++) {
|
|
22
|
+
for (let k_idx = 0; k_idx < k; k_idx++) {
|
|
23
|
+
const ix = x * stride + k_idx - padding;
|
|
24
|
+
if (ix >= 0 && ix < in_l) {
|
|
25
|
+
const in_idx = ic * in_l + ix;
|
|
26
|
+
const w_idx = (oc * in_c + ic) * k + k_idx;
|
|
27
|
+
sum += inBuf[in_idx] * wBuf[w_idx];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (relu && sum < 0) sum = 0;
|
|
32
|
+
outBuf[oc * out_l + x] = sum;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
package/js/ops/conv2D.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { _pair } from '../Tensor.js';
|
|
2
|
+
|
|
3
|
+
export function _cpuConv2D(node) {
|
|
4
|
+
|
|
5
|
+
const input = node.inputs.input;
|
|
6
|
+
const weight = node.inputs.weight;
|
|
7
|
+
const bias = node.inputs.bias ? node.inputs.bias.buffer : null;
|
|
8
|
+
const output = node.outputs.out;
|
|
9
|
+
const [batch, in_h, in_w, in_c] = input.shape;
|
|
10
|
+
const [k_h, k_w] = weight.shape;
|
|
11
|
+
const out_c = output.shape[3];
|
|
12
|
+
const out_h = output.shape[1];
|
|
13
|
+
const out_w = output.shape[2];
|
|
14
|
+
const [stride_y, stride_x] = _pair(node.params.stride, 1);
|
|
15
|
+
const [pad_y, pad_x] = _pair(node.params.padding, 0);
|
|
16
|
+
const pads = node.params.pads || [pad_y, pad_x, pad_y, pad_x];
|
|
17
|
+
const [dil_y, dil_x] = _pair(node.params.dilation, 1);
|
|
18
|
+
const inBuf = input.buffer;
|
|
19
|
+
const wBuf = weight.buffer;
|
|
20
|
+
const outBuf = output.buffer;
|
|
21
|
+
const groups = node.params.groups || 1;
|
|
22
|
+
const group_out = out_c / groups;
|
|
23
|
+
const group_in = weight.shape[2];
|
|
24
|
+
for (let b = 0; b < batch; b++) {
|
|
25
|
+
for (let oh = 0; oh < out_h; oh++) {
|
|
26
|
+
for (let ow = 0; ow < out_w; ow++) {
|
|
27
|
+
for (let oc = 0; oc < out_c; oc++) {
|
|
28
|
+
let sum = 0;
|
|
29
|
+
if (groups === in_c) {
|
|
30
|
+
const mult = out_c / in_c;
|
|
31
|
+
const ic = Math.floor(oc / mult);
|
|
32
|
+
const m = oc - ic * mult;
|
|
33
|
+
for (let kh = 0; kh < k_h; kh++) {
|
|
34
|
+
for (let kw = 0; kw < k_w; kw++) {
|
|
35
|
+
const ih = oh * stride_y + kh * dil_y - pads[0];
|
|
36
|
+
const iw = ow * stride_x + kw * dil_x - pads[1];
|
|
37
|
+
if (ih >= 0 && ih < in_h && iw >= 0 && iw < in_w) {
|
|
38
|
+
const in_idx = ((b * in_h + ih) * in_w + iw) * in_c + ic;
|
|
39
|
+
const w_idx = (((kh * k_w + kw) * in_c + ic) * mult) + m;
|
|
40
|
+
sum += inBuf[in_idx] * wBuf[w_idx];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
const g = Math.floor(oc / group_out);
|
|
46
|
+
const in_start = g * group_in;
|
|
47
|
+
for (let icl = 0; icl < group_in; icl++) {
|
|
48
|
+
const ic = in_start + icl;
|
|
49
|
+
for (let kh = 0; kh < k_h; kh++) {
|
|
50
|
+
for (let kw = 0; kw < k_w; kw++) {
|
|
51
|
+
const ih = oh * stride_y + kh * dil_y - pads[0];
|
|
52
|
+
const iw = ow * stride_x + kw * dil_x - pads[1];
|
|
53
|
+
if (ih >= 0 && ih < in_h && iw >= 0 && iw < in_w) {
|
|
54
|
+
const in_idx = ((b * in_h + ih) * in_w + iw) * in_c + ic;
|
|
55
|
+
const w_idx = (((kh * k_w + kw) * group_in + icl) * out_c) + oc;
|
|
56
|
+
sum += inBuf[in_idx] * wBuf[w_idx];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (bias) sum += bias[oc];
|
|
63
|
+
if (node.params.relu === 1 && sum < 0) sum = 0;
|
|
64
|
+
else if (node.params.relu >= 2) sum = Math.min(Math.max(sum, 0), 6);
|
|
65
|
+
outBuf[((b * out_h + oh) * out_w + ow) * out_c + oc] = sum;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export function _cpuConvTranspose2D(node) {
|
|
2
|
+
const input = node.inputs.input || node.inputs.x;
|
|
3
|
+
const weight = node.inputs.weight;
|
|
4
|
+
const bias = node.inputs.bias;
|
|
5
|
+
|
|
6
|
+
const inBuf = input.buffer;
|
|
7
|
+
const wBuf = weight.buffer;
|
|
8
|
+
const bBuf = bias ? bias.buffer : null;
|
|
9
|
+
const outBuf = node.outputs.out.buffer;
|
|
10
|
+
|
|
11
|
+
const [b, in_h, in_w, in_c] = input.shape;
|
|
12
|
+
const [out_b, out_h, out_w, out_c] = node.outputs.out.shape;
|
|
13
|
+
const kh = node.params.kernel[0], kw = node.params.kernel[1];
|
|
14
|
+
const sh = node.params.stride ? node.params.stride[0] : 1;
|
|
15
|
+
const sw = node.params.stride ? node.params.stride[1] : 1;
|
|
16
|
+
const ph = node.params.padding ? node.params.padding[0] : 0;
|
|
17
|
+
const pw = node.params.padding ? node.params.padding[1] : 0;
|
|
18
|
+
|
|
19
|
+
// Zero initialize output
|
|
20
|
+
for (let i = 0; i < outBuf.length; i++) {
|
|
21
|
+
outBuf[i] = bBuf ? bBuf[i % out_c] : 0.0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
for (let i_b = 0; i_b < b; i_b++) {
|
|
25
|
+
for (let i_ic = 0; i_ic < in_c; i_ic++) {
|
|
26
|
+
for (let iy = 0; iy < in_h; iy++) {
|
|
27
|
+
for (let ix = 0; ix < in_w; ix++) {
|
|
28
|
+
const in_val = inBuf[((i_b * in_h + iy) * in_w + ix) * in_c + i_ic];
|
|
29
|
+
for (let oc = 0; oc < out_c; oc++) {
|
|
30
|
+
for (let ky = 0; ky < kh; ky++) {
|
|
31
|
+
for (let kx = 0; kx < kw; kx++) {
|
|
32
|
+
const oy = iy * sh - ph + ky;
|
|
33
|
+
const ox = ix * sw - pw + kx;
|
|
34
|
+
if (oy >= 0 && oy < out_h && ox >= 0 && ox < out_w) {
|
|
35
|
+
const w_val = wBuf[i_ic * (out_c * kh * kw) + oc * (kh * kw) + ky * kw + kx];
|
|
36
|
+
outBuf[((i_b * out_h + oy) * out_w + ox) * out_c + oc] += in_val * w_val;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export function _cpuCrossAttention(node) {
|
|
2
|
+
|
|
3
|
+
const q_in = node.inputs.q.buffer;
|
|
4
|
+
const kv_in = node.inputs.kv.buffer;
|
|
5
|
+
const wBuf = node.inputs.weight.buffer;
|
|
6
|
+
const scale = node.inputs.scale ? node.inputs.scale.buffer : null;
|
|
7
|
+
const bias = node.inputs.bias ? node.inputs.bias.buffer : null;
|
|
8
|
+
const outBuf = node.outputs.out.buffer;
|
|
9
|
+
const seq_len_q = node.inputs.q.shape[1];
|
|
10
|
+
const seq_len_kv = node.inputs.kv.shape[1];
|
|
11
|
+
const d_model = node.outputs.out.shape[2];
|
|
12
|
+
const num_heads = node.params.heads || 8;
|
|
13
|
+
const head_dim = d_model / num_heads;
|
|
14
|
+
const scale_factor = 1 / Math.sqrt(head_dim);
|
|
15
|
+
for (let h = 0; h < num_heads; h++) {
|
|
16
|
+
for (let q = 0; q < seq_len_q; q++) {
|
|
17
|
+
const q_proj = new Float32Array(head_dim);
|
|
18
|
+
for (let d = 0; d < head_dim; d++) {
|
|
19
|
+
let sum = 0;
|
|
20
|
+
const out_col = h * head_dim + d;
|
|
21
|
+
for (let i = 0; i < d_model; i++) {
|
|
22
|
+
sum += q_in[q * d_model + i] * wBuf[out_col * d_model + i];
|
|
23
|
+
}
|
|
24
|
+
if (scale) sum *= scale[out_col];
|
|
25
|
+
if (bias) sum += bias[out_col];
|
|
26
|
+
q_proj[d] = sum;
|
|
27
|
+
}
|
|
28
|
+
const logits = new Float32Array(seq_len_kv);
|
|
29
|
+
let max_logit = -Infinity;
|
|
30
|
+
for (let k = 0; k < seq_len_kv; k++) {
|
|
31
|
+
let score = 0;
|
|
32
|
+
for (let d = 0; d < head_dim; d++) {
|
|
33
|
+
let k_val = 0;
|
|
34
|
+
const out_col = d_model + h * head_dim + d;
|
|
35
|
+
for (let i = 0; i < d_model; i++) {
|
|
36
|
+
k_val += kv_in[k * d_model + i] * wBuf[out_col * d_model + i];
|
|
37
|
+
}
|
|
38
|
+
if (scale) k_val *= scale[out_col];
|
|
39
|
+
if (bias) k_val += bias[out_col];
|
|
40
|
+
score += q_proj[d] * k_val;
|
|
41
|
+
}
|
|
42
|
+
score *= scale_factor;
|
|
43
|
+
logits[k] = score;
|
|
44
|
+
if (score > max_logit) max_logit = score;
|
|
45
|
+
}
|
|
46
|
+
let sum_exp = 0;
|
|
47
|
+
for (let k = 0; k < seq_len_kv; k++) {
|
|
48
|
+
const exp_val = Math.exp(logits[k] - max_logit);
|
|
49
|
+
logits[k] = exp_val;
|
|
50
|
+
sum_exp += exp_val;
|
|
51
|
+
}
|
|
52
|
+
for (let d = 0; d < head_dim; d++) {
|
|
53
|
+
let out_val = 0;
|
|
54
|
+
for (let k = 0; k < seq_len_kv; k++) {
|
|
55
|
+
const w = logits[k] / sum_exp;
|
|
56
|
+
let v_val = 0;
|
|
57
|
+
const out_col = d_model * 2 + h * head_dim + d;
|
|
58
|
+
for (let i = 0; i < d_model; i++) {
|
|
59
|
+
v_val += kv_in[k * d_model + i] * wBuf[out_col * d_model + i];
|
|
60
|
+
}
|
|
61
|
+
if (scale) v_val *= scale[out_col];
|
|
62
|
+
if (bias) v_val += bias[out_col];
|
|
63
|
+
out_val += w * v_val;
|
|
64
|
+
}
|
|
65
|
+
outBuf[q * d_model + h * head_dim + d] = out_val;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export function _cpuCrossSDPA(node) {
|
|
2
|
+
|
|
3
|
+
const q = node.inputs.q.buffer;
|
|
4
|
+
const k = node.inputs.k.buffer;
|
|
5
|
+
const v = node.inputs.v.buffer;
|
|
6
|
+
const outBuf = node.outputs.out.buffer;
|
|
7
|
+
const seqQ = node.inputs.q.shape[1];
|
|
8
|
+
const seqKV = node.inputs.k.shape[1];
|
|
9
|
+
const d_model = node.outputs.out.shape[node.outputs.out.shape.length - 1];
|
|
10
|
+
const heads = node.params.heads || 8;
|
|
11
|
+
const head_dim = d_model / heads;
|
|
12
|
+
const scale = 1 / Math.sqrt(head_dim);
|
|
13
|
+
for (let h = 0; h < heads; h++) {
|
|
14
|
+
for (let qi = 0; qi < seqQ; qi++) {
|
|
15
|
+
const logits = new Float32Array(seqKV);
|
|
16
|
+
let mx = -Infinity;
|
|
17
|
+
for (let ki = 0; ki < seqKV; ki++) {
|
|
18
|
+
let s = 0;
|
|
19
|
+
for (let d = 0; d < head_dim; d++) {
|
|
20
|
+
s += q[qi * d_model + h * head_dim + d] * k[ki * d_model + h * head_dim + d];
|
|
21
|
+
}
|
|
22
|
+
s *= scale;
|
|
23
|
+
logits[ki] = s;
|
|
24
|
+
if (s > mx) mx = s;
|
|
25
|
+
}
|
|
26
|
+
let sum = 0;
|
|
27
|
+
for (let ki = 0; ki < seqKV; ki++) {
|
|
28
|
+
const e = Math.exp(logits[ki] - mx);
|
|
29
|
+
logits[ki] = e;
|
|
30
|
+
sum += e;
|
|
31
|
+
}
|
|
32
|
+
for (let d = 0; d < head_dim; d++) {
|
|
33
|
+
let o = 0;
|
|
34
|
+
for (let ki = 0; ki < seqKV; ki++) {
|
|
35
|
+
o += (logits[ki] / sum) * v[ki * d_model + h * head_dim + d];
|
|
36
|
+
}
|
|
37
|
+
outBuf[qi * d_model + h * head_dim + d] = o;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function _cpuDequantizeLinear(node) {
|
|
2
|
+
const inBuf = node.inputs.input.buffer;
|
|
3
|
+
const scale = node.inputs.scale.buffer[0];
|
|
4
|
+
const zp = node.inputs.zero_point ? node.inputs.zero_point.buffer[0] : 0.0;
|
|
5
|
+
const outBuf = node.outputs.out.buffer;
|
|
6
|
+
for (let i = 0; i < outBuf.length; i++) {
|
|
7
|
+
outBuf[i] = (inBuf[i] - zp) * scale;
|
|
8
|
+
}
|
|
9
|
+
}
|
package/js/ops/div.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function _cpuDiv(node) {
|
|
2
|
+
|
|
3
|
+
const a = node.inputs.a;
|
|
4
|
+
const b = node.inputs.b;
|
|
5
|
+
const out = node.outputs.out;
|
|
6
|
+
const aBuf = a.buffer;
|
|
7
|
+
const bBuf = b.buffer;
|
|
8
|
+
const outBuf = out.buffer;
|
|
9
|
+
if (bBuf.length === 1) {
|
|
10
|
+
for (let i = 0; i < aBuf.length; i++) outBuf[i] = aBuf[i] / bBuf[0];
|
|
11
|
+
} else {
|
|
12
|
+
// Broadcast fallback assuming b is same size or inner dimension broadcasting
|
|
13
|
+
for (let i = 0; i < aBuf.length; i++) outBuf[i] = aBuf[i] / bBuf[i % bBuf.length];
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function _cpuEmbedding(node) {
|
|
2
|
+
|
|
3
|
+
const tokens = node.inputs.input.buffer;
|
|
4
|
+
const wBuf = node.inputs.weight.buffer;
|
|
5
|
+
const outBuf = node.outputs.out.buffer;
|
|
6
|
+
const d_model = node.outputs.out.shape[node.outputs.out.shape.length - 1];
|
|
7
|
+
const seq_len = tokens.length;
|
|
8
|
+
for (let i = 0; i < seq_len; i++) {
|
|
9
|
+
const token_id = tokens[i];
|
|
10
|
+
for (let j = 0; j < d_model; j++) {
|
|
11
|
+
outBuf[i * d_model + j] = wBuf[token_id * d_model + j];
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
package/js/ops/expand.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export function _cpuExpand(node) {
|
|
2
|
+
const inBuf = node.inputs.input.buffer;
|
|
3
|
+
const outBuf = node.outputs.out.buffer;
|
|
4
|
+
if (inBuf.length === 1) {
|
|
5
|
+
for (let i = 0; i < outBuf.length; i++) outBuf[i] = inBuf[0];
|
|
6
|
+
} else if (inBuf.length === outBuf.length) {
|
|
7
|
+
outBuf.set(inBuf);
|
|
8
|
+
} else {
|
|
9
|
+
const pad4 = (sh) => [1, 1, 1, 1].slice(0, 4 - sh.length).concat(sh);
|
|
10
|
+
const [ib, ih, iw, ic] = pad4(node.inputs.input.shape);
|
|
11
|
+
const [ob, oh, ow, oc] = pad4(node.outputs.out.shape);
|
|
12
|
+
for (let b = 0; b < ob; b++) {
|
|
13
|
+
for (let y = 0; y < oh; y++) {
|
|
14
|
+
for (let x = 0; x < ow; x++) {
|
|
15
|
+
for (let c = 0; c < oc; c++) {
|
|
16
|
+
const src = (((b % ib) * ih + (y % ih)) * iw + (x % iw)) * ic + (c % ic);
|
|
17
|
+
const dst = ((b * oh + y) * ow + x) * oc + c;
|
|
18
|
+
outBuf[dst] = inBuf[src];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
package/js/ops/gELU.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function _cpuGELU(node) {
|
|
2
|
+
|
|
3
|
+
const inBuf = node.inputs.input.buffer;
|
|
4
|
+
const outBuf = node.outputs.out.buffer;
|
|
5
|
+
for (let i = 0; i < inBuf.length; i++) {
|
|
6
|
+
const x = inBuf[i];
|
|
7
|
+
outBuf[i] = 0.5 * x * (1 + Math.tanh(0.7978845608 * (x + 0.044715 * x * x * x)));
|
|
8
|
+
}
|
|
9
|
+
}
|
package/js/ops/gather.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export function _cpuGather(node) {
|
|
2
|
+
|
|
3
|
+
const input = node.inputs.input;
|
|
4
|
+
const indices = node.inputs.indices;
|
|
5
|
+
const out = node.outputs.out;
|
|
6
|
+
let axis = node.params.axis || 0;
|
|
7
|
+
if (axis < 0) axis += input.shape.length;
|
|
8
|
+
|
|
9
|
+
const inShape = input.shape;
|
|
10
|
+
const outShape = out.shape;
|
|
11
|
+
const idxShape = indices.shape;
|
|
12
|
+
|
|
13
|
+
let inStrides = new Array(inShape.length);
|
|
14
|
+
let s = 1; for (let i = inShape.length - 1; i >= 0; i--) { inStrides[i] = s; s *= inShape[i]; }
|
|
15
|
+
|
|
16
|
+
let outStrides = new Array(outShape.length);
|
|
17
|
+
s = 1; for (let i = outShape.length - 1; i >= 0; i--) { outStrides[i] = s; s *= outShape[i]; }
|
|
18
|
+
|
|
19
|
+
let idxStrides = new Array(idxShape.length);
|
|
20
|
+
s = 1; for (let i = idxShape.length - 1; i >= 0; i--) { idxStrides[i] = s; s *= idxShape[i]; }
|
|
21
|
+
|
|
22
|
+
for (let i = 0; i < out.buffer.length; i++) {
|
|
23
|
+
let temp = i;
|
|
24
|
+
let outCoords = new Array(outShape.length);
|
|
25
|
+
for (let d = 0; d < outShape.length; d++) {
|
|
26
|
+
outCoords[d] = Math.floor(temp / outStrides[d]);
|
|
27
|
+
temp %= outStrides[d];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let idxOffset = 0;
|
|
31
|
+
for (let d = 0; d < idxShape.length; d++) {
|
|
32
|
+
idxOffset += outCoords[axis + d] * idxStrides[d];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let gatherIdx = indices.buffer[idxOffset];
|
|
36
|
+
if (gatherIdx < 0) {
|
|
37
|
+
out.buffer[i] = -1;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
let inOffset = 0;
|
|
41
|
+
for (let d = 0; d < axis; d++) {
|
|
42
|
+
inOffset += outCoords[d] * inStrides[d];
|
|
43
|
+
}
|
|
44
|
+
inOffset += gatherIdx * inStrides[axis];
|
|
45
|
+
for (let d = axis + 1; d < inShape.length; d++) {
|
|
46
|
+
inOffset += outCoords[d - 1 + idxShape.length] * inStrides[d];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
out.buffer[i] = input.buffer[inOffset];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export function _cpuGatherElements(node) {
|
|
2
|
+
|
|
3
|
+
// ONNX GatherElements: output has the same shape as `indices`; each element
|
|
4
|
+
// pulls from `data` at the same coordinates but with the `axis` coordinate
|
|
5
|
+
// replaced by the corresponding index value.
|
|
6
|
+
const data = node.inputs.input || node.inputs.data;
|
|
7
|
+
const indices = node.inputs.indices;
|
|
8
|
+
const out = node.outputs.out;
|
|
9
|
+
const dBuf = data.buffer;
|
|
10
|
+
const iBuf = indices.buffer;
|
|
11
|
+
const oBuf = out.buffer;
|
|
12
|
+
const dShape = data.shape;
|
|
13
|
+
const iShape = indices.shape;
|
|
14
|
+
let axis = node.params.axis !== undefined ? node.params.axis : 0;
|
|
15
|
+
if (axis < 0) axis += dShape.length;
|
|
16
|
+
|
|
17
|
+
const dStrides = new Array(dShape.length);
|
|
18
|
+
{ let s = 1; for (let k = dShape.length - 1; k >= 0; k--) { dStrides[k] = s; s *= dShape[k]; } }
|
|
19
|
+
const iStrides = new Array(iShape.length);
|
|
20
|
+
{ let s = 1; for (let k = iShape.length - 1; k >= 0; k--) { iStrides[k] = s; s *= iShape[k]; } }
|
|
21
|
+
|
|
22
|
+
const rank = iShape.length;
|
|
23
|
+
const coord = new Array(rank);
|
|
24
|
+
for (let lin = 0; lin < oBuf.length; lin++) {
|
|
25
|
+
let rem = lin;
|
|
26
|
+
for (let k = 0; k < rank; k++) { coord[k] = Math.floor(rem / iStrides[k]); rem %= iStrides[k]; }
|
|
27
|
+
let idx = iBuf[lin] | 0;
|
|
28
|
+
if (idx < 0) idx += dShape[axis];
|
|
29
|
+
let off = 0;
|
|
30
|
+
for (let k = 0; k < rank; k++) off += (k === axis ? idx : coord[k]) * dStrides[k];
|
|
31
|
+
oBuf[lin] = dBuf[off];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function _cpuGlobalAveragePool(node) {
|
|
2
|
+
|
|
3
|
+
const input = node.inputs.input;
|
|
4
|
+
const output = node.outputs.out;
|
|
5
|
+
const B = input.shape[0];
|
|
6
|
+
const H = input.shape[1];
|
|
7
|
+
const W = input.shape[2];
|
|
8
|
+
const C = input.shape[3];
|
|
9
|
+
const spatial = H * W;
|
|
10
|
+
for (let b = 0; b < B; b++) {
|
|
11
|
+
for (let c = 0; c < C; c++) {
|
|
12
|
+
let sum = 0;
|
|
13
|
+
for (let i = 0; i < spatial; i++) {
|
|
14
|
+
const y = Math.floor(i / W);
|
|
15
|
+
const x = i - y * W;
|
|
16
|
+
sum += input.buffer[((b * H + y) * W + x) * C + c];
|
|
17
|
+
}
|
|
18
|
+
output.buffer[b * C + c] = sum / spatial;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function _cpuHardSigmoid(node) {
|
|
2
|
+
|
|
3
|
+
const inBuf = node.inputs.input.buffer;
|
|
4
|
+
const outBuf = node.outputs.out.buffer;
|
|
5
|
+
for (let i = 0; i < inBuf.length; i++) {
|
|
6
|
+
const x = inBuf[i];
|
|
7
|
+
let v = x + 3.0;
|
|
8
|
+
if (v < 0.0) v = 0.0;
|
|
9
|
+
else if (v > 6.0) v = 6.0;
|
|
10
|
+
outBuf[i] = v / 6.0;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function _cpuHardSwish(node) {
|
|
2
|
+
|
|
3
|
+
const inBuf = node.inputs.input.buffer;
|
|
4
|
+
const outBuf = node.outputs.out.buffer;
|
|
5
|
+
for (let i = 0; i < inBuf.length; i++) {
|
|
6
|
+
const x = inBuf[i];
|
|
7
|
+
let v = x + 3.0;
|
|
8
|
+
if (v < 0.0) v = 0.0;
|
|
9
|
+
else if (v > 6.0) v = 6.0;
|
|
10
|
+
outBuf[i] = x * v / 6.0;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function _cpuInterp1D(node) {
|
|
2
|
+
|
|
3
|
+
const input = node.inputs.input;
|
|
4
|
+
const output = node.outputs.out;
|
|
5
|
+
const [c, in_l] = input.shape.slice(1);
|
|
6
|
+
const out_l = node.params.size;
|
|
7
|
+
const inBuf = input.buffer;
|
|
8
|
+
const outBuf = output.buffer;
|
|
9
|
+
// Half-pixel (align_corners=false) mapping, matching interp1d_f32 and the
|
|
10
|
+
// WebGPU shader (PyTorch F.interpolate default).
|
|
11
|
+
const scale = in_l / out_l;
|
|
12
|
+
for (let ch = 0; ch < c; ch++) {
|
|
13
|
+
for (let x = 0; x < out_l; x++) {
|
|
14
|
+
let pos = (x + 0.5) * scale - 0.5;
|
|
15
|
+
if (pos < 0) pos = 0;
|
|
16
|
+
if (pos > in_l - 1) pos = in_l - 1;
|
|
17
|
+
const x0 = Math.floor(pos);
|
|
18
|
+
const x1 = x0 + 1 < in_l ? x0 + 1 : x0;
|
|
19
|
+
const dx = pos - x0;
|
|
20
|
+
const v0 = inBuf[ch * in_l + x0];
|
|
21
|
+
const v1 = inBuf[ch * in_l + x1];
|
|
22
|
+
outBuf[ch * out_l + x] = v0 + dx * (v1 - v0);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function _cpuLayerNorm(node) {
|
|
2
|
+
|
|
3
|
+
const inBuf = node.inputs.input.buffer;
|
|
4
|
+
const wBuf = node.inputs.weight.buffer;
|
|
5
|
+
const bBuf = node.inputs.bias.buffer;
|
|
6
|
+
const outBuf = node.outputs.out.buffer;
|
|
7
|
+
const d_model = node.params.d_model;
|
|
8
|
+
const seq_len = node.inputs.input.shape.slice(0, -1).reduce((a, b) => a * b, 1);
|
|
9
|
+
for (let i = 0; i < seq_len; i++) {
|
|
10
|
+
const offset = i * d_model;
|
|
11
|
+
let sum = 0, sq_sum = 0;
|
|
12
|
+
for (let j = 0; j < d_model; j++) {
|
|
13
|
+
const val = inBuf[offset + j];
|
|
14
|
+
sum += val;
|
|
15
|
+
sq_sum += val * val;
|
|
16
|
+
}
|
|
17
|
+
const mean = sum / d_model;
|
|
18
|
+
const variance = sq_sum / d_model - mean * mean;
|
|
19
|
+
const inv_std = 1 / Math.sqrt(variance + 1e-5);
|
|
20
|
+
for (let j = 0; j < d_model; j++) {
|
|
21
|
+
const norm_val = (inBuf[offset + j] - mean) * inv_std;
|
|
22
|
+
outBuf[offset + j] = norm_val * wBuf[j] + bBuf[j];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function _cpuLeakyReLU(node) {
|
|
2
|
+
|
|
3
|
+
const inBuf = node.inputs.input.buffer;
|
|
4
|
+
const outBuf = node.outputs.out.buffer;
|
|
5
|
+
const alpha = node.params.alpha !== undefined ? node.params.alpha : 0.01;
|
|
6
|
+
for (let i = 0; i < inBuf.length; i++) {
|
|
7
|
+
const v = inBuf[i];
|
|
8
|
+
outBuf[i] = v > 0 ? v : alpha * v;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function _cpuLogSoftmax(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++) sum += Math.exp(input.buffer[i * d + j] - max);
|
|
12
|
+
const logSum = Math.log(sum);
|
|
13
|
+
for (let j = 0; j < d; j++) outBuf[i * d + j] = (input.buffer[i * d + j] - max) - logSum;
|
|
14
|
+
}
|
|
15
|
+
}
|