def forward_ehalver(g, n, k, v, s, t): m = n//2 c2 = [] type1 = set() type3 = set() i = k % m imask = 1<<i for j in range(m): jmask = 1<<(j+m) if (not (v & imask)) and (v & jmask): type1.add(j) elif (v & imask) and (not (v & jmask)): w = v + jmask - imask c2.append(Implies(g[k, j], Equal(t[v], s[v] | s[w])).to_cnf()) else: type3.add(j) if len(type1) > 0: cmp1 = [g[k, j] for j in range(m) if j not in type1] c1 = [Or(*(cmp1 + [~t[v]]))] else: c1 = [] if len(type3) > 0: cmp3 = [g[k, j] for j in range(m) if j not in type3] c3 = [Or(*(cmp3 + [~t[v], s[v]])), Or(*(cmp3 + [t[v], ~s[v]]))] else: c3 = [] return c1 + c2 + c3
def forward_size(g, n, k, v, s, t): c2 = [] type1 = set() type3 = set() for i in range(n - 1): for j in range(i + 1, n): imask = 1 << i jmask = 1 << j if (not (v & imask)) and (v & jmask): type1.add((i, j)) elif (v & imask) and (not (v & jmask)): w = v + jmask - imask c2.append( Implies(g[k, i, j], Equal(t[v], s[v] | s[w])).to_cnf()) else: type3.add((i, j)) if len(type1) > 0: cmp1 = [ g[k, i, j] for i in range(n - 1) for j in range(i + 1, n) if (i, j) not in type1 ] c1 = [Or(*(cmp1 + [~t[v]]))] else: c1 = [] if len(type3) > 0: cmp3 = [ g[k, i, j] for i in range(n - 1) for j in range(i + 1, n) if (i, j) not in type3 ] c3 = [Or(*(cmp3 + [~t[v], s[v]])), Or(*(cmp3 + [t[v], ~s[v]]))] else: c3 = [] return c1 + c2 + c3
def test_only_one_root(self): small_puzzle = P.Puzzle(["1", "2"], [["a", "b"]], []) sx = small_puzzle.X root_rule_form = (And( Or( And(sx[0,0,0], ~sx[0,0,1]), And(~sx[0,0,0], sx[0,0,1])), Or( And(sx[0,1,0], ~sx[0,1,1]), And(~sx[0,1,0], sx[0,1,1])))) self.assertTrue(And(*small_puzzle.only_one_root()).equivalent(root_rule_form))
def update(g, first, last, k, i, v, w): c = [] c += [Or(~v[i], oneDown(g, k, first, i), w).to_cnf()] c += [Or(v[i], oneUp(g, k, i, last), ~w).to_cnf()] c += [Or(used(g, first, last, k, i), Equal(w, v[i])).to_cnf()] c += [ Implies(g[k, j, i], Equal(w, v[j] & v[i])).to_cnf() for j in range(first, i) ] c += [ Implies(g[k, i, j], Equal(w, v[j] | v[i])).to_cnf() for j in range(i + 1, last) ] return c
def no_adjacent_unused_channels(g, n, d): c = [g[d - 1, 0, 1] | g[d - 1, 1, 2]] c += [ Or(g[d - 1, i - 1, i], g[d - 1, i, i + 1], g[d - 1, i + 1, i + 2]) for i in range(1, n - 2) ] c += [g[d - 1, n - 3, n - 2] | g[d - 1, n - 2, n - 1]] return c
def once_size(g, n, k): c = [ ~g[k, i, j] | ~g[k, l, m] for i in range(n - 1) for j in range(i + 1, n) for l in range(i, n) for m in range(l + 1, n) if (i != l) or (j < m) ] c.append(Or(*[g[k, i, j] for i in range(n) for j in range(i + 1, n)])) return c
def valid_ehalver(g, n, d): m = n//2 c = [] for k in range(0, d*m, m): for i in range(m): c += [~g[k + i, j] | ~g[k + i, l] for j in range(m) for l in range(j + 1, m)] c += [~g[k + j, i] | ~g[k + l, i] for j in range(m) for l in range(j + 1, m)] c.append(Or(*[g[k + i, j] for j in range(m)])) return c
def satisfy_all(rows_vars, cols_vars, horizontal, vertical): """Uses a SAT Solver to satisfy all horizontal and vertical propositions""" terms = [] for i, row_var in enumerate(rows_vars): row_terms = [] for combination in gen_all_combinations(horizontal[i], len(horizontal)): row_terms.append( And(*list(v if p else Not(v) for v, p in zip(row_var, combination)))) terms.append(Or(*row_terms)) for i, col_var in enumerate(cols_vars): col_terms = [] for combination in gen_all_combinations(vertical[i], len(vertical)): col_terms.append( And(*list(v if p else Not(v) for v, p in zip(col_var, combination)))) terms.append(Or(*col_terms)) return And(*terms).tseitin().satisfy_all()
def HMerge(a, b, prefix=''): n = len(a) if n == 1: if prefix == '': prefix = 'c' c = exprvars(prefix, 2) s = [Or(~a[0], ~b[0], c[1]), ~a[0] | c[0], ~b[0] | c[0]] return (c, s) else: c = exprvars(prefix + 'c', (1, 2 * n - 1)) d, sd = HMerge(even(a), even(b), prefix + 'd') e, se = HMerge(odd(a), odd(b), prefix + 'e') v = [d[0]] + [x for x in c] + [e[-1]] sp = [] for i in range(n - 1): sp.append(Or(*(~d[i + 1], ~e[i], c[2 * i + 2]))) sp.append(~d[i + 1] | c[2 * i + 1]) sp.append(~e[i] | c[2 * i + 1]) s = sd + se + sp return (v, s)
def second_last_layer(g, n, d): c = [~g[d - 2, i, j] for i in range(n) for j in range(i + 4, n)] c += [ Or(~g[d - 2, i, i + 2], g[d - 1, i, i + 1], g[d - 1, i + 1, i + 2]) for i in range(n - 2) ] c += [ ~g[d - 2, i, i + 3] | g[d - 1, k, k + 1] for i in range(n - 3) for k in (i, i + 2) ] return c
def SMerge(a, b, prefix=''): n = len(a) if n == 1: if prefix == '': prefix = 'c' c = exprvars(prefix, 2) s = [Or(~a[0], ~b[0], c[1]), ~a[0] | c[0], ~b[0] | c[0]] return (c, s) else: c = exprvars(prefix + 'c', (1, n + 1)) d, sd = SMerge(even(a), even(b), prefix + 'd') e, se = SMerge(odd(a), odd(b), prefix + 'e') v = [d[0]] + list(c) sp = [] for i in range(n // 2): sp.append(Or(~d[i + 1], ~e[i], c[2 * i + 2])) sp.append(~d[i + 1] | c[2 * i + 1]) sp.append(~e[i] | c[2 * i + 1]) s = sd + se + sp return (v, s)
def are_not(self, value1, value2): """ Returns the formula where the two given (non-root) values are a not-- they do not occur at the same root value :param value1: str- an english variable that is in self.groups :param value1: str- an english variable that is in self.groups, that is not in the same category as value1 :return Expr: the formula for this clue """ (group_1, val_1) = self.get_val_tuple(value1) (group_2, val_2) = self.get_val_tuple(value2) f_arenot = Or(*[ And(self.X[group_1, val_1, idx], ~self.X[group_2, val_2, idx]) for idx in range(0, self.items_per) ]) return f_arenot
def once_depth(g, n, k): clauses_from = [ ~g[k, i, j] | ~g[k, i, l] for j in range(n) for l in range(j) for i in range(l) ] clauses_to = [ ~g[k, j, i] | ~g[k, l, i] for j in range(n) for l in range(j + 1, n) for i in range(l + 1, n) ] clauses_to_from = [ ~g[k, j, i] | ~g[k, i, l] for j in range(n) for i in range(j + 1, n) for l in range(i + 1, n) ] once = [Or(*[g[k, i, j] for i in range(n) for j in range(i + 1, n)])] c = clauses_from + clauses_to + clauses_to_from + once return c
def sorts_backward_size(g, n, kf, kl, xlist, s=0): ninputs = 1 << n c = [] xset = set(xlist) # Input: TRUE for each input that is not sorted input = [TRUE if v != vsort(v) else FALSE for v in range(ninputs)] # Output variables o = exprvars('o', ninputs) if s == 0: # All sorted: 0 if already sorted by a prefix output = [0 if v in xset else o[v] for v in range(ninputs)] elif isinstance(s, (list, set)): # Exceptions: 1 to keep inputs in s unsorted output = [1 if v in s else 0 for v in range(ninputs)] else: # Fixed number of exceptions (unsorted inputs) output = o if s == 1: c_size = [Or(*output)] c_size += [ ~output[i] | ~output[j] for i in range(ninputs) for j in range(i + 1, ninputs) ] elif s > 1: card, c_size = Card(output, s + 1) c_size += [~card[s]] c += c_size d = kl - kf # Backward layer iteration if d == 1: for v in range(ninputs): c += backward_size(g, n, kf, v, input, output) else: s = exprvars('s', d - 1, ninputs) for v in range(ninputs): c += backward_size(g, n, kl - 1, v, input, s[0]) for k in range(1, d - 1): for v in range(ninputs): c += backward_size(g, n, kl - k - 1, v, s[k - 1], s[k]) for v in range(ninputs): c += backward_size(g, n, kl - d, v, s[d - 2], output) return c
def sorts_backward_depth(g, n, kf, kl, xlist=None, s=0): ninputs = 1 << n if xlist is None: xlist = range(ninputs) xset = set(xlist) c = [] # FALSE for each input that is not sorted input = [TRUE if v != vsort(v) else FALSE for v in range(ninputs)] # all sorted o = exprvars('o', ninputs) if s == 0: output = [FALSE if v in xset else o[v] for v in range(ninputs)] elif isinstance(s, (list, set)): output = [TRUE if v in s else FALSE for v in range(ninputs)] else: output = o if s == 1: c_size = [Or(*output)] c_size += [ ~output[i] | ~output[j] for i in range(ninputs) for j in range(i + 1, ninputs) ] elif s > 1: card, c_size = Card(output, s + 1, prefix='o_') c_size += [~card[s]] c += c_size d = kl - kf aux = exprvars('ly', d, n - 2, ninputs) if n > 2 else [None] if d == 1: c += backward_depth(g, n, kf, input, output, aux[0]) else: s = exprvars('s', d - 1, ninputs) c += backward_depth(g, n, kl - 1, input, s[0], aux[0]) for k in range(1, d - 1): c += backward_depth(g, n, kl - k - 1, s[k - 1], s[k], aux[k]) c += backward_depth(g, n, kl - d, s[d - 2], output, aux[d - 1]) return c
def all_adjacent(g, n, d): c = [Or(*[g[k, i, i + 1] for k in range(d)]) for i in range(n - 1)] return c
def comp(x1, x2, y1, y2): return [Or(~x1, ~x2, y2), ~x1 | y1, ~x2 | y1]
def oneUp(g, k, i, j): c = [g[k, i, l] for l in range(i + 1, j)] return Or(*c)
def test_translate_f(self): f = (Or(And(self.ex_puzzle.X[0,2,2], self.ex_puzzle.X[2,0,1]), And(self.ex_puzzle.X[0,2,1], self.ex_puzzle.X[2,0,0]))) f_str = 'Or(And(Starbuck_3, Batman_2), And(Starbuck_2, Batman_1))' self.assertEqual(self.ex_puzzle.translate_f(f), f_str)
def used(g, first, last, k, i): c = [g[k, i, j] for j in range(i + 1, last)] + [g[k, j, i] for j in range(first, i)] return Or(*c)
def oneDown(g, k, i, j): c = [g[k, l, j] for l in range(i, j)] return Or(*c)
def _to_pyeda_expr(self, val_to_sym: MutableMapping[T, str]) -> Expression: return Or(*(expr._to_pyeda_expr(val_to_sym) for expr in self.exprs))
def search(n, d, s, u=0, epsilon=1/4, opt='d', solver='picosat', lprefix=None, wsize=0, task=None, nthreads=4): if wsize <= 0: wsize = n + wsize print("n:", n, "d:", d, "s:", s, "u:", u, "opt:", opt, "solver:", solver, "wsize:", wsize, "task:", task, "prefix:", lprefix, "nthreads:", nthreads, file=sys.stderr) if opt.startswith('h'): # Comparator variables g = variables(n, d) # Empty prefix c_prefix = [] # valid constraints c_valid = valid_depth1(g, n, d) # size constraints if s: ncard = s gcard = [g[k, i, j] for i in range(n) for j in range(i+1, n) for k in range(d)] c, c_size = Card(gcard, ncard + 1, prefix='g_') if len(c) > ncard: c_size += [~c[ncard]] else: c_size = [] c_sorts = halver(g, n, 0, d, epsilon) elif opt.startswith('e'): m = n//2 g = exprvars('g', d*m, m) c_valid = valid_ehalver(g, n, d) c_prefix = [g[i, m+i] for i in range(m)] d0 = 1 c_size = [] net = Network(to_ehalver(c_prefix, g, n, d0)) xlist = net.outputs(n, range(1<<n)) c_sorts = halver(g, n, d0, d, epsilon, xlist) elif opt.startswith('s1'): d = s g = variables(n, d) c_prefix = [] c_valid = valid_size1(g, n, d) c_size = [] c_sorts = sorts_backward_size(g, n, 0, d, [], u) elif opt.startswith('s'): d = s g = variables(n, d) if lprefix is None: lprefix = [[(0, 1)]] c_prefix = [g[k, i, j] if (i, j) in layer else ~g[k, i, j] for k, layer in enumerate(lprefix) for i in range(n-1) for j in range(i+1, n)] if n > 3: c_prefix.append(Or(g[1, 0, 2], g[1, 2, 3])) else: c_prefix = [g[k, i, j] if (i, j) in layer else ~g[k, i, j] for k, layer in enumerate(lprefix) for i in range(n-1) for j in range(i+1, n)] net = Network(lprefix) d0 = len(net) c_valid = valid_size(g, n, d0, d) c_size = [] inputs = net.not_sorted(n) inputs = [x for x in inputs if window_size(x, n) > 2 and window_size(x, n) <= wsize] xlist = net.outputs(n, inputs) print("d0:", d0, "len(xlist):", len(xlist), file=sys.stderr) c_sorts = sorts(g, n, d0, d, xlist) elif opt.startswith('d1'): g = variables(n, d) c_valid = valid_depth1(g, n, d) c_prefix = [] if s: c, c_size = Card([g[k, i, j] for i in range(0, n) for j in range(i+1, n) for k in range(d)], s+1, prefix='g_') c_size += [~c[s]] else: c_size = [] c_sorts = sorts_backward_depth(g, n, 0, d, None, u) else: # Comparator variables g = variables(n, d) # Prefix constraints if lprefix is None: lprefix = [[(i, n-i-1) for i in range(n//2)]] sprefix = sum(len(layer) for layer in lprefix) c_prefix = [g[k, i, j] if (i, j) in layer else ~g[k, i, j] for k, layer in enumerate(lprefix) for i in range(n-1) for j in range(i+1, n)] net = Network(lprefix) d0 = len(net) # valid constraints c_valid = valid_depth(g, n, d0, d, s) # size constraints if s: ncard = s - sprefix gcard = [g[k, i, j] for i in range(n) for j in range(i+1, n) for k in range(d0, d)] c, c_size = Card(gcard, ncard + 1, prefix='g_') if len(c) > ncard: c_size += [~c[ncard]] else: c_size = [] inputs = net.not_sorted(n) inputs = [x for x in inputs if window_size(x, n) > 2 and window_size(x, n) <= wsize] xlist = net.outputs(n, inputs) print("d0:", d0, "len(xlist):", len(xlist), file=sys.stderr) c_sorts = sorts(g, n, d0, d, xlist) print("c_valid: ", len(c_valid), elapsed_time(), file=sys.stderr) print("c_prefix: ", len(c_prefix), elapsed_time(), file=sys.stderr) print("c_size: ", len(c_size), elapsed_time(), file=sys.stderr) print("c_sorts: ", len(c_sorts), elapsed_time(), file=sys.stderr) clauses = c_valid + c_prefix + c_sorts + c_size constraint = And(*clauses) print("Is cnf?", constraint.is_cnf(), "Size: ", len(clauses), elapsed_time(), file=sys.stderr) while True: if solver == "picosat": point = constraint.satisfy_one() res = point is not None else: litmap, cnf = expr2dimacscnf(constraint) task = task if task is not None else int(time.monotonic()) dimacscnf = "dimacscnf_{}.txt".format(task) litmapcnf = "litmap_{}.txt".format(task) with open(dimacscnf, "wt") as fp: print(cnf, file=fp) with open(litmapcnf, "wt") as fp: print(litmap, file=fp) ofile = "output_{}_{}_{}_{}.txt".format(n, d, s, task) with open(ofile+".log", "wt") as out: if solver == "minisat": subprocess.call(["minisat", dimacscnf, ofile], stdout=out) elif solver == "glucose": subprocess.call(["../../glucose-syrup-4.1/simp/glucose_release", "-model", dimacscnf], stdout=out) elif solver == "glucose-syrup": subprocess.call(["../../glucose-syrup-4.1/parallel/glucose-syrup_release", "-nthreads={}".format(nthreads), "-model", dimacscnf, ofile], stdout=out) else: subprocess.call(["../../cryptominisat-5.0.1/cryptominisat5", "-t", str(nthreads), dimacscnf], stdout=out) if solver == "minisat": res, soln = dimacs.read(ofile, minisat=True) else: res, soln = dimacs.read(ofile+".log") point = ConjNormalForm.soln2point(soln, litmap) print("len(point): ", len(point) if point else None, elapsed_time(), file=sys.stderr) if res is None: return 'UNDETERMINED' elif not res: return 'UNSATISFIABLE' else: if opt.startswith('h'): net = Halver(to_network(point, g, n, d)) return net elif opt.startswith('e'): net = Halver(to_ehalver(point, g, n, d)) return net else: net = to_network(point, g, n, d) xnew = net.not_sorted(n, log=False) if len(xnew) == u or opt not in ['d', 's']: return net else: m = 16 print("Unsorted:", len(xnew), file=sys.stderr) new_clauses = sorts(g, n, d0, d, xnew[:m]) clauses += new_clauses constraint = And(*clauses)