Path: blob/master/src/sage/homology/cubical_complex.py
8818 views
r"""1Finite cubical complexes23AUTHORS:45- John H. Palmieri (2009-08)67This module implements the basic structure of finite cubical8complexes. For full mathematical details, see Kaczynski, Mischaikow,9and Mrozek [KMM]_, for example.1011Cubical complexes are topological spaces built from gluing together12cubes of various dimensions; the collection of cubes must be closed13under taking faces, just as with a simplicial complex. In this14context, a "cube" means a product of intervals of length 1 or length 015(degenerate intervals), with integer endpoints, and its faces are16obtained by using the nondegenerate intervals: if `C` is a cube -- a17product of degenerate and nondegenerate intervals -- and if `[i,i+1]`18is the `k`-th nondegenerate factor, then `C` has two faces indexed by19`k`: the cubes obtained by replacing `[i, i+1]` with `[i, i]` or20`[i+1, i+1]`.2122So to construct a space homeomorphic to a circle as a cubical complex,23we could take for example the four line segments in the plane from24`(0,2)` to `(0,3)` to `(1,3)` to `(1,2)` to `(0,2)`. In Sage, this is25done with the following command::2627sage: S1 = CubicalComplex([([0,0], [2,3]), ([0,1], [3,3]), ([0,1], [2,2]), ([1,1], [2,3])]); S128Cubical complex with 4 vertices and 8 cubes2930The argument to ``CubicalComplex`` is a list of the maximal "cubes" in31the complex. Each "cube" can be an instance of the class ``Cube`` or32a list (or tuple) of "intervals", and an "interval" is a pair of33integers, of one of the two forms `[i, i]` or `[i, i+1]`. So the34cubical complex ``S1`` above has four maximal cubes::3536sage: S1.maximal_cells()37{[0,0] x [2,3], [1,1] x [2,3], [0,1] x [3,3], [0,1] x [2,2]}3839The first of these, for instance, is the product of the degenerate40interval `[0,0]` with the unit interval `[2,3]`: this is the line41segment in the plane from `(0,2)` to `(0,3)`. We could form a42topologically equivalent space by inserting some degenerate simplices::4344sage: S1.homology()45{0: 0, 1: Z}46sage: X = CubicalComplex([([0,0], [2,3], [2]), ([0,1], [3,3], [2]), ([0,1], [2,2], [2]), ([1,1], [2,3], [2])])47sage: X.homology()48{0: 0, 1: Z}4950Topologically, the cubical complex ``X`` consists of four edges of a51square in `\RR^3`: the same unit square as ``S1``, but embedded in52`\RR^3` with `z`-coordinate equal to 2. Thus ``X`` is homeomorphic to53``S1`` (in fact, they're "cubically equivalent"), and this is54reflected in the fact that they have isomorphic homology groups.5556REFERENCES:5758.. [KMM] Tomasz Kaczynski, Konstantin Mischaikow, and Marian Mrozek,59"Computational Homology", Springer-Verlag (2004).6061.. note::6263This class derives from64:class:`~sage.homology.cell_complex.GenericCellComplex`, and so65inherits its methods. Some of those methods are not listed here;66see the :mod:`Generic Cell Complex <sage.homology.cell_complex>`67page instead.68"""6970from copy import copy71from sage.homology.cell_complex import GenericCellComplex72from sage.structure.sage_object import SageObject73from sage.rings.integer import Integer74from sage.sets.set import Set75from sage.rings.integer_ring import ZZ76from sage.matrix.constructor import matrix77from sage.homology.chain_complex import ChainComplex78from sage.graphs.graph import Graph7980class Cube(SageObject):81r"""82Define a cube for use in constructing a cubical complex.8384"Elementary cubes" are products of intervals with integer85endpoints, each of which is either a unit interval or a degenerate86(length 0) interval; for example,8788.. math::8990[0,1] \times [3,4] \times [2,2] \times [1,2]9192is a 3-dimensional cube (since one of the intervals is degenerate)93embedded in `\RR^4`.9495:param data: list or tuple of terms of the form ``(i,i+1)`` or96``(i,i)`` or ``(i,)`` -- the last two are degenerate intervals.97:return: an elementary cube9899Each cube is stored in a standard form: a tuple of tuples, with a100nondegenerate interval ``[j,j]`` represented by ``(j,j)``, not101``(j,)``. (This is so that for any interval ``I``, ``I[1]`` will102produce a value, not an ``IndexError``.)103104EXAMPLES::105106sage: from sage.homology.cubical_complex import Cube107sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]]); C108[1,2] x [5,5] x [6,7] x [-1,0]109sage: C.dimension() # number of nondegenerate intervals1103111sage: C.nondegenerate_intervals() # indices of these intervals112[0, 2, 3]113sage: C.face(1, upper=False)114[1,2] x [5,5] x [6,6] x [-1,0]115sage: C.face(1, upper=True)116[1,2] x [5,5] x [7,7] x [-1,0]117sage: Cube(()).dimension() # empty cube has dimension -1118-1119"""120def __init__(self, data):121"""122Define a cube for use in constructing a cubical complex.123124See ``Cube`` for more information.125126EXAMPLES::127128sage: from sage.homology.cubical_complex import Cube129sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]]); C # indirect doctest130[1,2] x [5,5] x [6,7] x [-1,0]131sage: C == loads(dumps(C))132True133"""134if isinstance(data, Cube):135data = tuple(data)136new_data = []137nondegenerate = []138i = 0139for x in data:140if len(x) == 2:141try:142Integer(x[0])143except TypeError:144raise ValueError, "The interval %s is not of the correct form" % x145if x[0] + 1 == x[1]:146nondegenerate.append(i)147elif x[0] != x[1]:148raise ValueError, "The interval %s is not of the correct form" % x149new_data.append(tuple(x))150elif len(x) == 1:151y = tuple(x)152new_data.append(y+y)153elif len(x) != 1:154raise ValueError, "The interval %s is not of the correct form" % x155i += 1156self.__tuple = tuple(new_data)157self.__nondegenerate = nondegenerate158159def tuple(self):160"""161The tuple attached to this cube.162163EXAMPLES::164165sage: from sage.homology.cubical_complex import Cube166sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]])167sage: C.tuple()168((1, 2), (5, 5), (6, 7), (-1, 0))169"""170return self.__tuple171172def is_face(self, other):173"""174Return True iff this cube is a face of other.175176EXAMPLES::177178sage: from sage.homology.cubical_complex import Cube179sage: C1 = Cube([[1,2], [5,], [6,7], [-1, 0]])180sage: C2 = Cube([[1,2], [5,], [6,], [-1, 0]])181sage: C1.is_face(C2)182False183sage: C1.is_face(C1)184True185sage: C2.is_face(C1)186True187"""188def is_subinterval(i1, i2):189return ((i1[0] == i2[0] and i1[1] == i2[1]) or190(i1[0] == i2[0] and i1[1] == i2[0]) or191(i1[0] == i2[1] and i1[1] == i2[1]))192193t = self.tuple()194u = other.tuple()195embed = len(u)196if len(t) == embed: # these must be equal for self to be a face of other197return all([is_subinterval(t[i], u[i]) for i in range(embed)])198else:199return False200201def _translate(self, vec):202"""203Translate ``self`` by ``vec``.204205:param vec: anything which can be converted to a tuple of integers206:return: the translation of ``self`` by ``vec``207:rtype: Cube208209If ``vec`` is shorter than the list of intervals forming the210cube, pad with zeroes, and similarly if the cube's defining211tuple is too short.212213EXAMPLES::214215sage: from sage.homology.cubical_complex import Cube216sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]])217sage: C._translate((-12,))218[-11,-10] x [5,5] x [6,7] x [-1,0]219sage: C._translate((0, 0, 0, 0, 0, 5))220[1,2] x [5,5] x [6,7] x [-1,0] x [0,0] x [5,5]221"""222t = self.__tuple223embed = max(len(t), len(vec))224t = t + ((0,0),) * (embed-len(t))225vec = tuple(vec) + (0,) * (embed-len(vec))226new = []227for (a,b) in zip(t, vec):228new.append([a[0]+b, a[1]+b])229return Cube(new)230231def __getitem__(self, n):232"""233Return the nth interval in this cube.234235:param n: an integer236:return: tuple representing the `n`-th interval in the cube.237238EXAMPLES::239240sage: from sage.homology.cubical_complex import Cube241sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]])242sage: C[2]243(6, 7)244sage: C[1]245(5, 5)246"""247return self.__tuple.__getitem__(n)248249def __iter__(self):250"""251Iterator for the intervals of this cube.252253EXAMPLES::254255sage: from sage.homology.cubical_complex import Cube256sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]])257sage: [x[0] for x in C]258[1, 5, 6, -1]259"""260return self.__tuple.__iter__()261262def __add__(self, other):263"""264Cube obtained by concatenating the underlying tuples of the265two arguments.266267:param other: another cube268:return: the product of ``self`` and ``other``, as a Cube269270EXAMPLES::271272sage: from sage.homology.cubical_complex import Cube273sage: C = Cube([[1,2], [3,]])274sage: D = Cube([[4], [0,1]])275sage: C.product(D)276[1,2] x [3,3] x [4,4] x [0,1]277278You can also use ``__add__`` or ``+`` or ``__mul__`` or ``*``::279280sage: D * C281[4,4] x [0,1] x [1,2] x [3,3]282sage: D + C * C283[4,4] x [0,1] x [1,2] x [3,3] x [1,2] x [3,3]284"""285return Cube(self.__tuple.__add__(other.__tuple))286287# the __add__ operation actually produces the product of the two cubes288__mul__ = __add__289product = __add__290291def nondegenerate_intervals(self):292"""293The list of indices of nondegenerate intervals of this cube.294295EXAMPLES::296297sage: from sage.homology.cubical_complex import Cube298sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]])299sage: C.nondegenerate_intervals()300[0, 2, 3]301sage: C = Cube([[1,], [5,], [6,], [-1,]])302sage: C.nondegenerate_intervals()303[]304"""305return self.__nondegenerate306307def dimension(self):308"""309The dimension of this cube: the number of its nondegenerate intervals.310311EXAMPLES::312313sage: from sage.homology.cubical_complex import Cube314sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]])315sage: C.dimension()3163317sage: C = Cube([[1,], [5,], [6,], [-1,]])318sage: C.dimension()3190320sage: Cube([]).dimension() # empty cube has dimension -1321-1322"""323if len(self.__tuple) == 0: # empty cube324return -1325return len(self.nondegenerate_intervals())326327def face(self, n, upper=True):328"""329The nth primary face of this cube.330331:param n: an integer between 0 and one less than the dimension332of this cube333:param upper: if True, return the "upper" nth primary face;334otherwise, return the "lower" nth primary face.335:type upper: boolean; optional, default=True336:return: the cube obtained by replacing the nth non-degenrate337interval with either its upper or lower endpoint.338339EXAMPLES::340341sage: from sage.homology.cubical_complex import Cube342sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]]); C343[1,2] x [5,5] x [6,7] x [-1,0]344sage: C.face(0)345[2,2] x [5,5] x [6,7] x [-1,0]346sage: C.face(0, upper=False)347[1,1] x [5,5] x [6,7] x [-1,0]348sage: C.face(1)349[1,2] x [5,5] x [7,7] x [-1,0]350sage: C.face(2, upper=False)351[1,2] x [5,5] x [6,7] x [-1,-1]352sage: C.face(3)353Traceback (most recent call last):354...355ValueError: Can only compute the nth face if 0 <= n < dim.356"""357if n < 0 or n >= self.dimension():358raise ValueError, "Can only compute the nth face if 0 <= n < dim."359idx = self.nondegenerate_intervals()[n]360t = self.__tuple361if upper:362new = t[idx][1]363else:364new = t[idx][0]365return Cube(t[0:idx] + ((new,new),) + t[idx+1:])366367def faces(self):368"""369The list of faces (of codimension 1) of this cube.370371EXAMPLES::372373sage: from sage.homology.cubical_complex import Cube374sage: C = Cube([[1,2], [3,4]])375sage: C.faces()376[[2,2] x [3,4], [1,2] x [4,4], [1,1] x [3,4], [1,2] x [3,3]]377"""378upper = [self.face(i,True) for i in range(self.dimension())]379lower = [self.face(i,False) for i in range(self.dimension())]380return upper + lower381382def faces_as_pairs(self):383"""384The list of faces (of codimension 1) of this cube, as pairs385(upper, lower).386387EXAMPLES::388389sage: from sage.homology.cubical_complex import Cube390sage: C = Cube([[1,2], [3,4]])391sage: C.faces_as_pairs()392[([2,2] x [3,4], [1,1] x [3,4]), ([1,2] x [4,4], [1,2] x [3,3])]393"""394upper = [self.face(i,True) for i in range(self.dimension())]395lower = [self.face(i,False) for i in range(self.dimension())]396return zip(upper,lower)397398def _compare_for_gluing(self, other):399r"""400Given two cubes ``self`` and ``other``, describe how to401transform them so that they become equal.402403:param other: a cube of the same dimension as ``self``404:return: a triple ``(insert_self, insert_other, translate)``.405``insert_self`` is a tuple with entries ``(index, (list of406degenerate intervals))``. ``insert_other`` is similar.407``translate`` is a tuple of integers, suitable as a second408argument for the ``_translate`` method.409410To do this, ``self`` and ``other`` must have the same411dimension; degenerate intervals from ``other`` are added to412``self``, and vice versa. Intervals in ``other`` are413translated so that they coincide with the intervals in414``self``. The output is a triple, as noted above: in the415tuple ``insert_self``, an entry like ``(3, (3, 4, 0))`` means416that in position 3 in ``self``, insert the degenerate417intervals ``[3,3]``, ``[4,4]``, and ``[0,0]``. The same goes418for ``insert_other``. After applying the translations to the419cube ``other``, call ``_translate`` with argument the tuple420``translate``.421422This is used in forming connected sums of cubical complexes:423the two complexes are modified, via this method, so that they424have a cube which matches up, then those matching cubes are425removed.426427In the example below, this method is called with arguments428``C1`` and ``C2``, where429430.. math::431432C1 = [0,1] \times [3] \times [4] \times [6,7] \\433C2 = [2] \times [7,8] \times [9] \times [1,2] \times [0] \times [5]434435To C1, we need to add [2] in position 0 and [0] and [5] in436position 5. To C2, we need to add [4] in position 3. Once437this has been done, we need to translate the new C2 by the438vector ``(0, -7, -6, 0, 5, 0, 0)``.439440EXAMPLES::441442sage: from sage.homology.cubical_complex import Cube443sage: C1 = Cube([[0,1], [3], [4], [6,7]])444sage: C2 = Cube([[2], [7,8], [9], [1,2], [0], [5]])445sage: C1._compare_for_gluing(C2)446([(0, ((2, 2),)), (5, ((0, 0), (5, 5)))], [(3, ((4, 4),))], [0, -7, -6, 0, 5, 0, 0])447448sage: C1 = Cube([[1,1], [0,1]])449sage: C2 = Cube([[2,3], [4,4], [5,5]])450sage: C1._compare_for_gluing(C2)451([(2, ((4, 4), (5, 5)))], [(0, ((1, 1),))], [0, -2, 0, 0])452"""453d = self.dimension()454if d != other.dimension():455raise ValueError, "Cubes must be of the same dimension."456insert_self = []457insert_other = []458translate = []459self_tuple = self.tuple()460other_tuple = other.tuple()461nondegen = (zip(self.nondegenerate_intervals(),462other.nondegenerate_intervals())463+ [(len(self_tuple), len(other_tuple))])464old = (-1, -1)465self_added = 0466other_added = 0467468for current in nondegen:469# number of positions between nondegenerate intervals:470self_diff = current[0] - old[0]471other_diff = current[1] - old[1]472diff = self_diff - other_diff473474if diff < 0:475insert_self.append((old[0] + self_diff + self_added,476other.tuple()[current[1]+diff:current[1]]))477common_terms = self_diff478diff = -diff479self_added += diff480elif diff > 0:481insert_other.append((old[1] + other_diff + other_added,482self.tuple()[current[0]-diff:current[0]]))483common_terms = other_diff484other_added += diff485else:486common_terms = other_diff487488if old[0] > -1:489translate.extend([self_tuple[old[0]+idx][0] -490other_tuple[old[1]+idx][0] for idx in491range(common_terms)])492translate.extend(diff*[0])493old = current494495return (insert_self, insert_other, translate)496497def _triangulation_(self):498r"""499Triangulate this cube by "pulling vertices," as described by500Hetyei. Return a list of simplices which triangulate501``self``.502503ALGORITHM:504505If the cube is given by506507.. math::508509C = [i_1, j_1] \times [i_2, j_2] \times ... \times [i_k, j_k]510511let `v_1` be the "upper" corner of `C`: `v` is the point512`(j_1, ..., j_k)`. Choose a coordinate `n` where the interval513`[i_n, j_n]` is non-degenerate and form `v_2` by replacing514`j_n` by `i_n`; repeat to define `v_3`, etc. The last vertex515so defined will be `(i_1, ..., i_k)`. These vertices define a516simplex, as do the vertices obtained by making different517choices at each stage. Return the list of such simplices;518thus if `C` is `n`-dimensional, then it is subdivided into519`n!` simplices.520521REFERENCES:522523- G. Hetyei, "On the Stanley ring of a cubical complex",524Discrete Comput. Geom. 14 (1995), 305-330.525526EXAMPLES::527528sage: from sage.homology.cubical_complex import Cube529sage: C = Cube([[1,2], [3,4]]); C530[1,2] x [3,4]531sage: C._triangulation_()532[((1, 3), (1, 4), (2, 4)), ((1, 3), (2, 3), (2, 4))]533sage: C = Cube([[1,2], [3,4], [8,9]])534sage: len(C._triangulation_())5356536"""537from sage.homology.simplicial_complex import Simplex538if self.dimension() < 0: # the empty cube539return [Simplex(())] # the empty simplex540v = tuple([max(j) for j in self.tuple()])541if self.dimension() == 0: # just v542return [Simplex((v,))]543simplices = []544for i in range(self.dimension()):545for S in self.face(i, upper=False)._triangulation_():546simplices.append(S.join(Simplex((v,)), rename_vertices=False))547return simplices548549def __cmp__(self, other):550"""551Return True iff this cube is the same as ``other``: that is,552if they are the product of the same intervals in the same553order.554555:param other: another cube556557EXAMPLES::558559sage: from sage.homology.cubical_complex import Cube560sage: C1 = Cube([[1,1], [2,3], [4,5]])561sage: C2 = Cube([[1], [2,3], [4,5]])562sage: C3 = Cube([[0], [2,3], [4,5]])563sage: C1 == C2 # indirect doctest564True565sage: C1 == C3 # indirect doctest566False567"""568if self.__tuple == other.__tuple:569return 0570else:571return -1572573def __hash__(self):574"""575Hash value for this cube. This computes the hash value of the576underlying tuple, since this is what's important when testing577equality.578579EXAMPLES::580581sage: from sage.homology.cubical_complex import Cube582sage: C1 = Cube([[1,1], [2,3], [4,5]])583sage: C1.__hash__()584837272820736660832 # 64-bit585-1004989088 # 32-bit586"""587return hash(self.__tuple)588589def _repr_(self):590"""591Print representation of a cube.592593EXAMPLES::594595sage: from sage.homology.cubical_complex import Cube596sage: C1 = Cube([[1,1], [2,3], [4,5]])597sage: C1598[1,1] x [2,3] x [4,5]599sage: C1._repr_()600'[1,1] x [2,3] x [4,5]'601"""602s = ["[%s,%s]"%(str(x), str(y)) for (x,y) in self.__tuple]603return " x ".join(s)604605def _latex_(self):606r"""607LaTeX representation of a cube..608609EXAMPLES::610611sage: from sage.homology.cubical_complex import Cube612sage: C1 = Cube([[1,1], [2,3], [4,5]])613sage: latex(C1)614[1,1] \times [2,3] \times [4,5]615sage: C1._latex_()616'[1,1] \\times [2,3] \\times [4,5]'617"""618return self._repr_().replace('x', r'\times')619620621class CubicalComplex(GenericCellComplex):622r"""623Define a cubical complex.624625:param maximal_faces: set of maximal faces626:param maximality_check: see below627:type maximality_check: boolean; optional, default True628:return: a cubical complex629630``maximal_faces`` should be a list or tuple or set (or anything631which may be converted to a set) of "cubes": instances of the632class :class:`Cube`, or lists or tuples suitable for conversion to633cubes. These cubes are the maximal cubes in the complex.634635In addition, ``maximal_faces`` may be a cubical complex, in which636case that complex is returned. Also, ``maximal_faces`` may637instead be any object which has a ``_cubical_`` method (e.g., a638simplicial complex); then that method is used to convert the639object to a cubical complex.640641If ``maximality_check`` is True, check that each maximal face is,642in fact, maximal. In this case, when producing the internal643representation of the cubical complex, omit those that are not.644It is highly recommended that this be True; various methods for645this class may fail if faces which are claimed to be maximal are646in fact not.647648EXAMPLES:649650The empty complex, consisting of one cube, the empty cube::651652sage: CubicalComplex()653Cubical complex with 0 vertices and 1 cube654655A "circle" (four edges connecting the vertices (0,2), (0,3),656(1,2), and (1,3))::657658sage: S1 = CubicalComplex([([0,0], [2,3]), ([0,1], [3,3]), ([0,1], [2,2]), ([1,1], [2,3])])659sage: S1660Cubical complex with 4 vertices and 8 cubes661sage: S1.homology()662{0: 0, 1: Z}663664A set of five points and its product with ``S1``::665666sage: pts = CubicalComplex([([0],), ([3],), ([6],), ([-12],), ([5],)])667sage: pts668Cubical complex with 5 vertices and 5 cubes669sage: pts.homology()670{0: Z x Z x Z x Z}671sage: X = S1.product(pts); X672Cubical complex with 20 vertices and 40 cubes673sage: X.homology()674{0: Z x Z x Z x Z, 1: Z^5}675676Converting a simplicial complex to a cubical complex::677678sage: S2 = simplicial_complexes.Sphere(2)679sage: C2 = CubicalComplex(S2)680sage: all([C2.homology(n) == S2.homology(n) for n in range(3)])681True682683You can get the set of maximal cells or a dictionary of all cells::684685sage: X.maximal_cells()686{[0,0] x [2,3] x [-12,-12], [0,1] x [3,3] x [5,5], [0,1] x [2,2] x [3,3], [0,1] x [2,2] x [0,0], [0,1] x [3,3] x [6,6], [1,1] x [2,3] x [0,0], [0,1] x [2,2] x [-12,-12], [0,0] x [2,3] x [6,6], [1,1] x [2,3] x [-12,-12], [1,1] x [2,3] x [5,5], [0,1] x [2,2] x [5,5], [0,1] x [3,3] x [3,3], [1,1] x [2,3] x [3,3], [0,0] x [2,3] x [5,5], [0,1] x [3,3] x [0,0], [1,1] x [2,3] x [6,6], [0,1] x [2,2] x [6,6], [0,0] x [2,3] x [0,0], [0,0] x [2,3] x [3,3], [0,1] x [3,3] x [-12,-12]}687sage: S1.cells()688{0: set([[1,1] x [2,2], [0,0] x [2,2], [1,1] x [3,3], [0,0] x [3,3]]), 1: set([[0,1] x [3,3], [1,1] x [2,3], [0,0] x [2,3], [0,1] x [2,2]]), -1: set([])}689690Chain complexes, homology, and cohomology::691692sage: T = S1.product(S1); T693Cubical complex with 16 vertices and 64 cubes694sage: T.chain_complex()695Chain complex with at most 3 nonzero terms over Integer Ring696sage: T.homology(base_ring=QQ)697{0: Vector space of dimension 0 over Rational Field,6981: Vector space of dimension 2 over Rational Field,6992: Vector space of dimension 1 over Rational Field}700sage: RP2 = cubical_complexes.RealProjectivePlane()701sage: RP2.cohomology(dim=[1, 2], base_ring=GF(2))702{1: Vector space of dimension 1 over Finite Field of size 2,7032: Vector space of dimension 1 over Finite Field of size 2}704705Joins are not implemented::706707sage: S1.join(S1)708Traceback (most recent call last):709...710NotImplementedError: Joins are not implemented for cubical complexes.711712Therefore, neither are cones or suspensions.713"""714def __init__(self, maximal_faces=[], **kwds):715r"""716Define a cubical complex. See ``CubicalComplex`` for more717documentation.718719EXAMPLES::720721sage: X = CubicalComplex([([0,0], [2,3]), ([0,1], [3,3]), ([0,1], [2,2]), ([1,1], [2,3])]); X722Cubical complex with 4 vertices and 8 cubes723sage: X == loads(dumps(X))724True725"""726maximality_check = kwds.get('maximality_check', True)727728C = None729if isinstance(maximal_faces, CubicalComplex):730C = maximal_faces731try:732C = maximal_faces._cubical_()733except AttributeError:734pass735if C is not None:736self._facets = copy(C._facets)737self._cells = copy(C._cells)738self._complex = copy(C._complex)739return740741good_faces = []742maximal_cubes = [Cube(f) for f in maximal_faces]743for face in maximal_cubes:744# check whether each given face is actually maximal745face_is_maximal = True746if maximality_check:747faces_to_be_removed = []748for other in good_faces:749if other.is_face(face):750faces_to_be_removed.append(other)751elif face_is_maximal:752face_is_maximal = not face.is_face(other)753for x in faces_to_be_removed:754good_faces.remove(x)755if face_is_maximal:756good_faces += [face]757# if no maximal faces, add the empty face as a facet758if len(maximal_cubes) == 0:759good_faces.append(Cube(()))760# self._facets: tuple of facets761self._facets = tuple(good_faces)762# self._cells: dictionary of dictionaries of faces. The main763# dictionary is keyed by subcomplexes, and each value is a764# dictionary keyed by dimension. This should be empty until765# needed -- that is, until the faces method is called766self._cells = {}767# self._complex: dictionary indexed by dimension d, base_ring,768# etc.: differential from dim d to dim d-1 in the associated769# chain complex. thus to get the differential in the cochain770# complex from dim d-1 to dim d, take the transpose of this771# one.772self._complex = {}773774def maximal_cells(self):775"""776The set of maximal cells (with respect to inclusion) of this777cubical complex.778779:return: Set of maximal cells780781This just returns the set of cubes used in defining the782cubical complex, so if the complex was defined with no783maximality checking, none is done here, either.784785EXAMPLES::786787sage: interval = cubical_complexes.Cube(1)788sage: interval789Cubical complex with 2 vertices and 3 cubes790sage: interval.maximal_cells()791{[0,1]}792sage: interval.product(interval).maximal_cells()793{[0,1] x [0,1]}794"""795return Set(self._facets)796797def __cmp__(self,other):798r"""799Return True if the set of maximal cells is the same for800``self`` and ``other``.801802:param other: another cubical complex803:return: True if the set of maximal cells is the same for ``self`` and ``other``804:rtype: bool805806EXAMPLES::807808sage: I1 = cubical_complexes.Cube(1)809sage: I2 = cubical_complexes.Cube(1)810sage: I1.product(I2) == I2.product(I1)811True812sage: I1.product(I2.product(I2)) == I2.product(I1.product(I1))813True814sage: S1 = cubical_complexes.Sphere(1)815sage: I1.product(S1) == S1.product(I1)816False817"""818if self.maximal_cells() == other.maximal_cells():819return 0820else:821return -1822823def is_subcomplex(self, other):824r"""825Return True if ``self`` is a subcomplex of ``other``.826827:param other: a cubical complex828829Each maximal cube of ``self`` must be a face of a maximal cube830of ``other`` for this to be True.831832EXAMPLES::833834sage: S1 = cubical_complexes.Sphere(1)835sage: C0 = cubical_complexes.Cube(0)836sage: C1 = cubical_complexes.Cube(1)837sage: cyl = S1.product(C1)838sage: end = S1.product(C0)839sage: end.is_subcomplex(cyl)840True841sage: cyl.is_subcomplex(end)842False843844The embedding of the cubical complex is important here::845846sage: C2 = cubical_complexes.Cube(2)847sage: C1.is_subcomplex(C2)848False849sage: C1.product(C0).is_subcomplex(C2)850True851852``C1`` is not a subcomplex of ``C2`` because it's not embedded853in `\RR^2`. On the other hand, ``C1 x C0`` is a face of854``C2``. Look at their maximal cells::855856sage: C1.maximal_cells()857{[0,1]}858sage: C2.maximal_cells()859{[0,1] x [0,1]}860sage: C1.product(C0).maximal_cells()861{[0,1] x [0,0]}862"""863other_facets = other.maximal_cells()864answer = True865for cube in self.maximal_cells():866answer = answer and any([cube.is_face(other_cube)867for other_cube in other_facets])868return answer869870def cells(self, subcomplex=None):871"""872The cells of this cubical complex, in the form of a dictionary:873the keys are integers, representing dimension, and the value874associated to an integer d is the list of d-cells.875876If the optional argument ``subcomplex`` is present, then877return only the faces which are *not* in the subcomplex.878879:param subcomplex: a subcomplex of this cubical complex880:type subcomplex: a cubical complex; optional, default None881:return: cells of this complex not contained in ``subcomplex``882:rtype: dictionary883884EXAMPLES::885886sage: S2 = cubical_complexes.Sphere(2)887sage: S2.cells()[2]888set([[1,1] x [0,1] x [0,1], [0,1] x [0,0] x [0,1], [0,1] x [1,1] x [0,1], [0,0] x [0,1] x [0,1], [0,1] x [0,1] x [1,1], [0,1] x [0,1] x [0,0]])889"""890if subcomplex not in self._cells:891if subcomplex is not None and subcomplex.dimension() > -1:892if not subcomplex.is_subcomplex(self):893raise ValueError, "The 'subcomplex' is not actually a subcomplex."894# Cells is the dictionary of cells in self but not in895# subcomplex, indexed by dimension896Cells = {}897# sub_facets is the dictionary of facets in the subcomplex898sub_facets = {}899dimension = max([cube.dimension() for cube in self._facets])900# initialize the lists: add each maximal cube to Cells and sub_facets901for i in range(-1,dimension+1):902Cells[i] = set([])903sub_facets[i] = set([])904for f in self._facets:905Cells[f.dimension()].add(f)906if subcomplex is not None:907for g in subcomplex._facets:908dim = g.dimension()909Cells[dim].discard(g)910sub_facets[dim].add(g)911# bad_faces is the set of faces in the subcomplex in the912# current dimension913bad_faces = sub_facets[dimension]914for dim in range(dimension, -1, -1):915# bad_bdries = boundaries of bad_faces: things to be916# discarded in dim-1917bad_bdries = sub_facets[dim-1]918for f in bad_faces:919bad_bdries.update(f.faces())920for f in Cells[dim]:921Cells[dim-1].update(set(f.faces()).difference(bad_bdries))922bad_faces = bad_bdries923self._cells[subcomplex] = Cells924return self._cells[subcomplex]925926def n_cubes(self, n, subcomplex=None):927"""928The set of cubes of dimension n of this cubical complex.929If the optional argument ``subcomplex`` is present, then930return the ``n``-dimensional cubes which are *not* in the931subcomplex.932933:param n: dimension934:type n: integer935:param subcomplex: a subcomplex of this cubical complex936:type subcomplex: a cubical complex; optional, default None937:return: cells in dimension ``n``938:rtype: set939940EXAMPLES::941942sage: C = cubical_complexes.Cube(3)943sage: C.n_cubes(3)944set([[0,1] x [0,1] x [0,1]])945sage: C.n_cubes(2)946set([[1,1] x [0,1] x [0,1], [0,1] x [0,0] x [0,1], [0,1] x [1,1] x [0,1], [0,0] x [0,1] x [0,1], [0,1] x [0,1] x [1,1], [0,1] x [0,1] x [0,0]])947"""948return set(self.n_cells(n, subcomplex))949950def chain_complex(self, **kwds):951r"""952The chain complex associated to this cubical complex.953954:param dimensions: if None, compute the chain complex in all955dimensions. If a list or tuple of integers, compute the956chain complex in those dimensions, setting the chain groups957in all other dimensions to zero. NOT IMPLEMENTED YET: this958function always returns the entire chain complex959:param base_ring: commutative ring960:type base_ring: optional, default ZZ961:param subcomplex: a subcomplex of this cubical complex.962Compute the chain complex relative to this subcomplex.963:type subcomplex: optional, default empty964:param augmented: If True, return the augmented chain complex965(that is, include a class in dimension `-1` corresponding966to the empty cell). This is ignored if ``dimensions`` is967specified.968:type augmented: boolean; optional, default False969:param cochain: If True, return the cochain complex (that is,970the dual of the chain complex).971:type cochain: boolean; optional, default False972:param verbose: If True, print some messages as the chain973complex is computed.974:type verbose: boolean; optional, default False975:param check_diffs: If True, make sure that the chain complex976is actually a chain complex: the differentials are977composable and their product is zero.978:type check_diffs: boolean; optional, default False979980.. note::981982If subcomplex is nonempty, then the argument ``augmented``983has no effect: the chain complex relative to a nonempty984subcomplex is zero in dimension `-1`.985986EXAMPLES::987988sage: S2 = cubical_complexes.Sphere(2)989sage: S2.chain_complex()990Chain complex with at most 3 nonzero terms over Integer Ring991sage: Prod = S2.product(S2); Prod992Cubical complex with 64 vertices and 676 cubes993sage: Prod.chain_complex()994Chain complex with at most 5 nonzero terms over Integer Ring995sage: Prod.chain_complex(base_ring=QQ)996Chain complex with at most 5 nonzero terms over Rational Field997sage: C1 = cubical_complexes.Cube(1)998sage: S0 = cubical_complexes.Sphere(0)999sage: C1.chain_complex(subcomplex=S0)1000Chain complex with at most 1 nonzero terms over Integer Ring1001sage: C1.homology(subcomplex=S0)1002{0: 0, 1: Z}1003"""1004augmented = kwds.get('augmented', False)1005cochain = kwds.get('cochain', False)1006verbose = kwds.get('verbose', False)1007check_diffs = kwds.get('check_diffs', False)1008base_ring = kwds.get('base_ring', ZZ)1009dimensions = kwds.get('dimensions', None)1010subcomplex = kwds.get('subcomplex', None)10111012# initialize subcomplex1013if subcomplex is None:1014subcomplex = CubicalComplex()1015else:1016# subcomplex is not empty, so don't augment the chain complex1017augmented = False1018differentials = {}1019if augmented:1020empty_cell = 1 # number of (-1)-dimensional cubes1021else:1022empty_cell = 01023vertices = self.n_cubes(0, subcomplex=subcomplex)1024n = len(vertices)1025mat = matrix(base_ring, empty_cell, n, n*empty_cell*[1])1026if cochain:1027differentials[-1] = mat.transpose()1028else:1029differentials[0] = mat1030current = vertices1031# now loop from 1 to dimension of the complex1032for dim in range(1,self.dimension()+1):1033if verbose:1034print " starting dimension %s" % dim1035if (dim, subcomplex) in self._complex:1036if cochain:1037differentials[dim-1] = self._complex[(dim, subcomplex)].transpose().change_ring(base_ring)1038mat = differentials[dim-1]1039else:1040differentials[dim] = self._complex[(dim, subcomplex)].change_ring(base_ring)1041mat = differentials[dim]1042if verbose:1043print " boundary matrix (cached): it's %s by %s." % (mat.nrows(), mat.ncols())1044else:1045# 'current' is the list of cells in dimension n1046#1047# 'old' is a dictionary, with keys the cells in the1048# previous dimension, values the integers 0, 1, 2,1049# ... (the index of the face). finding an entry in a1050# dictionary seems to be faster than finding the index1051# of an entry in a list.1052old = dict(zip(current, range(len(current))))1053current = list(self.n_cubes(dim, subcomplex=subcomplex))1054# construct matrix. it is easiest to construct it as1055# a sparse matrix, specifying which entries are1056# nonzero via a dictionary.1057matrix_data = {}1058col = 01059if len(old) and len(current):1060for cube in current:1061faces = cube.faces_as_pairs()1062sign = 11063for (upper, lower) in faces:1064try:1065matrix_data[(old[upper], col)] = sign1066sign *= -11067matrix_data[(old[lower], col)] = sign1068except KeyError:1069pass1070col += 11071mat = matrix(ZZ, len(old), len(current), matrix_data)1072self._complex[(dim, subcomplex)] = mat1073if cochain:1074differentials[dim-1] = mat.transpose().change_ring(base_ring)1075else:1076differentials[dim] = mat.change_ring(base_ring)1077if verbose:1078print " boundary matrix computed: it's %s by %s." % (mat.nrows(), mat.ncols())1079# finally, return the chain complex1080if cochain:1081return ChainComplex(data=differentials, base_ring=base_ring,1082degree=1, check=check_diffs)1083else:1084return ChainComplex(data=differentials, base_ring=base_ring,1085degree=-1, check=check_diffs)10861087def n_skeleton(self, n):1088r"""1089The n-skeleton of this cubical complex.10901091:param n: dimension1092:type n: non-negative integer1093:return: cubical complex10941095EXAMPLES::10961097sage: S2 = cubical_complexes.Sphere(2)1098sage: C3 = cubical_complexes.Cube(3)1099sage: S2 == C3.n_skeleton(2)1100True1101"""1102if n >= self.dimension():1103return self1104else:1105data = []1106for d in range(n+1):1107data.extend(list(self.cells()[d]))1108return CubicalComplex(data)11091110def graph(self):1111"""1112The 1-skeleton of this cubical complex, as a graph.11131114EXAMPLES::11151116sage: cubical_complexes.Sphere(2).graph()1117Graph on 8 vertices1118"""1119data = {}1120vertex_dict = {}1121i = 01122for vertex in self.n_cells(0):1123vertex_dict[vertex] = i1124data[i] = []1125i += 11126for edge in self.n_cells(1):1127start = edge.face(0, False)1128end = edge.face(0, True)1129data[vertex_dict[start]].append(vertex_dict[end])1130return Graph(data)11311132def is_pure(self):1133"""1134True iff this cubical complex is pure: that is,1135all of its maximal faces have the same dimension.11361137.. warning::11381139This may give the wrong answer if the cubical complex1140was constructed with ``maximality_check`` set to False.11411142EXAMPLES::11431144sage: S4 = cubical_complexes.Sphere(4)1145sage: S4.is_pure()1146True1147sage: C = CubicalComplex([([0,0], [3,3]), ([1,2], [4,5])])1148sage: C.is_pure()1149False1150"""1151dims = [face.dimension() for face in self._facets]1152return max(dims) == min(dims)11531154def join(self, other):1155r"""1156The join of this cubical complex with another one.11571158NOT IMPLEMENTED.11591160:param other: another cubical complex11611162EXAMPLES::11631164sage: C1 = cubical_complexes.Cube(1)1165sage: C1.join(C1)1166Traceback (most recent call last):1167...1168NotImplementedError: Joins are not implemented for cubical complexes.1169"""1170raise NotImplementedError, "Joins are not implemented for cubical complexes."11711172# Use * to mean 'join':1173# __mul__ = join11741175def cone(self):1176r"""1177The cone on this cubical complex.11781179NOT IMPLEMENTED11801181The cone is the complex formed by taking the join of the1182original complex with a one-point complex (that is, a11830-dimensional cube). Since joins are not implemented for1184cubical complexes, neither are cones.11851186EXAMPLES::11871188sage: C1 = cubical_complexes.Cube(1)1189sage: C1.cone()1190Traceback (most recent call last):1191...1192NotImplementedError: Cones are not implemented for cubical complexes.1193"""1194#return self.join(cubical_complexes.Cube(0))1195raise NotImplementedError, "Cones are not implemented for cubical complexes."11961197def suspension(self, n=1):1198r"""1199The suspension of this cubical complex.12001201NOT IMPLEMENTED12021203:param n: suspend this many times1204:type n: positive integer; optional, default 112051206The suspension is the complex formed by taking the join of the1207original complex with a two-point complex (the 0-sphere).1208Since joins are not implemented for cubical complexes, neither1209are suspensions.12101211EXAMPLES::12121213sage: C1 = cubical_complexes.Cube(1)1214sage: C1.suspension()1215Traceback (most recent call last):1216...1217NotImplementedError: Suspensions are not implemented for cubical complexes.1218"""1219# if n<0:1220# raise ValueError, "n must be non-negative."1221# if n==0:1222# return self1223# if n==1:1224# return self.join(cubical_complexes.Sphere(0))1225# return self.suspension().suspension(int(n-1))1226raise NotImplementedError, "Suspensions are not implemented for cubical complexes."12271228def product(self, other):1229r"""1230The product of this cubical complex with another one.12311232:param other: another cubical complex12331234EXAMPLES::12351236sage: RP2 = cubical_complexes.RealProjectivePlane()1237sage: S1 = cubical_complexes.Sphere(1)1238sage: RP2.product(S1).homology()[1]1239Z x C21240"""1241facets = []1242for f in self._facets:1243for g in other._facets:1244facets.append(f.product(g))1245return CubicalComplex(facets)12461247def disjoint_union(self, other):1248"""1249The disjoint union of this cubical complex with another one.12501251:param right: the other cubical complex (the right-hand factor)12521253Algorithm: first embed both complexes in d-dimensional1254Euclidean space. Then embed in (1+d)-dimensional space,1255calling the new axis `x`, and putting the first complex at1256`x=0`, the second at `x=1`.12571258EXAMPLES::12591260sage: S1 = cubical_complexes.Sphere(1)1261sage: S2 = cubical_complexes.Sphere(2)1262sage: S1.disjoint_union(S2).homology()1263{0: Z, 1: Z, 2: Z}1264"""1265embedded_left = len(tuple(self.maximal_cells()[0]))1266embedded_right = len(tuple(other.maximal_cells()[0]))1267zero = [0] * max(embedded_left, embedded_right)1268facets = []1269for f in self.maximal_cells():1270facets.append(Cube([[0,0]]).product(f._translate(zero)))1271for f in other.maximal_cells():1272facets.append(Cube([[1,1]]).product(f._translate(zero)))1273return CubicalComplex(facets)12741275def wedge(self, other):1276"""1277The wedge (one-point union) of this cubical complex with1278another one.12791280:param right: the other cubical complex (the right-hand factor)12811282Algorithm: if ``self`` is embedded in `d` dimensions and1283``other`` in `n` dimensions, embed them in `d+n` dimensions:1284``self`` using the first `d` coordinates, ``other`` using the1285last `n`, translating them so that they have the origin as a1286common vertex.12871288.. note::12891290This operation is not well-defined if ``self`` or1291``other`` is not path-connected.12921293EXAMPLES::12941295sage: S1 = cubical_complexes.Sphere(1)1296sage: S2 = cubical_complexes.Sphere(2)1297sage: S1.wedge(S2).homology()1298{0: 0, 1: Z, 2: Z}1299"""1300embedded_left = len(tuple(self.maximal_cells()[0]))1301embedded_right = len(tuple(other.maximal_cells()[0]))1302translate_left = [-a[0] for a in self.maximal_cells()[0]] + [0] * embedded_right1303translate_right = [-a[0] for a in other.maximal_cells()[0]]1304point_right = Cube([[0,0]] * embedded_left)13051306facets = []1307for f in self.maximal_cells():1308facets.append(f._translate(translate_left))1309for f in other.maximal_cells():1310facets.append(point_right.product(f._translate(translate_right)))1311return CubicalComplex(facets)13121313def connected_sum(self, other):1314"""1315Return the connected sum of self with other.13161317:param other: another cubical complex1318:return: the connected sum ``self # other``13191320.. warning::13211322This does not check that self and other are manifolds, only1323that their facets all have the same dimension. Since a1324(more or less) random facet is chosen from each complex and1325then glued together, this method may return random1326results if applied to non-manifolds, depending on which1327facet is chosen.13281329EXAMPLES::13301331sage: T = cubical_complexes.Torus()1332sage: S2 = cubical_complexes.Sphere(2)1333sage: T.connected_sum(S2).cohomology() == T.cohomology()1334True1335sage: RP2 = cubical_complexes.RealProjectivePlane()1336sage: T.connected_sum(RP2).homology(1)1337Z x Z x C21338sage: RP2.connected_sum(RP2).connected_sum(RP2).homology(1)1339Z x Z x C21340"""1341# connected_sum: first check whether the complexes are pure1342# and of the same dimension. Then insert degenerate intervals1343# and translate them so that they have a common cube C. Add one1344# more dimension, embedding the first complex as (..., 0) and1345# the second as (..., 1). Keep all of the other facets, but remove1346# C x 0 and C x 1, putting in its place (its boundary) x (0,1).1347if not (self.is_pure() and other.is_pure() and1348self.dimension() == other.dimension()):1349raise ValueError, "Complexes are not pure of the same dimension."13501351self_facets = list(self.maximal_cells())1352other_facets = list(other.maximal_cells())13531354C1 = self_facets.pop()1355C2 = other_facets.pop()1356(insert_self, insert_other, translate) = C1._compare_for_gluing(C2)13571358CL = list(C1.tuple())1359for (idx, L) in insert_self:1360CL[idx:idx] = L1361removed = Cube(CL)13621363# start assembling the facets in the connected sum: first, the1364# cylinder on the removed face.1365new_facets = []1366cylinder = removed.product(Cube([[0,1]]))1367# don't want to include the ends of the cylinder, so don't1368# include the last pair of faces. therefore, choose faces up1369# to removed.dimension(), not cylinder.dimension().1370for n in range(removed.dimension()):1371new_facets.append(cylinder.face(n, upper=False))1372new_facets.append(cylinder.face(n, upper=True))13731374for cube in self_facets:1375CL = list(cube.tuple())1376for (idx, L) in insert_self:1377CL[idx:idx] = L1378CL.append((0,0))1379new_facets.append(Cube(CL))1380for cube in other_facets:1381CL = list(cube.tuple())1382for (idx, L) in insert_other:1383CL[idx:idx] = L1384CL.append((1,1))1385new_facets.append(Cube(CL)._translate(translate))1386return CubicalComplex(new_facets)13871388def _translate(self, vec):1389"""1390Translate ``self`` by ``vec``.13911392:param vec: anything which can be converted to a tuple of integers1393:return: the translation of ``self`` by ``vec``1394:rtype: cubical complex13951396If ``vec`` is shorter than the list of intervals forming the1397complex, pad with zeroes, and similarly if the complexes1398defining tuples are too short.13991400EXAMPLES::14011402sage: C1 = cubical_complexes.Cube(1)1403sage: C1.maximal_cells()1404{[0,1]}1405sage: C1._translate([2,6]).maximal_cells()1406{[2,3] x [6,6]}1407"""1408return CubicalComplex([f._translate(vec) for f in self.maximal_cells()])14091410def _chomp_repr_(self):1411r"""1412String representation of self suitable for use by the CHomP1413program. This lists each maximal cube on its own line.14141415EXAMPLES::14161417sage: C = cubical_complexes.Cube(0).product(cubical_complexes.Cube(2))1418sage: C.maximal_cells()1419{[0,0] x [0,1] x [0,1]}1420sage: C._chomp_repr_()1421'[0,0] x [0,1] x [0,1]\n'1422"""1423s = ""1424for c in self.maximal_cells():1425s += str(c)1426s += "\n"1427return s14281429def _simplicial_(self):1430r"""1431Simplicial complex constructed from self.14321433ALGORITHM:14341435This is constructed as described by Hetyei: choose a total1436ordering of the vertices of the cubical complex. Then for1437each maximal face14381439.. math::14401441C = [i_1, j_1] \times [i_2, j_2] \times ... \times [i_k, j_k]14421443let `v_1` be the "upper" corner of `C`: `v` is the point1444`(j_1, ..., j_k)`. Choose a coordinate `n` where the interval1445`[i_n, j_n]` is non-degenerate and form `v_2` by replacing1446`j_n` by `i_n`; repeat to define `v_3`, etc. The last vertex1447so defined will be `(i_1, ..., i_k)`. These vertices define a1448simplex, and do the vertices obtained by making different1449choices at each stage. Thus each `n`-cube is subdivided into1450`n!` simplices.14511452REFERENCES:14531454- G. Hetyei, "On the Stanley ring of a cubical complex",1455Discrete Comput. Geom. 14 (1995), 305-330.14561457EXAMPLES::14581459sage: T = cubical_complexes.Torus(); T1460Cubical complex with 16 vertices and 64 cubes1461sage: len(T.maximal_cells())14621614631464When this is triangulated, each maximal 2-dimensional cube1465gets turned into a pair of triangles. Since there were 161466maximal cubes, this results in 32 facets in the simplicial1467complex::14681469sage: Ts = T._simplicial_(); Ts1470Simplicial complex with 16 vertices and 32 facets1471sage: T.homology() == Ts.homology()1472True14731474Each `n`-dimensional cube produces `n!` `n`-simplices::14751476sage: S4 = cubical_complexes.Sphere(4)1477sage: len(S4.maximal_cells())1478101479sage: SimplicialComplex(S4) # calls S4._simplicial_()1480Simplicial complex with 32 vertices and 240 facets1481"""1482from sage.homology.simplicial_complex import SimplicialComplex1483simplices = []1484for C in self.maximal_cells():1485simplices.extend(C._triangulation_())1486return SimplicialComplex(simplices)14871488def _string_constants(self):1489"""1490Tuple containing the name of the type of complex, and the1491singular and plural of the name of the cells from which it is1492built. This is used in constructing the string representation.14931494EXAMPLES::14951496sage: S3 = cubical_complexes.Sphere(3)1497sage: S3._string_constants()1498('Cubical', 'cube', 'cubes')1499sage: S3._repr_() # indirect doctest1500'Cubical complex with 16 vertices and 80 cubes'1501"""1502return ('Cubical', 'cube', 'cubes')150315041505class CubicalComplexExamples():1506r"""1507Some examples of cubical complexes.15081509Here are the available examples; you can also type1510"cubical_complexes." and hit TAB to get a list::15111512Sphere1513Torus1514RealProjectivePlane1515KleinBottle1516SurfaceOfGenus1517Cube15181519EXAMPLES::15201521sage: cubical_complexes.Torus() # indirect doctest1522Cubical complex with 16 vertices and 64 cubes1523sage: cubical_complexes.Cube(7)1524Cubical complex with 128 vertices and 2187 cubes1525sage: cubical_complexes.Sphere(7)1526Cubical complex with 256 vertices and 6560 cubes1527"""15281529def Sphere(self,n):1530r"""1531A cubical complex representation of the `n`-dimensional sphere,1532formed by taking the boundary of an `(n+1)`-dimensional cube.15331534:param n: the dimension of the sphere1535:type n: non-negative integer15361537EXAMPLES::15381539sage: cubical_complexes.Sphere(7)1540Cubical complex with 256 vertices and 6560 cubes1541"""1542return CubicalComplex(Cube([[0,1]]*(n+1)).faces())15431544def Torus(self):1545r"""1546A cubical complex representation of the torus, obtained by1547taking the product of the circle with itself.15481549EXAMPLES::15501551sage: cubical_complexes.Torus()1552Cubical complex with 16 vertices and 64 cubes1553"""1554S1 = cubical_complexes.Sphere(1)1555return S1.product(S1)15561557def RealProjectivePlane(self):1558r"""1559A cubical complex representation of the real projective plane.1560This is taken from the examples from CHomP, the Computational1561Homology Project: http://chomp.rutgers.edu/.15621563EXAMPLES::15641565sage: cubical_complexes.RealProjectivePlane()1566Cubical complex with 21 vertices and 81 cubes1567"""1568return CubicalComplex([1569([0, 1], [0], [0], [0, 1], [0]),1570([0, 1], [0], [0], [0], [0, 1]),1571([0], [0, 1], [0, 1], [0], [0]),1572([0], [0, 1], [0], [0, 1], [0]),1573([0], [0], [0, 1], [0], [0, 1]),1574([0, 1], [0, 1], [1], [0], [0]),1575([0, 1], [1], [0, 1], [0], [0]),1576([1], [0, 1], [0, 1], [0], [0]),1577([0, 1], [0, 1], [0], [0], [1]),1578([0, 1], [1], [0], [0], [0, 1]),1579([1], [0, 1], [0], [0], [0, 1]),1580([0, 1], [0], [0, 1], [1], [0]),1581([0, 1], [0], [1], [0, 1], [0]),1582([1], [0], [0, 1], [0, 1], [0]),1583([0], [0, 1], [0], [0, 1], [1]),1584([0], [0, 1], [0], [1], [0, 1]),1585([0], [1], [0], [0, 1], [0, 1]),1586([0], [0], [0, 1], [0, 1], [1]),1587([0], [0], [0, 1], [1], [0, 1]),1588([0], [0], [1], [0, 1], [0, 1])])15891590def KleinBottle(self):1591r"""1592A cubical complex representation of the Klein bottle, formed1593by taking the connected sum of the real projective plane with1594itself.15951596EXAMPLES::15971598sage: cubical_complexes.KleinBottle()1599Cubical complex with 42 vertices and 168 cubes1600"""1601RP2 = cubical_complexes.RealProjectivePlane()1602return RP2.connected_sum(RP2)16031604def SurfaceOfGenus(self, g, orientable=True):1605"""1606A surface of genus g as a cubical complex.16071608:param g: the genus1609:type g: non-negative integer1610:param orientable: whether the surface should be orientable1611:type orientable: bool, optional, default True16121613In the orientable case, return a sphere if `g` is zero, and1614otherwise return a `g`-fold connected sum of a torus with1615itself.16161617In the non-orientable case, raise an error if `g` is zero. If1618`g` is positive, return a `g`-fold connected sum of a1619real projective plane with itself.16201621EXAMPLES::16221623sage: cubical_complexes.SurfaceOfGenus(2)1624Cubical complex with 32 vertices and 134 cubes1625sage: cubical_complexes.SurfaceOfGenus(1, orientable=False)1626Cubical complex with 21 vertices and 81 cubes1627"""1628try:1629g = Integer(g)1630except TypeError:1631raise ValueError("genus must be a non-negative integer")1632if g < 0:1633raise ValueError("genus must be a non-negative integer")1634if g == 0:1635if not orientable:1636raise ValueError("no non-orientable surface of genus zero")1637else:1638return cubical_complexes.Sphere(2)1639if orientable:1640T = cubical_complexes.Torus()1641else:1642T = cubical_complexes.RealProjectivePlane()1643S = T1644for i in range(g-1):1645S = S.connected_sum(T)1646return S16471648def Cube(self, n):1649r"""1650A cubical complex representation of an `n`-dimensional cube.16511652:param n: the dimension1653:type n: non-negative integer16541655EXAMPLES::16561657sage: cubical_complexes.Cube(0)1658Cubical complex with 1 vertex and 1 cube1659sage: cubical_complexes.Cube(3)1660Cubical complex with 8 vertices and 27 cubes1661"""1662if n == 0:1663return CubicalComplex([Cube([[0]])])1664else:1665return CubicalComplex([Cube([[0,1]]*n)])16661667cubical_complexes = CubicalComplexExamples()166816691670