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.
Files changed (172) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +145 -0
  3. package/bin/volvox.js +72 -0
  4. package/dist/v0.1.0/volvoxai.js +4664 -0
  5. package/dist/v0.1.0/volvoxai.min.js +1848 -0
  6. package/dist/v0.1.0/volvoxai.wasm +0 -0
  7. package/dist/volvoxai.js +4664 -0
  8. package/dist/volvoxai.min.js +1848 -0
  9. package/dist/volvoxai.wasm +0 -0
  10. package/docs/README.md +22 -0
  11. package/docs/browser-runtime.md +87 -0
  12. package/docs/efficientdet_tflite_vs_volvoxai.md +445 -0
  13. package/docs/microkernel_optimization_guide.md +153 -0
  14. package/docs/model-format.md +108 -0
  15. package/docs/models.md +103 -0
  16. package/docs/native-runtime.md +189 -0
  17. package/docs/operation_list.md +232 -0
  18. package/docs/operator_fusion_patterns.md +58 -0
  19. package/docs/quickstart.md +115 -0
  20. package/docs/roadmap.md +19 -0
  21. package/docs/testing.md +97 -0
  22. package/docs/textbook/01-foundations.md +233 -0
  23. package/docs/textbook/02-tinystories-language-model.md +300 -0
  24. package/docs/textbook/03-efficientdet-vision-model.md +281 -0
  25. package/docs/textbook/04-precision-and-quantization.md +208 -0
  26. package/docs/textbook/05-inside-the-engine.md +155 -0
  27. package/docs/textbook/06-native-engine-architecture.md +338 -0
  28. package/docs/textbook/07-glossary-and-next-steps.md +258 -0
  29. package/docs/textbook/README.md +85 -0
  30. package/docs/textbook/ko/01-foundations.md +231 -0
  31. package/docs/textbook/ko/02-tinystories-language-model.md +300 -0
  32. package/docs/textbook/ko/03-efficientdet-vision-model.md +277 -0
  33. package/docs/textbook/ko/04-precision-and-quantization.md +206 -0
  34. package/docs/textbook/ko/05-inside-the-engine.md +154 -0
  35. package/docs/textbook/ko/06-native-engine-architecture.md +333 -0
  36. package/docs/textbook/ko/07-glossary-and-next-steps.md +253 -0
  37. package/docs/textbook/ko/README.md +83 -0
  38. package/docs/xnnpack_optimization_guide.md +197 -0
  39. package/js/CPUEngine.js +241 -0
  40. package/js/Graph.js +49 -0
  41. package/js/GraphExecutor.js +1020 -0
  42. package/js/GraphLoader.js +282 -0
  43. package/js/ShaderLibrary.js +236 -0
  44. package/js/Tensor.js +25 -0
  45. package/js/Tokenizer.js +266 -0
  46. package/js/VolvoxAI.js +130 -0
  47. package/js/WasmEngine.js +378 -0
  48. package/js/WebNNEngine.js +169 -0
  49. package/js/index.js +11 -0
  50. package/js/ops/add.js +31 -0
  51. package/js/ops/argMax.js +33 -0
  52. package/js/ops/averagePool2D.js +38 -0
  53. package/js/ops/batchNorm2D.js +28 -0
  54. package/js/ops/cast.js +19 -0
  55. package/js/ops/clip.js +15 -0
  56. package/js/ops/concat2.js +18 -0
  57. package/js/ops/conv1D.js +35 -0
  58. package/js/ops/conv2D.js +70 -0
  59. package/js/ops/convTranspose2D.js +45 -0
  60. package/js/ops/crossAttention.js +69 -0
  61. package/js/ops/crossSDPA.js +41 -0
  62. package/js/ops/dequantizeLinear.js +9 -0
  63. package/js/ops/div.js +15 -0
  64. package/js/ops/embedding.js +14 -0
  65. package/js/ops/expand.js +24 -0
  66. package/js/ops/gELU.js +9 -0
  67. package/js/ops/gather.js +51 -0
  68. package/js/ops/gatherElements.js +33 -0
  69. package/js/ops/globalAveragePool.js +21 -0
  70. package/js/ops/hardSigmoid.js +12 -0
  71. package/js/ops/hardSwish.js +12 -0
  72. package/js/ops/interp1D.js +25 -0
  73. package/js/ops/layerNorm.js +25 -0
  74. package/js/ops/leakyReLU.js +10 -0
  75. package/js/ops/logSoftmax.js +15 -0
  76. package/js/ops/matMul.js +35 -0
  77. package/js/ops/maxPool2D.js +36 -0
  78. package/js/ops/meanHeight.js +17 -0
  79. package/js/ops/mul.js +31 -0
  80. package/js/ops/nonMaxSuppression.js +72 -0
  81. package/js/ops/pReLU.js +11 -0
  82. package/js/ops/pad.js +35 -0
  83. package/js/ops/profileX.js +22 -0
  84. package/js/ops/profileY.js +22 -0
  85. package/js/ops/rMSNorm.js +14 -0
  86. package/js/ops/reLU.js +8 -0
  87. package/js/ops/reduceMean.js +17 -0
  88. package/js/ops/reduceSum.js +19 -0
  89. package/js/ops/reshape.js +6 -0
  90. package/js/ops/resize.js +44 -0
  91. package/js/ops/sDPA.js +44 -0
  92. package/js/ops/siLU.js +8 -0
  93. package/js/ops/sigmoid.js +6 -0
  94. package/js/ops/slice.js +36 -0
  95. package/js/ops/softmax.js +18 -0
  96. package/js/ops/spatialSoftargmaxY.js +28 -0
  97. package/js/ops/split.js +24 -0
  98. package/js/ops/sub.js +11 -0
  99. package/js/ops/tanh.js +7 -0
  100. package/js/ops/transpose.js +34 -0
  101. package/js/ops/upsample2x.js +23 -0
  102. package/js/ops/where.js +15 -0
  103. package/package.json +33 -0
  104. package/shaders/add.wgsl +13 -0
  105. package/shaders/add3Relu.wgsl +23 -0
  106. package/shaders/addRelu.wgsl +22 -0
  107. package/shaders/averagePool2D.wgsl +24 -0
  108. package/shaders/batchNorm2D.wgsl +21 -0
  109. package/shaders/binaryBroadcast.wgsl +34 -0
  110. package/shaders/broadcastBinary.wgsl +26 -0
  111. package/shaders/clip.wgsl +10 -0
  112. package/shaders/concat2.wgsl +16 -0
  113. package/shaders/concatCopy.wgsl +10 -0
  114. package/shaders/concatSigmoidCopy.wgsl +16 -0
  115. package/shaders/conv1D.wgsl +37 -0
  116. package/shaders/conv2D.wgsl +80 -0
  117. package/shaders/conv2DDepthwise4.wgsl +74 -0
  118. package/shaders/conv2DDepthwise8.wgsl +66 -0
  119. package/shaders/conv2DPointwise16.wgsl +67 -0
  120. package/shaders/conv2DPointwise16Tile.wgsl +86 -0
  121. package/shaders/conv2DPointwise8.wgsl +85 -0
  122. package/shaders/conv2DPointwise8Vec2.wgsl +70 -0
  123. package/shaders/conv2DPointwise8Vec4.wgsl +65 -0
  124. package/shaders/conv2DRegularC3Out16.wgsl +75 -0
  125. package/shaders/convTranspose2D.wgsl +33 -0
  126. package/shaders/copy.wgsl +13 -0
  127. package/shaders/crossAttention.wgsl +140 -0
  128. package/shaders/crossAttentionF32.wgsl +98 -0
  129. package/shaders/crossSDPA.wgsl +74 -0
  130. package/shaders/dequantizeLinear.wgsl +14 -0
  131. package/shaders/div.wgsl +34 -0
  132. package/shaders/elementwise.wgsl +13 -0
  133. package/shaders/embedding.wgsl +22 -0
  134. package/shaders/expand.wgsl +18 -0
  135. package/shaders/gELU.wgsl +13 -0
  136. package/shaders/gather.wgsl +17 -0
  137. package/shaders/generalTranspose.wgsl +19 -0
  138. package/shaders/globalAveragePool.wgsl +19 -0
  139. package/shaders/hardSigmoid.wgsl +13 -0
  140. package/shaders/hardSwish.wgsl +13 -0
  141. package/shaders/interp1D.wgsl +28 -0
  142. package/shaders/layerNorm.wgsl +33 -0
  143. package/shaders/leakyReLU.wgsl +11 -0
  144. package/shaders/linearF32.wgsl +33 -0
  145. package/shaders/linearF32RowMajor.wgsl +24 -0
  146. package/shaders/linearInt8.wgsl +42 -0
  147. package/shaders/logSoftmax.wgsl +22 -0
  148. package/shaders/maxPool2D.wgsl +37 -0
  149. package/shaders/meanHeight.wgsl +18 -0
  150. package/shaders/mul.wgsl +32 -0
  151. package/shaders/nonMaxSuppression.wgsl +92 -0
  152. package/shaders/pReLU.wgsl +14 -0
  153. package/shaders/pad.wgsl +19 -0
  154. package/shaders/profileX.wgsl +28 -0
  155. package/shaders/profileY.wgsl +28 -0
  156. package/shaders/quantizeLinear.wgsl +69 -0
  157. package/shaders/rMSNorm.wgsl +21 -0
  158. package/shaders/reLU.wgsl +13 -0
  159. package/shaders/reduce.wgsl +17 -0
  160. package/shaders/resize.wgsl +52 -0
  161. package/shaders/sDPA.wgsl +71 -0
  162. package/shaders/siLU.wgsl +13 -0
  163. package/shaders/sigmoid.wgsl +13 -0
  164. package/shaders/slice.wgsl +26 -0
  165. package/shaders/softmax.wgsl +23 -0
  166. package/shaders/spatialSoftargmaxY.wgsl +32 -0
  167. package/shaders/split.wgsl +15 -0
  168. package/shaders/sub.wgsl +34 -0
  169. package/shaders/tanh.wgsl +13 -0
  170. package/shaders/upsample2x.wgsl +24 -0
  171. package/shaders/where.wgsl +12 -0
  172. package/volvoxai.wasm +0 -0
