feat(library/simplifier): preserve binder names when applying higher-order rewrite rules
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
parent
6da1b447f0
commit
b31ef34787
4 changed files with 56 additions and 8 deletions
|
@ -21,6 +21,7 @@ class hop_match_fn {
|
||||||
buffer<optional<expr>> & m_subst;
|
buffer<optional<expr>> & m_subst;
|
||||||
buffer<expr> m_vars;
|
buffer<expr> m_vars;
|
||||||
optional<ro_environment> m_env;
|
optional<ro_environment> m_env;
|
||||||
|
name_map<name> * m_name_subst;
|
||||||
|
|
||||||
bool is_free_var(expr const & x, unsigned ctx_size) const {
|
bool is_free_var(expr const & x, unsigned ctx_size) const {
|
||||||
return is_var(x) && var_idx(x) >= ctx_size;
|
return is_var(x) && var_idx(x) >= ctx_size;
|
||||||
|
@ -296,6 +297,8 @@ class hop_match_fn {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case expr_kind::Lambda: case expr_kind::Pi:
|
case expr_kind::Lambda: case expr_kind::Pi:
|
||||||
|
if (m_name_subst)
|
||||||
|
(*m_name_subst)[abst_name(p)] = abst_name(t);
|
||||||
return
|
return
|
||||||
match(abst_domain(p), abst_domain(t), ctx, ctx_size) &&
|
match(abst_domain(p), abst_domain(t), ctx, ctx_size) &&
|
||||||
match(abst_body(p), abst_body(t), extend(ctx, abst_name(t), abst_domain(t)), ctx_size+1);
|
match(abst_body(p), abst_body(t), extend(ctx, abst_name(t), abst_domain(t)), ctx_size+1);
|
||||||
|
@ -306,15 +309,17 @@ class hop_match_fn {
|
||||||
lean_unreachable();
|
lean_unreachable();
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
hop_match_fn(buffer<optional<expr>> & subst, optional<ro_environment> const & env):m_subst(subst), m_env(env) {}
|
hop_match_fn(buffer<optional<expr>> & subst, optional<ro_environment> const & env,
|
||||||
|
name_map<name> * name_subst):m_subst(subst), m_env(env), m_name_subst(name_subst) {}
|
||||||
|
|
||||||
bool operator()(expr const & p, expr const & t) {
|
bool operator()(expr const & p, expr const & t) {
|
||||||
return match(p, t, context(), 0);
|
return match(p, t, context(), 0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool hop_match(expr const & p, expr const & t, buffer<optional<expr>> & subst, optional<ro_environment> const & env) {
|
bool hop_match(expr const & p, expr const & t, buffer<optional<expr>> & subst, optional<ro_environment> const & env,
|
||||||
return hop_match_fn(subst, env)(p, t);
|
name_map<name> * name_subst) {
|
||||||
|
return hop_match_fn(subst, env, name_subst)(p, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hop_match_core(lua_State * L, optional<ro_environment> const & env) {
|
static int hop_match_core(lua_State * L, optional<ro_environment> const & env) {
|
||||||
|
|
|
@ -28,8 +28,13 @@ namespace lean {
|
||||||
|
|
||||||
If an environment is provided, then a constant \c c matches a term \c t if
|
If an environment is provided, then a constant \c c matches a term \c t if
|
||||||
\c c is definitionally equal to \c t.
|
\c c is definitionally equal to \c t.
|
||||||
|
|
||||||
|
If name_subst is different from nullptr, then the procedure stores in name_subst
|
||||||
|
a mapping for binder names. It maps the binder names used in the pattern \c p into
|
||||||
|
the binder names used in \c t.
|
||||||
*/
|
*/
|
||||||
bool hop_match(expr const & p, expr const & t, buffer<optional<expr>> & subst,
|
bool hop_match(expr const & p, expr const & t, buffer<optional<expr>> & subst,
|
||||||
optional<ro_environment> const & env = optional<ro_environment>());
|
optional<ro_environment> const & env = optional<ro_environment>(),
|
||||||
|
name_map<name> * name_subst = nullptr);
|
||||||
void open_hop_match(lua_State * L);
|
void open_hop_match(lua_State * L);
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,10 @@ Author: Leonardo de Moura
|
||||||
#define LEAN_SIMPLIFIER_MEMOIZE true
|
#define LEAN_SIMPLIFIER_MEMOIZE true
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef LEAN_SIMPLIFIER_PRESERVE_BINDER_NAMES
|
||||||
|
#define LEAN_SIMPLIFIER_PRESERVE_BINDER_NAMES true
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef LEAN_SIMPLIFIER_MAX_STEPS
|
#ifndef LEAN_SIMPLIFIER_MAX_STEPS
|
||||||
#define LEAN_SIMPLIFIER_MAX_STEPS std::numeric_limits<unsigned>::max()
|
#define LEAN_SIMPLIFIER_MAX_STEPS std::numeric_limits<unsigned>::max()
|
||||||
#endif
|
#endif
|
||||||
|
@ -79,6 +83,7 @@ static name g_simplifier_unfold {"simplifier", "unfold"};
|
||||||
static name g_simplifier_conditional {"simplifier", "conditional"};
|
static name g_simplifier_conditional {"simplifier", "conditional"};
|
||||||
static name g_simplifier_memoize {"simplifier", "memoize"};
|
static name g_simplifier_memoize {"simplifier", "memoize"};
|
||||||
static name g_simplifier_max_steps {"simplifier", "max_steps"};
|
static name g_simplifier_max_steps {"simplifier", "max_steps"};
|
||||||
|
static name g_simplifier_preserve_binder_names {"simplifier", "preserve_binder_names"};
|
||||||
|
|
||||||
RegisterBoolOption(g_simplifier_proofs, LEAN_SIMPLIFIER_PROOFS, "(simplifier) generate proofs");
|
RegisterBoolOption(g_simplifier_proofs, LEAN_SIMPLIFIER_PROOFS, "(simplifier) generate proofs");
|
||||||
RegisterBoolOption(g_simplifier_contextual, LEAN_SIMPLIFIER_CONTEXTUAL, "(simplifier) contextual simplification");
|
RegisterBoolOption(g_simplifier_contextual, LEAN_SIMPLIFIER_CONTEXTUAL, "(simplifier) contextual simplification");
|
||||||
|
@ -89,6 +94,8 @@ RegisterBoolOption(g_simplifier_eval, LEAN_SIMPLIFIER_EVAL, "(simplifier) apply
|
||||||
RegisterBoolOption(g_simplifier_unfold, LEAN_SIMPLIFIER_UNFOLD, "(simplifier) unfolds non-opaque definitions");
|
RegisterBoolOption(g_simplifier_unfold, LEAN_SIMPLIFIER_UNFOLD, "(simplifier) unfolds non-opaque definitions");
|
||||||
RegisterBoolOption(g_simplifier_conditional, LEAN_SIMPLIFIER_CONDITIONAL, "(simplifier) conditional rewriting");
|
RegisterBoolOption(g_simplifier_conditional, LEAN_SIMPLIFIER_CONDITIONAL, "(simplifier) conditional rewriting");
|
||||||
RegisterBoolOption(g_simplifier_memoize, LEAN_SIMPLIFIER_MEMOIZE, "(simplifier) memoize/cache intermediate results");
|
RegisterBoolOption(g_simplifier_memoize, LEAN_SIMPLIFIER_MEMOIZE, "(simplifier) memoize/cache intermediate results");
|
||||||
|
RegisterBoolOption(g_simplifier_preserve_binder_names, LEAN_SIMPLIFIER_PRESERVE_BINDER_NAMES,
|
||||||
|
"(simplifier) (try to) preserve binder names when applying higher-order rewrite rules");
|
||||||
RegisterUnsignedOption(g_simplifier_max_steps, LEAN_SIMPLIFIER_MAX_STEPS, "(simplifier) maximum number of steps");
|
RegisterUnsignedOption(g_simplifier_max_steps, LEAN_SIMPLIFIER_MAX_STEPS, "(simplifier) maximum number of steps");
|
||||||
|
|
||||||
bool get_simplifier_proofs(options const & opts) { return opts.get_bool(g_simplifier_proofs, LEAN_SIMPLIFIER_PROOFS); }
|
bool get_simplifier_proofs(options const & opts) { return opts.get_bool(g_simplifier_proofs, LEAN_SIMPLIFIER_PROOFS); }
|
||||||
|
@ -100,6 +107,9 @@ bool get_simplifier_eval(options const & opts) { return opts.get_bool(g_simplifi
|
||||||
bool get_simplifier_unfold(options const & opts) { return opts.get_bool(g_simplifier_unfold, LEAN_SIMPLIFIER_UNFOLD); }
|
bool get_simplifier_unfold(options const & opts) { return opts.get_bool(g_simplifier_unfold, LEAN_SIMPLIFIER_UNFOLD); }
|
||||||
bool get_simplifier_conditional(options const & opts) { return opts.get_bool(g_simplifier_conditional, LEAN_SIMPLIFIER_CONDITIONAL); }
|
bool get_simplifier_conditional(options const & opts) { return opts.get_bool(g_simplifier_conditional, LEAN_SIMPLIFIER_CONDITIONAL); }
|
||||||
bool get_simplifier_memoize(options const & opts) { return opts.get_bool(g_simplifier_memoize, LEAN_SIMPLIFIER_MEMOIZE); }
|
bool get_simplifier_memoize(options const & opts) { return opts.get_bool(g_simplifier_memoize, LEAN_SIMPLIFIER_MEMOIZE); }
|
||||||
|
bool get_simplifier_preserve_binder_names(options const & opts) {
|
||||||
|
return opts.get_bool(g_simplifier_preserve_binder_names, LEAN_SIMPLIFIER_PRESERVE_BINDER_NAMES);
|
||||||
|
}
|
||||||
unsigned get_simplifier_max_steps(options const & opts) { return opts.get_unsigned(g_simplifier_max_steps, LEAN_SIMPLIFIER_MAX_STEPS); }
|
unsigned get_simplifier_max_steps(options const & opts) { return opts.get_unsigned(g_simplifier_max_steps, LEAN_SIMPLIFIER_MAX_STEPS); }
|
||||||
|
|
||||||
static name g_local("local");
|
static name g_local("local");
|
||||||
|
@ -145,6 +155,7 @@ class simplifier_cell::imp {
|
||||||
unsigned m_next_idx; // index used to create fresh constants
|
unsigned m_next_idx; // index used to create fresh constants
|
||||||
unsigned m_num_steps; // number of steps performed
|
unsigned m_num_steps; // number of steps performed
|
||||||
unsigned m_depth; // recursion depth
|
unsigned m_depth; // recursion depth
|
||||||
|
name_map<name> m_name_subst;
|
||||||
std::shared_ptr<simplifier_monitor> m_monitor;
|
std::shared_ptr<simplifier_monitor> m_monitor;
|
||||||
|
|
||||||
// Configuration
|
// Configuration
|
||||||
|
@ -157,6 +168,7 @@ class simplifier_cell::imp {
|
||||||
bool m_unfold;
|
bool m_unfold;
|
||||||
bool m_conditional;
|
bool m_conditional;
|
||||||
bool m_memoize;
|
bool m_memoize;
|
||||||
|
bool m_preserve_binder_names;
|
||||||
unsigned m_max_steps;
|
unsigned m_max_steps;
|
||||||
|
|
||||||
struct updt_rule_set {
|
struct updt_rule_set {
|
||||||
|
@ -839,6 +851,29 @@ class simplifier_cell::imp {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct rename_binder_names_fn : public replace_visitor {
|
||||||
|
name_map<name> const & m_name_subst;
|
||||||
|
rename_binder_names_fn(name_map<name> const & s):m_name_subst(s) {}
|
||||||
|
virtual expr visit_abst(expr const & e, context const & ctx) {
|
||||||
|
auto it = m_name_subst.find(abst_name(e));
|
||||||
|
if (it != m_name_subst.end()) {
|
||||||
|
if (is_lambda(e))
|
||||||
|
return replace_visitor::visit_abst(mk_lambda(it->second, abst_domain(e), abst_body(e)), ctx);
|
||||||
|
else
|
||||||
|
return replace_visitor::visit_abst(mk_pi(it->second, abst_domain(e), abst_body(e)), ctx);
|
||||||
|
} else {
|
||||||
|
return replace_visitor::visit_abst(e, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
expr rename_binder_names(expr const & e, name_map<name> const & name_subst) {
|
||||||
|
if (m_preserve_binder_names && !name_subst.empty())
|
||||||
|
return rename_binder_names_fn(name_subst)(e);
|
||||||
|
else
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief Given lhs and rhs s.t. lhs = rhs.m_expr with proof rhs.m_proof,
|
\brief Given lhs and rhs s.t. lhs = rhs.m_expr with proof rhs.m_proof,
|
||||||
this method applies rewrite rules, beta and evaluation to \c rhs.m_expr,
|
this method applies rewrite rules, beta and evaluation to \c rhs.m_expr,
|
||||||
|
@ -856,12 +891,14 @@ class simplifier_cell::imp {
|
||||||
unsigned num = rule.get_num_args();
|
unsigned num = rule.get_num_args();
|
||||||
subst.clear();
|
subst.clear();
|
||||||
subst.resize(num);
|
subst.resize(num);
|
||||||
if (hop_match(rule.get_lhs(), target, subst, optional<ro_environment>(m_env))) {
|
m_name_subst.clear();
|
||||||
|
if (hop_match(rule.get_lhs(), target, subst, optional<ro_environment>(m_env), &m_name_subst)) {
|
||||||
new_args.clear();
|
new_args.clear();
|
||||||
new_args.resize(num+1);
|
new_args.resize(num+1);
|
||||||
if (found_all_args(num, subst, new_args)) {
|
if (found_all_args(num, subst, new_args)) {
|
||||||
// easy case: all arguments found
|
// easy case: all arguments found
|
||||||
new_rhs = instantiate(rule.get_rhs(), num, new_args.data() + 1);
|
expr rhs = rename_binder_names(rule.get_rhs(), m_name_subst);
|
||||||
|
new_rhs = instantiate(rhs, num, new_args.data() + 1);
|
||||||
if (rule.is_permutation() && !is_lt(new_rhs, target, false))
|
if (rule.is_permutation() && !is_lt(new_rhs, target, false))
|
||||||
return false;
|
return false;
|
||||||
if (m_proofs_enabled) {
|
if (m_proofs_enabled) {
|
||||||
|
@ -879,7 +916,7 @@ class simplifier_cell::imp {
|
||||||
// Conditional rewriting: we try to fill the missing
|
// Conditional rewriting: we try to fill the missing
|
||||||
// arguments by trying to find a proof for ones that are
|
// arguments by trying to find a proof for ones that are
|
||||||
// propositions.
|
// propositions.
|
||||||
expr ceq = rule.get_ceq();
|
expr ceq = rename_binder_names(rule.get_ceq(), m_name_subst);
|
||||||
buffer<expr> & proof_args = new_args;
|
buffer<expr> & proof_args = new_args;
|
||||||
proof_args.clear();
|
proof_args.clear();
|
||||||
if (m_proofs_enabled)
|
if (m_proofs_enabled)
|
||||||
|
@ -1452,6 +1489,7 @@ class simplifier_cell::imp {
|
||||||
m_conditional = get_simplifier_conditional(o);
|
m_conditional = get_simplifier_conditional(o);
|
||||||
m_memoize = get_simplifier_memoize(o);
|
m_memoize = get_simplifier_memoize(o);
|
||||||
m_max_steps = get_simplifier_max_steps(o);
|
m_max_steps = get_simplifier_max_steps(o);
|
||||||
|
m_preserve_binder_names = get_simplifier_preserve_binder_names(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -5,4 +5,4 @@
|
||||||
Assumed: g
|
Assumed: g
|
||||||
¬ (∀ x : ℕ, (∃ y : ℕ, p x y ∨ p (f x) (f y)) ∨ f 0 = 1)
|
¬ (∀ x : ℕ, (∃ y : ℕ, p x y ∨ p (f x) (f y)) ∨ f 0 = 1)
|
||||||
====>
|
====>
|
||||||
(∃ x : ℕ, (∀ x::1 : ℕ, ¬ p x x::1) ∧ (∀ x::1 : ℕ, ¬ p (f x) (f x::1))) ∧ ¬ f 0 = 1
|
(∃ x : ℕ, (∀ y : ℕ, ¬ p x y) ∧ (∀ y : ℕ, ¬ p (f x) (f y))) ∧ ¬ f 0 = 1
|
||||||
|
|
Loading…
Reference in a new issue