# HG changeset patch
# User J. H. Palmieri
# Date 1275517397 25200
# Node ID 7993564c6cb70b65d2f637b7017566e585d7f094
# Parent 3a18981812ffcad0bfbba8c9823ad3005f81ce86
trac 9122: conversions between simplicial and cubical complexes
diff -r 3a18981812ff -r 7993564c6cb7 sage/homology/cubical_complex.py
--- a/sage/homology/cubical_complex.py Tue Jun 01 15:10:17 2010 -0700
+++ b/sage/homology/cubical_complex.py Wed Jun 02 15:23:17 2010 -0700
@@ -497,6 +497,58 @@
return (insert_self, insert_other, translate)
+ def _triangulation_(self):
+ r"""
+ Triangulate this cube by "pulling vertices," as described by
+ Hetyei. Return a list of simplices which triangulate
+ ``self``.
+
+ ALGORITHM:
+
+ If the cube is given by
+
+ .. math::
+
+ C = [i_1, j_1] \times [i_2, j_2] \times ... \times [i_k, j_k]
+
+ let `v_1` be the "upper" corner of `C`: `v` is the point
+ `(j_1, ..., j_k)`. Choose a coordinate `n` where the interval
+ `[i_n, j_n]` is non-degenerate and form `v_2` by replacing
+ `j_n` by `i_n`; repeat to define `v_3`, etc. The last vertex
+ so defined will be `(i_1, ..., i_k)`. These vertices define a
+ simplex, as do the vertices obtained by making different
+ choices at each stage. Return the list of such simplices;
+ thus if `C` is `n`-dimensional, then it is subdivided into
+ `n!` simplices.
+
+ REFERENCES:
+
+ - G. Hetyei, "On the Stanley ring of a cubical complex",
+ Discrete Comput. Geom. 14 (1995), 305-330.
+
+ EXAMPLES::
+
+ sage: from sage.homology.cubical_complex import Cube
+ sage: C = Cube([[1,2], [3,4]]); C
+ [1,2] x [3,4]
+ sage: C._triangulation_()
+ [((1, 3), (1, 4), (2, 4)), ((1, 3), (2, 3), (2, 4))]
+ sage: C = Cube([[1,2], [3,4], [8,9]])
+ sage: len(C._triangulation_())
+ 6
+ """
+ from sage.homology.simplicial_complex import Simplex
+ if self.dimension() < 0: # the empty cube
+ return [Simplex(())] # the empty simplex
+ v = tuple([max(j) for j in self.tuple()])
+ if self.dimension() == 0: # just v
+ return [Simplex((v,))]
+ simplices = []
+ for i in range(self.dimension()):
+ for S in self.face(i, upper=False)._triangulation_():
+ simplices.append(S.join(Simplex((v,)), rename_vertices=False))
+ return simplices
+
def __cmp__(self, other):
"""
Return True iff this cube is the same as ``other``: that is,
@@ -583,6 +635,12 @@
class :class:`Cube`, or lists or tuples suitable for conversion to
cubes. These cubes are the maximal cubes in the complex.
+ In addition, ``maximal_faces`` may be a cubical complex, in which
+ case that complex is returned. Also, ``maximal_faces`` may
+ instead be any object which has a ``_cubical_`` method (e.g., a
+ simplicial complex); then that method is used to convert the
+ object to a cubical complex.
+
If ``maximality_check`` is True, check that each maximal face is,
in fact, maximal. In this case, when producing the internal
representation of the cubical complex, omit those that are not.
@@ -618,6 +676,13 @@
sage: X.homology()
{0: Z x Z x Z x Z, 1: Z^5}
+ Converting a simplicial complex to a cubical complex::
+
+ sage: S2 = simplicial_complexes.Sphere(2)
+ sage: C2 = CubicalComplex(S2)
+ sage: all([C2.homology(n) == S2.homology(n) for n in range(3)])
+ True
+
You can get the set of maximal cells or a dictionary of all cells::
sage: X.maximal_cells()
@@ -663,6 +728,19 @@
"""
maximality_check = kwds.get('maximality_check', True)
+ C = None
+ if isinstance(maximal_faces, CubicalComplex):
+ C = maximal_faces
+ try:
+ C = maximal_faces._cubical_()
+ except AttributeError:
+ pass
+ if C is not None:
+ self._facets = copy(C._facets)
+ self._cells = copy(C._cells)
+ self._complex = copy(C._complex)
+ return
+
good_faces = []
maximal_cubes = [Cube(f) for f in maximal_faces]
for face in maximal_cubes:
@@ -1353,6 +1431,65 @@
s += "\n"
return s
+ def _simplicial_(self):
+ r"""
+ Simplicial complex constructed from self.
+
+ ALGORITHM:
+
+ This is constructed as described by Hetyei: choose a total
+ ordering of the vertices of the cubical complex. Then for
+ each maximal face
+
+ .. math::
+
+ C = [i_1, j_1] \times [i_2, j_2] \times ... \times [i_k, j_k]
+
+ let `v_1` be the "upper" corner of `C`: `v` is the point
+ `(j_1, ..., j_k)`. Choose a coordinate `n` where the interval
+ `[i_n, j_n]` is non-degenerate and form `v_2` by replacing
+ `j_n` by `i_n`; repeat to define `v_3`, etc. The last vertex
+ so defined will be `(i_1, ..., i_k)`. These vertices define a
+ simplex, and do the vertices obtained by making different
+ choices at each stage. Thus each `n`-cube is subdivided into
+ `n!` simplices.
+
+ REFERENCES:
+
+ - G. Hetyei, "On the Stanley ring of a cubical complex",
+ Discrete Comput. Geom. 14 (1995), 305-330.
+
+ EXAMPLES::
+
+ sage: T = cubical_complexes.Torus(); T
+ Cubical complex with 16 vertices and 64 cubes
+ sage: len(T.maximal_cells())
+ 16
+
+ When this is triangulated, each maximal 2-dimensional cube
+ gets turned into a pair of triangles. Since there were 16
+ maximal cubes, this results in 32 facets in the simplicial
+ complex::
+
+ sage: Ts = T._simplicial_(); Ts
+ Simplicial complex with 16 vertices and 32 facets
+ sage: T.homology() == Ts.homology()
+ True
+
+ Each `n`-dimensional cube produces `n!` `n`-simplices::
+
+ sage: S4 = cubical_complexes.Sphere(4)
+ sage: len(S4.maximal_cells())
+ 10
+ sage: SimplicialComplex(S4) # calls S4._simplicial_()
+ Simplicial complex with 32 vertices and 240 facets
+ """
+ from sage.homology.simplicial_complex import SimplicialComplex
+ simplices = []
+ for C in self.maximal_cells():
+ simplices.extend(C._triangulation_())
+ return SimplicialComplex(simplices)
+
def _string_constants(self):
"""
Tuple containing the name of the type of complex, and the
diff -r 3a18981812ff -r 7993564c6cb7 sage/homology/simplicial_complex.py
--- a/sage/homology/simplicial_complex.py Tue Jun 01 15:10:17 2010 -0700
+++ b/sage/homology/simplicial_complex.py Wed Jun 02 15:23:17 2010 -0700
@@ -103,6 +103,7 @@
# should + have any meaning?
# cohomology: compute cup products (and Massey products?)
+from copy import copy
from sage.homology.cell_complex import GenericCellComplex
from sage.structure.sage_object import SageObject
from sage.rings.integer import Integer
@@ -713,6 +714,22 @@
sage: SimplicialComplex([[0,2], [0,3], [0,6]])
Simplicial complex with vertex set (0, 2, 3, 6) and facets {(0, 6), (0, 2), (0, 3)}
+ Finally, if ``vertex_set`` is the only argument and it is a
+ simplicial complex, return that complex. If it is an object with
+ a built-in conversion to simplicial complexes (via a
+ ``_simplicial_`` method), then the resulting simplicial complex is
+ returned::
+
+ sage: S = SimplicialComplex([[0,2], [0,3], [0,6]])
+ sage: SimplicialComplex(S) == S
+ True
+ sage: Tc = cubical_complexes.Torus(); Tc
+ Cubical complex with 16 vertices and 64 cubes
+ sage: Ts = SimplicialComplex(Tc); Ts
+ Simplicial complex with 16 vertices and 32 facets
+ sage: Ts.homology()
+ {0: 0, 1: Z x Z, 2: Z}
+
TESTS::
sage: S = SimplicialComplex(['a', 'b', 'c'], (('a', 'b'), ('a', 'c'), ('b', 'c')))
@@ -745,9 +762,30 @@
# 'maximal_faces', and use the union of all of the vertices for
# 'vertex_set'.
if maximal_faces == []:
+ C = None
if isinstance(vertex_set, (list, tuple)) and isinstance(vertex_set[0], (list, tuple, Simplex)):
maximal_faces = vertex_set
vertex_set = reduce(union, vertex_set)
+ elif isinstance(vertex_set, SimplicialComplex):
+ C = vertex_set
+ else:
+ try:
+ C = vertex_set._simplicial_()
+ except AttributeError:
+ pass
+ if C is not None:
+ self._vertex_set = copy(C.vertices())
+ self._facets = list(C.facets())
+ self._faces = copy(C._faces)
+ self._gen_dict = copy(C._gen_dict)
+ self._complex = copy(C._complex)
+ self.__contractible = copy(C.__contractible)
+ self.__enlarged = copy(C.__enlarged)
+ self._graph = copy(C._graph)
+ self._numeric = C._numeric
+ self._numeric_translation = copy(C._numeric_translation)
+ return
+
if sort_facets:
try: # vertex_set is an iterable
vertices = Simplex(sorted(vertex_set))
@@ -2392,6 +2430,80 @@
vertex_check=False, sort_facets=False)
self.__enlarged[subcomplex] = L
return L
+
+ def _cubical_(self):
+ r"""
+ Cubical complex constructed from self.
+
+ ALGORITHM:
+
+ The algorithm comes from a paper by Shtan'ko and Shtogrin, as
+ reported by Bukhshtaber and Panov. Let `I^m` denote the unit
+ `m`-cube, viewed as a cubical complex. Let `[m] = \{1, 2,
+ ..., m\}`; then each face of `I^m` has the following form, for
+ subsets `I \subset J \subset [m]`:
+
+ .. math::
+
+ F_{I \subset J} = \{ (y_1,...,y_m) \in I^m \,:\, y_i =0 \text{
+ for } i \in I, y_j = 1 \text{ for } j \not \in J\}.
+
+ If `K` is a simplicial complex on vertex set `[m]` and if `I
+ \subset [m]`, write `I \in K` if `I` is a simplex of `K`.
+ Then we associate to `K` the cubical subcomplex of `I^m` with
+ faces
+
+ .. math::
+
+ \{F_{I \subset J} \,:\, J \in K, I \neq \emptyset \}
+
+ The geometric realization of this cubical complex is
+ homeomorphic to the geometric realization of the original
+ simplicial complex.
+
+ REFERENCES:
+
+ - V. M. Bukhshtaber and T. E. Panov, "Moment-angle complexes
+ and combinatorics of simplicial manifolds," *Uspekhi
+ Mat. Nauk* 55 (2000), 171--172.
+
+ - M. A. Shtan'ko and and M. I. Shtogrin, "Embedding cubic
+ manifolds and complexes into a cubic lattice", *Uspekhi
+ Mat. Nauk* 47 (1992), 219-220.
+
+ EXAMPLES::
+
+ sage: T = simplicial_complexes.Torus()
+ sage: T.homology()
+ {0: 0, 1: Z x Z, 2: Z}
+ sage: Tc = T._cubical_()
+ sage: Tc
+ Cubical complex with 42 vertices and 168 cubes
+ sage: Tc.homology()
+ {0: 0, 1: Z x Z, 2: Z}
+ """
+ from sage.homology.cubical_complex import Cube, CubicalComplex
+ V = self.vertices()
+ embed = V.dimension() + 1
+ # dictionary to translate vertices to the numbers 1, ..., embed
+ vd = dict(zip(V, range(1, embed + 1)))
+ cubes = []
+ for JJ in self.facets():
+ J = [vd[i] for i in JJ]
+ for i in J:
+ # loop over indices from 1 to embed. if equal to i,
+ # set to 0. if not in J, set to 1. Otherwise, range
+ # from 0 to 1
+ cube = []
+ for n in range(1, embed+1):
+ if n == i:
+ cube.append([0,])
+ elif n not in J:
+ cube.append([1,])
+ else:
+ cube.append([0,1])
+ cubes.append(cube)
+ return CubicalComplex(cubes)
def category(self):
"""