chore(library/inductive_compiler/nested.cpp): prove all theorems in C++

This commit is contained in:
Daniel Selsam 2017-05-02 09:47:21 -07:00 committed by Leonardo de Moura
parent 4c9247d220
commit d727abeefc
7 changed files with 215 additions and 166 deletions

View file

@ -14,4 +14,4 @@ import init.meta.mk_dec_eq_instance init.meta.mk_inhabited_instance
import init.meta.simp_tactic init.meta.set_get_option_tactics
import init.meta.interactive init.meta.converter init.meta.vm
import init.meta.comp_value_tactics init.meta.smt
import init.meta.async_tactic init.meta.inductive_compiler
import init.meta.async_tactic

View file

@ -1,132 +0,0 @@
/-
Copyright (c) 2017 Daniel Selsam. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Daniel Selsam
-/
prelude
import init.meta.tactic init.meta.simp_tactic init.meta.rewrite_tactic init.meta.converter init.function
namespace inductive_compiler
namespace tactic
open tactic list
private meta def simp_assumption (ls : simp_lemmas) (e : expr) : tactic (expr × expr) := do
(a, new_e, pf) ← ext_simplify_core () {} ls (λ u, failed)
(λ a s r p e, failed)
(λ a s r p e, do ⟨u, new_e, pr⟩ ← conv.apply_lemmas_core s assumption r e,
return ((), new_e, pr, tt))
`iff e,
return (new_e, pf)
private meta def simp_at_assumption (S : simp_lemmas := simp_lemmas.mk) (h : expr) (extra_lemmas : list expr := []) (cfg : simp_config := {}) : tactic unit :=
do when (expr.is_local_constant h = ff) (fail "tactic fsimp_at failed, the given expression is not a hypothesis"),
htype ← infer_type h,
S ← S.append extra_lemmas,
(new_htype, heq) ← simp_assumption S htype,
assert (expr.local_pp_name h) new_htype,
mk_app `iff.mp [heq, h] >>= exact,
try $ clear h
private meta def fsimp (extra_lemmas : list expr := []) (cfg : simp_config := {}) : tactic unit :=
do S ← return (simp_lemmas.mk),
S ← S.append extra_lemmas,
simplify_goal S cfg >> try triv >> try (reflexivity reducible)
private meta def at_end (e : expr) : → tactic (list (option expr))
| 0 := fail "at_end expected arity > 0"
| 1 := return [some e]
| (n+1) := at_end n >>= (λ xs, return (none :: xs))
private meta def heq_to_eq_or_id (n : name) (H : expr) : tactic expr := do
Ht ← infer_type H,
do {
(A, lhs, B, rhs) ← match_heq Ht,
unify A B,
heq ← mk_app `eq [lhs, rhs],
pr ← mk_app `eq_of_heq [H],
assertv n heq pr,
clear H,
get_local n }
<|>
return H
private meta def intros_simp (inj_simps : simp_lemmas) : expr → tactic (list expr)
| (expr.pi n bi b d) := do
H ← intro n,
-- H ← heq_to_eq_or_id n H,
try $ simp_at_assumption inj_simps H,
H ← get_local n,
tgt ← target,
rest ← intros_simp tgt,
return (H :: rest)
| e := do return []
private meta def prove_conjuncts_by_assumption : list expr → expr → tactic unit
| (pf :: pfs) ```(and %%α %%β) := do
split,
exact pf,
prove_conjuncts_by_assumption pfs β
| [pf] _ := exact pf
| _ _ := fail "expecting same number of proofs as conjuncts"
private meta def intros_and_subst : expr → tactic unit
| (expr.pi n bi b d) := do
H ← intro n,
H ← heq_to_eq_or_id n H,
Ht ← infer_type H,
try $ do {
match_eq Ht,
subst H },
target >>= intros_and_subst
| e := return ()
private meta def tgt_to_eq : tactic unit := do
tgt ← target,
try (do c ← mk_const `heq_of_eq, apply c)
meta def prove_nested_inj (inj_simps : simp_lemmas) (inner_ir_inj_arrow : name) : tactic unit := do
xs ← intros,
triv <|> solve1 (do
H_orig_eq ← return (ilast xs),
c ← mk_const inner_ir_inj_arrow,
inner_inj ← to_expr `(%%c %%H_orig_eq),
apply inner_inj,
pfs ← (target >>= intros_simp inj_simps),
target >>= prove_conjuncts_by_assumption pfs)
meta def prove_pack_inj (unpack unpack_pack : name) : tactic unit := do
target >>= intros_and_subst,
split,
-- prove easy direction first
swap,
solve1 (do H ← intro1, H ← heq_to_eq_or_id `H_rhs_eq H, fsimp [H]),
-- hard direction
H ← intro1,
H ← heq_to_eq_or_id `H_lhs_eq H,
tgt_to_eq,
Ht ← infer_type H,
(lhs, rhs) ← match_eq Ht,
arity ← return (expr.get_app_num_args lhs),
args1 ← at_end lhs arity,
args2 ← at_end rhs arity,
lhs' ← mk_mapp unpack args1,
rhs' ← mk_mapp unpack args2,
H_ty ← mk_app `eq [lhs', rhs'],
assert `H_up H_ty,
solve1 (fsimp [H]),
H_up ← get_local `H_up,
solve1 (do e_unpack_pack ← mk_const unpack_pack,
rewrite_at_core semireducible tt tt occurrences.all ff e_unpack_pack H_up,
H_up ← get_local `H_up,
rewrite_at_core semireducible tt tt occurrences.all ff e_unpack_pack H_up,
H_up ← get_local `H_up,
exact H_up)
end tactic
end inductive_compiler

