@openfluke/welvet 0.2.0 → 0.74.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.
@@ -0,0 +1,230 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Loom — Multi-Architecture Training Comparison</title>
6
+ <style>
7
+ * { box-sizing: border-box; margin: 0; padding: 0; }
8
+ body { background: #0d0d0d; color: #c8c8c8; font-family: 'Courier New', monospace; font-size: 13px; padding: 20px; }
9
+ h1 { color: #7ec8e3; margin-bottom: 4px; font-size: 16px; }
10
+ .subtitle { color: #555; margin-bottom: 12px; font-size: 11px; }
11
+ #output { white-space: pre; line-height: 1.65; }
12
+ .pass { color: #4ec94e; }
13
+ .fail { color: #e04040; }
14
+ .warn { color: #e0a020; }
15
+ .info { color: #7ec8e3; }
16
+ .dim { color: #555; }
17
+ button { background: #1e3a4a; color: #7ec8e3; border: 1px solid #2d5a70; padding: 6px 18px;
18
+ font-family: inherit; font-size: 13px; cursor: pointer; margin-right: 8px; }
19
+ button:hover { background: #2d5a70; }
20
+ button:disabled { opacity: 0.4; cursor: not-allowed; }
21
+ #status { margin: 8px 0; color: #555; font-size: 11px; }
22
+ .progress { color: #7ec8e3; }
23
+ </style>
24
+ </head>
25
+ <body>
26
+ <h1>=== M-POLY-VTD Multi-Architecture Training Showdown (WASM) ===</h1>
27
+ <div class="subtitle">Browser port of benchmark_training_comparison.go — 6 architectures, 20 epochs, CPU training with loss tracking</div>
28
+ <button id="runBtn" onclick="runComparison()">Run Comparison</button>
29
+ <div id="status">Loading WASM...</div>
30
+ <pre id="output"></pre>
31
+
32
+ <script src="wasm_exec.js"></script>
33
+ <script>
34
+ let wasmReady = false;
35
+ const outEl = document.getElementById('output');
36
+
37
+ function print(msg, cls) {
38
+ const s = document.createElement('span');
39
+ if (cls) s.className = cls;
40
+ s.textContent = msg + '\n';
41
+ outEl.appendChild(s);
42
+ }
43
+ const p = m => print(m);
44
+ const pInfo= m => print(m, 'info');
45
+ const pPass= m => print(m, 'pass');
46
+ const pFail= m => print(m, 'fail');
47
+ const pWarn= m => print(m, 'warn');
48
+ const pDim = m => print(m, 'dim');
49
+
50
+ async function loadWASM() {
51
+ if (wasmReady) return;
52
+ const go = new Go();
53
+ const r = await WebAssembly.instantiateStreaming(fetch('main.wasm'), go.importObject);
54
+ go.run(r.instance);
55
+ await new Promise(res => setTimeout(res, 150));
56
+ wasmReady = true;
57
+ }
58
+
59
+ // ─────────────────────────────────────────────────────────────
60
+ // 6 architectures matching benchmark_training_comparison.go
61
+ // ─────────────────────────────────────────────────────────────
62
+ const ARCHITECTURES = [
63
+ {
64
+ name: 'Dense MLP (128→512→512→8)',
65
+ inputShape: [128], outputShape: [8],
66
+ numBatches: 8, batchSize: 64, epochs: 20, lr: 0.01,
67
+ cfg: JSON.stringify({ depth:1,rows:1,cols:1,layers_per_cell:3, layers:[
68
+ {z:0,y:0,x:0,l:0,type:"Dense",input_height:128,output_height:512,activation:"ReLU",dtype:"F32"},
69
+ {z:0,y:0,x:0,l:1,type:"Dense",input_height:512,output_height:512,activation:"ReLU",dtype:"F32"},
70
+ {z:0,y:0,x:0,l:2,type:"Dense",input_height:512,output_height:8,activation:"Linear",dtype:"F32"},
71
+ ]})
72
+ },
73
+ {
74
+ name: 'CNN 1D (3ch×128→32f→64f→Dense→8)',
75
+ inputShape: [3, 128], outputShape: [8],
76
+ numBatches: 8, batchSize: 32, epochs: 20, lr: 0.01,
77
+ cfg: JSON.stringify({ depth:1,rows:1,cols:1,layers_per_cell:3, layers:[
78
+ {z:0,y:0,x:0,l:0,type:"CNN1",input_channels:3,filters:32,kernel_size:3,stride:1,padding:1,input_height:128,output_height:128,dtype:"F32"},
79
+ {z:0,y:0,x:0,l:1,type:"CNN1",input_channels:32,filters:64,kernel_size:3,stride:1,padding:1,input_height:128,output_height:128,dtype:"F32"},
80
+ {z:0,y:0,x:0,l:2,type:"Dense",input_height:64*128,output_height:8,activation:"Linear",dtype:"F32"},
81
+ ]})
82
+ },
83
+ {
84
+ name: 'CNN 2D (3ch×32×32→16f→32f→Dense→8)',
85
+ inputShape: [3, 32, 32], outputShape: [8],
86
+ numBatches: 8, batchSize: 16, epochs: 20, lr: 0.01,
87
+ cfg: JSON.stringify({ depth:1,rows:1,cols:1,layers_per_cell:3, layers:[
88
+ {z:0,y:0,x:0,l:0,type:"CNN2",input_channels:3,filters:16,kernel_size:3,stride:1,padding:1,input_height:32,input_width:32,output_height:32,output_width:32,dtype:"F32"},
89
+ {z:0,y:0,x:0,l:1,type:"CNN2",input_channels:16,filters:32,kernel_size:3,stride:1,padding:1,input_height:32,input_width:32,output_height:32,output_width:32,dtype:"F32"},
90
+ {z:0,y:0,x:0,l:2,type:"Dense",input_height:32*32*32,output_height:8,activation:"Linear",dtype:"F32"},
91
+ ]})
92
+ },
93
+ {
94
+ name: 'CNN 3D (2ch×8×8×8→8f→Dense→8)',
95
+ inputShape: [2, 8, 8, 8], outputShape: [8],
96
+ numBatches: 8, batchSize: 8, epochs: 20, lr: 0.01,
97
+ cfg: JSON.stringify({ depth:1,rows:1,cols:1,layers_per_cell:2, layers:[
98
+ {z:0,y:0,x:0,l:0,type:"CNN3",input_channels:2,filters:8,kernel_size:3,stride:1,padding:1,input_depth:8,input_height:8,input_width:8,output_depth:8,output_height:8,output_width:8,dtype:"F32"},
99
+ {z:0,y:0,x:0,l:1,type:"Dense",input_height:8*8*8*8,output_height:8,activation:"Linear",dtype:"F32"},
100
+ ]})
101
+ },
102
+ {
103
+ name: 'RMSNorm MLP (128→Dense512→Norm→Dense512→8)',
104
+ inputShape: [128], outputShape: [8],
105
+ numBatches: 8, batchSize: 64, epochs: 20, lr: 0.01,
106
+ cfg: JSON.stringify({ depth:1,rows:1,cols:1,layers_per_cell:4, layers:[
107
+ {z:0,y:0,x:0,l:0,type:"Dense",input_height:128,output_height:512,activation:"Linear",dtype:"F32"},
108
+ {z:0,y:0,x:0,l:1,type:"RMSNorm",input_height:512,output_height:512,dtype:"F32"},
109
+ {z:0,y:0,x:0,l:2,type:"Dense",input_height:512,output_height:512,activation:"ReLU",dtype:"F32"},
110
+ {z:0,y:0,x:0,l:3,type:"Dense",input_height:512,output_height:8,activation:"Linear",dtype:"F32"},
111
+ ]})
112
+ },
113
+ {
114
+ name: 'Deep Dense MLP (128→512×4→8)',
115
+ inputShape: [128], outputShape: [8],
116
+ numBatches: 8, batchSize: 64, epochs: 20, lr: 0.01,
117
+ cfg: JSON.stringify({ depth:1,rows:1,cols:1,layers_per_cell:5, layers:[
118
+ {z:0,y:0,x:0,l:0,type:"Dense",input_height:128,output_height:512,activation:"ReLU",dtype:"F32"},
119
+ {z:0,y:0,x:0,l:1,type:"Dense",input_height:512,output_height:512,activation:"ReLU",dtype:"F32"},
120
+ {z:0,y:0,x:0,l:2,type:"Dense",input_height:512,output_height:512,activation:"ReLU",dtype:"F32"},
121
+ {z:0,y:0,x:0,l:3,type:"Dense",input_height:512,output_height:512,activation:"ReLU",dtype:"F32"},
122
+ {z:0,y:0,x:0,l:4,type:"Dense",input_height:512,output_height:8,activation:"Linear",dtype:"F32"},
123
+ ]})
124
+ },
125
+ ];
126
+
127
+ function makeBatches(inputShape, outputShape, numBatches, batchSize) {
128
+ const inDim = inputShape.reduce((a,b) => a*b, 1);
129
+ const outDim = outputShape.reduce((a,b) => a*b, 1);
130
+ const batches = [];
131
+ for (let b = 0; b < numBatches; b++) {
132
+ const inp = new Float32Array(batchSize * inDim);
133
+ const tgt = new Float32Array(batchSize * outDim);
134
+ for (let i = 0; i < inp.length; i++) inp[i] = (Math.random() * 2 - 1) * 0.5;
135
+ for (let i = 0; i < tgt.length; i++) tgt[i] = (Math.random() * 2 - 1) * 0.1;
136
+ batches.push({
137
+ input: { shape: [batchSize, ...inputShape], data: Array.from(inp) },
138
+ target: { shape: [batchSize, ...outputShape], data: Array.from(tgt) },
139
+ });
140
+ }
141
+ return batches;
142
+ }
143
+
144
+ async function runTest(arch) {
145
+ const net = window.createLoomNetwork(arch.cfg);
146
+ const batches = makeBatches(arch.inputShape, arch.outputShape, arch.numBatches, arch.batchSize);
147
+ const batchesJSON = JSON.stringify(batches);
148
+
149
+ const t0 = performance.now();
150
+ let result;
151
+ try {
152
+ result = await net.train(batchesJSON, arch.epochs, arch.lr);
153
+ } catch(e) {
154
+ net.free();
155
+ return { err: e.message };
156
+ }
157
+ const elapsed = performance.now() - t0;
158
+
159
+ let initialLoss = null, finalLoss = null;
160
+ try {
161
+ const r = typeof result === 'string' ? JSON.parse(result) : result;
162
+ if (r && r.loss_history && r.loss_history.length > 0) {
163
+ initialLoss = r.loss_history[0];
164
+ finalLoss = r.loss_history[r.loss_history.length - 1];
165
+ }
166
+ } catch(e) {}
167
+
168
+ net.free();
169
+ return { elapsed, initialLoss, finalLoss };
170
+ }
171
+
172
+ async function runComparison() {
173
+ outEl.innerHTML = '';
174
+ document.getElementById('runBtn').disabled = true;
175
+ await loadWASM();
176
+
177
+ pInfo('=== M-POLY-VTD Multi-Architecture Training Showdown ===\n');
178
+ p('Architectures: Dense MLP · CNN 1D · CNN 2D · CNN 3D · RMSNorm MLP · Deep Dense MLP');
179
+ p('Epochs: 20 | Loss: MSE | Learning Rate: 0.01');
180
+ p('');
181
+
182
+ p('| ' + 'Architecture'.padEnd(38) + ' | ' + 'Time (ms)'.padEnd(10) + ' | ' +
183
+ 'Init Loss'.padEnd(10) + ' | ' + 'Final Loss'.padEnd(10) + ' | ' +
184
+ 'Improvement'.padEnd(12) + ' |');
185
+ p('|' + '-'.repeat(39) + '|' + '-'.repeat(11) + '|' + '-'.repeat(11) + '|' +
186
+ '-'.repeat(11) + '|' + '-'.repeat(13) + '|');
187
+
188
+ for (const arch of ARCHITECTURES) {
189
+ document.getElementById('status').textContent = 'Running: ' + arch.name + '...';
190
+ await new Promise(r => setTimeout(r, 0));
191
+
192
+ const res = await runTest(arch);
193
+
194
+ if (res.err) {
195
+ print(`| ${arch.name.padEnd(38)} | ERROR: ${res.err.substring(0, 50)}`);
196
+ continue;
197
+ }
198
+
199
+ const timeStr = res.elapsed.toFixed(1).padEnd(10);
200
+ const iStr = res.initialLoss != null ? res.initialLoss.toFixed(5).padEnd(10) : 'N/A'.padEnd(10);
201
+ const fStr = res.finalLoss != null ? res.finalLoss.toFixed(5).padEnd(10) : 'N/A'.padEnd(10);
202
+ let impStr = 'N/A'.padEnd(12);
203
+ if (res.initialLoss != null && res.finalLoss != null && res.initialLoss > 0) {
204
+ const pct = (res.initialLoss - res.finalLoss) / res.initialLoss * 100;
205
+ const improved = pct > 0;
206
+ impStr = (pct >= 0 ? '+' : '') + pct.toFixed(1) + '%';
207
+ impStr = impStr.padEnd(12);
208
+ }
209
+
210
+ const row = `| ${arch.name.padEnd(38)} | ${timeStr} | ${iStr} | ${fStr} | ${impStr} |`;
211
+ const improved = res.initialLoss != null && res.finalLoss != null && res.finalLoss < res.initialLoss;
212
+ print(row, improved ? 'pass' : 'warn');
213
+ }
214
+
215
+ p('');
216
+ pDim('Note: browser WASM runs single-threaded; times are not comparable to native Go benchmarks.');
217
+ pDim('For CPU vs GPU comparison, the Go benchmark requires native WebGPU driver support.');
218
+
219
+ document.getElementById('status').textContent = 'Done.';
220
+ document.getElementById('runBtn').disabled = false;
221
+ }
222
+
223
+ window.addEventListener('load', async () => {
224
+ await loadWASM();
225
+ document.getElementById('status').textContent = wasmReady
226
+ ? 'WASM ready. Click Run Comparison.' : 'WASM load failed.';
227
+ });
228
+ </script>
229
+ </body>
230
+ </html>
@@ -0,0 +1,360 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Loom WASM C-ABI Verify</title>
6
+ <style>
7
+ * { box-sizing: border-box; margin: 0; padding: 0; }
8
+ body { background: #0d0d0d; color: #c8c8c8; font-family: 'Courier New', monospace; font-size: 13px; padding: 20px; }
9
+ h1 { color: #7ec8e3; margin-bottom: 4px; font-size: 16px; }
10
+ .subtitle { color: #555; margin-bottom: 16px; font-size: 11px; }
11
+ #output { white-space: pre; line-height: 1.6; }
12
+ .pass { color: #4ec94e; }
13
+ .fail { color: #e04040; }
14
+ .warn { color: #e0a020; }
15
+ .info { color: #7ec8e3; }
16
+ .dim { color: #555; }
17
+ button { background: #1e3a4a; color: #7ec8e3; border: 1px solid #2d5a70; padding: 6px 18px;
18
+ font-family: inherit; font-size: 13px; cursor: pointer; margin-right: 8px; }
19
+ button:hover { background: #2d5a70; }
20
+ button:disabled { opacity: 0.4; cursor: not-allowed; }
21
+ #status { margin: 8px 0; color: #555; font-size: 11px; }
22
+ </style>
23
+ </head>
24
+ <body>
25
+ <h1>=== Loom WASM C-ABI Diagnostic Report ===</h1>
26
+ <div class="subtitle">Browser equivalent of cabi_verify.c — checks all welvet WASM symbols and runs smoke tests</div>
27
+ <button id="runBtn" onclick="runAll()">Run Verification</button>
28
+ <div id="status">Load wasm first...</div>
29
+ <pre id="output"></pre>
30
+
31
+ <script src="wasm_exec.js"></script>
32
+ <script>
33
+ let wasmReady = false;
34
+ const out = document.getElementById('output');
35
+ const status = document.getElementById('status');
36
+
37
+ function print(msg, cls) {
38
+ const span = document.createElement('span');
39
+ if (cls) span.className = cls;
40
+ span.textContent = msg + '\n';
41
+ out.appendChild(span);
42
+ }
43
+
44
+ function printRaw(msg) { print(msg); }
45
+ function printPass(msg) { print(msg, 'pass'); }
46
+ function printFail(msg) { print(msg, 'fail'); }
47
+ function printInfo(msg) { print(msg, 'info'); }
48
+ function printWarn(msg) { print(msg, 'warn'); }
49
+ function printDim(msg) { print(msg, 'dim'); }
50
+
51
+ async function loadWASM() {
52
+ if (wasmReady) return;
53
+ status.textContent = 'Loading WASM...';
54
+ try {
55
+ const go = new Go();
56
+ const result = await WebAssembly.instantiateStreaming(
57
+ fetch('main.wasm'), go.importObject);
58
+ go.run(result.instance);
59
+ await new Promise(r => setTimeout(r, 150));
60
+ wasmReady = true;
61
+ status.textContent = 'WASM ready.';
62
+ } catch(e) {
63
+ status.textContent = 'WASM load failed: ' + e.message;
64
+ printFail('Failed to load WASM: ' + e.message);
65
+ }
66
+ }
67
+
68
+ // All expected WASM globals — mirrors the C-ABI symbol table
69
+ const EXPECTED_SYMBOLS = [
70
+ // Network lifecycle
71
+ 'createLoomNetwork', 'loadLoomNetwork',
72
+ // Inference / training
73
+ 'compareLoomDNA',
74
+ // Config factories
75
+ 'getDefaultTargetPropConfig', 'defaultSpliceConfig', 'defaultNEATConfig',
76
+ // Evolution
77
+ 'createLoomNEATPopulation',
78
+ // WebGPU
79
+ 'setupWebGPU',
80
+ ];
81
+
82
+ // Network method symbols (checked on a live network object)
83
+ const EXPECTED_NET_METHODS = [
84
+ 'sequentialForward', 'extractDNA', 'extractBlueprint', 'getLayerCount',
85
+ 'getLayerSpec', 'morphLayer', 'spliceDNA', 'neatMutate',
86
+ 'createSystolicState', 'createTargetPropState', 'initGPU', 'syncToGPU',
87
+ 'syncToCPU', 'train', 'free', '_id',
88
+ ];
89
+
90
+ const EXPECTED_POP_METHODS = [
91
+ '_id', 'size', 'getNetwork', 'evolveWithFitnesses',
92
+ 'best', 'bestFitness', 'summary', 'free',
93
+ ];
94
+
95
+ // Network JSON configs (from networks.h)
96
+ const DENSE_3L = JSON.stringify({
97
+ depth:3, rows:1, cols:1, layers_per_cell:1,
98
+ layers:[
99
+ {z:0,y:0,x:0,l:0,type:"Dense",input_height:16,output_height:16,activation:"ReLU",dtype:"F32"},
100
+ {z:1,y:0,x:0,l:0,type:"Dense",input_height:16,output_height:16,activation:"ReLU",dtype:"F32"},
101
+ {z:2,y:0,x:0,l:0,type:"Dense",input_height:16,output_height:4,activation:"Linear",dtype:"F32"},
102
+ ]
103
+ });
104
+
105
+ const SWIGLU_NET = JSON.stringify({
106
+ depth:2, rows:1, cols:1, layers_per_cell:1,
107
+ layers:[
108
+ {z:0,y:0,x:0,l:0,type:"SwiGLU",input_height:16,output_height:32,dtype:"F32"},
109
+ {z:1,y:0,x:0,l:0,type:"Dense",input_height:32,output_height:4,activation:"Linear",dtype:"F32"},
110
+ ]
111
+ });
112
+
113
+ function tableRow(name, status, detail) {
114
+ const col = status === 'PASS' ? 'pass' : (status === 'FAIL' ? 'fail' : 'warn');
115
+ return ` <span class="${col}">[${status}]</span> ${name.padEnd(52)} ${detail||''}`;
116
+ }
117
+
118
+ async function runAll() {
119
+ out.innerHTML = '';
120
+ document.getElementById('runBtn').disabled = true;
121
+ await loadWASM();
122
+ if (!wasmReady) { document.getElementById('runBtn').disabled = false; return; }
123
+
124
+ let pass = 0, fail = 0;
125
+
126
+ // ── 1. Global symbol check ────────────────────────────────────────────────
127
+ printInfo('\n[1] Checking global WASM exports...\n');
128
+ printDim(' ' + 'Symbol'.padEnd(40) + 'Status');
129
+ printDim(' ' + '─'.repeat(55));
130
+ for (const sym of EXPECTED_SYMBOLS) {
131
+ if (typeof window[sym] === 'function') {
132
+ printRaw(tableRow(sym, 'PASS'));
133
+ pass++;
134
+ } else {
135
+ printRaw(tableRow(sym, 'FAIL', '← missing'));
136
+ fail++;
137
+ }
138
+ }
139
+ printDim(' ' + '─'.repeat(55));
140
+ print(` Total globals checked: ${EXPECTED_SYMBOLS.length} | PASS: ${pass} | FAIL: ${fail}`);
141
+
142
+ // ── 2. Network method check ───────────────────────────────────────────────
143
+ printInfo('\n[2] Checking network wrapper methods...\n');
144
+ let netPass = 0, netFail = 0;
145
+ let net = null;
146
+ try {
147
+ net = window.createLoomNetwork(DENSE_3L);
148
+ printPass(' createLoomNetwork returned: ' + (typeof net));
149
+ } catch(e) {
150
+ printFail(' createLoomNetwork threw: ' + e);
151
+ net = null;
152
+ }
153
+
154
+ if (net) {
155
+ printDim(' ' + 'Method'.padEnd(40) + 'Status');
156
+ printDim(' ' + '─'.repeat(55));
157
+ for (const m of EXPECTED_NET_METHODS) {
158
+ const present = net[m] !== undefined;
159
+ if (present) { netPass++; print(tableRow(m, 'PASS')); }
160
+ else { netFail++; print(tableRow(m, 'FAIL', '← missing')); }
161
+ }
162
+ printDim(' ' + '─'.repeat(55));
163
+ print(` Methods checked: ${EXPECTED_NET_METHODS.length} | PASS: ${netPass} | FAIL: ${netFail}`);
164
+ }
165
+
166
+ // ── 3. Population method check ────────────────────────────────────────────
167
+ printInfo('\n[3] Checking NEAT population wrapper methods...\n');
168
+ let popPass = 0, popFail = 0;
169
+ if (net && typeof window.defaultNEATConfig === 'function') {
170
+ try {
171
+ const cfg = window.defaultNEATConfig(16);
172
+ const pop = window.createLoomNEATPopulation(net._id, 4, cfg);
173
+ printPass(' createLoomNEATPopulation returned: ' + (typeof pop) + ' (size=' + pop.size + ')');
174
+ printDim(' ' + 'Method'.padEnd(40) + 'Status');
175
+ printDim(' ' + '─'.repeat(55));
176
+ for (const m of EXPECTED_POP_METHODS) {
177
+ const present = pop[m] !== undefined;
178
+ if (present) { popPass++; print(tableRow(m, 'PASS')); }
179
+ else { popFail++; print(tableRow(m, 'FAIL', '← missing')); }
180
+ }
181
+ printDim(' ' + '─'.repeat(55));
182
+ print(` Methods checked: ${EXPECTED_POP_METHODS.length} | PASS: ${popPass} | FAIL: ${popFail}`);
183
+ pop.free();
184
+ } catch(e) {
185
+ printFail(' Population creation failed: ' + e);
186
+ }
187
+ }
188
+
189
+ // ── 4. Functional smoke tests ─────────────────────────────────────────────
190
+ printInfo('\n[4] Running functional smoke tests...\n');
191
+ let smokes = 0, smokesFail = 0;
192
+
193
+ function smokeTest(name, fn) {
194
+ try {
195
+ const result = fn();
196
+ printPass(' [PASS] ' + name + (result ? ' → ' + result : ''));
197
+ smokes++;
198
+ } catch(e) {
199
+ printFail(' [FAIL] ' + name + ' → ' + e.message);
200
+ smokesFail++;
201
+ }
202
+ }
203
+
204
+ // createLoomNetwork
205
+ smokeTest('createLoomNetwork(Dense)', () => {
206
+ const n = window.createLoomNetwork(DENSE_3L);
207
+ const c = n.getLayerCount();
208
+ n.free();
209
+ return 'layers=' + c;
210
+ });
211
+
212
+ // sequentialForward
213
+ smokeTest('sequentialForward (16→16→16→4)', () => {
214
+ const n = window.createLoomNetwork(DENSE_3L);
215
+ const input = new Float32Array(16).fill(0.5);
216
+ const out = n.sequentialForward(input);
217
+ n.free();
218
+ if (!out || out.length === 0) throw new Error('empty output');
219
+ return 'out[0]=' + out[0].toFixed(4);
220
+ });
221
+
222
+ // extractDNA
223
+ smokeTest('extractDNA', () => {
224
+ const n = window.createLoomNetwork(DENSE_3L);
225
+ const dna = n.extractDNA();
226
+ n.free();
227
+ const parsed = JSON.parse(dna);
228
+ return 'sigs=' + parsed.length;
229
+ });
230
+
231
+ // compareLoomDNA
232
+ smokeTest('compareLoomDNA', () => {
233
+ const n1 = window.createLoomNetwork(DENSE_3L);
234
+ const n2 = window.createLoomNetwork(DENSE_3L);
235
+ const dna1 = n1.extractDNA();
236
+ const dna2 = n2.extractDNA();
237
+ const result = JSON.parse(window.compareLoomDNA(dna1, dna2));
238
+ n1.free(); n2.free();
239
+ return 'overlap=' + (result.overall_overlap||result.OverallOverlap||'?');
240
+ });
241
+
242
+ // defaultSpliceConfig
243
+ smokeTest('defaultSpliceConfig', () => {
244
+ const cfg = JSON.parse(window.defaultSpliceConfig());
245
+ return 'mode=' + cfg.CrossoverMode;
246
+ });
247
+
248
+ // spliceDNA
249
+ smokeTest('spliceDNA (blend)', () => {
250
+ const a = window.createLoomNetwork(DENSE_3L);
251
+ const b = window.createLoomNetwork(DENSE_3L);
252
+ const cfg = window.defaultSpliceConfig();
253
+ const child = a.spliceDNA(b._id, cfg);
254
+ const c = child.getLayerCount();
255
+ a.free(); b.free(); child.free();
256
+ return 'child layers=' + c;
257
+ });
258
+
259
+ // defaultNEATConfig
260
+ smokeTest('defaultNEATConfig(16)', () => {
261
+ const cfg = JSON.parse(window.defaultNEATConfig(16));
262
+ return 'perturb=' + cfg.WeightPerturbRate;
263
+ });
264
+
265
+ // neatMutate
266
+ smokeTest('neatMutate', () => {
267
+ const n = window.createLoomNetwork(DENSE_3L);
268
+ const cfg = window.defaultNEATConfig(16);
269
+ const mutant = n.neatMutate(cfg);
270
+ const c = mutant.getLayerCount();
271
+ n.free(); mutant.free();
272
+ return 'mutant layers=' + c;
273
+ });
274
+
275
+ // NEAT population
276
+ smokeTest('createLoomNEATPopulation + evolveWithFitnesses', () => {
277
+ const n = window.createLoomNetwork(DENSE_3L);
278
+ const cfg = window.defaultNEATConfig(16);
279
+ const pop = window.createLoomNEATPopulation(n._id, 4, cfg);
280
+ const fitnesses = [1.0, 0.8, 0.5, 0.3];
281
+ pop.evolveWithFitnesses(fitnesses);
282
+ const summary = pop.summary(1);
283
+ pop.free(); n.free();
284
+ return summary.substring(0, 60);
285
+ });
286
+
287
+ // getDefaultTargetPropConfig
288
+ smokeTest('getDefaultTargetPropConfig', () => {
289
+ const cfg = JSON.parse(window.getDefaultTargetPropConfig());
290
+ return 'ok (keys=' + Object.keys(cfg).length + ')';
291
+ });
292
+
293
+ // extractBlueprint
294
+ smokeTest('extractBlueprint', () => {
295
+ const n = window.createLoomNetwork(DENSE_3L);
296
+ const bp = JSON.parse(n.extractBlueprint('test'));
297
+ n.free();
298
+ return 'ok';
299
+ });
300
+
301
+ // getLayerSpec
302
+ smokeTest('getLayerSpec(0)', () => {
303
+ const n = window.createLoomNetwork(DENSE_3L);
304
+ const spec = JSON.parse(n.getLayerSpec(0));
305
+ n.free();
306
+ return 'type=' + spec.type + ' ih=' + spec.input_height;
307
+ });
308
+
309
+ // morphLayer
310
+ smokeTest('morphLayer (F32→F16)', () => {
311
+ const n = window.createLoomNetwork(DENSE_3L);
312
+ const result = JSON.parse(n.morphLayer(0, 2)); // 2=F16
313
+ n.free();
314
+ return result.status || result.error || 'ok';
315
+ });
316
+
317
+ // createSystolicState
318
+ smokeTest('createSystolicState', () => {
319
+ const n = window.createLoomNetwork(DENSE_3L);
320
+ const s = n.createSystolicState();
321
+ const hasMethods = typeof s.step === 'function';
322
+ n.free();
323
+ return 'has step=' + hasMethods;
324
+ });
325
+
326
+ // SwiGLU network
327
+ smokeTest('createLoomNetwork (SwiGLU)', () => {
328
+ const n = window.createLoomNetwork(SWIGLU_NET);
329
+ const c = n.getLayerCount();
330
+ n.free();
331
+ return 'layers=' + c;
332
+ });
333
+
334
+ printDim('\n ' + '─'.repeat(55));
335
+ print(` Smoke tests: ${smokes + smokesFail} | PASS: ${smokes} | FAIL: ${smokesFail}`);
336
+
337
+ // ── 5. Summary ────────────────────────────────────────────────────────────
338
+ const totalPass = pass + netPass + popPass + smokes;
339
+ const totalFail = fail + netFail + popFail + smokesFail;
340
+ printInfo('\n[5] Final Summary\n');
341
+ printDim(' ═'.repeat(28));
342
+ if (totalFail === 0) {
343
+ printPass(` SUCCESS: All ${totalPass} checks passed.`);
344
+ } else {
345
+ printFail(` PARTIAL: ${totalPass} passed, ${totalFail} FAILED.`);
346
+ }
347
+ printDim(' ═'.repeat(28));
348
+
349
+ document.getElementById('runBtn').disabled = false;
350
+ if (net) net.free();
351
+ }
352
+
353
+ // Auto-load WASM on page load
354
+ window.addEventListener('load', async () => {
355
+ await loadWASM();
356
+ status.textContent = wasmReady ? 'WASM ready. Click Run Verification.' : 'WASM load failed.';
357
+ });
358
+ </script>
359
+ </body>
360
+ </html>