import React, { useState, useEffect, useRef } from 'react'; import { Activity, Settings, Play, Square, Trash2, Layers, Cpu, Zap, Plus, Info, Database, Move, Maximize, Search, ZoomIn, ZoomOut, Clock, AlertTriangle } from 'lucide-react'; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'; // --- BLOCK DEFINITIONS --- const BLOCK_TYPES = { INPUT: { id: 'INPUT', label: 'Data In', category: 'IO', color: 'bg-emerald-500', outputs: 1, inputs: 0, config: { batchSize: 32, seqLen: 128, vocabSize: 5000 } }, EMBED: { id: 'EMBED', label: 'Embedding', category: 'Core', color: 'bg-blue-500', outputs: 1, inputs: 1, config: { dim: 512 } }, ATTN: { id: 'ATTN', label: 'Attention', category: 'Core', color: 'bg-indigo-600', outputs: 1, inputs: 1, config: { heads: 8, headDim: 64, dropout: 0.1 } }, FFN: { id: 'FFN', label: 'Feed Forward', category: 'Core', color: 'bg-blue-600', outputs: 1, inputs: 1, config: { mult: 4, dropout: 0.1 } }, NORM: { id: 'NORM', label: 'Layer Norm', category: 'Core', color: 'bg-slate-500', outputs: 1, inputs: 1, config: { eps: 1e-5 } }, LSTM: { id: 'LSTM', label: 'LSTM', category: 'RNN', color: 'bg-amber-500', outputs: 1, inputs: 1, config: { hidden: 512, layers: 1 } }, MAMBA: { id: 'MAMBA', label: 'Mamba (SSM)', category: 'Modern', color: 'bg-purple-600', outputs: 1, inputs: 1, config: { d_state: 16, d_conv: 4, expand: 2 } }, RWKV: { id: 'RWKV', label: 'RWKV', category: 'Modern', color: 'bg-fuchsia-600', outputs: 1, inputs: 1, config: { mode: 'v5', head_size: 64 } }, RESIDUAL: { id: 'RESIDUAL', label: 'Residual +', category: 'Utility', color: 'bg-cyan-500', outputs: 1, inputs: 2, config: {} }, OUTPUT: { id: 'OUTPUT', label: 'Data Out', category: 'IO', color: 'bg-red-500', outputs: 0, inputs: 1, config: { loss: 'CrossEntropy' } } }; const App = () => { // State for Blocks and Connections const [nodes, setNodes] = useState([ { id: 'n1', type: 'INPUT', x: 50, y: 150, config: BLOCK_TYPES.INPUT.config }, { id: 'n2', type: 'EMBED', x: 250, y: 150, config: BLOCK_TYPES.EMBED.config }, { id: 'n3', type: 'OUTPUT', x: 450, y: 150, config: BLOCK_TYPES.OUTPUT.config }, ]); const [connections, setConnections] = useState([ { id: 'c1', from: 'n1', to: 'n2' }, { id: 'c2', from: 'n2', to: 'n3' } ]); // UI & Viewport State const [viewOffset, setViewOffset] = useState({ x: 0, y: 0 }); const [zoom, setZoom] = useState(1); const [isPanning, setIsPanning] = useState(false); const [panStart, setPanStart] = useState({ x: 0, y: 0 }); const [selectedNodeId, setSelectedNodeId] = useState(null); const [isTraining, setIsTraining] = useState(false); const [trainingData, setTrainingData] = useState([]); const [learningRate, setLearningRate] = useState(0.001); const [maxEpochs, setMaxEpochs] = useState(150); const [simAlert, setSimAlert] = useState(null); const [isDraggingNode, setIsDraggingNode] = useState(null); const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 }); const [connectingFrom, setConnectingFrom] = useState(null); const canvasRef = useRef(null); // --- ACCURATE SIMULATION ENGINE --- const findPath = (startType, endType) => { const startNodes = nodes.filter(n => n.type === startType); const endNodes = nodes.filter(n => n.type === endType); for (const start of startNodes) { let visited = new Set(); let queue = [[start.id, []]]; while (queue.length > 0) { const [currId, path] = queue.shift(); if (visited.has(currId)) continue; visited.add(currId); const currNode = nodes.find(n => n.id === currId); const currentPath = [...path, currNode]; if (currNode.type === endType) return currentPath; const nextConnections = connections.filter(c => c.from === currId); for (const conn of nextConnections) { queue.push([conn.to, currentPath]); } } } return null; }; const runTrainingStep = (epoch) => { // 1. Trace the graph to find the active computation path const activePath = findPath('INPUT', 'OUTPUT'); if (!activePath) { setSimAlert("Broken Computation Path: Input cannot reach Output."); return null; } setSimAlert(null); // 2. Calculate Architectural Metrics let depth = activePath.length; let totalCapacity = 0; let vanishingGradientRisk = 1.0; let bottleneck = Infinity; activePath.forEach(node => { const type = node.type; const cfg = node.config; // Capacity based on hidden dimensions (Parameter scaling) if (cfg.dim) totalCapacity += cfg.dim; if (cfg.hidden) totalCapacity += cfg.hidden; if (cfg.heads) totalCapacity += (cfg.heads * cfg.headDim); // Bottleneck detection (Information Theory) if (cfg.dim) bottleneck = Math.min(bottleneck, cfg.dim); // Gradient stability factors if (type === 'LSTM') vanishingGradientRisk *= 0.95; // LSTM helps but still RNN if (type === 'ATTN' || type === 'MAMBA') vanishingGradientRisk *= 0.99; // Very stable if (type === 'NORM') vanishingGradientRisk = Math.min(1.0, vanishingGradientRisk * 1.5); if (type === 'RESIDUAL') vanishingGradientRisk = Math.min(1.0, vanishingGradientRisk * 1.8); }); // 3. Realistic Training Physics // A. Learning Rate Explosion if (learningRate > 0.04 && Math.random() > 0.95) { setSimAlert("Gradient Explosion: Learning rate too high!"); return { epoch, loss: (Math.random() * 10 + 5).toFixed(4), accuracy: "0.0000" }; } // B. Vanishing Gradients // Probability of effective training decreases with depth unless stabilized const effectiveLearningPower = learningRate * Math.pow(vanishingGradientRisk, depth / 2); // C. Capacity vs Complexity // If bottleneck < 128, the model "underfits" (cannot reach low loss) const floorLoss = bottleneck < 128 ? 1.5 : 0.05; // D. The Loss Function (Log-Space Convergence) const prevLoss = trainingData.length > 0 ? parseFloat(trainingData[trainingData.length-1].loss) : 8.0; // Convergence speed depends on architecture efficiency const delta = (prevLoss - floorLoss) * effectiveLearningPower * 5; const noise = (Math.random() - 0.5) * (0.02 / (epoch + 1)); const newLoss = Math.max(floorLoss, prevLoss - delta + noise); const newAcc = Math.min(0.99, 1 - (newLoss / 8) + (Math.random() * 0.01)); return { epoch, loss: newLoss.toFixed(4), accuracy: newAcc.toFixed(4) }; }; useEffect(() => { let interval; if (isTraining) { interval = setInterval(() => { setTrainingData(prev => { const nextEpoch = prev.length; if (nextEpoch >= maxEpochs) { setIsTraining(false); return prev; } const step = runTrainingStep(nextEpoch); if (!step) { setIsTraining(false); return prev; } return [...prev, step]; }); }, 100); } return () => clearInterval(interval); }, [isTraining, nodes, connections, learningRate, maxEpochs, trainingData]); // --- INTERACTION HANDLERS --- const handleWheel = (e) => { e.preventDefault(); const zoomSpeed = 0.001; const newZoom = Math.min(Math.max(zoom - e.deltaY * zoomSpeed, 0.2), 2); setZoom(newZoom); }; const addNode = (type) => { const newNode = { id: `n${Date.now()}`, type, x: (-viewOffset.x + 100) / zoom, y: (-viewOffset.y + 100) / zoom, config: { ...BLOCK_TYPES[type].config } }; setNodes([...nodes, newNode]); }; const deleteNode = (id) => { setNodes(nodes.filter(n => n.id !== id)); setConnections(connections.filter(c => c.from !== id && c.to !== id)); if (selectedNodeId === id) setSelectedNodeId(null); }; const deleteConnection = (id) => { setConnections(connections.filter(c => c.id !== id)); }; const handleCanvasMouseDown = (e) => { if (e.target === canvasRef.current || e.target.tagName === 'svg' || e.target.id === 'grid-background') { setIsPanning(true); setPanStart({ x: e.clientX - viewOffset.x, y: e.clientY - viewOffset.y }); setSelectedNodeId(null); } }; const handleNodeMouseDown = (e, id) => { e.stopPropagation(); if (connectingFrom) return; setIsDraggingNode(id); const node = nodes.find(n => n.id === id); setDragOffset({ x: e.clientX / zoom - node.x, y: e.clientY / zoom - node.y }); setSelectedNodeId(id); }; const handleMouseMove = (e) => { if (isDraggingNode) { setNodes(nodes.map(n => n.id === isDraggingNode ? { ...n, x: e.clientX / zoom - dragOffset.x, y: e.clientY / zoom - dragOffset.y } : n)); } else if (isPanning) { setViewOffset({ x: e.clientX - panStart.x, y: e.clientY - panStart.y }); } }; const handleMouseUp = () => { setIsDraggingNode(null); setIsPanning(false); }; const startConnection = (e, id) => { e.stopPropagation(); setConnectingFrom(id); }; const endConnection = (e, id) => { e.stopPropagation(); if (connectingFrom && connectingFrom !== id) { if (!connections.some(c => c.from === connectingFrom && c.to === id)) { setConnections([...connections, { id: `c${Date.now()}`, from: connectingFrom, to: id }]); } } setConnectingFrom(null); }; const updateConfig = (key, val) => { setNodes(nodes.map(n => n.id === selectedNodeId ? { ...n, config: { ...n.config, [key]: val } } : n)); }; const resetView = () => { setViewOffset({ x: 0, y: 0 }); setZoom(1); }; const selectedNode = nodes.find(n => n.id === selectedNodeId); // Calculated properties for infinite grid const gridSize = 24 * zoom; const gridOffsetX = viewOffset.x % gridSize; const gridOffsetY = viewOffset.y % gridSize; const currentEpoch = trainingData.length; const trainingProgress = (currentEpoch / maxEpochs) * 100; return (
{/* Header */}

