Beispiel #1
0
    def create_object(self, version, key, **extra_args):
        r"""
        Create the valuation specified by ``key``.

        EXAMPLES::

            sage: K.<x> = FunctionField(QQ)
            sage: R.<x> = QQ[]
            sage: w = valuations.GaussValuation(R, QQ.valuation(2))
            sage: v = K.valuation(w); v # indirect doctest
            2-adic valuation

        """
        domain, valuation = key
        from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace
        parent = DiscretePseudoValuationSpace(domain)

        if isinstance(valuation, tuple) and len(valuation) == 3:
            valuation, to_valuation_domain, from_valuation_domain = valuation
            if domain is domain.base() and valuation.domain() is valuation.domain().base() and to_valuation_domain == domain.hom([~valuation.domain().gen()]) and from_valuation_domain == valuation.domain().hom([~domain.gen()]):
                # valuation on the rational function field after x |--> 1/x
                if valuation == valuation.domain().valuation(valuation.domain().gen()):
                    # the classical valuation at the place 1/x
                    return parent.__make_element_class__(InfiniteRationalFunctionFieldValuation)(parent)

                from sage.structure.dynamic_class import dynamic_class
                clazz = RationalFunctionFieldMappedValuation
                if valuation.is_discrete_valuation():
                    clazz = dynamic_class("RationalFunctionFieldMappedValuation_discrete", (clazz, DiscreteValuation))
                else:
                    clazz = dynamic_class("RationalFunctionFieldMappedValuation_infinite", (clazz, InfiniteDiscretePseudoValuation))
                return parent.__make_element_class__(clazz)(parent, valuation, to_valuation_domain, from_valuation_domain)
            return parent.__make_element_class__(FunctionFieldExtensionMappedValuation)(parent, valuation, to_valuation_domain, from_valuation_domain)

        if domain is valuation.domain():
            # we can not just return valuation in this case
            # as this would break uniqueness and pickling
            raise ValueError("valuation must not be a valuation on domain yet but %r is a valuation on %r"%(valuation, domain))

        if domain.base_field() is domain:
            # valuation is a base valuation on K[x] that induces a valuation on K(x)
            if valuation.restriction(domain.constant_base_field()).is_trivial() and valuation.is_discrete_valuation():
                # valuation corresponds to a finite place
                return parent.__make_element_class__(FiniteRationalFunctionFieldValuation)(parent, valuation)
            else:
                from sage.structure.dynamic_class import dynamic_class
                clazz = NonClassicalRationalFunctionFieldValuation
                if valuation.is_discrete_valuation():
                    clazz = dynamic_class("NonClassicalRationalFunctionFieldValuation_discrete", (clazz, DiscreteFunctionFieldValuation_base))
                else:
                    clazz = dynamic_class("NonClassicalRationalFunctionFieldValuation_negative_infinite", (clazz, NegativeInfiniteDiscretePseudoValuation))
                return parent.__make_element_class__(clazz)(parent, valuation)
        else:
            # valuation is a limit valuation that singles out an extension
            return parent.__make_element_class__(FunctionFieldFromLimitValuation)(parent, valuation, domain.polynomial(), extra_args['approximants'])

        raise NotImplementedError("valuation on %r from %r on %r"%(domain, valuation, valuation.domain()))
Beispiel #2
0
def _dynamic_MCF_class(base):
    r"""
    Return the MCF algorithm associated to some Cython base class including all
    the Python methods.

    This is where the fusion of cython methods with python methods is done with
    the use of ``dynamic_class``.

    INPUT:

    - ``base`` -- MCF algorithm base class

    OUTPUT:

        class

    EXAMPLES::

        sage: from slabbe.mult_cont_frac_pyx import Brun
        sage: from slabbe.mult_cont_frac import _dynamic_MCF_class
        sage: cls = _dynamic_MCF_class(Brun)
        sage: algo = cls()
        sage: algo
        Brun 3-dimensional continued fraction algorithm
        sage: algo.matrix_cocycle()
        Cocycle with 6 gens over Regular language over [123, 132, 213, 231, 312, 321]
        defined by: Automaton with 6 states
    """
    class_name = base().class_name()
    return dynamic_class(class_name, (base, _MCFAlgorithm_methods))
Beispiel #3
0
def _algo_with(base):
    r"""
    Return the MCF algorithm associated to some Cython base class including all
    the Python methods.

    This is where the fusion of cython methods with python methods is done with
    the use of ``dynamic_class``.

    INPUT:

    - ``base`` -- MCF algorithm base class

    EXAMPLES::

        sage: from slabbe.mult_cont_frac_pyx import Brun
        sage: from slabbe.mult_cont_frac import _algo_with
        sage: algo = _algo_with(Brun)
        sage: algo
        Brun 3-dimensional continued fraction algorithm
        sage: algo.matrix_cocycle()
        Cocycle with 6 gens over Regular language over [123, 132, 213, 231, 312, 321]
        defined by: Automaton with 6 states
    """
    class_name = base().class_name()
    cls = dynamic_class(class_name, (base, _MCFAlgorithm_methods))
    return cls()
Beispiel #4
0
def _late_import():
    """
    Used to reset the class of PARI finite field elements in their initialization.

    EXAMPLES::

        sage: from sage.rings.finite_rings.element_ext_pari import FiniteField_ext_pariElement
        sage: k.<a> = GF(3^17, impl='pari_mod')
        sage: a.__class__ is FiniteField_ext_pariElement # indirect doctest
        False
    """
    global dynamic_FiniteField_ext_pariElement
    dynamic_FiniteField_ext_pariElement = dynamic_class("%s_with_category"%FiniteField_ext_pariElement.__name__, (FiniteField_ext_pariElement, FiniteFields().element_class), doccls=FiniteField_ext_pariElement)
Beispiel #5
0
def _late_import():
    """
    Used to reset the class of PARI finite field elements in their initialization.

    EXAMPLES::

        sage: from sage.rings.finite_rings.element_ext_pari import FiniteField_ext_pariElement
        sage: k.<a> = GF(3^17, impl='pari_mod')
        sage: a.__class__ is FiniteField_ext_pariElement # indirect doctest
        False
    """
    global dynamic_FiniteField_ext_pariElement
    dynamic_FiniteField_ext_pariElement = dynamic_class("%s_with_category"%FiniteField_ext_pariElement.__name__, (FiniteField_ext_pariElement, FiniteFields().element_class), doccls=FiniteField_ext_pariElement)
Beispiel #6
0
    def _abstract_element_class(self):
        """
        An abstract class for the elements of this homset.

        This class is built from the element class of the homset
        category and the morphism class of the category.  This makes
        it possible for a category to provide code for its morphisms
        and for morphisms of all its subcategories, full or not.

        .. NOTE::

            The element class of ``C.Homsets()`` will be inherited by
            morphisms in *full* subcategories of ``C``, while the morphism
            class of ``C`` will be inherited by *all* subcategories of
            ``C``. Hence, if some feature of a morphism depends on the
            algebraic properties of the homsets, it should be implemented by
            ``C.Homsets.ElementMethods``, but if it depends only on the
            algebraic properties of domain and codomain, it should be
            implemented in ``C.MorphismMethods``.

            At this point, the homset element classes takes precedence over
            the morphism classes. But this may be subject to change.


        .. TODO::

            - Make sure this class is shared whenever possible.
            - Flatten join category classes

        .. SEEALSO::

            - :meth:`Parent._abstract_element_class`

        EXAMPLES:

        Let's take a homset of finite commutative groups as example; at
        this point this is the simplest one to create (gosh)::

            sage: cat = Groups().Finite().Commutative()
            sage: C3 = PermutationGroup([(1,2,3)])
            sage: C3._refine_category_(cat)
            sage: C2 = PermutationGroup([(1,2)])
            sage: C2._refine_category_(cat)
            sage: H = Hom(C3, C2, cat)
            sage: H.homset_category()
            Category of finite commutative groups
            sage: H.category()
            Category of homsets of unital magmas
            sage: cls = H._abstract_element_class; cls
            <class 'sage.categories.homsets.Homset_with_category._abstract_element_class'>
            sage: cls.__bases__ == (H.category().element_class, H.homset_category().morphism_class)
            True

        A morphism of finite commutative semigroups is also a morphism
        of semigroups, of magmas, ...; it thus inherits code from all
        those categories::

            sage: issubclass(cls, Semigroups().Finite().morphism_class)
            True
            sage: issubclass(cls, Semigroups().morphism_class)
            True
            sage: issubclass(cls, Magmas().Commutative().morphism_class)
            True
            sage: issubclass(cls, Magmas().morphism_class)
            True
            sage: issubclass(cls, Sets().morphism_class)
            True

        Recall that FiniteMonoids() is a full subcategory of
        ``Monoids()``, but not of ``FiniteSemigroups()``. Thus::

            sage: issubclass(cls, Monoids().Finite().Homsets().element_class)
            True
            sage: issubclass(cls, Semigroups().Finite().Homsets().element_class)
            False
        """
        class_name = "%s._abstract_element_class" % self.__class__.__name__
        return dynamic_class(class_name, (self.category().element_class, self.homset_category().morphism_class))
