My point is that we always should work with immutable objects and avoid extra
overhead (deep-copy) related to keeping cache safe for mutable things.
This simplifies things a bit for users, so related to #429
Now we've eliminated all usage of cache_it, let's kill to what it pointed --
cache_it_fast.
Plus kill mycopy (deep-coping)
Correctnes of the whole thing was verified with
SYMPY_USE_CACHE=debug py.test sympy/core/
and
SYMPY_USE_CACHE=debug py.test sympy/
but in the second check cache_it_debug was modified to only check that the
result is immutable. Otherwise we run out of 1G of memory.
diff --git a/sympy/core/cache.py b/sympy/core/cache.py
--- a/sympy/core/cache.py
+++ b/sympy/core/cache.py
@@ -1,15 +1,4 @@
""" Caching facility for SymPy """
-
-def mycopy(obj, level=0):
- if isinstance(obj, (list, tuple)):
- return obj.__class__(map(mycopy, obj))
- elif isinstance(obj, dict):
- d = obj.__class__()
- for k,v in obj.items():
- d[mycopy(k)] = mycopy(v)
- return d
- return obj
-
# TODO: refactor CACHE & friends into class?
@@ -58,28 +47,7 @@ def cache_it_nocache(func):
def cache_it_nocache(func):
return func
-def cache_it_fast(func):
- func._cache_it_cache = func_cache_it_cache = {}
- CACHE.append((func, func_cache_it_cache))
- def wrapper(*args, **kw_args):
- if kw_args:
- keys = kw_args.keys()
- keys.sort()
- items = [(k+'=',kw_args[k]) for k in keys]
- k = args + tuple(items)
- else:
- k = args
- cache_flag = False
- try:
- r = func_cache_it_cache[k]
- except KeyError:
- r = func(*args, **kw_args)
- cache_flag = True
- if cache_flag:
- func_cache_it_cache[k] = r
- return mycopy(r)
- return wrapper
def cache_it_immutable(func):
func._cache_it_cache = func_cache_it_cache = {}
@@ -104,14 +72,20 @@ def cache_it_immutable(func):
def cache_it_debug(func):
"""cache_it_fast + code to check cache consitency"""
- cfunc = cache_it_fast(func)
+ cfunc = __cache_it_immutable(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 ummutable
+ # this is not 100% check, but at least we catch lists usage
+ hash(r1), hash(r2)
+
+ # also see if returned values are the same
assert r1 == r2
+
return r1
return wrapper
@@ -310,15 +284,16 @@ usecache = os.getenv('SYMPY_USE_CACHE',
usecache = os.getenv('SYMPY_USE_CACHE', 'yes').lower()
if usecache=='no':
- cache_it_fast = cache_it_nocache
cache_it_immutable = cache_it_nocache
cache_it_debug = cache_it_nocache
cache_it_nondummy = cache_it_nocache
Memoizer = Memoizer_nocache
cache_it = cache_it_nocache
elif usecache=='yes':
- cache_it = cache_it_fast
+ cache_it = cache_it_immutable
elif usecache=='debug':
cache_it = cache_it_debug # twice slower
+ __cache_it_immutable = cache_it_immutable
+ cache_it_immutable = cache_it_debug
else:
raise RuntimeError('unknown argument in SYMPY_USE_CACHE: %s' % usecache)
don't use cache_it, always use cache_it_immutable
cache_it was created to cache mutable results, but there is no reason for it.
first, to ensure cache itself is kept unmodified from outside, each time return
value is deep-copied and this adds overhead.
second, we have no reason to cache mutable instances -- what we almost always
have is immutable, so why have additional unneccesary overhead (deep copy)?
Very seldom cache_it user which were returning mutable things (like
.as_coeff_factors) were rewrittend to return immutable objects. The performance
may be affected only positively from this since we avoid deep-copy, and
previously lists were used only to do l.insert(0, smth), and the same effect
can be achieved with tuple+tuple.
diff --git a/sympy/core/add.py b/sympy/core/add.py
--- a/sympy/core/add.py
+++ b/sympy/core/add.py
@@ -2,7 +2,7 @@ from basic import Basic, S, C
from basic import Basic, S, C
from operations import AssocOp
from methods import RelMeths, ArithMeths
-from cache import cache_it, cache_it_immutable
+from cache import cache_it_immutable
from symbol import Symbol, Wild, Temporary
# from numbers import Number /cyclic/
@@ -111,7 +111,7 @@ class Add(AssocOp, RelMeths, ArithMeths)
return '(%s)' % r
return r
- @cache_it
+ @cache_it_immutable
def as_coeff_factors(self, x=None):
if x is not None:
l1 = []
@@ -121,11 +121,11 @@ class Add(AssocOp, RelMeths, ArithMeths)
l2.append(f)
else:
l1.append(f)
- return Add(*l1), l2
+ return Add(*l1), tuple(l2)
coeff = self.args[0]
if isinstance(coeff, Number):
- return coeff, list(self.args[1:])
- return S.Zero, list(self.args[:])
+ return coeff, self.args[1:]
+ return S.Zero, self.args
def _eval_derivative(self, s):
return Add(*[f.diff(s) for f in self.args])
@@ -143,7 +143,7 @@ class Add(AssocOp, RelMeths, ArithMeths)
def _combine_inverse(lhs, rhs):
return lhs - rhs
- @cache_it
+ @cache_it_immutable
def as_two_terms(self):
if len(self.args) == 1:
return S.Zero, self
@@ -255,8 +255,8 @@ class Add(AssocOp, RelMeths, ArithMeths)
def as_coeff_terms(self, x=None):
# -2 + 2 * a -> -1, 2-2*a
if isinstance(self.args[0], Number) and self.args[0].is_negative:
- return -S.One,[-self]
- return S.One,[self]
+ return -S.One,(-self,)
+ return S.One,(self,)
def _eval_subs(self, old, new):
if self==old: return new
@@ -278,7 +278,7 @@ class Add(AssocOp, RelMeths, ArithMeths)
def _eval_oseries(self, order):
return Add(*[f.oseries(order) for f in self.args])
- @cache_it
+ @cache_it_immutable
def extract_leading_order(self, *symbols):
lst = []
seq = [(f, C.Order(f, *symbols)) for f in self.args]
@@ -295,7 +295,7 @@ class Add(AssocOp, RelMeths, ArithMeths)
continue
new_lst.append((e,o))
lst = new_lst
- return lst
+ return tuple(lst)
def _eval_as_leading_term(self, x):
coeff, factors = self.as_coeff_factors(x)
diff --git a/sympy/core/basic.py b/sympy/core/basic.py
--- a/sympy/core/basic.py
+++ b/sympy/core/basic.py
@@ -5,7 +5,7 @@ import decimal
import decimal
from assumptions import AssumeMeths
from sympify import sympify, SympifyError
-from cache import cache_it, cache_it_immutable, Memoizer, MemoizerArg
+from cache import cache_it_immutable, Memoizer, MemoizerArg
# from numbers import Number, Integer, Real /cyclic/
# from interval import Interval /cyclic/
@@ -1035,8 +1035,8 @@ class Basic(AssumeMeths):
# a -> c * t
if x is not None:
if not self.has(x):
- return self, []
- return S.One, [self]
+ return self, tuple()
+ return S.One, (self,)
def as_indep_terms(self, x):
coeff, terms = self.as_coeff_terms()
@@ -1053,8 +1053,8 @@ class Basic(AssumeMeths):
# a -> c + f
if x is not None:
if not self.has(x):
- return self, []
- return S.Zero, [self]
+ return self, tuple()
+ return S.Zero, (self,)
def as_numer_denom(self):
# a/b -> a,b
diff --git a/sympy/core/function.py b/sympy/core/function.py
--- a/sympy/core/function.py
+++ b/sympy/core/function.py
@@ -33,7 +33,7 @@ from basic import BasicType, BasicMeta
from basic import BasicType, BasicMeta
from methods import ArithMeths, RelMeths
from operations import AssocOp
-from cache import cache_it
+from cache import cache_it_immutable
from numbers import Rational
from symbol import Symbol
@@ -81,7 +81,7 @@ class Function(Basic, ArithMeths, RelMet
nargs = None
- @cache_it
+ @cache_it_immutable
def __new__(cls, *args, **options):
# NOTE: this __new__ is twofold:
#
diff --git a/sympy/core/mul.py b/sympy/core/mul.py
--- a/sympy/core/mul.py
+++ b/sympy/core/mul.py
@@ -2,7 +2,7 @@ from basic import Basic, S, C, sympify
from basic import Basic, S, C, sympify
from operations import AssocOp
from methods import RelMeths, ArithMeths
-from cache import cache_it, cache_it_immutable
+from cache import cache_it_immutable
from symbol import Symbol, Wild
# from function import WildFunction /cyclic/
@@ -189,7 +189,7 @@ class Mul(AssocOp, RelMeths, ArithMeths)
if coeff == -1:
return None
elif coeff < 0:
- return (-coeff)**e * Mul(*([S.NegativeOne] +rest))**e
+ return (-coeff)**e * Mul(*((S.NegativeOne,) +rest))**e
else:
return coeff**e * Mul(*[s**e for s in rest])
@@ -224,7 +224,7 @@ class Mul(AssocOp, RelMeths, ArithMeths)
if coeff.is_negative:
coeff = -coeff
if coeff is not S.One:
- terms.insert(0, coeff)
+ terms = (coeff,) + terms
if isinstance(terms, Basic):
terms = terms.args
r = '-' + '*'.join([t.tostr(precedence) for t in terms])
@@ -257,13 +257,13 @@ class Mul(AssocOp, RelMeths, ArithMeths)
return '(%s)' % r
return r
- @cache_it
+ @cache_it_immutable
def as_two_terms(self):
if len(self.args) == 1:
return S.One, self
return self.args[0], Mul(*self.args[1:])
- @cache_it
+ @cache_it_immutable
def as_coeff_terms(self, x=None):
if x is not None:
l1 = []
@@ -273,11 +273,11 @@ class Mul(AssocOp, RelMeths, ArithMeths)
l2.append(f)
else:
l1.append(f)
- return Mul(*l1), l2
+ return Mul(*l1), tuple(l2)
coeff = self.args[0]
if isinstance(coeff, Number):
- return coeff, list(self.args[1:])
- return S.One, list(self.args[:])
+ return coeff, self.args[1:]
+ return S.One, self.args
@staticmethod
def _expandsums(sums):
diff --git a/sympy/core/numbers.py b/sympy/core/numbers.py
--- a/sympy/core/numbers.py
+++ b/sympy/core/numbers.py
@@ -154,7 +154,7 @@ class Number(Atom, RelMeths, ArithMeths)
def as_coeff_terms(self, x=None):
# a -> c * t
- return self, []
+ return self, tuple()
decimal_to_Number_cls = {
decimal.Decimal('0').as_tuple():'Zero',
@@ -454,7 +454,7 @@ class Rational(Number):
obj = Basic.__new__(cls)
obj.p = p
obj.q = q
- #obj._args = [p, q]
+ #obj._args = (p, q)
return obj
def _hashable_content(self):
diff --git a/sympy/core/operations.py b/sympy/core/operations.py
--- a/sympy/core/operations.py
+++ b/sympy/core/operations.py
@@ -1,7 +1,7 @@
from basic import Basic, S, C
from sympify import _sympify
-from cache import cache_it, cache_it_immutable
+from cache import cache_it_immutable
# from add import Add /cyclic/
# from mul import Mul /cyclic/
diff --git a/sympy/core/power.py b/sympy/core/power.py
--- a/sympy/core/power.py
+++ b/sympy/core/power.py
@@ -2,7 +2,7 @@ from basic import Basic, S, C
from basic import Basic, S, C
from sympify import _sympify
from methods import ArithMeths, RelMeths
-from cache import cache_it, cache_it_immutable
+from cache import cache_it_immutable
from symbol import Symbol, Wild, Temporary
# from numbers import Number, Rational, Integer /cyclic/
diff --git a/sympy/core/symbol.py b/sympy/core/symbol.py
--- a/sympy/core/symbol.py
+++ b/sympy/core/symbol.py
@@ -1,7 +1,7 @@
from basic import Basic, Atom, S, C, sympify
from methods import RelMeths, ArithMeths
-from cache import cache_it, cache_it_nondummy
+from cache import cache_it_nondummy
# from function import Function, WildFunction /cyclic/
diff --git a/sympy/functions/elementary/exponential.py b/sympy/functions/elementary/exponential.py
--- a/sympy/functions/elementary/exponential.py
+++ b/sympy/functions/elementary/exponential.py
@@ -1,7 +1,7 @@
from sympy.core.basic import Basic, S, C, sympify
from sympy.core.function import Lambda, Function, Function
-from sympy.core.cache import cache_it, cache_it_immutable
+from sympy.core.cache import cache_it_immutable
class exp(Function):
@@ -115,10 +115,10 @@ class exp(Function):
arg = self.args[0]
if x is not None:
c,f = arg.as_coeff_factors(x)
- return self.func(c), [self.func(a) for a in f.args]
+ return self.func(c), tuple( self.func(a) for a in f.args )
if isinstance(arg, C.Add):
- return S.One, [self.func(a) for a in arg.args]
- return S.One,[self]
+ return S.One, tuple( self.func(a) for a in arg.args )
+ return S.One,(self,)
def _eval_subs(self, old, new):
if self==old: return new
diff --git a/sympy/functions/elementary/hyperbolic.py b/sympy/functions/elementary/hyperbolic.py
--- a/sympy/functions/elementary/hyperbolic.py
+++ b/sympy/functions/elementary/hyperbolic.py
@@ -1,7 +1,7 @@
from sympy.core.basic import Basic, S, C, sympify
from sympy.core.function import Function, Lambda
-from sympy.core.cache import cache_it, cache_it_immutable
+from sympy.core.cache import cache_it_immutable
###############################################################################
########################### HYPERBOLIC FUNCTIONS ##############################
diff --git a/sympy/functions/elementary/integers.py b/sympy/functions/elementary/integers.py
--- a/sympy/functions/elementary/integers.py
+++ b/sympy/functions/elementary/integers.py
@@ -84,7 +84,7 @@ class floor(Function):
return -ceiling(-arg)
else:
return cls(arg.evalf())
- elif terms == [ S.ImaginaryUnit ] and coeff.is_real:
+ elif terms == ( S.ImaginaryUnit, ) and coeff.is_real:
return cls(coeff)*S.ImaginaryUnit
def _eval_is_bounded(self):
@@ -174,7 +174,7 @@ class ceiling(Function):
return -floor(-arg)
else:
return cls(arg.evalf())
- elif terms == [ S.ImaginaryUnit ] and coeff.is_real:
+ elif terms == ( S.ImaginaryUnit, ) and coeff.is_real:
return cls(coeff)*S.ImaginaryUnit
def _eval_is_bounded(self):
diff --git a/sympy/functions/elementary/trigonometric.py b/sympy/functions/elementary/trigonometric.py
--- a/sympy/functions/elementary/trigonometric.py
+++ b/sympy/functions/elementary/trigonometric.py
@@ -2,7 +2,7 @@ from sympy.core.basic import Basic, S, C
from sympy.core.basic import Basic, S, C, sympify
from sympy.core.function import Lambda, Function
from miscellaneous import sqrt
-from sympy.core.cache import cache_it, cache_it_immutable
+from sympy.core.cache import cache_it_immutable
###############################################################################
########################## TRIGONOMETRIC FUNCTIONS ############################
diff --git a/sympy/functions/special/error_functions.py b/sympy/functions/special/error_functions.py
--- a/sympy/functions/special/error_functions.py
+++ b/sympy/functions/special/error_functions.py
@@ -2,7 +2,7 @@ from sympy.core.basic import Basic, S, C
from sympy.core.basic import Basic, S, C, sympify
from sympy.core.function import Function
from sympy.functions.elementary.miscellaneous import sqrt
-from sympy.core.cache import cache_it, cache_it_immutable
+from sympy.core.cache import cache_it_immutable
###############################################################################
################################ ERROR FUNCTION ###############################
diff --git a/sympy/series/limits_series.py b/sympy/series/limits_series.py
--- a/sympy/series/limits_series.py
+++ b/sympy/series/limits_series.py
@@ -10,7 +10,7 @@
from sympy.core.basic import Basic, S, C, sympify
from sympy.core.methods import RelMeths, ArithMeths
-from sympy.core.cache import cache_it, cache_it_immutable
+from sympy.core.cache import cache_it_immutable
class Limit(Basic, RelMeths, ArithMeths):
""" Find the limit of the expression under process x->xlim.
@@ -76,10 +76,11 @@ class Limit(Basic, RelMeths, ArithMeths)
return r
class InfLimit(Basic):
+ _xoo = C.Symbol('xoo', dummy=True, unbounded=True, positive=True)
+
@staticmethod
- @cache_it_immutable
def limit_process_symbol():
- return C.Symbol('xoo', dummy=True, unbounded=True, positive=True)
+ return InfLimit._xoo
@cache_it_immutable
def __new__(cls, expr, x):
diff --git a/sympy/series/order.py b/sympy/series/order.py
--- a/sympy/series/order.py
+++ b/sympy/series/order.py
@@ -1,7 +1,7 @@ from sympy.core.basic import Basic, S, C
from sympy.core.basic import Basic, S, C, sympify
from sympy.core import oo, Rational, Pow
from sympy.core.methods import ArithMeths, RelMeths
-from sympy.core.cache import cache_it, cache_it_immutable
+from sympy.core.cache import cache_it_immutable
class Order(Basic, ArithMeths, RelMeths):
"""
Now we've killed cacheit_fast, we have the only one usable cache_it_immutable,
and let's rename it to just `cacheit`
This patch touches only cache.py internals. I'll convert the rest of sympy in
the following commit.
diff --git a/sympy/core/cache.py b/sympy/core/cache.py
--- a/sympy/core/cache.py
+++ b/sympy/core/cache.py
@@ -44,12 +44,34 @@ def clear_cache():
########################################
-def cache_it_nocache(func):
+def __cacheit_nocache(func):
return func
-def cache_it_immutable(func):
+def __cacheit(func):
+ """caching decorator.
+
+ important: the result of cached function must be *immutable*
+
+
+ Example
+ -------
+
+ @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'
+ """
+
func._cache_it_cache = func_cache_it_cache = {}
CACHE.append((func, func_cache_it_cache))
@@ -70,9 +92,9 @@ def cache_it_immutable(func):
return wrapper
-def cache_it_debug(func):
- """cache_it_fast + code to check cache consitency"""
- cfunc = __cache_it_immutable(func)
+def __cacheit_debug(func):
+ """cacheit + code to check cache consitency"""
+ cfunc = __cacheit(func)
def wrapper(*args, **kw_args):
# always call function itself and compare it with cached version
@@ -91,7 +113,7 @@ def cache_it_debug(func):
return wrapper
-def cache_it_nondummy(func):
+def __cacheit_nondummy(func):
func._cache_it_cache = func_cache_it_cache = {}
CACHE.append((func, func_cache_it_cache))
@@ -284,16 +306,11 @@ usecache = os.getenv('SYMPY_USE_CACHE',
usecache = os.getenv('SYMPY_USE_CACHE', 'yes').lower()
if usecache=='no':
- cache_it_immutable = cache_it_nocache
- cache_it_debug = cache_it_nocache
- cache_it_nondummy = cache_it_nocache
Memoizer = Memoizer_nocache
- cache_it = cache_it_nocache
+ cacheit = __cacheit_nocache
elif usecache=='yes':
- cache_it = cache_it_immutable
+ cacheit = __cacheit
elif usecache=='debug':
- cache_it = cache_it_debug # twice slower
- __cache_it_immutable = cache_it_immutable
- cache_it_immutable = cache_it_debug
+ cacheit = __cacheit_debug # a lot slower
cache_it_immutable -> cacheit all over the place
diff --git a/sympy/core/add.py b/sympy/core/add.py
--- a/sympy/core/add.py
+++ b/sympy/core/add.py
@@ -2,7 +2,7 @@ from basic import Basic, S, C
from basic import Basic, S, C
from operations import AssocOp
from methods import RelMeths, ArithMeths
-from cache import cache_it_immutable
+from cache import cacheit
from symbol import Symbol, Wild, Temporary
# from numbers import Number /cyclic/
@@ -111,7 +111,7 @@ class Add(AssocOp, RelMeths, ArithMeths)
return '(%s)' % r
return r
- @cache_it_immutable
+ @cacheit
def as_coeff_factors(self, x=None):
if x is not None:
l1 = []
@@ -143,7 +143,7 @@ class Add(AssocOp, RelMeths, ArithMeths)
def _combine_inverse(lhs, rhs):
return lhs - rhs
- @cache_it_immutable
+ @cacheit
def as_two_terms(self):
if len(self.args) == 1:
return S.Zero, self
@@ -278,7 +278,7 @@ class Add(AssocOp, RelMeths, ArithMeths)
def _eval_oseries(self, order):
return Add(*[f.oseries(order) for f in self.args])
- @cache_it_immutable
+ @cacheit
def extract_leading_order(self, *symbols):
lst = []
seq = [(f, C.Order(f, *symbols)) for f in self.args]
diff --git a/sympy/core/basic.py b/sympy/core/basic.py
--- a/sympy/core/basic.py
+++ b/sympy/core/basic.py
@@ -5,7 +5,7 @@ import decimal
import decimal
from assumptions import AssumeMeths
from sympify import sympify, SympifyError
-from cache import cache_it_immutable, Memoizer, MemoizerArg
+from cache import cacheit, Memoizer, MemoizerArg
# from numbers import Number, Integer, Real /cyclic/
# from interval import Interval /cyclic/
@@ -450,7 +450,7 @@ class Basic(AssumeMeths):
return new
return self
- @cache_it_immutable
+ @cacheit
def subs(self, old, new):
"""Substitutes an expression old -> new."""
old = sympify(old)
@@ -743,7 +743,7 @@ class Basic(AssumeMeths):
l.sort()
return [(s,e) for i,s,e in l]
- @cache_it_immutable
+ @cacheit
def count_ops(self, symbolic=True):
""" Return the number of operations in expressions.
@@ -1200,7 +1200,7 @@ class Basic(AssumeMeths):
return self
return r + o
- @cache_it_immutable
+ @cacheit
def oseries(self, order):
"""
Return the series of an expression upto given Order symbol (without the
@@ -1282,7 +1282,7 @@ class Basic(AssumeMeths):
from sympy.series.limits_series import InfLimit
return InfLimit(self, x)
- @cache_it_immutable
+ @cacheit
def as_leading_term(self, *symbols):
if len(symbols)>1:
c = self
diff --git a/sympy/core/function.py b/sympy/core/function.py
--- a/sympy/core/function.py
+++ b/sympy/core/function.py
@@ -33,7 +33,7 @@ from basic import BasicType, BasicMeta
from basic import BasicType, BasicMeta
from methods import ArithMeths, RelMeths
from operations import AssocOp
-from cache import cache_it_immutable
+from cache import cacheit
from numbers import Rational
from symbol import Symbol
@@ -81,7 +81,7 @@ class Function(Basic, ArithMeths, RelMet
nargs = None
- @cache_it_immutable
+ @cacheit
def __new__(cls, *args, **options):
# NOTE: this __new__ is twofold:
#
diff --git a/sympy/core/mul.py b/sympy/core/mul.py
--- a/sympy/core/mul.py
+++ b/sympy/core/mul.py
@@ -2,7 +2,7 @@ from basic import Basic, S, C, sympify
from basic import Basic, S, C, sympify
from operations import AssocOp
from methods import RelMeths, ArithMeths
-from cache import cache_it_immutable
+from cache import cacheit
from symbol import Symbol, Wild
# from function import WildFunction /cyclic/
@@ -257,13 +257,13 @@ class Mul(AssocOp, RelMeths, ArithMeths)
return '(%s)' % r
return r
- @cache_it_immutable
+ @cacheit
def as_two_terms(self):
if len(self.args) == 1:
return S.One, self
return self.args[0], Mul(*self.args[1:])
- @cache_it_immutable
+ @cacheit
def as_coeff_terms(self, x=None):
if x is not None:
l1 = []
@@ -367,7 +367,7 @@ class Mul(AssocOp, RelMeths, ArithMeths)
denoms.append(d)
return Mul(*numers), Mul(*denoms)
- @cache_it_immutable
+ @cacheit
def count_ops(self, symbolic=True):
if symbolic:
return Add(*[t.count_ops(symbolic) for t in self[:]]) + Symbol('MUL') * (len(self[:])-1)
diff --git a/sympy/core/operations.py b/sympy/core/operations.py
--- a/sympy/core/operations.py
+++ b/sympy/core/operations.py
@@ -1,7 +1,7 @@
from basic import Basic, S, C
from sympify import _sympify
-from cache import cache_it_immutable
+from cache import cacheit
# from add import Add /cyclic/
# from mul import Mul /cyclic/
@@ -17,7 +17,7 @@ class AssocOp(Basic):
Base class for Add and Mul.
"""
- @cache_it_immutable
+ @cacheit
def __new__(cls, *args, **assumptions):
if len(args)==0:
return cls.identity()
diff --git a/sympy/core/power.py b/sympy/core/power.py
--- a/sympy/core/power.py
+++ b/sympy/core/power.py
@@ -2,7 +2,7 @@ from basic import Basic, S, C
from basic import Basic, S, C
from sympify import _sympify
from methods import ArithMeths, RelMeths
-from cache import cache_it_immutable
+from cache import cacheit
from symbol import Symbol, Wild, Temporary
# from numbers import Number, Rational, Integer /cyclic/
@@ -54,7 +54,7 @@ class Pow(Basic, ArithMeths, RelMeths):
precedence = Basic.Pow_precedence
- @cache_it_immutable
+ @cacheit
def __new__(cls, a, b, **assumptions):
a = _sympify(a)
b = _sympify(b)
@@ -391,7 +391,7 @@ class Pow(Basic, ArithMeths, RelMeths):
s = d[r] = Temporary()
return s
- @cache_it_immutable
+ @cacheit
def count_ops(self, symbolic=True):
if symbolic:
return Add(*[t.count_ops(symbolic) for t in self[:]]) + Symbol('POW')
@@ -504,7 +504,7 @@ class Pow(Basic, ArithMeths, RelMeths):
return self.base.as_leading_term(x) ** self.exp
return C.exp(self.exp * C.log(self.base)).as_leading_term(x)
- @cache_it_immutable
+ @cacheit
def taylor_term(self, n, x, *previous_terms): # of (1+x)**e
if n<0: return S.Zero
x = _sympify(x)
diff --git a/sympy/functions/elementary/exponential.py b/sympy/functions/elementary/exponential.py
--- a/sympy/functions/elementary/exponential.py
+++ b/sympy/functions/elementary/exponential.py
@@ -1,7 +1,7 @@
from sympy.core.basic import Basic, S, C, sympify
from sympy.core.function import Lambda, Function, Function
-from sympy.core.cache import cache_it_immutable
+from sympy.core.cache import cacheit
class exp(Function):
@@ -88,7 +88,7 @@ class exp(Function):
return C.Mul(*(excluded+[cls(C.Add(*included))]))
@staticmethod
- @cache_it_immutable
+ @cacheit
def taylor_term(n, x, *previous_terms):
if n<0: return S.Zero
if n==0: return S.One
@@ -318,7 +318,7 @@ class log(Function):
return x.is_unbounded
@staticmethod
- @cache_it_immutable
+ @cacheit
def taylor_term(n, x, *previous_terms): # of log(1+x)
if n<0: return S.Zero
x = sympify(x)
diff --git a/sympy/functions/elementary/hyperbolic.py b/sympy/functions/elementary/hyperbolic.py
--- a/sympy/functions/elementary/hyperbolic.py
+++ b/sympy/functions/elementary/hyperbolic.py
@@ -1,7 +1,7 @@
from sympy.core.basic import Basic, S, C, sympify
from sympy.core.function import Function, Lambda
-from sympy.core.cache import cache_it_immutable
+from sympy.core.cache import cacheit
###############################################################################
########################### HYPERBOLIC FUNCTIONS ##############################
@@ -51,7 +51,7 @@ class sinh(Function):
return -cls(-arg)
@staticmethod
- @cache_it_immutable
+ @cacheit
def taylor_term(n, x, *previous_terms):
if n < 0 or n % 2 == 0:
return S.Zero
@@ -147,7 +147,7 @@ class cosh(Function):
return cls(-arg)
@staticmethod
- @cache_it_immutable
+ @cacheit
def taylor_term(n, x, *previous_terms):
if n < 0 or n % 2 == 1:
return S.Zero
@@ -243,7 +243,7 @@ class tanh(Function):
return -cls(-arg)
@staticmethod
- @cache_it_immutable
+ @cacheit
def taylor_term(n, x, *previous_terms):
if n < 0 or n % 2 == 0:
return S.Zero
@@ -341,7 +341,7 @@ class coth(Function):
return -cls(-arg)
@staticmethod
- @cache_it_immutable
+ @cacheit
def taylor_term(n, x, *previous_terms):
if n == 0:
return 1 / sympify(x)
@@ -437,7 +437,7 @@ class asinh(Function):
return -cls(-arg)
@staticmethod
- @cache_it_immutable
+ @cacheit
def taylor_term(n, x, *previous_terms):
if n < 0 or n % 2 == 0:
return S.Zero
@@ -510,7 +510,7 @@ class acosh(Function):
return cst_table[arg]*S.ImaginaryUnit
@staticmethod
- @cache_it_immutable
+ @cacheit
def taylor_term(n, x, *previous_terms):
if n == 0:
return S.Pi*S.ImaginaryUnit / 2
@@ -579,7 +579,7 @@ class atanh(Function):
return -cls(-arg)
@staticmethod
- @cache_it_immutable
+ @cacheit
def taylor_term(n, x, *previous_terms):
if n < 0 or n % 2 == 0:
return S.Zero
@@ -640,7 +640,7 @@ class acoth(Function):
return -cls(-arg)
@staticmethod
- @cache_it_immutable
+ @cacheit
def taylor_term(n, x, *previous_terms):
if n == 0:
return S.Pi*S.ImaginaryUnit / 2
diff --git a/sympy/functions/elementary/trigonometric.py b/sympy/functions/elementary/trigonometric.py
--- a/sympy/functions/elementary/trigonometric.py
+++ b/sympy/functions/elementary/trigonometric.py
@@ -2,7 +2,7 @@ from sympy.core.basic import Basic, S, C
from sympy.core.basic import Basic, S, C, sympify
from sympy.core.function import Lambda, Function
from miscellaneous import sqrt
-from sympy.core.cache import cache_it_immutable
+from sympy.core.cache import cacheit
###############################################################################
########################## TRIGONOMETRIC FUNCTIONS ############################
@@ -72,7 +72,7 @@ class sin(Function):
@staticmethod
- @cache_it_immutable
+ @cacheit
def taylor_term(n, x, *previous_terms):
if n < 0 or n % 2 == 0:
return S.Zero
@@ -207,7 +207,7 @@ class cos(Function):
@staticmethod
- @cache_it_immutable
+ @cacheit
def taylor_term(n, x, *previous_terms):
if n < 0 or n % 2 == 1:
return S.Zero
@@ -344,7 +344,7 @@ class tan(Function):
@staticmethod
- @cache_it_immutable
+ @cacheit
def taylor_term(n, x, *previous_terms):
if n < 0 or n % 2 == 0:
return S.Zero
@@ -467,7 +467,7 @@ class cot(Function):
@staticmethod
- @cache_it_immutable
+ @cacheit
def taylor_term(n, x, *previous_terms):
if n == 0:
return 1 / sympify(x)
@@ -582,7 +582,7 @@ class asin(Function):
@staticmethod
- @cache_it_immutable
+ @cacheit
def taylor_term(n, x, *previous_terms):
if n < 0 or n % 2 == 0:
return S.Zero
@@ -659,7 +659,7 @@ class acos(Function):
@staticmethod
- @cache_it_immutable
+ @cacheit
def taylor_term(n, x, *previous_terms):
if n == 0:
return S.Pi / 2
@@ -748,7 +748,7 @@ class atan(Function):
@staticmethod
- @cache_it_immutable
+ @cacheit
def taylor_term(n, x, *previous_terms):
if n < 0 or n % 2 == 0:
return S.Zero
@@ -821,7 +821,7 @@ class acot(Function):
@staticmethod
- @cache_it_immutable
+ @cacheit
def taylor_term(n, x, *previous_terms):
if n == 0:
return S.Pi / 2 # FIX THIS
diff --git a/sympy/functions/special/error_functions.py b/sympy/functions/special/error_functions.py
--- a/sympy/functions/special/error_functions.py
+++ b/sympy/functions/special/error_functions.py
@@ -2,7 +2,7 @@ from sympy.core.basic import Basic, S, C
from sympy.core.basic import Basic, S, C, sympify
from sympy.core.function import Function
from sympy.functions.elementary.miscellaneous import sqrt
-from sympy.core.cache import cache_it_immutable
+from sympy.core.cache import cacheit
###############################################################################
################################ ERROR FUNCTION ###############################
@@ -44,7 +44,7 @@ class erf(Function):
return -cls(-arg)
@staticmethod
- @cache_it_immutable
+ @cacheit
def taylor_term(n, x, *previous_terms):
if n < 0 or n % 2 == 0:
return S.Zero
diff --git a/sympy/series/limits_series.py b/sympy/series/limits_series.py
--- a/sympy/series/limits_series.py
+++ b/sympy/series/limits_series.py
@@ -10,14 +10,14 @@
from sympy.core.basic import Basic, S, C, sympify
from sympy.core.methods import RelMeths, ArithMeths
-from sympy.core.cache import cache_it_immutable
+from sympy.core.cache import cacheit
class Limit(Basic, RelMeths, ArithMeths):
""" Find the limit of the expression under process x->xlim.
Limit(expr, x, xlim)
"""
- @cache_it_immutable
+ @cacheit
def __new__(cls, expr, x, xlim, direction='<', **assumptions):
expr = sympify(expr)
x = sympify(x)
@@ -82,7 +82,7 @@ class InfLimit(Basic):
def limit_process_symbol():
return InfLimit._xoo
- @cache_it_immutable
+ @cacheit
def __new__(cls, expr, x):
expr = orig_expr = sympify(expr)
orig_x = sympify(x)
@@ -139,7 +139,7 @@ class InfLimit(Basic):
return result
-@cache_it_immutable
+@cacheit
def mrv_inflimit(expr, x, _cache = {}):
if _cache.has_key((expr, x)):
raise RuntimeError('Detected recursion while computing mrv_inflimit(%s, %s)' % (expr, x))
@@ -172,11 +172,11 @@ def mrv_inflimit(expr, x, _cache = {}):
return C.sign(c) * S.Infinity
raise RuntimeError('Failed to compute mrv_inflimit(%s, %s), got lt=%s' % (self, x, lt))
-@cache_it_immutable
+@cacheit
def cmp_ops_count(e1,e2):
return cmp(e1.count_ops(symbolic=False), e2.count_ops(symbolic=False))
-@cache_it_immutable
+@cacheit
def mrv_compare(f, g, x):
log = C.log
if isinstance(f, C.exp): f = f.args[0]
diff --git a/sympy/series/order.py b/sympy/series/order.py
--- a/sympy/series/order.py
+++ b/sympy/series/order.py
@@ -1,7 +1,7 @@ from sympy.core.basic import Basic, S, C
from sympy.core.basic import Basic, S, C, sympify
from sympy.core import oo, Rational, Pow
from sympy.core.methods import ArithMeths, RelMeths
-from sympy.core.cache import cache_it_immutable
+from sympy.core.cache import cacheit
class Order(Basic, ArithMeths, RelMeths):
"""
@@ -81,7 +81,7 @@ class Order(Basic, ArithMeths, RelMeths)
_cache = {}
- @cache_it_immutable
+ @cacheit
def __new__(cls, expr, *symbols, **assumptions):
expr = sympify(expr).expand(trig=True)
if expr is S.NaN:
@@ -268,7 +268,7 @@ class Order(Basic, ArithMeths, RelMeths)
order_symbols = order_symbols + (s,)
return self.expr, order_symbols
- @cache_it_immutable
+ @cacheit
def contains(self, expr):
"""
Return True if expr belongs to Order(self.expr, *self.symbols).
What is the meaning of adding hash(r1), hash(r2) ? This should be
explained in the comments. Also I suggest to reword it:
# this check doesn't work 100%, ...
Is it supposed to trigger r1 and r2 to calculate and store their
caches? If so, it should be written in the comments. Otherwise, this
is an empty statement. :)
>
> cache_it was created to cache mutable results, but there is no reason for it.
>
> first, to ensure cache itself is kept unmodified from outside, each time return
> value is deep-copied and this adds overhead.
>
> second, we have no reason to cache mutable instances -- what we almost always
> have is immutable, so why have additional unneccesary overhead (deep copy)?
>
> Very seldom cache_it user which were returning mutable things (like
> .as_coeff_factors) were rewrittend to return immutable objects. The performance
^^ What is the meaning of this sentence? :) I think there are some
words missing in it?
Otherwise all patches are ok, feel free to commit, after fixing the above.
Ondrej
This is a try to catch mutable objects:
In [1]: l = [1,2,3]
In [2]: hash(l)
---------------------------------------------------------------------------
exceptions.TypeError Traceback (most recent call last)
TypeError: list objects are unhashable
In [3]: d = {'a':1, 'b':2}
In [4]: hash(d)
---------------------------------------------------------------------------
exceptions.TypeError Traceback (most recent call last)
TypeError: dict objects are unhashable
In [8]: t = ([1,2], 3)
In [9]: hash(t)
---------------------------------------------------------------------------
exceptions.TypeError Traceback (most recent call last)
TypeError: list objects are unhashable
In [5]: t = (1,2,3)
In [6]: hash(t)
Out[6]: -378539185
>
> >
> > cache_it was created to cache mutable results, but there is no reason for it.
> >
> > first, to ensure cache itself is kept unmodified from outside, each time return
> > value is deep-copied and this adds overhead.
> >
> > second, we have no reason to cache mutable instances -- what we almost always
> > have is immutable, so why have additional unneccesary overhead (deep copy)?
> >
> > Very seldom cache_it user which were returning mutable things (like
> > .as_coeff_factors) were rewrittend to return immutable objects. The performance
>
> ^^ What is the meaning of this sentence? :) I think there are some
> words missing in it?
It means "I'm too sleepy" :)
Sorry, I'll reword it tomorrow...
> Otherwise all patches are ok, feel free to commit, after fixing the above.
Yep, I'll add comments about hash(r1) and reword tommorrow.
Thanks for review.
--
Всего хорошего, Кирилл.
http://landau.phys.spbu.ru/~kirr/aiv/
All patches are in.
btw:
pushing to ssh://h...@hg.sympy.org/sympy
searching for changes
remote: adding changesets
remote: adding manifests
remote: adding file changes
remote: added 5 changesets with 30 changes to 16 files
remote: error: incoming.notify hook raised an exception:
remote: error: incoming.notify hook raised an exception:
I know, but it doesn't print any useful info and I don't know how to
get to know what is wrong. If you know, let me know.
Ondrej
I've tried to reproduce it locally, but everything run without an
exception. I'm on hg crew (e2cbdd931341) though.