def integral_homology_basis(self, dimension=1): C = self.chain_complex() from sage.interfaces.chomp import have_chomp if have_chomp(): if C.betti(1) != 0: ans = C.homology(generators=True)[1][1] else: ans = [] else: homology = C.homology(generators=True, algorithm='no_chomp')[dimension] ans = [factor[1].vector(dimension) for factor in homology] if dimension == 1: assert len(ans) == 2 - self.euler() ans = [OneCycle(self, a) for a in ans] return ans
def homology(self, dim=None, base_ring=ZZ, subcomplex=None, generators=False, cohomology=False, algorithm='pari', verbose=False, reduced=True, **kwds): r""" The (reduced) homology of this cell complex. :param dim: If None, then return the homology in every dimension. If ``dim`` is an integer or list, return the homology in the given dimensions. (Actually, if ``dim`` is a list, return the homology in the range from ``min(dim)`` to ``max(dim)``.) :type dim: integer or list of integers or None; optional, default None :param base_ring: commutative ring, must be ZZ or a field. :type base_ring: optional, default ZZ :param subcomplex: a subcomplex of this simplicial complex. Compute homology relative to this subcomplex. :type subcomplex: optional, default empty :param generators: If ``True``, return generators for the homology groups along with the groups. NOTE: Since :trac:`6100`, the result may not be what you expect when not using CHomP since its return is in terms of the chain complex. :type generators: boolean; optional, default False :param cohomology: If True, compute cohomology rather than homology. :type cohomology: boolean; optional, default False :param algorithm: The options are 'auto', 'dhsw', 'pari' or 'no_chomp'. See below for a description of what they mean. :type algorithm: string; optional, default 'pari' :param verbose: If True, print some messages as the homology is computed. :type verbose: boolean; optional, default False :param reduced: If ``True``, return the reduced homology. :type reduced: boolean; optional, default ``True`` ALGORITHM: If ``algorithm`` is set to 'auto', then use CHomP if available. (CHomP is available at the web page http://chomp.rutgers.edu/. It is also an optional package for Sage.) CHomP computes homology, not cohomology, and only works over the integers or finite prime fields. Therefore if any of these conditions fails, or if CHomP is not present, or if ``algorithm`` is set to 'no_chomp', go to plan B: if this complex has a ``_homology`` method -- each simplicial complex has this, for example -- then call that. Such a method implements specialized algorithms for the particular type of cell complex. Otherwise, move on to plan C: compute the chain complex of this complex and compute its homology groups. To do this: over a field, just compute ranks and nullities, thus obtaining dimensions of the homology groups as vector spaces. Over the integers, compute Smith normal form of the boundary matrices defining the chain complex according to the value of ``algorithm``. If ``algorithm`` is 'auto' or 'no_chomp', then for each relatively small matrix, use the standard Sage method, which calls the Pari package. For any large matrix, reduce it using the Dumas, Heckenbach, Saunders, and Welker elimination algorithm: see :func:`sage.homology.matrix_utils.dhsw_snf` for details. Finally, ``algorithm`` may also be 'pari' or 'dhsw', which forces the named algorithm to be used regardless of the size of the matrices and regardless of whether CHomP is available. As of this writing, ``'pari'`` is the fastest standard option. The optional CHomP package may be better still. EXAMPLES:: sage: P = delta_complexes.RealProjectivePlane() sage: P.homology() {0: 0, 1: C2, 2: 0} sage: P.homology(reduced=False) {0: Z, 1: C2, 2: 0} sage: P.homology(base_ring=GF(2)) {0: Vector space of dimension 0 over Finite Field of size 2, 1: Vector space of dimension 1 over Finite Field of size 2, 2: Vector space of dimension 1 over Finite Field of size 2} sage: S7 = delta_complexes.Sphere(7) sage: S7.homology(7) Z 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: 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)]) When generators are computed, Sage returns a pair for each dimension: the group and the list of generators. For simplicial complexes, each generator is represented as a linear combination of simplices, as above, and for cubical 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]]]) """ from sage.interfaces.chomp import have_chomp, homcubes, homsimpl from sage.homology.cubical_complex import CubicalComplex from sage.homology.simplicial_complex import SimplicialComplex from sage.modules.all import VectorSpace from sage.homology.homology_group import HomologyGroup if dim is not None: if isinstance(dim, (list, tuple, range)): low = min(dim) - 1 high = max(dim) + 2 else: low = dim - 1 high = dim + 2 dims = range(low, high) else: dims = None # try to use CHomP if computing homology (not cohomology) and # working over Z or F_p, p a prime. if (algorithm == 'auto' and cohomology is False and (base_ring == ZZ or (base_ring.is_prime_field() and base_ring != QQ))): # homcubes, homsimpl seems fastest if all of homology is computed. H = None if isinstance(self, CubicalComplex): if have_chomp('homcubes'): H = homcubes(self, subcomplex, base_ring=base_ring, verbose=verbose, generators=generators) elif isinstance(self, SimplicialComplex): if have_chomp('homsimpl'): H = homsimpl(self, subcomplex, base_ring=base_ring, verbose=verbose, generators=generators) # now pick off the requested dimensions if H: answer = {} if not dims: dims = range(self.dimension() + 1) for d in dims: answer[d] = H.get(d, HomologyGroup(0, base_ring)) if dim is not None: if not isinstance(dim, (list, tuple, range)): answer = answer.get(dim, HomologyGroup(0, base_ring)) return answer # Derived classes can implement specialized algorithms using a # _homology_ method. See SimplicialComplex for one example. # Those may allow for other arguments, so we pass **kwds. if hasattr(self, '_homology_'): return self._homology_(dim, subcomplex=subcomplex, cohomology=cohomology, base_ring=base_ring, verbose=verbose, algorithm=algorithm, reduced=reduced, **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 dim is None: dim = range(self.dimension() + 1) zero = HomologyGroup(0, base_ring) if isinstance(dim, (list, tuple, range)): return dict([d, answer.get(d, zero)] for d in dim) return answer.get(dim, zero)
def homology(self, dim=None, **kwds): r""" The reduced homology of this cell complex. :param dim: If None, then return the homology in every dimension. If ``dim`` is an integer or list, return the homology in the given dimensions. (Actually, if ``dim`` is a list, return the homology in the range from ``min(dim)`` to ``max(dim)``.) :type dim: integer or list of integers or None; optional, default None :param base_ring: commutative ring, must be ZZ or a field. :type base_ring: optional, default ZZ :param subcomplex: a subcomplex of this simplicial complex. Compute homology relative to this subcomplex. :type subcomplex: optional, default empty :param generators: If True, return generators for the homology groups along with the groups. NOTE: this is only implemented if the CHomP package is available. :type generators: boolean; optional, default False :param cohomology: If True, compute cohomology rather than homology. :type cohomology: boolean; optional, default False :param algorithm: The options are 'auto', 'dhsw', 'pari' or 'no_chomp'. See below for a description of what they mean. :type algorithm: string; optional, default 'auto' :param verbose: If True, print some messages as the homology is computed. :type verbose: boolean; optional, default False .. note:: The keyword arguments to this function get passed on to :meth:``chain_complex`` and its homology. ALGORITHM: If ``algorithm`` is set to 'auto' (the default), then use CHomP if available. (CHomP is available at the web page http://chomp.rutgers.edu/. It is also an experimental package for Sage.) CHomP computes homology, not cohomology, and only works over the integers or finite prime fields. Therefore if any of these conditions fails, or if CHomP is not present, or if ``algorithm`` is set to 'no_chomp', go to plan B: if ``self`` has a ``_homology`` method -- each simplicial complex has this, for example -- then call that. Such a method implements specialized algorithms for the particular type of cell complex. Otherwise, move on to plan C: compute the chain complex of ``self`` and compute its homology groups. To do this: over a field, just compute ranks and nullities, thus obtaining dimensions of the homology groups as vector spaces. Over the integers, compute Smith normal form of the boundary matrices defining the chain complex according to the value of ``algorithm``. If ``algorithm`` is 'auto' or 'no_chomp', then for each relatively small matrix, use the standard Sage method, which calls the Pari package. For any large matrix, reduce it using the Dumas, Heckenbach, Saunders, and Welker elimination algorithm: see :func:`sage.homology.chain_complex.dhsw_snf` for details. Finally, ``algorithm`` may also be 'pari' or 'dhsw', which forces the named algorithm to be used regardless of the size of the matrices and regardless of whether CHomP is available. As of this writing, CHomP is by far the fastest option, followed by the 'auto' or 'no_chomp' setting of using the Dumas, Heckenbach, Saunders, and Welker elimination algorithm for large matrices and Pari for small ones. EXAMPLES:: sage: P = delta_complexes.RealProjectivePlane() sage: P.homology() {0: 0, 1: C2, 2: 0} sage: P.homology(base_ring=GF(2)) {0: Vector space of dimension 0 over Finite Field of size 2, 1: Vector space of dimension 1 over Finite Field of size 2, 2: Vector space of dimension 1 over Finite Field of size 2} sage: S7 = delta_complexes.Sphere(7) sage: S7.homology(7) Z 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: 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)]) When generators are computed, Sage returns a pair for each dimension: the group and the list of generators. For simplicial complexes, each generator is represented as a linear combination of simplices, as above, and for cubical 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]]]) """ from sage.interfaces.chomp import have_chomp, homcubes, homsimpl from sage.homology.cubical_complex import CubicalComplex from sage.homology.simplicial_complex import SimplicialComplex from sage.modules.all import VectorSpace from sage.homology.chain_complex import HomologyGroup base_ring = kwds.get('base_ring', ZZ) cohomology = kwds.get('cohomology', False) subcomplex = kwds.get('subcomplex', None) verbose = kwds.get('verbose', False) algorithm = kwds.get('algorithm', 'auto') if dim is not None: if isinstance(dim, (list, tuple)): low = min(dim) - 1 high = max(dim) + 2 else: low = dim - 1 high = dim + 2 dims = range(low, high) else: dims = None # try to use CHomP if computing homology (not cohomology) and # working over Z or F_p, p a prime. if (algorithm == 'auto' and cohomology is False and (base_ring == ZZ or (base_ring.is_prime_field() and base_ring != QQ))): # homcubes, homsimpl seems fastest if all of homology is computed. H = None if isinstance(self, CubicalComplex): if have_chomp('homcubes'): if 'subcomplex' in kwds: del kwds['subcomplex'] H = homcubes(self, subcomplex, **kwds) elif isinstance(self, SimplicialComplex): if have_chomp('homsimpl'): if 'subcomplex' in kwds: del kwds['subcomplex'] H = homsimpl(self, subcomplex, **kwds) # now pick off the requested dimensions if H: answer = {} if not dims: for d in range(self.dimension() + 1): if base_ring == ZZ: answer[d] = H.get(d, HomologyGroup(0)) else: answer[d] = H.get(d, VectorSpace(base_ring, 0)) else: for d in dims: if base_ring == ZZ: answer[d] = H.get(d, HomologyGroup(0)) else: answer[d] = H.get(d, VectorSpace(base_ring, 0)) if dim is not None: if not isinstance(dim, (list, tuple)): if base_ring == ZZ: answer = answer.get(dim, HomologyGroup(0)) else: answer = answer.get(dim, VectorSpace(base_ring, 0)) return answer # Derived classes can implement specialized algorithms using a # _homology_ method. See SimplicialComplex for one example. if hasattr(self, '_homology_'): return self._homology_(dim, **kwds) C = self.chain_complex(cochain=cohomology, augmented=True, dimensions=dims, **kwds) if 'subcomplex' in kwds: del kwds['subcomplex'] answer = C.homology(**kwds) if isinstance(answer, dict): if cohomology: too_big = self.dimension() + 1 if (not ((isinstance(dim, (list, tuple)) and too_big in dim) or too_big == dim) and too_big in answer): del answer[too_big] if -2 in answer: del answer[-2] if -1 in answer: del answer[-1] for d in range(self.dimension() + 1): if d not in answer: if base_ring == ZZ: answer[d] = HomologyGroup(0) else: answer[d] = VectorSpace(base_ring, 0) if dim is not None: if isinstance(dim, (list, tuple)): temp = {} for n in dim: temp[n] = answer[n] answer = temp else: # just a single dimension if base_ring == ZZ: answer = answer.get(dim, HomologyGroup(0)) else: answer = answer.get(dim, VectorSpace(base_ring, 0)) return answer
#!/usr/bin/env python2 from sage.all import * import cubical_complex import itertools import logging import collections # Ensure we've got chomp from sage.interfaces.chomp import have_chomp assert have_chomp() is True # Logging configuration: by default, produce no output logger = logging.getLogger(__name__) logger.addHandler(logging.NullHandler()) def lookup(n): """\ Lookup returns a list of points in R^3, it amounts to a labeling of the vertices of the Y graph. Examples, from left to right: ``lookup(1)``, ``lookup(2)``, and ``lookup(3)``: | 2 4 0__ | | / 1__0 3 / | 3 2__1__0 / 5 /