Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
r""" Morphisms of simplicial complexes
AUTHORS:
- Benjamin Antieau <d.ben.antieau@gmail.com> (2009.06)
- Travis Scrimshaw (2012-08-18): Made all simplicial complexes immutable to work with the homset cache.
This module implements morphisms of simplicial complexes. The input is given by a dictionary on the vertex set of a simplicial complex. The initialization checks that faces are sent to faces.
There is also the capability to create the fiber product of two morphisms with the same codomain.
EXAMPLES::
sage: S = SimplicialComplex([[0,2],[1,5],[3,4]], is_mutable=False) sage: H = Hom(S,S.product(S, is_mutable=False)) sage: H.diagonal_morphism() Simplicial complex morphism: From: Simplicial complex with vertex set (0, 1, 2, 3, 4, 5) and facets {(3, 4), (1, 5), (0, 2)} To: Simplicial complex with 36 vertices and 18 facets Defn: [0, 1, 2, 3, 4, 5] --> ['L0R0', 'L1R1', 'L2R2', 'L3R3', 'L4R4', 'L5R5']
sage: S = SimplicialComplex([[0,2],[1,5],[3,4]], is_mutable=False) sage: T = SimplicialComplex([[0,2],[1,3]], is_mutable=False) sage: f = {0:0,1:1,2:2,3:1,4:3,5:3} sage: H = Hom(S,T) sage: x = H(f) sage: x.image() Simplicial complex with vertex set (0, 1, 2, 3) and facets {(1, 3), (0, 2)} sage: x.is_surjective() True sage: x.is_injective() False sage: x.is_identity() False
sage: S = simplicial_complexes.Sphere(2) sage: H = Hom(S,S) sage: i = H.identity() sage: i.image() Simplicial complex with vertex set (0, 1, 2, 3) and facets {(0, 2, 3), (0, 1, 2), (1, 2, 3), (0, 1, 3)} sage: i.is_surjective() True sage: i.is_injective() True sage: i.is_identity() True
sage: S = simplicial_complexes.Sphere(2) sage: H = Hom(S,S) sage: i = H.identity() sage: j = i.fiber_product(i) sage: j Simplicial complex morphism: From: Simplicial complex with 4 vertices and 4 facets To: Minimal triangulation of the 2-sphere Defn: L1R1 |--> 1 L3R3 |--> 3 L2R2 |--> 2 L0R0 |--> 0 sage: S = simplicial_complexes.Sphere(2) sage: T = S.product(SimplicialComplex([[0,1]]), rename_vertices = False, is_mutable=False) sage: H = Hom(T,S) sage: T Simplicial complex with 8 vertices and 12 facets sage: T.vertices() ((0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1), (3, 0), (3, 1)) sage: f = {(0, 0): 0, (0, 1): 0, (1, 0): 1, (1, 1): 1, (2, 0): 2, (2, 1): 2, (3, 0): 3, (3, 1): 3} sage: x = H(f) sage: U = simplicial_complexes.Sphere(1) sage: G = Hom(U,S) sage: U Minimal triangulation of the 1-sphere sage: g = {0:0,1:1,2:2} sage: y = G(g) sage: z = y.fiber_product(x) sage: z # this is the mapping path space Simplicial complex morphism: From: Simplicial complex with 6 vertices and 6 facets To: Minimal triangulation of the 2-sphere Defn: ['L2R(2, 0)', 'L2R(2, 1)', 'L0R(0, 0)', 'L0R(0, 1)', 'L1R(1, 0)', 'L1R(1, 1)'] --> [2, 2, 0, 0, 1, 1] """
#***************************************************************************** # Copyright (C) 2009 D. Benjamin Antieau <d.ben.antieau@gmail.com> # # Distributed under the terms of the GNU General Public License (GPL) # # This code is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # See the GNU General Public License for more details; the full text # is available at: # # http://www.gnu.org/licenses/ # #***************************************************************************** from __future__ import print_function from __future__ import absolute_import
from sage.homology.simplicial_complex import Simplex, SimplicialComplex from sage.matrix.constructor import matrix, zero_matrix from sage.rings.integer_ring import ZZ from sage.homology.chain_complex_morphism import ChainComplexMorphism from sage.combinat.permutation import Permutation from sage.algebras.steenrod.steenrod_algebra_misc import convert_perm from sage.categories.morphism import Morphism from sage.categories.homset import Hom from sage.categories.simplicial_complexes import SimplicialComplexes
def is_SimplicialComplexMorphism(x): """ Returns ``True`` if and only if ``x`` is a morphism of simplicial complexes.
EXAMPLES::
sage: from sage.homology.simplicial_complex_morphism import is_SimplicialComplexMorphism sage: S = SimplicialComplex([[0,1],[3,4]], is_mutable=False) sage: H = Hom(S,S) sage: f = {0:0,1:1,3:3,4:4} sage: x = H(f) sage: is_SimplicialComplexMorphism(x) True
"""
class SimplicialComplexMorphism(Morphism): """ An element of this class is a morphism of simplicial complexes. """ def __init__(self,f,X,Y): """ Input is a dictionary ``f``, the domain ``X``, and the codomain ``Y``.
One can define the dictionary on the vertices of `X`.
EXAMPLES::
sage: S = SimplicialComplex([[0,1],[2],[3,4],[5]], is_mutable=False) sage: H = Hom(S,S) sage: f = {0:0,1:1,2:2,3:3,4:4,5:5} sage: g = {0:0,1:1,2:0,3:3,4:4,5:0} sage: x = H(f) sage: y = H(g) sage: x == y False sage: x.image() Simplicial complex with vertex set (0, 1, 2, 3, 4, 5) and facets {(3, 4), (5,), (2,), (0, 1)} sage: y.image() Simplicial complex with vertex set (0, 1, 3, 4) and facets {(3, 4), (0, 1)} sage: x.image() == y.image() False """ raise ValueError("X and Y must be SimplicialComplexes") raise ValueError("f must be a dictionary from the vertex set of X to single values in the vertex set of Y") raise ValueError("f must be a dictionary from the vertices of X to the vertices of Y")
def __eq__(self,x): """ Returns ``True`` if and only if ``self == x``.
EXAMPLES::
sage: S = simplicial_complexes.Sphere(2) sage: H = Hom(S,S) sage: i = H.identity() sage: i Simplicial complex endomorphism of Minimal triangulation of the 2-sphere Defn: 0 |--> 0 1 |--> 1 2 |--> 2 3 |--> 3 sage: f = {0:0,1:1,2:2,3:2} sage: j = H(f) sage: i==j False
sage: T = SimplicialComplex([[1,2]], is_mutable=False) sage: T Simplicial complex with vertex set (1, 2) and facets {(1, 2)} sage: G = Hom(T,T) sage: k = G.identity() sage: g = {1:1,2:2} sage: l = G(g) sage: k == l True """ else:
def __call__(self,x,orientation=False): """ Input is a simplex of the domain. Output is the image simplex.
If the optional argument ``orientation`` is ``True``, then this returns a pair ``(image simplex, oriented)`` where ``oriented`` is 1 or `-1` depending on whether the map preserves or reverses the orientation of the image simplex.
EXAMPLES::
sage: S = simplicial_complexes.Sphere(2) sage: T = simplicial_complexes.Sphere(3) sage: S Minimal triangulation of the 2-sphere sage: T Minimal triangulation of the 3-sphere sage: f = {0:0,1:1,2:2,3:3} sage: H = Hom(S,T) sage: x = H(f) sage: from sage.homology.simplicial_complex import Simplex sage: x(Simplex([0,2,3])) (0, 2, 3)
An orientation-reversing example::
sage: X = SimplicialComplex([[0,1]], is_mutable=False) sage: g = Hom(X,X)({0:1, 1:0}) sage: g(Simplex([0,1])) (0, 1) sage: g(Simplex([0,1]), orientation=True) ((0, 1), -1) """ raise ValueError("x must be a simplex of the source of f") else: else:
def _repr_type(self): """ EXAMPLES::
sage: S = simplicial_complexes.Sphere(1) sage: T = simplicial_complexes.Sphere(2) sage: H = Hom(S,T) sage: f = {0:0,1:1,2:2} sage: H(f)._repr_type() 'Simplicial complex' """
def _repr_defn(self): """ If there are fewer than 5 vertices, print the image of each vertex on a separate line. Otherwise, print the map as a single line.
EXAMPLES::
sage: S = simplicial_complexes.Simplex(1) sage: print(Hom(S,S).identity()._repr_defn()) 0 |--> 0 1 |--> 1 sage: T = simplicial_complexes.Torus() sage: print(Hom(T,T).identity()._repr_defn()) [0, 1, 2, 3, 4, 5, 6] --> [0, 1, 2, 3, 4, 5, 6] """
def associated_chain_complex_morphism(self,base_ring=ZZ,augmented=False,cochain=False): """ Returns the associated chain complex morphism of ``self``.
EXAMPLES::
sage: S = simplicial_complexes.Sphere(1) sage: T = simplicial_complexes.Sphere(2) sage: H = Hom(S,T) sage: f = {0:0,1:1,2:2} sage: x = H(f) sage: x Simplicial complex morphism: From: Minimal triangulation of the 1-sphere To: Minimal triangulation of the 2-sphere Defn: 0 |--> 0 1 |--> 1 2 |--> 2 sage: a = x.associated_chain_complex_morphism() sage: a Chain complex morphism: From: Chain complex with at most 2 nonzero terms over Integer Ring To: Chain complex with at most 3 nonzero terms over Integer Ring sage: a._matrix_dictionary {0: [1 0 0] [0 1 0] [0 0 1] [0 0 0], 1: [1 0 0] [0 1 0] [0 0 0] [0 0 1] [0 0 0] [0 0 0], 2: []} sage: x.associated_chain_complex_morphism(augmented=True) Chain complex morphism: From: Chain complex with at most 3 nonzero terms over Integer Ring To: Chain complex with at most 4 nonzero terms over Integer Ring sage: x.associated_chain_complex_morphism(cochain=True) Chain complex morphism: From: Chain complex with at most 3 nonzero terms over Integer Ring To: Chain complex with at most 2 nonzero terms over Integer Ring sage: x.associated_chain_complex_morphism(augmented=True,cochain=True) Chain complex morphism: From: Chain complex with at most 4 nonzero terms over Integer Ring To: Chain complex with at most 3 nonzero terms over Integer Ring sage: x.associated_chain_complex_morphism(base_ring=GF(11)) Chain complex morphism: From: Chain complex with at most 2 nonzero terms over Finite Field of size 11 To: Chain complex with at most 3 nonzero terms over Finite Field of size 11
Some simplicial maps which reverse the orientation of a few simplices::
sage: g = {0:1, 1:2, 2:0} sage: H(g).associated_chain_complex_morphism()._matrix_dictionary {0: [0 0 1] [1 0 0] [0 1 0] [0 0 0], 1: [ 0 -1 0] [ 0 0 -1] [ 0 0 0] [ 1 0 0] [ 0 0 0] [ 0 0 0], 2: []} sage: X = SimplicialComplex([[0, 1]], is_mutable=False) sage: Hom(X,X)({0:1, 1:0}).associated_chain_complex_morphism()._matrix_dictionary {0: [0 1] [1 0], 1: [-1]} """ else: else: else: except KeyError: l1 = 0 except KeyError: l2 = 0 else: self.domain().chain_complex(base_ring=base_ring,augmented=augmented,cochain=cochain),\ self.codomain().chain_complex(base_ring=base_ring,augmented=augmented,cochain=cochain)) else: self.codomain().chain_complex(base_ring=base_ring,augmented=augmented,cochain=cochain),\ self.domain().chain_complex(base_ring=base_ring,augmented=augmented,cochain=cochain))
def image(self): """ Computes the image simplicial complex of `f`.
EXAMPLES::
sage: S = SimplicialComplex([[0,1],[2,3]], is_mutable=False) sage: T = SimplicialComplex([[0,1]], is_mutable=False) sage: f = {0:0,1:1,2:0,3:1} sage: H = Hom(S,T) sage: x = H(f) sage: x.image() Simplicial complex with vertex set (0, 1) and facets {(0, 1)}
sage: S = SimplicialComplex(is_mutable=False) sage: H = Hom(S,S) sage: i = H.identity() sage: i.image() Simplicial complex with vertex set () and facets {()} sage: i.is_surjective() True sage: S = SimplicialComplex([[0,1]], is_mutable=False) sage: T = SimplicialComplex([[0,1], [0,2]], is_mutable=False) sage: f = {0:0,1:1} sage: g = {0:0,1:1} sage: k = {0:0,1:2} sage: H = Hom(S,T) sage: x = H(f) sage: y = H(g) sage: z = H(k) sage: x == y True sage: x == z False sage: x.image() Simplicial complex with vertex set (0, 1) and facets {(0, 1)} sage: y.image() Simplicial complex with vertex set (0, 1) and facets {(0, 1)} sage: z.image() Simplicial complex with vertex set (0, 2) and facets {(0, 2)}
"""
def is_surjective(self): """ Returns ``True`` if and only if ``self`` is surjective.
EXAMPLES::
sage: S = SimplicialComplex([(0,1,2)], is_mutable=False) sage: S Simplicial complex with vertex set (0, 1, 2) and facets {(0, 1, 2)} sage: T = SimplicialComplex([(0,1)], is_mutable=False) sage: T Simplicial complex with vertex set (0, 1) and facets {(0, 1)} sage: H = Hom(S,T) sage: x = H({0:0,1:1,2:1}) sage: x.is_surjective() True
sage: S = SimplicialComplex([[0,1],[2,3]], is_mutable=False) sage: T = SimplicialComplex([[0,1]], is_mutable=False) sage: f = {0:0,1:1,2:0,3:1} sage: H = Hom(S,T) sage: x = H(f) sage: x.is_surjective() True """
def is_injective(self): """ Returns ``True`` if and only if ``self`` is injective.
EXAMPLES::
sage: S = simplicial_complexes.Sphere(1) sage: T = simplicial_complexes.Sphere(2) sage: U = simplicial_complexes.Sphere(3) sage: H = Hom(T,S) sage: G = Hom(T,U) sage: f = {0:0,1:1,2:0,3:1} sage: x = H(f) sage: g = {0:0,1:1,2:2,3:3} sage: y = G(g) sage: x.is_injective() False sage: y.is_injective() True
"""
def is_identity(self): """ If ``self`` is an identity morphism, returns ``True``. Otherwise, ``False``.
EXAMPLES::
sage: T = simplicial_complexes.Sphere(1) sage: G = Hom(T,T) sage: T Minimal triangulation of the 1-sphere sage: j = G({0:0,1:1,2:2}) sage: j.is_identity() True
sage: S = simplicial_complexes.Sphere(2) sage: T = simplicial_complexes.Sphere(3) sage: H = Hom(S,T) sage: f = {0:0,1:1,2:2,3:3} sage: x = H(f) sage: x Simplicial complex morphism: From: Minimal triangulation of the 2-sphere To: Minimal triangulation of the 3-sphere Defn: 0 |--> 0 1 |--> 1 2 |--> 2 3 |--> 3 sage: x.is_identity() False """ else: return False else:
def fiber_product(self, other, rename_vertices = True): """ Fiber product of ``self`` and ``other``. Both morphisms should have the same codomain. The method returns a morphism of simplicial complexes, which is the morphism from the space of the fiber product to the codomain.
EXAMPLES::
sage: S = SimplicialComplex([[0,1],[1,2]], is_mutable=False) sage: T = SimplicialComplex([[0,2],[1]], is_mutable=False) sage: U = SimplicialComplex([[0,1],[2]], is_mutable=False) sage: H = Hom(S,U) sage: G = Hom(T,U) sage: f = {0:0,1:1,2:0} sage: g = {0:0,1:1,2:1} sage: x = H(f) sage: y = G(g) sage: z = x.fiber_product(y) sage: z Simplicial complex morphism: From: Simplicial complex with 4 vertices and facets {('L2R0',), ('L1R1',), ('L0R0', 'L1R2')} To: Simplicial complex with vertex set (0, 1, 2) and facets {(2,), (0, 1)} Defn: L1R2 |--> 1 L1R1 |--> 1 L2R0 |--> 0 L0R0 |--> 0 """ raise ValueError("self and other must have the same codomain.") else: v.append((i,j)) f[(i,j)] = self._vertex_dictionary[i]
def mapping_torus(self): r""" The mapping torus of a simplicial complex endomorphism
The mapping torus is the simplicial complex formed by taking the product of the domain of ``self`` with a `4` point interval `[I_0, I_1, I_2, I_3]` and identifying vertices of the form `(I_0, v)` with `(I_3, w)` where `w` is the image of `v` under the given morphism.
See :wikipedia:`Mapping torus`
EXAMPLES::
sage: C = simplicial_complexes.Sphere(1) # Circle sage: T = Hom(C,C).identity().mapping_torus() ; T # Torus Simplicial complex with 9 vertices and 18 facets sage: T.homology() == simplicial_complexes.Torus().homology() True
sage: f = Hom(C,C)({0:0,1:2,2:1}) sage: K = f.mapping_torus() ; K # Klein Bottle Simplicial complex with 9 vertices and 18 facets sage: K.homology() == simplicial_complexes.KleinBottle().homology() True
TESTS::
sage: g = Hom(simplicial_complexes.Simplex([1]),C)({1:0}) sage: g.mapping_torus() Traceback (most recent call last): ... ValueError: self must have the same domain and codomain. """
def induced_homology_morphism(self, base_ring=None, cohomology=False): """ The map in (co)homology induced by this map
INPUT:
- ``base_ring`` -- must be a field (optional, default ``QQ``)
- ``cohomology`` -- boolean (optional, default ``False``). If ``True``, the map induced in cohomology rather than homology.
EXAMPLES::
sage: S = simplicial_complexes.Sphere(1) sage: T = S.product(S, is_mutable=False) sage: H = Hom(S,T) sage: diag = H.diagonal_morphism() sage: h = diag.induced_homology_morphism(QQ) sage: h Graded vector space morphism: From: Homology module of Minimal triangulation of the 1-sphere over Rational Field To: Homology module of Simplicial complex with 9 vertices and 18 facets over Rational Field Defn: induced by: Simplicial complex morphism: From: Minimal triangulation of the 1-sphere To: Simplicial complex with 9 vertices and 18 facets Defn: 0 |--> L0R0 1 |--> L1R1 2 |--> L2R2
We can view the matrix form for the homomorphism::
sage: h.to_matrix(0) # in degree 0 [1] sage: h.to_matrix(1) # in degree 1 [1] [0] sage: h.to_matrix() # the entire homomorphism [1|0] [-+-] [0|1] [0|0] [-+-] [0|0]
We can evaluate it on (co)homology classes::
sage: coh = diag.induced_homology_morphism(QQ, cohomology=True) sage: coh.to_matrix(1) [1 0] sage: x,y = list(T.cohomology_ring(QQ).basis(1)) sage: coh(x) h^{1,0} sage: coh(2*x+3*y) 2*h^{1,0}
Note that the complexes must be immutable for this to work. Many, but not all, complexes are immutable when constructed::
sage: S.is_immutable() True sage: S.barycentric_subdivision().is_immutable() False sage: S2 = S.suspension() sage: S2.is_immutable() False sage: h = Hom(S,S2)({0: 0, 1:1, 2:2}).induced_homology_morphism() Traceback (most recent call last): ... ValueError: the domain and codomain complexes must be immutable sage: S2.set_immutable(); S2.is_immutable() True sage: h = Hom(S,S2)({0: 0, 1:1, 2:2}).induced_homology_morphism() """
def is_contiguous_to(self, other): r""" Return ``True`` if ``self`` is contiguous to ``other``.
Two morphisms `f_0, f_1: K \to L` are *contiguous* if for any simplex `\sigma \in K`, the union `f_0(\sigma) \cup f_1(\sigma)` is a simplex in `L`. This is not a transitive relation, but it induces an equivalence relation on simplicial maps: `f` is equivalent to `g` if there is a finite sequence `f_0 = f`, `f_1`, ..., `f_n = g` such that `f_i` and `f_{i+1}` are contiguous for each `i`.
This is related to maps being homotopic: if they are contiguous, then they induce homotopic maps on the geometric realizations. Given two homotopic maps on the geometric realizations, then after barycentrically subdividing `n` times for some `n`, the maps have simplicial approximations which are in the same contiguity class. (This last fact is only true if the domain is a *finite* simplicial complex, by the way.)
See Section 3.5 of Spanier [Spa1966]_ for details.
ALGORITHM:
It is enough to check when `\sigma` ranges over the facets.
INPUT:
- ``other`` -- a simplicial complex morphism with the same domain and codomain as ``self``
EXAMPLES::
sage: K = simplicial_complexes.Simplex(1) sage: L = simplicial_complexes.Sphere(1) sage: H = Hom(K, L) sage: f = H({0: 0, 1: 1}) sage: g = H({0: 0, 1: 0}) sage: f.is_contiguous_to(f) True sage: f.is_contiguous_to(g) True sage: h = H({0: 1, 1: 2}) sage: f.is_contiguous_to(h) False
TESTS::
sage: one = Hom(K,K).identity() sage: one.is_contiguous_to(f) False sage: one.is_contiguous_to(3) # nonsensical input False """ for sigma in domain.facets())
|