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)
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")