passagemath-objects 10.6.45__cp313-cp313-musllinux_1_2_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of passagemath-objects might be problematic. Click here for more details.

Files changed (280) hide show
  1. passagemath_objects/__init__.py +3 -0
  2. passagemath_objects-10.6.45.dist-info/METADATA +115 -0
  3. passagemath_objects-10.6.45.dist-info/RECORD +280 -0
  4. passagemath_objects-10.6.45.dist-info/WHEEL +5 -0
  5. passagemath_objects-10.6.45.dist-info/top_level.txt +3 -0
  6. passagemath_objects.libs/libgmp-0e7fc84e.so.10.5.0 +0 -0
  7. sage/all__sagemath_objects.py +37 -0
  8. sage/arith/all__sagemath_objects.py +5 -0
  9. sage/arith/long.pxd +411 -0
  10. sage/arith/numerical_approx.cpython-313-x86_64-linux-musl.so +0 -0
  11. sage/arith/numerical_approx.pxd +35 -0
  12. sage/arith/numerical_approx.pyx +75 -0
  13. sage/arith/power.cpython-313-x86_64-linux-musl.so +0 -0
  14. sage/arith/power.pxd +31 -0
  15. sage/arith/power.pyx +127 -0
  16. sage/categories/action.cpython-313-x86_64-linux-musl.so +0 -0
  17. sage/categories/action.pxd +29 -0
  18. sage/categories/action.pyx +641 -0
  19. sage/categories/algebra_functor.py +745 -0
  20. sage/categories/all__sagemath_objects.py +33 -0
  21. sage/categories/basic.py +62 -0
  22. sage/categories/cartesian_product.py +295 -0
  23. sage/categories/category.py +3401 -0
  24. sage/categories/category_cy_helper.cpython-313-x86_64-linux-musl.so +0 -0
  25. sage/categories/category_cy_helper.pxd +8 -0
  26. sage/categories/category_cy_helper.pyx +322 -0
  27. sage/categories/category_singleton.cpython-313-x86_64-linux-musl.so +0 -0
  28. sage/categories/category_singleton.pxd +3 -0
  29. sage/categories/category_singleton.pyx +342 -0
  30. sage/categories/category_types.py +637 -0
  31. sage/categories/category_with_axiom.py +2876 -0
  32. sage/categories/covariant_functorial_construction.py +703 -0
  33. sage/categories/facade_sets.py +228 -0
  34. sage/categories/functor.cpython-313-x86_64-linux-musl.so +0 -0
  35. sage/categories/functor.pxd +7 -0
  36. sage/categories/functor.pyx +691 -0
  37. sage/categories/homset.py +1338 -0
  38. sage/categories/homsets.py +364 -0
  39. sage/categories/isomorphic_objects.py +73 -0
  40. sage/categories/map.cpython-313-x86_64-linux-musl.so +0 -0
  41. sage/categories/map.pxd +34 -0
  42. sage/categories/map.pyx +2106 -0
  43. sage/categories/morphism.cpython-313-x86_64-linux-musl.so +0 -0
  44. sage/categories/morphism.pxd +14 -0
  45. sage/categories/morphism.pyx +895 -0
  46. sage/categories/objects.py +167 -0
  47. sage/categories/primer.py +1696 -0
  48. sage/categories/pushout.py +4834 -0
  49. sage/categories/quotients.py +64 -0
  50. sage/categories/realizations.py +200 -0
  51. sage/categories/sets_cat.py +3290 -0
  52. sage/categories/sets_with_partial_maps.py +52 -0
  53. sage/categories/subobjects.py +64 -0
  54. sage/categories/subquotients.py +21 -0
  55. sage/categories/with_realizations.py +311 -0
  56. sage/cpython/__init__.py +19 -0
  57. sage/cpython/_py2_random.py +619 -0
  58. sage/cpython/all.py +3 -0
  59. sage/cpython/atexit.cpython-313-x86_64-linux-musl.so +0 -0
  60. sage/cpython/atexit.pyx +269 -0
  61. sage/cpython/builtin_types.cpython-313-x86_64-linux-musl.so +0 -0
  62. sage/cpython/builtin_types.pyx +7 -0
  63. sage/cpython/cython_metaclass.cpython-313-x86_64-linux-musl.so +0 -0
  64. sage/cpython/cython_metaclass.h +117 -0
  65. sage/cpython/cython_metaclass.pxd +3 -0
  66. sage/cpython/cython_metaclass.pyx +130 -0
  67. sage/cpython/debug.cpython-313-x86_64-linux-musl.so +0 -0
  68. sage/cpython/debug.pyx +302 -0
  69. sage/cpython/dict_del_by_value.cpython-313-x86_64-linux-musl.so +0 -0
  70. sage/cpython/dict_del_by_value.pxd +9 -0
  71. sage/cpython/dict_del_by_value.pyx +191 -0
  72. sage/cpython/dict_internal.h +245 -0
  73. sage/cpython/getattr.cpython-313-x86_64-linux-musl.so +0 -0
  74. sage/cpython/getattr.pxd +9 -0
  75. sage/cpython/getattr.pyx +439 -0
  76. sage/cpython/pycore_long.h +97 -0
  77. sage/cpython/pycore_long.pxd +10 -0
  78. sage/cpython/python_debug.h +44 -0
  79. sage/cpython/python_debug.pxd +47 -0
  80. sage/cpython/pyx_visit.h +13 -0
  81. sage/cpython/string.cpython-313-x86_64-linux-musl.so +0 -0
  82. sage/cpython/string.pxd +76 -0
  83. sage/cpython/string.pyx +34 -0
  84. sage/cpython/string_impl.h +60 -0
  85. sage/cpython/type.cpython-313-x86_64-linux-musl.so +0 -0
  86. sage/cpython/type.pxd +2 -0
  87. sage/cpython/type.pyx +40 -0
  88. sage/cpython/wrapperdescr.pxd +67 -0
  89. sage/ext/all__sagemath_objects.py +3 -0
  90. sage/ext/ccobject.h +64 -0
  91. sage/ext/cplusplus.pxd +17 -0
  92. sage/ext/mod_int.h +30 -0
  93. sage/ext/mod_int.pxd +24 -0
  94. sage/ext/stdsage.pxd +39 -0
  95. sage/groups/all__sagemath_objects.py +1 -0
  96. sage/groups/group.cpython-313-x86_64-linux-musl.so +0 -0
  97. sage/groups/group.pxd +14 -0
  98. sage/groups/group.pyx +322 -0
  99. sage/groups/old.cpython-313-x86_64-linux-musl.so +0 -0
  100. sage/groups/old.pxd +14 -0
  101. sage/groups/old.pyx +219 -0
  102. sage/libs/all__sagemath_objects.py +3 -0
  103. sage/libs/gmp/__init__.py +1 -0
  104. sage/libs/gmp/all.pxd +6 -0
  105. sage/libs/gmp/binop.pxd +23 -0
  106. sage/libs/gmp/misc.pxd +8 -0
  107. sage/libs/gmp/mpf.pxd +88 -0
  108. sage/libs/gmp/mpn.pxd +57 -0
  109. sage/libs/gmp/mpq.pxd +57 -0
  110. sage/libs/gmp/mpz.pxd +202 -0
  111. sage/libs/gmp/pylong.cpython-313-x86_64-linux-musl.so +0 -0
  112. sage/libs/gmp/pylong.pxd +12 -0
  113. sage/libs/gmp/pylong.pyx +150 -0
  114. sage/libs/gmp/random.pxd +25 -0
  115. sage/libs/gmp/randomize.pxd +59 -0
  116. sage/libs/gmp/types.pxd +53 -0
  117. sage/libs/gmpxx.pxd +19 -0
  118. sage/misc/abstract_method.py +276 -0
  119. sage/misc/all__sagemath_objects.py +43 -0
  120. sage/misc/bindable_class.py +253 -0
  121. sage/misc/c3_controlled.cpython-313-x86_64-linux-musl.so +0 -0
  122. sage/misc/c3_controlled.pxd +2 -0
  123. sage/misc/c3_controlled.pyx +1402 -0
  124. sage/misc/cachefunc.cpython-313-x86_64-linux-musl.so +0 -0
  125. sage/misc/cachefunc.pxd +43 -0
  126. sage/misc/cachefunc.pyx +3781 -0
  127. sage/misc/call.py +188 -0
  128. sage/misc/classcall_metaclass.cpython-313-x86_64-linux-musl.so +0 -0
  129. sage/misc/classcall_metaclass.pxd +14 -0
  130. sage/misc/classcall_metaclass.pyx +599 -0
  131. sage/misc/constant_function.cpython-313-x86_64-linux-musl.so +0 -0
  132. sage/misc/constant_function.pyx +130 -0
  133. sage/misc/decorators.py +747 -0
  134. sage/misc/fast_methods.cpython-313-x86_64-linux-musl.so +0 -0
  135. sage/misc/fast_methods.pxd +20 -0
  136. sage/misc/fast_methods.pyx +351 -0
  137. sage/misc/flatten.py +90 -0
  138. sage/misc/fpickle.cpython-313-x86_64-linux-musl.so +0 -0
  139. sage/misc/fpickle.pyx +177 -0
  140. sage/misc/function_mangling.cpython-313-x86_64-linux-musl.so +0 -0
  141. sage/misc/function_mangling.pxd +11 -0
  142. sage/misc/function_mangling.pyx +308 -0
  143. sage/misc/inherit_comparison.cpython-313-x86_64-linux-musl.so +0 -0
  144. sage/misc/inherit_comparison.pxd +5 -0
  145. sage/misc/inherit_comparison.pyx +105 -0
  146. sage/misc/instancedoc.cpython-313-x86_64-linux-musl.so +0 -0
  147. sage/misc/instancedoc.pyx +331 -0
  148. sage/misc/lazy_attribute.cpython-313-x86_64-linux-musl.so +0 -0
  149. sage/misc/lazy_attribute.pyx +607 -0
  150. sage/misc/lazy_format.py +135 -0
  151. sage/misc/lazy_import.cpython-313-x86_64-linux-musl.so +0 -0
  152. sage/misc/lazy_import.pyx +1299 -0
  153. sage/misc/lazy_import_cache.py +36 -0
  154. sage/misc/lazy_list.cpython-313-x86_64-linux-musl.so +0 -0
  155. sage/misc/lazy_list.pxd +19 -0
  156. sage/misc/lazy_list.pyx +1187 -0
  157. sage/misc/lazy_string.cpython-313-x86_64-linux-musl.so +0 -0
  158. sage/misc/lazy_string.pxd +7 -0
  159. sage/misc/lazy_string.pyx +546 -0
  160. sage/misc/misc.py +1066 -0
  161. sage/misc/misc_c.cpython-313-x86_64-linux-musl.so +0 -0
  162. sage/misc/misc_c.pxd +3 -0
  163. sage/misc/misc_c.pyx +766 -0
  164. sage/misc/namespace_package.py +37 -0
  165. sage/misc/nested_class.cpython-313-x86_64-linux-musl.so +0 -0
  166. sage/misc/nested_class.pxd +3 -0
  167. sage/misc/nested_class.pyx +394 -0
  168. sage/misc/persist.cpython-313-x86_64-linux-musl.so +0 -0
  169. sage/misc/persist.pyx +1251 -0
  170. sage/misc/prandom.py +418 -0
  171. sage/misc/randstate.cpython-313-x86_64-linux-musl.so +0 -0
  172. sage/misc/randstate.pxd +30 -0
  173. sage/misc/randstate.pyx +1059 -0
  174. sage/misc/repr.py +203 -0
  175. sage/misc/reset.cpython-313-x86_64-linux-musl.so +0 -0
  176. sage/misc/reset.pyx +196 -0
  177. sage/misc/sage_ostools.cpython-313-x86_64-linux-musl.so +0 -0
  178. sage/misc/sage_ostools.pyx +323 -0
  179. sage/misc/sage_timeit.py +275 -0
  180. sage/misc/sage_timeit_class.cpython-313-x86_64-linux-musl.so +0 -0
  181. sage/misc/sage_timeit_class.pyx +120 -0
  182. sage/misc/sage_unittest.py +637 -0
  183. sage/misc/sageinspect.py +2768 -0
  184. sage/misc/session.cpython-313-x86_64-linux-musl.so +0 -0
  185. sage/misc/session.pyx +392 -0
  186. sage/misc/superseded.py +557 -0
  187. sage/misc/test_nested_class.py +228 -0
  188. sage/misc/timing.py +264 -0
  189. sage/misc/unknown.py +222 -0
  190. sage/misc/verbose.py +253 -0
  191. sage/misc/weak_dict.cpython-313-x86_64-linux-musl.so +0 -0
  192. sage/misc/weak_dict.pxd +15 -0
  193. sage/misc/weak_dict.pyx +1231 -0
  194. sage/modules/all__sagemath_objects.py +1 -0
  195. sage/modules/module.cpython-313-x86_64-linux-musl.so +0 -0
  196. sage/modules/module.pxd +5 -0
  197. sage/modules/module.pyx +329 -0
  198. sage/rings/all__sagemath_objects.py +3 -0
  199. sage/rings/integer_fake.h +22 -0
  200. sage/rings/integer_fake.pxd +55 -0
  201. sage/sets/all__sagemath_objects.py +3 -0
  202. sage/sets/pythonclass.cpython-313-x86_64-linux-musl.so +0 -0
  203. sage/sets/pythonclass.pxd +9 -0
  204. sage/sets/pythonclass.pyx +247 -0
  205. sage/structure/__init__.py +4 -0
  206. sage/structure/all.py +30 -0
  207. sage/structure/category_object.cpython-313-x86_64-linux-musl.so +0 -0
  208. sage/structure/category_object.pxd +28 -0
  209. sage/structure/category_object.pyx +1087 -0
  210. sage/structure/coerce.cpython-313-x86_64-linux-musl.so +0 -0
  211. sage/structure/coerce.pxd +44 -0
  212. sage/structure/coerce.pyx +2107 -0
  213. sage/structure/coerce_actions.cpython-313-x86_64-linux-musl.so +0 -0
  214. sage/structure/coerce_actions.pxd +27 -0
  215. sage/structure/coerce_actions.pyx +988 -0
  216. sage/structure/coerce_dict.cpython-313-x86_64-linux-musl.so +0 -0
  217. sage/structure/coerce_dict.pxd +51 -0
  218. sage/structure/coerce_dict.pyx +1557 -0
  219. sage/structure/coerce_exceptions.py +23 -0
  220. sage/structure/coerce_maps.cpython-313-x86_64-linux-musl.so +0 -0
  221. sage/structure/coerce_maps.pxd +28 -0
  222. sage/structure/coerce_maps.pyx +718 -0
  223. sage/structure/debug_options.cpython-313-x86_64-linux-musl.so +0 -0
  224. sage/structure/debug_options.pxd +6 -0
  225. sage/structure/debug_options.pyx +54 -0
  226. sage/structure/dynamic_class.py +541 -0
  227. sage/structure/element.cpython-313-x86_64-linux-musl.so +0 -0
  228. sage/structure/element.pxd +272 -0
  229. sage/structure/element.pyx +4772 -0
  230. sage/structure/element_wrapper.cpython-313-x86_64-linux-musl.so +0 -0
  231. sage/structure/element_wrapper.pxd +12 -0
  232. sage/structure/element_wrapper.pyx +582 -0
  233. sage/structure/factorization.py +1422 -0
  234. sage/structure/factorization_integer.py +105 -0
  235. sage/structure/factory.cpython-313-x86_64-linux-musl.so +0 -0
  236. sage/structure/factory.pyx +786 -0
  237. sage/structure/formal_sum.py +489 -0
  238. sage/structure/gens_py.py +73 -0
  239. sage/structure/global_options.py +1743 -0
  240. sage/structure/indexed_generators.py +863 -0
  241. sage/structure/list_clone.cpython-313-x86_64-linux-musl.so +0 -0
  242. sage/structure/list_clone.pxd +65 -0
  243. sage/structure/list_clone.pyx +1867 -0
  244. sage/structure/list_clone_demo.cpython-313-x86_64-linux-musl.so +0 -0
  245. sage/structure/list_clone_demo.pyx +248 -0
  246. sage/structure/list_clone_timings.py +179 -0
  247. sage/structure/list_clone_timings_cy.cpython-313-x86_64-linux-musl.so +0 -0
  248. sage/structure/list_clone_timings_cy.pyx +86 -0
  249. sage/structure/mutability.cpython-313-x86_64-linux-musl.so +0 -0
  250. sage/structure/mutability.pxd +21 -0
  251. sage/structure/mutability.pyx +348 -0
  252. sage/structure/nonexact.py +69 -0
  253. sage/structure/parent.cpython-313-x86_64-linux-musl.so +0 -0
  254. sage/structure/parent.pxd +112 -0
  255. sage/structure/parent.pyx +3093 -0
  256. sage/structure/parent_base.cpython-313-x86_64-linux-musl.so +0 -0
  257. sage/structure/parent_base.pxd +13 -0
  258. sage/structure/parent_base.pyx +44 -0
  259. sage/structure/parent_gens.cpython-313-x86_64-linux-musl.so +0 -0
  260. sage/structure/parent_gens.pxd +22 -0
  261. sage/structure/parent_gens.pyx +377 -0
  262. sage/structure/parent_old.cpython-313-x86_64-linux-musl.so +0 -0
  263. sage/structure/parent_old.pxd +25 -0
  264. sage/structure/parent_old.pyx +294 -0
  265. sage/structure/proof/__init__.py +1 -0
  266. sage/structure/proof/all.py +243 -0
  267. sage/structure/proof/proof.py +300 -0
  268. sage/structure/richcmp.cpython-313-x86_64-linux-musl.so +0 -0
  269. sage/structure/richcmp.pxd +213 -0
  270. sage/structure/richcmp.pyx +495 -0
  271. sage/structure/sage_object.cpython-313-x86_64-linux-musl.so +0 -0
  272. sage/structure/sage_object.pxd +3 -0
  273. sage/structure/sage_object.pyx +988 -0
  274. sage/structure/sage_object_test.py +19 -0
  275. sage/structure/sequence.py +937 -0
  276. sage/structure/set_factories.py +1178 -0
  277. sage/structure/set_factories_example.py +527 -0
  278. sage/structure/support_view.py +179 -0
  279. sage/structure/test_factory.py +56 -0
  280. sage/structure/unique_representation.py +1359 -0
