The standard reduction from SAT to 3-SAT is not an isomorphism — it is many-to-one. The demo below walks through what an isomorphism would need to preserve: it must be injective (no two inputs map to the same output), surjective (every 3-SAT formula is hit), and both directions must run in polynomial time.
<p class="hint">A polynomial isomorphism between two NP-complete problems must be <b>injective</b> (no collisions), <b>surjective</b> (every output is reachable), and <b>invertible in poly-time</b>. Toggle each requirement to see what happens when one is dropped.</p>
<div class="controls">
<label class="toggle-wrap"><input type="checkbox" id="inj" checked> Injective (no two inputs → same output)</label>
<label class="toggle-wrap"><input type="checkbox" id="sur" checked> Surjective (every 3-SAT formula is reachable)</label>
<label class="toggle-wrap"><input type="checkbox" id="inv" checked> Invertible in polynomial time</label>
</div>
<div class="panels">
<div class="panel" id="sat-panel">
<div class="panel-title">SAT formulas (domain)</div>
<div id="sat-list"></div>
</div>
<div class="arrow-col" id="arrow-col"></div>
<div class="panel" id="tsat-panel">
<div class="panel-title">3-SAT formulas (codomain)</div>
<div id="tsat-list"></div>
</div>
</div>
<div class="status" id="status"></div>
<div class="legend">
<span class="dot ok"></span>isomorphism property holds
<span class="dot bad"></span>property violated
</div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #1a1a2e; margin: 0; font-size: 14px; }
.hint { font-size: .88rem; color: #444; margin: 0 0 .7rem; line-height: 1.45; }
.controls { display: flex; flex-direction: column; gap: .35rem; margin-bottom: .9rem; }
.toggle-wrap { display: flex; align-items: center; gap: .5rem; cursor: pointer; font-size: .88rem; }
.toggle-wrap input { width: 16px; height: 16px; cursor: pointer; accent-color: #1d3557; }
.panels { display: flex; align-items: flex-start; gap: 0; }
.panel { flex: 1; border: 1.5px solid #cdd9e3; border-radius: 8px; overflow: hidden; min-width: 0; }
.panel-title { background: #1d3557; color: #fff; font-weight: 700; font-size: .82rem;
padding: .4rem .7rem; letter-spacing: .02em; }
.formula { padding: .38rem .7rem; font-family: ui-monospace, monospace; font-size: .82rem;
border-bottom: 1px solid #e4eaf0; transition: background .2s; cursor: default; }
.formula:last-child { border-bottom: none; }
.formula.highlight { background: #dff0e8; }
.formula.collision { background: #fde8e8; }
.formula.unreachable { opacity: .38; }
.arrow-col { display: flex; flex-direction: column; align-items: center; justify-content: flex-start;
min-width: 60px; padding-top: 2.2rem; gap: 0; }
.arrow { font-size: .75rem; color: #1d3557; font-weight: 600; text-align: center;
height: 32px; display: flex; align-items: center; justify-content: center;
transition: color .2s; white-space: nowrap; }
.arrow.bad { color: #c92f3c; }
.arrow.ok { color: #0a7d33; }
.status { margin: .65rem 0 .3rem; font-weight: 700; font-size: .93rem; min-height: 1.3em; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
.legend { font-size: .78rem; color: #555; display: flex; align-items: center; gap: .3rem; flex-wrap: wrap; }
.dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; }
.dot.ok { background: #0a7d33; }
.dot.bad { background: #c92f3c; }
// We work with 4 small SAT formulas and 4 small 3-SAT formulas.
// A "polynomial isomorphism" f: SAT -> 3SAT must be:
// injective : f is one-to-one (no two SAT formulas map to the same 3-SAT formula)
// surjective : f is onto (every 3-SAT formula is the image of some SAT formula)
// invertible : f^{-1} is also poly-time (here we just flag if a fake non-invertible
// padding is in use)
//
// The demo has one "base" bijective mapping and lets the user break each property independently to see what goes wrong.
const SAT_FORMULAS = [
'(x₁)',
'(x₁ ∨ x₂)',
'(x₁ ∨ x₂) ∧ (¬x₁)',
'(x₁ ∨ x₂ ∨ x₃) ∧ (¬x₂)',
];
const TSAT_FORMULAS = [
'(x₁ ∨ x₁ ∨ x₁)',
'(x₁ ∨ x₂ ∨ x₂)',
'(x₁ ∨ x₂ ∨ x₂) ∧ (¬x₁ ∨ ¬x₁ ∨ ¬x₁)',
'(x₁ ∨ x₂ ∨ x₃) ∧ (¬x₂ ∨ ¬x₂ ∨ ¬x₂)',
];
// Base bijection: index i in SAT maps to index i in TSAT (a true isomorphism)
const BASE_MAP = [0, 1, 2, 3];
function buildMapping(injective, surjective, invertible) {
// Start from the base bijection and deliberately break selected properties.
// Break injectivity: map index 1 and 2 both to TSAT index 1 (collision).
// Break surjectivity: index 3 in TSAT is never the image of any SAT formula.
// Break invertibility: add an unrecoverable padding note (cosmetic here).
let map = BASE_MAP.slice(); // copy
if (!injective) {
// map SAT[1] and SAT[2] both to TSAT[1]
map[1] = 1;
map[2] = 1; // collision
}
if (!surjective) {
// skip TSAT[3]: redirect SAT[3] to TSAT[0] instead
map[3] = 0;
}
return map;
}
function render() {
const inj = document.getElementById('inj').checked;
const sur = document.getElementById('sur').checked;
const inv = document.getElementById('inv').checked;
const mapping = buildMapping(inj, sur, inv);
// Which TSAT indices are hit?
const hitSet = new Set(mapping);
// Which TSAT indices are hit more than once?
const counts = [0,0,0,0];
mapping.forEach(t => counts[t]++);
// Render SAT list
const satList = document.getElementById('sat-list');
satList.innerHTML = '';
SAT_FORMULAS.forEach((f, i) => {
const div = document.createElement('div');
div.className = 'formula';
div.textContent = f;
satList.appendChild(div);
});
// Render arrows
const arrowCol = document.getElementById('arrow-col');
arrowCol.innerHTML = '';
mapping.forEach((t, i) => {
const div = document.createElement('div');
const isCollision = counts[t] > 1;
div.className = 'arrow' + (isCollision ? ' bad' : ' ok');
div.textContent = '→ ' + (t + 1);
arrowCol.appendChild(div);
});
// Render TSAT list
const tsatList = document.getElementById('tsat-list');
tsatList.innerHTML = '';
TSAT_FORMULAS.forEach((f, i) => {
const div = document.createElement('div');
let cls = 'formula';
if (hitSet.has(i)) {
cls += counts[i] > 1 ? ' collision' : ' highlight';
} else {
cls += ' unreachable';
}
div.className = cls;
div.textContent = f;
tsatList.appendChild(div);
});
// Determine overall status
const hasCollision = mapping.some((t, i) => mapping.indexOf(t) !== i);
const missingTarget = [0,1,2,3].some(t => !hitSet.has(t));
const notInv = !inv;
const problems = [];
if (!inj || hasCollision) problems.push('NOT injective (collision)');
if (!sur || missingTarget) problems.push('NOT surjective (unreachable formula)');
if (notInv) problems.push('NOT invertible in poly-time');
const statusEl = document.getElementById('status');
if (problems.length === 0) {
statusEl.textContent = '✓ Valid polynomial isomorphism: bijective + invertible';
statusEl.className = 'status ok';
} else {
statusEl.textContent = '✗ ' + problems.join(' · ');
statusEl.className = 'status bad';
}
// Log verification to console
console.log('Mapping:', mapping, '| Injective:', inj && !hasCollision, '| Surjective:', sur && !missingTarget, '| Invertible:', inv);
}
document.getElementById('inj').addEventListener('change', render);
document.getElementById('sur').addEventListener('change', render);
document.getElementById('inv').addEventListener('change', render);
render();
<p class="hint">A polynomial isomorphism between two NP-complete problems must be <b>injective</b> (no collisions), <b>surjective</b> (every output is reachable), and <b>invertible in poly-time</b>. Toggle each requirement to see what happens when one is dropped.</p>
<div class="controls">
<label class="toggle-wrap"><input type="checkbox" id="inj" checked> Injective (no two inputs → same output)</label>
<label class="toggle-wrap"><input type="checkbox" id="sur" checked> Surjective (every 3-SAT formula is reachable)</label>
<label class="toggle-wrap"><input type="checkbox" id="inv" checked> Invertible in polynomial time</label>
</div>
<div class="panels">
<div class="panel" id="sat-panel">
<div class="panel-title">SAT formulas (domain)</div>
<div id="sat-list"></div>
</div>
<div class="arrow-col" id="arrow-col"></div>
<div class="panel" id="tsat-panel">
<div class="panel-title">3-SAT formulas (codomain)</div>
<div id="tsat-list"></div>
</div>
</div>
<div class="status" id="status"></div>
<div class="legend">
<span class="dot ok"></span>isomorphism property holds
<span class="dot bad"></span>property violated
</div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #1a1a2e; margin: 0; font-size: 14px; }
.hint { font-size: .88rem; color: #444; margin: 0 0 .7rem; line-height: 1.45; }
.controls { display: flex; flex-direction: column; gap: .35rem; margin-bottom: .9rem; }
.toggle-wrap { display: flex; align-items: center; gap: .5rem; cursor: pointer; font-size: .88rem; }
.toggle-wrap input { width: 16px; height: 16px; cursor: pointer; accent-color: #1d3557; }
.panels { display: flex; align-items: flex-start; gap: 0; }
.panel { flex: 1; border: 1.5px solid #cdd9e3; border-radius: 8px; overflow: hidden; min-width: 0; }
.panel-title { background: #1d3557; color: #fff; font-weight: 700; font-size: .82rem;
padding: .4rem .7rem; letter-spacing: .02em; }
.formula { padding: .38rem .7rem; font-family: ui-monospace, monospace; font-size: .82rem;
border-bottom: 1px solid #e4eaf0; transition: background .2s; cursor: default; }
.formula:last-child { border-bottom: none; }
.formula.highlight { background: #dff0e8; }
.formula.collision { background: #fde8e8; }
.formula.unreachable { opacity: .38; }
.arrow-col { display: flex; flex-direction: column; align-items: center; justify-content: flex-start;
min-width: 60px; padding-top: 2.2rem; gap: 0; }
.arrow { font-size: .75rem; color: #1d3557; font-weight: 600; text-align: center;
height: 32px; display: flex; align-items: center; justify-content: center;
transition: color .2s; white-space: nowrap; }
.arrow.bad { color: #c92f3c; }
.arrow.ok { color: #0a7d33; }
.status { margin: .65rem 0 .3rem; font-weight: 700; font-size: .93rem; min-height: 1.3em; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
.legend { font-size: .78rem; color: #555; display: flex; align-items: center; gap: .3rem; flex-wrap: wrap; }
.dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; }
.dot.ok { background: #0a7d33; }
.dot.bad { background: #c92f3c; }
// We work with 4 small SAT formulas and 4 small 3-SAT formulas.
// A "polynomial isomorphism" f: SAT -> 3SAT must be:
// injective : f is one-to-one (no two SAT formulas map to the same 3-SAT formula)
// surjective : f is onto (every 3-SAT formula is the image of some SAT formula)
// invertible : f^{-1} is also poly-time (here we just flag if a fake non-invertible
// padding is in use)
//
// The demo has one "base" bijective mapping and lets the user break each property independently to see what goes wrong.
const SAT_FORMULAS = [
'(x₁)',
'(x₁ ∨ x₂)',
'(x₁ ∨ x₂) ∧ (¬x₁)',
'(x₁ ∨ x₂ ∨ x₃) ∧ (¬x₂)',
];
const TSAT_FORMULAS = [
'(x₁ ∨ x₁ ∨ x₁)',
'(x₁ ∨ x₂ ∨ x₂)',
'(x₁ ∨ x₂ ∨ x₂) ∧ (¬x₁ ∨ ¬x₁ ∨ ¬x₁)',
'(x₁ ∨ x₂ ∨ x₃) ∧ (¬x₂ ∨ ¬x₂ ∨ ¬x₂)',
];
// Base bijection: index i in SAT maps to index i in TSAT (a true isomorphism)
const BASE_MAP = [0, 1, 2, 3];
function buildMapping(injective, surjective, invertible) {
// Start from the base bijection and deliberately break selected properties.
// Break injectivity: map index 1 and 2 both to TSAT index 1 (collision).
// Break surjectivity: index 3 in TSAT is never the image of any SAT formula.
// Break invertibility: add an unrecoverable padding note (cosmetic here).
let map = BASE_MAP.slice(); // copy
if (!injective) {
// map SAT[1] and SAT[2] both to TSAT[1]
map[1] = 1;
map[2] = 1; // collision
}
if (!surjective) {
// skip TSAT[3]: redirect SAT[3] to TSAT[0] instead
map[3] = 0;
}
return map;
}
function render() {
const inj = document.getElementById('inj').checked;
const sur = document.getElementById('sur').checked;
const inv = document.getElementById('inv').checked;
const mapping = buildMapping(inj, sur, inv);
// Which TSAT indices are hit?
const hitSet = new Set(mapping);
// Which TSAT indices are hit more than once?
const counts = [0,0,0,0];
mapping.forEach(t => counts[t]++);
// Render SAT list
const satList = document.getElementById('sat-list');
satList.innerHTML = '';
SAT_FORMULAS.forEach((f, i) => {
const div = document.createElement('div');
div.className = 'formula';
div.textContent = f;
satList.appendChild(div);
});
// Render arrows
const arrowCol = document.getElementById('arrow-col');
arrowCol.innerHTML = '';
mapping.forEach((t, i) => {
const div = document.createElement('div');
const isCollision = counts[t] > 1;
div.className = 'arrow' + (isCollision ? ' bad' : ' ok');
div.textContent = '→ ' + (t + 1);
arrowCol.appendChild(div);
});
// Render TSAT list
const tsatList = document.getElementById('tsat-list');
tsatList.innerHTML = '';
TSAT_FORMULAS.forEach((f, i) => {
const div = document.createElement('div');
let cls = 'formula';
if (hitSet.has(i)) {
cls += counts[i] > 1 ? ' collision' : ' highlight';
} else {
cls += ' unreachable';
}
div.className = cls;
div.textContent = f;
tsatList.appendChild(div);
});
// Determine overall status
const hasCollision = mapping.some((t, i) => mapping.indexOf(t) !== i);
const missingTarget = [0,1,2,3].some(t => !hitSet.has(t));
const notInv = !inv;
const problems = [];
if (!inj || hasCollision) problems.push('NOT injective (collision)');
if (!sur || missingTarget) problems.push('NOT surjective (unreachable formula)');
if (notInv) problems.push('NOT invertible in poly-time');
const statusEl = document.getElementById('status');
if (problems.length === 0) {
statusEl.textContent = '✓ Valid polynomial isomorphism: bijective + invertible';
statusEl.className = 'status ok';
} else {
statusEl.textContent = '✗ ' + problems.join(' · ');
statusEl.className = 'status bad';
}
// Log verification to console
console.log('Mapping:', mapping, '| Injective:', inj && !hasCollision, '| Surjective:', sur && !missingTarget, '| Invertible:', inv);
}
document.getElementById('inj').addEventListener('change', render);
document.getElementById('sur').addEventListener('change', render);
document.getElementById('inv').addEventListener('change', render);
render();
Toggle each property and see which formulas survive. The key insight: a p-isomorphism cannot just pad arbitrarily — the padding must be recoverable (invertible) in polynomial time. That recoverability requirement is what makes building and verifying isomorphisms so delicate.
Comments
Loading comments...