def ppz_parallel_encode(phi: CNF, x: Assignment, sig: Permutation, fill_first: bool = False) -> List[bool]: # enumerate all versions of phi with free coordinates of x filled in phi = phi.simplify() logger.info(f"ENCODING {x} with CNF {phi}") logger.info(f"{phi.clauses}") free_coords = [i + 1 for i, val in enumerate(x) if val == None] num_free = len(free_coords) phis = [deepcopy(phi) for _ in range(2**num_free)] _completions = completions(x) if fill_first: phis = [ phi.assign_on_variables(c, free_coords).simplify() for phi, c in zip(phis, _completions) ] enc = [] for _var in sig: var = _var + 1 logger.info(f"{var}") unit_clauses = get_literals_in_unit_clauses_from_lists_of_formulas( phis) logger.info(f"unit_clauses: {unit_clauses}") if var in unit_clauses: logger.info(f"variable {var} is in a unit clauses, assigning True") for i in range(len(phis)): phis[i].assign_single_variable( var, True, in_place=True).simplify_inplace() elif -var in unit_clauses: logger.info( f"variable {var} is in a unit clauses, assigning False") for i in range(len(phis)): phis[i].assign_single_variable( var, False, in_place=True).simplify_inplace() else: logger.info( f"variable does not appear in unit clause, assigning..") for i in range(len(phis)): logger.info(f"formula {phis[i]}") logger.info(f"assigning {var}: {_completions[i][var-1]}") if not fill_first or var not in free_coords: phis[i].assign_single_variable( var, _completions[i][var - 1], in_place=True).simplify_inplace() logger.info(f"updated formula: {phis[i]}") if var not in free_coords: logger.info(f"variable not free so appending to enc") enc.append(x[var - 1]) logger.info(f"enc: {enc}") return enc # type: ignore
def encode_3(phi: CNF, x: Assignment, pi: Permutation): enc = [] x = [x[i] for i in pi] phi = phi.rename_perm(pi) n = phi.n_vars for i in range(n): pass
def mod_then_parity(n: int, k: int, mod: int) -> CNF: phi = [] for i in range(n//k): if i == 0: phi.extend(mod_formula(k, mod).clauses) else: d = {a: a + i*k for a in range(1, k+1)} phi.extend(mod_formula(k, 2).rename(d).clauses) return CNF(phi)
def mod_formula(n: int, mod: int) -> CNF: not_satisfying = [x for x in bitstrings(n) if (sum(x) % mod) == 0] phi = [] for x in not_satisfying: clause = [] for _i, v in enumerate(x): i = _i +1 clause.append(-i if v == 1 else i) phi.append(clause) return CNF(phi)
def get_ith_cnf(index, n, k, m): fname = f"cnfs_list_n{n}_k{k}_m{m}.txt" if os.path.isfile(fname): string = read_index(fname, index) clauses = ast.literal_eval(string) return CNF(clauses) else: for i, phi in enumerate(all_cnfs(n, k, m)): if i == index: return phi
def mspas_with_same_free_bits(): # NOTE: This counterexample was kind of hard to find phi = CNF([[-1, 2, -3], [1, -2, 4], [-1, -3, 5], [2, -4, -5]]) mspas = get_prime_implicants(phi, 2) print(phi) print("MSPAS") pprint(mspas) free_coords = [get_free_coords(x) for x in mspas] relevant = [x for (x, i) in zip(mspas, free_coords) if i == (0, 4)] print() print("MSPAs with the same free bits:") print(relevant) print("Encodings:") for x in relevant: print(ppz_parallel_encode(phi, x, range(5))) draw_assignments_with_mspas(all_solutions(phi), relevant, phi, fname='mspas_with_same_free_bits.svg')
def counterexample_same_encoding_inconsistent(): n = 5 # phi = CNF([[-1, 4], [5], [-4, -2], [-1, 5]]) phi = CNF([[5, 2], [-5, 3, 4], [-2, 5, 1], [2, 1]]) mspas = get_prime_implicants(phi, 2) # mspa1 = [False, None, None, False, True] # Inconsistent b/c diverge in the 4th bit # mspa2 = [None, False, None, True, True] mspa1 = [True, True, None, None, False] mspa2 = [True, None, True, None, True] print(ppz_parallel_encode(phi, mspa1, list(range(n)))) print(ppz_parallel_encode(phi, mspa2, list(range(n)))) # extra = "\n MSPA1: 0**01, MSPA2: *0*11 both have encoding 0 but are inconsitent with one another" extra = "\n MSPA1: 11**0, MSPA2: 1*1*1 both have encoding 11 but are inconsistent with one another" draw_assignments( all_solutions(phi), phi, extra, fname="same_encoding_inconsistent.svg" )
def ppz_parallel_decode( phi: CNF, original_encoding, num_free_bits, sig: Permutation, fill_first: bool = False, ) -> Assignment: phi = phi.simplify() results = [] # consider all possible locations of free coordinates n = phi.n_vars all_possible_free_coords = itertools.combinations(range(1, n + 1), num_free_bits) for free_coords in all_possible_free_coords: decoding = ppz_decode_guessed_free_bit_locations( phi, original_encoding, free_coords, sig, fill_first) if decoding: results.append(decoding) return results
def ppz_once(phi: CNF) -> Optional[TotalAssignment]: n = phi.n_vars assignment: PartialAssignment = [None for _ in range(n)] for var in range(1, n + 1): if assignment[var - 1] is not None: # Already been assigned continue val = None for clause in phi.clauses: if len(clause) == 1 and lit_to_var(clause[0]) == var: # Forced val = clause[0] < 1 break if val is None: # Not forced val = random.choice([True, False]) assignment[var - 1] = val phi = phi.assign_single_variable(var, val).simplify() if [] in phi.clauses: return None return assignment # type: ignore
def satisfying_completions(phi: CNF, x: PartialAssignment) -> List[TotalAssignment]: return [c for c in completions(x) if phi.evaluate_on_assignment(c)]
def sensitivity(phi: CNF, x: TotalAssignment) -> int: n = phi.n_vars assert len(x) == n val = phi.evaluate_on_assignment(x) return sum(val != phi.evaluate_on_assignment(flip_assignment_at(x, i)) for i in range(1, n + 1))
def sensitive_at(phi: CNF, x: TotalAssignment, var: int): val = phi.evaluate_on_assignment(x) return val != phi.evaluate_on_assignment(flip_assignment_at(x, var))
def dist_monotone(n: int, k: int, m: int) -> CNF: clauses = [sample_monotone_clause(n, k) for _ in range(m)] return CNF(clauses)
def block_mod_formula(n: int, k: int, mod: int) -> CNF: phi = [] for i in range(n//k): d = {a: a + i*k for a in range(1, k+1)} phi.extend(mod_formula(k, mod).rename(d).clauses) return CNF(phi)