diff --git a/docs/src/CommutativeAlgebra/localizations.md b/docs/src/CommutativeAlgebra/localizations.md index 84c3ce982339..ea8f565ecdff 100644 --- a/docs/src/CommutativeAlgebra/localizations.md +++ b/docs/src/CommutativeAlgebra/localizations.md @@ -101,6 +101,24 @@ the general [Ring Interface](@ref) of Oscar! This has not been done to a full ex for the previous two examples, but for `MPolyLocalizedRing`; see below. +### Homomorphisms for localized rings + +Homomorphisms from localized rings to arbitrary algebras are of type +```@docs + AbsLocalizedRingHom +``` +Note that, in order to be well-defined, we must have +that ``\phi'(u) \in S`` must be a unit for every element ``u \in U``. + +The getters associated to this type which need to be implemented are +```@docs + domain(f::AbsLocalizedRingHom) + codomain(f::AbsLocalizedRingHom) + restricted_map(f::AbsLocalizedRingHom) +``` +Any concrete instance `f` of `AbsLocalizedRingHom` can then be applied to elements +`a` of `domain(f)` by calling `f(a)`. + ### Ideals in localized rings One of the main reasons to implement localizations in the first place @@ -408,64 +426,3 @@ lifted_denominator(a::MPolyQuoLocalizedRingElem) fraction(a::MPolyQuoLocalizedRingElem) ``` -## Homomorphisms of localized affine algebras -Suppose we are given two localizations of polynomial algebras -by means of commutative diagrams -```math -\begin{matrix} - R & → & P = R/I\\ - ↓ & & ↓ \\ -V = R[T⁻¹] & → & P[T⁻¹] -\end{matrix} -``` -and -```math -\begin{matrix} - S & → & Q = S/J\\ - ↓ & & ↓ \\ -W = S[U⁻¹] & → & Q[U⁻¹]. -\end{matrix} -``` - -**Lemma:** -For any homomorphism ``φ : P[T⁻¹] → Q[U⁻¹]`` the following holds. -```math -\begin{matrix} - &φ& \\ - P[T⁻¹] & → & Q[U⁻¹]\\ - ↑ & & ↑\\ - R[T⁻¹] & \dashrightarrow & S[U⁻¹]\\ - ↑ & ↗ ψ &↑ ι\\ - R &→ &S[c⁻¹]\\ - & η & ↑ κ\\ - & & S -\end{matrix} -``` - 1) The composition of maps ``R → Q[U⁻¹]`` completely determines ``φ`` by the images ``xᵢ ↦ [aᵢ]/[bᵢ]`` with ``aᵢ ∈ S``, ``bᵢ ∈ U``. - - 2) Let ``ψ : R → S[U⁻¹]`` be the map determined by some choice of the images ``xᵢ↦ aᵢ/b``ᵢ as above. Then ``ψ`` extends to a map ``R[T⁻¹] → S[U⁻¹]`` if and only if for all ``t ∈ T : ψ(t) ∈ U``. This is not necessarily the case as the lift of images ``φ(t) ∈ Q[U⁻¹]`` in ``S[U⁻¹]`` need only be elements of ``U + J``. - - 3) Choosing a common denominator ``c`` for all ``ψ(xᵢ)``, we obtain a ring homomorphism ``η : R → S[c⁻¹]`` such that ``ψ = ι ∘ η``. - -Upshot: In order to describe ``φ``, we may store some homomorphism -``ψ : R → S[U⁻¹]`` -lifting it and keep in mind the ambiguity of choices for such ``ψ``. -The latter point 3) will be useful for reducing to a homomorphism -of finitely generated algebras. - - -A homomorphism of localized affine algebras is stored in -```@docs -MPolyQuoLocalizedRingHom{ - BaseRingType, - BaseRingElemType, - RingType, - RingElemType, - DomainMultSetType, - CodomainMultSetType -} -``` -An additional getter method is -```@docs -images(f::MPolyQuoLocalizedRingHom) -``` diff --git a/docs/src/Experimental/elliptic_curves.md b/docs/src/Experimental/elliptic_curves.md index 9e5240b68936..00031cee88a5 100644 --- a/docs/src/Experimental/elliptic_curves.md +++ b/docs/src/Experimental/elliptic_curves.md @@ -104,7 +104,7 @@ A = ResidueRing(ZZ, ZZ(n)) S, (x,y,z) = PolynomialRing(A, ["x", "y", "z"]) T, _ = grade(S) E = Oscar.ProjEllipticCurve(T(y^2*z - x^3 - 10*x*z^2 + 2*z^3)) -PP = projective_space(A, 2) +PP = proj_space(A, 2) Q = Oscar.Geometry.ProjSpcElem(PP[1], [A(1), A(3), A(1)]) P = Oscar.Point_EllCurve(E, Q) P2 = Oscar.IntMult_Point_EllCurveZnZ(ZZ(2), P) diff --git a/docs/src/Experimental/plane_curves.md b/docs/src/Experimental/plane_curves.md index ca9b5ed52c38..ee81a426833f 100644 --- a/docs/src/Experimental/plane_curves.md +++ b/docs/src/Experimental/plane_curves.md @@ -80,7 +80,7 @@ the projective plane as follows, where `K` is the base ring: #### Example ```@repl oscar K = QQ -PP = projective_space(K, 2) +PP = proj_space(K, 2) ``` Then, one can define a projective point as follows: diff --git a/experimental/Experimental.jl b/experimental/Experimental.jl index 15b60a75bcdb..9d287069b43b 100644 --- a/experimental/Experimental.jl +++ b/experimental/Experimental.jl @@ -5,3 +5,9 @@ include("PlaneCurve.jl") include("InvariantTheory.jl") include("GITFans.jl") include("GModule.jl") + +include("Schemes/AffineSchemes.jl") +include("Schemes/SpecOpen.jl") +include("Schemes/Glueing.jl") +include("Schemes/ProjectiveSchemes.jl") + diff --git a/experimental/PlaneCurve/DivisorCurve.jl b/experimental/PlaneCurve/DivisorCurve.jl index 47216895a7fc..1f5de1ba748b 100644 --- a/experimental/PlaneCurve/DivisorCurve.jl +++ b/experimental/PlaneCurve/DivisorCurve.jl @@ -81,7 +81,7 @@ julia> T, _ = grade(S) julia> C = Oscar.ProjPlaneCurve(T(y^2 + y*z + x^2)) Projective plane curve defined by x^2 + y^2 + y*z -julia> PP = projective_space(QQ, 2) +julia> PP = proj_space(QQ, 2) (Projective space of dim 2 over Rational Field , MPolyElem_dec{fmpq, fmpq_mpoly}[x[0], x[1], x[2]]) @@ -302,7 +302,7 @@ julia> T, _ = grade(S) julia> C = Oscar.ProjPlaneCurve(T(y^2 + y*z + x^2)) Projective plane curve defined by x^2 + y^2 + y*z -julia> PP = projective_space(QQ, 2) +julia> PP = proj_space(QQ, 2) (Projective space of dim 2 over Rational Field , MPolyElem_dec{fmpq, fmpq_mpoly}[x[0], x[1], x[2]]) @@ -424,7 +424,7 @@ end function divisor(C::ProjPlaneCurve{S}, F::Oscar.MPolyElem_dec{S}) where S <: FieldElem R = parent(C.eq) - PP = projective_space(R.R.base_ring, 2) + PP = proj_space(R.R.base_ring, 2) return divisor(PP[1], C, F) end @@ -449,7 +449,7 @@ julia> T, _ = grade(S) julia> C = Oscar.ProjPlaneCurve(T(y^2 + y*z + x^2)) Projective plane curve defined by x^2 + y^2 + y*z -julia> PP = projective_space(QQ, 2) +julia> PP = proj_space(QQ, 2) (Projective space of dim 2 over Rational Field , MPolyElem_dec{fmpq, fmpq_mpoly}[x[0], x[1], x[2]]) @@ -471,7 +471,7 @@ end function divisor(C::ProjPlaneCurve{S}, phi::AbstractAlgebra.Generic.Frac{T}) where {S <: FieldElem, T <: Oscar.MPolyElem_dec{S}} R = parent(C.eq) - PP = projective_space(R.R.base_ring, 2) + PP = proj_space(R.R.base_ring, 2) return divisor(PP[1], C, phi) end @@ -596,7 +596,7 @@ julia> T, _ = grade(S) julia> C = Oscar.ProjPlaneCurve(T(y^2*z - x*(x-z)*(x+3*z))) Projective plane curve defined by -x^3 - 2*x^2*z + 3*x*z^2 + y^2*z -julia> PP = projective_space(QQ, 2) +julia> PP = proj_space(QQ, 2) (Projective space of dim 2 over Rational Field , MPolyElem_dec{fmpq, fmpq_mpoly}[x[0], x[1], x[2]]) @@ -695,7 +695,7 @@ julia> T, _ = grade(S) julia> C = Oscar.ProjPlaneCurve(T(y^2*z - x*(x-z)*(x+3*z))) Projective plane curve defined by -x^3 - 2*x^2*z + 3*x*z^2 + y^2*z -julia> PP = projective_space(QQ, 2) +julia> PP = proj_space(QQ, 2) (Projective space of dim 2 over Rational Field , MPolyElem_dec{fmpq, fmpq_mpoly}[x[0], x[1], x[2]]) @@ -739,7 +739,7 @@ julia> T, _ = grade(S) julia> C = Oscar.ProjPlaneCurve(T(y^2*z - x*(x-z)*(x+3*z))) Projective plane curve defined by -x^3 - 2*x^2*z + 3*x*z^2 + y^2*z -julia> PP = projective_space(QQ, 2) +julia> PP = proj_space(QQ, 2) (Projective space of dim 2 over Rational Field , MPolyElem_dec{fmpq, fmpq_mpoly}[x[0], x[1], x[2]]) @@ -781,7 +781,7 @@ julia> T, _ = grade(S) julia> C = Oscar.ProjPlaneCurve(T(y^2*z - x*(x-z)*(x+3*z))) Projective plane curve defined by -x^3 - 2*x^2*z + 3*x*z^2 + y^2*z -julia> PP = projective_space(QQ, 2) +julia> PP = proj_space(QQ, 2) (Projective space of dim 2 over Rational Field , MPolyElem_dec{fmpq, fmpq_mpoly}[x[0], x[1], x[2]]) diff --git a/experimental/PlaneCurve/ProjCurve.jl b/experimental/PlaneCurve/ProjCurve.jl index c2b4a54f1237..6449585ffc0c 100644 --- a/experimental/PlaneCurve/ProjCurve.jl +++ b/experimental/PlaneCurve/ProjCurve.jl @@ -91,7 +91,7 @@ julia> C = Oscar.ProjCurve(I) Projective curve defined by the ideal(x^2, y^2*z, z^2) -julia> PP = projective_space(QQ, 3) +julia> PP = proj_space(QQ, 3) (Projective space of dim 3 over Rational Field , MPolyElem_dec{fmpq, fmpq_mpoly}[x[0], x[1], x[2], x[3]]) diff --git a/experimental/PlaneCurve/ProjEllipticCurve.jl b/experimental/PlaneCurve/ProjEllipticCurve.jl index a4959d3bf4aa..cee15b78c578 100644 --- a/experimental/PlaneCurve/ProjEllipticCurve.jl +++ b/experimental/PlaneCurve/ProjEllipticCurve.jl @@ -1,6 +1,6 @@ export ProjEllipticCurve, discriminant, issmooth, j_invariant, Point_EllCurve, curve, weierstrass_form, toweierstrass, - iselliptic, list_rand, proj_space + iselliptic, list_rand ################################################################################ # Helping functions @@ -155,7 +155,7 @@ julia> T, _ = grade(S) julia> F = T(-x^3 - 3*x^2*y - 3*x*y^2 - x*z^2 - y^3 + y^2*z - y*z^2 - 4*z^3) -x^3 - 3*x^2*y - 3*x*y^2 - x*z^2 - y^3 + y^2*z - y*z^2 - 4*z^3 -julia> PP = projective_space(QQ, 2) +julia> PP = proj_space(QQ, 2) (Projective space of dim 2 over Rational Field , MPolyElem_dec{fmpq, fmpq_mpoly}[x[0], x[1], x[2]]) @@ -186,7 +186,7 @@ mutable struct ProjEllipticCurve{S} <: ProjectivePlaneCurve{S} v = shortformtest(eq.f) T = parent(eq) K = T.R.base_ring - PP = projective_space(K, 2) + PP = proj_space(K, 2) V = gens(T) new{S}(eq, 3, Dict{ProjEllipticCurve{S}, Int}(), Oscar.Geometry.ProjSpcElem(PP[1], [K(0), K(1), K(0)]), [hom(T, T, V), hom(T, T, V)], Hecke.EllipticCurve(v[2], v[1])) end @@ -218,7 +218,7 @@ mutable struct ProjEllipticCurve{S} <: ProjectivePlaneCurve{S} K = T.R.base_ring n = modulus(K) gcd(Hecke.data(d), n) == ZZ(1) || error("The discriminant is not invertible") - PP = projective_space(K, 2) + PP = proj_space(K, 2) V = gens(T) E = new{S}() E.eq = eq @@ -274,7 +274,7 @@ julia> T, _ = grade(S) julia> F = T(-x^3 - 3*x^2*y - 3*x*y^2 - x*z^2 - y^3 + y^2*z - y*z^2 - 4*z^3) -x^3 - 3*x^2*y - 3*x*y^2 - x*z^2 - y^3 + y^2*z - y*z^2 - 4*z^3 -julia> PP = projective_space(QQ, 2) +julia> PP = proj_space(QQ, 2) (Projective space of dim 2 over Rational Field , MPolyElem_dec{fmpq, fmpq_mpoly}[x[0], x[1], x[2]]) @@ -462,7 +462,7 @@ end Return the projective space to which the point `P` belongs. """ -function proj_space(P::Point_EllCurve{S}) where S <: FieldElem +function Oscar.Geometry.proj_space(P::Point_EllCurve{S}) where S <: FieldElem return P.Pt.parent end @@ -564,7 +564,7 @@ julia> T, _ = grade(S) y -> [1] z -> [1], MPolyElem_dec{fmpq, fmpq_mpoly}[x, y, z]) -julia> PP = projective_space(QQ, 2) +julia> PP = proj_space(QQ, 2) (Projective space of dim 2 over Rational Field , MPolyElem_dec{fmpq, fmpq_mpoly}[x[0], x[1], x[2]]) @@ -638,7 +638,7 @@ end Return the projective space to which the base point of the elliptic curve `E` belongs. """ -function proj_space(E::ProjEllipticCurve{S}) where S <: FieldElem +function Oscar.Geometry.proj_space(E::ProjEllipticCurve{S}) where S <: FieldElem return base_point(E).parent end diff --git a/experimental/PlaneCurve/ProjPlaneCurve.jl b/experimental/PlaneCurve/ProjPlaneCurve.jl index 074043207b10..ea473ac49535 100644 --- a/experimental/PlaneCurve/ProjPlaneCurve.jl +++ b/experimental/PlaneCurve/ProjPlaneCurve.jl @@ -32,7 +32,7 @@ julia> T, _ = grade(S) julia> C = Oscar.ProjPlaneCurve(x^2*(x+y)*(y^3-x^2*z)) Projective plane curve defined by -x^5*z - x^4*y*z + x^3*y^3 + x^2*y^4 -julia> PP = projective_space(QQ, 2) +julia> PP = proj_space(QQ, 2) (Projective space of dim 2 over Rational Field , MPolyElem_dec{fmpq, fmpq_mpoly}[x[0], x[1], x[2]]) @@ -74,7 +74,7 @@ julia> T, _ = grade(S) y -> [1] z -> [1], MPolyElem_dec{fmpq, fmpq_mpoly}[x, y, z]) -julia> PP = projective_space(QQ, 2) +julia> PP = proj_space(QQ, 2) (Projective space of dim 2 over Rational Field , MPolyElem_dec{fmpq, fmpq_mpoly}[x[0], x[1], x[2]]) @@ -155,7 +155,7 @@ julia> T, _ = grade(S) y -> [1] z -> [1], MPolyElem_dec{fmpq, fmpq_mpoly}[x, y, z]) -julia> PP = projective_space(QQ, 2) +julia> PP = proj_space(QQ, 2) (Projective space of dim 2 over Rational Field , MPolyElem_dec{fmpq, fmpq_mpoly}[x[0], x[1], x[2]]) @@ -223,7 +223,7 @@ end function curve_intersect(C::ProjectivePlaneCurve{S}, D::ProjectivePlaneCurve{S}) where S <: FieldElem R = parent(C.eq) - PP = projective_space(R.R.base_ring, 2) + PP = proj_space(R.R.base_ring, 2) curve_intersect(PP[1], C, D) end @@ -298,7 +298,7 @@ end function curve_singular_locus(C::ProjectivePlaneCurve) R = parent(C.eq) - PP = projective_space(R.R.base_ring, 2) + PP = proj_space(R.R.base_ring, 2) curve_singular_locus(PP[1], C) end diff --git a/experimental/Schemes/AffineSchemes.jl b/experimental/Schemes/AffineSchemes.jl index e107e3fae7f4..1103918b0d8b 100644 --- a/experimental/Schemes/AffineSchemes.jl +++ b/experimental/Schemes/AffineSchemes.jl @@ -1,14 +1,25 @@ import AbstractAlgebra.Ring import Base: intersect -export Spec, OO -export ⊂ - -export is_open_embedding, is_closed_embedding, hypersurface_complement, subscheme, name_of, set_name! +export Scheme +export Spec, OO, defining_ideal +export spec_type, ring_type +export base_ring_type, base_ring_elem_type, poly_type, poly_ring_type, mult_set_type, ring_type +export affine_space, empty_spec +export EmptyScheme + +export is_open_embedding, is_closed_embedding, is_canonically_isomorphic, hypersurface_complement, subscheme, name_of, set_name! export closure, product -export SpecMor -export pullback, domain, codomain, preimage, restrict, graph +export SpecMor, morphism_type +export pullback, domain, codomain, preimage, restrict, graph, identity_map, inclusion_map, is_isomorphism, is_inverse_of + +export strict_modulus + +# TODO for Tommy: Find out why the following are necessary +AbstractAlgebra.promote_rule(::Type{gfp_mpoly}, ::Type{fmpz}) = gfp_mpoly +AbstractAlgebra.promote_rule(::Type{gfp_elem}, ::Type{fmpz}) = gfp_elem +AbstractAlgebra.promote_rule(::Type{gfp_elem}, ::Type{AbstractAlgebra.Generic.Frac{gfp_mpoly}}) = AbstractAlgebra.Generic.Frac{gfp_mpoly} @Markdown.doc """ Scheme{BaseRingType<:Ring, BaseRingElemType<:RingElement} @@ -34,7 +45,7 @@ polynomial algebra of type `RT` over a base ring ``k`` of type with elements of type `RET`, and ``S`` a multiplicative set in ``R`` of type `MST`. """ -mutable struct Spec{BRT, BRET, RT, RET, MST} <: Scheme{BRT, BRET} +@attributes mutable struct Spec{BRT, BRET, RT, RET, MST} <: Scheme{BRT, BRET} # the basic fields OO::MPolyQuoLocalizedRing{BRT, BRET, RT, RET, MST} # fields for caching @@ -45,6 +56,33 @@ mutable struct Spec{BRT, BRET, RT, RET, MST} <: Scheme{BRT, BRET} end end +### Type getters + +ring_type(::Type{Spec{BRT, BRET, RT, RET, MST}}) where {BRT, BRET, RT, RET, MST} = MPolyQuoLocalizedRing{BRT, BRET, RT, RET, MST} +ring_type(X::Spec) = ring_type(typeof(X)) + +base_ring_type(X::Spec{BRT, BRET, RT, RET, MST}) where {BRT, BRET, RT, RET, MST} = BRT +base_ring_elem_type(X::Spec{BRT, BRET, RT, RET, MST}) where {BRT, BRET, RT, RET, MST} = BRET +mult_set_type(X::Spec{BRT, BRET, RT, RET, MST}) where {BRT, BRET, RT, RET, MST} = MST +poly_ring_type(X::Spec{BRT, BRET, RT, RET, MST}) where {BRT, BRET, RT, RET, MST} = RT +poly_type(X::Spec{BRT, BRET, RT, RET, MST}) where {BRT, BRET, RT, RET, MST} = RET + +base_ring_type(::Type{Spec{BRT, BRET, RT, RET, MST}}) where {BRT, BRET, RT, RET, MST} = BRT +base_ring_elem_type(::Type{Spec{BRT, BRET, RT, RET, MST}}) where {BRT, BRET, RT, RET, MST} = BRET +mult_set_type(::Type{Spec{BRT, BRET, RT, RET, MST}}) where {BRT, BRET, RT, RET, MST} = MST +poly_ring_type(::Type{Spec{BRT, BRET, RT, RET, MST}}) where {BRT, BRET, RT, RET, MST} = RT +poly_type(::Type{Spec{BRT, BRET, RT, RET, MST}}) where {BRT, BRET, RT, RET, MST} = RET + +### type constructors + +# this defaults to Specs over localizations of polynomial rings at hypersurfaces +# and does not cover localizations for germs! +spec_type(R::T) where {T<:AbstractAlgebra.Ring} = Spec{T, elem_type(T), mpoly_ring_type(T), mpoly_type(T), MPolyPowersOfElement{T, elem_type(T), mpoly_ring_type(T), mpoly_type(T)}} +spec_type(::Type{T}) where {T<:AbstractAlgebra.Ring} = Spec{T, elem_type(T), mpoly_ring_type(T), mpoly_type(T), MPolyPowersOfElement{T, elem_type(T), mpoly_ring_type(T), mpoly_type(T)}} +spec_type(L::MPolyQuoLocalizedRing{S, T, U, V, W}) where {S, T, U, V, W} = Spec{S, T, U, V, W} +spec_type(::Type{MPolyQuoLocalizedRing{S, T, U, V, W}}) where {S, T, U, V, W} = Spec{S, T, U, V, W} + + ### Getter functions @@ -74,6 +112,9 @@ function Base.show(io::IO, X::Spec) print(io, "Spec of $(OO(X))") end +base_ring(X::Spec) = coefficient_ring(base_ring(OO(X))) +defining_ideal(X::Spec) = modulus(OO(X)) + ### Copy constructor Spec(X::Spec) = Spec(OO(X)) @@ -88,6 +129,15 @@ Spec(Q::MPolyQuo) = Spec(MPolyQuoLocalizedRing(Q)) Spec(W::MPolyLocalizedRing) = Spec(MPolyQuoLocalizedRing(W)) Spec(R::MPolyRing, I::MPolyIdeal, U::AbsMPolyMultSet) = Spec(MPolyQuoLocalizedRing(R, I, U)) +Spec(R::MPolyRing, U::AbsMPolyMultSet) = Spec(MPolyQuoLocalizedRing(R, ideal(R, [zero(R)]), U)) +Spec(R::MPolyRing, I::MPolyIdeal) = Spec(MPolyQuoLocalizedRing(R, I, units_of(R))) + +# Hack for the construction of the empty scheme over kk +# as an instance of Spec +function empty_spec(kk::BRT) where {BRT<:AbstractAlgebra.Ring} + R, (x,) = PolynomialRing(kk, ["x"]) + return Spec(R, ideal(R, [x]), MPolyPowersOfElement(x)) +end ### closed subschemes defined by ideals function subscheme(X::Spec{BRT, BRET, RT, RET, MST}, I::MPolyLocalizedIdeal{BRT, BRET, RT, RET, MST}) where {BRT, BRET, RT, RET, MST} @@ -118,6 +168,26 @@ function subscheme(X::Spec{BRT, BRET, RT, RET, MST}, f::Vector{RET}) where {BRT, return subscheme(X, I) end +function subscheme(X::Spec, f::RET) where {RET<:MPolyQuoLocalizedRingElem} + I = ideal(OO(X), [f]) + return subscheme(X, I) +end + +function subscheme(X::Spec, f::Vector{RET}) where {RET<:MPolyQuoLocalizedRingElem} + I = ideal(OO(X), f) + return subscheme(X, I) +end + +function subscheme(X::Spec, f::RET) where {RET<:MPolyLocalizedRingElem} + I = ideal(OO(X), [f]) + return subscheme(X, I) +end + +function subscheme(X::Spec, f::Vector{RET}) where {RET<:MPolyLocalizedRingElem} + I = ideal(OO(X), f) + return subscheme(X, I) +end + function subscheme(X::Spec{BRT, BRET, RT, RET, MST}, f::BRET) where {BRT, BRET, RT, RET, MST} R = base_ring(OO(X)) I = ideal(R, R(f)) @@ -138,10 +208,43 @@ For a scheme ``X = Spec ((𝕜[x₁,…,xₙ]/I)[S⁻¹])`` and an element ``f this returns the open subscheme ``U = X ∖ V(f)`` defined by the complement of the vanishing locus of ``f``. """ -function hypersurface_complement(X::Spec{BRT, BRET, RT, RET, MST}, f::RET) where {BRT, BRET, RT, RET, MST<:MPolyPowersOfElement{BRT, BRET, RT, RET}} +function hypersurface_complement(X::Spec{BRT, BRET, RT, RET, MST}, f::RET; keep_cache::Bool=false) where {BRT, BRET, RT, RET, MST<:MPolyPowersOfElement{BRT, BRET, RT, RET}} R = base_ring(OO(X)) parent(f) == R || error("the element does not belong to the correct ring") - return Spec(Localization(OO(X), MPolyPowersOfElement(f))) + iszero(f) && return subscheme(X, [one(R)]) + f in inverted_set(OO(X)) && return X + #f = numerator(reduce(localized_ring(OO(X))(f), groebner_basis(localized_modulus(OO(X))))) + W = Localization(OO(X), MPolyPowersOfElement(R, [a[1] for a in factor(f)])) + if keep_cache + IX = localized_modulus(OO(X)) + DIX = groebner_bases(IX) + # we have a look at the possibility to transfer groebner bases that + # have already been computed. + if length(DIX)>0 + if has_attribute(IX, :saturated_ideal) + # in case the saturated ideal has already been computed once, + # we assume that this computation is also feasible a second time. + # We check whether the new saturation makes any difference and + # transfer the cached data if applicable. + Jsat = ideal(R, numerator.(oscar_gens(groebner_basis(IX)))) + for d in [b for b in denominators(inverted_set(W)) if !(b in inverted_set(OO(X)))] + for a in factor(d) + Jsat = saturation(Jsat, ideal(R, a[1])) + end + end + J = localized_modulus(W) + set_attribute!(J, :saturated_ideal, Jsat) + if issubset(Jsat, saturated_ideal(IX)) + for o in keys(DIX) + gb = DIX[o] + groebner_bases(J)[o] = LocalizedBiPolyArray(localized_ring(W), singular_gens(gb), shift(gb), true) + end + end + set_attribute!(W, :localized_modulus, J) + end + end + end + return Spec(W) end function hypersurface_complement( @@ -161,13 +264,12 @@ end ### testing containment -⊂( - X::Scheme{BRT, BRET}, - Y::Scheme{BRT, BRET} - ) where {BRT, BRET} = issubset(X, Y) - issubset(X::EmptyScheme{BRT, BRET}, Y::Scheme{BRT, BRET}) where {BRT, BRET} = true +function issubset(Y::Spec{BRT, BRET, RT, RET, MST1}, X::EmptyScheme{BRT, BRET}) where {BRT, BRET, RT, RET, MST1} + return iszero(one(OO(Y))) +end + @Markdown.doc """ issubset( X::Spec{BRT, BRET, RT, RET, MST1}, @@ -181,7 +283,7 @@ function issubset( Y::Spec{BRT, BRET, RT, RET, MST2} ) where {BRT, BRET, RT, RET, MST1<:MPolyPowersOfElement{BRT, BRET, RT, RET}, MST2<:MPolyPowersOfElement{BRT, BRET, RT, RET}} R = base_ring(OO(X)) - R == base_ring(OO(Y)) || return false + R == base_ring(OO(Y)) || error("schemes can not be compared") UX = inverted_set(OO(X)) UY = inverted_set(OO(Y)) if !issubset(UY, UX) @@ -194,13 +296,28 @@ function issubset( return issubset(J, localized_modulus(OO(X))) end -function ==( +function ==(X::T, Y::T) where {T<:Spec} + return X === Y +end + +function is_canonically_isomorphic( X::Spec{BRT, BRET, RT, RET, MST1}, Y::Spec{BRT, BRET, RT, RET, MST2} ) where {BRT, BRET, RT, RET, MST1<:MPolyPowersOfElement{BRT, BRET, RT, RET}, MST2<:MPolyPowersOfElement{BRT, BRET, RT, RET}} + X === Y && return true + isempty(X) && isempty(Y) && return true + base_ring(OO(X)) == base_ring(OO(Y)) || return false return issubset(X, Y) && issubset(Y, X) end +function is_canonically_isomorphic(X::Spec, Y::EmptyScheme) + return issubset(X, Y) +end + +is_canonically_isomorphic(X::EmptyScheme, Y::Spec) = is_canonically_isomorphic(Y, X) + +Base.isempty(X::Spec) = iszero(one(OO(X))) + @Markdown.doc """ is_open_embedding( X::Spec{BRT, BRET, RT, RET, MST1}, @@ -251,6 +368,7 @@ function Base.intersect( X::Spec{BRT, BRET, RT, RET, MST1}, Y::Spec{BRT, BRET, RT, RET, MST2} ) where {BRT, BRET, RT, RET, MST1<:MPolyPowersOfElement{BRT, BRET, RT, RET}, MST2<:MPolyPowersOfElement{BRT, BRET, RT, RET}} + base_ring(OO(X)) == base_ring(OO(Y)) || error("schemes can not be intersected") issubset(X, Y) && return X issubset(Y, X) && return Y UX = inverted_set(OO(X)) @@ -261,7 +379,7 @@ function Base.intersect( I = IX + IY R = base_ring(OO(X)) L = MPolyQuoLocalizedRing(R, I, U) - one(localized_ring(L)) in localized_modulus(L) && return EmptyScheme(coefficient_ring(R)) + #one(localized_ring(L)) in localized_modulus(L) && return EmptyScheme(coefficient_ring(R)) return Spec(L) end @@ -293,25 +411,33 @@ end # Morphisms of affine schemes # ######################################################################## -mutable struct SpecMor{BRT, BRET, RT, RET, MST1, MST2} - domain::Spec{BRT, BRET, RT, RET, MST1} - codomain::Spec{BRT, BRET, RT, RET, MST2} - pullback::MPolyQuoLocalizedRingHom{BRT, BRET, RT, RET, MST2, MST1} - - # fields used for caching - inverse::SpecMor{BRT, BRET, RT, RET, MST2, MST1} +@attributes mutable struct SpecMor{DomainType<:Spec, CodomainType<:Spec, PullbackType<:MPolyQuoLocalizedRingHom} + domain::DomainType + codomain::CodomainType + pullback::PullbackType function SpecMor( - X::Spec{BRT, BRET, RT, RET, MST1}, - Y::Spec{BRT, BRET, RT, RET, MST2}, - pullback::MPolyQuoLocalizedRingHom{BRT, BRET, RT, RET, MST2, MST1} - ) where {BRT, BRET, RT, RET, MST1, MST2} + X::DomainType, + Y::CodomainType, + pullback::PullbackType; + check::Bool=true + ) where {DomainType<:Spec, CodomainType<:Spec, PullbackType<:MPolyQuoLocalizedRingHom} OO(X) == codomain(pullback) || error("the coordinate ring of the domain does not coincide with the codomain of the pullback") OO(Y) == domain(pullback) || error("the coordinate ring of the codomain does not coincide with the domain of the pullback") - return new{BRT, BRET, RT, RET, MST1, MST2}(X, Y, pullback) + if check + # do some more expensive tests + end + return new{DomainType, CodomainType, PullbackType}(X, Y, pullback) end end +function morphism_type(::Type{SpecType1}, ::Type{SpecType2}) where {SpecType1<:Spec, SpecType2<:Spec} + return SpecMor{SpecType1, SpecType1, morphism_type(ring_type(SpecType2), ring_type(SpecType1))} +end + +morphism_type(X::Spec, Y::Spec) = morphism_type(typeof(X), typeof(Y)) + + ### getter functions pullback(phi::SpecMor) = phi.pullback domain(phi::SpecMor) = phi.domain @@ -321,24 +447,27 @@ codomain(phi::SpecMor) = phi.codomain function SpecMor( X::Spec{BRT, BRET, RT, RET, MST1}, Y::Spec{BRT, BRET, RT, RET, MST2}, - f::Vector + f::Vector; + check::Bool=true ) where {BRT, BRET, RT, RET, MST1, MST2} - return SpecMor(X, Y, MPolyQuoLocalizedRingHom(OO(Y), OO(X), f)) + return SpecMor(X, Y, MPolyQuoLocalizedRingHom(OO(Y), OO(X), OO(X).(f), check=check), check=check) end -function restrict(f::SpecMor{BRT, BRET, RT, RET, MST1, MST2}, - U::Spec{BRT, BRET, RT, RET, MST1}, - V::Spec{BRT, BRET, RT, RET, MST2} - ) where {BRT, BRET, RT, RET, MST1, MST2} - issubset(U, domain(f)) || error("second argument does not lay in the domain of the map") - issubset(V, codomain(f)) || error("third argument does not lay in the codomain of the map") - issubset(U, preimage(f, V)) || error("the image of the restriction is not contained in the restricted codomain") - return SpecMor(U, V, images(pullback(f))) +identity_map(X::Spec) = SpecMor(X, X, gens(base_ring(OO(X)))) +inclusion_map(X::T, Y::T) where {T<:Spec} = SpecMor(X, Y, gens(base_ring(OO(Y)))) + +function restrict(f::SpecMor, U::Spec, V::Spec; check::Bool=true) + if check + issubset(U, domain(f)) || error("second argument does not lay in the domain of the map") + issubset(V, codomain(f)) || error("third argument does not lay in the codomain of the map") + issubset(U, preimage(f, V)) || error("the image of the restriction is not contained in the restricted codomain") + end + return SpecMor(U, V, images(pullback(f)), check=check) end function compose(f::SpecMorType, g::SpecMorType) where {SpecMorType<:SpecMor} codomain(f) == domain(g) || error("Morphisms can not be composed") - return SpecMor(domain(f), codomain(g), compose(pullback(g), pullback(f))) + return SpecMor(domain(f), codomain(g), compose(pullback(g), pullback(f)), check=false) end function ==(f::SpecMorType, g::SpecMorType) where {SpecMorType<:SpecMor} @@ -351,7 +480,7 @@ end ### functionality function preimage( - phi::SpecMor{BRT, BRET, RT, RET, MST1, MST2}, + phi::SpecMor{Spec{BRT, BRET, RT, RET, MST1}, Spec{BRT, BRET, RT, RET, MST2}}, Z::Spec{BRT, BRET, RT, RET, MST3} ) where {BRT, BRET, RT, RET, MST1<:MPolyPowersOfElement{BRT, BRET, RT, RET}, MST2<:MPolyPowersOfElement{BRT, BRET, RT, RET}, MST3<:MPolyPowersOfElement{BRT, BRET, RT, RET}} X = domain(phi) @@ -366,18 +495,21 @@ function preimage( return Spec(MPolyQuoLocalizedRing(R, ideal(R, new_gens) + modulus(OO(X)), inverted_set(OO(X))*new_units)) end -function is_isomorphism(f::SpecMor{BRT, BRET, RT, RET, MST1, MST2}) where {BRT, BRET, RT, RET, MST1<:MPolyPowersOfElement{BRT, BRET, RT, RET}, MST2<:MPolyPowersOfElement{BRT, BRET, RT, RET}} +function is_isomorphism(f::SpecMor) is_isomorphism(pullback(f)) || return false - f.inverse = SpecMor(codomain(f), domain(f), inverse(pullback(f))) + set_attribute!(f, :inverse, SpecMor(codomain(f), domain(f), inverse(pullback(f)))) return true end -function inverse(f::SpecMor{BRT, BRET, RT, RET, MST1, MST2}) where {BRT, BRET, RT, RET, MST1<:MPolyPowersOfElement{BRT, BRET, RT, RET}, MST2<:MPolyPowersOfElement{BRT, BRET, RT, RET}} - if isdefined(f, :inverse) - return f.inverse +function is_inverse_of(f::S, g::T) where {S<:SpecMor, T<:SpecMor} + return is_isomorphism(f) && (inverse(f) == g) +end + +function inverse(f::SpecMor) + if !has_attribute(f, :inverse) + is_isomorphism(f) || error("the given morphism is not an isomorphism") end - is_isomorphism(f) || error("the given morphism is not an isomorphism") - return f.inverse + return get_attribute(f, :inverse)::morphism_type(codomain(f), domain(f)) end @Markdown.doc """ @@ -399,22 +531,22 @@ function product(X::Spec{BRT, BRET, RT, RET, MST}, Y::Spec{BRT, BRET, RT, RET, M m = length(gens(R)) n = length(gens(S)) RS, z = PolynomialRing(k, vcat(symbols(R), symbols(S))) - inc1 = AlgebraHomomorphism(R, RS, gens(RS)[1:m]) - inc2 = AlgebraHomomorphism(S, RS, gens(RS)[m+1:m+n]) - #pr1 = AlgebraHomomorphism(RS, R, vcat(gens(R), [zero(R) for i in 1:n])) - #pr2 = AlgebraHomomorphism(RS, S, vcat([zero(S) for i in 1:m], gens(S))) + inc1 = hom(R, RS, gens(RS)[1:m]) + inc2 = hom(S, RS, gens(RS)[m+1:m+n]) IX = ideal(RS, inc1.(gens(modulus(OO(X))))) IY = ideal(RS, inc2.(gens(modulus(OO(Y))))) UX = MPolyPowersOfElement(RS, inc1.(denominators(inverted_set(OO(X))))) UY = MPolyPowersOfElement(RS, inc2.(denominators(inverted_set(OO(Y))))) XxY = Spec(RS, IX + IY, UX*UY) - pr1 = SpecMor(XxY, X, gens(RS)[1:m]) - pr2 = SpecMor(XxY, Y, gens(RS)[m+1:m+n]) + pr1 = SpecMor(XxY, X, gens(RS)[1:m], check=false) + pr2 = SpecMor(XxY, Y, gens(RS)[m+1:m+n], check=false) return XxY, pr1, pr2 end -function graph(f::SpecMor{BRT, BRET, RT, RET, MST, MST}) where {BRT, BRET, RT, RET, MST<:MPolyPowersOfElement} +function graph(f::SpecMor{<:Spec{<:Any, <:Any, <:Any, <:Any, <:MPolyPowersOfElement}, + <:Spec{<:Any, <:Any, <:Any, <:Any, <:MPolyPowersOfElement}} + ) X = domain(f) Y = codomain(f) XxY, prX, prY = product(X, Y) @@ -432,3 +564,26 @@ function partition_of_unity(X::Spec{BRT, BRET, RT, RET, MST}, error("not implemented") end +#function hash(X::Spec, u::UInt) +# r = 0x753087204385757820349592852 +# return xor(r, hash(OO(X), u)) +#end + +function affine_space(kk::BRT, n::Int; variable_name="x") where {BRT<:Ring} + R, _ = PolynomialRing(kk, [ variable_name * "$i" for i in 1:n]) + return Spec(R) +end + +function affine_space(kk::BRT, var_symbols::Vector{Symbol}) where {BRT<:Ring} + R, _ = PolynomialRing(kk, var_symbols) + return Spec(R) +end + +function dim(X::Spec) + if !has_attribute(X, :dimension) + set_attribute!(X, :dimension, dim(saturated_ideal(localized_modulus(OO(X))))) + end + return get_attribute(X, :dimension)::Int64 +end + +strict_modulus(X::Spec) = saturated_ideal(localized_modulus(OO(X))) diff --git a/experimental/Schemes/Glueing.jl b/experimental/Schemes/Glueing.jl new file mode 100644 index 000000000000..04abc8685244 --- /dev/null +++ b/experimental/Schemes/Glueing.jl @@ -0,0 +1,123 @@ +export Glueing +export glueing_morphisms, patches, glueing_domains, inverse_glueing_morphism, inverse +export glueing_type + +export compose, maximal_extension, restriction + + +@Markdown.doc """ +Glueing{SpecType<:Spec, OpenType<:SpecOpen, MorType<:SpecOpenMor} + +Glueing of two affine schemes ``X ↩ U ≅ V ↪ Y`` along open subsets +``U ⊂ X`` and ``V ⊂ Y via some isomorphism ``φ : U → V``. +""" +mutable struct Glueing{SpecType<:Spec, OpenType<:SpecOpen, MorType<:SpecOpenMor} + X::SpecType + Y::SpecType + U::OpenType + V::OpenType + f::MorType # f : U → V + g::MorType + + function Glueing( + X::SpecType, Y::SpecType, f::MorType, g::MorType; check::Bool=true + ) where { + SpecType<:Spec, MorType<:SpecOpenMor + } + ambient(domain(f)) === X || error("the domain of the glueing morphism is not an open subset of the first argument") + ambient(codomain(f)) === Y || error("the codomain of the glueing morphism is not an open subset of the second argument") + if check + (is_canonically_isomorphic(domain(f), codomain(g)) && + is_canonically_isomorphic(domain(g), codomain(f))) || error("maps can not be isomorphisms") + compose(f, g) == identity_map(domain(f)) || error("glueing maps are not inverse of each other") + compose(g, f) == identity_map(domain(g)) || error("glueing maps are not inverse of each other") + end + return new{SpecType, open_subset_type(X), MorType}(X, Y, domain(f), domain(g), f, g) + end +end + +patches(G::Glueing) = G.X, G.Y +glueing_morphisms(G::Glueing) = G.f, G.g +glueing_domains(G::Glueing) = domain(G.f), domain(G.g) +inverse(G::Glueing) = Glueing(G.Y, G.X, G.g, G.f, check=false) + +function Base.show(io::IO, G::Glueing) + print(io, "Glueing of $(patches(G)[1]) and $(patches(G)[2]) along the map $(glueing_morphisms(G)[1])") +end + +@Markdown.doc """ +compose(G::GlueingType, H::GlueingType) where {GlueingType<:Glueing} + +Given glueings `X ↩ U ≅ V ↪ Y` and `Y ↩ V' ≅ W ↪ Z`, return the glueing +`X ↩ V ∩ V' ↪ Z`. + +**WARNING:** In general such a glueing will not provide a separated scheme. +Use `maximal_extension` to extend the glueing. +""" +function compose(G::GlueingType, H::GlueingType) where {GlueingType<:Glueing} + # make sure that Y is the second patch of the first glueing and + # the first patch of the second + if patches(G)[2] == patches(H)[2] + return compose(G, inverse(H)) + elseif patches(G)[1] == patches(H)[1] + return compose(inverse(G), H) + elseif patches(G)[1] == patches(H)[2] + return compose(inverse(G), inverse(H)) + end + X, Y = patches(G) + Y == patches(H)[1] || error("Glueings not compatible") + Z = patches(H)[2] + f, f_inv = glueing_morphisms(G) + g, g_inv = glueing_morphisms(H) + U_new = preimage(f, domain(g)) + W_new = preimage(g_inv, codomain(f)) + V_new = intersect(codomain(f), domain(g)) + return Glueing(X, Z, + compose(restriction(f, U_new, V_new), restriction(g, V_new, W_new)), + compose(restriction(g_inv, W_new, V_new), restriction(f_inv, V_new, U_new)) + ) +end + +@Markdown.doc """ +maximal_extension(G::Glueing) + +Given a glueing `X ↩ U ≅ V ↪ Y`, try to find the maximal extension to an open +subset `U' ⊃ U` in `X` and `V' ⊃ V` in `Y` so that the resulting scheme is separated. +""" +function maximal_extension(G::Glueing) + X = patches(G)[1] + Y = patches(G)[2] + f, g = glueing_morphisms(G) + f_ext = maximal_extension(X, Y, generic_fractions(f)) + g_ext = maximal_extension(Y, X, generic_fractions(g)) + f_ext = restriction(f_ext, preimage(f_ext, domain(g_ext)), domain(g_ext)) + g_ext = restriction(g_ext, preimage(g_ext, domain(f_ext)), domain(f_ext)) + return Glueing(X, Y, f_ext, g_ext) +end + +function ==(G::GlueingType, H::GlueingType) where {GlueingType<:Glueing} + if patches(G)[1] != patches(H)[1] + return G == inverse(H) + end + patches(G)[2] == patches(H)[2] || return false + glueing_morphisms(G) == glueing_morphisms(H) || return false + return true +end + +function glueing_type(X::SpecType) where {SpecType<:Spec} + return Glueing{SpecType, open_subset_type(SpecType), morphism_type(open_subset_type(SpecType), open_subset_type(SpecType))} +end + +function glueing_type(::Type{SpecType}) where {SpecType<:Spec} + return Glueing{SpecType, open_subset_type(SpecType), morphism_type(open_subset_type(SpecType), open_subset_type(SpecType))} +end + +function restriction(G::Glueing, X::SpecType, Y::SpecType; check::Bool=true) where {SpecType<:Spec} + U, V = glueing_domains(G) + f, g = glueing_morphisms(G) + if check + is_closed_embedding(intersect(X, ambient(U)), ambient(U)) || error("the scheme is not a closed in the ambient scheme of the open set") + is_closed_embedding(intersect(Y, ambient(V)), ambient(V)) || error("the scheme is not a closed in the ambient scheme of the open set") + end + return Glueing(X, Y, restriction(f, X, Y, check=check), restriction(g, Y, X, check=check), check=check) +end diff --git a/experimental/Schemes/ProjectiveSchemes.jl b/experimental/Schemes/ProjectiveSchemes.jl new file mode 100644 index 000000000000..2fd7afe1544e --- /dev/null +++ b/experimental/Schemes/ProjectiveSchemes.jl @@ -0,0 +1,751 @@ +import Oscar.AlgHom + +export ProjectiveScheme, base_ring, fiber_dimension, homogeneous_coordinate_ring, gens, getindex, affine_patch_type +export projective_scheme_type, affine_patch_type, base_ring_type, base_scheme_type, morphism_type +export projective_space, subscheme +export projection_to_base, affine_cone, set_base_scheme!, base_scheme, homogeneous_coordinates, homog_to_frac, as_covered_scheme, covered_projection_to_base, dehomogenize +export ProjectiveSchemeMor, domain, codomain, images_of_variables, map_on_affine_cones, is_well_defined, poly_to_homog, frac_to_homog_pair +export fiber_product, inclusion_map + +export == + +@Markdown.doc """ + ProjectiveScheme{CoeffRingType, CoeffRingElemType, RingType, RingElemType} + +Closed subschemes ``X ⊂ ℙʳ(A)`` of projective space of `fiber_dimension` ``r`` +over a ring of coefficients ``A`` of type `CoeffRingType` with elements of +type `CoeffRingElemType`. The subscheme ``X`` is given by means of a homogeneous +ideal ``I`` in the graded ring ``A[s₀,…,sᵣ]`` and the latter is of type +`RingType` with elements of type `RingElemType`. +""" +@attributes mutable struct ProjectiveScheme{CoeffRingType, CoeffRingElemType, RingType, RingElemType} + A::CoeffRingType # the base ring + r::Int # the relative dimension + S::RingType # A[s₀,…,sᵣ] + I::MPolyIdeal{RingElemType} # generators for the defining ideal + #TODO: Once MPolyIdeal is finally generic, use that instead of storing the generators. + + # fields used for caching + C::Spec # The affine cone of this scheme. + Y::Spec # the base scheme + projection_to_base::SpecMor + homog_coord::Vector # the homogeneous coordinates as functions on the affine cone + + function ProjectiveScheme(S::MPolyRing_dec) + #TODO: Check that all weights are equal to 1 + n = ngens(S)-1 + A = coefficient_ring(S) + I = ideal(S, [zero(S)]) + return new{typeof(A), elem_type(A), typeof(S), elem_type(S)}(A, n, S, I) + end + + function ProjectiveScheme(S::MPolyRing_dec, I::MPolyIdeal{T}) where {T<:RingElem} + for f in gens(I) + parent(f) == S || error("elements do not belong to the correct ring") + end + #TODO: Check that all weights are equal to 1 + n = ngens(S)-1 + A = coefficient_ring(S) + return new{typeof(A), elem_type(A), typeof(S), elem_type(S)}(A, n, S, I) + end + + function ProjectiveScheme(Q::MPolyQuo{MPolyElem_dec{T, AbstractAlgebra.Generic.MPoly{T}}}) where {T} + #TODO: Check that all weights are equal to 1 + S = base_ring(Q) + A = coefficient_ring(S) + I = gens(modulus(Q)) + r = ngens(S)-1 + return new{typeof(A), elem_type(A), typeof(S), elem_type(S)}(A, r, S, I) + end +end + +### type getters & constructors +projective_scheme_type(A::T) where {T<:AbstractAlgebra.Ring} = projective_scheme_type(typeof(A)) +projective_scheme_type(::Type{T}) where {T<:AbstractAlgebra.Ring} = +ProjectiveScheme{T, elem_type(T), mpoly_dec_ring_type(mpoly_ring_type(T)), mpoly_dec_type(mpoly_ring_type(T))} + +base_ring_type(P::ProjectiveScheme{S, T, U, V}) where {S, T, U, V} = S +base_ring_type(::Type{ProjectiveScheme{S, T, U, V}}) where {S, T, U, V} = S + +ring_type(P::ProjectiveScheme{S, T, U, V}) where {S, T, U, V} = U +ring_type(::Type{ProjectiveScheme{S, T, U, V}}) where {S, T, U, V} = U + +base_scheme_type(P::ProjectiveScheme{S, T, U, V}) where {S, T, U, V} = spec_type(S) +base_scheme_type(::Type{ProjectiveScheme{S, T, U, V}}) where {S, T, U, V} = spec_type(S) + +### type constructors + +# the type of a relative projective scheme over a given base scheme +projective_scheme_type(X::T) where {T<:Spec} = projective_scheme_type(ring_type(T)) +projective_scheme_type(::Type{T}) where {T<:Spec} = projective_scheme_type(ring_type(T)) + +# the type of the affine cone for a projective scheme +affine_cone_type(P::ProjectiveScheme{CRT}) where {CRT<:AbstractAlgebra.Ring} = spec_type(CRT) +affine_cone_type(::Type{ProjectiveScheme{CRT}}) where {CRT<:AbstractAlgebra.Ring} = spec_type(CRT) + +# again, this is the default assuming localizations at hypersurfaces +affine_cone_type(P::ProjectiveScheme{CRT}) where {CRT<:MPolyQuoLocalizedRing} = spec_type(CRT) +affine_cone_type(::Type{ProjectiveScheme{CRT}}) where {CRT<:MPolyQuoLocalizedRing} = spec_type(CRT) + +# Other localization types in the base will lead to mixed localizations +# **Warning:** the methods for taking products of multiplicative sets are not type-stable themselves! +# So the following is a heuristic for what should happen. +affine_cone_type( + P::ProjectiveScheme{MPolyQuoLocalizedRing{S, T, U, V, W}} + ) where { + S, T, U, V, W<:MPolyComplementOfPrimeIdeal + } = spec_type( + MPolyQuoLocalizedRing{ + S, T, U, V, + MPolyProductOfMultSets{ + S, T, U, V + } + } + ) +affine_cone_type( + ::Type{ProjectiveScheme{MPolyQuoLocalizedRing{S, T, U, V, W}}} + ) where { + S, T, U, V, W<:MPolyComplementOfPrimeIdeal + } = spec_type( + MPolyQuoLocalizedRing{ + S, T, U, V, + MPolyProductOfMultSets{ + S, T, U, V + } + } + ) +affine_cone_type( + P::ProjectiveScheme{MPolyQuoLocalizedRing{S, T, U, V, W}} + ) where { + S, T, U, V, W<:MPolyComplementOfKPointIdeal + } = spec_type( + MPolyQuoLocalizedRing{ + S, T, U, V, + MPolyProductOfMultSets{ + S, T, U, V + } + } + ) +affine_cone_type( + ::Type{ProjectiveScheme{MPolyQuoLocalizedRing{S, T, U, V, W}}} + ) where { + S, T, U, V, W<:MPolyComplementOfKPointIdeal + } = spec_type( + MPolyQuoLocalizedRing{ + S, T, U, V, + MPolyProductOfMultSets{ + S, T, U, V + } + } + ) + + +@Markdown.doc """ + base_ring(X::ProjectiveScheme) + +On ``X ⊂ ℙʳ(A)`` this returns ``A``. +""" +base_ring(P::ProjectiveScheme) = P.A + +@Markdown.doc """ + base_scheme(X::ProjectiveScheme{CRT, CRET, RT, RET}) where {CRT<:MPolyQuoLocalizedRing, CRET, RT, RET} + +Return the base scheme ``Y`` for ``X ⊂ ℙʳ×ₖ Y → Y`` with ``Y`` defined over a field ``𝕜``. +""" +function base_scheme(X::ProjectiveScheme{CRT, CRET, RT, RET}) where {CRT<:Union{MPolyRing, MPolyQuoLocalizedRing}, CRET, RT, RET} + if !isdefined(X, :Y) + X.Y = Spec(base_ring(X)) + end + return X.Y +end + +function set_base_scheme!(P::ProjectiveScheme{CRT, CRET, RT, RET}, X::Spec) where {CRT<:MPolyQuoLocalizedRing, CRET, RT, RET} + OO(X) == base_ring(P) || error("schemes are not compatible") + P.Y = X + return P +end + + +function projection_to_base(X::ProjectiveScheme{CRT, CRET, RT, RET}) where {CRT<:MPolyRing, CRET, RT, RET} + if !isdefined(X, :projection_to_base) + affine_cone(X) + end + return X.projection_to_base +end + +function projection_to_base(X::ProjectiveScheme{CRT, CRET, RT, RET}) where {CRT<:MPolyQuoLocalizedRing, CRET, RT, RET} + if !isdefined(X, :projection_to_base) + affine_cone(X) + end + return X.projection_to_base +end + +@Markdown.doc """ + fiber_dimension(X::ProjectiveScheme) + +On ``X ⊂ ℙʳ(A)`` this returns ``r``. +""" +fiber_dimension(P::ProjectiveScheme) = P.r + +@Markdown.doc """ + homogeneous_coordinate_ring(X::ProjectiveScheme) + +On ``X ⊂ ℙʳ(A)`` this returns ``A[s₀,…,sᵣ]``. +""" +homogeneous_coordinate_ring(P::ProjectiveScheme) = P.S + +@Markdown.doc """ + homogeneous_coordinates(X::ProjectiveScheme) + +On ``X ⊂ ℙʳ(A)`` this returns a vector with the homogeneous +coordinates ``[s₀,…,sᵣ]`` as entries where each one of the +``sᵢ`` is a function on the `affine cone` of ``X``. +""" +function homogeneous_coordinates(P::ProjectiveScheme) + if !isdefined(P, :homog_coord) + affine_cone(P) + end + return P.homog_coord +end + +homogeneous_coordinate(P::ProjectiveScheme, i::Int) = homogeneous_coordinates(P)[i] + +@Markdown.doc """ + defining_ideal(X::ProjectiveScheme) + +On ``X ⊂ ℙʳ(A)`` this returns the homogeneous +ideal ``I ⊂ A[s₀,…,sᵣ]`` defining ``X``. +""" +defining_ideal(X::ProjectiveScheme) = X.I + +function Base.show(io::IO, P::ProjectiveScheme) + print(io, "subscheme of ℙ^$(fiber_dimension(P))_{$(base_ring(P))} defined as the zero locus of $(defining_ideal(P))") +end + +original_ring(S::MPolyRing_dec) = S.R + +function affine_patch_type(X::ProjectiveScheme{CRT, CRET, RT, RET}) where {CRT<:AbstractAlgebra.Ring, CRET, RT, RET} + return Spec{typeof(base_ring(X)), + elem_type(base_ring(X)), + typeof(original_ring(homogeneous_coordinate_ring(X))), + elem_type(original_ring(homogeneous_coordinate_ring(X))), + MPolyPowersOfElement{ + typeof(base_ring(X)), + elem_type(base_ring(X)), + typeof(original_ring(homogeneous_coordinate_ring(X))), + elem_type(original_ring(homogeneous_coordinate_ring(X))) + } + } +end + +function affine_patch_type(X::ProjectiveScheme{CRT, CRET, RT, RET}) where {CRT<:MPolyRing, CRET, RT, RET} + return Spec{typeof(coefficient_ring(base_ring(X))), + elem_type(coefficient_ring(base_ring(X))), + typeof(base_ring(X)), + elem_type(base_ring(X)), + MPolyPowersOfElement{ + typeof(coefficient_ring(base_ring(X))), + elem_type(coefficient_ring(base_ring(X))), + typeof(original_ring(homogeneous_coordinate_ring(X))), + elem_type(original_ring(homogeneous_coordinate_ring(X))) + } + } +end + +# TODO: This only supports the localizations at hypersurfaces for now. +# In the future this will have to be modified as in the commented line; +# but then caching on the type should be used to avoid the computations of +# the product of multiplicative sets. +function affine_patch_type(X::ProjectiveScheme{CRT, CRET, RT, RET}) where {CRT<:MPolyQuoLocalizedRing, CRET, RT, RET} + Y = base_scheme(X) + return Spec{typeof(coefficient_ring(base_ring(OO(Y)))), + elem_type(coefficient_ring(base_ring(OO(Y)))), + typeof(base_ring(OO(Y))), + elem_type(base_ring(OO(Y))), + MPolyPowersOfElement{ + typeof(coefficient_ring(base_ring(OO(Y)))), + elem_type(coefficient_ring(base_ring(OO(Y)))), + typeof(base_ring(OO(Y))), + elem_type(base_ring(OO(Y))) + } + #typeof(inverted_set(OO(affine_cone(X)))*Localization(OO(affine_cone(X)), prod(lifted_numerator.(homogeneous_coordinates(X))))) + } +end + +function subscheme(P::ProjectiveScheme, f::RingElemType) where {RingElemType<:MPolyElem_dec} + S = homogeneous_coordinate_ring(P) + parent(f) == S || error("ring element does not belong to the correct ring") + Q = ProjectiveScheme(S, ideal(S, vcat(gens(defining_ideal(P)), [f]))) + if isdefined(P, :Y) + set_base_scheme!(Q, base_scheme(P)) + end + return Q +end + +function subscheme(P::ProjectiveScheme, f::Vector{RingElemType}) where {RingElemType<:MPolyElem_dec} + S = homogeneous_coordinate_ring(P) + length(f) == 1 && return P #TODO: Replace P by an honest copy! + for i in 1:length(f) + parent(f[i]) == S || error("ring element does not belong to the correct ring") + end + Q = ProjectiveScheme(S, ideal(S, vcat(gens(defining_ideal(P)),f))) + if isdefined(P, :Y) + set_base_scheme!(Q, base_scheme(P)) + end + return Q +end + +function subscheme(P::ProjectiveScheme, I::MPolyIdeal{T}) where {T<:RingElem} + S = homogeneous_coordinate_ring(P) + base_ring(I) == S || error("ideal does not belong to the correct ring") + Q = ProjectiveScheme(S, ideal(S, vcat(gens(I), gens(defining_ideal(P))))) + if isdefined(P, :Y) + set_base_scheme!(Q, base_scheme(P)) + end + return Q +end + +function projective_space(A::CoeffRingType, var_symb::Vector{Symbol}) where {CoeffRingType<:Ring} + n = length(var_symb) + R, _ = PolynomialRing(A, var_symb) + S, _ = grade(R, [1 for i in 1:n ]) + I = ideal(S, [zero(S)]) + return ProjectiveScheme(S, I) +end + +projective_space(A::CoeffRingType, var_names::Vector{String}) where {CoeffRingType<:Ring} = projective_space(A, Symbol.(var_names)) + + +function projective_space(A::CoeffRingType, r::Int; var_name::String="s") where {CoeffRingType<:Ring} + R, _ = PolynomialRing(A, [var_name*"$i" for i in 0:r]) + S, _ = grade(R, [1 for i in 0:r ]) + I = ideal(S, [zero(S)]) + return ProjectiveScheme(S, I) +end + +function projective_space(W::Spec, r::Int; var_name::String="s") + P = projective_space(OO(W), r, var_name=var_name) + set_base_scheme!(P, W) + return P +end + +function projective_space(W::Spec, var_names::Vector{Symbol}) + P = projective_space(OO(W), var_names) + set_base_scheme!(P, W) + return P +end + +function projective_space(W::Spec, var_names::Vector{String}) + P = projective_space(OO(W), var_names) + set_base_scheme!(P, W) + return P +end + +@Markdown.doc """ + homog_to_frac(X::ProjectiveScheme) + +Returns a map that converts a polynomial in the +`homogeneous_coordinate_ring` of `X` into a function on the +`affine_cone` of `X`. +""" +function homog_to_frac(X::ProjectiveScheme) + if !has_attribute(X, :homog_to_frac) + affine_cone(X) + end + return get_attribute(X, :homog_to_frac) +end + +@Markdown.doc """ + poly_to_homog(X::ProjectiveScheme) + +Returns a map that converts an element of the `base_ring` of +ring of functions `OO` of the `affine_cone` of `X` into +an element of the `homogeneous_coordinate_ring` of `X`. +""" +function poly_to_homog(X::ProjectiveScheme) + if !has_attribute(X, :poly_to_homog) + affine_cone(X) + end + return get_attribute(X, :poly_to_homog) +end + +@Markdown.doc """ + function frac_to_homog_pair(X::ProjectiveScheme) + +Returns a map that converts an element ``f = p/q`` of the ring of +functions `OO` of the `affine_cone` of `X` into a pair +``(a, b)`` of elements of the `homogeneous_coordinate_ring` of `X` +corresponding to ``p`` and ``q``, respectively. +""" +function frac_to_homog_pair(X::ProjectiveScheme) + if !has_attribute(X, :frac_to_homog_pair) + affine_cone(X) + end + return get_attribute(X, :frac_to_homog_pair) +end + + +### This is a temporary fix that needs to be addressed in AbstractAlgebra, issue #1105 +Generic.ordering(S::MPolyRing_dec) = :lex + +@Markdown.doc """ + affine_cone(X::ProjectiveScheme) + +On ``X ⊂ ℙʳ(𝕜)`` this returns the affine cone ``C(X)⊂ 𝕜ʳ⁺¹`` and similar +in the relative situation. +""" +function affine_cone(X::ProjectiveScheme{CRT, CRET, RT, RET}) where {CRT<:MPolyRing, CRET, RT, RET} + if !isdefined(X, :C) + A = base_ring(X) + Y = Spec(A) + X.Y = Y + kk = base_ring(A) + F = affine_space(kk, symbols(homogeneous_coordinate_ring(X))) + C, pr_fiber, pr_base = product(F, Y) + X.homog_coord = lift.([pullback(pr_fiber)(u) for u in gens(OO(F))]) + + S = homogeneous_coordinate_ring(X) + # use the new mapping types for polynomial rings. + inner_help_map = hom(A, OO(C), [pullback(pr_base)(x) for x in gens(OO(Y))]) + help_map = hom(S, OO(C), inner_help_map, [pullback(pr_fiber)(y) for y in gens(OO(F))]) + + # use the map to convert ideals: + #I = ideal(OO(C), [help_map(g) for g in gens(defining_ideal(X))]) + I = help_map(defining_ideal(X)) + CX = subscheme(C, I) + set_attribute!(X, :affine_cone, CX) + X.C = get_attribute(X, :affine_cone) + pr_base_res = restrict(pr_base, CX, Y, check=false) + pr_fiber_res = restrict(pr_fiber, CX, F, check=false) + + # store the various conversion maps + set_attribute!(X, :homog_to_frac, + hom(S, OO(CX), + hom(A, OO(CX), [pullback(pr_base_res)(x) for x in gens(OO(Y))]), + [pullback(pr_fiber_res)(y) for y in gens(OO(F))] + ) + ) + pth = hom(base_ring(OO(CX)), S, vcat(gens(S), S.(gens(A)))) + set_attribute!(X, :poly_to_homog, pth) + set_attribute!(X, :frac_to_homog_pair, (f -> (pth(lifted_numerator(OO(CX)(f))), pth(lifted_denominator(OO(CX)(f)))))) + X.projection_to_base = pr_base_res + end + return X.C +end + +function (f::MPolyAnyMap{<:MPolyRing, <:AbstractAlgebra.NCRing})(I::MPolyIdeal) + return ideal(codomain(f), [f(g) for g in gens(I)]) +end + +function affine_cone(X::ProjectiveScheme{CRT, CRET, RT, RET}) where {CRT<:MPolyQuoLocalizedRing, CRET, RT, RET} + if !isdefined(X, :C) + A = base_ring(X) + Y = base_scheme(X) + R = base_ring(A) + kk = coefficient_ring(R) + F = affine_space(kk, symbols(homogeneous_coordinate_ring(X))) + C, pr_fiber, pr_base = product(F, Y) + X.homog_coord = lift.([pullback(pr_fiber)(u) for u in gens(OO(F))]) + S = homogeneous_coordinate_ring(X) + + # store the various conversion maps + help_map = hom(S, OO(C), + (x -> pullback(pr_base)(x)), + [pullback(pr_fiber)(y) for y in gens(OO(F))] + ) + + I = help_map(defining_ideal(X)) + CX = subscheme(C, I) + pr_base_res = restrict(pr_base, CX, Y, check=false) + pr_fiber_res = restrict(pr_fiber, CX, F, check=false) + + set_attribute!(X, :homog_to_frac, + hom(S, OO(CX), + pullback(pr_base_res), + [pullback(pr_fiber_res)(y) for y in gens(OO(F))] + ) + ) + pth = hom(base_ring(OO(CX)), S, vcat(gens(S), S.(gens(A)))) + set_attribute!(X, :poly_to_homog, pth) + set_attribute!(X, :frac_to_homog_pair, (f -> (pth(lifted_numerator(OO(CX)(f))), pth(lifted_numerator(OO(CX)(f)))))) + X.C = CX + X.projection_to_base = pr_base_res + end + return X.C +end + +function affine_cone(X::ProjectiveScheme{CRT, CRET, RT, RET}) where {CRT<:AbstractAlgebra.Ring, CRET, RT, RET} + if !isdefined(X, :C) + kk = base_ring(X) + C = affine_space(kk, symbols(homogeneous_coordinate_ring(X))) + X.homog_coord = gens(OO(C)) + S = homogeneous_coordinate_ring(X) + help_map = hom(S, OO(C), gens(OO(C))) + I = help_map(defining_ideal(X)) + CX = subscheme(C, I) + + # store the various conversion maps + set_attribute!(X, :homog_to_frac, hom(S, OO(CX), gens(OO(CX)))) + pth = hom(base_ring(OO(CX)), S, gens(S)) + set_attribute!(X, :poly_to_homog, pth) + set_attribute!(X, :frac_to_homog_pair, (f -> (pth(lifted_numerator(OO(CX)(f))), pth(lifted_numerator(OO(CX)(f)))))) + X.C = CX + end + return X.C +end + +@Markdown.doc """ + ProjectiveSchemeMor + +A morphism of projective schemes + + ℙˢ(B) ℙʳ(A) + ∪ ∪ + P → Q + ↓ ↓ + Spec(B) → Spec(A) + +given by means of a commutative diagram of homomorphisms of +graded rings + + A[v₀,…,vᵣ] → B[u₀,…,uₛ] + ↑ ↑ + A → B + +If no morphism `A → B` of the base rings is specified, then +both ``P`` and ``Q`` are assumed to be defined in relative projective +space over the same ring with the identity on the base. +""" +mutable struct ProjectiveSchemeMor{ + DomainType<:ProjectiveScheme, + CodomainType<:ProjectiveScheme, + PullbackType + } + domain::DomainType + codomain::CodomainType + pullback::PullbackType + + #fields for caching + map_on_base_schemes::SpecMor + map_on_affine_cones::SpecMor + + function ProjectiveSchemeMor( + P::DomainType, + Q::CodomainType, + f::PullbackType; + check::Bool=true + ) where {DomainType<:ProjectiveScheme, CodomainType<:ProjectiveScheme, PullbackType<:Map} + T = homogeneous_coordinate_ring(P) + S = homogeneous_coordinate_ring(Q) + (S === domain(f) && T === codomain(f)) || error("pullback map incompatible") + if check + #TODO: Check map on ideals (not available yet) + end + return new{DomainType, CodomainType, PullbackType}(P, Q, f) + end +end + +### type getters +morphism_type(P::S, Q::T) where {S<:ProjectiveScheme, T<:ProjectiveScheme} = morphism_type(S, T) +morphism_type(::Type{S}, ::Type{T}) where {S<:ProjectiveScheme, T<:ProjectiveScheme} = ProjectiveSchemeMor{S, T, MPolyAnyMap{ring_type(T), ring_type(S), morphism_type(base_ring_type(T), base_ring_type(S)), elem_type(ring_type(T))}} + +morphism_type(P::S) where {S<:ProjectiveScheme} = morphism_type(S, S) +morphism_type(::Type{S}) where {S<:ProjectiveScheme} = morphism_type(S, S) + +### getters +domain(phi::ProjectiveSchemeMor) = phi.domain +codomain(phi::ProjectiveSchemeMor) = phi.codomain +pullback(phi::ProjectiveSchemeMor) = phi.pullback +base_ring_morphism(phi::ProjectiveSchemeMor) = coefficient_map(pullback(phi)) + +### additional constructors +function ProjectiveSchemeMor(X::T, Y::T, a::Vector{RET}) where {T<:ProjectiveScheme, RET<:MPolyElem_dec} + base_ring(X) === base_ring(Y) || error("projective schemes must be defined over the same base ring") + Q = homogeneous_coordinate_ring(X) + P = homogeneous_coordinate_ring(Y) + return ProjectiveSchemeMor(X, Y, hom(P, Q, a)) +end + +# in case we have honest base schemes, also make the map of schemes available +function base_map(phi::ProjectiveSchemeMor{<:ProjectiveScheme{<:MPolyQuoLocalizedRing}}) + if !isdefined(phi, :map_on_base_schemes) + phi.map_on_base_schemes = SpecMor(base_scheme(domain(phi)), base_scheme(codomain(phi)), coefficient_map(pullback(phi))) + end + return phi.map_on_base_schemes::morphism_type(affine_patch_type(domain(phi)), affine_patch_type(codomain(phi))) +end + +function map_on_affine_cones(phi::ProjectiveSchemeMor{<:ProjectiveScheme{<:MPolyQuoLocalizedRing}}) + if !isdefined(phi, :map_on_affine_cones) + A = base_ring(domain(phi)) + S = homogeneous_coordinate_ring(codomain(phi)) + T = homogeneous_coordinate_ring(domain(phi)) + P = domain(phi) + Q = codomain(phi) + pb_P = pullback(projection_to_base(P)) + pb_Q = pullback(projection_to_base(Q)) + imgs_base = pb_P.(gens(A)) + imgs_fiber = [homog_to_frac(P)(g) for g in pullback(phi).(gens(S))] + phi.map_on_affine_cones = SpecMor(affine_cone(P), affine_cone(Q), vcat(imgs_fiber, imgs_base)) + end + return phi.map_on_affine_cones +end + +function map_on_affine_cones(phi::ProjectiveSchemeMor{<:ProjectiveScheme{<:MPolyRing}}) + if !isdefined(phi, :map_on_affine_cones) + Y = base_scheme(domain(phi)) + A = OO(Y) + S = homogeneous_coordinate_ring(codomain(phi)) + T = homogeneous_coordinate_ring(domain(phi)) + P = domain(phi) + Q = codomain(phi) + pb_P = pullback(projection_to_base(P)) + pb_Q = pullback(projection_to_base(Q)) + imgs_base = pb_P.(gens(A)) + imgs_fiber = [homog_to_frac(P)(g) for g in pullback(phi).(gens(S))] + phi.map_on_affine_cones = SpecMor(affine_cone(P), affine_cone(Q), vcat(imgs_fiber, imgs_base)) + end + return phi.map_on_affine_cones +end + +function map_on_affine_cones(phi::ProjectiveSchemeMor{<:ProjectiveScheme{<:AbstractAlgebra.Ring}}) + if !isdefined(phi, :map_on_affine_cones) + S = homogeneous_coordinate_ring(codomain(phi)) + T = homogeneous_coordinate_ring(domain(phi)) + P = domain(phi) + Q = codomain(phi) + imgs_fiber = [homog_to_frac(P)(g) for g in pullback(phi).(gens(S))] + phi.map_on_affine_cones = SpecMor(affine_cone(P), affine_cone(Q), imgs_fiber) + end + return phi.map_on_affine_cones +end + +function is_well_defined(phi::ProjectiveSchemeMor) + CP = affine_cone(domain(phi)) + CQ = affine_cone(codomain(phi)) + return issubset(CP, preimage(map_on_affine_cones(phi), CQ)) +end + +function compose(f::T, g::T) where {T<:ProjectiveSchemeMor} + return ProjectiveSchemeMor(domain(f), codomain(g), compose(pullback(g), pullback(f))) +end + +function ==(f::ProjectiveSchemeMor, g::ProjectiveSchemeMor) + domain(f) === domain(g) || return false + codomain(f) === codomain(g) || return false + for s in gens(homogeneous_coordinate_ring(codomain(f))) + pullback(f)(s) - pullback(g)(s) in defining_ideal(domain(f)) || return false + end + return true +end + +function ==(f::ProjectiveSchemeMor{<:ProjectiveScheme{<:MPolyQuoLocalizedRing}}, + g::ProjectiveSchemeMor{<:ProjectiveScheme{<:MPolyQuoLocalizedRing}}) + domain(f) === domain(g) || return false + codomain(f) === codomain(g) || return false + return map_on_affine_cones(f) == map_on_affine_cones(g) +end + +### additional constructors + +function fiber_product(f::SpecMor, P::ProjectiveScheme{<:MPolyQuoLocalizedRing}) + codomain(f) == base_scheme(P) || error("codomain and base_scheme are incompatible") + X = domain(f) + Y = codomain(f) + Q_ambient = projective_space(X, symbols(homogeneous_coordinate_ring(P))) + help_map = hom(homogeneous_coordinate_ring(P), + homogeneous_coordinate_ring(Q_ambient), + pullback(f), + gens(homogeneous_coordinate_ring(Q_ambient)) + ) + I = help_map(defining_ideal(P)) + Q = subscheme(Q_ambient, I) + return Q, ProjectiveSchemeMor(Q, P, + hom(homogeneous_coordinate_ring(P), + homogeneous_coordinate_ring(Q), + pullback(f), + gens(homogeneous_coordinate_ring(Q)) + ) + ) +end + +fiber_product(X::Spec, P::ProjectiveScheme{<:MPolyQuoLocalizedRing}) = fiber_product(inclusion_map(X, base_scheme(P)), P) + +### canonical map constructors + +@Markdown.doc """ + inclusion_map(P::T, Q::T) + +Assuming that ``P ⊂ Q`` is a subscheme, both proper over an inclusion of +their base schemes, this returns the associated `ProjectiveSchemeMor`. +""" +function inclusion_map(P::T, Q::T) where {T<:ProjectiveScheme{<:MPolyQuoLocalizedRing}} + X = base_scheme(P) + Y = base_scheme(Q) + f = inclusion_map(X, Y) # will throw if X and Y are not compatible + return ProjectiveSchemeMor(P, Q, + hom(homogeneous_coordinate_ring(Q), + homogeneous_coordinate_ring(P), + pullback(f), + gens(homogeneous_coordinate_ring(P)) + ) + ) +end + +function inclusion_map(P::T, Q::T) where {T<:ProjectiveScheme{<:AbstractAlgebra.Ring}} + A = base_ring(Q) + B = base_ring(P) + A === B || error("can not compare schemes for non-equal base rings") # TODO: Extend by check for canonical maps, once they are available + return ProjectiveSchemeMor(P, Q, + hom(homogeneous_coordinate_ring(Q), + homogeneous_coordinate_ring(P), + gens(homogeneous_coordinate_ring(P)) + ) + ) +end + +function as_covered_scheme(P::ProjectiveScheme) + if !has_attribute(P, :as_covered_scheme) + C = standard_covering(P) + X = CoveredScheme(C) + set_attribute!(P, :as_covered_scheme, X) + end + return get_attribute(P, :as_covered_scheme) +end + +function covered_projection_to_base(X::ProjectiveScheme{<:MPolyQuoLocalizedRing}) + if !has_attribute(X, :covered_projection_to_base) + C = standard_covering(X) + end + return get_attribute(X, :covered_projection_to_base) # TODO: establish type assertion here! +end + + +function dehomogenize( + X::ProjectiveScheme{CRT}, + i::Int + ) where { + CRT<:MPolyQuoLocalizedRing + } + i in 0:fiber_dimension(X) || error("the given integer is not in the admissible range") + S = homogeneous_coordinate_ring(X) + C = standard_covering(X) + U = C[i+1] + p = covered_projection_to_base(X) + s = vcat(gens(OO(U))[1:i], [one(OO(U))], gens(OO(U))[i+1:fiber_dimension(X)]) + return hom(S, OO(U), pullback(p[U]), s) +end + +function dehomogenize( + X::ProjectiveScheme{CRT}, + i::Int + ) where { + CRT<:AbstractAlgebra.Ring + } + i in 0:fiber_dimension(X) || error("the given integer is not in the admissible range") + S = homogeneous_coordinate_ring(X) + C = standard_covering(X) + U = C[i+1] + s = vcat(gens(OO(U))[1:i], [one(OO(U))], gens(OO(U))[i+1:fiber_dimension(X)]) + return hom(S, OO(U), s) +end + diff --git a/experimental/Schemes/SpecOpen.jl b/experimental/Schemes/SpecOpen.jl index ce128d45d28c..0fc732fc51f6 100644 --- a/experimental/Schemes/SpecOpen.jl +++ b/experimental/Schemes/SpecOpen.jl @@ -1,13 +1,15 @@ -export SpecOpen, ambient, gens, complement, npatches, affine_patches, intersections, name, intersect, issubset, closure, find_non_zero_divisor, is_non_zero_divisor, is_dense +export SpecOpen, ambient, gens, complement, npatches, affine_patches, intersections, name, intersect, issubset, closure, find_non_zero_divisor, is_non_zero_divisor, is_dense, open_subset_type, ambient_type, is_canonically_isomorphic -export StructureSheafRing, scheme, domain, OO +export SpecOpenRing, scheme, domain, OO, structure_sheaf_ring_type, isdomain_type, isexact_type -export StructureSheafElem, domain, restrictions, patches, restrict, npatches +export SpecOpenRingElem, domain, restrictions, patches, restrict, npatches, structure_sheaf_elem_type export SpecOpenMor, maps_on_patches, restriction, identity_map, preimage, generic_fractions, pullback, maximal_extension +export adjoint + @Markdown.doc """ - SpecOpen{BRT, BRET, RT, RET, MST} <: Scheme{BRT, BRET} + SpecOpen{SpecType, BRT, BRET} <: Scheme{BRT, BRET} Zariski open subset ``U`` of an affine scheme ``X = Spec(R)``. This stores a list of generators ``f₁,…,fᵣ`` of an ideal @@ -19,32 +21,45 @@ The type parameters stand for the following: The ring ``R = (𝕜[x₁,…,xₙ]/I)[S⁻¹]`` is a localization of the quotient of a polynomial ring and - * BRT is the type of the coefficient ring ``𝕜``; - * BRET is the type of the elements of ``𝕜``; - * RT is the type of the polynomial ring ``𝕜[x₁,…,xₙ]``; - * RET is the type of the elements of ``𝕜[x₁,…,xₙ]``; - * MST is the type of the multiplicative set ``S``. + * `SpecType` is the type of the affine scheme ``X`` of which +this is an open subset; + * `BRT` is the type of the coefficient ring ``𝕜``; + * `BRET` is the type of the elements of ``𝕜``. """ -@attributes mutable struct SpecOpen{BRT, BRET, RT, RET, MST} <: Scheme{BRT, BRET} - X::Spec{BRT, BRET, RT, RET, MST} # the ambient scheme - gens::Vector{RET} # a list of functions defining the complement of the open subset +@attributes mutable struct SpecOpen{SpecType, BRT, BRET} <: Scheme{BRT, BRET} + X::SpecType # the ambient scheme + gens::Vector # a list of functions defining the complement of the open subset # fields used for caching name::String - patches::Vector{Spec{BRT, BRET, RT, RET, MST}} - intersections::Dict{Tuple{Int, Int}, Spec{BRT, BRET, RT, RET, MST}} - complement::Spec{BRT, BRET, RT, RET, MST} - - function SpecOpen(X::Spec{BRT, BRET, RT, RET, MST}, f::Vector{RET}; name::String="") where {BRT, BRET, RT, RET, MST} + patches::Vector{SpecType} + intersections::Dict{Tuple{Int, Int}, SpecType} + complement::SpecType + + function SpecOpen( + X::SpecType, + f::Vector{RET}; + name::String="", + check::Bool=true + ) where {SpecType<:Spec, RET<:RingElem} for a in f parent(a) == base_ring(OO(X)) || error("element does not belong to the correct ring") + if check + !isempty(X) && iszero(OO(X)(a)) && error("generators must not be zero") + end end - U = new{BRT, BRET, RT, RET, MST}(X, f) + U = new{SpecType, typeof(base_ring(X)), elem_type(base_ring(X))}(X, f) length(name) > 0 && set_name!(U, name) return U end end +open_subset_type(X::Spec) = SpecOpen{typeof(X), typeof(coefficient_ring(base_ring(OO(X)))), elem_type(coefficient_ring(base_ring(OO(X))))} +open_subset_type(::Type{Spec{BRT, BRET, RT, RET, MST}}) where {BRT, BRET, RT, RET, MST} = SpecOpen{Spec{BRT, BRET, RT, RET, MST}, BRT, BRET} + +ambient_type(U::SpecOpen{SpecType, BRT, BRET}) where {SpecType<:Spec, BRT, BRET} = SpecType +ambient_type(::Type{SpecOpen{SpecType, BRT, BRET}}) where {SpecType<:Spec, BRT, BRET} = SpecType + @Markdown.doc """ ambient(U::SpecOpen) @@ -65,7 +80,7 @@ npatches(U::SpecOpen) = length(U.gens) Return the generators ``[f₁,…,fᵣ]`` stored for the description of the complement of ``U``. """ -gens(U::SpecOpen) = U.gens +gens(U::SpecOpen) = U.gens::Vector{elem_type(base_ring(OO(ambient(U))))} @Markdown.doc """ affine_patch(U::SpecOpen, i::Int) @@ -79,6 +94,13 @@ of ``U``. This function can also be called using the affine_patch(U::SpecOpen, i::Int) = affine_patches(U)[i] getindex(U::SpecOpen, i::Int) = affine_patches(U)[i] +function getindex(U::SpecOpen, X::Spec) + for i in 1:npatches(U) + X == U[i] && return i + end + error("scheme $X not found among the open patches in $U") +end + function Base.show(io::IO, U::SpecOpen) if isdefined(U, :name) print(io, name(U)) @@ -110,12 +132,13 @@ function complement(X::T, Z::T) where {T<:Spec} return SpecOpen(X, modulus(OO(Z))) end -SpecOpen(X::Spec) = SpecOpen(X, [one(base_ring(OO(X)))]) +SpecOpen(X::Spec) = SpecOpen(X, [one(base_ring(OO(X)))], check=false) function complement(U::SpecOpen) if !isdefined(U, :complement) - I = radical(saturated_ideal(ideal(localized_ring(OO(ambient(U))), gens(U)))) - U.complement = subscheme(ambient(U), I) + #I = radical(saturated_ideal(ideal(localized_ring(OO(ambient(U))), gens(U)))) + #U.complement = subscheme(ambient(U), I) + U.complement = subscheme(ambient(U), gens(U)) end return U.complement end @@ -183,12 +206,12 @@ function intersect( V::SpecOpen ) X = ambient(U) - X == ambient(V) || error("ambient schemes do not coincide") + is_canonically_isomorphic(X, ambient(V)) || error("ambient schemes do not coincide") return SpecOpen(X, [a*b for a in gens(U) for b in gens(V)]) end function Base.union(U::T, V::T) where {T<:SpecOpen} - ambient(U) == ambient(V) || error("the two open sets do not lay in the same ambient scheme") + is_canonically_isomorphic(ambient(U), ambient(V)) || error("the two open sets do not lay in the same ambient scheme") return SpecOpen(ambient(U), vcat(gens(U), gens(V))) end @@ -211,25 +234,34 @@ end function issubset(U::T, V::T) where {T<:SpecOpen} base_ring(OO(ambient(U))) === base_ring(OO(ambient(V))) || return false - return issubset(complement(intersect(V, ambient(U))), complement(U)) + W = V + issubset(W, ambient(U)) || (W = intersect(V, ambient(U))) + Z = complement(W) + # perform an implicit radical membership test (Rabinowitsch) that is way more + # efficient than computing radicals. + for g in gens(U) + isempty(hypersurface_complement(Z, g)) || return false + end + return true + #return issubset(complement(intersect(V, ambient(U))), complement(U)) end -function ==(U::T, V::T) where {T<:SpecOpen} +function is_canonically_isomorphic(U::T, V::T) where {T<:SpecOpen} return issubset(U, V) && issubset(V, U) end -function ==( +function is_canonically_isomorphic( U::SpecOpen, Y::Spec ) return issubset(U, Y) && issubset(Y, U) end -function ==( +function is_canonically_isomorphic( Y::Spec, U::SpecOpen ) - return U == Y + return is_canonically_isomorphic(U, Y) end @Markdown.doc """ @@ -262,85 +294,109 @@ end @Markdown.doc """ - StructureSheafRing{BRT, BRET, RT, RET, MST} + SpecOpenRing{SpecType, OpenType} The ring of regular functions ``𝒪(X, U)`` on an open subset ``U`` of an affine scheme ``X``. + + * `SpecType` is the type of the affine scheme ``X`` on which +this sheaf is defined; + * `OpenType` is the type of the (Zariski) open subsets of ``U``. """ -mutable struct StructureSheafRing{BRT, BRET, RT, RET, MST} - scheme::Spec{BRT, BRET, RT, RET, MST} - domain::SpecOpen{BRT, BRET, RT, RET, MST} - - function StructureSheafRing( - X::Spec{BRT, BRET, RT, RET, MST}, - U::SpecOpen{BRT, BRET, RT, RET, MST} - ) where {BRT, BRET, RT, RET, MST} +mutable struct SpecOpenRing{SpecType, OpenType} <: Ring + scheme::SpecType + domain::OpenType + + function SpecOpenRing( + X::SpecType, + U::OpenType + ) where {SpecType<:Spec, OpenType<:SpecOpen} issubset(U, X) || error("open set does not lay in the scheme") - return new{BRT, BRET, RT, RET, MST}(X, U) + return new{SpecType, OpenType}(X, U) end end +SpecOpenRing(U::SpecOpen) = SpecOpenRing(ambient(U), U) + +spec_open_ring_type(::Type{T}) where {T<:Spec} = SpecOpenRing{T, open_subset_type(T)} +spec_open_ring_type(X::Spec) = spec_open_ring_type(typeof(X)) + @Markdown.doc """ - scheme(R::StructureSheafRing) + scheme(R::SpecOpenRing) The ring ``R = 𝒪(X, U)`` belongs to a sheaf of rings ``𝒪(X, -)`` and this returns the scheme ``X`` on which ``𝒪`` is defined. """ -scheme(R::StructureSheafRing) = R.scheme +scheme(R::SpecOpenRing) = R.scheme @Markdown.doc """ - domain(R::StructureSheafRing) + domain(R::SpecOpenRing) For a ring ``R = 𝒪(X, U)``, return ``U``. """ -domain(R::StructureSheafRing) = R.domain +domain(R::SpecOpenRing) = R.domain -OO(U::SpecOpen) = StructureSheafRing(ambient(U), U) -OO(X::Spec, U::SpecOpen) = StructureSheafRing(X, U) +OO(U::SpecOpen) = SpecOpenRing(ambient(U), U) +OO(X::Spec, U::SpecOpen) = SpecOpenRing(X, U) -function ==(R::T, S::T) where {T<:StructureSheafRing} - scheme(R) == scheme(S) || return false - domain(S) == domain(R) || return false - return true +function ==(R::T, S::T) where {T<:SpecOpenRing} + return is_canonically_isomorphic(scheme(R), scheme(S)) && is_canonically_isomorphic(domain(R), domain(S)) end -elem_type(R::StructureSheafRing) where {BRT, BRET, RT, RET, MST} = Type{StructureSheafElem{BRT, BRET, RT, RET, MST}} - @Markdown.doc """ - StructureSheafElem{BRT, BRET, RT, RET, MST} + SpecOpenRingElem{SpecOpenType, RestrictionType} An element ``f ∈ 𝒪(X, U)`` of the ring of regular functions on an open set ``U`` of an affine scheme ``X``. + + * `SpecOpenType` is the type of the open sets ``U`` of ``X``; + * `RestrictionType` is the type of the restrictions of ``f`` to +the affine patches of ``U``. """ -mutable struct StructureSheafElem{BRT, BRET, RT, RET, MST} - domain::SpecOpen{BRT, BRET, RT, RET, MST} - restrictions::Vector{MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, MST}} - - function StructureSheafElem( - U::SpecOpen{BRT, BRET, RT, RET, MST}, - f::Vector{MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, MST}} - ) where {BRT, BRET, RT, RET, MST} +mutable struct SpecOpenRingElem{SpecOpenRingType, RestrictionType} <: RingElem + parent::SpecOpenRingType + restrictions::Vector{RestrictionType} + + function SpecOpenRingElem( + R::SpecOpenRingType, + f::Vector{RestrictionType}; + check::Bool=true + ) where {SpecOpenRingType<:SpecOpenRing, RestrictionType<:RingElem} n = length(f) + U = domain(R) n == length(affine_patches(U)) || error("the number of restrictions does not coincide with the number of affine patches") - for i in 1:n - OO(affine_patches(U)[i])(lift(f[i])) # throws an error if conversion is not possible - end - return new{BRT, BRET, RT, RET, MST}(U, f) + g = elem_type(ring_type(scheme(R)))[OO(U[i])(f[i]) for i in 1:n] # will throw if conversion is not possible + return new{SpecOpenRingType, RestrictionType}(R, g) end end -scheme(f::StructureSheafElem) = ambient(domain(f)) -domain(f::StructureSheafElem) = f.domain -restrictions(f::StructureSheafElem) = f.restrictions -affine_patches(f::StructureSheafElem) = affine_patches(domain(f)) -npatches(f::StructureSheafElem) = length(f.restrictions) -getindex(f::StructureSheafElem, i::Int) = getindex(restrictions(f), i) -ambient(f::StructureSheafElem) = OO(scheme(f), domain(f)) +### type getters +elem_type(::Type{SpecOpenRing{S, T}}) where {S, T} = SpecOpenRingElem{SpecOpenRing{S, T}, elem_type(ring_type(S))} + +elem_type(R::SpecOpenRing) = elem_type(typeof(R)) + +parent_type(::Type{SpecOpenRingElem{S, T}}) where {S, T} = S +parent_type(f::SpecOpenRingElem) = parent_type(typeof(f)) + +parent(f::SpecOpenRingElem) = f.parent +scheme(f::SpecOpenRingElem) = scheme(parent(f)) +domain(f::SpecOpenRingElem) = domain(parent(f)) +restrictions(f::SpecOpenRingElem) = f.restrictions +affine_patches(f::SpecOpenRingElem) = affine_patches(domain(f)) +npatches(f::SpecOpenRingElem) = length(restrictions(f)) +getindex(f::SpecOpenRingElem, i::Int) = getindex(restrictions(f), i) +getindex(f::SpecOpenRingElem, U::Spec) = restrictions(f)[domain(f)[U]] + +### copying +function Base.deepcopy_internal(f::SpecOpenRingElem, dict::IdDict) + return SpecOpenRingElem(parent(f), copy(restrictions(f)), check=false) +end function restrict( - f::StructureSheafElem, + f::SpecOpenRingElem, V::Spec ) + isempty(V) && return zero(OO(V)) for i in 1:length(restrictions(f)) if V == affine_patches(domain(f))[i] return restrictions(f)[i] @@ -353,6 +409,104 @@ function restrict( return dot(l, OO(V).(lifted_numerator.(g))) end +(R::MPolyQuoLocalizedRing)(f::SpecOpenRingElem) = restrict(f, Spec(R)) + +(R::SpecOpenRing)(f::RingElem) = SpecOpenRingElem(R, [OO(U)(f) for U in affine_patches(domain(R))]) +(R::SpecOpenRing)(f::Vector{T}) where {T<:RingElem} = SpecOpenRingElem(R, [OO(domain(R)[i])(f[i]) for i in 1:length(f)]) + +function (R::SpecOpenRing)(f::SpecOpenRingElem) + parent(f) === R && return f + return SpecOpenRingElem(R, [restrict(f, U) for U in affine_patches(domain(R))]) +end + +function +(a::T, b::T) where {T<:SpecOpenRingElem} + parent(a) === parent(b) || return a + (parent(a)(b)) + return SpecOpenRingElem(parent(a), [a[i] + b[i] for i in 1:length(restrictions(a))], check=false) +end + +function -(a::T, b::T) where {T<:SpecOpenRingElem} + parent(a) === parent(b) || return a - (parent(a)(b)) + return SpecOpenRingElem(parent(a), [a[i] - b[i] for i in 1:length(restrictions(a))], check=false) +end + +function -(a::T) where {T<:SpecOpenRingElem} + return SpecOpenRingElem(parent(a), [-a[i] for i in 1:length(restrictions(a))], check=false) +end + +function *(a::T, b::T) where {T<:SpecOpenRingElem} + parent(a) === parent(b) || return a * (parent(a)(b)) + return SpecOpenRingElem(parent(a), [a[i] * b[i] for i in 1:length(restrictions(a))], check=false) +end + +function *(a::RingElem, b::T) where {T<:SpecOpenRingElem} + return b*(parent(b)(a)) +end + +function *(a::Integer, b::T) where {T<:SpecOpenRingElem} + return b*(parent(b)(a)) +end + +function *(b::T, a::RingElem) where {T<:SpecOpenRingElem} + return a*b +end + +function ==(a::T, b::T) where {T<:SpecOpenRingElem} + parent(a) === parent(b) || return a == (parent(a)(b)) + for i in 1:length(restrictions(a)) + a[i] == b[i] || return false + end + return true +end + +function ^(a::SpecOpenRingElem, i::Int64) + return SpecOpenRingElem(parent(a), [a[k]^i for k in 1:length(restrictions(a))]) +end +function ^(a::SpecOpenRingElem, i::Integer) + return SpecOpenRingElem(parent(a), [a[k]^i for k in 1:length(restrictions(a))]) +end +function ^(a::SpecOpenRingElem, i::fmpz) + return SpecOpenRingElem(parent(a), [a[k]^i for k in 1:length(restrictions(a))]) +end + +function divexact(a::T, b::T; check::Bool=false) where {T<:SpecOpenRingElem} + parent(a) === parent(b) || return divexact(a, (parent(a)(b))) + return SpecOpenRingElem(parent(a), [divexact(a[i], b[i]) for i in 1:length(restrictions(a))]) +end + +function isunit(a::SpecOpenRingElem) + for i in 1:length(restrictions(a)) + isunit(a[i]) || return false + end + return true +end + +inv(a::SpecOpenRingElem) = SpecOpenRingElem(parent(a), [inv(f) for f in restrictions(a)], check=false) + +one(R::SpecOpenRing) = SpecOpenRingElem(R, [one(OO(U)) for U in affine_patches(domain(R))], check=false) +zero(R::SpecOpenRing) = SpecOpenRingElem(R, [zero(OO(U)) for U in affine_patches(domain(R))], check=false) +(R::SpecOpenRing)() = zero(R) +(R::SpecOpenRing)(a::Integer) = SpecOpenRingElem(R, [OO(U)(a) for U in affine_patches(domain(R))], check=false) +(R::SpecOpenRing)(a::Int64) = SpecOpenRingElem(R, [OO(U)(a) for U in affine_patches(domain(R))], check=false) +(R::SpecOpenRing)(a::fmpz) = SpecOpenRingElem(R, [OO(U)(a) for U in affine_patches(domain(R))], check=false) + +isdomain_type(::Type{T}) where {T<:SpecOpenRingElem} = true +isdomain_type(a::SpecOpenRingElem) = isdomain_type(typeof(a)) +isexact_type(::Type{T}) where {T<:SpecOpenRingElem} = true +isexact_type(a::SpecOpenRingElem) = isexact_type(typeof(a)) +isdomain_type(::Type{T}) where {T<:SpecOpenRing} = true +isdomain_type(R::SpecOpenRing) = isdomain_type(typeof(R)) +isexact_type(::Type{T}) where {T<:SpecOpenRing} = true +isexact_type(R::SpecOpenRing) = isexact_type(typeof(R)) + +AbstractAlgebra.promote_rule(::Type{T}, ::Type{RET}) where {T<:SpecOpenRingElem, RET<:Integer} = T +AbstractAlgebra.promote_rule(::Type{RET}, ::Type{T}) where {T<:SpecOpenRingElem, RET<:Integer} = T + +### TODO: Rethink this. For instance, restrictions can happen both from and to Specs. +function AbstractAlgebra.promote_rule(::Type{T}, ::Type{RET}) where {T<:SpecOpenRingElem, RET<:RingElem} + return T +end + + @Markdown.doc """ maximal_extension(X::Spec, f::AbstractAlgebra.Generic.Frac) @@ -365,16 +519,17 @@ the numerator and denominator of ``f`` have to be elements of the ring ``𝕜[x₁,…,xₙ]``. """ function maximal_extension( - X::Spec{BRT, BRET, RT, RET, MST}, + X::Spec, f::AbstractAlgebra.Generic.Frac{RET} - ) where {BRT, BRET, RT, RET, MST} + ) where {RET<:RingElem} a = numerator(f) b = denominator(f) W = localized_ring(OO(X)) I = quotient(ideal(W, b) + localized_modulus(OO(X)), ideal(W, a)) U = SpecOpen(X, I) g = [OO(V)(f) for V in affine_patches(U)] - return StructureSheafElem(U, g) + R = SpecOpenRing(X, U) + return R(g) end @Markdown.doc """ @@ -389,11 +544,11 @@ the numerators and denominators of the entries of ``f`` have to be elements of the ring ``𝕜[x₁,…,xₙ]``. """ function maximal_extension( - X::Spec{BRT, BRET, RT, RET, MST}, + X::Spec, f::Vector{AbstractAlgebra.Generic.Frac{RET}} - ) where {BRT, BRET, RT, RET, MST} + ) where {RET<:RingElem} if length(f) == 0 - return SpecOpen(X), Vector{StructureSheafElem{BRT, BRET, RT, RET, MST}}() + return SpecOpen(X), Vector{structure_sheaf_elem_type(X)}() end R = base_ring(parent(f[1])) for a in f @@ -408,13 +563,16 @@ function maximal_extension( I = intersect(quotient(ideal(W, denominator(p)) + localized_modulus(OO(X)), ideal(W, numerator(p))), I) end U = SpecOpen(X, I) - return U, [StructureSheafElem(U, [OO(V)(a) for V in affine_patches(U)]) for a in f] + S = SpecOpenRing(X, U) + # TODO: For some reason, the type of the inner vector is not inferred if it has no entries. + # Investigate why? Type instability? + return U, [SpecOpenRingElem(S, (elem_type(OO(X))[OO(V)(a) for V in affine_patches(U)])) for a in f] end #TODO: implement the catchall versions of the above functions. @Markdown.doc """ - SpecOpenMor{BRT, BRET, RT, RET, MST1, MST2} + SpecOpenMor{DomainType<:SpecOpen, CodomainType<:SpecOpen, SpecMorType<:SpecMor} Morphisms ``f : U → V`` of open sets ``U ⊂ X`` and ``V ⊂ Y`` of affine schemes. These are stored as morphisms ``fᵢ: Uᵢ→ Y`` on the affine patches @@ -424,27 +582,25 @@ The type parameters stand for the following: When ``X = Spec(R)`` and ``Y = Spec(S)`` with ``R = (𝕜[x₁,…,xₘ]/I)[A⁻¹]`` and ``S = (𝕜[y₁,…,yₙ]/J)[B⁻¹]`` then - * BRT is the type of the coefficient ring ``𝕜``; - * BRET is the type of the elements of ``𝕜``; - * RT is the type of the polynomial rings ``𝕜[x₁,…,xₘ]`` and ``𝕜[y₁,…,yₙ]; - * RET is the type of the elements of these rings; - * MST1 is the type of the multiplicative set ``A``; - * MST2 is the type of the multiplicative set ``B``; + * `DomainType` is the type of the domain; + * `CodomainType` is the type of the codomain; + * `SpecMorType` is the type of the restriction of the morphism to the +affine patches of the domain to the affine ambient scheme of the codomain. """ -mutable struct SpecOpenMor{BRT, BRET, RT, RET, MST1, MST2} - domain::SpecOpen{BRT, BRET, RT, RET, MST1} - codomain::SpecOpen{BRT, BRET, RT, RET, MST2} - maps_on_patches::Vector{SpecMor{BRT, BRET, RT, RET, MST1, MST2}} +mutable struct SpecOpenMor{DomainType<:SpecOpen, CodomainType<:SpecOpen, SpecMorType<:SpecMor} + domain::DomainType + codomain::CodomainType + maps_on_patches::Vector{SpecMorType} # fields used for caching - inverse::SpecOpenMor{BRT, BRET, RT, RET, MST2, MST1} + inverse::SpecOpenMor function SpecOpenMor( - U::SpecOpen{BRT, BRET, RT, RET, MST1}, - V::SpecOpen{BRT, BRET, RT, RET, MST2}, - f::Vector{SpecMor{BRT, BRET, RT, RET, MST1, MST2}}; + U::DomainType, + V::CodomainType, + f::Vector{SpecMorType}; check::Bool=true - ) where {BRT, BRET, RT, RET, MST1, MST2} + ) where {DomainType<:SpecOpen, CodomainType<:SpecOpen, SpecMorType<:SpecMor} Y = ambient(V) n = length(f) n == length(affine_patches(U)) || error("number of patches does not coincide with the number of maps") @@ -464,26 +620,42 @@ mutable struct SpecOpenMor{BRT, BRET, RT, RET, MST1, MST2} one(localized_ring(OO(domain(g)))) in pullback(g)(I) + localized_modulus(OO(domain(g))) || error("image is not contained in the codomain") end end - return new{BRT, BRET, RT, RET, MST1, MST2}(U, V, f) + return new{DomainType, CodomainType, SpecMorType}(U, V, f) end end +morphism_type(U::S, V::T) where {S<:SpecOpen, T<:SpecOpen} = SpecOpenMor{S, T, morphism_type(ambient(U), ambient(V))} +morphism_type(::Type{DomainType}, ::Type{CodomainType}) where {DomainType<:SpecOpen, CodomainType<:SpecOpen} = SpecOpenMor{DomainType, CodomainType, morphism_type(ambient_type(DomainType), ambient_type(CodomainType))} + domain(f::SpecOpenMor) = f.domain codomain(f::SpecOpenMor) = f.codomain maps_on_patches(f::SpecOpenMor) = f.maps_on_patches getindex(f::SpecOpenMor, i::Int) = maps_on_patches(f)[i] -function SpecOpenMor(U::T, V::T, f::Vector) where {T<:SpecOpen} +function Base.show(io::IO, f::SpecOpenMor) + print(io, "Morphism from $(domain(f)) to $(codomain(f)) given by the rational map $(generic_fractions(f))") +end + +function SpecOpenMor(U::T, V::T, f::Vector; check::Bool=true) where {T<:SpecOpen} Y = ambient(V) - return SpecOpenMor(U, V, [SpecMor(W, Y, f) for W in affine_patches(U)]) + return SpecOpenMor(U, V, [SpecMor(W, Y, f) for W in affine_patches(U)], check=check) end -function inclusion_morphism(U::T, V::T) where {T<:SpecOpen} +function inclusion_morphism(U::T, V::T; check::Bool=true) where {T<:SpecOpen} X = ambient(U) - ambient(V) == X || error("method not implemented") - return SpecOpenMor(U, V, gens(base_ring(OO(X)))) + if check + issubset(U, V) || error("method not implemented") + end + return SpecOpenMor(U, V, gens(base_ring(OO(X))), check=false) end +function SpecOpenMor(X::SpecType, d::RET, Y::SpecType, e::RET, f::Vector{RET}; check::Bool=true) where {SpecType<:Spec, RET<:RingElem} + U = SpecOpen(X, [d], check=check) + V = SpecOpen(Y, [e], check=check) + return SpecOpenMor(U, V, [SpecMor(U[1], Y, OO(U[1]).(f), check=check)], check=check) +end + + @Markdown.doc """ compose(f::T, g::T) where {T<:SpecOpenMor} @@ -497,11 +669,13 @@ Compute the composition of two morphisms of open sets of affine varieties. """ -function compose(f::T, g::T) where {T<:SpecOpenMor} +function compose(f::T, g::T; check::Bool=true) where {T<:SpecOpenMor} U = domain(f) Cf = codomain(f) V = domain(g) - issubset(Cf, V) || error("maps are not compatible") + if check + issubset(Cf, V) || error("maps are not compatible") + end W = codomain(g) X = ambient(U) Y = ambient(V) @@ -526,7 +700,7 @@ function compose(f::T, g::T) where {T<:SpecOpenMor} ##################################################################### m = length(gens(U)) n = length(gens(V)) - result_maps = Vector{typeof(f_maps[1])}() + result_maps = Vector{morphism_type(X, Y)}() for i in 1:m U_i = affine_patches(U)[i] f_i = f_maps[i] @@ -552,15 +726,19 @@ function compose(f::T, g::T) where {T<:SpecOpenMor} return SpecOpenMor(U, W, result_maps) end -function pullback(f::SpecOpenMor{BRT, BRET, RT, RET, MST1, MST2}, a::RET) where {BRT, BRET, RT, RET, MST1, MST2} +function pullback(f::SpecOpenMor, a::RET) where {RET<:RingElem} U = domain(f) X = ambient(U) V = codomain(f) Y = ambient(V) R = base_ring(OO(Y)) parent(a) == R || error("element does not belong to the correct ring") - pb_a = [pullback(f[i])(a) for i in 1:npatches(U)] - return StructureSheafElem(U, pb_a) + pb_a = elem_type(OO(X))[pullback(f[i])(a) for i in 1:npatches(U)] + return SpecOpenRingElem(SpecOpenRing(X, U), pb_a) +end + +function pullback(f::SpecOpenMor, a::SpecOpenRingElem) + error("not implemented") end @Markdown.doc """ @@ -571,13 +749,13 @@ determined by ``ϕ*(yⱼ) = fⱼ = aⱼ/bⱼ``, find the maximal open subset ``U to which ``ϕ`` can be extended to a regular map ``g : U → Y`` and return ``g``. """ function maximal_extension( - X::Spec{BRT, BRET, RT, RET, MST}, - Y::Spec{BRT, BRET, RT, RET, MST}, + X::SpecType, + Y::SpecType, f::Vector{AbstractAlgebra.Generic.Frac{RET}} - ) where {BRT, BRET, RT, RET, MST} + ) where {SpecType<:Spec, RET<:RingElem} U, g = maximal_extension(X, f) n = length(affine_patches(U)) - maps = Vector{SpecMor{BRT, BRET, RT, RET, MST, MST}}() + maps = Vector{morphism_type(X, Y)}() for i in 1:n push!(maps, SpecMor(affine_patches(U)[i], Y, [restrictions(a)[i] for a in g])) end @@ -592,28 +770,49 @@ function maximal_extension( return maximal_extension(X, Y, FractionField(base_ring(OO(X))).(f)) end +### the restriction of a morphism to open subsets in domain and codomain function restriction( f::SpecOpenMor, U::SpecOpen, - V::SpecOpen + V::SpecOpen; + check::Bool=true ) - inc = SpecOpenMor(U, domain(f), [SpecMor(W, ambient(domain(f)), gens(localized_ring(OO(W)))) for W in affine_patches(U)]) - help_map = compose(inc, f) - return SpecOpenMor(U, V, maps_on_patches(help_map)) + if check + issubset(U, domain(f)) || error("the given open is not an open subset of the domain of the map") + issubset(V, codomain(f)) || error("the given open is not an open subset of the codomain of the map") + end + inc = inclusion_morphism(U, domain(f), check=check) + help_map = compose(inc, f, check=check) + return SpecOpenMor(U, V, maps_on_patches(help_map), check=check) +end + +### the restriction of a morphism to closed subsets in domain and codomain +function restriction( + f::SpecOpenMor, + X::SpecType, + Y::SpecType; + check::Bool=true + ) where {SpecType<:Spec} + U = intersect(X, domain(f)) + V = intersect(Y, codomain(f)) + + new_maps_on_patches = [restrict(f[i], U[i], Y, check=check) for i in 1:npatches(U)] + + return SpecOpenMor(U, V, new_maps_on_patches, check=check) end -identity_map(U::SpecOpen) = SpecOpenMor(U, U, [SpecMor(V, ambient(U), gens(localized_ring(OO(V)))) for V in affine_patches(U)]) +identity_map(U::SpecOpen) = SpecOpenMor(U, U, [SpecMor(V, ambient(U), gens(OO(V)), check=false) for V in affine_patches(U)], check=false) function ==(f::T, g::T) where {T<:SpecOpenMor} - domain(f) == domain(g) || return false - codomain(f) == codomain(g) || return false + is_canonically_isomorphic(domain(f), domain(g)) || return false + is_canonically_isomorphic(codomain(f), codomain(g)) || return false Y = ambient(codomain(f)) m = length(affine_patches(domain(f))) n = length(affine_patches(domain(g))) for i in 1:m for j in 1:n - restrict(f[i], intersect(domain(f)[i], domain(g)[i]), Y) == - restrict(g[i], intersect(domain(f)[i], domain(g)[i]), Y) || return false + U = intersect(domain(f)[i], domain(g)[i]) + restrict(f[i], U, Y) == restrict(g[i], U, Y) || return false end end return true @@ -629,7 +828,7 @@ function preimage(f::SpecOpenMor, Z::Spec) I = intersect(I, localized_modulus(OO(closure(preimage(f[i], Z), X)))) end fZbar = subscheme(X, I) - return SpecOpen(fZbar, gens(U)) + return SpecOpen(fZbar, [g for g in gens(U) if !iszero(OO(fZbar)(g))]) end function preimage(f::SpecOpenMor, V::SpecOpen) @@ -640,10 +839,10 @@ function preimage(f::SpecOpenMor, V::SpecOpen) for i in 1:npatches(U) I = intersect(I, saturated_ideal(ideal(OO(U[i]), pullback(f[i]).(gens(V))))) end - return intersect(U, SpecOpen(X, gens(I))) + return intersect(U, SpecOpen(X, I)) end -function is_non_zero_divisor(f::RET, U::SpecOpen{BRT, BRET, RT, RET}) where {BRT, BRET, RT, RET} +function is_non_zero_divisor(f::RET, U::SpecOpen) where {RET<:RingElem} for V in affine_patches(U) zero_ideal = ideal(OO(V), [zero(OO(V))]) zero_ideal == quotient(zero_ideal, ideal(OO(V), [f])) || return false @@ -654,8 +853,11 @@ end function find_non_zero_divisor(U::SpecOpen) n = length(gens(U)) X = ambient(U) - kk = coefficient_ring(base_ring(OO(X))) - d = dot([rand(kk, 0:100) for i in 1:n], gens(U)) + R = base_ring(OO(X)) + n == 0 && return zero(R) + kk = coefficient_ring(R) + coeff = elem_type(kk)[rand(kk, 0:100) for i in 1:n] + d = sum([coeff[i]*gens(U)[i] for i in 1:n]) while !is_non_zero_divisor(d, U) d = dot([rand(kk, 0:100) for i in 1:n], gens(U)) end @@ -694,3 +896,24 @@ function is_dense(U::SpecOpen) end return J == localized_modulus(OO(X)) end + +#function Base.adjoint(M::AbstractAlgebra.Generic.MatSpaceElem{T}) where {T} +function Base.adjoint(M::MatElem) + n = ncols(M) + n == nrows(M) || error("matrix is not square") + N = zero(M) + rows = collect(1:n) + cols = collect(1:n) + row_sign = 1 + for i in 1:n + column_sign = row_sign + for j in 1:n + N[j, i] = column_sign* det(M[deleteat!(copy(rows), i), deleteat!(copy(cols), j)]) + column_sign = column_sign*(-1) + end + row_sign = row_sign*(-1) + end + return N +end + +Base.inv(M::MatElem) = inv(det(M))*adjoint(M) diff --git a/src/Geometry/basics.jl b/src/Geometry/basics.jl index b890f5d71fd7..cbae684de0db 100644 --- a/src/Geometry/basics.jl +++ b/src/Geometry/basics.jl @@ -9,7 +9,7 @@ import Oscar: weights import AbstractAlgebra, Nemo import Base: ==, show, hash -export projective_space, normalize!, coordinate_ring +export proj_space, normalize!, coordinate_ring struct ProjSpc{T} <: Oscar.ProjSpc{T} R::AbstractAlgebra.Ring @@ -26,13 +26,13 @@ function Base.show(io::IO, P::ProjSpc) end end -function projective_space(R::AbstractAlgebra.Ring, n::Int, name::Symbol=:x) +function proj_space(R::AbstractAlgebra.Ring, n::Int, name::Symbol=:x) Sx = PolynomialRing(R, name => 0:n)[1] Rx = grade(Sx, [1 for i=0:n])[1] return ProjSpc(R, n, Rx), gens(Rx) end -function projective_space(R::AbstractAlgebra.Ring, n::Vector{<:Integer}, name::Symbol = :x) +function proj_space(R::AbstractAlgebra.Ring, n::Vector{<:Integer}, name::Symbol = :x) Sx = PolynomialRing(R, name => 0:length(n)-1)[1] Rx = grade(Sx, n)[1] return ProjSpc(R, length(n)-1, Rx), gens(Rx) @@ -188,4 +188,4 @@ end end # module Geometry using .Geometry -export projective_space +export proj_space diff --git a/src/Oscar.jl b/src/Oscar.jl index c7ddba15cc23..c7d6d602162c 100644 --- a/src/Oscar.jl +++ b/src/Oscar.jl @@ -289,9 +289,6 @@ include("Rings/binomial_ideals.jl") include("ToricVarieties/JToric.jl") -include("../experimental/Schemes/AffineSchemes.jl") -include("../experimental/Schemes/SpecOpen.jl") - if is_dev # include("../examples/ModStdNF.jl") # include("../examples/ModStdQ.jl") diff --git a/src/Rings/localization_interface.jl b/src/Rings/localization_interface.jl index 203e89f33d90..b5537874fce8 100644 --- a/src/Rings/localization_interface.jl +++ b/src/Rings/localization_interface.jl @@ -10,6 +10,8 @@ export numerator, denominator, parent export AbsLocalizedIdeal export ideal +export AbsLocalizedRingHom, domain, codomain, restricted_map + import AbstractAlgebra.Ring import AbstractAlgebra: expressify, show_via_expressify @@ -52,6 +54,15 @@ function Base.in(f::RingElemType, S::AbsMultSet{RingType, RingElemType}) where { error("method not implemented for multiplicatively closed sets of type $(typeof(S))") end +### iterator over the multiplicative set +# This can (and should) be used to iterate over some set of generators +# of the multiplicative set whenever possible. For instance, this is +# used to check well-definedness of homomorphisms from localized rings. +# By default, however, this iteration does nothing. +Base.iterate(U::T) where {T<:AbsMultSet} = (one(ambient_ring(U)), 1) +Base.iterate(U::T, a::Tuple{<:RingElem, Int}) where {T<:AbsMultSet} = nothing +Base.iterate(U::T, i::Int) where {T<:AbsMultSet} = nothing + ################################################################################# # Localizations of (commutative) rings at multiplicatively closed sets # @@ -129,7 +140,9 @@ function (W::AbsLocalizedRing{RingType, RingElemType, MultSetType})(a::RingElemT end ### Other conversions for the sake of convenience -(W::AbsLocalizedRing)(a::Oscar.IntegerUnion) = W(base_ring(W)(a)) +(W::AbsLocalizedRing)(a::Int) = W(base_ring(W)(a)) +(W::AbsLocalizedRing)(a::Integer) = W(base_ring(W)(a)) +(W::AbsLocalizedRing)(a::fmpz) = W(base_ring(W)(a)) ################################################################################# @@ -286,6 +299,8 @@ one(W::AbsLocalizedRing) = W(one(base_ring(W))) zero(W::AbsLocalizedRing) = W(zero(base_ring(W))) +(W::AbsLocalizedRing)() = zero(W) + canonical_unit(f::LocRingElemType) where {LocRingElemType<:AbsLocalizedRingElem} = one(parent(f)) characteristic(W::AbsLocalizedRing) = characteristic(base_ring(W)) @@ -318,14 +333,14 @@ function addeq!(a::T, b::T) where {T<:AbsLocalizedRingElem} end ### promotion rules -promote_rule(::Type{AbsLocalizedRingElem{RT, RET, MST}}, ::Type{AbsLocalizedRingElem{RT, RET, MST}}) where {RT<:Ring, RET<:RingElement, MST} = AbsLocalizedRingElem{RT, RET, MST} +AbstractAlgebra.promote_rule(::Type{S}, ::Type{S}) where {S<:AbsLocalizedRingElem} = S -function promote_rule(::Type{AbsLocalizedRingElem{RT, RET, MST}}, ::Type{T}) where {RT<:Ring, RET<:RingElement, MST, T<:RingElement} - promote_rule(RET, T) ? AbsLocalizedRingElem{RT, RET, MST} : Union{} +function AbstractAlgebra.promote_rule(::Type{S}, ::Type{T}) where {RT<:Ring, RET<:RingElement, MST, S<:AbsLocalizedRingElem{RT, RET, MST}, T<:RingElement} + AbstractAlgebra.promote_rule(RET, T) == RET ? S : Union{} end - - +### default conversion passing through the base ring +(L::AbsLocalizedRing)(f::RET) where {RET<:RingElem} = L(base_ring(L)(f)) ### Needs to be overwritten in case of zero divisors! @@ -383,6 +398,10 @@ function ideal( error("`ideal(W, v)` has not been implemented for `W` of type $(typeof(W)) and `v` of type $(typeof(v))") end +function (W::AbsLocalizedRing)(I::Ideal) + return ideal(W, W.(gens(I))) +end + ### required functionality # Checks for ideal membership of `f` in `I`. function Base.in( @@ -423,23 +442,33 @@ end ######################################################################## @Markdown.doc """ - AbsLocalizedRingHom{RingType, RingElemType, DomainMultSetType, CodomainMultSetType} - -Homomorphism ``ϕ : P[U⁻¹] → Q[V⁻¹]`` of localized rings with ``P`` and -``Q`` of type `RingType`, ``U`` of type `DomainMultSetType`, and -``V`` of type `CodomainMultSetType`. + AbsLocalizedRingHom{ + DomainType<:AbsLocalizedRing, + CodomainType<:Ring, + RestrictedMapType + } <: Map{ + DomainType, + CodomainType, + SetMap, + AbsLocalizedRingHom + } + +Homomorphism ``ϕ : R[U⁻¹] → S`` from the localization ``R[U⁻¹]`` of type +``DomainType`` to an arbitrary ring `S` of type `CodomainType`. Such a +homomorphism is completely determined by its 'restriction' +``ϕ' : R → R[U⁻¹] → S`` to the `base_ring` ``R`` before localization and +the type parameter `RestrictedMapType` is reserved for that map. """ abstract type AbsLocalizedRingHom{ - RingType, - RingElemType, - DomainMultSetType, - CodomainMultSetType - } <: Map{ - AbsLocalizedRing{RingType, RingElemType, DomainMultSetType}, - AbsLocalizedRing{RingType, RingElemType, CodomainMultSetType}, - SetMap, - AbsLocalizedRingHom - } + DomainType<:AbsLocalizedRing, + CodomainType<:Ring, + RestrictedMapType + } <: Map{ + DomainType, + CodomainType, + SetMap, + AbsLocalizedRingHom + } end @@ -462,16 +491,29 @@ function codomain(f::AbsLocalizedRingHom) error("`codomain(f)` not implemented for `f` of type $(typeof(f))") end +@Markdown.doc """ + restricted_map(f::AbsLocalizedRingHom) + +For a ring homomorphism ``ϕ : R[U⁻¹] → S`` return the underlying +restriction ``ϕ' : R → S``. +""" +function restricted_map(f::AbsLocalizedRingHom) + error("`restricted_map(f)` not implemented for `f` of type $(typeof(f))") +end + ### required functionality @Markdown.doc """ (f::AbsLocalizedRingHom)(a::T) where {T<:RingElement} Applies the map `f` to the element `a` in the domain of `f`. """ -(f::AbsLocalizedRingHom{RT, RET, DMST, CMST})(a::AbsLocalizedRingElem{RT, RET, DMST}) where {RT, RET, DMST, CMST} = error("mapping of elements of type $(typeof(a)) not implemented") +function (f::AbsLocalizedRingHom)(a::AbsLocalizedRingElem) + parent(a) === domain(f) || return f(domain(f)(a)) + return codomain(f)(restricted_map(f)(numerator(a)))*inv(codomain(f)(restricted_map(f)(denominator(a)))) +end ### generic functions -(f::AbsLocalizedRingHom{RT, RET, DMST, CMST})(a::RET) where {RT, RET, DMST, CMST} = f(domain(f)(a)) +(f::AbsLocalizedRingHom)(a::RingElem) = f(domain(f)(a)) (f::AbsLocalizedRingHom)(a::Integer) = f(domain(f)(a)) (f::AbsLocalizedRingHom)(a::fmpz) = f(domain(f)(a)) @@ -480,16 +522,20 @@ Applies the map `f` to the element `a` in the domain of `f`. Return the ideal generated by the images `f(hᵢ)` of the generators `hᵢ` of `I`. """ -(f::AbsLocalizedRingHom{RT, RET, DMST, CMST})(I::AbsLocalizedIdeal{RT, RET, DMST}) where {RT, RET, DMST, CMST} = ideal(codomain(f), f.(gens(I))) - -(f::AbsLocalizedRingHom{RT, RET, DMST, CMST})(I::Ideal{RET}) where {RT, RET, DMST, CMST} = ideal(codomain(f), f.(domain(f).(gens(I)))) +(f::AbsLocalizedRingHom)(I::Ideal) = ideal(codomain(f), f.(domain(f).(gens(I)))) ### implementing the Oscar map interface check_composable( - f::AbsLocalizedRingHom{RT, RET, MST1, MST2}, - g::AbsLocalizedRingHom{RT, RET, MST2, MST3} - ) where {RT, RET, MST1, MST2, MST3} = (codomain(f) == domain(g)) + f::AbsLocalizedRingHom{D, C}, + g::AbsLocalizedRingHom{C, E} + ) where {C, D, E} = (codomain(f) == domain(g)) + +function Base.show(io::IO, f::AbsLocalizedRingHom) + print(io, "morphism from the localized ring $(domain(f)) to $(codomain(f))") +end -function Base.show(io::IO, f::AbsLocalizedRingHom{RT, RET, MST1, MST2}) where {RT, RET, MST1, MST2} - print(io, "morphism of localized rings from $(domain(f)) to $(codomain(f))") +function ==(f::T, g::T) where {T<:AbsLocalizedRingHom} + domain(f) === domain(g) || return false + codomain(f) === codomain(g) || return false + return restricted_map(f) == restricted_map(g) end diff --git a/src/Rings/mpoly-localizations.jl b/src/Rings/mpoly-localizations.jl index 3bb2e3112a39..cad01c96f7a0 100644 --- a/src/Rings/mpoly-localizations.jl +++ b/src/Rings/mpoly-localizations.jl @@ -1,5 +1,5 @@ export MPolyComplementOfPrimeIdeal, MPolyComplementOfKPointIdeal, MPolyPowersOfElement, MPolyProductOfMultSets -export rand, sets, issubset, units_of +export rand, sets, issubset, units_of, simplify!, is_trivial export MPolyLocalizedRing export ambient_ring, point_coordinates, inverted_set, denominators, gens @@ -103,9 +103,17 @@ function Base.in( # We need to check whether for some a ∈ R and k ∈ ℕ we have # a⋅f = dᵏ. (i, o) = ppio(f, d) - return divides(one(R), o)[1] + #return divides(one(R), o)[1] + return isunit(o) end +### iteration +Base.iterate(U::MPolyPowersOfElement) = (U.a[1], 1) +Base.iterate(U::MPolyPowersOfElement, a::Tuple{<:MPolyElem, Int}) = (a[2] < length(U.a) ? (U.a[a[2]+1], a[2]+1) : nothing) +Base.iterate(U::MPolyPowersOfElement, i::Int) = (i < length(U.a) ? (U.a[i+1], i+1) : nothing) + +is_trivial(U::MPolyPowersOfElement) = (U == units_of(ambient_ring(U))) + ### printing function Base.show(io::IO, S::MPolyPowersOfElement) print(io, "powers of ") @@ -117,6 +125,20 @@ function rand(S::MPolyPowersOfElement, v1::UnitRange{Int}, v2::UnitRange{Int}, v return prod([f^(abs(rand(Int))%10) for f in denominators(S)])::elem_type(ambient_ring(S)) end +### simplification. +# Replaces each element d by its list of square free prime divisors. +function simplify!(S::MPolyPowersOfElement) + R = ambient_ring(S) + new_denom = Vector{elem_type(R)}() + for d in denominators(S) + for a in factor(d) + push!(new_denom, a[1]) + end + end + S.a = new_denom + return S +end + ######################################################################## # Complements of prime ideals # @@ -868,10 +890,24 @@ fraction(a::MPolyLocalizedRingElem) = a.frac BaseRingType, BaseRingElemType, RingType, - RingElemType, + RingElemType<:RingElem, MultSetType } = MPolyLocalizedRingElem(W, FractionField(base_ring(W))(f), check=check) +(W::MPolyLocalizedRing{ + BaseRingType, + BaseRingElemType, + RingType, + RingElemType, + MultSetType + })(f::BaseRingElemType) where { + BaseRingType, + BaseRingElemType, + RingType, + RingElemType, + MultSetType + } = MPolyLocalizedRingElem(W, FractionField(base_ring(W))(base_ring(W)(f)), check=false) + function (W::MPolyLocalizedRing{ BaseRingType, BaseRingElemType, @@ -941,7 +977,7 @@ function *(a::MPolyLocalizedRingElem{BRT, BRET, RT, RET, MST}, b::RET) where {BR end function *(a::BRET, b::MPolyLocalizedRingElem{BRT, BRET, RT, RET, MST}) where {BRT, BRET <: RingElem, RT, RET, MST} - return (parent(b))(a*fraction(b), check=false) + return (parent(b))(a*numerator(b), denominator(b), check=false) end function *(a::MPolyLocalizedRingElem{BRT, BRET, RT, RET, MST}, b::BRET) where {BRT, BRET <: RingElem, RT, RET, MST} @@ -1024,7 +1060,9 @@ parent_type(T::Type{MPolyLocalizedRingElem{BaseRingType, BaseRingElemType, RingT (W::MPolyLocalizedRing{BaseRingType, BaseRingElemType, RingType, RingElemType, MultSetType})(f::MPolyLocalizedRingElem{BaseRingType, BaseRingElemType, RingType, RingElemType, MultSetType}; check::Bool=true) where {BaseRingType, BaseRingElemType, RingType, RingElemType, MultSetType} = MPolyLocalizedRingElem(W, fraction(f), check=check) (W::MPolyLocalizedRing)() = zero(W) -(W::MPolyLocalizedRing)(a::Integer; check::Bool=true) = W(base_ring(W)(a), check=check) +(W::MPolyLocalizedRing)(a::Integer) = W(base_ring(W)(a), check=false) +(W::MPolyLocalizedRing)(a::Int64) = W(base_ring(W)(a), check=false) +(W::MPolyLocalizedRing)(a::fmpz) = W(base_ring(W)(a), check=false) isdomain_type(T::Type{MPolyLocalizedRingElem{BRT, BRET, RT, RET, MST}}) where {BRT, BRET, RT, RET, MST} = true isexact_type(T::Type{MPolyLocalizedRingElem{BRT, BRET, RT, RET, MST}}) where {BRT, BRET, RT, RET, MST} = true @@ -1076,7 +1114,8 @@ mutable struct LocalizedBiPolyArray{BRT, BRET, RT, RET, MST} oscar_ring::MPolyLocalizedRing{BRT, BRET, RT, RET, MST}, oscar_gens::Vector{MPolyLocalizedRingElem{BRT, BRET, RT, RET, MST}}, shift::Vector{BRET}, - ordering::Symbol + ordering::Symbol; + is_groebner_basis::Bool=false ) where {BRT, BRET, RT, RET, MST} lbpa = new{BRT, BRET, RT, RET, MST}() # TODO: Add some sanity checks here @@ -1088,7 +1127,7 @@ mutable struct LocalizedBiPolyArray{BRT, BRET, RT, RET, MST} push!(shift, zero(coefficient_ring(base_ring(oscar_ring)))) end lbpa.shift = shift - lbpa.is_groebner_basis=false + lbpa.is_groebner_basis=is_groebner_basis return lbpa end @@ -1146,7 +1185,8 @@ function singular_assure(lbpa::LocalizedBiPolyArray) shift_hom = hom(base_ring(oscar_ring(lbpa)), base_ring(oscar_ring(lbpa)), [gen(base_ring(oscar_ring(lbpa)), i) + lbpa.shift[i] for i in (1:nvars(base_ring(oscar_ring(lbpa))))]) lbpa.singular_gens = Singular.Ideal(lbpa.singular_ring, - [lbpa.singular_ring(shift_hom(numerator(x))) for x in oscar_gens(lbpa)]) + (elem_type(lbpa.singular_ring))[lbpa.singular_ring(shift_hom(numerator(x))) for x in oscar_gens(lbpa)]) + #lbpa.singular_gens = (length(oscar_gens(lbpa)) == 0 ? Singular.Ideal(lbpa.singular_ring) : Singular.Ideal(lbpa.singular_ring, [lbpa.singular_ring(shift_hom(numerator(x))) for x in oscar_gens(lbpa)])) end end @@ -1266,7 +1306,7 @@ end Ideals in localizations of polynomial rings. """ -mutable struct MPolyLocalizedIdeal{BRT, BRET, RT, RET, MST} <: AbsLocalizedIdeal{RT, RET, MST} +@attributes mutable struct MPolyLocalizedIdeal{BRT, BRET, RT, RET, MST} <: AbsLocalizedIdeal{RT, RET, MST} # the initial set of generators, not to be changed ever! gens::Vector{MPolyLocalizedRingElem{BRT, BRET, RT, RET, MST}} # the ambient ring for this ideal @@ -1275,7 +1315,6 @@ mutable struct MPolyLocalizedIdeal{BRT, BRET, RT, RET, MST} <: AbsLocalizedIdeal # Fields for caching groebner_bases::Dict{Symbol, LocalizedBiPolyArray{BRT, BRET, RT, RET, MST}} dimension::Int - saturated_ideal::MPolyIdeal{RET} function MPolyLocalizedIdeal( W::MPolyLocalizedRing{BRT, BRET, RT, RET, MST}, @@ -1315,73 +1354,83 @@ end function saturated_ideal( I::MPolyLocalizedIdeal{BRT, BRET, RT, RET, MPolyComplementOfPrimeIdeal{BRT, BRET, RT, RET}} ) where {BRT, BRET, RT, RET} - ### saturation has to proceed via primary decomposition in this case. - # We rely on the primary decomposition on the singular side which is - # already implemented so that all non-relevant components are thrown - # away. The remaining components are then intersected to produce - # the ideal in question. - lbpa = LocalizedBiPolyArray(I) - V = base_ring(I) - R = base_ring(V) - sing_decomp = Singular.LibPrimdec.primdecGTZ(singular_ring(lbpa), singular_gens(lbpa)) - decomp = [LocalizedBiPolyArray(V, K[1]) for K in Singular.LibPrimdec.primdecGTZ(singular_ring(lbpa), singular_gens(lbpa))] - if length(decomp) == 0 - return ideal(R, one(R)) - end - relevant_comp = LocalizedBiPolyArray([one(V)]) - U = inverted_set(V) - P = prime_ideal(U) - for comp in decomp - # Check whether the given component is contained in P. - issubset(ideal(R, numerator.(oscar_gens(comp))), P) && (relevant_comp = LocalizedBiPolyArray(V, Singular.intersection(singular_gens(relevant_comp), singular_gens(comp)))) + if !has_attribute(I, :saturated_ideal) + ### saturation has to proceed via primary decomposition in this case. + # We rely on the primary decomposition on the singular side which is + # already implemented so that all non-relevant components are thrown + # away. The remaining components are then intersected to produce + # the ideal in question. + lbpa = LocalizedBiPolyArray(I) + V = base_ring(I) + R = base_ring(V) + sing_decomp = Singular.LibPrimdec.primdecGTZ(singular_ring(lbpa), singular_gens(lbpa)) + decomp = [LocalizedBiPolyArray(V, K[1]) for K in Singular.LibPrimdec.primdecGTZ(singular_ring(lbpa), singular_gens(lbpa))] + if length(decomp) == 0 + return ideal(R, one(R)) + end + relevant_comp = LocalizedBiPolyArray([one(V)]) + U = inverted_set(V) + P = prime_ideal(U) + for comp in decomp + # Check whether the given component is contained in P. + issubset(ideal(R, numerator.(oscar_gens(comp))), P) && (relevant_comp = LocalizedBiPolyArray(V, Singular.intersection(singular_gens(relevant_comp), singular_gens(comp)))) + end + set_attribute!(I, :saturated_ideal, ideal(R, numerator.(oscar_gens(relevant_comp)))) end - return ideal(R, numerator.(oscar_gens(relevant_comp))) + return get_attribute(I, :saturated_ideal)::MPolyIdeal{RET} end function saturated_ideal( I::MPolyLocalizedIdeal{BRT, BRET, RT, RET, MPolyComplementOfKPointIdeal{BRT, BRET, RT, RET}} ) where {BRT, BRET, RT, RET} - ### saturation has to proceed via primary decomposition in this case. - # We rely on the primary decomposition on the singular side which is - # already implemented so that all non-relevant components are thrown - # away. The remaining components are then intersected to produce - # the ideal in question. - lbpa = LocalizedBiPolyArray(I) - V = base_ring(I) - R = base_ring(V) - sing_decomp = Singular.LibPrimdec.primdecGTZ(singular_ring(lbpa), singular_gens(lbpa)) - decomp = [LocalizedBiPolyArray(V, K[1]) for K in Singular.LibPrimdec.primdecGTZ(singular_ring(lbpa), singular_gens(lbpa))] - if length(decomp) == 0 - return ideal(R, one(R)) - end - relevant_comp = decomp[1] - for i in (2:length(decomp)) - relevant_comp = LocalizedBiPolyArray(V, Singular.intersection(singular_gens(relevant_comp), singular_gens(decomp[i]))) + if !has_attribute(I, :saturated_ideal) + ### saturation has to proceed via primary decomposition in this case. + # We rely on the primary decomposition on the singular side which is + # already implemented so that all non-relevant components are thrown + # away. The remaining components are then intersected to produce + # the ideal in question. + lbpa = LocalizedBiPolyArray(I) + V = base_ring(I) + R = base_ring(V) + sing_decomp = Singular.LibPrimdec.primdecGTZ(singular_ring(lbpa), singular_gens(lbpa)) + decomp = [LocalizedBiPolyArray(V, K[1]) for K in Singular.LibPrimdec.primdecGTZ(singular_ring(lbpa), singular_gens(lbpa))] + if length(decomp) == 0 + return ideal(R, one(R)) + end + relevant_comp = decomp[1] + for i in (2:length(decomp)) + relevant_comp = LocalizedBiPolyArray(V, Singular.intersection(singular_gens(relevant_comp), singular_gens(decomp[i]))) + end + set_attribute!(I, :saturated_ideal, ideal(R, numerator.(oscar_gens(relevant_comp)))) end - return ideal(R, numerator.(oscar_gens(relevant_comp))) + return get_attribute(I, :saturated_ideal)::MPolyIdeal{RET} end function saturated_ideal( I::MPolyLocalizedIdeal{BRT, BRET, RT, RET, MPolyPowersOfElement{BRT, BRET, RT, RET}} ) where {BRT, BRET, RT, RET} - if !isdefined(I, :saturated_ideal) + if is_trivial(inverted_set(base_ring(I))) + set_attribute!(I, :saturated_ideal, ideal(base_ring(base_ring(I)), numerator.(gens(I)))) + end + if !has_attribute(I, :saturated_ideal) W = base_ring(I) R = base_ring(W) U = inverted_set(W) lbpa = LocalizedBiPolyArray(I) - sat_ideal = singular_gens(lbpa) + ssat_ideal = singular_gens(lbpa) for a in denominators(U) - sat_ideal = Singular.saturation(sat_ideal, Singular.Ideal(singular_ring(lbpa), [singular_ring(lbpa)(a)]))[1] + ssat_ideal = Singular.saturation(ssat_ideal, Singular.Ideal(singular_ring(lbpa), [singular_ring(lbpa)(a)]))[1] end - I.saturated_ideal = ideal(R, R.(gens(sat_ideal))) + sat_ideal = ideal(R, R.(gens(ssat_ideal))) + set_attribute!(I, :saturated_ideal, sat_ideal) end - return I.saturated_ideal + return get_attribute(I, :saturated_ideal)::MPolyIdeal{RET} end function saturated_ideal( I::MPolyLocalizedIdeal{BRT, BRET, RT, RET, MPolyProductOfMultSets{BRT, BRET, RT, RET}} ) where {BRT, BRET, RT, RET} - if !isdefined(I, :saturated_ideal) + if !has_attribute(I, :saturated_ideal) W = base_ring(I) R = base_ring(W) J = ideal(R, numerator.(gens(I))) @@ -1389,9 +1438,9 @@ function saturated_ideal( L = Localization(U) J = saturated_ideal(L(J)) end - I.saturated_ideal = J + set_attribute!(I, :saturated_ideal, J) end - return I.saturated_ideal + return get_attribute(I, :saturated_ideal)::MPolyIdeal{RET} end @@ -1468,10 +1517,11 @@ function Base.in( f::MPolyLocalizedRingElem{BRT, BRET, RT, RET, MST}, I::MPolyLocalizedIdeal{BRT, BRET, RT, RET, MST} ) where {BRT, BRET, RT, RET, MST} + iszero(numerator(f)) && return true parent(f) == base_ring(I) || return false - #lbpa = groebner_basis(I) - #return iszero(reduce(f, lbpa)) - return numerator(f) in saturated_ideal(I) + lbpa = groebner_basis(I) + return iszero(reduce(f, lbpa)) + #return numerator(f) in saturated_ideal(I) end function Base.in( @@ -1538,7 +1588,6 @@ LocalizedBiPolyArray( I::Singular.sideal ) = LocalizedBiPolyArray(W, I, default_shift(W), false) - function Base.show(io::IO, I::MPolyLocalizedIdeal) print(io, "ideal in $(base_ring(I)) generated by the elements ") n = length(gens(I)) @@ -1673,14 +1722,23 @@ function Base.reduce( W = parent(f) W == oscar_ring(lbpa) || error("element does not belong to the Oscar ring of the biPolyArray") R = base_ring(W) - shift_hom = hom(R, R, [gen(R, i) + lbpa.shift[i] for i in (1:nvars(R))]) - singular_n = singular_ring(lbpa)(shift_hom(numerator(f))) - singular_n = Singular.reduce(singular_n, singular_gens(lbpa)) - if iszero(singular_n) - return zero(W) + if !iszero(shift(lbpa)) + shift_hom = hom(R, R, [gen(R, i) + lbpa.shift[i] for i in (1:nvars(R))]) + singular_n = singular_ring(lbpa)(shift_hom(numerator(f))) + singular_n = Singular.reduce(singular_n, singular_gens(lbpa)) + if iszero(singular_n) + return zero(W) + end + inv_shift_hom = hom(R,R, [gen(R, i) - lbpa.shift[i] for i in (1:nvars(R))]) + return W(inv_shift_hom(R(singular_n)), denominator(f)) + else + singular_n = singular_ring(lbpa)(numerator(f)) + singular_n = Singular.reduce(singular_n, singular_gens(lbpa)) + if iszero(singular_n) + return zero(W) + end + return W(R(singular_n), denominator(f)) end - inv_shift_hom = hom(R,R, [gen(R, i) - lbpa.shift[i] for i in (1:nvars(R))]) - return W(inv_shift_hom(R(singular_n)), denominator(f)) end @Markdown.doc """ @@ -1808,118 +1866,99 @@ end ######################################################################## # Homomorphisms of localized polynomial rings # ######################################################################## -# -# Let P = 𝕜[x₁,…,xₘ] and Q = 𝕜[y₁,…,yₙ] be polynomial rings -# Any homomorphism ϕ : P[U⁻¹] → Q[V⁻¹] is completely determined -# by the images of the variables -# -# ϕ(xᵢ) = aᵢ(y)/ bᵢ(y) -# -# where bᵢ(y) ∈ V for all i. Given such a list of images -# one can always define the associated homomorphism ϕ' : P → Q[V⁻¹]. -# This extends to a well defined homomorphism ϕ as above iff -# -# for all f ∈ U : ϕ'(f) is a unit in Q[V⁻¹] (*) -# -# and this is easily seen to be the case iff there exists an -# element c ∈ V such that c⋅ϕ'(f) ∈ V -mutable struct MPolyLocalizedRingHom{BaseRingType, BaseRingElemType, - RingType, RingElemType, DomainMultSetType, CodomainMultSetType - } <: AbsLocalizedRingHom{ - RingType, RingElemType, DomainMultSetType, CodomainMultSetType - } +mutable struct MPolyLocalizedRingHom{ + DomainType<:MPolyLocalizedRing, + CodomainType<:Ring, + RestrictedMapType<:Map + } <: AbsLocalizedRingHom{ + DomainType, + CodomainType, + RestrictedMapType + } # the domain of definition - V::MPolyLocalizedRing{BaseRingType, BaseRingElemType, - RingType, RingElemType, DomainMultSetType} - # the codomain - W::MPolyLocalizedRing{BaseRingType, BaseRingElemType, - RingType, RingElemType, CodomainMultSetType} - # the images of the variables - images::Vector{MPolyLocalizedRingElem{ - BaseRingType, BaseRingElemType, RingType, RingElemType, - CodomainMultSetType}} + W::DomainType + + ### the codomain + # Why do we need to store the codomain explicitly and not extract it from + # the restricted map? + # Because the restricted map res probably takes values in a strictly + # smaller or even completely different ring than S. If C is the codomain + # of res, then we impliticly assume (and check) that coercion from elements + # of C to S is possible. This is strictly weaker than requiring res to implement + # the full map interface. In that case, one would -- for example -- need to + # implement a new type just for the inclusion map R → R[U⁻¹] of a ring into + # its localization. + S::CodomainType + + # the restriction of the map to the base_ring of the domain + res::RestrictedMapType function MPolyLocalizedRingHom( - V::MPolyLocalizedRing{BRT, BRET, RT, RET, DMST}, - W::MPolyLocalizedRing{BRT, BRET, RT, RET, CMST}, - a::Vector{MPolyLocalizedRingElem{BRT, BRET, RT, RET, CMST}} - ) where {BRT, BRET, RT, RET, CMST, DMST} - R = base_ring(V) - S = base_ring(W) - k = coefficient_ring(R) - k == coefficient_ring(S) || error("the two polynomial rings are not defined over the same coefficient ring") - ngens(R) == length(a) || error("the number of images does not coincide with the number of variables") - parent_check = true - for x in a - parent_check = parent_check && parent(x) == W - denominator(x) in inverted_set(W) || error("the homomorphism is not well defined") + W::DomainType, + S::CodomainType, + res::RestrictedMapType; + check::Bool=true + ) where {DomainType<:MPolyLocalizedRing, CodomainType<:Ring, RestrictedMapType<:Map} + R = base_ring(W) + U = inverted_set(W) + domain(res) === R || error("the domain of the restricted map does not coincide with the base ring of the localization") + for f in U + isunit(S(res(f))) || error("image of $f is not a unit in the codomain") end - parent_check || error("the images of the variables are not elements of the codomain") - # Check whether this homomorphism is well defined - # TODO: Implement that! - return new{typeof(k), elem_type(k), typeof(R), elem_type(R), typeof(inverted_set(V)), typeof(inverted_set(W))}(V, W, a) + return new{DomainType, CodomainType, RestrictedMapType}(W, S, res) end end ### additional constructors function MPolyLocalizedRingHom( - R::RT, - W::MPolyLocalizedRing{BRT, BRET, RT, RET, CMST}, - a::Vector{MPolyLocalizedRingElem{BRT, BRET, RT, RET, CMST}} - ) where {BRT, BRET, RT, RET, CMST} - return MPolyLocalizedRingHom(Localization(units_of(R)), W, a) + R::MPolyRing, + S::Ring, + a::Vector{RingElemType} + ) where {RingElemType<:RingElem} + res = hom(R, S, a) + return MPolyLocalizedRingHom(Localization(units_of(R)), S, res) end function MPolyLocalizedRingHom( - V::MPolyLocalizedRing{BRT, BRET, RT, RET, DMST}, - S::RT, - a::Vector{RET} - ) where {BRT, BRET, RT, RET, DMST} - W = Localization(units_of(S)) - return MPolyLocalizedRingHom(V, W, W.(a)) + W::MPolyLocalizedRing, + S::Ring, + a::Vector{RingElemType} + ) where {RingElemType<:RingElem} + R = base_ring(W) + res = hom(R, S, a) + return MPolyLocalizedRingHom(W, S, res) end +hom(W::MPolyLocalizedRing, S::Ring, res::Map) = MPolyLocalizedRingHom(W, S, res) +hom(W::MPolyLocalizedRing, S::Ring, a::Vector{RET}) where {RET<:RingElem} = MPolyLocalizedRingHom(W, S, a) + ### required getter functions -domain(f::MPolyLocalizedRingHom) = f.V -codomain(f::MPolyLocalizedRingHom) = f.W -images(f::MPolyLocalizedRingHom) = f.images +domain(f::MPolyLocalizedRingHom) = f.W +codomain(f::MPolyLocalizedRingHom) = f.S +restricted_map(f::MPolyLocalizedRingHom) = f.res -### required functionality -function (f::MPolyLocalizedRingHom{BRT, BRET, RT, RET, DMST, CMST})(p::MPolyLocalizedRingElem{BRT, BRET, RT, RET, DMST}) where {BRT, BRET, RT, RET, DMST, CMST} - parent(p) == domain(f) || error("the given element does not belong to the domain of the map") - return evaluate(numerator(p), images(f))//evaluate(denominator(p), images(f)) +### implementing the Oscar map interface +function identity_map(W::T) where {T<:MPolyLocalizedRing} + MPolyLocalizedRingHom(W, W, identity_map(base_ring(W))) end -### overwriting of the generic method -function (f::MPolyLocalizedRingHom{BRT, BRET, RT, RET, DMST, CMST})(p::RET) where {BRT, BRET, RT, RET, DMST, CMST} - parent(p) == base_ring(domain(f)) || error("the given element does not belong to the domain of the map") - return evaluate(p, images(f)) +function compose( + f::MPolyLocalizedRingHom, + g::MPolyLocalizedRingHom + ) + codomain(f) === domain(g) || error("maps are not compatible") + return MPolyLocalizedRingHom(domain(f), codomain(g), compose(restricted_map(f), g)) end -### provide an extra method for elements of the base ring -function (f::MPolyLocalizedRingHom{BRT, BRET, RT, RET, DMST, CMST})(p::BRET) where {BRT, BRET, RT, RET, DMST, CMST} - parent(p) == coefficient_ring(base_ring(domain(f))) || error("the given element does not belong to the domain of the map") - return codomain(f)(p) -end +(f::MPolyLocalizedRingHom)(I::Ideal) = ideal(codomain(f), domain(f).(gens(I))) -### remove the ambiguity of methods in case the base ring is ℤ -function (f::MPolyLocalizedRingHom)(p::fmpz) - return codomain(f)(p) -end - -### implementing the Oscar map interface -identity_map(W::T) where {T<:MPolyLocalizedRing} = MPolyLocalizedRingHom(W, W, W.(gens(base_ring(W)))) -function compose( - f::MPolyLocalizedRingHom{BRT, BRET, RT, RET, MST1, MST2}, - g::MPolyLocalizedRingHom{BRT, BRET, RT, RET, MST2, MST3} - ) where {BRT, BRET, RT, RET, MST1, MST2, MST3} - codomain(f) == domain(g) || error("maps are not compatible") - return MPolyLocalizedRingHom(domain(f), codomain(g), g.(images(f))) +function ==(f::MPolyLocalizedRingHom, g::MPolyLocalizedRingHom) + domain(f) === domain(g) || return false + codomain(f) === codomain(g) || return false + for x in gens(base_ring(domain(f))) + f(x) == g(x) || return false + end + return true end -function preimage(f::MPolyLocalizedRingHom, I::MPolyLocalizedIdeal) - base_ring(I) == codomain(f) || error("the ideal does not belong to the codomain of the map") - R = base_ring(domain(f)) - error("not implemented") -end diff --git a/src/Rings/mpoly_types.jl b/src/Rings/mpoly_types.jl index dcea1c85d9d0..8c6136adfbce 100644 --- a/src/Rings/mpoly_types.jl +++ b/src/Rings/mpoly_types.jl @@ -1,4 +1,4 @@ -export mpoly_type, mpoly_ring_type +export mpoly_type, mpoly_ring_type, mpoly_dec_ring_type, mpoly_dec_type mpoly_ring_type(R::T) where {T<:AbstractAlgebra.Ring} = Generic.MPolyRing{elem_type(T)} mpoly_ring_type(::Type{T}) where {T<:AbstractAlgebra.Ring} = Generic.MPolyRing{elem_type(T)} @@ -30,3 +30,8 @@ mpoly_type(R::Nemo.GaloisField) = gfp_mpoly mpoly_type(::Type{Nemo.GaloisField}) = gfp_mpoly mpoly_type(a::gfp_elem) = gfp_mpoly mpoly_type(::Type{gfp_elem}) = gfp_mpoly + +mpoly_dec_ring_type(A::T) where {T<:MPolyRing} = mpoly_dec_ring_type(typeof(A)) +mpoly_dec_type(A::T) where {T<:MPolyRing} = mpoly_dec_type(typeof(A)) +mpoly_dec_ring_type(::Type{T}) where {S<:RingElem, T<:MPolyRing{S}} = MPolyRing_dec{S, T} +mpoly_dec_type(::Type{T}) where {S<:RingElem, T<:MPolyRing{S}} = MPolyElem_dec{S, elem_type(T)} diff --git a/src/Rings/mpolyquo-localizations.jl b/src/Rings/mpolyquo-localizations.jl index 2aabfc213305..10850c091a7f 100644 --- a/src/Rings/mpolyquo-localizations.jl +++ b/src/Rings/mpolyquo-localizations.jl @@ -9,7 +9,7 @@ export MPolyQuoLocalizedRingElem export numerator, denominator, parent, lift, isunit, inv, convert, lifted_numerator, lifted_denominator, fraction export MPolyQuoLocalizedRingHom -export domain, codomain, images +export domain, codomain, images, morphism_type, domain_type, codomain_type, restricted_map_type, ideal_type export helper_ring, helper_images, minimal_denominators, helper_eta, helper_kappa, common_denominator, helper_ideal export is_isomorphism, inverse @@ -73,7 +73,7 @@ Localization ``L = (𝕜[x₁,…,xₙ]/I)[S⁻¹]`` of a quotient of type `RingType` over a base ring ``𝕜`` of type `BaseRingType` at a multiplicative set ``S ⊂ P`` of type `MultSetType`. """ -mutable struct MPolyQuoLocalizedRing{ +@attributes mutable struct MPolyQuoLocalizedRing{ BaseRingType, BaseRingElemType, RingType, @@ -90,9 +90,6 @@ mutable struct MPolyQuoLocalizedRing{ Q::MPolyQuo{RingElemType} W::MPolyLocalizedRing{BaseRingType, BaseRingElemType, RingType, RingElemType, MultSetType} - # fields for caching - J::MPolyLocalizedIdeal{BaseRingType, BaseRingElemType, RingType, RingElemType, MultSetType} - function MPolyQuoLocalizedRing( R::RingType, I::MPolyIdeal{RingElemType}, @@ -116,11 +113,31 @@ mutable struct MPolyQuoLocalizedRing{ ambient_ring(S) == R || error("Multiplicative set does not belong to the ring") k = coefficient_ring(R) L = new{typeof(k), elem_type(k), typeof(R), RingElemType, MultSetType}(R, I, S, Q, W) - L.J = W(I) return L end end +### type getters +coefficient_ring_type(::Type{MPolyQuoLocalizedRing{BRT, BRET, RT, RET, MST}}) where {BRT, BRET, RT, RET, MST} = BRT +coefficient_ring_type(L::MPolyQuoLocalizedRing{BRT, BRET, RT, RET, MST}) where {BRT, BRET, RT, RET, MST} = coefficient_ring_type(typeof(L)) +coefficient_ring_elem_type(::Type{MPolyQuoLocalizedRing{BRT, BRET, RT, RET, MST}}) where {BRT, BRET, RT, RET, MST} = BRET +coefficient_ring_elem_type(L::MPolyQuoLocalizedRing{BRT, BRET, RT, RET, MST}) where {BRT, BRET, RT, RET, MST} = coefficient_ring_elem_type(typeof(L)) + +base_ring_type(::Type{MPolyQuoLocalizedRing{BRT, BRET, RT, RET, MST}}) where {BRT, BRET, RT, RET, MST} = RT +base_ring_type(L::MPolyQuoLocalizedRing{BRT, BRET, RT, RET, MST}) where {BRT, BRET, RT, RET, MST} = base_ring_type(typeof(L)) +base_ring_elem_type(::Type{MPolyQuoLocalizedRing{BRT, BRET, RT, RET, MST}}) where {BRT, BRET, RT, RET, MST} = RET +base_ring_elem_type(L::MPolyQuoLocalizedRing{BRT, BRET, RT, RET, MST}) where {BRT, BRET, RT, RET, MST} = base_ring_elem_type(typeof(L)) + +mult_set_type(::Type{MPolyQuoLocalizedRing{BRT, BRET, RT, RET, MST}}) where {BRT, BRET, RT, RET, MST} = MST +mult_set_type(L::MPolyQuoLocalizedRing{BRT, BRET, RT, RET, MST}) where {BRT, BRET, RT, RET, MST} = mult_set_type(typeof(L)) + +ideal_type(::Type{MPolyQuoLocalizedRing{BRT, BRET, RT, RET, MST}}) where {BRT, BRET, RT, RET, MST} = MPolyLocalizedIdeal{BRT, BRET, RT, RET, MST} +ideal_type(W::MPolyQuoLocalizedRing) = ideal_type(typeof(W)) + + + + + ### required getter functions base_ring(L::MPolyQuoLocalizedRing) = L.R inverted_set(L::MPolyQuoLocalizedRing) = L.S @@ -138,7 +155,12 @@ modulus(L::MPolyQuoLocalizedRing) = L.I For ``L = (𝕜[x₁,…,xₙ]/I)[S⁻¹]`` this returns ``IS⁻¹``. """ -localized_modulus(L::MPolyQuoLocalizedRing) = L.J +function localized_modulus(L::MPolyQuoLocalizedRing) + if !has_attribute(L, :localized_modulus) + set_attribute!(L, :localized_modulus, localized_ring(L)(modulus(L))) + end + return get_attribute(L, :localized_modulus)::ideal_type(L) +end @Markdown.doc """ quotient_ring(L::MPolyQuoLocalizedRing) @@ -173,8 +195,9 @@ function quo( ) where {BRT, BRET, RT, RET, MST} R = base_ring(W) S = inverted_set(W) - lbpa = groebner_basis(I) # In particular, this saturates the ideal - J = ideal(R, numerator.(oscar_gens(lbpa))) # the preimage of I in R + #lbpa = groebner_basis(I) # In particular, this saturates the ideal + #J = ideal(R, numerator.(oscar_gens(lbpa))) # the preimage of I in R + J = ideal(R, numerator.(gens(I))) return MPolyQuoLocalizedRing(R, J, S, quo(R, J)[1], W) end @@ -296,36 +319,32 @@ mutable struct MPolyQuoLocalizedRingElem{ function MPolyQuoLocalizedRingElem( L::MPolyQuoLocalizedRing{BaseRingType, BaseRingElemType, RingType, RingElemType, MultSetType}, a::RingElemType, - b::RingElemType + b::RingElemType; + check::Bool=true ) where {BaseRingType, BaseRingElemType, RingType, RingElemType, MultSetType} S = inverted_set(L) R = base_ring(L) parent(a) == parent(b) == R || error("elements do not belong to the correct ring") - if !(b in S) - error("can not convert to an admissible fraction") - #Q, _ = quo(R, saturated_ideal(localized_modulus(L))) - #l = write_as_linear_combination(Q(a), [Q(b)]) - #return L(lift(l[1])) - end + check && (b in S || error("denominator is not admissible")) return new{BaseRingType, BaseRingElemType, RingType, RingElemType, MultSetType}(L, a, b) end - - function MPolyQuoLocalizedRingElem( - L::MPolyQuoLocalizedRing{BaseRingType, BaseRingElemType, RingType, RingElemType, MPolyPowersOfElement{BaseRingType, BaseRingElemType, RingType, RingElemType}}, - a::RingElemType, - b::RingElemType - ) where {BaseRingType, BaseRingElemType, RingType, RingElemType} - S = inverted_set(L) - R = base_ring(L) - parent(a) == parent(b) == R || error("elements do not belong to the correct ring") - if !(b in inverted_set(L)) - return convert(L, a//b) - end - return new{BaseRingType, BaseRingElemType, RingType, RingElemType, MPolyPowersOfElement{BaseRingType, BaseRingElemType, RingType, RingElemType}}(L, a, b) - end end +### type getters +coefficient_ring_type(::Type{MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, MST}}) where {BRT, BRET, RT, RET, MST} = BRT +coefficient_ring_type(f::MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, MST}) where {BRT, BRET, RT, RET, MST} = base_ring_type(typeof(f)) +coefficient_ring_elem_type(::Type{MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, MST}}) where {BRT, BRET, RT, RET, MST} = BRET +coefficient_ring_elem_type(f::MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, MST}) where {BRT, BRET, RT, RET, MST} = base_ring_type(typeof(f)) + +base_ring_type(::Type{MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, MST}}) where {BRT, BRET, RT, RET, MST} = RT +base_ring_type(f::MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, MST}) where {BRT, BRET, RT, RET, MST} = base_ring_type(typeof(f)) +base_ring_elem_type(::Type{MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, MST}}) where {BRT, BRET, RT, RET, MST} = RET +base_ring_elem_type(f::MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, MST}) where {BRT, BRET, RT, RET, MST} = base_ring_type(typeof(f)) + +mult_set_type(::Type{MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, MST}}) where {BRT, BRET, RT, RET, MST} = MST +mult_set_type(f::MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, MST}) where {BRT, BRET, RT, RET, MST} = base_ring_type(typeof(f)) + ### required getter functions parent(a::MPolyQuoLocalizedRingElem) = a.L numerator(a::MPolyQuoLocalizedRingElem) = quotient_ring(parent(a))(a.numerator) @@ -334,6 +353,7 @@ denominator(a::MPolyQuoLocalizedRingElem) = quotient_ring(parent(a))(a.denominat ### additional getter functions quotient_ring(a::MPolyQuoLocalizedRingElem) = quotient_ring(parent(a)) localized_ring(a::MPolyQuoLocalizedRingElem) = localized_ring(parent(a)) +base_ring(a::MPolyQuoLocalizedRingElem) = base_ring(parent(a)) @Markdown.doc """ lifted_numerator(a::MPolyQuoLocalizedRingElem) @@ -359,36 +379,56 @@ For ``A//B ∈ (𝕜[x₁,…,xₙ]/I)[S⁻¹]`` this returns a representative """ fraction(a::MPolyQuoLocalizedRingElem) = lifted_numerator(a)//lifted_denominator(a) +### copying of elements +function Base.deepcopy_internal(f::MPolyQuoLocalizedRingElem, dict::IdDict) + return parent(f)(f, check=false) +end + ### required conversions -(L::MPolyQuoLocalizedRing{BaseRingType, BaseRingElemType, RingType, RingElemType, MultSetType})(f::RingElemType) where {BaseRingType, BaseRingElemType, RingType, RingElemType, MultSetType} = MPolyQuoLocalizedRingElem(L, f, one(f)) -(L::MPolyQuoLocalizedRing{BaseRingType, BaseRingElemType, RingType, RingElemType, MultSetType})(a::RingElemType, b::RingElemType) where {BaseRingType, BaseRingElemType, RingType, RingElemType, MultSetType} = MPolyQuoLocalizedRingElem(L, a, b) +(L::MPolyQuoLocalizedRing{BaseRingType, BaseRingElemType, RingType, RingElemType, MultSetType})(f::RingElemType) where {BaseRingType, BaseRingElemType, RingType, RingElemType<:RingElem, MultSetType} = MPolyQuoLocalizedRingElem(L, f, one(f), check=false) + +function (L::MPolyQuoLocalizedRing{ + BaseRingType, + BaseRingElemType, + RingType, + RingElemType, + MultSetType + })( + a::RingElemType, + b::RingElemType; + check::Bool=true + ) where { + BaseRingType, + BaseRingElemType, + RingType, + RingElemType, + MultSetType + } + check || return MPolyQuoLocalizedRingElem(L, a, b, check=false) + b in inverted_set(L) || return convert(L, a//b) + return MPolyQuoLocalizedRingElem(L, a, b, check=false) +end ### additional conversions -function (L::MPolyQuoLocalizedRing{BRT, BRET, RT, RET, MST})(f::Frac{RET}) where {BRT, BRET, RT, RET, MST} +function (L::MPolyQuoLocalizedRing{BRT, BRET, RT, RET, MST})(f::Frac{RET}; check::Bool=true) where {BRT, BRET, RT, RET, MST} R = base_ring(L) - return L(R(numerator(f)), R(denominator(f))) + return L(R(numerator(f)), R(denominator(f)), check=check) end -(L::MPolyQuoLocalizedRing{BaseRingType, BaseRingElemType, RingType, RingElemType, MultSetType})(a::T, b::T) where {BaseRingType, BaseRingElemType, RingType, RingElemType, MultSetType, T<:MPolyQuoElem{RingElemType}} = MPolyQuoLocalizedRingElem(L, lift(a), lift(b)) +(L::MPolyQuoLocalizedRing{BaseRingType, BaseRingElemType, RingType, RingElemType, MultSetType})(a::T, b::T; check::Bool=true) where {BaseRingType, BaseRingElemType, RingType, RingElemType, MultSetType, T<:MPolyQuoElem{RingElemType}} = L(lift(a), lift(b), check=check) -function (L::MPolyQuoLocalizedRing{BRT, BRET, RT, RET, MST})(f::MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, MST}) where {BRT, BRET, RT, RET, MST} - parent(f) == L && return f - return L(numerator(f), denominator(f)) +function (L::MPolyQuoLocalizedRing{BRT, BRET, RT, RET, MST})(f::MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, MST}; check::Bool=true) where {BRT, BRET, RT, RET, MST} + parent(f) === L && return f + return L(lifted_numerator(f), lifted_denominator(f), check=check) end -function (L::MPolyQuoLocalizedRing{BRT, BRET, RT, RET, MST})(f::MPolyLocalizedRingElem{BRT, BRET, RT, RET, MST}) where {BRT, BRET, RT, RET, MST} - if !(parent(f) == localized_ring(L)) - # it might still be the case that this can be transformed - # to an admissible element, so call the more sophisticated routine: - return L(numerator(f), denominator(f)) - end - lbpa = groebner_basis(localized_modulus(L)) - f = reduce(f, lbpa) - return MPolyQuoLocalizedRingElem(L, numerator(f), denominator(f)) +function (L::MPolyQuoLocalizedRing{BRT, BRET, RT, RET, MST})(f::MPolyLocalizedRingElem{BRT, BRET, RT, RET, MST}; check::Bool=true) where {BRT, BRET, RT, RET, MST} + parent(f) === localized_ring(L) && return L(numerator(f), denominator(f), check=false) + return L(numerator(f), denominator(f), check=check) end -function (L::MPolyQuoLocalizedRing{BRT, BRET, RT, RET, MST})(f::MPolyLocalizedRingElem{BRT, BRET, RT, RET, MST}) where {BRT, BRET, RT, RET, MST<:MPolyComplementOfKPointIdeal} - return L(numerator(f), denominator(f)) +function (L::MPolyQuoLocalizedRing{BRT, BRET, RT, RET, MST})(f::MPolyLocalizedRingElem{BRT, BRET, RT, RET, MST}; check::Bool=true) where {BRT, BRET, RT, RET, MST<:MPolyComplementOfKPointIdeal} + return L(numerator(f), denominator(f), check=check) end function (L::MPolyQuoLocalizedRing{BRT, BRET, RT, RET, MST})(f::MPolyQuoElem{RET}) where {BRT, BRET, RT, RET, MST} @@ -405,20 +445,26 @@ For ``f = A//B ∈ (𝕜[x₁,…,xₙ]/I)[S⁻¹]`` this returns a representati """ lift(f::MPolyQuoLocalizedRingElem) = localized_ring(f)(lifted_numerator(f), lifted_denominator(f)) -isunit(f::MPolyQuoLocalizedRingElem) = one(localized_ring(parent(f))) in localized_modulus(parent(f)) + ideal(localized_ring(parent(f)), lift(f)) +function isunit(f::MPolyQuoLocalizedRingElem) + lifted_numerator(f) in inverted_set(parent(f)) && return true + return one(localized_ring(parent(f))) in localized_modulus(parent(f)) + ideal(localized_ring(parent(f)), lift(f)) +end function isunit(L::MPolyQuoLocalizedRing, f::MPolyLocalizedRingElem) parent(f) == localized_ring(L) || error("element does not belong to the correct ring") + numerator(f) in inverted_set(L) && return true one(localized_ring(L)) in localized_modulus(L) + ideal(localized_ring(L), f) end function isunit(L::MPolyQuoLocalizedRing{BRT, BRET, RT, RET, MST}, f::RET) where {BRT, BRET, RT, RET, MST} parent(f) == base_ring(L) || error("element does not belong to the correct ring") + f in inverted_set(L) && return true return one(localized_ring(L)) in localized_modulus(L) + ideal(localized_ring(L), localized_ring(L)(f)) end function isunit(L::MPolyQuoLocalizedRing{BRT, BRET, RT, RET, MST}, f::MPolyQuoElem{RET}) where {BRT, BRET, RT, RET, MST} parent(f) == quotient_ring(L) || error("element does not belong to the correct ring") + lift(f) in inverted_set(L) && return true one(localized_ring(L)) in localized_modulus(L) + ideal(localized_ring(L), localized_ring(L)(f)) end @@ -462,7 +508,7 @@ function inv(L::MPolyQuoLocalizedRing{BRT, BRET, RT, RET, MPolyPowersOfElement{B end function inv(f::MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, MPolyPowersOfElement{BRT, BRET, RT, RET}}) where {BRT, BRET, RT, RET} - return parent(f)(denominator(f))*inv(parent(f), numerator(f)) + return parent(f)(denominator(f), numerator(f)) end ### @@ -499,7 +545,7 @@ function convert( lower = pop!(powers_of_d) while length(powers_of_d) > 0 middle = lower*pop!(powers_of_d) - (result, coefficient) = divides(Q(a*last(powers_of_d)), Q(b)) + (result, coefficient) = divides(Q(a*middle), Q(b)) if result upper = middle else @@ -513,10 +559,10 @@ end ### arithmetic ######################################################### function +(a::T, b::T) where {T<:MPolyQuoLocalizedRingElem} parent(a) == parent(b) || error("the arguments do not have the same parent ring") - if denominator(a) == denominator(b) - return reduce_fraction((parent(a))(lifted_numerator(a) + lifted_numerator(b), lifted_denominator(a))) + if lifted_denominator(a) == lifted_denominator(b) + return reduce_fraction((parent(a))(lifted_numerator(a) + lifted_numerator(b), lifted_denominator(a), check=false)) end - return reduce_fraction((parent(a))(lifted_numerator(a)*lifted_denominator(b) + lifted_numerator(b)*lifted_denominator(a), lifted_denominator(a)*lifted_denominator(b))) + return reduce_fraction((parent(a))(lifted_numerator(a)*lifted_denominator(b) + lifted_numerator(b)*lifted_denominator(a), lifted_denominator(a)*lifted_denominator(b), check=false)) end # TODO: improve this method. @@ -527,30 +573,30 @@ end function -(a::T, b::T) where {T<:MPolyQuoLocalizedRingElem} parent(a) == parent(b) || error("the arguments do not have the same parent ring") - if denominator(a) == denominator(b) - return reduce_fraction((parent(a))(lifted_numerator(a) - lifted_numerator(b), lifted_denominator(a))) + if lifted_denominator(a) == lifted_denominator(b) + return reduce_fraction((parent(a))(lifted_numerator(a) - lifted_numerator(b), lifted_denominator(a), check=false)) end - return reduce_fraction((parent(a))(lifted_numerator(a)*lifted_denominator(b) - lifted_numerator(b)*lifted_denominator(a), lifted_denominator(a)*lifted_denominator(b))) + return reduce_fraction((parent(a))(lifted_numerator(a)*lifted_denominator(b) - lifted_numerator(b)*lifted_denominator(a), lifted_denominator(a)*lifted_denominator(b), check=false)) end function *(a::T, b::T) where {T<:MPolyQuoLocalizedRingElem} parent(a) == parent(b) || error("the arguments do not have the same parent ring") - return reduce_fraction((parent(a))(lifted_numerator(a)*lifted_numerator(b), lifted_denominator(a)*lifted_denominator(b))) + return reduce_fraction((parent(a))(lifted_numerator(a)*lifted_numerator(b), lifted_denominator(a)*lifted_denominator(b), check=false)) end -function *(a::RET, b::MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, MST}) where {BRT, BRET, RT, RET <: RingElem, MST} - return reduce_fraction((parent(b))(a*lifted_numerator(b), lifted_denominator(b))) +function *(a::RET, b::MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, MST}) where {BRT<:Ring, BRET<:RingElem, RT<:Ring, RET <: RingElem, MST} + return reduce_fraction((parent(b))(a*lifted_numerator(b), lifted_denominator(b), check=false)) end -function *(a::MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, MST}, b::RET) where {BRT, BRET, RT, RET <: RingElem, MST} +function *(a::MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, MST}, b::RET) where {BRT<:Ring, BRET<:RingElem, RT<:Ring, RET <: RingElem, MST} return b*a end -function *(a::BRET, b::MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, MST}) where {BRT, BRET, RT, RET <: RingElem, MST} - return reduce_fraction((parent(b))(base_ring(b)(a)*lifted_numerator(b), lifted_denominator(b))) +function *(a::BRET, b::MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, MST}) where {BRT<:Ring, BRET<:RingElem, RT<:Ring, RET <: RingElem, MST} + return reduce_fraction((parent(b))(a*lifted_numerator(b), lifted_denominator(b), check=false)) end -function *(a::MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, MST}, b::BRET) where {BRT, BRET, RT, RET <: RingElem, MST} +function *(a::MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, MST}, b::BRET) where {BRT<:Ring, BRET<:RingElem, RT<:Ring, RET <: RingElem, MST} return b*a end @@ -585,42 +631,43 @@ end function ==(a::T, b::T) where {T<:MPolyQuoLocalizedRingElem} parent(a) == parent(b) || error("the arguments do not have the same parent ring") - return numerator(a)*denominator(b) == numerator(b)*denominator(a) + return lifted_numerator(a)*lifted_denominator(b) - lifted_numerator(b)*lifted_denominator(a) in localized_modulus(parent(a)) end function ^(a::MPolyQuoLocalizedRingElem, i::fmpz) - return parent(a)(lifted_numerator(a)^i, lifted_denominator(a)^i) + return parent(a)(lifted_numerator(a)^i, lifted_denominator(a)^i, check=false) end function ^(a::MPolyQuoLocalizedRingElem, i::Integer) - return parent(a)(lifted_numerator(a)^i, lifted_denominator(a)^i) + return parent(a)(lifted_numerator(a)^i, lifted_denominator(a)^i, check=false) end function isone(a::MPolyQuoLocalizedRingElem) - a = reduce_fraction(a) - return (numerator(a) == denominator(a)) + return lifted_numerator(a) - lifted_denominator(a) in localized_modulus(parent(a)) end function iszero(a::MPolyQuoLocalizedRingElem) - a = reduce_fraction(a) - return iszero(numerator(a)) + iszero(lifted_numerator(a)) && return true + return lifted_numerator(a) in localized_modulus(parent(a)) end ### enhancement of the arithmetic function reduce_fraction(f::MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, MST}) where {BRT, BRET, RT, RET, MST<:MPolyPowersOfElement} + return f h = lift(f) h = reduce(h, groebner_basis(localized_modulus(parent(f)))) g = gcd(numerator(h), denominator(h)) - h = parent(h)(divexact(numerator(h), g), divexact(denominator(h), g)) - return parent(f)(h) + h = parent(h)(divexact(numerator(h), g), divexact(denominator(h), g), check=false) + return parent(f)(h, check=false) end # for local orderings, reduction does not give the correct result. function reduce_fraction(f::MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, MST}) where {BRT, BRET, RT, RET, MST<:MPolyComplementOfKPointIdeal} + return f h = lift(f) g = gcd(numerator(h), denominator(h)) - h = parent(h)(divexact(numerator(h), g), divexact(denominator(h), g)) - return parent(f)(h) + h = parent(h)(divexact(numerator(h), g), divexact(denominator(h), g), check=false) + return parent(f)(h, check=false) end ### implementation of Oscar's general ring interface @@ -648,6 +695,7 @@ function ideal(L::MPolyQuoLocalizedRing{BRT, BRET, RT, RET, MST}, end ideal(L::MPolyQuoLocalizedRing, g::T) where {T<:RingElement} = ideal(L, [g]) +ideal(L::MPolyQuoLocalizedRing, g::T) where {T<:MPolyQuoLocalizedRingElem} = ideal(L, [g]) @Markdown.doc """ bring_to_common_denominator(f::Vector{T}) where {T<:MPolyQuoLocalizedRingElem} @@ -835,240 +883,208 @@ variables ``ϕ(xᵢ) ∈ (𝕜[y₁,…,yₙ]/J)[T⁻¹]`` so that the constructor takes as input the triple ``((𝕜[x₁,…,xₘ]/I)[S⁻¹], (𝕜[y₁,…,yₙ]/J)[T⁻¹], [ϕ(x₁),…,ϕ(xₘ)])``. """ -mutable struct MPolyQuoLocalizedRingHom{ - BaseRingType, - BaseRingElemType, - RingType, - RingElemType, - DomainMultSetType, - CodomainMultSetType - } <: AbsLocalizedRingHom{ - RingType, RingElemType, DomainMultSetType, CodomainMultSetType - } - domain::MPolyQuoLocalizedRing - codomain::MPolyQuoLocalizedRing - images::Vector{MPolyLocalizedRingElem} - - # variables for caching - helper_ring::RingType - helper_images::Vector{RingElemType} - minimal_denominators::Vector{RingElemType} - eta::AlgHom{BaseRingElemType} - kappa::AlgHom{BaseRingElemType} - - inverse::MPolyQuoLocalizedRingHom{ - BaseRingType, - BaseRingElemType, - RingType, - RingElemType, - CodomainMultSetType, - DomainMultSetType - } +@attributes mutable struct MPolyQuoLocalizedRingHom{ + DomainType<:MPolyQuoLocalizedRing, + CodomainType<:Ring, + RestrictedMapType<:Map + } <: AbsLocalizedRingHom{ + DomainType, + CodomainType, + RestrictedMapType + } + domain::DomainType + codomain::CodomainType + res::RestrictedMapType function MPolyQuoLocalizedRingHom( - L::MPolyQuoLocalizedRing{BRT, BRET, RT, RET, DMST}, - M::MPolyQuoLocalizedRing{BRT, BRET, RT, RET, CMST}, - a::Vector{MPolyLocalizedRingElem{BRT, BRET, RT, RET, CMST}} - ) where {BRT, BRET, RT, RET, CMST, DMST} + L::DomainType, + S::CodomainType, + res::RestrictedMapType; + check::Bool=true + ) where {DomainType<:MPolyQuoLocalizedRing, CodomainType<:Ring, RestrictedMapType<:Map} R = base_ring(L) - S = base_ring(M) - k = coefficient_ring(R) - k == coefficient_ring(S) || error("the two polynomial rings are not defined over the same coefficient ring") - ngens(R) == length(a) || error("the number of images does not coincide with the number of variables") - parent_check = true - for x in a - parent_check = parent_check && parent(x) == localized_ring(M) + R === domain(res) || error("restriction map is not compatible") + U = inverted_set(L) + if check + for f in U + isunit(S(res(f))) || error("map is not well defined") + end + for g in gens(modulus(L)) + iszero(S(res(g))) || error("map is not well defined") + end end - parent_check || error("the images of the variables are not elements of the codomain") - # Check whether this homomorphism is well defined - # TODO: Implement that! - return new{typeof(k), elem_type(k), typeof(R), elem_type(R), typeof(inverted_set(L)), typeof(inverted_set(M))}(L, M, a) + return new{DomainType, CodomainType, RestrictedMapType}(L, S, res) end end +### type getters +domain_type(::Type{MPolyQuoLocalizedRingHom{D, C, M}}) where {D, C, M} = D +domain_type(f::MPolyQuoLocalizedRingHom) = domain_type(typeof(f)) +codomain_type(::Type{MPolyQuoLocalizedRingHom{D, C, M}}) where {D, C, M} = C +codomain_type(f::MPolyQuoLocalizedRingHom) = domain_type(typeof(f)) +restricted_map_type(::Type{MPolyQuoLocalizedRingHom{D, C, M}}) where {D, C, M} = M +restricted_map_type(f::MPolyQuoLocalizedRingHom) = domain_type(typeof(f)) + +morphism_type(::Type{R}, ::Type{S}) where {R<:MPolyQuoLocalizedRing, S<:Ring} = MPolyQuoLocalizedRingHom{R, S, morphism_type(base_ring_type(R), S)} +morphism_type(L::MPolyQuoLocalizedRing, S::Ring) = morphism_type(typeof(L), typeof(S)) + +### TODO: Move to other file +morphism_type(::Type{R}, ::Type{S}) where {R<:MPolyRing, S<:Ring} = Oscar.MPolyAnyMap{R, S, Nothing, elem_type(S)} +morphism_type(R::MPolyRing, S::Ring) = morphism_type(typeof(R), typeof(S)) + ### required getter functions domain(f::MPolyQuoLocalizedRingHom) = f.domain codomain(f::MPolyQuoLocalizedRingHom) = f.codomain + @Markdown.doc """ - images(f::MPolyQuoLocalizedRingHom) + restricted_map(f::MPolyQuoLocalizedRingHom) -For a homomorphism ``ϕ : (𝕜[x₁,…,xₘ]/I)[S⁻¹] → (𝕜[y₁,…,yₙ]/J)[T⁻¹]`` -this returns the vector ``[ϕ(x₁),…,ϕ(xₘ)]``. +For a homomorphism ``ϕ : (𝕜[x₁,…,xₘ]/I)[U⁻¹] → S``this returns +the canonically associated map ``ϕ' : 𝕜[x₁,…,xₘ] → S``. """ -images(f::MPolyQuoLocalizedRingHom) = f.images +restricted_map(f::MPolyQuoLocalizedRingHom) = f.res -### required functionality -function (f::MPolyQuoLocalizedRingHom{BRT, BRET, RT, RET, DMST, CMST})( - p::MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, DMST} - ) where {BRT, BRET, RT, RET, DMST, CMST} - parent(p) == domain(f) || error("the given element does not belong to the domain of the map") - return codomain(f)(evaluate(lifted_numerator(p), images(f))//evaluate(lifted_denominator(p), images(f))) +function images(f::MPolyQuoLocalizedRingHom{<:Any, <:MPolyQuoLocalizedRing}) + return lift.((codomain(f)).(restricted_map(f).(gens(base_ring(domain(f)))))) end ### additional constructors function MPolyQuoLocalizedRingHom( - L::MPolyQuoLocalizedRing{BRT, BRET, RT, RET, DMST}, - M::MPolyQuoLocalizedRing{BRT, BRET, RT, RET, CMST}, - a::Vector{MPolyQuoLocalizedRingElem{BRT, BRET, RT, RET, CMST}} - ) where {BRT, BRET, RT, RET, CMST, DMST} - return MPolyQuoLocalizedRingHom(L, M, lift.(a)) -end - -function MPolyQuoLocalizedRingHom( - L::MPolyQuoLocalizedRing{BRT, BRET, RT, RET, DMST}, - M::MPolyQuoLocalizedRing{BRT, BRET, RT, RET, CMST}, - a::Vector{T} - ) where {BRT, BRET, RT, RET, CMST, DMST, T<:Any} - return MPolyQuoLocalizedRingHom(L, M, lift.(M.(a))) + L::MPolyQuoLocalizedRing, + S::Ring, + a::Vector{T}; + check::Bool=true + ) where {T<:RingElem} + return MPolyQuoLocalizedRingHom(L, S, hom(base_ring(L), S, a), check=check) end +hom(L::MPolyQuoLocalizedRing, S::Ring, a::Vector{T}) where {T<:RingElem} = MPolyQuoLocalizedRingHom(L, S, a) -### additional functionality -function (f::MPolyQuoLocalizedRingHom{BRT, BRET, RT, RET, DMST, CMST})( - p::MPolyLocalizedRingElem{BRT, BRET, RT, RET, DMST} - ) where {BRT, BRET, RT, RET, DMST, CMST} - parent(p) == localized_ring(domain(f)) || error("the given element does not belong to the domain of the map") - return codomain(f)(evaluate(numerator(p), images(f)))*inv(codomain(f)(evaluate(denominator(p), images(f)))) -end - -function (f::MPolyQuoLocalizedRingHom{BRT, BRET, RT, RET, DMST, CMST})( - p::MPolyQuoElem{RET} - ) where {BRT, BRET, RT, RET, DMST, CMST} - parent(p) == quotient_ring(domain(f)) || error("the given element does not belong to the domain of the map") - return codomain(f)(evaluate(lift(p), images(f))) -end - -### overwriting of the generic method -function (f::MPolyQuoLocalizedRingHom{BRT, BRET, RT, RET, DMST, CMST})( - p::RET - ) where {BRT, BRET, RT, RET, DMST, CMST} - parent(p) == base_ring(domain(f)) || error("the given element does not belong to the domain of the map") - return codomain(f)(evaluate(p, images(f))) -end - -### provide an extra method for elements of the base ring -function (f::MPolyQuoLocalizedRingHom{BRT, BRET, RT, RET, DMST, CMST})(p::BRET) where {BRT, BRET, RT, RET, DMST, CMST} - parent(p) == coefficient_ring(base_ring(domain(f))) || error("the given element does not belong to the domain of the map") - return codomain(f)(p) +### implementing the Oscar map interface +function identity_map(W::T) where {T<:MPolyQuoLocalizedRing} + MPolyQuoLocalizedRingHom(W, W, identity_map(base_ring(W))) end -### remove the ambiguity of methods in case the base ring is ZZ -function (f::MPolyQuoLocalizedRingHom)(p::fmpz) - return codomain(f)(p) +### we need to overwrite the following method because of the +# uncommon implementation of the numerator and denominator methods +function (f::MPolyQuoLocalizedRingHom)(a::AbsLocalizedRingElem) + parent(a) === domain(f) || return f(domain(f)(a)) + return codomain(f)(restricted_map(f)(lifted_numerator(a)))*inv(codomain(f)(restricted_map(f)(lifted_denominator(a)))) end -### implementing the Oscar map interface -identity_map(W::T) where {T<:MPolyQuoLocalizedRing} = MPolyQuoLocalizedRingHom(W, W, W.(gens(base_ring(W)))) function compose( - f::MPolyQuoLocalizedRingHom{BRT, BRET, RT, RET, MST1, MST2}, - g::MPolyQuoLocalizedRingHom{BRT, BRET, RT, RET, MST2, MST3} - ) where {BRT, BRET, RT, RET, MST1, MST2, MST3} - codomain(f) == domain(g) || error("maps are not compatible") - return MPolyQuoLocalizedRingHom(domain(f), codomain(g), g.(images(f))) + f::MPolyQuoLocalizedRingHom, + g::MPolyQuoLocalizedRingHom + ) + codomain(f) === domain(g) || error("maps are not compatible") + if codomain(restricted_map(f)) === domain(g) + return MPolyQuoLocalizedRingHom(domain(f), codomain(g), compose(restricted_map(f), g)) + elseif codomain(restricted_map(f)) === base_ring(domain(g)) + h = hom(base_ring(domain(g)), domain(g), domain(g).(gens(base_ring(domain(g))))) + return MPolyQuoLocalizedRingHom(domain(f), codomain(g), compose(compose(restricted_map(f), h), g)) + end + ### The fallback version. Careful: This might not carry over maps on the coefficient rings! + R = base_ring(domain(f)) + return MPolyQuoLocalizedRingHom(domain(f), codomain(g), hom(R, codomain(g), [g(f(x)) for x in gens(R)])) end -function Base.show(io::IO, f::MPolyQuoLocalizedRingHom) - print(io, "Ring homomorphism from $(domain(f)) to $(codomain(f)) mapping the generators to $(images(f))") -end +(f::MPolyQuoLocalizedRingHom)(I::Ideal) = ideal(codomain(f), f.(domain(f).(gens(I)))) function ==(f::MPolyQuoLocalizedRingHom, g::MPolyQuoLocalizedRingHom) - domain(f) == domain(g) || return false - codomain(f) == codomain(g) || return false - a = images(f) - b = images(g) - n = length(a) - for i in 1:n - a[i] == b[i] || return false + domain(f) === domain(g) || return false + codomain(f) === codomain(g) || return false + for x in gens(base_ring(domain(f))) + f(x) == g(x) || return false end return true end ### helper_ring # Sets up the ring S[c⁻¹] from the Lemma. -function helper_ring(f::MPolyQuoLocalizedRingHom{BRT, BRET, RT, RET, DMST, CMST}) where {BRT, BRET, RT, RET, DMST, CMST} - if isdefined(f, :helper_ring) - return f.helper_ring - end - f.minimal_denominators = Vector{RET}() - R = base_ring(domain(f)) - S = base_ring(codomain(f)) - p = one(S) - - for d in [denominator(y) for y in images(f)] - g = gcd(d, p) - d_min = divexact(d, g) - push!(f.minimal_denominators, d) - p = p*d_min +function helper_ring(f::MPolyQuoLocalizedRingHom{<:Any, <:MPolyQuoLocalizedRing}) + if !has_attribute(f, :helper_ring) + minimal_denominators = Vector{base_ring_elem_type(domain(f))}() + R = base_ring(domain(f)) + S = base_ring(codomain(f)) + p = one(S) + + for d in [denominator(y) for y in images(f)] + g = gcd(d, p) + d_min = divexact(d, g) + push!(minimal_denominators, d) + p = p*d_min + end + set_attribute!(f, :minimal_denominators, minimal_denominators) + + help_ring, help_kappa, theta = _add_variables(S, ["θ"]) + set_attribute!(f, :helper_ring, help_ring) + kappa = help_kappa + set_attribute!(f, :kappa, help_kappa) + c_inv = theta[1] + helper_images = [kappa(numerator(y))*c_inv*kappa(divexact(p, denominator(y))) for y in images(f)] + set_attribute!(f, :helper_images, helper_images) + eta = hom(R, help_ring, helper_images) + set_attribute!(f, :eta, eta) end - - help_ring, help_kappa, theta = _add_variables(S, ["θ"]) - f.helper_ring = help_ring - f.kappa = help_kappa - c_inv = theta[1] - f.helper_images = [f.kappa(numerator(y))*c_inv*f.kappa(divexact(p, denominator(y))) for y in images(f)] - f.eta = AlgebraHomomorphism(R, help_ring, f.helper_images) - return f.helper_ring + return get_attribute(f, :helper_ring)::base_ring_type(domain(f)) end function helper_images( - f::MPolyQuoLocalizedRingHom{BRT, BRET, RT, RET, DMST, CMST} - ) where {BRT, BRET, RT, RET, DMST, CMST} - if !isdefined(f, :helper_images) + f::MPolyQuoLocalizedRingHom{<:Any, <:MPolyQuoLocalizedRing} + ) + if !has_attribute(f, :helper_images) helper_ring(f) end - return f.helper_images + return get_attribute(f, :helper_images)::Vector{base_ring_elem_type(domain(f))} end function minimal_denominators( - f::MPolyQuoLocalizedRingHom{BRT, BRET, RT, RET, DMST, CMST} - ) where {BRT, BRET, RT, RET, DMST, CMST} - if !isdefined(f, :minimal_denominators) + f::MPolyQuoLocalizedRingHom{<:Any, <:MPolyQuoLocalizedRing} + ) + if !has_attribute(f, :minimal_denominators) helper_ring(f) end - return f.minimal_denominators + return get_attribute!(f, :minimal_denominators)::Vector{base_ring_elem_type(domain(f))} end function helper_eta( - f::MPolyQuoLocalizedRingHom{BRT, BRET, RT, RET, DMST, CMST} - ) where {BRT, BRET, RT, RET, DMST, CMST} - if !isdefined(f, :eta) + f::MPolyQuoLocalizedRingHom{<:Any, <:MPolyQuoLocalizedRing} + ) + if !has_attribute(f, :eta) helper_ring(f) end - return f.eta + return get_attribute(f, :eta)::morphism_type(base_ring_type(domain(f)), base_ring_type(domain(f))) end function helper_kappa( - f::MPolyQuoLocalizedRingHom{BRT, BRET, RT, RET, DMST, CMST} - ) where {BRT, BRET, RT, RET, DMST, CMST} - if !isdefined(f, :kappa) + f::MPolyQuoLocalizedRingHom{<:Any, <:MPolyQuoLocalizedRing} + ) + if !has_attribute(f, :kappa) helper_ring(f) end - return f.kappa + return get_attribute(f, :kappa)::morphism_type(base_ring_type(domain(f)), base_ring_type(domain(f))) end function common_denominator( - f::MPolyQuoLocalizedRingHom{BRT, BRET, RT, RET, DMST, CMST} - ) where {BRT, BRET, RT, RET, DMST, CMST} - if !isdefined(f, :minimal_denominators) + f::MPolyQuoLocalizedRingHom{<:Any, <:MPolyQuoLocalizedRing} + ) + if !has_attribute(f, :minimal_denominators) helper_ring(f) end - return (length(f.minimal_denominators) == 0 ? one(base_ring(codomain(f))) : prod(f.minimal_denominators)) + d = get_attribute(f, :minimal_denominators)::Vector{base_ring_elem_type(domain(f))} + return (length(d) == 0 ? one(base_ring(codomain(f))) : prod(d)) end function helper_ideal( - f::MPolyQuoLocalizedRingHom{BRT, BRET, RT, RET, DMST, CMST} - ) where {BRT, BRET, RT, RET, DMST, CMST} - if !isdefined(f, :helper_ring) - helper_ring(f) - end + f::MPolyQuoLocalizedRingHom{<:Any, <:MPolyQuoLocalizedRing} + ) Sc = helper_ring(f) return ideal(Sc, one(Sc)-last(gens(Sc))*helper_kappa(f)(common_denominator(f))) end # return the localized ring as a quotient of a polynomial ring using Rabinowitsch's trick. function as_affine_algebra( - L::MPolyQuoLocalizedRing{BRT, BRET, RT, RET, - MPolyPowersOfElement{BRT, BRET, RT, RET}}; + L::MPolyQuoLocalizedRing{<:Any, <:Any, <:Any, <:Any, <:MPolyPowersOfElement}; inverse_name::String="θ" ) where {BRT, BRET, RT, RET} R = base_ring(L) @@ -1079,11 +1095,10 @@ function as_affine_algebra( return A, I, f, phi, theta end - function is_isomorphism( - phi::MPolyQuoLocalizedRingHom{BRT, BRET, RT, RET, MST, MST} - ) where {BRT, BRET, RT, RET, MST<:MPolyPowersOfElement{BRT, BRET, RT, RET}} - if isdefined(phi, :inverse) + phi::MPolyQuoLocalizedRingHom{T, T} + ) where {T<:MPolyQuoLocalizedRing} + if has_attribute(phi, :inverse) return true end K = domain(phi) @@ -1133,13 +1148,13 @@ function is_isomorphism( pushfirst!(imagesB, prod(denoms)) # perform a sanity check - phiAB = AlgebraHomomorphism(A, B, imagesB) + phiAB = hom(A, B, imagesB) issubset(ideal(B, [phiAB(g) for g in gens(I)]), J) || error("the homomorphism is not well defined") # assemble a common ring in which the equations for the graph of phi can # be realized. C, j1, B_vars = _add_variables_first(A, String.(symbols(B))) - j2 = AlgebraHomomorphism(B, C, B_vars) + j2 = hom(B, C, B_vars) G = ideal(C, [j1(gens(A)[i]) - j2(imagesB[i]) for i in (1:length(gens(A)))]) + ideal(C, j2.(gens(J))) + ideal(C, j1.(gens(I))) singC, _ = Singular.PolynomialRing(Oscar.singular_ring(base_ring(C)), String.(symbols(C)), @@ -1179,72 +1194,58 @@ function is_isomorphism( # Make sure, this ordering is used again for the sanity check below! invJ == I || return false - phi.inverse = MPolyQuoLocalizedRingHom(L, K, pre_images) - phi.inverse.inverse = phi + set_attribute!(phi, :inverse, MPolyQuoLocalizedRingHom(L, K, pre_images)) + psi = get_attribute(phi, :inverse) + set_attribute!(psi, :inverse, phi) return true end -function inverse(phi::MPolyQuoLocalizedRingHom{BRT, BRET, RT, RET, MST, MST} - ) where {BRT, BRET, RT, RET, MST<:MPolyPowersOfElement{BRT, BRET, RT, RET}} - is_isomorphism(phi) || error("the given morphism is not an isomorphism") - return phi.inverse +function inverse( + f::MPolyQuoLocalizedRingHom{ + <:MPolyQuoLocalizedRing{<:Any, <:Any, <:Any, <:Any, + <:MPolyPowersOfElement + }, + <:MPolyQuoLocalizedRing{<:Any, <:Any, <:Any, <:Any, + <:MPolyPowersOfElement + } + } + ) + is_isomorphism(f) || error("the given morphism is not an isomorphism") + return get_attribute(f, :inverse)::morphism_type(codomain(f), domain(f)) end -######################################################################## -# Functionality for maps and ideals # -######################################################################## -# -# The methods have to be adapted to the type of localization in the -# target. It needs to be assured that all components which are invisible -# in the localization, are indeed discarded. - -function (f::MPolyQuoLocalizedRingHom{BRT, BRET, RT, RET, DMST, CMST})( - I::MPolyLocalizedIdeal{BRT, BRET, RT, RET, DMST} - ) where {BRT<:Ring, BRET<:RingElement, RT<:MPolyRing, RET<:MPolyElem, - DMST<:AbsMultSet{RT, RET}, CMST<:MPolyPowersOfElement{BRT, BRET, RT, RET} - } - base_ring(I) == localized_ring(domain(f)) || error("ideal does not lay in the correct ring") - imgs = f.(gens(I)) - return ideal(localized_ring(codomain(f)), lift.(imgs)) -end - -function (f::MPolyQuoLocalizedRingHom{BRT, BRET, RT, RET, DMST, CMST})( - I::MPolyIdeal{RET} - ) where {BRT<:Ring, BRET<:RingElement, RT<:MPolyRing, RET<:MPolyElem, - DMST<:AbsMultSet{RT, RET}, CMST<:MPolyPowersOfElement{BRT, BRET, RT, RET} - } - base_ring(I) == base_ring(domain(f)) || error("ideal does not lay in the correct ring") - return f(domain(f)(I)) -end - - ### adds the variables with names specified in v to the polynomial # ring R and returns a triple consisting of the new ring, the embedding # of the original one, and a list of the new variables. function _add_variables(R::RingType, v::Vector{String}) where {RingType<:MPolyRing} ext_R, _ = PolynomialRing(coefficient_ring(R), vcat(symbols(R), Symbol.(v))) n = length(gens(R)) - phi = AlgebraHomomorphism(R, ext_R, gens(ext_R)[1:n]) + phi = hom(R, ext_R, gens(ext_R)[1:n]) return ext_R, phi, gens(ext_R)[(length(gens(R))+1):length(gens(ext_R))] end function _add_variables_first(R::RingType, v::Vector{String}) where {RingType<:MPolyRing} ext_R, _ = PolynomialRing(coefficient_ring(R), vcat(Symbol.(v), symbols(R))) n = length(gens(R)) - phi = AlgebraHomomorphism(R, ext_R, gens(ext_R)[1+length(v):n+length(v)]) + phi = hom(R, ext_R, gens(ext_R)[1+length(v):n+length(v)]) return ext_R, phi, gens(ext_R)[(1:length(v))] end function preimage( - f::MPolyQuoLocalizedRingHom{BRT, BRET, RT, RET, DMST, CMST}, - I::MPolyLocalizedIdeal{BRT, BRET, RT, RET, CMST} - ) where {BRT<:Ring, BRET<:RingElement, RT<:MPolyRing, RET<:MPolyElem, - DMST<:AbsMultSet{RT, RET}, CMST<:MPolyPowersOfElement{BRT, BRET, RT, RET} - } + f::MPolyQuoLocalizedRingHom{ + <:MPolyQuoLocalizedRing{<:Any, <:Any, <:Any, <:Any, + <:MPolyPowersOfElement + }, + <:MPolyQuoLocalizedRing{<:Any, <:Any, <:Any, <:Any, + <:MPolyPowersOfElement + } + }, + I::MPolyLocalizedIdeal + ) base_ring(I) == localized_ring(codomain(f)) || error("the ideal does not belong to the codomain of the map") R = base_ring(domain(f)) S = base_ring(codomain(f)) @@ -1255,11 +1256,16 @@ function preimage( end function preimage( - f::MPolyQuoLocalizedRingHom{BRT, BRET, RT, RET, DMST, CMST}, - I::MPolyLocalizedIdeal{BRT, BRET, RT, RET, CMST} - ) where {BRT<:Ring, BRET<:RingElement, RT<:MPolyRing, RET<:MPolyElem, - DMST<:AbsMultSet{RT, RET}, CMST<:MPolyComplementOfKPointIdeal{BRT, BRET, RT, RET} - } + f::MPolyQuoLocalizedRingHom{ + <:MPolyQuoLocalizedRing{<:Any, <:Any, <:Any, <:Any, + <:MPolyPowersOfElement + }, + <:MPolyQuoLocalizedRing{<:Any, <:Any, <:Any, <:Any, + <:MPolyComplementOfKPointIdeal + } + }, + I::MPolyLocalizedIdeal + ) base_ring(I) == localized_ring(codomain(f)) || error("the ideal does not belong to the codomain of the map") J = ideal(helper_ring(f), helper_kappa(f).(gens(saturated_ideal(I)))) + helper_ideal(f) return localized_ring(domain(f))(preimage(helper_eta(f), J)) diff --git a/test/Experimental/PlaneCurve-test.jl b/test/Experimental/PlaneCurve-test.jl index 39f81fe60219..2e1a934d238e 100644 --- a/test/Experimental/PlaneCurve-test.jl +++ b/test/Experimental/PlaneCurve-test.jl @@ -164,7 +164,7 @@ end G = Oscar.ProjPlaneCurve(T(x * z + y^2 + z^2)) H = Oscar.ProjPlaneCurve(T(x * (x + y) * y)) M = Oscar.ProjPlaneCurve((x - y) * (x - 2 * z)) - PP = projective_space(QQ, 2) + PP = proj_space(QQ, 2) P = Oscar.Geometry.ProjSpcElem(PP[1], [QQ(0), QQ(0), QQ(1)]) Q = Oscar.Geometry.ProjSpcElem(PP[1], [QQ(2), QQ(-2), QQ(1)]) @@ -200,7 +200,7 @@ end T, _ = grade(R) F = Oscar.ProjPlaneCurve(T((x^2 + y^2) * (x^2 + y^2 + 2 * y * z))) G = Oscar.ProjPlaneCurve(T((x^2 + y^2) * (y^3 * x^6 - y^6 * x^2 * z))) - PP = projective_space(QQ, 2) + PP = proj_space(QQ, 2) L = Oscar.curve_intersect(PP[1], F, G) P = Oscar.Geometry.ProjSpcElem(PP[1], [QQ(0), QQ(0), QQ(1)]) @@ -218,7 +218,7 @@ end @testset "ProjPlaneCurve singularity functions" begin R, (x, y, z) = PolynomialRing(QQ, ["x", "y", "z"]) T, _ = grade(R) - PP = projective_space(QQ, 2) + PP = proj_space(QQ, 2) F = Oscar.ProjPlaneCurve(T(x * z + y^2)) P = Oscar.Geometry.ProjSpcElem(PP[1], [QQ(1), QQ(0), QQ(0)]) @@ -273,7 +273,7 @@ end S, (x, y, z) = PolynomialRing(QQ, ["x", "y", "z"]) T, _ = grade(S) C = Oscar.ProjPlaneCurve(T(y^2 + y * z + x^2)) - PP = projective_space(QQ, 2) + PP = proj_space(QQ, 2) P = Oscar.Geometry.ProjSpcElem(PP[1], [QQ(0), QQ(0), QQ(1)]) Q = Oscar.Geometry.ProjSpcElem(PP[1], [QQ(0), QQ(-1), QQ(1)]) D = Oscar.ProjCurveDivisor(C, Dict(P => 3, Q => -2)) @@ -293,7 +293,7 @@ end S, (x, y, z) = PolynomialRing(QQ, ["x", "y", "z"]) T, _ = grade(S) C = Oscar.ProjPlaneCurve(T(y^2 * z - x * (x - z) * (x + 3 * z))) - PP = projective_space(QQ, 2) + PP = proj_space(QQ, 2) P = Oscar.Geometry.ProjSpcElem(PP[1], [QQ(0), QQ(1), QQ(0)]) R = Oscar.Geometry.ProjSpcElem(PP[1], [QQ(0), QQ(0), QQ(1)]) D = Oscar.ProjCurveDivisor(C, P, 4) @@ -318,7 +318,7 @@ end @testset "Weierstrass form" begin S, (x, y, z) = PolynomialRing(QQ, ["x", "y", "z"]) T, _ = grade(S) - PP = projective_space(QQ, 2) + PP = proj_space(QQ, 2) P = Oscar.Geometry.ProjSpcElem(PP[1], [QQ(0), QQ(1), QQ(0)]) Q = Oscar.Geometry.ProjSpcElem(PP[1], [QQ(-1), QQ(1), QQ(0)]) C = Oscar.ProjPlaneCurve(T(y^2 * z - x^3 - x * z^2)) @@ -358,7 +358,7 @@ end S, (x, y, z) = PolynomialRing(QQ, ["x", "y", "z"]) T, _ = grade(S) F = T(-x^3 - 3 * x^2 * y - 3 * x * y^2 - x * z^2 - y^3 + y^2 * z - y * z^2 - 4 * z^3) - PP = projective_space(QQ, 2) + PP = proj_space(QQ, 2) P1 = Oscar.Geometry.ProjSpcElem(PP[1], [QQ(-1), QQ(1), QQ(0)]) E = Oscar.ProjEllipticCurve(F, P1) @test Oscar.weierstrass_form(E) == T(-x^3 - x * z^2 + y^2 * z - 4 * z^3) @@ -375,7 +375,7 @@ end S, (x, y, z) = PolynomialRing(K, ["x", "y", "z"]) T, _ = grade(S) F = T(-x^3 - 3 * x^2 * y - 3 * x * y^2 - x * z^2 - y^3 + y^2 * z - y * z^2 - 4 * z^3) - PP = projective_space(K, 2) + PP = proj_space(K, 2) P1 = Oscar.Geometry.ProjSpcElem(PP[1], [K(-1), K(1), K(0)]) E = Oscar.ProjEllipticCurve(F, P1) @test order(E) == 78633 @@ -385,7 +385,7 @@ end S, (x, y, z) = PolynomialRing(QQ, ["x", "y", "z"]) T, _ = grade(S) F = T(-x^3 - 3 * x^2 * y - 3 * x * y^2 - x * z^2 - y^3 + y^2 * z - y * z^2 - 4 * z^3) - PP = projective_space(QQ, 2) + PP = proj_space(QQ, 2) P1 = Oscar.Geometry.ProjSpcElem(PP[1], [QQ(-1), QQ(1), QQ(0)]) E = Oscar.ProjEllipticCurve(F, P1) Q1 = Oscar.Point_EllCurve(E, P1) @@ -408,7 +408,7 @@ end T, _ = grade(S) F = T(y^2 * z - x^3 - 10 * x * z^2 + 2 * z^3) E = Oscar.ProjEllipticCurve(F) - PP = projective_space(A, 2) + PP = proj_space(A, 2) P = Oscar.Point_EllCurve(E, Oscar.Geometry.ProjSpcElem(PP[1], [A(1), A(3), A(1)])) Q = Oscar.Point_EllCurve(E, Oscar.Geometry.ProjSpcElem(PP[1], [A(4332), A(3230), A(1)])) @test Oscar.sum_Point_EllCurveZnZ(P, P).Pt.v == Q.Pt.v @@ -478,7 +478,7 @@ end T, _ = grade(S) I = ideal(T, [x^2, y^2*z, z^2]) C = Oscar.ProjCurve(I) - PP = projective_space(QQ, 3) + PP = proj_space(QQ, 3) P = Oscar.Geometry.ProjSpcElem(PP[1], [QQ(0), QQ(2), QQ(0), QQ(5)]) @test P in C @test Oscar.isirreducible(C) diff --git a/test/Rings/mpoly-localizations.jl b/test/Rings/mpoly-localizations.jl index ea65f6e62f6f..366c67444e25 100644 --- a/test/Rings/mpoly-localizations.jl +++ b/test/Rings/mpoly-localizations.jl @@ -76,6 +76,17 @@ K = ideal(V, [x, y]) lbpa = groebner_basis(K) reduce(V(x), lbpa) + + R, v = ZZ["x", "y"] + x = v[1] + y = v[2] + f = (x^2 + y^2)^2 + S = MPolyComplementOfKPointIdeal(R, [ZZ(0), ZZ(0)]) + T = MPolyPowersOfElement(R, [f]) + U = MPolyProductOfMultSets(R, [S, T]) + @test f in U + @test (f*(x-1) in U) + @test !(f*x in U) end @testset "mpoly-localizations PowersOfElements" begin diff --git a/test/Rings/mpolyquo-localizations.jl b/test/Rings/mpolyquo-localizations.jl index bdc0994f588c..cf778aeb630f 100644 --- a/test/Rings/mpolyquo-localizations.jl +++ b/test/Rings/mpolyquo-localizations.jl @@ -1,5 +1,41 @@ -@testset "mpolyquo-localizations" begin +@testset "mpolyquo-localizations.jl" begin + R, v = QQ["x", "y", "u", "v"] + x = v[1] + y = v[2] + u = v[3] + v = v[4] + f = x*v-y*u + I = ideal(R, f) + Q, p = quo(R, I) + S = MPolyComplementOfKPointIdeal(R, [QQ(1), QQ(0), QQ(1), QQ(0)]) + T = MPolyComplementOfKPointIdeal(R, [QQ(0), QQ(0), QQ(0), QQ(0)]) + L = Localization(Q, S) + a = L(x) + b = L(y) + c = L(u) + d = L(v) + + kk= QQ + R, v = kk["x", "y"] + x = v[1] + y = v[2] + f = (x^2 + y^2) + T = MPolyComplementOfKPointIdeal(R, [kk(0), kk(0)]) + I = ideal(R, f) + V = MPolyQuoLocalizedRing(R, I, T) + + S = R + U = MPolyPowersOfElement(S, [f-1]) + J = ideal(S, zero(S)) + W = MPolyQuoLocalizedRing(S, J, U) + + h = MPolyQuoLocalizedRingHom(W, V, [x//(y-1), y//(x-5)]) + reduce_fraction(h(W(f//(f-1)^9))) + @test preimage(h, ideal(localized_ring(V), [x*(x-1), y*(y-3)])) == ideal(localized_ring(W), [x, y]) + + ### second round of tests #kk = GF(101) + ⊂ = issubset kk = QQ R, (x,y) = kk["x", "y"] @@ -7,6 +43,7 @@ S = MPolyPowersOfElement(x-1) T = MPolyComplementOfKPointIdeal(R, [1,1]) V = MPolyComplementOfPrimeIdeal(ideal(R, f)) + ⊂ = issubset @test S ⊂ V @test !(V ⊂ S) @test !(T ⊂ V) diff --git a/test/Rings/nmod-localizations.jl b/test/Rings/nmod-localizations.jl index fcfc073d8ccf..32565b24a7d1 100644 --- a/test/Rings/nmod-localizations.jl +++ b/test/Rings/nmod-localizations.jl @@ -124,6 +124,7 @@ end (W::NmodLocalizedRing)(a::T, b::T) where {T<:Oscar.IntegerUnion} = W(base_ring(W)(a), base_ring(W)(b)) (W::NmodLocalizedRing)(a::Oscar.IntegerUnion) = W(base_ring(W)(a), one(base_ring(W))) (W::NmodLocalizedRing)(q::fmpq) = W(numerator(q), denominator(q)) +(W::NmodLocalizedRing)(i::Int64) = W(base_ring(W)(i), one(base_ring(W))) (W::NmodLocalizedRing)(q::Rational{T}) where {T<:Oscar.IntegerUnion} = W(numerator(q), denominator(q)) ### implementation of Oscar's general ring interface diff --git a/test/Schemes/AffineSchemes.jl b/test/Schemes/AffineSchemes.jl index 24dd73833ef9..22d616542e85 100644 --- a/test/Schemes/AffineSchemes.jl +++ b/test/Schemes/AffineSchemes.jl @@ -11,12 +11,12 @@ set_name!(U, "U") UX = intersect(X, U) set_name!(UX, "U ∩ X") - @test X == closure(UX, A3) + @test is_canonically_isomorphic(X, closure(UX, A3)) @test is_open_embedding(UX, X) @test is_closed_embedding(X, A3) UZ = subscheme(UX, y^2) Z = subscheme(X, y^2) - @test closure(UZ, X) == Z + @test is_canonically_isomorphic(closure(UZ, X), Z) S, (u,v) = QQ["u", "v"] A2 = Spec(S) diff --git a/test/Schemes/Glueing.jl b/test/Schemes/Glueing.jl new file mode 100644 index 000000000000..b550afe16fd8 --- /dev/null +++ b/test/Schemes/Glueing.jl @@ -0,0 +1,40 @@ +@testset "glueings" begin + R, (x,y,z) = QQ["x", "y", "z"] + A3 = Spec(R) + set_name!(A3, "𝔸³") + f = (x*y-z^2) + #f = (x*y-z^2)*(x+y+2*z) + X = subscheme(A3, f) + set_name!(X, "X") + U = SpecOpen(A3, [x,y,z]) + UX = intersect(X, U) + d = find_non_zero_divisor(UX) + S, (u,v) = QQ["u", "v"] + A2 = Spec(S) + set_name!(A2, "𝔸²") + f = maximal_extension(X, A2, [x, z//y]) + a = generic_fractions(f) + @test maximal_extension(X, A2, a) == f + + Sx, (yx, zx) = QQ["yx", "zx"] + Sy, (xy, zy) = QQ["xy", "zy"] + Sz, (xz, yz) = QQ["xz", "yz"] + + Ax = Spec(Sx) + Ay = Spec(Sy) + Az = Spec(Sz) + + fxy = maximal_extension(Ax, Ay, [1//yx, zx//yx]) + fyx = maximal_extension(Ay, Ax, [1//xy, zy//xy]) + fxz = maximal_extension(Ax, Az, [1//zx, yx//zx]) + fzx = maximal_extension(Az, Ax, [yz//xz, 1//xz]) + fyz = maximal_extension(Ay, Az, [xy//zy, 1//zy]) + fzy = maximal_extension(Az, Ay, [xz//yz, 1//yz]) + + gxy = Glueing(Ax, Ay, restriction(fxy, domain(fxy), domain(fyx)), restriction(fyx, domain(fyx), domain(fxy))) + gxz = Glueing(Ax, Az, restriction(fxz, domain(fxz), domain(fzx)), restriction(fzx, domain(fzx), domain(fxz))) + gyz = Glueing(Ay, Az, restriction(fyz, domain(fyz), domain(fzy)), restriction(fzy, domain(fzy), domain(fyz))) + + gyz_alt = compose(gxy, gxz) + @test gyz == maximal_extension(gyz_alt) +end diff --git a/test/Schemes/ProjectiveSchemes.jl b/test/Schemes/ProjectiveSchemes.jl new file mode 100644 index 000000000000..8dd940a0d2de --- /dev/null +++ b/test/Schemes/ProjectiveSchemes.jl @@ -0,0 +1,49 @@ +@testset "projective_schemes_1" begin + +# test for relative projective space over a polynomial ring +R, (x,y) = QQ["x", "y"] +R_ext, _ = PolynomialRing(R, ["u", "v"]) +S, (u,v) = grade(R_ext, [1,1]) + +I = ideal(S, [x*v - y*u]) +X = ProjectiveScheme(S, I) +CX = affine_cone(X) +@test OO(CX).(homogeneous_coordinates(X)) == [homog_to_frac(X)(g) for g in gens(S)] +hc = homogeneous_coordinates(X) +frac_to_homog_pair(X)(hc[1]*hc[2]) + +phi = ProjectiveSchemeMor(X, X, [-u, -v]) + +g = map_on_affine_cones(phi) +@test is_well_defined(phi) + +# test for projective space over a field +R_ext, _ = PolynomialRing(QQ, ["u", "v"]) +S, (u,v) = grade(R_ext, [1,1]) + +I = ideal(S, [u]) +X = ProjectiveScheme(S, I) +CX = affine_cone(X) +@test OO(CX).(homogeneous_coordinates(X)) == [homog_to_frac(X)(g) for g in gens(S)] +hc = homogeneous_coordinates(X) +frac_to_homog_pair(X)(hc[1]*hc[2]) + +phi = ProjectiveSchemeMor(X, X, [u^2, v^2]) + +g = map_on_affine_cones(phi) + +@test is_well_defined(phi) + +# test for relative projective space over MPolyQuoLocalizedRings +Y = Spec(R) +Q = OO(Y) +R_ext, _ = PolynomialRing(Q, ["u", "v"]) +S, (u,v) = grade(R_ext, [1,1]) +X = ProjectiveScheme(S) + +phi = ProjectiveSchemeMor(X, X, [u^2, v^2]) + +g = map_on_affine_cones(phi) + +@test is_well_defined(phi) +end diff --git a/test/Schemes/SpecOpen.jl b/test/Schemes/SpecOpen.jl index 6a58b9727e4d..6e2ed1f32d24 100644 --- a/test/Schemes/SpecOpen.jl +++ b/test/Schemes/SpecOpen.jl @@ -10,7 +10,7 @@ Y = closure(UX) Z = closure(UX, A) - @test Z == Y + @test is_canonically_isomorphic(Z, Y) end @testset "SpecOpen_2" begin diff --git a/test/runtests.jl b/test/runtests.jl index 9ccb5061c94e..95b67b062bbb 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -54,3 +54,5 @@ include("ToricVarieties/runtests.jl") include("Schemes/AffineSchemes.jl") include("Schemes/SpecOpen.jl") +include("Schemes/Glueing.jl") +include("Schemes/ProjectiveSchemes.jl")