def interval_union_subset(t): """Given t of the form I1 Un I2, return a theorem of the form I1 Un I2 SUB I. """ assert t.is_comb('union', 2), "interval_union_subset" I1, I2 = t.args a, b = I1.args c, d = I2.args if is_closed_interval(I1) and is_closed_interval(I2): pt = apply_theorem('closed_interval_union', inst=Inst(a=a, b=b, c=c, d=d)) return pt.on_prop( arg_conv( then_conv(arg1_conv(const_min_conv()), arg_conv(const_max_conv())))) elif is_open_interval(I1) and is_ropen_interval(I2): if eval_hol_expr(c) <= eval_hol_expr(a): pt = apply_theorem('open_ropen_interval_union1', auto.auto_solve(real.less_eq(c, a)), inst=Inst(b=b, d=d)) else: pt = apply_theorem('open_ropen_interval_union2', auto.auto_solve(real.less(a, c)), inst=Inst(b=b, d=d)) return pt.on_prop(arg_conv(arg_conv(const_max_conv()))) else: raise NotImplementedError return pt
def eval_Sem(c, st): """Evaluates the effect of program c on state st.""" T = st.get_type() if c.is_const("Skip"): return apply_theorem("Sem_Skip", inst=Inst(s=st)) elif c.is_comb("Assign", 2): a, b = c.args Ta = a.get_type() Tb = b.get_type().range_type() pt = apply_theorem("Sem_Assign", inst=Inst(a=a, b=b, s=st)) return pt.on_arg(arg_conv(norm_cv)) elif c.is_comb("Seq", 2): c1, c2 = c.args pt1 = eval_Sem(c1, st) pt2 = eval_Sem(c2, pt1.prop.arg) pt = apply_theorem("Sem_seq", pt1, pt2) return pt.on_arg(function.fun_upd_norm_one_conv()) elif c.is_comb("Cond", 3): b, c1, c2 = c.args b_st = beta_norm(b(st)) b_eval = norm_cond_cv.get_proof_term(b_st) if b_eval.prop.arg == true: b_res = b_eval.on_prop(rewr_conv("eq_true", sym=True)) pt1 = eval_Sem(c1, st) return apply_theorem("Sem_if1", b_res, pt1, concl=Sem(T)(c, st, pt1.prop.arg)) else: b_res = b_eval.on_prop(rewr_conv("eq_false", sym=True)) pt2 = eval_Sem(c2, st) return apply_theorem("Sem_if2", b_res, pt2, concl=Sem(T)(c, st, pt2.prop.arg)) elif c.is_comb("While", 3): b, inv, body = c.args b_st = beta_norm(b(st)) b_eval = norm_cond_cv.get_proof_term(b_st) if b_eval.prop.arg == true: b_res = b_eval.on_prop(rewr_conv("eq_true", sym=True)) pt1 = eval_Sem(body, st) pt2 = eval_Sem(c, pt1.prop.arg) pt = apply_theorem("Sem_while_loop", b_res, pt1, pt2, concl=Sem(T)(c, st, pt2.prop.arg), inst=Inst(s3=pt1.prop.arg)) return pt.on_arg(function.fun_upd_norm_one_conv()) else: b_res = b_eval.on_prop(rewr_conv("eq_false", sym=True)) return apply_theorem("Sem_while_skip", b_res, concl=Sem(T)(c, st, st)) else: raise NotImplementedError
def substitution(self, inst=None, **kwargs): if inst is None: inst = Inst(**kwargs) if inst: return ProofTerm("substitution", inst, [self]) else: return self
def get_proof_term(self, args, pts): if self.with_inst: name, inst = args else: name = args inst = Inst() th = theory.get_theorem(name) As, C = th.prop.strip_implies() assert len(pts) <= len(As), "apply_theorem: too many prevs." # First attempt to match type variables svars = th.prop.get_svars() for v in svars: if v.name in inst: v.T.match_incr(inst[v.name].get_type(), inst.tyinst) pats = As[:len(pts)] ts = [pt.prop for pt in pts] inst = matcher.first_order_match_list(pats, ts, inst) pt = ProofTerm.theorem(name) pt = pt.subst_type(inst.tyinst).substitution(inst) if pt.prop.beta_norm() != pt.prop: pt = pt.on_prop(beta_norm_conv()) pt = pt.implies_elim(*pts) assert len(pt.prop.get_stvars()) == 0, "apply_theorem: unmatched type variables." vars = pt.prop.get_svars() for v in reversed(vars): pt = pt.forall_intr(v) return pt
def eval(self, args, prevs): if self.with_inst: name, inst = args else: name = args inst = Inst() th = theory.get_theorem(name) As, C = th.prop.strip_implies() assert len(prevs) <= len(As), "apply_theorem: too many prevs." # First attempt to match type variables svars = th.prop.get_svars() for v in svars: if v.name in inst: v.T.match_incr(inst[v.name].get_type(), inst.tyinst) pats = As[:len(prevs)] ts = [prev_th.prop for prev_th in prevs] inst = matcher.first_order_match_list(pats, ts, inst) As, C = th.prop.subst_norm(inst).strip_implies() new_prop = Implies(*(As[len(prevs):] + [C])) prev_hyps = sum([prev.hyps for prev in prevs], ()) th = Thm(th.hyps + prev_hyps, new_prop) assert len(new_prop.get_stvars()) == 0, "apply_theorem: unmatched type variables." vars = new_prop.get_svars() for v in reversed(vars): th = Thm.forall_intr(v, th) return th
def get_proof(self): invC = Const("inv", TFun(gcl.stateT, BoolType)) transC = Const("trans", TFun(gcl.stateT, gcl.stateT, BoolType)) s1 = Var("s1", gcl.stateT) s2 = Var("s2", gcl.stateT) prop = Thm([], Implies(invC(s1), transC(s1, s2), invC(s2))) # print(printer.print_thm(prop)) trans_pt = ProofTerm.assume(transC(s1, s2)) # print(printer.print_thm(trans_pt.th)) P = Implies(invC(s1), invC(s2)) ind_pt = apply_theorem("trans_cases", inst=Inst(a1=s1, a2=s2, P=P)) # print(printer.print_thm(ind_pt.th)) ind_As, ind_C = ind_pt.prop.strip_implies() for ind_A in ind_As[1:-1]: # print("ind_A: ", ind_A) vars, As, C = logic.strip_all_implies(ind_A, ["s", "k"]) # for A in As: # print("A: ", A) # print("C: ", C) eq1 = ProofTerm.assume(As[0]) eq2 = ProofTerm.assume(As[1]) guard = ProofTerm.assume(As[2]) inv_pre = ProofTerm.assume(As[3]).on_arg(rewr_conv(eq1)).on_prop( rewr_conv("inv_def")) C_goal = ProofTerm.assume(C).on_arg(rewr_conv(eq2)).on_prop( rewr_conv("inv_def"))
def get_proof_term(self, goal, pts): """Goal is of the form A_1 | ... | A_m --> B_1 | ...| B_n, where {A_1, ..., A_m} is a subset of {B_1, ..., B_n}.""" # Dictionary from B_i to B_i --> B_1 | ... | B_n pts_B = dict() # Fills up pts_B. def traverse_C(pt): if pt.prop.arg1.is_disj(): pt1 = apply_theorem('disjI1', concl=pt.prop.arg1) pt2 = apply_theorem('disjI2', concl=pt.prop.arg1) traverse_C(apply_theorem('syllogism', pt1, pt)) traverse_C(apply_theorem('syllogism', pt2, pt)) else: pts_B[pt.prop.arg1] = pt # Use pts_B to prove the implication def traverse_A(t): if t.is_disj(): pt1 = traverse_A(t.arg1) pt2 = traverse_A(t.arg) return apply_theorem('disjE2', pt1, pt2) else: assert t in pts_B, "imp_disj: %s not found in conclusion" % t return pts_B[t] triv = apply_theorem('trivial', inst=Inst(A=goal.arg)) traverse_C(triv) return traverse_A(goal.arg1)
def first_order_match_list(pats, ts, inst=None): """First-order matching of a list of pattern-term pairs. inst : optinal Inst Existing instantiation. Default to empty instantiation. """ if inst is None: inst = Inst() else: inst = copy(inst) if len(pats) == 0: return inst if len(pats) == 1: return first_order_match(pats[0], ts[0], inst) if is_pattern(pats[0], list(inst.keys())): inst = first_order_match(pats[0], ts[0], inst) inst = first_order_match_list(pats[1:], ts[1:], inst) return inst else: inst = first_order_match_list(pats[1:], ts[1:], inst) inst = first_order_match(pats[0], ts[0], inst) return inst
def testProofItem(self): test_data = [ (ProofItem(0, "theorem", args="conjD1"), "0: theorem conjD1", { 'id': 0, 'th': '', 'rule': 'theorem', 'args': 'conjD1', 'prevs': [] }), (ProofItem(1, "assume", args=A_to_B), "1: assume implies A B", { 'id': 1, 'th': '', 'rule': 'assume', 'args': 'implies A B', 'prevs': [] }), (ProofItem(5, "substitution", args=Inst(A=B, B=A), prevs=[4]), "5: substitution {A: B, B: A} from 4", { 'id': 5, 'th': '', 'rule': 'substitution', 'args': '{A: B, B: A}', 'prevs': [4] }), (ProofItem(6, "implies_elim", prevs=[5, 3]), "6: implies_elim from 5, 3", { 'id': 6, 'th': '', 'rule': 'implies_elim', 'args': '', 'prevs': [5, 3] }), (ProofItem(1, "apply_theorem_for", args=("conjD2", Inst(A=B, B=A)), prevs=[0]), "1: apply_theorem_for conjD2, {A: B, B: A} from 0", { 'id': 1, 'th': '', 'rule': 'apply_theorem_for', 'args': 'conjD2, {A: B, B: A}', 'prevs': [0] }) ] for item, s, d in test_data: self.assertEqual(str(item), s)
def ineq_one_proof_term(n): """Returns the inequality n ~= 1.""" assert n != 1, "ineq_one_proof_term: n = 1" if n == 0: return apply_theorem("ineq_sym", ProofTerm.theorem("one_nonzero")) elif n % 2 == 0: return apply_theorem("bit0_neq_one", inst=Inst(m=Binary(n // 2))) else: return apply_theorem("bit1_neq_one", ineq_zero_proof_term(n // 2))
def ineq_zero_proof_term(n): """Returns the inequality n ~= 0.""" assert n != 0, "ineq_zero_proof_term: n = 0" if n == 1: return ProofTerm.theorem("one_nonzero") elif n % 2 == 0: return apply_theorem("bit0_nonzero", ineq_zero_proof_term(n // 2)) else: return apply_theorem("bit1_nonzero", inst=Inst(m=Binary(n // 2)))
def get_proof_term(self, goal, *, args=None, prevs=None): assert isinstance(args, Term), "inst_exists_goal" C = goal.prop assert C.is_exists(), "inst_exists_goal: goal is not exists statement" argT = args.get_type() assert C.arg.var_T == argT, "inst_exists_goal: incorrect type: expect %s, given %s" % ( str(C.arg.var_T), str(argT)) return rule().get_proof_term(goal, args=('exI', Inst(P=C.arg, a=args)))
def get_proof_term(self, goal, *, args=None, prevs=None): if isinstance(args, tuple): th_name, inst = args else: th_name, inst = args, None assert isinstance(th_name, str), "rule: theorem name must be a string" if prevs is None: prevs = [] th = theory.get_theorem(th_name) As, C = th.assums, th.concl # Length of prevs is at most length of As assert len(prevs) <= len(As), "rule: too many previous facts" if inst is None: inst = Inst() # Match the conclusion and assumptions. Either the conclusion # or the list of assumptions must be a first-order pattern. if matcher.is_pattern(C, []): inst = matcher.first_order_match(C, goal.prop, inst) for pat, prev in zip(As, prevs): inst = matcher.first_order_match(pat, prev.prop, inst) else: for pat, prev in zip(As, prevs): inst = matcher.first_order_match(pat, prev.prop, inst) inst = matcher.first_order_match(C, goal.prop, inst) # Check that every variable in the theorem has an instantiation. unmatched_vars = [ v.name for v in term.get_svars(As + [C]) if v.name not in inst ] if unmatched_vars: raise theory.ParameterQueryException( list("param_" + name for name in unmatched_vars)) # Substitute and normalize As, _ = th.prop.subst_norm(inst).strip_implies() goal_Alen = len(goal.assums) if goal_Alen > 0: As = As[:-goal_Alen] pts = prevs + [ ProofTerm.sorry(Thm(goal.hyps, A)) for A in As[len(prevs):] ] # Determine whether it is necessary to provide instantiation # to apply_theorem. if set(term.get_svars(th.assums)) != set(th.prop.get_svars()) or \ set(term.get_stvars(th.assums)) != set(th.prop.get_stvars()) or \ not matcher.is_pattern_list(th.assums, []): return apply_theorem(th_name, *pts, inst=inst) else: return apply_theorem(th_name, *pts)
def testSubst(self): test_data = [ (SVar('a', Ta), c), (c, c), (f(SVar('a', Ta)), f(c)), (Abs("x", Ta, B0), Abs("x", Ta, B0)), (Abs("x", Ta, SVar('a', Ta)), Abs("x", Ta, c)), ] for t, res in test_data: self.assertEqual(t.subst(Inst(a=c)), res)
def apply(self, state, id, data, prevs): inst = Inst() with context.fresh_context(vars=state.get_vars(id)): for key, val in data.items(): if key.startswith("param_"): inst[key[6:]] = parser.parse_term(val) if inst: state.apply_tactic(id, tactic.apply_prev(), args=inst, prevs=prevs) else: state.apply_tactic(id, tactic.apply_prev(), prevs=prevs)
def testRule4(self): n = Var("n", NatType) self.run_test('nat', tactic.rule(), vars={"n": "nat"}, goal="n + 0 = n", args=("nat_induct", Inst(P=Lambda(n, Eq(n + 0, n)), x=n)), new_goals=[ "(0::nat) + 0 = 0", "!n. n + 0 = n --> Suc n + 0 = Suc n" ])
def testCheckProof4(self): """Proof of |- x = y --> x = y by instantiating an existing theorem.""" theory.thy.add_theorem("trivial", Thm([], Implies(A,A))) x_eq_y = Eq(x,y) prf = Proof() prf.add_item(0, "theorem", args="trivial") prf.add_item(1, "substitution", args=Inst(A=x_eq_y), prevs=[0]) rpt = ProofReport() th = Thm([], Implies(x_eq_y,x_eq_y)) self.assertEqual(theory.check_proof(prf, rpt), th) self.assertEqual(rpt.steps, 2)
def run_test(self, thy_name, pat, t, *, vars=None, svars=None, tyinst=None, inst=None, failed=None): context.set_context(thy_name, vars=vars, svars=svars) pat, t = Term(pat), Term(t) inst = Inst((nm, Term(s)) for nm, s in inst.items()) if inst is not None else Inst() if tyinst is not None: inst.tyinst = TyInst((nm, Type(s)) for nm, s in tyinst.items()) if failed is not None: self.assertRaises(failed, first_order_match, pat, t) return self.assertEqual(first_order_match(pat, t), inst)
def get_proof_term(self, t): # If self.eq_pt is not present, produce it from thy, self.pt # and self.sym. Decompose into self.As and self.C. if self.eq_pt is None: if isinstance(self.pt, str): self.eq_pt = ProofTerm.theorem(self.pt) else: self.eq_pt = self.pt self.As, self.C = self.eq_pt.prop.strip_implies() # The conclusion of eq_pt should be an equality, and the number of # assumptions in eq_pt should match number of conditions. assert self.C.is_equals(), "rewr_conv: theorem is not an equality." if len(self.As) != len(self.conds): raise ConvException("rewr_conv: number of conds does not agree") inst = Inst() ts = [cond.prop for cond in self.conds] if not self.sym: lhs = self.C.lhs else: lhs = self.C.rhs try: inst = matcher.first_order_match_list(self.As, ts, inst) inst = matcher.first_order_match(lhs, t, inst) except matcher.MatchException: raise ConvException("rewr_conv: cannot match left side") # Check that every variable in the theorem has an instantiation if set(term.get_svars(self.As + [lhs])) != set( term.get_svars(self.As + [self.C])): raise ConvException("rewr_conv: unmatched vars") pt = self.eq_pt pt = pt.substitution(inst) pt = pt.implies_elim(*self.conds) if self.sym: pt = pt.symmetric() assert pt.th.is_equals(), "rewr_conv: wrong result." if pt.th.prop.lhs != t: pt = pt.on_prop(beta_norm_conv()) if pt.th.prop.lhs != t: pt = pt.on_prop(eta_conv()) assert pt.th.prop.lhs == t, "rewr_conv: wrong result. Expected %s, got %s" % ( str(t), str(pt.th.prop.lhs)) return pt
def testCheckProof5(self): """Empty instantiation.""" theory.thy.add_theorem("trivial", Thm([], Implies(A,A))) x_eq_y = Eq(x,y) prf = Proof() prf.add_item(0, "theorem", args="trivial") prf.add_item(1, "substitution", args=Inst(), prevs=[0]) rpt = ProofReport() th = Thm([], Implies(SVar('A', BoolType), SVar('A', BoolType))) self.assertEqual(theory.check_proof(prf, rpt), th) self.assertEqual(rpt.steps_stat(), (1, 1, 0)) self.assertEqual(rpt.th_names, {"trivial"})
def get_proof_term(self, args, pts): if not self.with_inst: assert len(pts) >= 2, "apply fact: too few prevs" pt, pt_prevs = pts[0], pts[1:] # First, obtain the patterns new_names = get_forall_names(pt.prop) new_vars, As, C = strip_all_implies(pt.prop, new_names) assert len(pt_prevs) <= len(As), "apply_fact: too many prevs" if self.with_inst: assert len(args) == len(new_names), "apply_fact_macro: wrong number of args." inst = Inst({nm: v for nm, v in zip(new_names, args)}) else: inst = Inst() for idx, pt_prev in enumerate(pt_prevs): inst = matcher.first_order_match(As[idx], pt_prev.prop, inst) pt = pt.subst_type(inst.tyinst) for new_var in new_vars: if new_var.name in inst: pt = pt.forall_elim(inst[new_var.name]) else: pt = pt.forall_elim(new_var) if pt.prop.beta_norm() != pt.prop: pt = pt.on_prop(beta_norm_conv()) for prev_pt in pt_prevs: if prev_pt.prop != pt.assums[0]: prev_pt = prev_pt.on_prop(beta_norm_conv()) pt = pt.implies_elim(prev_pt) for new_var in new_vars: if new_var.name not in inst: pt = pt.forall_intr(new_var) return pt
def compute_wp(T, c, Q): """Compute the weakest precondition for the given command and postcondition. Here c is the program and Q is the postcondition. The computation is by case analysis on the form of c. The function returns a proof term showing [...] |- Valid P c Q, where P is the computed precondition, and [...] contains the additional subgoals. """ if c.is_const("Skip"): # Skip return apply_theorem("skip_rule", concl=Valid(T)(Q, c, Q)) elif c.is_comb("Assign", 2): # Assign a b a, b = c.args s = Var("s", T) P2 = Lambda(s, Q(function.mk_fun_upd(s, a, b(s).beta_conv()))) return apply_theorem("assign_rule", inst=Inst(b=b), concl=Valid(T)(P2, c, Q)) elif c.is_comb("Seq", 2): # Seq c1 c2 c1, c2 = c.args wp1 = compute_wp(T, c2, Q) # Valid Q' c2 Q wp2 = compute_wp(T, c1, wp1.prop.args[0]) # Valid Q'' c1 Q' return apply_theorem("seq_rule", wp2, wp1) elif c.is_comb("Cond", 3): # Cond b c1 c2 b, c1, c2 = c.args wp1 = compute_wp(T, c1, Q) wp2 = compute_wp(T, c2, Q) res = apply_theorem("if_rule", wp1, wp2, inst=Inst(b=b)) return res elif c.is_comb("While", 3): # While b I c _, I, _ = c.args pt = apply_theorem("while_rule", concl=Valid(T)(I, c, Q)) pt0 = ProofTerm.assume(pt.assums[0]) pt1 = vcg(T, pt.assums[1]) return pt.implies_elim(pt0, pt1) else: raise NotImplementedError
def has_rewrite(th, t, *, sym=False, conds=None): """Returns whether a rewrite is possible on a subterm of t. This can serve as a pre-check for top_sweep_conv, top_conv, and bottom_conv applied to rewr_conv. th -- either the name of a theorem, or the theorem itself. t -- target of rewriting. conds -- optional list of theorems matching assumptions of th. """ if isinstance(th, str): th = theory.get_theorem(th) if sym: th = Thm.symmetric(th) As, C = th.prop.strip_implies() if conds is None: conds = [] if not C.is_equals() or len(As) != len(conds): return False if set(term.get_svars(As + [C.lhs])) != set(term.get_svars(As + [C])): return False ts = [cond.prop for cond in conds] inst = Inst() try: inst = matcher.first_order_match_list(As, ts, inst) except matcher.MatchException: return False def rec(t): if not t.is_open() and matcher.can_first_order_match(C.lhs, t, inst): return True if t.is_comb(): return rec(t.fun) or rec(t.arg) elif t.is_abs(): _, body = t.dest_abs() return rec(body) else: return False return rec(t)
def apply(self, state, id, data, prevs): inst = Inst() with context.fresh_context(vars=state.get_vars(id)): for key, val in data.items(): if key.startswith("param_"): if val != '': inst[key[6:]] = parser.parse_term(val) th = theory.get_theorem(data['theorem']) # Check whether to ask for parameters As, C = th.prop.strip_implies() match_svars = term.get_svars(As[:len(prevs)]) all_svars = th.prop.get_svars() param_svars = [ v for v in all_svars if v not in match_svars and 'param_' + v.name not in data ] if param_svars: raise theory.ParameterQueryException( list("param_" + v.name for v in param_svars)) # First test apply_theorem prev_ths = [state.get_proof_item(prev).th for prev in prevs] macro = logic.apply_theorem_macro(with_inst=True) res_th = macro.eval((data['theorem'], inst), prev_ths) state.add_line_before(id, 1) if inst: state.set_line(id, 'apply_theorem_for', args=(data['theorem'], inst), prevs=prevs) else: state.set_line(id, 'apply_theorem', args=data['theorem'], prevs=prevs) id2 = id.incr_id(1) new_id = state.find_goal(state.get_proof_item(id2).th, id2) if new_id is not None: state.replace_id(id2, new_id)
def ineq_proof_term(m, n): """Returns the inequality m ~= n.""" assert m != n, "ineq_proof_term: m = n" if n == 0: return ineq_zero_proof_term(m) elif n == 1: return ineq_one_proof_term(m) elif m == 0: return apply_theorem("ineq_sym", ineq_zero_proof_term(n)) elif m == 1: return apply_theorem("ineq_sym", ineq_one_proof_term(n)) elif m % 2 == 0 and n % 2 == 0: return apply_theorem("bit0_neq", ineq_proof_term(m // 2, n // 2)) elif m % 2 == 1 and n % 2 == 1: return apply_theorem("bit1_neq", ineq_proof_term(m // 2, n // 2)) elif m % 2 == 0 and n % 2 == 1: return apply_theorem("bit0_bit1_neq", inst=Inst(m=Binary(m // 2), n=Binary(n // 2))) else: return apply_theorem("ineq_sym", ineq_proof_term(n, m))
def apply_theorem(th_name, *pts, concl=None, inst=None): """Wrapper for apply_theorem and apply_theorem_for macros. The function takes optional arguments concl, inst. Matching always starts with inst. If conclusion is specified, it is matched next. Finally, the assumptions are matched. """ typecheck.checkinstance('apply_theorem', pts, [ProofTerm]) if concl is None and inst is None: # Normal case, can use apply_theorem return ProofTerm("apply_theorem", th_name, pts) else: pt = ProofTerm.theorem(th_name) if inst is None: inst = Inst() if concl is not None: inst = matcher.first_order_match(pt.concl, concl, inst) for i, prev in enumerate(pts): inst = matcher.first_order_match(pt.assums[i], prev.prop, inst) return ProofTerm("apply_theorem_for", (th_name, inst), pts)
def get_proof_term(self, goal, *, args=None, prevs=None): assert isinstance(prevs, list) and len(prevs) >= 1, "apply_prev" pt, prev_pts = prevs[0], prevs[1:] # First, obtain the patterns new_names = logic.get_forall_names(pt.prop) new_vars, As, C = logic.strip_all_implies(pt.prop, new_names) assert len(prev_pts) <= len(As), "apply_prev: too many prev_pts" if args is None: inst = Inst() else: inst = args inst = matcher.first_order_match(C, goal.prop, inst) for idx, prev_pt in enumerate(prev_pts): inst = matcher.first_order_match(As[idx], prev_pt.prop, inst) unmatched_vars = [v for v in new_names if v not in inst] if unmatched_vars: raise theory.ParameterQueryException( list("param_" + name for name in unmatched_vars)) pt = pt.subst_type(inst.tyinst) for new_name in new_names: pt = pt.forall_elim(inst[new_name]) if pt.prop.beta_norm() != pt.prop: pt = pt.on_prop(beta_norm_conv()) inst_As, inst_C = pt.prop.strip_implies() inst_arg = [inst[new_name] for new_name in new_names] new_goals = [ ProofTerm.sorry(Thm(goal.hyps, A)) for A in inst_As[len(prev_pts):] ] if set(new_names).issubset({v.name for v in term.get_vars(As)}) and \ matcher.is_pattern_list(As, []): return ProofTerm('apply_fact', args=None, prevs=prevs + new_goals) else: return ProofTerm('apply_fact_for', args=inst_arg, prevs=prevs + new_goals)
def get_bounds_proof(t, var_range): """Given a term t and a mapping from variables to intervals, return a theorem for t belonging to an interval. t - Term, a HOL expression. var_range - dict(str, Thm): mapping from variables x to theorems of the form x Mem [a, b] or x Mem (a, b). Returns a theorem of the form t Mem [a, b] or t Mem (a, b). """ if t.ty == Term.VAR: assert t.name in var_range, "get_bounds_proof: variable %s not found" % t.name return var_range[t.name] elif t.is_number() or t == real.pi: return apply_theorem('const_interval', inst=Inst(x=t)) elif t.is_plus(): pt1 = get_bounds_proof(t.arg1, var_range) pt2 = get_bounds_proof(t.arg, var_range) if is_mem_closed(pt1) and is_mem_closed(pt2): pt = apply_theorem('add_interval_closed', pt1, pt2) elif is_mem_open(pt1) and is_mem_open(pt2): pt = apply_theorem('add_interval_open', pt1, pt2) elif is_mem_open(pt1) and is_mem_closed(pt2): pt = apply_theorem('add_interval_open_closed', pt1, pt2) elif is_mem_closed(pt1) and is_mem_open(pt2): pt = apply_theorem('add_interval_closed_open', pt1, pt2) elif is_mem_closed(pt1) and is_mem_lopen(pt2): pt = apply_theorem('add_interval_closed_lopen', pt1, pt2) elif is_mem_closed(pt1) and is_mem_ropen(pt2): pt = apply_theorem('add_interval_closed_ropen', pt1, pt2) elif is_mem_ropen(pt1) and is_mem_closed(pt2): pt = apply_theorem('add_interval_closed_ropen', pt2, pt1) pt = pt.on_prop(arg1_conv(rewr_conv('real_add_comm'))) else: raise NotImplementedError('get_bounds: %s, %s' % (pt1, pt2)) return norm_mem_interval(pt) elif t.is_uminus(): pt = get_bounds_proof(t.arg, var_range) if is_mem_closed(pt): pt = apply_theorem('neg_interval_closed', pt) elif is_mem_open(pt): pt = apply_theorem('neg_interval_open', pt) else: raise NotImplementedError return norm_mem_interval(pt) elif t.is_minus(): rewr_t = t.arg1 + (-t.arg) pt = get_bounds_proof(rewr_t, var_range) return pt.on_prop(arg1_conv(rewr_conv('real_minus_def', sym=True))) elif t.is_real_inverse(): pt = get_bounds_proof(t.arg, var_range) a, b = get_mem_bounds(pt) if eval_hol_expr(a) > 0 and is_mem_closed(pt): pt = apply_theorem('inverse_interval_pos_closed', auto.auto_solve(real_pos(a)), pt) else: raise NotImplementedError return norm_mem_interval(pt) elif t.is_times(): if t.arg1.has_var(): pt1 = get_bounds_proof(t.arg1, var_range) pt2 = get_bounds_proof(t.arg, var_range) a, b = get_mem_bounds(pt1) c, d = get_mem_bounds(pt2) if eval_hol_expr(a) >= 0 and eval_hol_expr(c) >= 0 and is_mem_open( pt1) and is_mem_open(pt2): pt = apply_theorem('mult_interval_pos_pos_open', auto.auto_solve(real_nonneg(a)), auto.auto_solve(real_nonneg(c)), pt1, pt2) elif eval_hol_expr(a) >= 0 and eval_hol_expr( c) >= 0 and is_mem_closed(pt1) and is_mem_closed(pt2): pt = apply_theorem('mult_interval_pos_pos_closed', auto.auto_solve(real_nonneg(a)), auto.auto_solve(real_nonneg(c)), pt1, pt2) elif eval_hol_expr(a) >= 0 and eval_hol_expr( c) >= 0 and is_mem_lopen(pt1) and is_mem_ropen(pt2): pt = apply_theorem('mult_interval_pos_pos_lopen_ropen', auto.auto_solve(real_nonneg(a)), auto.auto_solve(real_nonneg(c)), pt1, pt2) elif eval_hol_expr(b) <= 0 and eval_hol_expr( c) >= 0 and is_mem_open(pt1) and is_mem_open(pt2): pt = apply_theorem('mult_interval_neg_pos_open', auto.auto_solve(real_nonpos(b)), auto.auto_solve(real_nonneg(c)), pt1, pt2) else: raise NotImplementedError('get_bounds: %s, %s' % (pt1, pt2)) else: pt = get_bounds_proof(t.arg, var_range) a, b = get_mem_bounds(pt) c = t.arg1 nc = eval_hol_expr(c) if nc >= 0 and is_mem_closed(pt): pt = apply_theorem('mult_interval_pos_closed', auto.auto_solve(real_nonneg(c)), pt) elif nc >= 0 and is_mem_open(pt): pt = apply_theorem('mult_interval_pos_open', auto.auto_solve(real_nonneg(c)), pt) elif nc >= 0 and is_mem_lopen(pt): pt = apply_theorem('mult_interval_pos_lopen', auto.auto_solve(real_nonneg(c)), pt) elif nc >= 0 and is_mem_ropen(pt): pt = apply_theorem('mult_interval_pos_ropen', auto.auto_solve(real_nonneg(c)), pt) elif nc < 0 and is_mem_closed(pt): pt = apply_theorem('mult_interval_neg_closed', auto.auto_solve(real_neg(c)), pt) elif nc < 0 and is_mem_open(pt): pt = apply_theorem('mult_interval_neg_open', auto.auto_solve(real_neg(c)), pt) elif nc < 0 and is_mem_lopen(pt): pt = apply_theorem('mult_interval_neg_lopen', auto.auto_solve(real_neg(c)), pt) elif nc < 0 and is_mem_ropen(pt): pt = apply_theorem('mult_interval_neg_ropen', auto.auto_solve(real_neg(c)), pt) else: raise NotImplementedError return norm_mem_interval(pt) elif t.is_divides(): rewr_t = t.arg1 * (real.inverse(t.arg)) pt = get_bounds_proof(rewr_t, var_range) return pt.on_prop(arg1_conv(rewr_conv('real_divide_def', sym=True))) elif t.is_nat_power(): pt = get_bounds_proof(t.arg1, var_range) if not t.arg.is_number(): raise NotImplementedError return get_nat_power_bounds(pt, t.arg) elif t.is_real_power(): pt = get_bounds_proof(t.arg1, var_range) a, b = get_mem_bounds(pt) if not t.arg.is_number(): raise NotImplementedError p = t.arg.dest_number() if p >= 0 and eval_hol_expr(a) >= 0: nonneg_p = auto.auto_solve(real_nonneg(t.arg)) nonneg_a = auto.auto_solve(real_nonneg(a)) if is_mem_closed(pt): pt = apply_theorem('real_power_interval_pos_closed', nonneg_p, nonneg_a, pt) elif is_mem_open(pt): pt = apply_theorem('real_power_interval_pos_open', nonneg_p, nonneg_a, pt) else: raise NotImplementedError elif p < 0: neg_p = auto.auto_solve(real_neg(t.arg)) if is_mem_closed(pt) and eval_hol_expr(a) > 0: pos_a = auto.auto_solve(real_pos(a)) pt = apply_theorem('real_power_interval_neg_closed', neg_p, pos_a, pt) elif is_mem_open(pt): nonneg_a = auto.auto_solve(real_nonneg(a)) pt = apply_theorem('real_power_interval_neg_open', neg_p, nonneg_a, pt) elif is_mem_lopen(pt): nonneg_a = auto.auto_solve(real_nonneg(a)) pt = apply_theorem('real_power_interval_neg_lopen', neg_p, nonneg_a, pt) else: print(pt) raise NotImplementedError else: raise NotImplementedError return norm_mem_interval(pt) elif t.head == real.log: pt = get_bounds_proof(t.arg, var_range) a, b = get_mem_bounds(pt) if eval_hol_expr(a) > 0 and is_mem_closed(pt): pt = apply_theorem('log_interval_closed', auto.auto_solve(real_pos(a)), pt) elif eval_hol_expr(a) >= 0 and is_mem_open(pt): pt = apply_theorem('log_interval_open', auto.auto_solve(real_nonneg(a)), pt) else: raise NotImplementedError return norm_mem_interval(pt) elif t.head == real.exp: pt = get_bounds_proof(t.arg, var_range) if is_mem_closed(pt): pt = apply_theorem('exp_interval_closed', pt) elif is_mem_open(pt): pt = apply_theorem('exp_interval_open', pt) else: raise NotImplementedError return norm_mem_interval(pt) elif t.head == real.sin: pt = get_bounds_proof(t.arg, var_range) a, b = get_mem_bounds(pt) if eval_hol_expr(a) >= -math.pi / 2 and eval_hol_expr( b) <= math.pi / 2: if is_mem_closed(pt): pt = apply_theorem( 'sin_interval_main_closed', auto.auto_solve(real.greater_eq(a, -real.pi / 2)), auto.auto_solve(real.less_eq(b, real.pi / 2)), pt) elif is_mem_open(pt): pt = apply_theorem( 'sin_interval_main_open', auto.auto_solve(real.greater_eq(a, -real.pi / 2)), auto.auto_solve(real.less_eq(b, real.pi / 2)), pt) else: raise NotImplementedError elif eval_hol_expr(a) >= 0 and eval_hol_expr(b) <= math.pi: if is_mem_closed(pt): pt = apply_theorem('sin_interval_pos_closed', auto.auto_solve(real_nonneg(a)), auto.solve(real.less_eq(b, real.pi)), pt) elif is_mem_open(pt): pt = apply_theorem('sin_interval_pos_open', auto.auto_solve(real_nonneg(a)), auto.solve(real.less_eq(b, real.pi)), pt) else: raise NotImplementedError else: raise NotImplementedError return norm_mem_interval(pt) elif t.head == real.cos: pt = get_bounds_proof(t.arg, var_range) a, b = get_mem_bounds(pt) if eval_hol_expr(a) >= 0 and eval_hol_expr(b) <= math.pi: if is_mem_closed(pt): pt = apply_theorem('cos_interval_main_closed', auto.auto_solve(real_nonneg(a)), auto.auto_solve(real.less_eq(b, real.pi)), pt) elif is_mem_open(pt): pt = apply_theorem('cos_interval_main_open', auto.auto_solve(real_nonneg(a)), auto.auto_solve(real.less_eq(b, real.pi)), pt) else: raise NotImplementedError elif eval_hol_expr(a) >= -math.pi / 2 and eval_hol_expr( b) <= math.pi / 2: if is_mem_closed(pt): pt = apply_theorem( 'cos_interval_pos_closed', auto.auto_solve(real.greater_eq(a, -real.pi / 2)), auto.auto_solve(real.less_eq(b, real.pi / 2)), pt) elif is_mem_open(pt): pt = apply_theorem( 'cos_interval_pos_open', auto.auto_solve(real.greater_eq(a, -real.pi / 2)), auto.auto_solve(real.less_eq(b, real.pi / 2)), pt) else: raise NotImplementedError else: raise NotImplementedError return norm_mem_interval(pt) elif t.head == real.atn: pt = get_bounds_proof(t.arg, var_range) if is_mem_closed(pt): pt = apply_theorem('atn_interval_closed', pt) elif is_mem_open(pt): pt = apply_theorem('atn_interval_open', pt) else: raise NotImplementedError return norm_mem_interval(pt) else: print("Cannot deal with", t) raise NotImplementedError
def get_nat_power_bounds(pt, n): """Given theorem of the form t Mem I, obtain a theorem of the form t ^ n Mem J. """ a, b = get_mem_bounds(pt) if not n.is_number(): raise NotImplementedError if eval_hol_expr(a) >= 0 and is_mem_closed(pt): pt = apply_theorem('nat_power_interval_pos_closed', auto.auto_solve(real_nonneg(a)), pt, inst=Inst(n=n)) elif eval_hol_expr(a) >= 0 and is_mem_open(pt): pt = apply_theorem('nat_power_interval_pos_open', auto.auto_solve(real_nonneg(a)), pt, inst=Inst(n=n)) elif eval_hol_expr(a) >= 0 and is_mem_lopen(pt): pt = apply_theorem('nat_power_interval_pos_lopen', auto.auto_solve(real_nonneg(a)), pt, inst=Inst(n=n)) elif eval_hol_expr(a) >= 0 and is_mem_ropen(pt): pt = apply_theorem('nat_power_interval_pos_ropen', auto.auto_solve(real_nonneg(a)), pt, inst=Inst(n=n)) elif eval_hol_expr(b) <= 0 and is_mem_closed(pt): int_n = n.dest_number() if int_n % 2 == 0: even_pt = nat_as_even(int_n) pt = apply_theorem('nat_power_interval_neg_even_closed', auto.auto_solve(real_nonpos(b)), even_pt, pt) else: odd_pt = nat_as_odd(int_n) pt = apply_theorem('nat_power_interval_neg_odd_closed', auto.auto_solve(real_nonpos(b)), odd_pt, pt) elif eval_hol_expr(b) <= 0 and is_mem_open(pt): int_n = n.dest_number() if int_n % 2 == 0: even_pt = nat_as_even(int_n) pt = apply_theorem('nat_power_interval_neg_even_open', auto.auto_solve(real_nonpos(b)), even_pt, pt) else: odd_pt = nat_as_odd(int_n) pt = apply_theorem('nat_power_interval_neg_odd_open', auto.auto_solve(real_nonpos(b)), odd_pt, pt) elif is_mem_closed(pt): # Closed interval containing 0 t = pt.prop.arg1 assm1 = hol_set.mk_mem(t, real.closed_interval(a, Real(0))) assm2 = hol_set.mk_mem(t, real.closed_interval(Real(0), b)) pt1 = get_nat_power_bounds(ProofTerm.assume(assm1), n).implies_intr(assm1) pt2 = get_nat_power_bounds(ProofTerm.assume(assm2), n).implies_intr(assm2) x = Var('x', RealType) pt = apply_theorem('split_interval_closed', auto.auto_solve(real.less_eq(a, Real(0))), auto.auto_solve(real.less_eq(Real(0), b)), pt1, pt2, pt, inst=Inst(x=t, f=Lambda(x, x**n))) subset_pt = interval_union_subset(pt.prop.arg) pt = apply_theorem('subsetE', subset_pt, pt) elif is_mem_open(pt): # Open interval containing 0 t = pt.prop.arg1 assm1 = hol_set.mk_mem(t, real.open_interval(a, Real(0))) assm2 = hol_set.mk_mem(t, real.ropen_interval(Real(0), b)) pt1 = get_nat_power_bounds(ProofTerm.assume(assm1), n).implies_intr(assm1) pt2 = get_nat_power_bounds(ProofTerm.assume(assm2), n).implies_intr(assm2) x = Var('x', RealType) pt = apply_theorem('split_interval_open', auto.auto_solve(real.less_eq(a, Real(0))), auto.auto_solve(real.less_eq(Real(0), b)), pt1, pt2, pt, inst=Inst(x=t, f=Lambda(x, x**n))) subset_pt = interval_union_subset(pt.prop.arg) pt = apply_theorem('subsetE', subset_pt, pt) else: raise NotImplementedError return norm_mem_interval(pt)
def nat_as_odd(n): """Obtain theorem of form odd n.""" assert n % 2 == 1, "nat_as_odd: n is not odd" eq_pt = auto.auto_solve(Eq(nat.Suc(Nat(2) * Nat(n // 2)), Nat(n))) pt = apply_theorem('odd_double', inst=Inst(n=Nat(n // 2))) return pt.on_prop(arg_conv(rewr_conv(eq_pt)))