Revert "update vendor/"

This reverts commit 7ce522110e.
This commit is contained in:
Ettore Di Giacinto
2020-02-11 14:55:49 +01:00
parent 711c039296
commit 2f6bef14d5
26 changed files with 23 additions and 324 deletions

480
vendor/github.com/crillab/gophersat/bf/bf.go generated vendored Normal file
View File

@@ -0,0 +1,480 @@
package bf
import (
"fmt"
"io"
"math"
"sort"
"strconv"
"strings"
"github.com/crillab/gophersat/solver"
)
// A Formula is any kind of boolean formula, not necessarily in CNF.
type Formula interface {
nnf() Formula
String() string
Eval(model map[string]bool) bool
}
// Solve solves the given formula.
// f is first converted as a CNF formula. It is then given to gophersat.
// The function returns a model associating each variable name with its binding, or nil if the formula was not satisfiable.
func Solve(f Formula) map[string]bool {
return asCnf(f).solve()
}
// Dimacs writes the DIMACS CNF version of the formula on w.
// It is useful so as to feed it to any SAT solver.
// The original names of variables is associated with their DIMACS integer counterparts
// in comments, between the prolog and the set of clauses.
// For instance, if the variable "a" is associated with the index 1, there will be a comment line
// "c a=1".
func Dimacs(f Formula, w io.Writer) error {
cnf := asCnf(f)
nbVars := len(cnf.vars.all)
nbClauses := len(cnf.clauses)
prefix := fmt.Sprintf("p cnf %d %d\n", nbVars, nbClauses)
if _, err := io.WriteString(w, prefix); err != nil {
return fmt.Errorf("could not write DIMACS output: %v", err)
}
var pbVars []string
for v := range cnf.vars.pb {
if !v.dummy {
pbVars = append(pbVars, v.name)
}
}
sort.Sort(sort.StringSlice(pbVars))
for _, v := range pbVars {
idx := cnf.vars.pb[pbVar(v)]
line := fmt.Sprintf("c %s=%d\n", v, idx)
if _, err := io.WriteString(w, line); err != nil {
return fmt.Errorf("could not write DIMACS output: %v", err)
}
}
for _, clause := range cnf.clauses {
strClause := make([]string, len(clause))
for i, lit := range clause {
strClause[i] = strconv.Itoa(lit)
}
line := fmt.Sprintf("%s 0\n", strings.Join(strClause, " "))
if _, err := io.WriteString(w, line); err != nil {
return fmt.Errorf("could not write DIMACS output: %v", err)
}
}
return nil
}
// The "true" constant.
type trueConst struct{}
// True is the constant denoting a tautology.
var True Formula = trueConst{}
func (t trueConst) nnf() Formula { return t }
func (t trueConst) String() string { return "" }
func (t trueConst) Eval(model map[string]bool) bool { return true }
// The "false" constant.
type falseConst struct{}
// False is the constant denoting a contradiction.
var False Formula = falseConst{}
func (f falseConst) nnf() Formula { return f }
func (f falseConst) String() string { return "⊥" }
func (f falseConst) Eval(model map[string]bool) bool { return false }
// Var generates a named boolean variable in a formula.
func Var(name string) Formula {
return pbVar(name)
}
func pbVar(name string) variable {
return variable{name: name, dummy: false}
}
func dummyVar(name string) variable {
return variable{name: name, dummy: true}
}
type variable struct {
name string
dummy bool
}
func (v variable) nnf() Formula {
return lit{signed: false, v: v}
}
func (v variable) String() string {
return v.name
}
func (v variable) Eval(model map[string]bool) bool {
b, ok := model[v.name]
if !ok {
panic(fmt.Errorf("Model lacks binding for variable %s", v.name))
}
return b
}
type lit struct {
v variable
signed bool
}
func (l lit) nnf() Formula {
return l
}
func (l lit) String() string {
if l.signed {
return "not(" + l.v.name + ")"
}
return l.v.name
}
func (l lit) Eval(model map[string]bool) bool {
b := l.v.Eval(model)
if l.signed {
return !b
}
return b
}
// Not represents a negation. It negates the given subformula.
func Not(f Formula) Formula {
return not{f}
}
type not [1]Formula
func (n not) nnf() Formula {
switch f := n[0].(type) {
case variable:
l := f.nnf().(lit)
l.signed = true
return l
case lit:
f.signed = !f.signed
return f
case not:
return f[0].nnf()
case and:
subs := make([]Formula, len(f))
for i, sub := range f {
subs[i] = not{sub}.nnf()
}
return or(subs).nnf()
case or:
subs := make([]Formula, len(f))
for i, sub := range f {
subs[i] = not{sub}.nnf()
}
return and(subs).nnf()
case trueConst:
return False
case falseConst:
return True
default:
panic("invalid formula type")
}
}
func (n not) String() string {
return "not(" + n[0].String() + ")"
}
func (n not) Eval(model map[string]bool) bool {
return !n[0].Eval(model)
}
// And generates a conjunction of subformulas.
func And(subs ...Formula) Formula {
return and(subs)
}
type and []Formula
func (a and) nnf() Formula {
var res and
for _, s := range a {
nnf := s.nnf()
switch nnf := nnf.(type) {
case and: // Simplify: "and"s in the "and" get to the higher level
res = append(res, nnf...)
case trueConst: // True is ignored
case falseConst:
return False
default:
res = append(res, nnf)
}
}
if len(res) == 1 {
return res[0]
}
if len(res) == 0 {
return False
}
return res
}
func (a and) String() string {
strs := make([]string, len(a))
for i, f := range a {
strs[i] = f.String()
}
return "and(" + strings.Join(strs, ", ") + ")"
}
func (a and) Eval(model map[string]bool) (res bool) {
for i, s := range a {
b := s.Eval(model)
if i == 0 {
res = b
} else {
res = res && b
}
}
return
}
// Or generates a disjunction of subformulas.
func Or(subs ...Formula) Formula {
return or(subs)
}
type or []Formula
func (o or) nnf() Formula {
var res or
for _, s := range o {
nnf := s.nnf()
switch nnf := nnf.(type) {
case or: // Simplify: "or"s in the "or" get to the higher level
res = append(res, nnf...)
case falseConst: // False is ignored
case trueConst:
return True
default:
res = append(res, nnf)
}
}
if len(res) == 1 {
return res[0]
}
if len(res) == 0 {
return True
}
return res
}
func (o or) String() string {
strs := make([]string, len(o))
for i, f := range o {
strs[i] = f.String()
}
return "or(" + strings.Join(strs, ", ") + ")"
}
func (o or) Eval(model map[string]bool) (res bool) {
for i, s := range o {
b := s.Eval(model)
if i == 0 {
res = b
} else {
res = res || b
}
}
return
}
// Implies indicates a subformula implies another one.
func Implies(f1, f2 Formula) Formula {
return or{not{f1}, f2}
}
// Eq indicates a subformula is equivalent to another one.
func Eq(f1, f2 Formula) Formula {
return and{or{not{f1}, f2}, or{f1, not{f2}}}
}
// Xor indicates exactly one of the two given subformulas is true.
func Xor(f1, f2 Formula) Formula {
return and{or{not{f1}, not{f2}}, or{f1, f2}}
}
// Unique indicates exactly one of the given variables must be true.
// It might create dummy variables to reduce the number of generated clauses.
func Unique(vars ...string) Formula {
vars2 := make([]variable, len(vars))
for i, v := range vars {
vars2[i] = pbVar(v)
}
return uniqueRec(vars2...)
}
// uniqueSmall generates clauses indicating exactly one of the given variables is true.
// It is suitable when the number of variables is small (typically, <= 4).
func uniqueSmall(vars ...variable) Formula {
res := make([]Formula, 1, 1+(len(vars)*len(vars)-1)/2)
varsAsForms := make([]Formula, len(vars))
for i, v := range vars {
varsAsForms[i] = v
}
res[0] = Or(varsAsForms...)
for i := 0; i < len(vars)-1; i++ {
for j := i + 1; j < len(vars); j++ {
res = append(res, Or(Not(varsAsForms[i]), Not(varsAsForms[j])))
}
}
return And(res...)
}
func uniqueRec(vars ...variable) Formula {
nbVars := len(vars)
if nbVars <= 4 {
return uniqueSmall(vars...)
}
sqrt := math.Sqrt(float64(nbVars))
nbLines := int(sqrt + 0.5)
lines := make([]variable, nbLines)
linesF := make([][]Formula, nbLines)
allNames := make([]string, len(vars))
for i := range vars {
allNames[i] = vars[i].name
}
fullName := strings.Join(allNames, "-")
for i := range lines {
lines[i] = dummyVar(fmt.Sprintf("line-%d-%s", i, fullName))
linesF[i] = []Formula{}
}
nbCols := int(math.Ceil(sqrt))
cols := make([]variable, nbCols)
colsF := make([][]Formula, nbCols)
for i := range cols {
cols[i] = dummyVar(fmt.Sprintf("col-%d-%s", i, fullName))
colsF[i] = []Formula{}
}
res := make([]Formula, 0, 2*nbVars+1)
for i, v := range vars {
linesF[i/nbCols] = append(linesF[i/nbCols], v)
colsF[i%nbCols] = append(colsF[i%nbCols], v)
}
for i := range lines {
res = append(res, Eq(lines[i], Or(linesF[i]...)))
}
for i := range cols {
res = append(res, Eq(cols[i], Or(colsF[i]...)))
}
res = append(res, uniqueRec(lines...))
res = append(res, uniqueRec(cols...))
return And(res...)
}
// vars associate variable names with numeric indices.
type vars struct {
all map[variable]int // all vars, including those created when converting the formula
pb map[variable]int // Only the vars that appeared orinigally in the problem
}
// litValue returns the int value associated with the given problem var.
// If the var was not referenced yet, it is created first.
func (vars *vars) litValue(l lit) int {
val, ok := vars.all[l.v]
if !ok {
val = len(vars.all) + 1
vars.all[l.v] = val
vars.pb[l.v] = val
}
if l.signed {
return -val
}
return val
}
// Dummy creates a dummy variable and returns its associated index.
func (vars *vars) dummy() int {
val := len(vars.all) + 1
vars.all[dummyVar(fmt.Sprintf("dummy-%d", val))] = val
return val
}
// A CNF is the representation of a boolean formula as a conjunction of disjunction.
// It can be solved by a SAT solver.
type cnf struct {
vars vars
clauses [][]int
}
// solve solves the given formula.
// cnf is given to gophersat.
// If it is satisfiable, the function returns a model, associating each variable name with its binding.
// Else, the function returns nil.
func (cnf *cnf) solve() map[string]bool {
pb := solver.ParseSlice(cnf.clauses)
s := solver.New(pb)
if s.Solve() != solver.Sat {
return nil
}
m := s.Model()
vars := make(map[string]bool)
for v, idx := range cnf.vars.pb {
vars[v.name] = m[idx-1]
}
return vars
}
// asCnf returns a CNF representation of the given formula.
func asCnf(f Formula) *cnf {
vars := vars{all: make(map[variable]int), pb: make(map[variable]int)}
clauses := cnfRec(f.nnf(), &vars)
return &cnf{vars: vars, clauses: clauses}
}
// transforms the f NNF formula into a CNF formula.
// nbDummies is the current number of dummy variables created.
// Note: code should be improved, there are a few useless allocs/deallocs
// here and there.
func cnfRec(f Formula, vars *vars) [][]int {
switch f := f.(type) {
case lit:
return [][]int{{vars.litValue(f)}}
case and:
var res [][]int
for _, sub := range f {
res = append(res, cnfRec(sub, vars)...)
}
return res
case or:
var res [][]int
var lits []int
for _, sub := range f {
switch sub := sub.(type) {
case lit:
lits = append(lits, vars.litValue(sub))
case and:
d := vars.dummy()
lits = append(lits, d)
for _, sub2 := range sub {
cnf := cnfRec(sub2, vars)
cnf[0] = append(cnf[0], -d)
res = append(res, cnf...)
}
default:
panic("unexpected or in or")
}
}
res = append(res, lits)
return res
case trueConst: // True clauses are ignored
return [][]int{}
case falseConst: // TODO: improve this. This should simply be declared to make the problem UNSAT.
return [][]int{{}}
default:
panic("invalid NNF formula")
}
}