# Homsets#

The class `Hom` is the base class used to represent sets of morphisms between objects of a given category. `Hom` objects are usually “weakly” cached upon creation so that they don’t have to be generated over and over but can be garbage collected together with the corresponding objects when these are not strongly ref’ed anymore.

EXAMPLES:

In the following, the `Hom` object is indeed cached:

```sage: K = GF(17)
sage: H = Hom(ZZ, K)
sage: H
Set of Homomorphisms from Integer Ring to Finite Field of size 17
sage: H is Hom(ZZ, K)
True
```
```>>> from sage.all import *
>>> K = GF(Integer(17))
>>> H = Hom(ZZ, K)
>>> H
Set of Homomorphisms from Integer Ring to Finite Field of size 17
>>> H is Hom(ZZ, K)
True
```

Nonetheless, garbage collection occurs when the original references are overwritten:

```sage: # needs sage.libs.pari
sage: for p in prime_range(200):
....:     K = GF(p)
....:     H = Hom(ZZ, K)
sage: import gc
sage: _ = gc.collect()
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)
1
sage: L
[Finite Field of size 199]
```
```>>> from sage.all import *
>>> # needs sage.libs.pari
>>> for p in prime_range(Integer(200)):
...     K = GF(p)
...     H = Hom(ZZ, K)
>>> import gc
>>> _ = gc.collect()
>>> from sage.rings.finite_rings.finite_field_prime_modn import FiniteField_prime_modn as FF
>>> L = [x for x in gc.get_objects() if isinstance(x, FF)]
>>> len(L)
1
>>> L
[Finite Field of size 199]
```

AUTHORS:

• David Kohel and William Stein

• David Joyner (2005-12-17): added examples

• William Stein (2006-01-14): Changed from Homspace to Homset.

• Nicolas M. Thiery (2008-12-): Updated for the new category framework

• Simon King (2011-12): Use a weak cache for homsets

• Simon King (2013-02): added examples

sage.categories.homset.End(X, category=None)[source]#

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)                                                    # needs sage.modules
sage: End(V)                                                                    # needs sage.modules
Set of Morphisms (Linear Transformations)
from Vector space of dimension 3 over Rational Field
to Vector space of dimension 3 over Rational Field
```
```>>> from sage.all import *
>>> V = VectorSpace(QQ, Integer(3))                                                    # needs sage.modules
>>> End(V)                                                                    # needs sage.modules
Set of Morphisms (Linear Transformations)
from Vector space of dimension 3 over Rational Field
to Vector space of dimension 3 over Rational Field
```
```sage: # needs sage.groups
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 enumerated permutation groups
sage: S.domain()
Alternating group of order 3!/2 as a permutation group
```
```>>> from sage.all import *
>>> # needs sage.groups
>>> G = AlternatingGroup(Integer(3))
>>> 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 enumerated permutation groups
>>> S.domain()
Alternating group of order 3!/2 as a permutation group
```

To avoid creating superfluous categories, a homset in a category `Cs()` is in the homset category of the lowest full super category `Bs()` of `Cs()` that implements `Bs.Homsets` (or the join thereof if there are several). For example, finite groups form a full subcategory of unital magmas: any unital magma morphism between two finite groups is a finite group morphism. Since finite groups currently implement nothing more than unital magmas about their homsets, we have:

```sage: # needs sage.groups
sage: G = GL(3, 3)
sage: G.category()
Category of finite groups
sage: H = Hom(G, G)
sage: H.homset_category()
Category of finite groups
sage: H.category()
Category of endsets of unital magmas
```
```>>> from sage.all import *
>>> # needs sage.groups
>>> G = GL(Integer(3), Integer(3))
>>> G.category()
Category of finite groups
>>> H = Hom(G, G)
>>> H.homset_category()
Category of finite groups
>>> H.category()
Category of endsets of unital magmas
```

Similarly, a ring morphism just needs to preserve addition, multiplication, zero, and one. Accordingly, and since the category of rings implements nothing specific about its homsets, a ring homset is currently constructed in the category of homsets of unital magmas and unital additive magmas:

```sage: H = Hom(ZZ,ZZ,Rings())
sage: H.category()
```
```>>> from sage.all import *
>>> H = Hom(ZZ,ZZ,Rings())
>>> H.category()
```
sage.categories.homset.Hom(X, Y, category=None, check=True)[source]#

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)                                                    # needs sage.modules
sage: Hom(V, V)                                                                 # needs sage.modules
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)                                                   # needs sage.groups
sage: Hom(G, G)                                                                 # needs sage.groups
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))                                 # needs sage.modules
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))                                 # needs sage.modules
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
```
```>>> from sage.all import *
>>> V = VectorSpace(QQ, Integer(3))                                                    # needs sage.modules
>>> Hom(V, V)                                                                 # needs sage.modules
Set of Morphisms (Linear Transformations) from
Vector space of dimension 3 over Rational Field to
Vector space of dimension 3 over Rational Field
>>> G = AlternatingGroup(Integer(3))                                                   # needs sage.groups
>>> Hom(G, G)                                                                 # needs sage.groups
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
>>> Hom(ZZ, QQ, Sets())
Set of Morphisms from Integer Ring to Rational Field in Category of sets

>>> Hom(FreeModule(ZZ, Integer(1)), FreeModule(QQ, Integer(1)))                                 # needs sage.modules
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
>>> Hom(FreeModule(QQ, Integer(1)), FreeModule(ZZ, Integer(1)))                                 # needs sage.modules
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 Issue #11521 by using a weak cache:

```sage: # needs sage.libs.pari
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)
```
```>>> from sage.all import *
>>> # needs sage.libs.pari
>>> for p in prime_range(Integer(10)**Integer(3)):
...  K = GF(p)
...  a = K(Integer(0))
>>> import gc
>>> gc.collect()       # random
624
>>> from sage.rings.finite_rings.finite_field_prime_modn import FiniteField_prime_modn as FF
>>> L = [x for x in gc.get_objects() if isinstance(x, FF)]
>>> len(L), L[Integer(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                                                  # needs sage.groups
Symmetric group of order 3! as a permutation group
```
```>>> from sage.all import *
>>> X = ZZ; X
Integer Ring
>>> Y = SymmetricGroup(Integer(3)); Y                                                  # needs sage.groups
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)                                                                 # needs sage.groups
Set of Morphisms from Integer Ring
to Symmetric group of order 3! as a permutation group
in Category of enumerated monoids
```
```>>> from sage.all import *
>>> Hom(X, Y)                                                                 # needs sage.groups
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())                                                       # needs sage.groups
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())                                                       # needs sage.groups
Traceback (most recent call last):
...
ValueError: Integer Ring is not in Category of groups
```
```>>> from sage.all import *
>>> Hom(X, Y, Magmas())                                                       # needs sage.groups
Set of Morphisms
from Integer Ring
to Symmetric group of order 3! as a permutation group
in Category of magmas

>>> Hom(X, Y, Groups())                                                       # needs sage.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 `Homset` (e.g. `sage.rings.homset.RingHomset`):

```sage: Hom(QQ,QQ).__class__
<class 'sage.rings.homset.RingHomset_generic_with_category'>
```
```>>> from sage.all import *
>>> 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
```
```>>> from sage.all import *
>>> Hom(QQ,QQ) == QQ._Hom_(QQ, category=QQ.category())
True
>>> Hom(QQ,QQ) is QQ._Hom_(QQ, category=QQ.category())
False
```

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).

class sage.categories.homset.Homset(X, Y, category=None, base=None, check=True)[source]#

Bases: `Set_generic`

The class for collections of morphisms in a category.

EXAMPLES:

```sage: H = Hom(QQ^2, QQ^3)                                                       # needs sage.modules
sage: loads(H.dumps()) is H                                                     # needs sage.modules
True
```
```>>> from sage.all import *
>>> H = Hom(QQ**Integer(2), QQ**Integer(3))                                                       # needs sage.modules
>>> loads(H.dumps()) is H                                                     # needs sage.modules
True
```

Homsets of unique parents are unique as well:

```sage: H = End(AffineSpace(2, names='x,y'))
sage: loads(dumps(AffineSpace(2, names='x,y'))) is AffineSpace(2, names='x,y')
True
True
```
```>>> from sage.all import *
>>> H = End(AffineSpace(Integer(2), names='x,y'))
>>> loads(dumps(AffineSpace(Integer(2), names='x,y'))) is AffineSpace(Integer(2), names='x,y')
True
True
```

Conversely, homsets of non-unique parents are non-unique:

```sage: P11 = ProductProjectiveSpaces(QQ, [1, 1])
sage: H = End(P11)
sage: loads(dumps(P11)) is ProductProjectiveSpaces(QQ, [1, 1])
False
sage: loads(dumps(P11)) == ProductProjectiveSpaces(QQ, [1, 1])
True
False
True
```
```>>> from sage.all import *
>>> P11 = ProductProjectiveSpaces(QQ, [Integer(1), Integer(1)])
>>> H = End(P11)
>>> loads(dumps(P11)) is ProductProjectiveSpaces(QQ, [Integer(1), Integer(1)])
False
>>> loads(dumps(P11)) == ProductProjectiveSpaces(QQ, [Integer(1), Integer(1)])
True
False
True
```
codomain()[source]#

Return the codomain of this homset.

EXAMPLES:

```sage: P.<t> = ZZ[]
sage: f = P.hom([1/2*t])
sage: f.parent().codomain()
Univariate Polynomial Ring in t over Rational Field
sage: f.codomain() is f.parent().codomain()
True
```
```>>> from sage.all import *
>>> P = ZZ['t']; (t,) = P._first_ngens(1)
>>> f = P.hom([Integer(1)/Integer(2)*t])
>>> f.parent().codomain()
Univariate Polynomial Ring in t over Rational Field
>>> f.codomain() is f.parent().codomain()
True
```
domain()[source]#

Return the domain of this homset.

EXAMPLES:

```sage: P.<t> = ZZ[]
sage: f = P.hom([1/2*t])
sage: f.parent().domain()
Univariate Polynomial Ring in t over Integer Ring
sage: f.domain() is f.parent().domain()
True
```
```>>> from sage.all import *
>>> P = ZZ['t']; (t,) = P._first_ngens(1)
>>> f = P.hom([Integer(1)/Integer(2)*t])
>>> f.parent().domain()
Univariate Polynomial Ring in t over Integer Ring
>>> f.domain() is f.parent().domain()
True
```
element_class_set_morphism()[source]#

A base class for elements of this homset which are also `SetMorphism`, i.e. implemented by mean of a Python function.

This is currently plain `SetMorphism`, without inheritance from categories.

Todo

Refactor during the upcoming homset cleanup.

EXAMPLES:

```sage: H = Hom(ZZ, ZZ)
sage: H.element_class_set_morphism
<class 'sage.categories.morphism.SetMorphism'>
```
```>>> from sage.all import *
>>> H = Hom(ZZ, ZZ)
>>> H.element_class_set_morphism
<class 'sage.categories.morphism.SetMorphism'>
```
homset_category()[source]#

Return the category that this is a Hom in, i.e., this is typically the category of the domain or codomain object.

EXAMPLES:

```sage: H = Hom(AlternatingGroup(4), AlternatingGroup(7))                     # needs sage.groups
sage: H.homset_category()                                                   # needs sage.groups
Category of finite enumerated permutation groups
```
```>>> from sage.all import *
>>> H = Hom(AlternatingGroup(Integer(4)), AlternatingGroup(Integer(7)))                     # needs sage.groups
>>> H.homset_category()                                                   # needs sage.groups
Category of finite enumerated permutation groups
```
identity()[source]#

The identity map of this homset.

Note

Of course, this only exists for sets of endomorphisms.

EXAMPLES:

```sage: H = Hom(QQ,QQ)
sage: H.identity()
Identity endomorphism of Rational Field
sage: H = Hom(ZZ,QQ)
sage: H.identity()
Traceback (most recent call last):
...
TypeError: identity map only defined for endomorphisms; try natural_map() instead
sage: H.natural_map()
Natural morphism:
From: Integer Ring
To:   Rational Field
```
```>>> from sage.all import *
>>> H = Hom(QQ,QQ)
>>> H.identity()
Identity endomorphism of Rational Field
>>> H = Hom(ZZ,QQ)
>>> H.identity()
Traceback (most recent call last):
...
TypeError: identity map only defined for endomorphisms; try natural_map() instead
>>> H.natural_map()
Natural morphism:
From: Integer Ring
To:   Rational Field
```
natural_map()[source]#

Return the “natural map” of this homset.

Note

By default, a formal coercion morphism is returned.

EXAMPLES:

```sage: H = Hom(ZZ['t'],QQ['t'], CommutativeAdditiveGroups())
sage: H.natural_map()
Coercion morphism:
From: Univariate Polynomial Ring in t over Integer Ring
To:   Univariate Polynomial Ring in t over Rational Field
sage: H = Hom(QQ['t'], GF(3)['t'])
sage: H.natural_map()
Traceback (most recent call last):
...
TypeError: natural coercion morphism
from Univariate Polynomial Ring in t over Rational Field
to Univariate Polynomial Ring in t over Finite Field of size 3 not defined
```
```>>> from sage.all import *
>>> H.natural_map()
Coercion morphism:
From: Univariate Polynomial Ring in t over Integer Ring
To:   Univariate Polynomial Ring in t over Rational Field
>>> H = Hom(QQ['t'], GF(Integer(3))['t'])
>>> H.natural_map()
Traceback (most recent call last):
...
TypeError: natural coercion morphism
from Univariate Polynomial Ring in t over Rational Field
to Univariate Polynomial Ring in t over Finite Field of size 3 not defined
```
one()[source]#

The identity map of this homset.

Note

Of course, this only exists for sets of endomorphisms.

EXAMPLES:

```sage: K = GaussianIntegers()                                                # needs sage.rings.number_field
sage: End(K).one()                                                          # needs sage.rings.number_field
Identity endomorphism of Gaussian Integers generated by I
in Number Field in I with defining polynomial x^2 + 1 with I = 1*I
```
```>>> from sage.all import *
>>> K = GaussianIntegers()                                                # needs sage.rings.number_field
>>> End(K).one()                                                          # needs sage.rings.number_field
Identity endomorphism of Gaussian Integers generated by I
in Number Field in I with defining polynomial x^2 + 1 with I = 1*I
```
reversed()[source]#

Return the corresponding homset, but with the domain and codomain reversed.

EXAMPLES:

```sage: # needs sage.modules
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 finite dimensional modules with basis over (Dedekind
domains and euclidean domains and noetherian rings
and infinite enumerated sets and metric spaces)
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 finite dimensional modules with basis over (Dedekind
domains and euclidean domains and noetherian rings
and infinite enumerated sets and metric spaces)
sage: type(H.reversed())
<class 'sage.modules.free_module_homspace.FreeModuleHomspace_with_category'>
```
```>>> from sage.all import *
>>> # needs sage.modules
>>> H = Hom(ZZ**Integer(2), ZZ**Integer(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 finite dimensional modules with basis over (Dedekind
domains and euclidean domains and noetherian rings
and infinite enumerated sets and metric spaces)
>>> type(H)
<class 'sage.modules.free_module_homspace.FreeModuleHomspace_with_category'>
>>> 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 finite dimensional modules with basis over (Dedekind
domains and euclidean domains and noetherian rings
and infinite enumerated sets and metric spaces)
>>> type(H.reversed())
<class 'sage.modules.free_module_homspace.FreeModuleHomspace_with_category'>
```
class sage.categories.homset.HomsetWithBase(X, Y, category=None, check=True, base=None)[source]#

Bases: `Homset`

sage.categories.homset.end(X, f)[source]#

Return `End(X)(f)`, where `f` is data that defines an element of `End(X)`.

EXAMPLES:

```sage: R.<x> = QQ[]
sage: phi = end(R, [x + 1])
sage: phi
Ring endomorphism of Univariate Polynomial Ring in x over Rational Field
Defn: x |--> x + 1
sage: phi(x^2 + 5)
x^2 + 2*x + 6
```
```>>> from sage.all import *
>>> R = QQ['x']; (x,) = R._first_ngens(1)
>>> phi = end(R, [x + Integer(1)])
>>> phi
Ring endomorphism of Univariate Polynomial Ring in x over Rational Field
Defn: x |--> x + 1
>>> phi(x**Integer(2) + Integer(5))
x^2 + 2*x + 6
```
sage.categories.homset.hom(X, Y, f)[source]#

Return `Hom(X,Y)(f)`, where `f` is data that defines an element of `Hom(X,Y)`.

EXAMPLES:

```sage: R.<x> = QQ[]
sage: phi = hom(R, QQ, [2])
sage: phi(x^2 + 3)
7
```
```>>> from sage.all import *
>>> R = QQ['x']; (x,) = R._first_ngens(1)
>>> phi = hom(R, QQ, [Integer(2)])
>>> phi(x**Integer(2) + Integer(3))
7
```
sage.categories.homset.is_Endset(x)[source]#

Return `True` if `x` is a set of endomorphisms in a category.

EXAMPLES:

```sage: from sage.categories.homset import is_Endset
sage: P.<t> = ZZ[]
sage: f = P.hom([1/2*t])
sage: is_Endset(f.parent())
doctest:warning...
DeprecationWarning: the function is_Endset is deprecated;
use 'isinstance(..., Homset) and ....is_endomorphism_set()' instead
See https://github.com/sagemath/sage/issues/37922 for details.
False
sage: g = P.hom([2*t])
sage: is_Endset(g.parent())
True
```
```>>> from sage.all import *
>>> from sage.categories.homset import is_Endset
>>> P = ZZ['t']; (t,) = P._first_ngens(1)
>>> f = P.hom([Integer(1)/Integer(2)*t])
>>> is_Endset(f.parent())
doctest:warning...
DeprecationWarning: the function is_Endset is deprecated;
use 'isinstance(..., Homset) and ....is_endomorphism_set()' instead
See https://github.com/sagemath/sage/issues/37922 for details.
False
>>> g = P.hom([Integer(2)*t])
>>> is_Endset(g.parent())
True
```
sage.categories.homset.is_Homset(x)[source]#

Return `True` if `x` is a set of homomorphisms in a category.

EXAMPLES:

```sage: from sage.categories.homset import is_Homset
sage: P.<t> = ZZ[]
sage: f = P.hom([1/2*t])
sage: is_Homset(f)
doctest:warning...
DeprecationWarning: the function is_Homset is deprecated;
See https://github.com/sagemath/sage/issues/37922 for details.
False
sage: is_Homset(f.category())
False
sage: is_Homset(f.parent())
True
```
```>>> from sage.all import *
>>> from sage.categories.homset import is_Homset
>>> P = ZZ['t']; (t,) = P._first_ngens(1)
>>> f = P.hom([Integer(1)/Integer(2)*t])
>>> is_Homset(f)
doctest:warning...
DeprecationWarning: the function is_Homset is deprecated;