The Chow Ring of a Scheme

In general, the Chow ring of an algebraic variety is a graded ring, an algebraic analogue to the cohomology ring of its underlying topological space. Its elements correspond to formal linear combinations of algebraic subvarieties modulo rational equivalence. The product structure comes from the intersection of subvarieties.

See http://en.wikipedia.org/wiki/Chow_ring for an introduction to Chow Rings.

Calculating the Chow ring of an algebraic (projective, smooth) variety is a non trivial task. Here we suppose that the Chow ring is already given by generators and relations and will first provide some standard methods as computing a linear basis, the intersection matrix or the dual basis. Note also that we always work over \(\QQ\).

Later we will provide methods to compute the Chow ring for related algebraic varieties as a Grassmann bundle or a blowup (grass, blowup).

For example the Chow ring of the projective line is given by its generator \(h\) in degree \(1\) subject to the relation \(h^2\):

sage: A = ChowRing('h', 1, 'h^2')

Another example is the Chow ring of the projective plane blown up in a point given by two classes \(E\) and \(H\) in degree one subject to the relations \(E.H=0\) and \(E^2+H^2=0\). Note that the latter relations are not in a standard basis. However when retrieving the relations, a standard basis is returned:

sage: A = ChowRing(['H', 'E'], [1, 1], ['E*H', 'E^2+H^2'])
sage: A.rels()
[E^3, H^2 + E^2, H*E]

An important method for a ChowRing is the computation of a linear basis of algebraic cycles ChowRing_generic.basis():

sage: A = ChowRing('h', 1, 'h^3')  # P2
sage: A.basis()
[h^2, h, 1]

Note however we do not ask for a ChowRing to be Artin, even though this is the case, at least for projective smooth algebraic varieties. So the following is perfectly valid for us:

sage: A = ChowRing(['c1', 'c2'], [1, 2])

The reason for this is that it is sometimes very useful to compute in these more general rings, especially while computing the relations for a particular variety. Note that if \(A\) is not Artin, it does not make sens to compute a linear basis and an error message is thrown:

sage: A.basis()
Traceback (most recent call last):
...
ValueError: Ring is not of dimension 0.

If A is the Chow ring of a projective smooth algebraic variety, a point_class is an element in top degree that integrates to one. For example, for the projective space of dimension \(n\) with generator \(h\), the point_class is \(h^n\). Once we have a point class, we can compute the intersection matrix and the basis dual to those given by ChowRing_generic.basis() with respect to the intersection matrix:

sage: A.<h> = ChowRing('h', 1, 'h^4')  # P3
sage: A.basis()
[h^3, h^2, h, 1]
sage: A.set_point_class(h^3)
sage: A.intersection_matrix()
[0 0 0 1]
[0 0 1 0]
[0 1 0 0]
[1 0 0 0]
sage: A.dual_basis_slow()
[1, h, h^2, h^3]

Note that dual_basis_slow() is, as its name indicates, slow at least for large examples (e.g. with a large linear basis of A). This is due to the fact that ChowRing_generic.dual_basis_slow() simply computes the intersection matrix, then takes its inverse. A much faster method is ChowRing_generic.dual_basis() which computes only the relevant (i.e. in complementary degrees) part of the intersection matrix. Setting the optional parameter verbose=True allows to follow the computation:

sage: A.<H, E> = ChowRing(['H', 'E'], [1, 1], ['E*H', 'E^2+H^2'])
sage: A.basis()
[E^2, E, H, 1]
sage: A.set_point_class(H^2)
sage: A.dual_basis(verbose=True)
Computing block 0 of size 1 ...
Computing block 1 of size 2 ...
[-1, -E, H, -E^2]

Finally, there is a particular PointChowRing which is defined as the ChowRing with no generators and relations:

sage: A = ChowRing()
sage: A.set_dimension(0)
sage: A.set_point_class(1)
sage: B = PointChowRing
sage: A == B
True

In this case the underlying ring is \(\QQ[]/(O)\) and not simply \(\QQ\) as one might suspect, in order to get uniformly quotient rings:

sage: A = ChowRing(); A
Quotient of Multivariate Polynomial Ring in no variables over Rational Field by the ideal (0)
sage: A.gens()
()
sage: A.ngens()
0
sage: A.gen()
Traceback (most recent call last):
...
ValueError: Generator not defined.
sage: A.variable_names()
()
sage: A.variable_name()
Traceback (most recent call last):
...
IndexError: tuple index out of range

In the general case, when get as expected:

