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

File trac_11553-matrix-morphisms-additions-v4.patch, 15.3 KB (added by rbeezer, 10 years ago)

Rebase of v3 patch

  • sage/modules/matrix_morphism.py

    # HG changeset patch
    # User Rob Beezer <beezer@ups.edu>
    # Date 1309303921 25200
    # Node ID 50923b241abd47339df19224d01c37ac902f01ce
    # Parent  cbf6338f147d9b96be9fc3ae367c0659e6b419a9
    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::
     
    449573                                         check=False) for V, _ in E],
    450574                            cr=True, check=False)
    451575
     576    def trace(self):
     577        """
     578        EXAMPLES::
     579
     580            sage: V = ZZ^2; phi = V.hom([V.0+V.1, 2*V.1])
     581            sage: phi.trace()
     582            3
     583        """
     584        return self.matrix().trace()
     585
    452586    def det(self):
    453587        """
    454588        Return the determinant of this endomorphism.
     
    571705        raise NotImplementedError, "this method must be overridden in the extension class"
    572706
    573707    def rank(self):
    574         """
     708        r"""
     709        Returns the rank of the matrix representing this morphism.
     710
    575711        EXAMPLES::
    576        
     712
    577713            sage: V = ZZ^2; phi = V.hom(V.basis())
    578714            sage: phi.rank()
    579715            2
     
    583719        """
    584720        return self.matrix().rank()
    585721
     722    def nullity(self):
     723        r"""
     724        Returns the nullity of the matrix representing this morphism.
     725
     726        EXAMPLES::
     727
     728            sage: V = ZZ^2; phi = V.hom(V.basis())
     729            sage: phi.nullity()
     730            0
     731            sage: V = ZZ^2; phi = V.hom([V.0, V.0])
     732            sage: phi.nullity()
     733            1
     734        """
     735        return self.matrix().left_nullity()
     736
     737    def is_bijective(self):
     738        r"""
     739        Tell whether ``self`` is bijective.
     740
     741        EXAMPLES:
     742
     743        Two morphisms that are obviously not bijective, simply on
     744        considerations of the dimensions.  However, each fullfills
     745        half of the requirements to be a bijection.  ::
     746
     747            sage: V1 = QQ^2
     748            sage: V2 = QQ^3
     749            sage: m = matrix(QQ, [[1,2],[3,4],[5,6]])
     750            sage: phi = V1.hom(m, V2)
     751            sage: phi.is_injective()
     752            True
     753            sage: phi.is_bijective()
     754            False
     755            sage: rho = V2.hom(m.transpose(), V1)
     756            sage: rho.is_surjective()
     757            True
     758            sage: rho.is_bijective()
     759            False
     760
     761        We construct a simple bijection between two one-dimensional
     762        vector spaces.  ::
     763
     764            sage: V1 = QQ^3
     765            sage: V2 = QQ^2
     766            sage: phi = V1.hom(matrix(QQ, [[1, 2, 3], [4, 5, 6]]), V2)
     767            sage: x = vector(QQ, [1, -1, 4])
     768            sage: y = phi(x); y
     769            (18, 22)
     770            sage: rho = phi.restrict_domain(V1.span([x]))
     771            sage: zeta = rho.restrict_codomain(V2.span([y]))
     772            sage: zeta.is_bijective()
     773            True
     774
     775        AUTHOR:
     776
     777        - Rob Beezer (2011-06-28)
     778        """
     779        return self.is_injective() and self.is_surjective()
     780
     781    def is_identity(self):
     782        r"""
     783        Determines if this morphism is an identity function or not.
     784
     785        EXAMPLES:
     786
     787        A homomorphism that cannot possibly be the identity
     788        due to an unequal domain and codomain.  ::
     789
     790            sage: V = QQ^3
     791            sage: W = QQ^2
     792            sage: m = matrix(QQ, [[1, 2], [3, 4], [5, 6]])
     793            sage: phi = V.hom(m, W)
     794            sage: phi.is_identity()
     795            False
     796
     797        A bijection, but not the identity. ::
     798
     799            sage: V = QQ^3
     800            sage: n = matrix(QQ, [[3, 1, -8], [5, -4, 6], [1, 1, -5]])
     801            sage: phi = V.hom(n, V)
     802            sage: phi.is_bijective()
     803            True
     804            sage: phi.is_identity()
     805            False
     806
     807        A restriction that is the identity.  ::
     808
     809            sage: V = QQ^3
     810            sage: p = matrix(QQ, [[1, 0, 0], [5, 8, 3], [0, 0, 1]])
     811            sage: phi = V.hom(p, V)
     812            sage: rho = phi.restrict(V.span([V.0, V.2]))
     813            sage: rho.is_identity()
     814            True
     815
     816        An identity linear transformation that is defined with a
     817        domain and codomain with wildly different bases, so that the
     818        matrix representation is not simply the identity matrix. ::
     819
     820            sage: A = matrix(QQ, [[1, 1, 0], [2, 3, -4], [2, 4, -7]])
     821            sage: B = matrix(QQ, [[2, 7, -2], [-1, -3, 1], [-1, -6, 2]])
     822            sage: U = (QQ^3).subspace_with_basis(A.rows())
     823            sage: V = (QQ^3).subspace_with_basis(B.rows())
     824            sage: H = Hom(U, V)
     825            sage: id = lambda x: x
     826            sage: phi = H(id)
     827            sage: phi([203, -179, 34])
     828            (203, -179, 34)
     829            sage: phi.matrix()
     830            [  1   0   1]
     831            [ -9 -18  -2]
     832            [-17 -31  -5]
     833            sage: phi.is_identity()
     834            True
     835
     836        TEST::
     837
     838            sage: V = QQ^10
     839            sage: H = Hom(V, V)
     840            sage: id = H.identity()
     841            sage: id.is_identity()
     842            True
     843
     844        AUTHOR:
     845
     846        - Rob Beezer (2011-06-28)
     847        """
     848        if self.domain() != self.codomain():
     849            return False
     850        # testing for the identity matrix will only work for
     851        #   endomorphisms which have the same basis for domain and codomain
     852        #   so we test equality on a basis, which is sufficient
     853        return all( self(u) == u for u in self.domain().basis() )
     854
     855    def is_zero(self):
     856        r"""
     857        Determines if this morphism is a zero function or not.
     858
     859        EXAMPLES:
     860
     861        A zero morphism created from a function.  ::
     862
     863            sage: V = ZZ^5
     864            sage: W = ZZ^3
     865            sage: z = lambda x: zero_vector(ZZ, 3)
     866            sage: phi = V.hom(z, W)
     867            sage: phi.is_zero()
     868            True
     869
     870        An image list that just barely makes a non-zero morphism.  ::
     871
     872            sage: V = ZZ^4
     873            sage: W = ZZ^6
     874            sage: z = zero_vector(ZZ, 6)
     875            sage: images = [z, z, W.5, z]
     876            sage: phi = V.hom(images, W)
     877            sage: phi.is_zero()
     878            False
     879
     880        TEST::
     881
     882            sage: V = QQ^10
     883            sage: W = QQ^3
     884            sage: H = Hom(V, W)
     885            sage: rho = H.zero()
     886            sage: rho.is_zero()
     887            True
     888
     889        AUTHOR:
     890
     891        - Rob Beezer (2011-07-15)
     892        """
     893        # any nonzero entry in any matrix representation
     894        #   disqualifies the morphism as having totally zero outputs
     895        return self.matrix().is_zero()
     896
     897    def is_equal_function(self, other):
     898        r"""
     899        Determines if two morphisms are equal functions.
     900
     901        INPUT:
     902
     903        - ``other`` - a morphism to compare with ``self``
     904
     905        OUTPUT:
     906
     907        Returns ``True`` precisely when the two morphisms have
     908        equal domains and codomains (as sets) and produce identical
     909        output when given the same input.  Otherwise returns ``False``.
     910
     911        This is useful when ``self`` and ``other`` may have different
     912        representations.
     913
     914        Sage's default comparison of matrix morphisms requires the
     915        domains to have the same bases and the codomains to have the
     916        same bases, and then compares the matrix representations.
     917        This notion of equality is more permissive (it will
     918        return ``True`` "more often"), but is more correct
     919        mathematically.
     920
     921        EXAMPLES:
     922
     923        Three morphisms defined by combinations of different
     924        bases for the domain and codomain and different functions.
     925        Two are equal, the third is different from both of the others.  ::
     926
     927            sage: B = matrix(QQ, [[-3,  5, -4,  2],
     928            ...                   [-1,  2, -1,  4],
     929            ...                   [ 4, -6,  5, -1],
     930            ...                   [-5,  7, -6,  1]])
     931            sage: U = (QQ^4).subspace_with_basis(B.rows())
     932            sage: C = matrix(QQ, [[-1, -6, -4],
     933            ...                   [ 3, -5,  6],
     934            ...                   [ 1,  2,  3]])
     935            sage: V = (QQ^3).subspace_with_basis(C.rows())
     936            sage: H = Hom(U, V)
     937
     938            sage: D = matrix(QQ, [[-7, -2, -5,  2],
     939            ...                   [-5,  1, -4, -8],
     940            ...                   [ 1, -1,  1,  4],
     941            ...                   [-4, -1, -3,   1]])
     942            sage: X = (QQ^4).subspace_with_basis(D.rows())
     943            sage: E = matrix(QQ, [[ 4, -1,  4],
     944            ...                   [ 5, -4, -5],
     945            ...                   [-1,  0, -2]])
     946            sage: Y = (QQ^3).subspace_with_basis(E.rows())
     947            sage: K = Hom(X, Y)
     948
     949            sage: f = lambda x: vector(QQ, [x[0]+x[1], 2*x[1]-4*x[2], 5*x[3]])
     950            sage: g = lambda x: vector(QQ, [x[0]-x[2], 2*x[1]-4*x[2], 5*x[3]])
     951
     952            sage: rho = H(f)
     953            sage: phi = K(f)
     954            sage: zeta = H(g)
     955
     956            sage: rho.is_equal_function(phi)
     957            True
     958            sage: phi.is_equal_function(rho)
     959            True
     960            sage: zeta.is_equal_function(rho)
     961            False
     962            sage: phi.is_equal_function(zeta)
     963            False
     964
     965        TEST::
     966
     967            sage: H = Hom(ZZ^2, ZZ^2)
     968            sage: phi = H(matrix(ZZ, 2, range(4)))
     969            sage: phi.is_equal_function('junk')
     970            Traceback (most recent call last):
     971            ...
     972            TypeError: can only compare to a matrix morphism, not junk
     973
     974        AUTHOR:
     975
     976        - Rob Beezer (2011-07-15)
     977        """
     978        if not is_MatrixMorphism(other):
     979            msg = 'can only compare to a matrix morphism, not {0}'
     980            raise TypeError(msg.format(other))
     981        if self.domain() != other.domain():
     982            return False
     983        if self.codomain() != other.codomain():
     984            return False
     985        # check agreement on any basis of the domain
     986        return all( self(u) == other(u) for u in self.domain().basis() )
     987
    586988    def restrict_domain(self, sub):
    587989        """
    588990        Restrict this matrix morphism to a subspace sub of the domain. The
     
    7411143        A = self.matrix().restrict(V)
    7421144        H = sage.categories.homset.End(sub, self.domain().category())
    7431145        return H(A)
    744        
    745     def trace(self):
    746         """
    747         EXAMPLES::
    7481146
    749             sage: V = ZZ^2; phi = V.hom([V.0+V.1, 2*V.1])
    750             sage: phi.trace()
    751             3
    752         """
    753         return self.matrix().trace()
    7541147
    7551148class MatrixMorphism(MatrixMorphism_abstract):
    7561149    """