Architecture of affine schemes

Requirements on rings and ideals

Any type of ring $R$ to be used within the schemes framework must come with its own ideal type IdealType<:Ideal for which we require the following interface to be implemented:

# constructor of ideals in R
ideal(R::RingType, g::Vector{<:RingElem})::Ideal

# constructor for quotient rings
quo(R::RingType, I::IdealType)::Tuple{<:Ring, <:Map}

# ideal membership test
in(f::RingElem, I::IdealType)::Bool

# a (fixed) set of generators
gens(I::IdealType)::Vector{<:RingElem}

# writing an element as linear combination of the generators
coordinates(f::RingElem, I::IdealType)::Vector{<:RingElem}

The latter function must return a vector $v = (a_1,\dots, a_r)$ of elements in $R$ such that $f = a_1 \cdot g_1 + \dots + a_r \cdot g_r$ where $g_1,\dots,g_r$ is the set of gens(I). When $f$ does not belong to $I$, it must error. Note that the ring returned by quo must again be admissible for the AbsAffineScheme interface.

With a view towards the use of the ambient_coordinate_ring(X) for computations, it is customary to also implement

saturated_ideal(I::IdealType)::MPolyIdeal

returning an ideal $J$ in the ambient_coordinate_ring(X) with the property that $a \in I$ for some element $a \in R$ if and only if lifted_numerator(a) is in $J$.

Interplay between ambient coordinate ring and coordinate ring

Let $X$ be an affine variety. In practice, all computations in the coordinate ring R = OO(X) will be deferred to computations in P = ambient_coordinate_ring(X) in one way or the other; this is another reason to include the ambient affine space in our abstract interface for affine schemes. In order to make the ambient_coordinate_ring(X) accessible for this purpose, we need the following methods to be implemented for elements $a\in R$ of type RingElemType:

lifted_numerator(a::RingElemType)
lifted_denominator(a::RingElemType)

These must return representatives of the numerator and the denominator of $a$. Note that the denominator is equal to one(P) in case $R \cong P$ or $R \cong P/I$.

Recall that the coordinates $x_i$ of $X$ are induced by the coordinates of the ambient affine space. Moreover, we will assume that for homomorphisms from $R$ there is a method

hom(R::RingType, S::Ring, a::Vector{<:RingElem})

where RingType is the type of $R$ and a the images of the coordinates $x_i$ in $S$. This will be important when we come to morphisms of affine schemes below.

Algebraically speaking, embedding the affine scheme $X = Spec(R)$ over $𝕜$ into an affine space with coordinate ring $P = 𝕜[x₁,…,xₙ]$ corresponds to a morphism of rings $P → R$ with the property that for every other (commutative) ring $S$ and any homomorphism $φ : R → S$ there is a morphism $ψ : P → S$ factoring through $φ$ and such that $φ$ is uniquely determined by $ψ$. Note that the morphism $P → R$ is induced by natural coercions.

Existing types of affine schemes and how to derive new types

The abstract type for affine schemes is

AbsAffineSchemeType
AbsAffineScheme{BaseRingType, RingType<:Ring}

An affine scheme $X = Spec(R)$ with $R$ of type RingType over a ring $𝕜$ of type BaseRingType.

source

For any concrete instance of this type, we require the following functions to be implemented:

  • base_ring(X::AbsAffineScheme),
  • OO(X::AbsAffineScheme).

A concrete instance of this type is

AffineSchemeType
AffineScheme{BaseRingType, RingType}

An affine scheme $X = Spec(R)$ with $R$ a Noetherian ring of type RingType over a base ring $𝕜$ of type BaseRingType.

source

It provides an implementation of affine schemes for rings $R$ of type MPolyRing, MPolyQuoRing, MPolyLocRing, and MPolyQuoLocRing defined over the integers or algebraic field extensions of $\mathbb Q$. This minimal implementation can be used internally, when deriving new concrete types MyAffineScheme<:AbsAffineScheme such as, for instance, group schemes, toric schemes, schemes of a particular dimension like curves and surfaces, etc. To this end, one has to store an instance Y of AffineScheme in MyAffineScheme and implement the methods

underlying_scheme(X::MyAffineScheme)::AffineScheme # return Y as above

Then all methods implemented for AffineScheme are automatically forwarded to any instance of MyAffineScheme.

Note: The above method necessarily returns an instance of AffineScheme! Of course, it can be overwritten for any higher type MyAffineScheme<:AbsAffineScheme as needed.

Note: For implementations which do not explicitly compute the underlying_scheme on construction, but in a lazy way, the computation of underlying_scheme can be quite expensive. Yet, even for simple functions such as e.g. show, the default methods trigger this computation. Therefore, even calls to functions which may seem trivial can take a long time. In order to avoid such behavior, it is the programmer's responsibility to not only overwrite underlying_scheme for a new custom type, but eventually also methods for other functions so that underlying_scheme is not computed unnecessarily.

Existing types of affine scheme morphisms and how to derive new types

Any abstract morphism of affine schemes is of the following type:

AbsAffineSchemeMorType
AbsAffineSchemeMor{DomainType<:AbsAffineScheme,
           CodomainType<:AbsAffineScheme,
           PullbackType<:Map,
           MorphismType,
           BaseMorType
           }

Abstract type for morphisms $f : X → Y$ of affine schemes where

  • $X = Spec(S)$ is of type DomainType,
  • $Y = Spec(R)$ is of type CodomainType,
  • $f^* : R → S$ is a ring homomorphism of type PullbackType,
  • $f$ itself is of type MorphismType (required for the Map interface),
  • if $f$ is defined over a morphism of base schemes $BX → BY$ (e.g. a field extension), then this base scheme morphism is of type BaseMorType; otherwise, this can be set to Nothing.
source

Any such morphism has the attributes domain, codomain and pullback. A concrete and minimalistic implementation exist for the type AffineSchemeMor:

AffineSchemeMorType
AffineSchemeMor{DomainType<:AbsAffineScheme,
        CodomainType<:AbsAffineScheme,
        PullbackType<:Map
       }

A morphism $f : X → Y$ of affine schemes $X = Spec(S)$ of type DomainType and $Y = Spec(R)$ of type CodomainType, both defined over the same base_ring, with underlying ring homomorphism $f^* : R → S$ of type PullbackType.

source

This basic functionality consists of

  • compose(f::AbsAffineSchemeMor, g::AbsAffineSchemeMor),
  • identity_map(X::AbsAffineScheme),
  • restrict(f::AbsAffineSchemeMor, X::AbsAffineScheme, Y::AbsAffineScheme; check::Bool=true),
  • ==(f::AbsAffineSchemeMor, g::AbsAffineSchemeMor),
  • preimage(f::AbsAffineSchemeMor, Z::AbsAffineScheme).

In particular, for every concrete instance of a type MyAffineScheme<:AbsAffineScheme that implements underlying_scheme, this basic functionality of AffineSchemeMor should run naturally.

We may derive higher types of morphisms of affine schemes MyAffineSchemeMor<:AbsAffineSchemeMor by storing an instance g of AffineSchemeMor inside an instance f of MyAffineSchemeMor and implementing

underlying_morphism(f::MyAffineSchemeMor)::AffineSchemeMor # return g

For example, this allows us to define closed embeddings.