sage: A = ChowRing('h', 1, 'h^2')  # ChowRing of P1
sage: A.gens()
(h,)
sage: A.variable_names()
('h',)
sage: A.variable_name()
'h'
sage: A = ChowRing(['c1', 'c2'], [1, 2])
sage: A.variable_names()
('c1', 'c2')
sage: A.variable_name()
'c1'

AUTHORS:

  • Manfred Lehn (2013)
  • Christoph Sorger (2013)
sage.schemes.chow.ring.ChowRing(generators=None, degrees=None, relations=None, names=None, name=None, latex_name=None)

Return a ChowRing, given its dimension, generators, degrees, relations and point_class.

INPUT:

  • generators– An optional (list of) strings, the generators
  • degrees– An optional (list of) integers, the degrees of the generators
  • relations– An optional (list of) strings, the relations between the generators
  • names– An optional (list of) strings, names of the generators in the ChowRing
  • name– An optional string, the name of the ChowRing
  • latex_name– An optional string, a latex representation of the ChowRing

OUTPUT:

  • A ChowRing

EXAMPLES:

The ChowRing of the projective line is given by a generator \(h\) in degree \(1\), subject to the relation \(h^2\):

sage: A = ChowRing('h', 1, 'h^2')

It can equally be defined more verbosely by:

sage: B = ChowRing(generators='h', degrees=1, relations='h^2')
sage: A == B
True

If there is more than one generator (or more than one relation) they have to be specified as lists of strings:

sage: A = ChowRing(['H', 'E'], [1, 1], ['E*H', 'E^2+H^2'])

Note that by default the generators in the ChowRing have the same names as in the cover_ring as opposed to a quotient by a PolynomialRing:

sage: A = ChowRing('h', 1, 'h^2')
sage: A.gens()
(h,)
sage: P = PolynomialRing(QQ, 'h').quotient('h^2')
sage: P.gens()
(hbar,)

As for QuotientRings, the generators can be named explicitly:

sage: A.<x> = ChowRing('h', 1, 'h^2')
sage: A.gens()
(x,)

Only the dimension is mandatory. For example, one may define a point Chow Ring as follows:

sage: A = ChowRing(0); A
Quotient of Multivariate Polynomial Ring in no variables over Rational Field by the ideal (0)

Please note that the internal structure of A is not the field \(\QQ\) but rather the Quotient of \(\QQ\) by the ideal \((0)\). This is due to the fact that ChowRings are uniformly represented as quotient rings for us.

sage.schemes.chow.ring.ChowRingFromRing(S, names=None, name=None, latex_name=None)

Returns a ChowRing, given a ring S.

INPUT:

  • S – A (quotient of) a multivariate Polynomial Ring

OUTPUT:

  • The ChowRing defined by S

EXAMPLES:

sage: from sage.schemes.chow.ring import ChowRingFromRing
sage: variables, degrees, relations = ['x', 'y'], [2, 3], ['x*y']
sage: AX = ChowRing(variables, degrees, relations)
sage: OT = TermOrder('wdegrevlex', (2, 3))
sage: S = PolynomialRing(QQ, ['x', 'y'], order=OT).quotient('x*y'); S
Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x*y)
sage: AS = ChowRingFromRing(S)
sage: AS == AX
True

Note that we expect the ring S to be (quotient of) a multivariate Polynomial Ring:

sage: S = PolynomialRing(QQ, 'h').quotient('h^2'); S
Univariate Quotient Polynomial Ring in hbar over Rational Field with modulus h^2
sage: AS = ChowRingFromRing(S)
Traceback (most recent call last):
...
TypeError: Expected (Quotient of) Multivariate Polynomial Ring.
class sage.schemes.chow.ring.ChowRing_generic(R, I, names=None, name=None, latex_name=None)

Bases: sage.rings.quotient_ring.QuotientRing_generic

Construct a ChowRing_generic.

Warning

This class does not perform any checks of correctness of input. Use ChowRing() or ChowRingFromRing() to construct a ChowRing.

INPUT:

  • R\(\QQ\) or a multivariate polynomial ring;
  • I – an ideal of R;
  • names – list of variable names;
  • name– An optional string, the name of the ChowRing
  • latex_name– An optional string, a latex representation of the ChowRing

OUTPUT:

TESTS

sage: A = ChowRing()
ChowRingElement

alias of sage.schemes.chow.ring_element.ChowRingElement

Element

alias of sage.schemes.chow.ring_element.ChowRingElement

basis()

Returns a basis of the underlying ring of this ChowRing if the underlying ring is Artin.

OUTPUT:

  • A list of elements of the underlying ring.

EXAMPLES:

sage: A = ChowRing('h', 1, 'h^4')  # P3
sage: A.basis()
[h^3, h^2, h, 1]

