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,282 @@
|
|
|
1
|
+
import { Graph } from './Graph.js';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export class GraphLoader {
|
|
5
|
+
/**
|
|
6
|
+
* Loads a graph from a single Safetensors file.
|
|
7
|
+
* The Safetensors __metadata__ field must contain a 'volvox_nodes' JSON string.
|
|
8
|
+
* @param {Object} graphBuilder - An empty Graph instance from VolvoxAI.createGraph()
|
|
9
|
+
* @param {string} safetensorsUrl - URL to the .safetensors file
|
|
10
|
+
* @returns {Promise<Graph>} Populated graph
|
|
11
|
+
*/
|
|
12
|
+
static async load(graphBuilder, safetensorsUrl) {
|
|
13
|
+
let configUrl;
|
|
14
|
+
if (safetensorsUrl.endsWith('_weights.safetensors')) {
|
|
15
|
+
configUrl = safetensorsUrl.replace('_weights.safetensors', '_config.json');
|
|
16
|
+
} else {
|
|
17
|
+
configUrl = safetensorsUrl.replace(/\/[^\/]+$/, '/config.json');
|
|
18
|
+
}
|
|
19
|
+
console.log(`[VolvoxAI] Loading config from ${configUrl}...`);
|
|
20
|
+
const configResponse = await fetch(configUrl);
|
|
21
|
+
if (!configResponse.ok) throw new Error(`Failed to load config.json: ${configResponse.statusText}`);
|
|
22
|
+
const config = await configResponse.json();
|
|
23
|
+
|
|
24
|
+
console.log(`[VolvoxAI] Loading Safetensors model from ${safetensorsUrl}...`);
|
|
25
|
+
const response = await fetch(safetensorsUrl);
|
|
26
|
+
if (!response.ok) throw new Error(`Failed to load safetensors: ${response.statusText}`);
|
|
27
|
+
const buffer = await response.arrayBuffer();
|
|
28
|
+
const dataView = new DataView(buffer);
|
|
29
|
+
const headerLen = Number(dataView.getBigUint64(0, true));
|
|
30
|
+
const headerBytes = new Uint8Array(buffer, 8, headerLen);
|
|
31
|
+
const headerStr = new TextDecoder("utf-8").decode(headerBytes);
|
|
32
|
+
const header = JSON.parse(headerStr);
|
|
33
|
+
const metadata = header.__metadata__ || {};
|
|
34
|
+
|
|
35
|
+
const binaryOffset = 8 + headerLen;
|
|
36
|
+
const tensorsMap = /* @__PURE__ */ new Map();
|
|
37
|
+
for (const [name, info] of Object.entries(header)) {
|
|
38
|
+
if (name === "__metadata__") continue;
|
|
39
|
+
const dtype = info.dtype === "I8" ? "int8" : info.dtype === "U8" ? "uint8" : "float32";
|
|
40
|
+
const tensor = graphBuilder.addWeight(name, info.shape, dtype);
|
|
41
|
+
const startByte = binaryOffset + info.data_offsets[0];
|
|
42
|
+
const lengthBytes = info.data_offsets[1] - info.data_offsets[0];
|
|
43
|
+
if (dtype === "int8") {
|
|
44
|
+
tensor.buffer = new Int8Array(buffer, startByte, lengthBytes);
|
|
45
|
+
} else if (dtype === "uint8") {
|
|
46
|
+
tensor.buffer = new Uint8Array(buffer, startByte, lengthBytes);
|
|
47
|
+
} else if (info.dtype === "F16") {
|
|
48
|
+
tensor.buffer = GraphLoader._float16ToFloat32Array(buffer, startByte, lengthBytes);
|
|
49
|
+
} else {
|
|
50
|
+
tensor.buffer = new Float32Array(buffer, startByte, lengthBytes / 4);
|
|
51
|
+
}
|
|
52
|
+
tensorsMap.set(name, tensor);
|
|
53
|
+
}
|
|
54
|
+
if (config.inputs) {
|
|
55
|
+
const inputsDef = config.inputs;
|
|
56
|
+
for (const [name, info] of Object.entries(inputsDef)) {
|
|
57
|
+
const tensor = graphBuilder.addInput(name, info.shape, info.dtype || "float32");
|
|
58
|
+
tensorsMap.set(name, tensor);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (config.nodes) {
|
|
63
|
+
GraphLoader._buildFromBlueprint(graphBuilder, config, tensorsMap);
|
|
64
|
+
} else if (config.model_type) {
|
|
65
|
+
if (typeof GraphLoader.ModelBuilders === 'undefined' || !GraphLoader.ModelBuilders[config.model_type]) {
|
|
66
|
+
throw new Error(`[VolvoxAI] Unsupported Hugging Face model_type: '${config.model_type}'. No builder registered.`);
|
|
67
|
+
}
|
|
68
|
+
console.log(`[VolvoxAI] Building graph on the fly using model builder for '${config.model_type}'...`);
|
|
69
|
+
GraphLoader.ModelBuilders[config.model_type](graphBuilder, config, tensorsMap);
|
|
70
|
+
} else {
|
|
71
|
+
throw new Error("[VolvoxAI] config.json must contain either 'nodes' (Volvox blueprint) or 'model_type' (Hugging Face).");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
GraphLoader._dequantizeConvWeights(graphBuilder);
|
|
75
|
+
GraphLoader._normalizeConvWeightsForImageLayout(graphBuilder);
|
|
76
|
+
|
|
77
|
+
// Auto-detect output names: any tensor produced by a node that is NEVER used as an input
|
|
78
|
+
const usedAsInput = new Set();
|
|
79
|
+
for (const node of graphBuilder.nodes) {
|
|
80
|
+
for (const t of Object.values(node.inputs)) {
|
|
81
|
+
if (t && t.name) usedAsInput.add(t.name);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const outputNames = [];
|
|
85
|
+
for (const node of graphBuilder.nodes) {
|
|
86
|
+
for (const t of Object.values(node.outputs)) {
|
|
87
|
+
if (t && t.name && !usedAsInput.has(t.name)) {
|
|
88
|
+
outputNames.push(t.name);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
graphBuilder.outputNames = outputNames;
|
|
93
|
+
GraphLoader._resolveMatMulLayouts(graphBuilder);
|
|
94
|
+
console.log(`[VolvoxAI] Successfully assembled graph. Outputs:`, outputNames);
|
|
95
|
+
return graphBuilder;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* MatMul weights come in two layouts: PyTorch Linear stores [d_out, d_in]
|
|
100
|
+
* (out = x·Wᵀ); GPT-Neo/Conv1D stores [d_in, d_out] (out = x·W). Non-square weights
|
|
101
|
+
* disambiguate by shape; square ones can't, so infer the model-wide convention from
|
|
102
|
+
* the unambiguous weights and tag every MatMul node with `wLayout` ('dout' | 'din').
|
|
103
|
+
*/
|
|
104
|
+
static _resolveMatMulLayouts(graph) {
|
|
105
|
+
const isMM = (n) => n.opType === "MatMul" || n.opType === "Linear" || n.opType === "Gemm";
|
|
106
|
+
const dims = (n) => {
|
|
107
|
+
const K = n.inputs.input?.shape?.[n.inputs.input.shape.length - 1];
|
|
108
|
+
const outT = Object.values(n.outputs)[0];
|
|
109
|
+
const N = outT?.shape?.[outT.shape.length - 1];
|
|
110
|
+
return [K, N];
|
|
111
|
+
};
|
|
112
|
+
let din = 0, dout = 0;
|
|
113
|
+
for (const n of graph.nodes) {
|
|
114
|
+
if (!isMM(n) || !n.inputs.weight || n.inputs.scale) continue;
|
|
115
|
+
const w = n.inputs.weight.shape; if (!w || w.length < 2) continue;
|
|
116
|
+
const [K, N] = dims(n); if (K === N) continue;
|
|
117
|
+
if (w[0] === N && w[1] === K) dout++;
|
|
118
|
+
else if (w[0] === K && w[1] === N) din++;
|
|
119
|
+
}
|
|
120
|
+
const model = din > dout ? "din" : "dout";
|
|
121
|
+
for (const n of graph.nodes) {
|
|
122
|
+
if (!isMM(n)) continue;
|
|
123
|
+
const w = n.inputs.weight?.shape; const [K, N] = dims(n);
|
|
124
|
+
if (n.inputs.scale) n.wLayout = "dout"; // INT8 is [d_out, d_in]
|
|
125
|
+
else if (w && w.length >= 2 && K !== N && w[0] === N && w[1] === K) n.wLayout = "dout";
|
|
126
|
+
else if (w && w.length >= 2 && K !== N && w[0] === K && w[1] === N) n.wLayout = "din";
|
|
127
|
+
else n.wLayout = model; // square → model convention
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
static _buildFromBlueprint(graphBuilder, config, tensorsMap) {
|
|
132
|
+
const nodesDef = config.nodes;
|
|
133
|
+
for (const nodeDef of nodesDef) {
|
|
134
|
+
const inputs = {};
|
|
135
|
+
for (const [key, tName] of Object.entries(nodeDef.inputs)) {
|
|
136
|
+
let t = tensorsMap.get(tName) || graphBuilder.tensors.get(tName);
|
|
137
|
+
if (!t) {
|
|
138
|
+
console.warn(`[GraphLoader] Implicitly adding missing graph input '${tName}' with shape [1, 3, 224, 224]`);
|
|
139
|
+
t = graphBuilder.addInput(tName, [1, 3, 224, 224], "float32");
|
|
140
|
+
tensorsMap.set(tName, t);
|
|
141
|
+
}
|
|
142
|
+
inputs[key] = t;
|
|
143
|
+
}
|
|
144
|
+
const outputsShape = nodeDef.outputs_shape || {};
|
|
145
|
+
const opName = nodeDef.opType || nodeDef.op;
|
|
146
|
+
const outTensors = graphBuilder.addOp(opName, inputs, outputsShape, nodeDef.params || {});
|
|
147
|
+
for (const [key, tName] of Object.entries(nodeDef.outputs)) {
|
|
148
|
+
const t = outTensors[key];
|
|
149
|
+
if (!t) continue;
|
|
150
|
+
if (t.name !== tName) {
|
|
151
|
+
graphBuilder.tensors.delete(t.name);
|
|
152
|
+
t.name = tName;
|
|
153
|
+
graphBuilder.tensors.set(tName, t);
|
|
154
|
+
}
|
|
155
|
+
tensorsMap.set(tName, t);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Registry for Hugging Face model builders
|
|
161
|
+
static ModelBuilders = {};
|
|
162
|
+
|
|
163
|
+
static _float16ToFloat32Array(buffer, byteOffset, lengthBytes) {
|
|
164
|
+
const n = lengthBytes / 2;
|
|
165
|
+
const view = new DataView(buffer, byteOffset, lengthBytes);
|
|
166
|
+
const out = new Float32Array(n);
|
|
167
|
+
for (let i = 0; i < n; i++) out[i] = GraphLoader._float16BitsToFloat32(view.getUint16(i * 2, true));
|
|
168
|
+
return out;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
static _float16BitsToFloat32(h) {
|
|
172
|
+
const sign = (h & 0x8000) ? -1 : 1;
|
|
173
|
+
const exp = (h >> 10) & 0x1f;
|
|
174
|
+
const mant = h & 0x03ff;
|
|
175
|
+
if (exp === 0) {
|
|
176
|
+
if (mant === 0) return sign < 0 ? -0 : 0;
|
|
177
|
+
return sign * Math.pow(2, -14) * (mant / 1024);
|
|
178
|
+
}
|
|
179
|
+
if (exp === 31) return mant ? NaN : sign * Infinity;
|
|
180
|
+
return sign * Math.pow(2, exp - 15) * (1 + mant / 1024);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Conv kernels (JS and WASM) expect float32 weights and have no QConv path, so
|
|
185
|
+
* fold per-output-channel int8 scales into the weights up front. MatMul keeps
|
|
186
|
+
* its int8+scale fast path and is left untouched.
|
|
187
|
+
*/
|
|
188
|
+
static _dequantizeConvWeights(graph) {
|
|
189
|
+
for (const node of graph.nodes) {
|
|
190
|
+
if (node.opType !== "Conv2D" && node.opType !== "Conv1D" && node.opType !== "QConv2D") continue;
|
|
191
|
+
const w = node.inputs.weight;
|
|
192
|
+
const s = node.inputs.scale || node.inputs.weight_scale;
|
|
193
|
+
if (!s || !w || w.dtype !== "int8" || !w.buffer) continue;
|
|
194
|
+
const zp = node.inputs.weight_zero_point;
|
|
195
|
+
const outC = w.shape[0];
|
|
196
|
+
const perOut = w.buffer.length / outC;
|
|
197
|
+
const deq = new Float32Array(w.buffer.length);
|
|
198
|
+
for (let oc = 0; oc < outC; oc++) {
|
|
199
|
+
const sc = s.buffer[oc];
|
|
200
|
+
const z = zp && zp.buffer ? zp.buffer[zp.buffer.length === 1 ? 0 : oc] : 0;
|
|
201
|
+
const base = oc * perOut;
|
|
202
|
+
for (let j = 0; j < perOut; j++) deq[base + j] = (w.buffer[base + j] - z) * sc;
|
|
203
|
+
}
|
|
204
|
+
if (node.opType === "QConv2D") {
|
|
205
|
+
const deqWeight = graph.addWeight(`${w.name}__deq_${node.id}`, w.shape, "float32");
|
|
206
|
+
deqWeight.buffer = deq;
|
|
207
|
+
deqWeight.sizeBytes = deq.length * 4;
|
|
208
|
+
node.inputs.weight = deqWeight;
|
|
209
|
+
} else {
|
|
210
|
+
w.buffer = deq;
|
|
211
|
+
w.dtype = "float32";
|
|
212
|
+
w.sizeBytes = deq.length * 4;
|
|
213
|
+
}
|
|
214
|
+
delete node.inputs.scale;
|
|
215
|
+
delete node.inputs.weight_scale;
|
|
216
|
+
delete node.inputs.weight_zero_point;
|
|
217
|
+
if (node.opType === "QConv2D") node.opType = "Conv2D";
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
static _normalizeConvWeightsForImageLayout(graph) {
|
|
222
|
+
for (const node of graph.nodes) {
|
|
223
|
+
if (node.opType !== "Conv2D") continue;
|
|
224
|
+
const w = node.inputs.weight;
|
|
225
|
+
const input = node.inputs.input;
|
|
226
|
+
const output = node.outputs.out;
|
|
227
|
+
if (!w || !w.buffer || !input || !output || w.shape.length !== 4) continue;
|
|
228
|
+
const layout = node.params?.weight_layout || "HWIO";
|
|
229
|
+
if (layout === "HWIO" || layout === "HWCM") continue;
|
|
230
|
+
|
|
231
|
+
if (layout === "OHWI" || layout === "OIHW") {
|
|
232
|
+
const [ocN, a, b, c] = w.shape;
|
|
233
|
+
const kh = layout === "OHWI" ? a : b;
|
|
234
|
+
const kw = layout === "OHWI" ? b : c;
|
|
235
|
+
const icN = layout === "OHWI" ? c : a;
|
|
236
|
+
const src = w.buffer;
|
|
237
|
+
const dst = new Float32Array(src.length);
|
|
238
|
+
for (let oc = 0; oc < ocN; oc++) {
|
|
239
|
+
for (let y = 0; y < kh; y++) {
|
|
240
|
+
for (let x = 0; x < kw; x++) {
|
|
241
|
+
for (let ic = 0; ic < icN; ic++) {
|
|
242
|
+
dst[(((y * kw + x) * icN + ic) * ocN) + oc] =
|
|
243
|
+
layout === "OHWI"
|
|
244
|
+
? src[(((oc * kh + y) * kw + x) * icN) + ic]
|
|
245
|
+
: src[(((oc * icN + ic) * kh + y) * kw) + x];
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
w.buffer = dst;
|
|
251
|
+
w.shape = [kh, kw, icN, ocN];
|
|
252
|
+
w.sizeBytes = dst.length * 4;
|
|
253
|
+
node.params.weight_layout = "HWIO";
|
|
254
|
+
} else if (layout === "1HWO" || layout === "1HWM") {
|
|
255
|
+
const [, kh, kw, ocN] = w.shape;
|
|
256
|
+
const icN = input.shape[3];
|
|
257
|
+
const mult = output.shape[3] / icN;
|
|
258
|
+
if (!Number.isInteger(mult) || mult <= 0 || icN * mult !== ocN) {
|
|
259
|
+
throw new Error(`[GraphLoader] Invalid depthwise Conv2D shape for node ${node.id}`);
|
|
260
|
+
}
|
|
261
|
+
const src = w.buffer;
|
|
262
|
+
const dst = new Float32Array(src.length);
|
|
263
|
+
for (let y = 0; y < kh; y++) {
|
|
264
|
+
for (let x = 0; x < kw; x++) {
|
|
265
|
+
for (let ic = 0; ic < icN; ic++) {
|
|
266
|
+
for (let m = 0; m < mult; m++) {
|
|
267
|
+
dst[(((y * kw + x) * icN + ic) * mult) + m] =
|
|
268
|
+
src[((y * kw + x) * ocN) + ic * mult + m];
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
w.buffer = dst;
|
|
274
|
+
w.shape = [kh, kw, icN, mult];
|
|
275
|
+
w.sizeBytes = dst.length * 4;
|
|
276
|
+
node.params.weight_layout = "HWCM";
|
|
277
|
+
} else {
|
|
278
|
+
throw new Error(`[GraphLoader] Unsupported Conv2D weight_layout '${layout}'. VolvoxAI uses NHWC/HWIO only.`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
};
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import linearF32Shader from '../shaders/linearF32.wgsl';
|
|
2
|
+
import linearInt8Shader from '../shaders/linearInt8.wgsl';
|
|
3
|
+
import conv2DShader from '../shaders/conv2D.wgsl';
|
|
4
|
+
import conv2DDepthwise8Shader from '../shaders/conv2DDepthwise8.wgsl';
|
|
5
|
+
import conv2DPointwise16Shader from '../shaders/conv2DPointwise16.wgsl';
|
|
6
|
+
import conv2DPointwise16TileShader from '../shaders/conv2DPointwise16Tile.wgsl';
|
|
7
|
+
import conv2DPointwise8Vec2Shader from '../shaders/conv2DPointwise8Vec2.wgsl';
|
|
8
|
+
import conv2DPointwise8Vec4Shader from '../shaders/conv2DPointwise8Vec4.wgsl';
|
|
9
|
+
import conv2DRegularC3Out16Shader from '../shaders/conv2DRegularC3Out16.wgsl';
|
|
10
|
+
import layerNormShader from '../shaders/layerNorm.wgsl';
|
|
11
|
+
import binaryBroadcastShader from '../shaders/binaryBroadcast.wgsl';
|
|
12
|
+
import elementwiseShader from '../shaders/elementwise.wgsl';
|
|
13
|
+
import resizeShader from '../shaders/resize.wgsl';
|
|
14
|
+
import sliceShader from '../shaders/slice.wgsl';
|
|
15
|
+
import subShader from '../shaders/sub.wgsl';
|
|
16
|
+
import divShader from '../shaders/div.wgsl';
|
|
17
|
+
import siLUShader from '../shaders/siLU.wgsl';
|
|
18
|
+
import leakyReLUShader from '../shaders/leakyReLU.wgsl';
|
|
19
|
+
import tanhShader from '../shaders/tanh.wgsl';
|
|
20
|
+
import clipShader from '../shaders/clip.wgsl';
|
|
21
|
+
import rMSNormShader from '../shaders/rMSNorm.wgsl';
|
|
22
|
+
import softmaxShader from '../shaders/softmax.wgsl';
|
|
23
|
+
import pReLUShader from '../shaders/pReLU.wgsl';
|
|
24
|
+
import logSoftmaxShader from '../shaders/logSoftmax.wgsl';
|
|
25
|
+
import reduceShader from '../shaders/reduce.wgsl';
|
|
26
|
+
import averagePool2DShader from '../shaders/averagePool2D.wgsl';
|
|
27
|
+
import gatherShader from '../shaders/gather.wgsl';
|
|
28
|
+
import whereShader from '../shaders/where.wgsl';
|
|
29
|
+
import dequantizeLinearShader from '../shaders/dequantizeLinear.wgsl';
|
|
30
|
+
import expandShader from '../shaders/expand.wgsl';
|
|
31
|
+
import padShader from '../shaders/pad.wgsl';
|
|
32
|
+
import convTranspose2DShader from '../shaders/convTranspose2D.wgsl';
|
|
33
|
+
import reLUShader from '../shaders/reLU.wgsl';
|
|
34
|
+
import sigmoidShader from '../shaders/sigmoid.wgsl';
|
|
35
|
+
import hardSwishShader from '../shaders/hardSwish.wgsl';
|
|
36
|
+
import hardSigmoidShader from '../shaders/hardSigmoid.wgsl';
|
|
37
|
+
import copyShader from '../shaders/copy.wgsl';
|
|
38
|
+
import batchNorm2DShader from '../shaders/batchNorm2D.wgsl';
|
|
39
|
+
import gELUShader from '../shaders/gELU.wgsl';
|
|
40
|
+
import addShader from '../shaders/add.wgsl';
|
|
41
|
+
import upsample2xShader from '../shaders/upsample2x.wgsl';
|
|
42
|
+
import concatCopyShader from '../shaders/concatCopy.wgsl';
|
|
43
|
+
import concat2Shader from '../shaders/concat2.wgsl';
|
|
44
|
+
import broadcastBinaryShader from '../shaders/broadcastBinary.wgsl';
|
|
45
|
+
import generalTransposeShader from '../shaders/generalTranspose.wgsl';
|
|
46
|
+
import splitShader from '../shaders/split.wgsl';
|
|
47
|
+
import profileYShader from '../shaders/profileY.wgsl';
|
|
48
|
+
import profileXShader from '../shaders/profileX.wgsl';
|
|
49
|
+
import globalAveragePoolShader from '../shaders/globalAveragePool.wgsl';
|
|
50
|
+
import meanHeightShader from '../shaders/meanHeight.wgsl';
|
|
51
|
+
import maxPool2DShader from '../shaders/maxPool2D.wgsl';
|
|
52
|
+
import interp1DShader from '../shaders/interp1D.wgsl';
|
|
53
|
+
import conv1DShader from '../shaders/conv1D.wgsl';
|
|
54
|
+
import spatialSoftargmaxYShader from '../shaders/spatialSoftargmaxY.wgsl';
|
|
55
|
+
import embeddingShader from '../shaders/embedding.wgsl';
|
|
56
|
+
import sDPAShader from '../shaders/sDPA.wgsl';
|
|
57
|
+
import crossSDPAShader from '../shaders/crossSDPA.wgsl';
|
|
58
|
+
import crossAttentionShader from '../shaders/crossAttention.wgsl';
|
|
59
|
+
|
|
60
|
+
export class ShaderLibrary {
|
|
61
|
+
static getLinearF32Shader() {
|
|
62
|
+
return linearF32Shader;
|
|
63
|
+
}
|
|
64
|
+
static getLinearInt8Shader() {
|
|
65
|
+
return linearInt8Shader;
|
|
66
|
+
}
|
|
67
|
+
static getConv2DShader() {
|
|
68
|
+
return conv2DShader;
|
|
69
|
+
}
|
|
70
|
+
static getConv2DDepthwise8Shader() {
|
|
71
|
+
return conv2DDepthwise8Shader;
|
|
72
|
+
}
|
|
73
|
+
static getConv2DPointwise16Shader() {
|
|
74
|
+
return conv2DPointwise16Shader;
|
|
75
|
+
}
|
|
76
|
+
static getConv2DPointwise16TileShader() {
|
|
77
|
+
return conv2DPointwise16TileShader;
|
|
78
|
+
}
|
|
79
|
+
static getConv2DPointwise8Vec2Shader() {
|
|
80
|
+
return conv2DPointwise8Vec2Shader;
|
|
81
|
+
}
|
|
82
|
+
static getConv2DPointwise8Vec4Shader() {
|
|
83
|
+
return conv2DPointwise8Vec4Shader;
|
|
84
|
+
}
|
|
85
|
+
static getConv2DRegularC3Out16Shader() {
|
|
86
|
+
return conv2DRegularC3Out16Shader;
|
|
87
|
+
}
|
|
88
|
+
static getLayerNormShader() {
|
|
89
|
+
return layerNormShader;
|
|
90
|
+
}
|
|
91
|
+
static getBinaryBroadcastShader() {
|
|
92
|
+
return binaryBroadcastShader;
|
|
93
|
+
}
|
|
94
|
+
static getElementwiseShader() {
|
|
95
|
+
return elementwiseShader;
|
|
96
|
+
}
|
|
97
|
+
static getResizeShader() {
|
|
98
|
+
return resizeShader;
|
|
99
|
+
}
|
|
100
|
+
static getSliceShader() {
|
|
101
|
+
return sliceShader;
|
|
102
|
+
}
|
|
103
|
+
static getSubShader() {
|
|
104
|
+
return subShader;
|
|
105
|
+
}
|
|
106
|
+
static getDivShader() {
|
|
107
|
+
return divShader;
|
|
108
|
+
}
|
|
109
|
+
static getSiLUShader() {
|
|
110
|
+
return siLUShader;
|
|
111
|
+
}
|
|
112
|
+
static getLeakyReLUShader() {
|
|
113
|
+
return leakyReLUShader;
|
|
114
|
+
}
|
|
115
|
+
static getTanhShader() {
|
|
116
|
+
return tanhShader;
|
|
117
|
+
}
|
|
118
|
+
static getClipShader() {
|
|
119
|
+
return clipShader;
|
|
120
|
+
}
|
|
121
|
+
static getRMSNormShader() {
|
|
122
|
+
return rMSNormShader;
|
|
123
|
+
}
|
|
124
|
+
static getSoftmaxShader() {
|
|
125
|
+
return softmaxShader;
|
|
126
|
+
}
|
|
127
|
+
static getPReLUShader() {
|
|
128
|
+
return pReLUShader;
|
|
129
|
+
}
|
|
130
|
+
static getLogSoftmaxShader() {
|
|
131
|
+
return logSoftmaxShader;
|
|
132
|
+
}
|
|
133
|
+
static getReduceShader() {
|
|
134
|
+
return reduceShader;
|
|
135
|
+
}
|
|
136
|
+
static getAveragePool2DShader() {
|
|
137
|
+
return averagePool2DShader;
|
|
138
|
+
}
|
|
139
|
+
static getGatherShader() {
|
|
140
|
+
return gatherShader;
|
|
141
|
+
}
|
|
142
|
+
static getWhereShader() {
|
|
143
|
+
return whereShader;
|
|
144
|
+
}
|
|
145
|
+
static getDequantizeLinearShader() {
|
|
146
|
+
return dequantizeLinearShader;
|
|
147
|
+
}
|
|
148
|
+
static getExpandShader() {
|
|
149
|
+
return expandShader;
|
|
150
|
+
}
|
|
151
|
+
static getPadShader() {
|
|
152
|
+
return padShader;
|
|
153
|
+
}
|
|
154
|
+
static getConvTranspose2DShader() {
|
|
155
|
+
return convTranspose2DShader;
|
|
156
|
+
}
|
|
157
|
+
static getReLUShader() {
|
|
158
|
+
return reLUShader;
|
|
159
|
+
}
|
|
160
|
+
static getSigmoidShader() {
|
|
161
|
+
return sigmoidShader;
|
|
162
|
+
}
|
|
163
|
+
static getHardSwishShader() {
|
|
164
|
+
return hardSwishShader;
|
|
165
|
+
}
|
|
166
|
+
static getHardSigmoidShader() {
|
|
167
|
+
return hardSigmoidShader;
|
|
168
|
+
}
|
|
169
|
+
static getCopyShader() {
|
|
170
|
+
return copyShader;
|
|
171
|
+
}
|
|
172
|
+
static getBatchNorm2DShader() {
|
|
173
|
+
return batchNorm2DShader;
|
|
174
|
+
}
|
|
175
|
+
static getGELUShader() {
|
|
176
|
+
return gELUShader;
|
|
177
|
+
}
|
|
178
|
+
static getAddShader() {
|
|
179
|
+
return addShader;
|
|
180
|
+
}
|
|
181
|
+
static getUpsample2xShader() {
|
|
182
|
+
return upsample2xShader;
|
|
183
|
+
}
|
|
184
|
+
static getConcatCopyShader() {
|
|
185
|
+
return concatCopyShader;
|
|
186
|
+
}
|
|
187
|
+
static getConcat2Shader() {
|
|
188
|
+
return concat2Shader;
|
|
189
|
+
}
|
|
190
|
+
static getBroadcastBinaryShader(binOp = "out_val = av + bv;") {
|
|
191
|
+
// Substitute the per-op expression (Add/Mul/Sub/Div) into the shared template.
|
|
192
|
+
return broadcastBinaryShader.replace("//__BINOP__", binOp);
|
|
193
|
+
}
|
|
194
|
+
static getGeneralTransposeShader() {
|
|
195
|
+
return generalTransposeShader;
|
|
196
|
+
}
|
|
197
|
+
static getSplitShader() {
|
|
198
|
+
return splitShader;
|
|
199
|
+
}
|
|
200
|
+
static getProfileYShader() {
|
|
201
|
+
return profileYShader;
|
|
202
|
+
}
|
|
203
|
+
static getProfileXShader() {
|
|
204
|
+
return profileXShader;
|
|
205
|
+
}
|
|
206
|
+
static getGlobalAveragePoolShader() {
|
|
207
|
+
return globalAveragePoolShader;
|
|
208
|
+
}
|
|
209
|
+
static getMeanHeightShader() {
|
|
210
|
+
return meanHeightShader;
|
|
211
|
+
}
|
|
212
|
+
static getMaxPool2DShader() {
|
|
213
|
+
return maxPool2DShader;
|
|
214
|
+
}
|
|
215
|
+
static getInterp1DShader() {
|
|
216
|
+
return interp1DShader;
|
|
217
|
+
}
|
|
218
|
+
static getConv1DShader() {
|
|
219
|
+
return conv1DShader;
|
|
220
|
+
}
|
|
221
|
+
static getSpatialSoftargmaxYShader() {
|
|
222
|
+
return spatialSoftargmaxYShader;
|
|
223
|
+
}
|
|
224
|
+
static getEmbeddingShader() {
|
|
225
|
+
return embeddingShader;
|
|
226
|
+
}
|
|
227
|
+
static getSDPAShader() {
|
|
228
|
+
return sDPAShader;
|
|
229
|
+
}
|
|
230
|
+
static getCrossSDPAShader() {
|
|
231
|
+
return crossSDPAShader;
|
|
232
|
+
}
|
|
233
|
+
static getCrossAttentionShader() {
|
|
234
|
+
return crossAttentionShader;
|
|
235
|
+
}
|
|
236
|
+
}
|
package/js/Tensor.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export class Tensor {
|
|
2
|
+
constructor(name, shape, dtype = "float32", isWeight = false) {
|
|
3
|
+
this.name = name;
|
|
4
|
+
this.shape = shape;
|
|
5
|
+
this.dtype = dtype;
|
|
6
|
+
this.isWeight = isWeight;
|
|
7
|
+
this.gpuBuffer = null;
|
|
8
|
+
this.sizeBytes = this._calculateByteSize();
|
|
9
|
+
}
|
|
10
|
+
_calculateByteSize() {
|
|
11
|
+
const elements = this.shape.reduce((a, b) => a * b, 1);
|
|
12
|
+
if (this.dtype === "float32" || this.dtype === "int32") return elements * 4;
|
|
13
|
+
if (this.dtype === "int8" || this.dtype === "uint8") return elements;
|
|
14
|
+
return elements * 4;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// Normalize a stride/padding param that may be a scalar (e.g. 2), an array
|
|
19
|
+
// (e.g. [2, 2]), or missing into a [y, x] pair. KIE emits scalars; the CTC/DET
|
|
20
|
+
// exporters emit arrays.
|
|
21
|
+
export function _pair(v, def) {
|
|
22
|
+
if (Array.isArray(v)) return [v[0], v[1] ?? v[0]];
|
|
23
|
+
const s = v ?? def;
|
|
24
|
+
return [s, s];
|
|
25
|
+
}
|