@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,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* benchmark.ts
|
|
3
|
+
* TypeScript port of benchmark_training.html
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { loadLoomWASM } from "../src/loader.js";
|
|
7
|
+
|
|
8
|
+
const TRAINING_CASES = [
|
|
9
|
+
{
|
|
10
|
+
name: 'Dense (Linear)', iters: 5, inDim: 512, outDim: 512,
|
|
11
|
+
cfg: JSON.stringify({ depth: 1, rows: 1, cols: 1, layers_per_cell: 1, layers: [
|
|
12
|
+
{ z: 0, y: 0, x: 0, l: 0, type: "Dense", input_height: 512, output_height: 512, activation: "Linear", dtype: "F32" }
|
|
13
|
+
]})
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: 'RMSNorm', iters: 5, inDim: 512, outDim: 512,
|
|
17
|
+
cfg: JSON.stringify({ depth: 1, rows: 1, cols: 1, layers_per_cell: 1, layers: [
|
|
18
|
+
{ z: 0, y: 0, x: 0, l: 0, type: "RMSNorm", input_height: 512, output_height: 512, dtype: "F32" }
|
|
19
|
+
]})
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: 'SwiGLU (MLP)', iters: 5, inDim: 512, outDim: 1024,
|
|
23
|
+
cfg: JSON.stringify({ depth: 1, rows: 1, cols: 1, layers_per_cell: 1, layers: [
|
|
24
|
+
{ z: 0, y: 0, x: 0, l: 0, type: "SwiGLU", input_height: 512, output_height: 1024, dtype: "F32" }
|
|
25
|
+
]})
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: 'Embedding', iters: 5, inDim: 16, outDim: 2048, isEmbedding: true,
|
|
29
|
+
cfg: JSON.stringify({ depth: 1, rows: 1, cols: 1, layers_per_cell: 1, layers: [
|
|
30
|
+
{ z: 0, y: 0, x: 0, l: 0, type: "Embedding", vocab_size: 1024, embedding_dim: 128, dtype: "F32" }
|
|
31
|
+
]})
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: 'Residual Add', iters: 5, inDim: 512, outDim: 512,
|
|
35
|
+
cfg: JSON.stringify({ depth: 1, rows: 1, cols: 1, layers_per_cell: 1, layers: [
|
|
36
|
+
{ z: 0, y: 0, x: 0, l: 0, type: "Residual", input_height: 512, output_height: 512, dtype: "F32" }
|
|
37
|
+
]})
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: 'MHA (Fused)', iters: 5, inDim: 128, outDim: 128,
|
|
41
|
+
cfg: JSON.stringify({ depth: 1, rows: 1, cols: 1, layers_per_cell: 1, layers: [
|
|
42
|
+
{ z: 0, y: 0, x: 0, l: 0, type: "MHA", input_height: 128, output_height: 128, num_heads: 4, d_model: 128, dtype: "F32" }
|
|
43
|
+
]})
|
|
44
|
+
}
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
function makeTrainBatches(inDim: number, outDim: number, nBatches: number, batchSize: number, isEmbedding?: boolean) {
|
|
48
|
+
const batches: any[] = [];
|
|
49
|
+
for (let b = 0; b < nBatches; b++) {
|
|
50
|
+
const inp = new Float32Array(batchSize * inDim);
|
|
51
|
+
const tgt = new Float32Array(batchSize * outDim);
|
|
52
|
+
if (isEmbedding) {
|
|
53
|
+
for (let i = 0; i < inp.length; i++) inp[i] = i % 1024;
|
|
54
|
+
} else {
|
|
55
|
+
for (let i = 0; i < inp.length; i++) inp[i] = (Math.random() * 2 - 1) * 0.5;
|
|
56
|
+
}
|
|
57
|
+
for (let i = 0; i < tgt.length; i++) tgt[i] = Math.random() * 0.1;
|
|
58
|
+
batches.push({
|
|
59
|
+
input: { shape: [batchSize, inDim], data: Array.from(inp) },
|
|
60
|
+
target: { shape: [batchSize, outDim], data: Array.from(tgt) }
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return batches;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function runCase(tc: any) {
|
|
67
|
+
// @ts-ignore
|
|
68
|
+
const net = globalThis.createLoomNetwork(tc.cfg);
|
|
69
|
+
const batchSize = 4;
|
|
70
|
+
const nBatches = 4;
|
|
71
|
+
const epochs = 3;
|
|
72
|
+
|
|
73
|
+
const batches = makeTrainBatches(tc.inDim, tc.outDim, nBatches, batchSize, tc.isEmbedding);
|
|
74
|
+
const batchesJSON = JSON.stringify(batches);
|
|
75
|
+
|
|
76
|
+
const input = new Float32Array(tc.inDim);
|
|
77
|
+
input.fill(0.5);
|
|
78
|
+
if (tc.isEmbedding) for (let i = 0; i < input.length; i++) input[i] = i % 1024;
|
|
79
|
+
|
|
80
|
+
// warm-up
|
|
81
|
+
net.sequentialForward(input);
|
|
82
|
+
|
|
83
|
+
const t0 = performance.now();
|
|
84
|
+
let lastOut: any;
|
|
85
|
+
for (let i = 0; i < tc.iters; i++) {
|
|
86
|
+
lastOut = net.sequentialForward(input);
|
|
87
|
+
}
|
|
88
|
+
const fwdMs = (performance.now() - t0) / tc.iters;
|
|
89
|
+
|
|
90
|
+
let trainMs = -1;
|
|
91
|
+
let initialLoss: number | null = null, finalLoss: number | null = null;
|
|
92
|
+
try {
|
|
93
|
+
const t1 = performance.now();
|
|
94
|
+
const trainResult = await net.train(batchesJSON, epochs, 0.001);
|
|
95
|
+
trainMs = performance.now() - t1;
|
|
96
|
+
if (typeof trainResult === 'string') {
|
|
97
|
+
try {
|
|
98
|
+
const r = JSON.parse(trainResult);
|
|
99
|
+
if (r.loss_history && r.loss_history.length > 0) {
|
|
100
|
+
initialLoss = r.loss_history[0];
|
|
101
|
+
finalLoss = r.loss_history[r.loss_history.length - 1];
|
|
102
|
+
}
|
|
103
|
+
} catch (e) {}
|
|
104
|
+
}
|
|
105
|
+
} catch (e) {
|
|
106
|
+
trainMs = -1;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const sample = lastOut ? [lastOut[0] || 0, lastOut[1] || 0, lastOut[2] || 0] : null;
|
|
110
|
+
const sanity = sample && sample.some((v: number) => Math.abs(v) > 1e-9);
|
|
111
|
+
net.free();
|
|
112
|
+
return { fwdMs, trainMs, sample, sanity, initialLoss, finalLoss };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export async function runBenchmark() {
|
|
116
|
+
console.log("=== M-POLY-VTD Training Showdown Benchmark ===");
|
|
117
|
+
|
|
118
|
+
// Decide which loader to use
|
|
119
|
+
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
120
|
+
const { loadLoomWASM } = await import("../src/loader.js");
|
|
121
|
+
await loadLoomWASM();
|
|
122
|
+
} else {
|
|
123
|
+
// @ts-ignore
|
|
124
|
+
const { loadLoomWASMBrowser } = await import("../src/loader.browser.js");
|
|
125
|
+
await loadLoomWASMBrowser();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
console.log("Layer".padEnd(15) + " | " + "Fwd ms/it".padEnd(11) + " | " + "Train ms".padEnd(10) +
|
|
129
|
+
" | " + "Init Loss".padEnd(11) + " | " + "Final Loss".padEnd(11) + " | Sanity");
|
|
130
|
+
console.log("-".repeat(85));
|
|
131
|
+
|
|
132
|
+
for (const tc of TRAINING_CASES) {
|
|
133
|
+
const res = await runCase(tc);
|
|
134
|
+
|
|
135
|
+
const fwdStr = res.fwdMs >= 0 ? res.fwdMs.toFixed(3).padEnd(10) : 'N/A'.padEnd(10);
|
|
136
|
+
const trainStr = res.trainMs >= 0 ? res.trainMs.toFixed(1).padEnd(9) : 'N/A'.padEnd(9);
|
|
137
|
+
const iLoss = res.initialLoss != null ? res.initialLoss.toFixed(4).padEnd(10) : 'N/A'.padEnd(10);
|
|
138
|
+
const fLoss = res.finalLoss != null ? res.finalLoss.toFixed(4).padEnd(10) : 'N/A'.padEnd(10);
|
|
139
|
+
const sanStr = res.sanity ? 'REAL' : 'ZERO';
|
|
140
|
+
|
|
141
|
+
console.log(`${tc.name.padEnd(15)} | ${fwdStr} | ${trainStr} | ${iLoss} | ${fLoss} | ${sanStr}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Auto-run if executed directly via Node.js/tsx
|
|
146
|
+
if (typeof process !== "undefined" && import.meta.url.includes(process.argv[1].replace(/\\/g, '/'))) {
|
|
147
|
+
runBenchmark();
|
|
148
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cabi_verify.ts
|
|
3
|
+
* TypeScript port of cabi_verify.html
|
|
4
|
+
*/
|
|
5
|
+
const EXPECTED_SYMBOLS = [
|
|
6
|
+
'createLoomNetwork', 'loadLoomNetwork',
|
|
7
|
+
'compareLoomDNA',
|
|
8
|
+
'getDefaultTargetPropConfig', 'defaultSpliceConfig', 'defaultNEATConfig',
|
|
9
|
+
'createLoomNEATPopulation',
|
|
10
|
+
'setupWebGPU',
|
|
11
|
+
];
|
|
12
|
+
const EXPECTED_NET_METHODS = [
|
|
13
|
+
'sequentialForward', 'extractDNA', 'extractBlueprint', 'getLayerCount',
|
|
14
|
+
'getLayerSpec', 'morphLayer', 'spliceDNA', 'neatMutate',
|
|
15
|
+
'createSystolicState', 'createTargetPropState', 'initGPU', 'syncToGPU',
|
|
16
|
+
'syncToCPU', 'train', 'free', '_id',
|
|
17
|
+
];
|
|
18
|
+
const EXPECTED_POP_METHODS = [
|
|
19
|
+
'_id', 'size', 'getNetwork', 'evolveWithFitnesses',
|
|
20
|
+
'best', 'bestFitness', 'summary', 'free',
|
|
21
|
+
];
|
|
22
|
+
const DENSE_3L = JSON.stringify({
|
|
23
|
+
depth: 3, rows: 1, cols: 1, layers_per_cell: 1,
|
|
24
|
+
layers: [
|
|
25
|
+
{ z: 0, y: 0, x: 0, l: 0, type: "Dense", input_height: 16, output_height: 16, activation: "ReLU", dtype: "F32" },
|
|
26
|
+
{ z: 1, y: 0, x: 0, l: 0, type: "Dense", input_height: 16, output_height: 16, activation: "ReLU", dtype: "F32" },
|
|
27
|
+
{ z: 2, y: 0, x: 0, l: 0, type: "Dense", input_height: 16, output_height: 4, activation: "Linear", dtype: "F32" },
|
|
28
|
+
]
|
|
29
|
+
});
|
|
30
|
+
const SWIGLU_NET = JSON.stringify({
|
|
31
|
+
depth: 2, rows: 1, cols: 1, layers_per_cell: 1,
|
|
32
|
+
layers: [
|
|
33
|
+
{ z: 0, y: 0, x: 0, l: 0, type: "SwiGLU", input_height: 16, output_height: 32, dtype: "F32" },
|
|
34
|
+
{ z: 1, y: 0, x: 0, l: 0, type: "Dense", input_height: 32, output_height: 4, activation: "Linear", dtype: "F32" },
|
|
35
|
+
]
|
|
36
|
+
});
|
|
37
|
+
export async function runVerify() {
|
|
38
|
+
console.log("=== Loom WASM C-ABI Diagnostic Report ===");
|
|
39
|
+
// Decide which loader to use
|
|
40
|
+
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
41
|
+
const { loadLoomWASM } = await import("../src/loader.js");
|
|
42
|
+
await loadLoomWASM();
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
// @ts-ignore
|
|
46
|
+
const { loadLoomWASMBrowser } = await import("../src/loader.browser.js");
|
|
47
|
+
await loadLoomWASMBrowser();
|
|
48
|
+
}
|
|
49
|
+
let totalPass = 0;
|
|
50
|
+
let totalFail = 0;
|
|
51
|
+
// 1. Global symbol check
|
|
52
|
+
console.log("\n[1] Checking global WASM exports...");
|
|
53
|
+
for (const sym of EXPECTED_SYMBOLS) {
|
|
54
|
+
// @ts-ignore
|
|
55
|
+
if (typeof globalThis[sym] === 'function') {
|
|
56
|
+
console.log(` [PASS] ${sym}`);
|
|
57
|
+
totalPass++;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
console.error(` [FAIL] ${sym} (missing)`);
|
|
61
|
+
totalFail++;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// 2. Network method check
|
|
65
|
+
console.log("\n[2] Checking network wrapper methods...");
|
|
66
|
+
let net = null;
|
|
67
|
+
try {
|
|
68
|
+
// @ts-ignore
|
|
69
|
+
net = globalThis.createLoomNetwork(DENSE_3L);
|
|
70
|
+
if (net) {
|
|
71
|
+
for (const m of EXPECTED_NET_METHODS) {
|
|
72
|
+
if (net[m] !== undefined) {
|
|
73
|
+
console.log(` [PASS] ${m}`);
|
|
74
|
+
totalPass++;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
console.error(` [FAIL] ${m} (missing)`);
|
|
78
|
+
totalFail++;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
console.error(" [FAIL] createLoomNetwork failed:", e);
|
|
85
|
+
totalFail++;
|
|
86
|
+
}
|
|
87
|
+
// 3. Population method check
|
|
88
|
+
if (net) {
|
|
89
|
+
console.log("\n[3] Checking NEAT population wrapper methods...");
|
|
90
|
+
try {
|
|
91
|
+
// @ts-ignore
|
|
92
|
+
const cfg = globalThis.defaultNEATConfig(16);
|
|
93
|
+
// @ts-ignore
|
|
94
|
+
const pop = globalThis.createLoomNEATPopulation(net._id, 4, cfg);
|
|
95
|
+
if (pop) {
|
|
96
|
+
for (const m of EXPECTED_POP_METHODS) {
|
|
97
|
+
if (pop[m] !== undefined) {
|
|
98
|
+
console.log(` [PASS] ${m}`);
|
|
99
|
+
totalPass++;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
console.error(` [FAIL] ${m} (missing)`);
|
|
103
|
+
totalFail++;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
pop.free();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch (e) {
|
|
110
|
+
console.error(" [FAIL] Population creation failed:", e);
|
|
111
|
+
totalFail++;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// 4. Functional smoke tests
|
|
115
|
+
console.log("\n[4] Running functional smoke tests...");
|
|
116
|
+
const smokeTest = (name, fn) => {
|
|
117
|
+
try {
|
|
118
|
+
const result = fn();
|
|
119
|
+
console.log(` [PASS] ${name}${result ? " → " + result : ""}`);
|
|
120
|
+
totalPass++;
|
|
121
|
+
}
|
|
122
|
+
catch (e) {
|
|
123
|
+
console.error(` [FAIL] ${name} → ${e.message}`);
|
|
124
|
+
totalFail++;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
smokeTest("sequentialForward", () => {
|
|
128
|
+
// @ts-ignore
|
|
129
|
+
const n = globalThis.createLoomNetwork(DENSE_3L);
|
|
130
|
+
const input = new Float32Array(16).fill(0.5);
|
|
131
|
+
const out = n.sequentialForward(input);
|
|
132
|
+
n.free();
|
|
133
|
+
if (!out || out.length === 0)
|
|
134
|
+
throw new Error("empty output");
|
|
135
|
+
return "out[0]=" + out[0].toFixed(4);
|
|
136
|
+
});
|
|
137
|
+
smokeTest("extractDNA", () => {
|
|
138
|
+
// @ts-ignore
|
|
139
|
+
const n = globalThis.createLoomNetwork(DENSE_3L);
|
|
140
|
+
const dna = n.extractDNA();
|
|
141
|
+
n.free();
|
|
142
|
+
const parsed = JSON.parse(dna);
|
|
143
|
+
return "sigs=" + parsed.length;
|
|
144
|
+
});
|
|
145
|
+
smokeTest("compareLoomDNA", () => {
|
|
146
|
+
// @ts-ignore
|
|
147
|
+
const n1 = globalThis.createLoomNetwork(DENSE_3L);
|
|
148
|
+
// @ts-ignore
|
|
149
|
+
const n2 = globalThis.createLoomNetwork(DENSE_3L);
|
|
150
|
+
const dna1 = n1.extractDNA();
|
|
151
|
+
const dna2 = n2.extractDNA();
|
|
152
|
+
// @ts-ignore
|
|
153
|
+
const result = JSON.parse(globalThis.compareLoomDNA(dna1, dna2));
|
|
154
|
+
n1.free();
|
|
155
|
+
n2.free();
|
|
156
|
+
return "overlap=" + (result.overall_overlap || result.OverallOverlap || "?");
|
|
157
|
+
});
|
|
158
|
+
smokeTest("createLoomNetwork (SwiGLU)", () => {
|
|
159
|
+
// @ts-ignore
|
|
160
|
+
const n = globalThis.createLoomNetwork(SWIGLU_NET);
|
|
161
|
+
const c = n.getLayerCount();
|
|
162
|
+
n.free();
|
|
163
|
+
return "layers=" + c;
|
|
164
|
+
});
|
|
165
|
+
console.log("\n[5] Final Summary");
|
|
166
|
+
console.log("================================");
|
|
167
|
+
if (totalFail === 0) {
|
|
168
|
+
console.log(`SUCCESS: All ${totalPass} checks passed.`);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
console.warn(`PARTIAL: ${totalPass} passed, ${totalFail} FAILED.`);
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
174
|
+
console.log("================================");
|
|
175
|
+
if (net)
|
|
176
|
+
net.free();
|
|
177
|
+
}
|
|
178
|
+
// Auto-run if executed directly via Node.js/tsx
|
|
179
|
+
if (typeof process !== "undefined" && import.meta.url.includes(process.argv[1].replace(/\\/g, '/'))) {
|
|
180
|
+
runVerify();
|
|
181
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cabi_verify.ts
|
|
3
|
+
* TypeScript port of cabi_verify.html
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { loadLoomWASM } from "../src/loader.js";
|
|
7
|
+
|
|
8
|
+
const EXPECTED_SYMBOLS = [
|
|
9
|
+
'createLoomNetwork', 'loadLoomNetwork',
|
|
10
|
+
'compareLoomDNA',
|
|
11
|
+
'getDefaultTargetPropConfig', 'defaultSpliceConfig', 'defaultNEATConfig',
|
|
12
|
+
'createLoomNEATPopulation',
|
|
13
|
+
'setupWebGPU',
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
const EXPECTED_NET_METHODS = [
|
|
17
|
+
'sequentialForward', 'extractDNA', 'extractBlueprint', 'getLayerCount',
|
|
18
|
+
'getLayerSpec', 'morphLayer', 'spliceDNA', 'neatMutate',
|
|
19
|
+
'createSystolicState', 'createTargetPropState', 'initGPU', 'syncToGPU',
|
|
20
|
+
'syncToCPU', 'train', 'free', '_id',
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
const EXPECTED_POP_METHODS = [
|
|
24
|
+
'_id', 'size', 'getNetwork', 'evolveWithFitnesses',
|
|
25
|
+
'best', 'bestFitness', 'summary', 'free',
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const DENSE_3L = JSON.stringify({
|
|
29
|
+
depth: 3, rows: 1, cols: 1, layers_per_cell: 1,
|
|
30
|
+
layers: [
|
|
31
|
+
{ z: 0, y: 0, x: 0, l: 0, type: "Dense", input_height: 16, output_height: 16, activation: "ReLU", dtype: "F32" },
|
|
32
|
+
{ z: 1, y: 0, x: 0, l: 0, type: "Dense", input_height: 16, output_height: 16, activation: "ReLU", dtype: "F32" },
|
|
33
|
+
{ z: 2, y: 0, x: 0, l: 0, type: "Dense", input_height: 16, output_height: 4, activation: "Linear", dtype: "F32" },
|
|
34
|
+
]
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const SWIGLU_NET = JSON.stringify({
|
|
38
|
+
depth: 2, rows: 1, cols: 1, layers_per_cell: 1,
|
|
39
|
+
layers: [
|
|
40
|
+
{ z: 0, y: 0, x: 0, l: 0, type: "SwiGLU", input_height: 16, output_height: 32, dtype: "F32" },
|
|
41
|
+
{ z: 1, y: 0, x: 0, l: 0, type: "Dense", input_height: 32, output_height: 4, activation: "Linear", dtype: "F32" },
|
|
42
|
+
]
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
export async function runVerify() {
|
|
46
|
+
console.log("=== Loom WASM C-ABI Diagnostic Report ===");
|
|
47
|
+
|
|
48
|
+
// Decide which loader to use
|
|
49
|
+
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
50
|
+
const { loadLoomWASM } = await import("../src/loader.js");
|
|
51
|
+
await loadLoomWASM();
|
|
52
|
+
} else {
|
|
53
|
+
// @ts-ignore
|
|
54
|
+
const { loadLoomWASMBrowser } = await import("../src/loader.browser.js");
|
|
55
|
+
await loadLoomWASMBrowser();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let totalPass = 0;
|
|
59
|
+
let totalFail = 0;
|
|
60
|
+
|
|
61
|
+
// 1. Global symbol check
|
|
62
|
+
console.log("\n[1] Checking global WASM exports...");
|
|
63
|
+
for (const sym of EXPECTED_SYMBOLS) {
|
|
64
|
+
// @ts-ignore
|
|
65
|
+
if (typeof globalThis[sym] === 'function') {
|
|
66
|
+
console.log(` [PASS] ${sym}`);
|
|
67
|
+
totalPass++;
|
|
68
|
+
} else {
|
|
69
|
+
console.error(` [FAIL] ${sym} (missing)`);
|
|
70
|
+
totalFail++;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 2. Network method check
|
|
75
|
+
console.log("\n[2] Checking network wrapper methods...");
|
|
76
|
+
let net: any = null;
|
|
77
|
+
try {
|
|
78
|
+
// @ts-ignore
|
|
79
|
+
net = globalThis.createLoomNetwork(DENSE_3L);
|
|
80
|
+
if (net) {
|
|
81
|
+
for (const m of EXPECTED_NET_METHODS) {
|
|
82
|
+
if (net[m] !== undefined) {
|
|
83
|
+
console.log(` [PASS] ${m}`);
|
|
84
|
+
totalPass++;
|
|
85
|
+
} else {
|
|
86
|
+
console.error(` [FAIL] ${m} (missing)`);
|
|
87
|
+
totalFail++;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} catch (e) {
|
|
92
|
+
console.error(" [FAIL] createLoomNetwork failed:", e);
|
|
93
|
+
totalFail++;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 3. Population method check
|
|
97
|
+
if (net) {
|
|
98
|
+
console.log("\n[3] Checking NEAT population wrapper methods...");
|
|
99
|
+
try {
|
|
100
|
+
// @ts-ignore
|
|
101
|
+
const cfg = globalThis.defaultNEATConfig(16);
|
|
102
|
+
// @ts-ignore
|
|
103
|
+
const pop = globalThis.createLoomNEATPopulation(net._id, 4, cfg);
|
|
104
|
+
if (pop) {
|
|
105
|
+
for (const m of EXPECTED_POP_METHODS) {
|
|
106
|
+
if ((pop as any)[m] !== undefined) {
|
|
107
|
+
console.log(` [PASS] ${m}`);
|
|
108
|
+
totalPass++;
|
|
109
|
+
} else {
|
|
110
|
+
console.error(` [FAIL] ${m} (missing)`);
|
|
111
|
+
totalFail++;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
pop.free();
|
|
115
|
+
}
|
|
116
|
+
} catch (e) {
|
|
117
|
+
console.error(" [FAIL] Population creation failed:", e);
|
|
118
|
+
totalFail++;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 4. Functional smoke tests
|
|
123
|
+
console.log("\n[4] Running functional smoke tests...");
|
|
124
|
+
|
|
125
|
+
const smokeTest = (name: string, fn: () => any) => {
|
|
126
|
+
try {
|
|
127
|
+
const result = fn();
|
|
128
|
+
console.log(` [PASS] ${name}${result ? " → " + result : ""}`);
|
|
129
|
+
totalPass++;
|
|
130
|
+
} catch (e: any) {
|
|
131
|
+
console.error(` [FAIL] ${name} → ${e.message}`);
|
|
132
|
+
totalFail++;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
smokeTest("sequentialForward", () => {
|
|
137
|
+
// @ts-ignore
|
|
138
|
+
const n = globalThis.createLoomNetwork(DENSE_3L);
|
|
139
|
+
const input = new Float32Array(16).fill(0.5);
|
|
140
|
+
const out = n.sequentialForward(input);
|
|
141
|
+
n.free();
|
|
142
|
+
if (!out || out.length === 0) throw new Error("empty output");
|
|
143
|
+
return "out[0]=" + out[0].toFixed(4);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
smokeTest("extractDNA", () => {
|
|
147
|
+
// @ts-ignore
|
|
148
|
+
const n = globalThis.createLoomNetwork(DENSE_3L);
|
|
149
|
+
const dna = n.extractDNA();
|
|
150
|
+
n.free();
|
|
151
|
+
const parsed = JSON.parse(dna);
|
|
152
|
+
return "sigs=" + parsed.length;
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
smokeTest("compareLoomDNA", () => {
|
|
156
|
+
// @ts-ignore
|
|
157
|
+
const n1 = globalThis.createLoomNetwork(DENSE_3L);
|
|
158
|
+
// @ts-ignore
|
|
159
|
+
const n2 = globalThis.createLoomNetwork(DENSE_3L);
|
|
160
|
+
const dna1 = n1.extractDNA();
|
|
161
|
+
const dna2 = n2.extractDNA();
|
|
162
|
+
// @ts-ignore
|
|
163
|
+
const result = JSON.parse(globalThis.compareLoomDNA(dna1, dna2));
|
|
164
|
+
n1.free(); n2.free();
|
|
165
|
+
return "overlap=" + (result.overall_overlap || result.OverallOverlap || "?");
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
smokeTest("createLoomNetwork (SwiGLU)", () => {
|
|
169
|
+
// @ts-ignore
|
|
170
|
+
const n = globalThis.createLoomNetwork(SWIGLU_NET);
|
|
171
|
+
const c = n.getLayerCount();
|
|
172
|
+
n.free();
|
|
173
|
+
return "layers=" + c;
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
console.log("\n[5] Final Summary");
|
|
177
|
+
console.log("================================");
|
|
178
|
+
if (totalFail === 0) {
|
|
179
|
+
console.log(`SUCCESS: All ${totalPass} checks passed.`);
|
|
180
|
+
} else {
|
|
181
|
+
console.warn(`PARTIAL: ${totalPass} passed, ${totalFail} FAILED.`);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
console.log("================================");
|
|
185
|
+
|
|
186
|
+
if (net) net.free();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Auto-run if executed directly via Node.js/tsx
|
|
190
|
+
if (typeof process !== "undefined" && import.meta.url.includes(process.argv[1].replace(/\\/g, '/'))) {
|
|
191
|
+
runVerify();
|
|
192
|
+
}
|