diff --git a/docs/doc.main b/docs/doc.main index 87e8a8c4c00a..af45cebe4013 100644 --- a/docs/doc.main +++ b/docs/doc.main @@ -173,6 +173,7 @@ "AlgebraicGeometry/Schemes/MorphismsOfProjectiveSchemes.md", "AlgebraicGeometry/Schemes/RationalPointsProjective.md", "AlgebraicGeometry/Schemes/Sheaves.md", + "AlgebraicGeometry/Schemes/Cycles.md", ], "Algebraic Sets" => [ "AlgebraicGeometry/AlgebraicSets/AffineAlgebraicSet.md", diff --git a/docs/src/AlgebraicGeometry/Schemes/Cycles.md b/docs/src/AlgebraicGeometry/Schemes/Cycles.md new file mode 100644 index 000000000000..192aa1b213b3 --- /dev/null +++ b/docs/src/AlgebraicGeometry/Schemes/Cycles.md @@ -0,0 +1,83 @@ +```@meta +CurrentModule = Oscar +``` + +# Cycles and divisors + +## Algebraic Cycles +```@docs +AbsAlgebraicCycle{CoveredSchemeType<:AbsCoveredScheme, CoefficientRingType<:AbstractAlgebra.Ring} +``` +### Constructors +```@docs +algebraic_cycle(X::AbsCoveredScheme, R::Ring) +algebraic_cycle(I::AbsIdealSheaf, R::Ring) +algebraic_cycle(I::AbsIdealSheaf) +``` +### Properties +```@docs +ambient_scheme(D::AbsAlgebraicCycle) +components(D::AbsAlgebraicCycle) +dim(D::AbsAlgebraicCycle) +irreducible_decomposition(D::AbsAlgebraicCycle) +integral(W::AbsAlgebraicCycle; check::Bool=true) +``` +### Attributes +```@docs +is_effective(A::AbsAlgebraicCycle) +is_prime(D::AbsAlgebraicCycle) +``` +### Methods +```@docs +Base.:<=(A::AbsAlgebraicCycle,B::AbsAlgebraicCycle) +``` + +## Weil Divisors +```@docs +AbsWeilDivisor{CoveredSchemeType, CoefficientRingType} +``` +### Constructors +```@docs +weil_divisor(X::AbsCoveredScheme, R::Ring) +weil_divisor(I::AbsIdealSheaf; check::Bool=true) +weil_divisor(I::AbsIdealSheaf, R::Ring; check::Bool=true) +``` +### Methods +Besides the methods for [`AbsAlgebraicCycle`](@ref) +the following are available. +```@docs +is_in_linear_system(f::VarietyFunctionFieldElem, D::AbsWeilDivisor; regular_on_complement::Bool=false, check::Bool=true) +order_of_vanishing(f::VarietyFunctionFieldElem, D::AbsWeilDivisor; check::Bool=true) +intersect(D::AbsWeilDivisor, E::AbsWeilDivisor; covering::Covering=default_covering(scheme(D))) +``` + +## Linear Systems +```@docs +LinearSystem{DivisorType<:AbsWeilDivisor} +weil_divisor(L::LinearSystem) +variety(L::LinearSystem) +subsystem(L::LinearSystem, D::AbsWeilDivisor) +``` + +## Cartier Divisors +```@docs +CartierDivisor{CoveredSchemeType<:AbsCoveredScheme, CoeffType<:RingElem} +EffectiveCartierDivisor{CoveredSchemeType<:AbsCoveredScheme} +``` +Cartier divisors support elementary arithmetic. +### Constructors +```@docs +effective_cartier_divisor(I::AbsIdealSheaf; trivializing_covering::Covering = default_covering(scheme(I)), check::Bool = true) +effective_cartier_divisor(IP::AbsProjectiveScheme, f::Union{MPolyDecRingElem, MPolyQuoRingElem}) +cartier_divisor(E::EffectiveCartierDivisor) +cartier_divisor(IP::AbsProjectiveScheme, f::Union{MPolyDecRingElem, MPolyQuoRingElem}) +``` +### Attributes +```@docs +ideal_sheaf(C::EffectiveCartierDivisor) +ambient_scheme(C::EffectiveCartierDivisor) +ambient_scheme(C::CartierDivisor) +coefficient_ring(C::CartierDivisor) +components(C::CartierDivisor) +trivializing_covering(C::EffectiveCartierDivisor) +``` diff --git a/experimental/Schemes/src/Auxiliary.jl b/experimental/Schemes/src/Auxiliary.jl index b9c68e10a36b..b8cf7d827365 100644 --- a/experimental/Schemes/src/Auxiliary.jl +++ b/experimental/Schemes/src/Auxiliary.jl @@ -99,7 +99,7 @@ end function pullback(f::AbsCoveredSchemeMorphism, C::EffectiveCartierDivisor) X = domain(f) Y = codomain(f) - Y === scheme(C) || error("divisor must be defined on the codomain of the map") + Y === ambient_scheme(C) || error("divisor must be defined on the codomain of the map") # The challenge is that phi has two coverings cov1 → cov2 on which it is defined. # The covering cov3 on which C is principalized might be different from cov2. # Thus, we need to first pass to a common refinement cov' of cov2 and cov3, diff --git a/experimental/Schemes/src/BlowupMorphism.jl b/experimental/Schemes/src/BlowupMorphism.jl index 940d30e83e21..92da05d1d4fa 100644 --- a/experimental/Schemes/src/BlowupMorphism.jl +++ b/experimental/Schemes/src/BlowupMorphism.jl @@ -3,48 +3,7 @@ export center export exceptional_divisor export projection -@doc raw""" - AbsDesingMor{ - DomainType<:AbsCoveredScheme, - CodomainType<:AbsCoveredScheme, - BlowupMorphismType - } <: AbsCoveredSchemeMorphism{ - DomainType, - CodomainType, - Nothing, - BlowupMorphismType - } -Abstract type for desingularizations ``f : X -> Y `` of schemes where - - * ``Y`` is the scheme of which the singularities are to be resolved - * ``f`` is a birational proper map - may for instance be BlowUpSequence or Lipman-style combination of blow-ups and normalization - * ``Y`` is a regular scheme -""" -abstract type AbsDesingMor{ - DomainType<:AbsCoveredScheme, - CodomainType<:AbsCoveredScheme, - BlowupMorphismType - } <: AbsCoveredSchemeMorphism{ - DomainType, - CodomainType, - Nothing, - BlowupMorphismType - } -end -######################################################################## -# An abstract type for blowup morphisms. -# -# This should also comprise sequences of simple blowups leading -# to a partial or full resolution of singularities. The interface -# is specified below. -######################################################################## -abstract type AbsBlowupMorphism{DomainType<:AbsCoveredScheme, - CodomainType<:AbsCoveredScheme, - BlowupMorphismType - } <: AbsDesingMor{DomainType, CodomainType, BlowupMorphismType} -end # The interface inherits all functionality from AbsCoveredSchemeMorphism. # This is extended by the following: @@ -111,21 +70,6 @@ function total_transform(f::AbsBlowupMorphism, a::Any) error("not implemented") end -######################################################################## -# An abstract type for classical blowups of ideal sheaves. -# -# This can either be a BlowupMorphism as below, but also a special -# toric morphism induced by fan subdivisions. -######################################################################## -abstract type AbsSimpleBlowupMorphism{DomainType<:AbsCoveredScheme, - CodomainType<:AbsCoveredScheme, - BlowupMorphismType - } <: AbsBlowupMorphism{ - DomainType, - CodomainType, - BlowupMorphismType - } -end @doc raw""" exceptional_divisor(f::AbsSimpleBlowupMorphism) @@ -198,101 +142,7 @@ function controlled_transform(f::AbsSimpleBlowupMorphism, a::Any, k::Int) error("not implemented") end -######################################################################## -# BlowupMorphism -# -# A datastructure to maintain all information necessary to effectively -# handle blowups. This is work in progress and will one day serve as -# a building block for sequences of blowups -######################################################################## -@doc raw""" - BlowupMorphism - -A datastructure to encode blowups of covered schemes in some sheaves of ideals. - -It is described as a morphism from the new scheme to the blown-up scheme, with -information about its center (i.e. the ideal sheaves blown-up in the bottom -scheme) and its exceptional locus (i.e. the preimage of the center under the -blowup). - -# Examples -```jldoctest -julia> R, (x,y,z) = QQ[:x, :y, :z]; - -julia> A3 = spec(R) -Spectrum - of multivariate polynomial ring in 3 variables x, y, z - over rational field - -julia> I = ideal(R, [x,y,z]) -Ideal generated by - x - y - z - -julia> bl = blow_up(A3, I) -Blowup - of scheme over QQ covered with 1 patch - 1b: [x, y, z] affine 3-space - in sheaf of ideals with restriction - 1b: Ideal (x, y, z) -with domain - scheme over QQ covered with 3 patches - 1a: [(s1//s0), (s2//s0), x] scheme(0, 0, 0) - 2a: [(s0//s1), (s2//s1), y] scheme(0, 0, 0) - 3a: [(s0//s2), (s1//s2), z] scheme(0, 0, 0) -and exceptional divisor - effective cartier divisor defined by - sheaf of ideals with restrictions - 1a: Ideal (x) - 2a: Ideal (y) - 3a: Ideal (z) - -julia> E = exceptional_divisor(bl) -Effective cartier divisor - on scheme over QQ covered with 3 patches - 1: [(s1//s0), (s2//s0), x] scheme(0, 0, 0) - 2: [(s0//s1), (s2//s1), y] scheme(0, 0, 0) - 3: [(s0//s2), (s1//s2), z] scheme(0, 0, 0) -defined by - sheaf of ideals with restrictions - 1: Ideal (x) - 2: Ideal (y) - 3: Ideal (z) - -julia> Z = center(bl) -Sheaf of ideals - on scheme over QQ covered with 1 patch - 1: [x, y, z] affine 3-space -with restriction - 1: Ideal (x, y, z) -``` -""" -@attributes mutable struct BlowupMorphism{ - DomainType<:AbsCoveredScheme, # Not a concrete type in general because this is lazy - CodomainType<:AbsCoveredScheme, - } <: AbsSimpleBlowupMorphism{ - DomainType, - CodomainType, - BlowupMorphism{DomainType, CodomainType} - } - projective_bundle::CoveredProjectiveScheme - codomain::CodomainType # in general a CoveredScheme - center::AbsIdealSheaf # on codomain - projection::AbsCoveredSchemeMorphism - domain::AbsCoveredScheme # in general a CoveredScheme - exceptional_divisor::EffectiveCartierDivisor - - function BlowupMorphism( - IP::CoveredProjectiveScheme, - I::AbsIdealSheaf - ) - X = base_scheme(IP) - X === scheme(I) || error("ideal sheaf not compatible with blown up variety") - return new{AbsCoveredScheme, typeof(X)}(IP, X, I) - end -end ### Forward the essential functionality underlying_morphism(phi::BlowupMorphism) = projection(phi) @@ -550,7 +400,7 @@ function strict_transform(p::AbsSimpleBlowupMorphism, C::EffectiveCartierDivisor end function strict_transform_with_multiplicity(p::AbsSimpleBlowupMorphism, C::EffectiveCartierDivisor) - X = scheme(C) + X = ambient_scheme(C) Y = domain(p) X === codomain(p) || error("cartier divisor is not defined on the codomain of the morphism") E = exceptional_divisor(p) @@ -611,7 +461,7 @@ end function strict_transform(p::AbsSimpleBlowupMorphism, C::CartierDivisor) X = codomain(p) Y = domain(p) - X === scheme(C) || error("cartier divisor not defined on the codomain of the map") + X === ambient_scheme(C) || error("cartier divisor not defined on the codomain of the map") kk = coefficient_ring(C) result = CartierDivisor(Y, kk) for c in components(C) @@ -832,72 +682,8 @@ function compose(f::AbsCoveredSchemeMorphism, g::AbsSimpleBlowupMorphism) return composite_map(f, g) end -######################################################################## -# Resolutions of singularities # -######################################################################## - -@doc raw""" - BlowUpSequence{ - DomainType<:AbsCoveredScheme, - CodomainType<:AbsCoveredScheme - } <: AbsDesingMor{ - DomainType, - CodomainType, - } -""" -@attributes mutable struct BlowUpSequence{ - DomainType<:AbsCoveredScheme, - CodomainType<:AbsCoveredScheme - }<:AbsBlowupMorphism{ - DomainType, CodomainType, - BlowUpSequence{DomainType, CodomainType} - } - maps::Vector{<:BlowupMorphism} # count right to left: - # original scheme is codomain of map 1 - - embeddings::Vector{<:AbsCoveredSchemeMorphism} # if set, - # assert codomain(maps[i])===codomain(embeddings[i]) - # boolean flags - is_embedded::Bool # do not set embeddings, ex_mult, controlled_transform etc - # if is_embedded == false - resolves_sing::Bool # domain(maps[end]) smooth? - is_trivial::Bool # codomain already smooth? - is_strong::Bool # snc divisors ensured? - transform_type::Symbol # can be :strict, :weak or :control - # only relevant for is_embedded == true - - # fields for caching, may be filled during computation - ex_div::Vector{<:EffectiveCartierDivisor} # list of exc. divisors arising from individual steps - # lives in domain(maps[end]) - control::Int # value of control for controlled transform - ex_mult::Vector{Int} # multiplicities of exceptional divisors removed from - # total transform, not set for is_embedded == false - # or transform_type == strict - controlled_transform::AbsIdealSheaf # holds weak or controlled transform according to transform_type - dont_meet::Vector{Tuple{Int,Int}} # mostly for dim=2: intersections which cannot exist according - # to intermediate computations - caution_multi_charts::Vector{Tuple{Int,Int}} # only for dim=2: intersection of divisors not - # entirely visible in a single chart - - - # fields for caching to be filled a posteriori (on demand, only if partial_res==false) - underlying_morphism::CompositeCoveredSchemeMorphism{DomainType, CodomainType} - exceptional_divisor::CartierDivisor # exceptional divisor of composed_map - exceptional_locus::WeilDivisor # exceptional locus of composed map - exceptional_divisor_on_X::CartierDivisor # exceptional divisor of composed_map - # restricted to domain(embeddings[end]) - - function BlowUpSequence(maps::Vector{<:BlowupMorphism}) - n = length(maps) - for i in 1:n-1 - @assert domain(maps[i]) === codomain(maps[i+1]) "not a sequence of morphisms" - end - return new{typeof(domain(maps[end])),typeof(codomain(first(maps)))}(maps) - end -end - ######################################################################## @@ -909,35 +695,7 @@ function blow_up(m::AbsCoveredScheme, I::AbsIdealSheaf; coordinate_name = "e") return blow_up(I) end -######################################################################## -# strict transforms of ideal sheaves # -######################################################################## -@attributes mutable struct StrictTransformIdealSheaf{SpaceType, OpenType, OutputType, - RestrictionType - } <: AbsIdealSheaf{ - SpaceType, OpenType, - OutputType, RestrictionType - } - morphism::AbsSimpleBlowupMorphism - orig::AbsIdealSheaf - underlying_presheaf::AbsPreSheaf - - function StrictTransformIdealSheaf( - f::AbsSimpleBlowupMorphism, - J::AbsIdealSheaf - ) - @assert scheme(J) === codomain(f) - X = domain(f) - Ipre = PreSheafOnScheme(X, - OpenType=AbsAffineScheme, OutputType=Ideal, - RestrictionType=Map, - is_open_func=_is_open_func_for_schemes_without_affine_scheme_open_subscheme(X) - ) - I = new{typeof(X), AbsAffineScheme, Ideal, Map}(f, J, Ipre) - return I - end -end morphism(I::StrictTransformIdealSheaf) = I.morphism original_ideal_sheaf(I::StrictTransformIdealSheaf) = I.orig diff --git a/experimental/Schemes/src/CoveredProjectiveSchemes.jl b/experimental/Schemes/src/CoveredProjectiveSchemes.jl index c9a3d0851e34..71228841875c 100644 --- a/experimental/Schemes/src/CoveredProjectiveSchemes.jl +++ b/experimental/Schemes/src/CoveredProjectiveSchemes.jl @@ -8,10 +8,7 @@ export projective_patches export strict_transform export weak_transform -abstract type AbsProjectiveGluing{ - GluingType<:AbsGluing, - } -end + ### getters for the essential functionality base_gluing(PG::AbsProjectiveGluing) = base_gluing(underlying_gluing(PG)) @@ -26,54 +23,6 @@ gluing_morphisms(PG::AbsProjectiveGluing) = gluing_morphisms(underlying_gluing(P # ############################################################################### -@doc raw""" - LazyProjectiveGluing( - X::AbsProjectiveScheme, - Y::AbsProjectiveScheme, - BG::AbsGluing, - compute_function::Function, - gluing_data - ) - -Produce a container `pg` to host a non-computed `ProjectiveGluing` of ``X`` with ``Y``. - -The arguments consist of - - * the patches ``X`` and ``Y`` to be glued; - * a gluing `BG` of the `base_scheme`s of ``X`` and ``Y`` over which the - `ProjectiveGluing` to be computed sits; - * a function `compute_function` which takes a single argument `gluing_data` - of arbitrary type and actually carries out the computation; - * an arbitrary struct `gluing_data` that the user can fill with whatever - information is needed to properly feed their `compute_function`. - -The container `pg` can then be stored as the gluing of ``X`` and ``Y``. As soon -as it is asked about any data on the gluing beyond its two `patches`, it will -invoke the internally stored `compute_function` to actually carry out the computation -of the gluing and then serve the incoming request on the basis of that result. -The latter actual `ProjectiveGluing` will then be cached. -""" -mutable struct LazyProjectiveGluing{ - GluingType<:AbsGluing, - GluingDataType - } <: AbsProjectiveGluing{GluingType} - base_gluing::GluingType - patches::Tuple{AbsProjectiveScheme, AbsProjectiveScheme} - compute_function::Function - gluing_data::GluingDataType - underlying_gluing::AbsProjectiveGluing - - function LazyProjectiveGluing( - X::AbsProjectiveScheme, - Y::AbsProjectiveScheme, - BG::AbsGluing, - compute_function::Function, - gluing_data - ) - (base_scheme(X), base_scheme(Y)) == patches(BG) || error("gluing is incompatible with provided patches") - return new{typeof(BG), typeof(gluing_data)}(BG, (X, Y), compute_function, gluing_data) - end -end # Essential getters patches(G::LazyProjectiveGluing) = G.patches @@ -87,87 +36,6 @@ function underlying_gluing(G::LazyProjectiveGluing) return G.underlying_gluing end -@doc raw""" - ProjectiveGluing( - G::GluingType, - incP::IncType, incQ::IncType, - f::IsoType, g::IsoType; - check::Bool=true - ) where {GluingType<:AbsGluing, IncType<:ProjectiveSchemeMor, IsoType<:ProjectiveSchemeMor} - -The `AbsProjectiveSchemeMorphism`s `incP` and `incQ` are open embeddings over open -embeddings of their respective `base_scheme`s. - - PX ↩ PU ≅ QV ↪ QY - π ↓ ↓ ↓ ↓ π - G : X ↩ U ≅ V ↪ Y - -This creates a gluing of the projective schemes `codomain(incP)` and `codomain(incQ)` -over a gluing `G` of their `base_scheme`s along the morphisms of `AbsProjectiveScheme`s -`f` and `g`, identifying `domain(incP)` and `domain(incQ)`, respectively. -""" -mutable struct ProjectiveGluing{ - GluingType<:AbsGluing, - IsoType1<:ProjectiveSchemeMor, - IncType1<:ProjectiveSchemeMor, - IsoType2<:ProjectiveSchemeMor, - IncType2<:ProjectiveSchemeMor, - } <: AbsProjectiveGluing{GluingType} - G::GluingType # the underlying gluing of the base schemes - inc_to_P::IncType1 - inc_to_Q::IncType2 - f::IsoType1 - g::IsoType2 - - ### - # Given two relative projective schemes and a gluing - # - # PX ↩ PU ≅ QV ↪ QY - # π ↓ ↓ ↓ ↓ π - # G : X ↩ U ≅ V ↪ Y - # - # this constructs the gluing of PX and QY along - # their open subsets PU and QV, given the two inclusions - # and isomorphisms over the gluing G in the base schemes. - function ProjectiveGluing( - G::GluingType, - incP::IncType1, incQ::IncType2, - f::IsoType1, g::IsoType2; - check::Bool=true - ) where {GluingType<:AbsGluing, IncType1<:ProjectiveSchemeMor,IncType2<:ProjectiveSchemeMor, IsoType1<:ProjectiveSchemeMor, IsoType2<:ProjectiveSchemeMor} - (X, Y) = patches(G) - (U, V) = gluing_domains(G) - @vprint :Gluing 1 "computing projective gluing\n" - @vprint :Gluing 2 "$(X), coordinates $(ambient_coordinates(X))\n" - @vprint :Gluing 2 "and\n" - @vprint :Gluing 2 "$(Y) coordinates $(ambient_coordinates(X))\n" - (fb, gb) = gluing_morphisms(G) - (PX, QY) = (codomain(incP), codomain(incQ)) - (PU, QV) = (domain(incP), domain(incQ)) - (base_scheme(PX) === X && base_scheme(QY) === Y) || error("base gluing is incompatible with the projective schemes") - domain(f) === codomain(g) === PU && domain(g) === codomain(f) === QV || error("maps are not compatible") - SPU = homogeneous_coordinate_ring(domain(f)) - SQV = homogeneous_coordinate_ring(codomain(f)) - @check begin - # check the commutativity of the pullbacks - all(y->(pullback(f)(SQV(OO(V)(y))) == SPU(pullback(fb)(OO(V)(y)))), gens(base_ring(OO(Y)))) || error("maps do not commute") - all(x->(pullback(g)(SPU(OO(U)(x))) == SQV(pullback(gb)(OO(U)(x)))), gens(base_ring(OO(X)))) || error("maps do not commute") - fc = map_on_affine_cones(f, check=false) - gc = map_on_affine_cones(g, check=false) - idCPU = compose(fc, gc) - idCPU == identity_map(domain(fc)) || error("composition of maps is not the identity") - idCQV = compose(gc, fc) - idCQV == identity_map(domain(gc)) || error("composition of maps is not the identity") - # idPU = compose(f, g) - # all(t->(pullback(idPU)(t) == t), gens(SPU)) || error("composition of maps is not the identity") - # idQV = compose(g, f) - # all(t->(pullback(idQV)(t) == t), gens(SQV)) || error("composition of maps is not the identity") - end - @vprint :Gluing 1 "done computing the projective gluing\n" - return new{GluingType, IsoType1, IncType1, IsoType2, IncType2}(G, incP, incQ, f, g) - end -end - function Base.show(io::IO, PG::LazyProjectiveGluing) print(io, "Gluing of projective patches (not yet computed)") end @@ -205,47 +73,6 @@ gluing_domains(PG::ProjectiveGluing) = (domain(PG.f), domain(PG.g)) patches(PG::ProjectiveGluing) = (codomain(PG.inc_to_P), codomain(PG.inc_to_Q)) gluing_morphisms(PG::ProjectiveGluing) = (PG.f, PG.g) -### Proper schemes π : Z → X over a covered base scheme X -# -# When {Uᵢ} is an affine covering of X, the datum stored -# consists of a list of projective schemes -# -# Zᵢ ⊂ ℙʳ⁽ⁱ⁾(𝒪(Uᵢ)) → Uᵢ -# -# with varying ambient spaces ℙʳ⁽ⁱ⁾(𝒪(Uᵢ)) and a list of -# identifications (transitions) -# -# Zᵢ ∩ π⁻¹(Uⱼ) ≅ Zⱼ ∩ π⁻¹(Uᵢ) -# -# of projective schemes over Uᵢ∩ Uⱼ for all pairs (i,j). -# -# These structs are designed to accommodate blowups of -# covered schemes along arbitrary centers, as well as -# projective bundles. - -@attributes mutable struct CoveredProjectiveScheme{BRT} <: Scheme{BRT} - Y::AbsCoveredScheme # the base scheme - BC::Covering # the reference covering of the base scheme - patches::IdDict{AbsAffineScheme, AbsProjectiveScheme} # the projective spaces over the affine patches in the base covering - gluings::IdDict{Tuple{AbsAffineScheme, AbsAffineScheme}, AbsProjectiveGluing} # the transitions sitting over the affine patches in the gluing domains of the base scheme - - function CoveredProjectiveScheme( - Y::AbsCoveredScheme, - C::Covering, - projective_patches::IdDict{AbsAffineScheme, AbsProjectiveScheme}, - projective_gluings::IdDict{Tuple{AbsAffineScheme, AbsAffineScheme}, AbsProjectiveGluing}; - check::Bool=true - ) - C in coverings(Y) || error("covering not listed") - for P in values(projective_patches) - any(x->x===base_scheme(P), patches(C)) || error("base scheme not found in covering") - end - for (U, V) in keys(gluings(C)) - (U, V) in keys(projective_gluings) || error("not all projective gluings were provided") - end - return new{base_ring_type(Y)}(Y, C, projective_patches, projective_gluings) - end -end base_scheme(P::CoveredProjectiveScheme) = P.Y base_covering(P::CoveredProjectiveScheme) = P.BC diff --git a/experimental/Schemes/src/Resolution_structure.jl b/experimental/Schemes/src/Resolution_structure.jl index 3c982770cfa6..eda2a0862039 100644 --- a/experimental/Schemes/src/Resolution_structure.jl +++ b/experimental/Schemes/src/Resolution_structure.jl @@ -7,123 +7,6 @@ export exceptional_locus export NormalizationMorphism export locus_of_maximal_order -############################################################################## -## Concrete Type for normalization -## very similar to CoveredSchemeMorphism, but allowing disjoint handling -## of disjoint components -############################################################################## -@doc raw""" - NormalizationMorphism{ - DomainType<:AbsCoveredScheme, - CodomainType<:AbsCoveredScheme - } <:AbsCoveredSchemeMorphism{ - DomainType, - CodomainType, - Nothing, - NormalizationMorphism, - } -A datastructure to encode normalizations of covered schemes. - -It is described as the morphism from the new scheme to the original one, containing -information on the decomposition of the new scheme into disjoint components. -(This is the type of the return value of `normalization(X::AbsCoveredScheme)`.) -""" -@attributes mutable struct NormalizationMorphism{ - DomainType<:AbsCoveredScheme, - CodomainType<:AbsCoveredScheme - } <:AbsCoveredSchemeMorphism{ - DomainType, - CodomainType, - Nothing, - NormalizationMorphism, - } - - underlying_morphism::CoveredSchemeMorphism - inclusions::Vector{<:AbsCoveredSchemeMorphism} - - function NormalizationMorphism( - f::CoveredSchemeMorphism, - inclusions::Vector{<:AbsCoveredSchemeMorphism}; - check::Bool=true - ) - @check is_normal(X) "not a normalization morphism" - @assert all(inc->codomain(inc) === domain(f), inclusions) "domains and codomains do not match" - ret_value = new{typeof(domain(f)),typeof(codomain(f))}(f,inclusions) - return ret_value - end - - function NormalizationMorphism( - f::CoveredSchemeMorphism; - check::Bool=true - ) - @check is_normal(X) "not a normalization morphism" - ret_value = new{typeof(domain(f)),typeof(codomain(f))}(f,[identity_map(X)]) - return ret_value - end - -end - -##################################################################################################### -# Desingularization morphism: birational map between covered schemes with smooth domain -##################################################################################################### -@doc raw""" - MixedBlowUpSequence{ - DomainType<:AbsCoveredScheme, - CodomainType<:AbsCoveredScheme - }<:AbsDesingMor{ DomainType, - CodomainType, - MixedBlowUpSequence{DomainType, CodomainType} - } -A datastructure to encode sequences of blow-ups and normalizations of covered schemes -as needed for desingularization of non-embedded schemes by the approaches of Zariski and of -Lipman. -""" - -@attributes mutable struct MixedBlowUpSequence{ - DomainType<:AbsCoveredScheme, - CodomainType<:AbsCoveredScheme - }<:AbsDesingMor{ DomainType, - CodomainType, - MixedBlowUpSequence{DomainType, CodomainType} - } - maps::Vector{Union{<:BlowupMorphism,<:NormalizationMorphism}} # count right to left: - # original scheme is codomain of map 1 - # boolean flags - resolves_sing::Bool # domain not smooth yet? - is_trivial::Bool # codomain already smooth? - is_strong::Bool # snc divisors ensured? - - # fields for caching, to be filled during desingularization - # always carried along to domain(maps[end])) using strict_transform - ex_div::Vector{AbsIdealSheaf} # list of exc. divisors arising from individual steps - dont_meet::Vector{Tuple{Int,Int}} # mostly for dim=2: intersections which cannot exist - # according to intermediate computations - caution_multi_charts::Vector{Tuple{Int,Int}} # only for dim=2: intersection of divisors not - # entirely visible in a single chart - - # keep track of the normalization steps - normalization_steps::Vector{Int} - - # fields for caching to be filled a posteriori (on demand, only if partial_res==false) - underlying_morphism::CompositeCoveredSchemeMorphism{DomainType, CodomainType} - exceptional_divisor::AbsWeilDivisor - exceptional_locus::AbsAlgebraicCycle - - function MixedBlowUpSequence(maps::Vector{<:AbsCoveredSchemeMorphism}) - n = length(maps) - for i in 1:n - @assert all(x->((x isa BlowupMorphism) || (x isa NormalizationMorphism)), maps) "only blow-ups and normalizations allowed" - end - for i in 1:n-1 - @assert domain(maps[i]) === codomain(maps[i+1]) "not a sequence of morphisms" - end - resi = new{typeof(domain(maps[end])),typeof(codomain(first(maps)))}(maps) - resi.normalization_steps = [i for i in 1:n if maps[i] isa NormalizationMorphism] - return resi - end - -end - ################################################################################################## # getters @@ -279,7 +162,7 @@ function _exceptional_divisor_non_embedded(f::BlowUpSequence) !isdefined(f,:exceptional_divisor_on_X) || return f.exceptional_divisor_on_X ex_div_list = exceptional_divisor_list(f) - C = CartierDivisor(scheme(ex_div_list[1]),ZZ) + C = CartierDivisor(ambient_scheme(ex_div_list[1]),ZZ) for i in 1:length(ex_div_list) # do we want to introduce is_empty for divisors? dim(ideal_sheaf(ex_div_list[i]))== -1 && continue # kick out empty ones @@ -1238,7 +1121,11 @@ function check_A1_at_point_curve(IX::Ideal, Ipt::Ideal) return vector_space_dimension(F1quo) == 1 end -function divisor_intersections_with_X(current_div, I_X) +function divisor_intersections_with_X(current_div::Vector{<:EffectiveCartierDivisor}, I_X::AbsIdealSheaf) + return divisor_intersections_with_X(ideal_sheaf.(current_div), I_X) +end + +function divisor_intersections_with_X(current_div::Vector{<:AbsIdealSheaf}, I_X::AbsIdealSheaf) scheme(I_X) == scheme(current_div[1]) || error("underlying schemes do not match") n_max = dim(I_X) @@ -1249,7 +1136,7 @@ function divisor_intersections_with_X(current_div, I_X) # initialization: each divisor + I_X for k in 1:length(current_div) - Idiv = ideal_sheaf(current_div[k]) + I_X + Idiv = current_div[k] + I_X if !is_one(Idiv) inter_div_dict[[k]] = (Idiv,0) push!(old_keys, [k]) @@ -1295,8 +1182,8 @@ end function non_snc_locus(divs::Vector{<:EffectiveCartierDivisor}) is_empty(divs) && error("list of divisors must not be empty") - X = scheme(first(divs)) - @assert all(d->scheme(d) === X, divs) + X = ambient_scheme(first(divs)) + @assert all(d->ambient_scheme(d) === X, divs) @assert is_smooth(X) r = length(divs) triv_cov = trivializing_covering.(divs) diff --git a/experimental/Schemes/src/Types.jl b/experimental/Schemes/src/Types.jl index 8d417e674c8e..447d5ab178c5 100644 --- a/experimental/Schemes/src/Types.jl +++ b/experimental/Schemes/src/Types.jl @@ -4,3 +4,496 @@ export ProjectiveScheme export ProjectiveSchemeMor export VarietyFunctionField export VarietyFunctionFieldElem + +abstract type AbsProjectiveGluing{ + GluingType<:AbsGluing, + } +end + +@doc raw""" + LazyProjectiveGluing( + X::AbsProjectiveScheme, + Y::AbsProjectiveScheme, + BG::AbsGluing, + compute_function::Function, + gluing_data + ) + +Produce a container `pg` to host a non-computed `ProjectiveGluing` of ``X`` with ``Y``. + +The arguments consist of + + * the patches ``X`` and ``Y`` to be glued; + * a gluing `BG` of the `base_scheme`s of ``X`` and ``Y`` over which the + `ProjectiveGluing` to be computed sits; + * a function `compute_function` which takes a single argument `gluing_data` + of arbitrary type and actually carries out the computation; + * an arbitrary struct `gluing_data` that the user can fill with whatever + information is needed to properly feed their `compute_function`. + +The container `pg` can then be stored as the gluing of ``X`` and ``Y``. As soon +as it is asked about any data on the gluing beyond its two `patches`, it will +invoke the internally stored `compute_function` to actually carry out the computation +of the gluing and then serve the incoming request on the basis of that result. +The latter actual `ProjectiveGluing` will then be cached. +""" +mutable struct LazyProjectiveGluing{ + GluingType<:AbsGluing, + GluingDataType + } <: AbsProjectiveGluing{GluingType} + base_gluing::GluingType + patches::Tuple{AbsProjectiveScheme, AbsProjectiveScheme} + compute_function::Function + gluing_data::GluingDataType + underlying_gluing::AbsProjectiveGluing + + function LazyProjectiveGluing( + X::AbsProjectiveScheme, + Y::AbsProjectiveScheme, + BG::AbsGluing, + compute_function::Function, + gluing_data + ) + (base_scheme(X), base_scheme(Y)) == patches(BG) || error("gluing is incompatible with provided patches") + return new{typeof(BG), typeof(gluing_data)}(BG, (X, Y), compute_function, gluing_data) + end +end + +@doc raw""" + ProjectiveGluing( + G::GluingType, + incP::IncType, incQ::IncType, + f::IsoType, g::IsoType; + check::Bool=true + ) where {GluingType<:AbsGluing, IncType<:ProjectiveSchemeMor, IsoType<:ProjectiveSchemeMor} + +The `AbsProjectiveSchemeMorphism`s `incP` and `incQ` are open embeddings over open +embeddings of their respective `base_scheme`s. + + PX ↩ PU ≅ QV ↪ QY + π ↓ ↓ ↓ ↓ π + G : X ↩ U ≅ V ↪ Y + +This creates a gluing of the projective schemes `codomain(incP)` and `codomain(incQ)` +over a gluing `G` of their `base_scheme`s along the morphisms of `AbsProjectiveScheme`s +`f` and `g`, identifying `domain(incP)` and `domain(incQ)`, respectively. +""" +mutable struct ProjectiveGluing{ + GluingType<:AbsGluing, + IsoType1<:ProjectiveSchemeMor, + IncType1<:ProjectiveSchemeMor, + IsoType2<:ProjectiveSchemeMor, + IncType2<:ProjectiveSchemeMor, + } <: AbsProjectiveGluing{GluingType} + G::GluingType # the underlying gluing of the base schemes + inc_to_P::IncType1 + inc_to_Q::IncType2 + f::IsoType1 + g::IsoType2 + + ### + # Given two relative projective schemes and a gluing + # + # PX ↩ PU ≅ QV ↪ QY + # π ↓ ↓ ↓ ↓ π + # G : X ↩ U ≅ V ↪ Y + # + # this constructs the gluing of PX and QY along + # their open subsets PU and QV, given the two inclusions + # and isomorphisms over the gluing G in the base schemes. + function ProjectiveGluing( + G::GluingType, + incP::IncType1, incQ::IncType2, + f::IsoType1, g::IsoType2; + check::Bool=true + ) where {GluingType<:AbsGluing, IncType1<:ProjectiveSchemeMor,IncType2<:ProjectiveSchemeMor, IsoType1<:ProjectiveSchemeMor, IsoType2<:ProjectiveSchemeMor} + (X, Y) = patches(G) + (U, V) = gluing_domains(G) + @vprint :Gluing 1 "computing projective gluing\n" + @vprint :Gluing 2 "$(X), coordinates $(ambient_coordinates(X))\n" + @vprint :Gluing 2 "and\n" + @vprint :Gluing 2 "$(Y) coordinates $(ambient_coordinates(X))\n" + (fb, gb) = gluing_morphisms(G) + (PX, QY) = (codomain(incP), codomain(incQ)) + (PU, QV) = (domain(incP), domain(incQ)) + (base_scheme(PX) === X && base_scheme(QY) === Y) || error("base gluing is incompatible with the projective schemes") + domain(f) === codomain(g) === PU && domain(g) === codomain(f) === QV || error("maps are not compatible") + SPU = homogeneous_coordinate_ring(domain(f)) + SQV = homogeneous_coordinate_ring(codomain(f)) + @check begin + # check the commutativity of the pullbacks + all(y->(pullback(f)(SQV(OO(V)(y))) == SPU(pullback(fb)(OO(V)(y)))), gens(base_ring(OO(Y)))) || error("maps do not commute") + all(x->(pullback(g)(SPU(OO(U)(x))) == SQV(pullback(gb)(OO(U)(x)))), gens(base_ring(OO(X)))) || error("maps do not commute") + fc = map_on_affine_cones(f, check=false) + gc = map_on_affine_cones(g, check=false) + idCPU = compose(fc, gc) + idCPU == identity_map(domain(fc)) || error("composition of maps is not the identity") + idCQV = compose(gc, fc) + idCQV == identity_map(domain(gc)) || error("composition of maps is not the identity") + # idPU = compose(f, g) + # all(t->(pullback(idPU)(t) == t), gens(SPU)) || error("composition of maps is not the identity") + # idQV = compose(g, f) + # all(t->(pullback(idQV)(t) == t), gens(SQV)) || error("composition of maps is not the identity") + end + @vprint :Gluing 1 "done computing the projective gluing\n" + return new{GluingType, IsoType1, IncType1, IsoType2, IncType2}(G, incP, incQ, f, g) + end +end + +### Proper schemes π : Z → X over a covered base scheme X +# +# When {Uᵢ} is an affine covering of X, the datum stored +# consists of a list of projective schemes +# +# Zᵢ ⊂ ℙʳ⁽ⁱ⁾(𝒪(Uᵢ)) → Uᵢ +# +# with varying ambient spaces ℙʳ⁽ⁱ⁾(𝒪(Uᵢ)) and a list of +# identifications (transitions) +# +# Zᵢ ∩ π⁻¹(Uⱼ) ≅ Zⱼ ∩ π⁻¹(Uᵢ) +# +# of projective schemes over Uᵢ∩ Uⱼ for all pairs (i,j). +# +# These structs are designed to accommodate blowups of +# covered schemes along arbitrary centers, as well as +# projective bundles. + +@attributes mutable struct CoveredProjectiveScheme{BRT} <: Scheme{BRT} + Y::AbsCoveredScheme # the base scheme + BC::Covering # the reference covering of the base scheme + patches::IdDict{AbsAffineScheme, AbsProjectiveScheme} # the projective spaces over the affine patches in the base covering + gluings::IdDict{Tuple{AbsAffineScheme, AbsAffineScheme}, AbsProjectiveGluing} # the transitions sitting over the affine patches in the gluing domains of the base scheme + + function CoveredProjectiveScheme( + Y::AbsCoveredScheme, + C::Covering, + projective_patches::IdDict{AbsAffineScheme, AbsProjectiveScheme}, + projective_gluings::IdDict{Tuple{AbsAffineScheme, AbsAffineScheme}, AbsProjectiveGluing}; + check::Bool=true + ) + C in coverings(Y) || error("covering not listed") + for P in values(projective_patches) + any(x->x===base_scheme(P), patches(C)) || error("base scheme not found in covering") + end + for (U, V) in keys(gluings(C)) + (U, V) in keys(projective_gluings) || error("not all projective gluings were provided") + end + return new{base_ring_type(Y)}(Y, C, projective_patches, projective_gluings) + end +end + + +@doc raw""" + AbsDesingMor{ + DomainType<:AbsCoveredScheme, + CodomainType<:AbsCoveredScheme, + BlowupMorphismType + } <: AbsCoveredSchemeMorphism{ + DomainType, + CodomainType, + Nothing, + BlowupMorphismType + } +Abstract type for desingularizations ``f : X -> Y `` of schemes where + + * ``Y`` is the scheme of which the singularities are to be resolved + * ``f`` is a birational proper map + may for instance be BlowUpSequence or Lipman-style combination of blow-ups and normalization + * ``Y`` is a regular scheme +""" +abstract type AbsDesingMor{ + DomainType<:AbsCoveredScheme, + CodomainType<:AbsCoveredScheme, + BlowupMorphismType + } <: AbsCoveredSchemeMorphism{ + DomainType, + CodomainType, + Nothing, + BlowupMorphismType + } +end + +######################################################################## +# An abstract type for blowup morphisms. +# +# This should also comprise sequences of simple blowups leading +# to a partial or full resolution of singularities. The interface +# is specified below. +######################################################################## +abstract type AbsBlowupMorphism{DomainType<:AbsCoveredScheme, + CodomainType<:AbsCoveredScheme, + BlowupMorphismType + } <: AbsDesingMor{DomainType, CodomainType, BlowupMorphismType} +end + +######################################################################## +# An abstract type for classical blowups of ideal sheaves. +# +# This can either be a BlowupMorphism as below, but also a special +# toric morphism induced by fan subdivisions. +######################################################################## +abstract type AbsSimpleBlowupMorphism{DomainType<:AbsCoveredScheme, + CodomainType<:AbsCoveredScheme, + BlowupMorphismType + } <: AbsBlowupMorphism{ + DomainType, + CodomainType, + BlowupMorphismType + } +end + + +######################################################################## +# BlowupMorphism +# +# A datastructure to maintain all information necessary to effectively +# handle blowups. This is work in progress and will one day serve as +# a building block for sequences of blowups +######################################################################## + +@doc raw""" + BlowupMorphism + +A datastructure to encode blowups of covered schemes in some sheaves of ideals. + +It is described as a morphism from the new scheme to the blown-up scheme, with +information about its center (i.e. the ideal sheaves blown-up in the bottom +scheme) and its exceptional locus (i.e. the preimage of the center under the +blowup). + +# Examples +```jldoctest +julia> R, (x,y,z) = QQ[:x, :y, :z]; + +julia> A3 = spec(R) +Spectrum + of multivariate polynomial ring in 3 variables x, y, z + over rational field + +julia> I = ideal(R, [x,y,z]) +Ideal generated by + x + y + z + +julia> bl = blow_up(A3, I) +Blowup + of scheme over QQ covered with 1 patch + 1b: [x, y, z] affine 3-space + in sheaf of ideals with restriction + 1b: Ideal (x, y, z) +with domain + scheme over QQ covered with 3 patches + 1a: [(s1//s0), (s2//s0), x] scheme(0, 0, 0) + 2a: [(s0//s1), (s2//s1), y] scheme(0, 0, 0) + 3a: [(s0//s2), (s1//s2), z] scheme(0, 0, 0) +and exceptional divisor + effective cartier divisor defined by + sheaf of ideals with restrictions + 1a: Ideal (x) + 2a: Ideal (y) + 3a: Ideal (z) + +julia> E = exceptional_divisor(bl) +Effective cartier divisor + on scheme over QQ covered with 3 patches + 1: [(s1//s0), (s2//s0), x] scheme(0, 0, 0) + 2: [(s0//s1), (s2//s1), y] scheme(0, 0, 0) + 3: [(s0//s2), (s1//s2), z] scheme(0, 0, 0) +defined by + sheaf of ideals with restrictions + 1: Ideal (x) + 2: Ideal (y) + 3: Ideal (z) + +julia> Z = center(bl) +Sheaf of ideals + on scheme over QQ covered with 1 patch + 1: [x, y, z] affine 3-space +with restriction + 1: Ideal (x, y, z) +``` +""" +@attributes mutable struct BlowupMorphism{ + DomainType<:AbsCoveredScheme, # Not a concrete type in general because this is lazy + CodomainType<:AbsCoveredScheme, + } <: AbsSimpleBlowupMorphism{ + DomainType, + CodomainType, + BlowupMorphism{DomainType, CodomainType} + } + projective_bundle::CoveredProjectiveScheme + codomain::CodomainType # in general a CoveredScheme + center::AbsIdealSheaf # on codomain + projection::AbsCoveredSchemeMorphism + domain::AbsCoveredScheme # in general a CoveredScheme + exceptional_divisor::EffectiveCartierDivisor + + function BlowupMorphism( + IP::CoveredProjectiveScheme, + I::AbsIdealSheaf + ) + X = base_scheme(IP) + X === scheme(I) || error("ideal sheaf not compatible with blown up variety") + return new{AbsCoveredScheme, typeof(X)}(IP, X, I) + end +end + +######################################################################## +# Resolutions of singularities # +######################################################################## + +@doc raw""" + BlowUpSequence{ + DomainType<:AbsCoveredScheme, + CodomainType<:AbsCoveredScheme + } <: AbsDesingMor{ + DomainType, + CodomainType, + } + + +""" +@attributes mutable struct BlowUpSequence{ + DomainType<:AbsCoveredScheme, + CodomainType<:AbsCoveredScheme + }<:AbsBlowupMorphism{ + DomainType, CodomainType, + BlowUpSequence{DomainType, CodomainType} + } + maps::Vector{<:BlowupMorphism} # count right to left: + # original scheme is codomain of map 1 + + embeddings::Vector{<:AbsCoveredSchemeMorphism} # if set, + # assert codomain(maps[i])===codomain(embeddings[i]) + # boolean flags + is_embedded::Bool # do not set embeddings, ex_mult, controlled_transform etc + # if is_embedded == false + resolves_sing::Bool # domain(maps[end]) smooth? + is_trivial::Bool # codomain already smooth? + is_strong::Bool # snc divisors ensured? + transform_type::Symbol # can be :strict, :weak or :control + # only relevant for is_embedded == true + + # fields for caching, may be filled during computation + ex_div::Vector{<:EffectiveCartierDivisor} # list of exc. divisors arising from individual steps + # lives in domain(maps[end]) + control::Int # value of control for controlled transform + ex_mult::Vector{Int} # multiplicities of exceptional divisors removed from + # total transform, not set for is_embedded == false + # or transform_type == strict + controlled_transform::AbsIdealSheaf # holds weak or controlled transform according to transform_type + dont_meet::Vector{Tuple{Int,Int}} # mostly for dim=2: intersections which cannot exist according + # to intermediate computations + caution_multi_charts::Vector{Tuple{Int,Int}} # only for dim=2: intersection of divisors not + # entirely visible in a single chart + + + # fields for caching to be filled a posteriori (on demand, only if partial_res==false) + underlying_morphism::CompositeCoveredSchemeMorphism{DomainType, CodomainType} + exceptional_divisor::CartierDivisor # exceptional divisor of composed_map + exceptional_locus::WeilDivisor # exceptional locus of composed map + exceptional_divisor_on_X::CartierDivisor # exceptional divisor of composed_map + # restricted to domain(embeddings[end]) + + function BlowUpSequence(maps::Vector{<:BlowupMorphism}) + n = length(maps) + for i in 1:n-1 + @assert domain(maps[i]) === codomain(maps[i+1]) "not a sequence of morphisms" + end + return new{typeof(domain(maps[end])),typeof(codomain(first(maps)))}(maps) + end +end + +######################################################################## +# strict transforms of ideal sheaves # +######################################################################## + +@attributes mutable struct StrictTransformIdealSheaf{SpaceType, OpenType, OutputType, + RestrictionType + } <: AbsIdealSheaf{ + SpaceType, OpenType, + OutputType, RestrictionType + } + morphism::AbsSimpleBlowupMorphism + orig::AbsIdealSheaf + underlying_presheaf::AbsPreSheaf + + function StrictTransformIdealSheaf( + f::AbsSimpleBlowupMorphism, + J::AbsIdealSheaf + ) + @assert scheme(J) === codomain(f) + X = domain(f) + Ipre = PreSheafOnScheme(X, + OpenType=AbsAffineScheme, OutputType=Ideal, + RestrictionType=Map, + is_open_func=_is_open_func_for_schemes_without_affine_scheme_open_subscheme(X) + ) + I = new{typeof(X), AbsAffineScheme, Ideal, Map}(f, J, Ipre) + return I + end +end + +##################################################################################################### +# Desingularization morphism: birational map between covered schemes with smooth domain +##################################################################################################### +@doc raw""" + MixedBlowUpSequence{ + DomainType<:AbsCoveredScheme, + CodomainType<:AbsCoveredScheme + }<:AbsDesingMor{ DomainType, + CodomainType, + MixedBlowUpSequence{DomainType, CodomainType} + } +A datastructure to encode sequences of blow-ups and normalizations of covered schemes +as needed for desingularization of non-embedded schemes by the approaches of Zariski and of +Lipman. +""" + +@attributes mutable struct MixedBlowUpSequence{ + DomainType<:AbsCoveredScheme, + CodomainType<:AbsCoveredScheme + }<:AbsDesingMor{ DomainType, + CodomainType, + MixedBlowUpSequence{DomainType, CodomainType} + } + maps::Vector{Union{<:BlowupMorphism,<:NormalizationMorphism}} # count right to left: + # original scheme is codomain of map 1 + # boolean flags + resolves_sing::Bool # domain not smooth yet? + is_trivial::Bool # codomain already smooth? + is_strong::Bool # snc divisors ensured? + + # fields for caching, to be filled during desingularization + # always carried along to domain(maps[end])) using strict_transform + ex_div::Vector{AbsIdealSheaf} # list of exc. divisors arising from individual steps + dont_meet::Vector{Tuple{Int,Int}} # mostly for dim=2: intersections which cannot exist + # according to intermediate computations + caution_multi_charts::Vector{Tuple{Int,Int}} # only for dim=2: intersection of divisors not + # entirely visible in a single chart + + # keep track of the normalization steps + normalization_steps::Vector{Int} + + # fields for caching to be filled a posteriori (on demand, only if partial_res==false) + underlying_morphism::CompositeCoveredSchemeMorphism{DomainType, CodomainType} + exceptional_divisor::AbsWeilDivisor + exceptional_locus::AbsAlgebraicCycle + + function MixedBlowUpSequence(maps::Vector{<:AbsCoveredSchemeMorphism}) + n = length(maps) + for i in 1:n + @assert all(x->((x isa BlowupMorphism) || (x isa NormalizationMorphism)), maps) "only blow-ups and normalizations allowed" + end + for i in 1:n-1 + @assert domain(maps[i]) === codomain(maps[i+1]) "not a sequence of morphisms" + end + resi = new{typeof(domain(maps[end])),typeof(codomain(first(maps)))}(maps) + resi.normalization_steps = [i for i in 1:n if maps[i] isa NormalizationMorphism] + return resi + end + +end + + diff --git a/experimental/Schemes/src/elliptic_surface.jl b/experimental/Schemes/src/elliptic_surface.jl index 537cbb3ecfa3..d76d945f7d6f 100644 --- a/experimental/Schemes/src/elliptic_surface.jl +++ b/experimental/Schemes/src/elliptic_surface.jl @@ -2589,7 +2589,7 @@ function point_on_generic_fiber_from_divisor(I::AbsIdealSheaf{<:EllipticSurface} end function point_on_generic_fiber_from_divisor(D::AbsWeilDivisor{<:EllipticSurface}; check::Bool=true) - X = scheme(D) + X = ambient_scheme(D) E = generic_fiber(X) ex, pt, F = irreducible_fiber(X) WF = weil_divisor(F) @@ -2681,7 +2681,7 @@ function extract_mordell_weil_basis(phi::MorphismFromRationalFunctions{<:Ellipti end function _prepare_section(D::AbsWeilDivisor{<:EllipticSurface}) - X = scheme(D) + X = ambient_scheme(D) WX = weierstrass_chart_on_minimal_model(X) R = ambient_coordinate_ring(WX) I = first(components(D)) diff --git a/src/AlgebraicGeometry/Schemes/CoveredSchemes/Morphisms/Types.jl b/src/AlgebraicGeometry/Schemes/CoveredSchemes/Morphisms/Types.jl index 1bfa55576689..c4db15b91464 100644 --- a/src/AlgebraicGeometry/Schemes/CoveredSchemes/Morphisms/Types.jl +++ b/src/AlgebraicGeometry/Schemes/CoveredSchemes/Morphisms/Types.jl @@ -49,3 +49,61 @@ underlying_morphism(f::CoveredSchemeMorphism) = f function CoveredSchemeMorphism(f::CoveredSchemeMorphism) return f end + + + +############################################################################## +## Concrete Type for normalization +## very similar to CoveredSchemeMorphism, but allowing disjoint handling +## of disjoint components +############################################################################## +@doc raw""" + NormalizationMorphism{ + DomainType<:AbsCoveredScheme, + CodomainType<:AbsCoveredScheme + } <:AbsCoveredSchemeMorphism{ + DomainType, + CodomainType, + Nothing, + NormalizationMorphism, + } +A datastructure to encode normalizations of covered schemes. + +It is described as the morphism from the new scheme to the original one, containing +information on the decomposition of the new scheme into disjoint components. +(This is the type of the return value of `normalization(X::AbsCoveredScheme)`.) +""" +@attributes mutable struct NormalizationMorphism{ + DomainType<:AbsCoveredScheme, + CodomainType<:AbsCoveredScheme + } <:AbsCoveredSchemeMorphism{ + DomainType, + CodomainType, + Nothing, + NormalizationMorphism, + } + + underlying_morphism::CoveredSchemeMorphism + inclusions::Vector{<:AbsCoveredSchemeMorphism} + + function NormalizationMorphism( + f::CoveredSchemeMorphism, + inclusions::Vector{<:AbsCoveredSchemeMorphism}; + check::Bool=true + ) + @check is_normal(X) "not a normalization morphism" + @assert all(inc->codomain(inc) === domain(f), inclusions) "domains and codomains do not match" + ret_value = new{typeof(domain(f)),typeof(codomain(f))}(f,inclusions) + return ret_value + end + + function NormalizationMorphism( + f::CoveredSchemeMorphism; + check::Bool=true + ) + @check is_normal(X) "not a normalization morphism" + ret_value = new{typeof(domain(f)),typeof(codomain(f))}(f,[identity_map(X)]) + return ret_value + end + +end diff --git a/src/AlgebraicGeometry/Schemes/Divisors/AlgebraicCycles.jl b/src/AlgebraicGeometry/Schemes/Divisors/AlgebraicCycles.jl index 28d2fb2d9b6c..eb536f08b381 100644 --- a/src/AlgebraicGeometry/Schemes/Divisors/AlgebraicCycles.jl +++ b/src/AlgebraicGeometry/Schemes/Divisors/AlgebraicCycles.jl @@ -1,6 +1,3 @@ - - - ######################################################################## # # AbsAlgebraicCycle @@ -26,11 +23,11 @@ coefficient_ring_elem_type(::Type{AbsAlgebraicCycle{S, U}}) where {S, U} = elem_ ### essential getters and functionality @doc raw""" - scheme(D::AbsAlgebraicCycle) + ambient_scheme(D::AbsAlgebraicCycle) Return the `CoveredScheme` ``X`` on which `D` is defined. """ -scheme(D::AbsAlgebraicCycle) = scheme(underlying_cycle(D)) +ambient_scheme(D::AbsAlgebraicCycle) = ambient_scheme(underlying_cycle(D)) # For an element `I` of `components(D)`, this returns the coefficient # of `I` in the formal sum for `D`. @@ -39,25 +36,44 @@ getindex(D::AbsAlgebraicCycle, I::AbsIdealSheaf) = getindex(underlying_cycle(D), @doc raw""" components(D::AbsAlgebraicCycle) -Return the irreducible components ``Eⱼ`` of the divisor -``D = Σⱼ aⱼ ⋅ Eⱼ``. +Return a list of ideal sheaves such that `D` is a linear combination of +the corresponding cycles. + +!!! note + The order of the components may change in different julia sessions. + It is however consistent with the printing. + +!!! note + The ideal sheaves are only guaranteed equidimensional and may carry multiplicities. + See [`irreducible_decomposition(::AbsAlgebraicCycle)`](@ref) + for the more conventional decomposition. + """ components(D::AbsAlgebraicCycle) = components(underlying_cycle(D)) # Return the coefficient ring over which the cycle is defined coefficient_ring(D::AbsAlgebraicCycle) = coefficient_ring(underlying_cycle(D)) -# All `components` of a cycle `D` must be prime. This returns the supremum -# of their dimensions. + +@doc raw""" + dim(D::AbsAlgebraicCycle) + +Return the dimension of the support of the cycle `D`. +""" dim(D::AbsAlgebraicCycle) = dim(underlying_cycle(D)) set_name!(X::AbsAlgebraicCycle, name::String) = set_attribute!(X, :name, name) name(X::AbsAlgebraicCycle) = get_attribute(X, :name)::String has_name(X::AbsAlgebraicCycle) = has_attribute(X, :name) -function setindex!(D::AbsAlgebraicCycle, c::RingElem, I::AbsIdealSheaf) +@doc raw""" + setindex!(D::AbsAlgebraicCycle, c::RingElem, I::AbsIdealSheaf) + +Set the coefficient of `D` at `I` to `c`. +""" +function setindex!(D::AbsAlgebraicCycle, c::RingElem, I::AbsIdealSheaf; check::Bool=true) parent(c) === coefficient_ring(D) || error("coefficient does not belong to the correct ring") - return setindex!(underlying_cycle(D), c, I) + return setindex!(underlying_cycle(D), c, I; check) end # Non user-facing getters @@ -76,10 +92,39 @@ function coeff(D::AbsAlgebraicCycle, I::AbsIdealSheaf) end end +@doc raw""" + is_effective(A::AbsAlgebraicCycle) + +Return whether all the coefficients are non-negative. +""" function is_effective(A::AbsAlgebraicCycle) return all(coeff(A, I)>=0 for I in components(A)) end +# Prime cycles are those written as 1*Sheaf of prime ideals +@doc raw""" + is_prime(D::AbsAlgebraicCycle) + +An algebraic cycle is called prime if it consists of a single irreducible subvariety. + +Note that this property is not stable under base extension. +""" +@attr Bool function is_prime(D::AbsAlgebraicCycle) + length(components(D)) == 0 && return false # Cannot be prime if there are no components + E = irreducible_decomposition(D) + C = coefficient_dict(E) + length(C)>1 && return false + return isone(first(values(C))) +end + +is_irreducible(D::AbsAlgebraicCycle) = is_prime(D) + + +@doc raw""" + Base.:<=(A::AbsAlgebraicCycle, B::AbsAlgebraicCycle) + +$A \leq B$ if and only if $B - A$ is effective. +""" function Base.:<=(A::AbsAlgebraicCycle,B::AbsAlgebraicCycle) for I in components(A) coeff(A, I) <= coeff(B, I) || return false @@ -96,7 +141,7 @@ function underlying_cycle(D::AbsAlgebraicCycle) end ### implementation of the essential functionality -scheme(D::AlgebraicCycle) = D.X +ambient_scheme(D::AlgebraicCycle) = D.X getindex(D::AlgebraicCycle, I::AbsIdealSheaf) = (D.coefficients)[I] components(D::AlgebraicCycle) = collect(keys(D.coefficients)) @@ -107,8 +152,9 @@ set_name!(X::AlgebraicCycle, name::String) = set_attribute!(X, :name, name) name(X::AlgebraicCycle) = get_attribute(X, :name)::String has_name(X::AlgebraicCycle) = has_attribute(X, :name) -function setindex!(D::AlgebraicCycle, c::RingElem, I::AbsIdealSheaf) +function setindex!(D::AlgebraicCycle, c::RingElem, I::AbsIdealSheaf; check::Bool=true) parent(c) === coefficient_ring(D) || error("coefficient does not belong to the correct ring") + @check is_equidimensional(I) coefficient_dict(D)[I] = c end @@ -118,13 +164,13 @@ end Return the zero `AlgebraicCycle` over `X` with coefficients in `R`. """ -function AlgebraicCycle(X::AbsCoveredScheme, R::Ring) +function AlgebraicCycle(X::AbsCoveredScheme, R::Ring; check::Bool=true) D = IdDict{AbsIdealSheaf, elem_type(R)}() - return AlgebraicCycle(X, R, D) + return AlgebraicCycle(X, R, D; check) end -function zero(D::AbsAlgebraicCycle) - return AlgebraicCycle(scheme(D), coefficient_ring(D)) +function zero(D::AbsAlgebraicCycle; check::Bool=true) + return AlgebraicCycle(ambient_scheme(D), coefficient_ring(D); check) end # provide non-camelcase methods @@ -152,16 +198,16 @@ Zero algebraic cycle with coefficients in integer ring ``` """ -algebraic_cycle(X::AbsCoveredScheme, R::Ring) = AlgebraicCycle(X, R) +algebraic_cycle(X::AbsCoveredScheme, R::Ring; check::Bool=true) = AlgebraicCycle(X, R; check) @doc raw""" AlgebraicCycle(I::AbsIdealSheaf, R::Ring) -Return the `AlgebraicCycle` ``D = 1 ⋅ V(I)`` with coefficients -in ``R`` for a sheaf of prime ideals ``I``. +Return the `AlgebraicCycle` ``D = 1 ⋅ I`` with coefficients +in ``R`` for a sheaf of equidimensional ideals ``I``. """ -function AlgebraicCycle(I::AbsIdealSheaf, R::Ring) - D = AlgebraicCycle(space(I), R) +function AlgebraicCycle(I::AbsIdealSheaf, R::Ring; check::Bool=true) + D = AlgebraicCycle(space(I), R; check) D[I] = one(R) return D end @@ -169,9 +215,11 @@ end @doc raw""" algebraic_cycle(I::AbsIdealSheaf, R::Ring) -> AlgebraicCycle -Return the `AlgebraicCycle` ``D = 1 ⋅ V(I)`` with coefficients -in ``R`` for a sheaf of prime ideals ``I``. +Return the `AlgebraicCycle` ``D = 1 ⋅ I`` with coefficients +in ``R`` for a sheaf of equidimensional ideals ``I``. +Note that ``I`` must be equidimensional. + # Examples ```jldoctest julia> P, (x, y, z) = graded_polynomial_ring(QQ, [:x, :y, :z]); @@ -193,16 +241,16 @@ given as the formal sum of ``` """ -algebraic_cycle(I::AbsIdealSheaf, R::Ring) = AlgebraicCycle(I, R) +algebraic_cycle(I::AbsIdealSheaf, R::Ring; check::Bool=true) = AlgebraicCycle(I, R; check) @doc raw""" AlgebraicCycle(I::AbsIdealSheaf) -Return the `AlgebraicCycle` ``D = 1 ⋅ V(I)`` with coefficients -in ``ℤ`` for a sheaf of prime ideals ``I``. +Return the `AlgebraicCycle` ``D = 1 ⋅ I`` with coefficients +in ``ℤ`` for a sheaf of equidimensional ideals ``I``. """ -function AlgebraicCycle(I::AbsIdealSheaf) - D = AlgebraicCycle(space(I), ZZ) +function AlgebraicCycle(I::AbsIdealSheaf; check::Bool=true) + D = AlgebraicCycle(space(I), ZZ; check) D[I] = one(ZZ) return D end @@ -210,8 +258,8 @@ end @doc raw""" algebraic_cycle(I::AbsIdealSheaf) -> AlgebraicCycle -Return the `AlgebraicCycle` ``D = 1 ⋅ V(I)`` with coefficients -in ``ℤ`` for a sheaf of prime ideals ``I``. +Return the `AlgebraicCycle` ``D = 1 ⋅ I`` with coefficients +in ``ℤ`` for a sheaf of equidimensional ideals ``I``. # Examples ```jldoctest @@ -233,7 +281,7 @@ given as the formal sum of 1 * sheaf of ideals ``` """ -algebraic_cycle(I::AbsIdealSheaf) = AlgebraicCycle(I) +algebraic_cycle(I::AbsIdealSheaf; check::Bool=true) = AlgebraicCycle(I; check) ### copy constructor function copy(D::AlgebraicCycle) @@ -241,7 +289,7 @@ function copy(D::AlgebraicCycle) for I in keys(coefficient_dict(D)) new_dict[I] = D[I] end - return AlgebraicCycle(scheme(D), coefficient_ring(D), new_dict) + return AlgebraicCycle(ambient_scheme(D), coefficient_ring(D), new_dict) end ############################################################################### @@ -258,7 +306,7 @@ end # needs to take care about some left offsets. function Base.show(io::IO, ::MIME"text/plain", D::AlgebraicCycle) io = pretty(io) - X = scheme(D) + X = ambient_scheme(D) # If the IO context knows about a covering to be used, we use this one. # Otherwise, we check whether X has a simplified covering. If not, we use the # default covering of X @@ -305,7 +353,7 @@ end function Base.show(io::IO, D::AlgebraicCycle) io = pretty(io) - X = scheme(D) + X = ambient_scheme(D) eff = all(i >= 0 for i in collect(values(D.coefficients))) if length(components(D)) == 1 prim = D[components(D)[1]] == 1 ? true : false @@ -317,20 +365,20 @@ function Base.show(io::IO, D::AlgebraicCycle) elseif is_terse(io) print(io, "Algebraic cycle") elseif length(components(D)) == 0 - print(io, "Zero algebraic cycle on ", Lowercase(), scheme(D)) + print(io, "Zero algebraic cycle on ", Lowercase(), ambient_scheme(D)) elseif eff if prim - print(io, "Irreducible algebraic cycle on ", Lowercase(), scheme(D)) + print(io, "Irreducible algebraic cycle on ", Lowercase(), ambient_scheme(D)) else - print(io, "Effective algebraic cycle on ", Lowercase(), scheme(D)) + print(io, "Effective algebraic cycle on ", Lowercase(), ambient_scheme(D)) end else - print(io, "Algebraic cycle on ", Lowercase(), scheme(D)) + print(io, "Algebraic cycle on ", Lowercase(), ambient_scheme(D)) end end -@attr Any function dim(D::AlgebraicCycle) +@attr Int function dim(D::AlgebraicCycle) result = -1 for I in components(D) d = dim(I) @@ -348,8 +396,8 @@ end # so the implementation can not be truly generic. function +(D::T, E::T) where {T<:AbsAlgebraicCycle} - X = scheme(D) - X === scheme(E) || error("divisors do not live on the same scheme") + X = ambient_scheme(D) + X === ambient_scheme(E) || error("divisors do not live on the same scheme") R = coefficient_ring(D) R === coefficient_ring(E) || error("coefficient rings do not coincide") dict = IdDict{AbsIdealSheaf, elem_type(R)}() @@ -376,7 +424,7 @@ function -(D::T) where {T<:AbsAlgebraicCycle} for I in keys(coefficient_dict(D)) dict[I] = -D[I] end - return AlgebraicCycle(scheme(D), coefficient_ring(D), dict, check=false) + return AlgebraicCycle(ambient_scheme(D), coefficient_ring(D), dict, check=false) end -(D::T, E::T) where {T<:AbsAlgebraicCycle} = D + (-E) @@ -391,7 +439,7 @@ function *(a::RingElem, E::AbsAlgebraicCycle) dict[I] = c end end - return AlgebraicCycle(scheme(E), coefficient_ring(E), dict, check=false) + return AlgebraicCycle(ambient_scheme(E), coefficient_ring(E), dict, check=false) end *(a::Int, E::AbsAlgebraicCycle) = coefficient_ring(E)(a)*E @@ -402,7 +450,7 @@ end @doc raw""" irreducible_decomposition(D::AbsAlgebraicCycle) -Return a divisor ``E`` equal to ``D`` but as a formal sum ``E = ∑ₖ aₖ ⋅ Iₖ`` +Return a cycle ``E`` equal to ``D`` but as a formal sum ``E = ∑ₖ aₖ ⋅ Iₖ`` where the `components` ``Iₖ`` of ``E`` are all sheaves of prime ideals. """ function irreducible_decomposition(D::AbsAlgebraicCycle) @@ -415,7 +463,7 @@ function irreducible_decomposition(D::AbsAlgebraicCycle) k = _colength_in_localization(I, P) next_dict[P] = coefficient_ring(D)(k) end - result = result + a * AlgebraicCycle(scheme(D), coefficient_ring(D), next_dict, check=false) + result = result + a * AlgebraicCycle(ambient_scheme(D), coefficient_ring(D), next_dict, check=false) end return result end @@ -476,7 +524,7 @@ the lengths of all the components of dimension `0` of ``W``. """ function integral(W::AbsAlgebraicCycle; check::Bool=true) result = zero(coefficient_ring(W)) - X = scheme(W) + X = ambient_scheme(W) for I in components(W) @check begin dim(I) == 0 || continue diff --git a/src/AlgebraicGeometry/Schemes/Divisors/CartierDivisor.jl b/src/AlgebraicGeometry/Schemes/Divisors/CartierDivisor.jl index d740246057ee..34c5e1255791 100644 --- a/src/AlgebraicGeometry/Schemes/Divisors/CartierDivisor.jl +++ b/src/AlgebraicGeometry/Schemes/Divisors/CartierDivisor.jl @@ -1,11 +1,31 @@ - function (C::EffectiveCartierDivisor)(U::AbsAffineScheme) return gens(C.I(U)) end +iszero(C::EffectiveCartierDivisor) = isone(ideal_sheaf(C)) + +@doc raw""" + ideal_sheaf(C::EffectiveCartierDivisor) + +Return the sheaf of ideals $\mathcal{I}_C \subseteq \mathcal{O}_X$ representing `C`. +""" ideal_sheaf(C::EffectiveCartierDivisor) = C.I -scheme(C::EffectiveCartierDivisor) = C.X +@doc raw""" + ambient_scheme(C::EffectiveCartierDivisor) + +Return the ambient scheme containing `C`. +""" +ambient_scheme(C::EffectiveCartierDivisor) = C.X + +@doc raw""" + trivializing_covering(C::EffectiveCartierDivisor) + +Return the trivializing covering of the effective Cartier divisor `C`. + +A covering $(U_i)_{i \in I}$ is called trivializing for $C$ if +$C(U_i)$ is principal for all $i \in I$. +""" trivializing_covering(C::EffectiveCartierDivisor) = C.C function EffectiveCartierDivisor(I::AbsIdealSheaf; @@ -21,17 +41,32 @@ function EffectiveCartierDivisor(I::AbsIdealSheaf; return EffectiveCartierDivisor(X, eq_dict, trivializing_covering=trivializing_covering, check=check) end +@doc raw""" + ambient_scheme(C::CartierDivisor) + +Return the ambient scheme containing `C`. +""" +ambient_scheme(C::CartierDivisor) = C.X - - -scheme(C::CartierDivisor) = C.X +@doc raw""" + coefficient_ring(C::CartierDivisor) + +Return the ring of coefficients of `C`. +""" coefficient_ring(C::CartierDivisor) = C.R + coefficient_dict(C::CartierDivisor) = C.coeff_dict getindex(C::CartierDivisor, k::EffectiveCartierDivisor) = coefficient_dict(C)[k] + +@doc raw""" + components(C::CartierDivisor) + +Return a list of effective Cartier divisors $C_i$ such that $C$ is a linear combination of the $C_i$. +""" components(C::CartierDivisor) = collect(keys(coefficient_dict(C))) function +(C::CartierDivisor, D::CartierDivisor) - scheme(C) === scheme(D) || error("divisors must be defined over the same scheme") + ambient_scheme(C) === ambient_scheme(D) || error("divisors must be defined over the same scheme") coefficient_ring(C) === coefficient_ring(D) || error("divisors must have the same coefficient rings") R = coefficient_ring(C) coeff_dict = IdDict{EffectiveCartierDivisor, elem_type(R)}() @@ -50,7 +85,7 @@ function +(C::CartierDivisor, D::CartierDivisor) coeff_dict[k] = D[k] end end - return CartierDivisor(scheme(C), coefficient_ring(C), coeff_dict) + return CartierDivisor(ambient_scheme(C), coefficient_ring(C), coeff_dict) end function +(C::CartierDivisor, D::EffectiveCartierDivisor) @@ -65,6 +100,8 @@ function +(C::EffectiveCartierDivisor, D::CartierDivisor) return CartierDivisor(C) + D end +zero(D::CartierDivisor) = CartierDivisor(ambient_scheme(D),coefficient_ring(D)) + function *(a::RingElem, C::CartierDivisor) parent(a) === coefficient_ring(C) || return coefficient_ring(C)(a)*C coeff_dict = IdDict{EffectiveCartierDivisor, typeof(a)}() @@ -76,7 +113,7 @@ function *(a::RingElem, C::CartierDivisor) coeff_dict[k] = c end end - return CartierDivisor(scheme(C), coefficient_ring(C), coeff_dict) + return CartierDivisor(ambient_scheme(C), coefficient_ring(C), coeff_dict) end function *(a::Integer, C::CartierDivisor) @@ -88,18 +125,28 @@ function -(C::CartierDivisor, D::CartierDivisor) end function iszero(C::CartierDivisor) - return iszero(length(keys(coefficient_dict(C)))) || all(k->iszero(C[k]), components(C)) + iszero(length(keys(coefficient_dict(C)))) && return true + all(iszero, values(coefficient_dict(C))) && return true + all(iszero, keys(coefficient_dict(C))) && return true + + # write C = P - M with P and M effective. + # TODO: Multiplying everything together is quick and dirty + P = sum(ai*Ci for (Ci,ai) in coefficient_dict(C) if ai>0; init=zero(C)) + M = sum(-ai*Ci for (Ci,ai) in coefficient_dict(C) if ai<0; init=zero(C)) + if iszero(length(coefficient_dict(P))) || iszero(length(coefficient_dict(M))) + # we know that there is at least one non-zero summand and no cancellation + return false + end + IP = prod(ideal_sheaf(Ci)^ai for (Ci,ai) in coefficient_dict(P)) + IM = prod(ideal_sheaf(Ci)^ai for (Ci,ai) in coefficient_dict(M)) + return IP==IM end @doc raw""" cartier_divisor(E::EffectiveCartierDivisor) -> CartierDivisor -Given an effective cartier divisor `E`, return the cartier divisor -$1*E$. - -Mathematically both objects are the same, this function is a coercion method -to see effective cartier divisors as irreducible cartier divisor with coefficient -1. +Convert an `EffectiveCartierDivisor` into a `CartierDivisor` with +coefficient $1$ in the ring of integers. # Examples ```jldoctest @@ -134,7 +181,7 @@ defined by the formal sum of cartier_divisor(E::EffectiveCartierDivisor) = CartierDivisor(E) function CartierDivisor(C::EffectiveCartierDivisor) - return CartierDivisor(scheme(C), ZZ, IdDict([C => one(ZZ)])) + return CartierDivisor(ambient_scheme(C), ZZ, IdDict([C => one(ZZ)])) end function CartierDivisor(X::AbsCoveredScheme, kk::Ring) @@ -142,21 +189,14 @@ function CartierDivisor(X::AbsCoveredScheme, kk::Ring) end function *(a::RingElem, C::EffectiveCartierDivisor) - return CartierDivisor(scheme(C), parent(a), IdDict{EffectiveCartierDivisor, typeof(a)}([C => a])) + return CartierDivisor(ambient_scheme(C), parent(a), IdDict{EffectiveCartierDivisor, typeof(a)}([C => a])) end function *(a::Integer, C::EffectiveCartierDivisor) - return CartierDivisor(scheme(C), ZZ, IdDict{EffectiveCartierDivisor, elem_type(ZZ)}([C => ZZ(a)])) + return CartierDivisor(ambient_scheme(C), ZZ, IdDict{EffectiveCartierDivisor, elem_type(ZZ)}([C => ZZ(a)])) end function ==(C::CartierDivisor, D::CartierDivisor) - C === D && return true - for k in components(C) - iszero(C[k]) || (haskey(coefficient_dict(D), k) && D[k] == C[k]) || error("equality check not implemented in this complicated case") - end - for k in components(D) - iszero(D[k]) || (haskey(coefficient_dict(C), k) && D[k] == C[k]) || error("equality check not implemented in this complicated case") - end - return true + return iszero(C-D) end @doc raw""" @@ -192,6 +232,12 @@ defined by """ effective_cartier_divisor(I::AbsIdealSheaf; trivializing_covering::Covering = default_covering(scheme(I)), check::Bool = true) = EffectiveCartierDivisor(I, trivializing_covering=trivializing_covering, check=check) +@doc raw""" + effective_cartier_divisor(IP::AbsProjectiveScheme, f::Union{MPolyDecRingElem, MPolyQuoRingElem}) + +Return the effective Cartier divisor on the projective scheme ``X`` defined by the homogeneous +polynomial ``f``. +""" function effective_cartier_divisor(IP::AbsProjectiveScheme, f::Union{MPolyDecRingElem, MPolyQuoRingElem}) parent(f) === homogeneous_coordinate_ring(IP) || error("element does not belong to the correct ring") d = degree(f) @@ -204,6 +250,12 @@ function effective_cartier_divisor(IP::AbsProjectiveScheme, f::Union{MPolyDecRin return C end +@doc raw""" + cartier_divisor(IP::AbsProjectiveScheme, f::Union{MPolyDecRingElem, MPolyQuoRingElem}) + +Return the (effective) Cartier divisor on the projective scheme ``X`` defined by the homogeneous +polynomial ``f``. +""" function cartier_divisor(IP::AbsProjectiveScheme, f::Union{MPolyDecRingElem, MPolyQuoRingElem}) return one(ZZ)*effective_cartier_divisor(IP, f) end @@ -212,12 +264,15 @@ end ### (specialized variant of associated_points, using pure codimension 1 ### and taking multiplicities into account) @doc raw""" - irreducible_decomposition(C::EffectiveCartierDivisor) + irreducible_decomposition(C::EffectiveCartierDivisor; check::Bool=true) -> AbsWeilDivisor + +Return $C$ as a linear combination of prime Weil divisors. -Return a `Vector` of pairs ``(I,k)`` corresponding to the irreducible components of ``C``. More precisely, each ``I`` is a prime `AbsIdealSheaf` corresponding to an irreducible component of ``C`` and ``k``is the multiplicity of this component in ``C``. +Assumes that the ambient scheme is integral and locally noetherian. """ -function irreducible_decomposition(C::EffectiveCartierDivisor) - X = scheme(C) +function irreducible_decomposition(C::EffectiveCartierDivisor; check::Bool=true) + X = ambient_scheme(C) + @check is_integral(X) "ambient scheme must be integral" cov = default_covering(X) OOX = OO(X) @@ -241,13 +296,12 @@ function irreducible_decomposition(C::EffectiveCartierDivisor) components_here = minimal_primes(I_temp) for comp in components_here I_temp, saturation_index = saturation_with_index(I_temp, comp) - temp_dict=IdDict{AbsAffineScheme,Ideal}() - temp_dict[U] = comp - I_sheaf_temp = IdealSheaf(X, extend!(cov, temp_dict), check=false) + I_sheaf_temp = PrimeIdealSheafFromChart(X, U, comp, check=false) push!(associated_primes_temp, (I_sheaf_temp, saturation_index)) end end - return(associated_primes_temp) + D = WeilDivisor(X, ZZ, IdDict(associated_primes_temp); check=false) + return D end ### Conversion into WeilDivisors @@ -255,28 +309,10 @@ function weil_divisor(C::EffectiveCartierDivisor; is_prime::Bool=false # Indicate whether this divisor is already prime ) return WeilDivisor(ideal_sheaf(C), ZZ, check=is_prime) - - # TODO: See what we can recycle from the code below. - X = scheme(C) - OOX = OO(X) - - decomp = Vector{Tuple{typeof(ideal_sheaf(C)), Int}}() - if is_prime - push!(decomp, (ideal_sheaf(C), 1)) - else - decomp = irreducible_decomposition(C) - end - result = WeilDivisor(X, ZZ) - - for (I,k) in decomp - result = result + k*WeilDivisor(I,ZZ, check=false) - end - - return result end function weil_divisor(C::CartierDivisor) - X = scheme(C) + X = ambient_scheme(C) kk = coefficient_ring(C) result = WeilDivisor(X, kk) for c in components(C) @@ -285,11 +321,16 @@ function weil_divisor(C::CartierDivisor) return result end +@doc raw""" + intersect(W::WeilDivisor, C::EffectiveCartierDivisor; check::Bool=true) + +Computes the intersection of ``W`` and ``C`` as in [Ful98](@cite) and +returns an `AbsAlgebraicCycle` of codimension ``2``. +""" function intersect(W::WeilDivisor, C::EffectiveCartierDivisor; check::Bool=true) - X = scheme(W) + X = ambient_scheme(W) result = zero(W) - for I in components(W) - @check is_prime(I) "all components of the first argument must be sheaves of prime ideals" + for I in components(irreducible_decomposition(W)) inc_Y = CoveredClosedEmbedding(X, I, check=false) #inc_Y = CoveredClosedEmbedding(X, I, covering=trivializing_covering(C), check=false) Y = domain(inc_Y) @@ -305,11 +346,8 @@ end Computes the intersection of ``W`` and ``C`` as in [Ful98](@cite) and returns an `AbsAlgebraicCycle` of codimension ``2``. - -!!! note - The `components` of ``W`` must be sheaves of prime ideals; use `irreducible_decomposition(W)` to achieve this. The check for primality can be switched off using `check=false`. """ -function intersect(W::WeilDivisor, C::CartierDivisor; check::Bool=true) +function intersect(W::AbsWeilDivisor, C::CartierDivisor; check::Bool=true) result = zero(W) for c in components(C) result = result + C[c] * intersect(W, c, check=check) @@ -333,22 +371,8 @@ function intersect(D::CartierDivisor, C::CartierDivisor) return intersect(irreducible_decomposition(weil_divisor(D)), C) end - -function pushforward(inc::CoveredClosedEmbedding, W::WeilDivisor) - X = domain(inc) - Y = codomain(inc) - X === scheme(W) || error("divisor not defined on the domain") - kk = coefficient_ring(W) - ideal_dict = IdDict{AbsIdealSheaf, elem_type(kk)}() - for I in components(W) - pfI = pushforward(inc)(I) - ideal_dict[pfI] = W[I] - end - return WeilDivisor(Y, kk, ideal_dict, check=false) -end - -dim(C::EffectiveCartierDivisor) = dim(scheme(C))-1 -dim(C::CartierDivisor) = dim(scheme(C))-1 +dim(C::EffectiveCartierDivisor) = dim(ambient_scheme(C))-1 +dim(C::CartierDivisor) = dim(ambient_scheme(C))-1 ########################################################################### ## show functions for Cartier divisors @@ -356,7 +380,7 @@ dim(C::CartierDivisor) = dim(scheme(C))-1 function Base.show(io::IO, C::EffectiveCartierDivisor) io = pretty(io) if get(io, :show_semi_compact, false) - cov = Oscar._covering_for_printing(io, scheme(C)) + cov = Oscar._covering_for_printing(io, ambient_scheme(C)) n = get(io, :label, "") _show_semi_compact(io, C, cov, n) elseif is_terse(io) @@ -365,7 +389,7 @@ function Base.show(io::IO, C::EffectiveCartierDivisor) print(io, get_attribute(C, :name)) else print(io, "Effective cartier divisor on ", Lowercase()) - show(io, scheme(C)) + show(io, ambient_scheme(C)) end end @@ -374,7 +398,7 @@ end function Base.show(io::IO, ::MIME"text/plain", C::EffectiveCartierDivisor) io = pretty(io) I = ideal_sheaf(C) - X = scheme(C) + X = ambient_scheme(C) cov = Oscar._covering_for_printing(io, X) print(io, "Effective cartier divisor") @@ -401,7 +425,7 @@ end # We usually use "a" for the domain and "b" for the codomain function _show_semi_compact(io::IO, C::EffectiveCartierDivisor, cov::Covering, n::String) io = pretty(io) - X = scheme(C) + X = ambient_scheme(C) print(io, "Effective cartier divisor") if has_attribute(C, :name) print(io, " ", get_attribute(C, :name)) @@ -415,7 +439,7 @@ end function Base.show(io::IO, C::CartierDivisor) io = pretty(io) if get(io, :show_semi_compact, false) - cov = Oscar._covering_for_printing(io, scheme(C)) + cov = Oscar._covering_for_printing(io, ambient_scheme(C)) n = get(io, :label, "") _show_semi_compact(io, C, cov, n) elseif is_terse(io) @@ -424,7 +448,7 @@ function Base.show(io::IO, C::CartierDivisor) print(io, get_attribute(C, :name)) else print(io, "Cartier divisor on ", Lowercase()) - show(io, scheme(C)) + show(io, ambient_scheme(C)) end end @@ -435,13 +459,13 @@ end # right. function Base.show(io::IO, ::MIME"text/plain", C::CartierDivisor) io = pretty(io) - X = scheme(C) + X = ambient_scheme(C) cov = Oscar._covering_for_printing(io, X) cc = components(C) if length(cc) == 0 print(io, "Zero cartier divisor ") print(io, Indent(), "on ", Lowercase()) - show(IOContext(io, :covering => cov), scheme(C)) + show(IOContext(io, :covering => cov), ambient_scheme(C)) print(io, Dedent()) else print(io, "Cartier divisor") @@ -450,7 +474,7 @@ function Base.show(io::IO, ::MIME"text/plain", C::CartierDivisor) end println(io) print(io, Indent(), "on ", Lowercase()) - show(IOContext(io, :covering => cov), scheme(C)) + show(IOContext(io, :covering => cov), ambient_scheme(C)) println(io) println(io, Dedent(), "with coefficients in ", Lowercase(), coefficient_ring(C)) print(io, "defined by the formal sum of") @@ -480,7 +504,7 @@ end # We usually use "a" for the domain and "b" for the codomain function _show_semi_compact(io::IO, C::CartierDivisor, cov::Covering, n::String) io = pretty(io) - X = scheme(C) + X = ambient_scheme(C) cc = components(C) if length(cc) == 0 print(io, "Zero cartier divisor") diff --git a/src/AlgebraicGeometry/Schemes/Divisors/Types.jl b/src/AlgebraicGeometry/Schemes/Divisors/Types.jl index 0d095596008e..45c79c23ece1 100644 --- a/src/AlgebraicGeometry/Schemes/Divisors/Types.jl +++ b/src/AlgebraicGeometry/Schemes/Divisors/Types.jl @@ -26,12 +26,14 @@ space(D) === X || error("component of divisor does not lie in the given scheme") parent(coefficients[D]) === R || error("coefficients do not lie in the given ring") end - if check + @check begin is_integral(X) || error("scheme must be integral") #is_separated(X) || error("scheme must be separated") # We need to test this somehow, but how? + d = isempty(coefficients) ? 0 : dim(first(keys(coefficients))) for D in keys(coefficients) - is_equidimensional(D) || error("components of a divisor must be sheaves of equidimensional ideals") + (is_equidimensional(D) && dim(D) == d) || error("components of a cycle must be sheaves of equidimensional ideals of the same dimension") end + true end return new{typeof(X), CoefficientRingType, CoefficientRingElemType}(X, R, coefficients) end @@ -42,11 +44,11 @@ end #abstract type AbsWeilDivisor{CoveredSchemeType, CoefficientRingType} <: AbsAlgebraicCycle{CoveredSchemeType, CoefficientRingType} end @doc raw""" - WeilDivisor + WeilDivisor <: AbsWeilDivisor -A Weil divisor on an integral separated `AbsCoveredScheme` ``X``; +A Weil divisor on an integral `AbsCoveredScheme` ``X``; stored as a formal linear combination over some ring ``R`` of -(prime) ideal sheaves on ``X``. +ideal sheaves on ``X``. """ @attributes mutable struct WeilDivisor{ CoveredSchemeType<:AbsCoveredScheme, @@ -71,7 +73,7 @@ stored as a formal linear combination over some ring ``R`` of end function WeilDivisor(C::AlgebraicCycle; check::Bool=true) - X = scheme(C) + X = ambient_scheme(C) @check begin for D in keys(coefficient_dict(C)) is_equidimensional(D) || error("components of a divisor must be sheaves of equidimensional ideals") @@ -86,7 +88,7 @@ end @doc raw""" LinearSystem -A linear system of a Weil divisor `D` on a variety `X`, +A linear system of a Weil divisor ``D`` on a variety ``X``, generated by rational functions ``f₁,…,fᵣ ∈ K(X)``. """ @attributes mutable struct LinearSystem{DivisorType<:AbsWeilDivisor} @@ -97,18 +99,26 @@ generated by rational functions ``f₁,…,fᵣ ∈ K(X)``. length(f) == 0 && return new{typeof(D)}(D, Vector{VarietyFunctionFieldElem}()) KK = parent(f[1]) all(g -> (parent(g) === KK), f[2:end]) || error("elements must have the same parent") - X = scheme(D) + X = ambient_scheme(D) X === variety(KK) || error("input not compatible") @check begin all(is_prime, components(D)) || error("components of the divisor must be prime") - all(g->in_linear_system(g, D), f) || error("element not in linear system") + all(g->is_in_linear_system(g, D), f) || error("element not in linear system") end f = Vector{VarietyFunctionFieldElem}(f) return new{typeof(D)}(D, f) end end +@doc raw""" + EffectiveCartierDivisor{CoveredSchemeType<:AbsCoveredScheme} + +An effective Cartier divisor on a scheme $X$ is a closed subscheme $D \subseteq X$ whose ideal sheaf $\mathcal{I}_D \subseteq \mathcal{O}_X$ is an invertible $\mathcal{O}_X$-module. In particular, $\mathcal{I}_D$ is locally principal. + +Internally, $C$ stores a [`trivializing_covering(C::EffectiveCartierDivisor)`](@ref). +The scheme $X$ is of type `CoveredSchemeType`. +""" @attributes mutable struct EffectiveCartierDivisor{ CoveredSchemeType<:AbsCoveredScheme } @@ -145,6 +155,15 @@ end end end +@doc raw""" + CartierDivisor{CoveredSchemeType<:AbsCoveredScheme, CoeffType<:RingElem} + +A Cartier divisor $C$ on a scheme $X$ with coefficients $a_i$ in a ring $R$ is a formal linear combination +$\sum_i a_i D_i$ of effective Cartier divisors $D_i$. + +The scheme $X$ is of type `CoveredSchemeType`. +The coefficients $a_i$ are of type `CoeffType`. +""" @attributes mutable struct CartierDivisor{ CoveredSchemeType<:AbsCoveredScheme, CoeffType<:RingElem @@ -154,7 +173,7 @@ end coeff_dict::IdDict{EffectiveCartierDivisor, CoeffType} function CartierDivisor(X::AbsCoveredScheme, R::Ring, coeff_dict::IdDict{<:EffectiveCartierDivisor, CoeffType}) where {CoeffType<:RingElem} - all(x->(scheme(x)===X), keys(coeff_dict)) || error("all effective divisors must be defined over the same scheme") + all(x->(ambient_scheme(x)===X), keys(coeff_dict)) || error("all effective divisors must be defined over the same scheme") all(x->(parent(x) === R), values(coeff_dict)) || error("all coefficients must belong to the same parent") return new{typeof(X), CoeffType}(X, R, coeff_dict) end diff --git a/src/AlgebraicGeometry/Schemes/Divisors/WeilDivisor.jl b/src/AlgebraicGeometry/Schemes/Divisors/WeilDivisor.jl index efbb297f6df2..43f804700142 100644 --- a/src/AlgebraicGeometry/Schemes/Divisors/WeilDivisor.jl +++ b/src/AlgebraicGeometry/Schemes/Divisors/WeilDivisor.jl @@ -4,11 +4,6 @@ underlying_cycle(D::AbsWeilDivisor) = underlying_cycle(underlying_divisor(D)) ### forwarding of all essential functionality underlying_cycle(D::WeilDivisor) = D.C -@attr Any function dim(I::AbsIdealSheaf) - dims = [dim(I(U)) for U in affine_charts(scheme(I))] - return maximum(dims) -end - ### type getters scheme_type(D::WeilDivisor{S, U, V}) where{S, U, V} = S scheme_type(::Type{WeilDivisor{S, U, V}}) where{S, U, V} = S @@ -29,7 +24,7 @@ function WeilDivisor(X::AbsCoveredScheme, R::Ring) end function zero(W::WeilDivisor) - return WeilDivisor(scheme(W), coefficient_ring(W)) + return WeilDivisor(ambient_scheme(W), coefficient_ring(W)) end # provide non-camelcase methods @@ -59,8 +54,8 @@ weil_divisor(X::AbsCoveredScheme, R::Ring) = WeilDivisor(X, R) @doc raw""" WeilDivisor(I::AbsIdealSheaf) -Return the `WeilDivisor` ``D = 1 ⋅ V(I)`` with coefficients -in ``ℤ`` for a sheaf of prime ideals ``I``. +Return the `WeilDivisor` ``D = 1 ⋅ I`` with coefficients +in ``ℤ`` for a sheaf of ideals ``I`` of pure codimension `1`. """ function WeilDivisor(I::AbsIdealSheaf; check::Bool=true) WeilDivisor(I, ZZ, check=check) @@ -69,17 +64,17 @@ end @doc raw""" weil_divisor(I::AbsIdealSheaf) -> WeilDivisor -Given an ideal sheaf `I`, return the prime weil divisor $D = 1 ⋅ V(I)$ with +Given an ideal sheaf `I` of pure codimension ``1``, return the weil divisor $D = 1 ⋅ I$ with coefficients in the integer ring. # Example ```jldoctest julia> P, (x, y, z) = graded_polynomial_ring(QQ, [:x, :y, :z]); -julia> I = ideal([x^3-y^2*z]); - julia> Y = proj(P); +julia> I = ideal([(x^3-y^2*z)]); + julia> II = IdealSheaf(Y, I); julia> weil_divisor(II) @@ -88,6 +83,23 @@ Effective weil divisor with coefficients in integer ring given as the formal sum of 1 * sheaf of ideals + +julia> JJ = II^2; + +julia> D = weil_divisor(JJ) +Effective weil divisor + on scheme over QQ covered with 3 patches +with coefficients in integer ring +given as the formal sum of + 1 * product of 2 ideal sheaves + +julia> irreducible_decomposition(D) +Effective weil divisor + on scheme over QQ covered with 3 patches +with coefficients in integer ring +given as the formal sum of + 2 * sheaf of prime ideals + ``` """ weil_divisor(I::AbsIdealSheaf; check::Bool=true) = WeilDivisor(I, check=check) @@ -96,10 +108,16 @@ function WeilDivisor(I::AbsIdealSheaf, R::Ring; check::Bool=true) D = WeilDivisor(space(I), R) @check is_equidimensional(I) "ideal sheaf must be equidimensional" @check dim(space(I)) - dim(I) == 1 "components of a divisor must be of codimension one" - D[I] = one(R) + setindex!(D, one(R), I; check) return D end +@doc raw""" + weil_divisor(I::AbsIdealSheaf, R::Ring; check::Bool=true) + +Given an ideal sheaf `I` of pure codimension ``1`` and a ring `R`, return the weil divisor $D = 1 ⋅ I$ with +coefficients in `R`. +""" weil_divisor(I::AbsIdealSheaf, R::Ring; check::Bool=true) = WeilDivisor(I, R, check=check) ### copy constructor @@ -108,19 +126,19 @@ function copy(D::AbsWeilDivisor) for I in keys(coefficient_dict(D)) new_dict[I] = D[I] end - return WeilDivisor(scheme(D), coefficient_ring(D), new_dict, check=false) + return WeilDivisor(ambient_scheme(D), coefficient_ring(D), new_dict, check=false) end function irreducible_decomposition(D::AbsWeilDivisor) - decomp = irreducible_decomposition(underlying_cycle(D)) - return WeilDivisor(decomp, check=false) + E = irreducible_decomposition(underlying_cycle(D)) + return WeilDivisor(E; check=false) end # If we know something about the Weil divisor, we write it! Always good to have # relevant information for free function Base.show(io::IO, D::AbsWeilDivisor) io = pretty(io) - X = scheme(D) + X = ambient_scheme(D) if get(io, :show_semi_compact, false) cov = Oscar._covering_for_printing(io, X) _show_semi_compact(io, D, cov) @@ -156,7 +174,7 @@ end # everything consistently. function _show_semi_compact(io::IO, D::AbsWeilDivisor, cov::Covering) io = pretty(io) - X = scheme(D) + X = ambient_scheme(D) C = underlying_cycle(D) eff = all(i >= 0 for i in collect(values(coefficient_dict(C)))) prim = eff && get_attribute(D, :is_prime, false) @@ -180,7 +198,7 @@ end # on the right. function Base.show(io::IO, ::MIME"text/plain", D::AbsWeilDivisor) io = pretty(io) - X = scheme(D) + X = ambient_scheme(D) cov = Oscar._covering_for_printing(io, X) C = underlying_cycle(D) eff = all(i >= 0 for i in collect(values(coefficient_dict(C)))) @@ -293,34 +311,22 @@ function ==(D::WeilDivisor, E::WeilDivisor) end @doc raw""" - intersect(D::AbsWeilDivisor, E::AbsWeilDivisor) + intersect(D::AbsWeilDivisor, E::AbsWeilDivisor; covering::Covering=default_covering(ambient_scheme(D))) -For two `WeilDivisor`s on a complete smooth surface the intersection number is defined -as in Hartshorne's "Algebraic Geometry". This computes this intersection number. +Return the intersection number of the the Weil divisors `D` and `E` +on a complete smooth surface as defined in [Har77](@cite). + +# Input +The optional keyword argument `covering` specifies the covering to be used for the computation. """ function intersect(D::AbsWeilDivisor, E::AbsWeilDivisor; - covering::Covering=default_covering(scheme(D)) + covering::Covering=default_covering(ambient_scheme(D)) ) - X = scheme(D) - @assert dim(X) == 2 "intersection of Weil divisors is only implemented for surfaces." - X === scheme(E) || error("divisors do not live on the same scheme") + X = ambient_scheme(D) + @req dim(X) == 2 "intersection of Weil divisors is only implemented for surfaces." + X === ambient_scheme(E) || error("divisors do not live on the same scheme") R = coefficient_ring(D) R === coefficient_ring(E) || error("divisors do not have the same coefficient ring") -# # prepare a copy of the divisors -# D_copy = WeilDivisor(X, R) -# E_copy = WeilDivisor(X, R) -# # check whether a common refinement of the covering is necessary -# CD = covering(D) -# CE = covering(E) -# if CD != CE -# CC, f, g = common_refinement(X, CD, CE) -# D_copy = pullback(f, D) -# E_copy = pullback(g, E) -# else -# D_copy = D -# E_copy = E -# end - # TODO: Work out the intersection result = zero(R) for c1 in components(D) a1 = D[c1] @@ -345,131 +351,21 @@ function intersect(D::AbsWeilDivisor, E::AbsWeilDivisor; return result end -@attr Any function has_dimension_leq_zero(I::Ideal) - is_one(I) && return true - return dim(I) <= 0 -end - -@attr Any function has_dimension_leq_zero(I::MPolyLocalizedIdeal) - R = base_ring(I) - P = base_ring(R)::MPolyRing - J = ideal(P, numerator.(gens(I))) - has_dimension_leq_zero(J) && return true - is_one(I) && return true - return dim(I) <= 0 -end - -@attr Any function has_dimension_leq_zero(I::MPolyQuoLocalizedIdeal) - R = base_ring(I) - P = base_ring(R)::MPolyRing - J = ideal(P, lifted_numerator.(gens(I))) - has_dimension_leq_zero(J) && return true - is_one(I) && return true - return dim(I) <= 0 -end - -function has_dimension_leq_zero(I::AbsIdealSheaf; covering::Covering=default_covering(scheme(I))) - for U in keys(object_cache(I)) - has_dimension_leq_zero(I(U)) || return false +function pushforward(inc::CoveredClosedEmbedding, W::WeilDivisor) + X = domain(inc) + Y = codomain(inc) + X === ambient_scheme(W) || error("divisor not defined on the domain") + kk = coefficient_ring(W) + ideal_dict = IdDict{AbsIdealSheaf, elem_type(kk)}() + for I in components(W) + pfI = pushforward(inc)(I) + ideal_dict[pfI] = W[I] end - - all_patches = patches(covering) - for U in all_patches - if !has_dimension_leq_zero(cheap_sub_ideal(I, U)) - has_dimension_leq_zero(I(U)) || return false - end - end - return true + return WeilDivisor(Y, kk, ideal_dict, check=false) end -function has_dimension_leq_zero(I::SumIdealSheaf; - covering::Covering=default_covering(scheme(I)), - use_decomposition_info::Bool=true - ) - J = summands(I) - -# k = findfirst(x->x isa PrimeIdealSheafFromChart, J) -# if k !== nothing -# P = J[k] -# U = original_chart(P) -# if has_decomposition_info(covering) && use_decomposition_info -# K = ideal(OO(U), OO(U).(decomposition_info(covering)[U])) -# if !has_dimension_leq_zero(cheap_sub_ideal(I, U) + K) -# has_dimension_leq_zero(I(U) + K) || return false -# end -# else -# if !has_dimension_leq_zero(cheap_sub_ideal(I, U)) -# has_dimension_leq_zero(I(U)) || return false -# end -# end -# end - - common_patches = keys(object_cache(first(J))) - for JJ in J[2:end] - common_patches = [U for U in common_patches if U in keys(object_cache(JJ))] - end - - for U in common_patches - has_dimension_leq_zero(I(U)) || return false - end - all_patches = patches(covering) - for U in all_patches - patch_ok = false - # go through the object cache of the summands - if U in keys(object_cache(I)) - i = I(U) - if has_decomposition_info(covering) && use_decomposition_info - K = ideal(OO(U), OO(U).(decomposition_info(covering)[U])) - has_dimension_leq_zero(K + i) && (patch_ok = true) - else - has_dimension_leq_zero(i) && (patch_ok = true) - end - patch_ok && continue - end - - # Do the same for the summands - for j in summands(I) - if U in keys(object_cache(j)) - j_loc = j(U) - if has_decomposition_info(covering) && use_decomposition_info - K = ideal(OO(U), OO(U).(decomposition_info(covering)[U])) - has_dimension_leq_zero(K + j_loc) && (patch_ok = true) - else - has_dimension_leq_zero(j_loc) && (patch_ok = true) - end - patch_ok && break - end - end - patch_ok && continue - - # repeat with cheap sub-ideals - for j in summands(I) - j_cheap = cheap_sub_ideal(j, U) - if has_decomposition_info(covering) && use_decomposition_info - K = ideal(OO(U), OO(U).(decomposition_info(covering)[U])) - has_dimension_leq_zero(K + j_cheap) && (patch_ok = true) - else - has_dimension_leq_zero(j_cheap) && (patch_ok = true) - end - patch_ok && break - end - patch_ok && continue - - if has_decomposition_info(covering) && use_decomposition_info - K = ideal(OO(U), OO(U).(decomposition_info(covering)[U])) - if !has_dimension_leq_zero(cheap_sub_ideal(I, U) + K) - has_dimension_leq_zero(I(U) + K) || return false - end - else - if !has_dimension_leq_zero(cheap_sub_ideal(I, U)) - has_dimension_leq_zero(I(U)) || return false - end - end - end - return true -end """ _self_intersection(I::AbsIdealSheaf) -> Integer @@ -523,93 +419,16 @@ function _is_known_to_be_one(I::SumIdealSheaf, U::AbsAffineScheme; end -function colength(I::AbsIdealSheaf; covering::Covering=default_covering(scheme(I))) - X = scheme(I) - all_patches = copy(patches(covering)) - patches_done = AbsAffineScheme[] - patches_todo = AbsAffineScheme[] - @vprintln :Divisors 2 "checking colength of $(I)" - for U in all_patches - dec_inf = (has_decomposition_info(covering) ? elem_type(OO(U))[OO(U)(g) for g in decomposition_info(covering)[U]] : elem_type(OO(U))[]) - if _is_known_to_be_one(I, U; dec_inf) - push!(patches_done, U) - else - push!(patches_todo, U) - end - end - - result = 0 - while length(patches_todo) != 0 - U = pop!(patches_todo) - - # First do a cheaper test whether this chart needs to be looked at - J_cheap = cheap_sub_ideal(I, U) - if has_decomposition_info(covering) - h = decomposition_info(covering)[U] - if isone(J_cheap + ideal(OO(U), elem_type(OO(U))[OO(U)(a) for a in h])) # R(a) is not type stable for R::MPolyQuoRing - # push!(patches_done, U) - continue - end - end - - J = I(U) - if has_decomposition_info(covering) - h = decomposition_info(covering)[U] - # The elements in h indicate where components must - # be located so that they can not be spotted in other charts. - # We iteratively single out these components by adding a sufficiently high - # power of the equation to the ideal. - for f in h - g = f - while !(g in ideal(OO(U), g*f) + J) - g = g * g - end - J = J + ideal(OO(U), g) - if isone(J) - push!(patches_done, U) - continue - end - end - else - # To avoid overcounting, throw away all components that - # were already visible in other charts. - for V in patches_done - if !haskey(gluings(covering), (U, V)) - continue - end - G = covering[U, V] - (UV, VU) = gluing_domains(G) - UV isa PrincipalOpenSubset || error("method is only implemented for simple gluings") - f = complement_equation(UV) - # Find a sufficiently high power of f such that it throws - # away all components away from the horizon, but does not affect - # those on the horizon itself. - g = f - while !(g in ideal(OO(U), g*f) + J) - g = g * g - end - J = J + ideal(OO(U), g) - isone(J) && break - end - end - if !isone(J) - JJ = leading_ideal(saturated_ideal(J)) - A, _ = quo(base_ring(JJ), JJ) - result = result + ngens(vector_space(coefficient_ring(base_ring(A)), A)[1]) - end - push!(patches_done, U) - end - return result -end - - @doc raw""" - in_linear_system(f::VarietyFunctionFieldElem, D::WeilDivisor; regular_on_complement::Bool=true) -> Bool + is_in_linear_system(f::VarietyFunctionFieldElem, D::WeilDivisor; regular_on_complement::Bool=true, check::Bool=true) -> Bool -Check if the rational function `f` is in the linear system ``|D|``. +Return whether the rational function `f` is in the linear system ``|D|``, i.e. if $(f) + D \geq 0$. + +# Input +- `regular_on_complement` -- set to `true` if `f` is regular on the complement of the support of `D`. """ -function in_linear_system(f::VarietyFunctionFieldElem, D::AbsWeilDivisor; regular_on_complement::Bool=false, check::Bool=true) - X = scheme(D) +function is_in_linear_system(f::VarietyFunctionFieldElem, D::AbsWeilDivisor; regular_on_complement::Bool=false, check::Bool=true) + X = ambient_scheme(D) X === variety(parent(f)) || error("schemes not compatible") C = simplified_covering(X) for I in components(D) @@ -675,21 +494,22 @@ linear_system(f::Vector, D::AbsWeilDivisor; check::Bool=true) = LinearSystem(f, @doc raw""" weil_divisor(L::LinearSystem) -Return the divisor `D` of the linear system `L = |D|`. +Return the divisor $D$ of the linear system $L = |D|$. """ function weil_divisor(L::LinearSystem) return L.D end + gens(L::LinearSystem) = L.f number_of_generators(L::LinearSystem) = length(L.f) -gen(L::LinearSystem,i::Int) = L.f[i] +gen(L::LinearSystem, i::Int) = L.f[i] @doc raw""" variety(L::LinearSystem) Return the variety on which `L` is defined. """ -variety(L::LinearSystem) = scheme(weil_divisor(L)) +variety(L::LinearSystem) = ambient_scheme(weil_divisor(L)) # an alias for the user's convenience scheme(L::LinearSystem) = variety(L) @@ -795,26 +615,12 @@ function subsystem(L::LinearSystem, P::AbsWeilDivisor, n::Int; check::Bool=true) return subsystem(L, I, n) end -# Prime Weil divisor are those written as 1*Sheaf of prime ideals -@attr Bool function is_prime(D::AbsWeilDivisor) - length(components(D)) == 0 && return false # Cannot be prime if there are no components - # Two cases: - # - D is a sum of at least 2 disinct sheaf of prime ideals -> not prime - # - D is not, we then compute an irreducible decomposition as sum of distinct - # sheaf of prime ideals, with some coefficients, and we check whether this - # irreducible decomposition is prime - if length(components(D))>1 - all(is_prime, components(D)) && return false - return is_prime(irreducible_decomposition(D)) - end - # If D = a*C, then D is prime if and only if C is prime and a == 1 - C = components(D)[1] - !is_prime(C) && return false - return coefficient_dict(D)[C] == 1 -end -is_irreducible(D::AbsWeilDivisor) = is_prime(D) +@doc raw""" + order_of_vanishing(f::VarietyFunctionFieldElem, D::AbsWeilDivisor; check::Bool=true) +Return the order of vanishing of the rational function `f` on the prime divisor `D`. +""" function order_of_vanishing( f::VarietyFunctionFieldElem, D::AbsWeilDivisor; diff --git a/src/AlgebraicGeometry/Schemes/ProjectiveVariety/Objects/Constructors.jl b/src/AlgebraicGeometry/Schemes/ProjectiveVariety/Objects/Constructors.jl index 5c9e8eb59b84..81dbfb9603e1 100644 --- a/src/AlgebraicGeometry/Schemes/ProjectiveVariety/Objects/Constructors.jl +++ b/src/AlgebraicGeometry/Schemes/ProjectiveVariety/Objects/Constructors.jl @@ -75,7 +75,7 @@ Return the projective variety defined by the homogeneous polynomial `f`. This checks that `f` is absolutely irreducible. """ function variety(f::MPolyDecRingElem; check::Bool=true, is_radical::Bool=false) - if check + @check begin is_irreducible(f) || error("polynomial is reducible") ff = factor_absolute(forget_decoration(f))[2] # deal with weird type instability in factor_absolute @@ -83,6 +83,7 @@ function variety(f::MPolyDecRingElem; check::Bool=true, is_radical::Bool=false) @assert ff[2] == 1 g = ff[1] (length(g) == 1 || (length(g)==2 && isone(g[2]))) || error("polynomial is not absolutely irreducible") + true end return variety(ideal([f]), check=false, is_radical=is_radical) end diff --git a/src/AlgebraicGeometry/Schemes/ProjectiveVariety/Objects/Types.jl b/src/AlgebraicGeometry/Schemes/ProjectiveVariety/Objects/Types.jl index 9c01e8d8d78f..45d1fbc16db0 100644 --- a/src/AlgebraicGeometry/Schemes/ProjectiveVariety/Objects/Types.jl +++ b/src/AlgebraicGeometry/Schemes/ProjectiveVariety/Objects/Types.jl @@ -2,29 +2,27 @@ X::ProjectiveAlgebraicSet{BaseRing,GradedRingType} function ProjectiveVariety(X::ProjectiveScheme{S, T}; check::Bool=true) where {S, T} - if check + @check begin S <:QQField || error("varieties must be geometrically integral, but we test this only over QQ at the moment; disable this check if you know the variety is geometrically integral or proceed at your own risk") is_geometrically_integral(X) || error("varieties must be geometrically integral") - else - set_attribute!(X, :is_geometrically_integral => true) - set_attribute!(X, :is_integral => true) - set_attribute!(X, :is_geometrically_reduced => true) - set_attribute!(X, :is_reduced => true) end + set_attribute!(X, :is_geometrically_integral => true) + set_attribute!(X, :is_integral => true) + set_attribute!(X, :is_geometrically_reduced => true) + set_attribute!(X, :is_reduced => true) Y = ProjectiveAlgebraicSet(X, check=false) new{S, T}(Y) end function ProjectiveVariety(X::ProjectiveAlgebraicSet{S, T}; check::Bool=true) where {S, T} - if check + @check begin S <:QQField || error("varieties must be geometrically integral, but we test this only over QQ at the moment; disable this check if you know the variety is geometrically integral or proceed at your own risk") is_geometrically_integral(X) || error("varieties must be geometrically integral") - else - set_attribute!(X, :is_geometrically_integral => true) - set_attribute!(X, :is_integral => true) - set_attribute!(X, :is_geometrically_reduced => true) - set_attribute!(X, :is_reduced => true) end + set_attribute!(X, :is_geometrically_integral => true) + set_attribute!(X, :is_integral => true) + set_attribute!(X, :is_geometrically_reduced => true) + set_attribute!(X, :is_reduced => true) new{S, T}(X) end end diff --git a/src/AlgebraicGeometry/Schemes/Sheaves/CoherentSheaves.jl b/src/AlgebraicGeometry/Schemes/Sheaves/CoherentSheaves.jl index 68c91cdbcb82..ba41a41b91b6 100644 --- a/src/AlgebraicGeometry/Schemes/Sheaves/CoherentSheaves.jl +++ b/src/AlgebraicGeometry/Schemes/Sheaves/CoherentSheaves.jl @@ -224,11 +224,12 @@ identifications given by the gluings in the `default_covering`. #is_open_func=_is_open_for_modules(X) ) M = new{typeof(X), AbsAffineScheme, ModuleFP, Map}(MD, MG, OOX, Mpre, default_cov) - if check + @check begin # Check that all sheaves of modules are compatible on the overlaps. # TODO: eventually replace by a check that on every basic # affine patch, the ideal sheaf can be inferred from what is # given on one dense open subset. + true end return M end diff --git a/src/AlgebraicGeometry/Schemes/Sheaves/IdealSheaves.jl b/src/AlgebraicGeometry/Schemes/Sheaves/IdealSheaves.jl index d417fb17d116..b3a347593522 100644 --- a/src/AlgebraicGeometry/Schemes/Sheaves/IdealSheaves.jl +++ b/src/AlgebraicGeometry/Schemes/Sheaves/IdealSheaves.jl @@ -339,6 +339,121 @@ function subscheme(I::AbsIdealSheaf; covering::Covering=default_covering(scheme( end +@attr Int function dim(I::AbsIdealSheaf) + dims = [dim(I(U)) for U in affine_charts(scheme(I))] + return maximum(dims) +end + +@attr Bool function has_dimension_leq_zero(I::Ideal) + is_one(I) && return true + return dim(I) <= 0 +end + +@attr Bool function has_dimension_leq_zero(I::MPolyLocalizedIdeal) + R = base_ring(I) + P = base_ring(R)::MPolyRing + J = ideal(P, numerator.(gens(I))) + has_dimension_leq_zero(J) && return true + is_one(I) && return true + return dim(I) <= 0 +end + +@attr Bool function has_dimension_leq_zero(I::MPolyQuoLocalizedIdeal) + R = base_ring(I) + P = base_ring(R)::MPolyRing + J = ideal(P, lifted_numerator.(gens(I))) + has_dimension_leq_zero(J) && return true + is_one(I) && return true + return dim(I) <= 0 +end + + +function has_dimension_leq_zero(I::AbsIdealSheaf; covering::Covering=default_covering(scheme(I))) + for U in keys(object_cache(I)) + has_dimension_leq_zero(I(U)) || return false + end + + all_patches = patches(covering) + for U in all_patches + if !has_dimension_leq_zero(cheap_sub_ideal(I, U)) + has_dimension_leq_zero(I(U)) || return false + end + end + return true +end + +function has_dimension_leq_zero(I::SumIdealSheaf; + covering::Covering=default_covering(scheme(I)), + use_decomposition_info::Bool=true + ) + J = summands(I) + + common_patches = keys(object_cache(first(J))) + for JJ in J[2:end] + common_patches = [U for U in common_patches if U in keys(object_cache(JJ))] + end + + for U in common_patches + has_dimension_leq_zero(I(U)) || return false + end + + all_patches = patches(covering) + for U in all_patches + patch_ok = false + # go through the object cache of the summands + if U in keys(object_cache(I)) + i = I(U) + if has_decomposition_info(covering) && use_decomposition_info + K = ideal(OO(U), OO(U).(decomposition_info(covering)[U])) + has_dimension_leq_zero(K + i) && (patch_ok = true) + else + has_dimension_leq_zero(i) && (patch_ok = true) + end + patch_ok && continue + end + + # Do the same for the summands + for j in summands(I) + if U in keys(object_cache(j)) + j_loc = j(U) + if has_decomposition_info(covering) && use_decomposition_info + K = ideal(OO(U), OO(U).(decomposition_info(covering)[U])) + has_dimension_leq_zero(K + j_loc) && (patch_ok = true) + else + has_dimension_leq_zero(j_loc) && (patch_ok = true) + end + patch_ok && break + end + end + patch_ok && continue + + # repeat with cheap sub-ideals + for j in summands(I) + j_cheap = cheap_sub_ideal(j, U) + if has_decomposition_info(covering) && use_decomposition_info + K = ideal(OO(U), OO(U).(decomposition_info(covering)[U])) + has_dimension_leq_zero(K + j_cheap) && (patch_ok = true) + else + has_dimension_leq_zero(j_cheap) && (patch_ok = true) + end + patch_ok && break + end + patch_ok && continue + + if has_decomposition_info(covering) && use_decomposition_info + K = ideal(OO(U), OO(U).(decomposition_info(covering)[U])) + if !has_dimension_leq_zero(cheap_sub_ideal(I, U) + K) + has_dimension_leq_zero(I(U) + K) || return false + end + else + if !has_dimension_leq_zero(cheap_sub_ideal(I, U)) + has_dimension_leq_zero(I(U)) || return false + end + end + end + return true +end + @doc raw""" extend!(C::Covering, D::Dict{AffineSchemeType, IdealType}) where {AffineSchemeType<:AffineScheme, IdealType<:Ideal} @@ -703,7 +818,7 @@ Return the order of the rational function `f` on the prime divisor `D`. function order_of_vanishing(f::VarietyFunctionFieldElem, D::WeilDivisor; check::Bool=true) @check is_prime(D) "divisor must be prime for the order of vanishing to be defined" P = components(D)[1] - return order_of_vanishing(f, P) + return order_of_vanishing(f, P; check=false) end @doc raw""" @@ -721,14 +836,11 @@ function order_of_vanishing( X = space(I)::AbsCoveredScheme X == variety(parent(f)) || error("schemes not compatible") - #order_dict = Dict{AbsAffineScheme, Int}() - # Since X is integral and I is a sheaf of prime ideals, # it suffices to find one chart in which I is non-trivial. # We look for the chart with the least complexity V = first(affine_charts(X)) - #complexity = Vector{Tuple{AbsAffineScheme, Int}}() complexity = inf for U in keys(Oscar.object_cache(underlying_presheaf(I))) # Those charts on which I is known. U in default_covering(X) || continue @@ -761,27 +873,6 @@ function order_of_vanishing( num_mult = _minimal_power_such_that(J, x->(issubset(quotient(x+K, aR), J)))[1]-1 den_mult = _minimal_power_such_that(J, x->(issubset(quotient(x+K, bR), J)))[1]-1 return num_mult - den_mult -# # Deprecated code computing symbolic powers explicitly: -# L, map = localization(OO(U), -# MPolyComplementOfPrimeIdeal(saturated_ideal(I(U))) -# ) -# L isa Union{MPolyLocRing{<:Any, <:Any, <:Any, <:Any, -# <:MPolyComplementOfPrimeIdeal}, -# MPolyQuoLocRing{<:Any, <:Any, <:Any, <:Any, -# <:MPolyComplementOfPrimeIdeal} -# } || error("localization was not successful") -# -# floc = f[U] -# a = numerator(floc) -# b = denominator(floc) -# # TODO: cache groebner bases in a reasonable way. -# P = L(prime_ideal(inverted_set(L))) -# if one(L) in P -# continue # the multiplicity is -∞ in this case and does not count -# end -# upper = _minimal_power_such_that(P, x->!(L(a) in x))[1]-1 -# lower = _minimal_power_such_that(P, x->!(L(b) in x))[1]-1 -# order_dict[U] = upper-lower end @doc raw""" @@ -940,6 +1031,29 @@ function maximal_associated_points( return comps end +@doc raw""" + minimal_primes(I::AbsIdealSheaf; kwargs...) + +Return the minimal prime ideal sheaves of ``I``. + +These are the ideal sheaves of the maximal associated points of ``I``. + +See [`maximal_associated_points`](@ref) for possible keyword arguments. +""" +minimal_primes(I::AbsIdealSheaf; kwargs...) = maximal_associated_points(I; kwargs...) + +@doc raw""" + associated_primes(I::AbsIdealSheaf; kwargs...) + +Return the prime ideal sheaves associated to``I``. + +These are the ideal sheaves of the associated points of ``I``. + +See [`associated_points`](@ref) for possible keyword arguments. +""" +associated_primes(I::AbsIdealSheaf; kwargs...) = associated_points(I; kwargs...) + + @doc raw""" associated_points(I::AbsIdealSheaf) @@ -1192,9 +1306,7 @@ function Base.show(io::IO, ::MIME"text/plain", I::SumIdealSheaf) io = pretty(io) print(io, "Sum of \n") print(io, Indent()) - for J in summands(I) - print(io, J, "\n") - end + join(io, summands(I), "\n") print(io, Dedent()) end @@ -1202,30 +1314,18 @@ function Base.show(io::IO, ::MIME"text/plain", I::ProductIdealSheaf) io = pretty(io) print(io, "Product of \n") print(io, Indent()) - for J in factors(I) - print(io, J, "\n") - end + join(io, factors(I), "\n") print(io, Dedent()) end function Base.show(io::IO, I::SumIdealSheaf) io = pretty(io) - print(io, "Sum of \n") - print(io, Indent()) - for J in summands(I) - print(io, J, "\n") - end - print(io, Dedent()) + print(io, "Sum of $(length(summands(I))) ideal sheaves") end function Base.show(io::IO, I::ProductIdealSheaf) io = pretty(io) - print(io, "Product of \n") - print(io, Indent()) - for J in factors(I) - print(io, J, "\n") - end - print(io, Dedent()) + print(io, "Product of $(length(factors(I))) ideal sheaves") end function Base.show(io::IO, I::PrimeIdealSheafFromChart) @@ -2140,3 +2240,88 @@ is_one(II::SingularLocusIdealSheaf) = all(is_one(produce_non_radical_ideal_of_si in_radical(J::AbsIdealSheaf, II::SingularLocusIdealSheaf) = all(all(radical_membership(g, produce_non_radical_ideal_of_singular_locus(II, U)) for g in gens(J(U))) for U in affine_charts(scheme(J))) +@doc raw""" + colength(I::AbsIdealSheaf; covering::Covering=default_covering(scheme(I))) + +Return the colength of `I`. + + +""" +function colength(I::AbsIdealSheaf; covering::Covering=default_covering(scheme(I))) + X = scheme(I) + all_patches = copy(patches(covering)) + patches_done = AbsAffineScheme[] + patches_todo = AbsAffineScheme[] + @vprintln :Divisors 2 "checking colength of $(I)" + for U in all_patches + dec_inf = (has_decomposition_info(covering) ? elem_type(OO(U))[OO(U)(g) for g in decomposition_info(covering)[U]] : elem_type(OO(U))[]) + if _is_known_to_be_one(I, U; dec_inf) + push!(patches_done, U) + else + push!(patches_todo, U) + end + end + + result = 0 + while length(patches_todo) != 0 + U = pop!(patches_todo) + + # First do a cheaper test whether this chart needs to be looked at + J_cheap = cheap_sub_ideal(I, U) + if has_decomposition_info(covering) + h = decomposition_info(covering)[U] + if isone(J_cheap + ideal(OO(U), elem_type(OO(U))[OO(U)(a) for a in h])) # R(a) is not type stable for R::MPolyQuoRing + # push!(patches_done, U) + continue + end + end + + J = I(U) + if has_decomposition_info(covering) + h = decomposition_info(covering)[U] + # The elements in h indicate where components must + # be located so that they can not be spotted in other charts. + # We iteratively single out these components by adding a sufficiently high + # power of the equation to the ideal. + for f in h + g = f + while !(g in ideal(OO(U), g*f) + J) + g = g * g + end + J = J + ideal(OO(U), g) + if isone(J) + push!(patches_done, U) + continue + end + end + else + # To avoid overcounting, throw away all components that + # were already visible in other charts. + for V in patches_done + if !haskey(gluings(covering), (U, V)) + continue + end + G = covering[U, V] + (UV, VU) = gluing_domains(G) + UV isa PrincipalOpenSubset || error("method is only implemented for simple gluings") + f = complement_equation(UV) + # Find a sufficiently high power of f such that it throws + # away all components away from the horizon, but does not affect + # those on the horizon itself. + g = f + while !(g in ideal(OO(U), g*f) + J) + g = g * g + end + J = J + ideal(OO(U), g) + isone(J) && break + end + end + if !isone(J) + JJ = leading_ideal(saturated_ideal(J)) + A, _ = quo(base_ring(JJ), JJ) + result = result + ngens(vector_space(coefficient_ring(base_ring(A)), A)[1]) + end + push!(patches_done, U) + end + return result +end diff --git a/src/deprecations.jl b/src/deprecations.jl index fb2d27a26d9a..e48a6cc42ecd 100644 --- a/src/deprecations.jl +++ b/src/deprecations.jl @@ -139,3 +139,7 @@ Base.@deprecate_binding QQAbElem QQAbFieldElem Base.@deprecate_binding FreeAssAlgIdeal FreeAssociativeAlgebraIdeal +Base.@deprecate_binding in_linear_system is_in_linear_system +@deprecate scheme(W::AbsAlgebraicCycle) ambient_scheme(W) +@deprecate scheme(W::CartierDivisor) ambient_scheme(W) +@deprecate scheme(W::EffectiveCartierDivisor) ambient_scheme(W) diff --git a/src/exports.jl b/src/exports.jl index ef3a9a07e670..fab42ad7a681 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -276,6 +276,8 @@ export as_dictionary export as_gset export ascending_compositions export associahedron +export associated_primes +export associated_points export atlas_description export atlas_group export atlas_irrationality @@ -711,7 +713,7 @@ export image_in_Oq export images export img_gens export immaculate_line_bundles -export in_linear_system +export is_in_linear_system export incidence_matrix export inclusion_morphism export independent_sets @@ -756,6 +758,7 @@ export invert_birational_map export inverted_set export invlex export irreducible_components +export irreducible_decomposition export irreducible_secondary_invariants export irreducibles export irrelevant_ideal @@ -1033,6 +1036,7 @@ export matroid_hex export max_GC_rank_polytope export maxes export maximal_abelian_quotient, has_maximal_abelian_quotient, set_maximal_abelian_quotient +export maximal_associated_points export maximal_blocks export maximal_cells export maximal_cones diff --git a/src/forward_declarations.jl b/src/forward_declarations.jl index 52cb07156df9..afaa3e4942c7 100644 --- a/src/forward_declarations.jl +++ b/src/forward_declarations.jl @@ -20,22 +20,104 @@ abstract type AbsAffineScheme{BaseRingType, RingType<:Ring} <: Scheme{BaseRingTy @doc raw""" AbsCoveredScheme{BaseRingType} -An abstract scheme ``X`` over some `base_ring` ``𝕜`` of type +A scheme ``X`` over some `base_ring` ``𝕜`` of type `BaseRingType`, given by means of affine charts and their gluings. """ abstract type AbsCoveredScheme{BaseRingType} <: Scheme{BaseRingType} end -### Algebraic cycles and divisors -# CAUTION: This has been moved here from experimental!!! +@doc raw""" + AbsAlgebraicCycle{CoveredSchemeType<:AbsCoveredScheme, CoefficientRingType<:Ring} + +An algebraic cycle $D$ on a (locally) Noetherian integral scheme $X$ with coefficients in a ring $R$ +is a formal linear combination $\sum_i a_i D_i$ with +$D_i \subseteq X$ integral, closed subschemes and the $a_i \in R$. + +Such a cycle is represented non-uniquely as a formal sum $E = \sum_l b_l \mathcal{I}_l$ +of equidimensional ideal sheaves $\mathcal{I}_l \subseteq \mathcal{O}_X$. +For an equidimensional ideal sheaf $\mathcal{I}$ its interpretation as a cycle is as follows: +Let $V(\mathcal{I})=E_{1} \cup \dots \cup E_{n}$ be the decomposition of the vanishing locus +of $\mathcal{I}$ into irreducible components $E_i=V(\mathcal{P}_i)$ with $\mathcal{P}_i$ prime. +Then $E$ corresponds to the cycle $D = \sum_{i=1}^{n} \mathrm{colength}_{\mathcal{P}_i}(\mathcal{I})E_i$. + +# Examples +```jldoctest +julia> P2 = projective_space(QQ,2); (s0,s1,s2) = homogeneous_coordinates(P2); + +julia> I = ideal_sheaf(P2,ideal([s0,s1^2])) +Sheaf of ideals + on scheme over QQ covered with 3 patches + 1: [(s1//s0), (s2//s0)] affine 2-space + 2: [(s0//s1), (s2//s1)] affine 2-space + 3: [(s0//s2), (s1//s2)] affine 2-space +with restrictions + 1: Ideal (1, (s1//s0)^2) + 2: Ideal ((s0//s1), 1) + 3: Ideal ((s0//s2), (s1//s2)^2) + +julia> D = algebraic_cycle(I) +Effective algebraic cycle + on scheme over QQ covered with 3 patches +with coefficients in integer ring +given as the formal sum of + 1 * sheaf of ideals + +julia> irreducible_decomposition(D) +Effective algebraic cycle + on scheme over QQ covered with 3 patches +with coefficients in integer ring +given as the formal sum of + 2 * sheaf of prime ideals + +``` +""" abstract type AbsAlgebraicCycle{ CoveredSchemeType<:AbsCoveredScheme, CoefficientRingType<:AbstractAlgebra.Ring } end -abstract type AbsWeilDivisor{CoveredSchemeType, CoefficientRingType} <: AbsAlgebraicCycle{CoveredSchemeType, CoefficientRingType} end +@doc raw""" + AbsWeilDivisor{CoveredSchemeType, CoefficientRingType} <: AbsAlgebraicCycle{CoveredSchemeType, CoefficientRingType} + +A Weil divisor with coefficients of type `CoefficientRingType` on a (locally) Noetherian integral scheme ``X`` of type `CoveredSchemeType`. -### END Algebraic cycles and divisors +# Examples +``` +julia> P2 = projective_space(QQ,2); (s0,s1,s2) = homogeneous_coordinates(P2); + +julia> I = ideal((s0*s1)^2); + +julia> II = ideal_sheaf(P2, I); + +julia> D = weil_divisor(II) +Effective weil divisor + on scheme over QQ covered with 3 patches +with coefficients in integer ring +given as the formal sum of + 1 * sheaf of ideals + +julia> E = irreducible_decomposition(D) +Effective weil divisor + on scheme over QQ covered with 3 patches +with coefficients in integer ring +given as the formal sum of + 2 * prime ideal sheaf on scheme over QQ covered with 3 patches extended from ideal ((s1//s0)) on affine 2-space + 2 * prime ideal sheaf on scheme over QQ covered with 3 patches extended from ideal ((s0//s1)) on affine 2-space + +julia> P = components(E)[1] +Prime ideal sheaf on Scheme over QQ covered with 3 patches extended from Ideal ((s1//s0)) on Affine 2-space + +julia> components(D)[1] == II +true + +julia> D[II] # to get the coefficient +1 + +julia> E[P] +2 +``` +""" +abstract type AbsWeilDivisor{CoveredSchemeType, CoefficientRingType} <: AbsAlgebraicCycle{CoveredSchemeType, CoefficientRingType} end ########################################### # (2) Toric Varieties diff --git a/test/AlgebraicGeometry/Schemes/CartierDivisor.jl b/test/AlgebraicGeometry/Schemes/CartierDivisor.jl index ff96a5f79d8c..ce3ea584db59 100644 --- a/test/AlgebraicGeometry/Schemes/CartierDivisor.jl +++ b/test/AlgebraicGeometry/Schemes/CartierDivisor.jl @@ -51,11 +51,15 @@ end X = covered_scheme(IPX) h = (x+y+z+w)^3 u = (x-y+4*w) - C = Oscar.cartier_divisor(IPX, h) - D = Oscar.cartier_divisor(IPX, u) + C = cartier_divisor(IPX, h) + D = cartier_divisor(IPX, u) @test 2*C == C+C @test iszero(D-D) @test 3*(C + D) == 3*C + 3*D + Z = Oscar.cartier_divisor(IPX, S(1)) + @test iszero(Z) + C2 = cartier_divisor(IPX, h^2) + @test iszero(2*C - C2) end @testset "conversion of Cartier to Weil divisors" begin diff --git a/test/AlgebraicGeometry/Schemes/WeilDivisor.jl b/test/AlgebraicGeometry/Schemes/WeilDivisor.jl index 6d45c7796361..268f8fd893cb 100644 --- a/test/AlgebraicGeometry/Schemes/WeilDivisor.jl +++ b/test/AlgebraicGeometry/Schemes/WeilDivisor.jl @@ -123,11 +123,10 @@ end KK = function_field(X) R = ambient_coordinate_ring(representative_patch(KK)) x = gens(R) - @test in_linear_system(KK(x[1]), D) - @test !in_linear_system(KK(x[1]^2), D) - @test in_linear_system(KK(x[1]^2), 2*D) - # Not running at the moment; work in progress - #@test !in_linear_system(KK(x[1], x[2]), D) + @test is_in_linear_system(KK(x[1]), D) + @test !is_in_linear_system(KK(x[1]^2), D) + @test is_in_linear_system(KK(x[1]^2), 2*D) + @test !is_in_linear_system(KK(x[1], x[2]), D) L = LinearSystem(KK.([1, x[1], x[2], x[1]^2, x[1]*x[2], x[2]^2]), 2*D) H = S[1]+S[2]+S[3] @@ -160,7 +159,7 @@ end U = X[1][2] u, v = gens(OO(U)) f = KK(u, v) - @test !in_linear_system(f, D) + @test !is_in_linear_system(f, D) end @testset "intersections of weil divisors on surfaces" begin