Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/homology/simplicial_complex.py
4079 views
1
r"""
2
Finite simplicial complexes
3
4
AUTHORS:
5
6
- John H. Palmieri (2009-04)
7
8
- D. Benjamin Antieau (2009-06) - added is_connected, generated_subcomplex,
9
remove_facet, and is_flag_complex methods;
10
cached the output of the graph() method.
11
12
This module implements the basic structure of finite simplicial
13
complexes. Given a set `V` of "vertices", a simplicial complex on `V`
14
is a collection `K` of subsets of `V` satisfying the condition that if
15
`S` is one of the subsets in `K`, then so is every subset of `S`. The
16
subsets `S` are called the 'simplices' of `K`.
17
18
A simplicial complex `K` can be viewed as a purely combinatorial
19
object, as described above, but it also gives rise to a topological
20
space `|K|` (its *geometric realization*) as follows: first, the
21
points of `V` should be in general position in euclidean space. Next,
22
if `\{v\}` is in `K`, then the vertex `v` is in `|K|`. If `\{v, w\}`
23
is in `K`, then the line segment from `v` to `w` is in `|K|`. If `\{u,
24
v, w\}` is in `K`, then the triangle with vertices `u`, `v`, and `w`
25
is in `|K|`. In general, `|K|` is the union of the convex hulls of
26
simplices of `K`. Frequently, one abuses notation and uses `K` to
27
denote both the simplicial complex and the associated topological
28
space.
29
30
.. image:: ../../media/homology/simplices.png
31
32
For any simplicial complex `K` and any commutative ring `R` there is
33
an associated chain complex, with differential of degree `-1`. The
34
`n^{th}` term is the free `R`-module with basis given by the
35
`n`-simplices of `K`. The differential is determined by its value on
36
any simplex: on the `n`-simplex with vertices `(v_0, v_1, ..., v_n)`,
37
the differential is the alternating sum with `i^{th}` summand `(-1)^i`
38
multiplied by the `(n-1)`-simplex obtained by omitting vertex `v_i`.
39
40
In the implementation here, the vertex set must be finite. To define a
41
simplicial complex, specify its vertex set: this should be a list,
42
tuple, or set, or it can be a non-negative integer `n`, in which case
43
the vertex set is `(0, ..., n)`. Also specify the facets: the maximal
44
faces.
45
46
.. note::
47
48
The elements of the vertex set are not automatically contained in
49
the simplicial complex: each one is only included if and only if it
50
is a vertex of at least one of the specified facets.
51
52
.. note::
53
54
This class derives from
55
:class:`~sage.homology.cell_complex.GenericCellComplex`, and so
56
inherits its methods. Some of those methods are not listed here;
57
see the :mod:`Generic Cell Complex <sage.homology.cell_complex>`
58
page instead.
59
60
EXAMPLES::
61
62
sage: SimplicialComplex([1, 3, 7], [[1], [3, 7]])
63
Simplicial complex with vertex set (1, 3, 7) and facets {(3, 7), (1,)}
64
sage: SimplicialComplex(2) # the empty simplicial complex
65
Simplicial complex with vertex set (0, 1, 2) and facets {()}
66
sage: X = SimplicialComplex(3, [[0,1], [1,2], [2,3], [3,0]])
67
sage: X
68
Simplicial complex with vertex set (0, 1, 2, 3) and facets {(1, 2), (2, 3), (0, 3), (0, 1)}
69
sage: X.stanley_reisner_ring()
70
Quotient of Multivariate Polynomial Ring in x0, x1, x2, x3 over Integer Ring by the ideal (x1*x3, x0*x2)
71
sage: X.is_pure()
72
True
73
74
Sage can perform a number of operations on simplicial complexes, such
75
as the join and the product, and it can also compute homology::
76
77
sage: S = SimplicialComplex(2, [[0,1], [1,2], [0,2]]) # circle
78
sage: T = S.product(S) # torus
79
sage: T
80
Simplicial complex with 9 vertices and 18 facets
81
sage: T.homology() # this computes reduced homology
82
{0: 0, 1: Z x Z, 2: Z}
83
sage: T.euler_characteristic()
84
0
85
86
Sage knows about some basic combinatorial data associated to a
87
simplicial complex::
88
89
sage: X = SimplicialComplex(3, [[0,1], [1,2], [2,3], [0,3]])
90
sage: X.f_vector()
91
[1, 4, 4]
92
sage: X.face_poset()
93
Finite poset containing 8 elements
94
sage: X.stanley_reisner_ring()
95
Quotient of Multivariate Polynomial Ring in x0, x1, x2, x3 over Integer Ring by the ideal (x1*x3, x0*x2)
96
"""
97
98
# possible future directions for SimplicialComplex:
99
#
100
# make compatible with GAP (see http://linalg.org/gap.html)
101
# compare to and make compatible with polymake
102
# see Macaulay: http://www.math.uiuc.edu/Macaulay2/doc/Macaulay2-1.1/share/doc/Macaulay2/SimplicialComplexes/html/___Simplicial__Complex.html; compare performance and make compatible
103
# should + have any meaning?
104
# cohomology: compute cup products (and Massey products?)
105
106
from copy import copy
107
from sage.homology.cell_complex import GenericCellComplex
108
from sage.structure.sage_object import SageObject
109
from sage.rings.integer import Integer
110
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
111
from sage.sets.set import Set
112
from sage.rings.integer_ring import ZZ
113
from sage.structure.parent_gens import normalize_names
114
from sage.misc.latex import latex
115
from sage.matrix.constructor import matrix
116
from sage.homology.chain_complex import ChainComplex
117
from sage.graphs.graph import Graph
118
119
def lattice_paths(t1, t2, length=None):
120
"""
121
Given lists (or tuples or ...) ``t1`` and ``t2``, think of them as
122
labelings for vertices: ``t1`` labeling points on the x-axis,
123
``t2`` labeling points on the y-axis, both increasing. Return the
124
list of rectilinear paths along the grid defined by these points
125
in the plane, starting from ``(t1[0], t2[0])``, ending at
126
``(t1[last], t2[last])``, and at each grid point, going either
127
right or up. See the examples.
128
129
:param t1: labeling for vertices
130
:param t2: labeling for vertices
131
:param length: if not None, then an integer, the length of the desired path.
132
:type length: integer or None; optional, default None
133
:type t1: tuple, list, other iterable
134
:type t2: tuple, list, other iterable
135
:return: list of lists of vertices making up the paths as described above
136
:rtype: list of lists
137
138
This is used when triangulating the product of simplices. The
139
optional argument ``length`` is used for `\Delta`-complexes, to
140
specify all simplices in a product: in the triangulation of a
141
product of two simplices, there is a `d`-simplex for every path of
142
length `d+1` in the lattice. The path must start at the bottom
143
left and end at the upper right, and it must use at least one
144
point in each row and in each column, so if ``length`` is too
145
small, there will be no paths.
146
147
EXAMPLES::
148
149
sage: from sage.homology.simplicial_complex import lattice_paths
150
sage: lattice_paths([0,1,2], [0,1,2])
151
[[(0, 0), (0, 1), (0, 2), (1, 2), (2, 2)],
152
[(0, 0), (0, 1), (1, 1), (1, 2), (2, 2)],
153
[(0, 0), (1, 0), (1, 1), (1, 2), (2, 2)],
154
[(0, 0), (0, 1), (1, 1), (2, 1), (2, 2)],
155
[(0, 0), (1, 0), (1, 1), (2, 1), (2, 2)],
156
[(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)]]
157
sage: lattice_paths(('a', 'b', 'c'), (0, 3, 5))
158
[[('a', 0), ('a', 3), ('a', 5), ('b', 5), ('c', 5)],
159
[('a', 0), ('a', 3), ('b', 3), ('b', 5), ('c', 5)],
160
[('a', 0), ('b', 0), ('b', 3), ('b', 5), ('c', 5)],
161
[('a', 0), ('a', 3), ('b', 3), ('c', 3), ('c', 5)],
162
[('a', 0), ('b', 0), ('b', 3), ('c', 3), ('c', 5)],
163
[('a', 0), ('b', 0), ('c', 0), ('c', 3), ('c', 5)]]
164
sage: lattice_paths(range(3), range(3), length=2)
165
[]
166
sage: lattice_paths(range(3), range(3), length=3)
167
[[(0, 0), (1, 1), (2, 2)]]
168
sage: lattice_paths(range(3), range(3), length=4)
169
[[(0, 0), (1, 1), (1, 2), (2, 2)],
170
[(0, 0), (0, 1), (1, 2), (2, 2)],
171
[(0, 0), (1, 1), (2, 1), (2, 2)],
172
[(0, 0), (1, 0), (2, 1), (2, 2)],
173
[(0, 0), (0, 1), (1, 1), (2, 2)],
174
[(0, 0), (1, 0), (1, 1), (2, 2)]]
175
"""
176
if length is None:
177
# 0 x n (or k x 0) rectangle:
178
if len(t1) == 0 or len(t2) == 0:
179
return [[]]
180
# 1 x n (or k x 1) rectangle:
181
elif len(t1) == 1:
182
return [[(t1[0], w) for w in t2]]
183
elif len(t2) == 1:
184
return [[(v, t2[0]) for v in t1]]
185
else:
186
# recursive: paths in rectangle with either one fewer row
187
# or column, plus the upper right corner
188
return ([path + [(t1[-1], t2[-1])] for path
189
in lattice_paths(t1[:-1], t2)] +
190
[path + [(t1[-1], t2[-1])] for path
191
in lattice_paths(t1, t2[:-1])])
192
else:
193
if length > len(t1) + len(t2) - 1:
194
return []
195
# as above, except make sure that lengths are correct. if
196
# not, return an empty list.
197
#
198
# 0 x n (or k x 0) rectangle:
199
elif len(t1) == 0 or len(t2) == 0:
200
if length == 0:
201
return [[]]
202
else:
203
return []
204
# 1 x n (or k x 1) rectangle:
205
elif len(t1) == 1:
206
if length == len(t2):
207
return [[(t1[0], w) for w in t2]]
208
else:
209
return []
210
elif len(t2) == 1:
211
if length == len(t1):
212
return [[(v, t2[0]) for v in t1]]
213
else:
214
return []
215
else:
216
# recursive: paths of length one fewer in rectangle with
217
# either one fewer row, one fewer column, or one fewer of
218
# each, and then plus the upper right corner
219
return ([path + [(t1[-1], t2[-1])] for path
220
in lattice_paths(t1[:-1], t2, length=length-1)] +
221
[path + [(t1[-1], t2[-1])] for path
222
in lattice_paths(t1, t2[:-1], length=length-1)] +
223
[path + [(t1[-1], t2[-1])] for path
224
in lattice_paths(t1[:-1], t2[:-1], length=length-1)])
225
226
def rename_vertex(n, keep, left = True):
227
"""
228
Rename a vertex: the vertices from the list 'keep' get
229
relabeled 0, 1, 2, ..., in order. Any other vertex (e.g. 4) gets
230
renamed to by prepending an 'L' or an 'R' (thus to either 'L4' or
231
'R4'), depending on whether the argument left is True or False.
232
233
:param n: a 'vertex': either an integer or a string
234
:param keep: a list of three vertices
235
:param left: if True, rename for use in left factor
236
:type left: boolean; optional, default True
237
238
This is used by the ``connected_sum`` method for simplicial
239
complexes.
240
241
EXAMPLES::
242
243
sage: from sage.homology.simplicial_complex import rename_vertex
244
sage: rename_vertex(6, [5, 6, 7])
245
1
246
sage: rename_vertex(3, [5, 6, 7, 8, 9])
247
'L3'
248
sage: rename_vertex(3, [5, 6, 7], left=False)
249
'R3'
250
"""
251
lookup = dict(zip(keep, range(len(keep))))
252
try:
253
return lookup[n]
254
except:
255
if left:
256
return "L" + str(n)
257
else:
258
return "R" + str(n)
259
260
class Simplex(SageObject):
261
"""
262
Define a simplex.
263
264
Topologically, a simplex is the convex hull of a collection of
265
vertices in general position. Combinatorially, it is defined just
266
by specifying a set of vertices. It is represented in Sage by the
267
tuple of the vertices.
268
269
:param X: set of vertices
270
:type X: integer or list, tuple, or other iterable
271
:return: simplex with those vertices
272
273
``X`` may be a non-negative integer `n`, in which case the
274
simplicial complex will have `n+1` vertices `(0, 1, ..., n)`, or
275
it may be anything which may be converted to a tuple, in which
276
case the vertices will be that tuple. In the second case, each
277
vertex must be hashable, so it should be a number, a string, or a
278
tuple, for instance, but not a list.
279
280
.. warning::
281
282
The vertices should be distinct, and no error checking is done
283
to make sure this is the case.
284
285
EXAMPLES::
286
287
sage: Simplex(4)
288
(0, 1, 2, 3, 4)
289
sage: Simplex([3, 4, 1])
290
(3, 4, 1)
291
sage: X = Simplex((3, 'a', 'vertex')); X
292
(3, 'a', 'vertex')
293
sage: X == loads(dumps(X))
294
True
295
296
Vertices may be tuples but not lists::
297
298
sage: Simplex([(1,2), (3,4)])
299
((1, 2), (3, 4))
300
sage: Simplex([[1,2], [3,4]])
301
Traceback (most recent call last):
302
...
303
TypeError: unhashable type: 'list'
304
"""
305
306
def __init__(self, X):
307
"""
308
Define a simplex. See ``Simplex`` for full documentation.
309
310
EXAMPLES::
311
312
sage: Simplex(2)
313
(0, 1, 2)
314
sage: Simplex(('a', 'b', 'c'))
315
('a', 'b', 'c')
316
"""
317
try:
318
N = int(X) + 1
319
self.__tuple = tuple(range(N))
320
except TypeError:
321
self.__tuple = tuple(X)
322
self.__set = frozenset(self.__tuple)
323
324
def tuple(self):
325
"""
326
The tuple attached to this simplex.
327
328
EXAMPLES::
329
330
sage: Simplex(3).tuple()
331
(0, 1, 2, 3)
332
333
Although simplices are printed as if they were tuples, they
334
are not the same type::
335
336
sage: type(Simplex(3).tuple())
337
<type 'tuple'>
338
sage: type(Simplex(3))
339
<class 'sage.homology.simplicial_complex.Simplex'>
340
"""
341
return self.__tuple
342
343
def set(self):
344
"""
345
The frozenset attached to this simplex.
346
347
EXAMPLES::
348
349
sage: Simplex(3).set()
350
frozenset([0, 1, 2, 3])
351
"""
352
return self.__set
353
354
def is_face(self, other):
355
"""
356
Return True iff this simplex is a face of other.
357
358
EXAMPLES::
359
360
sage: Simplex(3).is_face(Simplex(5))
361
True
362
sage: Simplex(5).is_face(Simplex(2))
363
False
364
sage: Simplex(['a', 'b', 'c']).is_face(Simplex(8))
365
False
366
"""
367
return self.__set.issubset(other.__set)
368
369
def __contains__(self, x):
370
"""
371
Return True iff ``x`` is a vertex of this simplex.
372
373
EXAMPLES::
374
375
sage: 3 in Simplex(5)
376
True
377
sage: 3 in Simplex(2)
378
False
379
"""
380
return self.__set.__contains__(x)
381
382
def __getitem__(self, n):
383
"""
384
Return the nth vertex in this simplex.
385
386
EXAMPLES::
387
388
sage: Simplex(5)[2]
389
2
390
sage: Simplex(['a', 'b', 'c'])[1]
391
'b'
392
"""
393
return self.__tuple.__getitem__(n)
394
395
def __iter__(self):
396
"""
397
Iterator for the vertices of this simplex.
398
399
EXAMPLES::
400
401
sage: [v**2 for v in Simplex(3)]
402
[0, 1, 4, 9]
403
"""
404
return self.__tuple.__iter__()
405
406
def __add__(self, other):
407
"""
408
Simplex obtained by concatenating the underlying tuples of the
409
two arguments.
410
411
:param other: another simplex
412
413
EXAMPLES::
414
415
sage: Simplex((1,2,3)) + Simplex((5,6))
416
(1, 2, 3, 5, 6)
417
"""
418
return Simplex(self.__tuple.__add__(other.__tuple))
419
420
def face(self, n):
421
"""
422
The nth face of this simplex.
423
424
:param n: an integer between 0 and the dimension of this simplex
425
:type n: integer
426
:return: the simplex obtained by removing the nth vertex from this simplex
427
428
EXAMPLES::
429
430
sage: S = Simplex(4)
431
sage: S.face(0)
432
(1, 2, 3, 4)
433
sage: S.face(3)
434
(0, 1, 2, 4)
435
"""
436
if n >= 0 and n <= self.dimension():
437
return Simplex(self.__tuple[:n] + self.__tuple[n+1:])
438
else:
439
raise IndexError, "%s does not have an nth face for n=%s." % (self, n)
440
441
def faces(self):
442
"""
443
The list of faces (of codimension 1) of this simplex.
444
445
EXAMPLES::
446
447
sage: S = Simplex(4)
448
sage: S.faces()
449
[(1, 2, 3, 4), (0, 2, 3, 4), (0, 1, 3, 4), (0, 1, 2, 4), (0, 1, 2, 3)]
450
sage: len(Simplex(10).faces())
451
11
452
"""
453
return [self.face(i) for i in range(self.dimension()+1)]
454
455
def dimension(self):
456
"""
457
The dimension of this simplex: the number of vertices minus 1.
458
459
EXAMPLES::
460
461
sage: Simplex(5).dimension() == 5
462
True
463
sage: Simplex(5).face(1).dimension()
464
4
465
"""
466
return len(self.__tuple) - 1
467
468
def is_empty(self):
469
"""
470
Return True iff this simplex is the empty simplex.
471
472
EXAMPLES::
473
474
sage: [Simplex(n).is_empty() for n in range(-1,4)]
475
[True, False, False, False, False]
476
"""
477
return self.dimension() < 0
478
479
def join(self, right, rename_vertices=True):
480
"""
481
The join of this simplex with another one.
482
483
The join of two simplices `[v_0, ..., v_k]` and `[w_0, ...,
484
w_n]` is the simplex `[v_0, ..., v_k, w_0, ..., w_n]`.
485
486
:param right: the other simplex (the right-hand factor)
487
488
:param rename_vertices: If this is True, the vertices in the
489
join will be renamed by this formula: vertex "v" in the
490
left-hand factor --> vertex "Lv" in the join, vertex "w"
491
in the right-hand factor --> vertex "Rw" in the join. If
492
this is false, this tries to construct the join without
493
renaming the vertices; this may cause problems if the two
494
factors have any vertices with names in common.
495
496
:type rename_vertices: boolean; optional, default True
497
498
EXAMPLES::
499
500
sage: Simplex(2).join(Simplex(3))
501
('L0', 'L1', 'L2', 'R0', 'R1', 'R2', 'R3')
502
sage: Simplex(['a', 'b']).join(Simplex(['x', 'y', 'z']))
503
('La', 'Lb', 'Rx', 'Ry', 'Rz')
504
sage: Simplex(['a', 'b']).join(Simplex(['x', 'y', 'z']), rename_vertices=False)
505
('a', 'b', 'x', 'y', 'z')
506
"""
507
if rename_vertices:
508
vertex_set = (["L" + str(v) for v in self]
509
+ ["R" + str(w) for w in right])
510
else:
511
vertex_set = self.__tuple + right.__tuple
512
return Simplex(vertex_set)
513
514
def product(self, other, rename_vertices=True):
515
"""
516
The product of this simplex with another one, as a list of simplices.
517
518
:param other: the other simplex
519
520
:param rename_vertices: If this is False, then the vertices in
521
the product are the set of ordered pairs `(v,w)` where `v`
522
is a vertex in the left-hand factor (``self``) and `w` is
523
a vertex in the right-hand factor (``other``). If this is
524
True, then the vertices are renamed as "LvRw" (e.g., the
525
vertex (1,2) would become "L1R2"). This is useful if you
526
want to define the Stanley-Reisner ring of the complex:
527
vertex names like (0,1) are not suitable for that, while
528
vertex names like "L0R1" are.
529
530
:type rename-vertices: boolean; optional, default True
531
532
Algorithm: see Hatcher, p. 277-278 (who in turn refers to
533
Eilenberg-Steenrod, p. 68): given ``Simplex(m)`` and
534
``Simplex(n)``, then ``Simplex(m)`` x ``Simplex(n)`` can be
535
triangulated as follows: for each path `f` from `(0,0)` to
536
`(m,n)` along the integer grid in the plane, going up or right
537
at each lattice point, associate an `(m+n)`-simplex with
538
vertices `v_0`, `v_1`, ..., where `v_k` is the `k^{th}` vertex
539
in the path `f`.
540
541
Note that there are `m+n` choose `n` such paths. Note also
542
that each vertex in the product is a pair of vertices `(v,w)`
543
where `v` is a vertex in the left-hand factor and `w`
544
is a vertex in the right-hand factor.
545
546
.. note::
547
548
This produces a list of simplices -- not a ``Simplex``, not
549
a ``SimplicialComplex``.
550
551
EXAMPLES::
552
553
sage: len(Simplex(2).product(Simplex(2)))
554
6
555
sage: Simplex(1).product(Simplex(1))
556
[('L0R0', 'L0R1', 'L1R1'), ('L0R0', 'L1R0', 'L1R1')]
557
sage: Simplex(1).product(Simplex(1), rename_vertices=False)
558
[((0, 0), (0, 1), (1, 1)), ((0, 0), (1, 0), (1, 1))]
559
560
REFERENCES:
561
562
- A. Hatcher, "Algebraic Topology", Cambridge University Press
563
(2002).
564
"""
565
if not rename_vertices:
566
return [Simplex(x) for x in lattice_paths(self.tuple(), other.tuple())]
567
568
answer = []
569
for x in lattice_paths(self.tuple(), other.tuple()):
570
new = tuple(["L" + str(v) + "R" + str(w) for (v,w) in x])
571
answer.append(Simplex(new))
572
return answer
573
574
575
def __cmp__(self, other):
576
"""
577
Return True iff this simplex is the same as ``other``: that
578
is, if the vertices of the two are the same, even with a
579
different ordering
580
581
:param other: the other simplex
582
583
EXAMPLES::
584
585
sage: Simplex([0,1,2]) == Simplex([0,2,1])
586
True
587
sage: Simplex([0,1,2]) == Simplex(['a','b','c'])
588
False
589
sage: Simplex([1]) < Simplex([2])
590
True
591
sage: Simplex([1]) > Simplex([2])
592
False
593
"""
594
if not isinstance(other, Simplex):
595
return -1
596
if self.__set == other.__set:
597
return 0
598
return cmp(sorted(tuple(self.__set)), sorted(tuple(other.__set)))
599
600
def __hash__(self):
601
"""
602
Hash value for this simplex. This computes the hash value of
603
the Python frozenset of the underlying tuple, since this is
604
what's important when testing equality.
605
606
EXAMPLES::
607
608
sage: Simplex([1,2,0]).__hash__() == Simplex(2).__hash__()
609
True
610
sage: Simplex([1,2,0,1,1,2]).__hash__() == Simplex(2).__hash__()
611
True
612
"""
613
return hash(self.__set)
614
615
def _repr_(self):
616
"""
617
Print representation.
618
619
EXAMPLES::
620
621
sage: S = Simplex(5)
622
sage: S._repr_()
623
'(0, 1, 2, 3, 4, 5)'
624
"""
625
return self.__tuple.__repr__()
626
627
def _latex_(self):
628
r"""
629
LaTeX representation.
630
631
EXAMPLES::
632
633
sage: Simplex(3)._latex_()
634
\left(0,
635
1,
636
2,
637
3\right)
638
"""
639
return latex(self.__tuple)
640
641
642
class SimplicialComplex(GenericCellComplex):
643
r"""
644
Define a simplicial complex.
645
646
:param vertex_set: set of vertices
647
:param maximal_faces: set of maximal faces
648
:param vertex_check: see below
649
:type vertex_check: boolean; optional, default True
650
:param maximality_check: see below
651
:type maximality_check: boolean; optional, default True
652
:param sort_facets: see below
653
:type sort_facets: boolean; optional, default True
654
:param name_check: see below
655
:type name_check: boolean; optional, default False
656
:return: a simplicial complex
657
658
``vertex_set`` may be a non-negative integer `n` (in which case
659
the simplicial complex will have `n+1` vertices `\{0, 1, ...,
660
n\}`), or it may be anything which may be converted to a tuple.
661
Call the elements of this 'vertices'.
662
663
``maximal_faces`` should be a list or tuple or set (indeed,
664
anything which may be converted to a set) whose elements are lists
665
(or tuples, etc.) of vertices. Maximal faces are also known as
666
'facets'.
667
668
If ``vertex_check`` is True, check to see that each given maximal
669
face is a subset of the vertex set. Raise an error for any bad
670
face.
671
672
If ``maximality_check`` is True, check that each maximal face is,
673
in fact, maximal. In this case, when producing the internal
674
representation of the simplicial complex, omit those that are not.
675
It is highly recommended that this be True; various methods for
676
this class may fail if faces which are claimed to be maximal are
677
in fact not.
678
679
If ``sort_facets`` is True, sort the vertices in each facet. If
680
the vertices in different facets are not ordered compatibly (e.g.,
681
if you have facets (1, 3, 5) and (5, 3, 8)), then homology
682
calculations may have unpredictable results.
683
684
If ``name_check`` is True, check the names of the vertices to see
685
if they can be easily converted to generators of a polynomial ring
686
-- use this if you plan to use the Stanley-Reisner ring for the
687
simplicial complex.
688
689
.. note::
690
691
The elements of ``vertex_set`` are not automatically in the
692
simplicial complex: each one is only included if it is a vertex
693
of at least one of the specified facets.
694
695
EXAMPLES::
696
697
sage: SimplicialComplex(4, [[1,2], [1,4]])
698
Simplicial complex with vertex set (0, 1, 2, 3, 4) and facets {(1, 2), (1, 4)}
699
sage: SimplicialComplex(3, [[0,2], [0,3], [0]])
700
Simplicial complex with vertex set (0, 1, 2, 3) and facets {(0, 2), (0, 3)}
701
sage: SimplicialComplex(3, [[0,2], [0,3], [0]], maximality_check=False)
702
Simplicial complex with vertex set (0, 1, 2, 3) and facets {(0, 2), (0, 3), (0,)}
703
sage: S = SimplicialComplex(['a', 'b', 'c'], (('a', 'b'), ('a', 'c'), ('b', 'c')))
704
sage: S
705
Simplicial complex with vertex set ('a', 'b', 'c') and facets {('b', 'c'), ('a', 'c'), ('a', 'b')}
706
707
You can also omit the ``vertex_set`` argument -- if the first
708
argument is a list of lists (or anything similar -- something
709
which looks like it should be ``maximal_faces``), then it is used
710
for ``maximal_faces``, and the set of vertices is deduced from the
711
vertices used therein::
712
713
sage: SimplicialComplex([[0,2], [0,3], [0,6]])
714
Simplicial complex with vertex set (0, 2, 3, 6) and facets {(0, 6), (0, 2), (0, 3)}
715
716
Finally, if ``vertex_set`` is the only argument and it is a
717
simplicial complex, return that complex. If it is an object with
718
a built-in conversion to simplicial complexes (via a
719
``_simplicial_`` method), then the resulting simplicial complex is
720
returned::
721
722
sage: S = SimplicialComplex([[0,2], [0,3], [0,6]])
723
sage: SimplicialComplex(S) == S
724
True
725
sage: Tc = cubical_complexes.Torus(); Tc
726
Cubical complex with 16 vertices and 64 cubes
727
sage: Ts = SimplicialComplex(Tc); Ts
728
Simplicial complex with 16 vertices and 32 facets
729
sage: Ts.homology()
730
{0: 0, 1: Z x Z, 2: Z}
731
732
TESTS::
733
734
sage: S = SimplicialComplex(['a', 'b', 'c'], (('a', 'b'), ('a', 'c'), ('b', 'c')))
735
sage: S == loads(dumps(S))
736
True
737
"""
738
def __init__(self, vertex_set=[], maximal_faces=[], **kwds):
739
"""
740
Define a simplicial complex. See ``SimplicialComplex`` for more
741
documentation.
742
743
EXAMPLES::
744
745
sage: SimplicialComplex(3, [[0,2], [0,3], [0]])
746
Simplicial complex with vertex set (0, 1, 2, 3) and facets {(0, 2), (0, 3)}
747
sage: SimplicialComplex(['a', 'b', 'c'], (('a', 'b'), ('a', 'c'), ('b', 'c')))
748
Simplicial complex with vertex set ('a', 'b', 'c') and facets {('b', 'c'), ('a', 'c'), ('a', 'b')}
749
"""
750
from sage.misc.misc import union
751
# process kwds
752
sort_facets = kwds.get('sort_facets', True)
753
vertex_check = kwds.get('vertex_check', True)
754
maximality_check = kwds.get('maximality_check', True)
755
name_check = kwds.get('name_check', False)
756
# done with kwds
757
#
758
# if there are no maximal faces, perhaps the vertex_set was
759
# omitted. Check to see if it is a list (or tuple) of lists
760
# (or tuples or simplices) , and if so, use that for
761
# 'maximal_faces', and use the union of all of the vertices for
762
# 'vertex_set'.
763
if maximal_faces == []:
764
C = None
765
if isinstance(vertex_set, (list, tuple)) and isinstance(vertex_set[0], (list, tuple, Simplex)):
766
maximal_faces = vertex_set
767
vertex_set = reduce(union, vertex_set)
768
elif isinstance(vertex_set, SimplicialComplex):
769
C = vertex_set
770
else:
771
try:
772
C = vertex_set._simplicial_()
773
except AttributeError:
774
pass
775
if C is not None:
776
self._vertex_set = copy(C.vertices())
777
self._facets = list(C.facets())
778
self._faces = copy(C._faces)
779
self._gen_dict = copy(C._gen_dict)
780
self._complex = copy(C._complex)
781
self.__contractible = copy(C.__contractible)
782
self.__enlarged = copy(C.__enlarged)
783
self._graph = copy(C._graph)
784
self._numeric = C._numeric
785
self._numeric_translation = copy(C._numeric_translation)
786
return
787
788
if sort_facets:
789
try: # vertex_set is an iterable
790
vertices = Simplex(sorted(vertex_set))
791
except TypeError: # vertex_set is an integer
792
vertices = Simplex(vertex_set)
793
else:
794
vertices = Simplex(vertex_set)
795
gen_dict = {}
796
for v in vertices:
797
if name_check:
798
try:
799
if int(v) < 0:
800
raise ValueError, "The vertex %s does not have an appropriate name."%v
801
except ValueError: # v is not an integer
802
try:
803
normalize_names(1, v)
804
except ValueError:
805
raise ValueError, "The vertex %s does not have an appropriate name."%v
806
# build dictionary of generator names
807
try:
808
gen_dict[v] = 'x%s'%int(v)
809
except:
810
gen_dict[v] = v
811
# build set of facets
812
good_faces = []
813
maximal_simplices = [Simplex(f) for f in maximal_faces]
814
for face in maximal_simplices:
815
# check whether vertices of each face are contained in vertex set
816
if vertex_check:
817
if not face.is_face(vertices):
818
raise ValueError, "The face %s is not a subset of the vertex set." % face
819
# check whether each given face is actually maximal
820
face_is_maximal = True
821
if maximality_check:
822
faces_to_be_removed = []
823
for other in good_faces:
824
if other.is_face(face):
825
faces_to_be_removed.append(other)
826
elif face_is_maximal:
827
face_is_maximal = not face.is_face(other)
828
for x in faces_to_be_removed:
829
good_faces.remove(x)
830
if sort_facets:
831
face = Simplex(sorted(face.tuple()))
832
if face_is_maximal:
833
good_faces += [face]
834
# if no maximal faces, add the empty face as a facet
835
if len(maximal_simplices) == 0:
836
good_faces.append(Simplex(-1))
837
# now record the attributes for self
838
# self._vertex_set: the Simplex formed by the vertices
839
self._vertex_set = vertices
840
# self._facets: list of facets
841
self._facets = good_faces
842
# self._faces: dictionary of dictionaries of faces. The main
843
# dictionary is keyed by subcomplexes, and each value is a
844
# dictionary keyed by dimension. This should be empty until
845
# needed -- that is, until the faces method is called
846
self._faces = {}
847
# self._gen_dict: dictionary of names for the polynomial
848
# generators of the Stanley-Reisner ring
849
self._gen_dict = gen_dict
850
# self._complex: dictionary indexed by dimension d, subcomplex,
851
# etc.: differential from dim d to dim d-1 in the associated
852
# chain complex. thus to get the differential in the cochain
853
# complex from dim d-1 to dim d, take the transpose of this
854
# one.
855
self._complex = {}
856
# self.__contractible: if not None, a contractible subcomplex
857
# of self, as found by the _contractible_subcomplex method.
858
self.__contractible = None
859
# self.__enlarged: dictionary of enlarged subcomplexes,
860
# indexed by subcomplexes. For use in the _enlarge_subcomplex
861
# method.
862
self.__enlarged = {}
863
# initialize self._graph to None.
864
self._graph = None
865
# in self._numeric, record whether vertices are integers or
866
# something else. if something else, in
867
# self._numeric_translation, store a tuple of pairs for
868
# translating them back and forth.
869
numeric = all([isinstance(v, (int, Integer, long)) for v in vertices])
870
d = None
871
if not numeric:
872
d = zip(vertices, range(len(tuple(vertices))))
873
self._numeric = numeric
874
self._numeric_translation = d
875
876
def __cmp__(self,right):
877
"""
878
Two simplicial complexes are equal iff their vertex sets are
879
equal and their sets of facets are equal.
880
881
EXAMPLES::
882
883
sage: SimplicialComplex(4, [[1,2], [2,3], [4]]) == SimplicialComplex(4, [[4], [2,3], [3], [2,1]])
884
True
885
sage: X = SimplicialComplex(4)
886
sage: X.add_face([1,3])
887
sage: X == SimplicialComplex(4, [[1,3]])
888
True
889
"""
890
if (self.vertices() == right.vertices() and
891
set(self._facets) == set(right._facets)):
892
return 0
893
else:
894
return -1
895
896
def vertices(self):
897
"""
898
The vertex set of this simplicial complex.
899
900
EXAMPLES::
901
902
sage: S = SimplicialComplex(15, [[0,1], [1,2]])
903
sage: S
904
Simplicial complex with 16 vertices and facets {(1, 2), (0, 1)}
905
sage: S.vertices()
906
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
907
908
Note that this actually returns a simplex::
909
910
sage: type(S.vertices())
911
<class 'sage.homology.simplicial_complex.Simplex'>
912
"""
913
return self._vertex_set
914
915
def maximal_faces(self):
916
"""
917
The maximal faces (a.k.a. facets) of this simplicial complex.
918
919
This just returns the set of facets used in defining the
920
simplicial complex, so if the simplicial complex was defined
921
with no maximality checking, none is done here, either.
922
923
EXAMPLES::
924
925
sage: Y = SimplicialComplex(5, [[0,2], [1,4]])
926
sage: Y.maximal_faces()
927
{(1, 4), (0, 2)}
928
929
``facets`` is a synonym for ``maximal_faces``::
930
931
sage: S = SimplicialComplex(2, [[0,1], [0,1,2]])
932
sage: S.facets()
933
{(0, 1, 2)}
934
"""
935
return Set(self._facets)
936
937
facets = maximal_faces
938
939
def faces(self, subcomplex=None):
940
"""
941
The faces of this simplicial complex, in the form of a
942
dictionary of sets keyed by dimension. If the optional
943
argument ``subcomplex`` is present, then return only the
944
faces which are *not* in the subcomplex.
945
946
:param subcomplex: a subcomplex of this simplicial complex.
947
Return faces which are not in this subcomplex.
948
949
:type subcomplex: optional, default None
950
951
EXAMPLES::
952
953
sage: Y = SimplicialComplex(5, [[1,2], [1,4]])
954
sage: Y.faces()
955
{0: set([(4,), (2,), (1,)]), 1: set([(1, 2), (1, 4)]), -1: set([()])}
956
sage: L = SimplicialComplex(5, [[1,2]])
957
sage: Y.faces(subcomplex=L)
958
{0: set([(4,)]), 1: set([(1, 4)]), -1: set([])}
959
"""
960
if subcomplex not in self._faces:
961
# Faces is the dictionary of faces in self but not in
962
# subcomplex, indexed by dimension
963
Faces = {}
964
# sub_facets is the dictionary of facets in the subcomplex
965
sub_facets = {}
966
dimension = max([face.dimension() for face in self._facets])
967
for i in range(-1,dimension+1):
968
Faces[i] = set([])
969
sub_facets[i] = set([])
970
for f in self._facets:
971
dim = f.dimension()
972
Faces[dim].add(f)
973
if subcomplex is not None:
974
for g in subcomplex._facets:
975
dim = g.dimension()
976
Faces[dim].discard(g)
977
sub_facets[dim].add(g)
978
# bad_faces is the set of faces in the subcomplex in the
979
# current dimension
980
bad_faces = sub_facets[dimension]
981
for dim in range(dimension, -1, -1):
982
# bad_bdries = boundaries of bad_faces: things to be
983
# discarded in dim-1
984
bad_bdries = sub_facets[dim-1]
985
for f in bad_faces:
986
bad_bdries.update(f.faces())
987
for f in Faces[dim]:
988
Faces[dim-1].update(set(f.faces()).difference(bad_bdries))
989
bad_faces = bad_bdries
990
self._faces[subcomplex] = Faces
991
return self._faces[subcomplex]
992
993
cells = faces
994
995
def n_faces(self, n, subcomplex=None):
996
"""
997
The set of simplices of dimension n of this simplicial complex.
998
If the optional argument ``subcomplex`` is present, then
999
return the ``n``-dimensional faces which are *not* in the
1000
subcomplex.
1001
1002
:param n: non-negative integer
1003
:param subcomplex: a subcomplex of this simplicial complex.
1004
Return ``n``-dimensional faces which are not in this
1005
subcomplex.
1006
:type subcomplex: optional, default None
1007
1008
EXAMPLES::
1009
1010
sage: S = Set(range(1,5))
1011
sage: Z = SimplicialComplex(S, S.subsets())
1012
sage: Z
1013
Simplicial complex with vertex set (1, 2, 3, 4) and facets {(1, 2, 3, 4)}
1014
sage: Z.n_faces(2)
1015
set([(1, 3, 4), (1, 2, 3), (2, 3, 4), (1, 2, 4)])
1016
sage: K = SimplicialComplex(S, [[1,2,3], [2,3,4]])
1017
sage: Z.n_faces(2, subcomplex=K)
1018
set([(1, 3, 4), (1, 2, 4)])
1019
"""
1020
if n in self.faces(subcomplex):
1021
return self.faces(subcomplex)[n]
1022
else:
1023
return set([])
1024
1025
def is_pure(self):
1026
"""
1027
True iff this simplicial complex is pure: a simplicial complex
1028
is pure iff all of its maximal faces have the same dimension.
1029
1030
.. warning::
1031
1032
This may give the wrong answer if the simplicial complex
1033
was constructed with ``maximality_check`` set to False.
1034
1035
EXAMPLES::
1036
1037
sage: U = SimplicialComplex(5, [[1,2], [1, 3, 4]])
1038
sage: U.is_pure()
1039
False
1040
sage: X = SimplicialComplex(3, [[0,1], [0,2], [1,2]])
1041
sage: X.is_pure()
1042
True
1043
"""
1044
dims = [face.dimension() for face in self._facets]
1045
return max(dims) == min(dims)
1046
1047
def h_vector(self):
1048
r"""
1049
The `h`-vector of this simplicial complex.
1050
1051
If the complex has dimension `d` and `(f_{-1}, f_0, f_1, ...,
1052
f_d)` is its `f`-vector (with `f_{-1} = 1`, representing the
1053
empy simplex), then the `h`-vector `(h_0, h_1, ..., h_d,
1054
h_{d+1})` is defined by
1055
1056
.. math::
1057
1058
\sum_{i=0}^{d+1} h_i x^{d+1-i} = \sum_{i=0}^{d+1} f_{i-1} (x-1)^{d+1-i}.
1059
1060
Alternatively,
1061
1062
.. math::
1063
1064
h_j = \sum_{i=-1}^{j-1} (-1)^{j-i-1} \binom{d-i}{j-i-1} f_i.
1065
1066
EXAMPLES:
1067
1068
The `f`- and `h`-vectors of the boundary of an octahedron are
1069
computed in Wikipedia's page on simplicial complexes,
1070
http://en.wikipedia.org/wiki/Simplicial_complex::
1071
1072
sage: square = SimplicialComplex([[0,1], [1,2], [2,3], [0,3]])
1073
sage: S0 = SimplicialComplex([[0], [1]])
1074
sage: octa = square.join(S0) # boundary of an octahedron
1075
sage: octa.f_vector()
1076
[1, 6, 12, 8]
1077
sage: octa.h_vector()
1078
[1, 3, 3, 1]
1079
"""
1080
from sage.rings.arith import binomial
1081
d = self.dimension()
1082
f = self.f_vector() # indexed starting at 0, since it's a Python list
1083
h = []
1084
for j in range(0, d+2):
1085
s = 0
1086
for i in range(-1, j):
1087
s += (-1)**(j-i-1) * binomial(d-i, j-i-1) * f[i+1]
1088
h.append(s)
1089
return h
1090
1091
def g_vector(self):
1092
r"""
1093
The `g`-vector of this simplicial complex.
1094
1095
If the `h`-vector of the complex is `(h_0, h_1, ..., h_d,
1096
h_{d+1})` -- see :meth:`h_vector` -- then its `g`-vector
1097
`(g_0, g_1, ..., g_{[(d+1)/2]})` is defined by `g_0 = 1` and
1098
`g_i = h_i - h_{i-1}` for `i > 0`.
1099
1100
EXAMPLES::
1101
1102
sage: S3 = simplicial_complexes.Sphere(3).barycentric_subdivision()
1103
sage: S3.f_vector()
1104
[1, 30, 150, 240, 120]
1105
sage: S3.h_vector()
1106
[1, 26, 66, 26, 1]
1107
sage: S3.g_vector()
1108
[1, 25, 40]
1109
"""
1110
from sage.functions.other import floor
1111
d = self.dimension()
1112
h = self.h_vector()
1113
g = [1]
1114
for i in range(1, floor((d+1)/2) + 1):
1115
g.append(h[i] - h[i-1])
1116
return g
1117
1118
def product(self, right, rename_vertices=True):
1119
"""
1120
The product of this simplicial complex with another one.
1121
1122
:param right: the other simplicial complex (the right-hand
1123
factor)
1124
1125
:param rename_vertices: If this is False, then the vertices in
1126
the product are the set of ordered pairs `(v,w)` where `v`
1127
is a vertex in ``self`` and `w` is a vertex in
1128
``right``. If this is True, then the vertices are renamed
1129
as "LvRw" (e.g., the vertex (1,2) would become "L1R2").
1130
This is useful if you want to define the Stanley-Reisner
1131
ring of the complex: vertex names like (0,1) are not
1132
suitable for that, while vertex names like "L0R1" are.
1133
1134
:type rename_vertices: boolean; optional, default True
1135
1136
The vertices in the product will be the set of ordered pairs
1137
`(v,w)` where `v` is a vertex in self and `w` is a vertex in
1138
right.
1139
1140
.. warning::
1141
1142
If ``X`` and ``Y`` are simplicial complexes, then ``X*Y``
1143
returns their join, not their product.
1144
1145
EXAMPLES::
1146
1147
sage: S = SimplicialComplex(3, [[0,1], [1,2], [0,2]]) # circle
1148
sage: K = SimplicialComplex(1, [[0,1]]) # edge
1149
sage: S.product(K).vertices() # cylinder
1150
('L0R0', 'L0R1', 'L1R0', 'L1R1', 'L2R0', 'L2R1', 'L3R0', 'L3R1')
1151
sage: S.product(K, rename_vertices=False).vertices()
1152
((0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1), (3, 0), (3, 1))
1153
sage: T = S.product(S) # torus
1154
sage: T
1155
Simplicial complex with 16 vertices and 18 facets
1156
sage: T.homology()
1157
{0: 0, 1: Z x Z, 2: Z}
1158
1159
These can get large pretty quickly::
1160
1161
sage: T = simplicial_complexes.Torus(); T
1162
Simplicial complex with vertex set (0, 1, 2, 3, 4, 5, 6) and 14 facets
1163
sage: K = simplicial_complexes.KleinBottle(); K
1164
Simplicial complex with 9 vertices and 18 facets
1165
sage: T.product(K) # long time: 5 or 6 seconds
1166
Simplicial complex with 63 vertices and 1512 facets
1167
"""
1168
vertices = []
1169
for v in self.vertices():
1170
for w in right.vertices():
1171
if rename_vertices:
1172
vertices.append("L" + str(v) + "R" + str(w))
1173
else:
1174
vertices.append((v,w))
1175
facets = []
1176
for f in self._facets:
1177
for g in right._facets:
1178
facets.extend(f.product(g, rename_vertices))
1179
return SimplicialComplex(vertices, facets)
1180
1181
def join(self, right, rename_vertices=True):
1182
"""
1183
The join of this simplicial complex with another one.
1184
1185
The join of two simplicial complexes `S` and `T` is the
1186
simplicial complex `S*T` with simplices of the form `[v_0,
1187
..., v_k, w_0, ..., w_n]` for all simplices `[v_0, ..., v_k]` in
1188
`S` and `[w_0, ..., w_n]` in `T`.
1189
1190
:param right: the other simplicial complex (the right-hand factor)
1191
1192
:param rename_vertices: If this is True, the vertices in the
1193
join will be renamed by the formula: vertex "v" in the
1194
left-hand factor --> vertex "Lv" in the join, vertex "w" in
1195
the right-hand factor --> vertex "Rw" in the join. If this
1196
is false, this tries to construct the join without renaming
1197
the vertices; this will cause problems if the two factors
1198
have any vertices with names in common.
1199
1200
:type rename_vertices: boolean; optional, default True
1201
1202
EXAMPLES::
1203
1204
sage: S = SimplicialComplex(1, [[0], [1]])
1205
sage: T = SimplicialComplex([2, 3], [[2], [3]])
1206
sage: S.join(T)
1207
Simplicial complex with vertex set ('L0', 'L1', 'R2', 'R3') and 4 facets
1208
sage: S.join(T, rename_vertices=False)
1209
Simplicial complex with vertex set (0, 1, 2, 3) and facets {(1, 3), (1, 2), (0, 2), (0, 3)}
1210
1211
The notation '*' may be used, as well::
1212
1213
sage: S * S
1214
Simplicial complex with vertex set ('L0', 'L1', 'R0', 'R1') and 4 facets
1215
sage: S * S * S * S * S * S * S * S
1216
Simplicial complex with 16 vertices and 256 facets
1217
"""
1218
if rename_vertices:
1219
vertex_set = (["L" + str(v) for v in self.vertices()]
1220
+ ["R" + str(w) for w in right.vertices()])
1221
else:
1222
vertex_set = tuple(self._vertex_set) + tuple(right.vertices())
1223
facets = []
1224
for f in self._facets:
1225
for g in right._facets:
1226
facets.append(f.join(g, rename_vertices))
1227
return SimplicialComplex(vertex_set, facets)
1228
1229
# Use * to mean 'join':
1230
__mul__ = join
1231
1232
def cone(self):
1233
"""
1234
The cone on this simplicial complex.
1235
1236
The cone is the simplicial complex formed by adding a new
1237
vertex `C` and simplices of the form `[C, v_0, ..., v_k]` for
1238
every simplex `[v_0, ..., v_k]` in the original simplicial
1239
complex. That is, the cone is the join of the original
1240
complex with a one-point simplicial complex.
1241
1242
EXAMPLES::
1243
1244
sage: S = SimplicialComplex(1, [[0], [1]])
1245
sage: S.cone()
1246
Simplicial complex with vertex set ('L0', 'L1', 'R0') and facets {('L0', 'R0'), ('L1', 'R0')}
1247
"""
1248
return self.join(SimplicialComplex(["0"], [["0"]]),
1249
rename_vertices = True)
1250
1251
def suspension(self, n=1):
1252
"""
1253
The suspension of this simplicial complex.
1254
1255
:param n: positive integer -- suspend this many times.
1256
1257
:type n: optional, default 1
1258
1259
The suspension is the simplicial complex formed by adding two
1260
new vertices `S_0` and `S_1` and simplices of the form `[S_0,
1261
v_0, ..., v_k]` and `[S_1, v_0, ..., v_k]` for every simplex
1262
`[v_0, ..., v_k]` in the original simplicial complex. That
1263
is, the suspension is the join of the original complex with a
1264
two-point simplicial complex.
1265
1266
EXAMPLES::
1267
1268
sage: S = SimplicialComplex(1, [[0], [1]])
1269
sage: S.suspension()
1270
Simplicial complex with vertex set ('L0', 'L1', 'R0', 'R1') and 4 facets
1271
sage: S3 = S.suspension(3) # the 3-sphere
1272
sage: S3.homology()
1273
{0: 0, 1: 0, 2: 0, 3: Z}
1274
"""
1275
if n<0:
1276
raise ValueError, "n must be non-negative."
1277
if n==0:
1278
return self
1279
if n==1:
1280
return self.join(SimplicialComplex(["0", "1"], [["0"], ["1"]]),
1281
rename_vertices = True)
1282
return self.suspension().suspension(int(n-1))
1283
1284
def disjoint_union(self, right, rename_vertices=True):
1285
"""
1286
The disjoint union of this simplicial complex with another one.
1287
1288
:param right: the other simplicial complex (the right-hand factor)
1289
1290
:param rename_vertices: If this is True, the vertices in the
1291
disjoint union will be renamed by the formula: vertex "v"
1292
in the left-hand factor --> vertex "Lv" in the disjoint
1293
union, vertex "w" in the right-hand factor --> vertex "Rw"
1294
in the disjoint union. If this is false, this tries to
1295
construct the disjoint union without renaming the vertices;
1296
this will cause problems if the two factors have any
1297
vertices with names in common.
1298
1299
:type rename_vertices: boolean; optional, default True
1300
1301
EXAMPLES::
1302
1303
sage: S1 = simplicial_complexes.Sphere(1)
1304
sage: S2 = simplicial_complexes.Sphere(2)
1305
sage: S1.disjoint_union(S2).homology()
1306
{0: Z, 1: Z, 2: Z}
1307
"""
1308
if rename_vertices:
1309
vertex_set = (["L" + str(v) for v in self.vertices()]
1310
+ ["R" + str(w) for w in right.vertices()])
1311
else:
1312
vertex_set = tuple(self._vertex_set) + tuple(right.vertices())
1313
facets = []
1314
for f in self._facets:
1315
facets.append(tuple(["L" + str(v) for v in f]))
1316
for f in right._facets:
1317
facets.append(tuple(["R" + str(v) for v in f]))
1318
return SimplicialComplex(vertex_set, facets)
1319
1320
def wedge(self, right, rename_vertices=True):
1321
"""
1322
The wedge (one-point union) of this simplicial complex with
1323
another one.
1324
1325
:param right: the other simplicial complex (the right-hand factor)
1326
1327
:param rename_vertices: If this is True, the vertices in the
1328
wedge will be renamed by the formula: first vertex in each
1329
are glued together and called "0". Otherwise, each vertex
1330
"v" in the left-hand factor --> vertex "Lv" in the wedge,
1331
vertex "w" in the right-hand factor --> vertex "Rw" in the
1332
wedge. If this is false, this tries to construct the wedge
1333
without renaming the vertices; this will cause problems if
1334
the two factors have any vertices with names in common.
1335
1336
:type rename_vertices: boolean; optional, default True
1337
1338
.. note::
1339
1340
This operation is not well-defined if ``self`` or
1341
``other`` is not path-connected.
1342
1343
EXAMPLES::
1344
1345
sage: S1 = simplicial_complexes.Sphere(1)
1346
sage: S2 = simplicial_complexes.Sphere(2)
1347
sage: S1.wedge(S2).homology()
1348
{0: 0, 1: Z, 2: Z}
1349
"""
1350
left_vertices = list(self.vertices())
1351
left_0 = left_vertices.pop(0)
1352
right_vertices = list(right.vertices())
1353
right_0 = right_vertices.pop(0)
1354
left_dict = {left_0: 0}
1355
right_dict = {right_0: 0}
1356
if rename_vertices:
1357
vertex_set = ([0] + ["L" + str(v) for v in left_vertices]
1358
+ ["R" + str(w) for w in right_vertices])
1359
for v in left_vertices:
1360
left_dict[v] = "L" + str(v)
1361
for v in right_vertices:
1362
right_dict[v] = "R" + str(v)
1363
else:
1364
vertex_set = (0,) + tuple(left_vertices) + tuple(right_vertices)
1365
if rename_vertices:
1366
facets = []
1367
for f in self._facets:
1368
facets.append(tuple([left_dict[v] for v in f]))
1369
for f in right._facets:
1370
facets.append(tuple([right_dict[v] for v in f]))
1371
else:
1372
facets = self._facets + right._facets
1373
return SimplicialComplex(vertex_set, facets)
1374
1375
def chain_complex(self, **kwds):
1376
"""
1377
The chain complex associated to this simplicial complex.
1378
1379
:param dimensions: if None, compute the chain complex in all
1380
dimensions. If a list or tuple of integers, compute the
1381
chain complex in those dimensions, setting the chain groups
1382
in all other dimensions to zero.
1383
:param base_ring: commutative ring
1384
:type base_ring: optional, default ZZ
1385
:param subcomplex: a subcomplex of this simplicial complex.
1386
Compute the chain complex relative to this subcomplex.
1387
:type subcomplex: optional, default empty
1388
:param augmented: If True, return the augmented chain complex
1389
(that is, include a class in dimension `-1` corresponding
1390
to the empty cell). This is ignored if ``dimensions`` is
1391
specified.
1392
:type augmented: boolean; optional, default False
1393
:param cochain: If True, return the cochain complex (that is,
1394
the dual of the chain complex).
1395
:type cochain: boolean; optional, default False
1396
:param verbose: If True, print some messages as the chain
1397
complex is computed.
1398
:type verbose: boolean; optional, default False
1399
:param check_diffs: If True, make sure that the chain complex
1400
is actually a chain complex: the differentials are
1401
composable and their product is zero.
1402
:type check_diffs: boolean; optional, default False
1403
1404
.. note::
1405
1406
If subcomplex is nonempty, then the argument ``augmented``
1407
has no effect: the chain complex relative to a nonempty
1408
subcomplex is zero in dimension `-1`.
1409
1410
EXAMPLES::
1411
1412
sage: circle = SimplicialComplex(2, [[0,1], [1,2], [0, 2]])
1413
sage: circle.chain_complex()
1414
Chain complex with at most 2 nonzero terms over Integer Ring
1415
sage: circle.chain_complex()._latex_()
1416
'\\Bold{Z}^{3} \\xrightarrow{d_{1}} \\Bold{Z}^{3}'
1417
sage: circle.chain_complex(base_ring=QQ, augmented=True)
1418
Chain complex with at most 3 nonzero terms over Rational Field
1419
"""
1420
augmented = kwds.get('augmented', False)
1421
cochain = kwds.get('cochain', False)
1422
verbose = kwds.get('verbose', False)
1423
check_diffs = kwds.get('check_diffs', False)
1424
base_ring = kwds.get('base_ring', ZZ)
1425
dimensions = kwds.get('dimensions', None)
1426
subcomplex = kwds.get('subcomplex', None)
1427
1428
# initialize subcomplex
1429
if subcomplex is None:
1430
subcomplex = SimplicialComplex(self.vertices())
1431
else:
1432
# subcomplex is not empty, so don't augment the chain complex
1433
augmented = False
1434
# now construct the range of dimensions in which to compute
1435
if dimensions is None:
1436
dimensions = range(0, self.dimension()+1)
1437
first = 0
1438
else:
1439
augmented = False
1440
first = dimensions[0]
1441
differentials = {}
1442
# in the chain complex, compute the first dimension by hand,
1443
# and don't cache it: it may be differ from situation to
1444
# situation because of boundary effects.
1445
current = None
1446
current_dim = None
1447
if augmented: # then first == 0
1448
current = list(self.n_faces(0, subcomplex=subcomplex))
1449
current_dim = 0
1450
if cochain:
1451
differentials[-1] = matrix(base_ring, len(current), 1,
1452
[1]*len(current))
1453
else:
1454
differentials[0] = matrix(base_ring, 1, len(current),
1455
[1]*len(current))
1456
elif first == 0 and not augmented:
1457
current = list(self.n_faces(0, subcomplex=subcomplex))
1458
current_dim = 0
1459
if not cochain:
1460
differentials[0] = matrix(base_ring, 0, len(current))
1461
else: # first > 0
1462
current = list(self.n_faces(first, subcomplex=subcomplex))
1463
current_dim = first
1464
if not cochain:
1465
differentials[first] = matrix(base_ring, 0, len(current))
1466
for n in dimensions[1:]:
1467
if verbose:
1468
print " starting dimension %s" % n
1469
if (n, subcomplex) in self._complex:
1470
if cochain:
1471
differentials[n-1] = self._complex[(n, subcomplex)].transpose().change_ring(base_ring)
1472
mat = differentials[n-1]
1473
else:
1474
differentials[n] = self._complex[(n, subcomplex)].change_ring(base_ring)
1475
mat = differentials[n]
1476
if verbose:
1477
print " boundary matrix (cached): it's %s by %s." % (mat.nrows(), mat.ncols())
1478
else:
1479
# 'current' is the list of faces in dimension n
1480
#
1481
# 'old' is a dictionary, with keys the faces in the
1482
# previous dimension (dim n-1 for the chain complex,
1483
# n+1 for the cochain complex), values the integers 0,
1484
# 1, 2, ... (the index of the face). finding an entry
1485
# in a dictionary seems to be faster than finding the
1486
# index of an entry in a list.
1487
if current_dim == n-1:
1488
old = dict(zip(current, range(len(current))))
1489
else:
1490
set_of_faces = list(self.n_faces(n-1, subcomplex=subcomplex))
1491
old = dict(zip(set_of_faces, range(len(set_of_faces))))
1492
current = list(self.n_faces(n, subcomplex=subcomplex))
1493
current_dim = n
1494
# construct matrix. it is easiest to construct it as
1495
# a sparse matrix, specifying which entries are
1496
# nonzero via a dictionary.
1497
matrix_data = {}
1498
col = 0
1499
if len(old) and len(current):
1500
for simplex in current:
1501
for i in range(n+1):
1502
face_i = simplex.face(i)
1503
try:
1504
matrix_data[(old[face_i], col)] = (-1)**i
1505
except KeyError:
1506
pass
1507
col += 1
1508
mat = matrix(ZZ, len(old), len(current), matrix_data)
1509
if cochain:
1510
self._complex[(n, subcomplex)] = mat
1511
differentials[n-1] = mat.transpose().change_ring(base_ring)
1512
else:
1513
self._complex[(n, subcomplex)] = mat
1514
differentials[n] = mat.change_ring(base_ring)
1515
if verbose:
1516
print " boundary matrix computed: it's %s by %s." % (mat.nrows(), mat.ncols())
1517
# now for the cochain complex, compute the last dimension by
1518
# hand, and don't cache it.
1519
if cochain:
1520
n = dimensions[-1] + 1
1521
if current_dim != n-1:
1522
current = list(self.n_faces(n-1, subcomplex=subcomplex))
1523
differentials[n-1] = matrix(base_ring, 0, len(current))
1524
# finally, return the chain complex
1525
if cochain:
1526
return ChainComplex(data=differentials, degree=1, **kwds)
1527
else:
1528
return ChainComplex(data=differentials, degree=-1, **kwds)
1529
1530
def _homology_(self, dim=None, **kwds):
1531
"""
1532
The reduced homology of this simplicial complex.
1533
1534
:param dim: If None, then return the homology in every
1535
dimension. If ``dim`` is an integer or list, return the
1536
homology in the given dimensions. (Actually, if ``dim`` is
1537
a list, return the homology in the range from ``min(dim)``
1538
to ``max(dim)``.)
1539
1540
:type dim: integer or list of integers or None; optional,
1541
default None
1542
1543
:param base_ring: commutative ring. Must be ZZ or a field.
1544
1545
:type base_ring: optional, default ZZ
1546
1547
:param subcomplex: a subcomplex of this simplicial complex.
1548
Compute homology relative to this subcomplex.
1549
1550
:type subcomplex: optional, default None
1551
1552
:param cohomology: If True, compute cohomology rather than
1553
homology.
1554
1555
:type cohomology: boolean; optional, default False
1556
1557
:param enlarge: If True, find a new subcomplex homotopy
1558
equivalent to, and probably larger than, the given one.
1559
1560
:type enlarge: boolean; optional, default True
1561
1562
:param algorithm: The options are 'auto', 'dhsw', 'pari' or
1563
'no_chomp'. If 'auto', first try CHomP, then use the
1564
Dumas, Heckenbach, Saunders, and Welker elimination
1565
algorithm for large matrices, Pari for small ones. If
1566
'no_chomp', then don't try CHomP, but behave the same
1567
otherwise. If 'pari', then compute elementary divisors
1568
using Pari. If 'dhsw', then use the DHSW algorithm to
1569
compute elementary divisors. (As of this writing, CHomP is
1570
by far the fastest option, followed by the 'auto' or
1571
'no_chomp' setting of using DHSW for large matrices and
1572
Pari for small ones.)
1573
1574
:type algorithm: string; optional, default 'auto'
1575
1576
:param verbose: If True, print some messages as the homology
1577
is computed.
1578
1579
:type verbose: boolean; optional, default False
1580
1581
Algorithm: if ``subcomplex`` is None, replace it with a facet
1582
-- a contractible subcomplex of the original complex. Then no
1583
matter what ``subcomplex`` is, replace it with a subcomplex
1584
`L` which is homotopy equivalent and as large as possible.
1585
Compute the homology of the original complex relative to `L`:
1586
if `L` is large, then the relative chain complex will be small
1587
enough to speed up computations considerably.
1588
1589
EXAMPLES::
1590
1591
sage: circle = SimplicialComplex(2, [[0,1], [1,2], [0, 2]])
1592
sage: circle._homology_()
1593
{0: 0, 1: Z}
1594
sage: disk = SimplicialComplex(3, [[0,1,2,3]])
1595
sage: sphere = disk.remove_face([0,1,2,3])
1596
sage: sphere
1597
Simplicial complex with vertex set (0, 1, 2, 3) and facets {(0, 2, 3), (0, 1, 2), (1, 2, 3), (0, 1, 3)}
1598
sage: sphere._homology_()
1599
{0: 0, 1: 0, 2: Z}
1600
1601
Another way to get a two-sphere: take a two-point space and take its
1602
three-fold join with itself::
1603
1604
sage: S = SimplicialComplex(1, [[0], [1]])
1605
sage: (S*S*S)._homology_(dim=2, cohomology=True)
1606
Z
1607
1608
Relative homology::
1609
1610
sage: T = SimplicialComplex(2, [[0,1,2]])
1611
sage: U = SimplicialComplex(2, [[0,1], [1,2], [0,2]])
1612
sage: T._homology_(subcomplex=U)
1613
{0: 0, 1: 0, 2: Z}
1614
"""
1615
from sage.modules.all import VectorSpace
1616
from sage.homology.chain_complex import HomologyGroup
1617
1618
base_ring = kwds.get('base_ring', ZZ)
1619
cohomology = kwds.get('cohomology', False)
1620
enlarge = kwds.get('enlarge', True)
1621
verbose = kwds.get('verbose', False)
1622
subcomplex = kwds.get('subcomplex', None)
1623
1624
if dim is not None:
1625
if isinstance(dim, (list, tuple)):
1626
low = min(dim) - 1
1627
high = max(dim) + 2
1628
else:
1629
low = dim - 1
1630
high = dim + 2
1631
dims = range(low, high)
1632
else:
1633
dims = None
1634
1635
if verbose:
1636
print "starting calculation of the homology of this",
1637
print "%s-dimensional simplicial complex" % self.dimension()
1638
if subcomplex is None:
1639
if enlarge:
1640
if verbose:
1641
print "Constructing contractible subcomplex..."
1642
L = self._contractible_subcomplex(verbose=verbose)
1643
if verbose:
1644
print "Done finding contractible subcomplex."
1645
vec = [len(self.n_faces(n-1, subcomplex=L)) for n in range(self.dimension()+2)]
1646
print "The difference between the f-vectors is:"
1647
print " %s" % vec
1648
else:
1649
L = SimplicialComplex(self.vertices(), [[self.vertices().tuple()[0]]])
1650
else:
1651
if enlarge:
1652
if verbose:
1653
print "Enlarging subcomplex..."
1654
L = self._enlarge_subcomplex(subcomplex, verbose=verbose)
1655
if verbose:
1656
print "Done enlarging subcomplex:"
1657
else:
1658
L = subcomplex
1659
if verbose:
1660
print "Computing the chain complex..."
1661
kwds['subcomplex']=L
1662
C = self.chain_complex(dimensions=dims, augmented=True,
1663
cochain=cohomology, **kwds)
1664
if verbose:
1665
print " Done computing the chain complex. "
1666
print "Now computing homology..."
1667
if 'subcomplex' in kwds:
1668
del kwds['subcomplex']
1669
answer = C.homology(**kwds)
1670
if isinstance(answer, dict):
1671
if cohomology:
1672
too_big = self.dimension() + 1
1673
if (not ((isinstance(dim, (list, tuple)) and too_big in dim)
1674
or too_big == dim)
1675
and too_big in answer):
1676
del answer[too_big]
1677
if -2 in answer:
1678
del answer[-2]
1679
if -1 in answer:
1680
del answer[-1]
1681
if dim is not None:
1682
if isinstance(dim, (list, tuple)):
1683
temp = {}
1684
for n in dim:
1685
temp[n] = answer[n]
1686
answer = temp
1687
else: # just a single dimension
1688
if base_ring == ZZ:
1689
answer = answer.get(dim, HomologyGroup(0))
1690
else:
1691
answer = answer.get(dim, VectorSpace(base_ring, 0))
1692
return answer
1693
1694
def add_face(self, face):
1695
"""
1696
Add a face to this simplicial complex
1697
1698
:param face: a subset of the vertex set
1699
1700
This *changes* the simplicial complex, adding a new face and all
1701
of its subfaces.
1702
1703
EXAMPLES::
1704
1705
sage: X = SimplicialComplex(2, [[0,1], [0,2]])
1706
sage: X.add_face([0,1,2,]); X
1707
Simplicial complex with vertex set (0, 1, 2) and facets {(0, 1, 2)}
1708
sage: Y = SimplicialComplex(3); Y
1709
Simplicial complex with vertex set (0, 1, 2, 3) and facets {()}
1710
sage: Y.add_face([0,1])
1711
sage: Y.add_face([1,2,3])
1712
sage: Y
1713
Simplicial complex with vertex set (0, 1, 2, 3) and facets {(1, 2, 3), (0, 1)}
1714
1715
If you add a face which is already present, there is no effect::
1716
1717
sage: Y.add_face([1,3]); Y
1718
Simplicial complex with vertex set (0, 1, 2, 3) and facets {(1, 2, 3), (0, 1)}
1719
"""
1720
new_face = Simplex(face)
1721
if not new_face.is_face(self.vertices()):
1722
raise ValueError, "The face to be added is not a subset of the vertex set."
1723
else:
1724
face_is_maximal = True
1725
for other in self._facets:
1726
if face_is_maximal:
1727
face_is_maximal = not new_face.is_face(other)
1728
if face_is_maximal:
1729
# remove any old facets which are no longer maximal
1730
Facets = list(self._facets)
1731
for old_face in self._facets:
1732
if old_face.is_face(new_face):
1733
Facets.remove(old_face)
1734
# add new_face to facet list
1735
Facets.append(new_face)
1736
self._facets = Facets
1737
# update self._faces if necessary
1738
if None in self._faces:
1739
all_new_faces = SimplicialComplex(self.vertices(),
1740
[new_face]).faces()
1741
for dim in range(0, new_face.dimension()+1):
1742
if dim in self._faces[None]:
1743
self._faces[None][dim] = self._faces[None][dim].union(all_new_faces[dim])
1744
else:
1745
self._faces[None][dim] = all_new_faces[dim]
1746
# update self._graph if necessary
1747
if self._graph is None:
1748
pass
1749
else:
1750
d = new_face.dimension()+1
1751
for i in range(d):
1752
for j in range(i+1,d):
1753
self._graph.add_edge(new_face[i],new_face[j])
1754
return None
1755
1756
def remove_face(self, face):
1757
"""
1758
Remove a face from this simplicial complex and return the
1759
resulting simplicial complex.
1760
1761
:param face: a face of the simplicial complex
1762
1763
Algorithm: the facets of the new simplicial complex are
1764
the facets of the original complex not containing ``face``,
1765
together with those of ``link(face)*boundary(face)``.
1766
1767
EXAMPLES::
1768
1769
sage: S = range(1,5)
1770
sage: Z = SimplicialComplex(S, [S]); Z
1771
Simplicial complex with vertex set (1, 2, 3, 4) and facets {(1, 2, 3, 4)}
1772
sage: Z2 = Z.remove_face([1,2])
1773
sage: Z2
1774
Simplicial complex with vertex set (1, 2, 3, 4) and facets {(1, 3, 4), (2, 3, 4)}
1775
1776
sage: S = SimplicialComplex(4,[[0,1,2],[2,3]])
1777
sage: S
1778
Simplicial complex with vertex set (0, 1, 2, 3, 4) and facets {(0, 1, 2), (2, 3)}
1779
sage: S2 = S.remove_face([0,1,2])
1780
sage: S2
1781
Simplicial complex with vertex set (0, 1, 2, 3, 4) and facets {(1, 2), (2, 3), (0, 2), (0, 1)}
1782
"""
1783
simplex = Simplex(face)
1784
facets = self.facets()
1785
if all([not simplex.is_face(F) for F in facets]):
1786
# face is not in self: nothing to remove
1787
return self
1788
link = self.link(simplex)
1789
join_facets = []
1790
for f in simplex.faces():
1791
for g in link.facets():
1792
join_facets.append(f.join(g, rename_vertices=False))
1793
# join_facets is the list of facets in the join bdry(face) * link(face)
1794
other_facets = [elem for elem in facets if not simplex.is_face(elem)]
1795
return SimplicialComplex(self.vertices(), join_facets + other_facets)
1796
1797
def connected_sum(self, other):
1798
"""
1799
The connected sum of this simplicial complex with another one.
1800
1801
:param other: another simplicial complex
1802
:return: the connected sum ``self # other``
1803
1804
.. warning::
1805
1806
This does not check that self and other are manifolds, only
1807
that their facets all have the same dimension. Since a
1808
(more or less) random facet is chosen from each complex and
1809
then glued together, this method may return random
1810
results if applied to non-manifolds, depending on which
1811
facet is chosen.
1812
1813
Algorithm: a facet is chosen from each surface, and removed.
1814
The vertices of these two facets are relabeled to
1815
``(0,1,...,dim)``. Of the remaining vertices, the ones from
1816
the left-hand factor are renamed by prepending an "L", and
1817
similarly the remaining vertices in the right-hand factor are
1818
renamed by prepending an "R".
1819
1820
EXAMPLES::
1821
1822
sage: S1 = simplicial_complexes.Sphere(1)
1823
sage: S1.connected_sum(S1.connected_sum(S1)).homology()
1824
{0: 0, 1: Z}
1825
sage: P = simplicial_complexes.RealProjectivePlane(); P
1826
Simplicial complex with vertex set (0, 1, 2, 3, 4, 5) and 10 facets
1827
sage: P.connected_sum(P) # the Klein bottle
1828
Simplicial complex with 9 vertices and 18 facets
1829
1830
The notation '+' may be used for connected sum, also::
1831
1832
sage: P + P # the Klein bottle
1833
Simplicial complex with 9 vertices and 18 facets
1834
sage: (P + P).homology()[1]
1835
Z x C2
1836
"""
1837
if not (self.is_pure() and other.is_pure() and
1838
self.dimension() == other.dimension()):
1839
raise ValueError, "Complexes are not pure of the same dimension."
1840
# first find a top-dimensional simplex to remove from each surface
1841
keep_left = self._facets[0]
1842
keep_right = other._facets[0]
1843
dim = self.dimension()
1844
# construct the set of vertices:
1845
left = set(self.vertices()).difference(set(keep_left))
1846
right = set(other.vertices()).difference(set(keep_right))
1847
vertex_set = (range(dim+1) + ["L" + str(v) for v in left]
1848
+ ["R" + str(v) for v in right])
1849
# construct the set of facets:
1850
left = set(self._facets).difference(set([keep_left]))
1851
right = set(other._facets).difference(set([keep_right]))
1852
facet_set = ([[rename_vertex(v, keep=list(keep_left))
1853
for v in face] for face in left]
1854
+ [[rename_vertex(v, keep=list(keep_right), left=False)
1855
for v in face] for face in right])
1856
# return the new surface
1857
return SimplicialComplex(vertex_set, facet_set)
1858
1859
__add__ = connected_sum
1860
1861
def link(self, simplex):
1862
"""
1863
The link of a simplex in this simplicial complex.
1864
1865
The link of a simplex `F` is the simplicial complex formed by
1866
all simplices `G` which are disjoint from `F` but for which `F
1867
\cup G` is a simplex.
1868
1869
:param simplex: a simplex in this simplicial complex.
1870
1871
EXAMPLES::
1872
1873
sage: X = SimplicialComplex(4, [[0,1,2], [1,2,3]])
1874
sage: X.link(Simplex([0]))
1875
Simplicial complex with vertex set (0, 1, 2, 3, 4) and facets {(1, 2)}
1876
sage: X.link([1,2])
1877
Simplicial complex with vertex set (0, 1, 2, 3, 4) and facets {(3,), (0,)}
1878
sage: Y = SimplicialComplex(3, [[0,1,2,3]])
1879
sage: Y.link([1])
1880
Simplicial complex with vertex set (0, 1, 2, 3) and facets {(0, 2, 3)}
1881
"""
1882
faces = []
1883
s = Simplex(simplex)
1884
for f in self._facets:
1885
if s.is_face(f):
1886
faces.append(Simplex(list(f.set().difference(s.set()))))
1887
return SimplicialComplex(self.vertices(), faces)
1888
1889
def effective_vertices(self):
1890
"""
1891
The set of vertices belonging to some face. Returns a Simplex.
1892
1893
EXAMPLES::
1894
1895
sage: S = SimplicialComplex(15)
1896
sage: S
1897
Simplicial complex with 16 vertices and facets {()}
1898
sage: S.effective_vertices()
1899
()
1900
1901
sage: S = SimplicialComplex(15,[[0,1,2,3],[6,7]])
1902
sage: S
1903
Simplicial complex with 16 vertices and facets {(6, 7), (0, 1, 2, 3)}
1904
sage: S.effective_vertices()
1905
(0, 1, 2, 3, 6, 7)
1906
1907
sage: type(S.effective_vertices())
1908
<class 'sage.homology.simplicial_complex.Simplex'>
1909
1910
"""
1911
try:
1912
v = self.faces()[0]
1913
except KeyError:
1914
return Simplex(-1)
1915
f = []
1916
for i in v:
1917
f.append(i[0])
1918
return Simplex(set(f))
1919
1920
def generated_subcomplex(self,sub_vertex_set):
1921
"""
1922
Returns the largest sub-simplicial complex of self containing
1923
exactly ``sub_vertex_set`` as vertices.
1924
1925
EXAMPLES::
1926
1927
sage: S = simplicial_complexes.Sphere(2)
1928
sage: S
1929
Simplicial complex with vertex set (0, 1, 2, 3) and facets {(0, 2, 3), (0, 1, 2), (1, 2, 3), (0, 1, 3)}
1930
sage: S.generated_subcomplex([0,1,2])
1931
Simplicial complex with vertex set (0, 1, 2) and facets {(0, 1, 2)}
1932
1933
"""
1934
if not self.vertices().set().issuperset(sub_vertex_set):
1935
raise TypeError, "input must be a subset of the vertex set."
1936
faces = []
1937
for i in range(self.dimension()+1):
1938
for j in self.faces()[i]:
1939
if j.set().issubset(sub_vertex_set):
1940
faces.append(j)
1941
return SimplicialComplex(sub_vertex_set,faces,maximality_check=True)
1942
1943
def _complement(self, simplex):
1944
"""
1945
Return the complement of a simplex in the vertex set of this
1946
simplicial complex.
1947
1948
:param simplex: a simplex (need not be in the simplicial complex)
1949
1950
OUTPUT: its complement: the simplex formed by the vertices not
1951
contained in ``simplex``.
1952
1953
Note that this only depends on the vertex set of the
1954
simplicial complex, not on its simplices.
1955
1956
EXAMPLES::
1957
1958
sage: X = SimplicialComplex(5)
1959
sage: X._complement([1,2,3])
1960
(0, 4, 5)
1961
sage: X._complement([0,1,3,4])
1962
(2, 5)
1963
sage: X._complement([0,4,1,3])
1964
(2, 5)
1965
"""
1966
return Simplex(set(self.vertices()).difference(simplex))
1967
1968
def _transpose_simplices(self, *simplices):
1969
"""
1970
Given tuple ``L`` of simplices, returns new list, where each
1971
simplex is formed by taking a vertex from each simplex from
1972
``L``.
1973
1974
:param simplices: a bunch of simplices
1975
1976
If ``simplices`` consists of `(f_0, f_1, f_2, ...)`, then the
1977
output consists of all possible simplices of the form `(v_0,
1978
v_1, v_2, ...)`, where `v_i` is a vertex of `f_i`. If a
1979
vertex appears more than once in such a simplex, remove all
1980
but one of its appearances. If such a simplex contains others
1981
already produced, then ignore that larger simplex -- the
1982
output should be a list of minimal simplices constructed in
1983
this way.
1984
1985
This is used in computing the minimal nonfaces and hence the
1986
Stanley-Reisner ring.
1987
1988
Note that this only depends on the vertex set of the
1989
simplicial complex, not on its simplices.
1990
1991
I don't know if there is a standard name for this, but it
1992
looked sort of like the transpose of a matrix; hence the name
1993
for this method.
1994
1995
EXAMPLES::
1996
1997
sage: X = SimplicialComplex(5)
1998
sage: X._transpose_simplices([1,2])
1999
[(1,), (2,)]
2000
sage: X._transpose_simplices([1,2], [3,4])
2001
[(1, 3), (1, 4), (2, 3), (2, 4)]
2002
2003
In the following example, one can construct the simplices
2004
(1,2) and (1,3), but you can also construct (1,1) = (1,),
2005
which is a face of both of the others. So the answer omits
2006
(1,2) and (1,3)::
2007
2008
sage: X._transpose_simplices([1,2], [1,3])
2009
[(1,), (2, 3)]
2010
"""
2011
answer = []
2012
if len(simplices) == 1:
2013
answer = [Simplex((v,)) for v in simplices[0]]
2014
elif len(simplices) > 1:
2015
face = simplices[0]
2016
rest = simplices[1:]
2017
new_simplices = []
2018
for v in face:
2019
for partial in self._transpose_simplices(*rest):
2020
if v not in partial:
2021
L = [v] + list(partial)
2022
L.sort()
2023
simplex = Simplex(L)
2024
else:
2025
simplex = partial
2026
add_simplex = True
2027
simplices_to_delete = []
2028
for already in answer:
2029
if add_simplex:
2030
if already.is_face(simplex):
2031
add_simplex = False
2032
if add_simplex and simplex.is_face(already):
2033
simplices_to_delete.append(already)
2034
if add_simplex:
2035
answer.append(simplex)
2036
for x in simplices_to_delete:
2037
answer.remove(x)
2038
return answer
2039
2040
def minimal_nonfaces(self):
2041
"""
2042
Set consisting of the minimal subsets of the vertex set of
2043
this simplicial complex which do not form faces.
2044
2045
Algorithm: first take the complement (within the vertex set)
2046
of each facet, obtaining a set `(f_1, f_2, ...)` of simplices.
2047
Now form the set of all simplices of the form `(v_1, v_2,
2048
...)` where vertex `v_i` is in face `f_i`. This set will
2049
contain the minimal nonfaces and may contain some non-minimal
2050
nonfaces also, so loop through the set to find the minimal
2051
ones. (The last two steps are taken care of by the
2052
``_transpose_simplices`` routine.)
2053
2054
This is used in computing the Stanley-Reisner ring and the
2055
Alexander dual.
2056
2057
EXAMPLES::
2058
2059
sage: X = SimplicialComplex(4)
2060
sage: X.minimal_nonfaces()
2061
{(4,), (2,), (3,), (0,), (1,)}
2062
sage: X.add_face([1,2])
2063
sage: X.add_face([1,3])
2064
sage: X.minimal_nonfaces()
2065
{(4,), (2, 3), (0,)}
2066
sage: Y = SimplicialComplex(3, [[0,1], [1,2], [2,3], [3,0]])
2067
sage: Y.minimal_nonfaces()
2068
{(1, 3), (0, 2)}
2069
"""
2070
complements = [self._complement(facet) for facet in self._facets]
2071
return Set(self._transpose_simplices(*complements))
2072
2073
def _stanley_reisner_base_ring(self, base_ring=ZZ):
2074
"""
2075
The polynomial algebra of which the Stanley-Reisner ring is a
2076
quotient.
2077
2078
:param base_ring: a commutative ring
2079
:type base_ring: optional, default ZZ
2080
:return: a polynomial algebra with coefficients in base_ring,
2081
with one generator for each vertex in the simplicial complex.
2082
2083
See the documentation for ``stanley_reisner_ring`` for a
2084
warning about the names of the vertices.
2085
2086
EXAMPLES::
2087
2088
sage: X = SimplicialComplex(3, [[1,2], [0]])
2089
sage: X._stanley_reisner_base_ring()
2090
Multivariate Polynomial Ring in x0, x1, x2, x3 over Integer Ring
2091
sage: Y = SimplicialComplex(['a', 'b', 'c'])
2092
sage: Y._stanley_reisner_base_ring(base_ring=QQ)
2093
Multivariate Polynomial Ring in a, c, b over Rational Field
2094
"""
2095
return PolynomialRing(base_ring, self._gen_dict.values())
2096
2097
def stanley_reisner_ring(self, base_ring=ZZ):
2098
"""
2099
The Stanley-Reisner ring of this simplicial complex.
2100
2101
:param base_ring: a commutative ring
2102
:type base_ring: optional, default ZZ
2103
:return: a quotient of a polynomial algebra with coefficients
2104
in ``base_ring``, with one generator for each vertex in the
2105
simplicial complex, by the ideal generated by the products
2106
of those vertices which do not form faces in it.
2107
2108
Thus the ideal is generated by the products corresponding to
2109
the minimal nonfaces of the simplicial complex.
2110
2111
.. warning::
2112
2113
This may be quite slow!
2114
2115
Also, this may behave badly if the vertices have the
2116
'wrong' names. To avoid this, define the simplicial complex
2117
at the start with the flag ``name_check`` set to True.
2118
2119
More precisely, this is a quotient of a polynomial ring
2120
with one generator for each vertex. If the name of a
2121
vertex is a non-negative integer, then the corresponding
2122
polynomial generator is named 'x' followed by that integer
2123
(e.g., 'x2', 'x3', 'x5', ...). Otherwise, the polynomial
2124
generators are given the same names as the vertices. Thus
2125
if the vertex set is (2, 'x2'), there will be problems.
2126
2127
EXAMPLES::
2128
2129
sage: X = SimplicialComplex(3, [[0,1], [1,2], [2,3], [0,3]])
2130
sage: X.stanley_reisner_ring()
2131
Quotient of Multivariate Polynomial Ring in x0, x1, x2, x3 over Integer Ring by the ideal (x1*x3, x0*x2)
2132
sage: Y = SimplicialComplex(4); Y
2133
Simplicial complex with vertex set (0, 1, 2, 3, 4) and facets {()}
2134
sage: Y.stanley_reisner_ring()
2135
Quotient of Multivariate Polynomial Ring in x0, x1, x2, x3, x4 over Integer Ring by the ideal (x4, x2, x3, x0, x1)
2136
sage: Y.add_face([0,1,2,3,4])
2137
sage: Y.stanley_reisner_ring(base_ring=QQ)
2138
Quotient of Multivariate Polynomial Ring in x0, x1, x2, x3, x4 over Rational Field by the ideal (0)
2139
"""
2140
R = self._stanley_reisner_base_ring(base_ring)
2141
products = []
2142
for f in self.minimal_nonfaces():
2143
prod = 1
2144
for v in f:
2145
prod *= R(self._gen_dict[v])
2146
products.append(prod)
2147
return R.quotient(products)
2148
2149
def alexander_dual(self):
2150
"""
2151
The Alexander dual of this simplicial complex: according to
2152
the Macaulay2 documentation, this is the simplicial complex
2153
whose faces are the complements of its nonfaces.
2154
2155
Thus find the minimal nonfaces and take their complements to
2156
find the facets in the Alexander dual.
2157
2158
EXAMPLES::
2159
2160
sage: Y = SimplicialComplex(4); Y
2161
Simplicial complex with vertex set (0, 1, 2, 3, 4) and facets {()}
2162
sage: Y.alexander_dual()
2163
Simplicial complex with vertex set (0, 1, 2, 3, 4) and 5 facets
2164
sage: X = SimplicialComplex(3, [[0,1], [1,2], [2,3], [3,0]])
2165
sage: X.alexander_dual()
2166
Simplicial complex with vertex set (0, 1, 2, 3) and facets {(1, 3), (0, 2)}
2167
"""
2168
nonfaces = self.minimal_nonfaces()
2169
return SimplicialComplex(self.vertices(), [self._complement(f) for f in nonfaces])
2170
2171
def barycentric_subdivision(self):
2172
"""
2173
The barycentric subdivision of this simplicial complex.
2174
2175
See http://en.wikipedia.org/wiki/Barycentric_subdivision for a
2176
definition.
2177
2178
EXAMPLES::
2179
2180
sage: triangle = SimplicialComplex(2, [[0,1], [1,2], [0, 2]])
2181
sage: hexagon = triangle.barycentric_subdivision()
2182
sage: hexagon
2183
Simplicial complex with 6 vertices and 6 facets
2184
sage: hexagon.homology(1) == triangle.homology(1)
2185
True
2186
2187
Barycentric subdivisions can get quite large, since each
2188
`n`-dimensional facet in the original complex produces
2189
`(n+1)!` facets in the subdivision::
2190
2191
sage: S4 = simplicial_complexes.Sphere(4)
2192
sage: S4
2193
Simplicial complex with vertex set (0, 1, 2, 3, 4, 5) and 6 facets
2194
sage: S4.barycentric_subdivision()
2195
Simplicial complex with 62 vertices and 720 facets
2196
"""
2197
return self.face_poset().order_complex()
2198
2199
def graph(self):
2200
"""
2201
The 1-skeleton of this simplicial complex, as a graph.
2202
2203
.. warning::
2204
2205
This may give the wrong answer if the simplicial complex
2206
was constructed with ``maximality_check`` set to False.
2207
2208
EXAMPLES::
2209
2210
sage: S = SimplicialComplex(3, [[0,1,2,3]])
2211
sage: G = S.graph(); G
2212
Graph on 4 vertices
2213
sage: G.edges()
2214
[(0, 1, None), (0, 2, None), (0, 3, None), (1, 2, None), (1, 3, None), (2, 3, None)]
2215
2216
sage: S = SimplicialComplex(3,[[1,2,3],[1]],maximality_check=False)
2217
sage: G = S.graph()
2218
sage: G.is_connected()
2219
False
2220
sage: G.vertices() #random order
2221
[1, 2, 3, (1,)]
2222
sage: G.edges()
2223
[(1, 2, None), (1, 3, None), (2, 3, None)]
2224
2225
"""
2226
if self._graph is None:
2227
edges = self.n_faces(1)
2228
vertices = filter(lambda f: f.dimension() == 0, self._facets)
2229
used_vertices = [] # vertices which are in an edge
2230
d = {}
2231
for e in edges:
2232
v = min(e)
2233
if v in d:
2234
d[v].append(max(e))
2235
else:
2236
d[v] = [max(e)]
2237
used_vertices.extend(list(e))
2238
for v in vertices:
2239
if v not in used_vertices:
2240
d[v] = []
2241
self._graph = Graph(d)
2242
return self._graph
2243
2244
def delta_complex(self, sort_simplices=False):
2245
r"""
2246
Returns ``self`` as a `\Delta`-complex. The `\Delta`-complex
2247
is essentially identical to the simplicial complex: it has
2248
same simplices with the same boundaries.
2249
2250
:param sort_simplices: if True, sort the list of simplices in
2251
each dimension
2252
:type sort_simplices: boolean; optional, default False
2253
2254
EXAMPLES::
2255
2256
sage: T = simplicial_complexes.Torus()
2257
sage: Td = T.delta_complex()
2258
sage: Td
2259
Delta complex with 7 vertices and 43 simplices
2260
sage: T.homology() == Td.homology()
2261
True
2262
"""
2263
from delta_complex import DeltaComplex
2264
data = {}
2265
dim = self.dimension()
2266
n_cells = self.n_cells(dim)
2267
if sort_simplices:
2268
n_cells.sort()
2269
for n in range(dim, -1, -1):
2270
bdries = self.n_cells(n-1)
2271
if sort_simplices:
2272
bdries.sort()
2273
data[n] = []
2274
for f in n_cells:
2275
data[n].append([bdries.index(f.face(i)) for i in range(n+1)])
2276
n_cells = bdries
2277
return DeltaComplex(data)
2278
2279
def is_flag_complex(self):
2280
"""
2281
Returns True if and only if self is a flag complex.
2282
2283
A flag complex is a simplicial complex that is the largest simplicial complex on
2284
its 1-skeleton. Thus a flag complex is the clique complex of its graph.
2285
2286
EXAMPLES::
2287
2288
sage: h = Graph({0:[1,2,3,4],1:[2,3,4],2:[3]})
2289
sage: x = h.clique_complex()
2290
sage: x
2291
Simplicial complex with vertex set (0, 1, 2, 3, 4) and facets {(0, 1, 4), (0, 1, 2, 3)}
2292
sage: x.is_flag_complex()
2293
True
2294
2295
sage: X = simplicial_complexes.ChessboardComplex(3,3)
2296
sage: X.is_flag_complex()
2297
True
2298
"""
2299
return self==self.graph().clique_complex()
2300
2301
def is_connected(self):
2302
"""
2303
Returns True if and only if self is connected.
2304
2305
.. warning::
2306
2307
This may give the wrong answer if the simplicial complex
2308
was constructed with ``maximality_check`` set to False.
2309
See the final example.
2310
2311
EXAMPLES::
2312
2313
sage: V = SimplicialComplex([0,1,2,3],[[0,1,2],[3]])
2314
sage: V
2315
Simplicial complex with vertex set (0, 1, 2, 3) and facets {(0, 1, 2), (3,)}
2316
sage: V.is_connected()
2317
False
2318
2319
sage: X = SimplicialComplex([0,1,2,3],[[0,1,2]])
2320
sage: X.is_connected()
2321
True
2322
2323
sage: U = simplicial_complexes.ChessboardComplex(3,3)
2324
sage: U.is_connected()
2325
True
2326
2327
sage: W = simplicial_complexes.Sphere(3)
2328
sage: W.is_connected()
2329
True
2330
2331
sage: S = SimplicialComplex(4,[[0,1],[2,3]])
2332
sage: S.is_connected()
2333
False
2334
2335
sage: S = SimplicialComplex(2,[[0,1],[1],[0]],maximality_check=False)
2336
sage: S.is_connected()
2337
False
2338
"""
2339
return self.graph().is_connected()
2340
2341
def n_skeleton(self, n):
2342
"""
2343
The `n`-skeleton of this simplicial complex: the simplicial
2344
complex obtained by discarding all of the simplices in
2345
dimensions larger than `n`.
2346
2347
:param n: non-negative integer
2348
2349
EXAMPLES::
2350
2351
sage: X = SimplicialComplex(3, [[0,1], [1,2,3], [0,2,3]])
2352
sage: X.n_skeleton(1)
2353
Simplicial complex with vertex set (0, 1, 2, 3) and facets {(2, 3), (0, 2), (1, 3), (1, 2), (0, 3), (0, 1)}
2354
sage: X.n_skeleton(2)
2355
Simplicial complex with vertex set (0, 1, 2, 3) and facets {(0, 2, 3), (1, 2, 3), (0, 1)}
2356
"""
2357
facets = filter(lambda f: f.dimension()<n, self._facets)
2358
facets.extend(self.n_faces(n))
2359
return SimplicialComplex(self.vertices(), facets)
2360
2361
def _contractible_subcomplex(self, verbose=False):
2362
"""
2363
Find a contractible subcomplex `L` of this simplicial complex,
2364
preferably one which is as large as possible.
2365
2366
:param verbose: If True, print some messages as the simplicial
2367
complex is computed.
2368
:type verbose: boolean; optional, default False
2369
2370
Motivation: if `K` is the original complex and if `L` is
2371
contractible, then the relative homology `H_*(K,L)` is
2372
isomorphic to the reduced homology of `K`. If `L` is large,
2373
then the relative chain complex will be a good deal smaller
2374
than the augmented chain complex for `K`, and this leads to a
2375
speed improvement for computing the homology of `K`.
2376
2377
This just passes a subcomplex consisting of a facet to the
2378
method ``_enlarge_subcomplex``.
2379
2380
.. note::
2381
2382
Thus when the simplicial complex is empty, so is the
2383
resulting 'contractible subcomplex', which is therefore not
2384
technically contractible. In this case, that doesn't
2385
matter because the homology is computed correctly anyway.
2386
2387
EXAMPLES::
2388
2389
sage: disk = SimplicialComplex(3, [[0,1,2,3]])
2390
sage: sphere = disk.remove_face([0,1,2,3])
2391
sage: sphere
2392
Simplicial complex with vertex set (0, 1, 2, 3) and facets {(0, 2, 3), (0, 1, 2), (1, 2, 3), (0, 1, 3)}
2393
sage: L = sphere._contractible_subcomplex(); L
2394
Simplicial complex with vertex set (0, 1, 2, 3) and facets {(0, 2, 3), (1, 2, 3), (0, 1, 3)}
2395
sage: L.homology()
2396
{0: 0, 1: 0, 2: 0}
2397
"""
2398
vertices = self.vertices()
2399
facets = [self._facets[0]]
2400
return self._enlarge_subcomplex(SimplicialComplex(vertices, facets), verbose=verbose)
2401
2402
def _enlarge_subcomplex(self, subcomplex, verbose=False):
2403
"""
2404
Given a subcomplex `S` of this simplicial complex `K`, find a
2405
subcomplex `L`, as large as possible, containing `S` which is
2406
homotopy equivalent to `S` (so that `H_{*}(K,S)` is isomorphic
2407
to `H_{*}(K,L)`). This way, the chain complex for computing
2408
`H_{*}(K,L)` will be smaller than that for computing
2409
`H_{*}(K,S)`, so the computations should be faster.
2410
2411
:param subcomplex: a subcomplex of this simplicial complex
2412
:param verbose: If True, print some messages as the simplicial
2413
complex is computed.
2414
:type verbose: boolean; optional, default False
2415
:return: a complex `L` containing ``subcomplex`` and contained
2416
in ``self``, homotopy equivalent to ``subcomplex``.
2417
2418
Algorithm: start with the subcomplex `S` and loop through the
2419
facets of `K` which are not in `S`. For each one, see whether
2420
its intersection with `S` is contractible, and if so, add it.
2421
This is recursive: testing for contractibility calls this
2422
routine again, via ``_contractible_subcomplex``.
2423
2424
EXAMPLES::
2425
2426
sage: T = simplicial_complexes.Torus(); T
2427
Simplicial complex with vertex set (0, 1, 2, 3, 4, 5, 6) and 14 facets
2428
2429
Inside the torus, define a subcomplex consisting of a loop::
2430
2431
sage: S = SimplicialComplex(T.vertices(), [[0,1], [1,2], [0,2]])
2432
sage: S.homology()
2433
{0: 0, 1: Z}
2434
sage: L = T._enlarge_subcomplex(S)
2435
sage: L
2436
Simplicial complex with vertex set (0, 1, 2, 3, 4, 5, 6) and 8 facets
2437
sage: L.facets()
2438
{(0, 1, 5), (1, 3, 6), (1, 2), (1, 2, 4), (1, 3, 4), (0, 2), (1, 5, 6), (0, 1)}
2439
sage: L.homology()[1]
2440
Z
2441
"""
2442
if subcomplex in self.__enlarged:
2443
return self.__enlarged[subcomplex]
2444
faces = filter(lambda x: x not in subcomplex._facets, self._facets)
2445
done = False
2446
new_facets = list(subcomplex._facets)
2447
while not done:
2448
done = True
2449
remove_these = []
2450
if verbose:
2451
print " looping through %s facets" % len(faces)
2452
for f in faces:
2453
f_set = f.set()
2454
int_facets = []
2455
for a in new_facets:
2456
int_facets.append(a.set().intersection(f_set))
2457
intersection = SimplicialComplex(self.vertices(), int_facets)
2458
if not intersection._facets[0].is_empty():
2459
if (len(intersection._facets) == 1 or
2460
intersection == intersection._contractible_subcomplex()):
2461
new_facets.append(f)
2462
remove_these.append(f)
2463
done = False
2464
if verbose and not done:
2465
print " added %s facets" % len(remove_these)
2466
for f in remove_these:
2467
faces.remove(f)
2468
if verbose:
2469
print " now constructing a simplicial complex with %s vertices and %s facets" % (self.vertices().dimension()+1, len(new_facets))
2470
L = SimplicialComplex(self.vertices(), new_facets, maximality_check=False,
2471
vertex_check=False, sort_facets=False)
2472
self.__enlarged[subcomplex] = L
2473
return L
2474
2475
def _cubical_(self):
2476
r"""
2477
Cubical complex constructed from self.
2478
2479
ALGORITHM:
2480
2481
The algorithm comes from a paper by Shtan'ko and Shtogrin, as
2482
reported by Bukhshtaber and Panov. Let `I^m` denote the unit
2483
`m`-cube, viewed as a cubical complex. Let `[m] = \{1, 2,
2484
..., m\}`; then each face of `I^m` has the following form, for
2485
subsets `I \subset J \subset [m]`:
2486
2487
.. math::
2488
2489
F_{I \subset J} = \{ (y_1,...,y_m) \in I^m \,:\, y_i =0 \text{
2490
for } i \in I, y_j = 1 \text{ for } j \not \in J\}.
2491
2492
If `K` is a simplicial complex on vertex set `[m]` and if `I
2493
\subset [m]`, write `I \in K` if `I` is a simplex of `K`.
2494
Then we associate to `K` the cubical subcomplex of `I^m` with
2495
faces
2496
2497
.. math::
2498
2499
\{F_{I \subset J} \,:\, J \in K, I \neq \emptyset \}
2500
2501
The geometric realization of this cubical complex is
2502
homeomorphic to the geometric realization of the original
2503
simplicial complex.
2504
2505
REFERENCES:
2506
2507
- V. M. Bukhshtaber and T. E. Panov, "Moment-angle complexes
2508
and combinatorics of simplicial manifolds," *Uspekhi
2509
Mat. Nauk* 55 (2000), 171--172.
2510
2511
- M. A. Shtan'ko and and M. I. Shtogrin, "Embedding cubic
2512
manifolds and complexes into a cubic lattice", *Uspekhi
2513
Mat. Nauk* 47 (1992), 219-220.
2514
2515
EXAMPLES::
2516
2517
sage: T = simplicial_complexes.Torus()
2518
sage: T.homology()
2519
{0: 0, 1: Z x Z, 2: Z}
2520
sage: Tc = T._cubical_()
2521
sage: Tc
2522
Cubical complex with 42 vertices and 168 cubes
2523
sage: Tc.homology()
2524
{0: 0, 1: Z x Z, 2: Z}
2525
"""
2526
from sage.homology.cubical_complex import Cube, CubicalComplex
2527
V = self.vertices()
2528
embed = V.dimension() + 1
2529
# dictionary to translate vertices to the numbers 1, ..., embed
2530
vd = dict(zip(V, range(1, embed + 1)))
2531
cubes = []
2532
for JJ in self.facets():
2533
J = [vd[i] for i in JJ]
2534
for i in J:
2535
# loop over indices from 1 to embed. if equal to i,
2536
# set to 0. if not in J, set to 1. Otherwise, range
2537
# from 0 to 1
2538
cube = []
2539
for n in range(1, embed+1):
2540
if n == i:
2541
cube.append([0,])
2542
elif n not in J:
2543
cube.append([1,])
2544
else:
2545
cube.append([0,1])
2546
cubes.append(cube)
2547
return CubicalComplex(cubes)
2548
2549
def category(self):
2550
"""
2551
Return the category to which this chain complex belongs: the
2552
category of all simplicial complexes.
2553
2554
EXAMPLES::
2555
2556
sage: SimplicialComplex(5, [[0,1], [1,2,3,4,5]]).category()
2557
Category of simplicial complexes
2558
"""
2559
import sage.categories.all
2560
return sage.categories.all.SimplicialComplexes()
2561
2562
def _Hom_(self, other, category=None):
2563
"""
2564
Return the set of simplicial maps between simplicial complexes
2565
``self`` and ``other``.
2566
2567
EXAMPLES::
2568
2569
sage: S = simplicial_complexes.Sphere(1)
2570
sage: T = simplicial_complexes.Sphere(2)
2571
sage: H = Hom(S,T) # indirect doctest
2572
sage: H
2573
Set of Morphisms 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)} in Category of simplicial complexes
2574
sage: f = {0:0,1:1,2:3}
2575
sage: x = H(f)
2576
sage: x
2577
Simplicial complex morphism {0: 0, 1: 1, 2: 3} 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)}
2578
"""
2579
from sage.homology.simplicial_complex_homset import SimplicialComplexHomset
2580
return SimplicialComplexHomset(self, other)
2581
2582
def _chomp_repr_(self):
2583
r"""
2584
String representation of self suitable for use by the CHomP
2585
program. This lists each facet on its own line, and makes
2586
sure vertices are listed as numbers.
2587
2588
EXAMPLES::
2589
2590
sage: S = SimplicialComplex([(0,1,2), (2,3,5)])
2591
sage: print S._chomp_repr_()
2592
(2, 3, 5)
2593
(0, 1, 2)
2594
2595
A simplicial complex whose vertices are tuples, not integers::
2596
2597
::
2598
2599
sage: S = SimplicialComplex([[(0,1), (1,2), (3,4)]])
2600
sage: S._chomp_repr_()
2601
'(0, 1, 2)\n'
2602
"""
2603
s = ""
2604
if not self._numeric:
2605
d = dict(self._numeric_translation)
2606
for f in self.facets():
2607
if self._numeric:
2608
s += str(f)
2609
else:
2610
f_str = "("
2611
for a in f:
2612
f_str += str(d[a])
2613
f_str += ", "
2614
f_str = f_str.replace("'", "")
2615
f_str = f_str.replace('"', "")
2616
s += f_str.rstrip(", ") + ")"
2617
s += "\n"
2618
return s
2619
2620
# this function overrides the standard one for GenericCellComplex,
2621
# because it lists the maximal faces, not the total number of faces.
2622
def _repr_(self):
2623
"""
2624
Print representation. If there are only a few vertices or
2625
faces, they are listed. If there are lots, the number is
2626
given.
2627
2628
EXAMPLES::
2629
2630
sage: X = SimplicialComplex(3, [[0,1], [1,2]])
2631
sage: X._repr_()
2632
'Simplicial complex with vertex set (0, 1, 2, 3) and facets {(1, 2), (0, 1)}'
2633
sage: SimplicialComplex(15)
2634
Simplicial complex with 16 vertices and facets {()}
2635
"""
2636
vertex_limit = 45
2637
facet_limit = 55
2638
vertices = self.vertices()
2639
facets = Set(self._facets)
2640
vertex_string = "with vertex set %s" % vertices
2641
if len(vertex_string) > vertex_limit:
2642
vertex_string = "with %s vertices" % str(1+vertices.dimension())
2643
facet_string = "facets %s" % facets
2644
if len(facet_string) > facet_limit:
2645
facet_string = "%s facets" % len(facets)
2646
return "Simplicial complex " + vertex_string + " and " + facet_string
2647
2648