Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/structure/coerce_actions.pyx
4079 views
1
"""
2
Coerce actions
3
"""
4
#*****************************************************************************
5
# Copyright (C) 2007 Robert Bradshaw <[email protected]>
6
#
7
# Distributed under the terms of the GNU General Public License (GPL)
8
#
9
# http://www.gnu.org/licenses/
10
#*****************************************************************************
11
12
import operator
13
14
include "../ext/stdsage.pxi"
15
include "../ext/interrupt.pxi"
16
include "../ext/python_int.pxi"
17
include "../ext/python_number.pxi"
18
include "coerce.pxi"
19
20
from coerce_exceptions import CoercionException
21
22
cdef extern from *:
23
ctypedef struct RefPyObject "PyObject":
24
int ob_refcnt
25
26
cdef _record_exception():
27
from element import get_coercion_model
28
get_coercion_model()._record_exception()
29
30
cdef inline an_element(R):
31
if isinstance(R, Parent):
32
return R.an_element()
33
else:
34
for x in ([(1, 2)], "abc", 10.5, 10):
35
try:
36
return R(x)
37
except Exception:
38
pass
39
40
cdef class LAction(Action):
41
"""Action calls _l_action of the actor."""
42
def __init__(self, G, S):
43
Action.__init__(self, G, S, True, operator.mul)
44
cpdef _call_(self, g, a):
45
return g._l_action(a) # a * g
46
47
48
cdef class RAction(Action):
49
"""Action calls _r_action of the actor."""
50
def __init__(self, G, S):
51
Action.__init__(self, G, S, False, operator.mul)
52
cpdef _call_(self, a, g):
53
return g._r_action(a) # g * a
54
55
56
# In the code below, I take the convention that g is acting on a.
57
58
cdef class GenericAction(Action):
59
60
cdef _codomain
61
62
def __init__(self, Parent G, S, is_left, bint check=True):
63
"""
64
TESTS::
65
66
sage: sage.structure.coerce_actions.ActedUponAction(MatrixSpace(ZZ, 2), Cusps, True)
67
Left action by Full MatrixSpace of 2 by 2 dense matrices over Integer Ring on Set P^1(QQ) of all cusps
68
69
sage: sage.structure.coerce_actions.GenericAction(QQ, Zmod(6), True)
70
Traceback (most recent call last):
71
...
72
NotImplementedError: Action not implemented.
73
74
This will break if we tried to use it::
75
76
sage: sage.structure.coerce_actions.GenericAction(QQ, Zmod(6), True, check=False)
77
Left action by Rational Field on Ring of integers modulo 6
78
79
"""
80
Action.__init__(self, G, S, is_left, operator.mul)
81
if check:
82
res = self.act(G.an_element(), S.an_element())
83
if res is None:
84
raise CoercionException
85
_codomain = parent_c(res)
86
87
def codomain(self):
88
"""
89
Returns the "codomain" of this action, i.e. the Parent in which the
90
result elements live. Typically, this should be the same as the
91
acted upon set.
92
93
EXAMPLES::
94
95
sage: A = sage.structure.coerce_actions.ActedUponAction(MatrixSpace(ZZ, 2), Cusps, True)
96
sage: A.codomain()
97
Set P^1(QQ) of all cusps
98
sage: A = sage.structure.coerce_actions.ActOnAction(SymmetricGroup(3), QQ['x,y,z'], False)
99
sage: A.codomain()
100
Multivariate Polynomial Ring in x, y, z over Rational Field
101
"""
102
if self._codomain is None:
103
self._codomain = parent_c(self.act(an_element(self.G), an_element(self.S)))
104
return self._codomain
105
106
107
cdef class ActOnAction(GenericAction):
108
"""
109
Class for actions defined via the _act_on_ method.
110
"""
111
cpdef _call_(self, a, b):
112
"""
113
TESTS::
114
115
sage: G = SymmetricGroup(3)
116
sage: R.<x,y,z> = QQ[]
117
sage: A = sage.structure.coerce_actions.ActOnAction(G, R, False)
118
sage: A._call_(x^2 + y - z, G((1,2)))
119
y^2 + x - z
120
sage: A._call_(x+2*y+3*z, G((1,3,2)))
121
2*x + 3*y + z
122
123
sage: type(A)
124
<type 'sage.structure.coerce_actions.ActOnAction'>
125
"""
126
if self._is_left:
127
return (<Element>a)._act_on_(b, True)
128
else:
129
return (<Element>b)._act_on_(a, False)
130
131
cdef class ActedUponAction(GenericAction):
132
"""
133
Class for actions defined via the _acted_upon_ method.
134
"""
135
cpdef _call_(self, a, b):
136
"""
137
TESTS::
138
139
sage: A = sage.structure.coerce_actions.ActedUponAction(MatrixSpace(ZZ, 2), Cusps, True)
140
sage: A.act(matrix(ZZ, 2, [1,0,2,-1]), Cusp(1,2))
141
Infinity
142
sage: A._call_(matrix(ZZ, 2, [1,0,2,-1]), Cusp(1,2))
143
Infinity
144
145
sage: type(A)
146
<type 'sage.structure.coerce_actions.ActedUponAction'>
147
"""
148
if self._is_left:
149
return (<Element>b)._acted_upon_(a, False)
150
else:
151
return (<Element>a)._acted_upon_(b, True)
152
153
def detect_element_action(Parent X, Y, bint X_on_left):
154
r"""
155
Returns an action of X on Y or Y on X as defined by elements X, if any.
156
157
EXAMPLES::
158
159
sage: from sage.structure.coerce_actions import detect_element_action
160
sage: detect_element_action(ZZ['x'], ZZ, False)
161
Left scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring
162
sage: detect_element_action(ZZ['x'], QQ, True)
163
Right scalar multiplication by Rational Field on Univariate Polynomial Ring in x over Integer Ring
164
sage: detect_element_action(Cusps, MatrixSpace(ZZ, 2), False)
165
Left action by Full MatrixSpace of 2 by 2 dense matrices over Integer Ring on Set P^1(QQ) of all cusps
166
sage: detect_element_action(Cusps, MatrixSpace(ZZ, 2), True),
167
(None,)
168
sage: detect_element_action(ZZ, QQ, True),
169
(None,)
170
171
TESTS:
172
173
This test checks that the issue in Trac #7718 has been fixed::
174
175
sage: class MyParent(Parent):
176
... def an_element(self):
177
... pass
178
...
179
sage: A = MyParent()
180
sage: detect_element_action(A, ZZ, True)
181
Traceback (most recent call last):
182
...
183
RuntimeError: an_element() for <class '__main__.MyParent'> returned None
184
"""
185
cdef Element x = an_element(X)
186
if x is None:
187
raise RuntimeError, "an_element() for %s returned None" % X
188
y = an_element(Y)
189
if y is None:
190
if isinstance(Y, Parent):
191
raise RuntimeError, "an_element() for %s returned None" % Y
192
else:
193
return # don't know how to make elements of this type...
194
if isinstance(x, ModuleElement) and isinstance(y, RingElement):
195
# Elements defining _lmul_ and _rmul_
196
try:
197
return (RightModuleAction if X_on_left else LeftModuleAction)(Y, X)
198
except CoercionException:
199
_record_exception()
200
try:
201
# Elements defining _act_on_
202
if x._act_on_(y, X_on_left) is not None:
203
return ActOnAction(X, Y, X_on_left, False)
204
except CoercionException:
205
_record_exception()
206
if isinstance(Y, Parent):
207
try:
208
# Elements defining _acted_upon_
209
if x._acted_upon_(y, X_on_left) is not None:
210
return ActedUponAction(Y, X, not X_on_left, False)
211
except CoercionException:
212
_record_exception()
213
214
215
cdef class ModuleAction(Action):
216
217
def __init__(self, G, S):
218
"""
219
This creates an action of an element of a module by an element of its
220
base ring. The simplest example to keep in mind is R acting on the
221
polynomial ring R[x].
222
223
The actual action is implemented by the _rmul_ or _lmul_ function on
224
its elements. We must, however, be very particular about what we feed
225
into these functions because they operate under the assumption that
226
the inputs lie exactly in the base ring and may segfault otherwise.
227
228
Thus we handle all possible base extensions manually here. This is
229
an abstract class, one must actually instantiate a LeftModuleAction
230
or a RightModuleAction
231
232
EXAMPLES::
233
234
sage: from sage.structure.coerce_actions import LeftModuleAction
235
sage: LeftModuleAction(ZZ, ZZ['x'])
236
Left scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring
237
sage: LeftModuleAction(ZZ, QQ['x'])
238
Left scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Rational Field
239
sage: LeftModuleAction(QQ, ZZ['x'])
240
Left scalar multiplication by Rational Field on Univariate Polynomial Ring in x over Integer Ring
241
sage: LeftModuleAction(QQ, ZZ['x']['y'])
242
Left scalar multiplication by Rational Field on Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Integer Ring
243
244
The following tests against a problem that was relevant during work on
245
trac ticket #9944::
246
247
sage: R.<x> = PolynomialRing(ZZ)
248
sage: S.<x> = PolynomialRing(ZZ, sparse=True)
249
sage: 1/R.0
250
1/x
251
sage: 1/S.0
252
1/x
253
254
"""
255
Action.__init__(self, G, S, not PY_TYPE_CHECK(self, RightModuleAction), operator.mul)
256
if not isinstance(G, Parent):
257
# only let Parents act
258
raise TypeError, "Actor must be a parent."
259
base = S.base()
260
if base is S or base is None:
261
# The right thing to do is a normal multiplication
262
raise CoercionException, "Best viewed as standard multiplication"
263
# Objects are implemented with the assumption that
264
# _rmul_ is given an element of the base ring
265
if G is not base:
266
# first we try the easy case of coercing G to the base ring of S
267
self.connecting = base.coerce_map_from(G)
268
if self.connecting is None:
269
# otherwise, we try and find a base extension
270
from sage.categories.pushout import pushout
271
# this may raise a type error, which we propagate
272
self.extended_base = pushout(G, S)
273
# make sure the pushout actually gave correct a base extension of S
274
if self.extended_base.base() != pushout(G, base):
275
raise CoercionException, "Actor must be coercible into base."
276
else:
277
self.connecting = self.extended_base.base().coerce_map_from(G)
278
if self.connecting is None:
279
# this may happen if G is, say, int rather than a parent
280
# TODO: let python types be valid actions
281
raise CoercionException, "Missing connecting morphism"
282
283
# Don't waste time if our connecting morphisms happen to be the identity.
284
if self.connecting is not None and self.connecting.codomain() is G:
285
self.connecting = None
286
287
if self.extended_base is not None and self.extended_base is S:
288
self.extended_base = None
289
290
# At this point, we can assert it is safe to call _Xmul_c
291
the_ring = G if self.connecting is None else self.connecting.codomain()
292
the_set = S if self.extended_base is None else self.extended_base
293
assert the_ring is the_set.base(), "BUG in coercion model\n Apparently there are two versions of\n %s\n in the cache."%the_ring
294
295
g = G.an_element()
296
a = S.an_element()
297
if not isinstance(g, RingElement) or not isinstance(a, ModuleElement):
298
raise CoercionException, "Not a ring element acting on a module element."
299
res = self.act(g, a)
300
301
if parent_c(res) is not S and parent_c(res) is not self.extended_base:
302
# In particular we will raise an error if res is None
303
raise CoercionException, "Result is None or has wrong parent."
304
305
306
def _repr_name_(self):
307
"""
308
The default name of this action type, which is has a sane default.
309
310
EXAMPLES::
311
312
sage: from sage.structure.coerce_actions import LeftModuleAction, RightModuleAction
313
sage: A = LeftModuleAction(ZZ, ZZ['x']); A
314
Left scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring
315
sage: A._repr_name_()
316
'scalar multiplication'
317
sage: RightModuleAction(GF(5), GF(5)[['t']])
318
Right scalar multiplication by Finite Field of size 5 on Power Series Ring in t over Finite Field of size 5
319
"""
320
return "scalar multiplication"
321
322
def codomain(self):
323
"""
324
The codomain of self, which may or may not be equal to the domain.
325
326
EXAMPLES::
327
328
sage: from sage.structure.coerce_actions import LeftModuleAction
329
sage: A = LeftModuleAction(QQ, ZZ['x,y,z'])
330
sage: A.codomain()
331
Multivariate Polynomial Ring in x, y, z over Rational Field
332
"""
333
if self.extended_base is not None:
334
return self.extended_base
335
return self.S
336
337
def domain(self):
338
"""
339
The domain of self, which is the module that is being acted on.
340
341
EXAMPLES::
342
343
sage: from sage.structure.coerce_actions import LeftModuleAction
344
sage: A = LeftModuleAction(QQ, ZZ['x,y,z'])
345
sage: A.domain()
346
Multivariate Polynomial Ring in x, y, z over Integer Ring
347
"""
348
return self.S
349
350
351
352
cdef class LeftModuleAction(ModuleAction):
353
354
cpdef _call_(self, g, a):
355
"""
356
A left module action is an action that takes the ring element as the
357
first argument (the left side) and the module element as the second
358
argument (the right side).
359
360
EXAMPLES::
361
362
sage: from sage.structure.coerce_actions import LeftModuleAction
363
sage: R.<x> = QQ['x']
364
sage: A = LeftModuleAction(ZZ, R)
365
sage: A(5, x+1)
366
5*x + 5
367
sage: R.<x> = ZZ['x']
368
sage: A = LeftModuleAction(QQ, R)
369
sage: A(1/2, x+1)
370
1/2*x + 1/2
371
sage: A._call_(1/2, x+1) # safe only when arguments have exactly the correct parent
372
1/2*x + 1/2
373
"""
374
if self.connecting is not None:
375
g = self.connecting._call_(g)
376
if self.extended_base is not None:
377
a = self.extended_base(a)
378
return (<ModuleElement>a)._rmul_(<RingElement>g) # a * g
379
380
381
cdef class RightModuleAction(ModuleAction):
382
383
def __cinit__(self):
384
self.is_inplace = 0
385
386
cpdef _call_(self, a, g):
387
"""
388
A right module action is an action that takes the module element as the
389
first argument (the left side) and the ring element as the second
390
argument (the right side).
391
392
EXAMPLES::
393
394
sage: from sage.structure.coerce_actions import RightModuleAction
395
sage: R.<x> = QQ['x']
396
sage: A = RightModuleAction(ZZ, R)
397
sage: A(x+5, 2)
398
2*x + 10
399
sage: A._call_(x+5, 2) # safe only when arguments have exactly the correct parent
400
2*x + 10
401
"""
402
cdef PyObject* tmp
403
if self.connecting is not None:
404
g = self.connecting._call_(g)
405
if self.extended_base is not None:
406
a = self.extended_base(a)
407
# TODO: figure out where/why the polynomial constructor is caching 'a'
408
if (<RefPyObject *>a).ob_refcnt == 2:
409
b = self.extended_base(0)
410
if (<RefPyObject *>a).ob_refcnt == 1:
411
# This is a truly new object, mutate it
412
return (<ModuleElement>a)._ilmul_(<RingElement>g) # a * g
413
else:
414
return (<ModuleElement>a)._lmul_(<RingElement>g) # a * g
415
else:
416
# If we have few enough references to this object, then we know
417
# it is safe to do a (mutating) inplace operation.
418
if (<RefPyObject *>a).ob_refcnt < inplace_threshold + self.is_inplace:
419
return (<ModuleElement>a)._ilmul_(<RingElement>g) # a * g
420
else:
421
return (<ModuleElement>a)._lmul_(<RingElement>g) # a * g
422
423
424
425
cdef class IntegerMulAction(Action):
426
427
def __init__(self, ZZ, M, is_left=True):
428
r"""
429
This class implements the action `n \cdot a = a + a + \cdots + a` via
430
repeated doubling.
431
432
Both addition and negation must be defined on the set `A`.
433
434
EXAMPLES::
435
436
sage: from sage.structure.coerce_actions import IntegerMulAction
437
sage: R.<x> = QQ['x']
438
sage: act = IntegerMulAction(ZZ, R)
439
sage: act(5, x)
440
5*x
441
sage: act(0, x)
442
0
443
sage: act(-3, x-1)
444
-3*x + 3
445
"""
446
if PY_TYPE_CHECK(ZZ, type):
447
from sage.structure.parent import Set_PythonType
448
ZZ = Set_PythonType(ZZ)
449
test = M.an_element() + (-M.an_element()) # make sure addition and negation is allowed
450
Action.__init__(self, ZZ, M, is_left, operator.mul)
451
452
cpdef _call_(self, nn, a):
453
"""
454
EXAMPLES::
455
456
sage: from sage.structure.coerce_actions import IntegerMulAction
457
sage: act = IntegerMulAction(ZZ, GF(101))
458
sage: act(3, 9)
459
27
460
sage: act(3^689, 9)
461
42
462
sage: 3^689 * mod(9, 101)
463
42
464
465
Use round off error to verify this is doing actual repeated addition
466
instead of just multiplying::
467
468
sage: act = IntegerMulAction(ZZ, RR)
469
sage: act(49, 1/49) == 49*RR(1/49)
470
False
471
"""
472
if not self._is_left:
473
a, nn = nn, a
474
if not PyInt_CheckExact(nn):
475
nn = PyNumber_Int(nn)
476
if not PyInt_CheckExact(nn):
477
return fast_mul(a, nn)
478
return fast_mul_long(a, PyInt_AS_LONG(nn))
479
480
def __invert__(self):
481
"""
482
EXAMPLES::
483
484
sage: from sage.structure.coerce_actions import IntegerMulAction
485
sage: act = IntegerMulAction(ZZ, CDF)
486
sage: ~act
487
Traceback (most recent call last):
488
...
489
TypeError: No generic module division by Z.
490
"""
491
raise TypeError, "No generic module division by Z."
492
493
def _repr_name_(self):
494
"""
495
EXAMPLES::
496
497
sage: from sage.structure.coerce_actions import IntegerMulAction
498
sage: IntegerMulAction(ZZ, GF(5))
499
Left Integer Multiplication by Integer Ring on Finite Field of size 5
500
"""
501
return "Integer Multiplication"
502
503
504
505
cdef inline fast_mul(a, n):
506
# We cannot use sig_on() here because this may call arbitrary Python
507
# code raising exceptions. -- Jeroen Demeyer
508
if n < 0:
509
n = -n
510
a = -a
511
pow2a = a
512
while n & 1 == 0:
513
pow2a += pow2a
514
n = n >> 1
515
sum = pow2a
516
n = n >> 1
517
while n != 0:
518
pow2a += pow2a
519
if n & 1:
520
sum += pow2a
521
n = n >> 1
522
return sum
523
524
cdef inline fast_mul_long(a, long n):
525
# We cannot use sig_on() here because this may call arbitrary Python
526
# code raising exceptions. -- Jeroen Demeyer
527
if n < 0:
528
n = -n
529
a = -a
530
if n < 4:
531
if n == 0: return parent_c(a)(0)
532
elif n == 1: return a
533
elif n == 2: return a+a
534
elif n == 3: return a+a+a
535
pow2a = a
536
while n & 1 == 0:
537
pow2a += pow2a
538
n = n >> 1
539
sum = pow2a
540
n = n >> 1
541
while n != 0:
542
pow2a += pow2a
543
if n & 1:
544
sum += pow2a
545
n = n >> 1
546
return sum
547
548