Ticket #11553: trac_11553-matrix-morphisms-additions-v3.patch

File trac_11553-matrix-morphisms-additions-v3.patch, 19.7 KB (added by rbeezer, 10 years ago)
  • sage/modules/matrix_morphism.py

    # HG changeset patch
    # User Rob Beezer <beezer@ups.edu>
    # Date 1309303921 25200
    # Node ID 2c5ad01c9e18f83ec0086dc382f60d5c8041f3b4
    # Parent  e2d2c3ff0e9d631be7133e221c0a0f8b573c4d2c
    11553: matrix morphisms,  additional methods
    
    diff --git a/sage/modules/matrix_morphism.py b/sage/modules/matrix_morphism.py
    a b  
    4747- William Stein (2005-01-07): added __reduce__
    4848
    4949- Craig Citro (2008-03-18): refactored MatrixMorphism class
     50
     51- Rob Beezer (2011-07-15): additional methods, bug fixes, documentation
    5052"""
    5153
    52 
    5354import sage.categories.morphism
    5455import sage.categories.homset
    5556import sage.matrix.all as matrix
     
    192193        except TypeError:
    193194            raise ZeroDivisionError, "matrix morphism not invertible"
    194195
     196    def inverse(self):
     197        r"""
     198        Returns the inverse of this matrix morphism, if the inverse exists.
     199
     200        Raises a ``ZeroDivisionError`` if the inverse does not exist.
     201
     202        EXAMPLES:
     203
     204        An invertible morphism created as a restriction of
     205        a non-invertible morphism, and which has an unequal
     206        domain and codomain.  ::
     207
     208            sage: V = QQ^4
     209            sage: W = QQ^3
     210            sage: m = matrix(QQ, [[2, 0, 3], [-6, 1, 4], [1, 2, -4], [1, 0, 1]])
     211            sage: phi = V.hom(m, W)
     212            sage: rho = phi.restrict_domain(V.span([V.0, V.3]))
     213            sage: zeta = rho.restrict_codomain(W.span([W.0, W.2]))
     214            sage: x = vector(QQ, [2, 0, 0, -7])
     215            sage: y = zeta(x); y
     216            (-3, 0, -1)
     217            sage: inv = zeta.inverse(); inv
     218            Free module morphism defined by the matrix
     219            [-1  3]
     220            [ 1 -2]
     221            Domain: Vector space of degree 3 and dimension 2 over Rational Field
     222            Basis ...
     223            Codomain: Vector space of degree 4 and dimension 2 over Rational Field
     224            Basis ...
     225            sage: inv(y) == x
     226            True
     227
     228        An example of an invertible morphism between modules,
     229        (rather than between vector spaces).  ::
     230
     231            sage: M = ZZ^4
     232            sage: p = matrix(ZZ, [[ 0, -1,  1, -2],
     233            ...                   [ 1, -3,  2, -3],
     234            ...                   [ 0,  4, -3,  4],
     235            ...                   [-2,  8, -4,  3]])
     236            sage: phi = M.hom(p, M)
     237            sage: x = vector(ZZ, [1, -3, 5, -2])
     238            sage: y = phi(x); y
     239            (1, 12, -12, 21)
     240            sage: rho = phi.inverse(); rho
     241            Free module morphism defined by the matrix
     242            [ -5   3  -1   1]
     243            [ -9   4  -3   2]
     244            [-20   8  -7   4]
     245            [ -6   2  -2   1]
     246            Domain: Ambient free module of rank 4 over the principal ideal domain ...
     247            Codomain: Ambient free module of rank 4 over the principal ideal domain ...
     248            sage: rho(y) == x
     249            True
     250
     251        A non-invertible morphism, despite having an appropriate
     252        domain and codomain.  ::
     253
     254            sage: V = QQ^2
     255            sage: m = matrix(QQ, [[1, 2], [20, 40]])
     256            sage: phi = V.hom(m, V)
     257            sage: phi.is_bijective()
     258            False
     259            sage: phi.inverse()
     260            Traceback (most recent call last):
     261            ...
     262            ZeroDivisionError: matrix morphism not invertible
     263
     264        The matrix representation of this morphism is invertible
     265        over the rationals, but not over the integers, thus the
     266        morphism is not invertible as a map between modules.
     267        It is easy to notice from the definition that every
     268        vector of the image will have a second entry that
     269        is an even integer.  ::
     270
     271            sage: V = ZZ^2
     272            sage: q = matrix(ZZ, [[1, 2], [3, 4]])
     273            sage: phi = V.hom(q, V)
     274            sage: phi.matrix().change_ring(QQ).inverse()
     275            [  -2    1]
     276            [ 3/2 -1/2]
     277            sage: phi.is_bijective()
     278            False
     279            sage: phi.image()
     280            Free module of degree 2 and rank 2 over Integer Ring
     281            Echelon basis matrix:
     282            [1 0]
     283            [0 2]
     284            sage: phi.lift(vector(ZZ, [1, 1]))
     285            Traceback (most recent call last):
     286            ...
     287            ValueError: element is not in the image
     288            sage: phi.inverse()
     289            Traceback (most recent call last):
     290            ...
     291            ZeroDivisionError: matrix morphism not invertible
     292
     293        The unary invert operator (~, tilde, "wiggle") is synonymous
     294        with the ``inverse()`` method (and a lot easier to type).  ::
     295
     296            sage: V = QQ^2
     297            sage: r = matrix(QQ, [[4, 3], [-2, 5]])
     298            sage: phi = V.hom(r, V)
     299            sage: rho = phi.inverse()
     300            sage: zeta = ~phi
     301            sage: rho == zeta
     302            True
     303
     304        TESTS::
     305
     306            sage: V = QQ^2
     307            sage: W = QQ^3
     308            sage: U = W.span([W.0, W.1])
     309            sage: m = matrix(QQ, [[2, 1], [3, 4]])
     310            sage: phi = V.hom(m, U)
     311            sage: inv = phi.inverse()
     312            sage: (inv*phi).is_identity()
     313            True
     314            sage: (phi*inv).is_identity()
     315            True
     316        """
     317        return self.__invert__()
     318
    195319    def __rmul__(self, left):
    196320        """
    197321        EXAMPLES::
     
    446570                                         check=False) for V, _ in E],
    447571                            cr=True, check=False)
    448572
     573    def trace(self):
     574        """
     575        EXAMPLES::
     576
     577            sage: V = ZZ^2; phi = V.hom([V.0+V.1, 2*V.1])
     578            sage: phi.trace()
     579            3
     580        """
     581        return self.matrix().trace()
     582
    449583    def det(self):
    450584        """
    451585        Return the determinant of this endomorphism.
     
    568702        raise NotImplementedError, "this method must be overridden in the extension class"
    569703
    570704    def rank(self):
    571         """
     705        r"""
     706        Returns the rank of the matrix representing this morphism.
     707
    572708        EXAMPLES::
    573        
     709
    574710            sage: V = ZZ^2; phi = V.hom(V.basis())
    575711            sage: phi.rank()
    576712            2
     
    580716        """
    581717        return self.matrix().rank()
    582718
     719    def nullity(self):
     720        r"""
     721        Returns the nullity of the matrix representing this morphism.
     722
     723        EXAMPLES::
     724
     725            sage: V = ZZ^2; phi = V.hom(V.basis())
     726            sage: phi.nullity()
     727            0
     728            sage: V = ZZ^2; phi = V.hom([V.0, V.0])
     729            sage: phi.nullity()
     730            1
     731        """
     732        return self.matrix().left_nullity()
     733
     734    def is_injective(self):
     735        r"""
     736        Tell whether ``self`` is injective.
     737
     738        EXAMPLE::
     739
     740            sage: V1 = QQ^2
     741            sage: V2 = QQ^3
     742            sage: phi = V1.hom(Matrix([[1,2],[3,4],[5,6]]),V2)
     743            sage: phi.is_injective()
     744            True
     745            sage: psi = V2.hom(Matrix([[1,2,3],[4,5,6]]),V1)
     746            sage: psi.is_injective()
     747            False
     748
     749        AUTHOR:
     750
     751        -- Simon King (2010-05)
     752        """
     753        return self._matrix.kernel().dimension() == 0
     754
     755    def is_surjective(self):
     756        r"""
     757        Tell whether ``self`` is surjective.
     758
     759        EXAMPLES::
     760
     761            sage: V1 = QQ^2
     762            sage: V2 = QQ^3
     763            sage: phi = V1.hom(Matrix([[1,2],[3,4],[5,6]]), V2)
     764            sage: phi.is_surjective()
     765            False
     766            sage: psi = V2.hom(Matrix([[1,2,3],[4,5,6]]), V1)
     767            sage: psi.is_surjective()
     768            True
     769
     770        An example over a PID that is not `\ZZ`.  ::
     771
     772            sage: R = PolynomialRing(QQ, 'x')
     773            sage: A = R^2
     774            sage: B = R^2
     775            sage: H = A.hom([B([x^2-1, 1]), B([x^2, 1])])
     776            sage: H.image()
     777            Free module of degree 2 and rank 2 over Univariate Polynomial Ring in x over Rational Field
     778            Echelon basis matrix:
     779            [ 1  0]
     780            [ 0 -1]
     781            sage: H.is_surjective()
     782            True
     783
     784        This tests if Trac #11552 is fixed. ::
     785
     786            sage: V = ZZ^2
     787            sage: m = matrix(ZZ, [[1,2],[0,2]])
     788            sage: phi = V.hom(m, V)
     789            sage: phi.lift(vector(ZZ, [0, 1]))
     790            Traceback (most recent call last):
     791            ...
     792            ValueError: element is not in the image
     793            sage: phi.is_surjective()
     794            False
     795
     796        AUTHORS:
     797
     798        - Simon King (2010-05)
     799        - Rob Beezer (2011-06-28)
     800        """
     801        # Testing equality of free modules over PIDs is unreliable
     802        #   see Trac #11579 for explanation and status
     803        # We test if image equals codomain with two inclusions
     804        #   reverse inclusion of below is trivially true
     805        return self.codomain().is_submodule(self.image())
     806
     807    def is_bijective(self):
     808        r"""
     809        Tell whether ``self`` is bijective.
     810
     811        EXAMPLES:
     812
     813        Two morphisms that are obviously not bijective, simply on
     814        considerations of the dimensions.  However, each fullfills
     815        half of the requirements to be a bijection.  ::
     816
     817            sage: V1 = QQ^2
     818            sage: V2 = QQ^3
     819            sage: m = matrix(QQ, [[1,2],[3,4],[5,6]])
     820            sage: phi = V1.hom(m, V2)
     821            sage: phi.is_injective()
     822            True
     823            sage: phi.is_bijective()
     824            False
     825            sage: rho = V2.hom(m.transpose(), V1)
     826            sage: rho.is_surjective()
     827            True
     828            sage: rho.is_bijective()
     829            False
     830
     831        We construct a simple bijection between two one-dimensional
     832        vector spaces.  ::
     833
     834            sage: V1 = QQ^3
     835            sage: V2 = QQ^2
     836            sage: phi = V1.hom(matrix(QQ, [[1, 2, 3], [4, 5, 6]]), V2)
     837            sage: x = vector(QQ, [1, -1, 4])
     838            sage: y = phi(x); y
     839            (18, 22)
     840            sage: rho = phi.restrict_domain(V1.span([x]))
     841            sage: zeta = rho.restrict_codomain(V2.span([y]))
     842            sage: zeta.is_bijective()
     843            True
     844
     845        AUTHOR:
     846
     847        - Rob Beezer (2011-06-28)
     848        """
     849        return self.is_injective() and self.is_surjective()
     850
     851    def is_identity(self):
     852        r"""
     853        Determines if this morphism is an identity function or not.
     854
     855        EXAMPLES:
     856
     857        A homomorphism that cannot possibly be the identity
     858        due to an unequal domain and codomain.  ::
     859
     860            sage: V = QQ^3
     861            sage: W = QQ^2
     862            sage: m = matrix(QQ, [[1, 2], [3, 4], [5, 6]])
     863            sage: phi = V.hom(m, W)
     864            sage: phi.is_identity()
     865            False
     866
     867        A bijection, but not the identity. ::
     868
     869            sage: V = QQ^3
     870            sage: n = matrix(QQ, [[3, 1, -8], [5, -4, 6], [1, 1, -5]])
     871            sage: phi = V.hom(n, V)
     872            sage: phi.is_bijective()
     873            True
     874            sage: phi.is_identity()
     875            False
     876
     877        A restriction that is the identity.  ::
     878
     879            sage: V = QQ^3
     880            sage: p = matrix(QQ, [[1, 0, 0], [5, 8, 3], [0, 0, 1]])
     881            sage: phi = V.hom(p, V)
     882            sage: rho = phi.restrict(V.span([V.0, V.2]))
     883            sage: rho.is_identity()
     884            True
     885
     886        An identity linear transformation that is defined with a
     887        domain and codomain with wildly different bases, so that the
     888        matrix representation is not simply the identity matrix. ::
     889
     890            sage: A = matrix(QQ, [[1, 1, 0], [2, 3, -4], [2, 4, -7]])
     891            sage: B = matrix(QQ, [[2, 7, -2], [-1, -3, 1], [-1, -6, 2]])
     892            sage: U = (QQ^3).subspace_with_basis(A.rows())
     893            sage: V = (QQ^3).subspace_with_basis(B.rows())
     894            sage: H = Hom(U, V)
     895            sage: id = lambda x: x
     896            sage: phi = H(id)
     897            sage: phi([203, -179, 34])
     898            (203, -179, 34)
     899            sage: phi.matrix()
     900            [  1   0   1]
     901            [ -9 -18  -2]
     902            [-17 -31  -5]
     903            sage: phi.is_identity()
     904            True
     905
     906        TEST::
     907
     908            sage: V = QQ^10
     909            sage: H = Hom(V, V)
     910            sage: id = H.identity()
     911            sage: id.is_identity()
     912            True
     913
     914        AUTHOR:
     915
     916        - Rob Beezer (2011-06-28)
     917        """
     918        if self.domain() != self.codomain():
     919            return False
     920        # testing for the identity matrix will only work for
     921        #   endomorphisms which have the same basis for domain and codomain
     922        #   so we test equality on a basis, which is sufficient
     923        return all( self(u) == u for u in self.domain().basis() )
     924
     925    def is_zero(self):
     926        r"""
     927        Determines if this morphism is a zero function or not.
     928
     929        EXAMPLES:
     930
     931        A zero morphism created from a function.  ::
     932
     933            sage: V = ZZ^5
     934            sage: W = ZZ^3
     935            sage: z = lambda x: zero_vector(ZZ, 3)
     936            sage: phi = V.hom(z, W)
     937            sage: phi.is_zero()
     938            True
     939
     940        An image list that just barely makes a non-zero morphism.  ::
     941
     942            sage: V = ZZ^4
     943            sage: W = ZZ^6
     944            sage: z = zero_vector(ZZ, 6)
     945            sage: images = [z, z, W.5, z]
     946            sage: phi = V.hom(images, W)
     947            sage: phi.is_zero()
     948            False
     949
     950        TEST::
     951
     952            sage: V = QQ^10
     953            sage: W = QQ^3
     954            sage: H = Hom(V, W)
     955            sage: rho = H.zero()
     956            sage: rho.is_zero()
     957            True
     958
     959        AUTHOR:
     960
     961        - Rob Beezer (2011-07-15)
     962        """
     963        # any nonzero entry in any matrix representation
     964        #   disqualifies the morphism as having totally zero outputs
     965        return self.matrix().is_zero()
     966
     967    def is_equal_function(self, other):
     968        r"""
     969        Determines if two morphisms are equal functions.
     970
     971        INPUT:
     972
     973        - ``other`` - a morphism to compare with ``self``
     974
     975        OUTPUT:
     976
     977        Returns ``True`` precisely when the two morphisms have
     978        equal domains and codomains (as sets) and produce identical
     979        output when given the same input.  Otherwise returns ``False``.
     980
     981        This is useful when ``self`` and ``other`` may have different
     982        representations.
     983
     984        Sage's default comparison of matrix morphisms requires the
     985        domains to have the same bases and the codomains to have the
     986        same bases, and then compares the matrix representations.
     987        This notion of equality is more permissive (it will
     988        return ``True`` "more often"), but is more correct
     989        mathematically.
     990
     991        EXAMPLES:
     992
     993        Three morphisms defined by combinations of different
     994        bases for the domain and codomain and different functions.
     995        Two are equal, the third is different from both of the others.  ::
     996
     997            sage: B = matrix(QQ, [[-3,  5, -4,  2],
     998            ...                   [-1,  2, -1,  4],
     999            ...                   [ 4, -6,  5, -1],
     1000            ...                   [-5,  7, -6,  1]])
     1001            sage: U = (QQ^4).subspace_with_basis(B.rows())
     1002            sage: C = matrix(QQ, [[-1, -6, -4],
     1003            ...                   [ 3, -5,  6],
     1004            ...                   [ 1,  2,  3]])
     1005            sage: V = (QQ^3).subspace_with_basis(C.rows())
     1006            sage: H = Hom(U, V)
     1007
     1008            sage: D = matrix(QQ, [[-7, -2, -5,  2],
     1009            ...                   [-5,  1, -4, -8],
     1010            ...                   [ 1, -1,  1,  4],
     1011            ...                   [-4, -1, -3,   1]])
     1012            sage: X = (QQ^4).subspace_with_basis(D.rows())
     1013            sage: E = matrix(QQ, [[ 4, -1,  4],
     1014            ...                   [ 5, -4, -5],
     1015            ...                   [-1,  0, -2]])
     1016            sage: Y = (QQ^3).subspace_with_basis(E.rows())
     1017            sage: K = Hom(X, Y)
     1018
     1019            sage: f = lambda x: vector(QQ, [x[0]+x[1], 2*x[1]-4*x[2], 5*x[3]])
     1020            sage: g = lambda x: vector(QQ, [x[0]-x[2], 2*x[1]-4*x[2], 5*x[3]])
     1021
     1022            sage: rho = H(f)
     1023            sage: phi = K(f)
     1024            sage: zeta = H(g)
     1025
     1026            sage: rho.is_equal_function(phi)
     1027            True
     1028            sage: phi.is_equal_function(rho)
     1029            True
     1030            sage: zeta.is_equal_function(rho)
     1031            False
     1032            sage: phi.is_equal_function(zeta)
     1033            False
     1034
     1035        TEST::
     1036
     1037            sage: H = Hom(ZZ^2, ZZ^2)
     1038            sage: phi = H(matrix(ZZ, 2, range(4)))
     1039            sage: phi.is_equal_function('junk')
     1040            Traceback (most recent call last):
     1041            ...
     1042            TypeError: can only compare to a matrix morphism, not junk
     1043
     1044        AUTHOR:
     1045
     1046        - Rob Beezer (2011-07-15)
     1047        """
     1048        if not is_MatrixMorphism(other):
     1049            msg = 'can only compare to a matrix morphism, not {0}'
     1050            raise TypeError(msg.format(other))
     1051        if self.domain() != other.domain():
     1052            return False
     1053        if self.codomain() != other.codomain():
     1054            return False
     1055        # check agreement on any basis of the domain
     1056        return all( self(u) == other(u) for u in self.domain().basis() )
     1057
    5831058    def restrict_domain(self, sub):
    5841059        """
    5851060        Restrict this matrix morphism to a subspace sub of the domain. The
     
    7381213        A = self.matrix().restrict(V)
    7391214        H = sage.categories.homset.End(sub, self.domain().category())
    7401215        return H(A)
    741        
    742     def trace(self):
    743         """
    744         EXAMPLES::
    7451216
    746             sage: V = ZZ^2; phi = V.hom([V.0+V.1, 2*V.1])
    747             sage: phi.trace()
    748             3
    749         """
    750         return self.matrix().trace()
    7511217
    7521218class MatrixMorphism(MatrixMorphism_abstract):
    7531219    """
     
    7981264        """
    7991265        return self._matrix
    8001266
    801     def is_injective(self):
    802         """
    803         Tell whether ``self`` is injective.
    804 
    805         EXAMPLE::
    806 
    807             sage: V1 = QQ^2
    808             sage: V2 = QQ^3
    809             sage: phi = V1.hom(Matrix([[1,2],[3,4],[5,6]]),V2)
    810             sage: phi.is_injective()
    811             True
    812             sage: psi = V2.hom(Matrix([[1,2,3],[4,5,6]]),V1)
    813             sage: psi.is_injective()
    814             False
    815 
    816         AUTHOR:
    817 
    818         -- Simon King (2010-05)
    819         """
    820         return self._matrix.kernel().dimension() == 0
    821 
    822     def is_surjective(self):
    823         r"""
    824         Tell whether ``self`` is surjective.
    825 
    826         EXAMPLES::
    827 
    828             sage: V1 = QQ^2
    829             sage: V2 = QQ^3
    830             sage: phi = V1.hom(Matrix([[1,2],[3,4],[5,6]]), V2)
    831             sage: phi.is_surjective()
    832             False
    833             sage: psi = V2.hom(Matrix([[1,2,3],[4,5,6]]), V1)
    834             sage: psi.is_surjective()
    835             True
    836 
    837         An example over a PID that is not `\ZZ`.  ::
    838 
    839             sage: R = PolynomialRing(QQ, 'x')
    840             sage: A = R^2
    841             sage: B = R^2
    842             sage: H = A.hom([B([x^2-1, 1]), B([x^2, 1])])
    843             sage: H.image()
    844             Free module of degree 2 and rank 2 over Univariate Polynomial Ring in x over Rational Field
    845             Echelon basis matrix:
    846             [ 1  0]
    847             [ 0 -1]
    848             sage: H.is_surjective()
    849             True
    850 
    851         This tests if Trac #11552 is fixed. ::
    852 
    853             sage: V = ZZ^2
    854             sage: m = matrix(ZZ, [[1,2],[0,2]])
    855             sage: phi = V.hom(m, V)
    856             sage: phi.lift(vector(ZZ, [0, 1]))
    857             Traceback (most recent call last):
    858             ...
    859             ValueError: element is not in the image
    860             sage: phi.is_surjective()
    861             False
    862 
    863         AUTHORS:
    864 
    865         - Simon King (2010-05)
    866         - Rob Beezer (2011-06-28)
    867         """
    868         # Testing equality of free modules over PIDs is unreliable
    869         #   see Trac #11579 for explanation and status
    870         # We test if image equals codomain with two inclusions
    871         #   reverse inclusion of below is trivially true
    872         return self.codomain().is_submodule(self.image())
    8731267
    8741268    def _repr_(self):
    8751269        """