The demo below simulates a #P counting oracle on small Boolean formulas. Pick a query type (NP, coNP, or Σ₂), enter a formula as a truth table over two variables, and press Ask the oracle. The oracle returns an exact count of satisfying assignments — from that single number you can read off the answer to any level of the hierarchy.
<p class="hint">
Select a <b>query type</b> and define a Boolean formula on 2 variables (A, B)
by checking which truth-table rows are satisfying. Then press
<b>Ask the oracle</b> — the #P oracle counts solutions and answers the query.
</p>
<div class="panel">
<label class="row-label">Query type:</label>
<select id="qtype">
<option value="np">NP — Does any assignment satisfy φ?</option>
<option value="conp">coNP — Do ALL assignments satisfy φ?</option>
<option value="sigma2">Σ₂ — Does some A make ALL B satisfy φ(A,B)?</option>
</select>
</div>
<div class="panel">
<label class="row-label">Formula φ (truth table over A, B):</label>
<table id="tt">
<thead><tr><th>A</th><th>B</th><th>φ(A,B) = 1?</th></tr></thead>
<tbody>
<tr><td>0</td><td>0</td><td><input type="checkbox" data-row="0" checked></td></tr>
<tr><td>0</td><td>1</td><td><input type="checkbox" data-row="1" checked></td></tr>
<tr><td>1</td><td>0</td><td><input type="checkbox" data-row="2" checked></td></tr>
<tr><td>1</td><td>1</td><td><input type="checkbox" data-row="3"></td></tr>
</tbody>
</table>
</div>
<div class="btns">
<button id="ask" type="button">Ask the oracle</button>
<button id="reset" type="button" class="ghost">Reset</button>
</div>
<div id="oracle-out" class="oracle-out" aria-live="polite"></div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; font-size: 15px; }
.hint { font-size: .88rem; color: #444; margin: 0 0 .8rem; line-height: 1.5; }
.panel { margin-bottom: .7rem; }
.row-label { display: block; font-weight: 600; margin-bottom: .3rem; font-size: .9rem; }
select { font: 14px system-ui; padding: .35rem .5rem; border: 1px solid #bbb; border-radius: 6px; width: 100%; max-width: 440px; }
table { border-collapse: collapse; margin-top: .2rem; }
th, td { padding: .3rem .7rem; text-align: center; border: 1px solid #cdd9e3; font: 14px ui-monospace, monospace; }
th { background: #e8eef3; font-weight: 700; }
td input[type=checkbox] { width: 18px; height: 18px; cursor: pointer; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; margin: .6rem 0; }
button { font: 600 14px system-ui; padding: .45rem .9rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
.oracle-out { margin-top: .5rem; padding: .7rem 1rem; border-radius: 10px; font-size: .95rem;
line-height: 1.6; min-height: 2.4rem; display: none; }
.oracle-out.show { display: block; }
.oracle-out.yes { background: #e6f4ea; border: 1px solid #a8d5b0; color: #1a5c2a; }
.oracle-out.no { background: #fdecea; border: 1px solid #f5aba3; color: #8b1a1a; }
.oracle-out .step { font-size: .82rem; color: #555; margin-top: .3rem; font-family: ui-monospace, monospace; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Toda's theorem demo: simulate a #P counting oracle on 2-variable Boolean formulas.
// The oracle counts satisfying assignments; from that count we answer NP, coNP, Σ2.
//
// Variables: A ∈ {0,1}, B ∈ {0,1}. Row index = A*2 + B.
// truth[] stores which rows are satisfying (φ(A,B) = 1).
const checks = document.querySelectorAll('input[type=checkbox]');
const qtSel = document.getElementById('qtype');
const out = document.getElementById('oracle-out');
function getTruth() {
const t = [false, false, false, false];
checks.forEach(cb => { t[+cb.dataset.row] = cb.checked; });
return t;
}
// #P oracle: count all assignments (A,B) where φ(A,B)=1.
function sharpP(truth) {
return truth.filter(Boolean).length; // 0..4
}
// NP: exists (A,B) s.t. φ(A,B) = 1 ➺ count > 0
function queryNP(truth) {
const cnt = sharpP(truth);
const ans = cnt > 0;
return {
steps: [
fmt('#P oracle returns count = %cnt%', { cnt: cnt }),
fmt('NP answer: count > 0 ? → %v%', { v: ans ? 'YES' : 'NO' })
],
ans
};
}
// coNP: for all (A,B), φ(A,B) = 1 ➺ count = 4 (all 2^2 rows satisfied)
function queryCoNP(truth) {
const cnt = sharpP(truth);
const ans = cnt === 4;
return {
steps: [
fmt('#P oracle returns count = %cnt%', { cnt: cnt }),
fmt('coNP answer: count = 4 (= 2²) ? → %v%', { v: ans ? 'YES' : 'NO' })
],
ans
};
}
// Σ₂: ∃A ∀B φ(A,B) = 1
// For each value of A (outer existential), count over B (inner universal).
// Toda: a single #P oracle suffices; here we show the two sub-calls for pedagogy.
function querySigma2(truth) {
// For A=0: rows 0 (B=0) and 1 (B=1)
const cntA0 = (truth[0] ? 1 : 0) + (truth[1] ? 1 : 0);
// For A=1: rows 2 (B=0) and 3 (B=1)
const cntA1 = (truth[2] ? 1 : 0) + (truth[3] ? 1 : 0);
// ∀B φ(0,B) ➺ cntA0 = 2; ∀B φ(1,B) ➺ cntA1 = 2
const allB_for_A0 = cntA0 === 2;
const allB_for_A1 = cntA1 === 2;
const ans = allB_for_A0 || allB_for_A1;
return {
steps: [
fmt('Oracle call A=0: count of satisfying B = %cntA0% → ∀B φ(0,B)? %v%', { cntA0: cntA0, v: allB_for_A0 ? 'YES' : 'NO' }),
fmt('Oracle call A=1: count of satisfying B = %cntA1% → ∀B φ(1,B)? %v%', { cntA1: cntA1, v: allB_for_A1 ? 'YES' : 'NO' }),
fmt('Σ₂ answer: some A makes all B satisfy φ? → %v%', { v: ans ? 'YES' : 'NO' })
],
ans
};
}
document.getElementById('ask').addEventListener('click', () => {
const truth = getTruth();
const qt = qtSel.value;
let result;
if (qt === 'np') result = queryNP(truth);
else if (qt === 'conp') result = queryCoNP(truth);
else result = querySigma2(truth);
out.className = 'oracle-out show ' + (result.ans ? 'yes' : 'no');
const stepsHtml = result.steps.map(s => '<div class="step">' + s + '</div>').join('');
out.innerHTML = fmt('<b>Oracle answer: %v%</b>%stepsHtml%', { v: result.ans ? '✓ YES' : '✗ NO', stepsHtml: stepsHtml });
console.log('Query:', qt, '| truth:', truth, '| answer:', result.ans, '| steps:', result.steps);
});
document.getElementById('reset').addEventListener('click', () => {
checks.forEach((cb, i) => { cb.checked = i < 3; }); // default: 0,1,2=true; 3=false
out.className = 'oracle-out';
out.innerHTML = '';
});
<p class="hint">
Select a <b>query type</b> and define a Boolean formula on 2 variables (A, B)
by checking which truth-table rows are satisfying. Then press
<b>Ask the oracle</b> — the #P oracle counts solutions and answers the query.
</p>
<div class="panel">
<label class="row-label">Query type:</label>
<select id="qtype">
<option value="np">NP — Does any assignment satisfy φ?</option>
<option value="conp">coNP — Do ALL assignments satisfy φ?</option>
<option value="sigma2">Σ₂ — Does some A make ALL B satisfy φ(A,B)?</option>
</select>
</div>
<div class="panel">
<label class="row-label">Formula φ (truth table over A, B):</label>
<table id="tt">
<thead><tr><th>A</th><th>B</th><th>φ(A,B) = 1?</th></tr></thead>
<tbody>
<tr><td>0</td><td>0</td><td><input type="checkbox" data-row="0" checked></td></tr>
<tr><td>0</td><td>1</td><td><input type="checkbox" data-row="1" checked></td></tr>
<tr><td>1</td><td>0</td><td><input type="checkbox" data-row="2" checked></td></tr>
<tr><td>1</td><td>1</td><td><input type="checkbox" data-row="3"></td></tr>
</tbody>
</table>
</div>
<div class="btns">
<button id="ask" type="button">Ask the oracle</button>
<button id="reset" type="button" class="ghost">Reset</button>
</div>
<div id="oracle-out" class="oracle-out" aria-live="polite"></div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; font-size: 15px; }
.hint { font-size: .88rem; color: #444; margin: 0 0 .8rem; line-height: 1.5; }
.panel { margin-bottom: .7rem; }
.row-label { display: block; font-weight: 600; margin-bottom: .3rem; font-size: .9rem; }
select { font: 14px system-ui; padding: .35rem .5rem; border: 1px solid #bbb; border-radius: 6px; width: 100%; max-width: 440px; }
table { border-collapse: collapse; margin-top: .2rem; }
th, td { padding: .3rem .7rem; text-align: center; border: 1px solid #cdd9e3; font: 14px ui-monospace, monospace; }
th { background: #e8eef3; font-weight: 700; }
td input[type=checkbox] { width: 18px; height: 18px; cursor: pointer; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; margin: .6rem 0; }
button { font: 600 14px system-ui; padding: .45rem .9rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
.oracle-out { margin-top: .5rem; padding: .7rem 1rem; border-radius: 10px; font-size: .95rem;
line-height: 1.6; min-height: 2.4rem; display: none; }
.oracle-out.show { display: block; }
.oracle-out.yes { background: #e6f4ea; border: 1px solid #a8d5b0; color: #1a5c2a; }
.oracle-out.no { background: #fdecea; border: 1px solid #f5aba3; color: #8b1a1a; }
.oracle-out .step { font-size: .82rem; color: #555; margin-top: .3rem; font-family: ui-monospace, monospace; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Toda's theorem demo: simulate a #P counting oracle on 2-variable Boolean formulas.
// The oracle counts satisfying assignments; from that count we answer NP, coNP, Σ2.
//
// Variables: A ∈ {0,1}, B ∈ {0,1}. Row index = A*2 + B.
// truth[] stores which rows are satisfying (φ(A,B) = 1).
const checks = document.querySelectorAll('input[type=checkbox]');
const qtSel = document.getElementById('qtype');
const out = document.getElementById('oracle-out');
function getTruth() {
const t = [false, false, false, false];
checks.forEach(cb => { t[+cb.dataset.row] = cb.checked; });
return t;
}
// #P oracle: count all assignments (A,B) where φ(A,B)=1.
function sharpP(truth) {
return truth.filter(Boolean).length; // 0..4
}
// NP: exists (A,B) s.t. φ(A,B) = 1 ➺ count > 0
function queryNP(truth) {
const cnt = sharpP(truth);
const ans = cnt > 0;
return {
steps: [
fmt('#P oracle returns count = %cnt%', { cnt: cnt }),
fmt('NP answer: count > 0 ? → %v%', { v: ans ? 'YES' : 'NO' })
],
ans
};
}
// coNP: for all (A,B), φ(A,B) = 1 ➺ count = 4 (all 2^2 rows satisfied)
function queryCoNP(truth) {
const cnt = sharpP(truth);
const ans = cnt === 4;
return {
steps: [
fmt('#P oracle returns count = %cnt%', { cnt: cnt }),
fmt('coNP answer: count = 4 (= 2²) ? → %v%', { v: ans ? 'YES' : 'NO' })
],
ans
};
}
// Σ₂: ∃A ∀B φ(A,B) = 1
// For each value of A (outer existential), count over B (inner universal).
// Toda: a single #P oracle suffices; here we show the two sub-calls for pedagogy.
function querySigma2(truth) {
// For A=0: rows 0 (B=0) and 1 (B=1)
const cntA0 = (truth[0] ? 1 : 0) + (truth[1] ? 1 : 0);
// For A=1: rows 2 (B=0) and 3 (B=1)
const cntA1 = (truth[2] ? 1 : 0) + (truth[3] ? 1 : 0);
// ∀B φ(0,B) ➺ cntA0 = 2; ∀B φ(1,B) ➺ cntA1 = 2
const allB_for_A0 = cntA0 === 2;
const allB_for_A1 = cntA1 === 2;
const ans = allB_for_A0 || allB_for_A1;
return {
steps: [
fmt('Oracle call A=0: count of satisfying B = %cntA0% → ∀B φ(0,B)? %v%', { cntA0: cntA0, v: allB_for_A0 ? 'YES' : 'NO' }),
fmt('Oracle call A=1: count of satisfying B = %cntA1% → ∀B φ(1,B)? %v%', { cntA1: cntA1, v: allB_for_A1 ? 'YES' : 'NO' }),
fmt('Σ₂ answer: some A makes all B satisfy φ? → %v%', { v: ans ? 'YES' : 'NO' })
],
ans
};
}
document.getElementById('ask').addEventListener('click', () => {
const truth = getTruth();
const qt = qtSel.value;
let result;
if (qt === 'np') result = queryNP(truth);
else if (qt === 'conp') result = queryCoNP(truth);
else result = querySigma2(truth);
out.className = 'oracle-out show ' + (result.ans ? 'yes' : 'no');
const stepsHtml = result.steps.map(s => '<div class="step">' + s + '</div>').join('');
out.innerHTML = fmt('<b>Oracle answer: %v%</b>%stepsHtml%', { v: result.ans ? '✓ YES' : '✗ NO', stepsHtml: stepsHtml });
console.log('Query:', qt, '| truth:', truth, '| answer:', result.ans, '| steps:', result.steps);
});
document.getElementById('reset').addEventListener('click', () => {
checks.forEach((cb, i) => { cb.checked = i < 3; }); // default: 0,1,2=true; 3=false
out.className = 'oracle-out';
out.innerHTML = '';
});
Notice: the oracle never searches, backtracks, or guesses. It returns one number. Yet that number is enough to decide membership in NP (count > 0?), coNP (count =2n?), or Σ2 (does any assignment make the sub-formula count nonzero?). The alternation that makes these problems hard dissolves into arithmetic.
Comments
Loading comments...