def convert_cnf_recur(self): r""" Convert boolean formula to conjunctive normal form. OUTPUT: An instance of :class:`BooleanFormula` in conjunctive normal form. EXAMPLES: This example hows how to convert a formula to conjunctive normal form:: sage: import sage.logic.propcalc as propcalc sage: s = propcalc.formula("a^b<->c") sage: s.convert_cnf_recur() sage: s (~a|a|c)&(~b|a|c)&(~a|b|c)&(~b|b|c)&(~c|a|b)&(~c|~a|~b) .. NOTE:: This function works by applying a set of rules that are guaranteed to convert the formula. Worst case the converted expression has an `O(2^n)` increase in size (and time as well), but if the formula is already in CNF (or close to) it is only `O(n)`. This function can require an exponential blow up in space from the original expression. This in turn can require large amounts of time. Unless a formula is already in (or close to) being in cnf :meth:`convert_cnf()` is typically preferred, but results can vary. """ self.__tree = logicparser.apply_func(self.__tree, self.reduce_op) self.__tree = logicparser.apply_func(self.__tree, self.dist_not) self.__tree = logicparser.apply_func(self.__tree, self.dist_ors) self.convert_expression()
def convert_cnf_recur(self): r""" This function converts an instance of boolformula to conjunctive normal form. It does this by applying a set of rules that are guaranteed to convert the formula. Worst case the converted expression has an O(2^n) increase in size (and time as well), but if the formula is already in CNF (or close to) it is only O(n). INPUT: self -- the calling object. OUTPUT: An instance of boolformula with an identical truth table that is in conjunctive normal form. EXAMPLES: sage: import sage.logic.propcalc as propcalc sage: s = propcalc.formula("a^b<->c") sage: s.convert_cnf_recur() sage: s (~a|a|c)&(~b|a|c)&(~a|b|c)&(~b|b|c)&(~c|a|b)&(~c|~a|~b) NOTES: This function can require an exponential blow up in space from the original expression. This in turn can require large amounts of time. Unless a formula is already in (or close to) being in cnf convert_cnf() is typically preferred, but results can vary. """ self.__tree = logicparser.apply_func(self.__tree, self.reduce_op) self.__tree = logicparser.apply_func(self.__tree, self.dist_not) self.__tree = logicparser.apply_func(self.__tree, self.dist_ors) self.convert_expression()
def dist_ors(self, tree): r""" This function can be applied to a parse tree to distribute or over and. INPUT: self -- the calling object. tree -- a list of three elements corresponding to a branch of a parse tree. OUTPUT: A tree branch that does not contain un-distributed ors. EXAMPLES: sage: import sage.logic.propcalc as propcalc, sage.logic.logicparser as logicparser sage: s = propcalc.formula("(a&b)|(a&c)") sage: tree = ['|', ['&', 'a', 'b'], ['&', 'a', 'c']] sage: logicparser.apply_func(tree, s.dist_ors) #long time ['&', ['&', ['|', 'a', 'a'], ['|', 'b', 'a']], ['&', ['|', 'a', 'c'], ['|', 'b', 'c']]] """ if(tree[0] == '|' and type(tree[2]) is ListType and tree[2][0] == '&'): new_tree = ['&', ['|', tree[1], tree[2][1]], ['|', tree[1], tree[2][2]]] return logicparser.apply_func(new_tree, self.dist_ors) if(tree[0] == '|' and type(tree[1]) is ListType and tree[1][0] == '&'): new_tree = ['&', ['|', tree[1][1], tree[2]], ['|', tree[1][2], tree[2]]] return logicparser.apply_func(new_tree, self.dist_ors) return tree
def dist_ors(self, tree): r""" This function can be applied to a parse tree to distribute or over and. INPUT: self -- the calling object. tree -- a list of three elements corresponding to a branch of a parse tree. OUTPUT: A tree branch that does not contain un-distributed ors. EXAMPLES: sage: import sage.logic.propcalc as propcalc, sage.logic.logicparser as logicparser sage: s = propcalc.formula("(a&b)|(a&c)") sage: tree = ['|', ['&', 'a', 'b'], ['&', 'a', 'c']] sage: logicparser.apply_func(tree, s.dist_ors) #long time ['&', ['&', ['|', 'a', 'a'], ['|', 'b', 'a']], ['&', ['|', 'a', 'c'], ['|', 'b', 'c']]] """ if (tree[0] == '|' and type(tree[2]) is ListType and tree[2][0] == '&'): new_tree = [ '&', ['|', tree[1], tree[2][1]], ['|', tree[1], tree[2][2]] ] return logicparser.apply_func(new_tree, self.dist_ors) if (tree[0] == '|' and type(tree[1]) is ListType and tree[1][0] == '&'): new_tree = [ '&', ['|', tree[1][1], tree[2]], ['|', tree[1][2], tree[2]] ] return logicparser.apply_func(new_tree, self.dist_ors) return tree
def eval_formula(tree, vdict): r""" Evaluates the tree using the boolean values contained in dictionary and returns a single boolean value. INPUT: - ``tree`` -- a list of three elements corresponding to a branch of a parse tree. - ``vdict`` -- a dictionary containing variable keys and boolean values. OUTPUT: - Returns the boolean evaluation of a boolean formula. EXAMPLES:: sage: import sage.logic.booleval as booleval sage: t = ['|', ['&', 'a', 'b'], ['&', 'a', 'c']] sage: d = {'a' : True, 'b' : False, 'c' : True} sage: booleval.eval_formula(t, d) True sage: d['a'] = False sage: booleval.eval_formula(t, d) False """ global __vars __vars = vdict b = logicparser.apply_func(tree, eval_f) return b
def dist_not(self, tree): r""" This function can be applied to a parse tree to distribute not operators over other operators. INPUT: self -- the calling object. tree -- a list of three elements corresponding to a branch of a parse tree. OUTPUT: A tree branch that does not contain un-distributed nots. EXAMPLES: sage: import sage.logic.propcalc as propcalc, sage.logic.logicparser as logicparser sage: s = propcalc.formula("~(a&b)") sage: tree = ['~', ['&', 'a', 'b'], None] sage: logicparser.apply_func(tree, s.dist_not) #long time ['|', ['~', 'a', None], ['~', 'b', None]] """ if (tree[0] == '~' and type(tree[1]) is ListType): op = tree[1][0] if (op != '~'): if (op == '&'): op = '|' else: op = '&' new_tree = [ op, ['~', tree[1][1], None], ['~', tree[1][2], None] ] return logicparser.apply_func(new_tree, self.dist_not) else: #cancel double negative return tree[1][1] else: return tree
def convert_expression(self): r""" Convert the string representation of a formula to conjunctive normal form. EXAMPLES:: sage: import sage.logic.propcalc as propcalc sage: s = propcalc.formula("a^b<->c") sage: s.convert_expression(); s a^b<->c """ ttree = self.__tree[:] ttree = logicparser.apply_func(ttree, self.to_infix) self.__expression = '' str_tree = str(ttree) open_flag = False i = 0 for c in str_tree: if i < len(str_tree) - 1: op = self.get_next_op(str_tree[i:]) if op == '|' and not open_flag: self.__expression += '(' open_flag = True if i < len(str_tree) - 2 and str_tree[i + 1] == '&' and open_flag: open_flag = False self.__expression += ')' if str_tree[i:i + 4] == 'None': i += 4 if i < len(str_tree) and str_tree[i] not in ' \',[]': self.__expression += str_tree[i] i += 1 if open_flag is True: self.__expression += ')'
def eval_formula(tree, vdict): r""" Evaluate the tree and return a boolean value. INPUT: - ``tree`` -- a list of three elements corresponding to a branch of a parse tree - ``vdict`` -- a dictionary containing variable keys and boolean values OUTPUT: The result of the evaluation as a boolean value. EXAMPLES: This example illustrates evaluating a boolean formula:: sage: import sage.logic.booleval as booleval sage: t = ['|', ['&', 'a', 'b'], ['&', 'a', 'c']] sage: d = {'a' : True, 'b' : False, 'c' : True} sage: booleval.eval_formula(t, d) True :: sage: d['a'] = False sage: booleval.eval_formula(t, d) False """ global __vars __vars = vdict b = logicparser.apply_func(tree, eval_f) return b
def dist_not(self, tree): r""" This function can be applied to a parse tree to distribute not operators over other operators. INPUT: self -- the calling object. tree -- a list of three elements corresponding to a branch of a parse tree. OUTPUT: A tree branch that does not contain un-distributed nots. EXAMPLES: sage: import sage.logic.propcalc as propcalc, sage.logic.logicparser as logicparser sage: s = propcalc.formula("~(a&b)") sage: tree = ['~', ['&', 'a', 'b'], None] sage: logicparser.apply_func(tree, s.dist_not) #long time ['|', ['~', 'a', None], ['~', 'b', None]] """ if(tree[0] == '~' and type(tree[1]) is ListType): op = tree[1][0] if(op != '~'): if(op == '&'): op = '|' else: op = '&' new_tree = [op, ['~', tree[1][1], None], ['~', tree[1][2], None]] return logicparser.apply_func(new_tree, self.dist_not) else: #cancel double negative return tree[1][1] else: return tree
def dist_ors(self, tree): r""" Distribute | over &. INPUT: - ``self`` -- calling object - ``tree`` -- a list. This represents a branch of a parse tree. OUTPUT: A new list EXAMPLES: This example illustrates the distribution of '|' over '&'. :: sage: import sage.logic.propcalc as propcalc, sage.logic.logicparser as logicparser sage: s = propcalc.formula("(a&b)|(a&c)") sage: tree = ['|', ['&', 'a', 'b'], ['&', 'a', 'c']] sage: logicparser.apply_func(tree, s.dist_ors) #long time ['&', ['&', ['|', 'a', 'a'], ['|', 'b', 'a']], ['&', ['|', 'a', 'c'], ['|', 'b', 'c']]] .. NOTE:: This function only operates on a single branch of a parse tree. To apply the function to an entire parse tree, pass the function as an argument to :func:`apply_func` in logicparser.py. """ if tree[0] == '|' and isinstance(tree[2], ListType) and tree[2][0] == '&': new_tree = [ '&', ['|', tree[1], tree[2][1]], ['|', tree[1], tree[2][2]] ] return logicparser.apply_func(new_tree, self.dist_ors) if tree[0] == '|' and isinstance(tree[1], ListType) and tree[1][0] == '&': new_tree = [ '&', ['|', tree[1][1], tree[2]], ['|', tree[1][2], tree[2]] ] return logicparser.apply_func(new_tree, self.dist_ors) return tree
def dist_ors(self, tree): r""" Distribute | over &. INPUT: - ``self`` -- calling object - ``tree`` -- a list. This represents a branch of a parse tree. OUTPUT: A new list EXAMPLES: This example illustrates the distribution of '|' over '&'. :: sage: import sage.logic.propcalc as propcalc, sage.logic.logicparser as logicparser sage: s = propcalc.formula("(a&b)|(a&c)") sage: tree = ['|', ['&', 'a', 'b'], ['&', 'a', 'c']] sage: logicparser.apply_func(tree, s.dist_ors) #long time ['&', ['&', ['|', 'a', 'a'], ['|', 'b', 'a']], ['&', ['|', 'a', 'c'], ['|', 'b', 'c']]] .. NOTE:: This function only operates on a single branch of a parse tree. To apply the function to an entire parse tree, pass the function as an argument to :func:`apply_func` in logicparser.py. """ if tree[0] == '|' and isinstance(tree[2], ListType) and tree[2][0] == '&': new_tree = ['&', ['|', tree[1], tree[2][1]], ['|', tree[1], tree[2][2]]] return logicparser.apply_func(new_tree, self.dist_ors) if tree[0] == '|' and isinstance(tree[1], ListType) and tree[1][0] == '&': new_tree = ['&', ['|', tree[1][1], tree[2]], ['|', tree[1][2], tree[2]]] return logicparser.apply_func(new_tree, self.dist_ors) return tree
def dist_not(self, tree): r""" Distribute ~ operators over & and | operators. INPUT: - ``self`` calling object - ``tree`` a list. This represents a branch of a parse tree. OUTPUT: A new list EXAMPLES: This example illustrates the distribution of '~' over '&'. :: sage: import sage.logic.propcalc as propcalc, sage.logic.logicparser as logicparser sage: s = propcalc.formula("~(a&b)") sage: tree = ['~', ['&', 'a', 'b'], None] sage: logicparser.apply_func(tree, s.dist_not) #long time ['|', ['~', 'a', None], ['~', 'b', None]] .. NOTE:: This function only operates on a single branch of a parse tree. To apply the function to an entire parse tree, pass the function as an argument to :func:`apply_func` in logicparser.py. """ if tree[0] == '~' and isinstance(tree[1], ListType): op = tree[1][0] if op != '~': if op == '&': op = '|' else: op = '&' new_tree = [ op, ['~', tree[1][1], None], ['~', tree[1][2], None] ] return logicparser.apply_func(new_tree, self.dist_not) else: # cancel double negative return tree[1][1] else: return tree
def dist_not(self, tree): r""" Distribute ~ operators over & and | operators. INPUT: - ``self`` calling object - ``tree`` a list. This represents a branch of a parse tree. OUTPUT: A new list EXAMPLES: This example illustrates the distribution of '~' over '&'. :: sage: import sage.logic.propcalc as propcalc, sage.logic.logicparser as logicparser sage: s = propcalc.formula("~(a&b)") sage: tree = ['~', ['&', 'a', 'b'], None] sage: logicparser.apply_func(tree, s.dist_not) #long time ['|', ['~', 'a', None], ['~', 'b', None]] .. NOTES:: This function only operates on a single branch of a parse tree. To apply the function to an entire parse tree, pass the function as an argument to :func:`apply_func` in logicparser.py. """ if tree[0] == '~' and type(tree[1]) is ListType: op = tree[1][0] if op != '~': if op == '&': op = '|' else: op = '&' new_tree = [op, ['~', tree[1][1], None], ['~', tree[1][2], None]] return logicparser.apply_func(new_tree, self.dist_not) else: # cancel double negative return tree[1][1] else: return tree
def convert_expression(self): r""" Convert the string representation of a formula to conjunctive normal form. INPUT: -- ``self`` -- calling object OUTPUT: None EXAMPLES: We show how the converted formula is printed in conjunctive normal form. :: sage: import sage.logic.propcalc as propcalc sage: s = propcalc.formula("a^b<->c") sage: s.convert_cnf_recur(); s #long time (~a|a|c)&(~b|a|c)&(~a|b|c)&(~b|b|c)&(~c|a|b)&(~c|~a|~b) """ ttree = self.__tree[:] ttree = logicparser.apply_func(ttree, self.to_infix) self.__expression = '' str_tree = str(ttree) open_flag = False put_flag = False i = 0 for c in str_tree: if i < len(str_tree) - 1: op = self.get_next_op(str_tree[i:]) if op == '|' and not open_flag: self.__expression += '(' open_flag = True if i < len(str_tree) - 2 and str_tree[i + 1] == '&' and open_flag: open_flag = False self.__expression += ')' if str_tree[i:i + 4] == 'None': i += 4 if i < len(str_tree) and str_tree[i] not in ' \',[]': self.__expression += str_tree[i] i += 1 if open_flag == True: self.__expression += ')'
def convert_expression(self): r""" This function converts the string expression associated with an instance of boolformula to match with its tree representation after being converted to conjunctive normal form. INPUT: self -- the calling object. OUTPUT: None. EXAMPLES: sage: import sage.logic.propcalc as propcalc sage: s = propcalc.formula("a^b<->c") sage: s.convert_cnf_recur(); s #long time (~a|a|c)&(~b|a|c)&(~a|b|c)&(~b|b|c)&(~c|a|b)&(~c|~a|~b) """ ttree = self.__tree[:] ttree = logicparser.apply_func(ttree, self.to_infix) self.__expression = '' str_tree = str(ttree) open_flag = False put_flag = False i = 0 for c in str_tree: if (i < len(str_tree) - 1): op = self.get_next_op(str_tree[i:]) if (op == '|' and not open_flag): self.__expression += '(' open_flag = True if (i < len(str_tree) - 2 and str_tree[i + 1] == '&' and open_flag): open_flag = False self.__expression += ')' if (str_tree[i:i + 4] == 'None'): i += 4 if (i < len(str_tree) and str_tree[i] not in ' \',[]'): self.__expression += str_tree[i] i += 1 if (open_flag == True): self.__expression += ')'
def convert_expression(self): r""" This function converts the string expression associated with an instance of boolformula to match with its tree representation after being converted to conjunctive normal form. INPUT: self -- the calling object. OUTPUT: None. EXAMPLES: sage: import sage.logic.propcalc as propcalc sage: s = propcalc.formula("a^b<->c") sage: s.convert_cnf_recur(); s #long time (~a|a|c)&(~b|a|c)&(~a|b|c)&(~b|b|c)&(~c|a|b)&(~c|~a|~b) """ ttree = self.__tree[:] ttree = logicparser.apply_func(ttree, self.to_infix) self.__expression = '' str_tree = str(ttree) open_flag = False put_flag = False i = 0 for c in str_tree: if(i < len(str_tree) - 1): op = self.get_next_op(str_tree[i:]) if(op == '|' and not open_flag): self.__expression += '(' open_flag = True if(i < len(str_tree) - 2 and str_tree[i + 1] == '&' and open_flag): open_flag = False self.__expression += ')' if(str_tree[i:i + 4] == 'None'): i += 4 if(i < len(str_tree) and str_tree[i] not in ' \',[]'): self.__expression += str_tree[i] i += 1 if(open_flag == True): self.__expression += ')'