Skip to content

Commit 44df4ed

Browse files
author
Release Manager
committed
sagemathgh-39496: Add deformation cones and checking for regularity for Point Configurations and normal fans of Polyhedra In this pull request, we add the method `deformation_cone` (to point configurations and polyhedron) and `is_polytopal` (to fans). This is related to the Kahler cone of Toric Varieties, but has some subtle differences to make it work as it should in the discrete geometry context. Therefore, it has a separate implementation. TODO: In the future, perhaps it could be fusioned. ```sage sage: tc = Polyhedron([(1, -1), (1/3, 1), (1, 1/3), (-1, 1), (-1, -1)]) sage: dc = tc.deformation_cone() sage: dc.an_element() (2, 1, 1, 0, 0) sage: [_.A() for _ in tc.Hrepresentation()] [(1, 0), (0, 1), (0, -1), (-3, -3), (-1, 0)] sage: P = Polyhedron(rays=[(1, 0, 2), (0, 1, 1), (0, -1, 1), (-3, -3, 0), (-1, 0, 0)]) sage: P.rays() (A ray in the direction (-1, -1, 0), A ray in the direction (-1, 0, 0), A ray in the direction (0, -1, 1), A ray in the direction (0, 1, 1), A ray in the direction (1, 0, 2)) sage: py = Polyhedron([(0, -1, -1), (0, -1, 1), (0, 1, -1), (0, 1, 1), (1, 0, 0)]) sage: dc_py = py.deformation_cone(); dc_py A 4-dimensional polyhedron in QQ^5 defined as the convex hull of 1 vertex, 1 ray, 3 lines sage: [ineq.b() for ineq in py.Hrepresentation()] [0, 1, 1, 1, 1] sage: r = dc_py.rays()[0] sage: l1,l2,l3 = dc_py.lines() sage: r.vector()-l1.vector()/2-l2.vector()-l3.vector()/2 (0, 1, 1, 1, 1) ``` ### 📝 Checklist <!-- Put an `x` in all the boxes that apply. --> - [x] The title is concise and informative. - [x] The description explains in detail what this PR is about. - [x] I have linked a relevant issue or discussion. - [x] I have created tests covering the changes. - [ ] I have updated the documentation and checked the documentation preview. URL: sagemath#39496 Reported by: JP Labbe Reviewer(s): Frédéric Chapoton
2 parents 136ea1c + 2a9328a commit 44df4ed

File tree

4 files changed

+304
-13
lines changed

4 files changed

+304
-13
lines changed

src/doc/en/reference/references/index.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ REFERENCES:
104104
graphs and isoperimetric inequalities*, The Annals of Probability
105105
32 (2004), no. 3A, 1727-1745.
106106
107+
.. [ACEP2020] Federico Ardila, Federico Castillo, Christopher Eur, Alexander Postnikov,
108+
*Coxeter submodular functions and deformations of Coxeter permutahedra*,
109+
Advances in Mathematics, Volume 365, 13 May 2020.
110+
107111
.. [ALL2002] P. Auger, G. Labelle and P. Leroux, *Combinatorial
108112
addition formulas and applications*, Advances in Applied
109113
Mathematics 28 (2002) 302-342.

src/sage/geometry/fan.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2439,6 +2439,69 @@ def Gale_transform(self):
24392439
m = m.augment(matrix(ZZ, m.nrows(), 1, [1] * m.nrows()))
24402440
return matrix(ZZ, m.integer_kernel().matrix())
24412441

