Skip to content

Commit

Permalink
Trac #6102: cohomology ring of simplicial complexes
Browse files Browse the repository at this point in the history
Add functionality in sage to compute the cohomology ring of a simplicial
complex.

This relies on #6099, #6100, and #5882.

These will be examples of graded alebras, finite as modules over their
bases, that are graded-commutative.

URL: http://trac.sagemath.org/6102
Reported by: bantieau
Ticket author(s): John Palmieri, Travis Scrimshaw
Reviewer(s): Travis Scrimshaw, John Palmieri
  • Loading branch information
Release Manager authored and vbraun committed Oct 17, 2015
2 parents 32910af + 9bfc2d2 commit 8ebbe9a
Show file tree
Hide file tree
Showing 8 changed files with 2,012 additions and 47 deletions.
2 changes: 2 additions & 0 deletions src/doc/en/reference/homology/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ cell complexes.
sage/homology/cell_complex
sage/homology/koszul_complex
sage/homology/homology_group
sage/homology/homology_vector_space_with_basis
sage/homology/algebraic_topological_model
sage/homology/matrix_utils
sage/interfaces/chomp

Expand Down
596 changes: 596 additions & 0 deletions src/sage/homology/algebraic_topological_model.py

Large diffs are not rendered by default.

399 changes: 396 additions & 3 deletions src/sage/homology/cell_complex.py

Large diffs are not rendered by default.

74 changes: 35 additions & 39 deletions src/sage/homology/chain_homotopy.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
with differential of degree `-1`, a *chain homotopy* `H` between `f` and
`g` is a collection of maps `H_n: C_n \to D_{n+1}` satisfying
.. math::
.. MATH::
\partial_D H + H \partial_C = f - g
\partial_D H + H \partial_C = f - g.
The presence of a chain homotopy defines an equivalence relation
(*chain homotopic*) on chain maps. If `f` and `g` are chain homotopic,
Expand All @@ -33,20 +33,19 @@
REFERENCES:
.. [M-AR] H. Molina-Abril and P. Réal, "Homology computation using spanning
trees" in Progress in Pattern Recognition, Image Analysis,
Computer Vision, and Applications, Lecture Notes in Computer
Science, volume 5856, pp 272-278, Springer, Berlin (2009).
.. [M-AR] H. Molina-Abril and P. Réal, *Homology computation using spanning
trees* in Progress in Pattern Recognition, Image Analysis,
Computer Vision, and Applications, Lecture Notes in Computer
Science, volume 5856, pp 272-278, Springer, Berlin (2009).
.. [PR] P. Pilarczyk and P. Réal, "Computation of cubical homology,
cohomology, and (co)homological operations via chain contraction",
Adv. Comput. Math. 41 (2015), pp 253--275.
.. [PR] P. Pilarczyk and P. Réal, *Computation of cubical homology,
cohomology, and (co)homological operations via chain contraction*,
Adv. Comput. Math. 41 (2015), pp 253--275.
.. [RM-A] P. Réal and H. Molina-Abril, "Cell AT-models for digital
volumes" in Torsello, Escolano, Brun (eds.), Graph-Based
Representations in Pattern Recognition, Lecture Notes in
Computer Science, volume 5534, pp. 314-3232, Springer,
Berlin (2009).
.. [RM-A] P. Réal and H. Molina-Abril, *Cell AT-models for digital
volumes* in Torsello, Escolano, Brun (eds.), Graph-Based
Representations in Pattern Recognition, Lecture Notes in
Computer Science, volume 5534, pp. 314-3232, Springer, Berlin (2009).
"""

########################################################################
Expand All @@ -63,21 +62,22 @@
from sage.homology.chain_complex_morphism import ChainComplexMorphism

class ChainHomotopy(SageObject):
r"""A chain homotopy.
r"""
A chain homotopy.
A chain homotopy `H` between chain maps `f, g: C \to D` is a sequence
of maps `H_n: C_n \to D_{n+1}` (if the chain complexes are graded
homologically) satisfying
.. math::
.. MATH::
\partial_D H + H \partial_C = f - g
\partial_D H + H \partial_C = f - g.
INPUT:
- ``matrices`` -- dictionary of matrices, keyed by dimension
- ``f`` -- chain map `C \to D`.
- ``g`` (optional) -- chain map `C \to D`.
- ``f`` -- chain map `C \to D`
- ``g`` (optional) -- chain map `C \to D`
The dictionary ``matrices`` defines ``H`` by specifying the matrix
defining it in each degree: the entry `m` corresponding to key `i`
Expand All @@ -103,7 +103,8 @@ class ChainHomotopy(SageObject):
sage: g = Hom(C,D)({0: zero_matrix(ZZ, 1), 1: zero_matrix(ZZ, 1)})
sage: H = ChainHomotopy({0: zero_matrix(ZZ, 0, 1), 1: identity_matrix(ZZ, 1)}, f, g)
Note that the maps `f` and `g` are stored in the attributes ``H._f`` and ``H._g``::
Note that the maps `f` and `g` are stored in the attributes ``H._f``
and ``H._g``::
sage: H._f
Chain complex morphism
Expand All @@ -126,15 +127,10 @@ def __init__(self, matrices, f, g=None):
Create a chain homotopy between the given chain maps
from a dictionary of matrices.
INPUT:
- ``matrices`` -- dictionary of matrices, keyed by dimension
- ``f`` -- chain map `C \to D`.
- ``g`` (optional) -- chain map `C \to D`.
EXAMPLES:
If ``g`` is not specified, it is set equal to `f - (H \partial + \partial H)`. ::
If ``g`` is not specified, it is set equal to
`f - (H \partial + \partial H)`. ::
sage: from sage.homology.chain_homotopy import ChainHomotopy
sage: C = ChainComplex({1: matrix(ZZ, 1, 2, (1,0)), 2: matrix(ZZ, 2, 1, (0, 2))}, degree_of_differential=-1)
Expand Down Expand Up @@ -256,8 +252,10 @@ def is_homology_gradient_vector_field(self):
A homology gradient vector field is an algebraic gradient vector
field `H: C \to C` (i.e., a chain homotopy satisfying `H
H = 0`) such that `\partial H \partial = \partial` and `H
\partial H = H`. See Molina-Abril and Réal [M-AR]_ and Réal
and Molina-Abril [RM-A]_ for this and related terminology.
\partial H = H`.
See Molina-Abril and Réal [M-AR]_ and Réal and Molina-Abril
[RM-A]_ for this and related terminology.
See also :meth:`is_algebraic_gradient_vector_field`.
Expand Down Expand Up @@ -287,7 +285,7 @@ def is_homology_gradient_vector_field(self):

def in_degree(self, n):
"""
The matrix representing this chain homotopy in degree ``n``
The matrix representing this chain homotopy in degree ``n``.
INPUT:
Expand All @@ -303,7 +301,8 @@ def in_degree(self, n):
sage: H.in_degree(1)
[3 1]
This returns an appropriately sized zero matrix if the chain homotopy is not defined in degree n::
This returns an appropriately sized zero matrix if the chain
homotopy is not defined in degree n::
sage: H.in_degree(-3)
[]
Expand Down Expand Up @@ -418,6 +417,8 @@ def _repr_(self):

