Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/modules/matrix_morphism.py
4035 views
1
r"""
2
Morphisms defined by a matrix
3
4
A matrix morphism is a morphism that is defined by multiplication
5
by a matrix. Elements of domain must either have a method
6
``vector()`` that returns a vector that the defining
7
matrix can hit from the left, or be coercible into vector space of
8
appropriate dimension.
9
10
EXAMPLES::
11
12
sage: from sage.modules.matrix_morphism import MatrixMorphism, is_MatrixMorphism
13
sage: V = QQ^3
14
sage: T = End(V)
15
sage: M = MatrixSpace(QQ,3)
16
sage: I = M.identity_matrix()
17
sage: m = MatrixMorphism(T, I); m
18
Morphism defined by the matrix
19
[1 0 0]
20
[0 1 0]
21
[0 0 1]
22
sage: is_MatrixMorphism(m)
23
True
24
sage: m.charpoly('x')
25
x^3 - 3*x^2 + 3*x - 1
26
sage: m.base_ring()
27
Rational Field
28
sage: m.det()
29
1
30
sage: m.fcp('x')
31
(x - 1)^3
32
sage: m.matrix()
33
[1 0 0]
34
[0 1 0]
35
[0 0 1]
36
sage: m.rank()
37
3
38
sage: m.trace()
39
3
40
41
AUTHOR:
42
43
- William Stein: initial versions
44
45
- David Joyner (2005-12-17): added examples
46
47
- William Stein (2005-01-07): added __reduce__
48
49
- Craig Citro (2008-03-18): refactored MatrixMorphism class
50
51
- Rob Beezer (2011-07-15): additional methods, bug fixes, documentation
52
"""
53
54
import sage.categories.morphism
55
import sage.categories.homset
56
import sage.matrix.all as matrix
57
from sage.structure.all import Sequence
58
59
def is_MatrixMorphism(x):
60
"""
61
Return True if x is a Matrix morphism of free modules.
62
63
EXAMPLES::
64
65
sage: V = ZZ^2; phi = V.hom([3*V.0, 2*V.1])
66
sage: sage.modules.matrix_morphism.is_MatrixMorphism(phi)
67
True
68
sage: sage.modules.matrix_morphism.is_MatrixMorphism(3)
69
False
70
"""
71
return isinstance(x, MatrixMorphism_abstract)
72
73
class MatrixMorphism_abstract(sage.categories.morphism.Morphism):
74
def __init__(self, parent):
75
"""
76
INPUT:
77
78
79
- ``parent`` - a homspace
80
81
- ``A`` - matrix
82
83
84
EXAMPLES::
85
86
sage: from sage.modules.matrix_morphism import MatrixMorphism
87
sage: T = End(ZZ^3)
88
sage: M = MatrixSpace(ZZ,3)
89
sage: I = M.identity_matrix()
90
sage: A = MatrixMorphism(T, I)
91
sage: loads(A.dumps()) == A
92
True
93
"""
94
if not sage.categories.homset.is_Homset(parent):
95
raise TypeError, "parent must be a Hom space"
96
sage.categories.morphism.Morphism.__init__(self, parent)
97
98
def __cmp__(self, other):
99
"""
100
Compare two matrix morphisms.
101
102
EXAMPLES::
103
104
sage: V = ZZ^2; phi = V.hom([3*V.0, 2*V.1])
105
sage: phi == 3
106
False
107
sage: phi == phi
108
True
109
"""
110
return cmp(self.matrix(), other.matrix())
111
112
def __call__(self, x):
113
"""
114
Evaluate this matrix morphism at an element that can be coerced
115
into the domain.
116
117
EXAMPLES::
118
119
sage: V = QQ^3; W = QQ^2
120
sage: H = Hom(V, W); H
121
Set of Morphisms (Linear Transformations) from
122
Vector space of dimension 3 over Rational Field to
123
Vector space of dimension 2 over Rational Field
124
sage: phi = H(matrix(QQ, 3, 2, range(6))); phi
125
Vector space morphism represented by the matrix:
126
[0 1]
127
[2 3]
128
[4 5]
129
Domain: Vector space of dimension 3 over Rational Field
130
Codomain: Vector space of dimension 2 over Rational Field
131
sage: phi(V.0)
132
(0, 1)
133
sage: phi([1,2,3])
134
(16, 22)
135
sage: phi(5)
136
Traceback (most recent call last):
137
...
138
TypeError: 5 must be coercible into Vector space of dimension 3 over Rational Field
139
sage: phi([1,1])
140
Traceback (most recent call last):
141
...
142
TypeError: [1, 1] must be coercible into Vector space of dimension 3 over Rational Field
143
"""
144
try:
145
if not hasattr(x, 'parent') or x.parent() != self.domain():
146
x = self.domain()(x)
147
except TypeError:
148
raise TypeError, "%s must be coercible into %s"%(x,self.domain())
149
if self.domain().is_ambient():
150
x = x.element()
151
else:
152
x = self.domain().coordinate_vector(x)
153
v = x*self.matrix()
154
C = self.codomain()
155
if C.is_ambient():
156
return C(v)
157
return C(C.linear_combination_of_basis(v), check=False)
158
159
def _call_(self, x):
160
"""
161
Alternative for compatibility with sage.categories.map.FormalCompositeMap._call_
162
"""
163
return self.__call__(x)
164
165
def __invert__(self):
166
"""
167
Invert this matrix morphism.
168
169
EXAMPLES::
170
171
sage: V = QQ^2; phi = V.hom([3*V.0, 2*V.1])
172
sage: phi^(-1)
173
Vector space morphism represented by the matrix:
174
[1/3 0]
175
[ 0 1/2]
176
Domain: Vector space of dimension 2 over Rational Field
177
Codomain: Vector space of dimension 2 over Rational Field
178
179
Check that a certain non-invertible morphism isn't invertible::
180
181
sage: V = ZZ^2; phi = V.hom([3*V.0, 2*V.1])
182
sage: phi^(-1)
183
Traceback (most recent call last):
184
...
185
ZeroDivisionError: matrix morphism not invertible
186
"""
187
try:
188
B = ~(self.matrix())
189
except ZeroDivisionError:
190
raise ZeroDivisionError, "matrix morphism not invertible"
191
try:
192
return self.parent().reversed()(B)
193
except TypeError:
194
raise ZeroDivisionError, "matrix morphism not invertible"
195
196
def inverse(self):
197
r"""
198
Returns the inverse of this matrix morphism, if the inverse exists.
199
200
Raises a ``ZeroDivisionError`` if the inverse does not exist.
201
202
EXAMPLES:
203
204
An invertible morphism created as a restriction of
205
a non-invertible morphism, and which has an unequal
206
domain and codomain. ::
207
208
sage: V = QQ^4
209
sage: W = QQ^3
210
sage: m = matrix(QQ, [[2, 0, 3], [-6, 1, 4], [1, 2, -4], [1, 0, 1]])
211
sage: phi = V.hom(m, W)
212
sage: rho = phi.restrict_domain(V.span([V.0, V.3]))
213
sage: zeta = rho.restrict_codomain(W.span([W.0, W.2]))
214
sage: x = vector(QQ, [2, 0, 0, -7])
215
sage: y = zeta(x); y
216
(-3, 0, -1)
217
sage: inv = zeta.inverse(); inv
218
Vector space morphism represented by the matrix:
219
[-1 3]
220
[ 1 -2]
221
Domain: Vector space of degree 3 and dimension 2 over Rational Field
222
Basis matrix:
223
[1 0 0]
224
[0 0 1]
225
Codomain: Vector space of degree 4 and dimension 2 over Rational Field
226
Basis matrix:
227
[1 0 0 0]
228
[0 0 0 1]
229
sage: inv(y) == x
230
True
231
232
An example of an invertible morphism between modules,
233
(rather than between vector spaces). ::
234
235
sage: M = ZZ^4
236
sage: p = matrix(ZZ, [[ 0, -1, 1, -2],
237
... [ 1, -3, 2, -3],
238
... [ 0, 4, -3, 4],
239
... [-2, 8, -4, 3]])
240
sage: phi = M.hom(p, M)
241
sage: x = vector(ZZ, [1, -3, 5, -2])
242
sage: y = phi(x); y
243
(1, 12, -12, 21)
244
sage: rho = phi.inverse(); rho
245
Free module morphism defined by the matrix
246
[ -5 3 -1 1]
247
[ -9 4 -3 2]
248
[-20 8 -7 4]
249
[ -6 2 -2 1]
250
Domain: Ambient free module of rank 4 over the principal ideal domain ...
251
Codomain: Ambient free module of rank 4 over the principal ideal domain ...
252
sage: rho(y) == x
253
True
254
255
A non-invertible morphism, despite having an appropriate
256
domain and codomain. ::
257
258
sage: V = QQ^2
259
sage: m = matrix(QQ, [[1, 2], [20, 40]])
260
sage: phi = V.hom(m, V)
261
sage: phi.is_bijective()
262
False
263
sage: phi.inverse()
264
Traceback (most recent call last):
265
...
266
ZeroDivisionError: matrix morphism not invertible
267
268
The matrix representation of this morphism is invertible
269
over the rationals, but not over the integers, thus the
270
morphism is not invertible as a map between modules.
271
It is easy to notice from the definition that every
272
vector of the image will have a second entry that
273
is an even integer. ::
274
275
sage: V = ZZ^2
276
sage: q = matrix(ZZ, [[1, 2], [3, 4]])
277
sage: phi = V.hom(q, V)
278
sage: phi.matrix().change_ring(QQ).inverse()
279
[ -2 1]
280
[ 3/2 -1/2]
281
sage: phi.is_bijective()
282
False
283
sage: phi.image()
284
Free module of degree 2 and rank 2 over Integer Ring
285
Echelon basis matrix:
286
[1 0]
287
[0 2]
288
sage: phi.lift(vector(ZZ, [1, 1]))
289
Traceback (most recent call last):
290
...
291
ValueError: element is not in the image
292
sage: phi.inverse()
293
Traceback (most recent call last):
294
...
295
ZeroDivisionError: matrix morphism not invertible
296
297
The unary invert operator (~, tilde, "wiggle") is synonymous
298
with the ``inverse()`` method (and a lot easier to type). ::
299
300
sage: V = QQ^2
301
sage: r = matrix(QQ, [[4, 3], [-2, 5]])
302
sage: phi = V.hom(r, V)
303
sage: rho = phi.inverse()
304
sage: zeta = ~phi
305
sage: rho.is_equal_function(zeta)
306
True
307
308
TESTS::
309
310
sage: V = QQ^2
311
sage: W = QQ^3
312
sage: U = W.span([W.0, W.1])
313
sage: m = matrix(QQ, [[2, 1], [3, 4]])
314
sage: phi = V.hom(m, U)
315
sage: inv = phi.inverse()
316
sage: (inv*phi).is_identity()
317
True
318
sage: (phi*inv).is_identity()
319
True
320
"""
321
return self.__invert__()
322
323
def __rmul__(self, left):
324
"""
325
EXAMPLES::
326
327
sage: V = ZZ^2; phi = V.hom([V.0+V.1, 2*V.1])
328
sage: 2*phi
329
Free module morphism defined by the matrix
330
[2 2]
331
[0 4]...
332
sage: phi*2
333
Free module morphism defined by the matrix
334
[2 2]
335
[0 4]...
336
"""
337
R = self.base_ring()
338
return self.parent()(R(left) * self.matrix())
339
340
def __mul__(self, right):
341
"""
342
Composition of morphisms, denoted by \*.
343
344
EXAMPLES::
345
346
sage: V = ZZ^2; phi = V.hom([V.0+V.1, 2*V.1])
347
sage: phi*phi
348
Free module morphism defined by the matrix
349
[1 3]
350
[0 4]
351
Domain: Ambient free module of rank 2 over the principal ideal domain ...
352
Codomain: Ambient free module of rank 2 over the principal ideal domain ...
353
354
sage: V = QQ^3
355
sage: E = V.endomorphism_ring()
356
sage: phi = E(Matrix(QQ,3,range(9))) ; phi
357
Vector space morphism represented by the matrix:
358
[0 1 2]
359
[3 4 5]
360
[6 7 8]
361
Domain: Vector space of dimension 3 over Rational Field
362
Codomain: Vector space of dimension 3 over Rational Field
363
sage: phi*phi
364
Vector space morphism represented by the matrix:
365
[ 15 18 21]
366
[ 42 54 66]
367
[ 69 90 111]
368
Domain: Vector space of dimension 3 over Rational Field
369
Codomain: Vector space of dimension 3 over Rational Field
370
sage: phi.matrix()**2
371
[ 15 18 21]
372
[ 42 54 66]
373
[ 69 90 111]
374
375
::
376
377
sage: W = QQ**4
378
sage: E_VW = V.Hom(W)
379
sage: psi = E_VW(Matrix(QQ,3,4,range(12))) ; psi
380
Vector space morphism represented by the matrix:
381
[ 0 1 2 3]
382
[ 4 5 6 7]
383
[ 8 9 10 11]
384
Domain: Vector space of dimension 3 over Rational Field
385
Codomain: Vector space of dimension 4 over Rational Field
386
sage: psi*phi
387
Vector space morphism represented by the matrix:
388
[ 20 23 26 29]
389
[ 56 68 80 92]
390
[ 92 113 134 155]
391
Domain: Vector space of dimension 3 over Rational Field
392
Codomain: Vector space of dimension 4 over Rational Field
393
sage: phi*psi
394
Traceback (most recent call last):
395
...
396
TypeError: Incompatible composition of morphisms: domain of left morphism must be codomain of right.
397
sage: phi.matrix()*psi.matrix()
398
[ 20 23 26 29]
399
[ 56 68 80 92]
400
[ 92 113 134 155]
401
402
Composite maps can be formed with matrix morphisms::
403
404
sage: K.<a> = NumberField(x^2 + 23)
405
sage: V, VtoK, KtoV = K.vector_space()
406
sage: f = V.hom([V.0 - V.1, V.0 + V.1])*KtoV; f
407
Composite map:
408
From: Number Field in a with defining polynomial x^2 + 23
409
To: Vector space of dimension 2 over Rational Field
410
Defn: Isomorphism map:
411
From: Number Field in a with defining polynomial x^2 + 23
412
To: Vector space of dimension 2 over Rational Field
413
then
414
Vector space morphism represented by the matrix:
415
[ 1 -1]
416
[ 1 1]
417
Domain: Vector space of dimension 2 over Rational Field
418
Codomain: Vector space of dimension 2 over Rational Field
419
sage: f(a)
420
(1, 1)
421
"""
422
if not isinstance(right, MatrixMorphism):
423
if isinstance(right, (sage.categories.morphism.Morphism, sage.categories.map.Map)):
424
return sage.categories.map.Map.__mul__(self, right)
425
R = self.base_ring()
426
return self.parent()(self.matrix() * R(right))
427
if self.domain() != right.codomain():
428
raise TypeError, "Incompatible composition of morphisms: domain of left morphism must be codomain of right."
429
M = right.matrix() * self.matrix()
430
return right.domain().Hom(self.codomain())(M)
431
432
def __add__(self, right):
433
"""
434
Sum of morphisms, denoted by +.
435
436
EXAMPLES::
437
438
sage: phi = (ZZ**2).endomorphism_ring()(Matrix(ZZ,2,[2..5])) ; phi
439
Free module morphism defined by the matrix
440
[2 3]
441
[4 5]
442
Domain: Ambient free module of rank 2 over the principal ideal domain ...
443
Codomain: Ambient free module of rank 2 over the principal ideal domain ...
444
sage: phi + 3
445
Free module morphism defined by the matrix
446
[5 3]
447
[4 8]
448
Domain: Ambient free module of rank 2 over the principal ideal domain ...
449
Codomain: Ambient free module of rank 2 over the principal ideal domain ...
450
sage: phi + phi
451
Free module morphism defined by the matrix
452
[ 4 6]
453
[ 8 10]
454
Domain: Ambient free module of rank 2 over the principal ideal domain ...
455
Codomain: Ambient free module of rank 2 over the principal ideal domain ...
456
sage: psi = (ZZ**3).endomorphism_ring()(Matrix(ZZ,3,[22..30])) ; psi
457
Free module morphism defined by the matrix
458
[22 23 24]
459
[25 26 27]
460
[28 29 30]
461
Domain: Ambient free module of rank 3 over the principal ideal domain ...
462
Codomain: Ambient free module of rank 3 over the principal ideal domain ...
463
sage: phi + psi
464
Traceback (most recent call last):
465
...
466
ValueError: a matrix from
467
Full MatrixSpace of 3 by 3 dense matrices over Integer Ring
468
cannot be converted to a matrix in
469
Full MatrixSpace of 2 by 2 dense matrices over Integer Ring!
470
"""
471
# TODO: move over to any coercion model!
472
if not isinstance(right, MatrixMorphism):
473
R = self.base_ring()
474
return self.parent()(self.matrix() + R(right))
475
if not right.parent() == self.parent():
476
right = self.parent()(right)
477
M = self.matrix() + right.matrix()
478
return self.domain().Hom(right.codomain())(M)
479
480
def __neg__(self):
481
"""
482
EXAMPLES::
483
484
sage: V = ZZ^2; phi = V.hom([V.0+V.1, 2*V.1])
485
sage: -phi
486
Free module morphism defined by the matrix
487
[-1 -1]
488
[ 0 -2]...
489
"""
490
return self.parent()(-self.matrix())
491
492
def __sub__(self, other):
493
"""
494
EXAMPLES::
495
496
sage: V = ZZ^2; phi = V.hom([V.0+V.1, 2*V.1])
497
sage: phi - phi
498
Free module morphism defined by the matrix
499
[0 0]
500
[0 0]...
501
"""
502
# TODO: move over to any coercion model!
503
if not isinstance(other, MatrixMorphism):
504
R = self.base_ring()
505
return self.parent()(self.matrix() - R(other))
506
if not other.parent() == self.parent():
507
other = self.parent()(other)
508
return self.parent()(self.matrix() - other.matrix())
509
510
def base_ring(self):
511
"""
512
Return the base ring of self, that is, the ring over which self is
513
given by a matrix.
514
515
EXAMPLES::
516
517
sage: sage.modules.matrix_morphism.MatrixMorphism((ZZ**2).endomorphism_ring(), Matrix(ZZ,2,[3..6])).base_ring()
518
Integer Ring
519
"""
520
return self.domain().base_ring()
521
522
def characteristic_polynomial(self, var='x'):
523
r"""
524
Return the characteristic polynomial of this endomorphism.
525
526
``characteristic_polynomial`` and ``char_poly`` are the same method.
527
528
INPUT:
529
- var -- variable
530
531
EXAMPLES::
532
533
sage: V = ZZ^2; phi = V.hom([V.0+V.1, 2*V.1])
534
sage: phi.characteristic_polynomial()
535
x^2 - 3*x + 2
536
sage: phi.charpoly()
537
x^2 - 3*x + 2
538
sage: phi.matrix().charpoly()
539
x^2 - 3*x + 2
540
sage: phi.charpoly('T')
541
T^2 - 3*T + 2
542
"""
543
if not self.is_endomorphism():
544
raise ArithmeticError, "charpoly only defined for endomorphisms " +\
545
"(i.e., domain = range)"
546
return self.matrix().charpoly(var)
547
548
charpoly = characteristic_polynomial
549
550
def decomposition(self, *args, **kwds):
551
"""
552
Return decomposition of this endomorphism, i.e., sequence of
553
subspaces obtained by finding invariant subspaces of self.
554
555
See the documentation for self.matrix().decomposition for more
556
details. All inputs to this function are passed onto the
557
matrix one.
558
559
EXAMPLES::
560
561
sage: V = ZZ^2; phi = V.hom([V.0+V.1, 2*V.1])
562
sage: phi.decomposition()
563
[
564
Free module of degree 2 and rank 1 over Integer Ring
565
Echelon basis matrix:
566
[0 1],
567
Free module of degree 2 and rank 1 over Integer Ring
568
Echelon basis matrix:
569
[ 1 -1]
570
]
571
"""
572
if not self.is_endomorphism():
573
raise ArithmeticError, "Matrix morphism must be an endomorphism."
574
D = self.domain()
575
E = self.matrix().decomposition(*args,**kwds)
576
if D.is_ambient():
577
return Sequence([D.submodule(V, check=False) for V, _ in E],
578
cr=True, check=False)
579
else:
580
B = D.basis_matrix()
581
R = D.base_ring()
582
return Sequence([D.submodule((V.basis_matrix() * B).row_module(R),
583
check=False) for V, _ in E],
584
cr=True, check=False)
585
586
def trace(self):
587
r"""
588
Return the trace of this endomorphism.
589
590
EXAMPLES::
591
592
sage: V = ZZ^2; phi = V.hom([V.0+V.1, 2*V.1])
593
sage: phi.trace()
594
3
595
"""
596
return self._matrix.trace()
597
598
def det(self):
599
"""
600
Return the determinant of this endomorphism.
601
602
EXAMPLES::
603
604
sage: V = ZZ^2; phi = V.hom([V.0+V.1, 2*V.1])
605
sage: phi.det()
606
2
607
"""
608
if not self.is_endomorphism():
609
raise ArithmeticError, "Matrix morphism must be an endomorphism."
610
return self.matrix().determinant()
611
612
def fcp(self, var='x'):
613
"""
614
Return the factorization of the characteristic polynomial.
615
616
EXAMPLES::
617
618
sage: V = ZZ^2; phi = V.hom([V.0+V.1, 2*V.1])
619
sage: phi.fcp()
620
(x - 2) * (x - 1)
621
sage: phi.fcp('T')
622
(T - 2) * (T - 1)
623
"""
624
return self.charpoly(var).factor()
625
626
def kernel(self):
627
"""
628
Compute the kernel of this morphism.
629
630
EXAMPLES::
631
632
sage: V = VectorSpace(QQ,3)
633
sage: id = V.Hom(V)(identity_matrix(QQ,3))
634
sage: null = V.Hom(V)(0*identity_matrix(QQ,3))
635
sage: id.kernel()
636
Vector space of degree 3 and dimension 0 over Rational Field
637
Basis matrix:
638
[]
639
sage: phi = V.Hom(V)(matrix(QQ,3,range(9)))
640
sage: phi.kernel()
641
Vector space of degree 3 and dimension 1 over Rational Field
642
Basis matrix:
643
[ 1 -2 1]
644
sage: hom(CC^2, CC^2, matrix(CC, [[1,0], [0,1]])).kernel()
645
Vector space of degree 2 and dimension 0 over Complex Field with 53 bits of precision
646
Basis matrix:
647
[]
648
"""
649
V = self.matrix().kernel()
650
D = self.domain()
651
if not D.is_ambient():
652
# Transform V to ambient space
653
# This is a matrix multiply: we take the linear combinations of the basis for
654
# D given by the elements of the basis for V.
655
B = V.basis_matrix() * D.basis_matrix()
656
V = B.row_module(D.base_ring())
657
return self.domain().submodule(V, check=False)
658
659
def image(self):
660
"""
661
Compute the image of this morphism.
662
663
EXAMPLES::
664
665
sage: V = VectorSpace(QQ,3)
666
sage: phi = V.Hom(V)(matrix(QQ, 3, range(9)))
667
sage: phi.image()
668
Vector space of degree 3 and dimension 2 over Rational Field
669
Basis matrix:
670
[ 1 0 -1]
671
[ 0 1 2]
672
sage: hom(GF(7)^3, GF(7)^2, zero_matrix(GF(7), 3, 2)).image()
673
Vector space of degree 2 and dimension 0 over Finite Field of size 7
674
Basis matrix:
675
[]
676
677
678
Compute the image of the identity map on a ZZ-submodule::
679
680
sage: V = (ZZ^2).span([[1,2],[3,4]])
681
sage: phi = V.Hom(V)(identity_matrix(ZZ,2))
682
sage: phi(V.0) == V.0
683
True
684
sage: phi(V.1) == V.1
685
True
686
sage: phi.image()
687
Free module of degree 2 and rank 2 over Integer Ring
688
Echelon basis matrix:
689
[1 0]
690
[0 2]
691
sage: phi.image() == V
692
True
693
"""
694
V = self.matrix().image()
695
D = self.codomain()
696
if not D.is_ambient():
697
# Transform V to ambient space
698
# This is a matrix multiply: we take the linear combinations of the basis for
699
# D given by the elements of the basis for V.
700
B = V.basis_matrix() * D.basis_matrix()
701
V = B.row_module(self.domain().base_ring())
702
return self.codomain().submodule(V, check=False)
703
704
def matrix(self):
705
"""
706
EXAMPLES::
707
708
sage: V = ZZ^2; phi = V.hom(V.basis())
709
sage: phi.matrix()
710
[1 0]
711
[0 1]
712
sage: sage.modules.matrix_morphism.MatrixMorphism_abstract.matrix(phi)
713
Traceback (most recent call last):
714
...
715
NotImplementedError: this method must be overridden in the extension class
716
"""
717
raise NotImplementedError, "this method must be overridden in the extension class"
718
719
def rank(self):
720
r"""
721
Returns the rank of the matrix representing this morphism.
722
723
EXAMPLES::
724
725
sage: V = ZZ^2; phi = V.hom(V.basis())
726
sage: phi.rank()
727
2
728
sage: V = ZZ^2; phi = V.hom([V.0, V.0])
729
sage: phi.rank()
730
1
731
"""
732
return self.matrix().rank()
733
734
def nullity(self):
735
r"""
736
Returns the nullity of the matrix representing this morphism, which is the
737
dimension of its kernel.
738
739
EXAMPLES::
740
741
sage: V = ZZ^2; phi = V.hom(V.basis())
742
sage: phi.nullity()
743
0
744
sage: V = ZZ^2; phi = V.hom([V.0, V.0])
745
sage: phi.nullity()
746
1
747
"""
748
return self._matrix.left_nullity()
749
750
def is_bijective(self):
751
r"""
752
Tell whether ``self`` is bijective.
753
754
EXAMPLES:
755
756
Two morphisms that are obviously not bijective, simply on
757
considerations of the dimensions. However, each fullfills
758
half of the requirements to be a bijection. ::
759
760
sage: V1 = QQ^2
761
sage: V2 = QQ^3
762
sage: m = matrix(QQ, [[1, 2, 3], [4, 5, 6]])
763
sage: phi = V1.hom(m, V2)
764
sage: phi.is_injective()
765
True
766
sage: phi.is_bijective()
767
False
768
sage: rho = V2.hom(m.transpose(), V1)
769
sage: rho.is_surjective()
770
True
771
sage: rho.is_bijective()
772
False
773
774
We construct a simple bijection between two one-dimensional
775
vector spaces. ::
776
777
sage: V1 = QQ^3
778
sage: V2 = QQ^2
779
sage: phi = V1.hom(matrix(QQ, [[1, 2], [3, 4], [5, 6]]), V2)
780
sage: x = vector(QQ, [1, -1, 4])
781
sage: y = phi(x); y
782
(18, 22)
783
sage: rho = phi.restrict_domain(V1.span([x]))
784
sage: zeta = rho.restrict_codomain(V2.span([y]))
785
sage: zeta.is_bijective()
786
True
787
788
AUTHOR:
789
790
- Rob Beezer (2011-06-28)
791
"""
792
return self.is_injective() and self.is_surjective()
793
794
def is_identity(self):
795
r"""
796
Determines if this morphism is an identity function or not.
797
798
EXAMPLES:
799
800
A homomorphism that cannot possibly be the identity
801
due to an unequal domain and codomain. ::
802
803
sage: V = QQ^3
804
sage: W = QQ^2
805
sage: m = matrix(QQ, [[1, 2], [3, 4], [5, 6]])
806
sage: phi = V.hom(m, W)
807
sage: phi.is_identity()
808
False
809
810
A bijection, but not the identity. ::
811
812
sage: V = QQ^3
813
sage: n = matrix(QQ, [[3, 1, -8], [5, -4, 6], [1, 1, -5]])
814
sage: phi = V.hom(n, V)
815
sage: phi.is_bijective()
816
True
817
sage: phi.is_identity()
818
False
819
820
A restriction that is the identity. ::
821
822
sage: V = QQ^3
823
sage: p = matrix(QQ, [[1, 0, 0], [5, 8, 3], [0, 0, 1]])
824
sage: phi = V.hom(p, V)
825
sage: rho = phi.restrict(V.span([V.0, V.2]))
826
sage: rho.is_identity()
827
True
828
829
An identity linear transformation that is defined with a
830
domain and codomain with wildly different bases, so that the
831
matrix representation is not simply the identity matrix. ::
832
833
sage: A = matrix(QQ, [[1, 1, 0], [2, 3, -4], [2, 4, -7]])
834
sage: B = matrix(QQ, [[2, 7, -2], [-1, -3, 1], [-1, -6, 2]])
835
sage: U = (QQ^3).subspace_with_basis(A.rows())
836
sage: V = (QQ^3).subspace_with_basis(B.rows())
837
sage: H = Hom(U, V)
838
sage: id = lambda x: x
839
sage: phi = H(id)
840
sage: phi([203, -179, 34])
841
(203, -179, 34)
842
sage: phi.matrix()
843
[ 1 0 1]
844
[ -9 -18 -2]
845
[-17 -31 -5]
846
sage: phi.is_identity()
847
True
848
849
TEST::
850
851
sage: V = QQ^10
852
sage: H = Hom(V, V)
853
sage: id = H.identity()
854
sage: id.is_identity()
855
True
856
857
AUTHOR:
858
859
- Rob Beezer (2011-06-28)
860
"""
861
if self.domain() != self.codomain():
862
return False
863
# testing for the identity matrix will only work for
864
# endomorphisms which have the same basis for domain and codomain
865
# so we test equality on a basis, which is sufficient
866
return all( self(u) == u for u in self.domain().basis() )
867
868
def is_zero(self):
869
r"""
870
Determines if this morphism is a zero function or not.
871
872
EXAMPLES:
873
874
A zero morphism created from a function. ::
875
876
sage: V = ZZ^5
877
sage: W = ZZ^3
878
sage: z = lambda x: zero_vector(ZZ, 3)
879
sage: phi = V.hom(z, W)
880
sage: phi.is_zero()
881
True
882
883
An image list that just barely makes a non-zero morphism. ::
884
885
sage: V = ZZ^4
886
sage: W = ZZ^6
887
sage: z = zero_vector(ZZ, 6)
888
sage: images = [z, z, W.5, z]
889
sage: phi = V.hom(images, W)
890
sage: phi.is_zero()
891
False
892
893
TEST::
894
895
sage: V = QQ^10
896
sage: W = QQ^3
897
sage: H = Hom(V, W)
898
sage: rho = H.zero()
899
sage: rho.is_zero()
900
True
901
902
AUTHOR:
903
904
- Rob Beezer (2011-07-15)
905
"""
906
# any nonzero entry in any matrix representation
907
# disqualifies the morphism as having totally zero outputs
908
return self._matrix.is_zero()
909
910
def is_equal_function(self, other):
911
r"""
912
Determines if two morphisms are equal functions.
913
914
INPUT:
915
916
- ``other`` - a morphism to compare with ``self``
917
918
OUTPUT:
919
920
Returns ``True`` precisely when the two morphisms have
921
equal domains and codomains (as sets) and produce identical
922
output when given the same input. Otherwise returns ``False``.
923
924
This is useful when ``self`` and ``other`` may have different
925
representations.
926
927
Sage's default comparison of matrix morphisms requires the
928
domains to have the same bases and the codomains to have the
929
same bases, and then compares the matrix representations.
930
This notion of equality is more permissive (it will
931
return ``True`` "more often"), but is more correct
932
mathematically.
933
934
EXAMPLES:
935
936
Three morphisms defined by combinations of different
937
bases for the domain and codomain and different functions.
938
Two are equal, the third is different from both of the others. ::
939
940
sage: B = matrix(QQ, [[-3, 5, -4, 2],
941
... [-1, 2, -1, 4],
942
... [ 4, -6, 5, -1],
943
... [-5, 7, -6, 1]])
944
sage: U = (QQ^4).subspace_with_basis(B.rows())
945
sage: C = matrix(QQ, [[-1, -6, -4],
946
... [ 3, -5, 6],
947
... [ 1, 2, 3]])
948
sage: V = (QQ^3).subspace_with_basis(C.rows())
949
sage: H = Hom(U, V)
950
951
sage: D = matrix(QQ, [[-7, -2, -5, 2],
952
... [-5, 1, -4, -8],
953
... [ 1, -1, 1, 4],
954
... [-4, -1, -3, 1]])
955
sage: X = (QQ^4).subspace_with_basis(D.rows())
956
sage: E = matrix(QQ, [[ 4, -1, 4],
957
... [ 5, -4, -5],
958
... [-1, 0, -2]])
959
sage: Y = (QQ^3).subspace_with_basis(E.rows())
960
sage: K = Hom(X, Y)
961
962
sage: f = lambda x: vector(QQ, [x[0]+x[1], 2*x[1]-4*x[2], 5*x[3]])
963
sage: g = lambda x: vector(QQ, [x[0]-x[2], 2*x[1]-4*x[2], 5*x[3]])
964
965
sage: rho = H(f)
966
sage: phi = K(f)
967
sage: zeta = H(g)
968
969
sage: rho.is_equal_function(phi)
970
True
971
sage: phi.is_equal_function(rho)
972
True
973
sage: zeta.is_equal_function(rho)
974
False
975
sage: phi.is_equal_function(zeta)
976
False
977
978
TEST::
979
980
sage: H = Hom(ZZ^2, ZZ^2)
981
sage: phi = H(matrix(ZZ, 2, range(4)))
982
sage: phi.is_equal_function('junk')
983
Traceback (most recent call last):
984
...
985
TypeError: can only compare to a matrix morphism, not junk
986
987
AUTHOR:
988
989
- Rob Beezer (2011-07-15)
990
"""
991
if not is_MatrixMorphism(other):
992
msg = 'can only compare to a matrix morphism, not {0}'
993
raise TypeError(msg.format(other))
994
if self.domain() != other.domain():
995
return False
996
if self.codomain() != other.codomain():
997
return False
998
# check agreement on any basis of the domain
999
return all( self(u) == other(u) for u in self.domain().basis() )
1000
1001
def restrict_domain(self, sub):
1002
"""
1003
Restrict this matrix morphism to a subspace sub of the domain. The
1004
subspace sub should have a basis() method and elements of the basis
1005
should be coercible into domain.
1006
1007
The resulting morphism has the same codomain as before, but a new
1008
domain.
1009
1010
EXAMPLES::
1011
1012
sage: V = ZZ^2; phi = V.hom([3*V.0, 2*V.1])
1013
sage: phi.restrict_domain(V.span([V.0]))
1014
Free module morphism defined by the matrix
1015
[3 0]
1016
Domain: Free module of degree 2 and rank 1 over Integer Ring
1017
Echelon ...
1018
Codomain: Ambient free module of rank 2 over the principal ideal domain ...
1019
sage: phi.restrict_domain(V.span([V.1]))
1020
Free module morphism defined by the matrix
1021
[0 2]...
1022
"""
1023
D = self.domain()
1024
if hasattr(D, 'coordinate_module'):
1025
# We only have to do this in case the module supports
1026
# alternative basis. Some modules do, some modules don't.
1027
V = D.coordinate_module(sub)
1028
else:
1029
V = sub.free_module()
1030
A = self.matrix().restrict_domain(V)
1031
H = sub.Hom(self.codomain())
1032
return H(A)
1033
1034
def restrict_codomain(self, sub):
1035
"""
1036
Restrict this matrix morphism to a subspace sub of the codomain.
1037
1038
The resulting morphism has the same domain as before, but a new
1039
codomain.
1040
1041
EXAMPLES::
1042
1043
sage: V = ZZ^2; phi = V.hom([4*(V.0+V.1),0])
1044
sage: W = V.span([2*(V.0+V.1)])
1045
sage: phi
1046
Free module morphism defined by the matrix
1047
[4 4]
1048
[0 0]
1049
Domain: Ambient free module of rank 2 over the principal ideal domain ...
1050
Codomain: Ambient free module of rank 2 over the principal ideal domain ...
1051
sage: psi = phi.restrict_codomain(W); psi
1052
Free module morphism defined by the matrix
1053
[2]
1054
[0]
1055
Domain: Ambient free module of rank 2 over the principal ideal domain ...
1056
Codomain: Free module of degree 2 and rank 1 over Integer Ring
1057
Echelon ...
1058
1059
An example in which the codomain equals the full ambient space, but
1060
with a different basis::
1061
1062
sage: V = QQ^2
1063
sage: W = V.span_of_basis([[1,2],[3,4]])
1064
sage: phi = V.hom(matrix(QQ,2,[1,0,2,0]),W)
1065
sage: phi.matrix()
1066
[1 0]
1067
[2 0]
1068
sage: phi(V.0)
1069
(1, 2)
1070
sage: phi(V.1)
1071
(2, 4)
1072
sage: X = V.span([[1,2]]); X
1073
Vector space of degree 2 and dimension 1 over Rational Field
1074
Basis matrix:
1075
[1 2]
1076
sage: phi(V.0) in X
1077
True
1078
sage: phi(V.1) in X
1079
True
1080
sage: psi = phi.restrict_codomain(X); psi
1081
Vector space morphism represented by the matrix:
1082
[1]
1083
[2]
1084
Domain: Vector space of dimension 2 over Rational Field
1085
Codomain: Vector space of degree 2 and dimension 1 over Rational Field
1086
Basis matrix:
1087
[1 2]
1088
sage: psi(V.0)
1089
(1, 2)
1090
sage: psi(V.1)
1091
(2, 4)
1092
sage: psi(V.0).parent() is X
1093
True
1094
"""
1095
H = self.domain().Hom(sub)
1096
C = self.codomain()
1097
if hasattr(C, 'coordinate_module'):
1098
# We only have to do this in case the module supports
1099
# alternative basis. Some modules do, some modules don't.
1100
V = C.coordinate_module(sub)
1101
else:
1102
V = sub.free_module()
1103
return H(self.matrix().restrict_codomain(V))
1104
1105
1106
def restrict(self, sub):
1107
"""
1108
Restrict this matrix morphism to a subspace sub of the domain.
1109
1110
The codomain and domain of the resulting matrix are both sub.
1111
1112
EXAMPLES::
1113
1114
sage: V = ZZ^2; phi = V.hom([3*V.0, 2*V.1])
1115
sage: phi.restrict(V.span([V.0]))
1116
Free module morphism defined by the matrix
1117
[3]
1118
Domain: Free module of degree 2 and rank 1 over Integer Ring
1119
Echelon ...
1120
Codomain: Free module of degree 2 and rank 1 over Integer Ring
1121
Echelon ...
1122
1123
sage: V = (QQ^2).span_of_basis([[1,2],[3,4]])
1124
sage: phi = V.hom([V.0+V.1, 2*V.1])
1125
sage: phi(V.1) == 2*V.1
1126
True
1127
sage: W = span([V.1])
1128
sage: phi(W)
1129
Vector space of degree 2 and dimension 1 over Rational Field
1130
Basis matrix:
1131
[ 1 4/3]
1132
sage: psi = phi.restrict(W); psi
1133
Vector space morphism represented by the matrix:
1134
[2]
1135
Domain: Vector space of degree 2 and dimension 1 over Rational Field
1136
Basis matrix:
1137
[ 1 4/3]
1138
Codomain: Vector space of degree 2 and dimension 1 over Rational Field
1139
Basis matrix:
1140
[ 1 4/3]
1141
sage: psi.domain() == W
1142
True
1143
sage: psi(W.0) == 2*W.0
1144
True
1145
"""
1146
if not self.is_endomorphism():
1147
raise ArithmeticError, "matrix morphism must be an endomorphism"
1148
D = self.domain()
1149
C = self.codomain()
1150
if D is not C and (D.basis() != C.basis()):
1151
# Tricky case when two bases for same space
1152
return self.restrict_domain(sub).restrict_codomain(sub)
1153
if hasattr(D, 'coordinate_module'):
1154
# We only have to do this in case the module supports
1155
# alternative basis. Some modules do, some modules don't.
1156
V = D.coordinate_module(sub)
1157
else:
1158
V = sub.free_module()
1159
A = self.matrix().restrict(V)
1160
H = sage.categories.homset.End(sub, self.domain().category())
1161
return H(A)
1162
1163
1164
class MatrixMorphism(MatrixMorphism_abstract):
1165
"""
1166
A morphism defined by a matrix.
1167
"""
1168
def __init__(self, parent, A):
1169
"""
1170
INPUT:
1171
1172
1173
- ``parent`` - a homspace
1174
1175
- ``A`` - matrix
1176
1177
1178
EXAMPLES::
1179
1180
sage: from sage.modules.matrix_morphism import MatrixMorphism
1181
sage: T = End(ZZ^3)
1182
sage: M = MatrixSpace(ZZ,3)
1183
sage: I = M.identity_matrix()
1184
sage: A = MatrixMorphism(T, I)
1185
sage: loads(A.dumps()) == A
1186
True
1187
"""
1188
R = A.base_ring()
1189
if A.nrows() != parent.domain().rank():
1190
raise ArithmeticError, "number of rows of matrix (=%s) must equal rank of domain (=%s)"%(A.nrows(), parent.domain().rank())
1191
if A.ncols() != parent.codomain().rank():
1192
raise ArithmeticError, "number of columns of matrix (=%s) must equal rank of codomain (=%s)"%(A.ncols(), parent.codomain().rank())
1193
self._matrix = A
1194
MatrixMorphism_abstract.__init__(self, parent)
1195
1196
def matrix(self, side='left'):
1197
r"""
1198
Return a matrix that defines this morphism.
1199
1200
INPUT:
1201
1202
- ``side`` - default:``'left'`` - the side of the matrix
1203
where a vector is placed to effect the morphism (function).
1204
1205
OUTPUT:
1206
1207
A matrix which represents the morphism, relative to bases
1208
for the domain and codomain. If the modules are provided
1209
with user bases, then the representation is relative to
1210
these bases.
1211
1212
Internally, Sage represents a matrix morphism with the
1213
matrix multiplying a row vector placed to the left of the
1214
matrix. If the option ``side='right'`` is used, then a
1215
matrix is returned that acts on a vector to the right of
1216
the matrix. These two matrices are just transposes of
1217
each other and the difference is just a preference for
1218
the style of representation.
1219
1220
EXAMPLES::
1221
1222
sage: V = ZZ^2; W = ZZ^3
1223
sage: m = column_matrix([3*V.0 - 5*V.1, 4*V.0 + 2*V.1, V.0 + V.1])
1224
sage: phi = V.hom(m, W)
1225
sage: phi.matrix()
1226
[ 3 4 1]
1227
[-5 2 1]
1228
1229
sage: phi.matrix(side='right')
1230
[ 3 -5]
1231
[ 4 2]
1232
[ 1 1]
1233
1234
TESTS::
1235
1236
sage: V = ZZ^2
1237
sage: phi = V.hom([3*V.0, 2*V.1])
1238
sage: phi.matrix(side='junk')
1239
Traceback (most recent call last):
1240
...
1241
ValueError: side must be 'left' or 'right', not junk
1242
"""
1243
if not side in ['left', 'right']:
1244
raise ValueError("side must be 'left' or 'right', not {0}".format(side))
1245
if side == 'left':
1246
return self._matrix
1247
else:
1248
return self._matrix.transpose()
1249
1250
def is_injective(self):
1251
"""
1252
Tell whether ``self`` is injective.
1253
1254
EXAMPLE::
1255
1256
sage: V1 = QQ^2
1257
sage: V2 = QQ^3
1258
sage: phi = V1.hom(Matrix([[1,2,3],[4,5,6]]),V2)
1259
sage: phi.is_injective()
1260
True
1261
sage: psi = V2.hom(Matrix([[1,2],[3,4],[5,6]]),V1)
1262
sage: psi.is_injective()
1263
False
1264
1265
AUTHOR:
1266
1267
-- Simon King (2010-05)
1268
"""
1269
return self._matrix.kernel().dimension() == 0
1270
1271
def is_surjective(self):
1272
r"""
1273
Tell whether ``self`` is surjective.
1274
1275
EXAMPLES::
1276
1277
sage: V1 = QQ^2
1278
sage: V2 = QQ^3
1279
sage: phi = V1.hom(Matrix([[1,2,3],[4,5,6]]), V2)
1280
sage: phi.is_surjective()
1281
False
1282
sage: psi = V2.hom(Matrix([[1,2],[3,4],[5,6]]), V1)
1283
sage: psi.is_surjective()
1284
True
1285
1286
An example over a PID that is not `\ZZ`. ::
1287
1288
sage: R = PolynomialRing(QQ, 'x')
1289
sage: A = R^2
1290
sage: B = R^2
1291
sage: H = A.hom([B([x^2-1, 1]), B([x^2, 1])])
1292
sage: H.image()
1293
Free module of degree 2 and rank 2 over Univariate Polynomial Ring in x over Rational Field
1294
Echelon basis matrix:
1295
[ 1 0]
1296
[ 0 -1]
1297
sage: H.is_surjective()
1298
True
1299
1300
This tests if Trac #11552 is fixed. ::
1301
1302
sage: V = ZZ^2
1303
sage: m = matrix(ZZ, [[1,2],[0,2]])
1304
sage: phi = V.hom(m, V)
1305
sage: phi.lift(vector(ZZ, [0, 1]))
1306
Traceback (most recent call last):
1307
...
1308
ValueError: element is not in the image
1309
sage: phi.is_surjective()
1310
False
1311
1312
AUTHORS:
1313
1314
- Simon King (2010-05)
1315
- Rob Beezer (2011-06-28)
1316
"""
1317
# Testing equality of free modules over PIDs is unreliable
1318
# see Trac #11579 for explanation and status
1319
# We test if image equals codomain with two inclusions
1320
# reverse inclusion of below is trivially true
1321
return self.codomain().is_submodule(self.image())
1322
1323
def _repr_(self):
1324
r"""
1325
Return string representation of this matrix morphism.
1326
1327
This will typically be overloaded in a derived class.
1328
1329
EXAMPLES::
1330
1331
sage: V = ZZ^2; phi = V.hom([3*V.0, 2*V.1])
1332
sage: sage.modules.matrix_morphism.MatrixMorphism._repr_(phi)
1333
'Morphism defined by the matrix\n[3 0]\n[0 2]'
1334
1335
sage: phi._repr_()
1336
'Free module morphism defined by the matrix\n[3 0]\n[0 2]\nDomain: Ambient free module of rank 2 over the principal ideal domain Integer Ring\nCodomain: Ambient free module of rank 2 over the principal ideal domain Integer Ring'
1337
"""
1338
return "Morphism defined by the matrix\n{0}".format(self.matrix())
1339
1340