<p class="hint">A 3ร3 surface code patch. Click a <b>data qubit</b> (circles) to inject an X error (bit-flip) or Z error (phase-flip). Red stabilizers show where violations are detected. Press <b>Decode</b> to apply minimum-weight corrections.</p>
<div id="canvas-wrap">
<svg id="sc" viewBox="0 0 340 340" xmlns="http://www.w3.org/2000/svg"></svg>
</div>
<div class="legend">
<span class="leg-item"><span class="leg-dot data"></span>Data qubit</span>
<span class="leg-item"><span class="leg-sq xtype"></span>X stabilizer</span>
<span class="leg-item"><span class="leg-sq ztype"></span>Z stabilizer</span>
<span class="leg-item"><span class="leg-sq err"></span>Violation</span>
</div>
<div class="mode-row">
<button id="modeX" class="mode active" type="button">Inject X error</button>
<button id="modeZ" class="mode" type="button">Inject Z error</button>
</div>
<div class="status" id="status">Click a data qubit to inject an error.</div>
<div class="btns">
<button id="decode" type="button">Decode</button>
<button id="reset" type="button" class="ghost">Reset</button>
</div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .6rem; line-height: 1.45; }
#canvas-wrap { width: 100%; max-width: 340px; margin: 0 auto; }
#sc { width: 100%; height: auto; display: block; }
.legend { display: flex; flex-wrap: wrap; gap: .4rem .9rem; margin: .5rem 0; font-size: .82rem; align-items: center; }
.leg-item { display: flex; align-items: center; gap: .3rem; }
.leg-dot { width: 12px; height: 12px; border-radius: 50%; background: #d0d6de; border: 1.5px solid #8a9aaa; }
.leg-dot.data { background: #d0d6de; }
.leg-sq { width: 12px; height: 12px; border-radius: 2px; }
.leg-sq.xtype { background: #a8c8f0; border: 1.5px solid #4a90d9; }
.leg-sq.ztype { background: #f0c890; border: 1.5px solid #c8822a; }
.leg-sq.err { background: #e84040; border: 1.5px solid #a81818; }
.mode-row { display: flex; gap: .5rem; margin: .4rem 0; }
.mode { font: 600 13px system-ui; padding: .35rem .8rem; border: 1.5px solid #555; border-radius: 7px; background: #f5f5f5; color: #333; cursor: pointer; }
.mode.active { background: #1d3557; color: #fff; border-color: #1d3557; }
.status { font-size: .95rem; font-weight: 600; margin: .4rem 0; min-height: 1.4em; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
.status.info { color: #555; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
button { font: 600 14px system-ui; padding: .45rem .9rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Surface Code 3ร3 patch stabilizer simulation.
// Data qubits arranged on a 4ร4 grid at even positions (0,2,4 row and col indices).
// X-stabilizers: plaquettes on the interior rows (odd rows), measure XXXX on 4 data qubits.
// Z-stabilizers: plaquettes on the interior cols (odd cols), measure ZZZZ on 4 data qubits.
// For a 3ร3 (distance-3) rotated surface code:
// - 9 data qubits
// - 4 X-stabilizers (blue diamonds)
// - 4 Z-stabilizers (orange squares)
// An X error on qubit q anticommutes with Z stabilizers touching q.
// A Z error on qubit q anticommutes with X stabilizers touching q.
// โโ geometry โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
const CELL = 80; // pixels between data qubits
const OX = 50, OY = 50; // canvas offset
// 9 data qubits: (row, col) in {0,1,2} x {0,1,2}
const DATA = [];
for (let r = 0; r < 3; r++) for (let c = 0; c < 3; c++) DATA.push([r, c]);
// Stabilizers: each is {type:'X'|'Z', qubits:[indices into DATA]}
// In the rotated surface code, X and Z plaquettes tile the interior.
// X stabilizers (face-type, 4-qubit): detect Z errors
// Z stabilizers (vertex-type, 4-qubit): detect X errors
// For a 3ร3 patch we define them explicitly:
const STABS = [
// X stabilizers (blue) โ detect Z errors on their qubits
{ type: 'X', qubits: [0, 1, 3, 4] }, // top-left face
{ type: 'X', qubits: [1, 2, 4, 5] }, // top-right face
{ type: 'X', qubits: [3, 4, 6, 7] }, // bottom-left face
{ type: 'X', qubits: [4, 5, 7, 8] }, // bottom-right face
// Z stabilizers (orange) โ detect X errors on their qubits
{ type: 'Z', qubits: [0, 1, 3, 4] },
{ type: 'Z', qubits: [1, 2, 4, 5] },
{ type: 'Z', qubits: [3, 4, 6, 7] },
{ type: 'Z', qubits: [4, 5, 7, 8] },
];
// In the rotated surface code the X and Z plaquettes are actually on DIFFERENT
// physical locations. Let's use the standard rotated layout:
// X plaquettes are the 4 "face" centers; Z plaquettes are the 4 "vertex" centers.
// Proper assignment (distance-3 rotated surface code):
// X stabs detect Z errors; Z stabs detect X errors.
// We'll place them at distinct positions (the 4 faces and 4 vertices of the grid).
// Re-assign properly:
const STABILIZERS = [
// X stabilizers at face centers (between 4 data qubits)
{ type: 'X', qubits: [0, 1, 3, 4], row: 0.5, col: 0.5 },
{ type: 'X', qubits: [1, 2, 4, 5], row: 0.5, col: 1.5 },
{ type: 'X', qubits: [3, 4, 6, 7], row: 1.5, col: 0.5 },
{ type: 'X', qubits: [4, 5, 7, 8], row: 1.5, col: 1.5 },
// Z stabilizers at face centers โ for the rotated code they share the same
// positions but are conceptually different. For clarity in the visualization
// we offset them slightly or use a rotated code layout where X and Z alternate.
];
// For a more intuitive visualization use the "checkerboard" rotated surface code
// where X-faces and Z-faces tile the grid of plaquettes in an alternating pattern.
// We have 4 interior plaquettes total, alternating X and Z:
const STAB_DEFS = [
{ type: 'X', qubits: [0, 1, 3, 4], pr: 0.5, pc: 0.5 }, // top-left: X
{ type: 'Z', qubits: [1, 2, 4, 5], pr: 0.5, pc: 1.5 }, // top-right: Z
{ type: 'Z', qubits: [3, 4, 6, 7], pr: 1.5, pc: 0.5 }, // bottom-left: Z
{ type: 'X', qubits: [4, 5, 7, 8], pr: 1.5, pc: 1.5 }, // bottom-right: X
];
// X stab detects Z errors; Z stab detects X errors.
// โโ state โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
const xErr = new Array(9).fill(false); // bit-flip errors
const zErr = new Array(9).fill(false); // phase-flip errors
let injectMode = 'X'; // what clicking a qubit does
// โโ SVG helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
const svg = document.getElementById('sc');
const NS = 'http://www.w3.org/2000/svg';
function el(tag, attrs) {
const e = document.createElementNS(NS, tag);
Object.entries(attrs).forEach(([k, v]) => e.setAttribute(k, v));
return e;
}
function px(r, c) { return [OX + c * CELL, OY + r * CELL]; }
// โโ syndrome computation โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function syndrome(stab) {
// X stabilizer anticommutes with Z errors on its qubits
// Z stabilizer anticommutes with X errors on its qubits
let flip = false;
for (const qi of stab.qubits) {
if (stab.type === 'X' && zErr[qi]) flip = !flip;
if (stab.type === 'Z' && xErr[qi]) flip = !flip;
}
return flip; // true = violation
}
// โโ render โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function render() {
svg.innerHTML = '';
// Draw edges between adjacent data qubits
for (let r = 0; r < 3; r++) for (let c = 0; c < 3; c++) {
const qi = r * 3 + c;
const [x1, y1] = px(r, c);
if (c < 2) { const [x2, y2] = px(r, c + 1); svg.appendChild(el('line', { x1, y1, x2, y2, stroke: '#bbb', 'stroke-width': 1.5 })); }
if (r < 2) { const [x2, y2] = px(r + 1, c); svg.appendChild(el('line', { x1, y1, x2, y2, stroke: '#bbb', 'stroke-width': 1.5 })); }
}
// Draw stabilizers as rotated squares (diamonds)
STAB_DEFS.forEach(stab => {
const [cx, cy] = px(stab.pr, stab.pc);
const viol = syndrome(stab);
const fill = viol ? '#e84040' : (stab.type === 'X' ? '#a8c8f0' : '#f0c890');
const stroke = viol ? '#a81818' : (stab.type === 'X' ? '#4a90d9' : '#c8822a');
const sz = 28;
const pts = `${cx},${cy - sz} ${cx + sz},${cy} ${cx},${cy + sz} ${cx - sz},${cy}`;
svg.appendChild(el('polygon', { points: pts, fill, stroke, 'stroke-width': 1.8 }));
const label = el('text', { x: cx, y: cy + 5, 'text-anchor': 'middle', 'font-size': 12, 'font-weight': 'bold', fill: viol ? '#fff' : '#333' });
label.textContent = stab.type;
svg.appendChild(label);
});
// Draw data qubits
DATA.forEach(([r, c], qi) => {
const [cx, cy] = px(r, c);
const hasX = xErr[qi], hasZ = zErr[qi];
let fill = '#d0d6de', stroke = '#8a9aaa';
if (hasX && hasZ) { fill = '#a020f0'; stroke = '#700bb0'; }
else if (hasX) { fill = '#e84040'; stroke = '#a81818'; }
else if (hasZ) { fill = '#4a90d9'; stroke = '#2260a0'; }
const circ = el('circle', { cx, cy, r: 18, fill, stroke, 'stroke-width': 2, style: 'cursor:pointer' });
circ.addEventListener('click', () => injectError(qi));
svg.appendChild(circ);
if (hasX || hasZ) {
const lbl = el('text', { x: cx, y: cy + 5, 'text-anchor': 'middle', 'font-size': 13, 'font-weight': 'bold', fill: '#fff' });
lbl.textContent = hasX && hasZ ? 'Y' : (hasX ? 'X' : 'Z');
lbl.style.pointerEvents = 'none';
svg.appendChild(lbl);
lbl.addEventListener('click', () => injectError(qi));
}
});
}
function injectError(qi) {
if (injectMode === 'X') xErr[qi] = !xErr[qi];
else zErr[qi] = !zErr[qi];
const anyErr = xErr.some(Boolean) || zErr.some(Boolean);
const anyViol = STAB_DEFS.some(s => syndrome(s));
const status = document.getElementById('status');
if (!anyErr) { status.textContent = 'No errors โ logical qubit is protected.'; status.className = 'status ok'; }
else if (anyViol) { status.textContent = 'Syndrome detected! Red stabilizers show violations.'; status.className = 'status bad'; }
else { status.textContent = 'Errors injected โ but no syndrome yet (logical error?).'; status.className = 'status info'; }
render();
}
// Iterative greedy decoder: repeatedly correct the first violated stabilizer
// until no syndrome remains or we hit a fixed-point (logical error).
// A single pass can create new violations; iterating handles that.
function decode() {
const fixed = [];
for (let pass = 0; pass < 10; pass++) {
let changed = false;
STAB_DEFS.forEach(stab => {
if (syndrome(stab)) {
const qi = stab.qubits[0];
if (stab.type === 'X') { zErr[qi] = !zErr[qi]; fixed.push(fmt('Z on q%qi%', { qi: qi })); }
else { xErr[qi] = !xErr[qi]; fixed.push(fmt('X on q%qi%', { qi: qi })); }
changed = true;
}
});
if (!changed) break;
if (STAB_DEFS.every(s => !syndrome(s))) break;
}
const remaining = STAB_DEFS.filter(s => syndrome(s)).length;
const status = document.getElementById('status');
if (remaining === 0) {
status.textContent = fixed.length ? fmt('Corrected! Applied: %fixed%.', { fixed: fixed.join(', ') }) : 'No syndrome โ nothing to correct.';
status.className = 'status ok';
console.log('Decoder applied:', fixed.length ? fixed.join(', ') : 'nothing');
} else {
status.textContent = 'Residual syndrome after decoding โ likely a logical error.';
status.className = 'status bad';
console.log('Residual syndrome after decode');
}
render();
}
function reset() {
xErr.fill(false); zErr.fill(false);
document.getElementById('status').textContent = 'Click a data qubit to inject an error.';
document.getElementById('status').className = 'status';
render();
}
// โโ mode buttons โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
document.getElementById('modeX').addEventListener('click', () => {
injectMode = 'X';
document.getElementById('modeX').classList.add('active');
document.getElementById('modeZ').classList.remove('active');
});
document.getElementById('modeZ').addEventListener('click', () => {
injectMode = 'Z';
document.getElementById('modeZ').classList.add('active');
document.getElementById('modeX').classList.remove('active');
});
document.getElementById('decode').addEventListener('click', decode);
document.getElementById('reset').addEventListener('click', reset);
render();
<p class="hint">A 3ร3 surface code patch. Click a <b>data qubit</b> (circles) to inject an X error (bit-flip) or Z error (phase-flip). Red stabilizers show where violations are detected. Press <b>Decode</b> to apply minimum-weight corrections.</p>
<div id="canvas-wrap">
<svg id="sc" viewBox="0 0 340 340" xmlns="http://www.w3.org/2000/svg"></svg>
</div>
<div class="legend">
<span class="leg-item"><span class="leg-dot data"></span>Data qubit</span>
<span class="leg-item"><span class="leg-sq xtype"></span>X stabilizer</span>
<span class="leg-item"><span class="leg-sq ztype"></span>Z stabilizer</span>
<span class="leg-item"><span class="leg-sq err"></span>Violation</span>
</div>
<div class="mode-row">
<button id="modeX" class="mode active" type="button">Inject X error</button>
<button id="modeZ" class="mode" type="button">Inject Z error</button>
</div>
<div class="status" id="status">Click a data qubit to inject an error.</div>
<div class="btns">
<button id="decode" type="button">Decode</button>
<button id="reset" type="button" class="ghost">Reset</button>
</div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .6rem; line-height: 1.45; }
#canvas-wrap { width: 100%; max-width: 340px; margin: 0 auto; }
#sc { width: 100%; height: auto; display: block; }
.legend { display: flex; flex-wrap: wrap; gap: .4rem .9rem; margin: .5rem 0; font-size: .82rem; align-items: center; }
.leg-item { display: flex; align-items: center; gap: .3rem; }
.leg-dot { width: 12px; height: 12px; border-radius: 50%; background: #d0d6de; border: 1.5px solid #8a9aaa; }
.leg-dot.data { background: #d0d6de; }
.leg-sq { width: 12px; height: 12px; border-radius: 2px; }
.leg-sq.xtype { background: #a8c8f0; border: 1.5px solid #4a90d9; }
.leg-sq.ztype { background: #f0c890; border: 1.5px solid #c8822a; }
.leg-sq.err { background: #e84040; border: 1.5px solid #a81818; }
.mode-row { display: flex; gap: .5rem; margin: .4rem 0; }
.mode { font: 600 13px system-ui; padding: .35rem .8rem; border: 1.5px solid #555; border-radius: 7px; background: #f5f5f5; color: #333; cursor: pointer; }
.mode.active { background: #1d3557; color: #fff; border-color: #1d3557; }
.status { font-size: .95rem; font-weight: 600; margin: .4rem 0; min-height: 1.4em; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
.status.info { color: #555; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
button { font: 600 14px system-ui; padding: .45rem .9rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Surface Code 3ร3 patch stabilizer simulation.
// Data qubits arranged on a 4ร4 grid at even positions (0,2,4 row and col indices).
// X-stabilizers: plaquettes on the interior rows (odd rows), measure XXXX on 4 data qubits.
// Z-stabilizers: plaquettes on the interior cols (odd cols), measure ZZZZ on 4 data qubits.
// For a 3ร3 (distance-3) rotated surface code:
// - 9 data qubits
// - 4 X-stabilizers (blue diamonds)
// - 4 Z-stabilizers (orange squares)
// An X error on qubit q anticommutes with Z stabilizers touching q.
// A Z error on qubit q anticommutes with X stabilizers touching q.
// โโ geometry โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
const CELL = 80; // pixels between data qubits
const OX = 50, OY = 50; // canvas offset
// 9 data qubits: (row, col) in {0,1,2} x {0,1,2}
const DATA = [];
for (let r = 0; r < 3; r++) for (let c = 0; c < 3; c++) DATA.push([r, c]);
// Stabilizers: each is {type:'X'|'Z', qubits:[indices into DATA]}
// In the rotated surface code, X and Z plaquettes tile the interior.
// X stabilizers (face-type, 4-qubit): detect Z errors
// Z stabilizers (vertex-type, 4-qubit): detect X errors
// For a 3ร3 patch we define them explicitly:
const STABS = [
// X stabilizers (blue) โ detect Z errors on their qubits
{ type: 'X', qubits: [0, 1, 3, 4] }, // top-left face
{ type: 'X', qubits: [1, 2, 4, 5] }, // top-right face
{ type: 'X', qubits: [3, 4, 6, 7] }, // bottom-left face
{ type: 'X', qubits: [4, 5, 7, 8] }, // bottom-right face
// Z stabilizers (orange) โ detect X errors on their qubits
{ type: 'Z', qubits: [0, 1, 3, 4] },
{ type: 'Z', qubits: [1, 2, 4, 5] },
{ type: 'Z', qubits: [3, 4, 6, 7] },
{ type: 'Z', qubits: [4, 5, 7, 8] },
];
// In the rotated surface code the X and Z plaquettes are actually on DIFFERENT
// physical locations. Let's use the standard rotated layout:
// X plaquettes are the 4 "face" centers; Z plaquettes are the 4 "vertex" centers.
// Proper assignment (distance-3 rotated surface code):
// X stabs detect Z errors; Z stabs detect X errors.
// We'll place them at distinct positions (the 4 faces and 4 vertices of the grid).
// Re-assign properly:
const STABILIZERS = [
// X stabilizers at face centers (between 4 data qubits)
{ type: 'X', qubits: [0, 1, 3, 4], row: 0.5, col: 0.5 },
{ type: 'X', qubits: [1, 2, 4, 5], row: 0.5, col: 1.5 },
{ type: 'X', qubits: [3, 4, 6, 7], row: 1.5, col: 0.5 },
{ type: 'X', qubits: [4, 5, 7, 8], row: 1.5, col: 1.5 },
// Z stabilizers at face centers โ for the rotated code they share the same
// positions but are conceptually different. For clarity in the visualization
// we offset them slightly or use a rotated code layout where X and Z alternate.
];
// For a more intuitive visualization use the "checkerboard" rotated surface code
// where X-faces and Z-faces tile the grid of plaquettes in an alternating pattern.
// We have 4 interior plaquettes total, alternating X and Z:
const STAB_DEFS = [
{ type: 'X', qubits: [0, 1, 3, 4], pr: 0.5, pc: 0.5 }, // top-left: X
{ type: 'Z', qubits: [1, 2, 4, 5], pr: 0.5, pc: 1.5 }, // top-right: Z
{ type: 'Z', qubits: [3, 4, 6, 7], pr: 1.5, pc: 0.5 }, // bottom-left: Z
{ type: 'X', qubits: [4, 5, 7, 8], pr: 1.5, pc: 1.5 }, // bottom-right: X
];
// X stab detects Z errors; Z stab detects X errors.
// โโ state โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
const xErr = new Array(9).fill(false); // bit-flip errors
const zErr = new Array(9).fill(false); // phase-flip errors
let injectMode = 'X'; // what clicking a qubit does
// โโ SVG helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
const svg = document.getElementById('sc');
const NS = 'http://www.w3.org/2000/svg';
function el(tag, attrs) {
const e = document.createElementNS(NS, tag);
Object.entries(attrs).forEach(([k, v]) => e.setAttribute(k, v));
return e;
}
function px(r, c) { return [OX + c * CELL, OY + r * CELL]; }
// โโ syndrome computation โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function syndrome(stab) {
// X stabilizer anticommutes with Z errors on its qubits
// Z stabilizer anticommutes with X errors on its qubits
let flip = false;
for (const qi of stab.qubits) {
if (stab.type === 'X' && zErr[qi]) flip = !flip;
if (stab.type === 'Z' && xErr[qi]) flip = !flip;
}
return flip; // true = violation
}
// โโ render โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function render() {
svg.innerHTML = '';
// Draw edges between adjacent data qubits
for (let r = 0; r < 3; r++) for (let c = 0; c < 3; c++) {
const qi = r * 3 + c;
const [x1, y1] = px(r, c);
if (c < 2) { const [x2, y2] = px(r, c + 1); svg.appendChild(el('line', { x1, y1, x2, y2, stroke: '#bbb', 'stroke-width': 1.5 })); }
if (r < 2) { const [x2, y2] = px(r + 1, c); svg.appendChild(el('line', { x1, y1, x2, y2, stroke: '#bbb', 'stroke-width': 1.5 })); }
}
// Draw stabilizers as rotated squares (diamonds)
STAB_DEFS.forEach(stab => {
const [cx, cy] = px(stab.pr, stab.pc);
const viol = syndrome(stab);
const fill = viol ? '#e84040' : (stab.type === 'X' ? '#a8c8f0' : '#f0c890');
const stroke = viol ? '#a81818' : (stab.type === 'X' ? '#4a90d9' : '#c8822a');
const sz = 28;
const pts = `${cx},${cy - sz} ${cx + sz},${cy} ${cx},${cy + sz} ${cx - sz},${cy}`;
svg.appendChild(el('polygon', { points: pts, fill, stroke, 'stroke-width': 1.8 }));
const label = el('text', { x: cx, y: cy + 5, 'text-anchor': 'middle', 'font-size': 12, 'font-weight': 'bold', fill: viol ? '#fff' : '#333' });
label.textContent = stab.type;
svg.appendChild(label);
});
// Draw data qubits
DATA.forEach(([r, c], qi) => {
const [cx, cy] = px(r, c);
const hasX = xErr[qi], hasZ = zErr[qi];
let fill = '#d0d6de', stroke = '#8a9aaa';
if (hasX && hasZ) { fill = '#a020f0'; stroke = '#700bb0'; }
else if (hasX) { fill = '#e84040'; stroke = '#a81818'; }
else if (hasZ) { fill = '#4a90d9'; stroke = '#2260a0'; }
const circ = el('circle', { cx, cy, r: 18, fill, stroke, 'stroke-width': 2, style: 'cursor:pointer' });
circ.addEventListener('click', () => injectError(qi));
svg.appendChild(circ);
if (hasX || hasZ) {
const lbl = el('text', { x: cx, y: cy + 5, 'text-anchor': 'middle', 'font-size': 13, 'font-weight': 'bold', fill: '#fff' });
lbl.textContent = hasX && hasZ ? 'Y' : (hasX ? 'X' : 'Z');
lbl.style.pointerEvents = 'none';
svg.appendChild(lbl);
lbl.addEventListener('click', () => injectError(qi));
}
});
}
function injectError(qi) {
if (injectMode === 'X') xErr[qi] = !xErr[qi];
else zErr[qi] = !zErr[qi];
const anyErr = xErr.some(Boolean) || zErr.some(Boolean);
const anyViol = STAB_DEFS.some(s => syndrome(s));
const status = document.getElementById('status');
if (!anyErr) { status.textContent = 'No errors โ logical qubit is protected.'; status.className = 'status ok'; }
else if (anyViol) { status.textContent = 'Syndrome detected! Red stabilizers show violations.'; status.className = 'status bad'; }
else { status.textContent = 'Errors injected โ but no syndrome yet (logical error?).'; status.className = 'status info'; }
render();
}
// Iterative greedy decoder: repeatedly correct the first violated stabilizer
// until no syndrome remains or we hit a fixed-point (logical error).
// A single pass can create new violations; iterating handles that.
function decode() {
const fixed = [];
for (let pass = 0; pass < 10; pass++) {
let changed = false;
STAB_DEFS.forEach(stab => {
if (syndrome(stab)) {
const qi = stab.qubits[0];
if (stab.type === 'X') { zErr[qi] = !zErr[qi]; fixed.push(fmt('Z on q%qi%', { qi: qi })); }
else { xErr[qi] = !xErr[qi]; fixed.push(fmt('X on q%qi%', { qi: qi })); }
changed = true;
}
});
if (!changed) break;
if (STAB_DEFS.every(s => !syndrome(s))) break;
}
const remaining = STAB_DEFS.filter(s => syndrome(s)).length;
const status = document.getElementById('status');
if (remaining === 0) {
status.textContent = fixed.length ? fmt('Corrected! Applied: %fixed%.', { fixed: fixed.join(', ') }) : 'No syndrome โ nothing to correct.';
status.className = 'status ok';
console.log('Decoder applied:', fixed.length ? fixed.join(', ') : 'nothing');
} else {
status.textContent = 'Residual syndrome after decoding โ likely a logical error.';
status.className = 'status bad';
console.log('Residual syndrome after decode');
}
render();
}
function reset() {
xErr.fill(false); zErr.fill(false);
document.getElementById('status').textContent = 'Click a data qubit to inject an error.';
document.getElementById('status').className = 'status';
render();
}
// โโ mode buttons โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
document.getElementById('modeX').addEventListener('click', () => {
injectMode = 'X';
document.getElementById('modeX').classList.add('active');
document.getElementById('modeZ').classList.remove('active');
});
document.getElementById('modeZ').addEventListener('click', () => {
injectMode = 'Z';
document.getElementById('modeZ').classList.add('active');
document.getElementById('modeX').classList.remove('active');
});
document.getElementById('decode').addEventListener('click', decode);
document.getElementById('reset').addEventListener('click', reset);
render();
Comments
Loading comments...