Absolute_Zero / index.html
eaglelandsonce's picture
Update index.html
ac0a394 verified
Raw
History Blame Contribute Delete
35 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Absolute Zero Reasoner β€” Self-Play Simulation</title>
<style>
:root {
--bg: #07111f;
--panel: rgba(13, 28, 47, 0.82);
--panel-strong: #10233a;
--border: rgba(137, 184, 224, 0.22);
--text: #edf7ff;
--muted: #9cb3c8;
--cyan: #4bd9ff;
--blue: #6d8cff;
--green: #4ce0a0;
--yellow: #ffd166;
--red: #ff6b7a;
--purple: #c68cff;
--shadow: 0 18px 45px rgba(0, 0, 0, 0.28);
}
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
color: var(--text);
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
background:
radial-gradient(circle at 10% 0%, rgba(75,217,255,.16), transparent 30%),
radial-gradient(circle at 95% 10%, rgba(198,140,255,.12), transparent 28%),
linear-gradient(150deg, #050b14 0%, #07111f 48%, #091728 100%);
}
button, input, select { font: inherit; }
button { cursor: pointer; }
.app { max-width: 1500px; margin: 0 auto; padding: 26px; }
.hero {
display: grid;
grid-template-columns: 1fr auto;
gap: 22px;
align-items: start;
margin-bottom: 20px;
}
.eyebrow {
display: inline-flex;
align-items: center;
gap: 8px;
color: var(--cyan);
letter-spacing: .12em;
font-weight: 800;
font-size: 12px;
text-transform: uppercase;
}
.pulse {
width: 9px; height: 9px; border-radius: 50%; background: var(--cyan);
box-shadow: 0 0 18px var(--cyan);
}
h1 { margin: 8px 0 7px; font-size: clamp(30px, 4vw, 54px); line-height: 1.02; }
.subtitle { max-width: 850px; margin: 0; color: var(--muted); font-size: 16px; line-height: 1.6; }
.badge {
border: 1px solid var(--border);
background: rgba(75,217,255,.08);
color: #c9f5ff;
border-radius: 999px;
padding: 9px 14px;
font-weight: 700;
white-space: nowrap;
}
.grid { display: grid; grid-template-columns: 320px 1fr; gap: 18px; }
.panel {
background: var(--panel);
border: 1px solid var(--border);
border-radius: 20px;
box-shadow: var(--shadow);
backdrop-filter: blur(14px);
}
.panel-inner { padding: 18px; }
.panel h2 { margin: 0 0 14px; font-size: 17px; }
.panel h3 { margin: 0 0 10px; font-size: 14px; color: #d9efff; }
.left-stack { display: grid; gap: 16px; align-content: start; }
.controls { display: grid; gap: 14px; }
.control-row { display: grid; gap: 7px; }
.control-row label { color: var(--muted); font-size: 12px; font-weight: 700; display: flex; justify-content: space-between; }
input[type="range"] { width: 100%; accent-color: var(--cyan); }
select {
width: 100%; color: var(--text); background: #0a192a; border: 1px solid var(--border);
border-radius: 10px; padding: 9px 10px;
}
.button-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; }
.btn {
border: 1px solid var(--border); border-radius: 11px; padding: 10px 12px;
background: #10243b; color: var(--text); font-weight: 800;
transition: .18s ease;
}
.btn:hover { transform: translateY(-1px); border-color: rgba(75,217,255,.65); }
.btn.primary { background: linear-gradient(135deg, #2076a4, #4c5fc7); border-color: rgba(129,216,255,.45); }
.btn.danger { color: #ffdce1; background: rgba(255,107,122,.09); }
.btn.wide { grid-column: 1 / -1; }
.metric-list { display: grid; gap: 11px; }
.metric-row { display: grid; grid-template-columns: 92px 1fr 44px; gap: 8px; align-items: center; }
.metric-row span { color: var(--muted); font-size: 12px; }
.bar { height: 8px; background: #07111f; border-radius: 999px; overflow: hidden; border: 1px solid rgba(255,255,255,.06); }
.bar > i { display: block; height: 100%; border-radius: inherit; transition: width .25s ease; }
.ded { background: linear-gradient(90deg, var(--cyan), #3190ff); }
.abd { background: linear-gradient(90deg, var(--purple), #7f75ff); }
.ind { background: linear-gradient(90deg, var(--green), #1da7b5); }
.mini-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; }
.mini-card { padding: 12px; border: 1px solid var(--border); border-radius: 13px; background: rgba(5,13,23,.45); }
.mini-card small { display: block; color: var(--muted); margin-bottom: 4px; }
.mini-card strong { font-size: 21px; }
.main-stack { display: grid; gap: 18px; min-width: 0; }
.flow-panel { padding: 18px; }
.flow {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 10px;
align-items: stretch;
}
.flow-step {
position: relative; min-height: 104px; padding: 13px;
border: 1px solid var(--border); border-radius: 15px;
background: rgba(6,15,26,.58); transition: .2s ease;
}
.flow-step.active {
border-color: rgba(75,217,255,.8);
box-shadow: 0 0 0 1px rgba(75,217,255,.16), 0 0 30px rgba(75,217,255,.13);
transform: translateY(-2px);
}
.flow-step.done { border-color: rgba(76,224,160,.45); }
.flow-number { color: var(--cyan); font-size: 11px; font-weight: 900; letter-spacing: .1em; }
.flow-step strong { display: block; margin: 6px 0 4px; }
.flow-step p { margin: 0; color: var(--muted); font-size: 12px; line-height: 1.45; }
.task-grid { display: grid; grid-template-columns: 1.2fr .8fr; gap: 18px; }
.task-card, .stats-card { padding: 18px; min-width: 0; }
.task-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.type-pill { padding: 7px 10px; border-radius: 999px; font-size: 11px; font-weight: 900; text-transform: uppercase; letter-spacing: .09em; border: 1px solid var(--border); }
.type-deduction { color: #bcefff; background: rgba(75,217,255,.09); }
.type-abduction { color: #ead6ff; background: rgba(198,140,255,.10); }
.type-induction { color: #c8ffe9; background: rgba(76,224,160,.10); }
.difficulty { color: var(--muted); font-size: 12px; }
.code {
min-height: 118px; padding: 15px; border-radius: 14px; overflow: auto;
background: #040a12; border: 1px solid rgba(125,184,230,.18);
color: #dff6ff; font: 13px/1.65 "SFMono-Regular", Consolas, "Liberation Mono", monospace;
white-space: pre-wrap;
}
.qa { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 11px; }
.qa-box { padding: 12px; border: 1px solid var(--border); border-radius: 13px; background: rgba(9,20,34,.58); min-width: 0; }
.qa-box small { color: var(--muted); display: block; margin-bottom: 6px; }
.qa-box div { font-family: "SFMono-Regular", Consolas, monospace; overflow-wrap: anywhere; }
.answer-correct { color: var(--green); }
.answer-wrong { color: var(--red); }
.reward-ring-wrap { display: grid; place-items: center; padding: 4px 0 15px; }
.reward-ring {
--value: 0;
width: 150px; height: 150px; border-radius: 50%;
display: grid; place-items: center;
background: conic-gradient(var(--cyan) calc(var(--value) * 1%), rgba(255,255,255,.08) 0);
position: relative;
}
.reward-ring::after { content: ""; position: absolute; inset: 12px; border-radius: 50%; background: #0b1b2d; }
.ring-text { position: relative; z-index: 1; text-align: center; }
.ring-text strong { display: block; font-size: 31px; }
.ring-text small { color: var(--muted); }
.reward-details { display: grid; gap: 9px; }
.detail-row { display: flex; justify-content: space-between; gap: 16px; color: var(--muted); font-size: 12px; padding-bottom: 8px; border-bottom: 1px solid rgba(255,255,255,.06); }
.detail-row b { color: var(--text); }
.lower-grid { display: grid; grid-template-columns: 1fr .8fr; gap: 18px; }
.chart-panel, .log-panel { padding: 18px; min-width: 0; }
canvas { width: 100%; height: 255px; display: block; border-radius: 12px; background: rgba(4,10,18,.45); }
.legend { display: flex; flex-wrap: wrap; gap: 13px; margin-top: 10px; color: var(--muted); font-size: 11px; }
.legend span::before { content: ""; display: inline-block; width: 9px; height: 9px; border-radius: 50%; margin-right: 6px; background: var(--dot); }
.log { height: 288px; overflow: auto; display: grid; gap: 8px; padding-right: 4px; }
.log-entry { border-left: 3px solid var(--blue); padding: 8px 10px; background: rgba(5,13,23,.48); border-radius: 0 10px 10px 0; font-size: 12px; line-height: 1.45; color: #cfe2f1; }
.log-entry.success { border-left-color: var(--green); }
.log-entry.fail { border-left-color: var(--red); }
.log-entry.propose { border-left-color: var(--purple); }
.log-entry small { color: var(--muted); margin-right: 6px; }
.note { color: var(--muted); font-size: 11px; line-height: 1.5; margin: 12px 0 0; }
.footer { margin-top: 18px; color: #7f96aa; font-size: 11px; text-align: center; }
@media (max-width: 1050px) {
.grid { grid-template-columns: 1fr; }
.left-stack { grid-template-columns: 1fr 1fr; }
.flow { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 760px) {
.app { padding: 16px; }
.hero { grid-template-columns: 1fr; }
.left-stack, .task-grid, .lower-grid { grid-template-columns: 1fr; }
.flow { grid-template-columns: 1fr; }
.qa { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<div class="app">
<header class="hero">
<div>
<div class="eyebrow"><span class="pulse"></span> Reinforced self-play with zero external data</div>
<h1>Absolute Zero Reasoner</h1>
<p class="subtitle">A browser-based simulation of a single model that proposes its own code reasoning tasks, verifies them with an executor, solves them, and uses the resulting rewards to build an increasingly challenging curriculum.</p>
</div>
<div class="badge">Educational AZR Simulation</div>
</header>
<div class="grid">
<aside class="left-stack">
<section class="panel"><div class="panel-inner">
<h2>Simulation Controls</h2>
<div class="controls">
<div class="control-row">
<label><span>Speed</span><b id="speedLabel">650 ms</b></label>
<input id="speed" type="range" min="90" max="1400" value="650" step="10" />
</div>
<div class="control-row">
<label><span>Curriculum edge</span><b id="edgeLabel">+6%</b></label>
<input id="edge" type="range" min="-10" max="22" value="6" step="1" />
</div>
<div class="control-row">
<label><span>Learning rate</span><b id="lrLabel">0.055</b></label>
<input id="learningRate" type="range" min="10" max="100" value="55" step="1" />
</div>
<div class="control-row">
<label for="mode">Reasoning mode</label>
<select id="mode">
<option value="mixed">Mixed curriculum</option>
<option value="deduction">Deduction only</option>
<option value="abduction">Abduction only</option>
<option value="induction">Induction only</option>
</select>
</div>
<div class="button-grid">
<button id="startBtn" class="btn primary">Start</button>
<button id="stepBtn" class="btn">Single Step</button>
<button id="burstBtn" class="btn wide">Run 25 Iterations</button>
<button id="resetBtn" class="btn danger wide">Reset Simulation</button>
</div>
</div>
</div></section>
<section class="panel"><div class="panel-inner">
<h2>Unified Model Skills</h2>
<div class="metric-list">
<div class="metric-row"><span>Deduction</span><div class="bar"><i id="dedBar" class="ded" style="width:24%"></i></div><b id="dedValue">24%</b></div>
<div class="metric-row"><span>Abduction</span><div class="bar"><i id="abdBar" class="abd" style="width:20%"></i></div><b id="abdValue">20%</b></div>
<div class="metric-row"><span>Induction</span><div class="bar"><i id="indBar" class="ind" style="width:16%"></i></div><b id="indValue">16%</b></div>
</div>
<p class="note">The same simulated model plays both proposer and solver. Each skill changes independently as the model encounters tasks in that reasoning mode.</p>
</div></section>
<section class="panel"><div class="panel-inner">
<h2>Training State</h2>
<div class="mini-grid">
<div class="mini-card"><small>Iteration</small><strong id="iteration">0</strong></div>
<div class="mini-card"><small>Buffer</small><strong id="bufferSize">1</strong></div>
<div class="mini-card"><small>Accuracy</small><strong id="accuracy">β€”</strong></div>
<div class="mini-card"><small>Avg. reward</small><strong id="avgReward">β€”</strong></div>
</div>
</div></section>
</aside>
<main class="main-stack">
<section class="panel flow-panel">
<h2>Self-Evolving Training Loop</h2>
<div class="flow">
<div class="flow-step" data-flow="0"><span class="flow-number">01</span><strong>Propose</strong><p>Create a task near the solver’s current capability frontier.</p></div>
<div class="flow-step" data-flow="1"><span class="flow-number">02</span><strong>Validate</strong><p>Execute the generated program and reject invalid tasks.</p></div>
<div class="flow-step" data-flow="2"><span class="flow-number">03</span><strong>Solve</strong><p>Use the same model to reason over the self-generated task.</p></div>
<div class="flow-step" data-flow="3"><span class="flow-number">04</span><strong>Verify</strong><p>Compare the answer with executable ground truth.</p></div>
<div class="flow-step" data-flow="4"><span class="flow-number">05</span><strong>Learn</strong><p>Update proposer and solver behavior from verifiable rewards.</p></div>
</div>
</section>
<div class="task-grid">
<section class="panel task-card">
<div class="task-head">
<div>
<h2 style="margin-bottom:6px">Current Self-Generated Task</h2>
<span id="taskType" class="type-pill type-deduction">Deduction</span>
</div>
<div id="difficulty" class="difficulty">Difficulty 0.00</div>
</div>
<div id="taskPrompt" class="code">Press Start or Single Step to let the model propose its first task.</div>
<div class="qa">
<div class="qa-box"><small>Model answer</small><div id="modelAnswer">β€”</div></div>
<div class="qa-box"><small>Executor ground truth</small><div id="groundTruth">β€”</div></div>
</div>
</section>
<section class="panel stats-card">
<h2>Verifiable Reward</h2>
<div class="reward-ring-wrap">
<div id="rewardRing" class="reward-ring" style="--value:0">
<div class="ring-text"><strong id="rewardValue">0.00</strong><small>combined reward</small></div>
</div>
</div>
<div class="reward-details">
<div class="detail-row"><span>Task validity</span><b id="validityReward">β€”</b></div>
<div class="detail-row"><span>Learnability reward</span><b id="learnabilityReward">β€”</b></div>
<div class="detail-row"><span>Solver accuracy reward</span><b id="solverReward">β€”</b></div>
<div class="detail-row"><span>Predicted success</span><b id="predictedSuccess">β€”</b></div>
</div>
<p class="note">The learnability score is highest when predicted success is near 50%, representing tasks that are neither trivial nor impossible.</p>
</section>
</div>
<div class="lower-grid">
<section class="panel chart-panel">
<h2>Capability Growth</h2>
<canvas id="chart" width="900" height="330"></canvas>
<div class="legend">
<span style="--dot:#4bd9ff">Deduction</span>
<span style="--dot:#c68cff">Abduction</span>
<span style="--dot:#4ce0a0">Induction</span>
<span style="--dot:#ffd166">Rolling accuracy</span>
</div>
</section>
<section class="panel log-panel">
<h2>Training Trace</h2>
<div id="log" class="log"></div>
</section>
</div>
</main>
</div>
<div class="footer">This simulation illustrates the paper’s concepts; it does not reproduce the authors’ neural-network training, TRR++, or benchmark results.</div>
</div>
<script>
(() => {
"use strict";
const $ = (id) => document.getElementById(id);
const clamp = (v, lo = 0, hi = 1) => Math.max(lo, Math.min(hi, v));
const rand = (a, b) => a + Math.random() * (b - a);
const randInt = (a, b) => Math.floor(rand(a, b + 1));
const choice = (arr) => arr[Math.floor(Math.random() * arr.length)];
const sigmoid = (x) => 1 / (1 + Math.exp(-x));
const COLORS = {
deduction: "#4bd9ff",
abduction: "#c68cff",
induction: "#4ce0a0",
accuracy: "#ffd166",
grid: "rgba(190,220,244,.12)",
text: "rgba(211,231,247,.66)"
};
const initialState = () => ({
iteration: 0,
running: false,
busy: false,
timer: null,
skills: { deduction: 0.24, abduction: 0.20, induction: 0.16 },
buffer: [{ type: "seed", program: "f(x) = x", difficulty: 0.01 }],
history: [],
outcomes: [],
rewards: [],
current: null
});
let state = initialState();
function makeProgram(difficulty, invertible = false) {
const d = clamp(difficulty);
const tier = d < 0.3 ? 0 : d < 0.62 ? 1 : 2;
let family;
if (invertible) family = choice(tier === 0 ? ["linear"] : tier === 1 ? ["linear", "affine2"] : ["linear", "affine2", "piecewise"]);
else family = choice(tier === 0 ? ["linear", "square"] : tier === 1 ? ["linear", "square", "mod", "poly"] : ["poly", "mod", "piecewise", "nested"]);
if (family === "linear") {
const a = randInt(1, 2 + Math.round(d * 7));
const b = randInt(-3 - Math.round(d * 8), 3 + Math.round(d * 8));
return { family, label: `f(x) = ${a}x ${b >= 0 ? "+" : "-"} ${Math.abs(b)}`, run: x => a * x + b, params: {a,b} };
}
if (family === "affine2") {
const a = randInt(2, 4 + Math.round(d * 5));
const b = randInt(-8, 8);
const c = randInt(-5, 5);
return { family, label: `f(x) = ${a}(x ${b >= 0 ? "+" : "-"} ${Math.abs(b)}) ${c >= 0 ? "+" : "-"} ${Math.abs(c)}`, run: x => a * (x + b) + c, params: {a,b,c} };
}
if (family === "square") {
const a = randInt(1, 3 + Math.round(d * 4));
const b = randInt(-4, 6);
return { family, label: `f(x) = ${a}xΒ² ${b >= 0 ? "+" : "-"} ${Math.abs(b)}`, run: x => a * x * x + b, params: {a,b} };
}
if (family === "poly") {
const a = randInt(1, 3);
const b = randInt(-5, 5);
const c = randInt(-8, 8);
return { family, label: `f(x) = ${a}xΒ² ${b >= 0 ? "+" : "-"} ${Math.abs(b)}x ${c >= 0 ? "+" : "-"} ${Math.abs(c)}`, run: x => a*x*x + b*x + c, params: {a,b,c} };
}
if (family === "mod") {
const a = randInt(2, 8);
const b = randInt(0, 10);
const m = randInt(5, 13 + Math.round(d * 10));
return { family, label: `f(x) = (${a}x + ${b}) mod ${m}`, run: x => ((a*x+b)%m+m)%m, params: {a,b,m} };
}
if (family === "piecewise") {
const t = randInt(-2, 4);
const a = randInt(2, 5);
const b = randInt(1, 7);
return { family, label: `f(x) = x < ${t} ? ${a}x - ${b} : ${a}x + ${b}`, run: x => x < t ? a*x-b : a*x+b, params: {t,a,b} };
}
const a = randInt(2, 5), b = randInt(1, 8), m = randInt(7, 17);
return { family: "nested", label: `f(x) = ((${a}x + ${b})Β² + x) mod ${m}`, run: x => (((a*x+b)**2+x)%m+m)%m, params: {a,b,m} };
}
function formatExamples(xs, program) {
return xs.map(x => ` ${x} β†’ ${program.run(x)}`).join("\n");
}
function proposeTask(type) {
const skill = state.skills[type];
const edge = Number($("edge").value) / 100;
const target = clamp(skill + edge + rand(-0.09, 0.09), 0.03, 0.98);
const noise = rand(-0.025, 0.025);
const difficulty = clamp(target + noise, 0.02, 0.99);
const span = 5 + Math.round(difficulty * 20);
if (type === "deduction") {
const program = makeProgram(difficulty, false);
const input = randInt(-span, span);
const output = program.run(input);
return {
type, difficulty, program,
prompt: `REASONING MODE: DEDUCTION\n\nProgram:\n ${program.label}\n\nInput:\n x = ${input}\n\nQuestion:\n What output does the program produce?`,
truth: String(output),
input, output,
validate: () => Number.isFinite(output)
};
}
if (type === "abduction") {
const program = makeProgram(difficulty, true);
const hiddenInput = randInt(-span, span);
const output = program.run(hiddenInput);
const validInputs = [];
for (let x = -span - 8; x <= span + 8; x++) if (program.run(x) === output) validInputs.push(x);
return {
type, difficulty, program,
prompt: `REASONING MODE: ABDUCTION\n\nProgram:\n ${program.label}\n\nObserved output:\n y = ${output}\n\nSearch domain:\n ${-span-8} ≀ x ≀ ${span+8}\n\nQuestion:\n Infer one input x that could have produced this output.`,
truth: validInputs.join(" or "),
hiddenInput, output, validInputs,
validate: () => validInputs.length > 0
};
}
const candidateCount = difficulty < .35 ? 3 : difficulty < .7 ? 4 : 5;
const correct = makeProgram(difficulty, false);
const xs = [];
const needed = difficulty < .4 ? 3 : difficulty < .75 ? 4 : 5;
while (xs.length < needed) {
const x = randInt(-span, span);
if (!xs.includes(x)) xs.push(x);
}
const candidates = [correct];
while (candidates.length < candidateCount) {
const p = makeProgram(clamp(difficulty + rand(-.15,.15)), false);
const duplicate = candidates.some(c => c.label === p.label);
if (!duplicate) candidates.push(p);
}
candidates.sort(() => Math.random() - .5);
const labels = "ABCDE";
const correctIndex = candidates.findIndex(c => c === correct);
const optionText = candidates.map((p, i) => ` ${labels[i]}. ${p.label}`).join("\n");
return {
type, difficulty, program: correct, candidates, correctIndex,
prompt: `REASONING MODE: INDUCTION\n\nExamples:\n${formatExamples(xs, correct)}\n\nCandidate programs:\n${optionText}\n\nQuestion:\n Which program best explains the examples?`,
truth: `${labels[correctIndex]}. ${correct.label}`,
xs,
validate: () => xs.every(x => Number.isFinite(correct.run(x)))
};
}
function predictedSuccess(type, difficulty) {
const skill = state.skills[type];
return clamp(sigmoid((skill - difficulty) * 8.5));
}
function solveTask(task, pSuccess) {
const success = Math.random() < pSuccess;
if (success) {
if (task.type === "abduction") return { success: true, answer: String(choice(task.validInputs)) };
return { success: true, answer: task.truth };
}
if (task.type === "deduction") {
const truth = Number(task.truth);
const magnitude = Math.max(1, Math.round(1 + task.difficulty * 8));
let wrong = truth + choice([-1, 1]) * randInt(1, magnitude);
if (wrong === truth) wrong += 1;
return { success: false, answer: String(wrong) };
}
if (task.type === "abduction") {
const domain = 12 + Math.round(task.difficulty * 22);
let wrong = randInt(-domain, domain);
let guard = 0;
while (task.validInputs.includes(wrong) && guard++ < 30) wrong = randInt(-domain, domain);
return { success: false, answer: String(wrong) };
}
const labels = "ABCDE";
const wrongIndices = task.candidates.map((_,i)=>i).filter(i=>i!==task.correctIndex);
const idx = choice(wrongIndices);
return { success: false, answer: `${labels[idx]}. ${task.candidates[idx].label}` };
}
function verifyAnswer(task, answer) {
if (task.type === "deduction") {
const value = Number(String(answer).trim());
return Number.isFinite(value) && value === Number(task.truth);
}
if (task.type === "abduction") {
const value = Number(String(answer).trim());
return Number.isInteger(value) && task.validInputs.includes(value) && task.program.run(value) === task.output;
}
const match = String(answer).trim().match(/^([A-E])\./i);
if (!match) return false;
return "ABCDE".indexOf(match[1].toUpperCase()) === task.correctIndex;
}
function learn(task, pSuccess, success, learnability) {
const lr = Number($("learningRate").value) / 1000;
const old = state.skills[task.type];
const errorSignal = success ? (1 - old) : Math.max(0.12, task.difficulty - old + 0.22);
const frontierBonus = 0.55 + 0.9 * learnability;
const update = lr * errorSignal * frontierBonus * (success ? 1 : 0.52);
state.skills[task.type] = clamp(old + update, 0.01, 0.995);
// Small transfer between reasoning modes, representing shared parameters.
Object.keys(state.skills).forEach(k => {
if (k !== task.type) state.skills[k] = clamp(state.skills[k] + update * 0.035, 0.01, 0.995);
});
return state.skills[task.type] - old;
}
function chooseMode() {
const selected = $("mode").value;
if (selected !== "mixed") return selected;
const modes = ["deduction", "abduction", "induction"];
// Prioritize weaker skills while retaining exploration.
const weights = modes.map(m => (1.08 - state.skills[m]) + 0.22);
const total = weights.reduce((a,b)=>a+b,0);
let r = Math.random() * total;
for (let i=0; i<modes.length; i++) {
r -= weights[i];
if (r <= 0) return modes[i];
}
return modes[2];
}
function setFlow(index) {
document.querySelectorAll(".flow-step").forEach((el, i) => {
el.classList.toggle("active", i === index);
el.classList.toggle("done", i < index);
});
}
function wait(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }
function phaseDelay() { return Math.max(45, Number($("speed").value) / 5.4); }
async function iteration({animate = true} = {}) {
if (state.busy) return;
state.busy = true;
const type = chooseMode();
setFlow(0);
const task = proposeTask(type);
state.current = task;
renderTask(task);
addLog("propose", `Proposed a ${type} task at difficulty ${task.difficulty.toFixed(2)}.`);
if (animate) await wait(phaseDelay());
setFlow(1);
const valid = task.validate();
if (!valid) {
addLog("fail", "Executor rejected the proposed task; no solver update was applied.");
state.busy = false;
return;
}
state.buffer.push(task);
if (state.buffer.length > 180) state.buffer.shift();
if (animate) await wait(phaseDelay());
setFlow(2);
const pSuccess = predictedSuccess(type, task.difficulty);
const result = solveTask(task, pSuccess);
const verifiedCorrect = verifyAnswer(task, result.answer);
$("modelAnswer").textContent = result.answer;
$("modelAnswer").className = verifiedCorrect ? "answer-correct" : "answer-wrong";
if (animate) await wait(phaseDelay());
setFlow(3);
const solverReward = verifiedCorrect ? 1 : 0;
const learnability = 4 * pSuccess * (1 - pSuccess);
const combined = 0.52 * solverReward + 0.38 * learnability + 0.10;
renderRewards({valid, pSuccess, solverReward, learnability, combined});
if (animate) await wait(phaseDelay());
setFlow(4);
const delta = learn(task, pSuccess, verifiedCorrect, learnability);
state.iteration += 1;
state.outcomes.push(solverReward);
state.rewards.push(combined);
if (state.outcomes.length > 240) state.outcomes.shift();
if (state.rewards.length > 240) state.rewards.shift();
const rolling = state.outcomes.slice(-20);
const rollingAccuracy = rolling.length ? rolling.reduce((a,b)=>a+b,0)/rolling.length : 0;
state.history.push({
iteration: state.iteration,
deduction: state.skills.deduction,
abduction: state.skills.abduction,
induction: state.skills.induction,
accuracy: rollingAccuracy
});
if (state.history.length > 140) state.history.shift();
addLog(verifiedCorrect ? "success" : "fail",
`${verifiedCorrect ? "Executor verified correct" : "Executor rejected the answer"}; ${type} skill ${delta >= 0 ? "+" : ""}${(delta*100).toFixed(2)} points. Learnability ${learnability.toFixed(2)}.`);
updateUI();
drawChart();
if (animate) await wait(phaseDelay());
setFlow(-1);
state.busy = false;
}
async function runLoop() {
if (!state.running) return;
await iteration({animate:true});
if (state.running) state.timer = setTimeout(runLoop, Math.max(20, Number($("speed").value) / 9));
}
function toggleStart() {
state.running = !state.running;
$("startBtn").textContent = state.running ? "Pause" : "Start";
$("startBtn").classList.toggle("primary", !state.running);
if (state.running) runLoop();
else if (state.timer) clearTimeout(state.timer);
}
async function runBurst() {
if (state.running || state.busy) return;
$("burstBtn").disabled = true;
$("burstBtn").textContent = "Running…";
for (let i=0; i<25; i++) {
await iteration({animate:false});
await wait(12);
}
$("burstBtn").disabled = false;
$("burstBtn").textContent = "Run 25 Iterations";
}
function renderTask(task) {
const typeName = task.type[0].toUpperCase() + task.type.slice(1);
$("taskType").textContent = typeName;
$("taskType").className = `type-pill type-${task.type}`;
$("difficulty").textContent = `Difficulty ${task.difficulty.toFixed(2)}`;
$("taskPrompt").textContent = task.prompt;
$("modelAnswer").textContent = "Reasoning…";
$("modelAnswer").className = "";
$("groundTruth").textContent = task.truth;
}
function renderRewards({valid, pSuccess, solverReward, learnability, combined}) {
$("validityReward").textContent = valid ? "1.00" : "0.00";
$("learnabilityReward").textContent = learnability.toFixed(2);
$("solverReward").textContent = solverReward.toFixed(2);
$("predictedSuccess").textContent = `${Math.round(pSuccess*100)}%`;
$("rewardValue").textContent = combined.toFixed(2);
$("rewardRing").style.setProperty("--value", Math.round(combined*100));
}
function addLog(kind, text) {
const node = document.createElement("div");
node.className = `log-entry ${kind}`;
node.innerHTML = `<small>#${state.iteration + 1}</small>${text}`;
$("log").prepend(node);
while ($("log").children.length > 55) $("log").removeChild($("log").lastChild);
}
function updateUI() {
const skills = state.skills;
[["ded", skills.deduction], ["abd", skills.abduction], ["ind", skills.induction]].forEach(([prefix, value]) => {
$(`${prefix}Bar`).style.width = `${value*100}%`;
$(`${prefix}Value`).textContent = `${Math.round(value*100)}%`;
});
$("iteration").textContent = state.iteration;
$("bufferSize").textContent = state.buffer.length;
const recent = state.outcomes.slice(-30);
$("accuracy").textContent = recent.length ? `${Math.round(recent.reduce((a,b)=>a+b,0)/recent.length*100)}%` : "β€”";
const r = state.rewards.slice(-30);
$("avgReward").textContent = r.length ? (r.reduce((a,b)=>a+b,0)/r.length).toFixed(2) : "β€”";
}
function drawChart() {
const canvas = $("chart");
const rect = canvas.getBoundingClientRect();
const dpr = window.devicePixelRatio || 1;
canvas.width = Math.max(600, Math.floor(rect.width * dpr));
canvas.height = Math.floor(255 * dpr);
const ctx = canvas.getContext("2d");
ctx.scale(dpr, dpr);
const w = canvas.width / dpr, h = canvas.height / dpr;
const pad = {l:42, r:14, t:16, b:28};
ctx.clearRect(0,0,w,h);
ctx.strokeStyle = COLORS.grid;
ctx.lineWidth = 1;
ctx.fillStyle = COLORS.text;
ctx.font = "11px system-ui";
for (let i=0; i<=4; i++) {
const y = pad.t + (h-pad.t-pad.b) * i/4;
ctx.beginPath(); ctx.moveTo(pad.l,y); ctx.lineTo(w-pad.r,y); ctx.stroke();
const value = Math.round((1-i/4)*100);
ctx.fillText(`${value}%`, 6, y+4);
}
const hist = state.history.length ? state.history : [{iteration:0, deduction:.24, abduction:.20, induction:.16, accuracy:0}];
const minI = hist[0].iteration;
const maxI = Math.max(minI+1, hist[hist.length-1].iteration);
const xFor = it => pad.l + (it-minI)/(maxI-minI)*(w-pad.l-pad.r);
const yFor = v => pad.t + (1-v)*(h-pad.t-pad.b);
const plot = (key, color) => {
ctx.strokeStyle = color; ctx.lineWidth = 2.2; ctx.beginPath();
hist.forEach((p,i) => { const x=xFor(p.iteration), y=yFor(p[key]); i?ctx.lineTo(x,y):ctx.moveTo(x,y); });
ctx.stroke();
};
plot("deduction", COLORS.deduction);
plot("abduction", COLORS.abduction);
plot("induction", COLORS.induction);
plot("accuracy", COLORS.accuracy);
ctx.fillStyle = COLORS.text;
ctx.fillText(String(minI), pad.l-4, h-8);
ctx.textAlign = "right";
ctx.fillText(String(maxI), w-pad.r, h-8);
ctx.textAlign = "left";
}
function reset() {
if (state.timer) clearTimeout(state.timer);
state = initialState();
$("startBtn").textContent = "Start";
$("startBtn").classList.add("primary");
$("taskType").textContent = "Deduction";
$("taskType").className = "type-pill type-deduction";
$("difficulty").textContent = "Difficulty 0.00";
$("taskPrompt").textContent = "Press Start or Single Step to let the model propose its first task.";
$("modelAnswer").textContent = "β€”";
$("modelAnswer").className = "";
$("groundTruth").textContent = "β€”";
$("log").innerHTML = "";
renderRewards({valid:false,pSuccess:0,solverReward:0,learnability:0,combined:0});
$("validityReward").textContent = "β€”";
$("learnabilityReward").textContent = "β€”";
$("solverReward").textContent = "β€”";
$("predictedSuccess").textContent = "β€”";
setFlow(-1);
updateUI();
drawChart();
addLog("propose", "Initialized with a minimal identity-function seed. No external question dataset loaded.");
}
$("startBtn").addEventListener("click", toggleStart);
$("stepBtn").addEventListener("click", () => { if (!state.running) iteration({animate:true}); });
$("burstBtn").addEventListener("click", runBurst);
$("resetBtn").addEventListener("click", reset);
$("speed").addEventListener("input", e => $("speedLabel").textContent = `${e.target.value} ms`);
$("edge").addEventListener("input", e => $("edgeLabel").textContent = `${Number(e.target.value)>=0?"+":""}${e.target.value}%`);
$("learningRate").addEventListener("input", e => $("lrLabel").textContent = (Number(e.target.value)/1000).toFixed(3));
window.addEventListener("resize", drawChart);
reset();
})();
</script>
</body>
</html>