The aim of this tutorial is to explain how one can benefit from Sage’s
category framework and coercion model when implementing new algebraic
structures. It is based on a worksheet created in 2011.

We illustrate the concepts of Sage’s category framework and coercion model by
means of a detailed example, namely a toy implementation of fraction fields.
The code is developed step by step, so that the reader can focus on one detail
in each part of this tutorial. The complete code can be found in the appendix.

For using Sage’s coercion system, it is essential to work with sub–classes
of sage.structure.parent.Parent or
sage.structure.element.Element, respectively. They provide default
implementations of many “magical” double-underscore Python methods, which
must not be overridden. Instead, the actual implementation should be in
single underscore methods, such as _add_ or _mul_.

Turn your parent structure into an object of a category

Declare the category during initialisation—Your parent structure will
inherit further useful methods and consistency tests.

Provide your parent structure with an element class

Assign to it an attribute called Element—The elements will inherit
further useful methods from the category. In addition, some basic
conversions will immediately work.

Each method should be documented and provide a doc test (we are not giving
examples here). In addition, any method defined for the objects or elements
of a category should be supported by a test method, that is executed when
running the test suite.

Sage provides appropriate sub–classes of
Parent and
Element for a variety of more concrete
algebraic structures, such as groups, rings, or fields, and of their
elements. But some old stuff in Sage doesn’t use it. Volunteers for
refactoring are welcome!

Python uses double–underscore methods for arithmetic methods and string
representations. Sage’s base classes often have a default implementation,
and it is requested to implement SINGLE underscore methods _repr_, and
similarly _add_, _mul_ etc.

You are encouraged to make your parent “unique”. That’s to say, parents
should only evaluate equal if they are identical. Sage provides frameworks
to create unique parents. We use here the most easy one: Inheriting from the
class sage.structure.unique_representation.UniqueRepresentation is
enough. Making parents unique can be quite important for an efficient
implementation, because the repeated creation of “the same” parent would
take a lot of time.

Fraction fields are only defined for integral domains. Hence, we raise an
error if the given ring does not belong to the category of integral
domains. This is our first use case of categories.

Last, we add a method that returns the characteristic of the field. We don’t
go into details, but some automated tests that we study below implicitly
rely on this method.

We see that our basic implementation correctly refuses a ring that is not an
integral domain:

Inheritance from UniqueRepresentation
automatically provides our class with pickling, preserving the unique
parent condition. If we had defined the class in some external module or
in an interactive session, pickling would work immediately.

However, for making the following example work in Sage’s doctesting
framework, we need to assign our class as an attribute of the __main__
module, so that the class can be looked up during unpickling.

In the following sections, we will successively add or change details of
MyFrac. Rather than giving a full class definition in each step, we
define new versions of MyFrac by inheriting from the previously
defined version of MyFrac. We believe this will help the reader to
focus on the single detail that is relevant in each section.

Our toy implementation of fraction field elements is based on the following
considerations:

A fraction field element is defined by numerator and denominator, which both
need to be elements of the base. There should be methods returning numerator
resp. denominator.

The denominator must not be zero, and (provided that the base is an ordered
ring) we can make it non-negative, without loss of generality. By default,
the denominator is one.

The string representation is returned by the single–underscore method
_repr_. In order to make our fraction field elements distinguishable
from those already present in Sage, we use a different string representation.

Arithmetic is implemented in single–underscore method _add_, _mul_,
etc. We do not override the default double underscore __add__, __mul__,
since otherwise, we could not use Sage’s coercion model.

Comparisons can be implemented using _richcmp_ or
_cmp_. This automatically makes the relational operators like
== and < work. Beware: in these methods, calling the
Python2-only cmp function should be avoided for compatibility
with Python3. You can use instead the richcmp function provided
by sage.

Note that either _cmp_ or _richcmp_ should be provided,
since otherwise comparison does not work:

since \(a\) is defined as an element of \(P\). However, we can not verify yet that
the integers are contained in the fraction field of the ring of integers. It
does not even give a wrong answer, but results in an error:

Sometimes the base classes do not reflect the mathematics: The set of \(m\times
n\) matrices over a field forms, in general, not more than a vector
space. Hence, this set (called MatrixSpace)
is not implemented on top of sage.rings.ring.Ring. However, if
\(m=n\), then the matrix space is an algebra, thus, is a ring.

From the point of view of Python base classes, both cases are the same:

