feat(library/simplify): use custom matcher in the simplifier, and remove hack from type_context

@joehendrix This commit is implementing the matcher that postpones
implicit arguments. The lemma get_data_mk_byte can be proved without
using any hacks in the type_context unifier.

I also added the trace class: simplify.implicit_failure
If we use the command

   set_option trace.simplify.implicit_failure true

Then, the simplifier will generate a diagnostic message every time it
succeeds in the explicit part, but fails in the implicit one.

Please feel free to suggest a better name to his option.

BTW, we can now easily extend the matcher with additional features.
I'm wondering if we will eventually want to write some of these
extensions in Lean.
This commit is contained in:
Leonardo de Moura 2017-02-08 22:24:13 -08:00
parent f5e0fc4876
commit e4b3dee526
6 changed files with 136 additions and 96 deletions

View file

@ -1269,6 +1269,7 @@ class add_nested_inductive_decl_fn {
defeq_can_state dcs;
simplify_fn simplifier(tctx, dcs, all_lemmas, max_steps, contextual, lift_eq,
canonize_instances, canonize_proofs, use_axioms);
simplifier.set_use_matcher(false); // hack
auto thm_pr = simplifier.prove_by_simp(get_eq_name(), thm);
if (!thm_pr) {
formatter_factory const & fmtf = get_global_ios().get_formatter_factory();

View file

@ -497,14 +497,79 @@ simp_result simplify_core_fn::rewrite(expr const & e) {
return simp_result(e);
}
struct match_fn {
tmp_type_context & m_ctx;
name const & m_id;
buffer<std::tuple<expr, expr, bool>> m_postponed;
match_fn(tmp_type_context & ctx, name const & id):m_ctx(ctx), m_id(id) {}
bool match(expr const & p, expr const & t) {
if (m_ctx.ctx().is_mvar(p))
if (auto v = m_ctx.ctx().get_assignment(p))
return match(*v, t);
if (is_app(p) && is_app(t)) {
expr const & fn = get_app_fn(p);
if (m_ctx.is_def_eq(fn, get_app_fn(t))) {
buffer<expr> p_args;
buffer<expr> t_args;
get_app_args(p, p_args);
get_app_args(t, t_args);
fun_info finfo = get_fun_info(m_ctx.ctx(), fn);
if (p_args.size() != t_args.size())
return false;
auto it = finfo.get_params_info();
for (unsigned i = 0; i < p_args.size(); i++) {
if (it && head(it).is_inst_implicit()) {
m_postponed.emplace_back(p_args[i], t_args[i], true);
} else if (it && head(it).is_implicit()) {
m_postponed.emplace_back(p_args[i], t_args[i], false);
} else if (!match(p_args[i], t_args[i])) {
return false;
}
if (it) it = tail(it);
}
return true;
}
}
return m_ctx.is_def_eq(p, t);
}
bool operator()(expr const & p, expr const & t) {
if (!match(p, t)) return false;
for (auto const & e : m_postponed) {
expr p1, t1; bool implicit;
std::tie(p1, t1, implicit) = e;
p1 = m_ctx.instantiate_mvars(p1);
if (implicit)
p1 = m_ctx.ctx().complete_instance(p1);
if (!match(p1, t1)) {
lean_trace(name({"simplify", "implicit_failure"}), scope_trace_env scope(m_ctx.env(), m_ctx.ctx());
tout() << "fail to match '" << m_id << "':\n";
tout() << p << "\n=?=\n" << t << "\nbecause the following implicit match\n";
tout() << p1 << "\n=?=\n" << t1 << "\n";);
return false;
}
}
return true;
}
};
bool simplify_core_fn::match(tmp_type_context & ctx, simp_lemma const & sl, expr const & t) {
if (m_use_matcher)
return match_fn(ctx, sl.get_id())(sl.get_lhs(), t);
else
return ctx.is_def_eq(t, sl.get_lhs());
}
simp_result simplify_core_fn::rewrite_core(expr const & e, simp_lemma const & sl) {
tmp_type_context tmp_ctx(m_ctx, sl.get_num_umeta(), sl.get_num_emeta());
if (!tmp_ctx.is_def_eq(e, sl.get_lhs())) {
if (!match(tmp_ctx, sl, e)) {
lean_trace_d(name({"debug", "simplify", "try_rewrite"}),
tout() << "fail to unify '" << sl.get_id()
<< "':\n------------------------------------------------\n"
<< e << "\n=?=\n" << sl.get_lhs()
<< "\n------------------------------------------------\n";);
tout() << "fail to unify '" << sl.get_id() << "':\n"
<< e << "\n=?=\n" << sl.get_lhs() << "\n--------------\n";);
return simp_result(e);
}
@ -1158,6 +1223,7 @@ vm_obj tactic_ext_simplify_core(unsigned DEBUG_CODE(num), vm_obj const * args) {
void initialize_simplify() {
register_trace_class("simplify");
register_trace_class(name({"simplify", "failure"}));
register_trace_class(name({"simplify", "implicit_failure"}));
register_trace_class(name({"simplify", "context"}));
register_trace_class(name({"simplify", "canonize"}));
register_trace_class(name({"simplify", "congruence"}));

View file

@ -51,6 +51,9 @@ protected:
bool m_lift_eq;
bool m_canonize_instances;
bool m_canonize_proofs;
/* The following option should be removed as soon as we
refactor the inductive compiler. */
bool m_use_matcher{true};
simp_result join(simp_result const & r1, simp_result const & r2);
void inc_num_steps();
@ -100,6 +103,8 @@ protected:
simp_result simplify(expr const & e);
bool match(tmp_type_context & ctx, simp_lemma const & sl, expr const & t);
public:
simplify_core_fn(type_context & ctx, defeq_canonizer::state & dcs, simp_lemmas const & slss,
unsigned max_steps, bool contextual, bool lift_eq,
@ -108,6 +113,8 @@ public:
environment const & env() const;
simp_result operator()(name const & rel, expr const & e);
void set_use_matcher(bool flag) { m_use_matcher = flag; }
optional<expr> prove_by_simp(name const & rel, expr const & e);
};

View file

@ -2220,73 +2220,18 @@ bool type_context::is_def_eq_args(expr const & e1, expr const & e2) {
return false;
fun_info finfo = get_fun_info(*this, fn, args1.size());
unsigned i = 0;
/*
Try to solve unification constraint
(f a_1 ... a_n) =?= (f b_1 ... b_n)
by solving
a_i =?= b_i
We add i to postponed, if a_i or b_i is a numeral and
the i-th argument of f has forward dependencies.
The goal is to be able to handle unification constraints
coming from fixed size vector problems.
In this kind of problem, we have constraints such as
@to_list ?α (?m - ?n) (@dropn ?α ?m ?n ?v) =?= @to_list bool 6 (@dropn bool 8 2 v)
In the first pass, we have the constraint
?m - ?n =?= 6
which cannot be solved. However, after we unify the next argument, we have
?m := 8 and ?n := 2
and the constraint above becomes
8 - 2 =?= 6
which can be solved.
*/
buffer<unsigned> postponed;
bool progress = false;
for (param_info const & pinfo : finfo.get_params_info()) {
if (pinfo.is_inst_implicit()) {
args1[i] = complete_instance(args1[i]);
args2[i] = complete_instance(args2[i]);
}
if (is_def_eq_core(args1[i], args2[i])) {
progress = true;
} else if (pinfo.has_fwd_deps() && (to_small_num(args1[i]) || to_small_num(args2[i]))) {
postponed.push_back(i);
} else {
if (!is_def_eq_core(args1[i], args2[i]))
return false;
}
i++;
}
for (; i < args1.size(); i++) {
if (is_def_eq_core(args1[i], args2[i])) {
progress = true;
} else {
if (!is_def_eq_core(args1[i], args2[i]))
return false;
}
}
while (true) {
if (postponed.empty()) return true;
if (!progress) return false;
progress = false;
unsigned j = 0;
for (unsigned i = 0; i < postponed.size(); i++) {
if (is_def_eq_core(instantiate_mvars(args1[postponed[i]]), instantiate_mvars(args2[postponed[i]]))) {
progress = true;
} else {
postponed[j] = postponed[i];
j++;
}
}
postponed.shrink(j);
}
return true;
}
@ -3675,34 +3620,34 @@ expr type_context::eta_expand(expr const & e) {
return locals.mk_lambda(r);
}
tmp_type_context::tmp_type_context(type_context & tctx, unsigned num_umeta, unsigned num_emeta): m_tctx(tctx) {
tmp_type_context::tmp_type_context(type_context & ctx, unsigned num_umeta, unsigned num_emeta): m_ctx(ctx) {
m_tmp_uassignment.resize(num_umeta, none_level());
m_tmp_eassignment.resize(num_emeta, none_expr());
}
bool tmp_type_context::is_def_eq(expr const & e1, expr const & e2) {
type_context::tmp_mode_scope_with_buffers tmp_scope(m_tctx, m_tmp_uassignment, m_tmp_eassignment);
return m_tctx.is_def_eq(e1, e2);
type_context::tmp_mode_scope_with_buffers tmp_scope(m_ctx, m_tmp_uassignment, m_tmp_eassignment);
return m_ctx.is_def_eq(e1, e2);
}
expr tmp_type_context::infer(expr const & e) {
type_context::tmp_mode_scope_with_buffers tmp_scope(m_tctx, m_tmp_uassignment, m_tmp_eassignment);
return m_tctx.infer(e);
type_context::tmp_mode_scope_with_buffers tmp_scope(m_ctx, m_tmp_uassignment, m_tmp_eassignment);
return m_ctx.infer(e);
}
expr tmp_type_context::whnf(expr const & e) {
type_context::tmp_mode_scope_with_buffers tmp_scope(m_tctx, m_tmp_uassignment, m_tmp_eassignment);
return m_tctx.whnf(e);
type_context::tmp_mode_scope_with_buffers tmp_scope(m_ctx, m_tmp_uassignment, m_tmp_eassignment);
return m_ctx.whnf(e);
}
level tmp_type_context::mk_tmp_univ_mvar() {
type_context::tmp_mode_scope_with_buffers tmp_scope(m_tctx, m_tmp_uassignment, m_tmp_eassignment);
return m_tctx.mk_tmp_univ_mvar();
type_context::tmp_mode_scope_with_buffers tmp_scope(m_ctx, m_tmp_uassignment, m_tmp_eassignment);
return m_ctx.mk_tmp_univ_mvar();
}
expr tmp_type_context::mk_tmp_mvar(expr const & type) {
type_context::tmp_mode_scope_with_buffers tmp_scope(m_tctx, m_tmp_uassignment, m_tmp_eassignment);
return m_tctx.mk_tmp_mvar(type);
type_context::tmp_mode_scope_with_buffers tmp_scope(m_ctx, m_tmp_uassignment, m_tmp_eassignment);
return m_ctx.mk_tmp_mvar(type);
}
bool tmp_type_context::is_uassigned(unsigned i) const {
@ -3720,48 +3665,48 @@ void tmp_type_context::clear_eassignment() {
}
expr tmp_type_context::instantiate_mvars(expr const & e) {
type_context::tmp_mode_scope_with_buffers tmp_scope(m_tctx, m_tmp_uassignment, m_tmp_eassignment);
return m_tctx.instantiate_mvars(e);
type_context::tmp_mode_scope_with_buffers tmp_scope(m_ctx, m_tmp_uassignment, m_tmp_eassignment);
return m_ctx.instantiate_mvars(e);
}
void tmp_type_context::assign(expr const & m, expr const & v) {
type_context::tmp_mode_scope_with_buffers tmp_scope(m_tctx, m_tmp_uassignment, m_tmp_eassignment);
m_tctx.assign(m, v);
type_context::tmp_mode_scope_with_buffers tmp_scope(m_ctx, m_tmp_uassignment, m_tmp_eassignment);
m_ctx.assign(m, v);
}
expr tmp_type_context::mk_lambda(buffer<expr> const & locals, expr const & e) {
type_context::tmp_mode_scope_with_buffers tmp_scope(m_tctx, m_tmp_uassignment, m_tmp_eassignment);
return m_tctx.mk_lambda(locals, e);
type_context::tmp_mode_scope_with_buffers tmp_scope(m_ctx, m_tmp_uassignment, m_tmp_eassignment);
return m_ctx.mk_lambda(locals, e);
}
expr tmp_type_context::mk_pi(buffer<expr> const & locals, expr const & e) {
type_context::tmp_mode_scope_with_buffers tmp_scope(m_tctx, m_tmp_uassignment, m_tmp_eassignment);
return m_tctx.mk_pi(locals, e);
type_context::tmp_mode_scope_with_buffers tmp_scope(m_ctx, m_tmp_uassignment, m_tmp_eassignment);
return m_ctx.mk_pi(locals, e);
}
expr tmp_type_context::mk_lambda(expr const & local, expr const & e) {
type_context::tmp_mode_scope_with_buffers tmp_scope(m_tctx, m_tmp_uassignment, m_tmp_eassignment);
return m_tctx.mk_lambda(local, e);
type_context::tmp_mode_scope_with_buffers tmp_scope(m_ctx, m_tmp_uassignment, m_tmp_eassignment);
return m_ctx.mk_lambda(local, e);
}
expr tmp_type_context::mk_pi(expr const & local, expr const & e) {
type_context::tmp_mode_scope_with_buffers tmp_scope(m_tctx, m_tmp_uassignment, m_tmp_eassignment);
return m_tctx.mk_pi(local, e);
type_context::tmp_mode_scope_with_buffers tmp_scope(m_ctx, m_tmp_uassignment, m_tmp_eassignment);
return m_ctx.mk_pi(local, e);
}
expr tmp_type_context::mk_lambda(std::initializer_list<expr> const & locals, expr const & e) {
type_context::tmp_mode_scope_with_buffers tmp_scope(m_tctx, m_tmp_uassignment, m_tmp_eassignment);
return m_tctx.mk_lambda(locals, e);
type_context::tmp_mode_scope_with_buffers tmp_scope(m_ctx, m_tmp_uassignment, m_tmp_eassignment);
return m_ctx.mk_lambda(locals, e);
}
expr tmp_type_context::mk_pi(std::initializer_list<expr> const & locals, expr const & e) {
type_context::tmp_mode_scope_with_buffers tmp_scope(m_tctx, m_tmp_uassignment, m_tmp_eassignment);
return m_tctx.mk_pi(locals, e);
type_context::tmp_mode_scope_with_buffers tmp_scope(m_ctx, m_tmp_uassignment, m_tmp_eassignment);
return m_ctx.mk_pi(locals, e);
}
bool tmp_type_context::is_prop(expr const & e) {
type_context::tmp_mode_scope_with_buffers tmp_scope(m_tctx, m_tmp_uassignment, m_tmp_eassignment);
return m_tctx.is_prop(e);
type_context::tmp_mode_scope_with_buffers tmp_scope(m_ctx, m_tmp_uassignment, m_tmp_eassignment);
return m_ctx.is_prop(e);
}
/** \brief Helper class for pretty printing terms that contain local_decl_ref's and metavar_decl_ref's */

View file

@ -418,6 +418,9 @@ public:
expr eta_expand(expr const & e);
/* Try to assign metavariables occuring in e using type class resolution */
expr complete_instance(expr const & e);
struct transparency_scope : public flet<transparency_mode> {
transparency_scope(type_context & ctx, transparency_mode m):
flet<transparency_mode>(ctx.m_transparency_mode, m) {
@ -618,7 +621,6 @@ private:
bool is_def_eq_core_core(expr const & t, expr const & s);
bool is_def_eq_core(expr const & t, expr const & s);
bool is_def_eq_binding(expr e1, expr e2);
expr complete_instance(expr const & e);
expr try_to_unstuck_using_complete_instance(expr const & e);
bool is_def_eq_args(expr const & e1, expr const & e2);
bool is_def_eq_eta(expr const & e1, expr const & e2);
@ -703,15 +705,15 @@ public:
};
class tmp_type_context : public abstract_type_context {
type_context & m_tctx;
type_context & m_ctx;
buffer<optional<level>> m_tmp_uassignment;
buffer<optional<expr>> m_tmp_eassignment;
public:
tmp_type_context(type_context & tctx, unsigned num_umeta = 0, unsigned num_emeta = 0);
type_context & tctx() const { return m_tctx; }
tmp_type_context(type_context & ctx, unsigned num_umeta = 0, unsigned num_emeta = 0);
type_context & ctx() const { return m_ctx; }
virtual environment const & env() const override { return m_tctx.env(); }
virtual environment const & env() const override { return m_ctx.env(); }
virtual expr infer(expr const & e) override;
virtual expr whnf(expr const & e) override;
virtual bool is_def_eq(expr const & e1, expr const & e2) override;

View file

@ -0,0 +1,19 @@
import data.bitvec
open vector
def byte_type := bitvec 8
-- A byte is formed from concatenating two bits and a 6-bit field.
def mk_byte (a b : bool) (l : bitvec 6) : byte_type := a :: b :: l
-- Get the third component
def get_data (byte : byte_type) : bitvec 6 := vector.dropn 2 byte
lemma get_data_mk_byte {a b : bool} {l : bitvec 6} : get_data (mk_byte a b l) = l :=
begin
apply vector.eq,
unfold mk_byte,
unfold get_data,
simp [to_list_dropn, to_list_cons, list.dropn]
end