Skip to content
This repository was archived by the owner on Jul 24, 2024. It is now read-only.

Commit 64abe5a

Browse files
committed
feat(category_theory/sites): closed sieves (#5282)
- closed sieves - closure of a sieve - subobject classifier in Sheaf (without proof of universal property) - equivalent sheaf condition iff same topology - closure operator induces topology
1 parent e076a9c commit 64abe5a

File tree

2 files changed

+344
-0
lines changed

2 files changed

+344
-0
lines changed
Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
/-
2+
Copyright (c) 2020 Bhavik Mehta. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Bhavik Mehta
5+
-/
6+
import category_theory.sites.sheaf_of_types
7+
import order.closure
8+
9+
/-!
10+
# Closed sieves
11+
12+
A natural closure operator on sieves is a closure operator on `sieve X` for each `X` which commutes
13+
with pullback.
14+
We show that a Grothendieck topology `J` induces a natural closure operator, and define what the
15+
closed sieves are. The collection of `J`-closed sieves forms a presheaf which is a sheaf for `J`,
16+
and further this presheaf can be used to determine the Grothendieck topology from the sheaf
17+
predicate.
18+
Finally we show that a natural closure operator on sieves induces a Grothendieck topology, and hence
19+
that natural closure operators are in bijection with Grothendieck topologies.
20+
21+
## Main definitions
22+
23+
* `category_theory.grothendieck_topology.close`: Sends a sieve `S` on `X` to the set of arrows
24+
which it covers. This has all the usual properties of a closure operator, as well as commuting
25+
with pullback.
26+
* `category_theory.grothendieck_topology.closure_operator`: The bundled `closure_operator` given
27+
by `category_theory.grothendieck_topology.close`.
28+
* `category_theory.grothendieck_topology.closed`: A sieve `S` on `X` is closed for the topology `J`
29+
if it contains every arrow it covers.
30+
* `category_theory.functor.closed_sieves`: The presheaf sending `X` to the collection of `J`-closed
31+
sieves on `X`. This is additionally shown to be a sheaf for `J`, and if this is a sheaf for a
32+
different topology `J'`, then `J' ≤ J`.
33+
* `category_theory.grothendieck_topology.topology_of_closure_operator`: A closure operator on the
34+
set of sieves on every object which commutes with pullback additionally induces a Grothendieck
35+
topology, giving a bijection with `category_theory.grothendieck_topology.closure_operator`.
36+
37+
38+
## Tags
39+
40+
closed sieve, closure, Grothendieck topology
41+
42+
## References
43+
44+
* [S. MacLane, I. Moerdijk, *Sheaves in Geometry and Logic*][MM92]
45+
-/
46+
47+
universes v u
48+
49+
namespace category_theory
50+
51+
variables {C : Type u} [small_category C]
52+
53+
variables {P : Cᵒᵖ ⥤ Type u}
54+
variables (J₁ J₂ : grothendieck_topology C)
55+
56+
namespace grothendieck_topology
57+
58+
/-- The `J`-closure of a sieve is the collection of arrows which it covers. -/
59+
@[simps]
60+
def close {X : C} (S : sieve X) : sieve X :=
61+
{ arrows := λ Y f, J₁.covers S f,
62+
downward_closed' := λ Y Z f hS, J₁.arrow_stable _ _ hS }
63+
64+
/-- Any sieve is smaller than its closure. -/
65+
lemma le_close {X : C} (S : sieve X) : S ≤ J₁.close S :=
66+
λ Y g hg, J₁.covering_of_eq_top (S.pullback_eq_top_of_mem hg)
67+
68+
/--
69+
A sieve is closed for the Grothendieck topology if it contains every arrow it covers.
70+
In the case of the usual topology on a topological space, this means that the open cover contains
71+
every open set which it covers.
72+
73+
Note this has no relation to a closed subset of a topological space.
74+
-/
75+
def is_closed {X : C} (S : sieve X) : Prop :=
76+
∀ ⦃Y : C⦄ (f : Y ⟶ X), J₁.covers S f → S f
77+
78+
/-- If `S` is `J₁`-closed, then `S` covers exactly the arrows it contains. -/
79+
lemma covers_iff_mem_of_closed {X : C} {S : sieve X}
80+
(h : J₁.is_closed S) {Y : C} (f : Y ⟶ X) :
81+
J₁.covers S f ↔ S f :=
82+
⟨h _, J₁.arrow_max _ _⟩
83+
84+
/-- Being `J`-closed is stable under pullback. -/
85+
lemma is_closed_pullback {X Y : C} (f : Y ⟶ X) (S : sieve X) :
86+
J₁.is_closed S → J₁.is_closed (S.pullback f) :=
87+
λ hS Z g hg, hS (g ≫ f) (by rwa [J₁.covers_iff, sieve.pullback_comp])
88+
89+
/--
90+
The closure of a sieve `S` is the largest closed sieve which contains `S` (justifying the name
91+
"closure").
92+
-/
93+
lemma le_close_of_is_closed {X : C} {S T : sieve X}
94+
(h : S ≤ T) (hT : J₁.is_closed T) :
95+
J₁.close S ≤ T :=
96+
λ Y f hf, hT _ (J₁.superset_covering (sieve.pullback_monotone f h) hf)
97+
98+
/-- The closure of a sieve is closed. -/
99+
lemma close_is_closed {X : C} (S : sieve X) : J₁.is_closed (J₁.close S) :=
100+
λ Y g hg, J₁.arrow_trans g _ S hg (λ Z h hS, hS)
101+
102+
/-- The sieve `S` is closed iff its closure is equal to itself. -/
103+
lemma is_closed_iff_close_eq_self {X : C} (S : sieve X) :
104+
J₁.is_closed S ↔ J₁.close S = S :=
105+
begin
106+
split,
107+
{ intro h,
108+
apply le_antisymm,
109+
{ intros Y f hf,
110+
rw ← J₁.covers_iff_mem_of_closed h,
111+
apply hf },
112+
{ apply J₁.le_close } },
113+
{ intro e,
114+
rw ← e,
115+
apply J₁.close_is_closed }
116+
end
117+
118+
lemma close_eq_self_of_is_closed {X : C} {S : sieve X} (hS : J₁.is_closed S) :
119+
J₁.close S = S :=
120+
(J₁.is_closed_iff_close_eq_self S).1 hS
121+
122+
/-- Closing under `J` is stable under pullback. -/
123+
lemma pullback_close {X Y : C} (f : Y ⟶ X) (S : sieve X) :
124+
J₁.close (S.pullback f) = (J₁.close S).pullback f :=
125+
begin
126+
apply le_antisymm,
127+
{ refine J₁.le_close_of_is_closed (sieve.pullback_monotone _ (J₁.le_close S)) _,
128+
apply J₁.is_closed_pullback _ _ (J₁.close_is_closed _) },
129+
{ intros Z g hg,
130+
change _ ∈ J₁ _,
131+
rw ← sieve.pullback_comp,
132+
apply hg }
133+
end
134+
135+
@[mono]
136+
lemma monotone_close {X : C} :
137+
monotone (J₁.close : sieve X → sieve X) :=
138+
λ S₁ S₂ h, J₁.le_close_of_is_closed (h.trans (J₁.le_close _)) (J₁.close_is_closed S₂)
139+
140+
@[simp]
141+
lemma close_close {X : C} (S : sieve X) :
142+
J₁.close (J₁.close S) = J₁.close S :=
143+
le_antisymm
144+
(J₁.le_close_of_is_closed (le_refl _) (J₁.close_is_closed S))
145+
(J₁.monotone_close (J₁.le_close _))
146+
147+
/--
148+
The sieve `S` is in the topology iff its closure is the maximal sieve. This shows that the closure
149+
operator determines the topology.
150+
-/
151+
lemma close_eq_top_iff_mem {X : C} (S : sieve X) :
152+
J₁.close S = ⊤ ↔ S ∈ J₁ X :=
153+
begin
154+
split,
155+
{ intro h,
156+
apply J₁.transitive (J₁.top_mem X),
157+
intros Y f hf,
158+
change J₁.close S f,
159+
rwa h },
160+
{ intro hS,
161+
rw eq_top_iff,
162+
intros Y f hf,
163+
apply J₁.pullback_stable _ hS }
164+
end
165+
166+
/-- A Grothendieck topology induces a natural family of closure operators on sieves. -/
167+
@[simps {rhs_md := semireducible}]
168+
def closure_operator (X : C) : closure_operator (sieve X) :=
169+
closure_operator.mk'
170+
J₁.close
171+
(λ S₁ S₂ h, J₁.le_close_of_is_closed (h.trans (J₁.le_close _)) (J₁.close_is_closed S₂))
172+
J₁.le_close
173+
(λ S, J₁.le_close_of_is_closed (le_refl _) (J₁.close_is_closed S))
174+
175+
@[simp]
176+
lemma closed_iff_closed {X : C} (S : sieve X) :
177+
S ∈ (J₁.closure_operator X).closed ↔ J₁.is_closed S :=
178+
(J₁.is_closed_iff_close_eq_self S).symm
179+
180+
end grothendieck_topology
181+
182+
/--
183+
The presheaf sending each object to the set of `J`-closed sieves on it. This presheaf is a `J`-sheaf
184+
(and will turn out to be a subobject classifier for the category of `J`-sheaves).
185+
-/
186+
@[simps]
187+
def functor.closed_sieves : Cᵒᵖ ⥤ Type u :=
188+
{ obj := λ X, {S : sieve X.unop // J₁.is_closed S},
189+
map := λ X Y f S, ⟨S.1.pullback f.unop, J₁.is_closed_pullback f.unop _ S.2⟩ }
190+
191+
/--
192+
The presheaf of `J`-closed sieves is a `J`-sheaf.
193+
The proof of this is adapted from [MM92], Chatper III, Section 7, Lemma 1.
194+
-/
195+
lemma classifier_is_sheaf : presieve.is_sheaf J₁ (functor.closed_sieves J₁) :=
196+
begin
197+
intros X S hS,
198+
rw ← presieve.is_separated_for_and_exists_is_amalgamation_iff_sheaf_for,
199+
refine ⟨_, _⟩,
200+
{ rintro x ⟨M, hM⟩ ⟨N, hN⟩ hM₂ hN₂,
201+
ext,
202+
dsimp only [subtype.coe_mk],
203+
rw [← J₁.covers_iff_mem_of_closed hM, ← J₁.covers_iff_mem_of_closed hN],
204+
have q : ∀ ⦃Z : C⦄ (g : Z ⟶ X) (hg : S g), M.pullback g = N.pullback g,
205+
{ intros Z g hg,
206+
apply congr_arg subtype.val ((hM₂ g hg).trans (hN₂ g hg).symm) },
207+
have MSNS : M ⊓ S = N ⊓ S,
208+
{ ext Z g,
209+
rw [sieve.inter_apply, sieve.inter_apply, and_comm (N g), and_comm],
210+
apply and_congr_right,
211+
intro hg,
212+
rw [sieve.pullback_eq_top_iff_mem, sieve.pullback_eq_top_iff_mem, q g hg] },
213+
split,
214+
{ intro hf,
215+
rw J₁.covers_iff,
216+
apply J₁.superset_covering (sieve.pullback_monotone f inf_le_left),
217+
rw ← MSNS,
218+
apply J₁.arrow_intersect f M S hf (J₁.pullback_stable _ hS) },
219+
{ intro hf,
220+
rw J₁.covers_iff,
221+
apply J₁.superset_covering (sieve.pullback_monotone f inf_le_left),
222+
rw MSNS,
223+
apply J₁.arrow_intersect f N S hf (J₁.pullback_stable _ hS) } },
224+
{ intros x hx,
225+
rw presieve.compatible_iff_sieve_compatible at hx,
226+
let M := sieve.bind S (λ Y f hf, (x f hf).1),
227+
have : ∀ ⦃Y⦄ (f : Y ⟶ X) (hf : S f), M.pullback f = (x f hf).1,
228+
{ intros Y f hf,
229+
apply le_antisymm,
230+
{ rintro Z u ⟨W, g, f', hf', (hg : (x f' hf').1 _), c⟩,
231+
rw [sieve.pullback_eq_top_iff_mem,
232+
←(show (x (u ≫ f) _).1 = (x f hf).1.pullback u, from congr_arg subtype.val (hx f u hf))],
233+
simp_rw ← c,
234+
rw (show (x (g ≫ f') _).1 = _, from congr_arg subtype.val (hx f' g hf')),
235+
apply sieve.pullback_eq_top_of_mem _ hg },
236+
{ apply sieve.le_pullback_bind S (λ Y f hf, (x f hf).1) } },
237+
refine ⟨⟨_, J₁.close_is_closed M⟩, _⟩,
238+
{ intros Y f hf,
239+
ext1,
240+
dsimp,
241+
rw [← J₁.pullback_close, this _ hf],
242+
apply le_antisymm (J₁.le_close_of_is_closed (le_refl _) (x f hf).2) (J₁.le_close _) } },
243+
end
244+
245+
/--
246+
If presheaf of `J₁`-closed sieves is a `J₂`-sheaf then `J₁ ≤ J₂`. Note the converse is true by
247+
`classifier_is_sheaf` and `is_sheaf_of_le`.
248+
-/
249+
lemma le_topology_of_closed_sieves_is_sheaf {J₁ J₂ : grothendieck_topology C}
250+
(h : presieve.is_sheaf J₁ (functor.closed_sieves J₂)) :
251+
J₁ ≤ J₂ :=
252+
λ X S hS,
253+
begin
254+
rw ← J₂.close_eq_top_iff_mem,
255+
have : J₂.is_closed (⊤ : sieve X),
256+
{ intros Y f hf,
257+
trivial },
258+
suffices : (⟨J₂.close S, J₂.close_is_closed S⟩ : subtype _) = ⟨⊤, this⟩,
259+
{ rw subtype.ext_iff at this,
260+
exact this },
261+
apply (h S hS).is_separated_for.ext,
262+
{ intros Y f hf,
263+
ext1,
264+
dsimp,
265+
rw [sieve.pullback_top, ← J₂.pullback_close, S.pullback_eq_top_of_mem hf,
266+
J₂.close_eq_top_iff_mem],
267+
apply J₂.top_mem },
268+
end
269+
270+
/-- If being a sheaf for `J₁` is equivalent to being a sheaf for `J₂`, then `J₁ = J₂`. -/
271+
lemma topology_eq_iff_same_sheaves {J₁ J₂ : grothendieck_topology C} :
272+
J₁ = J₂ ↔ (∀ P, presieve.is_sheaf J₁ P ↔ presieve.is_sheaf J₂ P) :=
273+
begin
274+
split,
275+
{ rintro rfl,
276+
intro P,
277+
refl },
278+
{ intro h,
279+
apply le_antisymm,
280+
{ apply le_topology_of_closed_sieves_is_sheaf,
281+
rw h,
282+
apply classifier_is_sheaf },
283+
{ apply le_topology_of_closed_sieves_is_sheaf,
284+
rw ← h,
285+
apply classifier_is_sheaf } }
286+
end
287+
288+
/--
289+
A closure (increasing, inflationary and idempotent) operation on sieves that commutes with pullback
290+
induces a Grothendieck topology.
291+
In fact, such operations are in bijection with Grothendieck topologies.
292+
-/
293+
@[simps]
294+
def topology_of_closure_operator
295+
(c : Π (X : C), closure_operator (sieve X))
296+
(hc : Π ⦃X Y : C⦄ (f : Y ⟶ X) (S : sieve X), c _ (S.pullback f) = (c _ S).pullback f) :
297+
grothendieck_topology C :=
298+
{ sieves := λ X, {S | c X S = ⊤},
299+
top_mem' := λ X, top_unique ((c X).le_closure _),
300+
pullback_stable' := λ X Y S f hS,
301+
begin
302+
rw set.mem_set_of_eq at hS,
303+
rw [set.mem_set_of_eq, hc, hS, sieve.pullback_top],
304+
end,
305+
transitive' := λ X S hS R hR,
306+
begin
307+
rw set.mem_set_of_eq at hS,
308+
rw [set.mem_set_of_eq, ←(c X).idempotent, eq_top_iff, ←hS],
309+
apply (c X).monotone (λ Y f hf, _),
310+
rw [sieve.pullback_eq_top_iff_mem, ←hc],
311+
apply hR hf,
312+
end }
313+
314+
/--
315+
The topology given by the closure operator `J.close` on a Grothendieck topology is the same as `J`.
316+
-/
317+
lemma topology_of_closure_operator_self :
318+
topology_of_closure_operator J₁.closure_operator (λ X Y, J₁.pullback_close) = J₁ :=
319+
begin
320+
ext X S,
321+
apply grothendieck_topology.close_eq_top_iff_mem,
322+
end
323+
324+
lemma topology_of_closure_operator_close
325+
(c : Π (X : C), closure_operator (sieve X))
326+
(pb : Π ⦃X Y : C⦄ (f : Y ⟶ X) (S : sieve X), c Y (S.pullback f) = (c X S).pullback f)
327+
(X : C) (S : sieve X) :
328+
(topology_of_closure_operator c pb).close S = c X S :=
329+
begin
330+
ext,
331+
change c _ (sieve.pullback f S) = ⊤ ↔ c _ S f,
332+
rw [pb, sieve.pullback_eq_top_iff_mem],
333+
end
334+
335+
end category_theory

src/order/closure.lean

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,15 @@ variables {α} (c : closure_operator α)
6161
∀ (c₁ c₂ : closure_operator α), (c₁ : α → α) = (c₂ : α → α) → c₁ = c₂
6262
| ⟨⟨c₁, _⟩, _, _⟩ ⟨⟨c₂, _⟩, _, _⟩ h := by { congr, exact h }
6363

64+
/-- Constructor for a closure operator using the weaker idempotency axiom: `f (f x) ≤ f x`. -/
65+
@[simps]
66+
def mk' (f : α → α) (hf₁ : monotone f) (hf₂ : ∀ x, x ≤ f x) (hf₃ : ∀ x, f (f x) ≤ f x) :
67+
closure_operator α :=
68+
{ to_fun := f,
69+
monotone' := hf₁,
70+
le_closure' := hf₂,
71+
idempotent' := λ x, le_antisymm (hf₃ x) (hf₁ (hf₂ x)) }
72+
6473
@[mono] lemma monotone : monotone c := c.monotone'
6574
/--
6675
Every element is less than its closure. This property is sometimes referred to as extensivity or

0 commit comments

Comments
 (0)