Remove duplicate solutions in the higher order matching module. Simplify solutions when higher-order matching is used, and we don't have a residue.
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
parent
ffef055e34
commit
80a1b96925
3 changed files with 92 additions and 12 deletions
|
@ -29,6 +29,7 @@ class expr_eq_fn {
|
||||||
|
|
||||||
bool apply(expr const & a0, expr const & b0) {
|
bool apply(expr const & a0, expr const & b0) {
|
||||||
if (is_eqp(a0, b0)) return true;
|
if (is_eqp(a0, b0)) return true;
|
||||||
|
if (!a0 || !b0) return false;
|
||||||
if (UseHash && a0.hash() != b0.hash()) return false;
|
if (UseHash && a0.hash() != b0.hash()) return false;
|
||||||
expr const & a = m_norm(a0);
|
expr const & a = m_norm(a0);
|
||||||
expr const & b = m_norm(b0);
|
expr const & b = m_norm(b0);
|
||||||
|
|
|
@ -23,6 +23,10 @@ Author: Leonardo de Moura
|
||||||
#define LEAN_LIBRARY_HO_UNIFIER_MAX_SOLUTIONS std::numeric_limits<unsigned>::max()
|
#define LEAN_LIBRARY_HO_UNIFIER_MAX_SOLUTIONS std::numeric_limits<unsigned>::max()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef LEAN_LIBRARY_HO_UNIFIER_REMOVE_DUPLICATES
|
||||||
|
#define LEAN_LIBRARY_HO_UNIFIER_REMOVE_DUPLICATES true
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef LEAN_LIBRARY_HO_UNIFIER_USE_NORMALIZER
|
#ifndef LEAN_LIBRARY_HO_UNIFIER_USE_NORMALIZER
|
||||||
#define LEAN_LIBRARY_HO_UNIFIER_USE_NORMALIZER true
|
#define LEAN_LIBRARY_HO_UNIFIER_USE_NORMALIZER true
|
||||||
#endif
|
#endif
|
||||||
|
@ -32,12 +36,15 @@ Author: Leonardo de Moura
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace lean {
|
namespace lean {
|
||||||
static name g_library_ho_unifier_max_solutions {"library", "ho_unifier", "max_solutions"};
|
static name g_library_ho_unifier_max_solutions {"library", "ho_unifier", "max_solutions"};
|
||||||
static name g_library_ho_unifier_use_normalizer {"library", "ho_unifier", "use_normalizer"};
|
static name g_library_ho_unifier_remove_duplicates {"library", "ho_unifier", "remove_duplicates"};
|
||||||
static name g_library_ho_unifier_use_beta {"library", "ho_unifier", "use_beta"};
|
static name g_library_ho_unifier_use_normalizer {"library", "ho_unifier", "use_normalizer"};
|
||||||
|
static name g_library_ho_unifier_use_beta {"library", "ho_unifier", "use_beta"};
|
||||||
|
|
||||||
RegisterUnsignedOption(g_library_ho_unifier_max_solutions, LEAN_LIBRARY_HO_UNIFIER_MAX_SOLUTIONS,
|
RegisterUnsignedOption(g_library_ho_unifier_max_solutions, LEAN_LIBRARY_HO_UNIFIER_MAX_SOLUTIONS,
|
||||||
"maximum number of solutions for each invocation of the higher-order unifier");
|
"maximum number of solutions for each invocation of the higher-order unifier");
|
||||||
|
RegisterBoolOption(g_library_ho_unifier_remove_duplicates, LEAN_LIBRARY_HO_UNIFIER_REMOVE_DUPLICATES,
|
||||||
|
"remove duplicate solutions in the higher-order unification module");
|
||||||
RegisterBoolOption(g_library_ho_unifier_use_normalizer, LEAN_LIBRARY_HO_UNIFIER_USE_NORMALIZER,
|
RegisterBoolOption(g_library_ho_unifier_use_normalizer, LEAN_LIBRARY_HO_UNIFIER_USE_NORMALIZER,
|
||||||
"use normalizer in the higher-order unification module");
|
"use normalizer in the higher-order unification module");
|
||||||
RegisterBoolOption(g_library_ho_unifier_use_beta, LEAN_LIBRARY_HO_UNIFIER_USE_BETA,
|
RegisterBoolOption(g_library_ho_unifier_use_beta, LEAN_LIBRARY_HO_UNIFIER_USE_BETA,
|
||||||
|
@ -47,6 +54,10 @@ unsigned get_ho_unifier_max_solutions(options const & opts) {
|
||||||
return opts.get_unsigned(g_library_ho_unifier_max_solutions, LEAN_LIBRARY_HO_UNIFIER_MAX_SOLUTIONS);
|
return opts.get_unsigned(g_library_ho_unifier_max_solutions, LEAN_LIBRARY_HO_UNIFIER_MAX_SOLUTIONS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get_ho_unifier_remove_duplicates(options const & opts) {
|
||||||
|
return opts.get_bool(g_library_ho_unifier_remove_duplicates, LEAN_LIBRARY_HO_UNIFIER_REMOVE_DUPLICATES);
|
||||||
|
}
|
||||||
|
|
||||||
bool get_ho_unifier_use_normalizer(options const & opts) {
|
bool get_ho_unifier_use_normalizer(options const & opts) {
|
||||||
return opts.get_bool(g_library_ho_unifier_use_normalizer, LEAN_LIBRARY_HO_UNIFIER_USE_NORMALIZER);
|
return opts.get_bool(g_library_ho_unifier_use_normalizer, LEAN_LIBRARY_HO_UNIFIER_USE_NORMALIZER);
|
||||||
}
|
}
|
||||||
|
@ -74,9 +85,11 @@ class ho_unifier::imp {
|
||||||
state_stack m_state_stack;
|
state_stack m_state_stack;
|
||||||
unsigned m_delayed;
|
unsigned m_delayed;
|
||||||
unsigned m_next_state_id;
|
unsigned m_next_state_id;
|
||||||
|
bool m_ho;
|
||||||
volatile bool m_interrupted;
|
volatile bool m_interrupted;
|
||||||
// options
|
// options
|
||||||
unsigned m_max_solutions;
|
unsigned m_max_solutions;
|
||||||
|
bool m_remove_duplicates;
|
||||||
bool m_use_normalizer;
|
bool m_use_normalizer;
|
||||||
bool m_use_beta;
|
bool m_use_beta;
|
||||||
|
|
||||||
|
@ -116,13 +129,76 @@ class ho_unifier::imp {
|
||||||
|
|
||||||
void init(context const & ctx, expr const & l, expr const & r, metavar_env const & menv) {
|
void init(context const & ctx, expr const & l, expr const & r, metavar_env const & menv) {
|
||||||
m_next_state_id = 0;
|
m_next_state_id = 0;
|
||||||
|
m_ho = false;
|
||||||
m_state_stack.clear();
|
m_state_stack.clear();
|
||||||
m_state_stack.push_back(mk_state(menv, cqueue()));
|
m_state_stack.push_back(mk_state(menv, cqueue()));
|
||||||
add_constraint(ctx, l, r);
|
add_constraint(ctx, l, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
list<result> save_result(list<result> const & r, metavar_env const & s, residue const & rs) {
|
/**
|
||||||
return cons(result(s, rs), r);
|
\brief Return true iff \c r already contains the solution (s, rs).
|
||||||
|
|
||||||
|
\remark We only check if \c rs is empty.
|
||||||
|
|
||||||
|
\remark \c ini_s is the initial substitution set. \c s is an extension of \c ini_s
|
||||||
|
*/
|
||||||
|
bool contains_solution(list<result> const & r, metavar_env const & s, residue const & rs, metavar_env const & ini_s) {
|
||||||
|
return
|
||||||
|
empty(rs) &&
|
||||||
|
std::any_of(r.begin(), r.end(), [&](result const & prev) {
|
||||||
|
if (!empty(prev.second))
|
||||||
|
return false;
|
||||||
|
metavar_env const & prev_s = prev.first;
|
||||||
|
// Remark, prev_s and s are extensions of ini_s
|
||||||
|
for (unsigned i = 0; i < ini_s.size(); i++) {
|
||||||
|
if (!ini_s.is_assigned(i) && prev_s.get_subst(i) != s.get_subst(i))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Cleanup the result (remove auxiliary metavariables created by higher-order matching)
|
||||||
|
|
||||||
|
\remark \c ini_s is the initial substitution set. \c s is an extension of \c ini_s
|
||||||
|
*/
|
||||||
|
metavar_env cleanup_subst(metavar_env const & s, metavar_env const & ini_s) {
|
||||||
|
metavar_env new_s;
|
||||||
|
for (unsigned i = 0; i < ini_s.size(); i++) {
|
||||||
|
new_s.mk_metavar(s.get_type(i), s.get_context(i));
|
||||||
|
expr subst = s.get_subst(i);
|
||||||
|
if (subst) {
|
||||||
|
if (m_use_beta && !ini_s.is_assigned(i)) {
|
||||||
|
// beta-reduce the substitution in order to simplify expressions built using
|
||||||
|
// higher-order matching
|
||||||
|
subst = beta_reduce(subst);
|
||||||
|
}
|
||||||
|
new_s.assign(i, subst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new_s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Store a result (s, rs) into r.
|
||||||
|
If m_remove_duplicates is true, then we check if r does not already contains the solution (s, rs).
|
||||||
|
In the current implementation, we only perform the check when rs is empty.
|
||||||
|
|
||||||
|
\remark \c ini_s is the initial substitution set. \c s is an extension of \c ini_s
|
||||||
|
*/
|
||||||
|
list<result> save_result(list<result> const & r, metavar_env s, residue const & rs, metavar_env const & ini_s) {
|
||||||
|
if (empty(rs) && m_ho) {
|
||||||
|
// We only do the cleanup when we don't have a residue.
|
||||||
|
// If we have a residue, we can only remove auxiliary metavariables that do not occur in rs
|
||||||
|
s = cleanup_subst(s, ini_s);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_remove_duplicates && contains_solution(r, s, rs, ini_s)) {
|
||||||
|
return r;
|
||||||
|
} else {
|
||||||
|
return cons(result(s, rs), r);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -297,6 +373,7 @@ class ho_unifier::imp {
|
||||||
bool process_meta_app(context const & ctx, expr const & a, expr const & b) {
|
bool process_meta_app(context const & ctx, expr const & a, expr const & b) {
|
||||||
lean_assert(is_meta_app(a));
|
lean_assert(is_meta_app(a));
|
||||||
lean_assert(!is_meta_app(b));
|
lean_assert(!is_meta_app(b));
|
||||||
|
m_ho = true; // mark that higher-order matching was used
|
||||||
expr f_a = arg(a, 0);
|
expr f_a = arg(a, 0);
|
||||||
lean_assert(is_metavar(f_a));
|
lean_assert(is_metavar(f_a));
|
||||||
state top_state = m_state_stack.back();
|
state top_state = m_state_stack.back();
|
||||||
|
@ -490,11 +567,12 @@ public:
|
||||||
m_env(env),
|
m_env(env),
|
||||||
m_normalizer(env, opts),
|
m_normalizer(env, opts),
|
||||||
m_type_infer(env) {
|
m_type_infer(env) {
|
||||||
m_interrupted = false;
|
m_interrupted = false;
|
||||||
m_delayed = 0;
|
m_delayed = 0;
|
||||||
m_max_solutions = get_ho_unifier_max_solutions(opts);
|
m_max_solutions = get_ho_unifier_max_solutions(opts);
|
||||||
m_use_normalizer = get_ho_unifier_use_normalizer(opts);
|
m_remove_duplicates = get_ho_unifier_remove_duplicates(opts);
|
||||||
m_use_beta = get_ho_unifier_use_beta(opts);
|
m_use_normalizer = get_ho_unifier_use_normalizer(opts);
|
||||||
|
m_use_beta = get_ho_unifier_use_beta(opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
list<result> unify(context const & ctx, expr const & a, expr const & b, metavar_env const & menv) {
|
list<result> unify(context const & ctx, expr const & a, expr const & b, metavar_env const & menv) {
|
||||||
|
@ -510,7 +588,7 @@ public:
|
||||||
unsigned cq_size = cq.size();
|
unsigned cq_size = cq.size();
|
||||||
if (cq.empty()) {
|
if (cq.empty()) {
|
||||||
// no constraints left to be solved
|
// no constraints left to be solved
|
||||||
r = save_result(r, subst_of(top_state), residue());
|
r = save_result(r, subst_of(top_state), residue(), menv);
|
||||||
num_solutions++;
|
num_solutions++;
|
||||||
m_state_stack.pop_back();
|
m_state_stack.pop_back();
|
||||||
} else {
|
} else {
|
||||||
|
@ -529,7 +607,7 @@ public:
|
||||||
residue rs;
|
residue rs;
|
||||||
for (auto c : cq)
|
for (auto c : cq)
|
||||||
rs = cons(c, rs);
|
rs = cons(c, rs);
|
||||||
r = save_result(r, subst_of(top_state), rs);
|
r = save_result(r, subst_of(top_state), rs, menv);
|
||||||
num_solutions++;
|
num_solutions++;
|
||||||
reset_delayed();
|
reset_delayed();
|
||||||
m_state_stack.pop_back();
|
m_state_stack.pop_back();
|
||||||
|
|
|
@ -47,6 +47,7 @@ public:
|
||||||
operator bool() const { return m_ptr != nullptr; }
|
operator bool() const { return m_ptr != nullptr; }
|
||||||
|
|
||||||
friend bool is_nil(list const & l) { return l.m_ptr == nullptr; }
|
friend bool is_nil(list const & l) { return l.m_ptr == nullptr; }
|
||||||
|
friend bool empty(list const & l) { return is_nil(l); }
|
||||||
friend T const & head(list const & l) { lean_assert(!is_nil(l)); return l.m_ptr->m_head; }
|
friend T const & head(list const & l) { lean_assert(!is_nil(l)); return l.m_ptr->m_head; }
|
||||||
friend list const & tail(list const & l) { lean_assert(!is_nil(l)); return l.m_ptr->m_tail; }
|
friend list const & tail(list const & l) { lean_assert(!is_nil(l)); return l.m_ptr->m_tail; }
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue