Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/modules/fg_pid/fgp_module.py
4045 views
1
r"""
2
Finitely generated modules over a PID
3
4
You can use Sage to compute with finitely generated modules (FGM's)
5
over a principal ideal domain R presented as a quotient V/W, where V
6
and W are free.
7
8
NOTE: Currently this is only enabled over R=ZZ, since it has not been
9
tested and debugged over more general PIDs. All algorithms make sense
10
whenever there is a Hermite form implementation. In theory the
11
obstruction to extending the implementation is only that one has to
12
decide how elements print. If you're annoyed that by this, fix things
13
and post a patch!
14
15
We represent M=V/W as a pair (V,W) with W contained in V, and we
16
internally represent elements of M non-canonically as elements x of
17
V. We also fix independent generators g[i] for M in V, and when we
18
print out elements of V we print their coordinates with respect to the
19
g[i]; over `\ZZ` this is canonical, since each coefficient is reduce
20
modulo the additive order of g[i]. To obtain the vector in V
21
corresponding to x in M, use x.lift().
22
23
Morphisms between finitely generated R modules are well supported.
24
You create a homomorphism by simply giving the images of generators of
25
M0 in M1. Given a morphism phi:M0-->M1, you can compute the image of
26
phi, the kernel of phi, and using y=phi.lift(x) you can lift an
27
elements x in M1 to an element y in M0, if such a y exists.
28
29
TECHNICAL NOTE: For efficiency, we introduce a notion of optimized
30
representation for quotient modules. The optimized representation of
31
M=V/W is the quotient V'/W' where V' has as basis lifts of the
32
generators g[i] for M. We internally store a morphism from M0=V0/W0
33
to M1=V1/W1 by giving a morphism from the optimized representation V0'
34
of M0 to V1 that sends W0 into W1.
35
36
37
38
The following TUTORIAL illustrates several of the above points.
39
40
First we create free modules V0 and W0 and the quotient module M0.
41
Notice that everything works fine even though V0 and W0 are not
42
contained inside `\ZZ^n`, which is extremely convenient. ::
43
44
sage: V0 = span([[1/2,0,0],[3/2,2,1],[0,0,1]],ZZ); W0 = V0.span([V0.0+2*V0.1, 9*V0.0+2*V0.1, 4*V0.2])
45
sage: M0 = V0/W0; M0
46
Finitely generated module V/W over Integer Ring with invariants (4, 16)
47
48
The invariants are computed using the Smith normal form algorithm, and
49
determine the structure of this finitely generated module.
50
51
You can get the V and W used in constructing the quotient module using
52
V() and W() methods::
53
54
sage: M0.V()
55
Free module of degree 3 and rank 3 over Integer Ring
56
Echelon basis matrix:
57
[1/2 0 0]
58
[ 0 2 0]
59
[ 0 0 1]
60
sage: M0.W()
61
Free module of degree 3 and rank 3 over Integer Ring
62
Echelon basis matrix:
63
[1/2 4 0]
64
[ 0 32 0]
65
[ 0 0 4]
66
67
We note that the optimized representation of M0, mentioned above in
68
the technical note has a V that need not be equal to V0, in general. ::
69
70
sage: M0.optimized()[0].V()
71
Free module of degree 3 and rank 2 over Integer Ring
72
User basis matrix:
73
[0 0 1]
74
[0 2 0]
75
76
Create elements of M0 either by coercing in elements of V0, getting
77
generators, or coercing in a list or tuple or coercing in 0. Coercing
78
in a list or tuple takes the corresponding linear combination of the
79
generators of M0. ::
80
81
sage: M0(V0.0)
82
(0, 14)
83
sage: M0(V0.0 + W0.0) # no difference modulo W0
84
(0, 14)
85
sage: M0([3,20])
86
(3, 4)
87
sage: 3*M0.0 + 20*M0.1
88
(3, 4)
89
90
We make an element of M0 by taking a difference of two generators, and
91
lift it. We also illustrate making an element from a list, which
92
coerces to V0, then take the equivalence class modulo W0. ::
93
94
sage: x = M0.0 - M0.1; x
95
(1, 15)
96
sage: x.lift()
97
(0, -2, 1)
98
sage: M0(vector([1/2,0,0]))
99
(0, 14)
100
sage: x.additive_order()
101
16
102
103
104
Similarly, we construct V1 and W1, and the quotient M1, in a completely different
105
2-dimensional ambient space. ::
106
107
sage: V1 = span([[1/2,0],[3/2,2]],ZZ); W1 = V1.span([2*V1.0, 3*V1.1])
108
sage: M1 = V1/W1; M1
109
Finitely generated module V/W over Integer Ring with invariants (6)
110
111
We create the homomorphism from M0 to M1 that sends both generators of
112
M0 to 3 times the generator of M1. This is well defined since 3 times
113
the generator has order 2. ::
114
115
sage: f = M0.hom([3*M1.0, 3*M1.0]); f
116
Morphism from module over Integer Ring with invariants (4, 16) to module with invariants (6,) that sends the generators to [(3), (3)]
117
118
We evaluate the homomorphism on our element x of the domain, and on the
119
first generator of the domain. We also evaluate at an element of V0,
120
which is coerced into M0. ::
121
122
sage: f(x)
123
(0)
124
sage: f(M0.0)
125
(3)
126
sage: f(V0.1)
127
(3)
128
129
Here we illustrate lifting an element of the image of f, i.e., finding
130
an element of M0 that maps to a given element of M1::
131
132
sage: y = f.lift(3*M1.0); y
133
(0, 13)
134
sage: f(y)
135
(3)
136
137
We compute the kernel of f, i.e., the submodule of elements of M0 that
138
map to 0. Note that the kernel is not explicitly represented as a
139
submodule, but as another quotient V/W where V is contained in V0.
140
You can explicitly coerce elements of the kernel into M0 though. ::
141
142
sage: K = f.kernel(); K
143
Finitely generated module V/W over Integer Ring with invariants (2, 16)
144
145
sage: M0(K.0)
146
(2, 0)
147
sage: M0(K.1)
148
(3, 1)
149
sage: f(M0(K.0))
150
(0)
151
sage: f(M0(K.1))
152
(0)
153
154
We compute the image of f. ::
155
156
sage: f.image()
157
Finitely generated module V/W over Integer Ring with invariants (2)
158
159
Notice how the elements of the image are written as (0) and (1),
160
despite the image being naturally a submodule of M1, which has
161
elements (0), (1), (2), (3), (4), (5). However, below we coerce the
162
element (1) of the image into the codomain, and get (3)::
163
164
sage: list(f.image())
165
[(0), (1)]
166
sage: list(M1)
167
[(0), (1), (2), (3), (4), (5)]
168
sage: x = f.image().0; x
169
(1)
170
sage: M1(x)
171
(3)
172
173
174
TESTS::
175
176
sage: from sage.modules.fg_pid.fgp_module import FGP_Module
177
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ)
178
sage: W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
179
sage: Q = FGP_Module(V, W); Q
180
Finitely generated module V/W over Integer Ring with invariants (4, 12)
181
sage: Q([1,3])
182
(1, 3)
183
sage: Q(V([1,3,4]))
184
(0, 11)
185
sage: Q(W([1,16,0]))
186
(0, 0)
187
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],QQ)
188
sage: W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1])
189
sage: Q = FGP_Module(V, W); Q
190
Finitely generated module V/W over Rational Field with invariants (0)
191
sage: q = Q.an_element(); q
192
(1)
193
sage: q*(1/2)
194
(1/2)
195
sage: (1/2)*q
196
(1/2)
197
198
AUTHOR:
199
200
- William Stein, 2009
201
"""
202
203
####################################################################################
204
# Copyright (C) 2009 William Stein <[email protected]>
205
#
206
# Distributed under the terms of the GNU General Public License (GPL)
207
#
208
# This code is distributed in the hope that it will be useful,
209
# but WITHOUT ANY WARRANTY; without even the implied warranty of
210
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
211
# General Public License for more details.
212
#
213
# The full text of the GPL is available at:
214
#
215
# http://www.gnu.org/licenses/
216
####################################################################################
217
218
from sage.modules.module import Module
219
from sage.modules.free_module import is_FreeModule
220
from sage.structure.sequence import Sequence
221
from fgp_element import DEBUG, FGP_Element
222
from fgp_morphism import FGP_Morphism, FGP_Homset
223
from sage.rings.all import Integer, ZZ, lcm
224
from sage.misc.cachefunc import cached_method
225
226
import weakref
227
_fgp_module = weakref.WeakValueDictionary()
228
229
230
231
232
def FGP_Module(V, W, check=True):
233
"""
234
INPUT:
235
236
- ``V`` -- a free R-module
237
238
- ``W`` -- a free R-submodule of ``V``
239
240
- ``check`` -- bool (default: ``True``); if ``True``, more checks
241
on correctness are performed; in particular, we check the data
242
types of ``V`` and ``W``, and that ``W`` is a submodule of ``V``
243
with the same base ring.
244
245
OUTPUT:
246
247
- the quotient ``V/W`` as a finitely generated R-module
248
249
250
EXAMPLES::
251
252
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
253
sage: import sage.modules.fg_pid.fgp_module
254
sage: Q = sage.modules.fg_pid.fgp_module.FGP_Module(V, W)
255
sage: type(Q)
256
<class 'sage.modules.fg_pid.fgp_module.FGP_Module_class_with_category'>
257
sage: Q is sage.modules.fg_pid.fgp_module.FGP_Module(V, W, check=False)
258
True
259
"""
260
key = (V,V.basis_matrix(),W,W.basis_matrix())
261
try:
262
return _fgp_module[key]
263
except KeyError: pass
264
M = FGP_Module_class(V, W, check=check)
265
_fgp_module[key] = M
266
return M
267
268
269
def is_FGP_Module(x):
270
"""
271
Return true of x is an FGP module, i.e., a finitely generated
272
module over a PID represented as a quotient of finitely generated
273
free modules over a PID.
274
275
EXAMPLES::
276
277
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2]); Q = V/W
278
sage: sage.modules.fg_pid.fgp_module.is_FGP_Module(V)
279
False
280
sage: sage.modules.fg_pid.fgp_module.is_FGP_Module(Q)
281
True
282
"""
283
return isinstance(x, FGP_Module_class)
284
285
286
class FGP_Module_class(Module):
287
"""
288
A finitely generated module over a PID presented as a quotient V/W.
289
290
INPUT:
291
292
- ``V`` -- an R-module
293
294
- ``W`` -- an R-submodule of V
295
296
- ``check`` -- bool (default: True)
297
298
EXAMPLES::
299
300
sage: A = (ZZ^1)/span([[100]], ZZ); A
301
Finitely generated module V/W over Integer Ring with invariants (100)
302
sage: A.V()
303
Ambient free module of rank 1 over the principal ideal domain Integer Ring
304
sage: A.W()
305
Free module of degree 1 and rank 1 over Integer Ring
306
Echelon basis matrix:
307
[100]
308
309
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
310
sage: Q = V/W; Q
311
Finitely generated module V/W over Integer Ring with invariants (4, 12)
312
sage: type(Q)
313
<class 'sage.modules.fg_pid.fgp_module.FGP_Module_class_with_category'>
314
"""
315
316
# The class to be used for creating elements of this
317
# module. Should be overridden in derived classes.
318
Element = FGP_Element
319
320
def __init__(self, V, W, check=True):
321
"""
322
INPUT:
323
324
- ``V`` -- an R-module
325
326
- ``W`` -- an R-submodule of V
327
328
- ``check`` -- bool (default: True); if True, more checks on
329
correctness are performed; in particular, we check
330
the data types of V and W, and that W is a
331
submodule of V with the same base ring.
332
333
EXAMPLES::
334
335
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
336
sage: Q = V/W; Q
337
Finitely generated module V/W over Integer Ring with invariants (4, 12)
338
sage: type(Q)
339
<class 'sage.modules.fg_pid.fgp_module.FGP_Module_class_with_category'>
340
"""
341
if check:
342
if not is_FreeModule(V):
343
raise TypeError, "V must be a FreeModule"
344
if not is_FreeModule(W):
345
raise TypeError, "W must be a FreeModule"
346
if not W.is_submodule(V):
347
raise ValueError, "W must be a submodule of V"
348
if V.base_ring() != W.base_ring():
349
raise ValueError, "W and V must have the same base ring"
350
self._W = W
351
self._V = V
352
Module.__init__(self, base=V.base_ring())
353
354
# Note: There once was a
355
# def _subquotient_class():
356
# method that returned a functionoid to construct new modules, so
357
# you would call module._subquotient_class()(V,W,check). This has
358
# been replaced with module._module_constructor(V,W,check).
359
360
def _module_constructor(self, V, W, check=True):
361
r"""
362
Construct a quotient module ``V/W``.
363
364
This should be overridden in derived classes.
365
366
INPUT:
367
368
- ``V`` -- an R-module.
369
370
- ``W`` -- an R-submodule of ``V``.
371
372
- ``check`` -- bool (default: True).
373
374
OUTPUT:
375
376
The quotient ``V/W``.
377
378
EXAMPLES::
379
380
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
381
sage: Q = V/W; Q
382
Finitely generated module V/W over Integer Ring with invariants (4, 12)
383
sage: Q._module_constructor(V,W)
384
Finitely generated module V/W over Integer Ring with invariants (4, 12)
385
"""
386
return FGP_Module(V,W,check)
387
388
def _coerce_map_from_(self, S):
389
"""
390
Return whether ``S`` canonically coerces to ``self``a.
391
392
INPUT:
393
394
- ``S`` -- anything.
395
396
OUTPUT:
397
398
Boolean.
399
400
EXAMPLES::
401
402
sage: V = span([[5, -1/2]],ZZ); W = span([[20,-2]],ZZ); Q = V/W; phi=Q.hom([2*Q.0])
403
sage: Q._coerce_map_from_(ZZ)
404
False
405
sage: Q._coerce_map_from_(phi.kernel())
406
True
407
sage: Q._coerce_map_from_(Q)
408
True
409
sage: Q._coerce_map_from_(phi.image())
410
True
411
sage: Q._coerce_map_from_(V/V.zero_submodule())
412
True
413
sage: Q._coerce_map_from_(V/V)
414
False
415
sage: Q._coerce_map_from_(ZZ^2)
416
False
417
418
Of course, `V` canonically coerces to `Q`, as does twice `V`:
419
420
sage: Q._coerce_map_from_(V)
421
True
422
sage: Q._coerce_map_from_(V.scale(2))
423
True
424
"""
425
if is_FGP_Module(S):
426
return S.has_canonical_map_to(self)
427
return bool(self._V._coerce_map_from_(S))
428
429
def _repr_(self):
430
"""
431
Return string representation of this module.
432
433
EXAMPLES::
434
435
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
436
sage: (V/W)._repr_()
437
'Finitely generated module V/W over Integer Ring with invariants (4, 12)'
438
"""
439
I = str(self.invariants()).replace(',)',')')
440
return "Finitely generated module V/W over %s with invariants %s"%(self.base_ring(), I)
441
442
def __div__(self, other):
443
"""
444
Return the quotient self/other, where other must be a
445
submodule of self.
446
447
EXAMPLES::
448
449
sage: V = span([[5, -1/2]],ZZ); W = span([[20,-2]],ZZ); Q = V/W; phi=Q.hom([2*Q.0])
450
sage: Q
451
Finitely generated module V/W over Integer Ring with invariants (4)
452
sage: Q/phi.kernel()
453
Finitely generated module V/W over Integer Ring with invariants (2)
454
sage: Q/Q
455
Finitely generated module V/W over Integer Ring with invariants ()
456
"""
457
if not is_FGP_Module(other):
458
if is_FreeModule(other):
459
other = other / other.zero_submodule()
460
else:
461
raise TypeError, "other must be an FGP module"
462
if not other.is_submodule(self):
463
raise ValueError, "other must be a submodule of self"
464
return self._module_constructor(self._V, other._V+self._W)
465
466
def __eq__(self, other):
467
"""
468
EXAMPLES::
469
470
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
471
sage: Q = V/W
472
sage: Q == Q
473
True
474
sage: loads(dumps(Q)) == Q
475
True
476
sage: Q == V
477
False
478
sage: Q == V/V.zero_submodule()
479
False
480
"""
481
if not is_FGP_Module(other):
482
return False
483
return self._V == other._V and self._W == other._W
484
485
def __ne__(self, other):
486
"""
487
True iff self is not equal to other.
488
489
This may not be needed for modules created using the function
490
:func:`FGP_Module`, since those have uniqueness built into
491
them, but if the modules are created directly using the
492
__init__ method for this class, then this may fail; in
493
particular, for modules M and N with ``M == N`` returning
494
True, it may be the case that ``M != N`` may also return True.
495
In particular, for derived classes whose __init__ methods just
496
call the __init__ method for this class need this. See
497
http://trac.sagemath.org/sage_trac/ticket/9940 for
498
illustrations.
499
500
EXAMPLES:
501
502
Make sure that the problems in
503
http://trac.sagemath.org/sage_trac/ticket/9940 are fixed::
504
505
sage: G = AdditiveAbelianGroup([0,0])
506
sage: H = AdditiveAbelianGroup([0,0])
507
sage: G == H
508
True
509
sage: G != H # indirect doctest
510
False
511
512
sage: N1 = ToricLattice(3)
513
sage: sublattice1 = N1.submodule([(1,1,0), (3,2,1)])
514
sage: Q1 = N1/sublattice1
515
sage: N2 = ToricLattice(3)
516
sage: sublattice2 = N2.submodule([(1,1,0), (3,2,1)])
517
sage: Q2 = N2/sublattice2
518
sage: Q1 == Q2
519
True
520
sage: Q1 != Q2
521
False
522
"""
523
return not self.__eq__(other)
524
525
# __le__ is a synonym for `is_submodule`: see below
526
527
def __lt__(self, other):
528
"""
529
True iff self is a proper submodule of other.
530
531
EXAMPLES::
532
533
sage: V = ZZ^2; W = V.span([[1,2]]); W2 = W.scale(2)
534
sage: A = V/W; B = W/W2
535
sage: B < A
536
False
537
sage: A = V/W2; B = W/W2
538
sage: B < A
539
True
540
sage: A < A
541
False
542
"""
543
return self.__le__(other) and not self.__eq__(other)
544
545
def __gt__(self, other):
546
"""
547
True iff other is a proper submodule of self.
548
549
EXAMPLES::
550
551
sage: V = ZZ^2; W = V.span([[1,2]]); W2 = W.scale(2)
552
sage: A = V/W; B = W/W2
553
sage: A > B
554
False
555
sage: A = V/W2; B = W/W2
556
sage: A > B
557
True
558
sage: A > A
559
False
560
"""
561
return self.__ge__(other) and not self.__eq__(other)
562
563
def __ge__(self, other):
564
"""
565
True iff other is a submodule of self.
566
567
EXAMPLES::
568
569
sage: V = ZZ^2; W = V.span([[1,2]]); W2 = W.scale(2)
570
sage: A = V/W; B = W/W2
571
sage: A >= B
572
False
573
sage: A = V/W2; B = W/W2
574
sage: A >= B
575
True
576
sage: A >= A
577
True
578
"""
579
return other.is_submodule(self)
580
581
582
def _element_constructor_(self, x, check=True):
583
"""
584
INPUT:
585
586
- ``x`` -- a vector, an fgp module element, or a list or tuple:
587
588
- list or tuple: take the corresponding linear combination of
589
the generators of self.
590
591
- vector: coerce vector into V and define the
592
corresponding element of V/W
593
594
- fgp module element: lift to element of ambient vector
595
space and try to put into V. If x is in self already,
596
just return x.
597
598
- `check` -- bool (default: True)
599
600
EXAMPLES::
601
602
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
603
sage: Q = V/W
604
sage: x = Q(V.0-V.1); x # indirect doctest
605
(0, 3)
606
sage: type(x)
607
<class 'sage.modules.fg_pid.fgp_element.FGP_Module_class_with_category.element_class'>
608
sage: x is Q(x)
609
True
610
sage: x.parent() is Q
611
True
612
"""
613
# print '_element_constructor_', x, check
614
if isinstance(x, (list,tuple)):
615
try:
616
x = self.optimized()[0].V().linear_combination_of_basis(x)
617
except ValueError, msg:
618
raise TypeError, msg
619
elif isinstance(x, FGP_Element):
620
x = x.lift()
621
return self.element_class(self, self._V(x))
622
623
624
def linear_combination_of_smith_form_gens(self, x):
625
r"""
626
Compute a linear combination of the optimised generators of this module
627
as returned by :meth:`.smith_form_gens`.
628
629
EXAMPLE::
630
631
sage: X = ZZ**2 / span([[3,0],[0,2]], ZZ)
632
sage: X.linear_combination_of_smith_form_gens([1])
633
(1)
634
635
"""
636
try:
637
x = self.optimized()[0].V().linear_combination_of_basis(x)
638
except ValueError, msg:
639
raise TypeError, msg
640
return self.element_class(self, self._V(x))
641
642
def __contains__(self, x):
643
"""
644
Return true if x is contained in self.
645
646
EXAMPLES::
647
648
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
649
sage: Q = V/W; Q
650
Finitely generated module V/W over Integer Ring with invariants (4, 12)
651
sage: Q.0 in Q
652
True
653
sage: 0 in Q
654
True
655
sage: vector([1,2,3/7]) in Q
656
False
657
sage: vector([1,2,3]) in Q
658
True
659
sage: Q.0 - Q.1 in Q
660
True
661
"""
662
if hasattr(x, 'parent') and x.parent() == self:
663
return True
664
try:
665
self(x)
666
return True
667
except TypeError:
668
return False
669
670
def submodule(self, x):
671
"""
672
Return the submodule defined by x.
673
674
INPUT:
675
676
- ``x`` -- list, tuple, or FGP module
677
678
EXAMPLES::
679
680
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
681
sage: Q = V/W; Q
682
Finitely generated module V/W over Integer Ring with invariants (4, 12)
683
sage: Q.gens()
684
((1, 0), (0, 1))
685
686
We create submodules generated by a list or tuple of elements::
687
688
sage: Q.submodule([Q.0])
689
Finitely generated module V/W over Integer Ring with invariants (4)
690
sage: Q.submodule([Q.1])
691
Finitely generated module V/W over Integer Ring with invariants (12)
692
sage: Q.submodule((Q.0, Q.0 + 3*Q.1))
693
Finitely generated module V/W over Integer Ring with invariants (4, 4)
694
695
A submodule defined by a submodule::
696
697
sage: A = Q.submodule((Q.0, Q.0 + 3*Q.1)); A
698
Finitely generated module V/W over Integer Ring with invariants (4, 4)
699
sage: Q.submodule(A)
700
Finitely generated module V/W over Integer Ring with invariants (4, 4)
701
702
Inclusion is checked::
703
704
sage: A.submodule(Q)
705
Traceback (most recent call last):
706
...
707
ValueError: x.V() must be contained in self's V.
708
"""
709
if is_FGP_Module(x):
710
if not x._W.is_submodule(self._W):
711
raise ValueError, "x.W() must be contained in self's W."
712
713
V = x._V
714
if not V.is_submodule(self._V):
715
raise ValueError, "x.V() must be contained in self's V."
716
717
return x
718
719
if not isinstance(x, (list, tuple)):
720
raise TypeError, "x must be a list, tuple, or FGP module"
721
722
x = Sequence(x)
723
if is_FGP_Module(x.universe()):
724
# TODO: possibly inefficient in some cases
725
x = [self(v).lift() for v in x]
726
V = self._V.submodule(x) + self._W
727
return self._module_constructor(V, self._W)
728
729
def has_canonical_map_to(self, A):
730
"""
731
Return True if self has a canonical map to A, relative to the
732
given presentation of A. This means that A is a finitely
733
generated quotient module, self.V() is a submodule of A.V()
734
and self.W() is a submodule of A.W(), i.e., that there is a
735
natural map induced by inclusion of the V's. Note that we do
736
*not* require that this natural map be injective; for this use
737
:meth:`is_submodule`.
738
739
EXAMPLES::
740
741
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
742
sage: Q = V/W; Q
743
Finitely generated module V/W over Integer Ring with invariants (4, 12)
744
sage: A = Q.submodule((Q.0, Q.0 + 3*Q.1)); A
745
Finitely generated module V/W over Integer Ring with invariants (4, 4)
746
sage: A.has_canonical_map_to(Q)
747
True
748
sage: Q.has_canonical_map_to(A)
749
False
750
751
"""
752
if not is_FGP_Module(A):
753
return False
754
if self.cardinality() == 0 and self.base_ring() == A.base_ring():
755
return True
756
return self.V().is_submodule(A.V()) and self.W().is_submodule(A.W())
757
758
def is_submodule(self, A):
759
"""
760
Return True if self is a submodule of A. More precisely, this returns True if
761
if ``self.V()`` is a submodule of ``A.V()``, with ``self.W()`` equal to ``A.W()``.
762
763
Compare :meth:`.has_canonical_map_to`.
764
765
EXAMPLES::
766
767
sage: V = ZZ^2; W = V.span([[1,2]]); W2 = W.scale(2)
768
sage: A = V/W; B = W/W2
769
sage: B.is_submodule(A)
770
False
771
sage: A = V/W2; B = W/W2
772
sage: B.is_submodule(A)
773
True
774
775
This example illustrates that this command works in a subtle cases.::
776
777
sage: A = ZZ^1
778
sage: Q3 = A / A.span([[3]])
779
sage: Q6 = A / A.span([[6]])
780
sage: Q6.is_submodule(Q3)
781
False
782
sage: Q6.has_canonical_map_to(Q3)
783
True
784
sage: Q = A.span([[2]]) / A.span([[6]])
785
sage: Q.is_submodule(Q6)
786
True
787
"""
788
if not self.has_canonical_map_to(A):
789
return False
790
791
return self.V().is_submodule(A.V()) and self.W() == A.W()
792
793
__le__ = is_submodule
794
795
def V(self):
796
"""
797
If this module was constructed as a quotient V/W, returns V.
798
799
EXAMPLES::
800
801
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
802
sage: Q = V/W
803
sage: Q.V()
804
Free module of degree 3 and rank 3 over Integer Ring
805
Echelon basis matrix:
806
[1/2 0 0]
807
[ 0 1 0]
808
[ 0 0 1]
809
810
"""
811
return self._V
812
813
def cover(self):
814
"""
815
If this module was constructed as V/W, returns the cover module V. This is the same as self.V().
816
817
EXAMPLES::
818
819
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
820
sage: Q = V/W
821
sage: Q.V()
822
Free module of degree 3 and rank 3 over Integer Ring
823
Echelon basis matrix:
824
[1/2 0 0]
825
[ 0 1 0]
826
[ 0 0 1]
827
"""
828
return self.V()
829
830
def W(self):
831
"""
832
If this module was constructed as a quotient V/W, returns W.
833
834
EXAMPLES::
835
836
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
837
sage: Q = V/W
838
sage: Q.W()
839
Free module of degree 3 and rank 3 over Integer Ring
840
Echelon basis matrix:
841
[1/2 8 0]
842
[ 0 12 0]
843
[ 0 0 4]
844
"""
845
return self._W
846
847
def relations(self):
848
"""
849
If this module was constructed as V/W, returns the relations module V. This is the same as self.W().
850
851
EXAMPLES::
852
853
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
854
sage: Q = V/W
855
sage: Q.relations()
856
Free module of degree 3 and rank 3 over Integer Ring
857
Echelon basis matrix:
858
[1/2 8 0]
859
[ 0 12 0]
860
[ 0 0 4]
861
"""
862
return self.W()
863
864
@cached_method
865
def _relative_matrix(self):
866
"""
867
V has a fixed choice of basis, and W has a fixed choice of
868
basis, and both V and W are free R-modules. Since W is
869
contained in V, we can write each basis element of W as an
870
R-linear combination of the basis for V. This function
871
returns that matrix over R, where each row corresponds to a
872
basis element of W.
873
874
EXAMPLES::
875
876
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
877
sage: Q = V/W
878
sage: Q._relative_matrix()
879
[ 1 8 0]
880
[ 0 12 0]
881
[ 0 0 4]
882
"""
883
V = self._V
884
W = self._W
885
A = V.coordinate_module(W).basis_matrix().change_ring(self.base_ring())
886
return A
887
888
@cached_method
889
def _smith_form(self):
890
"""
891
Return matrices S, U, and V such that S = U*R*V, and S is in
892
Smith normal form, and R is the relative matrix that defines
893
self (see :meth:`._relative_matrix`).
894
895
EXAMPLES::
896
897
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
898
sage: Q = V/W
899
sage: Q._smith_form()
900
(
901
[ 1 0 0] [1 0 0] [ 1 0 -8]
902
[ 0 4 0] [0 0 1] [ 0 0 1]
903
[ 0 0 12], [0 1 0], [ 0 1 0]
904
)
905
"""
906
return self._relative_matrix().smith_form()
907
908
def base_ring(self):
909
"""
910
EXAMPLES::
911
912
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
913
sage: Q = V/W
914
sage: Q.base_ring()
915
Integer Ring
916
"""
917
return self._V.base_ring()
918
919
@cached_method
920
def invariants(self, include_ones=False):
921
"""
922
Return the diagonal entries of the smith form of the relative
923
matrix that defines self (see :meth:`._relative_matrix`)
924
padded with zeros, excluding 1's by default. Thus if v is the
925
list of integers returned, then self is abstractly isomorphic to
926
the product of cyclic groups `Z/nZ` where `n` is in `v`.
927
928
INPUT:
929
930
- ``include_ones`` -- bool (default: False); if True, also
931
include 1's in the output list.
932
933
EXAMPLES::
934
935
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
936
sage: Q = V/W
937
sage: Q.invariants()
938
(4, 12)
939
940
An example with 1 and 0 rows::
941
942
sage: V = ZZ^3; W = V.span([[1,2,0],[0,1,0], [0,2,0]]); Q = V/W; Q
943
Finitely generated module V/W over Integer Ring with invariants (0)
944
sage: Q.invariants()
945
(0,)
946
sage: Q.invariants(include_ones=True)
947
(1, 1, 0)
948
949
"""
950
D,_,_ = self._smith_form()
951
952
v = [D[i,i] for i in range(D.nrows())] + [Integer(0)]*(D.ncols()-D.nrows())
953
w = tuple([x for x in v if x != 1])
954
v = tuple(v)
955
self.invariants.set_cache(v, True)
956
self.invariants.set_cache(w, False)
957
return self.invariants(include_ones)
958
959
def gens(self):
960
"""
961
Returns tuple of elements `g_0,...,g_n` of self such that the module generated by
962
the gi is isomorphic to the direct sum of R/ei*R, where ei are the
963
invariants of self and R is the base ring.
964
965
Note that these are not generally uniquely determined, and depending on
966
how Smith normal form is implemented for the base ring, they may not
967
even be deterministic.
968
969
This can safely be overridden in all derived classes.
970
971
EXAMPLES::
972
973
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
974
sage: Q = V/W
975
sage: Q.gens()
976
((1, 0), (0, 1))
977
sage: Q.0
978
(1, 0)
979
"""
980
return self.smith_form_gens()
981
982
@cached_method
983
def smith_form_gens(self):
984
"""
985
Return a set of generators for self which are in Smith normal form.
986
987
EXAMPLES::
988
989
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
990
sage: Q = V/W
991
sage: Q.smith_form_gens()
992
((1, 0), (0, 1))
993
sage: [x.lift() for x in Q.smith_form_gens()]
994
[(0, 0, 1), (0, 1, 0)]
995
"""
996
# Get the rightmost transformation in the Smith form
997
_, _, X = self._smith_form()
998
# Invert it to get a matrix whose rows (in terms of the basis for V)
999
# are the gi (including 1 invariants).
1000
Y = X**(-1)
1001
# Get the basis matrix for V
1002
B = self._V.basis_matrix()
1003
# Multiply to express the gi in terms of the ambient vector space.
1004
Z = Y*B
1005
# Make gens out of the rows of Z that correspond to non-1 invariants.
1006
v = self.invariants(include_ones=True)
1007
non1 = [i for i in range(Z.nrows()) if v[i] != 1]
1008
Z = Z.matrix_from_rows(non1)
1009
self._gens = tuple([self(z, check=DEBUG) for z in Z.rows()])
1010
return self._gens
1011
1012
def coordinate_vector(self, x, reduce=False):
1013
"""
1014
Return coordinates of x with respect to the optimized
1015
representation of self.
1016
1017
INPUT:
1018
1019
- ``x`` -- element of self
1020
1021
- ``reduce`` -- (default: False); if True, reduce
1022
coefficients modulo invariants; this is
1023
ignored if the base ring isn't ZZ.
1024
1025
OUTPUT:
1026
1027
The coordinates as a vector. That is, the same type as
1028
``self.V()``, but in general with fewer entries.
1029
1030
EXAMPLES::
1031
1032
sage: V = span([[1/4,0,0],[3/4,4,2],[0,0,2]],ZZ); W = V.span([4*V.0+12*V.1])
1033
sage: Q = V/W; Q
1034
Finitely generated module V/W over Integer Ring with invariants (4, 0, 0)
1035
sage: Q.coordinate_vector(-Q.0)
1036
(-1, 0, 0)
1037
sage: Q.coordinate_vector(-Q.0, reduce=True)
1038
(3, 0, 0)
1039
1040
If x isn't in self, it is coerced in::
1041
1042
sage: Q.coordinate_vector(V.0)
1043
(1, 0, -3)
1044
sage: Q.coordinate_vector(Q(V.0))
1045
(1, 0, -3)
1046
1047
1048
TESTS::
1049
1050
sage: V = span([[1/2,0,0],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
1051
sage: Q = V/W; Q
1052
Finitely generated module V/W over Integer Ring with invariants (4, 12)
1053
sage: Q.coordinate_vector(Q.0 - Q.1)
1054
(1, -1)
1055
1056
sage: O, X = Q.optimized()
1057
sage: O.V()
1058
Free module of degree 3 and rank 2 over Integer Ring
1059
User basis matrix:
1060
[0 0 1]
1061
[0 2 0]
1062
sage: phi = Q.hom([Q.0, 4*Q.1])
1063
sage: x = Q(V.0); x
1064
(0, 4)
1065
sage: Q.coordinate_vector(x, reduce=True)
1066
(0, 4)
1067
sage: Q.coordinate_vector(-x, reduce=False)
1068
(0, -4)
1069
sage: x == 4*Q.1
1070
True
1071
sage: x = Q(V.1); x
1072
(0, 1)
1073
sage: Q.coordinate_vector(x)
1074
(0, 1)
1075
sage: x == Q.1
1076
True
1077
sage: x = Q(V.2); x
1078
(1, 0)
1079
sage: Q.coordinate_vector(x)
1080
(1, 0)
1081
sage: x == Q.0
1082
True
1083
"""
1084
try: T = self.__T
1085
except AttributeError:
1086
self.optimized() # computes T as side effect -- see the "optimized" method.
1087
T = self.__T
1088
1089
x = self(x)
1090
1091
c = self._V.coordinate_vector(x.lift())
1092
b = (c*T).change_ring(self.base_ring())
1093
if reduce and self.base_ring() == ZZ:
1094
1095
I = self.invariants()
1096
return b.parent()([b[i] if I[i] == 0 else b[i] % I[i] for i in range(len(I))])
1097
1098
else:
1099
# Don't know (or not requested) canonical way to reduce
1100
# each entry yet, or how to compute invariants.
1101
return b
1102
1103
def gen(self, i):
1104
"""
1105
Return the i-th generator of self.
1106
1107
INPUT:
1108
1109
- ``i`` -- integer
1110
1111
EXAMPLES::
1112
1113
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
1114
sage: Q = V/W; Q
1115
Finitely generated module V/W over Integer Ring with invariants (4, 12)
1116
sage: Q.gen(0)
1117
(1, 0)
1118
sage: Q.gen(1)
1119
(0, 1)
1120
sage: Q.gen(2)
1121
Traceback (most recent call last):
1122
...
1123
ValueError: Generator 2 not defined
1124
sage: Q.gen(-1)
1125
Traceback (most recent call last):
1126
...
1127
ValueError: Generator -1 not defined
1128
"""
1129
v = self.gens()
1130
if i < 0 or i >= len(v):
1131
raise ValueError, "Generator %s not defined"%i
1132
return v[i]
1133
1134
def smith_form_gen(self, i):
1135
"""
1136
Return the i-th generator of self. A private name (so we can freely
1137
override gen() in derived classes).
1138
1139
INPUT:
1140
1141
- ``i`` -- integer
1142
1143
EXAMPLES::
1144
1145
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
1146
sage: Q = V/W; Q
1147
Finitely generated module V/W over Integer Ring with invariants (4, 12)
1148
sage: Q.smith_form_gen(0)
1149
(1, 0)
1150
sage: Q.smith_form_gen(1)
1151
(0, 1)
1152
"""
1153
v = self.smith_form_gens()
1154
if i < 0 or i >= len(v):
1155
raise ValueError, "Smith form generator %s not defined"%i
1156
return v[i]
1157
1158
def optimized(self):
1159
"""
1160
Return a module isomorphic to this one, but with V replaced by
1161
a submodule of V such that the generators of self all lift
1162
trivially to generators of V. Replace W by the intersection
1163
of V and W. This has the advantage that V has small dimension
1164
and any homomorphism from self trivially extends to a
1165
homomorphism from V.
1166
1167
OUTPUT:
1168
1169
- ``Q`` -- an optimized quotient V0/W0 with V0 a submodule of V
1170
such that phi: V0/W0 --> V/W is an isomorphism
1171
1172
- ``Z`` -- matrix such that if x is in self.V() and
1173
c gives the coordinates of x in terms of the
1174
basis for self.V(), then c*Z is in V0
1175
and c*Z maps to x via phi above.
1176
1177
EXAMPLES::
1178
1179
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
1180
sage: Q = V/W
1181
sage: O, X = Q.optimized(); O
1182
Finitely generated module V/W over Integer Ring with invariants (4, 12)
1183
sage: O.V()
1184
Free module of degree 3 and rank 2 over Integer Ring
1185
User basis matrix:
1186
[0 0 1]
1187
[0 1 0]
1188
sage: O.W()
1189
Free module of degree 3 and rank 2 over Integer Ring
1190
Echelon basis matrix:
1191
[ 0 12 0]
1192
[ 0 0 4]
1193
sage: X
1194
[0 4 0]
1195
[0 1 0]
1196
[0 0 1]
1197
sage: OV = O.V()
1198
sage: Q(OV([0,-8,0])) == V.0
1199
True
1200
sage: Q(OV([0,1,0])) == V.1
1201
True
1202
sage: Q(OV([0,0,1])) == V.2
1203
True
1204
"""
1205
try:
1206
if self.__optimized is True:
1207
return self, None
1208
return self.__optimized
1209
except AttributeError: pass
1210
V = self._V.span_of_basis([x.lift() for x in self.smith_form_gens()])
1211
M = self._module_constructor(V, self._W.intersection(V))
1212
# Compute matrix T of linear transformation from self._V to V.
1213
# This matrix T gives each basis element of self._V in terms
1214
# of our new optimized V, modulo the W's.
1215
A = V.basis_matrix().stack(self._W.basis_matrix())
1216
B, d = A._clear_denom()
1217
H, U = B.hermite_form(transformation=True)
1218
Y = H.solve_left(d*self._V.basis_matrix())
1219
T = Y * U.matrix_from_columns(range(V.rank()))
1220
self.__T = T
1221
1222
# Finally we multiply by V.basis_matrix() so X gives vectors
1223
# in the ambient space instead of coefficients of linear
1224
# combinations of the basis for V.
1225
X = T * V.basis_matrix()
1226
1227
self.__optimized = M, X
1228
return M, X
1229
1230
1231
def hom(self, im_gens, codomain=None, check=True):
1232
"""
1233
Homomorphism defined by giving the images of ``self.gens()`` in some
1234
fixed fg R-module.
1235
1236
.. note ::
1237
1238
We do not assume that the generators given by ``self.gens()`` are
1239
the same as the Smith form generators, since this may not be true
1240
for a general derived class.
1241
1242
INPUTS:
1243
1244
- ``im_gens`` -- a list of the images of ``self.gens()`` in some
1245
R-module
1246
1247
1248
EXAMPLES::
1249
1250
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
1251
sage: Q = V/W
1252
sage: phi = Q.hom([3*Q.1, Q.0])
1253
sage: phi
1254
Morphism from module over Integer Ring with invariants (4, 12) to module with invariants (4, 12) that sends the generators to [(0, 3), (1, 0)]
1255
sage: phi(Q.0)
1256
(0, 3)
1257
sage: phi(Q.1)
1258
(1, 0)
1259
sage: Q.0 == phi(Q.1)
1260
True
1261
1262
This example illustrates creating a morphism to a free module.
1263
The free module is turned into an FGP module (i.e., quotient
1264
V/W with W=0), and the morphism is constructed::
1265
1266
sage: V = span([[1/2,0,0],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1])
1267
sage: Q = V/W; Q
1268
Finitely generated module V/W over Integer Ring with invariants (2, 0, 0)
1269
sage: phi = Q.hom([0,V.0,V.1]); phi
1270
Morphism from module over Integer Ring with invariants (2, 0, 0) to module with invariants (0, 0, 0) that sends the generators to [(0, 0, 0), (1, 0, 0), (0, 1, 0)]
1271
sage: phi.domain()
1272
Finitely generated module V/W over Integer Ring with invariants (2, 0, 0)
1273
sage: phi.codomain()
1274
Finitely generated module V/W over Integer Ring with invariants (0, 0, 0)
1275
sage: phi(Q.0)
1276
(0, 0, 0)
1277
sage: phi(Q.1)
1278
(1, 0, 0)
1279
sage: phi(Q.2) == V.1
1280
True
1281
1282
Constructing two zero maps from the zero module::
1283
1284
sage: A = (ZZ^2)/(ZZ^2); A
1285
Finitely generated module V/W over Integer Ring with invariants ()
1286
sage: A.hom([])
1287
Morphism from module over Integer Ring with invariants () to module with invariants () that sends the generators to []
1288
sage: A.hom([]).codomain() is A
1289
True
1290
sage: B = (ZZ^3)/(ZZ^3)
1291
sage: A.hom([],codomain=B)
1292
Morphism from module over Integer Ring with invariants () to module with invariants () that sends the generators to []
1293
sage: phi = A.hom([],codomain=B); phi
1294
Morphism from module over Integer Ring with invariants () to module with invariants () that sends the generators to []
1295
sage: phi(A(0))
1296
()
1297
sage: phi(A(0)) == B(0)
1298
True
1299
1300
1301
A degenerate case::
1302
1303
sage: A = (ZZ^2)/(ZZ^2)
1304
sage: phi = A.hom([]); phi
1305
Morphism from module over Integer Ring with invariants () to module with invariants () that sends the generators to []
1306
sage: phi(A(0))
1307
()
1308
1309
The code checks that the morphism is valid. In the example
1310
below we try to send a generator of order 2 to an element of
1311
order 14::
1312
1313
sage: V = span([[1/14,3/14],[0,1/2]],ZZ); W = ZZ^2
1314
sage: Q = V/W; Q
1315
Finitely generated module V/W over Integer Ring with invariants (2, 14)
1316
sage: Q([1,11]).additive_order()
1317
14
1318
sage: f = Q.hom([Q([1,11]), Q([1,3])]); f
1319
Traceback (most recent call last):
1320
...
1321
ValueError: phi must send optimized submodule of M.W() into N.W()
1322
1323
"""
1324
if len(im_gens) == 0:
1325
# 0 map
1326
N = self if codomain is None else codomain
1327
else:
1328
if codomain is None:
1329
im_gens = Sequence(im_gens)
1330
N = im_gens.universe()
1331
else:
1332
N = codomain
1333
im_gens = Sequence(im_gens, universe=N)
1334
1335
if is_FreeModule(N):
1336
# If im_smith_gens aren't in an R-module, but are in a Free-module,
1337
# then we quotient out by the 0 submodule and get an R-module.
1338
N = FGP_Module(N, N.zero_submodule(), check=DEBUG)
1339
im_gens = Sequence(im_gens, universe=N)
1340
1341
if len(im_gens) == 0:
1342
VO = self.optimized()[0].V()
1343
H = VO.Hom(N.V())
1344
return FGP_Morphism(self.Hom(N), H(0), check=DEBUG)
1345
1346
if self.gens() == self.smith_form_gens():
1347
return self._hom_from_smith(im_gens, check)
1348
else:
1349
return self._hom_general(im_gens, check)
1350
1351
1352
def _hom_general(self, im_gens, check=True):
1353
"""
1354
Homomorphism defined by giving the images of ``self.gens()`` in some
1355
fixed fg R-module. We do not assume that the generators given by
1356
``self.gens()`` are the same as the Smith form generators, since this
1357
may not be true for a general derived class.
1358
1359
INPUT:
1360
1361
- ``im_gens`` - a Sequence object giving the images of ``self.gens()``,
1362
whose universe is some fixed fg R-module
1363
1364
EXAMPLE::
1365
1366
sage: class SillyModule(sage.modules.fg_pid.fgp_module.FGP_Module_class):
1367
... def gens(self):
1368
... return tuple(flatten([[x,x] for x in self.smith_form_gens()]))
1369
sage: A = SillyModule(ZZ**1, span([[3]], ZZ))
1370
sage: A.gen(0)
1371
(1)
1372
sage: A.gen(1)
1373
(1)
1374
sage: B = ZZ**1 / span([[3]], ZZ)
1375
sage: A.hom([B.0, 2*B.0], B)
1376
Traceback (most recent call last):
1377
...
1378
ValueError: Images do not determine a valid homomorphism
1379
sage: A.hom([B.0, B.0], B) # indirect doctest
1380
Morphism from module over Integer Ring with invariants (3,) to module with invariants (3,) that sends the generators to [(1), (1)]
1381
1382
"""
1383
m = self.ngens()
1384
A = ZZ**m
1385
q = A.hom([x.lift() for x in self.gens()], self.V())
1386
B = q.inverse_image(self.W())
1387
N = im_gens.universe()
1388
r = A.hom([x.lift() for x in im_gens], N.V())
1389
if check:
1390
if not r(B).is_submodule(N.W()):
1391
raise ValueError, "Images do not determine a valid homomorphism"
1392
smith_images = Sequence([N(r(q.lift(x.lift()))) for x in self.smith_form_gens()])
1393
return self._hom_from_smith(smith_images, check=DEBUG)
1394
1395
def _hom_from_smith(self, im_smith_gens, check=True):
1396
"""
1397
Homomorphism defined by giving the images of the Smith-form generators
1398
of self in some fixed fg R-module.
1399
1400
INPUTS:
1401
1402
- ``im_gens`` -- a Sequence object giving the images of the Smith-form
1403
generators of self, whose universe is some fixed fg R-module
1404
1405
EXAMPLE::
1406
1407
sage: class SillyModule(sage.modules.fg_pid.fgp_module.FGP_Module_class):
1408
... def gens(self):
1409
... return tuple(flatten([[x,x] for x in self.smith_form_gens()]))
1410
sage: A = SillyModule(ZZ**1, span([[3]], ZZ))
1411
sage: A.gen(0)
1412
(1)
1413
sage: A.gen(1)
1414
(1)
1415
sage: B = ZZ**1 / span([[3]], ZZ)
1416
sage: A._hom_from_smith(Sequence([B.0]))
1417
Morphism from module over Integer Ring with invariants (3,) to module with invariants (3,) that sends the generators to [(1), (1)]
1418
"""
1419
if len(im_smith_gens) != len(self.smith_form_gens()):
1420
raise ValueError, "im_gens must have length the same as self.smith_form_gens()"
1421
1422
# replace self by representation in which smith-gens g_i are a basis for V.
1423
M, _ = self.optimized()
1424
# Define morphism from M to N
1425
f = M.V().hom([x.lift() for x in im_smith_gens])
1426
N = im_smith_gens.universe()
1427
homspace = self.Hom(N)
1428
phi = FGP_Morphism(homspace, f, check=DEBUG)
1429
return phi
1430
1431
def Hom(self, N):
1432
"""
1433
EXAMPLES::
1434
1435
sage: V = span([[1/2,0,0],[3/2,2,1],[0,0,1]],ZZ); W = V.span([V.0+2*V.1, 9*V.0+2*V.1, 4*V.2])
1436
sage: Q = V/W
1437
sage: Q.Hom(Q)
1438
Set of Morphisms from Finitely generated module V/W over Integer Ring with invariants (4, 16) to Finitely generated module V/W over Integer Ring with invariants (4, 16) in Category of modules over Integer Ring
1439
sage: M = V/V.zero_submodule()
1440
sage: M.Hom(Q)
1441
Set of Morphisms from Finitely generated module V/W over Integer Ring with invariants (0, 0, 0) to Finitely generated module V/W over Integer Ring with invariants (4, 16) in Category of modules over Integer Ring
1442
"""
1443
return FGP_Homset(self, N)
1444
1445
def random_element(self, *args, **kwds):
1446
"""
1447
Create a random element of self=V/W, by creating a random element of V and
1448
reducing it modulo W.
1449
1450
All arguments are passed onto the random_element method of V.
1451
1452
EXAMPLES::
1453
1454
sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
1455
sage: Q = V/W
1456
sage: Q.random_element()
1457
(1, 10)
1458
"""
1459
return self(self._V.random_element(*args, **kwds))
1460
1461
def cardinality(self):
1462
"""
1463
Return the cardinality of this module as a set.
1464
1465
EXAMPLES::
1466
1467
sage: V = ZZ^2; W = V.span([[1,2],[3,4]]); A = V/W; A
1468
Finitely generated module V/W over Integer Ring with invariants (2)
1469
sage: A.cardinality()
1470
2
1471
sage: V = ZZ^2; W = V.span([[1,2]]); A = V/W; A
1472
Finitely generated module V/W over Integer Ring with invariants (0)
1473
sage: A.cardinality()
1474
+Infinity
1475
sage: V = QQ^2; W = V.span([[1,2]]); A = V/W; A
1476
Vector space quotient V/W of dimension 1 over Rational Field where
1477
V: Vector space of dimension 2 over Rational Field
1478
W: Vector space of degree 2 and dimension 1 over Rational Field
1479
Basis matrix:
1480
[1 2]
1481
sage: A.cardinality()
1482
+Infinity
1483
"""
1484
try:
1485
return self.__cardinality
1486
except AttributeError:
1487
pass
1488
from sage.rings.all import infinity
1489
from sage.misc.all import prod
1490
v = self.invariants()
1491
self.__cardinality = infinity if 0 in v else prod(v)
1492
return self.__cardinality
1493
1494
def __iter__(self):
1495
"""
1496
Return iterator over all elements of self.
1497
1498
EXAMPLES::
1499
1500
sage: V = span([[1/2,0,0],[3/2,2,1],[0,0,1]],ZZ); W = V.span([V.0+2*V.1, 4*V.0+2*V.1, 4*V.2])
1501
sage: Q = V/W; Q
1502
Finitely generated module V/W over Integer Ring with invariants (2, 12)
1503
sage: z = list(V/W)
1504
sage: print z
1505
[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6), (0, 7), (0, 8), (0, 9), (0, 10), (0, 11), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (1, 7), (1, 8), (1, 9), (1, 10), (1, 11)]
1506
sage: len(z)
1507
24
1508
1509
We test that the trivial module is handled correctly (cf. trac #6561)::
1510
1511
sage: A = (ZZ**1)/(ZZ**1); list(A) == [A(0)]
1512
True
1513
"""
1514
if self.base_ring() != ZZ:
1515
raise NotImplementedError, "only implemented over ZZ"
1516
v = self.invariants()
1517
if 0 in v:
1518
raise NotImplementedError, "currently self must be finite to iterate over"
1519
B = self.optimized()[0].V().basis_matrix()
1520
V = self.base_ring()**B.nrows()
1521
from sage.misc.mrange import cartesian_product_iterator
1522
for a in cartesian_product_iterator([range(k) for k in v]):
1523
b = V(a) * B
1524
yield self(b)
1525
1526
def is_finite(self):
1527
"""
1528
Return True if self is finite and False otherwise.
1529
1530
EXAMPLES::
1531
1532
sage: V = span([[1/2,0,0],[3/2,2,1],[0,0,1]],ZZ); W = V.span([V.0+2*V.1, 9*V.0+2*V.1, 4*V.2])
1533
sage: Q = V/W; Q
1534
Finitely generated module V/W over Integer Ring with invariants (4, 16)
1535
sage: Q.is_finite()
1536
True
1537
sage: Q = V/V.zero_submodule(); Q
1538
Finitely generated module V/W over Integer Ring with invariants (0, 0, 0)
1539
sage: Q.is_finite()
1540
False
1541
"""
1542
return 0 not in self.invariants()
1543
1544
def annihilator(self):
1545
"""
1546
Return the ideal of the base ring that annihilates self. This
1547
is precisely the ideal generated by the LCM of the invariants
1548
of self if self is finite, and is 0 otherwise.
1549
1550
EXAMPLES::
1551
1552
sage: V = span([[1/2,0,0],[3/2,2,1],[0,0,1]],ZZ); W = V.span([V.0+2*V.1, 9*V.0+2*V.1, 4*V.2])
1553
sage: Q = V/W; Q.annihilator()
1554
Principal ideal (16) of Integer Ring
1555
sage: Q.annihilator().gen()
1556
16
1557
1558
sage: Q = V/V.span([V.0]); Q
1559
Finitely generated module V/W over Integer Ring with invariants (0, 0)
1560
sage: Q.annihilator()
1561
Principal ideal (0) of Integer Ring
1562
"""
1563
if not self.is_finite():
1564
g = 0
1565
else:
1566
g = reduce(lcm, self.invariants())
1567
return self.base_ring().ideal(g)
1568
1569
def ngens(self):
1570
r"""
1571
Return the number of generators of self.
1572
1573
(Note for developers: This is just the length of :meth:`.gens`, rather
1574
than of the minimal set of generators as returned by
1575
:meth:`.smith_form_gens`; these are the same in the
1576
:class:`~sage.modules.fg_pid.fgp_module.FGP_Module_class`, but not
1577
necessarily in derived classes.)
1578
1579
EXAMPLES::
1580
1581
sage: A = (ZZ**2) / span([[4,0],[0,3]], ZZ)
1582
sage: A.ngens()
1583
1
1584
1585
This works (but please don't do it in production code!) ::
1586
1587
sage: A.gens = lambda: [1,2,"Barcelona!"]
1588
sage: A.ngens()
1589
3
1590
"""
1591
return len(self.gens())
1592
1593
def __hash__(self):
1594
r"""
1595
Calculate a hash for self.
1596
1597
EXAMPLES::
1598
1599
sage: A = (ZZ**2) / span([[4,0],[0,3]], ZZ)
1600
sage: hash(A)
1601
1328975982 # 32-bit
1602
-7071641102956720018 # 64-bit
1603
"""
1604
return hash( (self.V(), self.W()) )
1605
1606
##############################################################
1607
# Useful for testing
1608
##############################################################
1609
1610
def random_fgp_module(n, R=ZZ, finite=False):
1611
"""
1612
Return a random FGP module inside a rank n free module over R.
1613
1614
INPUT:
1615
1616
- ``n`` -- nonnegative integer
1617
1618
- ``R`` -- base ring (default: ZZ)
1619
1620
- ``finite`` -- bool (default: True); if True, make the random module finite.
1621
1622
EXAMPLES::
1623
1624
sage: import sage.modules.fg_pid.fgp_module as fgp
1625
sage: fgp.random_fgp_module(4)
1626
Finitely generated module V/W over Integer Ring with invariants (4)
1627
"""
1628
K = R.fraction_field()
1629
V = K**n
1630
i = ZZ.random_element(max(n,1))
1631
A = V.span([V.random_element() for _ in range(i)], R)
1632
if not finite:
1633
i = ZZ.random_element(i+1)
1634
while True:
1635
B = A.span([A.random_element() for _ in range(i)], R)
1636
#Q = A/B
1637
Q = FGP_Module_class(A,B,check=DEBUG)
1638
if not finite or Q.is_finite():
1639
return Q
1640
1641
def random_fgp_morphism_0(*args, **kwds):
1642
"""
1643
Construct a random fgp module using random_fgp_module,
1644
then construct a random morphism that sends each generator
1645
to a random multiple of itself. Inputs are the same
1646
as to random_fgp_module.
1647
1648
EXAMPLES::
1649
1650
sage: import sage.modules.fg_pid.fgp_module as fgp
1651
sage: fgp.random_fgp_morphism_0(4)
1652
Morphism from module over Integer Ring with invariants (4,) to module with invariants (4,) that sends the generators to [(0)]
1653
1654
"""
1655
A = random_fgp_module(*args, **kwds)
1656
return A.hom([ZZ.random_element()*g for g in A.smith_form_gens()])
1657
1658
def test_morphism_0(*args, **kwds):
1659
"""
1660
EXAMPLES::
1661
1662
sage: import sage.modules.fg_pid.fgp_module as fgp
1663
sage: s = 0 # we set a seed so results clearly and easily reproducible across runs.
1664
sage: set_random_seed(s); v = [fgp.test_morphism_0(1) for _ in range(30)]
1665
sage: set_random_seed(s); v = [fgp.test_morphism_0(2) for _ in range(30)]
1666
sage: set_random_seed(s); v = [fgp.test_morphism_0(3) for _ in range(10)]
1667
sage: set_random_seed(s); v = [fgp.test_morphism_0(i) for i in range(1,20)]
1668
sage: set_random_seed(s); v = [fgp.test_morphism_0(4) for _ in range(50)] # long time
1669
"""
1670
phi = random_fgp_morphism_0(*args, **kwds)
1671
K = phi.kernel()
1672
I = phi.image()
1673
from sage.misc.all import prod
1674
if prod(K.invariants()):
1675
assert prod(phi.domain().invariants()) % prod(K.invariants()) == 0
1676
assert I.is_submodule(phi.codomain())
1677
if len(I.smith_form_gens()) > 0:
1678
x = phi.lift(I.smith_form_gen(0))
1679
assert phi(x) == I.smith_form_gen(0)
1680
1681
1682
1683