/
category_cy_helper.pyx
320 lines (248 loc) · 10.5 KB
/
category_cy_helper.pyx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
"""
Fast functions for the category framework
AUTHOR:
- Simon King (initial version)
"""
#*****************************************************************************
# Copyright (C) 2014 Simon King <simon.king@uni-jena.de>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
# http://www.gnu.org/licenses/
#*****************************************************************************
#######################################
## Sorting
cpdef inline tuple category_sort_key(object category):
"""
Return ``category._cmp_key``.
This helper function is used for sorting lists of categories.
It is semantically equivalent to
:func:`operator.attrgetter` ``("_cmp_key")``, but currently faster.
EXAMPLES::
sage: from sage.categories.category_cy_helper import category_sort_key
sage: category_sort_key(Rings()) is Rings()._cmp_key
True
"""
return category._cmp_key
cpdef tuple _sort_uniq(categories):
"""
Return the categories after sorting them and removing redundant categories.
Redundant categories include duplicates and categories which
are super categories of other categories in the input.
INPUT:
- ``categories`` -- a list (or iterable) of categories
OUTPUT: a sorted tuple of mutually incomparable categories
EXAMPLES::
sage: Category._sort_uniq([Rings(), Monoids(), Coalgebras(QQ)])
(Category of rings, Category of coalgebras over Rational Field)
Note that, in the above example, ``Monoids()`` does not appear
in the result because it is a super category of ``Rings()``.
"""
cdef tuple cats = tuple(sorted(categories, key=category_sort_key, reverse=True))
cdef list result = []
cdef bint append
for category in cats:
append = True
for cat in result:
if cat.is_subcategory(category):
append = False
break
if append:
result.append(category)
return tuple(result)
cpdef tuple _flatten_categories(categories, ClasscallMetaclass JoinCategory):
"""
Return the tuple of categories in ``categories``, while
flattening join categories.
INPUT:
- ``categories`` -- a list (or iterable) of categories
- ``JoinCategory`` -- A type such that instances of that type will be
replaced by its super categories. Usually, this type is
:class:`JoinCategory`.
.. NOTE::
It is needed to provide :class:`~sage.categories.category.JoinCategory` as
an argument, since we need to prevent a circular import.
EXAMPLES::
sage: Category._flatten_categories([Algebras(QQ), Category.join([Monoids(), Coalgebras(QQ)]), Sets()], sage.categories.category.JoinCategory)
(Category of algebras over Rational Field, Category of monoids,
Category of coalgebras over Rational Field, Category of sets)
"""
# Invariant: the super categories of a JoinCategory are not JoinCategories themselves
cdef list out = []
for category in categories:
if isinstance(category, JoinCategory):
out.extend(category.super_categories())
else:
out.append(category)
return tuple(out)
#############################################
## Join
cdef bint is_supercategory_of_done(new_cat, dict done):
# This is a helper function. It replaces the closure
# any(cat.is_subcategory(new_cat) for cat in done)
for cat in done:
if cat.is_subcategory(new_cat):
return True
return False
cpdef tuple join_as_tuple(tuple categories, tuple axioms, tuple ignore_axioms):
"""
Helper for :meth:`~sage.categories.category.Category.join`.
INPUT:
- ``categories`` -- tuple of categories to be joined,
- ``axioms`` -- tuple of strings; the names of some
supplementary axioms.
- ``ignore_axioms`` -- tuple of pairs ``(cat, axiom)``, such
that ``axiom`` will not be applied to ``cat``, should ``cat``
occur in the algorithm.
EXAMPLES::
sage: from sage.categories.category_cy_helper import join_as_tuple
sage: T = (Coalgebras(QQ), Sets().Finite(), Algebras(ZZ), SimplicialComplexes())
sage: join_as_tuple(T,(),())
(Category of algebras over Integer Ring,
Category of finite monoids,
Category of coalgebras over Rational Field,
Category of finite simplicial complexes)
sage: join_as_tuple(T,('WithBasis',),())
(Category of algebras with basis over Integer Ring,
Category of finite monoids,
Category of coalgebras with basis over Rational Field,
Category of finite simplicial complexes)
sage: join_as_tuple(T,(),((Monoids(),'Finite'),))
(Category of algebras over Integer Ring,
Category of coalgebras over Rational Field,
Category of finite simplicial complexes)
"""
cdef set axiomsS = set(axioms)
for category in categories:
axiomsS.update(category.axioms())
cdef dict done = dict()
cdef set todo = set()
cdef frozenset axs
for category in categories:
axs = category.axioms()
for (cat, axiom) in ignore_axioms:
if category.is_subcategory(cat):
axs = axs | {axiom}
done[category] = axs
for axiom in axiomsS.difference(axs):
todo.add( (category, axiom) )
# Invariants:
# - the current list of categories is stored in the keys of ``done``
# - todo contains the ``complement`` of done; i.e.
# for category in the keys of done,
# (category, axiom) is in todo iff axiom is not in done[category]
cdef list new_cats
cdef set new_axioms
while todo:
(category, axiom) = todo.pop()
# It's easier to remove categories from done than from todo
# So we check that ``category`` had not been removed
if category not in done:
continue
# Removes redundant categories
new_cats = [new_cat for new_cat in <tuple>(category._with_axiom_as_tuple(axiom))
if not is_supercategory_of_done(new_cat, done)]
for cat in done.keys():
for new_cat in new_cats:
if new_cat.is_subcategory(cat):
del done[cat]
break
new_axioms = set()
for new_cat in new_cats:
for axiom in new_cat.axioms():
if axiom not in axiomsS:
new_axioms.add(axiom)
# Mark old categories with new axioms as todo
for category in done:
for axiom in new_axioms:
todo.add( (category, axiom) )
for new_cat in new_cats:
axs = new_cat.axioms()
for (cat, axiom) in ignore_axioms:
if new_cat.is_subcategory(cat):
axs = axs | {axiom}
done[new_cat] = axs
for axiom in axiomsS.difference(axs):
todo.add( (new_cat, axiom) )
return _sort_uniq(done)
#############################################
## Axiom related functions
cdef class AxiomContainer(dict):
"""
A fast container for axioms.
This is derived from :class:`dict`. A key is the name of an axiom. The
corresponding value is the "rank" of this axiom, that is used to order the
axioms in :func:`canonicalize_axioms`.
EXAMPLES::
sage: all_axioms = sage.categories.category_with_axiom.all_axioms
sage: isinstance(all_axioms, sage.categories.category_with_axiom.AxiomContainer)
True
"""
def add(self, axiom):
"""
Add a new axiom name, of the next rank.
EXAMPLES::
sage: all_axioms = sage.categories.category_with_axiom.all_axioms
sage: m = max(all_axioms.values())
sage: all_axioms.add('Awesome')
sage: all_axioms['Awesome'] == m + 1
True
To avoid side effects, we remove the added axiom::
sage: del all_axioms['Awesome']
"""
self[axiom] = len(self)
def __iadd__(self, L):
"""
Inline addition, which means to add a list of axioms to the container.
EXAMPLES::
sage: all_axioms = sage.categories.category_with_axiom.all_axioms
sage: m = max(all_axioms.values())
sage: all_axioms += ('Fancy', 'Awesome')
sage: all_axioms['Awesome'] == m + 2
True
To avoid side effects, we delete the axioms that we just added::
sage: del all_axioms['Awesome'], all_axioms['Fancy']
"""
for axiom in L:
self.add(axiom)
return self
cpdef inline get_axiom_index(AxiomContainer all_axioms, str axiom):
"""
Helper function: Return the rank of an axiom.
INPUT:
- ``all_axioms`` -- the axiom collection
- ``axiom`` -- string, name of an axiom
EXAMPLES::
sage: all_axioms = sage.categories.category_with_axiom.all_axioms
sage: from sage.categories.category_cy_helper import get_axiom_index
sage: get_axiom_index(all_axioms, 'AdditiveCommutative') == all_axioms['AdditiveCommutative']
True
"""
return (<dict>all_axioms)[axiom]
cpdef tuple canonicalize_axioms(AxiomContainer all_axioms, axioms):
r"""
Canonicalize a set of axioms.
INPUT:
- ``all_axioms`` -- all available axioms
- ``axioms`` -- a set (or iterable) of axioms
.. NOTE::
:class:`AxiomContainer` provides a fast container for axioms, and the
collection of axioms is stored in
:mod:`sage.categories.category_with_axiom`. In order to avoid circular
imports, we expect that the collection of all axioms is provided as an
argument to this auxiliary function.
OUTPUT:
A set of axioms as a tuple sorted according to the order of the
tuple ``all_axioms`` in :mod:`sage.categories.category_with_axiom`.
EXAMPLES::
sage: from sage.categories.category_with_axiom import canonicalize_axioms, all_axioms
sage: canonicalize_axioms(all_axioms, ["Commutative", "Connected", "WithBasis", "Finite"])
('Finite', 'Connected', 'WithBasis', 'Commutative')
sage: canonicalize_axioms(all_axioms, ["Commutative", "Connected", "Commutative", "WithBasis", "Finite"])
('Finite', 'Connected', 'WithBasis', 'Commutative')
"""
cdef list L = list(set(axioms))
L.sort(key = (all_axioms).__getitem__)
return tuple(L)