feat(library/tactic/smt/congruence_closure): propagate disequalities
This commit is contained in:
parent
34073c6f32
commit
554cef1d36
6 changed files with 108 additions and 0 deletions
|
|
@ -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₁
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -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). */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue