passagemath-objects 10.6.41__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.
Potentially problematic release.
This version of passagemath-objects might be problematic. Click here for more details.
- passagemath_objects/.dylibs/libgmp.10.dylib +0 -0
- passagemath_objects/__init__.py +3 -0
- passagemath_objects-10.6.41.dist-info/METADATA +115 -0
- passagemath_objects-10.6.41.dist-info/RECORD +280 -0
- passagemath_objects-10.6.41.dist-info/WHEEL +6 -0
- passagemath_objects-10.6.41.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 +2112 -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 +3228 -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 +276 -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,1402 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-objects
|
|
2
|
+
"""
|
|
3
|
+
The C3 algorithm, under control of a total order
|
|
4
|
+
|
|
5
|
+
Abstract
|
|
6
|
+
========
|
|
7
|
+
|
|
8
|
+
Python handles multiple inheritance by computing, for each class,
|
|
9
|
+
a linear extension of the poset of all its super classes (the Method
|
|
10
|
+
Resolution Order, MRO). The MRO is calculated recursively from local
|
|
11
|
+
information (the *ordered* list of the direct super classes), with
|
|
12
|
+
the so-called ``C3`` algorithm. This algorithm can fail if the local
|
|
13
|
+
information is not consistent; worst, there exist hierarchies of
|
|
14
|
+
classes with provably no consistent local information.
|
|
15
|
+
|
|
16
|
+
For large hierarchy of classes, like those derived from categories in
|
|
17
|
+
Sage, maintaining consistent local information by hand does not scale
|
|
18
|
+
and leads to unpredictable ``C3`` failures (the dreaded "could not
|
|
19
|
+
find a consistent method resolution order"); a maintenance nightmare.
|
|
20
|
+
|
|
21
|
+
This module implements a final solution to this problem. Namely, it
|
|
22
|
+
allows for building automatically the local information from the bare
|
|
23
|
+
class hierarchy in such a way that guarantees that the ``C3``
|
|
24
|
+
algorithm will never fail.
|
|
25
|
+
|
|
26
|
+
Err, but you said that this was provably impossible? Well, not if one
|
|
27
|
+
relaxes a bit the hypotheses; but that's not something one would want
|
|
28
|
+
to do by hand :-)
|
|
29
|
+
|
|
30
|
+
The problem
|
|
31
|
+
===========
|
|
32
|
+
|
|
33
|
+
Consider the following hierarchy of classes::
|
|
34
|
+
|
|
35
|
+
sage: class A1(): pass
|
|
36
|
+
sage: class A2():
|
|
37
|
+
....: def foo(self): return 2
|
|
38
|
+
sage: class A3(): pass
|
|
39
|
+
sage: class A4():
|
|
40
|
+
....: def foo(self): return 4
|
|
41
|
+
sage: class A5(A2, A1):
|
|
42
|
+
....: def foo(self): return 5
|
|
43
|
+
sage: class A6(A4, A3): pass
|
|
44
|
+
sage: class A7(A6, A5): pass
|
|
45
|
+
|
|
46
|
+
If ``a`` is an instance of ``A7``, then Python needs to choose which
|
|
47
|
+
implementation to use upon calling ``a.foo()``: that of ``A4`` or
|
|
48
|
+
``A5``, but obviously not that of ``A2``. In Python, like in many
|
|
49
|
+
other dynamic object oriented languages, this is achieved by
|
|
50
|
+
calculating once for all a specific linear extension of the hierarchy
|
|
51
|
+
of the super classes of each class, called its Method Resolution Order
|
|
52
|
+
(MRO)::
|
|
53
|
+
|
|
54
|
+
sage: [cls.__name__ for cls in A7.mro()]
|
|
55
|
+
['A7', 'A6', 'A4', 'A3', 'A5', 'A2', 'A1', 'object']
|
|
56
|
+
|
|
57
|
+
Thus, in our example, the implementation in ``A4`` is chosen::
|
|
58
|
+
|
|
59
|
+
sage: a = A7()
|
|
60
|
+
sage: a.foo()
|
|
61
|
+
4
|
|
62
|
+
|
|
63
|
+
Specifically, the MRO is calculated using the so-called ``C3``
|
|
64
|
+
algorithm which guarantees that the MRO respects not only inheritance,
|
|
65
|
+
but also the order in which the bases (direct super classes) are given
|
|
66
|
+
for each class.
|
|
67
|
+
|
|
68
|
+
However, for large hierarchies of classes with lots of multiple
|
|
69
|
+
inheritance, like those derived from categories in Sage, this
|
|
70
|
+
algorithm easily fails if the order of the bases is not chosen
|
|
71
|
+
consistently (here for ``A2`` w.r.t. ``A1``)::
|
|
72
|
+
|
|
73
|
+
sage: class B6(A1,A2): pass
|
|
74
|
+
sage: class B7(B6,A5): pass
|
|
75
|
+
Traceback (most recent call last):
|
|
76
|
+
...
|
|
77
|
+
TypeError: Cannot create a consistent method resolution
|
|
78
|
+
order (MRO) for bases A1, A2
|
|
79
|
+
|
|
80
|
+
There actually exist hierarchies of classes for which ``C3`` fails
|
|
81
|
+
whatever order of the bases is chosen; the smallest such example,
|
|
82
|
+
admittedly artificial, has ten classes (see below). Still, this
|
|
83
|
+
highlights that this problem has to be tackled in a systematic way.
|
|
84
|
+
|
|
85
|
+
Fortunately, one can trick ``C3``, without changing the inheritance
|
|
86
|
+
semantic, by adding some super classes of ``A`` to the bases of
|
|
87
|
+
``A``. In the following example, we completely force a given MRO by
|
|
88
|
+
specifying *all* the super classes of ``A`` as bases::
|
|
89
|
+
|
|
90
|
+
sage: class A7(A6, A5, A4, A3, A2, A1): pass
|
|
91
|
+
sage: [cls.__name__ for cls in A7.mro()]
|
|
92
|
+
['A7', 'A6', 'A5', 'A4', 'A3', 'A2', 'A1', 'object']
|
|
93
|
+
|
|
94
|
+
Luckily this can be optimized; here it is sufficient to add a single
|
|
95
|
+
base to enforce the same MRO::
|
|
96
|
+
|
|
97
|
+
sage: class A7(A6, A5, A4): pass
|
|
98
|
+
sage: [cls.__name__ for cls in A7.mro()]
|
|
99
|
+
['A7', 'A6', 'A5', 'A4', 'A3', 'A2', 'A1', 'object']
|
|
100
|
+
|
|
101
|
+
A strategy to solve the problem
|
|
102
|
+
===============================
|
|
103
|
+
|
|
104
|
+
We should recall at this point a design decision that we took for the
|
|
105
|
+
hierarchy of classes derived from categories: *the semantic shall only
|
|
106
|
+
depend on the inheritance order*, not on the specific MRO, and in
|
|
107
|
+
particular not on the order of the bases (see
|
|
108
|
+
:ref:`On the order of super categories <category-primer-category-order>`).
|
|
109
|
+
|
|
110
|
+
If a choice needs to be made (for example for efficiency reasons),
|
|
111
|
+
then this should be done explicitly, on a method-by-method basis. In
|
|
112
|
+
practice this design goal is not yet met.
|
|
113
|
+
|
|
114
|
+
.. NOTE::
|
|
115
|
+
|
|
116
|
+
When managing large hierarchies of classes in other contexts this
|
|
117
|
+
may be too strong a design decision.
|
|
118
|
+
|
|
119
|
+
The strategy we use for hierarchies of classes derived from categories
|
|
120
|
+
is then:
|
|
121
|
+
|
|
122
|
+
1. To choose a global total order on the whole hierarchy of classes.
|
|
123
|
+
2. To control ``C3`` to get it to return MROs that follow this total order.
|
|
124
|
+
|
|
125
|
+
A basic approach for point 1., that will work for any hierarchy of
|
|
126
|
+
classes, is to enumerate the classes while they are constructed
|
|
127
|
+
(making sure that the bases of each class are enumerated before that
|
|
128
|
+
class), and to order the classes according to that enumeration. A more
|
|
129
|
+
conceptual ordering may be desirable, in particular to get
|
|
130
|
+
deterministic and reproducible results. In the context of Sage, this
|
|
131
|
+
is mostly relevant for those doctests displaying all the categories or
|
|
132
|
+
classes that an object inherits from.
|
|
133
|
+
|
|
134
|
+
Getting fine control on C3
|
|
135
|
+
==========================
|
|
136
|
+
|
|
137
|
+
This module is about point 2.
|
|
138
|
+
|
|
139
|
+
The natural approach would be to change the algorithm used by Python
|
|
140
|
+
to compute the MRO. However, changing Python's default algorithm just
|
|
141
|
+
for our needs is obviously not an option, and there is currently no
|
|
142
|
+
hook to customize specific classes to use a different algorithm.
|
|
143
|
+
Pushing the addition of such a hook into stock Python would take too
|
|
144
|
+
much time and effort.
|
|
145
|
+
|
|
146
|
+
Another approach would be to use the "adding bases" trick
|
|
147
|
+
straightforwardly, putting the list of *all* the super classes of a
|
|
148
|
+
class as its bases. However, this would have several drawbacks:
|
|
149
|
+
|
|
150
|
+
- It is not so elegant, in particular because it duplicates
|
|
151
|
+
information: we already know through ``A5`` that ``A7`` is a
|
|
152
|
+
subclass of ``A1``. This duplication could be acceptable in our
|
|
153
|
+
context because the hierarchy of classes is generated automatically
|
|
154
|
+
from a conceptual hierarchy (the categories) which serves as single
|
|
155
|
+
point of truth for calculating the bases of each class.
|
|
156
|
+
|
|
157
|
+
- It increases the complexity of the calculation of the MRO with
|
|
158
|
+
``C3``. For example, for a linear hierarchy of classes, the
|
|
159
|
+
complexity goes from `O(n^2)` to `O(n^3)` which is not acceptable.
|
|
160
|
+
|
|
161
|
+
- It increases the complexity of inspecting the classes. For example,
|
|
162
|
+
the current implementation of the ``dir`` command in Python has no
|
|
163
|
+
cache, and its complexity is linear in the number of maximal paths
|
|
164
|
+
in the class hierarchy graph as defined by the bases. For a linear
|
|
165
|
+
hierarchy, this is of complexity `O(p_n)` where `p_n` is the number
|
|
166
|
+
of integer partitions of `n`, which is exponential. And indeed,
|
|
167
|
+
running ``dir`` for a typical class like
|
|
168
|
+
``GradedHopfAlgebrasWithBasis(QQ).parent_class`` with ``37`` super
|
|
169
|
+
classes took `18` seconds with this approach.
|
|
170
|
+
|
|
171
|
+
Granted: this mostly affects the ``dir`` command and could be blamed
|
|
172
|
+
on its current implementation. With appropriate caching, it could be
|
|
173
|
+
reimplemented to have a complexity roughly linear in the number of
|
|
174
|
+
classes in the hierarchy. But this won't happen any time soon in a
|
|
175
|
+
stock Python.
|
|
176
|
+
|
|
177
|
+
This module refines this approach to make it acceptable, if not
|
|
178
|
+
seamless. Given a hierarchy and a total order on this hierarchy, it
|
|
179
|
+
calculates for each element of the hierarchy the smallest list of
|
|
180
|
+
additional bases that forces ``C3`` to return the desired MRO. This is
|
|
181
|
+
achieved by implementing an instrumented variant of the ``C3``
|
|
182
|
+
algorithm (which we call *instrumented* ``C3``) that detects when
|
|
183
|
+
``C3`` is about to take a wrong decision and adds one base to force
|
|
184
|
+
the right decision. Then, running the standard ``C3`` algorithm with
|
|
185
|
+
the updated list of bases (which we call *controlled* ``C3``) yields
|
|
186
|
+
the desired MRO.
|
|
187
|
+
|
|
188
|
+
EXAMPLES:
|
|
189
|
+
|
|
190
|
+
As an experimentation and testing tool, we use a class
|
|
191
|
+
:class:`HierarchyElement` whose instances can be constructed from a
|
|
192
|
+
hierarchy described by a poset, a digraph, or more generally a
|
|
193
|
+
successor relation. By default, the desired MRO is sorted
|
|
194
|
+
decreasingly. Another total order can be specified using a sorting
|
|
195
|
+
key.
|
|
196
|
+
|
|
197
|
+
We consider the smallest poset describing a class hierarchy admitting
|
|
198
|
+
no MRO whatsoever::
|
|
199
|
+
|
|
200
|
+
sage: P = Poset({10: [9,8,7], 9: [6,1], 8: [5,2], 7: [4,3], # needs sage.graphs
|
|
201
|
+
....: 6: [3,2], 5: [3,1], 4: [2,1]},
|
|
202
|
+
....: linear_extension=True, facade=True)
|
|
203
|
+
|
|
204
|
+
And build a :class:`HierarchyElement` from it::
|
|
205
|
+
|
|
206
|
+
sage: from sage.misc.c3_controlled import HierarchyElement
|
|
207
|
+
sage: x = HierarchyElement(10, P) # needs sage.graphs
|
|
208
|
+
|
|
209
|
+
Here are its bases::
|
|
210
|
+
|
|
211
|
+
sage: HierarchyElement(10, P)._bases # needs sage.graphs
|
|
212
|
+
[9, 8, 7]
|
|
213
|
+
|
|
214
|
+
Using the standard ``C3`` algorithm fails::
|
|
215
|
+
|
|
216
|
+
sage: x.mro_standard # needs sage.graphs
|
|
217
|
+
Traceback (most recent call last):
|
|
218
|
+
...
|
|
219
|
+
ValueError: Cannot merge the items 3, 3, 2.
|
|
220
|
+
|
|
221
|
+
We also get a failure when we relabel `P` according to another linear
|
|
222
|
+
extension. For easy relabelling, we first need to set an appropriate
|
|
223
|
+
default linear extension for `P`::
|
|
224
|
+
|
|
225
|
+
sage: linear_extension = list(reversed(IntegerRange(1, 11)))
|
|
226
|
+
sage: P = P.with_linear_extension(linear_extension) # needs sage.graphs
|
|
227
|
+
sage: list(P) # needs sage.graphs
|
|
228
|
+
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
|
|
229
|
+
|
|
230
|
+
Now we play with a specific linear extension of `P`::
|
|
231
|
+
|
|
232
|
+
sage: # needs sage.graphs
|
|
233
|
+
sage: Q = P.linear_extension([10, 9, 8, 7, 6, 5, 4, 1, 2, 3]).to_poset()
|
|
234
|
+
sage: Q.cover_relations()
|
|
235
|
+
[[10, 9], [10, 8], [10, 7], [9, 6], [9, 3], [8, 5], [8, 2], [7, 4],
|
|
236
|
+
[7, 1], [6, 2], [6, 1], [5, 3], [5, 1], [4, 3], [4, 2]]
|
|
237
|
+
sage: x = HierarchyElement(10, Q)
|
|
238
|
+
sage: x.mro_standard
|
|
239
|
+
Traceback (most recent call last):
|
|
240
|
+
...
|
|
241
|
+
ValueError: Cannot merge the items 2, 3, 3.
|
|
242
|
+
|
|
243
|
+
On the other hand, both the instrumented ``C3`` algorithm, and the
|
|
244
|
+
controlled ``C3`` algorithm give the desired MRO::
|
|
245
|
+
|
|
246
|
+
sage: x.mro # needs sage.graphs
|
|
247
|
+
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
|
|
248
|
+
sage: x.mro_controlled # needs sage.graphs
|
|
249
|
+
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
|
|
250
|
+
|
|
251
|
+
The above checks, and more, can be run with::
|
|
252
|
+
|
|
253
|
+
sage: x._test_mro() # needs sage.graphs
|
|
254
|
+
|
|
255
|
+
In practice, the control was achieved by adding the following bases::
|
|
256
|
+
|
|
257
|
+
sage: x._bases # needs sage.graphs
|
|
258
|
+
[9, 8, 7]
|
|
259
|
+
sage: x._bases_controlled # needs sage.graphs
|
|
260
|
+
[9, 8, 7, 6, 5]
|
|
261
|
+
|
|
262
|
+
Altogether, four bases were added for control::
|
|
263
|
+
|
|
264
|
+
sage: sum(len(HierarchyElement(q, Q)._bases) for q in Q) # needs sage.graphs
|
|
265
|
+
15
|
|
266
|
+
sage: sum(len(HierarchyElement(q, Q)._bases_controlled) for q in Q) # needs sage.graphs
|
|
267
|
+
19
|
|
268
|
+
|
|
269
|
+
This information can also be recovered with::
|
|
270
|
+
|
|
271
|
+
sage: x.all_bases_len() # needs sage.graphs
|
|
272
|
+
15
|
|
273
|
+
sage: x.all_bases_controlled_len() # needs sage.graphs
|
|
274
|
+
19
|
|
275
|
+
|
|
276
|
+
We now check that the ``C3`` algorithm fails for all linear extensions
|
|
277
|
+
`l` of this poset, whereas both the instrumented and controlled ``C3``
|
|
278
|
+
algorithms succeed; along the way, we collect some statistics::
|
|
279
|
+
|
|
280
|
+
sage: L = P.linear_extensions() # needs sage.graphs
|
|
281
|
+
sage: stats = []
|
|
282
|
+
sage: for l in L: # needs sage.graphs sage.modules
|
|
283
|
+
....: x = HierarchyElement(10, l.to_poset())
|
|
284
|
+
....: try: # Check that x.mro_standard always fails with a ValueError
|
|
285
|
+
....: x.mro_standard
|
|
286
|
+
....: except ValueError:
|
|
287
|
+
....: pass
|
|
288
|
+
....: else:
|
|
289
|
+
....: assert False
|
|
290
|
+
....: assert x.mro == list(P)
|
|
291
|
+
....: assert x.mro_controlled == list(P)
|
|
292
|
+
....: assert x.all_bases_len() == 15
|
|
293
|
+
....: stats.append(x.all_bases_controlled_len()-x.all_bases_len())
|
|
294
|
+
|
|
295
|
+
Depending on the linear extension `l` it was necessary to add between
|
|
296
|
+
one and five bases for control; for example, `216` linear extensions
|
|
297
|
+
required the addition of four bases::
|
|
298
|
+
|
|
299
|
+
sage: sorted(Word(stats).evaluation_sparse()) # needs sage.combinat sage.graphs sage.modules
|
|
300
|
+
[(1, 36), (2, 108), (3, 180), (4, 216), (5, 180)]
|
|
301
|
+
|
|
302
|
+
We now consider a hierarchy of categories::
|
|
303
|
+
|
|
304
|
+
sage: from operator import attrgetter
|
|
305
|
+
sage: x = HierarchyElement(Groups(), attrcall("super_categories"), attrgetter("_cmp_key"))
|
|
306
|
+
sage: x.mro
|
|
307
|
+
[Category of groups, Category of monoids, Category of semigroups,
|
|
308
|
+
Category of inverse unital magmas, Category of unital magmas, Category of magmas,
|
|
309
|
+
Category of sets, Category of sets with partial maps, Category of objects]
|
|
310
|
+
sage: x.mro_standard
|
|
311
|
+
[Category of groups, Category of monoids, Category of semigroups,
|
|
312
|
+
Category of inverse unital magmas, Category of unital magmas, Category of magmas,
|
|
313
|
+
Category of sets, Category of sets with partial maps, Category of objects]
|
|
314
|
+
|
|
315
|
+
For a typical category, few bases, if any, need to be added to force
|
|
316
|
+
``C3`` to give the desired order::
|
|
317
|
+
|
|
318
|
+
sage: C = FiniteFields()
|
|
319
|
+
sage: x = HierarchyElement(C, attrcall("super_categories"), attrgetter("_cmp_key"))
|
|
320
|
+
sage: x.mro == x.mro_standard
|
|
321
|
+
False
|
|
322
|
+
sage: x.all_bases_len()
|
|
323
|
+
72
|
|
324
|
+
sage: x.all_bases_controlled_len()
|
|
325
|
+
76
|
|
326
|
+
|
|
327
|
+
sage: C = GradedHopfAlgebrasWithBasis(QQ)
|
|
328
|
+
sage: x = HierarchyElement(C, attrcall("super_categories"), attrgetter("_cmp_key"))
|
|
329
|
+
sage: x._test_mro()
|
|
330
|
+
sage: x.mro == x.mro_standard
|
|
331
|
+
False
|
|
332
|
+
sage: x.all_bases_len()
|
|
333
|
+
114
|
|
334
|
+
sage: x.all_bases_controlled_len()
|
|
335
|
+
117
|
|
336
|
+
|
|
337
|
+
The following can be used to search through the Sage named categories
|
|
338
|
+
for any that requires the addition of some bases. The output may
|
|
339
|
+
change a bit when the category hierarchy is changed. As long as the
|
|
340
|
+
list below does not change radically, it's fine to just update this
|
|
341
|
+
doctest::
|
|
342
|
+
|
|
343
|
+
sage: from sage.categories.category import category_sample
|
|
344
|
+
sage: sorted([C for C in category_sample() # needs sage.combinat sage.graphs sage.modules sage.rings.number_field
|
|
345
|
+
....: if len(C._super_categories_for_classes) != len(C.super_categories())],
|
|
346
|
+
....: key=str)
|
|
347
|
+
[Category of affine Weyl groups,
|
|
348
|
+
Category of fields,
|
|
349
|
+
Category of finite Weyl groups,
|
|
350
|
+
Category of finite dimensional Hopf algebras with basis over Rational Field,
|
|
351
|
+
Category of finite dimensional algebras with basis over Rational Field,
|
|
352
|
+
Category of finite enumerated permutation groups,
|
|
353
|
+
Category of number fields]
|
|
354
|
+
|
|
355
|
+
AUTHOR:
|
|
356
|
+
|
|
357
|
+
- Nicolas M. Thiery (2012-09): initial version.
|
|
358
|
+
"""
|
|
359
|
+
# ****************************************************************************
|
|
360
|
+
# Copyright (C) 2012-2013 Nicolas M. Thiery <nthiery at users.sf.net>
|
|
361
|
+
#
|
|
362
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
363
|
+
# https://www.gnu.org/licenses/
|
|
364
|
+
# *****************************************************************************
|
|
365
|
+
|
|
366
|
+
from sage.misc.classcall_metaclass import ClasscallMetaclass, typecall
|
|
367
|
+
from sage.misc.cachefunc import cached_function, cached_method
|
|
368
|
+
from sage.misc.lazy_attribute import lazy_attribute
|
|
369
|
+
from sage.structure.dynamic_class import dynamic_class
|
|
370
|
+
|
|
371
|
+
##############################################################################
|
|
372
|
+
# Implementation of the total order between categories
|
|
373
|
+
##############################################################################
|
|
374
|
+
|
|
375
|
+
cdef tuple atoms = ("FacadeSets",
|
|
376
|
+
"FiniteSets", "Sets.Infinite", "EnumeratedSets", "SetsWithGrading",
|
|
377
|
+
"Posets", "LatticePosets", "Crystals", "AdditiveMagmas",
|
|
378
|
+
"FiniteDimensionalModules", "GradedModules", "ModulesWithBasis",
|
|
379
|
+
"Magmas", "Semigroups", "Monoids", "PermutationGroups",
|
|
380
|
+
"MagmasAndAdditiveMagmas", "Rngs", "Domains", "HopfAlgebras")
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
cdef dict flags = {atom: 1 << i for i, atom in enumerate(atoms)}
|
|
384
|
+
|
|
385
|
+
cdef class CmpKey:
|
|
386
|
+
r"""
|
|
387
|
+
This class implements the lazy attribute ``Category._cmp_key``.
|
|
388
|
+
|
|
389
|
+
The comparison key ``A._cmp_key`` of a category is used to define
|
|
390
|
+
an (almost) total order on non-join categories by setting, for two
|
|
391
|
+
categories `A` and `B`, `A<B` if ``A._cmp_key > B._cmp_key``. This
|
|
392
|
+
order in turn is used to give a normal form to join's, and help
|
|
393
|
+
toward having a consistent method resolution order for
|
|
394
|
+
parent/element classes.
|
|
395
|
+
|
|
396
|
+
The comparison key should satisfy the following properties:
|
|
397
|
+
|
|
398
|
+
- If `A` is a subcategory of `B`, then `A < B` (so that
|
|
399
|
+
``A._cmp_key > B._cmp_key``). In particular,
|
|
400
|
+
:class:`Objects() <Objects>` is the largest category.
|
|
401
|
+
|
|
402
|
+
- If `A != B` and taking the join of `A` and `B` makes sense
|
|
403
|
+
(e.g. taking the join of ``Algebras(GF(5))`` and
|
|
404
|
+
``Algebras(QQ)`` does not make sense), then `A<B` or `B<A`.
|
|
405
|
+
|
|
406
|
+
The rationale for the inversion above between `A<B` and
|
|
407
|
+
``A._cmp_key > B._cmp_key`` is that we want the order to
|
|
408
|
+
be compatible with inclusion of categories, yet it's easier in
|
|
409
|
+
practice to create keys that get bigger and bigger while we go
|
|
410
|
+
down the category hierarchy.
|
|
411
|
+
|
|
412
|
+
This implementation applies to join-irreducible categories
|
|
413
|
+
(i.e. categories that are not join categories). It returns a
|
|
414
|
+
pair of integers ``(flags, i)``, where ``flags`` is to be
|
|
415
|
+
interpreted as a bit vector. The first bit is set if ``self``
|
|
416
|
+
is a facade set. The second bit is set if ``self`` is finite.
|
|
417
|
+
And so on. The choice of the flags is adhoc and was primarily
|
|
418
|
+
crafted so that the order between categories would not change
|
|
419
|
+
too much upon integration of :issue:`13589` and would be
|
|
420
|
+
reasonably session independent. The number ``i`` is there
|
|
421
|
+
to resolve ambiguities; it is session dependent, and is
|
|
422
|
+
assigned increasingly when new categories are created.
|
|
423
|
+
|
|
424
|
+
.. NOTE::
|
|
425
|
+
|
|
426
|
+
This is currently not implemented using a
|
|
427
|
+
:class:`lazy_attribute` for speed reasons only (the code is in
|
|
428
|
+
Cython and takes advantage of the fact that Category objects
|
|
429
|
+
always have a ``__dict__`` dictionary)
|
|
430
|
+
|
|
431
|
+
.. TODO::
|
|
432
|
+
|
|
433
|
+
- Handle nicely (covariant) functorial constructions and axioms
|
|
434
|
+
|
|
435
|
+
EXAMPLES::
|
|
436
|
+
|
|
437
|
+
sage: Objects()._cmp_key
|
|
438
|
+
(0, 0)
|
|
439
|
+
sage: SetsWithPartialMaps()._cmp_key
|
|
440
|
+
(0, 1)
|
|
441
|
+
sage: Sets()._cmp_key
|
|
442
|
+
(0, 2)
|
|
443
|
+
sage: Sets().Facade()._cmp_key
|
|
444
|
+
(1, ...)
|
|
445
|
+
sage: Sets().Finite()._cmp_key
|
|
446
|
+
(2, ...)
|
|
447
|
+
sage: Sets().Infinite()._cmp_key
|
|
448
|
+
(4, ...)
|
|
449
|
+
sage: EnumeratedSets()._cmp_key
|
|
450
|
+
(8, ...)
|
|
451
|
+
sage: FiniteEnumeratedSets()._cmp_key
|
|
452
|
+
(10, ...)
|
|
453
|
+
sage: SetsWithGrading()._cmp_key
|
|
454
|
+
(16, ...)
|
|
455
|
+
sage: Posets()._cmp_key
|
|
456
|
+
(32, ...)
|
|
457
|
+
sage: LatticePosets()._cmp_key
|
|
458
|
+
(96, ...)
|
|
459
|
+
sage: Crystals()._cmp_key
|
|
460
|
+
(136, ...)
|
|
461
|
+
sage: AdditiveMagmas()._cmp_key
|
|
462
|
+
(256, ...)
|
|
463
|
+
sage: Magmas()._cmp_key
|
|
464
|
+
(4096, ...)
|
|
465
|
+
sage: CommutativeAdditiveSemigroups()._cmp_key
|
|
466
|
+
(256, ...)
|
|
467
|
+
sage: Rings()._cmp_key
|
|
468
|
+
(225536, ...)
|
|
469
|
+
sage: Algebras(QQ)._cmp_key
|
|
470
|
+
(225536, ...)
|
|
471
|
+
sage: AlgebrasWithBasis(QQ)._cmp_key
|
|
472
|
+
(227584, ...)
|
|
473
|
+
sage: GradedAlgebras(QQ)._cmp_key
|
|
474
|
+
(226560, ...)
|
|
475
|
+
sage: GradedAlgebrasWithBasis(QQ)._cmp_key
|
|
476
|
+
(228608, ...)
|
|
477
|
+
|
|
478
|
+
For backward compatibility we currently want the following comparisons::
|
|
479
|
+
|
|
480
|
+
sage: EnumeratedSets()._cmp_key > Sets().Facade()._cmp_key
|
|
481
|
+
True
|
|
482
|
+
sage: AdditiveMagmas()._cmp_key > EnumeratedSets()._cmp_key
|
|
483
|
+
True
|
|
484
|
+
|
|
485
|
+
sage: Category.join([EnumeratedSets(), Sets().Facade()]).parent_class._an_element_.__module__
|
|
486
|
+
'sage.categories.enumerated_sets'
|
|
487
|
+
|
|
488
|
+
sage: CommutativeAdditiveSemigroups()._cmp_key < Magmas()._cmp_key
|
|
489
|
+
True
|
|
490
|
+
sage: VectorSpaces(QQ)._cmp_key < Rings()._cmp_key
|
|
491
|
+
True
|
|
492
|
+
sage: VectorSpaces(QQ)._cmp_key < Magmas()._cmp_key
|
|
493
|
+
True
|
|
494
|
+
"""
|
|
495
|
+
cdef int count
|
|
496
|
+
|
|
497
|
+
def __init__(self):
|
|
498
|
+
"""
|
|
499
|
+
Set the internal category counter to zero.
|
|
500
|
+
|
|
501
|
+
EXAMPLES::
|
|
502
|
+
|
|
503
|
+
sage: Objects()._cmp_key # indirect doctest
|
|
504
|
+
(0, 0)
|
|
505
|
+
"""
|
|
506
|
+
self.count = -1
|
|
507
|
+
|
|
508
|
+
def __get__(self, object inst, object cls):
|
|
509
|
+
"""
|
|
510
|
+
Bind the comparison key to the given instance.
|
|
511
|
+
|
|
512
|
+
EXAMPLES::
|
|
513
|
+
|
|
514
|
+
sage: C = Algebras(FractionField(QQ['x']))
|
|
515
|
+
sage: C._cmp_key
|
|
516
|
+
(225536, ...)
|
|
517
|
+
sage: '_cmp_key' in C.__dict__ # indirect doctest
|
|
518
|
+
True
|
|
519
|
+
"""
|
|
520
|
+
# assert not isinstance(inst, JoinCategory)
|
|
521
|
+
# Note that cls is a DynamicClassMetaclass, hence not a type
|
|
522
|
+
cdef str classname = cls.__base__.__name__
|
|
523
|
+
cdef int flag = flags.get(classname, 0)
|
|
524
|
+
cdef object cat
|
|
525
|
+
for cat in inst._super_categories:
|
|
526
|
+
flag = flag | <int>(<tuple>(cat._cmp_key)[0])
|
|
527
|
+
self.count += 1
|
|
528
|
+
inst._cmp_key = (flag, self.count)
|
|
529
|
+
return flag, self.count
|
|
530
|
+
|
|
531
|
+
_cmp_key = CmpKey()
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
cdef class CmpKeyNamed:
|
|
535
|
+
"""
|
|
536
|
+
This class implements the lazy attribute ``CategoryWithParameters._cmp_key``.
|
|
537
|
+
|
|
538
|
+
.. SEEALSO::
|
|
539
|
+
|
|
540
|
+
- :class:`CmpKey`
|
|
541
|
+
- :class:`lazy_attribute`
|
|
542
|
+
- :class:`sage.categories.category.CategoryWithParameters`.
|
|
543
|
+
|
|
544
|
+
.. NOTE::
|
|
545
|
+
|
|
546
|
+
- The value of the attribute depends only on the parameters of
|
|
547
|
+
this category.
|
|
548
|
+
|
|
549
|
+
- This is currently not implemented using a
|
|
550
|
+
:class:`lazy_attribute` for speed reasons only.
|
|
551
|
+
|
|
552
|
+
EXAMPLES::
|
|
553
|
+
|
|
554
|
+
sage: Algebras(GF(3))._cmp_key == Algebras(GF(5))._cmp_key # indirect doctest
|
|
555
|
+
True
|
|
556
|
+
sage: Algebras(ZZ)._cmp_key != Algebras(GF(5))._cmp_key
|
|
557
|
+
True
|
|
558
|
+
"""
|
|
559
|
+
def __get__(self, object inst, object cls):
|
|
560
|
+
"""
|
|
561
|
+
EXAMPLES::
|
|
562
|
+
|
|
563
|
+
sage: Algebras(GF(3))._cmp_key == Algebras(GF(5))._cmp_key # indirect doctest
|
|
564
|
+
True
|
|
565
|
+
sage: Algebras(ZZ)._cmp_key != Algebras(GF(5))._cmp_key
|
|
566
|
+
True
|
|
567
|
+
"""
|
|
568
|
+
cdef dict D = cls._make_named_class_cache
|
|
569
|
+
cdef str name = "_cmp_key"
|
|
570
|
+
cdef tuple key = (cls.__base__, name, inst._make_named_class_key(name))
|
|
571
|
+
try:
|
|
572
|
+
result = D[key]
|
|
573
|
+
inst._cmp_key = result
|
|
574
|
+
return result
|
|
575
|
+
except KeyError:
|
|
576
|
+
pass
|
|
577
|
+
result = _cmp_key.__get__(inst, cls)
|
|
578
|
+
D[key] = result
|
|
579
|
+
return result
|
|
580
|
+
|
|
581
|
+
_cmp_key_named = CmpKeyNamed()
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
##############################################################################
|
|
585
|
+
|
|
586
|
+
def C3_merge(list lists):
|
|
587
|
+
r"""
|
|
588
|
+
Return the input lists merged using the ``C3`` algorithm.
|
|
589
|
+
|
|
590
|
+
EXAMPLES::
|
|
591
|
+
|
|
592
|
+
sage: from sage.misc.c3_controlled import C3_merge
|
|
593
|
+
sage: C3_merge([[3,2],[4,3,1]])
|
|
594
|
+
[4, 3, 2, 1]
|
|
595
|
+
sage: C3_merge([[3,2],[4,1]])
|
|
596
|
+
[3, 2, 4, 1]
|
|
597
|
+
|
|
598
|
+
This function is only used for testing and experimenting purposes,
|
|
599
|
+
but exercised quite some by the other doctests in this file.
|
|
600
|
+
|
|
601
|
+
It is an extract of :func:`sage.misc.c3.C3_algorithm`; the latter
|
|
602
|
+
could be possibly rewritten to use this one to avoid duplication.
|
|
603
|
+
"""
|
|
604
|
+
cdef list out = []
|
|
605
|
+
# Data structure / invariants:
|
|
606
|
+
# We will be working with the MROs of the super objects
|
|
607
|
+
# together with the list of bases of ``self``.
|
|
608
|
+
# Each list is split between its head (in ``heads``) and tail (in
|
|
609
|
+
# ``tails'') . Each tail is stored reversed, so that we can use a
|
|
610
|
+
# cheap pop() in lieue of pop(0). A duplicate of the tail is
|
|
611
|
+
# stored as a set in ``tailsets`` for cheap membership testing.
|
|
612
|
+
# Since we actually want comparison by identity, not equality,
|
|
613
|
+
# what we store is the set of memory locations of objects
|
|
614
|
+
cdef object O, X
|
|
615
|
+
cdef list tail, l
|
|
616
|
+
cdef set tailset
|
|
617
|
+
|
|
618
|
+
cdef list tails = [l[::-1] for l in lists if l]
|
|
619
|
+
cdef list heads = [tail.pop() for tail in tails]
|
|
620
|
+
cdef list tailsets = [set(O for O in tail) for tail in tails] # <size_t><void *>
|
|
621
|
+
|
|
622
|
+
cdef int i, j, nbheads
|
|
623
|
+
nbheads = len(heads)
|
|
624
|
+
cdef bint next_item_found
|
|
625
|
+
|
|
626
|
+
while nbheads:
|
|
627
|
+
for i in range(nbheads):
|
|
628
|
+
O = heads[i]
|
|
629
|
+
# Does O appear in none of the tails? ``all(O not in tail for tail in tailsets)``
|
|
630
|
+
next_item_found = True
|
|
631
|
+
for j in range(nbheads):
|
|
632
|
+
if j == i:
|
|
633
|
+
continue
|
|
634
|
+
tailset = tailsets[j]
|
|
635
|
+
if O in tailset: # <size_t><void *>O
|
|
636
|
+
next_item_found = False
|
|
637
|
+
break
|
|
638
|
+
if next_item_found:
|
|
639
|
+
out.append(O)
|
|
640
|
+
# Clear O from other heads, removing the line altogether
|
|
641
|
+
# if the tail is already empty.
|
|
642
|
+
# j goes down so that ``del heads[j]`` does not screw up the numbering
|
|
643
|
+
for j in range(nbheads-1, -1, -1):
|
|
644
|
+
if heads[j] == O: # is O
|
|
645
|
+
tail = tails[j]
|
|
646
|
+
if tail:
|
|
647
|
+
X = tail.pop()
|
|
648
|
+
heads[j] = X
|
|
649
|
+
tailset = tailsets[j]
|
|
650
|
+
tailset.remove(X) # <size_t><void *>X)
|
|
651
|
+
else:
|
|
652
|
+
del heads[j]
|
|
653
|
+
del tails[j]
|
|
654
|
+
del tailsets[j]
|
|
655
|
+
nbheads -= 1
|
|
656
|
+
break
|
|
657
|
+
if not next_item_found:
|
|
658
|
+
# No head is available
|
|
659
|
+
raise ValueError("Cannot merge the items %s." % ', '.join(repr(head) for head in heads))
|
|
660
|
+
return out
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
cpdef identity(x):
|
|
664
|
+
r"""
|
|
665
|
+
EXAMPLES::
|
|
666
|
+
|
|
667
|
+
sage: from sage.misc.c3_controlled import identity
|
|
668
|
+
sage: identity(10)
|
|
669
|
+
10
|
|
670
|
+
"""
|
|
671
|
+
return x
|
|
672
|
+
|
|
673
|
+
cpdef tuple C3_sorted_merge(list lists, key=identity):
|
|
674
|
+
r"""
|
|
675
|
+
Return the sorted input lists merged using the ``C3`` algorithm, with a twist.
|
|
676
|
+
|
|
677
|
+
INPUT:
|
|
678
|
+
|
|
679
|
+
- ``lists`` -- a non empty list (or iterable) of lists (or
|
|
680
|
+
iterables), each sorted strictly decreasingly according
|
|
681
|
+
to ``key``
|
|
682
|
+
- ``key`` -- a function
|
|
683
|
+
|
|
684
|
+
OUTPUT: a pair ``(result, suggestion)``
|
|
685
|
+
|
|
686
|
+
``result`` is the sorted list obtained by merging the lists in
|
|
687
|
+
``lists`` while removing duplicates, and ``suggestion`` is a list
|
|
688
|
+
such that applying ``C3`` on ``lists`` with its last list replaced
|
|
689
|
+
by ``suggestion`` would return ``result``.
|
|
690
|
+
|
|
691
|
+
EXAMPLES:
|
|
692
|
+
|
|
693
|
+
With the following input, :func:`C3_merge` returns right away a
|
|
694
|
+
sorted list::
|
|
695
|
+
|
|
696
|
+
sage: from sage.misc.c3_controlled import C3_merge
|
|
697
|
+
sage: C3_merge([[2],[1]])
|
|
698
|
+
[2, 1]
|
|
699
|
+
|
|
700
|
+
In that case, :func:`C3_sorted_merge` returns the same result,
|
|
701
|
+
with the last line unchanged::
|
|
702
|
+
|
|
703
|
+
sage: from sage.misc.c3_controlled import C3_sorted_merge
|
|
704
|
+
sage: C3_sorted_merge([[2],[1]])
|
|
705
|
+
([2, 1], [1])
|
|
706
|
+
|
|
707
|
+
On the other hand, with the following input, :func:`C3_merge`
|
|
708
|
+
returns a non sorted list::
|
|
709
|
+
|
|
710
|
+
sage: C3_merge([[1],[2]])
|
|
711
|
+
[1, 2]
|
|
712
|
+
|
|
713
|
+
Then, :func:`C3_sorted_merge` returns a sorted list, and suggests
|
|
714
|
+
to replace the last line by ``[2,1]``::
|
|
715
|
+
|
|
716
|
+
sage: C3_sorted_merge([[1],[2]])
|
|
717
|
+
([2, 1], [2, 1])
|
|
718
|
+
|
|
719
|
+
And indeed :func:`C3_merge` now returns the desired result::
|
|
720
|
+
|
|
721
|
+
sage: C3_merge([[1],[2,1]])
|
|
722
|
+
[2, 1]
|
|
723
|
+
|
|
724
|
+
From now on, we use this little wrapper that checks that
|
|
725
|
+
:func:`C3_merge`, with the suggestion of :func:`C3_sorted_merge`,
|
|
726
|
+
returns a sorted list::
|
|
727
|
+
|
|
728
|
+
sage: def C3_sorted_merge_check(lists):
|
|
729
|
+
....: result, suggestion = C3_sorted_merge(lists)
|
|
730
|
+
....: assert result == C3_merge(lists[:-1] + [suggestion])
|
|
731
|
+
....: return result, suggestion
|
|
732
|
+
|
|
733
|
+
Base cases::
|
|
734
|
+
|
|
735
|
+
sage: C3_sorted_merge_check([])
|
|
736
|
+
Traceback (most recent call last):
|
|
737
|
+
...
|
|
738
|
+
ValueError: The input should be a non empty list of lists (or iterables)
|
|
739
|
+
sage: C3_sorted_merge_check([[]])
|
|
740
|
+
([], [])
|
|
741
|
+
sage: C3_sorted_merge_check([[1]])
|
|
742
|
+
([1], [1])
|
|
743
|
+
sage: C3_sorted_merge_check([[3,2,1]])
|
|
744
|
+
([3, 2, 1], [3, 2, 1])
|
|
745
|
+
sage: C3_sorted_merge_check([[1],[1]])
|
|
746
|
+
([1], [1])
|
|
747
|
+
sage: C3_sorted_merge_check([[3,2,1],[3,2,1]])
|
|
748
|
+
([3, 2, 1], [3, 2, 1])
|
|
749
|
+
|
|
750
|
+
Exercise different states for the last line::
|
|
751
|
+
|
|
752
|
+
sage: C3_sorted_merge_check([[1],[2],[]])
|
|
753
|
+
([2, 1], [2, 1])
|
|
754
|
+
sage: C3_sorted_merge_check([[1],[2], [1]])
|
|
755
|
+
([2, 1], [2, 1])
|
|
756
|
+
|
|
757
|
+
Explore (all?) the different execution branches::
|
|
758
|
+
|
|
759
|
+
sage: C3_sorted_merge_check([[3,1],[4,2]])
|
|
760
|
+
([4, 3, 2, 1], [4, 3, 2, 1])
|
|
761
|
+
sage: C3_sorted_merge_check([[4,1],[3,2]])
|
|
762
|
+
([4, 3, 2, 1], [3, 2, 1])
|
|
763
|
+
sage: C3_sorted_merge_check([[3,2],[4,1]])
|
|
764
|
+
([4, 3, 2, 1], [4, 3, 1])
|
|
765
|
+
sage: C3_sorted_merge_check([[1],[4,3,2]])
|
|
766
|
+
([4, 3, 2, 1], [4, 3, 2, 1])
|
|
767
|
+
sage: C3_sorted_merge_check([[1],[3,2], []])
|
|
768
|
+
([3, 2, 1], [2, 1])
|
|
769
|
+
sage: C3_sorted_merge_check([[1],[4,3,2], []])
|
|
770
|
+
([4, 3, 2, 1], [2, 1])
|
|
771
|
+
sage: C3_sorted_merge_check([[1],[4,3,2], [2]])
|
|
772
|
+
([4, 3, 2, 1], [2, 1])
|
|
773
|
+
sage: C3_sorted_merge_check([[2],[1],[4],[3]])
|
|
774
|
+
([4, 3, 2, 1], [3, 2, 1])
|
|
775
|
+
sage: C3_sorted_merge_check([[2],[1],[4],[]])
|
|
776
|
+
([4, 2, 1], [4, 2, 1])
|
|
777
|
+
sage: C3_sorted_merge_check([[2],[1],[3],[4]])
|
|
778
|
+
([4, 3, 2, 1], [4, 3, 2, 1])
|
|
779
|
+
sage: C3_sorted_merge_check([[2],[1],[3,2,1],[3]])
|
|
780
|
+
([3, 2, 1], [3])
|
|
781
|
+
sage: C3_sorted_merge_check([[2],[1],[2,1],[3]])
|
|
782
|
+
([3, 2, 1], [3, 2])
|
|
783
|
+
|
|
784
|
+
Exercises adding one item when the last list has a single element;
|
|
785
|
+
the second example comes from an actual poset::
|
|
786
|
+
|
|
787
|
+
sage: C3_sorted_merge_check([[5,4,2],[4,3],[5,4,1]])
|
|
788
|
+
([5, 4, 3, 2, 1], [5, 4, 3, 2, 1])
|
|
789
|
+
sage: C3_sorted_merge_check([[6,4,2],[5,3],[6,5,1]])
|
|
790
|
+
([6, 5, 4, 3, 2, 1], [6, 5, 4, 3, 2, 1])
|
|
791
|
+
"""
|
|
792
|
+
lists = list(lists)
|
|
793
|
+
if not lists:
|
|
794
|
+
raise ValueError("The input should be a non empty list of lists (or iterables)")
|
|
795
|
+
# for l in lists:
|
|
796
|
+
# assert sorted(l, key = key, reverse=True) == l,\
|
|
797
|
+
# "Each input list should be sorted %s"%l
|
|
798
|
+
|
|
799
|
+
cdef set suggestion = set(lists[-1])
|
|
800
|
+
cdef bint last_list_non_empty = bool(lists[-1])
|
|
801
|
+
cdef list out = []
|
|
802
|
+
# Data structure / invariants:
|
|
803
|
+
# - Each list remains sorted and duplicate free.
|
|
804
|
+
# - Each list only evolves by popping its largest element
|
|
805
|
+
# Exception: elements can be inserted back into the last list.
|
|
806
|
+
# - Whenever a list becomes empty, it's removed from the data structure.
|
|
807
|
+
# The order between the (remaining non empty) lists remains unchanged.
|
|
808
|
+
# - nbheads contains the number of lists appearing in the data structure.
|
|
809
|
+
# - The flag ``last_list_non_empty`` states whether the last
|
|
810
|
+
# list is currently non empty; if yes, by the above, this list is stored last.
|
|
811
|
+
# - Each list is split between its head (in ``heads``) and tail (in ``tails'').
|
|
812
|
+
# - Each tail is stored reversed, so that we can use a cheap ``pop()``
|
|
813
|
+
# in lieue of ``pop(0)``.
|
|
814
|
+
# - A duplicate of this tail is stored as a set (of keys) in
|
|
815
|
+
# ``tailsets``, for cheap membership testing.
|
|
816
|
+
|
|
817
|
+
cdef int i, j, max_i
|
|
818
|
+
cdef bint cont
|
|
819
|
+
cdef list tail, l
|
|
820
|
+
cdef set tailset
|
|
821
|
+
|
|
822
|
+
cdef list tails = [l[::-1] for l in lists if l]
|
|
823
|
+
cdef list heads = [tail.pop() for tail in tails]
|
|
824
|
+
cdef set tmp_set
|
|
825
|
+
cdef list tailsets = [] # remove closure [set(key(O) for O in tail) for tail in tails]
|
|
826
|
+
for tail in tails:
|
|
827
|
+
tmp_set = set()
|
|
828
|
+
for O in tail:
|
|
829
|
+
tmp_set.add(key(O))
|
|
830
|
+
tailsets.append(tmp_set)
|
|
831
|
+
# for i in range(len(tails)):
|
|
832
|
+
# assert len(tails[i]) == len(tailsets[i]), \
|
|
833
|
+
# "All objects should be distinct and have distinct sorting key!"+'\n'.join(" - %s: %s"%(key(O), O) for O in sorted(tails[i], key=key))
|
|
834
|
+
|
|
835
|
+
cdef int nbheads = len(heads)
|
|
836
|
+
cdef dict holder = {}
|
|
837
|
+
|
|
838
|
+
# def print_state():
|
|
839
|
+
# print("-- %s -- %s ------"%(out,suggestion))
|
|
840
|
+
# for i in range(nbheads):
|
|
841
|
+
# print([heads[i]] + list(reversed(tails[i])))
|
|
842
|
+
|
|
843
|
+
# def check_state():
|
|
844
|
+
# for i in range(nbheads):
|
|
845
|
+
# l = tails[i]
|
|
846
|
+
# if heads[i] is not None:
|
|
847
|
+
# l = l+[heads[i]]
|
|
848
|
+
# assert sorted(l, key=key) == l
|
|
849
|
+
# assert len(set(l)) == len(l)
|
|
850
|
+
# assert len(tails[i]) == len(set(tails[i])), \
|
|
851
|
+
# "C3's input list should have no repeats %s"%tails[i]
|
|
852
|
+
# assert set(key(O) for O in tails[i]) == tailsets[i], \
|
|
853
|
+
# "inconsistent tails[i] and tailsets[i]: %s %s"%(tails[i], tailsets[i])
|
|
854
|
+
# assert len(tails[i]) == len(tailsets[i]), \
|
|
855
|
+
# "keys should be distinct"%(tails[i])
|
|
856
|
+
|
|
857
|
+
while nbheads:
|
|
858
|
+
# print_state()
|
|
859
|
+
# check_state()
|
|
860
|
+
# Find the position of the largest head which will become the next item
|
|
861
|
+
max_i = 0
|
|
862
|
+
max_key = key(heads[0])
|
|
863
|
+
for i in range(1, nbheads):
|
|
864
|
+
O = heads[i]
|
|
865
|
+
O_key = key(O)
|
|
866
|
+
if O_key > max_key:
|
|
867
|
+
max_i = i
|
|
868
|
+
max_key = O_key
|
|
869
|
+
max_value = heads[max_i]
|
|
870
|
+
|
|
871
|
+
# Find all the bad choices
|
|
872
|
+
max_bad = None
|
|
873
|
+
for i in range(max_i):
|
|
874
|
+
O = heads[i]
|
|
875
|
+
# Does O appear in none of the tails?
|
|
876
|
+
O_key = key(O)
|
|
877
|
+
# replace the closure
|
|
878
|
+
# if any(O_key in tailsets[j] for j in range(nbheads) if j != i): continue
|
|
879
|
+
cont = False
|
|
880
|
+
for j in range(i):
|
|
881
|
+
if O_key in tailsets[j]:
|
|
882
|
+
cont = True
|
|
883
|
+
break
|
|
884
|
+
if cont:
|
|
885
|
+
continue
|
|
886
|
+
for j from i<j<nbheads:
|
|
887
|
+
if O_key in tailsets[j]:
|
|
888
|
+
cont = True
|
|
889
|
+
break
|
|
890
|
+
if cont:
|
|
891
|
+
continue
|
|
892
|
+
|
|
893
|
+
# The plain C3 algorithm would have chosen O as next item!
|
|
894
|
+
if max_bad is None or O_key > key(max_bad):
|
|
895
|
+
max_bad = O
|
|
896
|
+
|
|
897
|
+
# We prevent this choice by inserting O in the tail of the
|
|
898
|
+
# suggestions. At this stage, we only insert it into the
|
|
899
|
+
# last list. Later, we will make sure that it is actually
|
|
900
|
+
# in the tail of the last list.
|
|
901
|
+
if not last_list_non_empty:
|
|
902
|
+
# Reinstate the last list for the suggestion
|
|
903
|
+
# if it had disappeared before
|
|
904
|
+
heads.append(O)
|
|
905
|
+
tails.append([])
|
|
906
|
+
tailsets.append(set())
|
|
907
|
+
nbheads += 1
|
|
908
|
+
last_list_non_empty = True
|
|
909
|
+
elif O_key > key(heads[-1]):
|
|
910
|
+
tails[-1].append(heads[-1])
|
|
911
|
+
tailsets[-1].add(key(heads[-1]))
|
|
912
|
+
heads[-1] = O
|
|
913
|
+
elif O != heads[-1]:
|
|
914
|
+
assert O_key not in tailsets[-1], "C3 should not have chosen this O"
|
|
915
|
+
# Use a heap or something for fast sorted insertion?
|
|
916
|
+
# Since Python uses TimSort, that's probably not so bad.
|
|
917
|
+
tails[-1].append(O)
|
|
918
|
+
tails[-1].sort(key=key)
|
|
919
|
+
tailsets[-1].add(O_key)
|
|
920
|
+
suggestion.add(O)
|
|
921
|
+
# check_state()
|
|
922
|
+
|
|
923
|
+
# Insert max_value in the last list, if needed to hold off the bad items
|
|
924
|
+
if max_bad is not None:
|
|
925
|
+
last_head = heads[-1]
|
|
926
|
+
if last_head is None or key(max_bad) >= key(last_head):
|
|
927
|
+
if last_head is not None and last_head != max_bad:
|
|
928
|
+
tails[-1].append(last_head)
|
|
929
|
+
tailsets[-1].add(key(last_head))
|
|
930
|
+
# check_state()
|
|
931
|
+
heads[-1] = max_value
|
|
932
|
+
holder[max_bad] = max_value
|
|
933
|
+
# check_state()
|
|
934
|
+
|
|
935
|
+
out.append(max_value)
|
|
936
|
+
# Clear O from other heads, removing the line altogether
|
|
937
|
+
# if the tail is already empty.
|
|
938
|
+
# j goes down so that ``del heads[j]`` does not screw up the numbering
|
|
939
|
+
for j in range(nbheads-1, -1, -1):
|
|
940
|
+
if heads[j] == max_value:
|
|
941
|
+
tail = tails[j]
|
|
942
|
+
if tail:
|
|
943
|
+
X = tail.pop()
|
|
944
|
+
heads[j] = X
|
|
945
|
+
tailset = tailsets[j]
|
|
946
|
+
tailset.remove(key(X))
|
|
947
|
+
else:
|
|
948
|
+
del heads[j]
|
|
949
|
+
del tails[j]
|
|
950
|
+
del tailsets[j]
|
|
951
|
+
nbheads -= 1
|
|
952
|
+
if last_list_non_empty and j == nbheads:
|
|
953
|
+
last_list_non_empty = False
|
|
954
|
+
# check_state()
|
|
955
|
+
suggestion.update(holder.values())
|
|
956
|
+
cdef list suggestion_list = sorted(suggestion, key=key, reverse=True)
|
|
957
|
+
# assert C3_merge(lists[:-1]+[suggestion_list]) == out
|
|
958
|
+
return (out, suggestion_list)
|
|
959
|
+
|
|
960
|
+
|
|
961
|
+
class HierarchyElement(object, metaclass=ClasscallMetaclass):
|
|
962
|
+
"""
|
|
963
|
+
A class for elements in a hierarchy.
|
|
964
|
+
|
|
965
|
+
This class is for testing and experimenting with various variants
|
|
966
|
+
of the ``C3`` algorithm to compute a linear extension of the
|
|
967
|
+
elements above an element in a hierarchy. Given the topic at hand,
|
|
968
|
+
we use the following naming conventions. For `x` an element of the
|
|
969
|
+
hierarchy, we call the elements just above `x` its *bases*, and
|
|
970
|
+
the linear extension of all elements above `x` its *MRO*.
|
|
971
|
+
|
|
972
|
+
By convention, the bases are given as lists of instances of
|
|
973
|
+
:class:`HierarchyElement`, and MROs are given a list of the
|
|
974
|
+
corresponding values.
|
|
975
|
+
|
|
976
|
+
INPUT:
|
|
977
|
+
|
|
978
|
+
- ``value`` -- an object
|
|
979
|
+
- ``succ`` -- a successor function, poset or digraph from which
|
|
980
|
+
one can recover the successors of ``value``
|
|
981
|
+
- ``key`` -- a function taking values as input (default: the
|
|
982
|
+
identity) this function is used to compute comparison keys for
|
|
983
|
+
sorting elements of the hierarchy.
|
|
984
|
+
|
|
985
|
+
.. NOTE::
|
|
986
|
+
|
|
987
|
+
Constructing a :class:`HierarchyElement` immediately constructs the
|
|
988
|
+
whole hierarchy above it.
|
|
989
|
+
|
|
990
|
+
EXAMPLES:
|
|
991
|
+
|
|
992
|
+
See the introduction of this module :mod:`sage.misc.c3_controlled`
|
|
993
|
+
for many examples. Here we consider a large example, originally
|
|
994
|
+
taken from the hierarchy of categories above
|
|
995
|
+
:class:`HopfAlgebrasWithBasis`::
|
|
996
|
+
|
|
997
|
+
sage: from sage.misc.c3_controlled import HierarchyElement
|
|
998
|
+
sage: G = DiGraph({ # needs sage.graphs
|
|
999
|
+
....: 44 : [43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
|
|
1000
|
+
....: 43 : [42, 41, 40, 36, 35, 39, 38, 37, 33, 32, 31, 30, 29, 28, 27, 26, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
|
|
1001
|
+
....: 42 : [36, 35, 37, 30, 29, 28, 27, 26, 15, 14, 12, 11, 9, 8, 5, 3, 2, 1, 0],
|
|
1002
|
+
....: 41 : [40, 36, 35, 33, 32, 31, 30, 29, 28, 27, 26, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
|
|
1003
|
+
....: 40 : [36, 35, 32, 31, 30, 29, 28, 27, 26, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
|
|
1004
|
+
....: 39 : [38, 37, 33, 32, 31, 30, 29, 28, 27, 26, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
|
|
1005
|
+
....: 38 : [37, 33, 32, 31, 30, 29, 28, 27, 26, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
|
|
1006
|
+
....: 37 : [30, 29, 28, 27, 26, 15, 14, 12, 11, 9, 8, 5, 3, 2, 1, 0],
|
|
1007
|
+
....: 36 : [35, 30, 29, 28, 27, 26, 15, 14, 12, 11, 9, 8, 5, 3, 2, 1, 0],
|
|
1008
|
+
....: 35 : [29, 28, 27, 26, 15, 14, 12, 11, 9, 8, 5, 3, 2, 1, 0],
|
|
1009
|
+
....: 34 : [33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
|
|
1010
|
+
....: 33 : [32, 31, 30, 29, 28, 27, 26, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
|
|
1011
|
+
....: 32 : [31, 30, 29, 28, 27, 26, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
|
|
1012
|
+
....: 31 : [30, 29, 28, 27, 26, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
|
|
1013
|
+
....: 30 : [29, 28, 27, 26, 15, 14, 12, 11, 9, 8, 5, 3, 2, 1, 0],
|
|
1014
|
+
....: 29 : [28, 27, 26, 15, 14, 12, 11, 9, 8, 5, 3, 2, 1, 0],
|
|
1015
|
+
....: 28 : [27, 26, 15, 14, 12, 11, 9, 8, 5, 3, 2, 1, 0],
|
|
1016
|
+
....: 27 : [15, 14, 12, 11, 9, 8, 5, 3, 2, 1, 0],
|
|
1017
|
+
....: 26 : [15, 14, 12, 11, 9, 8, 5, 3, 2, 1, 0],
|
|
1018
|
+
....: 25 : [24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
|
|
1019
|
+
....: 24 : [4, 2, 1, 0],
|
|
1020
|
+
....: 23 : [22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
|
|
1021
|
+
....: 22 : [21, 20, 18, 17, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
|
|
1022
|
+
....: 21 : [20, 17, 4, 2, 1, 0],
|
|
1023
|
+
....: 20 : [4, 2, 1, 0],
|
|
1024
|
+
....: 19 : [18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
|
|
1025
|
+
....: 18 : [17, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
|
|
1026
|
+
....: 17 : [4, 2, 1, 0],
|
|
1027
|
+
....: 16 : [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
|
|
1028
|
+
....: 15 : [14, 12, 11, 9, 8, 5, 3, 2, 1, 0],
|
|
1029
|
+
....: 14 : [11, 3, 2, 1, 0],
|
|
1030
|
+
....: 13 : [12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
|
|
1031
|
+
....: 12 : [11, 9, 8, 5, 3, 2, 1, 0],
|
|
1032
|
+
....: 11 : [3, 2, 1, 0],
|
|
1033
|
+
....: 10 : [9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
|
|
1034
|
+
....: 9 : [8, 5, 3, 2, 1, 0],
|
|
1035
|
+
....: 8 : [3, 2, 1, 0],
|
|
1036
|
+
....: 7 : [6, 5, 4, 3, 2, 1, 0],
|
|
1037
|
+
....: 6 : [4, 3, 2, 1, 0],
|
|
1038
|
+
....: 5 : [3, 2, 1, 0],
|
|
1039
|
+
....: 4 : [2, 1, 0],
|
|
1040
|
+
....: 3 : [2, 1, 0],
|
|
1041
|
+
....: 2 : [1, 0],
|
|
1042
|
+
....: 1 : [0],
|
|
1043
|
+
....: 0 : [],
|
|
1044
|
+
....: })
|
|
1045
|
+
|
|
1046
|
+
sage: # needs sage.combinat sage.graphs
|
|
1047
|
+
sage: x = HierarchyElement(44, G)
|
|
1048
|
+
sage: x.mro
|
|
1049
|
+
[44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
|
|
1050
|
+
sage: x.cls
|
|
1051
|
+
<class '44.cls'>
|
|
1052
|
+
sage: x.cls.mro()
|
|
1053
|
+
[<class '44.cls'>, <class '43.cls'>, <class '42.cls'>, <class '41.cls'>, <class '40.cls'>, <class '39.cls'>, <class '38.cls'>, <class '37.cls'>, <class '36.cls'>, <class '35.cls'>, <class '34.cls'>, <class '33.cls'>, <class '32.cls'>, <class '31.cls'>, <class '30.cls'>, <class '29.cls'>, <class '28.cls'>, <class '27.cls'>, <class '26.cls'>, <class '25.cls'>, <class '24.cls'>, <class '23.cls'>, <class '22.cls'>, <class '21.cls'>, <class '20.cls'>, <class '19.cls'>, <class '18.cls'>, <class '17.cls'>, <class '16.cls'>, <class '15.cls'>, <class '14.cls'>, <class '13.cls'>, <class '12.cls'>, <class '11.cls'>, <class '10.cls'>, <class '9.cls'>, <class '8.cls'>, <class '7.cls'>, <class '6.cls'>, <class '5.cls'>, <class '4.cls'>, <class '3.cls'>, <class '2.cls'>, <class '1.cls'>, <class '0.cls'>, <... 'object'>]
|
|
1054
|
+
"""
|
|
1055
|
+
@staticmethod
|
|
1056
|
+
def __classcall__(cls, value, succ, key=None):
|
|
1057
|
+
"""
|
|
1058
|
+
EXAMPLES::
|
|
1059
|
+
|
|
1060
|
+
sage: # needs sage.combinat sage.graphs
|
|
1061
|
+
sage: from sage.misc.c3_controlled import HierarchyElement
|
|
1062
|
+
sage: P = Poset((divisors(30), lambda x, y: y.divides(x)), facade=True)
|
|
1063
|
+
sage: x = HierarchyElement(10, P)
|
|
1064
|
+
sage: x
|
|
1065
|
+
10
|
|
1066
|
+
sage: x.bases
|
|
1067
|
+
[5, 2]
|
|
1068
|
+
sage: x.mro
|
|
1069
|
+
[10, 5, 2, 1]
|
|
1070
|
+
"""
|
|
1071
|
+
from sage.categories.sets_cat import Sets
|
|
1072
|
+
|
|
1073
|
+
try:
|
|
1074
|
+
from sage.combinat.posets.poset_examples import Posets
|
|
1075
|
+
except ImportError:
|
|
1076
|
+
pass
|
|
1077
|
+
else:
|
|
1078
|
+
if succ in Posets():
|
|
1079
|
+
assert succ in Sets().Facade()
|
|
1080
|
+
succ = succ.upper_covers
|
|
1081
|
+
|
|
1082
|
+
try:
|
|
1083
|
+
from sage.graphs.digraph import DiGraph
|
|
1084
|
+
except ImportError:
|
|
1085
|
+
pass
|
|
1086
|
+
else:
|
|
1087
|
+
if isinstance(succ, DiGraph):
|
|
1088
|
+
succ = succ.copy()
|
|
1089
|
+
succ._immutable = True
|
|
1090
|
+
succ = succ.neighbors_out
|
|
1091
|
+
|
|
1092
|
+
if key is None:
|
|
1093
|
+
key = identity
|
|
1094
|
+
|
|
1095
|
+
@cached_function
|
|
1096
|
+
def f(x):
|
|
1097
|
+
return typecall(cls, x, [f(y) for y in succ(x)], key, f)
|
|
1098
|
+
|
|
1099
|
+
return f(value)
|
|
1100
|
+
|
|
1101
|
+
def __init__(self, value, bases, key, from_value):
|
|
1102
|
+
"""
|
|
1103
|
+
EXAMPLES::
|
|
1104
|
+
|
|
1105
|
+
sage: # needs sage.graphs
|
|
1106
|
+
sage: from sage.misc.c3_controlled import HierarchyElement
|
|
1107
|
+
sage: P = Poset((divisors(30), lambda x, y: y.divides(x)), facade=True)
|
|
1108
|
+
sage: x = HierarchyElement(10, P)
|
|
1109
|
+
sage: x
|
|
1110
|
+
10
|
|
1111
|
+
sage: x.value
|
|
1112
|
+
10
|
|
1113
|
+
sage: x._bases
|
|
1114
|
+
[5, 2]
|
|
1115
|
+
sage: x._key
|
|
1116
|
+
<built-in function identity>
|
|
1117
|
+
sage: x._key(10)
|
|
1118
|
+
10
|
|
1119
|
+
|
|
1120
|
+
The ``_from_value`` attribute is a function that can be used
|
|
1121
|
+
to reconstruct an element of the hierarchy from its value::
|
|
1122
|
+
|
|
1123
|
+
sage: x._from_value # needs sage.graphs
|
|
1124
|
+
Cached version of <...__classcall__...>
|
|
1125
|
+
sage: x._from_value(x.value) is x # needs sage.graphs
|
|
1126
|
+
True
|
|
1127
|
+
"""
|
|
1128
|
+
self.value = value
|
|
1129
|
+
self._bases = sorted(bases, key=lambda x: key(x.value), reverse=True)
|
|
1130
|
+
self._key = key
|
|
1131
|
+
self._from_value = from_value
|
|
1132
|
+
|
|
1133
|
+
def __repr__(self):
|
|
1134
|
+
"""
|
|
1135
|
+
Return the representation of ``self`` which is that of its value.
|
|
1136
|
+
|
|
1137
|
+
EXAMPLES::
|
|
1138
|
+
|
|
1139
|
+
sage: from sage.misc.c3_controlled import HierarchyElement
|
|
1140
|
+
sage: P = Poset((divisors(30), lambda x, y: y.divides(x)), facade=True) # needs sage.graphs
|
|
1141
|
+
sage: x = HierarchyElement(10, P) # needs sage.graphs
|
|
1142
|
+
sage: x # needs sage.graphs
|
|
1143
|
+
10
|
|
1144
|
+
"""
|
|
1145
|
+
return repr(self.value)
|
|
1146
|
+
|
|
1147
|
+
@lazy_attribute
|
|
1148
|
+
def bases(self):
|
|
1149
|
+
"""
|
|
1150
|
+
The bases of ``self``.
|
|
1151
|
+
|
|
1152
|
+
The bases are given as a list of instances of
|
|
1153
|
+
:class:`HierarchyElement`, sorted decreasingly according to
|
|
1154
|
+
the ``key`` function.
|
|
1155
|
+
|
|
1156
|
+
EXAMPLES::
|
|
1157
|
+
|
|
1158
|
+
sage: # needs sage.combinat sage.graphs
|
|
1159
|
+
sage: from sage.misc.c3_controlled import HierarchyElement
|
|
1160
|
+
sage: P = Poset((divisors(30), lambda x, y: y.divides(x)), facade=True)
|
|
1161
|
+
sage: x = HierarchyElement(10, P)
|
|
1162
|
+
sage: x.bases
|
|
1163
|
+
[5, 2]
|
|
1164
|
+
sage: type(x.bases[0])
|
|
1165
|
+
<class 'sage.misc.c3_controlled.HierarchyElement'>
|
|
1166
|
+
sage: x.mro
|
|
1167
|
+
[10, 5, 2, 1]
|
|
1168
|
+
sage: x._bases_controlled
|
|
1169
|
+
[5, 2]
|
|
1170
|
+
"""
|
|
1171
|
+
return self._bases
|
|
1172
|
+
|
|
1173
|
+
@lazy_attribute
|
|
1174
|
+
def mro(self):
|
|
1175
|
+
"""
|
|
1176
|
+
The MRO for this object, calculated with :meth:`C3_sorted_merge`.
|
|
1177
|
+
|
|
1178
|
+
EXAMPLES::
|
|
1179
|
+
|
|
1180
|
+
sage: # needs sage.graphs
|
|
1181
|
+
sage: from sage.misc.c3_controlled import HierarchyElement, C3_sorted_merge, identity
|
|
1182
|
+
sage: P = Poset({7: [5, 6], 5: [1, 2], 6: [3, 4]}, facade=True)
|
|
1183
|
+
sage: x = HierarchyElement(5, P)
|
|
1184
|
+
sage: x.mro
|
|
1185
|
+
[5, 2, 1]
|
|
1186
|
+
sage: x = HierarchyElement(6, P)
|
|
1187
|
+
sage: x.mro
|
|
1188
|
+
[6, 4, 3]
|
|
1189
|
+
sage: x = HierarchyElement(7, P)
|
|
1190
|
+
sage: x.mro
|
|
1191
|
+
[7, 6, 5, 4, 3, 2, 1]
|
|
1192
|
+
|
|
1193
|
+
sage: C3_sorted_merge([[6, 4, 3], [5, 2, 1], [6, 5]], identity) # needs sage.graphs
|
|
1194
|
+
([6, 5, 4, 3, 2, 1], [6, 5, 4])
|
|
1195
|
+
|
|
1196
|
+
TESTS::
|
|
1197
|
+
|
|
1198
|
+
sage: assert all(isinstance(v, Integer) for v in x.mro) # needs sage.graphs
|
|
1199
|
+
"""
|
|
1200
|
+
bases = self._bases
|
|
1201
|
+
result, suggestion = C3_sorted_merge([base.mro for base in bases]+[[base.value for base in bases]], key=self._key)
|
|
1202
|
+
result = [self.value] + result
|
|
1203
|
+
self._bases_controlled = suggestion
|
|
1204
|
+
return result
|
|
1205
|
+
|
|
1206
|
+
@lazy_attribute
|
|
1207
|
+
def _bases_controlled(self):
|
|
1208
|
+
"""
|
|
1209
|
+
A list of bases controlled by :meth:`C3_sorted_merge`.
|
|
1210
|
+
|
|
1211
|
+
This triggers the calculation of the MRO using
|
|
1212
|
+
:meth:`C3_sorted_merge`, which sets this attribute as a side
|
|
1213
|
+
effect.
|
|
1214
|
+
|
|
1215
|
+
EXAMPLES::
|
|
1216
|
+
|
|
1217
|
+
sage: # needs sage.graphs
|
|
1218
|
+
sage: from sage.misc.c3_controlled import HierarchyElement
|
|
1219
|
+
sage: P = Poset({7: [5, 6], 5: [1, 2], 6: [3, 4]}, facade=True)
|
|
1220
|
+
sage: x = HierarchyElement(7, P)
|
|
1221
|
+
sage: x._bases
|
|
1222
|
+
[6, 5]
|
|
1223
|
+
sage: x._bases_controlled
|
|
1224
|
+
[6, 5, 4]
|
|
1225
|
+
"""
|
|
1226
|
+
self.mro
|
|
1227
|
+
return self._bases_controlled
|
|
1228
|
+
|
|
1229
|
+
@lazy_attribute
|
|
1230
|
+
def mro_standard(self):
|
|
1231
|
+
"""
|
|
1232
|
+
The MRO for this object, calculated with :meth:`C3_merge`.
|
|
1233
|
+
|
|
1234
|
+
EXAMPLES::
|
|
1235
|
+
|
|
1236
|
+
sage: from sage.misc.c3_controlled import HierarchyElement, C3_merge
|
|
1237
|
+
|
|
1238
|
+
sage: # needs sage.graphs
|
|
1239
|
+
sage: P = Poset({7: [5, 6], 5: [1, 2], 6: [3, 4]}, facade=True)
|
|
1240
|
+
sage: x = HierarchyElement(5, P)
|
|
1241
|
+
sage: x.mro_standard
|
|
1242
|
+
[5, 2, 1]
|
|
1243
|
+
sage: x = HierarchyElement(6, P)
|
|
1244
|
+
sage: x.mro_standard
|
|
1245
|
+
[6, 4, 3]
|
|
1246
|
+
sage: x = HierarchyElement(7, P)
|
|
1247
|
+
sage: x.mro_standard
|
|
1248
|
+
[7, 6, 4, 3, 5, 2, 1]
|
|
1249
|
+
|
|
1250
|
+
sage: C3_merge([[6, 4, 3], [5, 2, 1], [6, 5]])
|
|
1251
|
+
[6, 4, 3, 5, 2, 1]
|
|
1252
|
+
|
|
1253
|
+
TESTS::
|
|
1254
|
+
|
|
1255
|
+
sage: assert all(isinstance(v, Integer) for v in x.mro_standard) # needs sage.graphs
|
|
1256
|
+
"""
|
|
1257
|
+
bases = self._bases
|
|
1258
|
+
return [self.value] + C3_merge([base.mro_standard for base in bases]+[[base.value for base in bases]])
|
|
1259
|
+
|
|
1260
|
+
@lazy_attribute
|
|
1261
|
+
def mro_controlled(self):
|
|
1262
|
+
"""
|
|
1263
|
+
The MRO for this object, calculated with :meth:`C3_merge`, under
|
|
1264
|
+
control of :func:`C3_sorted_merge`
|
|
1265
|
+
|
|
1266
|
+
EXAMPLES::
|
|
1267
|
+
|
|
1268
|
+
sage: from sage.misc.c3_controlled import HierarchyElement, C3_merge
|
|
1269
|
+
|
|
1270
|
+
sage: # needs sage.graphs
|
|
1271
|
+
sage: P = Poset({7: [5, 6], 5: [1, 2], 6: [3, 4]}, facade=True)
|
|
1272
|
+
sage: x = HierarchyElement(5, P)
|
|
1273
|
+
sage: x.mro_controlled
|
|
1274
|
+
[5, 2, 1]
|
|
1275
|
+
sage: x = HierarchyElement(6, P)
|
|
1276
|
+
sage: x.mro_controlled
|
|
1277
|
+
[6, 4, 3]
|
|
1278
|
+
sage: x = HierarchyElement(7, P)
|
|
1279
|
+
sage: x.mro_controlled
|
|
1280
|
+
[7, 6, 5, 4, 3, 2, 1]
|
|
1281
|
+
sage: x._bases
|
|
1282
|
+
[6, 5]
|
|
1283
|
+
sage: x._bases_controlled
|
|
1284
|
+
[6, 5, 4]
|
|
1285
|
+
|
|
1286
|
+
sage: C3_merge([[6, 4, 3], [5, 2, 1], [6, 5]])
|
|
1287
|
+
[6, 4, 3, 5, 2, 1]
|
|
1288
|
+
sage: C3_merge([[6, 4, 3], [5, 2, 1], [6, 5, 4]])
|
|
1289
|
+
[6, 5, 4, 3, 2, 1]
|
|
1290
|
+
|
|
1291
|
+
TESTS::
|
|
1292
|
+
|
|
1293
|
+
sage: assert all(isinstance(v, Integer) for v in x.mro_controlled) # needs sage.graphs
|
|
1294
|
+
"""
|
|
1295
|
+
return [self.value] + C3_merge([base.mro_controlled for base in self._bases]+[self._bases_controlled])
|
|
1296
|
+
|
|
1297
|
+
@cached_method
|
|
1298
|
+
def _test_mro(self):
|
|
1299
|
+
r"""
|
|
1300
|
+
Run consistency tests.
|
|
1301
|
+
|
|
1302
|
+
This checks in particular that the instrumented ``C3`` and
|
|
1303
|
+
controlled ``C3`` algorithms give, as desired, the
|
|
1304
|
+
decreasingly sorted list of the objects above in the
|
|
1305
|
+
hierarchy. For the controlled ``C3`` algorithm, this includes
|
|
1306
|
+
both Sage's implementation, and Python's implementation (by
|
|
1307
|
+
constructing an appropriate hierarchy of classes).
|
|
1308
|
+
|
|
1309
|
+
It is cached because it is run recursively on the elements
|
|
1310
|
+
above ``self``.
|
|
1311
|
+
|
|
1312
|
+
EXAMPLES::
|
|
1313
|
+
|
|
1314
|
+
sage: from sage.misc.c3_controlled import HierarchyElement
|
|
1315
|
+
sage: P = Poset({7: [5, 6], 5: [1, 2], 6: [3, 4]}, facade=True) # needs sage.graphs
|
|
1316
|
+
sage: x = HierarchyElement(7, P) # needs sage.graphs
|
|
1317
|
+
sage: x._test_mro() # needs sage.graphs
|
|
1318
|
+
"""
|
|
1319
|
+
for b in self._bases:
|
|
1320
|
+
b._test_mro()
|
|
1321
|
+
try:
|
|
1322
|
+
assert self.mro_standard[0] == self.value
|
|
1323
|
+
except ValueError:
|
|
1324
|
+
# standard C3 failed to compute a mro; that's ok
|
|
1325
|
+
pass
|
|
1326
|
+
assert self.mro[0] == self.value
|
|
1327
|
+
assert self.mro_controlled[0] == self.value
|
|
1328
|
+
assert sorted([x.value for x in self.all_bases()], key=self._key, reverse = True) == self.mro
|
|
1329
|
+
assert self.mro == self.mro_controlled
|
|
1330
|
+
assert self.cls.mro() == [self._from_value(b).cls for b in self.mro]+[object]
|
|
1331
|
+
|
|
1332
|
+
@lazy_attribute
|
|
1333
|
+
def cls(self):
|
|
1334
|
+
"""
|
|
1335
|
+
Return a Python class with inheritance graph parallel to the hierarchy above ``self``.
|
|
1336
|
+
|
|
1337
|
+
EXAMPLES::
|
|
1338
|
+
|
|
1339
|
+
sage: # needs sage.graphs
|
|
1340
|
+
sage: from sage.misc.c3_controlled import HierarchyElement
|
|
1341
|
+
sage: P = Poset((divisors(30), lambda x, y: y.divides(x)), facade=True)
|
|
1342
|
+
sage: x = HierarchyElement(1, P)
|
|
1343
|
+
sage: x.cls
|
|
1344
|
+
<class '1.cls'>
|
|
1345
|
+
sage: x.cls.mro()
|
|
1346
|
+
[<class '1.cls'>, <... 'object'>]
|
|
1347
|
+
sage: x = HierarchyElement(30, P)
|
|
1348
|
+
sage: x.cls
|
|
1349
|
+
<class '30.cls'>
|
|
1350
|
+
sage: x.cls.mro()
|
|
1351
|
+
[<class '30.cls'>, <class '15.cls'>, <class '10.cls'>, <class '6.cls'>, <class '5.cls'>, <class '3.cls'>, <class '2.cls'>, <class '1.cls'>, <... 'object'>]
|
|
1352
|
+
"""
|
|
1353
|
+
super_classes = tuple(self._from_value(base).cls for base in self._bases_controlled)
|
|
1354
|
+
if not super_classes:
|
|
1355
|
+
super_classes = (object,)
|
|
1356
|
+
return dynamic_class("%s.cls" % self, super_classes)
|
|
1357
|
+
|
|
1358
|
+
@cached_method
|
|
1359
|
+
def all_bases(self):
|
|
1360
|
+
"""
|
|
1361
|
+
Return the set of all instances of :class:`HierarchyElement` above
|
|
1362
|
+
``self``, ``self`` included.
|
|
1363
|
+
|
|
1364
|
+
EXAMPLES::
|
|
1365
|
+
|
|
1366
|
+
sage: # needs sage.graphs
|
|
1367
|
+
sage: from sage.misc.c3_controlled import HierarchyElement
|
|
1368
|
+
sage: P = Poset((divisors(30), lambda x, y: y.divides(x)), facade=True)
|
|
1369
|
+
sage: HierarchyElement(1, P).all_bases()
|
|
1370
|
+
{1}
|
|
1371
|
+
sage: HierarchyElement(10, P).all_bases() # random output
|
|
1372
|
+
{10, 5, 2, 1}
|
|
1373
|
+
sage: sorted([x.value for x in HierarchyElement(10, P).all_bases()])
|
|
1374
|
+
[1, 2, 5, 10]
|
|
1375
|
+
"""
|
|
1376
|
+
return {self} | {x for base in self._bases for x in base.all_bases()}
|
|
1377
|
+
|
|
1378
|
+
def all_bases_len(self):
|
|
1379
|
+
"""
|
|
1380
|
+
Return the cumulated size of the bases of the elements above ``self`` in the hierarchy.
|
|
1381
|
+
|
|
1382
|
+
EXAMPLES::
|
|
1383
|
+
|
|
1384
|
+
sage: from sage.misc.c3_controlled import HierarchyElement
|
|
1385
|
+
sage: P = Poset((divisors(30), lambda x, y: y.divides(x)), facade=True) # needs sage.graphs
|
|
1386
|
+
sage: HierarchyElement(30, P).all_bases_len() # needs sage.graphs
|
|
1387
|
+
12
|
|
1388
|
+
"""
|
|
1389
|
+
return sum(len(x._bases) for x in self.all_bases())
|
|
1390
|
+
|
|
1391
|
+
def all_bases_controlled_len(self):
|
|
1392
|
+
"""
|
|
1393
|
+
Return the cumulated size of the controlled bases of the elements above ``self`` in the hierarchy.
|
|
1394
|
+
|
|
1395
|
+
EXAMPLES::
|
|
1396
|
+
|
|
1397
|
+
sage: from sage.misc.c3_controlled import HierarchyElement
|
|
1398
|
+
sage: P = Poset((divisors(30), lambda x, y: y.divides(x)), facade=True) # needs sage.graphs
|
|
1399
|
+
sage: HierarchyElement(30, P).all_bases_controlled_len() # needs sage.graphs
|
|
1400
|
+
13
|
|
1401
|
+
"""
|
|
1402
|
+
return sum(len(x._bases_controlled) for x in self.all_bases())
|