-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutils.py
184 lines (161 loc) · 6.87 KB
/
utils.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
176
177
178
179
180
181
182
183
184
import random
def is_consistent(graph, variable_value_pairs):
"""
returns True if the variables that have been assigned a value so far are consistent with the constraints,
and False otherwise.
variable_value_pairs can be used to access any value of any variable from the variable as a key
you can use variable_value_pairs.items() to traverse it as (state, color) pairs
variable_value_pairs.keys() to get all the variables,
and variable_value_pairs.values() to get all the values
"""
for variable, color in variable_value_pairs.items():
if color is not None:
for neighbor in graph[variable]:
neighbor_color = variable_value_pairs[neighbor]
if neighbor_color is not None and neighbor_color == color:
return False
return True
def is_solved(graph, variable_value_pairs):
"""
returns True if the CSP is solved, and False otherwise
"""
for variable, color in variable_value_pairs.items():
if color is not None:
for neighbor in graph[variable]:
neighbor_color = variable_value_pairs[neighbor]
if neighbor_color is None or neighbor_color == color:
return False
else:
return False
return True
def get_next_variable(variable_value_pairs, domains):
"""
returns the index of the next variable from the default order of the unassigned variables
"""
for variable, color in variable_value_pairs.items():
if color is None:
return variable
def get_chosen_variable(graph, variable_value_pairs, domains, mode):
"""
returns the next variable that is deemed the best choice by the proper heuristic
use a second heuristic for breaking ties from the first heuristic
"""
least_len_domain = float('inf')
least_domain_var = None
for variable, color in variable_value_pairs.items():
if color is None:
temp_len_domain = len(domains[variable])
if temp_len_domain < least_len_domain:
least_domain_var = variable
least_len_domain = temp_len_domain
# degree heuristic
elif temp_len_domain == least_len_domain:
# Bonus: first if is the optimized ordering for non-filtering mode
if mode == '-n':
degree_temp = 0
degree_least = 0
for neighbor in graph[variable]:
if variable_value_pairs[neighbor] is not None:
degree_temp += 1
for neighbor in graph[least_domain_var]:
if variable_value_pairs[neighbor] is not None:
degree_least += 1
if degree_temp > degree_least:
least_domain_var = variable
least_len_domain = temp_len_domain
else:
degree_temp = 0
degree_least = 0
for neighbor in graph[variable]:
if variable_value_pairs[neighbor] is None:
degree_temp += 1
for neighbor in graph[least_domain_var]:
if variable_value_pairs[neighbor] is None:
degree_least += 1
if degree_temp > degree_least:
least_domain_var = variable
least_len_domain = temp_len_domain
return least_domain_var
def count_constraint_for_value(graph, variable_value_pairs, domains, variable, color):
count = 0
for neighbor in graph[variable]:
if variable_value_pairs[neighbor] is None and color in domains[neighbor]:
count += 1
return count
def get_ordered_domain(graph, variable_value_pairs, domains, variable):
"""
returns the domain of the variable after ordering its values by the proper heuristic
(you may use imports here)
"""
domains[variable].sort(key=lambda color: count_constraint_for_value(graph, variable_value_pairs, domains, variable, color))
return domains[variable]
def forward_check(graph, variable_value_pairs, domains, variable, value):
"""
removes the value assigned to the current variable from its neighbors
returns True if backtracking is necessary, and False otherwise
"""
for neighbor in graph[variable]:
if variable_value_pairs[neighbor] is None:
if value in domains[neighbor]:
domains[neighbor].remove(value)
if not domains[neighbor]:
return True
return False
def ac3(graph, variable_value_pairs, domains):
"""
maintains arc-consistency
returns True if backtracking is necessary, and False otherwise
"""
queue = []
for variable, color in variable_value_pairs.items():
if color is None:
for neighbor in graph[variable]:
queue.append((variable, neighbor))
while queue:
x1, x2 = queue.pop(0)
if remove_inconsistent_values(domains, x1, x2):
if not domains[x1]:
return True
for neighbor in graph[x1]:
queue.append([neighbor, x1])
return False
def remove_inconsistent_values(domains, x1, x2):
removed = False
for color in domains[x1]:
flag = True
for value in domains[x2]:
if color != value:
flag = False
break
if flag:
domains[x1].remove(color)
removed = True
return removed
def random_choose_conflicted_var(graph, variable_value_pairs):
"""
returns a random variable that is conflicting with a constraint
"""
conflicted_vars = []
for var, neighbors in graph.items():
for neighbor in neighbors:
if variable_value_pairs[var] == variable_value_pairs[neighbor]:
conflicted_vars.append(var)
return random.choice(conflicted_vars)
def get_chosen_value(graph, variable_value_pairs, domains, variable):
"""
returns the value by using the proper heuristic
NOTE: handle tie-breaking by random
"""
min_conflicts = float('inf')
chosen_value = None
for color in domains[variable]:
conflicts = 0
for neighbor in graph[variable]:
if variable_value_pairs[neighbor] == color:
conflicts += 1
if conflicts < min_conflicts:
min_conflicts = conflicts
chosen_value = color
elif conflicts == min_conflicts:
chosen_value = random.choice([chosen_value, color])
return chosen_value