mirror of
https://github.com/ruvnet/RuView
synced 2026-08-08 20:11:43 +00:00
feat: vendor midstream and sublinear-time-solver libraries (#109)
Add ruvnet/midstream (AIMDS real-time inference) and ruvnet/sublinear-time-solver (sublinear optimization algorithms) as vendored dependencies under vendor/.
This commit is contained in:
@@ -0,0 +1,316 @@
|
||||
/**
|
||||
* Advanced Convergence Detection and Metrics System
|
||||
*
|
||||
* Provides proper residual norm calculation, convergence rate tracking,
|
||||
* and early stopping mechanisms for iterative solvers.
|
||||
*/
|
||||
|
||||
class ConvergenceDetector {
|
||||
constructor(options = {}) {
|
||||
this.tolerance = options.tolerance || 1e-10;
|
||||
this.maxIterations = options.maxIterations || 1000;
|
||||
this.relativeToleranceEnabled = options.relativeToleranceEnabled !== false;
|
||||
this.minIterations = options.minIterations || 1;
|
||||
this.stagnationThreshold = options.stagnationThreshold || 1e-14;
|
||||
this.convergenceWindowSize = options.convergenceWindowSize || 10;
|
||||
|
||||
// State tracking
|
||||
this.reset();
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.iteration = 0;
|
||||
this.residualHistory = [];
|
||||
this.convergenceRateHistory = [];
|
||||
this.relativeResidualHistory = [];
|
||||
this.initialResidualNorm = null;
|
||||
this.rhsNorm = null;
|
||||
this.isConverged = false;
|
||||
this.stagnationDetected = false;
|
||||
this.divergenceDetected = false;
|
||||
this.startTime = Date.now();
|
||||
this.lastUpdateTime = Date.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize with the right-hand side vector for relative residual calculation
|
||||
* @param {Array<number>} rhs - Right-hand side vector b
|
||||
*/
|
||||
initialize(rhs) {
|
||||
this.rhsNorm = this.vectorNorm(rhs);
|
||||
if (this.rhsNorm === 0) {
|
||||
console.warn('Zero RHS vector detected - using absolute residual tolerance');
|
||||
this.relativeToleranceEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute proper residual: r = b - Ax
|
||||
* @param {Object} matrix - Matrix A in supported format
|
||||
* @param {Array<number>} solution - Current solution vector x
|
||||
* @param {Array<number>} rhs - Right-hand side vector b
|
||||
* @returns {Array<number>} - Residual vector
|
||||
*/
|
||||
computeResidual(matrix, solution, rhs) {
|
||||
const Ax = this.multiplyMatrixVector(matrix, solution);
|
||||
return rhs.map((bi, i) => bi - Ax[i]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute relative residual norm: ||r|| / ||b||
|
||||
* @param {Array<number>} residual - Residual vector
|
||||
* @returns {number} - Relative residual norm
|
||||
*/
|
||||
computeRelativeResidualNorm(residual) {
|
||||
const residualNorm = this.vectorNorm(residual);
|
||||
|
||||
if (this.relativeToleranceEnabled && this.rhsNorm > 0) {
|
||||
return residualNorm / this.rhsNorm;
|
||||
} else {
|
||||
return residualNorm;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update convergence state with new iteration data
|
||||
* @param {Object} matrix - Matrix A
|
||||
* @param {Array<number>} solution - Current solution x
|
||||
* @param {Array<number>} rhs - Right-hand side b
|
||||
* @returns {Object} - Convergence metrics
|
||||
*/
|
||||
update(matrix, solution, rhs) {
|
||||
this.iteration++;
|
||||
this.lastUpdateTime = Date.now();
|
||||
|
||||
// Compute residual and norms
|
||||
const residual = this.computeResidual(matrix, solution, rhs);
|
||||
const residualNorm = this.vectorNorm(residual);
|
||||
const relativeResidualNorm = this.computeRelativeResidualNorm(residual);
|
||||
|
||||
// Store history
|
||||
this.residualHistory.push(residualNorm);
|
||||
this.relativeResidualHistory.push(relativeResidualNorm);
|
||||
|
||||
// Set initial residual for convergence rate calculation
|
||||
if (this.iteration === 1) {
|
||||
this.initialResidualNorm = relativeResidualNorm;
|
||||
}
|
||||
|
||||
// Compute convergence rate
|
||||
const convergenceRate = this.computeConvergenceRate();
|
||||
this.convergenceRateHistory.push(convergenceRate);
|
||||
|
||||
// Check convergence conditions
|
||||
this.checkConvergence(relativeResidualNorm);
|
||||
this.checkStagnation();
|
||||
this.checkDivergence();
|
||||
|
||||
const metrics = {
|
||||
iteration: this.iteration,
|
||||
residualNorm: residualNorm,
|
||||
relativeResidualNorm: relativeResidualNorm,
|
||||
convergenceRate: convergenceRate,
|
||||
isConverged: this.isConverged,
|
||||
stagnationDetected: this.stagnationDetected,
|
||||
divergenceDetected: this.divergenceDetected,
|
||||
shouldStop: this.shouldStop(),
|
||||
reductionFactor: this.getReductionFactor(),
|
||||
estimatedIterationsRemaining: this.estimateIterationsRemaining(),
|
||||
elapsedTime: this.lastUpdateTime - this.startTime,
|
||||
iterationsPerSecond: this.iteration / ((this.lastUpdateTime - this.startTime) / 1000)
|
||||
};
|
||||
|
||||
return metrics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute logarithmic convergence rate: log(r_k / r_{k-1})
|
||||
* Uses averaging over recent iterations for stability
|
||||
*/
|
||||
computeConvergenceRate() {
|
||||
if (this.relativeResidualHistory.length < 2) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
const current = this.relativeResidualHistory[this.relativeResidualHistory.length - 1];
|
||||
const previous = this.relativeResidualHistory[this.relativeResidualHistory.length - 2];
|
||||
|
||||
if (previous === 0 || current === 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// Single-step convergence rate
|
||||
const singleStepRate = current / previous;
|
||||
|
||||
// Average convergence rate over recent iterations
|
||||
if (this.relativeResidualHistory.length >= this.convergenceWindowSize) {
|
||||
const windowStart = this.relativeResidualHistory.length - this.convergenceWindowSize;
|
||||
const windowEnd = this.relativeResidualHistory.length - 1;
|
||||
|
||||
const initialWindow = this.relativeResidualHistory[windowStart];
|
||||
const finalWindow = this.relativeResidualHistory[windowEnd];
|
||||
|
||||
if (initialWindow > 0 && finalWindow > 0) {
|
||||
const averageRate = Math.pow(finalWindow / initialWindow, 1.0 / (this.convergenceWindowSize - 1));
|
||||
return averageRate;
|
||||
}
|
||||
}
|
||||
|
||||
return singleStepRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if convergence criteria are met
|
||||
*/
|
||||
checkConvergence(relativeResidualNorm) {
|
||||
if (this.iteration < this.minIterations) {
|
||||
this.isConverged = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.isConverged = relativeResidualNorm < this.tolerance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect if iteration is stagnating
|
||||
*/
|
||||
checkStagnation() {
|
||||
if (this.residualHistory.length < this.convergenceWindowSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
const recentResiduals = this.residualHistory.slice(-this.convergenceWindowSize);
|
||||
const maxRecent = Math.max(...recentResiduals);
|
||||
const minRecent = Math.min(...recentResiduals);
|
||||
|
||||
// Check if residual has barely changed
|
||||
if (maxRecent > 0 && (maxRecent - minRecent) / maxRecent < this.stagnationThreshold) {
|
||||
this.stagnationDetected = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect if iteration is diverging
|
||||
*/
|
||||
checkDivergence() {
|
||||
if (this.residualHistory.length < 5) {
|
||||
return;
|
||||
}
|
||||
|
||||
const current = this.residualHistory[this.residualHistory.length - 1];
|
||||
const previous = this.residualHistory[this.residualHistory.length - 2];
|
||||
const initial = this.residualHistory[0];
|
||||
|
||||
// Check for explosive growth
|
||||
if (current > 1000 * initial || (previous > 0 && current / previous > 10)) {
|
||||
this.divergenceDetected = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if solver should stop
|
||||
*/
|
||||
shouldStop() {
|
||||
return this.isConverged ||
|
||||
this.iteration >= this.maxIterations ||
|
||||
this.stagnationDetected ||
|
||||
this.divergenceDetected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get overall reduction factor from initial residual
|
||||
*/
|
||||
getReductionFactor() {
|
||||
if (this.initialResidualNorm === null || this.initialResidualNorm === 0) {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
const current = this.relativeResidualHistory[this.relativeResidualHistory.length - 1] || 0;
|
||||
return current / this.initialResidualNorm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimate iterations remaining based on convergence rate
|
||||
*/
|
||||
estimateIterationsRemaining() {
|
||||
if (this.isConverged) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const currentResidual = this.relativeResidualHistory[this.relativeResidualHistory.length - 1];
|
||||
const convergenceRate = this.convergenceRateHistory[this.convergenceRateHistory.length - 1];
|
||||
|
||||
if (!currentResidual || !convergenceRate || convergenceRate >= 1.0 || convergenceRate <= 0) {
|
||||
return this.maxIterations - this.iteration;
|
||||
}
|
||||
|
||||
// Estimate iterations to reach tolerance: n = log(tol/current) / log(rate)
|
||||
const iterationsNeeded = Math.log(this.tolerance / currentResidual) / Math.log(convergenceRate);
|
||||
|
||||
return Math.max(0, Math.min(iterationsNeeded, this.maxIterations - this.iteration));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get comprehensive convergence report
|
||||
*/
|
||||
getConvergenceReport() {
|
||||
const current = this.relativeResidualHistory[this.relativeResidualHistory.length - 1] || 0;
|
||||
const avgConvergenceRate = this.convergenceRateHistory.length > 0
|
||||
? this.convergenceRateHistory.reduce((a, b) => a + b, 0) / this.convergenceRateHistory.length
|
||||
: 0;
|
||||
|
||||
return {
|
||||
iterations: this.iteration,
|
||||
finalResidual: current,
|
||||
initialResidual: this.initialResidualNorm,
|
||||
reductionFactor: this.getReductionFactor(),
|
||||
averageConvergenceRate: avgConvergenceRate,
|
||||
converged: this.isConverged,
|
||||
stagnated: this.stagnationDetected,
|
||||
diverged: this.divergenceDetected,
|
||||
tolerance: this.tolerance,
|
||||
relativeToleranceUsed: this.relativeToleranceEnabled,
|
||||
elapsedTime: this.lastUpdateTime - this.startTime,
|
||||
residualHistory: [...this.residualHistory],
|
||||
convergenceRateHistory: [...this.convergenceRateHistory]
|
||||
};
|
||||
}
|
||||
|
||||
// Utility methods
|
||||
vectorNorm(vector) {
|
||||
return Math.sqrt(vector.reduce((sum, val) => sum + val * val, 0));
|
||||
}
|
||||
|
||||
multiplyMatrixVector(matrix, vector) {
|
||||
const result = new Array(matrix.rows).fill(0);
|
||||
|
||||
if (matrix.format === 'dense') {
|
||||
for (let i = 0; i < matrix.rows; i++) {
|
||||
for (let j = 0; j < matrix.cols; j++) {
|
||||
result[i] += matrix.data[i][j] * vector[j];
|
||||
}
|
||||
}
|
||||
} else if (matrix.format === 'coo') {
|
||||
for (let k = 0; k < matrix.data.values.length; k++) {
|
||||
const row = matrix.data.rowIndices[k];
|
||||
const col = matrix.data.colIndices[k];
|
||||
const val = matrix.data.values[k];
|
||||
result[row] += val * vector[col];
|
||||
}
|
||||
} else if (matrix.format === 'csr') {
|
||||
for (let i = 0; i < matrix.rows; i++) {
|
||||
const start = matrix.data.rowPointers[i];
|
||||
const end = matrix.data.rowPointers[i + 1];
|
||||
for (let k = start; k < end; k++) {
|
||||
const col = matrix.data.colIndices[k];
|
||||
const val = matrix.data.values[k];
|
||||
result[i] += val * vector[col];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { ConvergenceDetector };
|
||||
@@ -0,0 +1,413 @@
|
||||
/**
|
||||
* Advanced Metrics Reporting System
|
||||
*
|
||||
* Provides comprehensive performance metrics, convergence analysis,
|
||||
* and visualization support for solver benchmarks.
|
||||
*/
|
||||
|
||||
class MetricsReporter {
|
||||
constructor(options = {}) {
|
||||
this.verboseOutput = options.verbose || false;
|
||||
this.saveHistory = options.saveHistory !== false;
|
||||
this.maxHistorySize = options.maxHistorySize || 1000;
|
||||
this.enableProfiling = options.enableProfiling || false;
|
||||
|
||||
this.reset();
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.startTime = null;
|
||||
this.endTime = null;
|
||||
this.solverMetrics = [];
|
||||
this.performanceProfile = {
|
||||
matrixVectorMultiplications: 0,
|
||||
normComputations: 0,
|
||||
convergenceChecks: 0,
|
||||
memoryAllocations: 0
|
||||
};
|
||||
this.convergenceData = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start tracking metrics for a new solve
|
||||
*/
|
||||
startSolve(solverConfig, matrixInfo) {
|
||||
this.reset();
|
||||
this.startTime = Date.now();
|
||||
this.solverConfig = { ...solverConfig };
|
||||
this.matrixInfo = { ...matrixInfo };
|
||||
|
||||
if (this.verboseOutput) {
|
||||
console.log('📊 Starting metrics collection...');
|
||||
console.log(` Matrix: ${matrixInfo.rows}×${matrixInfo.cols}, format: ${matrixInfo.format}`);
|
||||
console.log(` Method: ${solverConfig.method}, tolerance: ${solverConfig.tolerance}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record iteration metrics
|
||||
*/
|
||||
recordIteration(convergenceMetrics, solverState = {}) {
|
||||
const iterationMetrics = {
|
||||
timestamp: Date.now(),
|
||||
iteration: convergenceMetrics.iteration,
|
||||
residualNorm: convergenceMetrics.residualNorm,
|
||||
relativeResidualNorm: convergenceMetrics.relativeResidualNorm,
|
||||
convergenceRate: convergenceMetrics.convergenceRate,
|
||||
reductionFactor: convergenceMetrics.reductionFactor,
|
||||
isConverged: convergenceMetrics.isConverged,
|
||||
shouldStop: convergenceMetrics.shouldStop,
|
||||
elapsedTime: convergenceMetrics.elapsedTime,
|
||||
iterationsPerSecond: convergenceMetrics.iterationsPerSecond,
|
||||
estimatedTimeRemaining: this.estimateTimeRemaining(convergenceMetrics),
|
||||
memoryUsage: this.getCurrentMemoryUsage(),
|
||||
...solverState
|
||||
};
|
||||
|
||||
// Store history if enabled
|
||||
if (this.saveHistory) {
|
||||
this.solverMetrics.push(iterationMetrics);
|
||||
|
||||
// Limit history size to prevent memory issues
|
||||
if (this.solverMetrics.length > this.maxHistorySize) {
|
||||
this.solverMetrics.shift();
|
||||
}
|
||||
}
|
||||
|
||||
// Update profiling counters
|
||||
if (this.enableProfiling) {
|
||||
this.performanceProfile.convergenceChecks++;
|
||||
if (convergenceMetrics.iteration > 0) {
|
||||
this.performanceProfile.matrixVectorMultiplications++;
|
||||
this.performanceProfile.normComputations++;
|
||||
}
|
||||
}
|
||||
|
||||
return iterationMetrics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize solve and generate comprehensive report
|
||||
*/
|
||||
finalizeSolve(convergenceDetector, finalSolution = null) {
|
||||
this.endTime = Date.now();
|
||||
this.convergenceData = convergenceDetector.getConvergenceReport();
|
||||
|
||||
const report = this.generateComprehensiveReport(finalSolution);
|
||||
|
||||
if (this.verboseOutput) {
|
||||
this.printDetailedReport(report);
|
||||
}
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate comprehensive performance and convergence report
|
||||
*/
|
||||
generateComprehensiveReport(finalSolution = null) {
|
||||
const totalTime = this.endTime - this.startTime;
|
||||
const iterationCount = this.convergenceData.iterations;
|
||||
|
||||
// Basic timing metrics
|
||||
const timingMetrics = {
|
||||
totalTime,
|
||||
averageTimePerIteration: iterationCount > 0 ? totalTime / iterationCount : 0,
|
||||
iterationsPerSecond: iterationCount / (totalTime / 1000),
|
||||
convergenceTime: this.convergenceData.elapsedTime
|
||||
};
|
||||
|
||||
// Convergence analysis
|
||||
const convergenceAnalysis = this.analyzeConvergence();
|
||||
|
||||
// Performance classification
|
||||
const performanceGrade = this.classifyPerformance();
|
||||
|
||||
// Memory analysis
|
||||
const memoryAnalysis = this.analyzeMemoryUsage();
|
||||
|
||||
// Solution quality (if solution provided)
|
||||
const solutionQuality = finalSolution ? this.assessSolutionQuality(finalSolution) : null;
|
||||
|
||||
const report = {
|
||||
summary: {
|
||||
method: this.solverConfig.method,
|
||||
matrixSize: `${this.matrixInfo.rows}×${this.matrixInfo.cols}`,
|
||||
converged: this.convergenceData.converged,
|
||||
iterations: iterationCount,
|
||||
finalResidual: this.convergenceData.finalResidual,
|
||||
reductionFactor: this.convergenceData.reductionFactor,
|
||||
grade: performanceGrade
|
||||
},
|
||||
timing: timingMetrics,
|
||||
convergence: convergenceAnalysis,
|
||||
performance: performanceGrade,
|
||||
memory: memoryAnalysis,
|
||||
solution: solutionQuality,
|
||||
raw: {
|
||||
convergenceData: this.convergenceData,
|
||||
solverMetrics: this.saveHistory ? this.solverMetrics : [],
|
||||
performanceProfile: this.performanceProfile
|
||||
}
|
||||
};
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze convergence behavior
|
||||
*/
|
||||
analyzeConvergence() {
|
||||
const analysis = {
|
||||
converged: this.convergenceData.converged,
|
||||
iterations: this.convergenceData.iterations,
|
||||
finalResidual: this.convergenceData.finalResidual,
|
||||
initialResidual: this.convergenceData.initialResidual,
|
||||
reductionFactor: this.convergenceData.reductionFactor,
|
||||
averageConvergenceRate: this.convergenceData.averageConvergenceRate,
|
||||
relativeToleranceUsed: this.convergenceData.relativeToleranceUsed,
|
||||
stagnated: this.convergenceData.stagnated,
|
||||
diverged: this.convergenceData.diverged
|
||||
};
|
||||
|
||||
// Convergence rate classification
|
||||
if (analysis.averageConvergenceRate > 0 && analysis.averageConvergenceRate < 1) {
|
||||
analysis.convergenceType = 'linear';
|
||||
analysis.convergenceQuality = analysis.averageConvergenceRate < 0.1 ? 'excellent' :
|
||||
analysis.averageConvergenceRate < 0.5 ? 'good' :
|
||||
analysis.averageConvergenceRate < 0.9 ? 'acceptable' : 'slow';
|
||||
} else {
|
||||
analysis.convergenceType = 'unknown';
|
||||
analysis.convergenceQuality = 'poor';
|
||||
}
|
||||
|
||||
// Efficiency assessment
|
||||
const theoreticalIterations = analysis.initialResidual > 0 && analysis.finalResidual > 0
|
||||
? Math.log(analysis.finalResidual / analysis.initialResidual) / Math.log(analysis.averageConvergenceRate)
|
||||
: analysis.iterations;
|
||||
|
||||
analysis.efficiency = analysis.iterations > 0 ? Math.min(1.0, theoreticalIterations / analysis.iterations) : 0;
|
||||
|
||||
// Convergence rate percentage (what users expect to see)
|
||||
analysis.convergenceRatePercent = analysis.converged ? 100 :
|
||||
analysis.reductionFactor > 0 ? Math.min(99, Math.max(0, (1 - analysis.reductionFactor) * 100)) : 0;
|
||||
|
||||
return analysis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Classify overall performance
|
||||
*/
|
||||
classifyPerformance() {
|
||||
const iterations = this.convergenceData.iterations;
|
||||
const converged = this.convergenceData.converged;
|
||||
const time = this.endTime - this.startTime;
|
||||
const matrixSize = this.matrixInfo.rows;
|
||||
|
||||
let score = 0;
|
||||
let grade = 'F';
|
||||
let description = 'Failed';
|
||||
|
||||
// Convergence score (40%)
|
||||
if (converged) {
|
||||
score += 40;
|
||||
const optimalIterations = Math.sqrt(matrixSize); // Rough estimate for well-conditioned systems
|
||||
if (iterations <= optimalIterations) score += 20;
|
||||
else if (iterations <= optimalIterations * 2) score += 15;
|
||||
else if (iterations <= optimalIterations * 5) score += 10;
|
||||
}
|
||||
|
||||
// Speed score (30%)
|
||||
const timePerElement = time / (matrixSize * matrixSize);
|
||||
if (timePerElement < 0.001) score += 30;
|
||||
else if (timePerElement < 0.01) score += 25;
|
||||
else if (timePerElement < 0.1) score += 20;
|
||||
else if (timePerElement < 1) score += 10;
|
||||
|
||||
// Convergence rate score (30%)
|
||||
const avgRate = this.convergenceData.averageConvergenceRate;
|
||||
if (avgRate > 0 && avgRate < 0.1) score += 30;
|
||||
else if (avgRate < 0.3) score += 25;
|
||||
else if (avgRate < 0.7) score += 15;
|
||||
else if (avgRate < 0.95) score += 10;
|
||||
|
||||
// Assign letter grade
|
||||
if (score >= 90) { grade = 'A+'; description = 'Excellent performance'; }
|
||||
else if (score >= 85) { grade = 'A'; description = 'Very good performance'; }
|
||||
else if (score >= 80) { grade = 'A-'; description = 'Good performance'; }
|
||||
else if (score >= 75) { grade = 'B+'; description = 'Above average performance'; }
|
||||
else if (score >= 70) { grade = 'B'; description = 'Average performance'; }
|
||||
else if (score >= 65) { grade = 'B-'; description = 'Below average performance'; }
|
||||
else if (score >= 60) { grade = 'C+'; description = 'Acceptable performance'; }
|
||||
else if (score >= 55) { grade = 'C'; description = 'Poor performance'; }
|
||||
else if (score >= 50) { grade = 'C-'; description = 'Very poor performance'; }
|
||||
else if (score >= 30) { grade = 'D'; description = 'Barely functional'; }
|
||||
|
||||
return {
|
||||
score,
|
||||
grade,
|
||||
description,
|
||||
factors: {
|
||||
convergence: converged ? 'Good' : 'Poor',
|
||||
speed: timePerElement < 0.01 ? 'Good' : timePerElement < 0.1 ? 'Average' : 'Slow',
|
||||
efficiency: avgRate < 0.3 ? 'Good' : avgRate < 0.7 ? 'Average' : 'Poor'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze memory usage patterns
|
||||
*/
|
||||
analyzeMemoryUsage() {
|
||||
if (!this.saveHistory || this.solverMetrics.length === 0) {
|
||||
return {
|
||||
available: false,
|
||||
reason: 'Memory tracking disabled or no data'
|
||||
};
|
||||
}
|
||||
|
||||
const memoryValues = this.solverMetrics.map(m => m.memoryUsage).filter(m => m !== undefined);
|
||||
if (memoryValues.length === 0) {
|
||||
return {
|
||||
available: false,
|
||||
reason: 'No memory data collected'
|
||||
};
|
||||
}
|
||||
|
||||
const initial = memoryValues[0];
|
||||
const peak = Math.max(...memoryValues);
|
||||
const final = memoryValues[memoryValues.length - 1];
|
||||
const average = memoryValues.reduce((a, b) => a + b, 0) / memoryValues.length;
|
||||
|
||||
return {
|
||||
available: true,
|
||||
initialMB: initial,
|
||||
peakMB: peak,
|
||||
finalMB: final,
|
||||
averageMB: average,
|
||||
growthMB: final - initial,
|
||||
efficiency: this.matrixInfo.rows > 0 ? peak / (this.matrixInfo.rows * this.matrixInfo.rows * 8 / 1024 / 1024) : null
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Assess solution quality if solution vector is provided
|
||||
*/
|
||||
assessSolutionQuality(solution) {
|
||||
return {
|
||||
solutionNorm: this.vectorNorm(solution),
|
||||
maxElement: Math.max(...solution.map(Math.abs)),
|
||||
minElement: Math.min(...solution.map(Math.abs)),
|
||||
hasNaN: solution.some(x => isNaN(x)),
|
||||
hasInf: solution.some(x => !isFinite(x))
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimate time remaining based on current convergence rate
|
||||
*/
|
||||
estimateTimeRemaining(convergenceMetrics) {
|
||||
if (convergenceMetrics.isConverged || convergenceMetrics.shouldStop) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const remainingIterations = convergenceMetrics.estimatedIterationsRemaining || 0;
|
||||
const avgTimePerIteration = convergenceMetrics.elapsedTime / Math.max(1, convergenceMetrics.iteration);
|
||||
|
||||
return remainingIterations * avgTimePerIteration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current memory usage
|
||||
*/
|
||||
getCurrentMemoryUsage() {
|
||||
try {
|
||||
const usage = process.memoryUsage();
|
||||
return Math.round(usage.heapUsed / 1024 / 1024); // MB
|
||||
} catch (error) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print detailed report to console
|
||||
*/
|
||||
printDetailedReport(report) {
|
||||
console.log('\n📊 DETAILED PERFORMANCE REPORT');
|
||||
console.log('=' .repeat(60));
|
||||
|
||||
// Summary
|
||||
console.log(`\n🎯 SUMMARY`);
|
||||
console.log(` Method: ${report.summary.method}`);
|
||||
console.log(` Matrix: ${report.summary.matrixSize}`);
|
||||
console.log(` Result: ${report.summary.converged ? '✅ Converged' : '❌ Did not converge'}`);
|
||||
console.log(` Iterations: ${report.summary.iterations}`);
|
||||
console.log(` Final Residual: ${report.summary.finalResidual.toExponential(3)}`);
|
||||
console.log(` Grade: ${report.performance.grade} (${report.performance.description})`);
|
||||
|
||||
// Convergence analysis
|
||||
console.log(`\n📈 CONVERGENCE ANALYSIS`);
|
||||
console.log(` Convergence Rate: ${(report.convergence.convergenceRatePercent).toFixed(1)}%`);
|
||||
console.log(` Reduction Factor: ${report.convergence.reductionFactor.toExponential(3)}`);
|
||||
console.log(` Type: ${report.convergence.convergenceType} (${report.convergence.convergenceQuality})`);
|
||||
console.log(` Efficiency: ${(report.convergence.efficiency * 100).toFixed(1)}%`);
|
||||
|
||||
// Timing
|
||||
console.log(`\n⏱️ TIMING`);
|
||||
console.log(` Total Time: ${report.timing.totalTime}ms`);
|
||||
console.log(` Avg Time/Iteration: ${report.timing.averageTimePerIteration.toFixed(2)}ms`);
|
||||
console.log(` Iterations/Second: ${report.timing.iterationsPerSecond.toFixed(1)}`);
|
||||
|
||||
// Memory (if available)
|
||||
if (report.memory.available) {
|
||||
console.log(`\n💾 MEMORY`);
|
||||
console.log(` Peak Usage: ${report.memory.peakMB.toFixed(1)}MB`);
|
||||
console.log(` Final Usage: ${report.memory.finalMB.toFixed(1)}MB`);
|
||||
console.log(` Growth: ${report.memory.growthMB > 0 ? '+' : ''}${report.memory.growthMB.toFixed(1)}MB`);
|
||||
}
|
||||
|
||||
console.log('\n' + '=' .repeat(60));
|
||||
}
|
||||
|
||||
/**
|
||||
* Export metrics for external analysis
|
||||
*/
|
||||
exportMetrics(format = 'json') {
|
||||
const data = {
|
||||
config: this.solverConfig,
|
||||
matrix: this.matrixInfo,
|
||||
convergence: this.convergenceData,
|
||||
metrics: this.saveHistory ? this.solverMetrics : [],
|
||||
performance: this.performanceProfile,
|
||||
exportTime: new Date().toISOString()
|
||||
};
|
||||
|
||||
if (format === 'json') {
|
||||
return JSON.stringify(data, null, 2);
|
||||
} else if (format === 'csv') {
|
||||
return this.convertToCsv(data);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// Utility methods
|
||||
vectorNorm(vector) {
|
||||
return Math.sqrt(vector.reduce((sum, val) => sum + val * val, 0));
|
||||
}
|
||||
|
||||
convertToCsv(data) {
|
||||
if (!this.saveHistory || this.solverMetrics.length === 0) {
|
||||
return 'No iteration data available';
|
||||
}
|
||||
|
||||
const headers = Object.keys(this.solverMetrics[0]);
|
||||
const rows = this.solverMetrics.map(metric =>
|
||||
headers.map(h => metric[h] !== undefined ? metric[h] : '').join(',')
|
||||
);
|
||||
|
||||
return [headers.join(','), ...rows].join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { MetricsReporter };
|
||||
Reference in New Issue
Block a user