Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/misc/c3.pyx
4057 views
1
"""
2
The C3 algorithm
3
4
The C3 algorithm is used as method resolution order for new style
5
classes in Python. The implementation here is used to order the list
6
of super categories of a category.
7
8
AUTHOR:
9
10
- Simon King (2011-11): initial version.
11
"""
12
#*****************************************************************************
13
# Copyright (C) 2011 Simon King <[email protected]>
14
#
15
# Distributed under the terms of the GNU General Public License (GPL)
16
# http://www.gnu.org/licenses/
17
#******************************************************************************
18
19
include "../ext/python.pxi"
20
21
cpdef list C3_algorithm(object start, str bases, str attribute, bint proper):
22
"""
23
An implementation of the C3 algorithm.
24
25
C3 is the algorithm used by Python to construct the method
26
resolution order for new style classes involving multiple
27
inheritance.
28
29
This implementation is used to order the list of super categories
30
of a category; see
31
:meth:`~sage.categories.category.Category.all_super_categories`.
32
The purpose is to ensure that list of super categories matches
33
with the method resolution order of the parent or element classes
34
of a category.
35
36
INPUT:
37
38
- ``start`` -- an object; the returned list is built upon data
39
provided by certain attributes of ``start``.
40
- ``bases`` -- a string; the name of an attribute of ``start``
41
providing a list of objects.
42
- ``attribute`` -- a string; the name of an attribute of the
43
objects provided in ``getattr(start,bases)``. That attribute is
44
supposed to provide a list.
45
46
ASSUMPTIONS:
47
48
Our implementation of the algorithm only works on lists of
49
objects that compare equal if and only if they are identical.
50
51
OUTPUT:
52
53
A list, the result of the C3 algorithm applied to the list
54
``[getattr(X,attribute) for X in getattr(start,bases)]``.
55
56
EXAMPLES:
57
58
We start with some categories having an inconsistent inheritance
59
order::
60
61
sage: class X(Category):
62
... def super_categories(self):
63
... return [Objects()]
64
sage: class Y(Category):
65
... def super_categories(self):
66
... return [Objects()]
67
sage: class A(Category):
68
... def super_categories(self):
69
... return [X(), Y()]
70
sage: class B(Category):
71
... def super_categories(self):
72
... return [Y(), X()]
73
sage: class Foo(Category):
74
... def super_categories(self):
75
... return [A(), B()]
76
sage: F = Foo()
77
78
Python is not able to create a consistent method resolution order
79
for the parent class::
80
81
sage: F.parent_class
82
Traceback (most recent call last):
83
...
84
TypeError: Cannot create a consistent method resolution
85
order (MRO) for bases ....parent_class, ....parent_class
86
87
Since the C3 algorithm is used for determining the list of
88
all super categories (by trac ticket #11943), a similar error
89
arises here::
90
91
sage: F.all_super_categories()
92
Traceback (most recent call last):
93
...
94
ValueError: Can not merge the items Category of x, Category of y.
95
96
Next, we demonstrate how our implementation of the C3 algorithm
97
is used to compute the list of all super categories::
98
99
sage: C = Category.join([HopfAlgebrasWithBasis(QQ), FiniteEnumeratedSets()])
100
sage: from sage.misc.c3 import C3_algorithm
101
sage: C3_algorithm(C,'_super_categories','_all_super_categories',True) == C._all_super_categories_proper
102
True
103
sage: C3_algorithm(C,'_super_categories','_all_super_categories',False) == C._all_super_categories
104
True
105
106
By trac ticket #11943, the following consistency tests are part
107
of the test suites of categories (except for hom categories)::
108
109
sage: C.parent_class.mro() == [x.parent_class for x in C.all_super_categories()]+[object]
110
True
111
sage: C.element_class.mro() == [x.element_class for x in C.all_super_categories()]+[object]
112
True
113
114
"""
115
# The lists in the arguments are reverted,
116
# so that we can do pop() in lieue of pop(0).
117
# In addition, containedness in the tail is tested using lists.
118
cdef list out
119
if proper:
120
out = []
121
else:
122
out = [start]
123
cdef list args = getattr(start,bases)
124
if not args:
125
return out
126
cdef list curr_tail, tmp_tail
127
cdef set curr_set, tmp_set
128
cdef object curr_obj
129
cdef bint next_item_found
130
cdef list heads = []
131
cdef list tails = []
132
cdef list tailsets = []
133
for curr_obj in args:
134
curr_tail = getattr(curr_obj, attribute)
135
heads.append(curr_tail[0])
136
tmp_tail = PyList_GetSlice(curr_tail,1,PyList_GET_SIZE(curr_tail))
137
PyList_Reverse(tmp_tail)
138
tails.append(tmp_tail)
139
tailsets.append(set(tmp_tail))
140
cdef int i,j, lenargs
141
lenargs = len(heads)
142
for i from 0<=i<lenargs:
143
curr_tail = <list>PyList_GET_ITEM(tails,i)
144
if curr_tail is None:
145
continue
146
curr_set = <set>PyList_GET_ITEM(tailsets,i)
147
O = <object>PyList_GET_ITEM(heads,i)
148
next_item_found=True
149
for j from 0<=j<i:
150
tmp_tail = <list>PyList_GET_ITEM(tails,j)
151
if tmp_tail is None:
152
continue
153
tmp_set = <set>PyList_GET_ITEM(tailsets,j)
154
X = <object>PyList_GET_ITEM(heads,j)
155
if X is O:
156
try:
157
X = tmp_tail.pop()
158
heads[j] = X
159
tmp_set.remove(X)
160
except IndexError:
161
tails[j] = None
162
elif O in tmp_set:
163
next_item_found=False
164
break
165
if next_item_found:
166
for j from i<j<lenargs:
167
tmp_tail = <list>PyList_GET_ITEM(tails,j)
168
if tmp_tail is None:
169
continue
170
tmp_set = <set>PyList_GET_ITEM(tailsets,j)
171
X = <object>PyList_GET_ITEM(heads,j)
172
if X is O:
173
try:
174
X = tmp_tail.pop()
175
heads[j] = X
176
tmp_set.remove(X)
177
except IndexError:
178
tails[j] = None
179
elif O in tmp_set:
180
next_item_found=False
181
break
182
if next_item_found:
183
out.append(O)
184
try:
185
O = curr_tail.pop()
186
heads[i] = O
187
curr_set.remove(O)
188
except IndexError:
189
tails[i] = None
190
191
i = -1
192
# Either we need to raise an error, or the list is done.
193
if tails.count(None)<lenargs:
194
raise ValueError, "Can not merge the items %s."%', '.join([repr(heads[i]) for i,t in enumerate(tails) if t is not None])
195
return out
196
197