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:
object
Auxiliary 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
,CombinatorialFreeModule
Class 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_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
,CombinatorialFreeModule
Class 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_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'>