© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2021
M. Cywiak, D. CywiakMulti-Platform Graphics Programming with Kivyhttps://doi.org/10.1007/978-1-4842-7113-1_11

11. SymPy

Moisés Cywiak1   and David Cywiak2
(1)
Leon, Guanajuato, Mexico
(2)
Queretaro, Mexico
 
In the previous chapters, we used NumPy to program our arrays, along with mathematical capabilities. In this chapter, we introduce SymPy. This Python library provides special mathematical functions and polynomials as well as powerful symbolic mathematics. You can include SymPy in your programs by means of the following directive:
import sympy as sp.
Now, your program can access SymPy functions through the local name sp and the dot operator. For example, we can obtain the symbolic value of π and assign it to a variable, which we will name Pi by utilizing the following directive:
Pi=sp.pi.

After executing this directive, the variable Pi will represent the symbolic value of π, not precisely its numerical value. As a result, if we now perform the print (Pi) directive, we will obtain pi instead of its numerical value. Furthermore, the print (Pi**2) directive will give pi**2. Notice that we have incorporated symbolic mathematics into our programs.

Now, if we need the numerical value stored in our variable Pi, we can use the following directive:
print (Pi.evalf()).
This directive will display the value 3.14159265358979 on the screen. Furthermore, the following directive:
print (Pi.evalf(100)),
will display this:
3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068.

11.1 Analytical Expressions and Symbols

To use analytical expressions in our programs, we need to declare symbols. For example, the code for the equation 2x2 + 5 + x + 1 is shown in Listing 11-1.
import sympy as sp
x=sp.symbols("x");
Q=2*x**2+5+x+1;
print(Q);
Listing 11-1

Code for the 2x² + 5 + x + 1 Equation

After executing this code, we obtain the following:
2*x**2 + x + 6
In this code, the following directive:
x=sp.symbols("x")
declares x as a symbolic variable. In turn, this directive:
Q=2*x**2+5+x+1

creates Q as a symbolic expression. It is worth noticing in this code that you don’t have to declare Q as a symbolic variable.

At this point, based on our programming experience with numerical functions, we might think that this directive:
print ( Q(1) )
would print the numerical value of Q, calculated at x=1. However, we will see that this assumption is incorrect. In response to this, we would obtain a list of errors, including the following error:
TypeError: 'Add' object is not callable.

Up to this point, we have learned that Q is not the usual numerical function that would allow us to calculate the numerical values of our expressions, but rather is a symbolic representation of an algebraic equation.

Let’s also try to obtain numerical values of Q by following the idea of the first example, using the code shown in Listing 11-2.
import sympy as sp
x=sp.symbols("x")
Q=2*x**2+5+x+1;
print ( Q(1).evalf() );
Listing 11-2

Incorrect Way to Obtain Numerical Q Values

In Listing 11-2, we intended to extend our previous notion in the first example, in which we successfully obtained the value of π. However, in this case, we will obtain several errors, including this one:
TypeError: 'Add' object is not callable.

With this outcome, we should now realize that the variable Pi in the first example holds only one value. In contrast, the Q variable is a function of x, which means it can take an infinite set of values.

The correct way to obtain a numerical value of Q, evaluated at some x, is to use the substitute property, abbreviated subs. For example, to calculate Q(1), we can use the following:
Q.subs(x,1).

This way, we can obtain numerical values of our symbolic Q equation. However, this is not exactly what we want, as we are looking for a numerical function.

Fortunately, there is a more versatile solution provided by SymPy. It utilizes a built-in function, referred to as lambdify. This function allows us to convert symbolic expressions into numerical formulas. To clarify this concept, take a look at the code in Listing 11-3.
import sympy as sp
x=sp.symbols("x");
Q=2*x**2+5+x+1;
R=sp.lambdify(x,Q);
print( R(1) );
Listing 11-3

Converting a Symbolic Function to a Numeric Function

In this code, the following directive:
R=sp.lambdify(x,Q)

assigns to the variable R the properties of a typical numerical function according to our symbolic Q equation. Therefore, R becomes a numerical function, and we can proceed to calculate values of it. For example, R(1) gives the value of the equation at x=1. The previous code prints the requested result to the screen, which in this case is 9.

11.2 Declaring Functions with Analytical Expressions

In general, it is hard to write large mathematical expressions. For these cases, it’s convenient to break the symbolic function code into several parts. As an example, let’s consider the following algebraic expression:
$$ frac{100}{left(5{x}^2+3x+2
ight)}left(75{x}^3+2{x}^2+3
ight)frac{left(50x-3
ight)}{left(12x+5
ight)} $$
(11.1)
The code in Listing 11-4 splits the symbolic expression into several lines, making the code simpler.
def f():
    N1=100/(5*x**2+3*x+2);
    N2=(75*x**3+2*x**2+3);
    N3=(50*x-3)/(12*x+5)
    return N1*N2*N3;
Q=f();
Listing 11-4

Splitting a Symbolic Expression Into Several Code Lines

If we now use the print (Q) directive, we obtain the following:
100*(50*x - 3)*(75*x**3 + 2*x**2 + 3)/((12*x + 5)*(5*x**2 + 3*x + 2))
From this result, we can see that Q symbolically represents our algebraic expression. We can obtain the corresponding numerical formula by using the sp.lambdify( Q) directive . The complete code is shown in Listing 11-5.
import sympy as sp
x=sp.symbols("x");
def f():
    N1=100/(5*x**2+3*x+2);
    N2=(75*x**3+2*x**2+3);
    N3=(50*x-3)/(12*x+5)
    return N1*N2*N3;
Q=f();
R=sp.lambdify(x,Q);
print( R(-2) ); # Calculate the function at x=-2
Listing 11-5

Example of a Symbolic Expression with Several Lines of Code

In this code, we defined the function f(), which does not receive any parameters and returns the symbolic algebraic expression. After this, we can execute the Q=f() directive to assign the symbolic function to the variable Q. Finally, this directive:
R=sp.lambdify(x,Q);

creates the numerical function R corresponding to the algebraic expression. R now allows numerical evaluation.

After executing this code, print( R(-2) ) will give the value -19956.25. This is the correct value of our symbolic equation, evaluated at x=-2.

At this point, note that we could avoid using Q by using this directive:
R=sp.lambdify( x,f() );

11.3 Solving Equations

Let’s consider the simple code in Listing 11-6, which can solve the equation ax + b = 0, where a and b are constant terms.
import sympy as sp
x,a,b =sp.symbols("x a b");
Q=a*x+b
R=sp.solve (Q,x);
print(R);
Listing 11-6

Code for Solving ax + b = 0 Symbolically

In this code, x, a, and b are symbolic variables. After running the code, the program will give the result [-b/a], which is correct.

Now let’s consider using the code in Listing 11-7 to solve ax² + bx + c = 0.
import sympy as sp
x,a,b,c =sp.symbols("x a b c");
Q=a*x**2+b*x+c
R1,R2=sp.solve (Q,x);
print(R1);
print(R2);
Listing 11-7

Symbolically Solving ax² + bx + c = 0

After executing this code, we obtain the following:
(-b + sqrt(-4*a*c + b**2))/(2*a)
-(b + sqrt(-4*a*c + b**2))/(2*a)
In this code, we anticipated that the quadratic equation has two solutions. Therefore, we assigned each solution to the variables R1 and R2. Equivalently, if we had not known the number of solutions beforehand, we could have combined NumPy and SymPy, as illustrated in Listing 11-8.
import sympy as sp
import numpy as np
x,a,b,c =sp.symbols("x a b c");
Q=a*x**2+b*x+c
R=sp.solve (Q,x);
N=np.size(R);
print(N);
for n in range(0, int(N) ):
    print( R[n] );
Listing 11-8

Alternative Code for Solving ax² + bx + c = 0

In this code, R takes the form of an array to store the complete set of solutions. Now, we use NumPy to obtain the number of solutions. Then, the for loop goes from 0 to N to print R[0], which holds one of the solutions, and R[1], which holds the second one. To investigate what R looks like, let’s print R, R[0], and R[1]. We obtain the following:
[(-b + sqrt(-4*a*c + b**2))/(2*a), -(b + sqrt(-4*a*c + b**2))/(2*a)],
(-b + sqrt(-4*a*c + b**2))/(2*a),
-(b + sqrt(-4*a*c + b**2))/(2*a).
For the cubic equation, we want to solve ax³ + bx² + cx + d = 0. The corresponding code is shown in Listing 11-9.
import sympy as sp
x,a,b,c,d =sp.symbols("x a b c d");
Q=a*x**3 + b*x**2 + c*x + d;
R=sp.solve (Q,x);
for n in range(0,3):
    print(R[n]);
    print(" ");
Listing 11-9

Symbolically Solving ax³ + bx² + cx + d = 0

We know in advance that the cubic equation has three solutions. Therefore, we used a for loop to print each one. We added the directive print (" ") to add carriage returns to separate the solutions. The results follow:
-(-3*c/a + b**2/a**2)/(3*(sqrt(-4*(-3*c/a + b**2/a**2)**3 + (27*d/a - 9*b*c/a**2 + 2*b**3/a**3)**2)/2 + 27*d/(2*a) - 9*b*c/(2*a**2) + b**3/a**3)**(1/3)) - (sqrt(-4*(-3*c/a + b**2/a**2)**3 + (27*d/a - 9*b*c/a**2 + 2*b**3/a**3)**2)/2 + 27*d/(2*a) - 9*b*c/(2*a**2) + b**3/a**3)**(1/3)/3 - b/(3*a),
-(-3*c/a + b**2/a**2)/(3*(-1/2 - sqrt(3)*I/2)*(sqrt(-4*(-3*c/a + b**2/a**2)**3 + (27*d/a - 9*b*c/a**2 + 2*b**3/a**3)**2)/2 + 27*d/(2*a) - 9*b*c/(2*a**2) + b**3/a**3)**(1/3)) - (-1/2 - sqrt(3)*I/2)*(sqrt(-4*(-3*c/a + b**2/a**2)**3 + (27*d/a - 9*b*c/a**2 + 2*b**3/a**3)**2)/2 + 27*d/(2*a) - 9*b*c/(2*a**2) + b**3/a**3)**(1/3)/3 - b/(3*a),
-(-3*c/a + b**2/a**2)/(3*(-1/2 + sqrt(3)*I/2)*(sqrt(-4*(-3*c/a + b**2/a**2)**3 + (27*d/a - 9*b*c/a**2 + 2*b**3/a**3)**2)/2 + 27*d/(2*a) - 9*b*c/(2*a**2) + b**3/a**3)**(1/3)) - (-1/2 + sqrt(3)*I/2)*(sqrt(-4*(-3*c/a + b**2/a**2)**3 + (27*d/a - 9*b*c/a**2 + 2*b**3/a**3)**2)/2 + 27*d/(2*a) - 9*b*c/(2*a**2) + b**3/a**3)**(1/3)/3 – b/(3*a).
This code gives the three roots of the cubic algebraic equation. As you can see, it will not be straightforward to demonstrate the correctness of the results. One approach consists of substituting the roots into the algebraic equation and using the simplify directive. Alternatively, you can test for some particular cases. For example, let’s test the a=b=c=d=1 case using the code in Listing 11-10.
import sympy as sp
x,a,b,c,d =sp.symbols("x a b c d");
Q=a*x**3 + b*x**2 +c*x+d
R=sp.solve (Q,x);
T0=sp.lambdify([a,b,c,d ], R[0]);
T1=sp.lambdify([a,b,c,d ], R[1]);
T2=sp.lambdify([a,b,c,d ], R[2]);
x0=T0(1,1,1,1);
x1=T1(1,1,1,1);
x2=T2(1,1,1,1);
print(x0);
print(x1);
print(x2);
Listing 11-10

Printing Numerical Values to the Symbolic Solution of ax³ + bx² + cx + d = 0 for Specific Values

We will focus on the following three directives:
T0=sp.lambdify([a,b,c,d ], R[0]);
T1=sp.lambdify([a,b,c,d ], R[1]);
T2=sp.lambdify([a,b,c,d ], R[2]);
Each directive converts the symbolic expressions R[0], R[1], and R[2] into the numerical parameters T0, T1, and T2. These are actually numerical functions that we will express as T0(a,b,c,d), T1(a,b,c,d), and T2(a,b,c,d), respectively. We can calculate the roots for specific values of a, b, c, and d. For example, for the case a=b=c=d=1, the results are as follows:
-0.9999999999999998
(-5.551115123125783e-17+0.9999999999999999j)
(-5.551115123125783e-17-0.9999999999999999j)

