Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

""" Caching facility for SymPy """ 

from __future__ import print_function, division 

 

from distutils.version import LooseVersion as V 

 

class _cache(list): 

""" List of cached functions """ 

 

def print_cache(self): 

"""print cache info""" 

 

for item in self: 

name = item.__name__ 

myfunc = item 

while hasattr(myfunc, '__wrapped__'): 

if hasattr(myfunc, 'cache_info'): 

info = myfunc.cache_info() 

break 

else: 

myfunc = myfunc.__wrapped__ 

else: 

info = None 

 

print(name, info) 

 

def clear_cache(self): 

"""clear cache content""" 

for item in self: 

myfunc = item 

while hasattr(myfunc, '__wrapped__'): 

if hasattr(myfunc, 'cache_clear'): 

myfunc.cache_clear() 

break 

else: 

myfunc = myfunc.__wrapped__ 

 

# global cache registry: 

CACHE = _cache() 

# make clear and print methods available 

print_cache = CACHE.print_cache 

clear_cache = CACHE.clear_cache 

 

from sympy.core.compatibility import lru_cache 

from functools import update_wrapper 

 

try: 

import fastcache 

from warnings import warn 

# the version attribute __version__ is not present for all versions 

if not hasattr(fastcache, '__version__'): 

warn("fastcache version >= 0.4.0 required", UserWarning) 

raise ImportError 

# ensure minimum required version of fastcache is present 

if V(fastcache.__version__) < '0.4.0': 

warn("fastcache version >= 0.4.0 required, detected {}"\ 

.format(fastcache.__version__), UserWarning) 

raise ImportError 

# Do not use fastcache if running under pypy 

import platform 

if platform.python_implementation() == 'PyPy': 

raise ImportError 

 

except ImportError: 

 

def __cacheit(maxsize): 

"""caching decorator. 

 

important: the result of cached function must be *immutable* 

 

 

Examples 

======== 

 

>>> from sympy.core.cache import cacheit 

>>> @cacheit 

... def f(a, b): 

... return a+b 

 

>>> @cacheit 

... def f(a, b): 

... return [a, b] # <-- WRONG, returns mutable object 

 

to force cacheit to check returned results mutability and consistency, 

set environment variable SYMPY_USE_CACHE to 'debug' 

""" 

def func_wrapper(func): 

cfunc = lru_cache(maxsize, typed=True)(func) 

 

# wraps here does not propagate all the necessary info 

# for py2.7, use update_wrapper below 

def wrapper(*args, **kwargs): 

try: 

retval = cfunc(*args, **kwargs) 

except TypeError: 

retval = func(*args, **kwargs) 

return retval 

 

wrapper.cache_info = cfunc.cache_info 

wrapper.cache_clear = cfunc.cache_clear 

 

# Some versions of update_wrapper erroneously assign the final 

# function of the wrapper chain to __wrapped__, see 

# https://bugs.python.org/issue17482 . 

# To work around this, we need to call update_wrapper first, then 

# assign to wrapper.__wrapped__. 

update_wrapper(wrapper, func) 

wrapper.__wrapped__ = cfunc.__wrapped__ 

 

CACHE.append(wrapper) 

return wrapper 

 

return func_wrapper 

else: 

 

def __cacheit(maxsize): 

"""caching decorator. 

 

important: the result of cached function must be *immutable* 

 

 

Examples 

======== 

 

>>> from sympy.core.cache import cacheit 

>>> @cacheit 

... def f(a, b): 

... return a+b 

 

>>> @cacheit 

... def f(a, b): 

... return [a, b] # <-- WRONG, returns mutable object 

 

to force cacheit to check returned results mutability and consistency, 

set environment variable SYMPY_USE_CACHE to 'debug' 

""" 

def func_wrapper(func): 

 

cfunc = fastcache.clru_cache(maxsize, typed=True, unhashable='ignore')(func) 

CACHE.append(cfunc) 

return cfunc 

 

return func_wrapper 

######################################## 

 

 

def __cacheit_nocache(func): 

return func 

 

 

def __cacheit_debug(maxsize): 

"""cacheit + code to check cache consistency""" 

def func_wrapper(func): 

from .decorators import wraps 

 

cfunc = __cacheit(maxsize)(func) 

 

@wraps(func) 

def wrapper(*args, **kw_args): 

# always call function itself and compare it with cached version 

r1 = func(*args, **kw_args) 

r2 = cfunc(*args, **kw_args) 

 

# try to see if the result is immutable 

# 

# this works because: 

# 

# hash([1,2,3]) -> raise TypeError 

# hash({'a':1, 'b':2}) -> raise TypeError 

# hash((1,[2,3])) -> raise TypeError 

# 

# hash((1,2,3)) -> just computes the hash 

hash(r1), hash(r2) 

 

# also see if returned values are the same 

if r1 != r2: 

raise RuntimeError("Returned values are not the same") 

return r1 

return wrapper 

return func_wrapper 

 

 

def _getenv(key, default=None): 

from os import getenv 

return getenv(key, default) 

 

# SYMPY_USE_CACHE=yes/no/debug 

USE_CACHE = _getenv('SYMPY_USE_CACHE', 'yes').lower() 

# SYMPY_CACHE_SIZE=some_integer/None 

# special cases : 

# SYMPY_CACHE_SIZE=0 -> No caching 

# SYMPY_CACHE_SIZE=None -> Unbounded caching 

scs = _getenv('SYMPY_CACHE_SIZE', '1000') 

if scs.lower() == 'none': 

SYMPY_CACHE_SIZE = None 

else: 

try: 

SYMPY_CACHE_SIZE = int(scs) 

except ValueError: 

raise RuntimeError( 

'SYMPY_CACHE_SIZE must be a valid integer or None. ' + \ 

'Got: %s' % SYMPY_CACHE_SIZE) 

 

if USE_CACHE == 'no': 

cacheit = __cacheit_nocache 

elif USE_CACHE == 'yes': 

cacheit = __cacheit(SYMPY_CACHE_SIZE) 

elif USE_CACHE == 'debug': 

cacheit = __cacheit_debug(SYMPY_CACHE_SIZE) # a lot slower 

else: 

raise RuntimeError( 

'unrecognized value for SYMPY_USE_CACHE: %s' % USE_CACHE)