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

532 lines (457 loc) • 15 kB
/** * Quantum Engine - Topology Operations * * Implements quantum topology computations including: * - Betti numbers (topological invariants) * - Persistent homology * - Simplicial complex analysis * * Used for analyzing agent relationship graphs and memory topology. */ import type { IQuantumEngine, BettiNumbers, PersistenceDiagram, PersistencePoint, SimplicialComplex, Filtration, TopologyResult, WasmModule } from '../types.js'; /** * QuantumEngine - WASM wrapper for quantum topology operations */ export class QuantumEngine implements IQuantumEngine { private wasmModule: WasmModule | null = null; constructor(wasmModule?: WasmModule) { this.wasmModule = wasmModule ?? null; } /** * Set the WASM module after initialization */ setWasmModule(module: WasmModule): void { this.wasmModule = module; } /** * Compute Betti numbers for a simplicial complex * * @param complex - Simplicial complex * @returns BettiNumbers value object */ async computeBettiNumbers(complex: SimplicialComplex): Promise<BettiNumbers> { if (complex.vertices.length === 0) { return this.createBettiNumbers([0]); } if (this.wasmModule) { // Convert complex to point cloud for WASM const points = this.complexToPointCloud(complex); const n = complex.vertices.length; const dim = 3; // Assume 3D embedding const maxDim = complex.maxDimension; const result = this.wasmModule.quantum_betti_numbers(points, n, dim, maxDim); return this.createBettiNumbers(Array.from(result)); } // Pure JS implementation return this.computeBettiNumbersJS(complex); } /** * Compute persistence diagram for a filtration * * @param filtration - Filtration of simplicial complex * @returns PersistenceDiagram */ async persistenceDiagram(filtration: Filtration): Promise<PersistenceDiagram> { if (filtration.complex.vertices.length === 0) { return { points: [], maxPersistence: 0, totalPersistence: 0 }; } if (this.wasmModule) { const points = this.complexToPointCloud(filtration.complex); const n = filtration.complex.vertices.length; const dim = 3; const result = this.wasmModule.quantum_persistence_diagram(points, n, dim); return this.parsePersistenceDiagram(result); } // Pure JS implementation return this.computePersistenceDiagramJS(filtration); } /** * Compute number of homology classes * * @param complex - Simplicial complex * @returns Number of homology classes */ async computeHomologyClasses(complex: SimplicialComplex): Promise<number> { const betti = await this.computeBettiNumbers(complex); // Sum of all Betti numbers return betti.values.reduce((sum, b) => sum + b, 0); } /** * Compute full topology result * * @param points - Point cloud as Float32Array[] * @param maxDimension - Maximum dimension for Betti numbers * @returns TopologyResult */ async computeTopology(points: Float32Array[], maxDimension: number = 2): Promise<TopologyResult> { // Build simplicial complex from points const complex = this.buildRipsComplex(points, maxDimension); // Build filtration const filtration = this.buildFiltration(complex, points); // Compute all features const bettiNumbers = await this.computeBettiNumbers(complex); const persistenceDiagram = await this.persistenceDiagram(filtration); const homologyClasses = await this.computeHomologyClasses(complex); return { bettiNumbers: bettiNumbers.values, persistenceDiagram, homologyClasses }; } /** * Create BettiNumbers value object */ createBettiNumbers(values: number[]): BettiNumbers { return { values: [...values], b0: values[0] ?? 0, b1: values[1] ?? 0, b2: values[2] ?? 0, connected: (values[0] ?? 0) === 1, hasLoops: (values[1] ?? 0) > 0, hasVoids: (values[2] ?? 0) > 0 }; } /** * Pure JS implementation of Betti number computation * Uses rank-nullity approach on boundary matrices */ private computeBettiNumbersJS(complex: SimplicialComplex): BettiNumbers { const values: number[] = []; // b0 = number of connected components const b0 = this.countConnectedComponents(complex); values.push(b0); // b1 = number of loops (1-cycles) if (complex.maxDimension >= 1) { const b1 = this.computeB1(complex); values.push(b1); } // b2 = number of voids (2-cycles) if (complex.maxDimension >= 2) { const b2 = this.computeB2(complex); values.push(b2); } return this.createBettiNumbers(values); } /** * Count connected components using union-find */ private countConnectedComponents(complex: SimplicialComplex): number { const n = complex.vertices.length; if (n === 0) return 0; const parent = new Array(n).fill(0).map((_, i) => i); const rank = new Array(n).fill(0); const find = (x: number): number => { const px = parent[x]; if (px !== undefined && px !== x) { parent[x] = find(px); } return parent[x] ?? x; }; const union = (x: number, y: number): void => { const px = find(x); const py = find(y); if (px === py) return; const rpx = rank[px] ?? 0; const rpy = rank[py] ?? 0; if (rpx < rpy) { parent[px] = py; } else if (rpx > rpy) { parent[py] = px; } else { parent[py] = px; rank[px] = rpx + 1; } }; // Get 1-simplices (edges) const edges = complex.simplices.filter(s => s.length === 2); for (const edge of edges) { const v1 = edge[0]; const v2 = edge[1]; if (v1 !== undefined && v2 !== undefined) { union(v1, v2); } } // Count unique components const components = new Set<number>(); for (let i = 0; i < n; i++) { components.add(find(i)); } return components.size; } /** * Compute b1 (number of 1-cycles / loops) */ private computeB1(complex: SimplicialComplex): number { const vertices = complex.vertices.length; const edges = complex.simplices.filter(s => s.length === 2).length; const components = this.countConnectedComponents(complex); // Euler characteristic relation: V - E + F = chi // b1 = E - V + components (simplified for 2D) // This is an approximation - full computation requires boundary matrix ranks return Math.max(0, edges - vertices + components); } /** * Compute b2 (number of 2-cycles / voids) */ private computeB2(complex: SimplicialComplex): number { const triangles = complex.simplices.filter(s => s.length === 3).length; const tetrahedra = complex.simplices.filter(s => s.length === 4).length; // Simplified approximation // Full computation requires boundary matrix of 3-simplices return Math.max(0, triangles - 3 * tetrahedra); } /** * Pure JS implementation of persistence diagram */ private computePersistenceDiagramJS(filtration: Filtration): PersistenceDiagram { const points: PersistencePoint[] = []; const { complex, values } = filtration; // Sort simplices by filtration value const sortedSimplices = complex.simplices .map((simplex, idx) => ({ simplex, value: values[idx] ?? 0 })) .sort((a, b) => a.value - b.value); // Track component births and merges const n = complex.vertices.length; const parent = new Array(n).fill(0).map((_, i) => i); const birthTime = new Array(n).fill(0); const find = (x: number): number => { const px = parent[x]; if (px !== undefined && px !== x) { parent[x] = find(px); } return parent[x] ?? x; }; for (const { simplex, value } of sortedSimplices) { if (simplex.length === 1) { // Vertex - birth of component const v = simplex[0]; if (v !== undefined) { birthTime[v] = value; } } else if (simplex.length === 2) { // Edge - potential merge const v1 = simplex[0]; const v2 = simplex[1]; if (v1 === undefined || v2 === undefined) continue; const p1 = find(v1); const p2 = find(v2); if (p1 !== p2) { // Merge - older component survives const birthP1 = birthTime[p1] ?? 0; const birthP2 = birthTime[p2] ?? 0; const older = birthP1 <= birthP2 ? p1 : p2; const younger = older === p1 ? p2 : p1; // Death of younger component const birth = birthTime[younger] ?? 0; const death = value; if (death > birth) { points.push({ birth, death, persistence: death - birth, dimension: 0 }); } parent[younger] = older; } else { // Same component - creates a loop (1-cycle born) points.push({ birth: value, death: Infinity, persistence: Infinity, dimension: 1 }); } } } // Components that never die const components = new Set<number>(); for (let i = 0; i < n; i++) { components.add(find(i)); } for (const comp of components) { points.push({ birth: birthTime[comp] ?? 0, death: Infinity, persistence: Infinity, dimension: 0 }); } const finitePoints = points.filter(p => p.persistence !== Infinity); const maxPersistence = finitePoints.length > 0 ? Math.max(...finitePoints.map(p => p.persistence)) : 0; const totalPersistence = finitePoints.reduce((sum, p) => sum + p.persistence, 0); return { points, maxPersistence, totalPersistence }; } /** * Parse WASM persistence diagram result */ private parsePersistenceDiagram(result: Float32Array): PersistenceDiagram { const points: PersistencePoint[] = []; // Format: [numPoints, birth1, death1, dim1, birth2, death2, dim2, ...] const numPoints = result[0] ?? 0; for (let i = 0; i < numPoints; i++) { const birth = result[1 + i * 3] ?? 0; const death = result[2 + i * 3] ?? 0; const dimension = result[3 + i * 3] ?? 0; points.push({ birth, death, persistence: death - birth, dimension }); } const finitePoints = points.filter(p => Number.isFinite(p.persistence)); const maxPersistence = finitePoints.length > 0 ? Math.max(...finitePoints.map(p => p.persistence)) : 0; const totalPersistence = finitePoints.reduce((sum, p) => sum + p.persistence, 0); return { points, maxPersistence, totalPersistence }; } /** * Build Vietoris-Rips complex from point cloud */ private buildRipsComplex(points: Float32Array[], maxDimension: number): SimplicialComplex { const n = points.length; const vertices = Array.from({ length: n }, (_, i) => i); const simplices: number[][] = []; // Add vertices as 0-simplices for (let i = 0; i < n; i++) { simplices.push([i]); } // Compute pairwise distances and add edges const threshold = this.computeThreshold(points); for (let i = 0; i < n; i++) { for (let j = i + 1; j < n; j++) { const pi = points[i]; const pj = points[j]; if (!pi || !pj) continue; const dist = this.euclideanDistance(pi, pj); if (dist <= threshold) { simplices.push([i, j]); } } } // Add triangles (2-simplices) if maxDimension >= 2 if (maxDimension >= 2) { const edges = simplices.filter(s => s.length === 2); for (let i = 0; i < n; i++) { for (let j = i + 1; j < n; j++) { for (let k = j + 1; k < n; k++) { // Check if all three edges exist const hasIJ = edges.some(e => (e[0] === i && e[1] === j) || (e[0] === j && e[1] === i)); const hasJK = edges.some(e => (e[0] === j && e[1] === k) || (e[0] === k && e[1] === j)); const hasIK = edges.some(e => (e[0] === i && e[1] === k) || (e[0] === k && e[1] === i)); if (hasIJ && hasJK && hasIK) { simplices.push([i, j, k]); } } } } } return { vertices, simplices, maxDimension }; } /** * Build filtration from complex */ private buildFiltration(complex: SimplicialComplex, points: Float32Array[]): Filtration { const values: number[] = []; for (const simplex of complex.simplices) { if (simplex.length === 1) { values.push(0); // Vertices appear at time 0 } else { // Edge/face appears when its diameter is reached let maxDist = 0; for (let i = 0; i < simplex.length; i++) { for (let j = i + 1; j < simplex.length; j++) { const si = simplex[i]; const sj = simplex[j]; if (si === undefined || sj === undefined) continue; const pi = points[si]; const pj = points[sj]; if (!pi || !pj) continue; const dist = this.euclideanDistance(pi, pj); maxDist = Math.max(maxDist, dist); } } values.push(maxDist); } } return { complex, values }; } /** * Compute threshold for Rips complex */ private computeThreshold(points: Float32Array[]): number { if (points.length < 2) return 0; // Use median of all pairwise distances const distances: number[] = []; for (let i = 0; i < points.length; i++) { for (let j = i + 1; j < points.length; j++) { const pi = points[i]; const pj = points[j]; if (!pi || !pj) continue; distances.push(this.euclideanDistance(pi, pj)); } } distances.sort((a, b) => a - b); return distances[Math.floor(distances.length / 2)] ?? 0; } /** * Euclidean distance between two points */ private euclideanDistance(a: Float32Array, b: Float32Array): number { let sum = 0; const len = Math.min(a.length, b.length); for (let i = 0; i < len; i++) { const diff = (a[i] ?? 0) - (b[i] ?? 0); sum += diff * diff; } return Math.sqrt(sum); } /** * Convert simplicial complex to point cloud for WASM */ private complexToPointCloud(complex: SimplicialComplex): Float32Array { // Simple embedding: use vertex index as coordinate const n = complex.vertices.length; const dim = 3; const points = new Float32Array(n * dim); for (let i = 0; i < n; i++) { // Create simple embedding based on vertex connectivity const edges = complex.simplices .filter(s => s.length === 2 && s.includes(i)); points[i * dim] = i; // x = vertex index points[i * dim + 1] = edges.length; // y = degree points[i * dim + 2] = 0; // z = 0 } return points; } }