class FastCSRMatrix {
constructor(values, colIndices, rowPtr, rows, cols) {
this.values = new Float64Array(values);
this.colIndices = new Uint32Array(colIndices);
this.rowPtr = new Uint32Array(rowPtr);
this.rows = rows;
this.cols = cols;
}
static fromTriplets(triplets, rows, cols) {
triplets.sort((a, b) => {
if (a[0] !== b[0]) return a[0] - b[0];
return a[1] - b[1];
});
const values = [];
const colIndices = [];
const rowPtr = new Array(rows + 1).fill(0);
let currentRow = 0;
for (const [row, col, val] of triplets) {
while (currentRow <= row) {
rowPtr[currentRow] = values.length;
currentRow++;
}
values.push(val);
colIndices.push(col);
}
while (currentRow <= rows) {
rowPtr[currentRow] = values.length;
currentRow++;
}
return new FastCSRMatrix(values, colIndices, rowPtr, rows, cols);
}
multiplyVector(x, y) {
y.fill(0.0);
for (let row = 0; row < this.rows; row++) {
const start = this.rowPtr[row];
const end = this.rowPtr[row + 1];
const nnz = end - start;
if (nnz === 0) continue;
if (nnz <= 4) {
let sum = 0.0;
for (let idx = start; idx < end; idx++) {
sum += this.values[idx] * x[this.colIndices[idx]];
}
y[row] = sum;
} else {
const chunks = Math.floor(nnz / 4);
const remainder = nnz % 4;
let sum = 0.0;
let idx = start;
for (let chunk = 0; chunk < chunks; chunk++) {
sum += this.values[idx] * x[this.colIndices[idx]] +
this.values[idx + 1] * x[this.colIndices[idx + 1]] +
this.values[idx + 2] * x[this.colIndices[idx + 2]] +
this.values[idx + 3] * x[this.colIndices[idx + 3]];
idx += 4;
}
for (let i = 0; i < remainder; i++) {
sum += this.values[idx] * x[this.colIndices[idx]];
idx++;
}
y[row] = sum;
}
}
}
get nnz() {
return this.values.length;
}
}
class FastConjugateGradient {
constructor(maxIterations = 1000, tolerance = 1e-10) {
this.maxIterations = maxIterations;
this.tolerance = tolerance;
this.toleranceSq = tolerance * tolerance;
}
solve(matrix, b) {
const n = matrix.rows;
if (matrix.rows !== matrix.cols) {
throw new Error('Matrix must be square');
}
if (b.length !== n) {
throw new Error('Vector size mismatch');
}
const x = new Float64Array(n);
const r = new Float64Array(b); const p = new Float64Array(b); const ap = new Float64Array(n);
let rsold = this.dotProductFast(r, r);
for (let iteration = 0; iteration < this.maxIterations; iteration++) {
if (rsold <= this.toleranceSq) {
break;
}
matrix.multiplyVector(p, ap);
const pap = this.dotProductFast(p, ap);
if (Math.abs(pap) < 1e-16) {
break;
}
const alpha = rsold / pap;
this.axpyFast(alpha, p, x);
this.axpyFast(-alpha, ap, r);
const rsnew = this.dotProductFast(r, r);
const beta = rsnew / rsold;
for (let i = 0; i < n; i++) {
p[i] = r[i] + beta * p[i];
}
rsold = rsnew;
}
return Array.from(x);
}
dotProductFast(x, y) {
const n = x.length;
const chunks = Math.floor(n / 4);
const remainder = n % 4;
let sum = 0.0;
let i = 0;
for (let chunk = 0; chunk < chunks; chunk++) {
sum += x[i] * y[i] +
x[i + 1] * y[i + 1] +
x[i + 2] * y[i + 2] +
x[i + 3] * y[i + 3];
i += 4;
}
for (let j = 0; j < remainder; j++) {
sum += x[i] * y[i];
i++;
}
return sum;
}
axpyFast(alpha, x, y) {
const n = x.length;
const chunks = Math.floor(n / 4);
const remainder = n % 4;
let i = 0;
for (let chunk = 0; chunk < chunks; chunk++) {
y[i] += alpha * x[i];
y[i + 1] += alpha * x[i + 1];
y[i + 2] += alpha * x[i + 2];
y[i + 3] += alpha * x[i + 3];
i += 4;
}
for (let j = 0; j < remainder; j++) {
y[i] += alpha * x[i];
i++;
}
}
}
class VectorPool {
constructor(size, capacity = 8) {
this.size = size;
this.buffers = [];
for (let i = 0; i < capacity; i++) {
this.buffers.push(new Float64Array(size));
}
}
getBuffer() {
return this.buffers.pop() || new Float64Array(this.size);
}
returnBuffer(buffer) {
if (buffer.length === this.size && this.buffers.length < 8) {
buffer.fill(0.0);
this.buffers.push(buffer);
}
}
}
class FastSolver {
constructor(config = {}) {
this.maxIterations = config.maxIterations || 1000;
this.tolerance = config.tolerance || 1e-10;
this.useWasm = config.useWasm && this.isWasmAvailable();
this.vectorPool = null;
}
isWasmAvailable() {
try {
return typeof WebAssembly !== 'undefined' &&
global.wasmSolver !== undefined;
} catch (e) {
return false;
}
}
createMatrix(triplets, rows, cols) {
if (this.useWasm) {
return this.createWasmMatrix(triplets, rows, cols);
} else {
return FastCSRMatrix.fromTriplets(triplets, rows, cols);
}
}
createWasmMatrix(triplets, rows, cols) {
console.log('WASM matrix creation not yet implemented, falling back to JS');
return FastCSRMatrix.fromTriplets(triplets, rows, cols);
}
solve(matrix, b, options = {}) {
const startTime = process.hrtime.bigint();
if (!this.vectorPool) {
this.vectorPool = new VectorPool(matrix.rows);
}
let result;
if (this.useWasm) {
result = this.solveWasm(matrix, b, options);
} else {
const solver = new FastConjugateGradient(this.maxIterations, this.tolerance);
result = solver.solve(matrix, b);
}
const endTime = process.hrtime.bigint();
const executionTime = Number(endTime - startTime) / 1e6;
return {
solution: result,
executionTime,
iterations: this.lastIterations || 0,
method: this.useWasm ? 'wasm' : 'javascript'
};
}
solveWasm(matrix, b, options) {
console.log('WASM solver not yet implemented, falling back to JS');
const solver = new FastConjugateGradient(this.maxIterations, this.tolerance);
return solver.solve(matrix, b);
}
generateTestMatrix(size, sparsity = 0.01) {
const triplets = [];
for (let i = 0; i < size; i++) {
const diagVal = 5.0 + i * 0.01;
triplets.push([i, i, diagVal]);
const nnzPerRow = Math.max(1, Math.floor(size * sparsity));
for (let k = 0; k < Math.min(nnzPerRow, 5); k++) {
const j = Math.floor(Math.random() * size);
if (i !== j) {
const val = Math.random() * 0.5; triplets.push([i, j, val]);
}
}
}
const matrix = this.createMatrix(triplets, size, size);
const b = new Array(size).fill(1.0);
return { matrix, b };
}
benchmark(sizes = [100, 1000]) {
console.log('🚀 Fast Solver Benchmark - Targeting Python performance');
console.log('=' * 60);
const results = [];
for (const size of sizes) {
console.log(`\n📊 Testing ${size}x${size} matrix...`);
const { matrix, b } = this.generateTestMatrix(size, 0.001);
this.solve(matrix, b);
const startTime = process.hrtime.bigint();
const result = this.solve(matrix, b);
const endTime = process.hrtime.bigint();
const timeMs = Number(endTime - startTime) / 1e6;
const pythonBaseline = size === 100 ? 2 : (size === 1000 ? 40 : 200);
const speedup = pythonBaseline / timeMs;
const status = speedup > 1 ? '✅ FASTER' : '❌ SLOWER';
console.log(` Time: ${timeMs.toFixed(2)}ms`);
console.log(` Python baseline: ${pythonBaseline}ms`);
console.log(` Speedup: ${speedup.toFixed(2)}x ${status}`);
console.log(` NNZ: ${matrix.nnz}`);
console.log(` Method: ${result.method}`);
results.push({
size,
timeMs,
pythonBaseline,
speedup,
nnz: matrix.nnz,
method: result.method
});
}
return results;
}
}
export {
FastCSRMatrix,
FastConjugateGradient,
VectorPool,
FastSolver
};