In [1]:
import random
import math
and there are many more. These libraries are part of the ''standard library''. This means that they come with the base version of Python. There are a variety of other libraries that exist and are developed independently. Some of these come as standard with Anaconda.
This lab sheet will introduce one such library: SymPy which allows us to do symbolic mathematics.
A video describing the concept.
Using Python we can calculate the square root and trigonometric values of numbers (we do this by importing the math library)::
In [2]:
import math
math.sqrt(20)
Out[2]:
In [3]:
math.cos(math.pi / 4)
Out[3]:
These are fine for numerical work but not when it comes to carrying out exact mathematical calculations, where for example we know that:
$$ \cos(\pi / 4) = \sqrt{2} / 2 $$This is where Sympy is useful: it can carry out exact mathematical calculations:
In [4]:
import sympy as sym
sym.sqrt(20)
Out[4]:
In [5]:
sym.cos(sym.pi / 4)
Out[5]:
We also have access to complex numbers:
In [6]:
sym.I ** 2
Out[6]:
In [7]:
sym.sqrt(-20)
Out[7]:
Sympy also has numerous functions to manipulate natural numbers:
In [8]:
N = 45 * 63
sym.isprime(N)
Out[8]:
In [9]:
sym.primefactors(N)
Out[9]:
In [10]:
all(sym.isprime(p) for p in sym.primefactors(N)) # All prime factors are prime
Out[10]:
In [11]:
sym.factorint(N)
Out[11]:
In [12]:
N == 3 ** 4 * 5 * 7 # Checking the output of `factorint`
Out[12]:
Repeat the above example with a different value of N
.
A video describing the concept.
Using Sympy it is possible to carry out symbolic computations. To do this we need to creates instances of the Sympy Symbols class.
In [13]:
x = sym.Symbol("x")
x
Out[13]:
In [14]:
type(x)
Out[14]:
We can then manipulate this abstract symbolic object without giving it a specific numerical value:
In [15]:
x + x
Out[15]:
In [16]:
x - x
Out[16]:
In [17]:
x ** 2
Out[17]:
Sympy has a helpful symbols
(with a small s
) function that lets us create multiple sympy.Symbol
objects at a time:
In [18]:
y, z = sym.symbols("y, z")
y, z
Out[18]:
Symbolic expressions can be manipulated using Sympy's:
factor
expand
Here we confirm some well known formula:
In [19]:
expr = x ** 2 + 2 * x * y + y ** 2
expr
Out[19]:
In [20]:
expr.factor()
Out[20]:
In [21]:
expr = (x - y) * (x + y)
expr
Out[21]:
In [22]:
sym.expand(expr) # Note we could also use `expr.expand`
Out[22]:
Sympy also has a simplify
command that can be powerful. Experiment with all of these as well as more complex expressions.
A video describing the concept.
We can use Sympy to solve symbolic equations. Let us solve the following symbolic equation:
$$ x ^ 2 + 3 x - 2 = 0 $$We do this using the solveset
function:
In [23]:
sym.solveset(x ** 2 + 3 * x - 2, x)
Out[23]:
If our equation had a non zero right hand side we can use one of two approaches:
$$ x^2 + 3x - 2=y $$1
. Modify the equation so that it corresponds to an equation with zero right
hand side:
In [24]:
sym.solveset(x ** 2 + 3 * x - 2 - y, x)
Out[24]:
2
. Create an Eq
object:
In [25]:
eqn = sym.Eq(x ** 2 + 3 * x - 2, y)
sym.solveset(eqn, x)
Out[25]:
We can also specify a domain. For example the following equation has two solutions (it's a quadratic):
$$ x^2 = -9 $$
In [26]:
sym.solveset(x ** 2 + 9, x)
Out[26]:
However if we restrict ourselves to the Reals this is no longer the case:
In [27]:
sym.solveset(x ** 2 + 9, x, domain=sym.S.Reals)
Out[27]:
A video describing the concept.
It is possible to carry out various symbolic calculus related operations using Sympy:
Let us consider the function:
$$ f(x) = 1 / x $$We will do this by defining a standard Python function:
In [28]:
def f(x):
return 1 / x
and passing it our symbolic variable:
In [29]:
f(x)
Out[29]:
We can compute the limits at $\pm\infty$
In [30]:
sym.limit(f(x), x, sym.oo)
Out[30]:
In [31]:
sym.limit(f(x), x, -sym.oo)
Out[31]:
In [32]:
sym.limit(f(x), x, +sym.oo)
Out[32]:
We can also compute the limit at $0$ however we must be careful here (you will recall from basic calculus that the limit depends on the direction):
In [33]:
sym.limit(f(x), x, 0) # The default direction of approach is positive.
Out[33]:
In [34]:
sym.limit(f(x), x, 0, dir="+")
Out[34]:
In [35]:
sym.limit(f(x), x, 0, dir="-")
Out[35]:
We can use Sympy to carry out differentiation:
In [36]:
sym.diff(f(x), x)
Out[36]:
In [37]:
sym.diff(sym.cos(x), x)
Out[37]:
We can carry out various orders of differentiation. These all give the second order derivative of $\cos(x)$:
In [38]:
sym.diff(sym.diff(sym.cos(x), x), x)
Out[38]:
In [39]:
sym.diff(sym.cos(x), x, x)
Out[39]:
In [40]:
sym.diff(sym.cos(x), x, 2)
Out[40]:
As well as differentiation it is possible to carry out integration.
We can do both definite and indefinite integrals:
In [41]:
sym.integrate(f(x), x) # An indefinite integral
Out[41]:
In [42]:
sym.integrate(f(x), (x, sym.exp(1), sym.exp(5))) # A definite integral
Out[42]:
A video describing the concept.
We can use SymPy to solve differential equations. For example:
$$ \frac{dy}{dx} = y $$
In [43]:
y = sym.Function('y')
x = sym.symbols('x')
sol = sym.dsolve(sym.Derivative(y(x), x) - y(x), y(x))
sol
Out[43]:
Let us verify that the solution is correct:
In [44]:
sym.diff(sol.rhs, x) == sol.rhs
Out[44]:
We can also solve higher order differential equations. For example, the following can be used to model the position of a mass on a spring:
$$ m\frac{d^2x}{dt^2} + c\frac{dx}{dt} + kx = 0 $$
In [45]:
m, c, k, t = sym.symbols('m, c, k, t')
x = sym.Function("x")
sym.dsolve(m * sym.Derivative(x(t), t, 2) + c * sym.Derivative(x(t), t) + k * x(t), x(t))
Out[45]:
We can solve systems of differential equations like the following:
$$ \begin{aligned} \frac{dx}{dt} & = 1-y\\ \frac{dy}{dt} & = 1-x\\ \end{aligned} $$
In [46]:
eq1 = sym.Derivative(x(t), t) - 1 + y(t)
eq2 = sym.Derivative(y(t), t) - 1 + x(t)
sym.dsolve((eq1, eq2))
Out[46]:
The solution is given as:
$$ \begin{align} x(t) & =-C_1e^{-t}-C_2e^{t} + 1\\ y(t) & =-C_1e^{-t}-C_2e^{t} + 1\\ \end{align} $$A video describing the concept.
We can make use of $\LaTeX$ to display the output of Sympy in a human friendly way:
In [47]:
sym.init_printing()
m, c, k, t = sym.symbols('m, c, k, t')
x = sym.Function("x")
sym.dsolve(m * sym.Derivative(x(t), t, 2) + c * sym.Derivative(x(t), t) + k * x(t), x(t))
Out[47]:
On some occasions it might be helpful to be able to turn this off (for example if we wanted to copy and paste the output):
In [48]:
sym.init_printing(False)
m, c, k, t = sym.symbols('m, c, k, t')
x = sym.Function("x")
sym.dsolve(m * sym.Derivative(x(t), t, 2) + c * sym.Derivative(x(t), t) + k * x(t), x(t))
Out[48]:
Here are a number of exercises that are possible to carry out using Sympy:
Use SymPy to write the first $10^3$ prime numbers to file. Compare this
file to primes.csv
(download) (not by hand!) and check that it is the same.
We need to do a bit of work here to find out how we generate primes using sympy. This requires searching.
In [49]:
sym.prime(5) # the 5th prime is 11
Out[49]:
In [50]:
# let us generate all the first 10 ** 6 primes: this takes a while
primes = [sym.prime(k) for k in range(1, 10 ** 3 + 1)]
In [51]:
# let us read in the primes from file:
with open("primes.csv", 'r') as f:
primes_from_file = f.read().split('\n')
primes_from_file[:5] # Seeing the first 5
Out[51]:
In [52]:
primes_from_file[-5:] # Seeing the last 5
Out[52]:
In [53]:
primes_from_file[:10 ** 3] == [str(p) for p in primes] # We only consider the first 10 ^ 3 values in the file
Out[53]:
Use Sympy's simplify
method (and other things) to verify the follow trigonometric identities:
symbols
for this)
In [54]:
theta = sym.symbols('theta')
(sym.sin(theta) ** 2 + sym.cos(theta) ** 2).simplify() == 1
Out[54]:
In [55]:
(2 * sym.cos(theta) * sym.sin(theta)).simplify() == sym.sin(2 * theta)
Out[55]:
We need to work a bit harder to check equality here, taking the difference and seeing it's equal to 0.
In [56]:
((1 - sym.cos(theta)) / 2 - sym.sin(theta / 2) ** 2).simplify()
Out[56]:
Let us find out what options can be passed to symbols:
In [57]:
sym.symbols?
In [58]:
n = sym.symbols('n', integer=True)
sym.cos(n * sym.pi).simplify()
Out[58]:
In [59]:
def f(x):
return x ** 3 + 3 * x - 20
In [60]:
h, x = sym.symbols('h, x')
rhs = (f(x + h) - f(x)) / h
rhs
Out[60]:
In [61]:
sym.limit(rhs, h, 0)
Out[61]:
In [62]:
sym.diff(f(x), x)
Out[62]:
In [63]:
eq1 = sym.Derivative(y(x), x) - 6 * y(x) - 3 * sym.exp(x)
sol1 = sym.dsolve(eq1, y(x))
sol1
Out[63]:
In [64]:
eq2 = sym.Derivative(y(x), x) + x * (2 * x - 3) / (x ** 2 + 1) - sym.sin(x)
sol2 = sym.dsolve(eq2, y(x))
sol2
Out[64]:
In [65]:
eq3 = sym.Derivative(y(x), x, 2) - y(x) - sym.sin(5 * x)
sol3 = sym.dsolve(eq3, y(x))
sol3
Out[65]:
In [66]:
eq4 = sym.Derivative(y(x), x, 2) + 2 * sym.Derivative(y(x), x) + 2 * x - sym.cosh(x)
sol4 = sym.dsolve(eq4, y(x))
sol4
Out[66]:
In [67]:
x, y = sym.Function('x'), sym.Function('y')
eq1 = sym.Derivative(x(t), t) + y(t)
eq2 = sym.Derivative(y(t), t) + 5 * x(t)
sols = sym.dsolve((eq1, eq2))
sols
Out[67]: