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– (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 higherEXAMPLES:
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()vanishesEXAMPLES:
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_complexEXAMPLES:
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_complexEXAMPLES:
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– (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 lowerEXAMPLES:
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()vanishesEXAMPLES:
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_complexEXAMPLES:
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_complexEXAMPLES:
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'>