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 are not stongly 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
Nonetheless, garbage collection occurs when the original references are overwritten:
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]
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)¶ Create the set of endomorphisms of
X
in the category category.INPUT:
X
– anythingcategory
– (optional) category in which to coerceX
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, a homset in a category
Cs()
is in the homset category of the lowest full super categoryBs()
ofCs()
that implementsBs.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: 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
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() Category of endsets of unital magmas and additive unital additive magmas
-
sage.categories.homset.
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 categoryY
– an object of a categorycategory
– a category in which the morphisms must be. (default: the meet of the categories ofX
andY
) BothX
andY
must belong to that category.check
– a boolean (default:True
): whether to check the input, and in particular thatX
andY
belong tocategory
.
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 ticket #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
andY
, 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, thencategory
is used, after checking thatX
andY
are indeed incategory
: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 aTypeError
. This hook is currently mostly used to create homsets in some specific subclass ofHomset
(e.g.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
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 ticket #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
andQ
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 ticket #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
andY
, and how much from the specificX
andY
. In particular, do we need several parent classes depending onX
andY
, 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 notX.category().is_subcategory(category)
as it used to be before trac ticket #16275 (see trac ticket #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
-
class
sage.categories.homset.
Homset
(X, Y, category=None, base=None, check=True)¶ Bases:
sage.structure.parent.Set_generic
The class for collections of morphisms in a category.
EXAMPLES:
sage: H = Hom(QQ^2, QQ^3) sage: loads(H.dumps()) is H 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 sage: loads(dumps(H)) is H True
Conversely, homsets of non-unique parents are non-unique:
sage: H = End(ProjectiveSpace(2, names=’x,y,z’)) sage: loads(dumps(ProjectiveSpace(2, names=’x,y,z’))) is ProjectiveSpace(2, names=’x,y,z’) False sage: loads(dumps(ProjectiveSpace(2, names=’x,y,z’))) == ProjectiveSpace(2, names=’x,y,z’) True sage: loads(dumps(H)) is H False sage: loads(dumps(H)) == H True-
codomain
()¶ 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
-
domain
()¶ 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
-
element_class_set_morphism
()¶ 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 <type 'sage.categories.morphism.SetMorphism'>
-
homset_category
()¶ 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)) sage: H.homset_category() Category of finite permutation groups
-
identity
()¶ 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() Ring Coercion morphism: From: Integer Ring To: Rational Field
-
is_endomorphism_set
()¶ Return
True
if the domain and codomain ofself
are the same object.EXAMPLES:
sage: P.<t> = ZZ[] sage: f = P.hom([1/2*t]) sage: f.parent().is_endomorphism_set() False sage: g = P.hom([2*t]) sage: g.parent().is_endomorphism_set() True
-
natural_map
()¶ 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.
-
reversed
()¶ 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 finite dimensional modules with basis over (euclidean domains 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 (euclidean domains and infinite enumerated sets and metric spaces) sage: 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)¶ Bases:
sage.categories.homset.Homset
TESTS:
sage: X = ZZ['x']; X.rename("X") sage: Y = ZZ['y']; Y.rename("Y") sage: class MyHomset(HomsetWithBase): ... def my_function(self, x): ... return Y(x[0]) ... def _an_element_(self): ... return sage.categories.morphism.SetMorphism(self, self.my_function) ... sage: import __main__; __main__.MyHomset = MyHomset # fakes MyHomset being defined in a Python module sage: H = MyHomset(X, Y, category=Monoids()) sage: H Set of Morphisms from X to Y in Category of monoids sage: H.base() Integer Ring sage: TestSuite(H).run()
-
sage.categories.homset.
end
(X, f)¶ Return
End(X)(f)
, wheref
is data that defines an element ofEnd(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
-
sage.categories.homset.
hom
(X, Y, f)¶ Return
Hom(X,Y)(f)
, wheref
is data that defines an element ofHom(X,Y)
.EXAMPLES:
sage: phi = hom(QQ['x'], QQ, [2]) sage: phi(x^2 + 3) 7
-
sage.categories.homset.
is_Endset
(x)¶ Return
True
ifx
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()) False sage: g = P.hom([2*t]) sage: is_Endset(g.parent()) True
-
sage.categories.homset.
is_Homset
(x)¶ Return
True
ifx
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) False sage: is_Homset(f.category()) False sage: is_Homset(f.parent()) True