Path: blob/master/sage/modules/free_module_morphism.py
4036 views
"""1Morphisms of free modules23AUTHORS:4- William Stein: initial version56- Miguel Marco (2010-06-19): added eigenvalues, eigenvectors and minpoly functions789TESTS::1011sage: V = ZZ^2; f = V.hom([V.1,-2*V.0])12sage: loads(dumps(f))13Free module morphism defined by the matrix14[ 0 1]15[-2 0]16Domain: Ambient free module of rank 2 over the principal ideal domain ...17Codomain: Ambient free module of rank 2 over the principal ideal domain ...18sage: loads(dumps(f)) == f19True20"""2122####################################################################################23# Copyright (C) 2009 William Stein <[email protected]>24#25# Distributed under the terms of the GNU General Public License (GPL)26#27# This code is distributed in the hope that it will be useful,28# but WITHOUT ANY WARRANTY; without even the implied warranty of29# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU30# General Public License for more details.31#32# The full text of the GPL is available at:33#34# http://www.gnu.org/licenses/35####################################################################################3637# A matrix morphism is a morphism that is defined by multiplication by a38# matrix. Elements of domain must either have a method "vector()" that39# returns a vector that the defining matrix can hit from the left, or40# be coercible into vector space of appropriate dimension.4142import sage.misc.misc as misc43import sage.modules.free_module as free_module44import matrix_morphism45from sage.structure.sequence import Sequence4647import free_module_homspace4849def is_FreeModuleMorphism(x):50"""51EXAMPLES::5253sage: V = ZZ^2; f = V.hom([V.1,-2*V.0])54sage: sage.modules.free_module_morphism.is_FreeModuleMorphism(f)55True56sage: sage.modules.free_module_morphism.is_FreeModuleMorphism(0)57False58"""59return isinstance(x, FreeModuleMorphism)6061class FreeModuleMorphism(matrix_morphism.MatrixMorphism):62def __init__(self, parent, A):63"""64INPUT:6566- ``parent`` - a homspace in a (sub) category of free modules6768- ``A`` - matrix6970EXAMPLES::7172sage: V = ZZ^3; W = span([[1,2,3],[-1,2,8]], ZZ)73sage: phi = V.hom(matrix(ZZ,3,[1..9]))74sage: type(phi)75<class 'sage.modules.free_module_morphism.FreeModuleMorphism'>76"""77if not free_module_homspace.is_FreeModuleHomspace(parent):78raise TypeError, "parent (=%s) must be a free module hom space"%parent79if isinstance(A, matrix_morphism.MatrixMorphism):80A = A.matrix()81A = parent._matrix_space()(A)82matrix_morphism.MatrixMorphism.__init__(self, parent, A)8384def __call__(self, x):85"""86Evaluate this matrix morphism at x, which is either an element87that can be coerced into the domain or a submodule of the domain.8889EXAMPLES::9091sage: V = QQ^3; W = span([[1,2,3],[-1,2,5/3]], QQ)92sage: phi = V.hom(matrix(QQ,3,[1..9]))9394We compute the image of some elements::9596sage: phi(V.0)97(1, 2, 3)98sage: phi(V.1)99(4, 5, 6)100sage: phi(V.0 - 1/4*V.1)101(0, 3/4, 3/2)102103We compute the image of a *subspace*::104105sage: V = QQ^3; W = span([[1,2,3],[-1,2,5/3]], QQ)106sage: phi = V.hom(matrix(QQ,3,[1..9]))107sage: phi.rank()1082109sage: phi(V)110Vector space of degree 3 and dimension 2 over Rational Field111Basis matrix:112[ 1 0 -1]113[ 0 1 2]114115We restrict phi to W and compute the image of an element::116117sage: psi = phi.restrict_domain(W)118sage: psi(W.0) == phi(W.0)119True120sage: psi(W.1) == phi(W.1)121True122123We compute the image of a submodule of a ZZ-module embedded in124a rational vector space::125126sage: V = QQ^3; W = V.span_of_basis([[2,2,3],[-1,2,5/3]], ZZ)127sage: phi = W.hom([W.0, W.0-W.1]); phi128Free module morphism defined by the matrix129[ 1 0]130[ 1 -1]...131sage: phi(span([2*W.1],ZZ))132Free module of degree 3 and rank 1 over Integer Ring133Echelon basis matrix:134[ 6 0 8/3]135sage: phi(2*W.1)136(6, 0, 8/3)137"""138if free_module.is_FreeModule(x):139V = self.domain().submodule(x)140return self.restrict_domain(V).image()141return matrix_morphism.MatrixMorphism.__call__(self, x)142143def _repr_(self):144r"""145Return string representation of this morphism of free modules.146147EXAMPLES::148149sage: V = ZZ^3; W = span([[1,2,3],[-1,2,8]], ZZ)150sage: phi = V.hom(matrix(ZZ,3,[1..9]))151sage: phi._repr_()152'Free module morphism defined by the matrix\n[1 2 3]\n[4 5 6]\n[7 8 9]\nDomain: Ambient free module of rank 3 over the principal ideal domain Integer Ring\nCodomain: Ambient free module of rank 3 over the principal ideal domain Integer Ring'153154sage: V = ZZ^6155sage: W = ZZ^4156sage: m = matrix(QQ, [[1, 0, 0 ,0], [0]*4, [0]*4, [0]*4, [0]*4, [0]*4])157sage: phi = V.hom(m, W)158sage: rho = phi.restrict_codomain(W.span([W.0]))159sage: rho160Free module morphism defined by the matrix161[1]162[0]163[0]164[0]165[0]166[0]167Domain: Ambient free module of rank 6 over the principal ideal domain Integer Ring168Codomain: Free module of degree 4 and rank 1 over Integer Ring169Echelon basis matrix:170[1 0 0 0]171172sage: V = QQ^40173sage: m = matrix(QQ, 40, 40, 1600)174sage: phi = V.hom(m, V)175sage: phi176Vector space morphism represented by the matrix:17740 x 40 dense matrix over Rational Field178Domain: Vector space of dimension 40 over Rational Field179Codomain: Vector space of dimension 40 over Rational Field180"""181r = "Free module morphism defined by the matrix\n{0}\nDomain: {1}\nCodomain: {2}"182return r.format(self.matrix(), self.domain(), self.codomain())183184def change_ring(self, R):185"""186Change the ring over which this morphism is defined. This changes the ring of the187domain, codomain, and underlying matrix.188189EXAMPLES::190191sage: V0 = span([[0,0,1],[0,2,0]],ZZ); V1 = span([[1/2,0],[0,2]],ZZ); W = span([[1,0],[0,6]],ZZ)192sage: h = V0.hom([-3*V1.0-3*V1.1, -3*V1.0-3*V1.1])193sage: h.base_ring()194Integer Ring195sage: h196Free module morphism defined by the matrix197[-3 -3]198[-3 -3]...199sage: h.change_ring(QQ).base_ring()200Rational Field201sage: f = h.change_ring(QQ); f202Vector space morphism represented by the matrix:203[-3 -3]204[-3 -3]205Domain: Vector space of degree 3 and dimension 2 over Rational Field206Basis matrix:207[0 1 0]208[0 0 1]209Codomain: Vector space of degree 2 and dimension 2 over Rational Field210Basis matrix:211[1 0]212[0 1]213sage: f = h.change_ring(GF(7)); f214Vector space morphism represented by the matrix:215[4 4]216[4 4]217Domain: Vector space of degree 3 and dimension 2 over Finite Field of size 7218Basis matrix:219[0 1 0]220[0 0 1]221Codomain: Vector space of degree 2 and dimension 2 over Finite Field of size 7222Basis matrix:223[1 0]224[0 1]225"""226D = self.domain().change_ring(R)227C = self.codomain().change_ring(R)228A = self.matrix().change_ring(R)229return D.hom(A, C)230231def inverse_image(self, V):232"""233Given a submodule V of the codomain of self, return the234inverse image of V under self, i.e., the biggest submodule of235the domain of self that maps into V.236237EXAMPLES:238239We test computing inverse images over a field::240241sage: V = QQ^3; W = span([[1,2,3],[-1,2,5/3]], QQ)242sage: phi = V.hom(matrix(QQ,3,[1..9]))243sage: phi.rank()2442245sage: I = phi.inverse_image(W); I246Vector space of degree 3 and dimension 2 over Rational Field247Basis matrix:248[ 1 0 0]249[ 0 1 -1/2]250sage: phi(I.0) in W251True252sage: phi(I.1) in W253True254sage: W = phi.image()255sage: phi.inverse_image(W) == V256True257258We test computing inverse images between two spaces embedded in different259ambient spaces.::260261sage: V0 = span([[0,0,1],[0,2,0]],ZZ); V1 = span([[1/2,0],[0,2]],ZZ); W = span([[1,0],[0,6]],ZZ)262sage: h = V0.hom([-3*V1.0-3*V1.1, -3*V1.0-3*V1.1])263sage: h.inverse_image(W)264Free module of degree 3 and rank 2 over Integer Ring265Echelon basis matrix:266[0 2 1]267[0 0 2]268sage: h(h.inverse_image(W)).is_submodule(W)269True270sage: h(h.inverse_image(W)).index_in(W)271+Infinity272sage: h(h.inverse_image(W))273Free module of degree 2 and rank 1 over Integer Ring274Echelon basis matrix:275[ 3 12]276277278We test computing inverse images over the integers::279280sage: V = QQ^3; W = V.span_of_basis([[2,2,3],[-1,2,5/3]], ZZ)281sage: phi = W.hom([W.0, W.0-W.1])282sage: Z = W.span([2*W.1]); Z283Free module of degree 3 and rank 1 over Integer Ring284Echelon basis matrix:285[ 2 -4 -10/3]286sage: Y = phi.inverse_image(Z); Y287Free module of degree 3 and rank 1 over Integer Ring288Echelon basis matrix:289[ 6 0 8/3]290sage: phi(Y) == Z291True292"""293if self.rank() == 0:294# Special case -- if this is the 0 map, then the only possibility295# for the inverse image is that it is the whole domain.296return self.domain()297298R = self.base_ring()299A = self.matrix()300301# Replace the module V that we are going to pullback by a302# submodule that is contained in the image of self, since our303# plan is to lift all generators of V.304V = self.image().intersection(V)305# Write V in terms of the basis for the codomain.306V = self.codomain().coordinate_module(V)307B = V.basis_matrix()308309# Compute the kernel, which is contained in the inverse image.310K = self.kernel()311312if R.is_field():313# By solving, find lifts of each of the basis elements of V.314# Each row of C gives a linear combination of the basis for the domain315# that maps to one of the basis elements V.316C = A.solve_left(B)317318else:319if not hasattr(A, 'hermite_form'):320raise NotImplementedError, "base ring (%s) must have hermite_form algorithm in order to compute inverse image"%R321322# 1. Compute H such that U*A = H = hnf(A) without zero323# rows. What this "does" is find a basis for the image of324# A and explicitly represents each element in this basis325# as the image of some element of the domain (the rows of326# U give these elements of the domain).327H, U = A.hermite_form(transformation=True,include_zero_rows=False)328329# 2. Next we find the unique solution to the equation330# Y*H = B. This writes each basis element of V in331# terms of our image basis found in the previous step.332Y = H.solve_left(B)333334# 3. Multiply Y by U then takes those same linear combinations335# from step 2 above and lifts them to coefficients that define336# linear combinations of the basis for the domain.337C = Y*U338339# Finally take the linear combinations of the basis for the340# domain defined by C. Together with the kernel K, this spans341# the inverse image of V.342if self.domain().is_ambient():343L = C.row_module(R)344else:345L = (C*self.domain().basis_matrix()).row_module(R)346347return K + L348349def lift(self, x):350r"""351Given an element of the image, return an element of the codomain that maps onto it.352353Note that ``lift`` and ``preimage_representative`` are354equivalent names for this method, with the latter suggesting355that the return value is a coset representative of the domain356modulo the kernel of the morphism.357358EXAMPLE::359360sage: X = QQ**2361sage: V = X.span([[2, 0], [0, 8]], ZZ)362sage: W = (QQ**1).span([[1/12]], ZZ)363sage: f = V.hom([W([1/3]), W([1/2])], W)364sage: f.lift([1/3])365(8, -16)366sage: f.lift([1/2])367(12, -24)368sage: f.lift([1/6])369(4, -8)370sage: f.lift([1/12])371Traceback (most recent call last):372...373ValueError: element is not in the image374sage: f.lift([1/24])375Traceback (most recent call last):376...377TypeError: element (= [1/24]) is not in free module378379This works for vector spaces, too::380381sage: V = VectorSpace(GF(3), 2)382sage: W = VectorSpace(GF(3), 3)383sage: f = V.hom([W.1, W.1 - W.0])384sage: f.lift(W.1)385(1, 0)386sage: f.lift(W.2)387Traceback (most recent call last):388...389ValueError: element is not in the image390sage: w = W((17, -2, 0))391sage: f(f.lift(w)) == w392True393394This example illustrates the use of the ``preimage_representative``395as an equivalent name for this method. ::396397sage: V = ZZ^3398sage: W = ZZ^2399sage: w = vector(ZZ, [1,2])400sage: f = V.hom([w, w, w], W)401sage: f.preimage_representative(vector(ZZ, [10, 20]))402(0, 0, 10)403"""404from free_module_element import vector405x = self.codomain()(x)406A = self.matrix()407R = self.base_ring()408if R.is_field():409try:410C = A.solve_left(x)411except ValueError:412raise ValueError, "element is not in the image"413else:414# see inverse_image for similar code but with comments415if not hasattr(A, 'hermite_form'):416raise NotImplementedError, "base ring (%s) must have hermite_form algorithm in order to compute inverse image"%R417H, U = A.hermite_form(transformation=True,include_zero_rows=False)418Y = H.solve_left(vector(self.codomain().coordinates(x)))419C = Y*U420try:421t = self.domain().linear_combination_of_basis(C)422except TypeError:423raise ValueError, "element is not in the image"424assert self(t) == x425return t426427preimage_representative = lift428429def eigenvalues(self,extend=True):430r"""431Returns a list with the eigenvalues of the endomorphism of vector spaces.432433INPUT:434435- ``extend`` -- boolean (default: True) decides if base field436extensions should be considered or not.437438EXAMPLES:439440We compute the eigenvalues of an endomorphism of `\QQ^3`::441442sage: V=QQ^3443sage: H=V.endomorphism_ring()([[1,-1,0],[-1,1,1],[0,3,1]])444sage: H.eigenvalues()445[3, 1, -1]446447Note the effect of the ``extend`` option::448449sage: V=QQ^2450sage: H=V.endomorphism_ring()([[0,-1],[1,0]])451sage: H.eigenvalues()452[-1*I, 1*I]453sage: H.eigenvalues(extend=False)454[]455"""456if self.base_ring().is_field():457if self.is_endomorphism():458return self.matrix().eigenvalues(extend=extend)459else:460raise TypeError, "not an endomorphism"461else:462raise NotImplementedError, "module must be a vector space"463464def eigenvectors(self,extend=True):465"""466Computes the subspace of eigenvectors of a given eigenvalue.467468INPUT:469470- ``extend`` -- boolean (default: True) decides if base field471extensions should be considered or not.472473OUTPUT:474475A sequence of tuples. Each tuple contains an eigenvalue, a sequence476with a basis of the corresponding subspace of eigenvectors, and the477algebraic multiplicity of the eigenvalue.478479EXAMPLES::480481sage: V=(QQ^4).subspace([[0,2,1,4],[1,2,5,0],[1,1,1,1]])482sage: H=(V.Hom(V))(matrix(QQ, [[0,1,0],[-1,0,0],[0,0,3]]))483sage: H.eigenvectors()484[(3, [485(0, 0, 1, -6/7)486], 1), (-1*I, [487(1, 1*I, 0, -0.571428571428572? + 2.428571428571429?*I)488], 1), (1*I, [489(1, -1*I, 0, -0.571428571428572? - 2.428571428571429?*I)490], 1)]491sage: H.eigenvectors(extend=False)492[(3, [493(0, 0, 1, -6/7)494], 1)]495sage: H1=(V.Hom(V))(matrix(QQ, [[2,1,0],[0,2,0],[0,0,3]]))496sage: H1.eigenvectors()497[(3, [498(0, 0, 1, -6/7)499], 1), (2, [500(0, 1, 0, 17/7)501], 2)]502sage: H1.eigenvectors(extend=False)503[(3, [504(0, 0, 1, -6/7)505], 1), (2, [506(0, 1, 0, 17/7)507], 2)]508"""509if self.base_ring().is_field():510if self.is_endomorphism():511seigenvec=self.matrix().eigenvectors_left(extend=extend)512resu=[]513for i in seigenvec:514V=self.domain().base_extend(i[0].parent())515svectors=Sequence(map(lambda j: V(j * V.basis_matrix()),i[1]), cr=True)516resu.append((i[0],svectors,i[2]))517return resu518else:519raise TypeError, "not an endomorphism"520else:521raise NotImplementedError, "module must be a vector space"522523def minimal_polynomial(self,var='x'):524r"""525Computes the minimal polynomial.526527``minpoly()`` and ``minimal_polynomial()`` are the same method.528529INPUT:530531- ``var`` - string (default: 'x') a variable name532533OUTPUT:534535polynomial in var - the minimal polynomial of the endomorphism.536537EXAMPLES:538539Compute the minimal polynomial, and check it. ::540541sage: V=GF(7)^3542sage: H=V.Hom(V)([[0,1,2],[-1,0,3],[2,4,1]])543sage: H544Vector space morphism represented by the matrix:545[0 1 2]546[6 0 3]547[2 4 1]548Domain: Vector space of dimension 3 over Finite Field of size 7549Codomain: Vector space of dimension 3 over Finite Field of size 7550551sage: H.minpoly()552x^3 + 6*x^2 + 6*x + 1553554sage: H.minimal_polynomial()555x^3 + 6*x^2 + 6*x + 1556557sage: H^3 + (H^2)*6 + H*6 + 1558Vector space morphism represented by the matrix:559[0 0 0]560[0 0 0]561[0 0 0]562Domain: Vector space of dimension 3 over Finite Field of size 7563Codomain: Vector space of dimension 3 over Finite Field of size 7564"""565if self.is_endomorphism():566return self.matrix().minpoly(var)567else:568raise TypeError, "not an endomorphism"569570minpoly = minimal_polynomial571572573