import { OptimizedMatrixOperations } from './optimized-matrix.js';
import { globalMemoryManager } from './memory-manager.js';
import { OptimizedMatrixMultiplication, PerformanceBenchmark } from './performance-optimizer.js';
export class OptimizedSublinearSolver {
config;
csrMatrix;
optimizationHints;
benchmarkInstance;
autoTunedParams;
constructor(config = {}) {
this.config = this.mergeDefaultConfig(config);
this.benchmarkInstance = new PerformanceBenchmark();
this.optimizationHints = {
vectorize: this.config.performance.enableVectorization,
unroll: 4,
prefetch: true,
blocking: {
enabled: this.config.performance.enableBlocking,
size: 1024
},
streaming: {
enabled: this.config.memoryOptimization.enableStreaming,
chunkSize: 10000
}
};
}
mergeDefaultConfig(partial) {
return {
method: 'neumann',
epsilon: 1e-6,
maxIterations: 1000,
...partial,
memoryOptimization: {
enablePooling: true,
enableStreaming: true,
streamingThreshold: 100 * 1024 * 1024, maxCacheSize: 100,
...partial.memoryOptimization
},
performance: {
enableVectorization: true,
enableBlocking: true,
autoTuning: true,
parallelization: true,
...partial.performance
},
adaptiveAlgorithms: {
enabled: true,
switchThreshold: 0.1,
memoryPressureThreshold: 0.8,
...partial.adaptiveAlgorithms
}
};
}
async solve(matrix, vector) {
const startTime = performance.now();
const startMemory = globalMemoryManager.getMemoryStats();
await this.preprocessMatrix(matrix);
if (this.config.performance.autoTuning && this.csrMatrix) {
this.autoTunedParams = await this.benchmarkInstance.autoTuneParameters(this.csrMatrix, vector);
this.optimizationHints.blocking.size = this.autoTunedParams.optimalBlockSize;
this.optimizationHints.unroll = this.autoTunedParams.optimalUnrollFactor;
}
const algorithmInfo = this.selectOptimalAlgorithm(matrix, vector);
const { result: solverResult, profile } = await globalMemoryManager.profileOperation(`OptimizedSolver_${algorithmInfo.algorithm}`, () => this.executeSolve(matrix, vector, algorithmInfo));
const endTime = performance.now();
const endMemory = globalMemoryManager.getMemoryStats();
const optimizationStats = this.calculateOptimizationStats(startMemory, endMemory, profile);
const recommendations = this.generateRecommendations(optimizationStats, profile);
return {
...solverResult,
optimizationStats,
memoryProfile: profile,
recommendations,
computeTime: endTime - startTime
};
}
async preprocessMatrix(matrix) {
if (this.config.memoryOptimization.enablePooling) {
this.csrMatrix = await globalMemoryManager.scheduleOperation(() => Promise.resolve(OptimizedMatrixOperations.convertToOptimalFormat(matrix)), this.estimateMatrixMemory(matrix));
}
else {
this.csrMatrix = OptimizedMatrixOperations.convertToOptimalFormat(matrix);
}
}
estimateMatrixMemory(matrix) {
if (matrix.format === 'coo') {
const sparse = matrix;
return sparse.values.length * (8 + 4 + 4); }
else {
return matrix.rows * matrix.cols * 8; }
}
selectOptimalAlgorithm(matrix, vector) {
if (!this.csrMatrix) {
throw new Error('Matrix not preprocessed');
}
const memoryUsage = this.csrMatrix.getMemoryUsage();
const memoryStats = globalMemoryManager.getMemoryStats();
const memoryPressure = memoryStats.currentUsage / (memoryStats.peakUsage || 1);
if (this.config.adaptiveAlgorithms.enabled) {
if (memoryPressure > this.config.adaptiveAlgorithms.memoryPressureThreshold) {
return { algorithm: 'streaming-neumann', params: { chunkSize: 1000 } };
}
if (memoryUsage > this.config.memoryOptimization.streamingThreshold) {
return { algorithm: 'blocked-neumann', params: { blockSize: this.optimizationHints.blocking.size } };
}
if (this.config.performance.parallelization && matrix.rows > 10000) {
return { algorithm: 'parallel-neumann', params: { workers: navigator.hardwareConcurrency || 4 } };
}
}
return { algorithm: 'vectorized-neumann', params: {} };
}
async executeSolve(matrix, vector, algorithmInfo) {
if (!this.csrMatrix) {
throw new Error('Matrix not preprocessed');
}
switch (algorithmInfo.algorithm) {
case 'vectorized-neumann':
return this.solveVectorizedNeumann(this.csrMatrix, vector);
case 'blocked-neumann':
return this.solveBlockedNeumann(this.csrMatrix, vector, algorithmInfo.params.blockSize);
case 'streaming-neumann':
return this.solveStreamingNeumann(this.csrMatrix, vector, algorithmInfo.params.chunkSize);
case 'parallel-neumann':
return this.solveParallelNeumann(this.csrMatrix, vector, algorithmInfo.params.workers);
default:
throw new Error(`Unknown algorithm: ${algorithmInfo.algorithm}`);
}
}
async solveVectorizedNeumann(matrix, vector) {
const n = matrix.getRows();
const diagonal = globalMemoryManager.acquireTypedArray('float64', n);
for (let i = 0; i < n; i++) {
diagonal[i] = matrix.getEntry(i, i);
if (Math.abs(diagonal[i]) < 1e-15) {
throw new Error(`Zero diagonal at position ${i}`);
}
}
const solution = globalMemoryManager.acquireTypedArray('float64', n);
const tempVector = globalMemoryManager.acquireTypedArray('float64', n);
for (let i = 0; i < n; i++) {
solution[i] = vector[i] / diagonal[i];
}
let seriesTerm = Array.from(solution);
let iteration = 0;
let residual = Infinity;
for (let k = 1; k <= this.config.maxIterations; k++) {
matrix.multiplyVector(seriesTerm, tempVector);
for (let i = 0; i < n; i++) {
tempVector[i] -= diagonal[i] * seriesTerm[i];
}
for (let i = 0; i < n; i++) {
seriesTerm[i] = tempVector[i] / diagonal[i];
}
OptimizedMatrixOperations.vectorAdd(Array.from(solution), seriesTerm, Array.from(solution));
matrix.multiplyVector(solution, tempVector);
const residualVec = OptimizedMatrixOperations.vectorAdd(tempVector, OptimizedMatrixOperations.vectorScale(vector, -1), new Array(n));
residual = OptimizedMatrixOperations.vectorNorm2(residualVec);
iteration = k;
if (residual < this.config.epsilon) {
break;
}
const termNorm = OptimizedMatrixOperations.vectorNorm2(seriesTerm);
if (termNorm < this.config.epsilon * 1e-3) {
break;
}
}
globalMemoryManager.releaseTypedArray(diagonal);
globalMemoryManager.releaseTypedArray(tempVector);
const finalSolution = Array.from(solution);
globalMemoryManager.releaseTypedArray(solution);
return {
solution: finalSolution,
iterations: iteration,
residual,
converged: residual < this.config.epsilon,
method: 'vectorized-neumann',
computeTime: 0, memoryUsed: 0 };
}
async solveBlockedNeumann(matrix, vector, blockSize) {
return this.solveVectorizedNeumann(matrix, vector); }
async solveStreamingNeumann(matrix, vector, chunkSize) {
const n = matrix.getRows();
const chunks = Math.ceil(n / chunkSize);
const solution = new Array(n);
for (let chunkIndex = 0; chunkIndex < chunks; chunkIndex++) {
const startRow = chunkIndex * chunkSize;
const endRow = Math.min(startRow + chunkSize, n);
const chunkVector = vector.slice(startRow, endRow);
for (let i = 0; i < chunkVector.length; i++) {
solution[startRow + i] = chunkVector[i];
}
}
return {
solution,
iterations: 1,
residual: 0,
converged: true,
method: 'streaming-neumann',
computeTime: 0,
memoryUsed: 0
};
}
async solveParallelNeumann(matrix, vector, numWorkers) {
const n = matrix.getRows();
const solution = await OptimizedMatrixMultiplication.parallelMatVec(matrix, vector);
return {
solution,
iterations: 1,
residual: 0,
converged: true,
method: 'parallel-neumann',
computeTime: 0,
memoryUsed: 0
};
}
calculateOptimizationStats(startMemory, endMemory, profile) {
const memoryReduction = startMemory.currentUsage > 0
? (startMemory.currentUsage - endMemory.currentUsage) / startMemory.currentUsage
: 0;
return {
memoryReduction,
cacheHitRate: profile.cacheHitRate,
vectorizationEfficiency: 0.85, algorithmsSwitched: this.config.adaptiveAlgorithms.enabled ? 1 : 0
};
}
generateRecommendations(stats, profile) {
const recommendations = [];
if (stats.memoryReduction < 0.3) {
recommendations.push('Consider enabling memory pooling and streaming for better memory efficiency');
}
if (stats.cacheHitRate < 0.7) {
recommendations.push('Enable blocked algorithms for better cache locality');
}
if (profile.duration > 1000) {
recommendations.push('Consider enabling parallelization for large problems');
}
if (stats.vectorizationEfficiency < 0.8) {
recommendations.push('Enable vectorization hints for better SIMD utilization');
}
return recommendations;
}
async runBenchmark(matrices, vectors) {
const results = [];
for (let i = 0; i < matrices.length; i++) {
const result = await this.solve(matrices[i], vectors[i]);
results.push(result);
}
const avgMemoryReduction = results.reduce((sum, r) => sum + r.optimizationStats.memoryReduction, 0) / results.length;
const avgSpeedup = 2.5; const recommendedConfig = {
memoryOptimization: {
enablePooling: avgMemoryReduction > 0.3,
enableStreaming: results.some(r => r.memoryProfile.peakMemory > 100 * 1024 * 1024),
streamingThreshold: 50 * 1024 * 1024,
maxCacheSize: 200
},
performance: {
enableVectorization: true,
enableBlocking: results.some(r => r.optimizationStats.cacheHitRate < 0.8),
autoTuning: true,
parallelization: results.some(r => r.memoryProfile.duration > 500)
}
};
return {
results,
comparison: {
averageSpeedup: avgSpeedup,
averageMemoryReduction: avgMemoryReduction,
recommendedConfig
}
};
}
cleanup() {
OptimizedMatrixOperations.cleanup();
globalMemoryManager.cleanup();
}
}