/*
* sat.js
* (C) 2012, all rights reserved,
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://[Log in to view URL]
*/
/*
* DESCRIPTION:
* This is a SAT solver implemented in javascript.
*/
/*
* State constructor.
*/
function State()
{
this.empty = false;
this.vars = [null];
this.clauses = [];
this.trail = [];
this.dlevel = 0;
this.tlevel = 0;
}
/*
* Variable constructor.
*/
function Variable()
{
this.set = false;
this.sign = false;
this.mark = false;
this.unit = false;
this.unit_sign = false;
this.dlevel = 0;
this.reason = null;
this.watches = [[], []];
this.setUnit = function(sign) {
this.unit = true;
this.unit_sign = sign;
}
}
/*
* Literal helper functions.
*/
function literalGetIdx(literal)
{
return (literal < 0? -literal: literal);
}
function literalGetSign(literal)
{
return (literal < 0);
}
function literalGetVar(state, literal)
{
var idx = literalGetIdx(literal);
return state.vars[idx];
}
function literalIsFalse(state, literal)
{
var v = literalGetVar(state, literal);
return (v.set && v.sign != literalGetSign(literal));
}
function literalGetMark(state, literal)
{
var v = literalGetVar(state, literal);
return v.mark;
}
function literalAddWatch(state, literal, clause)
{
var v = literalGetVar(state, literal);
var watch = v.watches[Number(literalGetSign(literal))];
watch.push(clause);
}
function literalSet(state, literal, reason)
{
var v = literalGetVar(state, literal);
v.sign = literalGetSign(literal);
v.set = true;
v.dlevel = state.dlevel;
v.reason = reason;
state.trail.push(literal);
}
/*
* Add a new clause.
*/
function satAddClause(state, clause)
{
switch (clause.length)
{
case 0:
// Empty clause:
state.empty = true;
return;
case 1:
var v = literalGetVar(state, clause[0]);
var sign = literalGetSign(clause[0]);
if (v.unit)
{
if (sign != v.unit_sign)
state.empty = true;
return;
}
v.setUnit(sign);
return;
default:
literalAddWatch(state, clause[0], clause);
literalAddWatch(state, clause[1], clause);
}
}
/*
* Select a literal. Chooses a literal at random (provided not already set).
*/
function satSelectLiteral(state)
{
var M = 1, N = state.vars.length-1;
var i = Math.floor(M + (1+N-M)*Math.random())
var i0 = i;
while (state.vars[i].set)
{
i++;
if (i >= state.vars.length)
i = 1;
if (i == i0)
return 0;
}
var literal = (Math.random() < 0.5? -i: i);
return literal;
}
/*
* Solver main loop.
*/
function satSolve(size, clauses)
{
// Create the state:
var state = new State();
for (var i = 0; i < size; i++)
state.vars.push(new Variable());
for (var i = 0; i < clauses.length; i++)
satAddClause(state, clauses[i]);
// UNSAT if empty clause has been asserted:
if (state.empty)
return false;
// Find and propagate unit clauses:
for (var i = 1; i < state.vars.length; i++)
{
var v = state.vars[i];
if (v.unit)
{
var literal = (v.unit_sign? -i: i);
if (!satUnitPropagate(state, literal, null))
return false;
}
}
// Main loop:
for (state.dlevel = 1; true; state.dlevel++)
{
var literal = satSelectLiteral(state);
if (literal == 0)
{
// All variables are now set; and no conflicts; therefore SAT
return true;
}
if (!satUnitPropagate(state, literal, null))
{
// UNSAT
return false;
}
}
return true;
}
/*
* Unit propagation.
*/
function satUnitPropagate(state, literal, reason)
{
var curr, next;
var restart;
do
{
curr = state.trail.length;
next = curr + 1;
literalSet(state, literal, reason);
restart = false;
while (curr < next)
{
literal = state.trail[curr];
curr++;
literal = -literal;
var v = literalGetVar(state, literal);
var watch = v.watches[Number(literalGetSign(literal))];
for (var i = 0; i < watch.length; i++)
{
var clause = watch[i];
var watch_idx = Number(clause[0] == literal);
var watch_lit = clause[watch_idx];
var watch_sign = literalGetSign(watch_lit);
var w = literalGetVar(state, watch_lit);
if (w.set && w.sign == watch_sign)
{
// 'clause' is true -- no work to do.
continue;
}
// Search for a non-false literal in 'clause'.
var j;
for (j = 2; j < clause.length &&
literalIsFalse(state, clause[j]); j++)
;
if (j >= clause.length)
{
// All other literals a false; use the other watch:
if (!w.set)
{
// Implied set:
if (watch_idx != 0)
{
clause[0] = watch_lit;
clause[1] = literal;
}
literalSet(state, watch_lit, clause);
next++;
continue;
}
// All literals in 'clause' are false; conflict!
reason = satBacktrack(state, clause);
if (reason == null)
return false;
literal = reason[0];
restart = true;
break;
}
// Watch the other literal:
var new_lit = clause[j];
clause[Number(!watch_idx)] = new_lit;
clause[j] = literal;
literalAddWatch(state, new_lit, clause);
if (i == watch.length-1)
watch.pop();
else
{
watch[i] = watch.pop();
i--;
}
}
if (restart)
break;
}
}
while (restart);
return true;
}
/*
* Backtracking and no-good learning.
*/
function satBacktrack(state, reason)
{
var conflicts = [];
// Level 0 failure; no work to do.
if (state.dlevel == 0)
return null;
// Mark literals in reason:
var count = 0;
for (var i = 0; i < reason.length; i++)
{
var v = literalGetVar(state, reason[i]);
if (v.dlevel == 0)
continue;
v.mark = true;
if (v.dlevel < state.dlevel)
conflicts.push(reason[i]);
else
count++;
}
// Find the UIP and collect conflicts:
var tlevel = state.trail.length-1;
var literal;
do
{
if (tlevel < 0)
return null;
literal = state.trail[tlevel--];
var v = literalGetVar(state, literal);
v.set = false;
if (!v.mark)
continue;
v.mark = false;
count--;
if (count <= 0)
break;
for (var i = 1; i < v.reason.length; i++)
{
literal = v.reason[i];
var w = literalGetVar(state, literal);
if (w.mark || w.dlevel == 0)
continue;
if (w.dlevel < state.dlevel)
conflicts.push(literal);
else
count++;
w.mark = true;
}
}
while (true);
// Simplify the conflicts; create the no-good.
var nogood = [-literal];
var blevel = 0;
for (var i = 0; i < conflicts.length; i++)
{
literal = conflicts[i];
v = literalGetVar(state, literal);
if (v.reason != null)
{
var k;
for (k = 1; k < v.reason.length &&
literalGetMark(state, v.reason[k]); k++)
;
if (k >= v.reason.length)
continue;
}
nogood.push(literal);
if (blevel < v.dlevel)
{
blevel = v.dlevel;
nogood[nogood.length-1] = nogood[1];
nogood[1] = literal;
}
}
// Unwind the trail:
while (tlevel >= 0)
{
literal = state.trail[tlevel];
v = literalGetVar(state, literal);
if (v.dlevel <= blevel)
break;
v.set = false;
tlevel--;
}
state.trail.length = tlevel+1;
// Clear the marks:
for (var i = 0; i < conflicts.length; i++)
{
v = literalGetVar(state, conflicts[i]);
v.mark = false;
}
// Add the no-good clause:
satAddClause(state, nogood);
state.dlevel = blevel;
if (state.empty)
return null;
return nogood;
}
let size = 10
let clauses = [[-8, -6, 4], [1, 2, 8], [-6, -3, 2], [-9, -6, 1], [9, 10, 6], [7, -7, -4], [-6, 10, 1], [5, -5, -2], [-2, -3, 8], [6, -3, -1], [-7, 7, -1], [-8, -9, 8], [5, 7, 2], [5, -8, -7], [-10, -3, -5], [-7, -6, -9], [8, -1, -10], [-8, 3, 5], [-6, -4, 7], [2, -4, -5], [1, 3, -5], [1, 6, -3], [2, -9, 1], [-6, 5, 10], [1, 3, 7], [-3, 2, 9], [9, -1, 7], [10, -7, -10], [-6, -10, -6], [10, -3, 8], [-7, -7, -2], [8, -4, 4], [-5, 9, -10], [-3, 6, 9], [-7, 4, 7], [-6, 3, 2], [-6, 6, 7], [-6, 4, -10], [3, -3, -3], [8, 7, -7], [-5, 1, -6], [-7, -6, 5]]
let res = satSolve(size, clauses)
console.log(res)
To embed this program on your website, copy the following code and paste it into your website's HTML: