def logic_subterms(t): """Returns the list of logical subterms for a term t.""" if t.is_implies() or t.is_equals() or logic.is_conj(t) or logic.is_disj(t): return logic_subterms(t.arg1) + logic_subterms(t.arg) + [t] elif logic.is_neg(t): return logic_subterms(t.arg) + [t] else: return [t]
def get_encode_proof(th): """Given resulting theorem for an encoding, obtain the proof of the theorem. The theorem is structured as follows: Each of the assumptions, except the last, is an equality, where the right side is either an atom or a logical operation between atoms. We call these assumptions As. The last assumption is the original formula. We call it F. The conclusion is in CNF. Each clause except the last is an expansion of one of As. The last clause is obtained by performing substitutions of As on F. """ As, F = th.hyps[:-1], th.hyps[-1] # Obtain the assumptions ptAs = [ProofTerm.assume(A) for A in As] ptF = ProofTerm.assume(F) # Obtain the expansion of each As to a non-atomic term. pts = [] for ptA in ptAs: rhs = ptA.prop.rhs if logic.is_conj(rhs): pts.append(ptA.on_prop(thy, rewr_conv("encode_conj"))) elif logic.is_disj(rhs): pts.append(ptA.on_prop(thy, rewr_conv("encode_disj"))) elif rhs.is_implies(): pts.append(ptA.on_prop(thy, rewr_conv("encode_imp"))) elif rhs.is_equals(): pts.append(ptA.on_prop(thy, rewr_conv("encode_eq"))) elif logic.is_neg(rhs): pts.append(ptA.on_prop(thy, rewr_conv("encode_not"))) # Obtain the rewrite of the original formula. cvs = [ top_conv(rewr_conv(ProofTerm.symmetric(ptA), match_vars=False)) for ptA in ptAs ] cv = every_conv(*cvs) pts.append(ptF.on_prop(thy, cv)) pt = pts[0] for pt2 in pts[1:]: pt = logic_macro.apply_theorem(thy, 'conjI', pt, pt2) return pt.on_prop(thy, logic.norm_conj_assoc())
def convert(t): """Convert term t to Z3 input.""" if t.is_var(): T = t.get_type() if T == nat.natT: return z3.Int(t.name) elif T == TFun(nat.natT, nat.natT): return z3.Function(t.name, z3.IntSort(), z3.IntSort()) elif T == TFun(nat.natT, boolT): return z3.Function(t.name, z3.IntSort(), z3.BoolSort()) elif T == boolT: return z3.Bool(t.name) else: print("convert: unsupported type " + repr(T)) raise NotImplementedError elif t.is_all(): if t.arg.var_T == nat.natT: v = Var(t.arg.var_name, nat.natT) z3_v = z3.Int(t.arg.var_name) return z3.ForAll([z3_v], convert(t.arg.subst_bound(v))) else: raise NotImplementedError elif t.is_implies(): return z3.Implies(convert(t.arg1), convert(t.arg)) elif t.is_equals(): return convert(t.arg1) == convert(t.arg) elif logic.is_conj(t): return z3.And(convert(t.arg1), convert(t.arg)) elif logic.is_disj(t): return z3.Or(convert(t.arg1), convert(t.arg)) elif logic.is_neg(t): return z3.Not(convert(t.arg)) elif nat.is_plus(t): return convert(t.arg1) + convert(t.arg) elif nat.is_times(t): return convert(t.arg1) * convert(t.arg) elif nat.is_binary(t): return nat.from_binary(t) elif t.is_comb(): return convert(t.fun)(convert(t.arg)) elif t.is_const(): if t == logic.true: return z3.BoolVal(True) elif t == logic.false: return z3.BoolVal(False) else: print("convert: unsupported constant " + repr(t)) raise NotImplementedError else: print("convert: unsupported operation " + repr(t)) raise NotImplementedError
def convert(t): if t.head in var_map: if len(t.args) == 0: return s(Ident(to_binary(var_map[t.head]))) elif len(t.args) == 1: return s(Para(Ident(to_binary(var_map[t.head])), t.arg)) else: raise NotImplementedError elif t.is_equals(): return Term.mk_equals(convert(t.arg1), convert(t.arg)) elif logic.is_neg(t): return logic.neg(convert(t.arg)) elif logic.is_conj(t): return logic.conj(convert(t.arg1), convert(t.arg)) elif logic.is_disj(t): return logic.disj(convert(t.arg1), convert(t.arg)) elif t.get_type() == boolT: return BoolV(t) elif t.get_type() == natT: return NatV(t) else: raise NotImplementedError
def encode(t): """Convert a holpy term into an equisatisfiable CNF. The result is a pair (cnf, prop), where cnf is the CNF form, and prop is a theorem stating that t, together with equality assumptions, imply the statement in CNF. """ # Find the list of logical subterms, remove duplicates. subterms = logic_subterms(t) subterms = list(OrderedDict.fromkeys(subterms)) subterms_dict = dict() for i, st in enumerate(subterms): subterms_dict[st] = i # The subterm at index i corresponds to variable x(i+1). def get_var(i): return Var("x" + str(i + 1), boolT) # Obtain the results: # eqs -- list of equality assumptions # clauses -- list of clauses eqs = [] clauses = [] for i, st in enumerate(subterms): l = get_var(i) if st.is_implies() or st.is_equals() or logic.is_conj( st) or logic.is_disj(st): r1 = get_var(subterms_dict[st.arg1]) r2 = get_var(subterms_dict[st.arg]) f = st.head eqs.append(Term.mk_equals(l, f(r1, r2))) if st.is_implies(): clauses.extend(encode_eq_imp(l, r1, r2)) elif st.is_equals(): clauses.extend(encode_eq_eq(l, r1, r2)) elif logic.is_conj(st): clauses.extend(encode_eq_conj(l, r1, r2)) else: # st.is_disj() clauses.extend(encode_eq_disj(l, r1, r2)) elif logic.is_neg(st): r = get_var(subterms_dict[st.arg]) eqs.append(Term.mk_equals(l, logic.neg(r))) clauses.extend(encode_eq_neg(l, r)) else: eqs.append(Term.mk_equals(l, st)) clauses.append(get_var(len(subterms) - 1)) # Final proposition: under the equality assumptions and the original # term t, can derive the conjunction of the clauses. th = Thm(eqs + [t], logic.mk_conj(*clauses)) # Final CNF: for each clause, get the list of disjuncts. cnf = [] def literal(t): if logic.is_neg(t): return (t.arg.name, False) else: return (t.name, True) for clause in clauses: cnf.append(list(literal(t) for t in logic.strip_disj(clause))) return cnf, th