def __call__(self, f): """ `f` is a dictionary of matrices in the basis of the chain complex. EXAMPLES:: sage: S = simplicial_complexes.Sphere(5) sage: H = Hom(S,S) sage: i = H.identity() sage: C = S.chain_complex() sage: G = Hom(C,C) sage: x = i.associated_chain_complex_morphism() sage: f = x._matrix_dictionary sage: y = G(f) sage: x == y True """ return ChainComplexMorphism(f, self.domain(), self.codomain())
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 {0: 0, 1: 1, 2: 2} from Simplicial complex with vertex set (0, 1, 2) and facets {(1, 2), (0, 2), (0, 1)} to Simplicial complex with vertex set (0, 1, 2, 3) and facets {(0, 2, 3), (0, 1, 2), (1, 2, 3), (0, 1, 3)} 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: [0 0 0] [0 1 0] [0 0 1] [1 0 0], 1: [0 0 0] [0 1 0] [0 0 0] [1 0 0] [0 0 0] [0 0 1], 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 0] [1 0 0] [0 1 0] [0 0 1], 1: [ 0 0 0] [-1 0 0] [ 0 0 0] [ 0 0 1] [ 0 0 0] [ 0 -1 0], 2: []} sage: X = SimplicialComplex(1, [[0, 1]]) sage: Hom(X,X)({0:1, 1:0}).associated_chain_complex_morphism()._matrix_dictionary {0: [0 1] [1 0], 1: [-1]} """ max_dim = max(self._domain.dimension(), self._codomain.dimension()) min_dim = min(self._domain.dimension(), self._codomain.dimension()) matrices = {} if augmented is True: m = matrix.Matrix(base_ring, 1, 1, 1) if not cochain: matrices[-1] = m else: matrices[-1] = m.transpose() for dim in range(min_dim + 1): # X_faces = list(self._domain.faces()[dim]) # Y_faces = list(self._codomain.faces()[dim]) X_faces = list(self._domain.n_cells(dim)) Y_faces = list(self._codomain.n_cells(dim)) num_faces_X = len(X_faces) num_faces_Y = len(Y_faces) mval = [0 for i in range(num_faces_X * num_faces_Y)] for i in X_faces: y, oriented = self(i, orientation=True) if y.dimension() < dim: pass else: mval[X_faces.index(i) + (Y_faces.index(y) * num_faces_X)] = oriented m = matrix.Matrix(base_ring, num_faces_Y, num_faces_X, mval, sparse=True) if not cochain: matrices[dim] = m else: matrices[dim] = m.transpose() for dim in range(min_dim + 1, max_dim + 1): try: l1 = len(self._codomain.n_cells(dim)) except KeyError: l1 = 0 try: l2 = len(self._domain.n_cells(dim)) except KeyError: l2 = 0 m = matrix.zero_matrix(base_ring, l1, l2, sparse=True) if not cochain: matrices[dim] = m else: matrices[dim] = m.transpose() if not cochain: return ChainComplexMorphism(matrices,\ 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: return ChainComplexMorphism(matrices,\ 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 __init__(self, matrices, f, g=None): r""" Create a chain homotopy between the given chain maps from a dictionary of matrices. EXAMPLES: 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) sage: D = ChainComplex({2: matrix(ZZ, 1, 1, (6,))}, degree_of_differential=-1) sage: f_d = {1: matrix(ZZ, 1, 2, (0,3)), 2: identity_matrix(ZZ, 1)} sage: f = Hom(C,D)(f_d) sage: H_d = {0: identity_matrix(ZZ, 1), 1: matrix(ZZ, 1, 2, (2,2))} sage: H = ChainHomotopy(H_d, f) sage: H._g.in_degree(0) [] sage: H._g.in_degree(1) [-13 -9] sage: H._g.in_degree(2) [-3] TESTS: Try to construct a chain homotopy in which the maps do not have matching domains and codomains:: sage: g = Hom(C,C)({}) # the zero chain map sage: H = ChainHomotopy(H_d, f, g) Traceback (most recent call last): ... ValueError: the chain maps are not compatible """ domain = f.domain() codomain = f.codomain() deg = domain.degree_of_differential() # Check that the chain complexes are compatible. This should # never arise, because first there should be errors in # constructing the chain maps. But just in case... if domain.degree_of_differential() != codomain.degree_of_differential( ): raise ValueError('the chain complexes are not compatible') if g is not None: # Check that the chain maps are compatible. if not (domain == g.domain() and codomain == g.codomain()): raise ValueError('the chain maps are not compatible') # Check that the data define a chain homotopy. for i in domain.differential(): if i in matrices and i + deg in matrices: if not (codomain.differential(i - deg) * matrices[i] + matrices[i + deg] * domain.differential(i) == f.in_degree(i) - g.in_degree(i)): raise ValueError( 'the data do not define a valid chain homotopy') elif i in matrices: if not (codomain.differential(i - deg) * matrices[i] == f.in_degree(i) - g.in_degree(i)): raise ValueError( 'the data do not define a valid chain homotopy') elif i + deg in matrices: if not (matrices[i + deg] * domain.differential(i) == f.in_degree(i) - g.in_degree(i)): raise ValueError( 'the data do not define a valid chain homotopy') else: # Define g. g_data = {} for i in domain.differential(): if i in matrices and i + deg in matrices: g_data[i] = f.in_degree( i) - matrices[i + deg] * domain.differential( i) - codomain.differential(i - deg) * matrices[i] elif i in matrices: g_data[i] = f.in_degree( i) - codomain.differential(i - deg) * matrices[i] elif i + deg in matrices: g_data[i] = f.in_degree( i) - matrices[i + deg] * domain.differential(i) g = ChainComplexMorphism(g_data, domain, codomain) self._matrix_dictionary = {} for i in matrices: m = matrices[i] # Use immutable matrices because they're hashable. m.set_immutable() self._matrix_dictionary[i] = m self._f = f self._g = g Morphism.__init__(self, Hom(domain, codomain))
def __init__(self, matrices, pi, iota): r""" Create a chain contraction from the given data. EXAMPLES:: sage: from sage.homology.chain_homotopy import ChainContraction 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 try construct a chain contraction, but get the map `\iota` wrong:: sage: pi = Hom(C,D)({0: identity_matrix(ZZ, 1)}) sage: iota = Hom(D,C)({0: zero_matrix(ZZ, 1)}) sage: H = ChainContraction({0: zero_matrix(ZZ, 0, 1), 1: zero_matrix(ZZ, 1), 2: identity_matrix(ZZ, 1)}, pi, iota) Traceback (most recent call last): ... ValueError: the composite 'pi iota' is not the identity Another bad `\iota`:: sage: iota = pi # wrong domain, codomain sage: H = ChainContraction({0: zero_matrix(ZZ, 0, 1), 1: zero_matrix(ZZ, 1), 2: identity_matrix(ZZ, 1)}, pi, iota) Traceback (most recent call last): ... ValueError: the chain maps are not composable `\iota` is okay, but wrong data defining `H`:: sage: iota = Hom(D,C)({0: identity_matrix(ZZ, 1)}) sage: H = ChainContraction({0: zero_matrix(ZZ, 0, 1), 1: identity_matrix(ZZ, 1), 2: identity_matrix(ZZ, 1)}, pi, iota) Traceback (most recent call last): ... ValueError: not an algebraic gradient vector field """ from sage.matrix.constructor import identity_matrix from .chain_complex_morphism import ChainComplexMorphism if not (pi.domain() == iota.codomain() and pi.codomain() == iota.domain()): raise ValueError('the chain maps are not composable') C = pi.domain() D = pi.codomain() base_ring = C.base_ring() # Check that the composite 'pi iota' is 1. for i in D.nonzero_degrees(): if pi.in_degree(i) * iota.in_degree(i) != identity_matrix( base_ring, D.free_module_rank(i)): raise ValueError("the composite 'pi iota' is not the identity") # Construct the chain map 'id_C'. id_C_dict = {} for i in C.nonzero_degrees(): id_C_dict[i] = identity_matrix(base_ring, C.free_module_rank(i)) id_C = ChainComplexMorphism(id_C_dict, C, C) # Now check that # - `H` is a chain homotopy between `id_C` and `\iota \pi` # - `HH = 0` ChainHomotopy.__init__(self, matrices, id_C, iota * pi) if not self.is_algebraic_gradient_vector_field(): raise ValueError('not an algebraic gradient vector field') # Check that `\pi H = 0`: deg = C.degree_of_differential() for i in matrices: if pi.in_degree(i - deg) * matrices[i] != 0: raise ValueError( 'the data do not define a valid chain contraction: pi H != 0' ) # Check that `H \iota = 0`: for i in iota._matrix_dictionary: if i in matrices: if matrices[i] * iota.in_degree(i) != 0: raise ValueError( 'the data do not define a valid chain contraction: H iota != 0' ) self._pi = pi self._iota = iota
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]} """ max_dim = max(self.domain().dimension(),self.codomain().dimension()) min_dim = min(self.domain().dimension(),self.codomain().dimension()) matrices = {} if augmented is True: m = matrix(base_ring,1,1,1) if not cochain: matrices[-1] = m else: matrices[-1] = m.transpose() for dim in range(min_dim+1): X_faces = list(self.domain().n_cells(dim)) Y_faces = list(self.codomain().n_cells(dim)) num_faces_X = len(X_faces) num_faces_Y = len(Y_faces) mval = [0 for i in range(num_faces_X*num_faces_Y)] for i in X_faces: y, oriented = self(i, orientation=True) if y.dimension() < dim: pass else: mval[X_faces.index(i)+(Y_faces.index(y)*num_faces_X)] = oriented m = matrix(base_ring,num_faces_Y,num_faces_X,mval,sparse=True) if not cochain: matrices[dim] = m else: matrices[dim] = m.transpose() for dim in range(min_dim+1,max_dim+1): try: l1 = len(self.codomain().n_cells(dim)) except KeyError: l1 = 0 try: l2 = len(self.domain().n_cells(dim)) except KeyError: l2 = 0 m = zero_matrix(base_ring,l1,l2,sparse=True) if not cochain: matrices[dim] = m else: matrices[dim] = m.transpose() if not cochain: return ChainComplexMorphism(matrices,\ 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: return ChainComplexMorphism(matrices,\ self.codomain().chain_complex(base_ring=base_ring,augmented=augmented,cochain=cochain),\ self.domain().chain_complex(base_ring=base_ring,augmented=augmented,cochain=cochain))