Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/geometry/toric_lattice.py
4096 views
1
r"""
2
Toric lattices
3
4
This module was designed as a part of the framework for toric varieties
5
(:mod:`~sage.schemes.toric.variety`,
6
:mod:`~sage.schemes.toric.fano_variety`).
7
8
All toric lattices are isomorphic to `\ZZ^n` for some `n`, but will prevent
9
you from doing "wrong" operations with objects from different lattices.
10
11
AUTHORS:
12
13
- Andrey Novoseltsev (2010-05-27): initial version.
14
- Andrey Novoseltsev (2010-07-30): sublattices and quotients.
15
16
EXAMPLES:
17
18
The simplest way to create a toric lattice is to specify its dimension only::
19
20
sage: N = ToricLattice(3)
21
sage: N
22
3-d lattice N
23
24
While our lattice ``N`` is called exactly "N" it is a coincidence: all
25
lattices are called "N" by default::
26
27
sage: another_name = ToricLattice(3)
28
sage: another_name
29
3-d lattice N
30
31
If fact, the above lattice is exactly the same as before as an object in
32
memory::
33
34
sage: N is another_name
35
True
36
37
There are actually four names associated to a toric lattice and they all must
38
be the same for two lattices to coincide::
39
40
sage: N, N.dual(), latex(N), latex(N.dual())
41
(3-d lattice N, 3-d lattice M, N, M)
42
43
Notice that the lattice dual to ``N`` is called "M" which is standard in toric
44
geometry. This happens only if you allow completely automatic handling of
45
names::
46
47
sage: another_N = ToricLattice(3, "N")
48
sage: another_N.dual()
49
3-d lattice N*
50
sage: N is another_N
51
False
52
53
What can you do with toric lattices? Well, their main purpose is to allow
54
creation of elements of toric lattices::
55
56
sage: n = N([1,2,3])
57
sage: n
58
N(1, 2, 3)
59
sage: M = N.dual()
60
sage: m = M(1,2,3)
61
sage: m
62
M(1, 2, 3)
63
64
Dual lattices can act on each other::
65
66
sage: n * m
67
14
68
sage: m * n
69
14
70
71
You can also add elements of the same lattice or scale them::
72
73
sage: 2 * n
74
N(2, 4, 6)
75
sage: n * 2
76
N(2, 4, 6)
77
sage: n + n
78
N(2, 4, 6)
79
80
However, you cannot "mix wrong lattices" in your expressions::
81
82
sage: n + m
83
Traceback (most recent call last):
84
...
85
TypeError: unsupported operand parent(s) for '+':
86
'3-d lattice N' and '3-d lattice M'
87
sage: n * n
88
Traceback (most recent call last):
89
...
90
TypeError: elements of the same toric lattice cannot be multiplied!
91
sage: n == m
92
False
93
94
Note that ``n`` and ``m`` are not equal to each other even though they are
95
both "just (1,2,3)." Moreover, you cannot easily convert elements between
96
toric lattices::
97
98
sage: M(n)
99
Traceback (most recent call last):
100
...
101
TypeError: N(1, 2, 3) cannot be converted to 3-d lattice M!
102
103
If you really need to consider elements of one lattice as elements of another,
104
you can either use intermediate conversion to "just a vector"::
105
106
sage: ZZ3 = ZZ^3
107
sage: n_in_M = M(ZZ3(n))
108
sage: n_in_M
109
M(1, 2, 3)
110
sage: n == n_in_M
111
False
112
sage: n_in_M == m
113
True
114
115
Or you can create a homomorphism from one lattice to any other::
116
117
sage: h = N.hom(identity_matrix(3), M)
118
sage: h(n)
119
M(1, 2, 3)
120
121
.. WARNING::
122
123
While integer vectors (elements of `\ZZ^n`) are printed as ``(1,2,3)``,
124
in the code ``(1,2,3)`` is a :class:`tuple`, which has nothing to do
125
neither with vectors, nor with toric lattices, so the following is
126
probably not what you want while working with toric geometry objects::
127
128
sage: (1,2,3) + (1,2,3)
129
(1, 2, 3, 1, 2, 3)
130
131
Instead, use syntax like ::
132
133
sage: N(1,2,3) + N(1,2,3)
134
N(2, 4, 6)
135
"""
136
# Parts of the "tutorial" above are also in toric_lattice_element.pyx.
137
138
139
#*****************************************************************************
140
# Copyright (C) 2010 Andrey Novoseltsev <[email protected]>
141
# Copyright (C) 2010 William Stein <[email protected]>
142
#
143
# Distributed under the terms of the GNU General Public License (GPL)
144
#
145
# http://www.gnu.org/licenses/
146
#*****************************************************************************
147
148
149
from sage.geometry.toric_lattice_element import (ToricLatticeElement,
150
is_ToricLatticeElement)
151
from sage.geometry.toric_plotter import ToricPlotter
152
from sage.misc.all import latex, parent
153
from sage.modules.fg_pid.fgp_element import FGP_Element
154
from sage.modules.fg_pid.fgp_module import FGP_Module_class
155
from sage.modules.free_module import (FreeModule_ambient_pid,
156
FreeModule_generic_pid,
157
FreeModule_submodule_pid,
158
FreeModule_submodule_with_basis_pid,
159
is_FreeModule)
160
from sage.rings.all import QQ, ZZ
161
from sage.structure.factory import UniqueFactory
162
163
164
165
166
167
def is_ToricLattice(x):
168
r"""
169
Check if ``x`` is a toric lattice.
170
171
INPUT:
172
173
- ``x`` -- anything.
174
175
OUTPUT:
176
177
- ``True`` if ``x`` is a toric lattice and ``False`` otherwise.
178
179
EXAMPLES::
180
181
sage: from sage.geometry.toric_lattice import (
182
... is_ToricLattice)
183
sage: is_ToricLattice(1)
184
False
185
sage: N = ToricLattice(3)
186
sage: N
187
3-d lattice N
188
sage: is_ToricLattice(N)
189
True
190
"""
191
return isinstance(x, ToricLattice_generic)
192
193
194
def is_ToricLatticeQuotient(x):
195
r"""
196
Check if ``x`` is a toric lattice quotient.
197
198
INPUT:
199
200
- ``x`` -- anything.
201
202
OUTPUT:
203
204
- ``True`` if ``x`` is a toric lattice quotient and ``False`` otherwise.
205
206
EXAMPLES::
207
208
sage: from sage.geometry.toric_lattice import (
209
... is_ToricLatticeQuotient)
210
sage: is_ToricLatticeQuotient(1)
211
False
212
sage: N = ToricLattice(3)
213
sage: N
214
3-d lattice N
215
sage: is_ToricLatticeQuotient(N)
216
False
217
sage: Q = N / N.submodule([(1,2,3), (3,2,1)])
218
sage: Q
219
Quotient with torsion of 3-d lattice N
220
by Sublattice <N(1, 2, 3), N(0, 4, 8)>
221
sage: is_ToricLatticeQuotient(Q)
222
True
223
"""
224
return isinstance(x, ToricLattice_quotient)
225
226
227
class ToricLatticeFactory(UniqueFactory):
228
r"""
229
Create a lattice for toric geometry objects.
230
231
INPUT:
232
233
- ``rank`` -- nonnegative integer, the only mandatory parameter;
234
235
- ``name`` -- string;
236
237
- ``dual_name`` -- string;
238
239
- ``latex_name`` -- string;
240
241
- ``latex_dual_name`` -- string.
242
243
OUTPUT:
244
245
- lattice.
246
247
A toric lattice is uniquely determined by its rank and associated names.
248
There are four such "associated names" whose meaning should be clear from
249
the names of the corresponding parameters, but the choice of default
250
values is a little bit involved. So here is the full description of the
251
"naming algorithm":
252
253
#. If no names were given at all, then this lattice will be called "N" and
254
the dual one "M". These are the standard choices in toric geometry.
255
256
#. If ``name`` was given and ``dual_name`` was not, then ``dual_name``
257
will be ``name`` followed by "*".
258
259
#. If LaTeX names were not given, they will coincide with the "usual"
260
names, but if ``dual_name`` was constructed automatically, the trailing
261
star will be typeset as a superscript.
262
263
EXAMPLES:
264
265
Let's start with no names at all and see how automatic names are given::
266
267
sage: L1 = ToricLattice(3)
268
sage: L1
269
3-d lattice N
270
sage: L1.dual()
271
3-d lattice M
272
273
If we give the name "N" explicitly, the dual lattice will be called "N*"::
274
275
sage: L2 = ToricLattice(3, "N")
276
sage: L2
277
3-d lattice N
278
sage: L2.dual()
279
3-d lattice N*
280
281
However, we can give an explicit name for it too::
282
283
sage: L3 = ToricLattice(3, "N", "M")
284
sage: L3
285
3-d lattice N
286
sage: L3.dual()
287
3-d lattice M
288
289
If you want, you may also give explicit LaTeX names::
290
291
sage: L4 = ToricLattice(3, "N", "M", r"\mathbb{N}", r"\mathbb{M}")
292
sage: latex(L4)
293
\mathbb{N}
294
sage: latex(L4.dual())
295
\mathbb{M}
296
297
While all four lattices above are called "N", only two of them are equal
298
(and are actually the same)::
299
300
sage: L1 == L2
301
False
302
sage: L1 == L3
303
True
304
sage: L1 is L3
305
True
306
sage: L1 == L4
307
False
308
309
The reason for this is that ``L2`` and ``L4`` have different names either
310
for dual lattices or for LaTeX typesetting.
311
"""
312
313
def create_key(self, rank, name=None, dual_name=None,
314
latex_name=None, latex_dual_name=None):
315
"""
316
Create a key that uniquely identifies this toric lattice.
317
318
See :class:`ToricLattice <ToricLatticeFactory>` for documentation.
319
320
.. WARNING::
321
322
You probably should not use this function directly.
323
324
TESTS::
325
326
sage: ToricLattice.create_key(3)
327
(3, 'N', 'M', 'N', 'M')
328
sage: N = ToricLattice(3)
329
sage: loads(dumps(N)) is N
330
True
331
sage: TestSuite(N).run()
332
"""
333
rank = int(rank)
334
# Should we use standard M and N lattices?
335
if name is None:
336
if dual_name is not None:
337
raise ValueError("you can name the dual lattice only if you "
338
"also name the original one!")
339
name = "N"
340
dual_name = "M"
341
if latex_name is None:
342
latex_name = name
343
# Now name and latex_name are set
344
# The default for latex_dual_name depends on whether dual_name was
345
# given or constructed, so we determine it before dual_name
346
if latex_dual_name is None:
347
latex_dual_name = (dual_name if dual_name is not None
348
else latex_name + "^*")
349
if dual_name is None:
350
dual_name = name + "*"
351
return (rank, name, dual_name, latex_name, latex_dual_name)
352
353
def create_object(self, version, key):
354
r"""
355
Create the toric lattice described by ``key``.
356
357
See :class:`ToricLattice <ToricLatticeFactory>` for documentation.
358
359
.. WARNING::
360
361
You probably should not use this function directly.
362
363
TESTS::
364
365
sage: key = ToricLattice.create_key(3)
366
sage: ToricLattice.create_object(1, key)
367
3-d lattice N
368
"""
369
return ToricLattice_ambient(*key)
370
371
372
ToricLattice = ToricLatticeFactory("ToricLattice")
373
374
375
# Possible TODO's:
376
# - implement a better construction() method, which still will prohibit
377
# operations mixing lattices by conversion to ZZ^n
378
# - maybe __call__ is not the right place to prohibit conversion between
379
# lattices (we need it now so that morphisms behave nicely)
380
class ToricLattice_generic(FreeModule_generic_pid):
381
r"""
382
Abstract base class for toric lattices.
383
"""
384
385
# This is how other free modules work now, but it seems that things
386
# should be a bit different in the new coersion model
387
_element_class = ToricLatticeElement
388
389
# It is not recommended to override __call__ in Parent-derived objects
390
# since it may interfere with the coersion model. We do it here to allow
391
# N(1,2,3) to be interpreted as N([1,2,3]). We also prohibit N(m) where
392
# m is an element of another lattice. Otherwise morphisms will care only
393
# about dimension of lattices.
394
def __call__(self, *args, **kwds):
395
r"""
396
Construct a new element of ``self``.
397
398
INPUT:
399
400
- anything that can be interpreted as coordinates, except for elements
401
of other lattices.
402
403
OUTPUT:
404
405
- :class:`~sage.geometry.toric_lattice_element.ToricLatticeElement`.
406
407
TESTS::
408
409
sage: N = ToricLattice(3)
410
sage: N.__call__([1,2,3])
411
N(1, 2, 3)
412
sage: N([1,2,3]) # indirect test
413
N(1, 2, 3)
414
415
The point of overriding this function was to allow writing the above
416
command as::
417
418
sage: N(1,2,3)
419
N(1, 2, 3)
420
421
And to prohibit conversion between different lattices::
422
423
sage: M = N.dual()
424
sage: M(N(1,2,3))
425
Traceback (most recent call last):
426
...
427
TypeError: N(1, 2, 3) cannot be converted to 3-d lattice M!
428
429
We also test that the special treatment of zero still works::
430
431
sage: N(0)
432
N(0, 0, 0)
433
434
Quotients of toric lattices can be converted to a new toric
435
lattice of the appropriate dimension::
436
437
sage: N3 = ToricLattice(3, 'N3')
438
sage: Q = N3 / N3.span([ N(1,2,3) ])
439
sage: Q.an_element()
440
N3[0, 0, 1]
441
sage: N2 = ToricLattice(2, 'N2')
442
sage: N2( Q.an_element() )
443
N2(1, 0)
444
"""
445
supercall = super(ToricLattice_generic, self).__call__
446
if args == (0, ):
447
# Special treatment for N(0) to return (0,...,0)
448
return supercall(*args, **kwds)
449
450
if (isinstance(args[0], ToricLattice_quotient_element)
451
and args[0].parent().is_torsion_free()):
452
# convert a torsion free quotient lattice
453
return supercall(list(args[0]), **kwds)
454
455
try:
456
coordinates = map(ZZ, args)
457
except TypeError:
458
# Prohibit conversion of elements of other lattices
459
if (is_ToricLatticeElement(args[0])
460
and args[0].parent().ambient_module()
461
is not self.ambient_module()):
462
raise TypeError("%s cannot be converted to %s!"
463
% (args[0], self))
464
# "Standard call"
465
return supercall(*args, **kwds)
466
# Coordinates were given without packing them into a list or a tuple
467
return supercall(coordinates, **kwds)
468
469
def __contains__(self, point):
470
r"""
471
Check if ``point`` is an element of ``self``.
472
473
INPUT:
474
475
- ``point`` -- anything.
476
477
OUTPUT:
478
479
- ``True`` if ``point`` is an element of ``self``, ``False``
480
otherwise.
481
482
TESTS::
483
484
sage: N = ToricLattice(3)
485
sage: M = N.dual()
486
sage: L = ToricLattice(3, "L")
487
sage: 1 in N
488
False
489
sage: (1,0) in N
490
False
491
sage: (1,0,0) in N
492
True
493
sage: N(1,0,0) in N
494
True
495
sage: M(1,0,0) in N
496
False
497
sage: L(1,0,0) in N
498
False
499
sage: (1/2,0,0) in N
500
False
501
sage: (2/2,0,0) in N
502
True
503
"""
504
try:
505
self(point)
506
except TypeError:
507
return False
508
return True
509
510
# We need to override this function, otherwise e.g. the sum of elements of
511
# different lattices of the same dimension will live in ZZ^n.
512
def construction(self):
513
r"""
514
Return the functorial construction of ``self``.
515
516
OUTPUT:
517
518
- ``None``, we do not think of toric lattices as constructed from
519
simpler objects since we do not want to perform arithmetic involving
520
different lattices.
521
522
TESTS::
523
524
sage: print ToricLattice(3).construction()
525
None
526
"""
527
return None
528
529
def direct_sum(self, other):
530
r"""
531
Return the direct sum with ``other``.
532
533
INPUT:
534
535
- ``other`` -- a toric lattice or more general module.
536
537
OUTPUT:
538
539
The direct sum of ``self`` and ``other`` as `\ZZ`-modules. If
540
``other`` is a :class:`ToricLattice <ToricLatticeFactory>`,
541
another toric lattice will be returned.
542
543
EXAMPLES::
544
545
sage: K = ToricLattice(3, 'K')
546
sage: L = ToricLattice(3, 'L')
547
sage: N = K.direct_sum(L); N
548
6-d lattice K+L
549
sage: N, N.dual(), latex(N), latex(N.dual())
550
(6-d lattice K+L, 6-d lattice K*+L*, K \oplus L, K^* \oplus L^*)
551
552
With default names::
553
554
sage: N = ToricLattice(3).direct_sum(ToricLattice(2))
555
sage: N, N.dual(), latex(N), latex(N.dual())
556
(5-d lattice N+N, 5-d lattice M+M, N \oplus N, M \oplus M)
557
558
If ``other`` is not a :class:`ToricLattice
559
<ToricLatticeFactory>`, fall back to sum of modules::
560
561
sage: ToricLattice(3).direct_sum(ZZ^2)
562
Free module of degree 5 and rank 5 over Integer Ring
563
Echelon basis matrix:
564
[1 0 0 0 0]
565
[0 1 0 0 0]
566
[0 0 1 0 0]
567
[0 0 0 1 0]
568
[0 0 0 0 1]
569
"""
570
if not isinstance(other, ToricLattice_generic):
571
return super(ToricLattice_generic, self).direct_sum(other)
572
573
def make_name(N1, N2, use_latex=False):
574
if use_latex:
575
return latex(N1)+ ' \oplus ' +latex(N2)
576
else:
577
return N1._name+ '+' +N2._name
578
579
rank = self.rank() + other.rank()
580
name = make_name(self, other, False)
581
dual_name = make_name(self.dual(), other.dual(), False)
582
latex_name = make_name(self, other, True)
583
latex_dual_name = make_name(self.dual(), other.dual(), True)
584
return ToricLattice(rank, name, dual_name, latex_name, latex_dual_name)
585
586
def intersection(self, other):
587
r"""
588
Return the intersection of ``self`` and ``other``.
589
590
INPUT:
591
592
- ``other`` - a toric (sub)lattice.dual
593
594
OUTPUT:
595
596
- a toric (sub)lattice.
597
598
EXAMPLES::
599
600
sage: N = ToricLattice(3)
601
sage: Ns1 = N.submodule([N(2,4,0), N(9,12,0)])
602
sage: Ns2 = N.submodule([N(1,4,9), N(9,2,0)])
603
sage: Ns1.intersection(Ns2)
604
Sublattice <N(54, 12, 0)>
605
606
Note that if one of the intersecting sublattices is a sublattice of
607
another, no new lattices will be constructed::
608
609
sage: N.intersection(N) is N
610
True
611
sage: Ns1.intersection(N) is Ns1
612
True
613
sage: N.intersection(Ns1) is Ns1
614
True
615
"""
616
# Lattice-specific input check
617
if not is_ToricLattice(other):
618
raise TypeError("%s is not a toric lattice!" % other)
619
if self.ambient_module() != other.ambient_module():
620
raise ValueError("%s and %s have different ambient lattices!" %
621
(self, other))
622
# Construct a generic intersection, but make sure to return a lattice.
623
I = super(ToricLattice_generic, self).intersection(other)
624
if not is_ToricLattice(I):
625
I = self.ambient_module().submodule(I.basis())
626
return I
627
628
def quotient(self, sub, check=True,
629
positive_point=None, positive_dual_point=None):
630
"""
631
Return the quotient of ``self`` by the given sublattice ``sub``.
632
633
INPUT:
634
635
- ``sub`` -- sublattice of self;
636
637
- ``check`` -- (default: True) whether or not to check that ``sub`` is
638
a valid sublattice.
639
640
If the quotient is one-dimensional and torsion free, the
641
following two mutually exclusive keyword arguments are also
642
allowed. They decide the sign choice for the (single)
643
generator of the quotient lattice:
644
645
- ``positive_point`` -- a lattice point of ``self`` not in the
646
sublattice ``sub`` (that is, not zero in the quotient
647
lattice). The quotient generator will be in the same
648
direction as ``positive_point``.
649
650
- ``positive_dual_point`` -- a dual lattice point. The
651
quotient generator will be chosen such that its lift has a
652
positive product with ``positive_dual_point``. Note: if
653
``positive_dual_point`` is not zero on the sublattice
654
``sub``, then the notion of positivity will depend on the
655
choice of lift!
656
657
EXAMPLES::
658
659
sage: N = ToricLattice(3)
660
sage: Ns = N.submodule([N(2,4,0), N(9,12,0)])
661
sage: Q = N/Ns
662
sage: Q
663
Quotient with torsion of 3-d lattice N
664
by Sublattice <N(1, 8, 0), N(0, 12, 0)>
665
666
See :class:`ToricLattice_quotient` for more examples.
667
"""
668
if check:
669
try:
670
sub = self.submodule(sub)
671
except (TypeError, ArithmeticError):
672
raise ArithmeticError("cannot interpret %s as a sublattice "
673
"of %s!" % (sub, self))
674
return ToricLattice_quotient(self, sub, check=False,
675
positive_point=positive_point,
676
positive_dual_point=positive_dual_point)
677
678
def saturation(self):
679
r"""
680
Return the saturation of ``self``.
681
682
OUTPUT:
683
684
- a :class:`toric lattice <ToricLatticeFactory>`.
685
686
EXAMPLES::
687
688
sage: N = ToricLattice(3)
689
sage: Ns = N.submodule([(1,2,3), (4,5,6)])
690
sage: Ns
691
Sublattice <N(1, 2, 3), N(0, 3, 6)>
692
sage: Ns_sat = Ns.saturation()
693
sage: Ns_sat
694
Sublattice <N(1, 0, -1), N(0, 1, 2)>
695
sage: Ns_sat is Ns_sat.saturation()
696
True
697
"""
698
S = super(ToricLattice_generic, self).saturation()
699
return S if is_ToricLattice(S) else self.ambient_module().submodule(S)
700
701
def span(self, *args, **kwds):
702
"""
703
Return the span of the given generators.
704
705
INPUT:
706
707
- ``gens`` -- list of elements of the ambient vector space of
708
``self``.
709
710
OUTPUT:
711
712
- submodule spanned by ``gens``.
713
714
.. NOTE::
715
716
The output need not be a submodule of ``self``, nor even of the
717
ambient space. It must, however, be contained in the ambient
718
vector space.
719
720
See also :meth:`span_of_basis`,
721
:meth:`~sage.modules.free_module.FreeModule_generic_pid.submodule`,
722
and
723
:meth:`~sage.modules.free_module.FreeModule_generic_pid.submodule_with_basis`,
724
725
EXAMPLES::
726
727
sage: N = ToricLattice(3)
728
sage: Ns = N.submodule([N.gen(0)])
729
sage: Ns.span([N.gen(1)])
730
Sublattice <N(0, 1, 0)>
731
sage: Ns.submodule([N.gen(1)])
732
Traceback (most recent call last):
733
...
734
ArithmeticError: Argument gens (= [N(0, 1, 0)])
735
does not generate a submodule of self.
736
"""
737
if len(args) > 1:
738
base_ring = args[1]
739
elif "base_ring" in kwds:
740
base_ring = kwds["base_ring"]
741
else:
742
base_ring = None
743
if base_ring is None or base_ring is ZZ:
744
return ToricLattice_sublattice(self.ambient_module(),
745
*args, **kwds)
746
else:
747
return super(ToricLattice_generic, self).span(*args, **kwds)
748
749
def span_of_basis(self, *args, **kwds):
750
r"""
751
Return the submodule with the given ``basis``.
752
753
INPUT:
754
755
- ``basis`` -- list of elements of the ambient vector space of
756
``self``.
757
758
OUTPUT:
759
760
- submodule spanned by ``basis``.
761
762
.. NOTE::
763
764
The output need not be a submodule of ``self``, nor even of the
765
ambient space. It must, however, be contained in the ambient
766
vector space.
767
768
See also :meth:`span`,
769
:meth:`~sage.modules.free_module.FreeModule_generic_pid.submodule`,
770
and
771
:meth:`~sage.modules.free_module.FreeModule_generic_pid.submodule_with_basis`,
772
773
EXAMPLES::
774
775
sage: N = ToricLattice(3)
776
sage: Ns = N.span_of_basis([(1,2,3)])
777
sage: Ns.span_of_basis([(2,4,0)])
778
Sublattice <N(2, 4, 0)>
779
sage: Ns.span_of_basis([(1/5,2/5,0), (1/7,1/7,0)])
780
Sublattice <(1/5, 2/5, 0), (1/7, 1/7, 0)>
781
782
Of course the input basis vectors must be linearly independent::
783
784
sage: Ns.span_of_basis([(1,2,0), (2,4,0)])
785
Traceback (most recent call last):
786
...
787
ValueError: The given basis vectors must be linearly independent.
788
"""
789
if len(args) > 1:
790
base_ring = args[1]
791
elif "base_ring" in kwds:
792
base_ring = kwds["base_ring"]
793
else:
794
base_ring = None
795
if base_ring is None or base_ring is ZZ:
796
return ToricLattice_sublattice_with_basis(self.ambient_module(),
797
*args, **kwds)
798
else:
799
return super(ToricLattice_generic, self).span_with_basis(*args,
800
**kwds)
801
802
803
class ToricLattice_ambient(ToricLattice_generic, FreeModule_ambient_pid):
804
r"""
805
Create a toric lattice.
806
807
See :class:`ToricLattice <ToricLatticeFactory>` for documentation.
808
809
.. WARNING::
810
811
There should be only one toric lattice with the given rank and
812
associated names. Using this class directly to create toric lattices
813
may lead to unexpected results. Please, use :class:`ToricLattice
814
<ToricLatticeFactory>` to create toric lattices.
815
816
TESTS::
817
818
sage: N = ToricLattice(3, "N", "M", "N", "M")
819
sage: N
820
3-d lattice N
821
sage: TestSuite(N).run()
822
"""
823
824
def __init__(self, rank, name, dual_name, latex_name, latex_dual_name):
825
r"""
826
See :class:`ToricLattice <ToricLatticeFactory>` for documentation.
827
828
TESTS::
829
830
sage: ToricLattice(3, "N", "M", "N", "M")
831
3-d lattice N
832
"""
833
super(ToricLattice_ambient, self).__init__(ZZ, rank)
834
self._name = name
835
self._dual_name = dual_name
836
self._latex_name = latex_name
837
self._latex_dual_name = latex_dual_name
838
839
def __cmp__(self, right):
840
r"""
841
Compare ``self`` and ``right``.
842
843
INPUT:
844
845
- ``right`` -- anything.
846
847
OUTPUT:
848
849
- 0 if ``right`` is a toric lattice of the same dimension as ``self``
850
and their associated names are the same, 1 or -1 otherwise.
851
852
TESTS::
853
854
sage: N3 = ToricLattice(3)
855
sage: N4 = ToricLattice(4)
856
sage: M3 = N3.dual()
857
sage: cmp(N3, N4)
858
-1
859
sage: cmp(N3, M3)
860
1
861
sage: abs( cmp(N3, 3) )
862
1
863
sage: cmp(N3, ToricLattice(3))
864
0
865
"""
866
if self is right:
867
return 0
868
c = cmp(type(self), type(right))
869
if c:
870
return c
871
c = cmp(self.rank(), right.rank())
872
if c:
873
return c
874
# If lattices are the same as ZZ-modules, compare associated names
875
return cmp([self._name, self._dual_name,
876
self._latex_name, self._latex_dual_name],
877
[right._name, right._dual_name,
878
right._latex_name, right._latex_dual_name])
879
880
def _latex_(self):
881
r"""
882
Return a LaTeX representation of ``self``.
883
884
OUTPUT:
885
886
- string.
887
888
TESTS::
889
890
sage: L = ToricLattice(3, "L")
891
sage: L.dual()._latex_()
892
'L^*'
893
"""
894
return self._latex_name
895
896
def _repr_(self):
897
r"""
898
Return a string representation of ``self``.
899
900
OUTPUT:
901
902
- string.
903
904
TESTS::
905
906
sage: L = ToricLattice(3, "L")
907
sage: L.dual()._repr_()
908
'3-d lattice L*'
909
"""
910
return "%d-d lattice %s" % (self.dimension(), self._name)
911
912
def ambient_module(self):
913
r"""
914
Return the ambient module of ``self``.
915
916
OUTPUT:
917
918
- :class:`toric lattice <ToricLatticeFactory>`.
919
920
.. NOTE::
921
922
For any ambient toric lattice its ambient module is the lattice
923
itself.
924
925
EXAMPLES::
926
927
sage: N = ToricLattice(3)
928
sage: N.ambient_module()
929
3-d lattice N
930
sage: N.ambient_module() is N
931
True
932
"""
933
return self
934
935
def dual(self):
936
r"""
937
Return the lattice dual to ``self``.
938
939
OUTPUT:
940
941
- :class:`toric lattice <ToricLatticeFactory>`.
942
943
EXAMPLES::
944
945
sage: N = ToricLattice(3)
946
sage: N
947
3-d lattice N
948
sage: M = N.dual()
949
sage: M
950
3-d lattice M
951
sage: M.dual() is N
952
True
953
954
Elements of dual lattices can act on each other::
955
956
sage: n = N(1,2,3)
957
sage: m = M(4,5,6)
958
sage: n * m
959
32
960
sage: m * n
961
32
962
"""
963
if "_dual" not in self.__dict__:
964
self._dual = ToricLattice(self.rank(), self._dual_name,
965
self._name, self._latex_dual_name, self._latex_name)
966
return self._dual
967
968
def plot(self, **options):
969
r"""
970
Plot ``self``.
971
972
INPUT:
973
974
- any options for toric plots (see :func:`toric_plotter.options
975
<sage.geometry.toric_plotter.options>`), none are mandatory.
976
977
OUTPUT:
978
979
- a plot.
980
981
EXAMPLES::
982
983
sage: N = ToricLattice(3)
984
sage: N.plot()
985
"""
986
if "show_lattice" not in options:
987
# Unless user made an explicit decision, we assume that lattice
988
# should be visible no matter what is the size of the bounding box.
989
options["show_lattice"] = True
990
tp = ToricPlotter(options, self.degree())
991
tp.adjust_options()
992
return tp.plot_lattice()
993
994
995
class ToricLattice_sublattice_with_basis(ToricLattice_generic,
996
FreeModule_submodule_with_basis_pid):
997
r"""
998
Construct the sublattice of ``ambient`` toric lattice with given ``basis``.
999
1000
INPUT (same as for
1001
:class:`~sage.modules.free_module.FreeModule_submodule_with_basis_pid`):
1002
1003
- ``ambient`` -- ambient :class:`toric lattice <ToricLatticeFactory>` for
1004
this sublattice;
1005
1006
- ``basis`` -- list of linearly independent elements of ``ambient``, these
1007
elements will be used as the default basis of the constructed
1008
sublattice;
1009
1010
- see the base class for other available options.
1011
1012
OUTPUT:
1013
1014
- sublattice of a toric lattice with a user-specified basis.
1015
1016
See also :class:`ToricLattice_sublattice` if you do not want to specify an
1017
explicit basis.
1018
1019
EXAMPLES:
1020
1021
The intended way to get objects of this class is to use
1022
:meth:`submodule_with_basis` method of toric lattices::
1023
1024
sage: N = ToricLattice(3)
1025
sage: sublattice = N.submodule_with_basis([(1,1,0), (3,2,1)])
1026
sage: sublattice.has_user_basis()
1027
True
1028
sage: sublattice.basis()
1029
[
1030
N(1, 1, 0),
1031
N(3, 2, 1)
1032
]
1033
1034
Even if you have provided your own basis, you still can access the
1035
"standard" one::
1036
1037
sage: sublattice.echelonized_basis()
1038
[
1039
N(1, 0, 1),
1040
N(0, 1, -1)
1041
]
1042
"""
1043
1044
def _repr_(self):
1045
r"""
1046
Return a string representation of ``self``.
1047
1048
OUTPUT:
1049
1050
- string.
1051
1052
TESTS::
1053
1054
sage: L = ToricLattice(3, "L")
1055
sage: L.submodule_with_basis([(3,2,1),(1,2,3)])
1056
Sublattice <L(3, 2, 1), L(1, 2, 3)>
1057
sage: print L.submodule([(3,2,1),(1,2,3)])._repr_()
1058
Sublattice <L(1, 2, 3), L(0, 4, 8)>
1059
"""
1060
s = 'Sublattice '
1061
s += '<'
1062
s += ', '.join(map(str,self.basis()))
1063
s += '>'
1064
return s
1065
1066
def _latex_(self):
1067
r"""
1068
Return a LaTeX representation of ``self``.
1069
1070
OUTPUT:
1071
1072
- string.
1073
1074
TESTS::
1075
1076
sage: L = ToricLattice(3, "L")
1077
sage: L.submodule_with_basis([(3,2,1),(1,2,3)])._latex_()
1078
'\\left\\langle\\left(3,\\,2,\\,1\\right)_{L},
1079
\\left(1,\\,2,\\,3\\right)_{L}\\right\\rangle'
1080
sage: L.submodule([(3,2,1),(1,2,3)])._latex_()
1081
'\\left\\langle\\left(1,\\,2,\\,3\\right)_{L},
1082
\\left(0,\\,4,\\,8\\right)_{L}\\right\\rangle'
1083
"""
1084
s = '\\left\\langle'
1085
s += ', '.join([ b._latex_() for b in self.basis() ])
1086
s += '\\right\\rangle'
1087
return s
1088
1089
def dual(self):
1090
r"""
1091
Return the lattice dual to ``self``.
1092
1093
OUTPUT:
1094
1095
- a :class:`toric lattice quotient <ToricLattice_quotient>`.
1096
1097
EXAMPLES::
1098
1099
sage: N = ToricLattice(3)
1100
sage: Ns = N.submodule([(1,1,0), (3,2,1)])
1101
sage: Ns.dual()
1102
2-d lattice, quotient of 3-d lattice M by Sublattice <M(1, -1, -1)>
1103
"""
1104
if "_dual" not in self.__dict__:
1105
if not self is self.saturation():
1106
raise ValueError("only dual lattices of saturated sublattices "
1107
"can be constructed! Got %s." % self)
1108
self._dual = (self.ambient_module().dual() /
1109
self.basis_matrix().right_kernel())
1110
self._dual._dual = self
1111
return self._dual
1112
1113
def plot(self, **options):
1114
r"""
1115
Plot ``self``.
1116
1117
INPUT:
1118
1119
- any options for toric plots (see :func:`toric_plotter.options
1120
<sage.geometry.toric_plotter.options>`), none are mandatory.
1121
1122
OUTPUT:
1123
1124
- a plot.
1125
1126
EXAMPLES::
1127
1128
sage: N = ToricLattice(3)
1129
sage: sublattice = N.submodule_with_basis([(1,1,0), (3,2,1)])
1130
sage: sublattice.plot()
1131
1132
Now we plot both the ambient lattice and its sublattice::
1133
1134
sage: N.plot() + sublattice.plot(point_color="red")
1135
"""
1136
if "show_lattice" not in options:
1137
# Unless user made an explicit decision, we assume that lattice
1138
# should be visible no matter what is the size of the bounding box.
1139
options["show_lattice"] = True
1140
if "lattice_filter" in options:
1141
old = options["lattice_filter"]
1142
options["lattice_filter"] = lambda pt: pt in self and old(pt)
1143
else:
1144
options["lattice_filter"] = lambda pt: pt in self
1145
tp = ToricPlotter(options, self.degree())
1146
tp.adjust_options()
1147
return tp.plot_lattice()
1148
1149
1150
class ToricLattice_sublattice(ToricLattice_sublattice_with_basis,
1151
FreeModule_submodule_pid):
1152
r"""
1153
Construct the sublattice of ``ambient`` toric lattice generated by ``gens``.
1154
1155
INPUT (same as for
1156
:class:`~sage.modules.free_module.FreeModule_submodule_pid`):
1157
1158
- ``ambient`` -- ambient :class:`toric lattice <ToricLatticeFactory>` for
1159
this sublattice;
1160
1161
- ``gens`` -- list of elements of ``ambient`` generating the constructed
1162
sublattice;
1163
1164
- see the base class for other available options.
1165
1166
OUTPUT:
1167
1168
- sublattice of a toric lattice with an automatically chosen basis.
1169
1170
See also :class:`ToricLattice_sublattice_with_basis` if you want to
1171
specify an explicit basis.
1172
1173
EXAMPLES:
1174
1175
The intended way to get objects of this class is to use
1176
:meth:`submodule` method of toric lattices::
1177
1178
sage: N = ToricLattice(3)
1179
sage: sublattice = N.submodule([(1,1,0), (3,2,1)])
1180
sage: sublattice.has_user_basis()
1181
False
1182
sage: sublattice.basis()
1183
[
1184
N(1, 0, 1),
1185
N(0, 1, -1)
1186
]
1187
1188
For sublattices without user-specified basis, the basis obtained above is
1189
the same as the "standard" one::
1190
1191
sage: sublattice.echelonized_basis()
1192
[
1193
N(1, 0, 1),
1194
N(0, 1, -1)
1195
]
1196
"""
1197
pass
1198
1199
1200
1201
class ToricLattice_quotient_element(FGP_Element):
1202
r"""
1203
Create an element of a toric lattice quotient.
1204
1205
.. WARNING::
1206
1207
You probably should not construct such elements explicitly.
1208
1209
INPUT:
1210
1211
- same as for :class:`~sage.modules.fg_pid.fgp_element.FGP_Element`.
1212
1213
OUTPUT:
1214
1215
- element of a toric lattice quotient.
1216
1217
TESTS::
1218
1219
sage: N = ToricLattice(3)
1220
sage: sublattice = N.submodule([(1,1,0), (3,2,1)])
1221
sage: Q = N/sublattice
1222
sage: e = Q(1,2,3)
1223
sage: e
1224
N[1, 2, 3]
1225
sage: e2 = Q(N(2,3,3))
1226
sage: e2
1227
N[2, 3, 3]
1228
sage: e == e2
1229
True
1230
sage: e.vector()
1231
(4)
1232
sage: e2.vector()
1233
(4)
1234
"""
1235
1236
def _latex_(self):
1237
r"""
1238
Return a LaTeX representation of ``self``.
1239
1240
OUTPUT:
1241
1242
- string.
1243
1244
TESTS::
1245
1246
sage: N = ToricLattice(3)
1247
sage: Ns = N.submodule([N(2,4,0), N(9,12,0)])
1248
sage: Q = N/Ns
1249
sage: print Q.gen(0)._latex_()
1250
\left[0,\,1,\,0\right]_{N}
1251
"""
1252
return latex(self.lift()).replace("(", "[", 1).replace(")", "]", 1)
1253
1254
def _repr_(self):
1255
r"""
1256
Return a string representation of ``self``.
1257
1258
OUTPUT:
1259
1260
- string.
1261
1262
TESTS::
1263
1264
sage: N = ToricLattice(3)
1265
sage: Ns = N.submodule([N(2,4,0), N(9,12,0)])
1266
sage: Q = N/Ns
1267
sage: print Q.gen(0)._repr_()
1268
N[0, 1, 0]
1269
"""
1270
return str(self.lift()).replace("(", "[", 1).replace(")", "]", 1)
1271
1272
def set_immutable(self):
1273
r"""
1274
Make ``self`` immutable.
1275
1276
OUTPUT:
1277
1278
- none.
1279
1280
.. note:: Elements of toric lattice quotients are always immutable, so
1281
this method does nothing, it is introduced for compatibility
1282
purposes only.
1283
1284
EXAMPLES::
1285
1286
sage: N = ToricLattice(3)
1287
sage: Ns = N.submodule([N(2,4,0), N(9,12,0)])
1288
sage: Q = N/Ns
1289
sage: Q.0.set_immutable()
1290
"""
1291
pass
1292
1293
1294
class ToricLattice_quotient(FGP_Module_class):
1295
r"""
1296
Construct the quotient of a toric lattice ``V`` by its sublattice ``W``.
1297
1298
INPUT:
1299
1300
- ``V`` -- ambient toric lattice;
1301
1302
- ``W`` -- sublattice of ``V``;
1303
1304
- ``check`` -- (default: ``True``) whether to check correctness of input
1305
or not.
1306
1307
If the quotient is one-dimensional and torsion free, the following
1308
two mutually exclusive keyword arguments are also allowed. They
1309
decide the sign choice for the (single) generator of the quotient
1310
lattice:
1311
1312
- ``positive_point`` -- a lattice point of ``self`` not in the
1313
sublattice ``sub`` (that is, not zero in the quotient
1314
lattice). The quotient generator will be in the same direction
1315
as ``positive_point``.
1316
1317
- ``positive_dual_point`` -- a dual lattice point. The quotient
1318
generator will be chosen such that its lift has a positive
1319
product with ``positive_dual_point``. Note: if
1320
``positive_dual_point`` is not zero on the sublattice ``sub``,
1321
then the notion of positivity will depend on the choice of lift!
1322
1323
OUTPUT:
1324
1325
- quotient of ``V`` by ``W``.
1326
1327
EXAMPLES:
1328
1329
The intended way to get objects of this class is to use
1330
:meth:`quotient` method of toric lattices::
1331
1332
sage: N = ToricLattice(3)
1333
sage: sublattice = N.submodule([(1,1,0), (3,2,1)])
1334
sage: Q = N/sublattice
1335
sage: Q
1336
1-d lattice, quotient of 3-d lattice N by Sublattice <N(1, 0, 1), N(0, 1, -1)>
1337
sage: Q.gens()
1338
(N[0, 0, 1],)
1339
1340
Here, ``sublattice`` happens to be of codimension one in ``N``. If
1341
you want to prescribe the sign of the quotient generator, you can
1342
do either::
1343
1344
sage: Q = N.quotient(sublattice, positive_point=N(0,0,-1)); Q
1345
1-d lattice, quotient of 3-d lattice N by Sublattice <N(1, 0, 1), N(0, 1, -1)>
1346
sage: Q.gens()
1347
(N[0, 0, -1],)
1348
1349
or::
1350
1351
sage: M = N.dual()
1352
sage: Q = N.quotient(sublattice, positive_dual_point=M(0,0,-1)); Q
1353
1-d lattice, quotient of 3-d lattice N by Sublattice <N(1, 0, 1), N(0, 1, -1)>
1354
sage: Q.gens()
1355
(N[0, 0, -1],)
1356
1357
TESTS::
1358
1359
sage: loads(dumps(Q)) == Q
1360
True
1361
sage: loads(dumps(Q)).gens() == Q.gens()
1362
True
1363
"""
1364
1365
def __init__(self, V, W, check=True, positive_point=None, positive_dual_point=None):
1366
r"""
1367
The constructor
1368
1369
See :class:`ToricLattice_quotient` for an explanation of the arguments.
1370
1371
EXAMPLES::
1372
1373
sage: N = ToricLattice(3)
1374
sage: from sage.geometry.toric_lattice import ToricLattice_quotient
1375
sage: ToricLattice_quotient(N, N.span([N(1,2,3)]))
1376
2-d lattice, quotient of 3-d lattice N by Sublattice <N(1, 2, 3)>
1377
"""
1378
super(ToricLattice_quotient, self).__init__(V, W, check)
1379
if (positive_point, positive_dual_point) == (None, None):
1380
self._flip_sign_of_generator = False
1381
return
1382
1383
self._flip_sign_of_generator = False
1384
assert self.is_torsion_free() and self.ngens()==1, \
1385
'You may only specify a positive direction in the codimension one case.'
1386
quotient_generator = self.gen(0)
1387
lattice = self.V().ambient_module()
1388
if (positive_point!=None) and (positive_dual_point==None):
1389
assert positive_point in lattice, 'positive_point must be a lattice point.'
1390
point_quotient = self(positive_point)
1391
scalar_product = quotient_generator.vector()[0] * point_quotient.vector()[0]
1392
if scalar_product==0:
1393
raise ValueError, str(positive_point)+' is zero in the quotient.'
1394
elif (positive_point==None) and (positive_dual_point!=None):
1395
assert positive_dual_point in lattice.dual(), 'positive_dual_point must be a dual lattice point.'
1396
scalar_product = quotient_generator.lift() * positive_dual_point
1397
if scalar_product==0:
1398
raise ValueError, str(positive_dual_point)+' is zero on the lift of the quotient generator.'
1399
else:
1400
raise ValueError, 'You may not specify both positive_point and positive_dual_point.'
1401
self._flip_sign_of_generator = (scalar_product<0)
1402
1403
def gens(self):
1404
"""
1405
Return the generators of the quotient.
1406
1407
OUTPUT:
1408
1409
A tuple of :class:`ToricLattice_quotient_element` generating
1410
the quotient.
1411
1412
EXAMPLES::
1413
1414
sage: N = ToricLattice(3)
1415
sage: Q = N.quotient(N.span([N(1,2,3), N(0,2,1)]), positive_point=N(0,-1,0))
1416
sage: Q.gens()
1417
(N[0, -1, 0],)
1418
"""
1419
gens = self.smith_form_gens()
1420
if self._flip_sign_of_generator:
1421
assert len(gens)==1
1422
return (-gens[0],)
1423
else:
1424
return gens
1425
1426
# Should be overridden in derived classes.
1427
Element = ToricLattice_quotient_element
1428
1429
def _element_constructor_(self, *x, **kwds):
1430
r"""
1431
Construct an element of ``self``.
1432
1433
INPUT:
1434
1435
- element of a compatible toric object (lattice, sublattice, quotient)
1436
or something that defines such an element (list, generic vector,
1437
etc.).
1438
1439
OUTPUT:
1440
1441
- :class:`toric lattice quotient element
1442
<ToricLattice_quotient_element>`.
1443
1444
EXAMPLES::
1445
1446
sage: N = ToricLattice(3)
1447
sage: Ns = N.submodule([N(2,4,0), N(9,12,0)])
1448
sage: Q = N/Ns
1449
sage: x = Q(1,2,3) # indirect doctest
1450
sage: x
1451
N[1, 2, 3]
1452
sage: type(x)
1453
<class 'sage.geometry.toric_lattice.ToricLattice_quotient_with_category.element_class'>
1454
sage: x is Q(x)
1455
True
1456
sage: x.parent() is Q
1457
True
1458
sage: x == Q(N(1,2,3))
1459
True
1460
sage: y = Q(3,6,3)
1461
sage: y
1462
N[3, 6, 3]
1463
sage: x == y
1464
True
1465
"""
1466
if len(x) == 1 and (x[0] not in ZZ or x[0] == 0):
1467
x = x[0]
1468
if parent(x) is self:
1469
return x
1470
try:
1471
x = x.lift()
1472
except AttributeError:
1473
pass
1474
try:
1475
return self.element_class(self, self._V(x), **kwds)
1476
except TypeError:
1477
return self.linear_combination_of_smith_form_gens(x)
1478
1479
def _latex_(self):
1480
r"""
1481
Return a LaTeX representation of ``self``.
1482
1483
OUTPUT:
1484
1485
- string.
1486
1487
TESTS::
1488
1489
sage: N = ToricLattice(3)
1490
sage: Ns = N.submodule([N(2,4,0), N(9,12,0)])
1491
sage: Q = N/Ns
1492
sage: print Q._latex_()
1493
N / \left\langle\left(1,\,8,\,0\right)_{N}, \left(0,\,12,\,0\right)_{N}\right\rangle
1494
sage: Ns = N.submodule([N(1,4,0)])
1495
sage: Q = N/Ns
1496
sage: print Q._latex_()
1497
N / \left\langle\left(1,\,4,\,0\right)_{N}\right\rangle
1498
"""
1499
return "%s / %s" % (latex(self.V()), latex(self.W()))
1500
1501
def _repr_(self):
1502
r"""
1503
Return a string representation of ``self``.
1504
1505
OUTPUT:
1506
1507
- string.
1508
1509
TESTS::
1510
1511
sage: N = ToricLattice(3)
1512
sage: Ns = N.submodule([N(2,4,0), N(9,12,0)])
1513
sage: Q = N/Ns
1514
sage: print Q._repr_()
1515
Quotient with torsion of 3-d lattice N
1516
by Sublattice <N(1, 8, 0), N(0, 12, 0)>
1517
sage: Ns = N.submodule([N(1,4,0)])
1518
sage: Q = N/Ns
1519
sage: print Q._repr_()
1520
2-d lattice, quotient of 3-d lattice N
1521
by Sublattice <N(1, 4, 0)>
1522
"""
1523
if self.is_torsion_free():
1524
return "%d-d lattice, quotient of %s by %s" % (self.rank(),
1525
self.V(), self.W())
1526
else:
1527
return "Quotient with torsion of %s by %s" % (self.V(), self.W())
1528
1529
def _module_constructor(self, V, W, check=True):
1530
r"""
1531
Construct new quotient modules.
1532
1533
INPUT:
1534
1535
- ``V`` -- ambient toric lattice;
1536
1537
- ``W`` -- sublattice of ``V``;
1538
1539
- ``check`` -- (default: ``True``) whether to check
1540
correctness of input or not.
1541
1542
TESTS::
1543
1544
sage: N = ToricLattice(3)
1545
sage: Ns = N.submodule([N(2,4,0), N(9,12,0)])
1546
sage: Q = N/Ns; Q
1547
Quotient with torsion of 3-d lattice N by Sublattice <N(1, 8, 0), N(0, 12, 0)>
1548
sage: Q._module_constructor(N,Ns)
1549
Quotient with torsion of 3-d lattice N by Sublattice <N(1, 8, 0), N(0, 12, 0)>
1550
"""
1551
return ToricLattice_quotient(V,W,check)
1552
1553
def base_extend(self, R):
1554
"""
1555
Return the base change of ``self`` to the ring ``R``.
1556
1557
INPUT:
1558
1559
- ``R`` -- either `\ZZ` or `\QQ`.
1560
1561
OUTPUT:
1562
1563
- ``self`` if `R=\ZZ`, quotient of the base extension of the ambient
1564
lattice by the base extension of the sublattice if `R=\QQ`.
1565
1566
EXAMPLES::
1567
1568
sage: N = ToricLattice(3)
1569
sage: Ns = N.submodule([N(2,4,0), N(9,12,0)])
1570
sage: Q = N/Ns
1571
sage: Q.base_extend(ZZ) is Q
1572
True
1573
sage: Q.base_extend(QQ)
1574
Vector space quotient V/W of dimension 1 over Rational Field where
1575
V: Vector space of dimension 3 over Rational Field
1576
W: Vector space of degree 3 and dimension 2 over Rational Field
1577
Basis matrix:
1578
[1 0 0]
1579
[0 1 0]
1580
"""
1581
if R is ZZ:
1582
return self
1583
if R is QQ:
1584
return self.V().base_extend(R) / self.W().base_extend(R)
1585
raise NotImplementedError("quotients of toric lattices can only be "
1586
"extended to ZZ or QQ, not %s!" % R)
1587
1588
def is_torsion_free(self):
1589
r"""
1590
Check if ``self`` is torsion-free.
1591
1592
OUTPUT:
1593
1594
- ``True`` is ``self`` has no torsion and ``False`` otherwise.
1595
1596
EXAMPLES::
1597
1598
sage: N = ToricLattice(3)
1599
sage: Ns = N.submodule([N(2,4,0), N(9,12,0)])
1600
sage: Q = N/Ns
1601
sage: Q.is_torsion_free()
1602
False
1603
sage: Ns = N.submodule([N(1,4,0)])
1604
sage: Q = N/Ns
1605
sage: Q.is_torsion_free()
1606
True
1607
"""
1608
return sum(self.invariants()) == 0
1609
1610
def rank(self):
1611
r"""
1612
Return the rank of ``self``.
1613
1614
OUTPUT:
1615
1616
Integer. The dimension of the free part of the quotient.
1617
1618
EXAMPLES::
1619
1620
sage: N = ToricLattice(3)
1621
sage: Ns = N.submodule([N(2,4,0), N(9,12,0)])
1622
sage: Q = N/Ns
1623
sage: Q.ngens()
1624
2
1625
sage: Q.rank()
1626
1
1627
sage: Ns = N.submodule([N(1,4,0)])
1628
sage: Q = N/Ns
1629
sage: Q.ngens()
1630
2
1631
sage: Q.rank()
1632
2
1633
"""
1634
return self.V().rank() - self.W().rank()
1635
1636
def coordinate_vector(self, x, reduce=False):
1637
"""
1638
Return coordinates of x with respect to the optimized
1639
representation of self.
1640
1641
INPUT:
1642
1643
- ``x`` -- element of ``self`` or convertable to ``self``.
1644
1645
- ``reduce`` -- (default: False); if True, reduce coefficients
1646
modulo invariants.
1647
1648
OUTPUT:
1649
1650
The coordinates as a vector.
1651
1652
EXAMPLES::
1653
1654
sage: N = ToricLattice(3)
1655
sage: Q = N.quotient(N.span([N(1,2,3), N(0,2,1)]), positive_point=N(0,-1,0))
1656
sage: q = Q.gen(0); q
1657
N[0, -1, 0]
1658
sage: q.vector() # indirect test
1659
(1)
1660
sage: Q.coordinate_vector(q)
1661
(1)
1662
"""
1663
coordinates = super(ToricLattice_quotient, self).coordinate_vector(x,reduce)
1664
if self._flip_sign_of_generator:
1665
assert len(coordinates)==1, "Sign flipped for a multi-dimensional quotient!"
1666
return -coordinates
1667
else:
1668
return coordinates
1669
1670
1671
1672
1673