Morphisms of vector spaces (linear transformations)

AUTHOR:

  • Rob Beezer: (2011-06-29)

A vector space morphism is a homomorphism between vector spaces, better known as a linear transformation. These are a specialization of Sage’s free module homomorphisms. (A free module is like a vector space, but with scalars from a ring that may not be a field.) So references to free modules in the documentation or error messages should be understood as simply reflecting a more general situation.

Creation

The constructor linear_transformation() is designed to accept a variety of inputs that can define a linear transformation. See the documentation of the function for all the possibilities. Here we give two.

First a matrix representation. By default input matrices are understood to act on vectors placed to left of the matrix. Optionally, an input matrix can be described as acting on vectors placed to the right.

sage: A = matrix(QQ, [[-1, 2, 3], [4, 2, 0]])
sage: phi = linear_transformation(A)
sage: phi
Vector space morphism represented by the matrix:
[-1  2  3]
[ 4  2  0]
Domain:   Vector space of dimension 2 over Rational Field
Codomain: Vector space of dimension 3 over Rational Field
sage: phi([2, -3])
(-14, -2, 6)
>>> from sage.all import *
>>> A = matrix(QQ, [[-Integer(1), Integer(2), Integer(3)], [Integer(4), Integer(2), Integer(0)]])
>>> phi = linear_transformation(A)
>>> phi
Vector space morphism represented by the matrix:
[-1  2  3]
[ 4  2  0]
Domain:   Vector space of dimension 2 over Rational Field
Codomain: Vector space of dimension 3 over Rational Field
>>> phi([Integer(2), -Integer(3)])
(-14, -2, 6)
A = matrix(QQ, [[-1, 2, 3], [4, 2, 0]])
phi = linear_transformation(A)
phi
phi([2, -3])

A symbolic function can be used to specify the “rule” for a linear transformation, along with explicit descriptions of the domain and codomain.

sage: # needs sage.symbolic
sage: F = Integers(13)
sage: D = F^3
sage: C = F^2
sage: x, y, z = var('x y z')
sage: f(x, y, z) = [2*x + 3*y + 5*z, x + z]
sage: rho = linear_transformation(D, C, f)
sage: f(1, 2, 3)
(23, 4)
sage: rho([1, 2, 3])
(10, 4)
>>> from sage.all import *
>>> # needs sage.symbolic
>>> F = Integers(Integer(13))
>>> D = F**Integer(3)
>>> C = F**Integer(2)
>>> x, y, z = var('x y z')
>>> __tmp__=var("x,y,z"); f = symbolic_expression([Integer(2)*x + Integer(3)*y + Integer(5)*z, x + z]).function(x,y,z)
>>> rho = linear_transformation(D, C, f)
>>> f(Integer(1), Integer(2), Integer(3))
(23, 4)
>>> rho([Integer(1), Integer(2), Integer(3)])
(10, 4)
# needs sage.symbolic
F = Integers(13)
D = F^3
C = F^2
x, y, z = var('x y z')
f(x, y, z) = [2*x + 3*y + 5*z, x + z]
rho = linear_transformation(D, C, f)
f(1, 2, 3)
rho([1, 2, 3])

A “vector space homspace” is the set of all linear transformations between two vector spaces. Various input can be coerced into a homspace to create a linear transformation. See sage.modules.vector_space_homspace for more.

sage: D = QQ^4
sage: C = QQ^2
sage: hom_space = Hom(D, C)
sage: images = [[1, 3], [2, -1], [4, 0], [3, 7]]
sage: zeta = hom_space(images)
sage: zeta
Vector space morphism represented by the matrix:
[ 1  3]
[ 2 -1]
[ 4  0]
[ 3  7]
Domain:   Vector space of dimension 4 over Rational Field
Codomain: Vector space of dimension 2 over Rational Field
>>> from sage.all import *
>>> D = QQ**Integer(4)
>>> C = QQ**Integer(2)
>>> hom_space = Hom(D, C)
>>> images = [[Integer(1), Integer(3)], [Integer(2), -Integer(1)], [Integer(4), Integer(0)], [Integer(3), Integer(7)]]
>>> zeta = hom_space(images)
>>> zeta
Vector space morphism represented by the matrix:
[ 1  3]
[ 2 -1]
[ 4  0]
[ 3  7]
Domain:   Vector space of dimension 4 over Rational Field
Codomain: Vector space of dimension 2 over Rational Field
D = QQ^4
C = QQ^2
hom_space = Hom(D, C)
images = [[1, 3], [2, -1], [4, 0], [3, 7]]
zeta = hom_space(images)
zeta

A homomorphism may also be created via a method on the domain.

sage: # needs sage.rings.number_field sage.symbolic
sage: F = QQ[sqrt(3)]
sage: a = F.gen(0)
sage: D = F^2
sage: C = F^2
sage: A = matrix(F, [[a, 1], [2*a, 2]])
sage: psi = D.hom(A, C)
sage: psi
Vector space morphism represented by the matrix:
[  sqrt3       1]
[2*sqrt3       2]
Domain:   Vector space of dimension 2 over Number Field in sqrt3
          with defining polynomial x^2 - 3 with sqrt3 = 1.732050807568878?
Codomain: Vector space of dimension 2 over Number Field in sqrt3
          with defining polynomial x^2 - 3 with sqrt3 = 1.732050807568878?
sage: psi([1, 4])
(9*sqrt3, 9)
>>> from sage.all import *
>>> # needs sage.rings.number_field sage.symbolic
>>> F = QQ[sqrt(Integer(3))]
>>> a = F.gen(Integer(0))
>>> D = F**Integer(2)
>>> C = F**Integer(2)
>>> A = matrix(F, [[a, Integer(1)], [Integer(2)*a, Integer(2)]])
>>> psi = D.hom(A, C)
>>> psi
Vector space morphism represented by the matrix:
[  sqrt3       1]
[2*sqrt3       2]
Domain:   Vector space of dimension 2 over Number Field in sqrt3
          with defining polynomial x^2 - 3 with sqrt3 = 1.732050807568878?
Codomain: Vector space of dimension 2 over Number Field in sqrt3
          with defining polynomial x^2 - 3 with sqrt3 = 1.732050807568878?
>>> psi([Integer(1), Integer(4)])
(9*sqrt3, 9)
# needs sage.rings.number_field sage.symbolic
F = QQ[sqrt(3)]
a = F.gen(0)
D = F^2
C = F^2
A = matrix(F, [[a, 1], [2*a, 2]])
psi = D.hom(A, C)
psi
psi([1, 4])

Properties

Many natural properties of a linear transformation can be computed. Some of these are more general methods of objects in the classes sage.modules.free_module_morphism.FreeModuleMorphism and sage.modules.matrix_morphism.MatrixMorphism.

Values are computed in a natural way, an inverse image of an element can be computed with the lift() method, when the inverse image actually exists.

sage: A = matrix(QQ, [[1,2], [2,4], [3,6]])
sage: phi = linear_transformation(A)
sage: phi([1,2,0])
(5, 10)
sage: phi.lift([10, 20])
(10, 0, 0)
sage: phi.lift([100, 100])
Traceback (most recent call last):
...
ValueError: element is not in the image
>>> from sage.all import *
>>> A = matrix(QQ, [[Integer(1),Integer(2)], [Integer(2),Integer(4)], [Integer(3),Integer(6)]])
>>> phi = linear_transformation(A)
>>> phi([Integer(1),Integer(2),Integer(0)])
(5, 10)
>>> phi.lift([Integer(10), Integer(20)])
(10, 0, 0)
>>> phi.lift([Integer(100), Integer(100)])
Traceback (most recent call last):
...
ValueError: element is not in the image
A = matrix(QQ, [[1,2], [2,4], [3,6]])
phi = linear_transformation(A)
phi([1,2,0])
phi.lift([10, 20])
phi.lift([100, 100])

Images and pre-images can be computed as vector spaces.

sage: A = matrix(QQ, [[1,2], [2,4], [3,6]])
sage: phi = linear_transformation(A)
sage: phi.image()
Vector space of degree 2 and dimension 1 over Rational Field
Basis matrix:
[1 2]

sage: phi.inverse_image( (QQ^2).span([[1,2]]) )
Vector space of degree 3 and dimension 3 over Rational Field
Basis matrix:
[1 0 0]
[0 1 0]
[0 0 1]

sage: phi.inverse_image( (QQ^2).span([[1,1]]) )
Vector space of degree 3 and dimension 2 over Rational Field
Basis matrix:
[   1    0 -1/3]
[   0    1 -2/3]
>>> from sage.all import *
>>> A = matrix(QQ, [[Integer(1),Integer(2)], [Integer(2),Integer(4)], [Integer(3),Integer(6)]])
>>> phi = linear_transformation(A)
>>> phi.image()
Vector space of degree 2 and dimension 1 over Rational Field
Basis matrix:
[1 2]

>>> phi.inverse_image( (QQ**Integer(2)).span([[Integer(1),Integer(2)]]) )
Vector space of degree 3 and dimension 3 over Rational Field
Basis matrix:
[1 0 0]
[0 1 0]
[0 0 1]

>>> phi.inverse_image( (QQ**Integer(2)).span([[Integer(1),Integer(1)]]) )
Vector space of degree 3 and dimension 2 over Rational Field
Basis matrix:
[   1    0 -1/3]
[   0    1 -2/3]
A = matrix(QQ, [[1,2], [2,4], [3,6]])
phi = linear_transformation(A)
phi.image()
phi.inverse_image( (QQ^2).span([[1,2]]) )
phi.inverse_image( (QQ^2).span([[1,1]]) )

Injectivity and surjectivity can be checked.

sage: A = matrix(QQ, [[1,2], [2,4], [3,6]])
sage: phi = linear_transformation(A)
sage: phi.is_injective()
False
sage: phi.is_surjective()
False
>>> from sage.all import *
>>> A = matrix(QQ, [[Integer(1),Integer(2)], [Integer(2),Integer(4)], [Integer(3),Integer(6)]])
>>> phi = linear_transformation(A)
>>> phi.is_injective()
False
>>> phi.is_surjective()
False
A = matrix(QQ, [[1,2], [2,4], [3,6]])
phi = linear_transformation(A)
phi.is_injective()
phi.is_surjective()

Restrictions and representations

It is possible to restrict the domain and codomain of a linear transformation to make a new linear transformation. We will use those commands to replace the domain and codomain by equal vector spaces, but with alternate bases. The point here is that the matrix representation used to represent linear transformations are relative to the bases of both the domain and codomain.

sage: A = graphs.PetersenGraph().adjacency_matrix()                                 # needs sage.graphs
sage: V = QQ^10
sage: phi = linear_transformation(V, V, A)                                          # needs sage.graphs
sage: phi                                                                           # needs sage.graphs
Vector space morphism represented by the matrix:
[0 1 0 0 1 1 0 0 0 0]
[1 0 1 0 0 0 1 0 0 0]
[0 1 0 1 0 0 0 1 0 0]
[0 0 1 0 1 0 0 0 1 0]
[1 0 0 1 0 0 0 0 0 1]
[1 0 0 0 0 0 0 1 1 0]
[0 1 0 0 0 0 0 0 1 1]
[0 0 1 0 0 1 0 0 0 1]
[0 0 0 1 0 1 1 0 0 0]
[0 0 0 0 1 0 1 1 0 0]
Domain:   Vector space of dimension 10 over Rational Field
Codomain: Vector space of dimension 10 over Rational Field

sage: # needs sage.graphs
sage: B1 = [V.gen(i) + V.gen(i+1) for i in range(9)] + [V.gen(9)]
sage: B2 = [V.gen(0)] + [-V.gen(i-1) + V.gen(i) for i in range(1,10)]
sage: D = V.subspace_with_basis(B1)
sage: C = V.subspace_with_basis(B2)
sage: rho = phi.restrict_codomain(C)
sage: zeta = rho.restrict_domain(D)
sage: zeta
Vector space morphism represented by the matrix:
[6 5 4 3 3 2 1 0 0 0]
[6 5 4 3 2 2 2 1 0 0]
[6 6 5 4 3 2 2 2 1 0]
[6 5 5 4 3 2 2 2 2 1]
[6 4 4 4 3 3 3 3 2 1]
[6 5 4 4 4 4 4 4 3 1]
[6 6 5 4 4 4 3 3 3 2]
[6 6 6 5 4 4 2 1 1 1]
[6 6 6 6 5 4 3 1 0 0]
[3 3 3 3 3 2 2 1 0 0]
Domain:   Vector space of degree 10 and dimension 10 over Rational Field
          User basis matrix:
          [1 1 0 0 0 0 0 0 0 0]
          [0 1 1 0 0 0 0 0 0 0]
          [0 0 1 1 0 0 0 0 0 0]
          [0 0 0 1 1 0 0 0 0 0]
          [0 0 0 0 1 1 0 0 0 0]
          [0 0 0 0 0 1 1 0 0 0]
          [0 0 0 0 0 0 1 1 0 0]
          [0 0 0 0 0 0 0 1 1 0]
          [0 0 0 0 0 0 0 0 1 1]
          [0 0 0 0 0 0 0 0 0 1]
Codomain: Vector space of degree 10 and dimension 10 over Rational Field
          User basis matrix:
          [ 1  0  0  0  0  0  0  0  0  0]
          [-1  1  0  0  0  0  0  0  0  0]
          [ 0 -1  1  0  0  0  0  0  0  0]
          [ 0  0 -1  1  0  0  0  0  0  0]
          [ 0  0  0 -1  1  0  0  0  0  0]
          [ 0  0  0  0 -1  1  0  0  0  0]
          [ 0  0  0  0  0 -1  1  0  0  0]
          [ 0  0  0  0  0  0 -1  1  0  0]
          [ 0  0  0  0  0  0  0 -1  1  0]
          [ 0  0  0  0  0  0  0  0 -1  1]
>>> from sage.all import *
>>> A = graphs.PetersenGraph().adjacency_matrix()                                 # needs sage.graphs
>>> V = QQ**Integer(10)
>>> phi = linear_transformation(V, V, A)                                          # needs sage.graphs
>>> phi                                                                           # needs sage.graphs
Vector space morphism represented by the matrix:
[0 1 0 0 1 1 0 0 0 0]
[1 0 1 0 0 0 1 0 0 0]
[0 1 0 1 0 0 0 1 0 0]
[0 0 1 0 1 0 0 0 1 0]
[1 0 0 1 0 0 0 0 0 1]
[1 0 0 0 0 0 0 1 1 0]
[0 1 0 0 0 0 0 0 1 1]
[0 0 1 0 0 1 0 0 0 1]
[0 0 0 1 0 1 1 0 0 0]
[0 0 0 0 1 0 1 1 0 0]
Domain:   Vector space of dimension 10 over Rational Field
Codomain: Vector space of dimension 10 over Rational Field

>>> # needs sage.graphs
>>> B1 = [V.gen(i) + V.gen(i+Integer(1)) for i in range(Integer(9))] + [V.gen(Integer(9))]
>>> B2 = [V.gen(Integer(0))] + [-V.gen(i-Integer(1)) + V.gen(i) for i in range(Integer(1),Integer(10))]
>>> D = V.subspace_with_basis(B1)
>>> C = V.subspace_with_basis(B2)
>>> rho = phi.restrict_codomain(C)
>>> zeta = rho.restrict_domain(D)
>>> zeta
Vector space morphism represented by the matrix:
[6 5 4 3 3 2 1 0 0 0]
[6 5 4 3 2 2 2 1 0 0]
[6 6 5 4 3 2 2 2 1 0]
[6 5 5 4 3 2 2 2 2 1]
[6 4 4 4 3 3 3 3 2 1]
[6 5 4 4 4 4 4 4 3 1]
[6 6 5 4 4 4 3 3 3 2]
[6 6 6 5 4 4 2 1 1 1]
[6 6 6 6 5 4 3 1 0 0]
[3 3 3 3 3 2 2 1 0 0]
Domain:   Vector space of degree 10 and dimension 10 over Rational Field
          User basis matrix:
          [1 1 0 0 0 0 0 0 0 0]
          [0 1 1 0 0 0 0 0 0 0]
          [0 0 1 1 0 0 0 0 0 0]
          [0 0 0 1 1 0 0 0 0 0]
          [0 0 0 0 1 1 0 0 0 0]
          [0 0 0 0 0 1 1 0 0 0]
          [0 0 0 0 0 0 1 1 0 0]
          [0 0 0 0 0 0 0 1 1 0]
          [0 0 0 0 0 0 0 0 1 1]
          [0 0 0 0 0 0 0 0 0 1]
Codomain: Vector space of degree 10 and dimension 10 over Rational Field
          User basis matrix:
          [ 1  0  0  0  0  0  0  0  0  0]
          [-1  1  0  0  0  0  0  0  0  0]
          [ 0 -1  1  0  0  0  0  0  0  0]
          [ 0  0 -1  1  0  0  0  0  0  0]
          [ 0  0  0 -1  1  0  0  0  0  0]
          [ 0  0  0  0 -1  1  0  0  0  0]
          [ 0  0  0  0  0 -1  1  0  0  0]
          [ 0  0  0  0  0  0 -1  1  0  0]
          [ 0  0  0  0  0  0  0 -1  1  0]
          [ 0  0  0  0  0  0  0  0 -1  1]
A = graphs.PetersenGraph().adjacency_matrix()                                 # needs sage.graphs
V = QQ^10
phi = linear_transformation(V, V, A)                                          # needs sage.graphs
phi                                                                           # needs sage.graphs
# needs sage.graphs
B1 = [V.gen(i) + V.gen(i+1) for i in range(9)] + [V.gen(9)]
B2 = [V.gen(0)] + [-V.gen(i-1) + V.gen(i) for i in range(1,10)]
D = V.subspace_with_basis(B1)
C = V.subspace_with_basis(B2)
rho = phi.restrict_codomain(C)
zeta = rho.restrict_domain(D)
zeta

An endomorphism is a linear transformation with an equal domain and codomain, and here each needs to have the same basis. We are using a matrix that has well-behaved eigenvalues, as part of showing that these do not change as the representation changes.

sage: # needs sage.graphs
sage: A = graphs.PetersenGraph().adjacency_matrix()
sage: V = QQ^10
sage: phi = linear_transformation(V, V, A)
sage: phi.eigenvalues()                                                             # needs sage.rings.number_field
[3, -2, -2, -2, -2, 1, 1, 1, 1, 1]
sage: B1 = [V.gen(i) + V.gen(i+1) for i in range(9)] + [V.gen(9)]
sage: C = V.subspace_with_basis(B1)
sage: zeta = phi.restrict(C)
sage: zeta
Vector space morphism represented by the matrix:
[ 1  0  1 -1  2 -1  2 -2  2 -2]
[ 1  0  1  0  0  0  1  0  0  0]
[ 0  1  0  1  0  0  0  1  0  0]
[ 1 -1  2 -1  2 -2  2 -2  3 -2]
[ 2 -2  2 -1  1 -1  1  0  1  0]
[ 1  0  0  0  0  0  0  1  1  0]
[ 0  1  0  0  0  1 -1  1  0  2]
[ 0  0  1  0  0  2 -1  1 -1  2]
[ 0  0  0  1  0  1  1  0  0  0]
[ 0  0  0  0  1 -1  2 -1  1 -1]
Domain:   Vector space of degree 10 and dimension 10 over Rational Field
          User basis matrix:
          [1 1 0 0 0 0 0 0 0 0]
          [0 1 1 0 0 0 0 0 0 0]
          [0 0 1 1 0 0 0 0 0 0]
          [0 0 0 1 1 0 0 0 0 0]
          [0 0 0 0 1 1 0 0 0 0]
          [0 0 0 0 0 1 1 0 0 0]
          [0 0 0 0 0 0 1 1 0 0]
          [0 0 0 0 0 0 0 1 1 0]
          [0 0 0 0 0 0 0 0 1 1]
          [0 0 0 0 0 0 0 0 0 1]
Codomain: Vector space of degree 10 and dimension 10 over Rational Field
          User basis matrix:
          [1 1 0 0 0 0 0 0 0 0]
          [0 1 1 0 0 0 0 0 0 0]
          [0 0 1 1 0 0 0 0 0 0]
          [0 0 0 1 1 0 0 0 0 0]
          [0 0 0 0 1 1 0 0 0 0]
          [0 0 0 0 0 1 1 0 0 0]
          [0 0 0 0 0 0 1 1 0 0]
          [0 0 0 0 0 0 0 1 1 0]
          [0 0 0 0 0 0 0 0 1 1]
          [0 0 0 0 0 0 0 0 0 1]
sage: zeta.eigenvalues()                                                            # needs sage.rings.number_field
[3, -2, -2, -2, -2, 1, 1, 1, 1, 1]
>>> from sage.all import *
>>> # needs sage.graphs
>>> A = graphs.PetersenGraph().adjacency_matrix()
>>> V = QQ**Integer(10)
>>> phi = linear_transformation(V, V, A)
>>> phi.eigenvalues()                                                             # needs sage.rings.number_field
[3, -2, -2, -2, -2, 1, 1, 1, 1, 1]
>>> B1 = [V.gen(i) + V.gen(i+Integer(1)) for i in range(Integer(9))] + [V.gen(Integer(9))]
>>> C = V.subspace_with_basis(B1)
>>> zeta = phi.restrict(C)
>>> zeta
Vector space morphism represented by the matrix:
[ 1  0  1 -1  2 -1  2 -2  2 -2]
[ 1  0  1  0  0  0  1  0  0  0]
[ 0  1  0  1  0  0  0  1  0  0]
[ 1 -1  2 -1  2 -2  2 -2  3 -2]
[ 2 -2  2 -1  1 -1  1  0  1  0]
[ 1  0  0  0  0  0  0  1  1  0]
[ 0  1  0  0  0  1 -1  1  0  2]
[ 0  0  1  0  0  2 -1  1 -1  2]
[ 0  0  0  1  0  1  1  0  0  0]
[ 0  0  0  0  1 -1  2 -1  1 -1]
Domain:   Vector space of degree 10 and dimension 10 over Rational Field
          User basis matrix:
          [1 1 0 0 0 0 0 0 0 0]
          [0 1 1 0 0 0 0 0 0 0]
          [0 0 1 1 0 0 0 0 0 0]
          [0 0 0 1 1 0 0 0 0 0]
          [0 0 0 0 1 1 0 0 0 0]
          [0 0 0 0 0 1 1 0 0 0]
          [0 0 0 0 0 0 1 1 0 0]
          [0 0 0 0 0 0 0 1 1 0]
          [0 0 0 0 0 0 0 0 1 1]
          [0 0 0 0 0 0 0 0 0 1]
Codomain: Vector space of degree 10 and dimension 10 over Rational Field
          User basis matrix:
          [1 1 0 0 0 0 0 0 0 0]
          [0 1 1 0 0 0 0 0 0 0]
          [0 0 1 1 0 0 0 0 0 0]
          [0 0 0 1 1 0 0 0 0 0]
          [0 0 0 0 1 1 0 0 0 0]
          [0 0 0 0 0 1 1 0 0 0]
          [0 0 0 0 0 0 1 1 0 0]
          [0 0 0 0 0 0 0 1 1 0]
          [0 0 0 0 0 0 0 0 1 1]
          [0 0 0 0 0 0 0 0 0 1]
>>> zeta.eigenvalues()                                                            # needs sage.rings.number_field
[3, -2, -2, -2, -2, 1, 1, 1, 1, 1]
# needs sage.graphs
A = graphs.PetersenGraph().adjacency_matrix()
V = QQ^10
phi = linear_transformation(V, V, A)
phi.eigenvalues()                                                             # needs sage.rings.number_field
B1 = [V.gen(i) + V.gen(i+1) for i in range(9)] + [V.gen(9)]
C = V.subspace_with_basis(B1)
zeta = phi.restrict(C)
zeta
zeta.eigenvalues()                                                            # needs sage.rings.number_field

Equality

Equality of linear transformations is a bit nuanced. The equality operator == tests if two linear transformations have equal matrix representations, while we determine if two linear transformations are the same function with the .is_equal_function() method. Notice in this example that the function never changes, just the representations.

sage: f = lambda x: vector(QQ, [x[1], x[0]+x[1], x[0]])
sage: H = Hom(QQ^2, QQ^3)
sage: phi = H(f)

sage: rho = linear_transformation(QQ^2, QQ^3, matrix(QQ,2, 3, [[0,1,1], [1,1,0]]))

sage: phi == rho
True

sage: U = (QQ^2).subspace_with_basis([[1, 2], [-3, 1]])
sage: V = (QQ^3).subspace_with_basis([[0, 1, 0], [2, 3, 1], [-1, 1, 6]])
sage: K = Hom(U, V)
sage: zeta = K(f)

sage: zeta == phi
False
sage: zeta.is_equal_function(phi)
True
sage: zeta.is_equal_function(rho)
True
>>> from sage.all import *
>>> f = lambda x: vector(QQ, [x[Integer(1)], x[Integer(0)]+x[Integer(1)], x[Integer(0)]])
>>> H = Hom(QQ**Integer(2), QQ**Integer(3))
>>> phi = H(f)

>>> rho = linear_transformation(QQ**Integer(2), QQ**Integer(3), matrix(QQ,Integer(2), Integer(3), [[Integer(0),Integer(1),Integer(1)], [Integer(1),Integer(1),Integer(0)]]))

>>> phi == rho
True

>>> U = (QQ**Integer(2)).subspace_with_basis([[Integer(1), Integer(2)], [-Integer(3), Integer(1)]])
>>> V = (QQ**Integer(3)).subspace_with_basis([[Integer(0), Integer(1), Integer(0)], [Integer(2), Integer(3), Integer(1)], [-Integer(1), Integer(1), Integer(6)]])
>>> K = Hom(U, V)
>>> zeta = K(f)

>>> zeta == phi
False
>>> zeta.is_equal_function(phi)
True
>>> zeta.is_equal_function(rho)
True
f = lambda x: vector(QQ, [x[1], x[0]+x[1], x[0]])
H = Hom(QQ^2, QQ^3)
phi = H(f)
rho = linear_transformation(QQ^2, QQ^3, matrix(QQ,2, 3, [[0,1,1], [1,1,0]]))
phi == rho
U = (QQ^2).subspace_with_basis([[1, 2], [-3, 1]])
V = (QQ^3).subspace_with_basis([[0, 1, 0], [2, 3, 1], [-1, 1, 6]])
K = Hom(U, V)
zeta = K(f)
zeta == phi
zeta.is_equal_function(phi)
zeta.is_equal_function(rho)
class sage.modules.vector_space_morphism.VectorSpaceMorphism(homspace, A, side='left')[source]

Bases: FreeModuleMorphism

Create a linear transformation, a morphism between vector spaces.

INPUT:

  • homspace – a homspace (of vector spaces) to serve as a parent for the linear transformation and a home for the domain and codomain of the morphism

  • A – a matrix representing the linear transformation, which will act on vectors placed to the left of the matrix

EXAMPLES:

Nominally, we require a homspace to hold the domain and codomain and a matrix representation of the morphism (linear transformation).

sage: from sage.modules.vector_space_homspace import VectorSpaceHomspace
sage: from sage.modules.vector_space_morphism import VectorSpaceMorphism
sage: H = VectorSpaceHomspace(QQ^3, QQ^2)
sage: A = matrix(QQ, 3, 2, range(6))
sage: zeta = VectorSpaceMorphism(H, A)
sage: zeta
Vector space morphism represented by the matrix:
[0 1]
[2 3]
[4 5]
Domain:   Vector space of dimension 3 over Rational Field
Codomain: Vector space of dimension 2 over Rational Field
>>> from sage.all import *
>>> from sage.modules.vector_space_homspace import VectorSpaceHomspace
>>> from sage.modules.vector_space_morphism import VectorSpaceMorphism
>>> H = VectorSpaceHomspace(QQ**Integer(3), QQ**Integer(2))
>>> A = matrix(QQ, Integer(3), Integer(2), range(Integer(6)))
>>> zeta = VectorSpaceMorphism(H, A)
>>> zeta
Vector space morphism represented by the matrix:
[0 1]
[2 3]
[4 5]
Domain:   Vector space of dimension 3 over Rational Field
Codomain: Vector space of dimension 2 over Rational Field
from sage.modules.vector_space_homspace import VectorSpaceHomspace
from sage.modules.vector_space_morphism import VectorSpaceMorphism
H = VectorSpaceHomspace(QQ^3, QQ^2)
A = matrix(QQ, 3, 2, range(6))
zeta = VectorSpaceMorphism(H, A)
zeta

See the constructor, sage.modules.vector_space_morphism.linear_transformation() for another way to create linear transformations.

The .hom() method of a vector space will create a vector space morphism.

sage: V = QQ^3; W = V.subspace_with_basis([[1,2,3], [-1,2,5/3], [0,1,-1]])
sage: phi = V.hom(matrix(QQ, 3, range(9)), codomain=W) # indirect doctest
sage: type(phi)
<class 'sage.modules.vector_space_morphism.VectorSpaceMorphism'>
>>> from sage.all import *
>>> V = QQ**Integer(3); W = V.subspace_with_basis([[Integer(1),Integer(2),Integer(3)], [-Integer(1),Integer(2),Integer(5)/Integer(3)], [Integer(0),Integer(1),-Integer(1)]])
>>> phi = V.hom(matrix(QQ, Integer(3), range(Integer(9))), codomain=W) # indirect doctest
>>> type(phi)
<class 'sage.modules.vector_space_morphism.VectorSpaceMorphism'>
V = QQ^3; W = V.subspace_with_basis([[1,2,3], [-1,2,5/3], [0,1,-1]])
phi = V.hom(matrix(QQ, 3, range(9)), codomain=W) # indirect doctest
type(phi)

A matrix may be coerced into a vector space homspace to create a vector space morphism.

sage: from sage.modules.vector_space_homspace import VectorSpaceHomspace
sage: H = VectorSpaceHomspace(QQ^3, QQ^2)
sage: A = matrix(QQ, 3, 2, range(6))
sage: rho = H(A)  # indirect doctest
sage: type(rho)
<class 'sage.modules.vector_space_morphism.VectorSpaceMorphism'>
>>> from sage.all import *
>>> from sage.modules.vector_space_homspace import VectorSpaceHomspace
>>> H = VectorSpaceHomspace(QQ**Integer(3), QQ**Integer(2))
>>> A = matrix(QQ, Integer(3), Integer(2), range(Integer(6)))
>>> rho = H(A)  # indirect doctest
>>> type(rho)
<class 'sage.modules.vector_space_morphism.VectorSpaceMorphism'>
from sage.modules.vector_space_homspace import VectorSpaceHomspace
H = VectorSpaceHomspace(QQ^3, QQ^2)
A = matrix(QQ, 3, 2, range(6))
rho = H(A)  # indirect doctest
type(rho)
is_invertible()[source]

Determines if the vector space morphism has an inverse.

OUTPUT:

True if the vector space morphism is invertible, otherwise False.

EXAMPLES:

If the dimension of the domain does not match the dimension of the codomain, then the morphism cannot be invertible.

sage: V = QQ^3
sage: U = V.subspace_with_basis([V.0 + V.1, 2*V.1 + 3*V.2])
sage: phi = V.hom([U.0, U.0 + U.1, U.0 - U.1], U)
sage: phi.is_invertible()
False
>>> from sage.all import *
>>> V = QQ**Integer(3)
>>> U = V.subspace_with_basis([V.gen(0) + V.gen(1), Integer(2)*V.gen(1) + Integer(3)*V.gen(2)])
>>> phi = V.hom([U.gen(0), U.gen(0) + U.gen(1), U.gen(0) - U.gen(1)], U)
>>> phi.is_invertible()
False
V = QQ^3
U = V.subspace_with_basis([V.0 + V.1, 2*V.1 + 3*V.2])
phi = V.hom([U.0, U.0 + U.1, U.0 - U.1], U)
phi.is_invertible()

An invertible linear transformation.

sage: A = matrix(QQ, 3, [[-3, 5, -5], [4, -7, 7], [6, -8, 10]])
sage: A.determinant()
2
sage: H = Hom(QQ^3, QQ^3)
sage: rho = H(A)
sage: rho.is_invertible()
True
>>> from sage.all import *
>>> A = matrix(QQ, Integer(3), [[-Integer(3), Integer(5), -Integer(5)], [Integer(4), -Integer(7), Integer(7)], [Integer(6), -Integer(8), Integer(10)]])
>>> A.determinant()
2
>>> H = Hom(QQ**Integer(3), QQ**Integer(3))
>>> rho = H(A)
>>> rho.is_invertible()
True
A = matrix(QQ, 3, [[-3, 5, -5], [4, -7, 7], [6, -8, 10]])
A.determinant()
H = Hom(QQ^3, QQ^3)
rho = H(A)
rho.is_invertible()

A non-invertible linear transformation, an endomorphism of a vector space over a finite field.

sage: # needs sage.rings.finite_rings
sage: F.<a> = GF(11^2)
sage: A = matrix(F, [[6*a + 3,   8*a +  2, 10*a + 3],
....:                [2*a + 7,   4*a +  3,  2*a + 3],
....:                [9*a + 2,  10*a + 10,  3*a + 3]])
sage: A.nullity()
1
sage: E = End(F^3)
sage: zeta = E(A)
sage: zeta.is_invertible()
False
>>> from sage.all import *
>>> # needs sage.rings.finite_rings
>>> F = GF(Integer(11)**Integer(2), names=('a',)); (a,) = F._first_ngens(1)
>>> A = matrix(F, [[Integer(6)*a + Integer(3),   Integer(8)*a +  Integer(2), Integer(10)*a + Integer(3)],
...                [Integer(2)*a + Integer(7),   Integer(4)*a +  Integer(3),  Integer(2)*a + Integer(3)],
...                [Integer(9)*a + Integer(2),  Integer(10)*a + Integer(10),  Integer(3)*a + Integer(3)]])
>>> A.nullity()
1
>>> E = End(F**Integer(3))
>>> zeta = E(A)
>>> zeta.is_invertible()
False
# needs sage.rings.finite_rings
F.<a> = GF(11^2)
A = matrix(F, [[6*a + 3,   8*a +  2, 10*a + 3],
               [2*a + 7,   4*a +  3,  2*a + 3],
               [9*a + 2,  10*a + 10,  3*a + 3]])
A.nullity()
E = End(F^3)
zeta = E(A)
zeta.is_invertible()
sage.modules.vector_space_morphism.is_VectorSpaceMorphism(x)[source]

Return True if x is a vector space morphism (a linear transformation).

This function is deprecated.

INPUT:

  • x – anything

OUTPUT:

True only if x is an instance of a vector space morphism, which are also known as linear transformations.

EXAMPLES:

sage: V = QQ^2; f = V.hom([V.1,-2*V.0])
sage: sage.modules.vector_space_morphism.is_VectorSpaceMorphism(f)
doctest:warning...
DeprecationWarning: is_VectorSpaceMorphism is deprecated;
use isinstance(..., VectorSpaceMorphism) or categories instead
See https://github.com/sagemath/sage/issues/37731 for details.
True
sage: sage.modules.vector_space_morphism.is_VectorSpaceMorphism('junk')
False
>>> from sage.all import *
>>> V = QQ**Integer(2); f = V.hom([V.gen(1),-Integer(2)*V.gen(0)])
>>> sage.modules.vector_space_morphism.is_VectorSpaceMorphism(f)
doctest:warning...
DeprecationWarning: is_VectorSpaceMorphism is deprecated;
use isinstance(..., VectorSpaceMorphism) or categories instead
See https://github.com/sagemath/sage/issues/37731 for details.
True
>>> sage.modules.vector_space_morphism.is_VectorSpaceMorphism('junk')
False
V = QQ^2; f = V.hom([V.1,-2*V.0])
sage.modules.vector_space_morphism.is_VectorSpaceMorphism(f)
sage.modules.vector_space_morphism.is_VectorSpaceMorphism('junk')
sage.modules.vector_space_morphism.linear_transformation(arg0, arg1=None, arg2=None, side='left')[source]

Create a linear transformation from a variety of possible inputs.

FORMATS:

In the following, D and C are vector spaces over the same field that are the domain and codomain (respectively) of the linear transformation.

side is a keyword that is either 'left' or 'right'. When a matrix is used to specify a linear transformation, as in the first two call formats below, you may specify if the function is given by matrix multiplication with the vector on the left, or the vector on the right. The default is ‘left’. The matrix representation may be obtained as either version, no matter how it is created.

  • linear_transformation(A, side='left')

    Where A is a matrix. The domain and codomain are inferred from the dimension of the matrix and the base ring of the matrix. The base ring must be a field, or have its fraction field implemented in Sage.

  • linear_transformation(D, C, A, side='left')

    A is a matrix that behaves as above. However, now the domain and codomain are given explicitly. The matrix is checked for compatibility with the domain and codomain. Additionally, the domain and codomain may be supplied with alternate (“user”) bases and the matrix is interpreted as being a representation relative to those bases.

  • linear_transformation(D, C, f)

    f is any function that can be applied to the basis elements of the domain and that produces elements of the codomain. The linear transformation returned is the unique linear transformation that extends this mapping on the basis elements. f may come from a function defined by a Python def statement, or may be defined as a lambda function.

    Alternatively, f may be specified by a callable symbolic function, see the examples below for a demonstration.

  • linear_transformation(D, C, images)

    images is a list, or tuple, of codomain elements, equal in number to the size of the basis of the domain. Each basis element of the domain is mapped to the corresponding element of the images list, and the linear transformation returned is the unique linear transformation that extends this mapping.

OUTPUT:

A linear transformation described by the input. This is a “vector space morphism”, an object of the class sage.modules.vector_space_morphism.

EXAMPLES:

We can define a linear transformation with just a matrix, understood to act on a vector placed on one side or the other. The field for the vector spaces used as domain and codomain is obtained from the base ring of the matrix, possibly promoting to a fraction field.

sage: A = matrix(ZZ, [[1, -1, 4], [2, 0, 5]])
sage: phi = linear_transformation(A)
sage: phi
Vector space morphism represented by the matrix:
[ 1 -1  4]
[ 2  0  5]
Domain: Vector space of dimension 2 over Rational Field
Codomain: Vector space of dimension 3 over Rational Field
sage: phi([1/2, 5])
(21/2, -1/2, 27)

sage: B = matrix(Integers(7), [[1, 2, 1], [3, 5, 6]])
sage: rho = linear_transformation(B, side='right')
sage: rho
Vector space morphism represented by the matrix:
[1 3]
[2 5]
[1 6]
Domain: Vector space of dimension 3 over Ring of integers modulo 7
Codomain: Vector space of dimension 2 over Ring of integers modulo 7
sage: rho([2, 4, 6])
(2, 6)
>>> from sage.all import *
>>> A = matrix(ZZ, [[Integer(1), -Integer(1), Integer(4)], [Integer(2), Integer(0), Integer(5)]])
>>> phi = linear_transformation(A)
>>> phi
Vector space morphism represented by the matrix:
[ 1 -1  4]
[ 2  0  5]
Domain: Vector space of dimension 2 over Rational Field
Codomain: Vector space of dimension 3 over Rational Field
>>> phi([Integer(1)/Integer(2), Integer(5)])
(21/2, -1/2, 27)

>>> B = matrix(Integers(Integer(7)), [[Integer(1), Integer(2), Integer(1)], [Integer(3), Integer(5), Integer(6)]])
>>> rho = linear_transformation(B, side='right')
>>> rho
Vector space morphism represented by the matrix:
[1 3]
[2 5]
[1 6]
Domain: Vector space of dimension 3 over Ring of integers modulo 7
Codomain: Vector space of dimension 2 over Ring of integers modulo 7
>>> rho([Integer(2), Integer(4), Integer(6)])
(2, 6)
A = matrix(ZZ, [[1, -1, 4], [2, 0, 5]])
phi = linear_transformation(A)
phi
phi([1/2, 5])
B = matrix(Integers(7), [[1, 2, 1], [3, 5, 6]])
rho = linear_transformation(B, side='right')
rho
rho([2, 4, 6])

We can define a linear transformation with a matrix, while explicitly giving the domain and codomain. Matrix entries will be coerced into the common field of scalars for the vector spaces.

