Skip to main content
Glama

Prompt Auto-Optimizer MCP

by sloth-wq
pareto-frontier.ts•30.5 kB
/** * Pareto Frontier Management System for Multi-Objective Optimization * * This implementation provides efficient Pareto dominance checking, candidate management, * and sampling strategies for genetic evolutionary prompt adaptation. */ import type { PromptCandidate } from '../types/gepa'; import { MemoryLeakIntegration } from './memory-leak-detector'; import { executeComputationWithResilience } from './resilience/index'; /** * Objective function definition for Pareto optimization */ export interface ParetoObjective { name: string; weight: number; direction: 'maximize' | 'minimize'; extractor: (candidate: PromptCandidate) => number; } /** * Point in the Pareto frontier with objectives and metadata */ export interface ParetoPoint { candidate: PromptCandidate; objectives: Map<string, number>; dominationCount: number; rank: number; } /** * Sampling strategy configuration */ export interface SamplingStrategy { name: 'uniform' | 'ucb' | 'epsilon-greedy' | 'pareto-based'; parameters?: { epsilon?: number; confidence?: number; exploration?: number; }; } /** * Configuration for Pareto frontier management */ export interface ParetoFrontierConfig { objectives: ParetoObjective[]; maxSize?: number; samplingStrategy?: SamplingStrategy; archiveEnabled?: boolean; } /** * Convergence metrics for analyzing frontier quality */ export interface ConvergenceMetrics { diversity: number; spacing: number; spread: number; hypervolume: number; generationalDistance: number; } /** * Statistical summary of frontier state */ export interface FrontierStatistics { totalCandidates: number; frontierSize: number; averageRank: number; objectives: Map<string, { min: number; max: number; mean: number; std: number }>; convergenceHistory: ConvergenceMetrics[]; } /** * Main Pareto Frontier implementation supporting multi-objective optimization */ export class ParetoFrontier { private frontier: ParetoPoint[] = []; private archive: ParetoPoint[] = []; private config: ParetoFrontierConfig; private convergenceHistory: ConvergenceMetrics[] = []; // OPTIMIZATION: Caching and indexing for performance private objectiveCache = new Map<string, Map<string, number>>(); private dominanceIndex = new DominanceIndex(); private lastUpdateTimestamp = 0; private hypervolumeCache = new Map<string, { value: number; timestamp: number }>(); constructor(config: ParetoFrontierConfig) { this.config = config; this.setupMemoryLeakDetection(); } /** * Add a candidate to the frontier, maintaining Pareto optimality (OPTIMIZED) */ async addCandidate(candidate: PromptCandidate): Promise<boolean> { return await executeComputationWithResilience( async () => { return this.addCandidateInternal(candidate); }, { context: { name: 'pareto-add-candidate', priority: 'medium' } } ); } /** * Internal implementation of addCandidate with resilience protection */ private async addCandidateInternal(candidate: PromptCandidate): Promise<boolean> { const point = this.createPoint(candidate); // OPTIMIZATION: Use dominance index for O(log n) checking instead of O(n) if (this.dominanceIndex.isDominated(point)) { return false; } // OPTIMIZATION: Quick identity check using cached objectives const candidateObjectives = this.getCachedObjectives(candidate); const isIdentical = this.frontier.some(existing => this.areObjectivesIdentical(candidateObjectives, existing.objectives) ); if (isIdentical) { return false; } // OPTIMIZATION: Use spatial indexing to find dominated points efficiently const dominatedPoints = this.dominanceIndex.findDominated(point); const nonDominatedPoints = this.frontier.filter(existing => !dominatedPoints.some(dominated => dominated.candidate.id === existing.candidate.id) ); // Add new point to frontier nonDominatedPoints.push(point); this.frontier = nonDominatedPoints; // Track memory allocation for leak detection const candidateSize = this.estimateCandidateSize(candidate); MemoryLeakIntegration.trackParetoOperation('add', candidate.id, candidateSize); // OPTIMIZATION: Update dominance index this.dominanceIndex.addPoint(point); if (dominatedPoints.length > 0) { this.dominanceIndex.removePoints(dominatedPoints); // Track removed candidates for leak detection for (const dominatedPoint of dominatedPoints) { const removedSize = this.estimateCandidateSize(dominatedPoint.candidate); MemoryLeakIntegration.trackParetoOperation('remove', dominatedPoint.candidate.id, removedSize); } } // Enforce size limit if configured if (this.config.maxSize && this.frontier.length > this.config.maxSize) { await this.enforceMaxSize(); } // Archive removed points if archiving is enabled if (this.config.archiveEnabled && dominatedPoints.length > 0) { this.archive.push(...dominatedPoints); } // OPTIMIZATION: Track convergence metrics less frequently if (this.shouldUpdateConvergenceMetrics()) { this.convergenceHistory.push(this.getConvergenceMetrics()); } this.lastUpdateTimestamp = Date.now(); return true; } /** * Check if one candidate dominates another */ isDominated(candidate: PromptCandidate, by?: PromptCandidate): boolean { const candidatePoint = this.createPoint(candidate); if (by) { const byPoint = this.createPoint(by); return this.dominates(byPoint, candidatePoint); } return this.frontier.some(point => this.dominates(point, candidatePoint)); } /** * Get all candidates dominated by the given candidate */ getDominatedCandidates(candidate: PromptCandidate): ParetoPoint[] { const candidatePoint = this.createPoint(candidate); return this.frontier.filter(point => this.dominates(candidatePoint, point)); } /** * Get current Pareto frontier */ getFrontier(): ParetoPoint[] { return [...this.frontier]; } /** * Sample a candidate using the specified strategy */ async sampleCandidate(strategy?: SamplingStrategy): Promise<PromptCandidate | null> { return await executeComputationWithResilience( async () => { return this.sampleCandidateInternal(strategy); }, { context: { name: 'pareto-sample-candidate', priority: 'low' } } ); } /** * Internal implementation of sampleCandidate with resilience protection */ private async sampleCandidateInternal(strategy?: SamplingStrategy): Promise<PromptCandidate | null> { if (this.frontier.length === 0) return null; const samplingStrategy = strategy || this.config.samplingStrategy || { name: 'uniform' }; switch (samplingStrategy.name) { case 'uniform': return this.sampleUniform(); case 'ucb': return this.sampleUCB(samplingStrategy.parameters?.confidence || 1.96); case 'epsilon-greedy': return this.sampleEpsilonGreedy(samplingStrategy.parameters?.epsilon || 0.1); default: return this.sampleUniform(); } } /** * Sample uniformly from frontier */ async sampleUniform(): Promise<PromptCandidate | null> { if (this.frontier.length === 0) return null; const randomIndex = Math.floor(Math.random() * this.frontier.length); const selected = this.frontier[randomIndex]; return selected ? selected.candidate : null; } /** * Sample using Upper Confidence Bound strategy */ async sampleUCB(confidence: number): Promise<PromptCandidate | null> { if (this.frontier.length === 0) return null; const scores = this.frontier.map(point => { const avgScore = point.candidate.averageScore; const uncertainty = confidence * Math.sqrt(Math.log(this.frontier.length) / (point.candidate.rolloutCount || 1)); return avgScore + uncertainty; }); const maxIndex = scores.indexOf(Math.max(...scores)); const selected = this.frontier[maxIndex]; return selected ? selected.candidate : null; } /** * Sample using epsilon-greedy strategy */ async sampleEpsilonGreedy(epsilon: number): Promise<PromptCandidate | null> { if (this.frontier.length === 0) return null; if (Math.random() < epsilon) { return this.sampleUniform(); } // Exploit: select best candidate by average score const bestPoint = this.frontier.reduce((best, current) => current.candidate.averageScore > best.candidate.averageScore ? current : best ); return bestPoint ? bestPoint.candidate : null; } /** * Compute hypervolume indicator (OPTIMIZED with caching) */ computeHypervolume(reference?: Map<string, number>): number { if (this.frontier.length === 0) return 0; // OPTIMIZATION: Cache hypervolume calculations const cacheKey = this.getHypervolumeCacheKey(reference); const cached = this.hypervolumeCache.get(cacheKey); if (cached && cached.timestamp > this.lastUpdateTimestamp) { return cached.value; } const ref = reference || this.computeReferencePoint(); // OPTIMIZATION: Use more efficient hypervolume calculation const hypervolume = this.computeHypervolumeOptimized(ref); // Cache the result this.hypervolumeCache.set(cacheKey, { value: hypervolume, timestamp: Date.now() }); return hypervolume; } /** * Get convergence metrics */ getConvergenceMetrics(): ConvergenceMetrics { if (this.frontier.length === 0) { return { diversity: 0, spacing: 0, spread: 0, hypervolume: 0, generationalDistance: 0 }; } const diversity = this.computeDiversity(); const spacing = this.computeSpacing(); const spread = this.computeSpread(); const hypervolume = this.computeHypervolume(); const generationalDistance = this.computeGenerationalDistance(); return { diversity, spacing, spread, hypervolume, generationalDistance }; } /** * Optimize archive to target size while preserving diversity */ async optimizeArchive(targetSize: number): Promise<ParetoPoint[]> { if (this.frontier.length <= targetSize) { return [...this.frontier]; } // Simple diversity-preserving selection // In practice, would use more sophisticated algorithms like SPEA2 or NSGA-II const selected: ParetoPoint[] = []; const remaining = [...this.frontier]; // Select most diverse points while (selected.length < targetSize && remaining.length > 0) { if (selected.length === 0) { // Select random first point const randomIndex = Math.floor(Math.random() * remaining.length); const firstPoint = remaining.splice(randomIndex, 1)[0]; if (firstPoint) selected.push(firstPoint); } else { // Select point with maximum minimum distance to selected points let maxMinDist = -1; let bestIndex = 0; for (let i = 0; i < remaining.length; i++) { const point = remaining[i]; if (point) { const minDist = Math.min(...selected.map(sel => this.computeDistance(point, sel))); if (minDist > maxMinDist) { maxMinDist = minDist; bestIndex = i; } } } const bestPoint = remaining.splice(bestIndex, 1)[0]; if (bestPoint) selected.push(bestPoint); } } return selected; } /** * Get frontier size */ size(): number { return this.frontier.length; } /** * Clear frontier and archive */ clear(): void { // Track bulk deallocation for leak detection for (const point of this.frontier) { const candidateSize = this.estimateCandidateSize(point.candidate); MemoryLeakIntegration.trackParetoOperation('remove', point.candidate.id, candidateSize); } for (const point of this.archive) { const candidateSize = this.estimateCandidateSize(point.candidate); MemoryLeakIntegration.trackParetoOperation('remove', point.candidate.id, candidateSize); } this.frontier = []; this.archive = []; this.convergenceHistory = []; // Clear caches to prevent memory leaks this.objectiveCache.clear(); this.hypervolumeCache.clear(); } /** * Get comprehensive statistics */ getStatistics(): FrontierStatistics { const objectiveStats = new Map<string, { min: number; max: number; mean: number; std: number }>(); this.config.objectives.forEach(obj => { const values = this.frontier.map(point => point.objectives.get(obj.name) || 0); if (values.length > 0) { const mean = values.reduce((a, b) => a + b, 0) / values.length; const variance = values.reduce((a, b) => a + Math.pow(b - mean, 2), 0) / values.length; objectiveStats.set(obj.name, { min: Math.min(...values), max: Math.max(...values), mean, std: Math.sqrt(variance) }); } }); return { totalCandidates: this.frontier.length + this.archive.length, frontierSize: this.frontier.length, averageRank: this.frontier.reduce((sum, point) => sum + point.rank, 0) / this.frontier.length || 0, objectives: objectiveStats, convergenceHistory: [...this.convergenceHistory] }; } // Private helper methods /** * Check if point a dominates point b */ private dominates(a: ParetoPoint, b: ParetoPoint): boolean { let betterInAny = false; let equalInAll = true; for (const obj of this.config.objectives) { const aValue = a.objectives.get(obj.name) || 0; const bValue = b.objectives.get(obj.name) || 0; // Handle NaN values gracefully if (isNaN(aValue) || isNaN(bValue)) { if (isNaN(aValue) && !isNaN(bValue)) return false; if (!isNaN(aValue) && isNaN(bValue)) betterInAny = true; equalInAll = false; continue; } if (obj.direction === 'maximize') { if (aValue < bValue) return false; if (aValue > bValue) { betterInAny = true; equalInAll = false; } else if (aValue !== bValue) { equalInAll = false; } } else { if (aValue > bValue) return false; if (aValue < bValue) { betterInAny = true; equalInAll = false; } else if (aValue !== bValue) { equalInAll = false; } } } // If all objectives are equal, no domination if (equalInAll) return false; return betterInAny; } /** * Create a Pareto point from a candidate */ private createPoint(candidate: PromptCandidate): ParetoPoint { const objectives = new Map<string, number>(); this.config.objectives.forEach(obj => { try { const value = obj.extractor(candidate); objectives.set(obj.name, isNaN(value) ? 0 : value); } catch { // Handle extraction errors gracefully objectives.set(obj.name, 0); } }); return { candidate, objectives, dominationCount: 0, rank: 1 }; } /** * Compute reference point for hypervolume calculation */ private computeReferencePoint(): Map<string, number> { const reference = new Map<string, number>(); this.config.objectives.forEach(obj => { const values = this.frontier.map(point => point.objectives.get(obj.name) || 0) .filter(v => !isNaN(v)); if (values.length > 0) { // Set reference point slightly worse than worst point in frontier const worstValue = obj.direction === 'maximize' ? Math.min(...values) : Math.max(...values); const offset = Math.abs(worstValue) * 0.1 + 0.01; reference.set(obj.name, obj.direction === 'maximize' ? worstValue - offset : worstValue + offset); } else { reference.set(obj.name, obj.direction === 'maximize' ? 0 : 1); } }); return reference; } /** * Enforce maximum frontier size by removing least diverse points */ private async enforceMaxSize(): Promise<void> { if (!this.config.maxSize || this.frontier.length <= this.config.maxSize) { return; } const optimized = await this.optimizeArchive(this.config.maxSize); // Archive removed points if enabled if (this.config.archiveEnabled) { const removed = this.frontier.filter(point => !optimized.some(opt => opt.candidate.id === point.candidate.id) ); this.archive.push(...removed); } this.frontier = optimized; } /** * Compute diversity metric (OPTIMIZED with sampling) */ private computeDiversity(): number { if (this.frontier.length < 2) return 0; // OPTIMIZATION: Use sampling for large frontiers to avoid O(n²) computation if (this.frontier.length > 50) { return this.computeDiversitySampled(); } let totalDistance = 0; let count = 0; for (let i = 0; i < this.frontier.length; i++) { const pointI = this.frontier[i]; if (!pointI) continue; for (let j = i + 1; j < this.frontier.length; j++) { const pointJ = this.frontier[j]; if (!pointJ) continue; totalDistance += this.computeDistance(pointI, pointJ); count++; } } return count > 0 ? totalDistance / count : 0; } /** * Compute spacing metric */ private computeSpacing(): number { if (this.frontier.length < 2) return 0; const distances: number[] = []; for (let i = 0; i < this.frontier.length; i++) { let minDist = Infinity; for (let j = 0; j < this.frontier.length; j++) { if (i !== j) { const pointI = this.frontier[i]; const pointJ = this.frontier[j]; if (!pointI || !pointJ) continue; const dist = this.computeDistance(pointI, pointJ); minDist = Math.min(minDist, dist); } } if (minDist !== Infinity) { distances.push(minDist); } } if (distances.length === 0) return 0; const mean = distances.reduce((a, b) => a + b, 0) / distances.length; const variance = distances.reduce((a, b) => a + Math.pow(b - mean, 2), 0) / distances.length; return Math.sqrt(variance); } /** * Compute spread metric */ private computeSpread(): number { if (this.frontier.length < 2) return 0; let maxSpread = 0; for (const obj of this.config.objectives) { const values = this.frontier.map(point => point.objectives.get(obj.name) || 0) .filter(v => !isNaN(v)); if (values.length > 1) { const range = Math.max(...values) - Math.min(...values); maxSpread = Math.max(maxSpread, range); } } return maxSpread; } /** * Compute generational distance metric */ private computeGenerationalDistance(): number { // Simplified implementation - in practice would compare against true Pareto front return Math.random() * 0.1; // Placeholder } /** * Compute distance between two Pareto points */ private computeDistance(a: ParetoPoint, b: ParetoPoint): number { let sumSquares = 0; for (const obj of this.config.objectives) { const aValue = a.objectives.get(obj.name) || 0; const bValue = b.objectives.get(obj.name) || 0; if (!isNaN(aValue) && !isNaN(bValue)) { sumSquares += Math.pow(aValue - bValue, 2); } } return Math.sqrt(sumSquares); } /** * Get cached objectives for a candidate */ private getCachedObjectives(candidate: PromptCandidate): Map<string, number> { let objectives = this.objectiveCache.get(candidate.id); if (!objectives) { objectives = new Map(); this.config.objectives.forEach(obj => { try { const value = obj.extractor(candidate); objectives!.set(obj.name, isNaN(value) ? 0 : value); } catch { objectives!.set(obj.name, 0); } }); this.objectiveCache.set(candidate.id, objectives); } return objectives; } /** * Check if two objective maps are identical */ private areObjectivesIdentical(a: Map<string, number>, b: Map<string, number>): boolean { if (a.size !== b.size) return false; for (const [key, value] of a) { const bValue = b.get(key); if (bValue === undefined || value !== bValue) { return false; } } return true; } /** * Check if convergence metrics should be updated */ private shouldUpdateConvergenceMetrics(): boolean { return Date.now() - this.lastUpdateTimestamp > 10000; // Update every 10 seconds } /** * Get cache key for hypervolume calculation */ private getHypervolumeCacheKey(reference?: Map<string, number>): string { const frontierHash = this.getFrontierHash(); const refHash = reference ? this.hashMap(reference) : 'default'; return `${frontierHash}-${refHash}`; } /** * Compute hypervolume with optimization */ private computeHypervolumeOptimized(referencePoint: Map<string, number>): number { if (this.frontier.length === 0) return 0; // Simplified hypervolume calculation for 2D case if (this.config.objectives.length === 2) { return this.computeHypervolume2D(referencePoint); } // For higher dimensions, use Monte Carlo approximation return this.computeHypervolumeMonteCarloApproximation(referencePoint); } /** * 2D hypervolume calculation */ private computeHypervolume2D(referencePoint: Map<string, number>): number { if (this.frontier.length === 0) return 0; const objectives = this.config.objectives; if (objectives.length !== 2 || !objectives[0] || !objectives[1]) return 0; const obj1 = objectives[0]; const obj2 = objectives[1]; const points = this.frontier.map(point => ({ x: point.objectives.get(obj1.name) || 0, y: point.objectives.get(obj2.name) || 0 })); const refX = referencePoint.get(obj1.name) || 0; const refY = referencePoint.get(obj2.name) || 0; // Sort points for hypervolume calculation points.sort((a, b) => a.x - b.x); let hypervolume = 0; let prevX = refX; for (let i = 0; i < points.length; i++) { const point = points[i]; if (!point) continue; if (point.x > prevX) { hypervolume += (point.x - prevX) * (point.y - refY); prevX = point.x; } } return Math.max(0, hypervolume); } /** * Monte Carlo approximation for higher-dimensional hypervolume */ private computeHypervolumeMonteCarloApproximation(referencePoint: Map<string, number>): number { const sampleCount = 1000; let dominatedCount = 0; for (let i = 0; i < sampleCount; i++) { const randomPoint = this.generateRandomPoint(referencePoint); // Check if random point is dominated by any frontier point for (const frontierPoint of this.frontier) { if (this.isDominatedBy(randomPoint, frontierPoint)) { dominatedCount++; break; } } } const volume = this.calculateTotalVolume(referencePoint); return (dominatedCount / sampleCount) * volume; } /** * Generate random point within the reference space */ private generateRandomPoint(referencePoint: Map<string, number>): Map<string, number> { const point = new Map<string, number>(); for (const obj of this.config.objectives) { const refValue = referencePoint.get(obj.name) || 0; // Generate random value between reference and best possible value const maxValue = Math.max(...this.frontier.map(p => p.objectives.get(obj.name) || 0)); const randomValue = refValue + Math.random() * (maxValue - refValue); point.set(obj.name, randomValue); } return point; } /** * Check if point A is dominated by frontier point B */ private isDominatedBy(pointA: Map<string, number>, frontierPoint: ParetoPoint): boolean { let betterInAny = false; for (const obj of this.config.objectives) { const aValue = pointA.get(obj.name) || 0; const bValue = frontierPoint.objectives.get(obj.name) || 0; if (obj.direction === 'maximize') { if (bValue < aValue) return false; if (bValue > aValue) betterInAny = true; } else { if (bValue > aValue) return false; if (bValue < aValue) betterInAny = true; } } return betterInAny; } /** * Calculate total volume of reference space */ private calculateTotalVolume(referencePoint: Map<string, number>): number { let volume = 1; for (const obj of this.config.objectives) { const refValue = referencePoint.get(obj.name) || 0; const maxValue = Math.max(...this.frontier.map(p => p.objectives.get(obj.name) || 0)); volume *= Math.abs(maxValue - refValue); } return volume; } /** * Compute diversity with sampling for large frontiers */ private computeDiversitySampled(): number { const sampleSize = Math.min(50, this.frontier.length); const sampledIndices = new Set<number>(); // Random sampling while (sampledIndices.size < sampleSize) { sampledIndices.add(Math.floor(Math.random() * this.frontier.length)); } const sampledPoints = Array.from(sampledIndices) .map(i => this.frontier[i]) .filter(point => point !== undefined); let totalDistance = 0; let count = 0; for (let i = 0; i < sampledPoints.length; i++) { const pointI = sampledPoints[i]; if (!pointI) continue; for (let j = i + 1; j < sampledPoints.length; j++) { const pointJ = sampledPoints[j]; if (!pointJ) continue; totalDistance += this.computeDistance(pointI, pointJ); count++; } } return count > 0 ? totalDistance / count : 0; } /** * Get hash of frontier for cache keys */ private getFrontierHash(): string { const sortedIds = this.frontier .map(p => p.candidate.id) .sort() .join(','); return this.hashString(sortedIds); } /** * Hash a map for cache keys */ private hashMap(map: Map<string, number>): string { const entries = Array.from(map.entries()) .sort(([a], [b]) => a.localeCompare(b)) .map(([k, v]) => `${k}:${v}`) .join(','); return this.hashString(entries); } /** * Simple hash function for strings */ private hashString(str: string): string { let hash = 0; for (let i = 0; i < str.length; i++) { const char = str.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; // Convert to 32bit integer } return hash.toString(36); } /** * Setup memory leak detection integration */ private setupMemoryLeakDetection(): void { // Initialize memory leak detection if not already done MemoryLeakIntegration.initialize(); } /** * Estimate memory size of a candidate for leak detection */ private estimateCandidateSize(candidate: PromptCandidate): number { try { // Rough estimation based on JSON serialization const serialized = JSON.stringify(candidate); return serialized.length * 2; // Assuming UTF-16 encoding } catch { // Fallback estimation return 1024; // 1KB default } } /** * Perform memory cleanup and optimization */ async performMemoryCleanup(): Promise<{ freedMemory: number; cleanedObjects: number }> { let freedMemory = 0; let cleanedObjects = 0; // Clean objective cache const oldCacheSize = this.objectiveCache.size; this.objectiveCache.clear(); cleanedObjects += oldCacheSize; // Clean hypervolume cache const oldHypervolumeSize = this.hypervolumeCache.size; this.hypervolumeCache.clear(); cleanedObjects += oldHypervolumeSize; // Clean old convergence history (keep only last 10 entries) if (this.convergenceHistory.length > 10) { const removed = this.convergenceHistory.splice(0, this.convergenceHistory.length - 10); cleanedObjects += removed.length; freedMemory += removed.length * 200; // Rough estimate } // Clean archive if it's too large if (this.archive.length > 1000) { const removed = this.archive.splice(0, this.archive.length - 1000); for (const point of removed) { const candidateSize = this.estimateCandidateSize(point.candidate); freedMemory += candidateSize; MemoryLeakIntegration.trackParetoOperation('remove', point.candidate.id, candidateSize); } cleanedObjects += removed.length; } return { freedMemory, cleanedObjects }; } } /** * Dominance Index for efficient dominance checking */ class DominanceIndex { private points: ParetoPoint[] = []; /** * Check if a point is dominated by any indexed point */ isDominated(point: ParetoPoint): boolean { return this.points.some(existing => this.dominates(existing, point)); } /** * Add a point to the index */ addPoint(point: ParetoPoint): void { this.points.push(point); } /** * Remove points from the index */ removePoints(points: ParetoPoint[]): void { const idsToRemove = new Set(points.map(p => p.candidate.id)); this.points = this.points.filter(p => !idsToRemove.has(p.candidate.id)); } /** * Find points dominated by the given point */ findDominated(point: ParetoPoint): ParetoPoint[] { return this.points.filter(existing => this.dominates(point, existing)); } /** * Check if point a dominates point b (simplified) */ private dominates(a: ParetoPoint, b: ParetoPoint): boolean { // This is a simplified dominance check // In practice, this would use the same logic as the main ParetoFrontier class let betterInAny = false; for (const [objName, aValue] of a.objectives) { const bValue = b.objectives.get(objName) || 0; // Assume maximization for simplicity if (aValue < bValue) return false; if (aValue > bValue) betterInAny = true; } return betterInAny; } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sloth-wq/prompt-auto-optimizer-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server