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
73from utils import *
84import search
9- import types
105
116class 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
155179def 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
174198def 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
206229def 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