The sympy module

There are many proprietary symbolic mathematical packages, such as Mathematica or [Maple](http://www.maplesoft.com/products/maple/, that can handle advanced angebraic expressions. sympy is an open-source Python module that is a free alternative to these expensive products. In this notebook we are going to get to know some sympy functions. Before jumping into these functions, here are two pictures as a warning:

vs.

It is generally true, that common users, mostly students, are prone to trust these functions. It is common to eavesdrop conversations in university corridors such as "even Mathematica could not calculate it!". These programmes/modules are not omniscient and omnipotent! We should rather treat them as useful aids, that only complement our own knowledge, and not an omniscient black box, that gives the right answer for every single question.

It is very important to note that there are many functions in pylab that have the name name as a function in sympy. Therefore, it is useful to run a new notebook for solving sympy problems, and using only %matplotlib inline instead of %pylab inline in these notebooks.

In [1]:
# the next command only prepares the environment for plotting
%matplotlib inline
# this is for symbolic computation
from sympy import * # loading sympy routines
init_printing()     # nicer printing

Variables, solving equations and equation systems

To be able to manipulate our variables in a symbolic way, we have to tell Python to treat them as symbols of a mathematical formula. In the simplest case it is done by:

In [2]:
x=symbols('x')

After that, we can use the variable $x$ for symbolic calculations. Let us for example solve the next simple equation: $$3x=5$$ We can do this by using the solve function. The first input of the solve function is the equation arranged to 0, the second is the variable that we are looking for:

In [3]:
solve(3*x-5,x)
Out[3]:
$$\left [ \frac{5}{3}\right ]$$

Let us define some more variables!

In [1]:
y,z,a,b,c=symbols('y,z,a,b,c') # defining more variables at once
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-1-7d87f1cdbf73> in <module>()
----> 1 y,z,a,b,c=symbols('y,z,a,b,c') # defining more variables at once

NameError: name 'symbols' is not defined
In [5]:
k,l=symbols('k,l',integer=True) # we constrain the defined variables to integers
p,q=symbols('p,q',real=True) # to real numbers

Let us now solve the equation $$a x+b=y$$ for $x$!

In [6]:
solve(a*x+b-y,x)
Out[6]:
$$\left [ \frac{1}{a} \left(- b + y\right)\right ]$$

If there are more solutions to an equation, then the solve function of the sympy module finds possibly all. For example, here are the solutions to a quadratic equation:

In [7]:
solve(a*x**2+b*x+c,x)
Out[7]:
$$\left [ \frac{1}{2 a} \left(- b + \sqrt{- 4 a c + b^{2}}\right), \quad - \frac{1}{2 a} \left(b + \sqrt{- 4 a c + b^{2}}\right)\right ]$$

The solution is not always real. The imaginary unit 1 is denoted by I in sympy.

In [8]:
I**2
Out[8]:
$$-1$$

We can use the solve function to find solutions of an equation system. We have to put all our equations arranged to zero into a list. The variables as well. For example, let us solve the following system for $x$ and $y$:

$$y=x^2+ax-4b$$$$y=x-b$$

The first equation is linear in x, therefore, we expect two solution pairs (a parabole dissects a line in at most two points).

In [9]:
solve([x**2 + a*x -4*b - y, x - y - b],[x,y]) # solving a system of equations
Out[9]:
$$\left [ \left ( - \frac{a}{2} - \frac{1}{2} \sqrt{a^{2} - 2 a + 12 b + 1} + \frac{1}{2}, \quad - \frac{a}{2} - b - \frac{1}{2} \sqrt{a^{2} - 2 a + 12 b + 1} + \frac{1}{2}\right ), \quad \left ( - \frac{a}{2} + \frac{1}{2} \sqrt{a^{2} - 2 a + 12 b + 1} + \frac{1}{2}, \quad - \frac{a}{2} - b + \frac{1}{2} \sqrt{a^{2} - 2 a + 12 b + 1} + \frac{1}{2}\right )\right ]$$

The output of the solve function is now a list, whose elements contain the $(x,y)$ pairs that solve the equation system.

Substitution and evaluation

In many cases, we can introduce an abbreviation for some pieces in a calculation that occur several times. After the calculations, we have to put these abbreviated pieces back into the results. Similar operations are obtainable with the subs() method, that can be used for any sympy objects. Let us see an example! Let the result of a calculcation be $x^2+ax-y$, and let us store it in the variable sg.

In [10]:
sg=x**2 + a*x - y
sg
Out[10]:
$$a x + x^{2} - y$$

Let us suppose now, that $y$ was used to substitue the expression $x^3$, and we want to write $x^3$ back. The subs method can then be used in the following way:

In [11]:
sg.subs(y,x**3)
Out[11]:
$$a x - x^{3} + x^{2}$$

The last step of calculation is evaulating our results numerically. This can be done with the evalf() method of sympy. For example:

In [12]:
foo=sqrt(8)
In [13]:
foo.evalf()
Out[13]:
$$2.82842712474619$$

Calling evalf with only one integer argument, we can influence the numerical precision of the result.

In [14]:
foo.evalf(40)
Out[14]:
$$2.828427124746190097603377448419396157139$$

If the expression contains more than one unknowns, then we can list these as a dictionary into the subs keyword argument of evalf.

In [15]:
foo2=sin(2*x)+cos(y)
In [16]:
foo2.evalf(12,subs={x:0.5,y:0.3})
Out[16]:
$$1.79680747393$$

Function analysis

One of the best advantages of sympy is that it makes it possible to perform some simple operations from analysis in Python. Below we show some simple functions, but these are far from a complete picture. For example, let us calculate the following limits with the help of the limit function: $$\lim_{x\rightarrow 0}\frac{\sin x}{x}=? $$ and $$\lim_{x\rightarrow \infty}\frac{1}{1+\mathrm{e}^{-x}}=? $$

In [17]:
limit(sin(x)/x,x,0)
Out[17]:
$$1$$

If we want the limit in the infinity, we can achieve it by using the oo symbol.

In [18]:
limit(1/(1+exp(-x)),x,oo)
Out[18]:
$$1$$

The derivative of expressions can be found with the diff function. The first derivative of $\sin(x)$ with respect to $x$ is:

In [19]:
diff(sin(x),x)
Out[19]:
$$\cos{\left (x \right )}$$

The second derivative:

In [20]:
diff(sin(x),x,x)
Out[20]:
$$- \sin{\left (x \right )}$$

or maybe a simpler way:

In [21]:
diff(sin(x),x,2)
Out[21]:
$$- \sin{\left (x \right )}$$

Of course, partial derivatives are also available.

In [22]:
diff(sin(x)*cos(y),x,y)
Out[22]:
$$- \sin{\left (y \right )} \cos{\left (x \right )}$$

Partial derivatives of higher order is a generalization of simple derivatives.

In [23]:
diff(sin(x)*cos(y),x,2,y,3)
Out[23]:
$$- \sin{\left (x \right )} \sin{\left (y \right )}$$

The integrate function calculated definite and indefinite integrals.

Let us search for the primitive function of $x^2$.

In [24]:
integrate(x**2,x)
Out[24]:
$$\frac{x^{3}}{3}$$

The $$\int_0^3 x^2\mathrm{d}x$$ definite integral can be evaluated in the following way:

In [25]:
integrate(x**2,(x,0,3))
Out[25]:
$$9$$

Of course, during the integration, we might have other parameters:

In [26]:
integrate(x**2+y**3,(x,0,3))
Out[26]:
$$3 y^{3} + 9$$

Multivariate integrals are evaluated by writing the variables (in the case of definite integrals, the limits) after each other.

The $$\int x^2+y^3 \mathrm{d}x\mathrm{d}y $$ definite integral:

In [27]:
integrate(x**2+y**3,x,y)
Out[27]:
$$\frac{x^{3} y}{3} + \frac{x y^{4}}{4}$$

The $$\int_0^3\int_{-3}^{5} x^2+y^3 \mathrm{d}x\mathrm{d}y $$ definite integral:

In [28]:
integrate(x**2+y**3,(x,0,3),(y,-3,5))
Out[28]:
$$480$$

Plotting analytical functions

When we determine analytical functions with the sympy module, we can also plot them. sympy uses the already familiar matplotlib functions, but with a slightly different syntax. The main difference between the two packages is that matplotlib can only handle striclty numerical data, therefore, the user has to make the sampling and the evaluation of the points before the plot, while sympy uses the analytical expression itself, and it makes the sampling and evaluation steps, and creates the numerical datastructures in the background. In the following section, we are going to use the plotting submodule of the sympy module for displaying analytical curves and functions.

In [29]:
from sympy.plotting import * # loading the module

The plot function can be used for displaying univariate scalar expressions.

In [30]:
plot(1/(exp(x)+1),(x,-10,10))
Out[30]:
<sympy.plotting.plot.Plot at 0x7f5178f044a8>

The plot_parametric functions serves to display general parametric curves.

In [31]:
plot_parametric(sin(x),cos(x),(x,0,2*pi))
Out[31]:
<sympy.plotting.plot.Plot at 0x7f5178f11cc0>

Spatial curves can be displayed using the plot3d_parametric_line function.

In [32]:
plot3d_parametric_line(cos(3*x),sin(3*x),x,(x,0,2*pi))
Out[32]:
<sympy.plotting.plot.Plot at 0x7f517909fef0>

Spatial surfaces might be displayed by the plot3d and the plot3d_parametric_surfacecommand.

In [33]:
plot3d(sin(x)*cos(y),(x,-pi,pi),(y,-pi,pi))
Out[33]:
<sympy.plotting.plot.Plot at 0x7f51790acb00>
In [34]:
 u, v = symbols('u v')
In [35]:
plot3d_parametric_surface(sin(v)*cos(u),sin(v)*sin(u),cos(v), (u, 0, 2*pi), (v, 0, pi))
Out[35]:
<sympy.plotting.plot.Plot at 0x7f51769f7550>

Simplification

The symmpy module has many methods for simplifying expressions. Let us now have a look at the simplest ones.

General case, the simplify function

The most general simplifying function is simplify, which aims to reduce the expression in its argument to its most compressed form without further specifications. Some examples follow.

In [36]:
simplify(sin(x)**2 + cos(x)**2)
Out[36]:
$$1$$
In [37]:
simplify((x**3 + x**2 - x - 1)/(x**2 + 2*x + 1))
Out[37]:
$$x - 1$$

We've seen some successful calls, let us now have a look at a failed one! simplify cannot deal with the next expression, though it is clear that it could be rewritten.

In [38]:
simplify(x**2 + 2*x +1)
Out[38]:
$$x^{2} + 2 x + 1$$

The cause for this phenomenon is that simplification is algorithmically ill defined. If we tell the function what to look for, it might be more efficiont. There are some specific functions that help us in certain simplification or rewriting tasks.

Polynomial and rational expressions

Polinomiális és racionális kifejezések

The expand() function expands polynomials in its most simple case.

In [39]:
expand((x+y)**3)
Out[39]:
$$x^{3} + 3 x^{2} y + 3 x y^{2} + y^{3}$$

The factor() function is in some sense the opposite of the expand() function. It aims to create the shortest possible multiplication form from the given expression, for example the previous expression which has been left untouched by simplify:

In [40]:
factor(x**2 + 2*x +1)
Out[40]:
$$\left(x + 1\right)^{2}$$

yields the expected simpler form.

For more general multivariate polynomial expressions it happens that we would like to order the polynomial according to a vertain variable. The collect() function might be used for this.

In [41]:
expr=x*y + x - 3 + 2*x**2 - z*x**2 + x**3
In [42]:
collect(expr,x)
Out[42]:
$$x^{3} + x^{2} \left(- z + 2\right) + x \left(y + 1\right) - 3$$
In [43]:
xek=collect(expr,x)

The coeff method gives the coefficients corresponding to the appropriate exponent.

In [44]:
xek.coeff(x,2)
Out[44]:
$$- z + 2$$

The cancel() function simplifies rational expressions, that are polynomial over polynomial shaped.

In [45]:
cancel((x*y**2 - 2*x*y*z + x*z**2 + y**2 - 2*y*z + z**2)/(x**2 - 1))
Out[45]:
$$\frac{1}{x - 1} \left(y^{2} - 2 y z + z^{2}\right)$$

The apart() function gives the partial fraction decomposition of a rational expression.

In [46]:
expr = (4*x**3 + 21*x**2 + 10*x + 12)/(x**4 + 5*x**3 + 5*x**2 + 4*x)
expr
Out[46]:
$$\frac{4 x^{3} + 21 x^{2} + 10 x + 12}{x^{4} + 5 x^{3} + 5 x^{2} + 4 x}$$
In [47]:
apart(expr)
Out[47]:
$$\frac{2 x - 1}{x^{2} + x + 1} - \frac{1}{x + 4} + \frac{3}{x}$$

Functions containing trigonometric functions

There are two methods for hadling trigonometric expressions.

The trigsimp() tries to shorten the expressions by using trigonometric equalities.

In [48]:
expr=sin(x)**4 - 2*cos(x)**2*sin(x)**2 + cos(x)**4
expr
Out[48]:
$$\sin^{4}{\left (x \right )} - 2 \sin^{2}{\left (x \right )} \cos^{2}{\left (x \right )} + \cos^{4}{\left (x \right )}$$
In [49]:
trigsimp(expr)
Out[49]:
$$\frac{1}{2} \cos{\left (4 x \right )} + \frac{1}{2}$$

The expand_trig() function creates expressions with the simplest possible arguments. Of course, the result might be much longer than the original expression.

In [50]:
expand_trig(cos(4*x))
Out[50]:
$$8 \cos^{4}{\left (x \right )} - 8 \cos^{2}{\left (x \right )} + 1$$

Rewrite

The rewrite method enables us to rewrite sympy expressions in a very general way. It can be used on trigonometric functions, for example.

In [51]:
tan(x).rewrite(sin)
Out[51]:
$$\frac{2 \sin^{2}{\left (x \right )}}{\sin{\left (2 x \right )}}$$

or transforming trigonometric expressions into exponential functions.

In [52]:
sin(x).rewrite(exp)
Out[52]:
$$- \frac{i}{2} \left(e^{i x} - e^{- i x}\right)$$

But some other examples are:

In [53]:
factorial(x).rewrite(gamma) # factorial and gamma function
Out[53]:
$$\Gamma{\left(x + 1 \right)}$$
In [54]:
binomial(x,y).rewrite(factorial) # binomial coefficients and factorial
Out[54]:
$$\frac{x!}{y! \left(x - y\right)!}$$

sympy has many more possibilities for rewriting formulae, we've only had a look at a small fraction of them so far. For further information it's worth to browse the documentation.

Analytical linear algebra

We can solve linear algebraic problems with sympy. Such problems use the Matrix class of the sympy module. Attention, this is not the matrix class of the numpy module, or its array class.

Next, we are going to have a look at creating symbolic matrices, and their operations.

Let us create a 2x2 matrix!

In [55]:
M=Matrix([[0, -x*exp(I*y)],
          [-x*exp(-I*y), 0]])
M
Out[55]:
$$\left[\begin{matrix}0 & - x e^{i y}\\- x e^{- i y} & 0\end{matrix}\right]$$
In [56]:
B=Matrix([[1,2],
          [4,5]])
B
Out[56]:
$$\left[\begin{matrix}1 & 2\\4 & 5\end{matrix}\right]$$

The element of the matrix M contain sympy variable themselves, while B matrix only contains numerical values. The two matrices behave as we would expect, we can add them up:

In [57]:
M+B
Out[57]:
$$\left[\begin{matrix}1 & - x e^{i y} + 2\\- x e^{- i y} + 4 & 5\end{matrix}\right]$$

or we can multiply them:

In [58]:
M*B
Out[58]:
$$\left[\begin{matrix}- 4 x e^{i y} & - 5 x e^{i y}\\- x e^{- i y} & - 2 x e^{- i y}\end{matrix}\right]$$

The result of the multiplicaction depends on the order of the matrices.

In [59]:
B*M
Out[59]:
$$\left[\begin{matrix}- 2 x e^{- i y} & - x e^{i y}\\- 5 x e^{- i y} & - 4 x e^{i y}\end{matrix}\right]$$

The power operation is also not elementwise, but uses the power of matrices!

In [60]:
M**2
Out[60]:
$$\left[\begin{matrix}x^{2} & 0\\0 & x^{2}\end{matrix}\right]$$

We can determine the inverse with the ** operator.

In [61]:
B**(-1)
Out[61]:
$$\left[\begin{matrix}- \frac{5}{3} & \frac{2}{3}\\\frac{4}{3} & - \frac{1}{3}\end{matrix}\right]$$

There are some functions that can be used for creating special matrices. eye() makes an identity matrix of arbitrary size.

In [62]:
eye(3)
Out[62]:
$$\left[\begin{matrix}1 & 0 & 0\\0 & 1 & 0\\0 & 0 & 1\end{matrix}\right]$$

The diag() function might be used for creating matrices with diagonal structure.

In [63]:
diag(sin(x),y,z)
Out[63]:
$$\left[\begin{matrix}\sin{\left (x \right )} & 0 & 0\\0 & y & 0\\0 & 0 & z\end{matrix}\right]$$

To solve some linear algebraic probblems, we have to have row and column vectors. Matrix class can be used for them, too.

In [64]:
v=Matrix([[1,2]])
v
Out[64]:
$$\left[\begin{matrix}1 & 2\end{matrix}\right]$$
In [65]:
w=Matrix([[1],[2]])
w
Out[65]:
$$\left[\begin{matrix}1\\2\end{matrix}\right]$$

Matrix-vector multiplication goes as we'd expect.

In [66]:
B*w
Out[66]:
$$\left[\begin{matrix}5\\14\end{matrix}\right]$$
In [67]:
v*B
Out[67]:
$$\left[\begin{matrix}9 & 12\end{matrix}\right]$$

Linear equation systems given by matrices and vectors can be solved by the solve() method of the Matrix class. For example the above defined B and w variables give the following equation:

$$Bx=w$$

that we can solve for $x$:

In [68]:
B.solve(w)
Out[68]:
$$\left[\begin{matrix}- \frac{1}{3}\\\frac{2}{3}\end{matrix}\right]$$

The determinant of a matrix is det, the trace of a matrix is trace.

In [69]:
M.det()
Out[69]:
$$- x^{2}$$
In [70]:
B.trace()
Out[70]:
$$6$$

The transpose or adjoint of a matrix is .T, or .H.

In [71]:
M.T
Out[71]:
$$\left[\begin{matrix}0 & - x e^{- i y}\\- x e^{i y} & 0\end{matrix}\right]$$
In [72]:
M.H
Out[72]:
$$\left[\begin{matrix}0 & - e^{i \overline{y}} \overline{x}\\- e^{- i \overline{y}} \overline{x} & 0\end{matrix}\right]$$

In the above results, the overbar denotes the complex conjugate.

For investigating eigenvalue and eigenvector problems, we can use the eigenvals() and eigenvects() methods. eigenvals() returns a dictionary that contains the eigenvalues as keys, and the value denotes the degeneracy of the eigenvalue.

In [73]:
M.eigenvals()
Out[73]:
$$\left \{ - x : 1, \quad x : 1\right \}$$

eigenvects() returns the eigenvalues, their multiplicity and the corresponding eigenvectors.

In [74]:
M.eigenvects()
Out[74]:
$$\left [ \left ( - x, \quad 1, \quad \left [ \left[\begin{matrix}e^{i y}\\1\end{matrix}\right]\right ]\right ), \quad \left ( x, \quad 1, \quad \left [ \left[\begin{matrix}- e^{i y}\\1\end{matrix}\right]\right ]\right )\right ]$$