Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/modules/vector_space_morphism.py
4036 views
1
r"""
2
Vector Space Morphisms (aka Linear Transformations)
3
4
AUTHOR:
5
6
- Rob Beezer: (2011-06-29)
7
8
A vector space morphism is a homomorphism between vector spaces, better known
9
as a linear transformation. These are a specialization of Sage's free module
10
homomorphisms. (A free module is like a vector space, but with scalars from a
11
ring that may not be a field.) So references to free modules in the
12
documentation or error messages should be understood as simply reflectng a
13
more general situation.
14
15
Creation
16
--------
17
18
The constructor :func:`linear_transformation` is designed to accept a
19
variety of inputs that can define a linear transformation. See the
20
documentation of the function for all the possibilities. Here we give two.
21
22
First a matrix representation. By default input matrices are understood
23
to act on vectors placed to left of the matrix. Optionally, an input
24
matrix can be described as acting on vectors placed to the right. ::
25
26
sage: A = matrix(QQ, [[-1, 2, 3], [4, 2, 0]])
27
sage: phi = linear_transformation(A)
28
sage: phi
29
Vector space morphism represented by the matrix:
30
[-1 2 3]
31
[ 4 2 0]
32
Domain: Vector space of dimension 2 over Rational Field
33
Codomain: Vector space of dimension 3 over Rational Field
34
sage: phi([2, -3])
35
(-14, -2, 6)
36
37
A symbolic function can be used to specify the "rule" for a
38
linear transformation, along with explicit descriptions of the
39
domain and codomain. ::
40
41
sage: F = Integers(13)
42
sage: D = F^3
43
sage: C = F^2
44
sage: x, y, z = var('x y z')
45
sage: f(x, y, z) = [2*x + 3*y + 5*z, x + z]
46
sage: rho = linear_transformation(D, C, f)
47
sage: f(1, 2, 3)
48
(23, 4)
49
sage: rho([1, 2, 3])
50
(10, 4)
51
52
A "vector space homspace" is the set of all linear transformations
53
between two vector spaces. Various input can be coerced into a
54
homspace to create a linear transformation. See
55
:mod:`sage.modules.vector_space_homspace` for more. ::
56
57
sage: D = QQ^4
58
sage: C = QQ^2
59
sage: hom_space = Hom(D, C)
60
sage: images = [[1, 3], [2, -1], [4, 0], [3, 7]]
61
sage: zeta = hom_space(images)
62
sage: zeta
63
Vector space morphism represented by the matrix:
64
[ 1 3]
65
[ 2 -1]
66
[ 4 0]
67
[ 3 7]
68
Domain: Vector space of dimension 4 over Rational Field
69
Codomain: Vector space of dimension 2 over Rational Field
70
71
A homomorphism may also be created via a method on the domain. ::
72
73
sage: F = QQ[sqrt(3)]
74
sage: a = F.gen(0)
75
sage: D = F^2
76
sage: C = F^2
77
sage: A = matrix(F, [[a, 1], [2*a, 2]])
78
sage: psi = D.hom(A, C)
79
sage: psi
80
Vector space morphism represented by the matrix:
81
[ sqrt3 1]
82
[2*sqrt3 2]
83
Domain: Vector space of dimension 2 over Number Field in sqrt3 with defining polynomial x^2 - 3
84
Codomain: Vector space of dimension 2 over Number Field in sqrt3 with defining polynomial x^2 - 3
85
sage: psi([1, 4])
86
(9*sqrt3, 9)
87
88
Properties
89
----------
90
91
Many natural properties of a linear transformation can be computed.
92
Some of these are more general methods of objects in the classes
93
:class:`sage.modules.free_module_morphism.FreeModuleMorphism` and
94
:class:`sage.modules.matrix_morphism.MatrixMorphism`.
95
96
Values are computed in a natural way, an inverse image of an
97
element can be computed with the ``lift()`` method, when the inverse
98
image actually exists. ::
99
100
sage: A = matrix(QQ, [[1,2], [2,4], [3,6]])
101
sage: phi = linear_transformation(A)
102
sage: phi([1,2,0])
103
(5, 10)
104
sage: phi.lift([10, 20])
105
(10, 0, 0)
106
sage: phi.lift([100, 100])
107
Traceback (most recent call last):
108
...
109
ValueError: element is not in the image
110
111
Images and pre-images can be computed as vector spaces. ::
112
113
sage: A = matrix(QQ, [[1,2], [2,4], [3,6]])
114
sage: phi = linear_transformation(A)
115
sage: phi.image()
116
Vector space of degree 2 and dimension 1 over Rational Field
117
Basis matrix:
118
[1 2]
119
120
sage: phi.inverse_image( (QQ^2).span([[1,2]]) )
121
Vector space of degree 3 and dimension 3 over Rational Field
122
Basis matrix:
123
[1 0 0]
124
[0 1 0]
125
[0 0 1]
126
127
sage: phi.inverse_image( (QQ^2).span([[1,1]]) )
128
Vector space of degree 3 and dimension 2 over Rational Field
129
Basis matrix:
130
[ 1 0 -1/3]
131
[ 0 1 -2/3]
132
133
Injectivity and surjectivity can be checked. ::
134
135
sage: A = matrix(QQ, [[1,2], [2,4], [3,6]])
136
sage: phi = linear_transformation(A)
137
sage: phi.is_injective()
138
False
139
sage: phi.is_surjective()
140
False
141
142
Restrictions and Representations
143
--------------------------------
144
145
It is possible to restrict the domain and codomain of a linear
146
transformation to make a new linear transformation. We will use
147
those commands to replace the domain and codomain by equal vector
148
spaces, but with alternate bases. The point here is that the
149
matrix representation used to represent linear transformations are
150
relative to the bases of both the domain and codomain. ::
151
152
sage: A = graphs.PetersenGraph().adjacency_matrix()
153
sage: V = QQ^10
154
sage: phi = linear_transformation(V, V, A)
155
sage: phi
156
Vector space morphism represented by the matrix:
157
[0 1 0 0 1 1 0 0 0 0]
158
[1 0 1 0 0 0 1 0 0 0]
159
[0 1 0 1 0 0 0 1 0 0]
160
[0 0 1 0 1 0 0 0 1 0]
161
[1 0 0 1 0 0 0 0 0 1]
162
[1 0 0 0 0 0 0 1 1 0]
163
[0 1 0 0 0 0 0 0 1 1]
164
[0 0 1 0 0 1 0 0 0 1]
165
[0 0 0 1 0 1 1 0 0 0]
166
[0 0 0 0 1 0 1 1 0 0]
167
Domain: Vector space of dimension 10 over Rational Field
168
Codomain: Vector space of dimension 10 over Rational Field
169
170
sage: B1 = [V.gen(i) + V.gen(i+1) for i in range(9)] + [V.gen(9)]
171
sage: B2 = [V.gen(0)] + [-V.gen(i-1) + V.gen(i) for i in range(1,10)]
172
sage: D = V.subspace_with_basis(B1)
173
sage: C = V.subspace_with_basis(B2)
174
sage: rho = phi.restrict_codomain(C)
175
sage: zeta = rho.restrict_domain(D)
176
sage: zeta
177
Vector space morphism represented by the matrix:
178
[6 5 4 3 3 2 1 0 0 0]
179
[6 5 4 3 2 2 2 1 0 0]
180
[6 6 5 4 3 2 2 2 1 0]
181
[6 5 5 4 3 2 2 2 2 1]
182
[6 4 4 4 3 3 3 3 2 1]
183
[6 5 4 4 4 4 4 4 3 1]
184
[6 6 5 4 4 4 3 3 3 2]
185
[6 6 6 5 4 4 2 1 1 1]
186
[6 6 6 6 5 4 3 1 0 0]
187
[3 3 3 3 3 2 2 1 0 0]
188
Domain: Vector space of degree 10 and dimension 10 over Rational Field
189
User basis matrix:
190
[1 1 0 0 0 0 0 0 0 0]
191
[0 1 1 0 0 0 0 0 0 0]
192
[0 0 1 1 0 0 0 0 0 0]
193
[0 0 0 1 1 0 0 0 0 0]
194
[0 0 0 0 1 1 0 0 0 0]
195
[0 0 0 0 0 1 1 0 0 0]
196
[0 0 0 0 0 0 1 1 0 0]
197
[0 0 0 0 0 0 0 1 1 0]
198
[0 0 0 0 0 0 0 0 1 1]
199
[0 0 0 0 0 0 0 0 0 1]
200
Codomain: Vector space of degree 10 and dimension 10 over Rational Field
201
User basis matrix:
202
[ 1 0 0 0 0 0 0 0 0 0]
203
[-1 1 0 0 0 0 0 0 0 0]
204
[ 0 -1 1 0 0 0 0 0 0 0]
205
[ 0 0 -1 1 0 0 0 0 0 0]
206
[ 0 0 0 -1 1 0 0 0 0 0]
207
[ 0 0 0 0 -1 1 0 0 0 0]
208
[ 0 0 0 0 0 -1 1 0 0 0]
209
[ 0 0 0 0 0 0 -1 1 0 0]
210
[ 0 0 0 0 0 0 0 -1 1 0]
211
[ 0 0 0 0 0 0 0 0 -1 1]
212
213
An endomorphism is a linear transformation with an equal domain and codomain,
214
and here each needs to have the same basis. We are using a
215
matrix that has well-behaved eigenvalues, as part of showing that these
216
do not change as the representation changes. ::
217
218
sage: A = graphs.PetersenGraph().adjacency_matrix()
219
sage: V = QQ^10
220
sage: phi = linear_transformation(V, V, A)
221
sage: phi.eigenvalues()
222
[3, -2, -2, -2, -2, 1, 1, 1, 1, 1]
223
224
sage: B1 = [V.gen(i) + V.gen(i+1) for i in range(9)] + [V.gen(9)]
225
sage: C = V.subspace_with_basis(B1)
226
sage: zeta = phi.restrict(C)
227
sage: zeta
228
Vector space morphism represented by the matrix:
229
[ 1 0 1 -1 2 -1 2 -2 2 -2]
230
[ 1 0 1 0 0 0 1 0 0 0]
231
[ 0 1 0 1 0 0 0 1 0 0]
232
[ 1 -1 2 -1 2 -2 2 -2 3 -2]
233
[ 2 -2 2 -1 1 -1 1 0 1 0]
234
[ 1 0 0 0 0 0 0 1 1 0]
235
[ 0 1 0 0 0 1 -1 1 0 2]
236
[ 0 0 1 0 0 2 -1 1 -1 2]
237
[ 0 0 0 1 0 1 1 0 0 0]
238
[ 0 0 0 0 1 -1 2 -1 1 -1]
239
Domain: Vector space of degree 10 and dimension 10 over Rational Field
240
User basis matrix:
241
[1 1 0 0 0 0 0 0 0 0]
242
[0 1 1 0 0 0 0 0 0 0]
243
[0 0 1 1 0 0 0 0 0 0]
244
[0 0 0 1 1 0 0 0 0 0]
245
[0 0 0 0 1 1 0 0 0 0]
246
[0 0 0 0 0 1 1 0 0 0]
247
[0 0 0 0 0 0 1 1 0 0]
248
[0 0 0 0 0 0 0 1 1 0]
249
[0 0 0 0 0 0 0 0 1 1]
250
[0 0 0 0 0 0 0 0 0 1]
251
Codomain: Vector space of degree 10 and dimension 10 over Rational Field
252
User basis matrix:
253
[1 1 0 0 0 0 0 0 0 0]
254
[0 1 1 0 0 0 0 0 0 0]
255
[0 0 1 1 0 0 0 0 0 0]
256
[0 0 0 1 1 0 0 0 0 0]
257
[0 0 0 0 1 1 0 0 0 0]
258
[0 0 0 0 0 1 1 0 0 0]
259
[0 0 0 0 0 0 1 1 0 0]
260
[0 0 0 0 0 0 0 1 1 0]
261
[0 0 0 0 0 0 0 0 1 1]
262
[0 0 0 0 0 0 0 0 0 1]
263
264
sage: zeta.eigenvalues()
265
[3, -2, -2, -2, -2, 1, 1, 1, 1, 1]
266
267
Equality
268
--------
269
270
Equality of linear transformations is a bit nuanced. The equality operator
271
``==`` tests if two linear transformations have equal matrix representations,
272
while we determine if two linear transformations are the same function with the
273
``.is_equal_function()`` method. Notice in this example that the function
274
never changes, just the representations. ::
275
276
sage: f = lambda x: vector(QQ, [x[1], x[0]+x[1], x[0]])
277
sage: H = Hom(QQ^2, QQ^3)
278
sage: phi = H(f)
279
280
sage: rho = linear_transformation(QQ^2, QQ^3, matrix(QQ,2, 3, [[0,1,1], [1,1,0]]))
281
282
sage: phi == rho
283
True
284
285
sage: U = (QQ^2).subspace_with_basis([[1, 2], [-3, 1]])
286
sage: V = (QQ^3).subspace_with_basis([[0, 1, 0], [2, 3, 1], [-1, 1, 6]])
287
sage: K = Hom(U, V)
288
sage: zeta = K(f)
289
290
sage: zeta == phi
291
False
292
sage: zeta.is_equal_function(phi)
293
True
294
sage: zeta.is_equal_function(rho)
295
True
296
297
TESTS::
298
299
sage: V = QQ^2
300
sage: H = Hom(V, V)
301
sage: f = H([V.1,-2*V.0])
302
sage: loads(dumps(f))
303
Vector space morphism represented by the matrix:
304
[ 0 1]
305
[-2 0]
306
Domain: Vector space of dimension 2 over Rational Field
307
Codomain: Vector space of dimension 2 over Rational Field
308
sage: loads(dumps(f)) == f
309
True
310
"""
311
312
####################################################################################
313
# Copyright (C) 2011 Rob Beezer <[email protected]>
314
#
315
# Distributed under the terms of the GNU General Public License (GPL)
316
#
317
# This code is distributed in the hope that it will be useful,
318
# but WITHOUT ANY WARRANTY; without even the implied warranty of
319
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
320
# General Public License for more details.
321
#
322
# The full text of the GPL is available at:
323
#
324
# http://www.gnu.org/licenses/
325
####################################################################################
326
327
328
import sage.modules.matrix_morphism as matrix_morphism
329
import sage.modules.free_module_morphism as free_module_morphism
330
import vector_space_homspace
331
from sage.matrix.matrix import is_Matrix
332
333
def linear_transformation(arg0, arg1=None, arg2=None, side='left'):
334
r"""
335
Create a linear transformation from a variety of possible inputs.
336
337
FORMATS:
338
339
In the following, ``D`` and ``C`` are vector spaces over
340
the same field that are the domain and codomain
341
(respectively) of the linear transformation.
342
343
``side`` is a keyword that is either 'left' or 'right'.
344
When a matrix is used to specify a linear transformation,
345
as in the first two call formats below, you may specify
346
if the function is given by matrix multiplication with
347
the vector on the left, or the vector on the right.
348
The default is 'left'. Internally representations are
349
always carried as the 'left' version, and the default
350
text representation is this version. However, the matrix
351
representation may be obtained as either version, no matter
352
how it is created.
353
354
- ``linear_transformation(A, side='left')``
355
356
Where ``A`` is a matrix. The domain and codomain are inferred
357
from the dimension of the matrix and the base ring of the matrix.
358
The base ring must be a field, or have its fraction field implemented
359
in Sage.
360
361
- ``linear_transformation(D, C, A, side='left')``
362
363
``A`` is a matrix that behaves as above. However, now the domain
364
and codomain are given explicitly. The matrix is checked for
365
compatibility with the domain and codomain. Additionally, the
366
domain and codomain may be supplied with alternate ("user") bases
367
and the matrix is interpreted as being a representation relative
368
to those bases.
369
370
- ``linear_transformation(D, C, f)``
371
372
``f`` is any function that can be applied to the basis elements of the
373
domain and that produces elements of the codomain. The linear
374
transformation returned is the unique linear transformation that
375
extends this mapping on the basis elements. ``f`` may come from a
376
function defined by a Python ``def`` statement, or may be defined as a
377
``lambda`` function.
378
379
Alternatively, ``f`` may be specified by a callable symbolic function,
380
see the examples below for a demonstration.
381
382
- ``linear_transformation(D, C, images)``
383
384
``images`` is a list, or tuple, of codomain elements, equal in number
385
to the size of the basis of the domain. Each basis element of the domain
386
is mapped to the corresponding element of the ``images`` list, and the
387
linear transformation returned is the unique linear transfromation that
388
extends this mapping.
389
390
OUTPUT:
391
392
A linear transformation described by the input. This is a
393
"vector space morphism", an object of the class
394
:class:`sage.modules.vector_space_morphism`.
395
396
EXAMPLES:
397
398
We can define a linear transformation with just a matrix, understood to
399
act on a vector placed on one side or the other. The field for the
400
vector spaces used as domain and codomain is obtained from the base
401
ring of the matrix, possibly promoting to a fraction field. ::
402
403
sage: A = matrix(ZZ, [[1, -1, 4], [2, 0, 5]])
404
sage: phi = linear_transformation(A)
405
sage: phi
406
Vector space morphism represented by the matrix:
407
[ 1 -1 4]
408
[ 2 0 5]
409
Domain: Vector space of dimension 2 over Rational Field
410
Codomain: Vector space of dimension 3 over Rational Field
411
sage: phi([1/2, 5])
412
(21/2, -1/2, 27)
413
414
sage: B = matrix(Integers(7), [[1, 2, 1], [3, 5, 6]])
415
sage: rho = linear_transformation(B, side='right')
416
sage: rho
417
Vector space morphism represented by the matrix:
418
[1 3]
419
[2 5]
420
[1 6]
421
Domain: Vector space of dimension 3 over Ring of integers modulo 7
422
Codomain: Vector space of dimension 2 over Ring of integers modulo 7
423
sage: rho([2, 4, 6])
424
(2, 6)
425
426
We can define a linear transformation with a matrix, while explicitly
427
giving the domain and codomain. Matrix entries will be coerced into the
428
common field of scalars for the vector spaces. ::
429
430
sage: D = QQ^3
431
sage: C = QQ^2
432
sage: A = matrix([[1, 7], [2, -1], [0, 5]])
433
sage: A.parent()
434
Full MatrixSpace of 3 by 2 dense matrices over Integer Ring
435
sage: zeta = linear_transformation(D, C, A)
436
sage: zeta.matrix().parent()
437
Full MatrixSpace of 3 by 2 dense matrices over Rational Field
438
sage: zeta
439
Vector space morphism represented by the matrix:
440
[ 1 7]
441
[ 2 -1]
442
[ 0 5]
443
Domain: Vector space of dimension 3 over Rational Field
444
Codomain: Vector space of dimension 2 over Rational Field
445
446
Matrix representations are relative to the bases for the domain
447
and codomain. ::
448
449
sage: u = vector(QQ, [1, -1])
450
sage: v = vector(QQ, [2, 3])
451
sage: D = (QQ^2).subspace_with_basis([u, v])
452
sage: x = vector(QQ, [2, 1])
453
sage: y = vector(QQ, [-1, 4])
454
sage: C = (QQ^2).subspace_with_basis([x, y])
455
sage: A = matrix(QQ, [[2, 5], [3, 7]])
456
sage: psi = linear_transformation(D, C, A)
457
sage: psi
458
Vector space morphism represented by the matrix:
459
[2 5]
460
[3 7]
461
Domain: Vector space of degree 2 and dimension 2 over Rational Field
462
User basis matrix:
463
[ 1 -1]
464
[ 2 3]
465
Codomain: Vector space of degree 2 and dimension 2 over Rational Field
466
User basis matrix:
467
[ 2 1]
468
[-1 4]
469
sage: psi(u) == 2*x + 5*y
470
True
471
sage: psi(v) == 3*x + 7*y
472
True
473
474
Functions that act on the domain may be used to compute images of
475
the domain's basis elements, and this mapping can be extended to
476
a unique linear transformation. The function may be a Python
477
function (via ``def`` or ``lambda``) or a Sage symbolic function. ::
478
479
sage: def g(x):
480
... return vector(QQ, [2*x[0]+x[2], 5*x[1]])
481
...
482
sage: phi = linear_transformation(QQ^3, QQ^2, g)
483
sage: phi
484
Vector space morphism represented by the matrix:
485
[2 0]
486
[0 5]
487
[1 0]
488
Domain: Vector space of dimension 3 over Rational Field
489
Codomain: Vector space of dimension 2 over Rational Field
490
491
sage: f = lambda x: vector(QQ, [2*x[0]+x[2], 5*x[1]])
492
sage: rho = linear_transformation(QQ^3, QQ^2, f)
493
sage: rho
494
Vector space morphism represented by the matrix:
495
[2 0]
496
[0 5]
497
[1 0]
498
Domain: Vector space of dimension 3 over Rational Field
499
Codomain: Vector space of dimension 2 over Rational Field
500
501
sage: x, y, z = var('x y z')
502
sage: h(x, y, z) = [2*x + z, 5*y]
503
sage: zeta = linear_transformation(QQ^3, QQ^2, h)
504
sage: zeta
505
Vector space morphism represented by the matrix:
506
[2 0]
507
[0 5]
508
[1 0]
509
Domain: Vector space of dimension 3 over Rational Field
510
Codomain: Vector space of dimension 2 over Rational Field
511
512
sage: phi == rho
513
True
514
sage: rho == zeta
515
True
516
517
518
We create a linear transformation relative to non-standard bases,
519
and capture its representation relative to standard bases. With this, we
520
can build functions that create the same linear transformation relative
521
to the nonstandard bases. ::
522
523
sage: u = vector(QQ, [1, -1])
524
sage: v = vector(QQ, [2, 3])
525
sage: D = (QQ^2).subspace_with_basis([u, v])
526
sage: x = vector(QQ, [2, 1])
527
sage: y = vector(QQ, [-1, 4])
528
sage: C = (QQ^2).subspace_with_basis([x, y])
529
sage: A = matrix(QQ, [[2, 5], [3, 7]])
530
sage: psi = linear_transformation(D, C, A)
531
sage: rho = psi.restrict_codomain(QQ^2).restrict_domain(QQ^2)
532
sage: rho.matrix()
533
[ -4/5 97/5]
534
[ 1/5 -13/5]
535
536
sage: f = lambda x: vector(QQ, [(-4/5)*x[0] + (1/5)*x[1], (97/5)*x[0] + (-13/5)*x[1]])
537
sage: psi = linear_transformation(D, C, f)
538
sage: psi.matrix()
539
[2 5]
540
[3 7]
541
542
sage: s, t = var('s t')
543
sage: h(s, t) = [(-4/5)*s + (1/5)*t, (97/5)*s + (-13/5)*t]
544
sage: zeta = linear_transformation(D, C, h)
545
sage: zeta.matrix()
546
[2 5]
547
[3 7]
548
549
Finally, we can give an explicit list of images for the basis
550
elements of the domain. ::
551
552
sage: x = polygen(QQ)
553
sage: F.<a> = NumberField(x^3+x+1)
554
sage: u = vector(F, [1, a, a^2])
555
sage: v = vector(F, [a, a^2, 2])
556
sage: w = u + v
557
sage: D = F^3
558
sage: C = F^3
559
sage: rho = linear_transformation(D, C, [u, v, w])
560
sage: rho.matrix()
561
[ 1 a a^2]
562
[ a a^2 2]
563
[ a + 1 a^2 + a a^2 + 2]
564
sage: C = (F^3).subspace_with_basis([u, v])
565
sage: D = (F^3).subspace_with_basis([u, v])
566
sage: psi = linear_transformation(C, D, [u+v, u-v])
567
sage: psi.matrix()
568
[ 1 1]
569
[ 1 -1]
570
571
TESTS:
572
573
We test some bad inputs. First, the wrong things in the wrong places. ::
574
575
sage: linear_transformation('junk')
576
Traceback (most recent call last):
577
...
578
TypeError: first argument must be a matrix or a vector space, not junk
579
580
sage: linear_transformation(QQ^2, QQ^3, 'stuff')
581
Traceback (most recent call last):
582
...
583
TypeError: third argument must be a matrix, function, or list of images, not stuff
584
585
sage: linear_transformation(QQ^2, 'garbage')
586
Traceback (most recent call last):
587
...
588
TypeError: if first argument is a vector space, then second argument must be a vector space, not garbage
589
590
sage: linear_transformation(QQ^2, Integers(7)^2)
591
Traceback (most recent call last):
592
...
593
TypeError: vector spaces must have the same field of scalars, not Rational Field and Ring of integers modulo 7
594
595
Matrices must be over a field (or a ring that can be promoted to a field),
596
and of the right size. ::
597
598
sage: linear_transformation(matrix(Integers(6), [[2, 3],[4, 5]]))
599
Traceback (most recent call last):
600
...
601
TypeError: matrix must have entries from a field, or a ring with a fraction field, not Ring of integers modulo 6
602
603
sage: A = matrix(QQ, 3, 4, range(12))
604
sage: linear_transformation(QQ^4, QQ^4, A)
605
Traceback (most recent call last):
606
...
607
TypeError: domain dimension is incompatible with matrix size
608
609
sage: linear_transformation(QQ^3, QQ^3, A, side='right')
610
Traceback (most recent call last):
611
...
612
TypeError: domain dimension is incompatible with matrix size
613
614
sage: linear_transformation(QQ^3, QQ^3, A)
615
Traceback (most recent call last):
616
...
617
TypeError: codomain dimension is incompatible with matrix size
618
619
sage: linear_transformation(QQ^4, QQ^4, A, side='right')
620
Traceback (most recent call last):
621
...
622
TypeError: codomain dimension is incompatible with matrix size
623
624
Lists of images can be of the wrong number, or not really
625
elements of the codomain. ::
626
627
sage: linear_transformation(QQ^3, QQ^2, [vector(QQ, [1,2])])
628
Traceback (most recent call last):
629
...
630
ValueError: number of images should equal the size of the domain's basis (=3), not 1
631
632
sage: C = (QQ^2).subspace_with_basis([vector(QQ, [1,1])])
633
sage: linear_transformation(QQ^1, C, [vector(QQ, [1,2])])
634
Traceback (most recent call last):
635
...
636
ArithmeticError: some proposed image is not in the codomain, because
637
element (= [1, 2]) is not in free module
638
639
640
Functions may not apply properly to domain elements,
641
or return values outside the codomain. ::
642
643
sage: f = lambda x: vector(QQ, [x[0], x[4]])
644
sage: linear_transformation(QQ^3, QQ^2, f)
645
Traceback (most recent call last):
646
...
647
ValueError: function cannot be applied properly to some basis element because
648
index out of range
649
650
sage: f = lambda x: vector(QQ, [x[0], x[1]])
651
sage: C = (QQ^2).span([vector(QQ, [1, 1])])
652
sage: linear_transformation(QQ^2, C, f)
653
Traceback (most recent call last):
654
...
655
ArithmeticError: some image of the function is not in the codomain, because
656
element (= [1, 0]) is not in free module
657
658
A Sage symbolic function can come in a variety of forms that are
659
not representative of a linear transformation. ::
660
661
sage: x, y = var('x, y')
662
sage: f(x, y) = [y, x, y]
663
sage: linear_transformation(QQ^3, QQ^3, f)
664
Traceback (most recent call last):
665
...
666
ValueError: symbolic function has the wrong number of inputs for domain
667
668
sage: linear_transformation(QQ^2, QQ^2, f)
669
Traceback (most recent call last):
670
...
671
ValueError: symbolic function has the wrong number of outputs for codomain
672
673
sage: x, y = var('x y')
674
sage: f(x, y) = [y, x*y]
675
sage: linear_transformation(QQ^2, QQ^2, f)
676
Traceback (most recent call last):
677
...
678
ValueError: symbolic function must be linear in all the inputs:
679
unable to convert y to a rational
680
681
sage: x, y = var('x y')
682
sage: f(x, y) = [x, 2*y]
683
sage: C = (QQ^2).span([vector(QQ, [1, 1])])
684
sage: linear_transformation(QQ^2, C, f)
685
Traceback (most recent call last):
686
...
687
ArithmeticError: some image of the function is not in the codomain, because
688
element (= [1, 0]) is not in free module
689
"""
690
from sage.matrix.constructor import matrix
691
from sage.modules.module import is_VectorSpace
692
from sage.modules.free_module import VectorSpace
693
from sage.categories.homset import Hom
694
from sage.symbolic.ring import SymbolicRing
695
from sage.modules.vector_callable_symbolic_dense import Vector_callable_symbolic_dense
696
from inspect import isfunction
697
698
if not side in ['left', 'right']:
699
raise ValueError("side must be 'left' or 'right', not {0}".format(side))
700
if not (is_Matrix(arg0) or is_VectorSpace(arg0)):
701
raise TypeError('first argument must be a matrix or a vector space, not {0}'.format(arg0))
702
if is_Matrix(arg0):
703
R = arg0.base_ring()
704
if not R.is_field():
705
try:
706
R = R.fraction_field()
707
except (NotImplementedError, TypeError):
708
msg = 'matrix must have entries from a field, or a ring with a fraction field, not {0}'
709
raise TypeError(msg.format(R))
710
if side == 'right':
711
arg0 = arg0.transpose()
712
side = 'left'
713
arg2 = arg0
714
arg0 = VectorSpace(R, arg2.nrows())
715
arg1 = VectorSpace(R, arg2.ncols())
716
elif is_VectorSpace(arg0):
717
if not is_VectorSpace(arg1):
718
msg = 'if first argument is a vector space, then second argument must be a vector space, not {0}'
719
raise TypeError(msg.format(arg1))
720
if arg0.base_ring() != arg1.base_ring():
721
msg = 'vector spaces must have the same field of scalars, not {0} and {1}'
722
raise TypeError(msg.format(arg0.base_ring(), arg1.base_ring()))
723
724
# Now arg0 = domain D, arg1 = codomain C, and
725
# both are vector spaces with common field of scalars
726
# use these to make a VectorSpaceHomSpace
727
# arg2 might be a matrix that began in arg0
728
D = arg0
729
C = arg1
730
H = Hom(D, C, category=None)
731
732
# Examine arg2 as the "rule" for the linear transformation
733
# Pass on matrices, Python functions and lists to homspace call
734
# Convert symbolic function here, to a matrix
735
if is_Matrix(arg2):
736
if side == 'right':
737
arg2 = arg2.transpose()
738
elif isinstance(arg2, (list, tuple)):
739
pass
740
elif isfunction(arg2):
741
pass
742
elif isinstance(arg2, Vector_callable_symbolic_dense):
743
args = arg2.parent().base_ring()._arguments
744
exprs = arg2.change_ring(SymbolicRing())
745
m = len(args)
746
n = len(exprs)
747
if m != D.degree():
748
raise ValueError('symbolic function has the wrong number of inputs for domain')
749
if n != C.degree():
750
raise ValueError('symbolic function has the wrong number of outputs for codomain')
751
arg2 = [[e.coeff(a) for e in exprs] for a in args]
752
try:
753
arg2 = matrix(D.base_ring(), m, n, arg2)
754
except TypeError, e:
755
msg = 'symbolic function must be linear in all the inputs:\n' + e.args[0]
756
raise ValueError(msg)
757
# have matrix with respect to standard bases, now consider user bases
758
images = [v*arg2 for v in D.basis()]
759
try:
760
arg2 = matrix([C.coordinates(C(a)) for a in images])
761
except (ArithmeticError, TypeError), e:
762
msg = 'some image of the function is not in the codomain, because\n' + e.args[0]
763
raise ArithmeticError(msg)
764
else:
765
msg = 'third argument must be a matrix, function, or list of images, not {0}'
766
raise TypeError(msg.format(arg2))
767
768
# arg2 now compatible with homspace H call method
769
# __init__ will check matrix sizes versus domain/codomain dimensions
770
return H(arg2)
771
772
def is_VectorSpaceMorphism(x):
773
r"""
774
Returns ``True`` if ``x`` is a vector space morphism (a linear transformation).
775
776
INPUT:
777
778
``x`` - anything
779
780
OUTPUT:
781
782
``True`` only if ``x`` is an instance of a vector space morphism,
783
which are also known as linear transformations.
784
785
EXAMPLES::
786
787
sage: V = QQ^2; f = V.hom([V.1,-2*V.0])
788
sage: sage.modules.vector_space_morphism.is_VectorSpaceMorphism(f)
789
True
790
sage: sage.modules.vector_space_morphism.is_VectorSpaceMorphism('junk')
791
False
792
"""
793
return isinstance(x, VectorSpaceMorphism)
794
795
796
class VectorSpaceMorphism(free_module_morphism.FreeModuleMorphism):
797
798
def __init__(self, homspace, A):
799
r"""
800
Create a linear transformation, a morphism between vector spaces.
801
802
INPUT:
803
804
- ``homspace`` - a homspace (of vector spaces) to serve
805
as a parent for the linear transformation and a home for
806
the domain and codomain of the morphism
807
- ``A`` - a matrix representing the linear transformation,
808
which will act on vectors placed to the left of the matrix
809
810
EXAMPLES:
811
812
Nominally, we require a homspace to hold the domain
813
and codomain and a matrix representation of the morphism
814
(linear transformation). ::
815
816
sage: from sage.modules.vector_space_homspace import VectorSpaceHomspace
817
sage: from sage.modules.vector_space_morphism import VectorSpaceMorphism
818
sage: H = VectorSpaceHomspace(QQ^3, QQ^2)
819
sage: A = matrix(QQ, 3, 2, range(6))
820
sage: zeta = VectorSpaceMorphism(H, A)
821
sage: zeta
822
Vector space morphism represented by the matrix:
823
[0 1]
824
[2 3]
825
[4 5]
826
Domain: Vector space of dimension 3 over Rational Field
827
Codomain: Vector space of dimension 2 over Rational Field
828
829
See the constructor,
830
:func:`sage.modules.vector_space_morphism.linear_transformation`
831
for another way to create linear transformations.
832
833
The ``.hom()`` method of a vector space will create a vector
834
space morphism. ::
835
836
sage: V = QQ^3; W = V.subspace_with_basis([[1,2,3], [-1,2,5/3], [0,1,-1]])
837
sage: phi = V.hom(matrix(QQ, 3, range(9)), codomain=W) # indirect doctest
838
sage: type(phi)
839
<class 'sage.modules.vector_space_morphism.VectorSpaceMorphism'>
840
841
A matrix may be coerced into a vector space homspace to
842
create a vector space morphism. ::
843
844
sage: from sage.modules.vector_space_homspace import VectorSpaceHomspace
845
sage: H = VectorSpaceHomspace(QQ^3, QQ^2)
846
sage: A = matrix(QQ, 3, 2, range(6))
847
sage: rho = H(A) # indirect doctest
848
sage: type(rho)
849
<class 'sage.modules.vector_space_morphism.VectorSpaceMorphism'>
850
"""
851
if not vector_space_homspace.is_VectorSpaceHomspace(homspace):
852
raise TypeError, 'homspace must be a vector space hom space, not {0}'.format(homspace)
853
if isinstance(A, matrix_morphism.MatrixMorphism):
854
A = A.matrix()
855
if not is_Matrix(A):
856
msg = 'input must be a matrix representation or another matrix morphism, not {0}'
857
raise TypeError(msg.format(A))
858
# now have a vector space homspace, and a matrix, check compatibility
859
860
if homspace.domain().dimension() != A.nrows():
861
raise TypeError('domain dimension is incompatible with matrix size')
862
if homspace.codomain().dimension() != A.ncols():
863
raise TypeError('codomain dimension is incompatible with matrix size')
864
865
A = homspace._matrix_space()(A)
866
free_module_morphism.FreeModuleMorphism.__init__(self, homspace, A)
867
868
def is_invertible(self):
869
r"""
870
Determines if the vector space morphism has an inverse.
871
872
OUTPUT:
873
874
``True`` if the vector space morphism is invertible, otherwise
875
``False``.
876
877
EXAMPLES:
878
879
If the dimension of the domain does not match the dimension
880
of the codomain, then the morphism cannot be invertible. ::
881
882
sage: V = QQ^3
883
sage: U = V.subspace_with_basis([V.0 + V.1, 2*V.1 + 3*V.2])
884
sage: phi = V.hom([U.0, U.0 + U.1, U.0 - U.1], U)
885
sage: phi.is_invertible()
886
False
887
888
An invertible linear transformation. ::
889
890
sage: A = matrix(QQ, 3, [[-3, 5, -5], [4, -7, 7], [6, -8, 10]])
891
sage: A.determinant()
892
2
893
sage: H = Hom(QQ^3, QQ^3)
894
sage: rho = H(A)
895
sage: rho.is_invertible()
896
True
897
898
A non-invertible linear transformation, an endomorphism of
899
a vector space over a finite field. ::
900
901
sage: F.<a> = GF(11^2)
902
sage: A = matrix(F, [[6*a + 3, 8*a + 2, 10*a + 3],
903
... [2*a + 7, 4*a + 3, 2*a + 3],
904
... [9*a + 2, 10*a + 10, 3*a + 3]])
905
sage: A.nullity()
906
1
907
sage: E = End(F^3)
908
sage: zeta = E(A)
909
sage: zeta.is_invertible()
910
False
911
"""
912
# endomorphism or not, this is equivalent to invertibility of
913
# the matrix representation, so any test of this will suffice
914
m = self.matrix()
915
if not m.is_square():
916
return False
917
return m.rank() == m.ncols()
918
919
def _latex_(self):
920
r"""
921
A LaTeX representation of this vector space morphism.
922
923
EXAMPLE::
924
925
sage: H = Hom(QQ^3, QQ^2)
926
sage: f = H(matrix(3, 2, range(6)))
927
sage: f._latex_().split(' ')
928
['\\texttt{vector', 'space', 'morphism', 'from',
929
'}\n\\Bold{Q}^{3}\\texttt{', 'to', '}\n\\Bold{Q}^{2}\\texttt{',
930
'represented', 'by', 'the', 'matrix',
931
'}\n\\left(\\begin{array}{rr}\n0', '&', '1',
932
'\\\\\n2', '&', '3', '\\\\\n4', '&', '5\n\\end{array}\\right)']
933
"""
934
from sage.misc.latex import latex
935
s = ('\\texttt{vector space morphism from }\n', self.domain()._latex_(),
936
'\\texttt{ to }\n', self.codomain()._latex_(),
937
'\\texttt{ represented by the matrix }\n', self.matrix()._latex_())
938
return ''.join(s)
939
940
def _repr_(self):
941
r"""
942
A text representation of this vector space morphism.
943
944
EXAMPLE::
945
946
sage: H = Hom(QQ^3, QQ^2)
947
sage: f = H(matrix(3, 2, range(6)))
948
sage: f._repr_().split(' ')
949
['Vector', 'space', 'morphism', 'represented', 'by',
950
'the', 'matrix:\n[0', '1]\n[2', '3]\n[4', '5]\nDomain:',
951
'Vector', 'space', 'of', 'dimension', '3', 'over',
952
'Rational', 'Field\nCodomain:', 'Vector', 'space', 'of',
953
'dimension', '2', 'over', 'Rational', 'Field']
954
"""
955
m = self.matrix()
956
msg = ("Vector space morphism represented by the matrix:\n",
957
"{0}\n",
958
"Domain: {1}\n",
959
"Codomain: {2}")
960
return ''.join(msg).format(m, self.domain(), self.codomain())
961
962