One can provide default methodsfor all objects of a category, and
for all elements of such objects. Hence, the category framework is a way
to inherit useful stuff that is not present in the base classes. These
default methods do not rely on implementation details, but on mathematical
concepts.

In addition, the categories define test suites for their objects and
elements—see the last section. Hence, one also gets basic sanity tests for
free.

Abstract base classes for the objects (“parent_class”) and the elements of
objects (“element_class”) are provided by attributes of the category. During
initialisation of a parent, the class of the parent is dynamically changed
into a sub–class of the category’s parent class. Likewise, sub–classes of
the category’s element class are available for the creation of elements of the
parent, as explained below.

A dynamic change of classes does not work in Cython. Nevertheless, method
inheritance still works, by virtue of a __getattr__ method.

Note

It is strongly recommended to use the category framework both in Python
and in Cython.

Let us see whether there is any gain in choosing the category of quotient
fields instead of the category of fields:

So, there is no immediate gain for our fraction fields, but additional methods
become available to our fraction field elements. Note that some of these
methods are place-holders: There is no default implementation, but it is
required (respectively is optional) to implement these methods:

Hence, when implementing elements of a quotient field, it is required to
implement methods returning the denominator and the numerator, and a method
that tells whether the element is nonzero, and in addition, it is optional
(but certainly recommended) to provide some arithmetic methods. If one forgets
to implement the required methods, the test suites of the category framework
will complain—see below.

The fraction field \(P\) inherits additional methods. For example, the base
class Field does not have a method sum. But
\(P\) inherits such method from the category of commutative additive
monoids—see
sum():

sage: P.sum.__module__'sage.categories.additive_monoids'

We have seen above that we can add elements. Nevertheless, the sum method
does not work, yet:

Similar to what we have seen for parents, a new class is dynamically created
that combines the element class of the parent’s category with the class that
we have implemented above. However, the category framework is implemented in a
different way for elements than for parents:

We provide the parent \(P\) (or its class) with an attribute called
“Element”, whose value is a class.

The parent automatically obtains an attribute P.element_class, that
subclasses both P.Element and P.category().element_class.

Hence, for providing our fraction fields with their own element classes, we
just need to add a single line to our class:

All elements of \(P\) should use the element class. In order to make sure
that this also holds for the result of arithmetic operations, we created
them as instances of self.__class__ in the arithmetic methods of
MyElement.

P.zero() defaults to returning P(0) and thus returns an
instance of P.element_class. Since P.sum([...]) starts the summation with
P.zero() and the class of the sum only depends on the first
summand, by our implementation, we have:

But that’s surprising: The element \(a\) is just an instance of MyElement,
but not of P.element_class, and its class does not know about the factor
method. In fact, this is due to a __getattr__ method defined for
sage.structure.element.Element.

The category framework is sometimes blamed for speed regressions, as in
trac ticket #9138 and trac ticket #11900. But if the category framework is used
properly, then it is fast. For illustration, we determine the time needed to
access an attribute inherited from the element class. First, we consider an
element that uses the class that we implemented above, but does not use the
category framework properly:

sage: type(a)<class '__main__.MyElement'>sage: timeit('a.factor',number=1000)# random1000 loops, best of 3: 2 us per loop

Now, we consider an element that is equal to \(a\), but uses the category
framework properly:

“Coercion” in the C programming language means “automatic type
conversion”. However, in Sage, coercion is involved if one wants to be able to
do arithmetic, comparisons, etc. between elements of distinct parents. Hence,
coercion is not about a change of types, but about a change of parents.

As an illustration, we show that elements of the same type may very well belong
to rather different parents:

A coercion happens implicitly, without being explicitly requested by the
user. Hence, coercion must be based on mathematical rigour. In our example,
any element of \(P_2\) can be naturally interpreted as an element of \(P_1\). We
thus have:

Hence, we only have a partial map. This is fine for a conversion,
but a partial map does not qualify as a coercion.

Coercions are structure preserving.

Any real number can be converted to an integer, namely by
rounding. However, such a conversion is not useful in arithmetic
operations, since the underlying algebraic structure is not preserved:

sage: int(1.6)+int(2.7)==int(1.6+2.7)False

The structure that is to be preserved depends on the category of the
involved parents. For example, the coercion from the integers into the
rational field is a homomorphism of euclidean domains:

sage: QQ.coerce_map_from(ZZ).category_for()Join of Category of euclidean domains and Category of infinite setsand Category of metric spaces

There is at most one coercion from one parent to another

