def test_ordered(): # Issue sympy/sympy#7210 - this had been failing with python2/3 problems assert (list(ordered([{ 1: 3, 2: 4, 9: 10 }, { 1: 3 }])) == [{ 1: 3 }, { 1: 3, 2: 4, 9: 10 }]) # warnings should not be raised for identical items l = [1, 1] assert list(ordered(l, warn=True)) == l l = [[1], [2], [1]] assert list(ordered(l, warn=True)) == [[1], [1], [2]] pytest.raises( ValueError, lambda: list( ordered( ['a', 'ab'], keys=[lambda x: x[0]], default=False, warn=True))) pytest.raises(ValueError, lambda: list(ordered(['a', 'b'], default=False)))
def test_sympyissue_8825(): import weakref a, b = symbols('a b') d = weakref.WeakKeyDictionary([(a, 1), (b, 2)]) assert sstr(list(ordered(d.items()))) == '[(a, 1), (b, 2)]' del a clear_cache() assert sstr(list(ordered(d.items()))) == '[(b, 2)]'
def test_sympyissue_8825(): t1, t2 = symbols('t1:3') d = weakref.WeakKeyDictionary([(t1, 1), (t2, 2)]) assert sstr(list(ordered(d.items()))) == '[(t1, 1), (t2, 2)]' del t1 clear_cache() gc.collect() assert sstr(list(ordered(d.items()))) == '[(t2, 2)]'
def test_sympyissue_8825(): t1, t2 = symbols('t1:3') d = weakref.WeakKeyDictionary([(t1, 1), (t2, 2)]) assert sstr(list(ordered(d.items()))) == '[(t1, 1), (t2, 2)]' del t1 clear_cache() gc.collect() assert sstr(list(ordered(d.items()))) == '[(t2, 2)]'
def test_ordered(): # Issue sympy/sympy#7210 - this had been failing with python2/3 problems assert (list(ordered([{1: 3, 2: 4, 9: 10}, {1: 3}])) == [{1: 3}, {1: 3, 2: 4, 9: 10}]) # warnings should not be raised for identical items l = [1, 1] assert list(ordered(l, warn=True)) == l l = [[1], [2], [1]] assert list(ordered(l, warn=True)) == [[1], [1], [2]] pytest.raises(ValueError, lambda: list(ordered(['a', 'ab'], keys=[lambda x: x[0]], default=False, warn=True))) pytest.raises(ValueError, lambda: list(ordered(['a', 'b'], default=False)))
def roots_cyclotomic(f, factor=False): """Compute roots of cyclotomic polynomials. """ L, U = _inv_totient_estimate(f.degree()) for n in range(L, U + 1): g = cyclotomic_poly(n, f.gen, polys=True) if f == g: break else: # pragma: no cover raise RuntimeError("failed to find index of a cyclotomic polynomial") roots = [] if not factor: # get the indices in the right order so the computed # roots will be sorted h = n//2 ks = [i for i in range(1, n + 1) if igcd(i, n) == 1] ks.sort(key=lambda x: (x, -1) if x <= h else (abs(x - n), 1)) d = 2*I*pi/n for k in reversed(ks): roots.append(exp(k*d).expand(complex=True)) else: g = Poly(f, extension=root(-1, n)) for h, _ in ordered(g.factor_list()[1]): roots.append(-h.TC()) return roots
def __new__(cls, *args, **kwargs): argset = set() obj = super(Xor, cls).__new__(cls, *args, **kwargs) for arg in obj._args: if isinstance(arg, Number) or arg in (True, False): if arg: arg = true else: continue if isinstance(arg, Xor): for a in arg.args: argset.remove(a) if a in argset else argset.add(a) elif arg in argset: argset.remove(arg) else: argset.add(arg) rel = [(r, r.canonical, (~r).canonical) for r in argset if r.is_Relational] odd = False # is number of complimentary pairs odd? start 0 -> False remove = [] for i, (r, c, nc) in enumerate(rel): for j in range(i + 1, len(rel)): rj, cj = rel[j][:2] if cj == nc: odd = ~odd break elif cj == c: break else: continue remove.append((r, rj)) if odd: argset.remove(true) if true in argset else argset.add(true) for a, b in remove: argset.remove(a) argset.remove(b) if len(argset) == 0: return false elif len(argset) == 1: return argset.pop() elif True in argset: argset.remove(True) return Not(Xor(*argset)) else: obj._args = tuple(ordered(argset)) obj._argset = frozenset(argset) return obj
def _finger(eq): """ Assign a 5-item fingerprint to each symbol in the equation: [ # of times it appeared as a Symbol, # of times it appeared as a Not(symbol), # of times it appeared as a Symbol in an And or Or, # of times it appeared as a Not(Symbol) in an And or Or, sum of the number of arguments with which it appeared, counting Symbol as 1 and Not(Symbol) as 2 ] >>> from diofant.logic.boolalg import _finger as finger >>> from diofant import And, Or, Not >>> from diofant.abc import a, b, x, y >>> eq = Or(And(Not(y), a), And(Not(y), b), And(x, y)) >>> dict(finger(eq)) == {(0, 0, 1, 0, 2): [x], ... (0, 0, 1, 0, 3): [a, b], ... (0, 0, 1, 2, 8): [y]} True So y and x have unique fingerprints, but a and b do not. """ f = eq.free_symbols d = dict(zip(f, ([0] * 5 for fi in f))) for a in eq.args: if a.is_Symbol: d[a][0] += 1 elif a.is_Not: d[a.args[0]][1] += 1 else: o = len(a.args) + sum(ai.func is Not for ai in a.args) for ai in a.args: if ai.is_Symbol: d[ai][2] += 1 d[ai][-1] += o else: d[ai.args[0]][3] += 1 d[ai.args[0]][-1] += o inv = defaultdict(list) for k, v in ordered(iter(d.items())): inv[tuple(v)].append(k) return inv
def root_factors(f, *gens, **args): """ Returns all factors of a univariate polynomial. Examples ======== >>> from diofant.abc import x, y >>> root_factors(x**2 - y, x) [x - sqrt(y), x + sqrt(y)] """ args = dict(args) filter = args.pop('filter', None) F = Poly(f, *gens, **args) if not F.is_Poly: return [f] if F.is_multivariate: raise ValueError('multivariate polynomials are not supported') x = F.gens[0] zeros = roots(F, filter=filter) if not zeros: factors = [F] else: factors, N = [], 0 for r, n in ordered(zeros.items()): factors, N = factors + [Poly(x - r, x)]*n, N + n if N < F.degree(): G = reduce(lambda p, q: p*q, factors) factors.append(F.quo(G)) if not isinstance(f, Poly): factors = [ f.as_expr() for f in factors ] return factors
def handle(expr): # Handle first reduces to the case # expr = 1/d, where d is an add, or d is base**p/2. # We do this by recursively calling handle on each piece. from diofant.simplify.simplify import nsimplify n, d = fraction(expr) if expr.is_Atom or (d.is_Atom and n.is_Atom): return expr elif not n.is_Atom: n = n.func(*[handle(a) for a in n.args]) return _unevaluated_Mul(n, handle(1 / d)) elif n is not S.One: return _unevaluated_Mul(n, handle(1 / d)) elif d.is_Mul: return _unevaluated_Mul(*[handle(1 / d) for d in d.args]) # By this step, expr is 1/d, and d is not a mul. if not symbolic and d.free_symbols: return expr if ispow2(d): d2 = sqrtdenest(sqrt(d.base))**fraction(d.exp)[0] if d2 != d: return handle(1 / d2) elif d.is_Pow and (d.exp.is_integer or d.base.is_positive): # (1/d**i) = (1/d)**i return handle(1 / d.base)**d.exp if not (d.is_Add or ispow2(d)): return 1 / d.func(*[handle(a) for a in d.args]) # handle 1/d treating d as an Add (though it may not be) keep = True # keep changes that are made # flatten it and collect radicals after checking for special # conditions d = _mexpand(d) # did it change? if d.is_Atom: return 1 / d # is it a number that might be handled easily? if d.is_number: _d = nsimplify(d) if _d.is_Number and _d.equals(d): return 1 / _d while True: # collect similar terms collected = defaultdict(list) for m in Add.make_args(d): # d might have become non-Add p2 = [] other = [] for i in Mul.make_args(m): if ispow2(i, log2=True): p2.append(i.base if i.exp is S.Half else i.base**( 2 * i.exp)) elif i is S.ImaginaryUnit: p2.append(S.NegativeOne) else: other.append(i) collected[tuple(ordered(p2))].append(Mul(*other)) rterms = list(ordered(list(collected.items()))) rterms = [(Mul(*i), Add(*j)) for i, j in rterms] nrad = len(rterms) - (1 if rterms[0][0] is S.One else 0) if nrad < 1: break elif nrad > max_terms: # there may have been invalid operations leading to this point # so don't keep changes, e.g. this expression is troublesome # in collecting terms so as not to raise the issue of 2834: # r = sqrt(sqrt(5) + 5) # eq = 1/(sqrt(5)*r + 2*sqrt(5)*sqrt(-sqrt(5) + 5) + 5*r) keep = False break if len(rterms) > 4: # in general, only 4 terms can be removed with repeated squaring # but other considerations can guide selection of radical terms # so that radicals are removed if all( [x.is_Integer and (y**2).is_Rational for x, y in rterms]): nd, d = rad_rationalize( S.One, Add._from_args([sqrt(x) * y for x, y in rterms])) n *= nd else: # is there anything else that might be attempted? keep = False break from diofant.simplify.powsimp import powsimp, powdenest num = powsimp(_num(rterms)) n *= num d *= num d = powdenest(_mexpand(d), force=symbolic) if d.is_Atom: break if not keep: return expr return _unevaluated_Mul(n, 1 / d)
def powsimp(expr, deep=False, combine='all', force=False, measure=count_ops): """ reduces expression by combining powers with similar bases and exponents. Notes ===== If deep is True then powsimp() will also simplify arguments of functions. By default deep is set to False. If force is True then bases will be combined without checking for assumptions, e.g. sqrt(x)*sqrt(y) -> sqrt(x*y) which is not true if x and y are both negative. You can make powsimp() only combine bases or only combine exponents by changing combine='base' or combine='exp'. By default, combine='all', which does both. combine='base' will only combine:: a a a 2x x x * y => (x*y) as well as things like 2 => 4 and combine='exp' will only combine :: a b (a + b) x * x => x combine='exp' will strictly only combine exponents in the way that used to be automatic. Also use deep=True if you need the old behavior. When combine='all', 'exp' is evaluated first. Consider the first example below for when there could be an ambiguity relating to this. This is done so things like the second example can be completely combined. If you want 'base' combined first, do something like powsimp(powsimp(expr, combine='base'), combine='exp'). Examples ======== >>> from diofant import powsimp, exp, log, symbols >>> from diofant.abc import x, y, z, n >>> powsimp(x**y*x**z*y**z, combine='all') x**(y + z)*y**z >>> powsimp(x**y*x**z*y**z, combine='exp') x**(y + z)*y**z >>> powsimp(x**y*x**z*y**z, combine='base', force=True) x**y*(x*y)**z >>> powsimp(x**z*x**y*n**z*n**y, combine='all', force=True) (n*x)**(y + z) >>> powsimp(x**z*x**y*n**z*n**y, combine='exp') n**(y + z)*x**(y + z) >>> powsimp(x**z*x**y*n**z*n**y, combine='base', force=True) (n*x)**y*(n*x)**z >>> x, y = symbols('x y', positive=True) >>> powsimp(log(exp(x)*exp(y))) log(E**x*E**y) >>> powsimp(log(exp(x)*exp(y)), deep=True) x + y Radicals with Mul bases will be combined if combine='exp' >>> from diofant import sqrt, Mul >>> x, y = symbols('x y') Two radicals are automatically joined through Mul: >>> a=sqrt(x*sqrt(y)) >>> a*a**3 == a**4 True But if an integer power of that radical has been autoexpanded then Mul does not join the resulting factors: >>> a**4 # auto expands to a Mul, no longer a Pow x**2*y >>> _*a # so Mul doesn't combine them x**2*y*sqrt(x*sqrt(y)) >>> powsimp(_) # but powsimp will (x*sqrt(y))**(5/2) >>> powsimp(x*y*a) # but won't when doing so would violate assumptions x*y*sqrt(x*sqrt(y)) """ from diofant.matrices.expressions.matexpr import MatrixSymbol def recurse(arg, **kwargs): _deep = kwargs.get('deep', deep) _combine = kwargs.get('combine', combine) _force = kwargs.get('force', force) _measure = kwargs.get('measure', measure) return powsimp(arg, _deep, _combine, _force, _measure) expr = sympify(expr) if (not isinstance(expr, Basic) or isinstance(expr, MatrixSymbol) or (expr.is_Atom or expr in (exp_polar(0), exp_polar(1)))): return expr if deep or expr.is_Add or expr.is_Mul and _y not in expr.args: expr = expr.func(*[recurse(w) for w in expr.args]) if expr.is_Pow: return recurse(expr * _y, deep=False) / _y if not expr.is_Mul: return expr # handle the Mul if combine in ('exp', 'all'): # Collect base/exp data, while maintaining order in the # non-commutative parts of the product c_powers = defaultdict(list) nc_part = [] newexpr = [] coeff = S.One for term in expr.args: if term.is_Rational: coeff *= term continue if term.is_Pow: term = _denest_pow(term) if term.is_commutative: b, e = term.as_base_exp() if deep: b, e = [recurse(i) for i in [b, e]] if b.is_Pow: # don't let smthg like sqrt(x**a) split into x**a, 1/2 # or else it will be joined as x**(a/2) later b, e = b**e, S.One c_powers[b].append(e) else: # This is the logic that combines exponents for equal, # but non-commutative bases: A**x*A**y == A**(x+y). if nc_part: b1, e1 = nc_part[-1].as_base_exp() b2, e2 = term.as_base_exp() if (b1 == b2 and e1.is_commutative and e2.is_commutative): nc_part[-1] = Pow(b1, Add(e1, e2)) continue nc_part.append(term) # add up exponents of common bases for b, e in ordered(iter(c_powers.items())): # allow 2**x/4 -> 2**(x - 2); don't do this when b and e are # Numbers since autoevaluation will undo it, e.g. # 2**(1/3)/4 -> 2**(1/3 - 2) -> 2**(1/3)/4 if (b and b.is_Number and not all(ei.is_Number for ei in e) and coeff is not S.One and b not in (S.One, S.NegativeOne)): m = multiplicity(abs(b), abs(coeff)) if m: e.append(m) coeff /= b**m c_powers[b] = Add(*e) if coeff is not S.One: if coeff in c_powers: c_powers[coeff] += S.One else: c_powers[coeff] = S.One # convert to plain dictionary c_powers = dict(c_powers) # check for base and inverted base pairs be = list(c_powers.items()) skip = set() # skip if we already saw them for b, e in be: if b in skip: continue bpos = b.is_positive or b.is_polar if bpos: binv = 1 / b if b != binv and binv in c_powers: if b.as_numer_denom()[0] is S.One: c_powers.pop(b) c_powers[binv] -= e else: skip.add(binv) e = c_powers.pop(binv) c_powers[b] -= e # check for base and negated base pairs be = list(c_powers.items()) _n = S.NegativeOne for i, (b, e) in enumerate(be): if ((-b).is_Symbol or b.is_Add) and -b in c_powers: if (b.is_positive in (0, 1) or e.is_integer): c_powers[-b] += c_powers.pop(b) if _n in c_powers: c_powers[_n] += e else: c_powers[_n] = e # filter c_powers and convert to a list c_powers = [(b, e) for b, e in c_powers.items() if e] # ============================================================== # check for Mul bases of Rational powers that can be combined with # separated bases, e.g. x*sqrt(x*y)*sqrt(x*sqrt(x*y)) -> # (x*sqrt(x*y))**(3/2) # ---------------- helper functions def ratq(x): '''Return Rational part of x's exponent as it appears in the bkey. ''' return bkey(x)[0][1] def bkey(b, e=None): '''Return (b**s, c.q), c.p where e -> c*s. If e is not given then it will be taken by using as_base_exp() on the input b. e.g. x**3/2 -> (x, 2), 3 x**y -> (x**y, 1), 1 x**(2*y/3) -> (x**y, 3), 2 exp(x/2) -> (exp(a), 2), 1 ''' if e is not None: # coming from c_powers or from below if e.is_Integer: return (b, S.One), e elif e.is_Rational: return (b, Integer(e.q)), Integer(e.p) else: c, m = e.as_coeff_Mul(rational=True) if c is not S.One and b.is_positive: return (b**m, Integer(c.q)), Integer(c.p) else: return (b**e, S.One), S.One else: return bkey(*b.as_base_exp()) def update(b): '''Decide what to do with base, b. If its exponent is now an integer multiple of the Rational denominator, then remove it and put the factors of its base in the common_b dictionary or update the existing bases if necessary. If it has been zeroed out, simply remove the base. ''' newe, r = divmod(common_b[b], b[1]) if not r: common_b.pop(b) if newe: for m in Mul.make_args(b[0]**newe): b, e = bkey(m) if b not in common_b: common_b[b] = 0 common_b[b] += e if b[1] != 1: bases.append(b) # ---------------- end of helper functions # assemble a dictionary of the factors having a Rational power common_b = {} done = [] bases = [] for b, e in c_powers: b, e = bkey(b, e) if b in common_b.keys(): common_b[b] = common_b[b] + e else: common_b[b] = e if b[1] != 1 and b[0].is_Mul: bases.append(b) c_powers = [(b, e) for b, e in common_b.items() if e] bases.sort(key=default_sort_key) # this makes tie-breaking canonical bases.sort(key=measure, reverse=True) # handle longest first for base in bases: if base not in common_b: # it may have been removed already continue b, exponent = base last = False # True when no factor of base is a radical qlcm = 1 # the lcm of the radical denominators while True: bstart = b qstart = qlcm bb = [] # list of factors ee = [] # (factor's expo. and it's current value in common_b) for bi in Mul.make_args(b): bib, bie = bkey(bi) if bib not in common_b or common_b[bib] < bie: ee = bb = [] # failed break ee.append([bie, common_b[bib]]) bb.append(bib) if ee: # find the number of extractions possible # e.g. [(1, 2), (2, 2)] -> min(2/1, 2/2) -> 1 min1 = ee[0][1] / ee[0][0] for i in range(len(ee)): rat = ee[i][1] / ee[i][0] if rat < 1: break min1 = min(min1, rat) else: # update base factor counts # e.g. if ee = [(2, 5), (3, 6)] then min1 = 2 # and the new base counts will be 5-2*2 and 6-2*3 for i in range(len(bb)): common_b[bb[i]] -= min1 * ee[i][0] update(bb[i]) # update the count of the base # e.g. x**2*y*sqrt(x*sqrt(y)) the count of x*sqrt(y) # will increase by 4 to give bkey (x*sqrt(y), 2, 5) common_b[base] += min1 * qstart * exponent if (last # no more radicals in base or len(common_b) == 1 # nothing left to join with or all(k[1] == 1 for k in common_b) # no rad's in common_b ): break # see what we can exponentiate base by to remove any radicals # so we know what to search for # e.g. if base were x**(1/2)*y**(1/3) then we should # exponentiate by 6 and look for powers of x and y in the ratio # of 2 to 3 qlcm = lcm([ratq(bi) for bi in Mul.make_args(bstart)]) if qlcm == 1: break # we are done b = bstart**qlcm qlcm *= qstart if all(ratq(bi) == 1 for bi in Mul.make_args(b)): last = True # we are going to be done after this next pass # this base no longer can find anything to join with and # since it was longer than any other we are done with it b, q = base done.append((b, common_b.pop(base) * Rational(1, q))) # update c_powers and get ready to continue with powsimp c_powers = done # there may be terms still in common_b that were bases that were # identified as needing processing, so remove those, too for (b, q), e in common_b.items(): if b.is_Pow and q is not S.One and not b.exp.is_Rational: b, be = b.as_base_exp() b = b**(be / q) else: b = root(b, q) c_powers.append((b, e)) check = len(c_powers) c_powers = dict(c_powers) assert len(c_powers) == check # there should have been no duplicates # ============================================================== # rebuild the expression newexpr = expr.func(*(newexpr + [Pow(b, e) for b, e in c_powers.items()])) if combine == 'exp': return expr.func(newexpr, expr.func(*nc_part)) else: return recurse(expr.func(*nc_part), combine='base') * \ recurse(newexpr, combine='base') elif combine == 'base': # Build c_powers and nc_part. These must both be lists not # dicts because exp's are not combined. c_powers = [] nc_part = [] for term in expr.args: if term.is_commutative: c_powers.append(list(term.as_base_exp())) else: # This is the logic that combines bases that are # different and non-commutative, but with equal and # commutative exponents: A**x*B**x == (A*B)**x. if nc_part: b1, e1 = nc_part[-1].as_base_exp() b2, e2 = term.as_base_exp() if (e1 == e2 and e2.is_commutative): nc_part[-1] = Pow(b1 * b2, e1) continue nc_part.append(term) # Pull out numerical coefficients from exponent if assumptions allow # e.g., 2**(2*x) => 4**x for i in range(len(c_powers)): b, e = c_powers[i] if not (all(x.is_nonnegative for x in b.as_numer_denom()) or e.is_integer or force or b.is_polar): continue exp_c, exp_t = e.as_coeff_Mul(rational=True) if exp_c is not S.One and exp_t is not S.One: c_powers[i] = [Pow(b, exp_c), exp_t] # Combine bases whenever they have the same exponent and # assumptions allow # first gather the potential bases under the common exponent c_exp = defaultdict(list) for b, e in c_powers: if deep: e = recurse(e) c_exp[e].append(b) del c_powers # Merge back in the results of the above to form a new product c_powers = defaultdict(list) for e in c_exp: bases = c_exp[e] # calculate the new base for e if len(bases) == 1: new_base = bases[0] elif e.is_integer or force: new_base = expr.func(*bases) else: # see which ones can be joined unk = [] nonneg = [] neg = [] for bi in bases: if bi.is_negative: neg.append(bi) elif bi.is_nonnegative: nonneg.append(bi) elif bi.is_polar: nonneg.append( bi) # polar can be treated like non-negative else: unk.append(bi) if len(unk) == 1 and not neg or len(neg) == 1 and not unk: # a single neg or a single unk can join the rest nonneg.extend(unk + neg) unk = neg = [] elif neg: # their negative signs cancel in groups of 2*q if we know # that e = p/q else we have to treat them as unknown israt = False if e.is_Rational: israt = True else: p, d = e.as_numer_denom() if p.is_integer and d.is_integer: israt = True if israt: neg = [-w for w in neg] unk.extend([S.NegativeOne] * len(neg)) else: unk.extend(neg) neg = [] del israt # these shouldn't be joined for b in unk: c_powers[b].append(e) # here is a new joined base new_base = expr.func(*(nonneg + neg)) # if there are positive parts they will just get separated # again unless some change is made def _terms(e): # return the number of terms of this expression # when multiplied out -- assuming no joining of terms if e.is_Add: return sum(_terms(ai) for ai in e.args) if e.is_Mul: return prod([_terms(mi) for mi in e.args]) return 1 xnew_base = expand_mul(new_base, deep=False) if len(Add.make_args(xnew_base)) < _terms(new_base): new_base = factor_terms(xnew_base) c_powers[new_base].append(e) # break out the powers from c_powers now c_part = [Pow(b, ei) for b, e in c_powers.items() for ei in e] # we're done return expr.func(*(c_part + nc_part)) else: raise ValueError("combine must be one of ('all', 'exp', 'base').")
def roots(f, *gens, **flags): """ Computes symbolic roots of a univariate polynomial. Given a univariate polynomial f with symbolic coefficients (or a list of the polynomial's coefficients), returns a dictionary with its roots and their multiplicities. Only roots expressible via radicals will be returned. To get a complete set of roots use RootOf class or numerical methods instead. By default cubic and quartic formulas are used in the algorithm. To disable them because of unreadable output set ``cubics=False`` or ``quartics=False`` respectively. If cubic roots are real but are expressed in terms of complex numbers (casus irreducibilis [1]) the ``trig`` flag can be set to True to have the solutions returned in terms of cosine and inverse cosine functions. To get roots from a specific domain set the ``filter`` flag with one of the following specifiers: Z, Q, R, I, C. By default all roots are returned (this is equivalent to setting ``filter='C'``). By default a dictionary is returned giving a compact result in case of multiple roots. However to get a list containing all those roots set the ``multiple`` flag to True; the list will have identical roots appearing next to each other in the result. (For a given Poly, the all_roots method will give the roots in sorted numerical order.) Examples ======== >>> from diofant import Poly, roots, sqrt >>> from diofant.abc import x, y >>> roots(x**2 - 1, x) == {-1: 1, 1: 1} True >>> p = Poly(x**2-1, x) >>> roots(p) == {-1: 1, 1: 1} True >>> p = Poly(x**2-y, x, y) >>> roots(Poly(p, x)) == {-sqrt(y): 1, sqrt(y): 1} True >>> roots(x**2 - y, x) == {-sqrt(y): 1, sqrt(y): 1} True >>> roots([1, 0, -1]) == {-1: 1, 1: 1} True References ========== .. [1] http://en.wikipedia.org/wiki/Cubic_function#Trigonometric_.28and_hyperbolic.29_method """ from diofant.polys.polytools import to_rational_coeffs flags = dict(flags) auto = flags.pop('auto', True) cubics = flags.pop('cubics', True) trig = flags.pop('trig', False) quartics = flags.pop('quartics', True) quintics = flags.pop('quintics', False) multiple = flags.pop('multiple', False) filter = flags.pop('filter', None) predicate = flags.pop('predicate', None) if isinstance(f, list): if gens: raise ValueError('redundant generators given') x = Dummy('x') poly, i = {}, len(f) - 1 for coeff in f: poly[i], i = sympify(coeff), i - 1 f = Poly(poly, x, field=True) else: try: f = Poly(f, *gens, **flags) if f.length == 2 and f.degree() != 1: # check for foo**n factors in the constant n = f.degree() npow_bases = [] expr = f.as_expr() con = expr.as_independent(*gens)[0] for p in Mul.make_args(con): if p.is_Pow and not p.exp % n: npow_bases.append(p.base**(p.exp/n)) else: other.append(p) if npow_bases: b = Mul(*npow_bases) B = Dummy() d = roots(Poly(expr - con + B**n*Mul(*others), *gens, **flags), *gens, **flags) rv = {} for k, v in d.items(): rv[k.subs(B, b)] = v return rv except GeneratorsNeeded: if multiple: return [] else: return {} if f.is_multivariate: raise PolynomialError('multivariate polynomials are not supported') def _update_dict(result, root, k): if root in result: result[root] += k else: result[root] = k def _try_decompose(f): """Find roots using functional decomposition. """ factors, roots = f.decompose(), [] for root in _try_heuristics(factors[0]): roots.append(root) for factor in factors[1:]: previous, roots = list(roots), [] for root in previous: g = factor - Poly(root, f.gen) for root in _try_heuristics(g): roots.append(root) return roots def _try_heuristics(f): """Find roots using formulas and some tricks. """ if f.is_ground: return [] if f.is_monomial: return [Integer(0)]*f.degree() if f.length() == 2: if f.degree() == 1: return list(map(cancel, roots_linear(f))) else: return roots_binomial(f) result = [] for i in [-1, 1]: if not f.eval(i): f = f.quo(Poly(f.gen - i, f.gen)) result.append(i) break n = f.degree() if n == 1: result += list(map(cancel, roots_linear(f))) elif n == 2: result += list(map(cancel, roots_quadratic(f))) elif f.is_cyclotomic: result += roots_cyclotomic(f) elif n == 3 and cubics: result += roots_cubic(f, trig=trig) elif n == 4 and quartics: result += roots_quartic(f) elif n == 5 and quintics: result += roots_quintic(f) return result (k,), f = f.terms_gcd() if not k: zeros = {} else: zeros = {Integer(0): k} coeff, f = preprocess_roots(f) if auto and f.get_domain().has_Ring: f = f.to_field() rescale_x = None translate_x = None result = {} if not f.is_ground: if not f.get_domain().is_Exact: for r in f.nroots(): _update_dict(result, r, 1) elif f.degree() == 1: result[roots_linear(f)[0]] = 1 elif f.length() == 2: roots_fun = roots_quadratic if f.degree() == 2 else roots_binomial for r in roots_fun(f): _update_dict(result, r, 1) else: _, factors = Poly(f.as_expr()).factor_list() if len(factors) == 1 and f.degree() == 2: for r in roots_quadratic(f): _update_dict(result, r, 1) else: if len(factors) == 1 and factors[0][1] == 1: if f.get_domain().is_EX: res = to_rational_coeffs(f) if res: if res[0] is None: translate_x, f = res[2:] else: rescale_x, f = res[1], res[-1] result = roots(f) if not result: for root in _try_decompose(f): _update_dict(result, root, 1) else: for root in _try_decompose(f): _update_dict(result, root, 1) else: for factor, k in factors: for r in _try_heuristics(Poly(factor, f.gen, field=True)): _update_dict(result, r, k) if coeff is not S.One: _result, result, = result, {} for root, k in _result.items(): result[coeff*root] = k result.update(zeros) if filter not in [None, 'C']: handlers = { 'Z': lambda r: r.is_Integer, 'Q': lambda r: r.is_Rational, 'R': lambda r: r.is_extended_real, 'I': lambda r: r.is_imaginary, } try: query = handlers[filter] except KeyError: raise ValueError("Invalid filter: %s" % filter) for zero in dict(result).keys(): if not query(zero): del result[zero] if predicate is not None: for zero in dict(result).keys(): if not predicate(zero): del result[zero] if rescale_x: result1 = {} for k, v in result.items(): result1[k*rescale_x] = v result = result1 if translate_x: result1 = {} for k, v in result.items(): result1[k + translate_x] = v result = result1 if not multiple: return result else: zeros = [] for zero in ordered(result): zeros.extend([zero]*result[zero]) return zeros
def clauses(self): return list(ordered(self.clauses_))
def args(self): return tuple(ordered(self._argset))
def test_sympyissue_9608(): a = Partition([1, 2, 3], [4]) b = Partition([1, 2], [3, 4]) assert list(ordered([a, b], Set._infimum_key)) # does not raise an error
def collect_const(expr, *vars, **kwargs): """A non-greedy collection of terms with similar number coefficients in an Add expr. If ``vars`` is given then only those constants will be targeted. Although any Number can also be targeted, if this is not desired set ``Numbers=False`` and no Float or Rational will be collected. Examples ======== >>> from diofant import sqrt >>> from diofant.abc import a, s, x, y, z >>> from diofant.simplify.radsimp import collect_const >>> collect_const(sqrt(3) + sqrt(3)*(1 + sqrt(2))) sqrt(3)*(sqrt(2) + 2) >>> collect_const(sqrt(3)*s + sqrt(7)*s + sqrt(3) + sqrt(7)) (sqrt(3) + sqrt(7))*(s + 1) >>> s = sqrt(2) + 2 >>> collect_const(sqrt(3)*s + sqrt(3) + sqrt(7)*s + sqrt(7)) (sqrt(2) + 3)*(sqrt(3) + sqrt(7)) >>> collect_const(sqrt(3)*s + sqrt(3) + sqrt(7)*s + sqrt(7), sqrt(3)) sqrt(7) + sqrt(3)*(sqrt(2) + 3) + sqrt(7)*(sqrt(2) + 2) The collection is sign-sensitive, giving higher precedence to the unsigned values: >>> collect_const(x - y - z) x - (y + z) >>> collect_const(-y - z) -(y + z) >>> collect_const(2*x - 2*y - 2*z, 2) 2*(x - y - z) >>> collect_const(2*x - 2*y - 2*z, -2) 2*x - 2*(y + z) See Also ======== collect, collect_sqrt, rcollect """ if not expr.is_Add: return expr recurse = False Numbers = kwargs.get('Numbers', True) if not vars: recurse = True vars = set() for a in expr.args: for m in Mul.make_args(a): if m.is_number: vars.add(m) else: vars = sympify(vars) if not Numbers: vars = [v for v in vars if not v.is_Number] vars = list(ordered(vars)) for v in vars: terms = defaultdict(list) Fv = Factors(v) for m in Add.make_args(expr): f = Factors(m) q, r = f.div(Fv) if r.is_one: # only accept this as a true factor if # it didn't change an exponent from an Integer # to a non-Integer, e.g. 2/sqrt(2) -> sqrt(2) # -- we aren't looking for this sort of change fwas = f.factors.copy() fnow = q.factors if not any(k in fwas and fwas[k].is_Integer and not fnow[k].is_Integer for k in fnow): terms[v].append(q.as_expr()) continue terms[S.One].append(m) args = [] hit = False uneval = False for k in ordered(terms): v = terms[k] if k is S.One: args.extend(v) continue if len(v) > 1: v = Add(*v) hit = True if recurse and v != expr: vars.append(v) else: v = v[0] # be careful not to let uneval become True unless # it must be because it's going to be more expensive # to rebuild the expression as an unevaluated one if Numbers and k.is_Number and v.is_Add: args.append(_keep_coeff(k, v, sign=True)) uneval = True else: args.append(k * v) if hit: if uneval: expr = _unevaluated_Add(*args) else: expr = Add(*args) if not expr.is_Add: break return expr
def collect_sqrt(expr, evaluate=True): """Return expr with terms having common square roots collected together. If ``evaluate`` is False a count indicating the number of sqrt-containing terms will be returned and, if non-zero, the terms of the Add will be returned, else the expression itself will be returned as a single term. If ``evaluate`` is True, the expression with any collected terms will be returned. Note: since I = sqrt(-1), it is collected, too. Examples ======== >>> from diofant import sqrt >>> from diofant.simplify.radsimp import collect_sqrt >>> from diofant.abc import a, b >>> r2, r3, r5 = [sqrt(i) for i in [2, 3, 5]] >>> collect_sqrt(a*r2 + b*r2) sqrt(2)*(a + b) >>> collect_sqrt(a*r2 + b*r2 + a*r3 + b*r3) sqrt(2)*(a + b) + sqrt(3)*(a + b) >>> collect_sqrt(a*r2 + b*r2 + a*r3 + b*r5) sqrt(3)*a + sqrt(5)*b + sqrt(2)*(a + b) If evaluate is False then the arguments will be sorted and returned as a list and a count of the number of sqrt-containing terms will be returned: >>> collect_sqrt(a*r2 + b*r2 + a*r3 + b*r5, evaluate=False) ((sqrt(3)*a, sqrt(5)*b, sqrt(2)*(a + b)), 3) >>> collect_sqrt(a*sqrt(2) + b, evaluate=False) ((b, sqrt(2)*a), 1) >>> collect_sqrt(a + b, evaluate=False) ((a + b,), 0) See Also ======== collect, collect_const, rcollect """ # this step will help to standardize any complex arguments # of sqrts coeff, expr = expr.as_content_primitive() vars = set() for a in Add.make_args(expr): for m in a.args_cnc()[0]: if m.is_number and (m.is_Pow and m.exp.is_Rational and m.exp.q == 2 or m is S.ImaginaryUnit): vars.add(m) # we only want radicals, so exclude Number handling; in this case # d will be evaluated d = collect_const(expr, *vars, Numbers=False) hit = expr != d if not evaluate: nrad = 0 # make the evaluated args canonical args = list(ordered(Add.make_args(d))) for i, m in enumerate(args): c, nc = m.args_cnc() for ci in c: # XXX should this be restricted to ci.is_number as above? if ci.is_Pow and ci.exp.is_Rational and ci.exp.q == 2 or \ ci is S.ImaginaryUnit: nrad += 1 break args[i] *= coeff if not (hit or nrad): args = [Add(*args)] return tuple(args), nrad return coeff * d
def _integrate(field=None): irreducibles = set() for poly in reducibles: for z in poly.free_symbols: if z in V: break # should this be: `irreducibles |= \ else: # set(root_factors(poly, z, filter=field))` continue # and the line below deleted? # | # V irreducibles |= set(root_factors(poly, z, filter=field)) log_coeffs, log_part = [], [] B = _symbols('B', len(irreducibles)) # Note: the ordering matters here for poly, b in reversed(list(ordered(zip(irreducibles, B)))): if poly.has(*V): poly_coeffs.append(b) log_part.append(b * log(poly)) # TODO: Currently it's better to use symbolic expressions here instead # of rational functions, because it's simpler and FracElement doesn't # give big speed improvement yet. This is because cancelation is slow # due to slow polynomial GCD algorithms. If this gets improved then # revise this code. candidate = poly_part / poly_denom + Add(*log_part) h = F - _derivation(candidate) / denom raw_numer = h.as_numer_denom()[0] # Rewrite raw_numer as a polynomial in K[coeffs][V] where K is a field # that we have to determine. We can't use simply atoms() because log(3), # sqrt(y) and similar expressions can appear, leading to non-trivial # domains. syms = set(poly_coeffs) | set(V) non_syms = set() def find_non_syms(expr): if expr.is_Integer or expr.is_Rational: pass # ignore trivial numbers elif expr in syms: pass # ignore variables elif not expr.has(*syms): non_syms.add(expr) elif expr.is_Add or expr.is_Mul or expr.is_Pow: list(map(find_non_syms, expr.args)) else: # TODO: Non-polynomial expression. This should have been # filtered out at an earlier stage. raise PolynomialError try: find_non_syms(raw_numer) except PolynomialError: return else: ground, _ = construct_domain(non_syms, field=True) coeff_ring = PolyRing(poly_coeffs, ground) ring = PolyRing(V, coeff_ring) numer = ring.from_expr(raw_numer) solution = solve_lin_sys(numer.coeffs(), coeff_ring) if solution is None: return else: solution = [(coeff_ring.symbols[coeff_ring.index(k)], v.as_expr()) for k, v in solution.items()] return candidate.subs(solution).subs( list(zip(poly_coeffs, [S.Zero] * len(poly_coeffs))))
def heurisch(f, x, rewrite=False, hints=None, mappings=None, retries=3, degree_offset=0, unnecessary_permutations=None): """ Compute indefinite integral using heuristic Risch algorithm. This is a heuristic approach to indefinite integration in finite terms using the extended heuristic (parallel) Risch algorithm, based on Manuel Bronstein's "Poor Man's Integrator" [1]_. The algorithm supports various classes of functions including transcendental elementary or special functions like Airy, Bessel, Whittaker and Lambert. Note that this algorithm is not a decision procedure. If it isn't able to compute the antiderivative for a given function, then this is not a proof that such a functions does not exist. One should use recursive Risch algorithm in such case. It's an open question if this algorithm can be made a full decision procedure. This is an internal integrator procedure. You should use toplevel 'integrate' function in most cases, as this procedure needs some preprocessing steps and otherwise may fail. Parameters ========== heurisch(f, x, rewrite=False, hints=None) f : Expr expression x : Symbol variable rewrite : Boolean, optional force rewrite 'f' in terms of 'tan' and 'tanh', default False. hints : None or list a list of functions that may appear in anti-derivate. If None (default) - no suggestions at all, if empty list - try to figure out. Examples ======== >>> from diofant import tan >>> from diofant.integrals.heurisch import heurisch >>> from diofant.abc import x, y >>> heurisch(y*tan(x), x) y*log(tan(x)**2 + 1)/2 References ========== .. [1] Manuel Bronstein's "Poor Man's Integrator", http://www-sop.inria.fr/cafe/Manuel.Bronstein/pmint/index.html .. [2] K. Geddes, L. Stefanus, On the Risch-Norman Integration Method and its Implementation in Maple, Proceedings of ISSAC'89, ACM Press, 212-217. .. [3] J. H. Davenport, On the Parallel Risch Algorithm (I), Proceedings of EUROCAM'82, LNCS 144, Springer, 144-157. .. [4] J. H. Davenport, On the Parallel Risch Algorithm (III): Use of Tangents, SIGSAM Bulletin 16 (1982), 3-6. .. [5] J. H. Davenport, B. M. Trager, On the Parallel Risch Algorithm (II), ACM Transactions on Mathematical Software 11 (1985), 356-362. See Also ======== diofant.integrals.integrals.Integral.doit diofant.integrals.integrals.Integral diofant.integrals.heurisch.components """ f = sympify(f) if x not in f.free_symbols: return f * x if not f.is_Add: indep, f = f.as_independent(x) else: indep = S.One rewritables = { (sin, cos, cot): tan, (sinh, cosh, coth): tanh, } if rewrite: for candidates, rule in rewritables.items(): f = f.rewrite(candidates, rule) else: for candidates in rewritables.keys(): if f.has(*candidates): break else: rewrite = True terms = components(f, x) if hints is not None: if not hints: a = Wild('a', exclude=[x]) b = Wild('b', exclude=[x]) c = Wild('c', exclude=[x]) for g in set(terms): # using copy of terms if g.is_Function: if g.func is li: M = g.args[0].match(a * x**b) if M is not None: terms.add( x * (li(M[a] * x**M[b]) - (M[a] * x**M[b])**(-1 / M[b]) * Ei( (M[b] + 1) * log(M[a] * x**M[b]) / M[b]))) # terms.add( x*(li(M[a]*x**M[b]) - (x**M[b])**(-1/M[b])*Ei((M[b]+1)*log(M[a]*x**M[b])/M[b])) ) # terms.add( x*(li(M[a]*x**M[b]) - x*Ei((M[b]+1)*log(M[a]*x**M[b])/M[b])) ) # terms.add( li(M[a]*x**M[b]) - Ei((M[b]+1)*log(M[a]*x**M[b])/M[b]) ) elif g.is_Pow: if g.base is S.Exp1: M = g.exp.match(a * x**2) if M is not None: if M[a].is_positive: terms.add(erfi(sqrt(M[a]) * x)) else: # M[a].is_negative or unknown terms.add(erf(sqrt(-M[a]) * x)) M = g.exp.match(a * x**2 + b * x + c) if M is not None: if M[a].is_positive: terms.add( sqrt(pi / 4 * (-M[a])) * exp(M[c] - M[b]**2 / (4 * M[a])) * erfi( sqrt(M[a]) * x + M[b] / (2 * sqrt(M[a])))) elif M[a].is_negative: terms.add( sqrt(pi / 4 * (-M[a])) * exp(M[c] - M[b]**2 / (4 * M[a])) * erf( sqrt(-M[a]) * x - M[b] / (2 * sqrt(-M[a])))) M = g.exp.match(a * log(x)**2) if M is not None: if M[a].is_positive: terms.add( erfi( sqrt(M[a]) * log(x) + 1 / (2 * sqrt(M[a])))) if M[a].is_negative: terms.add( erf( sqrt(-M[a]) * log(x) - 1 / (2 * sqrt(-M[a])))) elif g.exp.is_Rational and g.exp.q == 2: M = g.base.match(a * x**2 + b) if M is not None and M[b].is_positive: if M[a].is_positive: terms.add(asinh(sqrt(M[a] / M[b]) * x)) elif M[a].is_negative: terms.add(asin(sqrt(-M[a] / M[b]) * x)) M = g.base.match(a * x**2 - b) if M is not None and M[b].is_positive: if M[a].is_positive: terms.add(acosh(sqrt(M[a] / M[b]) * x)) elif M[a].is_negative: terms.add((-M[b] / 2 * sqrt(-M[a]) * atan( sqrt(-M[a]) * x / sqrt(M[a] * x**2 - M[b])) )) else: terms |= set(hints) for g in set(terms): # using copy of terms terms |= components(cancel(g.diff(x)), x) # TODO: caching is significant factor for why permutations work at all. Change this. V = _symbols('x', len(terms)) # sort mapping expressions from largest to smallest (last is always x). mapping = list( reversed( list( zip(*ordered( # [(a[0].as_independent(x)[1], a) for a in zip(terms, V)])))[1])) # rev_mapping = {v: k for k, v in mapping} # if mappings is None: # # optimizing the number of permutations of mapping # assert mapping[-1][0] == x # if not, find it and correct this comment unnecessary_permutations = [mapping.pop(-1)] mappings = permutations(mapping) else: unnecessary_permutations = unnecessary_permutations or [] def _substitute(expr): return expr.subs(mapping) for mapping in mappings: mapping = list(mapping) mapping = mapping + unnecessary_permutations diffs = [_substitute(cancel(g.diff(x))) for g in terms] denoms = [g.as_numer_denom()[1] for g in diffs] if all(h.is_polynomial(*V) for h in denoms) and _substitute(f).is_rational_function(*V): denom = reduce(lambda p, q: lcm(p, q, *V), denoms) break else: if not rewrite: result = heurisch( f, x, rewrite=True, hints=hints, unnecessary_permutations=unnecessary_permutations) if result is not None: return indep * result return numers = [cancel(denom * g) for g in diffs] def _derivation(h): return Add(*[d * h.diff(v) for d, v in zip(numers, V)]) def _deflation(p): for y in V: if not p.has(y): continue if _derivation(p) is not S.Zero: c, q = p.as_poly(y).primitive() return _deflation(c) * gcd(q, q.diff(y)).as_expr() else: return p def _splitter(p): for y in V: if not p.has(y): continue if _derivation(y) is not S.Zero: c, q = p.as_poly(y).primitive() q = q.as_expr() h = gcd(q, _derivation(q), y) s = quo(h, gcd(q, q.diff(y), y), y) c_split = _splitter(c) if s.as_poly(y).degree() == 0: return c_split[0], q * c_split[1] q_split = _splitter(cancel(q / s)) return c_split[0] * q_split[0] * s, c_split[1] * q_split[1] else: return S.One, p special = {} for term in terms: if term.is_Function: if term.func is tan: special[1 + _substitute(term)**2] = False elif term.func is tanh: special[1 + _substitute(term)] = False special[1 - _substitute(term)] = False elif term.func is LambertW: special[_substitute(term)] = True F = _substitute(f) P, Q = F.as_numer_denom() u_split = _splitter(denom) v_split = _splitter(Q) polys = set(list(v_split) + [u_split[0]] + list(special.keys())) s = u_split[0] * Mul(*[k for k, v in special.items() if v]) polified = [p.as_poly(*V) for p in [s, P, Q]] if None in polified: return # --- definitions for _integrate --- a, b, c = [p.total_degree() for p in polified] poly_denom = (s * v_split[0] * _deflation(v_split[1])).as_expr() def _exponent(g): if g.is_Pow: if g.exp.is_Rational and g.exp.q != 1: if g.exp.p > 0: return g.exp.p + g.exp.q - 1 else: return abs(g.exp.p + g.exp.q) else: return 1 elif not g.is_Atom and g.args: return max([_exponent(h) for h in g.args]) else: return 1 A, B = _exponent(f), a + max(b, c) if A > 1 and B > 1: monoms = itermonomials(V, A + B - 1 + degree_offset) else: monoms = itermonomials(V, A + B + degree_offset) poly_coeffs = _symbols('A', len(monoms)) poly_part = Add(*[ poly_coeffs[i] * monomial for i, monomial in enumerate(ordered(monoms)) ]) reducibles = set() for poly in polys: if poly.has(*V): try: factorization = factor(poly, greedy=True) except PolynomialError: factorization = poly factorization = poly if factorization.is_Mul: reducibles |= set(factorization.args) else: reducibles.add(factorization) def _integrate(field=None): irreducibles = set() for poly in reducibles: for z in poly.free_symbols: if z in V: break # should this be: `irreducibles |= \ else: # set(root_factors(poly, z, filter=field))` continue # and the line below deleted? # | # V irreducibles |= set(root_factors(poly, z, filter=field)) log_coeffs, log_part = [], [] B = _symbols('B', len(irreducibles)) # Note: the ordering matters here for poly, b in reversed(list(ordered(zip(irreducibles, B)))): if poly.has(*V): poly_coeffs.append(b) log_part.append(b * log(poly)) # TODO: Currently it's better to use symbolic expressions here instead # of rational functions, because it's simpler and FracElement doesn't # give big speed improvement yet. This is because cancelation is slow # due to slow polynomial GCD algorithms. If this gets improved then # revise this code. candidate = poly_part / poly_denom + Add(*log_part) h = F - _derivation(candidate) / denom raw_numer = h.as_numer_denom()[0] # Rewrite raw_numer as a polynomial in K[coeffs][V] where K is a field # that we have to determine. We can't use simply atoms() because log(3), # sqrt(y) and similar expressions can appear, leading to non-trivial # domains. syms = set(poly_coeffs) | set(V) non_syms = set() def find_non_syms(expr): if expr.is_Integer or expr.is_Rational: pass # ignore trivial numbers elif expr in syms: pass # ignore variables elif not expr.has(*syms): non_syms.add(expr) elif expr.is_Add or expr.is_Mul or expr.is_Pow: list(map(find_non_syms, expr.args)) else: # TODO: Non-polynomial expression. This should have been # filtered out at an earlier stage. raise PolynomialError try: find_non_syms(raw_numer) except PolynomialError: return else: ground, _ = construct_domain(non_syms, field=True) coeff_ring = PolyRing(poly_coeffs, ground) ring = PolyRing(V, coeff_ring) numer = ring.from_expr(raw_numer) solution = solve_lin_sys(numer.coeffs(), coeff_ring) if solution is None: return else: solution = [(coeff_ring.symbols[coeff_ring.index(k)], v.as_expr()) for k, v in solution.items()] return candidate.subs(solution).subs( list(zip(poly_coeffs, [S.Zero] * len(poly_coeffs)))) if not (F.free_symbols - set(V)): solution = _integrate('Q') if solution is None: solution = _integrate() else: solution = _integrate() if solution is not None: antideriv = solution.subs(rev_mapping) antideriv = cancel(antideriv).expand(force=True) if antideriv.is_Add: antideriv = antideriv.as_independent(x)[1] return indep * antideriv else: if retries >= 0: result = heurisch( f, x, mappings=mappings, rewrite=rewrite, hints=hints, retries=retries - 1, unnecessary_permutations=unnecessary_permutations) if result is not None: return indep * result return
def f(rv): if not (rv.is_Add or rv.is_Mul): return rv def gooda(a): # bool to tell whether the leading ``a`` in ``a*log(x)`` # could appear as log(x**a) return (a is not S.NegativeOne and # -1 *could* go, but we disallow (a.is_extended_real or force and a.is_extended_real is not False)) def goodlog(l): # bool to tell whether log ``l``'s argument can combine with others a = l.args[0] return a.is_positive or force and a.is_nonpositive is not False other = [] logs = [] log1 = defaultdict(list) for a in Add.make_args(rv): if a.func is log and goodlog(a): log1[()].append(([], a)) elif not a.is_Mul: other.append(a) else: ot = [] co = [] lo = [] for ai in a.args: if ai.is_Rational and ai < 0: ot.append(S.NegativeOne) co.append(-ai) elif ai.func is log and goodlog(ai): lo.append(ai) elif gooda(ai): co.append(ai) else: ot.append(ai) if len(lo) > 1: logs.append((ot, co, lo)) elif lo: log1[tuple(ot)].append((co, lo[0])) else: other.append(a) # if there is only one log at each coefficient and none have # an exponent to place inside the log then there is nothing to do if not logs and all( len(log1[k]) == 1 and log1[k][0] == [] for k in log1): return rv # collapse multi-logs as far as possible in a canonical way # TODO: see if x*log(a)+x*log(a)*log(b) -> x*log(a)*(1+log(b))? # -- in this case, it's unambiguous, but if it were were a log(c) in # each term then it's arbitrary whether they are grouped by log(a) or # by log(c). So for now, just leave this alone; it's probably better to # let the user decide for o, e, l in logs: l = list(ordered(l)) e = log(l.pop(0).args[0]**Mul(*e)) while l: li = l.pop(0) e = log(li.args[0]**e) c, l = Mul(*o), e if l.func is log: # it should be, but check to be sure log1[(c, )].append(([], l)) else: other.append(c * l) # logs that have the same coefficient can multiply for k in list(log1.keys()): log1[Mul(*k)] = log( logcombine(Mul(*[l.args[0]**Mul(*c) for c, l in log1.pop(k)]), force=force)) # logs that have oppositely signed coefficients can divide for k in ordered(list(log1.keys())): if k not in log1: # already popped as -k continue if -k in log1: # figure out which has the minus sign; the one with # more op counts should be the one num, den = k, -k if num.count_ops() > den.count_ops(): num, den = den, num other.append( num * log(log1.pop(num).args[0] / log1.pop(den).args[0])) else: other.append(k * log1.pop(k)) return Add(*other)
def rewrite(e, x, w): """ Rewrites expression in terms of the most rapidly varying subexpression. Parameters ========== e : Expr an expression x : Symbol variable of the `e` w : Symbol The symbol which is going to be used for substitution in place of the most rapidly varying in `x` subexpression. Returns ======= tuple A pair: rewritten (in `w`) expression and `\log(w)`. Examples ======== >>> from diofant import Symbol, exp >>> x = Symbol('x', real=True, positive=True) >>> m = Symbol('m', real=True, positive=True) >>> rewrite(exp(x), x, m) (1/m, -x) >>> rewrite(exp(x)*log(log(exp(x))), x, m) (log(x)/m, -x) """ Omega = mrv(e, x) if not Omega: return e, None # e really does not depend on x assert all(e.has(t) for t in Omega) if x in Omega: # Moving up in the asymptotical scale: with evaluate(False): e = e.xreplace({x: exp(x)}) Omega = {s.xreplace({x: exp(x)}) for s in Omega} Omega = list(ordered(Omega, keys=lambda a: -len(mrv(a, x)))) for g in Omega: sig = sign(g.exp, x) if sig not in (1, -1): raise NotImplementedError('Result depends on the sign of %s' % sig) if sig == 1: w = 1 / w # if g goes to oo, substitute 1/w # Rewrite and substitute subexpressions in the Omega. for a in Omega: c = limitinf(a.exp / g.exp, x) b = exp(a.exp - c * g.exp) * w**c # exponential must never be expanded here with evaluate(False): e = e.xreplace({a: b}) return e, -sig * g.exp