sage: D = QQ^3
sage: C = QQ^2
sage: A = matrix([[1, 7], [2, -1], [0, 5]])
sage: A.parent()
Full MatrixSpace of 3 by 2 dense matrices over Integer Ring
sage: zeta = linear_transformation(D, C, A)
sage: zeta.matrix().parent()
Full MatrixSpace of 3 by 2 dense matrices over Rational Field
sage: zeta
Vector space morphism represented by the matrix:
[ 1  7]
[ 2 -1]
[ 0  5]
Domain: Vector space of dimension 3 over Rational Field
Codomain: Vector space of dimension 2 over Rational Field
>>> from sage.all import *
>>> D = QQ**Integer(3)
>>> C = QQ**Integer(2)
>>> A = matrix([[Integer(1), Integer(7)], [Integer(2), -Integer(1)], [Integer(0), Integer(5)]])
>>> A.parent()
Full MatrixSpace of 3 by 2 dense matrices over Integer Ring
>>> zeta = linear_transformation(D, C, A)
>>> zeta.matrix().parent()
Full MatrixSpace of 3 by 2 dense matrices over Rational Field
>>> zeta
Vector space morphism represented by the matrix:
[ 1  7]
[ 2 -1]
[ 0  5]
Domain: Vector space of dimension 3 over Rational Field
Codomain: Vector space of dimension 2 over Rational Field
D = QQ^3
C = QQ^2
A = matrix([[1, 7], [2, -1], [0, 5]])
A.parent()
zeta = linear_transformation(D, C, A)
zeta.matrix().parent()
zeta

Matrix representations are relative to the bases for the domain and codomain.

sage: u = vector(QQ, [1, -1])
sage: v = vector(QQ, [2, 3])
sage: D = (QQ^2).subspace_with_basis([u, v])
sage: x = vector(QQ, [2, 1])
sage: y = vector(QQ, [-1, 4])
sage: C = (QQ^2).subspace_with_basis([x, y])
sage: A = matrix(QQ, [[2, 5], [3, 7]])
sage: psi = linear_transformation(D, C, A)
sage: psi
Vector space morphism represented by the matrix:
[2 5]
[3 7]
Domain: Vector space of degree 2 and dimension 2 over Rational Field
User basis matrix:
[ 1 -1]
[ 2  3]
Codomain: Vector space of degree 2 and dimension 2 over Rational Field
User basis matrix:
[ 2  1]
[-1  4]
sage: psi(u) == 2*x + 5*y
True
sage: psi(v) == 3*x + 7*y
True
>>> from sage.all import *
>>> u = vector(QQ, [Integer(1), -Integer(1)])
>>> v = vector(QQ, [Integer(2), Integer(3)])
>>> D = (QQ**Integer(2)).subspace_with_basis([u, v])
>>> x = vector(QQ, [Integer(2), Integer(1)])
>>> y = vector(QQ, [-Integer(1), Integer(4)])
>>> C = (QQ**Integer(2)).subspace_with_basis([x, y])
>>> A = matrix(QQ, [[Integer(2), Integer(5)], [Integer(3), Integer(7)]])
>>> psi = linear_transformation(D, C, A)
>>> psi
Vector space morphism represented by the matrix:
[2 5]
[3 7]
Domain: Vector space of degree 2 and dimension 2 over Rational Field
User basis matrix:
[ 1 -1]
[ 2  3]
Codomain: Vector space of degree 2 and dimension 2 over Rational Field
User basis matrix:
[ 2  1]
[-1  4]
>>> psi(u) == Integer(2)*x + Integer(5)*y
True
>>> psi(v) == Integer(3)*x + Integer(7)*y
True
u = vector(QQ, [1, -1])
v = vector(QQ, [2, 3])
D = (QQ^2).subspace_with_basis([u, v])
x = vector(QQ, [2, 1])
y = vector(QQ, [-1, 4])
C = (QQ^2).subspace_with_basis([x, y])
A = matrix(QQ, [[2, 5], [3, 7]])
psi = linear_transformation(D, C, A)
psi
psi(u) == 2*x + 5*y
psi(v) == 3*x + 7*y

Functions that act on the domain may be used to compute images of the domain’s basis elements, and this mapping can be extended to a unique linear transformation. The function may be a Python function (via def or lambda) or a Sage symbolic function.

sage: def g(x):
....:     return vector(QQ, [2*x[0]+x[2], 5*x[1]])
sage: phi = linear_transformation(QQ^3, QQ^2, g)
sage: phi
Vector space morphism represented by the matrix:
[2 0]
[0 5]
[1 0]
Domain: Vector space of dimension 3 over Rational Field
Codomain: Vector space of dimension 2 over Rational Field

sage: f = lambda x: vector(QQ, [2*x[0]+x[2], 5*x[1]])
sage: rho = linear_transformation(QQ^3, QQ^2, f)
sage: rho
Vector space morphism represented by the matrix:
[2 0]
[0 5]
[1 0]
Domain: Vector space of dimension 3 over Rational Field
Codomain: Vector space of dimension 2 over Rational Field

sage: # needs sage.symbolic
sage: x, y, z = var('x y z')
sage: h(x, y, z) = [2*x + z, 5*y]
sage: zeta = linear_transformation(QQ^3, QQ^2, h)
sage: zeta
Vector space morphism represented by the matrix:
[2 0]
[0 5]
[1 0]
Domain:   Vector space of dimension 3 over Rational Field
Codomain: Vector space of dimension 2 over Rational Field

sage: phi == rho
True
sage: rho == zeta                                                               # needs sage.symbolic
True
>>> from sage.all import *
>>> def g(x):
...     return vector(QQ, [Integer(2)*x[Integer(0)]+x[Integer(2)], Integer(5)*x[Integer(1)]])
>>> phi = linear_transformation(QQ**Integer(3), QQ**Integer(2), g)
>>> phi
Vector space morphism represented by the matrix:
[2 0]
[0 5]
[1 0]
Domain: Vector space of dimension 3 over Rational Field
Codomain: Vector space of dimension 2 over Rational Field

>>> f = lambda x: vector(QQ, [Integer(2)*x[Integer(0)]+x[Integer(2)], Integer(5)*x[Integer(1)]])
>>> rho = linear_transformation(QQ**Integer(3), QQ**Integer(2), f)
>>> rho
Vector space morphism represented by the matrix:
[2 0]
[0 5]
[1 0]
Domain: Vector space of dimension 3 over Rational Field
Codomain: Vector space of dimension 2 over Rational Field

>>> # needs sage.symbolic
>>> x, y, z = var('x y z')
>>> __tmp__=var("x,y,z"); h = symbolic_expression([Integer(2)*x + z, Integer(5)*y]).function(x,y,z)
>>> zeta = linear_transformation(QQ**Integer(3), QQ**Integer(2), h)
>>> zeta
Vector space morphism represented by the matrix:
[2 0]
[0 5]
[1 0]
Domain:   Vector space of dimension 3 over Rational Field
Codomain: Vector space of dimension 2 over Rational Field

