class _BaseSolver(object): """ define some basic function """ def __init__(self, pysat_name="m22", bootstrap_with=None): """ :param pysat_name :The name of SAT's solver """ self._pysat_sovlver = None self._compute_model_count = 0 self._pysat_name = pysat_name self._formula = CNF() self._cpu_time = 0.0 if bootstrap_with: self._formula.from_clauses(bootstrap_with) def print_status(self): used_mem = minimal_model.utils.get_used_memory() print("Memory used : %.2f MB" % used_mem) print("CPU time : %g s" % self.cpu_time) print("ComputeModelCount : %d " % self.compute_model_count) @property def compute_model_count(self) -> int: return self._compute_model_count @property def cpu_time(self) -> float: """ get the cpu time of solving minimal model """ return self._cpu_time @property def formula(self) -> CNF: """ get the original CNF formula """ return self._formula def add_clause(self, clause): """ add clause to formula """ self._formula.append(clause) def append_formula(self, formula): """ append formula to formula """ for clause in formula.clauses: self._formula.append(clause) def compute_minimal_model(self) -> Tuple[bool, list]: """ This method is used to a minimal model,it will return a `tuple`. The first value means whether a CNF formula given to the solver is satisfiability The second value is a minimal model only if the formula is satisfiability, otherwise the value is None\n :return: (satisfiability,minimal model) """ pass
def test_condition_one_clauses(self): for index, g in enumerate(self.games): cond = g.condition_one_clauses() self.assertLessEqual(len(cond), 8 * g.capacity) solver = Cadical(CNF(from_string=as_DIMACS_CNF(cond))) self.assertTrue(solver.solve())
def test_as_DIMACS_CNF(self): for index, g in enumerate(self.games): cond = g.get_cnf_solution() solver = Cadical(CNF(from_string=as_DIMACS_CNF(cond))) self.assertTrue(solver.solve())
def negarLinha(valor: int, lin: int, col: int, matriz: list[list], formula: CNF): for col in range(col, len(matriz[lin])): formula.append([-valor, -matriz[lin][col]])
def _encode(cls, lits, weights=None, bound=1, top_id=None, vpool=None, encoding=EncType.best, comparator='<'): """ This is the method that wraps the encoder of PyPBLib. Although the method can be invoked directly, a user is expected to call one of the following methods instead: :meth:`atmost`, :meth:`atleast`, or :meth:`equals`. The list of literals can contain either integers or pairs ``(l, w)``, where ``l`` is an integer literal and ``w`` is an integer weight. The latter can be done only if no ``weights`` are specified separately. :param lits: a list of literals in the sum. :param weights: a list of weights :param bound: the value of bound :math:`k`. :param top_id: top variable identifier used so far. :param vpool: variable pool for counting the number of variables. :param encoding: identifier of the encoding to use. :param comparator: identifier of the comparison operator :type lits: iterable(int) :type weights: iterable(int) :type bound: int :type top_id: integer or None :type vpool: :class:`.IDPool` :type encoding: integer :type comparator: str :rtype: :class:`pysat.formula.CNF` """ assert pblib_present, 'Package \'pypblib\' is unavailable. Check your installation.' if encoding < 0 or encoding > 5: raise(NoSuchEncodingError(encoding)) assert lits, 'No literals are provided.' assert not top_id or not vpool, \ 'Use either a top id or a pool of variables but not both.' # preparing weighted literals if weights: assert len(lits) == len(weights), 'Same number of literals and weights is expected.' wlits = [pblib.WeightedLit(l, w) for l, w in zip(lits, weights)] else: if all(map(lambda lw: (type(lw) in (list, tuple)) and len(lw) == 2, lits)): # literals are already weighted wlits = [pblib.WeightedLit(*wl) for wl in lits] lits = zip(*lits)[0] # unweighted literals for getting top_id elif all(map(lambda l: type(l) is int, lits)): # no weights are provided => all weights are units wlits = [pblib.WeightedLit(l, 1) for l in lits] else: assert 0, 'Incorrect literals given.' # obtaining the top id from the variable pool if vpool: top_id = vpool.top if not top_id: top_id = max(map(lambda x: abs(x), lits)) # pseudo-Boolean constraint and variable manager constr = pblib.PBConstraint(wlits, EncType._to_pbcmp[comparator], bound) varmgr = pblib.AuxVarManager(top_id + 1) # encoder configuration config = pblib.PBConfig() config.set_PB_Encoder(EncType._to_pbenc[encoding]) # encoding result = pblib.VectorClauseDatabase(config) pb2cnf = pblib.Pb2cnf(config) pb2cnf.encode(constr, result, varmgr) # extracting clauses ret = CNF(from_clauses=result.get_clauses()) # updating vpool if necessary if vpool: if vpool._occupied and vpool.top <= vpool._occupied[0][0] <= ret.nv: cls._update_vids(ret, vpool) else: vpool.top = ret.nv - 1 vpool._next() return ret
k=int(len(S)/2) S1=S[0:k] S2=S[k:len(S)] A1=diag(S2,S1,Diff(AC,S2)) A2=diag(A1,S2,Diff(AC,A1)) return A1 + A2 def Diff(li1, li2): li_dif = [i for i in li1 + li2 if i not in li1 or i not in li2] return li_dif #model=sys.argv[1] #requirements=sys.argv[2] #outFile=sys.argv[3] #model="./cnf/aircraft_fm.xml.cnf" #requirements="./cnf/aircraft_fm.xml.cnf_conf/1" requirements="../QX-Benchmark/cnf/betty/5000_30_0/16-50-4.prod" model="../QX-Benchmark/cnf/betty/5000_30_0.cnf" outFile="./out.txt" modelCNF = CNF(from_file=model) requirementsCNF = CNF(from_file=requirements) result= fmdiag(requirementsCNF.clauses, modelCNF.clauses + requirementsCNF.clauses) resCNF= CNF() for c in result: resCNF.append(c) resCNF.to_file(outFile) print(count)
def consistencyCheck(AC, solver, difficulty): if solver == "Sat4j": 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 org.sat4j.core.jar " + f.name).read() f.close() reqtime = time.time() - starttime time.sleep(reqtime * difficulty) if "UNSATISFIABLE" in out: return False else: 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")
class NaiveQBF(): """ This class work as a skeleton in order to define qbf solver based on SAT. """ def __init__(self): """ Constructor of the class. Always create an empty solver """ self.formula = CNF() self.quantifiers = [] self.propagate = [] def append_formula(self, formula): """ Append a formula (seen as a list of clauses) to the solver. """ for clause in formula: self.formula.append(clause) def propagate_literal(self, variable): """ Add a literal to be propagated. """ self.propagate.append(variable) if variable in self.quantifiers: self.quantifiers.remove(variable) def add_clause(self, clause): """ Add a clause to the formula """ self.formula.append(clause) def add_quantifiers(self, quantifier): """ Add a quantifier to the quantifiers list. Quantifiers are represented as integers. A positive quantifiers is interpreted as an exists, as well as a negative quantifiers is interpreted as a for all. """ if quantifier in self.quantifiers: raise ValueError('A variable can not be quantified twice') self.quantifiers.append(quantifier) def negate(self): """ negate the formula along with the quantifiers list. """ self.formula = self.formula.negate() self.quantifiers = [-quant for quant in self.quantifiers] def __solve(quantifiers, formula, propagate): """ Private method that implements the recursión that solve the problem. """ if quantifiers: new_quant = copy.deepcopy(quantifiers) quantifier = new_quant.pop() if quantifier < 0: return NaiveQBF.__solve( new_quant, formula, propagate + [quantifier]) and NaiveQBF.__solve( new_quant, formula, propagate + [-quantifier]) if NaiveQBF.__solve(new_quant, formula, propagate + [quantifier]): return True return NaiveQBF.__solve(new_quant, formula, propagate + [quantifier]) else: solver = Solver(name='cd') solver.append_formula(formula) print(formula.clauses) print(propagate) return solver.solve(assumptions=propagate) def solve(self): """ Solved the associated formula. """ return NaiveQBF.__solve(self.quantifiers, self.formula, self.propagate)
# very simple script that outputs #vars & #clauses for a given .cnf # useful to check if the instances correspond to their descriptions in a paper from pysat.formula import CNF import sys f = CNF(sys.argv[1]) print(f'{sys.argv[1]} has {f.nv} vars and {len(f.clauses)} clauses')
def test_simplify_CNF(): clauses = [[1, 2], [4, 5], [-1, 2, 3]] fmla = CNF(from_clauses=clauses) simplify_CNF(fmla, [1, 4]).to_fp(sys.stdout) fmla.to_fp(sys.stdout)
def solve_problem(problem): p_atoms, last = enumerate_states(problem, 1) a_atoms, last = enumerate_agent_actions(last, problem) spa_atoms, last = enumerate_spread_actions(problem, last) agea_atoms, last = enumerate_states(problem, last) noopa_atoms, last = enumerate_states(problem, last) observation_clauses = gen_observation_clauses(p_atoms, problem['observations']) action_preconditions_clauses = gen_precondition_clauses( p_atoms, a_atoms, spa_atoms, noopa_atoms, agea_atoms) fact_acheivement_clauses = gen_fact_acheivement_clauses( p_atoms, a_atoms, spa_atoms, noopa_atoms, agea_atoms) action_interefernce_clauses = gen_action_interefernce_clauses( a_atoms, spa_atoms, noopa_atoms, agea_atoms) must_spread_clauses = gen_must_spread_clauses(p_atoms, a_atoms, spa_atoms) must_age_clauses = gen_must_age_clauses(p_atoms, a_atoms, agea_atoms) S_representation_clauses = gen_S_representation_clauses(p_atoms) must_use_teams_clauses = gen_must_use_teams_clauses( p_atoms, a_atoms, problem['medics'], problem['police']) limited_teams_clauses = gen_limited_teams_clauses(p_atoms, a_atoms, problem['medics'], problem['police']) all_clauses = observation_clauses + action_preconditions_clauses + fact_acheivement_clauses + action_interefernce_clauses + must_spread_clauses + must_age_clauses + S_representation_clauses + must_use_teams_clauses + limited_teams_clauses answer = {} non_starter_list = ['I', 'Q'] for query in problem['queries']: row = query[0][0] col = query[0][1] step = query[1] status = query[2] if step == 0 and status in non_starter_list: answer[ query] = 'F' # a query about a state that can't be in step 0 returns 'F' instantly else: if status == 'S': query_clause = [ p_atoms['S3'][step][row][col], p_atoms['S2'][step][row][col], p_atoms['S1'][step][row][col] ] elif status == 'Q': if step == 0: # can't have Q in initial conditions query_clause = [False] else: query_clause = [ p_atoms['Q2'][step][row][col], p_atoms['Q1'][step][row][col] ] elif status == 'I': if step == 0: # can't have I in initial conditions query_clause = [False] else: query_clause = [p_atoms['I'][step][row][col]] else: query_clause = [p_atoms[status][step][row][col]] cnf_formula1 = CNF() sat_solver = Solver() cnf_formula1.clauses = all_clauses + [query_clause] #cnf_formula.clauses = all_clauses + [[21, 29, 37]] sat_solver.append_formula(cnf_formula1) res1 = sat_solver.solve() #res2 = sat_solver.get_model() sat_solver.delete() res1_other = False if res1 == True: # try to solve again- with any otehr possible status instead of status # If there is a solution- then it means there could be another status # It this case the result can;t be conclusive sat_solver = Solver() negative_query_clauses = [] for q in query_clause: negative_query_clauses.append([-q]) cnf_formula2 = CNF() cnf_formula2.clauses = all_clauses + negative_query_clauses sat_solver.append_formula(cnf_formula2) res1_other = sat_solver.solve() model = sat_solver.get_model() sat_solver.delete() if res1 and not res1_other: # The query status is possible and no other status is possible answer[query] = 'T' elif res1 and res1_other: # The query status is possible but also at least one more status can be answer[query] = '?' else: answer[query] = 'F' # The query status is not possible sat_solver.delete() return answer
def test_get_unsat_core(): with open("cnf/unsat_fmla.cnf", "r") as f: fmla = CNF(from_fp=f) result, bad_asms = get_unsat_core_pysat(fmla, None) validate_get_unsat_core_pysat(fmla, result, bad_asms)
def SATProblemToCNF(problem): return CNF(from_clauses=Clauses_to_clauses(problem.clauses()))
def __init__(self, name, lookahead_rewards=["march_cu"]): self.name = name self.lookahead_rewards = lookahead_rewards def cube(self, s# , assumptions ): lr = self.lookahead_rewards[0] status, lits = s.cube(# assumptions=assumptions, lookahead_reward=lr, lookahead_delta_fraction=1.0) if status != Z3Status.unknown: return None else: return lits[0].var() class Z3VarSelector: def __init__(self, name="bob"): self.name = name def __call__(self, fmla): problem = SATProblemFromCNF(fmla) S = Z3Solver(problem, opts(max_conflicts=0)) status, lits = S.cube(lookahead_reward="march_cu", lookahead_delta_fraction=1.0) if not status == Z3Status.unknown: return None else: return lits[0].var().idx() if __name__ == "__main__": test_1_dimacs = CNF(from_clauses=[[1,2,3,4],[-1,-2,-3],[5,6],[1,3,5],[-4,-2,-1],[-5],[4,-2,5]]) test_1_problem = SATProblemFromCNF(test_1_dimacs) s = Z3Solver(test_1_problem, opts(max_conflicts=100)) assert s.check() == Z3Status.sat
def encoding_Learn_SSAT(self, classifier, attributes, sensitive_attributes, auxiliary_variables, probs, filename, dependency_constraints=[], ignore_sensitive_attribute=None, verbose=True, find_maximization=True, negate_dependency_CNF=False): """ Maximization-minimization algorithm """ self.instance = "Learn" # TODO here we call twice: 1) get the sensitive feature with maximum favor, 2) get the sensitive feature with minimum favor # classifier is assumed to be a boolean formula. It is a 2D list # attributes, sensitive_attributes and auxiliary_variables is a list of variables # probs is the list of i.i.d. probabilities of attributes that are not sensitive self.num_variables = np.array( attributes + [abs(_var) for _group in sensitive_attributes for _var in _group] + auxiliary_variables).max() self.formula = "" # the sensitive attribute is exist quantified for group in sensitive_attributes: if (ignore_sensitive_attribute == None or group != ignore_sensitive_attribute): for var in group: self.formula += self._apply_exist_quantification(var) # random quantification over non-sensitive attributes if (len(attributes) > 0): for attribute in attributes: self.formula += self._apply_random_quantification( attribute, probs[attribute]) else: # dummy random variables self.formula += self._apply_random_quantification( self.num_variables, 0.5) self.num_variables += 1 if (ignore_sensitive_attribute != None): for var in ignore_sensitive_attribute: self.formula += self._apply_exist_quantification(var) # Negate the classifier if (not find_maximization): _classifier = CNF(from_clauses=classifier) _negated_classifier = _classifier.negate(topv=self.num_variables) classifier = _negated_classifier.clauses # print(auxiliary_variables, self.num_variables, _negated_classifier.auxvars) auxiliary_variables += [ i for i in range(self.num_variables + 1, _negated_classifier.nv + 1) ] # print(auxiliary_variables) self.num_variables = max(_negated_classifier.nv, self.num_variables) """ The following should not work as negation of dependency constraints does not make sense """ # # When dependency CNF is provided, we also need to negate it to learn the least favored group # if(negate_dependency_CNF and len(dependency_constraints) > 0): # """ # (not a) And b is provided where a = classifier and b = dependency constraints. # Additionally, (not a) is in CNF. # Our goal is to construct (not (a And b)) <-> (not a) Or (not b) <-> (x Or y) And (x -> not a) And (y -> not b). # In this encoding, x and y are two introduced variables. # """ # _dependency_CNF = CNF(from_clauses=dependency_constraints) # _negated_dependency_CNF = _dependency_CNF.negate(topv=self.num_variables) # # redefine # auxiliary_variables += [i for i in range(self.num_variables + 1, _negated_dependency_CNF.nv + 3)] # self.num_variables = max(_negated_dependency_CNF.nv, self.num_variables) # dependency_constraints = [clause + [-1 * (self.num_variables + 1)] for clause in _negated_dependency_CNF.clauses] # dependency_constraints += [[self.num_variables + 1, self.num_variables + 2]] # classifier = [clause + [-1 * (self.num_variables + 2)] for clause in classifier] # self.num_variables += 2 # two additional variables are introduced # for each sensitive-attribute (one hot vector), at least one group must be True self._formula_for_equal_one_constraints = "" for _group in sensitive_attributes: if (len(_group) > 1): # when categorical attribute is not Boolean equal_one_constraint = PBEnc.equals( lits=_group, weights=[1 for _ in _group], bound=1, top_id=self.num_variables) for clause in equal_one_constraint.clauses: self._formula_for_equal_one_constraints += self._construct_clause( clause) auxiliary_variables += [ i for i in range(self.num_variables + 1, equal_one_constraint.nv + 1) ] self.num_variables = max(equal_one_constraint.nv, self.num_variables) # other variables (auxiliary) are exist quantified for var in auxiliary_variables: self.formula += self._apply_exist_quantification(var) # clauses for the classifier for clause in classifier: self.formula += self._construct_clause(clause) # clauses for the dependency constraints for clause in dependency_constraints: self.formula += self._construct_clause(clause) self.formula += self._formula_for_equal_one_constraints # store in a file self.formula = self._construct_header() + self.formula[:-1] file = open(filename, "w") file.write(self.formula) file.close()
def solve_sudoku_SAT(sudoku: List[List[int]], k: int) -> Union[List[List[int]], None]: """Solve a k-by-k block Sudoku puzzle by encoding the problem as a propositional CNF formula and using a satisfiability (SAT) solver :param sudoku: A Sudoku puzzle represented as a list of lists. Values to be filled in are set to zero (0) :type sudoku: List[List[int]] :param k: Size of the Sudoku puzzle (a grid of k x k blocks) :type k: int :return: The solved sudoku puzzles as a list of lists or None if no solution is found. :rtype: Union[List[List[int]], None] """ # Record a (row, column, value) triple as a Variable ID in a PySAT ID Pool def triplet_to_id(row_index: int, column_index: int, val: int) -> int: # indexes need to start at 1 in the id pool return id_pool.id(tuple([row_index + 1, column_index + 1, val])) # Extract the (row, column, value) triple from the PySAT Variable ID def id_to_triplet(vid: int) -> Tuple[int, int, int]: row, column, val = id_pool.obj(vid) # convert back to indexes return row - 1, column - 1, val solved_sudoku = copy(sudoku) grid_size = k**2 cell_indices = list(get_cell_indices(k)) formula = CNF() id_pool = IDPool() # Build the CNF Formula using the definition of Kwon and Jain (2006) # (https://www.researchgate.net/publication/228566840_Optimized_CNF_encoding_for_sudoku_puzzles) # Extended Encoding = cell definedness AND cell uniqueness AND row definedness AND row uniqueness # AND column definedness AND column uniqueness AND block definedness # AND block uniqueness AND assigned cells # definedness: each (cell, row, column, block) has at least one number from 1 to grid_size+1 # uniqueness each (cell, row, column, block) has at most one number from 1 to grid_size+1 # Row/column/cell level # You can swap indices to declare uniqueness and definedness in a single loop for row_index, column_index in cell_indices: # Cell definedness formula.append([triplet_to_id(row_index, column_index, v) for v in range(grid_size)]) # Row definedness formula.append([triplet_to_id(row_index, v, column_index) for v in range(grid_size)]) # Column definedness formula.append([triplet_to_id(v, column_index, row_index) for v in range(grid_size)]) # Assigned cells val = sudoku[row_index][column_index] if val != 0: formula.append([triplet_to_id(row_index, column_index, val - 1)]) for val_index_i, val_index_j in itertools.combinations(range(grid_size), 2): # Cell uniqueness formula.append([ -triplet_to_id(row_index, column_index, val_index_i), -triplet_to_id(row_index, column_index, val_index_j)]) # Row uniqueness formula.append([ -triplet_to_id(row_index, val_index_i, column_index), -triplet_to_id(row_index, val_index_j, column_index)]) # Column uniqueness formula.append([ -triplet_to_id(val_index_i, row_index, column_index), -triplet_to_id(val_index_j, row_index, column_index)]) # Block level for val in range(grid_size): for block_indices in get_cell_indices_by_block(k): # Block definedness formula.append([triplet_to_id(r, c, val) for r, c in block_indices]) # Block uniqueness for block_index_tuple in itertools.combinations(block_indices, 2): (row_1, column_1), (row_2, column_2) = block_index_tuple formula.append([ -triplet_to_id(row_1, column_1, val), -triplet_to_id(row_2, column_2, val)]) # Solve solver = MinisatGH() solver.append_formula(formula) answer = solver.solve() if not answer: return None # Fill in solution model = solver.get_model() for vid in model: if vid > 0: row_index, column_index, val = id_to_triplet(vid) solved_sudoku[row_index][column_index] = val + 1 return solved_sudoku
# #============================================================================== if __name__ == '__main__': dcalls, to_enum, solver, verbose, files = parse_options() if type(to_enum) == str: to_enum = 0 if files: if files[0].endswith('cnf'): if files[0].endswith('.wcnf'): formula = WCNF(from_file=files[0]) else: # expecting '*.cnf' formula = CNF(from_file=files[0]).weighted() with LBX(formula, use_cld=dcalls, solver_name=solver, use_timer=True) as mcsls: for i, mcs in enumerate(mcsls.enumerate()): if verbose: print('c MCS:', ' '.join([str(cl_id) for cl_id in mcs]), '0') if verbose > 1: cost = sum([formula.wght[cl_id - 1] for cl_id in mcs]) print('c cost:', cost) if to_enum and i + 1 == to_enum: break mcsls.block(mcs)
adapt, blo, cmode, to_enum, exhaust, incr, minz, solver, trim, verbose, \ files = parse_options() if files: # parsing the input formula if files[0].endswith('.gz'): fp = gzip.open(files[0], 'rt') ftype = 'WCNF' if files[0].endswith('.wcnf.gz') else 'CNF' else: fp = open(files[0], 'r') ftype = 'WCNF' if files[0].endswith('.wcnf') else 'CNF' if ftype == 'WCNF': formula = WCNF(from_fp=fp) else: # expecting '*.cnf' formula = CNF(from_fp=fp).weighted() fp.close() # enabling the competition mode if cmode: assert cmode in ('a', 'b'), 'Wrong MSE18 mode chosen: {0}'.format(cmode) adapt, blo, exhaust, solver, verbose = True, True, True, 'g3', 3 if cmode == 'a': trim = 5 if max(formula.wght) > min(formula.wght) else 0 minz = False else: trim, minz = 0, True
def set_desired_formula(self, clauses, output_file): self.formula = CNF(from_clauses=clauses) self.output_file = output_file
def extract_datapoint(self, task, produce_derived=False, num_subproblems=0, num_units=0): try: dump_dir = tempfile.TemporaryDirectory(dir=task.tmpdir) except: dump_dir = tempfile.TemporaryDirectory() with dump_dir as dump_dir_name: TOO_BIG_FLAG = False cnf = CNF() try: cnf.from_file(task.cnf_path, compressed_with="use_ext") except: try: new_cnf_path = simplify_cnf_path(task.cnf_path, CLAUSE_LIMIT) cnf.from_file( new_cnf_path, compressed_with="use_ext" ) # circumvent pysat DIMACS parser complaining about invalid DIMACS files except: # shrug return 1, [] if len(cnf.clauses) == 0 or cnf.nv == 0: return 1, [] try: if len(cnf.clauses) > CLAUSE_LIMIT: print(f"PROBLEM WITH {len(cnf.clauses)} CLAUSES TOO BIG") status = -1 res = None TOO_BIG_FLAG = True else: res = gen_lbdp(tempfile.TemporaryDirectory(), cnf, dump_dir=(dump_dir_name + "/"), dumpfreq=self.dumpfreq, timeout=self.timeout, clause_limit=CLAUSE_LIMIT) glue_cutoff = 50 if np.sum(res.glue_counts) <= glue_cutoff: print( f"[WARNING] PROBLEM HAS FEWER THAN {glue_cutoff} GLUE COUNTS, DISCARDING" ) return 1, [] status = 0 except FileNotFoundError as e: print("[WARNING]: FILE NOT FOUND", e) print("TASK TYPE: ", task.task_type) print("TASK ORIGINAL CNF ", task.original_cnf_path) status = 1 except Exception as e: print("[WARNING]: EXITING GEN_LBDP DUE TO EXCEPTION", e) status = 1 if status == 1: print( f"FORMULA {task.cnf_path} SATISFIABLE OR ERROR RAISED, DISCARDING" ) return status, [] elif status == -1: # UNKNOWN i.e. timed out, so split the problem child_paths = [] print(f"SPLITTING CNF WITH {cnf.nv} AND {len(cnf.clauses)}") subproblem_random_units = 3 if TOO_BIG_FLAG: subproblem_random_units += math.ceil( math.log((len(cnf.clauses) - CLAUSE_LIMIT), 2)) num_tries = 0 while True: try: print("SPLITTING WITH RANDOM UNITS: ", subproblem_random_units) if num_tries > 30 or subproblem_random_units >= cnf.nv: print( "NUM TRIES OR RANDOM UNITS EXCEEDED LIMIT, STOPPING" ) break for subproblem in Subproblems( task.cnf_path, num_subproblems=num_subproblems, random_units=subproblem_random_units): print("ENTERING SUBPROBLEM LOOP") subproblem_path = os.path.join( task.tmpdir, (str(uuid.uuid4()) + ".cnf.gz")) subproblem.to_file(subproblem_path, compress_with="gzip") try: new_path = simplify_cnf_path( subproblem_path, CLAUSE_LIMIT) with open(new_path, "r") as f: header = f.readline().split()[2:] print("HEADER", header) n_clauses = int(header[1]) if n_clauses > CLAUSE_LIMIT: raise IndexError( f"CLAUSE LIMIT {CLAUSE_LIMIT} EXCEEDED BY N_CLAUSES {n_clauses}" ) if n_clauses == 0: continue child_paths.append(new_path) except IndexError as e: print("SIMPLIFY CNF FOUND NO SIMPLIFIED CNF") print(e) raise IndexError except Exception as e: print( "SIMPLIFY CNF PATH FAILED FOR SOME OTHER REASON" ) print(e) print("BAD PROBLEM: ", task.original_cnf_path) except IndexError: print("CAUGHT INDEXERROR, INCREMENTING RANDOM UNITS") num_tries += 1 subproblem_random_units += (num_tries**2) continue break print( f"SPLIT FORMULA INTO {len(child_paths)} SUBPROBLEMS: {child_paths}" ) return status, child_paths else: self.writer_handle.write.remote(res) child_paths = [] if produce_derived: dumps = recursively_get_files(dump_dir_name, forbidden=["bz2", "xz"], exts=["cnf", "gz"]) print("GOT DUMPS: ", dumps) for cnf_path in dumps: try: moved = shutil.move( cnf_path, os.path.join(task.tmpdir, str(uuid.uuid4()) + ".cnf")) print(f"APPENDING DUMPED CNF: {moved}") child_paths.append(moved) except: print("[WARNING] SOMETHING WENT TERRIBLY WRONG") if num_subproblems > 0 and task.task_type == 1: subproblem_random_units = 3 while True: try: if subproblem_random_units > 15: break for subproblem in Subproblems( task.cnf_path, num_subproblems=num_subproblems, random_units=subproblem_random_units): subproblem_path = os.path.join( task.tmpdir, (str(uuid.uuid4()) + ".cnf.gz")) subproblem.to_file(subproblem_path, compress_with="gzip") try: new_path = simplify_cnf_path( subproblem_path, CLAUSE_LIMIT) child_paths.append(new_path) except IndexError as e: print( "SIMPLIFY CNF FOUND NO SIMPLIFIED CNF") print(e) raise IndexError except Exception as e: print( "SIMPLIFY CNF PATH FAILED FOR SOME OTHER REASON" ) print(e) print("BAD PROBLEM: ", task.original_cnf_path) except IndexError: print( "CAUGHT INDEXERROR, INCREMENTING RANDOM UNITS") subproblem_random_units += 1 continue break print("RETURNING CHILD PATHS", child_paths) return status, child_paths
-5: "c2", -6: "c3", -7: "~c4", 1: "x1", 2: "x2", 3: "x3", -1: "~x1", -2: "~x2", -3: "~x3" } f = cost_puzzle(user_vars, I, weights) config_params = COusParams() ocus_expl_computer = OCUSExplain(C=CNF(from_clauses=all_constraints), params=config_params, matching_table=matching_table, verbose=True) ## Computing the Whole explanation sequence expl_sequence = ocus_expl_computer.explain(U=user_vars, f=f, I0=I) print("WHole sequence:\n", expl_sequence) ## Computing next best explanation given current interpretation expl_1_step = ocus_expl_computer.explain_1_step(U=user_vars, f=f, I0=I) print("Explain 1 step:\n", expl_1_step) ## Computing best explanation for given literal expl_1_lit = ocus_expl_computer.explain_1_lit(f=f, lit=2, I0=I) print("Explain 1 literal:\n", expl_1_lit)
def solve_sudoku_SAT(sudoku, k): """ Function that solves given sudoku list with SAT encoding. Edges correspond to undirected edge between cells in same row, column or k*k block. """ formula = CNF() num_vertices = (k * k) * (k * k) # 81 for k = 3 # Nd array representation of sudoku graph = np.array(sudoku).reshape(num_vertices, 1) # Create 2d np array of vertice numbers of 0 until num_vertices-1 for edges vertices = np.arange(num_vertices).reshape(k * k, k * k) edges = get_edges(vertices, k) vertices = vertices.reshape(num_vertices, 1) # Number of possible values in sudoku which we refer to as numbers (nums/n) num_nums = k * k # Return propositional variable for each vertex and num combination def var_v_n(v, n): return (v * num_nums) + n # Each cell contains a value between 1 and k*k for v in range(num_vertices): # Clause to ensure at least one num can be assigned to one vertex clause = [var_v_n(v, n) for n in range(1, num_nums + 1)] formula.append(clause) for n1 in range(1, num_nums + 1): for n2 in range(n1 + 1, num_nums + 1): # Clause ensures at most one num can be assigned to one vertex clause = [-1 * var_v_n(v, n1), -1 * var_v_n(v, n2)] formula.append(clause) # If a cell contains u in input, cell in solution must contain u if graph[v] != 0: cell = var_v_n(vertices[v].item(), graph[v].item()) formula.append([cell]) # Each two different cells in same row,col,block must contain diff values for (v1, v2) in edges: for n in range(1, num_nums + 1): # Num assigned to vertex 1 of edge should be true, OR num assigned # to other vertex of edge should be true clause = [-var_v_n(v1.item(), n), -var_v_n(v2.item(), n)] formula.append(clause) solver = MinisatGH() solver.append_formula(formula) answer = solver.solve() if answer: model = solver.get_model() # Fill in sudoku graph with answer for v in range(num_vertices): for n in range(1, num_nums + 1): if var_v_n(v, n) in model: graph[v] = n graph = graph.reshape(k * k, k * k).tolist() else: graph = None return graph
def cnf(c): """ Converts circuit to CNF using the Tseitin transformation Parameters ---------- c : Circuit circuit to transform Returns ------- variables : pysat.IDPool formula variable mapping formula : pysat.CNF CNF formula """ variables = IDPool() formula = CNF() for n in c.nodes(): variables.id(n) if c.type(n) == "and": for f in c.fanin(n): formula.append([-variables.id(n), variables.id(f)]) formula.append([variables.id(n)] + [-variables.id(f) for f in c.fanin(n)]) elif c.type(n) == "nand": for f in c.fanin(n): formula.append([variables.id(n), variables.id(f)]) formula.append([-variables.id(n)] + [-variables.id(f) for f in c.fanin(n)]) elif c.type(n) == "or": for f in c.fanin(n): formula.append([variables.id(n), -variables.id(f)]) formula.append([-variables.id(n)] + [variables.id(f) for f in c.fanin(n)]) elif c.type(n) == "nor": for f in c.fanin(n): formula.append([-variables.id(n), -variables.id(f)]) formula.append([variables.id(n)] + [variables.id(f) for f in c.fanin(n)]) elif c.type(n) == "not": if c.fanin(n): f = c.fanin(n).pop() formula.append([variables.id(n), variables.id(f)]) formula.append([-variables.id(n), -variables.id(f)]) elif c.type(n) == "buf": if c.fanin(n): f = c.fanin(n).pop() formula.append([variables.id(n), -variables.id(f)]) formula.append([-variables.id(n), variables.id(f)]) elif c.type(n) in ["xor", "xnor"]: # break into heirarchical xors nets = list(c.fanin(n)) # xor gen def xorClauses(a, b, c): formula.append( [-variables.id(c), -variables.id(b), -variables.id(a)]) formula.append( [-variables.id(c), variables.id(b), variables.id(a)]) formula.append( [variables.id(c), -variables.id(b), variables.id(a)]) formula.append( [variables.id(c), variables.id(b), -variables.id(a)]) while len(nets) > 2: # create new net new_net = "xor_" + nets[-2] + "_" + nets[-1] variables.id(new_net) # add sub xors xorClauses(nets[-2], nets[-1], new_net) # remove last 2 nets nets = nets[:-2] # insert before out nets.insert(0, new_net) # add final xor if c.type(n) == "xor": xorClauses(nets[-2], nets[-1], n) else: # invert xor variables.id(f"xor_inv_{n}") xorClauses(nets[-2], nets[-1], f"xor_inv_{n}") formula.append([variables.id(n), variables.id(f"xor_inv_{n}")]) formula.append( [-variables.id(n), -variables.id(f"xor_inv_{n}")]) elif c.type(n) == "0": formula.append([-variables.id(n)]) elif c.type(n) == "1": formula.append([variables.id(n)]) elif c.type(n) in ["ff", "lat", "input"]: formula.append([variables.id(n), -variables.id(n)]) else: print(f"unknown gate type: {c.type(n)}") code.interact(local=dict(globals(), **locals())) return formula, variables
count=0 genhash="" if len(sys.argv) > 1: model=sys.argv[1] requirements=sys.argv[2] lmax=int(sys.argv[3]) solver=sys.argv[4] difficulty=sys.argv[5] difficulty=int(sys.argv[5]) else: lmax=int(6) # requirements="./cnf/paperB/p1.prod" # model="./cnf/paperB/fm.cnf" solver="Sat4j" difficulty=int(0) requirements="./cnf/bench/prod-16-0.prod" model="./cnf/bench/model_16.cnf" model="./cnf/bench/model_16.cnf" modelCNF = CNF(from_file=model) requirementsCNF = CNF(from_file=requirements) #Vamos a almacenar el id con la C AC=sorted(enumerate(modelCNF.clauses,0), key=lambda x: x[0]) S=sorted(enumerate(requirementsCNF.clauses,len(modelCNF.clauses)), key=lambda x: x[0]) pool = mp.Pool(mp.cpu_count()) starttime = time.time() result= fastDiag(S,AC+S) reqtime = time.time() - starttime print(model+"|"+requirements+"|"+str(reqtime)+"|"+str(count+1)+"|"+str(len(cache))+"|"+str(lmax)+"|pFMDiag|"+solver+"|"+str(difficulty)+"|"+str(result)) pool.close() pool.terminate()
def create_clauses(epeg): class_to_var, var_to_class, \ class_pair_to_var, var_to_class_pair = create_vars_mappings(epeg) cnf = CNF(from_clauses=[]) top_id = max(class_pair_to_var.values()) # constraint (1) - at most one node from each class for class_name, class_nodes in epeg.classes.items(): vars = [class_to_var[class_name]] + list( set([node.id for node in class_nodes])) weights = [1] + [ -1 for _ in list(set([node.id for node in class_nodes])) ] constraint_formula = PBEnc.equals(lits=vars, weights=weights, bound=0, top_id=top_id) top_id = max([top_id] + [ abs(var) for clause in constraint_formula.clauses for var in clause ]) cnf.extend(constraint_formula.clauses) # constraint (2) - return class ret_class_var = class_to_var[epeg.root_class] return_constraint = PBEnc.equals(lits=[ret_class_var], weights=[1], bound=1, top_id=top_id) top_id = max( [top_id] + [abs(var) for clause in return_constraint.clauses for var in clause]) cnf.extend(return_constraint.clauses) # constraint (3) - taking node implies taking each of its children eq-classes for _id, node in epeg.nodes.items(): # node => child_eq_class ---> ~node \/ child_eq_class cnf.extend([[-_id, class_to_var[child.eq_class]] for child in node.children]) # constraint (4) - taking node implies taking pair of its class and each child class # (except for theta nodes' second child) for _id, node in epeg.nodes.items(): for i in range(len(node.children)): if not (isinstance(node, THETANode) and i == 1): node_child_pair_var = class_pair_to_var[( node.eq_class, node.children[i].eq_class)] clause = [-_id, node_child_pair_var] cnf.append(clause) # constraint (5) - if there is pair of same class, there must be at least one theta node from that class taken for class_name, nodes in epeg.classes.items(): pair_var = class_pair_to_var[(class_name, class_name)] clause = [-pair_var] + [ node.id for node in nodes if isinstance(node, THETANode) ] cnf.append(clause) # constraint (6) - transitivity for class_1 in epeg.classes.keys(): for class_2 in epeg.classes.keys(): for class_3 in epeg.classes.keys(): if len(set([class_1, class_2, class_3])) == 3: pair_1 = class_pair_to_var[(class_1, class_2)] pair_2 = class_pair_to_var[(class_2, class_3)] pair_3 = class_pair_to_var[(class_1, class_3)] clause = [-pair_1, -pair_2, pair_3] cnf.append(clause) return cnf, top_id
""" print('Usage:', os.path.basename(sys.argv[0]), '[options] dimacs-file') print('Options:') print(' -h, --help') print(' -s, --solver SAT solver to use') print( ' Available values: g3, lgl, m22, mc, mgh (default: m22)' ) print(' -v, --verbose Be verbose') # #============================================================================== if __name__ == '__main__': solver, verbose, files = parse_options() if files: formula = CNF(from_file=files[0]) with MUSX(formula, solver=solver, verbosity=verbose) as musx: mus = musx.compute() if mus: if verbose: print('c CNF size: {0}'.format(len(formula.clauses))) print('c MUS size: {0}'.format(len(mus))) print('v', ' '.join([str(clid) for clid in mus]), '0') print('c oracle time: {0:.4f}'.format(musx.oracle_time()))
def test_condition_three_clauses(self): for index, g in enumerate(self.games): cond = g.condition_three_clauses() solver = Cadical(CNF(from_string=as_DIMACS_CNF(cond))) self.assertTrue(solver.solve())
def solve_sudoku_SAT(sudoku, k): # give each possible value for cell (i, j) a (boolean) variable, resulting in in total k^6 variables variables = [[[v + k * k * (k * k * i + j) for v in range(1, k * k + 1)] for j in range(k * k)] for i in range(k * k)] formula = CNF() # iterate over rows and columns for i in range(k * k): # keep track of which variables belong to the row and column and to which values v row_vars = [[] for v in range(k * k)] col_vars = [[] for v in range(k * k)] for j in range(k * k): if sudoku[i][j] > 0: # values we are certain of are added as unit clause formula.append([variables[i][j][sudoku[i][j] - 1]]) # each cell must contain a value formula.append( variables[i][j] ) # this says: one of the variables belonging to (i,j) must be true for v1 in range(k * k): row_vars[v1].append( variables[i][j][v1] ) # save the variable belonging to the row for each value col_vars[v1].append( variables[j][i] [v1]) # same for the column: note the swapped i and j for v2 in range(v1 + 1, k * k): # prevent cells from having multiple values # in cell (i,j) any two variables v1, v2 (v1 != v2) cannot both be true formula.append( [-variables[i][j][v1], -variables[i][j][v2]]) for v in range(k * k): # each row and column must contain each possible value formula.append( row_vars[v] ) # for each possible value v, one corresponding variable must be true in row i formula.append(col_vars[v]) # same for the column for v1 in range(k * k): for v2 in range(v1 + 1, k * k): # and values in rows and columns cannot appear more than once # even though this is already implied, adding these lines speeds up the search process formula.append([-row_vars[v][v1], -row_vars[v][v2]]) formula.append([-col_vars[v][v1], -col_vars[v][v2]]) # iterate over blocks for b1 in range(k): # block vertical index for b2 in range(k): # block horizontal index # keep track of which variables belong to the block and to which values v block_vars = [[] for v in range(k * k)] for i in range(k * b1, k + k * b1): # row for j in range(k * b2, k + k * b2): # column for v in range(k * k): # value block_vars[v].append(variables[i][j][v]) for v in range(k * k): # the block must contain every possible value once formula.append(block_vars[v]) for v1 in range(k * k): for v2 in range(v1 + 1, k * k): # and two values in the block cannot be the same formula.append( [-block_vars[v][v1], -block_vars[v][v2]]) solver = MinisatGH() solver.append_formula(formula) answer = solver.solve() if answer: # if a solution is found # we convert the variables back to indices (i,j) and a value v solution = [[0 for j in range(k * k)] for i in range(k * k)] # empty k*k x k*k solution grid for l in solver.get_model(): # for each variable l in the model if l > 0: # variable l is true idx = l - 1 # this makes indexing easier i = int(idx / k**4) # row index j = int(idx / (k * k) % (k * k)) # column index v = idx % (k * k) + 1 # value solution[i][j] = v return solution return None # if no solution is found
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): ''' Function that solves a sudoku using a SAT solver. Creates the formula and feeds it to the MinisatGH solver. Returns: the filled in sudoku if it was solved, None otherwise. ''' kk = k * k formula = CNF() # functions to be used within this solver def var_number(i, j, n): ''' Determines a unique index for a sudoku cell based in its row, column and possible value. ''' return i * kk * kk + j * kk + n # create literals for values that are already filled in, these must always be satisfied for i in range(kk): for j in range(kk): if sudoku[i][j] != 0: clause = [var_number(i, j, sudoku[i][j])] formula.append(clause) # ensure all cells have at least one of the specified values for i in range(kk): for j in range(kk): clause = [] # iterate over all possible values, determine the unique index for n in range(1, kk + 1): clause.append(var_number(i, j, n)) # ensure at least one is satisfied formula.append(clause) # iterate over rows (needed for multiple checks) for i in range(kk): for j in range(kk): # ensure at most one value is picked for n1 in range(1, kk + 1): for n2 in range(n1 + 1, kk + 1): # only one of the two values can be true clause = [ -1 * var_number(i, j, n1), -1 * var_number(i, j, n2) ] formula.append(clause) # ensure proper row and column (no double values) for n in range(1, kk + 1): for j1 in range(kk): for j2 in range(j1 + 1, kk): # ensure no column contains same value twice clause = [ -1 * var_number(i, j1, n), -1 * var_number(i, j2, n) ] formula.append(clause) # ensure no row contains same value twice clause = [ -1 * var_number(j1, i, n), -1 * var_number(j2, i, n) ] formula.append(clause) # create list with indices of blocks indices = create_indices(k) # iterate over block indices for ind in indices: # iterate over possible values for n in range(1, kk + 1): # create every combination of block indices possible for i1 in range(len(ind)): for i2 in range(i1 + 1, len(ind)): # ensure no value appears more than once clause = [ -1 * var_number(ind[i1][0], ind[i1][1], n), -1 * var_number(ind[i2][0], ind[i2][1], n) ] formula.append(clause) # initialise the solver and give it the formula solver = MinisatGH() solver.append_formula(formula) # attempt to solve formula answer = solver.solve() # fill the sudoku when an answer has been found if answer: # get model from solver model = solver.get_model() for i in range(kk): for j in range(kk): for n in range(1, kk + 1): if var_number(i, j, n) in model: sudoku[i][j] = n return sudoku # return None when no solution was found return None