Beispiel #1
0
def test_variable_add_value():
    variable = base.Variable()
    domain = "abcdefghijklmn"
    for c in domain:
        variable.add_value(c)

    assert len(domain) == variable.domain_size()
    verify_domain_consistency(variable, domain)
Beispiel #2
0
def test_variable_domain_iteration():
    domain = list(range(10))
    variable = base.Variable(domain)
    assert len(domain) == variable.domain_size()

    domain_of_variable = []
    for value in variable:
        domain_of_variable.append(value)

    assert domain == domain_of_variable
Beispiel #3
0
def test_variable_pruned_index_iteration():
    domain = list(range(10))
    variable = base.Variable(domain)
    variable.prune_at_index(5)
    assert len(domain) - 1 == variable.domain_size()

    domain_of_variable = []
    for value in variable:
        domain_of_variable.append(value)

    assert [d for d in domain if d != 5] == domain_of_variable
    variable.prune_at_index(5, unprune=True)
    assert len(domain) == variable.domain_size()
Beispiel #4
0
def test_nested_iteration():
    # should go through all value combinations
    domain = list(range(10))
    variable = base.Variable(domain)

    value_combos = set()
    for v in variable:
        for vv in variable:
            value_combos.add((v, vv))

    should_produce = set(itertools.product(domain, domain))

    assert value_combos == should_produce
Beispiel #5
0
def solve(size, prop=propagator.ForwardCheck()):
    rows = list(range(size))
    cols = [base.Variable(rows) for _ in range(size)]
    csp = base.CSP(cols)
    for i in range(len(cols)):
        for j in range(i + 1, len(cols)):
            # can't be on same row
            csp.add_constraint(base.DifferentConstraint(cols[i], cols[j]))
            # upward diagonal (have to use default variables to bind at definition time)
            csp.add_constraint(base.FunctionConstraint([cols[i], cols[j]], lambda a, b, i=i, j=j: a != b + (j - i)))
            # downward diagonal
            csp.add_constraint(base.FunctionConstraint([cols[i], cols[j]], lambda a, b, i=i, j=j: a != b - (j - i)))

    bt = search.BacktrackSearch(csp, prop=prop)
    return bt.search()
def csp(nodes: typing.List[Node]):
    def select_next_var(csp):
        min_domain = math.inf
        chosen_i = None
        vs = csp.variables()
        chosen = []

        for i in range(len(vs)):
            v = vs[i]
            n = nodes[i]
            if v.is_assigned():
                continue
            # break ties by choosing variable corresponding to node with highest degree
            if v.domain_size() < min_domain or (
                    v.domain_size() == min_domain
                    and n.degree() > nodes[chosen_i].degree()):
                chosen_i = i
                min_domain = v.domain_size()
                chosen.append(chosen_i)

        # randomly choose from the top of the ones chosen
        if chosen_i is None:
            return None, None

        # choose from the best 3
        chosen_i = random.choice(chosen[-3:])
        return vs[chosen_i], chosen_i

    def select_next_val(csp: base.CSP, var_i):
        node = nodes[var_i]
        vars = csp.variables()

        # we only need to check our neighbouring colours + 1
        neighbouring_colours = set(vars[n.id].assigned_value()
                                   for n in node.neighbours)
        c = 0
        while c in neighbouring_colours:
            c += 1
        yield c

    # can use greedy algorithm as the baseline (won't need more colours than it)
    max_colours, _, assignments = greedy(nodes)

    # TODO select timesouts based on number of nodes and connectedness (problem hardness)
    # how much time to spend on everything
    single_search_timeout = 2000
    # how much time to spend on a single colour step down (increase to allow more restarts)
    colour_timeout = single_search_timeout * 5
    # how much time to spend on trying to reduce additional colours (increase to be more ambitious)
    total_timeout = colour_timeout * 5
    total_start = time.time()

    best_colours = max_colours
    # try to decrease the number of colours
    for num_colours in range(max_colours - 1, 0, -1):
        # create csp problem
        vars = []
        for _ in nodes:
            vars.append(base.Variable(range(num_colours)))

        csp = base.CSP(vars)
        for n in nodes:
            for neighbour in n.neighbours:
                # avoid adding duplicate constraints by having all lower id != higher id
                if n.id < neighbour.id:
                    csp.add_constraint(
                        base.DifferentConstraint(vars[n.id],
                                                 vars[neighbour.id]))

        # try to solve (assume last solution was the best if this time we can't solve for it)
        bt = search.BacktrackSearch(csp, select_next_variable=select_next_var)

        # use randomization and restarts to try to improve results
        # each search will have a timeout
        searcher = timeout.timeout(single_search_timeout)(bt.search)
        solution = None

        # in addition to total timeout
        start_time = time.time()
        while True:
            current_time = time.time()
            total_elapsed = current_time - total_start
            if total_elapsed > total_timeout:
                logger.info("Total timeout {} seconds".format(total_elapsed))
                break

            elapsed = current_time - start_time
            if elapsed > colour_timeout:
                logger.info("Colour timeout {} seconds".format(elapsed))
                break

            try:
                solution = searcher()
                logger.info("Search took {} seconds".format(time.time() -
                                                            current_time))
                if not solution:
                    logger.info(
                        "Could not exhaustively find a solution for {} colours"
                        .format(num_colours))
                    break
            except TimeoutError as e:
                # retry
                continue
            except Exception as e:
                elapsed = time.time() - current_time
                if abs(elapsed - single_search_timeout) > 1:
                    logger.info(
                        "Exception after searching {} seconds".format(elapsed))
                    raise e
                continue

            if solution:
                break

        if solution:
            logger.info("Solution for {} colours found in {} seconds".format(
                num_colours,
                time.time() - start_time))
            best_colours = num_colours
            assignments = solution
        else:
            break

    return best_colours, True, assignments
Beispiel #7
0
def test_variable_enumerated_iteration():
    domain = list(range(9, 0, -1))
    variable = base.Variable(domain)

    verify_domain_consistency(variable, domain)