def test_to_satfmt_singletons():

    symbs = symbols("A,B,C")
    a, b, c = symbs
    expr = cnf.AND([a, b | c])

    mapper = satbridge.SymbolMapper(symbs)
    ints = satbridge.expr_to_satfmt(expr, mapper)

    assert ints == [[1], [2, 3]]
def test_to_satfmt():

    symbs = symbols("A,B,C,D,E,F,G")
    expr = cnf.AND([
        cnf.OR(symbols("A,B,C")),
        cnf.OR(symbols("D,E,F")),
        ~Symbol("D") | ~Symbol("G"),
    ])

    mapper = satbridge.SymbolMapper(symbs)
    ints = satbridge.expr_to_satfmt(expr, mapper)

    assert ints == [[1, 2, 3], [4, 5, 6], [-4, -7]]
def test_tofrom_expr():

    symbs = symbols("A,B,C,D,E,F,G")
    expr = cnf.AND([
        cnf.OR(symbols("A,B,C")),
        cnf.OR(symbols("D,E,F")),
        ~Symbol("D") | ~Symbol("G"),
    ])

    mapper = satbridge.SymbolMapper(symbs)
    ints = satbridge.expr_to_satfmt(expr, mapper)
    sameexpr = satbridge.satfmt_to_expr(ints, mapper)

    assert expr == sameexpr
def test_exactly_n_true():

    symbs = symbols("a,b,c,d,e")
    mapper = satbridge.SymbolMapper(symbs)

    min_constraint = cnf.min_n_true(symbs, 2, mapper=mapper)
    max_constraint = cnf.max_n_true(symbs, 2, mapper=mapper)

    solutions = list(itersolve(min_constraint + max_constraint))

    assert len(list(combinations(symbs, 2))) == len(solutions)

    for sol in solutions:
        true_only = list(filter(lambda x: x > 0, sol))
        assert 2 == len(true_only)
def dnf_equivalence(expr, symbs):

    mapper = satbridge.SymbolMapper(symbs)

    # convert with sympy
    def control(expr):
        cnf_expr = form.to_cnf(expr, simplify=True, force=True)
        sat_in = satbridge.expr_to_satfmt(cnf_expr, mapper)
        return sat_in

    # convert with something I made up
    def experiment(expr):
        return cnf.from_dnf(expr, mapper)

    def get_solns(func):

        # make expression
        before = time()
        cnf_clauses = func(expr)

        # find solutions
        solutions = []
        for sat_out in itersolve(cnf_clauses):
            true_only = list(filter(lambda x: x > 0, sat_out))
            if true_only:
                expr_out = cnf.AND(list(map(mapper.to_symb, true_only)))
                solutions.append(expr_out)

        after = time()
        return solutions, after - before

    # do they yield the same solutions?
    experiment_solns, experiment_duration = get_solns(experiment)
    control_solns, control_duration = get_solns(control)

    print("control", control_duration, "seconds")
    print("experiment", experiment_duration, "seconds")

    # this an obnoxious way to assert equality,
    # but expressions aren't hashable and order doesn't matter

    for c in control_solns:
        assert c in experiment_solns

    for e in experiment_solns:
        assert e in control_solns
def test_bcd():

    # how many ways for 3 consecurive of these to be true?
    symbs = symbols("a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t")
    mapper = satbridge.SymbolMapper(symbs)
    a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t = symbs

    straight_expr = (a & b & c) | (b & c & d) | (c & d & e) | (d & e & f) | (
        g & h & i)
    straight = cnf.from_dnf(straight_expr, mapper=mapper)

    max3 = cnf.max_n_true(symbs, 3, mapper=mapper)

    sat_in = straight + max3
    solutions = list(itersolve(sat_in))

    nofalse = sorted([list(filter(lambda x: x > 0, y)) for y in solutions])
    assert 5 == len(nofalse)
    print(nofalse)
