示例#1
0
    def _create_(k, R):
        """
        Initialization.  We have to implement this as a static method
        in order to call ``__make_element_class__``.

        INPUT:

        - ``k`` -- the residue field of ``R``, or a residue ring of ``R``.
        - ``R`` -- a `p`-adic ring or field.

        EXAMPLES::

            sage: f = Zp(3).convert_map_from(Zmod(81))
            sage: TestSuite(f).run()
        """
        from sage.categories.sets_cat import Sets
        from sage.categories.homset import Hom
        kfield = R.residue_field()
        N = k.cardinality()
        q = kfield.cardinality()
        n = N.exact_log(q)
        if N != q**n:
            raise RuntimeError("N must be a power of q")
        H = Hom(k, R, Sets())
        f = H.__make_element_class__(ResidueLiftingMap)(H)
        f._n = n
        return f
    def _convert_map_from_(self, R):
        """
        Finds conversion maps from R to this ring.

        Currently, a conversion exists if the defining polynomial is the same.

        EXAMPLES::

            sage: R.<a> = Zq(125)
            sage: S = R.change(type='capped-abs', prec=40, print_mode='terse', print_pos=False)
            sage: S(a - 15)
            -15 + a + O(5^20)

        We get conversions from the exact field::

            sage: K = R.exact_field(); K
            Number Field in a with defining polynomial x^3 + 3*x + 3
            sage: R(K.gen())
            a + O(5^20)

        and its maximal order::

            sage: OK = K.maximal_order()
            sage: R(OK.gen(1))
            a + O(5^20)
        """
        cat = None
        if self._implementation == 'NTL' and R == QQ:
            # Want to use DefaultConvertMap
            return None
        if isinstance(R, pAdicExtensionGeneric) and R.defining_polynomial(exact=True) == self.defining_polynomial(exact=True):
            if R.is_field() and not self.is_field():
                cat = SetsWithPartialMaps()
            else:
                cat = R.category()
        elif isinstance(R, Order) and R.number_field().defining_polynomial() == self.defining_polynomial():
            cat = IntegralDomains()
        elif isinstance(R, NumberField) and R.defining_polynomial() == self.defining_polynomial():
            if self.is_field():
                cat = Fields()
            else:
                cat = SetsWithPartialMaps()
        else:
            k = self.residue_field()
            if R is k:
                return ResidueLiftingMap._create_(R, self)
        if cat is not None:
            H = Hom(R, self, cat)
            return H.__make_element_class__(DefPolyConversion)(H)
示例#3
0
    def _create_(R, k):
        """
        Initialization.  We have to implement this as a static method
        in order to call ``__make_element_class__``.

        INPUT:

        - ``R`` -- a `p`-adic ring or field.
        - ``k`` -- the residue field of ``R``, or a residue ring of ``R``.

        EXAMPLES::

            sage: f = Zmod(49).convert_map_from(Zp(7))
            sage: TestSuite(f).run()
            sage: K.<a> = Qq(125); k = K.residue_field(); f = k.convert_map_from(K)
            sage: TestSuite(f).run()
        """
        if R.is_field():
            from sage.categories.sets_with_partial_maps import SetsWithPartialMaps
            cat = SetsWithPartialMaps()
        else:
            from sage.categories.rings import Rings
            cat = Rings()
        from sage.categories.homset import Hom
        kfield = R.residue_field()
        N = k.cardinality()
        q = kfield.cardinality()
        n = N.exact_log(q)
        if N != q**n:
            raise RuntimeError("N must be a power of q")
        H = Hom(R, k, cat)
        f = H.__make_element_class__(ResidueReductionMap)(H)
        f._n = n
        if kfield is k:
            f._field = True
        else:
            f._field = False
        return f
示例#4
0
    def __init__(self, domain, codomain):
        """
        Initialize ``self``.

        EXAMPLES::

            sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example()
            sage: f = L.lift

        We skip the category test since this is currently not an element of
        a homspace::

            sage: TestSuite(f).run(skip="_test_category")
        """
        Morphism.__init__(self, Hom(domain, codomain))
