feat(library/tactic/rewrite_tactic): apply beta&eta reduction before rewriting steps, add option 'rewrite.beta_eta' (default true) to control new feature.

This commit is contained in:
Leonardo de Moura 2015-06-06 20:30:14 -07:00
parent feb385748f
commit 0e099b5fd8
8 changed files with 67 additions and 27 deletions

View file

@ -121,28 +121,6 @@ expr head_beta_reduce(expr const & t) {
} }
} }
expr beta_reduce(expr t) {
bool reduced = false;
auto f = [&](expr const & m, unsigned) -> optional<expr> {
if (is_head_beta(m)) {
reduced = true;
return some_expr(head_beta_reduce(m));
} else if (is_local(m) || is_metavar(m)) {
return some_expr(m); // do not simplify local constants and metavariables types.
} else {
return none_expr();
}
};
while (true) {
reduced = false;
expr new_t = replace(t, f);
if (!reduced)
return new_t;
else
t = new_t;
}
}
expr instantiate_univ_params(expr const & e, level_param_names const & ps, levels const & ls) { expr instantiate_univ_params(expr const & e, level_param_names const & ps, levels const & ls) {
if (!has_param_univ(e)) if (!has_param_univ(e))
return e; return e;

View file

@ -24,7 +24,6 @@ expr instantiate_rev(expr const & e, unsigned n, expr const * s);
expr apply_beta(expr f, unsigned num_args, expr const * args); expr apply_beta(expr f, unsigned num_args, expr const * args);
bool is_head_beta(expr const & t); bool is_head_beta(expr const & t);
expr head_beta_reduce(expr const & t); expr head_beta_reduce(expr const & t);
expr beta_reduce(expr t);
/** \brief Instantiate the universe level parameters \c ps occurring in \c e with the levels \c ls. /** \brief Instantiate the universe level parameters \c ps occurring in \c e with the levels \c ls.
\pre length(ps) == length(ls) \pre length(ps) == length(ls)

View file

@ -7,11 +7,13 @@ Author: Leonardo de Moura
#include <string> #include <string>
#include "util/interrupt.h" #include "util/interrupt.h"
#include "util/name_generator.h" #include "util/name_generator.h"
#include "kernel/replace_fn.h"
#include "kernel/type_checker.h" #include "kernel/type_checker.h"
#include "kernel/instantiate.h" #include "kernel/instantiate.h"
#include "kernel/abstract.h" #include "kernel/abstract.h"
#include "kernel/free_vars.h" #include "kernel/free_vars.h"
#include "kernel/inductive/inductive.h" #include "kernel/inductive/inductive.h"
#include "library/replace_visitor.h"
#include "library/reducible.h" #include "library/reducible.h"
#include "library/util.h" #include "library/util.h"
#include "library/scoped_ext.h" #include "library/scoped_ext.h"
@ -186,6 +188,42 @@ expr try_eta(expr const & e) {
} }
} }
template<bool Eta, bool Beta>
class eta_beta_reduce_fn : public replace_visitor {
public:
virtual expr visit_app(expr const & e) {
expr e1 = replace_visitor::visit_app(e);
if (Beta && is_head_beta(e1)) {
return visit(head_beta_reduce(e1));
} else {
return e1;
}
}
virtual expr visit_lambda(expr const & e) {
expr e1 = replace_visitor::visit_lambda(e);
if (Eta) {
while (true) {
expr e2 = try_eta(e1);
if (is_eqp(e1, e2))
return e1;
else
e1 = e2;
}
} else {
return e1;
}
}
};
expr beta_reduce(expr t) {
return eta_beta_reduce_fn<false, true>()(t);
}
expr beta_eta_reduce(expr t) {
return eta_beta_reduce_fn<true, true>()(t);
}
class normalize_fn { class normalize_fn {
type_checker & m_tc; type_checker & m_tc;
name_generator m_ngen; name_generator m_ngen;

View file

@ -61,6 +61,8 @@ environment erase_constructor_hint(environment const & env, name const & n, bool
optional<unsigned> has_constructor_hint(environment const & env, name const & d); optional<unsigned> has_constructor_hint(environment const & env, name const & d);
expr try_eta(expr const & e); expr try_eta(expr const & e);
expr beta_reduce(expr t);
expr beta_eta_reduce(expr t);
void initialize_normalize(); void initialize_normalize();
void finalize_normalize(); void finalize_normalize();

View file

@ -52,10 +52,16 @@ Author: Leonardo de Moura
#define LEAN_DEFAULT_REWRITER_TRACE true #define LEAN_DEFAULT_REWRITER_TRACE true
#endif #endif
#ifndef LEAN_DEFAULT_REWRITER_BETA_ETA
#define LEAN_DEFAULT_REWRITER_BETA_ETA true
#endif
namespace lean { namespace lean {
static name * g_rewriter_max_iterations = nullptr; static name * g_rewriter_max_iterations = nullptr;
static name * g_rewriter_syntactic = nullptr; static name * g_rewriter_syntactic = nullptr;
static name * g_rewriter_trace = nullptr; static name * g_rewriter_trace = nullptr;
static name * g_rewriter_beta_eta = nullptr;
unsigned get_rewriter_max_iterations(options const & opts) { unsigned get_rewriter_max_iterations(options const & opts) {
return opts.get_unsigned(*g_rewriter_max_iterations, LEAN_DEFAULT_REWRITER_MAX_ITERATIONS); return opts.get_unsigned(*g_rewriter_max_iterations, LEAN_DEFAULT_REWRITER_MAX_ITERATIONS);
@ -69,6 +75,10 @@ bool get_rewriter_trace(options const & opts) {
return opts.get_bool(*g_rewriter_trace, LEAN_DEFAULT_REWRITER_TRACE); return opts.get_bool(*g_rewriter_trace, LEAN_DEFAULT_REWRITER_TRACE);
} }
bool get_rewriter_beta_eta(options const & opts) {
return opts.get_bool(*g_rewriter_beta_eta, LEAN_DEFAULT_REWRITER_BETA_ETA);
}
class unfold_info { class unfold_info {
list<name> m_names; list<name> m_names;
location m_location; location m_location;
@ -568,6 +578,7 @@ class rewrite_fn {
bool m_use_trace; bool m_use_trace;
bool m_keyed; bool m_keyed;
unsigned m_max_iter; unsigned m_max_iter;
bool m_beta_eta;
buffer<optional<level>> m_lsubst; // auxiliary buffer for pattern matching buffer<optional<level>> m_lsubst; // auxiliary buffer for pattern matching
buffer<optional<expr>> m_esubst; // auxiliary buffer for pattern matching buffer<optional<expr>> m_esubst; // auxiliary buffer for pattern matching
@ -1271,9 +1282,16 @@ class rewrite_fn {
lean::check_term(m_env, m_g.abstract(H)); lean::check_term(m_env, m_g.abstract(H));
} }
expr apply_beta_eta(expr const & e) {
if (m_beta_eta)
return beta_eta_reduce(e);
else
return e;
}
bool process_rewrite_hypothesis(expr const & hyp, expr const & orig_elem, expr const & pattern, occurrence const & occ) { bool process_rewrite_hypothesis(expr const & hyp, expr const & orig_elem, expr const & pattern, occurrence const & occ) {
add_target(hyp, true); add_target(hyp, true);
expr Pa = mlocal_type(hyp); expr Pa = apply_beta_eta(mlocal_type(hyp));
bool is_goal = false; bool is_goal = false;
if (auto it = find_target(Pa, pattern, orig_elem, is_goal)) { if (auto it = find_target(Pa, pattern, orig_elem, is_goal)) {
expr a, Heq, b; // Heq is a proof of a = b expr a, Heq, b; // Heq is a proof of a = b
@ -1326,7 +1344,7 @@ class rewrite_fn {
} }
bool process_rewrite_goal(expr const & orig_elem, expr const & pattern, occurrence const & occ) { bool process_rewrite_goal(expr const & orig_elem, expr const & pattern, occurrence const & occ) {
expr Pa = m_g.get_type(); expr Pa = apply_beta_eta(m_g.get_type());
add_target(Pa, false); add_target(Pa, false);
bool is_goal = true; bool is_goal = true;
if (auto it = find_target(Pa, pattern, orig_elem, is_goal)) { if (auto it = find_target(Pa, pattern, orig_elem, is_goal)) {
@ -1539,6 +1557,7 @@ public:
m_subst = m_ps.get_subst(); m_subst = m_ps.get_subst();
m_max_iter = get_rewriter_max_iterations(ios.get_options()); m_max_iter = get_rewriter_max_iterations(ios.get_options());
m_use_trace = get_rewriter_trace(ios.get_options()); m_use_trace = get_rewriter_trace(ios.get_options());
m_beta_eta = get_rewriter_beta_eta(ios.get_options());
} }
proof_state_seq operator()(buffer<expr> const & elems) { proof_state_seq operator()(buffer<expr> const & elems) {
@ -1594,6 +1613,10 @@ void initialize_rewrite_tactic() {
g_rewriter_trace = new name{"rewriter", "trace"}; g_rewriter_trace = new name{"rewriter", "trace"};
register_bool_option(*g_rewriter_trace, LEAN_DEFAULT_REWRITER_TRACE, register_bool_option(*g_rewriter_trace, LEAN_DEFAULT_REWRITER_TRACE,
"(rewriter tactic) if true tactic will generate a trace for rewrite step failures"); "(rewriter tactic) if true tactic will generate a trace for rewrite step failures");
g_rewriter_beta_eta = new name{"rewriter", "beta_eta"};
register_bool_option(*g_rewriter_beta_eta, LEAN_DEFAULT_REWRITER_BETA_ETA,
"(rewriter tactic) apply beta and eta reduction before rewrite steps");
name rewrite_tac_name{"tactic", "rewrite_tac"}; name rewrite_tac_name{"tactic", "rewrite_tac"};
g_rewrite_tac = new expr(Const(rewrite_tac_name)); g_rewrite_tac = new expr(Const(rewrite_tac_name));
g_xrewrite_tac = new expr(Const(name{"tactic", "xrewrite_tac"})); g_xrewrite_tac = new expr(Const(name{"tactic", "xrewrite_tac"}));

View file

@ -16,6 +16,7 @@ Author: Leonardo de Moura
#include "kernel/type_checker.h" #include "kernel/type_checker.h"
#include "kernel/for_each_fn.h" #include "kernel/for_each_fn.h"
#include "kernel/replace_fn.h" #include "kernel/replace_fn.h"
#include "library/normalize.h"
#include "library/kernel_bindings.h" #include "library/kernel_bindings.h"
#include "library/tactic/tactic.h" #include "library/tactic/tactic.h"
#include "library/io_state_stream.h" #include "library/io_state_stream.h"

View file

@ -32,7 +32,6 @@ static void tst1() {
expr F3 = mk_app(Fun(x, mk_app(f, mk_app(Fun(y, y), x), x)), a); expr F3 = mk_app(Fun(x, mk_app(f, mk_app(Fun(y, y), x), x)), a);
lean_assert(is_head_beta(F3)); lean_assert(is_head_beta(F3));
lean_assert(head_beta_reduce(F3) == mk_app(f, mk_app(Fun(y, y), a), a)); lean_assert(head_beta_reduce(F3) == mk_app(f, mk_app(Fun(y, y), a), a));
lean_assert(beta_reduce(F3) == mk_app(f, a, a));
} }
int main() { int main() {

View file

@ -1,5 +1,5 @@
finset_induction_bug.lean:30:23: error:invalid 'rewrite' tactic, step produced type incorrect term, details: type mismatch at application finset_induction_bug.lean:30:23: error:invalid 'rewrite' tactic, step produced type incorrect term, details: type mismatch at application
@subtype.tag (list A) (λ (l : list A), @nodup A l) x nodup_al' @subtype.tag (list A) (@nodup A) x nodup_al'
term term
nodup_al' nodup_al'
has type has type