passagemath-polyhedra 10.6.31rc3__cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of passagemath-polyhedra might be problematic. Click here for more details.

Files changed (206) hide show
  1. passagemath_polyhedra-10.6.31rc3.dist-info/METADATA +367 -0
  2. passagemath_polyhedra-10.6.31rc3.dist-info/METADATA.bak +369 -0
  3. passagemath_polyhedra-10.6.31rc3.dist-info/RECORD +206 -0
  4. passagemath_polyhedra-10.6.31rc3.dist-info/WHEEL +6 -0
  5. passagemath_polyhedra-10.6.31rc3.dist-info/top_level.txt +2 -0
  6. passagemath_polyhedra.libs/libgmp-6e109695.so.10.5.0 +0 -0
  7. passagemath_polyhedra.libs/libgomp-e985bcbb.so.1.0.0 +0 -0
  8. sage/all__sagemath_polyhedra.py +50 -0
  9. sage/game_theory/all.py +8 -0
  10. sage/game_theory/catalog.py +6 -0
  11. sage/game_theory/catalog_normal_form_games.py +923 -0
  12. sage/game_theory/cooperative_game.py +844 -0
  13. sage/game_theory/matching_game.py +1181 -0
  14. sage/game_theory/normal_form_game.py +2697 -0
  15. sage/game_theory/parser.py +275 -0
  16. sage/geometry/all__sagemath_polyhedra.py +22 -0
  17. sage/geometry/cone.py +6940 -0
  18. sage/geometry/cone_catalog.py +847 -0
  19. sage/geometry/cone_critical_angles.py +1027 -0
  20. sage/geometry/convex_set.py +1119 -0
  21. sage/geometry/fan.py +3743 -0
  22. sage/geometry/fan_isomorphism.py +389 -0
  23. sage/geometry/fan_morphism.py +1884 -0
  24. sage/geometry/hasse_diagram.py +202 -0
  25. sage/geometry/hyperplane_arrangement/affine_subspace.py +390 -0
  26. sage/geometry/hyperplane_arrangement/all.py +1 -0
  27. sage/geometry/hyperplane_arrangement/arrangement.py +3895 -0
  28. sage/geometry/hyperplane_arrangement/check_freeness.py +145 -0
  29. sage/geometry/hyperplane_arrangement/hyperplane.py +773 -0
  30. sage/geometry/hyperplane_arrangement/library.py +825 -0
  31. sage/geometry/hyperplane_arrangement/ordered_arrangement.py +642 -0
  32. sage/geometry/hyperplane_arrangement/plot.py +520 -0
  33. sage/geometry/integral_points.py +35 -0
  34. sage/geometry/integral_points_generic_dense.cpython-314-x86_64-linux-gnu.so +0 -0
  35. sage/geometry/integral_points_generic_dense.pyx +7 -0
  36. sage/geometry/lattice_polytope.py +5894 -0
  37. sage/geometry/linear_expression.py +773 -0
  38. sage/geometry/newton_polygon.py +767 -0
  39. sage/geometry/point_collection.cpython-314-x86_64-linux-gnu.so +0 -0
  40. sage/geometry/point_collection.pyx +1008 -0
  41. sage/geometry/polyhedral_complex.py +2616 -0
  42. sage/geometry/polyhedron/all.py +8 -0
  43. sage/geometry/polyhedron/backend_cdd.py +460 -0
  44. sage/geometry/polyhedron/backend_cdd_rdf.py +231 -0
  45. sage/geometry/polyhedron/backend_field.py +347 -0
  46. sage/geometry/polyhedron/backend_normaliz.py +2503 -0
  47. sage/geometry/polyhedron/backend_number_field.py +168 -0
  48. sage/geometry/polyhedron/backend_polymake.py +765 -0
  49. sage/geometry/polyhedron/backend_ppl.py +582 -0
  50. sage/geometry/polyhedron/base.py +1206 -0
  51. sage/geometry/polyhedron/base0.py +1444 -0
  52. sage/geometry/polyhedron/base1.py +886 -0
  53. sage/geometry/polyhedron/base2.py +812 -0
  54. sage/geometry/polyhedron/base3.py +1845 -0
  55. sage/geometry/polyhedron/base4.py +1262 -0
  56. sage/geometry/polyhedron/base5.py +2700 -0
  57. sage/geometry/polyhedron/base6.py +1741 -0
  58. sage/geometry/polyhedron/base7.py +997 -0
  59. sage/geometry/polyhedron/base_QQ.py +1258 -0
  60. sage/geometry/polyhedron/base_RDF.py +98 -0
  61. sage/geometry/polyhedron/base_ZZ.py +934 -0
  62. sage/geometry/polyhedron/base_mutable.py +215 -0
  63. sage/geometry/polyhedron/base_number_field.py +122 -0
  64. sage/geometry/polyhedron/cdd_file_format.py +155 -0
  65. sage/geometry/polyhedron/combinatorial_polyhedron/all.py +1 -0
  66. sage/geometry/polyhedron/combinatorial_polyhedron/base.cpython-314-x86_64-linux-gnu.so +0 -0
  67. sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +76 -0
  68. sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +3859 -0
  69. sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.cpython-314-x86_64-linux-gnu.so +0 -0
  70. sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +39 -0
  71. sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +1038 -0
  72. sage/geometry/polyhedron/combinatorial_polyhedron/conversions.cpython-314-x86_64-linux-gnu.so +0 -0
  73. sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd +9 -0
  74. sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx +501 -0
  75. sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd +207 -0
  76. sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.cpython-314-x86_64-linux-gnu.so +0 -0
  77. sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +102 -0
  78. sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +2274 -0
  79. sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.cpython-314-x86_64-linux-gnu.so +0 -0
  80. sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd +370 -0
  81. sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pyx +84 -0
  82. sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.cpython-314-x86_64-linux-gnu.so +0 -0
  83. sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd +31 -0
  84. sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +587 -0
  85. sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.cpython-314-x86_64-linux-gnu.so +0 -0
  86. sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd +52 -0
  87. sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +560 -0
  88. sage/geometry/polyhedron/constructor.py +773 -0
  89. sage/geometry/polyhedron/double_description.py +753 -0
  90. sage/geometry/polyhedron/double_description_inhomogeneous.py +564 -0
  91. sage/geometry/polyhedron/face.py +1060 -0
  92. sage/geometry/polyhedron/generating_function.py +1810 -0
  93. sage/geometry/polyhedron/lattice_euclidean_group_element.py +178 -0
  94. sage/geometry/polyhedron/library.py +3502 -0
  95. sage/geometry/polyhedron/misc.py +121 -0
  96. sage/geometry/polyhedron/modules/all.py +1 -0
  97. sage/geometry/polyhedron/modules/formal_polyhedra_module.py +155 -0
  98. sage/geometry/polyhedron/palp_database.py +447 -0
  99. sage/geometry/polyhedron/parent.py +1279 -0
  100. sage/geometry/polyhedron/plot.py +1986 -0
  101. sage/geometry/polyhedron/ppl_lattice_polygon.py +556 -0
  102. sage/geometry/polyhedron/ppl_lattice_polytope.py +1257 -0
  103. sage/geometry/polyhedron/representation.py +1723 -0
  104. sage/geometry/pseudolines.py +515 -0
  105. sage/geometry/relative_interior.py +445 -0
  106. sage/geometry/toric_plotter.py +1103 -0
  107. sage/geometry/triangulation/all.py +2 -0
  108. sage/geometry/triangulation/base.cpython-314-x86_64-linux-gnu.so +0 -0
  109. sage/geometry/triangulation/base.pyx +963 -0
  110. sage/geometry/triangulation/data.h +147 -0
  111. sage/geometry/triangulation/data.pxd +4 -0
  112. sage/geometry/triangulation/element.py +914 -0
  113. sage/geometry/triangulation/functions.h +10 -0
  114. sage/geometry/triangulation/functions.pxd +4 -0
  115. sage/geometry/triangulation/point_configuration.py +2256 -0
  116. sage/geometry/triangulation/triangulations.h +49 -0
  117. sage/geometry/triangulation/triangulations.pxd +7 -0
  118. sage/geometry/voronoi_diagram.py +319 -0
  119. sage/interfaces/all__sagemath_polyhedra.py +1 -0
  120. sage/interfaces/polymake.py +2028 -0
  121. sage/numerical/all.py +13 -0
  122. sage/numerical/all__sagemath_polyhedra.py +11 -0
  123. sage/numerical/backends/all.py +1 -0
  124. sage/numerical/backends/all__sagemath_polyhedra.py +1 -0
  125. sage/numerical/backends/cvxopt_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  126. sage/numerical/backends/cvxopt_backend.pyx +1006 -0
  127. sage/numerical/backends/cvxopt_backend_test.py +19 -0
  128. sage/numerical/backends/cvxopt_sdp_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  129. sage/numerical/backends/cvxopt_sdp_backend.pyx +382 -0
  130. sage/numerical/backends/cvxpy_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  131. sage/numerical/backends/cvxpy_backend.pxd +41 -0
  132. sage/numerical/backends/cvxpy_backend.pyx +934 -0
  133. sage/numerical/backends/cvxpy_backend_test.py +13 -0
  134. sage/numerical/backends/generic_backend_test.py +24 -0
  135. sage/numerical/backends/interactivelp_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  136. sage/numerical/backends/interactivelp_backend.pxd +36 -0
  137. sage/numerical/backends/interactivelp_backend.pyx +1231 -0
  138. sage/numerical/backends/interactivelp_backend_test.py +12 -0
  139. sage/numerical/backends/logging_backend.py +391 -0
  140. sage/numerical/backends/matrix_sdp_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  141. sage/numerical/backends/matrix_sdp_backend.pxd +15 -0
  142. sage/numerical/backends/matrix_sdp_backend.pyx +478 -0
  143. sage/numerical/backends/ppl_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  144. sage/numerical/backends/ppl_backend.pyx +1126 -0
  145. sage/numerical/backends/ppl_backend_test.py +13 -0
  146. sage/numerical/backends/scip_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  147. sage/numerical/backends/scip_backend.pxd +22 -0
  148. sage/numerical/backends/scip_backend.pyx +1289 -0
  149. sage/numerical/backends/scip_backend_test.py +13 -0
  150. sage/numerical/interactive_simplex_method.py +5338 -0
  151. sage/numerical/knapsack.py +665 -0
  152. sage/numerical/linear_functions.cpython-314-x86_64-linux-gnu.so +0 -0
  153. sage/numerical/linear_functions.pxd +31 -0
  154. sage/numerical/linear_functions.pyx +1648 -0
  155. sage/numerical/linear_tensor.py +470 -0
  156. sage/numerical/linear_tensor_constraints.py +448 -0
  157. sage/numerical/linear_tensor_element.cpython-314-x86_64-linux-gnu.so +0 -0
  158. sage/numerical/linear_tensor_element.pxd +6 -0
  159. sage/numerical/linear_tensor_element.pyx +459 -0
  160. sage/numerical/mip.cpython-314-x86_64-linux-gnu.so +0 -0
  161. sage/numerical/mip.pxd +40 -0
  162. sage/numerical/mip.pyx +3667 -0
  163. sage/numerical/sdp.cpython-314-x86_64-linux-gnu.so +0 -0
  164. sage/numerical/sdp.pxd +39 -0
  165. sage/numerical/sdp.pyx +1433 -0
  166. sage/rings/all__sagemath_polyhedra.py +3 -0
  167. sage/rings/polynomial/all__sagemath_polyhedra.py +10 -0
  168. sage/rings/polynomial/omega.py +982 -0
  169. sage/schemes/all__sagemath_polyhedra.py +2 -0
  170. sage/schemes/toric/all.py +10 -0
  171. sage/schemes/toric/chow_group.py +1248 -0
  172. sage/schemes/toric/divisor.py +2082 -0
  173. sage/schemes/toric/divisor_class.cpython-314-x86_64-linux-gnu.so +0 -0
  174. sage/schemes/toric/divisor_class.pyx +322 -0
  175. sage/schemes/toric/fano_variety.py +1606 -0
  176. sage/schemes/toric/homset.py +650 -0
  177. sage/schemes/toric/ideal.py +451 -0
  178. sage/schemes/toric/library.py +1322 -0
  179. sage/schemes/toric/morphism.py +1958 -0
  180. sage/schemes/toric/points.py +1032 -0
  181. sage/schemes/toric/sheaf/all.py +1 -0
  182. sage/schemes/toric/sheaf/constructor.py +302 -0
  183. sage/schemes/toric/sheaf/klyachko.py +921 -0
  184. sage/schemes/toric/toric_subscheme.py +905 -0
  185. sage/schemes/toric/variety.py +3460 -0
  186. sage/schemes/toric/weierstrass.py +1078 -0
  187. sage/schemes/toric/weierstrass_covering.py +457 -0
  188. sage/schemes/toric/weierstrass_higher.py +288 -0
  189. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.info +10 -0
  190. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v03 +0 -0
  191. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v04 +0 -0
  192. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v05 +1 -0
  193. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v06 +1 -0
  194. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.info +22 -0
  195. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v04 +0 -0
  196. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v05 +0 -0
  197. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v06 +0 -0
  198. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v07 +0 -0
  199. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v08 +0 -0
  200. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v09 +0 -0
  201. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v10 +0 -0
  202. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v11 +1 -0
  203. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v12 +1 -0
  204. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v13 +1 -0
  205. sage_wheels/share/reflexive_polytopes/reflexive_polytopes_2d +80 -0
  206. sage_wheels/share/reflexive_polytopes/reflexive_polytopes_3d +37977 -0
