Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - feat(Set): API for subset restriction and coercion to/from subtypes #9940

Closed
wants to merge 45 commits into from
Closed
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
5728467
First prototype of api for subste restrictions
miguelmarco Jan 20, 2024
971becf
Implement coercion for sets
miguelmarco Jan 21, 2024
6a12b09
Change to split operations
Jan 22, 2024
e08c6bc
Add arrow-up notation in the infoview
miguelmarco Jan 22, 2024
1975bf6
Fix linting issues
miguelmarco Jan 23, 2024
4ab3575
Fix import and remove repeated simp lemmas
miguelmarco Jan 23, 2024
ae9d2eb
Merge remote-tracking branch 'origin/master' into subset
winstonyin Jan 25, 2024
c70e7f1
address style changes from review
miguelmarco Jan 26, 2024
84caaac
Merge branch 'subset' of github.com:leanprover-community/mathlib4 int…
miguelmarco Jan 26, 2024
7164b8a
fix whitespaces
miguelmarco Jan 26, 2024
719245c
define setRestrict as inverse image of coercion
miguelmarco Jan 26, 2024
30d5bb6
Fix linting
miguelmarco Jan 26, 2024
b5b8167
Add lemma expressing subgroupOf as subset
miguelmarco Jan 27, 2024
04d6ea6
Add comment in subgroupOf doctest
miguelmarco Jan 28, 2024
5d3225e
Address reviewer's suggestions.
miguelmarco Jan 29, 2024
2fade3f
style, golf
winstonyin Jan 29, 2024
62de577
style
winstonyin Jan 29, 2024
a2c18ab
Switch to notation
miguelmarco Feb 3, 2024
061639a
Merge branch 'subset' of github.com:leanprover-community/mathlib4 int…
miguelmarco Feb 3, 2024
6f03f51
Unmark simp lemmas that are not in simp normal form
miguelmarco Feb 3, 2024
19135ec
Use preimage_val in lemmas names, and lock down notation.
miguelmarco Feb 4, 2024
2c6a5da
Remove redundant lemma
miguelmarco Feb 4, 2024
1d4a7ca
Address reviewer's comments
miguelmarco Feb 8, 2024
c56a2d1
golfing as suggested by reviewer
miguelmarco Feb 8, 2024
541411f
Move ↑ notation to Data.Set.Functor
miguelmarco Feb 9, 2024
5355712
Fix namespace
miguelmarco Feb 9, 2024
6155396
Change coe names to image_val
miguelmarco Feb 9, 2024
7763f33
golf some proofs
eric-wieser Feb 10, 2024
3775fa8
fix universes
eric-wieser Feb 10, 2024
09e1d4f
even more golfing
eric-wieser Feb 10, 2024
90e8caa
whoops
eric-wieser Feb 10, 2024
46fd5f6
even more golf
eric-wieser Feb 10, 2024
3457096
cleanup
eric-wieser Feb 10, 2024
4ad83b2
one more
eric-wieser Feb 10, 2024
6781c2d
remove some ducplications and fix naming
miguelmarco Feb 10, 2024
6ec74cb
yet another deduplication
miguelmarco Feb 10, 2024
b5e347e
Fix broken proof
miguelmarco Feb 10, 2024
98a48d3
add preimage_val_sInter_eq_sInter
miguelmarco Feb 11, 2024
eb21b6a
oops
miguelmarco Feb 11, 2024
c6bf7a3
Add lemma about "going up and then down"
miguelmarco Feb 12, 2024
da816ed
Merge remote-tracking branch 'origin/master' into subset
miguelmarco Feb 19, 2024
9eca74e
Adapt to merged #10433
miguelmarco Feb 19, 2024
03dcb27
Remove redundant lemma
miguelmarco Feb 19, 2024
69a2365
Mark some lemmas as non-simp
miguelmarco Feb 20, 2024
cb8d85b
Update Mathlib/Data/Set/Image.lean
kmill Mar 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Mathlib.lean
Original file line number Diff line number Diff line change
Expand Up @@ -2008,6 +2008,7 @@ import Mathlib.Data.Set.Pointwise.Support
import Mathlib.Data.Set.Prod
import Mathlib.Data.Set.Semiring
import Mathlib.Data.Set.Sigma
import Mathlib.Data.Set.Subset
import Mathlib.Data.Set.Sups
import Mathlib.Data.Set.UnionLift
import Mathlib.Data.SetLike.Basic
Expand Down
21 changes: 21 additions & 0 deletions Mathlib/Data/Set/Functor.lean
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import Mathlib.Data.Set.Lattice
import Mathlib.Init.Set
import Mathlib.Control.Basic
import Mathlib.Lean.Expr.ExtraRecognizers

