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

Skip to content

Commit dfe938f

Browse files
antmarakisnorvig
authored andcommitted
Implementation: Tree CSP Solver (#434)
* Update csp.py * Add test
1 parent 86a1908 commit dfe938f

File tree

2 files changed

+49
-10
lines changed

2 files changed

+49
-10
lines changed

csp.py

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def display(self, assignment):
8585
# Subclasses can print in a prettier way, or display with a GUI
8686
print('CSP:', self, 'with assignment:', assignment)
8787

88-
# These methods are for the tree- and graph-search interface:
88+
# These methods are for the tree and graph-search interface:
8989

9090
def actions(self, state):
9191
"""Return a list of applicable actions: nonconflicting
@@ -308,15 +308,18 @@ def tree_csp_solver(csp):
308308
"""[Figure 6.11]"""
309309
assignment = {}
310310
root = csp.variables[0]
311-
root = 'NT'
312311
X, parent = topological_sort(csp, root)
312+
313+
csp.support_pruning()
313314
for Xj in reversed(X[1:]):
314315
if not make_arc_consistent(parent[Xj], Xj, csp):
315316
return None
316-
for Xi in X:
317-
if not csp.curr_domains[Xi]:
317+
318+
assignment[root] = csp.curr_domains[root][0]
319+
for Xi in X[1:]:
320+
assignment[Xi] = assign_value(parent[Xi], Xi, csp, assignment)
321+
if not assignment[Xi]:
318322
return None
319-
assignment[Xi] = csp.curr_domains[Xi][0]
320323
return assignment
321324

322325

@@ -361,7 +364,34 @@ def build_topological(node, parent, neighbors, visited, stack, parents):
361364

362365

363366
def make_arc_consistent(Xj, Xk, csp):
364-
raise NotImplementedError
367+
"""Make arc between parent (Xj) and child (Xk) consistent under the csp's constraints,
368+
by removing the possible values of Xj that cause inconsistencies."""
369+
#csp.curr_domains[Xj] = []
370+
for val1 in csp.domains[Xj]:
371+
keep = False # Keep or remove val1
372+
for val2 in csp.domains[Xk]:
373+
if csp.constraints(Xj, val1, Xk, val2):
374+
# Found a consistent assignment for val1, keep it
375+
keep = True
376+
break
377+
378+
if not keep:
379+
# Remove val1
380+
csp.prune(Xj, val1, None)
381+
382+
return csp.curr_domains[Xj]
383+
384+
385+
def assign_value(Xj, Xk, csp, assignment):
386+
"""Assign a value to Xk given Xj's (Xk's parent) assignment.
387+
Return the first value that satisfies the constraints."""
388+
parent_assignment = assignment[Xj]
389+
for val in csp.curr_domains[Xk]:
390+
if csp.constraints(Xj, parent_assignment, Xk, val):
391+
return val
392+
393+
# No consistent assignment available
394+
return None
365395

366396
# ______________________________________________________________________________
367397
# Map-Coloring Problems
@@ -389,8 +419,8 @@ def different_values_constraint(A, a, B, b):
389419

390420
def MapColoringCSP(colors, neighbors):
391421
"""Make a CSP for the problem of coloring a map with different colors
392-
for any two adjacent regions. Arguments are a list of colors, and a
393-
dict of {region: [neighbor,...]} entries. This dict may also be
422+
for any two adjacent regions. Arguments are a list of colors, and a
423+
dict of {region: [neighbor,...]} entries. This dict may also be
394424
specified as a string of the form defined by parse_neighbors."""
395425
if isinstance(neighbors, str):
396426
neighbors = parse_neighbors(neighbors)
@@ -400,9 +430,9 @@ def MapColoringCSP(colors, neighbors):
400430

401431
def parse_neighbors(neighbors, variables=[]):
402432
"""Convert a string of the form 'X: Y Z; Y: Z' into a dict mapping
403-
regions to neighbors. The syntax is a region name followed by a ':'
433+
regions to neighbors. The syntax is a region name followed by a ':'
404434
followed by zero or more region names, followed by ';', repeated for
405-
each region name. If you say 'X: Y' you don't need 'Y: X'.
435+
each region name. If you say 'X: Y' you don't need 'Y: X'.
406436
>>> parse_neighbors('X: Y Z; Y: Z') == {'Y': ['X', 'Z'], 'X': ['Y', 'Z'], 'Z': ['X', 'Y']}
407437
True
408438
"""

tests/test_csp.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@ def test_universal_dict():
338338
def test_parse_neighbours():
339339
assert parse_neighbors('X: Y Z; Y: Z') == {'Y': ['X', 'Z'], 'X': ['Y', 'Z'], 'Z': ['X', 'Y']}
340340

341+
341342
def test_topological_sort():
342343
root = 'NT'
343344
Sort, Parents = topological_sort(australia,root)
@@ -351,5 +352,13 @@ def test_topological_sort():
351352
assert Parents['WA'] == 'SA'
352353

353354

355+
def test_tree_csp_solver():
356+
australia_small = MapColoringCSP(list('RB'),
357+
'NT: WA Q; NSW: Q V')
358+
tcs = tree_csp_solver(australia_small)
359+
assert (tcs['NT'] == 'R' and tcs['WA'] == 'B' and tcs['Q'] == 'B' and tcs['NSW'] == 'R' and tcs['V'] == 'B') or \
360+
(tcs['NT'] == 'B' and tcs['WA'] == 'R' and tcs['Q'] == 'R' and tcs['NSW'] == 'B' and tcs['V'] == 'R')
361+
362+
354363
if __name__ == "__main__":
355364
pytest.main()

0 commit comments

Comments
 (0)