Calculating Sines with Taylor Polynomials

In this post I will write a Python function to calculate sines using Taylor polynomials. It is impossible to calculate these directly but they can be approximated to any accuracy using this method, and I will show how a Taylor series converges to ever-increasing accuracy.

Taylor Polynomials

I recently wrote a post on linear regression, the process of creating a formula in the format y = ax + b to describe the line of best fit for a set of data. Such a formula allows the value of y to be calculated for any given value of x using simple arithmetic. Unfortunately such formulae do not exist for certain functions such as sine and cosine, and therefore these need to be calculated using techniques such as Taylor polynomials. You might like to read the Wikipedia article, noting particularly their use for calculating sines.

The formula for sine looks slightly intimidating in the forms presented by Wikipedia

sine Taylor polynomial

but in the second form we can see that it consists of a simple repeating term.

sine Taylor polynomial

There are four points to note about this formula:

  • We start off with the angle x (in radians) we want to calculate the sine of
  • We then alternately subtract or add the repeating term
  • The powers and factorials start at 3 and are incremented by 2 on each iteration
  • The more terms we calculate the more accuracy we get

The Project

This project will consist of a Python module implementing a sine_of_radians function. When writing a function which returns or takes as an argument a value which could be expressed in different units I adopt the convention of including the unit in the function or argument names. In this case the angles can be expressed in degrees or radians so I have included the word radians in the function name. (Python's own math.sine function also takes radians - I wonder how many thousands of people over the years have been baffled by getting the wrong answer when they give it an angle in degrees.)

The code uses the Memoization of Factorials project I posted recently to avoid repeatedly calculating factorials. If you are calculating a large number of trigonometric functions, for example when plotting a sine wave, the repetitive overhead of calculating factorials can be significant so memoizing (storing pre-calculated values) can increase efficiency.

Create a folder somewhere convenient and within it create the following empty files. You can also download the source code from the Downloads page or clone/download from Github if you prefer.

  • factorialmemoization.py
  • taylorseries.py
  • main.py

I won't repeat the factorialmemoization.py code here but it is included with the download and of course in the post linked to above.

Open taylorseries.py and enter/paste the following.

taylorseries.py

import factorialmemoization

def sine_of_radians(radians, degree):

    """
    Calculate sine of given angle (in radians) using Taylor Polynomial to given degree.

    The degree argument refers to the number of terms of the polynomial
    calculated and has nothing to do with the degree as a unit of angle.
    """

    if degree < 3 or degree > 63:

        raise ValueError("degree must be between 3 and 63")

    multiplier = -1.0
    sine = radians

    fm = factorialmemoization.FactorialMemoization(degree)

    for currentdegree in range(3, (degree + 1), 2):

         sine += ( (radians ** currentdegree) / fm.get(currentdegree) * multiplier )

         multiplier *= -1

    return sine

The sine_of_radians function takes as arguments the angle in radians we wish to calculate the sine of, and the degree to which we wish to calculate the Taylor series. The function then returns the sine.

The maximum number in the 3,5,7... sequence of the Taylor series formula shown above is known - rather confusingly - as the degree of the polynomial. It has nothing to do with the meaning of the word degree as a unit of angle. The minimum is 3 and I have arbritrarily used 63 as a maximum; if the degree argument is outside this range an exception is raised.

As I mentioned above calculating a Taylor series involves alternating between subtracting and adding the next term. To cut down on fiddly code we'll always ADD the next term but first multiply it by a variable named multiplier which will flip between -1 and 1. It is initialized to -1 but is multiplied by -1 on each iteration. The sine variable is initialised to the angle before being used in the loop.

The degree to which we want to calculate the Taylor series corresponds to the maximum factorial which we need, so this value is used to construct a FactorialMemoization object to pre-calculate the required factorials.

The loop iterates from 3 to the specified degree, incrementing by 2 each time. The current term is then evaluated, using currentdegree as an exponent and to get the pre-calculated factorial. The multiplier sign is then "flipped", and when the loop exits we return the sine.

That's the code finished so now we can use it. In main.py paste the following code.

main.py

import taylorseries
import math

def main():

    """
    Test the taylorseries module by printing sines of a range of angles using
    math.sin and then taylorseries.sine_of_radians with various degrees of the polynomial.
    """

    print("----------------------\n| code-in-python.com |\n| Taylor Polynomials |\n----------------------\n")

    print("-----------------------------------------------------------------------------------------");
    print("| Degrees |   math.sin |   Taylor 3 |   Taylor 5 |   Taylor 7 |   Taylor 9 |  Taylor 11 |");
    print("-----------------------------------------------------------------------------------------");

    for degrees in range(0, 361, 30):

         print("| {:7.0f} | {:10.6f} | {:10.6f} | {:10.6f} | {:10.6f} | {:10.6f} | {:10.6f} |".format(
         degrees,
         math.sin(math.radians(degrees)),
         taylorseries.sine_of_radians(math.radians(degrees), 3),
         taylorseries.sine_of_radians(math.radians(degrees), 5),
         taylorseries.sine_of_radians(math.radians(degrees), 7),
         taylorseries.sine_of_radians(math.radians(degrees), 9),
         taylorseries.sine_of_radians(math.radians(degrees), 11)))

    print("-----------------------------------------------------------------------------------------");

#-----------------------------------------------------

main()

Firstly we print out headings for the table columns; the table will show degrees, the sine of the degrees calculated using Python's math.sine function, and finally the sines calculated to various degrees with our Taylor Polynomial function. Then we enter a loop, printing a row for each angle with the values for each column.

We can now run the code using this command in terminal.

Running the Program

python3.6 main.py

Program Output

----------------------
| code-in-python.com |
| Taylor Polynomials |
----------------------

-----------------------------------------------------------------------------------------
| Degrees |   math.sin |   Taylor 3 |   Taylor 5 |   Taylor 7 |   Taylor 9 |  Taylor 11 |
-----------------------------------------------------------------------------------------
|       0 |   0.000000 |   0.000000 |   0.000000 |   0.000000 |   0.000000 |   0.000000 |
|      30 |   0.500000 |   0.499674 |   0.500002 |   0.500000 |   0.500000 |   0.500000 |
|      60 |   0.866025 |   0.855801 |   0.866295 |   0.866021 |   0.866025 |   0.866025 |
|      90 |   1.000000 |   0.924832 |   1.004525 |   0.999843 |   1.000004 |   1.000000 |
|     120 |   0.866025 |   0.563221 |   0.899045 |   0.863971 |   0.866108 |   0.866023 |
|     150 |   0.500000 |  -0.372581 |   0.652273 |   0.485029 |   0.500950 |   0.499958 |
|     180 |   0.000000 |  -2.026120 |   0.524044 |  -0.075221 |   0.006925 |  -0.000445 |
|     210 |  -0.500000 |  -4.540945 |   0.970964 |  -0.792011 |  -0.463078 |  -0.503248 |
|     240 |  -0.866025 |  -8.060603 |   2.685767 |  -1.803648 |  -0.709604 |  -0.884114 |
|     270 |  -1.000000 | -12.728642 |   6.636667 |  -3.602330 |  -0.444366 |  -1.081890 |
|     300 |  -0.866025 | -18.688608 |  14.106711 |  -7.300487 |   0.850770 |  -1.180788 |
|     330 |  -0.500000 | -26.084051 |  26.733139 | -14.983433 |   4.236804 |  -1.559467 |
|     360 |  -0.000000 | -35.058517 |  46.546732 | -30.159127 |  11.899567 |  -3.195076 |
-----------------------------------------------------------------------------------------

As you can see, for smaller angles the sines are reasonably accurate compared to Python's math.sin function values, even with smaller degrees of the Taylor polynomial, and completely accurate to 6 decimal places up to 90 degrees at degree 11.

The values then become increasingly inaccurate. For 360 degrees the Taylor value is about -35 at degree 3 when it should be 0, and even at degree 11 is about -3.

Due to the symmetrical and cyclical nature of the sine function the inaccuracy beyond 90 degrees is not a problem - we can easily use the 0 to 90 degree values for any angle by shifting and/or negating them by the appropriate amount.

It would be useful to see a graph of the various sine waves of the above values: this will form a future post.