-
Notifications
You must be signed in to change notification settings - Fork 21
/
lit_mapping.go
215 lines (190 loc) · 5.85 KB
/
lit_mapping.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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
package solver
import (
"fmt"
"strings"
"github.com/go-air/gini/inter"
"github.com/go-air/gini/logic"
"github.com/go-air/gini/z"
"github.com/operator-framework/deppy/pkg/deppy"
)
type DuplicateIdentifier deppy.Identifier
func (e DuplicateIdentifier) Error() string {
return fmt.Sprintf("duplicate identifier %q in input", deppy.Identifier(e))
}
type inconsistentLitMapping []error
func (inconsistentLitMapping) Error() string {
return "internal solver failure"
}
// litMapping performs translation between the input and output types of
// Solve (Constraints, Variables, etc.) and the variables that
// appear in the SAT formula.
type litMapping struct {
inorder []deppy.Variable
variables map[z.Lit]deppy.Variable
lits map[deppy.Identifier]z.Lit
constraints map[z.Lit]deppy.AppliedConstraint
c *logic.C
errs inconsistentLitMapping
}
// newLitMapping returns a new litMapping with its state initialized based on
// the provided slice of Variables. This includes construction of
// the translation tables between Variables/Constraints and the
// inputs to the underlying solver.
func newLitMapping(variables []deppy.Variable) (*litMapping, error) {
d := litMapping{
inorder: variables,
variables: make(map[z.Lit]deppy.Variable, len(variables)),
lits: make(map[deppy.Identifier]z.Lit, len(variables)),
constraints: make(map[z.Lit]deppy.AppliedConstraint),
c: logic.NewCCap(len(variables)),
}
// First pass to assign lits:
for _, variable := range variables {
im := d.c.Lit()
if _, ok := d.lits[variable.Identifier()]; ok {
return nil, DuplicateIdentifier(variable.Identifier())
}
d.lits[variable.Identifier()] = im
d.variables[im] = variable
}
for _, variable := range variables {
for _, constraint := range variable.Constraints() {
m := constraint.Apply(&d, variable.Identifier())
if m == z.LitNull {
// This constraint doesn't have a
// useful representation in the SAT
// inputs.
continue
}
d.constraints[m] = deppy.AppliedConstraint{
Variable: variable,
Constraint: constraint,
}
}
}
return &d, nil
}
// LogicCircuit returns the lit mappings internal logic circuit
// used by constraint for translation into boolean expressions processed by the solver
func (d *litMapping) LogicCircuit() *logic.C {
return d.c
}
// LitOf returns the positive literal corresponding to the Variable
// with the given Identifier.
func (d *litMapping) LitOf(id deppy.Identifier) z.Lit {
m, ok := d.lits[id]
if ok {
return m
}
d.errs = append(d.errs, fmt.Errorf("variable %q referenced but not provided", id))
return z.LitNull
}
// VariableOf returns the Variable corresponding to the provided
// literal, or a zeroVariable if no such Variable exists.
func (d *litMapping) VariableOf(m z.Lit) deppy.Variable {
i, ok := d.variables[m]
if ok {
return i
}
d.errs = append(d.errs, fmt.Errorf("no variable corresponding to %s", m))
return zeroVariable{}
}
// ConstraintOf returns the constraint application corresponding to
// the provided literal, or a zeroConstraint if no such constraint
// exists.
func (d *litMapping) ConstraintOf(m z.Lit) deppy.AppliedConstraint {
if a, ok := d.constraints[m]; ok {
return a
}
d.errs = append(d.errs, fmt.Errorf("no constraint corresponding to %s", m))
return deppy.AppliedConstraint{
Variable: zeroVariable{},
Constraint: zeroConstraint{},
}
}
// Error returns a single error value that is an aggregation of all
// errors encountered during a litMapping's lifetime, or nil if there have
// been no errors. A non-nil return value likely indicates a problem
// with the solver or constraint implementations.
func (d *litMapping) Error() error {
if len(d.errs) == 0 {
return nil
}
s := make([]string, len(d.errs))
for i, err := range d.errs {
s[i] = err.Error()
}
return fmt.Errorf("%d errors encountered: %s", len(s), strings.Join(s, ", "))
}
// AddConstraints adds the current constraints encoded in the embedded circuit to the
// solver g
func (d *litMapping) AddConstraints(g inter.S) {
d.c.ToCnf(g)
}
func (d *litMapping) AssumeConstraints(s inter.S) {
for m := range d.constraints {
s.Assume(m)
}
}
// CardinalityConstrainer constructs a sorting network to provide
// cardinality constraints over the provided slice of literals. Any
// new clauses and variables are translated to CNF and taught to the
// given inter.Adder, so this function will panic if it is in a test
// context.
func (d *litMapping) CardinalityConstrainer(g inter.Adder, ms []z.Lit) *logic.CardSort {
clen := d.c.Len()
cs := d.c.CardSort(ms)
marks := make([]int8, clen, d.c.Len())
for i := range marks {
marks[i] = 1
}
for w := 0; w <= cs.N(); w++ {
marks, _ = d.c.CnfSince(g, marks, cs.Leq(w))
}
return cs
}
// AnchorIdentifiers returns a slice containing the Identifiers of
// every Variable with at least one "Anchor" constraint, in the
// Order they appear in the input.
func (d *litMapping) AnchorIdentifiers() []deppy.Identifier {
var ids []deppy.Identifier
for _, variable := range d.inorder {
for _, constraint := range variable.Constraints() {
if constraint.Anchor() {
ids = append(ids, variable.Identifier())
break
}
}
}
return ids
}
func (d *litMapping) Variables(g inter.S) []deppy.Variable {
var result []deppy.Variable
for _, i := range d.inorder {
if g.Value(d.LitOf(i.Identifier())) {
result = append(result, i)
}
}
return result
}
func (d *litMapping) Lits(dst []z.Lit) []z.Lit {
if cap(dst) < len(d.inorder) {
dst = make([]z.Lit, 0, len(d.inorder))
}
dst = dst[:0]
for _, i := range d.inorder {
m := d.LitOf(i.Identifier())
dst = append(dst, m)
}
return dst
}
func (d *litMapping) Conflicts(g inter.Assumable) []deppy.AppliedConstraint {
whys := g.Why(nil)
as := make([]deppy.AppliedConstraint, 0, len(whys))
for _, why := range whys {
if a, ok := d.constraints[why]; ok {
as = append(as, a)
}
}
return as
}