sage: A = ChowRing(['H', 'E'], [1, 1], ['E*H', 'E^2+H^2'])
sage: A.basis()
[E^2, E, H, 1]

sage: A = ChowRing(['c1', 'c2'], [1, 2])  # No relations!
sage: A.basis()
Traceback (most recent call last):
...
ValueError: Ring is not of dimension 0.
basis_by_degree()

Return a basis of the underlying ring of this ChowRing ordered by the degrees of the elements.

OUTPUT:

  • A list of list of elements of the underlying ring.

EXAMPLES:

sage: A = ChowRing('h', 1, 'h^4')  # P3
sage: A.basis_by_degree()
[[1], [h], [h^2], [h^3]]

sage: A = ChowRing(['H', 'E'], [1, 1], ['E*H', 'E^2+H^2'])
sage: A.basis_by_degree()
[[1], [E, H], [E^2]]
deg(i=None)

Return the degree of the i-th generator. If i is unspecified the degree of the first generator (eg i=0) is returned if exists.

INPUT:

  • i – an (optional) integer.

OUTPUT:

The degree of the i-th generator.

EXAMPLES:

sage: A = ChowRing('h', 1, 'h^2')  # ChowRing of P1
sage: A.deg()
1
sage: A = ChowRing(['c1', 'c2'], [1, 2])
sage: A.deg()
1
sage: A.deg(1)
2

CORNER CASE:

sage: A = PointChowRing
sage: A.deg()
Traceback (most recent call last):
...
IndexError: tuple index out of range
degs()

Return the degrees of the generators of this ChowRing.

OUTPUT:

A tuple of integers representing the degrees of the generators.

EXAMPLES:

sage: A = ChowRing('h', 1, 'h^2')  # ChowRing of P1
sage: A.degs()
(1,)
sage: A = ChowRing(['c1', 'c2'], [1, 2])
sage: A.degs()
(1, 2)

CORNER CASE:

sage: A = PointChowRing
sage: A.degs()
()
dimension()

Return the variety dimension (see \(:meth:set_dimension\)).

OUTPUT:

  • an integer

EXAMPLES:

sage: A = ChowRing('h', 1, 'h^2')
sage: A.dimension()  # None as not set yet.
sage: A.set_dimension(1)
sage: A.dimension()
1
dual_basis(verbose=False)

Returns the basis dual to basis() with respect to the intersection matrix. We carefully compute only the relevant part of the intersection matrix in order to invert several smaller matrices instead of one large matrix as in dual_basis_slow(). This method is mainly needed to compute \(f_*\) for morphisms \(f:X\rightarrow Y\) hence especially while computing a blowup and its tangentbundle.

Remark: This requires quite some computational time for large examples, so we print out which block we compute if verbose is True.

INPUT:

  • verbose – an (optional) flag for printing intermediate steps

OUTPUT:

  • A list of ring elements representing the basis dual to basis().

EXAMPLES:

sage: A.<H, E> = ChowRing(['H', 'E'], [1, 1], ['E*H', 'E^2+H^2'])
sage: A.set_point_class(H^2)
sage: A.dual_basis(verbose=True)
Computing block 0 of size 1 ...
Computing block 1 of size 2 ...
[-1, -E, H, -E^2]
dual_basis_dict(verbose=False)

Returns the dictionary \(e_i\rightarrow e_i^*\) where \(e_i\) is the basis given by basis() and \(e_i^*\) is the dual element. If verbose is set to True, intermediate steps are printing during the calculation of the dual basis.

INPUT:

  • verbose – an (optional) flag for printing intermediate steps

OUTPUT:

  • A dictionary associating \(e_i\rightarrow e_i^*\).

EXAMPLES:

sage: A = ChowRing(['H', 'E'], [1, 1], ['E*H', 'E^2+H^2'])
sage: A.set_point_class('H^2')
sage: DualBasis = A.dual_basis_dict()
sage: [(key, DualBasis[key]) for key in sorted(DualBasis)]
[(1, -E^2), (E, -E), (H, H), (E^2, -1)]
dual_basis_slow()

Returns the basis dual to self.basis() with respect to the intersection matrix. It is computed by simply inverting the intersection matrix. For a large ring basis, this method is somehow slow and has been optimized in self.dual_basis().

OUTPUT:

  • A list of ring elements representing the basis dual to self.basis()

EXAMPLES:

sage: A = ChowRing(['H', 'E'], [1, 1], ['E*H', 'E^2+H^2'])
sage: A.set_point_class('H^2')
sage: A.dual_basis_slow()
[-1, -E, H, -E^2]
identity_morphism()