In addition, if there is a coercion from \(P_2\) to \(P_1\), then a
conversion from \(P_2\) to \(P_1\) is defined for all elements of \(P_2\) and
coincides with the coercion.
Nonetheless, user-exposed maps are copies of the internally used maps whence
the lack of identity between different instantiations:

If there is a coercion \(\varphi: P_1 \to P_2\) and another coercion \(\psi:
P_2 \to P_3\), then the composition of \(\varphi\) followed by \(\psi\) must
yield the unique coercion from \(P_1\) to \(P_3\).

The identity is a coercion

Together with the two preceding axioms, it follows: If there are coercions
from \(P_1\) to \(P_2\) and from \(P_2\) to \(P_1\), then they are mutually
inverse.

We have seen above that some conversions into our fraction fields became
available after providing the attribute Element. However, we can not
convert elements of a fraction field into elements of another fraction field,
yet:

For implementing a conversion, the default __call__ method should (almost)
never be overridden. Instead, we implement the method
_element_constructor_, that should return an instance of the parent’s
element class. Some old parent classes violate that rule—please help to
refactor them!

In addition to the conversion from the base ring and from pairs of base ring
elements, we now also have a conversion from the rationals to our fraction
field of \(\ZZ\):

sage: P=MyFrac(ZZ)sage: P(2);P(2,3);P(3/4)(2):(1)(2):(3)(3):(4)

Recall that above, the test \(1 \in P\) failed with an error. We try again and
find that the error has disappeared. This is because we are now able to
convert the integer \(1\) into \(P\). But the containment test still yields a
wrong answer:

sage: 1inPFalse

The technical reason: We have a conversion \(P(1)\) of \(1\) into \(P\), but this is
not known as a coercion—yet!

A more flexible way is to provide a method _coerce_map_from_ for the
parent.

Let \(P\) and \(R\) be parents. If P._coerce_map_from_(R) returns False
or None, then there is no coercion from \(R\) to \(P\). If it returns a map
with domain \(R\) and codomain \(P\), then this map is used for coercion. If it
returns True, then the conversion from \(R\) to \(P\) is used as coercion.

Note that in the following implementation, we need a special case for the
rational field, since QQ.base() is not the ring of integers.

By the method above, a parent coercing into the base ring will also coerce
into the fraction field, and a fraction field coerces into another fraction
field if there is a coercion of the corresponding base rings. Now, we have:

Recall that above, the test \(1 \in P\) gave a wrong answer. Let us repeat the
test now:

sage: 1inPTrue

Why is that?

The default element containment test \(x \in P\) is based on the interplay of
three building blocks: conversion, coercion, and equality test.

Clearly, if the conversion \(P(x)\) raises an error, then \(x\) can not be seen as an element of \(P\). On the other hand, a conversion \(P(x)\) can generally do very nasty things. So, the fact that \(P(x)\) works without error is necessary, but not sufficient for \(x \in P\).

If \(P\) is the parent of \(x\), then the conversion \(P(x)\) will not change \(x\) (at least, that’s the default). Hence, we will have \(x=P(x)\).

Sage uses coercion not only for arithmetic operations, but also for comparison: If there is a coercion from the parent of \(x\) to \(P\), then the equality test x==P(x) reduces to P(x)==P(x). Otherwise, x==P(x) will evaluate as false.

That leads to the following default implementation of element containment testing:

Note

\(x \in P\) holds if and only if the test x==P(x) does not
raise an error and evaluates as true.

If the user is not happy with that behaviour, the “magical” Python method
__contains__ can be overridden.

In the next, seemingly more easy example, there “obviously” is a coercion from
the fraction field of \(\ZZ\) to the fraction field of \(\ZZ[x]\). However, Sage
does not know enough about our new implementation of fraction fields. Hence,
it does not recognise the coercion:

sage: Frac(ZZ['x']).has_coerce_map_from(P)False

Two obvious questions arise:

How / why has the new ring been constructed in the example above?

How can we establish a coercion from \(P\) to \(\mathrm{Frac}(\ZZ[x])\)?

The key to answering both question is the construction of parents from simpler
pieces, that we are studying now. Note that we will answer the second question
not by providing a coercion from \(P\) to \(\mathrm{Frac}(\ZZ[x])\), but by
teaching Sage to automatically construct \(\mathrm{MyFrac}(\ZZ[x])\) and coerce
both \(P\) and \(\mathrm{Frac}(\ZZ[x])\) into it.

