Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/structure/element_wrapper.pyx
8814 views
1
"""
2
Element Wrapper
3
4
Wrapping Sage or Python objects as Sage elements.
5
6
AUTHORS:
7
8
- Nicolas Thiery (2008-2010): Initial version
9
- Travis Scrimshaw (2013-05-04): Cythonized version
10
"""
11
#*****************************************************************************
12
# Copyright (C) 2008-2010 Nicolas M. Thiery <nthiery at users.sf.net>
13
#
14
# Distributed under the terms of the GNU General Public License (GPL)
15
# http://www.gnu.org/licenses/
16
#******************************************************************************
17
18
include "../ext/python.pxi"
19
from cpython cimport bool
20
21
from sage.structure.parent cimport Parent
22
from sage.structure.element cimport Element
23
from copy import copy
24
25
cdef class ElementWrapper(Element):
26
r"""
27
A class for wrapping Sage or Python objects as Sage elements.
28
29
EXAMPLES::
30
31
sage: from sage.structure.element_wrapper import DummyParent
32
sage: parent = DummyParent("A parent")
33
sage: o = ElementWrapper(parent, "bla"); o
34
'bla'
35
sage: isinstance(o, sage.structure.element.Element)
36
True
37
sage: o.parent()
38
A parent
39
sage: o.value
40
'bla'
41
42
Note that ``o`` is not *an instance of* ``str``, but rather
43
*contains a* ``str``. Therefore, ``o`` does not inherit the string
44
methods. On the other hand, it is provided with reasonable default
45
implementations for equality testing, hashing, etc.
46
47
The typical use case of ``ElementWrapper`` is for trivially
48
constructing new element classes from pre-existing Sage or Python
49
classes, with a containment relation. Here we construct the
50
tropical monoid of integers endowed with ``min`` as
51
multiplication. There, it is desirable *not* to inherit the
52
``factor`` method from ``Integer``::
53
54
sage: class MinMonoid(Parent):
55
....: def _repr_(self):
56
....: return "The min monoid"
57
....:
58
sage: M = MinMonoid()
59
sage: class MinMonoidElement(ElementWrapper):
60
....: wrapped_class = Integer
61
....:
62
....: def __mul__(self, other):
63
....: return MinMonoidElement(self.parent(), min(self.value, other.value))
64
sage: x = MinMonoidElement(M, 5); x
65
5
66
sage: x.parent()
67
The min monoid
68
sage: x.value
69
5
70
sage: y = MinMonoidElement(M, 3)
71
sage: x * y
72
3
73
74
This example was voluntarily kept to a bare minimum. See the
75
examples in the categories (e.g. ``Semigroups().example()``) for
76
several full featured applications.
77
78
.. WARNING::
79
80
Versions before :trac:`14519` had parent as the second argument and
81
the value as the first.
82
"""
83
cdef public object value
84
85
def __init__(self, parent, value):
86
"""
87
EXAMPLES::
88
89
sage: from sage.structure.element_wrapper import DummyParent
90
sage: a = ElementWrapper(DummyParent("A parent"), 1)
91
92
TESTS::
93
94
sage: TestSuite(a).run(skip = "_test_category")
95
96
sage: a = ElementWrapper(1, DummyParent("A parent"))
97
doctest:...: DeprecationWarning: the first argument must be a parent
98
See http://trac.sagemath.org/14519 for details.
99
100
.. NOTE::
101
102
:class:`ElementWrapper` is not intended to be used directly,
103
hence the failing category test.
104
"""
105
#assert isinstance(value, self.wrapped_class)
106
if not isinstance(parent, Parent):
107
from sage.misc.superseded import deprecation
108
deprecation(14519, 'the first argument must be a parent')
109
value, parent = parent, value
110
Element.__init__(self, parent=parent)
111
self.value = value
112
113
# When self is an extension type without a __dict__ attribute,
114
# this prevents self.__dict__ to return whatever crap obtained by
115
# lookup through the categories ...
116
__dict__ = {}
117
118
def __getstate__(self):
119
"""
120
Return a tuple describing the state of your object.
121
122
This emulates :meth:`Element.__getstate__`, playing as if
123
``self.value`` was in the dictionary of ``self`` as it used to
124
be before :trac:`14519`.
125
126
EXAMPLES::
127
128
sage: from sage.structure.element_wrapper import DummyParent
129
sage: P = DummyParent("A parent")
130
sage: a = ElementWrapper(P, 1)
131
sage: a.__getstate__()
132
(A parent, {'value': 1})
133
sage: class A(ElementWrapper):
134
....: pass
135
sage: a = A(P, 1)
136
sage: a.x = 2
137
sage: a.__getstate__() == (P, {'value': 1, 'x': 2})
138
True
139
"""
140
d = self.__dict__.copy()
141
d['value'] = self.value
142
return (self._parent, d)
143
144
def __setstate__(self, state):
145
r"""
146
Initialize the state of the object from data saved in a pickle.
147
148
This emulates :meth:`Element.__setstate__`, playing as if
149
``self.value`` was to be put in the dictionary of ``self`` as
150
it used to be before :trac:`14519`.
151
152
EXAMPLES::
153
154
sage: from sage.structure.element_wrapper import DummyParent
155
sage: class A(ElementWrapper):
156
....: pass
157
sage: a = A(DummyParent("A parent"), 1)
158
sage: a.__setstate__((DummyParent("Another parent"), {'value':0,'x':3}))
159
sage: a.parent()
160
Another parent
161
sage: a.value
162
0
163
sage: a.x
164
3
165
166
TESTS::
167
168
sage: a = A(DummyParent("A parent"), 1)
169
sage: import __main__; __main__.A = A # Fake A being defined in a python module
170
sage: a.x = 2
171
sage: a == loads(dumps(a))
172
True
173
sage: a = ElementWrapper(DummyParent("A parent"), 1)
174
sage: a == loads(dumps(a))
175
True
176
177
Checking that we can still load pickle from before :trac:`14519`::
178
179
sage: f = loads('x\x9c\x85\x8d\xbb\n\xc2@\x14D\xf1\x11\x8d\xebO\xd8\xda,\xf8\t\x82\xf6\x12\x08\x96a\x8dC\x08f\xd7\xdc\xbb{\x15\x8b\x80\x16\xd1\xdf6\x10\xad,,\xcf0s\xe6>\xcc\xbd)\xa0}`\xc9\x8304*X\xb8\x90]\xd9\xd45Xm{\xde\x7f\x90\x06\xcb\x07\xfd\x8c\xc4\x95$\xc8\x185\xc3wm\x13\xca\xb3S\xe2\x18G\xc9\xa1h\xf4\xefe#\xd6\xdev\x86\xbbL\xd18\x8d\xd7\x8b\x1e(j\x9b\x17M\x12\x9a6\x14\xa7\xd1\xc5T\x02\x9a\xf56.]\xe1u\xe9\x02\x8a\xce`\xcd\t\xd9\x17H\xa5\x83U\x9b\xd0\xdc?\x0f\xfa\rl4S\xbc')
180
sage: f == ElementWrapper(DummyParent("A Parent"), 1)
181
True
182
"""
183
# Make sure the first part of the state is the parent
184
if not isinstance(state[0], Parent):
185
state[0], state[1] = state[1], state[0]
186
self._set_parent(state[0])
187
d = state[1].copy()
188
self.value = d.pop('value')
189
if d:
190
self.__dict__ = d
191
192
def _repr_(self):
193
"""
194
EXAMPLES::
195
196
sage: from sage.structure.element_wrapper import DummyParent
197
sage: ElementWrapper(DummyParent("A parent"), 1)
198
1
199
"""
200
return repr(self.value)
201
202
def _latex_(self):
203
r"""
204
EXAMPLES::
205
206
sage: from sage.structure.element_wrapper import DummyParent
207
sage: ElementWrapper(DummyParent("A parent"), 1)._latex_()
208
1
209
sage: ElementWrapper(DummyParent("A parent"), 3/5)._latex_()
210
\frac{3}{5}
211
"""
212
from sage.misc.latex import latex
213
return latex(self.value)
214
215
def __hash__(self):
216
"""
217
Return the same hash as for the wrapped element.
218
219
EXAMPLES::
220
221
sage: from sage.structure.element_wrapper import DummyParent
222
sage: parent1 = DummyParent("A parent")
223
sage: parent2 = DummyParent("Another parent")
224
sage: hash(ElementWrapper(parent1, 1))
225
1
226
sage: hash(ElementWrapper(parent2, 1))
227
1
228
229
.. TODO::
230
231
Should this take the parent and/or the class into account?
232
"""
233
return hash(self.value)
234
235
def __richcmp__(left, right, int op):
236
"""
237
Return ``True`` if ``left`` compares with ``right`` based on ``op``.
238
239
Default implementation of ``self == other``: two elements are
240
equal if they have the same class, same parent, and same value.
241
242
Default implementation of ``self < other``: two elements are
243
always incomparable.
244
245
.. NOTE::
246
247
Another option would be to not define ``__lt__``, but
248
given the current implementation of SageObject, sorted(...)
249
would break.
250
251
TESTS:
252
253
Testing equality::
254
255
sage: from sage.structure.element_wrapper import DummyParent
256
sage: parent1 = DummyParent("A parent")
257
sage: parent2 = DummyParent("Another parent")
258
sage: parent1 == parent2
259
False
260
sage: l11 = ElementWrapper(parent1, 1)
261
sage: l12 = ElementWrapper(parent1, 2)
262
sage: l21 = ElementWrapper(parent2, 1)
263
sage: l22 = ElementWrapper(parent2, 2)
264
sage: l11 == l11
265
True
266
sage: l11 == l12
267
False
268
sage: l11 == l21
269
False
270
271
Testing inequality::
272
273
sage: from sage.structure.element_wrapper import DummyParent
274
sage: parent1 = DummyParent("A parent")
275
sage: parent2 = DummyParent("Another parent")
276
sage: parent1 == parent2
277
False
278
sage: l11 = ElementWrapper(parent1, 1)
279
sage: l12 = ElementWrapper(parent1, 2)
280
sage: l21 = ElementWrapper(parent2, 1)
281
sage: l22 = ElementWrapper(parent2, 2)
282
sage: l11 != l11
283
False
284
sage: l11 != l12
285
True
286
sage: l11 != l21
287
True
288
289
Testing less than::
290
291
sage: from sage.structure.element_wrapper import DummyParent
292
sage: parent = DummyParent("A parent")
293
sage: x = ElementWrapper(parent, 1)
294
sage: y = ElementWrapper(parent, 2)
295
sage: x.__lt__(x), x.__lt__(y), y.__lt__(x), x.__lt__(1)
296
(False, False, False, False)
297
sage: x < x, x < y, y < x, x < 1
298
(False, False, False, False)
299
sage: sorted([x,y])
300
[1, 2]
301
sage: sorted([y,x])
302
[2, 1]
303
"""
304
cdef ElementWrapper self
305
self = left
306
if self.__class__ != right.__class__ \
307
or self._parent != (<ElementWrapper>right)._parent:
308
return op == Py_NE
309
if op == Py_EQ or op == Py_LE or op == Py_GE:
310
return self.value == (<ElementWrapper>right).value
311
if op == Py_NE:
312
return self.value != (<ElementWrapper>right).value
313
return False
314
315
cpdef bool _lt_by_value(self, other):
316
"""
317
Return whether ``self`` is strictly smaller than ``other``.
318
319
With this implementation 'by value', they are always
320
incomparable unless ``self`` and ``other`` have the same
321
class, parent, and ``self.value < other.value``.
322
323
EXAMPLES::
324
325
sage: from sage.structure.element_wrapper import DummyParent
326
sage: class MyElement(ElementWrapper):
327
....: __lt__ = ElementWrapper._lt_by_value
328
....:
329
sage: parent1 = DummyParent("A parent")
330
sage: parent2 = DummyParent("Another parent")
331
sage: l11 = MyElement(parent1, 1)
332
sage: l12 = MyElement(parent1, 2)
333
sage: l21 = MyElement(parent2, 1)
334
sage: l22 = MyElement(parent2, 2)
335
sage: l11 < l11
336
False
337
sage: l11 < l12, l12 < l11 # values differ
338
(True, False)
339
sage: l11 < l21 # parents differ
340
False
341
sage: l11 < 1 # class differ
342
False
343
sage: 1 < l11 # random, since it depends on what the Integer 1 decides to do, which may just involve memory locations
344
False
345
"""
346
return self.__class__ is other.__class__ \
347
and self._parent is other.parent() \
348
and self.value < (<ElementWrapper>other).value
349
350
cpdef int _cmp_by_value(self, other):
351
"""
352
Implementation of ``cmp`` by comparing first values, then
353
parents, then class. This behavior (which implies a total
354
order) is not always desirable and hard to override. Hence
355
derived subclasses that want to take advantage of this
356
feature need to explicitely set :meth:`.__cmp__`.
357
358
EXAMPLES::
359
360
sage: class MyElement(ElementWrapper):
361
....: __cmp__ = ElementWrapper._cmp_by_value
362
....:
363
sage: from sage.structure.element_wrapper import DummyParent
364
sage: parent1 = DummyParent("A parent")
365
sage: parent2 = DummyParent("Another parent")
366
sage: parent1 == parent2
367
False
368
sage: l11 = MyElement(parent1, 1)
369
sage: l12 = MyElement(parent1, 2)
370
sage: l21 = MyElement(parent2, 1)
371
sage: l22 = MyElement(parent2, 2)
372
sage: cmp(l11, l11)
373
0
374
sage: cmp(l11, l12), cmp(l12, l11) # values differ
375
(-1, 1)
376
sage: cmp(l11, l21) in [-1, 1] # parents differ
377
True
378
sage: cmp(l21, l11) == -cmp(l11, l21)
379
True
380
sage: cmp(l11, 1) in [-1,1] # class differ
381
True
382
"""
383
if self.__class__ != other.__class__:
384
return cmp(self.__class__, other.__class__)
385
if self.parent() != other.parent():
386
return cmp(self.parent(), other.parent())
387
return cmp(self.value, other.value)
388
389
def __copy__(self):
390
"""
391
Copy ``self`` and in particular its ``value`` attribute.
392
393
EXAMPLES::
394
395
sage: from sage.structure.element_wrapper import DummyParent
396
sage: parent = DummyParent("A parent")
397
sage: o1 = ElementWrapper(parent, [1]); o1
398
[1]
399
sage: o2 = copy(o1); o2
400
[1]
401
sage: o1 is o2, o1.value is o2.value
402
(False, False)
403
sage: o2.value[0] = 3; o2
404
[3]
405
sage: o1
406
[1]
407
sage: class bla(ElementWrapper): pass
408
sage: o3 = bla(parent, [1])
409
sage: o4 = copy(o3)
410
sage: o3.value[0] = 3; o4
411
[1]
412
sage: o3.__class__
413
<class '__main__.bla'>
414
sage: o4.__class__
415
<class '__main__.bla'>
416
"""
417
# Note : copy(super(ElementWrapper, self)) does not work.
418
res = super(ElementWrapper, self).__copy__()
419
res.value = copy(self.value)
420
return res
421
422
from sage.structure.parent import Parent
423
from sage.structure.unique_representation import UniqueRepresentation
424
class DummyParent(UniqueRepresentation, Parent):
425
"""
426
A class for creating dummy parents for testing ElementWrapper
427
"""
428
def __init__(self, name):
429
"""
430
EXAMPLES::
431
432
sage: from sage.structure.element_wrapper import DummyParent
433
sage: parent = DummyParent("A Parent")
434
sage: TestSuite(parent).run(skip = ["_test_an_element",\
435
"_test_category",\
436
"_test_elements",\
437
"_test_elements_eq_reflexive",\
438
"_test_elements_eq_symmetric",\
439
"_test_elements_eq_transitive",\
440
"_test_elements_neq",\
441
"_test_some_elements"])
442
"""
443
self.name = name
444
445
def _repr_(self):
446
"""
447
EXAMPLES::
448
449
sage: from sage.structure.element_wrapper import DummyParent
450
sage: DummyParent("A Parent") # indirect doctest
451
A Parent
452
"""
453
return self.name
454
455
class ElementWrapperTester(ElementWrapper):
456
"""
457
Test class for the default :meth:`.__copy` method of subclasses of
458
:class:`ElementWrapper`.
459
460
TESTS::
461
462
sage: from sage.structure.element_wrapper import ElementWrapperTester
463
sage: x = ElementWrapperTester()
464
sage: x.append(2); y = copy(x); y.append(42)
465
sage: type(y)
466
<class 'sage.structure.element_wrapper.ElementWrapperTester'>
467
sage: x, y
468
([n=1, value=[2]], [n=2, value=[2, 42]])
469
sage: x.append(21); x.append(7)
470
sage: x, y
471
([n=3, value=[2, 21, 7]], [n=2, value=[2, 42]])
472
sage: x.value, y.value
473
([2, 21, 7], [2, 42])
474
sage: x.__dict__, y.__dict__
475
({'n': 3}, {'n': 2})
476
"""
477
def __init__(self):
478
"""
479
TESTS::
480
481
sage: from sage.structure.element_wrapper import ElementWrapperTester
482
sage: x = ElementWrapperTester(); x
483
[n=0, value=[]]
484
"""
485
from sage.categories.sets_cat import Sets
486
super(ElementWrapperTester, self).__init__(Sets().example("facade"), [])
487
self.n = 0
488
489
def append(self, x):
490
"""
491
TESTS::
492
493
sage: from sage.structure.element_wrapper import ElementWrapperTester
494
sage: x = ElementWrapperTester()
495
sage: x.append(2); x
496
[n=1, value=[2]]
497
"""
498
self.n +=1
499
self.value.append(x)
500
501
def _repr_(self):
502
"""
503
TESTS::
504
505
sage: from sage.structure.element_wrapper import ElementWrapperTester
506
sage: x = ElementWrapperTester
507
sage: x = ElementWrapperTester(); x
508
[n=0, value=[]]
509
sage: x.value = [2,32]; x # indirect doctest
510
[n=0, value=[2, 32]]
511
"""
512
return "[n=%s, value=%s]"%(self.n, self.value)
513
514
515