def simplify1(fm): """Simplify formula for one step.""" if fm.is_not(): if fm.arg == false: return true elif fm.arg == true: return false elif fm.arg.is_not(): return fm.arg.arg else: return fm elif fm.is_conj(): if fm.arg1 == false or fm.arg == false: return false elif fm.arg1 == true: return fm.arg elif fm.arg == true: return fm.arg1 else: return fm elif fm.is_disj(): if fm.arg1 == true or fm.arg == true: return true elif fm.arg1 == false: return fm.arg elif fm.arg == false: return fm.arg1 else: return fm elif fm.is_implies(): if fm.arg1 == false or fm.arg == true: return true elif fm.arg1 == true: return fm.arg elif fm.arg == false: return Not(fm.arg1) else: return fm elif fm.is_equals(): if fm.arg1 == true: return fm.arg elif fm.arg == true: return fm.arg1 elif fm.arg1 == false: return Not(fm.arg) elif fm.arg == false: return Not(fm.arg1) else: return fm elif fm.is_forall() or fm.is_exists(): if has_bound0(fm.arg.body): return fm else: return fm.arg.subst_bound(Var("_u", fm.arg.var_T)) else: return fm
def get_proof_term(self, args, pts): # First, find the pair i, j such that B_j = ~A_i or A_i = ~B_j, the # variable side records the side of the positive literal. pt1, pt2 = pts disj1 = strip_num(pt1.prop, args[0]) disj2 = strip_num(pt2.prop, args[1]) side = None for i, t1 in enumerate(disj1): for j, t2 in enumerate(disj2): if t2 == Not(t1): side = 'left' break elif t1 == Not(t2): side = 'right' break if side is not None: break assert side is not None, "resolution: literal not found" # If side is wrong, just swap: if side == 'right': return self.get_proof_term([args[1], args[0]], [pt2, pt1]) # Move items i and j to the front disj1 = [disj1[i]] + disj1[:i] + disj1[i + 1:] disj2 = [disj2[j]] + disj2[:j] + disj2[j + 1:] eq_pt1 = logic.imp_disj_iff(Eq(pt1.prop, Or(*disj1))) eq_pt2 = logic.imp_disj_iff(Eq(pt2.prop, Or(*disj2))) pt1 = eq_pt1.equal_elim(pt1) pt2 = eq_pt2.equal_elim(pt2) if len(disj1) > 1 and len(disj2) > 1: pt = logic.apply_theorem('resolution', pt1, pt2) elif len(disj1) > 1 and len(disj2) == 1: pt = logic.apply_theorem('resolution_left', pt1, pt2) elif len(disj1) == 1 and len(disj2) > 1: pt = logic.apply_theorem('resolution_right', pt1, pt2) else: pt = logic.apply_theorem('negE', pt2, pt1) # return pt.on_prop(disj_norm()) disj_new = set(disj1[1:] + disj2[1:]) # eq_pt_norm = logic.imp_disj_iff(Eq(pt.prop, Or(*disj_new))) implies_pt_norm = ProofTerm("imp_disj", Implies(pt.prop, Or(*disj_new))) pt_final = implies_pt_norm.implies_elim(pt) self.arity = len(disj_new) return pt_final.on_prop(conv.top_conv(conv.rewr_conv("double_neg")))
def get_proof_term(self, goal, *, args=None, prevs=None): assert isinstance(args, Term), "cases" As = goal.hyps C = goal.prop goal1 = ProofTerm.sorry(Thm(goal.hyps, Implies(args, C))) goal2 = ProofTerm.sorry(Thm(goal.hyps, Implies(Not(args), C))) return apply_theorem('classical_cases', goal1, goal2)
def get_proof_term(self, arg, pts): """Input proof terms are A_1 | ... | A_m and B_1 | ... | B_n, where there is some i, j such that B_j = ~A_i or A_i = ~B_j.""" # First, find the pair i, j such that B_j = ~A_i or A_i = ~B_j, the # variable side records the side of the positive literal. pt1, pt2 = pts disj1 = strip_disj(pt1.prop) disj2 = strip_disj(pt2.prop) side = None for i, t1 in enumerate(disj1): for j, t2 in enumerate(disj2): if t2 == Not(t1): side = 'left' break elif t1 == Not(t2): side = 'right' break if side is not None: break assert side is not None, "resolution: literal not found" # If side is wrong, just swap: if side == 'right': return self.get_proof_term(arg, [pt2, pt1]) # Move items i and j to the front disj1 = [disj1[i]] + disj1[:i] + disj1[i+1:] disj2 = [disj2[j]] + disj2[:j] + disj2[j+1:] eq_pt1 = imp_disj_iff(Eq(pt1.prop, Or(*disj1))) eq_pt2 = imp_disj_iff(Eq(pt2.prop, Or(*disj2))) pt1 = eq_pt1.equal_elim(pt1) pt2 = eq_pt2.equal_elim(pt2) if len(disj1) > 1 and len(disj2) > 1: pt = apply_theorem('resolution', pt1, pt2) elif len(disj1) > 1 and len(disj2) == 1: pt = apply_theorem('resolution_left', pt1, pt2) elif len(disj1) == 1 and len(disj2) > 1: pt = apply_theorem('resolution_right', pt1, pt2) else: pt = apply_theorem('negE', pt2, pt1) return pt.on_prop(disj_norm())
def get_proof_term(self, goal, pts): assert isinstance(goal, Term) assert len(pts) == 0, "nat_const_less_macro" m, n = goal.args assert m.dest_number() < n.dest_number() less_eq_pt = nat_const_less_eq_macro().get_proof_term(m <= n, []) ineq_pt = nat_const_ineq_macro().get_proof_term(Not(Eq(m, n)), []) return apply_theorem("less_lesseqI", less_eq_pt, ineq_pt)
def testEvalSem5(self): com = While(Lambda(s, Not(Eq(s(zero), Nat(3)))), assn_true, incr_one) st = mk_const_fun(NatType, zero) st2 = fun_upd_of_seq(0, 3) goal = Sem(com, st, st2) prf = imp.eval_Sem_macro().get_proof_term(goal, []).export() rpt = ProofReport() self.assertEqual(theory.check_proof(prf, rpt), Thm([], goal))
def testPelletier(self): with open('prover/tests/pelletier.json', 'r', encoding='utf-8') as f: f_data = json.load(f) for problem in f_data: context.set_context('sat', vars=problem['vars']) prop = parser.parse_term(problem['prop']) cnf = tseitin.convert_cnf(tseitin.encode(Not(prop)).prop) res, cert = sat.solve_cnf(cnf) self.assertEqual(res, 'unsatisfiable')
def get_proof_term(self, t): if not t.is_disj(): return refl(t) nnf_pt = nnf_conv().get_proof_term(Not(t)) norm_neg_disj_pt = sort_conj().get_proof_term(nnf_pt.rhs) nnf_pt_norm = nnf_pt.transitive(norm_neg_disj_pt) return nnf_pt_norm.on_prop(rewr_conv('neg_iff_both_sides'), arg1_conv(rewr_conv('double_neg')), arg_conv(nnf_conv()))
def encode(t): """Given a propositional formula t, compute its Tseitin encoding. 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. """ # Mapping from subterms to newly introduced variables subterm_dict = dict() for i, subt in enumerate(logic_subterms(t)): subterm_dict[subt] = Var('x' + str(i + 1), BoolType) # Collect list of equations eqs = [] for subt in subterm_dict: r = subterm_dict[subt] if not is_logical(subt): eqs.append(Eq(r, subt)) elif subt.is_not(): r1 = subterm_dict[subt.arg] eqs.append(Eq(r, Not(r1))) else: r1 = subterm_dict[subt.arg1] r2 = subterm_dict[subt.arg] eqs.append(Eq(r, subt.head(r1, r2))) # Form the proof term eq_pts = [ProofTerm.assume(eq) for eq in eqs] encode_pt = ProofTerm.assume(t) for eq_pt in eq_pts: encode_pt = encode_pt.on_prop(top_conv(rewr_conv(eq_pt, sym=True))) for eq_pt in eq_pts: if is_logical(eq_pt.rhs): encode_pt = logic.apply_theorem('conjI', eq_pt, encode_pt) # Rewrite using Tseitin rules encode_thms = [ 'encode_conj', 'encode_disj', 'encode_imp', 'encode_eq', 'encode_not' ] for th in encode_thms: encode_pt = encode_pt.on_prop(top_conv(rewr_conv(th))) # Normalize the conjuncts return encode_pt.on_prop(logic.conj_norm())
def get_proof_term(self, prevs, goal_lit): disj, *lit_pts = prevs pt_conj = lit_pts[0] for i in range(len(lit_pts)): pt = lit_pts[i] if not pt.prop.is_not(): lit_pts[i] = pt.on_prop(rewr_conv('double_neg', sym=True)) def conj_right_assoc(pts): """ Give a sequence of proof terms: ⊢ A, ⊢ B, ⊢ C, return ⊢ A ∧ (B ∧ C) """ if len(pts) == 1: return pts[0] else: return apply_theorem('conjI', pts[0], conj_right_assoc(pts[1:])) # get a /\ b /\ c pt_conj = conj_right_assoc(lit_pts) other_lits = [ l.prop.arg if l.prop.is_not() else Not(l.prop) for l in lit_pts ] # use de Morgan pt_conj1 = pt_conj.on_prop( bottom_conv(rewr_conv('de_morgan_thm2', sym=True))) # if len(other_lits) == 1 and other_lits[0].is_not(): # pt_conj1 = pt_conj.on_prop(rewr_conv('double_neg', sym=True)) # Equality for two disjunctions which literals are the same, but order is different. eq_pt = imp_disj_iff(Eq(disj.prop, Or(goal_lit, *other_lits))) new_disj_pt = disj.on_prop(top_conv(replace_conv(eq_pt))) # A \/ B --> ~B --> A pt = ProofTerm.theorem('force_disj_true1') A, B = pt.prop.strip_implies()[0] C = pt.prop.strip_implies()[1] inst1 = matcher.first_order_match(C, goal_lit) inst2 = matcher.first_order_match(A, Or(goal_lit, *other_lits), inst=inst1) inst3 = matcher.first_order_match(B, pt_conj1.prop, inst=inst2) pt_implies = apply_theorem('force_disj_true1', new_disj_pt, pt_conj1, inst=inst3) return pt_implies.on_prop(try_conv(rewr_conv('double_neg')))
def testEvalSem4(self): com = Cond(Lambda(s, Not(Eq(s(zero), one))), incr_one, Skip) st = mk_const_fun(NatType, zero) st2 = fun_upd_of_seq(0, 1) goal = Sem(com, st, st2) prf = imp.eval_Sem_macro().get_proof_term(goal, []).export() self.assertEqual(theory.check_proof(prf), Thm([], goal)) goal = Sem(com, st2, st2) prf = imp.eval_Sem_macro().get_proof_term(goal, []).export() self.assertEqual(theory.check_proof(prf), Thm([], goal))
def get_proof_term(self, t): pt = refl(t) if t.arg1 == true: return pt.on_rhs(rewr_conv('conj_true_left')) elif t.arg == true: return pt.on_rhs(rewr_conv('conj_true_right')) elif t.arg1 == false: return pt.on_rhs(rewr_conv('conj_false_right')) elif t.arg == false: return pt.on_rhs(rewr_conv('conj_false_left')) elif t.arg.is_conj(): if t.arg1 == Not(t.arg.arg1): # A /\ (A_1 /\ ... /\ A_n) return pt.on_rhs(rewr_conv('conj_assoc'), arg1_conv(rewr_conv('conj_neg_pos')), rewr_conv('conj_false_right')) elif Not(t.arg1) == t.arg.arg1: return pt.on_rhs(rewr_conv('conj_assoc'), arg1_conv(rewr_conv('conj_pos_neg')), rewr_conv('conj_false_right')) cp = term_ord.fast_compare(t.arg1, t.arg.arg1) if cp > 0: return pt.on_rhs(swap_conj_r(), arg_conv(self), try_conv(self)) elif cp == 0: return pt.on_rhs(rewr_conv('conj_assoc'), arg1_conv(rewr_conv('conj_same_atom'))) else: return pt else: if t.arg == Not(t.arg1): return pt.on_rhs(rewr_conv('conj_pos_neg')) elif t.arg1 == Not(t.arg): return pt.on_rhs(rewr_conv('conj_neg_pos')) cp = term_ord.fast_compare(t.arg1, t.arg) if cp > 0: return pt.on_rhs(swap_conj_r()) elif cp == 0: return pt.on_rhs(rewr_conv('conj_same_atom')) else: return pt
def convert_cnf_to_HOL(cnf_file): disjs = read_cnf_file(cnf_file) tms = [] for disj in disjs: lits = [] for var, stat in disj: if stat: lits.append(Var(var, BoolType)) else: lits.append(Not(Var(var, BoolType))) tms.append(Or(*lits)) return And(*tms)
def eval(self, goal, prevs): assert len(prevs) == 0, "int_const_ineq: no conditions expected" if goal.is_not(): goal = goal.arg assert (goal.is_compares() or goal.is_equals()) and goal.arg1.is_constant() and goal.arg.is_constant()\ and goal.arg1.get_type() == IntType, repr(goal) lhs, rhs = int_eval(goal.arg1), int_eval(goal.arg) if goal.is_less(): if lhs < rhs: return Thm([], goal) else: return Thm([], Not(goal)) elif goal.is_less_eq(): if lhs <= rhs: return Thm([], goal) else: return Thm([], Not(goal)) elif goal.is_greater(): if lhs > rhs: return Thm([], goal) else: return Thm([], Not(goal)) elif goal.is_greater_eq(): if lhs >= rhs: return Thm([], goal) else: return Thm([], Not(goal)) elif goal.is_equals(): if lhs == rhs: return Thm([], goal) else: return Thm([], Not(goal)) else: raise NotImplementedError
def simplify(fm): """Simplify formula. Remove true, false, and vacuous forall/exists quantification. """ if fm.is_not(): return simplify1(Not(simplify(fm.arg))) elif fm.is_conj() or fm.is_disj() or fm.is_implies() or fm.is_equals(): return simplify1(fm.head(simplify(fm.arg1), simplify(fm.arg))) elif fm.is_forall() or fm.is_exists(): assert fm.arg.is_abs() return simplify1( fm.fun(Abs(fm.arg.var_name, fm.arg.var_T, simplify(fm.arg.body)))) else: return fm
def testPrintUnicode(self): test_data = [ (And(A, B), "A ∧ B"), (Or(A, B), "A ∨ B"), (Implies(A, B), "A ⟶ B"), (Lambda(a, P(a)), "λa. P a"), (Forall(a, P(a)), "∀a. P a"), (Exists(a, P(a)), "∃a. P a"), (Not(A), "¬A"), (Lambda(m, m + 2), "λm::nat. m + 2"), (Lambda(m, m + n), "λm. m + n"), ] with global_setting(unicode=True): for t, s in test_data: self.assertEqual(printer.print_term(t), s)
def get_proof_term(self, goal, prevs=None): """{(not (= x_1 y_1)) ... (not (= x_n y_n)) (not (p x_1 ... x_n)) (p y_1 ... y_n)} Special case: (not (= x y)) (not (p x y)) (p y x) """ elems = goal.strip_disj() preds, pred_fun, concl = elems[:-2], elems[-2], elems[-1] if pred_fun.is_not(): args_pair = [(i, j) for i, j in zip(pred_fun.arg.strip_comb()[1], concl.strip_comb()[1])] else: args_pair = [(i, j) for i, j in zip(pred_fun.strip_comb()[1], concl.arg.strip_comb()[1])] if len(preds) > 1: preds_pair = [(i.arg.lhs, i.arg.rhs) for i in preds] else: preds_pair = [(preds[0].arg.lhs, preds[0].arg.rhs), (preds[0].arg.lhs, preds[0].arg.rhs)] if pred_fun.is_not(): fun = concl.head else: fun = pred_fun.head pt0 = ProofTerm.reflexive(fun) pt_args_assms = [] for arg, pred in zip(args_pair, preds_pair): if arg == pred: pt_args_assms.append(ProofTerm.assume(Eq(pred[0], pred[1]))) elif arg[0] == pred[1] and pred[0] == arg[1]: pt_args_assms.append( ProofTerm.assume(Eq(pred[0], pred[1])).symmetric()) else: raise NotImplementedError pt1 = functools.reduce(lambda x, y: x.combination(y), pt_args_assms, pt0) if pred_fun.is_not(): pt2 = logic.apply_theorem("eq_implies1", pt1).implies_elim( ProofTerm.assume(pred_fun.arg)) return ProofTerm("imp_to_or", elems[:-1] + [goal], prevs=[pt2]) else: pt2 = pt1.on_prop(conv.rewr_conv("neg_iff_both_sides")) pt3 = logic.apply_theorem("eq_implies1", pt2).implies_elim( ProofTerm.assume(Not(pred_fun))) return ProofTerm("imp_to_or", elems[:-1] + [goal], prevs=[pt3])
def get_extension(self): assert self.error is None, "get_extension" res = [] # Add to type and term signature. res.append(extension.TConst(self.name, len(self.args))) for constr in self.constrs: res.append( extension.Constant(constr['name'], constr['type'], ref_name=constr['cname'])) # Add non-equality theorems. for constr1, constr2 in itertools.combinations(self.constrs, 2): # For each A x_1 ... x_m and B y_1 ... y_n, get the theorem # ~ A x_1 ... x_m = B y_1 ... y_n. argT1, _ = constr1['type'].strip_type() argT2, _ = constr2['type'].strip_type() lhs_vars = [Var(nm, T) for nm, T in zip(constr1['args'], argT1)] rhs_vars = [Var(nm, T) for nm, T in zip(constr2['args'], argT2)] A = Const(constr1['name'], constr1['type']) B = Const(constr2['name'], constr2['type']) lhs = A(*lhs_vars) rhs = B(*rhs_vars) neq = Not(Eq(lhs, rhs)) th_name = "%s_%s_%s_neq" % (self.name, constr1['name'], constr2['name']) res.append(extension.Theorem(th_name, Thm([], neq))) # Add injectivity theorems. for constr in self.constrs: # For each A x_1 ... x_m with m > 0, get the theorem # A x_1 ... x_m = A x_1' ... x_m' --> x_1 = x_1' & ... & x_m = x_m' if constr['args']: argT, _ = constr['type'].strip_type() lhs_vars = [Var(nm, T) for nm, T in zip(constr['args'], argT)] rhs_vars = [ Var(nm + "1", T) for nm, T in zip(constr['args'], argT) ] A = Const(constr['name'], constr['type']) assum = Eq(A(*lhs_vars), A(*rhs_vars)) concls = [ Eq(var1, var2) for var1, var2 in zip(lhs_vars, rhs_vars) ] concl = And(*concls) th_name = "%s_%s_inject" % (self.name, constr['name']) res.append( extension.Theorem(th_name, Thm([], Implies(assum, concl)))) # Add the inductive theorem. tvars = [TVar(targ) for targ in self.args] T = TConst(self.name, *tvars) var_P = Var("P", TFun(T, BoolType)) ind_assums = [] for constr in self.constrs: A = Const(constr['name'], constr['type']) argT, _ = constr['type'].strip_type() args = [Var(nm, T2) for nm, T2 in zip(constr['args'], argT)] C = var_P(A(*args)) As = [ var_P(Var(nm, T2)) for nm, T2 in zip(constr['args'], argT) if T2 == T ] ind_assum = Implies(*(As + [C])) for arg in reversed(args): ind_assum = Forall(arg, ind_assum) ind_assums.append(ind_assum) ind_concl = var_P(Var("x", T)) th_name = self.name + "_induct" res.append( extension.Theorem(th_name, Thm([], Implies(*(ind_assums + [ind_concl]))))) res.append(extension.Attribute(th_name, "var_induct")) return res
def nnf(fm): """Negation normal form of a formula.""" if fm.is_conj(): return And(nnf(fm.arg1), nnf(fm.arg)) elif fm.is_disj(): return Or(nnf(fm.arg1), nnf(fm.arg)) elif fm.is_implies(): return Or(nnf(Not(fm.arg1)), nnf(fm.arg)) elif fm.is_equals(): return Or(And(nnf(fm.arg1), nnf(fm.arg)), And(nnf(Not(fm.arg1)), nnf(Not(fm.arg)))) elif fm.is_not(): p = fm.arg if p.is_not(): return nnf(p.arg) elif p.is_conj(): return Or(nnf(Not(p.arg1)), nnf(Not(p.arg))) elif p.is_disj(): return And(nnf(Not(p.arg1)), nnf(Not(p.arg))) elif p.is_implies(): return And(nnf(p.arg1), nnf(Not(p.arg))) elif p.is_equals(): return Or(And(nnf(p.arg1), nnf(Not(p.arg))), And(nnf(Not(p.arg1)), nnf(p.arg))) elif p.is_forall(): assert p.arg.is_abs() return term.exists(p.arg.var_T)(Abs(p.arg.var_name, p.arg.var_T, nnf(Not(p.arg.body)))) elif p.is_exists(): assert p.arg.is_abs() return term.forall(p.arg.var_T)(Abs(p.arg.var_name, p.arg.var_T, nnf(Not(p.arg.body)))) else: return fm elif fm.is_forall() or fm.is_exists(): assert fm.arg.is_abs() return fm.fun(Abs(fm.arg.var_name, fm.arg.var_T, nnf(fm.arg.body))) else: return fm
def ineq_cond(self, e1, e2): return Not(Eq(e1, e2))
def neg(self, t): return Not(t)
def solve(self): """ Call zChaff solver, return the proof term. """ # First write the cnf to a .cnf file. s = 'p cnf ' + str(self.var_num) + ' ' + str(self.clause_num) for clause in self.cnf_list: s += '\n' + ' '.join(str(l) for l in clause) + ' 0' with open('./sat/x.cnf', 'w') as f: f.write(s) # then call zChaff to get the proof trace p = subprocess.Popen('.\\sat\\binaries\\zchaff.exe .\\sat\\x.cnf', stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() result = stdout.decode('utf-8').split('\n')[-2] assert result == "RESULT:\tUNSAT\r" # proof reconstruct first = [] second = [] third = [] with open('.\\resolve_trace', 'r') as f: lines = f.readlines() for l in lines: if l.startswith('CL'): first.append(Resolvent(l)) elif l.startswith('VAR'): second.append(ImpliedVarValue(l)) else: third.append(Conflict(l)) for j, f in enumerate(first): res_cls = f.rsl pt = self.clause_pt[res_cls[0]] for i in range(len(res_cls) - 1): pt = resolution(pt, self.clause_pt[res_cls[i + 1]]) self.clause_pt[len(self.clause_pt)] = pt second = sorted(second, key=lambda x: x.level) # dictionary from var index to its true value var_pt = {} for s in second: cls_pt = self.clause_pt[s.act] pts = [] lits = [floor(l / 2) for l in s.lits if floor(l / 2) != s.var] if not lits: var_pt[s.var] = cls_pt continue exist_var_pt = [var_pt[l] for l in lits] exact_var = self.index_var[s.var] if s.value == 1 else Not( self.index_var[s.var]) prevs = [cls_pt] + exist_var_pt var_pt[s.var] = DisjForceMacro().get_proof_term(prevs, exact_var) conflict_cls = third[0] literal_pt = [var_pt[floor(i / 2)] for i in conflict_cls.lits] self.conflict_pt = DisjFalseMacro().get_proof_term( self.clause_pt[conflict_cls.cls], literal_pt) # return the theorem pt1, pt2 = self.encode_pt, self.conflict_pt while pt1.prop.is_conj(): pt_left = apply_theorem('conjD1', pt1) pt2 = pt2.implies_intr(pt_left.prop).implies_elim( pt_left) # remove one clause from assumption pt1 = apply_theorem('conjD2', pt1) pt2 = pt2.implies_intr(pt1.prop).implies_elim( pt1) # remove last clause from assumption # Clear definition of new variables from antecedent eqs = [t for t in pt2.hyps if t.is_equals()] eqs = list(reversed(sorted(eqs, key=lambda t: int(t.lhs().name[1:])))) for eq in eqs: pt2 = pt2.implies_intr(eq).forall_intr(eq.lhs).forall_elim(eq.rhs) \ .implies_elim(ProofTerm.reflexive(eq.rhs)) return apply_theorem('negI', pt2.implies_intr(pt2.hyps[0])).on_prop( rewr_conv('double_neg'))
def nat_const_ineq(a, b): return ProofTerm("nat_const_ineq", Not(Eq(a, b)), [])
def init_proof(prop): """Initialize proof for proposition.""" vars = prop.get_vars() assms, concl = prop.strip_implies() assms.append(concl.arg if concl.is_not() else Not(concl)) return ProofState(vars, assms)
def testPrintLogical(self): test_data = [ # Variables (SVar("P", BoolType), "?P"), (a, "a"), # Equality and implies (Eq(a, b), "a = b"), (Implies(A, B), "A --> B"), (Implies(A, B, C), "A --> B --> C"), (Implies(Implies(A, B), C), "(A --> B) --> C"), (Implies(A, Eq(a, b)), "A --> a = b"), (Eq(Implies(A, B), Implies(B, C)), "(A --> B) <--> (B --> C)"), (Eq(A, Eq(B, C)), "A <--> B <--> C"), (Eq(Eq(A, B), C), "(A <--> B) <--> C"), # Conjunction and disjunction (And(A, B), "A & B"), (Or(A, B), "A | B"), (And(A, And(B, C)), "A & B & C"), (And(And(A, B), C), "(A & B) & C"), (Or(A, Or(B, C)), "A | B | C"), (Or(Or(A, B), C), "(A | B) | C"), (Or(And(A, B), C), "A & B | C"), (And(Or(A, B), C), "(A | B) & C"), (Or(A, And(B, C)), "A | B & C"), (And(A, Or(B, C)), "A & (B | C)"), (Or(And(A, B), And(B, C)), "A & B | B & C"), (And(Or(A, B), Or(B, C)), "(A | B) & (B | C)"), # Negation (Not(A), "~A"), (Not(Not(A)), "~~A"), # Constants (true, "true"), (false, "false"), # Mixed (Implies(And(A, B), C), "A & B --> C"), (Implies(A, Or(B, C)), "A --> B | C"), (And(A, Implies(B, C)), "A & (B --> C)"), (Or(Implies(A, B), C), "(A --> B) | C"), (Not(And(A, B)), "~(A & B)"), (Not(Implies(A, B)), "~(A --> B)"), (Not(Eq(A, B)), "~(A <--> B)"), (Eq(Not(A), B), "~A <--> B"), (Eq(Not(A), Not(B)), "~A <--> ~B"), (Implies(A, Eq(B, C)), "A --> B <--> C"), (Eq(Implies(A, B), C), "(A --> B) <--> C"), # Abstraction (Lambda(a, And(P(a), Q(a))), "%a. P a & Q a"), # Quantifiers (Forall(a, P(a)), "!a. P a"), (Forall(a, Forall(b, And(P(a), P(b)))), "!a. !b. P a & P b"), (Forall(a, And(P(a), Q(a))), "!a. P a & Q a"), (And(Forall(a, P(a)), Q(a)), "(!a1. P a1) & Q a"), (Forall(a, Implies(P(a), Q(a))), "!a. P a --> Q a"), (Implies(Forall(a, P(a)), Q(a)), "(!a1. P a1) --> Q a"), (Implies(Forall(a, P(a)), Forall(a, Q(a))), "(!a. P a) --> (!a. Q a)"), (Implies(Exists(a, P(a)), Exists(a, Q(a))), "(?a. P a) --> (?a. Q a)"), (Eq(A, Forall(a, P(a))), "A <--> (!a. P a)"), (Exists(a, P(a)), "?a. P a"), (Exists(a, Forall(b, R(a, b))), "?a. !b. R a b"), (logic.mk_exists1(a, P(a)), "?!a. P a"), (logic.mk_the(a, P(a)), "THE a. P a"), (logic.mk_some(a, P(a)), "SOME a. P a"), (Forall(a, Exists(b, R(a, b))), "!a. ?b. R a b"), # If (mk_if(A, a, b), "if A then a else b"), (Eq(mk_if(A, a, b), a), "(if A then a else b) = a"), (mk_if(A, P, Q), "if A then P else Q"), ] with global_setting(unicode=False): for t, s in test_data: self.assertEqual(printer.print_term(t), s)
def get_proof_term(self, t): if not t.is_conj(): return refl(t) d_pos = dict() d_neg = dict() qu = deque([ProofTerm.assume(t)]) # collect each conjunct's proof term in conjuntion while qu: pt = qu.popleft() if pt.prop.is_conj(): conj1, conj2 = pt.prop.arg1, pt.prop.arg pt_conj1, pt_conj2 = apply_theorem('conjD1', pt), apply_theorem( 'conjD2', pt) if conj1 == false: th = ProofTerm.theorem("falseE") inst = matcher.first_order_match(th.prop.arg, t) pt_false_implies_conj = th.substitution(inst) return ProofTerm.equal_intr(pt_conj1.implies_intr(t), pt_false_implies_conj) elif conj2 == false: th = ProofTerm.theorem("falseE") inst = matcher.first_order_match(th.prop.arg, t) pt_false_implies_conj = th.substitution(inst) return ProofTerm.equal_intr(pt_conj2.implies_intr(t), pt_false_implies_conj) if conj1.is_conj(): qu.appendleft(pt_conj1) else: if conj1.is_not(): d_neg[conj1] = pt_conj1 else: d_pos[conj1] = pt_conj1 if conj2.is_conj(): qu.appendleft(pt_conj2) else: if conj2.is_not(): d_neg[conj2] = pt_conj2 else: d_pos[conj2] = pt_conj2 else: if pt.prop.is_not(): d_neg[pt.prop] = pt else: d_pos[pt.prop] = pt # first check if there are opposite terms in conjunctions, if there exists, return a false proof term for key in d_pos: if Not(key) in d_neg: pos_pt, neg_pt = d_pos[key], d_neg[Not(key)] pt_conj_pos_neg = apply_theorem("conjI", pos_pt, neg_pt) pt_conj_implies_false = pt_conj_pos_neg.on_prop( rewr_conv("conj_pos_neg")).implies_intr(t) th = ProofTerm.theorem("falseE") inst = matcher.first_order_match(th.prop.arg, t) pt_false_implies_conj = th.substitution(inst) return ProofTerm.equal_intr(pt_conj_implies_false, pt_false_implies_conj) d_pos.update(d_neg) d = d_pos def right_assoc(ts): l = len(ts) if l == 1: return d[ts[0]] elif l == 2: return apply_theorem('conjI', d[ts[0]], d[ts[1]]) else: return apply_theorem('conjI', d[ts[0]], right_assoc(ts[1:])) # pt_right = functools.reduce(lambda x, y: apply_theorem('conjI', x, d[y]), sorted(d.keys()), d[sorted(d.keys())[0]]) if true not in d: sorted_keys = term_ord.sorted_terms(d.keys()) else: d_keys_without_true = term_ord.sorted_terms( [k for k in d if k != true]) sorted_keys = [true] + d_keys_without_true sorted_keys_num = len(sorted_keys) pt_right = functools.reduce(lambda x, y: apply_theorem('conjI', d[sorted_keys[sorted_keys_num - y - 2]], x), \ range(sorted_keys_num - 1), d[sorted_keys[-1]]) # pt_right = right_assoc(sorted_keys) # order implies original dd = dict() norm_conj = And(*sorted_keys) norm_conj_pt = ProofTerm.assume(norm_conj) for k in sorted_keys: if k != sorted_keys[-1]: dd[k] = apply_theorem('conjD1', norm_conj_pt) norm_conj_pt = apply_theorem('conjD2', norm_conj_pt) else: dd[k] = norm_conj_pt def traverse(t): if not t.is_conj(): return dd[t] else: return apply_theorem('conjI', traverse(t.arg1), traverse(t.arg)) pt_left = traverse(t) pt_final = ProofTerm.equal_intr(pt_right.implies_intr(t), pt_left.implies_intr(norm_conj)) if true in d: return pt_final.on_rhs(rewr_conv("conj_true_left"), top_sweep_conv(sort_disj())) else: return pt_final.on_rhs(top_sweep_conv(sort_disj()))