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)
Example #4
0
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)
Example #6
0
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
Example #7
0
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))