diff --git a/src/sage/categories/category.py b/src/sage/categories/category.py index 60ea5aa5270..755d19dde07 100644 --- a/src/sage/categories/category.py +++ b/src/sage/categories/category.py @@ -2055,7 +2055,7 @@ def _with_axiom(self, axiom): INPUT: - - ``axiom`` -- a string, the name of an axiom + - ``axiom`` -- an axiom or a string, the name of an axiom EXAMPLES:: @@ -2086,7 +2086,7 @@ def _with_axioms(self, axioms): INPUT: - - ``axioms`` -- a list of strings, the names of the axioms + - ``axioms`` -- a list of axioms or names thereof EXAMPLES:: @@ -2097,13 +2097,20 @@ def _with_axioms(self, axioms): sage: FiniteSets()._with_axioms(["Finite"]) Category of finite sets - Axioms that are not defined for the ``self`` are ignored:: + Axioms that are not defined for ``self`` are ignored:: sage: Sets()._with_axioms(["Flying"]) Category of sets sage: Magmas()._with_axioms(["Flying", "Unital"]) Category of unital magmas + Unknown axioms trigger an error:: + + sage: Sets()._with_axioms(["FooBar"]) + Traceback (most recent call last): + ... + ValueError: No known axiom named FooBar + Note that adding several axioms at once can do more than adding them one by one. This is because the availability of an axiom may depend on another axiom. For example, for diff --git a/src/sage/categories/category_with_axiom.py b/src/sage/categories/category_with_axiom.py index 7b76623955e..2529ed7e0ce 100644 --- a/src/sage/categories/category_with_axiom.py +++ b/src/sage/categories/category_with_axiom.py @@ -1,5 +1,5 @@ """ -Axioms +Axioms and categories with axioms This documentation covers how to implement axioms and proceeds with an overview of the implementation of the axiom infrastructure. It assumes @@ -235,7 +235,7 @@ class ``Semigroups.Infinite`` inheriting from :class:`CategoryWithAxiom`. failed (try it!). In general, one needs to set the attribute explicitly:: sage: class FiniteCs(CategoryWithAxiom): - ....: _base_category_class_and_axiom = (Cs, 'Finite') + ....: _base_category_class_and_axiom = (Cs, axioms.Finite) ....: class ParentMethods: ....: def foo(self): ....: print("I am a method on finite C's") @@ -320,28 +320,35 @@ class from the base category class:: We describe now how to define a new axiom. The first step is to figure out the largest category where the axiom makes sense. For example ``Sets`` for ``Finite``, ``Magmas`` for ``Associative``, or -``Modules`` for ``FiniteDimensional``. Here we define the axiom -``Green`` for the category ``Cs`` and its subcategories:: +``Modules`` for ``FiniteDimensional``. Here we declare the new axiom +``Green``:: + + sage: from sage.categories.axiom import Axiom + sage: Axiom('Green'); + Green + +It immedieately appears in the catalog of axioms, which is a +convenient way to access it: + + sage: axioms.Green + Green + +We now define and implement this axiom for category ``Cs`` (and its +subcategories):: sage: from sage.categories.category_with_axiom import CategoryWithAxiom sage: class Cs(Category): ....: def super_categories(self): ....: return [Sets()] ....: class SubcategoryMethods: - ....: def Green(self): + ....: def Green(self): # definition ....: '' - ....: return self._with_axiom("Green") - ....: class Green(CategoryWithAxiom): + ....: return self._with_axiom(axioms.Green) + ....: class Green(CategoryWithAxiom): # implementation ....: class ParentMethods: ....: def foo(self): ....: print("I am a method on green C's") -With the current implementation, the name of the axiom must also be -added to a global container:: - - sage: all_axioms = sage.categories.category_with_axiom.all_axioms - sage: all_axioms += ("Green",) - We can now use the axiom as usual:: sage: Cs().Green() @@ -387,27 +394,30 @@ class from the base category class:: We could possibly define an @axiom decorator? This could hide two little implementation details: whether or not to make the method a cached method, and the call to _with_axiom(...) under the hood. It - could do possibly do some more magic. The gain is not obvious though. + could do possibly do some more magic. The gain is not obvious + though. Alternatively, one could try to merge the axiom + declaration and its definition .. NOTE:: - ``all_axioms`` is only used marginally, for sanity checks and when - trying to derive automatically the base category class. The order - of the axioms in this tuple also controls the order in which they - appear when printing out categories with axioms (see - :meth:`CategoryWithAxiom._repr_object_names_static`). + Internally, the catalog ``axioms`` is used marginally, for sanity + checks and when trying to derive automatically the axiom and base + category classes. - During a Sage session, new axioms should only be added at the *end* - of ``all_axioms``, as above, so as to not break the cache of - :func:`axioms_rank`. Otherwise, they can be inserted statically - anywhere in the tuple. For axioms defined within the Sage library, - the name is best inserted by editing directly the definition of - ``all_axioms`` in :mod:`sage.categories.category_with_axiom`. +.. NOTE:: + + The order in which axioms are declared influences the heuristic + that is used to try produce nice names for the objects of a + category; see :meth:`CategoryWithAxiom._repr_object_names`. + + As a rule of thumb, axioms should be declared from more general to + more specific (which is the natural tendency anyway when loading + the Sage library). .. TOPIC:: Design note Let us state again that, unlike what the existence of - ``all_axioms`` might suggest, the definition of an axiom is local + ``axioms`` might suggest, the definition of an axiom is local to a category and its subcategories. In particular, two independent categories ``Cs()`` and ``Ds()`` can very well define axioms with the same name and different semantics. As long as the @@ -421,12 +431,6 @@ class from the base category class:: This caveat is no different from that of name clashes in hierarchy of classes involving multiple inheritance. -.. TODO:: - - Explore ways to get rid of this global ``all_axioms`` tuple, - and/or have automatic registration there, and/or having a - register_axiom(...) method. - Special case: defining an axiom depending on several categories ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -794,9 +798,9 @@ def _(): return LazyImport('sage.categories.rngs', 'Rngs', at_startup=True) the subsets of axioms appearing in the abstract model. For example, a division ring necessarily has no zero divisors:: - sage: 'NoZeroDivisors' in Rings().Division().axioms() + sage: axioms.NoZeroDivisors in Rings().Division().axioms() True - sage: 'NoZeroDivisors' in Rings().axioms() + sage: axioms.NoZeroDivisors in Rings().axioms() False This deduction rule is implemented by the method @@ -903,7 +907,7 @@ def _(): return LazyImport('sage.categories.rngs', 'Rngs', at_startup=True) ....: return [MyDivisionRings()] sage: class MyFiniteFields(CategoryWithAxiom): - ....: _base_category_class_and_axiom = (MyDivisionRings, "Finite") + ....: _base_category_class_and_axiom = (MyDivisionRings, axioms.Finite) ....: def extra_super_categories(self): # Wedderburn's theorem ....: return [MyFields()] @@ -943,8 +947,8 @@ def _(): return LazyImport('sage.categories.rngs', 'Rngs', at_startup=True) sage: from sage.categories.category_singleton import Category_singleton sage: from sage.categories.category_with_axiom import axiom sage: import sage.categories.category_with_axiom - sage: all_axioms = sage.categories.category_with_axiom.all_axioms - sage: all_axioms += ("B","C","D","E","F") + sage: Axiom("B"), Axiom("C"), Axiom("D"), Axiom("E"), Axiom("F") + (B, C, D, E, F) sage: class As(Category_singleton): ....: def super_categories(self): diff --git a/src/sage/categories/division_rings.py b/src/sage/categories/division_rings.py index ecb6a1af9e9..713c6fd7f22 100644 --- a/src/sage/categories/division_rings.py +++ b/src/sage/categories/division_rings.py @@ -55,7 +55,8 @@ def extra_super_categories(self): sage: DivisionRings().extra_super_categories() (Category of domains,) - sage: "NoZeroDivisors" in DivisionRings().axioms() + + sage: axioms.NoZeroDivisors in DivisionRings().axioms() True """ return (Rings().NoZeroDivisors(),)