Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1c9a371

Browse files
committed
Convert csp.py to 3rd edition and fix inference bug.
1 parent e4bc956 commit 1c9a371

File tree

1 file changed

+79
-57
lines changed

1 file changed

+79
-57
lines changed

csp.py

Lines changed: 79 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
1-
"""CSP (Constraint Satisfaction Problems) problems and solvers. (Chapter 5)."""
1+
"""CSP (Constraint Satisfaction Problems) problems and solvers. (Chapter 6)."""
22

3-
# (Written for the second edition of AIMA; expect some discrepanciecs
4-
# from the third edition until this gets reviewed.)
5-
6-
from __future__ import generators
73
from utils import *
84
import search
9-
import types
105

116
class CSP(search.Problem):
127
"""This class describes finite-domain Constraint Satisfaction Problems.
@@ -129,28 +124,57 @@ def choices(self, var):
129124
"Return all values for var that aren't currently ruled out."
130125
return (self.curr_domains or self.domains)[var]
131126

132-
def restore(self, removals):
133-
"Undo all inferences from a supposition."
134-
for B, b in removals:
135-
self.curr_domains[B].append(b)
136-
137127
def infer_assignment(self):
138128
"Return the partial assignment implied by the current inferences."
139129
self.support_pruning()
140130
return dict((v, self.curr_domains[v][0])
141131
for v in self.vars if 1 == len(self.curr_domains[v]))
142132

133+
def restore(self, removals):
134+
"Undo a supposition and all inferences from it."
135+
for B, b in removals:
136+
self.curr_domains[B].append(b)
137+
143138
## This is for min_conflicts search
144139

145140
def conflicted_vars(self, current):
146141
"Return a list of variables in current assignment that are in conflict"
147142
return [var for var in self.vars
148143
if self.nconflicts(var, current[var], current) > 0]
149144

145+
#______________________________________________________________________________
146+
# Constraint Propagation with AC-3
147+
148+
def AC3(csp, queue=None, removals=None):
149+
"""[Fig. 6.3]"""
150+
if queue is None:
151+
queue = [(Xi, Xk) for Xi in csp.vars for Xk in csp.neighbors[Xi]]
152+
csp.support_pruning()
153+
while queue:
154+
(Xi, Xj) = queue.pop()
155+
if revise(csp, Xi, Xj, removals):
156+
if not csp.curr_domains[Xi]:
157+
return False
158+
for Xk in csp.neighbors[Xi]:
159+
if Xk != Xi:
160+
queue.append((Xk, Xi))
161+
return True
162+
163+
def revise(csp, Xi, Xj, removals):
164+
"Return true if we remove a value."
165+
revised = False
166+
for x in csp.curr_domains[Xi][:]:
167+
# If Xi=x conflicts with Xj=y for every possible y, eliminate Xi=x
168+
if every(lambda y: not csp.constraints(Xi, x, Xj, y),
169+
csp.curr_domains[Xj]):
170+
csp.prune(Xi, x, removals)
171+
revised = True
172+
return revised
173+
150174
#______________________________________________________________________________
151175
# CSP Backtracking Search
152176

153-
## Variable ordering
177+
# Variable ordering
154178

155179
def first_unassigned_variable(assignment, csp):
156180
"The default variable order."
@@ -169,7 +193,7 @@ def num_legal_values(csp, var, assignment):
169193
return count_if(lambda val: csp.nconflicts(var, val, assignment) == 0,
170194
csp.domains[var])
171195

172-
## Value ordering
196+
# Value ordering
173197

174198
def unordered_domain_values(var, assignment, csp):
175199
"The default value order."
@@ -180,34 +204,33 @@ def lcv(var, assignment, csp):
180204
return sorted(csp.choices(var),
181205
key=lambda val: csp.nconflicts(var, val, assignment))
182206

183-
## Inference
207+
# Inference
184208

185-
def no_inference(assignment, csp, var, value):
186-
return []
209+
def no_inference(csp, var, value, assignment, removals):
210+
return True
187211

188-
def forward_checking(assignment, csp, var, value):
212+
def forward_checking(csp, var, value, assignment, removals):
189213
"Prune neighbor values inconsistent with var=value."
190-
removals = csp.suppose(var, value)
191214
for B in csp.neighbors[var]:
192215
if B not in assignment:
193216
for b in csp.curr_domains[B][:]:
194217
if not csp.constraints(var, value, B, b):
195218
csp.prune(B, b, removals)
196-
return removals
219+
if not csp.curr_domains[B]:
220+
return False
221+
return True
197222

198-
def mac(assignment, csp, var, value):
223+
def mac(csp, var, value, assignment, removals):
199224
"Maintain arc consistency."
200-
removals = csp.suppose(var, value)
201-
AC3(csp, [(X, var) for X in csp.neighbors[var]], removals)
202-
return removals
225+
return AC3(csp, [(X, var) for X in csp.neighbors[var]], removals)
203226

204-
## The search, proper
227+
# The search, proper
205228

206229
def backtracking_search(csp,
207230
select_unassigned_variable = first_unassigned_variable,
208231
order_domain_values = unordered_domain_values,
209232
inference = no_inference):
210-
"""Fig. 6.5 (3rd edition)
233+
"""[Fig. 6.5]
211234
>>> backtracking_search(australia) is not None
212235
True
213236
>>> backtracking_search(australia, select_unassigned_variable=mrv) is not None
@@ -231,43 +254,19 @@ def backtrack(assignment):
231254
for value in order_domain_values(var, assignment, csp):
232255
if 0 == csp.nconflicts(var, value, assignment):
233256
csp.assign(var, value, assignment)
234-
removals = inference(assignment, csp, var, value)
235-
result = backtrack(assignment)
236-
if result is not None:
237-
return result
257+
removals = csp.suppose(var, value)
258+
if inference(csp, var, value, assignment, removals):
259+
result = backtrack(assignment)
260+
if result is not None:
261+
return result
238262
csp.restore(removals)
239-
csp.unassign(var, assignment)
263+
csp.unassign(var, assignment)
240264
return None
241265

242266
result = backtrack({})
243267
assert result is None or csp.goal_test(result)
244268
return result
245269

246-
#______________________________________________________________________________
247-
# Constraint Propagation with AC-3
248-
249-
def AC3(csp, queue=None, removals=None):
250-
"""[Fig. 5.7]"""
251-
if queue is None:
252-
queue = [(Xi, Xk) for Xi in csp.vars for Xk in csp.neighbors[Xi]]
253-
csp.support_pruning()
254-
while queue:
255-
(Xi, Xj) = queue.pop()
256-
if remove_inconsistent_values(csp, Xi, Xj, removals):
257-
for Xk in csp.neighbors[Xi]:
258-
queue.append((Xk, Xi))
259-
260-
def remove_inconsistent_values(csp, Xi, Xj, removals):
261-
"Return true if we remove a value."
262-
removed = False
263-
for x in csp.curr_domains[Xi][:]:
264-
# If Xi=x conflicts with Xj=y for every possible y, eliminate Xi=x
265-
if every(lambda y: not csp.constraints(Xi, x, Xj, y),
266-
csp.curr_domains[Xj]):
267-
csp.prune(Xi, x, removals)
268-
removed = True
269-
return removed
270-
271270
#______________________________________________________________________________
272271
# Min-conflicts hillclimbing search for CSPs
273272

@@ -294,6 +293,29 @@ def min_conflicts_value(csp, var, current):
294293
return argmin_random_tie(csp.domains[var],
295294
lambda val: csp.nconflicts(var, val, current))
296295

296+
#______________________________________________________________________________
297+
298+
def tree_csp_solver(csp):
299+
"[Fig. 6.11]"
300+
n = len(csp.vars)
301+
assignment = {}
302+
root = csp.vars[0]
303+
X, parent = topological_sort(csp.vars, root)
304+
for Xj in reversed(X):
305+
if not make_arc_consistent(parent[Xj], Xj, csp):
306+
return None
307+
for Xi in X:
308+
if not csp.curr_domains[Xi]:
309+
return None
310+
assignment[Xi] = csp.curr_domains[Xi][0]
311+
return assignment
312+
313+
def topological_sort(xs, x):
314+
unimplemented()
315+
316+
def make_arc_consistent(Xj, Xk, csp):
317+
unimplemented()
318+
297319
#______________________________________________________________________________
298320
# Map-Coloring Problems
299321

@@ -316,8 +338,7 @@ def MapColoringCSP(colors, neighbors):
316338
"""Make a CSP for the problem of coloring a map with different colors
317339
for any two adjacent regions. Arguments are a list of colors, and a
318340
dict of {region: [neighbor,...]} entries. This dict may also be
319-
specified as a string of the form defined by parse_neighbors"""
320-
341+
specified as a string of the form defined by parse_neighbors."""
321342
if isinstance(neighbors, str):
322343
neighbors = parse_neighbors(neighbors)
323344
return CSP(neighbors.keys(), UniversalDict(colors), neighbors,
@@ -475,6 +496,7 @@ class Sudoku(CSP):
475496
8 . . | 2 . 3 | . . 9
476497
. . 5 | . 1 . | 3 . .
477498
>>> AC3(e); e.display(e.infer_assignment())
499+
True
478500
4 8 3 | 9 2 1 | 6 5 7
479501
9 6 7 | 3 4 5 | 8 2 1
480502
2 5 1 | 8 7 6 | 4 9 3

0 commit comments

Comments
 (0)