Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/categories/action.pyx
8817 views
1
r"""
2
Group, ring, etc. actions on objects.
3
4
The terminology and notation used is suggestive of groups acting on sets,
5
but this framework can be used for modules, algebras, etc.
6
7
A group action $G \times S \rightarrow S$ is a functor from $G$ to Sets.
8
9
.. WARNING::
10
11
An :class:`Action` object only keeps a weak reference to the underlying set
12
which is acted upon. This decision was made in :trac:`715` in order to
13
allow garbage collection within the coercion framework (this is where
14
actions are mainly used) and avoid memory leaks.
15
16
::
17
18
sage: from sage.categories.action import Action
19
sage: class P: pass
20
sage: A = Action(P(),P())
21
sage: import gc
22
sage: _ = gc.collect()
23
sage: A
24
Traceback (most recent call last):
25
...
26
RuntimeError: This action acted on a set that became garbage collected
27
28
To avoid garbage collection of the underlying set, it is sufficient to
29
create a strong reference to it before the action is created.
30
31
::
32
33
sage: _ = gc.collect()
34
sage: from sage.categories.action import Action
35
sage: class P: pass
36
sage: q = P()
37
sage: A = Action(P(),q)
38
sage: gc.collect()
39
0
40
sage: A
41
Left action by <__main__.P instance at ...> on <__main__.P instance at ...>
42
43
AUTHOR:
44
45
- Robert Bradshaw: initial version
46
"""
47
48
#*****************************************************************************
49
# Copyright (C) 2007 Robert Bradshaw <[email protected]>
50
#
51
# Distributed under the terms of the GNU General Public License (GPL)
52
#
53
# This code is distributed in the hope that it will be useful,
54
# but WITHOUT ANY WARRANTY; without even the implied warranty
55
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
56
#
57
# See the GNU General Public License for more details; the full text
58
# is available at:
59
#
60
# http://www.gnu.org/licenses/
61
#*****************************************************************************
62
63
from functor cimport Functor
64
from morphism cimport Morphism
65
from map cimport Map
66
67
import homset
68
import sage.structure.element
69
from weakref import ref
70
71
include "sage/ext/stdsage.pxi"
72
73
cdef inline category(x):
74
try:
75
return x.category()
76
except AttributeError:
77
import sage.categories.all
78
return sage.categories.all.Objects()
79
80
cdef class Action(Functor):
81
82
def __init__(self, G, S, bint is_left = 1, op=None):
83
from groupoid import Groupoid
84
Functor.__init__(self, Groupoid(G), category(S))
85
self.G = G
86
self.US = ref(S)
87
self._is_left = is_left
88
self.op = op
89
90
def _apply_functor(self, x):
91
return self(x)
92
93
def __call__(self, *args):
94
if len(args) == 1:
95
g = args[0]
96
if g in self.G:
97
return ActionEndomorphism(self, self.G(g))
98
elif g == self.G:
99
return self.underlying_set()
100
else:
101
raise TypeError, "%s not an element of %s"%(g, self.G)
102
elif len(args) == 2:
103
if self._is_left:
104
return self._call_(self.G(args[0]), self.underlying_set()(args[1]))
105
else:
106
return self._call_(self.underlying_set()(args[0]), self.G(args[1]))
107
108
cpdef _call_(self, a, b):
109
raise NotImplementedError, "Action not implemented."
110
111
def act(self, g, a):
112
"""
113
This is a consistent interface for acting on a by g,
114
regardless of whether it's a left or right action.
115
"""
116
if self._is_left:
117
return self._call_(g, a)
118
else:
119
return self._call_(a, g)
120
121
def __invert__(self):
122
return InverseAction(self)
123
124
def is_left(self):
125
return self._is_left
126
127
def __repr__(self):
128
side = "Left" if self._is_left else "Right"
129
return "%s %s by %r on %r"%(side, self._repr_name_(), self.G,
130
self.underlying_set())
131
132
def _repr_name_(self):
133
return "action"
134
135
def actor(self):
136
return self.G
137
138
cdef underlying_set(self):
139
"""
140
The set on which the actor acts (it is not necessarily the codomain of
141
the action).
142
143
NOTE:
144
145
Since this is a cdef'ed method, we can only provide an indirect doctest.
146
147
EXAMPLES::
148
149
sage: P = QQ['x']
150
sage: R = (ZZ['x'])['y']
151
sage: A = R.get_action(P,operator.mul,True)
152
sage: A # indirect doctest
153
Right scalar multiplication by Univariate Polynomial Ring in x over
154
Rational Field on Univariate Polynomial Ring in y over Univariate
155
Polynomial Ring in x over Integer Ring
156
157
In this example, the underlying set is the ring ``R``. This is the same
158
as the left domain, which is different from the codomain of the action::
159
160
sage: A.codomain()
161
Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Rational Field
162
sage: A.codomain() == R
163
False
164
sage: A.left_domain() is R
165
True
166
167
By :trac:`715`, there is only a weak reference to the underlying set.
168
Hence, the underlying set may be garbage collected, even when the
169
action is still alive. This may result in a runtime error, as follows::
170
171
sage: from sage.categories.action import Action
172
sage: class P: pass
173
sage: p = P()
174
sage: q = P()
175
sage: A = Action(p,q)
176
sage: A
177
Left action by <__main__.P instance at ...> on <__main__.P instance at ...>
178
sage: del q
179
sage: import gc
180
sage: _ = gc.collect()
181
sage: A
182
Traceback (most recent call last):
183
...
184
RuntimeError: This action acted on a set that became garbage collected
185
186
"""
187
S = self.US()
188
if S is None:
189
raise RuntimeError, "This action acted on a set that became garbage collected"
190
return S
191
192
def codomain(self):
193
return self.underlying_set()
194
195
def domain(self):
196
return self.underlying_set()
197
198
def left_domain(self):
199
if self._is_left:
200
return self.G
201
else:
202
return self.domain()
203
204
def right_domain(self):
205
if self._is_left:
206
return self.domain()
207
else:
208
return self.G
209
210
def operation(self):
211
return self.op
212
213
214
cdef class InverseAction(Action):
215
"""
216
An action that acts as the inverse of the given action.
217
218
TESTS:
219
220
This illustrates a shortcoming in the current coercion model.
221
See the comments in _call_ below::
222
223
sage: x = polygen(QQ,'x')
224
sage: a = 2*x^2+2; a
225
2*x^2 + 2
226
sage: a / 2
227
x^2 + 1
228
sage: a /= 2
229
sage: a
230
x^2 + 1
231
"""
232
def __init__(self, Action action):
233
G = action.G
234
try:
235
from sage.groups.group import is_Group
236
# We must be in the case that parent(~a) == parent(a)
237
# so we can invert in call_c code below.
238
if (is_Group(G) and G.is_multiplicative()) or G.is_field():
239
Action.__init__(self, G, action.underlying_set(), action._is_left)
240
self._action = action
241
return
242
else:
243
K = G._pseudo_fraction_field()
244
Action.__init__(self, K, action.underlying_set(), action._is_left)
245
self._action = action
246
return
247
except (AttributeError, NotImplementedError):
248
pass
249
raise TypeError, "No inverse defined for %r." % action
250
251
cpdef _call_(self, a, b):
252
if self._action._is_left:
253
if self.S_precomposition is not None:
254
b = self.S_precomposition(b)
255
return self._action._call_(~a, b)
256
else:
257
if self.S_precomposition is not None:
258
a = self.S_precomposition(a)
259
return self._action._call_(a, ~b)
260
261
def codomain(self):
262
return self._action.codomain()
263
264
def __invert__(self):
265
return self._action
266
267
def _repr_name_(self):
268
return "inverse action"
269
270
cdef class PrecomposedAction(Action):
271
272
def __init__(self, Action action, Map left_precomposition, Map right_precomposition):
273
left = action.left_domain()
274
right = action.right_domain()
275
if left_precomposition is not None:
276
if left_precomposition._codomain is not left:
277
left_precomposition = homset.Hom(left_precomposition._codomain, left).natural_map() * left_precomposition
278
left = left_precomposition._domain
279
if right_precomposition is not None:
280
if right_precomposition._codomain is not right:
281
right_precomposition = homset.Hom(right_precomposition._codomain, right).natural_map() * right_precomposition
282
right = right_precomposition._domain
283
if action._is_left:
284
Action.__init__(self, left, action.underlying_set(), 1)
285
else:
286
Action.__init__(self, right, action.underlying_set(), 0)
287
self._action = action
288
self.left_precomposition = left_precomposition
289
self.right_precomposition = right_precomposition
290
291
cpdef _call_(self, a, b):
292
if self.left_precomposition is not None:
293
a = self.left_precomposition._call_(a)
294
if self.right_precomposition is not None:
295
b = self.right_precomposition._call_(b)
296
return self._action._call_(a, b)
297
298
def domain(self):
299
if self._is_left and self.right_precomposition is not None:
300
return self.right_precomposition.domain()
301
elif not self._is_left and self.left_precomposition is not None:
302
return self.left_precomposition.domain()
303
else:
304
return self._action.domain()
305
306
def codomain(self):
307
return self._action.codomain()
308
309
def __invert__(self):
310
return PrecomposedAction(~self._action, self.left_precomposition, self.right_precomposition)
311
312
def __repr__(self):
313
s = repr(self._action)
314
if self.left_precomposition is not None:
315
s += "\nwith precomposition on left by %r" % self.left_precomposition
316
if self.right_precomposition is not None:
317
s += "\nwith precomposition on right by %r" % self.right_precomposition
318
return s
319
320
321
cdef class ActionEndomorphism(Morphism):
322
323
def __init__(self, Action action, g):
324
Morphism.__init__(self, homset.Hom(action.underlying_set(),
325
action.underlying_set()))
326
self._action = action
327
self._g = g
328
329
cpdef Element _call_(self, x):
330
if self._action._is_left:
331
return self._action._call_(self._g, x)
332
else:
333
return self._action._call_(x, self._g)
334
335
def _repr_(self):
336
return "Action of %s on %s under %s."%(self._g,
337
self._action.underlying_set(), self._action)
338
339
def __mul__(left, right):
340
cdef ActionEndomorphism left_c, right_c
341
if PY_TYPE_CHECK(left, ActionEndomorphism) and PY_TYPE_CHECK(right, ActionEndomorphism):
342
left_c = left
343
right_c = right
344
if left_c._action is right_c._action:
345
if left_c._action._is_left:
346
return ActionEndomorphism(left_c._action, left_c._g * right_c._g)
347
else:
348
return ActionEndomorphism(left_c._action, right_c._g * left_c._g)
349
return Morphism.__mul__(left, right)
350
351
def __invert__(self):
352
inv_g = ~self._g
353
if sage.structure.element.parent(inv_g) is sage.structure.element.parent(self._g):
354
return ActionEndomorphism(self._action, inv_g)
355
else:
356
return (~self._action)(self._g)
357
358
359
360