Python Libraries: Sympy

Sympy is a Python library for symbolic computation where variables are represented as symbols. To use Sympy, it must first be imported as follows:

import sympy as sp

Symbolic Variables in Sympy

To create Symbolic variables that represents a mathematical symbol as opposed to a numeric value, use the symbols() function. This function takes a string of symbolic symbols separated by a space and returns Python variables

x,y = sp.symbols("x y")

To display Sympy expressions, first initialize printing: sp.init_printing() and then use display() to print

To express the following expression in Sympy, use only Sympy variables and Sympy functions

import sympy as sp
sp.init_printing()
x = sp.symbols("x")
y = x**2*(1 + sp.sqrt(x))/(x + 4)
display(y)

To substitute numerical values into the symbols, use the <expression>.subs({}) function, which takes a dictionary of the symbols and their numerical values. For example, if , , and , then <expression>.subs({x:1, y:2, z:3})

To evaluate the above expression at , do the following:

import sympy as sp
sp.init_printing()
x = sp.symbols("x")
y = x**2*(1 + sp.sqrt(x))/(x + 4)
y = y.subs({x:2})
display(y)

Notice that Sympy returns the substitution as fractions, to get the decimal equivalent, use the N() function

print(sp.N(y))

Sympy Matrices

To express the above matrix in Sympy, use Matrix():

import sympy as sp
sp.init_printing()
x = sp.Matrix([1,2,3])
display(x)

To express larger matrices like the matrix M below, each row in the matrix would be defined between two square brackets, individual rows are separated by commas and enclosed by a set of square brackets

import sympy as sp
sp.init_printing()
x = sp.Matrix([[1,2,3],[4,5,6],[7,8,9]])
display(x)

Matrix Math Operations

Let x and y be two unique vectors, let M be a matrix, and let n be a constant

• Dot Product: x.dot(y)
• Cross Product: x.cross(y)
• Multiplication of a Matrix With a Vector: M*x
• Multiplication of Matrices: M*M
• Scalar Multiplication: n*x
• Addition of Matrices: x+y
• Determinant: M.det()
• Inverse: M.inv()
• Transpose: M.T
import sympy as sp
sp.init_printing()

M = sp.Matrix([[1,2,3],[4,5,6],[7,8,9]])
x = sp.Matrix([1,2,3])
y = sp.Matrix([4,5,6])
display("Dot Product:",x.dot(y))        # 32
display("Cross Product:",x.cross(y))    # [[-3], [6], [-3]]
display("Multiplication of a Matrix With a Vector:",M*x)
# [[14], [32], [50]]
display("Multiplication of Matrices:",M*M)
# [[30, 36, 42], [66, 81, 96], [102, 126, 150]]
display("Scalar Multiplication:",2*x)   # [[2], [4], [6]]
display("Addition of Matrices:",x+y)    # [[5], [7], [9]]

M = sp.Matrix([[1, 2],[3, 4]])
display("Determinant:",M.det())         # -2
display("Inverse:",M.inv())             # [[-2, 1], [3/2, -1/2]]
display("Transpose:",M.T)               # [[1, 3], [2, 4]]

Matrix Access Operations

Let M be a matrix, let r be the row number, and let c be the column number

• Get Row r: M.row(r) or M[r,:]
• Get Column c: M.col(c) or M[:,c]
• Element at (r,c): M[r,c]
import sympy as sp
sp.init_printing()

M = sp.Matrix([[1,2,3],[4,5,6],[7,8,9]])
display("M:",M)                     # [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
display("Row 0",M.row(0))           # [[1, 2, 3]]
display("Row 0:",M[0,:])            # [[1, 2, 3]]
display("Col 1",M.col(1))           # [[2], [5], [8]]
display("Col 1:",M[:,1])            # [[2], [5], [8]]
display("Element at (1,2):",M[1,2]) # 6

Matrix Manipulation Operations

Let M be a matrix, let r be the row number, let c be the column number, and let n be a constant

• Insert to Row r: M = M.row_insert(r,sp.Matrix([[n,...]]))
• Insert to Column c: M = M.col_insert(c,sp.Matrix([n,...]))
• Delete Row r: M.row_del(r)
• Delete Column c: M.col_del(c)
import sympy as sp
sp.init_printing()

M = sp.Matrix([[1,2,3],[4,5,6],[7,8,9]])
display("M:",M)
M = M.row_insert(0, sp.Matrix([[9,9,9]]))
display("Insert to Row 0:",M)
M = M.col_insert(1, sp.Matrix([0,0,0,0]))
display("Insert to Col 1:",M)
M.row_del(0)
display("Delete Row 0:",M)
M.col_del(1)
display("Delete Col 1:",M)

Sympy Built In Functions

Let x = sp.symbols("x")

• Pi: sp.pi
• Infinity: sp.oo
• Sine: sp.sin(x)
• Cosine: sp.cos(x)
• Log: sp.log(x)
• Exponential: sp.exp(x)
• Factorial: sp.factorial(x)
• Square Root: sp.sqrt(x)
import sympy as sp
sp.init_printing()
x = sp.symbols("x")

display(sp.pi)
display(sp.oo)
display(sp.sin(x))
display(sp.cos(x))
display(sp.log(x))
display(sp.exp(x))
display(sp.factorial(x))
display(sp.sqrt(x))

Let x = sp.symbols("x"), let a and b be the lower and upper bounds, and let n be an integer

• Sum: sp.Sum(x**2,(x,a,b))
• First Derivative: sp.diff(x**2,x)
• Second Derivative: sp.diff(x**2,x,2)
• n-th Derivative: sp.diff(x**2,x,n)
• Integrate: sp.integrate(x**2,(x,a,b))
import sympy as sp
sp.init_printing()
x = sp.symbols("x")

