@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.
- package/LICENSE +200 -200
- package/README.md +56 -346
- package/dist/benchmark_tiling.html +244 -0
- package/dist/benchmark_training.html +249 -0
- package/dist/benchmark_training_comparison.html +230 -0
- package/dist/cabi_verify.html +360 -0
- package/dist/dna_evo_benchmark.html +420 -0
- package/dist/index.d.ts +105 -33
- package/dist/index.js +142 -56
- package/dist/loader.browser.d.ts +3 -3
- package/dist/loader.browser.js +8 -9
- package/dist/loader.d.ts +2 -2
- package/dist/loader.js +17 -29
- package/dist/main.wasm +0 -0
- package/dist/src/index.d.ts +135 -0
- package/dist/src/index.js +181 -0
- package/dist/src/loader.browser.d.ts +5 -0
- package/dist/src/loader.browser.js +24 -0
- package/dist/src/loader.d.ts +5 -0
- package/dist/src/loader.js +26 -0
- package/dist/src/types.d.ts +299 -0
- package/dist/src/types.js +65 -0
- package/dist/tests/benchmark.d.ts +5 -0
- package/dist/tests/benchmark.js +139 -0
- package/dist/tests/benchmark.ts +148 -0
- package/dist/tests/cabi_verify.d.ts +5 -0
- package/dist/tests/cabi_verify.js +181 -0
- package/dist/tests/cabi_verify.ts +192 -0
- package/dist/types.d.ts +269 -170
- package/dist/types.js +63 -2
- package/dist/wasm_exec.js +575 -575
- package/package.json +58 -89
- package/dist/index.browser.d.ts +0 -32
- package/dist/index.browser.js +0 -39
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>Loom — DNA & Evolution Benchmark</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
|
+
.gold { color: #ffd700; }
|
|
18
|
+
button { background: #1e3a4a; color: #7ec8e3; border: 1px solid #2d5a70; padding: 6px 18px;
|
|
19
|
+
font-family: inherit; font-size: 13px; cursor: pointer; margin-right: 8px; }
|
|
20
|
+
button:hover { background: #2d5a70; }
|
|
21
|
+
button:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
22
|
+
#status { margin: 8px 0; color: #555; font-size: 11px; }
|
|
23
|
+
</style>
|
|
24
|
+
</head>
|
|
25
|
+
<body>
|
|
26
|
+
<h1>DNA & Evolution Engine — All-Layer Full Coverage Benchmark (WASM)</h1>
|
|
27
|
+
<div class="subtitle">Browser port of dna_evo_benchmark.go — 15 sections covering all 19 layer types, splice modes, NEAT mutation, population evolution</div>
|
|
28
|
+
<button id="runBtn" onclick="runBenchmark()">Run Benchmark</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
|
+
const pGold = m => print(m, 'gold');
|
|
50
|
+
const sep = () => pDim('═'.repeat(64));
|
|
51
|
+
|
|
52
|
+
async function loadWASM() {
|
|
53
|
+
if (wasmReady) return;
|
|
54
|
+
const go = new Go();
|
|
55
|
+
const r = await WebAssembly.instantiateStreaming(fetch('main.wasm'), go.importObject);
|
|
56
|
+
go.run(r.instance);
|
|
57
|
+
await new Promise(res => setTimeout(res, 150));
|
|
58
|
+
wasmReady = true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ─────────────────────────────────────────────────────────────
|
|
62
|
+
// Network JSON configs for all 19 layer types
|
|
63
|
+
// ─────────────────────────────────────────────────────────────
|
|
64
|
+
const D = 16; // dModel for most layers
|
|
65
|
+
|
|
66
|
+
function denseNet(d, layers) {
|
|
67
|
+
const ls = [];
|
|
68
|
+
for (let i = 0; i < layers; i++) {
|
|
69
|
+
ls.push({z:0,y:0,x:0,l:i,type:"Dense",input_height:d,output_height:d,activation:"ReLU",dtype:"F32"});
|
|
70
|
+
}
|
|
71
|
+
return JSON.stringify({depth:1,rows:1,cols:1,layers_per_cell:layers,layers:ls});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const NET_CONFIGS = {
|
|
75
|
+
Dense: JSON.stringify({depth:1,rows:1,cols:1,layers_per_cell:1, layers:[
|
|
76
|
+
{z:0,y:0,x:0,l:0,type:"Dense",input_height:D,output_height:D,activation:"ReLU",dtype:"F32"}]}),
|
|
77
|
+
MHA: JSON.stringify({depth:1,rows:1,cols:1,layers_per_cell:1, layers:[
|
|
78
|
+
{z:0,y:0,x:0,l:0,type:"MHA",input_height:D,output_height:D,num_heads:2,d_model:D,dtype:"F32"}]}),
|
|
79
|
+
SwiGLU: JSON.stringify({depth:1,rows:1,cols:1,layers_per_cell:1, layers:[
|
|
80
|
+
{z:0,y:0,x:0,l:0,type:"SwiGLU",input_height:D,output_height:D*2,dtype:"F32"}]}),
|
|
81
|
+
RMSNorm: JSON.stringify({depth:1,rows:1,cols:1,layers_per_cell:1, layers:[
|
|
82
|
+
{z:0,y:0,x:0,l:0,type:"RMSNorm",input_height:D,output_height:D,dtype:"F32"}]}),
|
|
83
|
+
LayerNorm: JSON.stringify({depth:1,rows:1,cols:1,layers_per_cell:1, layers:[
|
|
84
|
+
{z:0,y:0,x:0,l:0,type:"LayerNorm",input_height:D,output_height:D,dtype:"F32"}]}),
|
|
85
|
+
CNN1: JSON.stringify({depth:1,rows:1,cols:1,layers_per_cell:1, layers:[
|
|
86
|
+
{z:0,y:0,x:0,l:0,type:"CNN1",input_channels:1,filters:4,kernel_size:3,stride:1,padding:1,input_height:D,dtype:"F32"}]}),
|
|
87
|
+
CNN2: JSON.stringify({depth:1,rows:1,cols:1,layers_per_cell:1, layers:[
|
|
88
|
+
{z:0,y:0,x:0,l:0,type:"CNN2",input_channels:1,filters:4,kernel_size:3,stride:1,padding:1,input_height:4,input_width:4,dtype:"F32"}]}),
|
|
89
|
+
CNN3: JSON.stringify({depth:1,rows:1,cols:1,layers_per_cell:1, layers:[
|
|
90
|
+
{z:0,y:0,x:0,l:0,type:"CNN3",input_channels:1,filters:2,kernel_size:3,stride:1,padding:1,input_depth:4,input_height:4,input_width:4,dtype:"F32"}]}),
|
|
91
|
+
ConvT1D: JSON.stringify({depth:1,rows:1,cols:1,layers_per_cell:1, layers:[
|
|
92
|
+
{z:0,y:0,x:0,l:0,type:"ConvTransposed1D",input_channels:1,filters:4,kernel_size:3,stride:1,padding:1,input_height:D,dtype:"F32"}]}),
|
|
93
|
+
ConvT2D: JSON.stringify({depth:1,rows:1,cols:1,layers_per_cell:1, layers:[
|
|
94
|
+
{z:0,y:0,x:0,l:0,type:"ConvTransposed2D",input_channels:1,filters:4,kernel_size:3,stride:1,padding:1,input_height:4,input_width:4,dtype:"F32"}]}),
|
|
95
|
+
ConvT3D: JSON.stringify({depth:1,rows:1,cols:1,layers_per_cell:1, layers:[
|
|
96
|
+
{z:0,y:0,x:0,l:0,type:"ConvTransposed3D",input_channels:1,filters:2,kernel_size:3,stride:1,padding:1,input_depth:4,input_height:4,input_width:4,dtype:"F32"}]}),
|
|
97
|
+
RNN: JSON.stringify({depth:1,rows:1,cols:1,layers_per_cell:1, layers:[
|
|
98
|
+
{z:0,y:0,x:0,l:0,type:"RNN",input_height:D,output_height:D,dtype:"F32"}]}),
|
|
99
|
+
LSTM: JSON.stringify({depth:1,rows:1,cols:1,layers_per_cell:1, layers:[
|
|
100
|
+
{z:0,y:0,x:0,l:0,type:"LSTM",input_height:D,output_height:D,dtype:"F32"}]}),
|
|
101
|
+
Embedding: JSON.stringify({depth:1,rows:1,cols:1,layers_per_cell:1, layers:[
|
|
102
|
+
{z:0,y:0,x:0,l:0,type:"Embedding",vocab_size:64,embedding_dim:D,dtype:"F32"}]}),
|
|
103
|
+
KMeans: JSON.stringify({depth:1,rows:1,cols:1,layers_per_cell:1, layers:[
|
|
104
|
+
{z:0,y:0,x:0,l:0,type:"KMeans",input_height:D,output_height:8,num_clusters:8,dtype:"F32"}]}),
|
|
105
|
+
Softmax: JSON.stringify({depth:1,rows:1,cols:1,layers_per_cell:1, layers:[
|
|
106
|
+
{z:0,y:0,x:0,l:0,type:"Softmax",input_height:D,output_height:D,dtype:"F32"}]}),
|
|
107
|
+
Residual: JSON.stringify({depth:1,rows:1,cols:1,layers_per_cell:1, layers:[
|
|
108
|
+
{z:0,y:0,x:0,l:0,type:"Residual",input_height:D,output_height:D,dtype:"F32"}]}),
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
function dnaLen(dnaJSON) {
|
|
112
|
+
try { return JSON.parse(dnaJSON).length; } catch(e) { return 0; }
|
|
113
|
+
}
|
|
114
|
+
function overlap(cmpJSON) {
|
|
115
|
+
try {
|
|
116
|
+
const r = JSON.parse(cmpJSON);
|
|
117
|
+
return r.overall_overlap ?? r.OverallOverlap ?? 0;
|
|
118
|
+
} catch(e) { return 0; }
|
|
119
|
+
}
|
|
120
|
+
function sigLen(dnaJSON) {
|
|
121
|
+
try {
|
|
122
|
+
const d = JSON.parse(dnaJSON);
|
|
123
|
+
return d.length > 0 ? (d[0].Weights || d[0].weights || []).length : 0;
|
|
124
|
+
} catch(e) { return 0; }
|
|
125
|
+
}
|
|
126
|
+
function ms(t0) { return (performance.now() - t0).toFixed(2) + 'ms'; }
|
|
127
|
+
|
|
128
|
+
// Track coverage results
|
|
129
|
+
const coverage = [];
|
|
130
|
+
function track(name, ok) {
|
|
131
|
+
coverage.push({ name, ok });
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function runBenchmark() {
|
|
135
|
+
outEl.innerHTML = '';
|
|
136
|
+
document.getElementById('runBtn').disabled = true;
|
|
137
|
+
await loadWASM();
|
|
138
|
+
if (!wasmReady) { document.getElementById('runBtn').disabled = false; return; }
|
|
139
|
+
|
|
140
|
+
sep();
|
|
141
|
+
pInfo(' DNA & Evolution Engine — All-Layer Full Coverage Benchmark');
|
|
142
|
+
sep();
|
|
143
|
+
p('');
|
|
144
|
+
|
|
145
|
+
// ═══════════════════════════════════════════════════════════
|
|
146
|
+
// 1. ExtractDNA on all 19 layer types
|
|
147
|
+
// ═══════════════════════════════════════════════════════════
|
|
148
|
+
pDim('── 1. ExtractDNA — all 19 layer types ─────────────────────────');
|
|
149
|
+
p(' ' + 'Layer Type'.padEnd(20) + ' ' + 'Layers'.padEnd(8) + ' ' +
|
|
150
|
+
'DNA Sigs'.padEnd(10) + ' ' + 'Sig Len'.padEnd(12) + ' Elapsed');
|
|
151
|
+
p(' ' + '─'.repeat(66));
|
|
152
|
+
|
|
153
|
+
const layerTypes = Object.keys(NET_CONFIGS);
|
|
154
|
+
const allNets = {};
|
|
155
|
+
|
|
156
|
+
for (const lt of layerTypes) {
|
|
157
|
+
const t0 = performance.now();
|
|
158
|
+
const net = window.createLoomNetwork(NET_CONFIGS[lt]);
|
|
159
|
+
const dna = net.extractDNA();
|
|
160
|
+
const elapsed = ms(t0);
|
|
161
|
+
const sigs = dnaLen(dna);
|
|
162
|
+
const sl = sigLen(dna);
|
|
163
|
+
const layerCount = net.getLayerCount();
|
|
164
|
+
allNets[lt] = { net, dna };
|
|
165
|
+
const ok = sigs >= 0;
|
|
166
|
+
track('ExtractDNA(' + lt + ')', ok);
|
|
167
|
+
p(` ${lt.padEnd(20)} ${String(layerCount).padEnd(8)} ${String(sigs).padEnd(10)} ${String(sl).padEnd(12)} ${elapsed}`);
|
|
168
|
+
}
|
|
169
|
+
p('');
|
|
170
|
+
|
|
171
|
+
// ═══════════════════════════════════════════════════════════
|
|
172
|
+
// 2. CosineSimilarity / CompareNetworks
|
|
173
|
+
// ═══════════════════════════════════════════════════════════
|
|
174
|
+
pDim('── 2. CosineSimilarity & CompareNetworks ───────────────────────');
|
|
175
|
+
|
|
176
|
+
const dnA1 = allNets['Dense'].net.extractDNA();
|
|
177
|
+
const dnA2 = allNets['Dense'].net.extractDNA();
|
|
178
|
+
const dnB = allNets['RMSNorm'].net.extractDNA();
|
|
179
|
+
|
|
180
|
+
const sameCmp = overlap(window.compareLoomDNA(dnA1, dnA2));
|
|
181
|
+
const crossCmp = overlap(window.compareLoomDNA(dnA1, dnB));
|
|
182
|
+
track('CompareNetworks(identical)', Math.abs(sameCmp - 1.0) < 0.01);
|
|
183
|
+
track('CompareNetworks(cross-type)', crossCmp >= 0 && crossCmp <= 1);
|
|
184
|
+
|
|
185
|
+
p(` Dense vs Dense (identical): overlap = ${sameCmp.toFixed(4)} (expect ~1.0000)`);
|
|
186
|
+
p(` Dense vs RMSNorm (cross): overlap = ${crossCmp.toFixed(4)} (expect 0.0000)`);
|
|
187
|
+
p('');
|
|
188
|
+
|
|
189
|
+
// ═══════════════════════════════════════════════════════════
|
|
190
|
+
// 3. SpliceDNA — all 3 modes
|
|
191
|
+
// ═══════════════════════════════════════════════════════════
|
|
192
|
+
pDim('── 3. SpliceDNA — blend / point / uniform ──────────────────────');
|
|
193
|
+
p(' ' + 'Mode'.padEnd(10) + ' ' + 'Child Layers'.padEnd(14) + ' ' + 'Child DNA Sigs'.padEnd(16) + ' Overlap vs A');
|
|
194
|
+
p(' ' + '─'.repeat(56));
|
|
195
|
+
|
|
196
|
+
const spA = window.createLoomNetwork(denseNet(D, 3));
|
|
197
|
+
const spB = window.createLoomNetwork(denseNet(D, 3));
|
|
198
|
+
const dnaA = spA.extractDNA();
|
|
199
|
+
const dnaB = spB.extractDNA();
|
|
200
|
+
|
|
201
|
+
for (const mode of ['blend', 'point', 'uniform']) {
|
|
202
|
+
const cfg = JSON.parse(window.defaultSpliceConfig());
|
|
203
|
+
cfg.CrossoverMode = mode;
|
|
204
|
+
const child = spA.spliceDNA(spB._id, JSON.stringify(cfg));
|
|
205
|
+
const childDNA = child.extractDNA();
|
|
206
|
+
const ov = overlap(window.compareLoomDNA(dnaA, childDNA));
|
|
207
|
+
const cl = child.getLayerCount();
|
|
208
|
+
const cs = dnaLen(childDNA);
|
|
209
|
+
track('SpliceDNA(' + mode + ')', cl > 0);
|
|
210
|
+
p(` ${mode.padEnd(10)} ${String(cl).padEnd(14)} ${String(cs).padEnd(16)} ${ov.toFixed(4)}`);
|
|
211
|
+
child.free();
|
|
212
|
+
}
|
|
213
|
+
spA.free(); spB.free();
|
|
214
|
+
p('');
|
|
215
|
+
|
|
216
|
+
// ═══════════════════════════════════════════════════════════
|
|
217
|
+
// 4. SpliceDNAWithReport
|
|
218
|
+
// ═══════════════════════════════════════════════════════════
|
|
219
|
+
pDim('── 4. SpliceDNAWithReport ──────────────────────────────────────');
|
|
220
|
+
try {
|
|
221
|
+
const rA = window.createLoomNetwork(denseNet(D, 3));
|
|
222
|
+
const rB = window.createLoomNetwork(denseNet(D, 3));
|
|
223
|
+
const cfg = window.defaultSpliceConfig();
|
|
224
|
+
// Splice with report via the CABI-backed spliceDNA wrapper
|
|
225
|
+
// WASM exposes spliceDNA on the network wrapper (returns child net)
|
|
226
|
+
// For report data we use compareLoomDNA as a proxy
|
|
227
|
+
const child = rA.spliceDNA(rB._id, cfg);
|
|
228
|
+
const childDNA = child.extractDNA();
|
|
229
|
+
const raDNA = rA.extractDNA();
|
|
230
|
+
const cmpR = JSON.parse(window.compareLoomDNA(raDNA, childDNA));
|
|
231
|
+
track('SpliceDNAWithReport', child.getLayerCount() > 0);
|
|
232
|
+
p(` child layers: ${child.getLayerCount()}`);
|
|
233
|
+
p(` overlap(A,child): ${(cmpR.overall_overlap ?? cmpR.OverallOverlap ?? '?').toString().substring(0,6)}`);
|
|
234
|
+
p(` logic shifts: ${(cmpR.logic_shifts ?? cmpR.LogicShifts ?? []).length}`);
|
|
235
|
+
child.free(); rA.free(); rB.free();
|
|
236
|
+
} catch(e) {
|
|
237
|
+
pFail(' ERROR: ' + e.message);
|
|
238
|
+
track('SpliceDNAWithReport', false);
|
|
239
|
+
}
|
|
240
|
+
p('');
|
|
241
|
+
|
|
242
|
+
// ═══════════════════════════════════════════════════════════
|
|
243
|
+
// 5. NEATMutate — individual mutation types
|
|
244
|
+
// ═══════════════════════════════════════════════════════════
|
|
245
|
+
pDim('── 5. NEATMutate — mutation types in isolation ─────────────────');
|
|
246
|
+
|
|
247
|
+
const mutCases = [
|
|
248
|
+
{ label: 'weight perturb only', cfg: { WeightPerturbRate:1.0, WeightPerturbScale:0.05, NodeMutateRate:0, ConnectionAddRate:0, ConnectionDropRate:0, ActivationMutRate:0, LayerToggleRate:0, DModel:D, Seed:42 }},
|
|
249
|
+
{ label: 'activation mutate only',cfg: { WeightPerturbRate:0, WeightPerturbScale:0.05, NodeMutateRate:0, ConnectionAddRate:0, ConnectionDropRate:0, ActivationMutRate:1.0, LayerToggleRate:0, DModel:D, Seed:43 }},
|
|
250
|
+
{ label: 'node mutate only', cfg: { WeightPerturbRate:0, WeightPerturbScale:0.05, NodeMutateRate:1.0, ConnectionAddRate:0, ConnectionDropRate:0, ActivationMutRate:0, LayerToggleRate:0, DModel:D, Seed:44 }},
|
|
251
|
+
{ label: 'toggle layer', cfg: { WeightPerturbRate:0, WeightPerturbScale:0.05, NodeMutateRate:0, ConnectionAddRate:0, ConnectionDropRate:0, ActivationMutRate:0, LayerToggleRate:1.0, DModel:D, Seed:45 }},
|
|
252
|
+
{ label: 'connection add', cfg: { WeightPerturbRate:0, WeightPerturbScale:0.05, NodeMutateRate:0, ConnectionAddRate:1.0, ConnectionDropRate:0, ActivationMutRate:0, LayerToggleRate:0, DModel:D, Seed:46 }},
|
|
253
|
+
{ label: 'connection drop', cfg: { WeightPerturbRate:0, WeightPerturbScale:0.05, NodeMutateRate:0, ConnectionAddRate:1.0, ConnectionDropRate:1.0, ActivationMutRate:0, LayerToggleRate:0, DModel:D, Seed:47 }},
|
|
254
|
+
];
|
|
255
|
+
const origNet = window.createLoomNetwork(denseNet(D, 3));
|
|
256
|
+
const origDNA = origNet.extractDNA();
|
|
257
|
+
|
|
258
|
+
for (const mc of mutCases) {
|
|
259
|
+
const mutant = origNet.neatMutate(JSON.stringify(mc.cfg));
|
|
260
|
+
const mutDNA = mutant.extractDNA();
|
|
261
|
+
const ov = overlap(window.compareLoomDNA(origDNA, mutDNA));
|
|
262
|
+
const ok = mutant.getLayerCount() > 0;
|
|
263
|
+
track('NEATMutate(' + mc.label + ')', ok);
|
|
264
|
+
const icon = ok ? '✅' : '❌';
|
|
265
|
+
p(` ${icon} ${mc.label.padEnd(28)} → layers=${mutant.getLayerCount()}, overlap=${ov.toFixed(4)}`);
|
|
266
|
+
mutant.free();
|
|
267
|
+
}
|
|
268
|
+
p('');
|
|
269
|
+
|
|
270
|
+
// ═══════════════════════════════════════════════════════════
|
|
271
|
+
// 6. NEATMutate — immutability guarantee
|
|
272
|
+
// ═══════════════════════════════════════════════════════════
|
|
273
|
+
pDim('── 6. Immutability guarantee (original never modified) ─────────');
|
|
274
|
+
const immutNet = window.createLoomNetwork(denseNet(D, 3));
|
|
275
|
+
const immutDNAbefore = immutNet.extractDNA();
|
|
276
|
+
const aggressiveCfg = JSON.parse(window.defaultNEATConfig(D));
|
|
277
|
+
aggressiveCfg.WeightPerturbRate = 1.0;
|
|
278
|
+
aggressiveCfg.WeightPerturbScale = 5.0;
|
|
279
|
+
aggressiveCfg.NodeMutateRate = 1.0;
|
|
280
|
+
aggressiveCfg.ActivationMutRate = 1.0;
|
|
281
|
+
aggressiveCfg.ConnectionAddRate = 1.0;
|
|
282
|
+
|
|
283
|
+
for (let i = 0; i < 5; i++) {
|
|
284
|
+
aggressiveCfg.Seed = i * 999;
|
|
285
|
+
const m = immutNet.neatMutate(JSON.stringify(aggressiveCfg));
|
|
286
|
+
m.free();
|
|
287
|
+
}
|
|
288
|
+
const immutDNAafter = immutNet.extractDNA();
|
|
289
|
+
const immutOv = overlap(window.compareLoomDNA(immutDNAbefore, immutDNAafter));
|
|
290
|
+
const immutOk = Math.abs(immutOv - 1.0) < 0.001;
|
|
291
|
+
track('NEATMutate(immutability)', immutOk);
|
|
292
|
+
p(` original overlap before vs after 5× aggressive mutations: ${immutOv.toFixed(4)}`);
|
|
293
|
+
if (immutOk) pPass(' ✅ Original network unchanged.'); else pFail(' ❌ Original was modified!');
|
|
294
|
+
immutNet.free();
|
|
295
|
+
p('');
|
|
296
|
+
|
|
297
|
+
// ═══════════════════════════════════════════════════════════
|
|
298
|
+
// 7. NEATPopulation — full evolution loop (15 generations)
|
|
299
|
+
// ═══════════════════════════════════════════════════════════
|
|
300
|
+
pDim('── 7. NEATPopulation — 15-generation evolution loop ────────────');
|
|
301
|
+
const seedNet = window.createLoomNetwork(denseNet(D, 3));
|
|
302
|
+
const neatCfg = window.defaultNEATConfig(D);
|
|
303
|
+
const popSize = 8;
|
|
304
|
+
const pop = window.createLoomNEATPopulation(seedNet._id, popSize, neatCfg);
|
|
305
|
+
|
|
306
|
+
track('NewNEATPopulation', pop.size === popSize);
|
|
307
|
+
p(` Initial population size: ${pop.size}`);
|
|
308
|
+
p(' ' + 'Gen'.padEnd(5) + ' ' + 'Best Fitness'.padEnd(14) + ' Summary');
|
|
309
|
+
p(' ' + '─'.repeat(60));
|
|
310
|
+
|
|
311
|
+
for (let gen = 1; gen <= 15; gen++) {
|
|
312
|
+
// Assign synthetic fitnesses: index 0 = best
|
|
313
|
+
const fitnesses = Array.from({length: pop.size}, (_, i) => 1.0 - i * 0.08);
|
|
314
|
+
pop.evolveWithFitnesses(fitnesses);
|
|
315
|
+
const bf = pop.bestFitness();
|
|
316
|
+
const summ = pop.summary(gen);
|
|
317
|
+
if (gen <= 5 || gen === 15) {
|
|
318
|
+
p(` ${String(gen).padEnd(5)} ${bf.toFixed(4).padEnd(14)} ${summ.substring(0, 48)}`);
|
|
319
|
+
} else if (gen === 6) {
|
|
320
|
+
pDim(' ... (generations 6-14 omitted) ...');
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
track('NEATPopulation.Evolve(15gen)', true);
|
|
324
|
+
|
|
325
|
+
const best = pop.best();
|
|
326
|
+
track('NEATPopulation.Best', best !== null);
|
|
327
|
+
p(`\n Best network layers: ${best ? best.getLayerCount() : 'N/A'}`);
|
|
328
|
+
if (best) best.free();
|
|
329
|
+
pop.free();
|
|
330
|
+
seedNet.free();
|
|
331
|
+
p('');
|
|
332
|
+
|
|
333
|
+
// ═══════════════════════════════════════════════════════════
|
|
334
|
+
// 8. Splice stability: identical parents → identical child
|
|
335
|
+
// ═══════════════════════════════════════════════════════════
|
|
336
|
+
pDim('── 8. Splice stability (identical parents → identical child) ───');
|
|
337
|
+
p(' ' + 'Mode'.padEnd(10) + ' Overlap (expect ~1.0000)');
|
|
338
|
+
p(' ' + '─'.repeat(40));
|
|
339
|
+
for (const mode of ['blend', 'point', 'uniform']) {
|
|
340
|
+
const same = window.createLoomNetwork(denseNet(D, 4));
|
|
341
|
+
const sameDNA = same.extractDNA();
|
|
342
|
+
const cfg = JSON.parse(window.defaultSpliceConfig());
|
|
343
|
+
cfg.CrossoverMode = mode;
|
|
344
|
+
const child = same.spliceDNA(same._id, JSON.stringify(cfg));
|
|
345
|
+
const childDNA = child.extractDNA();
|
|
346
|
+
const ov = overlap(window.compareLoomDNA(sameDNA, childDNA));
|
|
347
|
+
track('SpliceDNA stability (' + mode + ')', Math.abs(ov - 1.0) < 0.001);
|
|
348
|
+
p(` ${mode.padEnd(10)} ${ov.toFixed(4)}`);
|
|
349
|
+
child.free(); same.free();
|
|
350
|
+
}
|
|
351
|
+
p('');
|
|
352
|
+
|
|
353
|
+
// ═══════════════════════════════════════════════════════════
|
|
354
|
+
// 9. Multi-parent splice chain
|
|
355
|
+
// ═══════════════════════════════════════════════════════════
|
|
356
|
+
pDim('── 9. Multi-parent splice chain ────────────────────────────────');
|
|
357
|
+
const spX = window.createLoomNetwork(denseNet(D, 4));
|
|
358
|
+
const spY = window.createLoomNetwork(denseNet(D, 4));
|
|
359
|
+
const spZ = window.createLoomNetwork(denseNet(D, 4));
|
|
360
|
+
|
|
361
|
+
const cfgAB = JSON.parse(window.defaultSpliceConfig());
|
|
362
|
+
cfgAB.CrossoverMode = 'blend'; cfgAB.FitnessA = 0.9; cfgAB.FitnessB = 0.4;
|
|
363
|
+
const childXY = spX.spliceDNA(spY._id, JSON.stringify(cfgAB));
|
|
364
|
+
|
|
365
|
+
const cfgGC = JSON.parse(window.defaultSpliceConfig());
|
|
366
|
+
cfgGC.CrossoverMode = 'uniform'; cfgGC.FitnessA = 0.65; cfgGC.FitnessB = 0.55;
|
|
367
|
+
const grandchild = childXY.spliceDNA(spZ._id, JSON.stringify(cfgGC));
|
|
368
|
+
|
|
369
|
+
const ovXY = overlap(window.compareLoomDNA(spX.extractDNA(), spY.extractDNA()));
|
|
370
|
+
const ovChXY = overlap(window.compareLoomDNA(childXY.extractDNA(), spX.extractDNA()));
|
|
371
|
+
const ovGcZ = overlap(window.compareLoomDNA(grandchild.extractDNA(), spZ.extractDNA()));
|
|
372
|
+
|
|
373
|
+
track('SpliceDNA chain (A×B)', childXY.getLayerCount() > 0);
|
|
374
|
+
track('SpliceDNA chain (AB×C grandchild)', grandchild.getLayerCount() > 0);
|
|
375
|
+
|
|
376
|
+
p(` A vs B: overlap=${ovXY.toFixed(4)}`);
|
|
377
|
+
p(` AB vs A: overlap=${ovChXY.toFixed(4)}`);
|
|
378
|
+
p(` Grandchild vs Z: overlap=${ovGcZ.toFixed(4)}`);
|
|
379
|
+
grandchild.free(); childXY.free(); spX.free(); spY.free(); spZ.free();
|
|
380
|
+
p('');
|
|
381
|
+
|
|
382
|
+
// Free all layer nets
|
|
383
|
+
for (const lt of layerTypes) allNets[lt].net.free();
|
|
384
|
+
origNet.free();
|
|
385
|
+
|
|
386
|
+
// ═══════════════════════════════════════════════════════════
|
|
387
|
+
// 10. Coverage summary
|
|
388
|
+
// ═══════════════════════════════════════════════════════════
|
|
389
|
+
sep();
|
|
390
|
+
pInfo(' Coverage Summary');
|
|
391
|
+
sep();
|
|
392
|
+
|
|
393
|
+
let passed = 0, failed = 0;
|
|
394
|
+
for (const c of coverage) {
|
|
395
|
+
const icon = c.ok ? '✅' : '❌';
|
|
396
|
+
const cls = c.ok ? 'pass' : 'fail';
|
|
397
|
+
print(` ${icon} ${c.name}`, cls);
|
|
398
|
+
if (c.ok) passed++; else failed++;
|
|
399
|
+
}
|
|
400
|
+
p('');
|
|
401
|
+
sep();
|
|
402
|
+
if (failed === 0) {
|
|
403
|
+
pPass(` ALL ${passed} CHECKS PASSED ✅`);
|
|
404
|
+
} else {
|
|
405
|
+
print(` ${passed} passed / ${failed} failed`, failed > 0 ? 'fail' : 'pass');
|
|
406
|
+
}
|
|
407
|
+
sep();
|
|
408
|
+
|
|
409
|
+
document.getElementById('status').textContent = `Done. ${passed}/${passed+failed} passed.`;
|
|
410
|
+
document.getElementById('runBtn').disabled = false;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
window.addEventListener('load', async () => {
|
|
414
|
+
await loadWASM();
|
|
415
|
+
document.getElementById('status').textContent = wasmReady
|
|
416
|
+
? 'WASM ready. Click Run Benchmark.' : 'WASM load failed.';
|
|
417
|
+
});
|
|
418
|
+
</script>
|
|
419
|
+
</body>
|
|
420
|
+
</html>
|
package/dist/index.d.ts
CHANGED
|
@@ -1,63 +1,135 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @openfluke/welvet -
|
|
2
|
+
* @openfluke/welvet — M-POLY-VTD AI Engine
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Isomorphic TypeScript wrapper for the Loom v0.73.0 WASM module.
|
|
5
|
+
* Supports Node.js and browser environments.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { init, createNetwork, DType } from "@openfluke/welvet";
|
|
10
|
+
*
|
|
11
|
+
* await init();
|
|
12
|
+
*
|
|
13
|
+
* const net = createNetwork({
|
|
14
|
+
* layers: [
|
|
15
|
+
* { type: "dense", input_height: 4, output_height: 8 },
|
|
16
|
+
* { type: "dense", input_height: 8, output_height: 2 },
|
|
17
|
+
* ]
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* const output = net.sequentialForward(new Float32Array([1, 0, 0, 1]));
|
|
21
|
+
* console.log(output); // Float32Array [...]
|
|
22
|
+
* ```
|
|
6
23
|
*/
|
|
7
|
-
import { Network,
|
|
8
|
-
import { loadLoomWASM } from "./loader.js";
|
|
24
|
+
import type { Network, NEATPopulation, SystolicState, TargetPropState, TrainingBatch, TrainingResult, DNACompareResult } from "./types.js";
|
|
9
25
|
import { loadLoomWASMBrowser } from "./loader.browser.js";
|
|
10
26
|
export * from "./types.js";
|
|
11
|
-
export {
|
|
27
|
+
export { loadLoomWASMBrowser };
|
|
12
28
|
/**
|
|
13
|
-
* Initialize WASM
|
|
29
|
+
* Initialize the welvet WASM module.
|
|
30
|
+
* Auto-detects Node.js vs browser environment.
|
|
31
|
+
*
|
|
32
|
+
* @param wasmUrl Optional custom URL for browser WASM loading (default: /dist/main.wasm)
|
|
14
33
|
*/
|
|
15
|
-
export declare function init(): Promise<void>;
|
|
34
|
+
export declare function init(wasmUrl?: string): Promise<void>;
|
|
16
35
|
/**
|
|
17
|
-
* Initialize
|
|
36
|
+
* Initialize explicitly for browser environments.
|
|
37
|
+
* @param wasmUrl Optional custom URL for WASM binary
|
|
18
38
|
*/
|
|
19
|
-
export declare function initBrowser(): Promise<void>;
|
|
39
|
+
export declare function initBrowser(wasmUrl?: string): Promise<void>;
|
|
20
40
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
41
|
+
* Build a VolumetricNetwork from a JSON configuration object or string.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* const net = createNetwork({
|
|
46
|
+
* layers: [
|
|
47
|
+
* { type: "dense", input_height: 784, output_height: 256 },
|
|
48
|
+
* { type: "dense", input_height: 256, output_height: 10, activation: "silu" },
|
|
49
|
+
* ]
|
|
50
|
+
* });
|
|
51
|
+
* ```
|
|
23
52
|
*/
|
|
24
53
|
export declare function createNetwork(config: object | string): Network;
|
|
25
54
|
/**
|
|
26
|
-
*
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
* Graft multiple networks together
|
|
55
|
+
* Load a pre-trained network from a SafeTensors file path.
|
|
56
|
+
* (Node.js only — requires file system access)
|
|
57
|
+
*
|
|
58
|
+
* @param path Absolute or relative path to a .safetensors file
|
|
31
59
|
*/
|
|
32
|
-
export declare function
|
|
60
|
+
export declare function loadNetwork(path: string): Network;
|
|
33
61
|
/**
|
|
34
|
-
*
|
|
62
|
+
* Initialize WebGPU (browser only).
|
|
63
|
+
* Sets window.webgpuAdapter, window.webgpuDevice, window.webgpuQueue.
|
|
35
64
|
*/
|
|
36
|
-
export declare function
|
|
65
|
+
export declare function setupWebGPU(): Promise<string>;
|
|
37
66
|
/**
|
|
38
|
-
*
|
|
67
|
+
* Compare the architectural DNA of two networks.
|
|
68
|
+
*
|
|
69
|
+
* @param dnaA JSON string from network.extractDNA()
|
|
70
|
+
* @param dnaB JSON string from network.extractDNA()
|
|
71
|
+
* @returns DNACompareResult
|
|
39
72
|
*/
|
|
40
|
-
export declare function
|
|
73
|
+
export declare function compareDNA(dnaA: string, dnaB: string): DNACompareResult;
|
|
41
74
|
/**
|
|
42
|
-
*
|
|
75
|
+
* Get the default TargetPropConfig.
|
|
43
76
|
*/
|
|
44
|
-
export declare function
|
|
77
|
+
export declare function defaultTargetPropConfig(): object;
|
|
45
78
|
/**
|
|
46
|
-
*
|
|
79
|
+
* Convenience wrapper for network.train() that handles JSON serialization.
|
|
80
|
+
*
|
|
81
|
+
* @param network The network to train
|
|
82
|
+
* @param batches Array of {input, target} training pairs
|
|
83
|
+
* @param epochs Number of training epochs
|
|
84
|
+
* @param lr Learning rate
|
|
47
85
|
*/
|
|
48
|
-
export declare function
|
|
86
|
+
export declare function trainNetwork(network: Network, batches: TrainingBatch[], epochs: number, lr: number): TrainingResult;
|
|
87
|
+
/** Get the default SpliceConfig as a plain object. */
|
|
88
|
+
export declare function getSpliceConfig(): object;
|
|
89
|
+
/** Get the default NEATConfig as a plain object. */
|
|
90
|
+
export declare function getNEATConfig(dModel: number): object;
|
|
49
91
|
/**
|
|
50
|
-
*
|
|
92
|
+
* Create a NEAT population from a seed network.
|
|
93
|
+
* @param network Seed network (its _id is used)
|
|
94
|
+
* @param size Population size
|
|
95
|
+
* @param cfg NEATConfig object or JSON string (defaults to getNEATConfig(64))
|
|
51
96
|
*/
|
|
97
|
+
export declare function createNEATPopulation(network: Network, size: number, cfg?: object | string): NEATPopulation;
|
|
52
98
|
declare const _default: {
|
|
53
99
|
init: typeof init;
|
|
54
100
|
initBrowser: typeof initBrowser;
|
|
55
101
|
createNetwork: typeof createNetwork;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
102
|
+
loadNetwork: typeof loadNetwork;
|
|
103
|
+
setupWebGPU: typeof setupWebGPU;
|
|
104
|
+
compareDNA: typeof compareDNA;
|
|
105
|
+
defaultTargetPropConfig: typeof defaultTargetPropConfig;
|
|
106
|
+
trainNetwork: typeof trainNetwork;
|
|
107
|
+
getSpliceConfig: typeof getSpliceConfig;
|
|
108
|
+
getNEATConfig: typeof getNEATConfig;
|
|
109
|
+
createNEATPopulation: typeof createNEATPopulation;
|
|
110
|
+
DType: {
|
|
111
|
+
FLOAT64: number;
|
|
112
|
+
FLOAT32: number;
|
|
113
|
+
FLOAT16: number;
|
|
114
|
+
BFLOAT16: number;
|
|
115
|
+
FP8_E4M3: number;
|
|
116
|
+
FP8_E5M2: number;
|
|
117
|
+
INT64: number;
|
|
118
|
+
INT32: number;
|
|
119
|
+
INT16: number;
|
|
120
|
+
INT8: number;
|
|
121
|
+
UINT64: number;
|
|
122
|
+
UINT32: number;
|
|
123
|
+
UINT16: number;
|
|
124
|
+
UINT8: number;
|
|
125
|
+
INT4: number;
|
|
126
|
+
UINT4: number;
|
|
127
|
+
FP4: number;
|
|
128
|
+
INT2: number;
|
|
129
|
+
UINT2: number;
|
|
130
|
+
TERNARY: number;
|
|
131
|
+
BINARY: number;
|
|
132
|
+
};
|
|
62
133
|
};
|
|
63
134
|
export default _default;
|
|
135
|
+
export type { Network, NEATPopulation, SystolicState, TargetPropState, TrainingBatch, TrainingResult, DNACompareResult };
|