#align_import data.set.functor from "leanprover-community/mathlib"@"207cfac9fcd06138865b5d04f7091e46d9320432"

Expand Down Expand Up @@ -115,6 +116,26 @@
/-- Coercion using `(Subtype.val '' ·)` -/
instance : CoeHead (Set s) (Set α) := ⟨fun t => (Subtype.val '' t)⟩

namespace Notation

set_option pp.coercions false

Check failure on line 121 in Mathlib/Data/Set/Functor.lean

View workflow job for this annotation

GitHub Actions / Lint style

Mathlib/Data/Set/Functor.lean#L121: ERR_OPT: Forbidden set_option command

Check failure on line 121 in Mathlib/Data/Set/Functor.lean

View workflow job for this annotation

GitHub Actions / Lint style

Mathlib/Data/Set/Functor.lean#L121: ERR_OPT: Forbidden set_option command
eric-wieser marked this conversation as resolved.
Show resolved Hide resolved
#check ((1 : ℕ) : Int)

open Lean PrettyPrinter Delaborator SubExpr in
/--
If the `Set.Notation` namespace is open, sets of a subtype coerced to the ambient type are
represented with `↑`.
-/
@[scoped delab app.Set.image]
def delab_set_image_subtype : Delab := whenPPOption getPPCoercions do
let #[α, _, f, _] := (← getExpr).getAppArgs | failure
guard <| f.isAppOfArity ``Subtype.val 2
let some _ := α.coeTypeSet? | failure
let e ← withAppArg delab
`(↑$e)

end Notation

/-- The coercion from `Set.monad` as an instance is equal to the coercion defined above. -/
theorem coe_eq_image_val (t : Set s) :
@Lean.Internal.coeM Set s α _ Set.monad t = (t : Set α) := by
Expand Down
172 changes: 172 additions & 0 deletions Mathlib/Data/Set/Subset.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/-
Copyright (c) 2024 Miguel Marco. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Miguel Marco
-/
import Mathlib.Data.Set.Function
import Mathlib.Data.Set.Functor

/-!
# Sets in subtypes

This file is about sets in `Set A` when `A` is a set.

It defines notation `↓∩` for sets in a type pulled down to sets in a subtype, as an inverse
operation to the coercion that lifts sets in a subtype up to sets in the ambient type.

This module also provides lemmas for `↓∩` and this coercion.

## Notation

Let `α` be a `Type`, `A B : Set α` two sets in `α`, and `C : Set A` a set in the subtype `↑A`.

- `A ↓∩ B` denotes `(Subtype.val ⁻¹' B : Set A)` (that is, `{x : ↑A | ↑x ∈ B}`).
- `↑C` denotes `Subtype.val '' C` (that is, `{x : α | ∃ y ∈ C, ↑y = x}`).
kmill marked this conversation as resolved.
Show resolved Hide resolved

This notation, (together with the `↑` notation for `Set.CoeHead` in `Mathlib.Data.Set.Functor`)
is scoped to the `Set.Notation` namespace.
To enable it, use `open Set.Notation`.


## Naming conventions

Theorem names refer to `↓∩` as `preimage_val`.

miguelmarco marked this conversation as resolved.
Show resolved Hide resolved
## Tags

subsets
-/

open Set

variable {ι : Sort*} {α : Type*} {A B C : Set α} {D E : Set A}
variable {S : Set (Set α)} {T : Set (Set A)} {s : ι → Set α} {t : ι → Set A}

namespace Set.Notation

/--
Given two sets `A` and `B`, `A ↓∩ B` denotes the intersection of `A` and `B` as a set in `Set A`.

The notation is short for `((↑) ⁻¹' B : Set A)`, while giving hints to the elaborator
that both `A` and `B` are terms of `Set α` for the same `α`.
This set is the same as `{x : ↑A | ↑x ∈ B}`.
-/
scoped notation3 A:67 " ↓∩ " B:67 => (Subtype.val ⁻¹' (B : type_of% A) : Set (A : Set _))

end Set.Notation

namespace Set

open Notation

lemma preimage_val_eq_univ_of_subset (h : A ⊆ B) : A ↓∩ B = univ := by
rw [eq_univ_iff_forall, Subtype.forall]
exact h

lemma preimage_val_subset_preimage_val_iff : A ↓∩ B ⊆ A ↓∩ C ↔ A ∩ B ⊆ A ∩ C := by
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd argue we should state this as

Suggested change
lemma preimage_val_subset_preimage_val_iff : A ↓∩ B ⊆ A ↓∩ C ↔ ABAC := by
lemma preimage_val_subset_preimage_val_iff : A ↓∩ B ⊆ A ↓∩ C ↔ BACA := by

because this matches the direction of Subtype.image_preimage_coe.
Alternatively, we could flip Subtype.image_preimage_coe and related lemmas to match.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my initial implementation, I actually used the notation B ↓∩ A (as in meaning, "B is pulled down to A), and hence, this kind of results matched other existing results. With that notation, it totally made sense to use the expression you mention.

By @winstonyin suggestion I changed the notation, and then I am not so sure (It is kind of nice to "just change B ↓∩ A to B ∩ A ").

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think whatever the notation, Subtype.preimage_val_eq_preimage_val_iff should be changed if needed to have the sensible order (I don't think A ↓∩ B ⊆ A ↓∩ C ↔ B ∩ A ⊆ C ∩ A is sensible). I would prefer s ∩ t = s ∩ t because prefix operators are more common than postfix operators, and I'm seeing (s ∩ .) as a prefix operator.

I had suggested the current meaning of A ↓∩ B. Here are the options:

  • Use A ↓∩ B to mean "an intersection down to the subtype for A". I suggested this because I am seeing this as a prefix operator (A ↓∩ .) where A is configuration for the operator (it sets the type of the result too).

  • Use B ↓∩ A to mean "an intersection with A where B is pulled down to A". If this notation is like a type-ascription-based-coercion, then it has some parallel with (. : Set A) for the inverse operation, assuming that would trigger a coercion (though prefix arrow is also the inverse operation). (This is a matter of taste, but I would find the arrow on the other side to be easier to read, as in B ∩↓ A, but again, that's just personal taste.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do agree that the down arrow is better placed in the side of the set that acts as subtype (that is, the place where you are "going down" to.

Your argument about prefearing a prefix operator makes sense.... but then we get that preimage_val_subset_preimage_val_iff will either have a weird change of order in the statment, or go in a different direction that Subtype.image_preimage_coe.

So, I see the following possible options:

  1. Leave it as it is: a prefix operator, with statements that respect the sides of the intersections. The bad part is that it doesn't match Subtype.image_preimage_coe.
  2. Change the statement so it matches Subtype.image_preimage_coe, as @eric-wieser suggests. The problem with that option is that we get the weird change of order in the operands in the statement.
  3. Change the notation to B ∩↓ A. That solves both the inconsistencies with Subtype.image_preimage_coe and the order of operands. The problem here would be it is a postfix operator.
  4. Change the definition of Subtype.image_preimage_coe. That could be quite messy, since many results in mathlib depend on it.

My vote would go to 1 or 3. I personally don't like much 2. and 4. means opening a can of worms that i would rather not deal with.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For what it's worth, I don't see any reason Subtype.image_preimage_coe needs to be in its current form, and changing it (later) to ((↑) : s → α) '' (((↑) : s → α) ⁻¹' t) = s ∩ t seems perfectly fine (and maybe better?). I'm through half of mathlib so far testing out the changes.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, since some proofs in this PR would be affected by that change, shall we include that PR as a dependency for this one? Or just go on and make the change later?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that #10433 is merged, this lemma probably belongs next to Subtype.preimage_val_eq_preimage_val_iff in the Subtype namespace (and in the other file).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the right way to handle the merging of a branch that this depends on? merging main branch into this one? rebase?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merging the master branch should work

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved that lemma to the Image file.

rw [inter_comm A, inter_comm A, ← Subtype.image_preimage_coe, ← Subtype.image_preimage_coe,
image_subset_image_iff Subtype.val_injective]

lemma preimage_val_eq_iff : A ↓∩ B = A ↓∩ C ↔ A ∩ B = A ∩ C := by
rw [Subtype.preimage_val_eq_preimage_val_iff, inter_comm _ A, inter_comm _ A]
miguelmarco marked this conversation as resolved.
Show resolved Hide resolved

lemma preimage_val_sUnion : A ↓∩ (⋃₀ S) = ⋃₀ { (A ↓∩ B) | B ∈ S } := by
ext x
simp only [preimage_sUnion, mem_iUnion, mem_preimage, exists_prop, mem_sUnion, mem_setOf_eq,
exists_exists_and_eq_and]

@[simp]
lemma preimage_val_iInter : A ↓∩ (⋂ i, s i) = ⋂ i, A ↓∩ s i := preimage_iInter

lemma preimage_val_sInter : A ↓∩ (⋂₀ S) = ⋂₀ { (A ↓∩ B) | B ∈ S } := by
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be proven for the case of a general preimage, since the proof doesn't mention val.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(or maybe we should just have a lemma that says ⋂₀ { f B | B ∈ S } = ⋂ B ∈ S, f B, since the latter is the preferred spelling)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean for arbitrary f : Set α → Set β ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this lemma is fine to have, at least to specialize the notation and for discoverability.

@eric-wieser Set.preimage_sInter is that general lemma.

I guess the question is whether the RHS should instead be ⋂ B ∈ S, (A ↓∩ B). That way the proof of this lemma would be exactly Set.preimage_sInter.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the question is whether the RHS should instead be ⋂ B ∈ S, (A ↓∩ B). That way the proof of this lemma would be exactly Set.preimage_sInter.

I think the RHS should indeed be changed (here and elsewhere)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the general idea is to avoid ⋂₀ in general, and change it to (and same for unions)?

I have the opinion that it is nice to have lemmas that allow one to rewrite expressions with ⋂₀ into other expressions with ⋂₀, without converting them into . It reflects better some of the arguments we use on paper.

As @kmill commented, there is already a lemma that gives the other equality.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have the opinion that it is nice to have lemmas that allow one to rewrite expressions with ⋂₀ into other expressions with ⋂₀, without converting them into . It reflects better some of the arguments we use on paper.

If you feel strongly about this, then the resolution would be to change Set.preimage_sInter to have this statement instead of the current one (in a separate PR, and after discussion on zulip). We should try to avoid making difference choices to the same question in different files.

If we really were to change it, I'd argue that the RHS of the lemma should be ⋂₀ ((A ↓∩ .) '' S) since that's the canonical spelling of a set image.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you think @kmill ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to have both? Call this variant preimage_val_sInter_eq_sInter?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just added it.

ext x
simp only [preimage_sInter, mem_iInter, mem_preimage, mem_sInter, mem_setOf_eq,
forall_exists_index, and_imp, forall_apply_eq_imp_iff₂]

lemma eq_of_preimage_val_eq_of_subset (hB : B ⊆ A) (hC : C ⊆ A) (h : A ↓∩ B = A ↓∩ C) : B = C := by
simp only [← inter_eq_right] at hB hC
simp only [preimage_val_eq_iff, hB, hC] at h
exact h

/-!
The following simp lemmas try to transform operations in the subtype into operations in the ambient
type, if possible.
-/

lemma image_val_univ : ↑(univ : Set A) = A := Subtype.coe_image_univ A
miguelmarco marked this conversation as resolved.
Show resolved Hide resolved

lemma image_val_empty : ↑(∅ : Set A) = (∅ : Set α) := image_empty _

@[simp]
lemma image_val_union : (↑(D ∪ E) : Set α) = ↑D ∪ ↑E := image_union _ _ _

@[simp]
lemma image_val_inter : (↑(D ∩ E) : Set α) = ↑D ∩ ↑E := image_inter Subtype.val_injective

@[simp]
lemma image_val_diff : (↑(D \ E) : Set α) = ↑D \ ↑E := image_diff Subtype.val_injective _ _

@[simp]
lemma image_val_compl : ↑(Dᶜ) = A \ ↑D := by
rw [compl_eq_univ_diff, image_val_diff, image_val_univ]

@[simp]
lemma image_val_sUnion : ↑(⋃₀ T) = ⋃₀ { (B : Set α) | B ∈ T} := by
-- TODO: missing `image_sUnion` lemma
erw [sUnion_image]
simp_rw [sUnion_eq_biUnion, image_iUnion]

@[simp]
lemma image_val_iUnion : ↑(⋃ i, t i) = ⋃ i, (t i : Set α) := image_iUnion

@[simp]
lemma image_val_sInter (hT : T.Nonempty) : (↑(⋂₀ T) : Set α) = ⋂₀ { (↑B : Set α) | B ∈ T } := by
erw [sInter_image]
rw [sInter_eq_biInter, (Subtype.val_injective.injOn _).image_biInter_eq hT]

@[simp]
lemma image_val_iInter [Nonempty ι] : (↑(⋂ i, t i) : Set α) = ⋂ i, (↑(t i) : Set α) :=
(Subtype.val_injective.injOn _).image_iInter_eq

lemma image_val_subset_set : ↑D ⊆ A := image_val_subset

@[simp]
lemma image_val_union_self_right_eq : A ∪ ↑D = A :=
union_eq_left.2 image_val_subset

@[simp]
lemma image_val_union_self_left_eq : ↑D ∪ A = A :=
union_eq_right.2 image_val_subset

@[simp]
lemma cou_inter_self_right_eq_coe : A ∩ ↑D = ↑D :=
inter_eq_right.2 image_val_subset

@[simp]
lemma image_val_inter_self_left_eq_coe : ↑D ∩ A = ↑D :=
inter_eq_left.2 image_val_subset

@[simp]
lemma subset_preimage_val_image_val_iff : D ⊆ A ↓∩ ↑E ↔ D ⊆ E := by
rw [preimage_image_eq _ Subtype.val_injective]

@[simp]
lemma image_val_eq_iff : (D : Set α) = ↑E ↔ D = E := Subtype.val_injective.image_injective.eq_iff
miguelmarco marked this conversation as resolved.
Show resolved Hide resolved

lemma image_val_inj (h : (↑D : Set α) = ↑E) : D = E := Subtype.val_injective.image_injective h
miguelmarco marked this conversation as resolved.
Show resolved Hide resolved

lemma subset_of_image_val_subset_image_val (h : (↑D : Set α) ⊆ ↑E) : D ⊆ E :=
(image_subset_image_iff Subtype.val_injective).1 h

@[mono]
lemma image_val_mono (h : D ⊆ E) : (↑D : Set α) ⊆ ↑E :=
(image_subset_image_iff Subtype.val_injective).2 h

/-!
Relations between restriction and coercion.
-/

lemma image_val_preimage_val_subset_self : ↑(A ↓∩ B) ⊆ B :=
image_preimage_subset _ _

end Set
Loading