import sympy as sym
I. Introduction
From 2018 to 2020, while I was studying for my Bachelor’s of Science in Applied Mathematics at San Jose State University, I found myself constantly and tediously performing the same types of mathematical procedures in my numerical analysis courses - deriving, integrating, and expanding polynomials. Much of these procedures can be time-consuming and take away from more essential aspects of my homework and project assignments - analysis, drawing conclusions, and achieving target goals.
I ended up searching across the Internet for ways to build Python code to automate much of the bulk of the calculus-based work I did in my numerical analysis courses. That search ended up providing me a haphazard, but efficient collection of techniques and procedures for doing calculus through the use of the package sympy, which I plan to explore and discuss briefly through this blog.
Thus, as a heads up, this blog deviates from my usual R-related blogs given it’s the first one to focus exclusively on Python and calculus (so this blog assumes you know the basics of both). However, should you be interested in some resources to learn how to do some calculus in R, I’ve recommended some resources at the end of the blog in the Resources section.
So other than that note, let’s do some calculus in Python.
II. Performing Calculus in Python
A. Getting Started
Before we do any calculus at all, you first would need to have the package sympy installed on your computer. For context, sympy (or SymPy) stands for “Symbolic Python” and contains methods and tools for writing and manipulating mathematical expressions in Python.
With the library sympy imported, we can now explore some ways to perform calculus-based operations in Python. However, we need to first establish variables for Python to recognize when taking derivatives and integrating with respect to.
As an example, let’s declare a variable x to be recognized as a mathematical variable using the function symbols (as shown below).
x = sym.symbols('x')To declare multiple variables, we can pass in a list of variables to their corresponding Python objects (using Python’s ability to assign values to many variables at once). As a note, make sure the order of the mathematical variables being created correspond to the order the Python objects are assigned (see code below).
x, y = sym.symbols(['x', 'y'])Now, that we created a set of variables, let’s first create a mathematical expression, say \(x^2 + 2x + 1\). Note that x**2 represents x raised to the power of 2.
For more complex exponents (such as \(x^{2+1}\)), you can use parenthesis to close them and let Python know which elements are apart of the exponents and which are outside of them (i.e., write \(x^{2+1}\) in code as x**(2 + 1) as otherwise, typing x**2+1 would get you \(x^{2} + 1\)).
math_exp = x**2 + 2*x + 1
math_expx**2 + 2*x + 1
With our variables and mathematical expression defined, we can now define how to generally do derivatives and integrals in Python.
B. Taking the Derivative
Here, let’s take the derivative of math_exp (i.e., \(x^2 + 2x + 1\)).
To do so, we use the Derivative function in sympy. The first argument allows us to specify the mathematical expression to derive, the second argument allows us to specify which variable to derive in terms of, and the third argument allows us to set the order of the derivative (by default, it’s at 1, meaning the first derivative).
Once the Derivative function is set up, call in the function doit() using the . operator and execute the command.
As an example, here we take the first derivative of math_exp in terms of the variable x, which gives us \[\frac{d}{dx} (x^2 + 2x + 1) = 2\cdot x^{2 - 1} + 2 \cdot 1 \cdot x^{1-0} + 0\] \[=2x+1\]
sym.Derivative(math_exp, x, 1).doit()2*x + 2
If we want the second derivative of math_exp, we can change the number 1 to 2 in the function Derivative() and rerun the above command with that change.
Thus, we get \[\frac{d^2}{dx^2} (x^2 + 2x + 1) = \frac{d}{dx} (2x + 1)\] \[=2\]
sym.Derivative(math_exp, x, 2).doit()2
C. Integrating functions
In the previous section, we described how to derive mathematical expressions. Here, I’ll show you how to do definite and indefinite integrals in Python using the integrate function in the Python library sympy, starting with definite integrals.
Definite integrals
With definite integrals, let’s demonstrate with math_exp (i.e., \(x^2 + 2x + 1\)) and say we have the lower and upper bounds \(x = 1\) and \(x = 2\) respectively to integrate for (i.e., \(\int_{x=1}^{x=3}(x^2 + 2x + 1) dx\))
In the integrate() function, we first specify the mathematical expression, then we pass in a tuple for the second argument where we specify the integration variable, lower bound, and upper bound in the following format: (integration_variable, lower_bound, upper_bound) (in this case, (x, 1, 2)).
Finally, we call in the function doit() using the . operator and execute the command.
sym.integrate(math_exp, (x, 1, 2)).doit()19/3
This result makes sense as \[\int_{x=1}^{x=2}(x^2 + 2x + 1) dx=\tfrac{x^{2+1}}{2+1} + 2\cdot \tfrac{x^{1+1}}{1+1} + 1\cdot\tfrac{x^{0+1}}{0+1}\Big|_{x=1}^{x=2}\] \[=\frac{x^3}{3} + x^2 + x \Big{|}_{x=1}^{x=2}\] \[=\frac{2^3}{3} + 2^2 + 2 - (\frac{1}{3} + 1^3 + 1)\] \[=\frac{8}{3} + 4 + 2 - (\frac{1}{3} + 1 + 1)\] \[=\frac{19}{3}\]
Now, if you want to integral in terms of more than one variable, you repeat the process above, except you add in another tuple to specify the integration variable and bounds for the additional variable you want to integrate with respect to.
As an example, let’s say we want to evaluate the double integral \(\int_{y=1}^{y=3}\int_{x=1}^{x=2}(x^2 + 2x + 1) \text{ }dx \text{ } dy\).
We repeat the same code as we did for a single integral, except, to integrate in terms of y from y=1 to y=3, we add the tuple (y, 1, 3) after the tuple (x, 1, 2) (which can be further expanded in the same way for even triple integrals and beyond). This arrangement will first integrate in terms of x first and evaluates the integrated expression using x’s upper and lower bounds. Then, the Python interpreter will integrate the results of the previous integration in terms of y and its respective upper and lower bounds.
The results of \(\int_{y=1}^{y=3}\int_{x=1}^{x=2}(x^2 + 2x + 1) \text{ }dx \text{ } dy\) can be implemented and seen in the code below.
sym.integrate(math_exp, (x, 1, 2), (y, 1, 3)).doit()38/3
Indefinite integrals
Now, what if you don’t want to calculate definite integrals, but indefinite ones where you integrate from a more conceptual standpoint?
In this case, not much will change from the previous code segment, except we pass in a single variable than a tuple containing the integration variable and its specified bounds into the integrate() function.
As an example, let’s do an indefinite integral of $ (x^2 + 2x + 1) dx$ and save the result to an object called indef_int (see the code below).
Note below, that instead of a tuple like (x, 1, 2) passed in, we simply pass in the integration variable x after the mathematical expression stored in the Python object math_exp is passed in; after all, we’re doing indefinite integrals so no upper and lower bounds are inputted.
indef_int = sym.integrate(math_exp, x).doit()
indef_intx**3/3 + x**2 + x
It can be difficult to read, but optionally, you can convert it into a LaTex command you can use via the latex function in sympy.
sym.latex(indef_int)'\\frac{x^{3}}{3} + x^{2} + x'
By outputting the LaTex command above, we find that the above result reads as \(\frac{x^{3}}{3} + x^{2} + x\), which is missing the constant of integration for \(C\) in it for indefinite integrals. That’s because sympy typically doesn’t include constants of integration by default. Thus, the result should be that \(\int(x^2 + 2x + 1) \text{ }dx = \frac{x^{3}}{3} + x^{2} + x + C\) where \(C\) is a constant representing the constant of integration.
In such cases, it’s important to be wary when calculating indefinite integrals in Python due to the lack of a constant of integration being incorporated each time you integrate a function.
For more information about this phenomenon in detail, you can check out this section on integrals in the documentation for sympy here: https://docs.sympy.org/latest/tutorials/intro-tutorial/calculus.html#integrals
III. Some Useful SymPy Features to Know
Lastly, before I finish off this blog, I find it helpful to dive into some useful functions worth noting that’ll help you when evaluating mathematical expressions in Python.
These techniques aren’t necessary to know, but they can prove useful in simplifying mathematical expressions (especially the more lengthy and complex ones unearthed when derived).
A. Expanding mathematical expressions
As a small-scale case study, let’s say we have a compressed or simplified form of a mathematical expression, say the polynomial \(x^2 + 2x + 1\) being rewritten as \((x + 1)^2\).
We want to expand \((x + 1)^2\) out as \(x^2 + 2x + 1\), which is where the expand() function comes into play. First, we define our mathematical expression as we did before.
math_exp = (x + 1) **2
math_exp(x + 1)**2
Next, we call in the function expand() using the . (dot) operator on math_exp and run the command (see the code below).
math_exp.expand()x**2 + 2*x + 1
As you can see, sympy successfully expanded the mathematical expression \((x + 1)^2\) out as \(x^2 + 2x + 1\) since \[(x + 1)^2 = (x + 1)(x+1) \] \[= x^2 + x + x + 1 \] \[= x^2 + 2x + 1\]
Note that expand() may not always give the fullest and most expansive version of a mathematical expression since it only does so in the most general sense. Thus, when in doubt, you may need to verify the results, check by hand, or explore other sympy functions if needed.
For more details, check out this section on simplifying mathematical expressions in the SymPy documentation here: https://docs.sympy.org/latest/tutorials/intro-tutorial/simplification.html
B. Evaluating an expression at given values
Suppose you have a mathematical expression and you want to plug in values to evaluate it at. You can do so with the subs() function.
As an example, suppose we want to evaluate \(f(x) = x^2 + 2x + 1\) when \(x = 1\).
To do this operation in Python, we call in the subs() function with the . operator on the mathematical expression and pass in the selected variable as well as the value to replace each occurrence of the variable with.
In the case of the code below, we specify x and 1 to mean that for every occurrence of x in the mathematical expression, we replace it with the value 1 and evaluate it.
math_exp = x**2 + 2*x + 1
math_exp.subs(x, 1)4
We get that at \(x = 1\) for the expression \(f(x) = x^2 + 2x + 1\), we get \[f(x = 1) = 1^2 + 2\cdot 1 + 1 \] \[= 1 + 2 + 1 \] \[= 4\].
Now, what if you have multiple variables and you want to pass in multiple values? In this case, the same procedure applies, except instead of passing in two arguments, we pass in a Python dictionary of arguments.
To demonstrate, let’s say we want to evaluate the expression \(f(x, y) = x^2 + y^2 + 1\) with \(x=1\) and \(y=2\). We call in the subs() function using the . operator as before, but we pass in a dictionary of arguments for what we like x and y to be (see the code below).
math_exp = x**2 + y**2 + 1
math_exp.subs({x: 1, y:2})6
Here, we get that at \(x=1\) and \(y=2\) for the expression \(f(x,y) = x^2 + y^2 + 1\), we have that the result is equal to \(6\) since \[f(x=1,y=2) = 1^2 + 2^2 + 1\] \[=1+4+1\] \[=6\]
As shown earlier, the code is slightly different when passing in multiple arguments as otherwise, we get this result below where say, we only set \(x=1\).
math_exp = x**2 + y**2 + 1
math_exp.subs({x: 1})y**2 + 2
The code does function, but since we didn’t set a value for \(y\), we end up with a polynomial that’s been reduced significantly.
C. Rounding coefficients in a function
Sometimes, you may have large and complex decimals as the coefficients of the terms in a given mathematical expression. In those cases, you may find it fruitful to round them.
To demonstrate, let’s define a function \(f(x) = \frac{x^3}{3} + \frac{x^2}{6} + 4\).
math_exp = (x**3/3) + (x**2/6) + 4
math_expx**3/3 + x**2/6 + 4
We can put it in decimals by calling the evalf() using the . operator on the object math_exp. evalf() evaluates the coefficients and numeric arguments to an n number of decimal places (which by default, n = 15, meaning rounded up to 15 decimal places).
math_exp.evalf()0.333333333333333*x**3 + 0.166666666666667*x**2 + 4.0
The above equation can be read as \(0.333333333333333 \cdot x^3 + 0.166666666666667 \cdot x^2 + 4.0\).
We can change the amount of decimal places by setting n to be any particular number of decimal places (noting that the maximum amount of digits allowed is 100).
In this case, let’s round the coefficients to around two decimal places (i.e., n = 2), which gives us \(0.33 \cdot x^3 + 0.17 \cdot x^2 + 4.0\) (as shown in the code and output below)
math_exp.evalf(n = 2)0.33*x**3 + 0.17*x**2 + 4.0
You can find more details about the evalf() function in this section of the documentation on the sympy library here: https://docs.sympy.org/latest/modules/evalf.html
IV. Conclusion
Overall, Python has an array of functions and tools you can implement to perform integration and differentiation using the library sympy (which stands for “symbolic python”).
We’ve explored some of the basics of how to set up mathematical variables via the symbols() function, taking derivatives and integrals with the Derivative() and integrate() function, and some extra sympy functions that can enhance how you work with mathematical expressions in Python (i.e., expand(), evalf(), subs()).
While there’s certainly more to the Python library sympy than what I could cover here in this brief blog post, you can check out some resources I recommend below for more details and information on performing calculus operations in Python.
Otherwise, I hope you find this light introduction to doing calculus in Python useful and relevant to you. Happy coding, everyone!
V. Resources
For performing calculus-based operations in R (and Python) as it relates to data science and scientific programming, I recommend the following resources below to further enhance your knowledge in these areas:
A. Links
- Differentiation and integration in R by N.B. Venkateswarlu: Here’s a good online resource to figure out how to take derivatives and integrals in R, which is surprising given R’s often associated with statistics and data science. However, in a way, it’s not so surprising at the same time given that concepts such as probability distributions and linear regression make use of partial differentiation and integration. While this resource doesn’t show how to do indefinite integrals (something I’m looking to figure out), it’s a good place to start for doing calculus in R.
- Beginners’ Guide to Calculus with R by Daniel Kaplan: Thankfully, there’s a package in R called
mosaicCalcthat allows you to perform calculus-based operations in-depth, which includes creating vector fields. Additionally, in this introductory article, the author Daniel Kaplan also provides references to his Mosaic Calculus textbook (which covers topics in most introductory calculus courses with a focus on applications in R), which you can read online here: https://www.mosaic-web.org/MOSAIC-Calculus/. - Documentation for SymPy library: Here’s the documentation for the library SymPy if you’re interested in getting a more in-depth dive into what you can do with
sympyand how it works. The library also contains trigonometric functions and other distinctive mathematical functions should you want to perform derivatives or integrals with them, which this documentation explains as well (see the Functions section here for details: https://docs.sympy.org/latest/modules/functions/elementary.html). You can also read this part of the documentation to get more details on doing calculus in Python: https://docs.sympy.org/latest/tutorials/intro-tutorial/calculus.html - Review of Differentiation and Integration for Ordinary Differential Equations by Dr. Lynn Schreyer: Here’s a good review of derivatives and integration in case you need a quick refresher on calculus (especially if it’s been a while since you’ve taken such a course).
B. Books
- Automate the Boring Stuff with Python (2nd Edition) by Al Sweigart: This book’s one of my favorites for getting a thorough introduction to the Python programming language in a relevant manner that assumes you’re new to programming in general. While it doesn’t quite get into the details of object-oriented programming and inheritance (which are more computer-science heavy than practical for a Python book directed at non-STEM majors), it does cover a set of useful tools for automating basic clerical tasks you might find tedious (such as scraping websites, pattern matching in text data, reading in pdfs, deleting files, etc). You can read it online here: https://automatetheboringstuff.com/
- Python Data Science Handbook (2nd Edution) by Jake VanderPlas: This book is a classic on getting you started with performing common data science-related tasks in Python using popular libraries such as
pandas,numpy,matplotlib, and a few others. It assumes you have a basic understanding of Python and provides a good overview of features you can use to optimize your data science projects in Python. You can read the first edition of this book online here: https://jakevdp.github.io/PythonDataScienceHandbook/.