diff --git a/experimental/Schemes/Auxiliary.jl b/experimental/Schemes/Auxiliary.jl index c1f12a25bf85..7232877de0e9 100644 --- a/experimental/Schemes/Auxiliary.jl +++ b/experimental/Schemes/Auxiliary.jl @@ -372,3 +372,6 @@ function inherit_glueings!(ref::Covering, orig::Covering) return ref end +### Generic pullback and pushforward for composite maps +pushforward(f::Generic.CompositeMap, a::Any) = pushforward(map2(f), pushforward(map1(f), a)) +pullback(f::Generic.CompositeMap, a::Any) = pullback(map1(f), pullback(map2(f), a)) diff --git a/experimental/Schemes/BlowupMorphism.jl b/experimental/Schemes/BlowupMorphism.jl index d16069208ce2..44a2d2e50c46 100644 --- a/experimental/Schemes/BlowupMorphism.jl +++ b/experimental/Schemes/BlowupMorphism.jl @@ -73,8 +73,15 @@ with restriction ``` """ @attributes mutable struct BlowupMorphism{ - CodomainType<:AbsCoveredScheme - } # TODO: Derive this from AbsCoveredSchemeMorphism ? + DomainType<:AbsCoveredScheme, # Not a concrete type in general because this is lazy + CodomainType<:AbsCoveredScheme, + BaseMorphismType # Nothing in case of no base change + } <: AbsCoveredSchemeMorphism{ + DomainType, + CodomainType, + BaseMorphismType, + BlowupMorphism + } projective_bundle::CoveredProjectiveScheme codomain::CodomainType # in general a CoveredScheme center::IdealSheaf # on codomain @@ -88,10 +95,13 @@ with restriction ) X = base_scheme(IP) X === scheme(I) || error("ideal sheaf not compatible with blown up variety") - return new{typeof(X)}(IP, X, I) + return new{AbsCoveredScheme, typeof(X), Nothing}(IP, X, I) end end +### Forward the essential functionality +underlying_morphism(phi::BlowupMorphism) = projection(phi) + function domain(p::BlowupMorphism) if !isdefined(p, :domain) p.domain = covered_scheme(p.projective_bundle) @@ -151,10 +161,69 @@ function strict_transform(p::BlowupMorphism, inc::CoveredClosedEmbedding) inc_Z_trans = CoveredClosedEmbedding(Y, I_trans, covering=simplified_covering(Y), # Has been set by the previous call check=false) - inc_cov = covering_morphism(inc_Z_trans) + inc_dom_cov = covering_morphism(inc_Z_trans) + inc_cod_cov = covering_morphism(inc) Z_trans = domain(inc_Z_trans) pr_res = restrict(projection(p), inc_Z_trans, inc) + + if has_attribute(p, :isomorphism_on_open_subset) + OOX = OO(X) + OOY = OO(Y) + p_iso = isomorphism_on_open_subset(p) + U = domain(p_iso)::PrincipalOpenSubset + V = codomain(p_iso)::PrincipalOpenSubset + V_amb = ambient_scheme(V) + U_amb = ambient_scheme(U) + + # We have the following diagram: + # inc_dom + # Z_trans ↪ Y ⊃ U_amb ⊃ U + # + # pr_res↓ ↓ p ↓ p_iso + # Z ↪ X ⊃ V_amb ⊃ V + # inc_cod + # + # Given all the refinements that potentially had to be done, we have + # to do the following. + # 1. Find a `patch` `U_sub` of the `domain` of `inc_dom_cov` for which + # inc_dom : U_sub -> W has a codomain `W` which has `U_amb` as an + # ancestor. Since `U_amb` is one of the `affine_charts` of `Y`, this will work. + + k = findfirst(x->has_ancestor(y->y===U_amb, codomain(inc_dom_cov[x])), patches(domain(inc_dom_cov))) + U_sub = patches(domain(inc_dom_cov))[k] + + # 2. pr_res : U_amb -> V_amb has some codomain such that there exists + # an ancestor `VV` in the `domain` of `inc_dom_cov` such that + # inc_dom : VV -> W' has a codomain with `V_amb` as an ancestor. + # 3. outside the `complement_equation`s of `U` and `V` the projection + # was an isomorphism. Then the restriction of `pr_res` to those + # complements in `U_sub` and `V_sub` also is. + U_sub_res = PrincipalOpenSubset(U_sub, + pullback(inc_dom_cov[U_sub])( + OOX(U_amb, codomain(inc_dom_cov[U_sub]))( + complement_equation(U) + ) + ) + ) + + pr_res_cov = covering_morphism(pr_res) + pr_sub = pr_res_cov[U_sub] + + V_sub = codomain(pr_sub) + @assert has_ancestor(x->any(y->y===x, patches(domain(inc_cod_cov))), V_sub) + V_sub_inc, comp_eqns = _find_chart(V_sub, domain(inc_cod_cov)) + OOZ = OO(Z) + dummy_dom = codomain(V_sub_inc) + dummy_map = inc_cod_cov[dummy_dom] + dummy_cod = codomain(dummy_map) + V_sub_res = PrincipalOpenSubset(V_sub, + OOZ(dummy_dom, V_sub)(pullback(dummy_map)(OOY(V_amb, dummy_cod)(complement_equation(V))))) + result = restrict(pr_sub, U_sub_res, V_sub_res) + # TODO: Obtain the inverse another way? + @assert is_isomorphism(result) + set_attribute!(pr_res, :isomorphism_on_open_subset, result) + end return Z_trans, inc_Z_trans, pr_res end @@ -498,13 +567,17 @@ end return p_res end +### Some functionality used by function fields + +# If set this attribute shall return some isomorphism on some open subsets of the domain +# and codomain of phi. If both are irreducible, this automatically implies that both are +# dense, but we do not check for this. @attr AbsSpecMor function isomorphism_on_open_subset(f::BlowupMorphism) - pr = isomorphism_on_complement_of_center(f) - X = domain(pr) - Y = codomain(pr) - pr_cov = covering_morphism(pr) - U = first(patches(domain(pr_cov))) - pr_res = pr_cov[U] + X = domain(f) + Y = codomain(f) + iso_dict = get_attribute(f, :isos_on_complement_of_center) + pr_res = first(values(iso_dict)) + U = domain(pr_res) V = codomain(pr_res) iso_U = _flatten_open_subscheme(U, default_covering(X)) U_flat = codomain(iso_U) @@ -544,3 +617,20 @@ function pushforward(f::BlowupMorphism, g::VarietyFunctionFieldElem) pfg = fraction(pullback(phi)(OO(V)(numerator(h))))//fraction(pullback(phi)(OO(V)(denominator(h)))) return FY.(pfg) end + +@attr AbsSpecMor function isomorphism_on_open_subset(phi::AbsCoveredSchemeMorphism) + error("attribute not found; this needs to be set manually in general") +end + +function compose(f::BlowupMorphism, g::BlowupMorphism) + return composite_map(f, g) +end + +function compose(f::BlowupMorphism, g::AbsCoveredSchemeMorphism) + return composite_map(f, g) +end + +function compose(f::AbsCoveredSchemeMorphism, g::BlowupMorphism) + return composite_map(f, g) +end + diff --git a/experimental/Schemes/CoveredScheme.jl b/experimental/Schemes/CoveredScheme.jl index 54a0d83408a2..9a84c7536d2a 100644 --- a/experimental/Schemes/CoveredScheme.jl +++ b/experimental/Schemes/CoveredScheme.jl @@ -568,3 +568,197 @@ function CoveredClosedEmbedding(X::AbsCoveredScheme, I::IdealSheaf; return CoveredClosedEmbedding(Z, X, cov_inc, ideal_sheaf=I, check=false) end +######################################################################## +# Composite morphism of covered schemes +######################################################################## + +@doc raw""" + CompositeCoveredSchemeMorphism{ + DomainType<:AbsCoveredScheme, + CodomainType<:AbsCoveredScheme, + BaseMorphismType + } <: AbsCoveredSchemeMorphism{ + DomainType, + CodomainType, + BaseMorphismType, + CoveredSchemeMorphism + } + +A special concrete type of an `AbsCoveredSchemeMorphism` of the +form ``f = hᵣ ∘ hᵣ₋₁ ∘ … ∘ h₁: X → Y`` for arbitrary +`AbsCoveredSchemeMorphism`s ``h₁ : X → Z₁``, ``h₂ : Z₁ → Z₂``, ..., +``hᵣ : Zᵣ₋₁ → Y``. + +Since every such morphism ``hⱼ`` will in general have an underlying +`CoveringMorphism` with `domain` and `codomain` `covering` actual +composition of such a sequence of morphisms will lead to an exponential +increase in complexity of these coverings because of the necessary +refinements. Nevertheless, the pullback or pushforward of various objects +on either ``X`` or ``Y`` through such a chain of maps is possible stepwise. +This type allows one to have one concrete morphism rather than a list +of morphisms and to reroute such calculations to iteration over the +various maps. + +In addition to the usual functionality of the `AbsCoveredSchemeMorphism` +interface, this concrete type has the getters + + maps(f::CompositeCoveredSchemeMorphism) + +to obtain a list of the ``hⱼ`` and `map(f, j)` to obtain the `j`-th map +directly. +""" +@attributes mutable struct CompositeCoveredSchemeMorphism{ + DomainType<:AbsCoveredScheme, + CodomainType<:AbsCoveredScheme, + BaseMorphismType + } <: AbsCoveredSchemeMorphism{ + DomainType, + CodomainType, + BaseMorphismType, + CoveredSchemeMorphism + } + maps::Vector{<:AbsCoveredSchemeMorphism} + + # fields for caching + composed_map::AbsCoveredSchemeMorphism + + function CompositeCoveredSchemeMorphism(maps::Vector{<:AbsCoveredSchemeMorphism}) + n = length(maps) + for i in 1:n-1 + @assert codomain(maps[i]) === domain(maps[i+1]) "maps are not compatible" + end + # TODO: Take care of non-trivial base changes! + return new{typeof(domain(first(maps))), typeof(codomain(maps[end])), Nothing}(maps) + end +end + +### Essential getters +maps(f::CompositeCoveredSchemeMorphism) = f.maps +map(f::CompositeCoveredSchemeMorphism, i::Int) = f.maps[i] +domain(f::CompositeCoveredSchemeMorphism) = domain(first(f.maps)) +codomain(f::CompositeCoveredSchemeMorphism) = codomain(f.maps[end]) + +### Forwarding essential functionality (to be avoided!) +function underlying_morphism(f::CompositeCoveredSchemeMorphism) + if !isdefined(f, :composed_map) + result = underlying_morphism(first(maps(f)))::CoveredSchemeMorphism + for i in 2:length(maps(f)) + result = compose(result, underlying_morphism(maps(f)[i]))::CoveredSchemeMorphism + end + f.composed_map = result + end + return f.composed_map::CoveredSchemeMorphism +end + +### Specialized functionality + +# Casting into the minimal concrete type for AbsCoveredSchemeMorphism +function CoveredSchemeMorphism(f::CompositeCoveredSchemeMorphism) + return underlying_morphism(f) +end + +function CoveredSchemeMorphism(f::CoveredSchemeMorphism) + return f +end + +######################################################################## +# The standard constructors +######################################################################## +@doc raw""" + composite_map(f::AbsCoveredSchemeMorphism, g::AbsCoveredSchemeMorphism) + +Realize the composition ``x → g(f(x))`` as a composite map, i.e. an +instance of `CompositeCoveredSchemeMorphism`. + +# Examples +```jldoctest +julia> IA2 = affine_space(QQ, [:x, :y]) +Affine space of dimension 2 + over rational field +with coordinates [x, y] + +julia> (x, y) = gens(OO(IA2)); + +julia> I = ideal(OO(IA2), [x, y]); + +julia> pr = blow_up(IA2, I); + +julia> JJ = ideal_sheaf(exceptional_divisor(pr)); + +julia> inc_E = oscar.CoveredClosedEmbedding(domain(pr), JJ); + +julia> comp = oscar.composite_map(inc_E, pr) +Composite morphism of + Morphism: scheme over QQ covered with 2 patches -> scheme over QQ covered with 2 patches + Blow-up: scheme over QQ covered with 2 patches -> scheme over QQ covered with 1 patch + +julia> oscar.maps(comp)[1] === inc_E +true + +julia> oscar.maps(comp)[2] === pr +true + +``` +""" +function composite_map(f::AbsCoveredSchemeMorphism, g::AbsCoveredSchemeMorphism) + return CompositeCoveredSchemeMorphism([f, g]) +end + +function composite_map(f::AbsCoveredSchemeMorphism, g::CompositeCoveredSchemeMorphism) + return CompositeCoveredSchemeMorphism(pushfirst!(Vector{AbsCoveredSchemeMorphism}(copy(maps(g))), f)) +end + +function composite_map(f::CompositeCoveredSchemeMorphism, g::CompositeCoveredSchemeMorphism) + return CompositeCoveredSchemeMorphism(vcat(maps(f), maps(g))) +end + +function composite_map(f::CompositeCoveredSchemeMorphism, g::AbsCoveredSchemeMorphism) + return CompositeCoveredSchemeMorphism(push!(Vector{AbsCoveredSchemeMorphism}(copy(maps(f))), g)) +end + +######################################################################## +# Printing +######################################################################## +function Base.show(io::IO, f::CompositeCoveredSchemeMorphism) + io = pretty(io) + if get(io, :supercompact, false) + print(io, "Composite morphism") + else + print(io, "Composition of ", "$(domain(f)) -> ") + for i in 2:length(maps(f)) + print(io, "$(domain(map(f)[i])) -> ") + end + print(io, "$(codomain(map(f)[end]))") + end +end + +function Base.show(io::IO, ::MIME"text/plain", f::CompositeCoveredSchemeMorphism) + io = pretty(io) + println(io, "Composite morphism of", Indent()) + for g in maps(f) + println(io, g) + end + println(io, Dedent()) +end + +######################################################################## +# Bound functionality +######################################################################## +function pushforward(f::CompositeCoveredSchemeMorphism, a::VarietyFunctionFieldElem) + result = a + for g in maps(f) + result = pushforward(g, result) + end + return result +end + +function pullback(f::CompositeCoveredSchemeMorphism, a::VarietyFunctionFieldElem) + result = a + for g in reverse(maps(f)) + result = pullback(g, result) + end + return result +end + +### Missing compatibility +underlying_morphism(f::CoveredSchemeMorphism) = f diff --git a/experimental/Schemes/FunctionFields.jl b/experimental/Schemes/FunctionFields.jl index ff1b48ffc78f..63be557297ac 100644 --- a/experimental/Schemes/FunctionFields.jl +++ b/experimental/Schemes/FunctionFields.jl @@ -502,6 +502,19 @@ function is_regular(f::VarietyFunctionFieldElem, W::SpecOpen) end function pushforward(f::AbsCoveredSchemeMorphism, a::VarietyFunctionFieldElem) + X = domain(f) + Y = codomain(f) + parent(a) === function_field(X) || error("element does not belong to the correct ring") + has_attribute(f, :isomorphism_on_open_subset) || error("need an isomorphism on some open subset") + f_res = isomorphism_on_open_subset(f) + U = domain(f_res) + V = codomain(f_res) + aa = a[ambient_scheme(U)] + f_res_inv = inverse(f_res) + num = pullback(f_res_inv)(numerator(aa)) + den = pullback(f_res_inv)(denominator(aa)) + #bb = fraction(num)//fraction(den) + return function_field(Y)(lifted_numerator(num)*lifted_denominator(den), lifted_numerator(den)*lifted_denominator(num)) end function pullback(f::AbsCoveredSchemeMorphism, a::VarietyFunctionFieldElem) diff --git a/experimental/Schemes/elliptic_surface.jl b/experimental/Schemes/elliptic_surface.jl index d8b1ac7843d2..51d22067fd59 100644 --- a/experimental/Schemes/elliptic_surface.jl +++ b/experimental/Schemes/elliptic_surface.jl @@ -452,7 +452,7 @@ function relatively_minimal_model(E::EllipticSurface) ambient_exceptionals = EffectiveCartierDivisor{typeof(X0)}[] varnames = [:a,:b,:c,:d,:e,:f,:g,:h,:i,:j,:k,:l,:m,:n,:o,:p,:q,:r,:u,:v,:w] - projectionsX = BlowupMorphism{typeof(X0)}[] + projectionsX = BlowupMorphism[] projectionsY = AbsCoveredSchemeMorphism[] count = 0 diff --git a/test/AlgebraicGeometry/Schemes/BlowupMorphism.jl b/test/AlgebraicGeometry/Schemes/BlowupMorphism.jl index c90a647eb83e..23c1a5c31a75 100644 --- a/test/AlgebraicGeometry/Schemes/BlowupMorphism.jl +++ b/test/AlgebraicGeometry/Schemes/BlowupMorphism.jl @@ -8,6 +8,9 @@ bl = blow_up(IZ) + @test bl isa AbsCoveredSchemeMorphism{<:AbsCoveredScheme, typeof(X), Nothing, BlowupMorphism} + @test underlying_morphism(bl) === projection(bl) + Y = domain(bl) @test codomain(bl) === X @test Y isa AbsCoveredScheme @@ -20,7 +23,7 @@ end S = ambient_coordinate_ring(IP2) (x,y,z) = gens(S) I = ideal(S, [x, y]) - set_name!(X, "ℙ²") + #set_name!(IP2, "ℙ²") II = IdealSheaf(IP2, I) p = blow_up(II) C = Oscar.effective_cartier_divisor(IP2, (x+y)^2) @@ -39,11 +42,108 @@ end S = homogeneous_coordinate_ring(P) (x, y, z) = gens(S) II = IdealSheaf(P, [x, y]) + JJ = IdealSheaf(P, [x^2*z-y^3]) + Y = scheme(II) p = blow_up(II) + X = domain(p) f = oscar.isomorphism_on_complement_of_center(p) h = inverse(f) U = domain(f) @test compose(f, h) == identity_map(U) V = codomain(f) @test compose(h, f) == identity_map(V) + g = oscar.isomorphism_on_open_subset(p) + @test is_isomorphism(g) + KY = function_field(Y) + KX = function_field(X) + y, z = gens(ambient_coordinate_ring(first(affine_charts(Y)))) + a = KY(y, z) + b = KY(a[affine_charts(Y)[2]]) + @test pullback(p)(a) == pullback(p)(b) + + inc_C = oscar.CoveredClosedEmbedding(scheme(JJ), JJ) + C = domain(inc_C) + C_up, inc_C_up, p_res = strict_transform(p, inc_C) + + @test is_isomorphism(oscar.isomorphism_on_open_subset(p_res)) + + KC_up = function_field(C_up) + KC = function_field(C) + aa = KC(a[affine_charts(Y)[3]]) + @test pullback(p_res)(aa)^2 + one(KC_up) == pullback(p_res)(aa^2 + one(KC)) + +end + +@testset "pushforward of function field elements through resolutions" begin + IA3 = affine_space(QQ, [:x, :y, :z]) + x, y, z = gens(OO(IA3)) + pr1 = blow_up(IA3, ideal(OO(IA3), [x, y, z])) + Y0 = codomain(pr1) + Y1 = domain(pr1) + KY0 = function_field(Y0) + xx = KY0(x) + yy = KY0(y) + zz = KY0(z) + + f = x^4 - y*z + I = ideal(OO(IA3), f) + II = ideal_sheaf(Y0, IA3, [f]) + inc = oscar.CoveredClosedEmbedding(Y0, II) + X0 = domain(inc) + KX0 = function_field(X0) + xx0 = KX0(x) + yy0 = KX0(y) + zz0 = KX0(z) + # Need to call this once so that the attribute is set + oscar.isomorphism_on_open_subset(pr1) + X1, inc1, pr1_res = strict_transform(pr1, inc) + xx1 = pullback(pr1_res)(xx0) + @test xx0 == pushforward(pr1_res, xx1) + yy1 = pullback(pr1_res)(yy0) + @test yy0 == pushforward(pr1_res, yy1) + zz1 = pullback(pr1_res)(zz0) + @test zz0 == pushforward(pr1_res, zz1) + + I_sing = radical(pushforward(inc1, oscar.ideal_sheaf_of_singular_locus(X1))) + + pr2 = blow_up(I_sing) + @test scheme(I_sing) === domain(pr1) + @test codomain(pr2) === domain(pr1) + oscar.isomorphism_on_open_subset(pr2) + Y2 = domain(pr2) + X2, inc2, pr2_res = strict_transform(pr2, inc1) + + pr_res = oscar.composite_map(pr2_res, pr1_res) + pr = oscar.compose(pr2, pr1) + @test pushforward(pr, pullback(pr, yy)^2) == yy^2 + @test pushforward(pr_res, pullback(pr_res, yy0)^2) == yy0^2 + + pr_inc = compose(compose(inc2, pr2), pr1) + @test underlying_morphism(pr1) isa CoveredSchemeMorphism + @test domain(underlying_morphism(pr1)) === domain(pr1) + @test codomain(underlying_morphism(pr1)) === codomain(pr1) + @test compose(inc2, pr2) isa oscar.CompositeCoveredSchemeMorphism + @test domain(compose(inc2, pr2)) === domain(inc2) + @test codomain(compose(inc2, pr2)) === codomain(pr2) + + @test domain(pr1) === codomain(compose(inc2, pr2)) + @test domain(underlying_morphism(pr1)) === codomain(compose(inc2, pr2)) + pr_inc2 = compose(compose(inc2, pr2), underlying_morphism(pr1)) + pr_inc3 = compose(underlying_morphism(compose(inc2, pr2)), underlying_morphism(pr1)) + pr_inc4 = compose(underlying_morphism(compose(inc2, pr2)), pr1) + + @test CoveredSchemeMorphism(pr_inc) == CoveredSchemeMorphism(pr_inc2) + @test CoveredSchemeMorphism(pr_inc) == CoveredSchemeMorphism(pr_inc3) + @test CoveredSchemeMorphism(pr_inc) == CoveredSchemeMorphism(pr_inc4) + + pr_inc_alt = oscar.composite_map(pr2_res, compose(inc1, pr1)) + pr_inc_alt2 = oscar.composite_map(compose(pr2_res, inc1), pr1) + pr_inc_alt3 = oscar.composite_map(pr2_res, compose(inc1, underlying_morphism(pr1))) + pr_inc_alt4 = oscar.composite_map(compose(pr2_res, inc1), underlying_morphism(pr1)) + + @test CoveredSchemeMorphism(pr_inc) == CoveredSchemeMorphism(pr_inc_alt) + @test CoveredSchemeMorphism(pr_inc) == CoveredSchemeMorphism(pr_inc_alt2) + @test CoveredSchemeMorphism(pr_inc) == CoveredSchemeMorphism(pr_inc_alt3) + @test CoveredSchemeMorphism(pr_inc) == CoveredSchemeMorphism(pr_inc_alt4) end + diff --git a/test/AlgebraicGeometry/Schemes/runtests.jl b/test/AlgebraicGeometry/Schemes/runtests.jl index 4d25bec763c4..98b411985308 100644 --- a/test/AlgebraicGeometry/Schemes/runtests.jl +++ b/test/AlgebraicGeometry/Schemes/runtests.jl @@ -28,3 +28,4 @@ include("WeilDivisor.jl") include("RationalMap.jl") include("AffineRationalPoint.jl") include("ProjectiveRationalPoint.jl") +include("BlowupMorphism.jl")