View file

@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jared Roesch
-/
prelude
import init.meta.name init.meta.inductive_compiler
import init.meta.name
namespace ir

View file

@ -39,8 +39,12 @@ Author: Daniel Selsam
#include "library/inductive_compiler/nested.h"
#include "library/inductive_compiler/util.h"
#include "library/tactic/induction_tactic.h"
#include "library/tactic/apply_tactic.h"
#include "library/tactic/subst_tactic.h"
#include "library/tactic/assert_tactic.h"
#include "library/tactic/simp_result.h"
#include "library/tactic/intro_tactic.h"
#include "library/tactic/clear_tactic.h"
#include "library/tactic/simplify.h"
#include "library/tactic/eqn_lemmas.h"
@ -83,7 +87,7 @@ class add_nested_inductive_decl_fn {
environment m_env;
options m_opts;
name_map<implicit_infer_kind> m_implicit_infer_map;
// Note(dhs): m_nested_decl is morally const, but we make it non-const to passing around sizeof_lemmas
// Note(dhs): m_nested_decl is morally const, but we make it non-const to pass around sizeof_lemmas
ginductive_decl & m_nested_decl;
bool m_is_trusted;
ginductive_decl m_inner_decl;
@ -1425,27 +1429,6 @@ class add_nested_inductive_decl_fn {
return cfg;
}
expr prove_by_tactic(name const & lemma_name, expr const & goal_type, name const & tactic, buffer<vm_obj> const & rev_args) {
metavar_context mctx;
expr goal_mvar = mctx.mk_metavar_decl(local_context(), goal_type);
vm_obj tstate = to_obj(mk_tactic_state_for_metavar(m_env, m_opts, lemma_name, mctx, goal_mvar));
buffer<vm_obj> all_rev_args;
all_rev_args.push_back(tstate);
all_rev_args.append(rev_args);
vm_state S(m_env, m_opts);
vm_obj result = S.invoke(tactic, all_rev_args.size(), all_rev_args.begin());
if (optional<tactic_state> s_new = tactic::is_success(result)) {
lean_trace(name({"inductive_compiler", "nested", "prove"}), tout() << "[success]: " << lemma_name << "\n";);
mctx = s_new->mctx();
return mctx.instantiate_mvars(goal_mvar);
} else {
lean_trace(name({"inductive_compiler", "nested", "prove"}), tout() << "[failed]: " << lemma_name << "\n";);
throw exception(sstream() << "Failed to prove: '" << lemma_name << "'");
}
}
expr prove_by_simp(local_context const & lctx, expr const & thm, list<expr> Hs, bool use_sizeof) {
environment env = set_reducible(m_env, get_sizeof_name(), reducible_status::Irreducible, false);
env = set_reducible(env, get_has_add_add_name(), reducible_status::Irreducible, false);
@ -1554,7 +1537,7 @@ class add_nested_inductive_decl_fn {
buffer<vm_obj> args;
args.push_back(to_obj(mk_primitive_name(fn_type::UNPACK_PACK)));
args.push_back(to_obj(mk_primitive_name(fn_type::UNPACK)));
expr proof = prove_by_tactic(lemma_name, goal, get_inductive_compiler_tactic_prove_pack_inj_name(), args);
expr proof = prove_pack_injective(lemma_name, goal, mk_primitive_name(fn_type::UNPACK), mk_primitive_name(fn_type::UNPACK_PACK));
m_env = module::add(m_env, check(m_env, mk_definition_inferring_trusted(m_env, lemma_name, to_list(m_nested_decl.get_lp_names()), goal, proof, true)));
m_tctx.set_env(m_env);
m_inj_lemmas = add(m_tctx, m_inj_lemmas, lemma_name, LEAN_DEFAULT_PRIORITY);
@ -1608,7 +1591,7 @@ class add_nested_inductive_decl_fn {
buffer<vm_obj> args;
args.push_back(to_obj(mk_nested_name(fn_type::UNPACK_PACK, nest_idx)));
args.push_back(to_obj(mk_nested_name(fn_type::UNPACK, nest_idx)));
expr proof = prove_by_tactic(lemma_name, goal, get_inductive_compiler_tactic_prove_pack_inj_name(), args);
expr proof = prove_pack_injective(lemma_name, goal, mk_nested_name(fn_type::UNPACK, nest_idx), mk_nested_name(fn_type::UNPACK_PACK, nest_idx));
m_env = module::add(m_env, check(m_env, mk_definition_inferring_trusted(m_env, lemma_name, to_list(m_nested_decl.get_lp_names()), goal, proof, true)));
m_tctx.set_env(m_env);
m_inj_lemmas = add(m_tctx, m_inj_lemmas, lemma_name, LEAN_DEFAULT_PRIORITY);
@ -1705,7 +1688,7 @@ class add_nested_inductive_decl_fn {
buffer<vm_obj> args;
args.push_back(to_obj(mk_pi_name(fn_type::UNPACK_PACK)));
args.push_back(to_obj(mk_pi_name(fn_type::UNPACK)));
expr proof = prove_by_tactic(lemma_name, goal, get_inductive_compiler_tactic_prove_pack_inj_name(), args);
expr proof = prove_pack_injective(lemma_name, goal, mk_pi_name(fn_type::UNPACK), mk_pi_name(fn_type::UNPACK_PACK));
m_env = module::add(m_env, check(m_env, mk_definition_inferring_trusted(m_env, lemma_name, to_list(m_nested_decl.get_lp_names()), goal, proof, true)));
m_tctx.set_env(m_env);
m_inj_lemmas = add(m_tctx, m_inj_lemmas, lemma_name, LEAN_DEFAULT_PRIORITY);
@ -1955,6 +1938,204 @@ class add_nested_inductive_decl_fn {
return ty;
}
class assumption_simplify_fn : public simplify_fn {
protected:
virtual optional<expr> prove(expr const & e) override {
optional<local_decl> hyp = m_ctx.lctx().find_if([&](local_decl const & decl) { return m_ctx.is_def_eq(decl.get_type(), e); });
if (hyp) {
return some_expr(hyp->mk_ref());
} else {
return none_expr();
}
}
public:
assumption_simplify_fn(type_context & ctx, defeq_canonizer::state & dcs, simp_lemmas const & slss, simp_config const & cfg):
simplify_fn(ctx, dcs, slss, cfg) {}
};
expr intros_simp_prove_conjuncts(type_context & tctx, simp_lemmas const & slss, expr const & tgt) {
simp_config cfg = get_simp_config();
defeq_can_state dcs;
buffer<expr> hyps;
buffer<expr> pfs;
expr ty = tgt;
while (is_pi(ty)) {
expr hyp = tctx.push_local_from_binding(ty);
hyps.push_back(hyp);
assumption_simplify_fn simplify(tctx, dcs, slss, cfg);
simp_result r = finalize(tctx, get_iff_name(), simplify(get_iff_name(), tctx.infer(hyp)));
pfs.push_back(mk_iff_mp(tctx, r.get_proof(), hyp));
ty = instantiate(binding_body(ty), hyp);
}
if (pfs.empty()) {
return tctx.mk_lambda(hyps, mk_true_intro());
}
expr pf = pfs.back();
int i = pfs.size() - 2;
while (i >= 0) {
pf = mk_app(tctx, get_and_intro_name(), pfs[i], pf);
--i;
}
return tctx.mk_lambda(hyps, pf);
}
expr prove_nested_injective(expr const & inj_type, simp_lemmas const & slss, name const & inj_arrow_name) {
lean_trace(name({"inductive_compiler", "nested", "injective"}), tout() << "[try to prove]: " << inj_type << "\n";);
type_context tctx(m_env, m_opts);
buffer<expr> hyps;
expr ty = inj_type;
while (is_pi(ty)) {
expr hyp = tctx.push_local_from_binding(ty);
hyps.push_back(hyp);
ty = tctx.whnf(instantiate(binding_body(ty), hyp));
}
if (ty == mk_true()) {
return tctx.mk_lambda(hyps, mk_true_intro());
}
expr imp = mk_app(tctx, inj_arrow_name, hyps.size() + 1, hyps.back(), ty);
expr imp_type = tctx.infer(imp);
lean_assert(is_arrow(imp_type));
expr pf_of_antecedent = intros_simp_prove_conjuncts(tctx, slss, binding_domain(imp_type));
return tctx.mk_lambda(hyps, mk_app(imp, pf_of_antecedent));
}
tactic_state intros_and_subst(name const & pack_inj_name, expr const & pack_inj_type) {
tactic_state s = mk_tactic_state_for(m_env, m_opts, pack_inj_name, local_context(), pack_inj_type);
buffer<name> new_hyps;
while (optional<tactic_state> o_s = intron(1, s, new_hyps, true)) {
s = *o_s;
type_context tctx = mk_type_context_for(s);
local_decl hyp_decl = tctx.lctx().get_local_decl(new_hyps.back());
expr A, lhs, B, rhs;
if (is_heq(hyp_decl.get_type(), A, lhs, B, rhs) && tctx.is_def_eq(A, B)) {
expr eq_type = mk_eq(tctx, lhs, rhs);
expr eq_val = mk_eq_of_heq(tctx, hyp_decl.mk_ref());
name h_name = hyp_decl.get_name().append_after("_eq");
s = *tactic::is_success(assertv_definev(false, h_name, eq_type, eq_val, s));
s = *tactic::is_success(clear(hyp_decl.mk_ref(), s));
hyp_decl = *(s.get_main_goal_decl()->get_context().find_local_decl_from_user_name(h_name));
}
if (is_eq(hyp_decl.get_type())) {
s = *tactic::is_success(tactic_subst(hyp_decl.mk_ref(), s));
}
}
return s;
}
tactic_state prove_pack_injective_hard_direction(tactic_state const & s0, name const & unpack_name, name const & unpack_pack_name) {
tactic_state s = s0;
buffer<name> new_hyps;
s = *intron(1, s, new_hyps, true);
type_context tctx = mk_type_context_for(s);
local_decl hyp_decl = tctx.lctx().get_local_decl(new_hyps.back());
expr A, lhs, B, rhs;
if (is_heq(hyp_decl.get_type(), A, lhs, B, rhs) && tctx.is_def_eq(A, B)) {
expr eq_type = mk_eq(tctx, lhs, rhs);
expr eq_val = mk_eq_of_heq(tctx, hyp_decl.mk_ref());
name h_name = hyp_decl.get_name().append_after("_eq");
s = *tactic::is_success(assertv_definev(true, h_name, eq_type, eq_val, s));
s = *tactic::is_success(clear(hyp_decl.mk_ref(), s));
hyp_decl = *(s.get_main_goal_decl()->get_context().find_local_decl_from_user_name(h_name));
}
if (is_heq(s.get_main_goal_decl()->get_type(), A, lhs, B, rhs)) {
type_context tctx = mk_type_context_for(s);
lean_assert(tctx.is_def_eq(A, B));
expr e = mk_app(tctx, get_heq_of_eq_name(), 3, A, lhs, rhs);
s = *apply(tctx, false, false, e, s);
}
lean_assert(is_eq(s.get_main_goal_decl()->get_type()));
lean_verify(is_eq(hyp_decl.get_type(), A, lhs, rhs));
unsigned arity = get_app_num_args(lhs);
tctx = mk_type_context_for(s);
name H_unpack_name({"H_unpack"});
expr H_unpack_type = mk_eq(tctx, mk_app(tctx, unpack_name, arity, lhs), mk_app(tctx, unpack_name, arity, rhs));
s = *tactic::is_success(assert_define_core(true, H_unpack_name, H_unpack_type, s));
tctx = mk_type_context_for(s);
simp_config cfg = get_simp_config();
defeq_can_state dcs;
simp_lemmas slss;
slss = add(tctx, slss, hyp_decl.get_name(), hyp_decl.get_type(), hyp_decl.mk_ref(), LEAN_DEFAULT_PRIORITY);
simp_result r = finalize(tctx, get_eq_name(), simplify_fn(tctx, dcs, slss, cfg)(get_eq_name(), s.get_main_goal_decl()->get_type()));
lean_verify(is_eq(r.get_new(), lhs, rhs));
lean_assert(tctx.is_def_eq(lhs, rhs));
s = *apply(tctx, false, false, mk_eq_mpr(tctx, r.get_proof(), mk_eq_refl(tctx, lhs)), s);
s = *intron(1, s, new_hyps, true);
tctx = mk_type_context_for(s);
local_decl H_unpack_decl = tctx.lctx().get_local_decl(new_hyps.back());
slss = simp_lemmas();
slss = add(tctx, slss, unpack_pack_name, LEAN_DEFAULT_PRIORITY);
r = finalize(tctx, get_eq_name(), simplify_fn(tctx, dcs, slss, cfg)(get_eq_name(), H_unpack_decl.get_type()));
s = *apply(tctx, false, false, mk_eq_mp(tctx, r.get_proof(), H_unpack_decl.mk_ref()), s);
return s;
}
tactic_state prove_pack_injective_easy_direction(tactic_state const & s0) {
buffer<name> new_hyps;
tactic_state s = *intron(1, s0, new_hyps, true);
type_context tctx = mk_type_context_for(s);
local_decl hyp_decl = tctx.lctx().get_local_decl(new_hyps.back());
expr A, lhs, B, rhs;
if (is_heq(hyp_decl.get_type(), A, lhs, B, rhs) && tctx.is_def_eq(A, B)) {
expr eq_type = mk_eq(tctx, lhs, rhs);
expr eq_val = mk_eq_of_heq(tctx, hyp_decl.mk_ref());
name h_name = hyp_decl.get_name().append_after("_eq");
s = *tactic::is_success(assertv_definev(true, h_name, eq_type, eq_val, s));
s = *tactic::is_success(clear(hyp_decl.mk_ref(), s));
hyp_decl = *(s.get_main_goal_decl()->get_context().find_local_decl_from_user_name(h_name));
}
expr goal_type = s.get_main_goal_decl()->get_type();
tctx = mk_type_context_for(s);
simp_config cfg = get_simp_config();
defeq_can_state dcs;
simp_lemmas slss;
slss = add(tctx, slss, hyp_decl.get_name(), hyp_decl.get_type(), hyp_decl.mk_ref(), LEAN_DEFAULT_PRIORITY);
simp_result r = finalize(tctx, get_eq_name(), simplify_fn(tctx, dcs, slss, cfg)(get_eq_name(), goal_type));
expr pf;
if (is_eq(r.get_new(), lhs, rhs)) {
lean_verify(tctx.is_def_eq(lhs, rhs));
pf = mk_eq_refl(tctx, lhs);
} else {
lean_verify(is_heq(r.get_new(), lhs, rhs));
lean_assert(tctx.is_def_eq(lhs, rhs));
pf = mk_heq_refl(tctx, lhs);
}
pf = mk_eq_mpr(tctx, r.get_proof(), pf);
s = *apply(tctx, false, false, pf, s);
return s;
}
expr prove_pack_injective(name const & pack_inj_name, expr const & pack_inj_type, name const & unpack_name, name const & unpack_pack_name) {
vm_state vms(m_env, m_opts);
scope_vm_state vms_scope(vms);
tactic_state s = intros_and_subst(pack_inj_name, pack_inj_type);
type_context tctx = mk_type_context_for(s);
s = *apply(tctx, false, false, mk_constant(get_iff_intro_name()), s);
s = prove_pack_injective_hard_direction(s, unpack_name, unpack_pack_name);
s = prove_pack_injective_easy_direction(s);
metavar_context mctx = s.mctx();
expr pf = mctx.instantiate_mvars(s.main());
lean_trace(name({"inductive_compiler", "nested", "injective"}), tout() << "[pack_injective]: " << pf << "\n";);
return pf;
}
void define_nested_injectives() {
if (!m_prove_inj) return;
for (unsigned ind_idx = 0; ind_idx < m_nested_decl.get_num_inds(); ++ind_idx) {
@ -1964,11 +2145,9 @@ class add_nested_inductive_decl_fn {
expr inj_type = mk_injective_type(m_env, mlocal_name(ir), Pi(m_nested_decl.get_params(), mlocal_type(ir)),
m_nested_decl.get_num_params(), to_list(m_nested_decl.get_lp_names()));
buffer<vm_obj> rev_args;
rev_args.push_back(to_obj(mk_injective_arrow_name(mlocal_name(m_inner_decl.get_intro_rule(ind_idx, ir_idx)))));
rev_args.push_back(to_obj(m_inj_lemmas));
name inj_arrow_name = mk_injective_arrow_name(mlocal_name(m_inner_decl.get_intro_rule(ind_idx, ir_idx)));
expr inj_val = prove_by_tactic(inj_name, inj_type, get_inductive_compiler_tactic_prove_nested_inj_name(), rev_args);
expr inj_val = prove_nested_injective(inj_type, m_inj_lemmas, inj_arrow_name);
m_env = module::add(m_env,
check(m_env,
mk_definition_inferring_trusted(m_env, inj_name, to_list(m_nested_decl.get_lp_names()), inj_type, inj_val, true)));

View file

@ -7,8 +7,8 @@ Author: Leonardo de Moura
#pragma once
#include "library/tactic/tactic_state.h"
namespace lean {
optional<tactic_state> intron(unsigned n, tactic_state const & s, buffer<name> & new_Hs);
optional<tactic_state> intron(unsigned n, tactic_state const & s);
optional<tactic_state> intron(unsigned n, tactic_state const & s, buffer<name> & new_Hs, bool use_unused_names);
optional<tactic_state> intron(unsigned n, tactic_state const & s, bool use_unused_names);
/* Low-level versions of the previous procedures, they allow us to intron in any goal.
The new hypotheses "user names" are generated using \c new_hs_names (when available).
After execution, the buffer \c new_Hns stores the new interal names for the new hypotheses.

View file

@ -22,6 +22,8 @@ namespace lean {
expr subst(environment const & env, options const & opts, transparency_mode const & m, metavar_context & mctx,
expr const & mvar, expr const & H, bool symm, hsubstitution * substs);
vm_obj tactic_subst(expr const & l, tactic_state const & s);
void initialize_subst_tactic();
void finalize_subst_tactic();
}

View file

@ -65,7 +65,7 @@ tactic_state mk_tactic_state_for(environment const & env, options const & o, nam
tactic_state mk_tactic_state_for(environment const & env, options const & o, name const & decl_name,
local_context const & lctx, expr const & type) {
return mk_tactic_state_for(env, o, decl_name, {}, lctx, type);
return mk_tactic_state_for(env, o, decl_name, metavar_context(), lctx, type);
}
tactic_state mk_tactic_state_for_metavar(environment const & env, options const & opts, name const & decl_name,