diff --git a/experimental/Schemes/SpaceGerms.jl b/experimental/Schemes/SpaceGerms.jl index 8d80f0f09710..42a3c6036cc7 100644 --- a/experimental/Schemes/SpaceGerms.jl +++ b/experimental/Schemes/SpaceGerms.jl @@ -1,9 +1,3 @@ -export SpaceGerm -export ambient_germ -export germ_at_point -export point -export rational_point_coordinates -export representative import AbstractAlgebra: Ring @@ -35,7 +29,10 @@ GermAtGeometricPoint = Spec{<:Field, SpaceGerm{BaseRingType, RingType, SpecType} A space germ ``(X,O_{(X,x)}``, i.e. a ringed space with underlying scheme ``X`` of type SpecType and local ring ``O_{(X,x)}`` of type `RingType` over some base ring ``k`` of type `BaseRingType`. """ -@attributes mutable struct SpaceGerm{BaseRingType<:Ring, RingType<:Ring, SpecType<:Spec} <: AbsSpaceGerm{BaseRingType, RingType} +@attributes mutable struct SpaceGerm{ + BaseRingType<:Ring, + RingType<:Ring, + SpecType<:Spec} <: AbsSpaceGerm{BaseRingType, RingType} X::SpecType function SpaceGerm(X::GermAtClosedPoint) @@ -48,52 +45,268 @@ A space germ ``(X,O_{(X,x)}``, i.e. a ringed space with underlying scheme ``X`` end end +@doc raw""" + HypersurfaceGerm{BaseRingType, RingType, SpecType} + +A hypersurface germ ``(X,O_{(X,x)}``, i.e. a ringed space with underlying scheme ``X`` of type `SpecType` and local ring ``O_{(X,x)}`` of type `RingType` over some base ring ``k`` of type `BaseRingType`. +""" +@attributes mutable struct HypersurfaceGerm{ + BaseRingType<:Ring, + RingType<:Ring, + SpecType<:Spec} <: AbsSpaceGerm{BaseRingType, RingType} + X::SpecType + f::RingElem + + function HypersurfaceGerm(X::GermAtClosedPoint,f::MPolyLocRingElem; check::Bool=true) + base_ring(modulus(OO(X))) == parent(f) || error("baserings do not match") + @check begin + (ideal(parent(f),[f]) == modulus(OO(X))) || error("given f does not define given X") + end + return new{typeof(base_ring(X)), typeof(OO(X)), typeof(X)}(X,f) + end + +## the following is currently unused.... +## as no backend for groebner computations is currently available in this case + function HypersurfaceGerm(X::GermAtGeometricPoint, f::MPolyLocRingElem; check::Bool=true) + base_ring(modulus(OO(X))) == parent(f) || error("baserings do not match") + @check begin + (ideal(parent(f),[f]) == modulus(OO(X))) || error("given f does not define given X") + end + return new{typeof(base_ring(X)), typeof(OO(X)), typeof(X)}(X,f) + end +end + +@doc raw""" + CompleteIntersectionGerm{BaseRingType, RingType, SpecType} + +A complete intersection germ ``(X,O_{(X,x)}``, i.e. a ringed space with underlying scheme ``X`` of type SpecType and local ring ``O_{(X,x)}`` of type `RingType` over some base ring ``k`` of type `BaseRingType`. +""" +@attributes mutable struct CompleteIntersectionGerm{BaseRingType<:Ring, RingType<:Ring, SpecType<:Spec} <: AbsSpaceGerm{BaseRingType, RingType} + X::SpecType + v::Vector{<:RingElem} + + function CompleteIntersectionGerm(X::GermAtClosedPoint, v::Vector{T}; check::Bool=true) where T<:MPolyLocRingElem + R = base_ring(modulus(OO(X))) + all(x->parent(x) == R, v) || error("base_rings do not coincide") + @check begin +## TODO: change dim(Spec(R)) to dim(R) as soon as the fixes for dim have +## been moved to the algebra side!!!! + length(v) == dim(Spec(R)) - dim(X) || error("not a complete intersection") + modulus(OO(X)) == ideal(R,v) || error("given tuple does not generate modulus") + end + return new{typeof(base_ring(X)), typeof(OO(X)), typeof(X)}(X,v) + end + +## the following one is currently unused... +## as no backend for groebner computations is currently available in this case + function CompleteIntersectionGerm(X::GermAtGeometricPoint, v::Vector{MPolyLocRingElem}; check::Bool=true) + R = base_ring(OO(X)) + all(x->parent(x) == R, v) || error("base_rings do not coincide") + @check begin + length(v) == dim(Spec(R)) - dim(X) || error("not a complete intersection") + modulus(OO(X)) == ideal(R,v) || error("given tuple does not generate modulus") + end + return new{typeof(base_ring(X)), typeof(OO(X)), typeof(X)}(X,v) + end +end + +############################################################################## +## Some more shorthand notation +############################################################################## +const AnySpaceGerm = Union{SpaceGerm, HypersurfaceGerm, CompleteIntersectionGerm} +const AnySpaceGermClosedPoint = Union{SpaceGerm{<:Ring,<:Ring,<:GermAtClosedPoint}, + HypersurfaceGerm{<:Ring,<:Ring,<:GermAtClosedPoint}, + CompleteIntersectionGerm{<:Ring,<:Ring,<:GermAtClosedPoint}} +const AnySpaceGermGeometricPoint = Union{SpaceGerm{<:Ring,<:Ring,<:GermAtGeometricPoint}, + HypersurfaceGerm{<:Ring,<:Ring,<:GermAtGeometricPoint}, + CompleteIntersectionGerm{<:Ring,<:Ring,<:GermAtGeometricPoint}} + +############################################################################## ### Getter functions +############################################################################## -function underlying_scheme(X::SpaceGerm) +function underlying_scheme(X::AnySpaceGerm) return X.X end -@attr Spec function representative(X::SpaceGerm{<:Ring, <:MPolyQuoLocRing}) - return Spec(underlying_quotient(OO(X))) +@doc raw""" + representative(X::AnySpaceGerm) + representative(X::SpaceGerm{<:Ring, <:MPolyLocRing}) + +Return a representative `Y` of a space germ `(X,p)` at a point `p`. + +More precisely, let `(X,p)` be given by `Spec U^{-1}(R /I)`, where `R` is a polynomial +ring, `I` an ideal of it and `U` the complement of the maximal ideal corresponding +to `p. Then the representative `Y = Spec R/I` is returned. + +# Examples +```jldoctest +julia> R, (x,y,z) = QQ["x", "y", "z"]; + +julia> I = ideal(R, [(x-1)*(x^2 - y^2 + z^2)]); + +julia> X = Spec(R, I); + +julia> XS = SpaceGerm(X,[0,0,0]) +Spectrum + of localization + of quotient + of multivariate polynomial ring in 3 variables x, y, z + over rational field + by ideal(x^3 - x^2 - x*y^2 + x*z^2 + y^2 - z^2) + at complement of maximal ideal of point (0, 0, 0) + +julia> representative(XS) +Spectrum + of quotient + of multivariate polynomial ring in 3 variables x, y, z + over rational field + by ideal(x^3 - x^2 - x*y^2 + x*z^2 + y^2 - z^2) + +julia> representative(XS) == X +true + +julia> L, phi = Localization(R,complement_of_point_ideal(R,[0,0,0])); + +julia> IL = phi(I); + +julia> Z = germ_at_point(quo(L,IL)[1])[1]; + +julia> representative(Z) +Spectrum + of quotient + of multivariate polynomial ring in 3 variables x, y, z + over rational field + by ideal(x^3 - x^2 - x*y^2 + x*z^2 + y^2 - z^2) + +``` +""" +@attr Spec function representative(X::AnySpaceGerm) + return Spec(underlying_quotient(OO(X))) end @attr Spec function representative(X::SpaceGerm{<:Ring, <:MPolyLocRing}) - R = ambient_coordinate_ring(X) - return Spec(R) + R = ambient_coordinate_ring(X) + return Spec(R) end -function point(X::SpaceGerm{<:Any, <:Any, <:GermAtClosedPoint}) +@doc raw""" + point(X::AnySpaceGermClosedPoint) + point(X::AnySpaceGermGeometricPoint) + +Return the point `p` of a germ `(X,p)`, where p is specified +- as its point_coordinates in the first case +- as the respective prime ideal of `p` in the second case + +# Examples +```jldoctest +julia> R, (x,y,z) = QQ["x", "y", "z"]; + +julia> I = ideal(R, [(x-1)*(x^2 - y^2 + z^2)]); + +julia> X = Spec(R, I); + +julia> XS = SpaceGerm(X,[0,0,0]); + +julia> point(XS) +3-element Vector{QQFieldElem}: + 0 + 0 + 0 + +``` +""" +function point(X::AnySpaceGermClosedPoint) return point_coordinates(inverted_set(OO(X))) end -## currently unused case -function point(X::SpaceGerm{<:Any, <:Any, <:GermAtGeometricPoint}) +## TODO: move to higher level, i not already available +function point(X::AnySpaceGermGeometricPoint) return prime_ideal(inverted_set(OO(X))) end -Oscar.ring(X::AbsSpaceGerm) = OO(X) +coordinate_ring(X::AbsSpaceGerm) = OO(X) -function Oscar.ideal(X::AbsSpaceGerm{<:Ring,<:MPolyQuoLocRing}) +function defining_ideal(X::AnySpaceGerm) return modulus(OO(X)) end -function Oscar.ideal(X::AbsSpaceGerm{<:Ring,<:MPolyLocRing}) - return ideal(OO(X),[zero(OO(X))]) +function defining_ideal(X::SpaceGerm{<:Ring,<:MPolyLocRing}) + return ideal(OO(X),[]) end -@attr SpaceGerm function ambient_germ(X::AbsSpaceGerm{<:Ring,<:MPolyQuoLocRing}) +@doc raw""" + ambient_germ(X::AbsSpaceGerm) + +Return the ambient germ of a given germ `(X,p)`. + +More precisely, let `(X,p)` be given by `Spec U^{-1}(R /I)`, where `R` is a polynomial +ring, `I` an ideal of it and `U` the complement of the maximal ideal corresponding +to `p. Then the ambient germ `Spec U^{-1}R` is returned. + +# Examples +```jldoctest +julia> R, (x,y,z) = QQ["x", "y", "z"]; + +julia> I = ideal(R, [(x-1)*(x^2 - y^2 + z^2)]); + +julia> X = Spec(R, I); + +julia> XS = SpaceGerm(X,[0,0,0]); + +julia> ambient_germ(XS) +Spectrum + of localization + of multivariate polynomial ring in 3 variables x, y, z + over rational field + at complement of maximal ideal of point (0, 0, 0) + +``` +""" +@attr SpaceGerm function ambient_germ(X::AnySpaceGerm) Y,_ = germ_at_point(localized_ring(OO(X))) return Y end -@attr SpaceGerm function ambient_germ(X::AbsSpaceGerm{<:Ring,<:MPolyLocRing}) +@attr SpaceGerm function ambient_germ(X::SpaceGerm{<:Ring,<:MPolyLocRing}) return X end -############################################################################################################ +@doc raw""" + defining_ring_element(X::HypersurfaceGerm) + defining_ring_elements(X::CompleteIntersectionGerm) + +Return the (fixed) defining element(s) of the ideal of `X` in the ring of the ambient germ of `X`. Note that the return value is not an element of a polynomial ring, but of a localization of a polynomial ring the complement of a maximal ideal. (Hence each such element has a numerator and a denominator.) + +Caution: This command is not exported and is only provided for convenience in programming. +# Examples: +```jldoctest +julia> R, (x,y,z) = QQ["x", "y", "z"]; + +julia> I = ideal(R, [(x-1)*(x^2 - y^2 + z^2)]); + +julia> X = Spec(R, I); + +julia> XS = HypersurfaceGerm(X,[0,0,0]) +Spectrum + of localization + of quotient + of multivariate polynomial ring in 3 variables x, y, z + over rational field + by ideal(x^3 - x^2 - x*y^2 + x*z^2 + y^2 - z^2) + at complement of maximal ideal of point (0, 0, 0) + +julia> defining_ring_element(XS) +-x^3 + x^2 + x*y^2 - x*z^2 - y^2 + z^2 + +``` +""" +defining_ring_element(X::HypersurfaceGerm) = X.f::elem_type(localized_ring_type(ring_type(X))) +defining_ring_elements(X::CompleteIntersectionGerm) = X.v::Vector{elem_type(localized_ring_type(ring_type(X)))} + +################################################################################ # allow user to specify point also as ideal -############################################################################################################ +################################################################################ @doc raw""" rational_point_coordinates(I::MPolyIdeal) @@ -130,10 +343,63 @@ function rational_point_coordinates(I::MPolyIdeal) return [ iszero(a) ? zero(coefficient_ring(a)) : leading_coefficient(a) for a in nf_vec] # TODO does the ordering matter? end -############################################################################################################ +####################################################################################### ### constructors -############################################################################################################ -function SpaceGerm(X::AbsSpec, a::Vector) +####################################################################################### +@doc raw""" + SpaceGerm(X::AbsSpec, a::Vector{T}) where T<:Union{Integer, FieldElem} + SpaceGerm(X::AbsSpec, I:Ideal) + SpaceGerm(X::Spec(LocalRing)) + SpaceGerm(A::LocalRing) + +Return the space germ `(X,p)` arising from the given representative `X` or the given +`X = Spec(A)` for a local ring `A`, where the point `p` may be specified in several +equivalent ways: +- by its coordinates `a` in the ambient_space of `X` or +- by a maximal ideal `I`in the coordinate ring of `X` or +- by a maximal ideal `I` in the ambient_coordinate_ring of `X` +- by the maximal ideal of the local ring `A` + +!!!note + Only `LocalRing`s localized at rational points over the coefficient field are currently fully supported. + +# Examples +```jldoctest +julia> X = affine_space(QQ,3); + +julia> R = coordinate_ring(X); + +julia> (x,y,z) = gens(R); + +julia> XL = SpaceGerm(X,ideal(R,[x,y,z])) +Spectrum + of localization + of multivariate polynomial ring in 3 variables x1, x2, x3 + over rational field + at complement of maximal ideal of point (0, 0, 0) + +julia> RL = coordinate_ring(XL); + +julia> J = ideal(RL,[x^2-y^2+z^2]) +Ideal + of localized ring +with 1 generator + x1^2 - x2^2 + x3^2 + +julia> Q,_ = quo(RL,J); + +julia> SpaceGerm(Q) +Spectrum + of localization + of quotient + of multivariate polynomial ring in 3 variables x1, x2, x3 + over rational field + by ideal(x1^2 - x2^2 + x3^2) + at complement of maximal ideal of point (0, 0, 0) + +``` +""" +function SpaceGerm(X::AbsSpec, a::Vector{T}) where T<:Union{Integer, FieldElem} R = ambient_coordinate_ring(X) kk = coefficient_ring(R) b = [kk.(v) for v in a] ## throws an error, if vector entries are not compatible @@ -152,59 +418,419 @@ function SpaceGerm(X::AbsSpec, I::MPolyIdeal) return Y end -to_poly_ideal(I::MPolyQuoIdeal) = ideal(base_ring(base_ring(I)),lift.(gens(I))) + modulus(base_ring(I)) -to_poly_ideal(I::MPolyLocalizedIdeal) = ideal(base_ring(base_ring(I)), gens(saturated_ideal(I))) -to_poly_ideal(I::MPolyQuoLocalizedIdeal) = ideal(base_ring(base_ring(I)),gens(saturated_ideal(I))) + modulus(underlying_quotient(base_ring(I))) - function SpaceGerm(X::AbsSpec, I::Ideal) A = base_ring(I) A === OO(X) || error("rings are incompatible") - J = to_poly_ideal(I) + J = saturated_ideal(I) a = rational_point_coordinates(J) - Y = SpaceGerm(X,a) + return SpaceGerm(X,a) +end + +@doc raw""" + SpaceGerm(X::AbsSpec, p::AbsAffineRationalPoint) + SpaceGerm(p::AbsAffineRationalPoint) + +Return a space germ `(X,p)` for a given `X` and a rational point `p` on some affine scheme `Y`. If no `X` is specified, `Y` is used in the place of `Y`. +""" +SpaceGerm(p::AbsAffineRationalPoint) = SpaceGerm(codomain(p), coordinates(p)) + +function SpaceGerm(X::AbsSpec, p::AbsAffineRationalPoint) + ambient_space(X) == ambient_space(codomain(p)) || error("ambient spaces do not match") + return SpaceGerm(X,coordinates(p)) +end + +@doc raw""" + HypersurfaceGerm(X::AbsSpec, a::Vector{T}) where T<:Union{Integer, FieldElem} + HypersurfaceGerm(X::AbsSpec, I:Ideal) + HypersurfaceGerm(A::LocalRing) + +Checks that `X` (or `Spec(A)` respectively) represents a hypersurface germ at the given +point `p` and returns the hypersurface germ `(X,p)` from `X` in the affirmative case, where `p` +may be specified in several equivalent ways: +- by its coordinates `a` in the ambient_space of `X` or +- by a maximal ideal `I` in the coordinate ring of `X` or +- by a maximal ideal `I` in the ambient_coordinate_ring of `X` +- by the maximal ideal of the local ring `A` + + HypersurfaceGerm(X::Spec(LocalRing),f::MPolyLocRingElem) + +This variant allows explicit specification of the generator for the hypersurface. The given `f` is checked to generate to modulus of OO(X) or A respectively. In the affirmative case, the given generator will subsequently be used by all methods explicitly accessing a generator. + +!!!note + Only `LocalRing`s localized at rational points over the coefficient field are currently fully supported. + +# Examples +```jldoctest +julia> X = affine_space(QQ,3); + +julia> R = coordinate_ring(X); + +julia> (x,y,z) = gens(R); + +julia> Q,_ = quo(R,ideal(R,[x^2+y^2+z^2])); + +julia> HypersurfaceGerm(Spec(Q),[0,0,0]) +Spectrum + of localization + of quotient + of multivariate polynomial ring in 3 variables x1, x2, x3 + over rational field + by ideal(x1^2 + x2^2 + x3^2) + at complement of maximal ideal of point (0, 0, 0) + +``` +""" +function HypersurfaceGerm(X::AbsSpec, a::Vector{T}) where T<:Union{Integer, FieldElem} + R = ambient_coordinate_ring(X) + kk = coefficient_ring(R) + b = [kk.(v) for v in a] ## throws an error, if vector entries are not compatible + U = MPolyComplementOfKPointIdeal(R,b) + LX,_ = Localization(OO(X), U) + mingens = minimal_generating_set(modulus(LX)) + length(mingens) == 1 || error("not a hypersurface") + f = mingens[1] + Y = HypersurfaceGerm(Spec(LX),f) + set_attribute!(Y,:representative,X) return Y end -function germ_at_point(X::AbsSpec, I::Ideal) - Y = SpaceGerm(X, I) - restr_map = SpecMor(Y, X, hom(OO(X), OO(Y), gens(OO(Y)), check=false), check=false) - return Y, restr_map +function HypersurfaceGerm(X::AbsSpec, I::MPolyIdeal) + R = base_ring(I) + R === ambient_coordinate_ring(X) || error("rings are not compatible") + a = rational_point_coordinates(I) + Y = HypersurfaceGerm(X,a) + return Y end -function germ_at_point(X::AbsSpec, a::Vector) - Y = SpaceGerm(X, a) - restr_map = SpecMor(Y, X, hom(OO(X), OO(Y), gens(OO(Y)), check=false), check=false) - return Y, restr_map +function HypersurfaceGerm(X::AbsSpec, I::Ideal) + A = base_ring(I) + A === OO(X) || error("rings are incompatible") + J = saturated_ideal(I) + a = rational_point_coordinates(J) + return HypersurfaceGerm(X,a) end -function germ_at_point(A::MPolyRing, I::Ideal) - X = Spec(A) +@doc raw""" + HypersurfaceGerm(X::AbsSpec, p::AbsAffineRationalPoint) + HypersurfaceGerm(p::AbsAffineRationalPoint) + +Return a hypersurface germ `(X,p)` for a given `X` and a rational point `p` on some scheme `Y`. If no `X` is specified, `Y` is used in its place. +""" +HypersurfaceGerm(p::AbsAffineRationalPoint) = HypersurfaceGerm(codomain(p), coordinates(p)) + +function HypersurfaceGerm(X::AbsSpec, p::AbsAffineRationalPoint) + ambient_space(X) == ambient_space(codomain(p)) || error("ambient spaces do not match") + return HypersurfaceGerm(X,coordinates(p)) +end + +@doc raw""" + CompleteIntersectionGerm(X::AbsSpec, a::Vector{T}) where T<:Union{Integer, FieldElem} + CompleteIntersectionGerm(X::AbsSpec, I:Ideal) + +Checks that `X` represents a complete intersection germ at the given point `p` and returns +the complete intersection germ `(X,p)` from `X` in the affirmative case, where `p` may +be specified in several equivalent ways: +- by its coordinates `a` in the ambient_space of `X` or +- by a maximal ideal in the coordinate ring of `X` or +- by a maximal ideal in the ambient_coordinate_ring of `X` + +# Examples +```jldoctest +julia> X = affine_space(QQ,3); + +julia> R = coordinate_ring(X); + +julia> (x,y,z) = gens(R); + +julia> Q,_ = quo(R,ideal(R,[x^2+y^2+z^2,x*y])); + +julia> CompleteIntersectionGerm(Spec(Q),[0,0,0]) +Spectrum + of localization + of quotient + of multivariate polynomial ring in 3 variables x1, x2, x3 + over rational field + by ideal(x1^2 + x2^2 + x3^2, x1*x2) + at complement of maximal ideal of point (0, 0, 0) + +``` +""" +function CompleteIntersectionGerm(X::AbsSpec, a::Vector{T}) where T<:Union{Integer, FieldElem} + R = ambient_coordinate_ring(X) + kk = coefficient_ring(R) + b = [kk.(v) for v in a] ## throws an error, if vector entries are not compatible + U = MPolyComplementOfKPointIdeal(R,b) + L,_ = Localization(OO(X), U) + mingens = minimal_generating_set(modulus(L)) +## TODO: Should be dim(L) and not dim(Spec(L)), but dim for localized +## quotients is only repaired on the geometric side as of now!!! + SpecL = Spec(L) + length(mingens) == dim(R) - dim(SpecL) || error("not a complete intersection") + w = mingens + Y = CompleteIntersectionGerm(SpecL,w) + set_attribute!(Y,:representative,X) + return Y +end + +function CompleteIntersectionGerm(X::AbsSpec, I::MPolyIdeal) + R = base_ring(I) + R === ambient_coordinate_ring(X) || error("rings are not compatible") + a = rational_point_coordinates(I) + Y = CompleteIntersectionGerm(X,a) + return Y +end + +function ComleteIntersectionGerm(X::AbsSpec, I::Ideal) + A = base_ring(I) + A === OO(X) || error("rings are incompatible") + J = saturated_ideal(I) + a = rational_point_coordinates(J) + return CompleteIntersectionGerm(X,a) +end + +@doc raw""" + CompleteIntersectionGerm(X::AbsSpec, p::AbsAffineRationalPoint) + CompleteIntersectionGerm(p::AbsAffineRationalPoint) + +Return a complete intersection germ `(X,p)` for a given `X`and a rational point `p` on some affine scheme `Y`, provided that $X$ is locally a complete intersection in some neighbourhood of `p`. If no `X` is specified, `Y` is used in its place. +""" +CompleteIntersectionGerm(p::AbsAffineRationalPoint) = CompleteIntersectionGerm(codomain(p), coordinates(p)) + +function CompleteIntersectionGerm(X::AbsSpec, p::AbsAffineRationalPoint) + ambient_space(X) == ambient_space(codomain(p)) || error("ambient spaces do not match") + return CompleteIntersectionGerm(X,coordinates(p)) +end + +@doc raw""" + germ_at_point(X::AbsSpec, I::Union{Ideal,Vector}) + germ_at_point(X::AbsSpec, I::Union{Ideal,Vector}) + germ_at_point(A::Union{MPolyRing,MPolyQuoRing}, + I::Union{Ideal,Vector}) + germ_at_point(A::LocalRing, I::Union{Ideal,Vector}) + +Return a SpaceGerm `(X,p)` and the corresponding inclusion morphism of spectra +arising from the given representative `X` or the given +`X = Spec(A)` for a local ring `A`, where the point `p` may be specified in several +equivalent ways: +- by its coordinates `a` in the ambient_space of `X` or +- by a maximal ideal `I`in the coordinate ring of `X` or +- by a maximal ideal `I` in the ambient_coordinate_ring of `X` +- by the maximal ideal of the local ring `A` + +!!!note + Only `LocalRing`s localized at rational points over the coefficient field are currently fully supported. + +# Examples + +```jldoctest +julia> X = affine_space(QQ,3); + +julia> R = coordinate_ring(X); + +julia> (x,y,z) = gens(R); + +julia> Q,_ = quo(R,ideal(R,[x^2+y^2+z^2])); + +julia> Y,phi = germ_at_point(Q,[0,0,0]); + +julia> Y +Spectrum + of localization + of quotient + of multivariate polynomial ring in 3 variables x1, x2, x3 + over rational field + by ideal(x1^2 + x2^2 + x3^2) + at complement of maximal ideal of point (0, 0, 0) + +julia> phi +Morphism + from [x1, x2, x3] Spec of localization of quotient of multivariate polynomial ring at complement of maximal ideal + to [x1, x2, x3] V(x1^2 + x2^2 + x3^2) +given by the pullback function + x1 -> x1 + x2 -> x2 + x3 -> x3 + +``` +""" +function germ_at_point(X::AbsSpec, I::Union{Ideal,Vector}) Y = SpaceGerm(X, I) restr_map = SpecMor(Y, X, hom(OO(X), OO(Y), gens(OO(Y)), check=false), check=false) return Y, restr_map end -function germ_at_point(A::MPolyRing, a::Vector) - X = Spec(A) - Y = SpaceGerm(X, a) - restr_map = SpecMor(Y, X, hom(OO(X), OO(Y), gens(OO(Y)), check=false), check=false) - return Y, restr_map +germ_at_point(A::Union{MPolyRing,MPolyQuoRing}, + I::Union{Ideal,Vector}) = germ_at_point(Spec(A),I) + +@doc raw""" + germ_at_point(X::AbsSpec, p::AbsAffineRationalPoint) + germ_at_point(p::AbsAffineRationalPoint) + +Return a space germ `(X,p)` and the corresponding inclusion morphism of spectra arising +from the representative `X` for a given `X` and a rational point `p` on some affine scheme `Y`. If no `X` is specified, `Y` is used in its place.. + +""" +germ_at_point(p::AbsAffineRationalPoint) = germ_at_point(codomain(p), coordinates(p)) + +function germ_at_point(X::AbsSpec, p::AbsAffineRationalPoint) + ambient_space(X) == ambient_space(codomain(p)) || error("ambient spaces do not match") + return germ_at_point(X,coordinates(p)) end -function germ_at_point(A::MPolyQuoRing, I::Ideal) - X = Spec(A) - Y = SpaceGerm(X, I) +@doc raw""" + hypersurface_germ(X::AbsSpec, I::Union{Ideal,Vector}) + hypersurface_germ(X::AbsSpec, I::Union{Ideal,Vector}) + hypersurface_germ(A::Union{MPolyRing,MPolyQuoRing}, + I::Union{Ideal,Vector}) + hypersurface_germ(A::LocalRing, I::Union{Ideal,Vector}) + +Return a HypersurfaceGerm `(X,p)` and the corresponding inclusion morphism of spectra +arising from the given representative `X` or the given +`X = Spec(A)` for a local ring `A`, where the point `p` may be specified in several +equivalent ways: +- by its coordinates `a` in the ambient_space of `X` or +- by a maximal ideal `I`in the coordinate ring of `X` or +- by a maximal ideal `I` in the ambient_coordinate_ring of `X` +- by the maximal ideal of the local ring `A` + +!!!note + Only `LocalRing`s localized at rational points over the coefficient field are currently fully supported. + +!!!note + If the defining ideal of `(X,p)` is not principal, an error exception occurs. + +# Examples +```jldoctest +julia> X = affine_space(QQ,3); + +julia> R = coordinate_ring(X); + +julia> (x,y,z) = gens(R); + +julia> Q,_ = quo(R,ideal(R,[x^2+y^2+z^2])); + +julia> Y,phi = hypersurface_germ(Q,[0,0,0]); + +julia> Y +Spectrum + of localization + of quotient + of multivariate polynomial ring in 3 variables x1, x2, x3 + over rational field + by ideal(x1^2 + x2^2 + x3^2) + at complement of maximal ideal of point (0, 0, 0) + +julia> phi +Morphism + from [x1, x2, x3] Spec of localization of quotient of multivariate polynomial ring at complement of maximal ideal + to [x1, x2, x3] V(x1^2 + x2^2 + x3^2) +given by the pullback function + x1 -> x1 + x2 -> x2 + x3 -> x3 + +``` +""" +function hypersurface_germ(X::AbsSpec, I::Union{Ideal,Vector}) + Y = HypersurfaceGerm(X,I) restr_map = SpecMor(Y, X, hom(OO(X), OO(Y), gens(OO(Y)), check=false), check=false) return Y, restr_map end -function germ_at_point(A::MPolyQuoRing, a::Vector) - X = Spec(A) - Y = SpaceGerm(X, a) +hypersurface_germ(A::Union{MPolyRing,MPolyQuoRing}, + I::Union{Ideal,Vector}) = hypersurface_germ(Spec(A),I) + +@doc raw""" + hypersurface_germ(X::AbsSpec, p::AbsAffineRationalPoint) + hypersurface_germ(p::AbsAffineRationalPoint) + +Return a hypersurface germ `(X,p)` and the corresponding inclusion morphism of spectra for a given `X` and a rational point `p` on some affine scheme `Y`. If no `X` is specified, `Y` is used in its place. + +!!!note + If the defining ideal of `(X,p)` is not principal. an error exception occurs. +""" +hypersurface_germ(p::AbsAffineRationalPoint) = hypersurface_germ(codomain(p), coordinates(p)) + +function hypersurface_germ(X::AbsSpec, p::AbsAffineRationalPoint) + ambient_space(X) == ambient_space(codomain(p)) || error("ambient spaces do not match") + return hypersurface_germ(X,coordinates(p)) +end + +@doc raw""" + complete_intersection_germ(X::AbsSpec, I::Union{Ideal,Vector}) + complete_intersection_germ(X::AbsSpec, I::Union{Ideal,Vector}) + complete_intersection_germ(A::Union{MPolyRing,MPolyQuoRing}, + I::Union{Ideal,Vector}) + complete_intersection_germ(A::LocalRing, I::Union{Ideal,Vector}) + +Return a CompleteIntersectionGerm `(X,p)` and the corresponding inclusion morphism of spectra arising from the given representative `X` or the given `X = Spec(A)` for a local ring `A`, where the point `p` may be specified in several +equivalent ways: +- by its coordinates `a` in the ambient_space of `X` or +- by a maximal ideal `I`in the coordinate ring of `X` or +- by a maximal ideal `I` in the ambient_coordinate_ring of `X` +- by the maximal ideal of the local ring `A` + +!!!note: Only `LocalRing`s localized at rational points over the coefficient field are currently fully supported. + +!!!note: If the defining ideal of `(X,p)` is not principal, an error exception occurs. + +# Examples +```jldoctest +julia> X = affine_space(QQ,3); + +julia> R = coordinate_ring(X); + +julia> (x,y,z) = gens(R); + +julia> Q,_ = quo(R,ideal(R,[x^2+y^2+z^2,x*y])); + +julia> Y,phi = complete_intersection_germ(Q,[0,0,0]); + +julia> Y +Spectrum + of localization + of quotient + of multivariate polynomial ring in 3 variables x1, x2, x3 + over rational field + by ideal(x1^2 + x2^2 + x3^2, x1*x2) + at complement of maximal ideal of point (0, 0, 0) + +julia> phi +Morphism + from [x1, x2, x3] Spec of localization of quotient of multivariate polynomial ring at complement of maximal ideal + to [x1, x2, x3] V(x1^2 + x2^2 + x3^2, x1*x2) +given by the pullback function + x1 -> x1 + x2 -> x2 + x3 -> x3 + +``` +""" +function complete_intersection_germ(X::AbsSpec, I::Union{Ideal,Vector}) + Y = CompleteIntersectionGerm(X,I) restr_map = SpecMor(Y, X, hom(OO(X), OO(Y), gens(OO(Y)), check=false), check=false) return Y, restr_map end +complete_intersection_germ(A::Union{MPolyRing,MPolyQuoRing}, + I::Union{Ideal,Vector}) = complete_intersection_germ(Spec(A),I) + +@doc raw""" + complete_intersection_germ(X::AbsSpec, p::AbsAffineRationalPoint) + complete_intersection_germ(p::AbsAffineRationalPoint) + +Return a complete intersection germ `(X,p)` and the corresponding inclusion morphism of spectra for a given `X` and a rational point `p` on some affine scheme `Y`. If no `X` is specified, `Y` is used in its place. + +!!!note + If the defining ideal of `(X,p)` does not describe a complete intersection. an error exception occurs. +""" +complete_intersection_germ(p::AbsAffineRationalPoint) = hypersurface_germ(codomain(p), coordinates(p)) + +function complete_intersection_germ(X::AbsSpec, p::AbsAffineRationalPoint) + ambient_space(X) == ambient_space(codomain(p)) || error("ambient spaces do not match") + return complete_intersection_germ(X,coordinates(p)) +end + ######################################################################################### ## for convenience of users thinking in terms of local rings ######################################################################################### @@ -225,6 +851,21 @@ function SpaceGerm(A::LocalRing) return SpaceGerm(Spec(A)) end +function HypersurfaceGerm(A::LocalRing) + I = modulus(A) + v = minimal_generating_set(I) + length(v) == 1 || error("not a hypersurface germ") + return HypersurfaceGerm(Spec(A),v[1]) +end + +function CompleteIntersectionGerm(A::LocalRing) + I = modulus(A) + !iszero(I) || error("zero ideal not allowed for complete intersection germ") + v = minimal_generating_set(I) + length(v) == dim(base_ring(I)) - dim(A) || error("not a complete intersection germ") + return CompleteIntersectionGerm(Spec(A),v) +end + ## and with identity map to keep usage consistent function germ_at_point(A::LocalRing) X = SpaceGerm(A) @@ -232,14 +873,31 @@ function germ_at_point(A::LocalRing) return X, restr_map end +function hypersurface_germ(A::LocalRing) + X = HypersurfaceGerm(A) + restr_map = SpecMor(X, X, hom(OO(X), OO(X), gens(OO(X)), check=false), check=false) + return X, restr_map +end + +function complete_intersection_germ(A::LocalRing) + X = CompleteIntersectionGerm(A) + restr_map = SpecMor(X, X, hom(OO(X), OO(X), gens(OO(X)), check=false), check=false) + return X, restr_map +end + ### basic functionality for space germs ############################################################################## -# note: ==, intersect are inherited from Spec -# intersect with explicit fallback to Spec and change of return type +# note: ==, issubset are basically inherited from Spec +# intersect uses explicit fallback to Spec and adjusted return types +# union uses explicit fallback and adjusted return types ############################################################################## - -function issubset(X::AbsSpaceGerm{<:Any, <:MPolyQuoLocRing}, Y::AbsSpaceGerm{<:Any, <:MPolyQuoLocRing}) +#@doc raw""" +# is_subset(X::AbsSpaceGerm,Y::AbsSpaceGerm) +# +#Return whether `X` is a subset of `Y` as space germs +#""" +function is_subset(X::AbsSpaceGerm{<:Any, <:MPolyQuoLocRing}, Y::AbsSpaceGerm{<:Any, <:MPolyQuoLocRing}) R = ambient_coordinate_ring(X) R === ambient_coordinate_ring(Y) || return false point(X) == point(Y) || return false @@ -247,31 +905,72 @@ function issubset(X::AbsSpaceGerm{<:Any, <:MPolyQuoLocRing}, Y::AbsSpaceGerm{<:A return issubset(IY,modulus(OO(X))) end -function issubset(X::AbsSpaceGerm{<:Any, <:MPolyLocRing}, Y::AbsSpaceGerm{<:Any, <:MPolyQuoLocRing}) +function is_subset(X::AbsSpaceGerm{<:Any, <:MPolyLocRing}, Y::AbsSpaceGerm{<:Any, <:MPolyQuoLocRing}) R = ambient_coordinate_ring(X) R === ambient_coordinate_ring(Y) || return false point(X) == point(Y) || return false return iszero(modulus(OO(Y))) end -function issubset(X::AbsSpaceGerm{<:Any, <:MPolyLocRing}, Y::AbsSpaceGerm{<:Any, <:MPolyLocRing}) +function is_subset(X::AbsSpaceGerm{<:Any, <:MPolyLocRing}, Y::AbsSpaceGerm{<:Any, <:MPolyLocRing}) R = ambient_coordinate_ring(X) R === ambient_coordinate_ring(Y) || return false return point(X) == point(Y) end -function issubset(X::AbsSpaceGerm{<:Any, <:MPolyQuoLocRing}, Y::AbsSpaceGerm{<:Any, <:MPolyLocRing}) +function is_subset(X::AbsSpaceGerm{<:Any, <:MPolyQuoLocRing}, Y::AbsSpaceGerm{<:Any, <:MPolyLocRing}) R = ambient_coordinate_ring(X) R === ambient_coordinate_ring(Y) || return false return point(X) == point(Y) end +## note: intersection of hypersurfaces is hardly ever a hypersurface +## intersection of complete intersections need not be complete intersection +## hence return type always SpaceGerm +@doc raw""" + intersect(X::AbsSpaceGerm,Y::AbsSpaceGerm) --> SpaceGerm + +Return the intersection of `X` and `Y`, provided they are germs at the same point. +""" function Base.intersect(X::AbsSpaceGerm, Y::AbsSpaceGerm) point(X) == point(Y) || error("not the same point of the germ") Z = intersect(underlying_scheme(X),underlying_scheme(Y)) return SpaceGerm(Z) end +@doc raw""" + union(X::AbsSpaceGerm,Y::AbsSpaceGerm) --> SpaceGerm + union(X::HypersurfaceGerm, Y:: HypersurfaceGerm) --> HypersurfaceGerm + +Return the union of `X`and `Y`. If `X`and `Y` happen to be HypersurfaceGerms, so is the result. + +# Examples +```jldoctest +julia> X = affine_space(QQ,3); + +julia> R = coordinate_ring(X); + +julia> (x,y,z) = gens(R); + +julia> Q1,_ = quo(R,ideal(R,[x*y])); + +julia> Q2,_ = quo(R,ideal(R,[x*(x-y)])); + +julia> X1 = HypersurfaceGerm(Spec(Q1),[0,0,0]); + +julia> X2 = HypersurfaceGerm(Spec(Q2),[0,0,0]); + +julia> union(X1,X2) +Spectrum + of localization + of quotient + of multivariate polynomial ring in 3 variables x1, x2, x3 + over rational field + by ideal(x1^2*x2 - x1*x2^2) + at complement of maximal ideal of point (0, 0, 0) + +``` +""" function Base.union(X::AbsSpaceGerm, Y::AbsSpaceGerm) R = ambient_coordinate_ring(X) R === ambient_coordinate_ring(Y) || error("not subgerms of a common space germ") @@ -283,21 +982,276 @@ function Base.union(X::AbsSpaceGerm, Y::AbsSpaceGerm) return Z end +## note: union of hypersurface germs is again a hypersurface germ +## union of complete intersection germs need not even be equidimensional +function Base.union(X::HypersurfaceGerm, Y::HypersurfaceGerm) + R = ambient_coordinate_ring(X) + R === ambient_coordinate_ring(Y) || error("not subgerms of a common space germ") + point(X) == point(Y) || error("not the same point of the germ") + # comparison of points implicitly also checks that localization was performed at points + # otherwise 'point' is not implemented + f_new = numerator(defining_ring_element(X))*numerator(defining_ring_element(Y)) + f_new = radical(ideal(R,[f_new]))[1] + Y = HypersurfaceGerm(Spec(quo(R,ideal(R,f_new))[1]), point(X)) + Y.f = f_new + return Y +end + ############################################################################## # note: singular_locus, is_smooth and is_regular are inherited from Spec ############################################################################## -# We want the singular locus of a `SpaceGerm` to be a `SpaceGerm` again and +# We want the singular locus of an `AbsSpaceGerm` to be a `SpaceGerm` again and # not a plain `Spec`. +@doc raw""" + singular_locus(X::AbsSpaceGerm) --> SpaceGerm, ClosedEmbedding + +Return the space germ (Y,p) for a given germ (X,p) and the closed embedding of (Y,p) into (X,p), where Y is the singular locus of X. + +# Examples +```jldoctest +julia> X = affine_space(QQ,3); + +julia> R = coordinate_ring(X); + +julia> (x,y,z) = gens(R); + +julia> Q,_ = quo(R,ideal(R,[x^2+y^2])); + +julia> Y = SpaceGerm(Spec(Q),[0,0,0]); + +julia> XSL, incSL = singular_locus(Y); + +julia> XSL +Spectrum + of localization + of quotient + of multivariate polynomial ring in 3 variables x1, x2, x3 + over rational field + by ideal(x1^2 + x2^2, x2, x1) + at complement of maximal ideal of point (0, 0, 0) + +julia> incSL +Morphism + from [x1, x2, x3] Spec of localization of quotient of multivariate polynomial ring at complement of maximal ideal + to [x1, x2, x3] Spec of localization of quotient of multivariate polynomial ring at complement of maximal ideal +given by the pullback function + x1 -> x1 + x2 -> x2 + x3 -> x3 + +``` +""" function singular_locus(X::AbsSpaceGerm) S, inc = singular_locus(underlying_scheme(X)) Sgerm = SpaceGerm(S) return Sgerm, ClosedEmbedding(SpecMor(Sgerm, X, pullback(inc), check=false), image_ideal(inc), check=false) end -function subscheme(X::SpaceGerm, I::Ideal) +## note: subgerms of hypersurface and complete intersection germs are simply space germs +@doc raw""" + subgerm(X::AbsSpaceGerm, I::Ideal) --> SpaceGerm + +Return the space germ (Y,p) of (X,p) defined by the ideal I in the local ring of X at p + +!!! note: (Y,p) is of type SpaceGerm, even if (X,p) is a HypersurfaceGerm or a CompleteIntersectionGerm. +""" +function subgerm(X::AbsSpaceGerm, I::Ideal) base_ring(I) === OO(X) || error("ideal does not belong to the correct ring") Y = subscheme(underlying_scheme(X), I) return SpaceGerm(Y) end +subscheme(X::AbsSpaceGerm, I::Ideal) = subgerm(X::AbsSpaceGerm, I::Ideal) + +@doc raw""" + is_isolated_singularity(X::AbsSpaceGerm) + +Return whether `(X,p)` has at most an isolated singularity. +```jldoctest +julia> X = affine_space(QQ,3); + +julia> R = coordinate_ring(X); + +julia> (x,y,z) = gens(R); + +julia> Q,_ = quo(R,ideal(R,[x^2+y^2])); + +julia> Y = SpaceGerm(Spec(Q),[0,0,0]); + +julia> is_isolated_singularity(Y) +false + +``` +""" +@attr Bool function is_isolated_singularity(X::AbsSpaceGerm) + return dim(singular_locus(X)[1]) < 1 +end + +############################################################################## +# milnor_number, milnor_algebra for IHS +# milnor_number for ICIS +# -- beyond this we do no longer have a bouquet of spheres of same dimension +############################################################################## +@doc raw""" + milnor_algebra(X::HypersurfaceGerm) + +Return the local Milnor algebra of `(X,p)` at p +""" +function milnor_algebra(X::HypersurfaceGerm) + R = localized_ring(OO(X)) + ## milnor number independent of choice of representative + ## hence choose a polynomial representative for easier computation + f_poly = numerator(defining_ring_element(X)) + I = ideal(R, R.([derivative(f_poly, i) for i=1:nvars(base_ring(R))])) + return quo(R,I)[1] +end + +@doc raw""" + milnor_number(X::HypersurfaceGerm) + milnor_number(X::CompleteIntersectionGerm) + +Return the local Milnor number of `(X,p)` at p +""" +function milnor_number(X::HypersurfaceGerm) + return vector_space_dimension(milnor_algebra(X)) +end + +function milnor_number(X::CompleteIntersectionGerm) + R = localized_ring(OO(X)) + ## milnor number independent of choice of representative + ## hence choose polynomial representatives for easier computation + v = [numerator(a) for a in defining_ring_elements(X)] + w = typeof(v[1])[] ## already used entries of v + dims = 0 ## for building up the alternating sum + sign = 1 ## and the sign + + ## alternating sum of the Le-Greuel formula, one summand per while-loop pass + while !is_empty(v) + found = false ## keep track of 'colength condition satisfied?' + + ## run through the potential choices, until colength condition satisfied + for f in v + ## note: although we have already moved to polynomial data, we need to + ## hand localized ring to helper to ensure shift to origin + ## before computations + dtemp = _icis_milnor_helper(R,w,f) ## colength; -1 if condition violated + if dtemp > -1 + dims = dims + sign * dtemp ## contribute to alternating sum + sign = -sign + push!(w,f) ## put f in 'used' list + deleteat!(v, findfirst(x->x==f,v)) ## remove f from 'unused' list + found = true + break + end + end + found == true || error("retry/general linear combinations not implemented yet") + end + if dims > 0 + return dims + end + return -dims +end + +## internal helper for the summands in the Le-Greuel formula -- local case +function _icis_milnor_helper(L::MPolyLocRing, v::Vector,f::RingElem) + R = parent(f) + R == base_ring(L) || error("base_rings do not match") + all(a -> parent(a)==R,v) || error("base_rings do not match") + + ## establish the shift to origin to allow computations w.r.t. local ordering + shift,back_shift = base_ring_shifts(L) + w = [shift(a) for a in v] + g = shift(f) + + ## compute the appropriate summand in the Le-Greuel formula + ## for the (lenght(w))-th contribution with specified choice + I = ideal(R,w) + push!(w,g) + n = nvars(R) + JM = matrix(R,n, length(w), + [derivative(h,i) for i=1:n for h in w]) + mo = minors(JM,length(w)) + J = I + ideal(R,mo) + !isone(J) || return 0 + o = negdegrevlex(gens(R)) + LJ = leading_ideal(J;ordering=o) + + ## we might have a violated colength condition (i.e. dim(LJ)>0) + dim(quo(R,LJ)[1]) == 0 || return (-1) + return vector_space_dimension(LJ) +end + +@doc raw""" + milnor_algebra(X::Spec{<:Field,<:MPolyQuoRing}) + +Return the global milnor algebra of the affine hypersurface `X`. If `X` is not a hypersurface, an error occurs. +""" +function milnor_algebra(X::Spec{<:Field,<:MPolyQuoRing}) + R = base_ring(OO(X)) + ngens(modulus(OO(X))) == 1 || error("not a hypersurface (or unnecessary generators in specified generating set)") + v = gen(modulus(OO(X)),1) + I = ideal(R,R.([derivative(v,i) for i in 1:nvars(R)])) + return quo(R,I)[1] +end + +@doc raw""" + milnor_number(X::Spec{<:Field,<:MPolyQuoRing}) + +Return the global milnor number of the affine hypersurface or complete intersection `X`. If `X` is neither of the two, an error occurs. +""" +function milnor_number(X::Spec{<:Field,<:MPolyQuoRing}) + R = base_ring(OO(X)) + v = gens(modulus(OO(X))) + if length(v) == 1 + return vector_space_dimension(milnor_algebra(X)) + end + length(v) == dim(R) - dim(X) || error("not a complete intersection (or unnecessary generators in specified generating set)") + w = typeof(v[1])[] ## already used entries of v + dims = 0 ## for building up the alternating sum + sign = 1 + while !is_empty(v) + found = false ## colength condition satisfied? + for f in v + ## note: in the global case, we do not need to shift + ## hence helper with completely different signature + dtemp = _icis_milnor_helper(w,f) ## colength; -1 if condition violated + if dtemp > -1 + dims = dims + sign * dtemp ## alternating sum + sign = -sign + push!(w,f) ## put f in 'used' list + deleteat!(v, findfirst(x->x==f,v)) ## remove f from 'unused' list + found = true + break + end + end + found == true || error("retry/general linear combinations not implemented yet") + end + if dims > 0 + return dims + end + return -dims +end + +## internal helper for the summands in the Le-Greuel formula -- global case +function _icis_milnor_helper(v::Vector,f::MPolyRingElem) + R = parent(f) + all(a-> parent(a) == R,v) || error("base rings do not match") + + ## compute the appropriate step in the Le-Greuel formula + ## for the (lenght(w))-th contribution + I = ideal(R,v) + bla = copy(v) + push!(bla,f) + n = nvars(R) + JM = matrix(R,n, length(bla), + [derivative(h,i) for i in 1:n for h in bla]) + mo = minors(JM,length(bla)) + J = I + ideal(R,mo) + o = degrevlex(gens(R)) + LJ = leading_ideal(J;ordering=o) + + ## we might have a violated colength condition (i.e. dim(LJ)>0) + dim(LJ) == 0 || return -1 + return vector_space_dimension(quo(R,LJ)[1]) +end diff --git a/src/Rings/mpoly-localizations.jl b/src/Rings/mpoly-localizations.jl index 9bd84da60fc9..68b6dced328b 100644 --- a/src/Rings/mpoly-localizations.jl +++ b/src/Rings/mpoly-localizations.jl @@ -3191,3 +3191,6 @@ function small_generating_set(I::MPolyLocalizedIdeal{<:MPolyLocRing{<:Field, <:F I_min = L.(small_generating_set(saturated_ideal(I))) return filter(!iszero, I_min) end + +dim(R::MPolyLocRing{<:Field, <:FieldElem, <:MPolyRing, <:MPolyElem, <:MPolyComplementOfPrimeIdeal}) = nvars(base_ring(R)) - dim(prime_ideal(inverted_set(R))) + diff --git a/src/Rings/mpolyquo-localizations.jl b/src/Rings/mpolyquo-localizations.jl index 10b57336ef07..7efed635ef81 100644 --- a/src/Rings/mpolyquo-localizations.jl +++ b/src/Rings/mpolyquo-localizations.jl @@ -2253,3 +2253,6 @@ function small_generating_set( J = pre_image_ideal(I) return filter(!iszero, Q.(small_generating_set(J))) end + +dim(R::MPolyQuoLocRing{<:Field, <:FieldElem, <:MPolyRing, <:MPolyElem, <:MPolyComplementOfPrimeIdeal}) = dim(saturated_ideal(modulus(R))) - dim(prime_ideal(inverted_set(R))) + diff --git a/src/exports.jl b/src/exports.jl index 37e8a6ab4d7c..ad4eac185be8 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -40,6 +40,7 @@ export BorcherdsCtx export ClosedEmbedding export ClosedSubvarietyOfToricVariety export CohomologyClass +export CompleteIntersectionGerm export Cone export CoveredScheme export CoveredSchemeMorphism @@ -78,6 +79,7 @@ export Halfspace export Hecke export HilbertData export Hyperplane +export HypersurfaceGerm export IncidenceMatrix export K3Chamber export K3_surface_automorphism_group @@ -156,6 +158,7 @@ export SimpleGlueing export SimplicialComplex export Singular export Sp +export SpaceGerm export Spec export SpecMor export SpecOpen @@ -229,6 +232,7 @@ export ambient_coordinates export ambient_dim export ambient_embedding export ambient_free_module +export ambient_germ export ambient_module export ambient_representative export ambient_representatives_generators @@ -363,6 +367,7 @@ export complement_of_prime_ideal export complement_system, has_complement_system, set_complement_system export complete_bipartite_graph export complete_graph +export complete_intersection_germ export complex_projective_plane export components export compose @@ -423,6 +428,8 @@ export decoration export default_covering export default_ordering export defines_automorphism +export defining_ring_element +export defining_ring_elements export deglex export degree export degrees_of_generators @@ -566,6 +573,7 @@ export generic_fractions export gens, has_gens export gens_of_rational_equivalence_classes export geometric_irreducible_components +export germ_at_point export girth export gkz_vector export glueing_domains @@ -648,6 +656,7 @@ export hyperplane export hyperplanes export hypersimplex export hypersurface_complement +export hypersurface_germ export hypertruncated_cube export icosahedron export id_hom @@ -772,6 +781,7 @@ export is_invariant export is_inverse_of export is_invertible export is_irreducible +export is_isolated_singularity export is_isomorphic export is_isomorphic_with_alternating_group, has_is_isomorphic_with_alternating_group, set_is_isomorphic_with_alternating_group export is_isomorphic_with_map @@ -954,6 +964,8 @@ export maximal_polyhedra export maximal_subgroup_reps export maximal_subgroups, has_maximal_subgroups, set_maximal_subgroups export metadata +export milnor_algebra +export milnor_number export min_weights export minimal_betti_table export minimal_block_reps @@ -1119,6 +1131,7 @@ export platonic_solid export point_coordinates export point_matrix export point_vector +export point export points export pol_elementary_divisors export polarize @@ -1191,6 +1204,7 @@ export random_affine_linear_polynomials export rank export rank_action export rational_equivalence_class +export rational_point_coordinates export rational_solutions export rational_to_continued_fraction_hirzebruch_jung export ray_indices diff --git a/test/AlgebraicGeometry/Schemes/SpaceGerms.jl b/test/AlgebraicGeometry/Schemes/SpaceGerms.jl index eb61452ea86f..e8da7ec267ff 100644 --- a/test/AlgebraicGeometry/Schemes/SpaceGerms.jl +++ b/test/AlgebraicGeometry/Schemes/SpaceGerms.jl @@ -8,6 +8,10 @@ X1 = SpaceGerm(X,[1,2,1]) Y0 = SpaceGerm(Y,[0,0,0]) Y1 = SpaceGerm(Y,[1,2,1]) + Z0 = HypersurfaceGerm(X,[0,0,0]) + @test defining_ring_element(Z0) isa elem_type(Oscar.localized_ring_type(ring_type(Z0))) + Z1 = CompleteIntersectionGerm(X,[1,2,1]) + @test defining_ring_elements(Z1) isa Vector{elem_type(Oscar.localized_ring_type(ring_type(Z0)))} U0 = MPolyComplementOfKPointIdeal(R,[0,0,0]) U1 = MPolyComplementOfKPointIdeal(R,[1,2,1]) @test U0 == inverted_set(OO(X0)) @@ -15,6 +19,37 @@ @test X0 == Y0 @test X1 !=Y1 @test isempty(Y1) + @test milnor_number(Z0) == 1 + @test milnor_number(Z1) == 0 + XG = Spec(R,I) + @test milnor_number(XG) == 1 + K = ideal(R,[x^2+y^2-z^2,x*y]) + YG = Spec(R,K) + @test milnor_number(YG) == 5 + K = ideal(R,[x*y,x^2+y^2-z^2]) + ZG = Spec(R,K) + @test milnor_number(YG) == 5 +end + +@testset "SpaceGerms at geometric points" begin + X = affine_space(QQ,3) + OOX = coordinate_ring(X) + (x,y,z) = gens(OOX) + I = ideal(OOX,[x+y+z^2+1]) + J = ideal(OOX,[x+y,z^2+1]) + QI,_ = quo(OOX,I) + QJ,_ = quo(OOX,J) + U = complement_of_prime_ideal(ideal(OOX,[x,y,z^2+1])) + RI,_ = localization(QI,U) + RJ,_ = localization(QJ,U) + Y = SpaceGerm(RI) + @test OO(Y) == RI +# HypersurfaceGerm(RI) does not work without standard bases +# CompleteIntersectionGerm(TI) does not work without standard bases + V = complement_of_prime_ideal(ideal(OOX,[x,z^2+1])) + SI,_ = localization(QI,V) + Z = SpaceGerm(SI) + @test_broken dim(Z) == 1 end @testset "Space Germ constructors Spec-Ideal" begin @@ -123,9 +158,9 @@ end @test point(X0)==[0,0,0] Y = representative(X0) Y1 = representative(X1) - @test ideal(X0) != ideal(R,zero(R)) - @test ideal(X0) == ideal(OO(X0),[0]) - @test ideal(X2) == ideal(localized_ring(OO(X2)),[x,y]) + @test defining_ideal(X0) != ideal(R,zero(R)) + @test defining_ideal(X0) == ideal(OO(X0),[0]) + @test defining_ideal(X2) == ideal(localized_ring(OO(X2)),[x,y]) @test inverted_set(OO(ambient_germ(X2))) == MPolyComplementOfKPointIdeal(R,[0,0,0]) @test ambient_germ(X0) == X0 end @@ -147,13 +182,13 @@ end Z0 = SpaceGerm(Z,[0,0,0]) W0 = SpaceGerm(W,[0,0,0]) SY0 = SpaceGerm(SY,[0,0,0]) - @test isempty(Z0) - @test issubset(Z0,X0) - @test issubset(Y0,X0) - @test issubset(Z0,Y0) - @test issubset(X0,Y0) - @test !issubset(W0,X0) - @test !issubset(X0,Z0) + @test is_empty(Z0) + @test is_subset(Z0,X0) + @test is_subset(Y0,X0) + @test is_subset(Z0,Y0) + @test is_subset(X0,Y0) + @test !is_subset(W0,X0) + @test !is_subset(X0,Z0) @test Y0 == intersect(X0,Y0) V0=intersect(W0,X0) @test V0 == SpaceGerm(V,[0,0,0]) diff --git a/test/Rings/mpoly-localizations.jl b/test/Rings/mpoly-localizations.jl index 16efdb2bb43d..d9eccb816696 100644 --- a/test/Rings/mpoly-localizations.jl +++ b/test/Rings/mpoly-localizations.jl @@ -425,3 +425,21 @@ end @test length(small_gens) == 1 @test isone(first(small_gens)) end + +@testset "dimensions of localizations at prime ideals" begin + R, (x, y, z) = QQ[:x, :y, :z] + f = x^2 + x*y^2 - z^3 + P = ideal(R, f) + U = complement_of_prime_ideal(P) + L, loc = localization(R, U) + @test dim(L) == 1 + + U = complement_of_prime_ideal(ideal(R, [x^2 + 1, y])) + L, loc = localization(R, U) + @test dim(L) == 2 + + U = complement_of_prime_ideal(ideal(R, [x^2 + 1, y, z])) + L, loc = localization(R, U) + @test dim(L) == 3 +end + diff --git a/test/Rings/mpolyquo-localizations.jl b/test/Rings/mpolyquo-localizations.jl index 900ed9b81b6d..8378b10f10a2 100644 --- a/test/Rings/mpolyquo-localizations.jl +++ b/test/Rings/mpolyquo-localizations.jl @@ -327,3 +327,19 @@ end @test length(minJ) == 1 @test ideal(L,minJ) == J end + +@testset "dimensions of localizations at prime ideals" begin + R, (x, y, z) = QQ[:x, :y, :z] + A, pr = quo(R, ideal(R, [x^2 + 1])) + U = complement_of_prime_ideal(modulus(A) + ideal(R, y)) + L, loc = localization(A, U) + @test dim(L) == 1 + + U = complement_of_prime_ideal(modulus(A)) + L, loc = localization(A, U) + @test dim(L) == 0 + + U = complement_of_prime_ideal(modulus(A) + ideal(R, [y, z])) + L, loc = localization(A, U) + @test dim(L) == 2 +end