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