def search(self, state, id, prevs): prev_ths = [state.get_proof_item(prev).th for prev in prevs] thy = state.thy if len(prevs) == 0: return [] results = [] for name, th in thy.get_data("theorems").items(): if 'hint_forward' not in thy.get_attributes(name): continue instsp = (dict(), dict()) As, C = th.prop.strip_implies() if len(prevs) != len(As): continue if set(term.get_vars(As)) != set(term.get_vars(As + [C])): continue if not term.get_consts(As): continue try: for pat, prev in zip(As, prev_ths): matcher.first_order_match_incr(pat, prev.concl, instsp) except matcher.MatchException: continue # All matches succeed t = logic.subst_norm(th.prop, instsp) _, new_fact = t.strip_implies() results.append({"theorem": name, "_fact": [new_fact]}) return sorted(results, key=lambda d: d['theorem'])
def is_pattern_list(ts, matched_vars): """Test whether a list of ts can be matched.""" if len(ts) == 0: return True elif len(ts) == 1: return is_pattern(ts[0], matched_vars) else: if is_pattern(ts[0], matched_vars): all_vars = list(set(matched_vars + term.get_vars(ts[0]))) return is_pattern_list(ts[1:], all_vars) elif is_pattern_list(ts[1:], matched_vars): all_vars = list(set(matched_vars + term.get_vars(ts[1:]))) return is_pattern(ts[0], all_vars) else: return False
def solve_core(s, t, debug=False): # First strip foralls from t. t = norm_term(t) new_names = logic.get_forall_names(t, svar=False) _, As, C = logic.strip_all_implies(t, new_names, svar=False) def print_debug(*args): if debug: print(*args) var_names = [v.name for v in term.get_vars(As + [C])] assms = dict() to_real = dict() for A in As: try: z3_A = convert(A, var_names, assms, to_real, s.ctx) print_debug('A', z3_A) s.add(z3_A) except Z3Exception as e: print_debug(e) try: z3_C = convert(C, var_names, assms, to_real, s.ctx) print_debug('C', z3_C) s.add(z3.Not(z3_C)) except Z3Exception as e: print_debug(e) for nm, A in assms.items(): print_debug('A', A) s.add(A) return s
def testGetVars(self): test_data = [ (a, [a]), (f(a), [a, f]), (f(c), [f]), ([a, f(c)], [a, f]), ] for t, res in test_data: self.assertEqual(term.get_vars(t), res)
def search(self, state, id, prevs): goal_th = state.get_proof_item(id).th prev_ths = [state.get_proof_item(prev).th for prev in prevs] thy = state.thy results = [] for name, th in thy.get_data("theorems").items(): if 'hint_backward' not in thy.get_attributes(name): continue instsp = (dict(), dict()) As, C = th.assums, th.concl # Only process those theorems where C and the matched As # contain all of the variables. if set(term.get_vars(As[:len(prevs)] + [C])) != set( term.get_vars(As + [C])): continue # When there is no assumptions to match, only process those # theorems where C contains at least a constant (skip falseE, # induction theorems, etc). if len(prevs) == 0 and term.get_consts(C) == []: continue try: if matcher.is_pattern(C, []): matcher.first_order_match_incr(C, goal_th.prop, instsp) for pat, prev in zip(As, prev_ths): matcher.first_order_match_incr(pat, prev.prop, instsp) else: for pat, prev in zip(As, prev_ths): matcher.first_order_match_incr(pat, prev.prop, instsp) matcher.first_order_match_incr(C, goal_th.prop, instsp) except matcher.MatchException: continue # All matches succeed t = logic.subst_norm(th.prop, instsp) As, C = t.strip_implies() results.append({"theorem": name, "_goal": As[len(prevs):]}) return sorted(results, key=lambda d: d['theorem'])
def add_induct_predicate(name, T, props): """Add the given inductive predicate. The inductive predicate is specified by the name and type of the predicate, and a list of introduction rules, where each introduction rule must be given a name. """ exts = TheoryExtension() exts.add_extension(AxConstant(name, T)) for th_name, prop in props: exts.add_extension(Theorem(th_name, Thm([], prop))) exts.add_extension(Attribute(th_name, "hint_backward")) # Case rule Targs, _ = T.strip_type() vars = [] for i, Targ in enumerate(Targs): vars.append(Var("_a" + str(i + 1), Targ)) P = Var("P", boolT) pred = Const(name, T) assum0 = pred(*vars) assums = [] for th_name, prop in props: As, C = prop.strip_implies() assert C.head == pred, "add_induct_predicate: wrong form of prop." eq_assums = [ Term.mk_equals(var, arg) for var, arg in zip(vars, C.args) ] assum = Term.mk_implies(*(eq_assums + As), P) prop_vars = term.get_vars(prop) for var in reversed(term.get_vars(prop)): assum = Term.mk_all(var, assum) assums.append(assum) prop = Term.mk_implies(*([assum0] + assums + [P])) exts.add_extension(Theorem(name + "_cases", Thm([], prop))) return exts
def add_theorem(self, name, th, prf): """Add a theorem with proof to the file.""" assert len(th.hyps) == 0, "add_theorem" vars = dict((v.name, str(v.T)) for v in get_vars(th.prop)) data = { "name": name, "ty": "thm", "prop": printer.print_term(self.thy, th.prop), "vars": vars, "num_gaps": 0, "proof": sum([self.export_proof_json(item) for item in prf.items], []), } self.content.append(data)
def get_proof_term(self, thy, goal, *, args=None, prevs=None): if isinstance(args, tuple): th_name, instsp = args else: th_name = args instsp = None assert isinstance(th_name, str), "rule: theorem name must be a string" if prevs is None: prevs = [] th = thy.get_theorem(th_name) As, C = th.assums, th.concl if instsp is None: instsp = (dict(), dict()) if matcher.is_pattern(C, []): matcher.first_order_match_incr(C, goal.prop, instsp) for pat, prev in zip(As, prevs): matcher.first_order_match_incr(pat, prev.prop, instsp) else: for pat, prev in zip(As, prevs): matcher.first_order_match_incr(pat, prev.prop, instsp) matcher.first_order_match_incr(C, goal.prop, instsp) As, _ = logic.subst_norm(th.prop, instsp).strip_implies() pts = prevs + [ ProofTerm.sorry(Thm(goal.hyps, A)) for A in As[len(prevs):] ] if set(term.get_vars(th.assums)) != set(term.get_vars(th.prop)) or \ not matcher.is_pattern_list(th.assums, []): tyinst, inst = instsp return apply_theorem(thy, th_name, *pts, tyinst=tyinst, inst=inst) else: return apply_theorem(thy, th_name, *pts)
def search(self, state, id, prevs): cur_th = state.get_proof_item(id).th if len(cur_th.hyps) > 0: return [] results = [] for name, th in state.thy.get_data("theorems").items(): if 'var_induct' not in state.thy.get_attributes(name): continue var_T = th.concl.arg.T vars = [v for v in term.get_vars(cur_th.prop) if v.T == var_T] if len(vars) == 1: results.append({'theorem': name, 'var': vars[0].name}) elif len(vars) > 1: results.append({'theorem': name}) return results
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 match(pat, t, instsp, bd_vars): tyinst, inst = instsp # print("Match", pat, "with", t) if pat.head.is_var() and pat.head.name.startswith('_'): # Case where the head of the function is a variable. if pat.head.name not in inst: # If the variable is not instantiated, check that the # arguments are distinct bound variables, and all bound # variables appearing in t also appear as an argument. # If all conditions hold, assign appropriately. t_vars = term.get_vars(t) if any(v not in bd_vars for v in pat.args): raise MatchException if len(set(pat.args)) != len(pat.args): raise MatchException if any(v in t_vars and v not in pat.args for v in bd_vars): raise MatchException pat_T = TFun(*([v.T for v in pat.args] + [t.get_type()])) try: pat.head.T.match_incr(pat_T, tyinst, internal_only=True) except TypeMatchException: raise MatchException inst_t = t for v in reversed(pat.args): if inst_t.is_comb( ) and inst_t.arg == v and v not in term.get_vars( inst_t.fun): inst_t = inst_t.fun else: inst_t = Term.mk_abs(v, inst_t) inst[pat.head.name] = inst_t else: # If the variable is already instantiated, apply the # instantiation, simplify, and match again. pat2 = inst[pat.head.name](*pat.args).beta_norm() match(pat2, t.beta_norm(), instsp, bd_vars) elif pat.ty != t.ty: # In all other cases, top-level structure of the term # must agree. raise MatchException elif pat.is_var(): # The case where pat come from a bound variable. if pat.name != t.name: raise MatchException elif pat.is_const(): # When pat is a constant, t must also be a constant with # the same name and matching type. if pat.name != t.name: raise MatchException else: try: pat.T.match_incr(t.T, tyinst, internal_only=True) except TypeMatchException: raise MatchException elif pat.is_comb(): # In the combination case (where the head is not a variable), # match fun and arg. if is_pattern(pat.fun, list(instsp[1].keys())): match(pat.fun, t.fun, instsp, bd_vars) match(pat.arg, t.arg, instsp, bd_vars) else: match(pat.arg, t.arg, instsp, bd_vars) match(pat.fun, t.fun, instsp, bd_vars) elif pat.is_abs(): # When pat is a lambda term, t must also be a lambda term. # Replace bound variable by a variable, then match the body. try: pat.var_T.match_incr(t.var_T, tyinst, internal_only=True) except TypeMatchException: raise MatchException T = pat.var_T.subst(tyinst) v = Var(pat.var_name, T) pat_body = pat.subst_type(tyinst).subst_bound(v) t_body = t.subst_bound(v) match(pat_body, t_body, instsp, bd_vars + [v]) elif pat.is_bound(): raise MatchException else: raise TypeError
def solve(goal, pts=None): """The main automation function. If the function succeeds, it should return a proof term whose proposition is the goal. """ if debug_auto: print("Solve:", goal, [str(pt.prop) for pt in pts]) if pts is None: pts = [] elif isinstance(pts, tuple): pts = list(pts) # First handle the case where goal matches one of the conditions. for pt in pts: if goal == pt.prop: return pt # Next, consider the situation where one of the assumptions is # a conjunction or a disjunction. for i, pt in enumerate(pts): if pt.prop.is_conj(): pt1 = apply_theorem('conjD1', pt) pt2 = apply_theorem('conjD2', pt) return solve(goal, [pt1, pt2] + pts[:i] + pts[i + 1:]) if pt.prop.is_disj(): a1, a2 = pt.prop.args assume_pt1 = ProofTerm.assume(a1) assume_pt2 = ProofTerm.assume(a2) pt1 = solve(goal, [assume_pt1] + pts[:i] + pts[i + 1:]) pt1 = pt1.implies_intr(a1) pt2 = solve(goal, [assume_pt2] + pts[:i] + pts[i + 1:]) pt2 = pt2.implies_intr(a2) return apply_theorem('disjE', pt, pt1, pt2) # Handle various logical connectives. if goal.is_conj(): a1, a2 = goal.args pt1 = solve(a1, pts) pt2 = solve(a2, pts) return apply_theorem('conjI', pt1, pt2) if goal.is_disj(): a1, a2 = goal.args try: pt1 = solve(a1, pts) return apply_theorem('disjI1', pt1, concl=goal) except TacticException: pt2 = solve(a2, pts) return apply_theorem('disjI2', pt2, concl=goal) if goal.is_implies(): a1, a2 = goal.args assume_pt = ProofTerm.assume(a1) return solve(a2, [assume_pt] + pts).implies_intr(a1) if goal.is_forall(): var_names = [ v.name for v in term.get_vars([goal] + [pt.prop for pt in pts]) ] nm = name.get_variant_name(goal.arg.var_name, var_names) v = Var(nm, goal.arg.var_T) t = goal.arg.subst_bound(v) return solve(t, pts).forall_intr(v) # Normalize goal eq_pt = norm(goal, pts) goal = eq_pt.rhs if goal.is_conj(): pt = solve(goal, pts) return eq_pt.symmetric().equal_elim(pt) res_pt = None if not pts and goal in solve_record: res_pt = solve_record[goal] # Call registered functions elif goal.is_not() and goal.arg.head in global_autos_neg: for f in global_autos_neg[goal.arg.head]: try: res_pt = f(goal, pts) break except TacticException: pass elif goal.head in global_autos: for f in global_autos[goal.head]: try: res_pt = f(goal, pts) break except TacticException: pass if res_pt is not None: if not pts: solve_record[goal] = res_pt return eq_pt.symmetric().equal_elim(res_pt) else: raise TacticException('Cannot solve %s' % goal)