Buckets:
MisterAI/LocalAI_Demo_backends / cpu-diffusers.upgrade-tmp /venv /lib /python3.10 /site-packages /sympy /simplify /simplify.py
| from __future__ import annotations | |
| from typing import overload | |
| from collections import defaultdict | |
| from sympy.concrete.products import Product | |
| from sympy.concrete.summations import Sum | |
| from sympy.core import (Basic, S, Add, Mul, Pow, Symbol, sympify, | |
| expand_func, Function, Dummy, Expr, factor_terms, | |
| expand_power_exp, Eq) | |
| from sympy.core.exprtools import factor_nc | |
| from sympy.core.parameters import global_parameters | |
| from sympy.core.function import (expand_log, count_ops, _mexpand, | |
| nfloat, expand_mul, expand) | |
| from sympy.core.numbers import Float, I, pi, Rational, equal_valued | |
| from sympy.core.relational import Relational | |
| from sympy.core.rules import Transform | |
| from sympy.core.sorting import ordered | |
| from sympy.core.sympify import _sympify | |
| from sympy.core.traversal import bottom_up as _bottom_up, walk as _walk | |
| from sympy.functions import gamma, exp, sqrt, log, exp_polar, re | |
| from sympy.functions.combinatorial.factorials import CombinatorialFunction | |
| from sympy.functions.elementary.complexes import unpolarify, Abs, sign | |
| from sympy.functions.elementary.exponential import ExpBase | |
| from sympy.functions.elementary.hyperbolic import HyperbolicFunction | |
| from sympy.functions.elementary.integers import ceiling | |
| from sympy.functions.elementary.piecewise import (Piecewise, piecewise_fold, | |
| piecewise_simplify) | |
| from sympy.functions.elementary.trigonometric import TrigonometricFunction | |
| from sympy.functions.special.bessel import (BesselBase, besselj, besseli, | |
| besselk, bessely, jn) | |
| from sympy.functions.special.tensor_functions import KroneckerDelta | |
| from sympy.integrals.integrals import Integral | |
| from sympy.logic.boolalg import Boolean | |
| from sympy.matrices.expressions import (MatrixExpr, MatAdd, MatMul, | |
| MatPow, MatrixSymbol) | |
| from sympy.polys import together, cancel, factor | |
| from sympy.polys.numberfields.minpoly import _is_sum_surds, _minimal_polynomial_sq | |
| from sympy.sets.sets import Set | |
| from sympy.simplify.combsimp import combsimp | |
| from sympy.simplify.cse_opts import sub_pre, sub_post | |
| from sympy.simplify.hyperexpand import hyperexpand | |
| from sympy.simplify.powsimp import powsimp | |
| from sympy.simplify.radsimp import radsimp, fraction, collect_abs | |
| from sympy.simplify.sqrtdenest import sqrtdenest | |
| from sympy.simplify.trigsimp import trigsimp, exptrigsimp | |
| from sympy.utilities.decorator import deprecated | |
| from sympy.utilities.iterables import has_variety, sift, subsets, iterable | |
| from sympy.utilities.misc import as_int | |
| import mpmath | |
| def separatevars(expr, symbols=[], dict=False, force=False): | |
| """ | |
| Separates variables in an expression, if possible. By | |
| default, it separates with respect to all symbols in an | |
| expression and collects constant coefficients that are | |
| independent of symbols. | |
| Explanation | |
| =========== | |
| If ``dict=True`` then the separated terms will be returned | |
| in a dictionary keyed to their corresponding symbols. | |
| By default, all symbols in the expression will appear as | |
| keys; if symbols are provided, then all those symbols will | |
| be used as keys, and any terms in the expression containing | |
| other symbols or non-symbols will be returned keyed to the | |
| string 'coeff'. (Passing None for symbols will return the | |
| expression in a dictionary keyed to 'coeff'.) | |
| If ``force=True``, then bases of powers will be separated regardless | |
| of assumptions on the symbols involved. | |
| Notes | |
| ===== | |
| The order of the factors is determined by Mul, so that the | |
| separated expressions may not necessarily be grouped together. | |
| Although factoring is necessary to separate variables in some | |
| expressions, it is not necessary in all cases, so one should not | |
| count on the returned factors being factored. | |
| Examples | |
| ======== | |
| >>> from sympy.abc import x, y, z, alpha | |
| >>> from sympy import separatevars, sin | |
| >>> separatevars((x*y)**y) | |
| (x*y)**y | |
| >>> separatevars((x*y)**y, force=True) | |
| x**y*y**y | |
| >>> e = 2*x**2*z*sin(y)+2*z*x**2 | |
| >>> separatevars(e) | |
| 2*x**2*z*(sin(y) + 1) | |
| >>> separatevars(e, symbols=(x, y), dict=True) | |
| {'coeff': 2*z, x: x**2, y: sin(y) + 1} | |
| >>> separatevars(e, [x, y, alpha], dict=True) | |
| {'coeff': 2*z, alpha: 1, x: x**2, y: sin(y) + 1} | |
| If the expression is not really separable, or is only partially | |
| separable, separatevars will do the best it can to separate it | |
| by using factoring. | |
| >>> separatevars(x + x*y - 3*x**2) | |
| -x*(3*x - y - 1) | |
| If the expression is not separable then expr is returned unchanged | |
| or (if dict=True) then None is returned. | |
| >>> eq = 2*x + y*sin(x) | |
| >>> separatevars(eq) == eq | |
| True | |
| >>> separatevars(2*x + y*sin(x), symbols=(x, y), dict=True) is None | |
| True | |
| """ | |
| expr = sympify(expr) | |
| if dict: | |
| return _separatevars_dict(_separatevars(expr, force), symbols) | |
| else: | |
| return _separatevars(expr, force) | |
| def _separatevars(expr, force): | |
| if isinstance(expr, Abs): | |
| arg = expr.args[0] | |
| if arg.is_Mul and not arg.is_number: | |
| s = separatevars(arg, dict=True, force=force) | |
| if s is not None: | |
| return Mul(*map(expr.func, s.values())) | |
| else: | |
| return expr | |
| if len(expr.free_symbols) < 2: | |
| return expr | |
| # don't destroy a Mul since much of the work may already be done | |
| if expr.is_Mul: | |
| args = list(expr.args) | |
| changed = False | |
| for i, a in enumerate(args): | |
| args[i] = separatevars(a, force) | |
| changed = changed or args[i] != a | |
| if changed: | |
| expr = expr.func(*args) | |
| return expr | |
| # get a Pow ready for expansion | |
| if expr.is_Pow and expr.base != S.Exp1: | |
| expr = Pow(separatevars(expr.base, force=force), expr.exp) | |
| # First try other expansion methods | |
| expr = expr.expand(mul=False, multinomial=False, force=force) | |
| _expr, reps = posify(expr) if force else (expr, {}) | |
| expr = factor(_expr).subs(reps) | |
| if not expr.is_Add: | |
| return expr | |
| # Find any common coefficients to pull out | |
| args = list(expr.args) | |
| commonc = args[0].args_cnc(cset=True, warn=False)[0] | |
| for i in args[1:]: | |
| commonc &= i.args_cnc(cset=True, warn=False)[0] | |
| commonc = Mul(*commonc) | |
| commonc = commonc.as_coeff_Mul()[1] # ignore constants | |
| commonc_set = commonc.args_cnc(cset=True, warn=False)[0] | |
| # remove them | |
| for i, a in enumerate(args): | |
| c, nc = a.args_cnc(cset=True, warn=False) | |
| c = c - commonc_set | |
| args[i] = Mul(*c)*Mul(*nc) | |
| nonsepar = Add(*args) | |
| if len(nonsepar.free_symbols) > 1: | |
| _expr = nonsepar | |
| _expr, reps = posify(_expr) if force else (_expr, {}) | |
| _expr = (factor(_expr)).subs(reps) | |
| if not _expr.is_Add: | |
| nonsepar = _expr | |
| return commonc*nonsepar | |
| def _separatevars_dict(expr, symbols): | |
| if symbols: | |
| if not all(t.is_Atom for t in symbols): | |
| raise ValueError("symbols must be Atoms.") | |
| symbols = list(symbols) | |
| elif symbols is None: | |
| return {'coeff': expr} | |
| else: | |
| symbols = list(expr.free_symbols) | |
| if not symbols: | |
| return None | |
| ret = {i: [] for i in symbols + ['coeff']} | |
| for i in Mul.make_args(expr): | |
| expsym = i.free_symbols | |
| intersection = set(symbols).intersection(expsym) | |
| if len(intersection) > 1: | |
| return None | |
| if len(intersection) == 0: | |
| # There are no symbols, so it is part of the coefficient | |
| ret['coeff'].append(i) | |
| else: | |
| ret[intersection.pop()].append(i) | |
| # rebuild | |
| for k, v in ret.items(): | |
| ret[k] = Mul(*v) | |
| return ret | |
| def posify(eq): | |
| """Return ``eq`` (with generic symbols made positive) and a | |
| dictionary containing the mapping between the old and new | |
| symbols. | |
| Explanation | |
| =========== | |
| Any symbol that has positive=None will be replaced with a positive dummy | |
| symbol having the same name. This replacement will allow more symbolic | |
| processing of expressions, especially those involving powers and | |
| logarithms. | |
| A dictionary that can be sent to subs to restore ``eq`` to its original | |
| symbols is also returned. | |
| >>> from sympy import posify, Symbol, log, solve | |
| >>> from sympy.abc import x | |
| >>> posify(x + Symbol('p', positive=True) + Symbol('n', negative=True)) | |
| (_x + n + p, {_x: x}) | |
| >>> eq = 1/x | |
| >>> log(eq).expand() | |
| log(1/x) | |
| >>> log(posify(eq)[0]).expand() | |
| -log(_x) | |
| >>> p, rep = posify(eq) | |
| >>> log(p).expand().subs(rep) | |
| -log(x) | |
| It is possible to apply the same transformations to an iterable | |
| of expressions: | |
| >>> eq = x**2 - 4 | |
| >>> solve(eq, x) | |
| [-2, 2] | |
| >>> eq_x, reps = posify([eq, x]); eq_x | |
| [_x**2 - 4, _x] | |
| >>> solve(*eq_x) | |
| [2] | |
| """ | |
| eq = sympify(eq) | |
| if not isinstance(eq, Basic) and iterable(eq): | |
| f = type(eq) | |
| eq = list(eq) | |
| syms = set() | |
| for e in eq: | |
| syms = syms.union(e.atoms(Symbol)) | |
| reps = {} | |
| for s in syms: | |
| reps.update({v: k for k, v in posify(s)[1].items()}) | |
| for i, e in enumerate(eq): | |
| eq[i] = e.subs(reps) | |
| return f(eq), {r: s for s, r in reps.items()} | |
| reps = {s: Dummy(s.name, positive=True, **s.assumptions0) | |
| for s in eq.free_symbols if s.is_positive is None} | |
| eq = eq.subs(reps) | |
| return eq, {r: s for s, r in reps.items()} | |
| def hypersimp(f, k): | |
| """Given combinatorial term f(k) simplify its consecutive term ratio | |
| i.e. f(k+1)/f(k). The input term can be composed of functions and | |
| integer sequences which have equivalent representation in terms | |
| of gamma special function. | |
| Explanation | |
| =========== | |
| The algorithm performs three basic steps: | |
| 1. Rewrite all functions in terms of gamma, if possible. | |
| 2. Rewrite all occurrences of gamma in terms of products | |
| of gamma and rising factorial with integer, absolute | |
| constant exponent. | |
| 3. Perform simplification of nested fractions, powers | |
| and if the resulting expression is a quotient of | |
| polynomials, reduce their total degree. | |
| If f(k) is hypergeometric then as result we arrive with a | |
| quotient of polynomials of minimal degree. Otherwise None | |
| is returned. | |
| For more information on the implemented algorithm refer to: | |
| 1. W. Koepf, Algorithms for m-fold Hypergeometric Summation, | |
| Journal of Symbolic Computation (1995) 20, 399-417 | |
| """ | |
| f = sympify(f) | |
| g = f.subs(k, k + 1) / f | |
| g = g.rewrite(gamma) | |
| if g.has(Piecewise): | |
| g = piecewise_fold(g) | |
| g = g.args[-1][0] | |
| g = expand_func(g) | |
| g = powsimp(g, deep=True, combine='exp') | |
| if g.is_rational_function(k): | |
| return simplify(g, ratio=S.Infinity) | |
| else: | |
| return None | |
| def hypersimilar(f, g, k): | |
| """ | |
| Returns True if ``f`` and ``g`` are hyper-similar. | |
| Explanation | |
| =========== | |
| Similarity in hypergeometric sense means that a quotient of | |
| f(k) and g(k) is a rational function in ``k``. This procedure | |
| is useful in solving recurrence relations. | |
| For more information see hypersimp(). | |
| """ | |
| f, g = list(map(sympify, (f, g))) | |
| h = (f/g).rewrite(gamma) | |
| h = h.expand(func=True, basic=False) | |
| return h.is_rational_function(k) | |
| def signsimp(expr, evaluate=None): | |
| """Make all Add sub-expressions canonical wrt sign. | |
| Explanation | |
| =========== | |
| If an Add subexpression, ``a``, can have a sign extracted, | |
| as determined by could_extract_minus_sign, it is replaced | |
| with Mul(-1, a, evaluate=False). This allows signs to be | |
| extracted from powers and products. | |
| Examples | |
| ======== | |
| >>> from sympy import signsimp, exp, symbols | |
| >>> from sympy.abc import x, y | |
| >>> i = symbols('i', odd=True) | |
| >>> n = -1 + 1/x | |
| >>> n/x/(-n)**2 - 1/n/x | |
| (-1 + 1/x)/(x*(1 - 1/x)**2) - 1/(x*(-1 + 1/x)) | |
| >>> signsimp(_) | |
| 0 | |
| >>> x*n + x*-n | |
| x*(-1 + 1/x) + x*(1 - 1/x) | |
| >>> signsimp(_) | |
| 0 | |
| Since powers automatically handle leading signs | |
| >>> (-2)**i | |
| -2**i | |
| signsimp can be used to put the base of a power with an integer | |
| exponent into canonical form: | |
| >>> n**i | |
| (-1 + 1/x)**i | |
| By default, signsimp does not leave behind any hollow simplification: | |
| if making an Add canonical wrt sign didn't change the expression, the | |
| original Add is restored. If this is not desired then the keyword | |
| ``evaluate`` can be set to False: | |
| >>> e = exp(y - x) | |
| >>> signsimp(e) == e | |
| True | |
| >>> signsimp(e, evaluate=False) | |
| exp(-(x - y)) | |
| """ | |
| if evaluate is None: | |
| evaluate = global_parameters.evaluate | |
| expr = sympify(expr) | |
| if not isinstance(expr, (Expr, Relational)) or expr.is_Atom: | |
| return expr | |
| # get rid of an pre-existing unevaluation regarding sign | |
| e = expr.replace(lambda x: x.is_Mul and -(-x) != x, lambda x: -(-x)) | |
| e = sub_post(sub_pre(e)) | |
| if not isinstance(e, (Expr, Relational)) or e.is_Atom: | |
| return e | |
| if e.is_Add: | |
| rv = e.func(*[signsimp(a) for a in e.args]) | |
| if not evaluate and isinstance(rv, Add | |
| ) and rv.could_extract_minus_sign(): | |
| return Mul(S.NegativeOne, -rv, evaluate=False) | |
| return rv | |
| if evaluate: | |
| e = e.replace(lambda x: x.is_Mul and -(-x) != x, lambda x: -(-x)) | |
| return e | |
| def simplify(expr: Expr, **kwargs) -> Expr: ... | |
| def simplify(expr: Boolean, **kwargs) -> Boolean: ... | |
| def simplify(expr: Set, **kwargs) -> Set: ... | |
| def simplify(expr: Basic, **kwargs) -> Basic: ... | |
| def simplify(expr, ratio=1.7, measure=count_ops, rational=False, inverse=False, doit=True, **kwargs): | |
| """Simplifies the given expression. | |
| Explanation | |
| =========== | |
| Simplification is not a well defined term and the exact strategies | |
| this function tries can change in the future versions of SymPy. If | |
| your algorithm relies on "simplification" (whatever it is), try to | |
| determine what you need exactly - is it powsimp()?, radsimp()?, | |
| together()?, logcombine()?, or something else? And use this particular | |
| function directly, because those are well defined and thus your algorithm | |
| will be robust. | |
| Nonetheless, especially for interactive use, or when you do not know | |
| anything about the structure of the expression, simplify() tries to apply | |
| intelligent heuristics to make the input expression "simpler". For | |
| example: | |
| >>> from sympy import simplify, cos, sin | |
| >>> from sympy.abc import x, y | |
| >>> a = (x + x**2)/(x*sin(y)**2 + x*cos(y)**2) | |
| >>> a | |
| (x**2 + x)/(x*sin(y)**2 + x*cos(y)**2) | |
| >>> simplify(a) | |
| x + 1 | |
| Note that we could have obtained the same result by using specific | |
| simplification functions: | |
| >>> from sympy import trigsimp, cancel | |
| >>> trigsimp(a) | |
| (x**2 + x)/x | |
| >>> cancel(_) | |
| x + 1 | |
| In some cases, applying :func:`simplify` may actually result in some more | |
| complicated expression. The default ``ratio=1.7`` prevents more extreme | |
| cases: if (result length)/(input length) > ratio, then input is returned | |
| unmodified. The ``measure`` parameter lets you specify the function used | |
| to determine how complex an expression is. The function should take a | |
| single argument as an expression and return a number such that if | |
| expression ``a`` is more complex than expression ``b``, then | |
| ``measure(a) > measure(b)``. The default measure function is | |
| :func:`~.count_ops`, which returns the total number of operations in the | |
| expression. | |
| For example, if ``ratio=1``, ``simplify`` output cannot be longer | |
| than input. | |
| :: | |
| >>> from sympy import sqrt, simplify, count_ops, oo | |
| >>> root = 1/(sqrt(2)+3) | |
| Since ``simplify(root)`` would result in a slightly longer expression, | |
| root is returned unchanged instead:: | |
| >>> simplify(root, ratio=1) == root | |
| True | |
| If ``ratio=oo``, simplify will be applied anyway:: | |
| >>> count_ops(simplify(root, ratio=oo)) > count_ops(root) | |
| True | |
| Note that the shortest expression is not necessary the simplest, so | |
| setting ``ratio`` to 1 may not be a good idea. | |
| Heuristically, the default value ``ratio=1.7`` seems like a reasonable | |
| choice. | |
| You can easily define your own measure function based on what you feel | |
| should represent the "size" or "complexity" of the input expression. Note | |
| that some choices, such as ``lambda expr: len(str(expr))`` may appear to be | |
| good metrics, but have other problems (in this case, the measure function | |
| may slow down simplify too much for very large expressions). If you do not | |
| know what a good metric would be, the default, ``count_ops``, is a good | |
| one. | |
| For example: | |
| >>> from sympy import symbols, log | |
| >>> a, b = symbols('a b', positive=True) | |
| >>> g = log(a) + log(b) + log(a)*log(1/b) | |
| >>> h = simplify(g) | |
| >>> h | |
| log(a*b**(1 - log(a))) | |
| >>> count_ops(g) | |
| 8 | |
| >>> count_ops(h) | |
| 5 | |
| So you can see that ``h`` is simpler than ``g`` using the count_ops metric. | |
| However, we may not like how ``simplify`` (in this case, using | |
| ``logcombine``) has created the ``b**(log(1/a) + 1)`` term. A simple way | |
| to reduce this would be to give more weight to powers as operations in | |
| ``count_ops``. We can do this by using the ``visual=True`` option: | |
| >>> print(count_ops(g, visual=True)) | |
| 2*ADD + DIV + 4*LOG + MUL | |
| >>> print(count_ops(h, visual=True)) | |
| 2*LOG + MUL + POW + SUB | |
| >>> from sympy import Symbol, S | |
| >>> def my_measure(expr): | |
| ... POW = Symbol('POW') | |
| ... # Discourage powers by giving POW a weight of 10 | |
| ... count = count_ops(expr, visual=True).subs(POW, 10) | |
| ... # Every other operation gets a weight of 1 (the default) | |
| ... count = count.replace(Symbol, type(S.One)) | |
| ... return count | |
| >>> my_measure(g) | |
| 8 | |
| >>> my_measure(h) | |
| 14 | |
| >>> 15./8 > 1.7 # 1.7 is the default ratio | |
| True | |
| >>> simplify(g, measure=my_measure) | |
| -log(a)*log(b) + log(a) + log(b) | |
| Note that because ``simplify()`` internally tries many different | |
| simplification strategies and then compares them using the measure | |
| function, we get a completely different result that is still different | |
| from the input expression by doing this. | |
| If ``rational=True``, Floats will be recast as Rationals before simplification. | |
| If ``rational=None``, Floats will be recast as Rationals but the result will | |
| be recast as Floats. If rational=False(default) then nothing will be done | |
| to the Floats. | |
| If ``inverse=True``, it will be assumed that a composition of inverse | |
| functions, such as sin and asin, can be cancelled in any order. | |
| For example, ``asin(sin(x))`` will yield ``x`` without checking whether | |
| x belongs to the set where this relation is true. The default is | |
| False. | |
| Note that ``simplify()`` automatically calls ``doit()`` on the final | |
| expression. You can avoid this behavior by passing ``doit=False`` as | |
| an argument. | |
| Also, it should be noted that simplifying a boolean expression is not | |
| well defined. If the expression prefers automatic evaluation (such as | |
| :obj:`~.Eq()` or :obj:`~.Or()`), simplification will return ``True`` or | |
| ``False`` if truth value can be determined. If the expression is not | |
| evaluated by default (such as :obj:`~.Predicate()`), simplification will | |
| not reduce it and you should use :func:`~.refine` or :func:`~.ask` | |
| function. This inconsistency will be resolved in future version. | |
| See Also | |
| ======== | |
| sympy.assumptions.refine.refine : Simplification using assumptions. | |
| sympy.assumptions.ask.ask : Query for boolean expressions using assumptions. | |
| """ | |
| def shorter(*choices): | |
| """ | |
| Return the choice that has the fewest ops. In case of a tie, | |
| the expression listed first is selected. | |
| """ | |
| if not has_variety(choices): | |
| return choices[0] | |
| return min(choices, key=measure) | |
| def done(e): | |
| rv = e.doit() if doit else e | |
| return shorter(rv, collect_abs(rv)) | |
| expr = sympify(expr, rational=rational) | |
| kwargs = { | |
| "ratio": kwargs.get('ratio', ratio), | |
| "measure": kwargs.get('measure', measure), | |
| "rational": kwargs.get('rational', rational), | |
| "inverse": kwargs.get('inverse', inverse), | |
| "doit": kwargs.get('doit', doit)} | |
| # no routine for Expr needs to check for is_zero | |
| if isinstance(expr, Expr) and expr.is_zero: | |
| return S.Zero if not expr.is_Number else expr | |
| _eval_simplify = getattr(expr, '_eval_simplify', None) | |
| if _eval_simplify is not None: | |
| return _eval_simplify(**kwargs) | |
| original_expr = expr = collect_abs(signsimp(expr)) | |
| if not isinstance(expr, Basic) or not expr.args: # XXX: temporary hack | |
| return expr | |
| if inverse and expr.has(Function): | |
| expr = inversecombine(expr) | |
| if not expr.args: # simplified to atomic | |
| return expr | |
| # do deep simplification | |
| handled = Add, Mul, Pow, ExpBase | |
| expr = expr.replace( | |
| # here, checking for x.args is not enough because Basic has | |
| # args but Basic does not always play well with replace, e.g. | |
| # when simultaneous is True found expressions will be masked | |
| # off with a Dummy but not all Basic objects in an expression | |
| # can be replaced with a Dummy | |
| lambda x: isinstance(x, Expr) and x.args and not isinstance( | |
| x, handled), | |
| lambda x: x.func(*[simplify(i, **kwargs) for i in x.args]), | |
| simultaneous=False) | |
| if not isinstance(expr, handled): | |
| return done(expr) | |
| if not expr.is_commutative: | |
| expr = nc_simplify(expr) | |
| # TODO: Apply different strategies, considering expression pattern: | |
| # is it a purely rational function? Is there any trigonometric function?... | |
| # See also https://github.com/sympy/sympy/pull/185. | |
| # rationalize Floats | |
| floats = False | |
| if rational is not False and expr.has(Float): | |
| floats = True | |
| expr = nsimplify(expr, rational=True) | |
| expr = _bottom_up(expr, lambda w: getattr(w, 'normal', lambda: w)()) | |
| expr = Mul(*powsimp(expr).as_content_primitive()) | |
| _e = cancel(expr) | |
| expr1 = shorter(_e, _mexpand(_e).cancel()) # issue 6829 | |
| expr2 = shorter(together(expr, deep=True), together(expr1, deep=True)) | |
| if ratio is S.Infinity: | |
| expr = expr2 | |
| else: | |
| expr = shorter(expr2, expr1, expr) | |
| if not isinstance(expr, Basic): # XXX: temporary hack | |
| return expr | |
| expr = factor_terms(expr, sign=False) | |
| # must come before `Piecewise` since this introduces more `Piecewise` terms | |
| if expr.has(sign): | |
| expr = expr.rewrite(Abs) | |
| # Deal with Piecewise separately to avoid recursive growth of expressions | |
| if expr.has(Piecewise): | |
| # Fold into a single Piecewise | |
| expr = piecewise_fold(expr) | |
| # Apply doit, if doit=True | |
| expr = done(expr) | |
| # Still a Piecewise? | |
| if expr.has(Piecewise): | |
| # Fold into a single Piecewise, in case doit lead to some | |
| # expressions being Piecewise | |
| expr = piecewise_fold(expr) | |
| # kroneckersimp also affects Piecewise | |
| if expr.has(KroneckerDelta): | |
| expr = kroneckersimp(expr) | |
| # Still a Piecewise? | |
| if expr.has(Piecewise): | |
| # Do not apply doit on the segments as it has already | |
| # been done above, but simplify | |
| expr = piecewise_simplify(expr, deep=True, doit=False) | |
| # Still a Piecewise? | |
| if expr.has(Piecewise): | |
| # Try factor common terms | |
| expr = shorter(expr, factor_terms(expr)) | |
| # As all expressions have been simplified above with the | |
| # complete simplify, nothing more needs to be done here | |
| return expr | |
| # hyperexpand automatically only works on hypergeometric terms | |
| # Do this after the Piecewise part to avoid recursive expansion | |
| expr = hyperexpand(expr) | |
| if expr.has(KroneckerDelta): | |
| expr = kroneckersimp(expr) | |
| if expr.has(BesselBase): | |
| expr = besselsimp(expr) | |
| if expr.has(TrigonometricFunction, HyperbolicFunction): | |
| expr = trigsimp(expr, deep=True) | |
| if expr.has(log): | |
| expr = shorter(expand_log(expr, deep=True), logcombine(expr)) | |
| if expr.has(CombinatorialFunction, gamma): | |
| # expression with gamma functions or non-integer arguments is | |
| # automatically passed to gammasimp | |
| expr = combsimp(expr) | |
| if expr.has(Sum): | |
| expr = sum_simplify(expr, **kwargs) | |
| if expr.has(Integral): | |
| expr = expr.xreplace({ | |
| i: factor_terms(i) for i in expr.atoms(Integral)}) | |
| if expr.has(Product): | |
| expr = product_simplify(expr, **kwargs) | |
| from sympy.physics.units import Quantity | |
| if expr.has(Quantity): | |
| from sympy.physics.units.util import quantity_simplify | |
| expr = quantity_simplify(expr) | |
| short = shorter(powsimp(expr, combine='exp', deep=True), powsimp(expr), expr) | |
| short = shorter(short, cancel(short)) | |
| short = shorter(short, factor_terms(short), expand_power_exp(expand_mul(short))) | |
| if short.has(TrigonometricFunction, HyperbolicFunction, ExpBase, exp): | |
| short = exptrigsimp(short) | |
| # get rid of hollow 2-arg Mul factorization | |
| hollow_mul = Transform( | |
| lambda x: Mul(*x.args), | |
| lambda x: | |
| x.is_Mul and | |
| len(x.args) == 2 and | |
| x.args[0].is_Number and | |
| x.args[1].is_Add and | |
| x.is_commutative) | |
| expr = short.xreplace(hollow_mul) | |
| numer, denom = expr.as_numer_denom() | |
| if denom.is_Add: | |
| n, d = fraction(radsimp(1/denom, symbolic=False, max_terms=1)) | |
| if n is not S.One: | |
| expr = (numer*n).expand()/d | |
| if expr.could_extract_minus_sign(): | |
| n, d = fraction(expr) | |
| if d != 0: | |
| expr = signsimp(-n/(-d)) | |
| if measure(expr) > ratio*measure(original_expr): | |
| expr = original_expr | |
| # restore floats | |
| if floats and rational is None: | |
| expr = nfloat(expr, exponent=False) | |
| return done(expr) | |
| def sum_simplify(s, **kwargs): | |
| """Main function for Sum simplification""" | |
| if not isinstance(s, Add): | |
| s = s.xreplace({a: sum_simplify(a, **kwargs) | |
| for a in s.atoms(Add) if a.has(Sum)}) | |
| s = expand(s) | |
| if not isinstance(s, Add): | |
| return s | |
| terms = s.args | |
| s_t = [] # Sum Terms | |
| o_t = [] # Other Terms | |
| for term in terms: | |
| sum_terms, other = sift(Mul.make_args(term), | |
| lambda i: isinstance(i, Sum), binary=True) | |
| if not sum_terms: | |
| o_t.append(term) | |
| continue | |
| other = [Mul(*other)] | |
| s_t.append(Mul(*(other + [s._eval_simplify(**kwargs) for s in sum_terms]))) | |
| result = Add(sum_combine(s_t), *o_t) | |
| return result | |
| def sum_combine(s_t): | |
| """Helper function for Sum simplification | |
| Attempts to simplify a list of sums, by combining limits / sum function's | |
| returns the simplified sum | |
| """ | |
| used = [False] * len(s_t) | |
| for method in range(2): | |
| for i, s_term1 in enumerate(s_t): | |
| if not used[i]: | |
| for j, s_term2 in enumerate(s_t): | |
| if not used[j] and i != j: | |
| temp = sum_add(s_term1, s_term2, method) | |
| if isinstance(temp, (Sum, Mul)): | |
| s_t[i] = temp | |
| s_term1 = s_t[i] | |
| used[j] = True | |
| result = S.Zero | |
| for i, s_term in enumerate(s_t): | |
| if not used[i]: | |
| result = Add(result, s_term) | |
| return result | |
| def factor_sum(self, limits=None, radical=False, clear=False, fraction=False, sign=True): | |
| """Return Sum with constant factors extracted. | |
| If ``limits`` is specified then ``self`` is the summand; the other | |
| keywords are passed to ``factor_terms``. | |
| Examples | |
| ======== | |
| >>> from sympy import Sum | |
| >>> from sympy.abc import x, y | |
| >>> from sympy.simplify.simplify import factor_sum | |
| >>> s = Sum(x*y, (x, 1, 3)) | |
| >>> factor_sum(s) | |
| y*Sum(x, (x, 1, 3)) | |
| >>> factor_sum(s.function, s.limits) | |
| y*Sum(x, (x, 1, 3)) | |
| """ | |
| # XXX deprecate in favor of direct call to factor_terms | |
| kwargs = {"radical": radical, "clear": clear, | |
| "fraction": fraction, "sign": sign} | |
| expr = Sum(self, *limits) if limits else self | |
| return factor_terms(expr, **kwargs) | |
| def sum_add(self, other, method=0): | |
| """Helper function for Sum simplification""" | |
| #we know this is something in terms of a constant * a sum | |
| #so we temporarily put the constants inside for simplification | |
| #then simplify the result | |
| def __refactor(val): | |
| args = Mul.make_args(val) | |
| sumv = next(x for x in args if isinstance(x, Sum)) | |
| constant = Mul(*[x for x in args if x != sumv]) | |
| return Sum(constant * sumv.function, *sumv.limits) | |
| if isinstance(self, Mul): | |
| rself = __refactor(self) | |
| else: | |
| rself = self | |
| if isinstance(other, Mul): | |
| rother = __refactor(other) | |
| else: | |
| rother = other | |
| if type(rself) is type(rother): | |
| if method == 0: | |
| if rself.limits == rother.limits: | |
| return factor_sum(Sum(rself.function + rother.function, *rself.limits)) | |
| elif method == 1: | |
| if simplify(rself.function - rother.function) == 0: | |
| if len(rself.limits) == len(rother.limits) == 1: | |
| i = rself.limits[0][0] | |
| x1 = rself.limits[0][1] | |
| y1 = rself.limits[0][2] | |
| j = rother.limits[0][0] | |
| x2 = rother.limits[0][1] | |
| y2 = rother.limits[0][2] | |
| if i == j: | |
| if x2 == y1 + 1: | |
| return factor_sum(Sum(rself.function, (i, x1, y2))) | |
| elif x1 == y2 + 1: | |
| return factor_sum(Sum(rself.function, (i, x2, y1))) | |
| return Add(self, other) | |
| def product_simplify(s, **kwargs): | |
| """Main function for Product simplification""" | |
| terms = Mul.make_args(s) | |
| p_t = [] # Product Terms | |
| o_t = [] # Other Terms | |
| deep = kwargs.get('deep', True) | |
| for term in terms: | |
| if isinstance(term, Product): | |
| if deep: | |
| p_t.append(Product(term.function.simplify(**kwargs), | |
| *term.limits)) | |
| else: | |
| p_t.append(term) | |
| else: | |
| o_t.append(term) | |
| used = [False] * len(p_t) | |
| for method in range(2): | |
| for i, p_term1 in enumerate(p_t): | |
| if not used[i]: | |
| for j, p_term2 in enumerate(p_t): | |
| if not used[j] and i != j: | |
| tmp_prod = product_mul(p_term1, p_term2, method) | |
| if isinstance(tmp_prod, Product): | |
| p_t[i] = tmp_prod | |
| used[j] = True | |
| result = Mul(*o_t) | |
| for i, p_term in enumerate(p_t): | |
| if not used[i]: | |
| result = Mul(result, p_term) | |
| return result | |
| def product_mul(self, other, method=0): | |
| """Helper function for Product simplification""" | |
| if type(self) is type(other): | |
| if method == 0: | |
| if self.limits == other.limits: | |
| return Product(self.function * other.function, *self.limits) | |
| elif method == 1: | |
| if simplify(self.function - other.function) == 0: | |
| if len(self.limits) == len(other.limits) == 1: | |
| i = self.limits[0][0] | |
| x1 = self.limits[0][1] | |
| y1 = self.limits[0][2] | |
| j = other.limits[0][0] | |
| x2 = other.limits[0][1] | |
| y2 = other.limits[0][2] | |
| if i == j: | |
| if x2 == y1 + 1: | |
| return Product(self.function, (i, x1, y2)) | |
| elif x1 == y2 + 1: | |
| return Product(self.function, (i, x2, y1)) | |
| return Mul(self, other) | |
| def _nthroot_solve(p, n, prec): | |
| """ | |
| helper function for ``nthroot`` | |
| It denests ``p**Rational(1, n)`` using its minimal polynomial | |
| """ | |
| from sympy.solvers import solve | |
| while n % 2 == 0: | |
| p = sqrtdenest(sqrt(p)) | |
| n = n // 2 | |
| if n == 1: | |
| return p | |
| pn = p**Rational(1, n) | |
| x = Symbol('x') | |
| f = _minimal_polynomial_sq(p, n, x) | |
| if f is None: | |
| return None | |
| sols = solve(f, x) | |
| for sol in sols: | |
| if abs(sol - pn).n() < 1./10**prec: | |
| sol = sqrtdenest(sol) | |
| if _mexpand(sol**n) == p: | |
| return sol | |
| def logcombine(expr, force=False): | |
| """ | |
| Takes logarithms and combines them using the following rules: | |
| - log(x) + log(y) == log(x*y) if both are positive | |
| - a*log(x) == log(x**a) if x is positive and a is real | |
| If ``force`` is ``True`` then the assumptions above will be assumed to hold if | |
| there is no assumption already in place on a quantity. For example, if | |
| ``a`` is imaginary or the argument negative, force will not perform a | |
| combination but if ``a`` is a symbol with no assumptions the change will | |
| take place. | |
| Examples | |
| ======== | |
| >>> from sympy import Symbol, symbols, log, logcombine, I | |
| >>> from sympy.abc import a, x, y, z | |
| >>> logcombine(a*log(x) + log(y) - log(z)) | |
| a*log(x) + log(y) - log(z) | |
| >>> logcombine(a*log(x) + log(y) - log(z), force=True) | |
| log(x**a*y/z) | |
| >>> x,y,z = symbols('x,y,z', positive=True) | |
| >>> a = Symbol('a', real=True) | |
| >>> logcombine(a*log(x) + log(y) - log(z)) | |
| log(x**a*y/z) | |
| The transformation is limited to factors and/or terms that | |
| contain logs, so the result depends on the initial state of | |
| expansion: | |
| >>> eq = (2 + 3*I)*log(x) | |
| >>> logcombine(eq, force=True) == eq | |
| True | |
| >>> logcombine(eq.expand(), force=True) | |
| log(x**2) + I*log(x**3) | |
| See Also | |
| ======== | |
| posify: replace all symbols with symbols having positive assumptions | |
| sympy.core.function.expand_log: expand the logarithms of products | |
| and powers; the opposite of logcombine | |
| """ | |
| 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 isinstance(a, 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 isinstance(ai, 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 in other, put it with the | |
| # good logs | |
| if len(other) == 1 and isinstance(other[0], log): | |
| log1[()].append(([], other.pop())) | |
| # 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 isinstance(l, 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), evaluate=False) | |
| # 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], | |
| evaluate=False)) | |
| else: | |
| other.append(k*log1.pop(k)) | |
| return Add(*other) | |
| return _bottom_up(expr, f) | |
| def inversecombine(expr): | |
| """Simplify the composition of a function and its inverse. | |
| Explanation | |
| =========== | |
| No attention is paid to whether the inverse is a left inverse or a | |
| right inverse; thus, the result will in general not be equivalent | |
| to the original expression. | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.simplify import inversecombine | |
| >>> from sympy import asin, sin, log, exp | |
| >>> from sympy.abc import x | |
| >>> inversecombine(asin(sin(x))) | |
| x | |
| >>> inversecombine(2*log(exp(3*x))) | |
| 6*x | |
| """ | |
| def f(rv): | |
| if isinstance(rv, log): | |
| if isinstance(rv.args[0], exp) or (rv.args[0].is_Pow and rv.args[0].base == S.Exp1): | |
| rv = rv.args[0].exp | |
| elif rv.is_Function and hasattr(rv, "inverse"): | |
| if (len(rv.args) == 1 and len(rv.args[0].args) == 1 and | |
| isinstance(rv.args[0], rv.inverse(argindex=1))): | |
| rv = rv.args[0].args[0] | |
| if rv.is_Pow and rv.base == S.Exp1: | |
| if isinstance(rv.exp, log): | |
| rv = rv.exp.args[0] | |
| return rv | |
| return _bottom_up(expr, f) | |
| def kroneckersimp(expr): | |
| """ | |
| Simplify expressions with KroneckerDelta. | |
| The only simplification currently attempted is to identify multiplicative cancellation: | |
| Examples | |
| ======== | |
| >>> from sympy import KroneckerDelta, kroneckersimp | |
| >>> from sympy.abc import i | |
| >>> kroneckersimp(1 + KroneckerDelta(0, i) * KroneckerDelta(1, i)) | |
| 1 | |
| """ | |
| def args_cancel(args1, args2): | |
| for i1 in range(2): | |
| for i2 in range(2): | |
| a1 = args1[i1] | |
| a2 = args2[i2] | |
| a3 = args1[(i1 + 1) % 2] | |
| a4 = args2[(i2 + 1) % 2] | |
| if Eq(a1, a2) is S.true and Eq(a3, a4) is S.false: | |
| return True | |
| return False | |
| def cancel_kronecker_mul(m): | |
| args = m.args | |
| deltas = [a for a in args if isinstance(a, KroneckerDelta)] | |
| for delta1, delta2 in subsets(deltas, 2): | |
| args1 = delta1.args | |
| args2 = delta2.args | |
| if args_cancel(args1, args2): | |
| return S.Zero * m # In case of oo etc | |
| return m | |
| if not expr.has(KroneckerDelta): | |
| return expr | |
| if expr.has(Piecewise): | |
| expr = expr.rewrite(KroneckerDelta) | |
| newexpr = expr | |
| expr = None | |
| while newexpr != expr: | |
| expr = newexpr | |
| newexpr = expr.replace(lambda e: isinstance(e, Mul), cancel_kronecker_mul) | |
| return expr | |
| def besselsimp(expr): | |
| """ | |
| Simplify bessel-type functions. | |
| Explanation | |
| =========== | |
| This routine tries to simplify bessel-type functions. Currently it only | |
| works on the Bessel J and I functions, however. It works by looking at all | |
| such functions in turn, and eliminating factors of "I" and "-1" (actually | |
| their polar equivalents) in front of the argument. Then, functions of | |
| half-integer order are rewritten using trigonometric functions and | |
| functions of integer order (> 1) are rewritten using functions | |
| of low order. Finally, if the expression was changed, compute | |
| factorization of the result with factor(). | |
| >>> from sympy import besselj, besseli, besselsimp, polar_lift, I, S | |
| >>> from sympy.abc import z, nu | |
| >>> besselsimp(besselj(nu, z*polar_lift(-1))) | |
| exp(I*pi*nu)*besselj(nu, z) | |
| >>> besselsimp(besseli(nu, z*polar_lift(-I))) | |
| exp(-I*pi*nu/2)*besselj(nu, z) | |
| >>> besselsimp(besseli(S(-1)/2, z)) | |
| sqrt(2)*cosh(z)/(sqrt(pi)*sqrt(z)) | |
| >>> besselsimp(z*besseli(0, z) + z*(besseli(2, z))/2 + besseli(1, z)) | |
| 3*z*besseli(0, z)/2 | |
| """ | |
| # TODO | |
| # - better algorithm? | |
| # - simplify (cos(pi*b)*besselj(b,z) - besselj(-b,z))/sin(pi*b) ... | |
| # - use contiguity relations? | |
| def replacer(fro, to, factors): | |
| factors = set(factors) | |
| def repl(nu, z): | |
| if factors.intersection(Mul.make_args(z)): | |
| return to(nu, z) | |
| return fro(nu, z) | |
| return repl | |
| def torewrite(fro, to): | |
| def tofunc(nu, z): | |
| return fro(nu, z).rewrite(to) | |
| return tofunc | |
| def tominus(fro): | |
| def tofunc(nu, z): | |
| return exp(I*pi*nu)*fro(nu, exp_polar(-I*pi)*z) | |
| return tofunc | |
| orig_expr = expr | |
| ifactors = [I, exp_polar(I*pi/2), exp_polar(-I*pi/2)] | |
| expr = expr.replace( | |
| besselj, replacer(besselj, | |
| torewrite(besselj, besseli), ifactors)) | |
| expr = expr.replace( | |
| besseli, replacer(besseli, | |
| torewrite(besseli, besselj), ifactors)) | |
| minusfactors = [-1, exp_polar(I*pi)] | |
| expr = expr.replace( | |
| besselj, replacer(besselj, tominus(besselj), minusfactors)) | |
| expr = expr.replace( | |
| besseli, replacer(besseli, tominus(besseli), minusfactors)) | |
| z0 = Dummy('z') | |
| def expander(fro): | |
| def repl(nu, z): | |
| if (nu % 1) == S.Half: | |
| return simplify(trigsimp(unpolarify( | |
| fro(nu, z0).rewrite(besselj).rewrite(jn).expand( | |
| func=True)).subs(z0, z))) | |
| elif nu.is_Integer and nu > 1: | |
| return fro(nu, z).expand(func=True) | |
| return fro(nu, z) | |
| return repl | |
| expr = expr.replace(besselj, expander(besselj)) | |
| expr = expr.replace(bessely, expander(bessely)) | |
| expr = expr.replace(besseli, expander(besseli)) | |
| expr = expr.replace(besselk, expander(besselk)) | |
| def _bessel_simp_recursion(expr): | |
| def _use_recursion(bessel, expr): | |
| while True: | |
| bessels = expr.find(lambda x: isinstance(x, bessel)) | |
| try: | |
| for ba in sorted(bessels, key=lambda x: re(x.args[0])): | |
| a, x = ba.args | |
| bap1 = bessel(a+1, x) | |
| bap2 = bessel(a+2, x) | |
| if expr.has(bap1) and expr.has(bap2): | |
| expr = expr.subs(ba, 2*(a+1)/x*bap1 - bap2) | |
| break | |
| else: | |
| return expr | |
| except (ValueError, TypeError): | |
| return expr | |
| if expr.has(besselj): | |
| expr = _use_recursion(besselj, expr) | |
| if expr.has(bessely): | |
| expr = _use_recursion(bessely, expr) | |
| return expr | |
| expr = _bessel_simp_recursion(expr) | |
| if expr != orig_expr: | |
| expr = expr.factor() | |
| return expr | |
| def nthroot(expr, n, max_len=4, prec=15): | |
| """ | |
| Compute a real nth-root of a sum of surds. | |
| Parameters | |
| ========== | |
| expr : sum of surds | |
| n : integer | |
| max_len : maximum number of surds passed as constants to ``nsimplify`` | |
| Algorithm | |
| ========= | |
| First ``nsimplify`` is used to get a candidate root; if it is not a | |
| root the minimal polynomial is computed; the answer is one of its | |
| roots. | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.simplify import nthroot | |
| >>> from sympy import sqrt | |
| >>> nthroot(90 + 34*sqrt(7), 3) | |
| sqrt(7) + 3 | |
| """ | |
| expr = sympify(expr) | |
| n = sympify(n) | |
| p = expr**Rational(1, n) | |
| if not n.is_integer: | |
| return p | |
| if not _is_sum_surds(expr): | |
| return p | |
| surds = [] | |
| coeff_muls = [x.as_coeff_Mul() for x in expr.args] | |
| for x, y in coeff_muls: | |
| if not x.is_rational: | |
| return p | |
| if y is S.One: | |
| continue | |
| if not (y.is_Pow and y.exp == S.Half and y.base.is_integer): | |
| return p | |
| surds.append(y) | |
| surds.sort() | |
| surds = surds[:max_len] | |
| if expr < 0 and n % 2 == 1: | |
| p = (-expr)**Rational(1, n) | |
| a = nsimplify(p, constants=surds) | |
| res = a if _mexpand(a**n) == _mexpand(-expr) else p | |
| return -res | |
| a = nsimplify(p, constants=surds) | |
| if _mexpand(a) is not _mexpand(p) and _mexpand(a**n) == _mexpand(expr): | |
| return _mexpand(a) | |
| expr = _nthroot_solve(expr, n, prec) | |
| if expr is None: | |
| return p | |
| return expr | |
| def nsimplify(expr, constants=(), tolerance=None, full=False, rational=None, | |
| rational_conversion='base10'): | |
| """ | |
| Find a simple representation for a number or, if there are free symbols or | |
| if ``rational=True``, then replace Floats with their Rational equivalents. If | |
| no change is made and rational is not False then Floats will at least be | |
| converted to Rationals. | |
| Explanation | |
| =========== | |
| For numerical expressions, a simple formula that numerically matches the | |
| given numerical expression is sought (and the input should be possible | |
| to evalf to a precision of at least 30 digits). | |
| Optionally, a list of (rationally independent) constants to | |
| include in the formula may be given. | |
| A lower tolerance may be set to find less exact matches. If no tolerance | |
| is given then the least precise value will set the tolerance (e.g. Floats | |
| default to 15 digits of precision, so would be tolerance=10**-15). | |
| With ``full=True``, a more extensive search is performed | |
| (this is useful to find simpler numbers when the tolerance | |
| is set low). | |
| When converting to rational, if rational_conversion='base10' (the default), then | |
| convert floats to rationals using their base-10 (string) representation. | |
| When rational_conversion='exact' it uses the exact, base-2 representation. | |
| Examples | |
| ======== | |
| >>> from sympy import nsimplify, sqrt, GoldenRatio, exp, I, pi | |
| >>> nsimplify(4/(1+sqrt(5)), [GoldenRatio]) | |
| -2 + 2*GoldenRatio | |
| >>> nsimplify((1/(exp(3*pi*I/5)+1))) | |
| 1/2 - I*sqrt(sqrt(5)/10 + 1/4) | |
| >>> nsimplify(I**I, [pi]) | |
| exp(-pi/2) | |
| >>> nsimplify(pi, tolerance=0.01) | |
| 22/7 | |
| >>> nsimplify(0.333333333333333, rational=True, rational_conversion='exact') | |
| 6004799503160655/18014398509481984 | |
| >>> nsimplify(0.333333333333333, rational=True) | |
| 1/3 | |
| See Also | |
| ======== | |
| sympy.core.function.nfloat | |
| """ | |
| try: | |
| return sympify(as_int(expr)) | |
| except (TypeError, ValueError): | |
| pass | |
| expr = sympify(expr).xreplace({ | |
| Float('inf'): S.Infinity, | |
| Float('-inf'): S.NegativeInfinity, | |
| }) | |
| if expr is S.Infinity or expr is S.NegativeInfinity: | |
| return expr | |
| if rational or expr.free_symbols: | |
| return _real_to_rational(expr, tolerance, rational_conversion) | |
| # SymPy's default tolerance for Rationals is 15; other numbers may have | |
| # lower tolerances set, so use them to pick the largest tolerance if None | |
| # was given | |
| if tolerance is None: | |
| tolerance = 10**-min([15] + | |
| [mpmath.libmp.libmpf.prec_to_dps(n._prec) | |
| for n in expr.atoms(Float)]) | |
| # XXX should prec be set independent of tolerance or should it be computed | |
| # from tolerance? | |
| prec = 30 | |
| bprec = int(prec*3.33) | |
| constants_dict = {} | |
| for constant in constants: | |
| constant = sympify(constant) | |
| v = constant.evalf(prec) | |
| if not v.is_Float: | |
| raise ValueError("constants must be real-valued") | |
| constants_dict[str(constant)] = v._to_mpmath(bprec) | |
| exprval = expr.evalf(prec, chop=True) | |
| re, im = exprval.as_real_imag() | |
| # safety check to make sure that this evaluated to a number | |
| if not (re.is_Number and im.is_Number): | |
| return expr | |
| def nsimplify_real(x): | |
| orig = mpmath.mp.dps | |
| xv = x._to_mpmath(bprec) | |
| try: | |
| # We'll be happy with low precision if a simple fraction | |
| if not (tolerance or full): | |
| mpmath.mp.dps = 15 | |
| rat = mpmath.pslq([xv, 1]) | |
| if rat is not None: | |
| return Rational(-int(rat[1]), int(rat[0])) | |
| mpmath.mp.dps = prec | |
| newexpr = mpmath.identify(xv, constants=constants_dict, | |
| tol=tolerance, full=full) | |
| if not newexpr: | |
| raise ValueError | |
| if full: | |
| newexpr = newexpr[0] | |
| expr = sympify(newexpr) | |
| if x and not expr: # don't let x become 0 | |
| raise ValueError | |
| if expr.is_finite is False and xv not in [mpmath.inf, mpmath.ninf]: | |
| raise ValueError | |
| return expr | |
| finally: | |
| # even though there are returns above, this is executed | |
| # before leaving | |
| mpmath.mp.dps = orig | |
| try: | |
| if re: | |
| re = nsimplify_real(re) | |
| if im: | |
| im = nsimplify_real(im) | |
| except ValueError: | |
| if rational is None: | |
| return _real_to_rational(expr, rational_conversion=rational_conversion) | |
| return expr | |
| rv = re + im*S.ImaginaryUnit | |
| # if there was a change or rational is explicitly not wanted | |
| # return the value, else return the Rational representation | |
| if rv != expr or rational is False: | |
| return rv | |
| return _real_to_rational(expr, rational_conversion=rational_conversion) | |
| def _real_to_rational(expr, tolerance=None, rational_conversion='base10'): | |
| """ | |
| Replace all reals in expr with rationals. | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.simplify import _real_to_rational | |
| >>> from sympy.abc import x | |
| >>> _real_to_rational(.76 + .1*x**.5) | |
| sqrt(x)/10 + 19/25 | |
| If rational_conversion='base10', this uses the base-10 string. If | |
| rational_conversion='exact', the exact, base-2 representation is used. | |
| >>> _real_to_rational(0.333333333333333, rational_conversion='exact') | |
| 6004799503160655/18014398509481984 | |
| >>> _real_to_rational(0.333333333333333) | |
| 1/3 | |
| """ | |
| expr = _sympify(expr) | |
| inf = Float('inf') | |
| p = expr | |
| reps = {} | |
| reduce_num = None | |
| if tolerance is not None and tolerance < 1: | |
| reduce_num = ceiling(1/tolerance) | |
| for fl in p.atoms(Float): | |
| key = fl | |
| if reduce_num is not None: | |
| r = Rational(fl).limit_denominator(reduce_num) | |
| elif (tolerance is not None and tolerance >= 1 and | |
| fl.is_Integer is False): | |
| r = Rational(tolerance*round(fl/tolerance) | |
| ).limit_denominator(int(tolerance)) | |
| else: | |
| if rational_conversion == 'exact': | |
| r = Rational(fl) | |
| reps[key] = r | |
| continue | |
| elif rational_conversion != 'base10': | |
| raise ValueError("rational_conversion must be 'base10' or 'exact'") | |
| r = nsimplify(fl, rational=False) | |
| # e.g. log(3).n() -> log(3) instead of a Rational | |
| if fl and not r: | |
| r = Rational(fl) | |
| elif not r.is_Rational: | |
| if fl in (inf, -inf): | |
| r = S.ComplexInfinity | |
| elif fl < 0: | |
| fl = -fl | |
| d = Pow(10, int(mpmath.log(fl)/mpmath.log(10))) | |
| r = -Rational(str(fl/d))*d | |
| elif fl > 0: | |
| d = Pow(10, int(mpmath.log(fl)/mpmath.log(10))) | |
| r = Rational(str(fl/d))*d | |
| else: | |
| r = S.Zero | |
| reps[key] = r | |
| return p.subs(reps, simultaneous=True) | |
| def clear_coefficients(expr, rhs=S.Zero): | |
| """Return `p, r` where `p` is the expression obtained when Rational | |
| additive and multiplicative coefficients of `expr` have been stripped | |
| away in a naive fashion (i.e. without simplification). The operations | |
| needed to remove the coefficients will be applied to `rhs` and returned | |
| as `r`. | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.simplify import clear_coefficients | |
| >>> from sympy.abc import x, y | |
| >>> from sympy import Dummy | |
| >>> expr = 4*y*(6*x + 3) | |
| >>> clear_coefficients(expr - 2) | |
| (y*(2*x + 1), 1/6) | |
| When solving 2 or more expressions like `expr = a`, | |
| `expr = b`, etc..., it is advantageous to provide a Dummy symbol | |
| for `rhs` and simply replace it with `a`, `b`, etc... in `r`. | |
| >>> rhs = Dummy('rhs') | |
| >>> clear_coefficients(expr, rhs) | |
| (y*(2*x + 1), _rhs/12) | |
| >>> _[1].subs(rhs, 2) | |
| 1/6 | |
| """ | |
| was = None | |
| free = expr.free_symbols | |
| if expr.is_Rational: | |
| return (S.Zero, rhs - expr) | |
| while expr and was != expr: | |
| was = expr | |
| m, expr = ( | |
| expr.as_content_primitive() | |
| if free else | |
| factor_terms(expr).as_coeff_Mul(rational=True)) | |
| rhs /= m | |
| c, expr = expr.as_coeff_Add(rational=True) | |
| rhs -= c | |
| expr = signsimp(expr, evaluate = False) | |
| if expr.could_extract_minus_sign(): | |
| expr = -expr | |
| rhs = -rhs | |
| return expr, rhs | |
| def nc_simplify(expr, deep=True): | |
| ''' | |
| Simplify a non-commutative expression composed of multiplication | |
| and raising to a power by grouping repeated subterms into one power. | |
| Priority is given to simplifications that give the fewest number | |
| of arguments in the end (for example, in a*b*a*b*c*a*b*c simplifying | |
| to (a*b)**2*c*a*b*c gives 5 arguments while a*b*(a*b*c)**2 has 3). | |
| If ``expr`` is a sum of such terms, the sum of the simplified terms | |
| is returned. | |
| Keyword argument ``deep`` controls whether or not subexpressions | |
| nested deeper inside the main expression are simplified. See examples | |
| below. Setting `deep` to `False` can save time on nested expressions | |
| that do not need simplifying on all levels. | |
| Examples | |
| ======== | |
| >>> from sympy import symbols | |
| >>> from sympy.simplify.simplify import nc_simplify | |
| >>> a, b, c = symbols("a b c", commutative=False) | |
| >>> nc_simplify(a*b*a*b*c*a*b*c) | |
| a*b*(a*b*c)**2 | |
| >>> expr = a**2*b*a**4*b*a**4 | |
| >>> nc_simplify(expr) | |
| a**2*(b*a**4)**2 | |
| >>> nc_simplify(a*b*a*b*c**2*(a*b)**2*c**2) | |
| ((a*b)**2*c**2)**2 | |
| >>> nc_simplify(a*b*a*b + 2*a*c*a**2*c*a**2*c*a) | |
| (a*b)**2 + 2*(a*c*a)**3 | |
| >>> nc_simplify(b**-1*a**-1*(a*b)**2) | |
| a*b | |
| >>> nc_simplify(a**-1*b**-1*c*a) | |
| (b*a)**(-1)*c*a | |
| >>> expr = (a*b*a*b)**2*a*c*a*c | |
| >>> nc_simplify(expr) | |
| (a*b)**4*(a*c)**2 | |
| >>> nc_simplify(expr, deep=False) | |
| (a*b*a*b)**2*(a*c)**2 | |
| ''' | |
| if isinstance(expr, MatrixExpr): | |
| expr = expr.doit(inv_expand=False) | |
| _Add, _Mul, _Pow, _Symbol = MatAdd, MatMul, MatPow, MatrixSymbol | |
| else: | |
| _Add, _Mul, _Pow, _Symbol = Add, Mul, Pow, Symbol | |
| # =========== Auxiliary functions ======================== | |
| def _overlaps(args): | |
| # Calculate a list of lists m such that m[i][j] contains the lengths | |
| # of all possible overlaps between args[:i+1] and args[i+1+j:]. | |
| # An overlap is a suffix of the prefix that matches a prefix | |
| # of the suffix. | |
| # For example, let expr=c*a*b*a*b*a*b*a*b. Then m[3][0] contains | |
| # the lengths of overlaps of c*a*b*a*b with a*b*a*b. The overlaps | |
| # are a*b*a*b, a*b and the empty word so that m[3][0]=[4,2,0]. | |
| # All overlaps rather than only the longest one are recorded | |
| # because this information helps calculate other overlap lengths. | |
| m = [[([1, 0] if a == args[0] else [0]) for a in args[1:]]] | |
| for i in range(1, len(args)): | |
| overlaps = [] | |
| j = 0 | |
| for j in range(len(args) - i - 1): | |
| overlap = [] | |
| for v in m[i-1][j+1]: | |
| if j + i + 1 + v < len(args) and args[i] == args[j+i+1+v]: | |
| overlap.append(v + 1) | |
| overlap += [0] | |
| overlaps.append(overlap) | |
| m.append(overlaps) | |
| return m | |
| def _reduce_inverses(_args): | |
| # replace consecutive negative powers by an inverse | |
| # of a product of positive powers, e.g. a**-1*b**-1*c | |
| # will simplify to (a*b)**-1*c; | |
| # return that new args list and the number of negative | |
| # powers in it (inv_tot) | |
| inv_tot = 0 # total number of inverses | |
| inverses = [] | |
| args = [] | |
| for arg in _args: | |
| if isinstance(arg, _Pow) and arg.args[1].is_extended_negative: | |
| inverses = [arg**-1] + inverses | |
| inv_tot += 1 | |
| else: | |
| if len(inverses) == 1: | |
| args.append(inverses[0]**-1) | |
| elif len(inverses) > 1: | |
| args.append(_Pow(_Mul(*inverses), -1)) | |
| inv_tot -= len(inverses) - 1 | |
| inverses = [] | |
| args.append(arg) | |
| if inverses: | |
| args.append(_Pow(_Mul(*inverses), -1)) | |
| inv_tot -= len(inverses) - 1 | |
| return inv_tot, tuple(args) | |
| def get_score(s): | |
| # compute the number of arguments of s | |
| # (including in nested expressions) overall | |
| # but ignore exponents | |
| if isinstance(s, _Pow): | |
| return get_score(s.args[0]) | |
| elif isinstance(s, (_Add, _Mul)): | |
| return sum(get_score(a) for a in s.args) | |
| return 1 | |
| def compare(s, alt_s): | |
| # compare two possible simplifications and return a | |
| # "better" one | |
| if s != alt_s and get_score(alt_s) < get_score(s): | |
| return alt_s | |
| return s | |
| # ======================================================== | |
| if not isinstance(expr, (_Add, _Mul, _Pow)) or expr.is_commutative: | |
| return expr | |
| args = expr.args[:] | |
| if isinstance(expr, _Pow): | |
| if deep: | |
| return _Pow(nc_simplify(args[0]), args[1]).doit() | |
| else: | |
| return expr | |
| elif isinstance(expr, _Add): | |
| return _Add(*[nc_simplify(a, deep=deep) for a in args]).doit() | |
| else: | |
| # get the non-commutative part | |
| c_args, args = expr.args_cnc() | |
| com_coeff = Mul(*c_args) | |
| if not equal_valued(com_coeff, 1): | |
| return com_coeff*nc_simplify(expr/com_coeff, deep=deep) | |
| inv_tot, args = _reduce_inverses(args) | |
| # if most arguments are negative, work with the inverse | |
| # of the expression, e.g. a**-1*b*a**-1*c**-1 will become | |
| # (c*a*b**-1*a)**-1 at the end so can work with c*a*b**-1*a | |
| invert = False | |
| if inv_tot > len(args)/2: | |
| invert = True | |
| args = [a**-1 for a in args[::-1]] | |
| if deep: | |
| args = tuple(nc_simplify(a) for a in args) | |
| m = _overlaps(args) | |
| # simps will be {subterm: end} where `end` is the ending | |
| # index of a sequence of repetitions of subterm; | |
| # this is for not wasting time with subterms that are part | |
| # of longer, already considered sequences | |
| simps = {} | |
| post = 1 | |
| pre = 1 | |
| # the simplification coefficient is the number of | |
| # arguments by which contracting a given sequence | |
| # would reduce the word; e.g. in a*b*a*b*c*a*b*c, | |
| # contracting a*b*a*b to (a*b)**2 removes 3 arguments | |
| # while a*b*c*a*b*c to (a*b*c)**2 removes 6. It's | |
| # better to contract the latter so simplification | |
| # with a maximum simplification coefficient will be chosen | |
| max_simp_coeff = 0 | |
| simp = None # information about future simplification | |
| for i in range(1, len(args)): | |
| simp_coeff = 0 | |
| l = 0 # length of a subterm | |
| p = 0 # the power of a subterm | |
| if i < len(args) - 1: | |
| rep = m[i][0] | |
| start = i # starting index of the repeated sequence | |
| end = i+1 # ending index of the repeated sequence | |
| if i == len(args)-1 or rep == [0]: | |
| # no subterm is repeated at this stage, at least as | |
| # far as the arguments are concerned - there may be | |
| # a repetition if powers are taken into account | |
| if (isinstance(args[i], _Pow) and | |
| not isinstance(args[i].args[0], _Symbol)): | |
| subterm = args[i].args[0].args | |
| l = len(subterm) | |
| if args[i-l:i] == subterm: | |
| # e.g. a*b in a*b*(a*b)**2 is not repeated | |
| # in args (= [a, b, (a*b)**2]) but it | |
| # can be matched here | |
| p += 1 | |
| start -= l | |
| if args[i+1:i+1+l] == subterm: | |
| # e.g. a*b in (a*b)**2*a*b | |
| p += 1 | |
| end += l | |
| if p: | |
| p += args[i].args[1] | |
| else: | |
| continue | |
| else: | |
| l = rep[0] # length of the longest repeated subterm at this point | |
| start -= l - 1 | |
| subterm = args[start:end] | |
| p = 2 | |
| end += l | |
| if subterm in simps and simps[subterm] >= start: | |
| # the subterm is part of a sequence that | |
| # has already been considered | |
| continue | |
| # count how many times it's repeated | |
| while end < len(args): | |
| if l in m[end-1][0]: | |
| p += 1 | |
| end += l | |
| elif isinstance(args[end], _Pow) and args[end].args[0].args == subterm: | |
| # for cases like a*b*a*b*(a*b)**2*a*b | |
| p += args[end].args[1] | |
| end += 1 | |
| else: | |
| break | |
| # see if another match can be made, e.g. | |
| # for b*a**2 in b*a**2*b*a**3 or a*b in | |
| # a**2*b*a*b | |
| pre_exp = 0 | |
| pre_arg = 1 | |
| if start - l >= 0 and args[start-l+1:start] == subterm[1:]: | |
| if isinstance(subterm[0], _Pow): | |
| pre_arg = subterm[0].args[0] | |
| exp = subterm[0].args[1] | |
| else: | |
| pre_arg = subterm[0] | |
| exp = 1 | |
| if isinstance(args[start-l], _Pow) and args[start-l].args[0] == pre_arg: | |
| pre_exp = args[start-l].args[1] - exp | |
| start -= l | |
| p += 1 | |
| elif args[start-l] == pre_arg: | |
| pre_exp = 1 - exp | |
| start -= l | |
| p += 1 | |
| post_exp = 0 | |
| post_arg = 1 | |
| if end + l - 1 < len(args) and args[end:end+l-1] == subterm[:-1]: | |
| if isinstance(subterm[-1], _Pow): | |
| post_arg = subterm[-1].args[0] | |
| exp = subterm[-1].args[1] | |
| else: | |
| post_arg = subterm[-1] | |
| exp = 1 | |
| if isinstance(args[end+l-1], _Pow) and args[end+l-1].args[0] == post_arg: | |
| post_exp = args[end+l-1].args[1] - exp | |
| end += l | |
| p += 1 | |
| elif args[end+l-1] == post_arg: | |
| post_exp = 1 - exp | |
| end += l | |
| p += 1 | |
| # Consider a*b*a**2*b*a**2*b*a: | |
| # b*a**2 is explicitly repeated, but note | |
| # that in this case a*b*a is also repeated | |
| # so there are two possible simplifications: | |
| # a*(b*a**2)**3*a**-1 or (a*b*a)**3 | |
| # The latter is obviously simpler. | |
| # But in a*b*a**2*b**2*a**2 the simplifications are | |
| # a*(b*a**2)**2 and (a*b*a)**3*a in which case | |
| # it's better to stick with the shorter subterm | |
| if post_exp and exp % 2 == 0 and start > 0: | |
| exp = exp/2 | |
| _pre_exp = 1 | |
| _post_exp = 1 | |
| if isinstance(args[start-1], _Pow) and args[start-1].args[0] == post_arg: | |
| _post_exp = post_exp + exp | |
| _pre_exp = args[start-1].args[1] - exp | |
| elif args[start-1] == post_arg: | |
| _post_exp = post_exp + exp | |
| _pre_exp = 1 - exp | |
| if _pre_exp == 0 or _post_exp == 0: | |
| if not pre_exp: | |
| start -= 1 | |
| post_exp = _post_exp | |
| pre_exp = _pre_exp | |
| pre_arg = post_arg | |
| subterm = (post_arg**exp,) + subterm[:-1] + (post_arg**exp,) | |
| simp_coeff += end-start | |
| if post_exp: | |
| simp_coeff -= 1 | |
| if pre_exp: | |
| simp_coeff -= 1 | |
| simps[subterm] = end | |
| if simp_coeff > max_simp_coeff: | |
| max_simp_coeff = simp_coeff | |
| simp = (start, _Mul(*subterm), p, end, l) | |
| pre = pre_arg**pre_exp | |
| post = post_arg**post_exp | |
| if simp: | |
| subterm = _Pow(nc_simplify(simp[1], deep=deep), simp[2]) | |
| pre = nc_simplify(_Mul(*args[:simp[0]])*pre, deep=deep) | |
| post = post*nc_simplify(_Mul(*args[simp[3]:]), deep=deep) | |
| simp = pre*subterm*post | |
| if pre != 1 or post != 1: | |
| # new simplifications may be possible but no need | |
| # to recurse over arguments | |
| simp = nc_simplify(simp, deep=False) | |
| else: | |
| simp = _Mul(*args) | |
| if invert: | |
| simp = _Pow(simp, -1) | |
| # see if factor_nc(expr) is simplified better | |
| if not isinstance(expr, MatrixExpr): | |
| f_expr = factor_nc(expr) | |
| if f_expr != expr: | |
| alt_simp = nc_simplify(f_expr, deep=deep) | |
| simp = compare(simp, alt_simp) | |
| else: | |
| simp = simp.doit(inv_expand=False) | |
| return simp | |
| def dotprodsimp(expr, withsimp=False): | |
| """Simplification for a sum of products targeted at the kind of blowup that | |
| occurs during summation of products. Intended to reduce expression blowup | |
| during matrix multiplication or other similar operations. Only works with | |
| algebraic expressions and does not recurse into non. | |
| Parameters | |
| ========== | |
| withsimp : bool, optional | |
| Specifies whether a flag should be returned along with the expression | |
| to indicate roughly whether simplification was successful. It is used | |
| in ``MatrixArithmetic._eval_pow_by_recursion`` to avoid attempting to | |
| simplify an expression repetitively which does not simplify. | |
| """ | |
| def count_ops_alg(expr): | |
| """Optimized count algebraic operations with no recursion into | |
| non-algebraic args that ``core.function.count_ops`` does. Also returns | |
| whether rational functions may be present according to negative | |
| exponents of powers or non-number fractions. | |
| Returns | |
| ======= | |
| ops, ratfunc : int, bool | |
| ``ops`` is the number of algebraic operations starting at the top | |
| level expression (not recursing into non-alg children). ``ratfunc`` | |
| specifies whether the expression MAY contain rational functions | |
| which ``cancel`` MIGHT optimize. | |
| """ | |
| ops = 0 | |
| args = [expr] | |
| ratfunc = False | |
| while args: | |
| a = args.pop() | |
| if not isinstance(a, Basic): | |
| continue | |
| if a.is_Rational: | |
| if a is not S.One: # -1/3 = NEG + DIV | |
| ops += bool (a.p < 0) + bool (a.q != 1) | |
| elif a.is_Mul: | |
| if a.could_extract_minus_sign(): | |
| ops += 1 | |
| if a.args[0] is S.NegativeOne: | |
| a = a.as_two_terms()[1] | |
| else: | |
| a = -a | |
| n, d = fraction(a) | |
| if n.is_Integer: | |
| ops += 1 + bool (n < 0) | |
| args.append(d) # won't be -Mul but could be Add | |
| elif d is not S.One: | |
| if not d.is_Integer: | |
| args.append(d) | |
| ratfunc=True | |
| ops += 1 | |
| args.append(n) # could be -Mul | |
| else: | |
| ops += len(a.args) - 1 | |
| args.extend(a.args) | |
| elif a.is_Add: | |
| laargs = len(a.args) | |
| negs = 0 | |
| for ai in a.args: | |
| if ai.could_extract_minus_sign(): | |
| negs += 1 | |
| ai = -ai | |
| args.append(ai) | |
| ops += laargs - (negs != laargs) # -x - y = NEG + SUB | |
| elif a.is_Pow: | |
| ops += 1 | |
| args.append(a.base) | |
| if not ratfunc: | |
| ratfunc = a.exp.is_negative is not False | |
| return ops, ratfunc | |
| def nonalg_subs_dummies(expr, dummies): | |
| """Substitute dummy variables for non-algebraic expressions to avoid | |
| evaluation of non-algebraic terms that ``polys.polytools.cancel`` does. | |
| """ | |
| if not expr.args: | |
| return expr | |
| if expr.is_Add or expr.is_Mul or expr.is_Pow: | |
| args = None | |
| for i, a in enumerate(expr.args): | |
| c = nonalg_subs_dummies(a, dummies) | |
| if c is a: | |
| continue | |
| if args is None: | |
| args = list(expr.args) | |
| args[i] = c | |
| if args is None: | |
| return expr | |
| return expr.func(*args) | |
| return dummies.setdefault(expr, Dummy()) | |
| simplified = False # doesn't really mean simplified, rather "can simplify again" | |
| if isinstance(expr, Basic) and (expr.is_Add or expr.is_Mul or expr.is_Pow): | |
| expr2 = expr.expand(deep=True, modulus=None, power_base=False, | |
| power_exp=False, mul=True, log=False, multinomial=True, basic=False) | |
| if expr2 != expr: | |
| expr = expr2 | |
| simplified = True | |
| exprops, ratfunc = count_ops_alg(expr) | |
| if exprops >= 6: # empirically tested cutoff for expensive simplification | |
| if ratfunc: | |
| dummies = {} | |
| expr2 = nonalg_subs_dummies(expr, dummies) | |
| if expr2 is expr or count_ops_alg(expr2)[0] >= 6: # check again after substitution | |
| expr3 = cancel(expr2) | |
| if expr3 != expr2: | |
| expr = expr3.subs([(d, e) for e, d in dummies.items()]) | |
| simplified = True | |
| # very special case: x/(x-1) - 1/(x-1) -> 1 | |
| elif (exprops == 5 and expr.is_Add and expr.args [0].is_Mul and | |
| expr.args [1].is_Mul and expr.args [0].args [-1].is_Pow and | |
| expr.args [1].args [-1].is_Pow and | |
| expr.args [0].args [-1].exp is S.NegativeOne and | |
| expr.args [1].args [-1].exp is S.NegativeOne): | |
| expr2 = together (expr) | |
| expr2ops = count_ops_alg(expr2)[0] | |
| if expr2ops < exprops: | |
| expr = expr2 | |
| simplified = True | |
| else: | |
| simplified = True | |
| return (expr, simplified) if withsimp else expr | |
| bottom_up = deprecated( | |
| """ | |
| Using bottom_up from the sympy.simplify.simplify submodule is | |
| deprecated. | |
| Instead, use bottom_up from the top-level sympy namespace, like | |
| sympy.bottom_up | |
| """, | |
| deprecated_since_version="1.10", | |
| active_deprecations_target="deprecated-traversal-functions-moved", | |
| )(_bottom_up) | |
| # XXX: This function really should either be private API or exported in the | |
| # top-level sympy/__init__.py | |
| walk = deprecated( | |
| """ | |
| Using walk from the sympy.simplify.simplify submodule is | |
| deprecated. | |
| Instead, use walk from sympy.core.traversal.walk | |
| """, | |
| deprecated_since_version="1.10", | |
| active_deprecations_target="deprecated-traversal-functions-moved", | |
| )(_walk) | |
Xet Storage Details
- Size:
- 73.3 kB
- Xet hash:
- a67831b5554e0f44922fcfdce7a35a22af326e3278323b0d7ff1038ce521d234
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.