<!-- Moser-Tardos demo: variable row + constraint chips + log -->
<p class="hint">Ten binary variables x1–x10 and eight constraints. Each constraint forbids one specific pattern on three variables. Green = satisfied, red = violated. Run the algorithm or step through it manually.</p>
<div id="var-row" class="var-row" aria-label="Variable values"></div>
<div id="constraints-area" class="constraints-area" aria-label="Constraint status"></div>
<div class="status-bar" id="status">Press Run or Step to start.</div>
<div class="log-box" id="log" aria-label="Resampling log"></div>
<div class="btns">
<button id="btn-run" type="button">Run Moser-Tardos</button>
<button id="btn-step" type="button">Step</button>
<button id="btn-reset" type="button" class="ghost">Reset</button>
</div>
/* Layout and color coding for variables and constraints */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .88rem; color: #444; margin: 0 0 .7rem; line-height: 1.5; }
.var-row { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: .6rem; }
.var-box { width: 44px; height: 44px; display: flex; align-items: center; justify-content: center;
font: 700 15px ui-monospace, monospace; border-radius: 8px; border: 1.5px solid #adb1b8;
background: #e0e7ef; color: #1d3557; transition: background .2s, border-color .2s; }
.var-box.val-1 { background: #d0f0d8; border-color: #2a9d4a; color: #1a6632; }
.var-box.val-0 { background: #f5e0e3; border-color: #c92f3c; color: #7a1a1f; }
.var-box.resampled { outline: 3px solid #f4a261; outline-offset: 2px; }
.constraints-area { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: .5rem; }
.c-chip { font-size: .78rem; padding: 4px 8px; border-radius: 6px; border: 1.5px solid #cdd9e3;
background: #f0f4f8; color: #333; transition: background .2s, border-color .2s; }
.c-chip.violated { background: #fde9ea; border-color: #c92f3c; color: #7a1a1f; font-weight: 700; }
.c-chip.satisfied { background: #d5f2e0; border-color: #2a9d4a; color: #1a6632; }
.status-bar { font-size: .97rem; font-weight: 600; margin: .4rem 0; min-height: 1.4em; }
.status-bar.ok { color: #0a7d33; }
.status-bar.bad { color: #c92f3c; }
.status-bar.running { color: #e76f00; }
.log-box { font-size: .78rem; font-family: ui-monospace, monospace; color: #444;
max-height: 90px; overflow-y: auto; border: 1px solid #cdd9e3; border-radius: 6px;
padding: 4px 8px; margin-bottom: .5rem; background: #fafafa; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
button { font: 600 13px system-ui, sans-serif; padding: .42rem .85rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
button:disabled { opacity: .45; cursor: default; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Moser-Tardos resampling algorithm demo
// LLL condition: each constraint touches at most 4 others (well within ep(d+1)<=1)
// Number of binary variables x1..x10
const NUM_VARS = 10;
// Each constraint has: vars = indices of its 3 variables,
// bad = the forbidden pattern (the 'bad event' the constraint represents)
const CONSTRAINTS = [
{ vars: [0,1,2], bad: [1,1,1] },
{ vars: [1,2,3], bad: [0,0,0] },
{ vars: [2,3,4], bad: [1,0,1] },
{ vars: [3,4,5], bad: [0,1,0] },
{ vars: [4,5,6], bad: [1,1,0] },
{ vars: [5,6,7], bad: [0,0,1] },
{ vars: [6,7,8], bad: [1,0,0] },
{ vars: [7,8,9], bad: [0,1,1] },
];
// Algorithm state
let vals = [];
let stepCount = 0;
let running = false;
let done = false;
function randBit(){ return Math.random() < 0.5 ? 0 : 1; }
function initVals(){
vals = Array.from({ length: NUM_VARS }, () => randBit());
stepCount = 0;
done = false;
}
// Find first violated constraint (bad event that occurred)
function findViolated(){
return CONSTRAINTS.find(c =>
c.vars.every((vi, i) => vals[vi] === c.bad[i])
) || null;
}
// Check if all constraints are satisfied
function allSatisfied(){
return CONSTRAINTS.every(c =>
!c.vars.every((vi, i) => vals[vi] === c.bad[i])
);
}
// Render variables and constraint chips; highlight resampled vars
function render(highlighted){
const row = document.getElementById('var-row');
row.innerHTML = '';
vals.forEach((v, i) => {
const d = document.createElement('div');
d.className = 'var-box val-' + v + (highlighted && highlighted.includes(i) ? ' resampled' : '');
d.title = 'x' + (i+1) + ' = ' + v;
d.textContent = 'x' + (i+1);
row.appendChild(d);
});
const area = document.getElementById('constraints-area');
area.innerHTML = '';
CONSTRAINTS.forEach((c, ci) => {
const violated = c.vars.every((vi, i) => vals[vi] === c.bad[i]);
const d = document.createElement('div');
d.className = 'c-chip ' + (violated ? 'violated' : 'satisfied');
d.title = fmt('Bad pattern: %map%', { map: c.vars.map((vi,i) => 'x'+(vi+1)+'='+c.bad[i]).join(', ') });
d.textContent = 'C' + (ci+1);
area.appendChild(d);
});
}
function setStatus(t, cls){
const s = document.getElementById('status');
s.textContent = t;
s.className = 'status-bar ' + (cls||'');
}
function addLog(msg){
const lb = document.getElementById('log');
const line = document.createElement('div');
line.textContent = msg;
lb.appendChild(line);
lb.scrollTop = lb.scrollHeight;
}
// One resampling step: find a violated constraint and resample its variables
function doStep(){
if(done) return;
const c = findViolated();
if(!c){
done = true;
render([]);
setStatus('✓ All constraints satisfied!', 'ok');
addLog(fmt('Done in %stepCount% resampling steps.', { stepCount: stepCount }));
document.getElementById('btn-step').disabled = true;
return true;
}
// Resample all variables of the violated constraint independently
c.vars.forEach(vi => { vals[vi] = randBit(); });
stepCount++;
render(c.vars);
addLog(fmt('Step %stepCount%: resampled %map%', { stepCount: stepCount, map: c.vars.map(vi=>'x'+(vi+1)).join(',') }));
if(allSatisfied()){
done = true;
setStatus('✓ All constraints satisfied!', 'ok');
addLog(fmt('Done in %stepCount% resampling steps.', { stepCount: stepCount }));
document.getElementById('btn-step').disabled = true;
return true;
}
setStatus(fmt('Running… step %stepCount%', { stepCount: stepCount }), 'running');
return false;
}
function reset(){
running = false;
initVals();
render([]);
document.getElementById('log').innerHTML = '';
setStatus('Press Run or Step to start.', '');
document.getElementById('btn-step').disabled = false;
}
document.getElementById('btn-run').addEventListener('click', () => {
if(running) return;
if(done){ reset(); }
running = true;
setStatus(fmt('Running… step %stepCount%', { stepCount: 0 }), 'running');
addLog('--- Starting new run ---');
// Animated loop: one step every 160ms
function tick(){
if(!running){ return; }
const finished = doStep();
if(!finished){ setTimeout(tick, 160); }
else { running = false; }
}
tick();
});
document.getElementById('btn-step').addEventListener('click', () => {
if(done){ return; }
if(stepCount === 0){ addLog('--- Starting new run ---'); }
doStep();
});
document.getElementById('btn-reset').addEventListener('click', reset);
// Initialize on load
reset();
<!-- Moser-Tardos demo: variable row + constraint chips + log -->
<p class="hint">Ten binary variables x1–x10 and eight constraints. Each constraint forbids one specific pattern on three variables. Green = satisfied, red = violated. Run the algorithm or step through it manually.</p>
<div id="var-row" class="var-row" aria-label="Variable values"></div>
<div id="constraints-area" class="constraints-area" aria-label="Constraint status"></div>
<div class="status-bar" id="status">Press Run or Step to start.</div>
<div class="log-box" id="log" aria-label="Resampling log"></div>
<div class="btns">
<button id="btn-run" type="button">Run Moser-Tardos</button>
<button id="btn-step" type="button">Step</button>
<button id="btn-reset" type="button" class="ghost">Reset</button>
</div>
/* Layout and color coding for variables and constraints */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .88rem; color: #444; margin: 0 0 .7rem; line-height: 1.5; }
.var-row { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: .6rem; }
.var-box { width: 44px; height: 44px; display: flex; align-items: center; justify-content: center;
font: 700 15px ui-monospace, monospace; border-radius: 8px; border: 1.5px solid #adb1b8;
background: #e0e7ef; color: #1d3557; transition: background .2s, border-color .2s; }
.var-box.val-1 { background: #d0f0d8; border-color: #2a9d4a; color: #1a6632; }
.var-box.val-0 { background: #f5e0e3; border-color: #c92f3c; color: #7a1a1f; }
.var-box.resampled { outline: 3px solid #f4a261; outline-offset: 2px; }
.constraints-area { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: .5rem; }
.c-chip { font-size: .78rem; padding: 4px 8px; border-radius: 6px; border: 1.5px solid #cdd9e3;
background: #f0f4f8; color: #333; transition: background .2s, border-color .2s; }
.c-chip.violated { background: #fde9ea; border-color: #c92f3c; color: #7a1a1f; font-weight: 700; }
.c-chip.satisfied { background: #d5f2e0; border-color: #2a9d4a; color: #1a6632; }
.status-bar { font-size: .97rem; font-weight: 600; margin: .4rem 0; min-height: 1.4em; }
.status-bar.ok { color: #0a7d33; }
.status-bar.bad { color: #c92f3c; }
.status-bar.running { color: #e76f00; }
.log-box { font-size: .78rem; font-family: ui-monospace, monospace; color: #444;
max-height: 90px; overflow-y: auto; border: 1px solid #cdd9e3; border-radius: 6px;
padding: 4px 8px; margin-bottom: .5rem; background: #fafafa; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
button { font: 600 13px system-ui, sans-serif; padding: .42rem .85rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
button:disabled { opacity: .45; cursor: default; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Moser-Tardos resampling algorithm demo
// LLL condition: each constraint touches at most 4 others (well within ep(d+1)<=1)
// Number of binary variables x1..x10
const NUM_VARS = 10;
// Each constraint has: vars = indices of its 3 variables,
// bad = the forbidden pattern (the 'bad event' the constraint represents)
const CONSTRAINTS = [
{ vars: [0,1,2], bad: [1,1,1] },
{ vars: [1,2,3], bad: [0,0,0] },
{ vars: [2,3,4], bad: [1,0,1] },
{ vars: [3,4,5], bad: [0,1,0] },
{ vars: [4,5,6], bad: [1,1,0] },
{ vars: [5,6,7], bad: [0,0,1] },
{ vars: [6,7,8], bad: [1,0,0] },
{ vars: [7,8,9], bad: [0,1,1] },
];
// Algorithm state
let vals = [];
let stepCount = 0;
let running = false;
let done = false;
function randBit(){ return Math.random() < 0.5 ? 0 : 1; }
function initVals(){
vals = Array.from({ length: NUM_VARS }, () => randBit());
stepCount = 0;
done = false;
}
// Find first violated constraint (bad event that occurred)
function findViolated(){
return CONSTRAINTS.find(c =>
c.vars.every((vi, i) => vals[vi] === c.bad[i])
) || null;
}
// Check if all constraints are satisfied
function allSatisfied(){
return CONSTRAINTS.every(c =>
!c.vars.every((vi, i) => vals[vi] === c.bad[i])
);
}
// Render variables and constraint chips; highlight resampled vars
function render(highlighted){
const row = document.getElementById('var-row');
row.innerHTML = '';
vals.forEach((v, i) => {
const d = document.createElement('div');
d.className = 'var-box val-' + v + (highlighted && highlighted.includes(i) ? ' resampled' : '');
d.title = 'x' + (i+1) + ' = ' + v;
d.textContent = 'x' + (i+1);
row.appendChild(d);
});
const area = document.getElementById('constraints-area');
area.innerHTML = '';
CONSTRAINTS.forEach((c, ci) => {
const violated = c.vars.every((vi, i) => vals[vi] === c.bad[i]);
const d = document.createElement('div');
d.className = 'c-chip ' + (violated ? 'violated' : 'satisfied');
d.title = fmt('Bad pattern: %map%', { map: c.vars.map((vi,i) => 'x'+(vi+1)+'='+c.bad[i]).join(', ') });
d.textContent = 'C' + (ci+1);
area.appendChild(d);
});
}
function setStatus(t, cls){
const s = document.getElementById('status');
s.textContent = t;
s.className = 'status-bar ' + (cls||'');
}
function addLog(msg){
const lb = document.getElementById('log');
const line = document.createElement('div');
line.textContent = msg;
lb.appendChild(line);
lb.scrollTop = lb.scrollHeight;
}
// One resampling step: find a violated constraint and resample its variables
function doStep(){
if(done) return;
const c = findViolated();
if(!c){
done = true;
render([]);
setStatus('✓ All constraints satisfied!', 'ok');
addLog(fmt('Done in %stepCount% resampling steps.', { stepCount: stepCount }));
document.getElementById('btn-step').disabled = true;
return true;
}
// Resample all variables of the violated constraint independently
c.vars.forEach(vi => { vals[vi] = randBit(); });
stepCount++;
render(c.vars);
addLog(fmt('Step %stepCount%: resampled %map%', { stepCount: stepCount, map: c.vars.map(vi=>'x'+(vi+1)).join(',') }));
if(allSatisfied()){
done = true;
setStatus('✓ All constraints satisfied!', 'ok');
addLog(fmt('Done in %stepCount% resampling steps.', { stepCount: stepCount }));
document.getElementById('btn-step').disabled = true;
return true;
}
setStatus(fmt('Running… step %stepCount%', { stepCount: stepCount }), 'running');
return false;
}
function reset(){
running = false;
initVals();
render([]);
document.getElementById('log').innerHTML = '';
setStatus('Press Run or Step to start.', '');
document.getElementById('btn-step').disabled = false;
}
document.getElementById('btn-run').addEventListener('click', () => {
if(running) return;
if(done){ reset(); }
running = true;
setStatus(fmt('Running… step %stepCount%', { stepCount: 0 }), 'running');
addLog('--- Starting new run ---');
// Animated loop: one step every 160ms
function tick(){
if(!running){ return; }
const finished = doStep();
if(!finished){ setTimeout(tick, 160); }
else { running = false; }
}
tick();
});
document.getElementById('btn-step').addEventListener('click', () => {
if(done){ return; }
if(stepCount === 0){ addLog('--- Starting new run ---'); }
doStep();
});
document.getElementById('btn-reset').addEventListener('click', reset);
// Initialize on load
reset();
Comments
Loading comments...