Returns the identity morphism of this ChowRing.

OUTPUT:

  • The identity morphism

EXAMPLES:

sage: A = ChowRing('h', 1, 'h^2')
sage: f = A.identity_morphism()
sage: h = A.gen()
sage: f(h)
h
sage: f(1)
1

sage: A = PointChowRing
sage: f = A.identity_morphism()
sage: f(1)
1
intersection_matrix()

Returns the intersection matrix of this ChowRing.

OUTPUT:

  • A matrix, the intersection matrix of this ChowRing

EXAMPLES:

sage: A = ChowRing('h', 1, 'h^3')  # P2
sage: A.set_point_class('h^2')
sage: A.intersection_matrix()
[0 0 1]
[0 1 0]
[1 0 0]

sage: A.<H, E> = ChowRing(['H', 'E'], [1, 1], ['E*H', 'E^2+H^2'])
sage: A.basis()
[E^2, E, H, 1]
sage: A.set_point_class(H^2)
sage: A.intersection_matrix()
[ 0  0  0 -1]
[ 0 -1  0  0]
[ 0  0  1  0]
[-1  0  0  0]
is_point()

Returns True if this ChowRing corresponds to a point, e.g. is of dimension 0 and has no generators.

OUTPUT:

  • True or False

EXAMPLES:

sage: A = ChowRing('h', 1, 'h^4')  # ChowRing of P3
sage: A.is_point()
False

sage: A = ChowRing()
sage: A.is_point()
True
is_point_chowring()

Returns True if this ChowRing is the instance PointChowRing.

OUTPUT:

  • True or False

EXAMPLES:

sage: A = ChowRing()
sage: A.is_point_chowring()
False

sage: A = PointChowRing
sage: A.is_point_chowring()
True
krull_dimension()

Returns the Krull dimension of the underlying ring of this ChowRing. Remark: this is not the dimension of this ChowRing specified while defining this ChowRing.

OUTPUT:

  • An integer, the Krull dimension of this ring.

EXAMPLES:

sage: A = ChowRing('h', 1, 'h^4')  # P3
sage: A.krull_dimension()
0
sage: A = ChowRing(['c1', 'c2'], [1, 2])  # No relations!
sage: A.krull_dimension()
2
max_degree()

Return the maximal degree of this ChowRing if Artin else 0.

EXAMPLES:

sage: A = ChowRing('h', 1, 'h^4')  # P3
sage: A.max_degree()
3

sage: A = ChowRing(['H', 'E'], [1, 1], ['E*H', 'E^2+H^2'])
sage: A.max_degree()
2

sage: A = ChowRing('h', 1)  # not Artin
sage: A.max_degree()
0
nrels()

Return the number of relations of this ChowRing

OUTPUT:

An integer, the number of relations.

EXAMPLES:

sage: A = ChowRing('h', 1, 'h^2')  # ChowRing of P1
sage: A.nrels()
1
sage: A = ChowRing(['c1', 'c2'], [1, 2])
sage: A.nrels()
0
point_class()

Return the point class.

OUTPUT:

  • a ring element

EXAMPLES:

sage: A.<h> = ChowRing('h', 1, 'h^3')   # P2
sage: A.set_point_class(h^2)
sage: A.point_class()
h^2
rel(i=None)

Return the i-th relation. If i is unspecified the first relation (eg i=0) is returned if exists.

INPUT:

  • i – an (optional) integer.

OUTPUT:

The i-th relation.

EXAMPLES:

sage: A = ChowRing('h', 1, 'h^2')  # ChowRing of P1
sage: A.rel()
h^2
rels()

Return the relations of this ChowRing.

OUTPUT:

A list of ring elements representing the relations.

EXAMPLES:

sage: A = ChowRing('h', 1, 'h^2')  # ChowRing of P1
sage: A.rels()
[h^2]
sage: A = ChowRing(['c1', 'c2'], [1, 2])
sage: A.rels()
[]
set_dimension(dimension)

Set the dimension. If this ChowRing is the Chow ring of a variety \(X\), set the dimension to \(\dim(X)\)

INPUT:

  • an integer

EXAMPLES:

sage: A = ChowRing('h', 1, 'h^3')   # P2
sage: A.set_dimension(2)    # dim(P2)
set_point_class(point_class)

Set the point class.

EXAMPLES:

sage: A.<h> = ChowRing('h', 1, 'h^2')   # P2
sage: A.set_point_class(h)
sage.schemes.chow.ring.Kernel(f)

Given a morphism \(f:A\rightarrow B\) between (quotients of) multivariate polynomial rings, the kernel of \(f\) is returned.

