def is_satisfiable(e): formula = CNF() clauses, _, _ = _tseitin(e) for clause in clauses: formula.append(clause) with Lingeling(bootstrap_with=formula.clauses) as ling: return ling.solve()
def reduce_board(size: int, rows: [[int]], cols: [[int]]): """Reduces the the description of a Nonogram puzzle to a CNF formula :param size: The number of cells in each row/column :param rows: The hints for each row :param cols: The hints for each column :return: A CNF object representing the board's possible solutions """ clauses = [] base = size * size for i, row in enumerate(rows): uvars = [j + (i * size) + 1 for j in range(size)] reduction = reduce_set(size, row, uvars, base) clauses += reduction.clauses base += len(reduction.auxvars) for i, col in enumerate(cols): uvars = [i + (j * size) + 1 for j in range(size)] reduction = reduce_set(size, col, uvars, base) clauses += reduction.clauses base += len(reduction.auxvars) cnf = CNF() for clause in clauses: cnf.append(clause) return cnf
def sample_SR_aux(n, min_cls_len=1, p_binom=0.7, p_geo=0.4): """ Args: n: positive integer Returns: A randomly-generated formula and a clause which makes the formula unsat This procedure has no guarantees on the number of clauses in the formula. Reference implementation in source code of NeuroSAT: https://github.com/dselsam/neurosat/blob/master/python/gen_sr_dimacs.py """ result = CNF() with Solver(name="cdl") as S: while True: k = min_cls_len + np.random.binomial( n=1, p=p_binom) + np.random.geometric(p_geo) vs = np.random.choice(n, size=min(n, k), replace=False) vs = [ int(v + 1) if random.random() < 0.5 else int(-(v + 1)) for v in vs ] S.add_clause(vs) if S.solve(): result.append(vs) else: break return result, vs
def sample_SRC_aux(n, u1, c_idx, l_idx, p_geo=0.4, p_binom=0.7): """ Args: n: positive integer u1: an unsat core c_idx: a clause index l_idx: a literal index u1 must become sat if the literal at (c_idx, l_idx) is flipped. Note that if result, vs = sample_SR_aux(args...), then result + vs is a valid argument for u1 Returns: a random formula drawn from n variables containing u1 as an unsat core, and the unsat core """ result = CNF() u2 = u1.copy() u2.clauses[c_idx][l_idx] = -u2.clauses[c_idx][l_idx] with Solver(name="cdl") as S: while True: S.append_formula(u2) k = 1 + np.random.binomial(n=1, p=p_binom) + np.random.geometric(p_geo) vs = np.random.choice(n, size=min(n, k), replace=False) vs = [ int(v + 1) if random.random() < 0.5 else int(-(v + 1)) for v in vs ] S.add_clause(vs) if S.solve(): result.append(vs) else: break for cls in u1.clauses: result.append(cls) return result, u1 # TODO(jesse): output a core clause mask
def solve_sudoku_SAT(sudoku, k): ############# # this solution is adjusted from https://github.com/taufanardi/sudoku-sat-solver/blob/master/Sudoku.py # what I have done differently: # 1. Adjusted so that it can generate to k-sized problem, not just hardcoded k=3 in the original post # 2. Refactored the code to make it more readable and splitted into smaller functions instead of chunk of code # 3. Rewrited the `add_distinct_clauses` code to make it more robust and easy to understand ############# # make clauses clauses = create_clauses(sudoku, k) # append clauses to formula formula = CNF() for c in clauses: formula.append(c) # solve the SAT problem solver = MinisatGH() solver.append_formula(formula) answer = solver.solve() if not answer: return None # get the solution solution = solver.get_model() # reformat the solution into a suduko representation for i in range(1, k**2 + 1): for j in range(1, k**2 + 1): sudoku[i - 1][j - 1] = extract_digit_from_solution( i, j, solution, k) return sudoku
def resolver(passo: int, conjuntoVertices: bool = False, ordemVertices: bool = True): """Resolve o caminho eureliano do grafo. :param passo: Recebe um array contendo os primeiros passos de cada possível começo do problema :param conjuntoVertices: Exibir o uma lista fora de ordem com o conjunto dos vértices que resolvem o problema. (Default value = False) :param ordemVertices: Exibir o caminho pelos vértices a ser percorrido para resolver o problema. (Default value = True) """ formula = CNF() g = Glucose4() getClausulas(matriz, formula) formula.append([passo]) g.append_formula(formula) print( f'O passo inicial para prova é: \33[32m{passo}\33[m, o caminho com ele é \33[34m{g.solve()}\33[m' ) model = g.get_model() if model: valoracaoValida = valoresValidos(model) print(f'Passos válidos: \33[35m{valoracaoValida}\33[m') if conjuntoVertices: print( f'Esse são os conjuntos de vértices usados (fora de ordem): \33[36m{caminhosUsados(model)}\33[m' ) if ordemVertices: print(f"Ordem de passagem pelos vértices: ") printEmOrdem(valoracaoValida) print()
def unsat_core_example(): fmla = CNF() fmla.append([1, 2, 3]) fmla.append([-1, 2, 3]) fmla.append([2, -3]) fmla.append([-2, -3]) fmla.append([-2, 3]) return fmla
def validate_get_unsat_core_pysat(fmla, result, bad_asms): core = CNF(from_clauses=[fmla.clauses[i] for i in result]) for asm in bad_asms: core.append([asm]) with Solver(name="cdl") as S: S.append_formula(core) assert S.solve() is False print("ok")
def _mk_iter(self): for _ in range(self.num_subproblems): fmla = CNF() fmla.from_file(self.cnf_path) new_units = random.sample(list(range(1, fmla.nv + 1)), k=self.random_units) new_units = [[random.choice([1, -1]) * x] for x in new_units] for unit_clause in new_units: fmla.append(unit_clause) yield fmla
def TT_check_all_pysat(KB: 'ClauseTheory', a: 'AtomSet', symbols: 'AtomSet', model: 'AtomSet'): formula = CNF() for clause in KB: arr = [] arr.extend(clause.get_positive().get_atoms()) arr.extend(map(lambda x: -x, clause.get_negative().get_atoms())) formula.append(arr) assumptions = [] assumptions.extend(model) formula.append(list(map(lambda x: -x, a))) with Glucose4(bootstrap_with=formula.clauses) as g: return not g.solve(assumptions=assumptions)
def reduce_set(cells: int, blocks: [int], uvars: [int], nbase: int): """Produces a CNF-represented DNF which holds all the possible combinations for a row/col :param cells: The length of the row/col :param blocks: The hints for this row/col :param uvars: The literals to use for variables in this clause :param nbase: The base index for the auxiliary variables """ combos = [] if sum(blocks) + (len(blocks) - 1) > cells: raise Exception("The passed block values exceeded the number of cells") ogcombo = [] acc = 0 for block in blocks: ogcombo.append(acc) acc += block + 1 combos.append(ogcombo) ccombo = ogcombo.copy() lookat = len(blocks) - 1 while lookat >= 0: if blocks[-1] + ccombo[-1] < cells: ccombo[lookat] = ccombo[lookat] + 1 s = ccombo[lookat] + blocks[lookat] + 1 for i in range(lookat + 1, len(blocks)): ccombo[i] = s s += blocks[i] + 1 lookat = len(blocks) - 1 combos.append(ccombo.copy()) else: lookat -= 1 s = ccombo[lookat] + blocks[lookat] + 1 for i in range(lookat + 1, len(blocks)): ccombo[i] = s s += blocks[i] + 1 cnf = CNF() for combo in combos: clause = [ -v if in_combo(i, combo, blocks) else v for i, v in zip(range(cells), uvars) ] cnf.append(clause) return cnf.negate(nbase)
def validate_deserialized_TFDC(tfdc): def decode_l_idx(k): n_vars = tf.cast(tfdc.n_vars, dtype="int32") if k + 1 > n_vars: return -(k + 1 - n_vars) else: return k + 1 fmla = CNF_of_TFDC(tfdc) core = CNF() for i in range(len(tfdc.core_clause_mask)): if (tfdc.core_clause_mask[i]) == True: core.append(fmla.clauses[i]) core.to_fp(sys.stdout) print("") print("%%%%%%%% FORMULA %%%%%%%%") print("") fmla.to_fp(sys.stdout) with Solver(name="cdl") as S: S.append_formula(core) assert S.solve() is False with Solver(name="cdl") as S: S.append_formula(fmla) assert S.solve() is False # all variables in the core_var_mask are in the core for i in range(len(tfdc.core_var_mask)): if tfdc.core_var_mask[i] == True: var = i + 1 flag = False for cls in core.clauses: for lit in cls: if abs(lit) == var: flag = True assert flag # all variables in the core are in the core_var_mask for cls in core.clauses: for lit in cls: l_idx = abs(lit) - 1 assert tfdc.core_var_mask[l_idx] == True print("ok")
def consistencyCheck(AC, solver, difficulty): if solver == "Sat4j": f = tempfile.NamedTemporaryFile() cnf = CNF() for clause in AC: #AC es conjunto de conjuntos # print(clause[1]) cnf.append(clause[1]) #añadimos la constraint cnf.to_file(f.name) starttime = time.time() out = os.popen("java -jar org.sat4j.core.jar " + f.name).read() f.close() reqtime = time.time() - starttime time.sleep(reqtime * difficulty) if "UNSATISFIABLE" in out: # print("===> AC: " + str(AC) + " - False") return False else: # print("===> AC: " + str(AC) + " - True") return True elif solver == "Glucose3": g = Glucose3() for clause in AC: #AC es conjunto de conjuntos g.add_clause(clause[1]) #añadimos la constraint starttime = time.time() sol = g.solve() reqtime = time.time() - starttime time.sleep(reqtime * difficulty) return sol elif solver == "Choco4": f = tempfile.NamedTemporaryFile() cnf = CNF() for clause in AC: #AC es conjunto de conjuntos cnf.append(clause[1]) #añadimos la constraint cnf.to_file(f.name) starttime = time.time() out = os.popen("java -jar choco4solver.jar " + f.name).read() f.close() reqtime = time.time() - starttime time.sleep(reqtime * difficulty) if "UNSATISFIABLE" in out: return False else: return True else: raise ValueError("Solver not defined")
def reduce(x,sat_instance): sat_instance_reduced = CNF() # Creamos una instancia de la clase CNF donde se almacenaran las clausulas reducidas num_vars = sat_instance.nv # Obtenemos el numero de variables de la instancia a reducir new_clause_size = int(x) # Obtenemos el X del X-SAT que deseamos reducir for clause in sat_instance: clause_size = int(len(clause)) # Obtenemos el tamaño de la clausula en cada iteracion if new_clause_size == clause_size: # Si el tamaño de la clausula es igual al X-SAT que buscamos resumir entonces agregamos la misma clausula sin modificarla sat_instance_reduced.append(clause) else: if new_clause_size > clause_size: # Si el tamaño de X-SAT es mayor que el tamaño de la clausula actual, incrementamos sat_instance_reduced = increse_one_by_one(clause, num_vars, sat_instance_reduced, new_clause_size) # Reductor num_vars = sat_instance_reduced.nv elif new_clause_size < clause_size: # Si el tamaño de X-SAT es menor que el tamaño de la clausula actual, reducimos # Primero pasamos de SAT a 3-SAT num_vars = num_vars + 1 # Creamos la nueva variable positiva # Creamos la primera clausula new_clause = [] new_clause.append(clause[0]) new_clause.append(clause[1]) new_clause.append(num_vars) sat_instance_reduced.append(new_clause) # Creamos todas las clausulas intermedias for i in range(2, clause_size-2, 1): new_clause = [] neg_num_vars = num_vars * -1 # Creamos la nueva variable negativa num_vars = num_vars + 1 # Creamos la nueva variable positiva new_clause.append(neg_num_vars) new_clause.append(clause[i]) new_clause.append(num_vars) sat_instance_reduced.append(new_clause) # Creamos la ultima clausula neg_num_vars = num_vars * -1 # Creamos la nueva variable negativa new_clause = [] new_clause.append(neg_num_vars) new_clause.append(clause[-2]) new_clause.append(clause[-1]) sat_instance_reduced.append(new_clause) # Luego de tener todo en 3-SAT reducimos a X-SAT sat_instance_reduced = reduce(x, sat_instance_reduced) num_vars = sat_instance_reduced.nv return sat_instance_reduced
def validate_TFDC(tfdc): def decode_l_idx(k): n_vars = tf.cast(tfdc.n_vars, dtype="int32") if k + 1 > n_vars: return -(k + 1 - n_vars) else: return k + 1 fmla = CNF() for _ in range(tfdc.n_clauses): fmla.append([]) for edge in tfdc.CL_idxs: fmla.clauses[edge[0]].append(int(decode_l_idx(edge[1]))) core = CNF() for i in range(len(tfdc.core_clause_mask)): if tfdc.core_clause_mask[i] == 1: core.append(fmla.clauses[i]) with Solver(name="cdl") as S: S.append_formula(core) assert S.solve() is False with Solver(name="cdl") as S: S.append_formula(fmla) assert S.solve() is False # all variables in the core_var_mask are in the core for i in range(len(tfdc.core_var_mask)): if tfdc.core_var_mask[i] == 1: var = i + 1 flag = False for cls in core.clauses: for lit in cls: if abs(lit) == var: flag = True assert flag # all variables in the core are in the core_var_mask for cls in core.clauses: for lit in cls: l_idx = abs(lit) - 1 assert tfdc.core_var_mask[l_idx] == 1 print("ok")
def solve_sudoku_SAT(sudoku, k): num_vertices = k ** 4 vertices, edges = make_sudoku_graph(k) number_colors = k ** 2 formula = CNF() # Assign a positive integer for each propositional variable. def var_number(i, c): return ((i - 1) * number_colors) + c flatten_sudoku = np.array(sudoku).reshape(num_vertices) # Clause that ensures that each vertex there is one value. for i in range(1, num_vertices + 1): clause = [] sudoku_value = int(flatten_sudoku[i - 1]) not_assigned = sudoku_value == 0 if not_assigned: for c in range(1, number_colors + 1): clause.append(var_number(i, c)) else: clause.append(var_number(i, sudoku_value)) formula.append(clause) # Ensure that only one value is assigned. for i in range(1, num_vertices + 1): for c1 in range(1, number_colors + 1): for c2 in range(c1 + 1, number_colors + 1): clause = [-1 * var_number(i, c1), -1 * var_number(i, c2)] formula.append(clause) # Ensure that the rules of sudoku are kept, no adjacent vertices should have the same color/value. for (v1, v2) in edges: for c in range(1, number_colors + 1): clause = [-1 * var_number(v1, c), -1 * var_number(v2, c)] formula.append(clause) solver = MinisatGH() solver.append_formula(formula) answer = solver.solve() flatten_vertices = np.array(vertices).reshape(num_vertices) if answer: print("The sudoku is solved.") model = solver.get_model() print(model) for i in range(1, num_vertices + 1): for c in range(1, number_colors + 1): if var_number(i, c) in model: flatten_vertices[i - 1] = c return flatten_vertices.reshape(k**2, k**2).tolist() else: print("The sudoku has no solution.") return None
class PySATModel(VariabilityModel): @staticmethod def get_extension() -> str: return 'pysat' def __init__(self) -> None: #self.r_cnf = CNF() # ToDo: This should be avoid #self.ctc_cnf = CNF() # ToDo: This should be avoid self._cnf = CNF() self.variables: dict[str, int] = {} # feature's name -> id self.features: dict[int, str] = {} # id -> feature's name def add_clause(self, clause: list[int]) -> None: #self.ctc_cnf.append(clause) self._cnf.append(clause) def get_all_clauses(self) -> CNF: #clauses = CNF() #clauses.extend(self.r_cnf.clauses) #clauses.extend(self.ctc_cnf.clauses) #return clauses return self._cnf
def sat_to_dimacs_format(SATANSWERS): sat_answers = SATANSWERS nombre = "sat_dimacs_format_" answers_dimacs = [] sat_cnf = CNF() ACTUAL_DIRECTORY = getcwd( ) # Get the current directory path (../SAT/Reductor) PARENT_DIRECTORY = sep.join( ACTUAL_DIRECTORY.split(sep)[1:-1]) # Get the parent directory (../SAT) PARENT_DIRECTORY = join( sep, PARENT_DIRECTORY) # Apeend SO separator to access the folder X_SAT_directory = join(PARENT_DIRECTORY, "X-SAT") for index, answer in enumerate(SATANSWERS): num_archivo = str(index) nombre_res = nombre + num_archivo answer.pop(-1) for clause in answer: for index, variable in enumerate(clause): clause[index] = int(variable) sat_cnf.append(clause) sat_cnf.to_file(join(X_SAT_directory, nombre_res + ".cnf")) sat_cnf = CNF() return answers_dimacs
def getClausulas(matriz: list[list], formula: CNF, lin: int = 0, col: int = 0): for col in range(len(matriz[lin])): for lin in range(len(matriz)): for auxCol in range(col + 1, len(matriz[lin])): formula.append([-matriz[lin][col], -matriz[lin][auxCol]]) auxForm = [-matriz[lin][col]] for auxLin in range(len(matriz)): if auxLin != lin: formula.append([-matriz[lin][col], -matriz[auxLin][col]]) if arestas[lin][0] == arestas[auxLin][1] and arestas[lin][ 1] == arestas[auxLin][0]: negarLinha(matriz[lin][col], auxLin, col + 1, matriz, formula) if arestas[lin][1] == arestas[auxLin][0] and col < len( matriz[lin]) - 1: auxForm.append(matriz[auxLin][col + 1]) if len(auxForm) > 1: formula.append(auxForm)
def encode(self, label, nof_terms): """ Encode the problem of computing a DS of size nof_terms. """ self.nof_terms = nof_terms self.nof_samps = len(self.samps) enc = CNF() # constraint 2 for j in range(1, self.nof_terms + 1): for r in range(1, self.nof_feats + 1): enc.append([self.pvar(j, r), self.nvar(j, r)]) # constraint 3 if len(self.labels) == 1: # distinguish one class from all the others other_labels = set(self.samps.keys()) else: # distinguish the classes under question only other_labels = set(self.labels) other_labels.remove(label) other_labels = sorted(other_labels) for j in range(1, self.nof_terms + 1): for lb in other_labels: for q in self.samps[lb]: cl = [] shift = 0 for r in range(1, self.nof_feats + 1): if r - 1 in self.data.vmiss[q]: # this feature is missing in q'th sample cl.append(-self.pvar(j, r)) cl.append(-self.nvar(j, r)) shift += 1 else: cl.append(-self.slvar(j, r, q, shift)) enc.append(cl) # constraint 4 for j in range(1, self.nof_terms + 1): for q in self.samps[label]: shift = 0 for r in range(1, self.nof_feats + 1): if r - 1 in self.data.vmiss[q]: # this feature is missing in q'th sample enc.append([self.pvar(j, r), -self.crvar(j, q + 1)]) enc.append([self.nvar(j, r), -self.crvar(j, q + 1)]) shift += 1 else: enc.append([ self.slvar(j, r, q, shift), -self.crvar(j, q + 1) ]) # symmetry breaking constraints if self.options.bsymm: self.add_bsymm(enc) # constraint 5 if self.options.accuracy == 100.0: for q in self.samps[label]: enc.append([ self.crvar(j, q + 1) for j in range(1, self.nof_terms + 1) ]) else: for q in self.samps[label]: cv = self.cvvar(q + 1) enc.append([-cv] + [ self.crvar(j, q + 1) for j in range(1, self.nof_terms + 1) ]) for j in range(1, self.nof_terms + 1): enc.append([-self.crvar(j, q + 1), cv]) cnum = int(self.options.accuracy * len(self.samps[label]) / 100.0) al = CardEnc.atleast( [self.cvvar(q + 1) for q in self.samps[label]], bound=cnum, top_id=enc.nv, encoding=self.options.enc) if al: enc.extend(al.clauses) # at most one value can be chosen for a feature for feats in six.itervalues(self.ffmap.dir): if len(feats) > 2: for j in range(1, self.nof_terms + 1): lits = [-self.pvar(j, r + 1) for r in feats] # atmost1 can be true onev = CardEnc.atmost(lits, top_id=enc.nv, encoding=self.options.enc) enc.extend(onev.clauses) # saving comments for j in range(1, self.nof_terms + 1): for r in range(1, self.nof_feats + 1): enc.comments.append('c p({0}, {1}) => v{2}'.format( j, r, self.pvar(j, r))) enc.comments.append('c n({0}, {1}) => v{2}'.format( j, r, self.nvar(j, r))) for q in range(len(self.data.samps)): enc.comments.append('c cr({0}, {1}) => v{2}'.format( j, q + 1, self.crvar(j, q + 1))) for n, f in zip(self.data.names[:-1], self.data.feats[:-1]): for v in f: if self.data.fvmap.dir[(n, v)] > 0: enc.comments.append('c {0} = {1} => positive'.format(n, v)) else: enc.comments.append('c {0} = {1} => negative'.format(n, v)) return enc
from pysat.formula import CNF from pysat.solvers import Lingeling formula = CNF() formula.append([-1, 2]) formula.append([1, -2]) formula.append([-1, -2]) # formula.append([1, 2]) with Lingeling(bootstrap_with=formula.clauses, with_proof=True) as l: if l.solve() == False: print(l.get_proof())
class ColSAT: def __init__(self, g=nx.Graph(), ncolors=10): self.ncolors = ncolors self.g = g.copy() self.cmap = ColMap(g, ncolors) def check_coloring(self): for n1, n2 in self.g.edges(): if 'c' not in self.g.node[n1] or 'c' not in self.g.node[n2]: return False if self.g.node[n1]['c'] == self.g.node[n2]['c']: return False return True def apply_model(self): check = set() for var in self.model[self.model > 0]: node, color = self.cmap.dec(var) self.g.node[node]['c'] = color if (node, color) in check: raise Exception("Two colors for one node???") else: check.add((node, color)) self.colored = self.check_coloring() if self.colored != self.solved: raise Exception("Something went wrong!") return self.colored def build_cnf(self): self.formula = CNF() colors = list(range(1, self.ncolors + 1)) for n1, n2 in self.g.edges(): for c in colors: self.formula.append( [-self.cmap.enc(n1, c), -self.cmap.enc(n2, c)]) #specials = [28, 194, 242, 355, 387, 397, 468] #ii = 1 #for n in specials: # self.formula.append([self.cmap.enc(n, ii)]) # ii += 1 for n in self.g.nodes(): #if not n in specials: self.formula.append([self.cmap.enc(n, c) for c in colors]) for c1 in colors: for c2 in colors: if c1 < c2: self.formula.append( [-self.cmap.enc(n, c1), -self.cmap.enc(n, c2)]) return self.formula def solve_cnf(self, solver=''): triangle = find_triangle(self.g) assumptions = [] if len(triangle) > 0: assumptions = [ self.cmap.enc(triangle[0], 1), self.cmap.enc(triangle[1], 2), self.cmap.enc(triangle[2], 3) ] #Glucose3, Glucose4, Lingeling, MapleChrono, MapleCM, Maplesat, Minisat22, MinisatGH #with Glucose4(bootstrap_with=self.formula.clauses, with_proof=True) as ms: with Lingeling(bootstrap_with=self.formula.clauses) as ms: self.solved = ms.solve(assumptions=assumptions) if self.solved: self.model = np.array(ms.get_model()) self.apply_model() else: self.proof = [] #ms.get_proof() self.colored = False return self.solved
def solve_sudoku_SAT(sudoku, k): """ I used the rules described in here http://anytime.cs.umass.edu/aimath06/proceedings/P34.pdf """ k2 = k**2 N_propositionals = k2**3 propositions = list(range(1, N_propositionals + 1)) propositions = np.array(propositions).reshape((k2, k2, k2)) rules = CNF() ## These rules add the values we know to be true for i, row in enumerate(sudoku): for j, item in enumerate(row): props = list(propositions[i, j]) if item > 0: rules.append([int(props[item - 1])]) ## first rule each entry has a value, if it is set, that value must be True for i, row in enumerate(sudoku): for j, item in enumerate(row): rules.append([int(item) for item in propositions[i, j, :]]) ## second rule each row value appears once for y in range(k2): for z in range(k2): for x in range(k2 - 1): for i in range(x + 1, k2): props = [propositions[x, y, z], propositions[i, y, z]] rules.append([-int(item) for item in props]) ## third rule each column value appears once for x in range(k2): for z in range(k2): for y in range(k2 - 1): for i in range(y + 1, k2): props = [propositions[x, y, z], propositions[x, i, z]] rules.append([-int(item) for item in props]) ## fourth rule each 3x3 value appears once for z in range(k2): # for all values for i in range(k): # x block for j in range(k): # y block for x in range(k): # x place in block for y in range(k): # y place in block for l in range(y + 1, k): # for each props = [ propositions[k * i + x, k * j + y, z], propositions[k * i + x, k * j + l, z] ] rules.append([-int(item) for item in props]) for l in range(x + 1, k): for m in range(0, k): props = [ propositions[k * i + x, k * j + y, z], propositions[k * i + l, k * j + m, z] ] rules.append([-int(item) for item in props]) ## There is at most one number in each entry for y in range(k2): for x in range(k2): for z in range(k2 - 1): for i in range(z + 1, k2): props = [propositions[x, y, z], propositions[x, y, i]] rules.append([-int(item) for item in props]) ## each number appears at least once in row and column and 3x3 for y in range(k2): for z in range(k2): rules.append([int(item) for item in propositions[:, y, z]]) for x in range(k2): for z in range(k2): rules.append([int(item) for item in propositions[x, :, z]]) for i in range(k): # x block for j in range(k): # y block for x in range(k): # x place in block for y in range(k): # y place in block rules.append([ int(item) for item in propositions[k * i + x, k * j + y, :] ]) solver = MinisatGH() solver.append_formula(rules) answer = solver.solve() if answer == True: for i, lit in enumerate(solver.get_model()): if lit > 0: idx = lit - 1 sudoku[(idx // k2) // k2][(idx // k2) % k2] = (idx % k2) + 1 else: print("Did not find a model!") return sudoku
num_atoms = int(line.split()[-2]) elif line[0] == 'c': continue else: l = list(map(int, line.split()[:-1])) clauses.append(l) return clauses, num_atoms def dimacs_to_nnf( dimacs_path, nnf_path, c2d_path='./c2d_linux', ): import os r, output = subprocess.getstatusoutput(c2d_path + ' -in ' + dimacs_path) os.system('mv ' + dimacs_path + '.nnf' + ' ' + nnf_path) return output, r if __name__ == "__main__": print(num2triple(triple2num(6, 53, 88))) print(box_prop([336, 489, 324, 458], [94, 175, 306, 590])) f1 = CNF() f1.append([-1, 2]) print(f1.clauses) print(find(f1, 5))
def encode(self, label, nof_lits=1): """ Encode the problem of computing a DS of size nof_lits. """ self.nof_lits = nof_lits self.nof_samps = len(self.data.samps) self.nof_labls = len(self.labels) if len(self.labels) == 1: # distinguish one class from all the others other_labels = set(self.samps.keys()) else: # distinguish the classes under question only other_labels = set(self.labels) other_labels.remove(label) other_labels = sorted(other_labels) for j in range(1, self.nof_lits + 1): for r in range(1, self.nof_feats + 2): self.feat(j, r) for j in range(1, self.nof_lits + 1): self.sign(j) for j in range(1, self.nof_lits + 1): self.leaf(j) enc = CNF() # exactly one feature per node (including class/label) for j in range(1, self.nof_lits + 1): feats = [self.feat(j, r) for r in range(1, self.nof_feats + 2)] one = CardEnc.equals(lits=feats, vpool=self.idpool, encoding=self.options.enc) enc.extend(one) # at most one class/label per node for j in range(1, self.nof_lits + 1): labels = [self.label(j, z) for z in self.labels] am1 = CardEnc.atmost(lits=labels, vpool=self.idpool, encoding=self.options.enc) enc.extend(am1) # the model is split, # i.e. we currently target only rules for this concrete class enc.append([self.label(j, label)]) # leaf constraints enc.append([-self.leaf(1)]) # first node can't be a leaf enc.append([self.leaf(self.nof_lits)]) # last node should be a leaf # everything is reachable at node 1 for lb in self.labels: for i in self.samps[lb]: enc.append([self.reached(1, i + 1)]) # reachability propagation for j in range(1, self.nof_lits): for lb in self.labels: for i in self.samps[lb]: aij = self.agree(j, i + 1) cl, shift = [], 0 for r in range(1, self.nof_feats + 1): if r - 1 in self.data.vmiss[i]: # this feature is missing in i'th sample shift += 1 else: # a = self.agree(j, i + 1, r) # node j agrees with sample i on feature r f = self.feat(j, r) # feature r decided in node j s = self.sign(j) # polarity of node j if self.data.samps[i][r - 1 - shift] > 0: a = self.sets1(j, r) if a > enc.nv: enc.extend([[-a, f], [-a, s], [a, -f, -s]]) else: a = self.sets0(j, r) if a > enc.nv: enc.extend([[-a, f], [-a, -s], [a, -f, s]]) cl.append(a) enc.append([-aij] + cl) for l in cl: enc.append([aij, -l]) cur = self.reached(j, i + 1) # node j is reachable for sample i new = self.reached(j + 1, i + 1) # node j + 1 reachable for sample i enc.append([-new, self.leaf(j), cur]) enc.append([-new, self.leaf(j), aij]) enc.append([new, -self.leaf(j)]) enc.append([new, -cur, -aij]) # correctness of leafs for j in range(1, self.nof_lits + 1): for lb in self.labels: for i in self.samps[lb]: enc.append([ -self.leaf(j), -self.reached(j, i + 1), self.label(j, lb) ]) # coverage constraints for i in self.samps[label]: cl = [] for j in range(1, self.nof_lits + 1): cvar = self.covered(j, i + 1) enc.append([-cvar, self.leaf(j)]) enc.append([-cvar, self.reached(j, i + 1)]) enc.append([cvar, -self.leaf(j), -self.reached(j, i + 1)]) cl.append(cvar) enc.append(cl) # symmetry breaking if self.options.bsymm: self.add_bsymm(enc) for v, o in self.idpool.id2obj.items(): enc.comments.append('c {0} <=> {1}'.format(o, v)) return enc
def make_formula(n_police, n_medics, n_rows, n_cols, n_time): states = {'U', 'H', 'S', 'I', 'Q'} variables = {} formula = CNF() var_pool = IDPool() for t in range(n_time): for r in range(n_rows): for c in range(n_cols): for s in states: variables[(r, c), t, s] = var_pool.id(f'({r}, {c}), {t}, {s}') variables[(r, c), t, 'P'] = var_pool.id( f'({r}, {c}), {t}, P') # Police were used variables[(r, c), t, 'M'] = var_pool.id( f'({r}, {c}), {t}, M') # Medics were used variables[(r, c), t, 'SS'] = var_pool.id( f'({r}, {c}), {t}, SS') # Stayed sick from last time for t in range(n_time): formula.extend( CardEnc.atmost([ variables[(r, c), t, 'P'] for r in range(n_rows) for c in range(n_cols) ], bound=n_police, vpool=var_pool)) formula.extend( CardEnc.atmost([ variables[(r, c), t, 'M'] for r in range(n_rows) for c in range(n_cols) ], bound=n_medics, vpool=var_pool)) for r in range(n_rows): for c in range(n_cols): formula.extend( CardEnc.equals([variables[(r, c), t, s] for s in states], vpool=var_pool)) if t > 0: formula.extend( req_equiv([ -variables[(r, c), t - 1, 'Q'], variables[(r, c), t, 'Q'] ], [variables[(r, c), t, 'P']])) formula.extend( req_equiv([ -variables[(r, c), t - 1, 'I'], variables[(r, c), t, 'I'] ], [variables[(r, c), t, 'M']])) formula.extend( req_equiv([ variables[(r, c), t - 1, 'S'], variables[(r, c), t, 'S'] ], [variables[(r, c), t, 'SS']])) nearby_sick_condition = [] for r_, c_ in nearby(r, c, n_rows, n_cols): nearby_sick_condition.append(variables[(r_, c_), t, 'SS']) formula.extend( req_imply([ variables[(r, c), t, 'SS'], variables[(r_, c_), t - 1, 'H'] ], [ variables[(r_, c_), t, 'S'], variables[(r_, c_), t, 'I'] ])) # formula.extend(req_imply([variables[(r, c), t, 'SS']], [-variables[(r_, c_), t, 'H']])) formula.extend( req_imply([ variables[(r, c), t - 1, 'H'], variables[(r, c), t, 'S'] ], nearby_sick_condition)) if t + 1 < n_time: formula.extend( req_equiv([variables[(r, c), t, 'U']], [variables[(r, c), t + 1, 'U']])) formula.extend( req_imply([variables[(r, c), t, 'I']], [variables[(r, c), t + 1, 'I']])) formula.extend( req_imply([variables[(r, c), t + 1, 'S']], [ variables[(r, c), t, 'S'], variables[(r, c), t, 'H'] ])) formula.extend( req_imply([variables[(r, c), t + 1, 'Q']], [ variables[(r, c), t, 'Q'], variables[(r, c), t, 'S'] ])) if t == 0: formula.append([-variables[(r, c), t, 'Q']]) formula.append([-variables[(r, c), t, 'I']]) if t + 1 < n_time: formula.extend( req_imply([variables[(r, c), t, 'S']], [ variables[(r, c), t + 1, 'S'], variables[(r, c), t + 1, 'Q'] ])) formula.extend( req_imply([variables[(r, c), t, 'Q']], [variables[(r, c), t + 1, 'Q']])) if t + 2 < n_time: formula.extend( req_imply([ variables[(r, c), t, 'S'], variables[(r, c), t + 1, 'S'] ], [ variables[(r, c), t + 2, 'S'], variables[(r, c), t + 2, 'Q'] ])) formula.extend( req_imply([ variables[(r, c), t, 'S'], variables[(r, c), t + 1, 'Q'] ], [variables[(r, c), t + 2, 'Q']])) formula.extend( req_imply([variables[(r, c), t, 'Q']], [variables[(r, c), t + 2, 'H']])) if t + 3 < n_time: formula.extend( req_imply([ variables[(r, c), t, 'S'], variables[(r, c), t + 1, 'S'], variables[(r, c), t + 2, 'S'] ], [variables[(r, c), t + 3, 'H']])) if 0 < t and t + 1 < n_time: formula.extend( req_imply([ -variables[(r, c), t - 1, 'S'], variables[(r, c), t, 'S'] ], [ variables[(r, c), t + 1, 'S'], variables[(r, c), t + 1, 'Q'] ])) formula.extend( req_imply([ -variables[(r, c), t - 1, 'Q'], variables[(r, c), t, 'Q'] ], [variables[(r, c), t + 1, 'Q']])) if 0 < t and t + 2 < n_time: formula.extend( req_imply([ -variables[(r, c), t - 1, 'S'], variables[(r, c), t, 'S'], variables[(r, c), t + 1, 'S'] ], [ variables[(r, c), t + 2, 'S'], variables[(r, c), t + 2, 'Q'] ])) formula.extend( req_imply([ -variables[(r, c), t - 1, 'S'], variables[(r, c), t, 'S'], variables[(r, c), t + 1, 'Q'] ], [variables[(r, c), t + 2, 'Q']])) formula.extend( req_imply([ -variables[(r, c), t - 1, 'Q'], variables[(r, c), t, 'Q'] ], [variables[(r, c), t + 2, 'H']])) if 0 < t and t + 3 < n_time: formula.extend( req_imply([ -variables[(r, c), t - 1, 'S'], variables[(r, c), t, 'S'], variables[(r, c), t + 1, 'S'], variables[(r, c), t + 2, 'S'] ], [variables[(r, c), t + 3, 'H']])) return var_pool, formula
def sample_SRC_aux_test3(core_min=20, core_max=100, ratio_min=5, ratio_max=20, n1=10, n2=100): core_sizes = [] ratios = [] valid_count = 0 num_rounds = 200 with tempfile.TemporaryDirectory() as tmpdir: cnfdir = tmpdir + "/" cdl = cadical(cnf_dir=cnfdir) drat = drat_trim(cnf_dir=cnfdir) for _ in range(num_rounds): name = str(uuid.uuid4()) fmla_unsat, fmla_sat, c_idx, l_idx = sample_SR(n1, 2, p_binom=0.3) with open(os.path.join(cnfdir, name + ".cnf"), "w") as f: fmla_unsat.to_fp(f) cdl.set_rootname(name) cdl.run() # compute a proof of unsat for this formula cdl.process_output() assert cdl.result is False cdl.write_proof() drat.set_rootname(name) drat.run() # extract an unsat core by calling DRAT-trim tsr = parse_drat(drat.opt_path, fmla_unsat.nv) var_lemma_counts = lemma_occ(tsr) var_del_counts = del_occ(tsr) masks = parse_core(drat.core_path, fmla_unsat.nv, len(fmla_unsat.clauses)) core_clause_mask = masks["core_clause_mask"] u = CNF( ) # this unsat core should still satisfy the property that it becomes sat if the sign of a single literal is flipped for i in range(len(core_clause_mask)): if core_clause_mask[i] == 1: u.append(fmla_unsat.clauses[i]) l_idx = u.clauses[-1].index( fmla_unsat.clauses[c_idx][l_idx] ) # get new l_idx since DRAT sometimes permutes literals inside clauses fmla_src, u = sample_SRC_aux( n2, u, len(u.clauses) - 1, l_idx ) # use this unsat core as the seed to a call to sample_SR_aux, and obtain fmla_src core_size = len(u.clauses) ratio = float(len(fmla_src.clauses) / core_size) core_sizes.append(core_size) ratios.append(ratio) if core_size <= core_max and core_min <= core_size and ratio <= ratio_max and ratio_min <= ratio: valid_count += 1 print("max/min/mean core size:", np.max(core_sizes), np.min(core_sizes), np.mean(core_sizes)) print("max/min/mean ratios:", np.max(ratios), np.min(ratios), np.mean(ratios)) print("percent valid datapoints: ", str(float(valid_count / num_rounds) * 100) + "%")
def solve_sudoku_SAT(sudoku, k): ### note: this solution assumes that we don't go over 2 digits in the size/numbers (so works up to 9*9 units) from pysat.formula import CNF from pysat.solvers import MinisatGH ## constraint id is calculated as follows: concatenate row num, col num and value, padded two 2 digits solver = MinisatGH() formula = CNF() ## Approach: ## We have variables for each possible value in each cell (so rowNum * colNum * potential_values = k*k * k*k * k*k) ## for each unit (row, co, box) we add a rule that at least one should be true (a or b or c or ...) ## than for each pair in a unit, we add that the two cannot be true at the same time (not a or not b) ## adding: one position can't take two values for rowInd in range(k * k): for colInd in range(k * k): for valOne in range(k * k - 1): for valTwo in range(valOne + 1, k * k): formula.append([ -int( pad_str(rowInd + 1) + pad_str(colInd + 1) + pad_str(valOne + 1)), -int( pad_str(rowInd + 1) + pad_str(colInd + 1) + pad_str(valTwo + 1)) ]) ## adding: row rules for rowInd in range(k * k): for possible_value in range(k * k): ## adding that one should be true formula.append([ int( pad_str(rowInd + 1) + pad_str(colInd + 1) + pad_str(possible_value + 1)) for colInd in range(k * k) ]) ## adding that two cannot be true for colIndOne in range(k * k - 1): for colIndTwo in range(colIndOne + 1, k * k): formula.append([ -int( pad_str(rowInd + 1) + pad_str(colIndOne + 1) + pad_str(possible_value + 1)), -int( pad_str(rowInd + 1) + pad_str(colIndTwo + 1) + pad_str(possible_value + 1)) ]) ## adding: col rules for colInd in range(k * k): for possible_value in range(k * k): ## adding that one should be true formula.append([ int( pad_str(rowInd + 1) + pad_str(colInd + 1) + pad_str(possible_value + 1)) for rowInd in range(k * k) ]) ## adding that two cannot be true for rowIndOne in range(k * k - 1): for rowIndTwo in range(rowIndOne + 1, k * k): formula.append([ -int( pad_str(rowIndOne + 1) + pad_str(colInd + 1) + pad_str(possible_value + 1)), -int( pad_str(rowIndTwo + 1) + pad_str(colInd + 1) + pad_str(possible_value + 1)) ]) ## adding: box rules for rowStart in range(0, k * k, k): for colStart in range(0, k * k, k): ## looping inside box for possible_value in range(k * k): ## adding that one should be true box_ids = [] for rowInd in range(rowStart, rowStart + k): for colInd in range(colStart, colStart + k): box_ids.append( int( pad_str(rowInd + 1) + pad_str(colInd + 1) + pad_str(possible_value + 1))) formula.append(box_ids) ## adding that two cannot be true for rowIndOne in range(rowStart, rowStart + k): for colIndOne in range(colStart, colStart + k): for rowIndTwo in range(rowIndOne, rowStart + k): for colIndTwo in range(colIndOne, colStart + k): if not (rowIndOne == rowIndTwo and colIndOne == colIndTwo): formula.append([ -int( pad_str(rowIndOne + 1) + pad_str(colIndOne + 1) + pad_str(possible_value + 1)), -int( pad_str(rowIndTwo + 1) + pad_str(colIndTwo + 1) + pad_str(possible_value + 1)) ]) ## Adding the input values as literals for rowInd in range(k * k): for colInd in range(k * k): if sudoku[rowInd][colInd] != 0: formula.append([ int( pad_str(rowInd + 1) + pad_str(colInd + 1) + pad_str(sudoku[rowInd][colInd])) ]) ## calling the solver solver.append_formula(formula) answer = solver.solve() if not answer: return None else: ### reconstruct sudoku from solution for lit in solver.get_model(): if lit > 0: lit_split = [int(x) for x in str(lit)] ## appending leading zero if needed, since int conversion removes it if len(lit_split) < 6: lit_split = [0] + lit_split print(lit_split) sudoku[10 * lit_split[0] + lit_split[1] - 1][10 * lit_split[2] + lit_split[3] - 1] = 10 * lit_split[4] + lit_split[5] return sudoku
yield l[i * k:(i + 1) * k] V = list(group_by(N**2, list(group_by(N**2, V_flat)))) # Index of last solution variable (rest are added by cardinality encodings) max_problem_var = nv enc = EncType.ladder # Encode constraints for each column for i in range(N**2): for v in range(N**2): # Atleast-1 clause col = [V[i][j][v] for j in range(N**2)] F.append(col) # Atmost-1 encoding card = pysat.card.CardEnc.atmost(lits=col, top_id=nv, bound=1, encoding=enc) F.extend(card.clauses) nv = card.nv # Encode constraints for each row for j in range(N**2): for v in range(N**2): # Atleast-1 clause row = [V[i][j][v] for i in range(N**2)] F.append(row) # Atmost-1 encoding
def gen_src_aux(core_min=20, core_max=100, ratio_min=5, ratio_max=20, n1=10, n2=100, min_cls_len=2, cnfdir=None): """ Repeatedly samples formulas from SRC until it finds one which meets the specifications, then returns that formula. These parameters need to be tuned before running at scale to ensure that each call to this function rarely needs more than one iteration. Args: core_min: minimum size of the unsat core of the formula core_max: maximum size of the unsat core of the formula ratio_min: minimum ratio of formula-size to unsat-core-size ratio_max: maximum ratio of formula-size to unsat-core-size n1: `n` parameter passed to sample_SR when sampling the unsat core n2: `n` parameter passed to sample_SRC when sampling the larger formula containing the unsat core Returns: A formula from SRC obeying the constraints given by `core_min`, `core_max`, `ratio_min`, and `ratio_max`. """ # with tempfile.TemporaryDirectory() as cnfdir: cdl = cadical(cnf_dir=cnfdir) drat = drat_trim(cnf_dir=cnfdir) while True: name = str(uuid.uuid4()) fmla_unsat, fmla_sat, c_idx, l_idx = sample_SR(n1, min_cls_len, p_binom=0.3) with open(os.path.join(cnfdir, name + ".cnf"), "w") as f: fmla_unsat.to_fp(f) cdl.set_rootname(name) cdl.run() # compute a proof of unsat for this formula cdl.process_output() assert cdl.result is False cdl.write_proof() drat.set_rootname(name) drat.run() # extract an unsat core by calling DRAT-trim tsr = parse_drat(drat.opt_path, fmla_unsat.nv) var_lemma_counts = lemma_occ(tsr) var_del_counts = del_occ(tsr) masks = parse_core(drat.core_path, fmla_unsat.nv, len(fmla_unsat.clauses)) core_clause_mask = masks["core_clause_mask"] u = CNF( ) # this unsat core should still satisfy the property that it becomes sat if the sign of a single literal is flipped for i in range(len(core_clause_mask)): if core_clause_mask[i] == 1: u.append(fmla_unsat.clauses[i]) l_idx = u.clauses[-1].index( fmla_unsat.clauses[c_idx][l_idx] ) # get new l_idx since DRAT sometimes permutes literals inside clauses fmla_src, u = sample_SRC_aux( n2, u, len(u.clauses) - 1, l_idx ) # use this unsat core as the seed to a call to sample_SR_aux, and obtain fmla_src core_size = len(u.clauses) ratio = float(len(fmla_src.clauses) / core_size) # if fmla_src satisfies the constraints, return the TFDC if (ratio_min <= ratio and ratio <= ratio_max and core_min <= core_size and core_size <= core_max): break else: continue return fmla_src