示例#5
0
    def __invert__(self):
        r"""
        Return the inverse of ``self``.

        EXAMPLES::

            sage: RC = crystals.infinity.RiggedConfigurations(['A',3])
            sage: T = crystals.infinity.Tableaux(['A',3])
            sage: phi = RC.coerce_map_from(T)
            sage: ~phi
            Crystal Isomorphism morphism:
              From: The infinity crystal of rigged configurations of type ['A', 3]
              To:   The infinity crystal of tableaux of type ['A', 3]
        """
        return FromRCIsomorphism(Hom(self.codomain(), self.domain()))
示例#6
0
    def __init__(self, K, V):
        """
        EXAMPLES::

            sage: K.<x> = FunctionField(QQ); R.<y> = K[]
            sage: L.<y> = K.extension(y^2 - x*y + 4*x^3)
            sage: V, f, t = L.vector_space(); type(t)
            <class 'sage.rings.function_field.maps.MapFunctionFieldToVectorSpace'>
        """
        self._V = V
        self._K = K
        self._zero = K.base_ring()(0)
        self._n = K.degree()
        from sage.categories.homset import Hom
        FunctionFieldIsomorphism.__init__(self, Hom(K, V))
示例#7
0
    def __init__(self, K, L):
        r"""
        EXAMPLE::

            sage: K.<a, b> = NumberField([x^2 - 3, x^2 + 7])
            sage: L.<c, d> = K.change_names()
            sage: L.structure()
            (Isomorphism given by variable name change map:
              From: Number Field in c with defining polynomial x^2 - 3 over its base field
              To:   Number Field in a with defining polynomial x^2 - 3 over its base field, Isomorphism given by variable name change map:
              From: Number Field in a with defining polynomial x^2 - 3 over its base field
              To:   Number Field in c with defining polynomial x^2 - 3 over its base field)
        """
        self.__K = K
        self.__L = L
        NumberFieldIsomorphism.__init__(self, Hom(K, L))
示例#8
0
    def __init__(self, L, V, to_K, to_V):
        r"""
        EXAMPLES::

            sage: L.<a, b> = NumberField([x^2 + 3, x^2 + 5])
            sage: V, fr, to = L.absolute_vector_space() # indirect doctest
            sage: to
            Isomorphism map:
              From: Number Field in a with defining polynomial x^2 + 3 over its base field
              To:   Vector space of dimension 4 over Rational Field
        """
        self.__L = L
        self.__V = V
        self.__to_K = to_K
        self.__to_V = to_V
        NumberFieldIsomorphism.__init__(self, Hom(L, V))
示例#9
0
    def __init__(self, V, K):
        r"""
        EXAMPLES::

            sage: K.<c> = NumberField(x^9 + 3)
            sage: V, fr, to = K.vector_space(); fr # indirect doctest
            Isomorphism map:
              From: Vector space of dimension 9 over Rational Field
              To:   Number Field in c with defining polynomial x^9 + 3
            sage: type(fr)
            <class 'sage.rings.number_field.maps.MapVectorSpaceToNumberField'>
        """
        self.__V = V
        self.__K = K
        self.__R = K.polynomial_ring()
        NumberFieldIsomorphism.__init__(self, Hom(V, K))
示例#10
0
    def __init__(self, A, R):
        r"""
        EXAMPLE::

            sage: L.<a, b> = NumberField([x^2 + 3, x^2 + 5])
            sage: K.<c> = L.absolute_field()
            sage: f = K.structure()[0] # indirect doctest
            sage: type(f)
            <class 'sage.rings.number_field.maps.MapAbsoluteToRelativeNumberField'>
        """
        self.__A = A  # absolute field
        self.__R = R  # relative field
        self.__poly_ring = self.__A.polynomial_ring()
        self.__zero = QQ(0)
        self.__n = A.degree()
        NumberFieldIsomorphism.__init__(self, Hom(A, R))
