feat(library/unifier): add option 'unifier.conservative', use option by default in the calc_assistant

This commit is contained in:
Leonardo de Moura 2015-01-19 16:23:29 -08:00
parent 7149c49553
commit 2717adde94
10 changed files with 62 additions and 27 deletions

View file

@ -105,7 +105,7 @@ namespace morphism
= retraction_of f ∘ retraction_of g ∘ g ∘ f : assoc = retraction_of f ∘ retraction_of g ∘ g ∘ f : assoc
... = retraction_of f ∘ ((retraction_of g ∘ g) ∘ f) : aux ... = retraction_of f ∘ ((retraction_of g ∘ g) ∘ f) : aux
... = retraction_of f ∘ id ∘ f : {retraction_compose g} ... = retraction_of f ∘ id ∘ f : {retraction_compose g}
... = retraction_of f ∘ f : id_left f ... = retraction_of f ∘ f : {id_left f}
... = id : retraction_compose f) ... = id : retraction_compose f)
theorem composition_is_retraction [instance] (Hf : is_retraction f) (Hg : is_retraction g) theorem composition_is_retraction [instance] (Hf : is_retraction f) (Hg : is_retraction g)
@ -118,7 +118,7 @@ namespace morphism
= g ∘ f ∘ section_of f ∘ section_of g : assoc = g ∘ f ∘ section_of f ∘ section_of g : assoc
... = g ∘ (f ∘ section_of f) ∘ section_of g : aux ... = g ∘ (f ∘ section_of f) ∘ section_of g : aux
... = g ∘ id ∘ section_of g : compose_section f ... = g ∘ id ∘ section_of g : compose_section f
... = g ∘ section_of g : id_left (section_of g) ... = g ∘ section_of g : {id_left (section_of g)}
... = id : compose_section) ... = id : compose_section)
theorem composition_is_inverse [instance] (Hf : is_iso f) (Hg : is_iso g) : is_iso (g ∘ f) := theorem composition_is_inverse [instance] (Hf : is_iso f) (Hg : is_iso g) : is_iso (g ∘ f) :=

View file

@ -102,7 +102,7 @@ namespace is_equiv
have eq1 : ap f (sec a) = _, have eq1 : ap f (sec a) = _,
from calc ap f (sec a) from calc ap f (sec a)
= idp ⬝ ap f (sec a) : !concat_1p⁻¹ = idp ⬝ ap f (sec a) : !concat_1p⁻¹
... = (ret (f a) ⬝ (ret (f a)⁻¹)) ⬝ ap f (sec a) : concat_pV ... = (ret (f a) ⬝ (ret (f a)⁻¹)) ⬝ ap f (sec a) : {!concat_pV⁻¹}
... = ((ret (fgfa))⁻¹ ⬝ ap (f ∘ g) (ret (f a))) ⬝ ap f (sec a) : {!concat_pA1⁻¹} ... = ((ret (fgfa))⁻¹ ⬝ ap (f ∘ g) (ret (f a))) ⬝ ap f (sec a) : {!concat_pA1⁻¹}
... = ((ret (fgfa))⁻¹ ⬝ fgretrfa) ⬝ ap f (sec a) : {ap_compose g f _} ... = ((ret (fgfa))⁻¹ ⬝ fgretrfa) ⬝ ap f (sec a) : {ap_compose g f _}
... = (ret (fgfa))⁻¹ ⬝ (fgretrfa ⬝ ap f (sec a)) : !concat_pp_p, ... = (ret (fgfa))⁻¹ ⬝ (fgretrfa ⬝ ap f (sec a)) : !concat_pp_p,

View file

@ -268,7 +268,7 @@ namespace sigma
: ap (g a⁻¹) !transport_compose : ap (g a⁻¹) !transport_compose
... = g a⁻¹ (transport B' (ap f (sect f a)) (transport B' (ap f (sect f a)⁻¹) (g a b))) ... = g a⁻¹ (transport B' (ap f (sect f a)) (transport B' (ap f (sect f a)⁻¹) (g a b)))
: ap (λ x, g a⁻¹ (transport B' (ap f (sect f a)) (transport B' (x⁻¹) (g a b)))) (adj f a) : ap (λ x, g a⁻¹ (transport B' (ap f (sect f a)) (transport B' (x⁻¹) (g a b)))) (adj f a)
... = g a⁻¹ (g a b) : transport_pV ... = g a⁻¹ (g a b) : {!transport_pV}
... = b : sect (g a) b ... = b : sect (g a) b
end end
-- -- "rewrite ap_transport" -- -- "rewrite ap_transport"

