Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/structure/parent_old.pyx
8814 views
1
r"""
2
Base class for old-style parent objects
3
4
CLASS HIERARCHY:
5
6
SageObject
7
Parent
8
ParentWithBase
9
ParentWithGens
10
11
12
TESTS:
13
14
This came up in some subtle bug once.
15
::
16
17
sage: gp(2) + gap(3)
18
5
19
"""
20
21
###############################################################################
22
# Sage: System for Algebra and Geometry Experimentation
23
# Copyright (C) 2006 William Stein <[email protected]>
24
# Distributed under the terms of the GNU General Public License (GPL)
25
# The full text of the GPL is available at:
26
# http://www.gnu.org/licenses/
27
###############################################################################
28
29
cimport sage_object
30
import operator
31
from parent import Set_PythonType, Set_PythonType_class
32
from coerce import py_scalar_parent
33
from sage.structure.coerce_dict import MonoDict, TripleDict
34
35
from cpython.object cimport *
36
from cpython.bool cimport *
37
include 'sage/ext/stdsage.pxi'
38
39
cdef inline check_old_coerce(Parent p):
40
if p._element_constructor is not None:
41
raise RuntimeError, "%s still using old coercion framework" % p
42
43
44
## def make_parent_v0(_class, _dict, has_coerce_map_from):
45
## """
46
## This should work for any Python class deriving from this, as long
47
## as it doesn't implement some screwy __new__() method.
48
## """
49
## cdef Parent new_object
50
## new_object = _class.__new__(_class)
51
## if not _dict is None:
52
## new_object.__dict__ = _dict
53
## new_object._has_coerce_map_from = has_coerce_map_from
54
## return new_object
55
56
cdef class Parent(parent.Parent):
57
"""
58
Parents are the SAGE/mathematical analogues of container objects
59
in computer science.
60
61
TESTS::
62
63
sage: V = VectorSpace(GF(2,'a'),2)
64
sage: V.list()
65
[(0, 0), (1, 0), (0, 1), (1, 1)]
66
sage: MatrixSpace(GF(3), 1, 1).list()
67
[[0], [1], [2]]
68
sage: DirichletGroup(3).list()
69
[Dirichlet character modulo 3 of conductor 1 mapping 2 |--> 1,
70
Dirichlet character modulo 3 of conductor 3 mapping 2 |--> -1]
71
sage: K = GF(7^6,'a')
72
sage: K.list()[:10] # long time
73
[0, 1, 2, 3, 4, 5, 6, a, a + 1, a + 2]
74
sage: K.<a> = GF(4)
75
sage: K.list()
76
[0, a, a + 1, 1]
77
"""
78
79
def __init__(self, coerce_from=[], actions=[], embeddings=[], category=None):
80
# TODO: many classes don't call this at all, but __new__ crashes Sage
81
# if len(coerce_from) > 0:
82
# print type(self), coerce_from
83
self.init_coerce(False)
84
self._coerce_from_list = list(coerce_from)
85
self._coerce_from_hash = MonoDict(23)
86
self._action_list = list(actions)
87
self._action_hash = TripleDict(23)
88
89
cdef parent.Parent other
90
for mor in embeddings:
91
other = mor.domain()
92
print "embedding", self, " --> ", other
93
print mor
94
other.init_coerce() # TODO remove when we can
95
other._coerce_from_list.append(mor)
96
97
self._set_element_constructor()
98
99
# old
100
self._has_coerce_map_from = MonoDict(23)
101
if category is not None:
102
self._init_category_(category)
103
104
cdef int init_coerce(self, bint warn=False) except -1:
105
parent.Parent.init_coerce(self, warn)
106
107
108
#################################################################################
109
# New Coercion support functionality
110
#################################################################################
111
112
# def coerce_map_from(self, S):
113
# return self.coerce_map_from_c(S)
114
115
cpdef coerce_map_from_c(self, S):
116
"""
117
EXAMPLES:
118
119
Check to make sure that we handle coerce maps from Python
120
native types correctly::
121
122
sage: QQ['q,t'].coerce_map_from(int)
123
Composite map:
124
From: Set of Python objects of type 'int'
125
To: Multivariate Polynomial Ring in q, t over Rational Field
126
Defn: Native morphism:
127
From: Set of Python objects of type 'int'
128
To: Rational Field
129
then
130
Polynomial base injection morphism:
131
From: Rational Field
132
To: Multivariate Polynomial Ring in q, t over Rational Field
133
"""
134
check_old_coerce(self)
135
if S is self:
136
from sage.categories.homset import Hom
137
return Hom(self, self).identity()
138
elif S == self:
139
# non-unique parents
140
from sage.categories.homset import Hom
141
from sage.categories.morphism import CallMorphism
142
return CallMorphism(Hom(S, self))
143
elif isinstance(S, Set_PythonType_class):
144
return self.coerce_map_from_c(S._type)
145
if self._coerce_from_hash is None: # this is because parent.__init__() does not always get called
146
self.init_coerce()
147
cdef object ret
148
try:
149
ret = self._coerce_from_hash.get(S)
150
return ret
151
except KeyError:
152
pass
153
154
if HAS_DICTIONARY(self):
155
mor = self.coerce_map_from_impl(S)
156
else:
157
mor = self.coerce_map_from_c_impl(S)
158
import sage.categories.morphism
159
import sage.categories.map
160
if mor is True:
161
mor = sage.categories.morphism.CallMorphism(S, self)
162
elif mor is False:
163
mor = None
164
elif mor is not None and not isinstance(mor, sage.categories.map.Map):
165
raise TypeError, "coerce_map_from_impl must return a boolean, None, or an explicit Map"
166
167
if mor is None and isinstance(S, type):
168
#Convert Python types to native Sage types
169
sage_type = py_scalar_parent(S)
170
if sage_type is None:
171
self._coerce_from_hash[S] = None
172
return None
173
mor = self.coerce_map_from_c(sage_type)
174
if mor is not None:
175
mor = mor * sage_type.coerce_map_from(S)
176
177
if mor is not None:
178
self._coerce_from_hash.set(S, mor) # TODO: if this is None, could it be non-None in the future?
179
180
return mor
181
182
def coerce_map_from_impl(self, S):
183
check_old_coerce(self)
184
return self.coerce_map_from_c_impl(S)
185
186
cdef coerce_map_from_c_impl(self, S):
187
check_old_coerce(self)
188
import sage.categories.morphism
189
from sage.categories.map import Map
190
from sage.categories.homset import Hom
191
cdef parent.Parent R
192
for mor in self._coerce_from_list:
193
if PY_TYPE_CHECK(mor, Map):
194
R = mor.domain()
195
else:
196
R = mor
197
mor = sage.categories.morphism.CallMorphism(Hom(R, self))
198
i = self._coerce_from_list.index(R)
199
self._coerce_from_list[i] = mor # cache in case we need it again
200
if R is S:
201
return mor
202
else:
203
connecting = R.coerce_map_from(S)
204
if connecting is not None:
205
return mor * connecting
206
207
# Piggyback off the old code for now
208
# WARNING: when working on this, make sure circular dependencies aren't introduced!
209
if self.has_coerce_map_from_c(S):
210
if isinstance(S, type):
211
S = Set_PythonType(S)
212
return sage.categories.morphism.CallMorphism(Hom(S, self))
213
else:
214
return None
215
216
# def get_action(self, S, op=operator.mul, self_on_left=True):
217
# return self.get_action_c(S, op, self_on_left)
218
219
cpdef get_action_c(self, S, op, bint self_on_left):
220
check_old_coerce(self)
221
try:
222
if self._action_hash is None: # this is because parent.__init__() does not always get called
223
self.init_coerce()
224
return self._action_hash.get(S, op, self_on_left)
225
except KeyError:
226
pass
227
if HAS_DICTIONARY(self):
228
action = self.get_action_impl(S, op, self_on_left)
229
else:
230
action = self.get_action_c_impl(S, op, self_on_left)
231
if action is not None:
232
from sage.categories.action import Action
233
if not isinstance(action, Action):
234
raise TypeError, "get_action_impl must return None or an Action"
235
self._action_hash.set(S, op, self_on_left, action)
236
return action
237
238
def get_action_impl(self, S, op, self_on_left):
239
check_old_coerce(self)
240
return self.get_action_c_impl(S, op, self_on_left)
241
242
cdef get_action_c_impl(self, S, op, bint self_on_left):
243
check_old_coerce(self)
244
return self.discover_action(S, op, self_on_left, None, None)
245
246
#################################################################################
247
# Coercion support functionality
248
#################################################################################
249
250
def _coerce_(self, x): # Call this from Python (do not override!)
251
if self._element_constructor is not None:
252
return self.coerce(x)
253
check_old_coerce(self)
254
return self._coerce_c(x)
255
256
cpdef _coerce_c(self, x): # DO NOT OVERRIDE THIS (call it)
257
if self._element_constructor is not None:
258
return self.coerce(x)
259
check_old_coerce(self)
260
try:
261
P = x.parent() # todo -- optimize
262
if P is self:
263
return x
264
except AttributeError, msg:
265
pass
266
if HAS_DICTIONARY(self):
267
return self._coerce_impl(x)
268
else:
269
return self._coerce_c_impl(x)
270
271
cdef _coerce_c_impl(self, x): # OVERRIDE THIS FOR CYTHON CLASSES
272
"""
273
Canonically coerce x in assuming that the parent of x is not
274
equal to self.
275
"""
276
check_old_coerce(self)
277
raise TypeError
278
279
def _coerce_impl(self, x): # OVERRIDE THIS FOR PYTHON CLASSES
280
"""
281
Canonically coerce x in assuming that the parent of x is not
282
equal to self.
283
"""
284
check_old_coerce(self)
285
return self._coerce_c_impl(x)
286
287
def _coerce_try(self, x, v):
288
"""
289
Given a list v of rings, try to coerce x canonically into each
290
one in turn. Return the __call__ coercion of the result into
291
self of the first canonical coercion that succeeds. Raise a
292
TypeError if none of them succeed.
293
294
INPUT:
295
x -- Python object
296
v -- parent object or list (iterator) of parent objects
297
"""
298
check_old_coerce(self)
299
if not isinstance(v, list):
300
v = [v]
301
302
for R in v:
303
try:
304
y = R._coerce_(x)
305
return self(y)
306
except (TypeError, AttributeError), msg:
307
pass
308
raise TypeError, "no canonical coercion of element into self"
309
310
def _coerce_self(self, x):
311
check_old_coerce(self)
312
return self._coerce_self_c(x)
313
314
cdef _coerce_self_c(self, x):
315
"""
316
Try to canonically coerce x into self.
317
Return result on success or raise TypeError on failure.
318
"""
319
check_old_coerce(self)
320
# todo -- optimize?
321
try:
322
P = x.parent()
323
if P is self:
324
return x
325
elif P == self:
326
return self(x)
327
except AttributeError:
328
pass
329
raise TypeError, "no canonical coercion to self defined"
330
331
# def has_coerce_map_from(self, S):
332
# return self.has_coerce_map_from_c(S)
333
334
cpdef has_coerce_map_from_c(self, S):
335
"""
336
Return True if there is a natural map from S to self.
337
Otherwise, return False.
338
"""
339
check_old_coerce(self)
340
if self == S:
341
return True
342
if self._has_coerce_map_from is None:
343
self._has_coerce_map_from = MonoDict(23)
344
else:
345
try:
346
return self._has_coerce_map_from.get(S)
347
except KeyError:
348
pass
349
if HAS_DICTIONARY(self):
350
x = self.has_coerce_map_from_impl(S)
351
else:
352
x = self.has_coerce_map_from_c_impl(S)
353
self._has_coerce_map_from.set(S, x)
354
return x
355
356
def has_coerce_map_from_impl(self, S):
357
check_old_coerce(self)
358
return self.has_coerce_map_from_c_impl(S)
359
360
cdef has_coerce_map_from_c_impl(self, S):
361
check_old_coerce(self)
362
if not PY_TYPE_CHECK(S, parent.Parent):
363
return False
364
try:
365
self._coerce_c((<parent.Parent>S).an_element())
366
except TypeError:
367
return False
368
except NotImplementedError, msg:
369
raise NotImplementedError, "%s\nAlso, please make sure you have implemented has_coerce_map_from_impl or has_coerce_map_from_c_impl (or better _an_element_c_impl or _an_element_impl if possible) for %s"%(msg,self)
370
return True
371
372
def _an_element_impl(self): # override this in Python
373
check_old_coerce(self)
374
return self._an_element_c_impl()
375
376
cdef _an_element_c_impl(self): # override this in Cython
377
"""
378
Returns an element of self. Want it in sufficient generality
379
that poorly-written functions won't work when they're not
380
supposed to. This is cached so doesn't have to be super fast.
381
"""
382
check_old_coerce(self)
383
try:
384
return self.gen(0)
385
except Exception:
386
pass
387
388
try:
389
return self.gen()
390
except Exception:
391
pass
392
393
from sage.rings.infinity import infinity
394
for x in ['_an_element_', 'pi', 1.2, 2, 1, 0, infinity]:
395
try:
396
return self(x)
397
except (TypeError, NameError, NotImplementedError, AttributeError, ValueError):
398
pass
399
400
raise NotImplementedError, "please implement _an_element_c_impl or _an_element_impl for %s"%self
401
402
def _an_element(self): # do not override this (call from Python)
403
check_old_coerce(self)
404
return self._an_element_c()
405
406
cpdef _an_element_c(self): # do not override this (call from Cython)
407
check_old_coerce(self)
408
if not self.__an_element is None:
409
return self.__an_element
410
if HAS_DICTIONARY(self):
411
self.__an_element = self._an_element_impl()
412
else:
413
self.__an_element = self._an_element_c_impl()
414
return self.__an_element
415
416
# This should eventually be inherited from the EnumeratedSets() category
417
# This is just a convenient spot to cover the relevant cython parents,
418
# without bothering the new parents
419
list = parent.Parent._list_from_iterator_cached
420
421
422
################################################
423
# Comparison of parent objects
424
################################################
425
cdef _richcmp(left, right, int op):
426
"""
427
Compare left and right.
428
"""
429
check_old_coerce(left)
430
cdef int r
431
432
if not PY_TYPE_CHECK(right, parent.Parent) or not PY_TYPE_CHECK(left, parent.Parent):
433
# One is not a parent -- use arbitrary ordering
434
if (<PyObject*>left) < (<PyObject*>right):
435
r = -1
436
elif (<PyObject*>left) > (<PyObject*>right):
437
r = 1
438
else:
439
r = 0
440
441
else:
442
# Both are parents -- but need *not* have the same type.
443
if HAS_DICTIONARY(left):
444
r = left.__cmp__(right)
445
else:
446
r = left._cmp_c_impl(right)
447
448
if op == 0: #<
449
return PyBool_FromLong(r < 0)
450
elif op == 2: #==
451
return PyBool_FromLong(r == 0)
452
elif op == 4: #>
453
return PyBool_FromLong(r > 0)
454
elif op == 1: #<=
455
return PyBool_FromLong(r <= 0)
456
elif op == 3: #!=
457
return PyBool_FromLong(r != 0)
458
elif op == 5: #>=
459
return PyBool_FromLong(r >= 0)
460
461
## ####################################################################
462
## # For a derived Cython class, you **must** put the following in
463
## # your subclasses, in order for it to take advantage of the
464
## # above generic comparison code. You must also define
465
## # _cmp_c_impl for a Cython class.
466
## #
467
## # For a derived Python class, simply define __cmp__.
468
## ####################################################################
469
## def __richcmp__(left, right, int op):
470
## return (<Parent>left)._richcmp(right, op)
471
472
## # NOT NEEDED, since all attributes are public!
473
## def __reduce__(self):
474
## if HAS_DICTIONARY(self):
475
## _dict = self.__dict__
476
## else:
477
## _dict = None
478
## return (make_parent_v0, (self.__class__, _dict, self._has_coerce_map_from))
479
480
cdef int _cmp_c_impl(left, parent.Parent right) except -2:
481
check_old_coerce(left)
482
pass
483
# this would be nice to do, but we can't since
484
# it leads to infinite recursions -- and is slow -- and this
485
# stuff must be fast!
486
#if right.has_coerce_map_from(left):
487
# if left.has_coerce_map_from(right):
488
# return 0
489
# else:
490
# return -1
491
if (<PyObject*>left) < (<PyObject*>right):
492
return -1
493
elif (<PyObject*>left) > (<PyObject*>right):
494
return 1
495
return 0
496
497
## def __cmp__(left, right):
498
## return left._cmp_c_impl(right) # default
499
500
501
############################################################################
502
# Coercion Compatibility Layer
503
############################################################################
504
505
cpdef _coerce_map_from_(self, S):
506
if self._element_constructor is None:
507
return self.coerce_map_from_c(S)
508
else:
509
return parent.Parent._coerce_map_from_(self, S)
510
511
cpdef _get_action_(self, other, op, bint self_on_left):
512
if self._element_constructor is None:
513
return self.get_action_c(other, op, self_on_left)
514
else:
515
return parent.Parent._get_action_(self, other, op, self_on_left)
516
517
def _an_element_(self):
518
if self._element_constructor is None:
519
return self._an_element_c()
520
else:
521
return parent.Parent._an_element_(self)
522
523
cpdef _generic_convert_map(self, S):
524
if self._element_constructor is None:
525
if hasattr(self, '_element_constructor_'):
526
assert callable(self._element_constructor_)
527
self._element_constructor = self._element_constructor_
528
else:
529
from sage.categories.morphism import CallMorphism
530
from sage.categories.homset import Hom
531
if PY_TYPE_CHECK(S, type):
532
S = Set_PythonType(S)
533
return CallMorphism(Hom(S, self))
534
return parent.Parent._generic_convert_map(self, S)
535
536