trigsimp.py 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252
  1. from collections import defaultdict
  2. from functools import reduce
  3. from sympy.core import (sympify, Basic, S, Expr, factor_terms,
  4. Mul, Add, bottom_up)
  5. from sympy.core.cache import cacheit
  6. from sympy.core.function import (count_ops, _mexpand, FunctionClass, expand,
  7. expand_mul, _coeff_isneg, Derivative)
  8. from sympy.core.numbers import I, Integer, igcd
  9. from sympy.core.sorting import _nodes
  10. from sympy.core.symbol import Dummy, symbols, Wild
  11. from sympy.external.gmpy import SYMPY_INTS
  12. from sympy.functions import sin, cos, exp, cosh, tanh, sinh, tan, cot, coth
  13. from sympy.functions import atan2
  14. from sympy.functions.elementary.hyperbolic import HyperbolicFunction
  15. from sympy.functions.elementary.trigonometric import TrigonometricFunction
  16. from sympy.polys import Poly, factor, cancel, parallel_poly_from_expr
  17. from sympy.polys.domains import ZZ
  18. from sympy.polys.polyerrors import PolificationFailed
  19. from sympy.polys.polytools import groebner
  20. from sympy.simplify.cse_main import cse
  21. from sympy.strategies.core import identity
  22. from sympy.strategies.tree import greedy
  23. from sympy.utilities.iterables import iterable
  24. from sympy.utilities.misc import debug
  25. def trigsimp_groebner(expr, hints=[], quick=False, order="grlex",
  26. polynomial=False):
  27. """
  28. Simplify trigonometric expressions using a groebner basis algorithm.
  29. Explanation
  30. ===========
  31. This routine takes a fraction involving trigonometric or hyperbolic
  32. expressions, and tries to simplify it. The primary metric is the
  33. total degree. Some attempts are made to choose the simplest possible
  34. expression of the minimal degree, but this is non-rigorous, and also
  35. very slow (see the ``quick=True`` option).
  36. If ``polynomial`` is set to True, instead of simplifying numerator and
  37. denominator together, this function just brings numerator and denominator
  38. into a canonical form. This is much faster, but has potentially worse
  39. results. However, if the input is a polynomial, then the result is
  40. guaranteed to be an equivalent polynomial of minimal degree.
  41. The most important option is hints. Its entries can be any of the
  42. following:
  43. - a natural number
  44. - a function
  45. - an iterable of the form (func, var1, var2, ...)
  46. - anything else, interpreted as a generator
  47. A number is used to indicate that the search space should be increased.
  48. A function is used to indicate that said function is likely to occur in a
  49. simplified expression.
  50. An iterable is used indicate that func(var1 + var2 + ...) is likely to
  51. occur in a simplified .
  52. An additional generator also indicates that it is likely to occur.
  53. (See examples below).
  54. This routine carries out various computationally intensive algorithms.
  55. The option ``quick=True`` can be used to suppress one particularly slow
  56. step (at the expense of potentially more complicated results, but never at
  57. the expense of increased total degree).
  58. Examples
  59. ========
  60. >>> from sympy.abc import x, y
  61. >>> from sympy import sin, tan, cos, sinh, cosh, tanh
  62. >>> from sympy.simplify.trigsimp import trigsimp_groebner
  63. Suppose you want to simplify ``sin(x)*cos(x)``. Naively, nothing happens:
  64. >>> ex = sin(x)*cos(x)
  65. >>> trigsimp_groebner(ex)
  66. sin(x)*cos(x)
  67. This is because ``trigsimp_groebner`` only looks for a simplification
  68. involving just ``sin(x)`` and ``cos(x)``. You can tell it to also try
  69. ``2*x`` by passing ``hints=[2]``:
  70. >>> trigsimp_groebner(ex, hints=[2])
  71. sin(2*x)/2
  72. >>> trigsimp_groebner(sin(x)**2 - cos(x)**2, hints=[2])
  73. -cos(2*x)
  74. Increasing the search space this way can quickly become expensive. A much
  75. faster way is to give a specific expression that is likely to occur:
  76. >>> trigsimp_groebner(ex, hints=[sin(2*x)])
  77. sin(2*x)/2
  78. Hyperbolic expressions are similarly supported:
  79. >>> trigsimp_groebner(sinh(2*x)/sinh(x))
  80. 2*cosh(x)
  81. Note how no hints had to be passed, since the expression already involved
  82. ``2*x``.
  83. The tangent function is also supported. You can either pass ``tan`` in the
  84. hints, to indicate that tan should be tried whenever cosine or sine are,
  85. or you can pass a specific generator:
  86. >>> trigsimp_groebner(sin(x)/cos(x), hints=[tan])
  87. tan(x)
  88. >>> trigsimp_groebner(sinh(x)/cosh(x), hints=[tanh(x)])
  89. tanh(x)
  90. Finally, you can use the iterable form to suggest that angle sum formulae
  91. should be tried:
  92. >>> ex = (tan(x) + tan(y))/(1 - tan(x)*tan(y))
  93. >>> trigsimp_groebner(ex, hints=[(tan, x, y)])
  94. tan(x + y)
  95. """
  96. # TODO
  97. # - preprocess by replacing everything by funcs we can handle
  98. # - optionally use cot instead of tan
  99. # - more intelligent hinting.
  100. # For example, if the ideal is small, and we have sin(x), sin(y),
  101. # add sin(x + y) automatically... ?
  102. # - algebraic numbers ...
  103. # - expressions of lowest degree are not distinguished properly
  104. # e.g. 1 - sin(x)**2
  105. # - we could try to order the generators intelligently, so as to influence
  106. # which monomials appear in the quotient basis
  107. # THEORY
  108. # ------
  109. # Ratsimpmodprime above can be used to "simplify" a rational function
  110. # modulo a prime ideal. "Simplify" mainly means finding an equivalent
  111. # expression of lower total degree.
  112. #
  113. # We intend to use this to simplify trigonometric functions. To do that,
  114. # we need to decide (a) which ring to use, and (b) modulo which ideal to
  115. # simplify. In practice, (a) means settling on a list of "generators"
  116. # a, b, c, ..., such that the fraction we want to simplify is a rational
  117. # function in a, b, c, ..., with coefficients in ZZ (integers).
  118. # (2) means that we have to decide what relations to impose on the
  119. # generators. There are two practical problems:
  120. # (1) The ideal has to be *prime* (a technical term).
  121. # (2) The relations have to be polynomials in the generators.
  122. #
  123. # We typically have two kinds of generators:
  124. # - trigonometric expressions, like sin(x), cos(5*x), etc
  125. # - "everything else", like gamma(x), pi, etc.
  126. #
  127. # Since this function is trigsimp, we will concentrate on what to do with
  128. # trigonometric expressions. We can also simplify hyperbolic expressions,
  129. # but the extensions should be clear.
  130. #
  131. # One crucial point is that all *other* generators really should behave
  132. # like indeterminates. In particular if (say) "I" is one of them, then
  133. # in fact I**2 + 1 = 0 and we may and will compute non-sensical
  134. # expressions. However, we can work with a dummy and add the relation
  135. # I**2 + 1 = 0 to our ideal, then substitute back in the end.
  136. #
  137. # Now regarding trigonometric generators. We split them into groups,
  138. # according to the argument of the trigonometric functions. We want to
  139. # organise this in such a way that most trigonometric identities apply in
  140. # the same group. For example, given sin(x), cos(2*x) and cos(y), we would
  141. # group as [sin(x), cos(2*x)] and [cos(y)].
  142. #
  143. # Our prime ideal will be built in three steps:
  144. # (1) For each group, compute a "geometrically prime" ideal of relations.
  145. # Geometrically prime means that it generates a prime ideal in
  146. # CC[gens], not just ZZ[gens].
  147. # (2) Take the union of all the generators of the ideals for all groups.
  148. # By the geometric primality condition, this is still prime.
  149. # (3) Add further inter-group relations which preserve primality.
  150. #
  151. # Step (1) works as follows. We will isolate common factors in the
  152. # argument, so that all our generators are of the form sin(n*x), cos(n*x)
  153. # or tan(n*x), with n an integer. Suppose first there are no tan terms.
  154. # The ideal [sin(x)**2 + cos(x)**2 - 1] is geometrically prime, since
  155. # X**2 + Y**2 - 1 is irreducible over CC.
  156. # Now, if we have a generator sin(n*x), than we can, using trig identities,
  157. # express sin(n*x) as a polynomial in sin(x) and cos(x). We can add this
  158. # relation to the ideal, preserving geometric primality, since the quotient
  159. # ring is unchanged.
  160. # Thus we have treated all sin and cos terms.
  161. # For tan(n*x), we add a relation tan(n*x)*cos(n*x) - sin(n*x) = 0.
  162. # (This requires of course that we already have relations for cos(n*x) and
  163. # sin(n*x).) It is not obvious, but it seems that this preserves geometric
  164. # primality.
  165. # XXX A real proof would be nice. HELP!
  166. # Sketch that <S**2 + C**2 - 1, C*T - S> is a prime ideal of
  167. # CC[S, C, T]:
  168. # - it suffices to show that the projective closure in CP**3 is
  169. # irreducible
  170. # - using the half-angle substitutions, we can express sin(x), tan(x),
  171. # cos(x) as rational functions in tan(x/2)
  172. # - from this, we get a rational map from CP**1 to our curve
  173. # - this is a morphism, hence the curve is prime
  174. #
  175. # Step (2) is trivial.
  176. #
  177. # Step (3) works by adding selected relations of the form
  178. # sin(x + y) - sin(x)*cos(y) - sin(y)*cos(x), etc. Geometric primality is
  179. # preserved by the same argument as before.
  180. def parse_hints(hints):
  181. """Split hints into (n, funcs, iterables, gens)."""
  182. n = 1
  183. funcs, iterables, gens = [], [], []
  184. for e in hints:
  185. if isinstance(e, (SYMPY_INTS, Integer)):
  186. n = e
  187. elif isinstance(e, FunctionClass):
  188. funcs.append(e)
  189. elif iterable(e):
  190. iterables.append((e[0], e[1:]))
  191. # XXX sin(x+2y)?
  192. # Note: we go through polys so e.g.
  193. # sin(-x) -> -sin(x) -> sin(x)
  194. gens.extend(parallel_poly_from_expr(
  195. [e[0](x) for x in e[1:]] + [e[0](Add(*e[1:]))])[1].gens)
  196. else:
  197. gens.append(e)
  198. return n, funcs, iterables, gens
  199. def build_ideal(x, terms):
  200. """
  201. Build generators for our ideal. ``Terms`` is an iterable with elements of
  202. the form (fn, coeff), indicating that we have a generator fn(coeff*x).
  203. If any of the terms is trigonometric, sin(x) and cos(x) are guaranteed
  204. to appear in terms. Similarly for hyperbolic functions. For tan(n*x),
  205. sin(n*x) and cos(n*x) are guaranteed.
  206. """
  207. I = []
  208. y = Dummy('y')
  209. for fn, coeff in terms:
  210. for c, s, t, rel in (
  211. [cos, sin, tan, cos(x)**2 + sin(x)**2 - 1],
  212. [cosh, sinh, tanh, cosh(x)**2 - sinh(x)**2 - 1]):
  213. if coeff == 1 and fn in [c, s]:
  214. I.append(rel)
  215. elif fn == t:
  216. I.append(t(coeff*x)*c(coeff*x) - s(coeff*x))
  217. elif fn in [c, s]:
  218. cn = fn(coeff*y).expand(trig=True).subs(y, x)
  219. I.append(fn(coeff*x) - cn)
  220. return list(set(I))
  221. def analyse_gens(gens, hints):
  222. """
  223. Analyse the generators ``gens``, using the hints ``hints``.
  224. The meaning of ``hints`` is described in the main docstring.
  225. Return a new list of generators, and also the ideal we should
  226. work with.
  227. """
  228. # First parse the hints
  229. n, funcs, iterables, extragens = parse_hints(hints)
  230. debug('n=%s funcs: %s iterables: %s extragens: %s',
  231. (funcs, iterables, extragens))
  232. # We just add the extragens to gens and analyse them as before
  233. gens = list(gens)
  234. gens.extend(extragens)
  235. # remove duplicates
  236. funcs = list(set(funcs))
  237. iterables = list(set(iterables))
  238. gens = list(set(gens))
  239. # all the functions we can do anything with
  240. allfuncs = {sin, cos, tan, sinh, cosh, tanh}
  241. # sin(3*x) -> ((3, x), sin)
  242. trigterms = [(g.args[0].as_coeff_mul(), g.func) for g in gens
  243. if g.func in allfuncs]
  244. # Our list of new generators - start with anything that we cannot
  245. # work with (i.e. is not a trigonometric term)
  246. freegens = [g for g in gens if g.func not in allfuncs]
  247. newgens = []
  248. trigdict = {}
  249. for (coeff, var), fn in trigterms:
  250. trigdict.setdefault(var, []).append((coeff, fn))
  251. res = [] # the ideal
  252. for key, val in trigdict.items():
  253. # We have now assembeled a dictionary. Its keys are common
  254. # arguments in trigonometric expressions, and values are lists of
  255. # pairs (fn, coeff). x0, (fn, coeff) in trigdict means that we
  256. # need to deal with fn(coeff*x0). We take the rational gcd of the
  257. # coeffs, call it ``gcd``. We then use x = x0/gcd as "base symbol",
  258. # all other arguments are integral multiples thereof.
  259. # We will build an ideal which works with sin(x), cos(x).
  260. # If hint tan is provided, also work with tan(x). Moreover, if
  261. # n > 1, also work with sin(k*x) for k <= n, and similarly for cos
  262. # (and tan if the hint is provided). Finally, any generators which
  263. # the ideal does not work with but we need to accommodate (either
  264. # because it was in expr or because it was provided as a hint)
  265. # we also build into the ideal.
  266. # This selection process is expressed in the list ``terms``.
  267. # build_ideal then generates the actual relations in our ideal,
  268. # from this list.
  269. fns = [x[1] for x in val]
  270. val = [x[0] for x in val]
  271. gcd = reduce(igcd, val)
  272. terms = [(fn, v/gcd) for (fn, v) in zip(fns, val)]
  273. fs = set(funcs + fns)
  274. for c, s, t in ([cos, sin, tan], [cosh, sinh, tanh]):
  275. if any(x in fs for x in (c, s, t)):
  276. fs.add(c)
  277. fs.add(s)
  278. for fn in fs:
  279. for k in range(1, n + 1):
  280. terms.append((fn, k))
  281. extra = []
  282. for fn, v in terms:
  283. if fn == tan:
  284. extra.append((sin, v))
  285. extra.append((cos, v))
  286. if fn in [sin, cos] and tan in fs:
  287. extra.append((tan, v))
  288. if fn == tanh:
  289. extra.append((sinh, v))
  290. extra.append((cosh, v))
  291. if fn in [sinh, cosh] and tanh in fs:
  292. extra.append((tanh, v))
  293. terms.extend(extra)
  294. x = gcd*Mul(*key)
  295. r = build_ideal(x, terms)
  296. res.extend(r)
  297. newgens.extend({fn(v*x) for fn, v in terms})
  298. # Add generators for compound expressions from iterables
  299. for fn, args in iterables:
  300. if fn == tan:
  301. # Tan expressions are recovered from sin and cos.
  302. iterables.extend([(sin, args), (cos, args)])
  303. elif fn == tanh:
  304. # Tanh expressions are recovered from sihn and cosh.
  305. iterables.extend([(sinh, args), (cosh, args)])
  306. else:
  307. dummys = symbols('d:%i' % len(args), cls=Dummy)
  308. expr = fn( Add(*dummys)).expand(trig=True).subs(list(zip(dummys, args)))
  309. res.append(fn(Add(*args)) - expr)
  310. if myI in gens:
  311. res.append(myI**2 + 1)
  312. freegens.remove(myI)
  313. newgens.append(myI)
  314. return res, freegens, newgens
  315. myI = Dummy('I')
  316. expr = expr.subs(S.ImaginaryUnit, myI)
  317. subs = [(myI, S.ImaginaryUnit)]
  318. num, denom = cancel(expr).as_numer_denom()
  319. try:
  320. (pnum, pdenom), opt = parallel_poly_from_expr([num, denom])
  321. except PolificationFailed:
  322. return expr
  323. debug('initial gens:', opt.gens)
  324. ideal, freegens, gens = analyse_gens(opt.gens, hints)
  325. debug('ideal:', ideal)
  326. debug('new gens:', gens, " -- len", len(gens))
  327. debug('free gens:', freegens, " -- len", len(gens))
  328. # NOTE we force the domain to be ZZ to stop polys from injecting generators
  329. # (which is usually a sign of a bug in the way we build the ideal)
  330. if not gens:
  331. return expr
  332. G = groebner(ideal, order=order, gens=gens, domain=ZZ)
  333. debug('groebner basis:', list(G), " -- len", len(G))
  334. # If our fraction is a polynomial in the free generators, simplify all
  335. # coefficients separately:
  336. from sympy.simplify.ratsimp import ratsimpmodprime
  337. if freegens and pdenom.has_only_gens(*set(gens).intersection(pdenom.gens)):
  338. num = Poly(num, gens=gens+freegens).eject(*gens)
  339. res = []
  340. for monom, coeff in num.terms():
  341. ourgens = set(parallel_poly_from_expr([coeff, denom])[1].gens)
  342. # We compute the transitive closure of all generators that can
  343. # be reached from our generators through relations in the ideal.
  344. changed = True
  345. while changed:
  346. changed = False
  347. for p in ideal:
  348. p = Poly(p)
  349. if not ourgens.issuperset(p.gens) and \
  350. not p.has_only_gens(*set(p.gens).difference(ourgens)):
  351. changed = True
  352. ourgens.update(p.exclude().gens)
  353. # NOTE preserve order!
  354. realgens = [x for x in gens if x in ourgens]
  355. # The generators of the ideal have now been (implicitly) split
  356. # into two groups: those involving ourgens and those that don't.
  357. # Since we took the transitive closure above, these two groups
  358. # live in subgrings generated by a *disjoint* set of variables.
  359. # Any sensible groebner basis algorithm will preserve this disjoint
  360. # structure (i.e. the elements of the groebner basis can be split
  361. # similarly), and and the two subsets of the groebner basis then
  362. # form groebner bases by themselves. (For the smaller generating
  363. # sets, of course.)
  364. ourG = [g.as_expr() for g in G.polys if
  365. g.has_only_gens(*ourgens.intersection(g.gens))]
  366. res.append(Mul(*[a**b for a, b in zip(freegens, monom)]) * \
  367. ratsimpmodprime(coeff/denom, ourG, order=order,
  368. gens=realgens, quick=quick, domain=ZZ,
  369. polynomial=polynomial).subs(subs))
  370. return Add(*res)
  371. # NOTE The following is simpler and has less assumptions on the
  372. # groebner basis algorithm. If the above turns out to be broken,
  373. # use this.
  374. return Add(*[Mul(*[a**b for a, b in zip(freegens, monom)]) * \
  375. ratsimpmodprime(coeff/denom, list(G), order=order,
  376. gens=gens, quick=quick, domain=ZZ)
  377. for monom, coeff in num.terms()])
  378. else:
  379. return ratsimpmodprime(
  380. expr, list(G), order=order, gens=freegens+gens,
  381. quick=quick, domain=ZZ, polynomial=polynomial).subs(subs)
  382. _trigs = (TrigonometricFunction, HyperbolicFunction)
  383. def _trigsimp_inverse(rv):
  384. def check_args(x, y):
  385. try:
  386. return x.args[0] == y.args[0]
  387. except IndexError:
  388. return False
  389. def f(rv):
  390. # for simple functions
  391. g = getattr(rv, 'inverse', None)
  392. if (g is not None and isinstance(rv.args[0], g()) and
  393. isinstance(g()(1), TrigonometricFunction)):
  394. return rv.args[0].args[0]
  395. # for atan2 simplifications, harder because atan2 has 2 args
  396. if isinstance(rv, atan2):
  397. y, x = rv.args
  398. if _coeff_isneg(y):
  399. return -f(atan2(-y, x))
  400. elif _coeff_isneg(x):
  401. return S.Pi - f(atan2(y, -x))
  402. if check_args(x, y):
  403. if isinstance(y, sin) and isinstance(x, cos):
  404. return x.args[0]
  405. if isinstance(y, cos) and isinstance(x, sin):
  406. return S.Pi / 2 - x.args[0]
  407. return rv
  408. return bottom_up(rv, f)
  409. def trigsimp(expr, inverse=False, **opts):
  410. """Returns a reduced expression by using known trig identities.
  411. Parameters
  412. ==========
  413. inverse : bool, optional
  414. If ``inverse=True``, it will be assumed that a composition of inverse
  415. functions, such as sin and asin, can be cancelled in any order.
  416. For example, ``asin(sin(x))`` will yield ``x`` without checking whether
  417. x belongs to the set where this relation is true. The default is False.
  418. Default : True
  419. method : string, optional
  420. Specifies the method to use. Valid choices are:
  421. - ``'matching'``, default
  422. - ``'groebner'``
  423. - ``'combined'``
  424. - ``'fu'``
  425. - ``'old'``
  426. If ``'matching'``, simplify the expression recursively by targeting
  427. common patterns. If ``'groebner'``, apply an experimental groebner
  428. basis algorithm. In this case further options are forwarded to
  429. ``trigsimp_groebner``, please refer to
  430. its docstring. If ``'combined'``, it first runs the groebner basis
  431. algorithm with small default parameters, then runs the ``'matching'``
  432. algorithm. If ``'fu'``, run the collection of trigonometric
  433. transformations described by Fu, et al. (see the
  434. :py:func:`~sympy.simplify.fu.fu` docstring). If ``'old'``, the original
  435. SymPy trig simplification function is run.
  436. opts :
  437. Optional keyword arguments passed to the method. See each method's
  438. function docstring for details.
  439. Examples
  440. ========
  441. >>> from sympy import trigsimp, sin, cos, log
  442. >>> from sympy.abc import x
  443. >>> e = 2*sin(x)**2 + 2*cos(x)**2
  444. >>> trigsimp(e)
  445. 2
  446. Simplification occurs wherever trigonometric functions are located.
  447. >>> trigsimp(log(e))
  448. log(2)
  449. Using ``method='groebner'`` (or ``method='combined'``) might lead to
  450. greater simplification.
  451. The old trigsimp routine can be accessed as with method ``method='old'``.
  452. >>> from sympy import coth, tanh
  453. >>> t = 3*tanh(x)**7 - 2/coth(x)**7
  454. >>> trigsimp(t, method='old') == t
  455. True
  456. >>> trigsimp(t)
  457. tanh(x)**7
  458. """
  459. from sympy.simplify.fu import fu
  460. expr = sympify(expr)
  461. _eval_trigsimp = getattr(expr, '_eval_trigsimp', None)
  462. if _eval_trigsimp is not None:
  463. return _eval_trigsimp(**opts)
  464. old = opts.pop('old', False)
  465. if not old:
  466. opts.pop('deep', None)
  467. opts.pop('recursive', None)
  468. method = opts.pop('method', 'matching')
  469. else:
  470. method = 'old'
  471. def groebnersimp(ex, **opts):
  472. def traverse(e):
  473. if e.is_Atom:
  474. return e
  475. args = [traverse(x) for x in e.args]
  476. if e.is_Function or e.is_Pow:
  477. args = [trigsimp_groebner(x, **opts) for x in args]
  478. return e.func(*args)
  479. new = traverse(ex)
  480. if not isinstance(new, Expr):
  481. return new
  482. return trigsimp_groebner(new, **opts)
  483. trigsimpfunc = {
  484. 'fu': (lambda x: fu(x, **opts)),
  485. 'matching': (lambda x: futrig(x)),
  486. 'groebner': (lambda x: groebnersimp(x, **opts)),
  487. 'combined': (lambda x: futrig(groebnersimp(x,
  488. polynomial=True, hints=[2, tan]))),
  489. 'old': lambda x: trigsimp_old(x, **opts),
  490. }[method]
  491. expr_simplified = trigsimpfunc(expr)
  492. if inverse:
  493. expr_simplified = _trigsimp_inverse(expr_simplified)
  494. return expr_simplified
  495. def exptrigsimp(expr):
  496. """
  497. Simplifies exponential / trigonometric / hyperbolic functions.
  498. Examples
  499. ========
  500. >>> from sympy import exptrigsimp, exp, cosh, sinh
  501. >>> from sympy.abc import z
  502. >>> exptrigsimp(exp(z) + exp(-z))
  503. 2*cosh(z)
  504. >>> exptrigsimp(cosh(z) - sinh(z))
  505. exp(-z)
  506. """
  507. from sympy.simplify.fu import hyper_as_trig, TR2i
  508. def exp_trig(e):
  509. # select the better of e, and e rewritten in terms of exp or trig
  510. # functions
  511. choices = [e]
  512. if e.has(*_trigs):
  513. choices.append(e.rewrite(exp))
  514. choices.append(e.rewrite(cos))
  515. return min(*choices, key=count_ops)
  516. newexpr = bottom_up(expr, exp_trig)
  517. def f(rv):
  518. if not rv.is_Mul:
  519. return rv
  520. commutative_part, noncommutative_part = rv.args_cnc()
  521. # Since as_powers_dict loses order information,
  522. # if there is more than one noncommutative factor,
  523. # it should only be used to simplify the commutative part.
  524. if (len(noncommutative_part) > 1):
  525. return f(Mul(*commutative_part))*Mul(*noncommutative_part)
  526. rvd = rv.as_powers_dict()
  527. newd = rvd.copy()
  528. def signlog(expr, sign=S.One):
  529. if expr is S.Exp1:
  530. return sign, S.One
  531. elif isinstance(expr, exp) or (expr.is_Pow and expr.base == S.Exp1):
  532. return sign, expr.exp
  533. elif sign is S.One:
  534. return signlog(-expr, sign=-S.One)
  535. else:
  536. return None, None
  537. ee = rvd[S.Exp1]
  538. for k in rvd:
  539. if k.is_Add and len(k.args) == 2:
  540. # k == c*(1 + sign*E**x)
  541. c = k.args[0]
  542. sign, x = signlog(k.args[1]/c)
  543. if not x:
  544. continue
  545. m = rvd[k]
  546. newd[k] -= m
  547. if ee == -x*m/2:
  548. # sinh and cosh
  549. newd[S.Exp1] -= ee
  550. ee = 0
  551. if sign == 1:
  552. newd[2*c*cosh(x/2)] += m
  553. else:
  554. newd[-2*c*sinh(x/2)] += m
  555. elif newd[1 - sign*S.Exp1**x] == -m:
  556. # tanh
  557. del newd[1 - sign*S.Exp1**x]
  558. if sign == 1:
  559. newd[-c/tanh(x/2)] += m
  560. else:
  561. newd[-c*tanh(x/2)] += m
  562. else:
  563. newd[1 + sign*S.Exp1**x] += m
  564. newd[c] += m
  565. return Mul(*[k**newd[k] for k in newd])
  566. newexpr = bottom_up(newexpr, f)
  567. # sin/cos and sinh/cosh ratios to tan and tanh, respectively
  568. if newexpr.has(HyperbolicFunction):
  569. e, f = hyper_as_trig(newexpr)
  570. newexpr = f(TR2i(e))
  571. if newexpr.has(TrigonometricFunction):
  572. newexpr = TR2i(newexpr)
  573. # can we ever generate an I where there was none previously?
  574. if not (newexpr.has(I) and not expr.has(I)):
  575. expr = newexpr
  576. return expr
  577. #-------------------- the old trigsimp routines ---------------------
  578. def trigsimp_old(expr, *, first=True, **opts):
  579. """
  580. Reduces expression by using known trig identities.
  581. Notes
  582. =====
  583. deep:
  584. - Apply trigsimp inside all objects with arguments
  585. recursive:
  586. - Use common subexpression elimination (cse()) and apply
  587. trigsimp recursively (this is quite expensive if the
  588. expression is large)
  589. method:
  590. - Determine the method to use. Valid choices are 'matching' (default),
  591. 'groebner', 'combined', 'fu' and 'futrig'. If 'matching', simplify the
  592. expression recursively by pattern matching. If 'groebner', apply an
  593. experimental groebner basis algorithm. In this case further options
  594. are forwarded to ``trigsimp_groebner``, please refer to its docstring.
  595. If 'combined', first run the groebner basis algorithm with small
  596. default parameters, then run the 'matching' algorithm. 'fu' runs the
  597. collection of trigonometric transformations described by Fu, et al.
  598. (see the `fu` docstring) while `futrig` runs a subset of Fu-transforms
  599. that mimic the behavior of `trigsimp`.
  600. compare:
  601. - show input and output from `trigsimp` and `futrig` when different,
  602. but returns the `trigsimp` value.
  603. Examples
  604. ========
  605. >>> from sympy import trigsimp, sin, cos, log, cot
  606. >>> from sympy.abc import x
  607. >>> e = 2*sin(x)**2 + 2*cos(x)**2
  608. >>> trigsimp(e, old=True)
  609. 2
  610. >>> trigsimp(log(e), old=True)
  611. log(2*sin(x)**2 + 2*cos(x)**2)
  612. >>> trigsimp(log(e), deep=True, old=True)
  613. log(2)
  614. Using `method="groebner"` (or `"combined"`) can sometimes lead to a lot
  615. more simplification:
  616. >>> e = (-sin(x) + 1)/cos(x) + cos(x)/(-sin(x) + 1)
  617. >>> trigsimp(e, old=True)
  618. (1 - sin(x))/cos(x) + cos(x)/(1 - sin(x))
  619. >>> trigsimp(e, method="groebner", old=True)
  620. 2/cos(x)
  621. >>> trigsimp(1/cot(x)**2, compare=True, old=True)
  622. futrig: tan(x)**2
  623. cot(x)**(-2)
  624. """
  625. old = expr
  626. if first:
  627. if not expr.has(*_trigs):
  628. return expr
  629. trigsyms = set().union(*[t.free_symbols for t in expr.atoms(*_trigs)])
  630. if len(trigsyms) > 1:
  631. from sympy.simplify.simplify import separatevars
  632. d = separatevars(expr)
  633. if d.is_Mul:
  634. d = separatevars(d, dict=True) or d
  635. if isinstance(d, dict):
  636. expr = 1
  637. for k, v in d.items():
  638. # remove hollow factoring
  639. was = v
  640. v = expand_mul(v)
  641. opts['first'] = False
  642. vnew = trigsimp(v, **opts)
  643. if vnew == v:
  644. vnew = was
  645. expr *= vnew
  646. old = expr
  647. else:
  648. if d.is_Add:
  649. for s in trigsyms:
  650. r, e = expr.as_independent(s)
  651. if r:
  652. opts['first'] = False
  653. expr = r + trigsimp(e, **opts)
  654. if not expr.is_Add:
  655. break
  656. old = expr
  657. recursive = opts.pop('recursive', False)
  658. deep = opts.pop('deep', False)
  659. method = opts.pop('method', 'matching')
  660. def groebnersimp(ex, deep, **opts):
  661. def traverse(e):
  662. if e.is_Atom:
  663. return e
  664. args = [traverse(x) for x in e.args]
  665. if e.is_Function or e.is_Pow:
  666. args = [trigsimp_groebner(x, **opts) for x in args]
  667. return e.func(*args)
  668. if deep:
  669. ex = traverse(ex)
  670. return trigsimp_groebner(ex, **opts)
  671. trigsimpfunc = {
  672. 'matching': (lambda x, d: _trigsimp(x, d)),
  673. 'groebner': (lambda x, d: groebnersimp(x, d, **opts)),
  674. 'combined': (lambda x, d: _trigsimp(groebnersimp(x,
  675. d, polynomial=True, hints=[2, tan]),
  676. d))
  677. }[method]
  678. if recursive:
  679. w, g = cse(expr)
  680. g = trigsimpfunc(g[0], deep)
  681. for sub in reversed(w):
  682. g = g.subs(sub[0], sub[1])
  683. g = trigsimpfunc(g, deep)
  684. result = g
  685. else:
  686. result = trigsimpfunc(expr, deep)
  687. if opts.get('compare', False):
  688. f = futrig(old)
  689. if f != result:
  690. print('\tfutrig:', f)
  691. return result
  692. def _dotrig(a, b):
  693. """Helper to tell whether ``a`` and ``b`` have the same sorts
  694. of symbols in them -- no need to test hyperbolic patterns against
  695. expressions that have no hyperbolics in them."""
  696. return a.func == b.func and (
  697. a.has(TrigonometricFunction) and b.has(TrigonometricFunction) or
  698. a.has(HyperbolicFunction) and b.has(HyperbolicFunction))
  699. _trigpat = None
  700. def _trigpats():
  701. global _trigpat
  702. a, b, c = symbols('a b c', cls=Wild)
  703. d = Wild('d', commutative=False)
  704. # for the simplifications like sinh/cosh -> tanh:
  705. # DO NOT REORDER THE FIRST 14 since these are assumed to be in this
  706. # order in _match_div_rewrite.
  707. matchers_division = (
  708. (a*sin(b)**c/cos(b)**c, a*tan(b)**c, sin(b), cos(b)),
  709. (a*tan(b)**c*cos(b)**c, a*sin(b)**c, sin(b), cos(b)),
  710. (a*cot(b)**c*sin(b)**c, a*cos(b)**c, sin(b), cos(b)),
  711. (a*tan(b)**c/sin(b)**c, a/cos(b)**c, sin(b), cos(b)),
  712. (a*cot(b)**c/cos(b)**c, a/sin(b)**c, sin(b), cos(b)),
  713. (a*cot(b)**c*tan(b)**c, a, sin(b), cos(b)),
  714. (a*(cos(b) + 1)**c*(cos(b) - 1)**c,
  715. a*(-sin(b)**2)**c, cos(b) + 1, cos(b) - 1),
  716. (a*(sin(b) + 1)**c*(sin(b) - 1)**c,
  717. a*(-cos(b)**2)**c, sin(b) + 1, sin(b) - 1),
  718. (a*sinh(b)**c/cosh(b)**c, a*tanh(b)**c, S.One, S.One),
  719. (a*tanh(b)**c*cosh(b)**c, a*sinh(b)**c, S.One, S.One),
  720. (a*coth(b)**c*sinh(b)**c, a*cosh(b)**c, S.One, S.One),
  721. (a*tanh(b)**c/sinh(b)**c, a/cosh(b)**c, S.One, S.One),
  722. (a*coth(b)**c/cosh(b)**c, a/sinh(b)**c, S.One, S.One),
  723. (a*coth(b)**c*tanh(b)**c, a, S.One, S.One),
  724. (c*(tanh(a) + tanh(b))/(1 + tanh(a)*tanh(b)),
  725. tanh(a + b)*c, S.One, S.One),
  726. )
  727. matchers_add = (
  728. (c*sin(a)*cos(b) + c*cos(a)*sin(b) + d, sin(a + b)*c + d),
  729. (c*cos(a)*cos(b) - c*sin(a)*sin(b) + d, cos(a + b)*c + d),
  730. (c*sin(a)*cos(b) - c*cos(a)*sin(b) + d, sin(a - b)*c + d),
  731. (c*cos(a)*cos(b) + c*sin(a)*sin(b) + d, cos(a - b)*c + d),
  732. (c*sinh(a)*cosh(b) + c*sinh(b)*cosh(a) + d, sinh(a + b)*c + d),
  733. (c*cosh(a)*cosh(b) + c*sinh(a)*sinh(b) + d, cosh(a + b)*c + d),
  734. )
  735. # for cos(x)**2 + sin(x)**2 -> 1
  736. matchers_identity = (
  737. (a*sin(b)**2, a - a*cos(b)**2),
  738. (a*tan(b)**2, a*(1/cos(b))**2 - a),
  739. (a*cot(b)**2, a*(1/sin(b))**2 - a),
  740. (a*sin(b + c), a*(sin(b)*cos(c) + sin(c)*cos(b))),
  741. (a*cos(b + c), a*(cos(b)*cos(c) - sin(b)*sin(c))),
  742. (a*tan(b + c), a*((tan(b) + tan(c))/(1 - tan(b)*tan(c)))),
  743. (a*sinh(b)**2, a*cosh(b)**2 - a),
  744. (a*tanh(b)**2, a - a*(1/cosh(b))**2),
  745. (a*coth(b)**2, a + a*(1/sinh(b))**2),
  746. (a*sinh(b + c), a*(sinh(b)*cosh(c) + sinh(c)*cosh(b))),
  747. (a*cosh(b + c), a*(cosh(b)*cosh(c) + sinh(b)*sinh(c))),
  748. (a*tanh(b + c), a*((tanh(b) + tanh(c))/(1 + tanh(b)*tanh(c)))),
  749. )
  750. # Reduce any lingering artifacts, such as sin(x)**2 changing
  751. # to 1-cos(x)**2 when sin(x)**2 was "simpler"
  752. artifacts = (
  753. (a - a*cos(b)**2 + c, a*sin(b)**2 + c, cos),
  754. (a - a*(1/cos(b))**2 + c, -a*tan(b)**2 + c, cos),
  755. (a - a*(1/sin(b))**2 + c, -a*cot(b)**2 + c, sin),
  756. (a - a*cosh(b)**2 + c, -a*sinh(b)**2 + c, cosh),
  757. (a - a*(1/cosh(b))**2 + c, a*tanh(b)**2 + c, cosh),
  758. (a + a*(1/sinh(b))**2 + c, a*coth(b)**2 + c, sinh),
  759. # same as above but with noncommutative prefactor
  760. (a*d - a*d*cos(b)**2 + c, a*d*sin(b)**2 + c, cos),
  761. (a*d - a*d*(1/cos(b))**2 + c, -a*d*tan(b)**2 + c, cos),
  762. (a*d - a*d*(1/sin(b))**2 + c, -a*d*cot(b)**2 + c, sin),
  763. (a*d - a*d*cosh(b)**2 + c, -a*d*sinh(b)**2 + c, cosh),
  764. (a*d - a*d*(1/cosh(b))**2 + c, a*d*tanh(b)**2 + c, cosh),
  765. (a*d + a*d*(1/sinh(b))**2 + c, a*d*coth(b)**2 + c, sinh),
  766. )
  767. _trigpat = (a, b, c, d, matchers_division, matchers_add,
  768. matchers_identity, artifacts)
  769. return _trigpat
  770. def _replace_mul_fpowxgpow(expr, f, g, rexp, h, rexph):
  771. """Helper for _match_div_rewrite.
  772. Replace f(b_)**c_*g(b_)**(rexp(c_)) with h(b)**rexph(c) if f(b_)
  773. and g(b_) are both positive or if c_ is an integer.
  774. """
  775. # assert expr.is_Mul and expr.is_commutative and f != g
  776. fargs = defaultdict(int)
  777. gargs = defaultdict(int)
  778. args = []
  779. for x in expr.args:
  780. if x.is_Pow or x.func in (f, g):
  781. b, e = x.as_base_exp()
  782. if b.is_positive or e.is_integer:
  783. if b.func == f:
  784. fargs[b.args[0]] += e
  785. continue
  786. elif b.func == g:
  787. gargs[b.args[0]] += e
  788. continue
  789. args.append(x)
  790. common = set(fargs) & set(gargs)
  791. hit = False
  792. while common:
  793. key = common.pop()
  794. fe = fargs.pop(key)
  795. ge = gargs.pop(key)
  796. if fe == rexp(ge):
  797. args.append(h(key)**rexph(fe))
  798. hit = True
  799. else:
  800. fargs[key] = fe
  801. gargs[key] = ge
  802. if not hit:
  803. return expr
  804. while fargs:
  805. key, e = fargs.popitem()
  806. args.append(f(key)**e)
  807. while gargs:
  808. key, e = gargs.popitem()
  809. args.append(g(key)**e)
  810. return Mul(*args)
  811. _idn = lambda x: x
  812. _midn = lambda x: -x
  813. _one = lambda x: S.One
  814. def _match_div_rewrite(expr, i):
  815. """helper for __trigsimp"""
  816. if i == 0:
  817. expr = _replace_mul_fpowxgpow(expr, sin, cos,
  818. _midn, tan, _idn)
  819. elif i == 1:
  820. expr = _replace_mul_fpowxgpow(expr, tan, cos,
  821. _idn, sin, _idn)
  822. elif i == 2:
  823. expr = _replace_mul_fpowxgpow(expr, cot, sin,
  824. _idn, cos, _idn)
  825. elif i == 3:
  826. expr = _replace_mul_fpowxgpow(expr, tan, sin,
  827. _midn, cos, _midn)
  828. elif i == 4:
  829. expr = _replace_mul_fpowxgpow(expr, cot, cos,
  830. _midn, sin, _midn)
  831. elif i == 5:
  832. expr = _replace_mul_fpowxgpow(expr, cot, tan,
  833. _idn, _one, _idn)
  834. # i in (6, 7) is skipped
  835. elif i == 8:
  836. expr = _replace_mul_fpowxgpow(expr, sinh, cosh,
  837. _midn, tanh, _idn)
  838. elif i == 9:
  839. expr = _replace_mul_fpowxgpow(expr, tanh, cosh,
  840. _idn, sinh, _idn)
  841. elif i == 10:
  842. expr = _replace_mul_fpowxgpow(expr, coth, sinh,
  843. _idn, cosh, _idn)
  844. elif i == 11:
  845. expr = _replace_mul_fpowxgpow(expr, tanh, sinh,
  846. _midn, cosh, _midn)
  847. elif i == 12:
  848. expr = _replace_mul_fpowxgpow(expr, coth, cosh,
  849. _midn, sinh, _midn)
  850. elif i == 13:
  851. expr = _replace_mul_fpowxgpow(expr, coth, tanh,
  852. _idn, _one, _idn)
  853. else:
  854. return None
  855. return expr
  856. def _trigsimp(expr, deep=False):
  857. # protect the cache from non-trig patterns; we only allow
  858. # trig patterns to enter the cache
  859. if expr.has(*_trigs):
  860. return __trigsimp(expr, deep)
  861. return expr
  862. @cacheit
  863. def __trigsimp(expr, deep=False):
  864. """recursive helper for trigsimp"""
  865. from sympy.simplify.fu import TR10i
  866. if _trigpat is None:
  867. _trigpats()
  868. a, b, c, d, matchers_division, matchers_add, \
  869. matchers_identity, artifacts = _trigpat
  870. if expr.is_Mul:
  871. # do some simplifications like sin/cos -> tan:
  872. if not expr.is_commutative:
  873. com, nc = expr.args_cnc()
  874. expr = _trigsimp(Mul._from_args(com), deep)*Mul._from_args(nc)
  875. else:
  876. for i, (pattern, simp, ok1, ok2) in enumerate(matchers_division):
  877. if not _dotrig(expr, pattern):
  878. continue
  879. newexpr = _match_div_rewrite(expr, i)
  880. if newexpr is not None:
  881. if newexpr != expr:
  882. expr = newexpr
  883. break
  884. else:
  885. continue
  886. # use SymPy matching instead
  887. res = expr.match(pattern)
  888. if res and res.get(c, 0):
  889. if not res[c].is_integer:
  890. ok = ok1.subs(res)
  891. if not ok.is_positive:
  892. continue
  893. ok = ok2.subs(res)
  894. if not ok.is_positive:
  895. continue
  896. # if "a" contains any of trig or hyperbolic funcs with
  897. # argument "b" then skip the simplification
  898. if any(w.args[0] == res[b] for w in res[a].atoms(
  899. TrigonometricFunction, HyperbolicFunction)):
  900. continue
  901. # simplify and finish:
  902. expr = simp.subs(res)
  903. break # process below
  904. if expr.is_Add:
  905. args = []
  906. for term in expr.args:
  907. if not term.is_commutative:
  908. com, nc = term.args_cnc()
  909. nc = Mul._from_args(nc)
  910. term = Mul._from_args(com)
  911. else:
  912. nc = S.One
  913. term = _trigsimp(term, deep)
  914. for pattern, result in matchers_identity:
  915. res = term.match(pattern)
  916. if res is not None:
  917. term = result.subs(res)
  918. break
  919. args.append(term*nc)
  920. if args != expr.args:
  921. expr = Add(*args)
  922. expr = min(expr, expand(expr), key=count_ops)
  923. if expr.is_Add:
  924. for pattern, result in matchers_add:
  925. if not _dotrig(expr, pattern):
  926. continue
  927. expr = TR10i(expr)
  928. if expr.has(HyperbolicFunction):
  929. res = expr.match(pattern)
  930. # if "d" contains any trig or hyperbolic funcs with
  931. # argument "a" or "b" then skip the simplification;
  932. # this isn't perfect -- see tests
  933. if res is None or not (a in res and b in res) or any(
  934. w.args[0] in (res[a], res[b]) for w in res[d].atoms(
  935. TrigonometricFunction, HyperbolicFunction)):
  936. continue
  937. expr = result.subs(res)
  938. break
  939. # Reduce any lingering artifacts, such as sin(x)**2 changing
  940. # to 1 - cos(x)**2 when sin(x)**2 was "simpler"
  941. for pattern, result, ex in artifacts:
  942. if not _dotrig(expr, pattern):
  943. continue
  944. # Substitute a new wild that excludes some function(s)
  945. # to help influence a better match. This is because
  946. # sometimes, for example, 'a' would match sec(x)**2
  947. a_t = Wild('a', exclude=[ex])
  948. pattern = pattern.subs(a, a_t)
  949. result = result.subs(a, a_t)
  950. m = expr.match(pattern)
  951. was = None
  952. while m and was != expr:
  953. was = expr
  954. if m[a_t] == 0 or \
  955. -m[a_t] in m[c].args or m[a_t] + m[c] == 0:
  956. break
  957. if d in m and m[a_t]*m[d] + m[c] == 0:
  958. break
  959. expr = result.subs(m)
  960. m = expr.match(pattern)
  961. m.setdefault(c, S.Zero)
  962. elif expr.is_Mul or expr.is_Pow or deep and expr.args:
  963. expr = expr.func(*[_trigsimp(a, deep) for a in expr.args])
  964. try:
  965. if not expr.has(*_trigs):
  966. raise TypeError
  967. e = expr.atoms(exp)
  968. new = expr.rewrite(exp, deep=deep)
  969. if new == e:
  970. raise TypeError
  971. fnew = factor(new)
  972. if fnew != new:
  973. new = sorted([new, factor(new)], key=count_ops)[0]
  974. # if all exp that were introduced disappeared then accept it
  975. if not (new.atoms(exp) - e):
  976. expr = new
  977. except TypeError:
  978. pass
  979. return expr
  980. #------------------- end of old trigsimp routines --------------------
  981. def futrig(e, *, hyper=True, **kwargs):
  982. """Return simplified ``e`` using Fu-like transformations.
  983. This is not the "Fu" algorithm. This is called by default
  984. from ``trigsimp``. By default, hyperbolics subexpressions
  985. will be simplified, but this can be disabled by setting
  986. ``hyper=False``.
  987. Examples
  988. ========
  989. >>> from sympy import trigsimp, tan, sinh, tanh
  990. >>> from sympy.simplify.trigsimp import futrig
  991. >>> from sympy.abc import x
  992. >>> trigsimp(1/tan(x)**2)
  993. tan(x)**(-2)
  994. >>> futrig(sinh(x)/tanh(x))
  995. cosh(x)
  996. """
  997. from sympy.simplify.fu import hyper_as_trig
  998. e = sympify(e)
  999. if not isinstance(e, Basic):
  1000. return e
  1001. if not e.args:
  1002. return e
  1003. old = e
  1004. e = bottom_up(e, _futrig)
  1005. if hyper and e.has(HyperbolicFunction):
  1006. e, f = hyper_as_trig(e)
  1007. e = f(bottom_up(e, _futrig))
  1008. if e != old and e.is_Mul and e.args[0].is_Rational:
  1009. # redistribute leading coeff on 2-arg Add
  1010. e = Mul(*e.as_coeff_Mul())
  1011. return e
  1012. def _futrig(e):
  1013. """Helper for futrig."""
  1014. from sympy.simplify.fu import (
  1015. TR1, TR2, TR3, TR2i, TR10, L, TR10i,
  1016. TR8, TR6, TR15, TR16, TR111, TR5, TRmorrie, TR11, _TR11, TR14, TR22,
  1017. TR12)
  1018. if not e.has(TrigonometricFunction):
  1019. return e
  1020. if e.is_Mul:
  1021. coeff, e = e.as_independent(TrigonometricFunction)
  1022. else:
  1023. coeff = None
  1024. Lops = lambda x: (L(x), x.count_ops(), _nodes(x), len(x.args), x.is_Add)
  1025. trigs = lambda x: x.has(TrigonometricFunction)
  1026. tree = [identity,
  1027. (
  1028. TR3, # canonical angles
  1029. TR1, # sec-csc -> cos-sin
  1030. TR12, # expand tan of sum
  1031. lambda x: _eapply(factor, x, trigs),
  1032. TR2, # tan-cot -> sin-cos
  1033. [identity, lambda x: _eapply(_mexpand, x, trigs)],
  1034. TR2i, # sin-cos ratio -> tan
  1035. lambda x: _eapply(lambda i: factor(i.normal()), x, trigs),
  1036. TR14, # factored identities
  1037. TR5, # sin-pow -> cos_pow
  1038. TR10, # sin-cos of sums -> sin-cos prod
  1039. TR11, _TR11, TR6, # reduce double angles and rewrite cos pows
  1040. lambda x: _eapply(factor, x, trigs),
  1041. TR14, # factored powers of identities
  1042. [identity, lambda x: _eapply(_mexpand, x, trigs)],
  1043. TR10i, # sin-cos products > sin-cos of sums
  1044. TRmorrie,
  1045. [identity, TR8], # sin-cos products -> sin-cos of sums
  1046. [identity, lambda x: TR2i(TR2(x))], # tan -> sin-cos -> tan
  1047. [
  1048. lambda x: _eapply(expand_mul, TR5(x), trigs),
  1049. lambda x: _eapply(
  1050. expand_mul, TR15(x), trigs)], # pos/neg powers of sin
  1051. [
  1052. lambda x: _eapply(expand_mul, TR6(x), trigs),
  1053. lambda x: _eapply(
  1054. expand_mul, TR16(x), trigs)], # pos/neg powers of cos
  1055. TR111, # tan, sin, cos to neg power -> cot, csc, sec
  1056. [identity, TR2i], # sin-cos ratio to tan
  1057. [identity, lambda x: _eapply(
  1058. expand_mul, TR22(x), trigs)], # tan-cot to sec-csc
  1059. TR1, TR2, TR2i,
  1060. [identity, lambda x: _eapply(
  1061. factor_terms, TR12(x), trigs)], # expand tan of sum
  1062. )]
  1063. e = greedy(tree, objective=Lops)(e)
  1064. if coeff is not None:
  1065. e = coeff * e
  1066. return e
  1067. def _is_Expr(e):
  1068. """_eapply helper to tell whether ``e`` and all its args
  1069. are Exprs."""
  1070. if isinstance(e, Derivative):
  1071. return _is_Expr(e.expr)
  1072. if not isinstance(e, Expr):
  1073. return False
  1074. return all(_is_Expr(i) for i in e.args)
  1075. def _eapply(func, e, cond=None):
  1076. """Apply ``func`` to ``e`` if all args are Exprs else only
  1077. apply it to those args that *are* Exprs."""
  1078. if not isinstance(e, Expr):
  1079. return e
  1080. if _is_Expr(e) or not e.args:
  1081. return func(e)
  1082. return e.func(*[
  1083. _eapply(func, ei) if (cond is None or cond(ei)) else ei
  1084. for ei in e.args])