147 lines
5.5 KiB
JavaScript
147 lines
5.5 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
async function test() {
|
|
console.log('=== Micro HNSW WASM v2.2 Test Suite ===\n');
|
|
|
|
// Load WASM
|
|
const wasmPath = path.join(__dirname, 'micro_hnsw.wasm');
|
|
const wasmBuffer = fs.readFileSync(wasmPath);
|
|
const wasmModule = await WebAssembly.instantiate(wasmBuffer);
|
|
const wasm = wasmModule.instance.exports;
|
|
|
|
console.log('✓ WASM loaded successfully');
|
|
console.log(' Binary size: ' + wasmBuffer.length + ' bytes (' + (wasmBuffer.length/1024).toFixed(2) + ' KB)\n');
|
|
|
|
// List all exports
|
|
const exports = Object.keys(wasm).filter(k => typeof wasm[k] === 'function');
|
|
console.log('Exported functions (' + exports.length + '):');
|
|
exports.forEach(fn => console.log(' - ' + fn));
|
|
console.log('');
|
|
|
|
// Test 1: Initialize HNSW
|
|
console.log('Test 1: Initialize HNSW (dims=4, metric=0/euclidean, capacity=32)');
|
|
wasm.init(4, 0, 32);
|
|
console.log(' dims: ' + wasm.get_dims());
|
|
console.log(' metric: ' + wasm.get_metric());
|
|
console.log(' capacity: ' + wasm.get_capacity());
|
|
console.log(' count: ' + wasm.count());
|
|
console.log('✓ Init passed\n');
|
|
|
|
// Test 2: Insert vectors
|
|
console.log('Test 2: Insert vectors');
|
|
const memory = new Float32Array(wasm.memory.buffer);
|
|
const insertPtr = wasm.get_insert_ptr() / 4;
|
|
|
|
// Insert 3 vectors
|
|
const vectors = [
|
|
[1.0, 0.0, 0.0, 0.0],
|
|
[0.0, 1.0, 0.0, 0.0],
|
|
[0.5, 0.5, 0.0, 0.0],
|
|
];
|
|
|
|
for (let i = 0; i < vectors.length; i++) {
|
|
for (let j = 0; j < 4; j++) {
|
|
memory[insertPtr + j] = vectors[i][j];
|
|
}
|
|
const idx = wasm.insert();
|
|
console.log(' Inserted vector ' + i + ': index=' + idx);
|
|
}
|
|
console.log(' Total count: ' + wasm.count());
|
|
console.log('✓ Insert passed\n');
|
|
|
|
// Test 3: Search
|
|
console.log('Test 3: Search for nearest neighbors');
|
|
const queryPtr = wasm.get_query_ptr() / 4;
|
|
memory[queryPtr] = 0.9;
|
|
memory[queryPtr + 1] = 0.1;
|
|
memory[queryPtr + 2] = 0.0;
|
|
memory[queryPtr + 3] = 0.0;
|
|
|
|
const found = wasm.search(3);
|
|
console.log(' Query: [0.9, 0.1, 0.0, 0.0]');
|
|
console.log(' Found: ' + found + ' neighbors');
|
|
|
|
const resultPtr = wasm.get_result_ptr();
|
|
console.log(' Result ptr: ' + resultPtr);
|
|
console.log('✓ Search passed\n');
|
|
|
|
// Test 4: Node types
|
|
console.log('Test 4: Node types');
|
|
wasm.set_node_type(0, 5);
|
|
wasm.set_node_type(1, 10);
|
|
console.log(' Node 0 type: ' + wasm.get_node_type(0));
|
|
console.log(' Node 1 type: ' + wasm.get_node_type(1));
|
|
console.log(' Type match (0,0): ' + wasm.type_matches(0, 0));
|
|
console.log(' Type match (0,1): ' + wasm.type_matches(0, 1));
|
|
console.log('✓ Node types passed\n');
|
|
|
|
// Test 5: Edge weights (GNN feature)
|
|
console.log('Test 5: Edge weights (GNN)');
|
|
wasm.set_edge_weight(0, 200);
|
|
wasm.set_edge_weight(1, 100);
|
|
console.log(' Edge 0 weight: ' + wasm.get_edge_weight(0));
|
|
console.log(' Edge 1 weight: ' + wasm.get_edge_weight(1));
|
|
console.log('✓ Edge weights passed\n');
|
|
|
|
// Test 6: SNN features (if available)
|
|
if (wasm.snn_reset) {
|
|
console.log('Test 6: Spiking Neural Network (SNN)');
|
|
wasm.snn_reset();
|
|
console.log(' Initial time: ' + wasm.snn_get_time());
|
|
|
|
// Inject current to node 0
|
|
wasm.snn_inject(0, 0.5); // Inject below threshold
|
|
console.log(' Injected current 0.5 to node 0');
|
|
console.log(' Node 0 membrane: ' + wasm.snn_get_membrane(0).toFixed(3));
|
|
|
|
// Run simulation step with dt=1.0 ms
|
|
const dt = 1.0;
|
|
let spikes1 = wasm.snn_step(dt);
|
|
console.log(' After step 1 (dt=' + dt + 'ms): time=' + wasm.snn_get_time().toFixed(1) + ', membrane=' + wasm.snn_get_membrane(0).toFixed(3) + ', spikeCount=' + spikes1);
|
|
|
|
// Inject more to reach threshold
|
|
wasm.snn_inject(0, 0.8);
|
|
let spikes2 = wasm.snn_step(dt);
|
|
console.log(' After step 2 (+0.8 current): membrane=' + wasm.snn_get_membrane(0).toFixed(3) + ', spiked=' + wasm.snn_spiked(0) + ', spikeCount=' + spikes2);
|
|
|
|
// Check spikes bitset
|
|
const spikes = wasm.snn_get_spikes();
|
|
console.log(' Spike bitmask: 0b' + spikes.toString(2));
|
|
|
|
// Test combined tick function
|
|
wasm.snn_reset();
|
|
wasm.snn_inject(0, 1.5); // Above threshold
|
|
const tickSpikes = wasm.snn_tick(1.0, 0.5, 1); // dt=1.0, gain=0.5, learn=1
|
|
console.log(' snn_tick result: ' + tickSpikes + ' spikes');
|
|
|
|
console.log('✓ SNN passed\n');
|
|
} else {
|
|
console.log('Test 6: SNN not available (functions not exported)\n');
|
|
}
|
|
|
|
// Test 7: HNSW to SNN conversion
|
|
if (wasm.hnsw_to_snn) {
|
|
console.log('Test 7: HNSW to SNN conversion');
|
|
wasm.snn_reset();
|
|
// hnsw_to_snn(k, gain) - search for k neighbors and inject currents
|
|
const injected = wasm.hnsw_to_snn(3, 1.0);
|
|
console.log(' Converted HNSW search to SNN currents for ' + injected + ' nodes');
|
|
console.log(' Node 0 membrane after injection: ' + wasm.snn_get_membrane(0).toFixed(3));
|
|
console.log('✓ HNSW→SNN passed\n');
|
|
}
|
|
|
|
// Test 8: Aggregate neighbors (GNN)
|
|
if (wasm.aggregate_neighbors) {
|
|
console.log('Test 8: GNN aggregate neighbors');
|
|
wasm.aggregate_neighbors(0);
|
|
console.log(' Aggregated features for node 0');
|
|
console.log('✓ Aggregate passed\n');
|
|
}
|
|
|
|
console.log('=== All Tests Passed ===');
|
|
console.log('Final stats: ' + wasm.count() + ' vectors, ' + wasmBuffer.length + ' bytes');
|
|
}
|
|
|
|
test().catch(console.error);
|