Ticket #12876: trac_12876_category_abstract_classes_for_hom.patch

File trac_12876_category_abstract_classes_for_hom.patch, 46.2 KB (added by nthiery, 6 years ago)

Combined patch

  • doc/en/thematic_tutorials/coercion_and_categories.rst

    # HG changeset patch
    # User Nicolas M. Thiery <nthiery@users.sf.net>
    # Date 1335304627 -7200
    # Node ID 07f1ac6542d7d5d3257fea7331d8d30417826fde
    # Parent  d80269d214c21329c1d29885770629d564648717
    #12876: Fix element and parent classes of Hom categories to be abstract, and simplify the Hom logic
    
    This patch fixes the parent and element classes for Hom categories to
    be purely abstract, and simplifies the Hom logic:
    
    - Unified the logic for selecting the class when building a Homset
      (e.g. Homset, RingHomset, HeckeModuleHomspace, ...). This is now
      systematically done through the _Hom_ hook. The logic still has a
      fundamental flaw, but that's for the later #10668.
    - The cache for Hom is handled at a single point in Hom
      In particular, homsets created via the _Hom_ hook are now unique.
    - If category is None, Hom simply calls itself with the meet of the
      categories of the parent, which removes a cache handling duplication
      in the code.
    - Parent.Hom calls  Hom directly (removes duplicate _Hom_ logic).
    - ParentWithBase.Hom was redundant and is gone.
    - Reduce the footprint of the current trick to delegate
      Hom(F,F)(on_basis=...) to module_morphism, allow for the diagonal
      option too, an make sure the homset category is set properly.
    - Update a doctest in sage.modules.vector_space_homspace to take into
      account that homsets created via _Hom_ are now unique.
    - Scheme is (apparently) an abstract base class; so it should not be
      instantiated. I changed some doctests in
      sage.schemes.generic.SchemeMorphism to use instead the concrete
      Spec(ZZ). Those doctests were breaking because Scheme does not
      implement equality, which is required for Hom caching.
    
    As a byproduct, the HeckeModules category does not import any more
    HeckeModulesHomspace, which was a recurrent source of import loops.
    
    It was also needed to not use a weak cache in infinite_polynomial_ring
    for data that are cached anyway.
    * * *
    #12876: Review patch, adding a test demonstrating a cache shortcut
    
    diff --git a/doc/en/thematic_tutorials/coercion_and_categories.rst b/doc/en/thematic_tutorials/coercion_and_categories.rst
    a b coincide:: 
    408408    sage: len([s for s in dir(MS1) if inspect.ismethod(getattr(MS1,s,None))])
    409409    55
    410410    sage: len([s for s in dir(MS2) if inspect.ismethod(getattr(MS2,s,None))])
    411     77
     411    78
    412412    sage: MS1.__class__ is MS2.__class__
    413413    True
    414414
  • sage/categories/hecke_modules.py

    diff --git a/sage/categories/hecke_modules.py b/sage/categories/hecke_modules.py
    a b class HeckeModules(Category_module): 
    8484        R = self.base_ring()
    8585        return [ModulesWithBasis(R)]
    8686
     87
     88    class ParentMethods:
     89
     90        def _Hom_(self, Y, category):
     91            r"""
     92            Returns the homset from ``self`` to ``Y`` in the category ``category``
     93
     94            INPUT::
     95
     96            - ``Y`` -- an Hecke module
     97            - ``category`` -- a subcategory of :class:`HeckeModules`() or None
     98
     99            The sole purpose of this method is to construct the homset
     100            as a :class:`~sage.modular.hecke.homspace.HeckeModuleHomspace`. If
     101            ``category`` is specified and is not a subcategory of
     102            :class:`HeckeModules`, a ``TypeError`` is raised instead
     103
     104            This method is not meant to be called directly. Please use
     105            :func:`sage.categories.homset.Hom` instead.
     106
     107            EXAMPLES::
     108
     109                sage: M = ModularForms(Gamma0(7), 4)
     110                sage: H = M._Hom_(M, category = HeckeModules(QQ)); H
     111                Set of Morphisms from Modular Forms space of dimension 3 for Congruence Subgroup Gamma0(7) of weight 4 over Rational Field to Modular Forms space of dimension 3 for Congruence Subgroup Gamma0(7) of weight 4 over Rational Field in Category of Hecke modules over Rational Field
     112                sage: H.__class__
     113                <class 'sage.modular.hecke.homspace.HeckeModuleHomspace_with_category'>
     114                sage: TestSuite(H).run(skip=["_test_zero", "_test_elements", "_test_an_element", "_test_additive_associativity", "_test_elements_eq", "_test_elements_eq_reflexive", "_test_elements_eq_transitive", "_test_elements_eq_symmetric", "_test_elements_neq", "_test_some_elements"])
     115
     116            Fixing :meth:`_test_zero` (``__call__`` should accept a
     117            function as input) and :meth:`_test_elements*` (modular
     118            form morphisms elements should inherit from categories) is
     119            :trac:`12879`.
     120
     121            TESTS::
     122
     123                sage: H = M._Hom_(M, category = HeckeModules(GF(5))); H
     124                Traceback (most recent call last):
     125                ...
     126                TypeError: Category of Hecke modules over Finite Field of size 5 is not a subcategory of Category of Hecke modules over Rational Field
     127
     128            """
     129            # TODO: double check that it's the correct HeckeModules category below:
     130            if category is not None and not category.is_subcategory(HeckeModules(self.base_ring())):
     131                raise TypeError, "%s is not a subcategory of %s"%(category, HeckeModules(self.base_ring()))
     132            from sage.modular.hecke.homspace import HeckeModuleHomspace
     133            return HeckeModuleHomspace(self, Y, category = category)
     134
    87135    class HomCategory(HomCategory):
    88136        def extra_super_categories(self):
    89137            """
    class HeckeModules(Category_module): 
    94142            """
    95143            return [] # FIXME: what category structure is there on Homsets of hecke modules?
    96144
    97         import sage.modular.hecke.homspace
    98         class ParentMethods(sage.modular.hecke.homspace.HeckeModuleHomspace):
     145
     146        def base_ring(self):
     147            """
     148            EXAMPLES::
     149
     150                sage: HeckeModules(QQ).hom_category().base_ring()
     151                Rational Field
     152            """
     153            return self.base().base_ring()
     154
     155        class ParentMethods:
    99156            pass
  • sage/categories/homset.py

    diff --git a/sage/categories/homset.py b/sage/categories/homset.py
    a b AUTHORS: 
    6767from sage.categories.category import Category
    6868import morphism
    6969from sage.structure.parent import Parent, Set_generic
     70from sage.misc.constant_function import ConstantFunction
    7071from sage.misc.lazy_attribute import lazy_attribute
    7172import types
    7273
    def Hom(X, Y, category=None): 
    110111        Set of Morphisms from Integer Ring to Rational Field in Category of sets
    111112
    112113        sage: Hom(FreeModule(ZZ,1), FreeModule(QQ,1))
    113         Set of Morphisms from Ambient free module of rank 1 over the principal ideal domain Integer Ring to Vector space of dimension 1 over Rational Field in Category of modules with basis over Integer Ring
     114        Set of Morphisms from Ambient free module of rank 1 over the principal ideal domain Integer Ring to Vector space of dimension 1 over Rational Field in Category of commutative additive groups
    114115        sage: Hom(FreeModule(QQ,1), FreeModule(ZZ,1))
    115         Set of Morphisms from Vector space of dimension 1 over Rational Field to Ambient free module of rank 1 over the principal ideal domain Integer Ring in Category of vector spaces over Rational Field
     116        Set of Morphisms from Vector space of dimension 1 over Rational Field to Ambient free module of rank 1 over the principal ideal domain Integer Ring in Category of commutative additive groups
    116117
    117118    Here, we test against a memory leak that has been fixed at :trac:`11521` by
    118119    using a weak cache::
    def Hom(X, Y, category=None): 
    153154        ...
    154155        TypeError: Integer Ring is not in Category of groups
    155156
     157    A parent (or a parent class of a category) may specify how to
     158    construct certain homsets by implementing a method ``_Hom_(self,
     159    codomain, category)``. This method should either construct the
     160    requested homset or raise a ``TypeError``. This hook is currently
     161    mostly used to create homsets in some specific subclass of
     162    :class:`Homset` (e.g. :class:`sage.rings.homset.RingHomset`)::
     163
     164        sage: Hom(QQ,QQ).__class__
     165        <class 'sage.rings.homset.RingHomset_generic_with_category'>
     166
     167    Do not call this hook directly to create homsets, as it does not
     168    handle unique representation::
     169
     170        sage: Hom(QQ,QQ) == QQ._Hom_(QQ, category=QQ.category())
     171        True
     172        sage: Hom(QQ,QQ) is QQ._Hom_(QQ, category=QQ.category())
     173        False
    156174
    157175    TESTS:
    158176
    def Hom(X, Y, category=None): 
    164182        sage: H1 is H2
    165183        True
    166184
    167     Some doc tests in :mod:`sage.rings` (need to) break the unique parent
    168     assumption. But if domain or codomain are not unique parents, then the hom
    169     set will not fit. That is to say, the hom set found in the cache will have a
    170     (co)domain that is equal to, but not identic with, the given (co)domain.
     185    Moreover, if no category is provided, then the result is identical
     186    with the result for the meet of the categories of the domain and
     187    the codomain::
    171188
    172     By :trac:`9138`, we abandon the uniqueness of hom sets, if the domain or
    173     codomain break uniqueness::
     189        sage: Hom(QQ, ZZ) is Hom(QQ,ZZ, Category.meet([QQ.category(), ZZ.category()]))
     190        True
     191
     192    Some doc tests in :mod:`sage.rings` (need to) break the unique
     193    parent assumption. But if domain or codomain are not unique
     194    parents, then the homset will not fit. That is to say, the hom set
     195    found in the cache will have a (co)domain that is equal to, but
     196    not identical with, the given (co)domain.
     197
     198    By :trac:`9138`, we abandon the uniqueness of homsets, if the
     199    domain or codomain break uniqueness::
    174200
    175201        sage: from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing_polydict_domain
    176202        sage: P.<x,y,z>=MPolynomialRing_polydict_domain(QQ, 3, order='degrevlex')
    def Hom(X, Y, category=None): 
    190216        sage: H1 is H2
    191217        False
    192218
    193     It is always the most recently constructed hom set that remains in
     219    It is always the most recently constructed homset that remains in
    194220    the cache::
    195221
    196222        sage: H2 is Hom(QQ,Q)
    197223        True
    198224
     225    Variation on the theme::
     226
     227        sage: U1 = FreeModule(ZZ,2)
     228        sage: U2 = FreeModule(ZZ,2,inner_product_matrix=matrix([[1,0],[0,-1]]))
     229        sage: U1 == U2, U1 is U2
     230        (True, False)
     231        sage: V = ZZ^3
     232        sage: H1 = Hom(U1, V); H2 = Hom(U2, V)
     233        sage: H1 == H2, H1 is H2
     234        (True, False)
     235        sage: H1 = Hom(V, U1); H2 = Hom(V, U2)
     236        sage: H1 == H2, H1 is H2
     237        (True, False)
     238
    199239    Since :trac:`11900`, the meet of the categories of the given arguments is
    200240    used to determine the default category of the homset. This can also be a
    201241    join category, as in the following example::
    def Hom(X, Y, category=None): 
    211251
    212252    .. TODO::
    213253
    214         design decision: how much of the homset comes from the
    215         category of ``X`` and ``Y``, and how much from the specific ``X`` and
    216         ``Y``.  In particular, do we need several parent classes depending on
    217         ``X`` and ``Y``, or does the difference only lie in the elements (i.e.
    218         the morphism), and of course how the parent calls their constructors.
     254        - Design decision: how much of the homset comes from the
     255          category of ``X`` and ``Y``, and how much from the specific
     256          ``X`` and ``Y``.  In particular, do we need several parent
     257          classes depending on ``X`` and ``Y``, or does the difference
     258          only lie in the elements (i.e.  the morphism), and of course
     259          how the parent calls their constructors.
     260        - Specify the protocol for the ``_Hom_`` hook in case of ambiguity
     261          (e.g. if both a parent and some category thereof provide one).
     262
     263    TESTS::
     264
     265        sage: R = sage.structure.parent.Set_PythonType(int)
     266        sage: S = sage.structure.parent.Set_PythonType(float)
     267        sage: Hom(R, S)
     268        Set of Morphisms from Set of Python objects of type 'int' to Set of Python objects of type 'float' in Category of sets
    219269
    220270    """
    221271    # This should use cache_function instead
    222     # However it breaks somehow the coercion (see e.g. sage -t sage.rings.real_mpfr)
    223     # To be investigated.
     272    # However some special handling is currently needed for
     273    # domains/docomains that break the unique parent condition. Also,
     274    # at some point, it somehow broke the coercion (see e.g. sage -t
     275    # sage.rings.real_mpfr). To be investigated.
    224276    global _cache
    225277    key = (X,Y,category)
    226278    try:
    def Hom(X, Y, category=None): 
    228280    except KeyError:
    229281        H = None
    230282    if H is not None:
    231         # Are domain or codomain breaking the unique parent condition?
     283        # Return H unless the domain or codomain breaks the unique parent condition
    232284        if H.domain() is X and H.codomain() is Y:
    233285            return H
    234286
    235     try:
    236         # Apparently X._Hom_ is supposed to be cached
    237         # but it is not in some cases (e.g. X is a finite field)
    238         # To be investigated
    239         H = X._Hom_(Y,category)
    240         _cache[key] = H
    241         return H
    242     except (AttributeError, TypeError):
    243         pass
    244 
     287    # Determines the category
    245288    cat_X = X.category()
    246289    cat_Y = Y.category()
    247290    if category is None:
    248291        category = cat_X._meet_(cat_Y)
    249     elif isinstance(category, Category):
     292        # Recurse to make sure that Hom(X, Y) and Hom(X, Y, category) are identical
     293        H = Hom(X, Y, category)
     294    else:
     295        if not isinstance(category, Category):
     296            raise TypeError, "Argument category (= %s) must be a category."%category
    250297        if not cat_X.is_subcategory(category):
    251298            raise TypeError, "%s is not in %s"%(X, category)
    252299        if not cat_Y.is_subcategory(category):
    253300            raise TypeError, "%s is not in %s"%(Y, category)
    254     else:
    255         raise TypeError, "Argument category (= %s) must be a category."%category
    256     # Now, as the category may have changed, we try to find the hom set in the cache, again:
    257     key = (X,Y,category)
    258     try:
    259         H = _cache[key]
    260     except KeyError:
    261         H = None
    262     if H is not None:
    263         # Are domain or codomain breaking the unique parent condition?
    264         if H.domain() is X and H.codomain() is Y:
    265             return H
    266301
    267     # coercing would be incredibly annoying, since the domain and codomain
    268     # are totally different objects
    269     #X = category(X); Y = category(Y)
    270 
    271     # construct H
    272     # Design question: should the Homset classes get the category or the homset category?
    273     # For the moment, this is the category, for compatibility with the current implementations
    274     # of Homset in rings, schemes, ...
    275     H = category.hom_category().parent_class(X, Y, category = category)
    276            
    277     ##_cache[key] = weakref.ref(H)
     302        # Construct H
     303        try: # _Hom_ hook from the parent
     304            H = X._Hom_(Y, category)
     305        except (AttributeError, TypeError):
     306            try:
     307                # Workaround in case the above fails, but the category
     308                # also provides a _Hom_ hook.
     309                # FIXME:
     310                # - If X._Hom_ actually comes from category and fails, it
     311                #   will be called twice.
     312                # - This is bound to fail if X is an extension type and
     313                #   does not actually inherit from category.parent_class
     314                H = category.parent_class._Hom_(X, Y, category = category)
     315            except (AttributeError, TypeError):
     316                # By default, construct a plain homset.
     317                H = Homset(X, Y, category = category)
    278318    _cache[key] = H
    279319    return H
    280320
    class Homset(Set_generic): 
    398438            ...
    399439            AttributeError: 'sage.rings.integer.Integer' object has no attribute 'hom_category'
    400440        """
    401 
    402441        self._domain = X
    403442        self._codomain = Y
    404443        if category is None:
    class Homset(Set_generic): 
    513552        """
    514553        return self.__category
    515554
    516     def __call__(self, x=None, y=None, check=True, on_basis=None):
     555    def __call__(self, x=None, y=None, check=True, **options):
    517556        """
    518557        Construct a morphism in this homset from ``x`` if possible.
    519558       
    class Homset(Set_generic): 
    561600            Set of Morphisms from {1, 2, 3} to {1, 2, 3} in Category of sets
    562601            sage: f(1), f(2), f(3) # todo: not implemented
    563602
     603            sage: H = Hom(ZZ, QQ, Sets())
     604            sage: f = H( ConstantFunction(2/3) )
     605            sage: f.parent()
     606            Set of Morphisms from Integer Ring to Rational Field in Category of sets
     607            sage: f(1), f(2), f(3)
     608            (2/3, 2/3, 2/3)
     609
    564610        AUTHORS:
    565611
    566612        - Robert Bradshaw, with changes by Nicolas M. Thiery
    567613        """
    568         # Temporary workaround: currently, HomCategory.ParentMethods's cannot override
    569         # this __call__ method because of the class inheritance order
    570         # This dispatches back the call there
    571         if on_basis is not None:
    572             return self.__call_on_basis__(on_basis = on_basis)
     614        if options:
     615            # TODO: this is specific for ModulesWithBasis; generalize
     616            # this to allow homsets and categories to provide more
     617            # morphism constructors (on_algebra_generators, ...)
     618            if 'on_basis' or 'diagonal' in options:
     619                return self.__call_on_basis__(category = self.homset_category(),
     620                                              **options)
     621            else:
     622                raise NotImplementedError
     623
    573624        assert x is not None
    574 
    575625        if isinstance(x, morphism.Morphism):
    576626            if x.parent() is self:
    577627                return x
    class Homset(Set_generic): 
    591641                    x = mor * x
    592642                return x
    593643
    594         if isinstance(x, types.FunctionType) or isinstance(x, types.MethodType):
     644        if isinstance(x, (types.FunctionType, types.MethodType, ConstantFunction)):
    595645            return self.element_class_set_morphism(self, x)
    596            
     646
    597647        raise TypeError, "Unable to coerce x (=%s) to a morphism in %s"%(x,self)
    598648
    599649    @lazy_attribute
    class Homset(Set_generic): 
    782832            sage: type(H)
    783833            <class 'sage.modules.free_module_homspace.FreeModuleHomspace_with_category'>
    784834            sage: H.reversed()
    785             Set of Morphisms from Ambient free module of rank 3 over the principal ideal domain Integer Ring to Ambient free module of rank 2 over the principal ideal domain Integer Ring in Category of hom sets in Category of modules with basis over Integer Ring
     835            Set of Morphisms from Ambient free module of rank 3 over the principal ideal domain Integer Ring to Ambient free module of rank 2 over the principal ideal domain Integer Ring in Category of modules with basis over Integer Ring
    786836            sage: type(H.reversed())
    787837            <class 'sage.modules.free_module_homspace.FreeModuleHomspace_with_category'>
    788838        """
    789         return Hom(self.codomain(), self.domain(), category = self.category())
     839        return Hom(self.codomain(), self.domain(), category = self.homset_category())
    790840       
    791841    ############### For compatibility with old coercion model #######################
    792842   
  • sage/categories/modules_with_basis.py

    diff --git a/sage/categories/modules_with_basis.py b/sage/categories/modules_with_basis.py
    a b class ModulesWithBasis(Category_over_bas 
    908908        The category of homomorphisms sets Hom(X,Y) for X, Y modules with basis
    909909        """
    910910
    911         class ParentMethods: #(sage.modules.free_module_homspace.FreeModuleHomspace): #    Only works for plain FreeModule's
    912             """
    913             Abstract class for hom sets
    914             """
    915 
    916             def __call__(self, on_basis = None, *args, **options):
     911        class ParentMethods:
     912            def __call_on_basis__(self, **options):
    917913                """
    918                 Construct an element of this homset
     914                Construct a morphism in this homset from a function defined on the basis
    919915
    920916                INPUT:
    921917
    922                  - on_basis (optional) -- a function from the indices
    923                    of the basis of the domain of ``self`` to the
    924                    codomain of ``self``
     918                - ``on_basis`` -- a function from the indices of the
     919                  basis of the domain of ``self`` to the codomain of
     920                  ``self``
     921
     922                This method simply delegates the work to
     923                :meth:`ModulesWithBasis.ParentMethods.module_morphism`. It
     924                is used by :meth:`Homset.__call__` to handle the
     925                ``on_basis`` argument, and will disapear as soon as
     926                the logic will be generalized.
    925927
    926928                EXAMPLES::
    927929
    class ModulesWithBasis(Category_over_bas 
    930932                    sage: H = Hom(X, Y)
    931933                    sage: x = X.basis()
    932934
     935                    sage: phi = H(on_basis = lambda i: Y.monomial(i) + 2*Y.monomial(i+1)) # indirect doctest
     936                    sage: phi
     937                    Generic morphism:
     938                    From: X
     939                    To:   Y
     940                    sage: phi(x[1] + x[3])
     941                    B[1] + 2*B[2] + B[3] + 2*B[4]
     942
     943                Diagonal functions can be constructed using the ``diagonal`` option::
     944
     945                    sage: X = CombinatorialFreeModule(QQ, [1,2,3,4]); X.rename("X")
     946                    sage: Y = CombinatorialFreeModule(QQ, [1,2,3,4], key="Y"); Y.rename("Y")
     947                    sage: H = Hom(X, Y)
     948                    sage: x = X.basis()
     949                    sage: phi = H(diagonal = lambda x: x^2)
     950                    sage: phi(x[1] + x[2] + x[3])
     951                    B[1] + 4*B[2] + 9*B[3]
     952
     953                TESTS::
     954
    933955                As for usual homsets, the argument can be a Python function::
    934956
    935957                    sage: phi = H(lambda x: Y.zero())
    class ModulesWithBasis(Category_over_bas 
    940962                    sage: phi(x[1] + x[3])
    941963                    0
    942964
    943                 With the on_basis argument, the function can instead
    944                 be constructed by extending by linearity a function on
    945                 the basis::
     965               We check that the homset category is properly set up::
    946966
    947                     sage: phi = H(on_basis = lambda i: Y.monomial(i) + 2*Y.monomial(i+1))
    948                     sage: phi
    949                     Generic morphism:
    950                     From: X
    951                     To:   Y
    952                     sage: phi(x[1] + x[3])
    953                     B[1] + 2*B[2] + B[3] + 2*B[4]
    954 
    955                 This is achieved internaly by using
    956                 :meth:`ModulesWithBasis.ParentMethods.module_morphism`, which see.
     967                    sage: category = FiniteDimensionalModulesWithBasis(QQ)
     968                    sage: X = CombinatorialFreeModule(QQ, [1,2,3], category = category);   X.rename("X")
     969                    sage: Y = CombinatorialFreeModule(QQ, [1,2,3,4], category = category); Y.rename("Y")
     970                    sage: H = Hom(X, Y)
     971                    sage: H.zero().category_for()
     972                    Category of finite dimensional modules with basis over Rational Field
    957973                """
    958                 if on_basis is not None:
    959                     args = (self.domain().module_morphism(on_basis, codomain = self.codomain()),) + args
    960                 h = Homset.__call__(self, *args, **options)
    961                 if on_basis is not None:
    962                     h._on_basis = on_basis
    963                 return h
    964 
    965             # Temporary hack
    966             __call_on_basis__ = __call__
    967 
    968             @lazy_attribute
    969             def element_class_set_morphism(self):
    970                 """
    971                 A base class for elements of this homset which are
    972                 also SetMorphism's, i.e. implemented by mean of a
    973                 Python function.
    974 
    975                 This overrides the default implementation
    976                 :meth:`Homset.element_class_set_morphism`, to also
    977                 inherit from categories.
    978 
    979                 Todo: refactor during the upcoming homset cleanup.
    980 
    981                 EXAMPLES::
    982 
    983                     sage: X = CombinatorialFreeModule(QQ, [1,2,3]);   X.rename("X")
    984                     sage: Y = CombinatorialFreeModule(QQ, [1,2,3,4]); Y.rename("Y")
    985                     sage: H = Hom(X, Y)
    986                     sage: H.element_class_set_morphism
    987                     <class 'sage.categories.morphism.SetMorphism_with_category'>
    988                     sage: H.element_class_set_morphism.mro()
    989                     [<class 'sage.categories.morphism.SetMorphism_with_category'>,
    990                      <type 'sage.categories.morphism.SetMorphism'>,
    991                      <type 'sage.categories.morphism.Morphism'>,
    992                      <type 'sage.categories.map.Map'>,
    993                      <type 'sage.structure.element.Element'>,
    994                      <type 'sage.structure.sage_object.SageObject'>,
    995                      <class 'sage.categories.modules_with_basis.ModulesWithBasis.HomCategory.element_class'>,
    996                      <class 'sage.categories.category.Modules.HomCategory.element_class'>,
    997                      <class 'sage.categories.vector_spaces.VectorSpaces.element_class'>,
    998                      ...]
    999 
    1000                 Compare with:
    1001 
    1002                     sage: H = Hom(ZZ, ZZ)
    1003                     sage: H.element_class_set_morphism
    1004                     <type 'sage.categories.morphism.SetMorphism'>
    1005                 """
    1006                 return self.__make_element_class__(SetMorphism, inherit = True)
     974                return self.domain().module_morphism(codomain = self.codomain(),
     975                                                     **options)
    1007976
    1008977        class ElementMethods:
    1009978            """
    1010979            Abstract class for morphisms of modules with basis
    1011980            """
     981            @cached_method
    1012982            def on_basis(self):
    1013983                """
    1014984                Returns the action of this morphism on basis elements
    class ModulesWithBasis(Category_over_bas 
    10351005                    sage: g == f
    10361006                    True
    10371007                """
    1038                 if not hasattr(self, "_on_basis"):
    1039                     monomial = self.domain().monomial
    1040                     self._on_basis = lambda t: self(monomial(t))
    1041                 return self._on_basis
    1042 
     1008                monomial = self.domain().monomial
     1009                return lambda t: self(monomial(t))
    10431010
    10441011    class CartesianProducts(CartesianProductsCategory):
    10451012        """
    class ModuleMorphismByLinearity(Morphism 
    13601327            B[2]
    13611328            sage: phi.on_basis() == phi_on_basis
    13621329            True
    1363 
    1364         Note: could probably be inherited from the categories
    13651330        """
    13661331        return self._on_basis
    13671332
  • sage/categories/objects.py

    diff --git a/sage/categories/objects.py b/sage/categories/objects.py
    a b class Objects(Category): 
    8686            from sets_cat import Sets
    8787            return [Sets()]
    8888
    89         class ParentMethods(Homset):
     89        class ParentMethods:
    9090            pass
  • sage/categories/rings.py

    diff --git a/sage/categories/rings.py b/sage/categories/rings.py
    a b class Rings(Category_singleton): 
    9292            """
    9393            return x*y - y*x
    9494
     95        def _Hom_(self, Y, category):
     96            r"""
     97            Returns the homset from ``self`` to ``Y`` in the category ``category``
     98
     99            INPUT::
     100
     101            - ``Y`` -- a ring
     102            - ``category`` -- a subcategory of :class:`Rings`() or None
     103
     104            The sole purpose of this method is to construct the homset
     105            as a :class:`~sage.rings.homset.RingHomset`. If
     106            ``category`` is specified and is not a subcategory of
     107            :class:`Rings`, a ``TypeError`` is raised instead
     108
     109            This method is not meant to be called directly. Please use
     110            :func:`sage.categories.homset.Hom` instead.
     111
     112            EXAMPLES::
     113
     114                sage: H = QQ._Hom_(QQ, category = Rings()); H
     115                Set of Homomorphisms from Rational Field to Rational Field
     116                sage: H.__class__
     117                <class 'sage.rings.homset.RingHomset_generic_with_category'>
     118
     119            TESTS::
     120
     121                sage: Hom(QQ, QQ, category = Rings()).__class__
     122                <class 'sage.rings.homset.RingHomset_generic_with_category'>
     123
     124                sage: Hom(CyclotomicField(3), QQ, category = Rings()).__class__
     125                <class 'sage.rings.number_field.morphism.CyclotomicFieldHomset_with_category'>
     126
     127                sage: TestSuite(Hom(QQ, QQ, category = Rings())).run() # indirect doctest
     128
     129            """
     130            if category is not None and not category.is_subcategory(Rings()):
     131                raise TypeError, "%s is not a subcategory of Rings()"%category
     132            if Y not in Rings():
     133                raise TypeError, "%s is not a ring"%Y
     134            from sage.rings.homset import RingHomset
     135            return RingHomset(self, Y, category = category)
     136
    95137        # this is already in sage.rings.ring.Ring,
    96138        # but not all rings descend from that class,
    97139        # e.g., matrix spaces.
    class Rings(Category_singleton): 
    489531
    490532            ``MS`` is not an instance of :class:`~sage.rings.ring.Ring`.
    491533            But since its category was fully initalised (which is not
    492             by default, by trac ticket #11900), it is an instance of
     534            by default, by :trac:`11900`), it is an instance of
    493535            the parent class of the category of rings. The quotient
    494536            method is inherited from there::
    495537
    class Rings(Category_singleton): 
    573615            """
    574616            raise TypeError, "Use self.quo(I) or self.quotient(I) to construct the quotient ring."
    575617
    576 
    577618    class ElementMethods:
    578619        pass
    579620
    580621
    581622    class HomCategory(HomCategory):
    582         def extra_super_categories(self):
    583             """
    584             EXAMPLES::
    585 
    586                 sage: Rings().hom_category().extra_super_categories()
    587                 [Category of sets]
    588             """
    589             from sage.categories.sets_cat import Sets
    590             return [Sets()]
    591 
    592 #         def get_Parent(self, X, Y):
    593 #             """
    594 #             Given two objects X and Y in this category, returns the parent
    595 #             class to be used for the collection of the morphisms of this
    596 #             category between X and Y.
    597 
    598 #             Returns self.ParentMethods by default.
    599 
    600 #             Rationale: some categories, like Rings or Schemes, currently
    601 #             use different classes for their homset, depending on some
    602 #             specific properties of X or Y which do not fit in the category
    603 #             hierarchy. For example, if X is a quotient field, morphisms
    604 #             can be defined by the image of the generators, even if Y
    605 #             itself is not a quotient field.
    606 
    607 #             Design question: should this really concern the parent for the
    608 #             homset, or just the possible classes for the elements?
    609 #             """
    610 #             category = self.base_category
    611 #             assert(X in category and Y in category)
    612 #             # return self.hom_category()(X, Y)?
    613 #             #print self.hom_category(), X, Y, self.hom_category().parent_class, self.hom_category().parent_class.mro()
    614 #             return self.ParentMethods
    615 
    616         class ParentMethods:
    617             # Design issue: when X is a quotient field, we can build
    618             # morphisms from X to Y by specifying the images of the
    619             # generators. This is not something about the category,
    620             # because Y need not be a quotient field.
    621 
    622             # Currently, and to minimize the changes, this is done by
    623             # delegating the job to RingHomset. This is not very robust:
    624             # for example, only one category can do this hack.
    625 
    626             # This should be cleaned up upon the next homset overhaul
    627 
    628             def __new__(cls, X, Y, category):
    629                 """
    630                     sage: Hom(QQ, QQ, category = Rings()).__class__                  # indirect doctest
    631                     <class 'sage.rings.homset.RingHomset_generic_with_category'>
    632 
    633                     sage: Hom(CyclotomicField(3), QQ, category = Rings()).__class__  # indirect doctest
    634                     <class 'sage.rings.number_field.morphism.CyclotomicFieldHomset_with_category'>
    635                 """
    636                 from sage.rings.homset import RingHomset
    637                 return RingHomset(X, Y, category = category)
    638 
    639             def __getnewargs__(self):
    640                 """
    641                 Note: without this method, :meth:`.__new__` gets called with no
    642                 argument upon unpickling. Maybe it would be preferable to
    643                 have :meth:`.__new__` accept to be called without arguments.
    644 
    645                 TESTS::
    646 
    647                     sage: Hom(QQ, QQ, category = Rings()).__getnewargs__()
    648                     (Rational Field, Rational Field, Category of hom sets in Category of rings)
    649                     sage: TestSuite(Hom(QQ, QQ, category = Rings())).run() # indirect doctest
    650                 """
    651                 return (self.domain(), self.codomain(), self.category())
     623        pass
  • sage/categories/schemes.py

    diff --git a/sage/categories/schemes.py b/sage/categories/schemes.py
    a b class Schemes_abstract(Category): 
    148148            """
    149149            return []
    150150
    151         class ParentMethods:
    152 
    153             def __new__(cls, R, S, category):
    154                 """
    155                 TESTS::
    156 
    157                     sage: E = EllipticCurve('37a1')
    158                     sage: Hom(E, E).__class__
    159                     <class 'sage.schemes.generic.homset.SchemeHomset_generic_with_category'>
    160 
    161                 If both schemes R and S are actually specs, we want
    162                 the parent for Hom(R, S) to be in a different class::
    163 
    164                     sage: Hom(Spec(ZZ), Spec(ZZ)).__class__
    165                     <class 'sage.schemes.affine.affine_homset.SchemeHomset_points_spec_with_category'>
    166 
    167                 Currently, and to minimize the changes, this is done
    168                 by delegating the job to SchemeHomset. This is not
    169                 very robust: for example, only one category can do
    170                 this hack.
    171 
    172                 FIXME: this might be better handled by an extra Spec category
    173                 """
    174                 from sage.schemes.generic.homset import SchemeHomset
    175                 return SchemeHomset(R, S, category=category)
    176151
    177152
    178153#############################################################
  • sage/modules/vector_space_homspace.py

    diff --git a/sage/modules/vector_space_homspace.py b/sage/modules/vector_space_homspace.py
    a b class VectorSpaceHomspace(sage.modules.f 
    328328            [1 0 2]
    329329
    330330        Coercing a vector space morphism into the parent of a second vector
    331         space morphism will unify their parents. ::
     331        space morphism will unify their parents::
    332332
    333             sage: U = QQ^3
    334             sage: V = QQ^4
    335             sage: W = FreeModule(QQ,3,sparse=True)
    336             sage: X = QQ^4
     333            sage: U = FreeModule(QQ,3, sparse=True ); V = QQ^4
     334            sage: W = FreeModule(QQ,3, sparse=False); X = QQ^4
    337335            sage: H = Hom(U, V)
    338336            sage: K = Hom(W, X)
    339             sage: H is K
    340             False
     337            sage: H is K, H == K
     338            (False, True)
    341339
    342340            sage: A = matrix(QQ, 3, 4, [0]*12)
    343341            sage: f = H(A)
  • sage/rings/polynomial/infinite_polynomial_ring.py

    diff --git a/sage/rings/polynomial/infinite_polynomial_ring.py b/sage/rings/polynomial/infinite_polynomial_ring.py
    a b class InfinitePolynomialRing_sparse(Comm 
    658658            alpha_1*beta_2
    659659
    660660        """
    661         from weakref import WeakKeyDictionary
    662         self._coerce_cache = WeakKeyDictionary()
    663661        if not names:
    664662            names = ['x']
    665663        for n in names:
    class InfinitePolynomialRing_sparse(Comm 
    706704            VarList = [X+'_0' for X in names]
    707705        VarList.sort(cmp=self.varname_cmp, reverse=True)
    708706        self._minP = PolynomialRing(R, len(VarList), VarList)
    709         self._coerce_cache[self] = True
    710707        self._populate_coercion_lists_()
    711708
    712709    def __repr__(self):
    class InfinitePolynomialRing_sparse(Comm 
    816813        """
    817814        return [InfinitePolynomialFunctor(self._names, self._order, 'sparse'), self._base]
    818815
    819     #@cached_method  -- better no "strong" caching!
    820     # Otherwise, 1000s of polynomial rings will be
    821     # strongly referenced. We do weak value caching
    822     # internally
    823816    def _coerce_map_from_(self, S):
    824817        """
    825818        Coerce things into ``self``.
    class InfinitePolynomialRing_sparse(Comm 
    850843            a_4^2*x_1 + a_2*x_3
    851844
    852845        """
    853         try:
    854             v = self._coerce_cache.get(S)
    855         except TypeError:
    856             v = None
    857         if v is not None:
    858             return v
    859846        # Use Construction Functors!
    860847        from sage.categories.pushout import pushout, construction_tower
    861848        try:
    class InfinitePolynomialRing_sparse(Comm 
    865852            P = pushout(self,S)
    866853            # We don't care about the orders. But base ring and generators
    867854            # of the pushout should remain the same as in self.
    868             v = (P._names == self._names and P._base == self._base)
     855            return (P._names == self._names and P._base == self._base)
    869856        except StandardError:
    870             v = False
    871         try:
    872             self._coerce_cache[S] = v
    873         except StandardError:
    874             pass
    875         return v
     857            return False
    876858
    877859    def _element_constructor_(self, x):
    878860        """
  • sage/schemes/elliptic_curves/ell_curve_isogeny.py

    diff --git a/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/sage/schemes/elliptic_curves/ell_curve_isogeny.py
    a b class EllipticCurveIsogeny(Morphism): 
    33413341
    33423342            sage: E = EllipticCurve(j=GF(7)(0))
    33433343            sage: phi = EllipticCurveIsogeny(E, [E(0), E((0,1)), E((0,-1))])
     3344            sage: phi._composition_(phi, phi.parent())
     3345            Traceback (most recent call last):
     3346            ...
     3347            NotImplementedError
     3348
     3349        The following should test that :meth:`_composition_` is called
     3350        upon a product. However phi is currently improperly
     3351        constructed (see :trac:`12880`), which triggers an assertion
     3352        failure before the actual call ::
     3353
    33443354            sage: phi*phi
    33453355            Traceback (most recent call last):
    33463356            ...
    3347             NotImplementedError                     
    3348             sage: phi._composition_(phi, phi.parent())
     3357            TypeError: Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7 is not in Category of hom sets in Category of Schemes
     3358
     3359        Here would be the desired output::
     3360
     3361            sage: phi*phi            # not tested
    33493362            Traceback (most recent call last):
    33503363            ...
    3351             NotImplementedError                     
    3352 
     3364            NotImplementedError
    33533365        """
    33543366        raise NotImplementedError
    33553367
  • sage/schemes/generic/morphism.py

    diff --git a/sage/schemes/generic/morphism.py b/sage/schemes/generic/morphism.py
    a b class SchemeMorphism(Element): 
    127127
    128128    EXAMPLES::
    129129
    130         sage: from sage.schemes.generic.scheme import Scheme
    131         sage: X = Scheme(ZZ)
     130        sage: X = Spec(ZZ)
    132131        sage: Hom = X.Hom(X)
    133132        sage: from sage.schemes.generic.morphism import SchemeMorphism
    134133        sage: f = SchemeMorphism(Hom)
    class SchemeMorphism(Element): 
    141140       
    142141        EXAMPLES::
    143142       
    144             sage: from sage.schemes.generic.scheme import Scheme
    145             sage: X = Scheme(ZZ)
     143            sage: X = Spec(ZZ)
    146144            sage: Hom = X.Hom(X)
    147145            sage: from sage.schemes.generic.morphism import SchemeMorphism
    148146            sage: f = SchemeMorphism(Hom)
    class SchemeMorphism(Element): 
    165163
    166164        EXAMPLES::
    167165
    168             sage: from sage.schemes.generic.scheme import Scheme
    169             sage: X = Scheme(ZZ)
     166            sage: X = Spec(ZZ)
    170167            sage: Hom = X.Hom(X)
    171168            sage: from sage.schemes.generic.morphism import SchemeMorphism
    172169            sage: f = SchemeMorphism(Hom)
    class SchemeMorphism(Element): 
    207204
    208205        EXAMPLES::
    209206
    210             sage: from sage.schemes.generic.scheme import Scheme
    211             sage: X = Scheme(ZZ)
     207            sage: X = Spec(ZZ)
    212208            sage: Hom = X.Hom(X)
    213209            sage: from sage.schemes.generic.morphism import SchemeMorphism
    214210            sage: f = SchemeMorphism(Hom)
  • sage/schemes/generic/scheme.py

    diff --git a/sage/schemes/generic/scheme.py b/sage/schemes/generic/scheme.py
    a b class Scheme(Parent): 
    658658            sage: S = Spec(ZZ)
    659659            sage: S._Hom_(P)
    660660            Set of rational points of Projective Space of dimension 3 over Integer Ring
     661
     662        TESTS::
     663
     664            sage: S._Hom_(P).__class__
     665            <class 'sage.schemes.projective.projective_homset.SchemeHomset_points_projective_ring_with_category'>
     666
     667            sage: E = EllipticCurve('37a1')
     668            sage: Hom(E, E).__class__
     669            <class 'sage.schemes.generic.homset.SchemeHomset_generic_with_category'>
     670
     671            sage: Hom(Spec(ZZ), Spec(ZZ)).__class__
     672            <class 'sage.schemes.affine.affine_homset.SchemeHomset_points_spec_with_category'>
    661673        """
    662674        from sage.schemes.generic.homset import SchemeHomset
    663675        return SchemeHomset(self, Y, category=category, check=check)
  • sage/structure/parent.pyx

    diff --git a/sage/structure/parent.pyx b/sage/structure/parent.pyx
    a b cdef class Parent(category_object.Catego 
    13911391       raise NotImplementedError("Verification of correctness of homomorphisms from %s not yet implemented."%self)
    13921392
    13931393    def Hom(self, codomain, category=None):
    1394         r"""       
    1395         Return the homspace ``Hom(self, codomain, cat)`` of all
    1396         homomorphisms from self to codomain in the category cat.  The
    1397         default category is :meth:`category`.
     1394        r"""
     1395        Return the homspace ``Hom(self, codomain, category)``.
     1396
     1397        INPUT:
     1398
     1399        - ``codomain`` -- a parent
     1400        - ``category`` -- a category or ``None`` (default: ``None``)
     1401          If ``None``, the meet of the category of ``self`` and
     1402          ``codomain`` is used.
     1403
     1404        OUTPUT:
     1405
     1406        The homspace of all homomorphisms from ``self`` to
     1407        ``codomain`` in the category ``category``.
     1408
     1409        .. SEEALSO:: :func:`~sage.categories.homset.Hom`
    13981410
    13991411        EXAMPLES::
    14001412       
    cdef class Parent(category_object.Catego 
    14151427            Set of Morphisms from Rational Field to Integer Ring in Category of sets
    14161428
    14171429        A parent may specify how to construct certain homsets by
    1418         implementing a method ``_Hom_(codomain, category)``. This
    1419         method should either construct the requested homset or raise a
    1420         ``TypeError``.
     1430        implementing a method :meth:`_Hom_`(codomain, category).
     1431        See :func:`~sage.categories.homset.Hom` for details.
    14211432        """
    1422         try:
    1423             return self._Hom_(codomain, category)
    1424         except (AttributeError, TypeError):
    1425             pass
    14261433        from sage.categories.homset import Hom
    14271434        return Hom(self, codomain, category)
    14281435
    cdef class Set_PythonType_class(Set_gene 
    28682875            # probably
    28692876            import sage.rings.infinity
    28702877            return sage.rings.infinity.infinity
    2871            
    2872 #     def _Hom_disabled(self, domain, cat=None):
    2873 #         """
    2874 #         By default, create a homset in the category of sets.
    2875        
    2876 #         EXAMPLES:
    2877 #             sage: R = sage.structure.parent.Set_PythonType(int)
    2878 #             sage: S = sage.structure.parent.Set_PythonType(float)
    2879 #             sage: R._Hom_(S)
    2880 #             Set of Morphisms from Set of Python objects of type 'int' to Set of Python objects of type 'float' in Category of sets
    2881 #         """
    2882 #         from sage.categories.sets_cat import Sets
    2883 #         from sage.categories.homset import Homset
    2884 #         if cat is None:
    2885 #             cat = Sets()
    2886 #         return Homset(self, domain, cat)
    28872878
    28882879# These functions are to guarantee that user defined _lmul_, _rmul_,
    28892880# _act_on_, _acted_upon_ do not in turn call __mul__ on their
  • sage/structure/parent_base.pyx

    diff --git a/sage/structure/parent_base.pyx b/sage/structure/parent_base.pyx
    a b cdef class ParentWithBase(parent_old.Par 
    9090        check_old_coerce(self)
    9191        raise CoercionException, "BUG: the base_extend method must be defined for '%s' (class '%s')"%(
    9292            self, type(self))
    93 
    94     ############################################################################
    95     # Homomorphism --
    96     ############################################################################
    97     def Hom(self, codomain, category = None):
    98         r"""
    99         self.Hom(codomain, category = None):
    100        
    101         Returns the homspace \code{Hom(self, codomain, category)} of all
    102         homomorphisms from self to codomain in the category cat.  The
    103         default category is \code{self.category()}.
    104 
    105         EXAMPLES:
    106             sage: R.<x,y> = PolynomialRing(QQ, 2)
    107             sage: R.Hom(QQ)
    108             Set of Homomorphisms from Multivariate Polynomial Ring in x, y over Rational Field to Rational Field
    109 
    110         Homspaces are defined for very general \sage objects, even elements of familiar rings.
    111             sage: n = 5; Hom(n,7)
    112             Set of Morphisms from 5 to 7 in Category of elements of Integer Ring
    113             sage: z=(2/3); Hom(z,8/1)
    114             Set of Morphisms from 2/3 to 8 in Category of elements of Rational Field
    115 
    116         This example illustrates the optional third argument:
    117             sage: QQ.Hom(ZZ, Sets())
    118             Set of Morphisms from Rational Field to Integer Ring in Category of sets
    119         """
    120         # NT 01-2009: what's the difference with parent.Parent.Hom???
    121         if self._element_constructor is None:
    122             return parent.Parent.Hom(self, codomain, category)
    123         try:
    124             return self._Hom_(codomain, category)
    125         except (AttributeError, TypeError):
    126             pass
    127         from sage.categories.all import Hom
    128         return Hom(self, codomain, category)
    129