<div class="hint">
<b>AM Protocol for Graph Non-Isomorphism.</b> Arthur picks G<sub>0</sub> or G<sub>1</sub> at random, scrambles its vertices, and sends H to Merlin. Merlin identifies which graph H came from. If the graphs are non-isomorphic, Merlin always succeeds.
</div>
<div class="graphs-row">
<div class="graph-panel">
<div class="graph-label">G<sub>0</sub></div>
<canvas id="canvas0" width="140" height="140"></canvas>
</div>
<div class="graph-panel">
<div class="graph-label">G<sub>1</sub></div>
<canvas id="canvas1" width="140" height="140"></canvas>
</div>
<div class="graph-panel">
<div class="graph-label">H (scrambled)</div>
<canvas id="canvasH" width="140" height="140"></canvas>
</div>
</div>
<div class="controls">
<button id="btnRun" type="button">Run one round</button>
<button id="btnToggle" type="button" class="ghost">Switch to isomorphic graphs</button>
<button id="btnReset" type="button" class="ghost">Reset</button>
</div>
<div id="log" class="log"></div>
<div class="legend">
<span class="badge ok">โ Correct</span> Merlin identified the right graph.
<span class="badge bad">โ Wrong</span> Merlin guessed incorrectly (only possible if graphs are isomorphic).
</div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; font-size: 14px; }
.hint { font-size: .88rem; color: #444; margin: 0 0 .8rem; line-height: 1.5; }
.graphs-row { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: .7rem; }
.graph-panel { display: flex; flex-direction: column; align-items: center; }
.graph-label { font-size: .8rem; font-weight: 700; color: #1d3557; margin-bottom: 2px; }
canvas { border: 1px solid #cdd9e3; border-radius: 8px; background: #f5f8fa; }
.controls { display: flex; gap: .4rem; flex-wrap: wrap; margin-bottom: .6rem; }
button { font: 600 13px system-ui; padding: .4rem .85rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
.log { font-size: .85rem; max-height: 130px; overflow-y: auto;
border: 1px solid #e0e6ed; border-radius: 8px; padding: .5rem .7rem;
background: #f9fbfc; line-height: 1.6; }
.log-entry { padding: 1px 0; }
.log-entry.ok { color: #0a7d33; }
.log-entry.bad { color: #c92f3c; }
.legend { font-size: .8rem; color: #555; margin-top: .5rem; display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.badge { font-size: .75rem; font-weight: 700; padding: 1px 6px; border-radius: 4px; }
.badge.ok { background: #d1fae5; color: #065f46; }
.badge.bad { background: #fee2e2; color: #991b1b; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// โโ Graph definitions โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// NON-ISOMORPHIC pair: G0 = path P4 (degree seq 1,2,2,1), G1 = star K_{1,3}+isolated (degree seq 0,1,1,1,3)
// We use 4-node graphs for clarity. G0: cycle C4 (all degree 2). G1: path P4 with one extra edge (degree seq 1,2,3,2,2 โ wait, let's keep it simple)
// G0 = cycle C4: nodes 0-3, edges 0-1,1-2,2-3,3-0. Degree seq: [2,2,2,2]
// G1 = star K_{1,3}: node 0 center, edges 0-1,0-2,0-3. Degree seq: [3,1,1,1]
// These are clearly non-isomorphic.
const GRAPHS = {
nonIso: {
label: 'Non-isomorphic',
g0: { n: 4, edges: [[0,1],[1,2],[2,3],[3,0]] }, // C4 cycle
g1: { n: 4, edges: [[0,1],[0,2],[0,3]] } // K_{1,3} star
},
iso: {
label: 'Isomorphic',
g0: { n: 4, edges: [[0,1],[1,2],[2,3]] }, // P4 path
g1: { n: 4, edges: [[0,1],[0,2],[0,3],[3,4]] } // relabeled P4 โ actually we just use same graph
// For simplicity: both g0 and g1 = C4 (isomorphic to each other)
}
};
// Override iso graphs to be truly isomorphic: both = C4
GRAPHS.iso.g0 = { n: 4, edges: [[0,1],[1,2],[2,3],[3,0]] };
GRAPHS.iso.g1 = { n: 4, edges: [[0,1],[1,2],[2,3],[3,0]] };
let mode = 'nonIso';
let round = 0, correct = 0;
// โโ Layout helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function positions(n, cx, cy, r) {
return Array.from({length: n}, (_, i) => {
const a = (2 * Math.PI * i / n) - Math.PI / 2;
return [cx + r * Math.cos(a), cy + r * Math.sin(a)];
});
}
function drawGraph(canvas, graph, perm, highlight) {
const ctx = canvas.getContext('2d');
const W = canvas.width, H = canvas.height;
ctx.clearRect(0, 0, W, H);
const pos = positions(graph.n, W/2, H/2, W*0.35);
// Apply permutation: pos[perm[i]] is where node i is drawn
const permPos = perm ? pos.map((_, i) => pos[perm[i]]) : pos;
// Edges
ctx.strokeStyle = '#6b8fa8'; ctx.lineWidth = 1.8;
for (const [a, b] of graph.edges) {
ctx.beginPath();
ctx.moveTo(permPos[a][0], permPos[a][1]);
ctx.lineTo(permPos[b][0], permPos[b][1]);
ctx.stroke();
}
// Nodes
for (let i = 0; i < graph.n; i++) {
const [x, y] = permPos[i];
ctx.beginPath(); ctx.arc(x, y, 9, 0, 2*Math.PI);
ctx.fillStyle = highlight ? '#1d3557' : '#3a8dbe';
ctx.fill();
ctx.strokeStyle = '#fff'; ctx.lineWidth = 2; ctx.stroke();
ctx.fillStyle = '#fff'; ctx.font = 'bold 10px system-ui';
ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
ctx.fillText(i, x, y);
}
}
// โโ AM Protocol simulation โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function randPerm(n) {
const a = Array.from({length: n}, (_, i) => i);
for (let i = n-1; i > 0; i--) {
const j = Math.floor(Math.random() * (i+1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
}
// Compute degree sequence of a graph (sorted ascending) as fingerprint
function degSeq(graph, n) {
const deg = new Array(n).fill(0);
for (const [a, b] of graph.edges) { deg[a]++; deg[b]++; }
return deg.sort((x, y) => x - y).join(',');
}
// Merlin's strategy: compare degree sequence of H with G0 and G1.
// For non-isomorphic graphs this always gives the right answer.
// For isomorphic graphs, deg sequences match both, so Merlin picks randomly.
function merlinAnswer(graphChosen, perm, g0, g1) {
// Build permuted graph edges
const hEdges = graphChosen.edges.map(([a,b]) => [perm[a], perm[b]]);
const hGraph = { n: graphChosen.n, edges: hEdges };
const hDeg = degSeq(hGraph, hGraph.n);
const d0 = degSeq(g0, g0.n);
const d1 = degSeq(g1, g1.n);
if (hDeg === d0 && hDeg !== d1) return 0;
if (hDeg === d1 && hDeg !== d0) return 1;
// Ambiguous (isomorphic case): guess randomly
return Math.random() < 0.5 ? 0 : 1;
}
function runRound() {
const { g0, g1 } = GRAPHS[mode];
// Arthur: pick b in {0,1} at random, permute G_b
const b = Math.random() < 0.5 ? 0 : 1;
const chosen = b === 0 ? g0 : g1;
const perm = randPerm(chosen.n);
// Draw H
drawGraph(document.getElementById('canvasH'), chosen, perm, false);
// Merlin answers
const answer = merlinAnswer(chosen, perm, g0, g1);
const isCorrect = answer === b;
round++;
if (isCorrect) correct++;
const logEl = document.getElementById('log');
const entry = document.createElement('div');
entry.className = 'log-entry ' + (isCorrect ? 'ok' : 'bad');
entry.textContent = fmt('Round %round%: Arthur chose G%b%, Merlin said G%answer% โ %v% (%correct%/%round% correct)', { round: round, b: b, answer: answer, v: isCorrect ? 'โ Correct' : 'โ Wrong', correct: correct });
logEl.appendChild(entry);
logEl.scrollTop = logEl.scrollHeight;
}
function init() {
const { g0, g1 } = GRAPHS[mode];
drawGraph(document.getElementById('canvas0'), g0, null, false);
drawGraph(document.getElementById('canvas1'), g1, null, false);
const ctx = document.getElementById('canvasH').getContext('2d');
ctx.clearRect(0, 0, 140, 140);
ctx.fillStyle = '#aaa'; ctx.font = '12px system-ui';
ctx.textAlign = 'center'; ctx.fillText('(waiting)', 70, 74);
}
document.getElementById('btnRun').addEventListener('click', runRound);
document.getElementById('btnReset').addEventListener('click', () => {
round = 0; correct = 0;
document.getElementById('log').innerHTML = '';
init();
});
document.getElementById('btnToggle').addEventListener('click', () => {
mode = mode === 'nonIso' ? 'iso' : 'nonIso';
const btn = document.getElementById('btnToggle');
btn.textContent = mode === 'nonIso' ? 'Switch to isomorphic graphs' : 'Switch to non-isomorphic graphs';
round = 0; correct = 0;
document.getElementById('log').innerHTML = '';
init();
});
init();
<div class="hint">
<b>AM Protocol for Graph Non-Isomorphism.</b> Arthur picks G<sub>0</sub> or G<sub>1</sub> at random, scrambles its vertices, and sends H to Merlin. Merlin identifies which graph H came from. If the graphs are non-isomorphic, Merlin always succeeds.
</div>
<div class="graphs-row">
<div class="graph-panel">
<div class="graph-label">G<sub>0</sub></div>
<canvas id="canvas0" width="140" height="140"></canvas>
</div>
<div class="graph-panel">
<div class="graph-label">G<sub>1</sub></div>
<canvas id="canvas1" width="140" height="140"></canvas>
</div>
<div class="graph-panel">
<div class="graph-label">H (scrambled)</div>
<canvas id="canvasH" width="140" height="140"></canvas>
</div>
</div>
<div class="controls">
<button id="btnRun" type="button">Run one round</button>
<button id="btnToggle" type="button" class="ghost">Switch to isomorphic graphs</button>
<button id="btnReset" type="button" class="ghost">Reset</button>
</div>
<div id="log" class="log"></div>
<div class="legend">
<span class="badge ok">โ Correct</span> Merlin identified the right graph.
<span class="badge bad">โ Wrong</span> Merlin guessed incorrectly (only possible if graphs are isomorphic).
</div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; font-size: 14px; }
.hint { font-size: .88rem; color: #444; margin: 0 0 .8rem; line-height: 1.5; }
.graphs-row { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: .7rem; }
.graph-panel { display: flex; flex-direction: column; align-items: center; }
.graph-label { font-size: .8rem; font-weight: 700; color: #1d3557; margin-bottom: 2px; }
canvas { border: 1px solid #cdd9e3; border-radius: 8px; background: #f5f8fa; }
.controls { display: flex; gap: .4rem; flex-wrap: wrap; margin-bottom: .6rem; }
button { font: 600 13px system-ui; padding: .4rem .85rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
.log { font-size: .85rem; max-height: 130px; overflow-y: auto;
border: 1px solid #e0e6ed; border-radius: 8px; padding: .5rem .7rem;
background: #f9fbfc; line-height: 1.6; }
.log-entry { padding: 1px 0; }
.log-entry.ok { color: #0a7d33; }
.log-entry.bad { color: #c92f3c; }
.legend { font-size: .8rem; color: #555; margin-top: .5rem; display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.badge { font-size: .75rem; font-weight: 700; padding: 1px 6px; border-radius: 4px; }
.badge.ok { background: #d1fae5; color: #065f46; }
.badge.bad { background: #fee2e2; color: #991b1b; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// โโ Graph definitions โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// NON-ISOMORPHIC pair: G0 = path P4 (degree seq 1,2,2,1), G1 = star K_{1,3}+isolated (degree seq 0,1,1,1,3)
// We use 4-node graphs for clarity. G0: cycle C4 (all degree 2). G1: path P4 with one extra edge (degree seq 1,2,3,2,2 โ wait, let's keep it simple)
// G0 = cycle C4: nodes 0-3, edges 0-1,1-2,2-3,3-0. Degree seq: [2,2,2,2]
// G1 = star K_{1,3}: node 0 center, edges 0-1,0-2,0-3. Degree seq: [3,1,1,1]
// These are clearly non-isomorphic.
const GRAPHS = {
nonIso: {
label: 'Non-isomorphic',
g0: { n: 4, edges: [[0,1],[1,2],[2,3],[3,0]] }, // C4 cycle
g1: { n: 4, edges: [[0,1],[0,2],[0,3]] } // K_{1,3} star
},
iso: {
label: 'Isomorphic',
g0: { n: 4, edges: [[0,1],[1,2],[2,3]] }, // P4 path
g1: { n: 4, edges: [[0,1],[0,2],[0,3],[3,4]] } // relabeled P4 โ actually we just use same graph
// For simplicity: both g0 and g1 = C4 (isomorphic to each other)
}
};
// Override iso graphs to be truly isomorphic: both = C4
GRAPHS.iso.g0 = { n: 4, edges: [[0,1],[1,2],[2,3],[3,0]] };
GRAPHS.iso.g1 = { n: 4, edges: [[0,1],[1,2],[2,3],[3,0]] };
let mode = 'nonIso';
let round = 0, correct = 0;
// โโ Layout helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function positions(n, cx, cy, r) {
return Array.from({length: n}, (_, i) => {
const a = (2 * Math.PI * i / n) - Math.PI / 2;
return [cx + r * Math.cos(a), cy + r * Math.sin(a)];
});
}
function drawGraph(canvas, graph, perm, highlight) {
const ctx = canvas.getContext('2d');
const W = canvas.width, H = canvas.height;
ctx.clearRect(0, 0, W, H);
const pos = positions(graph.n, W/2, H/2, W*0.35);
// Apply permutation: pos[perm[i]] is where node i is drawn
const permPos = perm ? pos.map((_, i) => pos[perm[i]]) : pos;
// Edges
ctx.strokeStyle = '#6b8fa8'; ctx.lineWidth = 1.8;
for (const [a, b] of graph.edges) {
ctx.beginPath();
ctx.moveTo(permPos[a][0], permPos[a][1]);
ctx.lineTo(permPos[b][0], permPos[b][1]);
ctx.stroke();
}
// Nodes
for (let i = 0; i < graph.n; i++) {
const [x, y] = permPos[i];
ctx.beginPath(); ctx.arc(x, y, 9, 0, 2*Math.PI);
ctx.fillStyle = highlight ? '#1d3557' : '#3a8dbe';
ctx.fill();
ctx.strokeStyle = '#fff'; ctx.lineWidth = 2; ctx.stroke();
ctx.fillStyle = '#fff'; ctx.font = 'bold 10px system-ui';
ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
ctx.fillText(i, x, y);
}
}
// โโ AM Protocol simulation โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function randPerm(n) {
const a = Array.from({length: n}, (_, i) => i);
for (let i = n-1; i > 0; i--) {
const j = Math.floor(Math.random() * (i+1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
}
// Compute degree sequence of a graph (sorted ascending) as fingerprint
function degSeq(graph, n) {
const deg = new Array(n).fill(0);
for (const [a, b] of graph.edges) { deg[a]++; deg[b]++; }
return deg.sort((x, y) => x - y).join(',');
}
// Merlin's strategy: compare degree sequence of H with G0 and G1.
// For non-isomorphic graphs this always gives the right answer.
// For isomorphic graphs, deg sequences match both, so Merlin picks randomly.
function merlinAnswer(graphChosen, perm, g0, g1) {
// Build permuted graph edges
const hEdges = graphChosen.edges.map(([a,b]) => [perm[a], perm[b]]);
const hGraph = { n: graphChosen.n, edges: hEdges };
const hDeg = degSeq(hGraph, hGraph.n);
const d0 = degSeq(g0, g0.n);
const d1 = degSeq(g1, g1.n);
if (hDeg === d0 && hDeg !== d1) return 0;
if (hDeg === d1 && hDeg !== d0) return 1;
// Ambiguous (isomorphic case): guess randomly
return Math.random() < 0.5 ? 0 : 1;
}
function runRound() {
const { g0, g1 } = GRAPHS[mode];
// Arthur: pick b in {0,1} at random, permute G_b
const b = Math.random() < 0.5 ? 0 : 1;
const chosen = b === 0 ? g0 : g1;
const perm = randPerm(chosen.n);
// Draw H
drawGraph(document.getElementById('canvasH'), chosen, perm, false);
// Merlin answers
const answer = merlinAnswer(chosen, perm, g0, g1);
const isCorrect = answer === b;
round++;
if (isCorrect) correct++;
const logEl = document.getElementById('log');
const entry = document.createElement('div');
entry.className = 'log-entry ' + (isCorrect ? 'ok' : 'bad');
entry.textContent = fmt('Round %round%: Arthur chose G%b%, Merlin said G%answer% โ %v% (%correct%/%round% correct)', { round: round, b: b, answer: answer, v: isCorrect ? 'โ Correct' : 'โ Wrong', correct: correct });
logEl.appendChild(entry);
logEl.scrollTop = logEl.scrollHeight;
}
function init() {
const { g0, g1 } = GRAPHS[mode];
drawGraph(document.getElementById('canvas0'), g0, null, false);
drawGraph(document.getElementById('canvas1'), g1, null, false);
const ctx = document.getElementById('canvasH').getContext('2d');
ctx.clearRect(0, 0, 140, 140);
ctx.fillStyle = '#aaa'; ctx.font = '12px system-ui';
ctx.textAlign = 'center'; ctx.fillText('(waiting)', 70, 74);
}
document.getElementById('btnRun').addEventListener('click', runRound);
document.getElementById('btnReset').addEventListener('click', () => {
round = 0; correct = 0;
document.getElementById('log').innerHTML = '';
init();
});
document.getElementById('btnToggle').addEventListener('click', () => {
mode = mode === 'nonIso' ? 'iso' : 'nonIso';
const btn = document.getElementById('btnToggle');
btn.textContent = mode === 'nonIso' ? 'Switch to isomorphic graphs' : 'Switch to non-isomorphic graphs';
round = 0; correct = 0;
document.getElementById('log').innerHTML = '';
init();
});
init();
Run the protocol several times. Notice: when the graphs are non-isomorphic, Merlin answers correctly every round. Switch to isomorphic graphs and Merlin's answers become random coin flips โ after just a few rounds, Arthur's confidence in a cheating Merlin collapses to nearly zero.
Comments
Loading comments...