View file

@ -246,7 +246,7 @@ section
dvd.intro dvd.intro
(calc (calc
a * -c = -(a * c) : {!neg_mul_eq_mul_neg⁻¹} a * -c = -(a * c) : {!neg_mul_eq_mul_neg⁻¹}
... = -(-b) : H' ... = -(-b) : {H'}
... = b : neg_neg))) ... = b : neg_neg)))
(assume H : a | b, (assume H : a | b,
dvd.elim H dvd.elim H
@ -254,7 +254,7 @@ section
dvd.intro dvd.intro
(calc (calc
a * -c = -(a * c) : {!neg_mul_eq_mul_neg⁻¹} a * -c = -(a * c) : {!neg_mul_eq_mul_neg⁻¹}
... = -b : H'))) ... = -b : {H'})))
theorem neg_dvd_iff_dvd : -a | b ↔ a | b := theorem neg_dvd_iff_dvd : -a | b ↔ a | b :=
iff.intro iff.intro

View file

@ -70,12 +70,12 @@ namespace fin
to_nat_of_nat (p+1) (n+1) h := calc to_nat_of_nat (p+1) (n+1) h := calc
to_nat (of_nat (p+1) (n+1) h) to_nat (of_nat (p+1) (n+1) h)
= succ (to_nat (of_nat p n _)) : rfl = succ (to_nat (of_nat p n _)) : rfl
... = succ p : to_nat_of_nat p n _ ... = succ p : {to_nat_of_nat p n _}
theorem of_nat_to_nat : ∀ {n : nat} (f : fin n) (h : to_nat f < n), of_nat (to_nat f) n h = f, theorem of_nat_to_nat : ∀ {n : nat} (f : fin n) (h : to_nat f < n), of_nat (to_nat f) n h = f,
of_nat_to_nat (fz n) h := rfl, of_nat_to_nat (fz n) h := rfl,
of_nat_to_nat (@fs n f) h := calc of_nat_to_nat (@fs n f) h := calc
of_nat (to_nat (fs f)) (succ n) h = fs (of_nat (to_nat f) n _) : rfl of_nat (to_nat (fs f)) (succ n) h = fs (of_nat (to_nat f) n _) : rfl
... = fs f : of_nat_to_nat f _ ... = fs f : {of_nat_to_nat f _}
end fin end fin

View file

