12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195 |
- from typing import Tuple as tTuple
- from collections import defaultdict
- from functools import cmp_to_key, reduce
- from itertools import product
- import operator
- from .sympify import sympify
- from .basic import Basic
- from .singleton import S
- from .operations import AssocOp, AssocOpDispatcher
- from .cache import cacheit
- from .logic import fuzzy_not, _fuzzy_group
- from .expr import Expr
- from .parameters import global_parameters
- from .kind import KindDispatcher
- from .traversal import bottom_up
- from sympy.utilities.iterables import sift
- # internal marker to indicate:
- # "there are still non-commutative objects -- don't forget to process them"
- class NC_Marker:
- is_Order = False
- is_Mul = False
- is_Number = False
- is_Poly = False
- is_commutative = False
- # Key for sorting commutative args in canonical order
- _args_sortkey = cmp_to_key(Basic.compare)
- def _mulsort(args):
- # in-place sorting of args
- args.sort(key=_args_sortkey)
- def _unevaluated_Mul(*args):
- """Return a well-formed unevaluated Mul: Numbers are collected and
- put in slot 0, any arguments that are Muls will be flattened, and args
- are sorted. Use this when args have changed but you still want to return
- an unevaluated Mul.
- Examples
- ========
- >>> from sympy.core.mul import _unevaluated_Mul as uMul
- >>> from sympy import S, sqrt, Mul
- >>> from sympy.abc import x
- >>> a = uMul(*[S(3.0), x, S(2)])
- >>> a.args[0]
- 6.00000000000000
- >>> a.args[1]
- x
- Two unevaluated Muls with the same arguments will
- always compare as equal during testing:
- >>> m = uMul(sqrt(2), sqrt(3))
- >>> m == uMul(sqrt(3), sqrt(2))
- True
- >>> u = Mul(sqrt(3), sqrt(2), evaluate=False)
- >>> m == uMul(u)
- True
- >>> m == Mul(*m.args)
- False
- """
- args = list(args)
- newargs = []
- ncargs = []
- co = S.One
- while args:
- a = args.pop()
- if a.is_Mul:
- c, nc = a.args_cnc()
- args.extend(c)
- if nc:
- ncargs.append(Mul._from_args(nc))
- elif a.is_Number:
- co *= a
- else:
- newargs.append(a)
- _mulsort(newargs)
- if co is not S.One:
- newargs.insert(0, co)
- if ncargs:
- newargs.append(Mul._from_args(ncargs))
- return Mul._from_args(newargs)
- class Mul(Expr, AssocOp):
- """
- Expression representing multiplication operation for algebraic field.
- .. 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 ``Mul()`` must be ``Expr``. Infix operator ``*``
- on most scalar objects in SymPy calls this class.
- Another use of ``Mul()`` is to represent the structure of abstract
- multiplication so that its arguments can be substituted to return
- different class. Refer to examples section for this.
- ``Mul()`` evaluates the argument unless ``evaluate=False`` is passed.
- The evaluation logic includes:
- 1. Flattening
- ``Mul(x, Mul(y, z))`` -> ``Mul(x, y, z)``
- 2. Identity removing
- ``Mul(x, 1, y)`` -> ``Mul(x, y)``
- 3. Exponent collecting by ``.as_base_exp()``
- ``Mul(x, x**2)`` -> ``Pow(x, 3)``
- 4. Term sorting
- ``Mul(y, x, 2)`` -> ``Mul(2, x, y)``
- Since multiplication can be vector space operation, arguments may
- have the different :obj:`sympy.core.kind.Kind()`. Kind of the
- resulting object is automatically inferred.
- Examples
- ========
- >>> from sympy import Mul
- >>> from sympy.abc import x, y
- >>> Mul(x, 1)
- x
- >>> Mul(x, x)
- x**2
- If ``evaluate=False`` is passed, result is not evaluated.
- >>> Mul(1, 2, evaluate=False)
- 1*2
- >>> Mul(x, x, evaluate=False)
- x*x
- ``Mul()`` also represents the general structure of multiplication
- operation.
- >>> from sympy import MatrixSymbol
- >>> A = MatrixSymbol('A', 2,2)
- >>> expr = Mul(x,y).subs({y:A})
- >>> expr
- x*A
- >>> type(expr)
- <class 'sympy.matrices.expressions.matmul.MatMul'>
- See Also
- ========
- MatMul
- """
- __slots__ = ()
- args: tTuple[Expr]
- is_Mul = True
- _args_type = Expr
- _kind_dispatcher = KindDispatcher("Mul_kind_dispatcher", commutative=True)
- @property
- def kind(self):
- arg_kinds = (a.kind for a in self.args)
- return self._kind_dispatcher(*arg_kinds)
- def could_extract_minus_sign(self):
- if self == (-self):
- return False # e.g. zoo*x == -zoo*x
- c = self.args[0]
- return c.is_Number and c.is_extended_negative
- def __neg__(self):
- c, args = self.as_coeff_mul()
- if args[0] is not S.ComplexInfinity:
- c = -c
- if c is not S.One:
- if args[0].is_Number:
- args = list(args)
- if c is S.NegativeOne:
- args[0] = -args[0]
- else:
- args[0] *= c
- else:
- args = (c,) + args
- return self._from_args(args, self.is_commutative)
- @classmethod
- def flatten(cls, seq):
- """Return commutative, noncommutative and order arguments by
- combining related terms.
- Notes
- =====
- * In an expression like ``a*b*c``, Python process this through SymPy
- as ``Mul(Mul(a, b), c)``. This can have undesirable consequences.
- - Sometimes terms are not combined as one would like:
- {c.f. https://github.com/sympy/sympy/issues/4596}
- >>> from sympy import Mul, sqrt
- >>> from sympy.abc import x, y, z
- >>> 2*(x + 1) # this is the 2-arg Mul behavior
- 2*x + 2
- >>> y*(x + 1)*2
- 2*y*(x + 1)
- >>> 2*(x + 1)*y # 2-arg result will be obtained first
- y*(2*x + 2)
- >>> Mul(2, x + 1, y) # all 3 args simultaneously processed
- 2*y*(x + 1)
- >>> 2*((x + 1)*y) # parentheses can control this behavior
- 2*y*(x + 1)
- Powers with compound bases may not find a single base to
- combine with unless all arguments are processed at once.
- Post-processing may be necessary in such cases.
- {c.f. https://github.com/sympy/sympy/issues/5728}
- >>> a = sqrt(x*sqrt(y))
- >>> a**3
- (x*sqrt(y))**(3/2)
- >>> Mul(a,a,a)
- (x*sqrt(y))**(3/2)
- >>> a*a*a
- x*sqrt(y)*sqrt(x*sqrt(y))
- >>> _.subs(a.base, z).subs(z, a.base)
- (x*sqrt(y))**(3/2)
- - If more than two terms are being multiplied then all the
- previous terms will be re-processed for each new argument.
- So if each of ``a``, ``b`` and ``c`` were :class:`Mul`
- expression, then ``a*b*c`` (or building up the product
- with ``*=``) will process all the arguments of ``a`` and
- ``b`` twice: once when ``a*b`` is computed and again when
- ``c`` is multiplied.
- Using ``Mul(a, b, c)`` will process all arguments once.
- * The results of Mul are cached according to arguments, so flatten
- will only be called once for ``Mul(a, b, c)``. If you can
- structure a calculation so the arguments are most likely to be
- repeats then this can save time in computing the answer. For
- example, say you had a Mul, M, that you wished to divide by ``d[i]``
- and multiply by ``n[i]`` and you suspect there are many repeats
- in ``n``. It would be better to compute ``M*n[i]/d[i]`` rather
- than ``M/d[i]*n[i]`` since every time n[i] is a repeat, the
- product, ``M*n[i]`` will be returned without flattening -- the
- cached value will be returned. If you divide by the ``d[i]``
- first (and those are more unique than the ``n[i]``) then that will
- create a new Mul, ``M/d[i]`` the args of which will be traversed
- again when it is multiplied by ``n[i]``.
- {c.f. https://github.com/sympy/sympy/issues/5706}
- This consideration is moot if the cache is turned off.
- NB
- --
- The validity of the above notes depends on the implementation
- details of Mul and flatten which may change at any time. Therefore,
- you should only consider them when your code is highly performance
- sensitive.
- Removal of 1 from the sequence is already handled by AssocOp.__new__.
- """
- from sympy.calculus.accumulationbounds import AccumBounds
- from sympy.matrices.expressions import MatrixExpr
- rv = None
- if len(seq) == 2:
- a, b = seq
- if b.is_Rational:
- a, b = b, a
- seq = [a, b]
- assert a is not S.One
- if not a.is_zero and a.is_Rational:
- r, b = b.as_coeff_Mul()
- if b.is_Add:
- if r is not S.One: # 2-arg hack
- # leave the Mul as a Mul?
- ar = a*r
- if ar is S.One:
- arb = b
- else:
- arb = cls(a*r, b, evaluate=False)
- rv = [arb], [], None
- elif global_parameters.distribute and b.is_commutative:
- newb = Add(*[_keep_coeff(a, bi) for bi in b.args])
- rv = [newb], [], None
- if rv:
- return rv
- # apply associativity, separate commutative part of seq
- c_part = [] # out: commutative factors
- nc_part = [] # out: non-commutative factors
- nc_seq = []
- coeff = S.One # standalone term
- # e.g. 3 * ...
- c_powers = [] # (base,exp) n
- # e.g. (x,n) for x
- num_exp = [] # (num-base, exp) y
- # e.g. (3, y) for ... * 3 * ...
- neg1e = S.Zero # exponent on -1 extracted from Number-based Pow and I
- pnum_rat = {} # (num-base, Rat-exp) 1/2
- # e.g. (3, 1/2) for ... * 3 * ...
- order_symbols = None
- # --- PART 1 ---
- #
- # "collect powers and coeff":
- #
- # o coeff
- # o c_powers
- # o num_exp
- # o neg1e
- # o pnum_rat
- #
- # NOTE: this is optimized for all-objects-are-commutative case
- for o in seq:
- # O(x)
- if o.is_Order:
- o, order_symbols = o.as_expr_variables(order_symbols)
- # Mul([...])
- if o.is_Mul:
- if o.is_commutative:
- seq.extend(o.args) # XXX zerocopy?
- else:
- # NCMul can have commutative parts as well
- for q in o.args:
- if q.is_commutative:
- seq.append(q)
- else:
- nc_seq.append(q)
- # append non-commutative marker, so we don't forget to
- # process scheduled non-commutative objects
- seq.append(NC_Marker)
- continue
- # 3
- elif o.is_Number:
- if o is S.NaN or coeff is S.ComplexInfinity and o.is_zero:
- # we know for sure the result will be nan
- return [S.NaN], [], None
- elif coeff.is_Number or isinstance(coeff, AccumBounds): # it could be zoo
- coeff *= o
- if coeff is S.NaN:
- # we know for sure the result will be nan
- return [S.NaN], [], None
- continue
- elif isinstance(o, AccumBounds):
- coeff = o.__mul__(coeff)
- continue
- elif o is S.ComplexInfinity:
- if not coeff:
- # 0 * zoo = NaN
- return [S.NaN], [], None
- coeff = S.ComplexInfinity
- continue
- elif o is S.ImaginaryUnit:
- neg1e += S.Half
- continue
- elif o.is_commutative:
- # e
- # o = b
- b, e = o.as_base_exp()
- # y
- # 3
- if o.is_Pow:
- if b.is_Number:
- # get all the factors with numeric base so they can be
- # combined below, but don't combine negatives unless
- # the exponent is an integer
- if e.is_Rational:
- if e.is_Integer:
- coeff *= Pow(b, e) # it is an unevaluated power
- continue
- elif e.is_negative: # also a sign of an unevaluated power
- seq.append(Pow(b, e))
- continue
- elif b.is_negative:
- neg1e += e
- b = -b
- if b is not S.One:
- pnum_rat.setdefault(b, []).append(e)
- continue
- elif b.is_positive or e.is_integer:
- num_exp.append((b, e))
- continue
- c_powers.append((b, e))
- # NON-COMMUTATIVE
- # TODO: Make non-commutative exponents not combine automatically
- else:
- if o is not NC_Marker:
- nc_seq.append(o)
- # process nc_seq (if any)
- while nc_seq:
- o = nc_seq.pop(0)
- if not nc_part:
- nc_part.append(o)
- continue
- # b c b+c
- # try to combine last terms: a * a -> a
- o1 = nc_part.pop()
- b1, e1 = o1.as_base_exp()
- b2, e2 = o.as_base_exp()
- new_exp = e1 + e2
- # Only allow powers to combine if the new exponent is
- # not an Add. This allow things like a**2*b**3 == a**5
- # if a.is_commutative == False, but prohibits
- # a**x*a**y and x**a*x**b from combining (x,y commute).
- if b1 == b2 and (not new_exp.is_Add):
- o12 = b1 ** new_exp
- # now o12 could be a commutative object
- if o12.is_commutative:
- seq.append(o12)
- continue
- else:
- nc_seq.insert(0, o12)
- else:
- nc_part.extend([o1, o])
- # We do want a combined exponent if it would not be an Add, such as
- # y 2y 3y
- # x * x -> x
- # We determine if two exponents have the same term by using
- # as_coeff_Mul.
- #
- # Unfortunately, this isn't smart enough to consider combining into
- # exponents that might already be adds, so things like:
- # z - y y
- # x * x will be left alone. This is because checking every possible
- # combination can slow things down.
- # gather exponents of common bases...
- def _gather(c_powers):
- common_b = {} # b:e
- for b, e in c_powers:
- co = e.as_coeff_Mul()
- common_b.setdefault(b, {}).setdefault(
- co[1], []).append(co[0])
- for b, d in common_b.items():
- for di, li in d.items():
- d[di] = Add(*li)
- new_c_powers = []
- for b, e in common_b.items():
- new_c_powers.extend([(b, c*t) for t, c in e.items()])
- return new_c_powers
- # in c_powers
- c_powers = _gather(c_powers)
- # and in num_exp
- num_exp = _gather(num_exp)
- # --- PART 2 ---
- #
- # o process collected powers (x**0 -> 1; x**1 -> x; otherwise Pow)
- # o combine collected powers (2**x * 3**x -> 6**x)
- # with numeric base
- # ................................
- # now we have:
- # - coeff:
- # - c_powers: (b, e)
- # - num_exp: (2, e)
- # - pnum_rat: {(1/3, [1/3, 2/3, 1/4])}
- # 0 1
- # x -> 1 x -> x
- # this should only need to run twice; if it fails because
- # it needs to be run more times, perhaps this should be
- # changed to a "while True" loop -- the only reason it
- # isn't such now is to allow a less-than-perfect result to
- # be obtained rather than raising an error or entering an
- # infinite loop
- for i in range(2):
- new_c_powers = []
- changed = False
- for b, e in c_powers:
- if e.is_zero:
- # canceling out infinities yields NaN
- if (b.is_Add or b.is_Mul) and any(infty in b.args
- for infty in (S.ComplexInfinity, S.Infinity,
- S.NegativeInfinity)):
- return [S.NaN], [], None
- continue
- if e is S.One:
- if b.is_Number:
- coeff *= b
- continue
- p = b
- if e is not S.One:
- p = Pow(b, e)
- # check to make sure that the base doesn't change
- # after exponentiation; to allow for unevaluated
- # Pow, we only do so if b is not already a Pow
- if p.is_Pow and not b.is_Pow:
- bi = b
- b, e = p.as_base_exp()
- if b != bi:
- changed = True
- c_part.append(p)
- new_c_powers.append((b, e))
- # there might have been a change, but unless the base
- # matches some other base, there is nothing to do
- if changed and len({
- b for b, e in new_c_powers}) != len(new_c_powers):
- # start over again
- c_part = []
- c_powers = _gather(new_c_powers)
- else:
- break
- # x x x
- # 2 * 3 -> 6
- inv_exp_dict = {} # exp:Mul(num-bases) x x
- # e.g. x:6 for ... * 2 * 3 * ...
- for b, e in num_exp:
- inv_exp_dict.setdefault(e, []).append(b)
- for e, b in inv_exp_dict.items():
- inv_exp_dict[e] = cls(*b)
- c_part.extend([Pow(b, e) for e, b in inv_exp_dict.items() if e])
- # b, e -> e' = sum(e), b
- # {(1/5, [1/3]), (1/2, [1/12, 1/4]} -> {(1/3, [1/5, 1/2])}
- comb_e = {}
- for b, e in pnum_rat.items():
- comb_e.setdefault(Add(*e), []).append(b)
- del pnum_rat
- # process them, reducing exponents to values less than 1
- # and updating coeff if necessary else adding them to
- # num_rat for further processing
- num_rat = []
- for e, b in comb_e.items():
- b = cls(*b)
- if e.q == 1:
- coeff *= Pow(b, e)
- continue
- if e.p > e.q:
- e_i, ep = divmod(e.p, e.q)
- coeff *= Pow(b, e_i)
- e = Rational(ep, e.q)
- num_rat.append((b, e))
- del comb_e
- # extract gcd of bases in num_rat
- # 2**(1/3)*6**(1/4) -> 2**(1/3+1/4)*3**(1/4)
- pnew = defaultdict(list)
- i = 0 # steps through num_rat which may grow
- while i < len(num_rat):
- bi, ei = num_rat[i]
- grow = []
- for j in range(i + 1, len(num_rat)):
- bj, ej = num_rat[j]
- g = bi.gcd(bj)
- if g is not S.One:
- # 4**r1*6**r2 -> 2**(r1+r2) * 2**r1 * 3**r2
- # this might have a gcd with something else
- e = ei + ej
- if e.q == 1:
- coeff *= Pow(g, e)
- else:
- if e.p > e.q:
- e_i, ep = divmod(e.p, e.q) # change e in place
- coeff *= Pow(g, e_i)
- e = Rational(ep, e.q)
- grow.append((g, e))
- # update the jth item
- num_rat[j] = (bj/g, ej)
- # update bi that we are checking with
- bi = bi/g
- if bi is S.One:
- break
- if bi is not S.One:
- obj = Pow(bi, ei)
- if obj.is_Number:
- coeff *= obj
- else:
- # changes like sqrt(12) -> 2*sqrt(3)
- for obj in Mul.make_args(obj):
- if obj.is_Number:
- coeff *= obj
- else:
- assert obj.is_Pow
- bi, ei = obj.args
- pnew[ei].append(bi)
- num_rat.extend(grow)
- i += 1
- # combine bases of the new powers
- for e, b in pnew.items():
- pnew[e] = cls(*b)
- # handle -1 and I
- if neg1e:
- # treat I as (-1)**(1/2) and compute -1's total exponent
- p, q = neg1e.as_numer_denom()
- # if the integer part is odd, extract -1
- n, p = divmod(p, q)
- if n % 2:
- coeff = -coeff
- # if it's a multiple of 1/2 extract I
- if q == 2:
- c_part.append(S.ImaginaryUnit)
- elif p:
- # see if there is any positive base this power of
- # -1 can join
- neg1e = Rational(p, q)
- for e, b in pnew.items():
- if e == neg1e and b.is_positive:
- pnew[e] = -b
- break
- else:
- # keep it separate; we've already evaluated it as
- # much as possible so evaluate=False
- c_part.append(Pow(S.NegativeOne, neg1e, evaluate=False))
- # add all the pnew powers
- c_part.extend([Pow(b, e) for e, b in pnew.items()])
- # oo, -oo
- if coeff in (S.Infinity, S.NegativeInfinity):
- def _handle_for_oo(c_part, coeff_sign):
- new_c_part = []
- for t in c_part:
- if t.is_extended_positive:
- continue
- if t.is_extended_negative:
- coeff_sign *= -1
- continue
- new_c_part.append(t)
- return new_c_part, coeff_sign
- c_part, coeff_sign = _handle_for_oo(c_part, 1)
- nc_part, coeff_sign = _handle_for_oo(nc_part, coeff_sign)
- coeff *= coeff_sign
- # zoo
- if coeff is S.ComplexInfinity:
- # zoo might be
- # infinite_real + bounded_im
- # bounded_real + infinite_im
- # infinite_real + infinite_im
- # and non-zero real or imaginary will not change that status.
- c_part = [c for c in c_part if not (fuzzy_not(c.is_zero) and
- c.is_extended_real is not None)]
- nc_part = [c for c in nc_part if not (fuzzy_not(c.is_zero) and
- c.is_extended_real is not None)]
- # 0
- elif coeff.is_zero:
- # we know for sure the result will be 0 except the multiplicand
- # is infinity or a matrix
- if any(isinstance(c, MatrixExpr) for c in nc_part):
- return [coeff], nc_part, order_symbols
- if any(c.is_finite == False for c in c_part):
- return [S.NaN], [], order_symbols
- return [coeff], [], order_symbols
- # check for straggling Numbers that were produced
- _new = []
- for i in c_part:
- if i.is_Number:
- coeff *= i
- else:
- _new.append(i)
- c_part = _new
- # order commutative part canonically
- _mulsort(c_part)
- # current code expects coeff to be always in slot-0
- if coeff is not S.One:
- c_part.insert(0, coeff)
- # we are done
- if (global_parameters.distribute and not nc_part and len(c_part) == 2 and
- c_part[0].is_Number and c_part[0].is_finite and c_part[1].is_Add):
- # 2*(1+a) -> 2 + 2 * a
- coeff = c_part[0]
- c_part = [Add(*[coeff*f for f in c_part[1].args])]
- return c_part, nc_part, order_symbols
- def _eval_power(self, e):
- # don't break up NC terms: (A*B)**3 != A**3*B**3, it is A*B*A*B*A*B
- cargs, nc = self.args_cnc(split_1=False)
- if e.is_Integer:
- return Mul(*[Pow(b, e, evaluate=False) for b in cargs]) * \
- Pow(Mul._from_args(nc), e, evaluate=False)
- if e.is_Rational and e.q == 2:
- if self.is_imaginary:
- a = self.as_real_imag()[1]
- if a.is_Rational:
- from .power import integer_nthroot
- n, d = abs(a/2).as_numer_denom()
- n, t = integer_nthroot(n, 2)
- if t:
- d, t = integer_nthroot(d, 2)
- if t:
- from sympy.functions.elementary.complexes import sign
- r = sympify(n)/d
- return _unevaluated_Mul(r**e.p, (1 + sign(a)*S.ImaginaryUnit)**e.p)
- p = Pow(self, e, evaluate=False)
- if e.is_Rational or e.is_Float:
- return p._eval_expand_power_base()
- return p
- @classmethod
- def class_key(cls):
- return 3, 0, cls.__name__
- def _eval_evalf(self, prec):
- c, m = self.as_coeff_Mul()
- if c is S.NegativeOne:
- if m.is_Mul:
- rv = -AssocOp._eval_evalf(m, prec)
- else:
- mnew = m._eval_evalf(prec)
- if mnew is not None:
- m = mnew
- rv = -m
- else:
- rv = AssocOp._eval_evalf(self, prec)
- if rv.is_number:
- return rv.expand()
- return rv
- @property
- def _mpc_(self):
- """
- Convert self to an mpmath mpc if possible
- """
- from .numbers import Float
- im_part, imag_unit = self.as_coeff_Mul()
- if imag_unit is not 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 Mul to mpc. Must be of the form Number*I")
- return (Float(0)._mpf_, Float(im_part)._mpf_)
- @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_mul() which gives the head and a tuple containing
- the arguments of the tail when treated as a Mul.
- - if you want the coefficient when self is treated as an Add
- then use self.as_coeff_add()[0]
- Examples
- ========
- >>> from sympy.abc import x, y
- >>> (3*x*y).as_two_terms()
- (3, x*y)
- """
- args = self.args
- if len(args) == 1:
- return S.One, self
- elif len(args) == 2:
- return args
- else:
- return args[0], self._new_rawargs(*args[1:])
- @cacheit
- def as_coeff_mul(self, *deps, rational=True, **kwargs):
- if deps:
- l1, l2 = sift(self.args, lambda x: x.has(*deps), binary=True)
- return self._new_rawargs(*l2), tuple(l1)
- args = self.args
- if args[0].is_Number:
- if not rational or args[0].is_Rational:
- return args[0], args[1:]
- elif args[0].is_extended_negative:
- return S.NegativeOne, (-args[0],) + args[1:]
- return S.One, args
- def as_coeff_Mul(self, rational=False):
- """
- Efficiently extract the coefficient of a product.
- """
- coeff, args = self.args[0], self.args[1:]
- if coeff.is_Number:
- if not rational or coeff.is_Rational:
- if len(args) == 1:
- return coeff, args[0]
- else:
- return coeff, self._new_rawargs(*args)
- elif coeff.is_extended_negative:
- return S.NegativeOne, self._new_rawargs(*((-coeff,) + args))
- return S.One, self
- def as_real_imag(self, deep=True, **hints):
- from sympy.functions.elementary.complexes import Abs, im, re
- other = []
- coeffr = []
- coeffi = []
- addterms = S.One
- for a in self.args:
- r, i = a.as_real_imag()
- if i.is_zero:
- coeffr.append(r)
- elif r.is_zero:
- coeffi.append(i*S.ImaginaryUnit)
- elif a.is_commutative:
- aconj = a.conjugate() if other else None
- # search for complex conjugate pairs:
- for i, x in enumerate(other):
- if x == aconj:
- coeffr.append(Abs(x)**2)
- del other[i]
- break
- else:
- if a.is_Add:
- addterms *= a
- else:
- other.append(a)
- else:
- other.append(a)
- m = self.func(*other)
- if hints.get('ignore') == m:
- return
- if len(coeffi) % 2:
- imco = im(coeffi.pop(0))
- # all other pairs make a real factor; they will be
- # put into reco below
- else:
- imco = S.Zero
- reco = self.func(*(coeffr + coeffi))
- r, i = (reco*re(m), reco*im(m))
- if addterms == 1:
- if m == 1:
- if imco.is_zero:
- return (reco, S.Zero)
- else:
- return (S.Zero, reco*imco)
- if imco is S.Zero:
- return (r, i)
- return (-imco*i, imco*r)
- from .function import expand_mul
- addre, addim = expand_mul(addterms, deep=False).as_real_imag()
- if imco is S.Zero:
- return (r*addre - i*addim, i*addre + r*addim)
- else:
- r, i = -imco*i, imco*r
- return (r*addre - i*addim, r*addim + i*addre)
- @staticmethod
- def _expandsums(sums):
- """
- Helper function for _eval_expand_mul.
- sums must be a list of instances of Basic.
- """
- L = len(sums)
- if L == 1:
- return sums[0].args
- terms = []
- left = Mul._expandsums(sums[:L//2])
- right = Mul._expandsums(sums[L//2:])
- terms = [Mul(a, b) for a in left for b in right]
- added = Add(*terms)
- return Add.make_args(added) # it may have collapsed down to one term
- def _eval_expand_mul(self, **hints):
- from sympy.simplify.radsimp import fraction
- # Handle things like 1/(x*(x + 1)), which are automatically converted
- # to 1/x*1/(x + 1)
- expr = self
- n, d = fraction(expr)
- if d.is_Mul:
- n, d = [i._eval_expand_mul(**hints) if i.is_Mul else i
- for i in (n, d)]
- expr = n/d
- if not expr.is_Mul:
- return expr
- plain, sums, rewrite = [], [], False
- for factor in expr.args:
- if factor.is_Add:
- sums.append(factor)
- rewrite = True
- else:
- if factor.is_commutative:
- plain.append(factor)
- else:
- sums.append(Basic(factor)) # Wrapper
- if not rewrite:
- return expr
- else:
- plain = self.func(*plain)
- if sums:
- deep = hints.get("deep", False)
- terms = self.func._expandsums(sums)
- args = []
- for term in terms:
- t = self.func(plain, term)
- if t.is_Mul and any(a.is_Add for a in t.args) and deep:
- t = t._eval_expand_mul()
- args.append(t)
- return Add(*args)
- else:
- return plain
- @cacheit
- def _eval_derivative(self, s):
- args = list(self.args)
- terms = []
- for i in range(len(args)):
- d = args[i].diff(s)
- if d:
- # Note: reduce is used in step of Mul as Mul is unable to
- # handle subtypes and operation priority:
- terms.append(reduce(lambda x, y: x*y, (args[:i] + [d] + args[i + 1:]), S.One))
- return Add.fromiter(terms)
- @cacheit
- def _eval_derivative_n_times(self, s, n):
- from .function import AppliedUndef
- from .symbol import Symbol, symbols, Dummy
- if not isinstance(s, (AppliedUndef, Symbol)):
- # other types of s may not be well behaved, e.g.
- # (cos(x)*sin(y)).diff([[x, y, z]])
- return super()._eval_derivative_n_times(s, n)
- from .numbers import Integer
- args = self.args
- m = len(args)
- if isinstance(n, (int, Integer)):
- # https://en.wikipedia.org/wiki/General_Leibniz_rule#More_than_two_factors
- terms = []
- from sympy.ntheory.multinomial import multinomial_coefficients_iterator
- for kvals, c in multinomial_coefficients_iterator(m, n):
- p = Mul(*[arg.diff((s, k)) for k, arg in zip(kvals, args)])
- terms.append(c * p)
- return Add(*terms)
- from sympy.concrete.summations import Sum
- from sympy.functions.combinatorial.factorials import factorial
- from sympy.functions.elementary.miscellaneous import Max
- kvals = symbols("k1:%i" % m, cls=Dummy)
- klast = n - sum(kvals)
- nfact = factorial(n)
- e, l = (# better to use the multinomial?
- nfact/prod(map(factorial, kvals))/factorial(klast)*\
- Mul(*[args[t].diff((s, kvals[t])) for t in range(m-1)])*\
- args[-1].diff((s, Max(0, klast))),
- [(k, 0, n) for k in kvals])
- return Sum(e, *l)
- def _eval_difference_delta(self, n, step):
- from sympy.series.limitseq import difference_delta as dd
- arg0 = self.args[0]
- rest = Mul(*self.args[1:])
- return (arg0.subs(n, n + step) * dd(rest, n, step) + dd(arg0, n, step) *
- rest)
- def _matches_simple(self, expr, repl_dict):
- # handle (w*3).matches('x*5') -> {w: x*5/3}
- coeff, terms = self.as_coeff_Mul()
- terms = Mul.make_args(terms)
- if len(terms) == 1:
- newexpr = self.__class__._combine_inverse(expr, coeff)
- return terms[0].matches(newexpr, repl_dict)
- return
- def matches(self, expr, repl_dict=None, old=False):
- expr = sympify(expr)
- if self.is_commutative and expr.is_commutative:
- return self._matches_commutative(expr, repl_dict, old)
- elif self.is_commutative is not expr.is_commutative:
- return None
- # Proceed only if both both expressions are non-commutative
- c1, nc1 = self.args_cnc()
- c2, nc2 = expr.args_cnc()
- c1, c2 = [c or [1] for c in [c1, c2]]
- # TODO: Should these be self.func?
- comm_mul_self = Mul(*c1)
- comm_mul_expr = Mul(*c2)
- repl_dict = comm_mul_self.matches(comm_mul_expr, repl_dict, old)
- # If the commutative arguments didn't match and aren't equal, then
- # then the expression as a whole doesn't match
- if not repl_dict and c1 != c2:
- return None
- # Now match the non-commutative arguments, expanding powers to
- # multiplications
- nc1 = Mul._matches_expand_pows(nc1)
- nc2 = Mul._matches_expand_pows(nc2)
- repl_dict = Mul._matches_noncomm(nc1, nc2, repl_dict)
- return repl_dict or None
- @staticmethod
- def _matches_expand_pows(arg_list):
- new_args = []
- for arg in arg_list:
- if arg.is_Pow and arg.exp > 0:
- new_args.extend([arg.base] * arg.exp)
- else:
- new_args.append(arg)
- return new_args
- @staticmethod
- def _matches_noncomm(nodes, targets, repl_dict=None):
- """Non-commutative multiplication matcher.
- `nodes` is a list of symbols within the matcher multiplication
- expression, while `targets` is a list of arguments in the
- multiplication expression being matched against.
- """
- if repl_dict is None:
- repl_dict = {}
- else:
- repl_dict = repl_dict.copy()
- # List of possible future states to be considered
- agenda = []
- # The current matching state, storing index in nodes and targets
- state = (0, 0)
- node_ind, target_ind = state
- # Mapping between wildcard indices and the index ranges they match
- wildcard_dict = {}
- while target_ind < len(targets) and node_ind < len(nodes):
- node = nodes[node_ind]
- if node.is_Wild:
- Mul._matches_add_wildcard(wildcard_dict, state)
- states_matches = Mul._matches_new_states(wildcard_dict, state,
- nodes, targets)
- if states_matches:
- new_states, new_matches = states_matches
- agenda.extend(new_states)
- if new_matches:
- for match in new_matches:
- repl_dict[match] = new_matches[match]
- if not agenda:
- return None
- else:
- state = agenda.pop()
- node_ind, target_ind = state
- return repl_dict
- @staticmethod
- def _matches_add_wildcard(dictionary, state):
- node_ind, target_ind = state
- if node_ind in dictionary:
- begin, end = dictionary[node_ind]
- dictionary[node_ind] = (begin, target_ind)
- else:
- dictionary[node_ind] = (target_ind, target_ind)
- @staticmethod
- def _matches_new_states(dictionary, state, nodes, targets):
- node_ind, target_ind = state
- node = nodes[node_ind]
- target = targets[target_ind]
- # Don't advance at all if we've exhausted the targets but not the nodes
- if target_ind >= len(targets) - 1 and node_ind < len(nodes) - 1:
- return None
- if node.is_Wild:
- match_attempt = Mul._matches_match_wilds(dictionary, node_ind,
- nodes, targets)
- if match_attempt:
- # If the same node has been matched before, don't return
- # anything if the current match is diverging from the previous
- # match
- other_node_inds = Mul._matches_get_other_nodes(dictionary,
- nodes, node_ind)
- for ind in other_node_inds:
- other_begin, other_end = dictionary[ind]
- curr_begin, curr_end = dictionary[node_ind]
- other_targets = targets[other_begin:other_end + 1]
- current_targets = targets[curr_begin:curr_end + 1]
- for curr, other in zip(current_targets, other_targets):
- if curr != other:
- return None
- # A wildcard node can match more than one target, so only the
- # target index is advanced
- new_state = [(node_ind, target_ind + 1)]
- # Only move on to the next node if there is one
- if node_ind < len(nodes) - 1:
- new_state.append((node_ind + 1, target_ind + 1))
- return new_state, match_attempt
- else:
- # If we're not at a wildcard, then make sure we haven't exhausted
- # nodes but not targets, since in this case one node can only match
- # one target
- if node_ind >= len(nodes) - 1 and target_ind < len(targets) - 1:
- return None
- match_attempt = node.matches(target)
- if match_attempt:
- return [(node_ind + 1, target_ind + 1)], match_attempt
- elif node == target:
- return [(node_ind + 1, target_ind + 1)], None
- else:
- return None
- @staticmethod
- def _matches_match_wilds(dictionary, wildcard_ind, nodes, targets):
- """Determine matches of a wildcard with sub-expression in `target`."""
- wildcard = nodes[wildcard_ind]
- begin, end = dictionary[wildcard_ind]
- terms = targets[begin:end + 1]
- # TODO: Should this be self.func?
- mult = Mul(*terms) if len(terms) > 1 else terms[0]
- return wildcard.matches(mult)
- @staticmethod
- def _matches_get_other_nodes(dictionary, nodes, node_ind):
- """Find other wildcards that may have already been matched."""
- ind_node = nodes[node_ind]
- return [ind for ind in dictionary if nodes[ind] == ind_node]
- @staticmethod
- def _combine_inverse(lhs, rhs):
- """
- Returns lhs/rhs, but treats arguments like symbols, so things
- like oo/oo return 1 (instead of a nan) and ``I`` behaves like
- a symbol instead of sqrt(-1).
- """
- from sympy.simplify.simplify import signsimp
- from .symbol import Dummy
- if lhs == rhs:
- return S.One
- def check(l, r):
- if l.is_Float and r.is_comparable:
- # if both objects are added to 0 they will share the same "normalization"
- # and are more likely to compare the same. Since Add(foo, 0) will not allow
- # the 0 to pass, we use __add__ directly.
- return l.__add__(0) == r.evalf().__add__(0)
- return False
- if check(lhs, rhs) or check(rhs, lhs):
- return S.One
- if any(i.is_Pow or i.is_Mul for i in (lhs, rhs)):
- # gruntz and limit wants a literal I to not combine
- # with a power of -1
- d = Dummy('I')
- _i = {S.ImaginaryUnit: d}
- i_ = {d: S.ImaginaryUnit}
- a = lhs.xreplace(_i).as_powers_dict()
- b = rhs.xreplace(_i).as_powers_dict()
- blen = len(b)
- for bi in tuple(b.keys()):
- if bi in a:
- a[bi] -= b.pop(bi)
- if not a[bi]:
- a.pop(bi)
- if len(b) != blen:
- lhs = Mul(*[k**v for k, v in a.items()]).xreplace(i_)
- rhs = Mul(*[k**v for k, v in b.items()]).xreplace(i_)
- rv = lhs/rhs
- srv = signsimp(rv)
- return srv if srv.is_Number else rv
- def as_powers_dict(self):
- d = defaultdict(int)
- for term in self.args:
- for b, e in term.as_powers_dict().items():
- d[b] += e
- return d
- def as_numer_denom(self):
- # don't use _from_args to rebuild the numerators and denominators
- # as the order is not guaranteed to be the same once they have
- # been separated from each other
- numers, denoms = list(zip(*[f.as_numer_denom() for f in self.args]))
- return self.func(*numers), self.func(*denoms)
- def as_base_exp(self):
- e1 = None
- bases = []
- nc = 0
- for m in self.args:
- b, e = m.as_base_exp()
- if not b.is_commutative:
- nc += 1
- if e1 is None:
- e1 = e
- elif e != e1 or nc > 1:
- return self, S.One
- bases.append(b)
- return self.func(*bases), e1
- 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_commutative = lambda self: _fuzzy_group(
- a.is_commutative for a in self.args)
- def _eval_is_complex(self):
- comp = _fuzzy_group(a.is_complex for a in self.args)
- if comp is False:
- if any(a.is_infinite for a in self.args):
- if any(a.is_zero is not False for a in self.args):
- return None
- return False
- return comp
- def _eval_is_zero_infinite_helper(self):
- #
- # Helper used by _eval_is_zero and _eval_is_infinite.
- #
- # Three-valued logic is tricky so let us reason this carefully. It
- # would be nice to say that we just check is_zero/is_infinite in all
- # args but we need to be careful about the case that one arg is zero
- # and another is infinite like Mul(0, oo) or more importantly a case
- # where it is not known if the arguments are zero or infinite like
- # Mul(y, 1/x). If either y or x could be zero then there is a
- # *possibility* that we have Mul(0, oo) which should give None for both
- # is_zero and is_infinite.
- #
- # We keep track of whether we have seen a zero or infinity but we also
- # need to keep track of whether we have *possibly* seen one which
- # would be indicated by None.
- #
- # For each argument there is the possibility that is_zero might give
- # True, False or None and likewise that is_infinite might give True,
- # False or None, giving 9 combinations. The True cases for is_zero and
- # is_infinite are mutually exclusive though so there are 3 main cases:
- #
- # - is_zero = True
- # - is_infinite = True
- # - is_zero and is_infinite are both either False or None
- #
- # At the end seen_zero and seen_infinite can be any of 9 combinations
- # of True/False/None. Unless one is False though we cannot return
- # anything except None:
- #
- # - is_zero=True needs seen_zero=True and seen_infinite=False
- # - is_zero=False needs seen_zero=False
- # - is_infinite=True needs seen_infinite=True and seen_zero=False
- # - is_infinite=False needs seen_infinite=False
- # - anything else gives both is_zero=None and is_infinite=None
- #
- # The loop only sets the flags to True or None and never back to False.
- # Hence as soon as neither flag is False we exit early returning None.
- # In particular as soon as we encounter a single arg that has
- # is_zero=is_infinite=None we exit. This is a common case since it is
- # the default assumptions for a Symbol and also the case for most
- # expressions containing such a symbol. The early exit gives a big
- # speedup for something like Mul(*symbols('x:1000')).is_zero.
- #
- seen_zero = seen_infinite = False
- for a in self.args:
- if a.is_zero:
- if seen_infinite is not False:
- return None, None
- seen_zero = True
- elif a.is_infinite:
- if seen_zero is not False:
- return None, None
- seen_infinite = True
- else:
- if seen_zero is False and a.is_zero is None:
- if seen_infinite is not False:
- return None, None
- seen_zero = None
- if seen_infinite is False and a.is_infinite is None:
- if seen_zero is not False:
- return None, None
- seen_infinite = None
- return seen_zero, seen_infinite
- def _eval_is_zero(self):
- # True iff any arg is zero and no arg is infinite but need to handle
- # three valued logic carefully.
- seen_zero, seen_infinite = self._eval_is_zero_infinite_helper()
- if seen_zero is False:
- return False
- elif seen_zero is True and seen_infinite is False:
- return True
- else:
- return None
- def _eval_is_infinite(self):
- # True iff any arg is infinite and no arg is zero but need to handle
- # three valued logic carefully.
- seen_zero, seen_infinite = self._eval_is_zero_infinite_helper()
- if seen_infinite is True and seen_zero is False:
- return True
- elif seen_infinite is False:
- return False
- else:
- return None
- # We do not need to implement _eval_is_finite because the assumptions
- # system can infer it from finite = not infinite.
- def _eval_is_rational(self):
- r = _fuzzy_group((a.is_rational for a in self.args), quick_exit=True)
- if r:
- return r
- elif r is False:
- # All args except one are rational
- if all(a.is_zero is False for a in self.args):
- return False
- def _eval_is_algebraic(self):
- r = _fuzzy_group((a.is_algebraic for a in self.args), quick_exit=True)
- if r:
- return r
- elif r is False:
- # All args except one are algebraic
- if all(a.is_zero is False for a in self.args):
- return False
- # without involving odd/even checks this code would suffice:
- #_eval_is_integer = lambda self: _fuzzy_group(
- # (a.is_integer for a in self.args), quick_exit=True)
- def _eval_is_integer(self):
- from sympy.ntheory.factor_ import trailing
- is_rational = self._eval_is_rational()
- if is_rational is False:
- return False
- numerators = []
- denominators = []
- unknown = False
- for a in self.args:
- hit = False
- if a.is_integer:
- if abs(a) is not S.One:
- numerators.append(a)
- elif a.is_Rational:
- n, d = a.as_numer_denom()
- if abs(n) is not S.One:
- numerators.append(n)
- if d is not S.One:
- denominators.append(d)
- elif a.is_Pow:
- b, e = a.as_base_exp()
- if not b.is_integer or not e.is_integer:
- hit = unknown = True
- if e.is_negative:
- denominators.append(2 if a is S.Half else
- Pow(a, S.NegativeOne))
- elif not hit:
- # int b and pos int e: a = b**e is integer
- assert not e.is_positive
- # for rational self and e equal to zero: a = b**e is 1
- assert not e.is_zero
- return # sign of e unknown -> self.is_integer unknown
- else:
- # x**2, 2**x, or x**y with x and y int-unknown -> unknown
- return
- else:
- return
- if not denominators and not unknown:
- return True
- allodd = lambda x: all(i.is_odd for i in x)
- alleven = lambda x: all(i.is_even for i in x)
- anyeven = lambda x: any(i.is_even for i in x)
- from .relational import is_gt
- if not numerators and denominators and all(
- is_gt(_, S.One) for _ in denominators):
- return False
- elif unknown:
- return
- elif allodd(numerators) and anyeven(denominators):
- return False
- elif anyeven(numerators) and denominators == [2]:
- return True
- elif alleven(numerators) and allodd(denominators
- ) and (Mul(*denominators, evaluate=False) - 1
- ).is_positive:
- return False
- if len(denominators) == 1:
- d = denominators[0]
- if d.is_Integer and d.is_even:
- # if minimal power of 2 in num vs den is not
- # negative then we have an integer
- if (Add(*[i.as_base_exp()[1] for i in
- numerators if i.is_even]) - trailing(d.p)
- ).is_nonnegative:
- return True
- if len(numerators) == 1:
- n = numerators[0]
- if n.is_Integer and n.is_even:
- # if minimal power of 2 in den vs num is positive
- # then we have have a non-integer
- if (Add(*[i.as_base_exp()[1] for i in
- denominators if i.is_even]) - trailing(n.p)
- ).is_positive:
- return False
- def _eval_is_polar(self):
- has_polar = any(arg.is_polar for arg in self.args)
- return has_polar and \
- all(arg.is_polar or arg.is_positive for arg in self.args)
- def _eval_is_extended_real(self):
- return self._eval_real_imag(True)
- def _eval_real_imag(self, real):
- zero = False
- t_not_re_im = None
- for t in self.args:
- if (t.is_complex or t.is_infinite) is False and t.is_extended_real is False:
- return False
- elif t.is_imaginary: # I
- real = not real
- elif t.is_extended_real: # 2
- if not zero:
- z = t.is_zero
- if not z and zero is False:
- zero = z
- elif z:
- if all(a.is_finite for a in self.args):
- return True
- return
- elif t.is_extended_real is False:
- # symbolic or literal like `2 + I` or symbolic imaginary
- if t_not_re_im:
- return # complex terms might cancel
- t_not_re_im = t
- elif t.is_imaginary is False: # symbolic like `2` or `2 + I`
- if t_not_re_im:
- return # complex terms might cancel
- t_not_re_im = t
- else:
- return
- if t_not_re_im:
- if t_not_re_im.is_extended_real is False:
- if real: # like 3
- return zero # 3*(smthng like 2 + I or i) is not real
- if t_not_re_im.is_imaginary is False: # symbolic 2 or 2 + I
- if not real: # like I
- return zero # I*(smthng like 2 or 2 + I) is not real
- elif zero is False:
- return real # can't be trumped by 0
- elif real:
- return real # doesn't matter what zero is
- def _eval_is_imaginary(self):
- if all(a.is_zero is False and a.is_finite for a in self.args):
- return self._eval_real_imag(False)
- def _eval_is_hermitian(self):
- return self._eval_herm_antiherm(True)
- def _eval_is_antihermitian(self):
- return self._eval_herm_antiherm(False)
- def _eval_herm_antiherm(self, herm):
- for t in self.args:
- if t.is_hermitian is None or t.is_antihermitian is None:
- return
- if t.is_hermitian:
- continue
- elif t.is_antihermitian:
- herm = not herm
- else:
- return
- if herm is not False:
- return herm
- is_zero = self._eval_is_zero()
- if is_zero:
- return True
- elif is_zero is False:
- return herm
- 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 and fuzzy_not(x.is_zero)) is True for x in others):
- return True
- return
- if a is None:
- return
- if all(x.is_real for x in self.args):
- return False
- def _eval_is_extended_positive(self):
- """Return True if self is positive, False if not, and None if it
- cannot be determined.
- Explanation
- ===========
- This algorithm is non-recursive and works by keeping track of the
- sign which changes when a negative or nonpositive is encountered.
- Whether a nonpositive or nonnegative is seen is also tracked since
- the presence of these makes it impossible to return True, but
- possible to return False if the end result is nonpositive. e.g.
- pos * neg * nonpositive -> pos or zero -> None is returned
- pos * neg * nonnegative -> neg or zero -> False is returned
- """
- return self._eval_pos_neg(1)
- def _eval_pos_neg(self, sign):
- saw_NON = saw_NOT = False
- for t in self.args:
- if t.is_extended_positive:
- continue
- elif t.is_extended_negative:
- sign = -sign
- elif t.is_zero:
- if all(a.is_finite for a in self.args):
- return False
- return
- elif t.is_extended_nonpositive:
- sign = -sign
- saw_NON = True
- elif t.is_extended_nonnegative:
- saw_NON = True
- # FIXME: is_positive/is_negative is False doesn't take account of
- # Symbol('x', infinite=True, extended_real=True) which has
- # e.g. is_positive is False but has uncertain sign.
- elif t.is_positive is False:
- sign = -sign
- if saw_NOT:
- return
- saw_NOT = True
- elif t.is_negative is False:
- if saw_NOT:
- return
- saw_NOT = True
- else:
- return
- if sign == 1 and saw_NON is False and saw_NOT is False:
- return True
- if sign < 0:
- return False
- def _eval_is_extended_negative(self):
- return self._eval_pos_neg(-1)
- def _eval_is_odd(self):
- is_integer = self._eval_is_integer()
- if is_integer is not True:
- return is_integer
- from sympy.simplify.radsimp import fraction
- n, d = fraction(self)
- if d.is_Integer and d.is_even:
- from sympy.ntheory.factor_ import trailing
- # if minimal power of 2 in num vs den is
- # positive then we have an even number
- if (Add(*[i.as_base_exp()[1] for i in
- Mul.make_args(n) if i.is_even]) - trailing(d.p)
- ).is_positive:
- return False
- return
- r, acc = True, 1
- for t in self.args:
- if abs(t) is S.One:
- continue
- if t.is_even:
- return False
- if r is False:
- pass
- elif acc != 1 and (acc + t).is_odd:
- r = False
- elif t.is_even is None:
- r = None
- acc = t
- return r
- def _eval_is_even(self):
- from sympy.simplify.radsimp import fraction
- n, d = fraction(self)
- if n.is_Integer and n.is_even:
- # if minimal power of 2 in den vs num is not
- # negative then this is not an integer and
- # can't be even
- from sympy.ntheory.factor_ import trailing
- if (Add(*[i.as_base_exp()[1] for i in
- Mul.make_args(d) if i.is_even]) - trailing(n.p)
- ).is_nonnegative:
- return False
- def _eval_is_composite(self):
- """
- Here we count the number of arguments that have a minimum value
- greater than two.
- If there are more than one of such a symbol then the result is composite.
- Else, the result cannot be determined.
- """
- number_of_args = 0 # count of symbols with minimum value greater than one
- for arg in self.args:
- if not (arg.is_integer and arg.is_positive):
- return None
- if (arg-1).is_positive:
- number_of_args += 1
- if number_of_args > 1:
- return True
- def _eval_subs(self, old, new):
- from sympy.functions.elementary.complexes import sign
- from sympy.ntheory.factor_ import multiplicity
- from sympy.simplify.powsimp import powdenest
- from sympy.simplify.radsimp import fraction
- if not old.is_Mul:
- return None
- # try keep replacement literal so -2*x doesn't replace 4*x
- if old.args[0].is_Number and old.args[0] < 0:
- if self.args[0].is_Number:
- if self.args[0] < 0:
- return self._subs(-old, -new)
- return None
- def base_exp(a):
- # if I and -1 are in a Mul, they get both end up with
- # a -1 base (see issue 6421); all we want here are the
- # true Pow or exp separated into base and exponent
- from sympy.functions.elementary.exponential import exp
- if a.is_Pow or isinstance(a, exp):
- return a.as_base_exp()
- return a, S.One
- def breakup(eq):
- """break up powers of eq when treated as a Mul:
- b**(Rational*e) -> b**e, Rational
- commutatives come back as a dictionary {b**e: Rational}
- noncommutatives come back as a list [(b**e, Rational)]
- """
- (c, nc) = (defaultdict(int), [])
- for a in Mul.make_args(eq):
- a = powdenest(a)
- (b, e) = base_exp(a)
- if e is not S.One:
- (co, _) = e.as_coeff_mul()
- b = Pow(b, e/co)
- e = co
- if a.is_commutative:
- c[b] += e
- else:
- nc.append([b, e])
- return (c, nc)
- def rejoin(b, co):
- """
- Put rational back with exponent; in general this is not ok, but
- since we took it from the exponent for analysis, it's ok to put
- it back.
- """
- (b, e) = base_exp(b)
- return Pow(b, e*co)
- def ndiv(a, b):
- """if b divides a in an extractive way (like 1/4 divides 1/2
- but not vice versa, and 2/5 does not divide 1/3) then return
- the integer number of times it divides, else return 0.
- """
- if not b.q % a.q or not a.q % b.q:
- return int(a/b)
- return 0
- # give Muls in the denominator a chance to be changed (see issue 5651)
- # rv will be the default return value
- rv = None
- n, d = fraction(self)
- self2 = self
- if d is not S.One:
- self2 = n._subs(old, new)/d._subs(old, new)
- if not self2.is_Mul:
- return self2._subs(old, new)
- if self2 != self:
- rv = self2
- # Now continue with regular substitution.
- # handle the leading coefficient and use it to decide if anything
- # should even be started; we always know where to find the Rational
- # so it's a quick test
- co_self = self2.args[0]
- co_old = old.args[0]
- co_xmul = None
- if co_old.is_Rational and co_self.is_Rational:
- # if coeffs are the same there will be no updating to do
- # below after breakup() step; so skip (and keep co_xmul=None)
- if co_old != co_self:
- co_xmul = co_self.extract_multiplicatively(co_old)
- elif co_old.is_Rational:
- return rv
- # break self and old into factors
- (c, nc) = breakup(self2)
- (old_c, old_nc) = breakup(old)
- # update the coefficients if we had an extraction
- # e.g. if co_self were 2*(3/35*x)**2 and co_old = 3/5
- # then co_self in c is replaced by (3/5)**2 and co_residual
- # is 2*(1/7)**2
- if co_xmul and co_xmul.is_Rational and abs(co_old) != 1:
- mult = S(multiplicity(abs(co_old), co_self))
- c.pop(co_self)
- if co_old in c:
- c[co_old] += mult
- else:
- c[co_old] = mult
- co_residual = co_self/co_old**mult
- else:
- co_residual = 1
- # do quick tests to see if we can't succeed
- ok = True
- if len(old_nc) > len(nc):
- # more non-commutative terms
- ok = False
- elif len(old_c) > len(c):
- # more commutative terms
- ok = False
- elif {i[0] for i in old_nc}.difference({i[0] for i in nc}):
- # unmatched non-commutative bases
- ok = False
- elif set(old_c).difference(set(c)):
- # unmatched commutative terms
- ok = False
- elif any(sign(c[b]) != sign(old_c[b]) for b in old_c):
- # differences in sign
- ok = False
- if not ok:
- return rv
- if not old_c:
- cdid = None
- else:
- rat = []
- for (b, old_e) in old_c.items():
- c_e = c[b]
- rat.append(ndiv(c_e, old_e))
- if not rat[-1]:
- return rv
- cdid = min(rat)
- if not old_nc:
- ncdid = None
- for i in range(len(nc)):
- nc[i] = rejoin(*nc[i])
- else:
- ncdid = 0 # number of nc replacements we did
- take = len(old_nc) # how much to look at each time
- limit = cdid or S.Infinity # max number that we can take
- failed = [] # failed terms will need subs if other terms pass
- i = 0
- while limit and i + take <= len(nc):
- hit = False
- # the bases must be equivalent in succession, and
- # the powers must be extractively compatible on the
- # first and last factor but equal in between.
- rat = []
- for j in range(take):
- if nc[i + j][0] != old_nc[j][0]:
- break
- elif j == 0:
- rat.append(ndiv(nc[i + j][1], old_nc[j][1]))
- elif j == take - 1:
- rat.append(ndiv(nc[i + j][1], old_nc[j][1]))
- elif nc[i + j][1] != old_nc[j][1]:
- break
- else:
- rat.append(1)
- j += 1
- else:
- ndo = min(rat)
- if ndo:
- if take == 1:
- if cdid:
- ndo = min(cdid, ndo)
- nc[i] = Pow(new, ndo)*rejoin(nc[i][0],
- nc[i][1] - ndo*old_nc[0][1])
- else:
- ndo = 1
- # the left residual
- l = rejoin(nc[i][0], nc[i][1] - ndo*
- old_nc[0][1])
- # eliminate all middle terms
- mid = new
- # the right residual (which may be the same as the middle if take == 2)
- ir = i + take - 1
- r = (nc[ir][0], nc[ir][1] - ndo*
- old_nc[-1][1])
- if r[1]:
- if i + take < len(nc):
- nc[i:i + take] = [l*mid, r]
- else:
- r = rejoin(*r)
- nc[i:i + take] = [l*mid*r]
- else:
- # there was nothing left on the right
- nc[i:i + take] = [l*mid]
- limit -= ndo
- ncdid += ndo
- hit = True
- if not hit:
- # do the subs on this failing factor
- failed.append(i)
- i += 1
- else:
- if not ncdid:
- return rv
- # although we didn't fail, certain nc terms may have
- # failed so we rebuild them after attempting a partial
- # subs on them
- failed.extend(range(i, len(nc)))
- for i in failed:
- nc[i] = rejoin(*nc[i]).subs(old, new)
- # rebuild the expression
- if cdid is None:
- do = ncdid
- elif ncdid is None:
- do = cdid
- else:
- do = min(ncdid, cdid)
- margs = []
- for b in c:
- if b in old_c:
- # calculate the new exponent
- e = c[b] - old_c[b]*do
- margs.append(rejoin(b, e))
- else:
- margs.append(rejoin(b.subs(old, new), c[b]))
- if cdid and not ncdid:
- # in case we are replacing commutative with non-commutative,
- # we want the new term to come at the front just like the
- # rest of this routine
- margs = [Pow(new, cdid)] + margs
- return co_residual*self2.func(*margs)*self2.func(*nc)
- def _eval_nseries(self, x, n, logx, cdir=0):
- from .function import PoleError
- from sympy.functions.elementary.integers import ceiling
- from sympy.series.order import Order
- def coeff_exp(term, x):
- lt = term.as_coeff_exponent(x)
- if lt[0].has(x):
- try:
- lt = term.leadterm(x)
- except ValueError:
- return term, S.Zero
- return lt
- ords = []
- try:
- for t in self.args:
- coeff, exp = t.leadterm(x)
- if not coeff.has(x):
- ords.append((t, exp))
- else:
- raise ValueError
- n0 = sum(t[1] for t in ords if t[1].is_number)
- facs = []
- for t, m in ords:
- n1 = ceiling(n - n0 + (m if m.is_number else 0))
- s = t.nseries(x, n=n1, logx=logx, cdir=cdir)
- ns = s.getn()
- if ns is not None:
- if ns < n1: # less than expected
- n -= n1 - ns # reduce n
- facs.append(s)
- except (ValueError, NotImplementedError, TypeError, AttributeError, PoleError):
- n0 = sympify(sum(t[1] for t in ords if t[1].is_number))
- if n0.is_nonnegative:
- n0 = S.Zero
- facs = [t.nseries(x, n=ceiling(n-n0), logx=logx, cdir=cdir) for t in self.args]
- from sympy.simplify.powsimp import powsimp
- res = powsimp(self.func(*facs).expand(), combine='exp', deep=True)
- if res.has(Order):
- res += Order(x**n, x)
- return res
- res = S.Zero
- ords2 = [Add.make_args(factor) for factor in facs]
- for fac in product(*ords2):
- ords3 = [coeff_exp(term, x) for term in fac]
- coeffs, powers = zip(*ords3)
- power = sum(powers)
- if (power - n).is_negative:
- res += Mul(*coeffs)*(x**power)
- def max_degree(e, x):
- if e is x:
- return S.One
- if e.is_Atom:
- return S.Zero
- if e.is_Add:
- return max(max_degree(a, x) for a in e.args)
- if e.is_Mul:
- return Add(*[max_degree(a, x) for a in e.args])
- if e.is_Pow:
- return max_degree(e.base, x)*e.exp
- return S.Zero
- if self.is_polynomial(x):
- from sympy.polys.polyerrors import PolynomialError
- from sympy.polys.polytools import degree
- try:
- if max_degree(self, x) >= n or degree(self, x) != degree(res, x):
- res += Order(x**n, x)
- except PolynomialError:
- pass
- else:
- return res
- if res != self:
- if (self - res).subs(x, 0) == S.Zero and n > 0:
- lt = self._eval_as_leading_term(x, logx=logx, cdir=cdir)
- if lt == S.Zero:
- return res
- res += Order(x**n, x)
- return res
- def _eval_as_leading_term(self, x, logx=None, cdir=0):
- return self.func(*[t.as_leading_term(x, logx=logx, cdir=cdir) 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[::-1]])
- def _eval_adjoint(self):
- return self.func(*[t.adjoint() for t in self.args[::-1]])
- def as_content_primitive(self, radical=False, clear=True):
- """Return the tuple (R, self/R) where R is the positive Rational
- extracted from self.
- Examples
- ========
- >>> from sympy import sqrt
- >>> (-3*sqrt(2)*(2 - 2*sqrt(2))).as_content_primitive()
- (6, -sqrt(2)*(1 - sqrt(2)))
- See docstring of Expr.as_content_primitive for more examples.
- """
- coef = S.One
- args = []
- for a in self.args:
- c, p = a.as_content_primitive(radical=radical, clear=clear)
- coef *= c
- if p is not S.One:
- args.append(p)
- # don't use self._from_args here to reconstruct args
- # since there may be identical args now that should be combined
- # e.g. (2+2*x)*(3+3*x) should be (6, (1 + x)**2) not (6, (1+x)*(1+x))
- return coef, self.func(*args)
- def as_ordered_factors(self, order=None):
- """Transform an expression into an ordered list of factors.
- Examples
- ========
- >>> from sympy import sin, cos
- >>> from sympy.abc import x, y
- >>> (2*x*y*sin(x)*cos(x)).as_ordered_factors()
- [2, x, y, sin(x), cos(x)]
- """
- cpart, ncpart = self.args_cnc()
- cpart.sort(key=lambda expr: expr.sort_key(order=order))
- return cpart + ncpart
- @property
- def _sorted_args(self):
- return tuple(self.as_ordered_factors())
- mul = AssocOpDispatcher('mul')
- def prod(a, start=1):
- """Return product of elements of a. Start with int 1 so if only
- ints are included then an int result is returned.
- Examples
- ========
- >>> from sympy import prod, S
- >>> prod(range(3))
- 0
- >>> type(_) is int
- True
- >>> prod([S(2), 3])
- 6
- >>> _.is_Integer
- True
- You can start the product at something other than 1:
- >>> prod([1, 2], 3)
- 6
- """
- return reduce(operator.mul, a, start)
- def _keep_coeff(coeff, factors, clear=True, sign=False):
- """Return ``coeff*factors`` unevaluated if necessary.
- If ``clear`` is False, do not keep the coefficient as a factor
- if it can be distributed on a single factor such that one or
- more terms will still have integer coefficients.
- If ``sign`` is True, allow a coefficient of -1 to remain factored out.
- Examples
- ========
- >>> from sympy.core.mul import _keep_coeff
- >>> from sympy.abc import x, y
- >>> from sympy import S
- >>> _keep_coeff(S.Half, x + 2)
- (x + 2)/2
- >>> _keep_coeff(S.Half, x + 2, clear=False)
- x/2 + 1
- >>> _keep_coeff(S.Half, (x + 2)*y, clear=False)
- y*(x + 2)/2
- >>> _keep_coeff(S(-1), x + y)
- -x - y
- >>> _keep_coeff(S(-1), x + y, sign=True)
- -(x + y)
- """
- if not coeff.is_Number:
- if factors.is_Number:
- factors, coeff = coeff, factors
- else:
- return coeff*factors
- if factors is S.One:
- return coeff
- if coeff is S.One:
- return factors
- elif coeff is S.NegativeOne and not sign:
- return -factors
- elif factors.is_Add:
- if not clear and coeff.is_Rational and coeff.q != 1:
- args = [i.as_coeff_Mul() for i in factors.args]
- args = [(_keep_coeff(c, coeff), m) for c, m in args]
- if any(c.is_Integer for c, _ in args):
- return Add._from_args([Mul._from_args(
- i[1:] if i[0] == 1 else i) for i in args])
- return Mul(coeff, factors, evaluate=False)
- elif factors.is_Mul:
- margs = list(factors.args)
- if margs[0].is_Number:
- margs[0] *= coeff
- if margs[0] == 1:
- margs.pop(0)
- else:
- margs.insert(0, coeff)
- return Mul._from_args(margs)
- else:
- m = coeff*factors
- if m.is_Number and not factors.is_Number:
- m = Mul._from_args((coeff, factors))
- return m
- def expand_2arg(e):
- def do(e):
- if e.is_Mul:
- c, r = e.as_coeff_Mul()
- if c.is_Number and r.is_Add:
- return _unevaluated_Add(*[c*ri for ri in r.args])
- return e
- return bottom_up(e, do)
- from .numbers import Rational
- from .power import Pow
- from .add import Add, _unevaluated_Add
|