testing_space / index.html
Pepguy's picture
Update index.html
6529e4e verified
raw
history blame
18.2 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Topological Engine Lab</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.plot.ly/plotly-2.24.1.min.js"></script>
<style>
body { background: #06090e; color: #cbd5e1; font-family: monospace; overflow: hidden; }
.glass { background: rgba(15,23,42,0.96); border: 1px solid #334155; }
#drawer { transition: transform 0.3s ease-in-out; z-index: 100; }
.drawer-hidden { transform: translateY(100%); }
.panel-title { letter-spacing: 0.18em; text-transform: uppercase; }
</style>
</head>
<body class="flex flex-col h-screen overflow-hidden">
<header class="glass p-3 flex justify-between items-center z-10">
<div>
<h1 id="logic-label" class="text-blue-400 font-bold text-xs uppercase">TOPOLOGICAL ENGINE</h1>
<p id="sub-label" class="text-[10px] text-slate-500 mt-1">Ready</p>
</div>
<button onclick="toggleDrawer()" class="bg-blue-600 px-4 py-1 rounded text-white text-xs font-bold">
⚙️ SYSTEM DIALS
</button>
</header>
<aside id="drawer" class="fixed inset-x-0 bottom-0 drawer-hidden glass p-6 flex flex-col gap-5 z-50 max-h-[92vh] overflow-y-auto">
<div class="flex justify-between items-center border-b border-slate-700 pb-2">
<span class="text-orange-400 font-bold panel-title text-xs">LABORATORY CONTROLS</span>
<button onclick="toggleDrawer()" class="text-slate-400 text-2xl"></button>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<section class="bg-slate-900 border border-blue-900 p-3 rounded">
<h3 class="text-blue-400 font-bold text-[10px] mb-2 panel-title">1. ARCHITECTURE</h3>
<select id="architecture" class="w-full bg-black border border-slate-700 p-2 text-white rounded">
<option value="additive">Additive</option>
<option value="multiplicative">Multiplicative</option>
<option value="affine">Affine</option>
<option value="bilinear">Bilinear</option>
<option value="gated">Gated</option>
</select>
<select id="coeff_mode" class="w-full mt-2 bg-black border border-slate-700 p-2 text-white rounded">
<option value="single_k">Single K</option>
<option value="triple_k">Triple K</option>
<option value="per_edge_k">Per Edge K</option>
</select>
<select id="topology" class="w-full mt-2 bg-black border border-slate-700 p-2 text-white rounded">
<option value="single_cell">Single Cell</option>
<option value="chain">Chain</option>
<option value="mesh">Mesh</option>
</select>
<select id="mode" class="w-full mt-2 bg-black border border-slate-700 p-2 text-white rounded">
<option value="training">Training</option>
<option value="inference">Inference</option>
</select>
<div class="grid grid-cols-2 gap-2 mt-2">
<input id="num_cells" type="number" min="1" value="4" class="bg-black border border-slate-700 p-2 text-white rounded" />
<input id="batch_count" type="number" min="1" value="24" class="bg-black border border-slate-700 p-2 text-white rounded" />
</div>
<div class="grid grid-cols-3 gap-2 mt-2">
<input id="learning_rate" type="number" step="0.001" value="0.01" class="bg-black border border-slate-700 p-2 text-white rounded" />
<input id="coupling" type="number" step="0.001" value="0.05" class="bg-black border border-slate-700 p-2 text-white rounded" />
<input id="damping" type="number" step="0.001" value="0.12" class="bg-black border border-slate-700 p-2 text-white rounded" />
</div>
<button onclick="applyConfig()" class="w-full bg-blue-700 mt-3 py-2 font-bold rounded text-white">APPLY PHYSICS</button>
</section>
<section class="bg-slate-900 border border-purple-900 p-3 rounded">
<h3 class="text-purple-400 font-bold text-[10px] mb-2 panel-title">2. DATASET</h3>
<select id="dataset_family" class="w-full bg-black border border-slate-700 p-2 text-white rounded">
<option value="housing">Housing</option>
<option value="subtraction">Subtraction</option>
<option value="multiplication">Multiplication</option>
<option value="symbolic">Symbolic</option>
<option value="mixed">Mixed</option>
</select>
<button onclick="previewExample()" class="w-full bg-purple-700 mt-3 py-2 font-bold rounded text-white">GENERATE EXAMPLE</button>
<button onclick="startBatch()" class="w-full bg-indigo-700 mt-2 py-2 font-bold rounded text-white">START BATCH</button>
<div class="mt-3 p-3 rounded border border-slate-700 bg-black/40 text-[11px] space-y-1">
<div class="text-slate-400 panel-title text-[10px]">EXAMPLE PREVIEW</div>
<div id="example-view" class="text-slate-300">No sample loaded.</div>
</div>
</section>
<section class="bg-slate-900 border border-cyan-900 p-3 rounded">
<h3 class="text-cyan-400 font-bold text-[10px] mb-2 panel-title">3. CUSTOM INPUTS</h3>
<div class="grid grid-cols-3 gap-2">
<input id="custom_a" type="number" step="0.01" value="7" class="bg-black border border-slate-700 p-2 text-white rounded" placeholder="A" />
<input id="custom_b" type="number" step="0.01" value="8" class="bg-black border border-slate-700 p-2 text-white rounded" placeholder="B" />
<input id="custom_c" type="number" step="0.01" class="bg-black border border-slate-700 p-2 text-white rounded" placeholder="C target" />
</div>
<button onclick="runCustom()" class="w-full bg-cyan-700 mt-3 py-2 font-bold rounded text-white">RUN CUSTOM</button>
</section>
<section class="bg-slate-900 border border-red-900 p-3 rounded flex flex-col justify-between">
<h3 class="text-red-400 font-bold text-[10px] mb-2 panel-title">4. CONTROL</h3>
<button onclick="halt()" class="w-full border border-red-500 text-red-400 py-3 rounded font-bold">HALT ENGINE</button>
<div class="mt-3 text-[11px] text-slate-500">
Batch count and configuration are independent. The same data family can be tested under different physics.
</div>
</section>
</div>
</aside>
<main class="flex-grow flex flex-col min-h-0">
<div class="flex flex-col xl:flex-row flex-grow min-h-0">
<div class="flex-1 min-h-[320px]" id="plot"></div>
<div class="xl:w-[34rem] w-full glass border-l border-slate-800 p-4 overflow-y-auto">
<div class="flex justify-between items-center mb-3 border-b border-slate-800 pb-2">
<div>
<div class="text-blue-300 text-[10px] font-bold panel-title">LIVE STATUS</div>
<div id="status-line" class="text-[11px] text-slate-400 mt-1">Waiting...</div>
</div>
<div id="error-val" class="text-sm font-bold text-red-400">ERR: 0.000</div>
</div>
<div id="summary-box" class="grid grid-cols-2 gap-2 text-[11px] mb-4"></div>
<div class="mb-4">
<div class="text-[10px] text-slate-500 panel-title mb-2">CELL STATE</div>
<div id="cells-ui" class="space-y-2 text-[11px] font-mono"></div>
</div>
<div class="mb-4">
<div class="text-[10px] text-slate-500 panel-title mb-2">LOSS HISTORY</div>
<div id="loss-plot" class="h-56"></div>
</div>
<div>
<div class="text-[10px] text-slate-500 panel-title mb-2">LOGS</div>
<div id="logs" class="mt-auto pt-2 border-t border-slate-800 text-[10px] text-slate-500 h-44 overflow-y-auto space-y-1"></div>
</div>
</div>
</div>
</main>
<script>
const state = {
plotted: false,
lossPlotted: false,
example: null,
historyLoss: [],
historyError: []
};
function toggleDrawer() {
document.getElementById('drawer').classList.toggle('drawer-hidden');
}
function readConfig() {
return {
architecture: document.getElementById('architecture').value,
coeff_mode: document.getElementById('coeff_mode').value,
topology: document.getElementById('topology').value,
dataset_family: document.getElementById('dataset_family').value,
mode: document.getElementById('mode').value,
num_cells: parseInt(document.getElementById('num_cells').value || '1', 10),
batch_size: parseInt(document.getElementById('batch_count').value || '24', 10),
learning_rate: parseFloat(document.getElementById('learning_rate').value || '0.01'),
coupling: parseFloat(document.getElementById('coupling').value || '0.05'),
damping: parseFloat(document.getElementById('damping').value || '0.12'),
};
}
function setHeaderFromConfig(cfg) {
document.getElementById('logic-label').innerText =
`TOPOLOGICAL ENGINE | ${cfg.architecture.toUpperCase()} | ${cfg.coeff_mode.toUpperCase()} | ${cfg.topology.toUpperCase()}`;
document.getElementById('sub-label').innerText =
`${cfg.dataset_family.toUpperCase()} / ${cfg.mode.toUpperCase()}`;
}
async function applyConfig() {
const cfg = readConfig();
setHeaderFromConfig(cfg);
await fetch('/config', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(cfg)
});
await previewExample(false);
toggleDrawer();
}
async function previewExample(fillInputs = true) {
const cfg = readConfig();
const res = await fetch('/example', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(cfg)
});
const sample = await res.json();
state.example = sample;
const ex = document.getElementById('example-view');
ex.innerHTML = `
<div class="text-slate-200">a: <b>${sample.a.toFixed(3)}</b></div>
<div class="text-slate-200">b: <b>${sample.b.toFixed(3)}</b></div>
<div class="text-slate-200">c: <b>${sample.c.toFixed(3)}</b></div>
<div class="text-slate-400">label: ${sample.label}</div>
`;
if (fillInputs) {
document.getElementById('custom_a').value = sample.a.toFixed(3);
document.getElementById('custom_b').value = sample.b.toFixed(3);
document.getElementById('custom_c').value = sample.c.toFixed(3);
}
}
async function startBatch() {
const cfg = readConfig();
await fetch('/config', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(cfg)
});
await fetch('/generate_batch', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ count: cfg.batch_size })
});
toggleDrawer();
}
async function runCustom() {
const a = document.getElementById('custom_a').value;
const b = document.getElementById('custom_b').value;
const c = document.getElementById('custom_c').value;
await fetch('/test_custom', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ a, b, c: c === '' ? null : c })
});
toggleDrawer();
}
async function halt() {
await fetch('/halt', { method: 'POST' });
toggleDrawer();
}
function fmt(n) {
if (n === null || n === undefined || Number.isNaN(n)) return '—';
return Number(n).toFixed(4);
}
function renderSummary(d) {
const cfg = d.config;
const sample = d.current_sample || {};
const last = d.last_sample || sample;
const items = [
['Mode', cfg.mode],
['Arch', cfg.architecture],
['Coeffs', cfg.coeff_mode],
['Topology', cfg.topology],
['Dataset', cfg.dataset_family],
['Batch left', d.batch_remaining],
['Iteration', d.iteration],
['Sample', last.label || '—'],
['Error', fmt(d.current_error)],
['Loss', fmt(d.current_loss)],
['A', sample.a !== undefined ? fmt(sample.a) : '—'],
['B', sample.b !== undefined ? fmt(sample.b) : '—'],
];
document.getElementById('summary-box').innerHTML = items.map(([k, v]) => `
<div class="bg-black/40 border border-slate-800 rounded p-2">
<div class="text-[9px] text-slate-500 panel-title">${k}</div>
<div class="text-[12px] text-slate-100 mt-1">${v}</div>
</div>
`).join('');
}
function renderCells(d) {
const cells = d.cells || [];
document.getElementById('cells-ui').innerHTML = cells.map(cell => `
<div class="border border-slate-800 rounded p-3 bg-black/30">
<div class="flex justify-between items-center mb-1">
<div class="text-sky-300 font-bold">CELL ${cell.id} ${cell.anchored ? '[ANCHORED]' : '[DRIFTING]'}</div>
<div class="text-slate-400">label: ${cell.label || '—'}</div>
</div>
<div class="grid grid-cols-2 gap-x-3 gap-y-1">
<div>A: <span class="text-white">${fmt(cell.a)}</span></div>
<div>B: <span class="text-white">${fmt(cell.b)}</span></div>
<div>C: <span class="text-white">${fmt(cell.c)}</span></div>
<div>T: <span class="text-white">${cell.target !== null ? fmt(cell.target) : '—'}</span></div>
<div>P: <span class="text-white">${fmt(cell.prediction)}</span></div>
<div>E: <span class="text-white">${fmt(cell.error)}</span></div>
<div>Energy: <span class="text-white">${fmt(cell.energy)}</span></div>
<div>Force: <span class="text-white">${fmt(cell.force)}</span></div>
</div>
<div class="mt-2 flex gap-3 text-[10px] text-purple-300">
<div>k: ${fmt(cell.k)}</div>
<div>ka: ${fmt(cell.ka)}</div>
<div>kb: ${fmt(cell.kb)}</div>
<div>kc: ${fmt(cell.kc)}</div>
</div>
</div>
`).join('');
}
function renderLogs(d) {
document.getElementById('logs').innerHTML = (d.logs || []).map(l => `<div>${l}</div>`).join('');
}
function renderTopPlot(d) {
const cells = d.cells || [];
const xsPred = cells.map(c => c.prediction);
const ys = cells.map(c => c.id);
const zsErr = cells.map(c => c.error);
const labels = cells.map(c => `cell ${c.id}`);
const xsTarget = cells.map(c => c.target !== null ? c.target : null).filter(v => v !== null);
const ysTarget = cells.map(c => c.target !== null ? c.id : null).filter(v => v !== null);
const zsTarget = cells.map(() => 0).slice(0, xsTarget.length);
const trace1 = {
type: 'scatter3d',
mode: 'lines+markers+text',
x: xsPred,
y: ys,
z: zsErr,
text: labels,
textposition: 'top center',
marker: { size: 7 },
line: { width: 3 }
};
const trace2 = {
type: 'scatter3d',
mode: 'markers',
x: xsTarget,
y: ysTarget,
z: zsTarget,
marker: { size: 5, symbol: 'circle' }
};
const layout = {
margin: { l: 0, r: 0, t: 0, b: 0 },
paper_bgcolor: 'transparent',
plot_bgcolor: 'transparent',
scene: {
xaxis: { title: 'Prediction / C', color: '#475569', gridcolor: '#1e293b' },
yaxis: { title: 'Cell index', color: '#475569', gridcolor: '#1e293b' },
zaxis: { title: 'Error', color: '#475569', gridcolor: '#1e293b' },
camera: { eye: { x: 0.9, y: -1.8, z: 0.8 } }
},
showlegend: false
};
if (!window.topPlotted) {
Plotly.newPlot('plot', [trace1, trace2], layout, { displayModeBar: false, responsive: true });
window.topPlotted = true;
} else {
Plotly.react('plot', [trace1, trace2], layout, { displayModeBar: false, responsive: true });
}
}
function renderLossPlot(d) {
const loss = d.loss_history || [];
const err = d.error_history || [];
const x = loss.map((_, i) => i);
const lossTrace = {
type: 'scatter',
mode: 'lines',
x: x,
y: loss,
name: 'loss'
};
const errTrace = {
type: 'scatter',
mode: 'lines',
x: x,
y: err,
name: 'error'
};
const layout = {
margin: { l: 35, r: 10, t: 10, b: 30 },
paper_bgcolor: 'transparent',
plot_bgcolor: 'transparent',
xaxis: { color: '#94a3b8', gridcolor: '#1e293b' },
yaxis: { color: '#94a3b8', gridcolor: '#1e293b' },
showlegend: true,
legend: { orientation: 'h' }
};
if (!window.lossPlotted) {
Plotly.newPlot('loss-plot', [lossTrace, errTrace], layout, { displayModeBar: false, responsive: true });
window.lossPlotted = true;
} else {
Plotly.react('loss-plot', [lossTrace, errTrace], layout, { displayModeBar: false, responsive: true });
}
}
async function tick() {
try {
const res = await fetch('/state');
const d = await res.json();
document.getElementById('error-val').innerText = 'ERR: ' + fmt(d.current_error);
document.getElementById('error-val').className =
Math.abs(d.current_error) < 0.05 ? 'text-green-400 font-bold' : 'text-red-400 font-bold';
document.getElementById('status-line').innerText =
d.running ? 'Engine running...' : 'Engine idle.';
renderSummary(d);
renderCells(d);
renderLogs(d);
renderTopPlot(d);
renderLossPlot(d);
} catch (e) {
document.getElementById('status-line').innerText = 'Disconnected.';
}
}
// initial example + first config sync
(async () => {
const cfg = readConfig();
setHeaderFromConfig(cfg);
await previewExample(false);
setInterval(tick, 180);
})();
</script>
</body>
</html>