Below is a ladder. Each rung adds one more quantifier alternation to a Boolean question. Level Σ1 asks "does there exist an x that makes the formula true?" — that is plain NP. Level Σ2 asks "does there exist an x such that for all y …?". Level Σ3 adds another "there exists z …", and so on.
<p class="hint">Climb the hierarchy. Each level adds one quantifier block to a fixed Boolean formula over the variables <b>a b c</b>. <b>∃</b> means “there exists a value”, <b>∀</b> means “for all values”. <b>Solve</b> answers the current question by brute force and shows how many formula evaluations it took.</p>
<div class="quant" id="quant">∃ a . F(a,b,c)</div>
<div class="formula">F = (a OR b) AND (NOT b OR c) AND (a OR NOT c)</div>
<div class="status" id="status">Press Solve to answer this question.</div>
<div class="meter" id="meter"></div>
<div class="btns">
<button id="climb" type="button">Climb a level ↑</button>
<button id="solve" type="button">Solve</button>
<button id="reset" type="button" class="ghost">Reset to Σ₁</button>
</div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .7rem; line-height: 1.45; }
.quant { font: 700 22px ui-monospace, monospace; color: #1d3557; background: #e8eef3;
border: 1px solid #cdd9e3; border-radius: 10px; padding: .7rem .9rem; margin: .3rem 0;
overflow-x: auto; white-space: nowrap; }
.formula { font: 600 14px ui-monospace, monospace; color: #555; margin: 0 0 .6rem; }
.status { font-size: 1rem; font-weight: 600; margin: .5rem 0; min-height: 1.4em; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
.meter { display: flex; gap: 3px; flex-wrap: wrap; margin: .3rem 0 .7rem; }
.meter .tick { width: 9px; height: 18px; background: #1d3557; border-radius: 2px; opacity: .85; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
button:disabled { opacity: .4; cursor: default; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// The polynomial hierarchy by hand. We fix a Boolean formula F over a,b,c and
// ask quantified questions like exists a . forall b . exists c . F.
// Sigma_k = k alternating blocks STARTING with "exists". Climbing adds one block.
// Answering means evaluating F at every leaf of the quantifier tree: each new
// block multiplies the number of evaluations by 2 (one variable per block here),
// which is exactly why one extra alternation makes the search blow up.
var VARS = ['a','b','c'];
// F(a,b,c) = (a OR b) AND (NOT b OR c) AND (a OR NOT c)
function F(a,b,c){ return (a||b) && (!b||c) && (a|| !c); }
var evalF = function(env){ return F(env.a, env.b, env.c); };
var level = 1; // current Sigma level (1..3)
var evals = 0; // formula evaluations in the last Solve
// Quantifier for block i (0-based): even -> exists, odd -> forall (Sigma starts at exists).
function quantOf(i){ return (i % 2 === 0) ? 'E' : 'A'; }
// Recursively evaluate the quantified formula over the first `level` variables.
// Variables beyond `level` are existentially closed so F is fully determined
// (keeps the toy self-contained; the alternation structure is what matters).
function evaluate(idx, env){
if (idx === level){
// remaining vars (if any) existentially closed
return closeRest(level, env);
}
var q = quantOf(idx), v = VARS[idx];
var t = Object.assign({}, env); t[v] = true; var rt = evaluate(idx+1, t);
var f = Object.assign({}, env); f[v] = false; var rf = evaluate(idx+1, f);
return (q === 'E') ? (rt || rf) : (rt && rf);
}
// Existentially close any variables past the quantified prefix.
function closeRest(start, env){
if (start === VARS.length){ evals++; return evalF(env); }
var v = VARS[start];
var t = Object.assign({}, env); t[v] = true;
var f = Object.assign({}, env); f[v] = false;
return closeRest(start+1, t) || closeRest(start+1, f);
}
function quantString(){
var s = '';
for (var i=0;i<level;i++){ s += (quantOf(i)==='E'?'∃':'∀') + ' ' + VARS[i] + ' . '; }
return s + 'F(a,b,c)';
}
function render(){
document.getElementById('quant').textContent = quantString();
document.getElementById('climb').disabled = (level >= VARS.length);
}
function setStatus(t,cls){ var s=document.getElementById('status'); s.textContent=t; s.className='status '+cls; }
function drawMeter(n){
var m=document.getElementById('meter'); m.innerHTML='';
var cap=Math.min(n,64);
for(var i=0;i<cap;i++){ var d=document.createElement('div'); d.className='tick'; m.appendChild(d); }
}
function solve(){
evals = 0;
var ans = evaluate(0, {});
drawMeter(evals);
var label = (level===1?'Σ₁ (= NP)': level===2?'Σ₂':'Σ₃');
setStatus(fmt('%v% — %label% answered in %evals% formula evaluations.', { v: ans?'TRUE':'FALSE', label: label, evals: evals }),
ans?'ok':'bad');
console.log('level', level, 'answer', ans, 'evals', evals);
}
function climb(){ if(level<VARS.length){ level++; render(); setStatus('Added one alternation — now press Solve.',''); document.getElementById('meter').innerHTML=''; } }
function reset(){ level=1; render(); setStatus('Press Solve to answer this question.',''); document.getElementById('meter').innerHTML=''; }
document.getElementById('climb').addEventListener('click', climb);
document.getElementById('solve').addEventListener('click', solve);
document.getElementById('reset').addEventListener('click', reset);
render();
<p class="hint">Climb the hierarchy. Each level adds one quantifier block to a fixed Boolean formula over the variables <b>a b c</b>. <b>∃</b> means “there exists a value”, <b>∀</b> means “for all values”. <b>Solve</b> answers the current question by brute force and shows how many formula evaluations it took.</p>
<div class="quant" id="quant">∃ a . F(a,b,c)</div>
<div class="formula">F = (a OR b) AND (NOT b OR c) AND (a OR NOT c)</div>
<div class="status" id="status">Press Solve to answer this question.</div>
<div class="meter" id="meter"></div>
<div class="btns">
<button id="climb" type="button">Climb a level ↑</button>
<button id="solve" type="button">Solve</button>
<button id="reset" type="button" class="ghost">Reset to Σ₁</button>
</div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .7rem; line-height: 1.45; }
.quant { font: 700 22px ui-monospace, monospace; color: #1d3557; background: #e8eef3;
border: 1px solid #cdd9e3; border-radius: 10px; padding: .7rem .9rem; margin: .3rem 0;
overflow-x: auto; white-space: nowrap; }
.formula { font: 600 14px ui-monospace, monospace; color: #555; margin: 0 0 .6rem; }
.status { font-size: 1rem; font-weight: 600; margin: .5rem 0; min-height: 1.4em; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
.meter { display: flex; gap: 3px; flex-wrap: wrap; margin: .3rem 0 .7rem; }
.meter .tick { width: 9px; height: 18px; background: #1d3557; border-radius: 2px; opacity: .85; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
button:disabled { opacity: .4; cursor: default; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// The polynomial hierarchy by hand. We fix a Boolean formula F over a,b,c and
// ask quantified questions like exists a . forall b . exists c . F.
// Sigma_k = k alternating blocks STARTING with "exists". Climbing adds one block.
// Answering means evaluating F at every leaf of the quantifier tree: each new
// block multiplies the number of evaluations by 2 (one variable per block here),
// which is exactly why one extra alternation makes the search blow up.
var VARS = ['a','b','c'];
// F(a,b,c) = (a OR b) AND (NOT b OR c) AND (a OR NOT c)
function F(a,b,c){ return (a||b) && (!b||c) && (a|| !c); }
var evalF = function(env){ return F(env.a, env.b, env.c); };
var level = 1; // current Sigma level (1..3)
var evals = 0; // formula evaluations in the last Solve
// Quantifier for block i (0-based): even -> exists, odd -> forall (Sigma starts at exists).
function quantOf(i){ return (i % 2 === 0) ? 'E' : 'A'; }
// Recursively evaluate the quantified formula over the first `level` variables.
// Variables beyond `level` are existentially closed so F is fully determined
// (keeps the toy self-contained; the alternation structure is what matters).
function evaluate(idx, env){
if (idx === level){
// remaining vars (if any) existentially closed
return closeRest(level, env);
}
var q = quantOf(idx), v = VARS[idx];
var t = Object.assign({}, env); t[v] = true; var rt = evaluate(idx+1, t);
var f = Object.assign({}, env); f[v] = false; var rf = evaluate(idx+1, f);
return (q === 'E') ? (rt || rf) : (rt && rf);
}
// Existentially close any variables past the quantified prefix.
function closeRest(start, env){
if (start === VARS.length){ evals++; return evalF(env); }
var v = VARS[start];
var t = Object.assign({}, env); t[v] = true;
var f = Object.assign({}, env); f[v] = false;
return closeRest(start+1, t) || closeRest(start+1, f);
}
function quantString(){
var s = '';
for (var i=0;i<level;i++){ s += (quantOf(i)==='E'?'∃':'∀') + ' ' + VARS[i] + ' . '; }
return s + 'F(a,b,c)';
}
function render(){
document.getElementById('quant').textContent = quantString();
document.getElementById('climb').disabled = (level >= VARS.length);
}
function setStatus(t,cls){ var s=document.getElementById('status'); s.textContent=t; s.className='status '+cls; }
function drawMeter(n){
var m=document.getElementById('meter'); m.innerHTML='';
var cap=Math.min(n,64);
for(var i=0;i<cap;i++){ var d=document.createElement('div'); d.className='tick'; m.appendChild(d); }
}
function solve(){
evals = 0;
var ans = evaluate(0, {});
drawMeter(evals);
var label = (level===1?'Σ₁ (= NP)': level===2?'Σ₂':'Σ₃');
setStatus(fmt('%v% — %label% answered in %evals% formula evaluations.', { v: ans?'TRUE':'FALSE', label: label, evals: evals }),
ans?'ok':'bad');
console.log('level', level, 'answer', ans, 'evals', evals);
}
function climb(){ if(level<VARS.length){ level++; render(); setStatus('Added one alternation — now press Solve.',''); document.getElementById('meter').innerHTML=''; } }
function reset(){ level=1; render(); setStatus('Press Solve to answer this question.',''); document.getElementById('meter').innerHTML=''; }
document.getElementById('climb').addEventListener('click', climb);
document.getElementById('solve').addEventListener('click', solve);
document.getElementById('reset').addEventListener('click', reset);
render();
Press Climb a level to add an alternation, then Solve to let the computer answer the current question by brute force. Watch the counter: each new quantifier block multiplies the work, because the machine must check every value of the new variables nested inside everything before it. One extra alternation can turn a quick search into an exhausting one — and that growth is exactly what the hierarchy measures.
Comments
Loading comments...