def simplify(expr): """ Tries to simplify expressions very niavely. """ expr = propagate(expr, (None, True)) if type(expr) == bool: return PBB.mk_const_expr(expr) return expr
def propagate(expr, tup): """ Propagae the assignment TUP (string variable name, boolean value) through expression EXPR """ expr = PBA.propagate(expr, tup) if type(expr) == bool: return PBB.mk_const_expr(expr) return expr
def _parse_ite(expr): """ This method takes in any expression EXPR and parses it into a F, G, H expressions such that expr is the same as ite(F,G,H). This is possible because every boolean function can be represented in ite form. See Bryant's paper for formulas. """ if expr["type"] == "const": v = expr["value"] return pbb.mk_const_expr(v), pbb.mk_const_expr(v), pbb.mk_const_expr(v) if expr["type"] == "var": return expr, pbb.mk_const_expr(True), pbb.mk_const_expr(False) if expr["type"] == "neg": return expr["expr"], pbb.mk_const_expr(False), pbb.mk_const_expr(True) if expr["type"] == "and": return expr["expr1"], expr["expr2"], pbb.mk_const_expr(False) if expr["type"] == "or": return expr["expr1"], pbb.mk_const_expr(True), expr["expr2"] if expr["type"] == "impl": return expr["expr1"], expr["expr2"], pbb.mk_const_expr(True) if expr["type"] == "xor": return expr["expr1"], pbb.mk_neg_expr(expr["expr2"]), expr["expr2"] if expr["type"] == "eqv": return expr["expr1"], expr["expr2"], pbb.mk_neg_expr(expr["expr2"])
#Good example to demo PyBool. Step through with pdb, and print #variables at each stage. For the recursive representations (expr and expr2) #use Bool.print_expr(expr) for pretty printing. Uses very simple DIMACS #file example_dimacs_files/lecture.cnf if __name__ == "__main__": #Read and parse a dimacs file clauses = Bool.parse_dimacs("example_dimacs_files/lecture.cnf") clauses = clauses["clauses"] #convert dimacs form to recursive form expr = Bool.cnf_to_rec(clauses) #make a new formula that is the negation of previous expr = BoolB.mk_neg_expr(expr) expr2 = copy.deepcopy(expr) #Put in negation normal form expr = Bool.nne(expr) #now make a possibly exp. sized cnf expr = Bool.exp_cnf(expr) #with this expression we make a worst case #polynomial sized cnf expr2 = Bool.poly_cnf(expr2) #Now put the recursive formula back to list form clauses = Bool.cnf_list(expr2)
def _parse_ite(expr): """ This method takes in any expression EXPR and parses it into a F, G, H expressions such that expr is the same as ite(F,G,H). This is possible because every boolean function can be represented in ite form. See Bryant's paper for formulas. """ if expr["type"] == "const": v = expr["value"] return pbb.mk_const_expr(v),pbb.mk_const_expr(v),pbb.mk_const_expr(v) if expr["type"] == "var": return expr, pbb.mk_const_expr(True), pbb.mk_const_expr(False) if expr["type"] == "neg": return expr["expr"], pbb.mk_const_expr(False), pbb.mk_const_expr(True) if expr["type"] == "and": return expr["expr1"], expr["expr2"], pbb.mk_const_expr(False) if expr["type"] == "or": return expr["expr1"], pbb.mk_const_expr(True), expr["expr2"] if expr["type"] == "impl": return expr["expr1"], expr["expr2"], pbb.mk_const_expr(True) if expr["type"] == "xor": return expr["expr1"], pbb.mk_neg_expr(expr["expr2"]), expr["expr2"] if expr["type"] == "eqv": return expr["expr1"], expr["expr2"], pbb.mk_neg_expr(expr["expr2"])
def _ite(bdd,F,G,H): """ Main recursive method. Follows Bryants paper with a few added heuristics which are noted. """ #################### #Possible Base Cases #################### #If G and H and constants: # #Base case if F is a variable or constant #Else Parse F into FGH form. #Switching the _ite_mk args, because they seem to be backwards! while (_is_const(G,True) and _is_const(H,False)) or (_is_const(H,True) and _is_const(G,False)) and (F["type"] == "neg" or F["type"] == "var"): if _is_const(G,True) and _is_const(H,False): if F["type"] == "var": return _ite_mk(bdd, F["name"][0] ,0,1) elif F["type"] == "const": return 1 if F["value"] else 0 else: F,G,H = _parse_ite(F) elif _is_const(H,True) and _is_const(G,False): if F["type"] == "var": return _ite_mk(bdd, F["name"][0] ,1,0) elif F["type"] == "const": return 0 if F["value"] else 1 else: #Fix applied Feb. 16, 2013 by Tyler Sorensen F,G,H = _parse_ite(F) H = pbb.mk_neg_expr(H) G = pbb.mk_neg_expr(G) #################### #if H and G are equal and constant #Just return what H and G are (my heuristic) if _is_const(G, False) and _is_const(H, False): return 0 elif _is_const(G,True) and _is_const(H,True): return 1 #################### #Bug fix : forgot to check for these cases elif _is_const(F,False) and _is_const(H,True): return 1 elif _is_const(F,False) and _is_const(H,False): return 0 elif _is_const(F,True) and _is_const(G,False): return 0 elif _is_const(F,True) and _is_const(G,True): return 1 #################### #If F is a const, then we only have to consider #Either G or H. Base case if they're variable otherwise #parse them into F, G, H elif _is_const(F,True): if G["type"] == "var": return _ite_mk(bdd,G["name"][0],0,1) else: F,G,H = _parse_ite(G) elif _is_const(F,False): if H["type"] == "var": return _ite_mk(bdd, H["name"][0],0,1) else: F,G,H = _parse_ite(H) #################### #Find the top variable. v = top_variable(bdd, F,G,H) #create new expressions with variable propagated Fv, Gv, Hv = copy.deepcopy(F), copy.deepcopy(G), copy.deepcopy(H) Fv = pb.propagate(Fv, (v, True)) Gv = pb.propagate(Gv, (v, True)) Hv = pb.propagate(Hv, (v, True)) Fnv = pb.propagate(F, (v, False)) Gnv = pb.propagate(G, (v, False)) Hnv = pb.propagate(H, (v, False)) #Recursively find T (then) and E (else) nodes T = _ite(bdd, Fv, Gv, Hv) E = _ite(bdd, Fnv, Gnv, Hnv) #If they're the same, then we don't need to make a new #node #pdb.set_trace() if T == E: return T #make a new node and return it R = _ite_mk(bdd,v,E,T) return R
def _ite(bdd, F, G, H): """ Main recursive method. Follows Bryants paper with a few added heuristics which are noted. """ #################### #Possible Base Cases #################### #If G and H and constants: # #Base case if F is a variable or constant #Else Parse F into FGH form. #Switching the _ite_mk args, because they seem to be backwards! while (_is_const(G, True) and _is_const(H, False)) or (_is_const(H, True) and _is_const( G, False)) and (F["type"] == "neg" or F["type"] == "var"): if _is_const(G, True) and _is_const(H, False): if F["type"] == "var": return _ite_mk(bdd, F["name"][0], 0, 1) elif F["type"] == "const": return 1 if F["value"] else 0 else: F, G, H = _parse_ite(F) elif _is_const(H, True) and _is_const(G, False): if F["type"] == "var": return _ite_mk(bdd, F["name"][0], 1, 0) elif F["type"] == "const": return 0 if F["value"] else 1 else: #Fix applied Feb. 16, 2013 by Tyler Sorensen F, G, H = _parse_ite(F) H = pbb.mk_neg_expr(H) G = pbb.mk_neg_expr(G) #################### #if H and G are equal and constant #Just return what H and G are (my heuristic) if _is_const(G, False) and _is_const(H, False): return 0 elif _is_const(G, True) and _is_const(H, True): return 1 #################### #Bug fix : forgot to check for these cases elif _is_const(F, False) and _is_const(H, True): return 1 elif _is_const(F, False) and _is_const(H, False): return 0 elif _is_const(F, True) and _is_const(G, False): return 0 elif _is_const(F, True) and _is_const(G, True): return 1 #################### #If F is a const, then we only have to consider #Either G or H. Base case if they're variable otherwise #parse them into F, G, H elif _is_const(F, True): if G["type"] == "var": return _ite_mk(bdd, G["name"][0], 0, 1) else: F, G, H = _parse_ite(G) elif _is_const(F, False): if H["type"] == "var": return _ite_mk(bdd, H["name"][0], 0, 1) else: F, G, H = _parse_ite(H) #################### #Find the top variable. v = top_variable(bdd, F, G, H) #create new expressions with variable propagated Fv, Gv, Hv = copy.deepcopy(F), copy.deepcopy(G), copy.deepcopy(H) Fv = pb.propagate(Fv, (v, True)) Gv = pb.propagate(Gv, (v, True)) Hv = pb.propagate(Hv, (v, True)) Fnv = pb.propagate(F, (v, False)) Gnv = pb.propagate(G, (v, False)) Hnv = pb.propagate(H, (v, False)) #Recursively find T (then) and E (else) nodes T = _ite(bdd, Fv, Gv, Hv) E = _ite(bdd, Fnv, Gnv, Hnv) #If they're the same, then we don't need to make a new #node #pdb.set_trace() if T == E: return T #make a new node and return it R = _ite_mk(bdd, v, E, T) return R
def simplify(expr): expr = propagate(expr, (None, True)) if type(expr) == bool: return PBB.mk_const_expr(expr) return expr