Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/geometry/point_collection.pyx
8815 views
1
r"""
2
Point collections
3
4
This module was designed as a part of framework for toric varieties
5
(:mod:`~sage.schemes.toric.variety`,
6
:mod:`~sage.schemes.toric.fano_variety`).
7
8
AUTHORS:
9
10
- Andrey Novoseltsev (2011-04-25): initial version, based on cone module.
11
12
- Andrey Novoseltsev (2012-03-06): additions and doctest changes while
13
switching cones to use point collections.
14
15
EXAMPLES:
16
17
The idea behind :class:`point collections <PointCollection>` is to have a
18
container for points of the same space that
19
20
* behaves like a tuple *without significant performance penalty*::
21
22
sage: c = Cone([(0,0,1), (1,0,1), (0,1,1), (1,1,1)]).rays()
23
sage: c[1]
24
N(1, 0, 1)
25
sage: for point in c: point
26
N(0, 0, 1)
27
N(1, 0, 1)
28
N(0, 1, 1)
29
N(1, 1, 1)
30
31
* prints in a convenient way and with clear indication of the ambient space::
32
33
sage: c
34
N(0, 0, 1),
35
N(1, 0, 1),
36
N(0, 1, 1),
37
N(1, 1, 1)
38
in 3-d lattice N
39
40
* allows (cached) access to alternative representations::
41
42
sage: c.set()
43
frozenset([N(0, 1, 1), N(1, 1, 1), N(0, 0, 1), N(1, 0, 1)])
44
45
* allows introduction of additional methods::
46
47
sage: c.basis()
48
N(0, 0, 1),
49
N(1, 0, 1),
50
N(0, 1, 1)
51
in 3-d lattice N
52
53
Examples of natural point collections include ray and line generators of cones,
54
vertices and points of polytopes, normals to facets, their subcollections, etc.
55
56
Using this class for all of the above cases allows for unified interface *and*
57
cache sharing. Suppose that `\Delta` is a reflexive polytope. Then the same
58
point collection can be linked as
59
60
#. vertices of `\Delta`;
61
#. facet normals of its polar `\Delta^\circ`;
62
#. ray generators of the face fan of `\Delta`;
63
#. ray generators of the normal fan of `\Delta`.
64
65
If all these objects are in use and, say, a matrix representation was computed
66
for one of them, it becomes available to all others as well, eliminating the
67
need to spend time and memory four times.
68
"""
69
70
#*****************************************************************************
71
# Copyright (C) 2012 Andrey Novoseltsev <[email protected]>
72
#
73
# Distributed under the terms of the GNU General Public License (GPL)
74
# as published by the Free Software Foundation; either version 2 of
75
# the License, or (at your option) any later version.
76
# http://www.gnu.org/licenses/
77
#*****************************************************************************
78
79
from sage.structure.sage_object cimport SageObject
80
81
from sage.matrix.all import matrix
82
from sage.misc.all import latex
83
84
85
def is_PointCollection(x):
86
r"""
87
Check if ``x`` is a :class:`point collection <PointCollection>`.
88
89
INPUT:
90
91
- ``x`` -- anything.
92
93
OUTPUT:
94
95
- ``True`` if ``x`` is a point collection and ``False`` otherwise.
96
97
EXAMPLES::
98
99
sage: from sage.geometry.point_collection import is_PointCollection
100
sage: is_PointCollection(1)
101
False
102
sage: c = Cone([(0,0,1), (1,0,1), (0,1,1), (1,1,1)])
103
sage: is_PointCollection(c.rays())
104
True
105
"""
106
return isinstance(x, PointCollection)
107
108
109
_output_format = "default"
110
111
112
cdef class PointCollection(SageObject):
113
r"""
114
Create a point collection.
115
116
.. WARNING::
117
118
No correctness check or normalization is performed on the input data.
119
This class is designed for internal operations and you probably should
120
not use it directly.
121
122
Point collections are immutable, but cache most of the returned values.
123
124
INPUT:
125
126
- ``points`` -- an iterable structure of immutable elements of ``module``,
127
if ``points`` are already accessible to you as a :class:`tuple`, it is
128
preferable to use it for speed and memory consumption reasons;
129
130
- ``module`` -- an ambient module for ``points``. If ``None``, it will be
131
determined as :func:`parent` of the first point. Of course, this cannot
132
be done if there are no points, so in this case you must give an
133
appropriate ``module`` directly. Note that ``None`` is *not* the default
134
value - you always *must* give this argument explicitly, even if it is
135
``None``.
136
137
OUTPUT:
138
139
- a point collection.
140
"""
141
142
cdef:
143
tuple _points
144
object _module
145
# cache attributes
146
PointCollection _basis
147
object _matrix
148
frozenset _set
149
150
def __init__(self, points, module=None):
151
r"""
152
See :class:`PointCollection` for documentation.
153
154
TESTS::
155
156
sage: from sage.geometry.point_collection import PointCollection
157
sage: v = vector([1,0])
158
sage: v.set_immutable()
159
sage: c = PointCollection([v], ZZ^2)
160
sage: c.module()
161
Ambient free module of rank 2
162
over the principal ideal domain Integer Ring
163
sage: c = PointCollection([v], None)
164
sage: c.module() # Determined automatically
165
Ambient free module of rank 2
166
over the principal ideal domain Integer Ring
167
sage: TestSuite(c).run()
168
"""
169
super(PointCollection, self).__init__()
170
self._points = tuple(points)
171
self._module = self._points[0].parent() if module is None else module
172
173
def __add__(left, right):
174
r"""
175
Return the joint point collection.
176
177
INPUT:
178
179
- ``left`` -- a :class:`PointCollection`;
180
181
- ``right`` -- a :class:`PointCollection`.
182
183
OUTPUT:
184
185
- a :class:`PointCollection`.
186
187
TESTS::
188
189
sage: c = Cone([(0,0,1), (1,0,1), (0,1,1), (1,1,1)]).rays()
190
sage: c + c
191
N(0, 0, 1),
192
N(1, 0, 1),
193
N(0, 1, 1),
194
N(1, 1, 1),
195
N(0, 0, 1),
196
N(1, 0, 1),
197
N(0, 1, 1),
198
N(1, 1, 1)
199
in 3-d lattice N
200
"""
201
if not (isinstance(left, PointCollection) and
202
isinstance(right, PointCollection)):
203
raise NotImplementedError
204
cdef PointCollection left_pc = left
205
cdef PointCollection right_pc = right
206
if not left_pc._module is right_pc._module:
207
raise NotImplementedError
208
return PointCollection(left_pc._points + right_pc._points,
209
left_pc._module)
210
211
def __call__(self, *args):
212
r"""
213
Return a subcollection of ``self``.
214
215
INPUT:
216
217
- a list of integers (as a single or many arguments).
218
219
OUTPUT:
220
221
- a :class:`point collection <PointCollection>`.
222
223
TESTS::
224
225
sage: c = Cone([(0,0,1), (1,0,1), (0,1,1), (1,1,1)]).rays()
226
sage: c()
227
Empty collection
228
in 3-d lattice N
229
sage: c(2,1)
230
N(0, 1, 1),
231
N(1, 0, 1)
232
in 3-d lattice N
233
sage: c(range(4)) == c
234
True
235
"""
236
if len(args) == 1:
237
try:
238
args = tuple(args[0])
239
except TypeError:
240
pass
241
# Avoid creating a copy of self
242
if len(args) == len(self) and args == tuple(range(len(self))):
243
return self
244
else:
245
return PointCollection([self[i] for i in args], self._module)
246
247
def __cmp__(self, right):
248
r"""
249
Compare ``self`` and ``right``.
250
251
INPUT:
252
253
- ``right`` -- anything.
254
255
OUTPUT:
256
257
- 0 if ``right`` is of the same type as ``self`` (i.e. it is another
258
:class:`point collection <PointCollection>`), they have the same
259
:meth:`module`, and their points are the same and listed in the same
260
order. 1 or -1 otherwise.
261
262
TESTS::
263
264
sage: c = Cone([(0,0,1), (1,0,1), (0,1,1), (1,1,1)]).rays()
265
sage: cmp(c, c)
266
0
267
sage: cmp(c, 1) * cmp(1, c)
268
-1
269
"""
270
c = cmp(type(self), type(right))
271
if c != 0:
272
return c
273
cdef PointCollection pc = right
274
c = cmp(self._module, pc._module)
275
if c != 0:
276
return c
277
return cmp(self._points, pc._points)
278
279
def __getitem__(self, n):
280
r"""
281
Return the ``n``-th point of ``self``.
282
283
INPUT:
284
285
- ``n`` -- an integer.
286
287
OUTPUT:
288
289
- a point, an element of the ambient :meth:`module` of ``self``.
290
291
EXAMPLES::
292
293
sage: c = Cone([(0,0,1), (1,0,1), (0,1,1), (1,1,1)]).rays()
294
sage: c[0]
295
N(0, 0, 1)
296
"""
297
return self._points[n]
298
299
def __hash__(self):
300
r"""
301
Return the hash of ``self``.
302
303
OUTPUT:
304
305
- an integer.
306
307
TESTS::
308
309
sage: c = Cone([(0,0,1), (1,0,1), (0,1,1), (1,1,1)]).rays()
310
sage: hash(c) == hash(c)
311
True
312
"""
313
return hash(self._points)
314
315
def __iter__(self):
316
r"""
317
Return an iterator over points of ``self``.
318
319
OUTPUT:
320
321
- an iterator.
322
323
TESTS::
324
325
sage: c = Cone([(0,0,1), (1,0,1), (0,1,1), (1,1,1)]).rays()
326
sage: for point in c: print point
327
N(0, 0, 1)
328
N(1, 0, 1)
329
N(0, 1, 1)
330
N(1, 1, 1)
331
"""
332
return iter(self._points)
333
334
def __len__(self):
335
r"""
336
Return the number of points in ``self``.
337
338
OUTPUT:
339
340
- an integer.
341
342
EXAMPLES::
343
344
sage: c = Cone([(0,0,1), (1,0,1), (0,1,1), (1,1,1)]).rays()
345
sage: len(c)
346
4
347
"""
348
return len(self._points)
349
350
def __list__(self):
351
r"""
352
Return a list of points of ``self``.
353
354
OUTPUT:
355
356
- a list.
357
358
TESTS::
359
360
sage: c = Cone([(0,0,1), (1,0,1), (0,1,1), (1,1,1)]).rays()
361
sage: list(c)
362
[N(0, 0, 1), N(1, 0, 1), N(0, 1, 1), N(1, 1, 1)]
363
"""
364
return list(self._points)
365
366
def __reduce__(self):
367
r"""
368
Prepare ``self`` for pickling.
369
370
OUTPUT:
371
372
- a tuple, currently the class name and a tuple consisting of points
373
and the ambient module.
374
375
TESTS::
376
377
sage: c = Cone([(0,0,1), (1,0,1), (0,1,1), (1,1,1)]).rays()
378
sage: loads(dumps(c))
379
N(0, 0, 1),
380
N(1, 0, 1),
381
N(0, 1, 1),
382
N(1, 1, 1)
383
in 3-d lattice N
384
sage: loads(dumps(c)) == c
385
True
386
"""
387
return (PointCollection, (self._points, self._module))
388
389
def __tuple__(self):
390
r"""
391
Return the tuple of points of ``self``.
392
393
OUTPUT:
394
395
- a tuple.
396
397
TESTS::
398
399
sage: c = Cone([(0,0,1), (1,0,1), (0,1,1), (1,1,1)]).rays()
400
sage: tuple(c)
401
(N(0, 0, 1), N(1, 0, 1), N(0, 1, 1), N(1, 1, 1))
402
"""
403
return self._points
404
405
def _latex_(self):
406
r"""
407
Return a LaTeX representation of ``self``.
408
409
OUTPUT:
410
411
- a string.
412
413
TESTS::
414
415
sage: c = Cone([(0,0,1), (1,0,1), (0,1,1), (1,1,1)]).rays()
416
sage: print c._latex_()
417
\left(\left(0,\,0,\,1\right)_{N}, \left(1,\,0,\,1\right)_{N},
418
\left(0,\,1,\,1\right)_{N}, \left(1,\,1,\,1\right)_{N}\right)_{N}
419
"""
420
global _output_format
421
if _output_format in ["default", "tuple"]:
422
r = latex(tuple(self))
423
elif _output_format == "matrix":
424
r = latex(self.matrix())
425
elif _output_format == "column matrix":
426
r = latex(self.column_matrix())
427
elif _output_format == "separated column matrix":
428
r = latex(self.column_matrix())
429
r = r.replace("r" * len(self), "|".join("r" * len(self)))
430
return r"%s_{%s}" % (r, latex(self.module()))
431
432
def _matrix_(self, ring=None):
433
r"""
434
Return a matrix whose rows are points of ``self``.
435
436
INPUT:
437
438
- ``ring`` -- a base ring for the returned matrix (default: base ring of
439
:meth:`module` of ``self``).
440
441
OUTPUT:
442
443
- a :class:`matrix <Matrix>`.
444
445
EXAMPLES::
446
447
sage: c = Cone([(0,0,1), (1,0,1), (0,1,1), (1,1,1)]).rays()
448
sage: matrix(c)
449
[0 0 1]
450
[1 0 1]
451
[0 1 1]
452
[1 1 1]
453
"""
454
if ring is None:
455
return self.matrix()
456
else:
457
return self.matrix().change_ring(ring)
458
459
def _repr_(self):
460
r"""
461
Return a string representation of ``self``.
462
463
OUTPUT:
464
465
- a string.
466
467
TESTS::
468
469
sage: c = Cone([(0,0,1), (1,0,1), (0,1,1), (1,1,1)]).rays()
470
sage: print c._repr_()
471
N(0, 0, 1),
472
N(1, 0, 1),
473
N(0, 1, 1),
474
N(1, 1, 1)
475
in 3-d lattice N
476
"""
477
global _output_format
478
if _output_format == "default":
479
r = map(repr, self)
480
r = [point.split(",") for point in r]
481
if not r:
482
r = "Empty collection"
483
else:
484
if "(" in r[0][0]:
485
delimiter = "("
486
elif "[" in r[0][0]:
487
delimiter = "["
488
else:
489
raise ValueError("cannot parse point representation!")
490
heads = []
491
for point in r:
492
head, point[0] = point[0].rsplit(delimiter, 1)
493
heads.append(head + delimiter)
494
format = "{{:<{}}}".format(max(map(len, heads)))
495
widths = [0] * len(r[0])
496
for point in r:
497
for i, coordinate in enumerate(point):
498
widths[i] = max(widths[i], len(coordinate))
499
format += ",".join("{{:>{}}}".format(width) for width in widths)
500
r = ",\n".join([format.format(head, *point)
501
for head, point in zip(heads, r)])
502
elif _output_format == "tuple":
503
r = tuple(self)
504
elif _output_format == "matrix":
505
r = self.matrix()
506
else:
507
r = self.column_matrix()
508
return "{}\nin {}".format(r, self.module())
509
510
def basis(self):
511
r"""
512
Return a linearly independent subset of points of ``self``.
513
514
OUTPUT:
515
516
- a :class:`point collection <PointCollection>` giving a random (but
517
fixed) choice of an `\RR`-basis for the vector space spanned by the
518
points of ``self``.
519
520
EXAMPLES::
521
522
sage: c = Cone([(0,0,1), (1,0,1), (0,1,1), (1,1,1)]).rays()
523
sage: c.basis()
524
N(0, 0, 1),
525
N(1, 0, 1),
526
N(0, 1, 1)
527
in 3-d lattice N
528
529
Calling this method twice will always return *exactly the same* point
530
collection::
531
532
sage: c.basis().basis() is c.basis()
533
True
534
"""
535
if self._basis is None:
536
self._basis = self(self.matrix().pivot_rows())
537
return self._basis
538
539
def cardinality(self):
540
r"""
541
Return the number of points in ``self``.
542
543
OUTPUT:
544
545
- an integer.
546
547
EXAMPLES::
548
549
sage: c = Cone([(0,0,1), (1,0,1), (0,1,1), (1,1,1)]).rays()
550
sage: c.cardinality()
551
4
552
"""
553
return len(self._points)
554
555
def cartesian_product(self, other, module=None):
556
r"""
557
Return the Cartesian product of ``self`` with ``other``.
558
559
INPUT:
560
561
- ``other`` -- a :class:`point collection <PointCollection>`;
562
563
- ``module`` -- (optional) the ambient module for the result. By
564
default, the direct sum of the ambient modules of ``self`` and
565
``other`` is constructed.
566
567
OUTPUT:
568
569
- a :class:`point collection <PointCollection>`.
570
571
EXAMPLES::
572
573
sage: c = Cone([(0,0,1), (1,1,1)]).rays()
574
sage: c.cartesian_product(c)
575
N+N(0, 0, 1, 0, 0, 1),
576
N+N(1, 1, 1, 0, 0, 1),
577
N+N(0, 0, 1, 1, 1, 1),
578
N+N(1, 1, 1, 1, 1, 1)
579
in 6-d lattice N+N
580
"""
581
assert is_PointCollection(other)
582
if module is None:
583
module = self._module.direct_sum(other.module())
584
P = [list(p) for p in self]
585
Q = [list(q) for q in other]
586
PQ = [module(p + q) for q in Q for p in P]
587
for pq in PQ:
588
pq.set_immutable()
589
return PointCollection(PQ, module)
590
591
def column_matrix(self):
592
r"""
593
Return a matrix whose columns are points of ``self``.
594
595
OUTPUT:
596
597
- a :class:`matrix <Matrix>`.
598
599
EXAMPLES::
600
601
sage: c = Cone([(0,0,1), (1,0,1), (0,1,1), (1,1,1)]).rays()
602
sage: c.column_matrix()
603
[0 1 0 1]
604
[0 0 1 1]
605
[1 1 1 1]
606
"""
607
return self.matrix().transpose()
608
609
def dimension(self):
610
r"""
611
Return the dimension of the space spanned by points of ``self``.
612
613
.. note:: You can use either :meth:`dim` or :meth:`dimension`.
614
615
OUTPUT:
616
617
- an integer.
618
619
EXAMPLES::
620
621
sage: c = Cone([(0,0,1), (1,1,1)]).rays()
622
sage: c.dimension()
623
2
624
sage: c.dim()
625
2
626
"""
627
return self.matrix().rank()
628
629
dim = dimension
630
631
def dual_module(self):
632
r"""
633
Return the dual of the ambient module of ``self``.
634
635
OUTPUT:
636
637
- a :class:`module <FreeModule_generic>`. If possible (that is, if the
638
ambient :meth:`module` `M` of ``self`` has a ``dual()`` method), the
639
dual module is returned. Otherwise, `R^n` is returned, where `n` is
640
the dimension of `M` and `R` is its base ring.
641
642
EXAMPLES::
643
644
sage: c = Cone([(0,0,1), (1,0,1), (0,1,1), (1,1,1)]).rays()
645
sage: c.dual_module()
646
3-d lattice M
647
"""
648
M = self._module
649
try:
650
return M.dual()
651
except AttributeError:
652
# TODO: add support for torsion modules as well?
653
return M.base_ring() ** M.dimension()
654
655
def index(self, *args):
656
r"""
657
Return the index of the first occurrence of ``point`` in ``self``.
658
659
INPUT:
660
661
- ``point`` -- a point of ``self``;
662
663
- ``start`` -- (optional) an integer, if given, the search will start
664
at this position;
665
666
- ``stop`` -- (optional) an integer, if given, the search will stop
667
at this position.
668
669
OUTPUT:
670
671
- an integer if ``point`` is in ``self[start:stop]``, otherwise a
672
``ValueError`` exception is raised.
673
674
EXAMPLES::
675
676
sage: c = Cone([(0,0,1), (1,0,1), (0,1,1), (1,1,1)]).rays()
677
sage: c.index((0,1,1))
678
Traceback (most recent call last):
679
...
680
ValueError: tuple.index(x): x not in tuple
681
682
Note that this was not a mistake: the *tuple* ``(0,1,1)`` is *not* a
683
point of ``c``! We need to pass actual element of the ambient module of
684
``c`` to get their indices::
685
686
sage: N = c.module()
687
sage: c.index(N(0,1,1))
688
2
689
sage: c[2]
690
N(0, 1, 1)
691
"""
692
return self._points.index(*args)
693
694
def matrix(self):
695
r"""
696
Return a matrix whose rows are points of ``self``.
697
698
OUTPUT:
699
700
- a :class:`matrix <Matrix>`.
701
702
EXAMPLES::
703
704
sage: c = Cone([(0,0,1), (1,0,1), (0,1,1), (1,1,1)]).rays()
705
sage: c.matrix()
706
[0 0 1]
707
[1 0 1]
708
[0 1 1]
709
[1 1 1]
710
"""
711
if self._matrix is None:
712
M = matrix(self._module.base_ring(), len(self._points),
713
self._module.degree(), self._points)
714
M.set_immutable()
715
self._matrix = M
716
return self._matrix
717
718
def module(self):
719
r"""
720
Return the ambient module of ``self``.
721
722
OUTPUT:
723
724
- a :class:`module <FreeModule_generic>`.
725
726
EXAMPLES::
727
728
sage: c = Cone([(0,0,1), (1,0,1), (0,1,1), (1,1,1)]).rays()
729
sage: c.module()
730
3-d lattice N
731
"""
732
return self._module
733
734
@classmethod # @staticmethod does not work in Cython so far
735
def output_format(cls, format=None):
736
r"""
737
Return or set the output format for **ALL** point collections.
738
739
INPUT:
740
741
- ``format`` -- (optional) if given, must be one of the strings
742
* "default" -- output one point per line with vertical alignment of
743
coordinates in text mode, same as "tuple" for LaTeX;
744
* "tuple" -- output ``tuple(self)`` with lattice information;
745
* "matrix" -- output :meth:`matrix` with lattice information;
746
* "column matrix" -- output :meth:`column_matrix` with lattice
747
information;
748
* "separated column matrix" -- same as "column matrix" for text
749
mode, for LaTeX separate columns by lines (not shown by jsMath).
750
751
OUTPUT:
752
753
- a string with the current format (only if ``format`` was omitted).
754
755
This function affects both regular and LaTeX output.
756
757
EXAMPLES::
758
759
sage: c = Cone([(0,0,1), (1,0,1), (0,1,1), (1,1,1)]).rays()
760
sage: c
761
N(0, 0, 1),
762
N(1, 0, 1),
763
N(0, 1, 1),
764
N(1, 1, 1)
765
in 3-d lattice N
766
sage: c.output_format()
767
'default'
768
sage: c.output_format("tuple")
769
sage: c
770
(N(0, 0, 1), N(1, 0, 1), N(0, 1, 1), N(1, 1, 1))
771
in 3-d lattice N
772
sage: c.output_format("matrix")
773
sage: c
774
[0 0 1]
775
[1 0 1]
776
[0 1 1]
777
[1 1 1]
778
in 3-d lattice N
779
sage: c.output_format("column matrix")
780
sage: c
781
[0 1 0 1]
782
[0 0 1 1]
783
[1 1 1 1]
784
in 3-d lattice N
785
sage: c.output_format("separated column matrix")
786
sage: c
787
[0 1 0 1]
788
[0 0 1 1]
789
[1 1 1 1]
790
in 3-d lattice N
791
792
Note that the last two outpus are identical, separators are only
793
inserted in the LaTeX mode::
794
795
sage: latex(c)
796
\left(\begin{array}{r|r|r|r}
797
0 & 1 & 0 & 1 \\
798
0 & 0 & 1 & 1 \\
799
1 & 1 & 1 & 1
800
\end{array}\right)_{N}
801
802
Since this is a static method, you can call it for the class directly::
803
804
sage: from sage.geometry.point_collection import PointCollection
805
sage: PointCollection.output_format("default")
806
sage: c
807
N(0, 0, 1),
808
N(1, 0, 1),
809
N(0, 1, 1),
810
N(1, 1, 1)
811
in 3-d lattice N
812
"""
813
global _output_format
814
if format is None:
815
return _output_format
816
assert format in ["default", "tuple", "matrix", "column matrix",
817
"separated column matrix"]
818
_output_format = format
819
820
def set(self):
821
r"""
822
Return points of ``self`` as a :class:`frozenset`.
823
824
OUTPUT:
825
826
- a :class:`frozenset`.
827
828
EXAMPLES::
829
830
sage: c = Cone([(0,0,1), (1,0,1), (0,1,1), (1,1,1)]).rays()
831
sage: c.set()
832
frozenset([N(0, 1, 1), N(1, 1, 1), N(0, 0, 1), N(1, 0, 1)])
833
"""
834
if self._set is None:
835
self._set = frozenset(self._points)
836
return self._set
837
838