Beispiel #7
0
def Hom(X, Y, category=None, check=True):
    """
    Create the space of homomorphisms from X to Y in the category ``category``.

    INPUT:


    - ``X`` -- an object of a category

    - ``Y`` -- an object of a category

    - ``category`` -- a category in which the morphisms must be.
      (default: the meet of the categories of ``X`` and ``Y``)
      Both ``X`` and ``Y`` must belong to that category.

    - ``check`` -- a boolean (default: ``True``): whether to check the
      input, and in particular that ``X`` and ``Y`` belong to
      ``category``.

    OUTPUT: a homset in category

    EXAMPLES::

        sage: V = VectorSpace(QQ,3)
        sage: Hom(V, V)
        Set of Morphisms (Linear Transformations) from
        Vector space of dimension 3 over Rational Field to
        Vector space of dimension 3 over Rational Field
        sage: G = AlternatingGroup(3)
        sage: Hom(G, G)
        Set of Morphisms from Alternating group of order 3!/2 as a permutation group to Alternating group of order 3!/2 as a permutation group in Category of finite permutation groups
        sage: Hom(ZZ, QQ, Sets())
        Set of Morphisms from Integer Ring to Rational Field in Category of sets

        sage: Hom(FreeModule(ZZ,1), FreeModule(QQ,1))
        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
        sage: Hom(FreeModule(QQ,1), FreeModule(ZZ,1))
        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

    Here, we test against a memory leak that has been fixed at :trac:`11521` by
    using a weak cache::

        sage: for p in prime_range(10^3):
        ...    K = GF(p)
        ...    a = K(0)
        sage: import gc
        sage: gc.collect()       # random
        624
        sage: from sage.rings.finite_rings.finite_field_prime_modn import FiniteField_prime_modn as FF
        sage: L = [x for x in gc.get_objects() if isinstance(x, FF)]
        sage: len(L), L[0]
        (1, Finite Field of size 997)

    To illustrate the choice of the category, we consider the
    following parents as running examples::

        sage: X = ZZ; X
        Integer Ring
        sage: Y = SymmetricGroup(3); Y
        Symmetric group of order 3! as a permutation group

    By default, the smallest category containing both ``X`` and ``Y``,
    is used::

        sage: Hom(X, Y)
        Set of Morphisms from Integer Ring
         to Symmetric group of order 3! as a permutation group
         in Join of Category of monoids and Category of enumerated sets

    Otherwise, if ``category`` is specified, then ``category`` is used,
    after checking that ``X`` and ``Y`` are indeed in ``category``::

        sage: Hom(X, Y, Magmas())
        Set of Morphisms from Integer Ring to Symmetric group of order 3! as a permutation group in Category of magmas

        sage: Hom(X, Y, Groups())
        Traceback (most recent call last):
        ...
        ValueError: Integer Ring is not in Category of groups

    A parent (or a parent class of a category) may specify how to
    construct certain homsets by implementing a method ``_Hom_(self,
    codomain, category)``. This method should either construct the
    requested homset or raise a ``TypeError``. This hook is currently
    mostly used to create homsets in some specific subclass of
    :class:`Homset` (e.g. :class:`sage.rings.homset.RingHomset`)::

        sage: Hom(QQ,QQ).__class__
        <class 'sage.rings.homset.RingHomset_generic_with_category'>

    Do not call this hook directly to create homsets, as it does not
    handle unique representation::

        sage: Hom(QQ,QQ) == QQ._Hom_(QQ, category=QQ.category())
        True
        sage: Hom(QQ,QQ) is QQ._Hom_(QQ, category=QQ.category())
        False

    TESTS:

    Homset are unique parents::

        sage: k = GF(5)
        sage: H1 = Hom(k,k)
        sage: H2 = Hom(k,k)
        sage: H1 is H2
        True

    Moreover, if no category is provided, then the result is identical
    with the result for the meet of the categories of the domain and
    the codomain::

        sage: Hom(QQ, ZZ) is Hom(QQ,ZZ, Category.meet([QQ.category(), ZZ.category()]))
        True

    Some doc tests in :mod:`sage.rings` (need to) break the unique
    parent assumption. But if domain or codomain are not unique
    parents, then the homset will not fit. That is to say, the hom set
    found in the cache will have a (co)domain that is equal to, but
    not identical with, the given (co)domain.

    By :trac:`9138`, we abandon the uniqueness of homsets, if the
    domain or codomain break uniqueness::

        sage: from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing_polydict_domain
        sage: P.<x,y,z>=MPolynomialRing_polydict_domain(QQ, 3, order='degrevlex')
        sage: Q.<x,y,z>=MPolynomialRing_polydict_domain(QQ, 3, order='degrevlex')
        sage: P == Q
        True
        sage: P is Q
        False

    Hence, ``P`` and ``Q`` are not unique parents. By consequence, the
    following homsets aren't either::

        sage: H1 = Hom(QQ,P)
        sage: H2 = Hom(QQ,Q)
        sage: H1 == H2
        True
        sage: H1 is H2
        False

    It is always the most recently constructed homset that remains in
    the cache::

        sage: H2 is Hom(QQ,Q)
        True

    Variation on the theme::

        sage: U1 = FreeModule(ZZ,2)
        sage: U2 = FreeModule(ZZ,2,inner_product_matrix=matrix([[1,0],[0,-1]]))
        sage: U1 == U2, U1 is U2
        (True, False)
        sage: V = ZZ^3
        sage: H1 = Hom(U1, V); H2 = Hom(U2, V)
        sage: H1 == H2, H1 is H2
        (True, False)
        sage: H1 = Hom(V, U1); H2 = Hom(V, U2)
        sage: H1 == H2, H1 is H2
        (True, False)

    Since :trac:`11900`, the meet of the categories of the given arguments is
    used to determine the default category of the homset. This can also be a
    join category, as in the following example::

        sage: PA = Parent(category=Algebras(QQ))
        sage: PJ = Parent(category=Rings() & Modules(QQ))
        sage: Hom(PA,PJ)
        Set of Homomorphisms from <type 'sage.structure.parent.Parent'> to <type 'sage.structure.parent.Parent'>
        sage: Hom(PA,PJ).category()
        Category of homsets of unital magmas and right modules over Rational Field and left modules over Rational Field
        sage: Hom(PA,PJ, Rngs())
        Set of Morphisms from <type 'sage.structure.parent.Parent'> to <type 'sage.structure.parent.Parent'> in Category of rngs

    .. TODO::

        - Design decision: how much of the homset comes from the
          category of ``X`` and ``Y``, and how much from the specific
          ``X`` and ``Y``.  In particular, do we need several parent
          classes depending on ``X`` and ``Y``, or does the difference
          only lie in the elements (i.e.  the morphism), and of course
          how the parent calls their constructors.
        - Specify the protocol for the ``_Hom_`` hook in case of ambiguity
          (e.g. if both a parent and some category thereof provide one).

    TESTS:

    Facade parents over plain Python types are supported::

        sage: R = sage.structure.parent.Set_PythonType(int)
        sage: S = sage.structure.parent.Set_PythonType(float)
        sage: Hom(R, S)
        Set of Morphisms from Set of Python objects of type 'int' to Set of Python objects of type 'float' in Category of sets

    Checks that the domain and codomain are in the specified
    category. Case of a non parent::

        sage: S = SimplicialComplex([[1,2], [1,4]]); S.rename("S")
        sage: Hom(S, S, SimplicialComplexes())
        Set of Morphisms from S to S in Category of finite simplicial complexes

        sage: Hom(Set(), S, Sets())
        Set of Morphisms from {} to S in Category of sets

        sage: Hom(S, Set(), Sets())
        Set of Morphisms from S to {} in Category of sets

        sage: H = Hom(S, S, ChainComplexes(QQ))
        Traceback (most recent call last):
        ...
        ValueError: S is not in Category of chain complexes over Rational Field

    Those checks are done with the natural idiom ``X in category``,
    and not ``X.category().is_subcategory(category)`` as it used to be
    before :trac:`16275` (see :trac:`15801` for a real use case)::

        sage: class PermissiveCategory(Category):
        ....:     def super_categories(self): return [Objects()]
        ....:     def __contains__(self, X): return True
        sage: C = PermissiveCategory(); C.rename("Permissive category")
        sage: S.category().is_subcategory(C)
        False
        sage: S in C
        True
        sage: Hom(S, S, C)
        Set of Morphisms from S to S in Permissive category

    With ``check=False``, unitialized parents, as can appear upon
    unpickling, are supported. Case of a parent::

        sage: cls = type(Set())
        sage: S = unpickle_newobj(cls, ())  # A non parent
        sage: H = Hom(S, S, SimplicialComplexes(), check=False);
        sage: H = Hom(S, S, Sets(),                check=False)
        sage: H = Hom(S, S, ChainComplexes(QQ),    check=False)

    Case of a non parent::

        sage: cls = type(SimplicialComplex([[1,2], [1,4]]))
        sage: S = unpickle_newobj(cls, ())
        sage: H = Hom(S, S, Sets(),                check=False)
        sage: H = Hom(S, S, Groups(),              check=False)
        sage: H = Hom(S, S, SimplicialComplexes(), check=False)

    Typical example where unpickling involves calling Hom on an
    unitialized parent::

        sage: P.<x,y> = QQ['x,y']
        sage: Q = P.quotient([x^2-1,y^2-1])
        sage: q = Q.an_element()
        sage: explain_pickle(dumps(Q))
        pg_...
        ... = pg_dynamic_class('QuotientRing_generic_with_category', (pg_QuotientRing_generic, pg_getattr(..., 'parent_class')), None, None, pg_QuotientRing_generic)
        si... = unpickle_newobj(..., ())
        ...
        si... = pg_unpickle_MPolynomialRing_libsingular(..., ('x', 'y'), ...)
        si... = ... pg_Hom(si..., si..., ...) ...
        sage: Q == loads(dumps(Q))
        True
    """
    # This should use cache_function instead
    # However some special handling is currently needed for
    # domains/docomains that break the unique parent condition. Also,
    # at some point, it somehow broke the coercion (see e.g. sage -t
    # sage.rings.real_mpfr). To be investigated.
    global _cache
    key = (X, Y, category)
    try:
        H = _cache[key]
    except KeyError:
        H = None
    if H is not None:
        # Return H unless the domain or codomain breaks the unique parent condition
        if H.domain() is X and H.codomain() is Y:
            return H

    # Determines the category
    if category is None:
        category = X.category()._meet_(Y.category())
        # Recurse to make sure that Hom(X, Y) and Hom(X, Y, category) are identical
        # No need to check the input again
        H = Hom(X, Y, category, check=False)
    else:
        if check:
            if not isinstance(category, Category):
                raise TypeError("Argument category (= {}) must be a category.".format(category))
            for O in [X, Y]:
                try:
                    category_mismatch = O not in category
                except Exception:
                    # An error should not happen, this here is just to be on
                    # the safe side.
                    category_mismatch = True
                # A category mismatch does not necessarily mean that an error
                # should be raised. Instead, it could be the case that we are
                # unpickling an old pickle (that doesn't set the "check"
                # argument to False). In this case, it could be that the
                # (co)domain is not properly initialised, which we are
                # checking now. See trac #16275 and #14793.
                if category_mismatch and O._is_category_initialized():
                    # At this point, we can be rather sure that O is properly
                    # initialised, and thus its string representation is
                    # available for the following error message. It simply
                    # belongs to the wrong category.
                    raise ValueError("{} is not in {}".format(O, category))

        # Construct H
        try:  # _Hom_ hook from the parent
            H = X._Hom_(Y, category)
        except (AttributeError, TypeError):
            try:
                # Workaround in case the above fails, but the category
                # also provides a _Hom_ hook.
                # FIXME:
                # - If X._Hom_ actually comes from category and fails, it
                #   will be called twice.
                # - This is bound to fail if X is an extension type and
                #   does not actually inherit from category.parent_class
                H = category.parent_class._Hom_(X, Y, category=category)
            except (AttributeError, TypeError):
                # By default, construct a plain homset.
                H = Homset(X, Y, category=category, check=check)
    _cache[key] = H
    if isinstance(X, UniqueRepresentation) and isinstance(Y, UniqueRepresentation):
        if not isinstance(H, WithEqualityById):
            try:
                H.__class__ = dynamic_class(
                    H.__class__.__name__ + "_with_equality_by_id", (WithEqualityById, H.__class__), doccls=H.__class__
                )
            except Exception:
                pass
    return H
