Ticket #11556: trac_11556-linear-transformations-v2.patch

File trac_11556-linear-transformations-v2.patch, 56.5 KB (added by rbeezer, 10 years ago)

Working patch, needs doctests, here for safe-keeping

  • doc/en/reference/modules.rst

    # HG changeset patch
    # User Rob Beezer <beezer@ups.edu>
    # Date 1311032662 25200
    # Node ID 223b70a780f6fb16c723877730a2efd1ea5969d8
    # Parent  5e125d2ba2aec0144785331fa07bb644487e0273
    11556: linear transformations from free module morphisms
    
    diff --git a/doc/en/reference/modules.rst b/doc/en/reference/modules.rst
    a b  
    1414   sage/modules/real_double_vector
    1515   sage/modules/vector_callable_symbolic_dense   
    1616
     17   sage/modules/vector_space_homspace
     18   sage/modules/vector_space_morphism
     19
    1720   sage/modules/free_module_homspace
    1821   sage/modules/free_module_morphism
    19    
     22
    2023   sage/modules/matrix_morphism
    2124
    2225   sage/modules/fg_pid/fgp_module
  • sage/categories/homset.py

    diff --git a/sage/categories/homset.py b/sage/categories/homset.py
    a b  
    246246        True
    247247    """
    248248    def __init__(self, X, Y, category=None, base = None, check=True):
    249         """
     249        r"""
    250250        TESTS::
     251
    251252            sage: X = ZZ['x']; X.rename("X")
    252253            sage: Y = ZZ['y']; Y.rename("Y")
    253254            sage: class MyHomset(Homset):
     
    522523# Really needed???
    523524class HomsetWithBase(Homset):
    524525    def __init__(self, X, Y, category=None, check=True, base=None):
    525         """
     526        r"""
    526527        TESTS::
     528       
    527529            sage: X = ZZ['x']; X.rename("X")
    528530            sage: Y = ZZ['y']; Y.rename("Y")
    529531            sage: class MyHomset(HomsetWithBase):
  • sage/modules/all.py

    diff --git a/sage/modules/all.py b/sage/modules/all.py
    a b  
    2929
    3030import vector_callable_symbolic_dense
    3131
     32from vector_space_homspace import is_VectorSpaceHomspace
     33
     34from vector_space_morphism import is_VectorSpaceMorphism, linear_transformation
     35
    3236import vector_symbolic_dense
  • sage/modules/free_module.py

    diff --git a/sage/modules/free_module.py b/sage/modules/free_module.py
    a b  
    604604
    605605    # FIXME: what's the level of generality of FreeModuleHomspace?
    606606    # Should there be a category for free modules accepting it as hom space?
     607    # See similar method for FreeModule_generic_field class
    607608    def _Hom_(self, Y, category):
    608609        from free_module_homspace import FreeModuleHomspace
    609610        return FreeModuleHomspace(self, Y, category)
     
    28962897            raise TypeError, "The base_field (=%s) must be a field"%base_field
    28972898        FreeModule_generic_pid.__init__(self, base_ring, dimension, degree, sparse=sparse)
    28982899
     2900    def _Hom_(self, Y, category):
     2901        r"""
     2902        Returns a homspace whose morphisms have this vector space as domain.
     2903
     2904        This is called by the general methods such as
     2905        :meth:`sage.structure.parent.Parent.Hom` and
     2906        :meth:`sage.structure.parent_base.ParentWithBase.Hom`.
     2907
     2908        INPUT:
     2909
     2910        - ``Y`` - a free module (or vector space) that will
     2911          be the codomain of the morphisms in returned homspace
     2912        - ``category`` - the category for the homspace
     2913
     2914        OUTPUT:
     2915
     2916        If ``Y`` is a free module over a field, in other words, a vector space,
     2917        then this returns a space of homomorphisms between vector spaces,
     2918        in other words a space of linear transformations.
     2919
     2920        If ``Y`` is a free module that is not a vector space, then
     2921        the returned space contains homomorphisms between free modules.
     2922
     2923        EXAMPLES::
     2924
     2925            sage: V = QQ^2
     2926            sage: W = QQ^3
     2927            sage: H = V._Hom_(W, category=None)
     2928            sage: type(H)
     2929            <class 'sage.modules.vector_space_homspace.VectorSpaceHomspace_with_category'>
     2930            sage: H
     2931            Set of Morphisms (Linear Transformations) from Vector space of dimension 2 over Rational Field to Vector space of dimension 3 over Rational Field
     2932
     2933            sage: V = QQ^2
     2934            sage: W = ZZ^3
     2935            sage: H = V._Hom_(W, category=None)
     2936            sage: type(H)
     2937            <class 'sage.modules.free_module_homspace.FreeModuleHomspace_with_category'>
     2938            sage: H
     2939            Set of Morphisms from Vector space of dimension 2 over Rational Field to Ambient free module of rank 3 over the principal ideal domain Integer Ring in Category of vector spaces over Rational Field
     2940        """
     2941        if Y.base_ring().is_field():
     2942            import vector_space_homspace
     2943            return vector_space_homspace.VectorSpaceHomspace(self, Y, category)
     2944        import free_module_homspace
     2945        return free_module_homspace.FreeModuleHomspace(self, Y, category)
     2946
    28992947    def scale(self, other):
    29002948        """
    29012949        Return the product of self by the number other, which is the module
  • sage/modules/free_module_homspace.py

    diff --git a/sage/modules/free_module_homspace.py b/sage/modules/free_module_homspace.py
    a b  
    7979from matrix_morphism import MatrixMorphism
    8080
    8181def is_FreeModuleHomspace(x):
    82     """
    83     Return True if x is a Free module homspace.
     82    r"""
     83    Return ``True`` if ``x`` is a free module homspace.
    8484
    85     EXAMPLES::
     85    EXAMPLES:
    8686
    87         sage: H = Hom(QQ^3, QQ^2)
     87    Notice that every vector space is a field, but when we construct a set of
     88    morphisms between two vector spaces, it is a ``VectorSpaceHomspace``,
     89    which qualifies as a ``FreeModuleHomspace``, since the former is
     90    special case of the latter.
     91
     92        sage: H = Hom(ZZ^3, ZZ^2)
     93        sage: type(H)
     94        <class 'sage.modules.free_module_homspace.FreeModuleHomspace_with_category'>
    8895        sage: sage.modules.free_module_homspace.is_FreeModuleHomspace(H)
    8996        True
    90         sage: sage.modules.free_module_homspace.is_FreeModuleHomspace(2)
     97
     98        sage: K = Hom(QQ^3, ZZ^2)
     99        sage: type(K)
     100        <class 'sage.modules.free_module_homspace.FreeModuleHomspace_with_category'>
     101        sage: sage.modules.free_module_homspace.is_FreeModuleHomspace(K)
     102        True
     103
     104        sage: L = Hom(ZZ^3, QQ^2)
     105        sage: type(L)
     106        <class 'sage.modules.free_module_homspace.FreeModuleHomspace_with_category'>
     107        sage: sage.modules.free_module_homspace.is_FreeModuleHomspace(L)
     108        True
     109
     110        sage: P = Hom(QQ^3, QQ^2)
     111        sage: type(P)
     112        <class 'sage.modules.vector_space_homspace.VectorSpaceHomspace_with_category'>
     113        sage: sage.modules.free_module_homspace.is_FreeModuleHomspace(P)
     114        True
     115
     116        sage: sage.modules.free_module_homspace.is_FreeModuleHomspace('junk')
    91117        False
    92118    """
    93119    return isinstance(x, FreeModuleHomspace)
    94120
    95121class FreeModuleHomspace(sage.categories.homset.HomsetWithBase):
    96122    def __call__(self, A, check=True):
    97         """
     123        r"""
    98124        INPUT:
    99125
    100126        - A -- either a matrix or a list/tuple of images of generators,
     
    109135
    110136        EXAMPLES::
    111137
    112             sage: V = (QQ^3).span_of_basis([[1,1,0],[1,0,2]])
     138            sage: V = (ZZ^3).span_of_basis([[1,1,0],[1,0,2]])
    113139            sage: H = V.Hom(V); H
    114140            Set of Morphisms from ...
    115141            sage: H([V.0,V.1])                    # indirect doctest
  • sage/modules/matrix_morphism.py

    diff --git a/sage/modules/matrix_morphism.py b/sage/modules/matrix_morphism.py
    a b  
    12511251        self._matrix = A
    12521252        MatrixMorphism_abstract.__init__(self, parent)
    12531253
    1254     def matrix(self):
    1255         """
    1256         Return matrix that defines this morphism.
    1257        
     1254    def matrix(self, side='left'):
     1255        r"""
     1256        Return a matrix that defines this morphism.
     1257
     1258        INPUT:
     1259
     1260        - ``side`` - default:``'left'`` - the side of the matrix
     1261          where a vector is placed to effect the morphism (function).
     1262
     1263        OUTPUT:
     1264
     1265        A matrix which represents the morphism, relative to bases
     1266        for the domain and codomain.  If the modules are provided
     1267        with user bases, then the representation is relative to
     1268        these bases.
     1269
     1270        Internally, Sage represents a matrix morphism with the
     1271        matrix multiplying a row vector placed to the left of the
     1272        matrix.  If the option ``side='right'`` is used, then a
     1273        matrix is returned that acts on a vector to the right of
     1274        the matrix.  These two matrices are just transposes of
     1275        each other and the difference is just a preference for
     1276        the style of representation.
     1277
    12581278        EXAMPLES::
    12591279
    1260             sage: V = ZZ^2; phi = V.hom([3*V.0, 2*V.1])
     1280            sage: V = ZZ^2; W = ZZ^3
     1281            sage: phi = V.hom([3*V.0 - 5*V.1, 4*V.0 + 2*V.1, V.0 + V.1], W)
    12611282            sage: phi.matrix()
    1262             [3 0]
    1263             [0 2]
     1283            [ 3  4  1]
     1284            [-5  2  1]
     1285
     1286            sage: phi.matrix(side='right')
     1287            [ 3 -5]
     1288            [ 4  2]
     1289            [ 1  1]
     1290
     1291        TESTS::
     1292
     1293            sage: V = ZZ^2
     1294            sage: phi = V.hom([3*V.0, 2*V.1])
     1295            sage: phi.matrix(side='junk')
     1296            Traceback (most recent call last):
     1297            ...
     1298            ValueError: side must be 'left' or 'right', not junk
    12641299        """
    1265         return self._matrix
     1300        if not side in ['left', 'right']:
     1301            raise ValueError("side must be 'left' or 'right', not {0}".format(side))
     1302        if side == 'left':
     1303            return self._matrix
     1304        else:
     1305            return self._matrix.transpose()
    12661306
    12671307
    12681308    def _repr_(self):
  • new file sage/modules/vector_space_homspace.py

    diff --git a/sage/modules/vector_space_homspace.py b/sage/modules/vector_space_homspace.py
    new file mode 100644
    - +  
     1r"""
     2Space of Morphisms of Vector Spaces (Linear Transformations)
     3
     4AUTHOR:
     5
     6    - Rob Beezer: (2011-06-29)
     7
     8A :class:`VectorSpaceHomspace` object represents the set of all
     9possible homomorphisms from one vector space to another.
     10These mappings are usually known as linear transformations.
     11
     12For more information on the use linear of transformations,
     13consult the documentation for vector space morphisms at
     14:mod:`sage.modules.vector_space_morphism`. Also, this is
     15an extremely thin veneer on free module homspaces
     16(:mod:`sage.modules.free_module_homspace`) and free module
     17morphisms (:mod:`sage.modules.free_module_morphism`) -
     18objects which might also be useful, and places
     19where much of the documentation resides.
     20
     21EXAMPLES:
     22
     23Creation and basic examination is simple. ::
     24
     25    sage: V = QQ^3
     26    sage: W = QQ^2
     27    sage: H = Hom(V, W)
     28    sage: H
     29    Set of Morphisms (Linear Transformations) from Vector space of dimension 3 over Rational Field to Vector space of dimension 2 over Rational Field
     30    sage: H.domain()
     31    Vector space of dimension 3 over Rational Field
     32    sage: H.codomain()
     33    Vector space of dimension 2 over Rational Field
     34
     35Homspaces have a few useful properties.  A basis is provided by
     36a list of matrix representations, where these matrix representatives
     37are relative to the bases of the domain and codomain.  ::
     38
     39    sage: K = Hom(GF(3)^2, GF(3)^2)
     40    sage: B = K.basis()
     41    sage: for f in B:
     42    ...     print f, "\n"
     43    Linear transformation represented by the matrix:
     44    (acting on vectors to the left of the matrix)
     45    [1 0]
     46    [0 0]
     47    Domain: Vector space of dimension 2 over Finite Field of size 3
     48    Codomain: Vector space of dimension 2 over Finite Field of size 3
     49    <BLANKLINE>
     50    Linear transformation represented by the matrix:
     51    (acting on vectors to the left of the matrix)
     52    [0 1]
     53    [0 0]
     54    Domain: Vector space of dimension 2 over Finite Field of size 3
     55    Codomain: Vector space of dimension 2 over Finite Field of size 3
     56    <BLANKLINE>
     57    Linear transformation represented by the matrix:
     58    (acting on vectors to the left of the matrix)
     59    [0 0]
     60    [1 0]
     61    Domain: Vector space of dimension 2 over Finite Field of size 3
     62    Codomain: Vector space of dimension 2 over Finite Field of size 3
     63    <BLANKLINE>
     64    Linear transformation represented by the matrix:
     65    (acting on vectors to the left of the matrix)
     66    [0 0]
     67    [0 1]
     68    Domain: Vector space of dimension 2 over Finite Field of size 3
     69    Codomain: Vector space of dimension 2 over Finite Field of size 3
     70    <BLANKLINE>
     71
     72The zero and identity mappings are properties of the space.
     73The identity mapping will only be available if the domain and codomain
     74allow for endomorphisms (equal vector spaces with equal bases).  ::
     75
     76    sage: H = Hom(QQ^3, QQ^3)
     77    sage: g = H.zero()
     78    sage: g([1, 1/2, -3])
     79    (0, 0, 0)
     80    sage: f = H.identity()
     81    sage: f([1, 1/2, -3])
     82    (1, 1/2, -3)
     83
     84The homspace may be used with various representations of a
     85morphism in the space to create the morphism.  We demonstrate
     86three ways to create the same linear transformation between
     87two two-dimensional subspaces of `QQ^3`.  The ``V.n`` notation
     88is a shortcut to the generators of each vector space, better
     89known as the basis elements.  Note that the matrix representations
     90are relative to the bases, which are purposely fixed when the
     91subspaces are created ("user bases").  ::
     92
     93    sage: U = QQ^3
     94    sage: V = U.subspace_with_basis([U.0+U.1, U.1-U.2])
     95    sage: W = U.subspace_with_basis([U.0, U.1+U.2])
     96    sage: H = Hom(V, W)
     97
     98First, with a matrix.  Note that the matrix representation
     99acts by matrix multiplication with the vector on the left.
     100The input to the linear transformation, ``(3, 1, 2)``,
     101is converted to the coordinate vector ``(3, -2)``, then
     102matrix multiplication yields the vector ``(-3, -2)``,
     103which represents the vector ``(-3, -2, -2)`` in the codomain.  ::
     104
     105    sage: m = matrix(QQ, [[1, 2], [3, 4]])
     106    sage: f1 = H(m)
     107    sage: f1
     108    Linear transformation represented by the matrix:
     109    (acting on vectors to the left of the matrix)
     110    [1 2]
     111    [3 4]
     112    Domain: Vector space of degree 3 and dimension 2 over Rational Field
     113    User basis matrix:
     114    [ 1  1  0]
     115    [ 0  1 -1]
     116    Codomain: Vector space of degree 3 and dimension 2 over Rational Field
     117    User basis matrix:
     118    [1 0 0]
     119    [0 1 1]
     120    sage: f1([3,1,2])
     121    (-3, -2, -2)
     122
     123Second, with a list of images of the domain's basis elements.  ::
     124
     125    sage: img = [1*(U.0) + 2*(U.1+U.2), 3*U.0 + 4*(U.1+U.2)]
     126    sage: f2 = H(img)
     127    sage: f2
     128    Linear transformation represented by the matrix:
     129    (acting on vectors to the left of the matrix)
     130    [1 2]
     131    [3 4]
     132    Domain: Vector space of degree 3 and dimension 2 over Rational Field
     133    User basis matrix:
     134    [ 1  1  0]
     135    [ 0  1 -1]
     136    Codomain: Vector space of degree 3 and dimension 2 over Rational Field
     137    User basis matrix:
     138    [1 0 0]
     139    [0 1 1]
     140    sage: f2([3,1,2])
     141    (-3, -2, -2)
     142
     143Third, with a linear function taking the domain to the codomain.  ::
     144
     145    sage: g = lambda x: vector(QQ, [-2*x[0]+3*x[1], -2*x[0]+4*x[1], -2*x[0]+4*x[1]])
     146    sage: f3 = H(g)
     147    sage: f3
     148    Linear transformation represented by the matrix:
     149    (acting on vectors to the left of the matrix)
     150    [1 2]
     151    [3 4]
     152    Domain: Vector space of degree 3 and dimension 2 over Rational Field
     153    User basis matrix:
     154    [ 1  1  0]
     155    [ 0  1 -1]
     156    Codomain: Vector space of degree 3 and dimension 2 over Rational Field
     157    User basis matrix:
     158    [1 0 0]
     159    [0 1 1]
     160    sage: f3([3,1,2])
     161    (-3, -2, -2)
     162
     163The three linear transformations look the same, and are the same.  ::
     164
     165    sage: f1 == f2
     166    True
     167    sage: f2 == f3
     168    True
     169
     170TESTS::
     171
     172    sage: V = QQ^2
     173    sage: W = QQ^3
     174    sage: H = Hom(QQ^2, QQ^3)
     175    sage: loads(dumps(H))
     176    Set of Morphisms (Linear Transformations) from Vector space of dimension 2 over Rational Field to Vector space of dimension 3 over Rational Field
     177    sage: loads(dumps(H)) == H
     178    True
     179"""
     180
     181####################################################################################
     182#       Copyright (C) 2011 Rob Beezer <beezer@ups.edu>
     183#
     184#  Distributed under the terms of the GNU General Public License (GPL)
     185#
     186#    This code is distributed in the hope that it will be useful,
     187#    but WITHOUT ANY WARRANTY; without even the implied warranty of
     188#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     189#    General Public License for more details.
     190#
     191#  The full text of the GPL is available at:
     192#
     193#                  http://www.gnu.org/licenses/
     194####################################################################################
     195
     196import inspect
     197import sage.matrix.all as matrix
     198import sage.modules.free_module_homspace
     199import vector_space_morphism
     200
     201# This module initially overrides just the minimum functionality necessary
     202# from  sage.modules.free_module_homspace.FreeModuleHomSpace.
     203# If additional methods here override the free module homspace methods,
     204# consider adjusting the free module doctests, since many are written with
     205# examples that are actually vector spaces and not so many use "pure" modules
     206# for the examples.
     207
     208
     209def is_VectorSpaceHomspace(x):
     210    r"""
     211    Return ``True`` if ``x`` is a vector space homspace.
     212
     213    INPUT:
     214
     215    ``x`` - anything
     216
     217    EXAMPLES:
     218
     219    To be a vector space morphism, the domain and codomain must both be
     220    vector spaces, in other words, modules over fields.  If either
     221    set is just a module, then the ``Hom()`` constructor will build a
     222    space of free module morphisms.  ::
     223
     224        sage: H = Hom(QQ^3, QQ^2)
     225        sage: type(H)
     226        <class 'sage.modules.vector_space_homspace.VectorSpaceHomspace_with_category'>
     227        sage: sage.modules.vector_space_homspace.is_VectorSpaceHomspace(H)
     228        True
     229
     230        sage: K = Hom(QQ^3, ZZ^2)
     231        sage: type(K)
     232        <class 'sage.modules.free_module_homspace.FreeModuleHomspace_with_category'>
     233        sage: sage.modules.vector_space_homspace.is_VectorSpaceHomspace(K)
     234        False
     235
     236        sage: L = Hom(ZZ^3, QQ^2)
     237        sage: type(L)
     238        <class 'sage.modules.free_module_homspace.FreeModuleHomspace_with_category'>
     239        sage: sage.modules.vector_space_homspace.is_VectorSpaceHomspace(L)
     240        False
     241
     242        sage: sage.modules.vector_space_homspace.is_VectorSpaceHomspace('junk')
     243        False
     244    """
     245    return isinstance(x, VectorSpaceHomspace)
     246
     247class VectorSpaceHomspace(sage.modules.free_module_homspace.FreeModuleHomspace):
     248
     249    def __call__(self, A, check=True):
     250        r"""
     251        INPUT:
     252
     253        - ``A`` - one of several possible inputs representing
     254          a morphism from this vector space homspace.
     255          - a vector space morphism in this homspace
     256          - a matrix representation relative to the bases of the vector spaces,
     257            which acts on a vector placed to the left of the matrix
     258          - a list or tuple containing images of the domain's basis vectors
     259          - a function from the domain to the codomain
     260        - ``check`` (default: True) - ``True`` or ``False``, required for
     261          compatibility with calls from
     262          :meth:`sage.structure.parent_gens.ParentWithGens.hom`.
     263
     264        EXAMPLES::
     265
     266            sage: V = (QQ^3).span_of_basis([[1,1,0],[1,0,2]])
     267            sage: H = V.Hom(V)
     268            sage: H
     269            Set of Morphisms (Linear Transformations) from Vector space of degree 3 and dimension 2 over Rational Field
     270            User basis matrix:
     271            [1 1 0]
     272            [1 0 2] to Vector space of degree 3 and dimension 2 over Rational Field
     273            User basis matrix:
     274            [1 1 0]
     275            [1 0 2]
     276
     277            sage: A = matrix(QQ, [[0, 1], [1, 0]])
     278            sage: rho = H(A)          # indirect doctest
     279            sage: rho
     280            Linear transformation represented by the matrix:
     281            (acting on vectors to the left of the matrix)
     282            [0 1]
     283            [1 0]
     284            Domain: Vector space of degree 3 and dimension 2 over Rational Field
     285            User basis matrix:
     286            [1 1 0]
     287            [1 0 2]
     288            Codomain: Vector space of degree 3 and dimension 2 over Rational Field
     289            User basis matrix:
     290            [1 1 0]
     291            [1 0 2]
     292
     293            sage: phi = H([V.1, V.0])
     294            sage: phi(V.1) == V.0
     295            True
     296            sage: phi(V.0) == V.1
     297            True
     298            sage: phi
     299            Linear transformation represented by the matrix:
     300            (acting on vectors to the left of the matrix)
     301            [0 1]
     302            [1 0]
     303            Domain: Vector space of degree 3 and dimension 2 over Rational Field
     304            User basis matrix:
     305            [1 1 0]
     306            [1 0 2]
     307            Codomain: Vector space of degree 3 and dimension 2 over Rational Field
     308            User basis matrix:
     309            [1 1 0]
     310            [1 0 2]
     311
     312            sage: f = lambda x: vector(QQ, [x[0], (1/2)*x[2], 2*x[1]])
     313            sage: zeta = H(f)
     314            sage: zeta
     315            Linear transformation represented by the matrix:
     316            (acting on vectors to the left of the matrix)
     317            [0 1]
     318            [1 0]
     319            Domain: Vector space of degree 3 and dimension 2 over Rational Field
     320            User basis matrix:
     321            [1 1 0]
     322            [1 0 2]
     323            Codomain: Vector space of degree 3 and dimension 2 over Rational Field
     324            User basis matrix:
     325            [1 1 0]
     326            [1 0 2]
     327
     328        See other examples in the module-level documentation.
     329        """
     330        ##import vector_space_morphism
     331        ##print "in homspace call"
     332        ##if vector_space_morphism.is_VectorSpaceMorphism(A):
     333            ##if self == A.parent():
     334                ##return A
     335            ##else:
     336                ##raise ValueError('morphism is from wrong homspace')
     337        # convert A to a matrix representation, for class init
     338        D = self.domain()
     339        C = self.codomain()
     340        if matrix.is_Matrix(A):
     341            pass
     342        elif inspect.isfunction(A):
     343            try:
     344                images = [A(g) for g in D.basis()]
     345            except (ValueError, TypeError, IndexError), e:
     346                msg = 'function cannot be applied properly to some basis element because\n' + e.args[0]
     347                raise ValueError(msg)
     348            try:
     349                A = matrix.matrix([C.coordinates(C(a)) for a in images])
     350            except (ArithmeticError, TypeError), e:
     351                msg = 'some image of the function is not in the codomain, because\n' + e.args[0]
     352                raise ArithmeticError(msg)
     353        elif isinstance(A, (list, tuple)):
     354            if len(A) != len(D.basis()):
     355                msg = "number of images should equal the size of the domain's basis (={0}), not {1}"
     356                raise ValueError(msg.format(len(D.basis()), len(A)))
     357            try:
     358                v = [C(a) for a in A]
     359                A = matrix.matrix([C.coordinates(a) for a in v])
     360            except (ArithmeticError, TypeError), e:
     361                msg = 'some proposed image is not in the codomain, because\n' + e.args[0]
     362                raise ArithmeticError(msg)
     363        else:
     364            msg = 'vector space homspace can only coerce matrices, functions or lists, not {0}'
     365            raise TypeError(msg.format(A))
     366        ## print "to vsp morphism init with ", self, A
     367        return vector_space_morphism.VectorSpaceMorphism(self, A)
     368
     369
     370    def _repr_(self):
     371        r"""
     372        Text representation of a space of vector space morphisms.
     373
     374        EXAMPLE::
     375
     376            sage: H = Hom(QQ^2, QQ^3)
     377            sage: H._repr_()
     378            'Set of Morphisms (Linear Transformations) from Vector space of dimension 2 over Rational Field to Vector space of dimension 3 over Rational Field'
     379        """
     380        msg = 'Set of Morphisms (Linear Transformations) from {0} to {1}'
     381        return msg.format(self.domain(), self.codomain())
     382 No newline at end of file
  • new file sage/modules/vector_space_morphism.py

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