397 lines
9.7 KiB
JavaScript
397 lines
9.7 KiB
JavaScript
import init, {
|
|
WasmSublinearSolver,
|
|
MatrixView,
|
|
get_features,
|
|
enable_simd,
|
|
get_wasm_memory_usage,
|
|
benchmark_matrix_multiply
|
|
} from '../pkg/sublinear_time_solver.js';
|
|
|
|
// Initialize WebAssembly module
|
|
let wasmInitialized = false;
|
|
let wasmModule = null;
|
|
|
|
async function ensureWasmInitialized() {
|
|
if (!wasmInitialized) {
|
|
wasmModule = await init();
|
|
wasmInitialized = true;
|
|
}
|
|
return wasmModule;
|
|
}
|
|
|
|
/**
|
|
* Configuration interface for the solver
|
|
*/
|
|
export class SolverConfig {
|
|
constructor(options = {}) {
|
|
this.maxIterations = options.maxIterations || 1000;
|
|
this.tolerance = options.tolerance || 1e-10;
|
|
this.simdEnabled = options.simdEnabled !== false;
|
|
this.streamChunkSize = options.streamChunkSize || 100;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Matrix class for efficient data handling
|
|
*/
|
|
export class Matrix {
|
|
constructor(data, rows, cols) {
|
|
if (data instanceof Float64Array) {
|
|
this.data = data;
|
|
} else if (Array.isArray(data)) {
|
|
this.data = new Float64Array(data);
|
|
} else {
|
|
throw new Error('Matrix data must be Float64Array or Array');
|
|
}
|
|
|
|
this.rows = rows;
|
|
this.cols = cols;
|
|
|
|
if (this.data.length !== rows * cols) {
|
|
throw new Error('Data length must match matrix dimensions');
|
|
}
|
|
}
|
|
|
|
static zeros(rows, cols) {
|
|
return new Matrix(new Float64Array(rows * cols), rows, cols);
|
|
}
|
|
|
|
static identity(size) {
|
|
const data = new Float64Array(size * size);
|
|
for (let i = 0; i < size; i++) {
|
|
data[i * size + i] = 1.0;
|
|
}
|
|
return new Matrix(data, size, size);
|
|
}
|
|
|
|
static random(rows, cols) {
|
|
const data = new Float64Array(rows * cols);
|
|
for (let i = 0; i < data.length; i++) {
|
|
data[i] = Math.random();
|
|
}
|
|
return new Matrix(data, rows, cols);
|
|
}
|
|
|
|
get(row, col) {
|
|
return this.data[row * this.cols + col];
|
|
}
|
|
|
|
set(row, col, value) {
|
|
this.data[row * this.cols + col] = value;
|
|
}
|
|
|
|
toWasmView() {
|
|
return new MatrixView(this.rows, this.cols);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Solution step information for streaming interface
|
|
*/
|
|
export class SolutionStep {
|
|
constructor(iteration, residual, timestamp, convergence) {
|
|
this.iteration = iteration;
|
|
this.residual = residual;
|
|
this.timestamp = timestamp;
|
|
this.convergence = convergence;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Streaming solver using AsyncIterator pattern
|
|
*/
|
|
export class SolutionStream {
|
|
constructor(solver, matrix, vector) {
|
|
this.solver = solver;
|
|
this.matrix = matrix;
|
|
this.vector = vector;
|
|
this.buffer = [];
|
|
this.isComplete = false;
|
|
this.error = null;
|
|
}
|
|
|
|
async *[Symbol.asyncIterator]() {
|
|
try {
|
|
const solution = await new Promise((resolve, reject) => {
|
|
this.solver.wasmSolver.solve_stream(
|
|
this.matrix.data,
|
|
this.matrix.rows,
|
|
this.matrix.cols,
|
|
this.vector,
|
|
(stepData) => {
|
|
const step = new SolutionStep(
|
|
stepData.iteration,
|
|
stepData.residual,
|
|
stepData.timestamp,
|
|
stepData.convergence
|
|
);
|
|
this.buffer.push(step);
|
|
}
|
|
);
|
|
|
|
// Process buffered steps
|
|
this.processBuffer(resolve, reject);
|
|
});
|
|
|
|
// Yield all buffered steps
|
|
while (this.buffer.length > 0) {
|
|
yield this.buffer.shift();
|
|
}
|
|
|
|
} catch (error) {
|
|
throw new Error(`Streaming solve failed: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
async processBuffer(resolve, reject) {
|
|
// Simple processing - in production this would be more sophisticated
|
|
const checkBuffer = () => {
|
|
if (this.buffer.length > 0) {
|
|
const lastStep = this.buffer[this.buffer.length - 1];
|
|
if (lastStep.convergence) {
|
|
resolve();
|
|
return;
|
|
}
|
|
}
|
|
setTimeout(checkBuffer, 10);
|
|
};
|
|
checkBuffer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Memory manager for efficient WASM memory usage
|
|
*/
|
|
export class MemoryManager {
|
|
constructor() {
|
|
this.allocations = new Map();
|
|
}
|
|
|
|
allocateFloat64Array(length) {
|
|
const buffer = new Float64Array(length);
|
|
const id = Math.random().toString(36);
|
|
this.allocations.set(id, buffer);
|
|
return { id, buffer };
|
|
}
|
|
|
|
deallocate(id) {
|
|
this.allocations.delete(id);
|
|
}
|
|
|
|
getUsage() {
|
|
let totalBytes = 0;
|
|
for (const buffer of this.allocations.values()) {
|
|
totalBytes += buffer.byteLength;
|
|
}
|
|
return {
|
|
allocations: this.allocations.size,
|
|
totalBytes,
|
|
wasmMemory: get_wasm_memory_usage()
|
|
};
|
|
}
|
|
|
|
clear() {
|
|
this.allocations.clear();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Main SublinearSolver class with WASM backend
|
|
*/
|
|
export class SublinearSolver {
|
|
constructor(config = new SolverConfig()) {
|
|
this.config = config;
|
|
this.wasmSolver = null;
|
|
this.memoryManager = new MemoryManager();
|
|
this.initialized = false;
|
|
}
|
|
|
|
async initialize() {
|
|
if (this.initialized) return;
|
|
|
|
await ensureWasmInitialized();
|
|
|
|
try {
|
|
this.wasmSolver = new WasmSublinearSolver(this.config);
|
|
this.initialized = true;
|
|
} catch (error) {
|
|
throw new Error(`Failed to initialize WASM solver: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Solve linear system Ax = b synchronously
|
|
*/
|
|
async solve(matrix, vector) {
|
|
await this.initialize();
|
|
|
|
if (!(matrix instanceof Matrix)) {
|
|
throw new Error('Matrix must be instance of Matrix class');
|
|
}
|
|
|
|
if (!(vector instanceof Float64Array)) {
|
|
throw new Error('Vector must be Float64Array');
|
|
}
|
|
|
|
try {
|
|
const result = this.wasmSolver.solve(
|
|
matrix.data,
|
|
matrix.rows,
|
|
matrix.cols,
|
|
vector
|
|
);
|
|
|
|
return new Float64Array(result);
|
|
} catch (error) {
|
|
throw new Error(`Solve failed: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Solve with streaming progress updates
|
|
*/
|
|
async *solveStream(matrix, vector) {
|
|
await this.initialize();
|
|
|
|
const stream = new SolutionStream(this, matrix, vector);
|
|
for await (const step of stream) {
|
|
yield step;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Solve batch of problems efficiently
|
|
*/
|
|
async solveBatch(problems) {
|
|
await this.initialize();
|
|
|
|
const batchData = problems.map((problem, index) => ({
|
|
id: `batch_${index}`,
|
|
matrix_data: Array.from(problem.matrix.data),
|
|
matrix_rows: problem.matrix.rows,
|
|
matrix_cols: problem.matrix.cols,
|
|
vector_data: Array.from(problem.vector)
|
|
}));
|
|
|
|
try {
|
|
const results = this.wasmSolver.solve_batch(batchData);
|
|
return results.map(result => ({
|
|
id: result.id,
|
|
solution: new Float64Array(result.solution),
|
|
iterations: result.iterations,
|
|
error: result.error
|
|
}));
|
|
} catch (error) {
|
|
throw new Error(`Batch solve failed: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get current memory usage
|
|
*/
|
|
getMemoryUsage() {
|
|
if (!this.initialized) {
|
|
return { used: 0, capacity: 0, js: this.memoryManager.getUsage() };
|
|
}
|
|
|
|
const wasmUsage = this.wasmSolver.memory_usage;
|
|
const jsUsage = this.memoryManager.getUsage();
|
|
|
|
return {
|
|
used: wasmUsage.used,
|
|
capacity: wasmUsage.capacity,
|
|
js: jsUsage
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Get solver configuration
|
|
*/
|
|
getConfig() {
|
|
if (!this.initialized) return this.config;
|
|
return this.wasmSolver.get_config();
|
|
}
|
|
|
|
/**
|
|
* Clean up resources
|
|
*/
|
|
dispose() {
|
|
if (this.wasmSolver) {
|
|
this.wasmSolver.dispose();
|
|
this.wasmSolver = null;
|
|
}
|
|
this.memoryManager.clear();
|
|
this.initialized = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Factory function for easy initialization
|
|
*/
|
|
export async function createSolver(config) {
|
|
const solver = new SublinearSolver(config);
|
|
await solver.initialize();
|
|
return solver;
|
|
}
|
|
|
|
/**
|
|
* Utility functions
|
|
*/
|
|
export const Utils = {
|
|
async getFeatures() {
|
|
await ensureWasmInitialized();
|
|
return get_features();
|
|
},
|
|
|
|
async isSIMDEnabled() {
|
|
await ensureWasmInitialized();
|
|
return enable_simd();
|
|
},
|
|
|
|
async benchmarkMatrixMultiply(size) {
|
|
await ensureWasmInitialized();
|
|
return benchmark_matrix_multiply(size);
|
|
},
|
|
|
|
async getWasmMemoryUsage() {
|
|
await ensureWasmInitialized();
|
|
return get_wasm_memory_usage();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Error classes
|
|
*/
|
|
export class SolverError extends Error {
|
|
constructor(message, type = 'SOLVER_ERROR') {
|
|
super(message);
|
|
this.name = 'SolverError';
|
|
this.type = type;
|
|
}
|
|
}
|
|
|
|
export class MemoryError extends Error {
|
|
constructor(message) {
|
|
super(message);
|
|
this.name = 'MemoryError';
|
|
this.type = 'MEMORY_ERROR';
|
|
}
|
|
}
|
|
|
|
export class ValidationError extends Error {
|
|
constructor(message) {
|
|
super(message);
|
|
this.name = 'ValidationError';
|
|
this.type = 'VALIDATION_ERROR';
|
|
}
|
|
}
|
|
|
|
// Export everything
|
|
export {
|
|
Matrix,
|
|
SolverConfig,
|
|
SolutionStep,
|
|
SolutionStream,
|
|
MemoryManager,
|
|
SublinearSolver as default
|
|
}; |