123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287 |
- from typing import Tuple as tTuple
- from collections import defaultdict
- from functools import cmp_to_key, reduce
- from operator import attrgetter
- from .basic import Basic
- 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 ilcm, igcd, equal_valued
- from .expr import Expr
- from .kind import UndefinedKind
- from sympy.utilities.iterables import is_sequence, sift
- # Key for sorting commutative args in canonical order
- _args_sortkey = cmp_to_key(Basic.compare)
- def _could_extract_minus_sign(expr):
- # assume expr is Add-like
- # We choose the one with less arguments with minus signs
- 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
- # choose based on .sort_key() to prefer
- # x - 1 instead of 1 - x and
- # 3 - sqrt(2) instead of -3 + sqrt(2)
- return bool(expr.sort_key() < (-expr).sort_key())
- def _addsort(args):
- # in-place sorting of 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:
- # this will keep nesting from building up
- # so that x + (x + 1) -> x + x + 1 (3 args)
- 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 = {} # term -> coeff
- # e.g. x**2 -> 5 for ... + 5*x**2 + ...
- coeff = S.Zero # coefficient (Number or zoo) to always be in slot 0
- # e.g. 3 + ...
- order_factors = []
- extra = []
- for o in seq:
- # O(x)
- 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
- # 3 or NaN
- elif o.is_Number:
- if (o is S.NaN or coeff is S.ComplexInfinity and
- o.is_finite is False) and not extra:
- # we know for sure the result will be nan
- return [S.NaN], [], None
- if coeff.is_Number or isinstance(coeff, AccumBounds):
- coeff += o
- if coeff is S.NaN and not extra:
- # we know for sure the result will be nan
- return [S.NaN], [], None
- continue
- elif isinstance(o, AccumBounds):
- coeff = o.__add__(coeff)
- continue
- elif isinstance(o, MatrixExpr):
- # can't add 0 to Matrix so make sure coeff is not 0
- 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:
- # 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()
- # check for unevaluated Pow, e.g. 2**3 or 2**(-1/2)
- 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:
- # everything 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
- if terms[s] is S.NaN and not extra:
- # we know for sure the result will be nan
- return [S.NaN], [], None
- 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_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)
- elif s.is_Add:
- # we just re-create the unevaluated Mul
- newseq.append(Mul(c, s, evaluate=False))
- 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_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:
- # zoo might be
- # infinite_real + finite_im
- # finite_real + infinite_im
- # infinite_real + infinite_im
- # addition of a finite real or imaginary number won't be able to
- # change the zoo nature; adding an infinite qualtity would result
- # in a NaN condition if it had sign opposite of the infinite
- # portion of zoo, e.g., infinite_real - infinite_real.
- newseq = [c for c in newseq if not (c.is_finite and
- c.is_extended_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
- _addsort(newseq)
- # current code expects coeff to be first
- if coeff is not S.Zero:
- newseq.insert(0, coeff)
- if extra:
- newseq += extra
- noncommutative = True
- # we are done
- 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:
- # Since addition is group operator, kind must be same.
- # We know that this is unexpected signature, so return this.
- 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
- # 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 5524.
- 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:
- # looking for literal a + I*b
- 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
- # (r, i, D) is a Pythagorean triple
- root = sqrt(factor_terms((D - r)/2))**e.p
- return root*expand_multinomial((
- # principle value
- (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:
- # handle the Float case: (2.0 + 4*x)**e -> 4**e*(0.5 + x)**e
- c, m = zip(*[i.as_coeff_Mul() for i in self.args])
- if any(i.is_Float for i in c): # XXX should this always be done?
- 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):
- # 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
- 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
- """
- # clear rational denominator
- content, expr = self.primitive()
- if not isinstance(expr, Add):
- return Mul(content, expr, evaluate=False).as_numer_denom()
- ncon, dcon = content.as_numer_denom()
- # collect numerators and denominators of the terms
- nd = defaultdict(list)
- for f in expr.args:
- ni, di = f.as_numer_denom()
- nd[di].append(ni)
- # check for quick exit
- if len(nd) == 1:
- d, n = nd.popitem()
- return self.func(
- *[_keep_coeff(ncon, ni) for ni in n]), _keep_coeff(dcon, d)
- # sum up the terms having a common denominator
- for d, n in nd.items():
- if len(n) == 1:
- nd[d] = n[0]
- else:
- nd[d] = self.func(*n)
- # assemble single numerator and denominator
- 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)
- # assumption methods
- _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:
- # infinite+infinite might not be infinite
- 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:
- # issue 10528: there is no way to know if a nc symbol
- # is zero or not
- 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:
- # foo - oo is foo + (-oo) internally
- 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: # (2 + a).subs( 3 + a, y) -> -1 + y
- return self.func(new, coeff_self, -coeff_old)
- if terms_self == -terms_old: # (2 + a).subs(-3 - a, y) -> -1 - y
- 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): # (a+b+c).subs(b+c,x) -> a+x
- 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) # (a+b+c+d).subs(-b-c,x) -> a-x+d
- 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)
- # This expansion is the last part of expand_log. expand_log also calls
- # expand_mul with factor=True, which would be more expensive
- 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:
- # simple leading term analysis gave us cancelled terms but we have to send
- # back a term, so compute the leading term (via series)
- 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)
- # we don't need a complete re-flattening since no new terms will join
- # so we just use the same sort as is used in Add.flatten. When the
- # coefficient changes, the ordering of terms may change, e.g.
- # (3*x, 6*y) -> (2*y, x)
- #
- # We do need to make sure that term[0] stays in position 0, however.
- #
- 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:
- # look for common radicals that can be removed
- 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:
- # process rads
- # keep only those in common_q
- 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])
- # find the gcd of bases for each 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:
- # ValueError may seem more reasonable but since it's a @property,
- # we need to use AttributeError to keep from confusing things like
- # hasattr.
- 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
|