from core import C
from basic import Basic
from singleton import S
from operations import AssocOp
from cache import cacheit
from expr import Expr
[docs]class Add(AssocOp):
__slots__ = []
is_Add = True
#identity = S.Zero
# cyclic import, so defined in numbers.py
@classmethod
[docs] def flatten(cls, seq):
"""
Takes the sequence "seq" of nested Adds and returns a flatten list.
Returns: (commutative_part, noncommutative_part, order_symbols)
Applies associativity, all terms are commutable with respect to
addition.
"""
terms = {} # term -> coeff
# e.g. x**2 -> 5 for ... + 5*x**2 + ...
coeff = S.Zero # standalone term
# e.g. 3 + ...
order_factors = []
for o in seq:
# O(x)
if o.is_Order:
for o1 in order_factors:
if o1.contains(o):
o = None
break
if o is None:
continue
order_factors = [o]+[o1 for o1 in order_factors if not o.contains(o1)]
continue
# 3 or NaN
elif o.is_Number:
if o is S.NaN or coeff is S.ComplexInfinity and o.is_bounded is False:
# we know for sure the result will be nan
return [S.NaN], [], None
if coeff.is_Number:
coeff += o
if coeff is S.NaN:
# we know for sure the result will be nan
return [S.NaN], [], None
continue
elif o is S.ComplexInfinity:
if coeff.is_bounded is False:
# we know for sure the result will be nan
return [S.NaN], [], None
coeff = S.ComplexInfinity
continue
# Add([...])
elif o.is_Add:
# NB: here we assume Add is always commutative
seq.extend(o.args) # TODO zerocopy?
continue
# Mul([...])
elif o.is_Mul:
c, s = o.as_coeff_Mul()
# 3*...
if c.is_Number:
# unevaluated 2-arg Mul
if s.is_Add and s.is_commutative:
seq.extend([c*a for a in s.args])
continue
# everything else
else:
c = S.One
s = o
# now we have:
# o = c*s, where
#
# c is a Number
# s is an expression with number factor extracted
# let's collect terms with the same s, so e.g.
# 2*x**2 + 3*x**2 -> 5*x**2
if s in terms:
terms[s] += c
else:
terms[s] = c
# now let's construct new args:
# [2*x**2, x**3, 7*x**4, pi, ...]
newseq = []
noncommutative = False
for s,c in terms.items():
# 0*s
if c is S.Zero:
continue
# 1*s
elif c is S.One:
newseq.append(s)
# c*s
else:
if s.is_Mul:
# Mul, already keeps its arguments in perfect order.
# so we can simply put c in slot0 and go the fast way.
cs = s._new_rawargs(*((c,) + s.args))
newseq.append(cs)
else:
# alternatively we have to call all Mul's machinery (slow)
newseq.append(Mul(c,s))
noncommutative = noncommutative or not s.is_commutative
# oo, -oo
if coeff is S.Infinity:
newseq = [f for f in newseq if not (f.is_nonnegative or f.is_real and
(f.is_bounded or
f.is_finite or
f.is_infinitesimal))]
elif coeff is S.NegativeInfinity:
newseq = [f for f in newseq if not (f.is_nonpositive or f.is_real and
(f.is_bounded or
f.is_finite or
f.is_infinitesimal))]
if coeff is S.ComplexInfinity:
# zoo might be
# unbounded_real + bounded_im
# bounded_real + unbounded_im
# unbounded_real + unbounded_im
# addition of a bounded real or imaginary number won't be able to
# change the zoo nature; if unbounded a NaN condition could result if
# the unbounded symbol had sign opposite of the unbounded portion of zoo,
# e.g. unbounded_real - unbounded_real
newseq = [c for c in newseq if not (c.is_bounded and
c.is_real is not None)]
# process O(x)
if order_factors:
newseq2 = []
for t in newseq:
for o in order_factors:
# x + O(x) -> O(x)
if o.contains(t):
t = None
break
# x + O(x**2) -> x + O(x**2)
if t is not None:
newseq2.append(t)
newseq = newseq2 + order_factors
# 1 + O(1) -> O(1)
for o in order_factors:
if o.contains(coeff):
coeff = S.Zero
break
# order args canonically
# Currently we sort things using hashes, as it is quite fast. A better
# solution is not to sort things at all - but this needs some more
# fixing.
newseq.sort(key=hash)
# current code expects coeff to be always in slot-0
if coeff is not S.Zero:
newseq.insert(0, coeff)
# we are done
if noncommutative:
return [], newseq, None
else:
return newseq, [], None
@classmethod
def class_key(cls):
return 3, 1, cls.__name__
@cacheit
def as_coeff_add(self, *deps):
if deps:
l1 = []
l2 = []
for f in self.args:
if f.has(*deps):
l2.append(f)
else:
l1.append(f)
return self._new_rawargs(*l1), tuple(l2)
coeff, notrat = self.args[0].as_coeff_add()
if not coeff is S.Zero:
return coeff, notrat + self.args[1:]
return S.Zero, self.args
# Note, we intentionally do not implement Add.as_coeff_mul(). Rather, we
# let Expr.as_coeff_mul() just always return (S.One, self) for an Add. See
# issue 2425.
def _eval_derivative(self, s):
return Add(*[f.diff(s) for f in self.args])
def _eval_nseries(self, x, n, logx):
terms = [t.nseries(x, n=n, logx=logx) for t in self.args]
return Add(*terms)
def _matches_simple(self, expr, repl_dict):
# handle (w+3).matches('x+5') -> {w: x+2}
coeff, terms = self.as_coeff_add()
if len(terms)==1:
return terms[0].matches(expr - coeff, repl_dict)
return
matches = AssocOp._matches_commutative
@staticmethod
def _combine_inverse(lhs, rhs):
"""
Returns lhs - rhs, but treats arguments like symbols, so things like
oo - oo return 0, instead of a nan.
"""
from sympy import oo, I, expand_mul
if lhs == oo and rhs == oo or lhs == oo*I and rhs == oo*I:
return S.Zero
return expand_mul(lhs - rhs)
@cacheit
[docs] def as_two_terms(self):
"""Return head and tail of self.
This is the most efficient way to get the head and tail of an
expression.
- if you want only the head, use self.args[0];
- if you want to process the arguments of the tail then use
self.as_coef_add() which gives the head and a tuple containing
the arguments of the tail when treated as an Add.
- if you want the coefficient when self is treated as a Mul
then use self.as_coeff_mul()[0]
>>> from sympy.abc import x, y
>>> (3*x*y).as_two_terms()
(3, x*y)
"""
if len(self.args) == 1:
return S.Zero, self
return self.args[0], self._new_rawargs(*self.args[1:])
def as_numer_denom(self):
numers, denoms = [],[]
for n,d in [f.as_numer_denom() for f in self.args]:
numers.append(n)
denoms.append(d)
r = xrange(len(numers))
return Add(*[Mul(*(denoms[:i]+[numers[i]]+denoms[i+1:]))
for i in r]), Mul(*denoms)
def _eval_is_polynomial(self, syms):
return all(term._eval_is_polynomial(syms) for term in self.args)
def _eval_is_rational_function(self, syms):
return all(term._eval_is_rational_function(syms) for term in self.args)
# assumption methods
_eval_is_real = lambda self: self._eval_template_is_attr('is_real')
_eval_is_bounded = lambda self: self._eval_template_is_attr('is_bounded')
_eval_is_commutative = lambda self: self._eval_template_is_attr('is_commutative')
_eval_is_integer = lambda self: self._eval_template_is_attr('is_integer')
_eval_is_comparable = lambda self: self._eval_template_is_attr('is_comparable')
def _eval_is_odd(self):
l = [f for f in self.args if not (f.is_even==True)]
if not l:
return False
if l[0].is_odd:
return self._new_rawargs(*l[1:]).is_even
def _eval_is_irrational(self):
for t in self.args:
a = t.is_irrational
if a: return True
if a is None: return
return False
def _eval_is_positive(self):
c, r = self.as_two_terms()
if c.is_positive and r.is_positive:
return True
if c.is_unbounded:
if r.is_unbounded:
# either c or r is negative
return
else:
return c.is_positive
elif r.is_unbounded:
return r.is_positive
if c.is_nonnegative and r.is_positive:
return True
if r.is_nonnegative and c.is_positive:
return True
if c.is_nonpositive and r.is_nonpositive:
return False
def _eval_is_negative(self):
c, r = self.as_two_terms()
if c.is_negative and r.is_negative:
return True
if c.is_unbounded:
if r.is_unbounded:
# either c or r is positive
return
else:
return c.is_negative
elif r.is_unbounded:
return r.is_negative
if c.is_nonpositive and r.is_negative:
return True
if r.is_nonpositive and c.is_negative:
return True
if c.is_nonnegative and r.is_nonnegative:
return False
def _eval_subs(self, old, new):
if self == old:
return new
if isinstance(old, FunctionClass):
return self.__class__(*[s._eval_subs(old, new) for s in self.args ])
coeff_self, terms_self = self.as_coeff_add()
coeff_old, terms_old = old.as_coeff_add()
if terms_self == terms_old: # (2+a).subs(3+a,y) -> 2-3+y
return Add(new, coeff_self, -coeff_old)
if old.is_Add:
if len(terms_old) < len(terms_self): # (a+b+c+d).subs(b+c,x) -> a+x+d
self_set = set(terms_self)
old_set = set(terms_old)
if old_set < self_set:
ret_set = self_set - old_set
return Add(new, coeff_self, -coeff_old, *[s._eval_subs(old, new) for s in ret_set])
return self.__class__(*[s._eval_subs(old, new) for s in self.args])
def removeO(self):
args = [a for a in self.args if not a.is_Order]
return self._new_rawargs(*args)
def getO(self):
args = [a for a in self.args if a.is_Order]
if args:
return self._new_rawargs(*args)
@cacheit
def as_real_imag(self, deep=True):
sargs, terms = self.args, []
re_part, im_part = [], []
for term in sargs:
re, im = term.as_real_imag(deep=deep)
re_part.append(re)
im_part.append(im)
return (self.func(*re_part), self.func(*im_part))
def _eval_as_leading_term(self, x):
# TODO this does not need to call nseries!
coeff, terms = self.collect(x).as_coeff_add(x)
has_unbounded = bool([f for f in self.args if f.is_unbounded])
if has_unbounded:
if isinstance(terms, Basic):
terms = terms.args
terms = [f for f in terms if not f.is_bounded]
n = 1
s = self.nseries(x, n=n).collect(x) # could be 1/x + 1/(y*x)
while s.is_Order:
n +=1
s = self.nseries(x, n=n)
if s.is_Add:
s = s.removeO()
if s.is_Add:
lst = s.extract_leading_order(x)
return Add(*[e for (e,f) in lst])
return s.as_leading_term(x)
def _eval_conjugate(self):
return Add(*[t.conjugate() for t in self.args])
def _eval_expand_basic(self, deep=True, **hints):
sargs, terms = self.args, []
for term in sargs:
if hasattr(term, '_eval_expand_basic'):
newterm = term._eval_expand_basic(deep=deep, **hints)
else:
newterm = term
terms.append(newterm)
return self.func(*terms)
def _eval_expand_power_exp(self, deep=True, **hints):
sargs, terms = self.args, []
for term in sargs:
if hasattr(term, '_eval_expand_power_exp'):
newterm = term._eval_expand_power_exp(deep=deep, **hints)
else:
newterm = term
terms.append(newterm)
return self.func(*terms)
def _eval_expand_power_base(self, deep=True, **hints):
sargs, terms = self.args, []
for term in sargs:
if hasattr(term, '_eval_expand_power_base'):
newterm = term._eval_expand_power_base(deep=deep, **hints)
else:
newterm = term
terms.append(newterm)
return self.func(*terms)
def _eval_expand_mul(self, deep=True, **hints):
sargs, terms = self.args, []
for term in sargs:
if hasattr(term, '_eval_expand_mul'):
newterm = term._eval_expand_mul(deep=deep, **hints)
else:
newterm = term
terms.append(newterm)
return self.func(*terms)
def _eval_expand_multinomial(self, deep=True, **hints):
sargs, terms = self.args, []
for term in sargs:
if hasattr(term, '_eval_expand_multinomial'):
newterm = term._eval_expand_multinomial(deep=deep, **hints)
else:
newterm = term
terms.append(newterm)
return self.func(*terms)
def _eval_expand_log(self, deep=True, **hints):
sargs, terms = self.args, []
for term in sargs:
if hasattr(term, '_eval_expand_log'):
newterm = term._eval_expand_log(deep=deep, **hints)
else:
newterm = term
terms.append(newterm)
return self.func(*terms)
def _eval_expand_complex(self, deep=True, **hints):
sargs, terms = self.args, []
for term in sargs:
if hasattr(term, '_eval_expand_complex'):
newterm = term._eval_expand_complex(deep=deep, **hints)
else:
newterm = term
terms.append(newterm)
return self.func(*terms)
def _eval_expand_trig(self, deep=True, **hints):
sargs, terms = self.args, []
for term in sargs:
if hasattr(term, '_eval_expand_trig'):
newterm = term._eval_expand_trig(deep=deep, **hints)
else:
newterm = term
terms.append(newterm)
return self.func(*terms)
def _eval_expand_func(self, deep=True, **hints):
sargs, terms = self.args, []
for term in sargs:
if hasattr(term, '_eval_expand_func'):
newterm = term._eval_expand_func(deep=deep, **hints)
else:
newterm = term
terms.append(newterm)
return self.func(*terms)
def __neg__(self):
return Add(*[-t for t in self.args])
def _sage_(self):
s = 0
for x in self.args:
s += x._sage_()
return s
[docs] def primitive(self):
"""
Divide ``self`` by the GCD of coefficients of ``self``.
**Example**
>>> from sympy.abc import x, y
>>> (2*x + 4*y).primitive()
(2, x + 2*y)
>>> (2*x/3 + 4*y/9).primitive()
(2/9, 3*x + 2*y)
>>> (2*x/3 + 4.1*y).primitive()
(1, 2*x/3 + 4.1*y)
"""
terms = []
cont = S.Zero
for term in self.args:
coeff = term.as_coeff_mul()[0]
if coeff.is_Rational:
cont = cont.gcd(coeff)
if cont is not S.One:
terms.append(term)
continue
return S.One, self
for i, term in enumerate(terms):
# XXX: this is extremely slow
terms[i] = term/cont
return cont, self._new_rawargs(*terms)
from function import FunctionClass
from mul import Mul