Beispiel #8
0
    def _abstract_element_class(self):
        """
        An abstract class for the elements of this homset.

        This class is built from the element class of the homset
        category and the morphism class of the category.  This makes
        it possible for a category to provide code for its morphisms
        and for morphisms of all its subcategories, full or not.

        .. NOTE::

            The element class of ``C.Homsets()`` will be inherited by
            morphisms in *full* subcategories of ``C``, while the morphism
            class of ``C`` will be inherited by *all* subcategories of
            ``C``. Hence, if some feature of a morphism depends on the
            algebraic properties of the homsets, it should be implemented by
            ``C.Homsets.ElementMethods``, but if it depends only on the
            algebraic properties of domain and codomain, it should be
            implemented in ``C.MorphismMethods``.

            At this point, the homset element classes take precedence over the
            morphism classes. But this may be subject to change.


        .. TODO::

            - Make sure this class is shared whenever possible.
            - Flatten join category classes

        .. SEEALSO::

            - :meth:`Parent._abstract_element_class`

        EXAMPLES:

        Let's take a homset of finite commutative groups as example; at
        this point this is the simplest one to create (gosh)::

            sage: cat = Groups().Finite().Commutative()
            sage: C3 = PermutationGroup([(1,2,3)])
            sage: C3._refine_category_(cat)
            sage: C2 = PermutationGroup([(1,2)])
            sage: C2._refine_category_(cat)
            sage: H = Hom(C3, C2, cat)
            sage: H.homset_category()
            Category of finite commutative groups
            sage: H.category()
            Category of homsets of unital magmas
            sage: cls = H._abstract_element_class; cls
            <class 'sage.categories.homsets.GroupHomset_libgap_with_category._abstract_element_class'>
            sage: cls.__bases__ == (H.category().element_class, H.homset_category().morphism_class)
            True

        A morphism of finite commutative semigroups is also a morphism
        of semigroups, of magmas, ...; it thus inherits code from all
        those categories::

            sage: issubclass(cls, Semigroups().Finite().morphism_class)
            True
            sage: issubclass(cls, Semigroups().morphism_class)
            True
            sage: issubclass(cls, Magmas().Commutative().morphism_class)
            True
            sage: issubclass(cls, Magmas().morphism_class)
            True
            sage: issubclass(cls, Sets().morphism_class)
            True

        Recall that FiniteMonoids() is a full subcategory of
        ``Monoids()``, but not of ``FiniteSemigroups()``. Thus::

            sage: issubclass(cls, Monoids().Finite().Homsets().element_class)
            True
            sage: issubclass(cls, Semigroups().Finite().Homsets().element_class)
            False
        """
        class_name = "%s._abstract_element_class" % self.__class__.__name__
        return dynamic_class(class_name,
                             (self.category().element_class,
                              self.homset_category().morphism_class))
Beispiel #9
0
def Hom(X, Y, category=None, check=True):
    """
    Create the space of homomorphisms from X to Y in the category ``category``.

    INPUT:


    - ``X`` -- an object of a category

    - ``Y`` -- an object of a category

    - ``category`` -- a category in which the morphisms must be.
      (default: the meet of the categories of ``X`` and ``Y``)
      Both ``X`` and ``Y`` must belong to that category.

    - ``check`` -- a boolean (default: ``True``): whether to check the
      input, and in particular that ``X`` and ``Y`` belong to
      ``category``.

    OUTPUT: a homset in category

    EXAMPLES::

        sage: V = VectorSpace(QQ,3)
        sage: Hom(V, V)
        Set of Morphisms (Linear Transformations) from
        Vector space of dimension 3 over Rational Field to
        Vector space of dimension 3 over Rational Field
        sage: G = AlternatingGroup(3)
        sage: Hom(G, G)
        Set of Morphisms from Alternating group of order 3!/2 as a permutation group to Alternating group of order 3!/2 as a permutation group in Category of finite enumerated permutation groups
        sage: Hom(ZZ, QQ, Sets())
        Set of Morphisms from Integer Ring to Rational Field in Category of sets

        sage: Hom(FreeModule(ZZ,1), FreeModule(QQ,1))
        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
        sage: Hom(FreeModule(QQ,1), FreeModule(ZZ,1))
        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

    Here, we test against a memory leak that has been fixed at :trac:`11521` by
    using a weak cache::

        sage: for p in prime_range(10^3):
        ....:  K = GF(p)
        ....:  a = K(0)
        sage: import gc
        sage: gc.collect()       # random
        624
        sage: from sage.rings.finite_rings.finite_field_prime_modn import FiniteField_prime_modn as FF
        sage: L = [x for x in gc.get_objects() if isinstance(x, FF)]
        sage: len(L), L[0]
        (1, Finite Field of size 997)

    To illustrate the choice of the category, we consider the
    following parents as running examples::

        sage: X = ZZ; X
        Integer Ring
        sage: Y = SymmetricGroup(3); Y
        Symmetric group of order 3! as a permutation group

    By default, the smallest category containing both ``X`` and ``Y``,
    is used::

        sage: Hom(X, Y)
        Set of Morphisms from Integer Ring
         to Symmetric group of order 3! as a permutation group
         in Category of enumerated monoids

    Otherwise, if ``category`` is specified, then ``category`` is used,
    after checking that ``X`` and ``Y`` are indeed in ``category``::

        sage: Hom(X, Y, Magmas())
        Set of Morphisms from Integer Ring to Symmetric group of order 3! as a permutation group in Category of magmas

        sage: Hom(X, Y, Groups())
        Traceback (most recent call last):
        ...
        ValueError: Integer Ring is not in Category of groups

    A parent (or a parent class of a category) may specify how to
    construct certain homsets by implementing a method ``_Hom_(self,
    codomain, category)``. This method should either construct the
    requested homset or raise a ``TypeError``. This hook is currently
    mostly used to create homsets in some specific subclass of
    :class:`Homset` (e.g. :class:`sage.rings.homset.RingHomset`)::

        sage: Hom(QQ,QQ).__class__
        <class 'sage.rings.homset.RingHomset_generic_with_category'>

    Do not call this hook directly to create homsets, as it does not
    handle unique representation::

        sage: Hom(QQ,QQ) == QQ._Hom_(QQ, category=QQ.category())
        True
        sage: Hom(QQ,QQ) is QQ._Hom_(QQ, category=QQ.category())
        False

    TESTS:

    Homset are unique parents::

        sage: k = GF(5)
        sage: H1 = Hom(k,k)
        sage: H2 = Hom(k,k)
        sage: H1 is H2
        True

    Moreover, if no category is provided, then the result is identical
    with the result for the meet of the categories of the domain and
    the codomain::

        sage: Hom(QQ, ZZ) is Hom(QQ,ZZ, Category.meet([QQ.category(), ZZ.category()]))
        True

    Some doc tests in :mod:`sage.rings` (need to) break the unique
    parent assumption. But if domain or codomain are not unique
    parents, then the homset will not fit. That is to say, the hom set
    found in the cache will have a (co)domain that is equal to, but
    not identical with, the given (co)domain.

    By :trac:`9138`, we abandon the uniqueness of homsets, if the
    domain or codomain break uniqueness::

        sage: from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing_polydict_domain
        sage: P.<x,y,z>=MPolynomialRing_polydict_domain(QQ, 3, order='degrevlex')
        sage: Q.<x,y,z>=MPolynomialRing_polydict_domain(QQ, 3, order='degrevlex')
        sage: P == Q
        True
        sage: P is Q
        False

    Hence, ``P`` and ``Q`` are not unique parents. By consequence, the
    following homsets aren't either::

        sage: H1 = Hom(QQ,P)
        sage: H2 = Hom(QQ,Q)
        sage: H1 == H2
        True
        sage: H1 is H2
        False

    It is always the most recently constructed homset that remains in
    the cache::

        sage: H2 is Hom(QQ,Q)
        True

    Variation on the theme::

        sage: U1 = FreeModule(ZZ,2)
        sage: U2 = FreeModule(ZZ,2,inner_product_matrix=matrix([[1,0],[0,-1]]))
        sage: U1 == U2, U1 is U2
        (False, False)
        sage: V = ZZ^3
        sage: H1 = Hom(U1, V); H2 = Hom(U2, V)
        sage: H1 == H2, H1 is H2
        (False, False)
        sage: H1 = Hom(V, U1); H2 = Hom(V, U2)
        sage: H1 == H2, H1 is H2
        (False, False)

    Since :trac:`11900`, the meet of the categories of the given arguments is
    used to determine the default category of the homset. This can also be a
    join category, as in the following example::

        sage: PA = Parent(category=Algebras(QQ))
        sage: PJ = Parent(category=Rings() & Modules(QQ))
        sage: Hom(PA,PJ)
        Set of Homomorphisms from <sage.structure.parent.Parent object at ...> to <sage.structure.parent.Parent object at ...>
        sage: Hom(PA,PJ).category()
        Category of homsets of unital magmas and right modules over Rational Field and left modules over Rational Field
        sage: Hom(PA,PJ, Rngs())
        Set of Morphisms from <sage.structure.parent.Parent object at ...> to <sage.structure.parent.Parent object at ...> in Category of rngs

    .. TODO::

        - Design decision: how much of the homset comes from the
          category of ``X`` and ``Y``, and how much from the specific
          ``X`` and ``Y``.  In particular, do we need several parent
          classes depending on ``X`` and ``Y``, or does the difference
          only lie in the elements (i.e.  the morphism), and of course
          how the parent calls their constructors.
        - Specify the protocol for the ``_Hom_`` hook in case of ambiguity
          (e.g. if both a parent and some category thereof provide one).

    TESTS:

    Facade parents over plain Python types are supported::

        sage: from sage.sets.pythonclass import Set_PythonType
        sage: R = Set_PythonType(int)
        sage: S = Set_PythonType(float)
        sage: Hom(R, S)
        Set of Morphisms from Set of Python objects of class 'int' to Set of Python objects of class 'float' in Category of sets

    Checks that the domain and codomain are in the specified
    category. Case of a non parent::

        sage: S = SimplicialComplex([[1,2], [1,4]]); S.rename("S")
        sage: Hom(S, S, SimplicialComplexes())
        Set of Morphisms from S to S in Category of finite simplicial complexes

        sage: Hom(Set(), S, Sets())
        Set of Morphisms from {} to S in Category of sets

        sage: Hom(S, Set(), Sets())
        Set of Morphisms from S to {} in Category of sets

        sage: H = Hom(S, S, ChainComplexes(QQ))
        Traceback (most recent call last):
        ...
        ValueError: S is not in Category of chain complexes over Rational Field

    Those checks are done with the natural idiom ``X in category``,
    and not ``X.category().is_subcategory(category)`` as it used to be
    before :trac:`16275` (see :trac:`15801` for a real use case)::

        sage: class PermissiveCategory(Category):
        ....:     def super_categories(self): return [Objects()]
        ....:     def __contains__(self, X): return True
        sage: C = PermissiveCategory(); C.rename("Permissive category")
        sage: S.category().is_subcategory(C)
        False
        sage: S in C
        True
        sage: Hom(S, S, C)
        Set of Morphisms from S to S in Permissive category

    With ``check=False``, unitialized parents, as can appear upon
    unpickling, are supported. Case of a parent::

        sage: cls = type(Set())
        sage: S = unpickle_newobj(cls, ())  # A non parent
        sage: H = Hom(S, S, SimplicialComplexes(), check=False)
        sage: H = Hom(S, S, Sets(),                check=False)
        sage: H = Hom(S, S, ChainComplexes(QQ),    check=False)

    Case of a non parent::

        sage: cls = type(SimplicialComplex([[1,2], [1,4]]))
        sage: S = unpickle_newobj(cls, ())
        sage: H = Hom(S, S, Sets(),                check=False)
        sage: H = Hom(S, S, Groups(),              check=False)
        sage: H = Hom(S, S, SimplicialComplexes(), check=False)

    Typical example where unpickling involves calling Hom on an
    unitialized parent::

        sage: P.<x,y> = QQ['x,y']
        sage: Q = P.quotient([x^2-1,y^2-1])
        sage: q = Q.an_element()
        sage: explain_pickle(dumps(Q))
        pg_...
        ... = pg_dynamic_class('QuotientRing_generic_with_category', (pg_QuotientRing_generic, pg_getattr(..., 'parent_class')), None, None, pg_QuotientRing_generic)
        si... = unpickle_newobj(..., ())
        ...
        si... = pg_unpickle_MPolynomialRing_libsingular(..., ('x', 'y'), ...)
        si... = ... pg_Hom(si..., si..., ...) ...
        sage: Q == loads(dumps(Q))
        True

    Check that the ``_Hom_`` method of the ``category`` input is used::

        sage: from sage.categories.category_types import Category_over_base_ring
        sage: class ModulesWithHom(Category_over_base_ring):
        ....:     def super_categories(self):
        ....:         return [Modules(self.base_ring())]
        ....:     class ParentMethods:
        ....:         def _Hom_(self, Y, category=None):
        ....:             print("Modules")
        ....:             raise TypeError
        sage: class AlgebrasWithHom(Category_over_base_ring):
        ....:     def super_categories(self):
        ....:         return [Algebras(self.base_ring()), ModulesWithHom(self.base_ring())]
        ....:     class ParentMethods:
        ....:         def _Hom_(self, Y, category=None):
        ....:             R = self.base_ring()
        ....:             if category is not None and category.is_subcategory(Algebras(R)):
        ....:                 print("Algebras")
        ....:             raise TypeError
        sage: from sage.structure.element import Element
        sage: class Foo(Parent):
        ....:     _no_generic_basering_coercion = True
        ....:     class Element(Element):
        ....:         pass
        sage: X = Foo(base=QQ, category=AlgebrasWithHom(QQ))
        sage: H = Hom(X, X, ModulesWithHom(QQ))
        Modules
    """
    # This should use cache_function instead
    # However some special handling is currently needed for
    # domains/docomains that break the unique parent condition. Also,
    # at some point, it somehow broke the coercion (see e.g. sage -t
    # sage.rings.real_mpfr). To be investigated.
    global _cache
    key = (X, Y, category)
    try:
        H = _cache[key]
    except KeyError:
        H = None
    if H is not None:
        # Return H unless the domain or codomain breaks the unique parent condition
        if H.domain() is X and H.codomain() is Y:
            return H

    # Determines the category
    if category is None:
        category = X.category()._meet_(Y.category())
        # Recurse to make sure that Hom(X, Y) and Hom(X, Y, category) are identical
        # No need to check the input again
        H = Hom(X, Y, category, check=False)
    else:
        if check:
            if not isinstance(category, Category):
                raise TypeError(
                    "Argument category (= {}) must be a category.".format(
                        category))
            for O in [X, Y]:
                try:
                    category_mismatch = O not in category
                except Exception:
                    # An error should not happen, this here is just to be on
                    # the safe side.
                    category_mismatch = True
                # A category mismatch does not necessarily mean that an error
                # should be raised. Instead, it could be the case that we are
                # unpickling an old pickle (that doesn't set the "check"
                # argument to False). In this case, it could be that the
                # (co)domain is not properly initialised, which we are
                # checking now. See trac #16275 and #14793.
                if category_mismatch and O._is_category_initialized():
                    # At this point, we can be rather sure that O is properly
                    # initialised, and thus its string representation is
                    # available for the following error message. It simply
                    # belongs to the wrong category.
                    raise ValueError("{} is not in {}".format(O, category))

        # Construct H
        try:  # _Hom_ hook from the parent
            H = X._Hom_(Y, category)
        except (AttributeError, TypeError):
            # Workaround in case the above fails, but the category
            # also provides a _Hom_ hook.
            # FIXME:
            # - If X._Hom_ actually comes from category and fails, it
            #   will be called twice.
            # - This is bound to fail if X is an extension type and
            #   does not actually inherit from category.parent_class
            # For join categories, we check all of the direct super
            #   categories as the parent_class of the join category is
            #   not (necessarily) inherited and join categories do not
            #   implement a _Hom_ (see trac #23418).
            if not isinstance(category, JoinCategory):
                cats = [category]
            else:
                cats = category.super_categories()
            H = None
            for C in cats:
                try:
                    H = C.parent_class._Hom_(X, Y, category=category)
                    break
                except (AttributeError, TypeError):
                    pass
            if H is None:
                # By default, construct a plain homset.
                H = Homset(X, Y, category=category, check=check)
    _cache[key] = H
    if isinstance(X, UniqueRepresentation) and isinstance(
            Y, UniqueRepresentation):
        if not isinstance(H, WithEqualityById):
            try:
                H.__class__ = dynamic_class(H.__class__.__name__ +
                                            "_with_equality_by_id",
                                            (WithEqualityById, H.__class__),
                                            doccls=H.__class__)
            except Exception:
                pass
    return H
Beispiel #10
0
def HyperellipticCurve(f, h=0, names=None, PP=None, check_squarefree=True):
    r"""
    Returns the hyperelliptic curve `y^2 + h y = f`, for
    univariate polynomials `h` and `f`. If `h`
    is not given, then it defaults to 0.

    INPUT:

    -  ``f`` - univariate polynomial

    -  ``h`` - optional univariate polynomial

    -  ``names``  (default: ``["x","y"]``) - names for the
       coordinate functions

    -  ``check_squarefree`` (default: ``True``) - test if
       the input defines a hyperelliptic curve when f is
       homogenized to degree `2g+2` and h to degree
       `g+1` for some g.

    .. WARNING::

        When setting ``check_squarefree=False`` or using a base ring that is
        not a field, the output curves are not to be trusted. For example, the
        output of ``is_singular`` is always ``False``, without this being
        properly tested in that case.

    .. NOTE::

        The words "hyperelliptic curve" are normally only used for curves of
        genus at least two, but this class allows more general smooth double
        covers of the projective line (conics and elliptic curves), even though
        the class is not meant for those and some outputs may be incorrect.

    EXAMPLES:

    Basic examples::

        sage: R.<x> = QQ[]
        sage: HyperellipticCurve(x^5 + x + 1)
        Hyperelliptic Curve over Rational Field defined by y^2 = x^5 + x + 1
        sage: HyperellipticCurve(x^19 + x + 1, x-2)
        Hyperelliptic Curve over Rational Field defined by y^2 + (x - 2)*y = x^19 + x + 1

        sage: k.<a> = GF(9); R.<x> = k[]
        sage: HyperellipticCurve(x^3 + x - 1, x+a)
        Hyperelliptic Curve over Finite Field in a of size 3^2 defined by y^2 + (x + a)*y = x^3 + x + 2

    Characteristic two::

        sage: P.<x> = GF(8,'a')[]
        sage: HyperellipticCurve(x^7+1, x)
        Hyperelliptic Curve over Finite Field in a of size 2^3 defined by y^2 + x*y = x^7 + 1
        sage: HyperellipticCurve(x^8+x^7+1, x^4+1)
        Hyperelliptic Curve over Finite Field in a of size 2^3 defined by y^2 + (x^4 + 1)*y = x^8 + x^7 + 1

        sage: HyperellipticCurve(x^8+1, x)
        Traceback (most recent call last):
        ...
        ValueError: Not a hyperelliptic curve: highly singular at infinity.

        sage: HyperellipticCurve(x^8+x^7+1, x^4)
        Traceback (most recent call last):
        ...
        ValueError: Not a hyperelliptic curve: singularity in the provided affine patch.

        sage: F.<t> = PowerSeriesRing(FiniteField(2))
        sage: P.<x> = PolynomialRing(FractionField(F))
        sage: HyperellipticCurve(x^5+t, x)
        Hyperelliptic Curve over Laurent Series Ring in t over Finite Field of size 2 defined by y^2 + x*y = x^5 + t

    We can change the names of the variables in the output::

        sage: k.<a> = GF(9); R.<x> = k[]
        sage: HyperellipticCurve(x^3 + x - 1, x+a, names=['X','Y'])
        Hyperelliptic Curve over Finite Field in a of size 3^2 defined by Y^2 + (X + a)*Y = X^3 + X + 2

    This class also allows curves of genus zero or one, which are strictly
    speaking not hyperelliptic::

        sage: P.<x> = QQ[]
        sage: HyperellipticCurve(x^2+1)
        Hyperelliptic Curve over Rational Field defined by y^2 = x^2 + 1
        sage: HyperellipticCurve(x^4-1)
        Hyperelliptic Curve over Rational Field defined by y^2 = x^4 - 1
        sage: HyperellipticCurve(x^3+2*x+2)
        Hyperelliptic Curve over Rational Field defined by y^2 = x^3 + 2*x + 2

    Double roots::

        sage: P.<x> = GF(7)[]
        sage: HyperellipticCurve((x^3-x+2)^2*(x^6-1))
        Traceback (most recent call last):
        ...
        ValueError: Not a hyperelliptic curve: singularity in the provided affine patch.

        sage: HyperellipticCurve((x^3-x+2)^2*(x^6-1), check_squarefree=False)
        Hyperelliptic Curve over Finite Field of size 7 defined by y^2 = x^12 + 5*x^10 + 4*x^9 + x^8 + 3*x^7 + 3*x^6 + 2*x^4 + 3*x^3 + 6*x^2 + 4*x + 3

    The input for a (smooth) hyperelliptic curve of genus `g` should not
    contain polynomials of degree greater than `2g+2`. In the following
    example, the hyperelliptic curve has genus 2 and there exists a model
    `y^2 = F` of degree 6, so the model `y^2 + yh = f` of degree 200 is not
    allowed.::

        sage: P.<x> = QQ[]
        sage: h = x^100
        sage: F = x^6+1
        sage: f = F-h^2/4
        sage: HyperellipticCurve(f, h)
        Traceback (most recent call last):
        ...
        ValueError: Not a hyperelliptic curve: highly singular at infinity.

        sage: HyperellipticCurve(F)
        Hyperelliptic Curve over Rational Field defined by y^2 = x^6 + 1

    An example with a singularity over an inseparable extension of the
    base field::

        sage: F.<t> = GF(5)[]
        sage: P.<x> = F[]
        sage: HyperellipticCurve(x^5+t)
        Traceback (most recent call last):
        ...
        ValueError: Not a hyperelliptic curve: singularity in the provided affine patch.

    Input with integer coefficients creates objects with the integers
    as base ring, but only checks smoothness over `\QQ`, not over Spec(`\ZZ`).
    In other words, it is checked that the discriminant is non-zero, but it is
    not checked whether the discriminant is a unit in `\ZZ^*`.::

        sage: P.<x> = ZZ[]
        sage: HyperellipticCurve(3*x^7+6*x+6)
        Hyperelliptic Curve over Integer Ring defined by y^2 = 3*x^7 + 6*x + 6

    TESTS:

    Check that `f` can be a constant (see :trac:`15516`)::

        sage: R.<u> = PolynomialRing(Rationals())
        sage: HyperellipticCurve(-12, u^4 + 7)
        Hyperelliptic Curve over Rational Field defined by y^2 + (x^4 + 7)*y = -12

    Check that two curves with the same class name have the same class type::

        sage: R.<t> = PolynomialRing(GF(next_prime(10^9)))
        sage: C = HyperellipticCurve(t^5 + t + 1)
        sage: C2 = HyperellipticCurve(t^5 + 3*t + 1)
        sage: type(C2) == type(C)
        True

    Check that the inheritance is correct::

        sage: R.<t> = PolynomialRing(GF(next_prime(10^9)))
        sage: C = HyperellipticCurve(t^5 + t + 1)
        sage: type(C).mro()
        [<class 'sage.schemes.hyperelliptic_curves.constructor.HyperellipticCurve_g2_FiniteField_with_category'>,
         <class 'sage.schemes.hyperelliptic_curves.constructor.HyperellipticCurve_g2_FiniteField'>,
         <class 'sage.schemes.hyperelliptic_curves.hyperelliptic_g2.HyperellipticCurve_g2'>,
         <class 'sage.schemes.hyperelliptic_curves.hyperelliptic_finite_field.HyperellipticCurve_finite_field'>,
         <class 'sage.schemes.hyperelliptic_curves.hyperelliptic_generic.HyperellipticCurve_generic'>,
        ...]
    """
    # F is the discriminant; use this for the type check
    # rather than f and h, one of which might be constant.
    F = h**2 + 4 * f
    if not is_Polynomial(F):
        raise TypeError("Arguments f (= %s) and h (= %s) must be polynomials" %
                        (f, h))
    P = F.parent()
    f = P(f)
    h = P(h)
    df = f.degree()
    dh_2 = 2 * h.degree()
    if dh_2 < df:
        g = (df - 1) // 2
    else:
        g = (dh_2 - 1) // 2
    if check_squarefree:
        # Assuming we are working over a field, this checks that after
        # resolving the singularity at infinity, we get a smooth double cover
        # of P^1.
        if P(2) == 0:
            # characteristic 2
            if h == 0:
                raise ValueError(
                    "In characteristic 2, argument h (= %s) must be non-zero."
                    % h)
            if h[g + 1] == 0 and f[2 * g + 1]**2 == f[2 * g + 2] * h[g]**2:
                raise ValueError("Not a hyperelliptic curve: " \
                                  "highly singular at infinity.")
            should_be_coprime = [h, f * h.derivative()**2 + f.derivative()**2]
        else:
            # characteristic not 2
            if not F.degree() in [2 * g + 1, 2 * g + 2]:
                raise ValueError("Not a hyperelliptic curve: " \
                                  "highly singular at infinity.")
            should_be_coprime = [F, F.derivative()]
        try:
            smooth = should_be_coprime[0].gcd(
                should_be_coprime[1]).degree() == 0
        except (AttributeError, NotImplementedError, TypeError):
            try:
                smooth = should_be_coprime[0].resultant(
                    should_be_coprime[1]) != 0
            except (AttributeError, NotImplementedError, TypeError):
                raise NotImplementedError("Cannot determine whether " \
                      "polynomials %s have a common root. Use " \
                      "check_squarefree=False to skip this check." % \
                      should_be_coprime)
        if not smooth:
            raise ValueError("Not a hyperelliptic curve: " \
                              "singularity in the provided affine patch.")
    R = P.base_ring()
    PP = ProjectiveSpace(2, R)
    if names is None:
        names = ["x", "y"]

    superclass = []
    cls_name = ["HyperellipticCurve"]

    genus_classes = {2: HyperellipticCurve_g2}

    fields = [("FiniteField", is_FiniteField, HyperellipticCurve_finite_field),
              ("RationalField", is_RationalField,
               HyperellipticCurve_rational_field),
              ("pAdicField", is_pAdicField, HyperellipticCurve_padic_field)]

    if g in genus_classes:
        superclass.append(genus_classes[g])
        cls_name.append("g%s" % g)

    for name, test, cls in fields:
        if test(R):
            superclass.append(cls)
            cls_name.append(name)
            break

    class_name = "_".join(cls_name)
    cls = dynamic_class(class_name,
                        tuple(superclass),
                        HyperellipticCurve_generic,
                        doccls=HyperellipticCurve)
    return cls(PP, f, h, names=names, genus=g)
Beispiel #11
0
def Hom(X, Y, category=None):
    """
    Create the space of homomorphisms from X to Y in the category ``category``.

    INPUT:


    - ``X`` -- an object of a category

    - ``Y`` -- an object of a category

    - ``category`` -- a category in which the morphisms must be.
      (default: the meet of the categories of ``X`` and ``Y``)
      Both ``X`` and ``Y`` must belong to that category.

    OUTPUT: a homset in category

    EXAMPLES::

        sage: V = VectorSpace(QQ,3)
        sage: Hom(V, V)
        Set of Morphisms (Linear Transformations) from
        Vector space of dimension 3 over Rational Field to
        Vector space of dimension 3 over Rational Field
        sage: G = AlternatingGroup(3)
        sage: Hom(G, G)
        Set of Morphisms from Alternating group of order 3!/2 as a permutation group to Alternating group of order 3!/2 as a permutation group in Category of finite permutation groups
        sage: Hom(ZZ, QQ, Sets())
        Set of Morphisms from Integer Ring to Rational Field in Category of sets

        sage: Hom(FreeModule(ZZ,1), FreeModule(QQ,1))
        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
        sage: Hom(FreeModule(QQ,1), FreeModule(ZZ,1))
        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

    Here, we test against a memory leak that has been fixed at :trac:`11521` by
    using a weak cache::

        sage: for p in prime_range(10^3):
        ...    K = GF(p)
        ...    a = K(0)
        sage: import gc
        sage: gc.collect()       # random
        624
        sage: from sage.rings.finite_rings.finite_field_prime_modn import FiniteField_prime_modn as FF
        sage: L = [x for x in gc.get_objects() if isinstance(x, FF)]
        sage: len(L), L[0], L[len(L)-1]
        (2, Finite Field of size 2, Finite Field of size 997)

    To illustrate the choice of the category, we consider the
    following parents as running examples::

        sage: X = ZZ; X
        Integer Ring
        sage: Y = SymmetricGroup(3); Y
        Symmetric group of order 3! as a permutation group

    By default, the smallest category containing both ``X`` and ``Y``,
    is used::

        sage: Hom(X, Y)
        Set of Morphisms from Integer Ring to Symmetric group of order 3! as a permutation group in Category of monoids

    Otherwise, if ``category`` is specified, then ``category`` is used,
    after checking that ``X`` and ``Y`` are indeed in ``category``::

        sage: Hom(X, Y, Magmas())
        Set of Morphisms from Integer Ring to Symmetric group of order 3! as a permutation group in Category of magmas

        sage: Hom(X, Y, Groups())
        Traceback (most recent call last):
        ...
        TypeError: Integer Ring is not in Category of groups

    A parent (or a parent class of a category) may specify how to
    construct certain homsets by implementing a method ``_Hom_(self,
    codomain, category)``. This method should either construct the
    requested homset or raise a ``TypeError``. This hook is currently
    mostly used to create homsets in some specific subclass of
    :class:`Homset` (e.g. :class:`sage.rings.homset.RingHomset`)::

        sage: Hom(QQ,QQ).__class__
        <class 'sage.rings.homset.RingHomset_generic_with_category'>

    Do not call this hook directly to create homsets, as it does not
    handle unique representation::

        sage: Hom(QQ,QQ) == QQ._Hom_(QQ, category=QQ.category())
        True
        sage: Hom(QQ,QQ) is QQ._Hom_(QQ, category=QQ.category())
        False

    TESTS:

    Homset are unique parents::

        sage: k = GF(5)
        sage: H1 = Hom(k,k)
        sage: H2 = Hom(k,k)
        sage: H1 is H2
        True

    Moreover, if no category is provided, then the result is identical
    with the result for the meet of the categories of the domain and
    the codomain::

        sage: Hom(QQ, ZZ) is Hom(QQ,ZZ, Category.meet([QQ.category(), ZZ.category()]))
        True

    Some doc tests in :mod:`sage.rings` (need to) break the unique
    parent assumption. But if domain or codomain are not unique
    parents, then the homset will not fit. That is to say, the hom set
    found in the cache will have a (co)domain that is equal to, but
    not identical with, the given (co)domain.

    By :trac:`9138`, we abandon the uniqueness of homsets, if the
    domain or codomain break uniqueness::

        sage: from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing_polydict_domain
        sage: P.<x,y,z>=MPolynomialRing_polydict_domain(QQ, 3, order='degrevlex')
        sage: Q.<x,y,z>=MPolynomialRing_polydict_domain(QQ, 3, order='degrevlex')
        sage: P == Q
        True
        sage: P is Q
        False

    Hence, ``P`` and ``Q`` are not unique parents. By consequence, the
    following homsets aren't either::

        sage: H1 = Hom(QQ,P)
        sage: H2 = Hom(QQ,Q)
        sage: H1 == H2
        True
        sage: H1 is H2
        False

    It is always the most recently constructed homset that remains in
    the cache::

        sage: H2 is Hom(QQ,Q)
        True

    Variation on the theme::

        sage: U1 = FreeModule(ZZ,2)
        sage: U2 = FreeModule(ZZ,2,inner_product_matrix=matrix([[1,0],[0,-1]]))
        sage: U1 == U2, U1 is U2
        (True, False)
        sage: V = ZZ^3
        sage: H1 = Hom(U1, V); H2 = Hom(U2, V)
        sage: H1 == H2, H1 is H2
        (True, False)
        sage: H1 = Hom(V, U1); H2 = Hom(V, U2)
        sage: H1 == H2, H1 is H2
        (True, False)

    Since :trac:`11900`, the meet of the categories of the given arguments is
    used to determine the default category of the homset. This can also be a
    join category, as in the following example::

        sage: PA = Parent(category=Algebras(QQ))
        sage: PJ = Parent(category=Category.join([Fields(), ModulesWithBasis(QQ)]))
        sage: Hom(PA,PJ)
        Set of Homomorphisms from <type 'sage.structure.parent.Parent'> to <type 'sage.structure.parent.Parent'>
        sage: Hom(PA,PJ).category()
        Join of Category of hom sets in Category of modules over Rational Field and Category of hom sets in Category of rings
        sage: Hom(PA,PJ, Rngs())
        Set of Morphisms from <type 'sage.structure.parent.Parent'> to <type 'sage.structure.parent.Parent'> in Category of rngs

    .. TODO::

        - Design decision: how much of the homset comes from the
          category of ``X`` and ``Y``, and how much from the specific
          ``X`` and ``Y``.  In particular, do we need several parent
          classes depending on ``X`` and ``Y``, or does the difference
          only lie in the elements (i.e.  the morphism), and of course
          how the parent calls their constructors.
        - Specify the protocol for the ``_Hom_`` hook in case of ambiguity
          (e.g. if both a parent and some category thereof provide one).

    TESTS::

        sage: R = sage.structure.parent.Set_PythonType(int)
        sage: S = sage.structure.parent.Set_PythonType(float)
        sage: Hom(R, S)
        Set of Morphisms from Set of Python objects of type 'int' to Set of Python objects of type 'float' in Category of sets
    """
    # This should use cache_function instead
    # However some special handling is currently needed for
    # domains/docomains that break the unique parent condition. Also,
    # at some point, it somehow broke the coercion (see e.g. sage -t
    # sage.rings.real_mpfr). To be investigated.
    global _cache
    key = (X,Y,category)
    try:
        H = _cache[key]
    except KeyError:
        H = None
    if H is not None:
        # Return H unless the domain or codomain breaks the unique parent condition
        if H.domain() is X and H.codomain() is Y:
            return H

    # Determines the category
    if category is None:
        category = X.category()._meet_(Y.category())
        # Recurse to make sure that Hom(X, Y) and Hom(X, Y, category) are identical
        H = Hom(X, Y, category)
    else:
        if not isinstance(category, Category):
            raise TypeError("Argument category (= {}) must be a category.".format(category))
        # See trac #14793: It can happen, that Hom(X,X) is called during
        # unpickling of an instance X of a Python class at a time when
        # X.__dict__ is empty.  In some of these cases, X.category() would
        # raise a error or would return a too large category (Sets(), for
        # example) and (worse!) would assign this larger category to the
        # X._category cdef attribute, so that it would subsequently seem that
        # X's category was properly initialised.

        # However, if the above scenario happens, then *before* calling
        # X.category(), X._is_category_initialised() will correctly say that
        # it is not initialised. Moreover, since X.__class__ is a Python
        # class, we will find that `isinstance(X, category.parent_class)`. If
        # this is the case, then we trust that we indeed are in the process of
        # unpickling X.  Hence, we will trust that `category` has the correct
        # value, and we will thus skip the test whether `X in category`.
        try:
            unpickle_X = (not X._is_category_initialized()) and isinstance(X,category.parent_class)
        except AttributeError: # this happens for simplicial complexes
            unpickle_X = False
        try:
            unpickle_Y = (not Y._is_category_initialized()) and isinstance(Y,category.parent_class)
        except AttributeError:
            unpickle_Y = False
        if unpickle_X:
            cat_X = category
        else:
            try:
                cat_X = X.category()
            except BaseException:
                raise TypeError("%s is not in %s"%(X, category))
        if unpickle_Y:
            cat_Y = category
        else:
            try:
                cat_Y = Y.category()
            except BaseException:
                raise TypeError("%s is not in %s"%(Y, category))

        if not cat_X.is_subcategory(category):
            raise TypeError("%s is not in %s"%(X, category))
        if not cat_Y.is_subcategory(category):
            raise TypeError("%s is not in %s"%(Y, category))

        # Construct H
        try: # _Hom_ hook from the parent
            H = X._Hom_(Y, category)
        except (AttributeError, TypeError):
            try:
                # Workaround in case the above fails, but the category
                # also provides a _Hom_ hook.
                # FIXME:
                # - If X._Hom_ actually comes from category and fails, it
                #   will be called twice.
                # - This is bound to fail if X is an extension type and
                #   does not actually inherit from category.parent_class
                H = category.parent_class._Hom_(X, Y, category = category)
            except (AttributeError, TypeError):
                # By default, construct a plain homset.
                H = Homset(X, Y, category = category)
    _cache[key] = H
    if isinstance(X, UniqueRepresentation) and isinstance(Y, UniqueRepresentation):
        if not isinstance(H, WithEqualityById):
            try:
                H.__class__ = dynamic_class(H.__class__.__name__+"_with_equality_by_id", (WithEqualityById, H.__class__), doccls=H.__class__)
            except BaseException:
                pass
    return H