wifi-densepose/vendor/sublinear-time-solver/tests/validation/test-solver-fixes.js

332 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
const { createSolver, JSSolver } = require('../src/solver.js');
const { MatrixUtils } = require('../src/utils/matrix-utils.js');
/**
* Comprehensive test suite for solver fixes
*/
async function runSolverFixTests() {
console.log('🧪 Comprehensive Solver Fix Test Suite');
console.log('=====================================\n');
let totalTests = 0;
let passedTests = 0;
const results = [];
// Test Case 1: Auto-fix diagonal issues
console.log('Test 1: Auto-fix missing diagonal elements');
console.log('-'.repeat(45));
try {
totalTests++;
// Create matrix with missing diagonal
const problematicMatrix = {
rows: 3,
cols: 3,
format: 'coo',
entries: 5,
data: {
rowIndices: [0, 0, 1, 2, 2],
colIndices: [1, 2, 2, 0, 1],
values: [1, -1, 2, -1, 1]
}
};
const vector = [1, 2, 3];
// Should auto-fix the matrix
const solver = await createSolver({
matrix: problematicMatrix,
method: 'jacobi',
tolerance: 1e-8,
maxIterations: 100,
autoFixMatrix: true,
verbose: true
});
const result = await solver.solve(vector);
if (result.converged) {
console.log('✅ PASS: Auto-fix enabled successful convergence');
passedTests++;
results.push({ test: 'Auto-fix diagonal', status: 'PASS', details: `Converged in ${result.iterations} iterations` });
} else {
console.log('❌ FAIL: Auto-fix did not achieve convergence');
results.push({ test: 'Auto-fix diagonal', status: 'FAIL', details: `Did not converge after ${result.iterations} iterations` });
}
} catch (error) {
console.log(`❌ FAIL: Auto-fix test error: ${error.message}`);
results.push({ test: 'Auto-fix diagonal', status: 'FAIL', details: error.message });
}
console.log();
// Test Case 2: Well-conditioned matrix generation
console.log('Test 2: Well-conditioned matrix generation');
console.log('-'.repeat(45));
try {
totalTests++;
for (const size of [50, 100, 200]) {
console.log(` Testing ${size}×${size} matrix...`);
const matrix = MatrixUtils.generateWellConditionedSparseMatrix(size, 0.05, {
diagonalStrategy: 'rowsum_plus_one',
ensureDominance: true
});
const conditioning = MatrixUtils.analyzeConditioning(matrix);
if (conditioning.isWellConditioned && conditioning.isDiagonallyDominant) {
console.log(` ✅ Size ${size}: Grade ${conditioning.conditioningGrade}, dominance ratio ${conditioning.diagonalDominanceRatio.toFixed(3)}`);
} else {
console.log(` ❌ Size ${size}: Poor conditioning (Grade ${conditioning.conditioningGrade})`);
throw new Error(`Poor conditioning for size ${size}`);
}
}
console.log('✅ PASS: All matrix sizes well-conditioned');
passedTests++;
results.push({ test: 'Well-conditioned generation', status: 'PASS', details: 'All sizes passed conditioning checks' });
} catch (error) {
console.log(`❌ FAIL: Matrix generation test error: ${error.message}`);
results.push({ test: 'Well-conditioned generation', status: 'FAIL', details: error.message });
}
console.log();
// Test Case 3: Convergence rate testing
console.log('Test 3: Convergence rate analysis');
console.log('-'.repeat(45));
try {
totalTests++;
const testConfigs = [
{ size: 50, sparsity: 0.05, method: 'jacobi', matrixType: 'general' },
{ size: 50, sparsity: 0.05, method: 'gauss-seidel', matrixType: 'general' },
{ size: 50, sparsity: 0.05, method: 'conjugate-gradient', matrixType: 'symmetric' },
{ size: 100, sparsity: 0.03, method: 'jacobi', matrixType: 'general' },
{ size: 100, sparsity: 0.03, method: 'gauss-seidel', matrixType: 'general' },
{ size: 100, sparsity: 0.03, method: 'conjugate-gradient', matrixType: 'symmetric' }
];
let convergenceCount = 0;
const convergenceResults = [];
for (const config of testConfigs) {
console.log(` Testing ${config.method} on ${config.size}×${config.size} ${config.matrixType} matrix...`);
const matrix = config.matrixType === 'symmetric'
? MatrixUtils.generateSymmetricPositiveDefiniteMatrix(config.size, config.sparsity)
: MatrixUtils.generateWellConditionedSparseMatrix(config.size, config.sparsity);
const vector = Array.from({ length: config.size }, () => Math.random() * 10 - 5);
const solver = await createSolver({
matrix,
method: config.method,
tolerance: 1e-8,
maxIterations: 500,
verbose: false
});
const result = await solver.solve(vector);
const testResult = {
...config,
converged: result.converged,
iterations: result.iterations,
residual: result.residual
};
convergenceResults.push(testResult);
if (result.converged) {
convergenceCount++;
console.log(` ✅ Converged in ${result.iterations} iterations (residual: ${result.residual.toExponential(2)})`);
} else {
console.log(` ❌ Failed to converge (residual: ${result.residual.toExponential(2)})`);
}
}
const convergenceRate = (convergenceCount / testConfigs.length) * 100;
console.log(`\nOverall convergence rate: ${convergenceRate.toFixed(1)}%`);
if (convergenceRate >= 90) {
console.log('✅ PASS: Convergence rate ≥ 90%');
passedTests++;
results.push({ test: 'Convergence rate', status: 'PASS', details: `${convergenceRate.toFixed(1)}% convergence rate` });
} else {
console.log('❌ FAIL: Convergence rate < 90%');
results.push({ test: 'Convergence rate', status: 'FAIL', details: `Only ${convergenceRate.toFixed(1)}% convergence rate` });
}
} catch (error) {
console.log(`❌ FAIL: Convergence rate test error: ${error.message}`);
results.push({ test: 'Convergence rate', status: 'FAIL', details: error.message });
}
console.log();
// Test Case 4: Validation and error handling
console.log('Test 4: Enhanced validation and error handling');
console.log('-'.repeat(45));
try {
totalTests++;
// Test that invalid matrices are properly detected
const invalidMatrices = [
{
name: "Missing diagonal with autoFix disabled",
matrix: {
rows: 3, cols: 3, format: 'coo', entries: 3,
data: { rowIndices: [0, 1, 2], colIndices: [1, 2, 0], values: [1, 1, 1] }
},
shouldFail: true,
autoFix: false
},
{
name: "Zero diagonal elements",
matrix: {
rows: 2, cols: 2, format: 'dense',
data: [[0, 1], [1, 2]]
},
shouldFail: true,
autoFix: false
}
];
let validationTestsPassed = 0;
for (const test of invalidMatrices) {
try {
const solver = await createSolver({
matrix: test.matrix,
method: 'jacobi',
autoFixMatrix: test.autoFix,
verbose: false
});
const result = await solver.solve([1, 1]);
if (test.shouldFail) {
console.log(`${test.name}: Should have failed but didn't`);
} else {
console.log(`${test.name}: Passed as expected`);
validationTestsPassed++;
}
} catch (error) {
if (test.shouldFail) {
console.log(`${test.name}: Correctly failed with: ${error.message.slice(0, 50)}...`);
validationTestsPassed++;
} else {
console.log(`${test.name}: Unexpectedly failed with: ${error.message}`);
}
}
}
if (validationTestsPassed === invalidMatrices.length) {
console.log('✅ PASS: All validation tests behaved correctly');
passedTests++;
results.push({ test: 'Validation handling', status: 'PASS', details: 'All validation cases handled correctly' });
} else {
console.log(`❌ FAIL: ${validationTestsPassed}/${invalidMatrices.length} validation tests passed`);
results.push({ test: 'Validation handling', status: 'FAIL', details: `Only ${validationTestsPassed}/${invalidMatrices.length} passed` });
}
} catch (error) {
console.log(`❌ FAIL: Validation test error: ${error.message}`);
results.push({ test: 'Validation handling', status: 'FAIL', details: error.message });
}
console.log();
// Test Case 5: Performance with large matrices
console.log('Test 5: Performance with larger matrices');
console.log('-'.repeat(45));
try {
totalTests++;
const largeMatrix = MatrixUtils.generateWellConditionedSparseMatrix(500, 0.02);
const largeVector = Array.from({ length: 500 }, () => Math.random() * 5);
console.log(` Testing 500×500 matrix (${largeMatrix.entries} non-zeros)...`);
const startTime = Date.now();
const solver = await createSolver({
matrix: largeMatrix,
method: 'jacobi',
tolerance: 1e-6,
maxIterations: 1000,
verbose: false
});
const result = await solver.solve(largeVector);
const elapsed = Date.now() - startTime;
console.log(` Solve time: ${elapsed}ms`);
console.log(` Iterations: ${result.iterations}`);
console.log(` Converged: ${result.converged ? 'Yes' : 'No'}`);
console.log(` Final residual: ${result.residual.toExponential(2)}`);
if (result.converged && elapsed < 10000) { // Should solve within 10 seconds
console.log('✅ PASS: Large matrix solved efficiently');
passedTests++;
results.push({ test: 'Large matrix performance', status: 'PASS', details: `Solved in ${elapsed}ms with ${result.iterations} iterations` });
} else {
console.log('❌ FAIL: Large matrix performance unsatisfactory');
results.push({ test: 'Large matrix performance', status: 'FAIL', details: `${elapsed}ms, converged: ${result.converged}` });
}
} catch (error) {
console.log(`❌ FAIL: Large matrix test error: ${error.message}`);
results.push({ test: 'Large matrix performance', status: 'FAIL', details: error.message });
}
// Summary
console.log('\n' + '='.repeat(60));
console.log('🎯 TEST SUMMARY');
console.log('='.repeat(60));
console.log(`Total tests: ${totalTests}`);
console.log(`Passed: ${passedTests}`);
console.log(`Failed: ${totalTests - passedTests}`);
console.log(`Success rate: ${((passedTests / totalTests) * 100).toFixed(1)}%`);
console.log('\nDetailed Results:');
for (const result of results) {
const status = result.status === 'PASS' ? '✅' : '❌';
console.log(` ${status} ${result.test}: ${result.details}`);
}
if (passedTests === totalTests) {
console.log('\n🎉 ALL TESTS PASSED! The Jacobi solver fixes are working correctly.');
return true;
} else {
console.log(`\n⚠️ ${totalTests - passedTests} tests failed. Review the fixes.`);
return false;
}
}
// Run the test suite
if (require.main === module) {
runSolverFixTests()
.then(success => {
process.exit(success ? 0 : 1);
})
.catch(error => {
console.error('Fatal test error:', error);
process.exit(1);
});
}
module.exports = { runSolverFixTests };