@@ -0,0 +1,338 @@
1
+ # Chapter 6 — The Native Engine (CPU + multi-backend GPU)
2
+
3
+ *Goal: understand VolvoxAI's **native** side — a freestanding C program that runs the **same
4
+ blueprint** as the browser, on desktops and phones, across CPU and several GPU/NPU backends —
5
+ and the design ideas that make that possible.*
6
+
7
+ Chapters 1–5 mostly read the JavaScript tiers because they're the clearest teaching text. But
8
+ that's only half of VolvoxAI. The other half is `native/`: a **bare-metal C engine** that takes
9
+ the *identical* `config.json` + `.safetensors` and runs it with no browser, no Node, and — this
10
+ is the striking part — **no statically linked GPU SDK**. This chapter is the architecture and the
11
+ "why" behind it.
12
+
13
+ ---
14
+
15
+ ## 6.1 The core idea: one blueprint, two worlds
16
+
17
+ The whole project is organized around a single principle:
18
+
19
+ > **Write the model once as a blueprint; run it anywhere.**
20
+
21
+ ```mermaid
22
+ flowchart TD
23
+ BP["Blueprint<br/>config.json + model.safetensors"]:::bp
24
+ BP --> WEB[Browser world · JS/WASM/WGSL]
25
+ BP --> NAT[Native world · freestanding C]
26
+ WEB --> W1[WebNN]
27
+ WEB --> W2[WebGPU]
28
+ WEB --> W3[WASM SIMD]
29
+ WEB --> W4[Pure JS]
30
+ NAT --> N1[CPU · AVX2 / NEON]
31
+ NAT --> N2[Vulkan]
32
+ NAT --> N3[OpenGL / GLES]
33
+ NAT --> N4[Metal]
34
+ NAT --> N5[Android NNAPI]
35
+ classDef bp fill:#eef,stroke:#66a;
36
+ ```
37
+
38
+ The browser world was Chapter 5. The native world is a standalone executable (`native/volvoxai`)
39
+ you run from a terminal:
40
+
41
+ ```bash
42
+ ./native/volvoxai generate models/tinystories_1m --prompt "Once upon a time, Lily" --max-new 50
43
+ ./native/volvoxai detect models/efficientdet_lite0_int8 --image input0=photo.png \
44
+ --image-normalize raw-255 --boxes boxes --scores scores
45
+ ```
46
+
47
+ Same files the browser loads. That symmetry is the design.
48
+
49
+ ---
50
+
51
+ ## 6.2 The freestanding philosophy (why it's unusual)
52
+
53
+ Two deliberate constraints shape the native engine:
54
+
55
+ 1. **No Emscripten / no heavy runtime.** The WASM module is built with plain
56
+ `clang --target=wasm32 -msimd128` — a *freestanding* build with `--no-entry`, no libc runtime.
57
+ The native binary is ordinary `clang -O3 -mavx2 -mfma -pthread`. There is no framework
58
+ underneath; the engine *is* the code in `native/`.
59
+ 2. **No static GPU dependency.** The binary does **not** link `libvulkan` or an OpenGL SDK at
60
+ build time. Instead it **`dlopen`s** the GPU driver *at runtime* and looks up entry points by
61
+ name. If the driver is present, you get GPU acceleration; if not, the exact same binary runs on
62
+ CPU. One artifact, portable across machines with wildly different GPU stacks.
63
+
64
+ Here is that runtime loading, verbatim (`native/vulkan_engine.c`, `native/opengl_engine.c`):
65
+
66
+ ```c
67
+ // Vulkan: try the platform's loader names, in order, at runtime.
68
+ const char* names[] = { "libvulkan.so.1", "libvulkan.so", "vulkan-1.dll" };
69
+ for (i = 0; i < 3; i++) vulkan_lib = dlopen(names[i], RTLD_NOW | RTLD_LOCAL);
70
+ vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)dlsym(vulkan_lib, "vkGetInstanceProcAddr");
71
+
72
+ // OpenGL/GLES: likewise dlopen libEGL + libGLESv2/libGL (or *.dll on Windows).
73
+ ```
74
+
75
+ That's the whole trick behind "runs on Vulkan/OpenGL/Metal/NNAPI with no GPU SDK linkage." The
76
+ `-ldl` in the build command is the only price.
77
+
78
+ ---
79
+
80
+ ## 6.3 One kernel source, two machines (the shared ABI)
81
+
82
+ VolvoxAI avoids maintaining two copies of every kernel. The portable C in `native/kernels/*.c`
83
+ (bundled by `native/kernels.c`) compiles **both** to WebAssembly (Tier 3 in the browser) **and**
84
+ to the native binary. The trick is a **`uintptr_t` heap-pointer ABI**: kernels address memory
85
+ through integer offsets into one flat heap, so the same source works whether that heap is WASM
86
+ linear memory or a native `malloc` arena. The compiler then auto-vectorizes it to **AVX2 on x86**
87
+ and **NEON on arm64**.
88
+
89
+ ```
90
+ ┌─────────────────────────────┐
91
+ native/kernels/*.c │ portable C, uintptr_t heap │
92
+ └───────────┬─────────────────┘
93
+ clang --target=wasm32 │ clang -O3 -mavx2 (x86) / -march=…(arm64)
94
+ ┌──────────────┴───────────────┐
95
+ volvoxai.wasm native/volvoxai
96
+ (browser Tier 3) (desktop/Android CPU)
97
+ ```
98
+
99
+ The pure-JS ops (`js/ops/*.js`) remain the reference these are validated against — so there are
100
+ really **three** expressions of each op (JS reference, portable C, and — for hot ops — an
101
+ *optimized* C kernel), all required to agree.
102
+
103
+ ---
104
+
105
+ ## 6.4 The engine lifecycle (`native/engine.c`)
106
+
107
+ The native engine is a small, explicit state machine. Its public API (`native/engine.h`) is the C
108
+ counterpart of the JS `compile()` / `execute()`:
109
+
110
+ ```c
111
+ int engine_init(const char* config_path, const char* weights_path); // load + build ONCE
112
+ float* engine_input_ptr(const char* name, long* numel); // poke an input tensor
113
+ int engine_forward(void); // run the whole graph
114
+ const float* engine_last_logits(int* count); // read the output row
115
+ void engine_free_ctx(void); // tear down
116
+ // autoregressive helpers:
117
+ int engine_prefill(int n_tokens); // process a prompt, fill K/V caches
118
+ int engine_decode(int pos); // process one new token via the cache
119
+ ```
120
+
121
+ `engine_init` does the one-time heavy lifting (`native/engine.c`):
122
+
123
+ ```c
124
+ int engine_init(const char* config_path, const char* weights_path) {
125
+ load_weights(weights_path); // mmap/parse the .safetensors blob
126
+ build_graph(config_path); // parse config.json → g_t[] tensors, g_n[] nodes
127
+ prepack_qconv_weights(); // re-lay-out int8 conv weights for the fast kernel (§5.3)
128
+ prepack_conv_weights(); // re-lay-out fp32 conv weights (im2col/GEMM order)
129
+ g_loaded = 1;
130
+ }
131
+ ```
132
+
133
+ Then `engine_forward` is the familiar loop — walk the node list, dispatch each:
134
+
135
+ ```c
136
+ for (int i = 0; i < g_nn; i++)
137
+ run_node(&g_n[i], i, /*is_last=*/ i == g_nn - 1);
138
+ ```
139
+
140
+ The graph is built **once**; forwards are cheap and repeatable. This is what lets `generate` run
141
+ hundreds of forward passes without reloading weights.
142
+
143
+ ---
144
+
145
+ ## 6.5 Per-node backend selection (`native/engine_runtime.c`)
146
+
147
+ `run_node` is where "multi-backend" actually happens. For each node it decides *who computes it*,
148
+ and the decision is per-node, not per-model:
149
+
150
+ ```mermaid
151
+ flowchart TD
152
+ N[node i] --> Q{GPU enabled?<br/>--vulkan / --opengl}
153
+ Q -- no --> CPU
154
+ Q -- yes --> AR{autoregressive<br/>decode step?}
155
+ AR -- yes --> CPU[CPU kernel<br/>conv_f32_opt / quant_cpu_opt / kernels.c]
156
+ AR -- no --> SUP{op supported on<br/>GPU graph & FP32?}
157
+ SUP -- yes --> GPU[GPU graph node<br/>vk_graph_* / opengl_*]
158
+ SUP -- no --> CPU
159
+ ```
160
+
161
+ Key rules baked into the dispatcher:
162
+
163
+ - **GPU is opt-in** via CLI flags (`--vulkan`, `--opengl`, `--nnapi`); default is CPU. If the
164
+ driver can't load, it prints e.g. `Backend: CPU (Vulkan unavailable)` and continues.
165
+ - **GPU backends are FP32-only.** `QConv2D` (int8) nodes always stay on the CPU's quantized
166
+ island (`quant_cpu_opt.c`) even with `--vulkan` — so the int8 detector runs its convs on CPU and
167
+ only FP32 ops offload.
168
+ - **Generation mostly stays on CPU.** The Vulkan/OpenGL graph path is skipped during
169
+ `engine_decode`/`engine_prefill`, because token-by-token decode is latency-bound. Large
170
+ MatMul/Gemm/Linear nodes can still use one-shot Vulkan/OpenGL offload when the work is big
171
+ enough; small decode-time MatMuls stay on CPU to avoid dispatch overhead.
172
+ - Every node records which backend ran it (`"vulkan-graph"`, `"cpu-qconv"`, …) for the `--debug`
173
+ profile report.
174
+
175
+ ---
176
+
177
+ ## 6.6 The GPU path is a deferred command graph
178
+
179
+ The native GPU backends don't execute op-by-op with a CPU round-trip each time. Like the WebGPU
180
+ tier, they **build a command graph and replay it**, keeping data resident on the device. The
181
+ `vk_graph_*` interface (`native/vulkan_engine.h`) shows the shape of it:
182
+
183
+ ```c
184
+ vk_graph_begin_forward(); // start recording
185
+ vk_graph_conv2d_f32(in, out, w, b, …); // record a conv
186
+ vk_graph_add_relu_f32(a, b, out, n, relu); // record a fused add+relu
187
+ vk_graph_maxpool2d_f32(…); vk_graph_resize_nearest_f32(…);
188
+ vk_graph_layernorm_f32(…); vk_graph_gelu_f32(…); vk_graph_softmax_f32(…);
189
+ vk_graph_end_forward(); // submit + wait once
190
+ ```
191
+
192
+ Two supporting ideas make this correct:
193
+
194
+ - **Host/device sync tracking.** `vk_graph_mark_host()` / `vk_graph_sync_host()` track which
195
+ buffers the CPU touched, so data is uploaded/downloaded only when actually needed — not every
196
+ node. Weights upload once; activations stay on the GPU between nodes.
197
+ - **Fusion carries over.** The dispatcher records fused nodes (`add+relu`, `concat+sigmoid`) as
198
+ single GPU ops, so the graph-level fusion pass (§6.8) pays off on the GPU too.
199
+
200
+ The OpenGL/GLES backend mirrors this API (`opengl_graph_*`). Android **NNAPI**
201
+ (`nnapi_engine.c`) has its own selection branch for large dense layers. Metal
202
+ (`metal_engine.m`) has Apple-only graph dispatch for selected F32 ops such as attention,
203
+ Conv1D, elementwise Mul/Sub/Div, Split, DequantizeLinear, NMS, and custom profile ops. The
204
+ exact native GPU op matrix lives in
205
+ [`docs/operation_list.md`](../operation_list.md).
206
+
207
+ ---
208
+
209
+ ## 6.7 The shader pipeline (WGSL is the single source)
210
+
211
+ You might expect the native GPU backends to need hand-written Vulkan/Metal/GLSL shaders. They
212
+ don't — VolvoxAI keeps **WGSL as the one shader language** and *cross-compiles* it. `make
213
+ compile_shaders` runs `tools/compile_shaders.sh`, which uses Mozilla's **`naga`** to translate
214
+ every `shaders/*.wgsl` into the formats each native backend wants:
215
+
216
+ ```
217
+ shaders/*.wgsl ──naga──▶ native/shaders/spv/ (SPIR-V → Vulkan)
218
+ native/shaders/glsl/ (GLSL → desktop OpenGL)
219
+ native/shaders/gles/ (GLSL ES → Android/embedded)
220
+ native/shaders/metal/ (MSL → Apple Metal)
221
+ ```
222
+
223
+ Write a kernel's shader once in WGSL, then translate it to the native shader formats. Runtime
224
+ support still needs a backend wrapper and dispatcher call: today Vulkan/OpenGL wire selected
225
+ generated shaders, and Metal wires a smaller Apple-only subset through `metal_graph_*`.
226
+ That's the same "one source, many targets" discipline as the C kernels (§6.3), applied to shaders,
227
+ with wiring tracked separately from generation.
228
+
229
+ ---
230
+
231
+ ## 6.8 Compile-time operator fusion (`native/graph_opt_fusion.c`)
232
+
233
+ Before the first forward, the native engine runs a fusion pass over the parsed graph (the design
234
+ from Chapter 5, here at the C level). It rewrites the node list in place — flagging
235
+ `fuse_relu6`, marking `skip` on elided nodes, tagging `concat_sigmoid_fuse`:
236
+
237
+ - **Conv + ReLU6** → clamp inside the conv's write (`fuse_relu6`).
238
+ - **Chained `Add`** → collapse sequential residual adds.
239
+ - **Depthwise → Pointwise** → run the MBConv pair without spilling the middle tensor.
240
+ - **Concat + Sigmoid** → fuse the detector's class-head tail.
241
+ - **Alias elision** → drop no-op `Reshape`/copy nodes (`skip`).
242
+
243
+ Fewer nodes, fewer full passes over big feature maps — the single biggest lever after SIMD.
244
+
245
+ ---
246
+
247
+ ## 6.9 Task runtimes: from tensors to usefulness
248
+
249
+ `native/main.c` is a CLI that wraps the tensor engine into real tasks. The dispatcher is a plain
250
+ `switch` on `argv[1]`:
251
+
252
+ | Command | What it does | Extra machinery |
253
+ |---|---|---|
254
+ | `run` | Raw graph runner: feed input tensors/images, dump output tensors | `image_io.c` (stb_image PNG/JPEG → NHWC) |
255
+ | `generate` | Autoregressive text (TinyStories) | `tokenizer.c` (BPE) + prefill/decode + KV-cache |
256
+ | `classify` | Top-K image classification | argmax + `labels.txt` |
257
+ | `detect` | Object detection → ranked boxes | anchor scoring + labels |
258
+ | `ctc` | CTC sequence decoding (e.g. OCR) | CTC collapse |
259
+ | `seq2seq` / `chat` | Encoder–decoder / chat loops | cross-attention runtime |
260
+
261
+ Two supporting runtimes deserve a mention: **`tokenizer.c`** (a from-scratch byte-level BPE
262
+ tokenizer reading the same `vocab.bin` + `merges.txt` as `js/Tokenizer.js`) and
263
+ **`kie_runtime.c`** (a receipt key-information-extraction task). Everything sits on the one
264
+ `engine_forward` core.
265
+
266
+ ---
267
+
268
+ ## 6.10 KV-cache orchestration (native generation)
269
+
270
+ Chapter 2 introduced the KV-cache conceptually; the native engine is where it's implemented. Each
271
+ attention node owns a Key and Value cache (`g_kcache[i]`, `g_vcache[i]` in
272
+ `native/engine_internal.h`). Generation splits into two phases:
273
+
274
+ ```
275
+ engine_prefill(n_tokens): run the graph over the whole prompt once, filling every K/V cache
276
+ loop:
277
+ engine_last_logits() ─▶ argmax ─▶ next token
278
+ engine_decode(pos): run the graph for ONE new position, reading cached K/V,
279
+ appending this token's K/V (O(seq) work, not O(seq²))
280
+ ```
281
+
282
+ This is exactly the prefill/decode split that production LLM servers use — implemented in a few
283
+ hundred lines of C here, which makes it unusually readable.
284
+
285
+ ---
286
+
287
+ ## 6.11 Building the native engine
288
+
289
+ One `clang` line builds the whole thing (from the `Makefile`):
290
+
291
+ ```bash
292
+ clang -O3 -mavx2 -mfma -pthread -Inative \
293
+ native/cJSON.c native/safetensors.c native/kernels.c \
294
+ native/quant_cpu_opt.c native/conv_f32_opt.c native/tensor_f32_opt.c \
295
+ native/engine_runtime.c native/engine.c native/image_io.c native/kie_runtime.c \
296
+ native/vulkan_engine.c native/opengl_engine.c native/tokenizer.c native/nnapi_engine.c \
297
+ native/main.c -o native/volvoxai -lm -ldl
298
+ ```
299
+
300
+ - `make build_native` — desktop build (CPU + Vulkan/OpenGL via runtime `dlopen`).
301
+ - `make build_android` — adds `-DUSE_NNAPI` and links `nnapi_engine.c` for Android arm64.
302
+ - `make compile_shaders` — regenerate SPIR-V/GLSL/GLES/Metal from WGSL.
303
+
304
+ Notice there is **no** `-lvulkan` / `-lGL`: the only GPU-related flag is `-ldl`. That single
305
+ absence is the whole "no static GPU dependency" promise, made concrete.
306
+
307
+ ---
308
+
309
+ ## 6.12 The design, in one picture
310
+
311
+ ```
312
+ ┌───────────────────────── native/volvoxai ─────────────────────────┐
313
+ config.json ──▶ build_graph ──▶ fusion pass ──▶ prepack weights ──▶ engine_forward loop │
314
+ .safetensors ─▶ load_weights │ │
315
+ per node: run_node() │
316
+ ├─ CPU: conv_f32_opt / │
317
+ │ quant_cpu_opt / │
318
+ │ kernels.c (AVX2/NEON) │
319
+ └─ GPU/NPU (dlopen'd): │
320
+ vulkan / opengl / nnapi │
321
+ metal on Apple platforms │
322
+ └────────────────────────────────────────────────────────────────────┘
323
+ task wrappers: run · generate · classify · detect · ctc · seq2seq · chat
324
+ ```
325
+
326
+ **The takeaways:**
327
+
328
+ - VolvoxAI is **dual-target by design**: one blueprint feeds both the browser tiers and a
329
+ freestanding native binary.
330
+ - The native engine is **portable without being generic**: no Emscripten, no static GPU SDK —
331
+ GPU drivers are `dlopen`'d at runtime, so one binary spans very different machines.
332
+ - **Reuse is enforced across three axes**: one C kernel source (WASM + native), one shader
333
+ language (WGSL → generated native shader formats, wired per backend), one blueprint
334
+ (all backends) — with the pure-JS reference as the correctness oracle.
335
+ - Everything still reduces to the Chapter 1 loop: **walk the graph, dispatch each node to the best
336
+ available backend.** Native just adds more backends and sharper kernels.
337
+
338
+ **Next:** [Chapter 7 — Glossary & Next Steps →](07-glossary-and-next-steps.md)
@@ -0,0 +1,258 @@
1
+ # Chapter 7 — Glossary & Next Steps
2
+
3
+ *Goal: one place for every term, a concrete path through this repo, and exercises that turn
4
+ reading into skill.*
5
+
6
+ ---
7
+
8
+ ## 7.1 Glossary
9
+
10
+ **Activation** — a tensor of intermediate values flowing between ops (as opposed to a *weight*).
11
+ Kept in fp32 by VolvoxAI (int8 on the native quantized path).
12
+
13
+ **Anchor** — a fixed reference box (a prior) that a detector adjusts, instead of predicting a box
14
+ from scratch. EfficientDet-Lite0 uses 9 per grid cell → 19,206 total.
15
+
16
+ **Attention (SDPA)** — the transformer mechanism where each token compares its **Query** to every
17
+ token's **Key** and blends their **Values** by similarity. "Which earlier words matter to me?"
18
+
19
+ **Autoregressive** — generating a sequence one token at a time, feeding each output back as input.
20
+
21
+ **Backbone** — the feature-extractor stage of a vision model (here, EfficientNet-Lite0).
22
+
23
+ **Backend** — a concrete executor for the graph's ops. Browser backends are the four *tiers*;
24
+ native backends are CPU, Vulkan, OpenGL/GLES, Metal, and NNAPI. VolvoxAI picks one per node.
25
+
26
+ **BiFPN** — Bi-directional Feature Pyramid Network; fuses features across resolutions with
27
+ resize/pool/add so every scale has both detail and meaning.
28
+
29
+ **BPE (Byte-Pair Encoding)** — the tokenizer algorithm: start from bytes, repeatedly merge the
30
+ most frequent adjacent pair, per a learned merge list.
31
+
32
+ **Broadcast** — stretching a smaller tensor to match a larger one in an element-wise op (e.g.
33
+ adding a per-channel bias `[C]` to `[N,H,W,C]`).
34
+
35
+ **Causal mask** — restricting attention so position *q* only sees positions `≤ q`; makes a
36
+ left-to-right generator.
37
+
38
+ **Channel** — one "feature plane" of a tensor (the `C` in NHWC). Input images have 3 (RGB);
39
+ hidden layers have many.
40
+
41
+ **Convolution (Conv2D)** — slide a small learned filter over an image, dot-product at each spot,
42
+ to detect a pattern everywhere. **Depthwise** = per-channel spatial filter; **Pointwise** = 1×1
43
+ channel mixer; the two together (**depthwise-separable**) are cheap and power the backbone.
44
+
45
+ **Dequantize** — convert int8 back to float: `r = (q − zero_point) × scale`.
46
+
47
+ **dlopen / dlsym** — load a shared library and look up its functions *at runtime* (not link
48
+ time). How VolvoxAI's native binary uses a GPU driver (`libvulkan`, `libGL`) without linking any
49
+ GPU SDK — the "no static GPU dependency" design.
50
+
51
+ **Embedding** — a learned vector that represents a discrete token; the `Embedding` op is a table
52
+ lookup.
53
+
54
+ **Forward pass / Inference** — running the graph once, input → output. VolvoxAI does only this
55
+ (no training).
56
+
57
+ **Fusion** — merging adjacent ops (e.g. Conv+ReLU) so intermediate data is written once.
58
+
59
+ **GELU / ReLU / ReLU6 / Sigmoid** — nonlinearities; the "decision curves" between linear layers.
60
+ Without them, stacked MatMuls collapse into one.
61
+
62
+ **GEMM** — GEneral Matrix Multiply; the heavily-optimized routine most fast conv/matmul paths
63
+ reduce to.
64
+
65
+ **Graph** — the model's op list: nodes (ops) connected by named tensors. Stored as `config.json`.
66
+
67
+ **Head** — the final task-specific layer(s): the LM head (→ vocabulary logits) or the detector's
68
+ class/box heads.
69
+
70
+ **im2col** — "image to columns"; unfold conv input patches into a matrix so a conv becomes a GEMM.
71
+
72
+ **int8 / fp16 / fp32** — 8-bit integer / 16-bit float / 32-bit float number formats (1 / 2 / 4
73
+ bytes). See Chapter 4.
74
+
75
+ **KV-cache** — caching past tokens' Keys and Values so each generation step only computes the new
76
+ token's attention. In this repo: `engine_prefill` + `engine_decode`.
77
+
78
+ **LayerNorm / RMSNorm** — normalize a vector (mean 0, variance 1, then learned scale/shift) to
79
+ keep deep-network numbers stable.
80
+
81
+ **Logits** — raw, un-normalized scores (pre-softmax/sigmoid). The LM head and class head emit
82
+ these.
83
+
84
+ **MatMul** — matrix multiply; the core feature-mixing op of transformers.
85
+
86
+ **MBConv** — Mobile inverted BOTTLENECK conv block: expand → depthwise → project, with a residual.
87
+ The backbone's repeating unit.
88
+
89
+ **NHWC / NCHW** — tensor dimension order (batch, height, width, channels) vs (batch, channels,
90
+ height, width). VolvoxAI vision models use NHWC.
91
+
92
+ **naga** — the Rust tool VolvoxAI uses to cross-compile one WGSL shader into SPIR-V (Vulkan),
93
+ GLSL (OpenGL), GLSL ES, and MSL (Metal). Generated shader output is not the same as runtime
94
+ support; the native dispatcher must wire a backend wrapper for an op to run there.
95
+
96
+ **NMS (Non-Max Suppression)** — postprocess that removes overlapping duplicate detections,
97
+ keeping the highest-scoring box per object.
98
+
99
+ **NNAPI** — Android's Neural Networks API; VolvoxAI's native engine can dispatch to it on Android
100
+ (`native/nnapi_engine.c`, `make build_android`).
101
+
102
+ **Node** — one entry in the graph: an op plus its input/output tensor names and parameters.
103
+
104
+ **Op / Operation / Kernel** — a single math routine (Add, Conv2D, SDPA…). "Op" is the graph-level
105
+ name; "kernel" is a specific implementation of it.
106
+
107
+ **Quantization** — representing weights/activations with fewer bits via `scale` + `zero_point`.
108
+
109
+ **Residual (skip connection)** — adding a block's input to its output (`out = x + f(x)`) so
110
+ information and gradients survive deep stacks. In both models.
111
+
112
+ **Safetensors** — the standard binary file format for the weights.
113
+
114
+ **Scale / Zero-point** — the two numbers of a quantization recipe: tick size, and which integer
115
+ means real 0.
116
+
117
+ **Softmax** — turns a vector of scores into a probability distribution (positive, sums to 1).
118
+
119
+ **SPIR-V** — the binary shader format Vulkan consumes; `naga` compiles VolvoxAI's WGSL to it.
120
+
121
+ **Prefill / Decode** — the two phases of native text generation: *prefill* runs the prompt once
122
+ to fill the KV-cache; *decode* runs one new token at a time using the cache. See `engine_prefill`
123
+ / `engine_decode`.
124
+
125
+ **Tensor** — a multi-dimensional array of numbers with a shape; the only data type in the engine.
126
+
127
+ **Tier** — one of VolvoxAI's four browser backends (WebNN / WebGPU / WASM / Pure-JS), picked by
128
+ capability.
129
+
130
+ **Token** — a chunk of text (word/sub-word/byte) mapped to an integer id.
131
+
132
+ **Weight** — a number learned during training, read-only at inference.
133
+
134
+ ---
135
+
136
+ ## 7.2 A path through this repository
137
+
138
+ Read in this order to go from "I get the concepts" to "I can modify the engine":
139
+
140
+ 1. **The data model** — `js/Tensor.js` (25 lines), `js/Graph.js` (48 lines). Tiny; read fully.
141
+ 2. **The executor** — `js/CPUEngine.js`. See the `for (node of graph.nodes)` loop and the
142
+ `switch` dispatch. This is the whole runtime.
143
+ 3. **Four naive kernels** — `js/ops/add.js`, `embedding.js`, `layerNorm.js`, `matMul.js`. Each is
144
+ a few dozen readable lines.
145
+ 4. **The two models' blueprints** — skim `models/tinystories_1m/config.json` and
146
+ `models/efficientdet_lite0_fp32/config.json`. Match nodes to Chapters 2–3.
147
+ 5. **The attention + conv kernels** — `js/ops/sDPA.js`, `js/ops/conv2D.js`. The two "hearts."
148
+ 6. **Quantization** — `js/ops/dequantizeLinear.js`, then `native/quant_cpu_opt.c` for the real
149
+ int8 conv.
150
+ 7. **Optimization** — diff `js/ops/conv2D.js` against `native/conv_f32_opt.c` while reading
151
+ `docs/microkernel_optimization_guide.md` and `docs/xnnpack_optimization_guide.md`.
152
+ 8. **The GPU tier** — `shaders/*.wgsl` (e.g. `matmul`), and `js/GraphExecutor.js`.
153
+ 9. **The native engine** (Chapter 6) — `native/engine.h` + `native/engine.c` (lifecycle),
154
+ `native/engine_runtime.c` (`run_node` backend selection), then a device backend such as
155
+ `native/vulkan_engine.c` (see the `dlopen` at the top). `native/main.c` holds the task CLIs.
156
+
157
+ `docs/operation_list.md` is the per-op × per-backend support matrix — your reference map.
158
+
159
+ ---
160
+
161
+ ## 7.3 Run the models yourself
162
+
163
+ ```bash
164
+ # Language model — generate text (greedy). Builds native binary first: `make build_native`.
165
+ ./native/volvoxai generate models/tinystories_1m \
166
+ --prompt "Once upon a time, Lily" --max-new 50 [--debug]
167
+
168
+ # Raw graph runner — dump the logits tensor for a fixed set of tokens.
169
+ ./native/volvoxai run models/tinystories_1m \
170
+ --input tokens=models/tinystories_1m/tokens.i32 \
171
+ --input positions=models/tinystories_1m/positions.i32 \
172
+ --output logits=out.f32 --last-token 4
173
+
174
+ # Object detector — decode an image into ranked boxes.
175
+ ./native/volvoxai detect models/efficientdet_lite0_int8 \
176
+ --image input0=photo.png --image-normalize raw-255 \
177
+ --boxes boxes --scores scores --max-det 20
178
+
179
+ # In Node (WASM / pure-JS tiers), smoke-test any blueprint:
180
+ node bin/volvox.js run --model models/tinystories_1m/model.safetensors --backend wasm
181
+ ```
182
+
183
+ Add `--debug` to `generate` to see per-node timing and tokens/sec — a great way to *feel* where
184
+ time goes (and to watch the optimizations from Chapter 5 pay off).
185
+
186
+ ---
187
+
188
+ ## 7.4 Exercises (reading → skill)
189
+
190
+ 1. **Trace by hand.** Take the sequence `[5, 5]` (two identical tokens) and a made-up 2-dim
191
+ embedding. Walk `Embedding → Add(position) → LayerNorm` with pen and paper. Confirm the shapes
192
+ match `config.json`.
193
+ 2. **Break causality.** In `js/ops/sDPA.js`, change `k <= q` to `k < seq_len`. Predict what
194
+ happens to generated text and why. (Then revert.)
195
+ 3. **Quantize a weight.** Pick `scale = 0.02`, `zero_point = -5`. Quantize `r = 0.31`, then
196
+ dequantize it back. Report the round-trip error. Now try `scale = 0.002`. What did precision
197
+ cost you in range?
198
+ 4. **Count the FLOPs.** For the first `Conv2D` (stem: 320×320×3 → 160×160×32, 3×3 filter),
199
+ estimate the multiply-adds. Compare to a 1×1 pointwise conv of the same output size. Why is
200
+ depthwise-separable cheaper?
201
+ 5. **Add an op.** Implement an element-wise `Abs` kernel in `js/ops/`, wire it into
202
+ `CPUEngine.js`'s `switch`, and confirm it dispatches. (Follow `js/ops/reLU.js` as a template.)
203
+ 6. **Find a fusion.** In `models/efficientdet_lite0_fp32/config.json`, find a `Conv2D` whose
204
+ `relu` param is set — that's a Conv+ReLU fusion already baked in. Explain what two ops it
205
+ represents.
206
+
207
+ ---
208
+
209
+ ## 7.5 Current gaps in this codebase
210
+
211
+ VolvoxAI is an inference engine, and this textbook follows that boundary. The codebase can load
212
+ trained weights and run forward passes; it does not yet include the systems needed to create,
213
+ tune, or scientifically validate models.
214
+
215
+ | Missing area | What would need to be added | Why it matters |
216
+ |---|---|---|
217
+ | **Training** | Reverse-mode autodiff, backward kernels, loss functions, optimizers such as Adam/SGD, learning-rate schedules, initialization, checkpointing, and regularization. | This is how weights are discovered instead of only consumed. |
218
+ | **Math foundations** | Linear algebra derivations, calculus for chain rule and gradients, probability, entropy/cross-entropy, KL divergence, and likelihood. | These are the tools for explaining why training and evaluation behave the way they do. |
219
+ | **Data** | Dataset manifests, streaming/input pipelines, augmentation, cleaning, tokenizer training, train/validation/test splits, and leakage checks. | Model quality is usually bounded by data quality and experimental hygiene. |
220
+ | **Evaluation & experimentation** | Task metrics, validation loops, baselines, ablations, hyperparameter sweeps, overfitting checks, and bias-variance analysis. | This is how you know a model is actually better, not just different. |
221
+ | **Architecture breadth** | Diffusion models, graph neural networks, RNN/LSTM, reinforcement learning, VAE/GAN, retrieval and embedding systems, multimodal models, mixture-of-experts, and state-space models. | The current walkthrough covers one transformer LM and one CNN detector; many domains use different inductive biases. |
222
+ | **Modern LLM training stack** | Pretraining loops, supervised fine-tuning, LoRA/adapters, RLHF/DPO preference training, distributed data/model parallelism, and FlashAttention internals. | Most frontier LLM work happens around training recipes, memory-efficient attention, and large-scale systems. |
223
+ | **Research practice** | Paper reproduction, result derivation, controlled experiments, scaling-law analysis, error analysis, and documentation of assumptions. | This is the difference between running a model and producing reliable new knowledge. |
224
+
225
+ These are future textbook/code modules, not prerequisites for the inference chapters. Listing
226
+ them makes the scope explicit: this repo is a strong inference/runtime foundation, not a complete
227
+ training-and-research curriculum.
228
+
229
+ ---
230
+
231
+ ## 7.6 Where to go from here
232
+
233
+ The natural next steps split into two tracks:
234
+
235
+ - **Deepen this repo's inference path.** Compare `native/conv_f32_opt.c` to Google's **XNNPACK**;
236
+ `docs/xnnpack_optimization_guide.md` in this repo is a guided tour. Then inspect
237
+ `shaders/*.wgsl` and the native GPU backends.
238
+ - **Scale the transformer.** GPT-2/3, LLaMA, Mistral, Qwen are Chapter 2's graph, wider/deeper,
239
+ with tweaks: **RMSNorm** instead of LayerNorm, **RoPE** rotary positions instead of learned
240
+ `wpe`, **grouped-query attention**, **SwiGLU** MLPs. Each is a small variation on ops you know.
241
+ - **Broaden architectures.** Classification, segmentation, pose, diffusion, retrieval,
242
+ multimodal, MoE, and SSM systems all reuse the tensor/graph mental model, but add different
243
+ blocks and training objectives.
244
+ - **Close the training gap deliberately.** A small autograd engine, a cross-entropy loss, Adam,
245
+ a tiny dataset loader, and a validation loop would be the first concrete additions.
246
+ - **Read the source papers** once the mechanics are concrete: *Attention Is All You Need*
247
+ (transformers), *EfficientDet* (this detector), *EfficientNet* (the backbone), and a
248
+ quantization primer (e.g. the "gemmlowp"/TFLite integer-quantization write-ups).
249
+ - **Explore peer runtimes** from the landscape: **wonnx** (WebGPU/ONNX), **ncnn** (no-deps
250
+ native), **ggml/llama.cpp** (portable C LLM inference).
251
+
252
+ The mental model you built here — *a model is a graph of small tensor ops with learned weights;
253
+ inference walks the graph; performance is memory layout; precision is a size/accuracy dial* —
254
+ transfers to those future modules, but it is one part of the full stack.
255
+
256
+ ---
257
+
258
+ *End of the VolvoxAI textbook. Back to the [index](README.md).*