#!/usr/bin/env node const { Command } = require('commander'); const chalk = require('chalk'); const ora = require('ora'); const express = require('express'); const fs = require('fs').promises; const path = require('path'); const { createSolver } = require('../src/solver'); const { SolverServer } = require('../server/index'); const { FlowNexusIntegration } = require('../integrations/flow-nexus'); const program = new Command(); program .name('sublinear-time-solver') .description('Advanced Sublinear Time Sparse Linear System Solver') .version(require('../package.json').version || '1.0.0') .option('-v, --verbose', 'Enable verbose output') .option('-q, --quiet', 'Suppress non-essential output') .option('--debug', 'Enable debug mode'); // Solve command program .command('solve') .description('Solve linear system Ax = b') .requiredOption('-m, --matrix ', 'Input matrix file (JSON, CSV, MTX)') .option('-b, --vector ', 'Right-hand side vector file') .option('-o, --output ', 'Output solution file') .option('--method ', 'Solver method (jacobi|gauss-seidel|cg|hybrid)', 'adaptive') .option('--tolerance ', 'Convergence tolerance', '1e-10') .option('--max-iterations ', 'Maximum iterations', '1000') .option('--streaming', 'Enable streaming output') .option('--verify', 'Enable solution verification') .action(async (options) => { const spinner = ora('Loading matrix...').start(); try { // Load matrix data const matrixData = await loadMatrix(options.matrix); spinner.text = 'Loading vector...'; // Load or generate vector const vector = options.vector ? await loadVector(options.vector) : generateRandomVector(matrixData.rows); spinner.succeed(`Loaded ${matrixData.rows}Ɨ${matrixData.cols} system`); // Create solver const solver = await createSolver({ matrix: matrixData, method: options.method, tolerance: parseFloat(options.tolerance), maxIterations: parseInt(options.maxIterations), enableVerification: options.verify }); if (options.streaming) { console.log(chalk.blue('šŸ”„ Starting streaming solve...')); await streamingSolve(solver, vector, options); } else { console.log(chalk.blue('šŸ”„ Starting batch solve...')); await batchSolve(solver, vector, options); } } catch (error) { spinner.fail('Solve failed'); console.error(chalk.red('Error:'), error.message); if (options.debug) console.error(error.stack); process.exit(1); } }); // Serve command program .command('serve') .description('Start HTTP streaming server') .option('-p, --port ', 'Server port', '3000') .option('--cors', 'Enable CORS') .option('--flow-nexus', 'Enable Flow-Nexus integration') .option('--workers ', 'Number of worker threads', '1') .option('--max-sessions ', 'Maximum concurrent sessions', '100') .option('--auth-token ', 'Authentication token for protected endpoints') .action(async (options) => { const server = new SolverServer({ port: parseInt(options.port), cors: options.cors, workers: parseInt(options.workers), maxSessions: parseInt(options.maxSessions), authToken: options.authToken, flowNexusEnabled: options.flowNexus }); await server.start(); console.log(chalk.green(`šŸš€ Solver server running on port ${options.port}`)); console.log(chalk.blue(` REST API: http://localhost:${options.port}/api`)); console.log(chalk.blue(` WebSocket: ws://localhost:${options.port}/ws`)); if (options.flowNexus) { console.log(chalk.yellow(` Flow-Nexus: Enabled`)); } }); // Verify command program .command('verify') .description('Verify solution accuracy') .requiredOption('-m, --matrix ', 'Matrix file') .requiredOption('-x, --solution ', 'Solution vector file') .requiredOption('-b, --vector ', 'Right-hand side vector file') .option('--tolerance ', 'Verification tolerance', '1e-8') .option('--probes ', 'Number of random probes', '10') .action(async (options) => { const spinner = ora('Loading verification data...').start(); try { const matrix = await loadMatrix(options.matrix); const solution = await loadVector(options.solution); const vector = await loadVector(options.vector); spinner.text = 'Running verification...'; const result = await verifySolution({ matrix, solution, vector, tolerance: parseFloat(options.tolerance), probes: parseInt(options.probes) }); if (result.verified) { spinner.succeed('Solution verified successfully'); console.log(chalk.green(`āœ“ Max error: ${result.maxError.toExponential(2)}`)); console.log(chalk.green(`āœ“ Mean error: ${result.meanError.toExponential(2)}`)); } else { spinner.fail('Solution verification failed'); console.log(chalk.red(`āœ— Max error: ${result.maxError.toExponential(2)}`)); console.log(chalk.red(`āœ— Tolerance: ${options.tolerance}`)); } } catch (error) { spinner.fail('Verification failed'); console.error(chalk.red('Error:'), error.message); process.exit(1); } }); // Benchmark command program .command('benchmark') .description('Run performance benchmarks') .option('--size ', 'Matrix size', '1000') .option('--sparsity ', 'Matrix sparsity (0-1)', '0.01') .option('--methods ', 'Comma-separated list of methods', 'jacobi,cg,hybrid') .option('--iterations ', 'Benchmark iterations', '5') .option('--output ', 'Output results to JSON file') .action(async (options) => { const spinner = ora('Setting up benchmark...').start(); try { const results = await runBenchmark({ size: parseInt(options.size), sparsity: parseFloat(options.sparsity), methods: options.methods.split(','), iterations: parseInt(options.iterations) }); spinner.succeed('Benchmark completed'); console.log(chalk.blue('\nšŸ“Š Benchmark Results:')); results.forEach(result => { console.log(chalk.white(`\n${result.method}:`)); console.log(` Average time: ${result.avgTime.toFixed(2)}ms`); console.log(` Min time: ${result.minTime.toFixed(2)}ms`); console.log(` Max time: ${result.maxTime.toFixed(2)}ms`); console.log(` Iterations: ${result.avgIterations.toFixed(0)}`); console.log(` Convergence rate: ${(result.convergenceRate * 100).toFixed(1)}%`); }); if (options.output) { await fs.writeFile(options.output, JSON.stringify(results, null, 2)); console.log(chalk.green(`\nšŸ“ Results saved to ${options.output}`)); } } catch (error) { spinner.fail('Benchmark failed'); console.error(chalk.red('Error:'), error.message); process.exit(1); } }); // Convert command program .command('convert') .description('Convert between matrix formats') .requiredOption('-i, --input ', 'Input file') .requiredOption('-o, --output ', 'Output file') .option('--format ', 'Output format (json|csv|mtx|binary)', 'json') .option('--compress', 'Compress output') .action(async (options) => { const spinner = ora('Converting matrix format...').start(); try { await convertMatrix({ input: options.input, output: options.output, format: options.format, compress: options.compress }); spinner.succeed(`Converted ${options.input} → ${options.output}`); } catch (error) { spinner.fail('Conversion failed'); console.error(chalk.red('Error:'), error.message); process.exit(1); } }); // Flow-Nexus integration command program .command('flow-nexus') .description('Flow-Nexus platform integration') .option('--register', 'Register as solver service') .option('--swarm-join ', 'Join swarm with specified ID') .option('--endpoint ', 'Flow-Nexus endpoint URL') .option('--token ', 'Authentication token') .action(async (options) => { const spinner = ora('Connecting to Flow-Nexus...').start(); try { const integration = new FlowNexusIntegration({ endpoint: options.endpoint, token: options.token }); if (options.register) { await integration.registerSolver(); spinner.succeed('Registered with Flow-Nexus platform'); } if (options.swarmJoin) { await integration.joinSwarm(options.swarmJoin); spinner.succeed(`Joined swarm: ${options.swarmJoin}`); } } catch (error) { spinner.fail('Flow-Nexus integration failed'); console.error(chalk.red('Error:'), error.message); process.exit(1); } }); // Helper functions async function loadMatrix(filepath) { const data = await fs.readFile(filepath, 'utf8'); const ext = path.extname(filepath).toLowerCase(); switch (ext) { case '.json': return JSON.parse(data); case '.csv': return parseCSVMatrix(data); case '.mtx': return parseMatrixMarket(data); default: throw new Error(`Unsupported matrix format: ${ext}`); } } async function loadVector(filepath) { const data = await fs.readFile(filepath, 'utf8'); const ext = path.extname(filepath).toLowerCase(); switch (ext) { case '.json': return JSON.parse(data); case '.csv': return data.split('\n').map(line => parseFloat(line.trim())).filter(x => !isNaN(x)); default: throw new Error(`Unsupported vector format: ${ext}`); } } function generateRandomVector(size) { return Array.from({ length: size }, () => Math.random() * 10 - 5); } async function streamingSolve(solver, vector, options) { const outputStream = options.output ? require('fs').createWriteStream(options.output) : null; for await (const update of solver.streamSolve(vector)) { const line = JSON.stringify({ iteration: update.iteration, residual: update.residual, timestamp: new Date().toISOString(), convergence_rate: update.convergenceRate, memory_usage: update.memoryUsage }) + '\n'; if (outputStream) { outputStream.write(line); } else { console.log(chalk.gray(`[${update.iteration}]`), chalk.blue(`Residual: ${update.residual.toExponential(2)}`)); } if (update.converged) { console.log(chalk.green(`āœ“ Converged in ${update.iteration} iterations`)); break; } } if (outputStream) { outputStream.end(); console.log(chalk.green(`šŸ“ Streaming output saved to ${options.output}`)); } } async function batchSolve(solver, vector, options) { const startTime = Date.now(); const solution = await solver.solve(vector, { onProgress: (update) => { if (!options.quiet) { process.stdout.write(`\r${chalk.blue('Progress:')} Iteration ${update.iteration}, Residual: ${update.residual.toExponential(2)}`); } } }); const elapsed = Date.now() - startTime; console.log(chalk.green(`\nāœ“ Solution found in ${elapsed}ms`)); console.log(` Iterations: ${solution.iterations}`); console.log(` Final residual: ${solution.residual.toExponential(2)}`); console.log(` Memory usage: ${solution.memoryUsage}MB`); if (options.output) { await fs.writeFile(options.output, JSON.stringify({ solution: solution.values, metadata: { iterations: solution.iterations, residual: solution.residual, solveTime: elapsed, method: options.method } }, null, 2)); console.log(chalk.green(`šŸ“ Solution saved to ${options.output}`)); } } async function verifySolution({ matrix, solution, vector, tolerance, probes }) { // Implement verification logic const errors = []; // Full residual check: ||Ax - b|| const residual = computeResidual(matrix, solution, vector); const residualNorm = vectorNorm(residual); // Random probe verification for (let i = 0; i < probes; i++) { const idx = Math.floor(Math.random() * matrix.rows); const computed = multiplyMatrixRow(matrix, idx, solution); const error = Math.abs(computed - vector[idx]); errors.push(error); } const maxError = Math.max(...errors); const meanError = errors.reduce((a, b) => a + b) / errors.length; return { verified: maxError < tolerance && residualNorm < tolerance, maxError, meanError, residualNorm, probeErrors: errors }; } async function runBenchmark({ size, sparsity, methods, iterations }) { const results = []; for (const method of methods) { const times = []; const iterationCounts = []; let convergenceCount = 0; for (let i = 0; i < iterations; i++) { const matrix = generateRandomSparseMatrix(size, sparsity); const vector = generateRandomVector(size); const solver = await createSolver({ matrix, method }); const startTime = Date.now(); try { const solution = await solver.solve(vector); const elapsed = Date.now() - startTime; times.push(elapsed); iterationCounts.push(solution.iterations); if (solution.converged) convergenceCount++; } catch (error) { console.warn(`Benchmark failed for ${method}:`, error.message); } } if (times.length > 0) { results.push({ method, avgTime: times.reduce((a, b) => a + b) / times.length, minTime: Math.min(...times), maxTime: Math.max(...times), avgIterations: iterationCounts.reduce((a, b) => a + b) / iterationCounts.length, convergenceRate: convergenceCount / iterations }); } } return results; } async function convertMatrix({ input, output, format, compress }) { const matrix = await loadMatrix(input); let converted; switch (format.toLowerCase()) { case 'json': converted = JSON.stringify(matrix, null, compress ? 0 : 2); break; case 'csv': converted = matrixToCSV(matrix); break; case 'mtx': converted = matrixToMatrixMarket(matrix); break; case 'binary': converted = matrixToBinary(matrix); break; default: throw new Error(`Unsupported output format: ${format}`); } await fs.writeFile(output, converted); } // Matrix parsing utilities function parseCSVMatrix(data) { const lines = data.trim().split('\n'); const matrix = lines.map(line => line.split(',').map(val => parseFloat(val.trim())) ); return { rows: matrix.length, cols: matrix[0].length, data: matrix, format: 'dense' }; } function parseMatrixMarket(data) { const lines = data.trim().split('\n'); let headerLine = 0; // Skip comments while (lines[headerLine].startsWith('%')) headerLine++; const [rows, cols, entries] = lines[headerLine].split(' ').map(Number); const values = []; const rowIndices = []; const colIndices = []; for (let i = headerLine + 1; i < lines.length; i++) { if (lines[i].trim()) { const [row, col, val] = lines[i].split(' '); rowIndices.push(parseInt(row) - 1); // Convert to 0-based colIndices.push(parseInt(col) - 1); values.push(parseFloat(val)); } } return { rows, cols, entries, data: { values, rowIndices, colIndices }, format: 'coo' }; } // Mathematical utilities function computeResidual(matrix, x, b) { const Ax = multiplyMatrixVector(matrix, x); return Ax.map((val, i) => val - b[i]); } function vectorNorm(v) { return Math.sqrt(v.reduce((sum, val) => sum + val * val, 0)); } function multiplyMatrixVector(matrix, vector) { if (matrix.format === 'dense') { return matrix.data.map(row => row.reduce((sum, val, i) => sum + val * vector[i], 0) ); } else if (matrix.format === 'coo') { const result = new Array(matrix.rows).fill(0); for (let i = 0; i < matrix.data.values.length; i++) { const row = matrix.data.rowIndices[i]; const col = matrix.data.colIndices[i]; const val = matrix.data.values[i]; result[row] += val * vector[col]; } return result; } throw new Error(`Unsupported matrix format: ${matrix.format}`); } function multiplyMatrixRow(matrix, rowIndex, vector) { if (matrix.format === 'dense') { return matrix.data[rowIndex].reduce((sum, val, i) => sum + val * vector[i], 0); } else if (matrix.format === 'coo') { let result = 0; for (let i = 0; i < matrix.data.values.length; i++) { if (matrix.data.rowIndices[i] === rowIndex) { const col = matrix.data.colIndices[i]; const val = matrix.data.values[i]; result += val * vector[col]; } } return result; } throw new Error(`Unsupported matrix format: ${matrix.format}`); } function generateRandomSparseMatrix(size, sparsity) { const values = []; const rowIndices = []; const colIndices = []; const numEntries = Math.floor(size * size * sparsity); for (let i = 0; i < numEntries; i++) { const row = Math.floor(Math.random() * size); const col = Math.floor(Math.random() * size); const val = Math.random() * 10 - 5; rowIndices.push(row); colIndices.push(col); values.push(val); } return { rows: size, cols: size, entries: numEntries, data: { values, rowIndices, colIndices }, format: 'coo' }; } // Format conversion utilities function matrixToCSV(matrix) { if (matrix.format === 'dense') { return matrix.data.map(row => row.join(',')).join('\n'); } throw new Error('CSV export only supported for dense matrices'); } function matrixToMatrixMarket(matrix) { let output = '%%MatrixMarket matrix coordinate real general\n'; output += `${matrix.rows} ${matrix.cols} ${matrix.entries || matrix.data.values.length}\n`; if (matrix.format === 'coo') { for (let i = 0; i < matrix.data.values.length; i++) { const row = matrix.data.rowIndices[i] + 1; // Convert to 1-based const col = matrix.data.colIndices[i] + 1; const val = matrix.data.values[i]; output += `${row} ${col} ${val}\n`; } } return output; } function matrixToBinary(matrix) { // Simplified binary format const buffer = Buffer.alloc(8 + matrix.data.values.length * 8); buffer.writeInt32LE(matrix.rows, 0); buffer.writeInt32LE(matrix.cols, 4); for (let i = 0; i < matrix.data.values.length; i++) { buffer.writeDoubleLE(matrix.data.values[i], 8 + i * 8); } return buffer; } // Error handling process.on('uncaughtException', (error) => { console.error(chalk.red('Uncaught Exception:'), error.message); if (program.opts().debug) console.error(error.stack); process.exit(1); }); process.on('unhandledRejection', (error) => { console.error(chalk.red('Unhandled Rejection:'), error.message); if (program.opts().debug) console.error(error.stack); process.exit(1); }); // Parse command line arguments program.parseAsync().catch((error) => { console.error(chalk.red('CLI Error:'), error.message); process.exit(1); });