wifi-densepose/vendor/sublinear-time-solver/bin/cli.js

618 lines
19 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 { 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 <file>', 'Input matrix file (JSON, CSV, MTX)')
.option('-b, --vector <file>', 'Right-hand side vector file')
.option('-o, --output <file>', 'Output solution file')
.option('--method <name>', 'Solver method (jacobi|gauss-seidel|cg|hybrid)', 'adaptive')
.option('--tolerance <value>', 'Convergence tolerance', '1e-10')
.option('--max-iterations <n>', '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 <number>', 'Server port', '3000')
.option('--cors', 'Enable CORS')
.option('--flow-nexus', 'Enable Flow-Nexus integration')
.option('--workers <number>', 'Number of worker threads', '1')
.option('--max-sessions <number>', 'Maximum concurrent sessions', '100')
.option('--auth-token <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 <file>', 'Matrix file')
.requiredOption('-x, --solution <file>', 'Solution vector file')
.requiredOption('-b, --vector <file>', 'Right-hand side vector file')
.option('--tolerance <value>', 'Verification tolerance', '1e-8')
.option('--probes <count>', '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 <number>', 'Matrix size', '1000')
.option('--sparsity <value>', 'Matrix sparsity (0-1)', '0.01')
.option('--methods <list>', 'Comma-separated list of methods', 'jacobi,cg,hybrid')
.option('--iterations <number>', 'Benchmark iterations', '5')
.option('--output <file>', '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 <file>', 'Input file')
.requiredOption('-o, --output <file>', 'Output file')
.option('--format <type>', '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 <id>', 'Join swarm with specified ID')
.option('--endpoint <url>', 'Flow-Nexus endpoint URL')
.option('--token <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);
});