Buckets:
MisterAI/LocalAI_Demo_backends / cpu-diffusers.upgrade-tmp /venv /lib /python3.10 /site-packages /sympy /simplify /fu.py
| from collections import defaultdict | |
| from sympy.core.add import Add | |
| from sympy.core.cache import cacheit | |
| from sympy.core.expr import Expr | |
| from sympy.core.exprtools import Factors, gcd_terms, factor_terms | |
| from sympy.core.function import expand_mul | |
| from sympy.core.mul import Mul | |
| from sympy.core.numbers import pi, I | |
| from sympy.core.power import Pow | |
| from sympy.core.singleton import S | |
| from sympy.core.sorting import ordered | |
| from sympy.core.symbol import Dummy | |
| from sympy.core.sympify import sympify | |
| from sympy.core.traversal import bottom_up | |
| from sympy.functions.combinatorial.factorials import binomial | |
| from sympy.functions.elementary.hyperbolic import ( | |
| cosh, sinh, tanh, coth, sech, csch, HyperbolicFunction) | |
| from sympy.functions.elementary.trigonometric import ( | |
| cos, sin, tan, cot, sec, csc, sqrt, TrigonometricFunction) | |
| from sympy.ntheory.factor_ import perfect_power | |
| from sympy.polys.polytools import factor | |
| from sympy.strategies.tree import greedy | |
| from sympy.strategies.core import identity, debug | |
| from sympy import SYMPY_DEBUG | |
| # ================== Fu-like tools =========================== | |
| def TR0(rv): | |
| """Simplification of rational polynomials, trying to simplify | |
| the expression, e.g. combine things like 3*x + 2*x, etc.... | |
| """ | |
| # although it would be nice to use cancel, it doesn't work | |
| # with noncommutatives | |
| return rv.normal().factor().expand() | |
| def TR1(rv): | |
| """Replace sec, csc with 1/cos, 1/sin | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import TR1, sec, csc | |
| >>> from sympy.abc import x | |
| >>> TR1(2*csc(x) + sec(x)) | |
| 1/cos(x) + 2/sin(x) | |
| """ | |
| def f(rv): | |
| if isinstance(rv, sec): | |
| a = rv.args[0] | |
| return S.One/cos(a) | |
| elif isinstance(rv, csc): | |
| a = rv.args[0] | |
| return S.One/sin(a) | |
| return rv | |
| return bottom_up(rv, f) | |
| def TR2(rv): | |
| """Replace tan and cot with sin/cos and cos/sin | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import TR2 | |
| >>> from sympy.abc import x | |
| >>> from sympy import tan, cot, sin, cos | |
| >>> TR2(tan(x)) | |
| sin(x)/cos(x) | |
| >>> TR2(cot(x)) | |
| cos(x)/sin(x) | |
| >>> TR2(tan(tan(x) - sin(x)/cos(x))) | |
| 0 | |
| """ | |
| def f(rv): | |
| if isinstance(rv, tan): | |
| a = rv.args[0] | |
| return sin(a)/cos(a) | |
| elif isinstance(rv, cot): | |
| a = rv.args[0] | |
| return cos(a)/sin(a) | |
| return rv | |
| return bottom_up(rv, f) | |
| def TR2i(rv, half=False): | |
| """Converts ratios involving sin and cos as follows:: | |
| sin(x)/cos(x) -> tan(x) | |
| sin(x)/(cos(x) + 1) -> tan(x/2) if half=True | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import TR2i | |
| >>> from sympy.abc import x, a | |
| >>> from sympy import sin, cos | |
| >>> TR2i(sin(x)/cos(x)) | |
| tan(x) | |
| Powers of the numerator and denominator are also recognized | |
| >>> TR2i(sin(x)**2/(cos(x) + 1)**2, half=True) | |
| tan(x/2)**2 | |
| The transformation does not take place unless assumptions allow | |
| (i.e. the base must be positive or the exponent must be an integer | |
| for both numerator and denominator) | |
| >>> TR2i(sin(x)**a/(cos(x) + 1)**a) | |
| sin(x)**a/(cos(x) + 1)**a | |
| """ | |
| def f(rv): | |
| if not rv.is_Mul: | |
| return rv | |
| n, d = rv.as_numer_denom() | |
| if n.is_Atom or d.is_Atom: | |
| return rv | |
| def ok(k, e): | |
| # initial filtering of factors | |
| return ( | |
| (e.is_integer or k.is_positive) and ( | |
| k.func in (sin, cos) or (half and | |
| k.is_Add and | |
| len(k.args) >= 2 and | |
| any(any(isinstance(ai, cos) or ai.is_Pow and ai.base is cos | |
| for ai in Mul.make_args(a)) for a in k.args)))) | |
| n = n.as_powers_dict() | |
| ndone = [(k, n.pop(k)) for k in list(n.keys()) if not ok(k, n[k])] | |
| if not n: | |
| return rv | |
| d = d.as_powers_dict() | |
| ddone = [(k, d.pop(k)) for k in list(d.keys()) if not ok(k, d[k])] | |
| if not d: | |
| return rv | |
| # factoring if necessary | |
| def factorize(d, ddone): | |
| newk = [] | |
| for k in d: | |
| if k.is_Add and len(k.args) > 1: | |
| knew = factor(k) if half else factor_terms(k) | |
| if knew != k: | |
| newk.append((k, knew)) | |
| if newk: | |
| for i, (k, knew) in enumerate(newk): | |
| del d[k] | |
| newk[i] = knew | |
| newk = Mul(*newk).as_powers_dict() | |
| for k in newk: | |
| v = d[k] + newk[k] | |
| if ok(k, v): | |
| d[k] = v | |
| else: | |
| ddone.append((k, v)) | |
| del newk | |
| factorize(n, ndone) | |
| factorize(d, ddone) | |
| # joining | |
| t = [] | |
| for k in n: | |
| if isinstance(k, sin): | |
| a = cos(k.args[0], evaluate=False) | |
| if a in d and d[a] == n[k]: | |
| t.append(tan(k.args[0])**n[k]) | |
| n[k] = d[a] = None | |
| elif half: | |
| a1 = 1 + a | |
| if a1 in d and d[a1] == n[k]: | |
| t.append((tan(k.args[0]/2))**n[k]) | |
| n[k] = d[a1] = None | |
| elif isinstance(k, cos): | |
| a = sin(k.args[0], evaluate=False) | |
| if a in d and d[a] == n[k]: | |
| t.append(tan(k.args[0])**-n[k]) | |
| n[k] = d[a] = None | |
| elif half and k.is_Add and k.args[0] is S.One and \ | |
| isinstance(k.args[1], cos): | |
| a = sin(k.args[1].args[0], evaluate=False) | |
| if a in d and d[a] == n[k] and (d[a].is_integer or \ | |
| a.is_positive): | |
| t.append(tan(a.args[0]/2)**-n[k]) | |
| n[k] = d[a] = None | |
| if t: | |
| rv = Mul(*(t + [b**e for b, e in n.items() if e]))/\ | |
| Mul(*[b**e for b, e in d.items() if e]) | |
| rv *= Mul(*[b**e for b, e in ndone])/Mul(*[b**e for b, e in ddone]) | |
| return rv | |
| return bottom_up(rv, f) | |
| def TR3(rv): | |
| """Induced formula: example sin(-a) = -sin(a) | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import TR3 | |
| >>> from sympy.abc import x, y | |
| >>> from sympy import pi | |
| >>> from sympy import cos | |
| >>> TR3(cos(y - x*(y - x))) | |
| cos(x*(x - y) + y) | |
| >>> cos(pi/2 + x) | |
| -sin(x) | |
| >>> cos(30*pi/2 + x) | |
| -cos(x) | |
| """ | |
| from sympy.simplify.simplify import signsimp | |
| # Negative argument (already automatic for funcs like sin(-x) -> -sin(x) | |
| # but more complicated expressions can use it, too). Also, trig angles | |
| # between pi/4 and pi/2 are not reduced to an angle between 0 and pi/4. | |
| # The following are automatically handled: | |
| # Argument of type: pi/2 +/- angle | |
| # Argument of type: pi +/- angle | |
| # Argument of type : 2k*pi +/- angle | |
| def f(rv): | |
| if not isinstance(rv, TrigonometricFunction): | |
| return rv | |
| rv = rv.func(signsimp(rv.args[0])) | |
| if not isinstance(rv, TrigonometricFunction): | |
| return rv | |
| if (rv.args[0] - S.Pi/4).is_positive is (S.Pi/2 - rv.args[0]).is_positive is True: | |
| fmap = {cos: sin, sin: cos, tan: cot, cot: tan, sec: csc, csc: sec} | |
| rv = fmap[type(rv)](S.Pi/2 - rv.args[0]) | |
| return rv | |
| # touch numbers iside of trig functions to let them automatically update | |
| rv = rv.replace( | |
| lambda x: isinstance(x, TrigonometricFunction), | |
| lambda x: x.replace( | |
| lambda n: n.is_number and n.is_Mul, | |
| lambda n: n.func(*n.args))) | |
| return bottom_up(rv, f) | |
| def TR4(rv): | |
| """Identify values of special angles. | |
| a= 0 pi/6 pi/4 pi/3 pi/2 | |
| ---------------------------------------------------- | |
| sin(a) 0 1/2 sqrt(2)/2 sqrt(3)/2 1 | |
| cos(a) 1 sqrt(3)/2 sqrt(2)/2 1/2 0 | |
| tan(a) 0 sqt(3)/3 1 sqrt(3) -- | |
| Examples | |
| ======== | |
| >>> from sympy import pi | |
| >>> from sympy import cos, sin, tan, cot | |
| >>> for s in (0, pi/6, pi/4, pi/3, pi/2): | |
| ... print('%s %s %s %s' % (cos(s), sin(s), tan(s), cot(s))) | |
| ... | |
| 1 0 0 zoo | |
| sqrt(3)/2 1/2 sqrt(3)/3 sqrt(3) | |
| sqrt(2)/2 sqrt(2)/2 1 1 | |
| 1/2 sqrt(3)/2 sqrt(3) sqrt(3)/3 | |
| 0 1 zoo 0 | |
| """ | |
| # special values at 0, pi/6, pi/4, pi/3, pi/2 already handled | |
| return rv.replace( | |
| lambda x: | |
| isinstance(x, TrigonometricFunction) and | |
| (r:=x.args[0]/pi).is_Rational and r.q in (1, 2, 3, 4, 6), | |
| lambda x: | |
| x.func(x.args[0].func(*x.args[0].args))) | |
| def _TR56(rv, f, g, h, max, pow): | |
| """Helper for TR5 and TR6 to replace f**2 with h(g**2) | |
| Options | |
| ======= | |
| max : controls size of exponent that can appear on f | |
| e.g. if max=4 then f**4 will be changed to h(g**2)**2. | |
| pow : controls whether the exponent must be a perfect power of 2 | |
| e.g. if pow=True (and max >= 6) then f**6 will not be changed | |
| but f**8 will be changed to h(g**2)**4 | |
| >>> from sympy.simplify.fu import _TR56 as T | |
| >>> from sympy.abc import x | |
| >>> from sympy import sin, cos | |
| >>> h = lambda x: 1 - x | |
| >>> T(sin(x)**3, sin, cos, h, 4, False) | |
| (1 - cos(x)**2)*sin(x) | |
| >>> T(sin(x)**6, sin, cos, h, 6, False) | |
| (1 - cos(x)**2)**3 | |
| >>> T(sin(x)**6, sin, cos, h, 6, True) | |
| sin(x)**6 | |
| >>> T(sin(x)**8, sin, cos, h, 10, True) | |
| (1 - cos(x)**2)**4 | |
| """ | |
| def _f(rv): | |
| # I'm not sure if this transformation should target all even powers | |
| # or only those expressible as powers of 2. Also, should it only | |
| # make the changes in powers that appear in sums -- making an isolated | |
| # change is not going to allow a simplification as far as I can tell. | |
| if not (rv.is_Pow and rv.base.func == f): | |
| return rv | |
| if not rv.exp.is_real: | |
| return rv | |
| if (rv.exp < 0) == True: | |
| return rv | |
| if (rv.exp > max) == True: | |
| return rv | |
| if rv.exp == 1: | |
| return rv | |
| if rv.exp == 2: | |
| return h(g(rv.base.args[0])**2) | |
| else: | |
| if rv.exp % 2 == 1: | |
| e = rv.exp//2 | |
| return f(rv.base.args[0])*h(g(rv.base.args[0])**2)**e | |
| elif rv.exp == 4: | |
| e = 2 | |
| elif not pow: | |
| if rv.exp % 2: | |
| return rv | |
| e = rv.exp//2 | |
| else: | |
| p = perfect_power(rv.exp) | |
| if not p: | |
| return rv | |
| e = rv.exp//2 | |
| return h(g(rv.base.args[0])**2)**e | |
| return bottom_up(rv, _f) | |
| def TR5(rv, max=4, pow=False): | |
| """Replacement of sin**2 with 1 - cos(x)**2. | |
| See _TR56 docstring for advanced use of ``max`` and ``pow``. | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import TR5 | |
| >>> from sympy.abc import x | |
| >>> from sympy import sin | |
| >>> TR5(sin(x)**2) | |
| 1 - cos(x)**2 | |
| >>> TR5(sin(x)**-2) # unchanged | |
| sin(x)**(-2) | |
| >>> TR5(sin(x)**4) | |
| (1 - cos(x)**2)**2 | |
| """ | |
| return _TR56(rv, sin, cos, lambda x: 1 - x, max=max, pow=pow) | |
| def TR6(rv, max=4, pow=False): | |
| """Replacement of cos**2 with 1 - sin(x)**2. | |
| See _TR56 docstring for advanced use of ``max`` and ``pow``. | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import TR6 | |
| >>> from sympy.abc import x | |
| >>> from sympy import cos | |
| >>> TR6(cos(x)**2) | |
| 1 - sin(x)**2 | |
| >>> TR6(cos(x)**-2) #unchanged | |
| cos(x)**(-2) | |
| >>> TR6(cos(x)**4) | |
| (1 - sin(x)**2)**2 | |
| """ | |
| return _TR56(rv, cos, sin, lambda x: 1 - x, max=max, pow=pow) | |
| def TR7(rv): | |
| """Lowering the degree of cos(x)**2. | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import TR7 | |
| >>> from sympy.abc import x | |
| >>> from sympy import cos | |
| >>> TR7(cos(x)**2) | |
| cos(2*x)/2 + 1/2 | |
| >>> TR7(cos(x)**2 + 1) | |
| cos(2*x)/2 + 3/2 | |
| """ | |
| def f(rv): | |
| if not (rv.is_Pow and rv.base.func == cos and rv.exp == 2): | |
| return rv | |
| return (1 + cos(2*rv.base.args[0]))/2 | |
| return bottom_up(rv, f) | |
| def TR8(rv, first=True): | |
| """Converting products of ``cos`` and/or ``sin`` to a sum or | |
| difference of ``cos`` and or ``sin`` terms. | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import TR8 | |
| >>> from sympy import cos, sin | |
| >>> TR8(cos(2)*cos(3)) | |
| cos(5)/2 + cos(1)/2 | |
| >>> TR8(cos(2)*sin(3)) | |
| sin(5)/2 + sin(1)/2 | |
| >>> TR8(sin(2)*sin(3)) | |
| -cos(5)/2 + cos(1)/2 | |
| """ | |
| def f(rv): | |
| if not ( | |
| rv.is_Mul or | |
| rv.is_Pow and | |
| rv.base.func in (cos, sin) and | |
| (rv.exp.is_integer or rv.base.is_positive)): | |
| return rv | |
| if first: | |
| n, d = [expand_mul(i) for i in rv.as_numer_denom()] | |
| newn = TR8(n, first=False) | |
| newd = TR8(d, first=False) | |
| if newn != n or newd != d: | |
| rv = gcd_terms(newn/newd) | |
| if rv.is_Mul and rv.args[0].is_Rational and \ | |
| len(rv.args) == 2 and rv.args[1].is_Add: | |
| rv = Mul(*rv.as_coeff_Mul()) | |
| return rv | |
| args = {cos: [], sin: [], None: []} | |
| for a in Mul.make_args(rv): | |
| if a.func in (cos, sin): | |
| args[type(a)].append(a.args[0]) | |
| elif (a.is_Pow and a.exp.is_Integer and a.exp > 0 and \ | |
| a.base.func in (cos, sin)): | |
| # XXX this is ok but pathological expression could be handled | |
| # more efficiently as in TRmorrie | |
| args[type(a.base)].extend([a.base.args[0]]*a.exp) | |
| else: | |
| args[None].append(a) | |
| c = args[cos] | |
| s = args[sin] | |
| if not (c and s or len(c) > 1 or len(s) > 1): | |
| return rv | |
| args = args[None] | |
| n = min(len(c), len(s)) | |
| for i in range(n): | |
| a1 = s.pop() | |
| a2 = c.pop() | |
| args.append((sin(a1 + a2) + sin(a1 - a2))/2) | |
| while len(c) > 1: | |
| a1 = c.pop() | |
| a2 = c.pop() | |
| args.append((cos(a1 + a2) + cos(a1 - a2))/2) | |
| if c: | |
| args.append(cos(c.pop())) | |
| while len(s) > 1: | |
| a1 = s.pop() | |
| a2 = s.pop() | |
| args.append((-cos(a1 + a2) + cos(a1 - a2))/2) | |
| if s: | |
| args.append(sin(s.pop())) | |
| return TR8(expand_mul(Mul(*args))) | |
| return bottom_up(rv, f) | |
| def TR9(rv): | |
| """Sum of ``cos`` or ``sin`` terms as a product of ``cos`` or ``sin``. | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import TR9 | |
| >>> from sympy import cos, sin | |
| >>> TR9(cos(1) + cos(2)) | |
| 2*cos(1/2)*cos(3/2) | |
| >>> TR9(cos(1) + 2*sin(1) + 2*sin(2)) | |
| cos(1) + 4*sin(3/2)*cos(1/2) | |
| If no change is made by TR9, no re-arrangement of the | |
| expression will be made. For example, though factoring | |
| of common term is attempted, if the factored expression | |
| was not changed, the original expression will be returned: | |
| >>> TR9(cos(3) + cos(3)*cos(2)) | |
| cos(3) + cos(2)*cos(3) | |
| """ | |
| def f(rv): | |
| if not rv.is_Add: | |
| return rv | |
| def do(rv, first=True): | |
| # cos(a)+/-cos(b) can be combined into a product of cosines and | |
| # sin(a)+/-sin(b) can be combined into a product of cosine and | |
| # sine. | |
| # | |
| # If there are more than two args, the pairs which "work" will | |
| # have a gcd extractable and the remaining two terms will have | |
| # the above structure -- all pairs must be checked to find the | |
| # ones that work. args that don't have a common set of symbols | |
| # are skipped since this doesn't lead to a simpler formula and | |
| # also has the arbitrariness of combining, for example, the x | |
| # and y term instead of the y and z term in something like | |
| # cos(x) + cos(y) + cos(z). | |
| if not rv.is_Add: | |
| return rv | |
| args = list(ordered(rv.args)) | |
| if len(args) != 2: | |
| hit = False | |
| for i in range(len(args)): | |
| ai = args[i] | |
| if ai is None: | |
| continue | |
| for j in range(i + 1, len(args)): | |
| aj = args[j] | |
| if aj is None: | |
| continue | |
| was = ai + aj | |
| new = do(was) | |
| if new != was: | |
| args[i] = new # update in place | |
| args[j] = None | |
| hit = True | |
| break # go to next i | |
| if hit: | |
| rv = Add(*[_f for _f in args if _f]) | |
| if rv.is_Add: | |
| rv = do(rv) | |
| return rv | |
| # two-arg Add | |
| split = trig_split(*args) | |
| if not split: | |
| return rv | |
| gcd, n1, n2, a, b, iscos = split | |
| # application of rule if possible | |
| if iscos: | |
| if n1 == n2: | |
| return gcd*n1*2*cos((a + b)/2)*cos((a - b)/2) | |
| if n1 < 0: | |
| a, b = b, a | |
| return -2*gcd*sin((a + b)/2)*sin((a - b)/2) | |
| else: | |
| if n1 == n2: | |
| return gcd*n1*2*sin((a + b)/2)*cos((a - b)/2) | |
| if n1 < 0: | |
| a, b = b, a | |
| return 2*gcd*cos((a + b)/2)*sin((a - b)/2) | |
| return process_common_addends(rv, do) # DON'T sift by free symbols | |
| return bottom_up(rv, f) | |
| def TR10(rv, first=True): | |
| """Separate sums in ``cos`` and ``sin``. | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import TR10 | |
| >>> from sympy.abc import a, b, c | |
| >>> from sympy import cos, sin | |
| >>> TR10(cos(a + b)) | |
| -sin(a)*sin(b) + cos(a)*cos(b) | |
| >>> TR10(sin(a + b)) | |
| sin(a)*cos(b) + sin(b)*cos(a) | |
| >>> TR10(sin(a + b + c)) | |
| (-sin(a)*sin(b) + cos(a)*cos(b))*sin(c) + \ | |
| (sin(a)*cos(b) + sin(b)*cos(a))*cos(c) | |
| """ | |
| def f(rv): | |
| if rv.func not in (cos, sin): | |
| return rv | |
| f = rv.func | |
| arg = rv.args[0] | |
| if arg.is_Add: | |
| if first: | |
| args = list(ordered(arg.args)) | |
| else: | |
| args = list(arg.args) | |
| a = args.pop() | |
| b = Add._from_args(args) | |
| if b.is_Add: | |
| if f == sin: | |
| return sin(a)*TR10(cos(b), first=False) + \ | |
| cos(a)*TR10(sin(b), first=False) | |
| else: | |
| return cos(a)*TR10(cos(b), first=False) - \ | |
| sin(a)*TR10(sin(b), first=False) | |
| else: | |
| if f == sin: | |
| return sin(a)*cos(b) + cos(a)*sin(b) | |
| else: | |
| return cos(a)*cos(b) - sin(a)*sin(b) | |
| return rv | |
| return bottom_up(rv, f) | |
| def TR10i(rv): | |
| """Sum of products to function of sum. | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import TR10i | |
| >>> from sympy import cos, sin, sqrt | |
| >>> from sympy.abc import x | |
| >>> TR10i(cos(1)*cos(3) + sin(1)*sin(3)) | |
| cos(2) | |
| >>> TR10i(cos(1)*sin(3) + sin(1)*cos(3) + cos(3)) | |
| cos(3) + sin(4) | |
| >>> TR10i(sqrt(2)*cos(x)*x + sqrt(6)*sin(x)*x) | |
| 2*sqrt(2)*x*sin(x + pi/6) | |
| """ | |
| def f(rv): | |
| if not rv.is_Add: | |
| return rv | |
| def do(rv, first=True): | |
| # args which can be expressed as A*(cos(a)*cos(b)+/-sin(a)*sin(b)) | |
| # or B*(cos(a)*sin(b)+/-cos(b)*sin(a)) can be combined into | |
| # A*f(a+/-b) where f is either sin or cos. | |
| # | |
| # If there are more than two args, the pairs which "work" will have | |
| # a gcd extractable and the remaining two terms will have the above | |
| # structure -- all pairs must be checked to find the ones that | |
| # work. | |
| if not rv.is_Add: | |
| return rv | |
| args = list(ordered(rv.args)) | |
| if len(args) != 2: | |
| hit = False | |
| for i in range(len(args)): | |
| ai = args[i] | |
| if ai is None: | |
| continue | |
| for j in range(i + 1, len(args)): | |
| aj = args[j] | |
| if aj is None: | |
| continue | |
| was = ai + aj | |
| new = do(was) | |
| if new != was: | |
| args[i] = new # update in place | |
| args[j] = None | |
| hit = True | |
| break # go to next i | |
| if hit: | |
| rv = Add(*[_f for _f in args if _f]) | |
| if rv.is_Add: | |
| rv = do(rv) | |
| return rv | |
| # two-arg Add | |
| split = trig_split(*args, two=True) | |
| if not split: | |
| return rv | |
| gcd, n1, n2, a, b, same = split | |
| # identify and get c1 to be cos then apply rule if possible | |
| if same: # coscos, sinsin | |
| gcd = n1*gcd | |
| if n1 == n2: | |
| return gcd*cos(a - b) | |
| return gcd*cos(a + b) | |
| else: #cossin, cossin | |
| gcd = n1*gcd | |
| if n1 == n2: | |
| return gcd*sin(a + b) | |
| return gcd*sin(b - a) | |
| rv = process_common_addends( | |
| rv, do, lambda x: tuple(ordered(x.free_symbols))) | |
| # need to check for inducible pairs in ratio of sqrt(3):1 that | |
| # appeared in different lists when sorting by coefficient | |
| while rv.is_Add: | |
| byrad = defaultdict(list) | |
| for a in rv.args: | |
| hit = 0 | |
| if a.is_Mul: | |
| for ai in a.args: | |
| if ai.is_Pow and ai.exp is S.Half and \ | |
| ai.base.is_Integer: | |
| byrad[ai].append(a) | |
| hit = 1 | |
| break | |
| if not hit: | |
| byrad[S.One].append(a) | |
| # no need to check all pairs -- just check for the onees | |
| # that have the right ratio | |
| args = [] | |
| for a in byrad: | |
| for b in [_ROOT3()*a, _invROOT3()]: | |
| if b in byrad: | |
| for i in range(len(byrad[a])): | |
| if byrad[a][i] is None: | |
| continue | |
| for j in range(len(byrad[b])): | |
| if byrad[b][j] is None: | |
| continue | |
| was = Add(byrad[a][i] + byrad[b][j]) | |
| new = do(was) | |
| if new != was: | |
| args.append(new) | |
| byrad[a][i] = None | |
| byrad[b][j] = None | |
| break | |
| if args: | |
| rv = Add(*(args + [Add(*[_f for _f in v if _f]) | |
| for v in byrad.values()])) | |
| else: | |
| rv = do(rv) # final pass to resolve any new inducible pairs | |
| break | |
| return rv | |
| return bottom_up(rv, f) | |
| def TR11(rv, base=None): | |
| """Function of double angle to product. The ``base`` argument can be used | |
| to indicate what is the un-doubled argument, e.g. if 3*pi/7 is the base | |
| then cosine and sine functions with argument 6*pi/7 will be replaced. | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import TR11 | |
| >>> from sympy import cos, sin, pi | |
| >>> from sympy.abc import x | |
| >>> TR11(sin(2*x)) | |
| 2*sin(x)*cos(x) | |
| >>> TR11(cos(2*x)) | |
| -sin(x)**2 + cos(x)**2 | |
| >>> TR11(sin(4*x)) | |
| 4*(-sin(x)**2 + cos(x)**2)*sin(x)*cos(x) | |
| >>> TR11(sin(4*x/3)) | |
| 4*(-sin(x/3)**2 + cos(x/3)**2)*sin(x/3)*cos(x/3) | |
| If the arguments are simply integers, no change is made | |
| unless a base is provided: | |
| >>> TR11(cos(2)) | |
| cos(2) | |
| >>> TR11(cos(4), 2) | |
| -sin(2)**2 + cos(2)**2 | |
| There is a subtle issue here in that autosimplification will convert | |
| some higher angles to lower angles | |
| >>> cos(6*pi/7) + cos(3*pi/7) | |
| -cos(pi/7) + cos(3*pi/7) | |
| The 6*pi/7 angle is now pi/7 but can be targeted with TR11 by supplying | |
| the 3*pi/7 base: | |
| >>> TR11(_, 3*pi/7) | |
| -sin(3*pi/7)**2 + cos(3*pi/7)**2 + cos(3*pi/7) | |
| """ | |
| def f(rv): | |
| if rv.func not in (cos, sin): | |
| return rv | |
| if base: | |
| f = rv.func | |
| t = f(base*2) | |
| co = S.One | |
| if t.is_Mul: | |
| co, t = t.as_coeff_Mul() | |
| if t.func not in (cos, sin): | |
| return rv | |
| if rv.args[0] == t.args[0]: | |
| c = cos(base) | |
| s = sin(base) | |
| if f is cos: | |
| return (c**2 - s**2)/co | |
| else: | |
| return 2*c*s/co | |
| return rv | |
| elif not rv.args[0].is_Number: | |
| # make a change if the leading coefficient's numerator is | |
| # divisible by 2 | |
| c, m = rv.args[0].as_coeff_Mul(rational=True) | |
| if c.p % 2 == 0: | |
| arg = c.p//2*m/c.q | |
| c = TR11(cos(arg)) | |
| s = TR11(sin(arg)) | |
| if rv.func == sin: | |
| rv = 2*s*c | |
| else: | |
| rv = c**2 - s**2 | |
| return rv | |
| return bottom_up(rv, f) | |
| def _TR11(rv): | |
| """ | |
| Helper for TR11 to find half-arguments for sin in factors of | |
| num/den that appear in cos or sin factors in the den/num. | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import TR11, _TR11 | |
| >>> from sympy import cos, sin | |
| >>> from sympy.abc import x | |
| >>> TR11(sin(x/3)/(cos(x/6))) | |
| sin(x/3)/cos(x/6) | |
| >>> _TR11(sin(x/3)/(cos(x/6))) | |
| 2*sin(x/6) | |
| >>> TR11(sin(x/6)/(sin(x/3))) | |
| sin(x/6)/sin(x/3) | |
| >>> _TR11(sin(x/6)/(sin(x/3))) | |
| 1/(2*cos(x/6)) | |
| """ | |
| def f(rv): | |
| if not isinstance(rv, Expr): | |
| return rv | |
| def sincos_args(flat): | |
| # find arguments of sin and cos that | |
| # appears as bases in args of flat | |
| # and have Integer exponents | |
| args = defaultdict(set) | |
| for fi in Mul.make_args(flat): | |
| b, e = fi.as_base_exp() | |
| if e.is_Integer and e > 0: | |
| if b.func in (cos, sin): | |
| args[type(b)].add(b.args[0]) | |
| return args | |
| num_args, den_args = map(sincos_args, rv.as_numer_denom()) | |
| def handle_match(rv, num_args, den_args): | |
| # for arg in sin args of num_args, look for arg/2 | |
| # in den_args and pass this half-angle to TR11 | |
| # for handling in rv | |
| for narg in num_args[sin]: | |
| half = narg/2 | |
| if half in den_args[cos]: | |
| func = cos | |
| elif half in den_args[sin]: | |
| func = sin | |
| else: | |
| continue | |
| rv = TR11(rv, half) | |
| den_args[func].remove(half) | |
| return rv | |
| # sin in num, sin or cos in den | |
| rv = handle_match(rv, num_args, den_args) | |
| # sin in den, sin or cos in num | |
| rv = handle_match(rv, den_args, num_args) | |
| return rv | |
| return bottom_up(rv, f) | |
| def TR12(rv, first=True): | |
| """Separate sums in ``tan``. | |
| Examples | |
| ======== | |
| >>> from sympy.abc import x, y | |
| >>> from sympy import tan | |
| >>> from sympy.simplify.fu import TR12 | |
| >>> TR12(tan(x + y)) | |
| (tan(x) + tan(y))/(-tan(x)*tan(y) + 1) | |
| """ | |
| def f(rv): | |
| if not rv.func == tan: | |
| return rv | |
| arg = rv.args[0] | |
| if arg.is_Add: | |
| if first: | |
| args = list(ordered(arg.args)) | |
| else: | |
| args = list(arg.args) | |
| a = args.pop() | |
| b = Add._from_args(args) | |
| if b.is_Add: | |
| tb = TR12(tan(b), first=False) | |
| else: | |
| tb = tan(b) | |
| return (tan(a) + tb)/(1 - tan(a)*tb) | |
| return rv | |
| return bottom_up(rv, f) | |
| def TR12i(rv): | |
| """Combine tan arguments as | |
| (tan(y) + tan(x))/(tan(x)*tan(y) - 1) -> -tan(x + y). | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import TR12i | |
| >>> from sympy import tan | |
| >>> from sympy.abc import a, b, c | |
| >>> ta, tb, tc = [tan(i) for i in (a, b, c)] | |
| >>> TR12i((ta + tb)/(-ta*tb + 1)) | |
| tan(a + b) | |
| >>> TR12i((ta + tb)/(ta*tb - 1)) | |
| -tan(a + b) | |
| >>> TR12i((-ta - tb)/(ta*tb - 1)) | |
| tan(a + b) | |
| >>> eq = (ta + tb)/(-ta*tb + 1)**2*(-3*ta - 3*tc)/(2*(ta*tc - 1)) | |
| >>> TR12i(eq.expand()) | |
| -3*tan(a + b)*tan(a + c)/(2*(tan(a) + tan(b) - 1)) | |
| """ | |
| def f(rv): | |
| if not (rv.is_Add or rv.is_Mul or rv.is_Pow): | |
| return rv | |
| n, d = rv.as_numer_denom() | |
| if not d.args or not n.args: | |
| return rv | |
| dok = {} | |
| def ok(di): | |
| m = as_f_sign_1(di) | |
| if m: | |
| g, f, s = m | |
| if s is S.NegativeOne and f.is_Mul and len(f.args) == 2 and \ | |
| all(isinstance(fi, tan) for fi in f.args): | |
| return g, f | |
| d_args = list(Mul.make_args(d)) | |
| for i, di in enumerate(d_args): | |
| m = ok(di) | |
| if m: | |
| g, t = m | |
| s = Add(*[_.args[0] for _ in t.args]) | |
| dok[s] = S.One | |
| d_args[i] = g | |
| continue | |
| if di.is_Add: | |
| di = factor(di) | |
| if di.is_Mul: | |
| d_args.extend(di.args) | |
| d_args[i] = S.One | |
| elif di.is_Pow and (di.exp.is_integer or di.base.is_positive): | |
| m = ok(di.base) | |
| if m: | |
| g, t = m | |
| s = Add(*[_.args[0] for _ in t.args]) | |
| dok[s] = di.exp | |
| d_args[i] = g**di.exp | |
| else: | |
| di = factor(di) | |
| if di.is_Mul: | |
| d_args.extend(di.args) | |
| d_args[i] = S.One | |
| if not dok: | |
| return rv | |
| def ok(ni): | |
| if ni.is_Add and len(ni.args) == 2: | |
| a, b = ni.args | |
| if isinstance(a, tan) and isinstance(b, tan): | |
| return a, b | |
| n_args = list(Mul.make_args(factor_terms(n))) | |
| hit = False | |
| for i, ni in enumerate(n_args): | |
| m = ok(ni) | |
| if not m: | |
| m = ok(-ni) | |
| if m: | |
| n_args[i] = S.NegativeOne | |
| else: | |
| if ni.is_Add: | |
| ni = factor(ni) | |
| if ni.is_Mul: | |
| n_args.extend(ni.args) | |
| n_args[i] = S.One | |
| continue | |
| elif ni.is_Pow and ( | |
| ni.exp.is_integer or ni.base.is_positive): | |
| m = ok(ni.base) | |
| if m: | |
| n_args[i] = S.One | |
| else: | |
| ni = factor(ni) | |
| if ni.is_Mul: | |
| n_args.extend(ni.args) | |
| n_args[i] = S.One | |
| continue | |
| else: | |
| continue | |
| else: | |
| n_args[i] = S.One | |
| hit = True | |
| s = Add(*[_.args[0] for _ in m]) | |
| ed = dok[s] | |
| newed = ed.extract_additively(S.One) | |
| if newed is not None: | |
| if newed: | |
| dok[s] = newed | |
| else: | |
| dok.pop(s) | |
| n_args[i] *= -tan(s) | |
| if hit: | |
| rv = Mul(*n_args)/Mul(*d_args)/Mul(*[(Add(*[ | |
| tan(a) for a in i.args]) - 1)**e for i, e in dok.items()]) | |
| return rv | |
| return bottom_up(rv, f) | |
| def TR13(rv): | |
| """Change products of ``tan`` or ``cot``. | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import TR13 | |
| >>> from sympy import tan, cot | |
| >>> TR13(tan(3)*tan(2)) | |
| -tan(2)/tan(5) - tan(3)/tan(5) + 1 | |
| >>> TR13(cot(3)*cot(2)) | |
| cot(2)*cot(5) + 1 + cot(3)*cot(5) | |
| """ | |
| def f(rv): | |
| if not rv.is_Mul: | |
| return rv | |
| # XXX handle products of powers? or let power-reducing handle it? | |
| args = {tan: [], cot: [], None: []} | |
| for a in Mul.make_args(rv): | |
| if a.func in (tan, cot): | |
| args[type(a)].append(a.args[0]) | |
| else: | |
| args[None].append(a) | |
| t = args[tan] | |
| c = args[cot] | |
| if len(t) < 2 and len(c) < 2: | |
| return rv | |
| args = args[None] | |
| while len(t) > 1: | |
| t1 = t.pop() | |
| t2 = t.pop() | |
| args.append(1 - (tan(t1)/tan(t1 + t2) + tan(t2)/tan(t1 + t2))) | |
| if t: | |
| args.append(tan(t.pop())) | |
| while len(c) > 1: | |
| t1 = c.pop() | |
| t2 = c.pop() | |
| args.append(1 + cot(t1)*cot(t1 + t2) + cot(t2)*cot(t1 + t2)) | |
| if c: | |
| args.append(cot(c.pop())) | |
| return Mul(*args) | |
| return bottom_up(rv, f) | |
| def TRmorrie(rv): | |
| """Returns cos(x)*cos(2*x)*...*cos(2**(k-1)*x) -> sin(2**k*x)/(2**k*sin(x)) | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import TRmorrie, TR8, TR3 | |
| >>> from sympy.abc import x | |
| >>> from sympy import Mul, cos, pi | |
| >>> TRmorrie(cos(x)*cos(2*x)) | |
| sin(4*x)/(4*sin(x)) | |
| >>> TRmorrie(7*Mul(*[cos(x) for x in range(10)])) | |
| 7*sin(12)*sin(16)*cos(5)*cos(7)*cos(9)/(64*sin(1)*sin(3)) | |
| Sometimes autosimplification will cause a power to be | |
| not recognized. e.g. in the following, cos(4*pi/7) automatically | |
| simplifies to -cos(3*pi/7) so only 2 of the 3 terms are | |
| recognized: | |
| >>> TRmorrie(cos(pi/7)*cos(2*pi/7)*cos(4*pi/7)) | |
| -sin(3*pi/7)*cos(3*pi/7)/(4*sin(pi/7)) | |
| A touch by TR8 resolves the expression to a Rational | |
| >>> TR8(_) | |
| -1/8 | |
| In this case, if eq is unsimplified, the answer is obtained | |
| directly: | |
| >>> eq = cos(pi/9)*cos(2*pi/9)*cos(3*pi/9)*cos(4*pi/9) | |
| >>> TRmorrie(eq) | |
| 1/16 | |
| But if angles are made canonical with TR3 then the answer | |
| is not simplified without further work: | |
| >>> TR3(eq) | |
| sin(pi/18)*cos(pi/9)*cos(2*pi/9)/2 | |
| >>> TRmorrie(_) | |
| sin(pi/18)*sin(4*pi/9)/(8*sin(pi/9)) | |
| >>> TR8(_) | |
| cos(7*pi/18)/(16*sin(pi/9)) | |
| >>> TR3(_) | |
| 1/16 | |
| The original expression would have resolve to 1/16 directly with TR8, | |
| however: | |
| >>> TR8(eq) | |
| 1/16 | |
| References | |
| ========== | |
| .. [1] https://en.wikipedia.org/wiki/Morrie%27s_law | |
| """ | |
| def f(rv, first=True): | |
| if not rv.is_Mul: | |
| return rv | |
| if first: | |
| n, d = rv.as_numer_denom() | |
| return f(n, 0)/f(d, 0) | |
| args = defaultdict(list) | |
| coss = {} | |
| other = [] | |
| for c in rv.args: | |
| b, e = c.as_base_exp() | |
| if e.is_Integer and isinstance(b, cos): | |
| co, a = b.args[0].as_coeff_Mul() | |
| args[a].append(co) | |
| coss[b] = e | |
| else: | |
| other.append(c) | |
| new = [] | |
| for a in args: | |
| c = args[a] | |
| c.sort() | |
| while c: | |
| k = 0 | |
| cc = ci = c[0] | |
| while cc in c: | |
| k += 1 | |
| cc *= 2 | |
| if k > 1: | |
| newarg = sin(2**k*ci*a)/2**k/sin(ci*a) | |
| # see how many times this can be taken | |
| take = None | |
| ccs = [] | |
| for i in range(k): | |
| cc /= 2 | |
| key = cos(a*cc, evaluate=False) | |
| ccs.append(cc) | |
| take = min(coss[key], take or coss[key]) | |
| # update exponent counts | |
| for i in range(k): | |
| cc = ccs.pop() | |
| key = cos(a*cc, evaluate=False) | |
| coss[key] -= take | |
| if not coss[key]: | |
| c.remove(cc) | |
| new.append(newarg**take) | |
| else: | |
| b = cos(c.pop(0)*a) | |
| other.append(b**coss[b]) | |
| if new: | |
| rv = Mul(*(new + other + [ | |
| cos(k*a, evaluate=False) for a in args for k in args[a]])) | |
| return rv | |
| return bottom_up(rv, f) | |
| def TR14(rv, first=True): | |
| """Convert factored powers of sin and cos identities into simpler | |
| expressions. | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import TR14 | |
| >>> from sympy.abc import x, y | |
| >>> from sympy import cos, sin | |
| >>> TR14((cos(x) - 1)*(cos(x) + 1)) | |
| -sin(x)**2 | |
| >>> TR14((sin(x) - 1)*(sin(x) + 1)) | |
| -cos(x)**2 | |
| >>> p1 = (cos(x) + 1)*(cos(x) - 1) | |
| >>> p2 = (cos(y) - 1)*2*(cos(y) + 1) | |
| >>> p3 = (3*(cos(y) - 1))*(3*(cos(y) + 1)) | |
| >>> TR14(p1*p2*p3*(x - 1)) | |
| -18*(x - 1)*sin(x)**2*sin(y)**4 | |
| """ | |
| def f(rv): | |
| if not rv.is_Mul: | |
| return rv | |
| if first: | |
| # sort them by location in numerator and denominator | |
| # so the code below can just deal with positive exponents | |
| n, d = rv.as_numer_denom() | |
| if d is not S.One: | |
| newn = TR14(n, first=False) | |
| newd = TR14(d, first=False) | |
| if newn != n or newd != d: | |
| rv = newn/newd | |
| return rv | |
| other = [] | |
| process = [] | |
| for a in rv.args: | |
| if a.is_Pow: | |
| b, e = a.as_base_exp() | |
| if not (e.is_integer or b.is_positive): | |
| other.append(a) | |
| continue | |
| a = b | |
| else: | |
| e = S.One | |
| m = as_f_sign_1(a) | |
| if not m or m[1].func not in (cos, sin): | |
| if e is S.One: | |
| other.append(a) | |
| else: | |
| other.append(a**e) | |
| continue | |
| g, f, si = m | |
| process.append((g, e.is_Number, e, f, si, a)) | |
| # sort them to get like terms next to each other | |
| process = list(ordered(process)) | |
| # keep track of whether there was any change | |
| nother = len(other) | |
| # access keys | |
| keys = (g, t, e, f, si, a) = list(range(6)) | |
| while process: | |
| A = process.pop(0) | |
| if process: | |
| B = process[0] | |
| if A[e].is_Number and B[e].is_Number: | |
| # both exponents are numbers | |
| if A[f] == B[f]: | |
| if A[si] != B[si]: | |
| B = process.pop(0) | |
| take = min(A[e], B[e]) | |
| # reinsert any remainder | |
| # the B will likely sort after A so check it first | |
| if B[e] != take: | |
| rem = [B[i] for i in keys] | |
| rem[e] -= take | |
| process.insert(0, rem) | |
| elif A[e] != take: | |
| rem = [A[i] for i in keys] | |
| rem[e] -= take | |
| process.insert(0, rem) | |
| if isinstance(A[f], cos): | |
| t = sin | |
| else: | |
| t = cos | |
| other.append((-A[g]*B[g]*t(A[f].args[0])**2)**take) | |
| continue | |
| elif A[e] == B[e]: | |
| # both exponents are equal symbols | |
| if A[f] == B[f]: | |
| if A[si] != B[si]: | |
| B = process.pop(0) | |
| take = A[e] | |
| if isinstance(A[f], cos): | |
| t = sin | |
| else: | |
| t = cos | |
| other.append((-A[g]*B[g]*t(A[f].args[0])**2)**take) | |
| continue | |
| # either we are done or neither condition above applied | |
| other.append(A[a]**A[e]) | |
| if len(other) != nother: | |
| rv = Mul(*other) | |
| return rv | |
| return bottom_up(rv, f) | |
| def TR15(rv, max=4, pow=False): | |
| """Convert sin(x)**-2 to 1 + cot(x)**2. | |
| See _TR56 docstring for advanced use of ``max`` and ``pow``. | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import TR15 | |
| >>> from sympy.abc import x | |
| >>> from sympy import sin | |
| >>> TR15(1 - 1/sin(x)**2) | |
| -cot(x)**2 | |
| """ | |
| def f(rv): | |
| if not (isinstance(rv, Pow) and isinstance(rv.base, sin)): | |
| return rv | |
| e = rv.exp | |
| if e % 2 == 1: | |
| return TR15(rv.base**(e + 1))/rv.base | |
| ia = 1/rv | |
| a = _TR56(ia, sin, cot, lambda x: 1 + x, max=max, pow=pow) | |
| if a != ia: | |
| rv = a | |
| return rv | |
| return bottom_up(rv, f) | |
| def TR16(rv, max=4, pow=False): | |
| """Convert cos(x)**-2 to 1 + tan(x)**2. | |
| See _TR56 docstring for advanced use of ``max`` and ``pow``. | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import TR16 | |
| >>> from sympy.abc import x | |
| >>> from sympy import cos | |
| >>> TR16(1 - 1/cos(x)**2) | |
| -tan(x)**2 | |
| """ | |
| def f(rv): | |
| if not (isinstance(rv, Pow) and isinstance(rv.base, cos)): | |
| return rv | |
| e = rv.exp | |
| if e % 2 == 1: | |
| return TR15(rv.base**(e + 1))/rv.base | |
| ia = 1/rv | |
| a = _TR56(ia, cos, tan, lambda x: 1 + x, max=max, pow=pow) | |
| if a != ia: | |
| rv = a | |
| return rv | |
| return bottom_up(rv, f) | |
| def TR111(rv): | |
| """Convert f(x)**-i to g(x)**i where either ``i`` is an integer | |
| or the base is positive and f, g are: tan, cot; sin, csc; or cos, sec. | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import TR111 | |
| >>> from sympy.abc import x | |
| >>> from sympy import tan | |
| >>> TR111(1 - 1/tan(x)**2) | |
| 1 - cot(x)**2 | |
| """ | |
| def f(rv): | |
| if not ( | |
| isinstance(rv, Pow) and | |
| (rv.base.is_positive or rv.exp.is_integer and rv.exp.is_negative)): | |
| return rv | |
| if isinstance(rv.base, tan): | |
| return cot(rv.base.args[0])**-rv.exp | |
| elif isinstance(rv.base, sin): | |
| return csc(rv.base.args[0])**-rv.exp | |
| elif isinstance(rv.base, cos): | |
| return sec(rv.base.args[0])**-rv.exp | |
| return rv | |
| return bottom_up(rv, f) | |
| def TR22(rv, max=4, pow=False): | |
| """Convert tan(x)**2 to sec(x)**2 - 1 and cot(x)**2 to csc(x)**2 - 1. | |
| See _TR56 docstring for advanced use of ``max`` and ``pow``. | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import TR22 | |
| >>> from sympy.abc import x | |
| >>> from sympy import tan, cot | |
| >>> TR22(1 + tan(x)**2) | |
| sec(x)**2 | |
| >>> TR22(1 + cot(x)**2) | |
| csc(x)**2 | |
| """ | |
| def f(rv): | |
| if not (isinstance(rv, Pow) and rv.base.func in (cot, tan)): | |
| return rv | |
| rv = _TR56(rv, tan, sec, lambda x: x - 1, max=max, pow=pow) | |
| rv = _TR56(rv, cot, csc, lambda x: x - 1, max=max, pow=pow) | |
| return rv | |
| return bottom_up(rv, f) | |
| def TRpower(rv): | |
| """Convert sin(x)**n and cos(x)**n with positive n to sums. | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import TRpower | |
| >>> from sympy.abc import x | |
| >>> from sympy import cos, sin | |
| >>> TRpower(sin(x)**6) | |
| -15*cos(2*x)/32 + 3*cos(4*x)/16 - cos(6*x)/32 + 5/16 | |
| >>> TRpower(sin(x)**3*cos(2*x)**4) | |
| (3*sin(x)/4 - sin(3*x)/4)*(cos(4*x)/2 + cos(8*x)/8 + 3/8) | |
| References | |
| ========== | |
| .. [1] https://en.wikipedia.org/wiki/List_of_trigonometric_identities#Power-reduction_formulae | |
| """ | |
| def f(rv): | |
| if not (isinstance(rv, Pow) and isinstance(rv.base, (sin, cos))): | |
| return rv | |
| b, n = rv.as_base_exp() | |
| x = b.args[0] | |
| if n.is_Integer and n.is_positive: | |
| if n.is_odd and isinstance(b, cos): | |
| rv = 2**(1-n)*Add(*[binomial(n, k)*cos((n - 2*k)*x) | |
| for k in range((n + 1)/2)]) | |
| elif n.is_odd and isinstance(b, sin): | |
| rv = 2**(1-n)*S.NegativeOne**((n-1)/2)*Add(*[binomial(n, k)* | |
| S.NegativeOne**k*sin((n - 2*k)*x) for k in range((n + 1)/2)]) | |
| elif n.is_even and isinstance(b, cos): | |
| rv = 2**(1-n)*Add(*[binomial(n, k)*cos((n - 2*k)*x) | |
| for k in range(n/2)]) | |
| elif n.is_even and isinstance(b, sin): | |
| rv = 2**(1-n)*S.NegativeOne**(n/2)*Add(*[binomial(n, k)* | |
| S.NegativeOne**k*cos((n - 2*k)*x) for k in range(n/2)]) | |
| if n.is_even: | |
| rv += 2**(-n)*binomial(n, n/2) | |
| return rv | |
| return bottom_up(rv, f) | |
| def L(rv): | |
| """Return count of trigonometric functions in expression. | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import L | |
| >>> from sympy.abc import x | |
| >>> from sympy import cos, sin | |
| >>> L(cos(x)+sin(x)) | |
| 2 | |
| """ | |
| return S(rv.count(TrigonometricFunction)) | |
| # ============== end of basic Fu-like tools ===================== | |
| if SYMPY_DEBUG: | |
| (TR0, TR1, TR2, TR3, TR4, TR5, TR6, TR7, TR8, TR9, TR10, TR11, TR12, TR13, | |
| TR2i, TRmorrie, TR14, TR15, TR16, TR12i, TR111, TR22 | |
| )= list(map(debug, | |
| (TR0, TR1, TR2, TR3, TR4, TR5, TR6, TR7, TR8, TR9, TR10, TR11, TR12, TR13, | |
| TR2i, TRmorrie, TR14, TR15, TR16, TR12i, TR111, TR22))) | |
| # tuples are chains -- (f, g) -> lambda x: g(f(x)) | |
| # lists are choices -- [f, g] -> lambda x: min(f(x), g(x), key=objective) | |
| CTR1 = [(TR5, TR0), (TR6, TR0), identity] | |
| CTR2 = (TR11, [(TR5, TR0), (TR6, TR0), TR0]) | |
| CTR3 = [(TRmorrie, TR8, TR0), (TRmorrie, TR8, TR10i, TR0), identity] | |
| CTR4 = [(TR4, TR10i), identity] | |
| RL1 = (TR4, TR3, TR4, TR12, TR4, TR13, TR4, TR0) | |
| # XXX it's a little unclear how this one is to be implemented | |
| # see Fu paper of reference, page 7. What is the Union symbol referring to? | |
| # The diagram shows all these as one chain of transformations, but the | |
| # text refers to them being applied independently. Also, a break | |
| # if L starts to increase has not been implemented. | |
| RL2 = [ | |
| (TR4, TR3, TR10, TR4, TR3, TR11), | |
| (TR5, TR7, TR11, TR4), | |
| (CTR3, CTR1, TR9, CTR2, TR4, TR9, TR9, CTR4), | |
| identity, | |
| ] | |
| def fu(rv, measure=lambda x: (L(x), x.count_ops())): | |
| """Attempt to simplify expression by using transformation rules given | |
| in the algorithm by Fu et al. | |
| :func:`fu` will try to minimize the objective function ``measure``. | |
| By default this first minimizes the number of trig terms and then minimizes | |
| the number of total operations. | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import fu | |
| >>> from sympy import cos, sin, tan, pi, S, sqrt | |
| >>> from sympy.abc import x, y, a, b | |
| >>> fu(sin(50)**2 + cos(50)**2 + sin(pi/6)) | |
| 3/2 | |
| >>> fu(sqrt(6)*cos(x) + sqrt(2)*sin(x)) | |
| 2*sqrt(2)*sin(x + pi/3) | |
| CTR1 example | |
| >>> eq = sin(x)**4 - cos(y)**2 + sin(y)**2 + 2*cos(x)**2 | |
| >>> fu(eq) | |
| cos(x)**4 - 2*cos(y)**2 + 2 | |
| CTR2 example | |
| >>> fu(S.Half - cos(2*x)/2) | |
| sin(x)**2 | |
| CTR3 example | |
| >>> fu(sin(a)*(cos(b) - sin(b)) + cos(a)*(sin(b) + cos(b))) | |
| sqrt(2)*sin(a + b + pi/4) | |
| CTR4 example | |
| >>> fu(sqrt(3)*cos(x)/2 + sin(x)/2) | |
| sin(x + pi/3) | |
| Example 1 | |
| >>> fu(1-sin(2*x)**2/4-sin(y)**2-cos(x)**4) | |
| -cos(x)**2 + cos(y)**2 | |
| Example 2 | |
| >>> fu(cos(4*pi/9)) | |
| sin(pi/18) | |
| >>> fu(cos(pi/9)*cos(2*pi/9)*cos(3*pi/9)*cos(4*pi/9)) | |
| 1/16 | |
| Example 3 | |
| >>> fu(tan(7*pi/18)+tan(5*pi/18)-sqrt(3)*tan(5*pi/18)*tan(7*pi/18)) | |
| -sqrt(3) | |
| Objective function example | |
| >>> fu(sin(x)/cos(x)) # default objective function | |
| tan(x) | |
| >>> fu(sin(x)/cos(x), measure=lambda x: -x.count_ops()) # maximize op count | |
| sin(x)/cos(x) | |
| References | |
| ========== | |
| .. [1] https://www.sciencedirect.com/science/article/pii/S0895717706001609 | |
| """ | |
| fRL1 = greedy(RL1, measure) | |
| fRL2 = greedy(RL2, measure) | |
| was = rv | |
| rv = sympify(rv) | |
| if not isinstance(rv, Expr): | |
| return rv.func(*[fu(a, measure=measure) for a in rv.args]) | |
| rv = TR1(rv) | |
| if rv.has(tan, cot): | |
| rv1 = fRL1(rv) | |
| if (measure(rv1) < measure(rv)): | |
| rv = rv1 | |
| if rv.has(tan, cot): | |
| rv = TR2(rv) | |
| if rv.has(sin, cos): | |
| rv1 = fRL2(rv) | |
| rv2 = TR8(TRmorrie(rv1)) | |
| rv = min([was, rv, rv1, rv2], key=measure) | |
| return min(TR2i(rv), rv, key=measure) | |
| def process_common_addends(rv, do, key2=None, key1=True): | |
| """Apply ``do`` to addends of ``rv`` that (if ``key1=True``) share at least | |
| a common absolute value of their coefficient and the value of ``key2`` when | |
| applied to the argument. If ``key1`` is False ``key2`` must be supplied and | |
| will be the only key applied. | |
| """ | |
| # collect by absolute value of coefficient and key2 | |
| absc = defaultdict(list) | |
| if key1: | |
| for a in rv.args: | |
| c, a = a.as_coeff_Mul() | |
| if c < 0: | |
| c = -c | |
| a = -a # put the sign on `a` | |
| absc[(c, key2(a) if key2 else 1)].append(a) | |
| elif key2: | |
| for a in rv.args: | |
| absc[(S.One, key2(a))].append(a) | |
| else: | |
| raise ValueError('must have at least one key') | |
| args = [] | |
| hit = False | |
| for k in absc: | |
| v = absc[k] | |
| c, _ = k | |
| if len(v) > 1: | |
| e = Add(*v, evaluate=False) | |
| new = do(e) | |
| if new != e: | |
| e = new | |
| hit = True | |
| args.append(c*e) | |
| else: | |
| args.append(c*v[0]) | |
| if hit: | |
| rv = Add(*args) | |
| return rv | |
| fufuncs = ''' | |
| TR0 TR1 TR2 TR3 TR4 TR5 TR6 TR7 TR8 TR9 TR10 TR10i TR11 | |
| TR12 TR13 L TR2i TRmorrie TR12i | |
| TR14 TR15 TR16 TR111 TR22'''.split() | |
| FU = dict(list(zip(fufuncs, list(map(locals().get, fufuncs))))) | |
| def _ROOT2(): | |
| return sqrt(2) | |
| def _ROOT3(): | |
| return sqrt(3) | |
| def _invROOT3(): | |
| return 1/sqrt(3) | |
| def trig_split(a, b, two=False): | |
| """Return the gcd, s1, s2, a1, a2, bool where | |
| If two is False (default) then:: | |
| a + b = gcd*(s1*f(a1) + s2*f(a2)) where f = cos if bool else sin | |
| else: | |
| if bool, a + b was +/- cos(a1)*cos(a2) +/- sin(a1)*sin(a2) and equals | |
| n1*gcd*cos(a - b) if n1 == n2 else | |
| n1*gcd*cos(a + b) | |
| else a + b was +/- cos(a1)*sin(a2) +/- sin(a1)*cos(a2) and equals | |
| n1*gcd*sin(a + b) if n1 = n2 else | |
| n1*gcd*sin(b - a) | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import trig_split | |
| >>> from sympy.abc import x, y, z | |
| >>> from sympy import cos, sin, sqrt | |
| >>> trig_split(cos(x), cos(y)) | |
| (1, 1, 1, x, y, True) | |
| >>> trig_split(2*cos(x), -2*cos(y)) | |
| (2, 1, -1, x, y, True) | |
| >>> trig_split(cos(x)*sin(y), cos(y)*sin(y)) | |
| (sin(y), 1, 1, x, y, True) | |
| >>> trig_split(cos(x), -sqrt(3)*sin(x), two=True) | |
| (2, 1, -1, x, pi/6, False) | |
| >>> trig_split(cos(x), sin(x), two=True) | |
| (sqrt(2), 1, 1, x, pi/4, False) | |
| >>> trig_split(cos(x), -sin(x), two=True) | |
| (sqrt(2), 1, -1, x, pi/4, False) | |
| >>> trig_split(sqrt(2)*cos(x), -sqrt(6)*sin(x), two=True) | |
| (2*sqrt(2), 1, -1, x, pi/6, False) | |
| >>> trig_split(-sqrt(6)*cos(x), -sqrt(2)*sin(x), two=True) | |
| (-2*sqrt(2), 1, 1, x, pi/3, False) | |
| >>> trig_split(cos(x)/sqrt(6), sin(x)/sqrt(2), two=True) | |
| (sqrt(6)/3, 1, 1, x, pi/6, False) | |
| >>> trig_split(-sqrt(6)*cos(x)*sin(y), -sqrt(2)*sin(x)*sin(y), two=True) | |
| (-2*sqrt(2)*sin(y), 1, 1, x, pi/3, False) | |
| >>> trig_split(cos(x), sin(x)) | |
| >>> trig_split(cos(x), sin(z)) | |
| >>> trig_split(2*cos(x), -sin(x)) | |
| >>> trig_split(cos(x), -sqrt(3)*sin(x)) | |
| >>> trig_split(cos(x)*cos(y), sin(x)*sin(z)) | |
| >>> trig_split(cos(x)*cos(y), sin(x)*sin(y)) | |
| >>> trig_split(-sqrt(6)*cos(x), sqrt(2)*sin(x)*sin(y), two=True) | |
| """ | |
| a, b = [Factors(i) for i in (a, b)] | |
| ua, ub = a.normal(b) | |
| gcd = a.gcd(b).as_expr() | |
| n1 = n2 = 1 | |
| if S.NegativeOne in ua.factors: | |
| ua = ua.quo(S.NegativeOne) | |
| n1 = -n1 | |
| elif S.NegativeOne in ub.factors: | |
| ub = ub.quo(S.NegativeOne) | |
| n2 = -n2 | |
| a, b = [i.as_expr() for i in (ua, ub)] | |
| def pow_cos_sin(a, two): | |
| """Return ``a`` as a tuple (r, c, s) such that | |
| ``a = (r or 1)*(c or 1)*(s or 1)``. | |
| Three arguments are returned (radical, c-factor, s-factor) as | |
| long as the conditions set by ``two`` are met; otherwise None is | |
| returned. If ``two`` is True there will be one or two non-None | |
| values in the tuple: c and s or c and r or s and r or s or c with c | |
| being a cosine function (if possible) else a sine, and s being a sine | |
| function (if possible) else oosine. If ``two`` is False then there | |
| will only be a c or s term in the tuple. | |
| ``two`` also require that either two cos and/or sin be present (with | |
| the condition that if the functions are the same the arguments are | |
| different or vice versa) or that a single cosine or a single sine | |
| be present with an optional radical. | |
| If the above conditions dictated by ``two`` are not met then None | |
| is returned. | |
| """ | |
| c = s = None | |
| co = S.One | |
| if a.is_Mul: | |
| co, a = a.as_coeff_Mul() | |
| if len(a.args) > 2 or not two: | |
| return None | |
| if a.is_Mul: | |
| args = list(a.args) | |
| else: | |
| args = [a] | |
| a = args.pop(0) | |
| if isinstance(a, cos): | |
| c = a | |
| elif isinstance(a, sin): | |
| s = a | |
| elif a.is_Pow and a.exp is S.Half: # autoeval doesn't allow -1/2 | |
| co *= a | |
| else: | |
| return None | |
| if args: | |
| b = args[0] | |
| if isinstance(b, cos): | |
| if c: | |
| s = b | |
| else: | |
| c = b | |
| elif isinstance(b, sin): | |
| if s: | |
| c = b | |
| else: | |
| s = b | |
| elif b.is_Pow and b.exp is S.Half: | |
| co *= b | |
| else: | |
| return None | |
| return co if co is not S.One else None, c, s | |
| elif isinstance(a, cos): | |
| c = a | |
| elif isinstance(a, sin): | |
| s = a | |
| if c is None and s is None: | |
| return | |
| co = co if co is not S.One else None | |
| return co, c, s | |
| # get the parts | |
| m = pow_cos_sin(a, two) | |
| if m is None: | |
| return | |
| coa, ca, sa = m | |
| m = pow_cos_sin(b, two) | |
| if m is None: | |
| return | |
| cob, cb, sb = m | |
| # check them | |
| if (not ca) and cb or ca and isinstance(ca, sin): | |
| coa, ca, sa, cob, cb, sb = cob, cb, sb, coa, ca, sa | |
| n1, n2 = n2, n1 | |
| if not two: # need cos(x) and cos(y) or sin(x) and sin(y) | |
| c = ca or sa | |
| s = cb or sb | |
| if not isinstance(c, s.func): | |
| return None | |
| return gcd, n1, n2, c.args[0], s.args[0], isinstance(c, cos) | |
| else: | |
| if not coa and not cob: | |
| if (ca and cb and sa and sb): | |
| if isinstance(ca, sa.func) is not isinstance(cb, sb.func): | |
| return | |
| args = {j.args for j in (ca, sa)} | |
| if not all(i.args in args for i in (cb, sb)): | |
| return | |
| return gcd, n1, n2, ca.args[0], sa.args[0], isinstance(ca, sa.func) | |
| if ca and sa or cb and sb or \ | |
| two and (ca is None and sa is None or cb is None and sb is None): | |
| return | |
| c = ca or sa | |
| s = cb or sb | |
| if c.args != s.args: | |
| return | |
| if not coa: | |
| coa = S.One | |
| if not cob: | |
| cob = S.One | |
| if coa is cob: | |
| gcd *= _ROOT2() | |
| return gcd, n1, n2, c.args[0], pi/4, False | |
| elif coa/cob == _ROOT3(): | |
| gcd *= 2*cob | |
| return gcd, n1, n2, c.args[0], pi/3, False | |
| elif coa/cob == _invROOT3(): | |
| gcd *= 2*coa | |
| return gcd, n1, n2, c.args[0], pi/6, False | |
| def as_f_sign_1(e): | |
| """If ``e`` is a sum that can be written as ``g*(a + s)`` where | |
| ``s`` is ``+/-1``, return ``g``, ``a``, and ``s`` where ``a`` does | |
| not have a leading negative coefficient. | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import as_f_sign_1 | |
| >>> from sympy.abc import x | |
| >>> as_f_sign_1(x + 1) | |
| (1, x, 1) | |
| >>> as_f_sign_1(x - 1) | |
| (1, x, -1) | |
| >>> as_f_sign_1(-x + 1) | |
| (-1, x, -1) | |
| >>> as_f_sign_1(-x - 1) | |
| (-1, x, 1) | |
| >>> as_f_sign_1(2*x + 2) | |
| (2, x, 1) | |
| """ | |
| if not e.is_Add or len(e.args) != 2: | |
| return | |
| # exact match | |
| a, b = e.args | |
| if a in (S.NegativeOne, S.One): | |
| g = S.One | |
| if b.is_Mul and b.args[0].is_Number and b.args[0] < 0: | |
| a, b = -a, -b | |
| g = -g | |
| return g, b, a | |
| # gcd match | |
| a, b = [Factors(i) for i in e.args] | |
| ua, ub = a.normal(b) | |
| gcd = a.gcd(b).as_expr() | |
| if S.NegativeOne in ua.factors: | |
| ua = ua.quo(S.NegativeOne) | |
| n1 = -1 | |
| n2 = 1 | |
| elif S.NegativeOne in ub.factors: | |
| ub = ub.quo(S.NegativeOne) | |
| n1 = 1 | |
| n2 = -1 | |
| else: | |
| n1 = n2 = 1 | |
| a, b = [i.as_expr() for i in (ua, ub)] | |
| if a is S.One: | |
| a, b = b, a | |
| n1, n2 = n2, n1 | |
| if n1 == -1: | |
| gcd = -gcd | |
| n2 = -n2 | |
| if b is S.One: | |
| return gcd, a, n2 | |
| def _osborne(e, d): | |
| """Replace all hyperbolic functions with trig functions using | |
| the Osborne rule. | |
| Notes | |
| ===== | |
| ``d`` is a dummy variable to prevent automatic evaluation | |
| of trigonometric/hyperbolic functions. | |
| References | |
| ========== | |
| .. [1] https://en.wikipedia.org/wiki/Hyperbolic_function | |
| """ | |
| def f(rv): | |
| if not isinstance(rv, HyperbolicFunction): | |
| return rv | |
| a = rv.args[0] | |
| a = a*d if not a.is_Add else Add._from_args([i*d for i in a.args]) | |
| if isinstance(rv, sinh): | |
| return I*sin(a) | |
| elif isinstance(rv, cosh): | |
| return cos(a) | |
| elif isinstance(rv, tanh): | |
| return I*tan(a) | |
| elif isinstance(rv, coth): | |
| return cot(a)/I | |
| elif isinstance(rv, sech): | |
| return sec(a) | |
| elif isinstance(rv, csch): | |
| return csc(a)/I | |
| else: | |
| raise NotImplementedError('unhandled %s' % rv.func) | |
| return bottom_up(e, f) | |
| def _osbornei(e, d): | |
| """Replace all trig functions with hyperbolic functions using | |
| the Osborne rule. | |
| Notes | |
| ===== | |
| ``d`` is a dummy variable to prevent automatic evaluation | |
| of trigonometric/hyperbolic functions. | |
| References | |
| ========== | |
| .. [1] https://en.wikipedia.org/wiki/Hyperbolic_function | |
| """ | |
| def f(rv): | |
| if not isinstance(rv, TrigonometricFunction): | |
| return rv | |
| const, x = rv.args[0].as_independent(d, as_Add=True) | |
| a = x.xreplace({d: S.One}) + const*I | |
| if isinstance(rv, sin): | |
| return sinh(a)/I | |
| elif isinstance(rv, cos): | |
| return cosh(a) | |
| elif isinstance(rv, tan): | |
| return tanh(a)/I | |
| elif isinstance(rv, cot): | |
| return coth(a)*I | |
| elif isinstance(rv, sec): | |
| return sech(a) | |
| elif isinstance(rv, csc): | |
| return csch(a)*I | |
| else: | |
| raise NotImplementedError('unhandled %s' % rv.func) | |
| return bottom_up(e, f) | |
| def hyper_as_trig(rv): | |
| """Return an expression containing hyperbolic functions in terms | |
| of trigonometric functions. Any trigonometric functions initially | |
| present are replaced with Dummy symbols and the function to undo | |
| the masking and the conversion back to hyperbolics is also returned. It | |
| should always be true that:: | |
| t, f = hyper_as_trig(expr) | |
| expr == f(t) | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import hyper_as_trig, fu | |
| >>> from sympy.abc import x | |
| >>> from sympy import cosh, sinh | |
| >>> eq = sinh(x)**2 + cosh(x)**2 | |
| >>> t, f = hyper_as_trig(eq) | |
| >>> f(fu(t)) | |
| cosh(2*x) | |
| References | |
| ========== | |
| .. [1] https://en.wikipedia.org/wiki/Hyperbolic_function | |
| """ | |
| from sympy.simplify.simplify import signsimp | |
| from sympy.simplify.radsimp import collect | |
| # mask off trig functions | |
| trigs = rv.atoms(TrigonometricFunction) | |
| reps = [(t, Dummy()) for t in trigs] | |
| masked = rv.xreplace(dict(reps)) | |
| # get inversion substitutions in place | |
| reps = [(v, k) for k, v in reps] | |
| d = Dummy() | |
| return _osborne(masked, d), lambda x: collect(signsimp( | |
| _osbornei(x, d).xreplace(dict(reps))), S.ImaginaryUnit) | |
| def sincos_to_sum(expr): | |
| """Convert products and powers of sin and cos to sums. | |
| Explanation | |
| =========== | |
| Applied power reduction TRpower first, then expands products, and | |
| converts products to sums with TR8. | |
| Examples | |
| ======== | |
| >>> from sympy.simplify.fu import sincos_to_sum | |
| >>> from sympy.abc import x | |
| >>> from sympy import cos, sin | |
| >>> sincos_to_sum(16*sin(x)**3*cos(2*x)**2) | |
| 7*sin(x) - 5*sin(3*x) + 3*sin(5*x) - sin(7*x) | |
| """ | |
| if not expr.has(cos, sin): | |
| return expr | |
| else: | |
| return TR8(expand_mul(TRpower(expr))) | |
Xet Storage Details
- Size:
- 62.2 kB
- Xet hash:
- ac9446cef946b6ac0f05557f9888fa48e621af429dd703aa1abd84041d101ac9
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.