trigsimp.py 44 KB

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