Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/misc/cachefunc.pyx
8814 views
1
r"""
2
Cached Functions and Methods
3
4
AUTHORS:
5
6
- William Stein (inspired by conversation with Justin Walker).
7
- Mike Hansen (added doctests and made it work with class methods).
8
- Willem Jan Palenstijn (add CachedMethodCaller for binding cached
9
methods to instances).
10
- Tom Boothby (added DiskCachedFunction).
11
- Simon King (improved performance, more doctests, cython version,
12
CachedMethodCallerNoArgs, weak cached function, cached special methods).
13
14
EXAMPLES:
15
16
By trac ticket #11115, cached functions and methods are now also
17
available in Cython code. The following examples cover various ways
18
of usage.
19
20
Python functions::
21
22
sage: @cached_function
23
... def test_pfunc(x):
24
... '''
25
... Some documentation
26
... '''
27
... return -x
28
...
29
sage: test_pfunc(5) is test_pfunc(5)
30
True
31
32
In some cases, one would only want to keep the result in cache as long
33
as there is any other reference to the result. By :trac:`12215`, this is
34
enabled for :class:`~sage.structure.unique_representation.UniqueRepresentation`,
35
which is used to create unique parents: If an algebraic structure, such
36
as a finite field, is only temporarily used, then it will not stay in
37
cache forever. That behaviour is implemented using ``weak_cached_function``,
38
that behaves the same as ``cached_function``, except that it uses a
39
:class:`~sage.misc.weak_dict.WeakValueDictionary` for storing the results.
40
::
41
42
sage: from sage.misc.cachefunc import weak_cached_function
43
sage: class A: pass
44
sage: @weak_cached_function
45
... def f():
46
... print "doing a computation"
47
... return A()
48
...
49
sage: a = f()
50
doing a computation
51
52
The result is cached::
53
54
sage: b = f()
55
sage: a is b
56
True
57
58
However, if there are no strong references left, the result
59
may be garbage collected, and thus a new computation would
60
take place::
61
62
sage: del a
63
sage: del b
64
sage: import gc
65
sage: n = gc.collect()
66
sage: a = f()
67
doing a computation
68
69
Cython cdef functions do not allow arbitrary decorators.
70
However, one can wrap a Cython function and turn it into
71
a cached function, by :trac:`11115`. We need to provide
72
the name that the wrapped method or function should have,
73
since otherwise the name of the original function would
74
be used::
75
76
sage: cython('''cpdef test_funct(x): return -x''')
77
sage: wrapped_funct = cached_function(test_funct, name='wrapped_funct')
78
sage: wrapped_funct
79
Cached version of <built-in function test_funct>
80
sage: wrapped_funct.__name__
81
'wrapped_funct'
82
sage: wrapped_funct(5)
83
-5
84
sage: wrapped_funct(5) is wrapped_funct(5)
85
True
86
87
We can proceed similarly for cached methods of Cython classes,
88
provided that they allow attribute assignment or have a public
89
attribute ``__cached_methods`` of type ``<dict>``. Since
90
:trac:`11115`, this is the case for all classes inheriting from
91
:class:`~sage.structure.parent.Parent`. See below for a more explicit
92
example. By :trac:`12951`, cached methods of extension classes can
93
be defined by simply using the decorater. However, an indirect
94
approach is still needed for cpdef methods::
95
96
sage: cython_code = ['cpdef test_meth(self,x):',
97
... ' "some doc for a wrapped cython method"',
98
... ' return -x',
99
... 'from sage.all import cached_method',
100
... 'from sage.structure.parent cimport Parent',
101
... 'cdef class MyClass(Parent):',
102
... ' @cached_method',
103
... ' def direct_method(self, x):',
104
... ' "Some doc for direct method"',
105
... ' return 2*x',
106
... ' wrapped_method = cached_method(test_meth,name="wrapped_method")']
107
sage: cython(os.linesep.join(cython_code))
108
sage: O = MyClass()
109
sage: O.direct_method
110
Cached version of <method 'direct_method' of '...MyClass' objects>
111
sage: O.wrapped_method
112
Cached version of <built-in function test_meth>
113
sage: O.wrapped_method.__name__
114
'wrapped_method'
115
sage: O.wrapped_method(5)
116
-5
117
sage: O.wrapped_method(5) is O.wrapped_method(5)
118
True
119
sage: O.direct_method(5)
120
10
121
sage: O.direct_method(5) is O.direct_method(5)
122
True
123
124
In some cases, one would only want to keep the result in cache as long
125
as there is any other reference to the result. By :trac:`12215`, this is
126
enabled for :class:`~sage.structure.unique_representation.UniqueRepresentation`,
127
which is used to create unique parents: If an algebraic structure, such
128
as a finite field, is only temporarily used, then it will not stay in
129
cache forever. That behaviour is implemented using ``weak_cached_function``,
130
that behaves the same as ``cached_function``, except that it uses a
131
:class:`~sage.misc.weak_dict.WeakValueDictionary` for storing the results.
132
::
133
134
sage: from sage.misc.cachefunc import weak_cached_function
135
sage: class A: pass
136
sage: @weak_cached_function
137
... def f():
138
... print "doing a computation"
139
... return A()
140
...
141
sage: a = f()
142
doing a computation
143
144
The result is cached::
145
146
sage: b = f()
147
sage: a is b
148
True
149
150
However, if there are no strong references left, the result
151
may be garbage collected, and thus a new computation would
152
take place::
153
154
sage: del a
155
sage: del b
156
sage: import gc
157
sage: n = gc.collect()
158
sage: a = f()
159
doing a computation
160
161
By :trac:`11115`, even if a parent does not allow attribute
162
assignment, it can inherit a cached method from the parent class of a
163
category (previously, the cache would have been broken)::
164
165
sage: cython_code = ["from sage.all import cached_method, cached_in_parent_method, Category, Objects",
166
... "class MyCategory(Category):",
167
... " @cached_method",
168
... " def super_categories(self):",
169
... " return [Objects()]",
170
... " class ElementMethods:",
171
... " @cached_method",
172
... " def element_cache_test(self):",
173
... " return -self",
174
... " @cached_in_parent_method",
175
... " def element_via_parent_test(self):",
176
... " return -self",
177
... " class ParentMethods:",
178
... " @cached_method",
179
... " def one(self):",
180
... " return self.element_class(self,1)",
181
... " @cached_method",
182
... " def invert(self, x):",
183
... " return -x"]
184
sage: cython('\n'.join(cython_code))
185
sage: C = MyCategory()
186
187
In order to keep the memory footprint of elements small, it was
188
decided to not support the same freedom of using cached methods
189
for elements: If an instance of a class derived from
190
:class:`~sage.structure.element.Element` does not allow attribute
191
assignment, then a cached method inherited from the category of
192
its parent will break, as in the class ``MyBrokenElement`` below.
193
194
However, there is a class :class:`~sage.structure.element.ElementWithCachedMethod`
195
that has generally a slower attribute access, but fully supports
196
cached methods. We remark, however, that cached methods are
197
*much* faster if attribute access works. So, we expect that
198
:class:`~sage.structure.element.ElementWithCachedMethod` will
199
hardly by used.
200
::
201
202
sage: cython_code = ["from sage.structure.element cimport Element, ElementWithCachedMethod",
203
... "cdef class MyBrokenElement(Element):",
204
... " cdef public object x",
205
... " def __init__(self,P,x):",
206
... " self.x=x",
207
... " Element.__init__(self,P)",
208
... " def __neg__(self):",
209
... " return MyBrokenElement(self.parent(),-self.x)",
210
... " def _repr_(self):",
211
... " return '<%s>'%self.x",
212
... " def __hash__(self):",
213
... " return hash(self.x)",
214
... " def __cmp__(left, right):",
215
... " return (<Element>left)._cmp(right)",
216
... " def __richcmp__(left, right, op):",
217
... " return (<Element>left)._richcmp(right,op)",
218
... " cdef int _cmp_c_impl(left, Element right) except -2:",
219
... " return cmp(left.x,right.x)",
220
... " def raw_test(self):",
221
... " return -self",
222
... "cdef class MyElement(ElementWithCachedMethod):",
223
... " cdef public object x",
224
... " def __init__(self,P,x):",
225
... " self.x=x",
226
... " ElementWithCachedMethod.__init__(self,P)",
227
... " def __neg__(self):",
228
... " return MyElement(self.parent(),-self.x)",
229
... " def _repr_(self):",
230
... " return '<%s>'%self.x",
231
... " def __hash__(self):",
232
... " return hash(self.x)",
233
... " def __cmp__(left, right):",
234
... " return (<Element>left)._cmp(right)",
235
... " def __richcmp__(left, right, op):",
236
... " return (<Element>left)._richcmp(right,op)",
237
... " cdef int _cmp_c_impl(left, Element right) except -2:",
238
... " return cmp(left.x,right.x)",
239
... " def raw_test(self):",
240
... " return -self",
241
... "from sage.structure.parent cimport Parent",
242
... "cdef class MyParent(Parent):",
243
... " Element = MyElement"]
244
sage: cython('\n'.join(cython_code))
245
sage: P = MyParent(category=C)
246
sage: ebroken = MyBrokenElement(P,5)
247
sage: e = MyElement(P,5)
248
249
The cached methods inherited by the parent works::
250
251
sage: P.one()
252
<1>
253
sage: P.one() is P.one()
254
True
255
sage: P.invert(e)
256
<-5>
257
sage: P.invert(e) is P.invert(e)
258
True
259
260
The cached methods inherited by ``MyElement`` works::
261
262
sage: e.element_cache_test()
263
<-5>
264
sage: e.element_cache_test() is e.element_cache_test()
265
True
266
sage: e.element_via_parent_test()
267
<-5>
268
sage: e.element_via_parent_test() is e.element_via_parent_test()
269
True
270
271
The other element class can only inherit a ``cached_in_parent_method``, since
272
the cache is stored in the parent. In fact, equal elements share the cache,
273
even if they are of different types::
274
275
sage: e == ebroken
276
True
277
sage: type(e) == type(ebroken)
278
False
279
sage: ebroken.element_via_parent_test() is e.element_via_parent_test()
280
True
281
282
However, the cache of the other inherited method breaks, although the method
283
as such works::
284
285
sage: ebroken.element_cache_test()
286
<-5>
287
sage: ebroken.element_cache_test() is ebroken.element_cache_test()
288
False
289
290
The cache can be emptied::
291
292
sage: a = test_pfunc(5)
293
sage: test_pfunc.clear_cache()
294
sage: a is test_pfunc(5)
295
False
296
sage: a = P.one()
297
sage: P.one.clear_cache()
298
sage: a is P.one()
299
False
300
301
Since ``e`` and ``ebroken`` share the cache, when we empty it for one element
302
it is empty for the other as well::
303
304
sage: b = ebroken.element_via_parent_test()
305
sage: e.element_via_parent_test.clear_cache()
306
sage: b is ebroken.element_via_parent_test()
307
False
308
309
Introspection works::
310
311
sage: from sage.misc.edit_module import file_and_line
312
sage: from sage.misc.sageinspect import sage_getdoc, sage_getfile, sage_getsource
313
sage: print sage_getdoc(test_pfunc)
314
Some documentation
315
sage: print sage_getdoc(O.wrapped_method)
316
some doc for a wrapped cython method
317
<BLANKLINE>
318
sage: print sage_getdoc(O.direct_method)
319
Some doc for direct method
320
<BLANKLINE>
321
sage: print sage_getsource(O.wrapped_method)
322
cpdef test_meth(self,x):
323
"some doc for a wrapped cython method"
324
return -x
325
sage: print sage_getsource(O.direct_method)
326
def direct_method(self, x):
327
"Some doc for direct method"
328
return 2*x
329
330
It is a very common special case to cache a method that has no
331
arguments. In that special case, the time needed to access the cache
332
can be drastically reduced by using a special implementation. The
333
cached method decorator automatically determines which implementation
334
ought to be chosen. A typical example is
335
:meth:`sage.rings.polynomial.multi_polynomial_ideal.MPolynomialIdeal.gens`
336
(no arguments) versus
337
:meth:`sage.rings.polynomial.multi_polynomial_ideal.MPolynomialIdeal.groebner_basis`
338
(several arguments)::
339
340
sage: P.<a,b,c,d> = QQ[]
341
sage: I = P*[a,b]
342
sage: I.gens()
343
[a, b]
344
sage: I.gens() is I.gens()
345
True
346
sage: I.groebner_basis()
347
[a, b]
348
sage: I.groebner_basis() is I.groebner_basis()
349
True
350
sage: type(I.gens)
351
<type 'sage.misc.cachefunc.CachedMethodCallerNoArgs'>
352
sage: type(I.groebner_basis)
353
<type 'sage.misc.cachefunc.CachedMethodCaller'>
354
355
By :trac:`12951`, the cached_method decorator is also supported on non-c(p)def
356
methods of extension classes, as long as they either support attribute assignment
357
or have a public attribute of type ``<dict>`` called ``__cached_methods``. The
358
latter is easy::
359
360
sage: cython_code = [
361
... "from sage.misc.cachefunc import cached_method",
362
... "cdef class MyClass:",
363
... " cdef public dict __cached_methods",
364
... " @cached_method",
365
... " def f(self, a,b):",
366
... " return a*b"]
367
sage: cython(os.linesep.join(cython_code))
368
sage: P = MyClass()
369
sage: P.f(2,3)
370
6
371
sage: P.f(2,3) is P.f(2,3)
372
True
373
374
Providing attribute access is a bit more tricky, since it is needed that
375
an attribute inherited by the instance from its class can be overridden
376
on the instance. That is why providing a ``__getattr__`` would not be
377
enough in the following example::
378
379
sage: cython_code = [
380
... "from sage.misc.cachefunc import cached_method",
381
... "cdef class MyOtherClass:",
382
... " cdef dict D",
383
... " def __init__(self):",
384
... " self.D = {}",
385
... " def __setattr__(self, n,v):",
386
... " self.D[n] = v",
387
... " def __getattribute__(self, n):",
388
... " try:",
389
... " return self.D[n]",
390
... " except KeyError:",
391
... " pass",
392
... " return getattr(type(self),n).__get__(self)",
393
... " @cached_method",
394
... " def f(self, a,b):",
395
... " return a+b"]
396
sage: cython(os.linesep.join(cython_code))
397
sage: Q = MyOtherClass()
398
sage: Q.f(2,3)
399
5
400
sage: Q.f(2,3) is Q.f(2,3)
401
True
402
403
Note that supporting attribute access is somehow faster than the
404
easier method::
405
406
sage: timeit("a = P.f(2,3)") # random
407
625 loops, best of 3: 1.3 µs per loop
408
sage: timeit("a = Q.f(2,3)") # random
409
625 loops, best of 3: 931 ns per loop
410
411
"""
412
########################################################################
413
# Copyright (C) 2008 William Stein <[email protected]>
414
# Mike Hansen <[email protected]>
415
# 2011 Simon King <[email protected]>
416
#
417
# Distributed under the terms of the GNU General Public License (GPL)
418
#
419
# http://www.gnu.org/licenses/
420
########################################################################
421
from function_mangling import ArgumentFixer
422
import os
423
from sage.misc.sageinspect import sage_getfile, sage_getsourcelines, sage_getargspec
424
425
import sage.misc.weak_dict
426
from sage.misc.weak_dict import WeakValueDictionary
427
428
cdef frozenset special_method_names = frozenset(['__abs__', '__add__',
429
'__and__', '__call__', '__cmp__', '__coerce__', '__complex__', '__contains__', '__del__',
430
'__delattr__', '__delete__', '__delitem__', '__delslice__', '__dir__', '__div__',
431
'__eq__', '__float__', '__floordiv__', '__format__', '__ge__', '__get__', '__getattr__',
432
'__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__hex__',
433
'__iadd__', '__iand__', '__idiv__', '__ifloordiv__', '__ilshift__', '__imod__', '__imul__',
434
'__index__', '__init__', '__instancecheck__', '__int__', '__invert__', '__ior__', '__ipow__',
435
'__irshift__', '__isub__', '__iter__', '__itruediv__', '__ixor__', '__le__', '__len__',
436
'__length_hint__', '__long__', '__lshift__', '__lt__', '__missing__', '__mod__', '__mul__',
437
'__ne__', '__neg__', '__new__', '__nonzero__', '__oct__', '__or__', '__pos__', '__pow__',
438
'__radd__', '__rand__', '__rdiv__', '__repr__', '__reversed__', '__rfloordiv__', '__rlshift__',
439
'__rmod__', '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__',
440
'__rtruediv__', '__rxor__', '__set__', '__setattr__', '__setitem__', '__setslice__', '__sizeof__',
441
'__str__', '__sub__', '__subclasscheck__', '__truediv__', '__unicode__', '__xor__', 'next'])
442
443
def _cached_function_unpickle(module,name):
444
"""
445
Unpickling of cached functions.
446
447
NOTE:
448
449
Pickling and unpickling of cached functions is by importing
450
from the module in which the function is defined.
451
452
INPUT:
453
454
- ``module``: A string, describing the module to import the
455
function from.
456
- ``name``: A string, name of the to-be-imported cached function.
457
458
EXAMPLE::
459
460
sage: type(cunningham_prime_factors)
461
<type 'sage.misc.cachefunc.CachedFunction'>
462
sage: loads(dumps(cunningham_prime_factors)) is cunningham_prime_factors #indirect doctest
463
True
464
465
"""
466
return getattr(__import__(module, fromlist=['']),name)
467
468
cdef class CachedFunction(object):
469
"""
470
Create a cached version of a function, which only recomputes
471
values it hasn't already computed. Synonyme: ``cached_function``
472
473
INPUT:
474
475
- ``f`` -- a function
476
- ``name`` (optional string) -- name that the cached version
477
of ``f`` should be provided with.
478
479
If ``f`` is a function, do either ``g = CachedFunction(f)``
480
or ``g = cached_function(f)`` to make a cached version of ``f``,
481
or put ``@cached_function`` right before the definition of ``f``
482
(i.e., use Python decorators)::
483
484
@cached_function
485
def f(...):
486
....
487
488
The inputs to the function must be hashable.
489
490
EXAMPLES::
491
492
sage: @cached_function
493
... def mul(x, y=2):
494
... return x*y
495
...
496
sage: mul(3)
497
6
498
499
We demonstrate that the result is cached, and that, moreover,
500
the cache takes into account the various ways of providing
501
default arguments::
502
503
sage: mul(3) is mul(3,2)
504
True
505
sage: mul(3,y=2) is mul(3,2)
506
True
507
508
The user can clear the cache::
509
510
sage: a = mul(4)
511
sage: mul.clear_cache()
512
sage: a is mul(4)
513
False
514
515
It is also possible to explicitly override the cache with
516
a different value::
517
518
sage: mul.set_cache('foo',5)
519
sage: mul(5,2)
520
'foo'
521
522
"""
523
def __init__(self, f, classmethod=False, name=None):
524
"""
525
Create a cached version of a function, which only recomputes
526
values it hasn't already computed. A custom name can be
527
provided by an optional argument "name".
528
529
If f is a function, do either g = CachedFunction(f) to make
530
a cached version of f, or put @CachedFunction right before
531
the definition of f (i.e., use Python decorators, but then
532
the optional argument ``name`` can not be used)::
533
534
@CachedFunction
535
def f(...):
536
....
537
538
The inputs to the function must be hashable.
539
540
TESTS::
541
542
sage: g = CachedFunction(number_of_partitions)
543
sage: g.__name__
544
'number_of_partitions'
545
sage: 'partitions' in sage.misc.sageinspect.sage_getdoc(g)
546
True
547
sage: g(5)
548
7
549
sage: g.cache
550
{((5, None, 'default'), ()): 7}
551
sage: def f(t=1): print(t)
552
sage: h = CachedFunction(f)
553
sage: w = walltime()
554
sage: h(); h(1); h(t=1)
555
1
556
sage: walltime(w) < 2
557
True
558
559
"""
560
self.is_classmethod = classmethod
561
self._common_init(f, None, name=name)
562
self.cache = {}
563
564
def _common_init(self, f, argument_fixer, name=None):
565
"""
566
Perform initialization common to CachedFunction and CachedMethodCaller.
567
568
TESTS::
569
570
sage: @cached_function
571
....: def test_cache(x):
572
....: return -x
573
sage: test_cache.__name__ # indirect doctest
574
'test_cache'
575
576
"""
577
self.f = f
578
if name is not None:
579
self.__name__ = name
580
elif hasattr(f, "func_name"):
581
self.__name__ = f.func_name
582
else:
583
self.__name__ = f.__name__
584
try:
585
self.__module__ = f.__module__
586
except AttributeError:
587
self.__module__ = f.__objclass__.__module__
588
if argument_fixer is not None: # it is None unless the argument fixer
589
# was known previously. See #15038.
590
self._argument_fixer = argument_fixer
591
self._fix_to_pos = argument_fixer.fix_to_pos
592
593
cdef argfix_init(self):
594
"""
595
Perform initialization common to CachedFunction and CachedMethodCaller.
596
597
TESTS::
598
599
sage: @cached_function
600
....: def test_cache(x):
601
....: return -x
602
sage: test_cache(1)
603
-1
604
sage: test_cache._fix_to_pos is not None # indirect doctest
605
True
606
607
"""
608
A = ArgumentFixer(self.f,classmethod=self.is_classmethod)
609
self._argument_fixer = A
610
self._fix_to_pos = A.fix_to_pos
611
612
def __reduce__(self):
613
"""
614
Pickling of cached functions.
615
616
TEST::
617
618
sage: type(cunningham_prime_factors)
619
<type 'sage.misc.cachefunc.CachedFunction'>
620
sage: loads(dumps(cunningham_prime_factors)) is cunningham_prime_factors #indirect doctest
621
True
622
623
"""
624
return _cached_function_unpickle, (self.__module__, self.__name__)
625
626
#########
627
## Introspection
628
##
629
## We provide some methods explicitly, and
630
## forward other questions to the cached function.
631
632
def _sage_doc_(self):
633
"""
634
Provide documentation for the cached function.
635
636
A cached function shall inherit the documentation
637
from the function that is wrapped, not from the
638
documentation of the wrapper.
639
640
TEST::
641
642
sage: P.<x,y> = QQ[]
643
sage: I = P*[x,y]
644
sage: from sage.misc.sageinspect import sage_getdoc
645
sage: print sage_getdoc(I.groebner_basis) # indirect doctest
646
Return the reduced Groebner basis of this ideal.
647
...
648
ALGORITHM: Uses Singular, Magma (if available), Macaulay2 (if
649
available), or a toy implementation.
650
651
"""
652
f = self.f
653
if hasattr(f, "func_doc"):
654
try:
655
sourcelines = sage_getsourcelines(f)
656
except IOError:
657
sourcelines = None
658
if sourcelines is not None:
659
from sage.env import SAGE_SRC, SAGE_LIB
660
filename = sage_getfile(f)
661
# The following is a heuristics to get
662
# the file name of the cached function
663
# or method
664
if filename.startswith(SAGE_SRC):
665
filename = filename[len(SAGE_SRC):]
666
elif filename.startswith(SAGE_LIB):
667
filename = filename[len(SAGE_LIB):]
668
file_info = "File: %s (starting at line %d)\n"%(filename,sourcelines[1])
669
doc = file_info+(f.func_doc or '')
670
else:
671
doc = f.func_doc
672
else:
673
doc = f.__doc__
674
return doc
675
676
def _sage_src_(self):
677
"""
678
Returns the source code for the wrapped function.
679
680
TESTS::
681
682
sage: from sage.misc.sageinspect import sage_getsource
683
sage: g = CachedFunction(number_of_partitions)
684
sage: 'bober' in sage_getsource(g) # indirect doctest
685
True
686
687
"""
688
from sage.misc.sageinspect import sage_getsource
689
return sage_getsource(self.f)
690
691
def _sage_src_lines_(self):
692
r"""
693
Returns the list of source lines and the first line number
694
of the wrapped function.
695
696
TEST::
697
698
sage: P.<x,y> = QQ[]
699
sage: I = P*[x,y]
700
sage: from sage.misc.sageinspect import sage_getsourcelines
701
sage: l = " elif algorithm == 'macaulay2:gb':\n"
702
sage: l in sage_getsourcelines(I.groebner_basis)[0] # indirect doctest
703
True
704
705
"""
706
from sage.misc.sageinspect import sage_getsourcelines
707
return sage_getsourcelines(self.f)
708
709
def _sage_argspec_(self):
710
"""
711
Return the argspec of the wrapped function or method.
712
713
This was implemented in trac ticket #11115.
714
715
EXAMPLE::
716
717
sage: P.<x,y> = QQ[]
718
sage: I = P*[x,y]
719
sage: from sage.misc.sageinspect import sage_getargspec
720
sage: sage_getargspec(I.groebner_basis) # indirect doctest
721
ArgSpec(args=['self', 'algorithm', 'deg_bound', 'mult_bound', 'prot'],
722
varargs='args', keywords='kwds', defaults=('', None, None,
723
False))
724
725
"""
726
return sage_getargspec(self.f)
727
728
def __call__(self, *args, **kwds):
729
"""
730
Return value from cache or call the wrapped function,
731
caching the output.
732
733
TESTS::
734
735
sage: g = CachedFunction(number_of_partitions)
736
sage: a = g(5)
737
sage: g.get_cache()
738
{((5, None, 'default'), ()): 7}
739
sage: a = g(10^5) # indirect doctest
740
sage: a == number_of_partitions(10^5)
741
True
742
sage: a is g(10^5)
743
True
744
sage: a is number_of_partitions(10^5)
745
True
746
747
"""
748
# We shortcut a common case of no arguments
749
if args or kwds:
750
if self._argument_fixer is None:
751
self.argfix_init()
752
k = self._fix_to_pos(*args, **kwds)
753
else:
754
if self._default_key is not None:
755
k = self._default_key
756
else:
757
if self._argument_fixer is None:
758
self.argfix_init()
759
k = self._default_key = self._fix_to_pos()
760
761
try:
762
return (<dict>self.cache)[k]
763
except KeyError:
764
w = self.f(*args, **kwds)
765
self.cache[k] = w
766
return w
767
768
cpdef get_cache(self):
769
"""
770
Returns the cache dictionary.
771
772
EXAMPLES::
773
774
sage: g = CachedFunction(number_of_partitions)
775
sage: a = g(5)
776
sage: g.get_cache()
777
{((5, None, 'default'), ()): 7}
778
779
"""
780
return self.cache
781
782
def is_in_cache(self, *args, **kwds):
783
"""
784
Checks if the argument list is in the cache.
785
786
EXAMPLES::
787
788
sage: class Foo:
789
... def __init__(self, x):
790
... self._x = x
791
... @cached_method
792
... def f(self, z, y=0):
793
... return self._x*z+y
794
...
795
sage: a = Foo(2)
796
sage: a.f.is_in_cache(3)
797
False
798
sage: a.f(3)
799
6
800
sage: a.f.is_in_cache(3,y=0)
801
True
802
"""
803
if self._argument_fixer is None:
804
self.argfix_init()
805
return self._fix_to_pos(*args, **kwds) in (<dict>self.cache)
806
807
def set_cache(self, value, *args, **kwds):
808
"""
809
Set the value for those args and keyword args
810
Mind the unintuitive syntax (value first).
811
Any idea on how to improve that welcome!
812
813
EXAMPLES::
814
815
sage: g = CachedFunction(number_of_partitions)
816
sage: a = g(5)
817
sage: g.get_cache()
818
{((5, None, 'default'), ()): 7}
819
sage: g.set_cache(17, 5)
820
sage: g.get_cache()
821
{((5, None, 'default'), ()): 17}
822
sage: g(5)
823
17
824
825
DEVELOPER NOTE:
826
827
Is there a way to use the following intuitive syntax?
828
829
::
830
831
sage: g(5) = 19 # todo: not implemented
832
sage: g(5) # todo: not implemented
833
19
834
"""
835
if self._argument_fixer is None:
836
self.argfix_init()
837
(<dict>self.cache)[self._fix_to_pos(*args, **kwds)] = value
838
839
def get_key(self, *args, **kwds):
840
"""
841
Returns the key in the cache to be used when args
842
and kwds are passed in as parameters.
843
844
EXAMPLES::
845
846
sage: @cached_function
847
... def foo(x):
848
... return x^2
849
...
850
sage: foo(2)
851
4
852
sage: foo.get_key(2)
853
((2,), ())
854
sage: foo.get_key(x=3)
855
((3,), ())
856
"""
857
if self._argument_fixer is None:
858
self.argfix_init()
859
return self._fix_to_pos(*args, **kwds)
860
861
def __repr__(self):
862
"""
863
EXAMPLES::
864
865
sage: g = CachedFunction(number_of_partitions)
866
sage: g # indirect doctest
867
Cached version of <function number_of_partitions at 0x...>
868
"""
869
try:
870
return "Cached version of %s"%self.f
871
except AttributeError:
872
return "Cached version of a method (pending reassignment)"
873
874
cpdef clear_cache(self):
875
"""
876
Clear the cache dictionary.
877
878
EXAMPLES::
879
880
sage: g = CachedFunction(number_of_partitions)
881
sage: a = g(5)
882
sage: g.get_cache()
883
{((5, None, 'default'), ()): 7}
884
sage: g.clear_cache()
885
sage: g.get_cache()
886
{}
887
"""
888
cdef object cache = self.cache
889
for key in cache.keys():
890
del cache[key]
891
892
def precompute(self, arglist, num_processes=1):
893
"""
894
Cache values for a number of inputs. Do the computation
895
in parallel, and only bother to compute values that we
896
haven't already cached.
897
898
EXAMPLES::
899
900
sage: @cached_function
901
... def oddprime_factors(n):
902
... l = [p for p,e in factor(n) if p != 2]
903
... return len(l)
904
sage: oddprime_factors.precompute(range(1,100), 4)
905
sage: oddprime_factors.cache[(25,),()]
906
1
907
"""
908
from sage.parallel.decorate import parallel, normalize_input
909
P = parallel(num_processes)(self.f)
910
has_key = self.cache.has_key
911
if self._argument_fixer is None:
912
self.argfix_init()
913
get_key = self._fix_to_pos
914
new = lambda x: not has_key(get_key(*x[0],**x[1]))
915
arglist = filter(new, map(normalize_input, arglist))
916
for ((args,kwargs), val) in P(arglist):
917
self.set_cache(val, *args, **kwargs)
918
919
920
cached_function = CachedFunction
921
922
cdef class WeakCachedFunction(CachedFunction):
923
"""
924
A version of :class:`CachedFunction` using weak references on the values.
925
926
If f is a function, do either ``g = weak_cached_function(f)`` to make
927
a cached version of f, or put ``@weak_cached_function`` right before
928
the definition of f (i.e., use Python decorators, but then
929
the optional argument ``name`` can not be used)::
930
931
@weak_cached_function
932
def f(...):
933
...
934
935
EXAMPLES::
936
937
sage: from sage.misc.cachefunc import weak_cached_function
938
sage: class A: pass
939
sage: @weak_cached_function
940
... def f():
941
... print "doing a computation"
942
... return A()
943
...
944
sage: a = f()
945
doing a computation
946
947
The result is cached::
948
949
sage: b = f()
950
sage: a is b
951
True
952
953
However, if there are no strong references left, the result
954
may be garbage collected, and thus a new computation would
955
take place::
956
957
sage: del a
958
sage: del b
959
sage: import gc
960
sage: n = gc.collect()
961
sage: a = f()
962
doing a computation
963
964
"""
965
def __init__(self, f, classmethod=False, name=None):
966
"""
967
The inputs to the function must be hashable.
968
The outputs to the function must be weakly referenceable.
969
970
TESTS::
971
972
sage: from sage.misc.cachefunc import weak_cached_function
973
sage: class A: pass
974
sage: @weak_cached_function
975
... def f():
976
... return A()
977
...
978
sage: f
979
Cached version of <function f at ...>
980
981
We demonstrate that pickling works, provided the uncached function
982
is available::
983
984
sage: import __main__
985
sage: __main__.f = f
986
sage: loads(dumps(f))
987
Cached version of <function f at ...>
988
sage: str(f.cache)
989
'<WeakValueDictionary at 0x...>'
990
991
"""
992
self._common_init(f, None, name=name)
993
self.cache = WeakValueDictionary()
994
def __call__(self, *args, **kwds):
995
"""
996
Return value from cache or call the wrapped function,
997
caching the output.
998
999
TESTS::
1000
1001
sage: from sage.misc.cachefunc import weak_cached_function
1002
sage: class A: pass
1003
sage: @weak_cached_function
1004
... def f():
1005
... print "doing a computation"
1006
... return A()
1007
...
1008
sage: a = f() # indirect doctest
1009
doing a computation
1010
1011
The result is cached::
1012
1013
sage: b = f()
1014
sage: a is b
1015
True
1016
1017
However, if there are no strong references left, the result
1018
may be garbage collected, and thus a new computation would
1019
take place::
1020
1021
sage: del a
1022
sage: del b
1023
sage: import gc
1024
sage: n = gc.collect()
1025
sage: a = f()
1026
doing a computation
1027
1028
"""
1029
# We shortcut a common case of no arguments
1030
if args or kwds:
1031
if self._argument_fixer is None:
1032
self.argfix_init()
1033
k = self._fix_to_pos(*args, **kwds)
1034
else:
1035
if self._default_key is not None:
1036
k = self._default_key
1037
else:
1038
if self._argument_fixer is None:
1039
self.argfix_init()
1040
k = self._default_key = self._fix_to_pos()
1041
1042
try:
1043
return self.cache[k]
1044
except KeyError:
1045
w = self.f(*args, **kwds)
1046
self.cache[k] = w
1047
return w
1048
def is_in_cache(self, *args, **kwds):
1049
"""
1050
Checks if the argument list is in the cache.
1051
1052
EXAMPLES::
1053
1054
sage: from sage.misc.cachefunc import weak_cached_function
1055
sage: class A:
1056
... def __init__(self, x):
1057
... self.x = x
1058
...
1059
sage: @weak_cached_function
1060
... def f(n):
1061
... return A(n)
1062
...
1063
sage: a = f(5)
1064
1065
The key 5 is in the cache, as long as there is a strong
1066
reference to the corresponding value::
1067
1068
sage: f.is_in_cache(5)
1069
True
1070
1071
However, if there are no strong references left, the cached
1072
item is removed from cache after garbage collection::
1073
1074
sage: del a
1075
sage: import gc
1076
sage: n = gc.collect()
1077
sage: f.is_in_cache(5)
1078
False
1079
1080
"""
1081
if self._argument_fixer is None:
1082
self.argfix_init()
1083
return self._fix_to_pos(*args, **kwds) in self.cache
1084
1085
def set_cache(self, value, *args, **kwds):
1086
"""
1087
Set the value for those args and keyword args
1088
Mind the unintuitive syntax (value first).
1089
Any idea on how to improve that welcome!
1090
1091
It is required that the given value is weak
1092
referenceable. The item will be removed from
1093
cache if the value is garbage collected.
1094
1095
EXAMPLES::
1096
1097
sage: from sage.misc.cachefunc import weak_cached_function
1098
sage: @weak_cached_function
1099
... def f(n):
1100
... raise RuntimeError
1101
...
1102
sage: f.set_cache(ZZ, 5)
1103
sage: f(5)
1104
Integer Ring
1105
1106
"""
1107
if self._argument_fixer is None:
1108
self.argfix_init()
1109
self.cache[self._fix_to_pos(*args, **kwds)] = value
1110
1111
1112
weak_cached_function = WeakCachedFunction
1113
1114
class CachedMethodPickle(object):
1115
"""
1116
This class helps to unpickle cached methods.
1117
1118
NOTE:
1119
1120
Since trac ticket #8611, a cached method is an attribute
1121
of the instance (provided that it has a ``__dict__``).
1122
Hence, when pickling the instance, it would be attempted
1123
to pickle that attribute as well, but this is a problem,
1124
since functions can not be pickled, currently. Therefore,
1125
we replace the actual cached method by a place holder,
1126
that kills itself as soon as any attribute is requested.
1127
Then, the original cached attribute is reinstated. But the
1128
cached values are in fact saved.
1129
1130
EXAMPLE::
1131
1132
sage: R.<x, y, z> = PolynomialRing(QQ, 3)
1133
sage: I = R*(x^3 + y^3 + z^3,x^4-y^4)
1134
sage: I.groebner_basis()
1135
[y^5*z^3 - 1/4*x^2*z^6 + 1/2*x*y*z^6 + 1/4*y^2*z^6,
1136
x^2*y*z^3 - x*y^2*z^3 + 2*y^3*z^3 + z^6,
1137
x*y^3 + y^4 + x*z^3, x^3 + y^3 + z^3]
1138
sage: I.groebner_basis
1139
Cached version of <function groebner_basis at 0x...>
1140
1141
We now pickle and unpickle the ideal. The cached method
1142
``groebner_basis`` is replaced by a placeholder::
1143
1144
sage: J = loads(dumps(I))
1145
sage: J.groebner_basis
1146
Pickle of the cached method "groebner_basis"
1147
1148
But as soon as any other attribute is requested from the
1149
placeholder, it replaces itself by the cached method, and
1150
the entries of the cache are actually preserved::
1151
1152
sage: J.groebner_basis.is_in_cache()
1153
True
1154
sage: J.groebner_basis
1155
Cached version of <function groebner_basis at 0x...>
1156
sage: J.groebner_basis() == I.groebner_basis()
1157
True
1158
1159
TESTS:
1160
1161
Since Trac Ticket #11115, there is a special implementation for
1162
cached methods that don't take arguments::
1163
1164
sage: P.<a,b,c,d> = QQ[]
1165
sage: I = P*[a,b]
1166
sage: type(I.gens)
1167
<type 'sage.misc.cachefunc.CachedMethodCallerNoArgs'>
1168
sage: type(I.groebner_basis)
1169
<type 'sage.misc.cachefunc.CachedMethodCaller'>
1170
1171
We demonstrate that both implementations can be pickled and
1172
preserve the cache. For that purpose, we assign nonsense to the
1173
cache. Of course, it is a very bad idea to override the cache in
1174
that way. So, please don't try this at home::
1175
1176
sage: I.groebner_basis.set_cache('foo',algorithm='singular')
1177
sage: I.groebner_basis(algorithm='singular')
1178
'foo'
1179
sage: I.gens.set_cache('bar')
1180
sage: I.gens()
1181
'bar'
1182
sage: J = loads(dumps(I))
1183
sage: J.gens()
1184
'bar'
1185
sage: J.groebner_basis(algorithm='singular')
1186
'foo'
1187
1188
Anyway, the cache will be automatically reconstructed after
1189
clearing it::
1190
1191
sage: J.gens.clear_cache()
1192
sage: J.gens()
1193
[a, b]
1194
sage: J.groebner_basis.clear_cache()
1195
sage: J.groebner_basis(algorithm='singular')
1196
[a, b]
1197
1198
AUTHOR:
1199
1200
- Simon King (2011-01)
1201
"""
1202
def __init__(self, inst, name, cache=None):
1203
"""
1204
INPUT:
1205
1206
- ``inst`` - some instance.
1207
- ``name`` (string) - usually the name of an attribute
1208
of ``inst`` to which ``self`` is assigned.
1209
1210
TEST::
1211
1212
sage: from sage.misc.cachefunc import CachedMethodPickle
1213
sage: P = CachedMethodPickle(1, 'foo')
1214
sage: P
1215
Pickle of the cached method "foo"
1216
1217
"""
1218
self._instance = inst
1219
self._name = name
1220
self._cache = cache
1221
def __repr__(self):
1222
"""
1223
TEST::
1224
1225
sage: R.<x, y, z> = PolynomialRing(QQ, 3)
1226
sage: I = R*(x^3 + y^3 + z^3,x^4-y^4)
1227
sage: G = I.groebner_basis()
1228
sage: J = loads(dumps(I))
1229
sage: J.groebner_basis #indirect doctest
1230
Pickle of the cached method "groebner_basis"
1231
"""
1232
return 'Pickle of the cached method "%s"'%self._name
1233
1234
def __reduce__(self):
1235
"""
1236
This class is a pickle. However, sometimes, pickles
1237
need to be pickled another time.
1238
1239
TEST::
1240
1241
sage: R.<x, y, z> = PolynomialRing(QQ, 3)
1242
sage: I = R*(x^3 + y^3 + z^3,x^4-y^4)
1243
sage: I.groebner_basis()
1244
[y^5*z^3 - 1/4*x^2*z^6 + 1/2*x*y*z^6 + 1/4*y^2*z^6,
1245
x^2*y*z^3 - x*y^2*z^3 + 2*y^3*z^3 + z^6,
1246
x*y^3 + y^4 + x*z^3, x^3 + y^3 + z^3]
1247
sage: J = loads(dumps(I))
1248
sage: J.groebner_basis
1249
Pickle of the cached method "groebner_basis"
1250
1251
When we now pickle ``J``, the pickle of the cached method
1252
needs to be taken care of::
1253
1254
sage: K = loads(dumps(J)) # indirect doctest
1255
sage: K.groebner_basis
1256
Pickle of the cached method "groebner_basis"
1257
sage: K.groebner_basis.cache
1258
{(('', None, None, False), ()):
1259
[y^5*z^3 - 1/4*x^2*z^6 + 1/2*x*y*z^6 + 1/4*y^2*z^6,
1260
x^2*y*z^3 - x*y^2*z^3 + 2*y^3*z^3 + z^6,
1261
x*y^3 + y^4 + x*z^3, x^3 + y^3 + z^3]}
1262
"""
1263
return CachedMethodPickle,(self._instance,self._name,self._cache)
1264
1265
def __call__(self,*args,**kwds):
1266
"""
1267
The purpose of this call method is to kill ``self`` and to
1268
replace it by an actual :class:`CachedMethodCaller`. The last
1269
thing that ``self`` does before disappearing is to call the
1270
:class:`CachedMethodCaller` and return the result.
1271
1272
EXAMPLE::
1273
1274
sage: P.<a,b,c,d> = QQ[]
1275
sage: I = P*[a,b]
1276
sage: I.gens
1277
Cached version of <function gens at 0x...>
1278
sage: J = loads(dumps(I))
1279
sage: J.gens
1280
Pickle of the cached method "gens"
1281
sage: J.gens() # indirect doctest
1282
[a, b]
1283
sage: J.gens
1284
Cached version of <function gens at 0x...>
1285
1286
"""
1287
self._instance.__dict__.__delitem__(self._name)
1288
CM = getattr(self._instance,self._name)
1289
if self._cache is not None:
1290
if isinstance(CM, CachedMethodCallerNoArgs):
1291
CM.cache = self._cache
1292
else:
1293
for k,v in self._cache:
1294
CM.cache[k] = v
1295
return CM(*args,**kwds)
1296
1297
def __getattr__(self,s):
1298
"""
1299
TEST::
1300
1301
sage: R.<x, y, z> = PolynomialRing(QQ, 3)
1302
sage: I = R*(x^3 + y^3 + z^3,x^4-y^4)
1303
sage: G = I.groebner_basis()
1304
sage: J = loads(dumps(I))
1305
sage: J.groebner_basis
1306
Pickle of the cached method "groebner_basis"
1307
1308
If an attribute of name ``s`` is requested (say,
1309
``is_in_cache``), the attribute ``self._name`` of
1310
``self._instance`` is deleted. Then, the attribute
1311
of name ``s`` of the attribute ``self._name`` of
1312
``self._instance`` is requested. Since ``self._name``
1313
is a cached method defined for the class of
1314
``self._instance``, retrieving the just-deleted
1315
attribute ``self._name`` succeeds.
1316
1317
In that way, the unpickling of the cached method is
1318
finally accomplished::
1319
1320
sage: J.groebner_basis.is_in_cache() #indirect doctest
1321
True
1322
sage: J.groebner_basis
1323
Cached version of <function groebner_basis at 0x...>
1324
1325
"""
1326
self._instance.__dict__.__delitem__(self._name)
1327
CM = getattr(self._instance,self._name)
1328
if self._cache is not None:
1329
if isinstance(CM, CachedMethodCallerNoArgs):
1330
CM.cache = self._cache
1331
else:
1332
for k,v in self._cache:
1333
CM.cache[k] = v
1334
return getattr(CM,s)
1335
1336
cdef class CachedMethodCaller(CachedFunction):
1337
"""
1338
Utility class that is used by :class:`CachedMethod` to bind a
1339
cached method to an instance.
1340
1341
NOTE:
1342
1343
Since Trac Ticket #11115, there is a special implementation
1344
:class:`CachedMethodCallerNoArgs` for methods that do not take
1345
arguments.
1346
1347
EXAMPLE::
1348
1349
sage: class A:
1350
... @cached_method
1351
... def bar(self,x):
1352
... return x^2
1353
sage: a = A()
1354
sage: a.bar
1355
Cached version of <function bar at 0x...>
1356
sage: type(a.bar)
1357
<type 'sage.misc.cachefunc.CachedMethodCaller'>
1358
sage: a.bar(2) is a.bar(x=2)
1359
True
1360
1361
"""
1362
def __init__(self, CachedMethod cachedmethod, inst, cache=None, inst_in_key=False, name=None):
1363
"""
1364
EXAMPLES::
1365
1366
sage: class Foo:
1367
... def __init__(self, x):
1368
... self._x = x
1369
... @cached_method
1370
... def f(self,*args):
1371
... return self._x^2
1372
...
1373
sage: a = Foo(2)
1374
sage: a.f.get_cache()
1375
{}
1376
sage: a.f()
1377
4
1378
sage: a.f.get_cache()
1379
{((), ()): 4}
1380
"""
1381
# initialize CachedFunction. Since the cached method is actually bound
1382
# to an instance, it now makes sense to initialise the ArgumentFixer
1383
# and re-use it for all bound cached method callers of the unbound
1384
# cached method.
1385
if cachedmethod._cachedfunc._argument_fixer is None:
1386
cachedmethod._cachedfunc.argfix_init()
1387
self._common_init(cachedmethod._cachedfunc.f, cachedmethod._cachedfunc._argument_fixer, name=name)
1388
self.cache = {} if cache is None else cache
1389
self._instance = inst
1390
self._inst_in_key = inst_in_key
1391
self._cachedmethod = cachedmethod
1392
1393
def __reduce__(self):
1394
"""
1395
The pickle of a :class:`CachedMethodCaller` unpickles
1396
to a :class:`CachedMethodPickle`, that is able to replace
1397
itself by a copy of the original :class:`CachedMethodCaller`.
1398
1399
TEST::
1400
1401
sage: R.<x, y, z> = PolynomialRing(QQ, 3)
1402
sage: I = R*(x^3 + y^3 + z^3,x^4-y^4)
1403
sage: G = I.groebner_basis()
1404
sage: J = loads(dumps(I)) #indirect doctest
1405
sage: J.groebner_basis
1406
Pickle of the cached method "groebner_basis"
1407
sage: J.groebner_basis.is_in_cache()
1408
True
1409
sage: J.groebner_basis
1410
Cached version of <function groebner_basis at 0x...>
1411
1412
"""
1413
# if hasattr(self._instance,self._cachedmethod._cache_name):
1414
# return CachedMethodPickle,(self._instance,self.__name__)
1415
if isinstance(self._cachedmethod, CachedInParentMethod) or hasattr(self._instance,self._cachedmethod._cache_name):
1416
return CachedMethodPickle,(self._instance,self.__name__)
1417
return CachedMethodPickle,(self._instance,self.__name__,self.cache.items())
1418
1419
def _instance_call(self, *args, **kwds):
1420
"""
1421
Call the cached method without using the cache.
1422
1423
EXAMPLE::
1424
1425
sage: P.<a,b,c,d> = QQ[]
1426
sage: I = P*[a,b]
1427
sage: I.groebner_basis()
1428
[a, b]
1429
sage: I.groebner_basis._instance_call() is I.groebner_basis()
1430
False
1431
sage: I.groebner_basis._instance_call() == I.groebner_basis()
1432
True
1433
1434
"""
1435
return self._cachedmethod._instance_call(self._instance, *args, **kwds)
1436
1437
def __call__(self, *args, **kwds):
1438
"""
1439
Call the cached method.
1440
1441
TESTS::
1442
1443
sage: class Foo:
1444
... @cached_method
1445
... def f(self, x,y=1):
1446
... return x+y
1447
...
1448
sage: a = Foo()
1449
sage: a.f(1) #indirect doctest
1450
2
1451
1452
The result is cached, taking into account
1453
the three ways of providing (named) arguments::
1454
1455
sage: a.f(5) is a.f(5,1)
1456
True
1457
sage: a.f(5) is a.f(5,y=1)
1458
True
1459
sage: a.f(5) is a.f(y=1,x=5)
1460
True
1461
1462
We test that #5843 is fixed::
1463
1464
sage: class Foo:
1465
... def __init__(self, x):
1466
... self._x = x
1467
... @cached_method
1468
... def f(self, y):
1469
... return self._x
1470
...
1471
sage: a = Foo(2)
1472
sage: b = Foo(3)
1473
sage: a.f(b.f)
1474
2
1475
"""
1476
# We shortcut a common case of no arguments
1477
# and we avoid calling another python function,
1478
# although that means to duplicate code.
1479
cdef int lenargs
1480
cdef int nargs
1481
cdef tuple k
1482
cdef dict cache = self.cache
1483
if kwds:
1484
if self._argument_fixer is None:
1485
self.argfix_init()
1486
if self._inst_in_key:
1487
k = (self._instance,self._fix_to_pos(*args, **kwds))
1488
else:
1489
k = self._fix_to_pos(*args, **kwds)
1490
else:
1491
if args:
1492
lenargs = len(args)
1493
nargs = self._argument_fixer._nargs
1494
if self._inst_in_key:
1495
if lenargs>=nargs:
1496
k = (self._instance,(args,()))
1497
else:
1498
k = (self._instance,(<tuple>args+(<tuple>self._argument_fixer._default_tuple)[-nargs+lenargs:],()))
1499
else:
1500
if lenargs>=nargs:
1501
k = (args,())
1502
else:
1503
k = (<tuple>args+(<tuple>self._argument_fixer._default_tuple)[-nargs+lenargs:],())
1504
elif self._default_key is not None:
1505
k = self._default_key
1506
else:
1507
if self._argument_fixer is None:
1508
self.argfix_init()
1509
if self._inst_in_key:
1510
k = self._default_key = (self._instance,self._fix_to_pos())
1511
else:
1512
k = self._default_key = self._fix_to_pos()
1513
try:
1514
return cache[k]
1515
except KeyError:
1516
w = self._cachedmethod._instance_call(self._instance, *args, **kwds)
1517
cache[k] = w
1518
return w
1519
1520
def get_key(self, *args, **kwds):
1521
"""
1522
Convert arguments to the key for this instance's cache.
1523
1524
EXAMPLES::
1525
1526
sage: class Foo:
1527
... def __init__(self, x):
1528
... self._x = x
1529
... @cached_method
1530
... def f(self, y, z=0):
1531
... return self._x * y + z
1532
...
1533
sage: a = Foo(2)
1534
sage: z = a.f(37)
1535
sage: k = a.f.get_key(37); k
1536
((37, 0), ())
1537
sage: a.f.get_cache()[k] is z
1538
True
1539
1540
Note that the method does not test whether there are
1541
too many arguments, or wrong argument names::
1542
1543
sage: a.f.get_key(1,2,3,x=4,y=5,z=6)
1544
((1, 2, 3), (('x', 4), ('y', 5), ('z', 6)))
1545
1546
It does, however, take into account the different
1547
ways of providing named arguments, possibly with a
1548
default value::
1549
1550
sage: a.f.get_key(5)
1551
((5, 0), ())
1552
sage: a.f.get_key(y=5)
1553
((5, 0), ())
1554
sage: a.f.get_key(5,0)
1555
((5, 0), ())
1556
sage: a.f.get_key(5,z=0)
1557
((5, 0), ())
1558
sage: a.f.get_key(y=5,z=0)
1559
((5, 0), ())
1560
1561
"""
1562
if self._argument_fixer is None:
1563
self.argfix_init()
1564
if self._inst_in_key:
1565
return (self._instance,self._fix_to_pos(*args,**kwds))
1566
return self._fix_to_pos(*args,**kwds)
1567
1568
def __get__(self, inst, cls): #cls=None):
1569
r"""
1570
Get a :class:`CachedMethodCaller` bound to a specific
1571
instance of the class of the cached method.
1572
1573
NOTE:
1574
1575
:class:`CachedMethodCaller` has a separate ``__get__``
1576
since the categories framework creates and caches the
1577
return value of ``CachedMethod.__get__`` with
1578
``inst==None``.
1579
1580
This getter attempts to assign a bound method as an
1581
attribute to the given instance. If this is not
1582
possible (for example, for some extension classes),
1583
it is attempted to find an attribute ``__cached_methods``,
1584
and store/retrieve the bound method there. In that
1585
way, cached methods can be implemented for extension
1586
classes deriving from :class:`~sage.structure.parent.Parent`
1587
and :class:`~sage.structure.element.Element`.
1588
1589
TESTS:
1590
1591
Due to the separate ``__get__`` method, it is possible
1592
to define a cached method in one class and use it as
1593
an attribute of another class.
1594
1595
sage: class Foo:
1596
... @cached_method
1597
... def f(self, y):
1598
... return y - 1
1599
sage: class Bar:
1600
... f = Foo.f
1601
sage: b1 = Bar()
1602
sage: b2 = Bar()
1603
1604
The :class:`CachedMethod` is replaced by an instance
1605
of :class:`CachedMethodCaller` that (by trac ticket
1606
#8611) is set as an attribute. Hence, we have::
1607
1608
sage: b1.f is b1.f
1609
True
1610
1611
Any instance of ``Bar`` gets its own instance of
1612
:class:`CachedMethodCaller``::
1613
1614
sage: b1.f is b2.f
1615
False
1616
1617
The method caller knows the instance that it belongs
1618
to::
1619
1620
sage: Foo.f._instance is None
1621
True
1622
sage: b1.f._instance is b1
1623
True
1624
sage: b2.f._instance is b2
1625
True
1626
1627
An extension class can inherit a cached method from the
1628
parent or element class of a category (trac ticket #11115).
1629
See :class:`CachedMethodCaller` for examples.
1630
1631
"""
1632
# This is for Parents or Elements that do not allow attribute assignment
1633
try:
1634
return (<dict>inst.__cached_methods)[self._cachedmethod._cachedfunc.__name__]
1635
except (AttributeError,TypeError,KeyError):
1636
pass
1637
Caller = CachedMethodCaller(self._cachedmethod, inst, cache=self._cachedmethod._get_instance_cache(inst), inst_in_key=self._inst_in_key, name=self._cachedmethod._cachedfunc.__name__)
1638
try:
1639
setattr(inst,self._cachedmethod._cachedfunc.__name__, Caller)
1640
return Caller
1641
except AttributeError,msg:
1642
pass
1643
try:
1644
if inst.__cached_methods is None:
1645
inst.__cached_methods = {self._cachedmethod._cachedfunc.__name__ : Caller}
1646
else:
1647
(<dict>inst.__cached_methods)[self._cachedmethod._cachedfunc.__name__] = Caller
1648
except AttributeError,msg:
1649
pass
1650
return Caller
1651
1652
cdef class CachedMethodCallerNoArgs(CachedFunction):
1653
"""
1654
Utility class that is used by :class:`CachedMethod` to bind a
1655
cached method to an instance, in the case of a method that does
1656
not accept any arguments except ``self``.
1657
1658
NOTE:
1659
1660
The return value ``None`` would not be cached. So, if you have
1661
a method that does not accept arguments and may return ``None``
1662
after a lengthy computation, then ``@cached_method`` should not
1663
be used.
1664
1665
EXAMPLE::
1666
1667
sage: P.<a,b,c,d> = QQ[]
1668
sage: I = P*[a,b]
1669
sage: I.gens
1670
Cached version of <function gens at 0x...>
1671
sage: type(I.gens)
1672
<type 'sage.misc.cachefunc.CachedMethodCallerNoArgs'>
1673
sage: I.gens is I.gens
1674
True
1675
sage: I.gens() is I.gens()
1676
True
1677
1678
AUTHOR:
1679
1680
- Simon King (2011-04)
1681
"""
1682
def __init__(self, inst, f, cache=None, name=None):
1683
"""
1684
EXAMPLES::
1685
1686
sage: class Foo:
1687
... def __init__(self, x):
1688
... self._x = x
1689
... @cached_method
1690
... def f(self):
1691
... return self._x^2
1692
...
1693
sage: a = Foo(2)
1694
sage: print a.f.get_cache()
1695
None
1696
sage: a.f()
1697
4
1698
sage: a.f.get_cache()
1699
4
1700
1701
"""
1702
# initialize CachedFunction
1703
if isinstance(f,basestring):
1704
try:
1705
F = getattr(inst.__class__,f)
1706
except AttributeError:
1707
F = getattr(inst,f)
1708
if isinstance(F,CachedFunction):
1709
f = F.f
1710
else:
1711
f = F
1712
self._common_init(f, None, name=name)
1713
# This is for unpickling a CachedMethodCallerNoArgs out
1714
# of an old CachedMethodCaller:
1715
cachename = '_cache__' + self.__name__
1716
if hasattr(inst, cachename):
1717
# This is for data that are pickled in an old format
1718
CACHE = getattr(inst, cachename)
1719
if len(CACHE)>1:
1720
raise TypeError, "Apparently you are opening a pickle in which '%s' was a method accepting arguments"%name
1721
if len(CACHE)==1:
1722
self.cache = CACHE.values()[0]
1723
else:
1724
self.cache = cache
1725
delattr(inst, cachename)
1726
else:
1727
self.cache = cache # None means: the underlying method will be called
1728
self._instance = inst
1729
1730
def __reduce__(self):
1731
"""
1732
Since functions can not be pickled, the cached method caller
1733
is pickled by a :class:`CachedMethodPickle`, that replaces
1734
itself by an actual :class:`CachedMethodCallerNoArgs` as soon
1735
as it is asked to do anything.
1736
1737
TEST::
1738
1739
sage: P.<a,b,c,d> = QQ[]
1740
sage: I = P*[a,b]
1741
sage: I.gens()
1742
[a, b]
1743
sage: I.gens
1744
Cached version of <function gens at 0x...>
1745
sage: J = loads(dumps(I))
1746
sage: J.gens
1747
Pickle of the cached method "gens"
1748
sage: J.gens.cache
1749
[a, b]
1750
sage: J.gens
1751
Cached version of <function gens at 0x...>
1752
1753
"""
1754
return CachedMethodPickle,(self._instance,self.__name__,self.cache)
1755
1756
def _instance_call(self):
1757
"""
1758
Call the cached method without using the cache.
1759
1760
EXAMPLE::
1761
1762
sage: P.<a,b,c,d> = QQ[]
1763
sage: I = P*[a,b]
1764
sage: I.gens()
1765
[a, b]
1766
sage: I.gens._instance_call() is I.gens()
1767
False
1768
sage: I.gens._instance_call() == I.gens()
1769
True
1770
1771
"""
1772
return self.f(self._instance)
1773
1774
def __call__(self):
1775
"""
1776
Call the cached method.
1777
1778
EXAMPLE::
1779
1780
sage: P.<a,b,c,d> = QQ[]
1781
sage: I = P*[a,b]
1782
sage: I.gens() # indirect doctest
1783
[a, b]
1784
sage: I.gens() is I.gens()
1785
True
1786
1787
"""
1788
if self.cache is None:
1789
f = self.f
1790
self.cache = f(self._instance)
1791
return self.cache
1792
1793
def set_cache(self, value):
1794
"""
1795
Override the cache with a specific value.
1796
1797
NOTE:
1798
1799
``None`` is not suitable for a cached value. It would be
1800
interpreted as an empty cache, forcing a new computation.
1801
1802
EXAMPLES::
1803
1804
sage: P.<a,b,c,d> = QQ[]
1805
sage: I = P*[a,b]
1806
sage: I.gens()
1807
[a, b]
1808
sage: I.gens.set_cache('bar')
1809
sage: I.gens()
1810
'bar'
1811
1812
The cache can be emptied and thus the original value will
1813
be reconstructed::
1814
1815
sage: I.gens.clear_cache()
1816
sage: I.gens()
1817
[a, b]
1818
1819
The attempt to assign ``None`` to the cache fails::
1820
1821
sage: I.gens.set_cache(None)
1822
sage: I.gens()
1823
[a, b]
1824
1825
"""
1826
self.cache = value
1827
1828
cpdef clear_cache(self):
1829
r"""
1830
Clear the cache dictionary.
1831
1832
EXAMPLES::
1833
1834
sage: P.<a,b,c,d> = QQ[]
1835
sage: I = P*[a,b]
1836
sage: I.gens()
1837
[a, b]
1838
sage: I.gens.set_cache('bar')
1839
sage: I.gens()
1840
'bar'
1841
1842
The cache can be emptied and thus the original value will
1843
be reconstructed::
1844
1845
sage: I.gens.clear_cache()
1846
sage: I.gens()
1847
[a, b]
1848
1849
"""
1850
self.cache = None
1851
1852
def is_in_cache(self):
1853
"""
1854
Answers whether the return value is already in the cache.
1855
1856
NOTE:
1857
1858
Recall that a cached method without arguments can not cache
1859
the return value ``None``.
1860
1861
EXAMPLE::
1862
1863
sage: P.<x,y> = QQ[]
1864
sage: I = P*[x,y]
1865
sage: I.gens.is_in_cache()
1866
False
1867
sage: I.gens()
1868
[x, y]
1869
sage: I.gens.is_in_cache()
1870
True
1871
1872
"""
1873
return self.cache is not None
1874
1875
def __get__(self, inst, cls): #cls=None):
1876
"""
1877
Get a :class:`CachedMethodCallerNoArgs` bound to a specific
1878
instance of the class of the cached method.
1879
1880
NOTE:
1881
1882
:class:`CachedMethodCallerNoArgs` has a separate ``__get__``
1883
since the categories framework creates and caches the
1884
return value of ``CachedMethod.__get__`` with
1885
``inst==None``.
1886
1887
This getter attempts to assign a bound method as an
1888
attribute to the given instance. If this is not
1889
possible (for example, for some extension classes),
1890
it is attempted to find an attribute ``__cached_methods``,
1891
and store/retrieve the bound method there. In that
1892
way, cached methods can be implemented for extension
1893
classes deriving from :class:`~sage.structure.parent.Parent`
1894
and :class:`~sage.structure.element.Element`.
1895
1896
TESTS:
1897
1898
Due to the separate ``__get__`` method, it is possible
1899
to define a cached method in one class and use it as
1900
an attribute of another class.
1901
1902
sage: class Foo:
1903
... def __init__(self, n):
1904
... self.__n = n
1905
... @cached_method
1906
... def f(self):
1907
... return self.__n^2
1908
...
1909
sage: class Bar:
1910
... f = Foo.f
1911
...
1912
sage: b1 = Bar()
1913
sage: b2 = Bar()
1914
1915
The :class:`CachedMethod` is replaced by an instance of
1916
:class:`CachedMethodCallerNoArgs` that is set as an
1917
attribute. Hence, we have::
1918
1919
sage: b1.f is b1.f
1920
True
1921
sage: type(b1.f)
1922
<type 'sage.misc.cachefunc.CachedMethodCallerNoArgs'>
1923
1924
Any instance of ``Bar`` gets its own instance of
1925
:class:`CachedMethodCaller``::
1926
1927
sage: b1.f is b2.f
1928
False
1929
1930
The method caller knows the instance that it belongs
1931
to::
1932
1933
sage: Foo.f._instance is None
1934
True
1935
sage: b1.f._instance is b1
1936
True
1937
sage: b2.f._instance is b2
1938
True
1939
1940
"""
1941
# This is for Parents or Elements that do not allow attribute assignment
1942
try:
1943
return (<dict>inst.__cached_methods)[self.__name__]
1944
except (AttributeError,TypeError,KeyError),msg:
1945
pass
1946
Caller = CachedMethodCallerNoArgs(inst, self.f, name=self.__name__)
1947
try:
1948
setattr(inst,self.__name__, Caller)
1949
return Caller
1950
except AttributeError:
1951
pass
1952
try:
1953
if inst.__cached_methods is None:
1954
inst.__cached_methods = {self.__name__ : Caller}
1955
else:
1956
(<dict>inst.__cached_methods)[self.__name__] = Caller
1957
except AttributeError,msg:
1958
pass
1959
return Caller
1960
1961
cdef class CachedMethod(object):
1962
"""
1963
A decorator that creates a cached version of an instance
1964
method of a class.
1965
1966
NOTE:
1967
1968
For proper behavior, the method must be a pure function
1969
(no side effects). Arguments to the method must be hashable.
1970
1971
EXAMPLES::
1972
1973
sage: class Foo(object):
1974
... @cached_method
1975
... def f(self, t, x=2):
1976
... print 'computing'
1977
... return t**x
1978
sage: a = Foo()
1979
1980
The example shows that the actual computation
1981
takes place only once, and that the result is
1982
identical for equivalent input::
1983
1984
sage: res = a.f(3, 2); res
1985
computing
1986
9
1987
sage: a.f(t = 3, x = 2) is res
1988
True
1989
sage: a.f(3) is res
1990
True
1991
1992
Note, however, that the :class:`CachedMethod` is replaced by a
1993
:class:`CachedMethodCaller` or :class:`CachedMethodCallerNoArgs`
1994
as soon as it is bound to an instance or class::
1995
1996
sage: P.<a,b,c,d> = QQ[]
1997
sage: I = P*[a,b]
1998
sage: type(I.__class__.gens)
1999
<type 'sage.misc.cachefunc.CachedMethodCallerNoArgs'>
2000
2001
So, you would hardly ever see an instance of this class alive.
2002
"""
2003
def __init__(self, f, name=None):
2004
"""
2005
EXAMPLES::
2006
2007
sage: class Foo:
2008
... def __init__(self, x):
2009
... self._x = x
2010
... @cached_method
2011
... def f(self,n):
2012
... return self._x^n
2013
... @cached_method
2014
... def f0(self):
2015
... return self._x^2
2016
...
2017
sage: a = Foo(2)
2018
sage: a.f(2)
2019
4
2020
sage: a.f0()
2021
4
2022
2023
The computations in method ``f`` are tried to store in a
2024
dictionary assigned to the instance ``a``::
2025
2026
sage: hasattr(a, '_cache__f')
2027
True
2028
sage: a._cache__f
2029
{((2,), ()): 4}
2030
2031
As a shortcut, useful to speed up internal computations,
2032
the same dictionary is also available as an attribute
2033
of the ``CachedMethodCaller``::
2034
2035
sage: type(a.f)
2036
<type 'sage.misc.cachefunc.CachedMethodCaller'>
2037
sage: a.f.cache is a._cache__f
2038
True
2039
2040
Note that if the instance ``a`` would not accept attribute
2041
assignment, the computations would still be cached in
2042
``a.f.cache``, and they would in fact be preserved when
2043
pickling.
2044
2045
The cached method ``f0`` accepts no arguments, which allows
2046
for an improved way of caching: By an attribute of the cached
2047
method itsel. This cache is *only* available in that way, i.e.,
2048
it is not additionally stored as an attribute of ``a``::
2049
2050
sage: type(a.f0)
2051
<type 'sage.misc.cachefunc.CachedMethodCallerNoArgs'>
2052
sage: a.f0.cache
2053
4
2054
sage: sorted(dir(a))
2055
['__doc__', '__init__', '__module__', '_cache__f', '_x', 'f', 'f0']
2056
2057
"""
2058
self._cache_name = '_cache__' + (name or f.__name__)
2059
self._cachedfunc = CachedFunction(f, classmethod=True, name=name)
2060
2061
def _instance_call(self, inst, *args, **kwds):
2062
"""
2063
Call the cached method *without* using the cache.
2064
2065
INPUT:
2066
2067
- ``inst`` - an instance at which the method is to be called
2068
- Further positional or named arguments.
2069
2070
EXAMPLES::
2071
2072
sage: class Foo(object):
2073
... def __init__(self, x):
2074
... self._x = x
2075
... @cached_method
2076
... def f(self,n=2):
2077
... return self._x^n
2078
...
2079
sage: a = Foo(2)
2080
sage: a.f()
2081
4
2082
2083
Usually, a cached method is indeed cached::
2084
2085
sage: a.f() is a.f()
2086
True
2087
2088
However, when it becomes necessary, one can call it without
2089
using the cache. Note that ``a.f`` is an instance of
2090
:class:`CachedMethodCaller`. But its
2091
:meth:`CachedMethodCaller._instance_call` relies on this
2092
method, so, we have an indirect doctest::
2093
2094
sage: a.f._instance_call() is a.f() # indirect doctest
2095
False
2096
sage: a.f._instance_call() == a.f()
2097
True
2098
2099
"""
2100
return self._cachedfunc.f(inst, *args, **kwds)
2101
2102
cpdef dict _get_instance_cache(self, inst):
2103
"""
2104
Returns the cache dictionary.
2105
2106
TESTS::
2107
2108
sage: class Foo:
2109
... def __init__(self, x):
2110
... self._x = x
2111
... @cached_method
2112
... def f(self,n=2):
2113
... return self._x^n
2114
...
2115
sage: a = Foo(2)
2116
sage: a.f()
2117
4
2118
2119
Note that we can not provide a direct test, since ``a.f`` is
2120
an instance of :class:`CachedMethodCaller`. But during its
2121
initialisation, this method was called in order to provide the
2122
cached method caller with its cache, and, if possible, assign
2123
it to an attribute of ``a``. So, the following is an indirect
2124
doctest::
2125
2126
sage: a.f.get_cache() # indirect doctest
2127
{((2,), ()): 4}
2128
sage: a._cache__f
2129
{((2,), ()): 4}
2130
2131
"""
2132
try:
2133
return inst.__dict__.setdefault(self._cache_name, {})
2134
except AttributeError:
2135
return {}
2136
2137
def __get__(self, object inst, cls): #cls=None):
2138
"""
2139
Get a CachedMethodCaller bound to this specific instance of
2140
the class of the cached method.
2141
2142
TESTS::
2143
2144
sage: class Foo:
2145
... @cached_method
2146
... def f(self):
2147
... return 1
2148
... @cached_method
2149
... def g(self, x,n=3):
2150
... return x^n
2151
...
2152
sage: a = Foo()
2153
sage: type(a.f)
2154
<type 'sage.misc.cachefunc.CachedMethodCallerNoArgs'>
2155
sage: type(a.g)
2156
<type 'sage.misc.cachefunc.CachedMethodCaller'>
2157
2158
By trac ticket #8611, it is attempted to set the
2159
CachedMethodCaller as an attribute of the instance ``a``,
2160
replacing the original cached attribute. Therefore, the
2161
``__get__`` method will be used only once, which saves much
2162
time. Hence, we have::
2163
2164
sage: a.f is a.f
2165
True
2166
sage: a.g is a.g
2167
True
2168
2169
"""
2170
# This is for Parents or Elements that do not allow attribute assignment:
2171
cdef str name
2172
try:
2173
name = self._cachedfunc.__name__
2174
except AttributeError:
2175
name = self.__name__
2176
try:
2177
return (<dict>inst.__cached_methods)[name]
2178
except (AttributeError,TypeError,KeyError),msg:
2179
pass
2180
# Apparently we need to construct the caller.
2181
# Since we have an optimized version for functions that do not accept arguments,
2182
# we need to analyse the argspec
2183
f = (<CachedFunction>self._cachedfunc).f
2184
if self.nargs==0:
2185
args, varargs, keywords, defaults = sage_getargspec(f)
2186
if varargs is None and keywords is None and len(args)<=1:
2187
self.nargs = 1
2188
Caller = CachedMethodCallerNoArgs(inst, f, name=name)
2189
else:
2190
self.nargs = 2 # don't need the exact number
2191
Caller = CachedMethodCaller(self, inst,
2192
cache=self._get_instance_cache(inst),
2193
name=name)
2194
elif self.nargs==1:
2195
Caller = CachedMethodCallerNoArgs(inst, f, name=name)
2196
else:
2197
Caller = CachedMethodCaller(self, inst,
2198
cache=self._get_instance_cache(inst),
2199
name=name)
2200
try:
2201
setattr(inst, name, Caller)
2202
return Caller
2203
except AttributeError:
2204
pass
2205
try:
2206
if inst.__cached_methods is None:
2207
inst.__cached_methods = {name : Caller}
2208
else:
2209
(<dict>inst.__cached_methods)[name] = Caller
2210
except AttributeError:
2211
pass
2212
return Caller
2213
2214
# Note: a simpler approach to this would be
2215
# def caller(*args, **kwds):
2216
# return self._instance_call(inst, *args, **kwds)
2217
# return caller
2218
# The disadvantage to this is that it does not provide
2219
# is_in_cache(), set_cache(), clear_cache(), ... methods.
2220
2221
cdef class CachedSpecialMethod(CachedMethod):
2222
"""
2223
Cached version of *special* python methods.
2224
2225
IMPLEMENTATION:
2226
2227
For new style classes ``C``, it is not possible to override a special
2228
method, such as ``__hash__``, in the ``__dict__`` of an instance ``c`` of
2229
``C``, because Python will for efficiency reasons always use what is
2230
provided by the class, not by the instance.
2231
2232
By consequence, if ``__hash__`` would be wrapped by using
2233
:class:`CachedMethod`, then ``hash(c)`` will access ``C.__hash__`` and bind
2234
it to ``c``, which means that the ``__get__`` method of
2235
:class:`CachedMethod` will be called. But there, we assume that Python has
2236
already inspected ``__dict__``, and thus a :class:`CachedMethodCaller`
2237
will be created over and over again.
2238
2239
Here, the ``__get__`` method will explicitly access the ``__dict__``, so that
2240
``hash(c)`` will rely on a single :class:`CachedMethodCaller` stored in
2241
the ``__dict__``.
2242
2243
EXAMPLES::
2244
2245
sage: class C:
2246
....: @cached_method
2247
....: def __hash__(self):
2248
....: print "compute hash"
2249
....: return int(5)
2250
....:
2251
sage: c = C()
2252
sage: type(C.__hash__)
2253
<type 'sage.misc.cachefunc.CachedMethodCallerNoArgs'>
2254
2255
The hash is computed only once, subsequent calls will use the value from
2256
the cache. This was implemented in :trac:`12601`.
2257
2258
sage: hash(c) # indirect doctest
2259
compute hash
2260
5
2261
sage: hash(c)
2262
5
2263
2264
"""
2265
def __get__(self, object inst, cls):
2266
"""
2267
Bind a :class:`CachedMethodCaller` to a specific instance, using ``__dict__``.
2268
2269
EXAMPLES::
2270
2271
sage: class C:
2272
....: @cached_method
2273
....: def __hash__(self):
2274
....: print "compute hash"
2275
....: return int(5)
2276
....:
2277
sage: c = C()
2278
sage: type(C.__hash__)
2279
<type 'sage.misc.cachefunc.CachedMethodCallerNoArgs'>
2280
sage: hash(c) # indirect doctest
2281
compute hash
2282
5
2283
sage: hash(c)
2284
5
2285
"""
2286
# This is for Parents or Elements that do not allow attribute assignment:
2287
cdef str name
2288
try:
2289
name = self._cachedfunc.__name__
2290
except AttributeError:
2291
name = self.__name__
2292
cdef dict D = None
2293
if inst is not None:
2294
try:
2295
D = inst.__dict__
2296
except (TypeError, AttributeError):
2297
try:
2298
D = inst.__cached_methods
2299
except (TypeError, AttributeError):
2300
raise TypeError("For a cached special method, either attribute assignment or a public '__cached_methods' attribute of type <dict> is needed")
2301
if D is None:
2302
# This can only happen in the case of __cached_methods
2303
D = inst.__cached_methods = {}
2304
else:
2305
try:
2306
return D[name]
2307
except KeyError:
2308
pass
2309
# Apparently we need to construct the caller.
2310
# Since we have an optimized version for functions that do not accept arguments,
2311
# we need to analyse the argspec
2312
f = (<CachedFunction>self._cachedfunc).f
2313
if self.nargs==0:
2314
args, varargs, keywords, defaults = sage_getargspec(f)
2315
if varargs is None and keywords is None and len(args)<=1:
2316
self.nargs = 1
2317
Caller = CachedMethodCallerNoArgs(inst, f, name=name)
2318
else:
2319
self.nargs = 2 # don't need the exact number
2320
Caller = CachedMethodCaller(self, inst,
2321
cache=self._get_instance_cache(inst),
2322
name=name)
2323
elif self.nargs==1:
2324
Caller = CachedMethodCallerNoArgs(inst, f, name=name)
2325
else:
2326
Caller = CachedMethodCaller(self, inst,
2327
cache=self._get_instance_cache(inst),
2328
name=name)
2329
if inst is not None:
2330
try:
2331
setattr(inst,name, Caller)
2332
return Caller
2333
except AttributeError:
2334
pass
2335
D[name] = Caller
2336
return Caller
2337
2338
def cached_method(f, name=None):
2339
"""
2340
2341
EXAMPLES:
2342
2343
In the following examples, one can see how a cached method works in applicationy.
2344
Below, we demonstrate what is done behind the scenes::
2345
2346
sage: class C:
2347
....: @cached_method
2348
....: def __hash__(self):
2349
....: print "compute hash"
2350
....: return int(5)
2351
....: @cached_method
2352
....: def f(self, x):
2353
....: print "computing cached method"
2354
....: return x*2
2355
sage: c = C()
2356
sage: type(C.__hash__)
2357
<type 'sage.misc.cachefunc.CachedMethodCallerNoArgs'>
2358
sage: hash(c)
2359
compute hash
2360
5
2361
2362
When calling a cached method for the second time with the same arguments,
2363
the value is gotten from the cache, so that a new computation is not
2364
needed::
2365
2366
sage: hash(c)
2367
5
2368
sage: c.f(4)
2369
computing cached method
2370
8
2371
sage: c.f(4) is c.f(4)
2372
True
2373
2374
Using cached methods for the hash and other special methods was
2375
implemented in :trac:`12601`, by means of :class:`CachedSpecialMethod`. We
2376
show that it is used behind the scenes::
2377
2378
sage: cached_method(c.__hash__)
2379
<sage.misc.cachefunc.CachedSpecialMethod object at ...>
2380
sage: cached_method(c.f)
2381
<sage.misc.cachefunc.CachedMethod object at ...>
2382
2383
"""
2384
cdef str fname = name or f.__name__
2385
if fname in special_method_names:
2386
return CachedSpecialMethod(f, name)
2387
return CachedMethod(f, name)
2388
2389
cdef class CachedInParentMethod(CachedMethod):
2390
r"""
2391
A decorator that creates a cached version of an instance
2392
method of a class.
2393
2394
In contrast to :class:`CachedMethod`,
2395
the cache dictionary is an attribute of the parent of
2396
the instance to which the method belongs.
2397
2398
ASSUMPTION:
2399
2400
This way of caching works only if
2401
2402
- the instances *have* a parent, and
2403
- the instances are hashable (they are part of the cache key).
2404
2405
NOTE:
2406
2407
For proper behavior, the method must be a pure function (no side
2408
effects). If this decorator is used on a method, it will have
2409
identical output on equal elements. This is since the element is
2410
part of the hash key. Arguments to the method and the instance
2411
it is assigned to must be hashable.
2412
2413
Examples can be found at :mod:`~sage.misc.cachefunc`.
2414
2415
"""
2416
2417
def __init__(self, f, name=None):
2418
"""
2419
Constructs a new method with cache stored in the parent of the instance.
2420
2421
See also ``cached_method`` and ``cached_function``.
2422
2423
EXAMPLES::
2424
2425
sage: class MyParent(Parent):
2426
... pass
2427
sage: class Foo:
2428
... def __init__(self, x):
2429
... self._x = x
2430
... _parent = MyParent()
2431
... def parent(self):
2432
... return self._parent
2433
... @cached_in_parent_method #indirect doctest
2434
... def f(self):
2435
... return self._x^2
2436
...
2437
sage: a = Foo(2)
2438
sage: a.f()
2439
4
2440
sage: hasattr(a.parent(), '_cache__element_f')
2441
True
2442
2443
For speeding up internal computations, this dictionary
2444
is also accessible as an attribute of the CachedMethodCaller
2445
(by trac ticket #8611)::
2446
2447
sage: a.parent()._cache__element_f is a.f.cache
2448
True
2449
"""
2450
self._cache_name = '_cache__' + 'element_' + (name or f.__name__)
2451
self._cachedfunc = CachedFunction(f, classmethod=True, name=name)
2452
2453
cpdef dict _get_instance_cache(self, inst):
2454
"""
2455
Returns the cache dictionary, which is stored in the parent.
2456
2457
EXAMPLES::
2458
2459
sage: class MyParent(Parent):
2460
... pass
2461
...
2462
sage: class Foo:
2463
... def __init__(self, x):
2464
... self._x = x
2465
... _parent = MyParent()
2466
... def parent(self):
2467
... return self._parent
2468
... def __eq__(self, other):
2469
... return self._x^2 == other._x^2
2470
... def __hash__(self):
2471
... return hash(self._x^2)
2472
... def __repr__(self):
2473
... return 'My %s'%self._x
2474
... @cached_in_parent_method
2475
... def f(self):
2476
... return self._x^3
2477
...
2478
sage: a = Foo(2)
2479
sage: a.f()
2480
8
2481
sage: a.f.get_cache() #indirect doctest
2482
{(My 2, ((), ())): 8}
2483
2484
Since the key for the cache depends on equality of
2485
the instances, we obtain *identical* result for
2486
*equal* instance - even though in this particular
2487
example the result is wrong::
2488
2489
sage: b = Foo(-2)
2490
sage: a is not b
2491
True
2492
sage: a == b
2493
True
2494
sage: b.f() is a.f()
2495
True
2496
2497
Non-equal instances do not share the result of
2498
the cached method, but they do share the cache::
2499
2500
sage: c = Foo(3)
2501
sage: c.f()
2502
27
2503
sage: c.f.get_cache() is a.f.get_cache() #indirect doctest
2504
True
2505
2506
Note that the cache is also available as an
2507
attribute of the cached method, which speeds
2508
up internal computations::
2509
2510
sage: a.f.cache is b.f.get_cache() is c.f._cachedmethod._get_instance_cache(c)
2511
True
2512
2513
"""
2514
if inst is None:
2515
return {}
2516
try:
2517
P = inst.parent()
2518
return P.__dict__.setdefault(self._cache_name, {})
2519
except AttributeError:
2520
pass
2521
if not hasattr(P,'__cached_methods'):
2522
raise TypeError, "The parent of this element does not allow attribute assignment\n and does not descend from the Parent base class.\n Can not use CachedInParentMethod."
2523
if P.__cached_methods is None:
2524
P.__cached_methods = {}
2525
return (<dict>P.__cached_methods).setdefault(self._cache_name, {})
2526
2527
def __get__(self, inst, cls): #cls=None):
2528
"""
2529
Get a CachedMethodCaller bound to this specific instance of
2530
the class of the cached-in-parent method.
2531
"""
2532
Caller = CachedMethodCaller(self, inst, cache=self._get_instance_cache(inst), inst_in_key=True)
2533
try:
2534
setattr(inst,self._cachedfunc.__name__, Caller)
2535
except AttributeError:
2536
pass
2537
return Caller
2538
2539
cached_in_parent_method = CachedInParentMethod
2540
2541
class FileCache:
2542
"""
2543
FileCache is a dictionary-like class which stores keys and
2544
values on disk. The keys take the form of a tuple (A,K)
2545
2546
- A is a tuple of objects t where each t is an exact
2547
object which is uniquely identified by a short string.
2548
2549
- K is a tuple of tuples (s,v) where s is a valid
2550
variable name and v is an exact object which is uniquely
2551
identified by a short string with letters [a-zA-Z0-9-._]
2552
2553
The primary use case is the DiskCachedFunction. If
2554
``memory_cache == True``, we maintain a cache of objects seen
2555
during this session in memory -- but we don't load them from
2556
disk until necessary. The keys and values are stored in a
2557
pair of files:
2558
2559
- ``prefix-argstring.key.sobj`` contains the ``key`` only,
2560
- ``prefix-argstring.sobj`` contains the tuple ``(key,val)``
2561
2562
where ``self[key] == val``.
2563
2564
NOTE:
2565
2566
We assume that each FileCache lives in its own directory.
2567
Use **extreme** caution if you wish to break that assumption.
2568
"""
2569
def __init__(self, dir, prefix = '', memory_cache = False):
2570
"""
2571
EXAMPLES::
2572
2573
sage: from sage.misc.cachefunc import FileCache
2574
sage: dir = tmp_dir()
2575
sage: FC = FileCache(dir, memory_cache = True)
2576
sage: FC[((),())] = 1
2577
sage: FC[((1,2),())] = 2
2578
sage: FC[((),())]
2579
1
2580
"""
2581
from sage.misc.misc import sage_makedirs
2582
if len(dir) == 0 or dir[-1] != '/':
2583
dir += '/'
2584
self._dir = dir
2585
sage_makedirs(dir)
2586
2587
self._prefix = prefix + '-'
2588
2589
if memory_cache:
2590
self._cache = {}
2591
else:
2592
self._cache = None
2593
2594
def file_list(self):
2595
"""
2596
Returns the list of files corresponding to self.
2597
2598
EXAMPLES::
2599
2600
sage: from sage.misc.cachefunc import FileCache
2601
sage: dir = tmp_dir()
2602
sage: FC = FileCache(dir, memory_cache = True, prefix='t')
2603
sage: FC[((),())] = 1
2604
sage: FC[((1,2),())] = 2
2605
sage: FC[((1,),(('a',1),))] = 3
2606
sage: for f in sorted(FC.file_list()): print f[len(dir):]
2607
t-.key.sobj
2608
t-.sobj
2609
t-1_2.key.sobj
2610
t-1_2.sobj
2611
t-a-1.1.key.sobj
2612
t-a-1.1.sobj
2613
"""
2614
files = []
2615
prefix = self._prefix
2616
dir = self._dir
2617
l = len(prefix)
2618
for f in os.listdir(dir):
2619
if f[:l] == prefix:
2620
files.append( dir + f )
2621
return files
2622
2623
def items(self):
2624
"""
2625
Returns a list of tuples ``(k,v)`` where ``self[k] = v``.
2626
2627
EXAMPLES::
2628
2629
sage: from sage.misc.cachefunc import FileCache
2630
sage: dir = tmp_dir()
2631
sage: FC = FileCache(dir, memory_cache = False)
2632
sage: FC[((),())] = 1
2633
sage: FC[((1,2),())] = 2
2634
sage: FC[((1,),(('a',1),))] = 3
2635
sage: I = FC.items()
2636
sage: I.sort(); print I
2637
[(((), ()), 1), (((1,), (('a', 1),)), 3), (((1, 2), ()), 2)]
2638
"""
2639
return [(k,self[k]) for k in self]
2640
2641
def values(self):
2642
"""
2643
Returns a list of values that are stored in ``self``.
2644
2645
EXAMPLES::
2646
2647
sage: from sage.misc.cachefunc import FileCache
2648
sage: dir = tmp_dir()
2649
sage: FC = FileCache(dir, memory_cache = False)
2650
sage: FC[((),())] = 1
2651
sage: FC[((1,2),())] = 2
2652
sage: FC[((1,),(('a',1),))] = 3
2653
sage: FC[((),(('a',1),))] = 4
2654
sage: v = FC.values()
2655
sage: v.sort(); print v
2656
[1, 2, 3, 4]
2657
"""
2658
return [self[k] for k in self]
2659
2660
def __iter__(self):
2661
"""
2662
Returns a list of keys of ``self``.
2663
2664
EXAMPLES::
2665
2666
sage: from sage.misc.cachefunc import FileCache
2667
sage: dir = tmp_dir()
2668
sage: FC = FileCache(dir, memory_cache = False)
2669
sage: FC[((),())] = 1
2670
sage: FC[((1,2),())] = 2
2671
sage: FC[((1,),(('a',1),))] = 3
2672
sage: for k in sorted(FC): print k
2673
((), ())
2674
((1,), (('a', 1),))
2675
((1, 2), ())
2676
"""
2677
return self.keys().__iter__()
2678
2679
def keys(self):
2680
"""
2681
Returns a list of keys ``k`` where ``self[k]`` is defined.
2682
2683
EXAMPLES::
2684
2685
sage: from sage.misc.cachefunc import FileCache
2686
sage: dir = tmp_dir()
2687
sage: FC = FileCache(dir, memory_cache = False)
2688
sage: FC[((),())] = 1
2689
sage: FC[((1,2),())] = 2
2690
sage: FC[((1,),(('a',1),))] = 3
2691
sage: K = FC.keys()
2692
sage: K.sort(); print K
2693
[((), ()), ((1,), (('a', 1),)), ((1, 2), ())]
2694
"""
2695
cdef list K = []
2696
from sage.structure.sage_object import load
2697
for f in self.file_list():
2698
if f[-9:] == '.key.sobj':
2699
K.append(load(f))
2700
return K
2701
2702
def _filename(self, key):
2703
"""
2704
Computes the filename associated with a certain key.
2705
2706
EXAMPLES::
2707
2708
sage: from sage.misc.cachefunc import FileCache
2709
sage: dir = tmp_dir()
2710
sage: FC = FileCache(dir, memory_cache = False, prefix='foo')
2711
sage: N = FC._filename(((1,2), (('a',1),('b',2))))
2712
sage: print N[len(dir):]
2713
foo-a-1_b-2.1_2
2714
sage: N = FC._filename(((), (('a',1),('b',2))))
2715
sage: print N[len(dir):]
2716
foo-a-1_b-2
2717
sage: N = FC._filename(((1,2), ()))
2718
sage: print N[len(dir):]
2719
foo-1_2
2720
"""
2721
a,k = key
2722
kwdstr = '_'.join(['%s-%s'%x for x in k])
2723
argstr = '_'.join(['%s'%x for x in a])
2724
if kwdstr and argstr:
2725
keystr = kwdstr + '.' + argstr
2726
else:
2727
keystr = kwdstr + argstr
2728
return self._dir + self._prefix + keystr
2729
2730
def has_key(self, key):
2731
"""
2732
Returns ``True`` if ``self[key]`` is defined and False otherwise.
2733
2734
EXAMPLES::
2735
2736
sage: from sage.misc.cachefunc import FileCache
2737
sage: dir = tmp_dir()
2738
sage: FC = FileCache(dir, memory_cache = False, prefix='foo')
2739
sage: k = ((),(('a',1),))
2740
sage: FC[k] = True
2741
sage: FC.has_key(k)
2742
True
2743
sage: FC.has_key(((),()))
2744
False
2745
"""
2746
return os.path.exists(self._filename(key) + '.key.sobj')
2747
2748
def __getitem__(self, key):
2749
"""
2750
Returns the value set by ``self[key] = val``, in this session
2751
or a previous one.
2752
2753
EXAMPLES::
2754
2755
sage: from sage.misc.cachefunc import FileCache
2756
sage: dir = tmp_dir()
2757
sage: FC1 = FileCache(dir, memory_cache = False, prefix='foo')
2758
sage: FC2 = FileCache(dir, memory_cache = False, prefix='foo')
2759
sage: k = ((),(('a',1),))
2760
sage: t = randint(0, 1000)
2761
sage: FC1[k] = t
2762
sage: FC2[k] == FC1[k] == t
2763
True
2764
sage: FC1[(1,2),(('a',4),('b',2))]
2765
Traceback (most recent call last):
2766
...
2767
KeyError: ((1, 2), (('a', 4), ('b', 2)))
2768
2769
"""
2770
from sage.structure.sage_object import load
2771
2772
cache = self._cache
2773
if cache is not None:
2774
if cache.has_key(key):
2775
return cache[key]
2776
2777
f = self._filename(key) + '.sobj'
2778
try:
2779
k,v = load(f)
2780
except IOError:
2781
raise KeyError, key
2782
if k != key:
2783
raise RuntimeError, "cache corrupted"
2784
2785
if cache is not None:
2786
cache[key] = v
2787
return v
2788
2789
def __setitem__(self, key, value):
2790
"""
2791
Sets ``self[key] = value`` and stores both key and value on
2792
disk.
2793
2794
EXAMPLES::
2795
2796
sage: from sage.misc.cachefunc import FileCache
2797
sage: dir = tmp_dir()
2798
sage: FC1 = FileCache(dir, memory_cache = False, prefix='foo')
2799
sage: FC2 = FileCache(dir, memory_cache = False, prefix='foo')
2800
sage: k = ((),(('a',1),))
2801
sage: t = randint(0, 1000)
2802
sage: FC1[k] = t
2803
sage: FC2[k] == t
2804
True
2805
sage: FC1[k] = 2000
2806
sage: FC2[k]!= t
2807
True
2808
"""
2809
from sage.structure.sage_object import save
2810
2811
f = self._filename(key)
2812
2813
save(key, f+'.key.sobj')
2814
save((key,value), f + '.sobj')
2815
if self._cache is not None:
2816
self._cache[key] = value
2817
2818
def __delitem__(self, key):
2819
"""
2820
Delete the key,value pair from self and unlink the associated
2821
files from the file cache.
2822
2823
EXAMPLES::
2824
2825
sage: from sage.misc.cachefunc import FileCache
2826
sage: dir = tmp_dir()
2827
sage: FC1 = FileCache(dir, memory_cache = False, prefix='foo')
2828
sage: FC2 = FileCache(dir, memory_cache = False, prefix='foo')
2829
sage: k = ((),(('a',1),))
2830
sage: t = randint(0, 1000)
2831
sage: FC1[k] = t
2832
sage: del FC2[k]
2833
sage: FC1.has_key(k)
2834
False
2835
"""
2836
f = self._filename(key)
2837
cache = self._cache
2838
if cache is not None and cache.has_key(key):
2839
del self._cache[key]
2840
if os.path.exists(f + '.sobj'):
2841
os.remove(f + '.sobj')
2842
if os.path.exists(f + '.key.sobj'):
2843
os.remove(f + '.key.sobj')
2844
2845
2846
class DiskCachedFunction(CachedFunction):
2847
"""
2848
Works similar to CachedFunction, but instead, we keep the
2849
cache on disk (optionally, we keep it in memory too).
2850
2851
EXAMPLES::
2852
2853
sage: from sage.misc.cachefunc import DiskCachedFunction
2854
sage: dir = tmp_dir()
2855
sage: factor = DiskCachedFunction(factor, dir, memory_cache=True)
2856
sage: f = factor(2775); f
2857
3 * 5^2 * 37
2858
sage: f is factor(2775)
2859
True
2860
"""
2861
def __init__(self, f, dir, memory_cache=False):
2862
"""
2863
EXAMPLES::
2864
2865
sage: from sage.misc.cachefunc import DiskCachedFunction
2866
sage: def foo(x): sleep(x)
2867
sage: dir = tmp_dir()
2868
sage: bar = DiskCachedFunction(foo, dir, memory_cache = False)
2869
sage: w = walltime()
2870
sage: for i in range(10): bar(1)
2871
sage: walltime(w) < 2
2872
True
2873
"""
2874
CachedFunction.__init__(self, f)
2875
prefix = f.__name__
2876
self.cache = FileCache(dir, prefix=prefix, memory_cache = memory_cache)
2877
2878
2879
class disk_cached_function:
2880
"""
2881
Decorator for :class:`DiskCachedFunction`.
2882
2883
EXAMPLES::
2884
2885
sage: dir = tmp_dir()
2886
sage: @disk_cached_function(dir)
2887
... def foo(x): return next_prime(2^x)%x
2888
sage: x = foo(200);x
2889
11
2890
sage: @disk_cached_function(dir)
2891
... def foo(x): return 1/x
2892
sage: foo(200)
2893
11
2894
sage: foo.clear_cache()
2895
sage: foo(200)
2896
1/200
2897
"""
2898
def __init__(self, dir, memory_cache = False):
2899
"""
2900
EXAMPLES::
2901
2902
sage: dir = tmp_dir()
2903
sage: @disk_cached_function(dir, memory_cache=True)
2904
... def foo(x): return next_prime(2^x)
2905
sage: x = foo(200)
2906
sage: x is foo(200)
2907
True
2908
sage: @disk_cached_function(dir, memory_cache=False)
2909
... def foo(x): return next_prime(2^x)
2910
sage: x is foo(200)
2911
False
2912
"""
2913
self._dir = dir
2914
self._memory_cache = memory_cache
2915
2916
def __call__(self, f):
2917
"""
2918
EXAMPLES::
2919
2920
sage: dir = tmp_dir()
2921
sage: @disk_cached_function(dir)
2922
... def foo(x): return ModularSymbols(x)
2923
sage: foo(389)
2924
Modular Symbols space of dimension 65 for Gamma_0(389) of weight 2 with sign 0 over Rational Field
2925
"""
2926
return DiskCachedFunction(f, self._dir, memory_cache = self._memory_cache)
2927
2928
class ClearCacheOnPickle(object):
2929
r"""
2930
This class implements an appropriate ``__getstate__`` method that
2931
clears the cache of the methods (see @cached_method) before
2932
passing them on to the caller, typically the pickle and copy modules.
2933
2934
The implemented ``__getstate__`` method calls the ``__getstate__``
2935
methods of classes later in the method resolution
2936
order. Therefore, classes which want this behaviour should inherit
2937
first from this one.
2938
2939
EXAMPLE:
2940
2941
In the following example, we create a Python class that inherits
2942
from multivariate polynomial ideals, but does not pickle cached
2943
values. We provide the definition in Cython, however, since
2944
interactive Cython definitions provide introspection by trac
2945
ticket #9976, whereas Python definitions don't.
2946
::
2947
2948
sage: P.<a,b,c,d> = QQ[]
2949
sage: I = P*[a,b]
2950
sage: classdef = ['from sage.misc.cachefunc import ClearCacheOnPickle',
2951
... 'from sage.all import QQ',
2952
... 'P = QQ["a","b","c","d"]; I = P*[P.gen(0),P.gen(1)]',
2953
... 'class MyClass(ClearCacheOnPickle,I.__class__):',
2954
... ' def __init__(self,ring,gens):',
2955
... ' I.__class__.__init__(self,ring,gens)',
2956
... ' def __getnewargs__(self):',
2957
... ' return (self._Ideal_generic__ring,self._Ideal_generic__gens)']
2958
sage: cython('\n'.join(classdef))
2959
2960
We destroy the cache of two methods of ``I`` on purpose
2961
(demonstrating that the two different implementations of cached
2962
methods are correctly dealt with). Pickling ``I`` preserves the
2963
cache::
2964
2965
sage: I.gens.set_cache('bar')
2966
sage: I.groebner_basis.set_cache('foo',algorithm='singular')
2967
sage: J = loads(dumps(I))
2968
sage: J.gens()
2969
'bar'
2970
sage: J.groebner_basis(algorithm='singular')
2971
'foo'
2972
2973
However, if we have an ideal that additionally descends from
2974
:class:`ClearCacheOnPickle`, the carefully corrupted cache is not
2975
pickled::
2976
2977
sage: A = MyClass(P,[a,b])
2978
sage: A
2979
Ideal (a, b) of Multivariate Polynomial Ring in a, b, c, d over Rational Field
2980
sage: A.gens.set_cache('foo')
2981
sage: A.groebner_basis.set_cache('bar',algorithm='singular')
2982
sage: A.gens()
2983
'foo'
2984
sage: A.groebner_basis(algorithm='singular')
2985
'bar'
2986
sage: B = loads(dumps(A))
2987
sage: B.gens()
2988
[a, b]
2989
sage: B.groebner_basis(algorithm='singular')
2990
[a, b]
2991
sage: A.gens()
2992
'foo'
2993
2994
"""
2995
def __getstate__(self):
2996
r"""
2997
The idea is to remove that might provide a cache to some cached method
2998
from the return value of the ``__getstate__`` method.
2999
3000
EXAMPLE:
3001
3002
In the following example, we create a Python class that
3003
inherits from multivariate polynomial ideals, but clears the
3004
cache as well.
3005
3006
sage: P.<a,b,c,d> = QQ[]
3007
sage: I = P*[a,b]
3008
3009
We destroy the cache of two methods if ``I`` on purpose
3010
(demonstrating that the two different implementations of cached
3011
methods are correctly dealt with). Pickling ``I`` preserves the
3012
cache::
3013
3014
sage: I.gens.set_cache('bar')
3015
sage: I.groebner_basis.set_cache('foo',algorithm='singular')
3016
sage: J = loads(dumps(I))
3017
sage: J.gens()
3018
'bar'
3019
sage: J.groebner_basis(algorithm='singular')
3020
'foo'
3021
3022
However, if we do the same with a class that additionally
3023
descends from :class:`ClearCacheOnPickle`, the cache is not
3024
pickled. We provide the definition in Cython, however, since
3025
interactive Cython definitions provide introspection by trac
3026
ticket #9976, whereas Python definitions don't.
3027
::
3028
3029
sage: classdef = ['from sage.misc.cachefunc import ClearCacheOnPickle',
3030
... 'from sage.all import QQ',
3031
... 'from sage.rings.polynomial.multi_polynomial_ideal import MPolynomialIdeal',
3032
... 'class MyClass(ClearCacheOnPickle,MPolynomialIdeal):',
3033
... ' def __init__(self,ring,gens):',
3034
... ' MPolynomialIdeal.__init__(self,ring,gens)',
3035
... ' def __getnewargs__(self):',
3036
... ' return (self._Ideal_generic__ring,self._Ideal_generic__gens)']
3037
sage: cython('\n'.join(classdef))
3038
sage: A = MyClass(P,[a,b])
3039
sage: A
3040
Ideal (a, b) of Multivariate Polynomial Ring in a, b, c, d over Rational Field
3041
sage: A.gens.set_cache('foo')
3042
sage: A.groebner_basis.set_cache('bar',algorithm='singular')
3043
sage: A.gens()
3044
'foo'
3045
sage: A.groebner_basis(algorithm='singular')
3046
'bar'
3047
sage: B = loads(dumps(A))
3048
sage: B.gens()
3049
[a, b]
3050
sage: B.groebner_basis(algorithm='singular')
3051
[a, b]
3052
sage: A.gens()
3053
'foo'
3054
3055
And here is why the example works::
3056
3057
sage: ST = I.__getstate__(); ST[0],sorted(ST[1].items())
3058
(Monoid of ideals of Multivariate Polynomial Ring in a, b, c, d over Rational Field, [('_Ideal_generic__gens', (a, b)), ('_Ideal_generic__ring', Multivariate Polynomial Ring in a, b, c, d over Rational Field), ('_cache__groebner_basis', {(('singular', None, None, False), ()): 'foo'}), ('_gb_by_ordering', {}), ('gens', Cached version of <function gens at 0x...>), ('groebner_basis', Cached version of <function groebner_basis at 0x...>)])
3059
sage: ST = A.__getstate__(); ST[0],sorted(ST[1].items())
3060
(Monoid of ideals of Multivariate Polynomial Ring in a, b, c, d over Rational Field, [('_Ideal_generic__gens', (a, b)), ('_Ideal_generic__ring', Multivariate Polynomial Ring in a, b, c, d over Rational Field), ('_gb_by_ordering', {})])
3061
3062
"""
3063
OrigState = super(ClearCacheOnPickle, self).__getstate__()
3064
def clear_list(T):
3065
L = []
3066
for x in T:
3067
if isinstance(x,list):
3068
L.append(clear_list(x))
3069
elif isinstance(x,tuple):
3070
L.append(clear_tuple(x))
3071
elif isinstance(x,dict):
3072
L.append(clear_dict(x))
3073
elif not isinstance(x,CachedFunction):
3074
L.append(x)
3075
return L
3076
def clear_tuple(T):
3077
return tuple(clear_list(T))
3078
def clear_dict(T):
3079
D = {}
3080
for key,value in T.iteritems():
3081
if not ((type(key) == str and key[0:8] == '_cache__') or
3082
isinstance(value,CachedFunction)):
3083
if isinstance(value,list):
3084
D[key] = clear_list(value)
3085
elif isinstance(value,tuple):
3086
D[key] = clear_tuple(value)
3087
elif isinstance(value,dict):
3088
D[key] = clear_dict(value)
3089
else:
3090
D[key] = value
3091
return D
3092
if isinstance(OrigState,tuple):
3093
return clear_tuple(OrigState)
3094
if isinstance(OrigState,list):
3095
return clear_list(OrigState)
3096
if isinstance(OrigState,dict):
3097
return clear_dict(OrigState)
3098
return OrigState
3099
3100