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:
parent
feb385748f
commit
0e099b5fd8
8 changed files with 67 additions and 27 deletions
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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"}));
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue