def position(self, hyperplane): x = [Variable(i) for i in range(self.dim)] p = C_Polyhedron(Generator_System(self.ppl)) s_rel = p.relation_with( sum(hyperplane.a[i] * x[i] for i in range(self.dim)) + hyperplane.b < 0) if s_rel.implies(Poly_Con_Relation.is_included()): return -1 else: b_rel = p.relation_with( sum(hyperplane.a[i] * x[i] for i in range(self.dim)) + hyperplane.b > 0) if b_rel.implies(Poly_Con_Relation.is_included()): return 1 else: return 0
def has_IP_property(self): """ Whether the lattice polytope has the IP property. That is, the polytope is full-dimensional and the origin is a interior point not on the boundary. OUTPUT: Boolean. EXAMPLES:: sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL sage: LatticePolytope_PPL((-1,-1),(0,1),(1,0)).has_IP_property() True sage: LatticePolytope_PPL((-1,-1),(1,1)).has_IP_property() False """ origin = C_Polyhedron(point(0*Variable(self.space_dimension()))) is_included = Poly_Con_Relation.is_included() saturates = Poly_Con_Relation.saturates() for c in self.constraints(): rel = origin.relation_with(c) if (not rel.implies(is_included)) or rel.implies(saturates): return False return True
def contains(self, point_coordinates): r""" Test whether point is contained in the polytope. INPUT: - ``point_coordinates`` -- a list/tuple/iterable of rational numbers. The coordinates of the point. OUTPUT: Boolean. EXAMPLES:: sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL sage: line = LatticePolytope_PPL((1,2,3), (-1,-2,-3)) sage: line.contains([0,0,0]) True sage: line.contains([1,0,0]) False """ p = C_Polyhedron(point(Linear_Expression(list(point_coordinates), 1))) is_included = Poly_Con_Relation.is_included() for c in self.constraints(): if not p.relation_with(c).implies(is_included): return False return True
def vertices_saturating(self, constraint): r""" Return the vertices saturating the constraint INPUT: - ``constraint`` -- a constraint (inequality or equation) of the polytope. OUTPUT: The tuple of vertices saturating the constraint. The vertices are returned as `\ZZ`-vectors, as in :meth:`vertices`. EXAMPLES:: sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL sage: p = LatticePolytope_PPL((0,0),(0,1),(1,0)) sage: ieq = next(iter(p.constraints())); ieq x0>=0 sage: p.vertices_saturating(ieq) ((0, 0), (0, 1)) """ from ppl import C_Polyhedron, Poly_Con_Relation result = [] for i,v in enumerate(self.minimized_generators()): v = C_Polyhedron(v) if v.relation_with(constraint).implies(Poly_Con_Relation.saturates()): result.append(self.vertices()[i]) return tuple(result)
def find_isomorphism(self, polytope): r""" Return a lattice isomorphism with ``polytope``. INPUT: - ``polytope`` -- a polytope, potentially higher-dimensional. OUTPUT: A :class:`~sage.geometry.polyhedron.lattice_euclidean_group_element.LatticeEuclideanGroupElement`. It is not necessarily invertible if the affine dimension of ``self`` or ``polytope`` is not two. A :class:`~sage.geometry.polyhedron.lattice_euclidean_group_element.LatticePolytopesNotIsomorphicError` is raised if no such isomorphism exists. EXAMPLES:: sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL sage: L1 = LatticePolytope_PPL((1,0),(0,1),(0,0)) sage: L2 = LatticePolytope_PPL((1,0,3),(0,1,0),(0,0,1)) sage: iso = L1.find_isomorphism(L2) sage: iso(L1) == L2 True sage: L1 = LatticePolytope_PPL((0, 1), (3, 0), (0, 3), (1, 0)) sage: L2 = LatticePolytope_PPL((0,0,2,1),(0,1,2,0),(2,0,0,3),(2,3,0,0)) sage: iso = L1.find_isomorphism(L2) sage: iso(L1) == L2 True The following polygons are isomorphic over `\QQ`, but not as lattice polytopes:: sage: L1 = LatticePolytope_PPL((1,0),(0,1),(-1,-1)) sage: L2 = LatticePolytope_PPL((0, 0), (0, 1), (1, 0)) sage: L1.find_isomorphism(L2) Traceback (most recent call last): ... LatticePolytopesNotIsomorphicError: different number of integral points sage: L2.find_isomorphism(L1) Traceback (most recent call last): ... LatticePolytopesNotIsomorphicError: different number of integral points """ from sage.geometry.polyhedron.lattice_euclidean_group_element import \ LatticePolytopesNotIsomorphicError if polytope.affine_dimension() != self.affine_dimension(): raise LatticePolytopesNotIsomorphicError('different dimension') polytope_vertices = polytope.vertices() if len(polytope_vertices) != self.n_vertices(): raise LatticePolytopesNotIsomorphicError( 'different number of vertices') self_vertices = self.ordered_vertices() if len(polytope.integral_points()) != len(self.integral_points()): raise LatticePolytopesNotIsomorphicError( 'different number of integral points') if len(self_vertices) < 3: return self._find_isomorphism_degenerate(polytope) polytope_origin = polytope_vertices[0] origin_P = C_Polyhedron(next(iter(polytope.minimized_generators()))) neighbors = [] for c in polytope.minimized_constraints(): if not c.is_inequality(): continue if origin_P.relation_with(c).implies( Poly_Con_Relation.saturates()): for i, g in enumerate(polytope.minimized_generators()): if i == 0: continue g = C_Polyhedron(g) if g.relation_with(c).implies( Poly_Con_Relation.saturates()): neighbors.append(polytope_vertices[i]) break p_ray_left = neighbors[0] - polytope_origin p_ray_right = neighbors[1] - polytope_origin try: return self._find_cyclic_isomorphism_matching_edge( polytope, polytope_origin, p_ray_left, p_ray_right) except LatticePolytopesNotIsomorphicError: pass try: return self._find_cyclic_isomorphism_matching_edge( polytope, polytope_origin, p_ray_right, p_ray_left) except LatticePolytopesNotIsomorphicError: pass raise LatticePolytopesNotIsomorphicError('different polygons')
class Polytope(object): def __init__(self, halfspaces=None, vertices=None): if halfspaces is not None: self.halfspaces = halfspaces self.dim = halfspaces[0][0].dim self.poly = self.poly_from_constraints(halfspaces) self.vertices = self.vertices_from_poly() elif vertices is not None: v = vertices.pop() self.dim = v.dim vertices.add(v) self.poly = self.poly_from_vertices(vertices) self.vertices = self.vertices_from_poly() self.hyperplanes = self.hyperplanes_from_poly() self.halfspaces = zip(self.hyperplanes, [1] * len(self.hyperplanes)) else: self.hyperplanes = [] self.halfspaces = [] self.vertices = [] self.poly = C_Polyhedron(0) def as_convex_hull(self): vertices = np.array([vertex.coordinates for vertex in self.vertices]) convex_hull = ConvexHull(vertices, qhull_options='Pp') return convex_hull def get_vertex_coordinates(self): return np.array([v.coordinates for v in self.vertices]) def vertices_from_poly(self): # Get vertices of polytope resulting from intersection. only points not rays return np.array([Vertex(pt) for pt in self.poly.minimized_generators() if pt.is_point()]) def poly_from_constraints(self, halfspaces): # Create PPL variables. x = [Variable(i) for i in range(self.dim)] # Init constraint system. constraints = Constraint_System() # Add polytope facets to constraint systems. # Format of ConvexHull.equations: [a1 a2 .. b] => a1*x1 + a2*x2 + .. + b <= 0. for hyp, orient in halfspaces: if orient == -1: constraints.insert(sum(hyp.a[i] * x[i] for i in range(self.dim)) + hyp.b <= 0) elif orient == 1: constraints.insert(sum(hyp.a[i] * x[i] for i in range(self.dim)) + hyp.b >= 0) elif orient == 0: constraints.insert(sum(hyp.a[i] * x[i] for i in range(self.dim)) + hyp.b == 0) # Build PPL polyhedra. return C_Polyhedron(constraints) def poly_from_vertices(self, vertices): # Create PPL variables. x = [Variable(i) for i in range(self.dim)] # Init constraint system. generators = Generator_System() # ppl needs coordinates in form of point(sum(a_i *x_i), denom) with a_i integers for vertex in vertices: generators.insert(vertex.ppl) # Build PPL polyhedra. return C_Polyhedron(generators) def hyperplanes_from_poly(self): logging.debug('<nr generators: {}>, <nr constraints: {}>' .format(len(self.poly.generators()), len(self.poly.constraints()))) hyperplanes = [] # constraints in ppl are saved as of the form ax + b >= 0 for constraint in self.poly.minimized_constraints(): a = np.array(constraint.coefficients()) b = constraint.inhomogeneous_term() hyperplane = Hyperplane(a, b) hyperplanes.append(hyperplane) return hyperplanes def add_constraint(self, halfspace): # Create PPL variables. x = [Variable(i) for i in range(self.dim)] if halfspace[1] == -1: self.poly.add_constraint( sum(halfspace[0].a[i] * x[i] for i in range(self.dim)) + halfspace[0].b <= 0) elif halfspace[1] == 1: self.poly.add_constraint( sum(halfspace[0].a[i] * x[i] for i in range(self.dim)) + halfspace[0].b >= 0) elif halfspace[1] == 0: self.poly.add_constraint( sum(halfspace[0].a[i] * x[i] for i in range(self.dim)) + halfspace[0].b == 0) self.hyperplanes = self.hyperplanes_from_poly() self.halfspaces.append(halfspace) self.vertices = self.vertices_from_poly() def point_in_poly(self, point): return self.poly.relation_with(point.ppl).implies(Poly_Gen_Relation.subsumes())
def find_isomorphism(self, polytope): r""" Return a lattice isomorphism with ``polytope``. INPUT: - ``polytope`` -- a polytope, potentially higher-dimensional. OUTPUT: A :class:`~sage.geometry.polyhedron.lattice_euclidean_group_element.LatticeEuclideanGroupElement`. It is not necessarily invertible if the affine dimension of ``self`` or ``polytope`` is not two. A :class:`~sage.geometry.polyhedron.lattice_euclidean_group_element.LatticePolytopesNotIsomorphicError` is raised if no such isomorphism exists. EXAMPLES:: sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL sage: L1 = LatticePolytope_PPL((1,0),(0,1),(0,0)) sage: L2 = LatticePolytope_PPL((1,0,3),(0,1,0),(0,0,1)) sage: iso = L1.find_isomorphism(L2) sage: iso(L1) == L2 True sage: L1 = LatticePolytope_PPL((0, 1), (3, 0), (0, 3), (1, 0)) sage: L2 = LatticePolytope_PPL((0,0,2,1),(0,1,2,0),(2,0,0,3),(2,3,0,0)) sage: iso = L1.find_isomorphism(L2) sage: iso(L1) == L2 True The following polygons are isomorphic over `\QQ`, but not as lattice polytopes:: sage: L1 = LatticePolytope_PPL((1,0),(0,1),(-1,-1)) sage: L2 = LatticePolytope_PPL((0, 0), (0, 1), (1, 0)) sage: L1.find_isomorphism(L2) Traceback (most recent call last): ... LatticePolytopesNotIsomorphicError: different number of integral points sage: L2.find_isomorphism(L1) Traceback (most recent call last): ... LatticePolytopesNotIsomorphicError: different number of integral points """ from sage.geometry.polyhedron.lattice_euclidean_group_element import \ LatticePolytopesNotIsomorphicError if polytope.affine_dimension() != self.affine_dimension(): raise LatticePolytopesNotIsomorphicError('different dimension') polytope_vertices = polytope.vertices() if len(polytope_vertices) != self.n_vertices(): raise LatticePolytopesNotIsomorphicError('different number of vertices') self_vertices = self.ordered_vertices() if len(polytope.integral_points()) != len(self.integral_points()): raise LatticePolytopesNotIsomorphicError('different number of integral points') if len(self_vertices) < 3: return self._find_isomorphism_degenerate(polytope) polytope_origin = polytope_vertices[0] origin_P = C_Polyhedron(next(iter(polytope.minimized_generators()))) neighbors = [] for c in polytope.minimized_constraints(): if not c.is_inequality(): continue if origin_P.relation_with(c).implies(Poly_Con_Relation.saturates()): for i, g in enumerate(polytope.minimized_generators()): if i == 0: continue g = C_Polyhedron(g) if g.relation_with(c).implies(Poly_Con_Relation.saturates()): neighbors.append(polytope_vertices[i]) break p_ray_left = neighbors[0] - polytope_origin p_ray_right = neighbors[1] - polytope_origin try: return self._find_cyclic_isomorphism_matching_edge(polytope, polytope_origin, p_ray_left, p_ray_right) except LatticePolytopesNotIsomorphicError: pass try: return self._find_cyclic_isomorphism_matching_edge(polytope, polytope_origin, p_ray_right, p_ray_left) except LatticePolytopesNotIsomorphicError: pass raise LatticePolytopesNotIsomorphicError('different polygons')