In both cases, the first value returned by
construction() is a mathematical
construction, called construction functor—see
ConstructionFunctor. The second return value
is a simpler parent to which the construction functor is applied.

Being functors, the same construction can be applied to different objects of a
category:

sage: Poly(QQ)isQQ['x']Truesage: Poly(ZZ)isZZ['x']Truesage: Poly(P)isP['x']Truesage: Fract(QQ['x'])Fraction Field of Univariate Polynomial Ring in x over Rational Field

Let us see on which categories these construction functors are defined:

We can now formulate our problem. We have parents \(P_1\), \(P_2\) and \(R\), and
construction functors \(F_1\), \(F_2\), such that \(P_1 = F_1(R)\) and \(P_2 =
F_2(R)\). We want to find a new construction functor \(F_3\), such that both
\(P_1\) and \(P_2\) coerce into \(P_3 = F_3(R)\).

In analogy to a notion of category theory, \(P_3\) is called the
pushout() of \(P_1\) and \(P_2\); and similarly
\(F_3\) is called the pushout of \(F_1\) and \(F_2\).

sage: fromsage.categories.pushoutimportpushoutsage: pushout(Fract(ZZ),Poly(ZZ))Univariate Polynomial Ring in x over Rational Field

\(F_1\circ F_2\) and \(F_2\circ F_1\) are natural candidates for the pushout of
\(F_1\) and \(F_2\). However, the order of the functors must rely on a canonical
choice. “Indecomposable” construction functors have a rank, and this allows
to order them canonically:

Note

If F1.rank is smaller than F2.rank, then the pushout is \(F_2\circ
F_1\) (hence, \(F_1\) is applied first).

When we apply Fract, AlgClos, Poly and Fract to the ring of
integers, we obtain:

sage: (Fract*Poly*AlgClos*Fract)(ZZ)Fraction Field of Univariate Polynomial Ring in x over Algebraic Field

When we apply Compl, Matr and Poly to the ring of integers, we
obtain:

sage: (Poly*Matr*Compl)(ZZ)Univariate Polynomial Ring in x over Full MatrixSpace of 3 by 3 dense matrices over Integer Ring

Applying the shuffling procedure yields

sage: (Poly*Matr*Fract*Poly*AlgClos*Fract*Compl)(ZZ)Univariate Polynomial Ring in x over Full MatrixSpace of 3 by 3 dense matrices over Fraction Field of Univariate Polynomial Ring in x over Algebraic Field

and this is indeed equal to the pushout found by Sage:

sage: pushout((Fract*Poly*AlgClos*Fract)(ZZ),(Poly*Matr*Compl)(ZZ))Univariate Polynomial Ring in x over Full MatrixSpace of 3 by 3 dense matrices over Fraction Field of Univariate Polynomial Ring in x over Algebraic Field

If F1.rank==G1.rank then Sage’s pushout constructions offers two ways to
proceed:

Construction functors have a method merge() that either returns None or returns a construction functor—see below. If either F1.merge(G1) or G1.merge(F1) returns a construction functor \(H_1\), then we apply \(H_1\) and continue with \(...\circ F_3\circ F_2\) and \(...\circ G_3\circ G_2\).

Construction functors have a method commutes(). If either F1.commutes(G1) or G1.commutes(F1) returns True, then we apply both \(F_1\) and \(G_1\) in any order, and continue with \(...\circ F_3\circ F_2\) and \(...\circ G_3\circ G_2\).

By default, F1.merge(G1) returns F1 if F1==G1, and returns
None otherwise. The commutes() method exists, but it seems that so far
nobody has implemented two functors of the same rank that commute.

Being able to do arithmetics involving elements of different parents, with the
automatic creation of a pushout to contain the result, is certainly
convenient—but one should not rely on it, if speed matters. Simply the
conversion of elements into different parents takes time. Moreover, by
trac ticket #14058, the pushout may be subject to Python’s cyclic garbage
collection. Hence, if one does not keep a strong reference to it, the same
parent may be created repeatedly, which is a waste of time. In the following
example, we illustrate the slow–down resulting from blindly relying on
coercion:

The category framework does not only provide functionality but also a test
framework. This section logically belongs to the section on categories, but
without the bits that we have implemented in the section on coercion, our
implementation of fraction fields would not have passed the tests yet.

We have already seen above that a category can require/suggest certain parent
or element methods, that the user must/should implement. This is in order to
smoothly blend with the methods that already exist in Sage.

The methods that ought to be provided are called
abstract_method(). Let us see what methods
are needed for quotient fields and their elements:

