Zulip Chat Archive
Stream: Is there code for X?
Topic: finset proof
Patrick Thomas (May 10 2022 at 05:41):
I was wondering if anyone might have advice on proving this? I think I might make progress if I can get it to a point where I can use something similar to finset.bUnion_insert
?
import data.finset
lemma finset_bUnion_sdiff
{T : Type} [decidable_eq T]
(x x' : T)
(S : finset T)
(f : T → finset T)
(h1 : f x = {x'}) :
(finset.bUnion S f) \ {x'} = (finset.bUnion (S \ {x}) f) \ {x'} :=
begin
by_cases x ∈ S,
{
sorry
},
{
congr, symmetry, exact finset.sdiff_singleton_not_mem_eq_self S h,
}
end
Eric Wieser (May 10 2022 at 06:09):
Does docs#finset.bUnion_erase exist?
Stuart Presnell (May 10 2022 at 09:25):
Not particularly elegant, but this does the job:
lemma finset_bUnion_sdiff
{T : Type} [decidable_eq T]
(x x' : T)
(S : finset T)
(f : T → finset T)
(h1 : f x = {x'}) :
(finset.bUnion S f) \ {x'} = (finset.bUnion (S \ {x}) f) \ {x'} :=
begin
by_cases hxS : x ∈ S,
{ ext,
simp only [finset.mem_sdiff, finset.mem_bUnion, and.congr_left_iff, finset.mem_singleton],
intros ha,
split,
{ rintros ⟨i, ⟨hi1, hi2⟩⟩,
refine ⟨i, ⟨⟨hi1, _⟩, hi2⟩⟩,
contrapose! ha,
subst ha,
exact finset.mem_singleton.1 (h1 ▸ hi2) },
{ rintros ⟨i, ⟨hi1, hi2⟩⟩, exact ⟨i, ⟨hi1.1, hi2⟩⟩ } },
{ congr, symmetry, exact finset.sdiff_singleton_not_mem_eq_self S hxS }
end
Patrick Thomas (May 10 2022 at 16:47):
Thank you!
Patrick Johnson (May 10 2022 at 17:41):
Simpler proof:
import tactic
lemma finset_bUnion_sdiff
{T : Type} [decidable_eq T]
(x x' : T)
(S : finset T)
(f : T → finset T)
(h1 : f x = {x'}) :
(finset.bUnion S f) \ {x'} = (finset.bUnion (S \ {x}) f) \ {x'} :=
begin
ext y, simp, intro, congr', ext z,
have : y ∈ f z → z ≠ x := by rintro _ rfl; simp * at *,
tauto,
end
Kyle Miller (May 10 2022 at 18:13):
Taking care of the nonterminal simp and making it more mathliby:
lemma finset_bUnion_sdiff
{T : Type} [decidable_eq T]
(x x' : T)
(S : finset T)
(f : T → finset T)
(h1 : f x = {x'}) :
(finset.bUnion S f) \ {x'} = (finset.bUnion (S \ {x}) f) \ {x'} :=
begin
ext y,
simp only [finset.mem_sdiff, finset.mem_bUnion, exists_prop,
finset.mem_singleton, and.congr_left_iff],
intro,
congr' with z,
have : y ∈ f z → z ≠ x,
{ rintro h' rfl,
simpa only [*, finset.mem_singleton] using h', },
tauto,
end
By the way, congr'
takes a with
clause for doing ext
.
Kevin Buzzard (May 10 2022 at 18:38):
Using a non-terminal simp
is supposed to be a code smell, but sometimes it's really handy. I understand how simp only [finset.mem_sdiff, finset.mem_bUnion, exists_prop, finset.mem_singleton, and.congr_left_iff],
is easier to maintain, but it's a darn sight uglier :-/ Before squeeze_simp
we used to write suffices : [result of simp], by simpa
; probably this is often shorter than what we do nowadays, as well as being easier to read.
Kevin Buzzard (May 10 2022 at 18:41):
With that way of doing it, the proof looks like this:
begin
ext y,
suffices : y ≠ x' →
((∃ (a : T), a ∈ S ∧ y ∈ f a) ↔ ∃ (a : T), (a ∈ S ∧ a ≠ x) ∧ y ∈ f a),
{ simpa },
intro,
congr' with z,
have : y ∈ f z → z ≠ x,
{ rintro h' rfl,
simpa only [*, finset.mem_singleton] using h', },
tauto,
end
This way you get a glimpse at what simp
actually did, but you have to copy out the goal :-(
Patrick Johnson (May 10 2022 at 19:07):
I think the trade-off between readability and maintainability is unavoidable in this case. If we use simp
to modify the goal, we have to specify either what the goal should look like after simplification, or what simplification rules are allowed to be used. Otherwise, adding another @[simp]
lemma may break the proof.
Kyle Miller (May 10 2022 at 19:08):
I've been thinking that we should allow this type of nonterminal simp:
begin
ext y,
simp,
match_target ¬y = x' →
((∃ (a : T), a ∈ S ∧ y ∈ f a) ↔ ∃ (a : T), (a ∈ S ∧ ¬a = x) ∧ y ∈ f a),
intro,
congr' with z,
have : y ∈ f z → z ≠ x,
{ rintro h' rfl,
simpa only [*, finset.mem_singleton] using h', },
tauto,
end
Kyle Miller (May 10 2022 at 19:09):
It's a lot more readable than the suffices/simpa pattern in my opinion
Patrick Johnson (May 10 2022 at 19:42):
simp
is not aware of the match_target
's argument, while simpa ... using
is aware of what the result should be. Adding a new @[simp]
lemma may break simp, match_target
, but not simp only
or suffices ... simpa
Kyle Miller (May 10 2022 at 19:44):
simpa
doesn't actually use what the result should be -- the algorithm is more-or-less "simp both the target and the specified hypothesis then use exact
"
Kyle Miller (May 10 2022 at 19:46):
A reason non-terminal simp
is not allowed is that you get brittle code from not knowing what the intermediate goal is supposed to be. When you have a terminal simp
, you at least know what the goal is supposed to be, which makes it much easier to fix things when the proof breaks when the simp set changes.
Kyle Miller (May 10 2022 at 19:48):
If the simp set changes, the suffices/simpa
construct can also break (if it's not a simpa only
), but having the explicit goal right there in the tactic proof makes fixing it easier.
Patrick Thomas (May 15 2022 at 02:44):
Would the previous and the following be worth adding to mathlib? Did I miss simpler proofs from already existing theorems?
import data.finset
example
{T : Type}
[decidable_eq T]
(x : T)
(s : finset T)
(f : T → finset T)
(h1 : ∀ y : T, y ∈ s → x ∉ (f y)) :
(finset.bUnion s f) \ {x} = finset.bUnion s f :=
begin
ext1, finish
end
example
{T : Type}
[decidable_eq T]
(s t : finset T)
(f : T → finset T) :
(finset.bUnion s f) ∪ (finset.bUnion t f) = finset.bUnion (s ∪ t) f :=
begin
ext1, simp only [finset.mem_union, finset.mem_bUnion, exists_prop], split, finish, finish
end
example
{T : Type}
[decidable_eq T]
{n : ℕ}
(g : T → finset T)
(f : fin n → finset T) :
finset.bUnion finset.univ (fun i : fin n, finset.bUnion (f i) g) =
finset.bUnion (finset.bUnion finset.univ (fun i : fin n, (f i))) g :=
begin
ext1, simp only [finset.mem_bUnion, finset.mem_univ, exists_prop, exists_true_left], tauto
end
example
{T : Type}
[decidable_eq T]
(x : T)
(S : finset T)
(f : T → finset T) :
finset.bUnion ({x} ∪ S) f = (finset.bUnion S f) ∪ f x :=
begin
ext, simp only [finset.mem_bUnion, finset.mem_union, finset.mem_singleton, exists_prop],
split, finish, finish,
end
Kyle Miller (May 15 2022 at 04:21):
@Patrick Thomas I think some versions of these should find their way into mathlib. I had a go at generalizing them and adding some other missing lemmas.
import data.finset
namespace finset
variables {α : Type*} {β : Type*} {γ : Type*}
lemma mem_ite (x : α) (p : Prop) [decidable p] (s s' : finset α) :
x ∈ (if p then s else s') ↔ (p → x ∈ s) ∧ (¬ p → x ∈ s') :=
by split_ifs with h; simp [h]
lemma bUnion_sdiff [decidable_eq α] [decidable_eq β]
(s : finset α) (t : α → finset β) (s' : finset β) :
s.bUnion t \ s' = s.bUnion (λ x, t x \ s') :=
by { ext, simp only [mem_sdiff, mem_bUnion, exists_prop], tauto }
lemma bUnion_filter [decidable_eq α] [decidable_eq β]
(s : finset α) (t : α → finset β) (p : α → Prop) [decidable_pred p] :
(s.filter p).bUnion t = s.bUnion (λ x, if p x then t x else ∅) :=
begin
ext,
simp only [mem_ite, imp_iff_not_or, or_and_distrib_right, mem_bUnion, mem_filter, exists_prop,
not_mem_empty, not_not, or_false, not_and_self, false_or],
tauto,
end
lemma sdiff_singleton_bUnion [decidable_eq α] [decidable_eq β]
(s : finset α) (t : α → finset β)
(x : α) (s' : finset β)
(h : t x = s') :
(s \ {x}).bUnion t \ s' = s.bUnion t \ s' :=
begin
rw [← h, bUnion_sdiff, bUnion_sdiff, sdiff_eq_filter s, bUnion_filter],
congr',
ext y,
suffices : (a ∉ t y ∨ a ∈ t x) ∨ ¬y = x,
{ simpa [mem_ite, imp_false, imp_iff_not_or, and_or_distrib_left] },
rw [or_comm, ← imp_iff_not_or],
rintro rfl,
apply em',
end
lemma bUnion_union [decidable_eq α] [decidable_eq β]
(s s' : finset α) (t : α → finset β) :
(s ∪ s').bUnion t = s.bUnion t ∪ s'.bUnion t :=
by { ext, simp [or_and_distrib_right, exists_or_distrib] }
lemma bUnion_sdiff_of_forall_disjoint [decidable_eq β]
(s : finset α) (t : α → finset β) (s' : finset β)
(h : ∀ y : α, y ∈ s → disjoint (t y) s') :
(s.bUnion t) \ s' = s.bUnion t :=
by simpa [sdiff_eq_self_iff_disjoint, disjoint_bUnion_left]
-- Maybe this might be somewhat too specialized?
lemma bUnion_sdiff_singleton_of_forall_not_mem [decidable_eq β]
(s : finset α) (x : β) (t : α → finset β)
(h : ∀ y : α, y ∈ s → x ∉ t y) :
(s.bUnion t) \ {x} = s.bUnion t :=
bUnion_sdiff_of_forall_disjoint s t _ (by simpa [disjoint_singleton_right])
end finset
Kyle Miller (May 15 2022 at 04:21):
Here are then your specializations:
-- This one is `finset.bUnion_bUnion`
example
{T : Type}
[decidable_eq T]
{n : ℕ}
(g : T → finset T)
(f : fin n → finset T) :
finset.bUnion finset.univ (fun i : fin n, finset.bUnion (f i) g) =
finset.bUnion (finset.bUnion finset.univ (fun i : fin n, (f i))) g :=
(finset.bUnion_bUnion _ _ _).symm
-- This one is essentially `finset.bUnion_union`
example
{T : Type}
[decidable_eq T]
(x : T)
(S : finset T)
(f : T → finset T) :
finset.bUnion ({x} ∪ S) f = (finset.bUnion S f) ∪ f x :=
by simp [finset.bUnion_union, finset.union_comm]
example
{T : Type} [decidable_eq T]
(x x' : T)
(S : finset T)
(f : T → finset T)
(h1 : f x = {x'}) :
(finset.bUnion S f) \ {x'} = (finset.bUnion (S \ {x}) f) \ {x'} :=
(finset.sdiff_singleton_bUnion _ _ _ _ h1).symm
Kyle Miller (May 15 2022 at 04:22):
By the way, tactic#finish is no longer used for mathlib since it's not being ported to Lean 4.
Patrick Thomas (May 15 2022 at 04:37):
Thank you!
Kyle Miller (May 15 2022 at 04:45):
Here's a generalization of sdiff_singleton_bUnion
:
lemma and_self_imp {p q : Prop} : p ∧ (p → q) ↔ p ∧ q := by tauto
lemma finset.sdiff_bUnion_sdiff [decidable_eq α] [decidable_eq β]
(s s' : finset α) (t : α → finset β) :
(s \ s').bUnion t \ s'.bUnion t = s.bUnion t \ s'.bUnion t :=
begin
simp only [sdiff_eq_filter, bUnion_filter, filter_bUnion, ite_not, mem_bUnion,
exists_prop, not_exists, not_and],
congr',
ext,
simp only [mem_ite, imp_false, and_self_imp, imp_not_comm, mem_filter, not_mem_empty,
and.congr_left_iff, and_iff_right_iff_imp],
intro h,
exact h x,
end
Patrick Thomas (May 18 2022 at 02:57):
Thank you! What would be the next step to get these into mathlib?
Last updated: Dec 20 2023 at 11:08 UTC