Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/structure/coerce_actions.pyx
8814 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 "sage/ext/stdsage.pxi"
15
include "sage/ext/interrupt.pxi"
16
from cpython.int cimport *
17
from cpython.number cimport *
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),
104
an_element(self.underlying_set())))
105
return self._codomain
106
107
108
cdef class ActOnAction(GenericAction):
109
"""
110
Class for actions defined via the _act_on_ method.
111
"""
112
cpdef _call_(self, a, b):
113
"""
114
TESTS::
115
116
sage: G = SymmetricGroup(3)
117
sage: R.<x,y,z> = QQ[]
118
sage: A = sage.structure.coerce_actions.ActOnAction(G, R, False)
119
sage: A._call_(x^2 + y - z, G((1,2)))
120
y^2 + x - z
121
sage: A._call_(x+2*y+3*z, G((1,3,2)))
122
2*x + 3*y + z
123
124
sage: type(A)
125
<type 'sage.structure.coerce_actions.ActOnAction'>
126
"""
127
if self._is_left:
128
return (<Element>a)._act_on_(b, True)
129
else:
130
return (<Element>b)._act_on_(a, False)
131
132
cdef class ActedUponAction(GenericAction):
133
"""
134
Class for actions defined via the _acted_upon_ method.
135
"""
136
cpdef _call_(self, a, b):
137
"""
138
TESTS::
139
140
sage: A = sage.structure.coerce_actions.ActedUponAction(MatrixSpace(ZZ, 2), Cusps, True)
141
sage: A.act(matrix(ZZ, 2, [1,0,2,-1]), Cusp(1,2))
142
Infinity
143
sage: A._call_(matrix(ZZ, 2, [1,0,2,-1]), Cusp(1,2))
144
Infinity
145
146
sage: type(A)
147
<type 'sage.structure.coerce_actions.ActedUponAction'>
148
"""
149
if self._is_left:
150
return (<Element>b)._acted_upon_(a, False)
151
else:
152
return (<Element>a)._acted_upon_(b, True)
153
154
def detect_element_action(Parent X, Y, bint X_on_left, X_el=None, Y_el=None):
155
r"""
156
Returns an action of X on Y or Y on X as defined by elements X, if any.
157
158
EXAMPLES::
159
160
sage: from sage.structure.coerce_actions import detect_element_action
161
sage: detect_element_action(ZZ['x'], ZZ, False)
162
Left scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring
163
sage: detect_element_action(ZZ['x'], QQ, True)
164
Right scalar multiplication by Rational Field on Univariate Polynomial Ring in x over Integer Ring
165
sage: detect_element_action(Cusps, MatrixSpace(ZZ, 2), False)
166
Left action by Full MatrixSpace of 2 by 2 dense matrices over Integer Ring on Set P^1(QQ) of all cusps
167
sage: detect_element_action(Cusps, MatrixSpace(ZZ, 2), True),
168
(None,)
169
sage: detect_element_action(ZZ, QQ, True),
170
(None,)
171
172
TESTS:
173
174
This test checks that the issue in :trac:`7718` has been fixed::
175
176
sage: class MyParent(Parent):
177
....: def an_element(self):
178
....: pass
179
....:
180
sage: A = MyParent()
181
sage: detect_element_action(A, ZZ, True)
182
Traceback (most recent call last):
183
...
184
RuntimeError: an_element() for <class '__main__.MyParent'> returned None
185
"""
186
cdef Element x
187
if X_el is None or (parent_c(X_el) is not X):
188
x = an_element(X)
189
else:
190
x = X_el
191
if x is None:
192
raise RuntimeError("an_element() for %s returned None" % X)
193
if Y_el is None or (parent_c(Y_el) is not Y):
194
y = an_element(Y)
195
else:
196
y = Y_el
197
if y is None:
198
if isinstance(Y, Parent):
199
raise RuntimeError("an_element() for %s returned None" % Y)
200
else:
201
return # don't know how to make elements of this type...
202
if isinstance(x, ModuleElement) and isinstance(y, RingElement):
203
# Elements defining _lmul_ and _rmul_
204
try:
205
return (RightModuleAction if X_on_left else LeftModuleAction)(Y, X, y, x)
206
except CoercionException, msg:
207
_record_exception()
208
try:
209
# Elements defining _act_on_
210
if x._act_on_(y, X_on_left) is not None:
211
return ActOnAction(X, Y, X_on_left, False)
212
except CoercionException:
213
_record_exception()
214
if isinstance(Y, Parent):
215
try:
216
# Elements defining _acted_upon_
217
if x._acted_upon_(y, X_on_left) is not None:
218
return ActedUponAction(Y, X, not X_on_left, False)
219
except CoercionException:
220
_record_exception()
221
222
223
cdef class ModuleAction(Action):
224
"""
225
Module action.
226
227
.. SEEALSO::
228
229
This is an abstract class, one must actually instantiate a
230
:class:`LeftModuleAction` or a :class:`RightModuleAction`.
231
232
INPUT:
233
234
- ``G`` -- the actor, an instance of :class:`~sage.structure.parent.Parent`.
235
- ``S`` -- the object that is acted upon.
236
- ``g`` -- optional, an element of ``G``.
237
- ``a`` -- optional, an element of ``S``.
238
- ``check`` -- if True (default), then there will be no consistency tests
239
performed on sample elements.
240
241
NOTE:
242
243
By default, the sample elements of ``S`` and ``G`` are obtained from
244
:meth:`~sage.structure.parent.Parent.an_element`, which relies on the
245
implementation of an ``_an_element_()`` method. This is not always
246
awailable. But usually, the action is only needed when one already
247
*has* two elements. Hence, by :trac:`14249`, the coercion model will
248
pass these two elements the the :class:`ModuleAction` constructor.
249
250
The actual action is implemented by the ``_rmul_`` or ``_lmul_``
251
function on its elements. We must, however, be very particular about
252
what we feed into these functions, because they operate under the
253
assumption that the inputs lie exactly in the base ring and may
254
segfault otherwise. Thus we handle all possible base extensions
255
manually here.
256
257
"""
258
def __init__(self, G, S, g=None, a=None, check=True):
259
"""
260
This creates an action of an element of a module by an element of its
261
base ring. The simplest example to keep in mind is R acting on the
262
polynomial ring R[x].
263
264
EXAMPLES::
265
266
sage: from sage.structure.coerce_actions import LeftModuleAction
267
sage: LeftModuleAction(ZZ, ZZ['x'])
268
Left scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring
269
sage: LeftModuleAction(ZZ, QQ['x'])
270
Left scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Rational Field
271
sage: LeftModuleAction(QQ, ZZ['x'])
272
Left scalar multiplication by Rational Field on Univariate Polynomial Ring in x over Integer Ring
273
sage: LeftModuleAction(QQ, ZZ['x']['y'])
274
Left scalar multiplication by Rational Field on Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Integer Ring
275
276
The following tests against a problem that was relevant during work on
277
trac ticket #9944::
278
279
sage: R.<x> = PolynomialRing(ZZ)
280
sage: S.<x> = PolynomialRing(ZZ, sparse=True)
281
sage: 1/R.0
282
1/x
283
sage: 1/S.0
284
1/x
285
286
"""
287
Action.__init__(self, G, S, not PY_TYPE_CHECK(self, RightModuleAction), operator.mul)
288
if not isinstance(G, Parent):
289
# only let Parents act
290
raise TypeError, "Actor must be a parent."
291
base = S.base()
292
if base is S or base is None:
293
# The right thing to do is a normal multiplication
294
raise CoercionException, "Best viewed as standard multiplication"
295
# Objects are implemented with the assumption that
296
# _rmul_ is given an element of the base ring
297
if G is not base:
298
# first we try the easy case of coercing G to the base ring of S
299
self.connecting = base.coerce_map_from(G)
300
if self.connecting is None:
301
# otherwise, we try and find a base extension
302
from sage.categories.pushout import pushout
303
# this may raise a type error, which we propagate
304
self.extended_base = pushout(G, S)
305
# make sure the pushout actually gave correct a base extension of S
306
if self.extended_base.base() != pushout(G, base):
307
raise CoercionException, "Actor must be coercible into base."
308
else:
309
self.connecting = self.extended_base.base().coerce_map_from(G)
310
if self.connecting is None:
311
# this may happen if G is, say, int rather than a parent
312
# TODO: let python types be valid actions
313
raise CoercionException, "Missing connecting morphism"
314
315
# Don't waste time if our connecting morphisms happen to be the identity.
316
if self.connecting is not None and self.connecting.codomain() is G:
317
self.connecting = None
318
319
if self.extended_base is not None and self.extended_base is S:
320
self.extended_base = None
321
322
# At this point, we can assert it is safe to call _Xmul_c
323
the_ring = G if self.connecting is None else self.connecting.codomain()
324
the_set = S if self.extended_base is None else self.extended_base
325
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
326
327
if not check:
328
return
329
if g is None:
330
g = G.an_element()
331
if parent_c(g) is not G:
332
raise CoercionException("The parent of %s is not %s but %s"%(g,G,parent_c(g)))
333
if a is None:
334
a = S.an_element()
335
if parent_c(a) is not S:
336
raise CoercionException("The parent of %s is not %s but %s"%(a,S,parent_c(a)))
337
if not isinstance(g, RingElement) or not isinstance(a, ModuleElement):
338
raise CoercionException("Not a ring element acting on a module element.")
339
res = self.act(g, a)
340
if parent_c(res) is not the_set:
341
# In particular we will raise an error if res is None
342
raise CoercionException("Result is None or has wrong parent.")
343
344
345
def _repr_name_(self):
346
"""
347
The default name of this action type, which is has a sane default.
348
349
EXAMPLES::
350
351
sage: from sage.structure.coerce_actions import LeftModuleAction, RightModuleAction
352
sage: A = LeftModuleAction(ZZ, ZZ['x']); A
353
Left scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring
354
sage: A._repr_name_()
355
'scalar multiplication'
356
sage: RightModuleAction(GF(5), GF(5)[['t']])
357
Right scalar multiplication by Finite Field of size 5 on Power Series Ring in t over Finite Field of size 5
358
"""
359
return "scalar multiplication"
360
361
def codomain(self):
362
"""
363
The codomain of self, which may or may not be equal to the domain.
364
365
EXAMPLES::
366
367
sage: from sage.structure.coerce_actions import LeftModuleAction
368
sage: A = LeftModuleAction(QQ, ZZ['x,y,z'])
369
sage: A.codomain()
370
Multivariate Polynomial Ring in x, y, z over Rational Field
371
"""
372
if self.extended_base is not None:
373
return self.extended_base
374
return self.underlying_set()
375
376
def domain(self):
377
"""
378
The domain of self, which is the module that is being acted on.
379
380
EXAMPLES::
381
382
sage: from sage.structure.coerce_actions import LeftModuleAction
383
sage: A = LeftModuleAction(QQ, ZZ['x,y,z'])
384
sage: A.domain()
385
Multivariate Polynomial Ring in x, y, z over Integer Ring
386
"""
387
return self.underlying_set()
388
389
390
391
cdef class LeftModuleAction(ModuleAction):
392
393
cpdef _call_(self, g, a):
394
"""
395
A left module action is an action that takes the ring element as the
396
first argument (the left side) and the module element as the second
397
argument (the right side).
398
399
EXAMPLES::
400
401
sage: from sage.structure.coerce_actions import LeftModuleAction
402
sage: R.<x> = QQ['x']
403
sage: A = LeftModuleAction(ZZ, R)
404
sage: A(5, x+1)
405
5*x + 5
406
sage: R.<x> = ZZ['x']
407
sage: A = LeftModuleAction(QQ, R)
408
sage: A(1/2, x+1)
409
1/2*x + 1/2
410
sage: A._call_(1/2, x+1) # safe only when arguments have exactly the correct parent
411
1/2*x + 1/2
412
"""
413
if self.connecting is not None:
414
g = self.connecting._call_(g)
415
if self.extended_base is not None:
416
a = self.extended_base(a)
417
return (<ModuleElement>a)._rmul_(<RingElement>g) # a * g
418
419
420
cdef class RightModuleAction(ModuleAction):
421
422
def __cinit__(self):
423
self.is_inplace = 0
424
425
cpdef _call_(self, a, g):
426
"""
427
A right module action is an action that takes the module element as the
428
first argument (the left side) and the ring element as the second
429
argument (the right side).
430
431
EXAMPLES::
432
433
sage: from sage.structure.coerce_actions import RightModuleAction
434
sage: R.<x> = QQ['x']
435
sage: A = RightModuleAction(ZZ, R)
436
sage: A(x+5, 2)
437
2*x + 10
438
sage: A._call_(x+5, 2) # safe only when arguments have exactly the correct parent
439
2*x + 10
440
"""
441
cdef PyObject* tmp
442
if self.connecting is not None:
443
g = self.connecting._call_(g)
444
if self.extended_base is not None:
445
a = self.extended_base(a)
446
# TODO: figure out where/why the polynomial constructor is caching 'a'
447
if (<RefPyObject *>a).ob_refcnt == 2:
448
b = self.extended_base(0)
449
if (<RefPyObject *>a).ob_refcnt == 1:
450
# This is a truly new object, mutate it
451
return (<ModuleElement>a)._ilmul_(<RingElement>g) # a * g
452
else:
453
return (<ModuleElement>a)._lmul_(<RingElement>g) # a * g
454
else:
455
# If we have few enough references to this object, then we know
456
# it is safe to do a (mutating) inplace operation.
457
if (<RefPyObject *>a).ob_refcnt < inplace_threshold + self.is_inplace:
458
return (<ModuleElement>a)._ilmul_(<RingElement>g) # a * g
459
else:
460
return (<ModuleElement>a)._lmul_(<RingElement>g) # a * g
461
462
463
464
cdef class IntegerMulAction(Action):
465
466
def __init__(self, ZZ, M, is_left=True, m=None):
467
r"""
468
This class implements the action `n \cdot a = a + a + \cdots + a` via
469
repeated doubling.
470
471
Both addition and negation must be defined on the set `M`.
472
473
INPUT:
474
475
- An integer ring, ``ZZ``
476
- A ``ZZ`` module ``M``
477
- Optional: An element ``m`` of ``M``
478
479
EXAMPLES::
480
481
sage: from sage.structure.coerce_actions import IntegerMulAction
482
sage: R.<x> = QQ['x']
483
sage: act = IntegerMulAction(ZZ, R)
484
sage: act(5, x)
485
5*x
486
sage: act(0, x)
487
0
488
sage: act(-3, x-1)
489
-3*x + 3
490
"""
491
if PY_TYPE_CHECK(ZZ, type):
492
from sage.structure.parent import Set_PythonType
493
ZZ = Set_PythonType(ZZ)
494
if m is None:
495
m = M.an_element()
496
test = m + (-m) # make sure addition and negation is allowed
497
Action.__init__(self, ZZ, M, is_left, operator.mul)
498
499
cpdef _call_(self, nn, a):
500
"""
501
EXAMPLES::
502
503
sage: from sage.structure.coerce_actions import IntegerMulAction
504
sage: act = IntegerMulAction(ZZ, GF(101))
505
sage: act(3, 9)
506
27
507
sage: act(3^689, 9)
508
42
509
sage: 3^689 * mod(9, 101)
510
42
511
512
Use round off error to verify this is doing actual repeated addition
513
instead of just multiplying::
514
515
sage: act = IntegerMulAction(ZZ, RR)
516
sage: act(49, 1/49) == 49*RR(1/49)
517
False
518
"""
519
if not self._is_left:
520
a, nn = nn, a
521
if not PyInt_CheckExact(nn):
522
nn = PyNumber_Int(nn)
523
if not PyInt_CheckExact(nn):
524
return fast_mul(a, nn)
525
return fast_mul_long(a, PyInt_AS_LONG(nn))
526
527
def __invert__(self):
528
"""
529
EXAMPLES::
530
531
sage: from sage.structure.coerce_actions import IntegerMulAction
532
sage: act = IntegerMulAction(ZZ, CDF)
533
sage: ~act
534
Traceback (most recent call last):
535
...
536
TypeError: No generic module division by Z.
537
"""
538
raise TypeError, "No generic module division by Z."
539
540
def _repr_name_(self):
541
"""
542
EXAMPLES::
543
544
sage: from sage.structure.coerce_actions import IntegerMulAction
545
sage: IntegerMulAction(ZZ, GF(5))
546
Left Integer Multiplication by Integer Ring on Finite Field of size 5
547
"""
548
return "Integer Multiplication"
549
550
551
552
cdef inline fast_mul(a, n):
553
# We cannot use sig_on() here because this may call arbitrary Python
554
# code raising exceptions. -- Jeroen Demeyer
555
if n < 0:
556
n = -n
557
a = -a
558
pow2a = a
559
while n & 1 == 0:
560
pow2a += pow2a
561
n = n >> 1
562
sum = pow2a
563
n = n >> 1
564
while n != 0:
565
pow2a += pow2a
566
if n & 1:
567
sum += pow2a
568
n = n >> 1
569
return sum
570
571
cdef inline fast_mul_long(a, long n):
572
# We cannot use sig_on() here because this may call arbitrary Python
573
# code raising exceptions. -- Jeroen Demeyer
574
if n < 0:
575
n = -n
576
a = -a
577
if n < 4:
578
if n == 0: return parent_c(a)(0)
579
elif n == 1: return a
580
elif n == 2: return a+a
581
elif n == 3: return a+a+a
582
pow2a = a
583
while n & 1 == 0:
584
pow2a += pow2a
585
n = n >> 1
586
sum = pow2a
587
n = n >> 1
588
while n != 0:
589
pow2a += pow2a
590
if n & 1:
591
sum += pow2a
592
n = n >> 1
593
return sum
594
595