passagemath-objects 10.6.44__cp314-cp314t-macosx_13_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_objects/.dylibs/libgmp.10.dylib +0 -0
- passagemath_objects/__init__.py +3 -0
- passagemath_objects-10.6.44.dist-info/METADATA +115 -0
- passagemath_objects-10.6.44.dist-info/RECORD +280 -0
- passagemath_objects-10.6.44.dist-info/WHEEL +6 -0
- passagemath_objects-10.6.44.dist-info/top_level.txt +3 -0
- sage/all__sagemath_objects.py +37 -0
- sage/arith/all__sagemath_objects.py +5 -0
- sage/arith/long.pxd +411 -0
- sage/arith/numerical_approx.cpython-314t-darwin.so +0 -0
- sage/arith/numerical_approx.pxd +35 -0
- sage/arith/numerical_approx.pyx +75 -0
- sage/arith/power.cpython-314t-darwin.so +0 -0
- sage/arith/power.pxd +31 -0
- sage/arith/power.pyx +127 -0
- sage/categories/action.cpython-314t-darwin.so +0 -0
- sage/categories/action.pxd +29 -0
- sage/categories/action.pyx +641 -0
- sage/categories/algebra_functor.py +745 -0
- sage/categories/all__sagemath_objects.py +33 -0
- sage/categories/basic.py +62 -0
- sage/categories/cartesian_product.py +295 -0
- sage/categories/category.py +3401 -0
- sage/categories/category_cy_helper.cpython-314t-darwin.so +0 -0
- sage/categories/category_cy_helper.pxd +8 -0
- sage/categories/category_cy_helper.pyx +322 -0
- sage/categories/category_singleton.cpython-314t-darwin.so +0 -0
- sage/categories/category_singleton.pxd +3 -0
- sage/categories/category_singleton.pyx +342 -0
- sage/categories/category_types.py +637 -0
- sage/categories/category_with_axiom.py +2876 -0
- sage/categories/covariant_functorial_construction.py +703 -0
- sage/categories/facade_sets.py +228 -0
- sage/categories/functor.cpython-314t-darwin.so +0 -0
- sage/categories/functor.pxd +7 -0
- sage/categories/functor.pyx +691 -0
- sage/categories/homset.py +1338 -0
- sage/categories/homsets.py +364 -0
- sage/categories/isomorphic_objects.py +73 -0
- sage/categories/map.cpython-314t-darwin.so +0 -0
- sage/categories/map.pxd +34 -0
- sage/categories/map.pyx +2106 -0
- sage/categories/morphism.cpython-314t-darwin.so +0 -0
- sage/categories/morphism.pxd +14 -0
- sage/categories/morphism.pyx +895 -0
- sage/categories/objects.py +167 -0
- sage/categories/primer.py +1696 -0
- sage/categories/pushout.py +4834 -0
- sage/categories/quotients.py +64 -0
- sage/categories/realizations.py +200 -0
- sage/categories/sets_cat.py +3290 -0
- sage/categories/sets_with_partial_maps.py +52 -0
- sage/categories/subobjects.py +64 -0
- sage/categories/subquotients.py +21 -0
- sage/categories/with_realizations.py +311 -0
- sage/cpython/__init__.py +19 -0
- sage/cpython/_py2_random.py +619 -0
- sage/cpython/all.py +3 -0
- sage/cpython/atexit.cpython-314t-darwin.so +0 -0
- sage/cpython/atexit.pyx +269 -0
- sage/cpython/builtin_types.cpython-314t-darwin.so +0 -0
- sage/cpython/builtin_types.pyx +7 -0
- sage/cpython/cython_metaclass.cpython-314t-darwin.so +0 -0
- sage/cpython/cython_metaclass.h +117 -0
- sage/cpython/cython_metaclass.pxd +3 -0
- sage/cpython/cython_metaclass.pyx +130 -0
- sage/cpython/debug.cpython-314t-darwin.so +0 -0
- sage/cpython/debug.pyx +302 -0
- sage/cpython/dict_del_by_value.cpython-314t-darwin.so +0 -0
- sage/cpython/dict_del_by_value.pxd +9 -0
- sage/cpython/dict_del_by_value.pyx +191 -0
- sage/cpython/dict_internal.h +245 -0
- sage/cpython/getattr.cpython-314t-darwin.so +0 -0
- sage/cpython/getattr.pxd +9 -0
- sage/cpython/getattr.pyx +439 -0
- sage/cpython/pycore_long.h +97 -0
- sage/cpython/pycore_long.pxd +10 -0
- sage/cpython/python_debug.h +44 -0
- sage/cpython/python_debug.pxd +47 -0
- sage/cpython/pyx_visit.h +13 -0
- sage/cpython/string.cpython-314t-darwin.so +0 -0
- sage/cpython/string.pxd +76 -0
- sage/cpython/string.pyx +34 -0
- sage/cpython/string_impl.h +60 -0
- sage/cpython/type.cpython-314t-darwin.so +0 -0
- sage/cpython/type.pxd +2 -0
- sage/cpython/type.pyx +40 -0
- sage/cpython/wrapperdescr.pxd +67 -0
- sage/ext/all__sagemath_objects.py +3 -0
- sage/ext/ccobject.h +64 -0
- sage/ext/cplusplus.pxd +17 -0
- sage/ext/mod_int.h +30 -0
- sage/ext/mod_int.pxd +24 -0
- sage/ext/stdsage.pxd +39 -0
- sage/groups/all__sagemath_objects.py +1 -0
- sage/groups/group.cpython-314t-darwin.so +0 -0
- sage/groups/group.pxd +14 -0
- sage/groups/group.pyx +322 -0
- sage/groups/old.cpython-314t-darwin.so +0 -0
- sage/groups/old.pxd +14 -0
- sage/groups/old.pyx +219 -0
- sage/libs/all__sagemath_objects.py +3 -0
- sage/libs/gmp/__init__.py +1 -0
- sage/libs/gmp/all.pxd +6 -0
- sage/libs/gmp/binop.pxd +23 -0
- sage/libs/gmp/misc.pxd +8 -0
- sage/libs/gmp/mpf.pxd +88 -0
- sage/libs/gmp/mpn.pxd +57 -0
- sage/libs/gmp/mpq.pxd +57 -0
- sage/libs/gmp/mpz.pxd +202 -0
- sage/libs/gmp/pylong.cpython-314t-darwin.so +0 -0
- sage/libs/gmp/pylong.pxd +12 -0
- sage/libs/gmp/pylong.pyx +150 -0
- sage/libs/gmp/random.pxd +25 -0
- sage/libs/gmp/randomize.pxd +59 -0
- sage/libs/gmp/types.pxd +53 -0
- sage/libs/gmpxx.pxd +19 -0
- sage/misc/abstract_method.py +276 -0
- sage/misc/all__sagemath_objects.py +43 -0
- sage/misc/bindable_class.py +253 -0
- sage/misc/c3_controlled.cpython-314t-darwin.so +0 -0
- sage/misc/c3_controlled.pxd +2 -0
- sage/misc/c3_controlled.pyx +1402 -0
- sage/misc/cachefunc.cpython-314t-darwin.so +0 -0
- sage/misc/cachefunc.pxd +43 -0
- sage/misc/cachefunc.pyx +3781 -0
- sage/misc/call.py +188 -0
- sage/misc/classcall_metaclass.cpython-314t-darwin.so +0 -0
- sage/misc/classcall_metaclass.pxd +14 -0
- sage/misc/classcall_metaclass.pyx +599 -0
- sage/misc/constant_function.cpython-314t-darwin.so +0 -0
- sage/misc/constant_function.pyx +130 -0
- sage/misc/decorators.py +747 -0
- sage/misc/fast_methods.cpython-314t-darwin.so +0 -0
- sage/misc/fast_methods.pxd +20 -0
- sage/misc/fast_methods.pyx +351 -0
- sage/misc/flatten.py +90 -0
- sage/misc/fpickle.cpython-314t-darwin.so +0 -0
- sage/misc/fpickle.pyx +177 -0
- sage/misc/function_mangling.cpython-314t-darwin.so +0 -0
- sage/misc/function_mangling.pxd +11 -0
- sage/misc/function_mangling.pyx +308 -0
- sage/misc/inherit_comparison.cpython-314t-darwin.so +0 -0
- sage/misc/inherit_comparison.pxd +5 -0
- sage/misc/inherit_comparison.pyx +105 -0
- sage/misc/instancedoc.cpython-314t-darwin.so +0 -0
- sage/misc/instancedoc.pyx +331 -0
- sage/misc/lazy_attribute.cpython-314t-darwin.so +0 -0
- sage/misc/lazy_attribute.pyx +607 -0
- sage/misc/lazy_format.py +135 -0
- sage/misc/lazy_import.cpython-314t-darwin.so +0 -0
- sage/misc/lazy_import.pyx +1299 -0
- sage/misc/lazy_import_cache.py +36 -0
- sage/misc/lazy_list.cpython-314t-darwin.so +0 -0
- sage/misc/lazy_list.pxd +19 -0
- sage/misc/lazy_list.pyx +1187 -0
- sage/misc/lazy_string.cpython-314t-darwin.so +0 -0
- sage/misc/lazy_string.pxd +7 -0
- sage/misc/lazy_string.pyx +546 -0
- sage/misc/misc.py +1066 -0
- sage/misc/misc_c.cpython-314t-darwin.so +0 -0
- sage/misc/misc_c.pxd +3 -0
- sage/misc/misc_c.pyx +766 -0
- sage/misc/namespace_package.py +37 -0
- sage/misc/nested_class.cpython-314t-darwin.so +0 -0
- sage/misc/nested_class.pxd +3 -0
- sage/misc/nested_class.pyx +394 -0
- sage/misc/persist.cpython-314t-darwin.so +0 -0
- sage/misc/persist.pyx +1251 -0
- sage/misc/prandom.py +418 -0
- sage/misc/randstate.cpython-314t-darwin.so +0 -0
- sage/misc/randstate.pxd +30 -0
- sage/misc/randstate.pyx +1059 -0
- sage/misc/repr.py +203 -0
- sage/misc/reset.cpython-314t-darwin.so +0 -0
- sage/misc/reset.pyx +196 -0
- sage/misc/sage_ostools.cpython-314t-darwin.so +0 -0
- sage/misc/sage_ostools.pyx +323 -0
- sage/misc/sage_timeit.py +275 -0
- sage/misc/sage_timeit_class.cpython-314t-darwin.so +0 -0
- sage/misc/sage_timeit_class.pyx +120 -0
- sage/misc/sage_unittest.py +637 -0
- sage/misc/sageinspect.py +2768 -0
- sage/misc/session.cpython-314t-darwin.so +0 -0
- sage/misc/session.pyx +392 -0
- sage/misc/superseded.py +557 -0
- sage/misc/test_nested_class.py +228 -0
- sage/misc/timing.py +264 -0
- sage/misc/unknown.py +222 -0
- sage/misc/verbose.py +253 -0
- sage/misc/weak_dict.cpython-314t-darwin.so +0 -0
- sage/misc/weak_dict.pxd +15 -0
- sage/misc/weak_dict.pyx +1231 -0
- sage/modules/all__sagemath_objects.py +1 -0
- sage/modules/module.cpython-314t-darwin.so +0 -0
- sage/modules/module.pxd +5 -0
- sage/modules/module.pyx +329 -0
- sage/rings/all__sagemath_objects.py +3 -0
- sage/rings/integer_fake.h +22 -0
- sage/rings/integer_fake.pxd +55 -0
- sage/sets/all__sagemath_objects.py +3 -0
- sage/sets/pythonclass.cpython-314t-darwin.so +0 -0
- sage/sets/pythonclass.pxd +9 -0
- sage/sets/pythonclass.pyx +247 -0
- sage/structure/__init__.py +4 -0
- sage/structure/all.py +30 -0
- sage/structure/category_object.cpython-314t-darwin.so +0 -0
- sage/structure/category_object.pxd +28 -0
- sage/structure/category_object.pyx +1087 -0
- sage/structure/coerce.cpython-314t-darwin.so +0 -0
- sage/structure/coerce.pxd +44 -0
- sage/structure/coerce.pyx +2107 -0
- sage/structure/coerce_actions.cpython-314t-darwin.so +0 -0
- sage/structure/coerce_actions.pxd +27 -0
- sage/structure/coerce_actions.pyx +988 -0
- sage/structure/coerce_dict.cpython-314t-darwin.so +0 -0
- sage/structure/coerce_dict.pxd +51 -0
- sage/structure/coerce_dict.pyx +1557 -0
- sage/structure/coerce_exceptions.py +23 -0
- sage/structure/coerce_maps.cpython-314t-darwin.so +0 -0
- sage/structure/coerce_maps.pxd +28 -0
- sage/structure/coerce_maps.pyx +718 -0
- sage/structure/debug_options.cpython-314t-darwin.so +0 -0
- sage/structure/debug_options.pxd +6 -0
- sage/structure/debug_options.pyx +54 -0
- sage/structure/dynamic_class.py +541 -0
- sage/structure/element.cpython-314t-darwin.so +0 -0
- sage/structure/element.pxd +272 -0
- sage/structure/element.pyx +4772 -0
- sage/structure/element_wrapper.cpython-314t-darwin.so +0 -0
- sage/structure/element_wrapper.pxd +12 -0
- sage/structure/element_wrapper.pyx +582 -0
- sage/structure/factorization.py +1422 -0
- sage/structure/factorization_integer.py +105 -0
- sage/structure/factory.cpython-314t-darwin.so +0 -0
- sage/structure/factory.pyx +786 -0
- sage/structure/formal_sum.py +489 -0
- sage/structure/gens_py.py +73 -0
- sage/structure/global_options.py +1743 -0
- sage/structure/indexed_generators.py +863 -0
- sage/structure/list_clone.cpython-314t-darwin.so +0 -0
- sage/structure/list_clone.pxd +65 -0
- sage/structure/list_clone.pyx +1867 -0
- sage/structure/list_clone_demo.cpython-314t-darwin.so +0 -0
- sage/structure/list_clone_demo.pyx +248 -0
- sage/structure/list_clone_timings.py +179 -0
- sage/structure/list_clone_timings_cy.cpython-314t-darwin.so +0 -0
- sage/structure/list_clone_timings_cy.pyx +86 -0
- sage/structure/mutability.cpython-314t-darwin.so +0 -0
- sage/structure/mutability.pxd +21 -0
- sage/structure/mutability.pyx +348 -0
- sage/structure/nonexact.py +69 -0
- sage/structure/parent.cpython-314t-darwin.so +0 -0
- sage/structure/parent.pxd +112 -0
- sage/structure/parent.pyx +3093 -0
- sage/structure/parent_base.cpython-314t-darwin.so +0 -0
- sage/structure/parent_base.pxd +13 -0
- sage/structure/parent_base.pyx +44 -0
- sage/structure/parent_gens.cpython-314t-darwin.so +0 -0
- sage/structure/parent_gens.pxd +22 -0
- sage/structure/parent_gens.pyx +377 -0
- sage/structure/parent_old.cpython-314t-darwin.so +0 -0
- sage/structure/parent_old.pxd +25 -0
- sage/structure/parent_old.pyx +294 -0
- sage/structure/proof/__init__.py +1 -0
- sage/structure/proof/all.py +243 -0
- sage/structure/proof/proof.py +300 -0
- sage/structure/richcmp.cpython-314t-darwin.so +0 -0
- sage/structure/richcmp.pxd +213 -0
- sage/structure/richcmp.pyx +495 -0
- sage/structure/sage_object.cpython-314t-darwin.so +0 -0
- sage/structure/sage_object.pxd +3 -0
- sage/structure/sage_object.pyx +988 -0
- sage/structure/sage_object_test.py +19 -0
- sage/structure/sequence.py +937 -0
- sage/structure/set_factories.py +1178 -0
- sage/structure/set_factories_example.py +527 -0
- sage/structure/support_view.py +179 -0
- sage/structure/test_factory.py +56 -0
- sage/structure/unique_representation.py +1359 -0
|
@@ -0,0 +1,1696 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-objects
|
|
2
|
+
r"""
|
|
3
|
+
Elements, parents, and categories in Sage: a primer
|
|
4
|
+
|
|
5
|
+
.. contents::
|
|
6
|
+
:depth: 2
|
|
7
|
+
:class: this-will-duplicate-information-and-it-is-still-useful-here
|
|
8
|
+
|
|
9
|
+
Abstract
|
|
10
|
+
========
|
|
11
|
+
|
|
12
|
+
The purpose of categories in Sage is to translate the mathematical
|
|
13
|
+
concept of categories (category of groups, of vector spaces, ...)
|
|
14
|
+
into a concrete software engineering design pattern for:
|
|
15
|
+
|
|
16
|
+
- organizing and promoting generic code
|
|
17
|
+
- fostering consistency across the Sage library (naming
|
|
18
|
+
conventions, doc, tests)
|
|
19
|
+
- embedding more mathematical knowledge into the system
|
|
20
|
+
|
|
21
|
+
This design pattern is largely inspired from Axiom and its
|
|
22
|
+
followers (Aldor, Fricas, MuPAD, ...). It differs from those by:
|
|
23
|
+
|
|
24
|
+
- blending in the Magma inspired concept of Parent/Element
|
|
25
|
+
|
|
26
|
+
- being built on top of (and not into) the standard Python object
|
|
27
|
+
oriented and class hierarchy mechanism. This did not require
|
|
28
|
+
changing the language, and could in principle be implemented in
|
|
29
|
+
any language supporting the creation of new classes dynamically.
|
|
30
|
+
|
|
31
|
+
The general philosophy is that *Building mathematical information
|
|
32
|
+
into the system yields more expressive, more conceptual and, at
|
|
33
|
+
the end, easier to maintain and faster code* (within a programming
|
|
34
|
+
realm; this would not necessarily apply to specialized libraries
|
|
35
|
+
like gmp!).
|
|
36
|
+
|
|
37
|
+
One line pitch for mathematicians
|
|
38
|
+
---------------------------------
|
|
39
|
+
|
|
40
|
+
Categories in Sage provide a library of interrelated bookshelves, with
|
|
41
|
+
each bookshelf containing algorithms, tests, documentation, or some
|
|
42
|
+
mathematical facts about the objects of a given category (e.g. groups).
|
|
43
|
+
|
|
44
|
+
One line pitch for programmers
|
|
45
|
+
------------------------------
|
|
46
|
+
|
|
47
|
+
Categories in Sage provide a large hierarchy of abstract classes for
|
|
48
|
+
mathematical objects. To keep it maintainable, the inheritance
|
|
49
|
+
information between the classes is not hardcoded but instead
|
|
50
|
+
reconstructed dynamically from duplication free semantic information.
|
|
51
|
+
|
|
52
|
+
Introduction: Sage as a library of objects and algorithms
|
|
53
|
+
=========================================================
|
|
54
|
+
|
|
55
|
+
The Sage library, with more than one million lines of code,
|
|
56
|
+
documentation, and tests, implements:
|
|
57
|
+
|
|
58
|
+
- Thousands of different kinds of objects (classes):
|
|
59
|
+
|
|
60
|
+
Integers, polynomials, matrices, groups, number fields, elliptic
|
|
61
|
+
curves, permutations, morphisms, languages, ... and a few raccoons ...
|
|
62
|
+
|
|
63
|
+
- Tens of thousands methods and functions:
|
|
64
|
+
|
|
65
|
+
Arithmetic, integer and polynomial factorization, pattern matching
|
|
66
|
+
on words, ...
|
|
67
|
+
|
|
68
|
+
Some challenges
|
|
69
|
+
---------------
|
|
70
|
+
|
|
71
|
+
- How to organize this library?
|
|
72
|
+
|
|
73
|
+
One needs some bookshelves to group together related objects and algorithms.
|
|
74
|
+
|
|
75
|
+
- How to ensure consistency?
|
|
76
|
+
|
|
77
|
+
Similar objects should behave similarly::
|
|
78
|
+
|
|
79
|
+
sage: Permutations(5).cardinality()
|
|
80
|
+
120
|
|
81
|
+
|
|
82
|
+
sage: GL(2,2).cardinality() # needs sage.libs.gap sage.modules
|
|
83
|
+
6
|
|
84
|
+
|
|
85
|
+
sage: A = random_matrix(ZZ, 6, 3, x=7) # needs sage.modules
|
|
86
|
+
sage: L = LatticePolytope(A.rows()) # needs sage.geometry.polyhedron sage.modules
|
|
87
|
+
sage: L.npoints() # oops! # random # needs palp sage.geometry.polyhedron sage.modules
|
|
88
|
+
37
|
|
89
|
+
|
|
90
|
+
- How to ensure robustness?
|
|
91
|
+
|
|
92
|
+
- How to reduce duplication?
|
|
93
|
+
|
|
94
|
+
Example: binary powering::
|
|
95
|
+
|
|
96
|
+
sage: m = 3
|
|
97
|
+
sage: m^8 == m*m*m*m*m*m*m*m == ((m^2)^2)^2
|
|
98
|
+
True
|
|
99
|
+
|
|
100
|
+
::
|
|
101
|
+
|
|
102
|
+
sage: # needs sage.modules, known bug: windows (crash - https://github.com/passagemath/passagemath/issues/1892#issuecomment-3673607183)
|
|
103
|
+
sage: m = random_matrix(QQ, 4, algorithm='echelonizable',
|
|
104
|
+
....: rank=3, upper_bound=60)
|
|
105
|
+
sage: m^8 == m*m*m*m*m*m*m*m == ((m^2)^2)^2
|
|
106
|
+
True
|
|
107
|
+
|
|
108
|
+
We want to implement binary powering only once, as *generic* code
|
|
109
|
+
that will apply in all cases.
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
A bit of help from abstract algebra
|
|
113
|
+
===================================
|
|
114
|
+
|
|
115
|
+
The hierarchy of categories
|
|
116
|
+
---------------------------
|
|
117
|
+
|
|
118
|
+
What makes binary powering work in the above examples? In both cases,
|
|
119
|
+
we have *a set* endowed with a *multiplicative binary operation* which
|
|
120
|
+
is *associative* and which has a unit element. Such a set is called a
|
|
121
|
+
*monoid*, and binary powering (to a nonnegative power) works generally
|
|
122
|
+
for any monoid.
|
|
123
|
+
|
|
124
|
+
Sage knows about monoids::
|
|
125
|
+
|
|
126
|
+
sage: Monoids()
|
|
127
|
+
Category of monoids
|
|
128
|
+
|
|
129
|
+
and sure enough, binary powering is defined there::
|
|
130
|
+
|
|
131
|
+
sage: m._pow_int.__module__ # needs sage.modules
|
|
132
|
+
'sage.categories.monoids'
|
|
133
|
+
|
|
134
|
+
That's our bookshelf! And it's used in many places::
|
|
135
|
+
|
|
136
|
+
sage: GL(2, ZZ) in Monoids() # needs sage.modules
|
|
137
|
+
True
|
|
138
|
+
sage: NN in Monoids()
|
|
139
|
+
True
|
|
140
|
+
|
|
141
|
+
For a less trivial bookshelf we can consider euclidean rings: once we
|
|
142
|
+
know how to do euclidean division in some set `R`, we can compute
|
|
143
|
+
gcd's in `R` generically using the Euclidean algorithm.
|
|
144
|
+
|
|
145
|
+
We are in fact very lucky: abstract algebra provides us right away
|
|
146
|
+
with a large and robust set of bookshelves which is the result of
|
|
147
|
+
centuries of work of mathematicians to identify the important
|
|
148
|
+
concepts. This includes for example::
|
|
149
|
+
|
|
150
|
+
sage: Sets()
|
|
151
|
+
Category of sets
|
|
152
|
+
|
|
153
|
+
sage: Groups()
|
|
154
|
+
Category of groups
|
|
155
|
+
|
|
156
|
+
sage: Rings()
|
|
157
|
+
Category of rings
|
|
158
|
+
|
|
159
|
+
sage: Fields()
|
|
160
|
+
Category of fields
|
|
161
|
+
|
|
162
|
+
sage: HopfAlgebras(QQ)
|
|
163
|
+
Category of Hopf algebras over Rational Field
|
|
164
|
+
|
|
165
|
+
Each of the above is called a *category*. It typically specifies what
|
|
166
|
+
are the operations on the elements, as well as the axioms satisfied by
|
|
167
|
+
those operations. For example the category of groups specifies that a
|
|
168
|
+
group is a set endowed with a binary operation (the multiplication)
|
|
169
|
+
which is associative and admits a unit and inverses.
|
|
170
|
+
|
|
171
|
+
Each set in Sage knows which bookshelf of generic algorithms it can
|
|
172
|
+
use, that is to which category it belongs::
|
|
173
|
+
|
|
174
|
+
sage: G = GL(2, ZZ) # needs sage.modules
|
|
175
|
+
sage: G.category() # needs sage.modules
|
|
176
|
+
Category of infinite groups
|
|
177
|
+
|
|
178
|
+
In fact a group is a semigroup, and Sage knows about this::
|
|
179
|
+
|
|
180
|
+
sage: Groups().is_subcategory(Semigroups())
|
|
181
|
+
True
|
|
182
|
+
sage: G in Semigroups() # needs sage.modules
|
|
183
|
+
True
|
|
184
|
+
|
|
185
|
+
Altogether, our group gets algorithms from a bunch of bookshelves::
|
|
186
|
+
|
|
187
|
+
sage: G.categories() # needs sage.modules
|
|
188
|
+
[Category of infinite groups, Category of groups, Category of monoids,
|
|
189
|
+
...,
|
|
190
|
+
Category of magmas,
|
|
191
|
+
Category of infinite sets, ...]
|
|
192
|
+
|
|
193
|
+
Those can be viewed graphically::
|
|
194
|
+
|
|
195
|
+
sage: g = Groups().category_graph() # needs sage.graphs
|
|
196
|
+
sage: g.set_latex_options(format='dot2tex') # needs sage.graphs sage.modules sage.plot
|
|
197
|
+
sage: view(g) # not tested # needs sage.graphs sage.modules sage.plot
|
|
198
|
+
|
|
199
|
+
In case ``dot2tex`` is not available, you can use instead::
|
|
200
|
+
|
|
201
|
+
sage: g.show(vertex_shape=None, figsize=20) # needs sage.graphs sage.modules sage.plot
|
|
202
|
+
|
|
203
|
+
Here is an overview of all categories in Sage::
|
|
204
|
+
|
|
205
|
+
sage: g = sage.categories.category.category_graph() # needs sage.graphs sage.groups sage.modules
|
|
206
|
+
sage: g.set_latex_options(format='dot2tex') # needs sage.graphs sage.modules sage.plot
|
|
207
|
+
sage: view(g) # not tested # needs sage.graphs sage.modules sage.plot
|
|
208
|
+
|
|
209
|
+
Wrap-up: generic algorithms in Sage are organized in a hierarchy of
|
|
210
|
+
bookshelves modelled upon the usual hierarchy of categories provided
|
|
211
|
+
by abstract algebra.
|
|
212
|
+
|
|
213
|
+
.. _category-primer-parents-elements-categories:
|
|
214
|
+
|
|
215
|
+
Elements, Parents, Categories
|
|
216
|
+
-----------------------------
|
|
217
|
+
|
|
218
|
+
.. RUBRIC:: Parent
|
|
219
|
+
|
|
220
|
+
A *parent* is a Python instance modelling a set of mathematical
|
|
221
|
+
elements together with its additional (algebraic) structure.
|
|
222
|
+
|
|
223
|
+
Examples include the ring of integers, the group `S_3`, the set of
|
|
224
|
+
prime numbers, the set of linear maps between two given vector
|
|
225
|
+
spaces, and a given finite semigroup.
|
|
226
|
+
|
|
227
|
+
These sets are often equipped with additional structure: the set
|
|
228
|
+
of all integers forms a ring. The main way of encoding this
|
|
229
|
+
information is specifying which categories a parent belongs to.
|
|
230
|
+
|
|
231
|
+
It is completely possible to have different Python instances
|
|
232
|
+
modelling the same set of elements. For example, one might want
|
|
233
|
+
to consider the ring of integers, or the poset of integers under
|
|
234
|
+
their standard order, or the poset of integers under divisibility,
|
|
235
|
+
or the semiring of integers under the operations of maximum and
|
|
236
|
+
addition. Each of these would be a different instance, belonging
|
|
237
|
+
to different categories.
|
|
238
|
+
|
|
239
|
+
For a given model, there should be a unique instance in Sage
|
|
240
|
+
representing that parent::
|
|
241
|
+
|
|
242
|
+
sage: IntegerRing() is IntegerRing()
|
|
243
|
+
True
|
|
244
|
+
|
|
245
|
+
.. RUBRIC:: Element
|
|
246
|
+
|
|
247
|
+
An *element* is a Python instance modelling a mathematical element
|
|
248
|
+
of a set.
|
|
249
|
+
|
|
250
|
+
Examples of element include `5` in the integer ring, `x^3 - x` in
|
|
251
|
+
the polynomial ring in `x` over the rationals, `4 + O(3^3)` in the
|
|
252
|
+
3-adics, the transposition `(1 2)` in `S_3`, and the identity
|
|
253
|
+
morphism in the set of linear maps from `\QQ^3` to `\QQ^3`.
|
|
254
|
+
|
|
255
|
+
Every element in Sage has a parent. The standard idiom in Sage
|
|
256
|
+
for creating elements is to create their parent, and then provide
|
|
257
|
+
enough data to define the element::
|
|
258
|
+
|
|
259
|
+
sage: R = PolynomialRing(ZZ, name='x')
|
|
260
|
+
sage: R([1,2,3])
|
|
261
|
+
3*x^2 + 2*x + 1
|
|
262
|
+
|
|
263
|
+
One can also create elements using various methods on the parent
|
|
264
|
+
and arithmetic of elements::
|
|
265
|
+
|
|
266
|
+
sage: x = R.gen()
|
|
267
|
+
sage: 1 + 2*x + 3*x^2
|
|
268
|
+
3*x^2 + 2*x + 1
|
|
269
|
+
|
|
270
|
+
Unlike parents, elements in Sage are not necessarily unique::
|
|
271
|
+
|
|
272
|
+
sage: ZZ(5040) is ZZ(5040)
|
|
273
|
+
False
|
|
274
|
+
|
|
275
|
+
Many parents model algebraic structures, and their elements
|
|
276
|
+
support arithmetic operations. One often further wants to do
|
|
277
|
+
arithmetic by combining elements from different parents: adding
|
|
278
|
+
together integers and rationals for example. Sage supports this
|
|
279
|
+
feature using coercion (see :mod:`sage.structure.coerce` for more
|
|
280
|
+
details).
|
|
281
|
+
|
|
282
|
+
It is possible for a parent to also have simultaneously the
|
|
283
|
+
structure of an element. Consider for example the monoid of all
|
|
284
|
+
finite groups, endowed with the Cartesian product operation.
|
|
285
|
+
Then, every finite group (which is a parent) is also an element of
|
|
286
|
+
this monoid. This is not yet implemented, and the design details
|
|
287
|
+
are not yet fixed but experiments are underway in this direction.
|
|
288
|
+
|
|
289
|
+
.. TODO:: Give a concrete example, typically using :class:`ElementWrapper`.
|
|
290
|
+
|
|
291
|
+
.. RUBRIC:: Category
|
|
292
|
+
|
|
293
|
+
A *category* is a Python instance modelling a mathematical category.
|
|
294
|
+
|
|
295
|
+
Examples of categories include the category of finite semigroups,
|
|
296
|
+
the category of all (Python) objects, the category of
|
|
297
|
+
`\ZZ`-algebras, and the category of Cartesian products of
|
|
298
|
+
`\ZZ`-algebras::
|
|
299
|
+
|
|
300
|
+
sage: FiniteSemigroups()
|
|
301
|
+
Category of finite semigroups
|
|
302
|
+
sage: Objects()
|
|
303
|
+
Category of objects
|
|
304
|
+
sage: Algebras(ZZ)
|
|
305
|
+
Category of algebras over Integer Ring
|
|
306
|
+
sage: Algebras(ZZ).CartesianProducts()
|
|
307
|
+
Category of Cartesian products of algebras over Integer Ring
|
|
308
|
+
|
|
309
|
+
Mind the 's' in the names of the categories above;
|
|
310
|
+
``GroupAlgebra`` and ``GroupAlgebras`` are distinct things.
|
|
311
|
+
|
|
312
|
+
Every parent belongs to a collection of categories. Moreover,
|
|
313
|
+
categories are interrelated by the *super categories*
|
|
314
|
+
relation. For example, the category of rings is a super category
|
|
315
|
+
of the category of fields, because every field is also a ring.
|
|
316
|
+
|
|
317
|
+
A category serves two roles:
|
|
318
|
+
|
|
319
|
+
- to provide a model for the mathematical concept of a category
|
|
320
|
+
and the associated structures: homsets, morphisms, functorial
|
|
321
|
+
constructions, axioms.
|
|
322
|
+
|
|
323
|
+
- to organize and promote generic code, naming conventions,
|
|
324
|
+
documentation, and tests across similar mathematical structures.
|
|
325
|
+
|
|
326
|
+
.. RUBRIC:: CategoryObject
|
|
327
|
+
|
|
328
|
+
Objects of a mathematical category are not necessarily parents.
|
|
329
|
+
Parent has a superclass that provides a means of modeling such.
|
|
330
|
+
|
|
331
|
+
For example, the category of schemes does not have a faithful
|
|
332
|
+
forgetful functor to the category of sets, so it does not make
|
|
333
|
+
sense to talk about schemes as parents.
|
|
334
|
+
|
|
335
|
+
.. RUBRIC:: Morphisms, Homsets
|
|
336
|
+
|
|
337
|
+
As category theorists will expect, *Morphisms* and *Homsets* will
|
|
338
|
+
play an ever more important role, as support for them will
|
|
339
|
+
improve.
|
|
340
|
+
|
|
341
|
+
----
|
|
342
|
+
|
|
343
|
+
Much of the mathematical information in Sage is encoded as relations
|
|
344
|
+
between elements and their parents, parents and their categories, and
|
|
345
|
+
categories and their super categories::
|
|
346
|
+
|
|
347
|
+
sage: 1.parent()
|
|
348
|
+
Integer Ring
|
|
349
|
+
|
|
350
|
+
sage: ZZ
|
|
351
|
+
Integer Ring
|
|
352
|
+
|
|
353
|
+
sage: ZZ.category()
|
|
354
|
+
Join of Category of Dedekind domains
|
|
355
|
+
and Category of euclidean domains
|
|
356
|
+
and Category of noetherian rings
|
|
357
|
+
and Category of infinite enumerated sets
|
|
358
|
+
and Category of metric spaces
|
|
359
|
+
|
|
360
|
+
sage: ZZ.categories()
|
|
361
|
+
[Join of Category of Dedekind domains
|
|
362
|
+
and Category of euclidean domains
|
|
363
|
+
and Category of noetherian rings
|
|
364
|
+
and Category of infinite enumerated sets
|
|
365
|
+
and Category of metric spaces,
|
|
366
|
+
Category of Dedekind domains,
|
|
367
|
+
Category of euclidean domains, Category of principal ideal domains,
|
|
368
|
+
Category of unique factorization domains, Category of gcd domains,
|
|
369
|
+
Category of integral domains, Category of domains, ...
|
|
370
|
+
Category of commutative rings, Category of rings, ...
|
|
371
|
+
Category of magmas and additive magmas, ...
|
|
372
|
+
Category of monoids, Category of semigroups,
|
|
373
|
+
Category of commutative magmas, Category of unital magmas, Category of magmas,
|
|
374
|
+
Category of commutative additive groups, ..., Category of additive magmas,
|
|
375
|
+
Category of infinite enumerated sets, Category of enumerated sets,
|
|
376
|
+
Category of infinite sets, Category of metric spaces,
|
|
377
|
+
Category of topological spaces, Category of sets,
|
|
378
|
+
Category of sets with partial maps,
|
|
379
|
+
Category of objects]
|
|
380
|
+
|
|
381
|
+
sage: g = EuclideanDomains().category_graph() # needs sage.graphs
|
|
382
|
+
sage: g.set_latex_options(format='dot2tex') # needs sage.graphs sage.plot
|
|
383
|
+
sage: view(g) # not tested # needs sage.graphs sage.plot
|
|
384
|
+
|
|
385
|
+
A bit of help from computer science
|
|
386
|
+
===================================
|
|
387
|
+
|
|
388
|
+
Hierarchy of classes
|
|
389
|
+
--------------------
|
|
390
|
+
|
|
391
|
+
How are the bookshelves implemented in practice?
|
|
392
|
+
|
|
393
|
+
Sage uses the classical design paradigm of Object Oriented Programming
|
|
394
|
+
(OOP). Its fundamental principle is that any object that a program is
|
|
395
|
+
to manipulate should be modelled by an *instance* of a *class*. The
|
|
396
|
+
class implements:
|
|
397
|
+
|
|
398
|
+
- a *data structure*: which describes how the object is stored,
|
|
399
|
+
- *methods*: which describe the operations on the object.
|
|
400
|
+
|
|
401
|
+
The instance itself contains the data for the given object, according
|
|
402
|
+
to the specified data structure.
|
|
403
|
+
|
|
404
|
+
Hence, all the objects mentioned above should be instances of some
|
|
405
|
+
classes. For example, an integer in Sage is an instance of the class
|
|
406
|
+
:class:`Integer` (and it knows about it!)::
|
|
407
|
+
|
|
408
|
+
sage: i = 12
|
|
409
|
+
sage: type(i)
|
|
410
|
+
<class 'sage.rings.integer.Integer'>
|
|
411
|
+
|
|
412
|
+
Applying an operation is generally done by *calling a method*::
|
|
413
|
+
|
|
414
|
+
sage: i.factor()
|
|
415
|
+
2^2 * 3
|
|
416
|
+
|
|
417
|
+
sage: # needs sage.symbolic
|
|
418
|
+
sage: x = var('x')
|
|
419
|
+
sage: p = 6*x^2 + 12*x + 6
|
|
420
|
+
sage: type(p)
|
|
421
|
+
<class 'sage.symbolic.expression.Expression'>
|
|
422
|
+
sage: p.factor()
|
|
423
|
+
6*(x + 1)^2
|
|
424
|
+
|
|
425
|
+
sage: # needs sage.symbolic
|
|
426
|
+
sage: R.<x> = PolynomialRing(QQ, sparse=True)
|
|
427
|
+
sage: pQ = R(p)
|
|
428
|
+
sage: type(pQ)
|
|
429
|
+
<class 'sage.rings.polynomial.polynomial_ring.PolynomialRing_field_with_category.element_class'>
|
|
430
|
+
sage: pQ.factor()
|
|
431
|
+
(6) * (x + 1)^2
|
|
432
|
+
|
|
433
|
+
sage: # needs sage.symbolic
|
|
434
|
+
sage: pZ = ZZ['x'](p)
|
|
435
|
+
sage: type(pZ)
|
|
436
|
+
<class 'sage.rings.polynomial.polynomial_integer_dense_flint.Polynomial_integer_dense_flint'>
|
|
437
|
+
sage: pZ.factor()
|
|
438
|
+
2 * 3 * (x + 1)^2
|
|
439
|
+
|
|
440
|
+
Factoring integers, expressions, or polynomials are distinct tasks,
|
|
441
|
+
with completely different algorithms. Yet, from a user (or caller)
|
|
442
|
+
point of view, all those objects can be manipulated alike. This
|
|
443
|
+
illustrates the OOP concepts of *polymorphism*, *data abstraction*,
|
|
444
|
+
and *encapsulation*.
|
|
445
|
+
|
|
446
|
+
Let us be curious, and see where some methods are defined. This can be
|
|
447
|
+
done by introspection::
|
|
448
|
+
|
|
449
|
+
sage: i._mul_?? # not tested
|
|
450
|
+
|
|
451
|
+
For plain Python methods, one can also just ask in which module they
|
|
452
|
+
are implemented::
|
|
453
|
+
|
|
454
|
+
sage: i._pow_.__module__ # not tested (Issue #24275)
|
|
455
|
+
'sage.categories.semigroups'
|
|
456
|
+
|
|
457
|
+
sage: pQ._mul_.__module__ # needs sage.symbolic
|
|
458
|
+
'sage.rings.polynomial.polynomial_element_generic'
|
|
459
|
+
sage: pQ._pow_.__module__ # not tested (Issue #24275) # needs sage.symbolic
|
|
460
|
+
'sage.categories.semigroups'
|
|
461
|
+
|
|
462
|
+
We see that integers and polynomials have each their own
|
|
463
|
+
multiplication method: the multiplication algorithms are indeed
|
|
464
|
+
unrelated and deeply tied to their respective datastructures. On the
|
|
465
|
+
other hand, as we have seen above, they share the same powering method
|
|
466
|
+
because the set `\ZZ` of integers, and the set `\QQ[x]` of
|
|
467
|
+
polynomials are both semigroups. Namely, the class for integers and
|
|
468
|
+
the class for polynomials both derive from an *abstract class* for
|
|
469
|
+
semigroup elements, which factors out the *generic* methods like
|
|
470
|
+
``_pow_``. This illustrates the use of *hierarchy of classes* to share
|
|
471
|
+
common code between classes having common behaviour.
|
|
472
|
+
|
|
473
|
+
OOP design is all about isolating the objects that one wants to model
|
|
474
|
+
together with their operations, and designing an appropriate hierarchy
|
|
475
|
+
of classes for organizing the code. As we have seen above, the design
|
|
476
|
+
of the class hierarchy is easy since it can be modelled upon the
|
|
477
|
+
hierarchy of categories (bookshelves). Here is for example a piece of
|
|
478
|
+
the hierarchy of classes for an element of a group of permutations::
|
|
479
|
+
|
|
480
|
+
sage: P = Permutations(4)
|
|
481
|
+
sage: m = P.an_element()
|
|
482
|
+
sage: for cls in m.__class__.mro(): print(cls)
|
|
483
|
+
<class 'sage.combinat.permutation.StandardPermutations_n_with_category.element_class'>
|
|
484
|
+
<class 'sage.combinat.permutation.StandardPermutations_n.Element'>
|
|
485
|
+
<class 'sage.combinat.permutation.Permutation'>
|
|
486
|
+
...
|
|
487
|
+
<class 'sage.categories.groups.Groups.element_class'>
|
|
488
|
+
<class 'sage.categories.monoids.Monoids.element_class'>
|
|
489
|
+
...
|
|
490
|
+
<class 'sage.categories.semigroups.Semigroups.element_class'>
|
|
491
|
+
...
|
|
492
|
+
|
|
493
|
+
On the top, we see concrete classes that describe the data structure
|
|
494
|
+
for matrices and provide the operations that are tied to this data
|
|
495
|
+
structure. Then follow abstract classes that are attached to the
|
|
496
|
+
hierarchy of categories and provide generic algorithms.
|
|
497
|
+
|
|
498
|
+
The full hierarchy is best viewed graphically::
|
|
499
|
+
|
|
500
|
+
sage: g = class_graph(m.__class__) # needs sage.combinat sage.graphs
|
|
501
|
+
sage: g.set_latex_options(format='dot2tex') # needs sage.combinat sage.graphs sage.plot
|
|
502
|
+
sage: view(g) # not tested # needs sage.combinat sage.graphs sage.plot
|
|
503
|
+
|
|
504
|
+
Parallel hierarchy of classes for parents
|
|
505
|
+
-----------------------------------------
|
|
506
|
+
|
|
507
|
+
Let us recall that we do not just want to compute with elements of
|
|
508
|
+
mathematical sets, but with the sets themselves::
|
|
509
|
+
|
|
510
|
+
sage: ZZ.one()
|
|
511
|
+
1
|
|
512
|
+
|
|
513
|
+
sage: R = QQ['x,y']
|
|
514
|
+
sage: R.krull_dimension()
|
|
515
|
+
2
|
|
516
|
+
sage: A = R.quotient( R.ideal(x^2 - 2) )
|
|
517
|
+
sage: A.krull_dimension() # todo: not implemented
|
|
518
|
+
|
|
519
|
+
Here are some typical operations that one may want to carry on various
|
|
520
|
+
kinds of sets:
|
|
521
|
+
|
|
522
|
+
- The set of permutations of 5, the set of rational points of an
|
|
523
|
+
elliptic curve: counting, listing, random generation
|
|
524
|
+
|
|
525
|
+
- A language (set of words): rationality testing, counting elements,
|
|
526
|
+
generating series
|
|
527
|
+
|
|
528
|
+
- A finite semigroup: left/right ideals, center, representation theory
|
|
529
|
+
|
|
530
|
+
- A vector space, an algebra: Cartesian product, tensor product, quotient
|
|
531
|
+
|
|
532
|
+
Hence, following the OOP fundamental principle, parents should also be
|
|
533
|
+
modelled by instances of some (hierarchy of) classes. For example, our
|
|
534
|
+
group `G` is an instance of the following class::
|
|
535
|
+
|
|
536
|
+
sage: G = GL(2, ZZ) # needs sage.modules
|
|
537
|
+
sage: type(G) # needs sage.libs.gap sage.modules
|
|
538
|
+
<class 'sage.groups.matrix_gps.linear_gap.LinearMatrixGroup_gap_with_category'>
|
|
539
|
+
|
|
540
|
+
Here is a piece of the hierarchy of classes above it::
|
|
541
|
+
|
|
542
|
+
sage: for cls in G.__class__.mro(): print(cls) # needs sage.libs.gap sage.modules
|
|
543
|
+
<class 'sage.groups.matrix_gps.linear_gap.LinearMatrixGroup_gap_with_category'>
|
|
544
|
+
...
|
|
545
|
+
<class 'sage.categories.groups.Groups.parent_class'>
|
|
546
|
+
<class 'sage.categories.monoids.Monoids.parent_class'>
|
|
547
|
+
<class 'sage.categories.semigroups.Semigroups.parent_class'>
|
|
548
|
+
...
|
|
549
|
+
|
|
550
|
+
Note that the hierarchy of abstract classes is again attached to
|
|
551
|
+
categories and parallel to that we had seen for the elements. This is
|
|
552
|
+
best viewed graphically::
|
|
553
|
+
|
|
554
|
+
sage: # needs sage.combinat sage.graphs sage.modules sage.plot
|
|
555
|
+
sage: g = class_graph(m.__class__)
|
|
556
|
+
sage: g.relabel(lambda x: x.replace("_",r"\_"))
|
|
557
|
+
sage: g.set_latex_options(format='dot2tex')
|
|
558
|
+
sage: view(g) # not tested
|
|
559
|
+
|
|
560
|
+
.. NOTE::
|
|
561
|
+
|
|
562
|
+
This is a progress upon systems like Axiom or MuPAD where a parent
|
|
563
|
+
is modelled by the class of its elements; this oversimplification
|
|
564
|
+
leads to confusion between methods on parents and elements, and
|
|
565
|
+
makes parents special; in particular it prevents potentially
|
|
566
|
+
interesting constructions like "groups of groups".
|
|
567
|
+
|
|
568
|
+
Sage categories
|
|
569
|
+
===============
|
|
570
|
+
|
|
571
|
+
Why this business of categories? And to start with, why don't we just
|
|
572
|
+
have a good old hierarchy of classes ``Group``, ``Semigroup``,
|
|
573
|
+
``Magma``, ... ?
|
|
574
|
+
|
|
575
|
+
Dynamic hierarchy of classes
|
|
576
|
+
----------------------------
|
|
577
|
+
|
|
578
|
+
As we have just seen, when we manipulate groups, we actually
|
|
579
|
+
manipulate several kinds of objects:
|
|
580
|
+
|
|
581
|
+
- groups
|
|
582
|
+
- group elements
|
|
583
|
+
- morphisms between groups
|
|
584
|
+
- and even the category of groups itself!
|
|
585
|
+
|
|
586
|
+
Thus, on the group bookshelf, we want to put generic code for each of
|
|
587
|
+
the above. We therefore need three, parallel hierarchies of abstract
|
|
588
|
+
classes:
|
|
589
|
+
|
|
590
|
+
- Group, Monoid, Semigroup, Magma, ...
|
|
591
|
+
- GroupElement, MonoidElement, SemigroupElement, MagmaElement, ...
|
|
592
|
+
- GroupMorphism, MonoidMorphism, SemigroupMorphism, MagmaMorphism, ...
|
|
593
|
+
|
|
594
|
+
(and in fact many more as we will see).
|
|
595
|
+
|
|
596
|
+
We could implement the above hierarchies as usual::
|
|
597
|
+
|
|
598
|
+
class Group(Monoid):
|
|
599
|
+
# generic methods that apply to all groups
|
|
600
|
+
|
|
601
|
+
class GroupElement(MonoidElement):
|
|
602
|
+
# generic methods that apply to all group elements
|
|
603
|
+
|
|
604
|
+
class GroupMorphism(MonoidMorphism):
|
|
605
|
+
# generic methods that apply to all group morphisms
|
|
606
|
+
|
|
607
|
+
And indeed that's how it was done in Sage before 2009, and there are
|
|
608
|
+
still many traces of this. The drawback of this approach is
|
|
609
|
+
duplication: the fact that a group is a monoid is repeated three times
|
|
610
|
+
above!
|
|
611
|
+
|
|
612
|
+
Instead, Sage now uses the following syntax, where the :class:`Groups`
|
|
613
|
+
bookshelf is structured into units with *nested classes*::
|
|
614
|
+
|
|
615
|
+
class Groups(Category):
|
|
616
|
+
|
|
617
|
+
def super_categories(self):
|
|
618
|
+
return [Monoids(), ...]
|
|
619
|
+
|
|
620
|
+
class ParentMethods:
|
|
621
|
+
# generic methods that apply to all groups
|
|
622
|
+
|
|
623
|
+
class ElementMethods:
|
|
624
|
+
# generic methods that apply to all group elements
|
|
625
|
+
|
|
626
|
+
class MorphismMethods:
|
|
627
|
+
# generic methods that apply to all group morphisms (not yet implemented)
|
|
628
|
+
|
|
629
|
+
class SubcategoryMethods:
|
|
630
|
+
# generic methods that apply to all subcategories of Groups()
|
|
631
|
+
|
|
632
|
+
With this syntax, the information that a group is a monoid is
|
|
633
|
+
specified only once, in the :meth:`Category.super_categories`
|
|
634
|
+
method. And indeed, when the category of inverse unital magmas was
|
|
635
|
+
introduced, there was a *single point of truth* to update in order to
|
|
636
|
+
reflect the fact that a group is an inverse unital magma::
|
|
637
|
+
|
|
638
|
+
sage: Groups().super_categories()
|
|
639
|
+
[Category of monoids, Category of inverse unital magmas]
|
|
640
|
+
|
|
641
|
+
The price to pay (there is no free lunch) is that some magic is
|
|
642
|
+
required to construct the actual hierarchy of classes for parents,
|
|
643
|
+
elements, and morphisms. Namely, ``Groups.ElementMethods`` should be
|
|
644
|
+
seen as just a bag of methods, and the actual class
|
|
645
|
+
``Groups().element_class`` is constructed from it by adding the
|
|
646
|
+
appropriate super classes according to
|
|
647
|
+
``Groups().super_categories()``::
|
|
648
|
+
|
|
649
|
+
sage: Groups().element_class
|
|
650
|
+
<class 'sage.categories.groups.Groups.element_class'>
|
|
651
|
+
|
|
652
|
+
sage: Groups().element_class.__bases__
|
|
653
|
+
(<class 'sage.categories.monoids.Monoids.element_class'>,
|
|
654
|
+
<class 'sage.categories.magmas.Magmas.Unital.Inverse.element_class'>)
|
|
655
|
+
|
|
656
|
+
We now see that the hierarchy of classes for parents and elements is
|
|
657
|
+
parallel to the hierarchy of categories::
|
|
658
|
+
|
|
659
|
+
sage: Groups().all_super_categories()
|
|
660
|
+
[Category of groups,
|
|
661
|
+
Category of monoids,
|
|
662
|
+
Category of semigroups,
|
|
663
|
+
...
|
|
664
|
+
Category of magmas,
|
|
665
|
+
Category of sets,
|
|
666
|
+
...]
|
|
667
|
+
|
|
668
|
+
sage: for cls in Groups().element_class.mro(): print(cls)
|
|
669
|
+
<class 'sage.categories.groups.Groups.element_class'>
|
|
670
|
+
<class 'sage.categories.monoids.Monoids.element_class'>
|
|
671
|
+
<class 'sage.categories.semigroups.Semigroups.element_class'>
|
|
672
|
+
...
|
|
673
|
+
<class 'sage.categories.magmas.Magmas.element_class'>
|
|
674
|
+
...
|
|
675
|
+
sage: for cls in Groups().parent_class.mro(): print(cls)
|
|
676
|
+
<class 'sage.categories.groups.Groups.parent_class'>
|
|
677
|
+
<class 'sage.categories.monoids.Monoids.parent_class'>
|
|
678
|
+
<class 'sage.categories.semigroups.Semigroups.parent_class'>
|
|
679
|
+
...
|
|
680
|
+
<class 'sage.categories.magmas.Magmas.parent_class'>
|
|
681
|
+
...
|
|
682
|
+
|
|
683
|
+
Another advantage of building the hierarchy of classes dynamically is
|
|
684
|
+
that, for parametrized categories, the hierarchy may depend on the
|
|
685
|
+
parameters. For example an algebra over `\QQ` is a `\QQ`-vector space,
|
|
686
|
+
but an algebra over `\ZZ` is not (it is just a `\ZZ`-module)!
|
|
687
|
+
|
|
688
|
+
.. NOTE::
|
|
689
|
+
|
|
690
|
+
At this point this whole infrastructure may feel like
|
|
691
|
+
overdesigning, right? We felt like this too! But we will see later
|
|
692
|
+
that, once one gets used to it, this approach scales very
|
|
693
|
+
naturally.
|
|
694
|
+
|
|
695
|
+
From a computer science point of view, this infrastructure
|
|
696
|
+
implements, on top of standard multiple inheritance, a dynamic
|
|
697
|
+
composition mechanism of mixin classes (:wikipedia:`Mixin`),
|
|
698
|
+
governed by mathematical properties.
|
|
699
|
+
|
|
700
|
+
For implementation details on how the hierarchy of classes for
|
|
701
|
+
parents and elements is constructed, see :class:`Category`.
|
|
702
|
+
|
|
703
|
+
|
|
704
|
+
.. _category-primer-subcategory:
|
|
705
|
+
|
|
706
|
+
On the category hierarchy: subcategories and super categories
|
|
707
|
+
-------------------------------------------------------------
|
|
708
|
+
|
|
709
|
+
We have seen above that, for example, the category of sets is a super
|
|
710
|
+
category of the category of groups. This models the fact that a group
|
|
711
|
+
can be unambiguously considered as a set by forgetting its group
|
|
712
|
+
operation. In object-oriented parlance, we want the relation "a group
|
|
713
|
+
*is a* set", so that groups can directly inherit code implemented on
|
|
714
|
+
sets.
|
|
715
|
+
|
|
716
|
+
Formally, a category ``Cs()`` is a *super category* of a category
|
|
717
|
+
``Ds()`` if Sage considers any object of ``Ds()`` to be an object of
|
|
718
|
+
``Cs()``, up to an implicit application of a canonical functor from
|
|
719
|
+
``Ds()`` to ``Cs()``. This functor is normally an inclusion of
|
|
720
|
+
categories or a forgetful functor. Reciprocally, ``Ds()`` is said to
|
|
721
|
+
be a *subcategory* of ``Cs()``.
|
|
722
|
+
|
|
723
|
+
.. WARNING::
|
|
724
|
+
|
|
725
|
+
This terminology deviates from the usual mathematical definition
|
|
726
|
+
of *subcategory* and is subject to change. Indeed, the forgetful
|
|
727
|
+
functor from the category of groups to the category of sets is not
|
|
728
|
+
an inclusion of categories, as it is not injective: a given set
|
|
729
|
+
may admit more than one group structure. See :issue:`16183` for
|
|
730
|
+
more details. The name *supercategory* is also used with a
|
|
731
|
+
different meaning in certain areas of mathematics.
|
|
732
|
+
|
|
733
|
+
Categories are instances and have operations
|
|
734
|
+
--------------------------------------------
|
|
735
|
+
|
|
736
|
+
Note that categories themselves are naturally modelled by instances
|
|
737
|
+
because they can have operations of their own. An important one is::
|
|
738
|
+
|
|
739
|
+
sage: Groups().example() # needs sage.modules
|
|
740
|
+
General Linear Group of degree 4 over Rational Field
|
|
741
|
+
|
|
742
|
+
which gives an example of object of the category. Besides illustrating
|
|
743
|
+
the category, the example provides a minimal template for implementing
|
|
744
|
+
a new object in the category::
|
|
745
|
+
|
|
746
|
+
sage: S = Semigroups().example(); S
|
|
747
|
+
An example of a semigroup: the left zero semigroup
|
|
748
|
+
|
|
749
|
+
Its source code can be obtained by introspection::
|
|
750
|
+
|
|
751
|
+
sage: S?? # not tested
|
|
752
|
+
|
|
753
|
+
This example is also typically used for testing generic methods. See
|
|
754
|
+
:meth:`Category.example` for more.
|
|
755
|
+
|
|
756
|
+
Other operations on categories include querying the super categories
|
|
757
|
+
or the axioms satisfied by the operations of a category::
|
|
758
|
+
|
|
759
|
+
sage: Groups().super_categories()
|
|
760
|
+
[Category of monoids, Category of inverse unital magmas]
|
|
761
|
+
sage: Groups().axioms()
|
|
762
|
+
frozenset({'Associative', 'Inverse', 'Unital'})
|
|
763
|
+
|
|
764
|
+
or constructing the intersection of two categories, or the smallest
|
|
765
|
+
category containing them::
|
|
766
|
+
|
|
767
|
+
sage: Groups() & FiniteSets()
|
|
768
|
+
Category of finite groups
|
|
769
|
+
sage: Algebras(QQ) | Groups()
|
|
770
|
+
Category of monoids
|
|
771
|
+
|
|
772
|
+
Specifications and generic documentation
|
|
773
|
+
----------------------------------------
|
|
774
|
+
|
|
775
|
+
Categories do not only contain code but also the specifications of the
|
|
776
|
+
operations. In particular a list of mandatory and optional methods to
|
|
777
|
+
be implemented can be found by introspection with::
|
|
778
|
+
|
|
779
|
+
sage: Groups().required_methods()
|
|
780
|
+
{'element': {'optional': ['_mul_'], 'required': []},
|
|
781
|
+
'parent': {'optional': [], 'required': ['__contains__']}}
|
|
782
|
+
|
|
783
|
+
Documentation about those methods can be obtained with::
|
|
784
|
+
|
|
785
|
+
sage: G = Groups()
|
|
786
|
+
sage: G.element_class._mul_? # not tested
|
|
787
|
+
sage: G.parent_class.one? # not tested
|
|
788
|
+
|
|
789
|
+
See also the :func:`abstract_method` decorator.
|
|
790
|
+
|
|
791
|
+
.. WARNING::
|
|
792
|
+
|
|
793
|
+
Well, more precisely, that's how things should be, but there is
|
|
794
|
+
still some work to do in this direction. For example, the inverse
|
|
795
|
+
operation is not specified above. Also, we are still missing a
|
|
796
|
+
good programmatic syntax to specify the input and output types of
|
|
797
|
+
the methods. Finally, in many cases the implementer must provide
|
|
798
|
+
at least one of two methods, each having a default implementation
|
|
799
|
+
using the other one (e.g. listing or iterating for a finite
|
|
800
|
+
enumerated set); there is currently no good programmatic way to
|
|
801
|
+
specify this.
|
|
802
|
+
|
|
803
|
+
Generic tests
|
|
804
|
+
-------------
|
|
805
|
+
|
|
806
|
+
Another feature that parents and elements receive from categories is
|
|
807
|
+
generic tests; their purpose is to check (at least to some extent)
|
|
808
|
+
that the parent satisfies the required mathematical properties (is my
|
|
809
|
+
semigroup indeed associative?) and is implemented according to the
|
|
810
|
+
specifications (does the method ``an_element`` indeed return an
|
|
811
|
+
element of the parent?)::
|
|
812
|
+
|
|
813
|
+
sage: S = FiniteSemigroups().example(alphabet=('a', 'b'))
|
|
814
|
+
sage: TestSuite(S).run(verbose = True)
|
|
815
|
+
running ._test_an_element() . . . pass
|
|
816
|
+
running ._test_associativity() . . . pass
|
|
817
|
+
running ._test_cardinality() . . . pass
|
|
818
|
+
running ._test_category() . . . pass
|
|
819
|
+
running ._test_construction() . . . pass
|
|
820
|
+
running ._test_elements() . . .
|
|
821
|
+
Running the test suite of self.an_element()
|
|
822
|
+
running ._test_category() . . . pass
|
|
823
|
+
running ._test_eq() . . . pass
|
|
824
|
+
running ._test_new() . . . pass
|
|
825
|
+
running ._test_not_implemented_methods() . . . pass
|
|
826
|
+
running ._test_pickling() . . . pass
|
|
827
|
+
pass
|
|
828
|
+
running ._test_elements_eq_reflexive() . . . pass
|
|
829
|
+
running ._test_elements_eq_symmetric() . . . pass
|
|
830
|
+
running ._test_elements_eq_transitive() . . . pass
|
|
831
|
+
running ._test_elements_neq() . . . pass
|
|
832
|
+
running ._test_enumerated_set_contains() . . . pass
|
|
833
|
+
running ._test_enumerated_set_iter_cardinality() . . . pass
|
|
834
|
+
running ._test_enumerated_set_iter_list() . . . pass
|
|
835
|
+
running ._test_eq() . . . pass
|
|
836
|
+
running ._test_new() . . . pass
|
|
837
|
+
running ._test_not_implemented_methods() . . . pass
|
|
838
|
+
running ._test_pickling() . . . pass
|
|
839
|
+
running ._test_some_elements() . . . pass
|
|
840
|
+
|
|
841
|
+
Tests can be run individually::
|
|
842
|
+
|
|
843
|
+
sage: S._test_associativity()
|
|
844
|
+
|
|
845
|
+
Here is how to access the code of this test::
|
|
846
|
+
|
|
847
|
+
sage: S._test_associativity?? # not tested
|
|
848
|
+
|
|
849
|
+
Here is how to run the test on all elements::
|
|
850
|
+
|
|
851
|
+
sage: L = S.list()
|
|
852
|
+
sage: S._test_associativity(elements=L)
|
|
853
|
+
|
|
854
|
+
See :class:`TestSuite` for more information.
|
|
855
|
+
|
|
856
|
+
Let us see what happens when a test fails. Here we redefine the
|
|
857
|
+
product of `S` to something definitely not associative::
|
|
858
|
+
|
|
859
|
+
sage: S.product = lambda x, y: S("("+x.value +y.value+")")
|
|
860
|
+
|
|
861
|
+
And rerun the test::
|
|
862
|
+
|
|
863
|
+
sage: S._test_associativity(elements=L)
|
|
864
|
+
Traceback (most recent call last):
|
|
865
|
+
...
|
|
866
|
+
File ".../sage/categories/semigroups.py", line ..., in _test_associativity
|
|
867
|
+
tester.assertTrue((x * y) * z == x * (y * z))
|
|
868
|
+
...
|
|
869
|
+
AssertionError: '((aa)a)' != '(a(aa))'
|
|
870
|
+
|
|
871
|
+
We can recover instantly the actual values of ``x``, ``y``, ``z``, that is,
|
|
872
|
+
a counterexample to the associativity of our broken semigroup, using post
|
|
873
|
+
mortem introspection with the Python debugger ``pdb`` (this does not
|
|
874
|
+
work yet in the notebook)::
|
|
875
|
+
|
|
876
|
+
sage: import pdb
|
|
877
|
+
sage: pdb.pm() # not tested
|
|
878
|
+
> /opt/sage-5.11.rc1/local/lib/python/unittest/case.py(424)assertTrue()
|
|
879
|
+
-> raise self.failureException(msg)
|
|
880
|
+
(Pdb) u
|
|
881
|
+
> /opt/sage-5.11.rc1/local/lib/python2.7/site-packages/sage/categories/semigroups.py(145)_test_associativity()
|
|
882
|
+
-> tester.assertTrue((x * y) * z == x * (y * z))
|
|
883
|
+
(Pdb) p x, y, z
|
|
884
|
+
('a', 'a', 'a')
|
|
885
|
+
(Pdb) p (x * y) * z
|
|
886
|
+
'((aa)a)'
|
|
887
|
+
(Pdb) p x * (y * z)
|
|
888
|
+
'(a(aa))'
|
|
889
|
+
|
|
890
|
+
Wrap-up
|
|
891
|
+
-------
|
|
892
|
+
|
|
893
|
+
- Categories provide a natural hierarchy of bookshelves to organize
|
|
894
|
+
not only code, but also specifications and testing tools.
|
|
895
|
+
|
|
896
|
+
- Everything about, say, algebras with a distinguished basis is
|
|
897
|
+
gathered in :class:`AlgebrasWithBasis` or its super categories.
|
|
898
|
+
This includes properties and algorithms for elements, parents,
|
|
899
|
+
morphisms, but also, as we will see, for constructions like
|
|
900
|
+
Cartesian products or quotients.
|
|
901
|
+
|
|
902
|
+
- The mathematical relations between elements, parents, and categories
|
|
903
|
+
translate dynamically into a traditional hierarchy of classes.
|
|
904
|
+
|
|
905
|
+
- This design enforces robustness and consistency, which is
|
|
906
|
+
particularly welcome given that Python is an interpreted language
|
|
907
|
+
without static type checking.
|
|
908
|
+
|
|
909
|
+
Case study
|
|
910
|
+
==========
|
|
911
|
+
|
|
912
|
+
In this section, we study an existing parent in detail; a good followup is to
|
|
913
|
+
go through the :mod:`sage.categories.tutorial` or the thematic tutorial on
|
|
914
|
+
coercion and categories ("How to implement new algebraic structures in Sage")
|
|
915
|
+
to learn how to implement a new one!
|
|
916
|
+
|
|
917
|
+
We consider the example of finite semigroup provided by the category::
|
|
918
|
+
|
|
919
|
+
sage: S = FiniteSemigroups().example(); S
|
|
920
|
+
An example of a finite semigroup: the left regular band generated by ('a', 'b', 'c', 'd')
|
|
921
|
+
sage: S? # not tested
|
|
922
|
+
|
|
923
|
+
Where do all the operations on ``S`` and its elements come from?
|
|
924
|
+
|
|
925
|
+
::
|
|
926
|
+
|
|
927
|
+
sage: x = S('a')
|
|
928
|
+
|
|
929
|
+
``_repr_`` is a technical method which comes with the data structure
|
|
930
|
+
(:class:`ElementWrapper`); since it's implemented in Cython, we need
|
|
931
|
+
to use Sage's introspection tools to recover where it's implemented::
|
|
932
|
+
|
|
933
|
+
sage: x._repr_.__module__
|
|
934
|
+
sage: sage.misc.sageinspect.sage_getfile(x._repr_)
|
|
935
|
+
'.../sage/structure/element_wrapper.pyx'
|
|
936
|
+
|
|
937
|
+
``_pow_int`` is a generic method for all finite semigroups::
|
|
938
|
+
|
|
939
|
+
sage: x._pow_int.__module__
|
|
940
|
+
'sage.categories.semigroups'
|
|
941
|
+
|
|
942
|
+
``__mul__`` is a generic method provided by the :class:`Magmas`
|
|
943
|
+
category (a *magma* is a set with an inner law `*`, not necessarily
|
|
944
|
+
associative). If the two arguments are in the same parent, it will
|
|
945
|
+
call the method ``_mul_``, and otherwise let the :mod:`coercion model
|
|
946
|
+
<sage.structure.coerce>` try to discover how to do the
|
|
947
|
+
multiplication::
|
|
948
|
+
|
|
949
|
+
sage: x.__mul__?? # not tested
|
|
950
|
+
|
|
951
|
+
Since it is a speed critical method, it is implemented in Cython
|
|
952
|
+
in a separate file::
|
|
953
|
+
|
|
954
|
+
sage: x._mul_.__module__
|
|
955
|
+
'sage.categories.coercion_methods'
|
|
956
|
+
|
|
957
|
+
``_mul_`` is a default implementation, also provided by the
|
|
958
|
+
:class:`Magmas` category, that delegates the work to the method
|
|
959
|
+
``product`` of the parent (following the advice: if you do not know
|
|
960
|
+
what to do, ask your parent); it's also a speed critical method::
|
|
961
|
+
|
|
962
|
+
sage: x._mul_?? # not tested
|
|
963
|
+
sage: x._mul_.__module__
|
|
964
|
+
'sage.categories.coercion_methods'
|
|
965
|
+
sage: x._mul_.__func__ is Magmas.ElementMethods._mul_parent
|
|
966
|
+
True
|
|
967
|
+
|
|
968
|
+
``product`` is a mathematical method implemented by the parent::
|
|
969
|
+
|
|
970
|
+
sage: S.product.__module__
|
|
971
|
+
'sage.categories.examples.finite_semigroups'
|
|
972
|
+
|
|
973
|
+
``cayley_graph`` is a generic method on the parent, provided by the
|
|
974
|
+
:class:`FiniteSemigroups` category::
|
|
975
|
+
|
|
976
|
+
sage: S.cayley_graph.__module__
|
|
977
|
+
'sage.categories.semigroups'
|
|
978
|
+
|
|
979
|
+
``multiplication_table`` is a generic method on the parent, provided
|
|
980
|
+
by the :class:`Magmas` category (it does not require associativity)::
|
|
981
|
+
|
|
982
|
+
sage: S.multiplication_table.__module__
|
|
983
|
+
'sage.categories.magmas'
|
|
984
|
+
|
|
985
|
+
Consider now the implementation of the semigroup::
|
|
986
|
+
|
|
987
|
+
sage: S?? # not tested
|
|
988
|
+
|
|
989
|
+
This implementation specifies a data structure for the parents and the
|
|
990
|
+
elements, and makes a promise: the implemented parent is a finite
|
|
991
|
+
semigroup. Then it fulfills the promise by implementing the basic
|
|
992
|
+
operation ``product``. It also implements the optional method
|
|
993
|
+
``semigroup_generators``. In exchange, `S` and its elements receive
|
|
994
|
+
generic implementations of all the other operations. `S` may override
|
|
995
|
+
any of those by more efficient ones. It may typically implement the
|
|
996
|
+
element method ``is_idempotent`` to always return ``True``.
|
|
997
|
+
|
|
998
|
+
A (not yet complete) list of mandatory and optional methods to be
|
|
999
|
+
implemented can be found by introspection with::
|
|
1000
|
+
|
|
1001
|
+
sage: FiniteSemigroups().required_methods()
|
|
1002
|
+
{'element': {'optional': ['_mul_'], 'required': []},
|
|
1003
|
+
'parent': {'optional': ['semigroup_generators'],
|
|
1004
|
+
'required': ['__contains__']}}
|
|
1005
|
+
|
|
1006
|
+
``product`` does not appear in the list because a default implementation
|
|
1007
|
+
is provided in term of the method ``_mul_`` on elements. Of course, at
|
|
1008
|
+
least one of them should be implemented. On the other hand, a default
|
|
1009
|
+
implementation for ``__contains__`` is provided by :class:`Parent`.
|
|
1010
|
+
|
|
1011
|
+
Documentation about those methods can be obtained with::
|
|
1012
|
+
|
|
1013
|
+
sage: C = FiniteSemigroups().element_class
|
|
1014
|
+
sage: C._mul_? # not tested
|
|
1015
|
+
|
|
1016
|
+
See also the :func:`~sage.misc.abstract_method.abstract_method` decorator.
|
|
1017
|
+
|
|
1018
|
+
Here is the code for the finite semigroups category::
|
|
1019
|
+
|
|
1020
|
+
sage: FiniteSemigroups?? # not tested
|
|
1021
|
+
|
|
1022
|
+
|
|
1023
|
+
Specifying the category of a parent
|
|
1024
|
+
===================================
|
|
1025
|
+
|
|
1026
|
+
Some parent constructors (not enough!) allow to specify the desired
|
|
1027
|
+
category for the parent. This can typically be used to specify
|
|
1028
|
+
additional properties of the parent that we know to hold a priori. For
|
|
1029
|
+
example, permutation groups are by default in the category of finite
|
|
1030
|
+
permutation groups (no surprise)::
|
|
1031
|
+
|
|
1032
|
+
sage: P = PermutationGroup([[(1,2,3)]]); P # needs sage.groups
|
|
1033
|
+
Permutation Group with generators [(1,2,3)]
|
|
1034
|
+
sage: P.category() # needs sage.groups
|
|
1035
|
+
Category of finite enumerated permutation groups
|
|
1036
|
+
|
|
1037
|
+
In this case, the group is commutative, so we can specify this::
|
|
1038
|
+
|
|
1039
|
+
sage: P = PermutationGroup([[(1,2,3)]], # needs sage.groups
|
|
1040
|
+
....: category=PermutationGroups().Finite().Commutative()); P
|
|
1041
|
+
Permutation Group with generators [(1,2,3)]
|
|
1042
|
+
sage: P.category() # needs sage.groups
|
|
1043
|
+
Category of finite enumerated commutative permutation groups
|
|
1044
|
+
|
|
1045
|
+
This feature can even be used, typically in experimental code, to add
|
|
1046
|
+
more structure to existing parents, and in particular to add methods
|
|
1047
|
+
for the parents or the elements, without touching the code base::
|
|
1048
|
+
|
|
1049
|
+
sage: class Foos(Category):
|
|
1050
|
+
....: def super_categories(self):
|
|
1051
|
+
....: return [PermutationGroups().Finite().Commutative()]
|
|
1052
|
+
....: class ParentMethods:
|
|
1053
|
+
....: def foo(self): print("foo")
|
|
1054
|
+
....: class ElementMethods:
|
|
1055
|
+
....: def bar(self): print("bar")
|
|
1056
|
+
|
|
1057
|
+
sage: # needs sage.groups
|
|
1058
|
+
sage: P = PermutationGroup([[(1,2,3)]], category=Foos())
|
|
1059
|
+
sage: P.foo()
|
|
1060
|
+
foo
|
|
1061
|
+
sage: p = P.an_element()
|
|
1062
|
+
sage: p.bar()
|
|
1063
|
+
bar
|
|
1064
|
+
|
|
1065
|
+
In the long run, it would be thinkable to use this idiom to implement
|
|
1066
|
+
forgetful functors; for example the above group could be constructed
|
|
1067
|
+
as a plain set with::
|
|
1068
|
+
|
|
1069
|
+
sage: P = PermutationGroup([[(1,2,3)]], category=Sets()) # not implemented, needs sage.groups
|
|
1070
|
+
|
|
1071
|
+
At this stage though, this is still to be explored for robustness
|
|
1072
|
+
and practicality. For now, most parents that accept a category argument
|
|
1073
|
+
only accept a subcategory of the default one.
|
|
1074
|
+
|
|
1075
|
+
Scaling further: functorial constructions, axioms, ...
|
|
1076
|
+
======================================================
|
|
1077
|
+
|
|
1078
|
+
In this section, we explore more advanced features of categories.
|
|
1079
|
+
Along the way, we illustrate that a large hierarchy of categories is
|
|
1080
|
+
desirable to model complicated mathematics, and that scaling to
|
|
1081
|
+
support such a large hierarchy is the driving motivation for the
|
|
1082
|
+
design of the category infrastructure.
|
|
1083
|
+
|
|
1084
|
+
.. _category-primer-functorial-constructions:
|
|
1085
|
+
|
|
1086
|
+
Functorial constructions
|
|
1087
|
+
------------------------
|
|
1088
|
+
|
|
1089
|
+
Sage has support for a certain number of so-called *covariant
|
|
1090
|
+
functorial constructions* which can be used to construct new parents
|
|
1091
|
+
from existing ones while carrying over as much as possible of their
|
|
1092
|
+
algebraic structure. This includes:
|
|
1093
|
+
|
|
1094
|
+
- Cartesian products:
|
|
1095
|
+
See :const:`~sage.categories.cartesian_product.cartesian_product`.
|
|
1096
|
+
|
|
1097
|
+
- Tensor products:
|
|
1098
|
+
See :const:`~sage.categories.tensor.tensor`.
|
|
1099
|
+
|
|
1100
|
+
- Subquotients / quotients / subobjects / isomorphic objects:
|
|
1101
|
+
See:
|
|
1102
|
+
|
|
1103
|
+
- :meth:`Sets().Subquotients <Sets.SubcategoryMethods.Subquotients>`,
|
|
1104
|
+
- :meth:`Sets().Quotients <Sets.SubcategoryMethods.Quotients>`,
|
|
1105
|
+
- :meth:`Sets().Subobjects <Sets.SubcategoryMethods.Subobjects>`,
|
|
1106
|
+
- :meth:`Sets().IsomorphicObjects <Sets.SubcategoryMethods.IsomorphicObjects>`
|
|
1107
|
+
|
|
1108
|
+
- Dual objects:
|
|
1109
|
+
See :meth:`Modules().DualObjects <Modules.SubcategoryMethods.DualObjects>`.
|
|
1110
|
+
|
|
1111
|
+
- Algebras, as in group algebras, monoid algebras, ...:
|
|
1112
|
+
See: :meth:`Sets.ParentMethods.algebra`.
|
|
1113
|
+
|
|
1114
|
+
Let for example `A` and `B` be two parents, and let us construct the
|
|
1115
|
+
Cartesian product `A \times B \times B`::
|
|
1116
|
+
|
|
1117
|
+
sage: A = AlgebrasWithBasis(QQ).example(); A.rename('A') # needs sage.combinat sage.modules
|
|
1118
|
+
sage: B = HopfAlgebrasWithBasis(QQ).example(); B.rename('B') # needs sage.groups sage.modules
|
|
1119
|
+
sage: C = cartesian_product([A, B, B]); C # needs sage.combinat sage.groups sage.modules
|
|
1120
|
+
A (+) B (+) B
|
|
1121
|
+
|
|
1122
|
+
In which category should this new parent be? Since `A` and `B` are
|
|
1123
|
+
vector spaces, the result is, as a vector space, the direct sum
|
|
1124
|
+
`A \oplus B \oplus B`, hence the notation. Also, since both `A` and `B`
|
|
1125
|
+
are monoids, `A \times B \times B` is naturally endowed with a monoid
|
|
1126
|
+
structure for pointwise multiplication::
|
|
1127
|
+
|
|
1128
|
+
sage: C in Monoids() # needs sage.combinat sage.groups sage.modules
|
|
1129
|
+
True
|
|
1130
|
+
|
|
1131
|
+
the unit being the Cartesian product of the units of the operands::
|
|
1132
|
+
|
|
1133
|
+
sage: C.one() # needs sage.combinat sage.groups sage.modules
|
|
1134
|
+
B[(0, word: )] + B[(1, ())] + B[(2, ())]
|
|
1135
|
+
sage: cartesian_product([A.one(), B.one(), B.one()]) # needs sage.combinat sage.groups sage.modules
|
|
1136
|
+
B[(0, word: )] + B[(1, ())] + B[(2, ())]
|
|
1137
|
+
|
|
1138
|
+
The pointwise product can be implemented generically for all magmas
|
|
1139
|
+
(i.e. sets endowed with a multiplicative operation) that are
|
|
1140
|
+
constructed as Cartesian products. It's thus implemented in the
|
|
1141
|
+
:class:`Magmas` category::
|
|
1142
|
+
|
|
1143
|
+
sage: C.product.__module__ # needs sage.combinat sage.groups sage.modules
|
|
1144
|
+
'sage.categories.magmas'
|
|
1145
|
+
|
|
1146
|
+
More specifically, keeping on using nested classes to structure the
|
|
1147
|
+
code, the product method is put in the nested class
|
|
1148
|
+
:class:`Magmas.CartesianProducts.ParentMethods`::
|
|
1149
|
+
|
|
1150
|
+
class Magmas(Category):
|
|
1151
|
+
class ParentMethods:
|
|
1152
|
+
# methods for magmas
|
|
1153
|
+
class ElementMethods:
|
|
1154
|
+
# methods for elements of magmas
|
|
1155
|
+
class CartesianProduct(CartesianProductCategory):
|
|
1156
|
+
class ParentMethods:
|
|
1157
|
+
# methods for magmas that are constructed as Cartesian products
|
|
1158
|
+
def product(self, x, y):
|
|
1159
|
+
# ...
|
|
1160
|
+
class ElementMethods:
|
|
1161
|
+
# ...
|
|
1162
|
+
|
|
1163
|
+
.. NOTE::
|
|
1164
|
+
|
|
1165
|
+
The support for nested classes in Python is relatively
|
|
1166
|
+
recent. Their intensive use for the category infrastructure did
|
|
1167
|
+
reveal some glitches in their implementation, in particular around
|
|
1168
|
+
class naming and introspection. Sage currently works around the
|
|
1169
|
+
more annoying ones but some remain visible. See
|
|
1170
|
+
e.g. :mod:`sage.misc.test_nested_class`.
|
|
1171
|
+
|
|
1172
|
+
|
|
1173
|
+
Let us now look at the categories of ``C``::
|
|
1174
|
+
|
|
1175
|
+
sage: C.categories() # needs sage.combinat sage.groups sage.modules
|
|
1176
|
+
[Category of finite dimensional Cartesian products of algebras with basis over Rational Field, ...
|
|
1177
|
+
Category of Cartesian products of algebras over Rational Field, ...
|
|
1178
|
+
Category of Cartesian products of semigroups, Category of semigroups, ...
|
|
1179
|
+
Category of Cartesian products of magmas, ..., Category of magmas, ...
|
|
1180
|
+
Category of Cartesian products of additive magmas, ..., Category of additive magmas,
|
|
1181
|
+
Category of Cartesian products of sets, Category of sets, ...]
|
|
1182
|
+
|
|
1183
|
+
This reveals the parallel hierarchy of categories for Cartesian
|
|
1184
|
+
products of semigroups magmas, ... We are thus glad that Sage uses
|
|
1185
|
+
its knowledge that a monoid is a semigroup to automatically deduce
|
|
1186
|
+
that a Cartesian product of monoids is a Cartesian product of
|
|
1187
|
+
semigroups, and build the hierarchy of classes for parents and
|
|
1188
|
+
elements accordingly.
|
|
1189
|
+
|
|
1190
|
+
In general, the Cartesian product of `A` and `B` can potentially be an
|
|
1191
|
+
algebra, a coalgebra, a differential module, and be finite
|
|
1192
|
+
dimensional, or graded, or .... This can only be decided at runtime,
|
|
1193
|
+
by introspection into the properties of `A` and `B`; furthermore, the
|
|
1194
|
+
number of possible combinations (e.g. finite dimensional differential
|
|
1195
|
+
algebra) grows exponentially with the number of properties.
|
|
1196
|
+
|
|
1197
|
+
.. _category-primer-axioms:
|
|
1198
|
+
|
|
1199
|
+
Axioms
|
|
1200
|
+
------
|
|
1201
|
+
|
|
1202
|
+
First examples
|
|
1203
|
+
^^^^^^^^^^^^^^
|
|
1204
|
+
|
|
1205
|
+
We have seen that Sage is aware of the axioms satisfied by, for
|
|
1206
|
+
example, groups::
|
|
1207
|
+
|
|
1208
|
+
sage: Groups().axioms()
|
|
1209
|
+
frozenset({'Associative', 'Inverse', 'Unital'})
|
|
1210
|
+
|
|
1211
|
+
In fact, the category of groups can be *defined* by stating that a
|
|
1212
|
+
group is a magma, that is a set endowed with an internal binary
|
|
1213
|
+
multiplication, which satisfies the above axioms. Accordingly, we can
|
|
1214
|
+
construct the category of groups from the category of magmas::
|
|
1215
|
+
|
|
1216
|
+
sage: Magmas().Associative().Unital().Inverse()
|
|
1217
|
+
Category of groups
|
|
1218
|
+
|
|
1219
|
+
In general, we can construct new categories in Sage by specifying the
|
|
1220
|
+
axioms that are satisfied by the operations of the super
|
|
1221
|
+
categories. For example, starting from the category of magmas, we can
|
|
1222
|
+
construct all the following categories just by specifying the axioms
|
|
1223
|
+
satisfied by the multiplication::
|
|
1224
|
+
|
|
1225
|
+
sage: Magmas()
|
|
1226
|
+
Category of magmas
|
|
1227
|
+
sage: Magmas().Unital()
|
|
1228
|
+
Category of unital magmas
|
|
1229
|
+
|
|
1230
|
+
::
|
|
1231
|
+
|
|
1232
|
+
sage: Magmas().Commutative().Unital()
|
|
1233
|
+
Category of commutative unital magmas
|
|
1234
|
+
sage: Magmas().Unital().Commutative()
|
|
1235
|
+
Category of commutative unital magmas
|
|
1236
|
+
|
|
1237
|
+
::
|
|
1238
|
+
|
|
1239
|
+
sage: Magmas().Associative()
|
|
1240
|
+
Category of semigroups
|
|
1241
|
+
|
|
1242
|
+
::
|
|
1243
|
+
|
|
1244
|
+
sage: Magmas().Associative().Unital()
|
|
1245
|
+
Category of monoids
|
|
1246
|
+
|
|
1247
|
+
::
|
|
1248
|
+
|
|
1249
|
+
sage: Magmas().Associative().Unital().Commutative()
|
|
1250
|
+
Category of commutative monoids
|
|
1251
|
+
|
|
1252
|
+
::
|
|
1253
|
+
|
|
1254
|
+
sage: Magmas().Associative().Unital().Inverse()
|
|
1255
|
+
Category of groups
|
|
1256
|
+
|
|
1257
|
+
|
|
1258
|
+
Axioms and categories with axioms
|
|
1259
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
1260
|
+
|
|
1261
|
+
Here, ``Associative``, ``Unital``, ``Commutative`` are axioms. In
|
|
1262
|
+
general, any category ``Cs`` in Sage can declare a new axiom
|
|
1263
|
+
``A``. Then, the *category with axiom* ``Cs.A()`` models the
|
|
1264
|
+
subcategory of the objects of ``Cs`` satisfying the axiom
|
|
1265
|
+
``A``. Similarly, for any subcategory ``Ds`` of ``Cs``, ``Ds.A()``
|
|
1266
|
+
models the subcategory of the objects of ``Ds`` satisfying the axiom
|
|
1267
|
+
``A``. In most cases, it's a *full subcategory* (see
|
|
1268
|
+
:wikipedia:`Subcategory`).
|
|
1269
|
+
|
|
1270
|
+
For example, the category of sets defines the ``Finite`` axiom, and
|
|
1271
|
+
this axiom is available in the subcategory of groups::
|
|
1272
|
+
|
|
1273
|
+
sage: Sets().Finite()
|
|
1274
|
+
Category of finite sets
|
|
1275
|
+
sage: Groups().Finite()
|
|
1276
|
+
Category of finite groups
|
|
1277
|
+
|
|
1278
|
+
The meaning of each axiom is described in the documentation of the
|
|
1279
|
+
corresponding method, which can be obtained as usual by
|
|
1280
|
+
introspection::
|
|
1281
|
+
|
|
1282
|
+
sage: C = Groups()
|
|
1283
|
+
sage: C.Finite? # not tested
|
|
1284
|
+
|
|
1285
|
+
The purpose of categories with axioms is no different from other
|
|
1286
|
+
categories: to provide bookshelves of code, documentation,
|
|
1287
|
+
mathematical knowledge, tests, for their objects. The extra feature is
|
|
1288
|
+
that, when intersecting categories, axioms are automatically combined
|
|
1289
|
+
together::
|
|
1290
|
+
|
|
1291
|
+
sage: C = Magmas().Associative() & Magmas().Unital().Inverse() & Sets().Finite(); C
|
|
1292
|
+
Category of finite groups
|
|
1293
|
+
sage: sorted(C.axioms())
|
|
1294
|
+
['Associative', 'Finite', 'Inverse', 'Unital']
|
|
1295
|
+
|
|
1296
|
+
For a more advanced example, Sage knows that a ring is a set `C`
|
|
1297
|
+
endowed with a multiplication which distributes over addition, such
|
|
1298
|
+
that `(C, +)` is a commutative additive group and `(C, *)` is a monoid::
|
|
1299
|
+
|
|
1300
|
+
sage: C = (CommutativeAdditiveGroups() & Monoids()).Distributive(); C
|
|
1301
|
+
Category of rings
|
|
1302
|
+
|
|
1303
|
+
sage: sorted(C.axioms())
|
|
1304
|
+
['AdditiveAssociative', 'AdditiveCommutative', 'AdditiveInverse',
|
|
1305
|
+
'AdditiveUnital', 'Associative', 'Distributive', 'Unital']
|
|
1306
|
+
|
|
1307
|
+
The infrastructure allows for specifying further deduction rules, in
|
|
1308
|
+
order to encode mathematical facts like Wedderburn's theorem::
|
|
1309
|
+
|
|
1310
|
+
sage: DivisionRings() & Sets().Finite()
|
|
1311
|
+
Category of finite enumerated fields
|
|
1312
|
+
|
|
1313
|
+
.. NOTE::
|
|
1314
|
+
|
|
1315
|
+
When an axiom specifies the properties of some operations in Sage,
|
|
1316
|
+
the notations for those operations are tied to this axiom. For
|
|
1317
|
+
example, as we have seen above, we need two distinct axioms for
|
|
1318
|
+
associativity: the axiom "AdditiveAssociative" is about the
|
|
1319
|
+
properties of the addition `+`, whereas the axiom "Associative" is
|
|
1320
|
+
about the properties of the multiplication `*`.
|
|
1321
|
+
|
|
1322
|
+
We are touching here an inherent limitation of the current
|
|
1323
|
+
infrastructure. There is indeed no support for providing generic
|
|
1324
|
+
code that is independent of the notations. In particular, the
|
|
1325
|
+
category hierarchy about additive structures (additive monoids,
|
|
1326
|
+
additive groups, ...) is completely duplicated by that for
|
|
1327
|
+
multiplicative structures (monoids, groups, ...).
|
|
1328
|
+
|
|
1329
|
+
As far as we know, none of the existing computer algebra systems
|
|
1330
|
+
has a good solution for this problem. The difficulty is that this
|
|
1331
|
+
is not only about a single notation but a bunch of operators and
|
|
1332
|
+
methods: ``+, -, zero, summation, sum, ...`` in one case, ``*, /,
|
|
1333
|
+
one, product, prod, factor, ...`` in the other. Sharing something
|
|
1334
|
+
between the two hierarchies of categories would only be useful if
|
|
1335
|
+
one could write generic code that applies in both cases; for that
|
|
1336
|
+
one needs to somehow automatically substitute the right operations
|
|
1337
|
+
in the right spots in the code. That's kind of what we are doing
|
|
1338
|
+
manually between
|
|
1339
|
+
e.g. :meth:`AdditiveMagmas.ParentMethods.addition_table` and
|
|
1340
|
+
:meth:`Magmas.ParentMethods.multiplication_table`, but doing this
|
|
1341
|
+
systematically is a different beast from what we have been doing
|
|
1342
|
+
so far with just usual inheritance.
|
|
1343
|
+
|
|
1344
|
+
.. _category-primer-axioms-single-entry-point:
|
|
1345
|
+
|
|
1346
|
+
Single entry point and name space usage
|
|
1347
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
1348
|
+
|
|
1349
|
+
A nice feature of the notation ``Cs.A()`` is that, from a single entry
|
|
1350
|
+
point (say the category :class:`Magmas` as above), one can explore a
|
|
1351
|
+
whole range of related categories, typically with the help of
|
|
1352
|
+
introspection to discover which axioms are available, and without
|
|
1353
|
+
having to import new Python modules. This feature will be used in
|
|
1354
|
+
:issue:`15741` to unclutter the global name space from, for example,
|
|
1355
|
+
the many variants of the category of algebras like::
|
|
1356
|
+
|
|
1357
|
+
sage: FiniteDimensionalAlgebrasWithBasis(QQ)
|
|
1358
|
+
Category of finite dimensional algebras with basis over Rational Field
|
|
1359
|
+
|
|
1360
|
+
There will of course be a deprecation step, but it's recommended to
|
|
1361
|
+
prefer right away the more flexible notation::
|
|
1362
|
+
|
|
1363
|
+
sage: Algebras(QQ).WithBasis().FiniteDimensional()
|
|
1364
|
+
Category of finite dimensional algebras with basis over Rational Field
|
|
1365
|
+
|
|
1366
|
+
.. TOPIC:: Design discussion
|
|
1367
|
+
|
|
1368
|
+
How far should this be pushed? :class:`Fields` should definitely
|
|
1369
|
+
stay, but should :class:`FiniteGroups` or :class:`DivisionRings`
|
|
1370
|
+
be removed from the global namespace? Do we want to further
|
|
1371
|
+
completely deprecate the notation ``FiniteGroups()`` in favor of
|
|
1372
|
+
``Groups().Finite()``?
|
|
1373
|
+
|
|
1374
|
+
.. _category-primer-axioms-explosion:
|
|
1375
|
+
|
|
1376
|
+
On the potential combinatorial explosion of categories with axioms
|
|
1377
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
1378
|
+
|
|
1379
|
+
Even for a very simple category like ``Magmas``, there are about `2^5`
|
|
1380
|
+
potential combinations of the axioms! Think about what this becomes
|
|
1381
|
+
for a category with two operations `+` and `*`::
|
|
1382
|
+
|
|
1383
|
+
sage: C = (Magmas() & AdditiveMagmas()).Distributive(); C
|
|
1384
|
+
Category of distributive magmas and additive magmas
|
|
1385
|
+
|
|
1386
|
+
sage: CAA = C.Associative().AdditiveAssociative()
|
|
1387
|
+
sage: CAA.AdditiveCommutative().AdditiveUnital().AdditiveInverse()
|
|
1388
|
+
Category of rngs
|
|
1389
|
+
|
|
1390
|
+
sage: CAA.AdditiveCommutative().AdditiveUnital().Unital()
|
|
1391
|
+
Category of semirings
|
|
1392
|
+
|
|
1393
|
+
sage: CAA.AdditiveCommutative().AdditiveUnital().AdditiveInverse().Unital()
|
|
1394
|
+
Category of rings
|
|
1395
|
+
|
|
1396
|
+
sage: Rings().Division()
|
|
1397
|
+
Category of division rings
|
|
1398
|
+
|
|
1399
|
+
sage: Rings().Division().Commutative()
|
|
1400
|
+
Category of fields
|
|
1401
|
+
|
|
1402
|
+
sage: Rings().Division().Finite()
|
|
1403
|
+
Category of finite enumerated fields
|
|
1404
|
+
|
|
1405
|
+
or for more advanced categories::
|
|
1406
|
+
|
|
1407
|
+
sage: g = HopfAlgebras(QQ).WithBasis().Graded().Connected().category_graph() # needs sage.graphs
|
|
1408
|
+
sage: g.set_latex_options(format='dot2tex') # needs sage.graphs sage.plot
|
|
1409
|
+
sage: view(g) # not tested # needs sage.graphs sage.plot
|
|
1410
|
+
|
|
1411
|
+
Difference between axioms and regressive covariant functorial constructions
|
|
1412
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
1413
|
+
|
|
1414
|
+
Our running examples here will be the axiom ``FiniteDimensional`` and
|
|
1415
|
+
the regressive covariant functorial construction ``Graded``. Let
|
|
1416
|
+
``Cs`` be some subcategory of ``Modules``, say the category of modules
|
|
1417
|
+
itself::
|
|
1418
|
+
|
|
1419
|
+
sage: Cs = Modules(QQ)
|
|
1420
|
+
|
|
1421
|
+
Then, ``Cs.FiniteDimensional()`` (respectively ``Cs.Graded()``) is the
|
|
1422
|
+
subcategory of the objects ``O`` of ``Cs`` which are finite
|
|
1423
|
+
dimensional (respectively graded).
|
|
1424
|
+
|
|
1425
|
+
Let also ``Ds`` be a subcategory of ``Cs``, say::
|
|
1426
|
+
|
|
1427
|
+
sage: Ds = Algebras(QQ)
|
|
1428
|
+
|
|
1429
|
+
A finite dimensional algebra is also a finite dimensional module::
|
|
1430
|
+
|
|
1431
|
+
sage: Algebras(QQ).FiniteDimensional().is_subcategory(Modules(QQ).FiniteDimensional())
|
|
1432
|
+
True
|
|
1433
|
+
|
|
1434
|
+
Similarly a graded algebra is also a graded module::
|
|
1435
|
+
|
|
1436
|
+
sage: Algebras(QQ).Graded().is_subcategory( Modules(QQ).Graded() )
|
|
1437
|
+
True
|
|
1438
|
+
|
|
1439
|
+
This is the *covariance* property: for ``A`` an axiom or a covariant
|
|
1440
|
+
functorial construction, if ``Ds`` is a subcategory of ``Cs``, then
|
|
1441
|
+
``Ds.A()`` is a subcategory of ``Cs.A()``.
|
|
1442
|
+
|
|
1443
|
+
What happens if we consider reciprocally an object of ``Cs.A()`` which
|
|
1444
|
+
is also in ``Ds``? A finite dimensional module which is also an
|
|
1445
|
+
algebra is a finite dimensional algebra::
|
|
1446
|
+
|
|
1447
|
+
sage: Modules(QQ).FiniteDimensional() & Algebras(QQ)
|
|
1448
|
+
Category of finite dimensional algebras over Rational Field
|
|
1449
|
+
|
|
1450
|
+
On the other hand, a graded module `O` which is also an algebra is not
|
|
1451
|
+
necessarily a graded algebra! Indeed, the grading on `O` may not be
|
|
1452
|
+
compatible with the product on `O`::
|
|
1453
|
+
|
|
1454
|
+
sage: Modules(QQ).Graded() & Algebras(QQ)
|
|
1455
|
+
Join of Category of algebras over Rational Field
|
|
1456
|
+
and Category of graded vector spaces over Rational Field
|
|
1457
|
+
|
|
1458
|
+
The relevant difference between ``FiniteDimensional`` and ``Graded``
|
|
1459
|
+
is that ``FiniteDimensional`` is a statement about the properties of
|
|
1460
|
+
``O`` seen as a module (and thus does not depend on the given
|
|
1461
|
+
category), whereas ``Graded`` is a statement about the properties of
|
|
1462
|
+
``O`` and all its operations in the given category.
|
|
1463
|
+
|
|
1464
|
+
In general, if a category satisfies a given axiom, any subcategory
|
|
1465
|
+
also satisfies that axiom. Another formulation is that, for an axiom
|
|
1466
|
+
``A`` defined in a super category ``Cs`` of ``Ds``, ``Ds.A()`` is the
|
|
1467
|
+
intersection of the categories ``Ds`` and ``Cs.A()``::
|
|
1468
|
+
|
|
1469
|
+
sage: As = Algebras(QQ).FiniteDimensional(); As
|
|
1470
|
+
Category of finite dimensional algebras over Rational Field
|
|
1471
|
+
sage: Bs = Algebras(QQ) & Modules(QQ).FiniteDimensional(); As
|
|
1472
|
+
Category of finite dimensional algebras over Rational Field
|
|
1473
|
+
sage: As is Bs
|
|
1474
|
+
True
|
|
1475
|
+
|
|
1476
|
+
An immediate consequence is that, as we have already noticed, axioms
|
|
1477
|
+
commute::
|
|
1478
|
+
|
|
1479
|
+
sage: As = Algebras(QQ).FiniteDimensional().WithBasis(); As
|
|
1480
|
+
Category of finite dimensional algebras with basis over Rational Field
|
|
1481
|
+
sage: Bs = Algebras(QQ).WithBasis().FiniteDimensional(); Bs
|
|
1482
|
+
Category of finite dimensional algebras with basis over Rational Field
|
|
1483
|
+
sage: As is Bs
|
|
1484
|
+
True
|
|
1485
|
+
|
|
1486
|
+
On the other hand, axioms do not necessarily commute with functorial
|
|
1487
|
+
constructions, even if the current printout may missuggest so::
|
|
1488
|
+
|
|
1489
|
+
sage: As = Algebras(QQ).Graded().WithBasis(); As
|
|
1490
|
+
Category of graded algebras with basis over Rational Field
|
|
1491
|
+
sage: Bs = Algebras(QQ).WithBasis().Graded(); Bs
|
|
1492
|
+
Category of graded algebras with basis over Rational Field
|
|
1493
|
+
sage: As is Bs
|
|
1494
|
+
False
|
|
1495
|
+
|
|
1496
|
+
This is because ``Bs`` is the category of algebras endowed with basis,
|
|
1497
|
+
which are further graded; in particular the basis must respect the
|
|
1498
|
+
grading (i.e. be made of homogeneous elements). On the other hand,
|
|
1499
|
+
``As`` is the category of graded algebras, which are further endowed
|
|
1500
|
+
with some basis; that basis need not respect the grading. In fact
|
|
1501
|
+
``As`` is really a join category::
|
|
1502
|
+
|
|
1503
|
+
sage: type(As)
|
|
1504
|
+
<class 'sage.categories.category.JoinCategory_with_category'>
|
|
1505
|
+
sage: As._repr_(as_join=True)
|
|
1506
|
+
'Join of Category of algebras with basis over Rational Field and Category of graded algebras over Rational Field'
|
|
1507
|
+
|
|
1508
|
+
.. TODO::
|
|
1509
|
+
|
|
1510
|
+
Improve the printing of functorial constructions and joins to
|
|
1511
|
+
raise this potentially dangerous ambiguity.
|
|
1512
|
+
|
|
1513
|
+
|
|
1514
|
+
Further reading on axioms
|
|
1515
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
1516
|
+
|
|
1517
|
+
We refer to :mod:`sage.categories.category_with_axiom` for how to
|
|
1518
|
+
implement axioms.
|
|
1519
|
+
|
|
1520
|
+
Wrap-up
|
|
1521
|
+
-------
|
|
1522
|
+
|
|
1523
|
+
As we have seen, there is a combinatorial explosion of possible
|
|
1524
|
+
classes. Constructing by hand the full class hierarchy would not scale
|
|
1525
|
+
unless one would restrict to a very rigid subset. Even if it was
|
|
1526
|
+
possible to construct automatically the full hierarchy, this would not
|
|
1527
|
+
scale with respect to system resources.
|
|
1528
|
+
|
|
1529
|
+
When designing software systems with large hierarchies of abstract
|
|
1530
|
+
classes for business objects, the difficulty is usually to identify a
|
|
1531
|
+
proper set of key concepts. Here we are lucky, as the key concepts
|
|
1532
|
+
have been long identified and are relatively few:
|
|
1533
|
+
|
|
1534
|
+
- Operations (`+`, `*`, ...)
|
|
1535
|
+
- Axioms on those operations (associativity, ...)
|
|
1536
|
+
- Constructions (Cartesian products, ...)
|
|
1537
|
+
|
|
1538
|
+
Better, those concepts are sufficiently well known so that a user can
|
|
1539
|
+
reasonably be expected to be familiar with the concepts that are
|
|
1540
|
+
involved for his own needs.
|
|
1541
|
+
|
|
1542
|
+
Instead, the difficulty is concentrated in the huge number of possible
|
|
1543
|
+
combinations, an unpredictable large subset of which being potentially
|
|
1544
|
+
of interest; at the same time, only a small -- but moving -- subset
|
|
1545
|
+
has code naturally attached to it.
|
|
1546
|
+
|
|
1547
|
+
This has led to the current design, where one focuses on writing the
|
|
1548
|
+
relatively few classes for which there is actual code or mathematical
|
|
1549
|
+
information, and lets Sage *compose dynamically and lazily* those
|
|
1550
|
+
building blocks to construct the minimal hierarchy of classes needed
|
|
1551
|
+
for the computation at hand. This allows for the infrastructure to
|
|
1552
|
+
scale smoothly as bookshelves are added, extended, or reorganized.
|
|
1553
|
+
|
|
1554
|
+
|
|
1555
|
+
Writing a new category
|
|
1556
|
+
======================
|
|
1557
|
+
|
|
1558
|
+
Each category `C` **must** be provided with a method
|
|
1559
|
+
``C.super_categories()`` and *can* be provided with a method
|
|
1560
|
+
``C._subcategory_hook_(D)``. Also, it may be needed to insert `C` into
|
|
1561
|
+
the output of the ``super_categories()`` method of some other
|
|
1562
|
+
category. This determines the position of `C` in the category graph.
|
|
1563
|
+
|
|
1564
|
+
A category *may* provide methods that can be used by all its objects,
|
|
1565
|
+
respectively by all elements of its objects.
|
|
1566
|
+
|
|
1567
|
+
Each category *should* come with a good example, in
|
|
1568
|
+
:mod:`sage.categories.examples`.
|
|
1569
|
+
|
|
1570
|
+
Inserting the new category into the category graph
|
|
1571
|
+
--------------------------------------------------
|
|
1572
|
+
|
|
1573
|
+
``C.super_categories()`` *must* return a list of categories, namely
|
|
1574
|
+
the *immediate* super categories of `C`. Of course, if you know that
|
|
1575
|
+
your new category `C` is an immediate super category of some existing
|
|
1576
|
+
category `D`, then you should also update the method
|
|
1577
|
+
``D.super_categories`` to include `C`.
|
|
1578
|
+
|
|
1579
|
+
The immediate super categories of `C` *should not* be :class:`join
|
|
1580
|
+
categories <.category.JoinCategory>`. Furthermore, one always should have::
|
|
1581
|
+
|
|
1582
|
+
Cs().is_subcategory( Category.join(Cs().super_categories()) )
|
|
1583
|
+
|
|
1584
|
+
Cs()._cmp_key > other._cmp_key for other in Cs().super_categories()
|
|
1585
|
+
|
|
1586
|
+
This is checked by :meth:`~sage.categories.category.Category._test_category`.
|
|
1587
|
+
|
|
1588
|
+
In several cases, the category `C` is directly provided with a generic
|
|
1589
|
+
implementation of ``super_categories``; a typical example is when `C`
|
|
1590
|
+
implements an axiom or a functorial construction; in such a case, `C`
|
|
1591
|
+
may implement ``C.extra_super_categories()`` to complement the super
|
|
1592
|
+
categories discovered by the generic implementation. This method needs
|
|
1593
|
+
not return immediate super categories; instead it's usually best to
|
|
1594
|
+
specify the largest super category providing the desired mathematical
|
|
1595
|
+
information. For example, the category
|
|
1596
|
+
:class:`Magmas.Commutative.Algebras` just states that the algebra of a
|
|
1597
|
+
commutative magma is a commutative magma. This is sufficient to let
|
|
1598
|
+
Sage deduce that it's in fact a commutative algebra.
|
|
1599
|
+
|
|
1600
|
+
Methods for objects and elements
|
|
1601
|
+
--------------------------------
|
|
1602
|
+
|
|
1603
|
+
Different objects of the same category share some algebraic features, and
|
|
1604
|
+
very often these features can be encoded in a method, in a generic way.
|
|
1605
|
+
For example, for every commutative additive monoid, it makes sense to ask
|
|
1606
|
+
for the sum of a list of elements. Sage's category framework allows to
|
|
1607
|
+
provide a generic implementation for all objects of a category.
|
|
1608
|
+
|
|
1609
|
+
If you want to provide your new category with generic methods for
|
|
1610
|
+
objects (or elements of objects), then you simply add a nested class
|
|
1611
|
+
called ``ParentMethods`` (or ``ElementMethods``). The methods of that
|
|
1612
|
+
class will automatically become methods of the objects (or the
|
|
1613
|
+
elements). For instance::
|
|
1614
|
+
|
|
1615
|
+
sage: P.<x,y> = ZZ[]
|
|
1616
|
+
sage: P.prod([x,y,2])
|
|
1617
|
+
2*x*y
|
|
1618
|
+
sage: P.prod.__module__
|
|
1619
|
+
'sage.categories.monoids'
|
|
1620
|
+
sage: P.prod.__func__ is raw_getattr(Monoids().ParentMethods, "prod")
|
|
1621
|
+
True
|
|
1622
|
+
|
|
1623
|
+
We recommend to study the code of one example::
|
|
1624
|
+
|
|
1625
|
+
sage: C = CommutativeAdditiveMonoids()
|
|
1626
|
+
sage: C?? # not tested
|
|
1627
|
+
|
|
1628
|
+
.. _category-primer-category-order:
|
|
1629
|
+
|
|
1630
|
+
On the order of super categories
|
|
1631
|
+
--------------------------------
|
|
1632
|
+
|
|
1633
|
+
The generic method ``C.all_super_categories()`` determines recursively
|
|
1634
|
+
the list of *all* super categories of `C`.
|
|
1635
|
+
|
|
1636
|
+
The order of the categories in this list does influence the
|
|
1637
|
+
inheritance of methods for parents and elements. Namely, if `P` is an
|
|
1638
|
+
object in the category `C` and if `C_1` and `C_2` are both super
|
|
1639
|
+
categories of `C` defining some method ``foo`` in ``ParentMethods``,
|
|
1640
|
+
then `P` will use `C_1`'s version of ``foo`` if and only if `C_1`
|
|
1641
|
+
appears in ``C.all_super_categories()`` before `C_2`.
|
|
1642
|
+
|
|
1643
|
+
However this must be considered as an *implementation detail*: if
|
|
1644
|
+
`C_1` and `C_2` are incomparable categories, then the order in which
|
|
1645
|
+
they appear must be mathematically irrelevant: in particular, the
|
|
1646
|
+
methods ``foo`` in `C_1` and `C_2` must have the same semantic. Code
|
|
1647
|
+
should not rely on any specific order, as it is subject to later
|
|
1648
|
+
change. Whenever one of the implementations is preferred in some common
|
|
1649
|
+
subcategory of `C_1` and `C_2`, for example for efficiency reasons,
|
|
1650
|
+
the ambiguity should be resolved explicitly by defining a
|
|
1651
|
+
method ``foo`` in this category. See the method ``some_elements`` in
|
|
1652
|
+
the code of the category :class:`FiniteCoxeterGroups` for an example.
|
|
1653
|
+
|
|
1654
|
+
Since :issue:`11943`, ``C.all_super_categories()`` is computed by the
|
|
1655
|
+
so-called ``C3`` algorithm used by Python to compute Method Resolution
|
|
1656
|
+
Order of new-style classes. Thus the order in
|
|
1657
|
+
``C.all_super_categories()``, ``C.parent_class.mro()`` and
|
|
1658
|
+
``C.element_class.mro()`` are guaranteed to be consistent.
|
|
1659
|
+
|
|
1660
|
+
Since :issue:`13589`, the ``C3`` algorithm is put under control of some
|
|
1661
|
+
total order on categories. This order is not necessarily meaningful,
|
|
1662
|
+
but it guarantees that ``C3`` always finds a consistent Method
|
|
1663
|
+
Resolution Order. For background, see
|
|
1664
|
+
:mod:`sage.misc.c3_controlled`. A visible effect is that the order in
|
|
1665
|
+
which categories are specified in ``C.super_categories()``, or in a
|
|
1666
|
+
join category, no longer influences the result of
|
|
1667
|
+
``C.all_super_categories()``.
|
|
1668
|
+
|
|
1669
|
+
Subcategory hook (advanced optimization feature)
|
|
1670
|
+
------------------------------------------------
|
|
1671
|
+
|
|
1672
|
+
The default implementation of the method ``C.is_subcategory(D)`` is to
|
|
1673
|
+
look up whether `D` appears in ``C.all_super_categories()``. However,
|
|
1674
|
+
building the list of all the super categories of `C` is an expensive
|
|
1675
|
+
operation that is sometimes best avoided. For example, if both `C` and
|
|
1676
|
+
`D` are categories defined over a base, but the bases differ, then one
|
|
1677
|
+
knows right away that they can not be subcategories of each other.
|
|
1678
|
+
|
|
1679
|
+
When such a short-path is known, one can implement a method
|
|
1680
|
+
``_subcategory_hook_``. Then, ``C.is_subcategory(D)`` first calls
|
|
1681
|
+
``D._subcategory_hook_(C)``. If this returns ``Unknown``, then
|
|
1682
|
+
``C.is_subcategory(D)`` tries to find ``D`` in
|
|
1683
|
+
``C.all_super_categories()``. Otherwise, ``C.is_subcategory(D)``
|
|
1684
|
+
returns the result of ``D._subcategory_hook_(C)``.
|
|
1685
|
+
|
|
1686
|
+
By default, ``D._subcategory_hook_(C)`` tests whether
|
|
1687
|
+
``issubclass(C.parent_class,D.parent_class)``, which is very often
|
|
1688
|
+
giving the right answer::
|
|
1689
|
+
|
|
1690
|
+
sage: Rings()._subcategory_hook_(Algebras(QQ))
|
|
1691
|
+
True
|
|
1692
|
+
sage: HopfAlgebras(QQ)._subcategory_hook_(Algebras(QQ))
|
|
1693
|
+
False
|
|
1694
|
+
sage: Algebras(QQ)._subcategory_hook_(HopfAlgebras(QQ))
|
|
1695
|
+
True
|
|
1696
|
+
"""
|