Spaces:
Sleeping
Sleeping
| =========================================== | |
| Hongguang Fu's Trigonometric Simplification | |
| =========================================== | |
| .. automodule:: sympy.simplify.fu | |
| .. currentmodule:: sympy.simplify.fu | |
| Implementation of the trigsimp algorithm by Fu et al. | |
| The idea behind the Fu algorithm is to use a sequence of rules | |
| that students learn during their pre-calculus courses. | |
| The rules are applied heuristically and it uses a greedy algorithm to | |
| apply multiple rules simultaneously and choose the result with the least | |
| leaf counts. | |
| There are transform rules in which a single rule is applied to the | |
| expression tree. The following are just mnemonic in nature; see the | |
| docstrings for examples. | |
| - :func:`TR0` - simplify expression | |
| - :func:`TR1` - sec-csc to cos-sin | |
| - :func:`TR2` - tan-cot to sin-cos ratio | |
| - :func:`TR2i` - sin-cos ratio to tan | |
| - :func:`TR3` - angle canonicalization | |
| - :func:`TR4` - functions at special angles | |
| - :func:`TR5` - powers of sin to powers of cos | |
| - :func:`TR6` - powers of cos to powers of sin | |
| - :func:`TR7` - reduce cos power (increase angle) | |
| - :func:`TR8` - expand products of sin-cos to sums | |
| - :func:`TR9` - contract sums of sin-cos to products | |
| - :func:`TR10` - separate sin-cos arguments | |
| - :func:`TR10i` - collect sin-cos arguments | |
| - :func:`TR11` - reduce double angles | |
| - :func:`TR12` - separate tan arguments | |
| - :func:`TR12i` - collect tan arguments | |
| - :func:`TR13` - expand product of tan-cot | |
| - :func:`TRmorrie` - prod(cos(x*2**i), (i, 0, k - 1)) -> sin(2**k*x)/(2**k*sin(x)) | |
| - :func:`TR14` - factored powers of sin or cos to cos or sin power | |
| - :func:`TR15` - negative powers of sin to cot power | |
| - :func:`TR16` - negative powers of cos to tan power | |
| - :func:`TR22` - tan-cot powers to negative powers of sec-csc functions | |
| - :func:`TR111` - negative sin-cos-tan powers to csc-sec-cot | |
| There are 4 combination transforms (CTR1 - CTR4) in which a sequence of | |
| transformations are applied and the simplest expression is selected from | |
| a few options. | |
| Finally, there are the 2 rule lists (RL1 and RL2), which apply a | |
| sequence of transformations and combined transformations, and the ``fu`` | |
| algorithm itself, which applies rules and rule lists and selects the | |
| best expressions. There is also a function ``L`` which counts the number | |
| of trigonometric functions that appear in the expression. | |
| Other than TR0, re-writing of expressions is not done by the transformations. | |
| e.g. TR10i finds pairs of terms in a sum that are in the form like | |
| ``cos(x)*cos(y) + sin(x)*sin(y)``. Such expression are targeted in a bottom-up | |
| traversal of the expression, but no manipulation to make them appear is | |
| attempted. For example, | |
| Set-up for examples below: | |
| >>> from sympy.simplify.fu import fu, L, TR9, TR10i, TR11 | |
| >>> from sympy import factor, sin, cos, powsimp | |
| >>> from sympy.abc import x, y, z, a | |
| >>> from time import time | |
| >>> eq = cos(x + y)/cos(x) | |
| >>> TR10i(eq.expand(trig=True)) | |
| -sin(x)*sin(y)/cos(x) + cos(y) | |
| If the expression is put in "normal" form (with a common denominator) then | |
| the transformation is successful: | |
| >>> TR10i(_.normal()) | |
| cos(x + y)/cos(x) | |
| TR11's behavior is similar. It rewrites double angles as smaller angles but | |
| doesn't do any simplification of the result. | |
| >>> TR11(sin(2)**a*cos(1)**(-a), 1) | |
| (2*sin(1)*cos(1))**a/cos(1)**a | |
| >>> powsimp(_) | |
| (2*sin(1))**a | |
| The temptation is to try make these TR rules "smarter" but that should really | |
| be done at a higher level; the TR rules should try maintain the "do one thing | |
| well" principle. There is one exception, however. In TR10i and TR9 terms are | |
| recognized even when they are each multiplied by a common factor: | |
| >>> fu(a*cos(x)*cos(y) + a*sin(x)*sin(y)) | |
| a*cos(x - y) | |
| Factoring with ``factor_terms`` is used but it is "JIT"-like, being delayed | |
| until it is deemed necessary. Furthermore, if the factoring does not | |
| help with the simplification, it is not retained, so | |
| ``a*cos(x)*cos(y) + a*sin(x)*sin(z)`` does not become a factored | |
| (but unsimplified in the trigonometric sense) expression: | |
| >>> fu(a*cos(x)*cos(y) + a*sin(x)*sin(z)) | |
| a*sin(x)*sin(z) + a*cos(x)*cos(y) | |
| In some cases factoring might be a good idea, but the user is left | |
| to make that decision. For example: | |
| >>> expr=((15*sin(2*x) + 19*sin(x + y) + 17*sin(x + z) + 19*cos(x - z) + | |
| ... 25)*(20*sin(2*x) + 15*sin(x + y) + sin(y + z) + 14*cos(x - z) + | |
| ... 14*cos(y - z))*(9*sin(2*y) + 12*sin(y + z) + 10*cos(x - y) + 2*cos(y - | |
| ... z) + 18)).expand(trig=True).expand() | |
| In the expanded state, there are nearly 1000 trig functions: | |
| >>> L(expr) | |
| 932 | |
| If the expression were factored first, this would take time but the | |
| resulting expression would be transformed very quickly: | |
| >>> def clock(f, n=2): | |
| ... t=time(); f(); return round(time()-t, n) | |
| ... | |
| >>> clock(lambda: factor(expr)) # doctest: +SKIP | |
| 0.86 | |
| >>> clock(lambda: TR10i(expr), 3) # doctest: +SKIP | |
| 0.016 | |
| If the unexpanded expression is used, the transformation takes longer but | |
| not as long as it took to factor it and then transform it: | |
| >>> clock(lambda: TR10i(expr), 2) # doctest: +SKIP | |
| 0.28 | |
| So neither expansion nor factoring is used in ``TR10i``: if the | |
| expression is already factored (or partially factored) then expansion | |
| with ``trig=True`` would destroy what is already known and take | |
| longer; if the expression is expanded, factoring may take longer than | |
| simply applying the transformation itself. | |
| Although the algorithms should be canonical, always giving the same | |
| result, they may not yield the best result. This, in general, is | |
| the nature of simplification where searching all possible transformation | |
| paths is very expensive. Here is a simple example. There are 6 terms | |
| in the following sum: | |
| >>> expr = (sin(x)**2*cos(y)*cos(z) + sin(x)*sin(y)*cos(x)*cos(z) + | |
| ... sin(x)*sin(z)*cos(x)*cos(y) + sin(y)*sin(z)*cos(x)**2 + sin(y)*sin(z) + | |
| ... cos(y)*cos(z)) | |
| >>> args = expr.args | |
| Serendipitously, fu gives the best result: | |
| >>> fu(expr) | |
| 3*cos(y - z)/2 - cos(2*x + y + z)/2 | |
| But if different terms were combined, a less-optimal result might be | |
| obtained, requiring some additional work to get better simplification, | |
| but still less than optimal. The following shows an alternative form | |
| of ``expr`` that resists optimal simplification once a given step | |
| is taken since it leads to a dead end: | |
| >>> TR9(-cos(x)**2*cos(y + z) + 3*cos(y - z)/2 + | |
| ... cos(y + z)/2 + cos(-2*x + y + z)/4 - cos(2*x + y + z)/4) | |
| sin(2*x)*sin(y + z)/2 - cos(x)**2*cos(y + z) + 3*cos(y - z)/2 + cos(y + z)/2 | |
| Here is a smaller expression that exhibits the same behavior: | |
| >>> a = sin(x)*sin(z)*cos(x)*cos(y) + sin(x)*sin(y)*cos(x)*cos(z) | |
| >>> TR10i(a) | |
| sin(x)*sin(y + z)*cos(x) | |
| >>> newa = _ | |
| >>> TR10i(expr - a) # this combines two more of the remaining terms | |
| sin(x)**2*cos(y)*cos(z) + sin(y)*sin(z)*cos(x)**2 + cos(y - z) | |
| >>> TR10i(_ + newa) == _ + newa # but now there is no more simplification | |
| True | |
| Without getting lucky or trying all possible pairings of arguments, the | |
| final result may be less than optimal and impossible to find without | |
| better heuristics or brute force trial of all possibilities. | |
| Rules | |
| ===== | |
| .. autofunction:: TR0 | |
| .. autofunction:: TR1 | |
| .. autofunction:: TR2 | |
| .. autofunction:: TR2i | |
| .. autofunction:: TR3 | |
| .. autofunction:: TR4 | |
| .. autofunction:: TR5 | |
| .. autofunction:: TR6 | |
| .. autofunction:: TR7 | |
| .. autofunction:: TR8 | |
| .. autofunction:: TR9 | |
| .. autofunction:: TR10 | |
| .. autofunction:: TR10i | |
| .. autofunction:: TR11 | |
| .. autofunction:: TR12 | |
| .. autofunction:: TR12i | |
| .. autofunction:: TR13 | |
| .. autofunction:: TRmorrie | |
| .. autofunction:: TR14 | |
| .. autofunction:: TR15 | |
| .. autofunction:: TR16 | |
| .. autofunction:: TR111 | |
| .. autofunction:: TR22 | |
| .. autofunction:: TRpower | |
| .. autofunction:: fu | |
| Notes | |
| ===== | |
| This work was started by Dimitar Vlahovski at the Technological School | |
| "Electronic systems" (30.11.2011). | |
| Beyond TR13, other rules are not from the original paper, but extended | |
| in SymPy. | |
| References | |
| ========== | |
| .. [1] Fu, Hongguang, Xiuqin Zhong, and Zhenbing Zeng. | |
| "Automated and readable simplification of trigonometric expressions." | |
| Mathematical and computer modelling 44.11 (2006): 1169-1177. | |
| https://citeseerx.ist.psu.edu/document?repid=rep1&type=pdf&doi=718d67a8d1ce0a23808c1cc265612f81977357e4 | |
| .. [2] A formula sheet for trigonometric functions. | |
| http://www.sosmath.com/trig/Trig5/trig5/pdf/pdf.html | |