示例#11
0
    def __init__(self, domain, codomain):
        """
        Construct the morphism

        TESTS::

            sage: Z4 = AbelianGroupWithValues([I], [4])
            sage: from sage.groups.abelian_gps.values import AbelianGroupWithValuesEmbedding
            sage: AbelianGroupWithValuesEmbedding(Z4, Z4.values_group())
            Generic morphism:
              From: Multiplicative Abelian group isomorphic to C4
              To:   Symbolic Ring
        """
        assert domain.values_group() is codomain
        from sage.categories.homset import Hom
        Morphism.__init__(self, Hom(domain, codomain))
示例#12
0
    def __init__(self, original_code, subfield, embedding=None):
        r"""
        TESTS:

        ``subfield`` has to be a finite field, otherwise an error is raised::

            sage: C = codes.random_linear_code(GF(16, 'aa'), 7, 3)
            sage: Cs = codes.SubfieldSubcode(C, RR)
            Traceback (most recent call last):
            ...
            ValueError: subfield has to be a finite field

        ``subfield`` has to be a subfield of ``original_code``'s base field,
        otherwise an error is raised::

            sage: C = codes.random_linear_code(GF(16, 'aa'), 7, 3)
            sage: Cs = codes.SubfieldSubcode(C, GF(8, 'a'))
            Traceback (most recent call last):
            ...
            ValueError: subfield has to be a subfield of the base field of the original code

        """
        if not isinstance(original_code, AbstractLinearCode):
            raise ValueError("original_code must be a linear code")
        if not subfield.is_finite():
            raise ValueError("subfield has to be a finite field")
        p = subfield.characteristic()
        F = original_code.base_field()
        s = subfield.degree()
        sm = F.degree()
        if not s.divides(sm):
            raise ValueError(
                "subfield has to be a subfield of the base field of the original code"
            )
        self._original_code = original_code
        H = Hom(subfield, F)
        if embedding is not None and not embedding in H:
            raise ValueError(
                "embedding has to be an embedding from subfield to original_code's base field"
            )
        elif embedding is not None:
            self._embedding = RelativeFiniteFieldExtension(
                F, subfield, embedding)
        else:
            self._embedding = RelativeFiniteFieldExtension(F, subfield, H[0])
        super(SubfieldSubcode, self).__init__(subfield, original_code.length(),
                                              "Systematic", "Syndrome")
示例#13
0
    def __init__(self, V, K):
        r"""

        EXAMPLE::

            sage: K.<a, b> = NumberField([x^2 + 1, x^2 - 2])
            sage: V, _, to = K.relative_vector_space(); to # indirect doctest
            Isomorphism map:
              From: Number Field in a with defining polynomial x^2 + 1 over its base field
              To:   Vector space of dimension 2 over Number Field in b with defining polynomial x^2 - 2
        """
        self.__V = V
        self.__K = K
        self.__R = K.polynomial_ring()
        self.__rnf = K.pari_rnf()
        self.__B = K.base_field().absolute_field('a')
        NumberFieldIsomorphism.__init__(self, Hom(V, K))
示例#14
0
    def __init__(self, K, V):
        r"""
        Standard initialisation function.

        EXAMPLE::

            sage: L.<a> = NumberField(x^3 - x + 1)
            sage: L.vector_space()[2] # indirect doctest
            Isomorphism map:
              From: Number Field in a with defining polynomial x^3 - x + 1
              To:   Vector space of dimension 3 over Rational Field
        """
        self.__V = V
        self.__K = K
        self.__zero = QQ(0)
        self.__n = K.degree()
        NumberFieldIsomorphism.__init__(self, Hom(K, V))
示例#15
0
    def reversed(self):
        """
        Return the corresponding homset, but with the domain and codomain
        reversed.

        EXAMPLES::
        
            sage: H = Hom(ZZ^2, ZZ^3); H
            Set of Morphisms from Ambient free module of rank 2 over the principal ideal domain Integer Ring to Ambient free module of rank 3 over the principal ideal domain Integer Ring in Category of modules with basis over Integer Ring
            sage: type(H)
            <class 'sage.modules.free_module_homspace.FreeModuleHomspace_with_category'>
            sage: H.reversed()
            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
            sage: type(H.reversed())
            <class 'sage.modules.free_module_homspace.FreeModuleHomspace_with_category'>
        """
        return Hom(self.codomain(), self.domain(), category=self.category())
示例#16
0
    def __init__(self, domain, codomain, ringembed):
        r"""
        Initialize this morphism.

        TESTS::

            sage: k.<a> = GF(5^3)
            sage: S.<x> = SkewPolynomialRing(k, k.frobenius_endomorphism())
            sage: K = S.fraction_field()
            sage: Z = K.center()
            sage: iota = K.coerce_map_from(Z)
            sage: TestSuite(iota).run(skip=['_test_category'])
        """
        RingHomomorphism.__init__(self, Hom(domain, codomain))
        self._codomain = codomain
        self._ringembed = ringembed
        self._section = SectionOreFunctionCenterInjection(self)
示例#17
0
    def __init__(self, K):
        r"""
        Initialize a derivation from ``K`` to ``K``.

        EXAMPLES::

            sage: K.<x> = FunctionField(QQ)
            sage: d = K.derivation() # indirect doctest

        """
        from .function_field import is_FunctionField
        if not is_FunctionField(K):
            raise ValueError("K must be a function field")
        self.__field = K
        from sage.categories.homset import Hom
        from sage.categories.sets_cat import Sets
        Map.__init__(self, Hom(K, K, Sets()))
示例#18
0
    def __init__(self, domain, codomain, embed, order):
        r"""
        Initialize this morphism.

        EXAMPLES::

            sage: k.<a> = GF(5^3)
            sage: S.<x> = SkewPolynomialRing(k, k.frobenius_endomorphism())
            sage: Z = S.center()
            sage: S.convert_map_from(Z)   # indirect doctest
            Embedding of the center of Ore Polynomial Ring in x over Finite Field in a of size 5^3 twisted by a |--> a^5 into this ring
        """
        RingHomomorphism.__init__(self, Hom(domain, codomain))
        self._embed = embed
        self._order = order
        self._codomain = codomain
        self._section = SectionSkewPolynomialCenterInjection(self)
示例#19
0
def _dualizing_sheaf(poset, degree, base_ring, rank):
    p = degree * -1
    chains = sorted(filter(lambda chain: len(chain) == p + 1, poset.chains()))
    singleton = Poset({0: []}, {})
    sheaf = LocallyFreeSheafFinitePoset({0: rank}, {},
                                        base_ring=base_ring,
                                        domain_poset=singleton)
    result = None
    hom = Hom(singleton, poset)
    for c in chains:
        inclusion = hom(lambda point: {0: c[-1]}[point])
        to_add = sheaf.pushforward(inclusion)
        if result is None:
            result = to_add
        else:
            result = result + to_add
    return result
示例#20
0
    def _generic_convert_map(self, S):
        """
        Return a generic map from a given homset to ``self``.

        INPUT:

        - ``S`` -- a homset

        OUTPUT:

        A map (by default: a Call morphism) from ``S`` to ``self``.
        
        EXAMPLES::

            sage: H = Hom(ZZ,QQ['t'], CommutativeAdditiveGroups())
            sage: P.<t> = ZZ[]
            sage: f = P.hom([2*t])
            sage: H._generic_convert_map(f.parent())
            Call morphism:
              From: Set of Homomorphisms from Univariate Polynomial Ring in t over Integer Ring to Univariate Polynomial Ring in t over Integer Ring
              To:   Set of Morphisms from Integer Ring to Univariate Polynomial Ring in t over Rational Field in Category of commutative additive groups
            sage: H._generic_convert_map(f.parent())(f)
            Composite map:
              From: Integer Ring
              To:   Univariate Polynomial Ring in t over Rational Field
              Defn:   Composite map:
                      From: Integer Ring
                      To:   Univariate Polynomial Ring in t over Integer Ring
                      Defn:   Polynomial base injection morphism:
                              From: Integer Ring
                              To:   Univariate Polynomial Ring in t over Integer Ring
                            then
                              Ring endomorphism of Univariate Polynomial Ring in t over Integer Ring
                              Defn: t |--> 2*t
                    then
                      Conversion map:
                      From: Univariate Polynomial Ring in t over Integer Ring
                      To:   Univariate Polynomial Ring in t over Rational Field
        
        """
        if self._element_constructor is None:
            from sage.categories.morphism import CallMorphism
            from sage.categories.homset import Hom
            return CallMorphism(Hom(S, self))
        else:
            return Parent._generic_convert_map(self, S)
    def __init__(self, f, X, Y):
        """
        Input is a dictionary ``f``, the domain ``X``, and the codomain ``Y``.

        One can define the dictionary on the vertices of `X`.

        EXAMPLES::

            sage: S = SimplicialComplex([[0,1],[2],[3,4],[5]], is_mutable=False)
            sage: H = Hom(S,S)
            sage: f = {0:0,1:1,2:2,3:3,4:4,5:5}
            sage: g = {0:0,1:1,2:0,3:3,4:4,5:0}
            sage: x = H(f)
            sage: y = H(g)
            sage: x == y
            False
            sage: x.image()
            Simplicial complex with vertex set (0, 1, 2, 3, 4, 5) and facets {(2,), (5,), (0, 1), (3, 4)}
            sage: y.image()
            Simplicial complex with vertex set (0, 1, 3, 4) and facets {(0, 1), (3, 4)}
            sage: x.image() == y.image()
            False
        """
        if not isinstance(X, SimplicialComplex) or not isinstance(
                Y, SimplicialComplex):
            raise ValueError("X and Y must be SimplicialComplexes")
        if not set(f.keys()) == set(X.vertices()):
            raise ValueError(
                "f must be a dictionary from the vertex set of X to single values in the vertex set of Y"
            )
        dim = X.dimension()
        Y_faces = Y.faces()
        for k in range(dim + 1):
            for i in X.faces()[k]:
                tup = i.tuple()
                fi = []
                for j in tup:
                    fi.append(f[j])
                v = Simplex(set(fi))
            if v not in Y_faces[v.dimension()]:
                raise ValueError(
                    "f must be a dictionary from the vertices of X to the vertices of Y"
                )
        self._vertex_dictionary = f
        Morphism.__init__(self, Hom(X, Y, SimplicialComplexes()))
示例#22
0
    def _coerce_map_from_(self, P):
        """
        Return ``True`` or the coerce map from ``P`` if a map exists.

        EXAMPLES::

            sage: T = crystals.infinity.Tableaux(['A',3])
            sage: RC = crystals.infinity.RiggedConfigurations(['A',3])
            sage: T._coerce_map_from_(RC)
            Crystal Isomorphism morphism:
              From: The infinity crystal of rigged configurations of type ['A', 3]
              To:   The infinity crystal of tableaux of type ['A', 3]
        """
        from sage.combinat.rigged_configurations.rc_infinity import InfinityCrystalOfRiggedConfigurations
        if isinstance(P, InfinityCrystalOfRiggedConfigurations):
            from sage.combinat.rigged_configurations.bij_infinity import FromRCIsomorphism
            return FromRCIsomorphism(Hom(P, self))
        return super(InfinityCrystalOfTableaux, self)._coerce_map_from_(P)
示例#23
0
def End(X, category=None):
    r"""
    Create the set of endomorphisms of ``X`` in the category category.

    INPUT:

    -  ``X`` -- anything

    -  ``category`` -- (optional) category in which to coerce ``X``

    OUTPUT:

    A set of endomorphisms in category

    EXAMPLES::

        sage: V = VectorSpace(QQ, 3)
        sage: End(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: S = End(G); S
        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: from sage.categories.homset import is_Endset
        sage: is_Endset(S)
        True
        sage: S.domain()
        Alternating group of order 3!/2 as a permutation group

    To avoid creating superfluous categories, homsets are in the
    homset category of the lowest category which currently says
    something specific about its homsets. For example, ``S`` is not
    in the category of hom sets of the category of groups::

        sage: S.category()
        Category of hom sets in Category of sets
        sage: End(QQ).category()
        Category of hom sets in Category of rings
    """
    return Hom(X, X, category)
示例#24
0
文件: quotient.py 项目: yabirgb/sage
    def __init__(self, I, L, names, index_set, category=None):
        r"""
        Initialize ``self``.

        TESTS::

            sage: L.<x,y,z> = LieAlgebra(SR, {('x','y'): {'x':1}})
            sage: K = L.quotient(y)
            sage: K.dimension()
            1
            sage: TestSuite(K).run()
        """
        B = L.basis()
        sm = L.module().submodule_with_basis(
            [I.reduce(B[i]).to_vector() for i in index_set])
        SB = sm.basis()

        # compute and normalize structural coefficients for the quotient
        s_coeff = {}
        for i, ind_i in enumerate(index_set):
            for j in range(i + 1, len(index_set)):
                ind_j = index_set[j]

                brkt = I.reduce(L.bracket(SB[i], SB[j]))
                brktvec = sm.coordinate_vector(brkt.to_vector())
                s_coeff[(ind_i, ind_j)] = dict(zip(index_set, brktvec))
        s_coeff = LieAlgebraWithStructureCoefficients._standardize_s_coeff(
            s_coeff, index_set)

        self._ambient = L
        self._I = I
        self._sm = sm

        LieAlgebraWithStructureCoefficients.__init__(self,
                                                     L.base_ring(),
                                                     s_coeff,
                                                     names,
                                                     index_set,
                                                     category=category)

        # register the quotient morphism as a conversion
        H = Hom(L, self)
        f = SetMorphism(H, self.retract)
        self.register_conversion(f)
示例#25
0
    def __init__(self):
        """
        TESTS::

            sage: P = Sets().example("wrapper")
            sage: P.category()
            Category of sets
            sage: P(13) == 13
            True
            sage: ZZ(P(13)) == 13
            True
            sage: P(13) + 1 == 14
            True
        """
        Parent.__init__(self, category=Sets())
        from sage.rings.integer_ring import IntegerRing
        from sage.categories.homset import Hom
        self.mor = Hom(self, IntegerRing())(lambda z: z.value)
        self._populate_coercion_lists_(embedding=self.mor)
示例#26
0
    def __init__(self, R, repr_var1 = 'x', repr_var2 = 'y', inversed_ring = None):
        self._coeffs_ring = MultivariatePolynomialAlgebra_generic(R, repr_var2, always_show_main_var = True)
        MultivariatePolynomialAlgebra_generic.__init__(
            self,
            self._coeffs_ring,
            repr_var1,
            always_show_main_var = True,
            extra_bases_category = Finite_rank_double_bases()
        )
        self._repr_var1 = repr_var1
        self._repr_var2 = repr_var2
        self._coeffs_base_ring = R
        if(inversed_ring is None):
            self._inversed_ring = DoubleMultivariatePolynomialAlgebra_generic(R, repr_var2, repr_var1, self)
        else:
            self._inversed_ring = inversed_ring

        m = SetMorphism( Hom(self, self.inversed_ring()), lambda x : x.swap_coeffs_elements())
        m.register_as_coercion()
示例#27
0
    def __init__(self, R, A):
        r"""
        EXAMPLE::

            sage: L.<a, b> = NumberField([x^2 + 3, x^2 + 5])
            sage: K.<c> = L.absolute_field()
            sage: f = K.structure()[1]; f
            Isomorphism map:
              From: Number Field in a with defining polynomial x^2 + 3 over its base field
              To:   Number Field in c with defining polynomial x^4 + 16*x^2 + 4
            sage: type(f)
            <class 'sage.rings.number_field.maps.MapRelativeToAbsoluteNumberField'>
        """
        self.__R = R  # relative field
        self.__A = A  # absolute field
        self.__poly_ring = self.__A.polynomial_ring()
        self.__zero = QQ(0)
        self.__n = A.degree()
        NumberFieldIsomorphism.__init__(self, Hom(R, A))
示例#28
0
    def _coerce_map_from_(self, R):
        """
        EXAMPLES::

            sage: L.<x,y> = LaurentPolynomialRing(QQ)
            sage: L.coerce_map_from(QQ)
            Composite map:
              From: Rational Field
              To:   Multivariate Laurent Polynomial Ring in x, y over Rational Field
              Defn:   Polynomial base injection morphism:
                      From: Rational Field
                      To:   Multivariate Polynomial Ring in x, y over Rational Field
                    then
                      Call morphism:
                      From: Multivariate Polynomial Ring in x, y over Rational Field
                      To:   Multivariate Laurent Polynomial Ring in x, y over Rational Field

        Let us check that coercion between Laurent Polynomials over
        different base rings works (:trac:`15345`)::

            sage: R = LaurentPolynomialRing(ZZ, 'x')
            sage: T = LaurentPolynomialRing(QQ, 'x')
            sage: R.gen() + 3*T.gen()
            4*x
        """
        if R is self._R or (isinstance(R, LaurentPolynomialRing_generic)
                            and self._R.has_coerce_map_from(R._R)):
            from sage.structure.coerce_maps import CallableConvertMap
            return CallableConvertMap(R,
                                      self,
                                      self._element_constructor_,
                                      parent_as_first_arg=False)
        elif isinstance(R, LaurentPolynomialRing_generic) and \
             R.variable_names() == self.variable_names() and \
             self.base_ring().has_coerce_map_from(R.base_ring()):
            return True

        f = self._R.coerce_map_from(R)
        if f is not None:
            from sage.categories.homset import Hom
            from sage.categories.morphism import CallMorphism
            return CallMorphism(Hom(self._R, self)) * f
示例#29
0
    def _coerce_map_from_(self, P):
        """
        Return ``True`` or the coerce map from ``P`` if a map exists.

        EXAMPLES::

            sage: T = crystals.infinity.Tableaux(['C',3])
            sage: vct = CartanType(['C',3]).as_folding()
            sage: RC = crystals.infinity.RiggedConfigurations(vct)
            sage: RC._coerce_map_from_(T)
            Crystal Isomorphism morphism:
              From: The infinity crystal of tableaux of type ['C', 3]
              To:   The infinity crystal of rigged configurations of type ['C', 3]
        """
        if self.cartan_type().is_finite():
            from sage.combinat.crystals.infinity_crystals import InfinityCrystalOfTableaux
            if isinstance(P, InfinityCrystalOfTableaux):
                from sage.combinat.rigged_configurations.bij_infinity import FromTableauIsomorphism
                return FromTableauIsomorphism(Hom(P, self))
        return super(InfinityCrystalOfNonSimplyLacedRC, self)._coerce_map_from_(P)
示例#30
0
    def _composition(self, right):
        """
        A helper for multiplying maps by composition.

        .. WARNING::

            Do not override this method! Override :meth:`_composition_`
            instead.

        EXAMPLES::

            sage: X = AffineSpace(QQ,2)
            sage: f = X.structure_morphism()
            sage: Y = Spec(QQ)
            sage: g = Y.structure_morphism()
            sage: g * f    # indirect doctest
             Composite map:
              From: Affine Space of dimension 2 over Rational Field
              To:   Spectrum of Integer Ring
              Defn:   Generic morphism:
                      From: Affine Space of dimension 2 over Rational Field
                      To:   Spectrum of Rational Field
                    then
                      Generic morphism:
                      From: Spectrum of Rational Field
                      To:   Spectrum of Integer Ring

            sage: f * g
            Traceback (most recent call last):
            ...
            TypeError: self (=Scheme morphism:
              From: Affine Space of dimension 2 over Rational Field
              To:   Spectrum of Rational Field
              Defn: Structure map) domain must equal right (=Scheme morphism:
              From: Spectrum of Rational Field
              To:   Spectrum of Integer Ring
              Defn: Structure map) codomain
        """
        category = self.category_for()._meet_(right.category_for())
        H = Hom(right.domain(), self._codomain, category)
        return self._composition_(right, H)
示例#31
0
    def __init__(self, K, V):
        r"""
        EXAMPLE::

            sage: L.<b> = NumberField(x^4 + 3*x^2 + 1)
            sage: K = L.relativize(L.subfields(2)[0][1], 'a')
            sage: V, fr, to = K.relative_vector_space()
            sage: to
            Isomorphism map:
              From: Number Field in a0 with defining polynomial x^2 - b0*x + 1 over its base field
              To:   Vector space of dimension 2 over Number Field in b0 with defining polynomial x^2 + 1
        """
        self.__V = V
        self.__K = K
        self.__rnf = K.pari_rnf()
        self.__zero = QQ(0)
        self.__n = K.relative_degree()
        self.__x = pari("'x")
        self.__y = pari("'y")
        self.__B = K.absolute_base_field()
        NumberFieldIsomorphism.__init__(self, Hom(K, V))
示例#32
0
    def __init__(self, domain, codomain):
        r"""
        Initialize this morphism.

        EXAMPLES::

            sage: R.<t> = QQ[]
            sage: sigma = R.hom([t+1])
            sage: S.<x> = R['x',sigma]
            sage: K = S.fraction_field()

            sage: K.coerce_map_from(K.base_ring())  # indirect doctest
            Ore Function base injection morphism:
              From: Fraction Field of Univariate Polynomial Ring in t over Rational Field
              To:   Ore Function Field in x over Fraction Field of Univariate Polynomial Ring in t over Rational Field twisted by t |--> t + 1
        """
        assert codomain.base_ring() is domain, \
            "the domain of the injection must be the base ring of the Ore function field"
        Morphism.__init__(self, Hom(domain, codomain))
        self._an_element = codomain.gen()
        self._repr_type_str = "Ore Function base injection"
示例#33
0
    def _coerce_map_from_(self, S):
        """
        Define coercions.

        EXAMPLES:

        A place is converted to a prime divisor::

            sage: K.<x> = FunctionField(GF(5)); R.<t> = PolynomialRing(K)
            sage: F.<y> = K.extension(t^2-x^3-1)
            sage: O = F.maximal_order()
            sage: I = O.ideal(x+1,y)
            sage: P = I.place()
            sage: y.divisor() + P
            -3*Place (1/x, 1/x^2*y)
             + 2*Place (x + 1, y)
             + Place (x^2 + 4*x + 1, y)
        """
        if isinstance(S, PlaceSet):
            func = lambda place: prime_divisor(self._field, place)
            return SetMorphism(Hom(S, self), func)
示例#34
0
    def __init__(self):
        r"""
        EXAMPLES::

            sage: A = RingOfIntegers(); A
            Integer Ring (redone)
            sage: TestSuite(A).run()

        TESTS::

            sage: A = Sets().WithRealizations().example(); A
            The subset algebra of {1, 2, 3} over Rational Field
            sage: SageIntegers, LongIntegers = A.realizations()
            sage: type(LongIntegers.coerce_map_from(SageIntegers))
            <type 'sage.categories.morphism.SetMorphism'>
            sage: type(SageIntegers.coerce_map_from(LongIntegers))
            <type 'sage.categories.morphism.SetMorphism'>
            
        """
        Parent.__init__(self, category = Rings().WithRealizations())
        category = self.Notations()
        SageIntegers = self.SageIntegers()
        LongIntegers = self.LongIntegers()
        
        f_Sage_to_Long = lambda self: LongIntegers(str(self))
        Sage_to_Long = \
            Hom(SageIntegers, LongIntegers, Sets().Realizations())(f_Sage_to_Long)
            
        f_Long_to_Sage = lambda self:(SageIntegers(Integer(self.value)))
        Long_to_Sage = \
            Hom(LongIntegers, SageIntegers, Sets().Realizations())(f_Long_to_Sage)

        # Tried:
        # Hom(LongIntegers, SageIntegers, Rings().Realizations())(f_Long_to_Sage)
        # Hom(LongIntegers, SageIntegers, category)(f_Long_to_Sage)
        
        Sage_to_Long.register_as_coercion()
        Long_to_Sage.register_as_coercion()
示例#35
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
示例#36
0
文件: homset.py 项目: novoselt/sage
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