Download index.html from greatkoko/quantum-metamorphic-testing: direct link, hf CLI and curl.
- Browser
- Download file 48.2 kB
-
https://huggingface.co/spaces/greatkoko/quantum-metamorphic-testing/resolve/main/index.html
- Command line
-
hf download hf://spaces/greatkoko/quantum-metamorphic-testing/index.html
-
curl -L -o index.html https://huggingface.co/spaces/greatkoko/quantum-metamorphic-testing/resolve/main/index.html
48.2 kB
| <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 > 0.5 ยท MR ์ ์ง</span> | |
| <span class="badge b-uncertain">0.05 < 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> | |