def compile_new_proof(self, head, new_proof): """ Compiles a new proof for head :param head: the head whose sdd should be updated :param new_proof: a formula representing a new proof for head. I.e., a formula such that lit2sdd(head) should be updated to lit2sdd(head) | compile(new_proof) :return True if lit2sdd[head] has changed; False otherwise """ if head not in self.lit2sdd: self.lit2sdd[head] = sddm.sdd_manager_false(self.manager) sdd_head_changed = True sdd_head_old = self.lit2sdd[head] new_sdd = compile_formula(new_proof, self.manager, self.get_literal_value) # new_sdd is an SDD represeting the new proof final_sdd = sdd_disjoin([self.lit2sdd[head], new_sdd], self.manager) # final_sdd is an SDD represeting the new value of lit2sdd[head] sddm.sdd_deref(new_sdd, self.manager) self.lit2sdd[head] = final_sdd if sdd_head_old == self.lit2sdd[head]: sdd_head_changed = False sddm.sdd_deref(sdd_head_old, self.manager) return sdd_head_changed
def get_entire_program_as_sdd(self): """ Returns an SDD representing the entire logic program. :return: an SDD representing the entire logic program """ if self._entire_program_as_sdd is not None: return self._entire_program_as_sdd equivalences = [] for atom, sdd in self.atom2sdd.items(): sdd_var_num = self.get_sdd_var_num(atom) sdd4atom = sddm.sdd_manager_literal(sdd_var_num, self.manager) sddm.sdd_ref(sdd4atom, self.manager) equiv = sdd_equiv(sdd4atom, sdd, self.manager) sddm.sdd_deref(sdd4atom, self.manager) equivalences.append(equiv) equivalences.append(self.constraint) sddm.sdd_ref(self.constraint, self.manager) equivalences = sort_sdds(equivalences) result = sdd_conjoin(equivalences, self.manager) for sdd in equivalences: sddm.sdd_ref(sdd, self.manager) self._entire_program_as_sdd = result return result
def set_value_to(self, lit, formula): """ Sets the value of lit in lit2sdd to the value obtained by compiling the formula :param lit: the literal whose value should change :param formula: the formula representing the new value of lit :return: """ orig_value = self.lit2sdd.get(lit, sddm.sdd_manager_false(self.manager)) new_value = compile_formula(formula, self.manager, self.get_literal_value) sddm.sdd_deref(orig_value, self.manager) self.lit2sdd[lit] = new_value
def addconstraint_without_evaluating(self, formula): """ Adds a constraint to this data structure. Conjoins self.constraint with the compilation of the formula :param formula: the formula to compile. Literals in this constraint are replaced by the sdd var number stored for them in self.atom2sdd if existing, and to a new sdd variable otherwise. :return: nothing """ new_constraint = compile_formula(formula, self.manager, self.get_literal_sdd_leaf) old_constraint = self.constraint self.constraint = sdd_conjoin([old_constraint, new_constraint], self.manager) sddm.sdd_deref(new_constraint, self.manager) sddm.sdd_deref(old_constraint, self.manager)
def addconstraint(self, formula): """ Adds a constraint to this data structure. Conjoins self.constraint with the compilation of the formula :param formula: the formula to compile. Literals in this constraint are replaced by their value according to self.get_literal_value :return: nothing """ new_constraint = compile_formula(formula, self.manager, self.get_literal_value) old_constraint = self.constraint self.constraint = sdd_conjoin([old_constraint, new_constraint], self.manager) sddm.sdd_deref(new_constraint, self.manager) sddm.sdd_deref(old_constraint, self.manager)
def collect_changed_lits_since_backup(self): """ Collects all literals whose values have changed since last backup. Also cleans the set of backupSDDs :return: the set of literals whose value has changed. """ changed = [] for lit, backup_sdd in self.backup_sdd.items(): new_sdd = self.lit2sdd[lit] if new_sdd != backup_sdd: changed.append(lit) sddm.sdd_deref(backup_sdd, self.manager) self.backup_sdd = OrderedDict() return changed
def test_garbage_collection(self): all_sdds = list(self.lit2sdd.values()) single_top = sdd_conjoin(all_sdds, self.manager) self.print_ref_counts() for sdd in all_sdds: sddm.sdd_deref(sdd, self.manager) sddsize = sddm.sdd_size(single_top) sdd_livesize = sddm.sdd_manager_live_size(self.manager) if sddsize == sdd_livesize: print('Your dereferencing was completely correct!') else: print('WARNING : Your dereferencing was NOT correct !!') return
def sdd_disjoin(sdd_list, manager): """ Disjoins all the input sdds to one large sdd :param sdd_list: a list of input sdds :param manager: the manager managing all of the input sdds :return: An SDD: the disjunction of the input sdds. This SDD has +1 reference count. """ import time alpha = sddm.sdd_manager_false(manager) for sdd in sdd_list: beta = sddm.sdd_disjoin(alpha, sdd, manager) sddm.sdd_ref(beta, manager) sddm.sdd_deref(alpha, manager) alpha = beta return alpha
def sdd_conjoin(sdd_list, manager): """ Conjoins all the input sdds to one large sdd :param sdd_list: a list of input sdds :param manager: the manager managing all of the input sdds :return: An SDD: the conjunction of the input sdds. This SDD has +1 reference count. """ #print("lenth of sdd_list = ",len(sdd_list)) ####try divide and conquer, auto_SDD minimization '''if sdd_list is None: print("sdd_list is None") exit(0) elif len(sdd_list) == 1: alpha = sddm.sdd_manager_true(manager) beta = sddm.sdd_conjoin(alpha,sdd_list[0],manager) sddm.sdd_ref(beta, manager) sddm.sdd_deref(alpha, manager) alpha = beta sddm.sdd_ref(sdd_list[0], manager) return sdd_list[0] else: mid = len(sdd_list)//2 left = sdd_conjoin(sdd_list[:mid], manager) right = sdd_conjoin(sdd_list[mid:], manager) bigger = sddm.sdd_conjoin(left, right, manager) sddm.sdd_deref(left, manager) sddm.sdd_deref(right, manager) sddm.sdd_ref(bigger, manager) return bigger''' alpha = sddm.sdd_manager_true(manager) for sdd in sdd_list: beta = sddm.sdd_conjoin(alpha, sdd, manager) sddm.sdd_ref(beta, manager) sddm.sdd_deref(alpha, manager) alpha = beta return alpha
def sdd_forall(variables, sdd, manager): """ Returns the formula \forall v_1, v_2, ..., v_n: sdd where v_i are the vars in vars :param variables: variables to quantify universally over :param sdd: the sdd to quantify :param manager: a manager managing both the sdd and vars :return: The resulting SDD. This SDD has +1 reference count """ result = sdd sddm.sdd_ref(result, manager) for var in variables: newresult = sddm.sdd_forall(var, result, manager) sddm.sdd_ref(newresult, manager) sddm.sdd_deref(result, manager) result = newresult return result
def get_literal_sdd_leaf(self, lit): """ Returns an SDD representing the literal value. In case of a negated literal, returns the negation of the sdd representing the underlying atom :param lit: the literal to represent :return: An sdd representing this literal. Increases the reference count of this sdd by one """ negated = lit.negated if negated: lit = lit.negate() num = self.get_sdd_var_num(lit) result = sddm.sdd_manager_literal(num, self.manager) sddm.sdd_ref(result, self.manager) if negated: newresult = sdd_negate(result, self.manager) sddm.sdd_deref(result, self.manager) result = newresult return result
def compile_formula(formula, manager, get_literal_value): """ Compiles the input formula for the given manager. :param formula: the logical formula to compile to an sdd :param manager: the manager managing all sdds :param get_literal_value: a function that returns for each literal an sdd, used to compile leafs of the formula this function is supposed to increase the reference count of the SDD it outputs by one! :return: An SDD represeting the formula. This SDD has +1 reference count. """ # Flyweights doen iets raar met types. type(formula) is ground.LIterals werkt bijvoorbeeld niet. # Daarom de lelijke hacks if formula is logic.TrueForm(): return sddm.sdd_manager_true(manager) elif formula is logic.FalseForm(): return sddm.sdd_manager_false(manager) elif isinstance(formula, type(logic.Literal("", True))): result = get_literal_value(formula) # Note: get_literal_value does the increment of the reference count return result elif type(formula) is logic.Disjunction: compiled_subforms = [] for subform in formula.subforms: compiled_subform = compile_formula(subform, manager, get_literal_value) compiled_subforms.append(compiled_subform) result = sdd_disjoin(compiled_subforms, manager) # Note: sdd_disjoin does the increment of the reference count for compiled_form in compiled_subforms: sddm.sdd_deref(compiled_form, manager) return result elif type(formula) is logic.Conjunction: compiled_subforms = [] for subform in formula.subforms: compiled_subform = compile_formula(subform, manager, get_literal_value) compiled_subforms.append(compiled_subform) result = sdd_conjoin(compiled_subforms, manager) # Note: sdd_conjoin does the increment of the reference count for compiled_form in compiled_subforms: sddm.sdd_deref(compiled_form, manager) return result else: print(type(formula)) raise NotImplementedError
def backup_neg_literals(self): """ Backups all SDDs associated to negative literals and replaces those SDDs by "True". This is used to prepare a logic program for unfounded set computation. NOTE: if some atom is already interpreted two-valued, its negative literal will not be updated. :return: the set of literals whose value has changed. """ changed = [] self.backup_sdd = OrderedDict() for lit, sdd in self.lit2sdd.items(): to_backup = lit.negated to_backup &= (sdd != sddm.sdd_manager_true(self.manager)) true_sdd = self.lit2sdd.get(lit.negate(), sddm.sdd_manager_false(self.manager)) neg_true_sdd = sdd_negate(true_sdd, self.manager) to_backup &= (sdd != neg_true_sdd) sddm.sdd_deref(neg_true_sdd, self.manager) if to_backup: self.backup_sdd[lit] = sdd changed.append(lit) self.lit2sdd[lit] = sddm.sdd_manager_true(self.manager) return changed
def sorted_apply(sdd_list, manager, apply_func): """ Performs an apply operation on a list of sdds. Does this sorted in the sense that two smaller sdds will be applied before applying to larger sdds :param sdd_list: the sdds :param manager: the manager :param apply_func: an apply function (conjoin or disjoin); this function must increase reference count of its result :return: An SDD: the result of the apply. This SDD has +1 reference count. """ for sdd in sdd_list: sddm.sdd_ref(sdd, manager) while len(sdd_list) > 2: sdd_list = sort_sdds(sdd_list) sdd1 = sdd_list[0] sdd2 = sdd_list[1] new = apply_func([sdd1, sdd2], manager) sdd_list.remove(sdd1) sddm.sdd_deref(sdd1, manager) sdd_list.remove(sdd2) sddm.sdd_deref(sdd2, manager) sdd_list.append(new) result = apply_func(sdd_list, manager) for sdd in sdd_list: sddm.sdd_deref(sdd, manager) return result
def finish_two_valued(self, program): """ Finishes the compilation :return: a TwoValuedCompiledProgram containing the result of the compilation """ atom2sdd = OrderedDict() for lit, sdd in self.lit2sdd.items(): if lit.negated: atom = lit.negate() atom_sdd = self.lit2sdd.get(atom) if atom_sdd is None: raise Exception("Compiled " + repr(lit) + " but never compiled value for " + repr(atom)) neg_atom_sdd = sdd_negate(atom_sdd, self.manager) if neg_atom_sdd != sdd: raise Exception("Trying to compile two-valued, but the result is three-valued for literal: " + str( lit) + "\n This probably means that there are errors in the input program (" "the well-founded model is not two-valued; check your program for loops over negation") sddm.sdd_deref(neg_atom_sdd, self.manager) else: atom2sdd[lit] = sdd return TwoValuedCompiledProgram(atom2sdd, self.param2sddNum, self.manager, program)
def sdd_equiv(sdd1, sdd2, manager): """ Returns the sdd: sdd1 <=> sdd2 :param sdd1: An input SDD :param sdd2: An input SDD :param manager: the manager managing the input sdds :return: An SDD: the sdd representing sdd1 <=> sdd2. This SDD has +1 reference count. """ """ sdd1n = sdd_negate(sdd1, manager) sddm.sdd_ref(sdd1n, manager) sdd2n = sdd_negate(sdd2, manager) sddm.sdd_ref(sdd2n, manager) sdde1 = sdd_conjoin([sdd1, sdd2], manager) sddm.sdd_ref(sdde1, manager) sdde2 = sdd_conjoin([sdd1n, sdd2n], manager) sddm.sdd_ref(sdde2, manager) sddequiv = sdd_disjoin([sdde1, sdde2], manager) sddm.sdd_ref(sddequiv, manager) """ sdd1n = sdd_negate(sdd1, manager) sddm.sdd_ref(sdd1n, manager) sdd2n = sdd_negate(sdd2, manager) sddm.sdd_ref(sdd2n, manager) sdde1 = sdd_disjoin([sdd1, sdd2n], manager) sddm.sdd_ref(sdde1, manager) sdde2 = sdd_disjoin([sdd1n, sdd2], manager) sddm.sdd_ref(sdde2, manager) sddequiv = sdd_conjoin([sdde1, sdde2], manager) sddm.sdd_ref(sddequiv, manager) sddm.sdd_deref(sdd1n, manager) sddm.sdd_deref(sdd2n, manager) sddm.sdd_deref(sdde1, manager) sddm.sdd_deref(sdde2, manager) return sddequiv