Hence, the only required method (that is actually required for all parents
that belong to the category of sets) is an element containment test. That’s
fine, because the base class Parent provides a
default containment test.

Hence, the elements must provide denominator() and numerator()
methods, and must be able to tell whether they are zero or not. The base class
Element provides a default __nonzero__()
method. In addition, the elements may provide Sage’s single underscore
arithmetic methods (actually any ring element should provide them).

We have implemented all abstract methods (or inherit them from base classes),
we use the category framework, and we have implemented coercions. So, we are
confident that the test suite runs without an error. In fact, it does!

Note

The following trick with the __main__ module is only needed in
doctests, not in an interactive session or when defining the classes
externally.

As one can see, tests are also performed on elements. There are methods that
return one element or a list of some elements, relying on “typical” elements
that can be found in most algebraic structures.

sage: P.an_element();P.some_elements()(2):(1)[(2):(1)]

Unfortunately, the list of elements that is returned by the default method is
of length one, and that single element could also be a bit more interesting.
The method an_element relies on a method _an_element_(), so, we implement
that. We also override the some_elements method.

Now, as we have more interesting elements, we may also add a test for the
“factor” method. Recall that the method was inherited from the category, but
it appears that it is not tested.

Normally, a test for a method defined by a category should be provided by the
same category. Hence, since factor is defined in the category of quotient
fields, a test should be added there. But we won’t change source code here and
will instead create a sub–category.

Apparently, If \(e\) is an element of a quotient field, the product of the
factors returned by e.factor() should be equal to \(e\). For forming the
product, we use the prod method, that, no surprise, is inherited from
another category:

sage: P.prod.__module__'sage.categories.monoids'

When we want to create a sub–category, we need to provide a method
super_categories(), that returns a
list of all immediate super categories (here: category of quotient fields).

Warning

A sub–category \(S\) of a category \(C\) is not implemented as a
sub–class of C.__class__! \(S\) becomes a sub–category of \(C\) only
if S.super_categories() returns (a sub–category of) \(C\)!

The parent and element methods of a category are provided as methods of
classes that are the attributes ParentMethods and ElementMethods of
the category, as follows:

We provide an instance of our quotient field implementation with that new
category. Note that categories have a default _repr_ method, that guesses
a good string representation from the name of the class:
QuotientFieldsWithTest becomes “quotient fields with test”.

Note

The following trick with the __main__ module is only needed in
doctests, not in an interactive session or when defining the classes
externally.

Last, we observe that the new test has automatically become part of the test
suite. We remark that the existing tests became more serious as well, since we
made sage.structure.parent.Parent.an_element() return something more
interesting.

