passagemath-polyhedra 10.6.31rc3__cp314-cp314-musllinux_1_2_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.
- passagemath_polyhedra-10.6.31rc3.dist-info/METADATA +367 -0
- passagemath_polyhedra-10.6.31rc3.dist-info/METADATA.bak +369 -0
- passagemath_polyhedra-10.6.31rc3.dist-info/RECORD +208 -0
- passagemath_polyhedra-10.6.31rc3.dist-info/WHEEL +5 -0
- passagemath_polyhedra-10.6.31rc3.dist-info/top_level.txt +2 -0
- passagemath_polyhedra.libs/libgcc_s-0cd532bd.so.1 +0 -0
- passagemath_polyhedra.libs/libgmp-0e7fc84e.so.10.5.0 +0 -0
- passagemath_polyhedra.libs/libgomp-8949ffbe.so.1.0.0 +0 -0
- passagemath_polyhedra.libs/libstdc++-5d72f927.so.6.0.33 +0 -0
- sage/all__sagemath_polyhedra.py +50 -0
- sage/game_theory/all.py +8 -0
- sage/game_theory/catalog.py +6 -0
- sage/game_theory/catalog_normal_form_games.py +923 -0
- sage/game_theory/cooperative_game.py +844 -0
- sage/game_theory/matching_game.py +1181 -0
- sage/game_theory/normal_form_game.py +2697 -0
- sage/game_theory/parser.py +275 -0
- sage/geometry/all__sagemath_polyhedra.py +22 -0
- sage/geometry/cone.py +6940 -0
- sage/geometry/cone_catalog.py +847 -0
- sage/geometry/cone_critical_angles.py +1027 -0
- sage/geometry/convex_set.py +1119 -0
- sage/geometry/fan.py +3743 -0
- sage/geometry/fan_isomorphism.py +389 -0
- sage/geometry/fan_morphism.py +1884 -0
- sage/geometry/hasse_diagram.py +202 -0
- sage/geometry/hyperplane_arrangement/affine_subspace.py +390 -0
- sage/geometry/hyperplane_arrangement/all.py +1 -0
- sage/geometry/hyperplane_arrangement/arrangement.py +3895 -0
- sage/geometry/hyperplane_arrangement/check_freeness.py +145 -0
- sage/geometry/hyperplane_arrangement/hyperplane.py +773 -0
- sage/geometry/hyperplane_arrangement/library.py +825 -0
- sage/geometry/hyperplane_arrangement/ordered_arrangement.py +642 -0
- sage/geometry/hyperplane_arrangement/plot.py +520 -0
- sage/geometry/integral_points.py +35 -0
- sage/geometry/integral_points_generic_dense.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/integral_points_generic_dense.pyx +7 -0
- sage/geometry/lattice_polytope.py +5894 -0
- sage/geometry/linear_expression.py +773 -0
- sage/geometry/newton_polygon.py +767 -0
- sage/geometry/point_collection.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/point_collection.pyx +1008 -0
- sage/geometry/polyhedral_complex.py +2616 -0
- sage/geometry/polyhedron/all.py +8 -0
- sage/geometry/polyhedron/backend_cdd.py +460 -0
- sage/geometry/polyhedron/backend_cdd_rdf.py +231 -0
- sage/geometry/polyhedron/backend_field.py +347 -0
- sage/geometry/polyhedron/backend_normaliz.py +2503 -0
- sage/geometry/polyhedron/backend_number_field.py +168 -0
- sage/geometry/polyhedron/backend_polymake.py +765 -0
- sage/geometry/polyhedron/backend_ppl.py +582 -0
- sage/geometry/polyhedron/base.py +1206 -0
- sage/geometry/polyhedron/base0.py +1444 -0
- sage/geometry/polyhedron/base1.py +886 -0
- sage/geometry/polyhedron/base2.py +812 -0
- sage/geometry/polyhedron/base3.py +1845 -0
- sage/geometry/polyhedron/base4.py +1262 -0
- sage/geometry/polyhedron/base5.py +2700 -0
- sage/geometry/polyhedron/base6.py +1741 -0
- sage/geometry/polyhedron/base7.py +997 -0
- sage/geometry/polyhedron/base_QQ.py +1258 -0
- sage/geometry/polyhedron/base_RDF.py +98 -0
- sage/geometry/polyhedron/base_ZZ.py +934 -0
- sage/geometry/polyhedron/base_mutable.py +215 -0
- sage/geometry/polyhedron/base_number_field.py +122 -0
- sage/geometry/polyhedron/cdd_file_format.py +155 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/all.py +1 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +76 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +3859 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +39 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +1038 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd +9 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx +501 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd +207 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +102 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +2274 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd +370 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pyx +84 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd +31 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +587 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd +52 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +560 -0
- sage/geometry/polyhedron/constructor.py +773 -0
- sage/geometry/polyhedron/double_description.py +753 -0
- sage/geometry/polyhedron/double_description_inhomogeneous.py +564 -0
- sage/geometry/polyhedron/face.py +1060 -0
- sage/geometry/polyhedron/generating_function.py +1810 -0
- sage/geometry/polyhedron/lattice_euclidean_group_element.py +178 -0
- sage/geometry/polyhedron/library.py +3502 -0
- sage/geometry/polyhedron/misc.py +121 -0
- sage/geometry/polyhedron/modules/all.py +1 -0
- sage/geometry/polyhedron/modules/formal_polyhedra_module.py +155 -0
- sage/geometry/polyhedron/palp_database.py +447 -0
- sage/geometry/polyhedron/parent.py +1279 -0
- sage/geometry/polyhedron/plot.py +1986 -0
- sage/geometry/polyhedron/ppl_lattice_polygon.py +556 -0
- sage/geometry/polyhedron/ppl_lattice_polytope.py +1257 -0
- sage/geometry/polyhedron/representation.py +1723 -0
- sage/geometry/pseudolines.py +515 -0
- sage/geometry/relative_interior.py +445 -0
- sage/geometry/toric_plotter.py +1103 -0
- sage/geometry/triangulation/all.py +2 -0
- sage/geometry/triangulation/base.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/triangulation/base.pyx +963 -0
- sage/geometry/triangulation/data.h +147 -0
- sage/geometry/triangulation/data.pxd +4 -0
- sage/geometry/triangulation/element.py +914 -0
- sage/geometry/triangulation/functions.h +10 -0
- sage/geometry/triangulation/functions.pxd +4 -0
- sage/geometry/triangulation/point_configuration.py +2256 -0
- sage/geometry/triangulation/triangulations.h +49 -0
- sage/geometry/triangulation/triangulations.pxd +7 -0
- sage/geometry/voronoi_diagram.py +319 -0
- sage/interfaces/all__sagemath_polyhedra.py +1 -0
- sage/interfaces/polymake.py +2028 -0
- sage/numerical/all.py +13 -0
- sage/numerical/all__sagemath_polyhedra.py +11 -0
- sage/numerical/backends/all.py +1 -0
- sage/numerical/backends/all__sagemath_polyhedra.py +1 -0
- sage/numerical/backends/cvxopt_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/cvxopt_backend.pyx +1006 -0
- sage/numerical/backends/cvxopt_backend_test.py +19 -0
- sage/numerical/backends/cvxopt_sdp_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/cvxopt_sdp_backend.pyx +382 -0
- sage/numerical/backends/cvxpy_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/cvxpy_backend.pxd +41 -0
- sage/numerical/backends/cvxpy_backend.pyx +934 -0
- sage/numerical/backends/cvxpy_backend_test.py +13 -0
- sage/numerical/backends/generic_backend_test.py +24 -0
- sage/numerical/backends/interactivelp_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/interactivelp_backend.pxd +36 -0
- sage/numerical/backends/interactivelp_backend.pyx +1231 -0
- sage/numerical/backends/interactivelp_backend_test.py +12 -0
- sage/numerical/backends/logging_backend.py +391 -0
- sage/numerical/backends/matrix_sdp_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/matrix_sdp_backend.pxd +15 -0
- sage/numerical/backends/matrix_sdp_backend.pyx +478 -0
- sage/numerical/backends/ppl_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/ppl_backend.pyx +1126 -0
- sage/numerical/backends/ppl_backend_test.py +13 -0
- sage/numerical/backends/scip_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/scip_backend.pxd +22 -0
- sage/numerical/backends/scip_backend.pyx +1289 -0
- sage/numerical/backends/scip_backend_test.py +13 -0
- sage/numerical/interactive_simplex_method.py +5338 -0
- sage/numerical/knapsack.py +665 -0
- sage/numerical/linear_functions.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/linear_functions.pxd +31 -0
- sage/numerical/linear_functions.pyx +1648 -0
- sage/numerical/linear_tensor.py +470 -0
- sage/numerical/linear_tensor_constraints.py +448 -0
- sage/numerical/linear_tensor_element.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/linear_tensor_element.pxd +6 -0
- sage/numerical/linear_tensor_element.pyx +459 -0
- sage/numerical/mip.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/mip.pxd +40 -0
- sage/numerical/mip.pyx +3667 -0
- sage/numerical/sdp.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/sdp.pxd +39 -0
- sage/numerical/sdp.pyx +1433 -0
- sage/rings/all__sagemath_polyhedra.py +3 -0
- sage/rings/polynomial/all__sagemath_polyhedra.py +10 -0
- sage/rings/polynomial/omega.py +982 -0
- sage/schemes/all__sagemath_polyhedra.py +2 -0
- sage/schemes/toric/all.py +10 -0
- sage/schemes/toric/chow_group.py +1248 -0
- sage/schemes/toric/divisor.py +2082 -0
- sage/schemes/toric/divisor_class.cpython-314-x86_64-linux-musl.so +0 -0
- sage/schemes/toric/divisor_class.pyx +322 -0
- sage/schemes/toric/fano_variety.py +1606 -0
- sage/schemes/toric/homset.py +650 -0
- sage/schemes/toric/ideal.py +451 -0
- sage/schemes/toric/library.py +1322 -0
- sage/schemes/toric/morphism.py +1958 -0
- sage/schemes/toric/points.py +1032 -0
- sage/schemes/toric/sheaf/all.py +1 -0
- sage/schemes/toric/sheaf/constructor.py +302 -0
- sage/schemes/toric/sheaf/klyachko.py +921 -0
- sage/schemes/toric/toric_subscheme.py +905 -0
- sage/schemes/toric/variety.py +3460 -0
- sage/schemes/toric/weierstrass.py +1078 -0
- sage/schemes/toric/weierstrass_covering.py +457 -0
- sage/schemes/toric/weierstrass_higher.py +288 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.info +10 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v03 +0 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v04 +0 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v05 +1 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v06 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.info +22 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v04 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v05 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v06 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v07 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v08 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v09 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v10 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v11 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v12 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v13 +1 -0
- sage_wheels/share/reflexive_polytopes/reflexive_polytopes_2d +80 -0
- 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)
|