greatkoko's picture
Update index.html
08b0e1c verified
Raw History Blame Contribute Delete
48.2 kB
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Quantum Metamorphic Testing Lab</title>
<meta name="description" content="2-ํ๋น„ํŠธ ์–‘์ž ๊ฒŒ์ดํŠธ ์กฐํ•ฉ ๊ธฐ๋ฐ˜ ๋ฉ”ํƒ€๋ชจํ”ฝ ํ…Œ์ŠคํŒ…๊ณผ ๋…ธ์ด์ฆˆ ์˜ํ–ฅ ๋ถ„์„ ๊ต์œก์šฉ ์‹ค์Šต">
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
<style>
:root{
--bg:#f6f7fb; --panel:#ffffff; --ink:#1b2130; --muted:#5d667a; --line:#e2e5ee; --soft:#eef1f8;
--accent:#3b5bdb; --accent-ink:#ffffff; --code:#f1f3f9;
--s00:#2a78d6; --s01:#e3742b; --s10:#2e9e5b; --s11:#d64545;
--m-dep:#2a78d6; --m-ad:#e3742b; --m-pd:#2e9e5b;
--ok:#1f8a4c; --ok-bg:#e3f5ea; --warn:#a86500; --warn-bg:#fff3dc; --bad:#c23434; --bad-bg:#fde7e7;
--gate:#e8edff; --gate-line:#3b5bdb; --basis:#fff1e0; --basis-line:#d07a17;
}
@media (prefers-color-scheme: dark){
:root{
--bg:#11141b; --panel:#1a1f2a; --ink:#e6e9f0; --muted:#9aa3b5; --line:#2c3342; --soft:#222837;
--accent:#7b93ff; --accent-ink:#0d1020; --code:#232a39;
--s00:#5aa2f0; --s01:#f0955a; --s10:#4fc282; --s11:#f07272;
--m-dep:#5aa2f0; --m-ad:#f0955a; --m-pd:#4fc282;
--ok:#5ed38f; --ok-bg:#16301f; --warn:#f2b955; --warn-bg:#352a12; --bad:#f08080; --bad-bg:#3a1a1a;
--gate:#253056; --gate-line:#7b93ff; --basis:#3a2a14; --basis-line:#f0a85a;
}
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);
font-family:"Pretendard","Apple SD Gothic Neo","Malgun Gothic","Noto Sans KR",system-ui,-apple-system,"Segoe UI",sans-serif;
line-height:1.6;font-size:15px}
code,.mono{font-family:ui-monospace,"SFMono-Regular",Menlo,Consolas,monospace;font-size:.92em}
code{background:var(--code);padding:1px 6px;border-radius:5px}
a{color:var(--accent)}
.wrap{max-width:1180px;margin:0 auto;padding:0 16px}
header{padding:28px 0 8px}
header h1{margin:0;font-size:1.65rem;letter-spacing:-.01em}
header p{margin:6px 0 0;color:var(--muted)}
.cite{font-size:.85rem;color:var(--muted);margin-top:8px}
.panel{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:18px}
.controls{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:12px 16px;margin:16px 0}
label.f{display:flex;flex-direction:column;gap:4px;font-size:.82rem;color:var(--muted);font-weight:600}
select,input[type=text],input[type=number]{font:inherit;color:var(--ink);background:var(--panel);border:1px solid var(--line);
border-radius:9px;padding:8px 10px;width:100%}
input[type=range]{width:100%;accent-color:var(--accent)}
.seg{display:flex;border:1px solid var(--line);border-radius:9px;overflow:hidden}
.seg button{flex:1;border:0;background:transparent;color:var(--ink);padding:8px 6px;font:inherit;font-size:.85rem;cursor:pointer}
.seg button+button{border-left:1px solid var(--line)}
.seg button.on{background:var(--accent);color:var(--accent-ink);font-weight:600}
nav.tabs{display:flex;gap:6px;flex-wrap:wrap;border-bottom:1px solid var(--line);margin-top:6px}
nav.tabs button{border:0;background:transparent;color:var(--muted);font:inherit;font-weight:600;padding:10px 14px;cursor:pointer;
border-bottom:3px solid transparent;margin-bottom:-1px}
nav.tabs button.on{color:var(--ink);border-bottom-color:var(--accent)}
section.tab{display:none;padding:18px 0 8px}
section.tab.on{display:block}
.row{display:grid;gap:16px}
.r2{grid-template-columns:1fr 1fr}
.r3{grid-template-columns:repeat(3,1fr)}
@media (max-width:860px){.r2,.r3{grid-template-columns:1fr}}
.card h3{margin:0 0 10px;font-size:1rem}
.chart{position:relative;height:270px;min-width:0}
.chart canvas{max-width:100%}
.panel{min-width:0}
.chart.sm{height:240px}
.muted{color:var(--muted)}
.small{font-size:.85rem}
.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin:12px 0 4px}
.kpi{background:var(--soft);border-radius:10px;padding:10px 12px}
.kpi .k{font-size:.75rem;color:var(--muted);font-weight:600}
.kpi .v{font-size:1.15rem;font-weight:700;font-variant-numeric:tabular-nums}
.badge{display:inline-block;padding:2px 9px;border-radius:999px;font-size:.78rem;font-weight:700;white-space:nowrap}
.b-maintain{background:var(--ok-bg);color:var(--ok)}
.b-uncertain{background:var(--warn-bg);color:var(--warn)}
.b-collapse{background:var(--bad-bg);color:var(--bad)}
.note{border-left:4px solid var(--accent);background:var(--soft);padding:10px 14px;border-radius:0 10px 10px 0;margin:12px 0}
.note.warn{border-left-color:var(--warn)}
.note.bad{border-left-color:var(--bad)}
.tablewrap{overflow-x:auto;margin-top:12px}
table{border-collapse:collapse;width:100%;font-size:.86rem;font-variant-numeric:tabular-nums}
th,td{padding:7px 9px;border-bottom:1px solid var(--line);text-align:right;white-space:nowrap}
th{color:var(--muted);font-weight:600;background:var(--soft);position:sticky;top:0}
td:first-child,th:first-child{text-align:left}
td.l,th.l{text-align:left}
.btn{font:inherit;font-weight:600;border:1px solid var(--line);background:var(--panel);color:var(--ink);border-radius:9px;padding:8px 14px;cursor:pointer}
.btn.primary{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}
.btn:hover{filter:brightness(.97)}
.chips{display:flex;flex-wrap:wrap;gap:6px;margin:8px 0}
.chip{font:inherit;font-size:.82rem;border:1px solid var(--line);background:var(--soft);color:var(--ink);border-radius:999px;padding:4px 11px;cursor:pointer}
.circ{overflow-x:auto}
.circ svg{display:block}
.toolbar{display:flex;gap:8px;flex-wrap:wrap;align-items:center;justify-content:space-between;margin-top:12px}
.learn h2{font-size:1.15rem;margin:22px 0 8px}
.learn h2:first-child{margin-top:0}
.learn table th,.learn table td{text-align:left;white-space:normal}
.formula{font-family:"Cambria Math","Times New Roman",serif;font-size:1.05rem;background:var(--soft);padding:8px 12px;border-radius:8px;display:inline-block;margin:4px 0}
ol.q li{margin-bottom:12px}
ol.q .btn{margin-top:6px;padding:5px 11px;font-size:.82rem}
.err{color:var(--bad);font-weight:600}
footer{color:var(--muted);font-size:.8rem;padding:24px 0 40px}
</style>
</head>
<body>
<div class="wrap">
<header>
<h1>๐Ÿงช Quantum Metamorphic Testing Lab</h1>
<p>์ด์ƒ์  ํ™˜๊ฒฝ์—์„œ ๊ฐ™์€ ์ถœ๋ ฅ ๋ถ„ํฌ(|00โŸฉ 50%, |11โŸฉ 50%)๋ฅผ ๋‚ด๋Š” 2-ํ๋น„ํŠธ ๊ฒŒ์ดํŠธ ์กฐํ•ฉ(TC1~TC6)์— ๋…ธ์ด์ฆˆ๋ฅผ ๊ฐ€ํ•˜๊ณ ,
<b>Chi-Square ยท KL-Divergence ยท Cosine Similarity</b>๋กœ ๋ฉ”ํƒ€๋ชจํ”ฝ ๊ด€๊ณ„(MR)์˜ ์œ ์ง€์™€ ๋ถ•๊ดด๋ฅผ ๊ด€์ฐฐํ•˜๋Š” ๊ต์œก์šฉ ์‹ค์Šต์ž…๋‹ˆ๋‹ค.</p>
<div class="cite"> โ€œGenerating Metamorphic Test Cases using 2-qubit Quantum Gate Combinations,โ€
<i>ํ•œ๊ตญ์ปดํ“จํ„ฐ์ •๋ณดํ•™ํšŒ๋…ผ๋ฌธ์ง€</i>, 31(5), 1โ€“9, 2026.
<a href="https://doi.org/10.9708/jksci.2026.31.05.001" target="_blank" rel="noopener">doi:10.9708/jksci.2026.31.05.001</a></div>
</header>
<div class="panel controls" id="globals">
<label class="f">์ตœ๋Œ€ ๋…ธ์ด์ฆˆ ๊ฐ•๋„ <span class="mono" id="maxNoiseV">0.10</span>
<input type="range" id="maxNoise" min="0.02" max="0.3" step="0.01" value="0.1"></label>
<label class="f">๋…ธ์ด์ฆˆ ๊ฐ„๊ฒฉ
<select id="step"><option>0.005</option><option selected>0.01</option><option>0.02</option></select></label>
<label class="f">Shots
<select id="shots"><option>1024</option><option>2048</option><option selected>4096</option><option>8192</option></select></label>
<label class="f">Seed <input type="number" id="seed" value="1234"></label>
<div class="f" style="grid-column:span 2;min-width:0">
<label class="f" style="margin:0">Chi-Square ํŒ์ • ๋ฐฉ์‹</label>
<div class="seg" id="chiMode">
<button data-v="expected" class="on">๋…ผ๋ฌธ ๋ฐฉ์‹ ยท ๊ธฐ๋Œ€ ๋ถ„ํฌ ์ ํ•ฉ๋„</button>
<button data-v="source">๋ฉ”ํƒ€๋ชจํ”ฝ ๋ฐฉ์‹ ยท TC1๊ณผ ๋™์งˆ์„ฑ</button>
</div>
</div>
</div>
<nav class="tabs" id="tabs">
<button data-t="t1" class="on">โ‘  ๋‹จ์ผ ์‹คํ—˜</button>
<button data-t="t2">โ‘ก ๋…ธ์ด์ฆˆ ๋ชจ๋ธ ๋น„๊ต</button>
<button data-t="t3">โ‘ข ์ง์ ‘ ํšŒ๋กœ ๋งŒ๋“ค๊ธฐ</button>
<button data-t="t4">โ‘ฃ ๊ฐœ๋… ํ•™์Šต</button>
</nav>
<!-- โ‘  ๋‹จ์ผ ์‹คํ—˜ -->
<section class="tab on" id="t1">
<div class="panel">
<div class="controls" style="margin:0 0 6px">
<label class="f">ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค <select id="t1tc"></select></label>
<label class="f">๋…ธ์ด์ฆˆ ๋ชจ๋ธ <select id="t1nm"></select></label>
<div class="f"><span>์ธก์ • ๊ธฐ์ €</span>
<div class="seg" id="t1basis">
<button data-v="paper" class="on">๋…ผ๋ฌธ ๊ธฐ๋ณธ๊ฐ’</button><button data-v="Z">Z</button><button data-v="X">X</button>
</div></div>
</div>
<p class="small" id="t1desc"></p>
<div class="circ" id="t1circ"></div>
<div class="kpis" id="t1kpi"></div>
</div>
<div class="row r2" style="margin-top:16px">
<div class="panel card"><h3>์ถœ๋ ฅ ํ™•๋ฅ  ๋ถ„ํฌ <span class="muted small">(์ : ์ธก์ •๊ฐ’ ยท ์ ์„ : ์ •ํ™•ํ•œ ํ™•๋ฅ )</span></h3><div class="chart"><canvas id="t1dist"></canvas></div></div>
<div class="panel card"><h3>Chi-Square p-value</h3><div class="chart"><canvas id="t1p"></canvas></div></div>
</div>
<div class="row r2" style="margin-top:16px">
<div class="panel card"><h3>KL-Divergence</h3><div class="chart sm"><canvas id="t1kl"></canvas></div></div>
<div class="panel card"><h3>Cosine Similarity</h3><div class="chart sm"><canvas id="t1cos"></canvas></div></div>
</div>
<div class="panel" style="margin-top:16px">
<div class="toolbar"><h3 style="margin:0;font-size:1rem">๊ฒฐ๊ณผ ํ‘œ</h3><button class="btn" id="t1csv">CSV ๋‹ค์šด๋กœ๋“œ</button></div>
<div class="tablewrap" id="t1table"></div>
</div>
</section>
<!-- โ‘ก ๋น„๊ต -->
<section class="tab" id="t2">
<div class="panel">
<div class="controls" style="margin:0">
<label class="f">ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค <select id="t2tc"></select></label>
</div>
<p class="small muted" style="margin:4px 0 0">๋…ผ๋ฌธ Fig.1ยท2 ์žฌํ˜„: DepolarizingยทAmplitude Damping์€ Z-๊ธฐ์ €, Phase Damping์€ X-๊ธฐ์ €์—์„œ ์ธก์ •ํ•ฉ๋‹ˆ๋‹ค.</p>
<div class="circ" id="t2circ"></div>
</div>
<div class="row r3" style="margin-top:16px">
<div class="panel card"><h3>Depolarizing <span class="muted small">Z</span></h3><div class="chart sm"><canvas id="t2d0"></canvas></div></div>
<div class="panel card"><h3>Amplitude Damping <span class="muted small">Z</span></h3><div class="chart sm"><canvas id="t2d1"></canvas></div></div>
<div class="panel card"><h3>Phase Damping <span class="muted small">X</span></h3><div class="chart sm"><canvas id="t2d2"></canvas></div></div>
</div>
<div class="row r3" style="margin-top:16px">
<div class="panel card"><h3>KL-Divergence</h3><div class="chart sm"><canvas id="t2kl"></canvas></div></div>
<div class="panel card"><h3>Cosine Similarity</h3><div class="chart sm"><canvas id="t2cos"></canvas></div></div>
<div class="panel card"><h3>Chi-Square p-value</h3><div class="chart sm"><canvas id="t2p"></canvas></div></div>
</div>
<div class="panel" style="margin-top:16px">
<div class="toolbar">
<h3 style="margin:0;font-size:1rem">๊ฒฐ๊ณผ ํ‘œ</h3>
<div style="display:flex;gap:8px;flex-wrap:wrap">
<button class="btn" id="t2csv">์ด TC CSV</button>
<button class="btn primary" id="allcsv">์ „์ฒด ๋ฐ์ดํ„ฐ์…‹ CSV (6 TC ร— 3 ๋…ธ์ด์ฆˆ ร— ZยทX)</button>
</div>
</div>
<div class="tablewrap" id="t2table"></div>
</div>
</section>
<!-- โ‘ข ์ง์ ‘ ํšŒ๋กœ -->
<section class="tab" id="t3">
<div class="panel">
<p class="small" style="margin-top:0">๊ฒŒ์ดํŠธ๋ฅผ ๊ณต๋ฐฑ์œผ๋กœ ๊ตฌ๋ถ„ํ•ด ์ž…๋ ฅํ•˜์„ธ์š”. ํšŒ๋กœ๋Š” |00โŸฉ์—์„œ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
1-ํ๋น„ํŠธ: <code>h0</code> <code>x1</code> <code>y0</code> <code>z1</code> <code>s0</code> <code>sdg1</code> <code>t0</code> <code>tdg1</code> ยท
2-ํ๋น„ํŠธ: <code>cx01</code>(์ œ์–ด q0 โ†’ ๋ชฉํ‘œ q1) <code>cx10</code> <code>cz01</code> <code>swap01</code></p>
<div class="controls" style="margin:0">
<label class="f" style="grid-column:span 2">๊ฒŒ์ดํŠธ ์‹œํ€€์Šค <input type="text" id="t3seq" value="x0 h0 cx01" spellcheck="false"></label>
<label class="f">๋…ธ์ด์ฆˆ ๋ชจ๋ธ <select id="t3nm"></select></label>
<div class="f"><span>์ธก์ • ๊ธฐ์ €</span>
<div class="seg" id="t3basis"><button data-v="paper" class="on">๋…ผ๋ฌธ ๊ธฐ๋ณธ๊ฐ’</button><button data-v="Z">Z</button><button data-v="X">X</button></div></div>
</div>
<div class="chips" id="t3ex"></div>
<div id="t3err" class="err"></div>
<div class="circ" id="t3circ"></div>
<div id="t3verdict"></div>
<div class="kpis" id="t3kpi"></div>
</div>
<div class="row r2" style="margin-top:16px">
<div class="panel card"><h3>์ถœ๋ ฅ ํ™•๋ฅ  ๋ถ„ํฌ</h3><div class="chart"><canvas id="t3dist"></canvas></div></div>
<div class="panel card"><h3>Chi-Square p-value</h3><div class="chart"><canvas id="t3p"></canvas></div></div>
</div>
<div class="row r2" style="margin-top:16px">
<div class="panel card"><h3>KL-Divergence</h3><div class="chart sm"><canvas id="t3kl"></canvas></div></div>
<div class="panel card"><h3>Cosine Similarity</h3><div class="chart sm"><canvas id="t3cos"></canvas></div></div>
</div>
</section>
<!-- โ‘ฃ ๊ฐœ๋… ํ•™์Šต -->
<section class="tab" id="t4">
<div class="panel learn">
<h2>1. ์™œ ์–‘์ž ์†Œํ”„ํŠธ์›จ์–ด ํ…Œ์ŠคํŒ…์ด ์–ด๋ ค์šด๊ฐ€?</h2>
<p>๊ณ ์ „ ํ”„๋กœ๊ทธ๋žจ์€ ๊ฐ™์€ ์ž…๋ ฅ์— ๊ฐ™์€ ์ถœ๋ ฅ์ด ๋‚˜์˜ค์ง€๋งŒ, ์–‘์ž ํšŒ๋กœ๋Š” <b>์ธก์ • ์ˆœ๊ฐ„ ํ™•๋ฅ ์ ์œผ๋กœ</b> ๊ฒฐ๊ณผ๊ฐ€ ์ •ํ•ด์ง‘๋‹ˆ๋‹ค.
๊ฐ™์€ ํšŒ๋กœ๋ฅผ 4,096๋ฒˆ ์‹คํ–‰ํ•˜๋ฉด โ€œ์ •๋‹ต ํ•˜๋‚˜โ€๊ฐ€ ์•„๋‹ˆ๋ผ <b>์ถœ๋ ฅ ํ™•๋ฅ  ๋ถ„ํฌ</b>๊ฐ€ ๋‚˜์˜ต๋‹ˆ๋‹ค.
๊ทธ๋ž˜์„œ โ€œ๊ธฐ๋Œ€ ์ถœ๋ ฅ๊ณผ ์ผ์น˜ํ•˜๋Š”๊ฐ€?โ€๋ผ๋Š” ์ „ํ†ต์ ์ธ ํ…Œ์ŠคํŠธ ์˜ค๋ผํด์„ ๊ทธ๋Œ€๋กœ ์“ฐ๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค(<b>์˜ค๋ผํด ๋ฌธ์ œ</b>).</p>
<h2>2. ๋ฉ”ํƒ€๋ชจํ”ฝ ํ…Œ์ŠคํŒ…</h2>
<p>์ •๋‹ต์„ ๋ชฐ๋ผ๋„ <b>์ž…๋ ฅ ๋ณ€ํ˜•๊ณผ ์ถœ๋ ฅ ์‚ฌ์ด์˜ ๊ด€๊ณ„(Metamorphic Relation, MR)</b>๊ฐ€ ์ง€์ผœ์ง€๋Š”์ง€ ๊ฒ€์‚ฌํ•ฉ๋‹ˆ๋‹ค.</p>
<ul>
<li><b>์†Œ์Šค ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค</b>: TC1 <code>H(q0) โ†’ CX(q0,q1)</code> โ†’ Bell ์ƒํƒœ |ฮฆโบโŸฉ = (|00โŸฉ+|11โŸฉ)/โˆš2</li>
<li><b>ํŒ”๋กœ์šฐ์—… ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค</b>: TC2~TC6, ์ด์ƒ์ ์œผ๋กœ๋Š” ์ถœ๋ ฅ ๋ถ„ํฌ๋ฅผ ๋ฐ”๊พธ์ง€ ์•Š๋Š” ๊ตฌ์กฐ์  ๋ณ€ํ˜•</li>
<li><b>MR</b>: โ€œ๋…ผ๋ฆฌ์ ์œผ๋กœ ๋™์ผํ•œ ์ถœ๋ ฅ ๋ถ„ํฌ๋ฅผ ๋‚ด๋Š” ๊ฒŒ์ดํŠธ ์กฐํ•ฉ์€ ๋…ธ์ด์ฆˆ ํ™˜๊ฒฝ์—์„œ๋„ ๋™์ผํ•œ ๋ถ„ํฌ๋ฅผ ์œ ์ง€ํ•ด์•ผ ํ•œ๋‹ค.โ€</li>
</ul>
<div class="tablewrap"><table>
<tr><th>TC</th><th>๋ณ€ํ˜•</th><th>์™œ ๋ถ„ํฌ๊ฐ€ ๊ฐ™์€๊ฐ€?</th></tr>
<tr><td>TC2</td><td>X(q0) ์ถ”๊ฐ€</td><td>(|00โŸฉโˆ’|11โŸฉ)/โˆš2 โ€” ์œ„์ƒ ๋ถ€ํ˜ธ๋งŒ ๋‹ฌ๋ผ์ง€๊ณ  Z-๊ธฐ์ € ํ™•๋ฅ ์€ ๋™์ผ</td></tr>
<tr><td>TC3</td><td>Z(q0) ์ถ”๊ฐ€</td><td>Z|0โŸฉ = |0โŸฉ</td></tr>
<tr><td>TC4</td><td>Z(q0), Z(q1)</td><td>์ดˆ๊ธฐ ์ƒํƒœ |00โŸฉ ๋ถˆ๋ณ€</td></tr>
<tr><td>TC5</td><td>H ๋‘ ๋ฒˆ</td><td>HH = I (์ž๊ธฐ ์—ญ์›)</td></tr>
<tr><td>TC6</td><td>์ œ์–ด/๋ชฉํ‘œ ๊ตํ™˜</td><td>|ฮฆโบโŸฉ๋Š” ํ๋น„ํŠธ ๊ตํ™˜์— ๋Œ€์นญ</td></tr>
</table></div>
<h2>3. ์„ธ ๊ฐ€์ง€ ๋…ธ์ด์ฆˆ ๋ชจ๋ธ</h2>
<div class="tablewrap"><table>
<tr><th>๋ชจ๋ธ</th><th>๋ฌผ๋ฆฌ์  ์˜๋ฏธ</th><th>์ถœ๋ ฅ ๋ถ„ํฌ์— ๋‚˜ํƒ€๋‚˜๋Š” ๋ชจ์Šต</th><th>์ธก์ • ๊ธฐ์ €</th></tr>
<tr><td><b>Depolarizing</b> (p)</td><td>ํ™•๋ฅ  p๋กœ ์ƒํƒœ๊ฐ€ ์™„์ „ํžˆ ๋ฌด์ž‘์œ„ํ™”</td><td>4๊ฐœ ์ƒํƒœ๊ฐ€ 25%๋กœ <b>๊ท ๋“ฑํ•˜๊ฒŒ</b> ์ˆ˜๋ ด</td><td>Z</td></tr>
<tr><td><b>Amplitude Damping</b> (ฮณ)</td><td>์—๋„ˆ์ง€ ์ด์™„ |1โŸฉโ†’|0โŸฉ (T1)</td><td>|11โŸฉ ๊ฐ์†Œยท|00โŸฉ ์ฆ๊ฐ€์˜ <b>๋น„๋Œ€์นญ</b> ์™œ๊ณก</td><td>Z</td></tr>
<tr><td><b>Phase Damping</b> (ฮป)</td><td>์—๋„ˆ์ง€ ์†์‹ค ์—†์ด ์œ„์ƒ๋งŒ ์†Œ์‹ค (T2)</td><td>Z-๊ธฐ์ €์—์„œ๋Š” ์ž˜ ์•ˆ ๋ณด์—ฌ <b>X-๊ธฐ์ €</b>์—์„œ ๊ฐ„์„ญ ๊ฐ์‡  ๊ด€์ฐฐ</td><td>X</td></tr>
</table></div>
<p class="small muted">์ด ์‹ค์Šต์˜ ๋…ธ์ด์ฆˆ๋Š” ๊ฐ ๊ฒŒ์ดํŠธ ์งํ›„์— ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. 1-ํ๋น„ํŠธ ๊ฒŒ์ดํŠธ์—๋Š” ๋‹จ์ผ ํ๋น„ํŠธ ์˜ค๋ฅ˜, 2-ํ๋น„ํŠธ ๊ฒŒ์ดํŠธ์—๋Š” 2-ํ๋น„ํŠธ ์˜ค๋ฅ˜๋ฅผ ๋„ฃ์—ˆ๊ณ , X-๊ธฐ์ € ์ธก์ •์šฉ H ๊ฒŒ์ดํŠธ์—๋„ ๋…ธ์ด์ฆˆ๊ฐ€ ๋ถ™์Šต๋‹ˆ๋‹ค.</p>
<h2>4. ํ‰๊ฐ€ ์ง€ํ‘œ</h2>
<p><b>Chi-Square (ํ•ต์‹ฌ ์ง€ํ‘œ)</b> โ€” MR ์œ ์ง€ ์—ฌ๋ถ€๋ฅผ p-value๋กœ ํŒ์ •ํ•ฉ๋‹ˆ๋‹ค.<br>
<span class="formula">ฯ‡ยฒ = ฮฃ (Oแตข โˆ’ Eแตข)ยฒ / Eแตข</span><br>
<span class="badge b-maintain">p &gt; 0.5 ยท MR ์œ ์ง€</span>
<span class="badge b-uncertain">0.05 &lt; p โ‰ค 0.5 ยท ๋ถˆํ™•์‹ค</span>
<span class="badge b-collapse">p โ‰ค 0.05 ยท MR ๋ถ•๊ดด</span></p>
<p><b>KL-Divergence</b> โ€” ๊ธฐ๋Œ€ ๋ถ„ํฌ P์—์„œ ๊ด€์ธก ๋ถ„ํฌ Q๊ฐ€ ์–ผ๋งˆ๋‚˜ ์ดํƒˆํ–ˆ๋Š”์ง€(0์— ๊ฐ€๊นŒ์šธ์ˆ˜๋ก ๊ทผ์ ‘).<br>
<span class="formula">KL(Pโ€–Q) = ฮฃ P(x) ยท log( P(x) / Q(x) )</span></p>
<p><b>Cosine Similarity</b> โ€” ๋‘ ๋ถ„ํฌ๋ฅผ ๋ฒกํ„ฐ๋กœ ๋ณด๊ณ  ๋ฐฉํ–ฅ(๊ตฌ์กฐ)์˜ ์œ ์‚ฌ๋„๋ฅผ ์žฝ๋‹ˆ๋‹ค(1์— ๊ฐ€๊นŒ์šธ์ˆ˜๋ก ์œ ์‚ฌ).<br>
<span class="formula">CS = (P ยท Q) / (โ€–Pโ€– ยท โ€–Qโ€–)</span></p>
<h2>5. ์ƒ๊ฐํ•ด ๋ณผ ์งˆ๋ฌธ</h2>
<ol class="q">
<li>๊ธฐ๋Œ€ ๋ถ„ํฌ์—์„œ |01โŸฉ, |10โŸฉ์˜ ๊ธฐ๋Œ€ ๋นˆ๋„๋Š” 0์ž…๋‹ˆ๋‹ค. ์˜ค๋ฅ˜ ์ƒํƒœ๊ฐ€ <b>๋‹จ ๋ช‡ ๋ฒˆ๋งŒ</b> ๊ด€์ฐฐ๋ผ๋„ ์ ํ•ฉ๋„ ๊ฒ€์ •์˜ p-value๋Š” ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”? ํŒ์ • ๋ฐฉ์‹์„ ๋ฐ”๊ฟ” ๋น„๊ตํ•ด ๋ณด์„ธ์š”.
<br><button class="btn" data-go='{"tab":"t1","tc":"TC3","nm":"depolarizing","basis":"paper","chi":"source"}'>TC3๋ฅผ ๋ฉ”ํƒ€๋ชจํ”ฝ ๋ฐฉ์‹์œผ๋กœ ๋ณด๊ธฐ</button></li>
<li>TC5๋Š” ์ด์ƒ์ ์œผ๋กœ TC1๊ณผ ๊ฐ™์ง€๋งŒ, ๋…ธ์ด์ฆˆ ํ™˜๊ฒฝ์—์„œ๋Š” ์™œ ๋” ๋นจ๋ฆฌ ๋ฌด๋„ˆ์งˆ๊นŒ์š”? (๊ฒŒ์ดํŠธ ์ˆ˜์™€ ์˜ค๋ฅ˜ ๋ˆ„์ )
<br><button class="btn" data-go='{"tab":"t1","tc":"TC5","nm":"depolarizing","basis":"paper","chi":"source"}'>TC5 ์‹คํ–‰</button></li>
<li>๊ฐ™์€ ๋…ธ์ด์ฆˆ ๊ฐ•๋„์—์„œ Amplitude Damping์˜ KL์ด ๊ฐ€์žฅ ๊ฐ€ํŒŒ๋ฅด๊ฒŒ ์ฆ๊ฐ€ํ•˜๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ผ๊นŒ์š”?
<br><button class="btn" data-go='{"tab":"t2","tc":"TC1"}'>์„ธ ๋ชจ๋ธ ๋น„๊ต</button></li>
<li>Phase Damping์„ <b>Z-๊ธฐ์ €</b>๋กœ ์ธก์ •ํ•˜๋ฉด ์™œ ๋ณ€ํ™”๊ฐ€ ๊ฑฐ์˜ ๋ณด์ด์ง€ ์•Š์„๊นŒ์š”?
<br><button class="btn" data-go='{"tab":"t1","tc":"TC1","nm":"phase_damping","basis":"Z"}'>PD๋ฅผ Z-๊ธฐ์ €๋กœ ๋ณด๊ธฐ</button></li>
<li>TC2๋ฅผ <b>X-๊ธฐ์ €</b>๋กœ ์ธก์ •ํ•˜๋ฉด ๋…ธ์ด์ฆˆ๊ฐ€ 0์ผ ๋•Œ๋„ |01โŸฉ, |10โŸฉ๋งŒ ๋‚˜์˜ต๋‹ˆ๋‹ค. ์™œ์ผ๊นŒ์š”?
(ํžŒํŠธ: (|00โŸฉโˆ’|11โŸฉ)/โˆš2์— HโŠ—H๋ฅผ ์ ์šฉํ•ด ๋ณด์„ธ์š”.) MR์€ <b>์ธก์ • ๊ธฐ์ €๊นŒ์ง€ ํฌํ•จํ•ด</b> ์ •์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
<br><button class="btn" data-go='{"tab":"t1","tc":"TC2","nm":"phase_damping","basis":"paper"}'>TC2 + Phase Damping(X)</button></li>
<li><code>h0 cx01 s0</code>๋Š” Z-๊ธฐ์ € ์ถœ๋ ฅ ๋ถ„ํฌ๊ฐ€ TC1๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์œ ํšจํ•œ ํŒ”๋กœ์šฐ์—…์ผ๊นŒ์š”? X-๊ธฐ์ €์—์„œ๋Š” ์–ด๋–จ๊นŒ์š”?
<br><button class="btn" data-go='{"tab":"t3","seq":"h0 cx01 s0","nm":"phase_damping","basis":"paper"}'>์ง์ ‘ ํ™•์ธ</button></li>
</ol>
</div>
</section>
<footer>
๋ธŒ๋ผ์šฐ์ € ๋‚ด 2-ํ๋น„ํŠธ ๋ฐ€๋„ํ–‰๋ ฌ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ๋กœ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. ๋…ธ์ด์ฆˆ ์ฑ„๋„(Kraus ์—ฐ์‚ฐ์ž)์€ Qiskit Aer์˜
<code>depolarizing_error</code>, <code>amplitude_damping_error</code>, <code>phase_damping_error</code>์™€ ๊ฐ™๊ณ ,
์ธก์ • ๋นˆ๋„๋Š” seed ๊ธฐ๋ฐ˜ ๋‹คํ•ญ ์ƒ˜ํ”Œ๋ง์œผ๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๋‚œ์ˆ˜ ์ƒ์„ฑ๊ธฐ๊ฐ€ ๋‹ค๋ฅด๋ฏ€๋กœ Qiskit Aer์™€ ์ธก์ • ๋นˆ๋„๋Š” ์™„์ „ํžˆ ๊ฐ™์ง€ ์•Š์ง€๋งŒ ๋ถ„ํฌ๋Š” ํ†ต๊ณ„์ ์œผ๋กœ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.
</footer>
</div>
<script>
// ===== 2-qubit density-matrix simulator + metrics (Qiskit Aer-equivalent noise channels) =====
// Basis index i = 2*q1 + q0, label "q1q0" (Qiskit bit order).
const STATES = ["00", "01", "10", "11"];
const EXPECTED = [0.5, 0, 0, 0.5];
const EPS = 1e-6;
// ---- complex matrix helpers (n x n, row-major, separate re/im) ----
function M(n, re, im) {
return { n, re: Float64Array.from(re), im: Float64Array.from(im || new Array(n * n).fill(0)) };
}
function mul(A, B) {
const n = A.n, re = new Float64Array(n * n), im = new Float64Array(n * n);
for (let i = 0; i < n; i++)
for (let k = 0; k < n; k++) {
const ar = A.re[i * n + k], ai = A.im[i * n + k];
if (ar === 0 && ai === 0) continue;
for (let j = 0; j < n; j++) {
const br = B.re[k * n + j], bi = B.im[k * n + j];
re[i * n + j] += ar * br - ai * bi;
im[i * n + j] += ar * bi + ai * br;
}
}
return { n, re, im };
}
function dag(A) {
const n = A.n, re = new Float64Array(n * n), im = new Float64Array(n * n);
for (let i = 0; i < n; i++) for (let j = 0; j < n; j++) { re[i * n + j] = A.re[j * n + i]; im[i * n + j] = -A.im[j * n + i]; }
return { n, re, im };
}
function kron(A, B) { // A acts on the high bit (q1), B on the low bit (q0)
const n = A.n * B.n, re = new Float64Array(n * n), im = new Float64Array(n * n);
for (let a1 = 0; a1 < A.n; a1++) for (let b1 = 0; b1 < A.n; b1++) {
const ar = A.re[a1 * A.n + b1], ai = A.im[a1 * A.n + b1];
for (let a0 = 0; a0 < B.n; a0++) for (let b0 = 0; b0 < B.n; b0++) {
const br = B.re[a0 * B.n + b0], bi = B.im[a0 * B.n + b0];
const r = a1 * B.n + a0, c = b1 * B.n + b0;
re[r * n + c] = ar * br - ai * bi;
im[r * n + c] = ar * bi + ai * br;
}
}
return { n, re, im };
}
function scale(A, s) { return { n: A.n, re: A.re.map(v => v * s), im: A.im.map(v => v * s) }; }
function addTo(acc, B) { for (let i = 0; i < acc.re.length; i++) { acc.re[i] += B.re[i]; acc.im[i] += B.im[i]; } }
// ---- gates ----
const s2 = Math.SQRT1_2;
const G1 = {
i: M(2, [1, 0, 0, 1]),
h: M(2, [s2, s2, s2, -s2]),
x: M(2, [0, 1, 1, 0]),
y: M(2, [0, 0, 0, 0], [0, -1, 1, 0]),
z: M(2, [1, 0, 0, -1]),
s: M(2, [1, 0, 0, 0], [0, 0, 0, 1]),
sdg: M(2, [1, 0, 0, 0], [0, 0, 0, -1]),
t: M(2, [1, 0, 0, s2], [0, 0, 0, s2]),
tdg: M(2, [1, 0, 0, s2], [0, 0, 0, -s2]),
};
const ONE_Q = ["h", "x", "y", "z", "s", "sdg", "t", "tdg"];
const TWO_Q = ["cx", "cz", "swap"];
function lift(A, q) { return q === 0 ? kron(G1.i, A) : kron(A, G1.i); }
function permU(f) { const re = new Array(16).fill(0); for (let i = 0; i < 4; i++) re[f(i) * 4 + i] = 1; return M(4, re); }
function gate2(name, a, b) {
const bit = (i, q) => (i >> q) & 1;
if (name === "cx") return permU(i => bit(i, a) ? i ^ (1 << b) : i);
if (name === "swap") return permU(i => { const x = bit(i, 0), y = bit(i, 1); return (x << 1) | y; });
if (name === "cz") return M(4, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1]);
throw new Error(name);
}
function gateU(op) { return op.length === 2 ? lift(G1[op[0]], op[1]) : gate2(op[0], op[1], op[2]); }
// ---- noise channels (Kraus operators, identical to qiskit_aer.noise) ----
function kraus1(model, p) {
if (model === "depolarizing") // depolarizing_error(p,1): (1-p)ฯ + pยทI/2
return [scale(G1.i, Math.sqrt(1 - 3 * p / 4)), scale(G1.x, Math.sqrt(p / 4)), scale(G1.y, Math.sqrt(p / 4)), scale(G1.z, Math.sqrt(p / 4))];
if (model === "amplitude_damping") // amplitude_damping_error(ฮณ)
return [M(2, [1, 0, 0, Math.sqrt(1 - p)]), M(2, [0, Math.sqrt(p), 0, 0])];
if (model === "phase_damping") // phase_damping_error(ฮป)
return [M(2, [1, 0, 0, Math.sqrt(1 - p)]), M(2, [0, 0, 0, Math.sqrt(p)])];
throw new Error(model);
}
const PAULIS = [G1.i, G1.x, G1.y, G1.z];
function applyKraus(rho, Ks) {
const out = { n: 4, re: new Float64Array(16), im: new Float64Array(16) };
for (const K of Ks) addTo(out, mul(mul(K, rho), dag(K)));
return out;
}
function noiseAfter(rho, op, model, p) {
if (p <= 0) return rho;
if (op.length === 2) return applyKraus(rho, kraus1(model, p).map(K => lift(K, op[1])));
if (model === "depolarizing") { // depolarizing_error(p,2): (1-p)ฯ + pยทI/4
const Ks = [];
for (let a = 0; a < 4; a++) for (let b = 0; b < 4; b++)
Ks.push(scale(kron(PAULIS[a], PAULIS[b]), Math.sqrt(a === 0 && b === 0 ? 1 - 15 * p / 16 : p / 16)));
return applyKraus(rho, Ks);
}
// AD / PD on 2-qubit gates: e.tensor(e) โ†’ independent channel on each qubit
rho = applyKraus(rho, kraus1(model, p).map(K => lift(K, 0)));
return applyKraus(rho, kraus1(model, p).map(K => lift(K, 1)));
}
// Exact output probabilities (noisy gates; X-basis adds noisy H on both qubits; measurement is noiseless)
function exactProbs(ops, basis, model, p) {
let rho = M(4, [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
const all = basis === "X" ? [...ops, ["h", 0], ["h", 1]] : ops;
for (const op of all) {
const U = gateU(op);
rho = mul(mul(U, rho), dag(U));
rho = noiseAfter(rho, op, model, p);
}
const pr = [0, 1, 2, 3].map(i => Math.max(0, rho.re[i * 5]));
const s = pr.reduce((a, b) => a + b, 0);
return pr.map(v => v / s);
}
// ---- seeded sampling ----
function hash32(str) {
let h = 2166136261 >>> 0;
for (let i = 0; i < str.length; i++) { h ^= str.charCodeAt(i); h = Math.imul(h, 16777619); }
return h >>> 0;
}
function mulberry32(a) {
return function () {
a |= 0; a = (a + 0x6D2B79F5) | 0;
let t = Math.imul(a ^ (a >>> 15), 1 | a);
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
function sampleCounts(probs, shots, seedStr) {
const rnd = mulberry32(hash32(seedStr));
const cum = []; let c = 0;
for (const p of probs) { c += p; cum.push(c); }
const counts = [0, 0, 0, 0];
for (let s = 0; s < shots; s++) {
const u = rnd() * c;
let k = 0; while (k < 3 && u >= cum[k]) k++;
counts[k]++;
}
return counts;
}
// ---- statistics ----
function lnGamma(z) {
const g = [676.5203681218851, -1259.1392167224028, 771.32342877765313, -176.61502916214059,
12.507343278686905, -0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7];
if (z < 0.5) return Math.log(Math.PI / Math.sin(Math.PI * z)) - lnGamma(1 - z);
z -= 1; let x = 0.99999999999980993;
for (let i = 0; i < 8; i++) x += g[i] / (z + i + 1);
const t = z + 7.5;
return 0.5 * Math.log(2 * Math.PI) + (z + 0.5) * Math.log(t) - t + Math.log(x);
}
function gammaQ(a, x) { // regularized upper incomplete gamma
if (x <= 0) return 1;
if (x < a + 1) {
let sum = 1 / a, del = sum, ap = a;
for (let n = 0; n < 500; n++) { ap++; del *= x / ap; sum += del; if (Math.abs(del) < Math.abs(sum) * 1e-15) break; }
return Math.max(0, 1 - sum * Math.exp(-x + a * Math.log(x) - lnGamma(a)));
}
let b = x + 1 - a, c = 1e300, d = 1 / b, h = d;
for (let i = 1; i < 500; i++) {
const an = -i * (i - a); b += 2;
d = an * d + b; if (Math.abs(d) < 1e-300) d = 1e-300;
c = b + an / c; if (Math.abs(c) < 1e-300) c = 1e-300;
d = 1 / d; const del = d * c; h *= del;
if (Math.abs(del - 1) < 1e-15) break;
}
return Math.exp(-x + a * Math.log(x) - lnGamma(a)) * h;
}
function chi2sf(x, dof) { return gammaQ(dof / 2, x / 2); }
// ๋…ผ๋ฌธ ๋ฐฉ์‹: ๊ธฐ๋Œ€ ๋ถ„ํฌ์— ๋Œ€ํ•œ ์ ํ•ฉ๋„ ๊ฒ€์ • (๊ธฐ๋Œ€ ๋นˆ๋„ 0 โ†’ ฮต)
function chiExpected(counts, expected = EXPECTED) {
const shots = counts.reduce((a, b) => a + b, 0);
let pe = expected.map(v => v + EPS); const s = pe.reduce((a, b) => a + b, 0); pe = pe.map(v => v / s);
let stat = 0;
for (let i = 0; i < 4; i++) { const e = pe[i] * shots; stat += (counts[i] - e) ** 2 / e; }
return [stat, chi2sf(stat, 3)];
}
// ๋ฉ”ํƒ€๋ชจํ”ฝ ๋ฐฉ์‹: ์†Œ์Šค(TC1)์™€ ํŒ”๋กœ์šฐ์—…์˜ 2ร—k ๋™์งˆ์„ฑ ๊ฒ€์ •
function chiContingency(a, b) {
const cols = [];
for (let i = 0; i < 4; i++) if (a[i] + b[i] > 0) cols.push([a[i], b[i]]);
if (cols.length < 2) return [0, 1];
const ra = cols.reduce((s, c) => s + c[0], 0), rb = cols.reduce((s, c) => s + c[1], 0), N = ra + rb;
let stat = 0;
for (const [x, y] of cols) {
const ct = x + y, ea = ra * ct / N, eb = rb * ct / N;
stat += (x - ea) ** 2 / ea + (y - eb) ** 2 / eb;
}
return [stat, chi2sf(stat, cols.length - 1)];
}
function klDiv(expected, counts) {
const s = counts.reduce((a, b) => a + b, 0);
let q = counts.map(c => c / s + EPS); const qs = q.reduce((a, b) => a + b, 0); q = q.map(v => v / qs);
let kl = 0;
for (let i = 0; i < 4; i++) if (expected[i] > 0) kl += expected[i] * Math.log(expected[i] / q[i]);
return kl;
}
function cosSim(expected, counts) {
const s = counts.reduce((a, b) => a + b, 0), q = counts.map(c => c / s);
let dot = 0, na = 0, nb = 0;
for (let i = 0; i < 4; i++) { dot += expected[i] * q[i]; na += expected[i] ** 2; nb += q[i] ** 2; }
return na && nb ? dot / Math.sqrt(na * nb) : 0;
}
function judge(p) { return p > 0.5 ? "maintain" : p > 0.05 ? "uncertain" : "collapse"; }
// ---- test cases (paper Table 2) ----
const TEST_CASES = {
TC1: { name: "Base Combination", ops: [["h", 0], ["cx", 0, 1]] },
TC2: { name: "Logical Equivalence", ops: [["x", 0], ["h", 0], ["cx", 0, 1]] },
TC3: { name: "Single Phase Flip", ops: [["z", 0], ["h", 0], ["cx", 0, 1]] },
TC4: { name: "Double Phase Flip", ops: [["z", 0], ["z", 1], ["h", 0], ["cx", 0, 1]] },
TC5: { name: "Gate Invalidation", ops: [["h", 0], ["cx", 0, 1], ["h", 0], ["h", 0]] },
TC6: { name: "Swap Operations", ops: [["h", 1], ["cx", 1, 0]] },
};
const NOISE = {
depolarizing: { label: "Depolarizing", sym: "p", basis: "Z" },
amplitude_damping: { label: "Amplitude Damping", sym: "ฮณ", basis: "Z" },
phase_damping: { label: "Phase Damping", sym: "ฮป", basis: "X" },
};
function parseOps(text) {
const toks = text.replace(/,|โ†’|->/g, " ").trim().split(/\s+/).filter(Boolean);
if (!toks.length) throw new Error("๊ฒŒ์ดํŠธ ์‹œํ€€์Šค๊ฐ€ ๋น„์–ด ์žˆ์Šต๋‹ˆ๋‹ค.");
const ops = [];
for (const tok of toks) {
const t = tok.toLowerCase();
let m = t.match(/^(cx|cz|swap)([01])([01])$/);
if (m) {
if (m[2] === m[3]) throw new Error(`'${tok}': 2-ํ๋น„ํŠธ ๊ฒŒ์ดํŠธ๋Š” 01 ๋˜๋Š” 10 ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.`);
ops.push([m[1], +m[2], +m[3]]); continue;
}
m = t.match(/^(sdg|tdg|h|x|y|z|s|t)([01])$/);
if (m) { ops.push([m[1], +m[2]]); continue; }
throw new Error(`'${tok}' ์„(๋ฅผ) ํ•ด์„ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์˜ˆ: h0, x1, z0, s0, t1, cx01, cz10, swap01`);
}
return ops;
}
function levelsOf(maxNoise, step) {
const n = Math.round(maxNoise / step), out = [];
for (let i = 0; i <= n; i++) out.push(+(i * step).toFixed(4));
return out;
}
// One sweep: returns rows with counts, exact probs, metrics
function runSweep({ ops, tcKey, model, basis, levels, shots, seed, chiMode }) {
const expected = exactProbs(TEST_CASES.TC1.ops, basis, model, 0);
return levels.map(p => {
const exact = exactProbs(ops, basis, model, p);
const counts = sampleCounts(exact, shots, `${seed}|${tcKey}|${model}|${basis}|${p}`);
const srcCounts = sampleCounts(exactProbs(TEST_CASES.TC1.ops, basis, model, p), shots, `${seed}|TC1|${model}|${basis}|${p}`);
const [chiE, pE] = chiExpected(counts, expected);
const [chiS, pS] = tcKey === "TC1" ? [0, 1] : chiContingency(srcCounts, counts);
const [chi, pv] = chiMode === "source" ? [chiS, pS] : [chiE, pE];
return {
noise: p, counts, probs: counts.map(c => c / shots), exact,
chi, pv, chiE, pE, chiS, pS,
kl: klDiv(expected, counts), cos: cosSim(expected, counts), mr: judge(pv),
};
});
}
if (typeof module !== "undefined") module.exports = {
STATES, EXPECTED, TEST_CASES, NOISE, exactProbs, sampleCounts, chiExpected, chiContingency,
klDiv, cosSim, chi2sf, parseOps, runSweep, levelsOf, judge,
};
</script>
<script>
// ===================== UI =====================
const $ = id => document.getElementById(id);
const MODEL_KEYS = Object.keys(NOISE);
const TC_KEYS = Object.keys(TEST_CASES);
const TC_DESC = {
TC1: "์†Œ์Šค ๊ฒŒ์ดํŠธ ์กฐํ•ฉ. H(q0) โ†’ CX(q0,q1)๋กœ Bell ์ƒํƒœ |ฮฆโบโŸฉ = (|00โŸฉ+|11โŸฉ)/โˆš2๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.",
TC2: "H ์ด์ „์— X(q0) ์ถ”๊ฐ€. ์ƒํƒœ๊ฐ€ (|00โŸฉโˆ’|11โŸฉ)/โˆš2๋กœ ์œ„์ƒ ๋ถ€ํ˜ธ๋งŒ ๋‹ฌ๋ผ์ ธ Z-๊ธฐ์ € ํ™•๋ฅ ์€ ๊ฐ™์Šต๋‹ˆ๋‹ค. โš ๏ธ X-๊ธฐ์ €์—์„œ๋Š” (|01โŸฉ+|10โŸฉ)/โˆš2๋กœ ์ธก์ •๋˜์–ด ๋…ธ์ด์ฆˆ 0์—์„œ๋„ MR์ด ์„ฑ๋ฆฝํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.",
TC3: "H ์ด์ „์— Z(q0) ์ถ”๊ฐ€. Z|0โŸฉ = |0โŸฉ์ด๋ฏ€๋กœ ์ดˆ๊ธฐ ์ƒํƒœ๊ฐ€ ๋ฐ”๋€Œ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.",
TC4: "๋‘ ํ๋น„ํŠธ ๋ชจ๋‘์— Z ์ ์šฉ. ์ดˆ๊ธฐ ์ƒํƒœ |00โŸฉ์ด ๊ทธ๋Œ€๋กœ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.",
TC5: "CX ์ดํ›„ H(q0)๋ฅผ ๋‘ ๋ฒˆ ์ ์šฉ. HH = I์ด๋ฏ€๋กœ ์ƒํƒœ๋Š” ๊ฐ™์ง€๋งŒ, ๋…ธ์ด์ฆˆ ํ™˜๊ฒฝ์—์„œ๋Š” ๊ฒŒ์ดํŠธ๊ฐ€ ๋Š˜์–ด ์˜ค๋ฅ˜๊ฐ€ ๋” ์Œ“์ž…๋‹ˆ๋‹ค.",
TC6: "H(q1) โ†’ CX(q1,q0). |ฮฆโบโŸฉ๋Š” ํ๋น„ํŠธ ๊ตํ™˜์— ๋Œ€์นญ์ด๋ฏ€๋กœ ๊ฐ™์€ ์ƒํƒœ๊ฐ€ ๋งŒ๋“ค์–ด์ง‘๋‹ˆ๋‹ค.",
};
const MR_TXT = { maintain: "MR ์œ ์ง€", uncertain: "MR ๋ถˆํ™•์‹ค", collapse: "MR ๋ถ•๊ดด" };
const css = v => getComputedStyle(document.documentElement).getPropertyValue(v).trim();
const stateColor = i => css(["--s00", "--s01", "--s10", "--s11"][i]);
const modelColor = k => css({ depolarizing: "--m-dep", amplitude_damping: "--m-ad", phase_damping: "--m-pd" }[k]);
const state = { chiMode: "expected", t1basis: "paper", t3basis: "paper" };
// ---------- controls ----------
function fillSelect(el, items) { el.innerHTML = items.map(([v, t]) => `<option value="${v}">${t}</option>`).join(""); }
const tcOptions = TC_KEYS.map(k => [k, `${k} ยท ${TEST_CASES[k].name}`]);
const nmOptions = MODEL_KEYS.map(k => [k, NOISE[k].label]);
fillSelect($("t1tc"), tcOptions); fillSelect($("t2tc"), tcOptions);
fillSelect($("t1nm"), nmOptions); fillSelect($("t3nm"), nmOptions);
function segInit(id, key, onChange) {
$(id).querySelectorAll("button").forEach(b => b.addEventListener("click", () => {
$(id).querySelectorAll("button").forEach(x => x.classList.toggle("on", x === b));
state[key] = b.dataset.v; onChange();
}));
}
function segSet(id, key, v) {
$(id).querySelectorAll("button").forEach(x => x.classList.toggle("on", x.dataset.v === v)); state[key] = v;
}
function globals() {
return {
maxNoise: +$("maxNoise").value, step: +$("step").value, shots: +$("shots").value,
seed: Math.trunc(+$("seed").value) || 0, chiMode: state.chiMode,
};
}
function resolveBasis(choice, model) { return choice === "paper" ? NOISE[model].basis : choice; }
// ---------- formatting ----------
const fx = (v, d = 4) => Number.isFinite(v) ? v.toFixed(d) : "โ€“";
function fp(v) {
if (!Number.isFinite(v)) return "โ€“";
if (v === 0) return "< 1e-300";
return v < 1e-3 ? v.toExponential(2) : v.toFixed(4);
}
const badge = mr => `<span class="badge b-${mr}">${MR_TXT[mr]}</span>`;
function opsStr(ops) {
const n = { sdg: "Sโ€ ", tdg: "Tโ€ " };
return ops.map(o => `${n[o[0]] || o[0].toUpperCase()}(${o.slice(1).map(q => "q" + q).join(",")})`).join(" โ†’ ");
}
// ---------- circuit drawing ----------
function drawCircuit(ops, basis) {
const dx = 54, x0 = 46, y = [30, 84], cols = ops.length + (basis === "X" ? 1 : 0) + 1;
const W = x0 + cols * dx + 14, H = 112;
let s = `<svg width="${W}" height="${H}" viewBox="0 0 ${W} ${H}" role="img" aria-label="circuit">`;
const ink = css("--ink"), g = css("--gate"), gl = css("--gate-line"), b = css("--basis"), bl = css("--basis-line"), mu = css("--muted");
for (const q of [0, 1]) {
s += `<text x="8" y="${y[q] + 5}" font-size="13" fill="${mu}" font-family="ui-monospace,monospace">q${q}</text>`;
s += `<line x1="34" y1="${y[q]}" x2="${W - 8}" y2="${y[q]}" stroke="${mu}" stroke-width="1.3"/>`;
}
const box = (cx, cy, label, fill, stroke) =>
`<rect x="${cx - 16}" y="${cy - 15}" width="32" height="30" rx="5" fill="${fill}" stroke="${stroke}" stroke-width="1.4"/>` +
`<text x="${cx}" y="${cy + 5}" text-anchor="middle" font-size="14" font-weight="700" fill="${ink}" font-family="system-ui,sans-serif">${label}</text>`;
const names = { sdg: "Sโ€ ", tdg: "Tโ€ " };
ops.forEach((o, i) => {
const cx = x0 + i * dx + dx / 2;
if (o.length === 2) { s += box(cx, y[o[1]], names[o[0]] || o[0].toUpperCase(), g, gl); return; }
const [n, a, t] = o;
s += `<line x1="${cx}" y1="${y[0]}" x2="${cx}" y2="${y[1]}" stroke="${gl}" stroke-width="2"/>`;
if (n === "cx") {
s += `<circle cx="${cx}" cy="${y[a]}" r="5.5" fill="${gl}"/>`;
s += `<circle cx="${cx}" cy="${y[t]}" r="12" fill="${g}" stroke="${gl}" stroke-width="2"/>`;
s += `<line x1="${cx - 12}" y1="${y[t]}" x2="${cx + 12}" y2="${y[t]}" stroke="${gl}" stroke-width="2"/><line x1="${cx}" y1="${y[t] - 12}" x2="${cx}" y2="${y[t] + 12}" stroke="${gl}" stroke-width="2"/>`;
} else if (n === "cz") {
s += `<circle cx="${cx}" cy="${y[0]}" r="5.5" fill="${gl}"/><circle cx="${cx}" cy="${y[1]}" r="5.5" fill="${gl}"/>`;
} else {
for (const yy of y) s += `<path d="M${cx - 7} ${yy - 7}L${cx + 7} ${yy + 7}M${cx + 7} ${yy - 7}L${cx - 7} ${yy + 7}" stroke="${gl}" stroke-width="2.4"/>`;
}
});
let c = ops.length;
if (basis === "X") {
const bx = x0 + c * dx;
s += `<line x1="${bx}" y1="10" x2="${bx}" y2="${H - 10}" stroke="${mu}" stroke-dasharray="4 4"/>`;
for (const q of [0, 1]) s += box(bx + dx / 2, y[q], "H", b, bl);
c++;
}
const mx = x0 + c * dx + dx / 2;
for (const q of [0, 1]) {
s += `<rect x="${mx - 16}" y="${y[q] - 15}" width="32" height="30" rx="5" fill="${css("--soft")}" stroke="${mu}" stroke-width="1.4"/>`;
s += `<path d="M${mx - 10} ${y[q] + 7}A10 10 0 0 1 ${mx + 10} ${y[q] + 7}" fill="none" stroke="${ink}" stroke-width="1.5"/><line x1="${mx}" y1="${y[q] + 7}" x2="${mx + 7}" y2="${y[q] - 7}" stroke="${ink}" stroke-width="1.5"/>`;
}
return s + "</svg>";
}
// ---------- charts ----------
const charts = {};
function lineChart(id, labels, datasets, { yMin, yMax, alpha } = {}) {
if (typeof Chart === "undefined") { $(id).parentElement.innerHTML = '<p class="err small">์ฐจํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ์ธํ„ฐ๋„ท ์—ฐ๊ฒฐ์„ ํ™•์ธํ•˜์„ธ์š”.</p>'; return; }
if (charts[id]) charts[id].destroy();
const grid = css("--line"), mu = css("--muted");
if (alpha) datasets = [...datasets, { label: "ฮฑ = 0.05", data: labels.map(() => 0.05), borderColor: mu, borderDash: [5, 5], pointRadius: 0, borderWidth: 1.2 }];
charts[id] = new Chart($(id), {
type: "line",
data: { labels, datasets },
options: {
responsive: true, maintainAspectRatio: false, animation: false,
interaction: { mode: "index", intersect: false },
scales: {
x: { title: { display: true, text: "Noise strength", color: mu }, ticks: { color: mu }, grid: { color: grid } },
y: { min: yMin, max: yMax, ticks: { color: mu }, grid: { color: grid } },
},
plugins: {
legend: { labels: { color: mu, boxWidth: 14, filter: it => !it.text.startsWith("_") } },
tooltip: { filter: it => !it.dataset.label.startsWith("_") },
},
},
});
}
function distDatasets(rows) {
const ds = [];
STATES.forEach((s, i) => {
const c = stateColor(i);
ds.push({ label: `|${s}โŸฉ`, data: rows.map(r => r.probs[i]), borderColor: c, backgroundColor: c, pointRadius: 3, borderWidth: 2 });
ds.push({ label: `_exact${s}`, data: rows.map(r => r.exact[i]), borderColor: c, borderDash: [4, 4], pointRadius: 0, borderWidth: 1.2 });
});
return ds;
}
const mset = (label, color, data) => ({ label, data, borderColor: color, backgroundColor: color, pointRadius: 3, borderWidth: 2 });
// ---------- tables / csv ----------
function rowsTable(rows) {
const head = `<tr><th>๋…ธ์ด์ฆˆ</th>${STATES.map(s => `<th>P(|${s}โŸฉ)</th>`).join("")}<th>ฯ‡ยฒ</th><th>p-value</th><th>KL</th><th>Cosine</th><th class="l">ํŒ์ •</th></tr>`;
const body = rows.map(r => `<tr><td>${fx(r.noise, 3)}</td>${r.probs.map(v => `<td>${fx(v)}</td>`).join("")}<td>${fx(r.chi, 2)}</td><td>${fp(r.pv)}</td><td>${fx(r.kl, 5)}</td><td>${fx(r.cos, 5)}</td><td class="l">${badge(r.mr)}</td></tr>`).join("");
return `<table>${head}${body}</table>`;
}
const CSV_COLS = ["test_case", "noise_model", "basis", "paper_setting", "noise_strength", "shots", "seed",
"count_00", "count_01", "count_10", "count_11", "prob_00", "prob_01", "prob_10", "prob_11", "error_state_prob",
"exact_00", "exact_01", "exact_10", "exact_11",
"chi2_expected", "pvalue_expected", "mr_expected", "chi2_vs_tc1", "pvalue_vs_tc1", "mr_vs_tc1", "kl_divergence", "cosine_similarity"];
function csvRows(rows, meta, g) {
return rows.map(r => [meta.tc, meta.model, meta.basis, meta.basis === NOISE[meta.model].basis, r.noise, g.shots, g.seed,
...r.counts, ...r.probs.map(v => v.toFixed(6)), (r.probs[1] + r.probs[2]).toFixed(6), ...r.exact.map(v => v.toFixed(6)),
r.chiE.toFixed(4), r.pE, judge(r.pE), r.chiS.toFixed(4), r.pS, judge(r.pS), r.kl.toFixed(6), r.cos.toFixed(6)]);
}
function download(name, lines) {
const text = "๏ปฟ" + [CSV_COLS.join(","), ...lines.map(l => l.join(","))].join("\n");
const a = document.createElement("a");
a.href = URL.createObjectURL(new Blob([text], { type: "text/csv;charset=utf-8" }));
a.download = name; document.body.appendChild(a); a.click(); a.remove();
setTimeout(() => URL.revokeObjectURL(a.href), 1000);
}
function kpis(rows) {
const first = rows.find(r => r.pv <= 0.05), last = rows[rows.length - 1];
return `<div class="kpi"><div class="k">MR ๋ถ•๊ดด ์‹œ์ž‘ ๋…ธ์ด์ฆˆ</div><div class="v">${first ? fx(first.noise, 3) : "์—†์Œ"}</div></div>
<div class="kpi"><div class="k">์ตœ๋Œ€ ๋…ธ์ด์ฆˆ ํŒ์ •</div><div class="v">${badge(last.mr)}</div></div>
<div class="kpi"><div class="k">์ตœ๋Œ€ ๋…ธ์ด์ฆˆ KL</div><div class="v">${fx(last.kl)}</div></div>
<div class="kpi"><div class="k">์ตœ๋Œ€ ๋…ธ์ด์ฆˆ Cosine</div><div class="v">${fx(last.cos)}</div></div>
<div class="kpi"><div class="k">์˜ค๋ฅ˜ ์ƒํƒœ P(|01โŸฉ)+P(|10โŸฉ)</div><div class="v">${fx(last.probs[1] + last.probs[2])}</div></div>`;
}
function metricCharts(prefix, labels, sets) {
lineChart(prefix + "kl", labels, sets.map(s => mset(s.label, s.color, s.rows.map(r => r.kl))), { yMin: 0 });
lineChart(prefix + "cos", labels, sets.map(s => mset(s.label, s.color, s.rows.map(r => r.cos))));
lineChart(prefix + "p", labels, sets.map(s => mset(s.label, s.color, s.rows.map(r => r.pv))), { yMin: 0, yMax: 1, alpha: true });
}
// ---------- tab 1 ----------
let t1last = null;
function runT1() {
const g = globals(), tc = $("t1tc").value, model = $("t1nm").value, basis = resolveBasis(state.t1basis, model);
const ops = TEST_CASES[tc].ops, levels = levelsOf(g.maxNoise, g.step);
const rows = runSweep({ ops, tcKey: tc, model, basis, levels, shots: g.shots, seed: g.seed, chiMode: g.chiMode });
t1last = { rows, meta: { tc, model, basis }, g };
$("t1desc").innerHTML = `<b>${tc}</b> <code>${opsStr(ops)}</code> ยท ${NOISE[model].label} (${NOISE[model].sym}) ยท <b>${basis}-๊ธฐ์ €</b> ์ธก์ •<br><span class="muted">${TC_DESC[tc]}</span>`;
$("t1circ").innerHTML = drawCircuit(ops, basis);
$("t1kpi").innerHTML = kpis(rows);
lineChart("t1dist", levels, distDatasets(rows), { yMin: 0, yMax: 0.6 });
metricCharts("t1", levels, [{ label: NOISE[model].label, color: modelColor(model), rows }]);
$("t1table").innerHTML = rowsTable(rows);
}
$("t1csv").onclick = () => t1last && download(`${t1last.meta.tc}_${t1last.meta.model}_${t1last.meta.basis}.csv`, csvRows(t1last.rows, t1last.meta, t1last.g));
// ---------- tab 2 ----------
let t2last = null;
function runT2() {
const g = globals(), tc = $("t2tc").value, ops = TEST_CASES[tc].ops, levels = levelsOf(g.maxNoise, g.step);
$("t2circ").innerHTML = drawCircuit(ops, "Z");
const sets = MODEL_KEYS.map((m, i) => {
const basis = NOISE[m].basis;
const rows = runSweep({ ops, tcKey: tc, model: m, basis, levels, shots: g.shots, seed: g.seed, chiMode: g.chiMode });
lineChart("t2d" + i, levels, distDatasets(rows), { yMin: 0, yMax: 0.6 });
return { model: m, basis, label: NOISE[m].label, color: modelColor(m), rows };
});
metricCharts("t2", levels, sets);
t2last = { tc, sets, g };
$("t2table").innerHTML = sets.map(s => `<h4 style="margin:14px 0 4px">${s.label} <span class="muted small">${s.basis}-๊ธฐ์ €</span></h4>${rowsTable(s.rows)}`).join("");
}
$("t2csv").onclick = () => t2last && download(`${t2last.tc}_all_models.csv`,
t2last.sets.flatMap(s => csvRows(s.rows, { tc: t2last.tc, model: s.model, basis: s.basis }, t2last.g)));
$("allcsv").onclick = () => {
const g = globals(), levels = levelsOf(g.maxNoise, g.step), lines = [];
for (const m of MODEL_KEYS) for (const basis of ["Z", "X"]) for (const tc of TC_KEYS)
lines.push(...csvRows(runSweep({ ops: TEST_CASES[tc].ops, tcKey: tc, model: m, basis, levels, shots: g.shots, seed: g.seed, chiMode: g.chiMode }),
{ tc, model: m, basis }, g));
download(`quantum_metamorphic_results_seed${g.seed}.csv`, lines);
};
// ---------- tab 3 ----------
const EXAMPLES = ["x0 h0 cx01", "h0 cx01 s0", "h1 cx10", "h0 cx01 h0", "h0 cx01 swap01", "x1 h0 cx01", "h0 cx01 z0 z1"];
$("t3ex").innerHTML = EXAMPLES.map(e => `<button class="chip mono" data-e="${e}">${e}</button>`).join("");
$("t3ex").querySelectorAll("button").forEach(b => b.onclick = () => { $("t3seq").value = b.dataset.e; runT3(); });
function runT3() {
let ops;
try { ops = parseOps($("t3seq").value); $("t3err").textContent = ""; }
catch (e) { $("t3err").textContent = e.message; return; }
const g = globals(), model = $("t3nm").value, basis = resolveBasis(state.t3basis, model), levels = levelsOf(g.maxNoise, g.step);
const ideal = b => exactProbs(ops, b, model, 0), ref = b => exactProbs(TEST_CASES.TC1.ops, b, model, 0);
const same = b => ideal(b).every((v, i) => Math.abs(v - ref(b)[i]) < 1e-9);
const fmt = b => ideal(b).map((v, i) => `|${STATES[i]}โŸฉ ${fx(v, 3)}`).join(", ");
const okZ = same("Z"), okX = same("X"), okB = same(basis);
let cls = "note", msg;
if (okZ && okX) msg = "โœ… ZยทX ๋‘ ๊ธฐ์ € ๋ชจ๋‘์—์„œ ์ด์ƒ์  ์ถœ๋ ฅ์ด TC1๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค โ†’ ์œ ํšจํ•œ ํŒ”๋กœ์šฐ์—… ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค์ž…๋‹ˆ๋‹ค.";
else if (okB) { cls = "note warn"; msg = `โš ๏ธ ์ง€๊ธˆ ์ธก์ •ํ•˜๋Š” ${basis}-๊ธฐ์ €์—์„œ๋Š” TC1๊ณผ ๊ฐ™์ง€๋งŒ, ${basis === "Z" ? "X" : "Z"}-๊ธฐ์ €์—์„œ๋Š” ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ์ธก์ • ๊ธฐ์ €์— ๋”ฐ๋ผ MR ์„ฑ๋ฆฝ ์—ฌ๋ถ€๊ฐ€ ๋‹ฌ๋ผ์ง€๋Š” ๋ณ€ํ˜•์ž…๋‹ˆ๋‹ค.`; }
else { cls = "note bad"; msg = `โŒ ${basis}-๊ธฐ์ €์—์„œ ๋…ธ์ด์ฆˆ๊ฐ€ ์—†์–ด๋„ TC1๊ณผ ์ถœ๋ ฅ ๋ถ„ํฌ๊ฐ€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค โ†’ ์ด ์ธก์ • ์กฐ๊ฑด์—์„œ๋Š” ํŒ”๋กœ์šฐ์—… ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๊ฐ€ ๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. (์•„๋ž˜ ์ง€ํ‘œ๋Š” ์ฐธ๊ณ ์šฉ)`; }
$("t3verdict").innerHTML = `<div class="${cls}"><div><code>${opsStr(ops)}</code></div>
<div class="small">์ด์ƒ์  ๋ถ„ํฌ Z-๊ธฐ์ €: ${fmt("Z")} ${okZ ? "โœ“" : "โœ—"}<br>์ด์ƒ์  ๋ถ„ํฌ X-๊ธฐ์ €: ${fmt("X")} ${okX ? "โœ“" : "โœ—"}</div><div style="margin-top:6px">${msg}</div></div>`;
$("t3circ").innerHTML = drawCircuit(ops, basis);
const rows = runSweep({ ops, tcKey: "custom", model, basis, levels, shots: g.shots, seed: g.seed, chiMode: g.chiMode });
$("t3kpi").innerHTML = kpis(rows);
lineChart("t3dist", levels, distDatasets(rows), { yMin: 0, yMax: 1 });
metricCharts("t3", levels, [{ label: NOISE[model].label, color: modelColor(model), rows }]);
}
let t3timer; $("t3seq").addEventListener("input", () => { clearTimeout(t3timer); t3timer = setTimeout(runT3, 350); });
// ---------- wiring ----------
let current = "t1";
const runners = { t1: runT1, t2: runT2, t3: runT3, t4: () => {} };
function showTab(t) {
current = t;
$("tabs").querySelectorAll("button").forEach(b => b.classList.toggle("on", b.dataset.t === t));
document.querySelectorAll("section.tab").forEach(s => s.classList.toggle("on", s.id === t));
runners[t]();
}
$("tabs").querySelectorAll("button").forEach(b => b.onclick = () => showTab(b.dataset.t));
const rerun = () => runners[current]();
$("maxNoise").addEventListener("input", () => { $("maxNoiseV").textContent = (+$("maxNoise").value).toFixed(2); });
["maxNoise", "step", "shots", "seed"].forEach(id => $(id).addEventListener("change", rerun));
["t1tc", "t1nm"].forEach(id => $(id).addEventListener("change", runT1));
$("t2tc").addEventListener("change", runT2); $("t3nm").addEventListener("change", runT3);
segInit("chiMode", "chiMode", rerun); segInit("t1basis", "t1basis", runT1); segInit("t3basis", "t3basis", runT3);
document.querySelectorAll("[data-go]").forEach(b => b.onclick = () => {
const o = JSON.parse(b.dataset.go);
if (o.chi) segSet("chiMode", "chiMode", o.chi);
if (o.tab === "t1") { $("t1tc").value = o.tc; $("t1nm").value = o.nm; segSet("t1basis", "t1basis", o.basis); }
if (o.tab === "t2") $("t2tc").value = o.tc;
if (o.tab === "t3") { $("t3seq").value = o.seq; $("t3nm").value = o.nm; segSet("t3basis", "t3basis", o.basis); }
showTab(o.tab); window.scrollTo({ top: 0, behavior: "smooth" });
});
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", rerun);
showTab("t1");
</script>
</body>
</html>