def tree_csp_solver(constraint_problem: ConstraintProblem, with_history: bool = False) \ -> Optional[Deque[Tuple[Variable, Any]]]: actions_history = None if with_history: actions_history = deque() topological_sorted_unassigned_variables = __kahn_topological_sort(constraint_problem) if not topological_sorted_unassigned_variables: return actions_history for i in range(len(topological_sorted_unassigned_variables) - 1, 0, -1): for value in topological_sorted_unassigned_variables[i].domain: topological_sorted_unassigned_variables[i].assign(value) if not constraint_problem.get_consistent_domain(topological_sorted_unassigned_variables[i-1]): topological_sorted_unassigned_variables[i].remove_from_domain(value) topological_sorted_unassigned_variables[i].unassign() if not topological_sorted_unassigned_variables[i].domain: return actions_history for variable in topological_sorted_unassigned_variables: consistent_domain = constraint_problem.get_consistent_domain(variable) if not consistent_domain: return actions_history value, *_ = consistent_domain variable.assign(value) if with_history: actions_history.append((variable, value)) return actions_history
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 minimum_remaining_values( constraint_problem: ConstraintProblem, variables: Optional[FrozenSet[Variable]] = None ) -> FrozenSet[Variable]: if variables is not None: # then we're using minimum_remaining_values as secondary key min_variable = min( variables, key=lambda variable: len( constraint_problem.get_consistent_domain(variable))) return frozenset({min_variable}) 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) return frozenset(min_variables)
def naive_cycle_cutset(constraint_problem: ConstraintProblem, with_history: bool = False) \ -> Optional[Deque[Tuple[Variable, Any]]]: actions_history = None if with_history: actions_history = deque() variables = constraint_problem.get_variables() read_only_variables = constraint_problem.get_assigned_variables() constraints = list(constraint_problem.get_constraints()) constraints.sort(key=lambda constraint: len(constraint.variables), reverse=True) constraint_graph = constraint_problem.get_constraint_graph_as_adjacency_list() for i in range(1, len(constraints)): cutset_constraints = constraints[:i] cutset_variables = set() for cutset_const in cutset_constraints: cutset_variables.update(cutset_const.variables) reduced_graph = {var: neighbors for var, neighbors in constraint_graph.items() if var not in cutset_variables} for var in reduced_graph: reduced_graph[var] -= cutset_variables if __is_tree(reduced_graph): consistent_assignments_list = __get_consistent_assignments(cutset_variables, cutset_constraints, read_only_variables) non_cutset_variables = variables - cutset_variables non_cutset_vars_to_original_domains_map = {var: var.domain for var in non_cutset_variables} for consist_assignment in consistent_assignments_list: for var, value in zip(cutset_variables, consist_assignment): if var not in read_only_variables: var.assign(value) if with_history: actions_history.append((var, value)) for non_cutset_var in non_cutset_variables: if non_cutset_var not in read_only_variables: non_cutset_var.domain = list(constraint_problem.get_consistent_domain(non_cutset_var)) tree_csp_action_history = tree_csp_solver(constraint_problem, with_history) if with_history: actions_history.extend(tree_csp_action_history) if constraint_problem.is_completely_consistently_assigned(): return actions_history for var in variables: if var not in read_only_variables: var.unassign() if with_history: actions_history.append((var, None)) for var in non_cutset_vars_to_original_domains_map: if var not in read_only_variables: var.domain = non_cutset_vars_to_original_domains_map[var] return actions_history
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 i_consistency(constraint_problem: ConstraintProblem, i: int) -> bool: variables = constraint_problem.get_variables() assert 0 < i <= len(variables), "for i = {0}: i <= 0 or (number of variables in constraint_problem) < i.".format(i) i_minus_one_sized_subsets = combinations(variables, i - 1) i_subsets_consistent_assignments = __initialize_i_consistency(variables, i_minus_one_sized_subsets) reducing_domains = True while reducing_domains: reducing_domains = False for subset, ith_variable in i_subsets_consistent_assignments: if __revise_i(constraint_problem, subset, ith_variable, i_subsets_consistent_assignments): reducing_domains = True for var in constraint_problem.get_variables(): if not var.domain or not constraint_problem.get_consistent_domain(var): return False return True
def ac4(constraint_problem: ConstraintProblem) -> bool: support_counter = collections.Counter() variable_value_pairs_supported_by = collections.defaultdict(set) unsupported_variable_value_pairs = collections.deque() __initialize_ac4(constraint_problem.get_constraints(), support_counter, variable_value_pairs_supported_by, unsupported_variable_value_pairs) while unsupported_variable_value_pairs: second_variable, second_value = unsupported_variable_value_pairs.popleft() for first_variable, first_value in variable_value_pairs_supported_by[(second_variable, second_value)]: if first_value in first_variable.domain: support_counter[(first_variable, first_value, second_variable)] -= 1 if support_counter[(first_variable, first_value, second_variable)] == 0: first_variable.remove_from_domain(first_value) unsupported_variable_value_pairs.append((first_variable, first_value)) for var in constraint_problem.get_variables(): if not var.domain or not constraint_problem.get_consistent_domain(var): return False return True
def do_not_sort(constraint_problem: ConstraintProblem, variable: Variable) -> list: return list(constraint_problem.get_consistent_domain(variable))
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))