display("Sum:",sp.Sum(x**2,(x,1,2)))
display("First Derivative:",sp.diff(x**2,x))
display("Second Derivative",sp.diff(x**2,x,2))
display("Integrate",sp.integrate(x**2,(x,1,2)))

Let a,b be integers

• Convert to Number: sp.N(a/b)
• Convert to Fraction: sp.Rational(a.b)
• Convert Sympy expression to Numpy function:
• sp.lambdify((symbols), sympy expression)
import sympy as sp
sp.init_printing()

display('N:',sp.N(3/2))
display('Rational:',sp.Rational(1.5))

x, y = sp.symbols('x y')
f = sp.lambdify((x,y), 3*x + 2*y)
display('Sympy to Numpy:', f(1, 2))

Sum

The built in Sum function can be used to evaluate the sum of a sequence of numbers, consider the following sequence:

Therefore:

To find the sum of the first 10 terms use Sum:

import sympy as sp
sp.init_printing()
n = sp.symbols("n")
display(sp.Sum(n**2 - n, (n,0,10)))
display(sp.N(sp.Sum(n**2 - n, (n,0,10))))

If the infinite sum of the sequence exists, then use the infinity symbol: sp.oo. Consider the following sequence:

Therefore:

import sympy as sp
sp.init_printing()
n = sp.symbols("n")
display(sp.Sum((1/2)**n, (n,0,sp.oo)))
display(sp.N(sp.Sum((1/2)**n, (n,0,sp.oo))))

Sympy Simplify Expressions

simplify, expand, and factor can be used to simplify a Sympy expression. Let exp be a Sympy expression

• Simplify: sp.simplify(exp)
• Expand: sp.expand(exp)
• Factor: sp.factor(exp)
import sympy as sp
sp.init_printing()
x = sp.symbols("x")

display(sp.simplify(sp.sin(x)**2 + sp.cos(x)**2))
display(sp.expand((x - 2)*(x + 3)))
display(sp.factor(x**2 + x - 6))

Sympy Solving Equations

Sympy has built-in functions to solve systems of equations. linsolve is used to solve a system of linear equations, nonlinsolve for a system of non-linear equations, dsolve for ordinary differential equations, and nsolve to solve a system of equations numerically

If the solver returns EmptySet() or ∅, then no solution was found, in which case, try another function

linsolve

sp.linsolve([equations],[variables])

Consider the following linear equation, and find the values of , , and that would satisfy , , and

Therefore, solve the following system of equations

import sympy as sp
sp.init_printing()
x, a0, a1, a2 = sp.symbols("x a0 a1 a2")

y = a0 + a1*x + a2*x**2
sol = sp.linsolve([y.subs({x:1}) - 5,
y.subs({x:2}) - 10,
y.subs({x:3}) - 25],
[a0,a1,a2])
display(sol)  # {(10, −10, 5)}
sol = list(sol)
print("a0 = ",sol[0][0],"\na1 = ",sol[0][1],"\na2 = ",sol[0][2])

linsolve returns {(10,-10,5)} which is of type “FiniteSet”. In order to index the solution, convert FiniteSet to list type: sol = list(sol). Then, get the first list inside the outer list using sol[0] to get (10,-10,5). Each element in this list corresponds to each of the variables passed into the solver: (a0,a1,a2). So, a0=sol[0][0], a1=sol[0][1], and a2=sol[0][2]

In cases where there are many possible solutions for , , and , Sympy will return all solutions as a separate list in the outer list {(...),(...),...}, so to index the solution to get the n-th solution set use: sol[n]

nonlinsolve

sp.nonlinsolve([equations],[variables])

Consider the following non-linear equation

import sympy as sp
sp.init_printing()
x = sp.symbols("x")

sol = sp.nonlinsolve([-x**2 - x + 1], [x])
display(sol)
sol = list(sol)
display(sol[0][0])
display(sol[1][0])

Sympy returns two possible solutions for x: . The first solution can be accessed like so: sol[0][0] and the second like so: sol[1][0]

dsolve

Before using dsolve, a Function variable must be defined: y = sp.Function("y"), which is used with respect to a symbol, such as x: y(x)

Initial conditions in dsolve requires Sympy 1.2 or later, update Sympy by running: !pip install sympy --upgrade

sp.dsolve(equation)
# or
sp.dsolve(equation, ics={initial conditions})

Consider the following differential equation:

import sympy as sp
sp.init_printing()
y = sp.Function("y")
x = sp.symbols("x")

sol = sp.dsolve(y(x).diff(x) - y(x)*x)
display(sol)

Now, solve the constant using the initial condition, :

import sympy as sp
sp.init_printing()
y = sp.Function("y")
x = sp.symbols("x")

sol = sp.dsolve(y(x).diff(x) - y(x)*x, ics={y(1):1})
display(sol)

If dsolve returns an equation: Eq(lhs,rhs), then the actual solution is on the right-hand side: sol.rhs

nsolve

sp.nsolve([equations],[variables],[initial guesses])

Unlike other solvers, nsolve will return numerical solutions as opposed to symbolic ones. It also requires initial guesses for each of the variables that approximates the answer

Consider the following system of equations

import sympy as sp
sp.init_printing()
x1, x2 = sp.symbols("x1 x2")

sol = sp.nsolve([x1**2 + x1*x2 - 10,
x2 + 3*x1*x2**2 - 57],
[x1,x2], [1.5,3.5])
display(sol)

Given initial guesses of and , the correct solution is and