def find_isomorphism(self, polytope): """ 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(Generator_System_iterator(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')
def _find_isomorphism_degenerate(self, polytope): """ Helper to pick an isomorphism of degenerate polygons INPUT: - ``polytope`` -- a :class:`LatticePolytope_PPL_class`. The polytope to compare with. EXAMPLES:: sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL, C_Polyhedron sage: L1 = LatticePolytope_PPL(C_Polyhedron(2, 'empty')) sage: L2 = LatticePolytope_PPL(C_Polyhedron(3, 'empty')) sage: iso = L1.find_isomorphism(L2) # indirect doctest sage: iso(L1) == L2 True sage: iso = L1._find_isomorphism_degenerate(L2) sage: iso(L1) == L2 True sage: L1 = LatticePolytope_PPL((-1,4)) sage: L2 = LatticePolytope_PPL((2,1,5)) sage: iso = L1.find_isomorphism(L2) sage: iso(L1) == L2 True sage: L1 = LatticePolytope_PPL((-1,), (3,)) sage: L2 = LatticePolytope_PPL((2,1,5), (2,-3,5)) sage: iso = L1.find_isomorphism(L2) sage: iso(L1) == L2 True sage: L1 = LatticePolytope_PPL((-1,-1), (3,-1)) sage: L2 = LatticePolytope_PPL((2,1,5), (2,-3,5)) sage: iso = L1.find_isomorphism(L2) sage: iso(L1) == L2 True sage: L1 = LatticePolytope_PPL((-1,2), (3,1)) sage: L2 = LatticePolytope_PPL((1,2,3),(1,2,4)) sage: iso = L1.find_isomorphism(L2) sage: iso(L1) == L2 True sage: L1 = LatticePolytope_PPL((-1,2), (3,2)) sage: L2 = LatticePolytope_PPL((1,2,3),(1,2,4)) sage: L1.find_isomorphism(L2) Traceback (most recent call last): ... LatticePolytopesNotIsomorphicError: different number of integral points sage: L1 = LatticePolytope_PPL((-1,2), (3,1)) sage: L2 = LatticePolytope_PPL((1,2,3),(1,2,5)) sage: L1.find_isomorphism(L2) Traceback (most recent call last): ... LatticePolytopesNotIsomorphicError: different number of integral points """ from sage.geometry.polyhedron.lattice_euclidean_group_element import \ LatticePolytopesNotIsomorphicError polytope_vertices = polytope.vertices() self_vertices = self.ordered_vertices() # handle degenerate cases if self.n_vertices() == 0: A = zero_matrix(ZZ, polytope.space_dimension(), self.space_dimension()) b = zero_vector(ZZ, polytope.space_dimension()) return LatticeEuclideanGroupElement(A, b) if self.n_vertices() == 1: A = zero_matrix(ZZ, polytope.space_dimension(), self.space_dimension()) b = polytope_vertices[0] return LatticeEuclideanGroupElement(A, b) if self.n_vertices() == 2: self_origin = self_vertices[0] self_ray = self_vertices[1] - self_origin polytope_origin = polytope_vertices[0] polytope_ray = polytope_vertices[1] - polytope_origin Ds, Us, Vs = self_ray.column().smith_form() Dp, Up, Vp = polytope_ray.column().smith_form() assert Vs.nrows() == Vs.ncols() == Vp.nrows() == Vp.ncols() == 1 assert abs(Vs[0, 0]) == abs(Vp[0, 0]) == 1 A = zero_matrix(ZZ, Dp.nrows(), Ds.nrows()) A[0, 0] = 1 A = Up.inverse() * A * Us * (Vs[0, 0] * Vp[0, 0]) b = polytope_origin - A * self_origin try: A = matrix(ZZ, A) b = vector(ZZ, b) except TypeError: raise LatticePolytopesNotIsomorphicError('different lattice') hom = LatticeEuclideanGroupElement(A, b) if hom(self) == polytope: return hom raise LatticePolytopesNotIsomorphicError('different polygons')
def _find_cyclic_isomorphism_matching_edge(self, polytope, polytope_origin, p_ray_left, p_ray_right): """ Helper to find an isomorphism of polygons INPUT: - ``polytope`` -- the lattice polytope to compare to. - ``polytope_origin`` -- `\ZZ`-vector. a vertex of ``polytope`` - ``p_ray_left`` - vector. the vector from ``polytope_origin`` to one of its neighboring vertices. - ``p_ray_right`` - vector. the vector from ``polytope_origin`` to the other neighboring vertices. OUTPUT: The element of the lattice Euclidean group that maps ``self`` to ``polytope`` with given origin and left/right neighboring vertex. 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: v0, v1, v2 = L2.vertices() sage: L1._find_cyclic_isomorphism_matching_edge(L2, v0, v1-v0, v2-v0) The map A*x+b with A= [ 0 1] [-1 -1] [ 1 3] b = (0, 1, 0) """ from sage.geometry.polyhedron.lattice_euclidean_group_element import \ LatticePolytopesNotIsomorphicError polytope_matrix = block_matrix( 1, 2, [p_ray_left.column(), p_ray_right.column()]) self_vertices = self.ordered_vertices() for i in range(len(self_vertices)): # three consecutive vertices v_left = self_vertices[(i + 0) % len(self_vertices)] v_origin = self_vertices[(i + 1) % len(self_vertices)] v_right = self_vertices[(i + 2) % len(self_vertices)] r_left = v_left - v_origin r_right = v_right - v_origin self_matrix = block_matrix( 1, 2, [r_left.column(), r_right.column()]) A = self_matrix.solve_left(polytope_matrix) b = polytope_origin - A * v_origin try: A = matrix(ZZ, A) b = vector(ZZ, b) except TypeError: continue if A.elementary_divisors()[0:2] != [1, 1]: continue hom = LatticeEuclideanGroupElement(A, b) if hom(self) == polytope: return hom raise LatticePolytopesNotIsomorphicError('different polygons')