Skip to content

Commit

Permalink
Trac #30838: Generators for homology of simplicial complexes
Browse files Browse the repository at this point in the history
This is a followup to #6100, and was reported on
[https://ask.sagemath.org/question/54070/generators-of-simplicial-
homology/ this ask question]. If `K` is a simplicial complex, the
`generators` argument to `K.homology(...)` does nothing:
`K.homology(...)` calls the `homology` method for generic cell
complexes, which in turn calls `_homology_` for simplicial complexes,
and that method ignores the `generators` keyword.

There is an easy solution: in the generic `homology` method, if
`generators` is True, then don't call `_homology_`, just continue in
that method. This will give information about chain complex generators.
It is possible, although obscure, to extract the simplicial complex
information from that.

A better solution: actually do the obscure work, don't force the users
to figure it out. That is:
- call `K.chain_complex(generators=True)`, and
- use `K._n_cells_sorted()` to convert the chain complex generators
(which are just vectors in the corresponding free module over the base
ring) to actual chains in the simplicial complex.

URL: https://trac.sagemath.org/30838
Reported by: jhpalmieri
Ticket author(s): John Palmieri
Reviewer(s): Travis Scrimshaw
  • Loading branch information
Release Manager committed Nov 7, 2020
2 parents c186f8b + a5b41da commit 2c69b7d
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 15 deletions.
41 changes: 34 additions & 7 deletions src/sage/homology/cell_complex.py
Expand Up @@ -535,12 +535,14 @@ def homology(self, dim=None, base_ring=ZZ, subcomplex=None,
sage: cubical_complexes.KleinBottle().homology(1, base_ring=GF(2))
Vector space of dimension 2 over Finite Field of size 2
If CHomP is installed, Sage can compute generators of homology
groups::
Sage can compute generators of homology groups::
sage: S2 = simplicial_complexes.Sphere(2)
sage: S2.homology(dim=2, generators=True, base_ring=GF(2)) # optional - CHomP
(Vector space of dimension 1 over Finite Field of size 2, [(0, 1, 2) + (0, 1, 3) + (0, 2, 3) + (1, 2, 3)])
sage: S2.homology(dim=2, generators=True, base_ring=GF(2), algorithm='no_chomp')
[(Vector space of dimension 1 over Finite Field of size 2, (0, 1, 2) + (0, 1, 3) + (0, 2, 3) + (1, 2, 3))]
Note: the answer may be formatted differently if the optional
package CHomP is installed.
When generators are computed, Sage returns a pair for each
dimension: the group and the list of generators. For
Expand All @@ -549,8 +551,15 @@ def homology(self, dim=None, base_ring=ZZ, subcomplex=None,
complexes, each generator is a linear combination of cubes::
sage: S2_cub = cubical_complexes.Sphere(2)
sage: S2_cub.homology(dim=2, generators=True) # optional - CHomP
(Z, [-[[0,1] x [0,1] x [0,0]] + [[0,1] x [0,1] x [1,1]] - [[0,0] x [0,1] x [0,1]] - [[0,1] x [1,1] x [0,1]] + [[0,1] x [0,0] x [0,1]] + [[1,1] x [0,1] x [0,1]]])
sage: S2_cub.homology(dim=2, generators=True, algorithm='no_chomp')
[(Z,
[0,0] x [0,1] x [0,1] - [0,1] x [0,0] x [0,1] + [0,1] x [0,1] x [0,0] - [0,1] x [0,1] x [1,1] + [0,1] x [1,1] x [0,1] - [1,1] x [0,1] x [0,1])]
Similarly for simpicial sets::
sage: S = simplicial_sets.Sphere(2)
sage: S.homology(generators=True)
{0: [], 1: 0, 2: [(Z, sigma_2)]}
"""
from sage.interfaces.chomp import have_chomp, homcubes, homsimpl
from sage.homology.cubical_complex import CubicalComplex
Expand Down Expand Up @@ -604,13 +613,31 @@ def homology(self, dim=None, base_ring=ZZ, subcomplex=None,
return self._homology_(dim, subcomplex=subcomplex,
cohomology=cohomology, base_ring=base_ring,
verbose=verbose, algorithm=algorithm,
reduced=reduced, **kwds)
reduced=reduced, generators=generators,
**kwds)

C = self.chain_complex(cochain=cohomology, augmented=reduced,
dimensions=dims, subcomplex=subcomplex,
base_ring=base_ring, verbose=verbose)
answer = C.homology(base_ring=base_ring, generators=generators,
verbose=verbose, algorithm=algorithm)

if generators:
# Try to convert chain complex information to topological
# chain information.
for i in answer:
H_with_gens = answer[i]
if H_with_gens:
chains = self.n_chains(i, base_ring=base_ring)
new_H = []
for (H, gen) in H_with_gens:
v = gen.vector(i)
new_gen = chains.zero()
for (coeff, chain) in zip(v, chains.gens()):
new_gen += coeff * chain
new_H.append((H, new_gen))
answer[i] = new_H

if dim is None:
dim = range(self.dimension() + 1)
zero = HomologyGroup(0, base_ring)
Expand Down
19 changes: 14 additions & 5 deletions src/sage/homology/chain_complex.py
Expand Up @@ -1248,9 +1248,16 @@ def homology(self, deg=None, base_ring=None, generators=False,
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::
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. Each summand of the homology is
listed separately, with a corresponding generator::
sage: C.homology(1, generators=True, algorithm='no_chomp')
[(C3, Chain(1:(1, 0))), (Z, Chain(1:(0, 1)))]
Note that the answer will be formatted differently if the optional
package CHomP is installed. ::
sage: C.homology(1, generators=True) # optional - CHomP
(Z x C3, [(0, 1), (1, 0)])
Expand All @@ -1261,8 +1268,10 @@ def homology(self, deg=None, base_ring=None, generators=False,
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)]), 2: 0}
sage: C_k.homology(generators=true, algorithm='no_chomp')
{0: [(Z, Chain(0:(1)))],
1: [(C2, Chain(1:(1, 0, 0))), (Z, Chain(1:(0, 0, 1)))],
2: []}
From a torus using a field::
Expand Down
52 changes: 49 additions & 3 deletions src/sage/homology/simplicial_complex.py
Expand Up @@ -2181,7 +2181,7 @@ def chain_complex(self, subcomplex=None, augmented=False,

def _homology_(self, dim=None, base_ring=ZZ, subcomplex=None,
cohomology=False, enlarge=True, algorithm='pari',
verbose=False, reduced=True):
verbose=False, reduced=True, generators=False):
"""
The (reduced) homology of this simplicial complex.
Expand Down Expand Up @@ -2235,7 +2235,17 @@ def _homology_(self, dim=None, base_ring=ZZ, subcomplex=None,
:type reduced: boolean; optional, default ``True``
Algorithm: if ``subcomplex`` is ``None``, replace it with a
:param generators: If ``True``, return the homology groups and
also generators for them.
:type reduced: boolean; optional, default ``False``
Algorithm: if ``generators`` is ``True``, directly compute the
chain complex, compute its homology along with its generators,
and then convert the chain complex generators to chains in the
simplicial complex.
Otherwise: if ``subcomplex`` is ``None``, replace it with a
facet -- a contractible subcomplex of the original complex.
Then as long as ``enlarge`` is ``True``, no matter what
``subcomplex`` is, replace it with a subcomplex `L` which is
Expand All @@ -2262,6 +2272,15 @@ def _homology_(self, dim=None, base_ring=ZZ, subcomplex=None,
1: Vector space of dimension 0 over Finite Field of size 2,
2: Vector space of dimension 1 over Finite Field of size 2}
We need an immutable complex to compute homology generators::
sage: sphere.set_immutable()
sage: sphere._homology_(generators=True, algorithm='no_chomp')
{0: [], 1: [], 2: [(Z, (0, 1, 2) - (0, 1, 3) + (0, 2, 3) - (1, 2, 3))]}
Note that the answer may be formatted differently if the
optional package CHomP is installed.
Another way to get a two-sphere: take a two-point space and take its
three-fold join with itself::
Expand All @@ -2280,6 +2299,14 @@ def _homology_(self, dim=None, base_ring=ZZ, subcomplex=None,
sage: U = SimplicialComplex([[0,1], [1,2], [0,2]])
sage: T._homology_(subcomplex=U)
{0: 0, 1: 0, 2: Z}
Generators::
sage: simplicial_complexes.Torus().homology(generators=True, algorithm='no_chomp')
{0: [],
1: [(Z, (1, 2) - (1, 6) + (2, 6)), (Z, (3, 4) - (3, 6) + (4, 6))],
2: [(Z,
(0, 1, 2) - (0, 1, 5) + (0, 2, 6) - (0, 3, 4) + (0, 3, 5) - (0, 4, 6) - (1, 2, 4) + (1, 3, 4) - (1, 3, 6) + (1, 5, 6) - (2, 3, 5) + (2, 3, 6) + (2, 4, 5) - (4, 5, 6))]}
"""
from sage.homology.homology_group import HomologyGroup

Expand All @@ -2294,6 +2321,9 @@ def _homology_(self, dim=None, base_ring=ZZ, subcomplex=None,
else:
dims = None

if generators:
enlarge = False

if verbose:
print("starting calculation of the homology of this")
print("%s-dimensional simplicial complex" % self.dimension())
Expand Down Expand Up @@ -2329,7 +2359,23 @@ def _homology_(self, dim=None, base_ring=ZZ, subcomplex=None,
print(" Done computing the chain complex. ")
print("Now computing homology...")
answer = C.homology(base_ring=base_ring, verbose=verbose,
algorithm=algorithm)
algorithm=algorithm, generators=generators)

if generators:
# Convert chain complex information to simplicial complex
# information.
for i in answer:
H_with_gens = answer[i]
if H_with_gens:
chains = self.n_chains(i, base_ring=base_ring)
new_H = []
for (H, gen) in H_with_gens:
v = gen.vector(i)
new_gen = chains.zero()
for (coeff, chain) in zip(v, chains.gens()):
new_gen += coeff * chain
new_H.append((H, new_gen))
answer[i] = new_H

if dim is None:
dim = range(self.dimension() + 1)
Expand Down

0 comments on commit 2c69b7d

Please sign in to comment.