mirror of
https://github.com/mudler/luet.git
synced 2025-07-19 01:37:13 +00:00
126 lines
3.1 KiB
Go
126 lines
3.1 KiB
Go
package explain
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// A Problem is a conjunction of Clauses.
|
|
// This package does not use solver's representation.
|
|
// We want this code to be as simple as possible to be easy to audit.
|
|
// On the other hand, solver's code must be as efficient as possible.
|
|
type Problem struct {
|
|
Clauses [][]int
|
|
NbVars int
|
|
NbClauses int
|
|
units []int // For each var, 0 if the var is unbound, 1 if true, -1 if false
|
|
Options Options
|
|
tagged []bool // List of claused used whil proving the problem is unsat. Initialized lazily
|
|
}
|
|
|
|
func (pb *Problem) initTagged() {
|
|
pb.tagged = make([]bool, pb.NbClauses)
|
|
for i, clause := range pb.Clauses {
|
|
// Unit clauses are tagged as they will probably be used during resolution
|
|
pb.tagged[i] = len(clause) == 1
|
|
}
|
|
}
|
|
|
|
func (pb *Problem) clone() *Problem {
|
|
pb2 := &Problem{
|
|
Clauses: make([][]int, pb.NbClauses),
|
|
NbVars: pb.NbVars,
|
|
NbClauses: pb.NbClauses,
|
|
units: make([]int, pb.NbVars),
|
|
}
|
|
copy(pb2.units, pb.units)
|
|
for i, clause := range pb.Clauses {
|
|
pb2.Clauses[i] = make([]int, len(clause))
|
|
copy(pb2.Clauses[i], clause)
|
|
}
|
|
return pb2
|
|
}
|
|
|
|
// restore removes all learned clauses, if any.
|
|
func (pb *Problem) restore() {
|
|
pb.Clauses = pb.Clauses[:pb.NbClauses]
|
|
}
|
|
|
|
// unsat will be true iff the problem can be proven unsat through unit propagation.
|
|
// This methods modifies pb.units.
|
|
func (pb *Problem) unsat() bool {
|
|
done := make([]bool, len(pb.Clauses)) // clauses that were deemed sat during propagation
|
|
modified := true
|
|
for modified {
|
|
modified = false
|
|
for i, clause := range pb.Clauses {
|
|
if done[i] { // That clause was already proved true
|
|
continue
|
|
}
|
|
unbound := 0
|
|
var unit int // An unbound literal, if any
|
|
sat := false
|
|
for _, lit := range clause {
|
|
v := lit
|
|
if v < 0 {
|
|
v = -v
|
|
}
|
|
binding := pb.units[v-1]
|
|
if binding == 0 {
|
|
unbound++
|
|
if unbound == 1 {
|
|
unit = lit
|
|
} else {
|
|
break
|
|
}
|
|
} else if binding*lit == v { // (binding == -1 && lit < 0) || (binding == 1 && lit > 0) {
|
|
sat = true
|
|
break
|
|
}
|
|
}
|
|
if sat {
|
|
done[i] = true
|
|
continue
|
|
}
|
|
if unbound == 0 {
|
|
// All lits are false: problem is UNSAT
|
|
if i < pb.NbClauses {
|
|
pb.tagged[i] = true
|
|
}
|
|
return true
|
|
}
|
|
if unbound == 1 {
|
|
if unit < 0 {
|
|
pb.units[-unit-1] = -1
|
|
} else {
|
|
pb.units[unit-1] = 1
|
|
}
|
|
done[i] = true
|
|
if i < pb.NbClauses {
|
|
pb.tagged[i] = true
|
|
}
|
|
modified = true
|
|
}
|
|
}
|
|
}
|
|
// Problem is either sat or could not be proven unsat through unit propagation
|
|
return false
|
|
}
|
|
|
|
// CNF returns a representation of the problem using the Dimacs syntax.
|
|
func (pb *Problem) CNF() string {
|
|
lines := make([]string, 1, pb.NbClauses+1)
|
|
lines[0] = fmt.Sprintf("p cnf %d %d", pb.NbVars, pb.NbClauses)
|
|
for i := 0; i < pb.NbClauses; i++ {
|
|
clause := pb.Clauses[i]
|
|
strClause := make([]string, len(clause)+1)
|
|
for i, lit := range clause {
|
|
strClause[i] = fmt.Sprintf("%d", lit)
|
|
}
|
|
strClause[len(clause)] = "0"
|
|
line := strings.Join(strClause, " ")
|
|
lines = append(lines, line)
|
|
}
|
|
return strings.Join(lines, "\n")
|
|
}
|