feat(library/tactic/smt/congruence_closure): propagate disequalities

This commit is contained in:
Leonardo de Moura 2017-01-03 14:53:27 -08:00
parent 34073c6f32
commit 554cef1d36
6 changed files with 108 additions and 0 deletions

View file

@ -109,3 +109,9 @@ eq_false_intro (λ ha, absurd ha (eq.mpr h trivial))
cc_config.em is tt. -/
lemma eq_true_of_not_eq_false {a : Prop} (h : (not a) = false) : a = true :=
eq_true_intro (classical.by_contradiction (λ hna, eq.mp h hna))
lemma ne_of_eq_of_ne {α : Type u} {a b c : α} (h₁ : a = b) (h₂ : b ≠ c) : a ≠ c :=
h₁^.symm ▸ h₂
lemma ne_of_ne_of_eq {α : Type u} {a b c : α} (h₁ : a ≠ b) (h₂ : b = c) : a ≠ c :=
h₂ ▸ h₁

View file

@ -27,6 +27,15 @@ optional<expr> mk_constructor_eq_constructor_inconsistency_proof(type_context &
return some_expr(pr);
}
optional<expr> mk_constructor_ne_constructor_proof(type_context & ctx, expr const & e1, expr const & e2) {
type_context::tmp_locals locals(ctx);
expr h = locals.push_local("_h", mk_eq(ctx, e1, e2));
if (optional<expr> pr = mk_constructor_eq_constructor_inconsistency_proof(ctx, e1, e2, h))
return some_expr(locals.mk_lambda(*pr));
else
return none_expr();
}
optional<expr> mk_constructor_eq_constructor_implied_core(type_context & ctx, expr const & e1, expr const & e2, expr const & h, buffer<expr_pair> & implied_pairs) {
// TODO(Leo, Daniel): add support for generalized inductive datatypes
// TODO(Leo): add a definition for this proof at inductive datatype declaration time?

View file

@ -9,6 +9,11 @@ namespace lean {
/* If e1 and e2 are constructor applications, the constructors are different, and (h : e1 = e2), then
return a proof for false. */
optional<expr> mk_constructor_eq_constructor_inconsistency_proof(type_context & ctx, expr const & e1, expr const & e2, expr const & h);
/* If e1 and e2 are constructor applications, the constructors are different, then
return a proof for not (e1 = e2). */
optional<expr> mk_constructor_ne_constructor_proof(type_context & ctx, expr const & e1, expr const & e2);
/* If e1 and e2 are constructor applications for the same constructor, and (h : e1 = e2), then
return true, and store the triples (a_i, b_i, hab_i) at result, where (hab_i : a_i = b_i)
and a_i (b_i) are constructor fields of e1 (e2). */

View file

@ -1460,6 +1460,51 @@ bool congruence_closure::may_propagate(expr const & e) {
is_iff(e) || is_and(e) || is_or(e) || is_not(e) || is_arrow(e) || is_ite(e);
}
static name * g_ne_of_eq_of_ne = nullptr;
static name * g_ne_of_ne_of_eq = nullptr;
optional<expr> congruence_closure::mk_ne_of_eq_of_ne(expr const & a, expr const & a1, expr const & a1_ne_b) {
lean_assert(is_eqv(a, a1));
if (a == a1)
return some_expr(a1_ne_b);
auto a_eq_a1 = get_eq_proof(a, a1);
if (!a_eq_a1) return none_expr(); // failed to build proof
return some_expr(mk_app(m_ctx, *g_ne_of_eq_of_ne, 6, *a_eq_a1, a1_ne_b));
}
optional<expr> congruence_closure::mk_ne_of_ne_of_eq(expr const & a_ne_b1, expr const & b1, expr const & b) {
lean_assert(is_eqv(b, b1));
if (b == b1)
return some_expr(a_ne_b1);
auto b1_eq_b = get_eq_proof(b1, b);
if (!b1_eq_b) return none_expr(); // failed to build proof
return some_expr(mk_app(m_ctx, *g_ne_of_ne_of_eq, 6, a_ne_b1, *b1_eq_b));
}
void congruence_closure::propagate_eq_up(expr const & e) {
/* Remark: the positive case is implemented at check_eq_true
for any reflexive relation. */
expr a, b;
lean_verify(is_eq(e, a, b));
expr ra = get_root(a);
expr rb = get_root(b);
if (ra != rb) {
optional<expr> ra_ne_rb;
if (is_interpreted_value(ra) && is_interpreted_value(rb)) {
ra_ne_rb = mk_val_ne_proof(m_ctx, ra, rb);
} else {
if (optional<name> c1 = is_constructor_app(env(), ra))
if (optional<name> c2 = is_constructor_app(env(), rb))
if (c1 && c2 && *c1 != *c2)
ra_ne_rb = mk_constructor_ne_constructor_proof(m_ctx, ra, rb);
}
if (ra_ne_rb)
if (auto a_ne_rb = mk_ne_of_eq_of_ne(a, ra, *ra_ne_rb))
if (auto a_ne_b = mk_ne_of_ne_of_eq(*a_ne_rb, rb, b))
push_eq(e, mk_false(), mk_eq_false_intro(m_ctx, *a_ne_b));
}
}
void congruence_closure::propagate_up(expr const & e) {
if (m_state.m_inconsistent) return;
if (is_iff(e)) {
@ -1474,6 +1519,8 @@ void congruence_closure::propagate_up(expr const & e) {
propagate_imp_up(e);
} else if (is_ite(e)) {
propagate_ite_up(e);
} else if (is_eq(e)) {
propagate_eq_up(e);
}
}
@ -1957,6 +2004,9 @@ void initialize_congruence_closure() {
g_eq_false_of_not_eq_true = new expr(mk_constant("eq_false_of_not_eq_true"));
g_eq_true_of_not_eq_false = new expr(mk_constant("eq_true_of_not_eq_false"));
g_ne_of_eq_of_ne = new name("ne_of_eq_of_ne");
g_ne_of_ne_of_eq = new name("ne_of_ne_of_eq");
}
void finalize_congruence_closure() {
@ -2004,5 +2054,8 @@ void finalize_congruence_closure() {
delete g_eq_false_of_not_eq_true;
delete g_eq_true_of_not_eq_false;
delete g_ne_of_eq_of_ne;
delete g_ne_of_ne_of_eq;
}
}

View file

@ -230,6 +230,9 @@ private:
void propagate_not_up(expr const & e);
void propagate_imp_up(expr const & e);
void propagate_ite_up(expr const & e);
optional<expr> mk_ne_of_eq_of_ne(expr const & a, expr const & a1, expr const & a1_ne_b);
optional<expr> mk_ne_of_ne_of_eq(expr const & a_ne_b1, expr const & b1, expr const & b);
void propagate_eq_up(expr const & e);
void propagate_up(expr const & e);
void propagate_and_down(expr const & e);
void propagate_or_down(expr const & e);

View file

@ -16,3 +16,35 @@ by using_smt $ return ()
example (a b c d : nat) : a ≠ d → a = b ∧ b = c → a = c :=
by using_smt $ return ()
set_option pp.all false
example (f : nat → nat) (a b c : nat) :
f a = 0 →
f b = 1 →
f a = f b f a = c →
c = 0 :=
by using_smt $ return()
example (f : nat → nat) (a b c d e: nat) :
f a = 0 →
f a = d →
f b = e →
f b = 1 →
d = e f a = c →
c = 0 :=
by using_smt $ return()
example (f : bool → bool) (a b c : bool) :
f a = ff →
f b = tt →
f a = f b f a = c →
c = ff :=
by using_smt $ return()
example (f : list nat → list nat) (a b c : list nat) :
f a = [] →
f b = [0, 1] →
f a = f b f a = c →
c = [] :=
by using_smt $ return()