Singleton categories

Singleton categories

class sage.categories.category_singleton.Category_contains_method_by_parent_class

Bases: object

Returns whether x is an object in this category.

More specifically, returns True if and only if x has a category which is a subcategory of this one.

EXAMPLES:

sage: ZZ in Sets()
True
class sage.categories.category_singleton.Category_singleton(s=None)

Bases: sage.categories.category.Category

A base class for implementing singleton category

A singleton category is a category whose class takes no parameters like Fields() or Rings(). See also the Singleton design pattern.

This is a subclass of Category, with a couple optimizations for singleton categories.

The main purpose is to make the idioms:

sage: QQ in Fields() True sage: ZZ in Fields() False

as fast as possible, and in particular competitive to calling a constant Python method, in order to foster its systematic use throughout the Sage library. Such tests are time critical, in particular when creating a lot of polynomial rings over small fields like in the elliptic curve code.

EXAMPLES:

sage: from sage.categories.category_singleton import Category_singleton
sage: class MyRings(Category):
....:     def super_categories(self): return Rings().super_categories()
sage: class MyRingsSingleton(Category_singleton):
....:     def super_categories(self): return Rings().super_categories()

We create three rings. One of them is contained in the usual category of rings, one in the category of “my rings” and the third in the category of “my rings singleton”:

sage: R = QQ['x,y']
sage: R1 = Parent(category = MyRings())
sage: R2 = Parent(category = MyRingsSingleton())
sage: R in MyRings()
False
sage: R1 in MyRings()
True
sage: R1 in MyRingsSingleton()
False
sage: R2 in MyRings()
False
sage: R2 in MyRingsSingleton()
True

One sees that containment tests for the singleton class is a lot faster than for a usual class:

sage: timeit("R in MyRings()", number=10000)                  # not tested
10000 loops, best of 3: 7.12 µs per loop
sage: timeit("R1 in MyRings()", number=10000)                 # not tested
10000 loops, best of 3: 6.98 µs per loop
sage: timeit("R in MyRingsSingleton()", number=10000)         # not tested
10000 loops, best of 3: 3.08 µs per loop
sage: timeit("R2 in MyRingsSingleton()", number=10000)        # not tested
10000 loops, best of 3: 2.99 µs per loop

So this is an improvement, but not yet competitive with a pure Cython method:

sage: timeit("R.is_ring()", number=10000)                     # not tested
10000 loops, best of 3: 383 ns per loop

However, it is competitive with a Python method. Actually it is faster, if one stores the category in a variable:

sage: _Rings = Rings()
sage: R3 = Parent(category = _Rings)
sage: R3.is_ring.__module__
'sage.categories.rings'
sage: timeit("R3.is_ring()", number=10000)                    # not tested
10000 loops, best of 3: 2.64 µs per loop
sage: timeit("R3 in Rings()", number=10000)                   # not tested
10000 loops, best of 3: 3.01 µs per loop
sage: timeit("R3 in _Rings", number=10000)                    # not tested
10000 loops, best of 3: 652 ns per loop

This might not be easy to further optimize, since the time is consumed in many different spots:

sage: timeit("MyRingsSingleton.__classcall__()", number=10000)# not tested
10000 loops, best of 3: 306 ns per loop

sage: X = MyRingsSingleton()
sage: timeit("R in X  ", number=10000)                        # not tested
10000 loops, best of 3: 699 ns per loop

sage: c = MyRingsSingleton().__contains__
sage: timeit("c(R)", number = 10000)                          # not tested
10000 loops, best of 3: 661 ns per loop

Warning

A singleton concrete class \(A\) should not have a subclass \(B\) (necessarily concrete). Otherwise, creating an instance \(a\) of \(A\) and an instance \(b\) of \(B\) would break the singleton principle: \(A\) would have two instances \(a\) and \(b\).

With the current implementation only direct subclasses of Category_singleton are supported:

sage: class MyRingsSingleton(Category_singleton):
....:     def super_categories(self): return Rings().super_categories()
sage: class Disaster(MyRingsSingleton): pass
sage: Disaster()
Traceback (most recent call last):
...
AssertionError: <class '__main__.Disaster'> is not a direct subclass of <class 'sage.categories.category_singleton.Category_singleton'>

However, it is acceptable for a direct subclass \(R\) of Category_singleton to create its unique instance as an instance of a subclass of itself (in which case, its the subclass of \(R\) which is concrete, not \(R\) itself). This is used for example to plug in extra category code via a dynamic subclass:

sage: from sage.categories.category_singleton import Category_singleton
sage: class R(Category_singleton):
....:     def super_categories(self): return [Sets()]
sage: R()
Category of r
sage: R().__class__
<class '__main__.R_with_category'>
sage: R().__class__.mro()
[<class '__main__.R_with_category'>,
 <class '__main__.R'>,
 <class 'sage.categories.category_singleton.Category_singleton'>,
 <class 'sage.categories.category.Category'>,
 <class 'sage.structure.unique_representation.UniqueRepresentation'>,
 <class 'sage.structure.unique_representation.CachedRepresentation'>,
 <type 'sage.misc.fast_methods.WithEqualityById'>,
 <type 'sage.structure.sage_object.SageObject'>,
 <class '__main__.R.subcategory_class'>,
 <class 'sage.categories.sets_cat.Sets.subcategory_class'>,
 <class 'sage.categories.sets_with_partial_maps.SetsWithPartialMaps.subcategory_class'>,
 <class 'sage.categories.objects.Objects.subcategory_class'>,
 <type 'object'>]
sage: R() is R()
True
sage: R() is R().__class__()
True

In that case, R is an abstract class and has a single concrete subclass, so this does not break the Singleton design pattern.

TESTS:

sage: import __main__
sage: __main__.MyRings = MyRings
sage: __main__.MyRingsSingleton = MyRingsSingleton
sage: TestSuite(MyRingsSingleton()).run(skip=["_test_category"])

Note

The _test_category test is failing because MyRingsSingleton() is not a subcategory of the join of its super categories:

sage: C = MyRingsSingleton()
sage: C.super_categories()
[Category of rngs, Category of semirings]
sage: Rngs() & Semirings()
Category of rings
sage: C.is_subcategory(Rings())
False

Oh well; it’s not really relevant for those tests.

Previous topic

Specific category classes

Next topic

Axioms

This Page