diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py index 9a892a0408a..c59a881654a 100644 --- a/src/sage/geometry/lattice_polytope.py +++ b/src/sage/geometry/lattice_polytope.py @@ -43,14 +43,14 @@ .. NOTE:: - IMPORTANT: PALP requires some parameters to be determined during - compilation time, i.e., the maximum dimension of polytopes, the - maximum number of points, etc. These limitations may lead to errors - during calls to different functions of these module. Currently, a - :exc:`ValueError` exception will be raised if the output of ``poly.x`` - or ``nef.x`` is empty or contains the exclamation mark. The error - message will contain the exact command that caused an error, the - description and vertices of the polytope, and the obtained output. + IMPORTANT: PALP requires some parameters to be determined during + compilation time, i.e., the maximum dimension of polytopes, the + maximum number of points, etc. These limitations may lead to errors + during calls to different functions of these module. Currently, a + :exc:`ValueError` exception will be raised if the output of ``poly.x`` + or ``nef.x`` is empty or contains the exclamation mark. The error + message will contain the exact command that caused an error, the + description and vertices of the polytope, and the obtained output. Data obtained from PALP and some other data is cached and most returned values are immutable. In particular, you cannot change the @@ -360,16 +360,16 @@ def ReflexivePolytope(dim, n): .. NOTE:: - #. Numeration starts with zero: `0 \leq n \leq 15` for `{\rm dim} = 2` - and `0 \leq n \leq 4318` for `{\rm dim} = 3`. + #. Numeration starts with zero: `0 \leq n \leq 15` for `{\rm dim} = 2` + and `0 \leq n \leq 4318` for `{\rm dim} = 3`. - #. During the first call, all reflexive polytopes of requested - dimension are loaded and cached for future use, so the first - call for 3-dimensional polytopes can take several seconds, - but all consecutive calls are fast. + #. During the first call, all reflexive polytopes of requested + dimension are loaded and cached for future use, so the first + call for 3-dimensional polytopes can take several seconds, + but all consecutive calls are fast. - #. Equivalent to ``ReflexivePolytopes(dim)[n]`` but checks bounds - first. + #. Equivalent to ``ReflexivePolytopes(dim)[n]`` but checks bounds + first. EXAMPLES: @@ -420,9 +420,9 @@ def ReflexivePolytopes(dim): .. NOTE:: - During the first call the database is loaded and cached for - future use, so repetitive calls will return the same object in - memory. + During the first call the database is loaded and cached for + future use, so repetitive calls will return the same object in + memory. INPUT: @@ -970,14 +970,14 @@ def _palp(self, command, reduce_dimension=False): r""" Run ``command`` on vertices of this polytope. - Returns the output of ``command`` as a string. + This returns the output of ``command`` as a string. .. NOTE:: - PALP cannot be called for polytopes that do not span the ambient space. - If you specify ``reduce_dimension=True`` argument, PALP will be - called for vertices of this polytope in some basis of the affine space - it spans. + PALP cannot be called for polytopes that do not span the + ambient space. If you specify ``reduce_dimension=True`` + argument, PALP will be called for vertices of this + polytope in some basis of the affine space it spans. TESTS:: @@ -1409,13 +1409,14 @@ def affine_transform(self, a=1, b=0): .. NOTE:: - #. While ``a`` and ``b`` may be rational, the final result must be a - lattice polytope, i.e. all vertices must be integral. + #. While ``a`` and ``b`` may be rational, the final result + must be a lattice polytope, i.e. all vertices must be integral. - #. If the transform (restricted to this polytope) is bijective, facial - structure will be preserved, e.g. the first facet of the image will - be spanned by the images of vertices which span the first facet of - the original polytope. + #. If the transform (restricted to this polytope) is + bijective, facial structure will be preserved, e.g. the + first facet of the image will be spanned by the images + of vertices which span the first facet of the original + polytope. INPUT: @@ -1496,6 +1497,8 @@ def affine_transform(self, a=1, b=0): r._original = self return r + linear_transformation = affine_transform + def ambient(self): r""" Return the ambient structure of ``self``. @@ -2396,10 +2399,10 @@ def incidence_matrix(self): .. NOTE:: - The columns correspond to facets/facet normals - in the order of :meth:`facet_normals`, the rows - correspond to the vertices in the order of - :meth:`vertices`. + The columns correspond to facets/facet normals + in the order of :meth:`facet_normals`, the rows + correspond to the vertices in the order of + :meth:`vertices`. EXAMPLES:: @@ -2450,9 +2453,9 @@ def index(self): .. NOTE:: - The first call to this function for each dimension can take - a few seconds while the dictionary of all polytopes is - constructed, but after that it is cached and fast. + The first call to this function for each dimension can take + a few seconds while the dictionary of all polytopes is + constructed, but after that it is cached and fast. :rtype: integer @@ -4972,15 +4975,15 @@ def _palp(command, polytopes, reduce_dimension=False): Run ``command`` on vertices of given ``polytopes``. - Returns the name of the file containing the output of + This returns the name of the file containing the output of ``command``. You should delete it after using. .. NOTE:: - PALP cannot be called for polytopes that do not span the ambient space. - If you specify ``reduce_dimension=True`` argument, PALP will be - called for vertices of this polytope in some basis of the affine space - it spans. + PALP cannot be called for polytopes that do not span the + ambient space. If you specify ``reduce_dimension=True`` + argument, PALP will be called for vertices of this polytope in + some basis of the affine space it spans. TESTS:: @@ -5436,8 +5439,8 @@ def convex_hull(points): .. NOTE:: - ``points`` might not span the space. Also, it fails for large - numbers of vertices in dimensions 4 or greater + ``points`` might not span the space. Also, it fails for large + numbers of vertices in dimensions 4 or greater INPUT: @@ -5511,7 +5514,7 @@ def minkowski_sum(points1, points2): .. NOTE:: - Polytopes might not be of maximal dimension. + Polytopes might not be of maximal dimension. INPUT: diff --git a/src/sage/geometry/polyhedron/base5.py b/src/sage/geometry/polyhedron/base5.py index 64862bf28e0..47162d05c75 100644 --- a/src/sage/geometry/polyhedron/base5.py +++ b/src/sage/geometry/polyhedron/base5.py @@ -1521,6 +1521,8 @@ def translation(self, displacement): OUTPUT: the translated polyhedron + .. SEEALSO:: :meth:`linear_transformation`, :meth:`dilation` + EXAMPLES:: sage: P = Polyhedron([[0,0], [1,0], [0,1]], base_ring=ZZ) @@ -1606,6 +1608,8 @@ def dilation(self, scalar): The polyhedron dilated by that scalar, possibly coerced to a bigger base ring. + .. SEEALSO:: :meth:`linear_transformation`, :meth:`translation` + EXAMPLES:: sage: p = Polyhedron(vertices=[[t,t^2,t^3] for t in srange(2,6)]) @@ -1747,7 +1751,8 @@ def _test_dilation(self, tester=None, **options): p = self.change_ring(new_ring) tester.assertIsInstance(scalar*p, Polyhedron_base) - def linear_transformation(self, linear_transf, new_base_ring=None): + def linear_transformation(self, linear_transf, + new_base_ring=None): """ Return the linear transformation of ``self``. @@ -1762,6 +1767,8 @@ def linear_transformation(self, linear_transf, new_base_ring=None): The polyhedron transformed by that matrix, possibly coerced to a bigger base ring. + .. SEEALSO:: :meth:`dilation`, :meth:`translation` + EXAMPLES:: sage: b3 = polytopes.Birkhoff_polytope(3) @@ -1807,6 +1814,15 @@ def linear_transformation(self, linear_transf, new_base_ring=None): TESTS: + One can scale by a scalar as follows:: + + sage: P = polytopes.cube() + sage: P2 = P.linear_transformation(2); P2 + A 3-dimensional polyhedron in QQ^3 defined as + the convex hull of 8 vertices + sage: P2.volume() + 64 + Linear transformation respects backend:: sage: P = polytopes.simplex(backend='field') @@ -1862,6 +1878,11 @@ def linear_transformation(self, linear_transf, new_base_ring=None): True """ is_injective = False + + if linear_transf in self.base_ring(): + # allow for scalar input + linear_transf = linear_transf * self.ambient_vector_space().matrix() + if linear_transf.nrows() != 0: if new_base_ring: R = new_base_ring @@ -1870,17 +1891,18 @@ def linear_transformation(self, linear_transf, new_base_ring=None): # Multiplying a matrix with a vector is slow. # So we multiply the entire vertex matrix etc. - # Still we create generators, as possibly the Vrepresentation will be discarded later on. + # Still we create generators, as possibly the Vrepresentation + # will be discarded later on. if self.n_vertices(): - new_vertices = ( v for v in ((linear_transf*self.vertices_matrix(R)).transpose()) ) + new_vertices = iter((linear_transf*self.vertices_matrix(R)).transpose()) else: new_vertices = () if self.n_rays(): - new_rays = ( r for r in matrix(R, self.rays())*linear_transf.transpose() ) + new_rays = iter(matrix(R, self.rays())*linear_transf.transpose()) else: new_rays = () if self.n_lines(): - new_lines = ( l for l in matrix(R, self.lines())*linear_transf.transpose() ) + new_lines = iter(matrix(R, self.lines())*linear_transf.transpose()) else: new_lines = () @@ -1906,14 +1928,14 @@ def linear_transformation(self, linear_transf, new_base_ring=None): # Note that such N must exist, as our map is injective on the polytope. # It is uniquely defined by considering a basis of the homogeneous vertices. N = new_homogeneous_basis.solve_left(homogeneous_basis) - new_inequalities = ( h for h in matrix(R, self.inequalities())*N ) + new_inequalities = iter(matrix(R, self.inequalities())*N) # The equations are the left kernel matrix of the homogeneous vertices # or equivalently a basis thereof. new_equations = (new_homogeneous_basis.transpose()).right_kernel_matrix() else: - new_vertices = [[] for v in self.vertex_generator() ] + new_vertices = [[] for v in self.vertex_generator()] new_rays = [] new_lines = []