def test_min_n_true():

    symbs = symbols("a,b,c,d,e")
    mapper = satbridge.SymbolMapper(symbs)

    sat_in = cnf.min_n_true(symbs, 3, mapper=mapper)
    print(sat_in)
    ways = list(itersolve(sat_in))

    # none with more than three true
    for way in ways:
        assert len(way) == len(symbs)
        true_symbs = list(filter(lambda x: x > 0, way))
        assert 3 <= len(true_symbs)
        print(true_symbs)

    # one for each subset of size 0, 1, 2, or 3
    expect_num = (
        len(list(combinations(symbs, 3))) + len(list(combinations(symbs, 4))) +
        1  # the full solution
    )
    assert expect_num == len(ways)
示例#8
0
def riddler(answer, fragments, constraints=[]):
    """
    Return an interator over pronouncable riddle strings like
    "tommarvoloriddle" given answer strings like "iamlordvoldemort".

    If constraints are provided, require that they appear as substrings
    and in the given order (potentially with other material in between).

    If fragments is None and constraints are unset, iterate over permutations

    If fragments is None, iterate over permutations with constraints
    """

    # imagine an n*n grid, each row is an input answer letter
    # each column is an output riddle index
    # if the cell is marked, then that answer-letter is mapped to that riddle-index

    #         i                                  x
    #         a             x
    #         m          x
    #         l                                           x
    #         o                            x
    #         r                               x
    # --in--> d                                        X
    #         v                   x
    #         o                      x
    #         l                         x
    #         d                                     x
    #         e                                              x
    #         m       x
    #         o    x
    #         r                x
    #         t x
    #           00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
    #                                                  |
    #                                                  |
    #                                                 out
    #                                                  |
    #                                                  v
    #           t  o  m  m  a  r  v  o  l  o  r  i  d  d  l  e

    letters = clean(answer)
    riddle_positions = list(range(len(letters)))
    letter_numbers = riddle_positions.copy()

    # display numbers with 0 padding
    repr_width = floor(log(max(letter_numbers)))

    def numstr(num):
        template = "{:" + str(repr_width) + "}"
        return template.format(num)

    # a symbol for each cell in the grid above
    # and various ways to reference/describe them
    display_key = {}
    columns = []
    symb_num2riddle_letter = {}
    all_symbols = []
    i = 1
    for idx in riddle_positions:
        column = []
        for char_idx, char in enumerate(letters):

            symb = Symbol(str(i))
            all_symbols.append(symb)
            display_key[i] = f"{numstr(char_idx)}>{char}>{numstr(idx)}"
            symb_num2riddle_letter[i] = char
            column.append(symb)
            i += 1
        columns.append(column)

    rows = map(list, zip(
        *columns))  # transpose: https://stackoverflow.com/a/6473724/1054322

    mapper = satbridge.SymbolMapper(all_symbols)

    print()
    pprint(display_key)

    # at least one allocation per row
    permutation_constraints = []
    for row in rows:
        permutation_constraints.extend(cnf.min_n_true(row, 1, mapper=mapper))

    print(permutation_constraints)

    # at least one allocation per column
    for column in columns:
        permutation_constraints.extend(cnf.min_n_true(column, 1,
                                                      mapper=mapper))

    permutation_constraints.extend(
        cnf.max_n_true(all_symbols, len(letters), mapper=mapper))

    # so far we've just defined a fancy permutation generator
    if fragments is None and not constraints:

        permute = pycosat.itersolve(permutation_constraints)

        while True:
            try:

                # get a solution, treat symbols as integers
                solution = next(permute)
                chosen = list(map(lambda x: int(str(x)), solution))

                # translate into a riddle string
                chosen = filter(lambda x: x > 0, chosen)
                lookup = symb_num2riddle_letter
                riddle = "".join([lookup[sn] for sn in chosen])

                yield riddle

            except StopIteration:
                printerr("done")
                return
    else:
        raise NotImplementedError("Hey Matt, write this part")