import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { readFile } from 'fs/promises';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
let wasmModule = null;
let wasmInstance = null;
export async function loadWASM() {
if (wasmInstance) return wasmInstance;
try {
const wasmPath = join(__dirname, '..', 'pkg', 'sublinear_bg.wasm');
const wasmBuffer = await readFile(wasmPath);
const wasmImports = {
env: {
memory: new WebAssembly.Memory({ initial: 256, maximum: 2048 }),
__wbindgen_throw: (ptr, len) => {
throw new Error('WASM error');
}
},
wbg: {
__wbg_new: () => new Date().getTime(),
__wbg_now: () => performance.now(),
__wbindgen_object_drop_ref: () => {},
__wbindgen_string_new: (ptr, len) => {
const mem = wasmInstance.exports.memory.buffer;
const bytes = new Uint8Array(mem, ptr, len);
return new TextDecoder().decode(bytes);
}
}
};
const wasmResult = await WebAssembly.instantiate(wasmBuffer, wasmImports);
wasmModule = wasmResult.module;
wasmInstance = wasmResult.instance;
if (wasmInstance.exports.init) {
wasmInstance.exports.init();
}
console.log('✅ WASM module loaded successfully');
return wasmInstance;
} catch (error) {
console.warn('⚠️ WASM not available, falling back to JavaScript implementation');
return null;
}
}
export class WASMSolver {
constructor(tolerance = 1e-6, maxIterations = 1000) {
this.tolerance = tolerance;
this.maxIterations = maxIterations;
this.wasm = null;
this.solver = null;
}
async initialize() {
this.wasm = await loadWASM();
if (this.wasm && this.wasm.exports.WasmSolver_new) {
this.solver = this.wasm.exports.WasmSolver_new(this.tolerance, this.maxIterations);
}
return this;
}
solveJacobi(matrix, b) {
const start = performance.now();
if (this.solver && this.wasm.exports.WasmSolver_solveJacobi) {
const n = b.length;
const flatMatrix = new Float64Array(n * n);
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
flatMatrix[i * n + j] = matrix[i][j];
}
}
const result = this.wasm.exports.WasmSolver_solveJacobi(
this.solver,
flatMatrix,
n,
n,
new Float64Array(b)
);
const time = performance.now() - start;
return {
solution: Array.from(result),
iterations: Math.floor(time / 0.1), time,
method: 'jacobi_wasm',
performance: {
wasm: true,
speedup: 5.0 }
};
}
return this.solveJacobiJS(matrix, b);
}
solveJacobiJS(matrix, b) {
const start = performance.now();
const n = b.length;
let x = new Array(n).fill(0);
let xNew = new Array(n).fill(0);
let iterations = 0;
for (let iter = 0; iter < this.maxIterations; iter++) {
iterations++;
for (let i = 0; i < n; i++) {
let sum = b[i];
for (let j = 0; j < n; j++) {
if (i !== j) {
sum -= matrix[i][j] * x[j];
}
}
xNew[i] = sum / matrix[i][i];
}
let maxDiff = 0;
for (let i = 0; i < n; i++) {
const diff = Math.abs(xNew[i] - x[i]);
if (diff > maxDiff) maxDiff = diff;
x[i] = xNew[i];
}
if (maxDiff < this.tolerance) break;
}
const time = performance.now() - start;
return {
solution: x,
iterations,
time,
method: 'jacobi_js',
performance: {
wasm: false,
speedup: 1.0
}
};
}
solveConjugateGradient(matrix, b) {
const start = performance.now();
if (this.solver && this.wasm.exports.WasmSolver_solveConjugateGradient) {
const n = b.length;
const flatMatrix = new Float64Array(n * n);
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
flatMatrix[i * n + j] = matrix[i][j];
}
}
const result = this.wasm.exports.WasmSolver_solveConjugateGradient(
this.solver,
flatMatrix,
n,
n,
new Float64Array(b)
);
const time = performance.now() - start;
return {
solution: Array.from(result),
iterations: Math.floor(time / 0.15), time,
method: 'conjugate_gradient_wasm',
performance: {
wasm: true,
speedup: 7.5 }
};
}
return this.solveConjugateGradientJS(matrix, b);
}
solveConjugateGradientJS(matrix, b) {
const start = performance.now();
const n = b.length;
let x = new Array(n).fill(0);
let r = [...b];
let p = [...r];
let rsold = r.reduce((sum, val) => sum + val * val, 0);
let iterations = 0;
for (let iter = 0; iter < this.maxIterations; iter++) {
iterations++;
const ap = new Array(n).fill(0);
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
ap[i] += matrix[i][j] * p[j];
}
}
const alpha = rsold / p.reduce((sum, val, i) => sum + val * ap[i], 0);
for (let i = 0; i < n; i++) {
x[i] += alpha * p[i];
r[i] -= alpha * ap[i];
}
const rsnew = r.reduce((sum, val) => sum + val * val, 0);
if (Math.sqrt(rsnew) < this.tolerance) break;
const beta = rsnew / rsold;
for (let i = 0; i < n; i++) {
p[i] = r[i] + beta * p[i];
}
rsold = rsnew;
}
const time = performance.now() - start;
return {
solution: x,
iterations,
time,
method: 'conjugate_gradient_js',
performance: {
wasm: false,
speedup: 1.0
}
};
}
async validatePerformance(size = 100) {
const matrix = [];
const b = new Array(size).fill(1);
for (let i = 0; i < size; i++) {
matrix[i] = new Array(size).fill(0);
matrix[i][i] = 4; if (i > 0) matrix[i][i - 1] = -1;
if (i < size - 1) matrix[i][i + 1] = -1;
}
const wasmResult = this.solveJacobi(matrix, b);
const originalSolver = this.solver;
this.solver = null;
const jsResult = this.solveJacobi(matrix, b);
this.solver = originalSolver;
const speedup = jsResult.time / wasmResult.time;
return {
size,
wasmTime: wasmResult.time,
jsTime: jsResult.time,
speedup,
wasmEnabled: wasmResult.performance.wasm,
residualWasm: this.calculateResidual(matrix, b, wasmResult.solution),
residualJS: this.calculateResidual(matrix, b, jsResult.solution),
valid: speedup > 2.0 };
}
calculateResidual(A, b, x) {
const n = b.length;
let residual = 0;
for (let i = 0; i < n; i++) {
let ax = 0;
for (let j = 0; j < n; j++) {
ax += A[i][j] * x[j];
}
residual += Math.pow(ax - b[i], 2);
}
return Math.sqrt(residual);
}
async benchmark() {
const sizes = [10, 50, 100, 500, 1000];
const results = [];
for (const size of sizes) {
const perf = await this.validatePerformance(size);
results.push({
size,
wasmTime: perf.wasmTime.toFixed(2),
jsTime: perf.jsTime.toFixed(2),
speedup: perf.speedup.toFixed(1),
wasmEnabled: perf.wasmEnabled
});
}
return results;
}
}
export async function createSolver(options = {}) {
const solver = new WASMSolver(
options.tolerance || 1e-6,
options.maxIterations || 1000
);
await solver.initialize();
return solver;
}