159 lines
5.7 KiB
TypeScript
159 lines
5.7 KiB
TypeScript
|
|
import { Cpu, Layers, Target, TrendingUp, Database, Brain } from 'lucide-react';
|
||
|
|
|
||
|
|
interface OptimizerStatePanelProps {
|
||
|
|
sampler?: string;
|
||
|
|
nTrials: number;
|
||
|
|
completedTrials: number;
|
||
|
|
feaTrials?: number;
|
||
|
|
nnTrials?: number;
|
||
|
|
objectives?: Array<{ name: string; direction: string }>;
|
||
|
|
isMultiObjective: boolean;
|
||
|
|
paretoSize?: number;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function OptimizerStatePanel({
|
||
|
|
sampler = 'TPESampler',
|
||
|
|
nTrials,
|
||
|
|
completedTrials,
|
||
|
|
feaTrials = 0,
|
||
|
|
nnTrials = 0,
|
||
|
|
objectives = [],
|
||
|
|
isMultiObjective,
|
||
|
|
paretoSize = 0
|
||
|
|
}: OptimizerStatePanelProps) {
|
||
|
|
// Determine optimizer phase based on progress
|
||
|
|
const getPhase = () => {
|
||
|
|
if (completedTrials === 0) return 'Initializing';
|
||
|
|
if (completedTrials < 10) return 'Exploration';
|
||
|
|
if (completedTrials < nTrials * 0.5) return 'Exploitation';
|
||
|
|
if (completedTrials < nTrials * 0.9) return 'Refinement';
|
||
|
|
return 'Convergence';
|
||
|
|
};
|
||
|
|
|
||
|
|
const phase = getPhase();
|
||
|
|
|
||
|
|
// Format sampler name for display
|
||
|
|
const formatSampler = (s: string) => {
|
||
|
|
const samplers: Record<string, string> = {
|
||
|
|
'TPESampler': 'TPE (Bayesian)',
|
||
|
|
'NSGAIISampler': 'NSGA-II',
|
||
|
|
'NSGAIIISampler': 'NSGA-III',
|
||
|
|
'CmaEsSampler': 'CMA-ES',
|
||
|
|
'RandomSampler': 'Random',
|
||
|
|
'GridSampler': 'Grid',
|
||
|
|
'QMCSampler': 'Quasi-Monte Carlo'
|
||
|
|
};
|
||
|
|
return samplers[s] || s;
|
||
|
|
};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="bg-dark-750 rounded-lg border border-dark-600 p-4">
|
||
|
|
{/* Header */}
|
||
|
|
<div className="flex items-center gap-2 mb-4">
|
||
|
|
<Cpu className="w-5 h-5 text-primary-400" />
|
||
|
|
<span className="font-semibold text-white">Optimizer State</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Main Stats Grid */}
|
||
|
|
<div className="grid grid-cols-2 gap-3 mb-4">
|
||
|
|
{/* Sampler */}
|
||
|
|
<div className="bg-dark-700 rounded-lg p-3">
|
||
|
|
<div className="flex items-center gap-2 mb-1">
|
||
|
|
<Target className="w-4 h-4 text-dark-400" />
|
||
|
|
<span className="text-xs text-dark-400 uppercase">Sampler</span>
|
||
|
|
</div>
|
||
|
|
<div className="text-sm font-medium text-white truncate" title={sampler}>
|
||
|
|
{formatSampler(sampler)}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Phase */}
|
||
|
|
<div className="bg-dark-700 rounded-lg p-3">
|
||
|
|
<div className="flex items-center gap-2 mb-1">
|
||
|
|
<TrendingUp className="w-4 h-4 text-dark-400" />
|
||
|
|
<span className="text-xs text-dark-400 uppercase">Phase</span>
|
||
|
|
</div>
|
||
|
|
<div className={`text-sm font-medium ${
|
||
|
|
phase === 'Convergence' ? 'text-green-400' :
|
||
|
|
phase === 'Refinement' ? 'text-blue-400' :
|
||
|
|
phase === 'Exploitation' ? 'text-yellow-400' :
|
||
|
|
'text-primary-400'
|
||
|
|
}`}>
|
||
|
|
{phase}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* FEA vs NN Trials (for hybrid optimizations) */}
|
||
|
|
{(feaTrials > 0 || nnTrials > 0) && (
|
||
|
|
<div className="mb-4">
|
||
|
|
<div className="text-xs text-dark-400 uppercase mb-2">Trial Sources</div>
|
||
|
|
<div className="flex gap-2">
|
||
|
|
<div className="flex-1 bg-dark-700 rounded-lg p-2 text-center">
|
||
|
|
<Database className="w-4 h-4 text-blue-400 mx-auto mb-1" />
|
||
|
|
<div className="text-lg font-bold text-blue-400">{feaTrials}</div>
|
||
|
|
<div className="text-xs text-dark-400">FEA</div>
|
||
|
|
</div>
|
||
|
|
<div className="flex-1 bg-dark-700 rounded-lg p-2 text-center">
|
||
|
|
<Brain className="w-4 h-4 text-purple-400 mx-auto mb-1" />
|
||
|
|
<div className="text-lg font-bold text-purple-400">{nnTrials}</div>
|
||
|
|
<div className="text-xs text-dark-400">Neural Net</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
{nnTrials > 0 && (
|
||
|
|
<div className="mt-2 text-xs text-dark-400 text-center">
|
||
|
|
{((nnTrials / (feaTrials + nnTrials)) * 100).toFixed(0)}% acceleration from surrogate
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
|
||
|
|
{/* Objectives */}
|
||
|
|
{objectives.length > 0 && (
|
||
|
|
<div className="mb-4">
|
||
|
|
<div className="flex items-center gap-2 mb-2">
|
||
|
|
<Layers className="w-4 h-4 text-dark-400" />
|
||
|
|
<span className="text-xs text-dark-400 uppercase">
|
||
|
|
{isMultiObjective ? 'Multi-Objective' : 'Single Objective'}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
<div className="space-y-1">
|
||
|
|
{objectives.slice(0, 3).map((obj, idx) => (
|
||
|
|
<div
|
||
|
|
key={idx}
|
||
|
|
className="flex items-center justify-between text-sm bg-dark-700 rounded px-2 py-1"
|
||
|
|
>
|
||
|
|
<span className="text-dark-300 truncate" title={obj.name}>
|
||
|
|
{obj.name.length > 20 ? obj.name.slice(0, 18) + '...' : obj.name}
|
||
|
|
</span>
|
||
|
|
<span className={`text-xs px-1.5 py-0.5 rounded ${
|
||
|
|
obj.direction === 'minimize' ? 'bg-green-900/50 text-green-400' : 'bg-blue-900/50 text-blue-400'
|
||
|
|
}`}>
|
||
|
|
{obj.direction === 'minimize' ? 'min' : 'max'}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
{objectives.length > 3 && (
|
||
|
|
<div className="text-xs text-dark-500 text-center">
|
||
|
|
+{objectives.length - 3} more
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
|
||
|
|
{/* Pareto Front Size (for multi-objective) */}
|
||
|
|
{isMultiObjective && paretoSize > 0 && (
|
||
|
|
<div className="pt-3 border-t border-dark-600">
|
||
|
|
<div className="flex items-center justify-between">
|
||
|
|
<span className="text-xs text-dark-400">Pareto Front Size</span>
|
||
|
|
<span className="text-sm font-medium text-primary-400">
|
||
|
|
{paretoSize} solutions
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|