# Importing base classes, ...importsagefromsage.rings.ringimportFieldfromsage.structure.elementimportFieldElementfromsage.categories.categoryimportCategory# ... the UniqueRepresentation tool,fromsage.structure.unique_representationimportUniqueRepresentation# ... some categories, and ...fromsage.categories.fieldsimportFieldsfromsage.categories.quotient_fieldsimportQuotientFieldsfromsage.categories.integral_domainsimportIntegralDomains# construction functorsfromsage.categories.pushoutimportConstructionFunctor# Fraction field elementsclassMyElement(FieldElement):def__init__(self,parent,n,d=None):ifparentisNone:raiseValueError("The parent must be provided")B=parent.base()ifdisNone:# The default denominator is oned=B.one()# verify that both numerator and denominator belong to the baseifnnotinBordnotinB:raiseValueError("Numerator and denominator must be elements of %s"%B)# Numerator and denominator should not just be "in" B,# but should be defined as elements of Bd=B(d)n=B(n)# the denominator must not be zeroifd==0:raiseZeroDivisionError("The denominator must not be zero")# normalize the denominator: WLOG, it shall be non-negative.ifd<0:self.n=-nself.d=-delse:self.n=nself.d=dFieldElement.__init__(self,parent)# Methods required by the category of fraction fields:defnumerator(self):returnself.ndefdenominator(self):returnself.d# String representation (single underscore!)def_repr_(self):return"(%s):(%s)"%(self.n,self.d)# Comparison: We can assume that both arguments are coerced# into the same parent, which is a fraction field. Hence, we# are allowed to use the denominator() and numerator() methods# on the second argument.def_richcmp_(self,other,op):fromsage.structure.richcmpimportrichcmpreturnrichcmp(self.n*other.denominator(),other.numerator()*self.d,op)# Arithmetic methods, single underscore. We can assume that both# arguments are coerced into the same parent.# We return instances of self.__class__, because self.__class__ will# eventually be a sub-class of MyElement.def_add_(self,other):C=self.__class__D=self.d*other.denominator()returnC(self.parent(),self.n*other.denominator()+self.d*other.numerator(),D)def_sub_(self,other):C=self.__class__D=self.d*other.denominator()returnC(self.parent(),self.n*other.denominator()-self.d*other.numerator(),D)def_mul_(self,other):C=self.__class__returnC(self.parent(),self.n*other.numerator(),self.d*other.denominator())def_div_(self,other):C=self.__class__returnC(self.parent(),self.n*other.denominator(),self.d*other.numerator())# Inheritance from UniqueRepresentation implements the unique parent# behaviour. Moreover, it implements pickling (provided that Python# succeeds to look up the class definition).classMyFrac(UniqueRepresentation,Field):# Implement the category framework for elements, which also# makes some basic conversions work.Element=MyElement# Allow to pass to a different category, by an optional argumentdef__init__(self,base,category=None):# Fraction fields only exist for integral domainsifbasenotinIntegralDomains():raiseValueError("%s is no integral domain"%base)# Implement the category framework for the parentField.__init__(self,base,category=categoryorQuotientFields())# Single-underscore method for string representationdef_repr_(self):return"NewFrac(%s)"%repr(self.base())# Two methods that are implicitly used in some testsdefbase_ring(self):returnself.base().base_ring()defcharacteristic(self):returnself.base().characteristic()# Implement conversions. Do not override __call__!def_element_constructor_(self,*args,**kwds):iflen(args)!=1:returnself.element_class(self,*args,**kwds)x=args[0]try:P=x.parent()exceptAttributeError:returnself.element_class(self,x,**kwds)ifPinQuotientFields()andP!=self.base():returnself.element_class(self,x.numerator(),x.denominator(),**kwds)returnself.element_class(self,x,**kwds)# Implement coercion from the base and from fraction fields# over a ring that coerces into the basedef_coerce_map_from_(self,S):ifself.base().has_coerce_map_from(S):returnTrueifSinQuotientFields():ifself.base().has_coerce_map_from(S.base()):returnTrueifhasattr(S,'ring_of_integers')andself.base().has_coerce_map_from(S.ring_of_integers()):returnTrue# Tell how this parent was constructed, in order to enable pushout constructionsdefconstruction(self):returnMyFracFunctor(),self.base()# return some elements of this parentdef_an_element_(self):a=self.base().an_element()b=self.base_ring().an_element()if(a+b)!=0:returnself(a)**2/(self(a+b)**3)ifb!=0:returnself(a)/self(b)**2returnself(a)**2*self(b)**3defsome_elements(self):return[self.an_element(),self(self.base().an_element()),self(self.base_ring().an_element())]# A construction functor for our implementation of fraction fieldsclassMyFracFunctor(ConstructionFunctor):# The rank is the same for Sage's original fraction field functorrank=5def__init__(self):# The fraction field construction is a functor# from the category of integral domains into the category of# fields# NOTE: We could actually narrow the codomain and use the# category QuotientFields()ConstructionFunctor.__init__(self,IntegralDomains(),Fields())# Applying the functor to an object. Do not override __call__!def_apply_functor(self,R):returnMyFrac(R)# Note: To apply the functor to morphisms, implement# _apply_functor_to_morphism# Make sure that arithmetic involving elements of Frac(R) and# MyFrac(R) works and yields elements of MyFrac(R)defmerge(self,other):ifisinstance(other,(type(self),sage.categories.pushout.FractionField)):returnself# A quotient field category with additional tests.# Notes:# - Category inherits from UniqueRepresentation. Hence, there# is only one category for given arguments.# - Since QuotientFieldsWithTest is a singleton (there is only# one instance of this class), we could inherit from# sage.categories.category_singleton.Category_singleton# rather than from sage.categories.category.CategoryclassQuotientFieldsWithTest(Category):# Our category is a sub-category of the category of quotient fields,# by means of the following method.defsuper_categories(self):return[QuotientFields()]# Here, we could implement methods that are available for# all objects in this category.classParentMethods:pass# Here, we add a new test that is available for all elements# of any object in this category.classElementMethods:def_test_factorisation(self,**options):P=self.parent()# The methods prod() and factor() are inherited from# some other categories.assertself==P.prod([P(b)**eforb,einself.factor()])