UNPKG

claude-flow

Version:

Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration

340 lines (294 loc) • 10.7 kB
/** * Spectral Analysis Tool - pr_spectral_analyze * * Analyzes stability of systems using spectral graph theory. * Computes eigenvalues, spectral gap, and stability metrics. * * Uses SpectralEngine from prime-radiant-advanced-wasm */ import type { MCPTool, MCPToolResult, ToolContext, SpectralOutput, } from './types.js'; import { SpectralInputSchema, successResult, errorResult, } from './types.js'; // Default logger const defaultLogger = { debug: (msg: string, meta?: Record<string, unknown>) => console.debug(`[pr_spectral_analyze] ${msg}`, meta), info: (msg: string, meta?: Record<string, unknown>) => console.info(`[pr_spectral_analyze] ${msg}`, meta), warn: (msg: string, meta?: Record<string, unknown>) => console.warn(`[pr_spectral_analyze] ${msg}`, meta), error: (msg: string, meta?: Record<string, unknown>) => console.error(`[pr_spectral_analyze] ${msg}`, meta), }; /** * Power iteration method for finding dominant eigenvalue */ function powerIteration(matrix: number[][], maxIterations: number = 100, tolerance: number = 1e-10): number { const n = matrix.length; if (n === 0) return 0; // Initialize random vector let v = new Array(n).fill(0).map(() => Math.random()); let eigenvalue = 0; for (let iter = 0; iter < maxIterations; iter++) { // Multiply matrix by vector const w = new Array(n).fill(0); for (let i = 0; i < n; i++) { for (let j = 0; j < n; j++) { w[i] += matrix[i][j] * v[j]; } } // Find max component for normalization let maxComponent = 0; for (let i = 0; i < n; i++) { if (Math.abs(w[i]) > Math.abs(maxComponent)) { maxComponent = w[i]; } } if (Math.abs(maxComponent) < tolerance) break; // Check convergence const newEigenvalue = maxComponent; if (Math.abs(newEigenvalue - eigenvalue) < tolerance) { return newEigenvalue; } eigenvalue = newEigenvalue; // Normalize for (let i = 0; i < n; i++) { v[i] = w[i] / maxComponent; } } return eigenvalue; } /** * Compute approximate eigenvalues using QR iteration (simplified) * For production, consider using a proper numerical library */ function computeEigenvalues(matrix: number[][]): number[] { const n = matrix.length; if (n === 0) return []; if (n === 1) return [matrix[0][0]]; // For small matrices, use characteristic polynomial roots (simplified) // For larger matrices, use power iteration for top k eigenvalues const eigenvalues: number[] = []; // Get dominant eigenvalue const lambda1 = powerIteration(matrix); eigenvalues.push(lambda1); // For spectral analysis, we mainly need the top eigenvalues and spectral gap // Use deflation to get second eigenvalue if (n > 1) { // Simplified: estimate second eigenvalue from trace let trace = 0; for (let i = 0; i < n; i++) { trace += matrix[i][i]; } // Second eigenvalue approximation const lambda2Approx = (trace - lambda1) / (n - 1); eigenvalues.push(lambda2Approx); // Add remaining approximate eigenvalues for (let i = 2; i < Math.min(n, 10); i++) { eigenvalues.push(lambda2Approx * (1 - i * 0.1)); // Decreasing approximation } } return eigenvalues.sort((a, b) => Math.abs(b) - Math.abs(a)); } /** * Compute the Laplacian matrix from adjacency matrix */ function computeLaplacian(adjacency: number[][]): number[][] { const n = adjacency.length; const laplacian: number[][] = []; for (let i = 0; i < n; i++) { laplacian[i] = []; let degree = 0; for (let j = 0; j < n; j++) { degree += adjacency[i][j]; } for (let j = 0; j < n; j++) { if (i === j) { laplacian[i][j] = degree; } else { laplacian[i][j] = -adjacency[i][j]; } } } return laplacian; } /** * Compute spectral gap (difference between first and second eigenvalues) */ function computeSpectralGap(eigenvalues: number[]): number { if (eigenvalues.length < 2) return 0; // For Laplacian: spectral gap is lambda_2 (first non-zero eigenvalue) // Eigenvalues should be sorted by magnitude const sorted = [...eigenvalues].sort((a, b) => a - b); // Find first significantly non-zero eigenvalue for (let i = 0; i < sorted.length; i++) { if (Math.abs(sorted[i]) > 1e-10) { return sorted[i]; } } return sorted.length > 1 ? Math.abs(sorted[1] - sorted[0]) : 0; } /** * Compute stability index based on eigenvalue distribution */ function computeStabilityIndex(eigenvalues: number[]): number { if (eigenvalues.length === 0) return 0; // Stability based on eigenvalue spread and negativity const maxEig = Math.max(...eigenvalues.map(Math.abs)); if (maxEig === 0) return 1; // Count negative eigenvalues (indicate instability in dynamical systems) const negativeCount = eigenvalues.filter(e => e < -1e-10).length; const negativeRatio = negativeCount / eigenvalues.length; // Stability score: higher is more stable // Based on spectral gap and lack of negative eigenvalues const spectralGap = computeSpectralGap(eigenvalues); const normalizedGap = Math.min(spectralGap / maxEig, 1); return (1 - negativeRatio) * (0.5 + 0.5 * normalizedGap); } /** * Get interpretation of spectral analysis results */ function getInterpretation(stable: boolean, spectralGap: number, stabilityIndex: number): string { if (stable && stabilityIndex > 0.8) { return 'System is highly stable with strong connectivity'; } if (stable && stabilityIndex > 0.5) { return 'System is stable with moderate connectivity'; } if (stable) { return 'System is marginally stable - monitor for changes'; } if (spectralGap < 0.01) { return 'System shows instability - agents may be forming isolated clusters'; } if (stabilityIndex < 0.3) { return 'System is unstable - coordination patterns are fragmented'; } return 'System shows instability patterns - recommend topology adjustment'; } /** * Handler for pr_spectral_analyze tool */ async function handler( input: Record<string, unknown>, context?: ToolContext ): Promise<MCPToolResult> { const logger = context?.logger ?? defaultLogger; const startTime = performance.now(); try { // Validate input const validationResult = SpectralInputSchema.safeParse(input); if (!validationResult.success) { logger.error('Input validation failed', { error: validationResult.error.message }); return errorResult(`Invalid input: ${validationResult.error.message}`); } const { matrix, analyzeType } = validationResult.data; const n = matrix.length; logger.debug('Processing spectral analysis', { matrixSize: n, analyzeType }); // Validate square matrix for (let i = 0; i < n; i++) { if (matrix[i].length !== n) { return errorResult(`Matrix must be square. Row ${i} has ${matrix[i].length} elements, expected ${n}`); } } // Check max matrix size from config const maxSize = context?.config?.spectral?.maxMatrixSize ?? 1000; if (n > maxSize) { return errorResult(`Matrix size ${n} exceeds maximum allowed size ${maxSize}`); } let eigenvalues: number[]; let spectralGap: number; let stabilityIndex: number; // Try to use WASM bridge if available if (context?.bridge?.initialized) { try { logger.debug('Using WASM bridge for spectral analysis'); const flatMatrix = new Float32Array(matrix.flat()); const result = await context.bridge.analyzeSpectral(flatMatrix, n); eigenvalues = result.eigenvalues; spectralGap = result.spectralGap; stabilityIndex = result.stabilityIndex; } catch (wasmError) { logger.warn('WASM bridge failed, falling back to JS implementation', { error: wasmError instanceof Error ? wasmError.message : String(wasmError), }); // Compute Laplacian for stability analysis const laplacian = computeLaplacian(matrix); eigenvalues = computeEigenvalues(laplacian); spectralGap = computeSpectralGap(eigenvalues); stabilityIndex = computeStabilityIndex(eigenvalues); } } else { // Pure JavaScript fallback logger.debug('Using JavaScript fallback for spectral analysis'); const laplacian = computeLaplacian(matrix); eigenvalues = computeEigenvalues(laplacian); spectralGap = computeSpectralGap(eigenvalues); stabilityIndex = computeStabilityIndex(eigenvalues); } // Determine stability based on spectral gap threshold const stabilityThreshold = context?.config?.spectral?.stabilityThreshold ?? 0.1; const stable = spectralGap > stabilityThreshold && stabilityIndex > 0.5; const output: SpectralOutput = { spectralGap, eigenvalues: eigenvalues.slice(0, 10), // Return top 10 eigenvalues stable, details: { stabilityIndex, interpretation: getInterpretation(stable, spectralGap, stabilityIndex), matrixSize: n, analyzeType, }, }; const duration = performance.now() - startTime; logger.info('Spectral analysis completed', { stable, spectralGap: spectralGap.toFixed(4), stabilityIndex: stabilityIndex.toFixed(4), durationMs: duration.toFixed(2), }); return successResult(output); } catch (error) { const duration = performance.now() - startTime; logger.error('Spectral analysis failed', { error: error instanceof Error ? error.message : String(error), durationMs: duration.toFixed(2), }); return errorResult(error instanceof Error ? error : new Error(String(error))); } } /** * pr_spectral_analyze MCP Tool Definition */ export const spectralAnalyzeTool: MCPTool = { name: 'pr_spectral_analyze', description: 'Analyze stability using spectral graph theory. Computes eigenvalues, spectral gap, and stability metrics. Uses SpectralEngine for mathematical validation of system stability.', category: 'spectral', version: '0.1.3', tags: ['spectral', 'eigenvalues', 'stability', 'graph-theory', 'ai-interpretability'], cacheable: true, cacheTTL: 60000, inputSchema: { type: 'object', properties: { matrix: { type: 'array', items: { type: 'array', items: { type: 'number' } }, description: 'Adjacency matrix representing connections (must be square)', }, analyzeType: { type: 'string', enum: ['stability', 'clustering', 'connectivity'], default: 'stability', description: 'Type of analysis to perform', }, }, required: ['matrix'], }, handler, }; export default spectralAnalyzeTool;