Spaces:
Running
Running
| <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> | |