class CDCL: def __init__(self, input_file, heuristic): if input_file is not None: clause_list = self.read_input(input_file) variable_set = set(map(abs, itertools.chain.from_iterable(clause_list))) self.model = Model(variable_set) self.sentence = Sentence(clause_list, self.model) self.root_stage = 0 self.heuristic = heuristic def read_input(self, input_file): _sentence = [] has_param = False intermediate = [] with open(input_file, 'r') as f: for line in f: if has_param: l = line.split() if len(l) == 0: continue if l[0] == '%': break parsed_line = list(map(int, l)) if (parsed_line[-1] == 0): intermediate.extend(parsed_line) _sentence.append(intermediate[:-1]) intermediate = [] else: intermediate.extend(parsed_line) elif line[:5] == 'p cnf': params = list(map(int, line[5:].split())) num_lit = params[0] num_clauses = params[1] has_param = True return _sentence def check(self, ans): vl = [] for cl in self.sentence.clauses: vl.append(cl.is_true()) return all(vl) def solve(self): stage = 0 pick_var = 0 self.conflicts = 0 self.propogations = 0 self.dec = 0 while (not self.model.all_variables_assigned()): #self.DEBUG() (is_conflict, conflict_clause) = self._unit_propogation(stage) if (not self.model.all_variables_assigned()): if (is_conflict): #print("conflicts") learnt_clauses, backtrack_stage = self._analyze_conflict( conflict_clause, stage) if (backtrack_stage == self.root_stage - 1): return 'UNSAT' reset_watched_lit_stack = self.model.backup( stage, backtrack_stage) self.sentence.add_learnt_clauses(learnt_clauses, self.model, reset_watched_lit_stack) self.model.update_VSIDS(learnt_clauses) stage = backtrack_stage self.conflicts += 1 else: if self._restart(pick_var): reset_watched_lit_stack = self.model.backup(stage, -1) self.sentence.reset_watched_lits( reset_watched_lit_stack) stage = 0 pick_var += 1 print('reset') else: #if(pick_var%200 == 0): #self.DEBUG() stage += 1 pick_var += 1 self.dec += 1 value, var = self.pick_branching_variable() self.model.update_variable(var, value, stage) self.sentence.update(var) return self.model.variables def stats(self): return { 'Decisions': self.dec, 'Propagaions': self.propogations, 'Conflicts': self.conflicts } def DEBUG(self): print(self.model) print(self.sentence) def _restart(self, counter): return (counter % 1000 == 0 and counter > 0) def pick_branching_variable(self): if self.heuristic == 'RAND': var = random.choice(self.model.unassigned_variables) sign = random.choice([True, False]) if self.heuristic == 'VSIDS': var = self.model.unassigned_variables[0] sign = True if var.VSIDS_score[True] > var.VSIDS_score[ False] else False return sign, var def _check_conflict(self, literal): conf_list = [ literal.conflictsWith(clause.get_unit()) for clause in self.sentence.all_clauses() if clause.is_unit() ] return any(conf_list) def _unit_propogation(self, stage): (clauses, is_UIP) = self.sentence.next_unit_batch() while clauses != None: clause = clauses.pop() if (not clause.is_unit()): if (len(clauses) == 0): (clauses, is_UIP) = self.sentence.next_unit_batch() else: literal = clause.get_unit() if (self._check_conflict(literal)): return (True, clause) self.model.update_propogated(literal.var, literal.sign, stage, clause, is_UIP) self.propogations += 1 self.sentence.update(literal.var) if (len(clauses) == 0): (clauses, is_UIP) = self.sentence.next_unit_batch() return (False, None) def _analyze_conflict(self, confl_clause, stage): def find_conflicting_clause_pairs(): conf_literal = confl_clause.get_unit() clauses = self.sentence.get_clauses_with(conf_literal.var) pos_lit = [] neg_lit = [] for clause in clauses: if (clause.is_unit()): if (clause.get_unit() is conf_literal): pos_lit.append(clause) elif (clause.get_unit().conflictsWith(conf_literal)): neg_lit.append(clause) return [item for item in itertools.product(pos_lit, neg_lit)] def reached_1_UIP(intermediate_lit_list, stage): return len( list(filter(lambda x: x.stage == stage, intermediate_lit_list))) <= 1 def resolved_clause(i_lit_list, stage): while not reached_1_UIP(i_lit_list, stage): curr_sec_vars = list( filter(lambda x: x.stage == stage, i_lit_list)) resolve_var = max(curr_sec_vars, key=attrgetter('imply_q')) if (resolve_var.antecedent != None): i_lit_list = list( set(resolve_var.antecedent.variables()).union( set(i_lit_list))) i_lit_list.remove(resolve_var) #print('+++', list(map(str, i_lit_list))) else: break return i_lit_list confl_clause_pairs = find_conflicting_clause_pairs() conf_var = confl_clause.get_unit().var learnt_clauses = [] for (clauseA, clauseB) in confl_clause_pairs: var_list = list( set(clauseA.variables()).union(clauseB.variables())) var_list.remove(conf_var) if (len(var_list) == 0): return ([], -1) roots = var_list to_remove = [] for var in var_list: if var != var.implied_by_roots[0]: if (set(var.implied_by_roots).issubset(set(roots))): to_remove.append(var) for var in to_remove: roots.remove(var) #print(list(map(str, roots))) learnt_clause = resolved_clause(roots, stage) max_stage = max([var.stage for var in learnt_clause]) new_vars = list(map(lambda x: -x, map(int, learnt_clause))) backtrack_stage = [] learnt_clauses = [] if (len(learnt_clause) == 1): learnt_clauses.append( Clause(new_vars, self.model, len(self.sentence.all_clauses()))) backtrack_stage.append(max_stage - 1) else: learnt_clause.sort(key=lambda x: x.stage, reverse=True) backtrack_stage.append(learnt_clause[1].stage) learnt_clauses.append( Clause(new_vars, self.model, len(self.sentence.all_clauses()))) bc_stage = min(backtrack_stage) return learnt_clauses, bc_stage