-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcsp.py
175 lines (138 loc) · 5.51 KB
/
csp.py
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
class CSP:
def __init__(self, variables, domains, neighbors, constraints):
variables = variables or list(domains.keys())
self.variables = variables
self.domains = domains
self.neighbors = neighbors
self.constraints = constraints
self.initial = ()
self.curr_domains = None
self.nassigns = 0
self.n_bt = 0
def assign(self, var, val, assignment):
assignment[var] = val
self.nassigns += 1
def unassign(self, var, assignment):
if var in assignment:
del assignment[var]
def nconflicts(self, var, val, assignment):
count = 0
for var2 in self.neighbors.get(var):
val2 = None
if assignment.__contains__(var2):
val2 = assignment[var2]
if val2 is not None and self.constraints(var, val, var2, val2) is False:
count += 1
return count
def display(self, assignment):
print('CSP:', self, 'with assignment:', assignment)
def goal_test(self, state):
assignment = dict(state)
return (len(assignment) == len(self.variables)
and all(self.nconflicts(variables, assignment[variables], assignment) == 0
for variables in self.variables))
def support_pruning(self):
if self.curr_domains is None:
self.curr_domains = {v: list(self.domains[v]) for v in self.variables}
def suppose(self, var, value):
self.support_pruning()
removals = [(var, a) for a in self.curr_domains[var] if a != value]
self.curr_domains[var] = [value]
return removals
def prune(self, var, value, removals):
self.curr_domains[var].remove(value)
if removals is not None:
removals.append((var, value))
def choices(self, var):
return (self.curr_domains or self.domains)[var]
def restore(self, removals):
for B, b in removals:
self.curr_domains[B].append(b)
def AC3(csp, queue=None, removals=None):
if queue is None:
queue = [(Xi, Xk) for Xi in csp.variables for Xk in csp.neighbors[Xi]]
csp.support_pruning()
while queue:
(Xi, Xj) = queue.pop()
if revise(csp, Xi, Xj, removals):
if not csp.curr_domains[Xi]:
return False
for Xk in csp.neighbors[Xi]:
if Xk != Xi:
queue.append((Xk, Xi))
return True
def revise(csp, Xi, Xj, removals):
revised = False
for x in csp.curr_domains[Xi][:]:
# If Xi=x conflicts with Xj=y for every possible y, eliminate Xi=x
if all(not csp.constraints(Xi, x, Xj, y) for y in csp.curr_domains[Xj]):
csp.prune(Xi, x, removals)
revised = True
return revised
def first_unassigned_variable(assignment, csp):
for var in csp.variables:
if var not in assignment:
return var
def mrv(assignment, csp):
vars_to_check = []
size = []
for v in csp.variables:
if v not in assignment.keys():
vars_to_check.append(v)
size.append(num_legal_values(csp, v, assignment))
return vars_to_check[size.index(min(size))]
def num_legal_values(csp, var, assignment):
if csp.curr_domains:
return len(csp.curr_domains[var])
else:
count = 0
for val in csp.domains[var]:
if csp.nconflicts(var, val, assignment) == 0:
count += 1
return count
def unordered_domain_values(var, assignment, csp):
"""The default value order."""
return csp.choices(var)
def lcv(var, assignment, csp):
"""Least-constraining-values heuristic."""
return sorted(csp.choices(var),
key=lambda val: csp.nconflicts(var, val, assignment))
def no_inference(csp, var, value, assignment, removals):
return True
def forward_checking(csp, var, value, assignment, removals):
for B in csp.neighbors[var]:
if B not in assignment:
for b in csp.curr_domains[B][:]:
if not csp.constraints(var, value, B, b):
csp.prune(B, b, removals)
if not csp.curr_domains[B]:
return False
return True
def mac(csp, var, value, assignment, removals):
return AC3(csp, [(X, var) for X in csp.neighbors[var]], removals)
def backtracking_search(csp,
select_unassigned_variable,
order_domain_values,
inference):
def backtrack(assignment):
if len(assignment) == len(csp.variables):
return assignment
var = select_unassigned_variable(assignment, csp)
for value in order_domain_values(var, assignment, csp):
if 0 == csp.nconflicts(var, value, assignment):
csp.assign(var, value, assignment)
removals = csp.suppose(var, value)
if inference(csp, var, value, assignment, removals):
result = backtrack(assignment)
if result is not None:
return result
else:
csp.n_bt += 1
csp.restore(removals)
csp.unassign(var, assignment)
return None
result = backtrack({})
assert result is None or csp.goal_test(result)
return result
def different_values_constraint(A, a, B, b):
return a != b