Note that the first root is a real number, while the other two solutions are complex, accomplished using the complex conjugate roots theorem. The parameter j represents the imaginary unit.

To verify that the three roots are indeed solutions of our equation, we can add the code in Listing 11-11 to the end of the previous program.
a=1; b=1; c=1; d=1;
S0=a*x0**3 + b*x0**2 +c*x0+d
S1=a*x1**3 + b*x1**2 +c*x1+d
S2=a*x2**3 + b*x2**2 +c*x2+d
print(S0); print(S1); print(S2);
Listing 11-11

Verifying Specific Solutions for ax³ + bx² + cx + d = 0

From this code, we obtain the following values:
.440892098500626e-16
(2.220446049250313e-16+1.1102230246251565e-16j)
(2.220446049250313e-16-1.1102230246251565e-16j)

Each calculated root should satisfy the cubic equation, rigorously speaking, so the three values should be 0. Although they are not precisely equal to 0, the three numerical values are, for practical purposes, negligible (in the order of 10-16). This confirms that, within the numerical precision of the processor, these three roots are indeed solutions of the cubic equation.

Finally, let’s investigate if SymPy can solve the algebraic quartic equation written in the form ax4 + bx3 + cx2 + dx + e = 0.

The corresponding code is shown Listing 11-12.
import sympy as sp
x,a,b,c,d, e =sp.symbols("x a b c d e");
Q=a*x**4 + b*x**3 + c*x**2 + d*x + e;
R=sp.solve (Q,x);
for n in range(0,4):
    print(R[n]);
    print(" ");
Listing 11-12

Symbolically Solving ax4 + bx3 + cx2 + dx + e = 0

After executing this code, we obtain four messages with the following text:
Squeezed text (51 lines).

We can print the symbolic results by double-clicking each one of these messages. However, the results are lengthy. Therefore, as in the previous case, we need to verify their correctness by testing particular values.

11.4 Solving Simultaneous Equations

Let’s consider the following pair of simultaneous algebraic equations.
$$ ax+ by=C1 $$
(11.2)
$$ {cx}^{{}^2}+{dy}^{{}^2}=C2 $$
(11.3)
Listing 11-13 obtains the solutions symbolically.
import sympy as sp
x, y, a, b, c, d, C1, C2=sp.symbols("x y a b c d C1 C2");
Eq1=sp.Eq(a*x+b*y,C1);
Eq2=sp.Eq(c*x**2+d*y**2, C2);
R=sp.solve( [Eq1,Eq2],(x,y) );
print(R[0]);
print(" ");
print(R[1]);
Listing 11-13

Symbolically Solving Two Simultaneous Linear Equations

As the solutions are lengthy, we can proceed as in the previous example to test some particular solutions.

11.5 Symbolic Differentiation

Let’s consider the following expression:
$$ b mathit{exp}left(- ax
ight) $$
(11.4)
Listing 11-14 will print the symbolic derivative of the expression with respect to the variable x.
import sympy as sp
x,a,b=sp.symbols("x a b");
f=b*sp.exp(-a*x);
R=sp.diff(f,x); # first order derivative
print(R);
Listing 11-14

Example of Symbolic Differentiation

We obtain the following:
-a*b*exp(-a*x)
To code to obtain the second derivative is shown in Listing 11-15.
import sympy as sp
x,a,b=sp.symbols("x a b");
f=b*sp.exp(-a*x);
R=sp.diff(f,x,2);
print(R);
Listing 11-15

Second Symbolic Derivative Example

After executing this code, we obtain:
 a**2*b*exp(-a*x)
Let’s now consider a two-dimensional expression, as follows:
$$ c mathit{exp}left(- ax- by
ight) $$
(11.5)
We want to symbolically calculate the following derivative:
$$ frac{partial^{{}^2}}{partial {y}^{{}^2}}frac{partial }{partial x}left[c mathit{exp}left(- ax- by
ight)
ight] $$
(11.6)
This expression requires us to calculate three derivatives. The first one with respect to the variable x, and the first and second derivative with respect to the variable y. The code is shown in Listing 11-16.
import sympy as sp
x,y,a,b,c=sp.symbols("x y a b c");
f=c*sp.exp(-a*x-b*y);
#Perform first derivative respect to x and then, second derivatives respect to y.
R=sp.diff(f,x,1, y,2);
print(R);
Listing 11-16