2442+
def is_polytopal(self) -> bool:
2443+
r"""
2444+
Check if ``self`` is the normal fan of a polytope.
2445+
2446+
A rational polyhedral fan is *polytopal* if it is the normal fan of a
2447+
polytope. This is also called *regular*, or provides a *coherent*
2448+
subdivision or leads to a *projective* toric variety.
2449+
2450+
OUTPUT: ``True`` if ``self`` is polytopal and ``False`` otherwise
2451+
2452+
EXAMPLES:
2453+
2454+
This is the mother of all examples (see Section 7.1.1 in
2455+
[DLRS2010]_)::
2456+
2457+
sage: def mother(epsilon=0):
2458+
....: rays = [(4-epsilon,epsilon,0),(0,4-epsilon,epsilon),(epsilon,0,4-epsilon),(2,1,1),(1,2,1),(1,1,2),(-1,-1,-1)]
2459+
....: L = [(0,1,4),(0,3,4),(1,2,5),(1,4,5),(0,2,3),(2,3,5),(3,4,5),(6,0,1),(6,1,2),(6,2,0)]
2460+
....: S1 = [Cone([rays[i] for i in indices]) for indices in L]
2461+
....: return Fan(S1)
2462+
2463+
When epsilon=0, it is not polytopal::
2464+
2465+
sage: epsilon = 0
2466+
sage: mother(epsilon).is_polytopal()
2467+
False
2468+
2469+
Doing a slight perturbation makes the same subdivision polytopal::
2470+
2471+
sage: epsilon = 1/2
2472+
sage: mother(epsilon).is_polytopal()
2473+
True
2474+
2475+
TESTS::
2476+
2477+
sage: cone = Cone([(1,1), (2,1)])
2478+
sage: F = Fan([cone])
2479+
sage: F.is_polytopal()
2480+
Traceback (most recent call last):
2481+
...
2482+
ValueError: to be polytopal, the fan should be complete
2483+
2484+
.. SEEALSO::
2485+
2486+
:meth:`is_projective`.
2487+
"""
2488+
if not self.is_complete():
2489+
raise ValueError('to be polytopal, the fan should be complete')
2490+
from sage.geometry.triangulation.point_configuration import PointConfiguration
2491+
from sage.geometry.polyhedron.constructor import Polyhedron
2492+
pc = PointConfiguration(self.rays())
2493+
v_pc = [tuple(p) for p in pc]
2494+
pc_to_indices = {tuple(p):i for i, p in enumerate(pc)}
2495+
indices_to_vr = (tuple(r) for r in self.rays())
2496+
cone_indices = (cone.ambient_ray_indices() for cone in self.generating_cones())
2497+
translator = [pc_to_indices[t] for t in indices_to_vr]
2498+
translated_cone_indices = [[translator[i] for i in ci] for ci in cone_indices]
2499+
dc_pc = pc.deformation_cone(translated_cone_indices)
2500+
lift = dc_pc.an_element()
2501+
ieqs = [(lift_i,) + v for (lift_i, v) in zip(lift, v_pc)]
2502+
poly = Polyhedron(ieqs=ieqs)
2503+
return self.is_equivalent(poly.normal_fan())
2504+
24422505
def generating_cone(self, n):
24432506
r"""
24442507
Return the ``n``-th generating cone of ``self``.

src/sage/geometry/polyhedron/base5.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,72 @@ def lawrence_polytope(self):
659659
parent = self.parent().change_ring(self.base_ring(), ambient_dim=self.ambient_dim() + n)
660660
return parent.element_class(parent, [lambda_V, [], []], None)
661661

662+
def deformation_cone(self):
663+
r"""
664+
Return the deformation cone of ``self``.
665+
666+
Let `P` be a `d`-polytope in `\RR^r` with `n` facets. The deformation
667+
cone is a polyhedron in `\RR^n` whose points are the right-hand side `b`
668+
in `Ax\leq b` where `A` is the matrix of facet normals of ``self``, so
669+
that the resulting polytope has a normal fan which is a coarsening of
670+
the normal fan of ``self``.
671+
672+
EXAMPLES:
673+
674+
Let's examine the deformation cone of the square with one truncated
675+
vertex::
676+
677+
sage: tc = Polyhedron([(1, -1), (1/3, 1), (1, 1/3), (-1, 1), (-1, -1)])
678+
sage: dc = tc.deformation_cone()
679+
sage: dc.an_element()
680+
(2, 1, 1, 0, 0)
681+
sage: [_.A() for _ in tc.Hrepresentation()]
682+
[(1, 0), (0, 1), (0, -1), (-3, -3), (-1, 0)]
683+
sage: P = Polyhedron(rays=[(1, 0, 2), (0, 1, 1), (0, -1, 1), (-3, -3, 0), (-1, 0, 0)])
684+
sage: P.rays()
685+
(A ray in the direction (-1, -1, 0),
686+
A ray in the direction (-1, 0, 0),
687+
A ray in the direction (0, -1, 1),
688+
A ray in the direction (0, 1, 1),
689+
A ray in the direction (1, 0, 2))
690+
691+
Now, let's compute the deformation cone of the pyramid over a square
692+
and verify that it is not full dimensional::
693+
694+
sage: py = Polyhedron([(0, -1, -1), (0, -1, 1), (0, 1, -1), (0, 1, 1), (1, 0, 0)])
695+
sage: dc_py = py.deformation_cone(); dc_py
696+
A 4-dimensional polyhedron in QQ^5 defined as the convex hull of 1 vertex, 1 ray, 3 lines
697+
sage: [ineq.b() for ineq in py.Hrepresentation()]
698+
[0, 1, 1, 1, 1]
699+
sage: r = dc_py.rays()[0]
700+
sage: l1,l2,l3 = dc_py.lines()
701+
sage: r.vector()-l1.vector()/2-l2.vector()-l3.vector()/2
702+
(0, 1, 1, 1, 1)
703+
704+
.. SEEALSO::
705+
706+
:meth:`~sage.schemes.toric.variety.Kaehler_cone`
707+
708+
REFERENCES:
709+
710+
For more information, see Section 5.4 of [DLRS2010]_ and Section
711+
2.2 of [ACEP2020].
712+
"""
713+
from .constructor import Polyhedron
714+
m = matrix([ineq.A() for ineq in self.Hrepresentation()])
715+
m = m.transpose()
716+
m_ker = m.right_kernel_matrix(basis='computed')
717+
gale = tuple(m_ker.columns())
718+
collection = (f.ambient_H_indices() for f in self.faces(0))
719+
n = len(gale)
720+
c = None
721+
for cone_indices in collection:
722+
dual_cone = Polyhedron(rays=[gale[i] for i in range(n) if i not in
723+
cone_indices])
724+
c = c.intersection(dual_cone) if c is not None else dual_cone
725+
preimages = [m_ker.solve_right(r.vector()) for r in c.rays()]
726+
return Polyhedron(lines=m.rows(), rays=preimages)
727+
662728
###########################################################
663729
# Binary operations.
664730
###########################################################

0 commit comments

Comments
 (0)