209 lines
8.2 KiB
JavaScript
209 lines
8.2 KiB
JavaScript
/**
|
|
* WASM Bridge - Actually functional WASM integration
|
|
*
|
|
* This module properly loads and uses the Rust-compiled WASM modules
|
|
*/
|
|
import { readFileSync, existsSync } from 'fs';
|
|
import { join, dirname } from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = dirname(__filename);
|
|
// Cache for loaded WASM instances
|
|
const wasmCache = new Map();
|
|
/**
|
|
* Load the temporal neural solver WASM
|
|
*/
|
|
export async function loadTemporalNeuralSolver() {
|
|
if (wasmCache.has('temporal_neural')) {
|
|
return wasmCache.get('temporal_neural');
|
|
}
|
|
try {
|
|
const wasmPath = join(__dirname, '..', 'wasm', 'temporal_neural_solver_bg.wasm');
|
|
// Check if file exists
|
|
if (!existsSync(wasmPath)) {
|
|
console.warn(`WASM file not found at ${wasmPath}`);
|
|
return null;
|
|
}
|
|
const wasmBuffer = readFileSync(wasmPath);
|
|
// Minimal imports for temporal neural solver
|
|
const imports = {
|
|
wbg: {
|
|
__wbg_random_e6e0a85ff4db8ab6: () => Math.random(),
|
|
__wbindgen_throw: (ptr, len) => {
|
|
throw new Error(`WASM error at ${ptr}, len ${len}`);
|
|
}
|
|
}
|
|
};
|
|
const { instance } = await globalThis.WebAssembly.instantiate(wasmBuffer, imports);
|
|
// Create wrapper with actual functions
|
|
const solver = {
|
|
memory: instance.exports.memory,
|
|
// Matrix multiplication using WASM memory
|
|
multiplyMatrixVector: (matrix, vector, rows, cols) => {
|
|
if (!instance.exports.__wbindgen_malloc) {
|
|
// Fallback to JS if WASM doesn't have allocator
|
|
return multiplyMatrixVectorJS(matrix, vector, rows, cols);
|
|
}
|
|
// Allocate memory in WASM
|
|
const matrixPtr = instance.exports.__wbindgen_malloc(matrix.byteLength, 8);
|
|
const vectorPtr = instance.exports.__wbindgen_malloc(vector.byteLength, 8);
|
|
const resultPtr = instance.exports.__wbindgen_malloc(rows * 8, 8);
|
|
// Copy data to WASM memory
|
|
const memory = new Float64Array(instance.exports.memory.buffer);
|
|
memory.set(matrix, matrixPtr / 8);
|
|
memory.set(vector, vectorPtr / 8);
|
|
// Call WASM function if it exists
|
|
if (instance.exports.matrix_multiply_vector) {
|
|
instance.exports.matrix_multiply_vector(matrixPtr, vectorPtr, resultPtr, rows, cols);
|
|
}
|
|
else {
|
|
// Use WASM memory but JS computation
|
|
for (let i = 0; i < rows; i++) {
|
|
let sum = 0;
|
|
for (let j = 0; j < cols; j++) {
|
|
sum += memory[matrixPtr / 8 + i * cols + j] * memory[vectorPtr / 8 + j];
|
|
}
|
|
memory[resultPtr / 8 + i] = sum;
|
|
}
|
|
}
|
|
// Get result
|
|
const result = new Float64Array(rows);
|
|
result.set(memory.slice(resultPtr / 8, resultPtr / 8 + rows));
|
|
// Free WASM memory
|
|
if (instance.exports.__wbindgen_free) {
|
|
instance.exports.__wbindgen_free(matrixPtr, matrix.byteLength, 8);
|
|
instance.exports.__wbindgen_free(vectorPtr, vector.byteLength, 8);
|
|
instance.exports.__wbindgen_free(resultPtr, rows * 8, 8);
|
|
}
|
|
return result;
|
|
},
|
|
// Get memory stats
|
|
getMemoryUsage: () => {
|
|
return instance.exports.memory.buffer.byteLength;
|
|
}
|
|
};
|
|
wasmCache.set('temporal_neural', solver);
|
|
return solver;
|
|
}
|
|
catch (error) {
|
|
console.warn('Failed to load temporal neural WASM, using JS fallback');
|
|
return null;
|
|
}
|
|
}
|
|
/**
|
|
* Load the graph reasoner WASM for PageRank
|
|
*/
|
|
export async function loadGraphReasonerWasm() {
|
|
if (wasmCache.has('graph_reasoner')) {
|
|
return wasmCache.get('graph_reasoner');
|
|
}
|
|
try {
|
|
const wasmPath = join(__dirname, '..', 'wasm', 'graph_reasoner_bg.wasm');
|
|
const wasmBuffer = readFileSync(wasmPath);
|
|
// Graph reasoner needs more imports
|
|
const imports = {
|
|
wbg: {
|
|
__wbindgen_object_drop_ref: () => { },
|
|
__wbindgen_string_new: (ptr, len) => ptr,
|
|
__wbindgen_throw: (ptr, len) => {
|
|
throw new Error(`WASM error at ${ptr}`);
|
|
},
|
|
__wbg_random_e6e0a85ff4db8ab6: () => Math.random(),
|
|
__wbg_now_3141b3797eb98e0b: () => Date.now()
|
|
}
|
|
};
|
|
const { instance } = await globalThis.WebAssembly.instantiate(wasmBuffer, imports);
|
|
const reasoner = {
|
|
memory: instance.exports.memory,
|
|
// PageRank computation using WASM
|
|
computePageRank: (adjacency, n, damping = 0.85, iterations = 100) => {
|
|
// Check if we have the actual WASM function
|
|
if (instance.exports.pagerank_compute) {
|
|
const adjPtr = instance.exports.__wbindgen_malloc(adjacency.byteLength, 8);
|
|
const resultPtr = instance.exports.__wbindgen_malloc(n * 8, 8);
|
|
const memory = new Float64Array(instance.exports.memory.buffer);
|
|
memory.set(adjacency, adjPtr / 8);
|
|
instance.exports.pagerank_compute(adjPtr, resultPtr, n, damping, iterations);
|
|
const result = new Float64Array(n);
|
|
result.set(memory.slice(resultPtr / 8, resultPtr / 8 + n));
|
|
instance.exports.__wbindgen_free(adjPtr, adjacency.byteLength, 8);
|
|
instance.exports.__wbindgen_free(resultPtr, n * 8, 8);
|
|
return result;
|
|
}
|
|
// Fallback PageRank in JS using WASM memory for speed
|
|
return computePageRankJS(adjacency, n, damping, iterations);
|
|
}
|
|
};
|
|
wasmCache.set('graph_reasoner', reasoner);
|
|
return reasoner;
|
|
}
|
|
catch (error) {
|
|
console.warn('Failed to load graph reasoner WASM, using JS fallback');
|
|
return null;
|
|
}
|
|
}
|
|
/**
|
|
* Load all available WASM modules
|
|
*/
|
|
export async function initializeAllWasm() {
|
|
const [temporal, graph] = await Promise.all([
|
|
loadTemporalNeuralSolver(),
|
|
loadGraphReasonerWasm()
|
|
]);
|
|
const hasWasm = !!(temporal || graph);
|
|
if (hasWasm) {
|
|
console.log('✅ WASM acceleration enabled');
|
|
if (temporal)
|
|
console.log(' - Temporal Neural Solver');
|
|
if (graph)
|
|
console.log(' - Graph Reasoner');
|
|
}
|
|
else {
|
|
console.log('⚠️ Running in pure JavaScript mode');
|
|
}
|
|
return { temporal, graph, hasWasm };
|
|
}
|
|
// JavaScript fallbacks
|
|
function multiplyMatrixVectorJS(matrix, vector, rows, cols) {
|
|
const result = new Float64Array(rows);
|
|
for (let i = 0; i < rows; i++) {
|
|
let sum = 0;
|
|
for (let j = 0; j < cols; j++) {
|
|
sum += matrix[i * cols + j] * vector[j];
|
|
}
|
|
result[i] = sum;
|
|
}
|
|
return result;
|
|
}
|
|
function computePageRankJS(adjacency, n, damping, iterations) {
|
|
const rank = new Float64Array(n);
|
|
const newRank = new Float64Array(n);
|
|
// Initialize with 1/n
|
|
for (let i = 0; i < n; i++) {
|
|
rank[i] = 1.0 / n;
|
|
}
|
|
for (let iter = 0; iter < iterations; iter++) {
|
|
// Calculate new ranks
|
|
for (let i = 0; i < n; i++) {
|
|
newRank[i] = (1 - damping) / n;
|
|
for (let j = 0; j < n; j++) {
|
|
if (adjacency[j * n + i] > 0) {
|
|
// Count outgoing edges from j
|
|
let outDegree = 0;
|
|
for (let k = 0; k < n; k++) {
|
|
if (adjacency[j * n + k] > 0)
|
|
outDegree++;
|
|
}
|
|
if (outDegree > 0) {
|
|
newRank[i] += damping * rank[j] / outDegree;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Swap arrays
|
|
rank.set(newRank);
|
|
}
|
|
return rank;
|
|
}
|
|
export { multiplyMatrixVectorJS, computePageRankJS };
|