feat: Major update with validators, skills, dashboard, and docs reorganization
- Add validation framework (config, model, results, study validators) - Add Claude Code skills (create-study, run-optimization, generate-report, troubleshoot, analyze-model) - Add Atomizer Dashboard (React frontend + FastAPI backend) - Reorganize docs into structured directories (00-09) - Add neural surrogate modules and training infrastructure - Add multi-objective optimization support 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
import { Card } from '../common/Card';
|
||||
|
||||
interface ParallelCoordinatesPlotProps {
|
||||
data: any[];
|
||||
dimensions: string[];
|
||||
colorBy?: string;
|
||||
}
|
||||
|
||||
export const ParallelCoordinatesPlot = ({ data, dimensions }: ParallelCoordinatesPlotProps) => {
|
||||
// Filter out null/undefined data points
|
||||
const validData = data.filter(d => d && dimensions.every(dim => d[dim] !== null && d[dim] !== undefined));
|
||||
|
||||
if (validData.length === 0 || dimensions.length === 0) {
|
||||
return (
|
||||
<Card title="Parallel Coordinates">
|
||||
<div className="h-80 flex items-center justify-center text-dark-300">
|
||||
No data available for parallel coordinates
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
// Calculate min/max for each dimension for normalization
|
||||
const ranges = dimensions.map(dim => {
|
||||
const values = validData.map(d => d[dim]);
|
||||
return {
|
||||
min: Math.min(...values),
|
||||
max: Math.max(...values)
|
||||
};
|
||||
});
|
||||
|
||||
// Normalize function
|
||||
const normalize = (value: number, dimIdx: number): number => {
|
||||
const range = ranges[dimIdx];
|
||||
if (range.max === range.min) return 0.5;
|
||||
return (value - range.min) / (range.max - range.min);
|
||||
};
|
||||
|
||||
// Chart dimensions
|
||||
const width = 800;
|
||||
const height = 400;
|
||||
const margin = { top: 80, right: 20, bottom: 40, left: 20 };
|
||||
const plotWidth = width - margin.left - margin.right;
|
||||
const plotHeight = height - margin.top - margin.bottom;
|
||||
|
||||
const axisSpacing = plotWidth / (dimensions.length - 1);
|
||||
|
||||
return (
|
||||
<Card title={`Parallel Coordinates (${validData.length} solutions)`}>
|
||||
<svg width={width} height={height} className="overflow-visible">
|
||||
<g transform={`translate(${margin.left}, ${margin.top})`}>
|
||||
{/* Draw axes */}
|
||||
{dimensions.map((dim, i) => {
|
||||
const x = i * axisSpacing;
|
||||
return (
|
||||
<g key={dim} transform={`translate(${x}, 0)`}>
|
||||
{/* Axis line */}
|
||||
<line
|
||||
y1={0}
|
||||
y2={plotHeight}
|
||||
stroke="#475569"
|
||||
strokeWidth={2}
|
||||
/>
|
||||
|
||||
{/* Axis label */}
|
||||
<text
|
||||
y={-10}
|
||||
textAnchor="middle"
|
||||
fill="#94a3b8"
|
||||
fontSize={12}
|
||||
className="select-none"
|
||||
transform={`rotate(-45, 0, -10)`}
|
||||
>
|
||||
{dim}
|
||||
</text>
|
||||
|
||||
{/* Min/max labels */}
|
||||
<text
|
||||
y={plotHeight + 15}
|
||||
textAnchor="middle"
|
||||
fill="#64748b"
|
||||
fontSize={10}
|
||||
>
|
||||
{ranges[i].min.toFixed(2)}
|
||||
</text>
|
||||
<text
|
||||
y={-25}
|
||||
textAnchor="middle"
|
||||
fill="#64748b"
|
||||
fontSize={10}
|
||||
>
|
||||
{ranges[i].max.toFixed(2)}
|
||||
</text>
|
||||
</g>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* Draw lines for each trial */}
|
||||
{validData.map((trial, trialIdx) => {
|
||||
// Build path
|
||||
const pathData = dimensions.map((dim, i) => {
|
||||
const x = i * axisSpacing;
|
||||
const normalizedY = normalize(trial[dim], i);
|
||||
const y = plotHeight * (1 - normalizedY);
|
||||
return i === 0 ? `M ${x} ${y}` : `L ${x} ${y}`;
|
||||
}).join(' ');
|
||||
|
||||
return (
|
||||
<path
|
||||
key={trialIdx}
|
||||
d={pathData}
|
||||
fill="none"
|
||||
stroke={trial.isPareto !== false ? '#10b981' : '#60a5fa'}
|
||||
strokeWidth={1}
|
||||
opacity={0.4}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="transition-all duration-200"
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
{/* Legend */}
|
||||
<div className="flex gap-6 justify-center mt-4 text-sm">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-8 h-0.5 bg-green-400" />
|
||||
<span className="text-dark-200">Pareto Front</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-8 h-0.5 bg-blue-400" />
|
||||
<span className="text-dark-200">Other Solutions</span>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user