Arch-Sim v2.0 Accurate

LR setLearningRate(parseFloat(e.target.value))} className="w-20 accent-indigo-500" /> {learningRate}
Max Epochs setMaxEpochs(parseInt(e.target.value))} className="w-20 accent-indigo-500" /> {maxEpochs}
{/* Sidebar: Blocks Palette */} {/* Canvas Area */}
{/* Infinite Dot Grid Background */}
{/* Alert Message */} {simAlert && (
{simAlert}
)} {/* Viewport Transform Container */}
{/* Connections SVG */} {connections.map(conn => { const fromNode = nodes.find(n => n.id === conn.from); const toNode = nodes.find(n => n.id === conn.to); if (!fromNode || !toNode) return null; const x1 = fromNode.x + 160; const y1 = fromNode.y + 40; const x2 = toNode.x; const y2 = toNode.y + 40; const dx = (x2 - x1) / 2; return ( { e.stopPropagation(); deleteConnection(conn.id); }} /> ); })} {/* Nodes */} {nodes.map(node => { const type = BLOCK_TYPES[node.type]; const isSelected = selectedNodeId === node.id; return (
handleNodeMouseDown(e, node.id)} className={`absolute w-40 p-4 rounded-xl border-2 transition-all cursor-move select-none pointer-events-auto ${ isSelected ? 'border-indigo-500 bg-slate-800 shadow-[0_0_35px_rgba(99,102,241,0.3)]' : 'border-slate-800 bg-slate-900/90' }`} >
{isSelected && ( )}

{type.label}

{node.id}
{type.inputs > 0 && (
endConnection(e, node.id)} className={`absolute -left-3 top-1/2 -translate-y-1/2 w-6 h-6 rounded-full bg-slate-800 border-2 ${connectingFrom ? 'border-yellow-400 animate-pulse scale-125' : 'border-indigo-500'} flex items-center justify-center hover:bg-indigo-500 cursor-pointer z-30 transition-transform`} >
)} {type.outputs > 0 && (
startConnection(e, node.id)} className="absolute -right-3 top-1/2 -translate-y-1/2 w-6 h-6 rounded-full bg-slate-800 border-2 border-indigo-500 flex items-center justify-center hover:bg-indigo-500 cursor-pointer z-30" >
)}
); })}
{/* Navigation Controls Overlay */}
{/* Right Sidebar: Config & Monitoring */}
); }; export default App;