Chains and cochains¶
This module implements formal linear combinations of cells of a given
cell complex (Chains) and their dual (Cochains). It
is closely related to the sage.topology.chain_complex
module. The main differences are that chains and cochains here are of
homogeneous dimension only, and that they reference their cell
complex.
- class sage.homology.chains.CellComplexReference(cell_complex, degree, cells=None)[source]¶
Bases:
objectAuxiliary base class for chains and cochains
INPUT:
cell_complex– The cell complex to referencedegree– integer. The degree of the (co)chainscells– tuple of cells orNone. Does not necessarily have to be the cells in the given degree, for computational purposes this could also be any collection that is in one-to-one correspondence with the cells. IfNone, the cells of the complex in the given degree are used.
EXAMPLES:
sage: X = simplicial_complexes.Simplex(2) sage: from sage.homology.chains import CellComplexReference sage: c = CellComplexReference(X, 1) sage: c.cell_complex() is X True
>>> from sage.all import * >>> X = simplicial_complexes.Simplex(Integer(2)) >>> from sage.homology.chains import CellComplexReference >>> c = CellComplexReference(X, Integer(1)) >>> c.cell_complex() is X True
- cell_complex()[source]¶
Return the underlying cell complex
OUTPUT:
A cell complex.
EXAMPLES:
sage: X = simplicial_complexes.Simplex(2) sage: X.n_chains(1).cell_complex() is X True
>>> from sage.all import * >>> X = simplicial_complexes.Simplex(Integer(2)) >>> X.n_chains(Integer(1)).cell_complex() is X True
- class sage.homology.chains.Chains(cell_complex, degree, cells=None, base_ring=None)[source]¶
Bases:
CellComplexReference,CombinatorialFreeModuleClass for the free module of chains in a given degree.
INPUT:
n_cells– tuple of \(n\)-cells, which thus forms a basis for this modulebase_ring– optional (default \(\ZZ\))
One difference between chains and cochains is notation. In a simplicial complex, for example, a simplex
(0,1,2)is written as “(0,1,2)” in the group of chains but as “\chi_(0,1,2)” in the group of cochains.Also, since the free modules of chains and cochains are dual, there is a pairing \(\langle c, z \rangle\), sending a cochain \(c\) and a chain \(z\) to a scalar.
EXAMPLES:
sage: S2 = simplicial_complexes.Sphere(2) sage: C_2 = S2.n_chains(1) sage: C_2_co = S2.n_chains(1, cochains=True) sage: x = C_2.basis()[Simplex((0,2))] sage: y = C_2.basis()[Simplex((1,3))] sage: z = x+2*y sage: a = C_2_co.basis()[Simplex((1,3))] sage: b = C_2_co.basis()[Simplex((0,3))] sage: c = 3*a-2*b sage: z (0, 2) + 2*(1, 3) sage: c -2*\chi_(0, 3) + 3*\chi_(1, 3) sage: c.eval(z) 6
>>> from sage.all import * >>> S2 = simplicial_complexes.Sphere(Integer(2)) >>> C_2 = S2.n_chains(Integer(1)) >>> C_2_co = S2.n_chains(Integer(1), cochains=True) >>> x = C_2.basis()[Simplex((Integer(0),Integer(2)))] >>> y = C_2.basis()[Simplex((Integer(1),Integer(3)))] >>> z = x+Integer(2)*y >>> a = C_2_co.basis()[Simplex((Integer(1),Integer(3)))] >>> b = C_2_co.basis()[Simplex((Integer(0),Integer(3)))] >>> c = Integer(3)*a-Integer(2)*b >>> z (0, 2) + 2*(1, 3) >>> c -2*\chi_(0, 3) + 3*\chi_(1, 3) >>> c.eval(z) 6
- class Element[source]¶
Bases:
IndexedFreeModuleElement- boundary()[source]¶
Return the boundary of the chain
OUTPUT:
The boundary as a chain in one degree lower.
EXAMPLES:
sage: square = cubical_complexes.Cube(2) sage: C1 = square.n_chains(1, QQ) sage: from sage.topology.cubical_complex import Cube sage: chain = C1(Cube([[1, 1], [0, 1]])) - 2 * C1(Cube([[0, 1], [0, 0]])) sage: chain -2*[0,1] x [0,0] + [1,1] x [0,1] sage: chain.boundary() 2*[0,0] x [0,0] - 3*[1,1] x [0,0] + [1,1] x [1,1]
>>> from sage.all import * >>> square = cubical_complexes.Cube(Integer(2)) >>> C1 = square.n_chains(Integer(1), QQ) >>> from sage.topology.cubical_complex import Cube >>> chain = C1(Cube([[Integer(1), Integer(1)], [Integer(0), Integer(1)]])) - Integer(2) * C1(Cube([[Integer(0), Integer(1)], [Integer(0), Integer(0)]])) >>> chain -2*[0,1] x [0,0] + [1,1] x [0,1] >>> chain.boundary() 2*[0,0] x [0,0] - 3*[1,1] x [0,0] + [1,1] x [1,1]
- is_boundary()[source]¶
Test whether the chain is a boundary
OUTPUT:
Boolean. Whether the chain is the
boundary()of a chain in one degree higher.EXAMPLES:
sage: square = cubical_complexes.Cube(2) sage: C1 = square.n_chains(1, QQ) sage: from sage.topology.cubical_complex import Cube sage: chain = C1(Cube([[1, 1], [0, 1]])) - C1(Cube([[0, 1], [0, 0]])) sage: chain.is_boundary() False
>>> from sage.all import * >>> square = cubical_complexes.Cube(Integer(2)) >>> C1 = square.n_chains(Integer(1), QQ) >>> from sage.topology.cubical_complex import Cube >>> chain = C1(Cube([[Integer(1), Integer(1)], [Integer(0), Integer(1)]])) - C1(Cube([[Integer(0), Integer(1)], [Integer(0), Integer(0)]])) >>> chain.is_boundary() False
- is_cycle()[source]¶
Test whether the chain is a cycle
OUTPUT:
Boolean. Whether the
boundary()vanishes.EXAMPLES:
sage: square = cubical_complexes.Cube(2) sage: C1 = square.n_chains(1, QQ) sage: from sage.topology.cubical_complex import Cube sage: chain = C1(Cube([[1, 1], [0, 1]])) - C1(Cube([[0, 1], [0, 0]])) sage: chain.is_cycle() False
>>> from sage.all import * >>> square = cubical_complexes.Cube(Integer(2)) >>> C1 = square.n_chains(Integer(1), QQ) >>> from sage.topology.cubical_complex import Cube >>> chain = C1(Cube([[Integer(1), Integer(1)], [Integer(0), Integer(1)]])) - C1(Cube([[Integer(0), Integer(1)], [Integer(0), Integer(0)]])) >>> chain.is_cycle() False
- to_complex()[source]¶
Return the corresponding chain complex element
OUTPUT:
An element of the chain complex, see
sage.homology.chain_complex.EXAMPLES:
sage: square = cubical_complexes.Cube(2) sage: C1 = square.n_chains(1, QQ) sage: from sage.topology.cubical_complex import Cube sage: chain = C1(Cube([[1, 1], [0, 1]])) sage: chain.to_complex() Chain(1:(0, 0, 0, 1)) sage: ascii_art(_) d_0 [0] d_1 [0] d_2 d_3 0 <---- [0] <---- [0] <---- [0] <---- 0 [0] [0] [0] [1]
>>> from sage.all import * >>> square = cubical_complexes.Cube(Integer(2)) >>> C1 = square.n_chains(Integer(1), QQ) >>> from sage.topology.cubical_complex import Cube >>> chain = C1(Cube([[Integer(1), Integer(1)], [Integer(0), Integer(1)]])) >>> chain.to_complex() Chain(1:(0, 0, 0, 1)) >>> ascii_art(_) d_0 [0] d_1 [0] d_2 d_3 0 <---- [0] <---- [0] <---- [0] <---- 0 [0] [0] [0] [1]
- chain_complex()[source]¶
Return the chain complex.
OUTPUT:
Chain complex, see
sage.homology.chain_complex.EXAMPLES:
sage: square = cubical_complexes.Cube(2) sage: CC = square.n_chains(2, QQ).chain_complex(); CC Chain complex with at most 3 nonzero terms over Rational Field sage: ascii_art(CC) [-1 -1 0 0] [-1] [ 1 0 -1 0] [ 1] [ 0 1 0 -1] [-1] [ 0 0 1 1] [ 1] 0 <-- C_0 <-------------- C_1 <----- C_2 <-- 0
>>> from sage.all import * >>> square = cubical_complexes.Cube(Integer(2)) >>> CC = square.n_chains(Integer(2), QQ).chain_complex(); CC Chain complex with at most 3 nonzero terms over Rational Field >>> ascii_art(CC) [-1 -1 0 0] [-1] [ 1 0 -1 0] [ 1] [ 0 1 0 -1] [-1] [ 0 0 1 1] [ 1] 0 <-- C_0 <-------------- C_1 <----- C_2 <-- 0
- dual()[source]¶
Return the cochains.
OUTPUT:
The cochains of the same cells with the same base ring.
EXAMPLES:
sage: square = cubical_complexes.Cube(2) sage: chains = square.n_chains(1, ZZ); chains Free module generated by {[0,0] x [0,1], [0,1] x [0,0], [0,1] x [1,1], [1,1] x [0,1]} over Integer Ring sage: chains.dual() Free module generated by {[0,0] x [0,1], [0,1] x [0,0], [0,1] x [1,1], [1,1] x [0,1]} over Integer Ring sage: type(chains) <class 'sage.homology.chains.Chains_with_category'> sage: type(chains.dual()) <class 'sage.homology.chains.Cochains_with_category'>
>>> from sage.all import * >>> square = cubical_complexes.Cube(Integer(2)) >>> chains = square.n_chains(Integer(1), ZZ); chains Free module generated by {[0,0] x [0,1], [0,1] x [0,0], [0,1] x [1,1], [1,1] x [0,1]} over Integer Ring >>> chains.dual() Free module generated by {[0,0] x [0,1], [0,1] x [0,0], [0,1] x [1,1], [1,1] x [0,1]} over Integer Ring >>> type(chains) <class 'sage.homology.chains.Chains_with_category'> >>> type(chains.dual()) <class 'sage.homology.chains.Cochains_with_category'>
- class sage.homology.chains.Cochains(cell_complex, degree, cells=None, base_ring=None)[source]¶
Bases:
CellComplexReference,CombinatorialFreeModuleClass for the free module of cochains in a given degree.
INPUT:
n_cells– tuple of \(n\)-cells, which thus forms a basis for this modulebase_ring– optional (default \(\ZZ\))
One difference between chains and cochains is notation. In a simplicial complex, for example, a simplex
(0,1,2)is written as “(0,1,2)” in the group of chains but as “\chi_(0,1,2)” in the group of cochains.Also, since the free modules of chains and cochains are dual, there is a pairing \(\langle c, z \rangle\), sending a cochain \(c\) and a chain \(z\) to a scalar.
EXAMPLES:
sage: S2 = simplicial_complexes.Sphere(2) sage: C_2 = S2.n_chains(1) sage: C_2_co = S2.n_chains(1, cochains=True) sage: x = C_2.basis()[Simplex((0,2))] sage: y = C_2.basis()[Simplex((1,3))] sage: z = x+2*y sage: a = C_2_co.basis()[Simplex((1,3))] sage: b = C_2_co.basis()[Simplex((0,3))] sage: c = 3*a-2*b sage: z (0, 2) + 2*(1, 3) sage: c -2*\chi_(0, 3) + 3*\chi_(1, 3) sage: c.eval(z) 6
>>> from sage.all import * >>> S2 = simplicial_complexes.Sphere(Integer(2)) >>> C_2 = S2.n_chains(Integer(1)) >>> C_2_co = S2.n_chains(Integer(1), cochains=True) >>> x = C_2.basis()[Simplex((Integer(0),Integer(2)))] >>> y = C_2.basis()[Simplex((Integer(1),Integer(3)))] >>> z = x+Integer(2)*y >>> a = C_2_co.basis()[Simplex((Integer(1),Integer(3)))] >>> b = C_2_co.basis()[Simplex((Integer(0),Integer(3)))] >>> c = Integer(3)*a-Integer(2)*b >>> z (0, 2) + 2*(1, 3) >>> c -2*\chi_(0, 3) + 3*\chi_(1, 3) >>> c.eval(z) 6
- class Element[source]¶
Bases:
IndexedFreeModuleElement- coboundary()[source]¶
Return the coboundary of this cochain
OUTPUT:
The coboundary as a cochain in one degree higher.
EXAMPLES:
sage: square = cubical_complexes.Cube(2) sage: C1 = square.n_chains(1, QQ, cochains=True) sage: from sage.topology.cubical_complex import Cube sage: cochain = C1(Cube([[1, 1], [0, 1]])) - 2 * C1(Cube([[0, 1], [0, 0]])) sage: cochain -2*\chi_[0,1] x [0,0] + \chi_[1,1] x [0,1] sage: cochain.coboundary() -\chi_[0,1] x [0,1]
>>> from sage.all import * >>> square = cubical_complexes.Cube(Integer(2)) >>> C1 = square.n_chains(Integer(1), QQ, cochains=True) >>> from sage.topology.cubical_complex import Cube >>> cochain = C1(Cube([[Integer(1), Integer(1)], [Integer(0), Integer(1)]])) - Integer(2) * C1(Cube([[Integer(0), Integer(1)], [Integer(0), Integer(0)]])) >>> cochain -2*\chi_[0,1] x [0,0] + \chi_[1,1] x [0,1] >>> cochain.coboundary() -\chi_[0,1] x [0,1]
- cup_product(cochain)[source]¶
Return the cup product with another cochain.
INPUT:
cochain– cochain over the same cell complex
EXAMPLES:
sage: T2 = simplicial_complexes.Torus() sage: C1 = T2.n_chains(1, base_ring=ZZ, cochains=True) sage: def l(i, j): ....: return C1(Simplex([i, j])) sage: l1 = l(1, 3) + l(1, 4) + l(1, 6) + l(2, 4) - l(4, 5) + l(5, 6) sage: l2 = l(1, 6) - l(2, 3) - l(2, 5) + l(3, 6) - l(4, 5) + l(5, 6)
>>> from sage.all import * >>> T2 = simplicial_complexes.Torus() >>> C1 = T2.n_chains(Integer(1), base_ring=ZZ, cochains=True) >>> def l(i, j): ... return C1(Simplex([i, j])) >>> l1 = l(Integer(1), Integer(3)) + l(Integer(1), Integer(4)) + l(Integer(1), Integer(6)) + l(Integer(2), Integer(4)) - l(Integer(4), Integer(5)) + l(Integer(5), Integer(6)) >>> l2 = l(Integer(1), Integer(6)) - l(Integer(2), Integer(3)) - l(Integer(2), Integer(5)) + l(Integer(3), Integer(6)) - l(Integer(4), Integer(5)) + l(Integer(5), Integer(6))
The two one-cocycles are cohomology generators:
sage: l1.is_cocycle(), l1.is_coboundary() (True, False) sage: l2.is_cocycle(), l2.is_coboundary() (True, False)
>>> from sage.all import * >>> l1.is_cocycle(), l1.is_coboundary() (True, False) >>> l2.is_cocycle(), l2.is_coboundary() (True, False)
Their cup product is a two-cocycle that is again non-trivial in cohomology:
sage: l12 = l1.cup_product(l2) sage: l12 \chi_(1, 3, 6) - \chi_(2, 4, 5) - \chi_(4, 5, 6) sage: l1.parent().degree(), l2.parent().degree(), l12.parent().degree() (1, 1, 2) sage: l12.is_cocycle(), l12.is_coboundary() (True, False)
>>> from sage.all import * >>> l12 = l1.cup_product(l2) >>> l12 \chi_(1, 3, 6) - \chi_(2, 4, 5) - \chi_(4, 5, 6) >>> l1.parent().degree(), l2.parent().degree(), l12.parent().degree() (1, 1, 2) >>> l12.is_cocycle(), l12.is_coboundary() (True, False)
- eval(other)[source]¶
Evaluate this cochain on the chain
other.INPUT:
other– a chain for the same cell complex in the same dimension with the same base ring
OUTPUT: scalar
EXAMPLES:
sage: S2 = simplicial_complexes.Sphere(2) sage: C_2 = S2.n_chains(1) sage: C_2_co = S2.n_chains(1, cochains=True) sage: x = C_2.basis()[Simplex((0,2))] sage: y = C_2.basis()[Simplex((1,3))] sage: z = x+2*y sage: a = C_2_co.basis()[Simplex((1,3))] sage: b = C_2_co.basis()[Simplex((0,3))] sage: c = 3*a-2*b sage: z (0, 2) + 2*(1, 3) sage: c -2*\chi_(0, 3) + 3*\chi_(1, 3) sage: c.eval(z) 6
>>> from sage.all import * >>> S2 = simplicial_complexes.Sphere(Integer(2)) >>> C_2 = S2.n_chains(Integer(1)) >>> C_2_co = S2.n_chains(Integer(1), cochains=True) >>> x = C_2.basis()[Simplex((Integer(0),Integer(2)))] >>> y = C_2.basis()[Simplex((Integer(1),Integer(3)))] >>> z = x+Integer(2)*y >>> a = C_2_co.basis()[Simplex((Integer(1),Integer(3)))] >>> b = C_2_co.basis()[Simplex((Integer(0),Integer(3)))] >>> c = Integer(3)*a-Integer(2)*b >>> z (0, 2) + 2*(1, 3) >>> c -2*\chi_(0, 3) + 3*\chi_(1, 3) >>> c.eval(z) 6
- is_coboundary()[source]¶
Test whether the cochain is a coboundary
OUTPUT:
Boolean. Whether the cochain is the
coboundary()of a cochain in one degree lower.EXAMPLES:
sage: square = cubical_complexes.Cube(2) sage: C1 = square.n_chains(1, QQ, cochains=True) sage: from sage.topology.cubical_complex import Cube sage: cochain = C1(Cube([[1, 1], [0, 1]])) - C1(Cube([[0, 1], [0, 0]])) sage: cochain.is_coboundary() True
>>> from sage.all import * >>> square = cubical_complexes.Cube(Integer(2)) >>> C1 = square.n_chains(Integer(1), QQ, cochains=True) >>> from sage.topology.cubical_complex import Cube >>> cochain = C1(Cube([[Integer(1), Integer(1)], [Integer(0), Integer(1)]])) - C1(Cube([[Integer(0), Integer(1)], [Integer(0), Integer(0)]])) >>> cochain.is_coboundary() True
- is_cocycle()[source]¶
Test whether the cochain is a cocycle
OUTPUT:
Boolean. Whether the
coboundary()vanishes.EXAMPLES:
sage: square = cubical_complexes.Cube(2) sage: C1 = square.n_chains(1, QQ, cochains=True) sage: from sage.topology.cubical_complex import Cube sage: cochain = C1(Cube([[1, 1], [0, 1]])) - C1(Cube([[0, 1], [0, 0]])) sage: cochain.is_cocycle() True
>>> from sage.all import * >>> square = cubical_complexes.Cube(Integer(2)) >>> C1 = square.n_chains(Integer(1), QQ, cochains=True) >>> from sage.topology.cubical_complex import Cube >>> cochain = C1(Cube([[Integer(1), Integer(1)], [Integer(0), Integer(1)]])) - C1(Cube([[Integer(0), Integer(1)], [Integer(0), Integer(0)]])) >>> cochain.is_cocycle() True
- to_complex()[source]¶
Return the corresponding cochain complex element
OUTPUT:
An element of the cochain complex, see
sage.homology.chain_complex.EXAMPLES:
sage: square = cubical_complexes.Cube(2) sage: C1 = square.n_chains(1, QQ, cochains=True) sage: from sage.topology.cubical_complex import Cube sage: cochain = C1(Cube([[1, 1], [0, 1]])) sage: cochain.to_complex() Chain(1:(0, 0, 0, 1)) sage: ascii_art(_) d_2 d_1 [0] d_0 [0] d_-1 0 <---- [0] <---- [0] <---- [0] <----- 0 [0] [0] [1] [0]
>>> from sage.all import * >>> square = cubical_complexes.Cube(Integer(2)) >>> C1 = square.n_chains(Integer(1), QQ, cochains=True) >>> from sage.topology.cubical_complex import Cube >>> cochain = C1(Cube([[Integer(1), Integer(1)], [Integer(0), Integer(1)]])) >>> cochain.to_complex() Chain(1:(0, 0, 0, 1)) >>> ascii_art(_) d_2 d_1 [0] d_0 [0] d_-1 0 <---- [0] <---- [0] <---- [0] <----- 0 [0] [0] [1] [0]
- cochain_complex()[source]¶
Return the cochain complex.
OUTPUT:
Cochain complex, see
sage.homology.chain_complex.EXAMPLES:
sage: square = cubical_complexes.Cube(2) sage: C2 = square.n_chains(2, QQ, cochains=True) sage: C2.cochain_complex() Chain complex with at most 3 nonzero terms over Rational Field sage: ascii_art(C2.cochain_complex()) [-1 1 0 0] [-1 0 1 0] [ 0 -1 0 1] [-1 1 -1 1] [ 0 0 -1 1] 0 <-- C_2 <-------------- C_1 <-------------- C_0 <-- 0
>>> from sage.all import * >>> square = cubical_complexes.Cube(Integer(2)) >>> C2 = square.n_chains(Integer(2), QQ, cochains=True) >>> C2.cochain_complex() Chain complex with at most 3 nonzero terms over Rational Field >>> ascii_art(C2.cochain_complex()) [-1 1 0 0] [-1 0 1 0] [ 0 -1 0 1] [-1 1 -1 1] [ 0 0 -1 1] 0 <-- C_2 <-------------- C_1 <-------------- C_0 <-- 0
- dual()[source]¶
Return the chains
OUTPUT:
The chains of the same cells with the same base ring.
EXAMPLES:
sage: square = cubical_complexes.Cube(2) sage: cochains = square.n_chains(1, ZZ, cochains=True); cochains Free module generated by {[0,0] x [0,1], [0,1] x [0,0], [0,1] x [1,1], [1,1] x [0,1]} over Integer Ring sage: cochains.dual() Free module generated by {[0,0] x [0,1], [0,1] x [0,0], [0,1] x [1,1], [1,1] x [0,1]} over Integer Ring sage: type(cochains) <class 'sage.homology.chains.Cochains_with_category'> sage: type(cochains.dual()) <class 'sage.homology.chains.Chains_with_category'>
>>> from sage.all import * >>> square = cubical_complexes.Cube(Integer(2)) >>> cochains = square.n_chains(Integer(1), ZZ, cochains=True); cochains Free module generated by {[0,0] x [0,1], [0,1] x [0,0], [0,1] x [1,1], [1,1] x [0,1]} over Integer Ring >>> cochains.dual() Free module generated by {[0,0] x [0,1], [0,1] x [0,0], [0,1] x [1,1], [1,1] x [0,1]} over Integer Ring >>> type(cochains) <class 'sage.homology.chains.Cochains_with_category'> >>> type(cochains.dual()) <class 'sage.homology.chains.Chains_with_category'>