Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/geometry/hyperplane_arrangement/hyperplane.py
8817 views
1
r"""
2
Hyperplanes
3
4
.. NOTE::
5
6
If you want to learn about Sage's hyperplane arrangements then you
7
should start with
8
:mod:`sage.geometry.hyperplane_arrangement.arrangement`. This
9
module is used to represent the individual hyperplanes, but you
10
should never construct the classes from this module directly (but
11
only via the
12
:class:`~sage.geometry.hyperplane_arrangement.arrangement.HyperplaneArrangements`.
13
14
A linear expression, for example, `3x+3y-5z-7` stands for the
15
hyperplane with the equation `x+3y-5z=7`. To create it in Sage, you
16
first have to create a
17
:class:`~sage.geometry.hyperplane_arrangement.arrangement.HyperplaneArrangements`
18
object to define the variables `x`, `y`, `z`::
19
20
sage: H.<x,y,z> = HyperplaneArrangements(QQ)
21
sage: h = 3*x + 2*y - 5*z - 7; h
22
Hyperplane 3*x + 2*y - 5*z - 7
23
sage: h.coefficients()
24
[-7, 3, 2, -5]
25
sage: h.normal()
26
(3, 2, -5)
27
sage: h.constant_term()
28
-7
29
sage: h.change_ring(GF(3))
30
Hyperplane 0*x + 2*y + z + 2
31
sage: h.point()
32
(21/38, 7/19, -35/38)
33
sage: h.linear_part()
34
Vector space of degree 3 and dimension 2 over Rational Field
35
Basis matrix:
36
[ 1 0 3/5]
37
[ 0 1 2/5]
38
39
Another syntax to create hyperplanes is to specify coefficients and a
40
constant term::
41
42
sage: V = H.ambient_space(); V
43
3-dimensional linear space over Rational Field with coordinates x, y, z
44
sage: h in V
45
True
46
sage: V([3, 2, -5], -7)
47
Hyperplane 3*x + 2*y - 5*z - 7
48
49
Or constant term and coefficients together in one list/tuple/iterable::
50
51
sage: V([-7, 3, 2, -5])
52
Hyperplane 3*x + 2*y - 5*z - 7
53
sage: v = vector([-7, 3, 2, -5]); v
54
(-7, 3, 2, -5)
55
sage: V(v)
56
Hyperplane 3*x + 2*y - 5*z - 7
57
58
Note that the constant term comes first, which matches the notation
59
for Sage's :func:`~sage.geometry.polyhedron.constructor.Polyhedron` ::
60
61
sage: Polyhedron(ieqs=[(4,1,2,3)]).Hrepresentation()
62
(An inequality (1, 2, 3) x + 4 >= 0,)
63
64
The difference between hyperplanes as implemented in this module and
65
hyperplane arrangements is that:
66
67
* hyperplane arrangements contain multiple hyperplanes (of course),
68
69
* linear expressions are a module over the base ring, and these module
70
structure is inherited by the hyperplanes.
71
72
The latter means that you can add and multiply by a scalar::
73
74
sage: h = 3*x + 2*y - 5*z - 7; h
75
Hyperplane 3*x + 2*y - 5*z - 7
76
sage: -h
77
Hyperplane -3*x - 2*y + 5*z + 7
78
sage: h + x
79
Hyperplane 4*x + 2*y - 5*z - 7
80
sage: h + 7
81
Hyperplane 3*x + 2*y - 5*z + 0
82
sage: 3*h
83
Hyperplane 9*x + 6*y - 15*z - 21
84
sage: h * RDF(3)
85
Hyperplane 9.0*x + 6.0*y - 15.0*z - 21.0
86
87
Which you can't do with hyperplane arrangements::
88
89
sage: arrangement = H(h, x, y, x+y-1); arrangement
90
Arrangement <y | x | x + y - 1 | 3*x + 2*y - 5*z - 7>
91
sage: arrangement + x
92
Traceback (most recent call last):
93
TypeError: unsupported operand type(s) for +:
94
'HyperplaneArrangements_with_category.element_class' and
95
'HyperplaneArrangements_with_category.element_class'
96
"""
97
98
#*****************************************************************************
99
# Copyright (C) 2013 David Perkinson <[email protected]>
100
# Volker Braun <[email protected]>
101
#
102
# Distributed under the terms of the GNU General Public License (GPL)
103
# as published by the Free Software Foundation; either version 2 of
104
# the License, or (at your option) any later version.
105
# http://www.gnu.org/licenses/
106
#*****************************************************************************
107
108
109
from sage.misc.cachefunc import cached_method
110
from sage.geometry.linear_expression import LinearExpression, LinearExpressionModule
111
112
113
114
class Hyperplane(LinearExpression):
115
"""
116
A hyperplane.
117
118
You shoud always use :class:`AmbientVectorSpace` to construct
119
instances of this class.
120
121
INPUT:
122
123
- ``parent`` -- the parent :class:`AmbientVectorSpace`
124
125
- ``coefficients`` -- a vector of coefficients of the linear variables
126
127
- ``constant`` -- the constant term for the linear expression
128
129
EXAMPLES::
130
131
sage: H.<x,y> = HyperplaneArrangements(QQ)
132
sage: x+y-1
133
Hyperplane x + y - 1
134
135
sage: ambient = H.ambient_space()
136
sage: ambient._element_constructor_(x+y-1)
137
Hyperplane x + y - 1
138
139
For technical reasons, we must allow the degenerate cases of
140
an empty empty and full space::
141
142
sage: 0*x
143
Hyperplane 0*x + 0*y + 0
144
sage: 0*x + 1
145
Hyperplane 0*x + 0*y + 1
146
sage: x + 0 == x + ambient(0) # because coercion requires them
147
True
148
"""
149
def __init__(self, parent, coefficients, constant):
150
"""
151
Initialize ``self``.
152
153
TESTS::
154
155
sage: H.<x,y> = HyperplaneArrangements(QQ)
156
sage: x.change_ring(RR)
157
Hyperplane 1.00000000000000*x + 0.000000000000000*y + 0.000000000000000
158
sage: TestSuite(x+y-1).run()
159
"""
160
super(Hyperplane, self).__init__(parent, coefficients, constant)
161
162
def _repr_(self):
163
"""
164
Return a string representation.
165
166
OUTPUT:
167
168
A string.
169
170
EXAMPLES::
171
172
sage: H.<x> = HyperplaneArrangements(QQ)
173
sage: x._repr_()
174
'Hyperplane x + 0'
175
"""
176
return 'Hyperplane {0}'.format(self._repr_linear())
177
178
def _latex_(self):
179
r"""
180
Return a LaTeX representation.
181
182
OUTPUT:
183
184
A string.
185
186
EXAMPLES::
187
188
sage: H.<x> = HyperplaneArrangements(QQ)
189
sage: V = H.ambient_space()
190
sage: V([2, -3])._latex_()
191
'$-3x = -2$'
192
193
sage: H.<x, y, z> = HyperplaneArrangements(QQ)
194
sage: V = H.ambient_space()
195
sage: V([-5, 1, 3, 0])._latex_()
196
'$x + 3y = 5$'
197
sage: V([4, 1, 0, -1])._latex_()
198
'$x - z = -4$'
199
"""
200
linear = self._repr_linear(include_zero=False, include_constant=False, multiplication='')
201
s = '{0} = {1}'.format(linear, -self.b())
202
return '${0}$'.format(s)
203
204
def normal(self):
205
"""
206
Return the normal vector.
207
208
OUTPUT:
209
210
A vector over the base ring.
211
212
EXAMPLES::
213
214
sage: H.<x, y, z> = HyperplaneArrangements(QQ)
215
sage: x.normal()
216
(1, 0, 0)
217
sage: x.A(), x.b()
218
((1, 0, 0), 0)
219
sage: (x + 2*y + 3*z + 4).normal()
220
(1, 2, 3)
221
"""
222
return self.A()
223
224
def _normal_pivot(self):
225
"""
226
Return the index of the largest entry of the normal vector.
227
228
OUTPUT:
229
230
An integer. The index of the largest entry.
231
232
EXAMPLES::
233
234
sage: H.<x,y,z> = HyperplaneArrangements(QQ)
235
sage: V = H.ambient_space()
236
sage: (x + 3/2*y - 2*z)._normal_pivot()
237
2
238
239
sage: H.<x,y,z> = HyperplaneArrangements(GF(5))
240
sage: V = H.ambient_space()
241
sage: (x + 3*y - 4*z)._normal_pivot()
242
1
243
"""
244
try:
245
values = [abs(x) for x in self.A()]
246
except ArithmeticError:
247
from sage.rings.all import RDF
248
values = [abs(RDF(x)) for x in self.A()]
249
max_pos = 0
250
max_value = values[max_pos]
251
for i in range(1, len(values)):
252
if values[i] > max_value:
253
max_pos = i
254
max_value = values[i]
255
return max_pos
256
257
def __contains__(self, q):
258
r"""
259
Test whether the point ``q`` is in the hyperplane.
260
261
INPUT:
262
263
- ``q`` -- point (as a vector, list, or tuple)
264
265
OUTPUT:
266
267
A boolean.
268
269
EXAMPLES::
270
271
sage: H.<x,y,z> = HyperplaneArrangements(QQ)
272
sage: h = x + y + z - 1
273
sage: (1/3, 1/3, 1/3) in h
274
True
275
sage: (0,0,0) in h
276
False
277
"""
278
V = self.parent().ambient_vector_space()
279
q = V(q)
280
return self.A() * q + self._const == 0
281
282
@cached_method
283
def polyhedron(self):
284
"""
285
Return the hyperplane as a polyhedron.
286
287
OUTPUT:
288
289
A :func:`~sage.geometry.polyhedron.constructor.Polyhedron` instance.
290
291
EXAMPLES::
292
293
sage: H.<x,y,z> = HyperplaneArrangements(QQ)
294
sage: h = x + 2*y + 3*z - 4
295
sage: P = h.polyhedron(); P
296
A 2-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex and 2 lines
297
sage: P.Hrepresentation()
298
(An equation (1, 2, 3) x - 4 == 0,)
299
sage: P.Vrepresentation()
300
(A line in the direction (0, 3, -2),
301
A line in the direction (3, 0, -1),
302
A vertex at (0, 0, 4/3))
303
"""
304
from sage.geometry.polyhedron.constructor import Polyhedron
305
R = self.parent().base_ring()
306
return Polyhedron(eqns=[self.coefficients()], base_ring=R)
307
308
@cached_method
309
def linear_part(self):
310
r"""
311
The linear part of the affine space.
312
313
OUTPUT:
314
315
Vector subspace of the ambient vector space, parallel to the
316
hyperplane.
317
318
EXAMPLES::
319
320
sage: H.<x,y,z> = HyperplaneArrangements(QQ)
321
sage: h = x + 2*y + 3*z - 1
322
sage: h.linear_part()
323
Vector space of degree 3 and dimension 2 over Rational Field
324
Basis matrix:
325
[ 1 0 -1/3]
326
[ 0 1 -2/3]
327
"""
328
AA = self.parent().ambient_module()
329
from sage.matrix.constructor import matrix
330
return matrix(AA.base_ring(), [self.A()]).right_kernel()
331
332
def linear_part_projection(self, point):
333
"""
334
Orthogonal projection onto the linear part.
335
336
INPUT:
337
338
- ``point`` -- vector of the ambient space, or anything that
339
can be converted into one; not necessarily on the
340
hyperplane
341
342
OUTPUT:
343
344
Coordinate vector of the projection of ``point`` with respect
345
to the basis of :meth:`linear_part`. In particular, the length
346
of this vector is is one less than the ambient space
347
dimension.
348
349
EXAMPLES::
350
351
sage: H.<x,y,z> = HyperplaneArrangements(QQ)
352
sage: h = x + 2*y + 3*z - 4
353
sage: h.linear_part()
354
Vector space of degree 3 and dimension 2 over Rational Field
355
Basis matrix:
356
[ 1 0 -1/3]
357
[ 0 1 -2/3]
358
sage: p1 = h.linear_part_projection(0); p1
359
(0, 0)
360
sage: p2 = h.linear_part_projection([3,4,5]); p2
361
(8/7, 2/7)
362
sage: h.linear_part().basis()
363
[
364
(1, 0, -1/3),
365
(0, 1, -2/3)
366
]
367
sage: p3 = h.linear_part_projection([1,1,1]); p3
368
(4/7, 1/7)
369
"""
370
point = self.orthogonal_projection(point) - self.point()
371
return self.linear_part().coordinate_vector(point)
372
373
@cached_method
374
def point(self):
375
"""
376
Return the point closest to the origin.
377
378
OUTPUT:
379
380
A vector of the ambient vector space. The closest point to the
381
origin in the `L^2`-norm.
382
383
In finite characteristic a random point will be returned if
384
the norm of the hyperplane normal vector is zero.
385
386
EXAMPLES::
387
388
389
sage: H.<x,y,z> = HyperplaneArrangements(QQ)
390
sage: h = x + 2*y + 3*z - 4
391
sage: h.point()
392
(2/7, 4/7, 6/7)
393
sage: h.point() in h
394
True
395
396
sage: H.<x,y,z> = HyperplaneArrangements(GF(3))
397
sage: h = 2*x + y + z + 1
398
sage: h.point()
399
(1, 0, 0)
400
sage: h.point().base_ring()
401
Finite Field of size 3
402
403
sage: H.<x,y,z> = HyperplaneArrangements(GF(3))
404
sage: h = x + y + z + 1
405
sage: h.point()
406
(2, 0, 0)
407
"""
408
P = self.parent()
409
AA = P.ambient_module()
410
R = P.base_ring()
411
norm2 = sum(x**2 for x in self.A())
412
if norm2 == 0:
413
from sage.matrix.constructor import matrix, vector
414
solution = matrix(R, self.A()).solve_right(vector(R, [-self.b()]))
415
else:
416
solution = [-x * self.b() / norm2 for x in self.A()]
417
return AA(solution)
418
419
def dimension(self):
420
r"""
421
The dimension of the hyperplane.
422
423
OUTPUT:
424
425
An integer.
426
427
EXAMPLES::
428
429
sage: H.<x,y,z> = HyperplaneArrangements(QQ)
430
sage: h = x + y + z - 1
431
sage: h.dimension()
432
2
433
"""
434
return self.linear_part().dimension()
435
436
def intersection(self, other):
437
r"""
438
The intersection of ``self`` with ``other``.
439
440
INPUT:
441
442
- ``other`` -- a hyperplane, a polyhedron, or something that
443
defines a polyhedron
444
445
OUTPUT:
446
447
A polyhedron.
448
449
EXAMPLES::
450
451
sage: H.<x,y,z> = HyperplaneArrangements(QQ)
452
sage: h = x + y + z - 1
453
sage: h.intersection(x - y)
454
A 1-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex and 1 line
455
sage: h.intersection(polytopes.n_cube(3))
456
A 2-dimensional polyhedron in QQ^3 defined as the convex hull of 3 vertices
457
"""
458
from sage.geometry.polyhedron.base import is_Polyhedron
459
from sage.geometry.polyhedron.constructor import Polyhedron
460
if not is_Polyhedron(other):
461
try:
462
other = other.polyhedron()
463
except AttributeError:
464
other = Polyhedron(other)
465
return self.polyhedron().intersection(other)
466
467
def orthogonal_projection(self, point):
468
"""
469
Return the orthogonal projection of a point.
470
471
INPUT:
472
473
- ``point`` -- vector of the ambient space, or anything that
474
can be converted into one; not necessarily on the
475
hyperplane
476
477
OUTPUT:
478
479
A vector in the ambient vector space that lies on the
480
hyperplane.
481
482
In finite characteristic, a ``ValueError`` is raised if the
483
the norm of the hyperplane normal is zero.
484
485
EXAMPLES::
486
487
sage: H.<x,y,z> = HyperplaneArrangements(QQ)
488
sage: h = x + 2*y + 3*z - 4
489
sage: p1 = h.orthogonal_projection(0); p1
490
(2/7, 4/7, 6/7)
491
sage: p1 in h
492
True
493
sage: p2 = h.orthogonal_projection([3,4,5]); p2
494
(10/7, 6/7, 2/7)
495
sage: p1 in h
496
True
497
sage: p3 = h.orthogonal_projection([1,1,1]); p3
498
(6/7, 5/7, 4/7)
499
sage: p3 in h
500
True
501
"""
502
P = self.parent()
503
norm2 = sum(x**2 for x in self.A())
504
if norm2 == 0:
505
raise ValueError('norm of hyperplane normal is zero')
506
point = P.ambient_vector_space()(point)
507
n = self.normal()
508
return point - n * (self.b() + point*n) / norm2
509
510
def primitive(self, signed=True):
511
"""
512
Return hyperplane defined by primitive equation.
513
514
INPUT:
515
516
- ``signed`` -- boolean (optional, default: ``True``); whether
517
to preserve the overall sign
518
519
OUTPUT:
520
521
Hyperplane whose linear expression has common factors and
522
denominators cleared. That is, the same hyperplane (with the
523
same sign) but defined by a rescaled equation. Note that
524
different linear expressions must define different hyperplanes
525
as comparison is used in caching.
526
527
If ``signed``, the overall rescaling is by a positive constant
528
only.
529
530
EXAMPLES::
531
532
sage: H.<x,y> = HyperplaneArrangements(QQ)
533
sage: h = -1/3*x + 1/2*y - 1; h
534
Hyperplane -1/3*x + 1/2*y - 1
535
sage: h.primitive()
536
Hyperplane -2*x + 3*y - 6
537
sage: h == h.primitive()
538
False
539
sage: (4*x + 8).primitive()
540
Hyperplane x + 0*y + 2
541
542
sage: (4*x - y - 8).primitive(signed=True) # default
543
Hyperplane 4*x - y - 8
544
sage: (4*x - y - 8).primitive(signed=False)
545
Hyperplane -4*x + y + 8
546
"""
547
from sage.rings.all import lcm, gcd
548
coeffs = self.coefficients()
549
try:
550
d = lcm([x.denom() for x in coeffs])
551
n = gcd([x.numer() for x in coeffs])
552
except AttributeError:
553
return self
554
if not signed:
555
for x in coeffs:
556
if x > 0:
557
break
558
if x < 0:
559
d = -d
560
break
561
parent = self.parent()
562
d = parent.base_ring()(d)
563
n = parent.base_ring()(n)
564
if n == 0:
565
n = parent.base_ring().one()
566
return parent(self * d / n)
567
568
@cached_method
569
def _affine_subspace(self):
570
"""
571
Return the hyperplane as affine subspace.
572
573
OUTPUT:
574
575
The hyperplane as a
576
:class:`~sage.geometry.hyperplane_arrangement.affine_subspace.AffineSubspace`.
577
578
EXAMPLES::
579
580
sage: H.<x,y> = HyperplaneArrangements(QQ)
581
sage: h = -1/3*x + 1/2*y - 1; h
582
Hyperplane -1/3*x + 1/2*y - 1
583
sage: h._affine_subspace()
584
Affine space p + W where:
585
p = (-12/13, 18/13)
586
W = Vector space of degree 2 and dimension 1 over Rational Field
587
Basis matrix:
588
[ 1 2/3]
589
"""
590
from sage.geometry.hyperplane_arrangement.affine_subspace import AffineSubspace
591
return AffineSubspace(self.point(), self.linear_part())
592
593
def plot(self, **kwds):
594
"""
595
Plot the hyperplane.
596
597
OUTPUT:
598
599
A graphics object.
600
601
EXAMPLES::
602
603
sage: L.<x, y> = HyperplaneArrangements(QQ)
604
sage: (x+y-2).plot()
605
"""
606
from sage.geometry.hyperplane_arrangement.plot import plot_hyperplane
607
return plot_hyperplane(self, **kwds)
608
609
def __or__(self, other):
610
"""
611
Construct hyperplane arrangement from bitwise or.
612
613
EXAMPLES::
614
615
sage: L.<x, y> = HyperplaneArrangements(QQ)
616
sage: x | y + 1
617
Arrangement <y + 1 | x>
618
sage: x | [(0,1), 1]
619
Arrangement <y + 1 | x>
620
621
TESTS::
622
623
sage: (x | y).parent() is L
624
True
625
"""
626
from sage.geometry.hyperplane_arrangement.arrangement import HyperplaneArrangements
627
parent = self.parent()
628
arrangement = HyperplaneArrangements(parent.base_ring(), names=parent._names)
629
return arrangement(self, other)
630
631
632
633
class AmbientVectorSpace(LinearExpressionModule):
634
"""
635
The ambient space for hyperplanes.
636
637
This class is the parent for the :class:`Hyperplane` instances.
638
639
TESTS::
640
641
sage: from sage.geometry.hyperplane_arrangement.hyperplane import AmbientVectorSpace
642
sage: V = AmbientVectorSpace(QQ, ('x', 'y'))
643
sage: V.change_ring(QQ) is V
644
True
645
"""
646
647
Element = Hyperplane
648
649
def _repr_(self):
650
"""
651
Return a string representation.
652
653
OUTPUT:
654
655
A string.
656
657
EXAMPLES::
658
659
sage: from sage.geometry.hyperplane_arrangement.hyperplane import AmbientVectorSpace
660
sage: AmbientVectorSpace(QQ, ('x', 'y'))
661
2-dimensional linear space over Rational Field with coordinates x, y
662
"""
663
return '{0}-dimensional linear space over {3} with coordinate{1} {2}'.format(
664
self.dimension(),
665
's' if self.ngens() > 1 else '',
666
', '.join(self._names),
667
self.base_ring())
668
669
def dimension(self):
670
"""
671
Return the ambient space dimension.
672
673
OUTPUT:
674
675
An integer.
676
677
EXAMPLES::
678
679
sage: M.<x,y> = HyperplaneArrangements(QQ)
680
sage: x.parent().dimension()
681
2
682
sage: x.parent() is M.ambient_space()
683
True
684
sage: x.dimension()
685
1
686
"""
687
return self.ngens()
688
689
def change_ring(self, base_ring):
690
"""
691
Return a ambient vector space with a changed base ring.
692
693
INPUT:
694
695
- ``base_ring`` -- a ring; the new base ring
696
697
OUTPUT:
698
699
A new :class:`AmbientVectorSpace`.
700
701
EXAMPLES::
702
703
sage: M.<y> = HyperplaneArrangements(QQ)
704
sage: V = M.ambient_space()
705
sage: V.change_ring(RR)
706
1-dimensional linear space over Real Field with 53 bits of precision with coordinate y
707
708
TESTS::
709
710
sage: V.change_ring(QQ) is V
711
True
712
"""
713
return AmbientVectorSpace(base_ring, self._names)
714
715
716