Python Playground - SymPy

Learning the SymPy library - A library for symbolic mathematics and a replacement for LaTeX
Mathematics
Python Playground
Python
LaTeX
Published

April 2, 2025

“I threw down my enemy, and he fell from the high place and broke the mountain-side where he smote it in his ruin. Then darkness took me, and I strayed out of thought and time, and I wandered far on roads that I will not tell.”

Gandalf the White made this quote famous. Lesser known is my use of it each time I finish using LaTeX. Backslashes, starting and ending sections and equations, curly brackets - by the time I’ve finished writing out my own equations I can hardly read them, let alone solve them. I do not believe that x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a} is the best way to write out a formula. I’m a working in Python, let me write in Python.

Then I learned about SymPy.

SymPy is a library for use in symbolic mathematics and has built in functionality to work with those formulas. Unlike LaTeX, where the formula is static, SymPy allows you to both write out your formulas using mathematical symbols and solve them using those same formulas.

Their API Reference has tutorials on working with physics, calculus, and tensors, which is particularly useful for machine learning. I’ll be keeping it a bit simpler for this example. I’m just looking to get my feet wet with this library.

RPI Example

Over the last year I’ve had the privilege of working as the Project Data Lead for the Alabama Association of Basketball Coaches project to revamp high school basketball playoffs in the state. Part of that is building on the RPI formula which accounts for team win percentage, opponents’ win percentage, and opponents’ opponents’ win percentage. As such, I’ve written the formulas for RPI, win adjustments, and loss adjustments in LaTeX countless times, but need to separately solve the formulas in Python or R, depending on which language I am writing in. I want to see what it looks like if I use SymPy instead.

Win/Loss Adjustments

The formula for adjusted wins and losses are as follows:

\[\text{Adj. Win} = 1 + \frac{\text{Opp. RPI}}{2}\] \[\text{Adj. Loss} = 1 - \frac{\text{Opp. RPI}}{2}\]

I want to see what a win and loss against the number one team in the state would look like. That team is Hoover High School, who ended the season with an RPI of 0.8401.

from sympy import symbols, solve

# Set up symbols to work with
# You can use any symbols you like. They are separated by spaces or commas.
# Spaces appear to be the standard based on the Sympy documentation.
# adjw = Adjusted Win
# adjl = Adjusted Loss
# orpi = Opponent RPI

adjw, adjl, orpi = symbols('adjw adjl orpi')

# Create an expression for the adj. win formula
win_expr = 1 + (orpi / 2)

print(win_expr)
orpi/2 + 1

Something I learned I realized when playing around in that code cell is how I actually need to build expressions and formulas. I can’t just do adjw = 1 + (orpi / 2) because adjw is already set to an Symbol object. I also cannot immediately use the solve(). The arguments for the solve function assumes that the expression is equal to 0, which our expression is not.

Instead what we need to do is give the orpi object a numeric value using the subs() method, like so:

win_expr.subs(orpi, 0.8401)

\(\displaystyle 1.42005\)

A quick tangent because this caught me off guard. I had assumed using the subs() method would just give a value to the orpi object and then I would need to create an Eq object to set win_expr equal to adjw.

Instead, the subs() method solves the equation, subbing in the value given. The syntax is:

object.subs(variable, value to give variable)

There is also an Eq object that allows you to create symbolic equalities. That is what I thought I would need to use, but I learned that Eq is for building formulas, not solving for them.

from sympy import Eq

Eq(orpi,.8401)

\(\displaystyle orpi = 0.8401\)

As you can see, the Eq object doesn’t really give me what I am looking for. That just makes the output look nice, it does not actually give a value to orpi.

But, that means if I wanted to write out the formula it looks nice, like I might need out of LaTeX I could do something like this:

Eq(win_expr, adjw)

\(\displaystyle \frac{orpi}{2} + 1 = adjw\)

But, because symbols can be anything that I want, I would make my symbols be what I want to display. Applying what I’ve learned in the cells above, a better example would be something like this:

from sympy import pprint

# Create 'symbols' with full names
adjw, adjl, orpi = symbols('Adj.Win Adj.Loss OppRPI')

# Expression for adjusted wins formula
w_expr = 1 + (orpi / 2)

# Set up an Eq object that will print the full formula
win_formula = Eq(adjw, w_expr)

pprint(win_formula, use_unicode=True)

# Use subs to solve the equation
pprint(win_formula.subs(orpi, 0.8401))
          OppRPI    
Adj.Win = ────── + 1
            2       
Adj.Win = 1.42005

One thing you’ll notice is that I used pprint() instead of print(). This is not the same pprint() that is often used with JSON objects.

SymPy has multiple printing classes, one of which is sympy.printing.pretty.pretty.pretty. I promise you that is not a typo. pprint is a shortcut to import that class.

I figured this out because simply doing print(expression) does not actually output what you want. In the cells above, I just called the objects not in a print() method. Calling the objects at the end of your cell will give you what you want, but if you need to output multiple objects, doing print(expression) just outputs the class object, not the information you want. This is a longwinded explanation letting you know that pprint() is what you really want.

There are other printing classes you can use as well. An interesting bit that I found is that you can actually output the LaTeX code for your expressions creating an object of the LatexPrinter class and then calling the doprint(expression) method. This is certainly a useful hack when needing to write out long formulas.

Conclusion

I really enjoyed getting my hands dirty and playing around in this library. If you have beef with LaTeX like I do, I highly encourage you to check this out and play around on your own. I learned quite a bit through trial and error, and after reading through the documentation on this, it seems incredibly powerful. Its applications extend far beyond what I covered in this post. I do plan on using this in the future for when I am writing research papers.

I will be interested to see how it looks in a final format. For example, I often use LaTeX in an R Markdown file and then use Knitr to get a PDF output. LaTeX looks really nice in that. I hope that SymPy works just as well, because I already feel I have a better grasp of this library than I do with LaTeX. I suppose I will find out when I hit the publish button on this post!