class ChainContraction(ChainHomotopy):
r"""
A chain contraction.
An algebraic gradient vector field `H: C \to C` (that is a chain
homotopy satisfying `H H = 0`) for which there are chain
maps `\pi: C \to D` ("projection") and `\iota: D \to C`
Expand All @@ -442,8 +443,8 @@ class ChainContraction(ChainHomotopy):
sage: C = ChainComplex({0: zero_matrix(ZZ, 1), 1: identity_matrix(ZZ, 1)})
sage: D = ChainComplex({0: matrix(ZZ, 0, 1)})
The chain complex `C` is chain homotopy equivalent to `D`, which is just a
copy of `\ZZ` in degree 0, and we construct a chain contraction::
The chain complex `C` is chain homotopy equivalent to `D`, which is just
a copy of `\ZZ` in degree 0, and we construct a chain contraction::
sage: pi = Hom(C,D)({0: identity_matrix(ZZ, 1)})
sage: iota = Hom(D,C)({0: identity_matrix(ZZ, 1)})
Expand All @@ -453,12 +454,6 @@ def __init__(self, matrices, pi, iota):
r"""
Create a chain contraction from the given data.
INPUTS:
- ``matrices`` -- dictionary of matrices, keyed by dimension
- ``pi`` -- a chain map `C \to D`
- ``iota`` -- a chain map `D \to C`
EXAMPLES::
sage: from sage.homology.chain_homotopy import ChainContraction
Expand Down Expand Up @@ -632,3 +627,4 @@ def dual(self):
deg = self.domain().degree_of_differential()
matrices = {i-deg: matrix_dict[i].transpose() for i in matrix_dict}
return ChainContraction(matrices, self.iota().dual(), self.pi().dual())

