-
Notifications
You must be signed in to change notification settings - Fork 23
/
Copy pathproblem.go
125 lines (118 loc) · 3.06 KB
/
problem.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
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")
}