def LatticePolytope_PPL(*args): """ Construct a new instance of the PPL-based lattice polytope class. EXAMPLES:: sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL sage: LatticePolytope_PPL((0,0),(1,0),(0,1)) A 2-dimensional lattice polytope in ZZ^2 with 3 vertices sage: from sage.libs.ppl import point, Generator_System, C_Polyhedron, Linear_Expression, Variable sage: p = point(Linear_Expression([2,3],0)); p point(2/1, 3/1) sage: LatticePolytope_PPL(p) A 0-dimensional lattice polytope in ZZ^2 with 1 vertex sage: P = C_Polyhedron(Generator_System(p)); P A 0-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point sage: LatticePolytope_PPL(P) A 0-dimensional lattice polytope in ZZ^2 with 1 vertex A ``TypeError`` is raised if the arguments do not specify a lattice polytope:: sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL sage: LatticePolytope_PPL((0,0),(1/2,1)) Traceback (most recent call last): ... TypeError: no conversion of this rational to integer sage: from sage.libs.ppl import point, Generator_System, C_Polyhedron, Linear_Expression, Variable sage: p = point(Linear_Expression([2,3],0), 5); p point(2/5, 3/5) sage: LatticePolytope_PPL(p) Traceback (most recent call last): ... TypeError: generator is not a lattice polytope generator sage: P = C_Polyhedron(Generator_System(p)); P A 0-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point sage: LatticePolytope_PPL(P) Traceback (most recent call last): ... TypeError: polyhedron has non-integral generators """ polytope_class = LatticePolytope_PPL_class if len(args) == 1 and isinstance(args[0], C_Polyhedron): polyhedron = args[0] polytope_class = _class_for_LatticePolytope( polyhedron.space_dimension()) if not all(p.is_point() and p.divisor().is_one() for p in polyhedron.generators()): raise TypeError('polyhedron has non-integral generators') return polytope_class(polyhedron) if len(args)==1 \ and isinstance(args[0], (list, tuple)) \ and isinstance(args[0][0], (list,tuple)): vertices = args[0] else: vertices = args gs = Generator_System() for v in vertices: if isinstance(v, Generator): if (not v.is_point()) or (not v.divisor().is_one()): raise TypeError( 'generator is not a lattice polytope generator') gs.insert(v) else: gs.insert(point(Linear_Expression(v, 0))) if not gs.empty(): dim = next(Generator_System_iterator(gs)).space_dimension() polytope_class = _class_for_LatticePolytope(dim) return polytope_class(gs)
def _init_from_Vrepresentation(self, vertices, rays, lines, minimize=True, verbose=False): """ Construct polyhedron from V-representation data. INPUT: - ``vertices`` -- list of point. Each point can be specified as any iterable container of :meth:`~sage.geometry.polyhedron.base.base_ring` elements. - ``rays`` -- list of rays. Each ray can be specified as any iterable container of :meth:`~sage.geometry.polyhedron.base.base_ring` elements. - ``lines`` -- list of lines. Each line can be specified as any iterable container of :meth:`~sage.geometry.polyhedron.base.base_ring` elements. - ``verbose`` -- boolean (default: ``False``). Whether to print verbose output for debugging purposes. EXAMPLES:: sage: p = Polyhedron(backend='ppl') sage: from sage.geometry.polyhedron.backend_ppl import Polyhedron_ppl sage: Polyhedron_ppl._init_from_Vrepresentation(p, [], [], []) """ gs = Generator_System() if vertices is None: vertices = [] for v in vertices: d = LCM_list([denominator(v_i) for v_i in v]) if d.is_one(): gs.insert(point(Linear_Expression(v, 0))) else: dv = [d * v_i for v_i in v] gs.insert(point(Linear_Expression(dv, 0), d)) if rays is None: rays = [] for r in rays: d = LCM_list([denominator(r_i) for r_i in r]) if d.is_one(): gs.insert(ray(Linear_Expression(r, 0))) else: dr = [d * r_i for r_i in r] gs.insert(ray(Linear_Expression(dr, 0))) if lines is None: lines = [] for l in lines: d = LCM_list([denominator(l_i) for l_i in l]) if d.is_one(): gs.insert(line(Linear_Expression(l, 0))) else: dl = [d * l_i for l_i in l] gs.insert(line(Linear_Expression(dl, 0))) if gs.empty(): self._ppl_polyhedron = C_Polyhedron(self.ambient_dim(), 'empty') else: self._ppl_polyhedron = C_Polyhedron(gs) self._init_Vrepresentation_from_ppl(minimize) self._init_Hrepresentation_from_ppl(minimize)
def fibration_generator(self, dim): """ Generate the lattice polytope fibrations. For the purposes of this function, a lattice polytope fiber is a sub-lattice polytope. Projecting the plane spanned by the subpolytope to a point yields another lattice polytope, the base of the fibration. INPUT: - ``dim`` -- integer. The dimension of the lattice polytope fiber. OUTPUT: A generator yielding the distinct lattice polytope fibers of given dimension. EXAMPLES:: sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL sage: p = LatticePolytope_PPL((-9,-6,-1,-1),(0,0,0,1),(0,0,1,0),(0,1,0,0),(1,0,0,0)) sage: list( p.fibration_generator(2) ) [A 2-dimensional lattice polytope in ZZ^4 with 3 vertices] """ assert self.is_full_dimensional() codim = self.space_dimension() - dim # "points" are the potential vertices of the fiber. They are # in the $codim$-skeleton of the polytope, which is contained # in the points that saturate at least $dim$ equations. points = [ p for p in self._integral_points_saturating() if len(p[1]) >= dim ] points = sorted(points, key=lambda x: len(x[1])) # iterate over point combinations subject to all points being on one facet. def point_combinations_iterator(n, i0=0, saturated=None): for i in range(i0, len(points)): p, ieqs = points[i] if saturated is None: saturated_ieqs = ieqs else: saturated_ieqs = saturated.intersection(ieqs) if len(saturated_ieqs) == 0: continue if n == 1: yield [i] else: for c in point_combinations_iterator( n - 1, i + 1, saturated_ieqs): yield [i] + c point_lines = [line(Linear_Expression(p[0].list(), 0)) for p in points] origin = point() fibers = set() gs = Generator_System() for indices in point_combinations_iterator(dim): gs.clear() gs.insert(origin) for i in indices: gs.insert(point_lines[i]) plane = C_Polyhedron(gs) if plane.affine_dimension() != dim: continue plane.intersection_assign(self) if (not self.is_full_dimensional()) and (plane.affine_dimension() != dim): continue try: fiber = LatticePolytope_PPL(plane) except TypeError: # not a lattice polytope continue fiber_vertices = tuple(sorted(fiber.vertices())) if fiber_vertices not in fibers: yield fiber fibers.update([fiber_vertices])
def normal_cone(self): r""" Return the (closure of the) normal cone of the triangulation. Recall that a regular triangulation is one that equals the "crease lines" of a convex piecewise-linear function. This support function is not unique, for example, you can scale it by a positive constant. The set of all piecewise-linear functions with fixed creases forms an open cone. This cone can be interpreted as the cone of normal vectors at a point of the secondary polytope, which is why we call it normal cone. See [GKZ]_ Section 7.1 for details. OUTPUT: The closure of the normal cone. The `i`-th entry equals the value of the piecewise-linear function at the `i`-th point of the configuration. For an irregular triangulation, the normal cone is empty. In this case, a single point (the origin) is returned. EXAMPLES:: sage: triangulation = polytopes.n_cube(2).triangulate(engine='internal') sage: triangulation (<0,1,3>, <0,2,3>) sage: N = triangulation.normal_cone(); N 4-d cone in 4-d lattice sage: N.rays() (-1, 0, 0, 0), ( 1, 0, 1, 0), (-1, 0, -1, 0), ( 1, 0, 0, -1), (-1, 0, 0, 1), ( 1, 1, 0, 0), (-1, -1, 0, 0) in Ambient free module of rank 4 over the principal ideal domain Integer Ring sage: N.dual().rays() (-1, 1, 1, -1) in Ambient free module of rank 4 over the principal ideal domain Integer Ring TESTS:: sage: polytopes.n_simplex(2).triangulate().normal_cone() 3-d cone in 3-d lattice sage: _.dual().is_trivial() True """ if not self.point_configuration().base_ring().is_subring(QQ): raise NotImplementedError( 'Only base rings ZZ and QQ are supported') from sage.libs.ppl import Variable, Constraint, Constraint_System, Linear_Expression, C_Polyhedron from sage.matrix.constructor import matrix from sage.misc.misc import uniq from sage.rings.arith import lcm pc = self.point_configuration() cs = Constraint_System() for facet in self.interior_facets(): s0, s1 = self._boundary_simplex_dictionary()[facet] p = set(s0).difference(facet).pop() q = set(s1).difference(facet).pop() origin = pc.point(p).reduced_affine_vector() base_indices = [i for i in s0 if i != p] base = matrix([ pc.point(i).reduced_affine_vector() - origin for i in base_indices ]) sol = base.solve_left(pc.point(q).reduced_affine_vector() - origin) relation = [0] * pc.n_points() relation[p] = sum(sol) - 1 relation[q] = 1 for i, base_i in enumerate(base_indices): relation[base_i] = -sol[i] rel_denom = lcm([QQ(r).denominator() for r in relation]) relation = [ZZ(r * rel_denom) for r in relation] ex = Linear_Expression(relation, 0) cs.insert(ex >= 0) from sage.modules.free_module import FreeModule ambient = FreeModule(ZZ, self.point_configuration().n_points()) if cs.empty(): cone = C_Polyhedron(ambient.dimension(), 'universe') else: cone = C_Polyhedron(cs) from sage.geometry.cone import _Cone_from_PPL return _Cone_from_PPL(cone, lattice=ambient)