@@ -0,0 +1,3781 @@
1
+ # sage_setup: distribution = sagemath-objects
2
+ r"""
3
+ Cached Functions and Methods
4
+
5
+ AUTHORS:
6
+
7
+ - William Stein: initial version, (inspired by conversation with Justin Walker)
8
+ - Mike Hansen: added doctests and made it work with class methods.
9
+ - Willem Jan Palenstijn: add CachedMethodCaller for binding cached methods to
10
+ instances.
11
+ - Tom Boothby: added DiskCachedFunction.
12
+ - Simon King: improved performance, more doctests, cython version,
13
+ CachedMethodCallerNoArgs, weak cached function, cached special methods.
14
+ - Julian Rueth (2014-03-19, 2014-05-09, 2014-05-12): added ``key`` parameter, allow caching
15
+ for unhashable elements, added ``do_pickle`` parameter
16
+
17
+ EXAMPLES:
18
+
19
+ By :issue:`11115`, cached functions and methods are now also
20
+ available in Cython code. The following examples cover various ways
21
+ of usage.
22
+
23
+ Python functions::
24
+
25
+ sage: @cached_function
26
+ ....: def test_pfunc(x):
27
+ ....: '''
28
+ ....: Some documentation
29
+ ....: '''
30
+ ....: return -x
31
+ sage: test_pfunc(5) is test_pfunc(5)
32
+ True
33
+
34
+ In some cases, one would only want to keep the result in cache as long
35
+ as there is any other reference to the result. By :issue:`12215`, this is
36
+ enabled for :class:`~sage.structure.unique_representation.UniqueRepresentation`,
37
+ which is used to create unique parents: If an algebraic structure, such
38
+ as a finite field, is only temporarily used, then it will not stay in
39
+ cache forever. That behaviour is implemented using ``weak_cached_function``,
40
+ that behaves the same as ``cached_function``, except that it uses a
41
+ :class:`~sage.misc.weak_dict.CachedWeakValueDictionary` for storing the results.
42
+
43
+ Cython cdef functions do not allow arbitrary decorators.
44
+ However, one can wrap a Cython function and turn it into
45
+ a cached function, by :issue:`11115`. We need to provide
46
+ the name that the wrapped method or function should have,
47
+ since otherwise the name of the original function would
48
+ be used::
49
+
50
+ sage: # needs sage.misc.cython
51
+ sage: cython('''cpdef test_funct(x): return -x''')
52
+ sage: wrapped_funct = cached_function(test_funct, name='wrapped_funct')
53
+ sage: wrapped_funct
54
+ Cached version of <cyfunction test_funct at ...>
55
+ sage: wrapped_funct.__name__
56
+ 'wrapped_funct'
57
+ sage: wrapped_funct(5)
58
+ -5
59
+ sage: wrapped_funct(5) is wrapped_funct(5)
60
+ True
61
+
62
+ We can proceed similarly for cached methods of Cython classes,
63
+ provided that they allow attribute assignment or have a public
64
+ attribute ``_cached_methods`` of type ``<dict>``. Since
65
+ :issue:`11115`, this is the case for all classes inheriting from
66
+ :class:`~sage.structure.parent.Parent`. See below for a more explicit
67
+ example. By :issue:`12951`, cached methods of extension classes can
68
+ be defined by simply using the decorator. However, an indirect
69
+ approach is still needed for cpdef methods::
70
+
71
+ sage: # needs sage.misc.cython
72
+ sage: cython_code = ['cpdef test_meth(self, x):',
73
+ ....: ' "some doc for a wrapped cython method"',
74
+ ....: ' return -x',
75
+ ....: 'from sage.misc.cachefunc import cached_method',
76
+ ....: 'from sage.structure.parent cimport Parent',
77
+ ....: 'cdef class MyClass(Parent):',
78
+ ....: ' @cached_method',
79
+ ....: ' def direct_method(self, x):',
80
+ ....: ' "Some doc for direct method"',
81
+ ....: ' return 2*x',
82
+ ....: ' wrapped_method = cached_method(test_meth,name="wrapped_method")']
83
+ sage: cython(os.linesep.join(cython_code))
84
+ sage: O = MyClass()
85
+ sage: O.direct_method
86
+ Cached version of <cyfunction MyClass.direct_method at ...>
87
+ sage: O.wrapped_method
88
+ Cached version of <cyfunction test_meth at ...>
89
+ sage: O.wrapped_method.__name__
90
+ 'wrapped_method'
91
+ sage: O.wrapped_method(5)
92
+ -5
93
+ sage: O.wrapped_method(5) is O.wrapped_method(5)
94
+ True
95
+ sage: O.direct_method(5)
96
+ 10
97
+ sage: O.direct_method(5) is O.direct_method(5)
98
+ True
99
+
100
+ By :issue:`11115`, even if a parent does not allow attribute
101
+ assignment, it can inherit a cached method from the parent class of a
102
+ category (previously, the cache would have been broken)::
103
+
104
+ sage: cython_code = ["from sage.misc.cachefunc import cached_method",
105
+ ....: "from sage.misc.cachefunc import cached_in_parent_method",
106
+ ....: "from sage.categories.category import Category",
107
+ ....: "from sage.categories.objects import Objects",
108
+ ....: "class MyCategory(Category):",
109
+ ....: " @cached_method",
110
+ ....: " def super_categories(self):",
111
+ ....: " return [Objects()]",
112
+ ....: " class ElementMethods:",
113
+ ....: " @cached_method",
114
+ ....: " def element_cache_test(self):",
115
+ ....: " return -self",
116
+ ....: " @cached_in_parent_method",
117
+ ....: " def element_via_parent_test(self):",
118
+ ....: " return -self",
119
+ ....: " class ParentMethods:",
120
+ ....: " @cached_method",
121
+ ....: " def one(self):",
122
+ ....: " return self.element_class(self,1)",
123
+ ....: " @cached_method",
124
+ ....: " def invert(self, x):",
125
+ ....: " return -x"]
126
+ sage: cython('\n'.join(cython_code)) # needs sage.misc.cython
127
+ sage: C = MyCategory() # needs sage.misc.cython
128
+
129
+ In order to keep the memory footprint of elements small, it was
130
+ decided to not support the same freedom of using cached methods
131
+ for elements: If an instance of a class derived from
132
+ :class:`~sage.structure.element.Element` does not allow attribute
133
+ assignment, then a cached method inherited from the category of
134
+ its parent will break, as in the class ``MyBrokenElement`` below.
135
+
136
+ However, there is a class :class:`~sage.structure.element.ElementWithCachedMethod`
137
+ that has generally a slower attribute access, but fully supports
138
+ cached methods. We remark, however, that cached methods are
139
+ *much* faster if attribute access works. So, we expect that
140
+ :class:`~sage.structure.element.ElementWithCachedMethod` will
141
+ hardly be used.
142
+ ::
143
+
144
+ sage: # needs sage.misc.cython
145
+ sage: cython_code = ["from sage.structure.element cimport Element, ElementWithCachedMethod", "from cpython.object cimport PyObject_RichCompare",
146
+ ....: "cdef class MyBrokenElement(Element):",
147
+ ....: " cdef public object x",
148
+ ....: " def __init__(self, P, x):",
149
+ ....: " self.x=x",
150
+ ....: " Element.__init__(self,P)",
151
+ ....: " def __neg__(self):",
152
+ ....: " return MyBrokenElement(self.parent(),-self.x)",
153
+ ....: " def _repr_(self):",
154
+ ....: " return '<%s>'%self.x",
155
+ ....: " def __hash__(self):",
156
+ ....: " return hash(self.x)",
157
+ ....: " cpdef _richcmp_(left, right, int op):",
158
+ ....: " return PyObject_RichCompare(left.x, right.x, op)",
159
+ ....: " def raw_test(self):",
160
+ ....: " return -self",
161
+ ....: "cdef class MyElement(ElementWithCachedMethod):",
162
+ ....: " cdef public object x",
163
+ ....: " def __init__(self, P, x):",
164
+ ....: " self.x=x",
165
+ ....: " ElementWithCachedMethod.__init__(self,P)",
166
+ ....: " def __neg__(self):",
167
+ ....: " return MyElement(self.parent(),-self.x)",
168
+ ....: " def _repr_(self):",
169
+ ....: " return '<%s>'%self.x",
170
+ ....: " def __hash__(self):",
171
+ ....: " return hash(self.x)",
172
+ ....: " cpdef _richcmp_(left, right, int op):",
173
+ ....: " return PyObject_RichCompare(left.x, right.x, op)",
174
+ ....: " def raw_test(self):",
175
+ ....: " return -self",
176
+ ....: "from sage.structure.parent cimport Parent",
177
+ ....: "cdef class MyParent(Parent):",
178
+ ....: " Element = MyElement"]
179
+ sage: cython('\n'.join(cython_code))
180
+ sage: P = MyParent(category=C)
181
+ sage: ebroken = MyBrokenElement(P, 5)
182
+ sage: e = MyElement(P, 5)
183
+
184
+ The cached methods inherited by the parent works::
185
+
186
+ sage: # needs sage.misc.cython
187
+ sage: P.one()
188
+ <1>
189
+ sage: P.one() is P.one()
190
+ True
191
+ sage: P.invert(e)
192
+ <-5>
193
+ sage: P.invert(e) is P.invert(e)
194
+ True
195
+
196
+ The cached methods inherited by ``MyElement`` works::
197
+
198
+ sage: # needs sage.misc.cython
199
+ sage: e.element_cache_test()
200
+ <-5>
201
+ sage: e.element_cache_test() is e.element_cache_test()
202
+ True
203
+ sage: e.element_via_parent_test()
204
+ <-5>
205
+ sage: e.element_via_parent_test() is e.element_via_parent_test()
206
+ True
207
+
208
+ The other element class can only inherit a ``cached_in_parent_method``, since
209
+ the cache is stored in the parent. In fact, equal elements share the cache,
210
+ even if they are of different types::
211
+
212
+ sage: e == ebroken # needs sage.misc.cython
213
+ True
214
+ sage: type(e) == type(ebroken) # needs sage.misc.cython
215
+ False
216
+ sage: ebroken.element_via_parent_test() is e.element_via_parent_test() # needs sage.misc.cython
217
+ True
218
+
219
+ However, the cache of the other inherited method breaks, although the method
220
+ as such works::
221
+
222
+ sage: ebroken.element_cache_test() # needs sage.misc.cython
223
+ <-5>
224
+ sage: ebroken.element_cache_test() is ebroken.element_cache_test() # needs sage.misc.cython
225
+ False
226
+
227
+ The cache can be emptied::
228
+
229
+ sage: # needs sage.misc.cython
230
+ sage: a = test_pfunc(5)
231
+ sage: test_pfunc.clear_cache()
232
+ sage: a is test_pfunc(5)
233
+ False
234
+ sage: a = P.one()
235
+ sage: P.one.clear_cache()
236
+ sage: a is P.one()
237
+ False
238
+
239
+ Since ``e`` and ``ebroken`` share the cache, when we empty it for one element
240
+ it is empty for the other as well::
241
+
242
+ sage: b = ebroken.element_via_parent_test() # needs sage.misc.cython
243
+ sage: e.element_via_parent_test.clear_cache() # needs sage.misc.cython
244
+ sage: b is ebroken.element_via_parent_test() # needs sage.misc.cython
245
+ False
246
+
247
+ Introspection works::
248
+
249
+ sage: # needs sage.misc.cython
250
+ sage: from sage.misc.edit_module import file_and_line
251
+ sage: from sage.misc.sageinspect import sage_getdoc, sage_getfile, sage_getsource
252
+ sage: print(sage_getdoc(test_pfunc))
253
+ Some documentation
254
+ sage: print(sage_getdoc(O.wrapped_method))
255
+ some doc for a wrapped cython method
256
+ <BLANKLINE>
257
+ sage: print(sage_getdoc(O.direct_method))
258
+ Some doc for direct method
259
+ <BLANKLINE>
260
+ sage: print(sage_getsource(O.wrapped_method))
261
+ cpdef test_meth(self, x):
262
+ "some doc for a wrapped cython method"
263
+ return -x
264
+ sage: print(sage_getsource(O.direct_method))
265
+ @cached_method
266
+ def direct_method(self, x):
267
+ "Some doc for direct method"
268
+ return 2*x
269
+
270
+ It is a very common special case to cache a method that has no
271
+ arguments. In that special case, the time needed to access the cache
272
+ can be drastically reduced by using a special implementation. The
273
+ cached method decorator automatically determines which implementation
274
+ ought to be chosen. A typical example is
275
+ :meth:`sage.rings.polynomial.multi_polynomial_ideal.MPolynomialIdeal.gens`
276
+ (no arguments) versus
277
+ :meth:`sage.rings.polynomial.multi_polynomial_ideal.MPolynomialIdeal.groebner_basis`
278
+ (several arguments)::
279
+
280
+ sage: P.<a,b,c,d> = QQ[]
281
+ sage: I = P * [a, b]
282
+ sage: I.gens()
283
+ [a, b]
284
+ sage: I.gens() is I.gens()
285
+ True
286
+ sage: I.groebner_basis() # needs sage.libs.singular
287
+ [a, b]
288
+ sage: I.groebner_basis() is I.groebner_basis() # needs sage.libs.singular
289
+ True
290
+ sage: type(I.gens)
291
+ <class 'sage.misc.cachefunc.CachedMethodCallerNoArgs'>
292
+ sage: type(I.groebner_basis)
293
+ <class 'sage.misc.cachefunc.CachedMethodCaller'>
294
+
295
+ By :issue:`12951`, the cached_method decorator is also supported on non-c(p)def
296
+ methods of extension classes, as long as they either support attribute assignment
297
+ or have a public attribute of type ``<dict>`` called ``_cached_methods``. The
298
+ latter is easy::
299
+
300
+ sage: # needs sage.misc.cython
301
+ sage: cython_code = [
302
+ ....: "from sage.misc.cachefunc import cached_method",
303
+ ....: "cdef class MyClass:",
304
+ ....: " cdef public dict _cached_methods",
305
+ ....: " @cached_method",
306
+ ....: " def f(self, a, b):",
307
+ ....: " return a*b"]
308
+ sage: cython(os.linesep.join(cython_code))
309
+ sage: P = MyClass()
310
+ sage: P.f(2, 3)
311
+ 6
312
+ sage: P.f(2, 3) is P.f(2, 3)
313
+ True
314
+
315
+ Providing attribute access is a bit more tricky, since it is needed that
316
+ an attribute inherited by the instance from its class can be overridden
317
+ on the instance. That is why providing a ``__getattr__`` would not be
318
+ enough in the following example::
319
+
320
+ sage: # needs sage.misc.cython
321
+ sage: cython_code = [
322
+ ....: "from sage.misc.cachefunc import cached_method",
323
+ ....: "cdef class MyOtherClass:",
324
+ ....: " cdef dict D",
325
+ ....: " def __init__(self):",
326
+ ....: " self.D = {}",
327
+ ....: " def __setattr__(self, n, v):",
328
+ ....: " self.D[n] = v",
329
+ ....: " def __getattribute__(self, n):",
330
+ ....: " try:",
331
+ ....: " return self.D[n]",
332
+ ....: " except KeyError:",
333
+ ....: " pass",
334
+ ....: " return getattr(type(self),n).__get__(self)",
335
+ ....: " @cached_method",
336
+ ....: " def f(self, a, b):",
337
+ ....: " return a+b"]
338
+ sage: cython(os.linesep.join(cython_code))
339
+ sage: Q = MyOtherClass()
340
+ sage: Q.f(2, 3)
341
+ 5
342
+ sage: Q.f(2, 3) is Q.f(2, 3)
343
+ True
344
+
345
+ Note that supporting attribute access is somehow faster than the
346
+ easier method::
347
+
348
+ sage: timeit("a = P.f(2,3)") # random # needs sage.misc.cython
349
+ 625 loops, best of 3: 1.3 µs per loop
350
+ sage: timeit("a = Q.f(2,3)") # random # needs sage.misc.cython
351
+ 625 loops, best of 3: 931 ns per loop
352
+
353
+ Some immutable objects (such as `p`-adic numbers) cannot implement a
354
+ reasonable hash function because their ``==`` operator has been
355
+ modified to return ``True`` for objects which might behave differently
356
+ in some computations::
357
+
358
+ sage: # needs sage.rings.padics
359
+ sage: K.<a> = Qq(9)
360
+ sage: b = a.add_bigoh(1)
361
+ sage: c = a + 3
362
+ sage: b
363
+ a + O(3)
364
+ sage: c
365
+ a + 3 + O(3^20)
366
+ sage: b == c
367
+ True
368
+ sage: b == a
369
+ True
370
+ sage: c == a
371
+ False
372
+
373
+ If such objects defined a non-trivial hash function, this would break
374
+ caching in many places. However, such objects should still be usable
375
+ in caches. This can be achieved by defining an appropriate method
376
+ ``_cache_key``::
377
+
378
+ sage: # needs sage.rings.padics
379
+ sage: hash(b)
380
+ Traceback (most recent call last):
381
+ ...
382
+ TypeError: ...unhashable type: 'sage.rings.padics.qadic_flint_CR.qAdicCappedRelativeElement'...
383
+ sage: from sage.misc.cachefunc import cached_method
384
+ sage: @cached_method
385
+ ....: def f(x): return x == a
386
+ sage: f(b)
387
+ True
388
+ sage: f(c) # if b and c were hashable, this would return True
389
+ False
390
+ sage: b._cache_key()
391
+ (..., ((0, 1),), 0, 1)
392
+ sage: c._cache_key()
393
+ (..., ((0, 1), (1,)), 0, 20)
394
+
395
+ .. NOTE::
396
+
397
+ This attribute will only be accessed if the object itself
398
+ is not hashable.
399
+
400
+ An implementation must make sure that for elements ``a`` and ``b``,
401
+ if ``a != b``, then also ``a._cache_key() != b._cache_key()``.
402
+ In practice this means that the ``_cache_key`` should always include
403
+ the parent as its first argument::
404
+
405
+ sage: S.<a> = Qq(4) # needs sage.rings.padics
406
+ sage: d = a.add_bigoh(1) # needs sage.rings.padics
407
+ sage: b._cache_key() == d._cache_key() # this would be True if the parents were not included
408
+ False
409
+
410
+ Note that shallow copy of mutable objects may behave unexpectedly::
411
+
412
+ sage: class Foo:
413
+ ....: @cached_method
414
+ ....: def f(self):
415
+ ....: return self.x
416
+ sage: from copy import copy, deepcopy
417
+ sage: a = Foo()
418
+ sage: a.x = 1
419
+ sage: a.f()
420
+ 1
421
+ sage: b = copy(a)
422
+ sage: b.x = 2
423
+ sage: b.f() # incorrect!
424
+ 1
425
+ sage: b.f is a.f # this is the problem
426
+ True
427
+ sage: b = deepcopy(a)
428
+ sage: b.x = 2
429
+ sage: b.f() # correct
430
+ 2
431
+ sage: b.f is a.f
432
+ False
433
+ """
434
+
435
+ # ****************************************************************************
436
+ # Copyright (C) 2008 William Stein <wstein@gmail.com>
437
+ # Mike Hansen <mhansen@gmail.com>
438
+ # 2011 Simon King <simon.king@uni-jena.de>
439
+ # 2014 Julian Rueth <julian.rueth@fsfe.org>
440
+ # 2015 Jeroen Demeyer <jdemeyer@cage.ugent.be>
441
+ #
442
+ # This program is free software: you can redistribute it and/or modify
443
+ # it under the terms of the GNU General Public License as published by
444
+ # the Free Software Foundation, either version 2 of the License, or
445
+ # (at your option) any later version.
446
+ # https://www.gnu.org/licenses/
447
+ # ****************************************************************************
448
+
449
+ cdef extern from "methodobject.h":
450
+ cdef int METH_NOARGS, METH_O
451
+ cdef int PyCFunction_GetFlags(object op) except -1
452
+
453
+ import os
454
+ from sage.misc.sageinspect import sage_getfile_relative, sage_getsourcelines, sage_getargspec
455
+ from inspect import isfunction
456
+
457
+ from sage.misc.weak_dict cimport CachedWeakValueDictionary
458
+ from sage.misc.decorators import decorator_keywords
459
+
460
+ cdef frozenset special_method_names = frozenset(
461
+ ['__abs__', '__add__',
462
+ '__and__', '__call__', '__cmp__', '__coerce__', '__complex__', '__contains__', '__del__',
463
+ '__delattr__', '__delete__', '__delitem__', '__delslice__', '__dir__', '__div__',
464
+ '__eq__', '__float__', '__floordiv__', '__format__', '__ge__', '__get__', '__getattr__',
465
+ '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__hex__',
466
+ '__iadd__', '__iand__', '__idiv__', '__ifloordiv__', '__ilshift__', '__imod__', '__imul__',
467
+ '__index__', '__init__', '__instancecheck__', '__int__', '__invert__', '__ior__', '__ipow__',
468
+ '__irshift__', '__isub__', '__iter__', '__itruediv__', '__ixor__', '__le__', '__len__',
469
+ '__length_hint__', '__long__', '__lshift__', '__lt__', '__missing__', '__mod__', '__mul__',
470
+ '__ne__', '__neg__', '__new__', '__oct__', '__or__', '__pos__', '__pow__',
471
+ '__radd__', '__rand__', '__rdiv__', '__repr__', '__reversed__', '__rfloordiv__', '__rlshift__',
472
+ '__rmod__', '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__',
473
+ '__rtruediv__', '__rxor__', '__set__', '__setattr__', '__setitem__', '__setslice__', '__sizeof__',
474
+ '__str__', '__sub__', '__subclasscheck__', '__truediv__', '__unicode__', '__xor__', 'next'])
475
+
476
+
477
+ def _cached_function_unpickle(module, name, cache=None):
478
+ """
479
+ Unpickle the cache function ``name`` defined in ``module``.
480
+
481
+ This function loads ``name`` from ``module`` (it does not restore the code
482
+ of the actual function when it was pickled.) The cache is restored from
483
+ ``cache`` if present.
484
+
485
+ INPUT:
486
+
487
+ - ``module`` -- the name of the module to import the function from
488
+ - ``name`` -- the name of the cached function
489
+ - ``cache`` -- list of cached key value pairs
490
+
491
+ TESTS::
492
+
493
+ sage: type(hilbert_class_polynomial) # needs sage.schemes
494
+ <class 'sage.misc.cachefunc.CachedFunction'>
495
+ sage: loads(dumps(hilbert_class_polynomial)) is hilbert_class_polynomial #indirect doctest # needs sage.schemes
496
+ True
497
+
498
+ Verify that the ``cache`` parameter works::
499
+
500
+ sage: @cached_function(do_pickle=True)
501
+ ....: def f(n): return n
502
+ sage: import __main__
503
+ sage: __main__.f = f
504
+ sage: f(0)
505
+ 0
506
+ sage: ((0,),()) in f.cache
507
+ True
508
+
509
+ sage: s = dumps(f)
510
+ sage: f.clear_cache()
511
+ sage: ((0,),()) in f.cache
512
+ False
513
+ sage: f = loads(s)
514
+ sage: ((0,),()) in f.cache
515
+ True
516
+ sage: f(0)
517
+ 0
518
+ """
519
+ ret = getattr(__import__(module, fromlist=['']), name)
520
+ if cache is not None:
521
+ ret.cache.update(cache)
522
+ return ret
523
+
524
+
525
+ cdef class NonpicklingDict(dict):
526
+ r"""
527
+ A special dict which does not pickle its contents.
528
+
529
+ EXAMPLES::
530
+
531
+ sage: from sage.misc.cachefunc import NonpicklingDict
532
+ sage: d = NonpicklingDict()
533
+ sage: d[0] = 0
534
+ sage: loads(dumps(d))
535
+ {}
536
+ """
537
+ def __reduce__(self):
538
+ r"""
539
+ Return data required to unpickle this dictionary.
540
+
541
+ EXAMPLES::
542
+
543
+ sage: from sage.misc.cachefunc import NonpicklingDict
544
+ sage: d = NonpicklingDict()
545
+ sage: d[0] = 0
546
+ sage: d.__reduce__()
547
+ (<class 'sage.misc.cachefunc.NonpicklingDict'>, ())
548
+ """
549
+ return NonpicklingDict, ()
550
+
551
+
552
+ cdef unhashable_key = object()
553
+
554
+ cpdef inline dict_key(o):
555
+ """
556
+ Return a key to cache object ``o`` in a dict.
557
+
558
+ This is different from ``cache_key`` since the ``cache_key`` might
559
+ get confused with the key of a hashable object. Therefore, such keys
560
+ include ``unhashable_key`` which acts as a unique marker which is
561
+ certainly not stored in the dictionary otherwise.
562
+
563
+ EXAMPLES::
564
+
565
+ sage: from sage.misc.cachefunc import dict_key
566
+ sage: dict_key(42)
567
+ 42
568
+ sage: K.<u> = Qq(9) # needs sage.rings.padics
569
+ sage: dict_key(u) # needs sage.rings.padics
570
+ (<object object at ...>, (..., 20))
571
+ """
572
+ try:
573
+ hash(o)
574
+ except TypeError:
575
+ o = (unhashable_key, cache_key_unhashable(o))
576
+ return o
577
+
578
+
579
+ cpdef inline cache_key(o):
580
+ r"""
581
+ Helper function to return a hashable key for ``o`` which can be used for
582
+ caching.
583
+
584
+ This function is intended for objects which are not hashable such as
585
+ `p`-adic numbers. The difference from calling an object's ``_cache_key``
586
+ method directly, is that it also works for tuples and unpacks them
587
+ recursively (if necessary, i.e., if they are not hashable).
588
+
589
+ EXAMPLES::
590
+
591
+ sage: from sage.misc.cachefunc import cache_key
592
+ sage: K.<u> = Qq(9) # needs sage.rings.padics
593
+ sage: a = K(1); a # needs sage.rings.padics
594
+ 1 + O(3^20)
595
+ sage: cache_key(a) # needs sage.rings.padics
596
+ (..., ((1,),), 0, 20)
597
+
598
+ This function works if ``o`` is a tuple. In this case it unpacks its
599
+ entries recursively::
600
+
601
+ sage: o = (1, 2, (3, a)) # needs sage.rings.padics
602
+ sage: cache_key(o) # needs sage.rings.padics
603
+ (1, 2, (3, (..., ((1,),), 0, 20)))
604
+
605
+ Note that tuples are only partially unpacked if some of its entries are
606
+ hashable::
607
+
608
+ sage: o = (1/2, a) # needs sage.rings.padics
609
+ sage: cache_key(o) # needs sage.rings.padics
610
+ (1/2, (..., ((1,),), 0, 20))
611
+ """
612
+ try:
613
+ hash(o)
614
+ except TypeError:
615
+ o = cache_key_unhashable(o)
616
+ return o
617
+
618
+
619
+ cdef cache_key_unhashable(o):
620
+ """
621
+ Return a key for caching an item which is unhashable.
622
+ """
623
+ if isinstance(o, tuple):
624
+ return tuple(cache_key(item) for item in o)
625
+ try:
626
+ k = o._cache_key()
627
+ except AttributeError:
628
+ raise TypeError("unhashable type: {!r}".format(type(o).__name__))
629
+ return cache_key(k)
630
+
631
+
632
+ cdef class CachedFunction():
633
+ """
634
+ Create a cached version of a function, which only recomputes
635
+ values it hasn't already computed. Synonyme: ``cached_function``
636
+
637
+ INPUT:
638
+
639
+ - ``f`` -- a function
640
+ - ``name`` -- (optional string) name that the cached version
641
+ of ``f`` should be provided with
642
+ - ``key`` -- (optional callable) takes the input and returns a
643
+ key for the cache, typically one would use this to normalize input
644
+ - ``do_pickle`` -- (optional boolean) whether or not the contents of the
645
+ cache should be included when pickling this function; the default is not
646
+ to include them.
647
+
648
+ If ``f`` is a function, do either ``g = CachedFunction(f)``
649
+ or ``g = cached_function(f)`` to make a cached version of ``f``,
650
+ or put ``@cached_function`` right before the definition of ``f``
651
+ (i.e., use Python decorators)::
652
+
653
+ @cached_function
654
+ def f(...):
655
+ ....
656
+
657
+ The inputs to the function must be hashable or they must define
658
+ :meth:`sage.structure.sage_object.SageObject._cache_key`.
659
+
660
+ EXAMPLES::
661
+
662
+ sage: @cached_function
663
+ ....: def mul(x, y=2):
664
+ ....: return x*y
665
+ sage: mul(3)
666
+ 6
667
+
668
+ We demonstrate that the result is cached, and that, moreover,
669
+ the cache takes into account the various ways of providing
670
+ default arguments::
671
+
672
+ sage: mul(3) is mul(3,2)
673
+ True
674
+ sage: mul(3,y=2) is mul(3,2)
675
+ True
676
+
677
+ The user can clear the cache::
678
+
679
+ sage: a = mul(4)
680
+ sage: mul.clear_cache()
681
+ sage: a is mul(4)
682
+ False
683
+
684
+ It is also possible to explicitly override the cache with
685
+ a different value::
686
+
687
+ sage: mul.set_cache('foo',5)
688
+ sage: mul(5,2)
689
+ 'foo'
690
+
691
+ The parameter ``key`` can be used to ignore parameters for
692
+ caching. In this example we ignore the parameter ``algorithm``::
693
+
694
+ sage: @cached_function(key=lambda x,y,algorithm: (x,y))
695
+ ....: def mul(x, y, algorithm='default'):
696
+ ....: return x*y
697
+ sage: mul(1,1,algorithm='default') is mul(1,1,algorithm='algorithm') is mul(1,1) is mul(1,1,'default')
698
+ True
699
+ """
700
+ def __init__(self, f, *, classmethod=False, name=None, key=None, do_pickle=None):
701
+ """
702
+ Create a cached version of a function, which only recomputes
703
+ values it hasn't already computed. A custom name can be
704
+ provided by an optional argument "name".
705
+
706
+ If ``f`` is a function, do either ``g = CachedFunction(f)``
707
+ to make a cached version of ``f``, or put ``@CachedFunction``
708
+ right before the definition of ``f`` (i.e., use Python decorators)::
709
+
710
+ @CachedFunction
711
+ def f(...):
712
+ ....
713
+
714
+ The inputs to the function must be hashable or they must define
715
+ :meth:`sage.structure.sage_object.SageObject._cache_key`.
716
+
717
+ TESTS::
718
+
719
+ sage: # needs sage.combinat
720
+ sage: g = CachedFunction(number_of_partitions)
721
+ sage: g.__name__
722
+ 'number_of_partitions'
723
+ sage: 'partitions' in sage.misc.sageinspect.sage_getdoc(g)
724
+ True
725
+ sage: g(5) # needs sage.libs.flint
726
+ 7
727
+ sage: g.cache # needs sage.libs.flint
728
+ {((5, 'default'), ()): 7}
729
+
730
+ sage: def f(t=1): print(t)
731
+ sage: h = CachedFunction(f)
732
+ sage: w = walltime()
733
+ sage: h(); h(1); h(t=1)
734
+ 1
735
+ sage: walltime(w) < 2
736
+ True
737
+
738
+ By default, the contents of the cache are not pickled::
739
+
740
+ sage: @cached_function
741
+ ....: def f(n): return None
742
+ sage: import __main__
743
+ sage: __main__.f = f
744
+ sage: for i in range(100): f(i)
745
+ sage: len(f.cache)
746
+ 100
747
+
748
+ sage: s = dumps(f)
749
+ sage: f.clear_cache()
750
+ sage: f = loads(s)
751
+ sage: len(f.cache)
752
+ 0
753
+
754
+ If ``do_pickle`` is set, then the cache is pickled::
755
+
756
+ sage: @cached_function(do_pickle=True)
757
+ ....: def f(n): return None
758
+ sage: __main__.f = f
759
+ sage: for i in range(100): f(i)
760
+ sage: len(f.cache)
761
+ 100
762
+
763
+ sage: s = dumps(f)
764
+ sage: f.clear_cache()
765
+ sage: f = loads(s)
766
+ sage: len(f.cache)
767
+ 100
768
+ """
769
+ self.is_classmethod = classmethod
770
+ self._common_init(f, None, name=name, key=key, do_pickle=do_pickle)
771
+ self.cache = {} if do_pickle else NonpicklingDict()
772
+
773
+ def _common_init(self, f, argument_fixer, name=None, key=None, do_pickle=None):
774
+ """
775
+ Perform initialization common to CachedFunction and CachedMethodCaller.
776
+
777
+ TESTS::
778
+
779
+ sage: @cached_function
780
+ ....: def test_cache(x):
781
+ ....: return -x
782
+ sage: test_cache.__name__ # indirect doctest
783
+ 'test_cache'
784
+ """
785
+ self.f = f
786
+ self.key = key
787
+ self.do_pickle = do_pickle
788
+ if name is not None:
789
+ self.__name__ = name
790
+ else:
791
+ self.__name__ = f.__name__
792
+ try:
793
+ self.__cached_module__ = f.__module__
794
+ except AttributeError:
795
+ self.__cached_module__ = f.__objclass__.__module__
796
+ if argument_fixer is not None:
797
+ # it is None unless the argument fixer
798
+ # was known previously. See #15038.
799
+ self._argument_fixer = argument_fixer
800
+
801
+ @property
802
+ def __module__(self):
803
+ return self.__cached_module__
804
+
805
+ cdef get_key_args_kwds(self, tuple args, dict kwds):
806
+ """
807
+ Return the key in the cache to be used when ``args`` and
808
+ ``kwds`` are passed in as parameters.
809
+
810
+ See ``get_key`` for the Python interface and tests.
811
+ """
812
+ # The key for "no arguments" is cached in empty_key
813
+ if not args and not kwds:
814
+ if self.empty_key is None:
815
+ self.empty_key = self.fix_args_kwds(args, kwds)
816
+ return self.empty_key
817
+
818
+ return self.fix_args_kwds(args, kwds)
819
+
820
+ cdef int argfix_init(self) except -1:
821
+ """
822
+ TESTS::
823
+
824
+ sage: @cached_function
825
+ ....: def test_cache(x):
826
+ ....: return -x
827
+ sage: test_cache(1)
828
+ -1
829
+ """
830
+ self._argument_fixer = ArgumentFixer(self.f,
831
+ classmethod=self.is_classmethod)
832
+
833
+ cdef fix_args_kwds(self, tuple args, dict kwds):
834
+ r"""
835
+ Normalize parameters to obtain a key for the cache.
836
+
837
+ TESTS::
838
+
839
+ sage: @cached_function(key=lambda x,y,algorithm: (x,y))
840
+ ....: def mul(x, y, algorithm='default'):
841
+ ....: return x*y
842
+ sage: mul.get_key(1,1,"default") # indirect doctest
843
+ (1, 1)
844
+ """
845
+ if self._argument_fixer is None:
846
+ self.argfix_init()
847
+
848
+ k = self._argument_fixer.fix_to_pos_args_kwds(args, kwds)
849
+ if self.key is None:
850
+ return k
851
+ else:
852
+ return self.key(*k[0], **dict(k[1]))
853
+
854
+ def __reduce__(self):
855
+ """
856
+ Pickling of cached functions.
857
+
858
+ TESTS::
859
+
860
+ sage: type(hilbert_class_polynomial) # needs sage.schemes
861
+ <class 'sage.misc.cachefunc.CachedFunction'>
862
+ sage: loads(dumps(hilbert_class_polynomial)) is hilbert_class_polynomial #indirect doctest # needs sage.schemes
863
+ True
864
+ """
865
+ return _cached_function_unpickle, (self.__cached_module__, self.__name__, self.cache)
866
+
867
+ #########
868
+ # Introspection
869
+ #
870
+ # We provide some methods explicitly, and
871
+ # forward other questions to the cached function.
872
+
873
+ def _instancedoc_(self):
874
+ """
875
+ Provide documentation for the cached function.
876
+
877
+ A cached function shall inherit the documentation
878
+ from the function that is wrapped, not from the
879
+ documentation of the wrapper.
880
+
881
+ TESTS::
882
+
883
+ sage: P.<x,y> = QQ[]
884
+ sage: I = P * [x,y]
885
+ sage: from sage.misc.sageinspect import sage_getdoc
886
+ sage: print(sage_getdoc(I.groebner_basis)) # indirect doctest
887
+ WARNING: the enclosing module is marked...
888
+ Return the reduced Groebner basis of this ideal.
889
+ ...
890
+
891
+ Test that :issue:`15184` is fixed::
892
+
893
+ sage: from sage.misc.sageinspect import sage_getfile
894
+ sage: type(I.groebner_basis)
895
+ <class 'sage.misc.cachefunc.CachedMethodCaller'>
896
+ sage: os.path.exists(sage_getfile(I.groebner_basis))
897
+ True
898
+
899
+ Test that :issue:`18064` is fixed::
900
+
901
+ sage: @cached_function
902
+ ....: def f():
903
+ ....: return 3
904
+ sage: f.__doc__
905
+ 'File: ... (starting at line 1)\n'
906
+ """
907
+ from sage.misc.sageinspect import _extract_embedded_position
908
+ f = self.f
909
+ doc = f.__doc__ or ''
910
+ if not doc or _extract_embedded_position(doc) is None:
911
+ try:
912
+ sourcelines = sage_getsourcelines(f)
913
+ filename = sage_getfile_relative(f)
914
+ # this is a rather expensive way of getting the line number, because
915
+ # retrieving the source requires reading the source file and in many
916
+ # cases this is not required (in cython it's embedded in the docstring,
917
+ # on code objects you'll find it in co_filename and co_firstlineno)
918
+ # however, this hasn't been factored out yet in sageinspect
919
+ # and the logic in sage_getsourcelines is rather intricate.
920
+ file_info = "File: {} (starting at line {})".format(filename, sourcelines[1]) + os.linesep
921
+
922
+ doc = file_info + doc
923
+ except IOError:
924
+ pass
925
+ return doc
926
+
927
+ def _sage_src_(self):
928
+ """
929
+ Return the source code for the wrapped function.
930
+
931
+ TESTS::
932
+
933
+ sage: from sage.misc.sageinspect import sage_getsource
934
+ sage: g = CachedFunction(number_of_partitions)
935
+ sage: 'flint' in sage_getsource(g) # indirect doctest
936
+ True
937
+ """
938
+ from sage.misc.sageinspect import sage_getsource
939
+ return sage_getsource(self.f)
940
+
941
+ def _sage_src_lines_(self):
942
+ r"""
943
+ Return the list of source lines and the first line number
944
+ of the wrapped function.
945
+
946
+ TESTS::
947
+
948
+ sage: P.<x,y> = QQ[]
949
+ sage: I = P*[x,y]
950
+ sage: from sage.misc.sageinspect import sage_getsourcelines
951
+ sage: l = ' elif algorithm.startswith("macaulay2:"):\n'
952
+ sage: l in sage_getsourcelines(I.groebner_basis)[0] # indirect doctest
953
+ True
954
+ """
955
+ from sage.misc.sageinspect import sage_getsourcelines
956
+ return sage_getsourcelines(self.f)
957
+
958
+ def _sage_argspec_(self):
959
+ """
960
+ Return the argspec of the wrapped function or method.
961
+
962
+ This was implemented in :issue:`11115`.
963
+
964
+ EXAMPLES::
965
+
966
+ sage: P.<x,y> = QQ[]
967
+ sage: I = P*[x,y]
968
+ sage: from sage.misc.sageinspect import sage_getargspec
969
+ sage: sage_getargspec(I.groebner_basis) # indirect doctest
970
+ FullArgSpec(args=['self', 'algorithm', 'deg_bound', 'mult_bound', 'prot'],
971
+ varargs='args', varkw='kwds', defaults=('', None, None, False),
972
+ kwonlyargs=[], kwonlydefaults=None, annotations={})
973
+ """
974
+ return sage_getargspec(self.f)
975
+
976
+ def __call__(self, *args, **kwds):
977
+ """
978
+ Return value from cache or call the wrapped function,
979
+ caching the output.
980
+
981
+ TESTS::
982
+
983
+ sage: # needs sage.combinat sage.libs.flint
984
+ sage: g = CachedFunction(number_of_partitions)
985
+ sage: a = g(5)
986
+ sage: g.cache
987
+ {((5, 'default'), ()): 7}
988
+ sage: a = g(10^5) # indirect doctest
989
+ sage: a == number_of_partitions(10^5)
990
+ True
991
+ sage: a is g(10^5)
992
+ True
993
+ sage: a is number_of_partitions(10^5)
994
+ True
995
+
996
+ Check that :issue:`16316` has been fixed, i.e., caching works for
997
+ immutable unhashable objects which define
998
+ :meth:`sage.structure.sage_object.SageObject._cache_key`::
999
+
1000
+ sage: # needs sage.rings.padics
1001
+ sage: @cached_function
1002
+ ....: def f(x): return x+x
1003
+ sage: K.<u> = Qq(4)
1004
+ sage: x = K(1,1); x
1005
+ 1 + O(2)
1006
+ sage: y = K(1,2); y
1007
+ 1 + O(2^2)
1008
+ sage: x == y
1009
+ True
1010
+ sage: f(x) is f(x)
1011
+ True
1012
+ sage: f(y) is not f(x)
1013
+ True
1014
+ """
1015
+ k = self.get_key_args_kwds(args, kwds)
1016
+
1017
+ try:
1018
+ try:
1019
+ return self.cache[k]
1020
+ except TypeError: # k is not hashable
1021
+ k = dict_key(k)
1022
+ return self.cache[k]
1023
+ except KeyError:
1024
+ w = self.f(*args, **kwds)
1025
+ self.cache[k] = w
1026
+ return w
1027
+
1028
+ def cached(self, *args, **kwds):
1029
+ """
1030
+ Return the result from the cache if available. If the value is
1031
+ not cached, raise :exc:`KeyError`.
1032
+
1033
+ EXAMPLES::
1034
+
1035
+ sage: @cached_function
1036
+ ....: def f(x):
1037
+ ....: return x
1038
+ sage: f.cached(5)
1039
+ Traceback (most recent call last):
1040
+ ...
1041
+ KeyError: ((5,), ())
1042
+ sage: f(5)
1043
+ 5
1044
+ sage: f.cached(5)
1045
+ 5
1046
+ """
1047
+ k = self.get_key_args_kwds(args, kwds)
1048
+
1049
+ try:
1050
+ return self.cache[k]
1051
+ except TypeError: # k is not hashable
1052
+ k = dict_key(k)
1053
+ return self.cache[k]
1054
+
1055
+ def is_in_cache(self, *args, **kwds):
1056
+ """
1057
+ Check if the argument list is in the cache.
1058
+
1059
+ EXAMPLES::
1060
+
1061
+ sage: class Foo:
1062
+ ....: def __init__(self, x):
1063
+ ....: self._x = x
1064
+ ....: @cached_method
1065
+ ....: def f(self, z, y=0):
1066
+ ....: return self._x*z+y
1067
+ sage: a = Foo(2)
1068
+ sage: a.f.is_in_cache(3)
1069
+ False
1070
+ sage: a.f(3)
1071
+ 6
1072
+ sage: a.f.is_in_cache(3,y=0)
1073
+ True
1074
+
1075
+ TESTS:
1076
+
1077
+ Check that :issue:`16316` has been fixed, i.e., caching works for
1078
+ immutable unhashable objects which define
1079
+ :meth:`sage.structure.sage_object.SageObject._cache_key`::
1080
+
1081
+ sage: # needs sage.rings.padics
1082
+ sage: @cached_function
1083
+ ....: def f(x): return x
1084
+ sage: K.<u> = Qq(4)
1085
+ sage: x = K(1,1); x
1086
+ 1 + O(2)
1087
+ sage: f.is_in_cache(x)
1088
+ False
1089
+ sage: f(x)
1090
+ 1 + O(2)
1091
+ sage: f.is_in_cache(x)
1092
+ True
1093
+ """
1094
+ k = self.get_key_args_kwds(args, kwds)
1095
+ try:
1096
+ return k in self.cache
1097
+ except TypeError: # k is not hashable
1098
+ k = dict_key(k)
1099
+ return k in self.cache
1100
+
1101
+ def set_cache(self, value, *args, **kwds):
1102
+ """
1103
+ Set the value for those args and keyword args
1104
+ Mind the unintuitive syntax (value first).
1105
+ Any idea on how to improve that welcome!
1106
+
1107
+ EXAMPLES::
1108
+
1109
+ sage: # needs sage.combinat sage.libs.flint
1110
+ sage: g = CachedFunction(number_of_partitions)
1111
+ sage: a = g(5)
1112
+ sage: g.cache
1113
+ {((5, 'default'), ()): 7}
1114
+ sage: g.set_cache(17, 5)
1115
+ sage: g.cache
1116
+ {((5, 'default'), ()): 17}
1117
+ sage: g(5)
1118
+ 17
1119
+
1120
+ TESTS:
1121
+
1122
+ Check that :issue:`16316` has been fixed, i.e., caching works for
1123
+ immutable unhashable objects which define
1124
+ :meth:`sage.structure.sage_object.SageObject._cache_key`::
1125
+
1126
+ sage: # needs sage.rings.padics
1127
+ sage: @cached_function
1128
+ ....: def f(x): return x
1129
+ sage: K.<u> = Qq(4)
1130
+ sage: x = K(1,1); x
1131
+ 1 + O(2)
1132
+ sage: f.set_cache(x, x)
1133
+ sage: f.is_in_cache(x)
1134
+ True
1135
+
1136
+ DEVELOPER NOTE:
1137
+
1138
+ Is there a way to use the following intuitive syntax?
1139
+
1140
+ ::
1141
+
1142
+ sage: g(5) = 19 # todo: not implemented
1143
+ sage: g(5) # todo: not implemented
1144
+ 19
1145
+ """
1146
+ k = self.get_key_args_kwds(args, kwds)
1147
+ try:
1148
+ self.cache[k] = value
1149
+ except TypeError: # k is not hashable
1150
+ k = dict_key(k)
1151
+ self.cache[k] = value
1152
+
1153
+ def get_key(self, *args, **kwds):
1154
+ """
1155
+ Return the key in the cache to be used when ``args``
1156
+ and ``kwds`` are passed in as parameters.
1157
+
1158
+ EXAMPLES::
1159
+
1160
+ sage: @cached_function
1161
+ ....: def foo(x):
1162
+ ....: return x^2
1163
+ sage: foo(2)
1164
+ 4
1165
+ sage: foo.get_key(2)
1166
+ ((2,), ())
1167
+ sage: foo.get_key(x=3)
1168
+ ((3,), ())
1169
+
1170
+ Examples for cached methods::
1171
+
1172
+ sage: class Foo:
1173
+ ....: def __init__(self, x):
1174
+ ....: self._x = x
1175
+ ....: @cached_method
1176
+ ....: def f(self, y, z=0):
1177
+ ....: return self._x * y + z
1178
+ sage: a = Foo(2)
1179
+ sage: z = a.f(37)
1180
+ sage: k = a.f.get_key(37); k
1181
+ ((37, 0), ())
1182
+ sage: a.f.cache[k] is z
1183
+ True
1184
+
1185
+ Note that the method does not test whether there are
1186
+ too many arguments, or wrong argument names::
1187
+
1188
+ sage: a.f.get_key(1,2,3,x=4,y=5,z=6)
1189
+ ((1, 2, 3), (('x', 4), ('y', 5), ('z', 6)))
1190
+
1191
+ It does, however, take into account the different
1192
+ ways of providing named arguments, possibly with a
1193
+ default value::
1194
+
1195
+ sage: a.f.get_key(5)
1196
+ ((5, 0), ())
1197
+ sage: a.f.get_key(y=5)
1198
+ ((5, 0), ())
1199
+ sage: a.f.get_key(5,0)
1200
+ ((5, 0), ())
1201
+ sage: a.f.get_key(5,z=0)
1202
+ ((5, 0), ())
1203
+ sage: a.f.get_key(y=5,z=0)
1204
+ ((5, 0), ())
1205
+ """
1206
+ return self.get_key_args_kwds(args, kwds)
1207
+
1208
+ def __repr__(self):
1209
+ """
1210
+ EXAMPLES::
1211
+
1212
+ sage: g = CachedFunction(number_of_partitions)
1213
+ sage: g # indirect doctest
1214
+ Cached version of <function number_of_partitions at 0x...>
1215
+ """
1216
+ try:
1217
+ return "Cached version of {}".format(self.f)
1218
+ except AttributeError:
1219
+ return "Cached version of a method (pending reassignment)"
1220
+
1221
+ def clear_cache(self):
1222
+ """
1223
+ Clear the cache dictionary.
1224
+
1225
+ EXAMPLES::
1226
+
1227
+ sage: # needs sage.combinat
1228
+ sage: g = CachedFunction(number_of_partitions)
1229
+ sage: a = g(5) # needs sage.libs.flint
1230
+ sage: g.cache # needs sage.libs.flint
1231
+ {((5, 'default'), ()): 7}
1232
+ sage: g.clear_cache()
1233
+ sage: g.cache
1234
+ {}
1235
+ """
1236
+ self.cache.clear()
1237
+
1238
+ def precompute(self, arglist, num_processes=1):
1239
+ """
1240
+ Cache values for a number of inputs. Do the computation
1241
+ in parallel, and only bother to compute values that we
1242
+ haven't already cached.
1243
+
1244
+ INPUT:
1245
+
1246
+ - ``arglist`` -- list (or iterables) of arguments for which
1247
+ the method shall be precomputed
1248
+
1249
+ - ``num_processes`` -- number of processes used by
1250
+ :func:`~sage.parallel.decorate.parallel`
1251
+
1252
+ EXAMPLES::
1253
+
1254
+ sage: @cached_function
1255
+ ....: def oddprime_factors(n):
1256
+ ....: l = [p for p,e in factor(n) if p != 2]
1257
+ ....: return len(l)
1258
+ sage: oddprime_factors.precompute(range(1,100), 4)
1259
+ sage: oddprime_factors.cache[(25,),()]
1260
+ 1
1261
+ """
1262
+ from sage.parallel.decorate import parallel, normalize_input
1263
+ P = parallel(num_processes)(self.f)
1264
+ cdef list arglist2 = []
1265
+ for a in arglist:
1266
+ ak = normalize_input(a)
1267
+ if self.get_key_args_kwds(ak[0], ak[1]) not in self.cache:
1268
+ arglist2.append(ak)
1269
+ for ((args, kwargs), val) in P(arglist2):
1270
+ self.set_cache(val, *args, **kwargs)
1271
+
1272
+
1273
+ cached_function = decorator_keywords(CachedFunction)
1274
+
1275
+
1276
+ cdef class WeakCachedFunction(CachedFunction):
1277
+ """
1278
+ A version of :class:`CachedFunction` using weak references on the
1279
+ values.
1280
+
1281
+ If ``f`` is a function, do either ``g = weak_cached_function(f)`` to make
1282
+ a cached version of ``f``, or put ``@weak_cached_function`` right before
1283
+ the definition of ``f`` (i.e., use Python decorators)::
1284
+
1285
+ @weak_cached_function
1286
+ def f(...):
1287
+ ...
1288
+
1289
+ As an exception meant to improve performance, the most recently
1290
+ computed values are strongly referenced. The number of strongly
1291
+ cached values can be controlled by the ``cache`` keyword.
1292
+
1293
+ EXAMPLES::
1294
+
1295
+ sage: from sage.misc.cachefunc import weak_cached_function
1296
+ sage: class A: pass
1297
+ sage: @weak_cached_function(cache=0)
1298
+ ....: def f():
1299
+ ....: print("doing a computation")
1300
+ ....: return A()
1301
+ sage: a = f()
1302
+ doing a computation
1303
+
1304
+ The result is cached::
1305
+
1306
+ sage: b = f()
1307
+ sage: a is b
1308
+ True
1309
+
1310
+ However, if there are no strong references left, the result is
1311
+ deleted, and thus a new computation takes place::
1312
+
1313
+ sage: del a
1314
+ sage: del b
1315
+ sage: a = f()
1316
+ doing a computation
1317
+
1318
+ Above, we used the ``cache=0`` keyword. With a larger value, the
1319
+ most recently computed values are cached anyway, even if they are
1320
+ not referenced::
1321
+
1322
+ sage: @weak_cached_function(cache=3)
1323
+ ....: def f(x):
1324
+ ....: print("doing a computation for x={}".format(x))
1325
+ ....: return A()
1326
+ sage: a = f(1); del a
1327
+ doing a computation for x=1
1328
+ sage: a = f(2), f(1); del a
1329
+ doing a computation for x=2
1330
+ sage: a = f(3), f(1); del a
1331
+ doing a computation for x=3
1332
+ sage: a = f(4), f(1); del a
1333
+ doing a computation for x=4
1334
+ doing a computation for x=1
1335
+ sage: a = f(5), f(1); del a
1336
+ doing a computation for x=5
1337
+
1338
+ The parameter ``key`` can be used to ignore parameters for
1339
+ caching. In this example we ignore the parameter ``algorithm``::
1340
+
1341
+ sage: @weak_cached_function(key=lambda x,algorithm: x)
1342
+ ....: def mod_ring(x, algorithm='default'):
1343
+ ....: return IntegerModRing(x)
1344
+ sage: mod_ring(1,algorithm='default') is mod_ring(1,algorithm='algorithm') is mod_ring(1) is mod_ring(1,'default')
1345
+ True
1346
+
1347
+ TESTS:
1348
+
1349
+ Check that :issue:`16316` has been fixed, i.e., caching works for
1350
+ immutable unhashable objects which define
1351
+ :meth:`sage.structure.sage_object.SageObject._cache_key`::
1352
+
1353
+ sage: # needs sage.rings.padics
1354
+ sage: from sage.misc.cachefunc import weak_cached_function
1355
+ sage: @weak_cached_function
1356
+ ....: def f(x): return x+x
1357
+ sage: K.<u> = Qq(4)
1358
+ sage: R.<t> = K[]
1359
+ sage: x = t + K(1,1); x
1360
+ (1 + O(2^20))*t + 1 + O(2)
1361
+ sage: y = t + K(1,2); y
1362
+ (1 + O(2^20))*t + 1 + O(2^2)
1363
+ sage: x == y
1364
+ True
1365
+ sage: f(x) is f(x)
1366
+ True
1367
+ sage: f(y) is not f(x)
1368
+ True
1369
+
1370
+ Examples and tests for ``is_in_cache``::
1371
+
1372
+ sage: from sage.misc.cachefunc import weak_cached_function
1373
+ sage: class A:
1374
+ ....: def __init__(self, x):
1375
+ ....: self.x = x
1376
+ sage: @weak_cached_function(cache=0)
1377
+ ....: def f(n):
1378
+ ....: return A(n)
1379
+ sage: a = f(5)
1380
+
1381
+ The key 5 is in the cache, as long as there is a strong
1382
+ reference to the corresponding value::
1383
+
1384
+ sage: f.is_in_cache(5)
1385
+ True
1386
+
1387
+ However, if there are no strong references left, the cached
1388
+ item is removed from the cache::
1389
+
1390
+ sage: del a
1391
+ sage: f.is_in_cache(5)
1392
+ False
1393
+
1394
+ Check that :issue:`16316` has been fixed, i.e., caching works for
1395
+ immutable unhashable objects which define
1396
+ :meth:`sage.structure.sage_object.SageObject._cache_key`::
1397
+
1398
+ sage: # needs sage.rings.padics
1399
+ sage: from sage.misc.cachefunc import weak_cached_function
1400
+ sage: @weak_cached_function
1401
+ ....: def f(x): return x
1402
+ sage: K.<u> = Qq(4)
1403
+ sage: R.<t> = K[]
1404
+ sage: f.is_in_cache(t)
1405
+ False
1406
+ sage: f(t)
1407
+ (1 + O(2^20))*t
1408
+ sage: f.is_in_cache(t)
1409
+ True
1410
+
1411
+ Examples and tests for ``set_cache``::
1412
+
1413
+ sage: from sage.misc.cachefunc import weak_cached_function
1414
+ sage: @weak_cached_function
1415
+ ....: def f(n):
1416
+ ....: raise RuntimeError
1417
+ sage: f.set_cache(ZZ, 5)
1418
+ sage: f(5)
1419
+ Integer Ring
1420
+
1421
+ Check that :issue:`16316` has been fixed, i.e., caching works for
1422
+ immutable unhashable objects which define
1423
+ :meth:`sage.structure.sage_object.SageObject._cache_key`::
1424
+
1425
+ sage: # needs sage.rings.padics
1426
+ sage: from sage.misc.cachefunc import weak_cached_function
1427
+ sage: @weak_cached_function
1428
+ ....: def f(x): return x
1429
+ sage: K.<u> = Qq(4)
1430
+ sage: R.<t> = K[]
1431
+ sage: f.set_cache(t,t)
1432
+ sage: f.is_in_cache(t)
1433
+ True
1434
+ """
1435
+ def __init__(self, f, *, classmethod=False, name=None, key=None, **kwds):
1436
+ """
1437
+ The inputs to the function must be hashable or they must define
1438
+ :meth:`sage.structure.sage_object.SageObject._cache_key`.
1439
+ The outputs to the function must be weakly referenceable.
1440
+
1441
+ TESTS::
1442
+
1443
+ sage: from sage.misc.cachefunc import weak_cached_function
1444
+ sage: class A: pass
1445
+ sage: @weak_cached_function
1446
+ ....: def f():
1447
+ ....: return A()
1448
+ sage: f
1449
+ Cached version of <function f at ...>
1450
+
1451
+ We demonstrate that pickling works, provided the uncached function
1452
+ is available::
1453
+
1454
+ sage: import __main__
1455
+ sage: __main__.f = f
1456
+ sage: loads(dumps(f))
1457
+ Cached version of <function f at ...>
1458
+ sage: str(f.cache)
1459
+ '<CachedWeakValueDictionary at 0x...>'
1460
+ """
1461
+ self._common_init(f, None, name=name, key=key)
1462
+ self.cache = CachedWeakValueDictionary(**kwds)
1463
+
1464
+
1465
+ weak_cached_function = decorator_keywords(WeakCachedFunction)
1466
+
1467
+
1468
+ class CachedMethodPickle():
1469
+ """
1470
+ This class helps to unpickle cached methods.
1471
+
1472
+ .. NOTE::
1473
+
1474
+ Since :issue:`8611`, a cached method is an attribute
1475
+ of the instance (provided that it has a ``__dict__``).
1476
+ Hence, when pickling the instance, it would be attempted
1477
+ to pickle that attribute as well, but this is a problem,
1478
+ since functions cannot be pickled, currently. Therefore,
1479
+ we replace the actual cached method by a place holder,
1480
+ that kills itself as soon as any attribute is requested.
1481
+ Then, the original cached attribute is reinstated. But the
1482
+ cached values are in fact saved (if ``do_pickle`` is set.)
1483
+
1484
+ EXAMPLES::
1485
+
1486
+ sage: R.<x, y, z> = PolynomialRing(QQ, 3)
1487
+ sage: I = R * (x^3 + y^3 + z^3, x^4 - y^4)
1488
+ sage: I.groebner_basis() # needs sage.libs.singular
1489
+ [y^5*z^3 - 1/4*x^2*z^6 + 1/2*x*y*z^6 + 1/4*y^2*z^6,
1490
+ x^2*y*z^3 - x*y^2*z^3 + 2*y^3*z^3 + z^6,
1491
+ x*y^3 + y^4 + x*z^3, x^3 + y^3 + z^3]
1492
+ sage: I.groebner_basis
1493
+ Cached version of <function ...groebner_basis at 0x...>
1494
+
1495
+ We now pickle and unpickle the ideal. The cached method
1496
+ ``groebner_basis`` is replaced by a placeholder::
1497
+
1498
+ sage: J = loads(dumps(I))
1499
+ sage: J.groebner_basis
1500
+ Pickle of the cached method "groebner_basis"
1501
+
1502
+ But as soon as any other attribute is requested from the
1503
+ placeholder, it replaces itself by the cached method, and
1504
+ the entries of the cache are actually preserved::
1505
+
1506
+ sage: J.groebner_basis.is_in_cache() # needs sage.libs.singular
1507
+ True
1508
+ sage: J.groebner_basis
1509
+ Cached version of <function ...groebner_basis at 0x...>
1510
+ sage: J.groebner_basis() == I.groebner_basis() # needs sage.libs.singular
1511
+ True
1512
+
1513
+ TESTS:
1514
+
1515
+ Since :issue:`11115`, there is a special implementation for
1516
+ cached methods that don't take arguments::
1517
+
1518
+ sage: class A:
1519
+ ....: @cached_method(do_pickle=True)
1520
+ ....: def f(self): return 1
1521
+ ....: @cached_method(do_pickle=True)
1522
+ ....: def g(self, x): return x
1523
+
1524
+ sage: import __main__
1525
+ sage: __main__.A = A
1526
+ sage: a = A()
1527
+ sage: type(a.f)
1528
+ <class 'sage.misc.cachefunc.CachedMethodCallerNoArgs'>
1529
+ sage: type(a.g)
1530
+ <class 'sage.misc.cachefunc.CachedMethodCaller'>
1531
+
1532
+ We demonstrate that both implementations can be pickled and
1533
+ preserve the cache. For that purpose, we assign nonsense to the
1534
+ cache. Of course, it is a very bad idea to override the cache in
1535
+ that way. So, please don't try this at home::
1536
+
1537
+ sage: a.f.set_cache(0)
1538
+ sage: a.f()
1539
+ 0
1540
+ sage: a.g.set_cache(0,x=1)
1541
+ sage: a.g(1)
1542
+ 0
1543
+ sage: b = loads(dumps(a))
1544
+ sage: b.f()
1545
+ 0
1546
+ sage: b.g(1)
1547
+ 0
1548
+
1549
+ Anyway, the cache will be automatically reconstructed after
1550
+ clearing it::
1551
+
1552
+ sage: a.f.clear_cache()
1553
+ sage: a.f()
1554
+ 1
1555
+
1556
+ sage: a.g.clear_cache()
1557
+ sage: a.g(1)
1558
+ 1
1559
+
1560
+ AUTHOR:
1561
+
1562
+ - Simon King (2011-01)
1563
+ """
1564
+ def __init__(self, inst, name, cache=None):
1565
+ """
1566
+ INPUT:
1567
+
1568
+ - ``inst`` -- some instance
1569
+ - ``name`` -- string; usually the name of an attribute
1570
+ of ``inst`` to which ``self`` is assigned
1571
+
1572
+ TESTS::
1573
+
1574
+ sage: from sage.misc.cachefunc import CachedMethodPickle
1575
+ sage: P = CachedMethodPickle(1, 'foo')
1576
+ sage: P
1577
+ Pickle of the cached method "foo"
1578
+ """
1579
+ self._instance = inst
1580
+ self._name = name
1581
+ self._cache = cache
1582
+
1583
+ def __repr__(self):
1584
+ """
1585
+ TESTS::
1586
+
1587
+ sage: R.<x, y, z> = PolynomialRing(QQ, 3)
1588
+ sage: I = R * (x^3 + y^3 + z^3, x^4 - y^4)
1589
+ sage: G = I.groebner_basis() # needs sage.libs.singular
1590
+ sage: J = loads(dumps(I))
1591
+ sage: J.groebner_basis # indirect doctest
1592
+ Pickle of the cached method "groebner_basis"
1593
+ """
1594
+ return 'Pickle of the cached method "{}"'.format(self._name)
1595
+
1596
+ def __reduce__(self):
1597
+ """
1598
+ This class is a pickle. However, sometimes, pickles
1599
+ need to be pickled another time.
1600
+
1601
+ TESTS::
1602
+
1603
+ sage: R.<x, y, z> = PolynomialRing(QQ, 3)
1604
+ sage: I = R * (x^3 + y^3 + z^3, x^4 - y^4)
1605
+ sage: I.groebner_basis() # needs sage.libs.singular
1606
+ [y^5*z^3 - 1/4*x^2*z^6 + 1/2*x*y*z^6 + 1/4*y^2*z^6,
1607
+ x^2*y*z^3 - x*y^2*z^3 + 2*y^3*z^3 + z^6,
1608
+ x*y^3 + y^4 + x*z^3, x^3 + y^3 + z^3]
1609
+ sage: J = loads(dumps(I))
1610
+ sage: J.groebner_basis
1611
+ Pickle of the cached method "groebner_basis"
1612
+
1613
+ When we now pickle ``J``, the pickle of the cached method
1614
+ needs to be taken care of::
1615
+
1616
+ sage: K = loads(dumps(J)) # indirect doctest
1617
+ sage: K.groebner_basis
1618
+ Pickle of the cached method "groebner_basis"
1619
+ sage: K.groebner_basis.cache # needs sage.libs.singular
1620
+ {(('', None, None, False), ()):
1621
+ [y^5*z^3 - 1/4*x^2*z^6 + 1/2*x*y*z^6 + 1/4*y^2*z^6,
1622
+ x^2*y*z^3 - x*y^2*z^3 + 2*y^3*z^3 + z^6,
1623
+ x*y^3 + y^4 + x*z^3, x^3 + y^3 + z^3]}
1624
+ """
1625
+ return CachedMethodPickle, (self._instance, self._name, self._cache)
1626
+
1627
+ def __call__(self, *args, **kwds):
1628
+ """
1629
+ The purpose of this call method is to kill ``self`` and to
1630
+ replace it by an actual :class:`CachedMethodCaller`. The last
1631
+ thing that ``self`` does before disappearing is to call the
1632
+ :class:`CachedMethodCaller` and return the result.
1633
+
1634
+ EXAMPLES::
1635
+
1636
+ sage: P.<a,b,c,d> = QQ[]
1637
+ sage: I = P*[a,b]
1638
+ sage: I.gens
1639
+ Cached version of <function ...gens at 0x...>
1640
+ sage: J = loads(dumps(I))
1641
+ sage: J.gens
1642
+ Pickle of the cached method "gens"
1643
+ sage: J.gens() # indirect doctest
1644
+ [a, b]
1645
+ sage: J.gens
1646
+ Cached version of <function ...gens at 0x...>
1647
+ """
1648
+ self._instance.__dict__.__delitem__(self._name)
1649
+ CM = getattr(self._instance, self._name)
1650
+ if self._cache is not None:
1651
+ if isinstance(CM, CachedMethodCallerNoArgs):
1652
+ CM.cache = self._cache
1653
+ else:
1654
+ for k, v in self._cache:
1655
+ CM.cache[k] = v
1656
+ return CM(*args, **kwds)
1657
+
1658
+ def __getattr__(self, s):
1659
+ """
1660
+ TESTS::
1661
+
1662
+ sage: R.<x, y, z> = PolynomialRing(QQ, 3)
1663
+ sage: I = R * (x^3 + y^3 + z^3, x^4 - y^4)
1664
+ sage: G = I.groebner_basis() # needs sage.libs.singular
1665
+ sage: J = loads(dumps(I))
1666
+ sage: J.groebner_basis
1667
+ Pickle of the cached method "groebner_basis"
1668
+
1669
+ If an attribute of name ``s`` is requested (say,
1670
+ ``is_in_cache``), the attribute ``self._name`` of
1671
+ ``self._instance`` is deleted. Then, the attribute
1672
+ of name ``s`` of the attribute ``self._name`` of
1673
+ ``self._instance`` is requested. Since ``self._name``
1674
+ is a cached method defined for the class of
1675
+ ``self._instance``, retrieving the just-deleted
1676
+ attribute ``self._name`` succeeds.
1677
+
1678
+ In that way, the unpickling of the cached method is
1679
+ finally accomplished::
1680
+
1681
+ sage: J.groebner_basis.is_in_cache() # indirect doctest # needs sage.libs.singular
1682
+ True
1683
+ sage: J.groebner_basis
1684
+ Cached version of <function ...groebner_basis at 0x...>
1685
+ """
1686
+ self._instance.__dict__.__delitem__(self._name)
1687
+ CM = getattr(self._instance, self._name)
1688
+ if self._cache is not None:
1689
+ if isinstance(CM, CachedMethodCallerNoArgs):
1690
+ CM.cache = self._cache
1691
+ else:
1692
+ for k, v in self._cache:
1693
+ CM.cache[k] = v
1694
+ return getattr(CM, s)
1695
+
1696
+
1697
+ cdef class CachedMethodCaller(CachedFunction):
1698
+ """
1699
+ Utility class that is used by :class:`CachedMethod` to bind a
1700
+ cached method to an instance.
1701
+
1702
+ .. NOTE::
1703
+
1704
+ Since :issue:`11115`, there is a special implementation
1705
+ :class:`CachedMethodCallerNoArgs` for methods that do not take
1706
+ arguments.
1707
+
1708
+ EXAMPLES::
1709
+
1710
+ sage: class A:
1711
+ ....: @cached_method
1712
+ ....: def bar(self, x):
1713
+ ....: return x^2
1714
+ sage: a = A()
1715
+ sage: a.bar
1716
+ Cached version of <function ...bar at 0x...>
1717
+ sage: type(a.bar)
1718
+ <class 'sage.misc.cachefunc.CachedMethodCaller'>
1719
+ sage: a.bar(2) is a.bar(x=2)
1720
+ True
1721
+
1722
+ TESTS:
1723
+
1724
+ As of :issue:`15692` the contents of the cache are not pickled anymore::
1725
+
1726
+ sage: import __main__
1727
+ sage: __main__.A = A
1728
+ sage: len(a.bar.cache)
1729
+ 1
1730
+ sage: b = loads(dumps(a))
1731
+ sage: len(b.bar.cache)
1732
+ 0
1733
+
1734
+ The parameter ``do_pickle`` can be used to change this behaviour::
1735
+
1736
+ sage: class A:
1737
+ ....: @cached_method(do_pickle=True)
1738
+ ....: def bar(self, x):
1739
+ ....: return x^2
1740
+
1741
+ sage: __main__.A = A
1742
+ sage: a = A()
1743
+ sage: a.bar(2)
1744
+ 4
1745
+ sage: len(a.bar.cache)
1746
+ 1
1747
+ sage: b = loads(dumps(a))
1748
+ sage: len(b.bar.cache)
1749
+ 1
1750
+ """
1751
+ def __init__(self, CachedMethod cachedmethod, inst, *, cache=None, name=None, key=None, do_pickle=None):
1752
+ """
1753
+ EXAMPLES::
1754
+
1755
+ sage: class Foo:
1756
+ ....: def __init__(self, x):
1757
+ ....: self._x = x
1758
+ ....: @cached_method
1759
+ ....: def f(self, *args):
1760
+ ....: return self._x^2
1761
+ sage: a = Foo(2)
1762
+ sage: a.f.cache
1763
+ {}
1764
+ sage: a.f()
1765
+ 4
1766
+ sage: a.f.cache
1767
+ {((), ()): 4}
1768
+ """
1769
+ # initialize CachedFunction. Since the cached method is actually bound
1770
+ # to an instance, it now makes sense to initialise the ArgumentFixer
1771
+ # and reuse it for all bound cached method callers of the unbound
1772
+ # cached method.
1773
+ if cachedmethod._cachedfunc._argument_fixer is None:
1774
+ cachedmethod._cachedfunc.argfix_init()
1775
+ self._common_init(cachedmethod._cachedfunc.f,
1776
+ cachedmethod._cachedfunc._argument_fixer,
1777
+ name=name,
1778
+ key=key,
1779
+ do_pickle=do_pickle)
1780
+ if cache is None:
1781
+ self.cache = NonpicklingDict() if do_pickle else {}
1782
+ else:
1783
+ self.cache = cache
1784
+ self._instance = inst
1785
+ self._cachedmethod = cachedmethod
1786
+
1787
+ def __reduce__(self):
1788
+ """
1789
+ The pickle of a :class:`CachedMethodCaller` unpickles
1790
+ to a :class:`CachedMethodPickle`, that is able to replace
1791
+ itself by a copy of the original :class:`CachedMethodCaller`.
1792
+
1793
+ TESTS::
1794
+
1795
+ sage: R.<x, y, z> = PolynomialRing(QQ, 3)
1796
+ sage: I = R * (x^3 + y^3 + z^3, x^4 - y^4)
1797
+ sage: G = I.groebner_basis() # needs sage.libs.singular
1798
+ sage: J = loads(dumps(I)) # indirect doctest
1799
+ sage: J.groebner_basis
1800
+ Pickle of the cached method "groebner_basis"
1801
+ sage: J.groebner_basis.is_in_cache() # needs sage.libs.singular
1802
+ True
1803
+ sage: J.groebner_basis
1804
+ Cached version of <function ...groebner_basis at 0x...>
1805
+ """
1806
+ if isinstance(self._cachedmethod, CachedInParentMethod) or hasattr(self._instance, self._cachedmethod._cache_name):
1807
+ return CachedMethodPickle, (self._instance, self.__name__)
1808
+ else:
1809
+ return CachedMethodPickle, (self._instance, self.__name__, self.cache)
1810
+
1811
+ def _instance_call(self, *args, **kwds):
1812
+ """
1813
+ Call the cached method without using the cache.
1814
+
1815
+ EXAMPLES::
1816
+
1817
+ sage: P.<a,b,c,d> = QQ[]
1818
+ sage: I = P * [a, b]
1819
+ sage: I.groebner_basis() # needs sage.libs.singular
1820
+ [a, b]
1821
+ sage: I.groebner_basis._instance_call() is I.groebner_basis() # needs sage.libs.singular
1822
+ False
1823
+ sage: I.groebner_basis._instance_call() == I.groebner_basis() # needs sage.libs.singular
1824
+ True
1825
+
1826
+ ::
1827
+
1828
+ sage: class Foo():
1829
+ ....: def __init__(self, x):
1830
+ ....: self._x = x
1831
+ ....: @cached_method
1832
+ ....: def f(self, n=2):
1833
+ ....: return self._x^n
1834
+ sage: a = Foo(2)
1835
+ sage: a.f()
1836
+ 4
1837
+
1838
+ Usually, a cached method is indeed cached::
1839
+
1840
+ sage: a.f() is a.f()
1841
+ True
1842
+
1843
+ However, when it becomes necessary, one can call it without
1844
+ using the cache::
1845
+
1846
+ sage: a.f._instance_call() is a.f()
1847
+ False
1848
+ sage: a.f._instance_call() == a.f()
1849
+ True
1850
+ """
1851
+ return self.f(self._instance, *args, **kwds)
1852
+
1853
+ cdef fix_args_kwds(self, tuple args, dict kwds):
1854
+ r"""
1855
+ Normalize parameters to obtain a key for the cache.
1856
+
1857
+ TESTS::
1858
+
1859
+ sage: class A():
1860
+ ....: def _f_normalize(self, x, algorithm): return x
1861
+ ....: @cached_method(key=_f_normalize)
1862
+ ....: def f(self, x, algorithm='default'): return x
1863
+ sage: a = A()
1864
+ sage: a.f(1, algorithm='default') is a.f(1) is a.f(1, algorithm='algorithm')
1865
+ True
1866
+ """
1867
+ if self._argument_fixer is None:
1868
+ self.argfix_init()
1869
+
1870
+ k = self._argument_fixer.fix_to_pos_args_kwds(args, kwds)
1871
+ if self.key is None:
1872
+ return k
1873
+ else:
1874
+ return self.key(self._instance, *k[0], **dict(k[1]))
1875
+
1876
+ def __call__(self, *args, **kwds):
1877
+ """
1878
+ Call the cached method.
1879
+
1880
+ TESTS::
1881
+
1882
+ sage: from sage.misc.superseded import deprecated_function_alias
1883
+ sage: class Foo:
1884
+ ....: @cached_method
1885
+ ....: def f(self, x, y=1):
1886
+ ....: return x+y
1887
+ ....: g = deprecated_function_alias(57, f)
1888
+ sage: a = Foo()
1889
+ sage: a.f(1) #indirect doctest
1890
+ 2
1891
+
1892
+ The result is cached, taking into account
1893
+ the three ways of providing (named) arguments::
1894
+
1895
+ sage: a.f(5) is a.f(5,1)
1896
+ True
1897
+ sage: a.f(5) is a.f(5,y=1)
1898
+ True
1899
+ sage: a.f(5) is a.f(y=1,x=5)
1900
+ True
1901
+
1902
+ The method can be called as an unbound function using the same cache::
1903
+
1904
+ sage: a.f(5) is Foo.f(a, 5)
1905
+ True
1906
+ sage: a.f(5) is Foo.f(a,5,1)
1907
+ True
1908
+ sage: a.f(5) is Foo.f(a, 5,y=1)
1909
+ True
1910
+ sage: a.f(5) is Foo.f(a, y=1,x=5)
1911
+ True
1912
+
1913
+ Cached methods are compatible with
1914
+ :meth:`sage.misc.superseded.deprecated_function_alias`::
1915
+
1916
+ sage: a.g(5) is a.f(5)
1917
+ doctest:...: DeprecationWarning: g is deprecated. Please use f instead.
1918
+ See https://github.com/sagemath/sage/issues/57 for details.
1919
+ True
1920
+ sage: Foo.g(a, 5) is a.f(5)
1921
+ True
1922
+ sage: Foo.g(a, y=1,x=5) is a.f(5)
1923
+ True
1924
+
1925
+ We test that :issue:`5843` is fixed::
1926
+
1927
+ sage: class Foo:
1928
+ ....: def __init__(self, x):
1929
+ ....: self._x = x
1930
+ ....: @cached_method
1931
+ ....: def f(self, y):
1932
+ ....: return self._x
1933
+ sage: a = Foo(2)
1934
+ sage: b = Foo(3)
1935
+ sage: a.f(b.f)
1936
+ 2
1937
+
1938
+ Check that :issue:`16316` has been fixed, i.e., caching works for
1939
+ immutable unhashable objects which define
1940
+ :meth:`sage.structure.sage_object.SageObject._cache_key`::
1941
+
1942
+ sage: # needs sage.rings.padics
1943
+ sage: K.<u> = Qq(4)
1944
+ sage: class A():
1945
+ ....: @cached_method
1946
+ ....: def f(self, x): return x+x
1947
+ sage: a = A()
1948
+ sage: x = K(1,1); x
1949
+ 1 + O(2)
1950
+ sage: y = K(1,2); y
1951
+ 1 + O(2^2)
1952
+ sage: x == y
1953
+ True
1954
+ sage: a.f(x) is a.f(x)
1955
+ True
1956
+ sage: a.f(y) is not a.f(x)
1957
+ True
1958
+ """
1959
+ if self._instance is None:
1960
+ # unbound cached method such as ``Foo.f``
1961
+ instance = args[0]
1962
+ args = args[1:]
1963
+ return self._cachedmethod.__get__(instance)(*args, **kwds)
1964
+
1965
+ k = self.get_key_args_kwds(args, kwds)
1966
+
1967
+ cdef dict cache = <dict>self.cache
1968
+ try:
1969
+ try:
1970
+ return cache[k]
1971
+ except TypeError: # k is not hashable
1972
+ k = dict_key(k)
1973
+ return cache[k]
1974
+ except KeyError:
1975
+ w = self._instance_call(*args, **kwds)
1976
+ cache[k] = w
1977
+ return w
1978
+
1979
+ def cached(self, *args, **kwds):
1980
+ """
1981
+ Return the result from the cache if available. If the value is
1982
+ not cached, raise :exc:`KeyError`.
1983
+
1984
+ EXAMPLES::
1985
+
1986
+ sage: class CachedMethodTest():
1987
+ ....: @cached_method
1988
+ ....: def f(self, x):
1989
+ ....: return x
1990
+ sage: o = CachedMethodTest()
1991
+ sage: CachedMethodTest.f.cached(o, 5)
1992
+ Traceback (most recent call last):
1993
+ ...
1994
+ KeyError: ((5,), ())
1995
+ sage: o.f.cached(5)
1996
+ Traceback (most recent call last):
1997
+ ...
1998
+ KeyError: ((5,), ())
1999
+ sage: o.f(5)
2000
+ 5
2001
+ sage: CachedMethodTest.f.cached(o, 5)
2002
+ 5
2003
+ sage: o.f.cached(5)
2004
+ 5
2005
+ """
2006
+ if self._instance is None:
2007
+ # unbound cached method such as ``CachedMethodTest.f``
2008
+ instance = args[0]
2009
+ args = args[1:]
2010
+ return self._cachedmethod.__get__(instance).cached(*args, **kwds)
2011
+
2012
+ k = self.get_key_args_kwds(args, kwds)
2013
+
2014
+ try:
2015
+ return self.cache[k]
2016
+ except TypeError: # k is not hashable
2017
+ k = dict_key(k)
2018
+ return self.cache[k]
2019
+
2020
+ def __get__(self, inst, cls):
2021
+ r"""
2022
+ Get a :class:`CachedMethodCaller` bound to a specific
2023
+ instance of the class of the cached method.
2024
+
2025
+ NOTE:
2026
+
2027
+ :class:`CachedMethodCaller` has a separate ``__get__``
2028
+ since the categories framework creates and caches the
2029
+ return value of ``CachedMethod.__get__`` with
2030
+ ``inst==None``.
2031
+
2032
+ This getter attempts to assign a bound method as an
2033
+ attribute to the given instance. If this is not
2034
+ possible (for example, for some extension classes),
2035
+ it is attempted to find an attribute ``_cached_methods``,
2036
+ and store/retrieve the bound method there. In that
2037
+ way, cached methods can be implemented for extension
2038
+ classes deriving from :class:`~sage.structure.parent.Parent`
2039
+ and :class:`~sage.structure.element.Element`.
2040
+
2041
+ TESTS:
2042
+
2043
+ Due to the separate ``__get__`` method, it is possible
2044
+ to define a cached method in one class and use it as
2045
+ an attribute of another class. ::
2046
+
2047
+ sage: class Foo:
2048
+ ....: @cached_method
2049
+ ....: def f(self, y):
2050
+ ....: return y - 1
2051
+ sage: class Bar:
2052
+ ....: f = Foo.f
2053
+ sage: b1 = Bar()
2054
+ sage: b2 = Bar()
2055
+
2056
+ The :class:`CachedMethod` is replaced by an instance
2057
+ of :class:`CachedMethodCaller` that (by :issue:`8611`)
2058
+ is set as an attribute. Hence, we have::
2059
+
2060
+ sage: b1.f is b1.f
2061
+ True
2062
+
2063
+ Any instance of ``Bar`` gets its own instance of
2064
+ :class:`CachedMethodCaller`::
2065
+
2066
+ sage: b1.f is b2.f
2067
+ False
2068
+
2069
+ The method caller knows the instance that it belongs
2070
+ to::
2071
+
2072
+ sage: Foo.f._instance is None
2073
+ True
2074
+ sage: b1.f._instance is b1
2075
+ True
2076
+ sage: b2.f._instance is b2
2077
+ True
2078
+
2079
+ An extension class can inherit a cached method from the
2080
+ parent or element class of a category (:issue:`11115`).
2081
+ See :class:`CachedMethodCaller` for examples.
2082
+
2083
+ Verify that :issue:`16337` has been resolved::
2084
+
2085
+ sage: class Foo:
2086
+ ....: @cached_method(key=lambda self,y: y+1)
2087
+ ....: def f(self, y):
2088
+ ....: return y - 1
2089
+ sage: class Bar:
2090
+ ....: f = Foo.f
2091
+
2092
+ sage: b = Bar()
2093
+ sage: b.f(0)
2094
+ -1
2095
+ sage: b.f.cache
2096
+ {1: -1}
2097
+ """
2098
+ # This is for Parents or Elements that do not allow attribute assignment
2099
+ try:
2100
+ return (<dict>inst._cached_methods)[self._cachedmethod._cachedfunc.__name__]
2101
+ except (AttributeError, TypeError, KeyError):
2102
+ pass
2103
+
2104
+ cls = type(self)
2105
+ Caller = cls(self._cachedmethod, inst,
2106
+ cache=self._cachedmethod._get_instance_cache(inst),
2107
+ name=self._cachedmethod._cachedfunc.__name__,
2108
+ key=self.key, do_pickle=self.do_pickle)
2109
+
2110
+ try:
2111
+ setattr(inst, self._cachedmethod._cachedfunc.__name__, Caller)
2112
+ return Caller
2113
+ except AttributeError:
2114
+ pass
2115
+ try:
2116
+ if inst._cached_methods is None:
2117
+ inst._cached_methods = {self._cachedmethod._cachedfunc.__name__: Caller}
2118
+ else:
2119
+ (<dict>inst._cached_methods)[self._cachedmethod._cachedfunc.__name__] = Caller
2120
+ except AttributeError:
2121
+ pass
2122
+ return Caller
2123
+
2124
+ def precompute(self, arglist, num_processes=1):
2125
+ """
2126
+ Cache values for a number of inputs. Do the computation
2127
+ in parallel, and only bother to compute values that we
2128
+ haven't already cached.
2129
+
2130
+ INPUT:
2131
+
2132
+ - ``arglist`` -- list (or iterables) of arguments for which
2133
+ the method shall be precomputed
2134
+
2135
+ - ``num_processes`` -- number of processes used by
2136
+ :func:`~sage.parallel.decorate.parallel`
2137
+
2138
+ EXAMPLES::
2139
+
2140
+ sage: class Foo():
2141
+ ....: @cached_method
2142
+ ....: def f(self, i):
2143
+ ....: return i^2
2144
+ sage: foo = Foo()
2145
+ sage: foo.f(3)
2146
+ 9
2147
+ sage: foo.f(1)
2148
+ 1
2149
+ sage: foo.f.precompute(range(2), 2)
2150
+ sage: foo.f.cache == {((0,), ()): 0, ((1,), ()): 1, ((3,), ()): 9}
2151
+ True
2152
+ """
2153
+ from sage.parallel.decorate import parallel, normalize_input
2154
+ P = parallel(num_processes)(self._instance_call)
2155
+ cdef list arglist2 = []
2156
+ for a in arglist:
2157
+ ak = normalize_input(a)
2158
+ if self.get_key_args_kwds(ak[0], ak[1]) not in self.cache:
2159
+ arglist2.append(ak)
2160
+ for ((args, kwargs), val) in P(arglist2):
2161
+ self.set_cache(val, *args, **kwargs)
2162
+
2163
+
2164
+ cdef class CachedMethodCallerNoArgs(CachedFunction):
2165
+ """
2166
+ Utility class that is used by :class:`CachedMethod` to bind a
2167
+ cached method to an instance, in the case of a method that does
2168
+ not accept any arguments except ``self``.
2169
+
2170
+ .. NOTE::
2171
+
2172
+ The return value ``None`` would not be cached. So, if you have
2173
+ a method that does not accept arguments and may return ``None``
2174
+ after a lengthy computation, then ``@cached_method`` should not
2175
+ be used.
2176
+
2177
+ EXAMPLES::
2178
+
2179
+ sage: P.<a,b,c,d> = QQ[]
2180
+ sage: I = P*[a,b]
2181
+ sage: I.gens
2182
+ Cached version of <function ...gens at 0x...>
2183
+ sage: type(I.gens)
2184
+ <class 'sage.misc.cachefunc.CachedMethodCallerNoArgs'>
2185
+ sage: I.gens is I.gens
2186
+ True
2187
+ sage: I.gens() is I.gens()
2188
+ True
2189
+
2190
+ TESTS:
2191
+
2192
+ As of :issue:`15692` the contents of the cache are not pickled anymore::
2193
+
2194
+ sage: class A:
2195
+ ....: @cached_method
2196
+ ....: def bar(self):
2197
+ ....: return 4
2198
+ sage: import __main__
2199
+ sage: __main__.A = A
2200
+ sage: a = A()
2201
+ sage: a.bar()
2202
+ 4
2203
+ sage: a.bar.cache
2204
+ 4
2205
+ sage: b = loads(dumps(a))
2206
+ sage: b.bar.cache
2207
+
2208
+ The parameter ``do_pickle`` can be used to change this behaviour::
2209
+
2210
+ sage: class A:
2211
+ ....: @cached_method(do_pickle=True)
2212
+ ....: def bar(self):
2213
+ ....: return 4
2214
+
2215
+ sage: __main__.A = A
2216
+ sage: a = A()
2217
+ sage: a.bar()
2218
+ 4
2219
+ sage: a.bar.cache
2220
+ 4
2221
+ sage: b = loads(dumps(a))
2222
+ sage: b.bar.cache
2223
+ 4
2224
+
2225
+ AUTHOR:
2226
+
2227
+ - Simon King (2011-04)
2228
+ """
2229
+ def __init__(self, inst, f, cache=None, name=None, do_pickle=None):
2230
+ """
2231
+ EXAMPLES::
2232
+
2233
+ sage: class Foo:
2234
+ ....: def __init__(self, x):
2235
+ ....: self._x = x
2236
+ ....: @cached_method
2237
+ ....: def f(self):
2238
+ ....: return self._x^2
2239
+ sage: a = Foo(2)
2240
+ sage: print(a.f.cache)
2241
+ None
2242
+ sage: a.f()
2243
+ 4
2244
+ sage: a.f.cache
2245
+ 4
2246
+ """
2247
+ # initialize CachedFunction
2248
+ if isinstance(f, str):
2249
+ try:
2250
+ F = getattr(inst.__class__, f)
2251
+ except AttributeError:
2252
+ F = getattr(inst, f)
2253
+ if isinstance(F, CachedFunction):
2254
+ f = F.f
2255
+ else:
2256
+ f = F
2257
+ self._common_init(f, None, name=name, do_pickle=do_pickle)
2258
+ # This is for unpickling a CachedMethodCallerNoArgs out
2259
+ # of an old CachedMethodCaller:
2260
+ cachename = '_cache__' + self.__name__
2261
+ if hasattr(inst, cachename):
2262
+ # This is for data that are pickled in an old format
2263
+ CACHE = getattr(inst, cachename)
2264
+ if len(CACHE) > 1:
2265
+ raise TypeError("Apparently you are opening a pickle in which '{}' was a method accepting arguments".format(name))
2266
+ if len(CACHE) == 1:
2267
+ self.cache = CACHE.values()[0]
2268
+ else:
2269
+ self.cache = cache
2270
+ delattr(inst, cachename)
2271
+ else:
2272
+ self.cache = cache # None means: the underlying method will be called
2273
+ self._instance = inst
2274
+
2275
+ def __reduce__(self):
2276
+ """
2277
+ Since functions cannot be pickled, the cached method caller
2278
+ is pickled by a :class:`CachedMethodPickle`, that replaces
2279
+ itself by an actual :class:`CachedMethodCallerNoArgs` as soon
2280
+ as it is asked to do anything.
2281
+
2282
+ TESTS::
2283
+
2284
+ sage: P.<a,b,c,d> = QQ[]
2285
+ sage: I = P*[a,b]
2286
+ sage: I.gens()
2287
+ [a, b]
2288
+ sage: I.gens
2289
+ Cached version of <function ...gens at 0x...>
2290
+ sage: J = loads(dumps(I))
2291
+ sage: J.gens
2292
+ Pickle of the cached method "gens"
2293
+ sage: J.gens.cache # the cache is dropped because gens is not marked with do_pickle=True
2294
+ sage: J.gens
2295
+ Cached version of <function ...gens at 0x...>
2296
+ """
2297
+ if self.do_pickle:
2298
+ return CachedMethodPickle, (self._instance, self.__name__, self.cache)
2299
+ return CachedMethodPickle, (self._instance, self.__name__)
2300
+
2301
+ def _instance_call(self):
2302
+ """
2303
+ Call the cached method without using the cache.
2304
+
2305
+ EXAMPLES::
2306
+
2307
+ sage: P.<a,b,c,d> = QQ[]
2308
+ sage: I = P*[a,b]
2309
+ sage: I.gens()
2310
+ [a, b]
2311
+ sage: I.gens._instance_call() is I.gens()
2312
+ False
2313
+ sage: I.gens._instance_call() == I.gens()
2314
+ True
2315
+ """
2316
+ return self.f(self._instance)
2317
+
2318
+ def __call__(self):
2319
+ """
2320
+ Call the cached method.
2321
+
2322
+ EXAMPLES::
2323
+
2324
+ sage: P.<a,b,c,d> = QQ[]
2325
+ sage: I = P*[a,b]
2326
+ sage: I.gens() # indirect doctest
2327
+ [a, b]
2328
+ sage: I.gens() is I.gens()
2329
+ True
2330
+ """
2331
+ if self.cache is None:
2332
+ f = self.f
2333
+ self.cache = f(self._instance)
2334
+ return self.cache
2335
+
2336
+ def set_cache(self, value):
2337
+ """
2338
+ Override the cache with a specific value.
2339
+
2340
+ .. NOTE::
2341
+
2342
+ ``None`` is not suitable for a cached value. It would be
2343
+ interpreted as an empty cache, forcing a new computation.
2344
+
2345
+ EXAMPLES::
2346
+
2347
+ sage: P.<a,b,c,d> = QQ[]
2348
+ sage: I = P*[a,b]
2349
+ sage: I.gens()
2350
+ [a, b]
2351
+ sage: I.gens.set_cache('bar')
2352
+ sage: I.gens()
2353
+ 'bar'
2354
+
2355
+ The cache can be emptied and thus the original value will
2356
+ be reconstructed::
2357
+
2358
+ sage: I.gens.clear_cache()
2359
+ sage: I.gens()
2360
+ [a, b]
2361
+
2362
+ The attempt to assign ``None`` to the cache fails::
2363
+
2364
+ sage: I.gens.set_cache(None)
2365
+ sage: I.gens()
2366
+ [a, b]
2367
+ """
2368
+ self.cache = value
2369
+
2370
+ def clear_cache(self):
2371
+ r"""
2372
+ Clear the cache dictionary.
2373
+
2374
+ EXAMPLES::
2375
+
2376
+ sage: P.<a,b,c,d> = QQ[]
2377
+ sage: I = P*[a,b]
2378
+ sage: I.gens()
2379
+ [a, b]
2380
+ sage: I.gens.set_cache('bar')
2381
+ sage: I.gens()
2382
+ 'bar'
2383
+
2384
+ The cache can be emptied and thus the original value will
2385
+ be reconstructed::
2386
+
2387
+ sage: I.gens.clear_cache()
2388
+ sage: I.gens()
2389
+ [a, b]
2390
+ """
2391
+ self.cache = None
2392
+
2393
+ def is_in_cache(self):
2394
+ """
2395
+ Answers whether the return value is already in the cache.
2396
+
2397
+ .. NOTE::
2398
+
2399
+ Recall that a cached method without arguments cannot cache
2400
+ the return value ``None``.
2401
+
2402
+ EXAMPLES::
2403
+
2404
+ sage: P.<x,y> = QQ[]
2405
+ sage: I = P*[x,y]
2406
+ sage: I.gens.is_in_cache()
2407
+ False
2408
+ sage: I.gens()
2409
+ [x, y]
2410
+ sage: I.gens.is_in_cache()
2411
+ True
2412
+ """
2413
+ return self.cache is not None
2414
+
2415
+ def __get__(self, inst, cls):
2416
+ """
2417
+ Get a :class:`CachedMethodCallerNoArgs` bound to a specific
2418
+ instance of the class of the cached method.
2419
+
2420
+ NOTE:
2421
+
2422
+ :class:`CachedMethodCallerNoArgs` has a separate ``__get__``
2423
+ since the categories framework creates and caches the
2424
+ return value of ``CachedMethod.__get__`` with
2425
+ ``inst==None``.
2426
+
2427
+ This getter attempts to assign a bound method as an
2428
+ attribute to the given instance. If this is not
2429
+ possible (for example, for some extension classes),
2430
+ it is attempted to find an attribute ``_cached_methods``,
2431
+ and store/retrieve the bound method there. In that
2432
+ way, cached methods can be implemented for extension
2433
+ classes deriving from :class:`~sage.structure.parent.Parent`
2434
+ and :class:`~sage.structure.element.Element`.
2435
+
2436
+ TESTS:
2437
+
2438
+ Due to the separate ``__get__`` method, it is possible
2439
+ to define a cached method in one class and use it as
2440
+ an attribute of another class. ::
2441
+
2442
+ sage: class Foo:
2443
+ ....: def __init__(self, n):
2444
+ ....: self.__n = n
2445
+ ....: @cached_method
2446
+ ....: def f(self):
2447
+ ....: return self.__n^2
2448
+ sage: class Bar:
2449
+ ....: f = Foo.f
2450
+ sage: b1 = Bar()
2451
+ sage: b2 = Bar()
2452
+
2453
+ The :class:`CachedMethod` is replaced by an instance of
2454
+ :class:`CachedMethodCallerNoArgs` that is set as an
2455
+ attribute. Hence, we have::
2456
+
2457
+ sage: b1.f is b1.f
2458
+ True
2459
+ sage: type(b1.f)
2460
+ <class 'sage.misc.cachefunc.CachedMethodCallerNoArgs'>
2461
+
2462
+ Any instance of ``Bar`` gets its own instance of
2463
+ :class:`CachedMethodCaller`::
2464
+
2465
+ sage: b1.f is b2.f
2466
+ False
2467
+
2468
+ The method caller knows the instance that it belongs
2469
+ to::
2470
+
2471
+ sage: Foo.f._instance is None
2472
+ True
2473
+ sage: b1.f._instance is b1
2474
+ True
2475
+ sage: b2.f._instance is b2
2476
+ True
2477
+ """
2478
+ # This is for Parents or Elements that do not allow attribute assignment
2479
+ try:
2480
+ return (<dict>inst._cached_methods)[self.__name__]
2481
+ except (AttributeError, TypeError, KeyError):
2482
+ pass
2483
+ Caller = CachedMethodCallerNoArgs(inst, self.f, name=self.__name__, do_pickle=self.do_pickle)
2484
+ try:
2485
+ setattr(inst, self.__name__, Caller)
2486
+ return Caller
2487
+ except AttributeError:
2488
+ pass
2489
+ try:
2490
+ if inst._cached_methods is None:
2491
+ inst._cached_methods = {self.__name__: Caller}
2492
+ else:
2493
+ (<dict>inst._cached_methods)[self.__name__] = Caller
2494
+ except AttributeError:
2495
+ pass
2496
+ return Caller
2497
+
2498
+
2499
+ cdef class GloballyCachedMethodCaller(CachedMethodCaller):
2500
+ """
2501
+ Implementation of cached methods in case that the cache is not
2502
+ stored in the instance, but in some global object. In particular,
2503
+ it is used to implement :class:`CachedInParentMethod`.
2504
+
2505
+ The only difference is that the instance is used as part of the
2506
+ key.
2507
+ """
2508
+ cdef get_key_args_kwds(self, tuple args, dict kwds):
2509
+ """
2510
+ Return the key in the cache to be used when ``args`` and
2511
+ ``kwds`` are passed in as parameters.
2512
+
2513
+ EXAMPLES::
2514
+
2515
+ sage: class MyParent(Parent):
2516
+ ....: pass
2517
+ sage: class MyElement(): # indirect doctest
2518
+ ....: def __init__(self, x):
2519
+ ....: self.x = x
2520
+ ....: def parent(self):
2521
+ ....: return MyParent()
2522
+ ....: @cached_in_parent_method
2523
+ ....: def f(self):
2524
+ ....: return self.x^2
2525
+ sage: a = MyElement(2)
2526
+ sage: a.f.get_key()
2527
+ (<__main__.MyElement object at 0x...>, ((), ()))
2528
+ sage: a.f.get_key()[0] is a
2529
+ True
2530
+ """
2531
+ k = self.fix_args_kwds(args, kwds)
2532
+ return (self._instance, k)
2533
+
2534
+
2535
+ cdef class CachedMethod():
2536
+ """
2537
+ A decorator that creates a cached version of an instance
2538
+ method of a class.
2539
+
2540
+ .. NOTE::
2541
+
2542
+ For proper behavior, the method must be a pure function (no side
2543
+ effects). Arguments to the method must be hashable or transformed into
2544
+ something hashable using ``key`` or they must define
2545
+ :meth:`sage.structure.sage_object.SageObject._cache_key`.
2546
+
2547
+ EXAMPLES::
2548
+
2549
+ sage: class Foo():
2550
+ ....: @cached_method
2551
+ ....: def f(self, t, x=2):
2552
+ ....: print('computing')
2553
+ ....: return t**x
2554
+ sage: a = Foo()
2555
+
2556
+ The example shows that the actual computation
2557
+ takes place only once, and that the result is
2558
+ identical for equivalent input::
2559
+
2560
+ sage: res = a.f(3, 2); res
2561
+ computing
2562
+ 9
2563
+ sage: a.f(t = 3, x = 2) is res
2564
+ True
2565
+ sage: a.f(3) is res
2566
+ True
2567
+
2568
+ Note, however, that accessing the attribute directly will call :meth:`__get__`,
2569
+ and returns a :class:`CachedMethodCaller` or :class:`CachedMethodCallerNoArgs`.
2570
+
2571
+ ::
2572
+
2573
+ sage: P.<a,b,c,d> = QQ[]
2574
+ sage: I = P*[a,b]
2575
+ sage: type(I.__class__.gens)
2576
+ <class 'sage.misc.cachefunc.CachedMethodCallerNoArgs'>
2577
+ sage: type(I.__class__.__dict__["gens"])
2578
+ <class 'sage.misc.cachefunc.CachedMethod'>
2579
+
2580
+ The parameter ``key`` can be used to pass a function which creates a
2581
+ custom cache key for inputs. In the following example, this parameter is
2582
+ used to ignore the ``algorithm`` keyword for caching::
2583
+
2584
+ sage: class A():
2585
+ ....: def _f_normalize(self, x, algorithm): return x
2586
+ ....: @cached_method(key=_f_normalize)
2587
+ ....: def f(self, x, algorithm='default'): return x
2588
+ sage: a = A()
2589
+ sage: a.f(1, algorithm='default') is a.f(1) is a.f(1, algorithm='algorithm')
2590
+ True
2591
+
2592
+ The parameter ``do_pickle`` can be used to enable pickling of the cache.
2593
+ Usually the cache is not stored when pickling::
2594
+
2595
+ sage: class A():
2596
+ ....: @cached_method
2597
+ ....: def f(self, x): return None
2598
+ sage: import __main__
2599
+ sage: __main__.A = A
2600
+ sage: a = A()
2601
+ sage: a.f(1)
2602
+ sage: len(a.f.cache)
2603
+ 1
2604
+ sage: b = loads(dumps(a))
2605
+ sage: len(b.f.cache)
2606
+ 0
2607
+
2608
+ When ``do_pickle`` is set, the pickle contains the contents of the cache::
2609
+
2610
+ sage: class A():
2611
+ ....: @cached_method(do_pickle=True)
2612
+ ....: def f(self, x): return None
2613
+ sage: __main__.A = A
2614
+ sage: a = A()
2615
+ sage: a.f(1)
2616
+ sage: len(a.f.cache)
2617
+ 1
2618
+ sage: b = loads(dumps(a))
2619
+ sage: len(b.f.cache)
2620
+ 1
2621
+
2622
+ Cached methods cannot be copied like usual methods, see :issue:`12603`.
2623
+ Copying them can lead to very surprising results::
2624
+
2625
+ sage: class A:
2626
+ ....: @cached_method
2627
+ ....: def f(self):
2628
+ ....: return 1
2629
+ sage: class B:
2630
+ ....: g=A.f
2631
+ ....: def f(self):
2632
+ ....: return 2
2633
+
2634
+ sage: b=B()
2635
+ sage: b.f()
2636
+ 2
2637
+ sage: b.g()
2638
+ 1
2639
+ sage: b.f()
2640
+ 1
2641
+ """
2642
+ def __init__(self, f, name=None, key=None, do_pickle=None):
2643
+ """
2644
+ EXAMPLES::
2645
+
2646
+ sage: class Foo():
2647
+ ....: def __init__(self, x):
2648
+ ....: self._x = x
2649
+ ....: @cached_method
2650
+ ....: def f(self, n):
2651
+ ....: return self._x^n
2652
+ ....: @cached_method
2653
+ ....: def f0(self):
2654
+ ....: return self._x^2
2655
+ sage: a = Foo(2)
2656
+ sage: a.f(2)
2657
+ 4
2658
+ sage: a.f0()
2659
+ 4
2660
+
2661
+ For methods with parameters, the results of method ``f`` is attempted
2662
+ to be stored in a dictionary attribute of the instance ``a``::
2663
+
2664
+ sage: hasattr(a, '_cache__f')
2665
+ True
2666
+ sage: a._cache__f
2667
+ {((2,), ()): 4}
2668
+ sage: a._cache_f0
2669
+ Traceback (most recent call last):
2670
+ ...
2671
+ AttributeError: 'Foo' object has no attribute '_cache_f0'...
2672
+
2673
+ As a shortcut, useful to speed up internal computations,
2674
+ the same dictionary is also available as an attribute
2675
+ of the ``CachedMethodCaller``::
2676
+
2677
+ sage: type(a.f)
2678
+ <class 'sage.misc.cachefunc.CachedMethodCaller'>
2679
+ sage: a.f.cache is a._cache__f
2680
+ True
2681
+
2682
+ Note that if the instance ``a`` would not accept attribute
2683
+ assignment, the computations would still be cached in
2684
+ ``a.f.cache``, and they would in fact be preserved when
2685
+ pickling.
2686
+
2687
+ The cached method ``f0`` accepts no arguments, which allows
2688
+ for an improved way of caching: By an attribute of the cached
2689
+ method itself. This cache is *only* available in that way, i.e.,
2690
+ it is not additionally stored as an attribute of ``a``::
2691
+
2692
+ sage: type(a.f0)
2693
+ <class 'sage.misc.cachefunc.CachedMethodCallerNoArgs'>
2694
+ sage: a.f0.cache
2695
+ 4
2696
+ sage: sorted(n for n in dir(a) if not n.startswith('__'))
2697
+ ['_cache__f', '_x', 'f', 'f0']
2698
+
2699
+ The cached method has its name and module set::
2700
+
2701
+ sage: f = Foo.__dict__["f"]
2702
+ sage: f.__name__
2703
+ 'f'
2704
+ sage: f.__module__
2705
+ '__main__'
2706
+ """
2707
+ self._cache_name = '_cache__' + (name or f.__name__)
2708
+ self._cachedfunc = CachedFunction(f, classmethod=True, name=name, key=key, do_pickle=do_pickle)
2709
+ self.__name__ = self._cachedfunc.__name__
2710
+ self.__cached_module__ = self._cachedfunc.__module__
2711
+
2712
+ @property
2713
+ def __module__(self):
2714
+ return self.__cached_module__
2715
+
2716
+ def __call__(self, inst, *args, **kwds):
2717
+ """
2718
+ Call the cached method as a function on an instance.
2719
+ This code path is not used directly except in a few rare cases,
2720
+ see examples for details.
2721
+
2722
+ INPUT:
2723
+
2724
+ - ``inst`` -- an instance on which the method is to be called
2725
+ - further positional or named arguments
2726
+
2727
+ EXAMPLES::
2728
+
2729
+
2730
+ sage: from sage.misc.superseded import deprecated_function_alias
2731
+ sage: class Foo():
2732
+ ....: def __init__(self, x):
2733
+ ....: self._x = x
2734
+ ....: @cached_method
2735
+ ....: def f(self, n=2):
2736
+ ....: return self._x^n
2737
+ ....: g = deprecated_function_alias(57, f)
2738
+ sage: a = Foo(2)
2739
+ sage: Foo.__dict__['f'](a)
2740
+ 4
2741
+
2742
+ This uses the cache as usual::
2743
+
2744
+ sage: Foo.__dict__['f'](a) is a.f()
2745
+ True
2746
+
2747
+ This feature makes cached methods compatible with
2748
+ :meth:`sage.misc.superseded.deprecated_function_alias`::
2749
+
2750
+ sage: a.g() is a.f()
2751
+ doctest:...: DeprecationWarning: g is deprecated. Please use f instead.
2752
+ See https://github.com/sagemath/sage/issues/57 for details.
2753
+ True
2754
+ sage: Foo.g(a) is a.f()
2755
+ True
2756
+ """
2757
+ return self.__get__(inst)(*args, **kwds)
2758
+
2759
+ cpdef _get_instance_cache(self, inst):
2760
+ """
2761
+ Return the cache dictionary.
2762
+
2763
+ TESTS::
2764
+
2765
+ sage: class Foo:
2766
+ ....: def __init__(self, x):
2767
+ ....: self._x = x
2768
+ ....: @cached_method
2769
+ ....: def f(self, n=2):
2770
+ ....: return self._x^n
2771
+ sage: a = Foo(2)
2772
+
2773
+ Initially ``_cache__f`` is not an attribute of ``a``::
2774
+
2775
+ sage: hasattr(a, "_cache__f")
2776
+ False
2777
+
2778
+ When the attribute is accessed (thus ``__get__`` is called),
2779
+ the cache is created and assigned to the attribute::
2780
+
2781
+ sage: a.f
2782
+ Cached version of <function Foo.f at 0x...>
2783
+ sage: a._cache__f
2784
+ {}
2785
+ sage: a.f()
2786
+ 4
2787
+ sage: a.f.cache
2788
+ {((2,), ()): 4}
2789
+ sage: a._cache__f
2790
+ {((2,), ()): 4}
2791
+
2792
+ Testing the method directly::
2793
+
2794
+ sage: a = Foo(2)
2795
+ sage: hasattr(a, "_cache__f")
2796
+ False
2797
+ sage: Foo.__dict__["f"]._get_instance_cache(a)
2798
+ {}
2799
+ sage: a._cache__f
2800
+ {}
2801
+ sage: a.f()
2802
+ 4
2803
+ sage: Foo.__dict__["f"]._get_instance_cache(a)
2804
+ {((2,), ()): 4}
2805
+
2806
+ Using ``__dict__`` is needed to access this function because
2807
+ ``Foo.f`` would call ``__get__`` and thus create a
2808
+ :class:`CachedMethodCaller`::
2809
+
2810
+ sage: type(Foo.f)
2811
+ <class 'sage.misc.cachefunc.CachedMethodCaller'>
2812
+ sage: type(Foo.__dict__["f"])
2813
+ <class 'sage.misc.cachefunc.CachedMethod'>
2814
+ """
2815
+ default = {} if self._cachedfunc.do_pickle else NonpicklingDict()
2816
+ try:
2817
+ return inst.__dict__.setdefault(self._cache_name, default)
2818
+ except AttributeError:
2819
+ return default
2820
+
2821
+ def __get__(self, object inst, cls):
2822
+ """
2823
+ Get a CachedMethodCaller bound to this specific instance of
2824
+ the class of the cached method.
2825
+
2826
+ TESTS::
2827
+
2828
+ sage: class Foo:
2829
+ ....: @cached_method
2830
+ ....: def f(self):
2831
+ ....: return 1
2832
+ ....: @cached_method
2833
+ ....: def g(self, x, n=3):
2834
+ ....: return x^n
2835
+ sage: a = Foo()
2836
+ sage: type(a.f)
2837
+ <class 'sage.misc.cachefunc.CachedMethodCallerNoArgs'>
2838
+ sage: type(a.g)
2839
+ <class 'sage.misc.cachefunc.CachedMethodCaller'>
2840
+
2841
+ By :issue:`8611`, it is attempted to set the
2842
+ CachedMethodCaller as an attribute of the instance ``a``,
2843
+ replacing the original cached attribute. Therefore, the
2844
+ ``__get__`` method will be used only once, which saves much
2845
+ time. Hence, we have::
2846
+
2847
+ sage: a.f is a.f
2848
+ True
2849
+ sage: a.g is a.g
2850
+ True
2851
+
2852
+ Verify that :issue:`16337` has been resolved::
2853
+
2854
+ sage: class Foo:
2855
+ ....: @cached_method(key=lambda self, x:x+1)
2856
+ ....: def f(self, x=0):
2857
+ ....: return x
2858
+
2859
+ sage: a = Foo()
2860
+ sage: a.f(0)
2861
+ 0
2862
+ sage: a.f.cache
2863
+ {1: 0}
2864
+ """
2865
+ # This is for Parents or Elements that do not allow attribute assignment:
2866
+ cdef str name
2867
+ try:
2868
+ name = self._cachedfunc.__name__
2869
+ except AttributeError:
2870
+ name = self.__name__
2871
+ try:
2872
+ return (<dict>inst._cached_methods)[name]
2873
+ except (AttributeError, TypeError, KeyError):
2874
+ pass
2875
+ # Apparently we need to construct the caller.
2876
+ # Since we have an optimized version for functions that do not accept arguments,
2877
+ # we need to analyse the argspec
2878
+ f = self._cachedfunc.f
2879
+ if self.nargs == 0:
2880
+ if isinstance(f, object) and not isfunction(f):
2881
+ try:
2882
+ if METH_NOARGS&PyCFunction_GetFlags(f.__get__(inst, cls)):
2883
+ self.nargs = 1
2884
+ except Exception:
2885
+ pass
2886
+ if self.nargs == 0:
2887
+ args, varargs, keywords, defaults, kwonlyargs, kwonlydefaults, annotations = sage_getargspec(f)
2888
+ if varargs is None and keywords is None and len(args)<=1:
2889
+ self.nargs = 1
2890
+ else:
2891
+ self.nargs = 2 # don't need the exact number
2892
+ if self.nargs == 1:
2893
+ Caller = CachedMethodCallerNoArgs(inst, f, name=name, do_pickle=self._cachedfunc.do_pickle)
2894
+ else:
2895
+ Caller = CachedMethodCaller(self, inst,
2896
+ cache=self._get_instance_cache(inst),
2897
+ name=name,
2898
+ key=self._cachedfunc.key,
2899
+ do_pickle=self._cachedfunc.do_pickle)
2900
+ try:
2901
+ setattr(inst, name, Caller)
2902
+ return Caller
2903
+ except AttributeError:
2904
+ pass
2905
+ try:
2906
+ if inst._cached_methods is None:
2907
+ inst._cached_methods = {name: Caller}
2908
+ else:
2909
+ (<dict>inst._cached_methods)[name] = Caller
2910
+ except AttributeError:
2911
+ pass
2912
+ return Caller
2913
+
2914
+
2915
+ cdef class CachedSpecialMethod(CachedMethod):
2916
+ """
2917
+ Cached version of *special* python methods.
2918
+
2919
+ IMPLEMENTATION:
2920
+
2921
+ For new style classes ``C``, it is not possible to override a special
2922
+ method, such as ``__hash__``, in the ``__dict__`` of an instance ``c`` of
2923
+ ``C``, because Python will always use what is provided by the class, not
2924
+ by the instance to avoid metaclass confusion. See
2925
+ `<https://docs.python.org/3/reference/datamodel.html#special-method-lookup>`_.
2926
+
2927
+ By consequence, if ``__hash__`` would be wrapped by using
2928
+ :class:`CachedMethod`, then ``hash(c)`` will access ``C.__hash__`` and bind
2929
+ it to ``c``, which means that the ``__get__`` method of
2930
+ :class:`CachedMethod` will be called. But there, we assume that Python has
2931
+ already inspected ``__dict__``, and thus a :class:`CachedMethodCaller`
2932
+ will be created over and over again.
2933
+
2934
+ Here, the ``__get__`` method will explicitly access the ``__dict__``, so that
2935
+ ``hash(c)`` will rely on a single :class:`CachedMethodCaller` stored in
2936
+ the ``__dict__``.
2937
+
2938
+ EXAMPLES::
2939
+
2940
+ sage: class C:
2941
+ ....: @cached_method
2942
+ ....: def __hash__(self):
2943
+ ....: print("compute hash")
2944
+ ....: return int(5)
2945
+ ....:
2946
+ sage: c = C()
2947
+ sage: type(C.__hash__)
2948
+ <class 'sage.misc.cachefunc.CachedMethodCallerNoArgs'>
2949
+
2950
+ The hash is computed only once, subsequent calls will use the value from
2951
+ the cache. This was implemented in :issue:`12601`.
2952
+
2953
+ ::
2954
+
2955
+ sage: hash(c) # indirect doctest
2956
+ compute hash
2957
+ 5
2958
+ sage: hash(c)
2959
+ 5
2960
+ """
2961
+ def __get__(self, object inst, cls):
2962
+ """
2963
+ Bind a :class:`CachedMethodCaller` to a specific instance, using ``__dict__``.
2964
+
2965
+ EXAMPLES::
2966
+
2967
+ sage: class C:
2968
+ ....: @cached_method
2969
+ ....: def __hash__(self):
2970
+ ....: print("compute hash")
2971
+ ....: return int(5)
2972
+ sage: c = C()
2973
+ sage: type(C.__hash__)
2974
+ <class 'sage.misc.cachefunc.CachedMethodCallerNoArgs'>
2975
+ sage: hash(c) # indirect doctest
2976
+ compute hash
2977
+ 5
2978
+ sage: hash(c)
2979
+ 5
2980
+
2981
+ Verify that :issue:`16337` has been resolved::
2982
+
2983
+ sage: class Foo:
2984
+ ....: @cached_method(key=lambda self, x:x+1)
2985
+ ....: def __hash__(self, x=0):
2986
+ ....: return x
2987
+
2988
+ sage: a = Foo()
2989
+ sage: a.__hash__(0)
2990
+ 0
2991
+ sage: a.__hash__.cache
2992
+ {1: 0}
2993
+ """
2994
+ # This is for Parents or Elements that do not allow attribute assignment:
2995
+ cdef str name
2996
+ try:
2997
+ name = self._cachedfunc.__name__
2998
+ except AttributeError:
2999
+ name = self.__name__
3000
+ cdef dict D = None
3001
+ if inst is not None:
3002
+ try:
3003
+ D = inst.__dict__
3004
+ except (TypeError, AttributeError):
3005
+ try:
3006
+ D = inst._cached_methods
3007
+ except (TypeError, AttributeError):
3008
+ raise TypeError("For a cached special method, either attribute assignment or a public '_cached_methods' attribute of type <dict> is needed")
3009
+ if D is None:
3010
+ # This can only happen in the case of _cached_methods
3011
+ D = inst._cached_methods = {}
3012
+ else:
3013
+ try:
3014
+ return D[name]
3015
+ except KeyError:
3016
+ pass
3017
+ # Apparently we need to construct the caller.
3018
+ # Since we have an optimized version for functions that do not accept arguments,
3019
+ # we need to analyse the argspec
3020
+ f = self._cachedfunc.f
3021
+ if self.nargs == 0:
3022
+ args, varargs, keywords, defaults, kwonlyargs, kwonlydefaults, annotations = sage_getargspec(f)
3023
+ if varargs is None and keywords is None and len(args)<=1:
3024
+ self.nargs = 1
3025
+ Caller = CachedMethodCallerNoArgs(inst, f, name=name, do_pickle=self._cachedfunc.do_pickle)
3026
+ else:
3027
+ self.nargs = 2 # don't need the exact number
3028
+ Caller = CachedMethodCaller(self, inst,
3029
+ cache=self._get_instance_cache(inst),
3030
+ name=name,
3031
+ key=self._cachedfunc.key,
3032
+ do_pickle=self._cachedfunc.do_pickle)
3033
+ elif self.nargs == 1:
3034
+ Caller = CachedMethodCallerNoArgs(inst, f, name=name, do_pickle=self._cachedfunc.do_pickle)
3035
+ else:
3036
+ Caller = CachedMethodCaller(self, inst,
3037
+ cache=self._get_instance_cache(inst),
3038
+ name=name,
3039
+ key=self._cachedfunc.key,
3040
+ do_pickle=self._cachedfunc.do_pickle)
3041
+ if inst is not None:
3042
+ try:
3043
+ setattr(inst, name, Caller)
3044
+ return Caller
3045
+ except AttributeError:
3046
+ pass
3047
+ D[name] = Caller
3048
+ return Caller
3049
+
3050
+
3051
+ @decorator_keywords
3052
+ def cached_method(f, name=None, key=None, do_pickle=None):
3053
+ """
3054
+ A decorator for cached methods.
3055
+
3056
+ EXAMPLES:
3057
+
3058
+ In the following examples, one can see how a cached method works
3059
+ in application. Below, we demonstrate what is done behind the scenes::
3060
+
3061
+ sage: class C:
3062
+ ....: @cached_method
3063
+ ....: def __hash__(self):
3064
+ ....: print("compute hash")
3065
+ ....: return int(5)
3066
+ ....: @cached_method
3067
+ ....: def f(self, x):
3068
+ ....: print("computing cached method")
3069
+ ....: return x*2
3070
+ sage: c = C()
3071
+ sage: type(C.__hash__)
3072
+ <class 'sage.misc.cachefunc.CachedMethodCallerNoArgs'>
3073
+ sage: hash(c)
3074
+ compute hash
3075
+ 5
3076
+
3077
+ When calling a cached method for the second time with the same arguments,
3078
+ the value is gotten from the cache, so that a new computation is not
3079
+ needed::
3080
+
3081
+ sage: hash(c)
3082
+ 5
3083
+ sage: c.f(4)
3084
+ computing cached method
3085
+ 8
3086
+ sage: c.f(4) is c.f(4)
3087
+ True
3088
+
3089
+ Different instances have distinct caches::
3090
+
3091
+ sage: d = C()
3092
+ sage: d.f(4) is c.f(4)
3093
+ computing cached method
3094
+ False
3095
+ sage: d.f.clear_cache()
3096
+ sage: c.f(4)
3097
+ 8
3098
+ sage: d.f(4)
3099
+ computing cached method
3100
+ 8
3101
+
3102
+ Using cached methods for the hash and other special methods was
3103
+ implemented in :issue:`12601`, by means of :class:`CachedSpecialMethod`. We
3104
+ show that it is used behind the scenes::
3105
+
3106
+ sage: cached_method(c.__hash__)
3107
+ <sage.misc.cachefunc.CachedSpecialMethod object at ...>
3108
+ sage: cached_method(c.f)
3109
+ <sage.misc.cachefunc.CachedMethod object at ...>
3110
+
3111
+ The parameter ``do_pickle`` can be used if the contents of the cache should be
3112
+ stored in a pickle of the cached method. This can be dangerous with special
3113
+ methods such as ``__hash__``::
3114
+
3115
+ sage: class C:
3116
+ ....: @cached_method(do_pickle=True)
3117
+ ....: def __hash__(self):
3118
+ ....: return id(self)
3119
+
3120
+ sage: import __main__
3121
+ sage: __main__.C = C
3122
+ sage: c = C()
3123
+ sage: hash(c) # random output
3124
+ sage: d = loads(dumps(c))
3125
+ sage: hash(d) == hash(c)
3126
+ True
3127
+
3128
+ However, the contents of a method's cache are not pickled unless ``do_pickle``
3129
+ is set::
3130
+
3131
+ sage: class C:
3132
+ ....: @cached_method
3133
+ ....: def __hash__(self):
3134
+ ....: return id(self)
3135
+
3136
+ sage: __main__.C = C
3137
+ sage: c = C()
3138
+ sage: hash(c) # random output
3139
+ sage: d = loads(dumps(c))
3140
+ sage: hash(d) == hash(c)
3141
+ False
3142
+ """
3143
+ cdef str fname = name or f.__name__
3144
+ if fname in special_method_names:
3145
+ return CachedSpecialMethod(f, name, key=key, do_pickle=do_pickle)
3146
+ return CachedMethod(f, name, key=key, do_pickle=do_pickle)
3147
+
3148
+
3149
+ cdef class CachedInParentMethod(CachedMethod):
3150
+ r"""
3151
+ A decorator that creates a cached version of an instance
3152
+ method of a class.
3153
+
3154
+ In contrast to :class:`CachedMethod`,
3155
+ the cache dictionary is an attribute of the parent of
3156
+ the instance to which the method belongs.
3157
+
3158
+ ASSUMPTION:
3159
+
3160
+ This way of caching works only if
3161
+
3162
+ - the instances *have* a parent, and
3163
+ - the instances are hashable (they are part of the cache key) or they
3164
+ define :meth:`sage.structure.sage_object.SageObject._cache_key`
3165
+
3166
+ NOTE:
3167
+
3168
+ For proper behavior, the method must be a pure function (no side effects).
3169
+ If this decorator is used on a method, it will have identical output on
3170
+ equal elements. This is since the element is part of the hash key.
3171
+ Arguments to the method must be hashable or define
3172
+ :meth:`sage.structure.sage_object.SageObject._cache_key`. The instance it
3173
+ is assigned to must be hashable.
3174
+
3175
+ Examples can be found at :mod:`~sage.misc.cachefunc`.
3176
+ """
3177
+
3178
+ def __init__(self, f, name=None, key=None, do_pickle=None):
3179
+ """
3180
+ Construct a new method with cache stored in the parent of the instance.
3181
+
3182
+ See also ``cached_method`` and ``cached_function``.
3183
+
3184
+ EXAMPLES::
3185
+
3186
+ sage: class MyParent(Parent):
3187
+ ....: pass
3188
+ sage: class Foo: # indirect doctest
3189
+ ....: def __init__(self, x):
3190
+ ....: self._x = x
3191
+ ....: self._parent = MyParent()
3192
+ ....: def parent(self):
3193
+ ....: return self._parent
3194
+ ....: @cached_in_parent_method
3195
+ ....: def f(self):
3196
+ ....: return self._x^2
3197
+ sage: a = Foo(2)
3198
+ sage: a.f()
3199
+ 4
3200
+ sage: b = Foo(3)
3201
+ sage: b.f()
3202
+ 9
3203
+ sage: hasattr(a.parent(), '_cache__element_f')
3204
+ True
3205
+
3206
+ For speeding up internal computations, this dictionary
3207
+ is also accessible as an attribute of the CachedMethodCaller
3208
+ (by :issue:`8611`)::
3209
+
3210
+ sage: a.parent()._cache__element_f is a.f.cache
3211
+ True
3212
+
3213
+ TESTS:
3214
+
3215
+ Test that ``key`` works::
3216
+
3217
+ sage: class A():
3218
+ ....: def __init__(self):
3219
+ ....: self._parent = MyParent()
3220
+ ....: def parent(self): return self._parent
3221
+ ....: def _f_normalize(self, x, algorithm): return x
3222
+ ....: @cached_in_parent_method(key=_f_normalize)
3223
+ ....: def f(self, x, algorithm='default'): return x
3224
+ sage: a = A()
3225
+ sage: a.f(1, algorithm='default') is a.f(1) is a.f(1, algorithm='algorithm')
3226
+ True
3227
+
3228
+ Test that ``do_pickle`` works. Usually the contents of the cache are not
3229
+ pickled::
3230
+
3231
+ sage: class A():
3232
+ ....: def __init__(self):
3233
+ ....: self._parent = MyParent()
3234
+ ....: def parent(self): return self._parent
3235
+ ....: @cached_in_parent_method
3236
+ ....: def f(self, x): return x
3237
+ sage: import __main__
3238
+ sage: __main__.A = A
3239
+ sage: __main__.MyParent = MyParent
3240
+ sage: a = A()
3241
+ sage: a.f(1)
3242
+ 1
3243
+ sage: len(a.f.cache)
3244
+ 1
3245
+ sage: b = loads(dumps(a))
3246
+ sage: len(b.f.cache)
3247
+ 0
3248
+
3249
+ Pickling can be enabled with ``do_pickle``::
3250
+
3251
+ sage: class A():
3252
+ ....: def __init__(self):
3253
+ ....: self._parent = MyParent()
3254
+ ....: def parent(self): return self._parent
3255
+ ....: @cached_in_parent_method(do_pickle=True)
3256
+ ....: def f(self, x): return x
3257
+ sage: __main__.A = A
3258
+ sage: a = A()
3259
+ sage: a.f(1)
3260
+ 1
3261
+ sage: len(a.f.cache)
3262
+ 1
3263
+ sage: b = loads(dumps(a))
3264
+ sage: len(b.f.cache)
3265
+ 1
3266
+ """
3267
+ self._cache_name = '_cache__' + 'element_' + (name or f.__name__)
3268
+ self._cachedfunc = CachedFunction(f, classmethod=True, name=name, key=key, do_pickle=do_pickle)
3269
+
3270
+ cpdef _get_instance_cache(self, inst):
3271
+ """
3272
+ Return the cache dictionary, which is stored in the parent.
3273
+
3274
+ EXAMPLES::
3275
+
3276
+ sage: class MyParent(Parent):
3277
+ ....: pass
3278
+ sage: class Foo:
3279
+ ....: def __init__(self, x):
3280
+ ....: self._x = x
3281
+ ....: _parent = MyParent()
3282
+ ....: def parent(self):
3283
+ ....: return self._parent
3284
+ ....: def __eq__(self, other):
3285
+ ....: return self._x^2 == other._x^2
3286
+ ....: def __hash__(self):
3287
+ ....: return hash(self._x^2)
3288
+ ....: def __repr__(self):
3289
+ ....: return 'My %s'%self._x
3290
+ ....: @cached_in_parent_method
3291
+ ....: def f(self):
3292
+ ....: return self._x^3
3293
+ sage: a = Foo(2)
3294
+ sage: a.f()
3295
+ 8
3296
+ sage: a.f.cache #indirect doctest
3297
+ {(My 2, ((), ())): 8}
3298
+
3299
+ Since the key for the cache depends on equality of
3300
+ the instances, we obtain *identical* result for
3301
+ *equal* instance - even though in this particular
3302
+ example the result is wrong::
3303
+
3304
+ sage: b = Foo(-2)
3305
+ sage: a is not b
3306
+ True
3307
+ sage: a == b
3308
+ True
3309
+ sage: b.f() is a.f()
3310
+ True
3311
+
3312
+ Non-equal instances do not share the result of
3313
+ the cached method, but they do share the cache::
3314
+
3315
+ sage: c = Foo(3)
3316
+ sage: c.f()
3317
+ 27
3318
+ sage: c.f.cache is a.f.cache #indirect doctest
3319
+ True
3320
+
3321
+ Note that the cache is also available as an
3322
+ attribute of the cached method, which speeds
3323
+ up internal computations::
3324
+
3325
+ sage: a.f.cache is b.f.cache is c.f._cachedmethod._get_instance_cache(c)
3326
+ True
3327
+ """
3328
+ default = {} if self._cachedfunc.do_pickle else NonpicklingDict()
3329
+ if inst is None:
3330
+ return default
3331
+ try:
3332
+ P = inst.parent()
3333
+ return P.__dict__.setdefault(self._cache_name, default)
3334
+ except AttributeError:
3335
+ pass
3336
+ if not hasattr(P, '_cached_methods'):
3337
+ raise TypeError("The parent of this element does not allow attribute assignment\n" +
3338
+ " and does not descend from the Parent base class.\n" +
3339
+ " Cannot use CachedInParentMethod.")
3340
+ if P._cached_methods is None:
3341
+ P._cached_methods = {}
3342
+ return (<dict>P._cached_methods).setdefault(self._cache_name, default)
3343
+
3344
+ def __get__(self, inst, cls):
3345
+ """
3346
+ Get a CachedMethodCaller bound to this specific instance of
3347
+ the class of the cached-in-parent method.
3348
+ """
3349
+ Caller = GloballyCachedMethodCaller(self, inst, cache=self._get_instance_cache(inst), key=self._cachedfunc.key, do_pickle=self._cachedfunc.do_pickle)
3350
+ try:
3351
+ setattr(inst, self._cachedfunc.__name__, Caller)
3352
+ except AttributeError:
3353
+ pass
3354
+ return Caller
3355
+
3356
+
3357
+ cached_in_parent_method = decorator_keywords(CachedInParentMethod)
3358
+
3359
+
3360
+ class FileCache():
3361
+ """
3362
+ :class:`FileCache` is a dictionary-like class which stores keys
3363
+ and values on disk. The keys take the form of a tuple ``(A,K)``
3364
+
3365
+ - ``A`` -- tuple of objects ``t`` where each ``t`` is an
3366
+ exact object which is uniquely identified by a short string
3367
+
3368
+ - ``K`` -- tuple of tuples ``(s,v)`` where ``s`` is a valid
3369
+ variable name and ``v`` is an exact object which is uniquely
3370
+ identified by a short string with letters [a-zA-Z0-9-._]
3371
+
3372
+ The primary use case is the :class:`DiskCachedFunction`. If
3373
+ ``memory_cache == True``, we maintain a cache of objects seen
3374
+ during this session in memory -- but we don't load them from
3375
+ disk until necessary. The keys and values are stored in a
3376
+ pair of files:
3377
+
3378
+ - ``prefix-argstring.key.sobj`` contains the ``key`` only,
3379
+ - ``prefix-argstring.sobj`` contains the tuple ``(key,val)``
3380
+
3381
+ where ``self[key] == val``.
3382
+
3383
+ .. NOTE::
3384
+
3385
+ We assume that each :class:`FileCache` lives in its own directory.
3386
+ Use **extreme** caution if you wish to break that assumption.
3387
+ """
3388
+ def __init__(self, dir, prefix='', memory_cache=False):
3389
+ """
3390
+ EXAMPLES::
3391
+
3392
+ sage: from sage.misc.cachefunc import FileCache
3393
+ sage: dir = tmp_dir()
3394
+ sage: FC = FileCache(dir, memory_cache = True)
3395
+ sage: FC[((),())] = 1
3396
+ sage: FC[((1,2),())] = 2
3397
+ sage: FC[((),())]
3398
+ 1
3399
+ """
3400
+ if not dir or dir[-1] != '/':
3401
+ dir += '/'
3402
+ self._dir = dir
3403
+ os.makedirs(dir, exist_ok=True)
3404
+
3405
+ self._prefix = prefix + '-'
3406
+
3407
+ if memory_cache:
3408
+ self._cache = {}
3409
+ else:
3410
+ self._cache = None
3411
+
3412
+ def file_list(self):
3413
+ """
3414
+ Return the list of files corresponding to ``self``.
3415
+
3416
+ EXAMPLES::
3417
+
3418
+ sage: from sage.misc.cachefunc import FileCache
3419
+ sage: dir = tmp_dir()
3420
+ sage: FC = FileCache(dir, memory_cache = True, prefix='t')
3421
+ sage: FC[((),())] = 1
3422
+ sage: FC[((1,2),())] = 2
3423
+ sage: FC[((1,),(('a',1),))] = 3
3424
+ sage: for f in sorted(FC.file_list()): print(f[len(dir):])
3425
+ t-.key.sobj
3426
+ t-.sobj
3427
+ t-1_2.key.sobj
3428
+ t-1_2.sobj
3429
+ t-a-1.1.key.sobj
3430
+ t-a-1.1.sobj
3431
+ """
3432
+ files = []
3433
+ prefix = self._prefix
3434
+ dir = self._dir
3435
+ l = len(prefix)
3436
+ for f in os.listdir(dir):
3437
+ if f[:l] == prefix:
3438
+ files.append(dir + f)
3439
+ return files
3440
+
3441
+ def items(self):
3442
+ """
3443
+ Return a list of tuples ``(k,v)`` where ``self[k] = v``.
3444
+
3445
+ EXAMPLES::
3446
+
3447
+ sage: from sage.misc.cachefunc import FileCache
3448
+ sage: dir = tmp_dir()
3449
+ sage: FC = FileCache(dir, memory_cache = False)
3450
+ sage: FC[((),())] = 1
3451
+ sage: FC[((1,2),())] = 2
3452
+ sage: FC[((1,),(('a',1),))] = 3
3453
+ sage: I = FC.items()
3454
+ sage: I.sort(); I
3455
+ [(((), ()), 1), (((1,), (('a', 1),)), 3), (((1, 2), ()), 2)]
3456
+ """
3457
+ return [(k, self[k]) for k in self]
3458
+
3459
+ def values(self):
3460
+ """
3461
+ Return a list of values that are stored in ``self``.
3462
+
3463
+ EXAMPLES::
3464
+
3465
+ sage: from sage.misc.cachefunc import FileCache
3466
+ sage: dir = tmp_dir()
3467
+ sage: FC = FileCache(dir, memory_cache = False)
3468
+ sage: FC[((),())] = 1
3469
+ sage: FC[((1,2),())] = 2
3470
+ sage: FC[((1,),(('a',1),))] = 3
3471
+ sage: FC[((),(('a',1),))] = 4
3472
+ sage: v = FC.values()
3473
+ sage: v.sort(); v
3474
+ [1, 2, 3, 4]
3475
+ """
3476
+ return [self[k] for k in self]
3477
+
3478
+ def __iter__(self):
3479
+ """
3480
+ Return a list of keys of ``self``.
3481
+
3482
+ EXAMPLES::
3483
+
3484
+ sage: from sage.misc.cachefunc import FileCache
3485
+ sage: dir = tmp_dir()
3486
+ sage: FC = FileCache(dir, memory_cache = False)
3487
+ sage: FC[((),())] = 1
3488
+ sage: FC[((1,2),())] = 2
3489
+ sage: FC[((1,),(('a',1),))] = 3
3490
+ sage: for k in sorted(FC): print(k)
3491
+ ((), ())
3492
+ ((1,), (('a', 1),))
3493
+ ((1, 2), ())
3494
+ """
3495
+ return iter(self.keys())
3496
+
3497
+ def keys(self):
3498
+ """
3499
+ Return a list of keys ``k`` where ``self[k]`` is defined.
3500
+
3501
+ EXAMPLES::
3502
+
3503
+ sage: from sage.misc.cachefunc import FileCache
3504
+ sage: dir = tmp_dir()
3505
+ sage: FC = FileCache(dir, memory_cache = False)
3506
+ sage: FC[((),())] = 1
3507
+ sage: FC[((1,2),())] = 2
3508
+ sage: FC[((1,),(('a',1),))] = 3
3509
+ sage: K = FC.keys()
3510
+ sage: K.sort(); K
3511
+ [((), ()), ((1,), (('a', 1),)), ((1, 2), ())]
3512
+ """
3513
+ cdef list K = []
3514
+ from sage.misc.persist import load
3515
+ for f in self.file_list():
3516
+ if f[-9:] == '.key.sobj':
3517
+ K.append(load(f))
3518
+ return K
3519
+
3520
+ def clear(self):
3521
+ """
3522
+ Clear all key, value pairs from ``self`` and unlink the associated files
3523
+ from the file cache.
3524
+
3525
+ EXAMPLES::
3526
+
3527
+ sage: from sage.misc.cachefunc import FileCache
3528
+ sage: dir = tmp_dir()
3529
+ sage: FC1 = FileCache(dir, memory_cache=False, prefix='foo')
3530
+ sage: FC2 = FileCache(dir, memory_cache=False, prefix='foo')
3531
+ sage: k1 = ((), (('a', 1),))
3532
+ sage: t1 = randint(0, 1000)
3533
+ sage: k2 = ((), (('b', 1),))
3534
+ sage: t2 = randint(0, 1000)
3535
+ sage: FC1[k1] = t1
3536
+ sage: FC2[k2] = t2
3537
+ sage: FC2.clear()
3538
+ sage: k1 in FC1
3539
+ False
3540
+ sage: k2 in FC1
3541
+ False
3542
+ """
3543
+ for k in self:
3544
+ del self[k]
3545
+
3546
+ def _filename(self, key):
3547
+ """
3548
+ Compute the filename associated with a certain key.
3549
+
3550
+ EXAMPLES::
3551
+
3552
+ sage: from sage.misc.cachefunc import FileCache
3553
+ sage: dir = tmp_dir()
3554
+ sage: FC = FileCache(dir, memory_cache = False, prefix='foo')
3555
+ sage: N = FC._filename(((1,2), (('a',1),('b',2))))
3556
+ sage: print(N[len(dir):])
3557
+ foo-a-1_b-2.1_2
3558
+ sage: N = FC._filename(((), (('a',1),('b',2))))
3559
+ sage: print(N[len(dir):])
3560
+ foo-a-1_b-2
3561
+ sage: N = FC._filename(((1,2), ()))
3562
+ sage: print(N[len(dir):])
3563
+ foo-1_2
3564
+ """
3565
+ a, k = key
3566
+ kwdstr = '_'.join('%s-%s' % x for x in k)
3567
+ argstr = '_'.join('%s' % x for x in a)
3568
+ if kwdstr and argstr:
3569
+ keystr = kwdstr + '.' + argstr
3570
+ else:
3571
+ keystr = kwdstr + argstr
3572
+ return self._dir + self._prefix + keystr
3573
+
3574
+ def __contains__(self, key):
3575
+ """
3576
+ Return ``True`` if ``self[key]`` is defined and ``False`` otherwise.
3577
+
3578
+ EXAMPLES::
3579
+
3580
+ sage: from sage.misc.cachefunc import FileCache
3581
+ sage: dir = tmp_dir()
3582
+ sage: FC = FileCache(dir, memory_cache = False, prefix='foo')
3583
+ sage: k = ((),(('a',1),))
3584
+ sage: FC[k] = True
3585
+ sage: k in FC
3586
+ True
3587
+ sage: ((),()) in FC
3588
+ False
3589
+ """
3590
+ return os.path.exists(self._filename(key) + '.key.sobj')
3591
+
3592
+ def __getitem__(self, key):
3593
+ """
3594
+ Return the value set by ``self[key] = val``, in this session
3595
+ or a previous one.
3596
+
3597
+ EXAMPLES::
3598
+
3599
+ sage: from sage.misc.cachefunc import FileCache
3600
+ sage: dir = tmp_dir()
3601
+ sage: FC1 = FileCache(dir, memory_cache = False, prefix='foo')
3602
+ sage: FC2 = FileCache(dir, memory_cache = False, prefix='foo')
3603
+ sage: k = ((),(('a',1),))
3604
+ sage: t = randint(0, 1000)
3605
+ sage: FC1[k] = t
3606
+ sage: FC2[k] == FC1[k] == t
3607
+ True
3608
+ sage: FC1[(1,2),(('a',4),('b',2))]
3609
+ Traceback (most recent call last):
3610
+ ...
3611
+ KeyError: ((1, 2), (('a', 4), ('b', 2)))
3612
+ """
3613
+ from sage.misc.persist import load
3614
+
3615
+ cache = self._cache
3616
+ if cache is not None:
3617
+ if key in cache:
3618
+ return cache[key]
3619
+
3620
+ f = self._filename(key) + '.sobj'
3621
+ try:
3622
+ k, v = load(f)
3623
+ except IOError:
3624
+ raise KeyError(key)
3625
+ if k != key:
3626
+ raise RuntimeError("cache corrupted")
3627
+
3628
+ if cache is not None:
3629
+ cache[key] = v
3630
+ return v
3631
+
3632
+ def __setitem__(self, key, value):
3633
+ """
3634
+ Set ``self[key] = value`` and stores both key and value on
3635
+ disk.
3636
+
3637
+ EXAMPLES::
3638
+
3639
+ sage: from sage.misc.cachefunc import FileCache
3640
+ sage: dir = tmp_dir()
3641
+ sage: FC1 = FileCache(dir, memory_cache = False, prefix='foo')
3642
+ sage: FC2 = FileCache(dir, memory_cache = False, prefix='foo')
3643
+ sage: k = ((),(('a',1),))
3644
+ sage: t = randint(0, 1000)
3645
+ sage: FC1[k] = t
3646
+ sage: FC2[k] == t
3647
+ True
3648
+ sage: FC1[k] = 2000
3649
+ sage: FC2[k]!= t
3650
+ True
3651
+ """
3652
+ from sage.misc.persist import save
3653
+
3654
+ f = self._filename(key)
3655
+
3656
+ save(key, f+'.key.sobj')
3657
+ save((key, value), f + '.sobj')
3658
+ if self._cache is not None:
3659
+ self._cache[key] = value
3660
+
3661
+ def __delitem__(self, key):
3662
+ """
3663
+ Delete the ``key, value`` pair from ``self`` and unlink the associated
3664
+ files from the file cache.
3665
+
3666
+ EXAMPLES::
3667
+
3668
+ sage: from sage.misc.cachefunc import FileCache
3669
+ sage: dir = tmp_dir()
3670
+ sage: FC1 = FileCache(dir, memory_cache = False, prefix='foo')
3671
+ sage: FC2 = FileCache(dir, memory_cache = False, prefix='foo')
3672
+ sage: k = ((),(('a',1),))
3673
+ sage: t = randint(0, 1000)
3674
+ sage: FC1[k] = t
3675
+ sage: del FC2[k]
3676
+ sage: k in FC1
3677
+ False
3678
+ """
3679
+ f = self._filename(key)
3680
+ cache = self._cache
3681
+ if cache is not None and key in cache:
3682
+ del self._cache[key]
3683
+ if os.path.exists(f + '.sobj'):
3684
+ os.remove(f + '.sobj')
3685
+ if os.path.exists(f + '.key.sobj'):
3686
+ os.remove(f + '.key.sobj')
3687
+
3688
+
3689
+ class DiskCachedFunction(CachedFunction):
3690
+ """
3691
+ Works similar to CachedFunction, but instead, we keep the
3692
+ cache on disk (optionally, we keep it in memory too).
3693
+
3694
+ EXAMPLES::
3695
+
3696
+ sage: from sage.misc.cachefunc import DiskCachedFunction
3697
+ sage: dir = tmp_dir()
3698
+ sage: factor = DiskCachedFunction(factor, dir, memory_cache=True)
3699
+ sage: f = factor(2775); f
3700
+ 3 * 5^2 * 37
3701
+ sage: f is factor(2775)
3702
+ True
3703
+ """
3704
+ def __init__(self, f, dir, memory_cache=False, key=None):
3705
+ """
3706
+ EXAMPLES::
3707
+
3708
+ sage: from sage.misc.cachefunc import DiskCachedFunction
3709
+ sage: def foo(x): sleep(x)
3710
+ sage: dir = tmp_dir()
3711
+ sage: bar = DiskCachedFunction(foo, dir, memory_cache = False)
3712
+ sage: w = walltime()
3713
+ sage: for i in range(10): bar(1)
3714
+ sage: walltime(w) < 2
3715
+ True
3716
+ """
3717
+ CachedFunction.__init__(self, f, key=key)
3718
+ prefix = f.__name__
3719
+ self.cache = FileCache(dir, prefix=prefix, memory_cache = memory_cache)
3720
+
3721
+
3722
+ class disk_cached_function:
3723
+ """
3724
+ Decorator for :class:`DiskCachedFunction`.
3725
+
3726
+ EXAMPLES::
3727
+
3728
+ sage: dir = tmp_dir()
3729
+ sage: @disk_cached_function(dir)
3730
+ ....: def foo(x): return next_prime(2^x)%x
3731
+ sage: x = foo(200); x # needs sage.libs.pari
3732
+ 11
3733
+ sage: @disk_cached_function(dir)
3734
+ ....: def foo(x): return 1/x
3735
+ sage: foo(200) # needs sage.libs.pari
3736
+ 11
3737
+ sage: foo.clear_cache()
3738
+ sage: foo(200)
3739
+ 1/200
3740
+ """
3741
+ def __init__(self, dir, memory_cache=False, key=None):
3742
+ """
3743
+ EXAMPLES::
3744
+
3745
+ sage: dir = tmp_dir()
3746
+ sage: @disk_cached_function(dir, memory_cache=True)
3747
+ ....: def foo(x): return next_prime(2^x)
3748
+ sage: x = foo(200) # needs sage.libs.pari
3749
+ sage: x is foo(200) # needs sage.libs.pari
3750
+ True
3751
+ sage: @disk_cached_function(dir, memory_cache=False)
3752
+ ....: def foo(x): return next_prime(2^x)
3753
+ sage: x is foo(200) # needs sage.libs.pari
3754
+ False
3755
+ """
3756
+ self._dir = dir
3757
+ self._memory_cache = memory_cache
3758
+ self._key = key
3759
+
3760
+ def __call__(self, f):
3761
+ """
3762
+ EXAMPLES::
3763
+
3764
+ sage: dir = tmp_dir()
3765
+ sage: @disk_cached_function(dir)
3766
+ ....: def foo(x): return ModularSymbols(x)
3767
+ sage: foo(389) # needs sage.modular
3768
+ Modular Symbols space of dimension 65 for Gamma_0(389) of weight 2
3769
+ with sign 0 over Rational Field
3770
+ """
3771
+ return DiskCachedFunction(f, self._dir, memory_cache=self._memory_cache, key=self._key)
3772
+
3773
+
3774
+ # Add support for _instancedoc_
3775
+ from sage.misc.instancedoc import instancedoc
3776
+ instancedoc(CachedFunction)
3777
+ instancedoc(WeakCachedFunction)
3778
+ instancedoc(CachedMethodCaller)
3779
+ instancedoc(CachedMethodCallerNoArgs)
3780
+ instancedoc(GloballyCachedMethodCaller)
3781
+ instancedoc(DiskCachedFunction)