Skip to content

Commit d262d94

Browse files
chrisflavFormulaRabbit81
authored andcommitted
feat(AlgebraicGeometry): directed covers (leanprover-community#24528)
Directed covers are covers, where every intersection can be covered by a component of the cover. For open covers this is equivalent to the images forming a basis of the topology. If a cover is directed, the compatibility conditions for gluing become easier, because only compatibility with the transition maps needs to be checked. In particular, the covered scheme naturally is the colimit of the components of the cover. Co-authored-by: Christian Merten <christian@merten.dev>
1 parent 48ab29a commit d262d94

File tree

2 files changed

+291
-0
lines changed

2 files changed

+291
-0
lines changed

Mathlib.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1176,6 +1176,7 @@ import Mathlib.Algebra.Vertex.VertexOperator
11761176
import Mathlib.AlgebraicGeometry.AffineScheme
11771177
import Mathlib.AlgebraicGeometry.AffineSpace
11781178
import Mathlib.AlgebraicGeometry.AffineTransitionLimit
1179+
import Mathlib.AlgebraicGeometry.Cover.Directed
11791180
import Mathlib.AlgebraicGeometry.Cover.MorphismProperty
11801181
import Mathlib.AlgebraicGeometry.Cover.Open
11811182
import Mathlib.AlgebraicGeometry.Cover.Over
Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
/-
2+
Copyright (c) 2025 Christian Merten. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Christian Merten
5+
-/
6+
import Mathlib.CategoryTheory.LocallyDirected
7+
import Mathlib.AlgebraicGeometry.PullbackCarrier
8+
import Mathlib.AlgebraicGeometry.Gluing
9+
10+
/-!
11+
# Locally directed covers
12+
13+
A locally directed `P`-cover of a scheme `X` is a cover `𝒰` with an ordering
14+
on the indices and compatible transition maps `𝒰ᵢ ⟶ 𝒰ⱼ` for `i ≤ j` such that
15+
every `x : 𝒰ᵢ ×[X] 𝒰ⱼ` comes from some `𝒰ₖ` for a `k ≤ i` and `k ≤ j`.
16+
17+
Gluing along directed covers is easier, because the intersections `𝒰ᵢ ×[X] 𝒰ⱼ` can
18+
be covered by a subcover of `𝒰`. In particular, if `𝒰` is a Zariski cover,
19+
`X` naturally is the colimit of the `𝒰ᵢ`.
20+
21+
Many natural covers are naturally directed, most importantly the cover of all affine
22+
opens of a scheme.
23+
-/
24+
25+
universe u
26+
27+
noncomputable section
28+
29+
open CategoryTheory Limits
30+
31+
namespace AlgebraicGeometry.Scheme
32+
33+
variable {P : MorphismProperty Scheme.{u}} {X : Scheme.{u}}
34+
35+
namespace Cover
36+
37+
/-- A directed `P`-cover of a scheme `X` is a cover `𝒰` with an ordering
38+
on the indices and compatible transition maps `𝒰ᵢ ⟶ 𝒰ⱼ` for `i ≤ j` such that
39+
every `x : 𝒰ᵢ ×[X] 𝒰ⱼ` comes from some `𝒰ₖ` for a `k ≤ i` and `k ≤ j`. -/
40+
class LocallyDirected (𝒰 : X.Cover P) [Category 𝒰.J] where
41+
/-- The transition map `𝒰ᵢ ⟶ 𝒰ⱼ` for `i ≤ j`. -/
42+
trans {i j : 𝒰.J} (hij : i ⟶ j) : 𝒰.obj i ⟶ 𝒰.obj j
43+
trans_id (i : 𝒰.J) : trans (𝟙 i) = 𝟙 (𝒰.obj i)
44+
trans_comp {i j k : 𝒰.J} (hij : i ⟶ j) (hjk : j ⟶ k): trans (hij ≫ hjk) = trans hij ≫ trans hjk
45+
w {i j : 𝒰.J} (hij : i ⟶ j) : trans hij ≫ 𝒰.map j = 𝒰.map i := by aesop_cat
46+
directed {i j : 𝒰.J} (x : (pullback (𝒰.map i) (𝒰.map j)).carrier) :
47+
∃ (k : 𝒰.J) (hki : k ⟶ i) (hkj : k ⟶ j) (y : 𝒰.obj k),
48+
(pullback.lift (trans hki) (trans hkj) (by simp [w])).base y = x
49+
property_trans {i j : 𝒰.J} (hij : i ⟶ j) : P (trans hij) := by infer_instance
50+
51+
variable (𝒰 : X.Cover P) [Category 𝒰.J] [𝒰.LocallyDirected]
52+
53+
/-- The transition maps of a directed cover. -/
54+
def trans {i j : 𝒰.J} (hij : i ⟶ j) : 𝒰.obj i ⟶ 𝒰.obj j := LocallyDirected.trans hij
55+
56+
@[simp]
57+
lemma trans_map {i j : 𝒰.J} (hij : i ⟶ j) : 𝒰.trans hij ≫ 𝒰.map j = 𝒰.map i :=
58+
LocallyDirected.w hij
59+
60+
@[simp]
61+
lemma trans_id (i : 𝒰.J) : 𝒰.trans (𝟙 i) = 𝟙 (𝒰.obj i) := LocallyDirected.trans_id i
62+
63+
@[simp]
64+
lemma trans_comp {i j k : 𝒰.J} (hij : i ⟶ j) (hjk : j ⟶ k) :
65+
𝒰.trans (hij ≫ hjk) = 𝒰.trans hij ≫ 𝒰.trans hjk := LocallyDirected.trans_comp hij hjk
66+
67+
lemma exists_lift_trans_eq {i j : 𝒰.J} (x : (pullback (𝒰.map i) (𝒰.map j)).carrier) :
68+
∃ (k : 𝒰.J) (hki : k ⟶ i) (hkj : k ⟶ j) (y : 𝒰.obj k),
69+
(pullback.lift (𝒰.trans hki) (𝒰.trans hkj) (by simp)).base y = x :=
70+
LocallyDirected.directed x
71+
72+
lemma property_trans {i j : 𝒰.J} (hij : i ⟶ j) : P (𝒰.trans hij) :=
73+
LocallyDirected.property_trans hij
74+
75+
/-- If `𝒰` is a directed cover of `X`, this is the cover of `𝒰ᵢ ×[X] 𝒰ⱼ` by `{𝒰ₖ}` where
76+
`k ≤ i` and `k ≤ j`. -/
77+
@[simps map]
78+
def intersectionOfLocallyDirected [P.IsStableUnderBaseChange] [P.HasOfPostcompProperty P]
79+
(i j : 𝒰.J) : (pullback (𝒰.map i) (𝒰.map j)).Cover P where
80+
J := Σ (k : 𝒰.J), (k ⟶ i) × (k ⟶ j)
81+
obj k := 𝒰.obj k.1
82+
map k := pullback.lift (𝒰.trans k.2.1) (𝒰.trans k.2.2) (by simp)
83+
f x := ⟨(𝒰.exists_lift_trans_eq x).choose, (𝒰.exists_lift_trans_eq x).choose_spec.choose,
84+
(𝒰.exists_lift_trans_eq x).choose_spec.choose_spec.choose⟩
85+
covers x := (𝒰.exists_lift_trans_eq x).choose_spec.choose_spec.choose_spec
86+
map_prop k := by
87+
apply P.of_postcomp (W' := P) _ (pullback.fst _ _) (P.pullback_fst _ _ (𝒰.map_prop _))
88+
rw [pullback.lift_fst]
89+
exact 𝒰.property_trans _
90+
91+
/-- The canonical diagram induced by a locally directed cover. -/
92+
@[simps]
93+
def functorOfLocallyDirected : 𝒰.J ⥤ Scheme.{u} where
94+
obj := 𝒰.obj
95+
map := 𝒰.trans
96+
97+
instance : (𝒰.functorOfLocallyDirected ⋙ Scheme.forget).IsLocallyDirected where
98+
cond {i j k} fi fj xi xj hxij := by
99+
simp only [Functor.comp_obj, Cover.functorOfLocallyDirected_obj, forget_obj, Functor.comp_map,
100+
Cover.functorOfLocallyDirected_map, forget_map] at hxij
101+
have : (𝒰.map i).base xi = (𝒰.map j).base xj := by
102+
rw [← 𝒰.trans_map fi, ← 𝒰.trans_map fj, comp_base, comp_base, ConcreteCategory.comp_apply,
103+
hxij, ConcreteCategory.comp_apply]
104+
obtain ⟨z, rfl, rfl⟩ := Scheme.Pullback.exists_preimage_pullback xi xj this
105+
obtain ⟨l, gi, gj, y, rfl⟩ := 𝒰.exists_lift_trans_eq z
106+
refine ⟨l, gi, gj, y, ?_, ?_⟩ <;> simp [← Scheme.comp_base_apply]
107+
108+
/--
109+
The canonical cocone with point `X` on the functor induced by the locally directed cover `𝒰`.
110+
If `𝒰` is an open cover, this is colimiting (see `OpenCover.isColimitCoconeOfLocallyDirected`).
111+
-/
112+
@[simps]
113+
def coconeOfLocallyDirected : Cocone 𝒰.functorOfLocallyDirected where
114+
pt := X
115+
ι.app := 𝒰.map
116+
117+
section BaseChange
118+
119+
variable [P.IsStableUnderBaseChange] (𝒰 : X.Cover P)
120+
[Category 𝒰.J] [𝒰.LocallyDirected] {Y : Scheme.{u}} (f : Y ⟶ X)
121+
122+
instance : Category (𝒰.pullbackCover f).J := inferInstanceAs <| Category 𝒰.J
123+
124+
instance locallyDirectedPullbackCover : (𝒰.pullbackCover f).LocallyDirected where
125+
trans {i j} hij := pullback.map f (𝒰.map i) f (𝒰.map j) (𝟙 _) (𝒰.trans hij) (𝟙 _)
126+
(by simp) (by simp)
127+
trans_id i := by simp
128+
trans_comp hij hjk := by simp [pullback.map_comp]
129+
directed {i j} x := by
130+
dsimp at i j x ⊢
131+
let iso : pullback (pullback.fst f (𝒰.map i)) (pullback.fst f (𝒰.map j)) ≅
132+
pullback f (pullback.fst (𝒰.map i) (𝒰.map j) ≫ 𝒰.map i) :=
133+
pullbackRightPullbackFstIso _ _ _ ≪≫ pullback.congrHom pullback.condition rfl ≪≫
134+
pullbackAssoc ..
135+
have (k : 𝒰.J) (hki : k ⟶ i) (hkj : k ⟶ j) :
136+
(pullback.lift
137+
(pullback.map f (𝒰.map k) f (𝒰.map i) (𝟙 Y) (𝒰.trans hki) (𝟙 X) (by simp) (by simp))
138+
(pullback.map f (𝒰.map k) f (𝒰.map j) (𝟙 Y) (𝒰.trans hkj) (𝟙 X) (by simp) (by simp))
139+
(by simp)) =
140+
pullback.map _ _ _ _ (𝟙 Y) (pullback.lift (𝒰.trans hki) (𝒰.trans hkj) (by simp)) (𝟙 X)
141+
(by simp) (by simp) ≫ iso.inv := by
142+
apply pullback.hom_ext <;> apply pullback.hom_ext <;> simp [iso]
143+
obtain ⟨k, hki, hkj, yk, hyk⟩ := 𝒰.exists_lift_trans_eq ((iso.hom ≫ pullback.snd _ _).base x)
144+
refine ⟨k, hki, hkj, show x ∈ Set.range _ from ?_⟩
145+
rw [this, Scheme.comp_base, TopCat.coe_comp, Set.range_comp, Pullback.range_map]
146+
use iso.hom.base x
147+
simp only [id.base, TopCat.hom_id, ContinuousMap.coe_id, Set.range_id, Set.preimage_univ,
148+
Set.univ_inter, Set.mem_preimage, Set.mem_range, iso_hom_base_inv_base_apply, and_true]
149+
exact ⟨yk, hyk⟩
150+
property_trans {i j} hij := by
151+
let iso : pullback f (𝒰.map i) ≅ pullback (pullback.snd f (𝒰.map j)) (𝒰.trans hij) :=
152+
pullback.congrHom rfl (by simp) ≪≫ (pullbackLeftPullbackSndIso _ _ _).symm
153+
rw [← P.cancel_left_of_respectsIso iso.inv]
154+
simp only [pullbackCover_obj, Iso.trans_inv, Iso.symm_inv, pullback.congrHom_inv,
155+
Category.assoc, iso]
156+
convert P.pullback_fst _ _ (𝒰.property_trans hij)
157+
apply pullback.hom_ext <;> simp [pullback.condition]
158+
159+
end BaseChange
160+
161+
end Cover
162+
163+
namespace OpenCover
164+
165+
variable (𝒰 : X.OpenCover) [Category 𝒰.J] [𝒰.LocallyDirected]
166+
167+
instance {i j : 𝒰.J} (f : i ⟶ j) : IsOpenImmersion (𝒰.trans f) :=
168+
𝒰.property_trans f
169+
170+
instance {i j : 𝒰.J} (f : i ⟶ j) : IsOpenImmersion (𝒰.functorOfLocallyDirected.map f) :=
171+
𝒰.property_trans f
172+
173+
/--
174+
If `𝒰` is a directed open cover of `X`, to glue morphisms `{gᵢ : 𝒰ᵢ ⟶ Y}` it suffices
175+
to check compatibility with the transition maps.
176+
See `OpenCover.isColimitCoconeOfLocallyDirected` for this result stated in the language of
177+
colimits.
178+
-/
179+
def glueMorphismsOfLocallyDirected (𝒰 : X.OpenCover) [Category 𝒰.J] [𝒰.LocallyDirected]
180+
{Y : Scheme.{u}}
181+
(g : ∀ i, 𝒰.obj i ⟶ Y) (h : ∀ {i j : 𝒰.J} (hij : i ⟶ j), 𝒰.trans hij ≫ g j = g i) :
182+
X ⟶ Y :=
183+
𝒰.glueMorphisms g <| fun i j ↦ by
184+
apply (𝒰.intersectionOfLocallyDirected i j).hom_ext
185+
intro k
186+
simp [h]
187+
188+
@[reassoc (attr := simp)]
189+
lemma map_glueMorphismsOfLocallyDirected {Y : Scheme.{u}} (g : ∀ i, 𝒰.obj i ⟶ Y)
190+
(h : ∀ {i j : 𝒰.J} (hij : i ⟶ j), 𝒰.trans hij ≫ g j = g i) (i : 𝒰.J) :
191+
𝒰.map i ≫ 𝒰.glueMorphismsOfLocallyDirected g h = g i := by
192+
simp [glueMorphismsOfLocallyDirected]
193+
194+
/-- If `𝒰` is an open cover of `X` that is locally directed, `X` is
195+
the colimit of the components of `𝒰`. -/
196+
def isColimitCoconeOfLocallyDirected : IsColimit 𝒰.coconeOfLocallyDirected where
197+
desc s := 𝒰.glueMorphismsOfLocallyDirected s.ι.app fun _ ↦ s.ι.naturality _
198+
uniq s m hm := 𝒰.hom_ext _ _ fun j ↦ by simpa using hm j
199+
200+
/-- If `𝒰` is a directed open cover of `X`, to glue morphisms `{gᵢ : 𝒰ᵢ ⟶ Y}` over `S` it suffices
201+
to check compatibility with the transition maps. -/
202+
def glueMorphismsOverOfLocallyDirected {S : Scheme.{u}} {X : Over S}
203+
(𝒰 : X.left.OpenCover) [Category 𝒰.J] [𝒰.LocallyDirected] {Y : Over S}
204+
(g : ∀ i, 𝒰.obj i ⟶ Y.left)
205+
(h : ∀ {i j : 𝒰.J} (hij : i ⟶ j), 𝒰.trans hij ≫ g j = g i)
206+
(w : ∀ i, g i ≫ Y.hom = 𝒰.map i ≫ X.hom) :
207+
X ⟶ Y :=
208+
Over.homMk (𝒰.glueMorphismsOfLocallyDirected g h) <| by
209+
apply 𝒰.hom_ext
210+
intro i
211+
simp [w]
212+
213+
@[reassoc (attr := simp)]
214+
lemma map_glueMorphismsOverOfLocallyDirected_left {S : Scheme.{u}} {X : Over S}
215+
(𝒰 : X.left.OpenCover) [Category 𝒰.J] [𝒰.LocallyDirected] {Y : Over S}
216+
(g : ∀ i, 𝒰.obj i ⟶ Y.left) (h : ∀ {i j : 𝒰.J} (hij : i ⟶ j), 𝒰.trans hij ≫ g j = g i)
217+
(w : ∀ i, g i ≫ Y.hom = 𝒰.map i ≫ X.hom) (i : 𝒰.J) :
218+
𝒰.map i ≫ (𝒰.glueMorphismsOverOfLocallyDirected g h w).left = g i := by
219+
simp [glueMorphismsOverOfLocallyDirected]
220+
221+
end OpenCover
222+
223+
/-- If `𝒰` is an open cover such that the images of the components form a basis of the topology
224+
of `X`, `𝒰` is directed by the ordering of subset inclusion of the images. -/
225+
def Cover.LocallyDirected.ofIsBasisOpensRange {𝒰 : X.OpenCover} [Preorder 𝒰.J]
226+
(hle : ∀ {i j : 𝒰.J}, i ≤ j ↔ (𝒰.map i).opensRange ≤ (𝒰.map j).opensRange)
227+
(H : TopologicalSpace.Opens.IsBasis (Set.range <| fun i ↦ (𝒰.map i).opensRange)) :
228+
𝒰.LocallyDirected where
229+
trans {i j} hij := IsOpenImmersion.lift (𝒰.map j) (𝒰.map i) (hle.mp (leOfHom hij))
230+
trans_id i := by rw [← cancel_mono (𝒰.map i)]; simp
231+
trans_comp hij hjk := by rw [← cancel_mono (𝒰.map _)]; simp
232+
directed {i j} x := by
233+
have : (pullback.fst (𝒰.map i) (𝒰.map j) ≫ 𝒰.map i).base x ∈
234+
(pullback.fst (𝒰.map i) (𝒰.map j) ≫ 𝒰.map i).opensRange := ⟨x, rfl⟩
235+
obtain ⟨k, ⟨k, rfl⟩, ⟨y, hy⟩, h⟩ := TopologicalSpace.Opens.isBasis_iff_nbhd.mp H this
236+
refine ⟨k, homOfLE <| hle.mpr <| le_trans h ?_, homOfLE <| hle.mpr <| le_trans h ?_, y, ?_⟩
237+
· rw [Scheme.Hom.opensRange_comp]
238+
exact Set.image_subset_range _ _
239+
· simp_rw [pullback.condition, Scheme.Hom.opensRange_comp]
240+
exact Set.image_subset_range _ _
241+
· apply (pullback.fst (𝒰.map i) (𝒰.map j) ≫ 𝒰.map i).isOpenEmbedding.injective
242+
rw [← Scheme.comp_base_apply, pullback.lift_fst_assoc, IsOpenImmersion.lift_fac, hy]
243+
244+
section Constructions
245+
246+
section
247+
248+
variable {𝒰 : X.OpenCover} [Preorder 𝒰.J]
249+
(hle : ∀ {i j : 𝒰.J}, i ≤ j ↔ (𝒰.map i).opensRange ≤ (𝒰.map j).opensRange)
250+
(H : TopologicalSpace.Opens.IsBasis (Set.range <| fun i ↦ (𝒰.map i).opensRange))
251+
252+
include hle in
253+
lemma Cover.LocallyDirected.ofIsBasisOpensRange_le_iff (i j : 𝒰.J) :
254+
letI := Cover.LocallyDirected.ofIsBasisOpensRange hle H
255+
i ≤ j ↔ (𝒰.map i).opensRange ≤ (𝒰.map j).opensRange := hle
256+
257+
lemma Cover.LocallyDirected.ofIsBasisOpensRange_trans {i j : 𝒰.J} :
258+
letI := Cover.LocallyDirected.ofIsBasisOpensRange hle H
259+
(hij : i ≤ j) → 𝒰.trans (homOfLE hij) = IsOpenImmersion.lift (𝒰.map j) (𝒰.map i) (hle.mp hij) :=
260+
fun _ ↦ rfl
261+
262+
end
263+
264+
variable (X) in
265+
open TopologicalSpace.Opens in
266+
/-- The directed affine open cover of `X` given by all affine opens. -/
267+
@[simps J obj map]
268+
def directedAffineCover : X.OpenCover where
269+
J := X.affineOpens
270+
obj U := U
271+
map U := U.1
272+
f x := ⟨(isBasis_iff_nbhd.mp (isBasis_affine_open X) (mem_top x)).choose,
273+
(isBasis_iff_nbhd.mp (isBasis_affine_open X) (mem_top x)).choose_spec.1
274+
covers x := by
275+
simpa using (isBasis_iff_nbhd.mp (isBasis_affine_open X) (mem_top x)).choose_spec.2.1
276+
277+
instance : Preorder X.directedAffineCover.J := inferInstanceAs <| Preorder X.affineOpens
278+
279+
instance : Scheme.Cover.LocallyDirected X.directedAffineCover :=
280+
.ofIsBasisOpensRange (by simp) <| by
281+
convert isBasis_affine_open X
282+
simp
283+
284+
@[simp]
285+
lemma directedAffineCover_trans {U V : X.affineOpens} (hUV : U ≤ V) :
286+
Cover.trans X.directedAffineCover (homOfLE hUV) = X.homOfLE hUV := rfl
287+
288+
end Constructions
289+
290+
end AlgebraicGeometry.Scheme

0 commit comments

Comments
 (0)