|
from typing import Tuple as tTuple |
|
from collections import defaultdict |
|
from functools import reduce |
|
from operator import attrgetter |
|
from .basic import _args_sortkey |
|
from .parameters import global_parameters |
|
from .logic import _fuzzy_group, fuzzy_or, fuzzy_not |
|
from .singleton import S |
|
from .operations import AssocOp, AssocOpDispatcher |
|
from .cache import cacheit |
|
from .numbers import equal_valued |
|
from .intfunc import ilcm, igcd |
|
from .expr import Expr |
|
from .kind import UndefinedKind |
|
from sympy.utilities.iterables import is_sequence, sift |
|
|
|
def _could_extract_minus_sign(expr): |
|
|
|
|
|
negative_args = sum(1 for i in expr.args |
|
if i.could_extract_minus_sign()) |
|
positive_args = len(expr.args) - negative_args |
|
if positive_args > negative_args: |
|
return False |
|
elif positive_args < negative_args: |
|
return True |
|
|
|
|
|
|
|
return bool(expr.sort_key() < (-expr).sort_key()) |
|
|
|
|
|
def _addsort(args): |
|
|
|
args.sort(key=_args_sortkey) |
|
|
|
|
|
def _unevaluated_Add(*args): |
|
"""Return a well-formed unevaluated Add: Numbers are collected and |
|
put in slot 0 and args are sorted. Use this when args have changed |
|
but you still want to return an unevaluated Add. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.core.add import _unevaluated_Add as uAdd |
|
>>> from sympy import S, Add |
|
>>> from sympy.abc import x, y |
|
>>> a = uAdd(*[S(1.0), x, S(2)]) |
|
>>> a.args[0] |
|
3.00000000000000 |
|
>>> a.args[1] |
|
x |
|
|
|
Beyond the Number being in slot 0, there is no other assurance of |
|
order for the arguments since they are hash sorted. So, for testing |
|
purposes, output produced by this in some other function can only |
|
be tested against the output of this function or as one of several |
|
options: |
|
|
|
>>> opts = (Add(x, y, evaluate=False), Add(y, x, evaluate=False)) |
|
>>> a = uAdd(x, y) |
|
>>> assert a in opts and a == uAdd(x, y) |
|
>>> uAdd(x + 1, x + 2) |
|
x + x + 3 |
|
""" |
|
args = list(args) |
|
newargs = [] |
|
co = S.Zero |
|
while args: |
|
a = args.pop() |
|
if a.is_Add: |
|
|
|
|
|
args.extend(a.args) |
|
elif a.is_Number: |
|
co += a |
|
else: |
|
newargs.append(a) |
|
_addsort(newargs) |
|
if co: |
|
newargs.insert(0, co) |
|
return Add._from_args(newargs) |
|
|
|
|
|
class Add(Expr, AssocOp): |
|
""" |
|
Expression representing addition operation for algebraic group. |
|
|
|
.. deprecated:: 1.7 |
|
|
|
Using arguments that aren't subclasses of :class:`~.Expr` in core |
|
operators (:class:`~.Mul`, :class:`~.Add`, and :class:`~.Pow`) is |
|
deprecated. See :ref:`non-expr-args-deprecated` for details. |
|
|
|
Every argument of ``Add()`` must be ``Expr``. Infix operator ``+`` |
|
on most scalar objects in SymPy calls this class. |
|
|
|
Another use of ``Add()`` is to represent the structure of abstract |
|
addition so that its arguments can be substituted to return different |
|
class. Refer to examples section for this. |
|
|
|
``Add()`` evaluates the argument unless ``evaluate=False`` is passed. |
|
The evaluation logic includes: |
|
|
|
1. Flattening |
|
``Add(x, Add(y, z))`` -> ``Add(x, y, z)`` |
|
|
|
2. Identity removing |
|
``Add(x, 0, y)`` -> ``Add(x, y)`` |
|
|
|
3. Coefficient collecting by ``.as_coeff_Mul()`` |
|
``Add(x, 2*x)`` -> ``Mul(3, x)`` |
|
|
|
4. Term sorting |
|
``Add(y, x, 2)`` -> ``Add(2, x, y)`` |
|
|
|
If no argument is passed, identity element 0 is returned. If single |
|
element is passed, that element is returned. |
|
|
|
Note that ``Add(*args)`` is more efficient than ``sum(args)`` because |
|
it flattens the arguments. ``sum(a, b, c, ...)`` recursively adds the |
|
arguments as ``a + (b + (c + ...))``, which has quadratic complexity. |
|
On the other hand, ``Add(a, b, c, d)`` does not assume nested |
|
structure, making the complexity linear. |
|
|
|
Since addition is group operation, every argument should have the |
|
same :obj:`sympy.core.kind.Kind()`. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy import Add, I |
|
>>> from sympy.abc import x, y |
|
>>> Add(x, 1) |
|
x + 1 |
|
>>> Add(x, x) |
|
2*x |
|
>>> 2*x**2 + 3*x + I*y + 2*y + 2*x/5 + 1.0*y + 1 |
|
2*x**2 + 17*x/5 + 3.0*y + I*y + 1 |
|
|
|
If ``evaluate=False`` is passed, result is not evaluated. |
|
|
|
>>> Add(1, 2, evaluate=False) |
|
1 + 2 |
|
>>> Add(x, x, evaluate=False) |
|
x + x |
|
|
|
``Add()`` also represents the general structure of addition operation. |
|
|
|
>>> from sympy import MatrixSymbol |
|
>>> A,B = MatrixSymbol('A', 2,2), MatrixSymbol('B', 2,2) |
|
>>> expr = Add(x,y).subs({x:A, y:B}) |
|
>>> expr |
|
A + B |
|
>>> type(expr) |
|
<class 'sympy.matrices.expressions.matadd.MatAdd'> |
|
|
|
Note that the printers do not display in args order. |
|
|
|
>>> Add(x, 1) |
|
x + 1 |
|
>>> Add(x, 1).args |
|
(1, x) |
|
|
|
See Also |
|
======== |
|
|
|
MatAdd |
|
|
|
""" |
|
|
|
__slots__ = () |
|
|
|
args: tTuple[Expr, ...] |
|
|
|
is_Add = True |
|
|
|
_args_type = Expr |
|
|
|
@classmethod |
|
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. |
|
|
|
NB: the removal of 0 is already handled by AssocOp.__new__ |
|
|
|
See Also |
|
======== |
|
|
|
sympy.core.mul.Mul.flatten |
|
|
|
""" |
|
from sympy.calculus.accumulationbounds import AccumBounds |
|
from sympy.matrices.expressions import MatrixExpr |
|
from sympy.tensor.tensor import TensExpr |
|
rv = None |
|
if len(seq) == 2: |
|
a, b = seq |
|
if b.is_Rational: |
|
a, b = b, a |
|
if a.is_Rational: |
|
if b.is_Mul: |
|
rv = [a, b], [], None |
|
if rv: |
|
if all(s.is_commutative for s in rv[0]): |
|
return rv |
|
return [], rv[0], None |
|
|
|
terms = {} |
|
|
|
|
|
coeff = S.Zero |
|
|
|
order_factors = [] |
|
|
|
extra = [] |
|
|
|
for o in seq: |
|
|
|
|
|
if o.is_Order: |
|
if o.expr.is_zero: |
|
continue |
|
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 |
|
|
|
|
|
elif o.is_Number: |
|
if (o is S.NaN or coeff is S.ComplexInfinity and |
|
o.is_finite is False) and not extra: |
|
|
|
return [S.NaN], [], None |
|
if coeff.is_Number or isinstance(coeff, AccumBounds): |
|
coeff += o |
|
if coeff is S.NaN and not extra: |
|
|
|
return [S.NaN], [], None |
|
continue |
|
|
|
elif isinstance(o, AccumBounds): |
|
coeff = o.__add__(coeff) |
|
continue |
|
|
|
elif isinstance(o, MatrixExpr): |
|
|
|
extra.append(o) |
|
continue |
|
|
|
elif isinstance(o, TensExpr): |
|
coeff = o.__add__(coeff) if coeff else o |
|
continue |
|
|
|
elif o is S.ComplexInfinity: |
|
if coeff.is_finite is False and not extra: |
|
|
|
return [S.NaN], [], None |
|
coeff = S.ComplexInfinity |
|
continue |
|
|
|
|
|
elif o.is_Add: |
|
|
|
seq.extend(o.args) |
|
continue |
|
|
|
|
|
elif o.is_Mul: |
|
c, s = o.as_coeff_Mul() |
|
|
|
|
|
elif o.is_Pow: |
|
b, e = o.as_base_exp() |
|
if b.is_Number and (e.is_Integer or |
|
(e.is_Rational and e.is_negative)): |
|
seq.append(b**e) |
|
continue |
|
c, s = S.One, o |
|
|
|
else: |
|
|
|
c = S.One |
|
s = o |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if s in terms: |
|
terms[s] += c |
|
if terms[s] is S.NaN and not extra: |
|
|
|
return [S.NaN], [], None |
|
else: |
|
terms[s] = c |
|
|
|
|
|
|
|
newseq = [] |
|
noncommutative = False |
|
for s, c in terms.items(): |
|
|
|
if c.is_zero: |
|
continue |
|
|
|
elif c is S.One: |
|
newseq.append(s) |
|
|
|
else: |
|
if s.is_Mul: |
|
|
|
|
|
cs = s._new_rawargs(*((c,) + s.args)) |
|
newseq.append(cs) |
|
elif s.is_Add: |
|
|
|
newseq.append(Mul(c, s, evaluate=False)) |
|
else: |
|
|
|
newseq.append(Mul(c, s)) |
|
|
|
noncommutative = noncommutative or not s.is_commutative |
|
|
|
|
|
if coeff is S.Infinity: |
|
newseq = [f for f in newseq if not (f.is_extended_nonnegative or f.is_real)] |
|
|
|
elif coeff is S.NegativeInfinity: |
|
newseq = [f for f in newseq if not (f.is_extended_nonpositive or f.is_real)] |
|
|
|
if coeff is S.ComplexInfinity: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
newseq = [c for c in newseq if not (c.is_finite and |
|
c.is_extended_real is not None)] |
|
|
|
|
|
if order_factors: |
|
newseq2 = [] |
|
for t in newseq: |
|
for o in order_factors: |
|
|
|
if o.contains(t): |
|
t = None |
|
break |
|
|
|
if t is not None: |
|
newseq2.append(t) |
|
newseq = newseq2 + order_factors |
|
|
|
for o in order_factors: |
|
if o.contains(coeff): |
|
coeff = S.Zero |
|
break |
|
|
|
|
|
_addsort(newseq) |
|
|
|
|
|
if coeff is not S.Zero: |
|
newseq.insert(0, coeff) |
|
|
|
if extra: |
|
newseq += extra |
|
noncommutative = True |
|
|
|
|
|
if noncommutative: |
|
return [], newseq, None |
|
else: |
|
return newseq, [], None |
|
|
|
@classmethod |
|
def class_key(cls): |
|
return 3, 1, cls.__name__ |
|
|
|
@property |
|
def kind(self): |
|
k = attrgetter('kind') |
|
kinds = map(k, self.args) |
|
kinds = frozenset(kinds) |
|
if len(kinds) != 1: |
|
|
|
|
|
result = UndefinedKind |
|
else: |
|
result, = kinds |
|
return result |
|
|
|
def could_extract_minus_sign(self): |
|
return _could_extract_minus_sign(self) |
|
|
|
@cacheit |
|
def as_coeff_add(self, *deps): |
|
""" |
|
Returns a tuple (coeff, args) where self is treated as an Add and coeff |
|
is the Number term and args is a tuple of all other terms. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.abc import x |
|
>>> (7 + 3*x).as_coeff_add() |
|
(7, (3*x,)) |
|
>>> (7*x).as_coeff_add() |
|
(0, (7*x,)) |
|
""" |
|
if deps: |
|
l1, l2 = sift(self.args, lambda x: x.has_free(*deps), binary=True) |
|
return self._new_rawargs(*l2), tuple(l1) |
|
coeff, notrat = self.args[0].as_coeff_add() |
|
if coeff is not S.Zero: |
|
return coeff, notrat + self.args[1:] |
|
return S.Zero, self.args |
|
|
|
def as_coeff_Add(self, rational=False, deps=None): |
|
""" |
|
Efficiently extract the coefficient of a summation. |
|
""" |
|
coeff, args = self.args[0], self.args[1:] |
|
|
|
if coeff.is_Number and not rational or coeff.is_Rational: |
|
return coeff, self._new_rawargs(*args) |
|
return S.Zero, self |
|
|
|
|
|
|
|
|
|
|
|
def _eval_power(self, e): |
|
from .evalf import pure_complex |
|
from .relational import is_eq |
|
if len(self.args) == 2 and any(_.is_infinite for _ in self.args): |
|
if e.is_zero is False and is_eq(e, S.One) is False: |
|
|
|
a, b = self.args |
|
if a.coeff(S.ImaginaryUnit): |
|
a, b = b, a |
|
ico = b.coeff(S.ImaginaryUnit) |
|
if ico and ico.is_extended_real and a.is_extended_real: |
|
if e.is_extended_negative: |
|
return S.Zero |
|
if e.is_extended_positive: |
|
return S.ComplexInfinity |
|
return |
|
if e.is_Rational and self.is_number: |
|
ri = pure_complex(self) |
|
if ri: |
|
r, i = ri |
|
if e.q == 2: |
|
from sympy.functions.elementary.miscellaneous import sqrt |
|
D = sqrt(r**2 + i**2) |
|
if D.is_Rational: |
|
from .exprtools import factor_terms |
|
from sympy.functions.elementary.complexes import sign |
|
from .function import expand_multinomial |
|
|
|
root = sqrt(factor_terms((D - r)/2))**e.p |
|
return root*expand_multinomial(( |
|
|
|
(D + r)/abs(i) + sign(i)*S.ImaginaryUnit)**e.p) |
|
elif e == -1: |
|
return _unevaluated_Mul( |
|
r - i*S.ImaginaryUnit, |
|
1/(r**2 + i**2)) |
|
elif e.is_Number and abs(e) != 1: |
|
|
|
c, m = zip(*[i.as_coeff_Mul() for i in self.args]) |
|
if any(i.is_Float for i in c): |
|
big = -1 |
|
for i in c: |
|
if abs(i) >= big: |
|
big = abs(i) |
|
if big > 0 and not equal_valued(big, 1): |
|
from sympy.functions.elementary.complexes import sign |
|
bigs = (big, -big) |
|
c = [sign(i) if i in bigs else i/big for i in c] |
|
addpow = Add(*[c*m for c, m in zip(c, m)])**e |
|
return big**e*addpow |
|
|
|
@cacheit |
|
def _eval_derivative(self, s): |
|
return self.func(*[a.diff(s) for a in self.args]) |
|
|
|
def _eval_nseries(self, x, n, logx, cdir=0): |
|
terms = [t.nseries(x, n=n, logx=logx, cdir=cdir) for t in self.args] |
|
return self.func(*terms) |
|
|
|
def _matches_simple(self, expr, repl_dict): |
|
|
|
coeff, terms = self.as_coeff_add() |
|
if len(terms) == 1: |
|
return terms[0].matches(expr - coeff, repl_dict) |
|
return |
|
|
|
def matches(self, expr, repl_dict=None, old=False): |
|
return self._matches_commutative(expr, repl_dict, old) |
|
|
|
@staticmethod |
|
def _combine_inverse(lhs, rhs): |
|
""" |
|
Returns lhs - rhs, but treats oo like a symbol so oo - oo |
|
returns 0, instead of a nan. |
|
""" |
|
from sympy.simplify.simplify import signsimp |
|
inf = (S.Infinity, S.NegativeInfinity) |
|
if lhs.has(*inf) or rhs.has(*inf): |
|
from .symbol import Dummy |
|
oo = Dummy('oo') |
|
reps = { |
|
S.Infinity: oo, |
|
S.NegativeInfinity: -oo} |
|
ireps = {v: k for k, v in reps.items()} |
|
eq = lhs.xreplace(reps) - rhs.xreplace(reps) |
|
if eq.has(oo): |
|
eq = eq.replace( |
|
lambda x: x.is_Pow and x.base is oo, |
|
lambda x: x.base) |
|
rv = eq.xreplace(ireps) |
|
else: |
|
rv = lhs - rhs |
|
srv = signsimp(rv) |
|
return srv if srv.is_Number else rv |
|
|
|
@cacheit |
|
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 - 2*y + 5).as_two_terms() |
|
(5, 3*x - 2*y) |
|
""" |
|
return self.args[0], self._new_rawargs(*self.args[1:]) |
|
|
|
def as_numer_denom(self): |
|
""" |
|
Decomposes an expression to its numerator part and its |
|
denominator part. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.abc import x, y, z |
|
>>> (x*y/z).as_numer_denom() |
|
(x*y, z) |
|
>>> (x*(y + 1)/y**7).as_numer_denom() |
|
(x*(y + 1), y**7) |
|
|
|
See Also |
|
======== |
|
|
|
sympy.core.expr.Expr.as_numer_denom |
|
""" |
|
|
|
content, expr = self.primitive() |
|
if not isinstance(expr, Add): |
|
return Mul(content, expr, evaluate=False).as_numer_denom() |
|
ncon, dcon = content.as_numer_denom() |
|
|
|
|
|
nd = defaultdict(list) |
|
for f in expr.args: |
|
ni, di = f.as_numer_denom() |
|
nd[di].append(ni) |
|
|
|
|
|
if len(nd) == 1: |
|
d, n = nd.popitem() |
|
return self.func( |
|
*[_keep_coeff(ncon, ni) for ni in n]), _keep_coeff(dcon, d) |
|
|
|
|
|
for d, n in nd.items(): |
|
if len(n) == 1: |
|
nd[d] = n[0] |
|
else: |
|
nd[d] = self.func(*n) |
|
|
|
|
|
denoms, numers = [list(i) for i in zip(*iter(nd.items()))] |
|
n, d = self.func(*[Mul(*(denoms[:i] + [numers[i]] + denoms[i + 1:])) |
|
for i in range(len(numers))]), Mul(*denoms) |
|
|
|
return _keep_coeff(ncon, n), _keep_coeff(dcon, d) |
|
|
|
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) |
|
|
|
def _eval_is_meromorphic(self, x, a): |
|
return _fuzzy_group((arg.is_meromorphic(x, a) for arg in self.args), |
|
quick_exit=True) |
|
|
|
def _eval_is_algebraic_expr(self, syms): |
|
return all(term._eval_is_algebraic_expr(syms) for term in self.args) |
|
|
|
|
|
_eval_is_real = lambda self: _fuzzy_group( |
|
(a.is_real for a in self.args), quick_exit=True) |
|
_eval_is_extended_real = lambda self: _fuzzy_group( |
|
(a.is_extended_real for a in self.args), quick_exit=True) |
|
_eval_is_complex = lambda self: _fuzzy_group( |
|
(a.is_complex for a in self.args), quick_exit=True) |
|
_eval_is_antihermitian = lambda self: _fuzzy_group( |
|
(a.is_antihermitian for a in self.args), quick_exit=True) |
|
_eval_is_finite = lambda self: _fuzzy_group( |
|
(a.is_finite for a in self.args), quick_exit=True) |
|
_eval_is_hermitian = lambda self: _fuzzy_group( |
|
(a.is_hermitian for a in self.args), quick_exit=True) |
|
_eval_is_integer = lambda self: _fuzzy_group( |
|
(a.is_integer for a in self.args), quick_exit=True) |
|
_eval_is_rational = lambda self: _fuzzy_group( |
|
(a.is_rational for a in self.args), quick_exit=True) |
|
_eval_is_algebraic = lambda self: _fuzzy_group( |
|
(a.is_algebraic for a in self.args), quick_exit=True) |
|
_eval_is_commutative = lambda self: _fuzzy_group( |
|
a.is_commutative for a in self.args) |
|
|
|
def _eval_is_infinite(self): |
|
sawinf = False |
|
for a in self.args: |
|
ainf = a.is_infinite |
|
if ainf is None: |
|
return None |
|
elif ainf is True: |
|
|
|
if sawinf is True: |
|
return None |
|
sawinf = True |
|
return sawinf |
|
|
|
def _eval_is_imaginary(self): |
|
nz = [] |
|
im_I = [] |
|
for a in self.args: |
|
if a.is_extended_real: |
|
if a.is_zero: |
|
pass |
|
elif a.is_zero is False: |
|
nz.append(a) |
|
else: |
|
return |
|
elif a.is_imaginary: |
|
im_I.append(a*S.ImaginaryUnit) |
|
elif a.is_Mul and S.ImaginaryUnit in a.args: |
|
coeff, ai = a.as_coeff_mul(S.ImaginaryUnit) |
|
if ai == (S.ImaginaryUnit,) and coeff.is_extended_real: |
|
im_I.append(-coeff) |
|
else: |
|
return |
|
else: |
|
return |
|
b = self.func(*nz) |
|
if b != self: |
|
if b.is_zero: |
|
return fuzzy_not(self.func(*im_I).is_zero) |
|
elif b.is_zero is False: |
|
return False |
|
|
|
def _eval_is_zero(self): |
|
if self.is_commutative is False: |
|
|
|
|
|
return |
|
nz = [] |
|
z = 0 |
|
im_or_z = False |
|
im = 0 |
|
for a in self.args: |
|
if a.is_extended_real: |
|
if a.is_zero: |
|
z += 1 |
|
elif a.is_zero is False: |
|
nz.append(a) |
|
else: |
|
return |
|
elif a.is_imaginary: |
|
im += 1 |
|
elif a.is_Mul and S.ImaginaryUnit in a.args: |
|
coeff, ai = a.as_coeff_mul(S.ImaginaryUnit) |
|
if ai == (S.ImaginaryUnit,) and coeff.is_extended_real: |
|
im_or_z = True |
|
else: |
|
return |
|
else: |
|
return |
|
if z == len(self.args): |
|
return True |
|
if len(nz) in [0, len(self.args)]: |
|
return None |
|
b = self.func(*nz) |
|
if b.is_zero: |
|
if not im_or_z: |
|
if im == 0: |
|
return True |
|
elif im == 1: |
|
return False |
|
if b.is_zero is False: |
|
return False |
|
|
|
def _eval_is_odd(self): |
|
l = [f for f in self.args if not (f.is_even is 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: |
|
others = list(self.args) |
|
others.remove(t) |
|
if all(x.is_rational is True for x in others): |
|
return True |
|
return None |
|
if a is None: |
|
return |
|
return False |
|
|
|
def _all_nonneg_or_nonppos(self): |
|
nn = np = 0 |
|
for a in self.args: |
|
if a.is_nonnegative: |
|
if np: |
|
return False |
|
nn = 1 |
|
elif a.is_nonpositive: |
|
if nn: |
|
return False |
|
np = 1 |
|
else: |
|
break |
|
else: |
|
return True |
|
|
|
def _eval_is_extended_positive(self): |
|
if self.is_number: |
|
return super()._eval_is_extended_positive() |
|
c, a = self.as_coeff_Add() |
|
if not c.is_zero: |
|
from .exprtools import _monotonic_sign |
|
v = _monotonic_sign(a) |
|
if v is not None: |
|
s = v + c |
|
if s != self and s.is_extended_positive and a.is_extended_nonnegative: |
|
return True |
|
if len(self.free_symbols) == 1: |
|
v = _monotonic_sign(self) |
|
if v is not None and v != self and v.is_extended_positive: |
|
return True |
|
pos = nonneg = nonpos = unknown_sign = False |
|
saw_INF = set() |
|
args = [a for a in self.args if not a.is_zero] |
|
if not args: |
|
return False |
|
for a in args: |
|
ispos = a.is_extended_positive |
|
infinite = a.is_infinite |
|
if infinite: |
|
saw_INF.add(fuzzy_or((ispos, a.is_extended_nonnegative))) |
|
if True in saw_INF and False in saw_INF: |
|
return |
|
if ispos: |
|
pos = True |
|
continue |
|
elif a.is_extended_nonnegative: |
|
nonneg = True |
|
continue |
|
elif a.is_extended_nonpositive: |
|
nonpos = True |
|
continue |
|
|
|
if infinite is None: |
|
return |
|
unknown_sign = True |
|
|
|
if saw_INF: |
|
if len(saw_INF) > 1: |
|
return |
|
return saw_INF.pop() |
|
elif unknown_sign: |
|
return |
|
elif not nonpos and not nonneg and pos: |
|
return True |
|
elif not nonpos and pos: |
|
return True |
|
elif not pos and not nonneg: |
|
return False |
|
|
|
def _eval_is_extended_nonnegative(self): |
|
if not self.is_number: |
|
c, a = self.as_coeff_Add() |
|
if not c.is_zero and a.is_extended_nonnegative: |
|
from .exprtools import _monotonic_sign |
|
v = _monotonic_sign(a) |
|
if v is not None: |
|
s = v + c |
|
if s != self and s.is_extended_nonnegative: |
|
return True |
|
if len(self.free_symbols) == 1: |
|
v = _monotonic_sign(self) |
|
if v is not None and v != self and v.is_extended_nonnegative: |
|
return True |
|
|
|
def _eval_is_extended_nonpositive(self): |
|
if not self.is_number: |
|
c, a = self.as_coeff_Add() |
|
if not c.is_zero and a.is_extended_nonpositive: |
|
from .exprtools import _monotonic_sign |
|
v = _monotonic_sign(a) |
|
if v is not None: |
|
s = v + c |
|
if s != self and s.is_extended_nonpositive: |
|
return True |
|
if len(self.free_symbols) == 1: |
|
v = _monotonic_sign(self) |
|
if v is not None and v != self and v.is_extended_nonpositive: |
|
return True |
|
|
|
def _eval_is_extended_negative(self): |
|
if self.is_number: |
|
return super()._eval_is_extended_negative() |
|
c, a = self.as_coeff_Add() |
|
if not c.is_zero: |
|
from .exprtools import _monotonic_sign |
|
v = _monotonic_sign(a) |
|
if v is not None: |
|
s = v + c |
|
if s != self and s.is_extended_negative and a.is_extended_nonpositive: |
|
return True |
|
if len(self.free_symbols) == 1: |
|
v = _monotonic_sign(self) |
|
if v is not None and v != self and v.is_extended_negative: |
|
return True |
|
neg = nonpos = nonneg = unknown_sign = False |
|
saw_INF = set() |
|
args = [a for a in self.args if not a.is_zero] |
|
if not args: |
|
return False |
|
for a in args: |
|
isneg = a.is_extended_negative |
|
infinite = a.is_infinite |
|
if infinite: |
|
saw_INF.add(fuzzy_or((isneg, a.is_extended_nonpositive))) |
|
if True in saw_INF and False in saw_INF: |
|
return |
|
if isneg: |
|
neg = True |
|
continue |
|
elif a.is_extended_nonpositive: |
|
nonpos = True |
|
continue |
|
elif a.is_extended_nonnegative: |
|
nonneg = True |
|
continue |
|
|
|
if infinite is None: |
|
return |
|
unknown_sign = True |
|
|
|
if saw_INF: |
|
if len(saw_INF) > 1: |
|
return |
|
return saw_INF.pop() |
|
elif unknown_sign: |
|
return |
|
elif not nonneg and not nonpos and neg: |
|
return True |
|
elif not nonneg and neg: |
|
return True |
|
elif not neg and not nonpos: |
|
return False |
|
|
|
def _eval_subs(self, old, new): |
|
if not old.is_Add: |
|
if old is S.Infinity and -old in self.args: |
|
|
|
return self.xreplace({-old: -new}) |
|
return None |
|
|
|
coeff_self, terms_self = self.as_coeff_Add() |
|
coeff_old, terms_old = old.as_coeff_Add() |
|
|
|
if coeff_self.is_Rational and coeff_old.is_Rational: |
|
if terms_self == terms_old: |
|
return self.func(new, coeff_self, -coeff_old) |
|
if terms_self == -terms_old: |
|
return self.func(-new, coeff_self, coeff_old) |
|
|
|
if coeff_self.is_Rational and coeff_old.is_Rational \ |
|
or coeff_self == coeff_old: |
|
args_old, args_self = self.func.make_args( |
|
terms_old), self.func.make_args(terms_self) |
|
if len(args_old) < len(args_self): |
|
self_set = set(args_self) |
|
old_set = set(args_old) |
|
|
|
if old_set < self_set: |
|
ret_set = self_set - old_set |
|
return self.func(new, coeff_self, -coeff_old, |
|
*[s._subs(old, new) for s in ret_set]) |
|
|
|
args_old = self.func.make_args( |
|
-terms_old) |
|
old_set = set(args_old) |
|
if old_set < self_set: |
|
ret_set = self_set - old_set |
|
return self.func(-new, coeff_self, coeff_old, |
|
*[s._subs(old, new) for s in ret_set]) |
|
|
|
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 extract_leading_order(self, symbols, point=None): |
|
""" |
|
Returns the leading term and its order. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.abc import x |
|
>>> (x + 1 + 1/x**5).extract_leading_order(x) |
|
((x**(-5), O(x**(-5))),) |
|
>>> (1 + x).extract_leading_order(x) |
|
((1, O(1)),) |
|
>>> (x + x**2).extract_leading_order(x) |
|
((x, O(x)),) |
|
|
|
""" |
|
from sympy.series.order import Order |
|
lst = [] |
|
symbols = list(symbols if is_sequence(symbols) else [symbols]) |
|
if not point: |
|
point = [0]*len(symbols) |
|
seq = [(f, Order(f, *zip(symbols, point))) for f in self.args] |
|
for ef, of in seq: |
|
for e, o in lst: |
|
if o.contains(of) and o != of: |
|
of = None |
|
break |
|
if of is None: |
|
continue |
|
new_lst = [(ef, of)] |
|
for e, o in lst: |
|
if of.contains(o) and o != of: |
|
continue |
|
new_lst.append((e, o)) |
|
lst = new_lst |
|
return tuple(lst) |
|
|
|
def as_real_imag(self, deep=True, **hints): |
|
""" |
|
Return a tuple representing a complex number. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy import I |
|
>>> (7 + 9*I).as_real_imag() |
|
(7, 9) |
|
>>> ((1 + I)/(1 - I)).as_real_imag() |
|
(0, 1) |
|
>>> ((1 + 2*I)*(1 + 3*I)).as_real_imag() |
|
(-5, 5) |
|
""" |
|
sargs = 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, logx=None, cdir=0): |
|
from sympy.core.symbol import Dummy, Symbol |
|
from sympy.series.order import Order |
|
from sympy.functions.elementary.exponential import log |
|
from sympy.functions.elementary.piecewise import Piecewise, piecewise_fold |
|
from .function import expand_mul |
|
|
|
o = self.getO() |
|
if o is None: |
|
o = Order(0) |
|
old = self.removeO() |
|
|
|
if old.has(Piecewise): |
|
old = piecewise_fold(old) |
|
|
|
|
|
|
|
if any(isinstance(a, log) for a in self.args): |
|
logflags = {"deep": True, "log": True, "mul": False, "power_exp": False, |
|
"power_base": False, "multinomial": False, "basic": False, "force": False, |
|
"factor": False} |
|
old = old.expand(**logflags) |
|
expr = expand_mul(old) |
|
|
|
if not expr.is_Add: |
|
return expr.as_leading_term(x, logx=logx, cdir=cdir) |
|
|
|
infinite = [t for t in expr.args if t.is_infinite] |
|
|
|
_logx = Dummy('logx') if logx is None else logx |
|
leading_terms = [t.as_leading_term(x, logx=_logx, cdir=cdir) for t in expr.args] |
|
|
|
min, new_expr = Order(0), 0 |
|
|
|
try: |
|
for term in leading_terms: |
|
order = Order(term, x) |
|
if not min or order not in min: |
|
min = order |
|
new_expr = term |
|
elif min in order: |
|
new_expr += term |
|
|
|
except TypeError: |
|
return expr |
|
|
|
if logx is None: |
|
new_expr = new_expr.subs(_logx, log(x)) |
|
|
|
is_zero = new_expr.is_zero |
|
if is_zero is None: |
|
new_expr = new_expr.trigsimp().cancel() |
|
is_zero = new_expr.is_zero |
|
if is_zero is True: |
|
|
|
|
|
try: |
|
n0 = min.getn() |
|
except NotImplementedError: |
|
n0 = S.One |
|
if n0.has(Symbol): |
|
n0 = S.One |
|
res = Order(1) |
|
incr = S.One |
|
while res.is_Order: |
|
res = old._eval_nseries(x, n=n0+incr, logx=logx, cdir=cdir).cancel().powsimp().trigsimp() |
|
incr *= 2 |
|
return res.as_leading_term(x, logx=logx, cdir=cdir) |
|
|
|
elif new_expr is S.NaN: |
|
return old.func._from_args(infinite) + o |
|
|
|
else: |
|
return new_expr |
|
|
|
def _eval_adjoint(self): |
|
return self.func(*[t.adjoint() for t in self.args]) |
|
|
|
def _eval_conjugate(self): |
|
return self.func(*[t.conjugate() for t in self.args]) |
|
|
|
def _eval_transpose(self): |
|
return self.func(*[t.transpose() for t in self.args]) |
|
|
|
def primitive(self): |
|
""" |
|
Return ``(R, self/R)`` where ``R``` is the Rational GCD of ``self```. |
|
|
|
``R`` is collected only from the leading coefficient of each term. |
|
|
|
Examples |
|
======== |
|
|
|
>>> 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.2*y).primitive() |
|
(1/3, 2*x + 12.6*y) |
|
|
|
No subprocessing of term factors is performed: |
|
|
|
>>> ((2 + 2*x)*x + 2).primitive() |
|
(1, x*(2*x + 2) + 2) |
|
|
|
Recursive processing can be done with the ``as_content_primitive()`` |
|
method: |
|
|
|
>>> ((2 + 2*x)*x + 2).as_content_primitive() |
|
(2, x*(x + 1) + 1) |
|
|
|
See also: primitive() function in polytools.py |
|
|
|
""" |
|
|
|
terms = [] |
|
inf = False |
|
for a in self.args: |
|
c, m = a.as_coeff_Mul() |
|
if not c.is_Rational: |
|
c = S.One |
|
m = a |
|
inf = inf or m is S.ComplexInfinity |
|
terms.append((c.p, c.q, m)) |
|
|
|
if not inf: |
|
ngcd = reduce(igcd, [t[0] for t in terms], 0) |
|
dlcm = reduce(ilcm, [t[1] for t in terms], 1) |
|
else: |
|
ngcd = reduce(igcd, [t[0] for t in terms if t[1]], 0) |
|
dlcm = reduce(ilcm, [t[1] for t in terms if t[1]], 1) |
|
|
|
if ngcd == dlcm == 1: |
|
return S.One, self |
|
if not inf: |
|
for i, (p, q, term) in enumerate(terms): |
|
terms[i] = _keep_coeff(Rational((p//ngcd)*(dlcm//q)), term) |
|
else: |
|
for i, (p, q, term) in enumerate(terms): |
|
if q: |
|
terms[i] = _keep_coeff(Rational((p//ngcd)*(dlcm//q)), term) |
|
else: |
|
terms[i] = _keep_coeff(Rational(p, q), term) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if terms[0].is_Number or terms[0] is S.ComplexInfinity: |
|
c = terms.pop(0) |
|
else: |
|
c = None |
|
_addsort(terms) |
|
if c: |
|
terms.insert(0, c) |
|
return Rational(ngcd, dlcm), self._new_rawargs(*terms) |
|
|
|
def as_content_primitive(self, radical=False, clear=True): |
|
"""Return the tuple (R, self/R) where R is the positive Rational |
|
extracted from self. If radical is True (default is False) then |
|
common radicals will be removed and included as a factor of the |
|
primitive expression. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy import sqrt |
|
>>> (3 + 3*sqrt(2)).as_content_primitive() |
|
(3, 1 + sqrt(2)) |
|
|
|
Radical content can also be factored out of the primitive: |
|
|
|
>>> (2*sqrt(2) + 4*sqrt(10)).as_content_primitive(radical=True) |
|
(2, sqrt(2)*(1 + 2*sqrt(5))) |
|
|
|
See docstring of Expr.as_content_primitive for more examples. |
|
""" |
|
con, prim = self.func(*[_keep_coeff(*a.as_content_primitive( |
|
radical=radical, clear=clear)) for a in self.args]).primitive() |
|
if not clear and not con.is_Integer and prim.is_Add: |
|
con, d = con.as_numer_denom() |
|
_p = prim/d |
|
if any(a.as_coeff_Mul()[0].is_Integer for a in _p.args): |
|
prim = _p |
|
else: |
|
con /= d |
|
if radical and prim.is_Add: |
|
|
|
args = prim.args |
|
rads = [] |
|
common_q = None |
|
for m in args: |
|
term_rads = defaultdict(list) |
|
for ai in Mul.make_args(m): |
|
if ai.is_Pow: |
|
b, e = ai.as_base_exp() |
|
if e.is_Rational and b.is_Integer: |
|
term_rads[e.q].append(abs(int(b))**e.p) |
|
if not term_rads: |
|
break |
|
if common_q is None: |
|
common_q = set(term_rads.keys()) |
|
else: |
|
common_q = common_q & set(term_rads.keys()) |
|
if not common_q: |
|
break |
|
rads.append(term_rads) |
|
else: |
|
|
|
|
|
for r in rads: |
|
for q in list(r.keys()): |
|
if q not in common_q: |
|
r.pop(q) |
|
for q in r: |
|
r[q] = Mul(*r[q]) |
|
|
|
G = [] |
|
for q in common_q: |
|
g = reduce(igcd, [r[q] for r in rads], 0) |
|
if g != 1: |
|
G.append(g**Rational(1, q)) |
|
if G: |
|
G = Mul(*G) |
|
args = [ai/G for ai in args] |
|
prim = G*prim.func(*args) |
|
|
|
return con, prim |
|
|
|
@property |
|
def _sorted_args(self): |
|
from .sorting import default_sort_key |
|
return tuple(sorted(self.args, key=default_sort_key)) |
|
|
|
def _eval_difference_delta(self, n, step): |
|
from sympy.series.limitseq import difference_delta as dd |
|
return self.func(*[dd(a, n, step) for a in self.args]) |
|
|
|
@property |
|
def _mpc_(self): |
|
""" |
|
Convert self to an mpmath mpc if possible |
|
""" |
|
from .numbers import Float |
|
re_part, rest = self.as_coeff_Add() |
|
im_part, imag_unit = rest.as_coeff_Mul() |
|
if not imag_unit == S.ImaginaryUnit: |
|
|
|
|
|
|
|
raise AttributeError("Cannot convert Add to mpc. Must be of the form Number + Number*I") |
|
|
|
return (Float(re_part)._mpf_, Float(im_part)._mpf_) |
|
|
|
def __neg__(self): |
|
if not global_parameters.distribute: |
|
return super().__neg__() |
|
return Mul(S.NegativeOne, self) |
|
|
|
add = AssocOpDispatcher('add') |
|
|
|
from .mul import Mul, _keep_coeff, _unevaluated_Mul |
|
from .numbers import Rational |
|
|