From 081520758ecb915ca111999d098a4cd26ae3c0b1 Mon Sep 17 00:00:00 2001 From: Antonis Maronikolakis Date: Wed, 22 Mar 2017 21:44:25 +0200 Subject: [PATCH 1/4] Implement Topological Sort - Implemented topological_sort - Added auxiliary function build_topological - Updated call to topological_sort --- csp.py | 52 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/csp.py b/csp.py index 1e97d7780..6a03c8b50 100644 --- a/csp.py +++ b/csp.py @@ -14,12 +14,13 @@ class CSP(search.Problem): """This class describes finite-domain Constraint Satisfaction Problems. A CSP is specified by the following inputs: - variables A list of variables; each is atomic (e.g. int or string). + variables A list of variables; each is atomic (e.g. int or string). domains A dict of {var:[possible_value, ...]} entries. neighbors A dict of {var:[var,...]} that for each variable lists the other variables that participate in constraints. constraints A function f(A, a, B, b) that returns true if neighbors A, B satisfy the constraint when they have values A=a, B=b + In the textbook and in most mathematical definitions, the constraints are specified as explicit pairs of allowable values, but the formulation here is easier to express and more compact for @@ -29,7 +30,7 @@ class CSP(search.Problem): problem, that's all there is. However, the class also supports data structures and methods that help you - solve CSPs by calling a search function on the CSP. Methods and slots are + solve CSPs by calling a search function on the CSP. Methods and slots are as follows, where the argument 'a' represents an assignment, which is a dict of {var:val} entries: assign(var, val, a) Assign a[var] = val; do other bookkeeping @@ -307,8 +308,12 @@ def tree_csp_solver(csp): """[Figure 6.11]""" assignment = {} root = csp.variables[0] - X, parent = topological_sort(csp.variables, root) + X, parent = topological_sort(csp, root) for Xj in reversed(X): + if(Xj == root): + # THIS SHOULD BE REMOVED + continue + if not make_arc_consistent(parent[Xj], Xj, csp): return None for Xi in X: @@ -318,8 +323,45 @@ def tree_csp_solver(csp): return assignment -def topological_sort(xs, x): - raise NotImplementedError +def topological_sort(X, root): + """Returns the topological sort of X starting from the root. + + Input: + X is a list with the nodes of the graph + N is the dictionary with the neighbors of each node + root denotes the root of the graph. + + Output: + stack is a list with the nodes topologically sorted + parents is a dictionary pointing to each node's parent + + Other: + visited shows the state (visited - not visited) of nodes + + """ + nodes = X.variables + neighbors = X.neighbors + + visited = {} + for x in nodes: + visited[x] = False + + stack = [] + parents = {} + + build_topological(root, None, neighbors, visited, stack, parents) + return stack, parents + +def build_topological(node, parent, neighbors, visited, stack, parents): + """Builds the topological sort and the parents of each node in the graph""" + visited[node] = True + + for n in neighbors[node]: + if(not visited[n]): + build_topological(n, node, neighbors, visited, stack, parents) + + parents[node] = parent + stack.insert(0,node) def make_arc_consistent(Xj, Xk, csp): From fca8121f83b4f684bde9f87089e74e25f7507eb9 Mon Sep 17 00:00:00 2001 From: Antonis Maronikolakis Date: Wed, 22 Mar 2017 22:57:24 +0200 Subject: [PATCH 2/4] Use defaultdict to build visited --- csp.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/csp.py b/csp.py index 6a03c8b50..9bb3aed77 100644 --- a/csp.py +++ b/csp.py @@ -342,9 +342,7 @@ def topological_sort(X, root): nodes = X.variables neighbors = X.neighbors - visited = {} - for x in nodes: - visited[x] = False + visited = defaultdict(lambda: False) stack = [] parents = {} From fe03025e4e161e7d5cd66cf82bb32cf30cd773f9 Mon Sep 17 00:00:00 2001 From: Antonis Maronikolakis Date: Wed, 22 Mar 2017 23:07:04 +0200 Subject: [PATCH 3/4] Added test --- tests/test_csp.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/test_csp.py b/tests/test_csp.py index 5bed85c05..803dede74 100644 --- a/tests/test_csp.py +++ b/tests/test_csp.py @@ -274,6 +274,18 @@ def test_universal_dict(): def test_parse_neighbours(): assert parse_neighbors('X: Y Z; Y: Z') == {'Y': ['X', 'Z'], 'X': ['Y', 'Z'], 'Z': ['X', 'Y']} +def test_topological_sort(): + root = 'NT' + Sort, Parents = topological_sort(australia,root) + + assert Sort == ['NT','SA','Q','NSW','V','WA'] + assert Parents['NT'] == None + assert Parents['SA'] == 'NT' + assert Parents['Q'] == 'SA' + assert Parents['NSW'] == 'Q' + assert Parents['V'] == 'NSW' + assert Parents['WA'] == 'SA' + if __name__ == "__main__": pytest.main() From f3bbbd0f1efe28dab2257a36625002bcf0c41204 Mon Sep 17 00:00:00 2001 From: Antonis Maronikolakis Date: Thu, 23 Mar 2017 19:05:30 +0200 Subject: [PATCH 4/4] Update csp.py Skip first item of iteration --- csp.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/csp.py b/csp.py index 9bb3aed77..8c5ecde3d 100644 --- a/csp.py +++ b/csp.py @@ -308,12 +308,9 @@ def tree_csp_solver(csp): """[Figure 6.11]""" assignment = {} root = csp.variables[0] + root = 'NT' X, parent = topological_sort(csp, root) - for Xj in reversed(X): - if(Xj == root): - # THIS SHOULD BE REMOVED - continue - + for Xj in reversed(X[1:]): if not make_arc_consistent(parent[Xj], Xj, csp): return None for Xi in X: