Categories#
AUTHORS:
David Kohel, William Stein and Nicolas M. Thiery
Every Sage object lies in a category. Categories in Sage are modeled on the mathematical idea of category, and are distinct from Python classes, which are a programming construct.
In most cases, typing x.category()
returns the category to which x
belongs. If C
is a category and x
is any object, C(x)
tries to
make an object in C
from x
. Checking if x
belongs to C
is done
as usually by x in C
.
See Category
and sage.categories.primer
for more details.
EXAMPLES:
We create a couple of categories:
sage: Sets()
Category of sets
sage: GSets(AbelianGroup([2, 4, 9])) # needs sage.groups
Category of G-sets for Multiplicative Abelian group isomorphic to C2 x C4 x C9
sage: Semigroups()
Category of semigroups
sage: VectorSpaces(FiniteField(11))
Category of vector spaces over Finite Field of size 11
sage: Ideals(IntegerRing())
Category of ring ideals in Integer Ring
>>> from sage.all import *
>>> Sets()
Category of sets
>>> GSets(AbelianGroup([Integer(2), Integer(4), Integer(9)])) # needs sage.groups
Category of G-sets for Multiplicative Abelian group isomorphic to C2 x C4 x C9
>>> Semigroups()
Category of semigroups
>>> VectorSpaces(FiniteField(Integer(11)))
Category of vector spaces over Finite Field of size 11
>>> Ideals(IntegerRing())
Category of ring ideals in Integer Ring
Let’s request the category of some objects:
sage: V = VectorSpace(RationalField(), 3) # needs sage.modules
sage: V.category() # needs sage.modules
Category of finite dimensional vector spaces with basis
over (number fields and quotient fields and metric spaces)
sage: G = SymmetricGroup(9) # needs sage.groups
sage: G.category() # needs sage.groups
Join of
Category of finite enumerated permutation groups and
Category of finite Weyl groups and
Category of well generated finite irreducible complex reflection groups
sage: P = PerfectMatchings(3) # needs sage.combinat
sage: P.category() # needs sage.combinat
Category of finite enumerated sets
>>> from sage.all import *
>>> V = VectorSpace(RationalField(), Integer(3)) # needs sage.modules
>>> V.category() # needs sage.modules
Category of finite dimensional vector spaces with basis
over (number fields and quotient fields and metric spaces)
>>> G = SymmetricGroup(Integer(9)) # needs sage.groups
>>> G.category() # needs sage.groups
Join of
Category of finite enumerated permutation groups and
Category of finite Weyl groups and
Category of well generated finite irreducible complex reflection groups
>>> P = PerfectMatchings(Integer(3)) # needs sage.combinat
>>> P.category() # needs sage.combinat
Category of finite enumerated sets
Let’s check some memberships:
sage: V in VectorSpaces(QQ) # needs sage.modules
True
sage: V in VectorSpaces(FiniteField(11)) # needs sage.modules
False
sage: G in Monoids() # needs sage.groups
True
sage: P in Rings() # needs sage.combinat
False
>>> from sage.all import *
>>> V in VectorSpaces(QQ) # needs sage.modules
True
>>> V in VectorSpaces(FiniteField(Integer(11))) # needs sage.modules
False
>>> G in Monoids() # needs sage.groups
True
>>> P in Rings() # needs sage.combinat
False
For parametrized categories one can use the following shorthand:
sage: V in VectorSpaces # needs sage.modules
True
sage: G in VectorSpaces # needs sage.groups
False
>>> from sage.all import *
>>> V in VectorSpaces # needs sage.modules
True
>>> G in VectorSpaces # needs sage.groups
False
A parent P
is in a category C
if P.category()
is a subcategory of
C
.
Note
Any object of a category should be an instance of
CategoryObject
.
For backward compatibility this is not yet enforced:
sage: class A:
....: def category(self):
....: return Fields()
sage: A() in Rings()
True
>>> from sage.all import *
>>> class A:
... def category(self):
... return Fields()
>>> A() in Rings()
True
By default, the category of an element \(x\) of a parent \(P\) is the category of all objects of \(P\) (this is dubious and may be deprecated):
sage: V = VectorSpace(RationalField(), 3) # needs sage.modules
sage: v = V.gen(1) # needs sage.modules
sage: v.category() # needs sage.modules
Category of elements of Vector space of dimension 3 over Rational Field
>>> from sage.all import *
>>> V = VectorSpace(RationalField(), Integer(3)) # needs sage.modules
>>> v = V.gen(Integer(1)) # needs sage.modules
>>> v.category() # needs sage.modules
Category of elements of Vector space of dimension 3 over Rational Field
- class sage.categories.category.Category[source]#
Bases:
UniqueRepresentation
,SageObject
The base class for modeling mathematical categories, like for example:
Groups()
: the category of groupsEuclideanDomains()
: the category of euclidean ringsVectorSpaces(QQ)
: the category of vector spaces over the field of rationals
See
sage.categories.primer
for an introduction to categories in Sage, their relevance, purpose, and usage. The documentation below will focus on their implementation.Technically, a category is an instance of the class
Category
or some of its subclasses. Some categories, likeVectorSpaces
, are parametrized:VectorSpaces(QQ)
is one of many instances of the classVectorSpaces
. On the other hand,EuclideanDomains()
is the single instance of the classEuclideanDomains
.Recall that an algebraic structure (say, the ring \(\QQ[x]\)) is modelled in Sage by an object which is called a parent. This object belongs to certain categories (here
EuclideanDomains()
andAlgebras()
). The elements of the ring are themselves objects.The class of a category (say
EuclideanDomains
) can define simultaneously:Operations on the category itself (what is its super categories? its category of morphisms? its dual category?).
Generic operations on parents in this category, like the ring \(\QQ[x]\).
Generic operations on elements of such parents (e. g., the Euclidean algorithm for computing gcds).
Generic operations on morphisms of this category.
This is achieved as follows:
sage: from sage.categories.category import Category sage: class EuclideanDomains(Category): ....: # operations on the category itself ....: def super_categories(self): ....: [Rings()] ....: ....: def dummy(self): # TODO: find some good examples ....: pass ....: ....: class ParentMethods: # holds the generic operations on parents ....: # TODO: find a good example of an operation ....: pass ....: ....: class ElementMethods:# holds the generic operations on elements ....: def gcd(x,y): ....: # Euclid algorithms ....: pass ....: ....: class MorphismMethods: # holds the generic operations on morphisms ....: # TODO: find a good example of an operation ....: pass ....:
>>> from sage.all import * >>> from sage.categories.category import Category >>> class EuclideanDomains(Category): ... # operations on the category itself ... def super_categories(self): ... [Rings()] ....: >>> def dummy(self): # TODO: find some good examples ... pass ....: >>> class ParentMethods: # holds the generic operations on parents ... # TODO: find a good example of an operation ... pass ....: >>> class ElementMethods:# holds the generic operations on elements ... def gcd(x,y): ... # Euclid algorithms ... pass ....: >>> class MorphismMethods: # holds the generic operations on morphisms ... # TODO: find a good example of an operation ... pass ....:
Note that the nested class
ParentMethods
is merely a container of operations, and does not inherit from anything. Instead, the hierarchy relation is defined once at the level of the categories, and the actual hierarchy of classes is built in parallel from all theParentMethods
nested classes, and stored in the attributesparent_class
. Then, a parent in a categoryC
receives the appropriate operations from all the super categories by usual class inheritance fromC.parent_class
.Similarly, two other hierarchies of classes, for elements and morphisms respectively, are built from all the
ElementMethods
andMorphismMethods
nested classes.EXAMPLES:
We define a hierarchy of four categories
As()
,Bs()
,Cs()
,Ds()
with a diamond inheritance. Think for example:As()
: the category of setsBs()
: the category of additive groupsCs()
: the category of multiplicative monoidsDs()
: the category of rings
sage: from sage.categories.category import Category sage: from sage.misc.lazy_attribute import lazy_attribute sage: class As (Category): ....: def super_categories(self): ....: return [] ....: ....: class ParentMethods: ....: def fA(self): ....: return "A" ....: f = fA sage: class Bs (Category): ....: def super_categories(self): ....: return [As()] ....: ....: class ParentMethods: ....: def fB(self): ....: return "B" sage: class Cs (Category): ....: def super_categories(self): ....: return [As()] ....: ....: class ParentMethods: ....: def fC(self): ....: return "C" ....: f = fC sage: class Ds (Category): ....: def super_categories(self): ....: return [Bs(),Cs()] ....: ....: class ParentMethods: ....: def fD(self): ....: return "D"
>>> from sage.all import * >>> from sage.categories.category import Category >>> from sage.misc.lazy_attribute import lazy_attribute >>> class As (Category): ... def super_categories(self): ... return [] ....: >>> class ParentMethods: ... def fA(self): ... return "A" ... f = fA >>> class Bs (Category): ... def super_categories(self): ... return [As()] ....: >>> class ParentMethods: ... def fB(self): ... return "B" >>> class Cs (Category): ... def super_categories(self): ... return [As()] ....: >>> class ParentMethods: ... def fC(self): ... return "C" ... f = fC >>> class Ds (Category): ... def super_categories(self): ... return [Bs(),Cs()] ....: >>> class ParentMethods: ... def fD(self): ... return "D"
Categories should always have unique representation; by Issue #12215, this means that it will be kept in cache, but only if there is still some strong reference to it.
We check this before proceeding:
sage: import gc sage: idAs = id(As()) sage: _ = gc.collect() sage: n == id(As()) False sage: a = As() sage: id(As()) == id(As()) True sage: As().parent_class == As().parent_class True
>>> from sage.all import * >>> import gc >>> idAs = id(As()) >>> _ = gc.collect() >>> n == id(As()) False >>> a = As() >>> id(As()) == id(As()) True >>> As().parent_class == As().parent_class True
We construct a parent in the category
Ds()
(that, is an instance ofDs().parent_class
), and check that it has access to all the methods provided by all the categories, with the appropriate inheritance order:sage: D = Ds().parent_class() sage: [ D.fA(), D.fB(), D.fC(), D.fD() ] ['A', 'B', 'C', 'D'] sage: D.f() 'C'
>>> from sage.all import * >>> D = Ds().parent_class() >>> [ D.fA(), D.fB(), D.fC(), D.fD() ] ['A', 'B', 'C', 'D'] >>> D.f() 'C'
sage: C = Cs().parent_class() sage: [ C.fA(), C.fC() ] ['A', 'C'] sage: C.f() 'C'
>>> from sage.all import * >>> C = Cs().parent_class() >>> [ C.fA(), C.fC() ] ['A', 'C'] >>> C.f() 'C'
Here is the parallel hierarchy of classes which has been built automatically, together with the method resolution order (
.mro()
):sage: As().parent_class <class '__main__.As.parent_class'> sage: As().parent_class.__bases__ (<... 'object'>,) sage: As().parent_class.mro() [<class '__main__.As.parent_class'>, <... 'object'>]
>>> from sage.all import * >>> As().parent_class <class '__main__.As.parent_class'> >>> As().parent_class.__bases__ (<... 'object'>,) >>> As().parent_class.mro() [<class '__main__.As.parent_class'>, <... 'object'>]
sage: Bs().parent_class <class '__main__.Bs.parent_class'> sage: Bs().parent_class.__bases__ (<class '__main__.As.parent_class'>,) sage: Bs().parent_class.mro() [<class '__main__.Bs.parent_class'>, <class '__main__.As.parent_class'>, <... 'object'>]
>>> from sage.all import * >>> Bs().parent_class <class '__main__.Bs.parent_class'> >>> Bs().parent_class.__bases__ (<class '__main__.As.parent_class'>,) >>> Bs().parent_class.mro() [<class '__main__.Bs.parent_class'>, <class '__main__.As.parent_class'>, <... 'object'>]
sage: Cs().parent_class <class '__main__.Cs.parent_class'> sage: Cs().parent_class.__bases__ (<class '__main__.As.parent_class'>,) sage: Cs().parent_class.__mro__ (<class '__main__.Cs.parent_class'>, <class '__main__.As.parent_class'>, <... 'object'>)
>>> from sage.all import * >>> Cs().parent_class <class '__main__.Cs.parent_class'> >>> Cs().parent_class.__bases__ (<class '__main__.As.parent_class'>,) >>> Cs().parent_class.__mro__ (<class '__main__.Cs.parent_class'>, <class '__main__.As.parent_class'>, <... 'object'>)
sage: Ds().parent_class <class '__main__.Ds.parent_class'> sage: Ds().parent_class.__bases__ (<class '__main__.Cs.parent_class'>, <class '__main__.Bs.parent_class'>) sage: Ds().parent_class.mro() [<class '__main__.Ds.parent_class'>, <class '__main__.Cs.parent_class'>, <class '__main__.Bs.parent_class'>, <class '__main__.As.parent_class'>, <... 'object'>]
>>> from sage.all import * >>> Ds().parent_class <class '__main__.Ds.parent_class'> >>> Ds().parent_class.__bases__ (<class '__main__.Cs.parent_class'>, <class '__main__.Bs.parent_class'>) >>> Ds().parent_class.mro() [<class '__main__.Ds.parent_class'>, <class '__main__.Cs.parent_class'>, <class '__main__.Bs.parent_class'>, <class '__main__.As.parent_class'>, <... 'object'>]
Note that two categories in the same class need not have the same
super_categories
. For example,Algebras(QQ)
hasVectorSpaces(QQ)
as super category, whereasAlgebras(ZZ)
only hasModules(ZZ)
as super category. In particular, the constructed parent class and element class will differ (inheriting, or not, methods specific for vector spaces):sage: Algebras(QQ).parent_class is Algebras(ZZ).parent_class False sage: issubclass(Algebras(QQ).parent_class, VectorSpaces(QQ).parent_class) True
>>> from sage.all import * >>> Algebras(QQ).parent_class is Algebras(ZZ).parent_class False >>> issubclass(Algebras(QQ).parent_class, VectorSpaces(QQ).parent_class) True
On the other hand, identical hierarchies of classes are, preferably, built only once (e.g. for categories over a base ring):
sage: Algebras(GF(5)).parent_class is Algebras(GF(7)).parent_class True sage: F = FractionField(ZZ['t']) sage: Coalgebras(F).parent_class is Coalgebras(FractionField(F['x'])).parent_class True
>>> from sage.all import * >>> Algebras(GF(Integer(5))).parent_class is Algebras(GF(Integer(7))).parent_class True >>> F = FractionField(ZZ['t']) >>> Coalgebras(F).parent_class is Coalgebras(FractionField(F['x'])).parent_class True
We now construct a parent in the usual way:
sage: class myparent(Parent): ....: def __init__(self): ....: Parent.__init__(self, category=Ds()) ....: def g(self): ....: return "myparent" ....: class Element(): ....: pass sage: D = myparent() sage: D.__class__ <class '__main__.myparent_with_category'> sage: D.__class__.__bases__ (<class '__main__.myparent'>, <class '__main__.Ds.parent_class'>) sage: D.__class__.mro() [<class '__main__.myparent_with_category'>, <class '__main__.myparent'>, <class 'sage.structure.parent.Parent'>, <class 'sage.structure.category_object.CategoryObject'>, <class 'sage.structure.sage_object.SageObject'>, <class '__main__.Ds.parent_class'>, <class '__main__.Cs.parent_class'>, <class '__main__.Bs.parent_class'>, <class '__main__.As.parent_class'>, <... 'object'>] sage: D.fA() 'A' sage: D.fB() 'B' sage: D.fC() 'C' sage: D.fD() 'D' sage: D.f() 'C' sage: D.g() 'myparent'
>>> from sage.all import * >>> class myparent(Parent): ... def __init__(self): ... Parent.__init__(self, category=Ds()) ... def g(self): ... return "myparent" ... class Element(): ... pass >>> D = myparent() >>> D.__class__ <class '__main__.myparent_with_category'> >>> D.__class__.__bases__ (<class '__main__.myparent'>, <class '__main__.Ds.parent_class'>) >>> D.__class__.mro() [<class '__main__.myparent_with_category'>, <class '__main__.myparent'>, <class 'sage.structure.parent.Parent'>, <class 'sage.structure.category_object.CategoryObject'>, <class 'sage.structure.sage_object.SageObject'>, <class '__main__.Ds.parent_class'>, <class '__main__.Cs.parent_class'>, <class '__main__.Bs.parent_class'>, <class '__main__.As.parent_class'>, <... 'object'>] >>> D.fA() 'A' >>> D.fB() 'B' >>> D.fC() 'C' >>> D.fD() 'D' >>> D.f() 'C' >>> D.g() 'myparent'
sage: D.element_class <class '__main__.myparent_with_category.element_class'> sage: D.element_class.mro() [<class '__main__.myparent_with_category.element_class'>, <class ...__main__....Element...>, <class '__main__.Ds.element_class'>, <class '__main__.Cs.element_class'>, <class '__main__.Bs.element_class'>, <class '__main__.As.element_class'>, <... 'object'>]
>>> from sage.all import * >>> D.element_class <class '__main__.myparent_with_category.element_class'> >>> D.element_class.mro() [<class '__main__.myparent_with_category.element_class'>, <class ...__main__....Element...>, <class '__main__.Ds.element_class'>, <class '__main__.Cs.element_class'>, <class '__main__.Bs.element_class'>, <class '__main__.As.element_class'>, <... 'object'>]
- _super_categories()[source]#
The immediate super categories of this category.
This lazy attribute caches the result of the mandatory method
super_categories()
for speed. It also does some mangling (flattening join categories, sorting, …).Whenever speed matters, developers are advised to use this lazy attribute rather than calling
super_categories()
.Note
This attribute is likely to eventually become a tuple. When this happens, we might as well use
Category._sort()
, if notCategory._sort_uniq()
.EXAMPLES:
sage: Rings()._super_categories [Category of rngs, Category of semirings]
>>> from sage.all import * >>> Rings()._super_categories [Category of rngs, Category of semirings]
- _super_categories_for_classes()[source]#
The super categories of this category used for building classes.
This is a close variant of
_super_categories()
used for constructing the list of the bases forparent_class()
,element_class()
, and friends. The purpose is ensure that Python will find a proper Method Resolution Order for those classes. For background, seesage.misc.c3_controlled
.See also
_cmp_key()
.Note
This attribute is calculated as a by-product of computing
_all_super_categories()
.EXAMPLES:
sage: Rings()._super_categories_for_classes [Category of rngs, Category of semirings]
>>> from sage.all import * >>> Rings()._super_categories_for_classes [Category of rngs, Category of semirings]
- _all_super_categories()[source]#
All the super categories of this category, including this category.
Since Issue #11943, the order of super categories is determined by Python’s method resolution order C3 algorithm.
See also
Note
this attribute is likely to eventually become a tuple.
Note
this sets
_super_categories_for_classes()
as a side effectEXAMPLES:
sage: C = Rings(); C Category of rings sage: C._all_super_categories [Category of rings, Category of rngs, Category of semirings, ... Category of monoids, ... Category of commutative additive groups, ... Category of sets, Category of sets with partial maps, Category of objects]
>>> from sage.all import * >>> C = Rings(); C Category of rings >>> C._all_super_categories [Category of rings, Category of rngs, Category of semirings, ... Category of monoids, ... Category of commutative additive groups, ... Category of sets, Category of sets with partial maps, Category of objects]
- _all_super_categories_proper()[source]#
All the proper super categories of this category.
Since Issue #11943, the order of super categories is determined by Python’s method resolution order C3 algorithm.
See also
Note
this attribute is likely to eventually become a tuple.
EXAMPLES:
sage: C = Rings(); C Category of rings sage: C._all_super_categories_proper [Category of rngs, Category of semirings, ... Category of monoids, ... Category of commutative additive groups, ... Category of sets, Category of sets with partial maps, Category of objects]
>>> from sage.all import * >>> C = Rings(); C Category of rings >>> C._all_super_categories_proper [Category of rngs, Category of semirings, ... Category of monoids, ... Category of commutative additive groups, ... Category of sets, Category of sets with partial maps, Category of objects]
- _set_of_super_categories()[source]#
The frozen set of all proper super categories of this category.
Note
this is used for speeding up category containment tests.
See also
EXAMPLES:
sage: sorted(Groups()._set_of_super_categories, key=str) [Category of inverse unital magmas, Category of magmas, Category of monoids, Category of objects, Category of semigroups, Category of sets, Category of sets with partial maps, Category of unital magmas] sage: sorted(Groups()._set_of_super_categories, key=str) [Category of inverse unital magmas, Category of magmas, Category of monoids, Category of objects, Category of semigroups, Category of sets, Category of sets with partial maps, Category of unital magmas]
>>> from sage.all import * >>> sorted(Groups()._set_of_super_categories, key=str) [Category of inverse unital magmas, Category of magmas, Category of monoids, Category of objects, Category of semigroups, Category of sets, Category of sets with partial maps, Category of unital magmas] >>> sorted(Groups()._set_of_super_categories, key=str) [Category of inverse unital magmas, Category of magmas, Category of monoids, Category of objects, Category of semigroups, Category of sets, Category of sets with partial maps, Category of unital magmas]
- _make_named_class(name, method_provider, cache=False, picklable=True)[source]#
Construction of the parent/element/… class of
self
.INPUT:
name
– a string; the name of the class as an attribute ofself
. E.g. “parent_class”method_provider
– a string; the name of an attribute ofself
that provides methods for the new class (in addition to those coming from the super categories). E.g. “ParentMethods”cache
– a boolean orignore_reduction
(default:False
) (passed down to dynamic_class; for internal use only)picklable
– a boolean (default:True
)
ASSUMPTION:
It is assumed that this method is only called from a lazy attribute whose name coincides with the given
name
.OUTPUT:
A dynamic class with bases given by the corresponding named classes of
self
’s super_categories, and methods taken from the classgetattr(self,method_provider)
.Note
In this default implementation, the reduction data of the named class makes it depend on
self
. Since the result is going to be stored in a lazy attribute ofself
anyway, we may as well disable the caching indynamic_class
(hence the default valuecache=False
).CategoryWithParameters
overrides this method so that the same parent/element/… classes can be shared between closely related categories.The bases of the named class may also contain the named classes of some indirect super categories, according to
_super_categories_for_classes()
. This is to guarantee that Python will build consistent method resolution orders. For background, seesage.misc.c3_controlled
.
See also
CategoryWithParameters._make_named_class()
EXAMPLES:
sage: PC = Rings()._make_named_class("parent_class", "ParentMethods"); PC <class 'sage.categories.rings.Rings.parent_class'> sage: type(PC) <class 'sage.structure.dynamic_class.DynamicMetaclass'> sage: PC.__bases__ (<class 'sage.categories.rngs.Rngs.parent_class'>, <class 'sage.categories.semirings.Semirings.parent_class'>)
>>> from sage.all import * >>> PC = Rings()._make_named_class("parent_class", "ParentMethods"); PC <class 'sage.categories.rings.Rings.parent_class'> >>> type(PC) <class 'sage.structure.dynamic_class.DynamicMetaclass'> >>> PC.__bases__ (<class 'sage.categories.rngs.Rngs.parent_class'>, <class 'sage.categories.semirings.Semirings.parent_class'>)
Note that, by default, the result is not cached:
sage: PC is Rings()._make_named_class("parent_class", "ParentMethods") False
>>> from sage.all import * >>> PC is Rings()._make_named_class("parent_class", "ParentMethods") False
Indeed this method is only meant to construct lazy attributes like
parent_class
which already handle this caching:sage: Rings().parent_class <class 'sage.categories.rings.Rings.parent_class'>
>>> from sage.all import * >>> Rings().parent_class <class 'sage.categories.rings.Rings.parent_class'>
Reduction for pickling also assumes the existence of this lazy attribute:
sage: PC._reduction (<built-in function getattr>, (Category of rings, 'parent_class')) sage: loads(dumps(PC)) is Rings().parent_class True
>>> from sage.all import * >>> PC._reduction (<built-in function getattr>, (Category of rings, 'parent_class')) >>> loads(dumps(PC)) is Rings().parent_class True
- _repr_()[source]#
Return the print representation of this category.
EXAMPLES:
sage: Sets() # indirect doctest Category of sets
>>> from sage.all import * >>> Sets() # indirect doctest Category of sets
- _repr_object_names()[source]#
Return the name of the objects of this category.
EXAMPLES:
sage: FiniteGroups()._repr_object_names() 'finite groups' sage: AlgebrasWithBasis(QQ)._repr_object_names() 'algebras with basis over Rational Field'
>>> from sage.all import * >>> FiniteGroups()._repr_object_names() 'finite groups' >>> AlgebrasWithBasis(QQ)._repr_object_names() 'algebras with basis over Rational Field'
- _test_category(**options)[source]#
Run generic tests on this category
See also
EXAMPLES:
sage: Sets()._test_category()
>>> from sage.all import * >>> Sets()._test_category()
Let us now write a couple broken categories:
sage: class MyObjects(Category): ....: pass sage: MyObjects()._test_category() Traceback (most recent call last): ... NotImplementedError: <abstract method super_categories at ...> sage: class MyObjects(Category): ....: def super_categories(self): ....: return tuple() sage: MyObjects()._test_category() Traceback (most recent call last): ... AssertionError: Category of my objects.super_categories() should return a list sage: class MyObjects(Category): ....: def super_categories(self): ....: return [] sage: MyObjects()._test_category() Traceback (most recent call last): ... AssertionError: Category of my objects is not a subcategory of Objects()
>>> from sage.all import * >>> class MyObjects(Category): ... pass >>> MyObjects()._test_category() Traceback (most recent call last): ... NotImplementedError: <abstract method super_categories at ...> >>> class MyObjects(Category): ... def super_categories(self): ... return tuple() >>> MyObjects()._test_category() Traceback (most recent call last): ... AssertionError: Category of my objects.super_categories() should return a list >>> class MyObjects(Category): ... def super_categories(self): ... return [] >>> MyObjects()._test_category() Traceback (most recent call last): ... AssertionError: Category of my objects is not a subcategory of Objects()
- _with_axiom(axiom)[source]#
Return the subcategory of the objects of
self
satisfying the givenaxiom
.INPUT:
axiom
– a string, the name of an axiom
EXAMPLES:
sage: Sets()._with_axiom("Finite") Category of finite sets sage: type(Magmas().Finite().Commutative()) <class 'sage.categories.category.JoinCategory_with_category'> sage: Magmas().Finite().Commutative().super_categories() [Category of commutative magmas, Category of finite sets] sage: C = Algebras(QQ).WithBasis().Commutative() sage: C is Algebras(QQ).Commutative().WithBasis() True
>>> from sage.all import * >>> Sets()._with_axiom("Finite") Category of finite sets >>> type(Magmas().Finite().Commutative()) <class 'sage.categories.category.JoinCategory_with_category'> >>> Magmas().Finite().Commutative().super_categories() [Category of commutative magmas, Category of finite sets] >>> C = Algebras(QQ).WithBasis().Commutative() >>> C is Algebras(QQ).Commutative().WithBasis() True
When
axiom
is not defined forself
,self
is returned:sage: Sets()._with_axiom("Associative") Category of sets
>>> from sage.all import * >>> Sets()._with_axiom("Associative") Category of sets
Warning
This may be changed in the future to raising an error.
- _with_axiom_as_tuple(axiom)[source]#
Return a tuple of categories whose join is
self._with_axiom()
.INPUT:
axiom
– a string, the name of an axiom
This is a lazy version of
_with_axiom()
which is used to avoid recursion loops during join calculations.Note
The order in the result is irrelevant.
EXAMPLES:
sage: Sets()._with_axiom_as_tuple('Finite') (Category of finite sets,) sage: Magmas()._with_axiom_as_tuple('Finite') (Category of magmas, Category of finite sets) sage: Rings().Division()._with_axiom_as_tuple('Finite') (Category of division rings, Category of finite monoids, Category of commutative magmas, Category of finite additive groups) sage: HopfAlgebras(QQ)._with_axiom_as_tuple('FiniteDimensional') (Category of Hopf algebras over Rational Field, Category of finite dimensional vector spaces over Rational Field)
>>> from sage.all import * >>> Sets()._with_axiom_as_tuple('Finite') (Category of finite sets,) >>> Magmas()._with_axiom_as_tuple('Finite') (Category of magmas, Category of finite sets) >>> Rings().Division()._with_axiom_as_tuple('Finite') (Category of division rings, Category of finite monoids, Category of commutative magmas, Category of finite additive groups) >>> HopfAlgebras(QQ)._with_axiom_as_tuple('FiniteDimensional') (Category of Hopf algebras over Rational Field, Category of finite dimensional vector spaces over Rational Field)
- _without_axioms(named=False)[source]#
Return the category without the axioms that have been added to create it.
INPUT:
named
– a boolean (default:False
)
Todo
Improve this explanation.
If
named
isTrue
, then this stops at the first category that has an explicit name of its own. Seecategory_with_axiom.CategoryWithAxiom._without_axioms()
EXAMPLES:
sage: Sets()._without_axioms() Category of sets sage: Semigroups()._without_axioms() Category of magmas sage: Algebras(QQ).Commutative().WithBasis()._without_axioms() Category of magmatic algebras over Rational Field sage: Algebras(QQ).Commutative().WithBasis()._without_axioms(named=True) Category of algebras over Rational Field
>>> from sage.all import * >>> Sets()._without_axioms() Category of sets >>> Semigroups()._without_axioms() Category of magmas >>> Algebras(QQ).Commutative().WithBasis()._without_axioms() Category of magmatic algebras over Rational Field >>> Algebras(QQ).Commutative().WithBasis()._without_axioms(named=True) Category of algebras over Rational Field
- static _sort(categories)[source]#
Return the categories after sorting them decreasingly according to their comparison key.
See also
_cmp_key()
INPUT:
categories
– a list (or iterable) of non-join categories
OUTPUT:
A sorted tuple of categories, possibly with repeats.
Note
The auxiliary function
_flatten_categories
used in the test below expects a second argument, which is a type such that instances of that type will be replaced by its super categories. Usually, this type isJoinCategory
.EXAMPLES:
sage: Category._sort([Sets(), Objects(), Coalgebras(QQ), Monoids(), Sets().Finite()]) (Category of monoids, Category of coalgebras over Rational Field, Category of finite sets, Category of sets, Category of objects) sage: Category._sort([Sets().Finite(), Semigroups().Finite(), Sets().Facade(),Magmas().Commutative()]) (Category of finite semigroups, Category of commutative magmas, Category of finite sets, Category of facade sets) sage: Category._sort(Category._flatten_categories([Sets().Finite(), Algebras(QQ).WithBasis(), Semigroups().Finite(), ....: Sets().Facade(), Algebras(QQ).Commutative(), Algebras(QQ).Graded().WithBasis()], ....: sage.categories.category.JoinCategory)) (Category of algebras with basis over Rational Field, Category of algebras with basis over Rational Field, Category of graded algebras over Rational Field, Category of commutative algebras over Rational Field, Category of finite semigroups, Category of finite sets, Category of facade sets)
>>> from sage.all import * >>> Category._sort([Sets(), Objects(), Coalgebras(QQ), Monoids(), Sets().Finite()]) (Category of monoids, Category of coalgebras over Rational Field, Category of finite sets, Category of sets, Category of objects) >>> Category._sort([Sets().Finite(), Semigroups().Finite(), Sets().Facade(),Magmas().Commutative()]) (Category of finite semigroups, Category of commutative magmas, Category of finite sets, Category of facade sets) >>> Category._sort(Category._flatten_categories([Sets().Finite(), Algebras(QQ).WithBasis(), Semigroups().Finite(), ... Sets().Facade(), Algebras(QQ).Commutative(), Algebras(QQ).Graded().WithBasis()], ... sage.categories.category.JoinCategory)) (Category of algebras with basis over Rational Field, Category of algebras with basis over Rational Field, Category of graded algebras over Rational Field, Category of commutative algebras over Rational Field, Category of finite semigroups, Category of finite sets, Category of facade sets)
- static _sort_uniq(categories)[source]#
Return the categories after sorting them and removing redundant categories.
Redundant categories include duplicates and categories which are super categories of other categories in the input.
INPUT:
categories
– a list (or iterable) of categories
OUTPUT: a sorted tuple of mutually incomparable categories
EXAMPLES:
sage: Category._sort_uniq([Rings(), Monoids(), Coalgebras(QQ)]) (Category of rings, Category of coalgebras over Rational Field)
>>> from sage.all import * >>> Category._sort_uniq([Rings(), Monoids(), Coalgebras(QQ)]) (Category of rings, Category of coalgebras over Rational Field)
Note that, in the above example,
Monoids()
does not appear in the result because it is a super category ofRings()
.
- static __classcall__(*args, **options)[source]#
Input mangling for unique representation.
Let
C = Cs(...)
be a category. Since Issue #12895, the class ofC
is a dynamic subclassCs_with_category
ofCs
in order forC
to inherit code from theSubcategoryMethods
nested classes of its super categories.The purpose of this
__classcall__
method is to ensure that reconstructingC
from its class withCs_with_category(...)
actually calls properlyCs(...)
and gives backC
.See also
EXAMPLES:
sage: A = Algebras(QQ) sage: A.__class__ <class 'sage.categories.algebras.Algebras_with_category'> sage: A is Algebras(QQ) True sage: A is A.__class__(QQ) True
>>> from sage.all import * >>> A = Algebras(QQ) >>> A.__class__ <class 'sage.categories.algebras.Algebras_with_category'> >>> A is Algebras(QQ) True >>> A is A.__class__(QQ) True
- __init__()[source]#
Initialize this category.
EXAMPLES:
sage: class SemiprimitiveRings(Category): ....: def super_categories(self): ....: return [Rings()] ....: class ParentMethods: ....: def jacobson_radical(self): ....: return self.ideal(0) sage: C = SemiprimitiveRings() sage: C Category of semiprimitive rings sage: C.__class__ <class '__main__.SemiprimitiveRings_with_category'>
>>> from sage.all import * >>> class SemiprimitiveRings(Category): ... def super_categories(self): ... return [Rings()] ... class ParentMethods: ... def jacobson_radical(self): ... return self.ideal(Integer(0)) >>> C = SemiprimitiveRings() >>> C Category of semiprimitive rings >>> C.__class__ <class '__main__.SemiprimitiveRings_with_category'>
Note
If the default name of the category (built from the name of the class) is not adequate, please implement
_repr_object_names()
to customize it.
- Realizations()[source]#
Return the category of realizations of the parent
self
or of objects of the categoryself
INPUT:
self
– a parent or a concrete category
Note
this function is actually inserted as a method in the class
Category
(seeRealizations()
). It is defined here for code locality reasons.EXAMPLES:
The category of realizations of some algebra:
sage: Algebras(QQ).Realizations() Join of Category of algebras over Rational Field and Category of realizations of unital magmas
>>> from sage.all import * >>> Algebras(QQ).Realizations() Join of Category of algebras over Rational Field and Category of realizations of unital magmas
The category of realizations of a given algebra:
sage: A = Sets().WithRealizations().example(); A # needs sage.modules The subset algebra of {1, 2, 3} over Rational Field sage: A.Realizations() # needs sage.modules Category of realizations of The subset algebra of {1, 2, 3} over Rational Field sage: C = GradedHopfAlgebrasWithBasis(QQ).Realizations(); C Join of Category of graded Hopf algebras with basis over Rational Field and Category of realizations of Hopf algebras over Rational Field sage: C.super_categories() [Category of graded Hopf algebras with basis over Rational Field, Category of realizations of Hopf algebras over Rational Field] sage: TestSuite(C).run()
>>> from sage.all import * >>> A = Sets().WithRealizations().example(); A # needs sage.modules The subset algebra of {1, 2, 3} over Rational Field >>> A.Realizations() # needs sage.modules Category of realizations of The subset algebra of {1, 2, 3} over Rational Field >>> C = GradedHopfAlgebrasWithBasis(QQ).Realizations(); C Join of Category of graded Hopf algebras with basis over Rational Field and Category of realizations of Hopf algebras over Rational Field >>> C.super_categories() [Category of graded Hopf algebras with basis over Rational Field, Category of realizations of Hopf algebras over Rational Field] >>> TestSuite(C).run()
See also
ClasscallMetaclass
Todo
Add an optional argument to allow for:
sage: Realizations(A, category=Blahs()) # todo: not implemented
>>> from sage.all import * >>> Realizations(A, category=Blahs()) # todo: not implemented
- WithRealizations()[source]#
Return the category of parents in
self
endowed with multiple realizations.INPUT:
self
– a category
See also
The documentation and code (
sage.categories.examples.with_realizations
) ofSets().WithRealizations().example()
for more on how to use and implement a parent with several realizations.Various use cases:
algebras.Moebius
ExtendedAffineWeylGroup
The Implementing Algebraic Structures thematic tutorial.
Note
this function is actually inserted as a method in the class
Category
(seeWithRealizations()
). It is defined here for code locality reasons.EXAMPLES:
sage: Sets().WithRealizations() Category of sets with realizations
>>> from sage.all import * >>> Sets().WithRealizations() Category of sets with realizations
Parent with realizations
Let us now explain the concept of realizations. A parent with realizations is a facade parent (see
Sets.Facade
) admitting multiple concrete realizations where its elements are represented. Consider for example an algebra \(A\) which admits several natural bases:sage: A = Sets().WithRealizations().example(); A # needs sage.modules The subset algebra of {1, 2, 3} over Rational Field
>>> from sage.all import * >>> A = Sets().WithRealizations().example(); A # needs sage.modules The subset algebra of {1, 2, 3} over Rational Field
For each such basis \(B\) one implements a parent \(P_B\) which realizes \(A\) with its elements represented by expanding them on the basis \(B\):
sage: # needs sage.modules sage: A.F() The subset algebra of {1, 2, 3} over Rational Field in the Fundamental basis sage: A.Out() The subset algebra of {1, 2, 3} over Rational Field in the Out basis sage: A.In() The subset algebra of {1, 2, 3} over Rational Field in the In basis sage: A.an_element() F[{}] + 2*F[{1}] + 3*F[{2}] + F[{1, 2}]
>>> from sage.all import * >>> # needs sage.modules >>> A.F() The subset algebra of {1, 2, 3} over Rational Field in the Fundamental basis >>> A.Out() The subset algebra of {1, 2, 3} over Rational Field in the Out basis >>> A.In() The subset algebra of {1, 2, 3} over Rational Field in the In basis >>> A.an_element() F[{}] + 2*F[{1}] + 3*F[{2}] + F[{1, 2}]
If \(B\) and \(B'\) are two bases, then the change of basis from \(B\) to \(B'\) is implemented by a canonical coercion between \(P_B\) and \(P_{B'}\):
sage: # needs sage.modules sage: F = A.F(); In = A.In(); Out = A.Out() sage: i = In.an_element(); i In[{}] + 2*In[{1}] + 3*In[{2}] + In[{1, 2}] sage: F(i) 7*F[{}] + 3*F[{1}] + 4*F[{2}] + F[{1, 2}] sage: F.coerce_map_from(Out) Generic morphism: From: The subset algebra of {1, 2, 3} over Rational Field in the Out basis To: The subset algebra of {1, 2, 3} over Rational Field in the Fundamental basis
>>> from sage.all import * >>> # needs sage.modules >>> F = A.F(); In = A.In(); Out = A.Out() >>> i = In.an_element(); i In[{}] + 2*In[{1}] + 3*In[{2}] + In[{1, 2}] >>> F(i) 7*F[{}] + 3*F[{1}] + 4*F[{2}] + F[{1, 2}] >>> F.coerce_map_from(Out) Generic morphism: From: The subset algebra of {1, 2, 3} over Rational Field in the Out basis To: The subset algebra of {1, 2, 3} over Rational Field in the Fundamental basis
allowing for mixed arithmetic:
sage: (1 + Out.from_set(1)) * In.from_set(2,3) # needs sage.modules Out[{}] + 2*Out[{1}] + 2*Out[{2}] + 2*Out[{3}] + 2*Out[{1, 2}] + 2*Out[{1, 3}] + 4*Out[{2, 3}] + 4*Out[{1, 2, 3}]
>>> from sage.all import * >>> (Integer(1) + Out.from_set(Integer(1))) * In.from_set(Integer(2),Integer(3)) # needs sage.modules Out[{}] + 2*Out[{1}] + 2*Out[{2}] + 2*Out[{3}] + 2*Out[{1, 2}] + 2*Out[{1, 3}] + 4*Out[{2, 3}] + 4*Out[{1, 2, 3}]
In our example, there are three realizations:
sage: A.realizations() # needs sage.modules [The subset algebra of {1, 2, 3} over Rational Field in the Fundamental basis, The subset algebra of {1, 2, 3} over Rational Field in the In basis, The subset algebra of {1, 2, 3} over Rational Field in the Out basis]
>>> from sage.all import * >>> A.realizations() # needs sage.modules [The subset algebra of {1, 2, 3} over Rational Field in the Fundamental basis, The subset algebra of {1, 2, 3} over Rational Field in the In basis, The subset algebra of {1, 2, 3} over Rational Field in the Out basis]
Instead of manually defining the shorthands
F
,In
, andOut
, as above one can just do:sage: A.inject_shorthands() # needs sage.modules Defining F as shorthand for The subset algebra of {1, 2, 3} over Rational Field in the Fundamental basis Defining In as shorthand for The subset algebra of {1, 2, 3} over Rational Field in the In basis Defining Out as shorthand for The subset algebra of {1, 2, 3} over Rational Field in the Out basis
>>> from sage.all import * >>> A.inject_shorthands() # needs sage.modules Defining F as shorthand for The subset algebra of {1, 2, 3} over Rational Field in the Fundamental basis Defining In as shorthand for The subset algebra of {1, 2, 3} over Rational Field in the In basis Defining Out as shorthand for The subset algebra of {1, 2, 3} over Rational Field in the Out basis
Rationale
Besides some goodies described below, the role of \(A\) is threefold:
To provide, as illustrated above, a single entry point for the algebra as a whole: documentation, access to its properties and different realizations, etc.
To provide a natural location for the initialization of the bases and the coercions between, and other methods that are common to all bases.
To let other objects refer to \(A\) while allowing elements to be represented in any of the realizations.
We now illustrate this second point by defining the polynomial ring with coefficients in \(A\):
sage: P = A['x']; P # needs sage.modules Univariate Polynomial Ring in x over The subset algebra of {1, 2, 3} over Rational Field sage: x = P.gen() # needs sage.modules
>>> from sage.all import * >>> P = A['x']; P # needs sage.modules Univariate Polynomial Ring in x over The subset algebra of {1, 2, 3} over Rational Field >>> x = P.gen() # needs sage.modules
In the following examples, the coefficients turn out to be all represented in the \(F\) basis:
sage: P.one() # needs sage.modules F[{}] sage: (P.an_element() + 1)^2 # needs sage.modules F[{}]*x^2 + 2*F[{}]*x + F[{}]
>>> from sage.all import * >>> P.one() # needs sage.modules F[{}] >>> (P.an_element() + Integer(1))**Integer(2) # needs sage.modules F[{}]*x^2 + 2*F[{}]*x + F[{}]
However we can create a polynomial with mixed coefficients, and compute with it:
sage: p = P([1, In[{1}], Out[{2}] ]); p # needs sage.modules Out[{2}]*x^2 + In[{1}]*x + F[{}] sage: p^2 # needs sage.modules Out[{2}]*x^4 + (-8*In[{}] + 4*In[{1}] + 8*In[{2}] + 4*In[{3}] - 4*In[{1, 2}] - 2*In[{1, 3}] - 4*In[{2, 3}] + 2*In[{1, 2, 3}])*x^3 + (F[{}] + 3*F[{1}] + 2*F[{2}] - 2*F[{1, 2}] - 2*F[{2, 3}] + 2*F[{1, 2, 3}])*x^2 + (2*F[{}] + 2*F[{1}])*x + F[{}]
>>> from sage.all import * >>> p = P([Integer(1), In[{Integer(1)}], Out[{Integer(2)}] ]); p # needs sage.modules Out[{2}]*x^2 + In[{1}]*x + F[{}] >>> p**Integer(2) # needs sage.modules Out[{2}]*x^4 + (-8*In[{}] + 4*In[{1}] + 8*In[{2}] + 4*In[{3}] - 4*In[{1, 2}] - 2*In[{1, 3}] - 4*In[{2, 3}] + 2*In[{1, 2, 3}])*x^3 + (F[{}] + 3*F[{1}] + 2*F[{2}] - 2*F[{1, 2}] - 2*F[{2, 3}] + 2*F[{1, 2, 3}])*x^2 + (2*F[{}] + 2*F[{1}])*x + F[{}]
Note how each coefficient involves a single basis which need not be that of the other coefficients. Which basis is used depends on how coercion happened during mixed arithmetic and needs not be deterministic.
One can easily coerce all coefficient to a given basis with:
sage: p.map_coefficients(In) # needs sage.modules (-4*In[{}] + 2*In[{1}] + 4*In[{2}] + 2*In[{3}] - 2*In[{1, 2}] - In[{1, 3}] - 2*In[{2, 3}] + In[{1, 2, 3}])*x^2 + In[{1}]*x + In[{}]
>>> from sage.all import * >>> p.map_coefficients(In) # needs sage.modules (-4*In[{}] + 2*In[{1}] + 4*In[{2}] + 2*In[{3}] - 2*In[{1, 2}] - In[{1, 3}] - 2*In[{2, 3}] + In[{1, 2, 3}])*x^2 + In[{1}]*x + In[{}]
Alas, the natural notation for constructing such polynomials does not yet work:
sage: In[{1}] * x # needs sage.modules Traceback (most recent call last): ... TypeError: unsupported operand parent(s) for *: 'The subset algebra of {1, 2, 3} over Rational Field in the In basis' and 'Univariate Polynomial Ring in x over The subset algebra of {1, 2, 3} over Rational Field'
>>> from sage.all import * >>> In[{Integer(1)}] * x # needs sage.modules Traceback (most recent call last): ... TypeError: unsupported operand parent(s) for *: 'The subset algebra of {1, 2, 3} over Rational Field in the In basis' and 'Univariate Polynomial Ring in x over The subset algebra of {1, 2, 3} over Rational Field'
The category of realizations of \(A\)
The set of all realizations of \(A\), together with the coercion morphisms is a category (whose class inherits from
Category_realization_of_parent
):sage: A.Realizations() # needs sage.modules Category of realizations of The subset algebra of {1, 2, 3} over Rational Field
>>> from sage.all import * >>> A.Realizations() # needs sage.modules Category of realizations of The subset algebra of {1, 2, 3} over Rational Field
The various parent realizing \(A\) belong to this category:
sage: A.F() in A.Realizations() # needs sage.modules True
>>> from sage.all import * >>> A.F() in A.Realizations() # needs sage.modules True
\(A\) itself is in the category of algebras with realizations:
sage: A in Algebras(QQ).WithRealizations() # needs sage.modules True
>>> from sage.all import * >>> A in Algebras(QQ).WithRealizations() # needs sage.modules True
The (mostly technical)
WithRealizations
categories are the analogs of the*WithSeveralBases
categories in MuPAD-Combinat. They provide support tools for handling the different realizations and the morphisms between them.Typically,
VectorSpaces(QQ).FiniteDimensional().WithRealizations()
will eventually be in charge, whenever a coercion \(\phi: A\mapsto B\) is registered, to register \(\phi^{-1}\) as coercion \(B \mapsto A\) if there is none defined yet. To achieve this,FiniteDimensionalVectorSpaces
would provide a nested classWithRealizations
implementing the appropriate logic.WithRealizations
is aregressive covariant functorial construction
. On our example, this simply means that \(A\) is automatically in the category of rings with realizations (covariance):sage: A in Rings().WithRealizations() # needs sage.modules True
>>> from sage.all import * >>> A in Rings().WithRealizations() # needs sage.modules True
and in the category of algebras (regressiveness):
sage: A in Algebras(QQ) # needs sage.modules True
>>> from sage.all import * >>> A in Algebras(QQ) # needs sage.modules True
Note
For
C
a category,C.WithRealizations()
in fact callssage.categories.with_realizations.WithRealizations(C)
. The later is responsible for building the hierarchy of the categories with realizations in parallel to that of their base categories, optimizing away those categories that do not provide aWithRealizations
nested class. Seesage.categories.covariant_functorial_construction
for the technical details.Note
Design question: currently
WithRealizations
is a regressive construction. That isself.WithRealizations()
is a subcategory ofself
by default:sage: Algebras(QQ).WithRealizations().super_categories() [Category of algebras over Rational Field, Category of monoids with realizations, Category of additive unital additive magmas with realizations]
>>> from sage.all import * >>> Algebras(QQ).WithRealizations().super_categories() [Category of algebras over Rational Field, Category of monoids with realizations, Category of additive unital additive magmas with realizations]
Is this always desirable? For example,
AlgebrasWithBasis(QQ).WithRealizations()
should certainly be a subcategory ofAlgebras(QQ)
, but not ofAlgebrasWithBasis(QQ)
. This is becauseAlgebrasWithBasis(QQ)
is specifying something about the concrete realization.
- additional_structure()[source]#
Return whether
self
defines additional structure.OUTPUT:
self
ifself
defines additional structure andNone
otherwise. This default implementation returnsself
.
A category \(C\) defines additional structure if \(C\)-morphisms shall preserve more structure (e.g. operations) than that specified by the super categories of \(C\). For example, the category of magmas defines additional structure, namely the operation \(*\) that shall be preserved by magma morphisms. On the other hand the category of rings does not define additional structure: a function between two rings that is both a unital magma morphism and a unital additive magma morphism is automatically a ring morphism.
Formally speaking \(C\) defines additional structure, if \(C\) is not a full subcategory of the join of its super categories: the morphisms need to preserve more structure, and thus the homsets are smaller.
By default, a category is considered as defining additional structure, unless it is a category with axiom.
EXAMPLES:
Here are some typical structure categories, with the additional structure they define:
sage: Sets().additional_structure() Category of sets sage: Magmas().additional_structure() # `*` Category of magmas sage: AdditiveMagmas().additional_structure() # `+` Category of additive magmas sage: LeftModules(ZZ).additional_structure() # left multiplication by scalar Category of left modules over Integer Ring sage: Coalgebras(QQ).additional_structure() # coproduct Category of coalgebras over Rational Field sage: Crystals().additional_structure() # crystal operators Category of crystals
>>> from sage.all import * >>> Sets().additional_structure() Category of sets >>> Magmas().additional_structure() # `*` Category of magmas >>> AdditiveMagmas().additional_structure() # `+` Category of additive magmas >>> LeftModules(ZZ).additional_structure() # left multiplication by scalar Category of left modules over Integer Ring >>> Coalgebras(QQ).additional_structure() # coproduct Category of coalgebras over Rational Field >>> Crystals().additional_structure() # crystal operators Category of crystals
On the other hand, the category of semigroups is not a structure category, since its operation \(+\) is already defined by the category of magmas:
sage: Semigroups().additional_structure()
>>> from sage.all import * >>> Semigroups().additional_structure()
Most categories with axiom don’t define additional structure:
sage: Sets().Finite().additional_structure() sage: Rings().Commutative().additional_structure() sage: Modules(QQ).FiniteDimensional().additional_structure() sage: from sage.categories.magmatic_algebras import MagmaticAlgebras sage: MagmaticAlgebras(QQ).Unital().additional_structure()
>>> from sage.all import * >>> Sets().Finite().additional_structure() >>> Rings().Commutative().additional_structure() >>> Modules(QQ).FiniteDimensional().additional_structure() >>> from sage.categories.magmatic_algebras import MagmaticAlgebras >>> MagmaticAlgebras(QQ).Unital().additional_structure()
As of Sage 6.4, the only exceptions are the category of unital magmas or the category of unital additive magmas (both define a unit which shall be preserved by morphisms):
sage: Magmas().Unital().additional_structure() Category of unital magmas sage: AdditiveMagmas().AdditiveUnital().additional_structure() Category of additive unital additive magmas
>>> from sage.all import * >>> Magmas().Unital().additional_structure() Category of unital magmas >>> AdditiveMagmas().AdditiveUnital().additional_structure() Category of additive unital additive magmas
Similarly, functorial construction categories don’t define additional structure, unless the construction is actually defined by their base category. For example, the category of graded modules defines a grading which shall be preserved by morphisms:
sage: Modules(ZZ).Graded().additional_structure() Category of graded modules over Integer Ring
>>> from sage.all import * >>> Modules(ZZ).Graded().additional_structure() Category of graded modules over Integer Ring
On the other hand, the category of graded algebras does not define additional structure; indeed an algebra morphism which is also a module morphism is a graded algebra morphism:
sage: Algebras(ZZ).Graded().additional_structure()
>>> from sage.all import * >>> Algebras(ZZ).Graded().additional_structure()
Similarly, morphisms are requested to preserve the structure given by the following constructions:
sage: Sets().Quotients().additional_structure() Category of quotients of sets sage: Sets().CartesianProducts().additional_structure() Category of Cartesian products of sets sage: Modules(QQ).TensorProducts().additional_structure()
>>> from sage.all import * >>> Sets().Quotients().additional_structure() Category of quotients of sets >>> Sets().CartesianProducts().additional_structure() Category of Cartesian products of sets >>> Modules(QQ).TensorProducts().additional_structure()
This might change, as we are lacking enough data points to guarantee that this was the correct design decision.
Note
In some cases a category defines additional structure, where the structure can be useful to manipulate morphisms but where, in most use cases, we don’t want the morphisms to necessarily preserve it. For example, in the context of finite dimensional vector spaces, having a distinguished basis allows for representing morphisms by matrices; yet considering only morphisms that preserve that distinguished basis would be boring.
In such cases, we might want to eventually have two categories, one where the additional structure is preserved, and one where it’s not necessarily preserved (we would need to find an idiom for this).
At this point, a choice is to be made each time, according to the main use cases. Some of those choices are yet to be settled. For example, should by default:
an euclidean domain morphism preserve euclidean division?
sage: EuclideanDomains().additional_structure() Category of euclidean domains
>>> from sage.all import * >>> EuclideanDomains().additional_structure() Category of euclidean domains
an enumerated set morphism preserve the distinguished enumeration?
sage: EnumeratedSets().additional_structure()
>>> from sage.all import * >>> EnumeratedSets().additional_structure()
a module with basis morphism preserve the distinguished basis?
sage: Modules(QQ).WithBasis().additional_structure()
>>> from sage.all import * >>> Modules(QQ).WithBasis().additional_structure()
See also
This method together with the methods overloading it provide the basic data to determine, for a given category, the super categories that define some structure (see
structure()
), and to test whether a category is a full subcategory of some other category (seeis_full_subcategory()
). For example, the category of Coxeter groups is not full subcategory of the category of groups since morphisms need to preserve the distinguished generators:sage: CoxeterGroups().is_full_subcategory(Groups()) False
>>> from sage.all import * >>> CoxeterGroups().is_full_subcategory(Groups()) False
The support for modeling full subcategories has been introduced in Issue #16340.
- all_super_categories(proper=False)[source]#
Returns the list of all super categories of this category.
INPUT:
proper
– a boolean (default:False
); whether to exclude this category.
Since Issue #11943, the order of super categories is determined by Python’s method resolution order C3 algorithm.
Note
Whenever speed matters, the developers are advised to use instead the lazy attributes
_all_super_categories()
,_all_super_categories_proper()
, or_set_of_super_categories()
, as appropriate. Simply because lazy attributes are much faster than any method.EXAMPLES:
sage: C = Rings(); C Category of rings sage: C.all_super_categories() [Category of rings, Category of rngs, Category of semirings, ... Category of monoids, ... Category of commutative additive groups, ... Category of sets, Category of sets with partial maps, Category of objects] sage: C.all_super_categories(proper = True) [Category of rngs, Category of semirings, ... Category of monoids, ... Category of commutative additive groups, ... Category of sets, Category of sets with partial maps, Category of objects] sage: Sets().all_super_categories() [Category of sets, Category of sets with partial maps, Category of objects] sage: Sets().all_super_categories(proper=True) [Category of sets with partial maps, Category of objects] sage: Sets().all_super_categories() is Sets()._all_super_categories True sage: Sets().all_super_categories(proper=True) is Sets()._all_super_categories_proper True
>>> from sage.all import * >>> C = Rings(); C Category of rings >>> C.all_super_categories() [Category of rings, Category of rngs, Category of semirings, ... Category of monoids, ... Category of commutative additive groups, ... Category of sets, Category of sets with partial maps, Category of objects] >>> C.all_super_categories(proper = True) [Category of rngs, Category of semirings, ... Category of monoids, ... Category of commutative additive groups, ... Category of sets, Category of sets with partial maps, Category of objects] >>> Sets().all_super_categories() [Category of sets, Category of sets with partial maps, Category of objects] >>> Sets().all_super_categories(proper=True) [Category of sets with partial maps, Category of objects] >>> Sets().all_super_categories() is Sets()._all_super_categories True >>> Sets().all_super_categories(proper=True) is Sets()._all_super_categories_proper True
- classmethod an_instance()[source]#
Return an instance of this class.
EXAMPLES:
sage: Rings.an_instance() Category of rings
>>> from sage.all import * >>> Rings.an_instance() Category of rings
Parametrized categories should overload this default implementation to provide appropriate arguments:
sage: Algebras.an_instance() Category of algebras over Rational Field sage: Bimodules.an_instance() # needs sage.rings.real_mpfr Category of bimodules over Rational Field on the left and Real Field with 53 bits of precision on the right sage: AlgebraIdeals.an_instance() Category of algebra ideals in Univariate Polynomial Ring in x over Rational Field
>>> from sage.all import * >>> Algebras.an_instance() Category of algebras over Rational Field >>> Bimodules.an_instance() # needs sage.rings.real_mpfr Category of bimodules over Rational Field on the left and Real Field with 53 bits of precision on the right >>> AlgebraIdeals.an_instance() Category of algebra ideals in Univariate Polynomial Ring in x over Rational Field
- axioms()[source]#
Return the axioms known to be satisfied by all the objects of
self
.Technically, this is the set of all the axioms
A
such that, ifCs
is the category definingA
, thenself
is a subcategory ofCs().A()
. Any additional axiomA
would yield a strict subcategory ofself
, at the very leastself & Cs().A()
whereCs
is the category definingA
.EXAMPLES:
sage: Monoids().axioms() frozenset({'Associative', 'Unital'}) sage: (EnumeratedSets().Infinite() & Sets().Facade()).axioms() frozenset({'Enumerated', 'Facade', 'Infinite'})
>>> from sage.all import * >>> Monoids().axioms() frozenset({'Associative', 'Unital'}) >>> (EnumeratedSets().Infinite() & Sets().Facade()).axioms() frozenset({'Enumerated', 'Facade', 'Infinite'})
- category()[source]#
Return the category of this category. So far, all categories are in the category of objects.
EXAMPLES:
sage: Sets().category() Category of objects sage: VectorSpaces(QQ).category() Category of objects
>>> from sage.all import * >>> Sets().category() Category of objects >>> VectorSpaces(QQ).category() Category of objects
- category_graph()[source]#
Returns the graph of all super categories of this category
EXAMPLES:
sage: C = Algebras(QQ) sage: G = C.category_graph() # needs sage.graphs sage: G.is_directed_acyclic() # needs sage.graphs True
>>> from sage.all import * >>> C = Algebras(QQ) >>> G = C.category_graph() # needs sage.graphs >>> G.is_directed_acyclic() # needs sage.graphs True
The girth of a directed acyclic graph is infinite, however, the girth of the underlying undirected graph is 4 in this case:
sage: Graph(G).girth() # needs sage.graphs 4
>>> from sage.all import * >>> Graph(G).girth() # needs sage.graphs 4
- element_class()[source]#
A common super class for all elements of parents in this category (and its subcategories).
This class contains the methods defined in the nested class
self.ElementMethods
(if it exists), and has as bases the element classes of the super categories ofself
.See also
Category
for details
EXAMPLES:
sage: C = Algebras(QQ).element_class; C <class 'sage.categories.algebras.Algebras.element_class'> sage: type(C) <class 'sage.structure.dynamic_class.DynamicMetaclass'>
>>> from sage.all import * >>> C = Algebras(QQ).element_class; C <class 'sage.categories.algebras.Algebras.element_class'> >>> type(C) <class 'sage.structure.dynamic_class.DynamicMetaclass'>
By Issue #11935, some categories share their element classes. For example, the element class of an algebra only depends on the category of the base. A typical example is the category of algebras over a field versus algebras over a non-field:
sage: Algebras(GF(5)).element_class is Algebras(GF(3)).element_class True sage: Algebras(QQ).element_class is Algebras(ZZ).element_class False sage: Algebras(ZZ['t']).element_class is Algebras(ZZ['t','x']).element_class True
>>> from sage.all import * >>> Algebras(GF(Integer(5))).element_class is Algebras(GF(Integer(3))).element_class True >>> Algebras(QQ).element_class is Algebras(ZZ).element_class False >>> Algebras(ZZ['t']).element_class is Algebras(ZZ['t','x']).element_class True
These classes are constructed with
__slots__ = ()
, so instances may not have a__dict__
:sage: E = FiniteEnumeratedSets().element_class sage: E.__dictoffset__ 0
>>> from sage.all import * >>> E = FiniteEnumeratedSets().element_class >>> E.__dictoffset__ 0
See also
- example(*args, **keywords)[source]#
Returns an object in this category. Most of the time, this is a parent.
This serves three purposes:
Give a typical example to better explain what the category is all about. (and by the way prove that the category is non empty :-) )
Provide a minimal template for implementing other objects in this category
Provide an object on which to test generic code implemented by the category
For all those applications, the implementation of the object shall be kept to a strict minimum. The object is therefore not meant to be used for other applications; most of the time a full featured version is available elsewhere in Sage, and should be used instead.
Technical note: by default
FooBar(...).example()
is constructed by looking upsage.categories.examples.foo_bar.Example
and calling it asExample()
. Extra positional or named parameters are also passed down. For a category over base ring, the base ring is further passed down as an optional argument.Categories are welcome to override this default implementation.
EXAMPLES:
sage: Semigroups().example() An example of a semigroup: the left zero semigroup sage: Monoids().Subquotients().example() NotImplemented
>>> from sage.all import * >>> Semigroups().example() An example of a semigroup: the left zero semigroup >>> Monoids().Subquotients().example() NotImplemented
- full_super_categories()[source]#
Return the immediate full super categories of
self
.See also
Warning
The current implementation selects the full subcategories among the immediate super categories of
self
. This assumes that, if \(C\subset B\subset A\) is a chain of categories and \(C\) is a full subcategory of \(A\), then \(C\) is a full subcategory of \(B\) and \(B\) is a full subcategory of \(A\).This assumption is guaranteed to hold with the current model and implementation of full subcategories in Sage. However, mathematically speaking, this is too restrictive. This indeed prevents the complete modelling of situations where any \(A\) morphism between elements of \(C\) automatically preserves the \(B\) structure. See below for an example.
EXAMPLES:
A semigroup morphism between two finite semigroups is a finite semigroup morphism:
sage: Semigroups().Finite().full_super_categories() [Category of semigroups]
>>> from sage.all import * >>> Semigroups().Finite().full_super_categories() [Category of semigroups]
On the other hand, a semigroup morphism between two monoids is not necessarily a monoid morphism (which must map the unit to the unit):
sage: Monoids().super_categories() [Category of semigroups, Category of unital magmas] sage: Monoids().full_super_categories() [Category of unital magmas]
>>> from sage.all import * >>> Monoids().super_categories() [Category of semigroups, Category of unital magmas] >>> Monoids().full_super_categories() [Category of unital magmas]
Any semigroup morphism between two groups is automatically a monoid morphism (in a group the unit is the unique idempotent, so it has to be mapped to the unit). Yet, due to the limitation of the model advertised above, Sage currently cannot be taught that the category of groups is a full subcategory of the category of semigroups:
sage: Groups().full_super_categories() # todo: not implemented [Category of monoids, Category of semigroups, Category of inverse unital magmas] sage: Groups().full_super_categories() [Category of monoids, Category of inverse unital magmas]
>>> from sage.all import * >>> Groups().full_super_categories() # todo: not implemented [Category of monoids, Category of semigroups, Category of inverse unital magmas] >>> Groups().full_super_categories() [Category of monoids, Category of inverse unital magmas]
- is_abelian()[source]#
Return whether this category is abelian.
An abelian category is a category satisfying:
It has a zero object;
It has all pullbacks and pushouts;
All monomorphisms and epimorphisms are normal.
Equivalently, one can define an increasing sequence of conditions:
A category is pre-additive if it is enriched over abelian groups (all homsets are abelian groups and composition is bilinear);
A pre-additive category is additive if every finite set of objects has a biproduct (we can form direct sums and direct products);
An additive category is pre-abelian if every morphism has both a kernel and a cokernel;
A pre-abelian category is abelian if every monomorphism is the kernel of some morphism and every epimorphism is the cokernel of some morphism.
EXAMPLES:
sage: Modules(ZZ).is_abelian() True sage: FreeModules(ZZ).is_abelian() False sage: FreeModules(QQ).is_abelian() True sage: CommutativeAdditiveGroups().is_abelian() True sage: Semigroups().is_abelian() Traceback (most recent call last): ... NotImplementedError: is_abelian
>>> from sage.all import * >>> Modules(ZZ).is_abelian() True >>> FreeModules(ZZ).is_abelian() False >>> FreeModules(QQ).is_abelian() True >>> CommutativeAdditiveGroups().is_abelian() True >>> Semigroups().is_abelian() Traceback (most recent call last): ... NotImplementedError: is_abelian
- is_full_subcategory(other)[source]#
Return whether
self
is a full subcategory ofother
.A subcategory \(B\) of a category \(A\) is a full subcategory if any \(A\)-morphism between two objects of \(B\) is also a \(B\)-morphism (the reciprocal always holds: any \(B\)-morphism between two objects of \(B\) is an \(A\)-morphism).
This is computed by testing whether
self
is a subcategory ofother
and whether they have the same structure, as determined bystructure()
from the result ofadditional_structure()
on the super categories.Warning
A positive answer is guaranteed to be mathematically correct. A negative answer may mean that Sage has not been taught enough information (or can not yet within the current model) to derive this information. See
full_super_categories()
for a discussion.See also
EXAMPLES:
sage: Magmas().Associative().is_full_subcategory(Magmas()) True sage: Magmas().Unital().is_full_subcategory(Magmas()) False sage: Rings().is_full_subcategory(Magmas().Unital() & AdditiveMagmas().AdditiveUnital()) True
>>> from sage.all import * >>> Magmas().Associative().is_full_subcategory(Magmas()) True >>> Magmas().Unital().is_full_subcategory(Magmas()) False >>> Rings().is_full_subcategory(Magmas().Unital() & AdditiveMagmas().AdditiveUnital()) True
Here are two typical examples of false negatives:
sage: Groups().is_full_subcategory(Semigroups()) False sage: Groups().is_full_subcategory(Semigroups()) # todo: not implemented True sage: Fields().is_full_subcategory(Rings()) False sage: Fields().is_full_subcategory(Rings()) # todo: not implemented True
>>> from sage.all import * >>> Groups().is_full_subcategory(Semigroups()) False >>> Groups().is_full_subcategory(Semigroups()) # todo: not implemented True >>> Fields().is_full_subcategory(Rings()) False >>> Fields().is_full_subcategory(Rings()) # todo: not implemented True
Todo
The latter is a consequence of
EuclideanDomains
currently being a structure category. Is this what we want?sage: EuclideanDomains().is_full_subcategory(Rings()) False
>>> from sage.all import * >>> EuclideanDomains().is_full_subcategory(Rings()) False
- is_subcategory(c)[source]#
Returns True if self is naturally embedded as a subcategory of c.
EXAMPLES:
sage: AbGrps = CommutativeAdditiveGroups() sage: Rings().is_subcategory(AbGrps) True sage: AbGrps.is_subcategory(Rings()) False
>>> from sage.all import * >>> AbGrps = CommutativeAdditiveGroups() >>> Rings().is_subcategory(AbGrps) True >>> AbGrps.is_subcategory(Rings()) False
The
is_subcategory
function takes into account the base.sage: M3 = VectorSpaces(FiniteField(3)) sage: M9 = VectorSpaces(FiniteField(9, 'a')) # needs sage.rings.finite_rings sage: M3.is_subcategory(M9) # needs sage.rings.finite_rings False
>>> from sage.all import * >>> M3 = VectorSpaces(FiniteField(Integer(3))) >>> M9 = VectorSpaces(FiniteField(Integer(9), 'a')) # needs sage.rings.finite_rings >>> M3.is_subcategory(M9) # needs sage.rings.finite_rings False
Join categories are properly handled:
sage: CatJ = Category.join((CommutativeAdditiveGroups(), Semigroups())) sage: Rings().is_subcategory(CatJ) True
>>> from sage.all import * >>> CatJ = Category.join((CommutativeAdditiveGroups(), Semigroups())) >>> Rings().is_subcategory(CatJ) True
sage: V3 = VectorSpaces(FiniteField(3)) sage: POSet = PartiallyOrderedSets() sage: PoV3 = Category.join((V3, POSet)) sage: A3 = AlgebrasWithBasis(FiniteField(3)) sage: PoA3 = Category.join((A3, POSet)) sage: PoA3.is_subcategory(PoV3) True sage: PoV3.is_subcategory(PoV3) True sage: PoV3.is_subcategory(PoA3) False
>>> from sage.all import * >>> V3 = VectorSpaces(FiniteField(Integer(3))) >>> POSet = PartiallyOrderedSets() >>> PoV3 = Category.join((V3, POSet)) >>> A3 = AlgebrasWithBasis(FiniteField(Integer(3))) >>> PoA3 = Category.join((A3, POSet)) >>> PoA3.is_subcategory(PoV3) True >>> PoV3.is_subcategory(PoV3) True >>> PoV3.is_subcategory(PoA3) False
- static join(categories, as_list=False, ignore_axioms=(), axioms=())[source]#
Return the join of the input categories in the lattice of categories.
At the level of objects and morphisms, this operation corresponds to intersection: the objects and morphisms of a join category are those that belong to all its super categories.
INPUT:
categories
– a list (or iterable) of categoriesas_list
– a boolean (default:False
); whether the result should be returned as a listaxioms
– a tuple of strings; the names of some supplementary axioms
See also
__and__()
for a shortcutEXAMPLES:
sage: J = Category.join((Groups(), CommutativeAdditiveMonoids())); J Join of Category of groups and Category of commutative additive monoids sage: J.super_categories() [Category of groups, Category of commutative additive monoids] sage: J.all_super_categories(proper=True) [Category of groups, ..., Category of magmas, Category of commutative additive monoids, ..., Category of additive magmas, Category of sets, ...]
>>> from sage.all import * >>> J = Category.join((Groups(), CommutativeAdditiveMonoids())); J Join of Category of groups and Category of commutative additive monoids >>> J.super_categories() [Category of groups, Category of commutative additive monoids] >>> J.all_super_categories(proper=True) [Category of groups, ..., Category of magmas, Category of commutative additive monoids, ..., Category of additive magmas, Category of sets, ...]
As a short hand, one can use:
sage: Groups() & CommutativeAdditiveMonoids() Join of Category of groups and Category of commutative additive monoids
>>> from sage.all import * >>> Groups() & CommutativeAdditiveMonoids() Join of Category of groups and Category of commutative additive monoids
This is a commutative and associative operation:
sage: Groups() & Posets() Join of Category of groups and Category of posets sage: Posets() & Groups() Join of Category of groups and Category of posets sage: Groups() & (CommutativeAdditiveMonoids() & Posets()) Join of Category of groups and Category of commutative additive monoids and Category of posets sage: (Groups() & CommutativeAdditiveMonoids()) & Posets() Join of Category of groups and Category of commutative additive monoids and Category of posets
>>> from sage.all import * >>> Groups() & Posets() Join of Category of groups and Category of posets >>> Posets() & Groups() Join of Category of groups and Category of posets >>> Groups() & (CommutativeAdditiveMonoids() & Posets()) Join of Category of groups and Category of commutative additive monoids and Category of posets >>> (Groups() & CommutativeAdditiveMonoids()) & Posets() Join of Category of groups and Category of commutative additive monoids and Category of posets
The join of a single category is the category itself:
sage: Category.join([Monoids()]) Category of monoids
>>> from sage.all import * >>> Category.join([Monoids()]) Category of monoids
Similarly, the join of several mutually comparable categories is the smallest one:
sage: Category.join((Sets(), Rings(), Monoids())) Category of rings
>>> from sage.all import * >>> Category.join((Sets(), Rings(), Monoids())) Category of rings
In particular, the unit is the top category
Objects
:sage: Groups() & Objects() Category of groups
>>> from sage.all import * >>> Groups() & Objects() Category of groups
If the optional parameter
as_list
isTrue
, this returns the super categories of the join as a list, without constructing the join category itself:sage: Category.join((Groups(), CommutativeAdditiveMonoids()), as_list=True) [Category of groups, Category of commutative additive monoids] sage: Category.join((Sets(), Rings(), Monoids()), as_list=True) [Category of rings] sage: Category.join((Modules(ZZ), FiniteFields()), as_list=True) [Category of finite enumerated fields, Category of modules over Integer Ring] sage: Category.join([], as_list=True) [] sage: Category.join([Groups()], as_list=True) [Category of groups] sage: Category.join([Groups() & Posets()], as_list=True) [Category of groups, Category of posets]
>>> from sage.all import * >>> Category.join((Groups(), CommutativeAdditiveMonoids()), as_list=True) [Category of groups, Category of commutative additive monoids] >>> Category.join((Sets(), Rings(), Monoids()), as_list=True) [Category of rings] >>> Category.join((Modules(ZZ), FiniteFields()), as_list=True) [Category of finite enumerated fields, Category of modules over Integer Ring] >>> Category.join([], as_list=True) [] >>> Category.join([Groups()], as_list=True) [Category of groups] >>> Category.join([Groups() & Posets()], as_list=True) [Category of groups, Category of posets]
Support for axiom categories (TODO: put here meaningful examples):
sage: Sets().Facade() & Sets().Infinite() Category of facade infinite sets sage: Magmas().Infinite() & Sets().Facade() Category of facade infinite magmas sage: FiniteSets() & Monoids() Category of finite monoids sage: Rings().Commutative() & Sets().Finite() Category of finite commutative rings
>>> from sage.all import * >>> Sets().Facade() & Sets().Infinite() Category of facade infinite sets >>> Magmas().Infinite() & Sets().Facade() Category of facade infinite magmas >>> FiniteSets() & Monoids() Category of finite monoids >>> Rings().Commutative() & Sets().Finite() Category of finite commutative rings
Note that several of the above examples are actually join categories; they are just nicely displayed:
sage: AlgebrasWithBasis(QQ) & FiniteSets().Algebras(QQ) Join of Category of finite dimensional algebras with basis over Rational Field and Category of finite set algebras over Rational Field sage: UniqueFactorizationDomains() & Algebras(QQ) Join of Category of unique factorization domains and Category of commutative algebras over Rational Field
>>> from sage.all import * >>> AlgebrasWithBasis(QQ) & FiniteSets().Algebras(QQ) Join of Category of finite dimensional algebras with basis over Rational Field and Category of finite set algebras over Rational Field >>> UniqueFactorizationDomains() & Algebras(QQ) Join of Category of unique factorization domains and Category of commutative algebras over Rational Field
- static meet(categories)[source]#
Returns the meet of a list of categories
INPUT:
categories
– a non empty list (or iterable) of categories
See also
__or__()
for a shortcutEXAMPLES:
sage: Category.meet([Algebras(ZZ), Algebras(QQ), Groups()]) Category of monoids
>>> from sage.all import * >>> Category.meet([Algebras(ZZ), Algebras(QQ), Groups()]) Category of monoids
That meet of an empty list should be a category which is a subcategory of all categories, which does not make practical sense:
sage: Category.meet([]) Traceback (most recent call last): ... ValueError: The meet of an empty list of categories is not implemented
>>> from sage.all import * >>> Category.meet([]) Traceback (most recent call last): ... ValueError: The meet of an empty list of categories is not implemented
- morphism_class()[source]#
A common super class for all morphisms between parents in this category (and its subcategories).
This class contains the methods defined in the nested class
self.MorphismMethods
(if it exists), and has as bases the morphism classes of the super categories ofself
.See also
Category
for details
EXAMPLES:
sage: C = Algebras(QQ).morphism_class; C <class 'sage.categories.algebras.Algebras.morphism_class'> sage: type(C) <class 'sage.structure.dynamic_class.DynamicMetaclass'>
>>> from sage.all import * >>> C = Algebras(QQ).morphism_class; C <class 'sage.categories.algebras.Algebras.morphism_class'> >>> type(C) <class 'sage.structure.dynamic_class.DynamicMetaclass'>
- or_subcategory(category=None, join=False)[source]#
Return
category
orself
ifcategory
isNone
.INPUT:
category
– a sub category ofself
, tuple/list thereof, orNone
join
– a boolean (default:False
)
OUTPUT:
a category
EXAMPLES:
sage: Monoids().or_subcategory(Groups()) Category of groups sage: Monoids().or_subcategory(None) Category of monoids
>>> from sage.all import * >>> Monoids().or_subcategory(Groups()) Category of groups >>> Monoids().or_subcategory(None) Category of monoids
If category is a list/tuple, then a join category is returned:
sage: Monoids().or_subcategory((CommutativeAdditiveMonoids(), Groups())) Join of Category of groups and Category of commutative additive monoids
>>> from sage.all import * >>> Monoids().or_subcategory((CommutativeAdditiveMonoids(), Groups())) Join of Category of groups and Category of commutative additive monoids
If
join
isFalse
, an error if raised if category is not a subcategory ofself
:sage: Monoids().or_subcategory(EnumeratedSets()) Traceback (most recent call last): ... ValueError: Subcategory of `Category of monoids` required; got `Category of enumerated sets`
>>> from sage.all import * >>> Monoids().or_subcategory(EnumeratedSets()) Traceback (most recent call last): ... ValueError: Subcategory of `Category of monoids` required; got `Category of enumerated sets`
Otherwise, the two categories are joined together:
sage: Monoids().or_subcategory(EnumeratedSets(), join=True) Category of enumerated monoids
>>> from sage.all import * >>> Monoids().or_subcategory(EnumeratedSets(), join=True) Category of enumerated monoids
- parent_class()[source]#
A common super class for all parents in this category (and its subcategories).
This class contains the methods defined in the nested class
self.ParentMethods
(if it exists), and has as bases the parent classes of the super categories ofself
.See also
Category
for details
EXAMPLES:
sage: C = Algebras(QQ).parent_class; C <class 'sage.categories.algebras.Algebras.parent_class'> sage: type(C) <class 'sage.structure.dynamic_class.DynamicMetaclass'>
>>> from sage.all import * >>> C = Algebras(QQ).parent_class; C <class 'sage.categories.algebras.Algebras.parent_class'> >>> type(C) <class 'sage.structure.dynamic_class.DynamicMetaclass'>
By Issue #11935, some categories share their parent classes. For example, the parent class of an algebra only depends on the category of the base ring. A typical example is the category of algebras over a finite field versus algebras over a non-field:
sage: Algebras(GF(7)).parent_class is Algebras(GF(5)).parent_class True sage: Algebras(QQ).parent_class is Algebras(ZZ).parent_class False sage: Algebras(ZZ['t']).parent_class is Algebras(ZZ['t','x']).parent_class True
>>> from sage.all import * >>> Algebras(GF(Integer(7))).parent_class is Algebras(GF(Integer(5))).parent_class True >>> Algebras(QQ).parent_class is Algebras(ZZ).parent_class False >>> Algebras(ZZ['t']).parent_class is Algebras(ZZ['t','x']).parent_class True
See
CategoryWithParameters
for an abstract base class for categories that depend on parameters, even though the parent and element classes only depend on the parent or element classes of its super categories. It is used inBimodules
,Category_over_base
andsage.categories.category.JoinCategory
.
- required_methods()[source]#
Returns the methods that are required and optional for parents in this category and their elements.
EXAMPLES:
sage: Algebras(QQ).required_methods() {'element': {'optional': ['_add_', '_mul_'], 'required': ['__bool__']}, 'parent': {'optional': ['algebra_generators'], 'required': ['__contains__']}}
>>> from sage.all import * >>> Algebras(QQ).required_methods() {'element': {'optional': ['_add_', '_mul_'], 'required': ['__bool__']}, 'parent': {'optional': ['algebra_generators'], 'required': ['__contains__']}}
- structure()[source]#
Return the structure
self
is endowed with.This method returns the structure that morphisms in this category shall be preserving. For example, it tells that a ring is a set endowed with a structure of both a unital magma and an additive unital magma which satisfies some further axioms. In other words, a ring morphism is a function that preserves the unital magma and additive unital magma structure.
In practice, this returns the collection of all the super categories of
self
that define some additional structure, as a frozen set.EXAMPLES:
sage: Objects().structure() frozenset() sage: def structure(C): ....: return Category._sort(C.structure()) sage: structure(Sets()) (Category of sets, Category of sets with partial maps) sage: structure(Magmas()) (Category of magmas, Category of sets, Category of sets with partial maps)
>>> from sage.all import * >>> Objects().structure() frozenset() >>> def structure(C): ... return Category._sort(C.structure()) >>> structure(Sets()) (Category of sets, Category of sets with partial maps) >>> structure(Magmas()) (Category of magmas, Category of sets, Category of sets with partial maps)
In the following example, we only list the smallest structure categories to get a more readable output:
sage: def structure(C): ....: return Category._sort_uniq(C.structure()) sage: structure(Magmas()) (Category of magmas,) sage: structure(Rings()) (Category of unital magmas, Category of additive unital additive magmas) sage: structure(Fields()) (Category of euclidean domains, Category of noetherian rings) sage: structure(Algebras(QQ)) (Category of unital magmas, Category of right modules over Rational Field, Category of left modules over Rational Field) sage: structure(HopfAlgebras(QQ).Graded().WithBasis().Connected()) (Category of Hopf algebras over Rational Field, Category of graded modules over Rational Field)
>>> from sage.all import * >>> def structure(C): ... return Category._sort_uniq(C.structure()) >>> structure(Magmas()) (Category of magmas,) >>> structure(Rings()) (Category of unital magmas, Category of additive unital additive magmas) >>> structure(Fields()) (Category of euclidean domains, Category of noetherian rings) >>> structure(Algebras(QQ)) (Category of unital magmas, Category of right modules over Rational Field, Category of left modules over Rational Field) >>> structure(HopfAlgebras(QQ).Graded().WithBasis().Connected()) (Category of Hopf algebras over Rational Field, Category of graded modules over Rational Field)
This method is used in
is_full_subcategory()
for deciding whether a category is a full subcategory of some other category, and for documentation purposes. It is computed recursively from the result ofadditional_structure()
on the super categories ofself
.
- subcategory_class()[source]#
A common superclass for all subcategories of this category (including this one).
This class derives from
D.subcategory_class
for each super category \(D\) ofself
, and includes all the methods from the nested classself.SubcategoryMethods
, if it exists.EXAMPLES:
sage: cls = Rings().subcategory_class; cls <class 'sage.categories.rings.Rings.subcategory_class'> sage: type(cls) <class 'sage.structure.dynamic_class.DynamicMetaclass'>
>>> from sage.all import * >>> cls = Rings().subcategory_class; cls <class 'sage.categories.rings.Rings.subcategory_class'> >>> type(cls) <class 'sage.structure.dynamic_class.DynamicMetaclass'>
Rings()
is an instance of this class, as well as all its subcategories:sage: isinstance(Rings(), cls) True sage: isinstance(AlgebrasWithBasis(QQ), cls) True
>>> from sage.all import * >>> isinstance(Rings(), cls) True >>> isinstance(AlgebrasWithBasis(QQ), cls) True
- super_categories()[source]#
Return the immediate super categories of
self
.OUTPUT:
a duplicate-free list of categories.
Every category should implement this method.
EXAMPLES:
sage: Groups().super_categories() [Category of monoids, Category of inverse unital magmas] sage: Objects().super_categories() []
>>> from sage.all import * >>> Groups().super_categories() [Category of monoids, Category of inverse unital magmas] >>> Objects().super_categories() []
Note
Since Issue #10963, the order of the categories in the result is irrelevant. For details, see On the order of super categories.
Note
Whenever speed matters, developers are advised to use the lazy attribute
_super_categories()
instead of calling this method.
- class sage.categories.category.CategoryWithParameters[source]#
Bases:
Category
A parametrized category whose parent/element classes depend only on its super categories.
Many categories in Sage are parametrized, like
C = Algebras(K)
which takes a base ring as parameter. In many cases, however, the operations provided byC
in the parent class and element class depend only on the super categories ofC
. For example, the vector space operations are provided if and only ifK
is a field, sinceVectorSpaces(K)
is a super category ofC
only in that case. In such cases, and as an optimization (see Issue #11935), we want to use the same parent and element class for all fields. This is the purpose of this abstract class.Currently,
JoinCategory
,Category_over_base
andBimodules
inherit from this class.EXAMPLES:
sage: C1 = Algebras(GF(5)) sage: C2 = Algebras(GF(3)) sage: C3 = Algebras(ZZ) sage: from sage.categories.category import CategoryWithParameters sage: isinstance(C1, CategoryWithParameters) True sage: C1.parent_class is C2.parent_class True sage: C1.parent_class is C3.parent_class False
>>> from sage.all import * >>> C1 = Algebras(GF(Integer(5))) >>> C2 = Algebras(GF(Integer(3))) >>> C3 = Algebras(ZZ) >>> from sage.categories.category import CategoryWithParameters >>> isinstance(C1, CategoryWithParameters) True >>> C1.parent_class is C2.parent_class True >>> C1.parent_class is C3.parent_class False
- Category._make_named_class(name, method_provider, cache=False, picklable=True)#
Construction of the parent/element/… class of
self
.INPUT:
name
– a string; the name of the class as an attribute ofself
. E.g. “parent_class”method_provider
– a string; the name of an attribute ofself
that provides methods for the new class (in addition to those coming from the super categories). E.g. “ParentMethods”cache
– a boolean orignore_reduction
(default:False
) (passed down to dynamic_class; for internal use only)picklable
– a boolean (default:True
)
ASSUMPTION:
It is assumed that this method is only called from a lazy attribute whose name coincides with the given
name
.OUTPUT:
A dynamic class with bases given by the corresponding named classes of
self
’s super_categories, and methods taken from the classgetattr(self,method_provider)
.Note
In this default implementation, the reduction data of the named class makes it depend on
self
. Since the result is going to be stored in a lazy attribute ofself
anyway, we may as well disable the caching indynamic_class
(hence the default valuecache=False
).CategoryWithParameters
overrides this method so that the same parent/element/… classes can be shared between closely related categories.The bases of the named class may also contain the named classes of some indirect super categories, according to
_super_categories_for_classes()
. This is to guarantee that Python will build consistent method resolution orders. For background, seesage.misc.c3_controlled
.
See also
CategoryWithParameters._make_named_class()
EXAMPLES:
sage: PC = Rings()._make_named_class("parent_class", "ParentMethods"); PC <class 'sage.categories.rings.Rings.parent_class'> sage: type(PC) <class 'sage.structure.dynamic_class.DynamicMetaclass'> sage: PC.__bases__ (<class 'sage.categories.rngs.Rngs.parent_class'>, <class 'sage.categories.semirings.Semirings.parent_class'>)
>>> from sage.all import * >>> PC = Rings()._make_named_class("parent_class", "ParentMethods"); PC <class 'sage.categories.rings.Rings.parent_class'> >>> type(PC) <class 'sage.structure.dynamic_class.DynamicMetaclass'> >>> PC.__bases__ (<class 'sage.categories.rngs.Rngs.parent_class'>, <class 'sage.categories.semirings.Semirings.parent_class'>)
Note that, by default, the result is not cached:
sage: PC is Rings()._make_named_class("parent_class", "ParentMethods") False
>>> from sage.all import * >>> PC is Rings()._make_named_class("parent_class", "ParentMethods") False
Indeed this method is only meant to construct lazy attributes like
parent_class
which already handle this caching:sage: Rings().parent_class <class 'sage.categories.rings.Rings.parent_class'>
>>> from sage.all import * >>> Rings().parent_class <class 'sage.categories.rings.Rings.parent_class'>
Reduction for pickling also assumes the existence of this lazy attribute:
sage: PC._reduction (<built-in function getattr>, (Category of rings, 'parent_class')) sage: loads(dumps(PC)) is Rings().parent_class True
>>> from sage.all import * >>> PC._reduction (<built-in function getattr>, (Category of rings, 'parent_class')) >>> loads(dumps(PC)) is Rings().parent_class True
- class sage.categories.category.JoinCategory(super_categories, **kwds)[source]#
Bases:
CategoryWithParameters
A class for joins of several categories. Do not use directly; see Category.join instead.
EXAMPLES:
sage: from sage.categories.category import JoinCategory sage: J = JoinCategory((Groups(), CommutativeAdditiveMonoids())); J Join of Category of groups and Category of commutative additive monoids sage: J.super_categories() [Category of groups, Category of commutative additive monoids] sage: J.all_super_categories(proper=True) [Category of groups, ..., Category of magmas, Category of commutative additive monoids, ..., Category of additive magmas, Category of sets, Category of sets with partial maps, Category of objects]
>>> from sage.all import * >>> from sage.categories.category import JoinCategory >>> J = JoinCategory((Groups(), CommutativeAdditiveMonoids())); J Join of Category of groups and Category of commutative additive monoids >>> J.super_categories() [Category of groups, Category of commutative additive monoids] >>> J.all_super_categories(proper=True) [Category of groups, ..., Category of magmas, Category of commutative additive monoids, ..., Category of additive magmas, Category of sets, Category of sets with partial maps, Category of objects]
By Issue #11935, join categories and categories over base rings inherit from
CategoryWithParameters
. This allows for sharing parent and element classes between similar categories. For example, since group algebras belong to a join category and since the underlying implementation is the same for all finite fields, we have:sage: # needs sage.groups sage.rings.finite_rings sage: G = SymmetricGroup(10) sage: A3 = G.algebra(GF(3)) sage: A5 = G.algebra(GF(5)) sage: type(A3.category()) <class 'sage.categories.category.JoinCategory_with_category'> sage: type(A3) is type(A5) True
>>> from sage.all import * >>> # needs sage.groups sage.rings.finite_rings >>> G = SymmetricGroup(Integer(10)) >>> A3 = G.algebra(GF(Integer(3))) >>> A5 = G.algebra(GF(Integer(5))) >>> type(A3.category()) <class 'sage.categories.category.JoinCategory_with_category'> >>> type(A3) is type(A5) True
- Category._repr_object_names()#
Return the name of the objects of this category.
EXAMPLES:
sage: FiniteGroups()._repr_object_names() 'finite groups' sage: AlgebrasWithBasis(QQ)._repr_object_names() 'algebras with basis over Rational Field'
>>> from sage.all import * >>> FiniteGroups()._repr_object_names() 'finite groups' >>> AlgebrasWithBasis(QQ)._repr_object_names() 'algebras with basis over Rational Field'
- Category._repr_()#
Return the print representation of this category.
EXAMPLES:
sage: Sets() # indirect doctest Category of sets
>>> from sage.all import * >>> Sets() # indirect doctest Category of sets
- Category._without_axioms(named=False)#
Return the category without the axioms that have been added to create it.
INPUT:
named
– a boolean (default:False
)
Todo
Improve this explanation.
If
named
isTrue
, then this stops at the first category that has an explicit name of its own. Seecategory_with_axiom.CategoryWithAxiom._without_axioms()
EXAMPLES:
sage: Sets()._without_axioms() Category of sets sage: Semigroups()._without_axioms() Category of magmas sage: Algebras(QQ).Commutative().WithBasis()._without_axioms() Category of magmatic algebras over Rational Field sage: Algebras(QQ).Commutative().WithBasis()._without_axioms(named=True) Category of algebras over Rational Field
>>> from sage.all import * >>> Sets()._without_axioms() Category of sets >>> Semigroups()._without_axioms() Category of magmas >>> Algebras(QQ).Commutative().WithBasis()._without_axioms() Category of magmatic algebras over Rational Field >>> Algebras(QQ).Commutative().WithBasis()._without_axioms(named=True) Category of algebras over Rational Field
- additional_structure()[source]#
Return
None
.Indeed, a join category defines no additional structure.
See also
EXAMPLES:
sage: Modules(ZZ).additional_structure()
>>> from sage.all import * >>> Modules(ZZ).additional_structure()
- is_subcategory(C)[source]#
Check whether this join category is subcategory of another category
C
.EXAMPLES:
sage: Category.join([Rings(),Modules(QQ)]).is_subcategory(Category.join([Rngs(),Bimodules(QQ,QQ)])) True
>>> from sage.all import * >>> Category.join([Rings(),Modules(QQ)]).is_subcategory(Category.join([Rngs(),Bimodules(QQ,QQ)])) True
- super_categories()[source]#
Returns the immediate super categories, as per
Category.super_categories()
.EXAMPLES:
sage: from sage.categories.category import JoinCategory sage: JoinCategory((Semigroups(), FiniteEnumeratedSets())).super_categories() [Category of semigroups, Category of finite enumerated sets]
>>> from sage.all import * >>> from sage.categories.category import JoinCategory >>> JoinCategory((Semigroups(), FiniteEnumeratedSets())).super_categories() [Category of semigroups, Category of finite enumerated sets]
- sage.categories.category.category_graph(categories=None)[source]#
Return the graph of the categories in Sage.
INPUT:
categories
– a list (or iterable) of categories
If
categories
is specified, then the graph contains the mentioned categories together with all their super categories. Otherwise the graph contains (an instance of) each category insage.categories.all
(e.g.Algebras(QQ)
for algebras).For readability, the names of the category are shortened.
Todo
Further remove the base ring (see also Issue #15801).
EXAMPLES:
sage: G = sage.categories.category.category_graph(categories=[Groups()]) # needs sage.graphs sage: G.vertices(sort=True) # needs sage.graphs ['groups', 'inverse unital magmas', 'magmas', 'monoids', 'objects', 'semigroups', 'sets', 'sets with partial maps', 'unital magmas'] sage: G.plot() # needs sage.graphs sage.plot Graphics object consisting of 20 graphics primitives sage: sage.categories.category.category_graph().plot() # needs sage.graphs sage.groups sage.plot Graphics object consisting of ... graphics primitives
>>> from sage.all import * >>> G = sage.categories.category.category_graph(categories=[Groups()]) # needs sage.graphs >>> G.vertices(sort=True) # needs sage.graphs ['groups', 'inverse unital magmas', 'magmas', 'monoids', 'objects', 'semigroups', 'sets', 'sets with partial maps', 'unital magmas'] >>> G.plot() # needs sage.graphs sage.plot Graphics object consisting of 20 graphics primitives >>> sage.categories.category.category_graph().plot() # needs sage.graphs sage.groups sage.plot Graphics object consisting of ... graphics primitives
- sage.categories.category.category_sample()[source]#
Return a sample of categories.
It is constructed by looking for all concrete category classes declared in
sage.categories.all
, callingCategory.an_instance()
on those and taking all their super categories.EXAMPLES:
sage: from sage.categories.category import category_sample sage: sorted(category_sample(), key=str) # needs sage.groups [Category of Coxeter groups, Category of Dedekind domains, Category of G-sets for Symmetric group of order 8! as a permutation group, Category of Hecke modules over Rational Field, Category of Hopf algebras over Rational Field, Category of Hopf algebras with basis over Rational Field, Category of Jacobians over Rational Field, Category of Lie algebras over Rational Field, Category of Weyl groups, Category of abelian varieties over Rational Field, Category of additive magmas, ..., Category of fields, ..., Category of graded Hopf algebras with basis over Rational Field, ..., Category of modular abelian varieties over Rational Field, ..., Category of simplicial complexes, ..., Category of vector spaces over Rational Field, ...
>>> from sage.all import * >>> from sage.categories.category import category_sample >>> sorted(category_sample(), key=str) # needs sage.groups [Category of Coxeter groups, Category of Dedekind domains, Category of G-sets for Symmetric group of order 8! as a permutation group, Category of Hecke modules over Rational Field, Category of Hopf algebras over Rational Field, Category of Hopf algebras with basis over Rational Field, Category of Jacobians over Rational Field, Category of Lie algebras over Rational Field, Category of Weyl groups, Category of abelian varieties over Rational Field, Category of additive magmas, ..., Category of fields, ..., Category of graded Hopf algebras with basis over Rational Field, ..., Category of modular abelian varieties over Rational Field, ..., Category of simplicial complexes, ..., Category of vector spaces over Rational Field, ...
- sage.categories.category.is_Category(x)[source]#
Returns True if x is a category.
EXAMPLES:
sage: sage.categories.category.is_Category(CommutativeAdditiveSemigroups()) doctest:warning... DeprecationWarning: the function is_Category is deprecated; use 'isinstance(..., Category)' instead See https://github.com/sagemath/sage/issues/37922 for details. True sage: sage.categories.category.is_Category(ZZ) False
>>> from sage.all import * >>> sage.categories.category.is_Category(CommutativeAdditiveSemigroups()) doctest:warning... DeprecationWarning: the function is_Category is deprecated; use 'isinstance(..., Category)' instead See https://github.com/sagemath/sage/issues/37922 for details. True >>> sage.categories.category.is_Category(ZZ) False