Python is without question the programming language I am most comfortable with, and this is largely because it’s in such high demand. I have been in internship positions where the programming language of choice was Python, and so I had to learn how to use it. I also took two programming courses at Carnegie Mellon that were largely about programming in Python. Despite this, it took until my second year of graduate school before I needed the capabilities of solving algebraic and differential equations in a programming language.
While the main focus of my research has been in nonlocal partial differential equations, I picked up a side project a few months ago. The project is a collaboration with another graduate student at the University of Tennessee involving both statistical and analytical reviews of nonlinear disease models (not COVID-19 models, surprisingly enough). I have largely been in charge of the analytical component since that utilizes my strengths better. But eventually, the algebra needed to manipulate our systems of differential equations by hand became so cumbersome, I realized that even if I knew how to do the manipulations by hand, I’d likely make a mistake trying. So, I investigated ways to perform some of the necessary operations computationally, and stumbled across SymPy. After assuring myself that SymPy had the capabilities I needed, I installed the needed packages and got to work.
In this article I’m going to discuss the syntax for some of the tools in SymPy I had to learn how to use:
- Where to install SymPy and how
- Some necessary imports in your Python code
- Solving algebraic equations
- Taking ordinary and partial derivatives
- How to algebraically manipulate ordinary and partial differential equations
As a disclaimer: in no way am I trying to discredit MATLAB. At this point in the project most of the code we had written was in Python anyway, so it made sense to stick with Python.
Setup and Imports
Before we get into how SymPy works, I figured it’d be best to start with a link to download the package (for free) if you want to play around with SymPy’s capabilities yourself. Go to https://github.com/sympy/sympy/releases. There is also an extensive documentation for SymPy’s ins and outs, both in HTML form and as a PDF. Take note that new releases are added periodically, but most of the work is in the initial installation.
Going to this link will give a few options for installation instructions. I personally found it easiest to do the installation via the command line, but there are some other options as well. All of this assumes you’ve already downloaded and installed Python successfully, of course.
Now, in order to use any of the functions I’m going to demonstrate today, you need some imports. Here’s one set of imports you could use to empower yourself with the functions we need in this article:
(Quick tip: I like to comment my code so I remember why I included certain lines!)
I’ll give a quick breakdown of what I wrote above, but of course the full story of why these lines are necessary will be revealed later on.
- The “import sympy” line just tells your compiler that you will be using SymPy packages. It also allows the phrase “from sympy” to run without raising an error.
- The second line lets us import from the catalogue of SymPy libraries. For our examples here, the most important tool will be trigonometric functions, such as sine and cosine.
- By importing “symbols,” this lets us use designated letters like parameters, or constants. We have to designate specific letters to be the constants.
- By importing “solve,” we’ll have access to the solve function that can — you guessed it — solve equations.
- By importing “Functions” we will be able to explicitly define an algebraic function, and manipulate it as we would want to in a high school algebra class.
- The sympy.abc library lets us designate certain letters of the alphabet as algebraic variables. Obviously there is some flexibility for what letters you use, but the only ones available for this purpose through SymPy are the 26 standard uppercase and lowercase letters. Since I want my examples to be illustrative rather than cryptic I’m using common choices for variables.
Here are a couple of other lines I need to continue the setup:
The first three lines define symbols using the Symbols function. It works as follows: it takes a string (any alphanumeric characters and a few other things such as underscores), and lets us use parameters that aren’t just single letters. Remember, we made x, y, z, and t algebraic variables earlier. These are examples of the “constants” you might face, especially in physical or biological applications. I won’t need these symbols again for this article, but that’s how the syntax works.
Finally, you can likely guess what the last line is for: it designates f as an algebraic function, in terms of any number of variables we’d like. We just have to indicate what those variables are when we define a formula for the function.
To learn more about the SymPy tools I needed, I created three Python functions, each comprised of a series of examples in the form of assertion statements. Each of the last three sections will be organized as follows: I’ll provide my code for one of those functions, and explain how each example works one by one.
The Solve Function
Here is a series of examples utilizing the solve function in SymPy:
The basic idea of the “solve” function is that you identify the left-hand side of an equation. At first it confused me how we get to choose the right-hand side of the equation, but then I realized the solve function assumes the right-hand side of your equation is 0. From an algebraic standpoint this is an easy obstacle to overcome, because we can just move all terms to one side of the equation via addition and subtraction. We also have to specify what variable we are “solving” for. The reason the solve function works this way is to prevent ambiguity when there are multiple variables in the equation.
For these examples, remember defined x and y to be variables, and al to be a parameter (al is short for “alpha”). Now let’s break down each example separately.
- Example 1: solving x -3= 0 for x gives the solution x = 3
- Example 2: solving x-5 = 0 for x gives the solution x = 5
- Example 3: solving x²-1=0 for x gives two real solutions, x = -1 and x = 1
- Example 4: solving x -y = 0 for y gives y = x. Since x appears in the initial equation, it also appears in the final equation.
- Example 5: solving x -y + 2 = 0 for y gives the solution y = x + 2. In an algebra class, we’d say to add y to both sides of the equation to get the y by itself.
- Example 6: solving f(x)-x = 0 for f(x) gives f(x) = x. While f may be a function, the solve function treats f(x) kind of like an algebraic variable.
- Example 7: solving x²-y = 0 for y gives y = x².
- Example 8: solving x-al = 0 for x gives the solution x = al. This appears identical to Example 4 at first glance, except al is a parameter instead of a variable. As far as the solve function is concerned, though, it won’t treat these instances any differently from each other since we are still solving for a variable, x. If you tried to solve for the parameter al instead, issues would arise with your compiler.
Derivative and Partial Derivative Syntax
Now let’s dive into some examples of derivative calculations in SymPy:
One of the highlights of how differentiation works in SymPy is the flexibility between utilizing ordinary derivatives from partial derivatives. That highlight comes from the fact that you must always indicate what variable you are differentiating with respect to. Remember also that we defined f to be an algebraic function in our setup, and will treat it as such.
- Example 1: define f(x) = 2x²+3, and then its derivative with respect to x is 4x. The notation f.diff(x) literally means “differentiate f with respect to x.”
- Example 2: keeping f(x) = 2x²+3, the derivative with respect to y is 0. The notation f.diff(y) literally means “differentiate f with respect to y.”
- Example 3: define f(x, y) = 2x + 3y. Then the next two assertion lines indicate that the (partial) derivative with respect to x is 2, and that the (partial) derivative with respect to y is 3.
- Example 4: this example shows that we can differentiate things besides polynomials. Now we are letting f(x) = (x + 1)/(x²+1), and use the Quotient Rule for the derivative (or the Product Rule AND the Chain Rule).
One side comment: using the solve function and the derivative notation f.diff(x), we can solve for derivatives algebraically in a differential equation, in terms of other functions or variables that may appear. In other words, the solve function can isolate derivatives of functions in addition to variables and functions themselves.
The Substitution Function
We wrap up this article with the “subs” function, which lets us evaluate algebraic expressions by plugging in values. Here are the examples:
Due to importing SymPy libraries, we get the definitions of cosine and sine for free.
- Example 1: the first two assertion lines do substitutions on the algebraic expression expr = cos(x) + 1. In the first assertion, we simply replace, or substitute, all xs with ys, and we get cos(y) + 1. The second assertion demonstrates that we may replace a variable with a number instead. Replacing x with 0 yields the real number 2.
- Example 2: our initial expression can certainly be in terms of more than one variable, and we can even set one variable equal to the other. The third and fourth assertions demonstrating this. We define expr = cos(x) + cos(y), and then try substituting x for y, and substituting y for x. In both cases we get another expression in terms of just one variable.
Here’s a parting thought you may have observed yourself as we worked through these examples: the algebraic expressions are immutable, so performing a substitution does not change the identity of the original function. After the first assertion is checked, expr still equals cos(x) + 1.