EXAMPLE (http://trac.sagemath.org/sage_trac/ticket/9792):

sage: from sage.schemes.chow.ring import Kernel
sage: R = PolynomialRing(QQ, 4, names=['x', 'y', 'z', 'w'])
sage: S = PolynomialRing(QQ, 2, names=['s', 't'])
sage: s, t = S.gens()
sage: f = R.hom([s ** 4, s ** 3 * t, s * t ** 3, t ** 4], S)
sage: Kernel(f).gens()
[y*z - x*w, z^3 - y*w^2, x*z^2 - y^2*w, y^3 - x^2*z]

TESTS (taken from the Singular documentation of alg_kernel and kernel):

sage: from sage.schemes.chow.ring import Kernel
sage: R = PolynomialRing(QQ, 3, names=['a', 'b', 'c'])
sage: S = PolynomialRing(QQ, 6, names=['x', 'y', 'z', 'u', 'v', 'w'])
sage: x, y, z, u, v, w = S.gens()
sage: f = R.hom([x - w, u^2 * w + 1, y * z - v], S)
sage: Kernel(f).gens()
[0]

sage: from sage.schemes.chow.ring import Kernel
sage: R = PolynomialRing(QQ, 3, names=['a', 'b', 'c'])
sage: S = PolynomialRing(QQ, 3, names=['x', 'y', 'z']).quotient(x - y)
sage: x, y, z = S.gens()
sage: f = R.hom([x, y, x^2 - y^3], S)
sage: Kernel(f).gens()
[a - b, b^3 - b^2 + c]

sage: from sage.schemes.chow.ring import Kernel
sage: R = PolynomialRing(QQ, 4, names=['a', 'b', 'c', 'd'])
sage: S = PolynomialRing(QQ, 3, names=['a', 'b', 'c'])
sage: a, b, c = S.gens()
sage: f = R.hom([a, b, c, 0], S)
sage: Kernel(f).gens()
[d]
sage.schemes.chow.ring.Kernel2(f)

Given a morphism \(f:A\rightarrow B\) between (quotients of) multivariate polynomial rings, the kernel of \(f\) is returned.

EXAMPLE (http://trac.sagemath.org/sage_trac/ticket/9792):

sage: from sage.schemes.chow.ring import Kernel
sage: R = PolynomialRing(QQ, 4, names=['x', 'y', 'z', 'w'])
sage: S = PolynomialRing(QQ, 2, names=['s', 't'])
sage: s, t = S.gens()
sage: f = R.hom([s ** 4, s ** 3 * t, s * t ** 3, t ** 4], S)
sage: Kernel(f).gens()
[y*z - x*w, z^3 - y*w^2, x*z^2 - y^2*w, y^3 - x^2*z]

TESTS (taken from the Singular documentation of alg_kernel and kernel):

sage: from sage.schemes.chow.ring import Kernel
sage: R = PolynomialRing(QQ, 3, names=['a', 'b', 'c'])
sage: S = PolynomialRing(QQ, 6, names=['x', 'y', 'z', 'u', 'v', 'w'])
sage: x, y, z, u, v, w = S.gens()
sage: f = R.hom([x - w, u^2 * w + 1, y * z - v], S)
sage: Kernel(f).gens()
[0]

sage: from sage.schemes.chow.ring import Kernel
sage: R = PolynomialRing(QQ, 3, names=['a', 'b', 'c'])
sage: S = PolynomialRing(QQ, 3, names=['x', 'y', 'z']).quotient(x - y)
sage: x, y, z = S.gens()
sage: f = R.hom([x, y, x^2 - y^3], S)
sage: Kernel(f).gens()
[a - b, b^3 - b^2 + c]

sage: from sage.schemes.chow.ring import Kernel
sage: R = PolynomialRing(QQ, 4, names=['a', 'b', 'c', 'd'])
sage: S = PolynomialRing(QQ, 3, names=['a', 'b', 'c'])
sage: a, b, c = S.gens()
sage: f = R.hom([a, b, c, 0], S)
sage: Kernel(f).gens()
[d]
sage.schemes.chow.ring.ideals_are_equal(I, J, A)

Internal Helper. Fast check that \(I\subset J\) and \(J\subset I\) in A using Singular.

INPUT:

  • I – an ideal in the ring A
  • J – an ideal in the ring A
  • A – a ring
OUTPUT:
True or False depending whether I = J in A or not.
sage.schemes.chow.ring.is_chowRing(R)

Test whether R is a ChowRing.

INPUT:

  • R – anything.

OUTPUT:

Boolean. Whether R derives from ChowRing_generic.