Download docs/qsim.html from Snapkitty/cpsc-qft: direct link, hf CLI and curl.
- Browser
- Download file 14.2 kB
-
https://huggingface.co/Snapkitty/cpsc-qft/resolve/main/docs/qsim.html
- Command line
-
hf download hf://Snapkitty/cpsc-qft/docs/qsim.html
-
curl -L -o qsim.html https://huggingface.co/Snapkitty/cpsc-qft/resolve/main/docs/qsim.html
14.2 kB
| <!-- | |
| Copyright (c) 2026 Ahmad Ali Parr and others. | |
| SPDX-License-Identifier: LicenseRef-CPSC-ESCL-1.0 OR AGPL-3.0-only | |
| --> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <title>Mercury — Quantum Circuit Console · CPSC-QFT</title> | |
| <style> | |
| :root { --bg:#0a0e14; --panel:#111722; --line:#1e2a3a; --txt:#d6e2f0; --dim:#7d8ea8; | |
| --acc:#5cc8ff; --ok:#5df08a; --bad:#ff6b6b; --warn:#ffd479; } | |
| * { box-sizing:border-box; } | |
| body { margin:0; background:var(--bg); color:var(--txt); | |
| font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; font-size:13px; } | |
| header { padding:14px 20px; border-bottom:1px solid var(--line); display:flex; | |
| align-items:baseline; gap:14px; flex-wrap:wrap; } | |
| header h1 { margin:0; font-size:17px; letter-spacing:2px; color:var(--acc); } | |
| header span { color:var(--dim); font-size:12px; } | |
| #status { margin-left:auto; font-size:12px; color:var(--warn); } | |
| #status.ready { color:var(--ok); } | |
| main { display:grid; grid-template-columns:minmax(340px,430px) 1fr; gap:14px; padding:14px 20px; } | |
| @media (max-width:900px){ main{ grid-template-columns:1fr; } } | |
| .card { background:var(--panel); border:1px solid var(--line); border-radius:8px; padding:12px; margin-bottom:14px; } | |
| .card h2 { margin:0 0 10px; font-size:12px; letter-spacing:1.5px; color:var(--dim); text-transform:uppercase; } | |
| label { display:block; color:var(--dim); font-size:11px; margin:8px 0 3px; } | |
| input[type=text], input[type=number], textarea, select { | |
| width:100%; background:#0a0e14; color:var(--txt); border:1px solid var(--line); | |
| border-radius:5px; padding:7px 9px; font:inherit; font-size:12.5px; } | |
| textarea { min-height:150px; resize:vertical; line-height:1.5; } | |
| .row { display:flex; gap:8px; } .row > div { flex:1; } | |
| button { background:#16202f; color:var(--txt); border:1px solid var(--line); border-radius:5px; | |
| padding:7px 10px; font:inherit; font-size:12px; cursor:pointer; } | |
| button:hover { border-color:var(--acc); color:var(--acc); } | |
| button.primary { background:#12324a; border-color:var(--acc); color:var(--acc); font-weight:bold; } | |
| button.primary:hover { background:#17405e; } | |
| .palette { display:flex; flex-wrap:wrap; gap:6px; margin-top:6px; } | |
| .palette button { padding:5px 9px; } | |
| .presets { display:flex; flex-wrap:wrap; gap:6px; margin-top:8px; } | |
| pre.diagram { background:#0a0e14; border:1px solid var(--line); border-radius:5px; | |
| padding:10px; overflow-x:auto; font-size:12.5px; line-height:1.7; } | |
| table { width:100%; border-collapse:collapse; font-size:12px; } | |
| th, td { text-align:right; padding:4px 8px; border-bottom:1px solid var(--line); } | |
| th { color:var(--dim); font-weight:normal; } td:first-child, th:first-child { text-align:left; } | |
| .verdict-ok { color:var(--ok); font-weight:bold; } .verdict-bad { color:var(--bad); font-weight:bold; } | |
| .rej { color:var(--bad); } .muted { color:var(--dim); } | |
| canvas { width:100%; height:220px; background:#0a0e14; border:1px solid var(--line); border-radius:5px; } | |
| .kv { display:flex; justify-content:space-between; padding:3px 0; } | |
| .kv b { font-weight:normal; color:var(--dim); } | |
| footer { padding:10px 20px 20px; color:var(--dim); font-size:11px; } | |
| a { color:var(--acc); } | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <h1>MERCURY</h1> | |
| <a href="./" style="color:#7d8ea8;text-decoration:none">site</a> | |
| <span>quantum circuit console · CPSC-QFT frontend · Ruby/WASM statevector simulator</span> | |
| <span id="status">● loading Ruby…</span> | |
| </header> | |
| <main> | |
| <div> | |
| <div class="card"> | |
| <h2>Circuit</h2> | |
| <div class="row"> | |
| <div><label>Qubits (1–12)</label><input type="number" id="n" value="2" min="1" max="12"></div> | |
| <div><label>Initial state (bitstring)</label><input type="text" id="initial" value="00"></div> | |
| </div> | |
| <label>Script — one op per line: <span class="muted">h 0 · rx 1.57 1 · cnot 0 1 · cp 0.79 1 0 · rzz 0.5 0 1 · xx 0 1 · measure 0</span></label> | |
| <textarea id="dsl">h 0 | |
| cnot 0 1</textarea> | |
| <label>Gate palette (appends)</label> | |
| <div class="palette" id="palette"></div> | |
| <label>Presets</label> | |
| <div class="presets"> | |
| <button data-preset="bell">Bell pair</button> | |
| <button data-preset="ghz">GHZ-3</button> | |
| <button data-preset="qft">QFT-3</button> | |
| <button data-preset="trotter">Trotter scattering</button> | |
| <button data-preset="adversarial">Adversarial X</button> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <h2>Run</h2> | |
| <div class="row"> | |
| <div><label>Shots (0 = statevector only)</label><input type="number" id="shots" value="1024" min="0" max="100000"></div> | |
| <div><label>Seed (blank = random)</label><input type="text" id="seed" value="42"></div> | |
| </div> | |
| <label style="margin-top:10px"><input type="checkbox" id="cpsc" checked style="width:auto"> CPSC conservation check</label> | |
| <div class="row"> | |
| <div><label>J (coupling)</label><input type="text" id="pj" value="1.0"></div> | |
| <div><label>λ (transverse field)</label><input type="text" id="plam" value="0.0"></div> | |
| <div><label style="margin-top:22px"><input type="checkbox" id="pmag" style="width:auto"> conserve ΣZ</label></div> | |
| </div> | |
| <div style="margin-top:10px"><button class="primary" id="run" style="width:100%; padding:10px">▶ Run circuit</button></div> | |
| </div> | |
| <div class="card"> | |
| <h2>Conservation (CPSC)</h2> | |
| <div id="cpscOut"><span class="muted">Run a circuit to check symmetry-sector admission.</span></div> | |
| </div> | |
| </div> | |
| <div> | |
| <div class="card"> | |
| <h2>Circuit diagram</h2> | |
| <pre class="diagram" id="diagram"><span class="muted">—</span></pre> | |
| </div> | |
| <div class="card"> | |
| <h2>Measurement histogram</h2> | |
| <canvas id="hist" width="800" height="220"></canvas> | |
| </div> | |
| <div class="card"> | |
| <h2>Statevector (top amplitudes)</h2> | |
| <table><thead><tr><th>basis</th><th>Re</th><th>Im</th><th>P</th></tr></thead> | |
| <tbody id="svBody"><tr><td colspan="4" class="muted">—</td></tr></tbody></table> | |
| </div> | |
| <div class="card"> | |
| <h2>Probabilities</h2> | |
| <table><thead><tr><th>basis</th><th>P</th></tr></thead> | |
| <tbody id="probBody"><tr><td colspan="2" class="muted">—</td></tr></tbody></table> | |
| </div> | |
| </div> | |
| </main> | |
| <footer> | |
| Mercury is a dependency-free Ruby statevector simulator (up to 12 qubits in-browser). The CPSC panel mirrors | |
| <span class="muted">src/cpsc/analyzer.py</span>: gates are admitted only if they preserve the Z<sub>2</sub> parity sector — | |
| a compile-time rejection rule, not post-selection. Trotter preset evolves the transverse-field Ising H under test. | |
| </footer> | |
| <script src="https://cdn.jsdelivr.net/npm/@ruby/wasm-wasi@2.3.0/dist/browser.umd.js"></script> | |
| <script> | |
| /* Mercury WASM boot — same pattern as the Intentsim console. */ | |
| const W = { vm: null, ready: false }; | |
| const RUBY_WASM_URL = "https://cdn.jsdelivr.net/npm/ruby-3_2-wasm-wasi@2.3.0/dist/ruby+stdlib.wasm"; | |
| function setBoot(t, ok) { | |
| const el = document.getElementById("status"); | |
| el.textContent = t; | |
| el.classList.toggle("ready", !!ok); | |
| document.getElementById("run").disabled = !ok; | |
| } | |
| function rbEscape(s) { return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'"); } | |
| function mercuryRun(req) { | |
| const code = `handle_request('${rbEscape(JSON.stringify(req))}')`; | |
| return JSON.parse(W.vm.eval(code).toString()); | |
| } | |
| async function bootRuby() { | |
| try { | |
| if (!window["ruby-wasm-wasi"]) throw new Error("ruby-wasm-wasi bundle missing"); | |
| setBoot("● loading ruby 3.2 webassembly (≈32 mb)…"); | |
| const { DefaultRubyVM } = window["ruby-wasm-wasi"]; | |
| const response = await fetch(RUBY_WASM_URL); | |
| if (!response.ok) throw new Error("wasm fetch failed: " + response.status); | |
| setBoot("● compiling webassembly…"); | |
| const module = await WebAssembly.compile(await response.arrayBuffer()); | |
| setBoot("● starting ruby vm…"); | |
| const { vm } = await DefaultRubyVM(module); | |
| setBoot("● evaluating mercury bundle…"); | |
| const src = await (await fetch("mercury_bundle.rb")).text(); | |
| if (!src.includes("module Mercury")) throw new Error("mercury bundle not found"); | |
| vm.eval(src); | |
| W.vm = vm; W.ready = true; | |
| setBoot("● ruby ready — live simulation", true); | |
| } catch (e) { | |
| setBoot("● wasm unreachable (" + (e.message || e) + ")"); | |
| } | |
| } | |
| const PARAM_N = {rx:1, ry:1, rz:1, cp:1, rzz:1, u:3}; | |
| const GATE_LIST = ["h","x","y","z","s","t","rx","ry","rz","cnot","cz","cp","swap","rzz","xx","ccx"]; | |
| function parseDSL(text) { | |
| const ops = []; | |
| for (const raw of text.split("\n")) { | |
| const line = raw.trim(); | |
| if (!line || line.startsWith("#")) continue; | |
| const t = line.split(/\s+/); | |
| const gate = t[0].toLowerCase(); | |
| let idx = 1, params = []; | |
| const pn = PARAM_N[gate] || 0; | |
| for (let k = 0; k < pn; k++) { | |
| if (idx >= t.length) throw new Error("missing param for " + gate); | |
| const v = parseFloat(t[idx++]); | |
| if (!isFinite(v)) throw new Error("bad param " + t[idx-1]); | |
| params.push(v); | |
| } | |
| const qubits = []; | |
| while (idx < t.length) { | |
| const q = parseInt(t[idx++], 10); | |
| if (!isFinite(q) || q < 0) throw new Error("bad qubit"); | |
| qubits.push(q); | |
| } | |
| ops.push([gate, qubits, params]); | |
| } | |
| return ops; | |
| } | |
| document.getElementById("run").disabled = true; | |
| document.addEventListener("DOMContentLoaded", bootRuby); | |
| // gate palette | |
| const pal = document.getElementById("palette"); | |
| for (const g of GATE_LIST) { | |
| const b = document.createElement("button"); | |
| b.textContent = g.toUpperCase(); | |
| b.onclick = () => { | |
| const dsl = document.getElementById("dsl"); | |
| const pn = PARAM_N[g] || 0; | |
| dsl.value += (dsl.value.endsWith("\n") || dsl.value === "" ? "" : "\n") + g + " " + "0.5 ".repeat(pn) + "0\n"; | |
| dsl.focus(); | |
| }; | |
| pal.appendChild(b); | |
| } | |
| const PRESETS = { | |
| bell: { n:"2", initial:"00", dsl:"h 0\ncnot 0 1" }, | |
| ghz: { n:"3", initial:"000", dsl:"h 0\ncnot 0 1\ncnot 1 2" }, | |
| qft: { n:"3", initial:"000", | |
| dsl:"h 0\ncp 1.5708 1 0\ncp 0.7854 2 0\nh 1\ncp 1.5708 2 1\nh 2\nswap 0 2" }, | |
| trotter:{ n:"4", initial:"0011", lam:"0.0", | |
| dsl:"# Trotter: H = -J ZZ + lam X, J=1, t=0.4, 8 steps\n" + | |
| Array(8).fill( | |
| "rzz -0.1 0 1\nrzz -0.1 1 2\nrzz -0.1 2 3\nrzz -0.1 3 0" | |
| ).join("\n") }, | |
| adversarial: { n:"4", initial:"0000", | |
| dsl:"rzz 0.5 0 1\nx 2\nrzz 0.5 2 3" } | |
| }; | |
| document.querySelectorAll("[data-preset]").forEach(b => b.onclick = () => { | |
| const p = PRESETS[b.dataset.preset]; | |
| document.getElementById("n").value = p.n; | |
| document.getElementById("initial").value = p.initial; | |
| document.getElementById("dsl").value = p.dsl; | |
| if (p.lam !== undefined) document.getElementById("plam").value = p.lam; | |
| }); | |
| document.getElementById("run").onclick = () => { | |
| let req; | |
| try { | |
| const n = parseInt(document.getElementById("n").value, 10); | |
| req = { | |
| n, initial: document.getElementById("initial").value.trim(), | |
| ops: parseDSL(document.getElementById("dsl").value), | |
| shots: parseInt(document.getElementById("shots").value, 10) || 0, | |
| seed: document.getElementById("seed").value.trim() === "" ? null | |
| : parseInt(document.getElementById("seed").value, 10), | |
| cpsc: document.getElementById("cpsc").checked, | |
| magnetization: document.getElementById("pmag").checked, | |
| j: parseFloat(document.getElementById("pj").value) || 0, | |
| lam: parseFloat(document.getElementById("plam").value) || 0 | |
| }; | |
| } catch (e) { alert("Script error: " + e.message); return; } | |
| let out; | |
| try { out = mercuryRun(req); } | |
| catch (e) { alert("Simulator error: " + e.message); return; } | |
| if (out.error) { alert("Simulator error: " + out.error); return; } | |
| render(out); | |
| }; | |
| function render(out) { | |
| document.getElementById("diagram").textContent = out.diagram; | |
| // histogram: prefer counts, fall back to probs | |
| const data = Object.keys(out.counts).length ? out.counts : | |
| Object.fromEntries(out.probs.map(([b, p]) => [b, p])); | |
| drawHist(data); | |
| const svb = document.getElementById("svBody"); svb.innerHTML = ""; | |
| for (const [b, re, im] of out.statevector) { | |
| const p = re*re + im*im; | |
| svb.innerHTML += `<tr><td>|${b}⟩</td><td>${re.toFixed(4)}</td><td>${im.toFixed(4)}</td><td>${p.toFixed(4)}</td></tr>`; | |
| } | |
| const prb = document.getElementById("probBody"); prb.innerHTML = ""; | |
| for (const [b, p] of out.probs) prb.innerHTML += `<tr><td>|${b}⟩</td><td>${p.toFixed(4)}</td></tr>`; | |
| const c = document.getElementById("cpscOut"); | |
| if (out.cpsc) { | |
| const v = out.cpsc; | |
| let html = v.admitted | |
| ? `<div class="verdict-ok">✓ ADMITTED — every gate preserves the symmetry sector</div>` | |
| : `<div class="verdict-bad">✗ REJECTED at compile time</div>`; | |
| for (const r of v.rejections) html += `<div class="rej">· ${r.gate}: ${r.reason}</div>`; | |
| html += `<div class="kv"><b>parity leakage</b><span>${v.leakage.toExponential(2)}</span></div>`; | |
| html += `<div class="kv"><b>⟨H⟩ (J,λ as set)</b><span>${v.energy.toFixed(6)}</span></div>`; | |
| html += `<div class="kv"><b>norm</b><span>${out.norm.toFixed(6)}</span></div>`; | |
| c.innerHTML = html; | |
| } else c.innerHTML = '<span class="muted">CPSC check disabled.</span>'; | |
| } | |
| function drawHist(data) { | |
| const cv = document.getElementById("hist"), ctx = cv.getContext("2d"); | |
| const W = cv.width, H = cv.height; | |
| ctx.clearRect(0, 0, W, H); | |
| const keys = Object.keys(data).sort(); | |
| if (!keys.length) return; | |
| const max = Math.max(...Object.values(data)); | |
| const bw = W / keys.length; | |
| ctx.fillStyle = "#5cc8ff"; ctx.strokeStyle = "#1e2a3a"; | |
| ctx.font = "11px monospace"; ctx.textAlign = "center"; | |
| keys.forEach((k, i) => { | |
| const h = (data[k] / max) * (H - 40); | |
| const x = i * bw; | |
| ctx.fillRect(x + 3, H - 25 - h, bw - 6, h); | |
| ctx.fillStyle = "#7d8ea8"; | |
| ctx.fillText(k, x + bw / 2, H - 10); | |
| ctx.fillText(String(Math.round(data[k] * 100) / 100), x + bw / 2, H - 28 - h); | |
| ctx.fillStyle = "#5cc8ff"; | |
| }); | |
| } | |
| </script> | |
| </body> | |
| </html> | |