Path: blob/master/src/sage/geometry/hyperplane_arrangement/hyperplane.py
8817 views
r"""1Hyperplanes23.. NOTE::45If you want to learn about Sage's hyperplane arrangements then you6should start with7:mod:`sage.geometry.hyperplane_arrangement.arrangement`. This8module is used to represent the individual hyperplanes, but you9should never construct the classes from this module directly (but10only via the11:class:`~sage.geometry.hyperplane_arrangement.arrangement.HyperplaneArrangements`.1213A linear expression, for example, `3x+3y-5z-7` stands for the14hyperplane with the equation `x+3y-5z=7`. To create it in Sage, you15first have to create a16:class:`~sage.geometry.hyperplane_arrangement.arrangement.HyperplaneArrangements`17object to define the variables `x`, `y`, `z`::1819sage: H.<x,y,z> = HyperplaneArrangements(QQ)20sage: h = 3*x + 2*y - 5*z - 7; h21Hyperplane 3*x + 2*y - 5*z - 722sage: h.coefficients()23[-7, 3, 2, -5]24sage: h.normal()25(3, 2, -5)26sage: h.constant_term()27-728sage: h.change_ring(GF(3))29Hyperplane 0*x + 2*y + z + 230sage: h.point()31(21/38, 7/19, -35/38)32sage: h.linear_part()33Vector space of degree 3 and dimension 2 over Rational Field34Basis matrix:35[ 1 0 3/5]36[ 0 1 2/5]3738Another syntax to create hyperplanes is to specify coefficients and a39constant term::4041sage: V = H.ambient_space(); V423-dimensional linear space over Rational Field with coordinates x, y, z43sage: h in V44True45sage: V([3, 2, -5], -7)46Hyperplane 3*x + 2*y - 5*z - 74748Or constant term and coefficients together in one list/tuple/iterable::4950sage: V([-7, 3, 2, -5])51Hyperplane 3*x + 2*y - 5*z - 752sage: v = vector([-7, 3, 2, -5]); v53(-7, 3, 2, -5)54sage: V(v)55Hyperplane 3*x + 2*y - 5*z - 75657Note that the constant term comes first, which matches the notation58for Sage's :func:`~sage.geometry.polyhedron.constructor.Polyhedron` ::5960sage: Polyhedron(ieqs=[(4,1,2,3)]).Hrepresentation()61(An inequality (1, 2, 3) x + 4 >= 0,)6263The difference between hyperplanes as implemented in this module and64hyperplane arrangements is that:6566* hyperplane arrangements contain multiple hyperplanes (of course),6768* linear expressions are a module over the base ring, and these module69structure is inherited by the hyperplanes.7071The latter means that you can add and multiply by a scalar::7273sage: h = 3*x + 2*y - 5*z - 7; h74Hyperplane 3*x + 2*y - 5*z - 775sage: -h76Hyperplane -3*x - 2*y + 5*z + 777sage: h + x78Hyperplane 4*x + 2*y - 5*z - 779sage: h + 780Hyperplane 3*x + 2*y - 5*z + 081sage: 3*h82Hyperplane 9*x + 6*y - 15*z - 2183sage: h * RDF(3)84Hyperplane 9.0*x + 6.0*y - 15.0*z - 21.08586Which you can't do with hyperplane arrangements::8788sage: arrangement = H(h, x, y, x+y-1); arrangement89Arrangement <y | x | x + y - 1 | 3*x + 2*y - 5*z - 7>90sage: arrangement + x91Traceback (most recent call last):92TypeError: unsupported operand type(s) for +:93'HyperplaneArrangements_with_category.element_class' and94'HyperplaneArrangements_with_category.element_class'95"""9697#*****************************************************************************98# Copyright (C) 2013 David Perkinson <[email protected]>99# Volker Braun <[email protected]>100#101# Distributed under the terms of the GNU General Public License (GPL)102# as published by the Free Software Foundation; either version 2 of103# the License, or (at your option) any later version.104# http://www.gnu.org/licenses/105#*****************************************************************************106107108from sage.misc.cachefunc import cached_method109from sage.geometry.linear_expression import LinearExpression, LinearExpressionModule110111112113class Hyperplane(LinearExpression):114"""115A hyperplane.116117You shoud always use :class:`AmbientVectorSpace` to construct118instances of this class.119120INPUT:121122- ``parent`` -- the parent :class:`AmbientVectorSpace`123124- ``coefficients`` -- a vector of coefficients of the linear variables125126- ``constant`` -- the constant term for the linear expression127128EXAMPLES::129130sage: H.<x,y> = HyperplaneArrangements(QQ)131sage: x+y-1132Hyperplane x + y - 1133134sage: ambient = H.ambient_space()135sage: ambient._element_constructor_(x+y-1)136Hyperplane x + y - 1137138For technical reasons, we must allow the degenerate cases of139an empty empty and full space::140141sage: 0*x142Hyperplane 0*x + 0*y + 0143sage: 0*x + 1144Hyperplane 0*x + 0*y + 1145sage: x + 0 == x + ambient(0) # because coercion requires them146True147"""148def __init__(self, parent, coefficients, constant):149"""150Initialize ``self``.151152TESTS::153154sage: H.<x,y> = HyperplaneArrangements(QQ)155sage: x.change_ring(RR)156Hyperplane 1.00000000000000*x + 0.000000000000000*y + 0.000000000000000157sage: TestSuite(x+y-1).run()158"""159super(Hyperplane, self).__init__(parent, coefficients, constant)160161def _repr_(self):162"""163Return a string representation.164165OUTPUT:166167A string.168169EXAMPLES::170171sage: H.<x> = HyperplaneArrangements(QQ)172sage: x._repr_()173'Hyperplane x + 0'174"""175return 'Hyperplane {0}'.format(self._repr_linear())176177def _latex_(self):178r"""179Return a LaTeX representation.180181OUTPUT:182183A string.184185EXAMPLES::186187sage: H.<x> = HyperplaneArrangements(QQ)188sage: V = H.ambient_space()189sage: V([2, -3])._latex_()190'$-3x = -2$'191192sage: H.<x, y, z> = HyperplaneArrangements(QQ)193sage: V = H.ambient_space()194sage: V([-5, 1, 3, 0])._latex_()195'$x + 3y = 5$'196sage: V([4, 1, 0, -1])._latex_()197'$x - z = -4$'198"""199linear = self._repr_linear(include_zero=False, include_constant=False, multiplication='')200s = '{0} = {1}'.format(linear, -self.b())201return '${0}$'.format(s)202203def normal(self):204"""205Return the normal vector.206207OUTPUT:208209A vector over the base ring.210211EXAMPLES::212213sage: H.<x, y, z> = HyperplaneArrangements(QQ)214sage: x.normal()215(1, 0, 0)216sage: x.A(), x.b()217((1, 0, 0), 0)218sage: (x + 2*y + 3*z + 4).normal()219(1, 2, 3)220"""221return self.A()222223def _normal_pivot(self):224"""225Return the index of the largest entry of the normal vector.226227OUTPUT:228229An integer. The index of the largest entry.230231EXAMPLES::232233sage: H.<x,y,z> = HyperplaneArrangements(QQ)234sage: V = H.ambient_space()235sage: (x + 3/2*y - 2*z)._normal_pivot()2362237238sage: H.<x,y,z> = HyperplaneArrangements(GF(5))239sage: V = H.ambient_space()240sage: (x + 3*y - 4*z)._normal_pivot()2411242"""243try:244values = [abs(x) for x in self.A()]245except ArithmeticError:246from sage.rings.all import RDF247values = [abs(RDF(x)) for x in self.A()]248max_pos = 0249max_value = values[max_pos]250for i in range(1, len(values)):251if values[i] > max_value:252max_pos = i253max_value = values[i]254return max_pos255256def __contains__(self, q):257r"""258Test whether the point ``q`` is in the hyperplane.259260INPUT:261262- ``q`` -- point (as a vector, list, or tuple)263264OUTPUT:265266A boolean.267268EXAMPLES::269270sage: H.<x,y,z> = HyperplaneArrangements(QQ)271sage: h = x + y + z - 1272sage: (1/3, 1/3, 1/3) in h273True274sage: (0,0,0) in h275False276"""277V = self.parent().ambient_vector_space()278q = V(q)279return self.A() * q + self._const == 0280281@cached_method282def polyhedron(self):283"""284Return the hyperplane as a polyhedron.285286OUTPUT:287288A :func:`~sage.geometry.polyhedron.constructor.Polyhedron` instance.289290EXAMPLES::291292sage: H.<x,y,z> = HyperplaneArrangements(QQ)293sage: h = x + 2*y + 3*z - 4294sage: P = h.polyhedron(); P295A 2-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex and 2 lines296sage: P.Hrepresentation()297(An equation (1, 2, 3) x - 4 == 0,)298sage: P.Vrepresentation()299(A line in the direction (0, 3, -2),300A line in the direction (3, 0, -1),301A vertex at (0, 0, 4/3))302"""303from sage.geometry.polyhedron.constructor import Polyhedron304R = self.parent().base_ring()305return Polyhedron(eqns=[self.coefficients()], base_ring=R)306307@cached_method308def linear_part(self):309r"""310The linear part of the affine space.311312OUTPUT:313314Vector subspace of the ambient vector space, parallel to the315hyperplane.316317EXAMPLES::318319sage: H.<x,y,z> = HyperplaneArrangements(QQ)320sage: h = x + 2*y + 3*z - 1321sage: h.linear_part()322Vector space of degree 3 and dimension 2 over Rational Field323Basis matrix:324[ 1 0 -1/3]325[ 0 1 -2/3]326"""327AA = self.parent().ambient_module()328from sage.matrix.constructor import matrix329return matrix(AA.base_ring(), [self.A()]).right_kernel()330331def linear_part_projection(self, point):332"""333Orthogonal projection onto the linear part.334335INPUT:336337- ``point`` -- vector of the ambient space, or anything that338can be converted into one; not necessarily on the339hyperplane340341OUTPUT:342343Coordinate vector of the projection of ``point`` with respect344to the basis of :meth:`linear_part`. In particular, the length345of this vector is is one less than the ambient space346dimension.347348EXAMPLES::349350sage: H.<x,y,z> = HyperplaneArrangements(QQ)351sage: h = x + 2*y + 3*z - 4352sage: h.linear_part()353Vector space of degree 3 and dimension 2 over Rational Field354Basis matrix:355[ 1 0 -1/3]356[ 0 1 -2/3]357sage: p1 = h.linear_part_projection(0); p1358(0, 0)359sage: p2 = h.linear_part_projection([3,4,5]); p2360(8/7, 2/7)361sage: h.linear_part().basis()362[363(1, 0, -1/3),364(0, 1, -2/3)365]366sage: p3 = h.linear_part_projection([1,1,1]); p3367(4/7, 1/7)368"""369point = self.orthogonal_projection(point) - self.point()370return self.linear_part().coordinate_vector(point)371372@cached_method373def point(self):374"""375Return the point closest to the origin.376377OUTPUT:378379A vector of the ambient vector space. The closest point to the380origin in the `L^2`-norm.381382In finite characteristic a random point will be returned if383the norm of the hyperplane normal vector is zero.384385EXAMPLES::386387388sage: H.<x,y,z> = HyperplaneArrangements(QQ)389sage: h = x + 2*y + 3*z - 4390sage: h.point()391(2/7, 4/7, 6/7)392sage: h.point() in h393True394395sage: H.<x,y,z> = HyperplaneArrangements(GF(3))396sage: h = 2*x + y + z + 1397sage: h.point()398(1, 0, 0)399sage: h.point().base_ring()400Finite Field of size 3401402sage: H.<x,y,z> = HyperplaneArrangements(GF(3))403sage: h = x + y + z + 1404sage: h.point()405(2, 0, 0)406"""407P = self.parent()408AA = P.ambient_module()409R = P.base_ring()410norm2 = sum(x**2 for x in self.A())411if norm2 == 0:412from sage.matrix.constructor import matrix, vector413solution = matrix(R, self.A()).solve_right(vector(R, [-self.b()]))414else:415solution = [-x * self.b() / norm2 for x in self.A()]416return AA(solution)417418def dimension(self):419r"""420The dimension of the hyperplane.421422OUTPUT:423424An integer.425426EXAMPLES::427428sage: H.<x,y,z> = HyperplaneArrangements(QQ)429sage: h = x + y + z - 1430sage: h.dimension()4312432"""433return self.linear_part().dimension()434435def intersection(self, other):436r"""437The intersection of ``self`` with ``other``.438439INPUT:440441- ``other`` -- a hyperplane, a polyhedron, or something that442defines a polyhedron443444OUTPUT:445446A polyhedron.447448EXAMPLES::449450sage: H.<x,y,z> = HyperplaneArrangements(QQ)451sage: h = x + y + z - 1452sage: h.intersection(x - y)453A 1-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex and 1 line454sage: h.intersection(polytopes.n_cube(3))455A 2-dimensional polyhedron in QQ^3 defined as the convex hull of 3 vertices456"""457from sage.geometry.polyhedron.base import is_Polyhedron458from sage.geometry.polyhedron.constructor import Polyhedron459if not is_Polyhedron(other):460try:461other = other.polyhedron()462except AttributeError:463other = Polyhedron(other)464return self.polyhedron().intersection(other)465466def orthogonal_projection(self, point):467"""468Return the orthogonal projection of a point.469470INPUT:471472- ``point`` -- vector of the ambient space, or anything that473can be converted into one; not necessarily on the474hyperplane475476OUTPUT:477478A vector in the ambient vector space that lies on the479hyperplane.480481In finite characteristic, a ``ValueError`` is raised if the482the norm of the hyperplane normal is zero.483484EXAMPLES::485486sage: H.<x,y,z> = HyperplaneArrangements(QQ)487sage: h = x + 2*y + 3*z - 4488sage: p1 = h.orthogonal_projection(0); p1489(2/7, 4/7, 6/7)490sage: p1 in h491True492sage: p2 = h.orthogonal_projection([3,4,5]); p2493(10/7, 6/7, 2/7)494sage: p1 in h495True496sage: p3 = h.orthogonal_projection([1,1,1]); p3497(6/7, 5/7, 4/7)498sage: p3 in h499True500"""501P = self.parent()502norm2 = sum(x**2 for x in self.A())503if norm2 == 0:504raise ValueError('norm of hyperplane normal is zero')505point = P.ambient_vector_space()(point)506n = self.normal()507return point - n * (self.b() + point*n) / norm2508509def primitive(self, signed=True):510"""511Return hyperplane defined by primitive equation.512513INPUT:514515- ``signed`` -- boolean (optional, default: ``True``); whether516to preserve the overall sign517518OUTPUT:519520Hyperplane whose linear expression has common factors and521denominators cleared. That is, the same hyperplane (with the522same sign) but defined by a rescaled equation. Note that523different linear expressions must define different hyperplanes524as comparison is used in caching.525526If ``signed``, the overall rescaling is by a positive constant527only.528529EXAMPLES::530531sage: H.<x,y> = HyperplaneArrangements(QQ)532sage: h = -1/3*x + 1/2*y - 1; h533Hyperplane -1/3*x + 1/2*y - 1534sage: h.primitive()535Hyperplane -2*x + 3*y - 6536sage: h == h.primitive()537False538sage: (4*x + 8).primitive()539Hyperplane x + 0*y + 2540541sage: (4*x - y - 8).primitive(signed=True) # default542Hyperplane 4*x - y - 8543sage: (4*x - y - 8).primitive(signed=False)544Hyperplane -4*x + y + 8545"""546from sage.rings.all import lcm, gcd547coeffs = self.coefficients()548try:549d = lcm([x.denom() for x in coeffs])550n = gcd([x.numer() for x in coeffs])551except AttributeError:552return self553if not signed:554for x in coeffs:555if x > 0:556break557if x < 0:558d = -d559break560parent = self.parent()561d = parent.base_ring()(d)562n = parent.base_ring()(n)563if n == 0:564n = parent.base_ring().one()565return parent(self * d / n)566567@cached_method568def _affine_subspace(self):569"""570Return the hyperplane as affine subspace.571572OUTPUT:573574The hyperplane as a575:class:`~sage.geometry.hyperplane_arrangement.affine_subspace.AffineSubspace`.576577EXAMPLES::578579sage: H.<x,y> = HyperplaneArrangements(QQ)580sage: h = -1/3*x + 1/2*y - 1; h581Hyperplane -1/3*x + 1/2*y - 1582sage: h._affine_subspace()583Affine space p + W where:584p = (-12/13, 18/13)585W = Vector space of degree 2 and dimension 1 over Rational Field586Basis matrix:587[ 1 2/3]588"""589from sage.geometry.hyperplane_arrangement.affine_subspace import AffineSubspace590return AffineSubspace(self.point(), self.linear_part())591592def plot(self, **kwds):593"""594Plot the hyperplane.595596OUTPUT:597598A graphics object.599600EXAMPLES::601602sage: L.<x, y> = HyperplaneArrangements(QQ)603sage: (x+y-2).plot()604"""605from sage.geometry.hyperplane_arrangement.plot import plot_hyperplane606return plot_hyperplane(self, **kwds)607608def __or__(self, other):609"""610Construct hyperplane arrangement from bitwise or.611612EXAMPLES::613614sage: L.<x, y> = HyperplaneArrangements(QQ)615sage: x | y + 1616Arrangement <y + 1 | x>617sage: x | [(0,1), 1]618Arrangement <y + 1 | x>619620TESTS::621622sage: (x | y).parent() is L623True624"""625from sage.geometry.hyperplane_arrangement.arrangement import HyperplaneArrangements626parent = self.parent()627arrangement = HyperplaneArrangements(parent.base_ring(), names=parent._names)628return arrangement(self, other)629630631632class AmbientVectorSpace(LinearExpressionModule):633"""634The ambient space for hyperplanes.635636This class is the parent for the :class:`Hyperplane` instances.637638TESTS::639640sage: from sage.geometry.hyperplane_arrangement.hyperplane import AmbientVectorSpace641sage: V = AmbientVectorSpace(QQ, ('x', 'y'))642sage: V.change_ring(QQ) is V643True644"""645646Element = Hyperplane647648def _repr_(self):649"""650Return a string representation.651652OUTPUT:653654A string.655656EXAMPLES::657658sage: from sage.geometry.hyperplane_arrangement.hyperplane import AmbientVectorSpace659sage: AmbientVectorSpace(QQ, ('x', 'y'))6602-dimensional linear space over Rational Field with coordinates x, y661"""662return '{0}-dimensional linear space over {3} with coordinate{1} {2}'.format(663self.dimension(),664's' if self.ngens() > 1 else '',665', '.join(self._names),666self.base_ring())667668def dimension(self):669"""670Return the ambient space dimension.671672OUTPUT:673674An integer.675676EXAMPLES::677678sage: M.<x,y> = HyperplaneArrangements(QQ)679sage: x.parent().dimension()6802681sage: x.parent() is M.ambient_space()682True683sage: x.dimension()6841685"""686return self.ngens()687688def change_ring(self, base_ring):689"""690Return a ambient vector space with a changed base ring.691692INPUT:693694- ``base_ring`` -- a ring; the new base ring695696OUTPUT:697698A new :class:`AmbientVectorSpace`.699700EXAMPLES::701702sage: M.<y> = HyperplaneArrangements(QQ)703sage: V = M.ambient_space()704sage: V.change_ring(RR)7051-dimensional linear space over Real Field with 53 bits of precision with coordinate y706707TESTS::708709sage: V.change_ring(QQ) is V710True711"""712return AmbientVectorSpace(base_ring, self._names)713714715716