Path: blob/master/src/sage/homology/simplicial_complex_morphism.py
8818 views
r"""1Morphisms of simplicial complexes23AUTHORS:45- Benjamin Antieau <[email protected]> (2009.06)67- Travis Scrimshaw (2012-08-18): Made all simplicial complexes immutable to8work with the homset cache.910This module implements morphisms of simplicial complexes. The input is given11by a dictionary on the vertex set of a simplicial complex. The initialization12checks that faces are sent to faces.1314There is also the capability to create the fiber product of two morphisms with15the same codomain.1617EXAMPLES::1819sage: S = SimplicialComplex([[0,2],[1,5],[3,4]], is_mutable=False)20sage: H = Hom(S,S.product(S, is_mutable=False))21sage: H.diagonal_morphism()22Simplicial complex morphism {0: 'L0R0', 1: 'L1R1', 2: 'L2R2', 3: 'L3R3', 4: 'L4R4', 5: 'L5R5'} 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 facets2324sage: S = SimplicialComplex([[0,2],[1,5],[3,4]], is_mutable=False)25sage: T = SimplicialComplex([[0,2],[1,3]], is_mutable=False)26sage: f = {0:0,1:1,2:2,3:1,4:3,5:3}27sage: H = Hom(S,T)28sage: x = H(f)29sage: x.image()30Simplicial complex with vertex set (0, 1, 2, 3) and facets {(1, 3), (0, 2)}31sage: x.is_surjective()32True33sage: x.is_injective()34False35sage: x.is_identity()36False3738sage: S = simplicial_complexes.Sphere(2)39sage: H = Hom(S,S)40sage: i = H.identity()41sage: i.image()42Simplicial complex with vertex set (0, 1, 2, 3) and facets {(0, 2, 3), (0, 1, 2), (1, 2, 3), (0, 1, 3)}43sage: i.is_surjective()44True45sage: i.is_injective()46True47sage: i.is_identity()48True4950sage: S = simplicial_complexes.Sphere(2)51sage: H = Hom(S,S)52sage: i = H.identity()53sage: j = i.fiber_product(i)54sage: j55Simplicial complex morphism {'L1R1': 1, 'L3R3': 3, 'L2R2': 2, 'L0R0': 0} from Simplicial complex with 4 vertices and 4 facets to Simplicial complex with vertex set (0, 1, 2, 3) and facets {(0, 2, 3), (0, 1, 2), (1, 2, 3), (0, 1, 3)}5657sage: S = simplicial_complexes.Sphere(2)58sage: T = S.product(SimplicialComplex([[0,1]]), rename_vertices = False, is_mutable=False)59sage: H = Hom(T,S)60sage: T61Simplicial complex with 8 vertices and 12 facets62sage: T.vertices()63((0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1), (3, 0), (3, 1))64sage: 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}65sage: x = H(f)66sage: U = simplicial_complexes.Sphere(1)67sage: G = Hom(U,S)68sage: U69Simplicial complex with vertex set (0, 1, 2) and facets {(1, 2), (0, 2), (0, 1)}70sage: g = {0:0,1:1,2:2}71sage: y = G(g)72sage: z = y.fiber_product(x)73sage: z # this is the mapping path space74Simplicial complex morphism {'L2R(2, 0)': 2, 'L2R(2, 1)': 2, 'L0R(0, 0)': 0, 'L0R(0, 1)': 0, 'L1R(1, 0)': 1, 'L1R(1, 1)': 1} from Simplicial complex with 6 vertices and 6 facets to Simplicial complex with vertex set (0, 1, 2, 3) and facets {(0, 2, 3), (0, 1, 2), (1, 2, 3), (0, 1, 3)}75"""7677#*****************************************************************************78# Copyright (C) 2009 D. Benjamin Antieau <[email protected]>79#80# Distributed under the terms of the GNU General Public License (GPL)81#82# This code is distributed in the hope that it will be useful,83# but WITHOUT ANY WARRANTY; without even the implied warranty84# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.85#86# See the GNU General Public License for more details; the full text87# is available at:88#89# http://www.gnu.org/licenses/90#91#*****************************************************************************9293import sage.homology.simplicial_complex as simplicial_complex94import sage.matrix.all as matrix95from sage.structure.sage_object import SageObject96from sage.rings.integer_ring import ZZ97from sage.homology.chain_complex_morphism import ChainComplexMorphism98from sage.combinat.permutation import Permutation99from sage.algebras.steenrod.steenrod_algebra_misc import convert_perm100101def is_SimplicialComplexMorphism(x):102"""103Returns ``True`` if and only if ``x`` is a morphism of simplicial complexes.104105EXAMPLES::106107sage: from sage.homology.simplicial_complex_morphism import is_SimplicialComplexMorphism108sage: S = SimplicialComplex([[0,1],[3,4]], is_mutable=False)109sage: H = Hom(S,S)110sage: f = {0:0,1:1,3:3,4:4}111sage: x = H(f)112sage: is_SimplicialComplexMorphism(x)113True114115"""116return isinstance(x,SimplicialComplexMorphism)117118class SimplicialComplexMorphism(SageObject):119"""120An element of this class is a morphism of simplicial complexes.121"""122def __init__(self,f,X,Y):123"""124Input is a dictionary ``f``, the domain ``X``, and the codomain ``Y``.125126One can define the dictionary on the vertices of `X`.127128EXAMPLES::129130sage: S = SimplicialComplex([[0,1],[2],[3,4],[5]], is_mutable=False)131sage: H = Hom(S,S)132sage: f = {0:0,1:1,2:2,3:3,4:4,5:5}133sage: g = {0:0,1:1,2:0,3:3,4:4,5:0}134sage: x = H(f)135sage: y = H(g)136sage: x == y137False138sage: x.image()139Simplicial complex with vertex set (0, 1, 2, 3, 4, 5) and facets {(3, 4), (5,), (2,), (0, 1)}140sage: y.image()141Simplicial complex with vertex set (0, 1, 3, 4) and facets {(3, 4), (0, 1)}142sage: x.image() == y.image()143False144"""145if not isinstance(X,simplicial_complex.SimplicialComplex) or not isinstance(Y,simplicial_complex.SimplicialComplex):146raise ValueError("X and Y must be SimplicialComplexes.")147if not set(f.keys()) == X._vertex_set.set():148raise ValueError("f must be a dictionary from the vertex set of X to single values in the vertex set of Y.")149dim = X.dimension()150Y_faces = Y.faces()151for k in range(dim+1):152for i in X.faces()[k]:153tup = i.tuple()154fi = []155for j in tup:156fi.append(f[j])157v = simplicial_complex.Simplex(set(fi))158if not v in Y_faces[v.dimension()]:159raise ValueError("f must be a dictionary from the vertices of X to the vertices of Y.")160self._vertex_dictionary = f161self._domain = X162self._codomain = Y163164def __eq__(self,x):165"""166Returns ``True`` if and only if ``self == x``.167168EXAMPLES::169170sage: S = simplicial_complexes.Sphere(2)171sage: H = Hom(S,S)172sage: i = H.identity()173sage: i174Simplicial complex morphism {0: 0, 1: 1, 2: 2, 3: 3} from Simplicial complex with vertex set (0, 1, 2, 3) and facets {(0, 2, 3), (0, 1, 2), (1, 2, 3), (0, 1, 3)} to Simplicial complex with vertex set (0, 1, 2, 3) and facets {(0, 2, 3), (0, 1, 2), (1, 2, 3), (0, 1, 3)}175sage: f = {0:0,1:1,2:2,3:2}176sage: j = H(f)177sage: i==j178False179180sage: T = SimplicialComplex([[1,2]], is_mutable=False)181sage: T182Simplicial complex with vertex set (1, 2) and facets {(1, 2)}183sage: G = Hom(T,T)184sage: k = G.identity()185sage: g = {1:1,2:2}186sage: l = G(g)187sage: k == l188True189190"""191if not isinstance(x,SimplicialComplexMorphism) or self._codomain != x._codomain or self._domain != x._domain or self._vertex_dictionary != x._vertex_dictionary:192return False193else:194return True195196def __call__(self,x,orientation=False):197"""198Input is a simplex of the domain. Output is the image simplex.199200If the optional argument ``orientation`` is ``True``, then this201returns a pair ``(image simplex, oriented)`` where ``oriented``202is 1 or `-1` depending on whether the map preserves or reverses203the orientation of the image simplex.204205EXAMPLES::206207sage: S = simplicial_complexes.Sphere(2)208sage: T = simplicial_complexes.Sphere(3)209sage: S210Simplicial complex with vertex set (0, 1, 2, 3) and facets {(0, 2, 3), (0, 1, 2), (1, 2, 3), (0, 1, 3)}211sage: T212Simplicial complex with vertex set (0, 1, 2, 3, 4) and 5 facets213sage: f = {0:0,1:1,2:2,3:3}214sage: H = Hom(S,T)215sage: x = H(f)216sage: from sage.homology.simplicial_complex import Simplex217sage: x(Simplex([0,2,3]))218(0, 2, 3)219220An orientation-reversing example::221222sage: X = SimplicialComplex([[0,1]], is_mutable=False)223sage: g = Hom(X,X)({0:1, 1:0})224sage: g(Simplex([0,1]))225(0, 1)226sage: g(Simplex([0,1]), orientation=True)227((0, 1), -1)228"""229dim = self._domain.dimension()230if not isinstance(x,simplicial_complex.Simplex) or x.dimension() > dim or not x in self._domain.faces()[x.dimension()]:231raise ValueError, "x must be a simplex of the source of f"232tup=x.tuple()233fx=[]234for j in tup:235fx.append(self._vertex_dictionary[j])236if orientation:237if len(set(fx)) == len(tup):238oriented = Permutation(convert_perm(fx)).signature()239else:240oriented = 1241return (simplicial_complex.Simplex(set(fx)), oriented)242else:243return simplicial_complex.Simplex(set(fx))244245def _repr_(self):246"""247Return a string representation of ``self``.248249EXAMPLES::250251sage: S = simplicial_complexes.Sphere(2)252sage: H = Hom(S,S)253sage: i = H.identity()254sage: i255Simplicial complex morphism {0: 0, 1: 1, 2: 2, 3: 3} from Simplicial complex with vertex set (0, 1, 2, 3) and facets {(0, 2, 3), (0, 1, 2), (1, 2, 3), (0, 1, 3)} to Simplicial complex with vertex set (0, 1, 2, 3) and facets {(0, 2, 3), (0, 1, 2), (1, 2, 3), (0, 1, 3)}256sage: i._repr_()257'Simplicial complex morphism {0: 0, 1: 1, 2: 2, 3: 3} from Simplicial complex with vertex set (0, 1, 2, 3) and facets {(0, 2, 3), (0, 1, 2), (1, 2, 3), (0, 1, 3)} to Simplicial complex with vertex set (0, 1, 2, 3) and facets {(0, 2, 3), (0, 1, 2), (1, 2, 3), (0, 1, 3)}'258"""259return "Simplicial complex morphism " + str(self._vertex_dictionary) + " from " + self._domain._repr_() + " to " + self._codomain._repr_()260261def associated_chain_complex_morphism(self,base_ring=ZZ,augmented=False,cochain=False):262"""263Returns the associated chain complex morphism of ``self``.264265EXAMPLES::266267sage: S = simplicial_complexes.Sphere(1)268sage: T = simplicial_complexes.Sphere(2)269sage: H = Hom(S,T)270sage: f = {0:0,1:1,2:2}271sage: x = H(f)272sage: x273Simplicial 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)}274sage: a = x.associated_chain_complex_morphism()275sage: a276Chain 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 Ring277sage: a._matrix_dictionary278{0: [0 0 0]279[0 1 0]280[0 0 1]281[1 0 0],2821: [0 0 0]283[0 1 0]284[0 0 0]285[1 0 0]286[0 0 0]287[0 0 1],2882: []}289sage: x.associated_chain_complex_morphism(augmented=True)290Chain 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 Ring291sage: x.associated_chain_complex_morphism(cochain=True)292Chain 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 Ring293sage: x.associated_chain_complex_morphism(augmented=True,cochain=True)294Chain 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 Ring295sage: x.associated_chain_complex_morphism(base_ring=GF(11))296Chain 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 11297298Some simplicial maps which reverse the orientation of a few simplices::299300sage: g = {0:1, 1:2, 2:0}301sage: H(g).associated_chain_complex_morphism()._matrix_dictionary302{0: [0 0 0]303[1 0 0]304[0 1 0]305[0 0 1], 1: [ 0 0 0]306[-1 0 0]307[ 0 0 0]308[ 0 0 1]309[ 0 0 0]310[ 0 -1 0], 2: []}311312sage: X = SimplicialComplex([[0, 1]], is_mutable=False)313sage: Hom(X,X)({0:1, 1:0}).associated_chain_complex_morphism()._matrix_dictionary314{0: [0 1]315[1 0], 1: [-1]}316"""317max_dim = max(self._domain.dimension(),self._codomain.dimension())318min_dim = min(self._domain.dimension(),self._codomain.dimension())319matrices = {}320if augmented is True:321m = matrix.Matrix(base_ring,1,1,1)322if not cochain:323matrices[-1] = m324else:325matrices[-1] = m.transpose()326for dim in range(min_dim+1):327# X_faces = list(self._domain.faces()[dim])328# Y_faces = list(self._codomain.faces()[dim])329X_faces = list(self._domain.n_cells(dim))330Y_faces = list(self._codomain.n_cells(dim))331num_faces_X = len(X_faces)332num_faces_Y = len(Y_faces)333mval = [0 for i in range(num_faces_X*num_faces_Y)]334for i in X_faces:335y, oriented = self(i, orientation=True)336if y.dimension() < dim:337pass338else:339mval[X_faces.index(i)+(Y_faces.index(y)*num_faces_X)] = oriented340m = matrix.Matrix(base_ring,num_faces_Y,num_faces_X,mval,sparse=True)341if not cochain:342matrices[dim] = m343else:344matrices[dim] = m.transpose()345for dim in range(min_dim+1,max_dim+1):346try:347l1 = len(self._codomain.n_cells(dim))348except KeyError:349l1 = 0350try:351l2 = len(self._domain.n_cells(dim))352except KeyError:353l2 = 0354m = matrix.zero_matrix(base_ring,l1,l2,sparse=True)355if not cochain:356matrices[dim] = m357else:358matrices[dim] = m.transpose()359if not cochain:360return ChainComplexMorphism(matrices,\361self._domain.chain_complex(base_ring=base_ring,augmented=augmented,cochain=cochain),\362self._codomain.chain_complex(base_ring=base_ring,augmented=augmented,cochain=cochain))363else:364return ChainComplexMorphism(matrices,\365self._codomain.chain_complex(base_ring=base_ring,augmented=augmented,cochain=cochain),\366self._domain.chain_complex(base_ring=base_ring,augmented=augmented,cochain=cochain))367368def image(self):369"""370Computes the image simplicial complex of `f`.371372EXAMPLES::373374sage: S = SimplicialComplex([[0,1],[2,3]], is_mutable=False)375sage: T = SimplicialComplex([[0,1]], is_mutable=False)376sage: f = {0:0,1:1,2:0,3:1}377sage: H = Hom(S,T)378sage: x = H(f)379sage: x.image()380Simplicial complex with vertex set (0, 1) and facets {(0, 1)}381382sage: S = SimplicialComplex(is_mutable=False)383sage: H = Hom(S,S)384sage: i = H.identity()385sage: i.image()386Simplicial complex with vertex set () and facets {()}387sage: i.is_surjective()388True389sage: S = SimplicialComplex([[0,1]], is_mutable=False)390sage: T = SimplicialComplex([[0,1], [0,2]], is_mutable=False)391sage: f = {0:0,1:1}392sage: g = {0:0,1:1}393sage: k = {0:0,1:2}394sage: H = Hom(S,T)395sage: x = H(f)396sage: y = H(g)397sage: z = H(k)398sage: x == y399True400sage: x == z401False402sage: x.image()403Simplicial complex with vertex set (0, 1) and facets {(0, 1)}404sage: y.image()405Simplicial complex with vertex set (0, 1) and facets {(0, 1)}406sage: z.image()407Simplicial complex with vertex set (0, 2) and facets {(0, 2)}408409"""410fa = [self(i) for i in self._domain.facets()]411return simplicial_complex.SimplicialComplex(fa, maximality_check=True)412413def domain(self):414"""415Returns the domain of the morphism.416417EXAMPLES::418419sage: S = SimplicialComplex([[0,1],[2,3]], is_mutable=False)420sage: T = SimplicialComplex([[0,1]], is_mutable=False)421sage: f = {0:0,1:1,2:0,3:1}422sage: H = Hom(S,T)423sage: x = H(f)424sage: x.domain()425Simplicial complex with vertex set (0, 1, 2, 3) and facets {(2, 3), (0, 1)}426"""427return self._domain428429def codomain(self):430"""431Returns the codomain of the morphism.432433EXAMPLES::434435sage: S = SimplicialComplex([[0,1],[2,3]], is_mutable=False)436sage: T = SimplicialComplex([[0,1]], is_mutable=False)437sage: f = {0:0,1:1,2:0,3:1}438sage: H = Hom(S,T)439sage: x = H(f)440sage: x.codomain()441Simplicial complex with vertex set (0, 1) and facets {(0, 1)}442443"""444return self._codomain445446def is_surjective(self):447"""448Returns ``True`` if and only if ``self`` is surjective.449450EXAMPLES::451452sage: S = SimplicialComplex([(0,1,2)], is_mutable=False)453sage: S454Simplicial complex with vertex set (0, 1, 2) and facets {(0, 1, 2)}455sage: T = SimplicialComplex([(0,1)], is_mutable=False)456sage: T457Simplicial complex with vertex set (0, 1) and facets {(0, 1)}458sage: H = Hom(S,T)459sage: x = H({0:0,1:1,2:1})460sage: x.is_surjective()461True462463sage: S = SimplicialComplex([[0,1],[2,3]], is_mutable=False)464sage: T = SimplicialComplex([[0,1]], is_mutable=False)465sage: f = {0:0,1:1,2:0,3:1}466sage: H = Hom(S,T)467sage: x = H(f)468sage: x.is_surjective()469True470"""471return self._codomain == self.image()472473def is_injective(self):474"""475Returns ``True`` if and only if ``self`` is injective.476477EXAMPLES::478479sage: S = simplicial_complexes.Sphere(1)480sage: T = simplicial_complexes.Sphere(2)481sage: U = simplicial_complexes.Sphere(3)482sage: H = Hom(T,S)483sage: G = Hom(T,U)484sage: f = {0:0,1:1,2:0,3:1}485sage: x = H(f)486sage: g = {0:0,1:1,2:2,3:3}487sage: y = G(g)488sage: x.is_injective()489False490sage: y.is_injective()491True492493"""494v = [self._vertex_dictionary[i[0]] for i in self._domain.faces()[0]]495for i in v:496if v.count(i) > 1:497return False498return True499500def is_identity(self):501"""502If ``self`` is an identity morphism, returns ``True``.503Otherwise, ``False``.504505EXAMPLES::506507sage: T = simplicial_complexes.Sphere(1)508sage: G = Hom(T,T)509sage: T510Simplicial complex with vertex set (0, 1, 2) and facets {(1, 2), (0, 2), (0, 1)}511sage: j = G({0:0,1:1,2:2})512sage: j.is_identity()513True514515sage: S = simplicial_complexes.Sphere(2)516sage: T = simplicial_complexes.Sphere(3)517sage: H = Hom(S,T)518sage: f = {0:0,1:1,2:2,3:3}519sage: x = H(f)520sage: x521Simplicial complex morphism {0: 0, 1: 1, 2: 2, 3: 3} from Simplicial complex with vertex set (0, 1, 2, 3) and facets {(0, 2, 3), (0, 1, 2), (1, 2, 3), (0, 1, 3)} to Simplicial complex with vertex set (0, 1, 2, 3, 4) and 5 facets522sage: x.is_identity()523False524525"""526if self._domain != self._codomain:527return False528else:529f = dict()530for i in self._domain._vertex_set.set():531f[i] = i532if self._vertex_dictionary != f:533return False534else:535return True536537def fiber_product(self, other, rename_vertices = True):538"""539Fiber product of ``self`` and ``other``. Both morphisms should have540the same codomain. The method returns a morphism of simplicial541complexes, which is the morphism from the space of the fiber product542to the codomain.543544EXAMPLES::545546sage: S = SimplicialComplex([[0,1],[1,2]], is_mutable=False)547sage: T = SimplicialComplex([[0,2],[1]], is_mutable=False)548sage: U = SimplicialComplex([[0,1],[2]], is_mutable=False)549sage: H = Hom(S,U)550sage: G = Hom(T,U)551sage: f = {0:0,1:1,2:0}552sage: g = {0:0,1:1,2:1}553sage: x = H(f)554sage: y = G(g)555sage: z = x.fiber_product(y)556sage: z557Simplicial complex morphism {'L1R2': 1, 'L1R1': 1, 'L2R0': 0, 'L0R0': 0}558from Simplicial complex with 4 vertices and facets559{('L2R0',), ('L1R1',), ('L0R0', 'L1R2')} to Simplicial complex560with vertex set (0, 1, 2) and facets {(2,), (0, 1)}561"""562if self._codomain != other._codomain:563raise ValueError("self and other must have the same codomain.")564X = self._domain.product(other._domain,rename_vertices = rename_vertices)565v = []566f = dict()567eff1 = self._domain._vertex_set568eff2 = other._domain._vertex_set569for i in eff1:570for j in eff2:571if self(simplicial_complex.Simplex([i])) == other(simplicial_complex.Simplex([j])):572if rename_vertices:573v.append("L"+str(i)+"R"+str(j))574f["L"+str(i)+"R"+str(j)] = self._vertex_dictionary[i]575else:576v.append((i,j))577f[(i,j)] = self._vertex_dictionary[i]578return SimplicialComplexMorphism(f, X.generated_subcomplex(v), self._codomain)579580def mapping_torus(self):581r"""582The mapping torus of a simplicial complex endomorphism583584The mapping torus is the simplicial complex formed by taking585the product of the domain of ``self`` with a `4` point586interval `[I_0, I_1, I_2, I_3]` and identifying vertices of587the form `(I_0, v)` with `(I_3, w)` where `w` is the image of588`v` under the given morphism.589590See :wikipedia:`Mapping torus`591592EXAMPLES::593594sage: C = simplicial_complexes.Sphere(1) # Circle595sage: T = Hom(C,C).identity().mapping_torus() ; T # Torus596Simplicial complex with 9 vertices and 18 facets597sage: T.homology() == simplicial_complexes.Torus().homology()598True599600sage: f = Hom(C,C)({0:0,1:2,2:1})601sage: K = f.mapping_torus() ; K # Klein Bottle602Simplicial complex with 9 vertices and 18 facets603sage: K.homology() == simplicial_complexes.KleinBottle().homology()604True605606TESTS::607608sage: g = Hom(simplicial_complexes.Simplex([1]),C)({1:0})609sage: g.mapping_torus()610Traceback (most recent call last):611...612ValueError: self must have the same domain and codomain.613"""614if self._domain != self._codomain:615raise ValueError("self must have the same domain and codomain.")616map_dict = self._vertex_dictionary617interval = simplicial_complex.SimplicialComplex([["I0","I1"],["I1","I2"]])618product = interval.product(self._domain,False)619facets = list(product.maximal_faces())620for facet in self._domain._facets:621left = [ ("I0",v) for v in facet ]622right = [ ("I2",map_dict[v]) for v in facet ]623for i in range(facet.dimension()+1):624facets.append(tuple(left[:i+1]+right[i:]))625return simplicial_complex.SimplicialComplex(facets)626627628