|
| 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