Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/structure/coerce_maps.pyx
4057 views
1
"""
2
Coerce maps
3
"""
4
include "../ext/stdsage.pxi"
5
6
import re
7
import types
8
9
from parent import Set_PythonType
10
from sage.structure.parent cimport Parent
11
from sage.structure.element cimport Element
12
13
cdef object BuiltinMethodType = type(repr)
14
15
# COERCE TODO: remove or integrate better (as this bit is only checked on an error)
16
cdef bint print_warnings = 0
17
18
19
cdef class DefaultConvertMap(Map):
20
"""
21
This morphism simply calls the codomain's element_constructor method,
22
passing in the codomain as the first argument.
23
"""
24
def __init__(self, domain, codomain, force_use=False):
25
if not PY_TYPE_CHECK(domain, Parent):
26
domain = Set_PythonType(domain)
27
Map.__init__(self, domain, codomain)
28
self._coerce_cost = 100
29
self._force_use = force_use
30
if self._codomain._element_constructor is None:
31
raise RuntimeError, "BUG in coercion model, no element constructor for %s" % type(self._codomain)
32
self._repr_type_str = "Coercion" if self._is_coercion else "Conversion"
33
34
cpdef Element _call_(self, x):
35
try:
36
return self._codomain._element_constructor(self._codomain, x)
37
except:
38
if print_warnings:
39
print type(self._codomain), self._codomain
40
print type(self._codomain._element_constructor), self._codomain._element_constructor
41
raise
42
43
cpdef Element _call_with_args(self, x, args=(), kwds={}):
44
try:
45
if len(args) == 0:
46
if len(kwds) == 0:
47
# This line is apparently never used in any tests (hivert, 2009-04-28)
48
return self._codomain._element_constructor(self._codomain, x)
49
else:
50
return self._codomain._element_constructor(self._codomain, x, **kwds)
51
else:
52
if len(kwds) == 0:
53
return self._codomain._element_constructor(self._codomain, x, *args)
54
else:
55
return self._codomain._element_constructor(self._codomain, x, *args, **kwds)
56
except:
57
if print_warnings:
58
print type(self._codomain), self._codomain
59
print type(self._codomain._element_constructor), self._codomain._element_constructor
60
raise
61
62
63
cdef class DefaultConvertMap_unique(DefaultConvertMap):
64
"""
65
This morphism simply defers action to the codomain's
66
element_constructor method, WITHOUT passing in the codomain as the
67
first argument.
68
69
This is used for creating elements that don't take a parent as the
70
first argument to their __init__ method, for example, Integers,
71
Rationals, Algebraic Reals... all have a unique parent. It is also
72
used when the element_constructor is a bound method (whose self
73
argument is assumed to be bound to the codomain).
74
"""
75
cpdef Element _call_(self, x):
76
try:
77
return self._codomain._element_constructor(x)
78
except:
79
if print_warnings:
80
print type(self._codomain), self._codomain
81
print type(self._codomain._element_constructor), self._codomain._element_constructor
82
raise
83
84
cpdef Element _call_with_args(self, x, args=(), kwds={}):
85
try:
86
if len(args) == 0:
87
if len(kwds) == 0:
88
return self._codomain._element_constructor(x)
89
else:
90
return self._codomain._element_constructor(x, **kwds)
91
else:
92
if len(kwds) == 0:
93
return self._codomain._element_constructor(x, *args)
94
else:
95
return self._codomain._element_constructor(x, *args, **kwds)
96
except:
97
if print_warnings:
98
print type(self._codomain), self._codomain
99
print type(self._codomain._element_constructor), self._codomain._element_constructor
100
raise
101
102
103
cdef class NamedConvertMap(Map):
104
"""
105
This is used for creating a elements via the _xxx_ methods.
106
107
For example, many elements implement an _integer_ method to
108
convert to ZZ, or a _rational_ method to convert to QQ.
109
"""
110
111
def __init__(self, domain, codomain, method_name, force_use=False):
112
"""
113
EXAMPLES::
114
115
sage: from sage.structure.coerce_maps import NamedConvertMap
116
sage: var('t')
117
t
118
sage: mor = NamedConvertMap(SR, QQ['t'], '_polynomial_')
119
sage: mor(t^2/4+1)
120
1/4*t^2 + 1
121
sage: mor = NamedConvertMap(SR, GF(7)[['t']], '_polynomial_')
122
sage: mor(t^2/4+1)
123
1 + 2*t^2
124
"""
125
if PY_TYPE_CHECK(domain, type):
126
domain = Set_PythonType(domain)
127
Map.__init__(self, domain, codomain)
128
self._coerce_cost = 400
129
self._force_use = force_use
130
self.method_name = method_name
131
self._repr_type_str = "Conversion via %s method" % self.method_name
132
133
cpdef Element _call_(self, x):
134
"""
135
EXAMPLES::
136
137
sage: from sage.structure.coerce_maps import NamedConvertMap
138
sage: f = NamedConvertMap(GF(5), QQ, '_integer_'); f
139
Conversion via _integer_ method map:
140
From: Finite Field of size 5
141
To: Rational Field
142
sage: f(19)
143
4
144
sage: f(19).parent()
145
Rational Field
146
"""
147
try:
148
method = getattr(x, self.method_name)
149
except AttributeError:
150
if print_warnings:
151
print type(x), x
152
print type(self._codomain), self._codomain
153
print self.method_name
154
raise TypeError, "Cannot coerce %s to %s"%(x, self._codomain)
155
cdef Map m
156
cdef Element e = method(self._codomain)
157
if e is None:
158
raise RuntimeError, "BUG in coercion model: %s method of %s returned None" % (self.method_name, type(x))
159
if e._parent is not self._codomain:
160
m = self._codomain.convert_map_from(e._parent)
161
if m is None or m is self:
162
raise TypeError
163
e = m._call_(e)
164
return e
165
166
cpdef Element _call_with_args(self, x, args=(), kwds={}):
167
"""
168
EXAMPLES::
169
170
sage: from sage.structure.coerce_maps import NamedConvertMap
171
sage: f = NamedConvertMap(SR, ZZ['x'], '_polynomial_')
172
sage: f(x^2+1, check=True)
173
x^2 + 1
174
"""
175
return self._codomain._element_constructor(self._call_(x), *args, **kwds)
176
177
178
# Perhaps this could be a method, extracting (<PyMethodDescrObject *>(<object>Parent).coerce_map_from).d_method.ml_meth and/or PyCFunction_GET_FUNCTION(method)
179
# and constructing a CCallableConvertMap_class if it is bound to the codomain.
180
181
cdef class CallableConvertMap(Map):
182
cdef bint _parent_as_first_arg
183
cdef _func
184
185
def __init__(self, domain, codomain, func, parent_as_first_arg=None):
186
"""
187
This lets one easily create maps from any callable object.
188
189
This is especially useful to create maps from bound methods.
190
191
EXAMPLES::
192
193
sage: from sage.structure.coerce_maps import CallableConvertMap
194
sage: def foo(P, x): return x/2
195
sage: f = CallableConvertMap(ZZ, QQ, foo)
196
sage: f(3)
197
3/2
198
sage: f
199
Conversion via foo map:
200
From: Integer Ring
201
To: Rational Field
202
203
Create a homomorphism from $\RR$ to $\RR^+$ viewed as additive groups.
204
205
::
206
207
sage: f = CallableConvertMap(RR, RR, exp, parent_as_first_arg=False)
208
sage: f(0)
209
1.00000000000000
210
sage: f(1)
211
2.71828182845905
212
sage: f(-3)
213
0.0497870683678639
214
"""
215
if PY_TYPE_CHECK(domain, type):
216
domain = Set_PythonType(domain)
217
Map.__init__(self, domain, codomain)
218
self._coerce_cost = 100
219
self._func = func
220
if parent_as_first_arg is None:
221
if PY_TYPE_CHECK(func, types.MethodType):
222
# can't easily access self
223
parent_as_first_arg = False
224
elif PY_TYPE_CHECK(func, BuiltinMethodType):
225
parent_as_first_arg = codomain is func.__self__
226
else:
227
parent_as_first_arg = True
228
self._parent_as_first_arg = parent_as_first_arg
229
try:
230
self._repr_type_str = "Conversion via %s" % self._func.__name__
231
except AttributeError:
232
self._repr_type_str = "Conversion via %s" % self._func
233
234
cpdef Element _call_(self, x):
235
"""
236
Because self._func may be anything we do a little bit of sanity
237
checking (the return value must be an element with the correct parent).
238
239
TESTS::
240
241
sage: from sage.structure.coerce_maps import CallableConvertMap
242
sage: def foo(P, x): return x
243
sage: f = CallableConvertMap(ZZ, ZZ, foo)
244
sage: f(0)
245
0
246
sage: f = CallableConvertMap(ZZ, QQ, foo)
247
sage: f(0)
248
Traceback (most recent call last):
249
...
250
RuntimeError: BUG in coercion model: <function foo at ...> returned element with wrong parent (expected Rational Field got Integer Ring)
251
sage: def foo(P, x): return None
252
sage: f = CallableConvertMap(ZZ, QQ, foo)
253
sage: f(0)
254
Traceback (most recent call last):
255
...
256
RuntimeError: BUG in coercion model: <function foo at ...> returned None
257
"""
258
cdef Element y
259
try:
260
if self._parent_as_first_arg:
261
y = self._func(self._codomain, x)
262
else:
263
y = self._func(x)
264
except:
265
if print_warnings:
266
print self._func
267
print self._codomain
268
raise
269
if y is None:
270
raise RuntimeError, "BUG in coercion model: %s returned None" % (self._func)
271
elif y._parent is not self._codomain:
272
raise RuntimeError, "BUG in coercion model: %s returned element with wrong parent (expected %s got %s)" % (self._func, self._codomain, y._parent)
273
return y
274
275
cpdef Element _call_with_args(self, x, args=(), kwds={}):
276
"""
277
TESTS::
278
279
sage: from sage.structure.coerce_maps import CallableConvertMap
280
sage: def foo(P, x, y): return x or y
281
sage: f = CallableConvertMap(ZZ, ZZ, foo)
282
sage: f(0, 3)
283
3
284
sage: f = CallableConvertMap(ZZ, QQ, foo)
285
sage: f(0, 3)
286
Traceback (most recent call last):
287
...
288
RuntimeError: BUG in coercion model: <function foo at ...> returned element with wrong parent (expected Rational Field got Integer Ring)
289
sage: f(None, None)
290
Traceback (most recent call last):
291
...
292
RuntimeError: BUG in coercion model: <function foo at ...> returned None
293
"""
294
cdef Element y
295
try:
296
if self._parent_as_first_arg:
297
y = self._func(self._codomain, x, *args, **kwds)
298
else:
299
y = self._func(x, *args, **kwds)
300
except:
301
if print_warnings:
302
print self._func
303
print self._codomain
304
raise
305
if y is None:
306
raise RuntimeError, "BUG in coercion model: %s returned None" % (self._func)
307
elif y._parent is not self._codomain:
308
raise RuntimeError, "BUG in coercion model: %s returned element with wrong parent (expected %s got %s)" % (self._func, self._codomain, y._parent)
309
return y
310
311
312
cdef class CCallableConvertMap_class(Map):
313
cdef Element (*_func)(Parent, object)
314
cdef public _name
315
316
def __init__(self, domain, codomain, name):
317
if PY_TYPE_CHECK(domain, type):
318
domain = Set_PythonType(domain)
319
Map.__init__(self, domain, codomain)
320
self._coerce_cost = 10
321
self._name = name
322
323
cpdef Element _call_(self, x):
324
"""
325
TESTS::
326
327
sage: from sage.structure.coerce_maps import test_CCallableConvertMap
328
sage: f = test_CCallableConvertMap(QQ, 'test')
329
sage: f(1/3)
330
-8/27
331
"""
332
return self._func(self._codomain, x)
333
334
def _repr_type(self):
335
"""
336
EXAMPLES::
337
338
sage: from sage.structure.coerce_maps import test_CCallableConvertMap
339
sage: test_CCallableConvertMap(ZZ, 'any name')
340
Conversion via c call 'any name' map:
341
From: Integer Ring
342
To: Integer Ring
343
sage: test_CCallableConvertMap(ZZ, None) # random address
344
Conversion via c call at 0xc339000 map:
345
From: Integer Ring
346
To: Integer Ring
347
"""
348
if self._name is None:
349
return "Conversion via c call at 0x%x" % <long>self._func
350
else:
351
return "Conversion via c call '%s'" % self._name
352
353
354
cdef Map CCallableConvertMap(domain, codomain, void* func, name):
355
"""
356
Use this to create a map from domain to codomain by calling func
357
(which must be a function pointer taking a Parent and object, and
358
returning an Element in the given Parent).
359
360
This is the c analogue of CallableConvertMap.
361
"""
362
# Cython doesn't yet accept function pointers as arguments,
363
# change this when it does.
364
cdef CCallableConvertMap_class map = CCallableConvertMap_class(domain, codomain, name)
365
map._func = <Element (*)(Parent, object)>func
366
return map
367
368
cpdef Element _ccall_test_function(codomain, x):
369
"""
370
For testing CCallableConvertMap_class. Returns x*x*x-x in the codomain.
371
372
TESTS::
373
374
sage: from sage.structure.coerce_maps import _ccall_test_function
375
sage: _ccall_test_function(ZZ, 1)
376
0
377
sage: _ccall_test_function(ZZ, 2)
378
6
379
sage: _ccall_test_function(ZZ, -3)
380
-24
381
"""
382
return codomain(x*x*x-x)
383
384
def test_CCallableConvertMap(domain, name=None):
385
"""
386
For testing CCallableConvertMap_class.
387
388
TESTS::
389
390
sage: from sage.structure.coerce_maps import test_CCallableConvertMap
391
sage: f = test_CCallableConvertMap(ZZ, 'test'); f
392
Conversion via c call 'test' map:
393
From: Integer Ring
394
To: Integer Ring
395
sage: f(3)
396
24
397
sage: f(9)
398
720
399
"""
400
return CCallableConvertMap(domain, domain, <void*>&_ccall_test_function, name)
401
402
403
cdef class ListMorphism(Map):
404
405
cdef Map _real_morphism
406
407
def __init__(self, domain, Map real_morphism):
408
if not PY_TYPE_CHECK(domain, Parent):
409
domain = Set_PythonType(domain)
410
Map.__init__(self, domain, real_morphism.codomain())
411
self._coerce_cost = real_morphism._coerce_cost + 3
412
self._real_morphism = real_morphism
413
self._repr_type_str = "List"
414
415
cpdef Element _call_(self, x):
416
try:
417
x = x._data
418
except AttributeError:
419
x = list(x)
420
return self._real_morphism._call_(x)
421
422
cpdef Element _call_with_args(self, x, args=(), kwds={}):
423
try:
424
x = x._data
425
except AttributeError:
426
x = list(x)
427
return self._real_morphism._call_with_args(x, args, kwds)
428
429
430
cdef class TryMap(Map):
431
def __init__(self, Map morphism_preferred, Map morphism_backup, error_types=None):
432
"""
433
TESTS::
434
435
sage: sage.structure.coerce_maps.TryMap(RDF.coerce_map_from(QQ), RDF.coerce_map_from(ZZ))
436
Traceback (most recent call last):
437
...
438
TypeError: incorrectly matching parent
439
"""
440
if (morphism_preferred.domain() is not morphism_backup.domain()
441
or morphism_preferred.codomain() is not morphism_backup.codomain()):
442
raise TypeError, "incorrectly matching parent"
443
Map.__init__(self, morphism_preferred.parent())
444
self._map_p = morphism_preferred
445
self._map_b = morphism_backup
446
if error_types is None:
447
self._error_types = (ValueError, TypeError, AttributeError)
448
else:
449
self._error_types = error_types
450
451
cpdef Element _call_(self, x):
452
"""
453
EXAMPLES::
454
455
sage: map1 = sage.structure.coerce_maps.CallableConvertMap(ZZ, QQ, lambda parent, x: 1/x)
456
sage: map2 = QQ.coerce_map_from(ZZ)
457
sage: map = sage.structure.coerce_maps.TryMap(map1, map2, error_types=(ZeroDivisionError,))
458
sage: map(3)
459
1/3
460
sage: map(-7)
461
-1/7
462
sage: map(0)
463
0
464
"""
465
try:
466
return self._map_p._call_(x)
467
except self._error_types:
468
return self._map_b._call_(x)
469
470
cpdef Element _call_with_args(self, x, args=(), kwds={}):
471
"""
472
EXAMPLES::
473
474
sage: map1 = sage.structure.coerce_maps.CallableConvertMap(ZZ, QQ, lambda parent, x, y: y/x)
475
sage: map2 = sage.structure.coerce_maps.CallableConvertMap(ZZ, QQ, lambda parent, x, y: 23/1)
476
sage: map = sage.structure.coerce_maps.TryMap(map1, map2, error_types=(ZeroDivisionError,))
477
sage: map._call_with_args(3, (2,))
478
2/3
479
sage: map._call_with_args(-7, (5,))
480
-5/7
481
sage: map._call_with_args(0, (1,))
482
23
483
"""
484
try:
485
return self._map_p._call_with_args(x, args, kwds)
486
except self._error_types:
487
return self._map_b._call_with_args(x, args, kwds)
488
489