@@ -0,0 +1,2256 @@
1
+ # sage_setup: distribution = sagemath-polyhedra
2
+ r"""
3
+ Triangulations of a point configuration
4
+
5
+ A point configuration is a finite set of points in Euclidean space or,
6
+ more generally, in projective space. A triangulation is a simplicial
7
+ decomposition of the convex hull of a given point configuration such
8
+ that all vertices of the simplices end up lying on points of the
9
+ configuration. That is, there are no new vertices apart from the
10
+ initial points.
11
+
12
+ Note that points that are not vertices of the convex hull need not be
13
+ used in the triangulation. A triangulation that does make use of all
14
+ points of the configuration is called fine, and you can restrict
15
+ yourself to such triangulations if you want. See
16
+ :class:`PointConfiguration` and
17
+ :meth:`~PointConfiguration.restrict_to_fine_triangulations` for
18
+ more details.
19
+
20
+ Finding a single triangulation and listing all connected
21
+ triangulations is implemented natively in this package. However, for
22
+ more advanced options [TOPCOM]_ needs to be installed; see :ref:`spkg_topcom`.
23
+
24
+ .. NOTE::
25
+
26
+ TOPCOM and the internal algorithms tend to enumerate
27
+ triangulations in a different order. This is why we always
28
+ explicitly specify the engine as ``engine='topcom'`` or
29
+ ``engine='internal'`` in the doctests. In your own applications,
30
+ you do not need to specify the engine. By default, TOPCOM is used
31
+ if it is available and the internal algorithms are used otherwise.
32
+
33
+ EXAMPLES:
34
+
35
+ First, we select the internal implementation for enumerating
36
+ triangulations::
37
+
38
+ sage: PointConfiguration.set_engine('internal') # to make doctests independent of TOPCOM
39
+
40
+ A 2-dimensional point configuration::
41
+
42
+ sage: p = PointConfiguration([[0,0], [0,1], [1,0], [1,1], [-1,-1]]); p
43
+ A point configuration in affine 2-space over Integer Ring consisting
44
+ of 5 points. The triangulations of this point configuration are
45
+ assumed to be connected, not necessarily fine, not necessarily regular.
46
+
47
+ .. PLOT::
48
+ :width: 300 px
49
+
50
+ p = PointConfiguration([[-1,-1], [1,1], [1,0], [0,1], [0,0]])
51
+ sphinx_plot(p.plot(axes=False))
52
+
53
+ A triangulation of it::
54
+
55
+ sage: t = p.triangulate(); t # a single triangulation
56
+ (<1,3,4>, <2,3,4>)
57
+ sage: len(t)
58
+ 2
59
+ sage: t[0]
60
+ (1, 3, 4)
61
+ sage: t[1]
62
+ (2, 3, 4)
63
+ sage: list(t)
64
+ [(1, 3, 4), (2, 3, 4)]
65
+ sage: t.plot(axes=False) # needs sage.plot
66
+ Graphics object consisting of 12 graphics primitives
67
+
68
+ .. PLOT::
69
+ :width: 300 px
70
+
71
+ p = PointConfiguration([[0,0], [0,1], [1,0], [1,1], [-1,-1]])
72
+ t = p.triangulate()
73
+ sphinx_plot(t.plot(axes=False))
74
+
75
+ List triangulations of it::
76
+
77
+ sage: list(p.triangulations())
78
+ [(<1,3,4>, <2,3,4>),
79
+ (<0,1,3>, <0,1,4>, <0,2,3>, <0,2,4>),
80
+ (<1,2,3>, <1,2,4>),
81
+ (<0,1,2>, <0,1,4>, <0,2,4>, <1,2,3>)]
82
+ sage: p_fine = p.restrict_to_fine_triangulations(); p_fine
83
+ A point configuration in affine 2-space over Integer Ring consisting
84
+ of 5 points. The triangulations of this point configuration are
85
+ assumed to be connected, fine, not necessarily regular.
86
+ sage: list(p_fine.triangulations())
87
+ [(<0,1,3>, <0,1,4>, <0,2,3>, <0,2,4>),
88
+ (<0,1,2>, <0,1,4>, <0,2,4>, <1,2,3>)]
89
+
90
+ A 3-dimensional point configuration::
91
+
92
+ sage: p = [[0,-1,-1], [0,0,1], [0,1,0], [1,-1,-1], [1,0,1], [1,1,0]]
93
+ sage: points = PointConfiguration(p)
94
+ sage: triang = points.triangulate()
95
+ sage: triang.plot(axes=False) # needs sage.plot
96
+ Graphics3d Object
97
+
98
+ .. PLOT::
99
+ :width: 300 px
100
+
101
+ p = [[0,-1,-1], [0,0,1], [0,1,0], [1,-1,-1], [1,0,1], [1,1,0]]
102
+ points = PointConfiguration(p)
103
+ triang = points.triangulate()
104
+ sphinx_plot(triang.plot(axes=False))
105
+
106
+ The standard example of a non-regular triangulation (requires TOPCOM)::
107
+
108
+ sage: # optional - topcom
109
+ sage: PointConfiguration.set_engine('topcom')
110
+ sage: p = PointConfiguration([[-1,-5/9], [0,10/9], [1,-5/9],
111
+ ....: [-2,-10/9], [0,20/9], [2,-10/9]])
112
+ sage: p_regular = p.restrict_to_regular_triangulations(True)
113
+ sage: regular = p_regular.triangulations_list()
114
+ sage: p_nonregular = p.restrict_to_regular_triangulations(False)
115
+ sage: nonregular = p_nonregular.triangulations_list()
116
+ sage: len(regular)
117
+ 16
118
+ sage: len(nonregular)
119
+ 2
120
+ sage: nonregular[0].plot(aspect_ratio=1, axes=False) # needs sage.plot
121
+ Graphics object consisting of 25 graphics primitives
122
+ sage: PointConfiguration.set_engine('internal') # to make doctests independent of TOPCOM
123
+
124
+ Note that the points need not be in general position. That is, the
125
+ points may lie in a hyperplane and the linear dependencies will be
126
+ removed before passing the data to TOPCOM which cannot handle it::
127
+
128
+ sage: points = [[0,0,0,1], [0,3,0,1], [3,0,0,1], [0,0,1,1],
129
+ ....: [0,3,1,1], [3,0,1,1], [1,1,2,1]]
130
+ sage: points = [p + [1,2,3] for p in points]
131
+ sage: pc = PointConfiguration(points)
132
+ sage: pc.ambient_dim()
133
+ 7
134
+ sage: pc.dim()
135
+ 3
136
+ sage: pc.triangulate()
137
+ (<0,1,2,6>, <0,1,3,6>, <0,2,3,6>, <1,2,4,6>, <1,3,4,6>, <2,3,5,6>, <2,4,5,6>)
138
+ sage: _ in pc.triangulations()
139
+ True
140
+ sage: len(pc.triangulations_list())
141
+ 26
142
+
143
+ AUTHORS:
144
+
145
+ - Volker Braun: initial version, 2010
146
+
147
+ - Josh Whitney: added functionality for computing
148
+ volumes and secondary polytopes of PointConfigurations
149
+
150
+ - Marshall Hampton: improved documentation and doctest coverage
151
+
152
+ - Volker Braun: rewrite using Parent/Element and categories. Added
153
+ a Point class. More doctests. Less zombies.
154
+
155
+ - Volker Braun: Cythonized parts of it, added a C++ implementation
156
+ of the bistellar flip algorithm to enumerate all connected
157
+ triangulations.
158
+
159
+ - Volker Braun 2011: switched the triangulate() method to the
160
+ placing triangulation (faster).
161
+ """
162
+
163
+ ########################################################################
164
+ # Note: The doctests that make use of TOPCOM are
165
+ # marked # optional - topcom
166
+ # If you have it installed, run doctests as
167
+ #
168
+ # sage -tp 4 --long --optional=sage,topcom sage/geometry/triangulation/
169
+ ########################################################################
170
+
171
+
172
+ ########################################################################
173
+ # Copyright (C) 2010 Volker Braun <vbraun.name@gmail.com>
174
+ # Copyright (C) 2010 Josh Whitney <josh.r.whitney@gmail.com>
175
+ # Copyright (C) 2010 Marshall Hampton <hamptonio@gmail.com>
176
+ #
177
+ # Distributed under the terms of the GNU General Public License (GPL)
178
+ #
179
+ # https://www.gnu.org/licenses/
180
+ ########################################################################
181
+
182
+ import itertools
183
+ from copy import copy
184
+ import sys
185
+
186
+ from sage.features import FeatureNotPresentError
187
+ from sage.features.topcom import TOPCOMExecutable
188
+ from sage.matrix.constructor import matrix
189
+ from sage.misc.cachefunc import cached_method
190
+ from sage.modules.free_module_element import vector
191
+ from sage.rings.integer_ring import ZZ
192
+ from sage.rings.rational_field import QQ
193
+ from sage.structure.unique_representation import UniqueRepresentation
194
+
195
+ from sage.geometry.triangulation.base import \
196
+ PointConfiguration_base, Point, ConnectedTriangulationsIterator
197
+
198
+ from sage.geometry.triangulation.element import Triangulation
199
+
200
+
201
+ ########################################################################
202
+ class PointConfiguration(UniqueRepresentation, PointConfiguration_base):
203
+ """
204
+ A collection of points in Euclidean (or projective) space.
205
+
206
+ This is the parent class for the triangulations of the point
207
+ configuration. There are a few options to specifically select what
208
+ kind of triangulations are admissible.
209
+
210
+ INPUT:
211
+
212
+ The constructor accepts the following arguments:
213
+
214
+ - ``points`` -- the points; technically, any iterable of iterables
215
+ will do. In particular, a :class:`PointConfiguration` can be passed.
216
+
217
+ - ``projective`` -- boolean (default: ``False``); whether the
218
+ point coordinates should be interpreted as projective (``True``)
219
+ or affine (``False``) coordinates. If necessary, points are
220
+ projectivized by setting the last homogeneous coordinate to one
221
+ and/or affine patches are chosen internally.
222
+
223
+ - ``connected`` -- boolean (default: ``True``); whether the
224
+ triangulations should be connected to the regular triangulations
225
+ via bistellar flips. These are much easier to compute than all
226
+ triangulations.
227
+
228
+ - ``fine`` -- boolean (default: ``False``); whether the
229
+ triangulations must be fine, that is, make use of all points of
230
+ the configuration
231
+
232
+ - ``regular`` -- boolean or ``None`` (default: ``None``); whether
233
+ the triangulations must be regular. A regular triangulation is
234
+ one that is induced by a piecewise-linear convex support
235
+ function. In other words, the shadows of the faces of a
236
+ polyhedron in one higher dimension.
237
+
238
+ * ``True``: Only regular triangulations.
239
+
240
+ * ``False``: Only non-regular triangulations.
241
+
242
+ * ``None`` (default): Both kinds of triangulation.
243
+
244
+ - ``star`` -- either ``None`` or a point; whether the
245
+ triangulations must be star. A triangulation is star if all
246
+ maximal simplices contain a common point. The central point can
247
+ be specified by its index (an integer) in the given points or by
248
+ its coordinates (anything iterable.)
249
+
250
+ EXAMPLES::
251
+
252
+ sage: p = PointConfiguration([[0,0], [0,1], [1,0], [1,1], [-1,-1]]); p
253
+ A point configuration in affine 2-space over Integer Ring
254
+ consisting of 5 points. The triangulations of this point
255
+ configuration are assumed to be connected, not necessarily fine,
256
+ not necessarily regular.
257
+ sage: p.triangulate() # a single triangulation
258
+ (<1,3,4>, <2,3,4>)
259
+ """
260
+
261
+ # we cache the output of _have_TOPCOM() in this class variable
262
+ _have_TOPCOM_cached = None
263
+
264
+ # whether to use TOPCOM. Will be set to True or False during
265
+ # initialization. All implementations should check this boolean
266
+ # variable to decide whether to call TOPCOM or not
267
+ _use_TOPCOM = None
268
+
269
+ @classmethod
270
+ def _have_TOPCOM(cls):
271
+ r"""
272
+ Return whether TOPCOM is installed.
273
+
274
+ EXAMPLES::
275
+
276
+ sage: PointConfiguration._have_TOPCOM() # optional - topcom
277
+ True
278
+ """
279
+ if PointConfiguration._have_TOPCOM_cached is not None:
280
+ return PointConfiguration._have_TOPCOM_cached
281
+
282
+ try:
283
+ import pexpect
284
+ except ImportError:
285
+ return False
286
+
287
+ try:
288
+ out = next(PointConfiguration._TOPCOM_exec('points2placingtriang',
289
+ '[[0,1],[1,1]]', verbose=False))
290
+ PointConfiguration._have_TOPCOM_cached = True
291
+ assert out == '{{0,1}}',\
292
+ 'TOPCOM ran but did not produce the correct output!'
293
+ except (FeatureNotPresentError, pexpect.ExceptionPexpect):
294
+ PointConfiguration._have_TOPCOM_cached = False
295
+
296
+ PointConfiguration.set_engine('auto')
297
+ return PointConfiguration._have_TOPCOM_cached
298
+
299
+ @staticmethod
300
+ def __classcall__(cls, points, projective=False, connected=True, fine=False, regular=None, star=None):
301
+ r"""
302
+ Normalize the constructor arguments to be unique keys.
303
+
304
+ EXAMPLES::
305
+
306
+ sage: pc1 = PointConfiguration([[1,2], [2,3], [3,4]], connected=True)
307
+ sage: pc2 = PointConfiguration(((1,2), (2,3), (3,4)), regular=None)
308
+ sage: pc1 is pc2 # indirect doctest
309
+ True
310
+ """
311
+ if isinstance(points, PointConfiguration_base):
312
+ pc = points
313
+ points = tuple( p.projective() for p in points )
314
+ projective = True
315
+ defined_affine = pc.is_affine()
316
+ elif projective:
317
+ points = tuple( tuple(p) for p in points )
318
+ defined_affine = False
319
+ else:
320
+ points = tuple( tuple(p)+(1,) for p in points )
321
+ defined_affine = True
322
+ if star is not None and star not in ZZ:
323
+ star_point = tuple(star)
324
+ if len(star_point) < len(points[0]):
325
+ star_point = tuple(star)+(1,)
326
+ star = points.index(star_point)
327
+ return super().__classcall__(cls, points, connected, fine,
328
+ regular, star, defined_affine)
329
+
330
+ def __init__(self, points, connected, fine, regular, star, defined_affine):
331
+ """
332
+ Initialize a :class:`PointConfiguration` object.
333
+
334
+ EXAMPLES::
335
+
336
+ sage: p = PointConfiguration([[0,4], [2,3], [3,2], [4,0], [3,-2], [2,-3],
337
+ ....: [0,-4], [-2,-3], [-3,-2], [-4,0], [-3,2], [-2,3]])
338
+ sage: len(p.triangulations_list()) # long time (26s on sage.math, 2012)
339
+ 16796
340
+
341
+ TESTS::
342
+
343
+ sage: TestSuite(p).run()
344
+ """
345
+ # first, test if we have TOPCOM and set up class variables accordingly
346
+ PointConfiguration._have_TOPCOM()
347
+
348
+ assert connected in [True, False], 'Unknown value: connected=' + str(connected)
349
+ self._connected = connected
350
+ if not connected and not PointConfiguration._have_TOPCOM():
351
+ raise ValueError('You must install TOPCOM to find non-connected triangulations.')
352
+
353
+ assert fine in [True, False], 'Unknown value: fine=' + str(fine)
354
+ self._fine = fine
355
+
356
+ assert regular in [True, False, None], 'Unknown value: regular=' + str(regular)
357
+ self._regular = regular
358
+ if regular is not None and not PointConfiguration._have_TOPCOM():
359
+ raise ValueError('You must install TOPCOM to test for regularity.')
360
+
361
+ assert star is None or star in ZZ, 'Unknown value: fine=' + str(star)
362
+ self._star = star
363
+
364
+ PointConfiguration_base.__init__(self, points, defined_affine)
365
+
366
+ @classmethod
367
+ def set_engine(cls, engine='auto'):
368
+ r"""
369
+ Set the engine used to compute triangulations.
370
+
371
+ INPUT:
372
+
373
+ - ``engine`` -- either ``'auto'`` (default), ``'internal'``, or
374
+ ``'topcom'``. The latter two instruct this package to always use
375
+ its own triangulation algorithms or TOPCOM's algorithms,
376
+ respectively. By default (``'auto'``), internal routines are used.
377
+
378
+ EXAMPLES::
379
+
380
+ sage: # optional - topcom
381
+ sage: p = PointConfiguration([[0,0], [0,1], [1,0], [1,1], [-1,-1]])
382
+ sage: p.set_engine('internal') # to make doctests independent of TOPCOM
383
+ sage: p.triangulate()
384
+ (<1,3,4>, <2,3,4>)
385
+ sage: p.set_engine('topcom')
386
+ sage: p.triangulate()
387
+ (<0,1,2>, <0,1,4>, <0,2,4>, <1,2,3>)
388
+ sage: p.set_engine('internal')
389
+ """
390
+ engine = engine.lower()
391
+ if engine not in ['auto', 'topcom', 'internal']:
392
+ raise ValueError('Unknown value for "engine": '+str(engine))
393
+
394
+ PointConfiguration._use_TOPCOM = (engine == 'topcom')
395
+
396
+ def star_center(self):
397
+ r"""
398
+ Return the center used for star triangulations.
399
+
400
+ .. SEEALSO:: :meth:`restrict_to_star_triangulations`.
401
+
402
+ OUTPUT:
403
+
404
+ A :class:`~sage.geometry.triangulation.base.Point` if a
405
+ distinguished star central point has been fixed.
406
+ :exc:`ValueError` exception is raised otherwise.
407
+
408
+ EXAMPLES::
409
+
410
+ sage: pc = PointConfiguration([(1,0), (-1,0), (0,1), (0,2)], star=(0,1)); pc
411
+ A point configuration in affine 2-space over Integer Ring
412
+ consisting of 4 points. The triangulations of this point
413
+ configuration are assumed to be connected, not necessarily
414
+ fine, not necessarily regular, and star with center P(0, 1).
415
+ sage: pc.star_center()
416
+ P(0, 1)
417
+
418
+ sage: pc_nostar = pc.restrict_to_star_triangulations(None); pc_nostar
419
+ A point configuration in affine 2-space over Integer Ring
420
+ consisting of 4 points. The triangulations of this point
421
+ configuration are assumed to be connected, not necessarily
422
+ fine, not necessarily regular.
423
+ sage: pc_nostar.star_center()
424
+ Traceback (most recent call last):
425
+ ...
426
+ ValueError: The point configuration has no star center defined.
427
+ """
428
+ if self._star is None:
429
+ raise ValueError('The point configuration has no star center defined.')
430
+ else:
431
+ return self[self._star]
432
+
433
+ def __reduce__(self):
434
+ r"""
435
+ Override __reduce__ to correctly pickle/unpickle.
436
+
437
+ TESTS::
438
+
439
+ sage: p = PointConfiguration([[0, 1], [0, 0], [1, 0], [1,1]])
440
+ sage: loads(p.dumps()) is p
441
+ True
442
+
443
+ sage: p = PointConfiguration([[0, 1, 1], [0, 0, 1], [1, 0, 1], [1,1, 1]],
444
+ ....: projective=True)
445
+ sage: loads(p.dumps()) is p
446
+ True
447
+ """
448
+ if self.is_affine():
449
+ points = tuple( p.affine() for p in self )
450
+ return (PointConfiguration, (points, False,
451
+ self._connected, self._fine, self._regular, self._star))
452
+ else:
453
+ points = tuple( p.projective() for p in self )
454
+ return (PointConfiguration, (points, True,
455
+ self._connected, self._fine, self._regular, self._star))
456
+
457
+ def an_element(self):
458
+ """
459
+ Synonymous for :meth:`triangulate`.
460
+
461
+ TESTS::
462
+
463
+ sage: p = PointConfiguration([[0, 1], [0, 0], [1, 0], [1,1]])
464
+ sage: p.an_element()
465
+ (<0,1,3>, <1,2,3>)
466
+ """
467
+ return self.triangulate()
468
+
469
+ def _element_constructor_(self, e):
470
+ """
471
+ Construct a triangulation.
472
+
473
+ TESTS::
474
+
475
+ sage: p = PointConfiguration([[0, 1], [0, 0], [1, 0], [1,1]])
476
+ sage: p._element_constructor_([(0,1,2), (2,3,0)])
477
+ (<0,1,2>, <0,2,3>)
478
+ """
479
+ return self.element_class(e, parent=self)
480
+
481
+ Element = Triangulation
482
+
483
+ def __iter__(self):
484
+ """
485
+ Iterate through the points of the point configuration.
486
+
487
+ OUTPUT:
488
+
489
+ Returns projective coordinates of the points. See also the
490
+ ``PointConfiguration.points()`` method, which returns affine
491
+ coordinates.
492
+
493
+ EXAMPLES::
494
+
495
+ sage: p = PointConfiguration([[1,1], [2,2], [3,3]])
496
+ sage: list(p) # indirect doctest
497
+ [P(1, 1), P(2, 2), P(3, 3)]
498
+ sage: [p[i] for i in range(p.n_points())]
499
+ [P(1, 1), P(2, 2), P(3, 3)]
500
+ sage: list(p.points())
501
+ [P(1, 1), P(2, 2), P(3, 3)]
502
+ sage: [p.point(i) for i in range(p.n_points())]
503
+ [P(1, 1), P(2, 2), P(3, 3)]
504
+ """
505
+ yield from self.points()
506
+
507
+ def _repr_(self):
508
+ r"""
509
+ Return a string representation.
510
+
511
+ TESTS::
512
+
513
+ sage: p = PointConfiguration([[1,1,1], [-1,1,1], [1,-1,1], [-1,-1,1], [1,1,-1],
514
+ ....: [-1,1,-1], [1,-1,-1], [-1,-1,-1], [0,0,0]])
515
+ sage: p._repr_()
516
+ 'A point configuration in affine 3-space over Integer Ring
517
+ consisting of 9 points. The triangulations of this point
518
+ configuration are assumed to be connected, not necessarily
519
+ fine, not necessarily regular.'
520
+
521
+ sage: PointConfiguration([[1, 1, 1], [-1, 1, 1], [1, -1, 1], [-1, -1, 1]],
522
+ ....: projective=True)
523
+ A point configuration in projective 2-space over Integer
524
+ Ring consisting of 4 points. The triangulations of this
525
+ point configuration are assumed to be connected,
526
+ not necessarily fine, not necessarily regular.
527
+ """
528
+ s = 'A point configuration in'
529
+ if self.is_affine():
530
+ s += ' affine'
531
+ else:
532
+ s += ' projective'
533
+ s += " %s-space over %s" % (self.ambient_dim(),self.base_ring())
534
+ if len(self) == 1:
535
+ s += ' consisting of '+str(len(self))+' point. '
536
+ else:
537
+ s += ' consisting of '+str(len(self))+' points. '
538
+
539
+ s += 'The triangulations of this point configuration are assumed to be'
540
+
541
+ if self._connected:
542
+ s += ' connected,'
543
+ else:
544
+ s += ' not necessarily connected,'
545
+
546
+ if self._fine:
547
+ s += ' fine,'
548
+ else:
549
+ s += ' not necessarily fine,'
550
+
551
+ if self._regular:
552
+ s += ' regular'
553
+ elif self._regular is False: # may be False or None, with different meanings
554
+ s += ' irregular'
555
+ else:
556
+ s += ' not necessarily regular'
557
+
558
+ if self._star is None:
559
+ s += '.'
560
+ else:
561
+ s += ', and star with center '+str(self.star_center())+'.'
562
+ if self.n_points() == 0:
563
+ s = 'The pointless empty configuration'
564
+ return s
565
+
566
+ def _TOPCOM_points(self):
567
+ r"""
568
+ Convert the list of input points to a string that can be fed
569
+ to TOPCOM.
570
+
571
+ TESTS::
572
+
573
+ sage: p = PointConfiguration([[1,1,1], [-1,1,1], [1,-1,1], [-1,-1,1], [1,1,-1]])
574
+ sage: p._TOPCOM_points()
575
+ '[[0,0,0,1],[-2,0,0,1],[0,-2,0,1],[-2,-2,0,1],[0,0,-2,1]]'
576
+ """
577
+ s = '['
578
+ s += ','.join([
579
+ '[' + ','.join(map(str,p.reduced_projective())) + ']'
580
+ for p in self ])
581
+ s += ']'
582
+ return s
583
+
584
+ @classmethod
585
+ def _TOPCOM_exec(cls, executable, input_string, verbose=True):
586
+ r"""
587
+ Run TOPCOM.
588
+
589
+ INPUT:
590
+
591
+ - ``executable`` -- string; the name of the executable
592
+
593
+ - ``input_string`` -- string; will be piped into the running
594
+ executable's stdin
595
+
596
+ - ``verbose`` -- boolean; whether to print out the TOPCOM
597
+ interaction
598
+
599
+ TESTS::
600
+
601
+ sage: p = PointConfiguration([[1,1,1], [-1,1,1], [1,-1,1], [-1,-1,1], [1,1,-1]])
602
+ sage: out = p._TOPCOM_exec('points2placingtriang',
603
+ ....: '[[0,0,0,1],[-2,0,0,1],[0,-2,0,1],[-2,-2,0,1],[0,0,-2,1]]',
604
+ ....: verbose=True)
605
+ sage: list(out) # optional - topcom
606
+ #### TOPCOM input ####
607
+ # points2placingtriang
608
+ # [[0,0,0,1],[-2,0,0,1],[0,-2,0,1],[-2,-2,0,1],[0,0,-2,1]]
609
+ #### TOPCOM output ####
610
+ # {{0,1,2,4},{1,2,3,4}}
611
+ #######################
612
+ ['{{0,1,2,4},{1,2,3,4}}']
613
+ """
614
+ import pexpect
615
+
616
+ timeout = 600
617
+ executable_name, *args = executable.split()
618
+ executable_absname = TOPCOMExecutable(executable_name).absolute_filename()
619
+ proc = pexpect.spawn(executable_absname, args, timeout=timeout)
620
+ proc.expect(r'Evaluating Commandline Options \.\.\.')
621
+ proc.expect(r'\.\.\. done\.')
622
+ proc.setecho(0)
623
+ assert proc.readline().strip() == b''
624
+
625
+ if verbose:
626
+ print("#### TOPCOM input ####")
627
+ print("# " + executable)
628
+ print("# " + input_string)
629
+ sys.stdout.flush()
630
+
631
+ proc.send(input_string)
632
+ proc.send('X\nX\n')
633
+
634
+ if verbose:
635
+ print("#### TOPCOM output ####")
636
+ sys.stdout.flush()
637
+
638
+ while True:
639
+ try:
640
+ line = proc.readline().strip()
641
+ if not isinstance(line, str):
642
+ line = line.decode()
643
+ except pexpect.TIMEOUT:
644
+ if verbose:
645
+ print('# Still running ' + str(executable))
646
+ continue
647
+ if len(line) == 0: # EOF
648
+ break
649
+ if verbose:
650
+ print("# " + line)
651
+ sys.stdout.flush()
652
+
653
+ try:
654
+ yield line.strip()
655
+ except GeneratorExit:
656
+ proc.close(force=True)
657
+ return
658
+
659
+ if verbose:
660
+ print("#######################")
661
+ sys.stdout.flush()
662
+
663
+ def _TOPCOM_communicate(self, executable, verbose=True):
664
+ r"""
665
+ Execute TOPCOM and parse the output into a
666
+ :class:`~sage.geometry.triangulation.element.Triangulation`.
667
+
668
+ TESTS::
669
+
670
+ sage: p = PointConfiguration([[1,1,1], [-1,1,1], [1,-1,1], [-1,-1,1], [1,1,-1]])
671
+ sage: out = p._TOPCOM_communicate('points2placingtriang', verbose=True)
672
+ sage: list(out) # optional - topcom
673
+ #### TOPCOM input ####
674
+ # points2placingtriang
675
+ # [[0,0,0,1],[-2,0,0,1],[0,-2,0,1],[-2,-2,0,1],[0,0,-2,1]]
676
+ #### TOPCOM output ####
677
+ # {{0,1,2,4},{1,2,3,4}}
678
+ #######################
679
+ [(<0,1,2,4>, <1,2,3,4>)]
680
+ """
681
+ for line in self._TOPCOM_exec(executable,
682
+ self._TOPCOM_points(), verbose):
683
+ triangulation = line[ line.find('{{')+2 : line.rfind('}}') ]
684
+ triangulation = triangulation.split('},{')
685
+ triangulation = [ [ QQ(t) for t in triangle.split(',') ]
686
+ for triangle in triangulation ]
687
+
688
+ if self._star is not None:
689
+ o = self._star
690
+ if not all( t.count(o) > 0 for t in triangulation):
691
+ continue
692
+
693
+ yield self(triangulation)
694
+
695
+ def _TOPCOM_triangulations(self, verbose=True):
696
+ r"""
697
+ Return all triangulations satisfying the restrictions imposed.
698
+
699
+ EXAMPLES::
700
+
701
+ sage: p = PointConfiguration([[0,0], [0,1], [1,0], [1,1], [-1,-1]])
702
+ sage: iter = p._TOPCOM_triangulations(verbose=True)
703
+ sage: next(iter) # optional - topcom
704
+ #### TOPCOM input ####
705
+ # points2triangs
706
+ # [[0,0,1],[0,1,1],[1,0,1],[1,1,1],[-1,-1,1]]
707
+ #### TOPCOM output ####
708
+ # T[0] := {{0,1,2},{0,1,4},{0,2,4},{1,2,3}};
709
+ (<0,1,2>, <0,1,4>, <0,2,4>, <1,2,3>)
710
+ """
711
+ command = 'points2'
712
+
713
+ if not self._connected:
714
+ command += 'all'
715
+
716
+ if self._fine:
717
+ command += 'fine'
718
+
719
+ command += 'triangs'
720
+
721
+ if self._regular:
722
+ command += ' --regular'
723
+ if self._regular is False:
724
+ command += ' --nonregular'
725
+
726
+ yield from self._TOPCOM_communicate(command, verbose)
727
+
728
+ def _TOPCOM_triangulate(self, verbose=True):
729
+ r"""
730
+ Return one (in no particular order) triangulation subject
731
+ to all restrictions imposed previously.
732
+
733
+ INPUT:
734
+
735
+ - ``verbose`` -- boolean; whether to print out the TOPCOM
736
+ interaction
737
+
738
+ OUTPUT:
739
+
740
+ A :class:`~sage.geometry.triangulation.element.Triangulation`
741
+ satisfying all restrictions imposed. This raises a :exc:`ValueError`
742
+ if no such triangulation exists.
743
+
744
+ EXAMPLES::
745
+
746
+ sage: # optional - topcom
747
+ sage: p = PointConfiguration([[0,0], [0,1], [1,0], [1,1], [-1,-1]])
748
+ sage: p.set_engine('topcom')
749
+ sage: p._TOPCOM_triangulate(verbose=False)
750
+ (<0,1,2>, <0,1,4>, <0,2,4>, <1,2,3>)
751
+ sage: list( p.triangulate() )
752
+ [(0, 1, 2), (0, 1, 4), (0, 2, 4), (1, 2, 3)]
753
+ sage: p.set_engine('internal')
754
+ """
755
+ assert self._regular is not False, \
756
+ 'When asked for a single triangulation TOPCOM ' + \
757
+ 'always returns a regular triangulation.'
758
+
759
+ command = "points2"
760
+ if self._fine:
761
+ command += "finetriang"
762
+ else:
763
+ command += "placingtriang"
764
+
765
+ return next(self._TOPCOM_communicate(command, verbose))
766
+
767
+ def restrict_to_regular_triangulations(self, regular=True):
768
+ """
769
+ Restrict to regular triangulations.
770
+
771
+ NOTE:
772
+
773
+ Regularity testing requires the optional TOPCOM package.
774
+
775
+ INPUT:
776
+
777
+ - ``regular`` -- ``True``, ``False``, or ``None``; whether to
778
+ restrict to regular triangulations, irregular
779
+ triangulations, or lift any restrictions on regularity
780
+
781
+ OUTPUT:
782
+
783
+ A new :class:`PointConfiguration` with the same points, but
784
+ whose triangulations will all be regular as specified. See
785
+ :class:`PointConfiguration` for details.
786
+
787
+ EXAMPLES::
788
+
789
+ sage: p = PointConfiguration([[0,0], [0,1], [1,0], [1,1], [-1,-1]]); p
790
+ A point configuration in affine 2-space over Integer Ring
791
+ consisting of 5 points. The triangulations of this point
792
+ configuration are assumed to be connected, not necessarily
793
+ fine, not necessarily regular.
794
+ sage: len(p.triangulations_list())
795
+ 4
796
+ sage: PointConfiguration.set_engine('topcom')
797
+ sage: p_regular = p.restrict_to_regular_triangulations() # optional - topcom
798
+ sage: len(p_regular.triangulations_list()) # optional - topcom
799
+ 4
800
+ sage: p == p_regular.restrict_to_regular_triangulations(regular=None) # optional - topcom
801
+ True
802
+ sage: PointConfiguration.set_engine('internal')
803
+ """
804
+ return PointConfiguration(self,
805
+ connected=self._connected,
806
+ fine=self._fine,
807
+ regular=regular,
808
+ star=self._star)
809
+
810
+ def restrict_to_connected_triangulations(self, connected=True):
811
+ """
812
+ Restrict to connected triangulations.
813
+
814
+ NOTE:
815
+
816
+ Finding non-connected triangulations requires the optional
817
+ TOPCOM package.
818
+
819
+ INPUT:
820
+
821
+ - ``connected`` -- boolean; whether to restrict to
822
+ triangulations that are connected by bistellar flips to the
823
+ regular triangulations
824
+
825
+ OUTPUT:
826
+
827
+ A new :class:`PointConfiguration` with the same points, but
828
+ whose triangulations will all be in the connected
829
+ component. See :class:`PointConfiguration` for details.
830
+
831
+ EXAMPLES::
832
+
833
+ sage: p = PointConfiguration([[0,0], [0,1], [1,0], [1,1], [-1,-1]]); p
834
+ A point configuration in affine 2-space over Integer Ring
835
+ consisting of 5 points. The triangulations of this point
836
+ configuration are assumed to be connected, not necessarily
837
+ fine, not necessarily regular.
838
+ sage: len(p.triangulations_list())
839
+ 4
840
+ sage: PointConfiguration.set_engine('topcom')
841
+ sage: p_all = p.restrict_to_connected_triangulations(connected=False) # optional - topcom
842
+ sage: len(p_all.triangulations_list()) # optional - topcom
843
+ 4
844
+ sage: p == p_all.restrict_to_connected_triangulations(connected=True) # optional - topcom
845
+ True
846
+ sage: PointConfiguration.set_engine('internal')
847
+ """
848
+ return PointConfiguration(self,
849
+ connected=connected,
850
+ fine=self._fine,
851
+ regular=self._regular,
852
+ star=self._star)
853
+
854
+ def restrict_to_fine_triangulations(self, fine=True):
855
+ """
856
+ Restrict to fine triangulations.
857
+
858
+ INPUT:
859
+
860
+ - ``fine`` -- boolean; whether to restrict to fine triangulations
861
+
862
+ OUTPUT:
863
+
864
+ A new :class:`PointConfiguration` with the same points, but
865
+ whose triangulations will all be fine. See
866
+ :class:`PointConfiguration` for details.
867
+
868
+ EXAMPLES::
869
+
870
+ sage: p = PointConfiguration([[0,0], [0,1], [1,0], [1,1], [-1,-1]])
871
+ sage: p
872
+ A point configuration in affine 2-space over Integer Ring
873
+ consisting of 5 points. The triangulations of this point
874
+ configuration are assumed to be connected, not necessarily
875
+ fine, not necessarily regular.
876
+
877
+ sage: len(p.triangulations_list())
878
+ 4
879
+ sage: p_fine = p.restrict_to_fine_triangulations()
880
+ sage: len(p.triangulations_list())
881
+ 4
882
+ sage: p == p_fine.restrict_to_fine_triangulations(fine=False)
883
+ True
884
+ """
885
+ return PointConfiguration(self,
886
+ connected=self._connected,
887
+ fine=fine,
888
+ regular=self._regular,
889
+ star=self._star)
890
+
891
+ def restrict_to_star_triangulations(self, star):
892
+ """
893
+ Restrict to star triangulations with the given point as the
894
+ center.
895
+
896
+ INPUT:
897
+
898
+ - ``origin`` -- ``None`` or an integer or the coordinates of a
899
+ point. An integer denotes the index of the central point. If
900
+ ``None`` is passed, any restriction on the starshape will be
901
+ removed.
902
+
903
+ OUTPUT:
904
+
905
+ A new :class:`PointConfiguration` with the same points, but
906
+ whose triangulations will all be star. See
907
+ :class:`PointConfiguration` for details.
908
+
909
+ EXAMPLES::
910
+
911
+ sage: p = PointConfiguration([[0,0], [0,1], [1,0], [1,1], [-1,-1]])
912
+ sage: len(list(p.triangulations()))
913
+ 4
914
+ sage: p_star = p.restrict_to_star_triangulations(0)
915
+ sage: p_star is p.restrict_to_star_triangulations((0,0))
916
+ True
917
+ sage: p_star.triangulations_list()
918
+ [(<0,1,3>, <0,1,4>, <0,2,3>, <0,2,4>)]
919
+ sage: p_newstar = p_star.restrict_to_star_triangulations(1) # pick different origin
920
+ sage: p_newstar.triangulations_list()
921
+ [(<1,2,3>, <1,2,4>)]
922
+ sage: p == p_star.restrict_to_star_triangulations(star=None)
923
+ True
924
+ """
925
+ return PointConfiguration(self,
926
+ connected=self._connected,
927
+ fine=self._fine,
928
+ regular=self._regular,
929
+ star=star)
930
+
931
+ def triangulations(self, verbose=False):
932
+ r"""
933
+ Return all triangulations.
934
+
935
+ - ``verbose`` -- boolean (default: ``False``); whether to
936
+ print out the TOPCOM interaction, if any
937
+
938
+ OUTPUT:
939
+
940
+ A generator for the triangulations satisfying all the
941
+ restrictions imposed. Each triangulation is returned as a
942
+ :class:`~sage.geometry.triangulation.element.Triangulation` object.
943
+
944
+ EXAMPLES::
945
+
946
+ sage: p = PointConfiguration([[0,0], [0,1], [1,0], [1,1], [-1,-1]])
947
+ sage: iter = p.triangulations()
948
+ sage: next(iter)
949
+ (<1,3,4>, <2,3,4>)
950
+ sage: next(iter)
951
+ (<0,1,3>, <0,1,4>, <0,2,3>, <0,2,4>)
952
+ sage: next(iter)
953
+ (<1,2,3>, <1,2,4>)
954
+ sage: next(iter)
955
+ (<0,1,2>, <0,1,4>, <0,2,4>, <1,2,3>)
956
+ sage: p.triangulations_list()
957
+ [(<1,3,4>, <2,3,4>),
958
+ (<0,1,3>, <0,1,4>, <0,2,3>, <0,2,4>),
959
+ (<1,2,3>, <1,2,4>),
960
+ (<0,1,2>, <0,1,4>, <0,2,4>, <1,2,3>)]
961
+ sage: p_fine = p.restrict_to_fine_triangulations()
962
+ sage: p_fine.triangulations_list()
963
+ [(<0,1,3>, <0,1,4>, <0,2,3>, <0,2,4>),
964
+ (<0,1,2>, <0,1,4>, <0,2,4>, <1,2,3>)]
965
+
966
+ Note that we explicitly asked the internal algorithm to
967
+ compute the triangulations. Using TOPCOM, we obtain the same
968
+ triangulations but in a different order::
969
+
970
+ sage: # optional - topcom
971
+ sage: p.set_engine('topcom')
972
+ sage: iter = p.triangulations()
973
+ sage: next(iter)
974
+ (<0,1,2>, <0,1,4>, <0,2,4>, <1,2,3>)
975
+ sage: next(iter)
976
+ (<0,1,3>, <0,1,4>, <0,2,3>, <0,2,4>)
977
+ sage: next(iter)
978
+ (<1,2,3>, <1,2,4>)
979
+ sage: next(iter)
980
+ (<1,3,4>, <2,3,4>)
981
+ sage: p.triangulations_list()
982
+ [(<0,1,2>, <0,1,4>, <0,2,4>, <1,2,3>),
983
+ (<0,1,3>, <0,1,4>, <0,2,3>, <0,2,4>),
984
+ (<1,2,3>, <1,2,4>),
985
+ (<1,3,4>, <2,3,4>)]
986
+ sage: p_fine = p.restrict_to_fine_triangulations()
987
+ sage: p_fine.set_engine('topcom')
988
+ sage: p_fine.triangulations_list()
989
+ [(<0,1,2>, <0,1,4>, <0,2,4>, <1,2,3>),
990
+ (<0,1,3>, <0,1,4>, <0,2,3>, <0,2,4>)]
991
+ sage: p.set_engine('internal')
992
+ """
993
+ if self._use_TOPCOM:
994
+ yield from self._TOPCOM_triangulations(verbose)
995
+ else:
996
+ if not self._connected:
997
+ raise ValueError('Need TOPCOM to find disconnected triangulations.')
998
+ if (self._regular is not None):
999
+ raise ValueError('Need TOPCOM to test for regularity.')
1000
+ ci = ConnectedTriangulationsIterator(self, star=self._star, fine=self._fine)
1001
+ for encoded_triangulation in ci:
1002
+ yield self(encoded_triangulation)
1003
+
1004
+ def triangulations_list(self, verbose=False):
1005
+ r"""
1006
+ Return all triangulations.
1007
+
1008
+ INPUT:
1009
+
1010
+ - ``verbose`` -- boolean; whether to print out the TOPCOM
1011
+ interaction, if any
1012
+
1013
+ OUTPUT:
1014
+
1015
+ A list of triangulations (see
1016
+ :class:`~sage.geometry.triangulation.element.Triangulation`)
1017
+ satisfying all restrictions imposed previously.
1018
+
1019
+ EXAMPLES::
1020
+
1021
+ sage: p = PointConfiguration([[0,0], [0,1], [1,0], [1,1]])
1022
+ sage: p.triangulations_list()
1023
+ [(<0,1,2>, <1,2,3>), (<0,1,3>, <0,2,3>)]
1024
+ sage: list(map(list, p.triangulations_list()))
1025
+ [[(0, 1, 2), (1, 2, 3)], [(0, 1, 3), (0, 2, 3)]]
1026
+ sage: p.set_engine('topcom')
1027
+ sage: p.triangulations_list() # optional - topcom
1028
+ [(<0,1,2>, <1,2,3>), (<0,1,3>, <0,2,3>)]
1029
+ sage: p.set_engine('internal')
1030
+ """
1031
+ return list(self.triangulations(verbose))
1032
+
1033
+ def triangulate(self, verbose=False):
1034
+ r"""
1035
+ Return one (in no particular order) triangulation.
1036
+
1037
+ INPUT:
1038
+
1039
+ - ``verbose`` -- boolean; whether to print out the TOPCOM
1040
+ interaction, if any
1041
+
1042
+ OUTPUT:
1043
+
1044
+ A :class:`~sage.geometry.triangulation.element.Triangulation`
1045
+ satisfying all restrictions imposed. This raises a :exc:`ValueError`
1046
+ if no such triangulation exists.
1047
+
1048
+ EXAMPLES::
1049
+
1050
+ sage: p = PointConfiguration([[0,0], [0,1], [1,0], [1,1], [-1,-1]])
1051
+ sage: p.triangulate()
1052
+ (<1,3,4>, <2,3,4>)
1053
+ sage: list( p.triangulate() )
1054
+ [(1, 3, 4), (2, 3, 4)]
1055
+
1056
+ Using TOPCOM yields a different, but equally good, triangulation::
1057
+
1058
+ sage: # optional - topcom
1059
+ sage: p.set_engine('topcom')
1060
+ sage: p.triangulate()
1061
+ (<0,1,2>, <0,1,4>, <0,2,4>, <1,2,3>)
1062
+ sage: list(p.triangulate())
1063
+ [(0, 1, 2), (0, 1, 4), (0, 2, 4), (1, 2, 3)]
1064
+ sage: p.set_engine('internal')
1065
+ """
1066
+ if self._use_TOPCOM and self._regular is not False:
1067
+ try:
1068
+ return self._TOPCOM_triangulate(verbose)
1069
+ except StopIteration:
1070
+ # either topcom did not return a triangulation or we filtered it out
1071
+ pass
1072
+
1073
+ if self._connected and not self._fine and self._regular is not False and self._star is None:
1074
+ return self.placing_triangulation()
1075
+
1076
+ try:
1077
+ return next(self.triangulations(verbose))
1078
+ except StopIteration:
1079
+ # there is no triangulation
1080
+ pass
1081
+ raise ValueError('No triangulation with the required properties.')
1082
+
1083
+ def convex_hull(self):
1084
+ """
1085
+ Return the convex hull of the point configuration.
1086
+
1087
+ EXAMPLES::
1088
+
1089
+ sage: p = PointConfiguration([[0,0], [0,1], [1,0], [1,1], [-1,-1]])
1090
+ sage: p.convex_hull()
1091
+ A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices
1092
+ """
1093
+ try:
1094
+ return self._polyhedron
1095
+ except AttributeError:
1096
+ pass
1097
+
1098
+ from sage.geometry.polyhedron.constructor import Polyhedron
1099
+ pts = [p.reduced_affine() for p in self.points()]
1100
+ self._polyhedron = Polyhedron(vertices=pts)
1101
+ return self._polyhedron
1102
+
1103
+ @cached_method
1104
+ def restricted_automorphism_group(self):
1105
+ r"""
1106
+ Return the restricted automorphism group.
1107
+
1108
+ First, let the linear automorphism group be the subgroup of
1109
+ the affine group `AGL(d,\RR) = GL(d,\RR) \ltimes \RR^d`
1110
+ preserving the `d`-dimensional point configuration. The
1111
+ affine group acts in the usual way `\vec{x}\mapsto
1112
+ A\vec{x}+b` on the ambient space.
1113
+
1114
+ The restricted automorphism group is the subgroup of the
1115
+ linear automorphism group generated by permutations of
1116
+ points. See [BSS2009]_ for more details and a description of the
1117
+ algorithm.
1118
+
1119
+ OUTPUT:
1120
+
1121
+ A
1122
+ :class:`PermutationGroup<sage.groups.perm_gps.permgroup.PermutationGroup_generic>`
1123
+ that is isomorphic to the restricted automorphism group is
1124
+ returned.
1125
+
1126
+ Note that in Sage, permutation groups always act on positive
1127
+ integers while lists etc. are indexed by nonnegative
1128
+ integers. The indexing of the permutation group is chosen to
1129
+ be shifted by ``+1``. That is, the transposition ``(i,j)`` in
1130
+ the permutation group corresponds to exchange of ``self[i-1]``
1131
+ and ``self[j-1]``.
1132
+
1133
+ EXAMPLES::
1134
+
1135
+ sage: pyramid = PointConfiguration([[1,0,0], [0,1,1], [0,1,-1],
1136
+ ....: [0,-1,-1], [0,-1,1]])
1137
+ sage: G = pyramid.restricted_automorphism_group() # needs sage.graphs sage.groups
1138
+ sage: G == PermutationGroup([[(3,5)], [(2,3),(4,5)], [(2,4)]]) # needs sage.graphs sage.groups
1139
+ True
1140
+ sage: DihedralGroup(4).is_isomorphic(G) # needs sage.graphs sage.groups
1141
+ True
1142
+
1143
+ The square with an off-center point in the middle. Note that
1144
+ the middle point breaks the restricted automorphism group
1145
+ `D_4` of the convex hull::
1146
+
1147
+ sage: square = PointConfiguration([(3/4,3/4), (1,1), (1,-1), (-1,-1), (-1,1)])
1148
+ sage: square.restricted_automorphism_group() # needs sage.graphs sage.groups
1149
+ Permutation Group with generators [(3,5)]
1150
+ sage: DihedralGroup(1).is_isomorphic(_) # needs sage.graphs sage.groups
1151
+ True
1152
+ """
1153
+ v_list = [ vector(p.projective()) for p in self ]
1154
+ Qinv = sum( v.column() * v.row() for v in v_list ).inverse()
1155
+
1156
+ # construct the graph
1157
+ from sage.graphs.graph import Graph
1158
+
1159
+ # Was set to sparse = False, but there is a problem with Graph
1160
+ # backends. It should probably be set back to sparse = False as soon as
1161
+ # the backends are fixed.
1162
+ G = Graph(sparse=True)
1163
+ for i in range(len(v_list)):
1164
+ for j in range(i+1,len(v_list)):
1165
+ v_i = v_list[i]
1166
+ v_j = v_list[j]
1167
+ G.add_edge(i+1,j+1, v_i * Qinv * v_j)
1168
+
1169
+ return G.automorphism_group(edge_labels=True)
1170
+
1171
+ def face_codimension(self, point):
1172
+ r"""
1173
+ Return the smallest `d\in\ZZ` such that ``point`` is
1174
+ contained in the interior of a codimension-`d` face.
1175
+
1176
+ EXAMPLES::
1177
+
1178
+ sage: triangle = PointConfiguration([[0,0], [1,-1], [1,0], [1,1]])
1179
+ sage: triangle.point(2)
1180
+ P(1, 0)
1181
+ sage: triangle.face_codimension(2)
1182
+ 1
1183
+ sage: triangle.face_codimension([1,0])
1184
+ 1
1185
+
1186
+ This also works for degenerate cases like the tip of the
1187
+ pyramid over a square (which saturates four inequalities)::
1188
+
1189
+ sage: pyramid = PointConfiguration([[1,0,0], [0,1,1], [0,1,-1],
1190
+ ....: [0,-1,-1], [0,-1,1]])
1191
+ sage: pyramid.face_codimension(0)
1192
+ 3
1193
+ """
1194
+ try:
1195
+ p = vector(self.point(point).reduced_affine())
1196
+ except TypeError:
1197
+ p = vector(point)
1198
+
1199
+ inequalities = []
1200
+ for ieq in self.convex_hull().inequality_generator():
1201
+ if (ieq.A()*p + ieq.b() == 0):
1202
+ inequalities += [ ieq.vector() ]
1203
+ return matrix(inequalities).rank()
1204
+
1205
+ def face_interior(self, dim=None, codim=None):
1206
+ """
1207
+ Return points by the codimension of the containing face in the convex hull.
1208
+
1209
+ EXAMPLES::
1210
+
1211
+ sage: triangle = PointConfiguration([[-1,0], [0,0], [1,-1], [1,0], [1,1]])
1212
+ sage: triangle.face_interior()
1213
+ ((1,), (3,), (0, 2, 4))
1214
+ sage: triangle.face_interior(dim=0) # the vertices of the convex hull
1215
+ (0, 2, 4)
1216
+ sage: triangle.face_interior(codim=1) # interior of facets
1217
+ (3,)
1218
+ """
1219
+ assert not (dim is not None and codim is not None), "You cannot specify both dim and codim."
1220
+
1221
+ if (dim is not None):
1222
+ return self.face_interior()[self.convex_hull().dim()-dim]
1223
+ if (codim is not None):
1224
+ return self.face_interior()[codim]
1225
+
1226
+ try:
1227
+ return self._face_interior
1228
+ except AttributeError:
1229
+ pass
1230
+
1231
+ d = [ self.face_codimension(i) for i in range(self.n_points()) ]
1232
+
1233
+ return tuple( tuple(i for i in range(self.n_points()) if d[i] == codim )
1234
+ for codim in range(self.dim()+1) )
1235
+
1236
+ def exclude_points(self, point_idx_list):
1237
+ """
1238
+ Return a new point configuration with the given points
1239
+ removed.
1240
+
1241
+ INPUT:
1242
+
1243
+ - ``point_idx_list`` -- list of integers; the indices of
1244
+ points to exclude
1245
+
1246
+ OUTPUT:
1247
+
1248
+ A new :class:`PointConfiguration` with the given points
1249
+ removed.
1250
+
1251
+ EXAMPLES::
1252
+
1253
+ sage: p = PointConfiguration([[-1,0], [0,0], [1,-1], [1,0], [1,1]])
1254
+ sage: list(p)
1255
+ [P(-1, 0), P(0, 0), P(1, -1), P(1, 0), P(1, 1)]
1256
+ sage: q = p.exclude_points([3])
1257
+ sage: list(q)
1258
+ [P(-1, 0), P(0, 0), P(1, -1), P(1, 1)]
1259
+ sage: p.exclude_points(p.face_interior(codim=1)).points()
1260
+ (P(-1, 0), P(0, 0), P(1, -1), P(1, 1))
1261
+ """
1262
+ points = [self.point(i) for i in range(self.n_points())
1263
+ if i not in point_idx_list]
1264
+ return PointConfiguration(points,
1265
+ projective=False,
1266
+ connected=self._connected,
1267
+ fine=self._fine,
1268
+ regular=self._regular,
1269
+ star=self._star)
1270
+
1271
+ def volume(self, simplex=None):
1272
+ """
1273
+ Find `n!` times the `n`-volume of a simplex of dimension `n`.
1274
+
1275
+ INPUT:
1276
+
1277
+ - ``simplex`` -- (optional argument) a simplex from a
1278
+ triangulation T specified as a list of point indices
1279
+
1280
+ OUTPUT:
1281
+
1282
+ * If a simplex was passed as an argument: `n!` * (volume of ``simplex``).
1283
+
1284
+ * Without argument: `n!` * (the total volume of the convex hull).
1285
+
1286
+ EXAMPLES:
1287
+
1288
+ The volume of the standard simplex should always be 1::
1289
+
1290
+ sage: p = PointConfiguration([[0,0], [1,0], [0,1], [1,1]])
1291
+ sage: p.volume([0,1,2])
1292
+ 1
1293
+ sage: simplex = p.triangulate()[0] # first simplex of triangulation
1294
+ sage: p.volume(simplex)
1295
+ 1
1296
+
1297
+ The square can be triangulated into two minimal simplices, so
1298
+ in the "integral" normalization its volume equals two::
1299
+
1300
+ sage: p.volume()
1301
+ 2
1302
+
1303
+ .. NOTE::
1304
+
1305
+ We return `n!` * (metric volume of the simplex) to ensure that
1306
+ the volume is an integer. Essentially, this normalizes
1307
+ things so that the volume of the standard `n`-simplex is 1.
1308
+ See [GKZ1994]_ page 182.
1309
+ """
1310
+ if (simplex is None):
1311
+ return sum([ self.volume(s) for s in self.triangulate() ])
1312
+
1313
+ #Form a matrix whose columns are the points of simplex
1314
+ #with the first point of simplex shifted to the origin.
1315
+ v = [ self.point(i).reduced_affine_vector() for i in simplex ]
1316
+ m = matrix([ v_i - v[0] for v_i in v[1:] ])
1317
+ return abs(m.det())
1318
+
1319
+ def secondary_polytope(self):
1320
+ r"""
1321
+ Calculate the secondary polytope of the point configuration.
1322
+
1323
+ For a definition of the secondary polytope, see [GKZ1994]_ page 220
1324
+ Definition 1.6.
1325
+
1326
+ Note that if you restricted the admissible triangulations of
1327
+ the point configuration then the output will be the
1328
+ corresponding face of the whole secondary polytope.
1329
+
1330
+ OUTPUT:
1331
+
1332
+ The secondary polytope of the point configuration as an
1333
+ instance of
1334
+ :class:`~sage.geometry.polyhedron.base.Polyhedron_base`.
1335
+
1336
+ EXAMPLES::
1337
+
1338
+ sage: p = PointConfiguration([[0,0], [1,0], [2,1], [1,2], [0,1]])
1339
+ sage: poly = p.secondary_polytope()
1340
+ sage: poly.vertices_matrix()
1341
+ [1 1 3 3 5]
1342
+ [3 5 1 4 1]
1343
+ [4 2 5 2 4]
1344
+ [2 4 2 5 4]
1345
+ [5 3 4 1 1]
1346
+ sage: poly.Vrepresentation()
1347
+ (A vertex at (1, 3, 4, 2, 5),
1348
+ A vertex at (1, 5, 2, 4, 3),
1349
+ A vertex at (3, 1, 5, 2, 4),
1350
+ A vertex at (3, 4, 2, 5, 1),
1351
+ A vertex at (5, 1, 4, 4, 1))
1352
+ sage: poly.Hrepresentation()
1353
+ (An equation (0, 0, 1, 2, 1) x - 13 == 0,
1354
+ An equation (1, 0, 0, 2, 2) x - 15 == 0,
1355
+ An equation (0, 1, 0, -3, -2) x + 13 == 0,
1356
+ An inequality (0, 0, 0, -1, -1) x + 7 >= 0,
1357
+ An inequality (0, 0, 0, 1, 0) x - 2 >= 0,
1358
+ An inequality (0, 0, 0, -2, -1) x + 11 >= 0,
1359
+ An inequality (0, 0, 0, 0, 1) x - 1 >= 0,
1360
+ An inequality (0, 0, 0, 3, 2) x - 14 >= 0)
1361
+ """
1362
+ from sage.geometry.polyhedron.constructor import Polyhedron
1363
+ #TODO: once restriction to regular triangulations is fixed,
1364
+ #change the next line to only take the regular triangulations,
1365
+ #since they are the vertices of the secondary polytope anyway.
1366
+ l = self.triangulations_list()
1367
+ return Polyhedron(vertices=[x.gkz_phi() for x in l])
1368
+
1369
+ def circuits_support(self):
1370
+ r"""
1371
+ A generator for the supports of the circuits of the point configuration.
1372
+
1373
+ See :meth:`circuits` for details.
1374
+
1375
+ OUTPUT:
1376
+
1377
+ A generator for the supports `C_-\cup C_+` (returned as a
1378
+ Python tuple) for all circuits of the point configuration.
1379
+
1380
+ EXAMPLES::
1381
+
1382
+ sage: p = PointConfiguration([(0,0), (+1,0), (-1,0), (0,+1), (0,-1)])
1383
+ sage: sorted(p.circuits_support())
1384
+ [(0, 1, 2), (0, 3, 4), (1, 2, 3, 4)]
1385
+ """
1386
+ n = len(self)
1387
+ U = [self[i].reduced_projective() for i in range(n)]
1388
+
1389
+ # the index set of U
1390
+ I = set(range(n))
1391
+ # The (indices of) known independent elements of U
1392
+ independent_k = [(i,) for i in range(n)]
1393
+ supports_k = []
1394
+
1395
+ for k in range(2, self.dim() + 3):
1396
+
1397
+ # possibly linear dependent subsets
1398
+ supports_knext = set()
1399
+ possible_dependency = set()
1400
+ for indep in independent_k:
1401
+ indep_plus_one = [ tuple(sorted(indep+(i,))) for i in (I-set(indep)) ]
1402
+ possible_dependency.update(indep_plus_one)
1403
+ for supp in supports_k:
1404
+ supp_plus_one = [ tuple(sorted(supp+(i,))) for i in (I-set(supp)) ]
1405
+ possible_dependency.difference_update(supp_plus_one)
1406
+ supports_knext.update(supp_plus_one)
1407
+
1408
+ # remember supports and independents for the next k-iteration
1409
+ supports_k = list(supports_knext)
1410
+ independent_k = []
1411
+ for idx in possible_dependency:
1412
+ rk = matrix([ U[i] for i in idx ]).rank()
1413
+ if rk == k:
1414
+ independent_k.append(idx)
1415
+ else:
1416
+ supports_k.append(idx)
1417
+ yield idx
1418
+ assert independent_k == [] # there are no independent (self.dim()+3)-tuples
1419
+
1420
+ def circuits(self):
1421
+ r"""
1422
+ Return the circuits of the point configuration.
1423
+
1424
+ Roughly, a circuit is a minimal linearly dependent subset of
1425
+ the points. That is, a circuit is a partition
1426
+
1427
+ .. MATH::
1428
+
1429
+ \{ 0, 1, \dots, n-1 \} = C_+ \cup C_0 \cup C_-
1430
+
1431
+ such that there is an (unique up to an overall normalization) affine
1432
+ relation
1433
+
1434
+ .. MATH::
1435
+
1436
+ \sum_{i\in C_+} \alpha_i \vec{p}_i =
1437
+ \sum_{j\in C_-} \alpha_j \vec{p}_j
1438
+
1439
+ with all positive (or all negative) coefficients, where
1440
+ `\vec{p}_i=(p_1,\dots,p_k,1)` are the projective coordinates
1441
+ of the `i`-th point.
1442
+
1443
+ OUTPUT:
1444
+
1445
+ The list of (unsigned) circuits as triples `(C_+, C_0,
1446
+ C_-)`. The swapped circuit `(C_-, C_0, C_+)` is not returned
1447
+ separately.
1448
+
1449
+ EXAMPLES::
1450
+
1451
+ sage: p = PointConfiguration([(0,0), (+1,0), (-1,0), (0,+1), (0,-1)])
1452
+ sage: sorted(p.circuits())
1453
+ [((0,), (1, 2), (3, 4)), ((0,), (3, 4), (1, 2)), ((1, 2), (0,), (3, 4))]
1454
+
1455
+
1456
+ TESTS::
1457
+
1458
+ sage: U=matrix([
1459
+ ....: [ 0, 0, 0, 0, 0, 2, 4,-1, 1, 1, 0, 0, 1, 0],
1460
+ ....: [ 0, 0, 0, 1, 0, 0,-1, 0, 0, 0, 0, 0, 0, 0],
1461
+ ....: [ 0, 2, 0, 0, 0, 0,-1, 0, 1, 0, 1, 0, 0, 1],
1462
+ ....: [ 0, 1, 1, 0, 0, 1, 0,-2, 1, 0, 0,-1, 1, 1],
1463
+ ....: [ 0, 0, 0, 0, 1, 0,-1, 0, 0, 0, 0, 0, 0, 0]
1464
+ ....: ])
1465
+ sage: p = PointConfiguration(U.columns())
1466
+ sage: len(p.circuits()) # long time
1467
+ 218
1468
+ """
1469
+ try:
1470
+ return self._circuits
1471
+ except AttributeError:
1472
+ pass
1473
+
1474
+ n = len(self)
1475
+ U = [self[i].reduced_projective() for i in range(n)]
1476
+
1477
+ Circuits = ()
1478
+ for support in self.circuits_support():
1479
+ m = matrix([ U[i] for i in support ]).transpose()
1480
+ ker = m.right_kernel().basis()[0]
1481
+ assert len(ker) == len(support)
1482
+ Cplus = [ support[i] for i in range(len(support)) if ker[i] > 0 ]
1483
+ Cminus = [ support[i] for i in range(len(support)) if ker[i] < 0 ]
1484
+ Czero = set( range(n) ).difference(support)
1485
+ Circuits += ( (tuple(Cplus), tuple(Czero), tuple(Cminus)), )
1486
+ self._circuits = Circuits
1487
+ return Circuits
1488
+
1489
+ def positive_circuits(self, *negative):
1490
+ r"""
1491
+ Return the positive part of circuits with fixed negative part.
1492
+
1493
+ A circuit is a pair `(C_+, C_-)`, each consisting of a subset
1494
+ (actually, an ordered tuple) of point indices.
1495
+
1496
+ INPUT:
1497
+
1498
+ - ``*negative`` -- integer; the indices of points
1499
+
1500
+ OUTPUT: a tuple of all circuits with `C_-` = ``negative``
1501
+
1502
+ EXAMPLES::
1503
+
1504
+ sage: p = PointConfiguration([(1,0,0), (0,1,0), (0,0,1), (-2,0,-1), (-2,-1,0),
1505
+ ....: (-3,-1,-1), (1,1,1), (-1,0,0), (0,0,0)])
1506
+ sage: sorted(p.positive_circuits(8))
1507
+ [(0, 1, 2, 5), (0, 1, 4), (0, 2, 3), (0, 3, 4, 6), (0, 5, 6), (0, 7)]
1508
+ sage: p.positive_circuits(0,5,6)
1509
+ ((8,),)
1510
+ """
1511
+ pos = ()
1512
+ negative = tuple(sorted(negative))
1513
+ for circuit in self.circuits():
1514
+ Cpos = circuit[0]
1515
+ Cneg = circuit[2]
1516
+ if Cpos == negative:
1517
+ pos += ( Cneg, )
1518
+ elif Cneg == negative:
1519
+ pos += ( Cpos, )
1520
+ return pos
1521
+
1522
+ def bistellar_flips(self):
1523
+ r"""
1524
+ Return the bistellar flips.
1525
+
1526
+ OUTPUT:
1527
+
1528
+ The bistellar flips as a tuple. Each flip is a pair
1529
+ `(T_+,T_-)` where `T_+` and `T_-` are partial triangulations
1530
+ of the point configuration.
1531
+
1532
+ EXAMPLES::
1533
+
1534
+ sage: pc = PointConfiguration([(0,0),(1,0),(0,1),(1,1)])
1535
+ sage: pc.bistellar_flips()
1536
+ (((<0,1,3>, <0,2,3>), (<0,1,2>, <1,2,3>)),)
1537
+ sage: Tpos, Tneg = pc.bistellar_flips()[0]
1538
+ sage: Tpos.plot(axes=False) # needs sage.plot
1539
+ Graphics object consisting of 11 graphics primitives
1540
+ sage: Tneg.plot(axes=False) # needs sage.plot
1541
+ Graphics object consisting of 11 graphics primitives
1542
+
1543
+ The 3d analog::
1544
+
1545
+ sage: pc = PointConfiguration([(0,0,0),(0,2,0),(0,0,2),(-1,0,0),(1,1,1)])
1546
+ sage: pc.bistellar_flips()
1547
+ (((<0,1,2,3>, <0,1,2,4>), (<0,1,3,4>, <0,2,3,4>, <1,2,3,4>)),)
1548
+
1549
+ A 2d flip on the base of the pyramid over a square::
1550
+
1551
+ sage: pc = PointConfiguration([(0,0,0),(0,2,0),(0,0,2),(0,2,2),(1,1,1)])
1552
+ sage: pc.bistellar_flips()
1553
+ (((<0,1,3>, <0,2,3>), (<0,1,2>, <1,2,3>)),)
1554
+ sage: Tpos, Tneg = pc.bistellar_flips()[0]
1555
+ sage: Tpos.plot(axes=False) # needs sage.plot
1556
+ Graphics3d Object
1557
+ """
1558
+ flips = []
1559
+ for C in self.circuits():
1560
+ Cpos = list(C[0])
1561
+ Cneg = list(C[2])
1562
+ Tpos = [Cpos + Cneg[0:i] + Cneg[i+1:len(Cneg)]
1563
+ for i in range(len(Cneg))]
1564
+ Tneg = [Cneg + Cpos[0:i] + Cpos[i+1:len(Cpos)]
1565
+ for i in range(len(Cpos))]
1566
+ flips.append((self.element_class(Tpos, parent=self, check=False),
1567
+ self.element_class(Tneg, parent=self, check=False)))
1568
+ return tuple(flips)
1569
+
1570
+ def lexicographic_triangulation(self):
1571
+ r"""
1572
+ Return the lexicographic triangulation.
1573
+
1574
+ The algorithm was taken from [PUNTOS]_.
1575
+
1576
+ EXAMPLES::
1577
+
1578
+ sage: p = PointConfiguration([(0,0), (+1,0), (-1,0), (0,+1), (0,-1)])
1579
+ sage: p.lexicographic_triangulation()
1580
+ (<1,3,4>, <2,3,4>)
1581
+
1582
+ TESTS::
1583
+
1584
+ sage: U = matrix([
1585
+ ....: [ 0, 0, 0, 0, 0, 2, 4,-1, 1, 1, 0, 0, 1, 0],
1586
+ ....: [ 0, 0, 0, 1, 0, 0,-1, 0, 0, 0, 0, 0, 0, 0],
1587
+ ....: [ 0, 2, 0, 0, 0, 0,-1, 0, 1, 0, 1, 0, 0, 1],
1588
+ ....: [ 0, 1, 1, 0, 0, 1, 0,-2, 1, 0, 0,-1, 1, 1],
1589
+ ....: [ 0, 0, 0, 0, 1, 0,-1, 0, 0, 0, 0, 0, 0, 0]
1590
+ ....: ])
1591
+ sage: pc = PointConfiguration(U.columns())
1592
+ sage: pc.lexicographic_triangulation()
1593
+ (<1,3,4,7,10,13>, <1,3,4,8,10,13>, <1,3,6,7,10,13>, <1,3,6,8,10,13>,
1594
+ <1,4,6,7,10,13>, <1,4,6,8,10,13>, <2,3,4,6,7,12>, <2,3,4,7,12,13>,
1595
+ <2,3,6,7,12,13>, <2,4,6,7,12,13>, <3,4,5,6,9,12>, <3,4,5,8,9,12>,
1596
+ <3,4,6,7,11,12>, <3,4,6,9,11,12>, <3,4,7,10,11,13>, <3,4,7,11,12,13>,
1597
+ <3,4,8,9,10,12>, <3,4,8,10,12,13>, <3,4,9,10,11,12>, <3,4,10,11,12,13>,
1598
+ <3,5,6,8,9,12>, <3,6,7,10,11,13>, <3,6,7,11,12,13>, <3,6,8,9,10,12>,
1599
+ <3,6,8,10,12,13>, <3,6,9,10,11,12>, <3,6,10,11,12,13>, <4,5,6,8,9,12>,
1600
+ <4,6,7,10,11,13>, <4,6,7,11,12,13>, <4,6,8,9,10,12>, <4,6,8,10,12,13>,
1601
+ <4,6,9,10,11,12>, <4,6,10,11,12,13>)
1602
+ sage: len(_)
1603
+ 34
1604
+ """
1605
+ lex_supp = set()
1606
+ for circuit in self.circuits():
1607
+ Cplus = circuit[0]
1608
+ Cminus = circuit[2]
1609
+ s0 = min(Cplus + Cminus)
1610
+ if s0 in Cplus:
1611
+ lex_supp.add(Cplus)
1612
+ else:
1613
+ lex_supp.add(Cminus)
1614
+
1615
+ lex_supp = sorted(lex_supp, key=lambda x:-len(x))
1616
+ basepts = copy(lex_supp)
1617
+ for i in range(len(lex_supp)-1):
1618
+ for j in range(i+1,len(lex_supp)):
1619
+ if set(lex_supp[j]).issubset(set(lex_supp[i])):
1620
+ try:
1621
+ basepts.remove(lex_supp[i])
1622
+ except ValueError:
1623
+ pass
1624
+
1625
+ basepts = [ (len(b),)+b for b in basepts ] # decorate
1626
+ basepts = sorted(basepts) # sort
1627
+ basepts = [ b[1:] for b in basepts ] # undecorate
1628
+
1629
+ def make_cotriang(basepts):
1630
+ if len(basepts) == 0:
1631
+ return [frozenset()]
1632
+ triangulation = set()
1633
+ for tail in make_cotriang(basepts[1:]):
1634
+ for head in basepts[0]:
1635
+ triangulation.update([ frozenset([head]).union(tail) ])
1636
+
1637
+ nonminimal = set()
1638
+ for rel in itertools.combinations(triangulation, 2):
1639
+ if rel[0].issubset(rel[1]):
1640
+ nonminimal.update([rel[1]])
1641
+ if rel[1].issubset(rel[0]):
1642
+ nonminimal.update([rel[0]])
1643
+ triangulation.difference_update(nonminimal)
1644
+
1645
+ triangulation = [ [len(t)]+sorted(t) for t in triangulation ] # decorate
1646
+ triangulation = sorted(triangulation) # sort
1647
+ triangulation = [ frozenset(t[1:]) for t in triangulation ] # undecorate
1648
+
1649
+ return triangulation
1650
+
1651
+ triangulation = make_cotriang(basepts)
1652
+ I = frozenset(range(self.n_points()))
1653
+ triangulation = [ tuple(I.difference(t)) for t in triangulation ]
1654
+
1655
+ return self(triangulation)
1656
+
1657
+ @cached_method
1658
+ def distance_affine(self, x, y):
1659
+ r"""
1660
+ Return the distance between two points.
1661
+
1662
+ The distance function used in this method is `d_{aff}(x,y)^2`,
1663
+ the square of the usual affine distance function
1664
+
1665
+ .. MATH::
1666
+
1667
+ d_{aff}(x,y) = |x-y|
1668
+
1669
+ INPUT:
1670
+
1671
+ - ``x``, ``y`` -- two points of the point configuration
1672
+
1673
+ OUTPUT:
1674
+
1675
+ The metric distance-square `d_{aff}(x,y)^2`. Note that this
1676
+ distance lies in the same field as the entries of ``x``,
1677
+ ``y``. That is, the distance of rational points will be
1678
+ rational and so on.
1679
+
1680
+ EXAMPLES::
1681
+
1682
+ sage: pc = PointConfiguration([(0,0),(1,0),(2,1),(1,2),(0,1)])
1683
+ sage: [pc.distance_affine(pc.point(0), p) for p in pc.points()]
1684
+ [0, 1, 5, 5, 1]
1685
+ """
1686
+ self._assert_is_affine()
1687
+ d = 0
1688
+ for xi, yi in zip(x.projective(), y.projective()):
1689
+ d += (xi-yi)**2
1690
+ return d
1691
+
1692
+ @cached_method
1693
+ def distance_FS(self, x, y):
1694
+ r"""
1695
+ Return the distance between two points.
1696
+
1697
+ The distance function used in this method is `1-\cos
1698
+ d_{FS}(x,y)^2`, where `d_{FS}` is the Fubini-Study distance of
1699
+ projective points. Recall the Fubini-Studi distance function
1700
+
1701
+ .. MATH::
1702
+
1703
+ d_{FS}(x,y) = \arccos \sqrt{ \frac{(x\cdot y)^2}{|x|^2 |y|^2} }
1704
+
1705
+ INPUT:
1706
+
1707
+ - ``x``, ``y`` -- two points of the point configuration
1708
+
1709
+ OUTPUT:
1710
+
1711
+ The distance `1-\cos d_{FS}(x,y)^2`. Note that this distance
1712
+ lies in the same field as the entries of ``x``, ``y``. That
1713
+ is, the distance of rational points will be rational and so
1714
+ on.
1715
+
1716
+ EXAMPLES::
1717
+
1718
+ sage: pc = PointConfiguration([(0,0), (1,0), (2,1), (1,2), (0,1)])
1719
+ sage: [pc.distance_FS(pc.point(0), p) for p in pc.points()]
1720
+ [0, 1/2, 5/6, 5/6, 1/2]
1721
+ """
1722
+ x2 = y2 = xy = 0
1723
+ for xi, yi in zip(x.projective(), y.projective()):
1724
+ x2 += xi*xi
1725
+ y2 += yi*yi
1726
+ xy += xi*yi
1727
+ return 1-xy*xy/(x2*y2)
1728
+
1729
+ @cached_method
1730
+ def distance(self, x, y):
1731
+ """
1732
+ Return the distance between two points.
1733
+
1734
+ INPUT:
1735
+
1736
+ - ``x``, ``y`` -- two points of the point configuration
1737
+
1738
+ OUTPUT:
1739
+
1740
+ The distance between ``x`` and ``y``, measured either with
1741
+ :meth:`distance_affine` or :meth:`distance_FS` depending on
1742
+ whether the point configuration is defined by affine or
1743
+ projective points. These are related, but not equal to the
1744
+ usual flat and Fubini-Study distance.
1745
+
1746
+ EXAMPLES::
1747
+
1748
+ sage: pc = PointConfiguration([(0,0), (1,0), (2,1), (1,2), (0,1)])
1749
+ sage: [pc.distance(pc.point(0), p) for p in pc.points()]
1750
+ [0, 1, 5, 5, 1]
1751
+
1752
+ sage: pc = PointConfiguration([(0,0,1), (1,0,1), (2,1,1), (1,2,1), (0,1,1)],
1753
+ ....: projective=True)
1754
+ sage: [pc.distance(pc.point(0), p) for p in pc.points()]
1755
+ [0, 1/2, 5/6, 5/6, 1/2]
1756
+ """
1757
+ if self.is_affine():
1758
+ return self.distance_affine(x,y)
1759
+ else:
1760
+ return self.distance_FS(x,y)
1761
+
1762
+ def farthest_point(self, points, among=None):
1763
+ """
1764
+ Return the point with the most distance from ``points``.
1765
+
1766
+ INPUT:
1767
+
1768
+ - ``points`` -- list of points
1769
+
1770
+ - ``among`` -- list of points or ``None`` (default); the set
1771
+ of points from which to pick the farthest one. By default,
1772
+ all points of the configuration are considered.
1773
+
1774
+ OUTPUT:
1775
+
1776
+ A :class:`~sage.geometry.triangulation.base.Point` with
1777
+ largest minimal distance from all given ``points``.
1778
+
1779
+ EXAMPLES::
1780
+
1781
+ sage: pc = PointConfiguration([(0,0), (1,0), (1,1), (0,1)])
1782
+ sage: pc.farthest_point([pc.point(0)])
1783
+ P(1, 1)
1784
+ """
1785
+ if len(points) == 0:
1786
+ return self.point(0)
1787
+ if among is None:
1788
+ among = self.points()
1789
+ p_max = None
1790
+ for p in among:
1791
+ if p in points:
1792
+ continue
1793
+ if p_max is None:
1794
+ p_max = p
1795
+ d_max = min(self.distance(p,q) for q in points)
1796
+ continue
1797
+ d = min(self.distance(p,q) for q in points)
1798
+ if d > d_max:
1799
+ p_max = p
1800
+ return p_max
1801
+
1802
+ def contained_simplex(self, large=True, initial_point=None, point_order=None):
1803
+ """
1804
+ Return a simplex contained in the point configuration.
1805
+
1806
+ INPUT:
1807
+
1808
+ - ``large`` -- boolean; whether to attempt to return a large
1809
+ simplex
1810
+
1811
+ - ``initial_point`` -- a
1812
+ :class:`~sage.geometry.triangulation.base.Point` or ``None``
1813
+ (default). A specific point to start with when picking the
1814
+ simplex vertices.
1815
+
1816
+ - ``point_order`` -- list or tuple of (some or all)
1817
+ :class:`~sage.geometry.triangulation.base.Point` s or ``None``
1818
+ (default)
1819
+
1820
+ OUTPUT:
1821
+
1822
+ A tuple of points that span a simplex of dimension
1823
+ :meth:`dim`. If ``large==True``, the simplex is constructed by
1824
+ successively picking the farthest point. This will ensure that
1825
+ the simplex is not unnecessarily small, but will in general
1826
+ not return a maximal simplex.
1827
+ If a ``point_order`` is specified, the simplex is greedily
1828
+ constructed by considering the points in this order.
1829
+ The ``large`` option and ``initial_point`` is ignored in this case.
1830
+ The ``point_order`` may contain only a subset of the points;
1831
+ in this case, the dimension of the simplex will be the dimension of
1832
+ this subset.
1833
+
1834
+ EXAMPLES::
1835
+
1836
+ sage: pc = PointConfiguration([(0,0), (1,0), (2,1), (1,1), (0,1)])
1837
+ sage: pc.contained_simplex()
1838
+ (P(0, 1), P(2, 1), P(1, 0))
1839
+ sage: pc.contained_simplex(large=False)
1840
+ (P(0, 1), P(1, 1), P(1, 0))
1841
+ sage: pc.contained_simplex(initial_point=pc.point(2))
1842
+ (P(2, 1), P(0, 0), P(1, 0))
1843
+
1844
+ sage: pc = PointConfiguration([[0,0], [0,1], [1,0], [1,1], [-1,-1]])
1845
+ sage: pc.contained_simplex()
1846
+ (P(-1, -1), P(1, 1), P(0, 1))
1847
+ sage: pc.contained_simplex(point_order=[pc[1], pc[3], pc[4], pc[2], pc[0]])
1848
+ (P(0, 1), P(1, 1), P(-1, -1))
1849
+
1850
+ Lower-dimensional example::
1851
+
1852
+ sage: pc.contained_simplex(point_order=[pc[0], pc[3], pc[4]])
1853
+ (P(0, 0), P(1, 1))
1854
+
1855
+ TESTS::
1856
+
1857
+ sage: pc = PointConfiguration([[0,0], [0,1], [1,0]])
1858
+ sage: pc.contained_simplex()
1859
+ (P(1, 0), P(0, 1), P(0, 0))
1860
+ sage: pc = PointConfiguration([[0,0], [0,1]])
1861
+ sage: pc.contained_simplex()
1862
+ (P(0, 1), P(0, 0))
1863
+ sage: pc = PointConfiguration([[0,0]])
1864
+ sage: pc.contained_simplex()
1865
+ (P(0, 0),)
1866
+ sage: pc = PointConfiguration([])
1867
+ sage: pc.contained_simplex()
1868
+ ()
1869
+ """
1870
+ self._assert_is_affine()
1871
+ if point_order is None:
1872
+ points = list(self.points())
1873
+ else:
1874
+ points = list(reversed(point_order))
1875
+ # points are removed one by one from the end.
1876
+ initial_point = None
1877
+ large = False
1878
+ # If point_order is specified, the points of the
1879
+ # PointConfiguration are actually ignored.
1880
+ if not points:
1881
+ return tuple()
1882
+
1883
+ if initial_point is None:
1884
+ origin = points.pop()
1885
+ else:
1886
+ origin = initial_point
1887
+ points.remove(origin)
1888
+ vertices = [origin]
1889
+ edges = []
1890
+ while points and len(vertices) <= self.dim():
1891
+ if large:
1892
+ p = self.farthest_point(vertices, points)
1893
+ points.remove(p)
1894
+ else:
1895
+ p = points.pop()
1896
+ edge = p.reduced_affine_vector() - origin.reduced_affine_vector()
1897
+ if edges and (ker * edge).is_zero():
1898
+ continue
1899
+ vertices.append(p)
1900
+ edges.append(edge)
1901
+ ker = matrix(edges).right_kernel().matrix()
1902
+ return tuple(vertices)
1903
+
1904
+ def placing_triangulation(self, point_order=None):
1905
+ r"""
1906
+ Construct the placing (pushing) triangulation.
1907
+
1908
+ INPUT:
1909
+
1910
+ - ``point_order`` -- list of points or integers. The order in
1911
+ which the points are to be placed. If not given, the points
1912
+ will be placed in some arbitrary order that attempts to
1913
+ produce a small number of simplices.
1914
+
1915
+ OUTPUT: a :class:`~sage.geometry.triangulation.triangulation.Triangulation`
1916
+
1917
+ EXAMPLES::
1918
+
1919
+ sage: pc = PointConfiguration([(0,0), (1,0), (2,1), (1,2), (0,1)])
1920
+ sage: pc.placing_triangulation()
1921
+ (<0,1,2>, <0,2,4>, <2,3,4>)
1922
+ sage: pc.placing_triangulation(point_order=(3,2,1,4,0))
1923
+ (<0,1,4>, <1,2,3>, <1,3,4>)
1924
+ sage: pc.placing_triangulation(point_order=[pc[1], pc[3], pc[4], pc[0]])
1925
+ (<0,1,4>, <1,3,4>)
1926
+ sage: U = matrix([
1927
+ ....: [ 0, 0, 0, 0, 0, 2, 4,-1, 1, 1, 0, 0, 1, 0],
1928
+ ....: [ 0, 0, 0, 1, 0, 0,-1, 0, 0, 0, 0, 0, 0, 0],
1929
+ ....: [ 0, 2, 0, 0, 0, 0,-1, 0, 1, 0, 1, 0, 0, 1],
1930
+ ....: [ 0, 1, 1, 0, 0, 1, 0,-2, 1, 0, 0,-1, 1, 1],
1931
+ ....: [ 0, 0, 0, 0, 1, 0,-1, 0, 0, 0, 0, 0, 0, 0]
1932
+ ....: ])
1933
+ sage: p = PointConfiguration(U.columns())
1934
+ sage: triangulation = p.placing_triangulation(); triangulation
1935
+ (<0,2,3,4,6,7>, <0,2,3,4,6,12>, <0,2,3,4,7,13>, <0,2,3,4,12,13>,
1936
+ <0,2,3,6,7,13>, <0,2,3,6,12,13>, <0,2,4,6,7,13>, <0,2,4,6,12,13>,
1937
+ <0,3,4,6,7,12>, <0,3,4,7,12,13>, <0,3,6,7,12,13>, <0,4,6,7,12,13>,
1938
+ <1,3,4,5,6,12>, <1,3,4,6,11,12>, <1,3,4,7,11,13>, <1,3,4,11,12,13>,
1939
+ <1,3,6,7,11,13>, <1,3,6,11,12,13>, <1,4,6,7,11,13>, <1,4,6,11,12,13>,
1940
+ <3,4,6,7,11,12>, <3,4,7,11,12,13>, <3,6,7,11,12,13>, <4,6,7,11,12,13>)
1941
+ sage: sum(p.volume(t) for t in triangulation)
1942
+ 42
1943
+ sage: p0 = PointConfiguration([(0,0), (+1,0), (-1,0), (0,+1), (0,-1)])
1944
+ sage: p0.pushing_triangulation(point_order=[1,2,0,3,4])
1945
+ (<1,2,3>, <1,2,4>)
1946
+ sage: p0.pushing_triangulation(point_order=[0,1,2,3,4])
1947
+ (<0,1,3>, <0,1,4>, <0,2,3>, <0,2,4>)
1948
+
1949
+ The same triangulation with renumbered points 0->4, 1->0, etc::
1950
+
1951
+ sage: p1 = PointConfiguration([(+1,0), (-1,0), (0,+1), (0,-1), (0,0)])
1952
+ sage: p1.pushing_triangulation(point_order=[4,0,1,2,3])
1953
+ (<0,2,4>, <0,3,4>, <1,2,4>, <1,3,4>)
1954
+ """
1955
+ facet_normals = dict()
1956
+
1957
+ def facets_of_simplex(simplex):
1958
+ """
1959
+ Return the facets of the simplex and store the normals in facet_normals
1960
+ """
1961
+ simplex = list(simplex)
1962
+ origin = simplex[0]
1963
+ rest = simplex[1:]
1964
+ span = matrix([origin.reduced_affine_vector()-p.reduced_affine_vector()
1965
+ for p in rest])
1966
+ # span.inverse() linearly transforms the simplex into the unit simplex
1967
+ normals = span.inverse().columns()
1968
+ facets = []
1969
+ # The facets incident to the chosen vertex "origin"
1970
+ for opposing_vertex, normal in zip(rest, normals):
1971
+ facet = frozenset([origin] + [p for p in rest if p is not opposing_vertex])
1972
+ facets.append(facet)
1973
+ normal.set_immutable()
1974
+ facet_normals[facet] = normal
1975
+ # The remaining facet that is not incident to "origin"
1976
+ facet = frozenset(rest)
1977
+ normal = -sum(normals)
1978
+ normal.set_immutable()
1979
+ facet_normals[facet] = normal
1980
+ facets.append(facet)
1981
+ return set(facets)
1982
+
1983
+ # input verification
1984
+ self._assert_is_affine()
1985
+
1986
+ point_order_is_given = point_order is not None
1987
+ if point_order is None:
1988
+ point_order = list(self.points())
1989
+ elif isinstance(point_order[0], Point):
1990
+ point_order = list(point_order)
1991
+ assert all(p.point_configuration() is self for p in point_order),\
1992
+ "Some point in 'point_order' does not belong to the PointConfiguration."
1993
+ else:
1994
+ point_order = [self.point(i) for i in point_order]
1995
+
1996
+ # construct the initial simplex
1997
+ if point_order_is_given:
1998
+ simplices = [frozenset(self.contained_simplex(large=False, point_order=point_order))]
1999
+ else:
2000
+ simplices = [frozenset(self.contained_simplex(large=True))]
2001
+ for s in simplices[0]:
2002
+ try:
2003
+ point_order.remove(s)
2004
+ except ValueError:
2005
+ pass
2006
+ facets = facets_of_simplex(simplices[0])
2007
+
2008
+ # successively place the remaining points
2009
+
2010
+ # TODO: In concordance with the heuristic to choose a LARGE starting simplex,
2011
+ # one could continue to try to pick points that are far away from the previous ones,
2012
+ # unless point_order_is_given.
2013
+ for point in point_order:
2014
+ # identify visible facets
2015
+ visible_facets = []
2016
+ for facet in facets:
2017
+ origin = next(iter(facet))
2018
+ normal = facet_normals[facet]
2019
+ v = point.reduced_affine_vector() - origin.reduced_affine_vector()
2020
+ if v * normal > 0:
2021
+ visible_facets.append(facet)
2022
+
2023
+ # construct simplices over each visible facet
2024
+ new_facets = set()
2025
+ for facet in visible_facets:
2026
+ simplex = frozenset(list(facet) + [point])
2027
+ simplices.append(simplex)
2028
+ for facet in facets_of_simplex(simplex):
2029
+ if facet in visible_facets:
2030
+ continue
2031
+ if facet in new_facets:
2032
+ new_facets.remove(facet)
2033
+ continue
2034
+ new_facets.add(facet)
2035
+ facets.difference_update(visible_facets)
2036
+ facets.update(new_facets)
2037
+
2038
+ # construct the triangulation
2039
+ triangulation = [[p.index() for p in simplx] for simplx in simplices]
2040
+ return self(triangulation)
2041
+
2042
+ pushing_triangulation = placing_triangulation
2043
+
2044
+ @cached_method
2045
+ def Gale_transform(self, points=None, homogenize=True):
2046
+ r"""
2047
+ Return the Gale transform of ``self``.
2048
+
2049
+ INPUT:
2050
+
2051
+ - ``points`` -- tuple of points or point indices or ``None``
2052
+ (default). A subset of points for which to compute the Gale
2053
+ transform. By default, all points are used.
2054
+
2055
+ - ``homogenize`` -- boolean (default: ``True``); whether to add a row
2056
+ of 1's before taking the transform.
2057
+
2058
+ OUTPUT: a matrix over :meth:`base_ring`
2059
+
2060
+ EXAMPLES::
2061
+
2062
+ sage: pc = PointConfiguration([(0,0), (1,0), (2,1), (1,1), (0,1)])
2063
+ sage: pc.Gale_transform()
2064
+ [ 1 -1 0 1 -1]
2065
+ [ 0 0 1 -2 1]
2066
+
2067
+ sage: pc.Gale_transform((0,1,3,4))
2068
+ [ 1 -1 1 -1]
2069
+
2070
+ sage: points = (pc.point(0), pc.point(1), pc.point(3), pc.point(4))
2071
+ sage: pc.Gale_transform(points)
2072
+ [ 1 -1 1 -1]
2073
+
2074
+ It is possible to take the inverse of the Gale transform, by specifying
2075
+ whether to homogenize or not::
2076
+
2077
+ sage: pc2 = PointConfiguration([[0,0],[3,0],[0,3],[3,3],[1,1]])
2078
+ sage: pc2.Gale_transform(homogenize=False)
2079
+ [ 1 0 0 0 0]
2080
+ [ 0 1 1 0 -3]
2081
+ [ 0 0 0 1 -3]
2082
+ sage: pc2.Gale_transform(homogenize=True)
2083
+ [ 1 1 1 0 -3]
2084
+ [ 0 2 2 -1 -3]
2085
+
2086
+ It might not affect the result (when acyclic)::
2087
+
2088
+ sage: PC = PointConfiguration([[4,0,0],[0,4,0],[0,0,4],[2,1,1],[1,2,1],[1,1,2]])
2089
+ sage: GT = PC.Gale_transform(homogenize=False);GT
2090
+ [ 1 0 0 -3 1 1]
2091
+ [ 0 1 0 1 -3 1]
2092
+ [ 0 0 1 1 1 -3]
2093
+ sage: GT = PC.Gale_transform(homogenize=True);GT
2094
+ [ 1 0 0 -3 1 1]
2095
+ [ 0 1 0 1 -3 1]
2096
+ [ 0 0 1 1 1 -3]
2097
+
2098
+ The following point configuration is totally cyclic (the cone spanned
2099
+ by the vectors is equal to the vector space spanned by the points),
2100
+ hence its Gale dual is acyclic (there is a linear functional that is
2101
+ positive in all the points of the configuration) when not homogenized::
2102
+
2103
+ sage: pc3 = PointConfiguration([[-1, -1, -1], [-1, 0, 0], [0, -1, 0], [0, 0, -1], [1, 0, 0], [0, 0, 1], [0, 1, 0]])
2104
+ sage: g_hom = pc3.Gale_transform(homogenize=True);g_hom
2105
+ [ 1 0 0 -2 1 -1 1]
2106
+ [ 0 1 0 -1 1 -1 0]
2107
+ [ 0 0 1 -1 0 -1 1]
2108
+ sage: g_inhom = pc3.Gale_transform(homogenize=False);g_inhom
2109
+ [1 0 0 0 1 1 1]
2110
+ [0 1 0 0 1 0 0]
2111
+ [0 0 1 0 0 0 1]
2112
+ [0 0 0 1 0 1 0]
2113
+ sage: Polyhedron(rays=g_hom.columns())
2114
+ A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 1 vertex and 3 lines
2115
+ sage: Polyhedron(rays=g_inhom.columns())
2116
+ A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 1 vertex and 4 rays
2117
+ """
2118
+ self._assert_is_affine()
2119
+ if points is None:
2120
+ points = self.points()
2121
+ else:
2122
+ try:
2123
+ points = [ self.point(ZZ(i)) for i in points ]
2124
+ except TypeError:
2125
+ pass
2126
+ if homogenize:
2127
+ m = matrix([(1,) + p.affine() for p in points])
2128
+ else:
2129
+ m = matrix([p.affine() for p in points])
2130
+ return m.left_kernel().matrix()
2131
+
2132
+ def deformation_cone(self, collection):
2133
+ r"""
2134
+ Return the deformation cone for the ``collection`` of subconfigurations
2135
+ of ``self``.
2136
+
2137
+ INPUT:
2138
+
2139
+ - ``collection`` -- a collection of subconfigurations of ``self``.
2140
+ Subconfigurations are given as indices
2141
+
2142
+ OUTPUT: a polyhedron. It contains the liftings of the point configuration
2143
+ making the collection a regular (or coherent, or projective, or
2144
+ polytopal) subdivision.
2145
+
2146
+ EXAMPLES::
2147
+
2148
+ sage: PC = PointConfiguration([(-1, -1), (-1, 0), (0, -1), (1, 0), (0, 1)])
2149
+ sage: coll = [(1, 4), (0, 2), (0, 1), (2, 3), (3, 4)]
2150
+ sage: dc = PC.deformation_cone(coll);dc
2151
+ A 5-dimensional polyhedron in QQ^5 defined as the convex hull of 1 vertex, 3 rays, 2 lines
2152
+ sage: dc.rays()
2153
+ (A ray in the direction (1, 0, 1, 0, 0),
2154
+ A ray in the direction (1, 1, 0, 0, 0),
2155
+ A ray in the direction (1, 1, 1, 0, 0))
2156
+ sage: dc.lines()
2157
+ (A line in the direction (1, 0, 1, 0, -1),
2158
+ A line in the direction (1, 1, 0, -1, 0))
2159
+ sage: dc.an_element()
2160
+ (3, 2, 2, 0, 0)
2161
+
2162
+ We add to the interior element the first line and we verify that the
2163
+ given rays are defining rays of the lower hull::
2164
+
2165
+ sage: P = Polyhedron(rays=[(-1, -1, 4), (-1, 0, 3), (0, -1, 2), (1, 0, -1), (0, 1, 0)])
2166
+ sage: P.rays()
2167
+ (A ray in the direction (-1, -1, 4),
2168
+ A ray in the direction (-1, 0, 3),
2169
+ A ray in the direction (0, -1, 2),
2170
+ A ray in the direction (0, 1, 0),
2171
+ A ray in the direction (1, 0, -1))
2172
+
2173
+ Let's verify the mother of all examples explained in Section 7.1.1 of
2174
+ [DLRS2010]_::
2175
+
2176
+ sage: def mother(epsilon=0):
2177
+ ....: return PointConfiguration([(4-epsilon,epsilon,0),(0,4-epsilon,epsilon),(epsilon,0,4-epsilon),(2,1,1),(1,2,1),(1,1,2)])
2178
+
2179
+ sage: epsilon = 0
2180
+ sage: m = mother(0)
2181
+ sage: m.points()
2182
+ (P(4, 0, 0), P(0, 4, 0), P(0, 0, 4), P(2, 1, 1), P(1, 2, 1), P(1, 1, 2))
2183
+ sage: S1 = [(0,1,4),(0,3,4),(1,2,5),(1,4,5),(0,2,3),(2,3,5)]
2184
+ sage: S2 = [(0,1,3),(1,3,4),(1,2,4),(2,4,5),(0,2,5),(0,3,5)]
2185
+
2186
+ Both subdivisions `S1` and `S2` are not regular::
2187
+
2188
+ sage: mother_dc1 = m.deformation_cone(S1)
2189
+ sage: mother_dc1
2190
+ A 4-dimensional polyhedron in QQ^6 defined as the convex hull of 1 vertex, 1 ray, 3 lines
2191
+ sage: mother_dc2 = m.deformation_cone(S2)
2192
+ sage: mother_dc2
2193
+ A 4-dimensional polyhedron in QQ^6 defined as the convex hull of 1 vertex, 1 ray, 3 lines
2194
+
2195
+ Notice that they have a ray which provides a degenerate lifting which
2196
+ only provides a coarsening of the subdivision from the lower hull (it
2197
+ has 5 facets, and should have 8)::
2198
+
2199
+ sage: result = Polyhedron([vector(list(m.points()[_])+[mother_dc1.rays()[0][_]]) for _ in range(len(m.points()))])
2200
+ sage: result.f_vector()
2201
+ (1, 6, 9, 5, 1)
2202
+
2203
+ But if we use epsilon to perturb the configuration, suddenly
2204
+ `S1` becomes regular::
2205
+
2206
+ sage: epsilon = 1/2
2207
+ sage: mp = mother(epsilon)
2208
+ sage: mp.points()
2209
+ (P(7/2, 1/2, 0),
2210
+ P(0, 7/2, 1/2),
2211
+ P(1/2, 0, 7/2),
2212
+ P(2, 1, 1),
2213
+ P(1, 2, 1),
2214
+ P(1, 1, 2))
2215
+ sage: mother_dc1 = mp.deformation_cone(S1);mother_dc1
2216
+ A 6-dimensional polyhedron in QQ^6 defined as the convex hull of 1 vertex, 3 rays, 3 lines
2217
+ sage: mother_dc2 = mp.deformation_cone(S2);mother_dc2
2218
+ A 3-dimensional polyhedron in QQ^6 defined as the convex hull of 1 vertex and 3 lines
2219
+
2220
+ .. SEEALSO::
2221
+
2222
+ :meth:`~sage.schemes.toric.variety.Kaehler_cone`
2223
+
2224
+ REFERENCES:
2225
+
2226
+ For more information, see Section 5.4 of [DLRS2010]_ and Section
2227
+ 2.2 of [ACEP2020].
2228
+ """
2229
+ from sage.geometry.polyhedron.constructor import Polyhedron
2230
+ gale = self.Gale_transform(homogenize=False)
2231
+ dual_rays = gale.columns()
2232
+ n = self.n_points()
2233
+ K = None
2234
+ for cone_indices in collection:
2235
+ dual_cone = Polyhedron(rays=[dual_rays[i] for i in range(n) if i not in cone_indices])
2236
+ K = K.intersection(dual_cone) if K is not None else dual_cone
2237
+ preimages = [gale.solve_right(r.vector()) for r in K.rays()]
2238
+ return Polyhedron(lines=matrix(self.points()).transpose().rows(),rays=preimages)
2239
+
2240
+ def plot(self, **kwds):
2241
+ r"""
2242
+ Produce a graphical representation of the point configuration.
2243
+
2244
+ EXAMPLES::
2245
+
2246
+ sage: p = PointConfiguration([[0,0], [0,1], [1,0], [1,1], [-1,-1]])
2247
+ sage: p.plot(axes=False) # needs sage.plot
2248
+ Graphics object consisting of 5 graphics primitives
2249
+
2250
+ .. PLOT::
2251
+ :width: 300 px
2252
+
2253
+ p = PointConfiguration([[0,0], [0,1], [1,0], [1,1], [-1,-1]])
2254
+ sphinx_plot(p.plot(axes=False))
2255
+ """
2256
+ return self.element_class([], parent=self, check=False).plot(**kwds)