Symbolic Third Order with Partial Derivatives

The result obtained with this code reads as follows:
-a*b**2*c*exp(-a*x – b*y

11.6 Integration

Let’s now symbolically calculate the following integral:
$$ {int}_b^cleft({ax}^2+3
ight) dx $$
(11.7)
The corresponding code is shown in Listing 11-17.
import sympy as sp
x,a,b,c=sp.symbols("x a b c");
f=a*x**2 + 3;
R=sp.integrate(f, (x,b,c)); #Integrate f with  respect to x from b to c.
print(R);
Listing 11-17

Symbolic Integration Example

After executing the code, we obtain the following:
-a*b**3/3 + a*c**3/3 - 3*b + 3*c
Let’s investigate if SymPy can calculate some improper integrals. In these types of integrals, one or both of the integration limits is infinity. As an example, consider the following improper integral:
$$ {int}_{-infty}^{infty}mathit{exp}left(-{x}^2
ight) dx $$
(11.8)

It is well known that the result of this integral equals $$ {pi}^{frac{1}{2}} $$.

The corresponding code to calculate the integral is shown in Listing 11-18.
import sympy as sp
x=sp.symbols("x");
Infinity=sp.oo; #Read sympy infinity and assign it to the parameter Infinity.
f=sp.exp(-x**2);
R=sp.integrate(f, (x,-Infinity,Infinity)); #Calculate the integral
print (R); # Print the result
Listing 11-18

Symbolic Improper Integral Calculation

After executing this code, the program prints the following result:
sqrt(pi)

This result confirms the capability of SymPy to calculate improper integrals.

Let’s now extend the integral to the two-dimensional case:
$$ {int}_{-infty}^{infty }{int}_{-infty}^{infty}mathit{exp}left(-left({x}^2+{y}^2
ight)
ight) dx dy $$
(11.9)
The code to calculate this integral is shown in Listing 11-19.
import sympy as sp
x,y=sp.symbols("x y");
Infinity=sp.oo; #Assigning the value of infinity to a variable.
f=sp.exp(-x**2-y**2);
#Calculating the integral
R=sp.integrate(f,(x,-Infinity,Infinity), (y,-Infinity,Infinity));
print(R);
Listing 11-19

Symbolic Two-Dimensional Improper Integral Calculation

After executing this code, we obtain the result, pi, as expected.

In the following chapters, you will use some SymPy functions. In the next subsection, we describe the procedure to incorporate SymPy into Android projects.

11.7 Incorporating SymPy Into Android Projects

Now you learn how to incorporate SymPy into your Android projects. At first glance, you might think that it will suffice to add SymPy to the end of the requirements line in the buildozer.spec file, in the same manner as you did with NumPy and Pillow. However, if you do so, Buildozer will fail in the conversion process. Among the list of errors, you will see: "mpmath with version larger or equal to 0.19 is required."

To verify if mpath is installed on your computer, open a Terminal window and type pip3 list. In our case, we had mpmath version 1.1.0.

At this point, you might conclude that SymPy, apparently, is not compatible with Buildozer. Fortunately, this is not the case. At https://github.com/kivy/python-for-android/issues/2303, we found a solution posted by Robert Flatt to solve this problem. The solution consists of adding mpmath, instead of SymPy, to the row that corresponds to requirements in the buildozer.spec file, as follows:
requirements = python3,kivy,numpy,pillow,mpmath
Then you need to copy SymPy’s source code into the working folder. In Ubuntu, you’ll find the code in the following folder:
/home/ComputerName/.local/lib/python3.8/site-packages

In this path, the term ComputerName has to be replaced with the name you assigned to your computer when Ubuntu was installed.

The source code we are looking for corresponds to the folder named sympy. Therefore, you must copy this folder into your working one.

After accomplishing this procedure, you can use Buildozer as usual to create the Android APK. Type the following:
buildozer -v android debug

In the following chapters, you will need SymPy.

11.8 Summary

In this chapter, we introduced SymPy to symbolically access special mathematical functions and polynomials. We described how to incorporate SymPy into your projects, and we described its functionality by using working examples.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
44.204.94.166