mirror of
https://github.com/mudler/luet.git
synced 2025-09-13 05:42:52 +00:00
update vendor/
This commit is contained in:
480
vendor/github.com/crillab/gophersat/bf/bf.go
generated
vendored
480
vendor/github.com/crillab/gophersat/bf/bf.go
generated
vendored
@@ -1,480 +0,0 @@
|
||||
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")
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user