-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathconstraint.go
209 lines (181 loc) · 7.72 KB
/
constraint.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
// Copyright 2022 Gabriel Boorse
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package centipede
import (
"fmt"
"golang.org/x/exp/constraints"
)
// Constraint CSP constraint considering integer variables
type Constraint[T comparable] struct {
Vars VariableNames
ConstraintFunction VariablesConstraintFunction[T]
}
// Constraints collection type for Constraint
type Constraints[T comparable] []Constraint[T]
// VariablesConstraintFunction function used to determine validity of Variables
type VariablesConstraintFunction[T comparable] func(variables *Variables[T]) bool
// AllSatisfied check if a collection of Constraints are satisfied
func (constraints *Constraints[T]) AllSatisfied(variables *Variables[T]) bool {
flag := true
for _, constraint := range *constraints {
flag = flag && constraint.Satisfied(variables)
}
return flag
}
// FilterByName return all constraints related to a particular variable name
func (constraints *Constraints[T]) FilterByName(name VariableName) Constraints[T] {
filtered := make(Constraints[T], 0)
for _, constraint := range *constraints {
if constraint.Vars.Contains(name) {
filtered = append(filtered, constraint)
}
}
return filtered
}
// FilterByOrder return all constraints with the given order (number of related variables)
func (constraints *Constraints[T]) FilterByOrder(order int) Constraints[T] {
filtered := make(Constraints[T], 0)
for _, constraint := range *constraints {
if len(constraint.Vars) == order {
filtered = append(filtered, constraint)
}
}
return filtered
}
// Satisfied checks to see if the given Constraint is satisfied by the variables presented
func (constraint *Constraint[T]) Satisfied(variables *Variables[T]) bool {
constraintVariablesSatisfied := true
domainSatisfied := true
for _, varname := range constraint.Vars {
// make sure Variables contains an object for each name in Constraint.Vars
constraintVariablesSatisfied = constraintVariablesSatisfied && (variables.Contains(varname))
}
for _, variable := range *variables {
// make sure each Variable being passed in has a value consistent with its domain or is empty
domainSatisfied = domainSatisfied && (variable.Domain.Contains(variable.Value) || variable.Empty)
if !variable.Domain.Contains(variable.Value) && !variable.Empty {
fmt.Printf("Variable %v with domain %v does not support value %v\n", variable.Name, variable.Domain, variable.Value)
}
}
if !constraintVariablesSatisfied {
panic(fmt.Sprintf("Insufficient variables provided. Expected %v", constraint.Vars))
}
if !domainSatisfied {
panic("Variables do not satisfy the domains given.")
}
// now finally call the constraint function
return constraint.ConstraintFunction(variables)
}
// Equals Constraint generator that checks if two vars are equal
func Equals[T comparable](var1 VariableName, var2 VariableName) Constraint[T] {
return Constraint[T]{VariableNames{var1, var2}, func(variables *Variables[T]) bool {
if variables.Find(var1).Empty || variables.Find(var2).Empty {
return true
}
return variables.Find(var1).Value == variables.Find(var2).Value
}}
}
// NotEquals Constraint generator that checks if two vars are not equal
func NotEquals[T comparable](var1 VariableName, var2 VariableName) Constraint[T] {
return Constraint[T]{VariableNames{var1, var2}, func(variables *Variables[T]) bool {
if variables.Find(var1).Empty || variables.Find(var2).Empty {
return true
}
return variables.Find(var1).Value != variables.Find(var2).Value
}}
}
// UnaryEquals Unary constraint that checks if var1 equals some constant
func UnaryEquals[T comparable](var1 VariableName, value interface{}) Constraint[T] {
return Constraint[T]{VariableNames{var1}, func(variables *Variables[T]) bool {
if variables.Find(var1).Empty {
return true
}
return variables.Find(var1).Value == value
}}
}
// UnaryNotEquals Unary constraint that checks if var1 is not equal to some constant
func UnaryNotEquals[T comparable](var1 VariableName, value interface{}) Constraint[T] {
return Constraint[T]{VariableNames{var1}, func(variables *Variables[T]) bool {
if variables.Find(var1).Empty {
return true
}
return variables.Find(var1).Value != value
}}
}
// LessThan Constraint generator that checks if first variable is less than second variable
func LessThan[T constraints.Integer | constraints.Float](var1 VariableName, var2 VariableName) Constraint[T] {
return Constraint[T]{VariableNames{var1, var1}, func(variables *Variables[T]) bool {
if variables.Find(var1).Empty || variables.Find(var2).Empty {
return true
}
return variables.Find(var1).Value < variables.Find(var2).Value
}}
}
// GreaterThan Constraint generator that checks if first variable is less than second variable
func GreaterThan[T constraints.Integer | constraints.Float](var1 VariableName, var2 VariableName) Constraint[T] {
return Constraint[T]{VariableNames{var1, var1}, func(variables *Variables[T]) bool {
if variables.Find(var1).Empty || variables.Find(var2).Empty {
return true
}
return variables.Find(var1).Value > variables.Find(var2).Value
}}
}
// LessThanOrEqualTo Constraint generator that checks if first variable is less than or equal to second variable
func LessThanOrEqualTo[T constraints.Integer | constraints.Float](var1 VariableName, var2 VariableName) Constraint[T] {
return Constraint[T]{VariableNames{var1, var1}, func(variables *Variables[T]) bool {
if variables.Find(var1).Empty || variables.Find(var2).Empty {
return true
}
return variables.Find(var1).Value <= variables.Find(var2).Value
}}
}
// GreaterThanOrEqualTo Constraint generator that checks if first variable is less than or equal to second variable
func GreaterThanOrEqualTo[T constraints.Integer | constraints.Float](var1 VariableName, var2 VariableName) Constraint[T] {
return Constraint[T]{VariableNames{var1, var1}, func(variables *Variables[T]) bool {
if variables.Find(var1).Empty || variables.Find(var2).Empty {
return true
}
return variables.Find(var1).Value >= variables.Find(var2).Value
}}
}
// AllEquals Constraint generator that checks that all given variables are equal
func AllEquals[T comparable](varnames ...VariableName) Constraints[T] {
return mapCombinationsToBinaryConstraint(varnames, Equals[T])
}
// AllUnique Constraint generator to check if all variable values are unique
func AllUnique[T comparable](varnames ...VariableName) Constraints[T] {
return mapCombinationsToBinaryConstraint(varnames, NotEquals[T])
}
func mapCombinationsToBinaryConstraint[T comparable](varnames VariableNames, fx func(VariableName, VariableName) Constraint[T]) Constraints[T] {
if len(varnames) <= 0 {
panic("Not enough variable names provided!")
}
constraints := make(Constraints[T], 0)
// map of commutative, unique pairs
uniqueMap := make(map[[2]VariableName]struct{})
for _, name1 := range varnames {
for _, name2 := range varnames {
// if we've already seen this pair before, continue
if _, ok := uniqueMap[[2]VariableName{name1, name2}]; ok {
continue
} else if _, ok := uniqueMap[[2]VariableName{name2, name1}]; ok {
continue
}
// we don't want to make constraints for A == A or A != A
if name1 == name2 {
continue
}
uniqueMap[[2]VariableName{name1, name2}] = struct{}{}
constraints = append(constraints, fx(name1, name2))
}
}
return constraints
}