def __kahn_topological_sort(constraint_problem: ConstraintProblem) -> List[Variable]: variables = constraint_problem.get_unassigned_variables() directed_graph = defaultdict(set) for var in variables: for neighbor in constraint_problem.get_unassigned_neighbors(var): if var not in directed_graph[neighbor]: directed_graph[var].add(neighbor) in_degree = {var: 0 for var in variables} for var in variables: for neighbor in directed_graph[var]: in_degree[neighbor] += 1 zero_in_degree_variables = set(filter(lambda variable: in_degree[variable] == 0, in_degree.keys())) topologically_sorted_unassigned_variables = list() while zero_in_degree_variables: var = zero_in_degree_variables.pop() topologically_sorted_unassigned_variables.append(var) for neighbor in directed_graph[var]: in_degree[neighbor] -= 1 if in_degree[neighbor] == 0: zero_in_degree_variables.add(neighbor) if len(topologically_sorted_unassigned_variables) != len(variables): return list() return topologically_sorted_unassigned_variables
def ac3(constraint_problem: ConstraintProblem, assigned_variable: Variable = None) -> bool: if assigned_variable is not None: # usage of ac3 as part of Maintaining Arc Consistency (MAC) algorithm unassigned_neighbors = constraint_problem.get_unassigned_neighbors( assigned_variable) arcs = {(unassigned_neighbor, assigned_variable) for unassigned_neighbor in unassigned_neighbors} else: arcs = {(variable, neighbor) for variable in constraint_problem.get_unassigned_variables() for neighbor in constraint_problem.get_neighbors(variable)} while arcs: variable, neighbor = arcs.pop() if __revise(constraint_problem, variable, neighbor): if not constraint_problem.get_consistent_domain(variable): return False rest_of_neighbors = constraint_problem.get_neighbors(variable) - { neighbor } if rest_of_neighbors: for other_neighbor in rest_of_neighbors: arcs.add((other_neighbor, variable)) for var in constraint_problem.get_variables(): if not var.domain or not constraint_problem.get_consistent_domain(var): return False return True
def forward_check(constraint_problem: ConstraintProblem, assigned_variable: Variable) -> bool: unassigned_neighbors_frozenset = constraint_problem.get_unassigned_neighbors( assigned_variable) unsatisfiable_neighbors = filter( lambda unassigned_neighbor: not constraint_problem. get_consistent_domain(unassigned_neighbor), unassigned_neighbors_frozenset) return False if any(unsatisfiable_neighbors) else True
def degree_heuristic( constraint_problem: ConstraintProblem, variables: Optional[FrozenSet[Variable]] = None ) -> FrozenSet[Variable]: if variables is not None: # then we're using degree_heuristic as secondary key max_variable = max( variables, key=lambda var: len( constraint_problem.get_unassigned_neighbors(var))) return frozenset({max_variable}) unassigned_variables = constraint_problem.get_unassigned_variables() max_variable = max( unassigned_variables, key=lambda var: len(constraint_problem.get_unassigned_neighbors(var))) max_degree = len(constraint_problem.get_unassigned_neighbors(max_variable)) max_variables = filter( lambda var: len(constraint_problem.get_unassigned_neighbors(var)) == max_degree, unassigned_variables) return frozenset(max_variables)
def least_constraining_value(constraint_problem: ConstraintProblem, variable: Variable) -> list: unassigned_neighbors = constraint_problem.get_unassigned_neighbors( variable) def neighbors_consistent_domain_lengths(value) -> int: variable.assign(value) consistent_domain_lengths = map( lambda neighbor: len( (constraint_problem.get_consistent_domain(neighbor))), unassigned_neighbors) variable.unassign() return sum(consistent_domain_lengths) return sorted(constraint_problem.get_consistent_domain(variable), key=neighbors_consistent_domain_lengths, reverse=True)
def __forward_checking_backtrack( constraint_problem: ConstraintProblem, find_all_solutions: bool = False, with_history: bool = False) -> Optional[Dict[Variable, Any]]: variable, *_ = constraint_problem.get_unassigned_variables() for value in variable.domain: variable.assign(value) if with_history: __actions_history.append((variable, value)) unassigned_neighbors_frozenset = constraint_problem.get_unassigned_neighbors( variable) unsatisfiable_neighbors = filter( lambda unassigned_neighbor: not constraint_problem. get_consistent_domain(unassigned_neighbor), unassigned_neighbors_frozenset) if any(unsatisfiable_neighbors): variable.unassign() if with_history: __actions_history.append((variable, None)) continue if constraint_problem.is_completely_assigned(): if constraint_problem.is_consistently_assigned(): if find_all_solutions: yield constraint_problem.get_current_assignment() else: yield None variable.unassign() if with_history: __actions_history.append((variable, None)) continue if constraint_problem.is_consistently_assigned(): for solution_assignment in __forward_checking_backtrack( constraint_problem, find_all_solutions, with_history): yield solution_assignment variable.unassign() if with_history: __actions_history.append((variable, None))
def __optimized_heuristic_backtrack(constraint_problem: ConstraintProblem, find_all_solutions: bool = False, with_history: bool = False): unassigned_variables = constraint_problem.get_unassigned_variables() min_variable = min( unassigned_variables, key=lambda var: len(constraint_problem.get_consistent_domain(var))) min_remaining_values = len( constraint_problem.get_consistent_domain(min_variable)) min_variables = filter( lambda var: len(constraint_problem.get_consistent_domain(var)) == min_remaining_values, unassigned_variables) selected_unassigned_vars = frozenset(min_variables) if len(selected_unassigned_vars) > 1: selected_variable = max( selected_unassigned_vars, key=lambda var: len( constraint_problem.get_unassigned_neighbors(var))) else: selected_variable, *_ = selected_unassigned_vars unassigned_neighbors = constraint_problem.get_unassigned_neighbors( selected_variable) def neighbors_consistent_domain_lengths(val) -> int: selected_variable.assign(val) consistent_domain_lengths = map( lambda neighbor: len( (constraint_problem.get_consistent_domain(neighbor))), unassigned_neighbors) selected_variable.unassign() return sum(consistent_domain_lengths) sorted_domain = sorted( constraint_problem.get_consistent_domain(selected_variable), key=neighbors_consistent_domain_lengths, reverse=True) for value in sorted_domain: selected_variable.assign(value) if with_history: __actions_history.append((selected_variable, value)) if constraint_problem.is_completely_assigned(): if constraint_problem.is_consistently_assigned(): if find_all_solutions: yield constraint_problem.get_current_assignment() else: yield None selected_variable.unassign() if with_history: __actions_history.append((selected_variable, None)) continue if constraint_problem.is_consistently_assigned(): for solution_assignment in __optimized_heuristic_backtrack( constraint_problem, find_all_solutions, with_history): yield solution_assignment selected_variable.unassign() if with_history: __actions_history.append((selected_variable, None))