Singleton categories - Category Framework

Skip to content
Back to top
View this page
Edit this page

# Singleton categories

class sage.categories.category_singleton.Category_contains_method_by_parent_class

  Bases: `object`

  Return 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

      sage: ZZ in Sets()
      True

  Python

      >>> from sage.all import *
      >>> ZZ in Sets()
      True

class sage.categories.category_singleton.Category_singleton

  Bases: `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

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

  Python

      >>> from sage.all import *
      >>> QQ in Fields()
      True
      >>> 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

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

  [Python]

      >>> from sage.all import *
      >>> from sage.categories.category_singleton import Category_singleton
      >>> class MyRings(Category):
      ...     def super_categories(self): return Rings().super_categories()
      >>> 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

      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

  Python

      >>> from sage.all import *
      >>> R = QQ['x,y']
      >>> R1 = Parent(category = MyRings())
      >>> R2 = Parent(category = MyRingsSingleton())
      >>> R in MyRings()
      False
      >>> R1 in MyRings()
      True
      >>> R1 in MyRingsSingleton()
      False
      >>> R2 in MyRings()
      False
      >>> R2 in MyRingsSingleton()
      True

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

  Sage

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

  [Python]

      >>> from sage.all import *
      >>> # not tested
      >>> timeit("R in MyRings()", number=Integer(10000))
      10000 loops, best of 3: 7.12 µs per loop
      >>> timeit("R1 in MyRings()", number=Integer(10000))
      10000 loops, best of 3: 6.98 µs per loop
      >>> timeit("R in MyRingsSingleton()", number=Integer(10000))
      10000 loops, best of 3: 3.08 µs per loop
      >>> timeit("R2 in MyRingsSingleton()", number=Integer(10000))
      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

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

  Python

      >>> from sage.all import *
      >>> timeit("R.is_ring()", number=Integer(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

      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

  [Python]

      >>> from sage.all import *
      >>> _Rings = Rings()
      >>> R3 = Parent(category = _Rings)
      >>> R3.is_ring.__module__
      'sage.categories.rings'
      >>> timeit("R3.is_ring()", number=Integer(10000))                    # not tested
      10000 loops, best of 3: 2.64 µs per loop
      >>> timeit("R3 in Rings()", number=Integer(10000))                   # not tested
      10000 loops, best of 3: 3.01 µs per loop
      >>> timeit("R3 in _Rings", number=Integer(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

      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

  Python

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

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

      >>> c = MyRingsSingleton().__contains__
      >>> timeit("c(R)", number = Integer(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

      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'>

  Python

      >>> from sage.all import *
      >>> class MyRingsSingleton(Category_singleton):
      ...     def super_categories(self): return Rings().super_categories()
      >>> class Disaster(MyRingsSingleton): pass
      >>> 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

      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.misc.fast_methods.WithEqualityById'>,
       <class 'sage.structure.unique_representation.CachedRepresentation'>,
       <class 'sage.structure.unique_representation.WithPicklingByInitArgs'>,
       <class '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'>,
       <class 'object'>]
      sage: R() is R()
      True
      sage: R() is R().__class__()
      True

  [Python]

      >>> from sage.all import *
      >>> from sage.categories.category_singleton import Category_singleton
      >>> class R(Category_singleton):
      ...     def super_categories(self): return [Sets()]
      >>> R()
      Category of r
      >>> R().__class__
      <class '__main__.R_with_category'>
      >>> 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.misc.fast_methods.WithEqualityById'>,
       <class 'sage.structure.unique_representation.CachedRepresentation'>,
       <class 'sage.structure.unique_representation.WithPicklingByInitArgs'>,
       <class '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'>,
       <class 'object'>]
      >>> R() is R()
      True
      >>> 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.

  See also

  `Category.__classcall__()`, `Category.__init__()`

  Note

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

  [Sage]

      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

  [Python]

      >>> from sage.all import *
      >>> C = MyRingsSingleton()
      >>> C.super_categories()
      [Category of rngs, Category of semirings]
      >>> Rngs() & Semirings()
      Category of rings
      >>> C.is_subcategory(Rings())
      False

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