@ -43,7 +43,7 @@ inductive int : Type :=
notation `` := int notation `` := int
coercion [persistent] int.of_nat coercion [persistent] int.of_nat
definition int.of_num [coercion] (n : num) : := int.of_nat (nat.of_num n) definition int.of_num [coercion] [reducible] (n : num) : := int.of_nat (nat.of_num n)
namespace int namespace int
@ -506,7 +506,7 @@ cases_on a
pmul (repr (neg_succ_of_nat m')) (repr n) = pmul (repr (neg_succ_of_nat m')) (repr n) =
(0 * n + succ m' * 0, 0 * 0 + succ m' * n) : rfl (0 * n + succ m' * 0, 0 * 0 + succ m' * n) : rfl
... = (0 + succ m' * 0, 0 * 0 + succ m' * n) : zero_mul ... = (0 + succ m' * 0, 0 * 0 + succ m' * n) : zero_mul
... = (0 + succ m' * 0, succ m' * n) : nat.zero_add ... = (0 + succ m' * 0, succ m' * n) : {!nat.zero_add}
... = repr (mul (neg_succ_of_nat m') n) : repr_neg_of_nat)⁻¹) ... = repr (mul (neg_succ_of_nat m') n) : repr_neg_of_nat)⁻¹)
(take n', (take n',
(calc (calc

View file

@ -45,7 +45,7 @@ theorem add_div_left {x z : } (H : z > 0) : (x + z) div z = succ (x div z) :=
calc calc
(x + z) div z = if 0 < z ∧ z ≤ x + z then (x + z - z) div z + 1 else 0 : !divide_def (x + z) div z = if 0 < z ∧ z ≤ x + z then (x + z - z) div z + 1 else 0 : !divide_def
... = (x + z - z) div z + 1 : if_pos (and.intro H (le_add_left z x)) ... = (x + z - z) div z + 1 : if_pos (and.intro H (le_add_left z x))
... = succ (x div z) : add_sub_cancel ... = succ (x div z) : {!add_sub_cancel}
theorem add_div_right {x z : } (H : x > 0) : (x + z) div x = succ (z div x) := theorem add_div_right {x z : } (H : x > 0) : (x + z) div x = succ (z div x) :=
!add.comm ▸ add_div_left H !add.comm ▸ add_div_left H

View file

@ -154,7 +154,7 @@ constraint mk_calc_proof_cnstr(environment const & env, options const & opts,
fn(e); fn(e);
} }
auto try_alternative = [&](expr const & e, expr const & e_type, constraint_seq fcs) { auto try_alternative = [&](expr const & e, expr const & e_type, constraint_seq fcs, bool conservative) {
justification new_j = mk_type_mismatch_jst(e, e_type, meta_type); justification new_j = mk_type_mismatch_jst(e, e_type, meta_type);
if (!tc->is_def_eq(e_type, meta_type, new_j, fcs)) if (!tc->is_def_eq(e_type, meta_type, new_j, fcs))
throw unifier_exception(new_j, s); throw unifier_exception(new_j, s);
@ -166,8 +166,9 @@ constraint mk_calc_proof_cnstr(environment const & env, options const & opts,
cs_buffer.push_back(mk_eq_cnstr(meta, e, j, relax)); cs_buffer.push_back(mk_eq_cnstr(meta, e, j, relax));
unifier_config new_cfg(cfg); unifier_config new_cfg(cfg);
new_cfg.m_discard = false; new_cfg.m_discard = false;
unify_result_seq seq = unify(env, cs_buffer.size(), cs_buffer.data(), ngen, substitution(), new_cfg); new_cfg.m_conservative = conservative;
unify_result_seq seq = unify(env, cs_buffer.size(), cs_buffer.data(), ngen, substitution(), new_cfg);
auto p = seq.pull(); auto p = seq.pull();
lean_assert(p); lean_assert(p);
substitution new_s = p->first.first; substitution new_s = p->first.first;
@ -183,30 +184,35 @@ constraint mk_calc_proof_cnstr(environment const & env, options const & opts,
}; };
if (!get_elaborator_calc_assistant(opts)) { if (!get_elaborator_calc_assistant(opts)) {
return try_alternative(e, e_type, new_cs); bool conservative = false;
return try_alternative(e, e_type, new_cs, conservative);
} else { } else {
std::unique_ptr<throwable> saved_ex; std::unique_ptr<throwable> saved_ex;
try { try {
return try_alternative(e, e_type, new_cs); bool conservative = false;
return try_alternative(e, e_type, new_cs, conservative);
} catch (exception & ex) { } catch (exception & ex) {
saved_ex.reset(ex.clone()); saved_ex.reset(ex.clone());
} }
constraint_seq symm_cs = new_cs; constraint_seq symm_cs = new_cs;
auto symm = apply_symmetry(env, ctx, ngen, tc, e, e_type, symm_cs, g); auto symm = apply_symmetry(env, ctx, ngen, tc, e, e_type, symm_cs, g);
if (symm) { if (symm) {
try { return try_alternative(symm->first, symm->second, symm_cs); } catch (exception &) {} bool conservative = false;
try { return try_alternative(symm->first, symm->second, symm_cs, conservative); } catch (exception &) {}
} }
constraint_seq subst_cs = new_cs; constraint_seq subst_cs = new_cs;
if (auto subst = apply_subst(env, ctx, ngen, tc, e, e_type, meta_type, subst_cs, g)) { if (auto subst = apply_subst(env, ctx, ngen, tc, e, e_type, meta_type, subst_cs, g)) {
try { return try_alternative(subst->first, subst->second, subst_cs); } catch (exception&) {} bool conservative = true;
try { return try_alternative(subst->first, subst->second, subst_cs, conservative); } catch (exception&) {}
} }
if (symm) { if (symm) {
constraint_seq subst_cs = symm_cs; constraint_seq subst_cs = symm_cs;
bool conservative = true;
if (auto subst = apply_subst(env, ctx, ngen, tc, symm->first, symm->second, meta_type, subst_cs, g)) { if (auto subst = apply_subst(env, ctx, ngen, tc, symm->first, symm->second, meta_type, subst_cs, g)) {
try { return try_alternative(subst->first, subst->second, subst_cs); } catch (exception&) {} try { return try_alternative(subst->first, subst->second, subst_cs, conservative); } catch (exception&) {}
} }
} }

View file

@ -45,10 +45,15 @@ Author: Leonardo de Moura
#define LEAN_DEFAULT_UNIFIER_EXPENSIVE_CLASSES false #define LEAN_DEFAULT_UNIFIER_EXPENSIVE_CLASSES false
#endif #endif
#ifndef LEAN_DEFAULT_UNIFIER_CONSERVATIVE
#define LEAN_DEFAULT_UNIFIER_CONSERVATIVE false
#endif
namespace lean { namespace lean {
static name * g_unifier_max_steps = nullptr; static name * g_unifier_max_steps = nullptr;
static name * g_unifier_computation = nullptr; static name * g_unifier_computation = nullptr;
static name * g_unifier_expensive_classes = nullptr; static name * g_unifier_expensive_classes = nullptr;
static name * g_unifier_conservative = nullptr;
unsigned get_unifier_max_steps(options const & opts) { unsigned get_unifier_max_steps(options const & opts) {
return opts.get_unsigned(*g_unifier_max_steps, LEAN_DEFAULT_UNIFIER_MAX_STEPS); return opts.get_unsigned(*g_unifier_max_steps, LEAN_DEFAULT_UNIFIER_MAX_STEPS);
@ -62,12 +67,17 @@ bool get_unifier_expensive_classes(options const & opts) {
return opts.get_bool(*g_unifier_expensive_classes, LEAN_DEFAULT_UNIFIER_EXPENSIVE_CLASSES); return opts.get_bool(*g_unifier_expensive_classes, LEAN_DEFAULT_UNIFIER_EXPENSIVE_CLASSES);
} }
bool get_unifier_conservative(options const & opts) {
return opts.get_bool(*g_unifier_conservative, LEAN_DEFAULT_UNIFIER_CONSERVATIVE);
}
unifier_config::unifier_config(bool use_exceptions, bool discard): unifier_config::unifier_config(bool use_exceptions, bool discard):
m_use_exceptions(use_exceptions), m_use_exceptions(use_exceptions),
m_max_steps(LEAN_DEFAULT_UNIFIER_MAX_STEPS), m_max_steps(LEAN_DEFAULT_UNIFIER_MAX_STEPS),
m_computation(LEAN_DEFAULT_UNIFIER_COMPUTATION), m_computation(LEAN_DEFAULT_UNIFIER_COMPUTATION),
m_expensive_classes(LEAN_DEFAULT_UNIFIER_EXPENSIVE_CLASSES), m_expensive_classes(LEAN_DEFAULT_UNIFIER_EXPENSIVE_CLASSES),
m_discard(discard) { m_discard(discard),
m_conservative(LEAN_DEFAULT_UNIFIER_CONSERVATIVE) {
m_cheap = false; m_cheap = false;
m_ignore_context_check = false; m_ignore_context_check = false;
} }
@ -77,7 +87,8 @@ unifier_config::unifier_config(options const & o, bool use_exceptions, bool disc
m_max_steps(get_unifier_max_steps(o)), m_max_steps(get_unifier_max_steps(o)),
m_computation(get_unifier_computation(o)), m_computation(get_unifier_computation(o)),
m_expensive_classes(get_unifier_expensive_classes(o)), m_expensive_classes(get_unifier_expensive_classes(o)),
m_discard(discard) { m_discard(discard),
m_conservative(get_unifier_conservative(o)) {
m_cheap = false; m_cheap = false;
m_ignore_context_check = false; m_ignore_context_check = false;
} }
@ -419,6 +430,11 @@ struct unifier_fn {
m_tc[1] = m_tc[0]; m_tc[1] = m_tc[0];
m_flex_rigid_tc = m_tc[0]; m_flex_rigid_tc = m_tc[0];
m_config.m_computation = false; m_config.m_computation = false;
} else if (m_config.m_conservative) {
m_tc[0] = mk_type_checker(env, m_ngen.mk_child(), false, OpaqueIfNotReducibleOn);
m_tc[1] = m_tc[0];
m_flex_rigid_tc = m_tc[0];
m_config.m_computation = false;
} else { } else {
m_tc[0] = mk_type_checker(env, m_ngen.mk_child(), false); m_tc[0] = mk_type_checker(env, m_ngen.mk_child(), false);
m_tc[1] = mk_type_checker(env, m_ngen.mk_child(), true); m_tc[1] = mk_type_checker(env, m_ngen.mk_child(), true);
@ -1325,7 +1341,8 @@ struct unifier_fn {
justification a; justification a;
bool relax = relax_main_opaque(c); bool relax = relax_main_opaque(c);
if (!m_config.m_cheap && (m_config.m_computation || module::is_definition(m_env, d.get_name()) || is_reducible_on(m_env, d.get_name()))) { if (!m_config.m_cheap && !m_config.m_conservative &&
(m_config.m_computation || module::is_definition(m_env, d.get_name()) || is_reducible_on(m_env, d.get_name()))) {
// add case_split for t =?= s // add case_split for t =?= s
a = mk_assumption_justification(m_next_assumption_idx); a = mk_assumption_justification(m_next_assumption_idx);
add_case_split(std::unique_ptr<case_split>(new delta_unfold_case_split(*this, j, c))); add_case_split(std::unique_ptr<case_split>(new delta_unfold_case_split(*this, j, c)));
@ -1871,7 +1888,7 @@ struct unifier_fn {
*/ */
bool use_flex_rigid_whnf_split(expr const & lhs, expr const & rhs) { bool use_flex_rigid_whnf_split(expr const & lhs, expr const & rhs) {
lean_assert(is_meta(lhs)); lean_assert(is_meta(lhs));
if (m_config.m_cheap) if (m_config.m_cheap || m_config.m_conservative)
return false; return false;
if (m_config.m_computation) if (m_config.m_computation)
return true; // if unifier.computation is true, we always consider the additional whnf split return true; // if unifier.computation is true, we always consider the additional whnf split
@ -2514,12 +2531,15 @@ void initialize_unifier() {
g_unifier_max_steps = new name{"unifier", "max_steps"}; g_unifier_max_steps = new name{"unifier", "max_steps"};
g_unifier_computation = new name{"unifier", "computation"}; g_unifier_computation = new name{"unifier", "computation"};
g_unifier_expensive_classes = new name{"unifier", "expensive_classes"}; g_unifier_expensive_classes = new name{"unifier", "expensive_classes"};
g_unifier_conservative = new name{"unifier", "conservative"};
register_unsigned_option(*g_unifier_max_steps, LEAN_DEFAULT_UNIFIER_MAX_STEPS, "(unifier) maximum number of steps"); register_unsigned_option(*g_unifier_max_steps, LEAN_DEFAULT_UNIFIER_MAX_STEPS, "(unifier) maximum number of steps");
register_bool_option(*g_unifier_computation, LEAN_DEFAULT_UNIFIER_COMPUTATION, register_bool_option(*g_unifier_computation, LEAN_DEFAULT_UNIFIER_COMPUTATION,
"(unifier) always case-split on reduction/computational steps when solving flex-rigid and delta-delta constraints"); "(unifier) always case-split on reduction/computational steps when solving flex-rigid and delta-delta constraints");
register_bool_option(*g_unifier_expensive_classes, LEAN_DEFAULT_UNIFIER_EXPENSIVE_CLASSES, register_bool_option(*g_unifier_expensive_classes, LEAN_DEFAULT_UNIFIER_EXPENSIVE_CLASSES,
"(unifier) use \"full\" higher-order unification when solving class instances"); "(unifier) use \"full\" higher-order unification when solving class instances");
register_bool_option(*g_unifier_conservative, LEAN_DEFAULT_UNIFIER_CONSERVATIVE,
"(unifier) unfolds only constants marked as reducible, avoid expensive case-splits (it is faster but less complete)");
g_dont_care_cnstr = new constraint(mk_eq_cnstr(expr(), expr(), justification(), false)); g_dont_care_cnstr = new constraint(mk_eq_cnstr(expr(), expr(), justification(), false));
g_tmp_prefix = new name(name::mk_internal_unique_name()); g_tmp_prefix = new name(name::mk_internal_unique_name());
@ -2531,5 +2551,6 @@ void finalize_unifier() {
delete g_unifier_max_steps; delete g_unifier_max_steps;
delete g_unifier_computation; delete g_unifier_computation;
delete g_unifier_expensive_classes; delete g_unifier_expensive_classes;
delete g_unifier_conservative;
} }
} }

View file

@ -40,7 +40,15 @@ struct unifier_config {
// If m_discard is true, then constraints that cannot be solved are discarded (or incomplete methods are used) // If m_discard is true, then constraints that cannot be solved are discarded (or incomplete methods are used)
// If m_discard is false, unify returns the set of constraints that could not be handled. // If m_discard is false, unify returns the set of constraints that could not be handled.
bool m_discard; bool m_discard;
// If m_conservative is true, then the following restrictions are imposed:
// - All constants that are not marked as reducible as treated as
// opaque.
// - Disables case-split on delta-delta constraints.
// - Disables reduction case-split on flex-rigid constraints.
// Default is m_conservative == false
bool m_conservative;
// If m_cheap is true, then expensive case-analysis is not performed (e.g., delta). // If m_cheap is true, then expensive case-analysis is not performed (e.g., delta).
// It is more restrictive than m_conservative
// Default is m_cheap == false // Default is m_cheap == false
bool m_cheap; bool m_cheap;
// If m_ignore_context_check == true, then occurs-check is skipped. // If m_ignore_context_check == true, then occurs-check is skipped.