>>> phi == rho
True
>>> rho == zeta                                                               # needs sage.symbolic
True
def g(x):
    return vector(QQ, [2*x[0]+x[2], 5*x[1]])
phi = linear_transformation(QQ^3, QQ^2, g)
phi
f = lambda x: vector(QQ, [2*x[0]+x[2], 5*x[1]])
rho = linear_transformation(QQ^3, QQ^2, f)
rho
# needs sage.symbolic
x, y, z = var('x y z')
h(x, y, z) = [2*x + z, 5*y]
zeta = linear_transformation(QQ^3, QQ^2, h)
zeta
phi == rho
rho == zeta                                                               # needs sage.symbolic

We create a linear transformation relative to non-standard bases, and capture its representation relative to standard bases. With this, we can build functions that create the same linear transformation relative to the nonstandard bases.

sage: u = vector(QQ, [1, -1])
sage: v = vector(QQ, [2, 3])
sage: D = (QQ^2).subspace_with_basis([u, v])
sage: x = vector(QQ, [2, 1])
sage: y = vector(QQ, [-1, 4])
sage: C = (QQ^2).subspace_with_basis([x, y])
sage: A = matrix(QQ, [[2, 5], [3, 7]])
sage: psi = linear_transformation(D, C, A)
sage: rho = psi.restrict_codomain(QQ^2).restrict_domain(QQ^2)
sage: rho.matrix()
[ -4/5  97/5]
[  1/5 -13/5]

sage: f = lambda x: vector(QQ, [(-4/5)*x[0] + (1/5)*x[1], (97/5)*x[0] + (-13/5)*x[1]])
sage: psi = linear_transformation(D, C, f)
sage: psi.matrix()
[2 5]
[3 7]

sage: # needs sage.symbolic
sage: s, t = var('s t')
sage: h(s, t) = [(-4/5)*s + (1/5)*t, (97/5)*s + (-13/5)*t]
sage: zeta = linear_transformation(D, C, h)
sage: zeta.matrix()
[2 5]
[3 7]
>>> from sage.all import *
>>> u = vector(QQ, [Integer(1), -Integer(1)])
>>> v = vector(QQ, [Integer(2), Integer(3)])
>>> D = (QQ**Integer(2)).subspace_with_basis([u, v])
>>> x = vector(QQ, [Integer(2), Integer(1)])
>>> y = vector(QQ, [-Integer(1), Integer(4)])
>>> C = (QQ**Integer(2)).subspace_with_basis([x, y])
>>> A = matrix(QQ, [[Integer(2), Integer(5)], [Integer(3), Integer(7)]])
>>> psi = linear_transformation(D, C, A)
>>> rho = psi.restrict_codomain(QQ**Integer(2)).restrict_domain(QQ**Integer(2))
>>> rho.matrix()
[ -4/5  97/5]
[  1/5 -13/5]

>>> f = lambda x: vector(QQ, [(-Integer(4)/Integer(5))*x[Integer(0)] + (Integer(1)/Integer(5))*x[Integer(1)], (Integer(97)/Integer(5))*x[Integer(0)] + (-Integer(13)/Integer(5))*x[Integer(1)]])
>>> psi = linear_transformation(D, C, f)
>>> psi.matrix()
[2 5]
[3 7]

>>> # needs sage.symbolic
>>> s, t = var('s t')
>>> __tmp__=var("s,t"); h = symbolic_expression([(-Integer(4)/Integer(5))*s + (Integer(1)/Integer(5))*t, (Integer(97)/Integer(5))*s + (-Integer(13)/Integer(5))*t]).function(s,t)
>>> zeta = linear_transformation(D, C, h)
>>> zeta.matrix()
[2 5]
[3 7]
u = vector(QQ, [1, -1])
v = vector(QQ, [2, 3])
D = (QQ^2).subspace_with_basis([u, v])
x = vector(QQ, [2, 1])
y = vector(QQ, [-1, 4])
C = (QQ^2).subspace_with_basis([x, y])
A = matrix(QQ, [[2, 5], [3, 7]])
psi = linear_transformation(D, C, A)
rho = psi.restrict_codomain(QQ^2).restrict_domain(QQ^2)
rho.matrix()
f = lambda x: vector(QQ, [(-4/5)*x[0] + (1/5)*x[1], (97/5)*x[0] + (-13/5)*x[1]])
psi = linear_transformation(D, C, f)
psi.matrix()
# needs sage.symbolic
s, t = var('s t')
h(s, t) = [(-4/5)*s + (1/5)*t, (97/5)*s + (-13/5)*t]
zeta = linear_transformation(D, C, h)
zeta.matrix()

Finally, we can give an explicit list of images for the basis elements of the domain.

sage: # needs sage.rings.number_field
sage: x = polygen(QQ)
sage: F.<a> = NumberField(x^3 + x + 1)
sage: u = vector(F, [1, a, a^2])
sage: v = vector(F, [a, a^2, 2])
sage: w = u + v
sage: D = F^3
sage: C = F^3
sage: rho = linear_transformation(D, C, [u, v, w])
sage: rho.matrix()
[      1       a     a^2]
[      a     a^2       2]
[  a + 1 a^2 + a a^2 + 2]
sage: C = (F^3).subspace_with_basis([u, v])
sage: D = (F^3).subspace_with_basis([u, v])
sage: psi = linear_transformation(C, D, [u+v, u-v])
sage: psi.matrix()
[ 1  1]
[ 1 -1]
>>> from sage.all import *
>>> # needs sage.rings.number_field
>>> x = polygen(QQ)
>>> F = NumberField(x**Integer(3) + x + Integer(1), names=('a',)); (a,) = F._first_ngens(1)
>>> u = vector(F, [Integer(1), a, a**Integer(2)])
>>> v = vector(F, [a, a**Integer(2), Integer(2)])
>>> w = u + v
>>> D = F**Integer(3)
>>> C = F**Integer(3)
>>> rho = linear_transformation(D, C, [u, v, w])
>>> rho.matrix()
[      1       a     a^2]
[      a     a^2       2]
[  a + 1 a^2 + a a^2 + 2]
>>> C = (F**Integer(3)).subspace_with_basis([u, v])
>>> D = (F**Integer(3)).subspace_with_basis([u, v])
>>> psi = linear_transformation(C, D, [u+v, u-v])
>>> psi.matrix()
[ 1  1]
[ 1 -1]
# needs sage.rings.number_field
x = polygen(QQ)
F.<a> = NumberField(x^3 + x + 1)
u = vector(F, [1, a, a^2])
v = vector(F, [a, a^2, 2])
w = u + v
D = F^3
C = F^3
rho = linear_transformation(D, C, [u, v, w])
rho.matrix()
C = (F^3).subspace_with_basis([u, v])
D = (F^3).subspace_with_basis([u, v])
psi = linear_transformation(C, D, [u+v, u-v])
psi.matrix()