UNPKG

picosat

Version:
154 lines (127 loc) 4.58 kB
'use strict' const bindings = require('node-gyp-build')(__dirname) const encodeInt32Array = require('./lib/encode') // todo: decodeInt32Array const encodeStrings = (formula, assumptions) => { if (!Array.isArray(formula)) throw new Error('formula must be an array.') if (!Array.isArray(assumptions)) throw new Error('assumptions must be an array.') if (formula.length === 0) { throw new Error('formula must have 1 or more clauses.') } const encodedFormula = [] const variableToId = Object.create(null) let nextVariableId = 1 for (let i = 0; i < formula.length; i++) { const clause = formula[i] if (!Array.isArray(clause)) { throw new Error(`clause formula[${i}] must be an array.`) } for (let j = 0; j < clause.length; j++) { const literal = clause[j] if ('string' !== typeof literal) { throw new Error(`literal formula[${i}][${j}] must be a string.`) } const isNegated = literal[0] === '!' const variable = isNegated ? literal.slice(1) : literal if (variable.length === 0) { throw new Error(`literal formula[${i}][${j}] has an invalid format.`) } if (!(variable in variableToId)) variableToId[variable] = nextVariableId++ const id = variableToId[variable] encodedFormula.push(isNegated ? id * -1 : id) } encodedFormula.push(0) // separator } const encodedAssumptions = [] for (let i = 0; i < assumptions.length; i++) { const literal = assumptions[i] const isNegated = literal[0] === '!' const variable = isNegated ? literal.slice(1) : literal if (!(variable in variableToId)) { throw new Error(`unknown variable '${variable}' in assumptions[${i}].`) } const id = variableToId[variable] encodedAssumptions.push(isNegated ? id * -1 : id) } // PicoSAT expects Int32 return [ encodeInt32Array(encodedFormula), encodeInt32Array(encodedAssumptions) ] } const encodeIntegers = (formula, assumptions) => { if (!Array.isArray(formula)) throw new Error('formula must be an array.') if (!Array.isArray(assumptions)) throw new Error('assumptions must be an array.') if (formula.length === 0) { throw new Error('formula must have 1 or more clauses.') } const encodedFormula = [] for (let i = 0; i < formula.length; i++) { const clause = formula[i] if (!Array.isArray(clause)) { throw new Error(`clause formula[${i}] must be an array.`) } for (let j = 0; j < clause.length; j++) { const literal = clause[j] if ('number' !== typeof literal) { throw new Error(`literal formula[${i}][${j}] must be a number.`) } if ((literal | 0) !== literal) { throw new Error(`literal formula[${i}][${j}] must be an integer.`) } if (literal === 0) { throw new Error(`literal formula[${i}][${j}] must be != 0.`) } encodedFormula.push(literal) } encodedFormula.push(0) // separator } for (let i = 0; i < assumptions.length; i++) { const literal = assumptions[i] if ('number' !== typeof literal) { throw new Error(`literal assumptions[${i}] must be a number.`) } if ((literal | 0) !== literal) { throw new Error(`literal assumptions[${i}] must be an integer.`) } if (literal === 0) { throw new Error(`literal assumptions[${i}] must be != 0.`) } } // PicoSAT expects Int32 return [ encodeInt32Array(encodedFormula), encodeInt32Array(assumptions) ] } const UNKNOWN = 'unknown' const SATISFIABLE = 'satisfiable' const UNSATISFIABLE = 'unsatisfiable' const solveUnsafe = (formula, assumptions) => { return bindings.node_picosat_sat(formula, assumptions) } const _solve = (formula, assumptions, encode) => { const [encFormula, encAssumptions] = encode(formula, assumptions) const solution = solveUnsafe(encFormula, encAssumptions) let statusCode if (solution[0] === 10) statusCode = SATISFIABLE else if (solution[0] === 20) statusCode = UNSATISFIABLE else statusCode = UNKNOWN return { satisfiable: statusCode === 'satisfiable', statusCode, solution: solution.slice(1) } } const solveWithStrings = (formula, assumptions = []) => { return _solve(formula, assumptions, encodeStrings) } const solveWithIntegers = (formula, assumptions = []) => { return _solve(formula, assumptions, encodeIntegers) } Object.assign(solveWithIntegers, { encodeStrings, encodeIntegers, solveWithStrings, solveUnsafe, UNKNOWN, SATISFIABLE, UNSATISFIABLE }) module.exports = solveWithIntegers