AUTHORS:
This module implements bounded chain complexes of free -modules,
for any commutative ring
(although the interesting things, like
homology, only work if
is the integers or a field).
Fix a ring . A chain complex over
is a collection of
-modules
indexed by the integers, with
-module maps
such that
for
all
. The maps
are called differentials.
One can vary this somewhat: the differentials may decrease degree by
one instead of increasing it: sometimes a chain complex is defined
with for each
. Indeed, the
differentials may change dimension by any fixed integer.
Also, the modules may be indexed over an abelian group other than the
integers, e.g., for some integer
, in which case
the differentials may change the grading by any element of that
grading group. The elements of the grading group are generally called
degrees, so
is the module in degree
and so on.
In this implementation, the ring must be commutative and the
modules
must be free
-modules. As noted above, homology
calculations will only work if the ring
is either
or a
field. The modules may be indexed by any free abelian group. The
differentials may increase degree by 1 or decrease it, or indeed
change it by any fixed amount: this is controlled by the
degree_of_differential parameter used in defining the chain
complex.
Define a chain complex.
INPUT:
The following keyword arguments are supported:
base_ring – a commutative ring (optional), the ring over which the chain complex is defined. If this is not specified, it is determined by the data defining the chain complex.
grading_group – a additive free abelian group (optional, default ZZ), the group over which the chain complex is indexed.
(optional, default 1). The degree of the differential.
degree – alias for degree_of_differential.
check – boolean (optional, default True). If True, check that each consecutive pair of differentials are composable and have composite equal to zero.
OUTPUT:
A chain complex.
Warning
Right now, homology calculations will only work if the base
ring is either or a field, so please take this into account
when defining a chain complex.
Use data to define the chain complex. This may be in any of the following forms.
Note
In fact, the free modules in case 2 and the ranks
in case 3 are ignored: only the matrices are kept, and from
their shapes, the ranks of the modules are determined.
(Indeed, if data is a list or tuple, then any element which
is not a matrix is discarded; thus the list may have any number
of different things in it, and all of the non-matrices will be
ignored.) No error checking is done to make sure, for
instance, that the given modules have the appropriate ranks for
the given matrices. However, as long as check is True, the
code checks to see if the matrices are composable and that each
appropriate composite is zero.
If the base ring is not specified, then the matrices are examined to determine a ring over which they are all naturally defined, and this becomes the base ring for the complex. If no such ring can be found, an error is raised. If the base ring is specified, then the matrices are converted automatically to this ring when defining the chain complex. If some matrix cannot be converted, then an error is raised.
EXAMPLES:
sage: ChainComplex()
Trivial chain complex over Integer Ring
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
sage: C
Chain complex with at most 2 nonzero terms over Integer Ring
sage: m = matrix(ZZ, 2, 2, [0, 1, 0, 0])
sage: D = ChainComplex([m, m], base_ring=GF(2)); D
Chain complex with at most 3 nonzero terms over Finite Field of size 2
sage: D == loads(dumps(D))
True
sage: D.differential(0)==m, m.is_immutable(), D.differential(0).is_immutable()
(True, False, True)
Note that when a chain complex is defined in Sage, new differentials may be created: every nonzero module in the chain complex must have a differential coming from it, even if that differential is zero:
sage: IZ = ChainComplex({0: identity_matrix(ZZ, 1)})
sage: IZ.differential() # the differentials in the chain complex
{0: [1], 1: [], -1: []}
sage: IZ.differential(1).parent()
Full MatrixSpace of 0 by 1 dense matrices over Integer Ring
sage: mat = ChainComplex({0: matrix(ZZ, 3, 4)}).differential(1)
sage: mat.nrows(), mat.ncols()
(0, 3)
Defining the base ring implicitly:
sage: ChainComplex([matrix(QQ, 3, 1), matrix(ZZ, 4, 3)])
Chain complex with at most 3 nonzero terms over Rational Field
sage: ChainComplex([matrix(GF(125, 'a'), 3, 1), matrix(ZZ, 4, 3)])
Chain complex with at most 3 nonzero terms over Finite Field in a of size 5^3
If the matrices are defined over incompatible rings, an error results:
sage: ChainComplex([matrix(GF(125, 'a'), 3, 1), matrix(QQ, 4, 3)])
Traceback (most recent call last):
...
TypeError: unable to find a common ring for all elements
If the base ring is given explicitly but is not compatible with the matrices, an error results:
sage: ChainComplex([matrix(GF(125, 'a'), 3, 1)], base_ring=QQ)
Traceback (most recent call last):
...
TypeError: Unable to coerce 0 (<type
'sage.rings.finite_rings.element_givaro.FiniteField_givaroElement'>) to Rational
Bases: sage.structure.parent.Parent
See ChainComplex() for full documentation.
The differentials are required to be in the following canonical form:
This and more is ensured by the assertions in the constructor. The ChainComplex() factory function must ensure that only valid input is passed.
EXAMPLES:
sage: C = ChainComplex(); C
Trivial chain complex over Integer Ring
sage: D = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
sage: D
Chain complex with at most 2 nonzero terms over Integer Ring
alias of Chain_class
The Betti number the chain complex.
That is, write the homology in this degree as a direct sum of a free module and a torsion module; the Betti number is the rank of the free summand.
INPUT:
OUTPUT:
The Betti number in degree deg – the rank of the free part of the homology module in this degree.
EXAMPLES:
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
sage: C.betti(0)
2
sage: [C.betti(n) for n in range(5)]
[2, 1, 0, 0, 0]
sage: C.betti()
{0: 2, 1: 1}
Return the degree of the differentials of the complex
OUTPUT:
An element of the grading group.
EXAMPLES:
sage: D = ChainComplex({0: matrix(ZZ, 2, 2, [1,0,0,2])})
sage: D.degree_of_differential()
1
The differentials which make up the chain complex.
INPUT:
OUTPUT:
Either a dictionary of all of the differentials or a single differential (i.e., a matrix).
EXAMPLES:
sage: D = ChainComplex({0: matrix(ZZ, 2, 2, [1,0,0,2])})
sage: D.differential()
{0: [1 0]
[0 2], 1: [], -1: []}
sage: D.differential(0)
[1 0]
[0 2]
sage: C = ChainComplex({0: identity_matrix(ZZ, 40)})
sage: C.differential()
{0: 40 x 40 dense matrix over Integer Ring, 1: [],
-1: 40 x 0 dense matrix over Integer Ring}
The dual chain complex to self.
Since all modules in self are free of finite rank, the
dual in dimension is isomorphic to the original chain
complex in dimension
, and the corresponding boundary
matrix is the transpose of the matrix in the original complex.
This converts a chain complex to a cochain complex and vice versa.
EXAMPLES:
sage: C = ChainComplex({2: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
sage: C.degree_of_differential()
1
sage: C.differential(2)
[3 0 0]
[0 0 0]
sage: C.dual().degree_of_differential()
-1
sage: C.dual().differential(3)
[3 0]
[0 0]
[0 0]
Return the free module at fixed degree, or their sum.
INPUT:
OUTPUT:
The free module at the given degree
. If the degree
is not specified, the sum
is returned.
EXAMPLES:
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0]), 1: matrix(ZZ, [[0, 1]])})
sage: C.free_module()
Ambient free module of rank 6 over the principal ideal domain Integer Ring
sage: C.free_module(0)
Ambient free module of rank 3 over the principal ideal domain Integer Ring
sage: C.free_module(1)
Ambient free module of rank 2 over the principal ideal domain Integer Ring
sage: C.free_module(2)
Ambient free module of rank 1 over the principal ideal domain Integer Ring
Return the rank of the free module at the given degree.
INPUT:
OUTPUT:
Integer. The rank of the free module at the given degree
.
EXAMPLES:
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0]), 1: matrix(ZZ, [[0, 1]])})
sage: [C.free_module_rank(i) for i in range(-2, 5)]
[0, 0, 3, 2, 1, 0, 0]
Return the grading group.
OUTPUT:
The discrete abelian group that indexes the individual modules
of the complex. Usually .
EXAMPLES:
sage: G = AdditiveAbelianGroup([0, 3])
sage: C = ChainComplex(grading_group=G, degree=G([1,2]))
sage: C.grading_group()
Additive abelian group isomorphic to Z/3 + Z
sage: C.degree_of_differential()
(2, 1)
The homology of the chain complex.
INPUT:
see below for descriptions
OUTPUT:
If the degree is specified, the homology in degree deg. Otherwise, the homology in every dimension as a dictionary indexed by dimension.
ALGORITHM:
If algorithm is set to 'auto' (the default), then use CHomP if available. CHomP is available at the web page http://chomp.rutgers.edu/. It is also an experimental package for Sage. If algorithm is chomp, always use chomp.
CHomP computes homology, not cohomology, and only works over the integers or finite prime fields. Therefore if any of these conditions fails, or if CHomP is not present, or if algorithm is set to ‘no_chomp’, go to plan B: if self has a _homology method – each simplicial complex has this, for example – then call that. Such a method implements specialized algorithms for the particular type of cell complex.
Otherwise, move on to plan C: compute the chain complex of self and compute its homology groups. To do this: over a field, just compute ranks and nullities, thus obtaining dimensions of the homology groups as vector spaces. Over the integers, compute Smith normal form of the boundary matrices defining the chain complex according to the value of algorithm. If algorithm is 'auto' or 'no_chomp', then for each relatively small matrix, use the standard Sage method, which calls the Pari package. For any large matrix, reduce it using the Dumas, Heckenbach, Saunders, and Welker elimination algorithm [DHSW]: see dhsw_snf() for details.
Finally, algorithm may also be 'pari' or 'dhsw', which forces the named algorithm to be used regardless of the size of the matrices and regardless of whether CHomP is available.
As of this writing, CHomP is by far the fastest option, followed by the 'auto' or 'no_chomp' setting of using the Dumas, Heckenbach, Saunders, and Welker elimination algorithm [DHSW] for large matrices and Pari for small ones.
Warning
This only works if the base ring is the integers or a field. Other values will return an error.
EXAMPLES:
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
sage: C.homology()
{0: Z x Z, 1: Z x C3}
sage: C.homology(deg=1, base_ring = GF(3))
Vector space of dimension 2 over Finite Field of size 3
sage: D = ChainComplex({0: identity_matrix(ZZ, 4), 4: identity_matrix(ZZ, 30)})
sage: D.homology()
{0: 0, 1: 0, 4: 0, 5: 0}
Generators: generators are given as a list of cycles, each of which is an element in the appropriate free module, and hence is represented as a vector:
sage: C.homology(1, generators=True) # optional - CHomP
(Z x C3, [(0, 1), (1, 0)])
Tests for trac ticket #6100, the Klein bottle with generators:
sage: d0 = matrix(ZZ, 0,1)
sage: d1 = matrix(ZZ, 1,3, [[0,0,0]])
sage: d2 = matrix(ZZ, 3,2, [[1,1], [1,-1], [-1,1]])
sage: C_k = ChainComplex({0:d0, 1:d1, 2:d2}, degree=-1)
sage: C_k.homology(generators=true) # optional - CHomP
{0: (Z, [(1)]), 1: (Z x C2, [(0, 0, 1), (0, 1, -1)])}
From a torus using a field:
sage: T = simplicial_complexes.Torus()
sage: C_t = T.chain_complex()
sage: C_t.homology(base_ring=QQ, generators=True)
{0: [(Vector space of dimension 1 over Rational Field, Chain(0:(0, 0, 0, 0, 0, 0, 1)))],
1: [(Vector space of dimension 1 over Rational Field,
Chain(1:(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 0, 0, 0, -1, 0, 1, 0))),
(Vector space of dimension 1 over Rational Field,
Chain(1:(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, -1, -1)))],
2: [(Vector space of dimension 1 over Rational Field,
Chain(2:(1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1)))]}
Return the degrees in which the module is non-trivial.
See also ordered_degrees().
OUTPUT:
The tuple containing all degrees (grading group elements)
such that the module
of the chain is non-trivial.
EXAMPLES:
sage: one = matrix(ZZ, [[1]])
sage: D = ChainComplex({0: one, 2: one, 6:one})
sage: ascii_art(D)
[1] [1] [0] [1]
0 <-- C_7 <---- C_6 <-- 0 ... 0 <-- C_3 <---- C_2 <---- C_1 <---- C_0 <-- 0
sage: D.nonzero_degrees()
(0, 1, 2, 3, 6, 7)
Sort the degrees in the order determined by the differential
INPUT:
OUTPUT:
If start has been specified, the longest tuple of degrees
If start has not been specified, a tuple of such tuples of degrees. One for each sequence of non-zero differentials. They are returned in sort order.
EXAMPLES:
sage: one = matrix(ZZ, [[1]])
sage: D = ChainComplex({0: one, 2: one, 6:one})
sage: ascii_art(D)
[1] [1] [0] [1]
0 <-- C_7 <---- C_6 <-- 0 ... 0 <-- C_3 <---- C_2 <---- C_1 <---- C_0 <-- 0
sage: D.ordered_degrees()
((-1, 0, 1, 2, 3), (5, 6, 7))
sage: D.ordered_degrees(exclude_first=True)
((0, 1, 2, 3), (6, 7))
sage: D.ordered_degrees(6)
(5, 6, 7)
sage: D.ordered_degrees(5, exclude_first=True)
(6, 7)
Return a random element.
EXAMPLES:
sage: D = ChainComplex({0: matrix(ZZ, 2, 2, [1,0,0,2])})
sage: D.random_element() # random output
Chain with 1 nonzero terms over Integer Ring
Return the rank of a differential
INPUT:
OUTPUT:
The rank of the differential , where
is the base ring of the chain complex.
EXAMPLES:
sage: C = ChainComplex({0:matrix(ZZ, [[2]])})
sage: C.differential(0)
[2]
sage: C.rank(0)
1
sage: C.rank(0, ring=GF(2))
0
Look for torsion in this chain complex by computing its mod
homology for a range of primes
.
INPUT:
Return a list of pairs where
is a prime at which
there is torsion and
is a list of dimensions in which this
torsion occurs.
The base ring for the chain complex must be the integers; if not, an error is raised.
ALGORITHM:
let denote the chain complex. Let
equal
max_prime. Compute the mod
homology of
, and use
this as the base-line computation: the assumption is that this
is isomorphic to the integral homology tensored with
. Then compute the mod
homology for a range of
primes
, and record whenever the answer differs from the
base-line answer.
EXAMPLES:
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
sage: C.homology()
{0: Z x Z, 1: Z x C3}
sage: C.torsion_list(11)
[(3, [1])]
sage: C = ChainComplex([matrix(ZZ, 1, 1, [2]), matrix(ZZ, 1, 1), matrix(1, 1, [3])])
sage: C.homology(1)
C2
sage: C.homology(3)
C3
sage: C.torsion_list(5)
[(2, [1]), (3, [3])]
Bases: sage.structure.element.ModuleElement
A Chain in a Chain Complex
A chain is collection of module elements for each module
of the chain complex
. There is no restriction on
how the differentials
act on the elements of the chain.
Note
You must use the chain complex to construct chains.
EXAMPLES:
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}, base_ring=GF(7))
sage: C.category()
Category of chain complexes over Finite Field of size 7
TESTS:
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
sage: c = C({0:vector([0, 1, 2]), 1:vector([3, 4])})
sage: TestSuite(c).run()
Return whether the chain is a boundary.
OUTPUT:
Boolean. Whether the elements of the chain are in the image of the differentials.
EXAMPLES:
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
sage: c = C({0:vector([0, 1, 2]), 1:vector([3, 4])})
sage: c.is_boundary()
False
sage: z3 = C({1:(1, 0)})
sage: z3.is_cycle()
True
sage: (2*z3).is_boundary()
False
sage: (3*z3).is_boundary()
True
Return whether the chain is a cycle.
OUTPUT:
Boolean. Whether the elements of the chain are in the kernel of the differentials.
EXAMPLES:
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
sage: c = C({0:vector([0, 1, 2]), 1:vector([3, 4])})
sage: c.is_cycle()
True
Return the free module element in degree.
EXAMPLES:
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
sage: c = C({0:vector([1, 2, 3]), 1:vector([4, 5])})
sage: c.vector(0)
(1, 2, 3)
sage: c.vector(1)
(4, 5)
sage: c.vector(2)
()