68 changes: 65 additions & 3 deletions src/sage/homology/cubical_complex.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
r"""
Finite cubical complexes
Expand Down Expand Up @@ -547,6 +548,67 @@ def _triangulation_(self):
simplices.append(S.join(Simplex((v,)), rename_vertices=False))
return simplices

def alexander_whitney(self, dim):
r"""
Subdivide this cube into pairs of cubes.
This provides a cubical approximation for the diagonal map
`K \to K \times K`.
INPUT:
- ``dim`` -- integer between 0 and one more than the
dimension of this cube
OUTPUT:
- a list containing triples ``(coeff, left, right)``
This uses the algorithm described by Pilarczyk and Réal [PR]_
on p. 267; the formula is originally due to Serre. Calling
this method ``alexander_whitney`` is an abuse of notation,
since the actual Alexander-Whitney map goes from `C(K \times
L) \to C(K) \otimes C(L)`, where `C(-)` denotes the associated
chain complex, but this subdivision of cubes is at the heart
of it.
EXAMPLES::
sage: from sage.homology.cubical_complex import Cube
sage: C1 = Cube([[0,1], [3,4]])
sage: C1.alexander_whitney(0)
[(1, [0,0] x [3,3], [0,1] x [3,4])]
sage: C1.alexander_whitney(1)
[(1, [0,1] x [3,3], [1,1] x [3,4]), (-1, [0,0] x [3,4], [0,1] x [4,4])]
sage: C1.alexander_whitney(2)
[(1, [0,1] x [3,4], [1,1] x [4,4])]
"""
from sage.sets.set import Set
N = Set(self.nondegenerate_intervals())
result = []
for J in N.subsets(dim):
Jprime = N.difference(J)
nu = 0
for i in J:
for j in Jprime:
if j<i:
nu += 1
t = self.tuple()
left = []
right = []
for j in range(len(t)):
if j in Jprime:
left.append((t[j][0], t[j][0]))
right.append(t[j])
elif j in J:
left.append(t[j])
right.append((t[j][1], t[j][1]))
else:
left.append(t[j])
right.append(t[j])
result.append(((-1)**nu, Cube(left), Cube(right)))
return result

def __cmp__(self, other):
"""
Return True iff this cube is the same as ``other``: that is,
Expand Down Expand Up @@ -1048,7 +1110,7 @@ def chain_complex(self, **kwds):
empty_cell = 1 # number of (-1)-dimensional cubes
else:
empty_cell = 0
vertices = self.n_cubes(0, subcomplex=subcomplex)
vertices = self.n_cells(0, subcomplex=subcomplex)
n = len(vertices)
mat = matrix(base_ring, empty_cell, n, n*empty_cell*[1])
if cochain:
Expand Down Expand Up @@ -1078,7 +1140,7 @@ def chain_complex(self, **kwds):
# dictionary seems to be faster than finding the index
# of an entry in a list.
old = dict(zip(current, range(len(current))))
current = list(self.n_cubes(dim, subcomplex=subcomplex))
current = list(self.n_cells(dim, subcomplex=subcomplex))
# construct matrix. it is easiest to construct it as
# a sparse matrix, specifying which entries are
# nonzero via a dictionary.
Expand Down Expand Up @@ -1263,7 +1325,7 @@ def product(self, other):
sage: RP2 = cubical_complexes.RealProjectivePlane()
sage: S1 = cubical_complexes.Sphere(1)
sage: RP2.product(S1).homology()[1]
sage: RP2.product(S1).homology()[1] # long time: 5 seconds
Z x C2
"""
facets = []
Expand Down
38 changes: 36 additions & 2 deletions src/sage/homology/delta_complex.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"""

from copy import copy
from sage.homology.cell_complex import GenericCellComplex
from sage.homology.cell_complex import GenericCellComplex, Chains
from sage.rings.integer_ring import ZZ
from sage.rings.integer import Integer
from sage.matrix.constructor import matrix
Expand Down Expand Up @@ -1142,7 +1142,7 @@ def connected_sum(self, other):
old_idx += 1
# reindex all simplices to be processed and add them to data
for s in process_now:
data[n].append([renaming[i] for i in s])
data[n].append(tuple([renaming[i] for i in s]))
# set up for next loop, one dimension down
renaming = {}
process_now = process_later
Expand Down Expand Up @@ -1438,6 +1438,40 @@ def barycentric_subdivision(self):
"""
raise NotImplementedError("Barycentric subdivisions are not implemented for Delta complexes.")

def n_chains(self, n, base_ring=None, cochains=False):
r"""
Return the free module of chains in degree ``n`` over ``base_ring``.
INPUTS:
- ``n`` -- integer
- ``base_ring`` -- ring (optional, default `\ZZ`)
- ``cochains`` -- boolean (optional, default ``False``); if
``True``, return cochains instead
Since the list of `n`-cells for a `\Delta`-complex may have
some ambiguity -- for example, the list of edges may look like
``[(0, 0), (0, 0), (0, 0)]`` if each edge starts and ends at
vertex 0 -- we record the indices of the cells along with
their tuples. So the basis of chains in such a case would look
like ``[(0, (0, 0)), (1, (0, 0)), (2, (0, 0))]``.
The only difference between chains and cochains is notation:
the dual cochain to the chain basis element ``b`` is written
as ``\chi_b``.
EXAMPLES::
sage: T = delta_complexes.Torus()
sage: T.n_chains(1, QQ)
Free module generated by {(0, (0, 0)), (1, (0, 0)), (2, (0, 0))} over Rational Field
sage: list(T.n_chains(1, QQ, cochains=False).basis())
[(0, (0, 0)), (1, (0, 0)), (2, (0, 0))]
sage: list(T.n_chains(1, QQ, cochains=True).basis())
[\chi_(0, (0, 0)), \chi_(1, (0, 0)), \chi_(2, (0, 0))]
"""
return Chains(tuple(enumerate(self.n_cells(n))), base_ring, cochains)

# the second barycentric subdivision is a simplicial complex. implement this somehow?
# def simplicial_complex(self):
# X = self.barycentric_subdivision().barycentric_subdivision()
Expand Down
Loading

0 comments on commit 8ebbe9a

Please sign in to comment.